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

Last change on this file since 14171 was 14135, checked in by tbretz, 13 years ago
Added a function dim.out which will only print to the local console; improved conversion from dim service elements to ECMA numbers
File size: 15.8 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 obj->Set(String::New("index"), rc.first<=-256?Undefined():Integer::New(rc.first), ReadOnly);
219 obj->Set(String::New("name"), rc.first<=-256?Undefined():String::New(rc.second.c_str()), ReadOnly);
220 //obj->Set(String::New("toString"), String::New(("[Object state "+string(*str)+"]").c_str()), ReadOnly);
221
222 return handle_scope.Close(obj->NewInstance());
223}
224
225Handle<Value> InterpreterV8::FuncExit(const Arguments &)
226{
227 v8::V8::TerminateExecution(fThreadId);
228 return ThrowException(String::New("exit"));
229/*
230 if (args.Length()!=1)
231 return ThrowException(String::New("Number of arguments must be exactly 1."));
232
233 if (!args[0]->IsUint32())
234 return ThrowException(String::New("Argument 1 must be an uint32."));
235
236 const HandleScope handle_scope;
237
238 JsSleep(args[0]->Int32Value());
239*/
240 return Undefined();
241}
242
243// The callback that is invoked by v8 whenever the JavaScript 'print'
244// function is called. Prints its arguments on stdout separated by
245// spaces and ending with a newline.
246Handle<Value> InterpreterV8::FuncPrint(const Arguments& args)
247{
248 for (int i=0; i<args.Length(); i++)
249 {
250 const HandleScope handle_scope;
251
252 const String::Utf8Value str(args[i]);
253 if (*str)
254 JsPrint(*str);
255 }
256 return Undefined();
257}
258
259Handle<Value> InterpreterV8::FuncOut(const Arguments& args)
260{
261 for (int i=0; i<args.Length(); i++)
262 {
263 const HandleScope handle_scope;
264
265 const String::Utf8Value str(args[i]);
266 if (*str)
267 JsOut(*str);
268 }
269 return Undefined();
270}
271
272// The callback that is invoked by v8 whenever the JavaScript 'load'
273// function is called. Loads, compiles and executes its argument
274// JavaScript file.
275Handle<Value> InterpreterV8::FuncInclude(const Arguments& args)
276{
277 for (int i = 0; i<args.Length(); i++)
278 {
279 const HandleScope handle_scope;
280
281 const String::Utf8Value file(args[i]);
282 if (*file == NULL)
283 return ThrowException(String::New(("Error loading file '"+string(*file)+"'").c_str()));
284
285 if (!ExecuteFile(*file))
286 return ThrowException(String::New(("Execution of '"+string(*file)+"' failed").c_str()));
287 }
288 return Undefined();
289}
290
291Handle<Value> InterpreterV8::FuncVersion(const Arguments&)
292{
293 return String::New(V8::GetVersion());
294}
295
296Handle<Value> Convert(char type, const char* &ptr)
297{
298 // Dim values are always unsigned per (FACT++) definition
299 switch (type)
300 {
301 case 'F':
302 {
303 // Remove the "imprecision" effect coming from casting a float to
304 // a double and then showing it with double precision
305 ostringstream val;
306 val << setprecision(7) << *reinterpret_cast<const float*>(ptr);
307 Handle<Value> v=Number::New(stod(val.str()));
308 ptr+=4;
309 return v;
310 }
311 case 'D': { Handle<Value> v=Number::New(*reinterpret_cast<const double*>(ptr)); ptr+=8; return v; }
312 case 'I':
313 case 'L': { Handle<Value> v=Integer::NewFromUnsigned(*reinterpret_cast<const uint32_t*>(ptr)); ptr += 4; return v; }
314 case 'X':
315 {
316 const uint64_t val = *reinterpret_cast<const uint64_t*>(ptr);
317 ptr += 8;
318 if (val>UINT32_MAX)
319 return Number::New(val);
320 return Integer::NewFromUnsigned(val);
321 }
322 case 'S': { Handle<Value> v=Integer::NewFromUnsigned(*reinterpret_cast<const uint16_t*>(ptr)); ptr += 2; return v; }
323 case 'C': { Handle<Value> v=Integer::NewFromUnsigned((uint16_t)*reinterpret_cast<const uint8_t*>(ptr)); ptr += 1; return v; }
324 case ':': { Handle<Value> v=String::New(ptr); return v; }
325 }
326 return Undefined();
327}
328
329
330Handle<Value> InterpreterV8::FuncClose(const Arguments &args)
331{
332 HandleScope handle_scope;
333
334 //const void *ptr = Local<External>::Cast(info.This()->GetInternalField(0))->Value();
335
336 const String::Utf8Value str(args.Holder()->Get(String::New("name")));
337 return Boolean::New(JsUnsubscribe(*str));
338}
339
340Handle<Value> InterpreterV8::FuncGetData(const Arguments &args)
341{
342 HandleScope handle_scope;
343
344 const String::Utf8Value str(args.Holder()->Get(String::New("name")));
345
346 const pair<uint64_t, EventImp *> p = JsGetEvent(*str);
347
348 const EventImp *evt = p.second;
349 if (!evt)
350 return Undefined();
351
352 //if (counter==cnt)
353 // return info.Holder();//Holder()->Get(String::New("data"));
354
355 const vector<Description> vec = JsDescription(*str);
356
357 Handle<Array> ret = Array::New();
358 ret->Set(String::New("format"), String::New(evt->GetFormat().c_str()), ReadOnly);
359 ret->Set(String::New("named"), Boolean::New(vec.size()>0), ReadOnly);
360 ret->Set(String::New("counter"), Integer::New(p.first), ReadOnly);
361 ret->Set(String::New("time"), Date::New(evt->GetJavaDate()), ReadOnly);
362 ret->Set(String::New("qos"), Integer::New(evt->GetQoS()), ReadOnly);
363
364 typedef boost::char_separator<char> separator;
365 const boost::tokenizer<separator> tokenizer(evt->GetFormat(), separator(";:"));
366
367 const vector<string> tok(tokenizer.begin(), tokenizer.end());
368
369 Handle<Array> obj = tok.size()==1 ? ret : Array::New();
370
371 const char *ptr = evt->GetText();
372 try
373 {
374 size_t pos = 1;
375 for (auto it=tok.begin(); it!=tok.end(); it++, pos++)
376 {
377 char type = (*it)[0];
378 it++;
379
380 if (it==tok.end() && type=='C')
381 type = ':';
382
383 if (it==tok.end() && type!=':')
384 return ThrowException(String::New(("Format string invalid '"+evt->GetFormat()+"'").c_str()));
385
386 string name = pos<vec.size() ? vec[pos].name : "";
387 if (tok.size()==1)
388 name = "data";
389
390 const uint32_t cnt = it==tok.end() ? 1 : stoi(it->c_str());
391
392 if (cnt==1)
393 {
394 const Handle<Value> v = Convert(type, ptr);
395 if (tok.size()>1)
396 obj->Set(pos-1, v);
397 if (!name.empty())
398 obj->Set(String::New(name.c_str()), v);
399 }
400 else
401 {
402 Handle<Array> a = Array::New(cnt);
403 for (uint32_t i=0; i<cnt; i++)
404 a->Set(i, Convert(type, ptr));
405 if (tok.size()>1)
406 obj->Set(pos-1, a);
407 if (!name.empty())
408 obj->Set(String::New(name.c_str()), a);
409 }
410
411 if (it==tok.end())
412 break;
413 }
414
415 if (tok.size()>1)
416 ret->Set(String::New("data"), obj, ReadOnly);
417
418 return handle_scope.Close(ret);
419 }
420 catch (...)
421 {
422 return ThrowException(String::New(("Format string conversion '"+evt->GetFormat()+"' failed.").c_str()));
423 }
424}
425
426/*
427void Cleanup( Persistent<Value> object, void *parameter )
428{
429 cout << "======================> RemoveMyObj()" << endl;
430}*/
431
432Handle<Value> InterpreterV8::FuncOpen(const Arguments &args)
433{
434 if (args.Length()!=1)
435 return ThrowException(String::New("Number of arguments must be exactly 1."));
436
437 if (!args[0]->IsString())
438 return ThrowException(String::New("Argument 1 must be a string."));
439
440 //if (!args.IsConstructCall())
441 // return ThrowException(String::New("Must be used as constructor."));
442
443 HandleScope handle_scope;
444
445 const String::Utf8Value str(args[0]);
446
447 void *ptr = JsSubscribe(*str);
448 if (ptr==0)
449 return ThrowException(String::New(("Subscription to '"+string(*str)+"' already exists.").c_str()));
450
451 Handle<ObjectTemplate> tem = ObjectTemplate::New();
452 tem->Set(String::New("get"), FunctionTemplate::New(WrapGetData), ReadOnly);
453 tem->Set(String::New("close"), FunctionTemplate::New(WrapClose), ReadOnly);
454 tem->Set(String::New("name"), String::New(*str), ReadOnly);
455 tem->SetInternalFieldCount(1);
456 //tem->Set(String::New("toString"), String::New(("[object Dim "+string(*str)+"]").c_str()), ReadOnly);
457
458 Handle<Object> obj = tem->NewInstance();
459 obj->SetInternalField(0, External::New(ptr));
460
461 return handle_scope.Close(obj);
462
463 // Persistent<Object> p = Persistent<Object>::New(obj->NewInstance());
464 // obj.MakeWeak((void*)1, Cleanup);
465 // return obj;
466}
467
468bool InterpreterV8::JsRun(const string &filename, const map<string, string> &map)
469{
470 v8::Locker locker;
471 fThreadId = V8::GetCurrentThreadId();
472
473 JsLoad(filename);
474
475 HandleScope handle_scope;
476
477 // Create a template for the global object.
478 Handle<ObjectTemplate> dim = ObjectTemplate::New();
479 dim->Set(String::New("print"), FunctionTemplate::New(WrapPrint), ReadOnly);
480 dim->Set(String::New("out"), FunctionTemplate::New(WrapOut), ReadOnly);
481 dim->Set(String::New("wait"), FunctionTemplate::New(WrapWait), ReadOnly);
482 dim->Set(String::New("send"), FunctionTemplate::New(WrapSend), ReadOnly);
483 dim->Set(String::New("state"), FunctionTemplate::New(WrapState), ReadOnly);
484 dim->Set(String::New("sleep"), FunctionTemplate::New(WrapSleep), ReadOnly);
485 dim->Set(String::New("open"), FunctionTemplate::New(WrapOpen), ReadOnly);
486
487 Handle<ObjectTemplate> global = ObjectTemplate::New();
488 global->Set(String::New("dim"), dim, ReadOnly);
489 global->Set(String::New("include"), FunctionTemplate::New(WrapInclude), ReadOnly);
490 global->Set(String::New("exit"), FunctionTemplate::New(WrapExit), ReadOnly);
491 global->Set(String::New("version"), FunctionTemplate::New(InterpreterV8::FuncVersion), ReadOnly);
492
493 // Persistent
494 Handle<Context> context = Context::New(NULL, global);
495 if (context.IsEmpty())
496 {
497 //printf("Error creating context\n");
498 return false;
499 }
500
501 v8::Context::Scope scope(context);
502
503 Local<Array> args = Array::New(map.size());
504 for (auto it=map.begin(); it!=map.end(); it++)
505 args->Set(String::New(it->first.c_str()), String::New(it->second.c_str()));
506 context->Global()->Set(String::New("$"), args, ReadOnly);
507 context->Global()->Set(String::New("arg"), args, ReadOnly);
508
509 JsStart(filename);
510
511 //context->Enter();
512 v8::Locker::StartPreemption(10);
513 const bool rc = ExecuteFile(filename);
514 v8::Locker::StopPreemption();
515 //context->Exit();
516
517 //context.Dispose();
518
519 JsEnd(filename);
520
521 return rc;
522}
523
524void InterpreterV8::JsStop()
525{
526 v8::Locker locker;
527 //cout << "Terminate " << fThreadId << endl;
528 if (This->fThreadId>=0)
529 v8::V8::TerminateExecution(This->fThreadId);
530 //cout << "Terminate " << fThreadId << endl;
531 v8::Unlocker unlocker;
532}
533
534#endif
535
Note: See TracBrowser for help on using the repository browser.