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