blob: 919f24786975c93abf1111d0c25f5de71bf290ac [file] [log] [blame]
commit-bot@chromium.org0fc0dea2013-12-18 04:45:37 +00001/*
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
15Global* Global::gGlobal = NULL;
16
17// Extracts a C string from a V8 Utf8Value.
18static const char* to_cstring(const v8::String::Utf8Value& value) {
19 return *value ? *value : "<string conversion failed>";
20}
21
commit-bot@chromium.org872796b2013-12-20 15:56:52 +000022int32_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
commit-bot@chromium.org0fc0dea2013-12-18 04:45:37 +000032// Slight modification to an original function found in the V8 sample shell.cc.
jcgregorioe22f45f2014-10-24 12:49:17 -070033void Global::reportException(v8::TryCatch* tryCatch) {
34 v8::HandleScope handleScope(fIsolate);
35 v8::String::Utf8Value exception(tryCatch->Exception());
commit-bot@chromium.org0fc0dea2013-12-18 04:45:37 +000036 const char* exceptionString = to_cstring(exception);
jcgregorioe22f45f2014-10-24 12:49:17 -070037 v8::Handle<v8::Message> message = tryCatch->Message();
commit-bot@chromium.org0fc0dea2013-12-18 04:45:37 +000038 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).
jcgregorioe22f45f2014-10-24 12:49:17 -070044 v8::String::Utf8Value filename(message->GetScriptOrigin().ResourceName());
commit-bot@chromium.org0fc0dea2013-12-18 04:45:37 +000045 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.
jcgregorioe22f45f2014-10-24 12:49:17 -070050 v8::String::Utf8Value sourceline(message->GetSourceLine());
commit-bot@chromium.org0fc0dea2013-12-18 04:45:37 +000051 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");
jcgregorioe22f45f2014-10-24 12:49:17 -070063 v8::String::Utf8Value stackTrace(tryCatch->StackTrace());
commit-bot@chromium.org0fc0dea2013-12-18 04:45:37 +000064 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();
jcgregorioe22f45f2014-10-24 12:49:17 -070075void Global::Inval(const v8::FunctionCallbackInfo<v8::Value>& args) {
commit-bot@chromium.org0fc0dea2013-12-18 04:45:37 +000076 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");
84void Global::Print(const v8::FunctionCallbackInfo<v8::Value>& args) {
85 bool first = true;
jcgregorioe22f45f2014-10-24 12:49:17 -070086 v8::HandleScope handleScope(args.GetIsolate());
commit-bot@chromium.org0fc0dea2013-12-18 04:45:37 +000087 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);
commit-bot@chromium.org0fc0dea2013-12-18 04:45:37 +0000104void 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 }
jcgregorioe22f45f2014-10-24 12:49:17 -0700117 v8::Handle<v8::Function> timeoutFn = v8::Handle<v8::Function>::Cast(args[0]);
commit-bot@chromium.org0fc0dea2013-12-18 04:45:37 +0000118
119 double delay = args[1]->NumberValue();
commit-bot@chromium.org872796b2013-12-20 15:56:52 +0000120 int32_t id = gGlobal->getNextTimerID();
121
122 gGlobal->fTimeouts[id].Reset(gGlobal->fIsolate, timeoutFn);
commit-bot@chromium.org0fc0dea2013-12-18 04:45:37 +0000123
124 // Create an SkEvent and add it with the right delay.
commit-bot@chromium.org872796b2013-12-20 15:56:52 +0000125 SkEvent* evt = new SkEvent();
126 evt->setTargetProc(Global::TimeOutProc);
127 evt->setFast32(id);
128 evt->postDelay(delay);
commit-bot@chromium.org0fc0dea2013-12-18 04:45:37 +0000129
jcgregorioe22f45f2014-10-24 12:49:17 -0700130 args.GetReturnValue().Set(v8::Integer::New(gGlobal->fIsolate, id));
commit-bot@chromium.org0fc0dea2013-12-18 04:45:37 +0000131}
132
133// Callback function for SkEvents used to implement timeouts.
134bool Global::TimeOutProc(const SkEvent& evt) {
135 // Create a handle scope to keep the temporary object references.
jcgregorioe22f45f2014-10-24 12:49:17 -0700136 v8::HandleScope handleScope(gGlobal->getIsolate());
commit-bot@chromium.org0fc0dea2013-12-18 04:45:37 +0000137
138 // Create a local context from our global context.
jcgregorioe22f45f2014-10-24 12:49:17 -0700139 v8::Local<v8::Context> context = gGlobal->getContext();
commit-bot@chromium.org0fc0dea2013-12-18 04:45:37 +0000140
141 // Enter the context so all the remaining operations take place there.
jcgregorioe22f45f2014-10-24 12:49:17 -0700142 v8::Context::Scope contextScope(context);
commit-bot@chromium.org0fc0dea2013-12-18 04:45:37 +0000143
144 // Set up an exception handler before calling the Process function.
jcgregorioe22f45f2014-10-24 12:49:17 -0700145 v8::TryCatch tryCatch;
commit-bot@chromium.org0fc0dea2013-12-18 04:45:37 +0000146
commit-bot@chromium.org872796b2013-12-20 15:56:52 +0000147 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
commit-bot@chromium.org0fc0dea2013-12-18 04:45:37 +0000153 const int argc = 0;
jcgregorioe22f45f2014-10-24 12:49:17 -0700154 v8::Local<v8::Function> onTimeout =
155 v8::Local<v8::Function>::New(gGlobal->getIsolate(), gGlobal->fTimeouts[id]);
156 v8::Handle<v8::Value> result = onTimeout->Call(context->Global(), argc, NULL);
commit-bot@chromium.org872796b2013-12-20 15:56:52 +0000157 gGlobal->fTimeouts.erase(id);
commit-bot@chromium.org0fc0dea2013-12-18 04:45:37 +0000158
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.
jcgregorioe22f45f2014-10-24 12:49:17 -0700169 v8::String::Utf8Value str(result);
commit-bot@chromium.org0fc0dea2013-12-18 04:45:37 +0000170 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.
jcgregorioe22f45f2014-10-24 12:49:17 -0700178v8::Handle<v8::Context> Global::createRootContext() {
commit-bot@chromium.org0fc0dea2013-12-18 04:45:37 +0000179 // Create a template for the global object.
jcgregorioe22f45f2014-10-24 12:49:17 -0700180 v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
commit-bot@chromium.org0fc0dea2013-12-18 04:45:37 +0000181
182 global->Set(v8::String::NewFromUtf8(fIsolate, "print"),
commit-bot@chromium.orgb1639842014-01-08 15:14:09 +0000183 v8::FunctionTemplate::New(fIsolate, Global::Print));
commit-bot@chromium.org0fc0dea2013-12-18 04:45:37 +0000184 global->Set(v8::String::NewFromUtf8(fIsolate, "setTimeout"),
commit-bot@chromium.orgb1639842014-01-08 15:14:09 +0000185 v8::FunctionTemplate::New(fIsolate, Global::SetTimeout));
commit-bot@chromium.org0fc0dea2013-12-18 04:45:37 +0000186 global->Set(v8::String::NewFromUtf8(fIsolate, "inval"),
commit-bot@chromium.orgb1639842014-01-08 15:14:09 +0000187 v8::FunctionTemplate::New(fIsolate, Global::Inval));
commit-bot@chromium.org0fc0dea2013-12-18 04:45:37 +0000188
189
jcgregorioe22f45f2014-10-24 12:49:17 -0700190 return v8::Context::New(fIsolate, NULL, global);
commit-bot@chromium.org0fc0dea2013-12-18 04:45:37 +0000191}
192
commit-bot@chromium.orgc8d73282014-01-06 18:17:24 +0000193void Global::initialize() {
194 // Create a stack-allocated handle scope.
jcgregorioe22f45f2014-10-24 12:49:17 -0700195 v8::HandleScope handleScope(fIsolate);
commit-bot@chromium.orgc8d73282014-01-06 18:17:24 +0000196
197 // Create a new context.
jcgregorioe22f45f2014-10-24 12:49:17 -0700198 v8::Handle<v8::Context> context = this->createRootContext();
commit-bot@chromium.orgc8d73282014-01-06 18:17:24 +0000199
200 // Make the context persistent.
201 fContext.Reset(fIsolate, context);
202}
203
commit-bot@chromium.org0fc0dea2013-12-18 04:45:37 +0000204
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.
210bool Global::parseScript(const char script[]) {
211
212 // Create a stack-allocated handle scope.
jcgregorioe22f45f2014-10-24 12:49:17 -0700213 v8::HandleScope handleScope(fIsolate);
commit-bot@chromium.org0fc0dea2013-12-18 04:45:37 +0000214
commit-bot@chromium.orgc8d73282014-01-06 18:17:24 +0000215 // Get the global context.
jcgregorioe22f45f2014-10-24 12:49:17 -0700216 v8::Handle<v8::Context> context = this->getContext();
commit-bot@chromium.org0fc0dea2013-12-18 04:45:37 +0000217
218 // Enter the scope so all operations take place in the scope.
jcgregorioe22f45f2014-10-24 12:49:17 -0700219 v8::Context::Scope contextScope(context);
commit-bot@chromium.org0fc0dea2013-12-18 04:45:37 +0000220
221 v8::TryCatch tryCatch;
222
223 // Compile the source code.
jcgregorioe22f45f2014-10-24 12:49:17 -0700224 v8::Handle<v8::String> source = v8::String::NewFromUtf8(fIsolate, script);
225 v8::Handle<v8::Script> compiledScript = v8::Script::Compile(source);
commit-bot@chromium.org0fc0dea2013-12-18 04:45:37 +0000226
227 if (compiledScript.IsEmpty()) {
228 // Print errors that happened during compilation.
229 this->reportException(&tryCatch);
230 return false;
231 }
commit-bot@chromium.org0fc0dea2013-12-18 04:45:37 +0000232
233 // Try running it now to create the onDraw function.
jcgregorioe22f45f2014-10-24 12:49:17 -0700234 v8::Handle<v8::Value> result = compiledScript->Run();
commit-bot@chromium.org0fc0dea2013-12-18 04:45:37 +0000235
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;
commit-bot@chromium.org0fc0dea2013-12-18 04:45:37 +0000242 }
243
commit-bot@chromium.org0fc0dea2013-12-18 04:45:37 +0000244 return true;
245}