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