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 <v8.h> 29 #include <assert.h> 30 #include <fcntl.h> 31 #include <string.h> 32 #include <stdio.h> 33 #include <stdlib.h> 34 35 #ifdef COMPRESS_STARTUP_DATA_BZ2 36 #error Using compressed startup data is not supported for this sample 37 #endif 38 39 /** 40 * This sample program shows how to implement a simple javascript shell 41 * based on V8. This includes initializing V8 with command line options, 42 * creating global functions, compiling and executing strings. 43 * 44 * For a more sophisticated shell, consider using the debug shell D8. 45 */ 46 47 48 v8::Handle<v8::Context> CreateShellContext(v8::Isolate* isolate); 49 void RunShell(v8::Handle<v8::Context> context); 50 int RunMain(v8::Isolate* isolate, int argc, char* argv[]); 51 bool ExecuteString(v8::Isolate* isolate, 52 v8::Handle<v8::String> source, 53 v8::Handle<v8::Value> name, 54 bool print_result, 55 bool report_exceptions); 56 void Print(const v8::FunctionCallbackInfo<v8::Value>& args); 57 void Read(const v8::FunctionCallbackInfo<v8::Value>& args); 58 void Load(const v8::FunctionCallbackInfo<v8::Value>& args); 59 void Quit(const v8::FunctionCallbackInfo<v8::Value>& args); 60 void Version(const v8::FunctionCallbackInfo<v8::Value>& args); 61 v8::Handle<v8::String> ReadFile(const char* name); 62 void ReportException(v8::Isolate* isolate, v8::TryCatch* handler); 63 64 65 static bool run_shell; 66 67 68 int main(int argc, char* argv[]) { 69 v8::V8::InitializeICU(); 70 v8::V8::SetFlagsFromCommandLine(&argc, argv, true); 71 v8::Isolate* isolate = v8::Isolate::GetCurrent(); 72 run_shell = (argc == 1); 73 int result; 74 { 75 v8::HandleScope handle_scope(isolate); 76 v8::Handle<v8::Context> context = CreateShellContext(isolate); 77 if (context.IsEmpty()) { 78 fprintf(stderr, "Error creating context\n"); 79 return 1; 80 } 81 context->Enter(); 82 result = RunMain(isolate, argc, argv); 83 if (run_shell) RunShell(context); 84 context->Exit(); 85 } 86 v8::V8::Dispose(); 87 return result; 88 } 89 90 91 // Extracts a C string from a V8 Utf8Value. 92 const char* ToCString(const v8::String::Utf8Value& value) { 93 return *value ? *value : "<string conversion failed>"; 94 } 95 96 97 // Creates a new execution environment containing the built-in 98 // functions. 99 v8::Handle<v8::Context> CreateShellContext(v8::Isolate* isolate) { 100 // Create a template for the global object. 101 v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New(); 102 // Bind the global 'print' function to the C++ Print callback. 103 global->Set(v8::String::New("print"), v8::FunctionTemplate::New(Print)); 104 // Bind the global 'read' function to the C++ Read callback. 105 global->Set(v8::String::New("read"), v8::FunctionTemplate::New(Read)); 106 // Bind the global 'load' function to the C++ Load callback. 107 global->Set(v8::String::New("load"), v8::FunctionTemplate::New(Load)); 108 // Bind the 'quit' function 109 global->Set(v8::String::New("quit"), v8::FunctionTemplate::New(Quit)); 110 // Bind the 'version' function 111 global->Set(v8::String::New("version"), v8::FunctionTemplate::New(Version)); 112 113 return v8::Context::New(isolate, NULL, global); 114 } 115 116 117 // The callback that is invoked by v8 whenever the JavaScript 'print' 118 // function is called. Prints its arguments on stdout separated by 119 // spaces and ending with a newline. 120 void Print(const v8::FunctionCallbackInfo<v8::Value>& args) { 121 bool first = true; 122 for (int i = 0; i < args.Length(); i++) { 123 v8::HandleScope handle_scope(args.GetIsolate()); 124 if (first) { 125 first = false; 126 } else { 127 printf(" "); 128 } 129 v8::String::Utf8Value str(args[i]); 130 const char* cstr = ToCString(str); 131 printf("%s", cstr); 132 } 133 printf("\n"); 134 fflush(stdout); 135 } 136 137 138 // The callback that is invoked by v8 whenever the JavaScript 'read' 139 // function is called. This function loads the content of the file named in 140 // the argument into a JavaScript string. 141 void Read(const v8::FunctionCallbackInfo<v8::Value>& args) { 142 if (args.Length() != 1) { 143 v8::ThrowException(v8::String::New("Bad parameters")); 144 return; 145 } 146 v8::String::Utf8Value file(args[0]); 147 if (*file == NULL) { 148 v8::ThrowException(v8::String::New("Error loading file")); 149 return; 150 } 151 v8::Handle<v8::String> source = ReadFile(*file); 152 if (source.IsEmpty()) { 153 v8::ThrowException(v8::String::New("Error loading file")); 154 return; 155 } 156 args.GetReturnValue().Set(source); 157 } 158 159 160 // The callback that is invoked by v8 whenever the JavaScript 'load' 161 // function is called. Loads, compiles and executes its argument 162 // JavaScript file. 163 void Load(const v8::FunctionCallbackInfo<v8::Value>& args) { 164 for (int i = 0; i < args.Length(); i++) { 165 v8::HandleScope handle_scope(args.GetIsolate()); 166 v8::String::Utf8Value file(args[i]); 167 if (*file == NULL) { 168 v8::ThrowException(v8::String::New("Error loading file")); 169 return; 170 } 171 v8::Handle<v8::String> source = ReadFile(*file); 172 if (source.IsEmpty()) { 173 v8::ThrowException(v8::String::New("Error loading file")); 174 return; 175 } 176 if (!ExecuteString(args.GetIsolate(), 177 source, 178 v8::String::New(*file), 179 false, 180 false)) { 181 v8::ThrowException(v8::String::New("Error executing file")); 182 return; 183 } 184 } 185 } 186 187 188 // The callback that is invoked by v8 whenever the JavaScript 'quit' 189 // function is called. Quits. 190 void Quit(const v8::FunctionCallbackInfo<v8::Value>& args) { 191 // If not arguments are given args[0] will yield undefined which 192 // converts to the integer value 0. 193 int exit_code = args[0]->Int32Value(); 194 fflush(stdout); 195 fflush(stderr); 196 exit(exit_code); 197 } 198 199 200 void Version(const v8::FunctionCallbackInfo<v8::Value>& args) { 201 args.GetReturnValue().Set(v8::String::New(v8::V8::GetVersion())); 202 } 203 204 205 // Reads a file into a v8 string. 206 v8::Handle<v8::String> ReadFile(const char* name) { 207 FILE* file = fopen(name, "rb"); 208 if (file == NULL) return v8::Handle<v8::String>(); 209 210 fseek(file, 0, SEEK_END); 211 int size = ftell(file); 212 rewind(file); 213 214 char* chars = new char[size + 1]; 215 chars[size] = '\0'; 216 for (int i = 0; i < size;) { 217 int read = static_cast<int>(fread(&chars[i], 1, size - i, file)); 218 i += read; 219 } 220 fclose(file); 221 v8::Handle<v8::String> result = v8::String::New(chars, size); 222 delete[] chars; 223 return result; 224 } 225 226 227 // Process remaining command line arguments and execute files 228 int RunMain(v8::Isolate* isolate, int argc, char* argv[]) { 229 for (int i = 1; i < argc; i++) { 230 const char* str = argv[i]; 231 if (strcmp(str, "--shell") == 0) { 232 run_shell = true; 233 } else if (strcmp(str, "-f") == 0) { 234 // Ignore any -f flags for compatibility with the other stand- 235 // alone JavaScript engines. 236 continue; 237 } else if (strncmp(str, "--", 2) == 0) { 238 fprintf(stderr, 239 "Warning: unknown flag %s.\nTry --help for options\n", str); 240 } else if (strcmp(str, "-e") == 0 && i + 1 < argc) { 241 // Execute argument given to -e option directly. 242 v8::Handle<v8::String> file_name = v8::String::New("unnamed"); 243 v8::Handle<v8::String> source = v8::String::New(argv[++i]); 244 if (!ExecuteString(isolate, source, file_name, false, true)) return 1; 245 } else { 246 // Use all other arguments as names of files to load and run. 247 v8::Handle<v8::String> file_name = v8::String::New(str); 248 v8::Handle<v8::String> source = ReadFile(str); 249 if (source.IsEmpty()) { 250 fprintf(stderr, "Error reading '%s'\n", str); 251 continue; 252 } 253 if (!ExecuteString(isolate, source, file_name, false, true)) return 1; 254 } 255 } 256 return 0; 257 } 258 259 260 // The read-eval-execute loop of the shell. 261 void RunShell(v8::Handle<v8::Context> context) { 262 fprintf(stderr, "V8 version %s [sample shell]\n", v8::V8::GetVersion()); 263 static const int kBufferSize = 256; 264 // Enter the execution environment before evaluating any code. 265 v8::Context::Scope context_scope(context); 266 v8::Local<v8::String> name(v8::String::New("(shell)")); 267 while (true) { 268 char buffer[kBufferSize]; 269 fprintf(stderr, "> "); 270 char* str = fgets(buffer, kBufferSize, stdin); 271 if (str == NULL) break; 272 v8::HandleScope handle_scope(context->GetIsolate()); 273 ExecuteString(context->GetIsolate(), 274 v8::String::New(str), 275 name, 276 true, 277 true); 278 } 279 fprintf(stderr, "\n"); 280 } 281 282 283 // Executes a string within the current v8 context. 284 bool ExecuteString(v8::Isolate* isolate, 285 v8::Handle<v8::String> source, 286 v8::Handle<v8::Value> name, 287 bool print_result, 288 bool report_exceptions) { 289 v8::HandleScope handle_scope(isolate); 290 v8::TryCatch try_catch; 291 v8::Handle<v8::Script> script = v8::Script::Compile(source, name); 292 if (script.IsEmpty()) { 293 // Print errors that happened during compilation. 294 if (report_exceptions) 295 ReportException(isolate, &try_catch); 296 return false; 297 } else { 298 v8::Handle<v8::Value> result = script->Run(); 299 if (result.IsEmpty()) { 300 assert(try_catch.HasCaught()); 301 // Print errors that happened during execution. 302 if (report_exceptions) 303 ReportException(isolate, &try_catch); 304 return false; 305 } else { 306 assert(!try_catch.HasCaught()); 307 if (print_result && !result->IsUndefined()) { 308 // If all went well and the result wasn't undefined then print 309 // the returned value. 310 v8::String::Utf8Value str(result); 311 const char* cstr = ToCString(str); 312 printf("%s\n", cstr); 313 } 314 return true; 315 } 316 } 317 } 318 319 320 void ReportException(v8::Isolate* isolate, v8::TryCatch* try_catch) { 321 v8::HandleScope handle_scope(isolate); 322 v8::String::Utf8Value exception(try_catch->Exception()); 323 const char* exception_string = ToCString(exception); 324 v8::Handle<v8::Message> message = try_catch->Message(); 325 if (message.IsEmpty()) { 326 // V8 didn't provide any extra information about this error; just 327 // print the exception. 328 fprintf(stderr, "%s\n", exception_string); 329 } else { 330 // Print (filename):(line number): (message). 331 v8::String::Utf8Value filename(message->GetScriptResourceName()); 332 const char* filename_string = ToCString(filename); 333 int linenum = message->GetLineNumber(); 334 fprintf(stderr, "%s:%i: %s\n", filename_string, linenum, exception_string); 335 // Print line of source code. 336 v8::String::Utf8Value sourceline(message->GetSourceLine()); 337 const char* sourceline_string = ToCString(sourceline); 338 fprintf(stderr, "%s\n", sourceline_string); 339 // Print wavy underline (GetUnderline is deprecated). 340 int start = message->GetStartColumn(); 341 for (int i = 0; i < start; i++) { 342 fprintf(stderr, " "); 343 } 344 int end = message->GetEndColumn(); 345 for (int i = start; i < end; i++) { 346 fprintf(stderr, "^"); 347 } 348 fprintf(stderr, "\n"); 349 v8::String::Utf8Value stack_trace(try_catch->StackTrace()); 350 if (stack_trace.length() > 0) { 351 const char* stack_trace_string = ToCString(stack_trace); 352 fprintf(stderr, "%s\n", stack_trace_string); 353 } 354 } 355 } 356