Home | History | Annotate | Download | only in SkV8Example
      1 /*
      2  * Copyright 2013 Google Inc.
      3  *
      4  *
      5  * Use of this source code is governed by a BSD-style license that can be
      6  * found in the LICENSE file.
      7  *
      8  */
      9 #include "Global.h"
     10 
     11 #include "SkWindow.h"
     12 #include "SkEvent.h"
     13 
     14 
     15 Global* Global::gGlobal = NULL;
     16 
     17 // Extracts a C string from a V8 Utf8Value.
     18 static const char* to_cstring(const v8::String::Utf8Value& value) {
     19     return *value ? *value : "<string conversion failed>";
     20 }
     21 
     22 int32_t Global::getNextTimerID() {
     23     do {
     24         fLastTimerID++;
     25         if (fLastTimerID < 0) {
     26             fLastTimerID = 0;
     27         }
     28     } while (fTimeouts.find(fLastTimerID) != fTimeouts.end());
     29     return fLastTimerID;
     30 }
     31 
     32 // Slight modification to an original function found in the V8 sample shell.cc.
     33 void Global::reportException(TryCatch* tryCatch) {
     34     HandleScope handleScope(fIsolate);
     35     String::Utf8Value exception(tryCatch->Exception());
     36     const char* exceptionString = to_cstring(exception);
     37     Handle<Message> message = tryCatch->Message();
     38     if (message.IsEmpty()) {
     39         // V8 didn't provide any extra information about this error; just
     40         // print the exception.
     41         fprintf(stderr, "%s\n", exceptionString);
     42     } else {
     43         // Print (filename):(line number): (message).
     44         String::Utf8Value filename(message->GetScriptOrigin().ResourceName());
     45         const char* filenameString = to_cstring(filename);
     46         int linenum = message->GetLineNumber();
     47         fprintf(stderr,
     48                 "%s:%i: %s\n", filenameString, linenum, exceptionString);
     49         // Print line of source code.
     50         String::Utf8Value sourceline(message->GetSourceLine());
     51         const char* sourceLineString = to_cstring(sourceline);
     52         fprintf(stderr, "%s\n", sourceLineString);
     53         // Print wavy underline.
     54         int start = message->GetStartColumn();
     55         for (int i = 0; i < start; i++) {
     56             fprintf(stderr, " ");
     57         }
     58         int end = message->GetEndColumn();
     59         for (int i = start; i < end; i++) {
     60             fprintf(stderr, "^");
     61         }
     62         fprintf(stderr, "\n");
     63         String::Utf8Value stackTrace(tryCatch->StackTrace());
     64         if (stackTrace.length() > 0) {
     65             const char* stackTraceString = to_cstring(stackTrace);
     66             fprintf(stderr, "%s\n", stackTraceString);
     67         }
     68     }
     69 }
     70 
     71 // The callback that implements the JavaScript 'inval' function.
     72 // Invalidates the current window, forcing a redraw.
     73 //
     74 // JS: inval();
     75 void Global::Inval(const v8::FunctionCallbackInfo<Value>& args) {
     76     gGlobal->getWindow()->inval(NULL);
     77 }
     78 
     79 // The callback that is invoked by v8 whenever the JavaScript 'print'
     80 // function is called. Prints its arguments on stdout separated by
     81 // spaces and ending with a newline.
     82 //
     83 // JS: print("foo", "bar");
     84 void Global::Print(const v8::FunctionCallbackInfo<v8::Value>& args) {
     85     bool first = true;
     86     HandleScope handleScope(args.GetIsolate());
     87     for (int i = 0; i < args.Length(); i++) {
     88         if (first) {
     89             first = false;
     90         } else {
     91             printf(" ");
     92         }
     93         v8::String::Utf8Value str(args[i]);
     94         printf("%s", to_cstring(str));
     95     }
     96     printf("\n");
     97     fflush(stdout);
     98 }
     99 
    100 // The callback that is invoked by v8 whenever the JavaScript 'setTimeout'
    101 // function is called.
    102 //
    103 // JS: setTimeout(on_timeout, 500);
    104 void Global::SetTimeout(const v8::FunctionCallbackInfo<v8::Value>& args) {
    105     if (args.Length() != 2) {
    106         args.GetIsolate()->ThrowException(
    107                 v8::String::NewFromUtf8(
    108                         args.GetIsolate(), "Error: 2 arguments required."));
    109         return;
    110     }
    111 
    112     // Pull out the first arg, make sure it's a function.
    113     if (!args[0]->IsFunction()) {
    114         printf("Not a function passed to setTimeout.\n");
    115         return;
    116     }
    117     Handle<Function> timeoutFn = Handle<Function>::Cast(args[0]);
    118 
    119     double delay = args[1]->NumberValue();
    120     int32_t id = gGlobal->getNextTimerID();
    121 
    122     gGlobal->fTimeouts[id].Reset(gGlobal->fIsolate, timeoutFn);
    123 
    124     // Create an SkEvent and add it with the right delay.
    125     SkEvent* evt = new SkEvent();
    126     evt->setTargetProc(Global::TimeOutProc);
    127     evt->setFast32(id);
    128     evt->postDelay(delay);
    129 
    130     args.GetReturnValue().Set(Integer::New(gGlobal->fIsolate, id));
    131 }
    132 
    133 // Callback function for SkEvents used to implement timeouts.
    134 bool Global::TimeOutProc(const SkEvent& evt) {
    135     // Create a handle scope to keep the temporary object references.
    136     HandleScope handleScope(gGlobal->getIsolate());
    137 
    138     // Create a local context from our global context.
    139     Local<Context> context = gGlobal->getContext();
    140 
    141     // Enter the context so all the remaining operations take place there.
    142     Context::Scope contextScope(context);
    143 
    144     // Set up an exception handler before calling the Process function.
    145     TryCatch tryCatch;
    146 
    147     int32_t id = evt.getFast32();
    148     if (gGlobal->fTimeouts.find(gGlobal->fLastTimerID) == gGlobal->fTimeouts.end()) {
    149         printf("Not a valid timer ID.\n");
    150         return true;
    151     }
    152 
    153     const int argc = 0;
    154     Local<Function> onTimeout =
    155             Local<Function>::New(gGlobal->getIsolate(), gGlobal->fTimeouts[id]);
    156     Handle<Value> result = onTimeout->Call(context->Global(), argc, NULL);
    157     gGlobal->fTimeouts.erase(id);
    158 
    159     // Handle any exceptions or output.
    160     if (result.IsEmpty()) {
    161         SkASSERT(tryCatch.HasCaught());
    162         // Print errors that happened during execution.
    163         gGlobal->reportException(&tryCatch);
    164     } else {
    165         SkASSERT(!tryCatch.HasCaught());
    166         if (!result->IsUndefined()) {
    167             // If all went well and the result wasn't undefined then print the
    168             // returned value.
    169             String::Utf8Value str(result);
    170             const char* cstr = to_cstring(str);
    171             printf("%s\n", cstr);
    172         }
    173     }
    174     return true;
    175 }
    176 
    177 // Creates a new execution environment containing the built-in functions.
    178 Handle<Context> Global::createRootContext() {
    179   // Create a template for the global object.
    180   Handle<ObjectTemplate> global = ObjectTemplate::New();
    181 
    182   global->Set(v8::String::NewFromUtf8(fIsolate, "print"),
    183               v8::FunctionTemplate::New(fIsolate, Global::Print));
    184   global->Set(v8::String::NewFromUtf8(fIsolate, "setTimeout"),
    185               v8::FunctionTemplate::New(fIsolate, Global::SetTimeout));
    186   global->Set(v8::String::NewFromUtf8(fIsolate, "inval"),
    187               v8::FunctionTemplate::New(fIsolate, Global::Inval));
    188 
    189 
    190   return Context::New(fIsolate, NULL, global);
    191 }
    192 
    193 void Global::initialize() {
    194     // Create a stack-allocated handle scope.
    195     HandleScope handleScope(fIsolate);
    196 
    197     // Create a new context.
    198     Handle<Context> context = this->createRootContext();
    199 
    200     // Make the context persistent.
    201     fContext.Reset(fIsolate, context);
    202 }
    203 
    204 
    205 // Creates the root context, parses the script into it, then stores the
    206 // context in a global.
    207 //
    208 // TODO(jcgregorio) Currently only handles one script. Need to move
    209 // createRootContext to another call that's only done once.
    210 bool Global::parseScript(const char script[]) {
    211 
    212     // Create a stack-allocated handle scope.
    213     HandleScope handleScope(fIsolate);
    214 
    215     // Get the global context.
    216     Handle<Context> context = this->getContext();
    217 
    218     // Enter the scope so all operations take place in the scope.
    219     Context::Scope contextScope(context);
    220 
    221     v8::TryCatch tryCatch;
    222 
    223     // Compile the source code.
    224     Handle<String> source = String::NewFromUtf8(fIsolate, script);
    225     Handle<Script> compiledScript = Script::Compile(source);
    226 
    227     if (compiledScript.IsEmpty()) {
    228         // Print errors that happened during compilation.
    229         this->reportException(&tryCatch);
    230         return false;
    231     }
    232 
    233     // Try running it now to create the onDraw function.
    234     Handle<Value> result = compiledScript->Run();
    235 
    236     // Handle any exceptions or output.
    237     if (result.IsEmpty()) {
    238         SkASSERT(tryCatch.HasCaught());
    239         // Print errors that happened during execution.
    240         this->reportException(&tryCatch);
    241         return false;
    242     }
    243 
    244     return true;
    245 }
    246