blob: 7de82b759f3b75b93390d1c6c41ef1318fe7468c [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
29#include <stdlib.h>
30#include <errno.h>
31
Steve Block44f0eee2011-05-26 01:26:41 +010032#include "v8.h"
33
Steve Blocka7e24c12009-10-30 11:49:00 +000034#include "d8.h"
35#include "d8-debug.h"
36#include "debug.h"
37#include "api.h"
38#include "natives.h"
39#include "platform.h"
40
41
42namespace v8 {
43
44
45const char* Shell::kHistoryFileName = ".d8_history";
46const char* Shell::kPrompt = "d8> ";
47
48
49LineEditor *LineEditor::first_ = NULL;
50
51
52LineEditor::LineEditor(Type type, const char* name)
53 : type_(type),
54 name_(name),
55 next_(first_) {
56 first_ = this;
57}
58
59
60LineEditor* LineEditor::Get() {
61 LineEditor* current = first_;
62 LineEditor* best = current;
63 while (current != NULL) {
64 if (current->type_ > best->type_)
65 best = current;
66 current = current->next_;
67 }
68 return best;
69}
70
71
72class DumbLineEditor: public LineEditor {
73 public:
74 DumbLineEditor() : LineEditor(LineEditor::DUMB, "dumb") { }
75 virtual i::SmartPointer<char> Prompt(const char* prompt);
76};
77
78
79static DumbLineEditor dumb_line_editor;
80
81
82i::SmartPointer<char> DumbLineEditor::Prompt(const char* prompt) {
83 static const int kBufferSize = 256;
84 char buffer[kBufferSize];
85 printf("%s", prompt);
86 char* str = fgets(buffer, kBufferSize, stdin);
87 return i::SmartPointer<char>(str ? i::StrDup(str) : str);
88}
89
90
91CounterMap* Shell::counter_map_;
92i::OS::MemoryMappedFile* Shell::counters_file_ = NULL;
93CounterCollection Shell::local_counters_;
94CounterCollection* Shell::counters_ = &local_counters_;
95Persistent<Context> Shell::utility_context_;
96Persistent<Context> Shell::evaluation_context_;
97
98
99bool CounterMap::Match(void* key1, void* key2) {
100 const char* name1 = reinterpret_cast<const char*>(key1);
101 const char* name2 = reinterpret_cast<const char*>(key2);
102 return strcmp(name1, name2) == 0;
103}
104
105
106// Converts a V8 value to a C string.
Steve Block6ded16b2010-05-10 14:33:55 +0100107const char* Shell::ToCString(const v8::String::Utf8Value& value) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000108 return *value ? *value : "<string conversion failed>";
109}
110
111
112// Executes a string within the current v8 context.
113bool Shell::ExecuteString(Handle<String> source,
114 Handle<Value> name,
115 bool print_result,
116 bool report_exceptions) {
117 HandleScope handle_scope;
118 TryCatch try_catch;
119 if (i::FLAG_debugger) {
120 // When debugging make exceptions appear to be uncaught.
121 try_catch.SetVerbose(true);
122 }
123 Handle<Script> script = Script::Compile(source, name);
124 if (script.IsEmpty()) {
125 // Print errors that happened during compilation.
126 if (report_exceptions && !i::FLAG_debugger)
127 ReportException(&try_catch);
128 return false;
129 } else {
130 Handle<Value> result = script->Run();
131 if (result.IsEmpty()) {
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100132 ASSERT(try_catch.HasCaught());
Steve Blocka7e24c12009-10-30 11:49:00 +0000133 // Print errors that happened during execution.
134 if (report_exceptions && !i::FLAG_debugger)
135 ReportException(&try_catch);
136 return false;
137 } else {
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100138 ASSERT(!try_catch.HasCaught());
Steve Blocka7e24c12009-10-30 11:49:00 +0000139 if (print_result && !result->IsUndefined()) {
140 // If all went well and the result wasn't undefined then print
141 // the returned value.
142 v8::String::Utf8Value str(result);
143 const char* cstr = ToCString(str);
144 printf("%s\n", cstr);
145 }
146 return true;
147 }
148 }
149}
150
151
152Handle<Value> Shell::Print(const Arguments& args) {
153 Handle<Value> val = Write(args);
154 printf("\n");
155 return val;
156}
157
158
159Handle<Value> Shell::Write(const Arguments& args) {
160 for (int i = 0; i < args.Length(); i++) {
161 HandleScope handle_scope;
162 if (i != 0) {
163 printf(" ");
164 }
165 v8::String::Utf8Value str(args[i]);
Steve Blockd0582a62009-12-15 09:54:21 +0000166 int n = fwrite(*str, sizeof(**str), str.length(), stdout);
167 if (n != str.length()) {
168 printf("Error in fwrite\n");
169 exit(1);
170 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000171 }
172 return Undefined();
173}
174
175
176Handle<Value> Shell::Read(const Arguments& args) {
177 String::Utf8Value file(args[0]);
178 if (*file == NULL) {
179 return ThrowException(String::New("Error loading file"));
180 }
181 Handle<String> source = ReadFile(*file);
182 if (source.IsEmpty()) {
183 return ThrowException(String::New("Error loading file"));
184 }
185 return source;
186}
187
188
189Handle<Value> Shell::ReadLine(const Arguments& args) {
190 i::SmartPointer<char> line(i::ReadLine(""));
191 if (*line == NULL) {
192 return Null();
193 }
194 size_t len = strlen(*line);
195 if (len > 0 && line[len - 1] == '\n') {
196 --len;
197 }
198 return String::New(*line, len);
199}
200
201
202Handle<Value> Shell::Load(const Arguments& args) {
203 for (int i = 0; i < args.Length(); i++) {
204 HandleScope handle_scope;
205 String::Utf8Value file(args[i]);
206 if (*file == NULL) {
207 return ThrowException(String::New("Error loading file"));
208 }
209 Handle<String> source = ReadFile(*file);
210 if (source.IsEmpty()) {
211 return ThrowException(String::New("Error loading file"));
212 }
213 if (!ExecuteString(source, String::New(*file), false, false)) {
Steve Blockd0582a62009-12-15 09:54:21 +0000214 return ThrowException(String::New("Error executing file"));
Steve Blocka7e24c12009-10-30 11:49:00 +0000215 }
216 }
217 return Undefined();
218}
219
220
221Handle<Value> Shell::Yield(const Arguments& args) {
222 v8::Unlocker unlocker;
223 return Undefined();
224}
225
226
227Handle<Value> Shell::Quit(const Arguments& args) {
228 int exit_code = args[0]->Int32Value();
229 OnExit();
230 exit(exit_code);
231 return Undefined();
232}
233
234
235Handle<Value> Shell::Version(const Arguments& args) {
236 return String::New(V8::GetVersion());
237}
238
239
240void Shell::ReportException(v8::TryCatch* try_catch) {
241 HandleScope handle_scope;
242 v8::String::Utf8Value exception(try_catch->Exception());
243 const char* exception_string = ToCString(exception);
244 Handle<Message> message = try_catch->Message();
245 if (message.IsEmpty()) {
246 // V8 didn't provide any extra information about this error; just
247 // print the exception.
248 printf("%s\n", exception_string);
249 } else {
250 // Print (filename):(line number): (message).
251 v8::String::Utf8Value filename(message->GetScriptResourceName());
252 const char* filename_string = ToCString(filename);
253 int linenum = message->GetLineNumber();
254 printf("%s:%i: %s\n", filename_string, linenum, exception_string);
255 // Print line of source code.
256 v8::String::Utf8Value sourceline(message->GetSourceLine());
257 const char* sourceline_string = ToCString(sourceline);
258 printf("%s\n", sourceline_string);
259 // Print wavy underline (GetUnderline is deprecated).
260 int start = message->GetStartColumn();
261 for (int i = 0; i < start; i++) {
262 printf(" ");
263 }
264 int end = message->GetEndColumn();
265 for (int i = start; i < end; i++) {
266 printf("^");
267 }
268 printf("\n");
269 }
270}
271
272
273Handle<Array> Shell::GetCompletions(Handle<String> text, Handle<String> full) {
274 HandleScope handle_scope;
275 Context::Scope context_scope(utility_context_);
276 Handle<Object> global = utility_context_->Global();
277 Handle<Value> fun = global->Get(String::New("GetCompletions"));
278 static const int kArgc = 3;
279 Handle<Value> argv[kArgc] = { evaluation_context_->Global(), text, full };
280 Handle<Value> val = Handle<Function>::Cast(fun)->Call(global, kArgc, argv);
281 return handle_scope.Close(Handle<Array>::Cast(val));
282}
283
284
285#ifdef ENABLE_DEBUGGER_SUPPORT
286Handle<Object> Shell::DebugMessageDetails(Handle<String> message) {
287 Context::Scope context_scope(utility_context_);
288 Handle<Object> global = utility_context_->Global();
289 Handle<Value> fun = global->Get(String::New("DebugMessageDetails"));
290 static const int kArgc = 1;
291 Handle<Value> argv[kArgc] = { message };
292 Handle<Value> val = Handle<Function>::Cast(fun)->Call(global, kArgc, argv);
293 return Handle<Object>::Cast(val);
294}
295
296
297Handle<Value> Shell::DebugCommandToJSONRequest(Handle<String> command) {
298 Context::Scope context_scope(utility_context_);
299 Handle<Object> global = utility_context_->Global();
300 Handle<Value> fun = global->Get(String::New("DebugCommandToJSONRequest"));
301 static const int kArgc = 1;
302 Handle<Value> argv[kArgc] = { command };
303 Handle<Value> val = Handle<Function>::Cast(fun)->Call(global, kArgc, argv);
304 return val;
305}
306#endif
307
308
309int32_t* Counter::Bind(const char* name, bool is_histogram) {
310 int i;
311 for (i = 0; i < kMaxNameSize - 1 && name[i]; i++)
312 name_[i] = static_cast<char>(name[i]);
313 name_[i] = '\0';
314 is_histogram_ = is_histogram;
315 return ptr();
316}
317
318
319void Counter::AddSample(int32_t sample) {
320 count_++;
321 sample_total_ += sample;
322}
323
324
325CounterCollection::CounterCollection() {
326 magic_number_ = 0xDEADFACE;
327 max_counters_ = kMaxCounters;
328 max_name_size_ = Counter::kMaxNameSize;
329 counters_in_use_ = 0;
330}
331
332
333Counter* CounterCollection::GetNextCounter() {
334 if (counters_in_use_ == kMaxCounters) return NULL;
335 return &counters_[counters_in_use_++];
336}
337
338
339void Shell::MapCounters(const char* name) {
340 counters_file_ = i::OS::MemoryMappedFile::create(name,
341 sizeof(CounterCollection), &local_counters_);
342 void* memory = (counters_file_ == NULL) ?
343 NULL : counters_file_->memory();
344 if (memory == NULL) {
345 printf("Could not map counters file %s\n", name);
346 exit(1);
347 }
348 counters_ = static_cast<CounterCollection*>(memory);
349 V8::SetCounterFunction(LookupCounter);
350 V8::SetCreateHistogramFunction(CreateHistogram);
351 V8::SetAddHistogramSampleFunction(AddHistogramSample);
352}
353
354
355int CounterMap::Hash(const char* name) {
356 int h = 0;
357 int c;
358 while ((c = *name++) != 0) {
359 h += h << 5;
360 h += c;
361 }
362 return h;
363}
364
365
366Counter* Shell::GetCounter(const char* name, bool is_histogram) {
367 Counter* counter = counter_map_->Lookup(name);
368
369 if (counter == NULL) {
370 counter = counters_->GetNextCounter();
371 if (counter != NULL) {
372 counter_map_->Set(name, counter);
373 counter->Bind(name, is_histogram);
374 }
375 } else {
376 ASSERT(counter->is_histogram() == is_histogram);
377 }
378 return counter;
379}
380
381
382int* Shell::LookupCounter(const char* name) {
383 Counter* counter = GetCounter(name, false);
384
385 if (counter != NULL) {
386 return counter->ptr();
387 } else {
388 return NULL;
389 }
390}
391
392
393void* Shell::CreateHistogram(const char* name,
394 int min,
395 int max,
396 size_t buckets) {
397 return GetCounter(name, true);
398}
399
400
401void Shell::AddHistogramSample(void* histogram, int sample) {
402 Counter* counter = reinterpret_cast<Counter*>(histogram);
403 counter->AddSample(sample);
404}
405
406
407void Shell::Initialize() {
408 Shell::counter_map_ = new CounterMap();
409 // Set up counters
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100410 if (i::StrLength(i::FLAG_map_counters) != 0)
Steve Blocka7e24c12009-10-30 11:49:00 +0000411 MapCounters(i::FLAG_map_counters);
412 if (i::FLAG_dump_counters) {
413 V8::SetCounterFunction(LookupCounter);
414 V8::SetCreateHistogramFunction(CreateHistogram);
415 V8::SetAddHistogramSampleFunction(AddHistogramSample);
416 }
417
418 // Initialize the global objects
419 HandleScope scope;
420 Handle<ObjectTemplate> global_template = ObjectTemplate::New();
421 global_template->Set(String::New("print"), FunctionTemplate::New(Print));
422 global_template->Set(String::New("write"), FunctionTemplate::New(Write));
423 global_template->Set(String::New("read"), FunctionTemplate::New(Read));
424 global_template->Set(String::New("readline"),
425 FunctionTemplate::New(ReadLine));
426 global_template->Set(String::New("load"), FunctionTemplate::New(Load));
427 global_template->Set(String::New("quit"), FunctionTemplate::New(Quit));
428 global_template->Set(String::New("version"), FunctionTemplate::New(Version));
429
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100430#ifdef LIVE_OBJECT_LIST
431 global_template->Set(String::New("lol_is_enabled"), Boolean::New(true));
432#else
433 global_template->Set(String::New("lol_is_enabled"), Boolean::New(false));
434#endif
435
Steve Blocka7e24c12009-10-30 11:49:00 +0000436 Handle<ObjectTemplate> os_templ = ObjectTemplate::New();
437 AddOSMethods(os_templ);
438 global_template->Set(String::New("os"), os_templ);
439
440 utility_context_ = Context::New(NULL, global_template);
441 utility_context_->SetSecurityToken(Undefined());
442 Context::Scope utility_scope(utility_context_);
443
444 i::JSArguments js_args = i::FLAG_js_arguments;
445 i::Handle<i::FixedArray> arguments_array =
Steve Block44f0eee2011-05-26 01:26:41 +0100446 FACTORY->NewFixedArray(js_args.argc());
Steve Blocka7e24c12009-10-30 11:49:00 +0000447 for (int j = 0; j < js_args.argc(); j++) {
448 i::Handle<i::String> arg =
Steve Block44f0eee2011-05-26 01:26:41 +0100449 FACTORY->NewStringFromUtf8(i::CStrVector(js_args[j]));
Steve Blocka7e24c12009-10-30 11:49:00 +0000450 arguments_array->set(j, *arg);
451 }
452 i::Handle<i::JSArray> arguments_jsarray =
Steve Block44f0eee2011-05-26 01:26:41 +0100453 FACTORY->NewJSArrayWithElements(arguments_array);
Steve Blocka7e24c12009-10-30 11:49:00 +0000454 global_template->Set(String::New("arguments"),
455 Utils::ToLocal(arguments_jsarray));
456
457#ifdef ENABLE_DEBUGGER_SUPPORT
458 // Install the debugger object in the utility scope
Steve Block44f0eee2011-05-26 01:26:41 +0100459 i::Debug* debug = i::Isolate::Current()->debug();
460 debug->Load();
461 i::Handle<i::JSObject> js_debug
462 = i::Handle<i::JSObject>(debug->debug_context()->global());
Steve Blocka7e24c12009-10-30 11:49:00 +0000463 utility_context_->Global()->Set(String::New("$debug"),
Steve Block44f0eee2011-05-26 01:26:41 +0100464 Utils::ToLocal(js_debug));
Steve Blocka7e24c12009-10-30 11:49:00 +0000465#endif
466
467 // Run the d8 shell utility script in the utility context
468 int source_index = i::NativesCollection<i::D8>::GetIndex("d8");
469 i::Vector<const char> shell_source
470 = i::NativesCollection<i::D8>::GetScriptSource(source_index);
471 i::Vector<const char> shell_source_name
472 = i::NativesCollection<i::D8>::GetScriptName(source_index);
473 Handle<String> source = String::New(shell_source.start(),
474 shell_source.length());
475 Handle<String> name = String::New(shell_source_name.start(),
476 shell_source_name.length());
477 Handle<Script> script = Script::Compile(source, name);
478 script->Run();
479
480 // Mark the d8 shell script as native to avoid it showing up as normal source
481 // in the debugger.
Steve Block6ded16b2010-05-10 14:33:55 +0100482 i::Handle<i::Object> compiled_script = Utils::OpenHandle(*script);
483 i::Handle<i::Script> script_object = compiled_script->IsJSFunction()
484 ? i::Handle<i::Script>(i::Script::cast(
485 i::JSFunction::cast(*compiled_script)->shared()->script()))
486 : i::Handle<i::Script>(i::Script::cast(
487 i::SharedFunctionInfo::cast(*compiled_script)->script()));
Steve Blocka7e24c12009-10-30 11:49:00 +0000488 script_object->set_type(i::Smi::FromInt(i::Script::TYPE_NATIVE));
489
490 // Create the evaluation context
491 evaluation_context_ = Context::New(NULL, global_template);
492 evaluation_context_->SetSecurityToken(Undefined());
493
494#ifdef ENABLE_DEBUGGER_SUPPORT
495 // Set the security token of the debug context to allow access.
Steve Block44f0eee2011-05-26 01:26:41 +0100496 debug->debug_context()->set_security_token(HEAP->undefined_value());
Steve Blocka7e24c12009-10-30 11:49:00 +0000497
498 // Start the debugger agent if requested.
499 if (i::FLAG_debugger_agent) {
Iain Merrick9ac36c92010-09-13 15:29:50 +0100500 v8::Debug::EnableAgent("d8 shell", i::FLAG_debugger_port, true);
Steve Blocka7e24c12009-10-30 11:49:00 +0000501 }
502
503 // Start the in-process debugger if requested.
504 if (i::FLAG_debugger && !i::FLAG_debugger_agent) {
505 v8::Debug::SetDebugEventListener(HandleDebugEvent);
506 }
507#endif
508}
509
510
511void Shell::OnExit() {
512 if (i::FLAG_dump_counters) {
513 ::printf("+----------------------------------------+-------------+\n");
514 ::printf("| Name | Value |\n");
515 ::printf("+----------------------------------------+-------------+\n");
516 for (CounterMap::Iterator i(counter_map_); i.More(); i.Next()) {
517 Counter* counter = i.CurrentValue();
518 if (counter->is_histogram()) {
519 ::printf("| c:%-36s | %11i |\n", i.CurrentKey(), counter->count());
520 ::printf("| t:%-36s | %11i |\n",
521 i.CurrentKey(),
522 counter->sample_total());
523 } else {
524 ::printf("| %-38s | %11i |\n", i.CurrentKey(), counter->count());
525 }
526 }
527 ::printf("+----------------------------------------+-------------+\n");
528 }
529 if (counters_file_ != NULL)
530 delete counters_file_;
531}
532
533
534static char* ReadChars(const char* name, int* size_out) {
535 v8::Unlocker unlocker; // Release the V8 lock while reading files.
536 FILE* file = i::OS::FOpen(name, "rb");
537 if (file == NULL) return NULL;
538
539 fseek(file, 0, SEEK_END);
540 int size = ftell(file);
541 rewind(file);
542
543 char* chars = new char[size + 1];
544 chars[size] = '\0';
545 for (int i = 0; i < size;) {
546 int read = fread(&chars[i], 1, size - i, file);
547 i += read;
548 }
549 fclose(file);
550 *size_out = size;
551 return chars;
552}
553
554
555static char* ReadToken(char* data, char token) {
556 char* next = i::OS::StrChr(data, token);
557 if (next != NULL) {
558 *next = '\0';
559 return (next + 1);
560 }
561
562 return NULL;
563}
564
565
566static char* ReadLine(char* data) {
567 return ReadToken(data, '\n');
568}
569
570
571static char* ReadWord(char* data) {
572 return ReadToken(data, ' ');
573}
574
575
576// Reads a file into a v8 string.
577Handle<String> Shell::ReadFile(const char* name) {
578 int size = 0;
579 char* chars = ReadChars(name, &size);
580 if (chars == NULL) return Handle<String>();
581 Handle<String> result = String::New(chars);
582 delete[] chars;
583 return result;
584}
585
586
587void Shell::RunShell() {
588 LineEditor* editor = LineEditor::Get();
589 printf("V8 version %s [console: %s]\n", V8::GetVersion(), editor->name());
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100590 if (i::FLAG_debugger) {
591 printf("JavaScript debugger enabled\n");
592 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000593 editor->Open();
594 while (true) {
595 Locker locker;
596 HandleScope handle_scope;
597 Context::Scope context_scope(evaluation_context_);
598 i::SmartPointer<char> input = editor->Prompt(Shell::kPrompt);
599 if (input.is_empty())
600 break;
601 editor->AddHistory(*input);
602 Handle<String> name = String::New("(d8)");
603 ExecuteString(String::New(*input), name, true, true);
604 }
605 editor->Close();
606 printf("\n");
607}
608
609
610class ShellThread : public i::Thread {
611 public:
Steve Block44f0eee2011-05-26 01:26:41 +0100612 ShellThread(i::Isolate* isolate, int no, i::Vector<const char> files)
613 : Thread(isolate, "d8:ShellThread"),
Steve Block9fac8402011-05-12 15:51:54 +0100614 no_(no), files_(files) { }
Steve Blocka7e24c12009-10-30 11:49:00 +0000615 virtual void Run();
616 private:
617 int no_;
618 i::Vector<const char> files_;
619};
620
621
622void ShellThread::Run() {
623 // Prepare the context for this thread.
624 Locker locker;
625 HandleScope scope;
626 Handle<ObjectTemplate> global_template = ObjectTemplate::New();
627 global_template->Set(String::New("print"),
628 FunctionTemplate::New(Shell::Print));
629 global_template->Set(String::New("write"),
630 FunctionTemplate::New(Shell::Write));
631 global_template->Set(String::New("read"),
632 FunctionTemplate::New(Shell::Read));
633 global_template->Set(String::New("readline"),
634 FunctionTemplate::New(Shell::ReadLine));
635 global_template->Set(String::New("load"),
636 FunctionTemplate::New(Shell::Load));
637 global_template->Set(String::New("yield"),
638 FunctionTemplate::New(Shell::Yield));
639 global_template->Set(String::New("version"),
640 FunctionTemplate::New(Shell::Version));
641
642 char* ptr = const_cast<char*>(files_.start());
643 while ((ptr != NULL) && (*ptr != '\0')) {
644 // For each newline-separated line.
645 char* next_line = ReadLine(ptr);
646
647 if (*ptr == '#') {
648 // Skip comment lines.
649 ptr = next_line;
650 continue;
651 }
652
653 Persistent<Context> thread_context = Context::New(NULL, global_template);
654 thread_context->SetSecurityToken(Undefined());
655 Context::Scope context_scope(thread_context);
656
657 while ((ptr != NULL) && (*ptr != '\0')) {
658 char* filename = ptr;
659 ptr = ReadWord(ptr);
660
661 // Skip empty strings.
662 if (strlen(filename) == 0) {
663 break;
664 }
665
666 Handle<String> str = Shell::ReadFile(filename);
667 if (str.IsEmpty()) {
668 printf("WARNING: %s not found\n", filename);
669 break;
670 }
671
672 Shell::ExecuteString(str, String::New(filename), false, false);
673 }
674
675 thread_context.Dispose();
676 ptr = next_line;
677 }
678}
679
680
681int Shell::Main(int argc, char* argv[]) {
682 i::FlagList::SetFlagsFromCommandLine(&argc, argv, true);
683 if (i::FLAG_help) {
684 return 1;
685 }
686 Initialize();
687 bool run_shell = (argc == 1);
688
689 // Default use preemption if threads are created.
690 bool use_preemption = true;
691
692 // Default to use lowest possible thread preemption interval to test as many
693 // edgecases as possible.
694 int preemption_interval = 1;
695
696 i::List<i::Thread*> threads(1);
697
698 {
699 // Acquire the V8 lock once initialization has finished. Since the thread
700 // below may spawn new threads accessing V8 holding the V8 lock here is
701 // mandatory.
702 Locker locker;
703 Context::Scope context_scope(evaluation_context_);
704 for (int i = 1; i < argc; i++) {
705 char* str = argv[i];
706 if (strcmp(str, "--shell") == 0) {
707 run_shell = true;
708 } else if (strcmp(str, "--preemption") == 0) {
709 use_preemption = true;
710 } else if (strcmp(str, "--no-preemption") == 0) {
711 use_preemption = false;
712 } else if (strcmp(str, "--preemption-interval") == 0) {
713 if (i + 1 < argc) {
714 char* end = NULL;
715 preemption_interval = strtol(argv[++i], &end, 10); // NOLINT
716 if (preemption_interval <= 0 || *end != '\0' || errno == ERANGE) {
717 printf("Invalid value for --preemption-interval '%s'\n", argv[i]);
718 return 1;
719 }
720 } else {
721 printf("Missing value for --preemption-interval\n");
722 return 1;
723 }
724 } else if (strcmp(str, "-f") == 0) {
725 // Ignore any -f flags for compatibility with other stand-alone
726 // JavaScript engines.
727 continue;
728 } else if (strncmp(str, "--", 2) == 0) {
729 printf("Warning: unknown flag %s.\nTry --help for options\n", str);
730 } else if (strcmp(str, "-e") == 0 && i + 1 < argc) {
731 // Execute argument given to -e option directly.
732 v8::HandleScope handle_scope;
733 v8::Handle<v8::String> file_name = v8::String::New("unnamed");
734 v8::Handle<v8::String> source = v8::String::New(argv[i + 1]);
735 if (!ExecuteString(source, file_name, false, true)) {
736 OnExit();
737 return 1;
738 }
739 i++;
740 } else if (strcmp(str, "-p") == 0 && i + 1 < argc) {
741 int size = 0;
742 const char* files = ReadChars(argv[++i], &size);
743 if (files == NULL) return 1;
744 ShellThread* thread =
Steve Block44f0eee2011-05-26 01:26:41 +0100745 new ShellThread(i::Isolate::Current(),
746 threads.length(),
Steve Blocka7e24c12009-10-30 11:49:00 +0000747 i::Vector<const char>(files, size));
748 thread->Start();
749 threads.Add(thread);
750 } else {
751 // Use all other arguments as names of files to load and run.
752 HandleScope handle_scope;
753 Handle<String> file_name = v8::String::New(str);
754 Handle<String> source = ReadFile(str);
755 if (source.IsEmpty()) {
756 printf("Error reading '%s'\n", str);
757 return 1;
758 }
759 if (!ExecuteString(source, file_name, false, true)) {
760 OnExit();
761 return 1;
762 }
763 }
764 }
765
766 // Start preemption if threads have been created and preemption is enabled.
767 if (threads.length() > 0 && use_preemption) {
768 Locker::StartPreemption(preemption_interval);
769 }
770
771#ifdef ENABLE_DEBUGGER_SUPPORT
772 // Run the remote debugger if requested.
773 if (i::FLAG_remote_debugger) {
774 RunRemoteDebugger(i::FLAG_debugger_port);
775 return 0;
776 }
777#endif
778 }
779 if (run_shell)
780 RunShell();
781 for (int i = 0; i < threads.length(); i++) {
782 i::Thread* thread = threads[i];
783 thread->Join();
784 delete thread;
785 }
786 OnExit();
787 return 0;
788}
789
790
791} // namespace v8
792
793
794int main(int argc, char* argv[]) {
795 return v8::Shell::Main(argc, argv);
796}