1 // Copyright 2012 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are
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.
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.
29 // Defined when linking against shared lib on Windows.
30 #if defined(USING_V8_SHARED) && !defined(V8_SHARED)
34 #ifdef COMPRESS_STARTUP_DATA_BZ2
45 #include "../include/v8-testing.h"
60 #if !defined(_WIN32) && !defined(_WIN64)
61 #include <unistd.h> // NOLINT
65 #define ASSERT(condition) assert(condition)
70 LineEditor *LineEditor::first_ = NULL;
73 LineEditor::LineEditor(Type type, const char* name)
81 LineEditor* LineEditor::Get() {
82 LineEditor* current = first_;
83 LineEditor* best = current;
84 while (current != NULL) {
85 if (current->type_ > best->type_)
87 current = current->next_;
93 class DumbLineEditor: public LineEditor {
95 DumbLineEditor() : LineEditor(LineEditor::DUMB, "dumb") { }
96 virtual Handle<String> Prompt(const char* prompt);
100 static DumbLineEditor dumb_line_editor;
103 Handle<String> DumbLineEditor::Prompt(const char* prompt) {
104 printf("%s", prompt);
105 return Shell::ReadFromStdin();
110 CounterMap* Shell::counter_map_;
111 i::OS::MemoryMappedFile* Shell::counters_file_ = NULL;
112 CounterCollection Shell::local_counters_;
113 CounterCollection* Shell::counters_ = &local_counters_;
114 i::Mutex* Shell::context_mutex_(i::OS::CreateMutex());
115 Persistent<Context> Shell::utility_context_;
118 LineEditor* Shell::console = NULL;
119 Persistent<Context> Shell::evaluation_context_;
120 ShellOptions Shell::options;
121 const char* Shell::kPrompt = "d8> ";
124 const int MB = 1024 * 1024;
128 bool 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;
136 // Converts a V8 value to a C string.
137 const char* Shell::ToCString(const v8::String::Utf8Value& value) {
138 return *value ? *value : "<string conversion failed>";
142 // Executes a string within the current v8 context.
143 bool Shell::ExecuteString(Handle<String> source,
146 bool report_exceptions) {
147 #if !defined(V8_SHARED) && defined(ENABLE_DEBUGGER_SUPPORT)
148 bool FLAG_debugger = i::FLAG_debugger;
150 bool FLAG_debugger = false;
151 #endif // !V8_SHARED && ENABLE_DEBUGGER_SUPPORT
152 HandleScope handle_scope;
154 options.script_executed = true;
156 // When debugging make exceptions appear to be uncaught.
157 try_catch.SetVerbose(true);
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);
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);
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 size_t count = fwrite(*str, sizeof(**str), str.length(), stdout);
180 (void) count; // Silence GCC-4.5.x "unused result" warning.
189 Handle<Value> Shell::Print(const Arguments& args) {
190 Handle<Value> val = Write(args);
197 Handle<Value> Shell::Write(const Arguments& args) {
198 for (int i = 0; i < args.Length(); i++) {
199 HandleScope handle_scope;
203 v8::String::Utf8Value str(args[i]);
204 int n = static_cast<int>(fwrite(*str, sizeof(**str), str.length(), stdout));
205 if (n != str.length()) {
206 printf("Error in fwrite\n");
214 Handle<Value> Shell::EnableProfiler(const Arguments& args) {
215 V8::ResumeProfiler();
220 Handle<Value> Shell::DisableProfiler(const Arguments& args) {
226 Handle<Value> Shell::Read(const Arguments& args) {
227 String::Utf8Value file(args[0]);
229 return ThrowException(String::New("Error loading file"));
231 Handle<String> source = ReadFile(*file);
232 if (source.IsEmpty()) {
233 return ThrowException(String::New("Error loading file"));
239 Handle<String> Shell::ReadFromStdin() {
240 static const int kBufferSize = 256;
241 char buffer[kBufferSize];
242 Handle<String> accumulator = String::New("");
245 // Continue reading if the line ends with an escape '\\' or the line has
246 // not been fully read into the buffer yet (does not end with '\n').
247 // If fgets gets an error, just give up.
249 { // Release lock for blocking input.
250 Unlocker unlock(Isolate::GetCurrent());
251 input = fgets(buffer, kBufferSize, stdin);
253 if (input == NULL) return Handle<String>();
254 length = static_cast<int>(strlen(buffer));
257 } else if (buffer[length-1] != '\n') {
258 accumulator = String::Concat(accumulator, String::New(buffer, length));
259 } else if (length > 1 && buffer[length-2] == '\\') {
260 buffer[length-2] = '\n';
261 accumulator = String::Concat(accumulator, String::New(buffer, length-1));
263 return String::Concat(accumulator, String::New(buffer, length-1));
269 Handle<Value> Shell::Load(const Arguments& args) {
270 for (int i = 0; i < args.Length(); i++) {
271 HandleScope handle_scope;
272 String::Utf8Value file(args[i]);
274 return ThrowException(String::New("Error loading file"));
276 Handle<String> source = ReadFile(*file);
277 if (source.IsEmpty()) {
278 return ThrowException(String::New("Error loading file"));
280 if (!ExecuteString(source, String::New(*file), false, true)) {
281 return ThrowException(String::New("Error executing file"));
287 static size_t convertToUint(Local<Value> value_in, TryCatch* try_catch) {
288 if (value_in->IsUint32()) {
289 return value_in->Uint32Value();
292 Local<Value> number = value_in->ToNumber();
293 if (try_catch->HasCaught()) return 0;
295 ASSERT(number->IsNumber());
296 Local<Int32> int32 = number->ToInt32();
297 if (try_catch->HasCaught() || int32.IsEmpty()) return 0;
299 int32_t raw_value = int32->Int32Value();
300 if (try_catch->HasCaught()) return 0;
303 ThrowException(String::New("Array length must not be negative."));
307 static const int kMaxLength = 0x3fffffff;
309 ASSERT(kMaxLength == i::ExternalArray::kMaxLength);
311 if (raw_value > static_cast<int32_t>(kMaxLength)) {
313 String::New("Array length exceeds maximum length."));
315 return static_cast<size_t>(raw_value);
319 const char kArrayBufferMarkerPropName[] = "_is_array_buffer_";
320 const char kArrayBufferReferencePropName[] = "_array_buffer_ref_";
322 static const int kExternalArrayAllocationHeaderSize = 2;
324 Handle<Value> Shell::CreateExternalArray(const Arguments& args,
325 ExternalArrayType type,
326 size_t element_size) {
328 bool is_array_buffer_construct = element_size == 0;
329 if (is_array_buffer_construct) {
330 type = v8::kExternalByteArray;
333 ASSERT(element_size == 1 || element_size == 2 || element_size == 4 ||
335 if (args.Length() == 0) {
336 return ThrowException(
337 String::New("Array constructor must have at least one "
340 bool first_arg_is_array_buffer =
341 args[0]->IsObject() &&
342 args[0]->ToObject()->Get(
343 String::New(kArrayBufferMarkerPropName))->IsTrue();
344 // Currently, only the following constructors are supported:
345 // TypedArray(unsigned long length)
346 // TypedArray(ArrayBuffer buffer,
347 // optional unsigned long byteOffset,
348 // optional unsigned long length)
349 if (args.Length() > 3) {
350 return ThrowException(
351 String::New("Array constructor from ArrayBuffer must "
352 "have 1-3 parameters."));
355 Local<Value> length_value = (args.Length() < 3)
356 ? (first_arg_is_array_buffer
357 ? args[0]->ToObject()->Get(String::New("byteLength"))
360 size_t byteLength = convertToUint(length_value, &try_catch);
361 size_t length = byteLength;
362 if (try_catch.HasCaught()) return try_catch.Exception();
367 Handle<Object> array = Object::New();
368 if (first_arg_is_array_buffer) {
369 Handle<Object> derived_from = args[0]->ToObject();
370 data = derived_from->GetIndexedPropertiesExternalArrayData();
372 size_t array_buffer_length = convertToUint(
373 derived_from->Get(String::New("byteLength")),
375 if (try_catch.HasCaught()) return try_catch.Exception();
377 if (data == NULL && array_buffer_length != 0) {
378 return ThrowException(
379 String::New("ArrayBuffer doesn't have data"));
382 if (args.Length() > 1) {
383 offset = convertToUint(args[1], &try_catch);
384 if (try_catch.HasCaught()) return try_catch.Exception();
386 // The given byteOffset must be a multiple of the element size of the
387 // specific type, otherwise an exception is raised.
388 if (offset % element_size != 0) {
389 return ThrowException(
390 String::New("offset must be multiple of element_size"));
394 if (offset > array_buffer_length) {
395 return ThrowException(
396 String::New("byteOffset must be less than ArrayBuffer length."));
399 if (args.Length() == 2) {
400 // If length is not explicitly specified, the length of the ArrayBuffer
401 // minus the byteOffset must be a multiple of the element size of the
402 // specific type, or an exception is raised.
403 length = array_buffer_length - offset;
406 if (args.Length() != 3) {
407 if (length % element_size != 0) {
408 return ThrowException(
409 String::New("ArrayBuffer length minus the byteOffset must be a "
410 "multiple of the element size"));
412 length /= element_size;
415 // If a given byteOffset and length references an area beyond the end of
416 // the ArrayBuffer an exception is raised.
417 if (offset + (length * element_size) > array_buffer_length) {
418 return ThrowException(
419 String::New("length references an area beyond the end of the "
423 // Hold a reference to the ArrayBuffer so its buffer doesn't get collected.
424 array->Set(String::New(kArrayBufferReferencePropName), args[0], ReadOnly);
427 if (is_array_buffer_construct) {
428 array->Set(String::New(kArrayBufferMarkerPropName), True(), ReadOnly);
431 Persistent<Object> persistent_array = Persistent<Object>::New(array);
432 if (data == NULL && length != 0) {
433 // Make sure the total size fits into a (signed) int.
434 static const int kMaxSize = 0x7fffffff;
435 if (length > (kMaxSize - sizeof(size_t)) / element_size) {
436 return ThrowException(String::New("Array exceeds maximum size (2G)"));
438 // Prepend the size of the allocated chunk to the data itself.
439 int total_size = length * element_size +
440 kExternalArrayAllocationHeaderSize * sizeof(size_t);
441 data = malloc(total_size);
443 return ThrowException(String::New("Memory allocation failed."));
445 *reinterpret_cast<size_t*>(data) = total_size;
446 data = reinterpret_cast<size_t*>(data) + kExternalArrayAllocationHeaderSize;
447 memset(data, 0, length * element_size);
448 V8::AdjustAmountOfExternalAllocatedMemory(total_size);
450 persistent_array.MakeWeak(data, ExternalArrayWeakCallback);
451 persistent_array.MarkIndependent();
453 array->SetIndexedPropertiesToExternalArrayData(
454 reinterpret_cast<uint8_t*>(data) + offset, type,
455 static_cast<int>(length));
456 array->Set(String::New("byteLength"),
457 Int32::New(static_cast<int32_t>(byteLength)), ReadOnly);
458 if (!is_array_buffer_construct) {
459 array->Set(String::New("length"),
460 Int32::New(static_cast<int32_t>(length)), ReadOnly);
461 array->Set(String::New("byteOffset"),
462 Int32::New(static_cast<int32_t>(offset)), ReadOnly);
463 array->Set(String::New("BYTES_PER_ELEMENT"),
464 Int32::New(static_cast<int32_t>(element_size)));
465 // We currently support 'buffer' property only if constructed from a buffer.
466 if (first_arg_is_array_buffer) {
467 array->Set(String::New("buffer"), args[0], ReadOnly);
474 void Shell::ExternalArrayWeakCallback(Persistent<Value> object, void* data) {
476 Handle<String> prop_name = String::New(kArrayBufferReferencePropName);
477 Handle<Object> converted_object = object->ToObject();
478 Local<Value> prop_value = converted_object->Get(prop_name);
479 if (data != NULL && !prop_value->IsObject()) {
480 data = reinterpret_cast<size_t*>(data) - kExternalArrayAllocationHeaderSize;
481 V8::AdjustAmountOfExternalAllocatedMemory(
482 -static_cast<int>(*reinterpret_cast<size_t*>(data)));
489 Handle<Value> Shell::ArrayBuffer(const Arguments& args) {
490 return CreateExternalArray(args, v8::kExternalByteArray, 0);
494 Handle<Value> Shell::Int8Array(const Arguments& args) {
495 return CreateExternalArray(args, v8::kExternalByteArray, sizeof(int8_t));
499 Handle<Value> Shell::Uint8Array(const Arguments& args) {
500 return CreateExternalArray(args, kExternalUnsignedByteArray, sizeof(uint8_t));
504 Handle<Value> Shell::Int16Array(const Arguments& args) {
505 return CreateExternalArray(args, kExternalShortArray, sizeof(int16_t));
509 Handle<Value> Shell::Uint16Array(const Arguments& args) {
510 return CreateExternalArray(args, kExternalUnsignedShortArray,
515 Handle<Value> Shell::Int32Array(const Arguments& args) {
516 return CreateExternalArray(args, kExternalIntArray, sizeof(int32_t));
520 Handle<Value> Shell::Uint32Array(const Arguments& args) {
521 return CreateExternalArray(args, kExternalUnsignedIntArray, sizeof(uint32_t));
525 Handle<Value> Shell::Float32Array(const Arguments& args) {
526 return CreateExternalArray(args, kExternalFloatArray,
527 sizeof(float)); // NOLINT
531 Handle<Value> Shell::Float64Array(const Arguments& args) {
532 return CreateExternalArray(args, kExternalDoubleArray,
533 sizeof(double)); // NOLINT
537 Handle<Value> Shell::PixelArray(const Arguments& args) {
538 return CreateExternalArray(args, kExternalPixelArray, sizeof(uint8_t));
542 Handle<Value> Shell::Yield(const Arguments& args) {
543 v8::Unlocker unlocker;
548 Handle<Value> Shell::Quit(const Arguments& args) {
549 int exit_code = args[0]->Int32Value();
558 Handle<Value> Shell::Version(const Arguments& args) {
559 return String::New(V8::GetVersion());
563 void Shell::ReportException(v8::TryCatch* try_catch) {
564 HandleScope handle_scope;
565 #if !defined(V8_SHARED) && defined(ENABLE_DEBUGGER_SUPPORT)
566 bool enter_context = !Context::InContext();
567 if (enter_context) utility_context_->Enter();
568 #endif // !V8_SHARED && ENABLE_DEBUGGER_SUPPORT
569 v8::String::Utf8Value exception(try_catch->Exception());
570 const char* exception_string = ToCString(exception);
571 Handle<Message> message = try_catch->Message();
572 if (message.IsEmpty()) {
573 // V8 didn't provide any extra information about this error; just
574 // print the exception.
575 printf("%s\n", exception_string);
577 // Print (filename):(line number): (message).
578 v8::String::Utf8Value filename(message->GetScriptResourceName());
579 const char* filename_string = ToCString(filename);
580 int linenum = message->GetLineNumber();
581 printf("%s:%i: %s\n", filename_string, linenum, exception_string);
582 // Print line of source code.
583 v8::String::Utf8Value sourceline(message->GetSourceLine());
584 const char* sourceline_string = ToCString(sourceline);
585 printf("%s\n", sourceline_string);
586 // Print wavy underline (GetUnderline is deprecated).
587 int start = message->GetStartColumn();
588 for (int i = 0; i < start; i++) {
591 int end = message->GetEndColumn();
592 for (int i = start; i < end; i++) {
596 v8::String::Utf8Value stack_trace(try_catch->StackTrace());
597 if (stack_trace.length() > 0) {
598 const char* stack_trace_string = ToCString(stack_trace);
599 printf("%s\n", stack_trace_string);
603 #if !defined(V8_SHARED) && defined(ENABLE_DEBUGGER_SUPPORT)
604 if (enter_context) utility_context_->Exit();
605 #endif // !V8_SHARED && ENABLE_DEBUGGER_SUPPORT
610 Handle<Array> Shell::GetCompletions(Handle<String> text, Handle<String> full) {
611 HandleScope handle_scope;
612 Context::Scope context_scope(utility_context_);
613 Handle<Object> global = utility_context_->Global();
614 Handle<Value> fun = global->Get(String::New("GetCompletions"));
615 static const int kArgc = 3;
616 Handle<Value> argv[kArgc] = { evaluation_context_->Global(), text, full };
617 Handle<Value> val = Handle<Function>::Cast(fun)->Call(global, kArgc, argv);
618 return handle_scope.Close(Handle<Array>::Cast(val));
622 #ifdef ENABLE_DEBUGGER_SUPPORT
623 Handle<Object> Shell::DebugMessageDetails(Handle<String> message) {
624 Context::Scope context_scope(utility_context_);
625 Handle<Object> global = utility_context_->Global();
626 Handle<Value> fun = global->Get(String::New("DebugMessageDetails"));
627 static const int kArgc = 1;
628 Handle<Value> argv[kArgc] = { message };
629 Handle<Value> val = Handle<Function>::Cast(fun)->Call(global, kArgc, argv);
630 return Handle<Object>::Cast(val);
634 Handle<Value> Shell::DebugCommandToJSONRequest(Handle<String> command) {
635 Context::Scope context_scope(utility_context_);
636 Handle<Object> global = utility_context_->Global();
637 Handle<Value> fun = global->Get(String::New("DebugCommandToJSONRequest"));
638 static const int kArgc = 1;
639 Handle<Value> argv[kArgc] = { command };
640 Handle<Value> val = Handle<Function>::Cast(fun)->Call(global, kArgc, argv);
645 void Shell::DispatchDebugMessages() {
646 v8::Context::Scope scope(Shell::evaluation_context_);
647 v8::Debug::ProcessDebugMessages();
649 #endif // ENABLE_DEBUGGER_SUPPORT
654 int32_t* Counter::Bind(const char* name, bool is_histogram) {
656 for (i = 0; i < kMaxNameSize - 1 && name[i]; i++)
657 name_[i] = static_cast<char>(name[i]);
659 is_histogram_ = is_histogram;
664 void Counter::AddSample(int32_t sample) {
666 sample_total_ += sample;
670 CounterCollection::CounterCollection() {
671 magic_number_ = 0xDEADFACE;
672 max_counters_ = kMaxCounters;
673 max_name_size_ = Counter::kMaxNameSize;
674 counters_in_use_ = 0;
678 Counter* CounterCollection::GetNextCounter() {
679 if (counters_in_use_ == kMaxCounters) return NULL;
680 return &counters_[counters_in_use_++];
684 void Shell::MapCounters(const char* name) {
685 counters_file_ = i::OS::MemoryMappedFile::create(
686 name, sizeof(CounterCollection), &local_counters_);
687 void* memory = (counters_file_ == NULL) ?
688 NULL : counters_file_->memory();
689 if (memory == NULL) {
690 printf("Could not map counters file %s\n", name);
693 counters_ = static_cast<CounterCollection*>(memory);
694 V8::SetCounterFunction(LookupCounter);
695 V8::SetCreateHistogramFunction(CreateHistogram);
696 V8::SetAddHistogramSampleFunction(AddHistogramSample);
700 int CounterMap::Hash(const char* name) {
703 while ((c = *name++) != 0) {
711 Counter* Shell::GetCounter(const char* name, bool is_histogram) {
712 Counter* counter = counter_map_->Lookup(name);
714 if (counter == NULL) {
715 counter = counters_->GetNextCounter();
716 if (counter != NULL) {
717 counter_map_->Set(name, counter);
718 counter->Bind(name, is_histogram);
721 ASSERT(counter->is_histogram() == is_histogram);
727 int* Shell::LookupCounter(const char* name) {
728 Counter* counter = GetCounter(name, false);
730 if (counter != NULL) {
731 return counter->ptr();
738 void* Shell::CreateHistogram(const char* name,
742 return GetCounter(name, true);
746 void Shell::AddHistogramSample(void* histogram, int sample) {
747 Counter* counter = reinterpret_cast<Counter*>(histogram);
748 counter->AddSample(sample);
752 void Shell::InstallUtilityScript() {
755 // If we use the utility context, we have to set the security tokens so that
756 // utility, evaluation and debug context can all access each other.
757 utility_context_->SetSecurityToken(Undefined());
758 evaluation_context_->SetSecurityToken(Undefined());
759 Context::Scope utility_scope(utility_context_);
761 #ifdef ENABLE_DEBUGGER_SUPPORT
762 if (i::FLAG_debugger) printf("JavaScript debugger enabled\n");
763 // Install the debugger object in the utility scope
764 i::Debug* debug = i::Isolate::Current()->debug();
766 i::Handle<i::JSObject> js_debug
767 = i::Handle<i::JSObject>(debug->debug_context()->global());
768 utility_context_->Global()->Set(String::New("$debug"),
769 Utils::ToLocal(js_debug));
770 debug->debug_context()->set_security_token(HEAP->undefined_value());
771 #endif // ENABLE_DEBUGGER_SUPPORT
773 // Run the d8 shell utility script in the utility context
774 int source_index = i::NativesCollection<i::D8>::GetIndex("d8");
775 i::Vector<const char> shell_source =
776 i::NativesCollection<i::D8>::GetRawScriptSource(source_index);
777 i::Vector<const char> shell_source_name =
778 i::NativesCollection<i::D8>::GetScriptName(source_index);
779 Handle<String> source = String::New(shell_source.start(),
780 shell_source.length());
781 Handle<String> name = String::New(shell_source_name.start(),
782 shell_source_name.length());
783 Handle<Script> script = Script::Compile(source, name);
785 // Mark the d8 shell script as native to avoid it showing up as normal source
787 i::Handle<i::Object> compiled_script = Utils::OpenHandle(*script);
788 i::Handle<i::Script> script_object = compiled_script->IsJSFunction()
789 ? i::Handle<i::Script>(i::Script::cast(
790 i::JSFunction::cast(*compiled_script)->shared()->script()))
791 : i::Handle<i::Script>(i::Script::cast(
792 i::SharedFunctionInfo::cast(*compiled_script)->script()));
793 script_object->set_type(i::Smi::FromInt(i::Script::TYPE_NATIVE));
795 #ifdef ENABLE_DEBUGGER_SUPPORT
796 // Start the in-process debugger if requested.
797 if (i::FLAG_debugger && !i::FLAG_debugger_agent) {
798 v8::Debug::SetDebugEventListener(HandleDebugEvent);
800 #endif // ENABLE_DEBUGGER_SUPPORT
805 #ifdef COMPRESS_STARTUP_DATA_BZ2
806 class BZip2Decompressor : public v8::StartupDataDecompressor {
808 virtual ~BZip2Decompressor() { }
811 virtual int DecompressData(char* raw_data,
813 const char* compressed_data,
814 int compressed_data_size) {
815 ASSERT_EQ(v8::StartupData::kBZip2,
816 v8::V8::GetCompressedStartupDataAlgorithm());
817 unsigned int decompressed_size = *raw_data_size;
819 BZ2_bzBuffToBuffDecompress(raw_data,
821 const_cast<char*>(compressed_data),
822 compressed_data_size,
824 if (result == BZ_OK) {
825 *raw_data_size = decompressed_size;
832 Handle<ObjectTemplate> Shell::CreateGlobalTemplate() {
833 Handle<ObjectTemplate> global_template = ObjectTemplate::New();
834 global_template->Set(String::New("print"), FunctionTemplate::New(Print));
835 global_template->Set(String::New("write"), FunctionTemplate::New(Write));
836 global_template->Set(String::New("read"), FunctionTemplate::New(Read));
837 global_template->Set(String::New("readbinary"),
838 FunctionTemplate::New(ReadBinary));
839 global_template->Set(String::New("readbuffer"),
840 FunctionTemplate::New(ReadBuffer));
841 global_template->Set(String::New("readline"),
842 FunctionTemplate::New(ReadLine));
843 global_template->Set(String::New("load"), FunctionTemplate::New(Load));
844 global_template->Set(String::New("quit"), FunctionTemplate::New(Quit));
845 global_template->Set(String::New("version"), FunctionTemplate::New(Version));
846 global_template->Set(String::New("enableProfiler"),
847 FunctionTemplate::New(EnableProfiler));
848 global_template->Set(String::New("disableProfiler"),
849 FunctionTemplate::New(DisableProfiler));
851 // Bind the handlers for external arrays.
852 global_template->Set(String::New("ArrayBuffer"),
853 FunctionTemplate::New(ArrayBuffer));
854 global_template->Set(String::New("Int8Array"),
855 FunctionTemplate::New(Int8Array));
856 global_template->Set(String::New("Uint8Array"),
857 FunctionTemplate::New(Uint8Array));
858 global_template->Set(String::New("Int16Array"),
859 FunctionTemplate::New(Int16Array));
860 global_template->Set(String::New("Uint16Array"),
861 FunctionTemplate::New(Uint16Array));
862 global_template->Set(String::New("Int32Array"),
863 FunctionTemplate::New(Int32Array));
864 global_template->Set(String::New("Uint32Array"),
865 FunctionTemplate::New(Uint32Array));
866 global_template->Set(String::New("Float32Array"),
867 FunctionTemplate::New(Float32Array));
868 global_template->Set(String::New("Float64Array"),
869 FunctionTemplate::New(Float64Array));
870 global_template->Set(String::New("PixelArray"),
871 FunctionTemplate::New(PixelArray));
873 #ifdef LIVE_OBJECT_LIST
874 global_template->Set(String::New("lol_is_enabled"), True());
876 global_template->Set(String::New("lol_is_enabled"), False());
879 #if !defined(V8_SHARED) && !defined(_WIN32) && !defined(_WIN64)
880 Handle<ObjectTemplate> os_templ = ObjectTemplate::New();
881 AddOSMethods(os_templ);
882 global_template->Set(String::New("os"), os_templ);
885 return global_template;
889 void Shell::Initialize() {
890 #ifdef COMPRESS_STARTUP_DATA_BZ2
891 BZip2Decompressor startup_data_decompressor;
892 int bz2_result = startup_data_decompressor.Decompress();
893 if (bz2_result != BZ_OK) {
894 fprintf(stderr, "bzip error code: %d\n", bz2_result);
900 Shell::counter_map_ = new CounterMap();
902 if (i::StrLength(i::FLAG_map_counters) != 0)
903 MapCounters(i::FLAG_map_counters);
904 if (i::FLAG_dump_counters) {
905 V8::SetCounterFunction(LookupCounter);
906 V8::SetCreateHistogramFunction(CreateHistogram);
907 V8::SetAddHistogramSampleFunction(AddHistogramSample);
910 if (options.test_shell) return;
915 Handle<ObjectTemplate> global_template = CreateGlobalTemplate();
916 utility_context_ = Context::New(NULL, global_template);
918 #ifdef ENABLE_DEBUGGER_SUPPORT
919 // Start the debugger agent if requested.
920 if (i::FLAG_debugger_agent) {
921 v8::Debug::EnableAgent("d8 shell", i::FLAG_debugger_port, true);
922 v8::Debug::SetDebugMessageDispatchHandler(DispatchDebugMessages, true);
924 #endif // ENABLE_DEBUGGER_SUPPORT
929 Persistent<Context> Shell::CreateEvaluationContext() {
931 // This needs to be a critical section since this is not thread-safe
932 i::ScopedLock lock(context_mutex_);
934 // Initialize the global objects
935 Handle<ObjectTemplate> global_template = CreateGlobalTemplate();
936 Persistent<Context> context = Context::New(NULL, global_template);
937 ASSERT(!context.IsEmpty());
938 Context::Scope scope(context);
941 i::JSArguments js_args = i::FLAG_js_arguments;
942 i::Handle<i::FixedArray> arguments_array =
943 FACTORY->NewFixedArray(js_args.argc());
944 for (int j = 0; j < js_args.argc(); j++) {
945 i::Handle<i::String> arg =
946 FACTORY->NewStringFromUtf8(i::CStrVector(js_args[j]));
947 arguments_array->set(j, *arg);
949 i::Handle<i::JSArray> arguments_jsarray =
950 FACTORY->NewJSArrayWithElements(arguments_array);
951 context->Global()->Set(String::New("arguments"),
952 Utils::ToLocal(arguments_jsarray));
958 void Shell::Exit(int exit_code) {
959 // Use _exit instead of exit to avoid races between isolate
960 // threads and static destructors.
968 struct CounterAndKey {
974 int CompareKeys(const void* a, const void* b) {
975 return strcmp(static_cast<const CounterAndKey*>(a)->key,
976 static_cast<const CounterAndKey*>(b)->key);
980 void Shell::OnExit() {
981 if (console != NULL) console->Close();
982 if (i::FLAG_dump_counters) {
983 int number_of_counters = 0;
984 for (CounterMap::Iterator i(counter_map_); i.More(); i.Next()) {
985 number_of_counters++;
987 CounterAndKey* counters = new CounterAndKey[number_of_counters];
989 for (CounterMap::Iterator i(counter_map_); i.More(); i.Next(), j++) {
990 counters[j].counter = i.CurrentValue();
991 counters[j].key = i.CurrentKey();
993 qsort(counters, number_of_counters, sizeof(counters[0]), CompareKeys);
994 printf("+--------------------------------------------+-------------+\n");
995 printf("| Name | Value |\n");
996 printf("+--------------------------------------------+-------------+\n");
997 for (j = 0; j < number_of_counters; j++) {
998 Counter* counter = counters[j].counter;
999 const char* key = counters[j].key;
1000 if (counter->is_histogram()) {
1001 printf("| c:%-40s | %11i |\n", key, counter->count());
1002 printf("| t:%-40s | %11i |\n", key, counter->sample_total());
1004 printf("| %-42s | %11i |\n", key, counter->count());
1007 printf("+--------------------------------------------+-------------+\n");
1010 delete counters_file_;
1011 delete counter_map_;
1016 static FILE* FOpen(const char* path, const char* mode) {
1017 #if defined(_MSC_VER) && (defined(_WIN32) || defined(_WIN64))
1019 if (fopen_s(&result, path, mode) == 0) {
1025 FILE* file = fopen(path, mode);
1026 if (file == NULL) return NULL;
1027 struct stat file_stat;
1028 if (fstat(fileno(file), &file_stat) != 0) return NULL;
1029 bool is_regular_file = ((file_stat.st_mode & S_IFREG) != 0);
1030 if (is_regular_file) return file;
1037 static char* ReadChars(const char* name, int* size_out) {
1038 // Release the V8 lock while reading files.
1039 v8::Unlocker unlocker(Isolate::GetCurrent());
1040 FILE* file = FOpen(name, "rb");
1041 if (file == NULL) return NULL;
1043 fseek(file, 0, SEEK_END);
1044 int size = ftell(file);
1047 char* chars = new char[size + 1];
1049 for (int i = 0; i < size;) {
1050 int read = static_cast<int>(fread(&chars[i], 1, size - i, file));
1059 Handle<Value> Shell::ReadBinary(const Arguments& args) {
1060 String::Utf8Value filename(args[0]);
1062 if (*filename == NULL) {
1063 return ThrowException(String::New("Error loading file"));
1065 char* chars = ReadChars(*filename, &size);
1066 if (chars == NULL) {
1067 return ThrowException(String::New("Error reading file"));
1069 // We skip checking the string for UTF8 characters and use it raw as
1070 // backing store for the external string with 8-bit characters.
1071 BinaryResource* resource = new BinaryResource(chars, size);
1072 return String::NewExternal(resource);
1076 Handle<Value> Shell::ReadBuffer(const Arguments& args) {
1077 String::Utf8Value filename(args[0]);
1079 if (*filename == NULL) {
1080 return ThrowException(String::New("Error loading file"));
1082 char* data = ReadChars(*filename, &length);
1084 return ThrowException(String::New("Error reading file"));
1087 Handle<Object> buffer = Object::New();
1088 buffer->Set(String::New(kArrayBufferMarkerPropName), True(), ReadOnly);
1090 Persistent<Object> persistent_buffer = Persistent<Object>::New(buffer);
1091 persistent_buffer.MakeWeak(data, ExternalArrayWeakCallback);
1092 persistent_buffer.MarkIndependent();
1094 buffer->SetIndexedPropertiesToExternalArrayData(
1095 reinterpret_cast<uint8_t*>(data), kExternalUnsignedByteArray, length);
1096 buffer->Set(String::New("byteLength"),
1097 Int32::New(static_cast<int32_t>(length)), ReadOnly);
1103 static char* ReadToken(char* data, char token) {
1104 char* next = i::OS::StrChr(data, token);
1114 static char* ReadLine(char* data) {
1115 return ReadToken(data, '\n');
1119 static char* ReadWord(char* data) {
1120 return ReadToken(data, ' ');
1125 // Reads a file into a v8 string.
1126 Handle<String> Shell::ReadFile(const char* name) {
1128 char* chars = ReadChars(name, &size);
1129 if (chars == NULL) return Handle<String>();
1130 Handle<String> result = String::New(chars);
1136 void Shell::RunShell() {
1138 Context::Scope context_scope(evaluation_context_);
1139 HandleScope outer_scope;
1140 Handle<String> name = String::New("(d8)");
1141 console = LineEditor::Get();
1142 printf("V8 version %s [console: %s]\n", V8::GetVersion(), console->name());
1145 HandleScope inner_scope;
1146 Handle<String> input = console->Prompt(Shell::kPrompt);
1147 if (input.IsEmpty()) break;
1148 ExecuteString(input, name, true, true);
1155 class ShellThread : public i::Thread {
1157 // Takes ownership of the underlying char array of |files|.
1158 ShellThread(int no, char* files)
1159 : Thread("d8:ShellThread"),
1160 no_(no), files_(files) { }
1173 void ShellThread::Run() {
1175 while ((ptr != NULL) && (*ptr != '\0')) {
1176 // For each newline-separated line.
1177 char* next_line = ReadLine(ptr);
1180 // Skip comment lines.
1185 // Prepare the context for this thread.
1187 HandleScope outer_scope;
1188 Persistent<Context> thread_context = Shell::CreateEvaluationContext();
1189 Context::Scope context_scope(thread_context);
1191 while ((ptr != NULL) && (*ptr != '\0')) {
1192 HandleScope inner_scope;
1193 char* filename = ptr;
1194 ptr = ReadWord(ptr);
1196 // Skip empty strings.
1197 if (strlen(filename) == 0) {
1201 Handle<String> str = Shell::ReadFile(filename);
1202 if (str.IsEmpty()) {
1203 printf("File '%s' not found\n", filename);
1207 Shell::ExecuteString(str, String::New(filename), false, false);
1210 thread_context.Dispose();
1217 SourceGroup::~SourceGroup() {
1219 delete next_semaphore_;
1220 next_semaphore_ = NULL;
1221 delete done_semaphore_;
1222 done_semaphore_ = NULL;
1229 void SourceGroup::Execute() {
1230 for (int i = begin_offset_; i < end_offset_; ++i) {
1231 const char* arg = argv_[i];
1232 if (strcmp(arg, "-e") == 0 && i + 1 < end_offset_) {
1233 // Execute argument given to -e option directly.
1234 HandleScope handle_scope;
1235 Handle<String> file_name = String::New("unnamed");
1236 Handle<String> source = String::New(argv_[i + 1]);
1237 if (!Shell::ExecuteString(source, file_name, false, true)) {
1241 } else if (arg[0] == '-') {
1242 // Ignore other options. They have been parsed already.
1244 // Use all other arguments as names of files to load and run.
1245 HandleScope handle_scope;
1246 Handle<String> file_name = String::New(arg);
1247 Handle<String> source = ReadFile(arg);
1248 if (source.IsEmpty()) {
1249 printf("Error reading '%s'\n", arg);
1252 if (!Shell::ExecuteString(source, file_name, false, true)) {
1260 Handle<String> SourceGroup::ReadFile(const char* name) {
1262 const char* chars = ReadChars(name, &size);
1263 if (chars == NULL) return Handle<String>();
1264 Handle<String> result = String::New(chars, size);
1271 i::Thread::Options SourceGroup::GetThreadOptions() {
1272 // On some systems (OSX 10.6) the stack size default is 0.5Mb or less
1273 // which is not enough to parse the big literal expressions used in tests.
1274 // The stack size should be at least StackGuard::kLimitSize + some
1275 // OS-specific padding for thread startup code. 2Mbytes seems to be enough.
1276 return i::Thread::Options("IsolateThread", 2 * MB);
1280 void SourceGroup::ExecuteInThread() {
1281 Isolate* isolate = Isolate::New();
1283 if (next_semaphore_ != NULL) next_semaphore_->Wait();
1285 Isolate::Scope iscope(isolate);
1286 Locker lock(isolate);
1288 Persistent<Context> context = Shell::CreateEvaluationContext();
1290 Context::Scope cscope(context);
1295 if (done_semaphore_ != NULL) done_semaphore_->Signal();
1296 } while (!Shell::options.last_run);
1301 void SourceGroup::StartExecuteInThread() {
1302 if (thread_ == NULL) {
1303 thread_ = new IsolateThread(this);
1306 next_semaphore_->Signal();
1310 void SourceGroup::WaitForThread() {
1311 if (thread_ == NULL) return;
1312 if (Shell::options.last_run) {
1315 done_semaphore_->Wait();
1321 bool Shell::SetOptions(int argc, char* argv[]) {
1322 for (int i = 0; i < argc; i++) {
1323 if (strcmp(argv[i], "--stress-opt") == 0) {
1324 options.stress_opt = true;
1326 } else if (strcmp(argv[i], "--stress-deopt") == 0) {
1327 options.stress_deopt = true;
1329 } else if (strcmp(argv[i], "--noalways-opt") == 0) {
1330 // No support for stressing if we can't use --always-opt.
1331 options.stress_opt = false;
1332 options.stress_deopt = false;
1333 } else if (strcmp(argv[i], "--shell") == 0) {
1334 options.interactive_shell = true;
1336 } else if (strcmp(argv[i], "--test") == 0) {
1337 options.test_shell = true;
1339 } else if (strcmp(argv[i], "--preemption") == 0) {
1341 printf("D8 with shared library does not support multi-threading\n");
1344 options.use_preemption = true;
1347 } else if (strcmp(argv[i], "--nopreemption") == 0) {
1349 printf("D8 with shared library does not support multi-threading\n");
1352 options.use_preemption = false;
1355 } else if (strcmp(argv[i], "--preemption-interval") == 0) {
1357 printf("D8 with shared library does not support multi-threading\n");
1363 options.preemption_interval = strtol(argv[i], &end, 10); // NOLINT
1364 if (options.preemption_interval <= 0
1366 || errno == ERANGE) {
1367 printf("Invalid value for --preemption-interval '%s'\n", argv[i]);
1372 printf("Missing value for --preemption-interval\n");
1376 } else if (strcmp(argv[i], "-f") == 0) {
1377 // Ignore any -f flags for compatibility with other stand-alone
1378 // JavaScript engines.
1380 } else if (strcmp(argv[i], "--isolate") == 0) {
1382 printf("D8 with shared library does not support multi-threading\n");
1385 options.num_isolates++;
1386 } else if (strcmp(argv[i], "-p") == 0) {
1388 printf("D8 with shared library does not support multi-threading\n");
1391 options.num_parallel_files++;
1395 else if (strcmp(argv[i], "--dump-counters") == 0) {
1396 printf("D8 with shared library does not include counters\n");
1398 } else if (strcmp(argv[i], "--debugger") == 0) {
1399 printf("Javascript debugger not included\n");
1406 // Run parallel threads if we are not using --isolate
1407 options.parallel_files = new char*[options.num_parallel_files];
1408 int parallel_files_set = 0;
1409 for (int i = 1; i < argc; i++) {
1410 if (argv[i] == NULL) continue;
1411 if (strcmp(argv[i], "-p") == 0 && i + 1 < argc) {
1412 if (options.num_isolates > 1) {
1413 printf("-p is not compatible with --isolate\n");
1418 options.parallel_files[parallel_files_set] = argv[i];
1419 parallel_files_set++;
1423 if (parallel_files_set != options.num_parallel_files) {
1424 printf("-p requires a file containing a list of files as parameter\n");
1429 v8::V8::SetFlagsFromCommandLine(&argc, argv, true);
1431 // Set up isolated source groups.
1432 options.isolate_sources = new SourceGroup[options.num_isolates];
1433 SourceGroup* current = options.isolate_sources;
1434 current->Begin(argv, 1);
1435 for (int i = 1; i < argc; i++) {
1436 const char* str = argv[i];
1437 if (strcmp(str, "--isolate") == 0) {
1440 current->Begin(argv, i + 1);
1441 } else if (strncmp(argv[i], "--", 2) == 0) {
1442 printf("Warning: unknown flag %s.\nTry --help for options\n", argv[i]);
1451 int Shell::RunMain(int argc, char* argv[]) {
1453 i::List<i::Thread*> threads(1);
1454 if (options.parallel_files != NULL) {
1455 for (int i = 0; i < options.num_parallel_files; i++) {
1457 { Locker lock(Isolate::GetCurrent());
1459 files = ReadChars(options.parallel_files[i], &size);
1461 if (files == NULL) {
1462 printf("File list '%s' not found\n", options.parallel_files[i]);
1465 ShellThread* thread = new ShellThread(threads.length(), files);
1467 threads.Add(thread);
1470 for (int i = 1; i < options.num_isolates; ++i) {
1471 options.isolate_sources[i].StartExecuteInThread();
1477 Persistent<Context> context = CreateEvaluationContext();
1478 if (options.last_run) {
1479 // Keep using the same context in the interactive shell.
1480 evaluation_context_ = context;
1481 #if !defined(V8_SHARED) && defined(ENABLE_DEBUGGER_SUPPORT)
1482 // If the interactive debugger is enabled make sure to activate
1483 // it before running the files passed on the command line.
1484 if (i::FLAG_debugger) {
1485 InstallUtilityScript();
1487 #endif // !V8_SHARED && ENABLE_DEBUGGER_SUPPORT
1490 Context::Scope cscope(context);
1491 options.isolate_sources[0].Execute();
1493 if (!options.last_run) {
1495 #if !defined(V8_SHARED)
1496 if (i::FLAG_send_idle_notification) {
1497 const int kLongIdlePauseInMs = 1000;
1498 V8::ContextDisposedNotification();
1499 V8::IdleNotification(kLongIdlePauseInMs);
1501 #endif // !V8_SHARED
1505 // Start preemption if threads have been created and preemption is enabled.
1506 if (threads.length() > 0
1507 && options.use_preemption) {
1508 Locker::StartPreemption(options.preemption_interval);
1514 for (int i = 1; i < options.num_isolates; ++i) {
1515 options.isolate_sources[i].WaitForThread();
1518 for (int i = 0; i < threads.length(); i++) {
1519 i::Thread* thread = threads[i];
1524 if (threads.length() > 0 && options.use_preemption) {
1526 Locker::StopPreemption();
1533 int Shell::Main(int argc, char* argv[]) {
1534 if (!SetOptions(argc, argv)) return 1;
1538 if (options.stress_opt || options.stress_deopt) {
1539 Testing::SetStressRunType(
1540 options.stress_opt ? Testing::kStressTypeOpt
1541 : Testing::kStressTypeDeopt);
1542 int stress_runs = Testing::GetStressRuns();
1543 for (int i = 0; i < stress_runs && result == 0; i++) {
1544 printf("============ Stress %d/%d ============\n", i + 1, stress_runs);
1545 Testing::PrepareStressRun(i);
1546 options.last_run = (i == stress_runs - 1);
1547 result = RunMain(argc, argv);
1549 printf("======== Full Deoptimization =======\n");
1550 Testing::DeoptimizeAll();
1551 #if !defined(V8_SHARED)
1552 } else if (i::FLAG_stress_runs > 0) {
1553 int stress_runs = i::FLAG_stress_runs;
1554 for (int i = 0; i < stress_runs && result == 0; i++) {
1555 printf("============ Run %d/%d ============\n", i + 1, stress_runs);
1556 options.last_run = (i == stress_runs - 1);
1557 result = RunMain(argc, argv);
1561 result = RunMain(argc, argv);
1565 #if !defined(V8_SHARED) && defined(ENABLE_DEBUGGER_SUPPORT)
1566 // Run remote debugger if requested, but never on --test
1567 if (i::FLAG_remote_debugger && !options.test_shell) {
1568 InstallUtilityScript();
1569 RunRemoteDebugger(i::FLAG_debugger_port);
1572 #endif // !V8_SHARED && ENABLE_DEBUGGER_SUPPORT
1574 // Run interactive shell if explicitly requested or if no script has been
1575 // executed, but never on --test
1577 if (( options.interactive_shell
1578 || !options.script_executed )
1579 && !options.test_shell ) {
1580 #if !defined(V8_SHARED) && defined(ENABLE_DEBUGGER_SUPPORT)
1581 if (!i::FLAG_debugger) {
1582 InstallUtilityScript();
1584 #endif // !V8_SHARED && ENABLE_DEBUGGER_SUPPORT
1601 int main(int argc, char* argv[]) {
1602 return v8::Shell::Main(argc, argv);