2013-12-18 04:45:37 +00:00
|
|
|
/*
|
|
|
|
* Copyright 2013 Google Inc.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
|
|
* found in the LICENSE file.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
#include "Global.h"
|
|
|
|
|
|
|
|
#include "SkWindow.h"
|
|
|
|
#include "SkEvent.h"
|
|
|
|
|
|
|
|
|
|
|
|
Global* Global::gGlobal = NULL;
|
|
|
|
|
|
|
|
// Extracts a C string from a V8 Utf8Value.
|
|
|
|
static const char* to_cstring(const v8::String::Utf8Value& value) {
|
|
|
|
return *value ? *value : "<string conversion failed>";
|
|
|
|
}
|
|
|
|
|
2013-12-20 15:56:52 +00:00
|
|
|
int32_t Global::getNextTimerID() {
|
|
|
|
do {
|
|
|
|
fLastTimerID++;
|
|
|
|
if (fLastTimerID < 0) {
|
|
|
|
fLastTimerID = 0;
|
|
|
|
}
|
|
|
|
} while (fTimeouts.find(fLastTimerID) != fTimeouts.end());
|
|
|
|
return fLastTimerID;
|
|
|
|
}
|
|
|
|
|
2013-12-18 04:45:37 +00:00
|
|
|
// Slight modification to an original function found in the V8 sample shell.cc.
|
2014-10-24 19:49:17 +00:00
|
|
|
void Global::reportException(v8::TryCatch* tryCatch) {
|
|
|
|
v8::HandleScope handleScope(fIsolate);
|
|
|
|
v8::String::Utf8Value exception(tryCatch->Exception());
|
2013-12-18 04:45:37 +00:00
|
|
|
const char* exceptionString = to_cstring(exception);
|
2014-10-24 19:49:17 +00:00
|
|
|
v8::Handle<v8::Message> message = tryCatch->Message();
|
2013-12-18 04:45:37 +00:00
|
|
|
if (message.IsEmpty()) {
|
|
|
|
// V8 didn't provide any extra information about this error; just
|
|
|
|
// print the exception.
|
|
|
|
fprintf(stderr, "%s\n", exceptionString);
|
|
|
|
} else {
|
|
|
|
// Print (filename):(line number): (message).
|
2014-10-24 19:49:17 +00:00
|
|
|
v8::String::Utf8Value filename(message->GetScriptOrigin().ResourceName());
|
2013-12-18 04:45:37 +00:00
|
|
|
const char* filenameString = to_cstring(filename);
|
|
|
|
int linenum = message->GetLineNumber();
|
|
|
|
fprintf(stderr,
|
|
|
|
"%s:%i: %s\n", filenameString, linenum, exceptionString);
|
|
|
|
// Print line of source code.
|
2014-10-24 19:49:17 +00:00
|
|
|
v8::String::Utf8Value sourceline(message->GetSourceLine());
|
2013-12-18 04:45:37 +00:00
|
|
|
const char* sourceLineString = to_cstring(sourceline);
|
|
|
|
fprintf(stderr, "%s\n", sourceLineString);
|
|
|
|
// Print wavy underline.
|
|
|
|
int start = message->GetStartColumn();
|
|
|
|
for (int i = 0; i < start; i++) {
|
|
|
|
fprintf(stderr, " ");
|
|
|
|
}
|
|
|
|
int end = message->GetEndColumn();
|
|
|
|
for (int i = start; i < end; i++) {
|
|
|
|
fprintf(stderr, "^");
|
|
|
|
}
|
|
|
|
fprintf(stderr, "\n");
|
2014-10-24 19:49:17 +00:00
|
|
|
v8::String::Utf8Value stackTrace(tryCatch->StackTrace());
|
2013-12-18 04:45:37 +00:00
|
|
|
if (stackTrace.length() > 0) {
|
|
|
|
const char* stackTraceString = to_cstring(stackTrace);
|
|
|
|
fprintf(stderr, "%s\n", stackTraceString);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// The callback that implements the JavaScript 'inval' function.
|
|
|
|
// Invalidates the current window, forcing a redraw.
|
|
|
|
//
|
|
|
|
// JS: inval();
|
2014-10-24 19:49:17 +00:00
|
|
|
void Global::Inval(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
2013-12-18 04:45:37 +00:00
|
|
|
gGlobal->getWindow()->inval(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
// The callback that is invoked by v8 whenever the JavaScript 'print'
|
|
|
|
// function is called. Prints its arguments on stdout separated by
|
|
|
|
// spaces and ending with a newline.
|
|
|
|
//
|
|
|
|
// JS: print("foo", "bar");
|
|
|
|
void Global::Print(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
|
|
bool first = true;
|
2014-10-24 19:49:17 +00:00
|
|
|
v8::HandleScope handleScope(args.GetIsolate());
|
2013-12-18 04:45:37 +00:00
|
|
|
for (int i = 0; i < args.Length(); i++) {
|
|
|
|
if (first) {
|
|
|
|
first = false;
|
|
|
|
} else {
|
|
|
|
printf(" ");
|
|
|
|
}
|
|
|
|
v8::String::Utf8Value str(args[i]);
|
|
|
|
printf("%s", to_cstring(str));
|
|
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
fflush(stdout);
|
|
|
|
}
|
|
|
|
|
|
|
|
// The callback that is invoked by v8 whenever the JavaScript 'setTimeout'
|
|
|
|
// function is called.
|
|
|
|
//
|
|
|
|
// JS: setTimeout(on_timeout, 500);
|
|
|
|
void Global::SetTimeout(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
|
|
if (args.Length() != 2) {
|
|
|
|
args.GetIsolate()->ThrowException(
|
|
|
|
v8::String::NewFromUtf8(
|
|
|
|
args.GetIsolate(), "Error: 2 arguments required."));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Pull out the first arg, make sure it's a function.
|
|
|
|
if (!args[0]->IsFunction()) {
|
|
|
|
printf("Not a function passed to setTimeout.\n");
|
|
|
|
return;
|
|
|
|
}
|
2014-10-24 19:49:17 +00:00
|
|
|
v8::Handle<v8::Function> timeoutFn = v8::Handle<v8::Function>::Cast(args[0]);
|
2013-12-18 04:45:37 +00:00
|
|
|
|
|
|
|
double delay = args[1]->NumberValue();
|
2013-12-20 15:56:52 +00:00
|
|
|
int32_t id = gGlobal->getNextTimerID();
|
|
|
|
|
|
|
|
gGlobal->fTimeouts[id].Reset(gGlobal->fIsolate, timeoutFn);
|
2013-12-18 04:45:37 +00:00
|
|
|
|
|
|
|
// Create an SkEvent and add it with the right delay.
|
2013-12-20 15:56:52 +00:00
|
|
|
SkEvent* evt = new SkEvent();
|
|
|
|
evt->setTargetProc(Global::TimeOutProc);
|
|
|
|
evt->setFast32(id);
|
|
|
|
evt->postDelay(delay);
|
2013-12-18 04:45:37 +00:00
|
|
|
|
2014-10-24 19:49:17 +00:00
|
|
|
args.GetReturnValue().Set(v8::Integer::New(gGlobal->fIsolate, id));
|
2013-12-18 04:45:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Callback function for SkEvents used to implement timeouts.
|
|
|
|
bool Global::TimeOutProc(const SkEvent& evt) {
|
|
|
|
// Create a handle scope to keep the temporary object references.
|
2014-10-24 19:49:17 +00:00
|
|
|
v8::HandleScope handleScope(gGlobal->getIsolate());
|
2013-12-18 04:45:37 +00:00
|
|
|
|
|
|
|
// Create a local context from our global context.
|
2014-10-24 19:49:17 +00:00
|
|
|
v8::Local<v8::Context> context = gGlobal->getContext();
|
2013-12-18 04:45:37 +00:00
|
|
|
|
|
|
|
// Enter the context so all the remaining operations take place there.
|
2014-10-24 19:49:17 +00:00
|
|
|
v8::Context::Scope contextScope(context);
|
2013-12-18 04:45:37 +00:00
|
|
|
|
|
|
|
// Set up an exception handler before calling the Process function.
|
2014-10-24 19:49:17 +00:00
|
|
|
v8::TryCatch tryCatch;
|
2013-12-18 04:45:37 +00:00
|
|
|
|
2013-12-20 15:56:52 +00:00
|
|
|
int32_t id = evt.getFast32();
|
|
|
|
if (gGlobal->fTimeouts.find(gGlobal->fLastTimerID) == gGlobal->fTimeouts.end()) {
|
|
|
|
printf("Not a valid timer ID.\n");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-12-18 04:45:37 +00:00
|
|
|
const int argc = 0;
|
2014-10-24 19:49:17 +00:00
|
|
|
v8::Local<v8::Function> onTimeout =
|
|
|
|
v8::Local<v8::Function>::New(gGlobal->getIsolate(), gGlobal->fTimeouts[id]);
|
|
|
|
v8::Handle<v8::Value> result = onTimeout->Call(context->Global(), argc, NULL);
|
2013-12-20 15:56:52 +00:00
|
|
|
gGlobal->fTimeouts.erase(id);
|
2013-12-18 04:45:37 +00:00
|
|
|
|
|
|
|
// Handle any exceptions or output.
|
|
|
|
if (result.IsEmpty()) {
|
|
|
|
SkASSERT(tryCatch.HasCaught());
|
|
|
|
// Print errors that happened during execution.
|
|
|
|
gGlobal->reportException(&tryCatch);
|
|
|
|
} else {
|
|
|
|
SkASSERT(!tryCatch.HasCaught());
|
|
|
|
if (!result->IsUndefined()) {
|
|
|
|
// If all went well and the result wasn't undefined then print the
|
|
|
|
// returned value.
|
2014-10-24 19:49:17 +00:00
|
|
|
v8::String::Utf8Value str(result);
|
2013-12-18 04:45:37 +00:00
|
|
|
const char* cstr = to_cstring(str);
|
|
|
|
printf("%s\n", cstr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Creates a new execution environment containing the built-in functions.
|
2014-10-24 19:49:17 +00:00
|
|
|
v8::Handle<v8::Context> Global::createRootContext() {
|
2013-12-18 04:45:37 +00:00
|
|
|
// Create a template for the global object.
|
2014-10-24 19:49:17 +00:00
|
|
|
v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
|
2013-12-18 04:45:37 +00:00
|
|
|
|
|
|
|
global->Set(v8::String::NewFromUtf8(fIsolate, "print"),
|
2014-01-08 15:14:09 +00:00
|
|
|
v8::FunctionTemplate::New(fIsolate, Global::Print));
|
2013-12-18 04:45:37 +00:00
|
|
|
global->Set(v8::String::NewFromUtf8(fIsolate, "setTimeout"),
|
2014-01-08 15:14:09 +00:00
|
|
|
v8::FunctionTemplate::New(fIsolate, Global::SetTimeout));
|
2013-12-18 04:45:37 +00:00
|
|
|
global->Set(v8::String::NewFromUtf8(fIsolate, "inval"),
|
2014-01-08 15:14:09 +00:00
|
|
|
v8::FunctionTemplate::New(fIsolate, Global::Inval));
|
2013-12-18 04:45:37 +00:00
|
|
|
|
|
|
|
|
2014-10-24 19:49:17 +00:00
|
|
|
return v8::Context::New(fIsolate, NULL, global);
|
2013-12-18 04:45:37 +00:00
|
|
|
}
|
|
|
|
|
2014-01-06 18:17:24 +00:00
|
|
|
void Global::initialize() {
|
|
|
|
// Create a stack-allocated handle scope.
|
2014-10-24 19:49:17 +00:00
|
|
|
v8::HandleScope handleScope(fIsolate);
|
2014-01-06 18:17:24 +00:00
|
|
|
|
|
|
|
// Create a new context.
|
2014-10-24 19:49:17 +00:00
|
|
|
v8::Handle<v8::Context> context = this->createRootContext();
|
2014-01-06 18:17:24 +00:00
|
|
|
|
|
|
|
// Make the context persistent.
|
|
|
|
fContext.Reset(fIsolate, context);
|
|
|
|
}
|
|
|
|
|
2013-12-18 04:45:37 +00:00
|
|
|
|
|
|
|
// Creates the root context, parses the script into it, then stores the
|
|
|
|
// context in a global.
|
|
|
|
//
|
|
|
|
// TODO(jcgregorio) Currently only handles one script. Need to move
|
|
|
|
// createRootContext to another call that's only done once.
|
|
|
|
bool Global::parseScript(const char script[]) {
|
|
|
|
|
|
|
|
// Create a stack-allocated handle scope.
|
2014-10-24 19:49:17 +00:00
|
|
|
v8::HandleScope handleScope(fIsolate);
|
2013-12-18 04:45:37 +00:00
|
|
|
|
2014-01-06 18:17:24 +00:00
|
|
|
// Get the global context.
|
2014-10-24 19:49:17 +00:00
|
|
|
v8::Handle<v8::Context> context = this->getContext();
|
2013-12-18 04:45:37 +00:00
|
|
|
|
|
|
|
// Enter the scope so all operations take place in the scope.
|
2014-10-24 19:49:17 +00:00
|
|
|
v8::Context::Scope contextScope(context);
|
2013-12-18 04:45:37 +00:00
|
|
|
|
|
|
|
v8::TryCatch tryCatch;
|
|
|
|
|
|
|
|
// Compile the source code.
|
2014-10-24 19:49:17 +00:00
|
|
|
v8::Handle<v8::String> source = v8::String::NewFromUtf8(fIsolate, script);
|
|
|
|
v8::Handle<v8::Script> compiledScript = v8::Script::Compile(source);
|
2013-12-18 04:45:37 +00:00
|
|
|
|
|
|
|
if (compiledScript.IsEmpty()) {
|
|
|
|
// Print errors that happened during compilation.
|
|
|
|
this->reportException(&tryCatch);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try running it now to create the onDraw function.
|
2014-10-24 19:49:17 +00:00
|
|
|
v8::Handle<v8::Value> result = compiledScript->Run();
|
2013-12-18 04:45:37 +00:00
|
|
|
|
|
|
|
// Handle any exceptions or output.
|
|
|
|
if (result.IsEmpty()) {
|
|
|
|
SkASSERT(tryCatch.HasCaught());
|
|
|
|
// Print errors that happened during execution.
|
|
|
|
this->reportException(&tryCatch);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|