1// Copyright 2012 the V8 project authors. All rights reserved. 2// Redistribution and use in source and binary forms, with or without 3// modification, are permitted provided that the following conditions are 4// met: 5// 6// * Redistributions of source code must retain the above copyright 7// notice, this list of conditions and the following disclaimer. 8// * Redistributions in binary form must reproduce the above 9// copyright notice, this list of conditions and the following 10// disclaimer in the documentation and/or other materials provided 11// with the distribution. 12// * Neither the name of Google Inc. nor the names of its 13// contributors may be used to endorse or promote products derived 14// from this software without specific prior written permission. 15// 16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28#include <include/v8.h> 29 30#include <include/libplatform/libplatform.h> 31 32#include <assert.h> 33#include <fcntl.h> 34#include <stdio.h> 35#include <stdlib.h> 36#include <string.h> 37 38/** 39 * This sample program shows how to implement a simple javascript shell 40 * based on V8. This includes initializing V8 with command line options, 41 * creating global functions, compiling and executing strings. 42 * 43 * For a more sophisticated shell, consider using the debug shell D8. 44 */ 45 46 47v8::Local<v8::Context> CreateShellContext(v8::Isolate* isolate); 48void RunShell(v8::Local<v8::Context> context, v8::Platform* platform); 49int RunMain(v8::Isolate* isolate, v8::Platform* platform, int argc, 50 char* argv[]); 51bool ExecuteString(v8::Isolate* isolate, v8::Local<v8::String> source, 52 v8::Local<v8::Value> name, bool print_result, 53 bool report_exceptions); 54void Print(const v8::FunctionCallbackInfo<v8::Value>& args); 55void Read(const v8::FunctionCallbackInfo<v8::Value>& args); 56void Load(const v8::FunctionCallbackInfo<v8::Value>& args); 57void Quit(const v8::FunctionCallbackInfo<v8::Value>& args); 58void Version(const v8::FunctionCallbackInfo<v8::Value>& args); 59v8::MaybeLocal<v8::String> ReadFile(v8::Isolate* isolate, const char* name); 60void ReportException(v8::Isolate* isolate, v8::TryCatch* handler); 61 62 63static bool run_shell; 64 65 66int main(int argc, char* argv[]) { 67 v8::V8::InitializeICUDefaultLocation(argv[0]); 68 v8::V8::InitializeExternalStartupData(argv[0]); 69 v8::Platform* platform = v8::platform::CreateDefaultPlatform(); 70 v8::V8::InitializePlatform(platform); 71 v8::V8::Initialize(); 72 v8::V8::SetFlagsFromCommandLine(&argc, argv, true); 73 v8::Isolate::CreateParams create_params; 74 create_params.array_buffer_allocator = 75 v8::ArrayBuffer::Allocator::NewDefaultAllocator(); 76 v8::Isolate* isolate = v8::Isolate::New(create_params); 77 run_shell = (argc == 1); 78 int result; 79 { 80 v8::Isolate::Scope isolate_scope(isolate); 81 v8::HandleScope handle_scope(isolate); 82 v8::Local<v8::Context> context = CreateShellContext(isolate); 83 if (context.IsEmpty()) { 84 fprintf(stderr, "Error creating context\n"); 85 return 1; 86 } 87 v8::Context::Scope context_scope(context); 88 result = RunMain(isolate, platform, argc, argv); 89 if (run_shell) RunShell(context, platform); 90 } 91 isolate->Dispose(); 92 v8::V8::Dispose(); 93 v8::V8::ShutdownPlatform(); 94 delete platform; 95 delete create_params.array_buffer_allocator; 96 return result; 97} 98 99 100// Extracts a C string from a V8 Utf8Value. 101const char* ToCString(const v8::String::Utf8Value& value) { 102 return *value ? *value : "<string conversion failed>"; 103} 104 105 106// Creates a new execution environment containing the built-in 107// functions. 108v8::Local<v8::Context> CreateShellContext(v8::Isolate* isolate) { 109 // Create a template for the global object. 110 v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate); 111 // Bind the global 'print' function to the C++ Print callback. 112 global->Set( 113 v8::String::NewFromUtf8(isolate, "print", v8::NewStringType::kNormal) 114 .ToLocalChecked(), 115 v8::FunctionTemplate::New(isolate, Print)); 116 // Bind the global 'read' function to the C++ Read callback. 117 global->Set(v8::String::NewFromUtf8( 118 isolate, "read", v8::NewStringType::kNormal).ToLocalChecked(), 119 v8::FunctionTemplate::New(isolate, Read)); 120 // Bind the global 'load' function to the C++ Load callback. 121 global->Set(v8::String::NewFromUtf8( 122 isolate, "load", v8::NewStringType::kNormal).ToLocalChecked(), 123 v8::FunctionTemplate::New(isolate, Load)); 124 // Bind the 'quit' function 125 global->Set(v8::String::NewFromUtf8( 126 isolate, "quit", v8::NewStringType::kNormal).ToLocalChecked(), 127 v8::FunctionTemplate::New(isolate, Quit)); 128 // Bind the 'version' function 129 global->Set( 130 v8::String::NewFromUtf8(isolate, "version", v8::NewStringType::kNormal) 131 .ToLocalChecked(), 132 v8::FunctionTemplate::New(isolate, Version)); 133 134 return v8::Context::New(isolate, NULL, global); 135} 136 137 138// The callback that is invoked by v8 whenever the JavaScript 'print' 139// function is called. Prints its arguments on stdout separated by 140// spaces and ending with a newline. 141void Print(const v8::FunctionCallbackInfo<v8::Value>& args) { 142 bool first = true; 143 for (int i = 0; i < args.Length(); i++) { 144 v8::HandleScope handle_scope(args.GetIsolate()); 145 if (first) { 146 first = false; 147 } else { 148 printf(" "); 149 } 150 v8::String::Utf8Value str(args[i]); 151 const char* cstr = ToCString(str); 152 printf("%s", cstr); 153 } 154 printf("\n"); 155 fflush(stdout); 156} 157 158 159// The callback that is invoked by v8 whenever the JavaScript 'read' 160// function is called. This function loads the content of the file named in 161// the argument into a JavaScript string. 162void Read(const v8::FunctionCallbackInfo<v8::Value>& args) { 163 if (args.Length() != 1) { 164 args.GetIsolate()->ThrowException( 165 v8::String::NewFromUtf8(args.GetIsolate(), "Bad parameters", 166 v8::NewStringType::kNormal).ToLocalChecked()); 167 return; 168 } 169 v8::String::Utf8Value file(args[0]); 170 if (*file == NULL) { 171 args.GetIsolate()->ThrowException( 172 v8::String::NewFromUtf8(args.GetIsolate(), "Error loading file", 173 v8::NewStringType::kNormal).ToLocalChecked()); 174 return; 175 } 176 v8::Local<v8::String> source; 177 if (!ReadFile(args.GetIsolate(), *file).ToLocal(&source)) { 178 args.GetIsolate()->ThrowException( 179 v8::String::NewFromUtf8(args.GetIsolate(), "Error loading file", 180 v8::NewStringType::kNormal).ToLocalChecked()); 181 return; 182 } 183 args.GetReturnValue().Set(source); 184} 185 186 187// The callback that is invoked by v8 whenever the JavaScript 'load' 188// function is called. Loads, compiles and executes its argument 189// JavaScript file. 190void Load(const v8::FunctionCallbackInfo<v8::Value>& args) { 191 for (int i = 0; i < args.Length(); i++) { 192 v8::HandleScope handle_scope(args.GetIsolate()); 193 v8::String::Utf8Value file(args[i]); 194 if (*file == NULL) { 195 args.GetIsolate()->ThrowException( 196 v8::String::NewFromUtf8(args.GetIsolate(), "Error loading file", 197 v8::NewStringType::kNormal).ToLocalChecked()); 198 return; 199 } 200 v8::Local<v8::String> source; 201 if (!ReadFile(args.GetIsolate(), *file).ToLocal(&source)) { 202 args.GetIsolate()->ThrowException( 203 v8::String::NewFromUtf8(args.GetIsolate(), "Error loading file", 204 v8::NewStringType::kNormal).ToLocalChecked()); 205 return; 206 } 207 if (!ExecuteString(args.GetIsolate(), source, args[i], false, false)) { 208 args.GetIsolate()->ThrowException( 209 v8::String::NewFromUtf8(args.GetIsolate(), "Error executing file", 210 v8::NewStringType::kNormal).ToLocalChecked()); 211 return; 212 } 213 } 214} 215 216 217// The callback that is invoked by v8 whenever the JavaScript 'quit' 218// function is called. Quits. 219void Quit(const v8::FunctionCallbackInfo<v8::Value>& args) { 220 // If not arguments are given args[0] will yield undefined which 221 // converts to the integer value 0. 222 int exit_code = 223 args[0]->Int32Value(args.GetIsolate()->GetCurrentContext()).FromMaybe(0); 224 fflush(stdout); 225 fflush(stderr); 226 exit(exit_code); 227} 228 229 230void Version(const v8::FunctionCallbackInfo<v8::Value>& args) { 231 args.GetReturnValue().Set( 232 v8::String::NewFromUtf8(args.GetIsolate(), v8::V8::GetVersion(), 233 v8::NewStringType::kNormal).ToLocalChecked()); 234} 235 236 237// Reads a file into a v8 string. 238v8::MaybeLocal<v8::String> ReadFile(v8::Isolate* isolate, const char* name) { 239 FILE* file = fopen(name, "rb"); 240 if (file == NULL) return v8::MaybeLocal<v8::String>(); 241 242 fseek(file, 0, SEEK_END); 243 size_t size = ftell(file); 244 rewind(file); 245 246 char* chars = new char[size + 1]; 247 chars[size] = '\0'; 248 for (size_t i = 0; i < size;) { 249 i += fread(&chars[i], 1, size - i, file); 250 if (ferror(file)) { 251 fclose(file); 252 return v8::MaybeLocal<v8::String>(); 253 } 254 } 255 fclose(file); 256 v8::MaybeLocal<v8::String> result = v8::String::NewFromUtf8( 257 isolate, chars, v8::NewStringType::kNormal, static_cast<int>(size)); 258 delete[] chars; 259 return result; 260} 261 262 263// Process remaining command line arguments and execute files 264int RunMain(v8::Isolate* isolate, v8::Platform* platform, int argc, 265 char* argv[]) { 266 for (int i = 1; i < argc; i++) { 267 const char* str = argv[i]; 268 if (strcmp(str, "--shell") == 0) { 269 run_shell = true; 270 } else if (strcmp(str, "-f") == 0) { 271 // Ignore any -f flags for compatibility with the other stand- 272 // alone JavaScript engines. 273 continue; 274 } else if (strncmp(str, "--", 2) == 0) { 275 fprintf(stderr, 276 "Warning: unknown flag %s.\nTry --help for options\n", str); 277 } else if (strcmp(str, "-e") == 0 && i + 1 < argc) { 278 // Execute argument given to -e option directly. 279 v8::Local<v8::String> file_name = 280 v8::String::NewFromUtf8(isolate, "unnamed", 281 v8::NewStringType::kNormal).ToLocalChecked(); 282 v8::Local<v8::String> source; 283 if (!v8::String::NewFromUtf8(isolate, argv[++i], 284 v8::NewStringType::kNormal) 285 .ToLocal(&source)) { 286 return 1; 287 } 288 bool success = ExecuteString(isolate, source, file_name, false, true); 289 while (v8::platform::PumpMessageLoop(platform, isolate)) continue; 290 if (!success) return 1; 291 } else { 292 // Use all other arguments as names of files to load and run. 293 v8::Local<v8::String> file_name = 294 v8::String::NewFromUtf8(isolate, str, v8::NewStringType::kNormal) 295 .ToLocalChecked(); 296 v8::Local<v8::String> source; 297 if (!ReadFile(isolate, str).ToLocal(&source)) { 298 fprintf(stderr, "Error reading '%s'\n", str); 299 continue; 300 } 301 bool success = ExecuteString(isolate, source, file_name, false, true); 302 while (v8::platform::PumpMessageLoop(platform, isolate)) continue; 303 if (!success) return 1; 304 } 305 } 306 return 0; 307} 308 309 310// The read-eval-execute loop of the shell. 311void RunShell(v8::Local<v8::Context> context, v8::Platform* platform) { 312 fprintf(stderr, "V8 version %s [sample shell]\n", v8::V8::GetVersion()); 313 static const int kBufferSize = 256; 314 // Enter the execution environment before evaluating any code. 315 v8::Context::Scope context_scope(context); 316 v8::Local<v8::String> name( 317 v8::String::NewFromUtf8(context->GetIsolate(), "(shell)", 318 v8::NewStringType::kNormal).ToLocalChecked()); 319 while (true) { 320 char buffer[kBufferSize]; 321 fprintf(stderr, "> "); 322 char* str = fgets(buffer, kBufferSize, stdin); 323 if (str == NULL) break; 324 v8::HandleScope handle_scope(context->GetIsolate()); 325 ExecuteString( 326 context->GetIsolate(), 327 v8::String::NewFromUtf8(context->GetIsolate(), str, 328 v8::NewStringType::kNormal).ToLocalChecked(), 329 name, true, true); 330 while (v8::platform::PumpMessageLoop(platform, context->GetIsolate())) 331 continue; 332 } 333 fprintf(stderr, "\n"); 334} 335 336 337// Executes a string within the current v8 context. 338bool ExecuteString(v8::Isolate* isolate, v8::Local<v8::String> source, 339 v8::Local<v8::Value> name, bool print_result, 340 bool report_exceptions) { 341 v8::HandleScope handle_scope(isolate); 342 v8::TryCatch try_catch(isolate); 343 v8::ScriptOrigin origin(name); 344 v8::Local<v8::Context> context(isolate->GetCurrentContext()); 345 v8::Local<v8::Script> script; 346 if (!v8::Script::Compile(context, source, &origin).ToLocal(&script)) { 347 // Print errors that happened during compilation. 348 if (report_exceptions) 349 ReportException(isolate, &try_catch); 350 return false; 351 } else { 352 v8::Local<v8::Value> result; 353 if (!script->Run(context).ToLocal(&result)) { 354 assert(try_catch.HasCaught()); 355 // Print errors that happened during execution. 356 if (report_exceptions) 357 ReportException(isolate, &try_catch); 358 return false; 359 } else { 360 assert(!try_catch.HasCaught()); 361 if (print_result && !result->IsUndefined()) { 362 // If all went well and the result wasn't undefined then print 363 // the returned value. 364 v8::String::Utf8Value str(result); 365 const char* cstr = ToCString(str); 366 printf("%s\n", cstr); 367 } 368 return true; 369 } 370 } 371} 372 373 374void ReportException(v8::Isolate* isolate, v8::TryCatch* try_catch) { 375 v8::HandleScope handle_scope(isolate); 376 v8::String::Utf8Value exception(try_catch->Exception()); 377 const char* exception_string = ToCString(exception); 378 v8::Local<v8::Message> message = try_catch->Message(); 379 if (message.IsEmpty()) { 380 // V8 didn't provide any extra information about this error; just 381 // print the exception. 382 fprintf(stderr, "%s\n", exception_string); 383 } else { 384 // Print (filename):(line number): (message). 385 v8::String::Utf8Value filename(message->GetScriptOrigin().ResourceName()); 386 v8::Local<v8::Context> context(isolate->GetCurrentContext()); 387 const char* filename_string = ToCString(filename); 388 int linenum = message->GetLineNumber(context).FromJust(); 389 fprintf(stderr, "%s:%i: %s\n", filename_string, linenum, exception_string); 390 // Print line of source code. 391 v8::String::Utf8Value sourceline( 392 message->GetSourceLine(context).ToLocalChecked()); 393 const char* sourceline_string = ToCString(sourceline); 394 fprintf(stderr, "%s\n", sourceline_string); 395 // Print wavy underline (GetUnderline is deprecated). 396 int start = message->GetStartColumn(context).FromJust(); 397 for (int i = 0; i < start; i++) { 398 fprintf(stderr, " "); 399 } 400 int end = message->GetEndColumn(context).FromJust(); 401 for (int i = start; i < end; i++) { 402 fprintf(stderr, "^"); 403 } 404 fprintf(stderr, "\n"); 405 v8::Local<v8::Value> stack_trace_string; 406 if (try_catch->StackTrace(context).ToLocal(&stack_trace_string) && 407 stack_trace_string->IsString() && 408 v8::Local<v8::String>::Cast(stack_trace_string)->Length() > 0) { 409 v8::String::Utf8Value stack_trace(stack_trace_string); 410 const char* stack_trace_string = ToCString(stack_trace); 411 fprintf(stderr, "%s\n", stack_trace_string); 412 } 413 } 414} 415