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

Last change on this file since 14561 was 14560, checked in by tbretz, 12 years ago
Added the possibility to change the state of the state machine and define new states; renamed open to subscribe
File size: 35.3 KB
Line 
1#include "InterpreterV8.h"
2
3#ifdef HAVE_SQL
4#include "Database.h"
5#endif
6
7#include <boost/tokenizer.hpp>
8
9InterpreterV8 *InterpreterV8::This = 0;
10
11#ifdef HAVE_V8
12
13#include <v8.h>
14#include <fstream>
15#include <sstream>
16#include <iomanip>
17
18using namespace std;
19using namespace v8;
20
21bool InterpreterV8::ReportException(TryCatch* try_catch)
22{
23 if (!try_catch->CanContinue())
24 return false;
25
26 const HandleScope handle_scope;
27
28 const String::Utf8Value exception(try_catch->Exception());
29
30 if (*exception && string(*exception)=="exit")
31 return true;
32 if (*exception && string(*exception)=="null")
33 return false;
34
35 const Handle<Message> message = try_catch->Message();
36 if (message.IsEmpty())
37 return false;
38
39 // Print (filename):(line number): (message).
40 const String::Utf8Value filename(message->GetScriptResourceName());
41
42 ostringstream out;
43
44 if (*filename)
45 out << *filename << ": ";
46 out << "l." << message->GetLineNumber();
47 if (*exception)
48 out << ": " << *exception;
49
50 JsException(out.str());
51
52 // Print line of source code.
53 const String::Utf8Value sourceline(message->GetSourceLine());
54 if (*sourceline)
55 JsException(*sourceline);
56
57 // Print wavy underline (GetUnderline is deprecated).
58 const int start = message->GetStartColumn();
59 const int end = message->GetEndColumn();
60
61 out.str("");
62 if (start>0)
63 out << setfill(' ') << setw(start) << ' ';
64 out << setfill('^') << setw(end-start) << '^';
65
66 JsException(out.str());
67
68 String::Utf8Value stack_trace(try_catch->StackTrace());
69 if (stack_trace.length()<=0)
70 return false;
71
72 //if (*stack_trace)
73 // JsException(string("\n")+*stack_trace);
74
75 return false;
76}
77
78// Executes a string within the current v8 context.
79bool InterpreterV8::ExecuteStringNT(const Handle<String> &code, const Handle<Value> &file)
80{
81 if (code.IsEmpty())
82 return true;
83
84 const HandleScope handle_scope;
85
86 const Handle<Script> script = Script::Compile(code, file);
87 if (script.IsEmpty())
88 return false;
89
90 JsSetState(3);
91
92 const Handle<Value> result = script->Run();
93 if (result.IsEmpty())
94 return false;
95
96 // If all went well and the result wasn't undefined then print
97 // the returned value.
98 if (!result->IsUndefined())
99 JsResult(*String::Utf8Value(result));
100
101 return true;
102}
103
104bool InterpreterV8::ExecuteCode(const Handle<String> &code, const Handle<Value> &file)
105{
106 TryCatch exception;
107
108 const bool rc = ExecuteStringNT(code, file);
109
110 // Check if this is a termination exception
111 //if (!exception.CanContinue())
112 // return false;
113
114 if (exception.HasCaught())
115 return ReportException(&exception);
116
117 return rc;
118}
119
120bool InterpreterV8::ExecuteCode(const string &code, const string &file)
121{
122 return ExecuteCode(String::New(code.c_str(), code.size()),
123 String::New(file.c_str()));
124}
125
126bool InterpreterV8::ExecuteFile(const string &name)
127{
128 ifstream fin(name.c_str());
129 if (!fin)
130 {
131 JsException("Error - Could not open file '"+name+"'");
132 return false;
133 }
134
135 string buffer;
136 if (!getline(fin, buffer, '\0'))
137 return true;
138
139 if (fin.fail())
140 {
141 JsException("Error - reading file.");
142 return false;
143 }
144
145 return ExecuteCode(buffer, name);
146}
147
148Handle<Value> InterpreterV8::FuncWait(const Arguments& args)
149{
150 if (args.Length()!=2 && args.Length()!=3)
151 return ThrowException(String::New("Number of arguments must be 2 or 3."));
152
153 if (!args[0]->IsString())
154 return ThrowException(String::New("Argument 1 not a string."));
155
156 if (!args[1]->IsInt32() && !args[1]->IsString())
157 return ThrowException(String::New("Argument 2 not an int32 and not a string."));
158
159 if (args.Length()==3 && !args[2]->IsUint32())
160 return ThrowException(String::New("Argument 3 not an uint32."));
161
162 // Using a Javascript function has the advantage that it is fully
163 // interruptable without the need of C++ code
164
165 const string index = args[1]->IsInt32() ? "s.index" : "s.name";
166 const bool timeout = args.Length()==3;
167 const string arg0 = *String::Utf8Value(args[0]);
168 const string state = args[1]->IsString() ? *String::Utf8Value(args[1]) : "";
169 const string arg1 = args[1]->IsString() ? ("\""+state+"\"") : to_string(args[1]->Int32Value());
170
171 if (arg0.find_first_of("\"'")!=string::npos)
172 return ThrowException(String::New("Server name must not contain quotation marks."));
173
174 if (args[1]->IsString())
175 if (state.find_first_of("\"'")!=string::npos)
176 return ThrowException(String::New("State name must not contain quotation marks."));
177
178 string code = "(function(name,state,ms)"
179 "{";
180 if (timeout)
181 code += "var t = new Date();";
182 code += "while (1)"
183 "{"
184 "var s = dim.state(name);"
185 "if(!"+index+")throw 'Waitig for state "+arg1+" of server "+arg0+" failed.';"
186 "if(state=="+index+")return true;";
187 if (timeout)
188 code += "if((new Date()-t)>ms)return false;";
189
190 code += "dim.sleep();"
191 "}"
192 "})('"+arg0+"',"+arg1;
193 if (timeout)
194 code += "," + to_string(args[2]->Int32Value());
195 code += ");";
196
197 const HandleScope handle_scope;
198
199 // It is not strictly necessary to catch the exception, instead
200 // script->Run() could just be returned, but catching the
201 // exception allow to print the position in the file in
202 // the exception handler instead of just the posiiton in the script.
203 TryCatch exception;
204
205 const Handle<Script> script = Script::Compile(String::New(code.c_str()));
206 const Handle<Value> result = script->Run();
207
208 return exception.HasCaught() ? exception.ReThrow() : result;
209
210 /*
211 const string server = *String::Utf8Value(args[0]);
212 const int32_t state = args[1]->Int32Value();
213 const uint32_t millisec = args.Length()==3 ? args[2]->Int32Value() : 0;
214
215 const int rc = JsWait(server, state, millisec);
216
217 if (rc==0 || rc==1)
218 return Boolean::New(rc);
219
220 return ThrowException(String::New(("Waitig for state "+to_string(state)+" of server '"+server+"' failed.").c_str()));
221 */
222}
223
224Handle<Value> InterpreterV8::FuncSend(const Arguments& args)
225{
226 if (args.Length()==0)
227 return ThrowException(String::New("Number of arguments must be at least 1."));
228
229 if (!args[0]->IsString())
230 return ThrowException(String::New("Argument 1 must be a string."));
231
232 const HandleScope handle_scope;
233
234 const String::Utf8Value str(args[0]);
235
236 string command = *str;
237
238 // Escape all string arguments. All others can be kept as they are.
239 for (int i=1; i<args.Length(); i++)
240 {
241 const String::Utf8Value arg(args[i]);
242 if (args[i]->IsString())
243 command += " \""+string(*arg)+"\"";
244 else
245 command += " "+string(*arg);
246 }
247
248 return Boolean::New(JsSend(command));
249}
250
251Handle<Value> InterpreterV8::FuncSleep(const Arguments& args)
252{
253 if (args.Length()==0)
254 {
255 // Theoretically, the CPU usage can be reduced by maybe a factor
256 // of four using a larger value, but this also means that the
257 // JavaScript is locked for a longer time.
258 usleep(1000);
259 return Undefined();
260 }
261
262 if (args.Length()!=1)
263 return ThrowException(String::New("Number of arguments must be exactly 1."));
264
265 if (!args[0]->IsUint32())
266 return ThrowException(String::New("Argument 1 must be an uint32."));
267
268 // Using a Javascript function has the advantage that it is fully
269 // interruptable without the need of C++ code
270
271 const string code =
272 "(function(){"
273 "var t=new Date();"
274 "while ((new Date()-t)<"+to_string(args[0]->Int32Value())+") dim.sleep();"
275 "})();";
276
277 const HandleScope handle_scope;
278
279 const Handle<Script> script = Script::Compile(String::New(code.c_str()));
280 return script->Run();
281
282 //JsSleep(args[0]->Int32Value());
283 //return Undefined();
284}
285
286Handle<Value> InterpreterV8::FuncState(const Arguments& args)
287{
288 if (args.Length()!=1)
289 return ThrowException(String::New("Number of arguments must be exactly 1."));
290
291 if (!args[0]->IsString())
292 return ThrowException(String::New("Argument 1 must be a string."));
293
294 // Return state.name/state.index
295
296 const String::Utf8Value str(args[0]);
297
298 const State rc = JsState(*str);
299
300 //if (rc.first<=-256)
301 // return Undefined();
302
303 HandleScope handle_scope;
304
305 // It is important to catch the exception thrown
306 // by Date::New in case of thread termination!
307 Local<Value> date;
308 {
309 TryCatch exception;
310 date = Date::New(rc.time.JavaDate());
311 if (exception.HasCaught())
312 return exception.ReThrow();
313 }
314
315 Handle<ObjectTemplate> obj = ObjectTemplate::New();
316 obj->Set(String::New("index"), rc.index<=-256?Undefined():Integer::New(rc.index), ReadOnly);
317 obj->Set(String::New("name"), rc.index<=-256?Undefined():String::New(rc.name.c_str()), ReadOnly);
318 if (rc.index>-256)
319 obj->Set(String::New("time"), date);
320 //obj->Set(String::New("toString"), String::New(("[Object state "+string(*str)+":"+to_string(rc.index)+"]").c_str()));
321
322 return handle_scope.Close(obj->NewInstance());
323}
324
325Handle<Value> InterpreterV8::FuncNewState(const Arguments& args)
326{
327 if (args.Length()<1 || args.Length()>3)
328 return ThrowException(String::New("Number of arguments must be 1, 2 or 3."));
329
330 if (!args[0]->IsUint32())
331 return ThrowException(String::New("Argument 1 must be an uint32."));
332 if (args.Length()>1 && !args[1]->IsString())
333 return ThrowException(String::New("Argument 2 must be a string."));
334 if (args.Length()>2 && !args[2]->IsString())
335 return ThrowException(String::New("Argument 3 must be a string."));
336
337 const HandleScope handle_scope;
338
339 const uint32_t index = args[0]->Int32Value();
340 const string name = *String::Utf8Value(args[1]);
341 const string comment = *String::Utf8Value(args[2]);
342
343 if (index<10 || index>255)
344 return ThrowException(String::New("State must be in the range [10, 255]."));
345
346 if (name.empty())
347 return ThrowException(String::New("State name must not be empty."));
348
349 if (name.find_first_of(':')!=string::npos || name.find_first_of('=')!=string::npos)
350 return ThrowException(String::New("State name must not contain : or =."));
351
352 if (JsHasState(index) || JsHasState(name))
353 {
354 const string what =
355 "State index ["+to_string(index)+"] or name ["+name+"] already defined.";
356
357 return ThrowException(String::New(what.c_str()));
358 }
359
360 return Boolean::New(JsNewState(index, name, comment));
361}
362
363Handle<Value> InterpreterV8::FuncSetState(const Arguments& args)
364{
365 if (args.Length()!=1)
366 return ThrowException(String::New("Number of arguments must be exactly 1."));
367
368 if (!args[0]->IsUint32() && !args[0]->IsString())
369 return ThrowException(String::New("Argument must be an unint32 or a string."));
370
371 const HandleScope handle_scope;
372
373 int index = -2;
374 if (args[0]->IsUint32())
375 {
376 index = args[0]->Int32Value();
377 }
378 else
379 {
380 const string name = *String::Utf8Value(args[0]);
381 index = JsGetState(name);
382 if (index==-2)
383 return ThrowException(String::New(("State '"+name+"' not found.").c_str()));
384 }
385
386 if (index<10 || index>255)
387 return ThrowException(String::New("State must be in the range [10, 255]."));
388
389 return Boolean::New(JsSetState(index));
390}
391
392Handle<Value> InterpreterV8::FuncExit(const Arguments &)
393{
394 V8::TerminateExecution(fThreadId);
395 return ThrowException(String::New("exit"));
396/*
397 if (args.Length()!=1)
398 return ThrowException(String::New("Number of arguments must be exactly 1."));
399
400 if (!args[0]->IsUint32())
401 return ThrowException(String::New("Argument 1 must be an uint32."));
402
403 const HandleScope handle_scope;
404
405 JsSleep(args[0]->Int32Value());
406*/
407 return Undefined();
408}
409
410// The callback that is invoked by v8 whenever the JavaScript 'print'
411// function is called. Prints its arguments on stdout separated by
412// spaces and ending with a newline.
413Handle<Value> InterpreterV8::FuncPrint(const Arguments& args)
414{
415 for (int i=0; i<args.Length(); i++)
416 {
417 const HandleScope handle_scope;
418
419 const String::Utf8Value str(args[i]);
420 if (*str)
421 JsPrint(*str);
422 }
423 return Undefined();
424}
425
426Handle<Value> InterpreterV8::FuncAlarm(const Arguments& args)
427{
428 for (int i=0; i<args.Length(); i++)
429 {
430 const HandleScope handle_scope;
431
432 const String::Utf8Value str(args[i]);
433 if (*str)
434 JsAlarm(*str);
435 }
436
437 if (args.Length()==0)
438 JsAlarm();
439
440 return Undefined();
441}
442
443Handle<Value> InterpreterV8::FuncOut(const Arguments& args)
444{
445 for (int i=0; i<args.Length(); i++)
446 {
447 const HandleScope handle_scope;
448
449 const String::Utf8Value str(args[i]);
450 if (*str)
451 JsOut(*str);
452 }
453 return Undefined();
454}
455
456// The callback that is invoked by v8 whenever the JavaScript 'load'
457// function is called. Loads, compiles and executes its argument
458// JavaScript file.
459Handle<Value> InterpreterV8::FuncInclude(const Arguments& args)
460{
461 for (int i=0; i<args.Length(); i++)
462 {
463 const HandleScope handle_scope;
464
465 const String::Utf8Value file(args[i]);
466 if (*file == NULL)
467 return ThrowException(String::New(("Error loading file '"+string(*file)+"'").c_str()));
468
469 if (!ExecuteFile(*file))
470 return ThrowException(String::New(("Execution of '"+string(*file)+"' failed").c_str()));
471 }
472 return Undefined();
473}
474
475Handle<Value> InterpreterV8::FuncVersion(const Arguments&)
476{
477 return String::New(V8::GetVersion());
478}
479
480Handle<Value> InterpreterV8::FuncDbClose(const Arguments &args)
481{
482 HandleScope handle_scope;
483
484 void *ptr = Handle<External>::Cast(args.This()->GetInternalField(0))->Value();
485 if (!ptr)
486 return Boolean::New(false);
487
488#ifdef HAVE_SQL
489 Database *db = reinterpret_cast<Database*>(ptr);
490 auto it = find(fDatabases.begin(), fDatabases.end(), db);
491 fDatabases.erase(it);
492 delete db;
493#endif
494
495 args.This()->SetInternalField(0, External::New(0));
496
497 return Boolean::New(true);
498}
499Handle<Value> InterpreterV8::FuncDbQuery(const Arguments &args)
500{
501 if (args.Length()!=1)
502 return ThrowException(String::New("Number of arguments must be exactly 1."));
503
504 if (!args[0]->IsString())
505 return ThrowException(String::New("Both arguments must be a string."));
506
507 HandleScope handle_scope;
508
509 void *ptr = Handle<External>::Cast(args.This()->GetInternalField(0))->Value();
510 if (!ptr)
511 return Undefined();
512
513 const String::Utf8Value query(args[0]);
514
515#ifdef HAVE_SQL
516 try
517 {
518 Database *db = reinterpret_cast<Database*>(ptr);
519
520 const mysqlpp::StoreQueryResult res = db->query(*query).store();
521
522 Handle<Array> ret = Array::New();
523 ret->Set(String::New("table"), String::New(res.table()), ReadOnly);
524
525 Handle<Array> cols = Array::New();
526
527 int irow=0;
528 for (vector<mysqlpp::Row>::const_iterator it=res.begin(); it<res.end(); it++)
529 {
530 Handle<Array> row = Array::New();
531
532 const mysqlpp::FieldNames *list = it->field_list().list;
533
534 for (size_t i=0; i<it->size(); i++)
535 {
536 const Handle<Value> name = String::New((*list)[i].c_str());
537 if (irow==0)
538 cols->Set(Integer::NewFromUnsigned(i), name, ReadOnly);
539
540 if ((*it)[i].is_null())
541 {
542 row->Set(name, Undefined(), ReadOnly);
543 continue;
544 }
545
546 const string sql_type = (*it)[i].type().sql_name();
547
548 const bool uns = sql_type.find("UNSIGNED")==string::npos;
549
550 if (sql_type.find("BIGINT")!=string::npos)
551 {
552 if (uns)
553 {
554 const uint64_t val = (uint64_t)(*it)[i];
555 if (val>UINT32_MAX)
556 row->Set(name, Number::New(val), ReadOnly);
557 else
558 row->Set(name, Integer::NewFromUnsigned(val), ReadOnly);
559 }
560 else
561 {
562 const int64_t val = (int64_t)(*it)[i];
563 if (val<INT32_MIN || val>INT32_MAX)
564 row->Set(name, Number::New(val), ReadOnly);
565 else
566 row->Set(name, Integer::NewFromUnsigned(val), ReadOnly);
567 }
568 continue;
569 }
570
571 // 32 bit
572 if (sql_type.find("INT")!=string::npos)
573 {
574 if (uns)
575 row->Set(name, Integer::NewFromUnsigned((uint32_t)(*it)[i]), ReadOnly);
576 else
577 row->Set(name, Integer::New((int32_t)(*it)[i]), ReadOnly);
578 }
579
580 if (sql_type.find("BOOL")!=string::npos )
581 {
582 row->Set(name, Boolean::New((bool)(*it)[i]), ReadOnly);
583 continue;
584 }
585
586 if (sql_type.find("FLOAT")!=string::npos)
587 {
588 ostringstream val;
589 val << setprecision(7) << (float)(*it)[i];
590 row->Set(name, Number::New(stod(val.str())), ReadOnly);
591 continue;
592
593 }
594 if (sql_type.find("DOUBLE")!=string::npos)
595 {
596 row->Set(name, Number::New((double)(*it)[i]), ReadOnly);
597 continue;
598 }
599
600 if (sql_type.find("CHAR")!=string::npos ||
601 sql_type.find("TEXT")!=string::npos)
602 {
603 row->Set(name, String::New((const char*)(*it)[i]), ReadOnly);
604 continue;
605 }
606
607 time_t date = 0;
608 if (sql_type.find("TIMESTAMP")!=string::npos)
609 date = mysqlpp::Time((*it)[i]);
610
611 if (sql_type.find("DATETIME")!=string::npos)
612 date = mysqlpp::DateTime((*it)[i]);
613
614 if (sql_type.find(" DATE ")!=string::npos)
615 date = mysqlpp::Date((*it)[i]);
616
617 if (date>0)
618 {
619 // It is important to catch the exception thrown
620 // by Date::New in case of thread termination!
621 TryCatch exception;
622 Local<Value> val = Date::New(date*1000);
623 if (exception.HasCaught())
624 return exception.ReThrow();
625 //if (V8::IsExecutionTerminating())
626 // return Undefined();
627
628 row->Set(name, val, ReadOnly);
629 }
630 }
631
632 ret->Set(Integer::NewFromUnsigned(irow++), row, ReadOnly);
633 }
634
635 if (irow>0)
636 ret->Set(String::New("cols"), cols, ReadOnly);
637
638 return handle_scope.Close(ret);
639 }
640 catch (const exception &e)
641 {
642 return ThrowException(String::New(e.what()));
643 }
644#endif
645}
646
647Handle<Value> InterpreterV8::FuncDatabase(const Arguments &args)
648{
649 if (args.Length()!=1)
650 return ThrowException(String::New("Number of arguments must be exactly 1."));
651
652 if (!args[0]->IsString())
653 return ThrowException(String::New("Argument 1 must be a string."));
654
655 HandleScope handle_scope;
656
657 const String::Utf8Value database(args[0]);
658 const String::Utf8Value query (args[1]);
659
660#ifdef HAVE_SQL
661 try
662 {
663 Database *db = new Database(*database);
664 fDatabases.push_back(db);
665
666 Handle<ObjectTemplate> tem = ObjectTemplate::New();
667 tem->Set(String::New("user"), String::New(db->user.c_str()), ReadOnly);
668 tem->Set(String::New("server"), String::New(db->server.c_str()), ReadOnly);
669 tem->Set(String::New("database"), String::New(db->db.c_str()), ReadOnly);
670 tem->Set(String::New("port"), db->port==0?Undefined():Integer::NewFromUnsigned(db->port), ReadOnly);
671 tem->Set(String::New("query"), FunctionTemplate::New(WrapDbQuery), ReadOnly);
672 tem->Set(String::New("close"), FunctionTemplate::New(WrapDbClose), ReadOnly);
673 tem->SetInternalFieldCount(1);
674
675 Handle<Object> obj = tem->NewInstance();
676 obj->SetInternalField(0, External::New(db));
677
678 return handle_scope.Close(obj);
679 }
680 catch (const exception &e)
681 {
682 return ThrowException(String::New(e.what()));
683 }
684#endif
685}
686
687Handle<Value> InterpreterV8::Convert(char type, const char* &ptr)
688{
689 // Dim values are always unsigned per (FACT++) definition
690 switch (type)
691 {
692 case 'F':
693 {
694 // Remove the "imprecision" effect coming from casting a float to
695 // a double and then showing it with double precision
696 ostringstream val;
697 val << setprecision(7) << *reinterpret_cast<const float*>(ptr);
698 Handle<Value> v=Number::New(stod(val.str()));
699 ptr+=4;
700 return v;
701 }
702 case 'D': { Handle<Value> v=Number::New(*reinterpret_cast<const double*>(ptr)); ptr+=8; return v; }
703 case 'I':
704 case 'L': { Handle<Value> v=Integer::NewFromUnsigned(*reinterpret_cast<const uint32_t*>(ptr)); ptr += 4; return v; }
705 case 'X':
706 {
707 const uint64_t val = *reinterpret_cast<const uint64_t*>(ptr);
708 ptr += 8;
709 if (val>UINT32_MAX)
710 return Number::New(val);
711 return Integer::NewFromUnsigned(val);
712 }
713 case 'S': { Handle<Value> v=Integer::NewFromUnsigned(*reinterpret_cast<const uint16_t*>(ptr)); ptr += 2; return v; }
714 case 'C': { Handle<Value> v=Integer::NewFromUnsigned((uint16_t)*reinterpret_cast<const uint8_t*>(ptr)); ptr += 1; return v; }
715 case ':': { Handle<Value> v=String::New(ptr); return v; }
716 }
717 return Undefined();
718}
719
720Handle<Value> InterpreterV8::FuncClose(const Arguments &args)
721{
722 const HandleScope handle_scope;
723
724 //const void *ptr = Local<External>::Cast(args.Holder()->GetInternalField(0))->Value();
725
726 const String::Utf8Value str(args.Holder()->Get(String::New("name")));
727
728 const auto it = fReverseMap.find(*str);
729 if (it!=fReverseMap.end())
730 {
731 it->second.Dispose();
732 fReverseMap.erase(it);
733 }
734
735 args.Holder()->Set(String::New("isOpen"), Boolean::New(false), ReadOnly);
736
737 return Boolean::New(JsUnsubscribe(*str));
738}
739
740Handle<Value> InterpreterV8::ConvertEvent(const EventImp *evt, uint64_t counter, const char *str)
741{
742 Local<Value> date;
743
744 // It is important to catch the exception thrown
745 // by Date::New in case of thread termination!
746 {
747 TryCatch exception;
748 date = Date::New(evt->GetJavaDate());
749 if (exception.HasCaught())
750 return exception.ReThrow();
751 }
752
753 const vector<Description> vec = JsDescription(str);
754
755 Handle<Array> ret = Array::New();
756 ret->Set(String::New("name"), String::New(str), ReadOnly);
757 ret->Set(String::New("format"), String::New(evt->GetFormat().c_str()), ReadOnly);
758 ret->Set(String::New("named"), Boolean::New(vec.size()>0), ReadOnly);
759 ret->Set(String::New("qos"), Integer::New(evt->GetQoS()), ReadOnly);
760 ret->Set(String::New("size"), Integer::New(evt->GetSize()), ReadOnly);
761 ret->Set(String::New("counter"), Integer::New(counter), ReadOnly);
762 ret->Set(String::New("time"), date, ReadOnly);
763
764 typedef boost::char_separator<char> separator;
765 const boost::tokenizer<separator> tokenizer(evt->GetFormat(), separator(";:"));
766
767 const vector<string> tok(tokenizer.begin(), tokenizer.end());
768
769 Handle<Array> obj = tok.size()==1 ? ret : Array::New();
770
771 const char *ptr = evt->GetText();
772 try
773 {
774 size_t pos = 1;
775 for (auto it=tok.begin(); it!=tok.end(); it++, pos++)
776 {
777 if (ptr>=evt->GetText())
778 return ret;
779
780 char type = (*it)[0];
781 it++;
782
783 if (it==tok.end() && type=='C')
784 type = ':';
785
786 if (it==tok.end() && type!=':')
787 return Exception::Error(String::New(("Format string invalid '"+evt->GetFormat()+"'").c_str()));
788
789 string name = pos<vec.size() ? vec[pos].name : "";
790 if (tok.size()==1)
791 name = "data";
792
793 const uint32_t cnt = it==tok.end() ? 1 : stoi(it->c_str());
794
795 if (cnt==1)
796 {
797 const Handle<Value> v = Convert(type, ptr);
798 if (tok.size()>1)
799 obj->Set(pos-1, v);
800 if (!name.empty())
801 obj->Set(String::New(name.c_str()), v);
802 }
803 else
804 {
805 Handle<Array> a = Array::New(cnt);
806 for (uint32_t i=0; i<cnt; i++)
807 a->Set(i, Convert(type, ptr));
808 if (tok.size()>1)
809 obj->Set(pos-1, a);
810 if (!name.empty())
811 obj->Set(String::New(name.c_str()), a);
812 }
813
814 if (it==tok.end())
815 break;
816 }
817
818 if (tok.size()>1)
819 ret->Set(String::New("data"), obj, ReadOnly);
820
821 return ret;
822 }
823 catch (...)
824 {
825 return Exception::Error(String::New(("Format string conversion '"+evt->GetFormat()+"' failed.").c_str()));
826 }
827}
828
829Handle<Value> InterpreterV8::FuncGetData(const Arguments &args)
830{
831 HandleScope handle_scope;
832
833 const String::Utf8Value str(args.Holder()->Get(String::New("name")));
834
835 const pair<uint64_t, EventImp *> p = JsGetEvent(*str);
836
837 const EventImp *evt = p.second;
838 if (!evt)
839 return Undefined();
840
841 //if (counter==cnt)
842 // return info.Holder();//Holder()->Get(String::New("data"));
843
844 Handle<Value> ret = ConvertEvent(evt, p.first, *str);
845 return ret->IsNativeError() ? ThrowException(ret) : handle_scope.Close(ret);
846}
847
848// This is a callback from the RemoteControl piping event handling
849// to the java script ---> in test phase!
850void InterpreterV8::JsHandleEvent(const EventImp &evt, uint64_t cnt, const string &service)
851{
852 if (fThreadId<0)
853 return;
854
855 Locker locker;
856
857 const auto it = fReverseMap.find(service);
858 if (it==fReverseMap.end())
859 return;
860
861 const HandleScope handle_scope;
862
863 Handle<Object> obj = it->second;
864 if (obj.IsEmpty())
865 return;
866
867 const Handle<String> onchange = String::New("onchange");
868 if (!obj->Has(onchange))
869 return;
870
871 const Handle<Value> val = obj->Get(onchange);
872 if (!val->IsFunction())
873 return;
874
875 // -------------------------------------------------------------------
876 // We are not in a context... we need to get into one for Array::New
877
878 Persistent<Context> context = Context::New();
879 if (context.IsEmpty())
880 return;
881
882 const Context::Scope scope(context);
883
884 // -------------------------------------------------------------------
885
886 TryCatch exception;
887
888 Handle<Value> ret = ConvertEvent(&evt, cnt, service.c_str());
889 if (ret->IsArray())
890 {
891 Handle<Array> data = Handle<Array>::Cast(ret);
892 Handle<Value> args[] = { data };
893
894 Handle<Function>::Cast(val)->Call(obj, 1, args);
895 }
896
897 if (exception.HasCaught())
898 ReportException(&exception);
899
900 if (ret->IsNativeError())
901 JsException(service+".onchange callback - "+*String::Utf8Value(ret));
902
903 context.Dispose();
904
905 if (ret->IsUndefined() || ret->IsNativeError() || exception.HasCaught())
906 V8::TerminateExecution(fThreadId);
907}
908
909Handle<Value> InterpreterV8::OnChangeSet(Local<String> prop, Local< Value > value, const AccessorInfo &)
910{
911 const HandleScope handle_scope;
912
913 // Returns the value if the setter intercepts the request. Otherwise, returns an empty handle.
914 const string server = *String::Utf8Value(prop);
915 auto it = fStateCallbacks.find(server);
916
917 if (it!=fStateCallbacks.end())
918 {
919 it->second.Dispose();
920 fStateCallbacks.erase(it);
921 }
922
923 if (value->IsFunction())
924 fStateCallbacks[server] = Persistent<Value>::New(value);
925
926 return Handle<Value>();
927}
928
929
930void InterpreterV8::JsHandleState(const std::string &server, const State &state)
931{
932 if (fThreadId<0)
933 return;
934
935 Locker locker;
936
937 auto it = fStateCallbacks.find(server);
938 if (it==fStateCallbacks.end())
939 {
940 it = fStateCallbacks.find("*");
941 if (it==fStateCallbacks.end())
942 return;
943 }
944
945 const HandleScope handle_scope;
946
947 if (it->second.IsEmpty() || !it->second->IsFunction())
948 return;
949
950 // -------------------------------------------------------------------
951 // We are not in a context... we need to get into one for Array::New
952
953 Persistent<Context> context = Context::New();
954 if (context.IsEmpty())
955 return;
956
957 const Context::Scope scope(context);
958
959 // -------------------------------------------------------------------
960
961 TryCatch exception;
962
963 // It is important to catch the exception thrown
964 // by Date::New in case of thread termination!
965 Local<Value> date = Date::New(state.time.JavaDate());
966
967 if (!exception.HasCaught())
968 {
969 Handle<ObjectTemplate> obj = ObjectTemplate::New();
970 obj->Set(String::New("index"), state.index<=-256?Undefined():Integer::New(state.index), ReadOnly);
971 obj->Set(String::New("name"), state.index<=-256?Undefined():String::New(state.name.c_str()), ReadOnly);
972 obj->Set(String::New("comment"), state.index<=-256?Undefined():String::New(state.comment.c_str()), ReadOnly);
973 obj->Set(String::New("server"), String::New(server.c_str()), ReadOnly);
974 if (state.index>-256)
975 obj->Set(String::New("time"), date);
976
977 Handle<Value> args[] = { obj->NewInstance() };
978
979 Handle<Function> fun = Handle<Function>::Cast(it->second);
980 fun->Call(fun, 1, args);
981 }
982
983 if (exception.HasCaught())
984 ReportException(&exception);
985
986 context.Dispose();
987
988 if (exception.HasCaught())
989 V8::TerminateExecution(fThreadId);
990}
991
992/*
993void Cleanup( Persistent<Value> object, void *parameter )
994{
995 cout << "======================> RemoveMyObj()" << endl;
996}*/
997
998Handle<Value> InterpreterV8::FuncSubscribe(const Arguments &args)
999{
1000 if (args.Length()!=1)
1001 return ThrowException(String::New("Number of arguments must be exactly 1."));
1002
1003 if (!args[0]->IsString())
1004 return ThrowException(String::New("Argument 1 must be a string."));
1005
1006 //if (!args.IsConstructCall())
1007 // return ThrowException(String::New("Must be used as constructor."));
1008
1009 HandleScope handle_scope;
1010
1011 const String::Utf8Value str(args[0]);
1012
1013 void *ptr = JsSubscribe(*str);
1014 if (ptr==0)
1015 return ThrowException(String::New(("Subscription to '"+string(*str)+"' already exists.").c_str()));
1016
1017 Handle<ObjectTemplate> tem = ObjectTemplate::New();
1018 tem->Set(String::New("get"), FunctionTemplate::New(WrapGetData), ReadOnly);
1019 tem->Set(String::New("close"), FunctionTemplate::New(WrapClose), ReadOnly);
1020 tem->Set(String::New("name"), String::New(*str), ReadOnly);
1021 tem->Set(String::New("isOpen"), Boolean::New(true));
1022 tem->SetInternalFieldCount(1);
1023 //tem->Set(String::New("toString"), String::New(("[object Dim "+string(*str)+"]").c_str()), ReadOnly);
1024
1025 Handle<Object> obj = tem->NewInstance();
1026 obj->SetInternalField(0, External::New(ptr));
1027
1028 fReverseMap[*str] = Persistent<Object>::New(obj);
1029
1030 return handle_scope.Close(obj);
1031
1032 // Persistent<Object> p = Persistent<Object>::New(obj->NewInstance());
1033 // obj.MakeWeak((void*)1, Cleanup);
1034 // return obj;
1035}
1036
1037bool InterpreterV8::JsRun(const string &filename, const map<string, string> &map)
1038{
1039 Locker locker;
1040 fThreadId = V8::GetCurrentThreadId();
1041
1042 JsLoad(filename);
1043
1044 HandleScope handle_scope;
1045
1046 // Create a template for the global object.
1047 Handle<ObjectTemplate> dim = ObjectTemplate::New();
1048 dim->Set(String::New("print"), FunctionTemplate::New(WrapPrint), ReadOnly);
1049 dim->Set(String::New("alarm"), FunctionTemplate::New(WrapAlarm), ReadOnly);
1050 dim->Set(String::New("out"), FunctionTemplate::New(WrapOut), ReadOnly);
1051 dim->Set(String::New("wait"), FunctionTemplate::New(WrapWait), ReadOnly);
1052 dim->Set(String::New("send"), FunctionTemplate::New(WrapSend), ReadOnly);
1053 dim->Set(String::New("state"), FunctionTemplate::New(WrapState), ReadOnly);
1054 dim->Set(String::New("newState"), FunctionTemplate::New(WrapNewState), ReadOnly);
1055 dim->Set(String::New("setState"), FunctionTemplate::New(WrapSetState), ReadOnly);
1056 dim->Set(String::New("sleep"), FunctionTemplate::New(WrapSleep), ReadOnly);
1057 dim->Set(String::New("subscribe"), FunctionTemplate::New(WrapSubscribe), ReadOnly);
1058 dim->Set(String::New("database"), FunctionTemplate::New(WrapDatabase), ReadOnly);
1059
1060 Handle<ObjectTemplate> onchange = ObjectTemplate::New();
1061 onchange->SetNamedPropertyHandler(OnChangeGet, WrapOnChangeSet);
1062 dim->Set(v8::String::New("onchange"), onchange);
1063
1064 Handle<ObjectTemplate> global = ObjectTemplate::New();
1065 global->Set(String::New("dim"), dim, ReadOnly);
1066 global->Set(String::New("include"), FunctionTemplate::New(WrapInclude), ReadOnly);
1067 global->Set(String::New("exit"), FunctionTemplate::New(WrapExit), ReadOnly);
1068 global->Set(String::New("version"), FunctionTemplate::New(InterpreterV8::FuncVersion), ReadOnly);
1069
1070 // Persistent
1071 Persistent<Context> context = Context::New(NULL, global);
1072 if (context.IsEmpty())
1073 {
1074 //printf("Error creating context\n");
1075 return false;
1076 }
1077
1078 Context::Scope scope(context);
1079
1080 Handle<Array> args = Array::New(map.size());
1081 for (auto it=map.begin(); it!=map.end(); it++)
1082 args->Set(String::New(it->first.c_str()), String::New(it->second.c_str()));
1083 context->Global()->Set(String::New("$"), args, ReadOnly);
1084 context->Global()->Set(String::New("arg"), args, ReadOnly);
1085
1086 JsStart(filename);
1087
1088 //context->Enter();
1089 Locker::StartPreemption(10);
1090 const bool rc = ExecuteFile(filename);
1091
1092 // -----
1093 // This is how an exit handler could look like, but there is no way to interrupt it
1094 // -----
1095 // Handle<Object> obj = Handle<Object>::Cast(context->Global()->Get(String::New("dim")));
1096 // if (!obj.IsEmpty())
1097 // {
1098 // Handle<Value> onexit = obj->Get(String::New("onexit"));
1099 // if (!onexit->IsUndefined())
1100 // Handle<Function>::Cast(onexit)->NewInstance(0, NULL); // argc, argv
1101 // // Handle<Object> result = Handle<Function>::Cast(onexit)->NewInstance(0, NULL); // argc, argv
1102 // }
1103
1104 Locker::StopPreemption();
1105 //context->Exit();
1106
1107 for (auto it=fStateCallbacks.begin(); it!=fStateCallbacks.end(); it++)
1108 it->second.Dispose();
1109 fStateCallbacks.clear();
1110
1111 for (auto it=fReverseMap.begin(); it!=fReverseMap.end(); it++)
1112 it->second.Dispose();
1113 fReverseMap.clear();
1114
1115 context.Dispose();
1116
1117#ifdef HAVE_SQL
1118 for (auto it=fDatabases.begin(); it!=fDatabases.end(); it++)
1119 delete *it;
1120 fDatabases.clear();
1121#endif
1122
1123 JsEnd(filename);
1124
1125 return rc;
1126}
1127
1128void InterpreterV8::JsStop()
1129{
1130 Locker locker;
1131
1132 //cout << "Terminate " << fThreadId << endl;
1133 if (This->fThreadId>=0)
1134 V8::TerminateExecution(This->fThreadId);
1135 //cout << "Terminate " << fThreadId << endl;
1136
1137 //Unlocker unlocker;
1138}
1139
1140#endif
Note: See TracBrowser for help on using the repository browser.