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