1// Copyright 2012 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#ifdef USING_V8_SHARED  // Defined when linking against shared lib on Windows.
30#define V8_SHARED
31#endif
32
33#ifdef COMPRESS_STARTUP_DATA_BZ2
34#include <bzlib.h>
35#endif
36
37#include <errno.h>
38#include <stdlib.h>
39#include <string.h>
40#include <sys/stat.h>
41
42#ifdef V8_SHARED
43#include <assert.h>
44#include "../include/v8-testing.h"
45#endif  // V8_SHARED
46
47#include "d8.h"
48
49#ifndef V8_SHARED
50#include "api.h"
51#include "checks.h"
52#include "d8-debug.h"
53#include "debug.h"
54#include "natives.h"
55#include "platform.h"
56#include "v8.h"
57#endif  // V8_SHARED
58
59#if !defined(_WIN32) && !defined(_WIN64)
60#include <unistd.h>  // NOLINT
61#endif
62
63#ifndef ASSERT
64#define ASSERT(condition) assert(condition)
65#endif
66
67namespace v8 {
68
69LineEditor *LineEditor::first_ = NULL;
70
71
72LineEditor::LineEditor(Type type, const char* name)
73    : type_(type),
74      name_(name),
75      next_(first_) {
76  first_ = this;
77}
78
79
80LineEditor* LineEditor::Get() {
81  LineEditor* current = first_;
82  LineEditor* best = current;
83  while (current != NULL) {
84    if (current->type_ > best->type_)
85      best = current;
86    current = current->next_;
87  }
88  return best;
89}
90
91
92class DumbLineEditor: public LineEditor {
93 public:
94  DumbLineEditor() : LineEditor(LineEditor::DUMB, "dumb") { }
95  virtual Handle<String> Prompt(const char* prompt);
96};
97
98
99static DumbLineEditor dumb_line_editor;
100
101
102Handle<String> DumbLineEditor::Prompt(const char* prompt) {
103  printf("%s", prompt);
104  return Shell::ReadFromStdin();
105}
106
107
108#ifndef V8_SHARED
109CounterMap* Shell::counter_map_;
110i::OS::MemoryMappedFile* Shell::counters_file_ = NULL;
111CounterCollection Shell::local_counters_;
112CounterCollection* Shell::counters_ = &local_counters_;
113i::Mutex* Shell::context_mutex_(i::OS::CreateMutex());
114Persistent<Context> Shell::utility_context_;
115#endif  // V8_SHARED
116
117LineEditor* Shell::console = NULL;
118Persistent<Context> Shell::evaluation_context_;
119ShellOptions Shell::options;
120const char* Shell::kPrompt = "d8> ";
121
122
123const int MB = 1024 * 1024;
124
125
126#ifndef V8_SHARED
127bool CounterMap::Match(void* key1, void* key2) {
128  const char* name1 = reinterpret_cast<const char*>(key1);
129  const char* name2 = reinterpret_cast<const char*>(key2);
130  return strcmp(name1, name2) == 0;
131}
132#endif  // V8_SHARED
133
134
135// Converts a V8 value to a C string.
136const char* Shell::ToCString(const v8::String::Utf8Value& value) {
137  return *value ? *value : "<string conversion failed>";
138}
139
140
141// Executes a string within the current v8 context.
142bool Shell::ExecuteString(Handle<String> source,
143                          Handle<Value> name,
144                          bool print_result,
145                          bool report_exceptions) {
146#if !defined(V8_SHARED) && defined(ENABLE_DEBUGGER_SUPPORT)
147  bool FLAG_debugger = i::FLAG_debugger;
148#else
149  bool FLAG_debugger = false;
150#endif  // !V8_SHARED && ENABLE_DEBUGGER_SUPPORT
151  HandleScope handle_scope;
152  TryCatch try_catch;
153  options.script_executed = true;
154  if (FLAG_debugger) {
155    // When debugging make exceptions appear to be uncaught.
156    try_catch.SetVerbose(true);
157  }
158  Handle<Script> script = Script::Compile(source, name);
159  if (script.IsEmpty()) {
160    // Print errors that happened during compilation.
161    if (report_exceptions && !FLAG_debugger)
162      ReportException(&try_catch);
163    return false;
164  } else {
165    Handle<Value> result = script->Run();
166    if (result.IsEmpty()) {
167      ASSERT(try_catch.HasCaught());
168      // Print errors that happened during execution.
169      if (report_exceptions && !FLAG_debugger)
170        ReportException(&try_catch);
171      return false;
172    } else {
173      ASSERT(!try_catch.HasCaught());
174      if (print_result && !result->IsUndefined()) {
175        // If all went well and the result wasn't undefined then print
176        // the returned value.
177        v8::String::Utf8Value str(result);
178        size_t count = fwrite(*str, sizeof(**str), str.length(), stdout);
179        (void) count;  // Silence GCC-4.5.x "unused result" warning.
180        printf("\n");
181      }
182      return true;
183    }
184  }
185}
186
187
188Handle<Value> Shell::Print(const Arguments& args) {
189  Handle<Value> val = Write(args);
190  printf("\n");
191  fflush(stdout);
192  return val;
193}
194
195
196Handle<Value> Shell::Write(const Arguments& args) {
197  for (int i = 0; i < args.Length(); i++) {
198    HandleScope handle_scope;
199    if (i != 0) {
200      printf(" ");
201    }
202    v8::String::Utf8Value str(args[i]);
203    int n = static_cast<int>(fwrite(*str, sizeof(**str), str.length(), stdout));
204    if (n != str.length()) {
205      printf("Error in fwrite\n");
206      Exit(1);
207    }
208  }
209  return Undefined();
210}
211
212
213Handle<Value> Shell::EnableProfiler(const Arguments& args) {
214  V8::ResumeProfiler();
215  return Undefined();
216}
217
218
219Handle<Value> Shell::DisableProfiler(const Arguments& args) {
220  V8::PauseProfiler();
221  return Undefined();
222}
223
224
225Handle<Value> Shell::Read(const Arguments& args) {
226  String::Utf8Value file(args[0]);
227  if (*file == NULL) {
228    return ThrowException(String::New("Error loading file"));
229  }
230  Handle<String> source = ReadFile(*file);
231  if (source.IsEmpty()) {
232    return ThrowException(String::New("Error loading file"));
233  }
234  return source;
235}
236
237
238Handle<String> Shell::ReadFromStdin() {
239  static const int kBufferSize = 256;
240  char buffer[kBufferSize];
241  Handle<String> accumulator = String::New("");
242  int length;
243  while (true) {
244    // Continue reading if the line ends with an escape '\\' or the line has
245    // not been fully read into the buffer yet (does not end with '\n').
246    // If fgets gets an error, just give up.
247    char* input = NULL;
248    {  // Release lock for blocking input.
249      Unlocker unlock(Isolate::GetCurrent());
250      input = fgets(buffer, kBufferSize, stdin);
251    }
252    if (input == NULL) return Handle<String>();
253    length = static_cast<int>(strlen(buffer));
254    if (length == 0) {
255      return accumulator;
256    } else if (buffer[length-1] != '\n') {
257      accumulator = String::Concat(accumulator, String::New(buffer, length));
258    } else if (length > 1 && buffer[length-2] == '\\') {
259      buffer[length-2] = '\n';
260      accumulator = String::Concat(accumulator, String::New(buffer, length-1));
261    } else {
262      return String::Concat(accumulator, String::New(buffer, length-1));
263    }
264  }
265}
266
267
268Handle<Value> Shell::Load(const Arguments& args) {
269  for (int i = 0; i < args.Length(); i++) {
270    HandleScope handle_scope;
271    String::Utf8Value file(args[i]);
272    if (*file == NULL) {
273      return ThrowException(String::New("Error loading file"));
274    }
275    Handle<String> source = ReadFile(*file);
276    if (source.IsEmpty()) {
277      return ThrowException(String::New("Error loading file"));
278    }
279    if (!ExecuteString(source, String::New(*file), false, true)) {
280      return ThrowException(String::New("Error executing file"));
281    }
282  }
283  return Undefined();
284}
285
286static size_t convertToUint(Local<Value> value_in, TryCatch* try_catch) {
287  if (value_in->IsUint32()) {
288    return value_in->Uint32Value();
289  }
290
291  Local<Value> number = value_in->ToNumber();
292  if (try_catch->HasCaught()) return 0;
293
294  ASSERT(number->IsNumber());
295  Local<Int32> int32 = number->ToInt32();
296  if (try_catch->HasCaught() || int32.IsEmpty()) return 0;
297
298  int32_t raw_value = int32->Int32Value();
299  if (try_catch->HasCaught()) return 0;
300
301  if (raw_value < 0) {
302    ThrowException(String::New("Array length must not be negative."));
303    return 0;
304  }
305
306  static const int kMaxLength = 0x3fffffff;
307#ifndef V8_SHARED
308  ASSERT(kMaxLength == i::ExternalArray::kMaxLength);
309#endif  // V8_SHARED
310  if (raw_value > static_cast<int32_t>(kMaxLength)) {
311    ThrowException(
312        String::New("Array length exceeds maximum length."));
313  }
314  return static_cast<size_t>(raw_value);
315}
316
317
318const char kArrayBufferReferencePropName[] = "_is_array_buffer_";
319const char kArrayBufferMarkerPropName[] = "_array_buffer_ref_";
320
321
322Handle<Value> Shell::CreateExternalArray(const Arguments& args,
323                                         ExternalArrayType type,
324                                         size_t element_size) {
325  TryCatch try_catch;
326  bool is_array_buffer_construct = element_size == 0;
327  if (is_array_buffer_construct) {
328    type = v8::kExternalByteArray;
329    element_size = 1;
330  }
331  ASSERT(element_size == 1 || element_size == 2 || element_size == 4 ||
332         element_size == 8);
333  if (args.Length() == 0) {
334    return ThrowException(
335        String::New("Array constructor must have at least one "
336                    "parameter."));
337  }
338  bool first_arg_is_array_buffer =
339      args[0]->IsObject() &&
340      args[0]->ToObject()->Get(
341          String::New(kArrayBufferMarkerPropName))->IsTrue();
342  // Currently, only the following constructors are supported:
343  //   TypedArray(unsigned long length)
344  //   TypedArray(ArrayBuffer buffer,
345  //              optional unsigned long byteOffset,
346  //              optional unsigned long length)
347  if (args.Length() > 3) {
348    return ThrowException(
349        String::New("Array constructor from ArrayBuffer must "
350                    "have 1-3 parameters."));
351  }
352
353  Local<Value> length_value = (args.Length() < 3)
354      ? (first_arg_is_array_buffer
355         ? args[0]->ToObject()->Get(String::New("length"))
356         : args[0])
357      : args[2];
358  size_t length = convertToUint(length_value, &try_catch);
359  if (try_catch.HasCaught()) return try_catch.Exception();
360
361  void* data = NULL;
362  size_t offset = 0;
363
364  Handle<Object> array = Object::New();
365  if (first_arg_is_array_buffer) {
366    Handle<Object> derived_from = args[0]->ToObject();
367    data = derived_from->GetIndexedPropertiesExternalArrayData();
368
369    size_t array_buffer_length = convertToUint(
370        derived_from->Get(String::New("length")),
371        &try_catch);
372    if (try_catch.HasCaught()) return try_catch.Exception();
373
374    if (data == NULL && array_buffer_length != 0) {
375      return ThrowException(
376          String::New("ArrayBuffer doesn't have data"));
377    }
378
379    if (args.Length() > 1) {
380      offset = convertToUint(args[1], &try_catch);
381      if (try_catch.HasCaught()) return try_catch.Exception();
382
383      // The given byteOffset must be a multiple of the element size of the
384      // specific type, otherwise an exception is raised.
385      if (offset % element_size != 0) {
386        return ThrowException(
387            String::New("offset must be multiple of element_size"));
388      }
389    }
390
391    if (offset > array_buffer_length) {
392      return ThrowException(
393          String::New("byteOffset must be less than ArrayBuffer length."));
394    }
395
396    if (args.Length() == 2) {
397      // If length is not explicitly specified, the length of the ArrayBuffer
398      // minus the byteOffset must be a multiple of the element size of the
399      // specific type, or an exception is raised.
400      length = array_buffer_length - offset;
401    }
402
403    if (args.Length() != 3) {
404      if (length % element_size != 0) {
405        return ThrowException(
406            String::New("ArrayBuffer length minus the byteOffset must be a "
407                        "multiple of the element size"));
408      }
409      length /= element_size;
410    }
411
412    // If a given byteOffset and length references an area beyond the end of
413    // the ArrayBuffer an exception is raised.
414    if (offset + (length * element_size) > array_buffer_length) {
415      return ThrowException(
416          String::New("length references an area beyond the end of the "
417                      "ArrayBuffer"));
418    }
419
420    // Hold a reference to the ArrayBuffer so its buffer doesn't get collected.
421    array->Set(String::New(kArrayBufferReferencePropName), args[0], ReadOnly);
422  }
423
424  if (is_array_buffer_construct) {
425    array->Set(String::New(kArrayBufferMarkerPropName), True(), ReadOnly);
426  }
427
428  Persistent<Object> persistent_array = Persistent<Object>::New(array);
429  persistent_array.MakeWeak(data, ExternalArrayWeakCallback);
430  persistent_array.MarkIndependent();
431  if (data == NULL && length != 0) {
432    data = calloc(length, element_size);
433    if (data == NULL) {
434      return ThrowException(String::New("Memory allocation failed."));
435    }
436  }
437
438  array->SetIndexedPropertiesToExternalArrayData(
439      reinterpret_cast<uint8_t*>(data) + offset, type,
440      static_cast<int>(length));
441  array->Set(String::New("length"),
442             Int32::New(static_cast<int32_t>(length)), ReadOnly);
443  array->Set(String::New("BYTES_PER_ELEMENT"),
444             Int32::New(static_cast<int32_t>(element_size)));
445  return array;
446}
447
448
449void Shell::ExternalArrayWeakCallback(Persistent<Value> object, void* data) {
450  HandleScope scope;
451  Handle<String> prop_name = String::New(kArrayBufferReferencePropName);
452  Handle<Object> converted_object = object->ToObject();
453  Local<Value> prop_value = converted_object->Get(prop_name);
454  if (data != NULL && !prop_value->IsObject()) {
455    free(data);
456  }
457  object.Dispose();
458}
459
460
461Handle<Value> Shell::ArrayBuffer(const Arguments& args) {
462  return CreateExternalArray(args, v8::kExternalByteArray, 0);
463}
464
465
466Handle<Value> Shell::Int8Array(const Arguments& args) {
467  return CreateExternalArray(args, v8::kExternalByteArray, sizeof(int8_t));
468}
469
470
471Handle<Value> Shell::Uint8Array(const Arguments& args) {
472  return CreateExternalArray(args, kExternalUnsignedByteArray, sizeof(uint8_t));
473}
474
475
476Handle<Value> Shell::Int16Array(const Arguments& args) {
477  return CreateExternalArray(args, kExternalShortArray, sizeof(int16_t));
478}
479
480
481Handle<Value> Shell::Uint16Array(const Arguments& args) {
482  return CreateExternalArray(args, kExternalUnsignedShortArray,
483                             sizeof(uint16_t));
484}
485
486
487Handle<Value> Shell::Int32Array(const Arguments& args) {
488  return CreateExternalArray(args, kExternalIntArray, sizeof(int32_t));
489}
490
491
492Handle<Value> Shell::Uint32Array(const Arguments& args) {
493  return CreateExternalArray(args, kExternalUnsignedIntArray, sizeof(uint32_t));
494}
495
496
497Handle<Value> Shell::Float32Array(const Arguments& args) {
498  return CreateExternalArray(args, kExternalFloatArray,
499                             sizeof(float));  // NOLINT
500}
501
502
503Handle<Value> Shell::Float64Array(const Arguments& args) {
504  return CreateExternalArray(args, kExternalDoubleArray,
505                             sizeof(double));  // NOLINT
506}
507
508
509Handle<Value> Shell::PixelArray(const Arguments& args) {
510  return CreateExternalArray(args, kExternalPixelArray, sizeof(uint8_t));
511}
512
513
514Handle<Value> Shell::Yield(const Arguments& args) {
515  v8::Unlocker unlocker;
516  return Undefined();
517}
518
519
520Handle<Value> Shell::Quit(const Arguments& args) {
521  int exit_code = args[0]->Int32Value();
522#ifndef V8_SHARED
523  OnExit();
524#endif  // V8_SHARED
525  exit(exit_code);
526  return Undefined();
527}
528
529
530Handle<Value> Shell::Version(const Arguments& args) {
531  return String::New(V8::GetVersion());
532}
533
534
535void Shell::ReportException(v8::TryCatch* try_catch) {
536  HandleScope handle_scope;
537#if !defined(V8_SHARED) && defined(ENABLE_DEBUGGER_SUPPORT)
538  bool enter_context = !Context::InContext();
539  if (enter_context) utility_context_->Enter();
540#endif  // !V8_SHARED && ENABLE_DEBUGGER_SUPPORT
541  v8::String::Utf8Value exception(try_catch->Exception());
542  const char* exception_string = ToCString(exception);
543  Handle<Message> message = try_catch->Message();
544  if (message.IsEmpty()) {
545    // V8 didn't provide any extra information about this error; just
546    // print the exception.
547    printf("%s\n", exception_string);
548  } else {
549    // Print (filename):(line number): (message).
550    v8::String::Utf8Value filename(message->GetScriptResourceName());
551    const char* filename_string = ToCString(filename);
552    int linenum = message->GetLineNumber();
553    printf("%s:%i: %s\n", filename_string, linenum, exception_string);
554    // Print line of source code.
555    v8::String::Utf8Value sourceline(message->GetSourceLine());
556    const char* sourceline_string = ToCString(sourceline);
557    printf("%s\n", sourceline_string);
558    // Print wavy underline (GetUnderline is deprecated).
559    int start = message->GetStartColumn();
560    for (int i = 0; i < start; i++) {
561      printf(" ");
562    }
563    int end = message->GetEndColumn();
564    for (int i = start; i < end; i++) {
565      printf("^");
566    }
567    printf("\n");
568    v8::String::Utf8Value stack_trace(try_catch->StackTrace());
569    if (stack_trace.length() > 0) {
570      const char* stack_trace_string = ToCString(stack_trace);
571      printf("%s\n", stack_trace_string);
572    }
573  }
574  printf("\n");
575#if !defined(V8_SHARED) && defined(ENABLE_DEBUGGER_SUPPORT)
576  if (enter_context) utility_context_->Exit();
577#endif  // !V8_SHARED && ENABLE_DEBUGGER_SUPPORT
578}
579
580
581#ifndef V8_SHARED
582Handle<Array> Shell::GetCompletions(Handle<String> text, Handle<String> full) {
583  HandleScope handle_scope;
584  Context::Scope context_scope(utility_context_);
585  Handle<Object> global = utility_context_->Global();
586  Handle<Value> fun = global->Get(String::New("GetCompletions"));
587  static const int kArgc = 3;
588  Handle<Value> argv[kArgc] = { evaluation_context_->Global(), text, full };
589  Handle<Value> val = Handle<Function>::Cast(fun)->Call(global, kArgc, argv);
590  return handle_scope.Close(Handle<Array>::Cast(val));
591}
592
593
594#ifdef ENABLE_DEBUGGER_SUPPORT
595Handle<Object> Shell::DebugMessageDetails(Handle<String> message) {
596  Context::Scope context_scope(utility_context_);
597  Handle<Object> global = utility_context_->Global();
598  Handle<Value> fun = global->Get(String::New("DebugMessageDetails"));
599  static const int kArgc = 1;
600  Handle<Value> argv[kArgc] = { message };
601  Handle<Value> val = Handle<Function>::Cast(fun)->Call(global, kArgc, argv);
602  return Handle<Object>::Cast(val);
603}
604
605
606Handle<Value> Shell::DebugCommandToJSONRequest(Handle<String> command) {
607  Context::Scope context_scope(utility_context_);
608  Handle<Object> global = utility_context_->Global();
609  Handle<Value> fun = global->Get(String::New("DebugCommandToJSONRequest"));
610  static const int kArgc = 1;
611  Handle<Value> argv[kArgc] = { command };
612  Handle<Value> val = Handle<Function>::Cast(fun)->Call(global, kArgc, argv);
613  return val;
614}
615
616
617void Shell::DispatchDebugMessages() {
618  v8::Context::Scope scope(Shell::evaluation_context_);
619  v8::Debug::ProcessDebugMessages();
620}
621#endif  // ENABLE_DEBUGGER_SUPPORT
622#endif  // V8_SHARED
623
624
625#ifndef V8_SHARED
626int32_t* Counter::Bind(const char* name, bool is_histogram) {
627  int i;
628  for (i = 0; i < kMaxNameSize - 1 && name[i]; i++)
629    name_[i] = static_cast<char>(name[i]);
630  name_[i] = '\0';
631  is_histogram_ = is_histogram;
632  return ptr();
633}
634
635
636void Counter::AddSample(int32_t sample) {
637  count_++;
638  sample_total_ += sample;
639}
640
641
642CounterCollection::CounterCollection() {
643  magic_number_ = 0xDEADFACE;
644  max_counters_ = kMaxCounters;
645  max_name_size_ = Counter::kMaxNameSize;
646  counters_in_use_ = 0;
647}
648
649
650Counter* CounterCollection::GetNextCounter() {
651  if (counters_in_use_ == kMaxCounters) return NULL;
652  return &counters_[counters_in_use_++];
653}
654
655
656void Shell::MapCounters(const char* name) {
657  counters_file_ = i::OS::MemoryMappedFile::create(
658      name, sizeof(CounterCollection), &local_counters_);
659  void* memory = (counters_file_ == NULL) ?
660      NULL : counters_file_->memory();
661  if (memory == NULL) {
662    printf("Could not map counters file %s\n", name);
663    Exit(1);
664  }
665  counters_ = static_cast<CounterCollection*>(memory);
666  V8::SetCounterFunction(LookupCounter);
667  V8::SetCreateHistogramFunction(CreateHistogram);
668  V8::SetAddHistogramSampleFunction(AddHistogramSample);
669}
670
671
672int CounterMap::Hash(const char* name) {
673  int h = 0;
674  int c;
675  while ((c = *name++) != 0) {
676    h += h << 5;
677    h += c;
678  }
679  return h;
680}
681
682
683Counter* Shell::GetCounter(const char* name, bool is_histogram) {
684  Counter* counter = counter_map_->Lookup(name);
685
686  if (counter == NULL) {
687    counter = counters_->GetNextCounter();
688    if (counter != NULL) {
689      counter_map_->Set(name, counter);
690      counter->Bind(name, is_histogram);
691    }
692  } else {
693    ASSERT(counter->is_histogram() == is_histogram);
694  }
695  return counter;
696}
697
698
699int* Shell::LookupCounter(const char* name) {
700  Counter* counter = GetCounter(name, false);
701
702  if (counter != NULL) {
703    return counter->ptr();
704  } else {
705    return NULL;
706  }
707}
708
709
710void* Shell::CreateHistogram(const char* name,
711                             int min,
712                             int max,
713                             size_t buckets) {
714  return GetCounter(name, true);
715}
716
717
718void Shell::AddHistogramSample(void* histogram, int sample) {
719  Counter* counter = reinterpret_cast<Counter*>(histogram);
720  counter->AddSample(sample);
721}
722
723
724void Shell::InstallUtilityScript() {
725  Locker lock;
726  HandleScope scope;
727  // If we use the utility context, we have to set the security tokens so that
728  // utility, evaluation and debug context can all access each other.
729  utility_context_->SetSecurityToken(Undefined());
730  evaluation_context_->SetSecurityToken(Undefined());
731  Context::Scope utility_scope(utility_context_);
732
733#ifdef ENABLE_DEBUGGER_SUPPORT
734  if (i::FLAG_debugger) printf("JavaScript debugger enabled\n");
735  // Install the debugger object in the utility scope
736  i::Debug* debug = i::Isolate::Current()->debug();
737  debug->Load();
738  i::Handle<i::JSObject> js_debug
739      = i::Handle<i::JSObject>(debug->debug_context()->global());
740  utility_context_->Global()->Set(String::New("$debug"),
741                                  Utils::ToLocal(js_debug));
742  debug->debug_context()->set_security_token(HEAP->undefined_value());
743#endif  // ENABLE_DEBUGGER_SUPPORT
744
745  // Run the d8 shell utility script in the utility context
746  int source_index = i::NativesCollection<i::D8>::GetIndex("d8");
747  i::Vector<const char> shell_source =
748      i::NativesCollection<i::D8>::GetRawScriptSource(source_index);
749  i::Vector<const char> shell_source_name =
750      i::NativesCollection<i::D8>::GetScriptName(source_index);
751  Handle<String> source = String::New(shell_source.start(),
752      shell_source.length());
753  Handle<String> name = String::New(shell_source_name.start(),
754      shell_source_name.length());
755  Handle<Script> script = Script::Compile(source, name);
756  script->Run();
757  // Mark the d8 shell script as native to avoid it showing up as normal source
758  // in the debugger.
759  i::Handle<i::Object> compiled_script = Utils::OpenHandle(*script);
760  i::Handle<i::Script> script_object = compiled_script->IsJSFunction()
761      ? i::Handle<i::Script>(i::Script::cast(
762          i::JSFunction::cast(*compiled_script)->shared()->script()))
763      : i::Handle<i::Script>(i::Script::cast(
764          i::SharedFunctionInfo::cast(*compiled_script)->script()));
765  script_object->set_type(i::Smi::FromInt(i::Script::TYPE_NATIVE));
766
767#ifdef ENABLE_DEBUGGER_SUPPORT
768  // Start the in-process debugger if requested.
769  if (i::FLAG_debugger && !i::FLAG_debugger_agent) {
770    v8::Debug::SetDebugEventListener(HandleDebugEvent);
771  }
772#endif  // ENABLE_DEBUGGER_SUPPORT
773}
774#endif  // V8_SHARED
775
776
777#ifdef COMPRESS_STARTUP_DATA_BZ2
778class BZip2Decompressor : public v8::StartupDataDecompressor {
779 public:
780  virtual ~BZip2Decompressor() { }
781
782 protected:
783  virtual int DecompressData(char* raw_data,
784                             int* raw_data_size,
785                             const char* compressed_data,
786                             int compressed_data_size) {
787    ASSERT_EQ(v8::StartupData::kBZip2,
788              v8::V8::GetCompressedStartupDataAlgorithm());
789    unsigned int decompressed_size = *raw_data_size;
790    int result =
791        BZ2_bzBuffToBuffDecompress(raw_data,
792                                   &decompressed_size,
793                                   const_cast<char*>(compressed_data),
794                                   compressed_data_size,
795                                   0, 1);
796    if (result == BZ_OK) {
797      *raw_data_size = decompressed_size;
798    }
799    return result;
800  }
801};
802#endif
803
804Handle<ObjectTemplate> Shell::CreateGlobalTemplate() {
805  Handle<ObjectTemplate> global_template = ObjectTemplate::New();
806  global_template->Set(String::New("print"), FunctionTemplate::New(Print));
807  global_template->Set(String::New("write"), FunctionTemplate::New(Write));
808  global_template->Set(String::New("read"), FunctionTemplate::New(Read));
809  global_template->Set(String::New("readbinary"),
810                       FunctionTemplate::New(ReadBinary));
811  global_template->Set(String::New("readline"),
812                       FunctionTemplate::New(ReadLine));
813  global_template->Set(String::New("load"), FunctionTemplate::New(Load));
814  global_template->Set(String::New("quit"), FunctionTemplate::New(Quit));
815  global_template->Set(String::New("version"), FunctionTemplate::New(Version));
816  global_template->Set(String::New("enableProfiler"),
817                       FunctionTemplate::New(EnableProfiler));
818  global_template->Set(String::New("disableProfiler"),
819                       FunctionTemplate::New(DisableProfiler));
820
821  // Bind the handlers for external arrays.
822  global_template->Set(String::New("ArrayBuffer"),
823                       FunctionTemplate::New(ArrayBuffer));
824  global_template->Set(String::New("Int8Array"),
825                       FunctionTemplate::New(Int8Array));
826  global_template->Set(String::New("Uint8Array"),
827                       FunctionTemplate::New(Uint8Array));
828  global_template->Set(String::New("Int16Array"),
829                       FunctionTemplate::New(Int16Array));
830  global_template->Set(String::New("Uint16Array"),
831                       FunctionTemplate::New(Uint16Array));
832  global_template->Set(String::New("Int32Array"),
833                       FunctionTemplate::New(Int32Array));
834  global_template->Set(String::New("Uint32Array"),
835                       FunctionTemplate::New(Uint32Array));
836  global_template->Set(String::New("Float32Array"),
837                       FunctionTemplate::New(Float32Array));
838  global_template->Set(String::New("Float64Array"),
839                       FunctionTemplate::New(Float64Array));
840  global_template->Set(String::New("PixelArray"),
841                       FunctionTemplate::New(PixelArray));
842
843#ifdef LIVE_OBJECT_LIST
844  global_template->Set(String::New("lol_is_enabled"), True());
845#else
846  global_template->Set(String::New("lol_is_enabled"), False());
847#endif
848
849#if !defined(V8_SHARED) && !defined(_WIN32) && !defined(_WIN64)
850  Handle<ObjectTemplate> os_templ = ObjectTemplate::New();
851  AddOSMethods(os_templ);
852  global_template->Set(String::New("os"), os_templ);
853#endif  // V8_SHARED
854
855  return global_template;
856}
857
858
859void Shell::Initialize() {
860#ifdef COMPRESS_STARTUP_DATA_BZ2
861  BZip2Decompressor startup_data_decompressor;
862  int bz2_result = startup_data_decompressor.Decompress();
863  if (bz2_result != BZ_OK) {
864    fprintf(stderr, "bzip error code: %d\n", bz2_result);
865    Exit(1);
866  }
867#endif
868
869#ifndef V8_SHARED
870  Shell::counter_map_ = new CounterMap();
871  // Set up counters
872  if (i::StrLength(i::FLAG_map_counters) != 0)
873    MapCounters(i::FLAG_map_counters);
874  if (i::FLAG_dump_counters) {
875    V8::SetCounterFunction(LookupCounter);
876    V8::SetCreateHistogramFunction(CreateHistogram);
877    V8::SetAddHistogramSampleFunction(AddHistogramSample);
878  }
879#endif  // V8_SHARED
880  if (options.test_shell) return;
881
882#ifndef V8_SHARED
883  Locker lock;
884  HandleScope scope;
885  Handle<ObjectTemplate> global_template = CreateGlobalTemplate();
886  utility_context_ = Context::New(NULL, global_template);
887
888#ifdef ENABLE_DEBUGGER_SUPPORT
889  // Start the debugger agent if requested.
890  if (i::FLAG_debugger_agent) {
891    v8::Debug::EnableAgent("d8 shell", i::FLAG_debugger_port, true);
892    v8::Debug::SetDebugMessageDispatchHandler(DispatchDebugMessages, true);
893  }
894#endif  // ENABLE_DEBUGGER_SUPPORT
895#endif  // V8_SHARED
896}
897
898
899Persistent<Context> Shell::CreateEvaluationContext() {
900#ifndef V8_SHARED
901  // This needs to be a critical section since this is not thread-safe
902  i::ScopedLock lock(context_mutex_);
903#endif  // V8_SHARED
904  // Initialize the global objects
905  Handle<ObjectTemplate> global_template = CreateGlobalTemplate();
906  Persistent<Context> context = Context::New(NULL, global_template);
907  ASSERT(!context.IsEmpty());
908  Context::Scope scope(context);
909
910#ifndef V8_SHARED
911  i::JSArguments js_args = i::FLAG_js_arguments;
912  i::Handle<i::FixedArray> arguments_array =
913      FACTORY->NewFixedArray(js_args.argc());
914  for (int j = 0; j < js_args.argc(); j++) {
915    i::Handle<i::String> arg =
916        FACTORY->NewStringFromUtf8(i::CStrVector(js_args[j]));
917    arguments_array->set(j, *arg);
918  }
919  i::Handle<i::JSArray> arguments_jsarray =
920      FACTORY->NewJSArrayWithElements(arguments_array);
921  context->Global()->Set(String::New("arguments"),
922                         Utils::ToLocal(arguments_jsarray));
923#endif  // V8_SHARED
924  return context;
925}
926
927
928void Shell::Exit(int exit_code) {
929  // Use _exit instead of exit to avoid races between isolate
930  // threads and static destructors.
931  fflush(stdout);
932  fflush(stderr);
933  _exit(exit_code);
934}
935
936
937#ifndef V8_SHARED
938struct CounterAndKey {
939  Counter* counter;
940  const char* key;
941};
942
943
944int CompareKeys(const void* a, const void* b) {
945  return strcmp(static_cast<const CounterAndKey*>(a)->key,
946                static_cast<const CounterAndKey*>(b)->key);
947}
948
949
950void Shell::OnExit() {
951  if (console != NULL) console->Close();
952  if (i::FLAG_dump_counters) {
953    int number_of_counters = 0;
954    for (CounterMap::Iterator i(counter_map_); i.More(); i.Next()) {
955      number_of_counters++;
956    }
957    CounterAndKey* counters = new CounterAndKey[number_of_counters];
958    int j = 0;
959    for (CounterMap::Iterator i(counter_map_); i.More(); i.Next(), j++) {
960      counters[j].counter = i.CurrentValue();
961      counters[j].key = i.CurrentKey();
962    }
963    qsort(counters, number_of_counters, sizeof(counters[0]), CompareKeys);
964    printf("+--------------------------------------------+-------------+\n");
965    printf("| Name                                       | Value       |\n");
966    printf("+--------------------------------------------+-------------+\n");
967    for (j = 0; j < number_of_counters; j++) {
968      Counter* counter = counters[j].counter;
969      const char* key = counters[j].key;
970      if (counter->is_histogram()) {
971        printf("| c:%-40s | %11i |\n", key, counter->count());
972        printf("| t:%-40s | %11i |\n", key, counter->sample_total());
973      } else {
974        printf("| %-42s | %11i |\n", key, counter->count());
975      }
976    }
977    printf("+--------------------------------------------+-------------+\n");
978    delete [] counters;
979  }
980  if (counters_file_ != NULL)
981    delete counters_file_;
982}
983#endif  // V8_SHARED
984
985
986static FILE* FOpen(const char* path, const char* mode) {
987#if defined(_MSC_VER) && (defined(_WIN32) || defined(_WIN64))
988  FILE* result;
989  if (fopen_s(&result, path, mode) == 0) {
990    return result;
991  } else {
992    return NULL;
993  }
994#else
995  FILE* file = fopen(path, mode);
996  if (file == NULL) return NULL;
997  struct stat file_stat;
998  if (fstat(fileno(file), &file_stat) != 0) return NULL;
999  bool is_regular_file = ((file_stat.st_mode & S_IFREG) != 0);
1000  if (is_regular_file) return file;
1001  fclose(file);
1002  return NULL;
1003#endif
1004}
1005
1006
1007static char* ReadChars(const char* name, int* size_out) {
1008  // Release the V8 lock while reading files.
1009  v8::Unlocker unlocker(Isolate::GetCurrent());
1010  FILE* file = FOpen(name, "rb");
1011  if (file == NULL) return NULL;
1012
1013  fseek(file, 0, SEEK_END);
1014  int size = ftell(file);
1015  rewind(file);
1016
1017  char* chars = new char[size + 1];
1018  chars[size] = '\0';
1019  for (int i = 0; i < size;) {
1020    int read = static_cast<int>(fread(&chars[i], 1, size - i, file));
1021    i += read;
1022  }
1023  fclose(file);
1024  *size_out = size;
1025  return chars;
1026}
1027
1028
1029Handle<Value> Shell::ReadBinary(const Arguments& args) {
1030  String::Utf8Value filename(args[0]);
1031  int size;
1032  if (*filename == NULL) {
1033    return ThrowException(String::New("Error loading file"));
1034  }
1035  char* chars = ReadChars(*filename, &size);
1036  if (chars == NULL) {
1037    return ThrowException(String::New("Error reading file"));
1038  }
1039  // We skip checking the string for UTF8 characters and use it raw as
1040  // backing store for the external string with 8-bit characters.
1041  BinaryResource* resource = new BinaryResource(chars, size);
1042  return String::NewExternal(resource);
1043}
1044
1045
1046#ifndef V8_SHARED
1047static char* ReadToken(char* data, char token) {
1048  char* next = i::OS::StrChr(data, token);
1049  if (next != NULL) {
1050    *next = '\0';
1051    return (next + 1);
1052  }
1053
1054  return NULL;
1055}
1056
1057
1058static char* ReadLine(char* data) {
1059  return ReadToken(data, '\n');
1060}
1061
1062
1063static char* ReadWord(char* data) {
1064  return ReadToken(data, ' ');
1065}
1066#endif  // V8_SHARED
1067
1068
1069// Reads a file into a v8 string.
1070Handle<String> Shell::ReadFile(const char* name) {
1071  int size = 0;
1072  char* chars = ReadChars(name, &size);
1073  if (chars == NULL) return Handle<String>();
1074  Handle<String> result = String::New(chars);
1075  delete[] chars;
1076  return result;
1077}
1078
1079
1080void Shell::RunShell() {
1081  Locker locker;
1082  Context::Scope context_scope(evaluation_context_);
1083  HandleScope outer_scope;
1084  Handle<String> name = String::New("(d8)");
1085  console = LineEditor::Get();
1086  printf("V8 version %s [console: %s]\n", V8::GetVersion(), console->name());
1087  console->Open();
1088  while (true) {
1089    HandleScope inner_scope;
1090    Handle<String> input = console->Prompt(Shell::kPrompt);
1091    if (input.IsEmpty()) break;
1092    ExecuteString(input, name, true, true);
1093  }
1094  printf("\n");
1095}
1096
1097
1098#ifndef V8_SHARED
1099class ShellThread : public i::Thread {
1100 public:
1101  // Takes ownership of the underlying char array of |files|.
1102  ShellThread(int no, char* files)
1103      : Thread("d8:ShellThread"),
1104        no_(no), files_(files) { }
1105
1106  ~ShellThread() {
1107    delete[] files_;
1108  }
1109
1110  virtual void Run();
1111 private:
1112  int no_;
1113  char* files_;
1114};
1115
1116
1117void ShellThread::Run() {
1118  char* ptr = files_;
1119  while ((ptr != NULL) && (*ptr != '\0')) {
1120    // For each newline-separated line.
1121    char* next_line = ReadLine(ptr);
1122
1123    if (*ptr == '#') {
1124      // Skip comment lines.
1125      ptr = next_line;
1126      continue;
1127    }
1128
1129    // Prepare the context for this thread.
1130    Locker locker;
1131    HandleScope outer_scope;
1132    Persistent<Context> thread_context = Shell::CreateEvaluationContext();
1133    Context::Scope context_scope(thread_context);
1134
1135    while ((ptr != NULL) && (*ptr != '\0')) {
1136      HandleScope inner_scope;
1137      char* filename = ptr;
1138      ptr = ReadWord(ptr);
1139
1140      // Skip empty strings.
1141      if (strlen(filename) == 0) {
1142        continue;
1143      }
1144
1145      Handle<String> str = Shell::ReadFile(filename);
1146      if (str.IsEmpty()) {
1147        printf("File '%s' not found\n", filename);
1148        Shell::Exit(1);
1149      }
1150
1151      Shell::ExecuteString(str, String::New(filename), false, false);
1152    }
1153
1154    thread_context.Dispose();
1155    ptr = next_line;
1156  }
1157}
1158#endif  // V8_SHARED
1159
1160
1161SourceGroup::~SourceGroup() {
1162#ifndef V8_SHARED
1163  delete next_semaphore_;
1164  next_semaphore_ = NULL;
1165  delete done_semaphore_;
1166  done_semaphore_ = NULL;
1167  delete thread_;
1168  thread_ = NULL;
1169#endif  // V8_SHARED
1170}
1171
1172
1173void SourceGroup::Execute() {
1174  for (int i = begin_offset_; i < end_offset_; ++i) {
1175    const char* arg = argv_[i];
1176    if (strcmp(arg, "-e") == 0 && i + 1 < end_offset_) {
1177      // Execute argument given to -e option directly.
1178      HandleScope handle_scope;
1179      Handle<String> file_name = String::New("unnamed");
1180      Handle<String> source = String::New(argv_[i + 1]);
1181      if (!Shell::ExecuteString(source, file_name, false, true)) {
1182        Shell::Exit(1);
1183      }
1184      ++i;
1185    } else if (arg[0] == '-') {
1186      // Ignore other options. They have been parsed already.
1187    } else {
1188      // Use all other arguments as names of files to load and run.
1189      HandleScope handle_scope;
1190      Handle<String> file_name = String::New(arg);
1191      Handle<String> source = ReadFile(arg);
1192      if (source.IsEmpty()) {
1193        printf("Error reading '%s'\n", arg);
1194        Shell::Exit(1);
1195      }
1196      if (!Shell::ExecuteString(source, file_name, false, true)) {
1197        Shell::Exit(1);
1198      }
1199    }
1200  }
1201}
1202
1203
1204Handle<String> SourceGroup::ReadFile(const char* name) {
1205  int size;
1206  const char* chars = ReadChars(name, &size);
1207  if (chars == NULL) return Handle<String>();
1208  Handle<String> result = String::New(chars, size);
1209  delete[] chars;
1210  return result;
1211}
1212
1213
1214#ifndef V8_SHARED
1215i::Thread::Options SourceGroup::GetThreadOptions() {
1216  // On some systems (OSX 10.6) the stack size default is 0.5Mb or less
1217  // which is not enough to parse the big literal expressions used in tests.
1218  // The stack size should be at least StackGuard::kLimitSize + some
1219  // OS-specific padding for thread startup code.  2Mbytes seems to be enough.
1220  return i::Thread::Options("IsolateThread", 2 * MB);
1221}
1222
1223
1224void SourceGroup::ExecuteInThread() {
1225  Isolate* isolate = Isolate::New();
1226  do {
1227    if (next_semaphore_ != NULL) next_semaphore_->Wait();
1228    {
1229      Isolate::Scope iscope(isolate);
1230      Locker lock(isolate);
1231      HandleScope scope;
1232      Persistent<Context> context = Shell::CreateEvaluationContext();
1233      {
1234        Context::Scope cscope(context);
1235        Execute();
1236      }
1237      context.Dispose();
1238    }
1239    if (done_semaphore_ != NULL) done_semaphore_->Signal();
1240  } while (!Shell::options.last_run);
1241  isolate->Dispose();
1242}
1243
1244
1245void SourceGroup::StartExecuteInThread() {
1246  if (thread_ == NULL) {
1247    thread_ = new IsolateThread(this);
1248    thread_->Start();
1249  }
1250  next_semaphore_->Signal();
1251}
1252
1253
1254void SourceGroup::WaitForThread() {
1255  if (thread_ == NULL) return;
1256  if (Shell::options.last_run) {
1257    thread_->Join();
1258  } else {
1259    done_semaphore_->Wait();
1260  }
1261}
1262#endif  // V8_SHARED
1263
1264
1265bool Shell::SetOptions(int argc, char* argv[]) {
1266  for (int i = 0; i < argc; i++) {
1267    if (strcmp(argv[i], "--stress-opt") == 0) {
1268      options.stress_opt = true;
1269      argv[i] = NULL;
1270    } else if (strcmp(argv[i], "--stress-deopt") == 0) {
1271      options.stress_deopt = true;
1272      argv[i] = NULL;
1273    } else if (strcmp(argv[i], "--noalways-opt") == 0) {
1274      // No support for stressing if we can't use --always-opt.
1275      options.stress_opt = false;
1276      options.stress_deopt = false;
1277    } else if (strcmp(argv[i], "--shell") == 0) {
1278      options.interactive_shell = true;
1279      argv[i] = NULL;
1280    } else if (strcmp(argv[i], "--test") == 0) {
1281      options.test_shell = true;
1282      argv[i] = NULL;
1283    } else if (strcmp(argv[i], "--preemption") == 0) {
1284#ifdef V8_SHARED
1285      printf("D8 with shared library does not support multi-threading\n");
1286      return false;
1287#else
1288      options.use_preemption = true;
1289      argv[i] = NULL;
1290#endif  // V8_SHARED
1291    } else if (strcmp(argv[i], "--nopreemption") == 0) {
1292#ifdef V8_SHARED
1293      printf("D8 with shared library does not support multi-threading\n");
1294      return false;
1295#else
1296      options.use_preemption = false;
1297      argv[i] = NULL;
1298#endif  // V8_SHARED
1299    } else if (strcmp(argv[i], "--preemption-interval") == 0) {
1300#ifdef V8_SHARED
1301      printf("D8 with shared library does not support multi-threading\n");
1302      return false;
1303#else
1304      if (++i < argc) {
1305        argv[i-1] = NULL;
1306        char* end = NULL;
1307        options.preemption_interval = strtol(argv[i], &end, 10);  // NOLINT
1308        if (options.preemption_interval <= 0
1309            || *end != '\0'
1310            || errno == ERANGE) {
1311          printf("Invalid value for --preemption-interval '%s'\n", argv[i]);
1312          return false;
1313        }
1314        argv[i] = NULL;
1315      } else {
1316        printf("Missing value for --preemption-interval\n");
1317        return false;
1318      }
1319#endif  // V8_SHARED
1320    } else if (strcmp(argv[i], "-f") == 0) {
1321      // Ignore any -f flags for compatibility with other stand-alone
1322      // JavaScript engines.
1323      continue;
1324    } else if (strcmp(argv[i], "--isolate") == 0) {
1325#ifdef V8_SHARED
1326      printf("D8 with shared library does not support multi-threading\n");
1327      return false;
1328#endif  // V8_SHARED
1329      options.num_isolates++;
1330    } else if (strcmp(argv[i], "-p") == 0) {
1331#ifdef V8_SHARED
1332      printf("D8 with shared library does not support multi-threading\n");
1333      return false;
1334#else
1335      options.num_parallel_files++;
1336#endif  // V8_SHARED
1337    }
1338#ifdef V8_SHARED
1339    else if (strcmp(argv[i], "--dump-counters") == 0) {
1340      printf("D8 with shared library does not include counters\n");
1341      return false;
1342    } else if (strcmp(argv[i], "--debugger") == 0) {
1343      printf("Javascript debugger not included\n");
1344      return false;
1345    }
1346#endif  // V8_SHARED
1347  }
1348
1349#ifndef V8_SHARED
1350  // Run parallel threads if we are not using --isolate
1351  options.parallel_files = new char*[options.num_parallel_files];
1352  int parallel_files_set = 0;
1353  for (int i = 1; i < argc; i++) {
1354    if (argv[i] == NULL) continue;
1355    if (strcmp(argv[i], "-p") == 0 && i + 1 < argc) {
1356      if (options.num_isolates > 1) {
1357        printf("-p is not compatible with --isolate\n");
1358        return false;
1359      }
1360      argv[i] = NULL;
1361      i++;
1362      options.parallel_files[parallel_files_set] = argv[i];
1363      parallel_files_set++;
1364      argv[i] = NULL;
1365    }
1366  }
1367  if (parallel_files_set != options.num_parallel_files) {
1368    printf("-p requires a file containing a list of files as parameter\n");
1369    return false;
1370  }
1371#endif  // V8_SHARED
1372
1373  v8::V8::SetFlagsFromCommandLine(&argc, argv, true);
1374
1375  // Set up isolated source groups.
1376  options.isolate_sources = new SourceGroup[options.num_isolates];
1377  SourceGroup* current = options.isolate_sources;
1378  current->Begin(argv, 1);
1379  for (int i = 1; i < argc; i++) {
1380    const char* str = argv[i];
1381    if (strcmp(str, "--isolate") == 0) {
1382      current->End(i);
1383      current++;
1384      current->Begin(argv, i + 1);
1385    } else if (strncmp(argv[i], "--", 2) == 0) {
1386      printf("Warning: unknown flag %s.\nTry --help for options\n", argv[i]);
1387    }
1388  }
1389  current->End(argc);
1390
1391  return true;
1392}
1393
1394
1395int Shell::RunMain(int argc, char* argv[]) {
1396#ifndef V8_SHARED
1397  i::List<i::Thread*> threads(1);
1398  if (options.parallel_files != NULL) {
1399    for (int i = 0; i < options.num_parallel_files; i++) {
1400      char* files = NULL;
1401      { Locker lock(Isolate::GetCurrent());
1402        int size = 0;
1403        files = ReadChars(options.parallel_files[i], &size);
1404      }
1405      if (files == NULL) {
1406        printf("File list '%s' not found\n", options.parallel_files[i]);
1407        Exit(1);
1408      }
1409      ShellThread* thread = new ShellThread(threads.length(), files);
1410      thread->Start();
1411      threads.Add(thread);
1412    }
1413  }
1414  for (int i = 1; i < options.num_isolates; ++i) {
1415    options.isolate_sources[i].StartExecuteInThread();
1416  }
1417#endif  // V8_SHARED
1418  {  // NOLINT
1419    Locker lock;
1420    HandleScope scope;
1421    Persistent<Context> context = CreateEvaluationContext();
1422    if (options.last_run) {
1423      // Keep using the same context in the interactive shell.
1424      evaluation_context_ = context;
1425#if !defined(V8_SHARED) && defined(ENABLE_DEBUGGER_SUPPORT)
1426      // If the interactive debugger is enabled make sure to activate
1427      // it before running the files passed on the command line.
1428      if (i::FLAG_debugger) {
1429        InstallUtilityScript();
1430      }
1431#endif  // !V8_SHARED && ENABLE_DEBUGGER_SUPPORT
1432    }
1433    {
1434      Context::Scope cscope(context);
1435      options.isolate_sources[0].Execute();
1436    }
1437    if (!options.last_run) {
1438      context.Dispose();
1439#if !defined(V8_SHARED)
1440      if (i::FLAG_send_idle_notification) {
1441        const int kLongIdlePauseInMs = 1000;
1442        V8::ContextDisposedNotification();
1443        V8::IdleNotification(kLongIdlePauseInMs);
1444      }
1445#endif  // !V8_SHARED
1446    }
1447
1448#ifndef V8_SHARED
1449    // Start preemption if threads have been created and preemption is enabled.
1450    if (threads.length() > 0
1451        && options.use_preemption) {
1452      Locker::StartPreemption(options.preemption_interval);
1453    }
1454#endif  // V8_SHARED
1455  }
1456
1457#ifndef V8_SHARED
1458  for (int i = 1; i < options.num_isolates; ++i) {
1459    options.isolate_sources[i].WaitForThread();
1460  }
1461
1462  for (int i = 0; i < threads.length(); i++) {
1463    i::Thread* thread = threads[i];
1464    thread->Join();
1465    delete thread;
1466  }
1467
1468  if (threads.length() > 0 && options.use_preemption) {
1469    Locker lock;
1470    Locker::StopPreemption();
1471  }
1472#endif  // V8_SHARED
1473  return 0;
1474}
1475
1476
1477int Shell::Main(int argc, char* argv[]) {
1478  if (!SetOptions(argc, argv)) return 1;
1479  Initialize();
1480
1481  int result = 0;
1482  if (options.stress_opt || options.stress_deopt) {
1483    Testing::SetStressRunType(
1484        options.stress_opt ? Testing::kStressTypeOpt
1485                           : Testing::kStressTypeDeopt);
1486    int stress_runs = Testing::GetStressRuns();
1487    for (int i = 0; i < stress_runs && result == 0; i++) {
1488      printf("============ Stress %d/%d ============\n", i + 1, stress_runs);
1489      Testing::PrepareStressRun(i);
1490      options.last_run = (i == stress_runs - 1);
1491      result = RunMain(argc, argv);
1492    }
1493    printf("======== Full Deoptimization =======\n");
1494    Testing::DeoptimizeAll();
1495#if !defined(V8_SHARED)
1496  } else if (i::FLAG_stress_runs > 0) {
1497    int stress_runs = i::FLAG_stress_runs;
1498    for (int i = 0; i < stress_runs && result == 0; i++) {
1499      printf("============ Run %d/%d ============\n", i + 1, stress_runs);
1500      options.last_run = (i == stress_runs - 1);
1501      result = RunMain(argc, argv);
1502    }
1503#endif
1504  } else {
1505    result = RunMain(argc, argv);
1506  }
1507
1508
1509#if !defined(V8_SHARED) && defined(ENABLE_DEBUGGER_SUPPORT)
1510  // Run remote debugger if requested, but never on --test
1511  if (i::FLAG_remote_debugger && !options.test_shell) {
1512    InstallUtilityScript();
1513    RunRemoteDebugger(i::FLAG_debugger_port);
1514    return 0;
1515  }
1516#endif  // !V8_SHARED && ENABLE_DEBUGGER_SUPPORT
1517
1518  // Run interactive shell if explicitly requested or if no script has been
1519  // executed, but never on --test
1520
1521  if (( options.interactive_shell
1522      || !options.script_executed )
1523      && !options.test_shell ) {
1524#if !defined(V8_SHARED) && defined(ENABLE_DEBUGGER_SUPPORT)
1525    if (!i::FLAG_debugger) {
1526      InstallUtilityScript();
1527    }
1528#endif  // !V8_SHARED && ENABLE_DEBUGGER_SUPPORT
1529    RunShell();
1530  }
1531
1532  V8::Dispose();
1533
1534#ifndef V8_SHARED
1535  OnExit();
1536#endif  // V8_SHARED
1537
1538  return result;
1539}
1540
1541}  // namespace v8
1542
1543
1544#ifndef GOOGLE3
1545int main(int argc, char* argv[]) {
1546  return v8::Shell::Main(argc, argv);
1547}
1548#endif
1549