blob: 0710d4643aa3a4845714f6e0259a9198ff5e81f0 [file] [log] [blame]
Steve Blocka7e24c12009-10-30 11:49:00 +00001// Copyright 2009 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#include <v8.h>
Ben Murdochb0fe1622011-05-05 13:52:32 +010029#include <v8-testing.h>
Ben Murdoche0cee9b2011-05-25 10:26:03 +010030#include <assert.h>
Steve Blocka7e24c12009-10-30 11:49:00 +000031#include <fcntl.h>
32#include <string.h>
33#include <stdio.h>
34#include <stdlib.h>
35
Steve Block44f0eee2011-05-26 01:26:41 +010036// When building with V8 in a shared library we cannot use functions which
37// is not explicitly a part of the public V8 API. This extensive use of
38// #ifndef USING_V8_SHARED/#endif is a hack until we can resolve whether to
39// still use the shell sample for testing or change to use the developer
40// shell d8 TODO(1272).
41#ifndef USING_V8_SHARED
42#include "../src/v8.h"
43#endif // USING_V8_SHARED
Steve Blocka7e24c12009-10-30 11:49:00 +000044
Steve Block44f0eee2011-05-26 01:26:41 +010045#if !defined(_WIN32) && !defined(_WIN64)
46#include <unistd.h> // NOLINT
47#endif
48
49static void ExitShell(int exit_code) {
50 // Use _exit instead of exit to avoid races between isolate
51 // threads and static destructors.
52 fflush(stdout);
53 fflush(stderr);
54 _exit(exit_code);
55}
56
57v8::Persistent<v8::Context> CreateShellContext();
Steve Blocka7e24c12009-10-30 11:49:00 +000058void RunShell(v8::Handle<v8::Context> context);
59bool ExecuteString(v8::Handle<v8::String> source,
60 v8::Handle<v8::Value> name,
61 bool print_result,
62 bool report_exceptions);
63v8::Handle<v8::Value> Print(const v8::Arguments& args);
64v8::Handle<v8::Value> Read(const v8::Arguments& args);
65v8::Handle<v8::Value> Load(const v8::Arguments& args);
66v8::Handle<v8::Value> Quit(const v8::Arguments& args);
67v8::Handle<v8::Value> Version(const v8::Arguments& args);
68v8::Handle<v8::String> ReadFile(const char* name);
69void ReportException(v8::TryCatch* handler);
70
71
Steve Block44f0eee2011-05-26 01:26:41 +010072static bool last_run = true;
73
74class SourceGroup {
75 public:
76 SourceGroup() :
77#ifndef USING_V8_SHARED
78 next_semaphore_(v8::internal::OS::CreateSemaphore(0)),
79 done_semaphore_(v8::internal::OS::CreateSemaphore(0)),
80 thread_(NULL),
81#endif // USING_V8_SHARED
82 argv_(NULL),
83 begin_offset_(0),
84 end_offset_(0) { }
85
86 void Begin(char** argv, int offset) {
87 argv_ = const_cast<const char**>(argv);
88 begin_offset_ = offset;
89 }
90
91 void End(int offset) { end_offset_ = offset; }
92
93 void Execute() {
94 for (int i = begin_offset_; i < end_offset_; ++i) {
95 const char* arg = argv_[i];
96 if (strcmp(arg, "-e") == 0 && i + 1 < end_offset_) {
97 // Execute argument given to -e option directly.
98 v8::HandleScope handle_scope;
99 v8::Handle<v8::String> file_name = v8::String::New("unnamed");
100 v8::Handle<v8::String> source = v8::String::New(argv_[i + 1]);
101 if (!ExecuteString(source, file_name, false, true)) {
102 ExitShell(1);
103 return;
104 }
105 ++i;
106 } else if (arg[0] == '-') {
107 // Ignore other options. They have been parsed already.
108 } else {
109 // Use all other arguments as names of files to load and run.
110 v8::HandleScope handle_scope;
111 v8::Handle<v8::String> file_name = v8::String::New(arg);
112 v8::Handle<v8::String> source = ReadFile(arg);
113 if (source.IsEmpty()) {
114 printf("Error reading '%s'\n", arg);
115 }
116 if (!ExecuteString(source, file_name, false, true)) {
117 ExitShell(1);
118 return;
119 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000120 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000121 }
122 }
Steve Block44f0eee2011-05-26 01:26:41 +0100123
124#ifndef USING_V8_SHARED
125 void StartExecuteInThread() {
126 if (thread_ == NULL) {
127 thread_ = new IsolateThread(this);
128 thread_->Start();
129 }
130 next_semaphore_->Signal();
131 }
132
133 void WaitForThread() {
134 if (thread_ == NULL) return;
135 if (last_run) {
136 thread_->Join();
137 thread_ = NULL;
138 } else {
139 done_semaphore_->Wait();
140 }
141 }
142#endif // USING_V8_SHARED
143
144 private:
145#ifndef USING_V8_SHARED
146 static v8::internal::Thread::Options GetThreadOptions() {
147 v8::internal::Thread::Options options;
148 options.name = "IsolateThread";
149 // On some systems (OSX 10.6) the stack size default is 0.5Mb or less
150 // which is not enough to parse the big literal expressions used in tests.
151 // The stack size should be at least StackGuard::kLimitSize + some
152 // OS-specific padding for thread startup code.
153 options.stack_size = 2 << 20; // 2 Mb seems to be enough
154 return options;
155 }
156
157 class IsolateThread : public v8::internal::Thread {
158 public:
159 explicit IsolateThread(SourceGroup* group)
160 : v8::internal::Thread(NULL, GetThreadOptions()), group_(group) {}
161
162 virtual void Run() {
163 group_->ExecuteInThread();
164 }
165
166 private:
167 SourceGroup* group_;
168 };
169
170 void ExecuteInThread() {
171 v8::Isolate* isolate = v8::Isolate::New();
172 do {
173 if (next_semaphore_ != NULL) next_semaphore_->Wait();
174 {
175 v8::Isolate::Scope iscope(isolate);
176 v8::HandleScope scope;
177 v8::Persistent<v8::Context> context = CreateShellContext();
178 {
179 v8::Context::Scope cscope(context);
180 Execute();
181 }
182 context.Dispose();
183 }
184 if (done_semaphore_ != NULL) done_semaphore_->Signal();
185 } while (!last_run);
186 isolate->Dispose();
187 }
188
189 v8::internal::Semaphore* next_semaphore_;
190 v8::internal::Semaphore* done_semaphore_;
191 v8::internal::Thread* thread_;
192#endif // USING_V8_SHARED
193
194 const char** argv_;
195 int begin_offset_;
196 int end_offset_;
197};
198
199
200static SourceGroup* isolate_sources = NULL;
201
202
203int RunMain(int argc, char* argv[]) {
204 v8::V8::SetFlagsFromCommandLine(&argc, argv, true);
205 v8::HandleScope handle_scope;
206 v8::Persistent<v8::Context> context = CreateShellContext();
207 // Enter the newly created execution environment.
208 context->Enter();
209 if (context.IsEmpty()) {
210 printf("Error creating context\n");
211 return 1;
212 }
213
214 bool run_shell = (argc == 1);
215 int num_isolates = 1;
216 for (int i = 1; i < argc; i++) {
217 if (strcmp(argv[i], "--isolate") == 0) {
218#ifndef USING_V8_SHARED
219 ++num_isolates;
220#else // USING_V8_SHARED
221 printf("Error: --isolate not supported when linked with shared "
222 "library\n");
223 ExitShell(1);
224#endif // USING_V8_SHARED
225 }
226 }
227 if (isolate_sources == NULL) {
228 isolate_sources = new SourceGroup[num_isolates];
229 SourceGroup* current = isolate_sources;
230 current->Begin(argv, 1);
231 for (int i = 1; i < argc; i++) {
232 const char* str = argv[i];
233 if (strcmp(str, "--isolate") == 0) {
234 current->End(i);
235 current++;
236 current->Begin(argv, i + 1);
237 } else if (strcmp(str, "--shell") == 0) {
238 run_shell = true;
239 } else if (strcmp(str, "-f") == 0) {
240 // Ignore any -f flags for compatibility with the other stand-
241 // alone JavaScript engines.
242 continue;
243 } else if (strncmp(str, "--", 2) == 0) {
244 printf("Warning: unknown flag %s.\nTry --help for options\n", str);
245 }
246 }
247 current->End(argc);
248 }
249#ifndef USING_V8_SHARED
250 for (int i = 1; i < num_isolates; ++i) {
251 isolate_sources[i].StartExecuteInThread();
252 }
253#endif // USING_V8_SHARED
254 isolate_sources[0].Execute();
Steve Blocka7e24c12009-10-30 11:49:00 +0000255 if (run_shell) RunShell(context);
Steve Block44f0eee2011-05-26 01:26:41 +0100256#ifndef USING_V8_SHARED
257 for (int i = 1; i < num_isolates; ++i) {
258 isolate_sources[i].WaitForThread();
259 }
260#endif // USING_V8_SHARED
261 if (last_run) {
262 delete[] isolate_sources;
263 isolate_sources = NULL;
264 }
265 context->Exit();
Ben Murdochb0fe1622011-05-05 13:52:32 +0100266 context.Dispose();
Steve Blocka7e24c12009-10-30 11:49:00 +0000267 return 0;
268}
269
270
271int main(int argc, char* argv[]) {
Ben Murdochb0fe1622011-05-05 13:52:32 +0100272 // Figure out if we're requested to stress the optimization
273 // infrastructure by running tests multiple times and forcing
274 // optimization in the last run.
275 bool FLAG_stress_opt = false;
276 bool FLAG_stress_deopt = false;
277 for (int i = 0; i < argc; i++) {
278 if (strcmp(argv[i], "--stress-opt") == 0) {
279 FLAG_stress_opt = true;
280 argv[i] = NULL;
281 } else if (strcmp(argv[i], "--stress-deopt") == 0) {
282 FLAG_stress_deopt = true;
283 argv[i] = NULL;
284 } else if (strcmp(argv[i], "--noalways-opt") == 0) {
285 // No support for stressing if we can't use --always-opt.
286 FLAG_stress_opt = false;
287 FLAG_stress_deopt = false;
288 break;
289 }
290 }
291
292 v8::V8::SetFlagsFromCommandLine(&argc, argv, true);
293 int result = 0;
294 if (FLAG_stress_opt || FLAG_stress_deopt) {
295 v8::Testing::SetStressRunType(FLAG_stress_opt
296 ? v8::Testing::kStressTypeOpt
297 : v8::Testing::kStressTypeDeopt);
298 int stress_runs = v8::Testing::GetStressRuns();
299 for (int i = 0; i < stress_runs && result == 0; i++) {
300 printf("============ Stress %d/%d ============\n",
301 i + 1, stress_runs);
302 v8::Testing::PrepareStressRun(i);
Steve Block44f0eee2011-05-26 01:26:41 +0100303 last_run = (i == stress_runs - 1);
Ben Murdochb0fe1622011-05-05 13:52:32 +0100304 result = RunMain(argc, argv);
305 }
Steve Block44f0eee2011-05-26 01:26:41 +0100306 printf("======== Full Deoptimization =======\n");
307 v8::Testing::DeoptimizeAll();
Ben Murdochb0fe1622011-05-05 13:52:32 +0100308 } else {
309 result = RunMain(argc, argv);
310 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000311 v8::V8::Dispose();
312 return result;
313}
314
315
316// Extracts a C string from a V8 Utf8Value.
317const char* ToCString(const v8::String::Utf8Value& value) {
318 return *value ? *value : "<string conversion failed>";
319}
320
321
Steve Block44f0eee2011-05-26 01:26:41 +0100322// Creates a new execution environment containing the built-in
323// functions.
324v8::Persistent<v8::Context> CreateShellContext() {
325 // Create a template for the global object.
326 v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
327 // Bind the global 'print' function to the C++ Print callback.
328 global->Set(v8::String::New("print"), v8::FunctionTemplate::New(Print));
329 // Bind the global 'read' function to the C++ Read callback.
330 global->Set(v8::String::New("read"), v8::FunctionTemplate::New(Read));
331 // Bind the global 'load' function to the C++ Load callback.
332 global->Set(v8::String::New("load"), v8::FunctionTemplate::New(Load));
333 // Bind the 'quit' function
334 global->Set(v8::String::New("quit"), v8::FunctionTemplate::New(Quit));
335 // Bind the 'version' function
336 global->Set(v8::String::New("version"), v8::FunctionTemplate::New(Version));
337 return v8::Context::New(NULL, global);
338}
339
340
Steve Blocka7e24c12009-10-30 11:49:00 +0000341// The callback that is invoked by v8 whenever the JavaScript 'print'
342// function is called. Prints its arguments on stdout separated by
343// spaces and ending with a newline.
344v8::Handle<v8::Value> Print(const v8::Arguments& args) {
345 bool first = true;
346 for (int i = 0; i < args.Length(); i++) {
347 v8::HandleScope handle_scope;
348 if (first) {
349 first = false;
350 } else {
351 printf(" ");
352 }
353 v8::String::Utf8Value str(args[i]);
354 const char* cstr = ToCString(str);
355 printf("%s", cstr);
356 }
357 printf("\n");
358 fflush(stdout);
359 return v8::Undefined();
360}
361
362
363// The callback that is invoked by v8 whenever the JavaScript 'read'
364// function is called. This function loads the content of the file named in
365// the argument into a JavaScript string.
366v8::Handle<v8::Value> Read(const v8::Arguments& args) {
367 if (args.Length() != 1) {
368 return v8::ThrowException(v8::String::New("Bad parameters"));
369 }
370 v8::String::Utf8Value file(args[0]);
371 if (*file == NULL) {
372 return v8::ThrowException(v8::String::New("Error loading file"));
373 }
374 v8::Handle<v8::String> source = ReadFile(*file);
375 if (source.IsEmpty()) {
376 return v8::ThrowException(v8::String::New("Error loading file"));
377 }
378 return source;
379}
380
381
382// The callback that is invoked by v8 whenever the JavaScript 'load'
383// function is called. Loads, compiles and executes its argument
384// JavaScript file.
385v8::Handle<v8::Value> Load(const v8::Arguments& args) {
386 for (int i = 0; i < args.Length(); i++) {
387 v8::HandleScope handle_scope;
388 v8::String::Utf8Value file(args[i]);
389 if (*file == NULL) {
390 return v8::ThrowException(v8::String::New("Error loading file"));
391 }
392 v8::Handle<v8::String> source = ReadFile(*file);
393 if (source.IsEmpty()) {
394 return v8::ThrowException(v8::String::New("Error loading file"));
395 }
396 if (!ExecuteString(source, v8::String::New(*file), false, false)) {
397 return v8::ThrowException(v8::String::New("Error executing file"));
398 }
399 }
400 return v8::Undefined();
401}
402
403
404// The callback that is invoked by v8 whenever the JavaScript 'quit'
405// function is called. Quits.
406v8::Handle<v8::Value> Quit(const v8::Arguments& args) {
407 // If not arguments are given args[0] will yield undefined which
408 // converts to the integer value 0.
409 int exit_code = args[0]->Int32Value();
Steve Block44f0eee2011-05-26 01:26:41 +0100410 ExitShell(exit_code);
Steve Blocka7e24c12009-10-30 11:49:00 +0000411 return v8::Undefined();
412}
413
414
415v8::Handle<v8::Value> Version(const v8::Arguments& args) {
416 return v8::String::New(v8::V8::GetVersion());
417}
418
419
420// Reads a file into a v8 string.
421v8::Handle<v8::String> ReadFile(const char* name) {
422 FILE* file = fopen(name, "rb");
423 if (file == NULL) return v8::Handle<v8::String>();
424
425 fseek(file, 0, SEEK_END);
426 int size = ftell(file);
427 rewind(file);
428
429 char* chars = new char[size + 1];
430 chars[size] = '\0';
431 for (int i = 0; i < size;) {
432 int read = fread(&chars[i], 1, size - i, file);
433 i += read;
434 }
435 fclose(file);
436 v8::Handle<v8::String> result = v8::String::New(chars, size);
437 delete[] chars;
438 return result;
439}
440
441
442// The read-eval-execute loop of the shell.
443void RunShell(v8::Handle<v8::Context> context) {
444 printf("V8 version %s\n", v8::V8::GetVersion());
445 static const int kBufferSize = 256;
Ben Murdochb0fe1622011-05-05 13:52:32 +0100446 // Enter the execution environment before evaluating any code.
447 v8::Context::Scope context_scope(context);
Steve Blocka7e24c12009-10-30 11:49:00 +0000448 while (true) {
449 char buffer[kBufferSize];
450 printf("> ");
451 char* str = fgets(buffer, kBufferSize, stdin);
452 if (str == NULL) break;
453 v8::HandleScope handle_scope;
454 ExecuteString(v8::String::New(str),
455 v8::String::New("(shell)"),
456 true,
457 true);
458 }
459 printf("\n");
460}
461
462
463// Executes a string within the current v8 context.
464bool ExecuteString(v8::Handle<v8::String> source,
465 v8::Handle<v8::Value> name,
466 bool print_result,
467 bool report_exceptions) {
468 v8::HandleScope handle_scope;
469 v8::TryCatch try_catch;
470 v8::Handle<v8::Script> script = v8::Script::Compile(source, name);
471 if (script.IsEmpty()) {
472 // Print errors that happened during compilation.
473 if (report_exceptions)
474 ReportException(&try_catch);
475 return false;
476 } else {
477 v8::Handle<v8::Value> result = script->Run();
478 if (result.IsEmpty()) {
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100479 assert(try_catch.HasCaught());
Steve Blocka7e24c12009-10-30 11:49:00 +0000480 // Print errors that happened during execution.
481 if (report_exceptions)
482 ReportException(&try_catch);
483 return false;
484 } else {
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100485 assert(!try_catch.HasCaught());
Steve Blocka7e24c12009-10-30 11:49:00 +0000486 if (print_result && !result->IsUndefined()) {
487 // If all went well and the result wasn't undefined then print
488 // the returned value.
489 v8::String::Utf8Value str(result);
490 const char* cstr = ToCString(str);
491 printf("%s\n", cstr);
492 }
493 return true;
494 }
495 }
496}
497
498
499void ReportException(v8::TryCatch* try_catch) {
500 v8::HandleScope handle_scope;
501 v8::String::Utf8Value exception(try_catch->Exception());
502 const char* exception_string = ToCString(exception);
503 v8::Handle<v8::Message> message = try_catch->Message();
504 if (message.IsEmpty()) {
505 // V8 didn't provide any extra information about this error; just
506 // print the exception.
507 printf("%s\n", exception_string);
508 } else {
509 // Print (filename):(line number): (message).
510 v8::String::Utf8Value filename(message->GetScriptResourceName());
511 const char* filename_string = ToCString(filename);
512 int linenum = message->GetLineNumber();
513 printf("%s:%i: %s\n", filename_string, linenum, exception_string);
514 // Print line of source code.
515 v8::String::Utf8Value sourceline(message->GetSourceLine());
516 const char* sourceline_string = ToCString(sourceline);
517 printf("%s\n", sourceline_string);
518 // Print wavy underline (GetUnderline is deprecated).
519 int start = message->GetStartColumn();
520 for (int i = 0; i < start; i++) {
521 printf(" ");
522 }
523 int end = message->GetEndColumn();
524 for (int i = start; i < end; i++) {
525 printf("^");
526 }
527 printf("\n");
Kristian Monsen25f61362010-05-21 11:50:48 +0100528 v8::String::Utf8Value stack_trace(try_catch->StackTrace());
529 if (stack_trace.length() > 0) {
530 const char* stack_trace_string = ToCString(stack_trace);
531 printf("%s\n", stack_trace_string);
532 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000533 }
534}