1 | #include "InterpreterV8.h"
|
---|
2 |
|
---|
3 | InterpreterV8 *InterpreterV8::This = 0;
|
---|
4 |
|
---|
5 | #ifdef HAVE_V8
|
---|
6 |
|
---|
7 | #include <v8.h>
|
---|
8 | #include <fstream>
|
---|
9 | #include <sstream>
|
---|
10 | #include <iomanip>
|
---|
11 |
|
---|
12 | using namespace std;
|
---|
13 | using namespace v8;
|
---|
14 |
|
---|
15 | bool InterpreterV8::ReportException(TryCatch* try_catch)
|
---|
16 | {
|
---|
17 | const HandleScope handle_scope;
|
---|
18 |
|
---|
19 | const String::Utf8Value exception(try_catch->Exception());
|
---|
20 |
|
---|
21 | if (*exception && string(*exception)=="exit")
|
---|
22 | return true;
|
---|
23 |
|
---|
24 | const Handle<Message> message = try_catch->Message();
|
---|
25 |
|
---|
26 | // Print (filename):(line number): (message).
|
---|
27 | const String::Utf8Value filename(message->GetScriptResourceName());
|
---|
28 |
|
---|
29 | ostringstream out;
|
---|
30 |
|
---|
31 | if (*filename)
|
---|
32 | out << *filename;
|
---|
33 | if (!message.IsEmpty())
|
---|
34 | out << ": l." << message->GetLineNumber();
|
---|
35 | if (*exception)
|
---|
36 | out << ": " << *exception;
|
---|
37 |
|
---|
38 | JsException(out.str());
|
---|
39 |
|
---|
40 | if (message.IsEmpty())
|
---|
41 | return false;
|
---|
42 |
|
---|
43 | // Print line of source code.
|
---|
44 | const String::Utf8Value sourceline(message->GetSourceLine());
|
---|
45 | if (*sourceline)
|
---|
46 | JsException(*sourceline);
|
---|
47 |
|
---|
48 | // Print wavy underline (GetUnderline is deprecated).
|
---|
49 | const int start = message->GetStartColumn();
|
---|
50 | const int end = message->GetEndColumn();
|
---|
51 |
|
---|
52 | out.str("");
|
---|
53 | if (start>0)
|
---|
54 | out << setfill(' ') << setw(start) << ' ';
|
---|
55 | out << setfill('^') << setw(end-start) << '^';
|
---|
56 |
|
---|
57 | JsException(out.str());
|
---|
58 |
|
---|
59 | String::Utf8Value stack_trace(try_catch->StackTrace());
|
---|
60 | if (stack_trace.length()<=0)
|
---|
61 | return false;
|
---|
62 |
|
---|
63 | //if (*stack_trace)
|
---|
64 | // JsException(string("\n")+*stack_trace);
|
---|
65 |
|
---|
66 | return false;
|
---|
67 | }
|
---|
68 |
|
---|
69 | // Executes a string within the current v8 context.
|
---|
70 | bool InterpreterV8::ExecuteStringNT(const Handle<String> &code, const Handle<Value> &file)
|
---|
71 | {
|
---|
72 | if (code.IsEmpty())
|
---|
73 | return true;
|
---|
74 |
|
---|
75 | const HandleScope handle_scope;
|
---|
76 |
|
---|
77 | const Handle<Script> script = Script::Compile(code, file);
|
---|
78 | if (script.IsEmpty())
|
---|
79 | return false;
|
---|
80 |
|
---|
81 | const Handle<Value> result = script->Run();
|
---|
82 | if (result.IsEmpty())
|
---|
83 | return false;
|
---|
84 |
|
---|
85 | // If all went well and the result wasn't undefined then print
|
---|
86 | // the returned value.
|
---|
87 | if (!result->IsUndefined())
|
---|
88 | JsResult(*String::Utf8Value(result));
|
---|
89 |
|
---|
90 | return true;
|
---|
91 | }
|
---|
92 |
|
---|
93 | bool InterpreterV8::ExecuteCode(const Handle<String> &code, const Handle<Value> &file)
|
---|
94 | {
|
---|
95 | TryCatch exception;
|
---|
96 |
|
---|
97 | const bool rc = ExecuteStringNT(code, file);
|
---|
98 |
|
---|
99 | if (!exception.CanContinue())
|
---|
100 | return false;
|
---|
101 |
|
---|
102 | if (exception.HasCaught())
|
---|
103 | return ReportException(&exception);
|
---|
104 |
|
---|
105 | return rc;
|
---|
106 | }
|
---|
107 |
|
---|
108 | bool InterpreterV8::ExecuteCode(const string &code, const string &file)
|
---|
109 | {
|
---|
110 | return ExecuteCode(String::New(code.c_str(), code.size()),
|
---|
111 | String::New(file.c_str()));
|
---|
112 | }
|
---|
113 |
|
---|
114 | bool InterpreterV8::ExecuteFile(const string &name)
|
---|
115 | {
|
---|
116 | ifstream fin(name.c_str());
|
---|
117 | if (!fin)
|
---|
118 | {
|
---|
119 | JsException("Error - Could not open file '"+name+"'");
|
---|
120 | return false;
|
---|
121 | }
|
---|
122 |
|
---|
123 | string buffer;
|
---|
124 | if (!getline(fin, buffer, '\0'))
|
---|
125 | return true;
|
---|
126 |
|
---|
127 | if (fin.fail())
|
---|
128 | {
|
---|
129 | JsException("Error - reading file.");
|
---|
130 | return false;
|
---|
131 | }
|
---|
132 |
|
---|
133 | return ExecuteCode(buffer, name);
|
---|
134 | }
|
---|
135 |
|
---|
136 | Handle<Value> InterpreterV8::FuncWait(const Arguments& args)
|
---|
137 | {
|
---|
138 | if (args.Length()!=2 && args.Length()!=3)
|
---|
139 | return ThrowException(String::New("Number of arguments must be 2 or 3."));
|
---|
140 |
|
---|
141 | if (!args[0]->IsString())
|
---|
142 | return ThrowException(String::New("Argument 1 not a string."));
|
---|
143 |
|
---|
144 | if (!args[1]->IsInt32())
|
---|
145 | return ThrowException(String::New("Argument 2 not an int32."));
|
---|
146 |
|
---|
147 | if (args.Length()==3 && !args[2]->IsUint32())
|
---|
148 | return ThrowException(String::New("Argument 3 not an uint32."));
|
---|
149 |
|
---|
150 | const string server = *String::Utf8Value(args[0]);
|
---|
151 | const int32_t state = args[1]->Int32Value();
|
---|
152 | const uint32_t millisec = args.Length()==3 ? args[2]->Int32Value() : 0;
|
---|
153 |
|
---|
154 | const int rc = JsWait(server, state, millisec);
|
---|
155 |
|
---|
156 | if (rc==0 || rc==1)
|
---|
157 | return Boolean::New(rc);
|
---|
158 |
|
---|
159 | return ThrowException(String::New(("Waitig for state "+to_string(state)+" of server '"+server+"' failed.").c_str()));
|
---|
160 | }
|
---|
161 |
|
---|
162 | Handle<Value> InterpreterV8::FuncSend(const Arguments& args)
|
---|
163 | {
|
---|
164 | if (args.Length()==0)
|
---|
165 | return ThrowException(String::New("Number of arguments must be at least 1."));
|
---|
166 |
|
---|
167 | if (!args[0]->IsString())
|
---|
168 | return ThrowException(String::New("Argument 1 must be a string."));
|
---|
169 |
|
---|
170 | const HandleScope handle_scope;
|
---|
171 |
|
---|
172 | const String::Utf8Value str(args[0]);
|
---|
173 |
|
---|
174 | string command = *str;
|
---|
175 |
|
---|
176 | for (int i=1; i<args.Length(); i++)
|
---|
177 | {
|
---|
178 | const String::Utf8Value arg(args[i]);
|
---|
179 | command += " \""+string(*arg)+"\"";
|
---|
180 | }
|
---|
181 |
|
---|
182 | return Boolean::New(JsSend(command));
|
---|
183 | }
|
---|
184 |
|
---|
185 | Handle<Value> InterpreterV8::FuncSleep(const Arguments& args)
|
---|
186 | {
|
---|
187 | if (args.Length()!=1)
|
---|
188 | return ThrowException(String::New("Number of arguments must be exactly 1."));
|
---|
189 |
|
---|
190 | if (!args[0]->IsUint32())
|
---|
191 | return ThrowException(String::New("Argument 1 must be an uint32."));
|
---|
192 |
|
---|
193 | JsSleep(args[0]->Int32Value());
|
---|
194 |
|
---|
195 | return Undefined();
|
---|
196 | }
|
---|
197 |
|
---|
198 | Handle<Value> InterpreterV8::FuncState(const Arguments& args)
|
---|
199 | {
|
---|
200 | if (args.Length()!=1)
|
---|
201 | return ThrowException(String::New("Number of arguments must be exactly 1."));
|
---|
202 |
|
---|
203 | if (!args[0]->IsString())
|
---|
204 | return ThrowException(String::New("Argument 1 must be a string."));
|
---|
205 |
|
---|
206 | // Return state.name/state.index
|
---|
207 |
|
---|
208 | HandleScope handle_scope;
|
---|
209 |
|
---|
210 | const String::Utf8Value str(args[0]);
|
---|
211 |
|
---|
212 | const pair<int32_t, string> rc = JsState(*str);
|
---|
213 |
|
---|
214 | Handle<ObjectTemplate> obj = ObjectTemplate::New();
|
---|
215 | obj->Set(String::New("index"), rc.first <=-256?Undefined():Integer::New(rc.first), ReadOnly);
|
---|
216 | obj->Set(String::New("name"), rc.first <=-256?Undefined():String::New (rc.second.c_str()), ReadOnly);
|
---|
217 |
|
---|
218 | return handle_scope.Close(obj->NewInstance());
|
---|
219 | }
|
---|
220 |
|
---|
221 | Handle<Value> InterpreterV8::FuncExit(const Arguments &)
|
---|
222 | {
|
---|
223 | v8::V8::TerminateExecution(fThreadId);
|
---|
224 | return ThrowException(String::New("exit"));
|
---|
225 | /*
|
---|
226 | if (args.Length()!=1)
|
---|
227 | return ThrowException(String::New("Number of arguments must be exactly 1."));
|
---|
228 |
|
---|
229 | if (!args[0]->IsUint32())
|
---|
230 | return ThrowException(String::New("Argument 1 must be an uint32."));
|
---|
231 |
|
---|
232 | const HandleScope handle_scope;
|
---|
233 |
|
---|
234 | JsSleep(args[0]->Int32Value());
|
---|
235 | */
|
---|
236 | return Undefined();
|
---|
237 | }
|
---|
238 |
|
---|
239 | // The callback that is invoked by v8 whenever the JavaScript 'print'
|
---|
240 | // function is called. Prints its arguments on stdout separated by
|
---|
241 | // spaces and ending with a newline.
|
---|
242 | Handle<Value> InterpreterV8::FuncPrint(const Arguments& args)
|
---|
243 | {
|
---|
244 | for (int i=0; i<args.Length(); i++)
|
---|
245 | {
|
---|
246 | const HandleScope handle_scope;
|
---|
247 |
|
---|
248 | const String::Utf8Value str(args[i]);
|
---|
249 | if (*str)
|
---|
250 | JsPrint(*str);
|
---|
251 | }
|
---|
252 | return Undefined();
|
---|
253 | }
|
---|
254 |
|
---|
255 | // The callback that is invoked by v8 whenever the JavaScript 'load'
|
---|
256 | // function is called. Loads, compiles and executes its argument
|
---|
257 | // JavaScript file.
|
---|
258 | Handle<Value> InterpreterV8::FuncInclude(const Arguments& args)
|
---|
259 | {
|
---|
260 | for (int i = 0; i<args.Length(); i++)
|
---|
261 | {
|
---|
262 | const HandleScope handle_scope;
|
---|
263 |
|
---|
264 | const String::Utf8Value file(args[i]);
|
---|
265 | if (*file == NULL)
|
---|
266 | return ThrowException(String::New(("Error loading file '"+string(*file)+"'").c_str()));
|
---|
267 |
|
---|
268 | if (!ExecuteFile(*file))
|
---|
269 | return ThrowException(String::New(("Execution of '"+string(*file)+"' failed").c_str()));
|
---|
270 | }
|
---|
271 | return Undefined();
|
---|
272 | }
|
---|
273 |
|
---|
274 | Handle<Value> InterpreterV8::FuncVersion(const Arguments&)
|
---|
275 | {
|
---|
276 | return String::New(V8::GetVersion());
|
---|
277 | }
|
---|
278 |
|
---|
279 | bool InterpreterV8::JsRun(const string &filename, const map<string, string> &map)
|
---|
280 | {
|
---|
281 | v8::Locker locker;
|
---|
282 | fThreadId = V8::GetCurrentThreadId();
|
---|
283 |
|
---|
284 | JsLoad(filename);
|
---|
285 |
|
---|
286 | HandleScope handle_scope;
|
---|
287 |
|
---|
288 | // Create a template for the global object.
|
---|
289 | Handle<ObjectTemplate> dim = ObjectTemplate::New();
|
---|
290 | dim->Set(String::New("print"), FunctionTemplate::New(WrapPrint), ReadOnly);
|
---|
291 | dim->Set(String::New("wait"), FunctionTemplate::New(WrapWait), ReadOnly);
|
---|
292 | dim->Set(String::New("send"), FunctionTemplate::New(WrapSend), ReadOnly);
|
---|
293 | dim->Set(String::New("state"), FunctionTemplate::New(WrapState), ReadOnly);
|
---|
294 | dim->Set(String::New("sleep"), FunctionTemplate::New(WrapSleep), ReadOnly);
|
---|
295 |
|
---|
296 | Handle<ObjectTemplate> global = ObjectTemplate::New();
|
---|
297 | global->Set(String::New("dim"), dim, ReadOnly);
|
---|
298 | global->Set(String::New("include"), FunctionTemplate::New(WrapInclude), ReadOnly);
|
---|
299 | global->Set(String::New("exit"), FunctionTemplate::New(WrapExit), ReadOnly);
|
---|
300 | global->Set(String::New("version"), FunctionTemplate::New(InterpreterV8::FuncVersion), ReadOnly);
|
---|
301 |
|
---|
302 | // Persistent
|
---|
303 | Handle<Context> context = Context::New(NULL, global);
|
---|
304 | if (context.IsEmpty())
|
---|
305 | {
|
---|
306 | //printf("Error creating context\n");
|
---|
307 | return false;
|
---|
308 | }
|
---|
309 |
|
---|
310 | v8::Context::Scope scope(context);
|
---|
311 |
|
---|
312 | Local<Array> args = Array::New(map.size());
|
---|
313 | for (auto it=map.begin(); it!=map.end(); it++)
|
---|
314 | args->Set(String::New(it->first.c_str()), String::New(it->second.c_str()));
|
---|
315 | context->Global()->Set(String::New("$"), args, ReadOnly);
|
---|
316 | context->Global()->Set(String::New("arg"), args, ReadOnly);
|
---|
317 |
|
---|
318 | JsStart(filename);
|
---|
319 |
|
---|
320 | //context->Enter();
|
---|
321 | v8::Locker::StartPreemption(10);
|
---|
322 | const bool rc = ExecuteFile(filename);
|
---|
323 | v8::Locker::StopPreemption();
|
---|
324 | //context->Exit();
|
---|
325 |
|
---|
326 | //context.Dispose();
|
---|
327 |
|
---|
328 | JsEnd(filename);
|
---|
329 |
|
---|
330 | return rc;
|
---|
331 | }
|
---|
332 |
|
---|
333 | void InterpreterV8::JsStop()
|
---|
334 | {
|
---|
335 | v8::Locker locker;
|
---|
336 | //cout << "Terminate " << fThreadId << endl;
|
---|
337 | if (This->fThreadId>=0)
|
---|
338 | v8::V8::TerminateExecution(This->fThreadId);
|
---|
339 | //cout << "Terminate " << fThreadId << endl;
|
---|
340 | v8::Unlocker unlocker;
|
---|
341 | }
|
---|
342 |
|
---|
343 | #endif
|
---|
344 |
|
---|