source: trunk/FACT++/src/InterpreterV8.cc@ 14098

Last change on this file since 14098 was 14093, checked in by tbretz, 12 years ago
Implemented and/or fixed the support for service subscriptions.
File size: 14.9 KB
Line 
1#include "InterpreterV8.h"
2
3#include <boost/tokenizer.hpp>
4
5InterpreterV8 *InterpreterV8::This = 0;
6
7#ifdef HAVE_V8
8
9#include <v8.h>
10#include <fstream>
11#include <sstream>
12#include <iomanip>
13
14using namespace std;
15using namespace v8;
16
17bool InterpreterV8::ReportException(TryCatch* try_catch)
18{
19 const HandleScope handle_scope;
20
21 const String::Utf8Value exception(try_catch->Exception());
22
23 if (*exception && string(*exception)=="exit")
24 return true;
25
26 const Handle<Message> message = try_catch->Message();
27
28 // Print (filename):(line number): (message).
29 const String::Utf8Value filename(message->GetScriptResourceName());
30
31 ostringstream out;
32
33 if (*filename)
34 out << *filename;
35 if (!message.IsEmpty())
36 out << ": l." << message->GetLineNumber();
37 if (*exception)
38 out << ": " << *exception;
39
40 JsException(out.str());
41
42 if (message.IsEmpty())
43 return false;
44
45 // Print line of source code.
46 const String::Utf8Value sourceline(message->GetSourceLine());
47 if (*sourceline)
48 JsException(*sourceline);
49
50 // Print wavy underline (GetUnderline is deprecated).
51 const int start = message->GetStartColumn();
52 const int end = message->GetEndColumn();
53
54 out.str("");
55 if (start>0)
56 out << setfill(' ') << setw(start) << ' ';
57 out << setfill('^') << setw(end-start) << '^';
58
59 JsException(out.str());
60
61 String::Utf8Value stack_trace(try_catch->StackTrace());
62 if (stack_trace.length()<=0)
63 return false;
64
65 //if (*stack_trace)
66 // JsException(string("\n")+*stack_trace);
67
68 return false;
69}
70
71// Executes a string within the current v8 context.
72bool InterpreterV8::ExecuteStringNT(const Handle<String> &code, const Handle<Value> &file)
73{
74 if (code.IsEmpty())
75 return true;
76
77 const HandleScope handle_scope;
78
79 const Handle<Script> script = Script::Compile(code, file);
80 if (script.IsEmpty())
81 return false;
82
83 const Handle<Value> result = script->Run();
84 if (result.IsEmpty())
85 return false;
86
87 // If all went well and the result wasn't undefined then print
88 // the returned value.
89 if (!result->IsUndefined())
90 JsResult(*String::Utf8Value(result));
91
92 return true;
93}
94
95bool InterpreterV8::ExecuteCode(const Handle<String> &code, const Handle<Value> &file)
96{
97 TryCatch exception;
98
99 const bool rc = ExecuteStringNT(code, file);
100
101 if (!exception.CanContinue())
102 return false;
103
104 if (exception.HasCaught())
105 return ReportException(&exception);
106
107 return rc;
108}
109
110bool InterpreterV8::ExecuteCode(const string &code, const string &file)
111{
112 return ExecuteCode(String::New(code.c_str(), code.size()),
113 String::New(file.c_str()));
114}
115
116bool InterpreterV8::ExecuteFile(const string &name)
117{
118 ifstream fin(name.c_str());
119 if (!fin)
120 {
121 JsException("Error - Could not open file '"+name+"'");
122 return false;
123 }
124
125 string buffer;
126 if (!getline(fin, buffer, '\0'))
127 return true;
128
129 if (fin.fail())
130 {
131 JsException("Error - reading file.");
132 return false;
133 }
134
135 return ExecuteCode(buffer, name);
136}
137
138Handle<Value> InterpreterV8::FuncWait(const Arguments& args)
139{
140 if (args.Length()!=2 && args.Length()!=3)
141 return ThrowException(String::New("Number of arguments must be 2 or 3."));
142
143 if (!args[0]->IsString())
144 return ThrowException(String::New("Argument 1 not a string."));
145
146 if (!args[1]->IsInt32())
147 return ThrowException(String::New("Argument 2 not an int32."));
148
149 if (args.Length()==3 && !args[2]->IsUint32())
150 return ThrowException(String::New("Argument 3 not an uint32."));
151
152 const string server = *String::Utf8Value(args[0]);
153 const int32_t state = args[1]->Int32Value();
154 const uint32_t millisec = args.Length()==3 ? args[2]->Int32Value() : 0;
155
156 const int rc = JsWait(server, state, millisec);
157
158 if (rc==0 || rc==1)
159 return Boolean::New(rc);
160
161 return ThrowException(String::New(("Waitig for state "+to_string(state)+" of server '"+server+"' failed.").c_str()));
162}
163
164Handle<Value> InterpreterV8::FuncSend(const Arguments& args)
165{
166 if (args.Length()==0)
167 return ThrowException(String::New("Number of arguments must be at least 1."));
168
169 if (!args[0]->IsString())
170 return ThrowException(String::New("Argument 1 must be a string."));
171
172 const HandleScope handle_scope;
173
174 const String::Utf8Value str(args[0]);
175
176 string command = *str;
177
178 for (int i=1; i<args.Length(); i++)
179 {
180 const String::Utf8Value arg(args[i]);
181 command += " \""+string(*arg)+"\"";
182 }
183
184 return Boolean::New(JsSend(command));
185}
186
187Handle<Value> InterpreterV8::FuncSleep(const Arguments& args)
188{
189 if (args.Length()!=1)
190 return ThrowException(String::New("Number of arguments must be exactly 1."));
191
192 if (!args[0]->IsUint32())
193 return ThrowException(String::New("Argument 1 must be an uint32."));
194
195 JsSleep(args[0]->Int32Value());
196
197 return Undefined();
198}
199
200Handle<Value> InterpreterV8::FuncState(const Arguments& args)
201{
202 if (args.Length()!=1)
203 return ThrowException(String::New("Number of arguments must be exactly 1."));
204
205 if (!args[0]->IsString())
206 return ThrowException(String::New("Argument 1 must be a string."));
207
208 // Return state.name/state.index
209
210 HandleScope handle_scope;
211
212 const String::Utf8Value str(args[0]);
213
214 const pair<int32_t, string> rc = JsState(*str);
215
216 Handle<ObjectTemplate> obj = ObjectTemplate::New();
217
218
219 obj->Set(String::New("index"), rc.first<=-256?Undefined():Integer::New(rc.first), ReadOnly);
220 obj->Set(String::New("name"), rc.first<=-256?Undefined():String::New(rc.second.c_str()), ReadOnly);
221 //obj->Set(String::New("toString"), String::New(("[Object state "+string(*str)+"]").c_str()), ReadOnly);
222
223 return handle_scope.Close(obj->NewInstance());
224}
225
226Handle<Value> InterpreterV8::FuncExit(const Arguments &)
227{
228 v8::V8::TerminateExecution(fThreadId);
229 return ThrowException(String::New("exit"));
230/*
231 if (args.Length()!=1)
232 return ThrowException(String::New("Number of arguments must be exactly 1."));
233
234 if (!args[0]->IsUint32())
235 return ThrowException(String::New("Argument 1 must be an uint32."));
236
237 const HandleScope handle_scope;
238
239 JsSleep(args[0]->Int32Value());
240*/
241 return Undefined();
242}
243
244// The callback that is invoked by v8 whenever the JavaScript 'print'
245// function is called. Prints its arguments on stdout separated by
246// spaces and ending with a newline.
247Handle<Value> InterpreterV8::FuncPrint(const Arguments& args)
248{
249 for (int i=0; i<args.Length(); i++)
250 {
251 const HandleScope handle_scope;
252
253 const String::Utf8Value str(args[i]);
254 if (*str)
255 JsPrint(*str);
256 }
257 return Undefined();
258}
259
260// The callback that is invoked by v8 whenever the JavaScript 'load'
261// function is called. Loads, compiles and executes its argument
262// JavaScript file.
263Handle<Value> InterpreterV8::FuncInclude(const Arguments& args)
264{
265 for (int i = 0; i<args.Length(); i++)
266 {
267 const HandleScope handle_scope;
268
269 const String::Utf8Value file(args[i]);
270 if (*file == NULL)
271 return ThrowException(String::New(("Error loading file '"+string(*file)+"'").c_str()));
272
273 if (!ExecuteFile(*file))
274 return ThrowException(String::New(("Execution of '"+string(*file)+"' failed").c_str()));
275 }
276 return Undefined();
277}
278
279Handle<Value> InterpreterV8::FuncVersion(const Arguments&)
280{
281 return String::New(V8::GetVersion());
282}
283
284Handle<Value> Convert(char type, const char* &ptr)
285{
286 switch (type)
287 {
288 case 'F': { Handle<Value> v=Number::New(*reinterpret_cast<const float*>(ptr)); ptr+=4; return v; }
289 case 'D': { Handle<Value> v=Number::New(*reinterpret_cast<const double*>(ptr)); ptr+=8; return v; }
290 case 'I': {
291 case 'L': Handle<Value> v=Integer::New(*reinterpret_cast<const uint32_t*>(ptr)); ptr += 4; return v; }
292 case 'X': { Handle<Value> v=Integer::New(*reinterpret_cast<const uint64_t*>(ptr)); ptr += 8; return v; }
293 case 'S': { Handle<Value> v=Integer::New(*reinterpret_cast<const uint16_t*>(ptr)); ptr += 2; return v; }
294 case 'C': { Handle<Value> v=Integer::New((uint16_t)*reinterpret_cast<const uint8_t*>(ptr)); ptr += 1; return v; }
295 case ':': { Handle<Value> v=String::New(ptr); return v; }
296 }
297 return Undefined();
298}
299
300
301Handle<Value> InterpreterV8::FuncClose(const Arguments &args)
302{
303 HandleScope handle_scope;
304
305 const String::Utf8Value str(args.Holder()->Get(String::New("name")));
306 return Boolean::New(JsUnsubscribe(*str));
307}
308
309Handle<Value> InterpreterV8::FuncGetData(const Arguments &args)
310{
311 HandleScope handle_scope;
312
313 const String::Utf8Value str(args.Holder()->Get(String::New("name")));
314
315 const pair<uint64_t, EventImp *> p = JsGetEvent(*str);
316
317 const EventImp *evt = p.second;
318 if (!evt)
319 return Undefined();
320
321 //if (counter==cnt)
322 // return info.Holder();//Holder()->Get(String::New("data"));
323 //cout << counter << " " << cnt << endl;
324
325 //const void *ppp = Local<External>::Cast(info.This()->GetInternalField(0))->Value();
326 //info.This()->SetInternalField(0, External::New((void*)evt));
327 //cout << info.This().
328
329 const vector<Description> vec = JsDescription(*str);
330
331 Handle<Array> ret = Array::New();
332 ret->Set(String::New("format"), String::New(evt->GetFormat().c_str()), ReadOnly);
333 ret->Set(String::New("named"), Boolean::New(vec.size()>0), ReadOnly);
334 ret->Set(String::New("counter"), Integer::New(p.first), ReadOnly);
335 ret->Set(String::New("time"), Date::New(evt->GetJavaDate()), ReadOnly);
336
337 typedef boost::char_separator<char> separator;
338 const boost::tokenizer<separator> tokenizer(evt->GetFormat(), separator(";:"));
339
340 const vector<string> tok(tokenizer.begin(), tokenizer.end());
341
342 Handle<Array> obj = tok.size()==1 ? ret : Array::New();
343
344 const char *ptr = evt->GetText();
345 try
346 {
347 size_t pos = 1;
348 for (auto it=tok.begin(); it!=tok.end(); it++, pos++)
349 {
350 char type = (*it)[0];
351 it++;
352
353 if (it==tok.end() && type=='C')
354 type = ':';
355
356 if (it==tok.end() && type!=':')
357 return ThrowException(String::New(("Format string invalid '"+evt->GetFormat()+"'").c_str()));
358
359 string name = pos<vec.size() ? vec[pos].name : "";
360 if (tok.size()==1)
361 name = "data";
362
363 const uint32_t cnt = it==tok.end() ? 1 : stoi(it->c_str());
364
365 if (cnt==1)
366 {
367 const Handle<Value> v = Convert(type, ptr);
368 if (tok.size()>1)
369 obj->Set(pos-1, v);
370 if (!name.empty())
371 obj->Set(String::New(name.c_str()), v);
372 }
373 else
374 {
375 Handle<Array> a = Array::New(cnt);
376 for (uint32_t i=0; i<cnt; i++)
377 a->Set(i, Convert(type, ptr));
378 if (tok.size()>1)
379 obj->Set(pos-1, a);
380 if (!name.empty())
381 obj->Set(String::New(name.c_str()), a);
382 }
383
384 if (it==tok.end())
385 break;
386 }
387
388 if (tok.size()>1)
389 ret->Set(String::New("data"), obj, ReadOnly);
390
391 return handle_scope.Close(ret);
392 }
393 catch (...)
394 {
395 return ThrowException(String::New(("Format string conversion '"+evt->GetFormat()+"' failed.").c_str()));
396 }
397}
398
399/*
400void Cleanup( Persistent<Value> object, void *parameter )
401{
402 cout << "======================> RemoveMyObj()" << endl;
403}*/
404
405Handle<Value> InterpreterV8::FuncOpen(const Arguments &args)
406{
407 if (args.Length()!=1)
408 return ThrowException(String::New("Number of arguments must be exactly 1."));
409
410 if (!args[0]->IsString())
411 return ThrowException(String::New("Argument 1 must be a string."));
412
413 //if (!args.IsConstructCall())
414 // return ThrowException(String::New("Must be used as constructor."));
415
416 HandleScope handle_scope;
417
418 const String::Utf8Value str(args[0]);
419
420 JsSubscribe(*str);
421
422 //args.This()->SetInternalField(0, External::New((void*)test));
423 //obj->SetInternalFieldCount(1);
424
425 Handle<ObjectTemplate> obj = ObjectTemplate::New();
426 obj->Set(String::New("get"), FunctionTemplate::New(WrapGetData), ReadOnly);
427 obj->Set(String::New("close"), FunctionTemplate::New(WrapClose), ReadOnly);
428 obj->Set(String::New("name"), String::New(*str), ReadOnly);
429 //obj->Set(String::New("toString"), String::New(("[object Dim "+string(*str)+"]").c_str()), ReadOnly);
430
431 return obj->NewInstance();
432
433 // Persistent<Object> p = Persistent<Object>::New(obj->NewInstance());
434 // obj.MakeWeak((void*)1, Cleanup);
435 // return obj;
436}
437
438bool InterpreterV8::JsRun(const string &filename, const map<string, string> &map)
439{
440 v8::Locker locker;
441 fThreadId = V8::GetCurrentThreadId();
442
443 JsLoad(filename);
444
445 HandleScope handle_scope;
446
447 // Create a template for the global object.
448 Handle<ObjectTemplate> dim = ObjectTemplate::New();
449 dim->Set(String::New("print"), FunctionTemplate::New(WrapPrint), ReadOnly);
450 dim->Set(String::New("wait"), FunctionTemplate::New(WrapWait), ReadOnly);
451 dim->Set(String::New("send"), FunctionTemplate::New(WrapSend), ReadOnly);
452 dim->Set(String::New("state"), FunctionTemplate::New(WrapState), ReadOnly);
453 dim->Set(String::New("sleep"), FunctionTemplate::New(WrapSleep), ReadOnly);
454 dim->Set(String::New("open"), FunctionTemplate::New(WrapOpen), ReadOnly);
455
456 Handle<ObjectTemplate> global = ObjectTemplate::New();
457 global->Set(String::New("dim"), dim, ReadOnly);
458 global->Set(String::New("include"), FunctionTemplate::New(WrapInclude), ReadOnly);
459 global->Set(String::New("exit"), FunctionTemplate::New(WrapExit), ReadOnly);
460 global->Set(String::New("version"), FunctionTemplate::New(InterpreterV8::FuncVersion), ReadOnly);
461
462 // Persistent
463 Handle<Context> context = Context::New(NULL, global);
464 if (context.IsEmpty())
465 {
466 //printf("Error creating context\n");
467 return false;
468 }
469
470 v8::Context::Scope scope(context);
471
472 Local<Array> args = Array::New(map.size());
473 for (auto it=map.begin(); it!=map.end(); it++)
474 args->Set(String::New(it->first.c_str()), String::New(it->second.c_str()));
475 context->Global()->Set(String::New("$"), args, ReadOnly);
476 context->Global()->Set(String::New("arg"), args, ReadOnly);
477
478 JsStart(filename);
479
480 //context->Enter();
481 v8::Locker::StartPreemption(10);
482 const bool rc = ExecuteFile(filename);
483 v8::Locker::StopPreemption();
484 //context->Exit();
485
486 //context.Dispose();
487
488 cout << "JsEnd" << endl;
489 JsEnd(filename);
490
491 return rc;
492}
493
494void InterpreterV8::JsStop()
495{
496 v8::Locker locker;
497 //cout << "Terminate " << fThreadId << endl;
498 if (This->fThreadId>=0)
499 v8::V8::TerminateExecution(This->fThreadId);
500 //cout << "Terminate " << fThreadId << endl;
501 v8::Unlocker unlocker;
502}
503
504#endif
505
Note: See TracBrowser for help on using the repository browser.