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

Last change on this file since 14566 was 14562, checked in by tbretz, 12 years ago
Distinguish the case between 'no data received' and 'data received but zero size'
File size: 35.6 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 // If no event was received (usually a disconnection event in
765 // the context of FACT++), no data is returned
766 if (evt->IsEmpty())
767 return ret;
768
769 // If valid data was received, but the size was zero, then
770 // null is returned as data
771 if (evt->GetSize()==0)
772 {
773 ret->Set(String::New("data"), Null(), ReadOnly);
774 return ret;
775 }
776
777 typedef boost::char_separator<char> separator;
778 const boost::tokenizer<separator> tokenizer(evt->GetFormat(), separator(";:"));
779
780 const vector<string> tok(tokenizer.begin(), tokenizer.end());
781
782 Handle<Array> obj = tok.size()==1 ? ret : Array::New();
783
784 const char *ptr = evt->GetText();
785 try
786 {
787 size_t pos = 1;
788 for (auto it=tok.begin(); it!=tok.end(); it++, pos++)
789 {
790 if (ptr>=evt->GetText())
791 return ret;
792
793 char type = (*it)[0];
794 it++;
795
796 if (it==tok.end() && type=='C')
797 type = ':';
798
799 if (it==tok.end() && type!=':')
800 return Exception::Error(String::New(("Format string invalid '"+evt->GetFormat()+"'").c_str()));
801
802 string name = pos<vec.size() ? vec[pos].name : "";
803 if (tok.size()==1)
804 name = "data";
805
806 const uint32_t cnt = it==tok.end() ? 1 : stoi(it->c_str());
807
808 if (cnt==1)
809 {
810 const Handle<Value> v = Convert(type, ptr);
811 if (tok.size()>1)
812 obj->Set(pos-1, v);
813 if (!name.empty())
814 obj->Set(String::New(name.c_str()), v);
815 }
816 else
817 {
818 Handle<Array> a = Array::New(cnt);
819 for (uint32_t i=0; i<cnt; i++)
820 a->Set(i, Convert(type, ptr));
821 if (tok.size()>1)
822 obj->Set(pos-1, a);
823 if (!name.empty())
824 obj->Set(String::New(name.c_str()), a);
825 }
826
827 if (it==tok.end())
828 break;
829 }
830
831 if (tok.size()>1)
832 ret->Set(String::New("data"), obj, ReadOnly);
833
834 return ret;
835 }
836 catch (...)
837 {
838 return Exception::Error(String::New(("Format string conversion '"+evt->GetFormat()+"' failed.").c_str()));
839 }
840}
841
842Handle<Value> InterpreterV8::FuncGetData(const Arguments &args)
843{
844 HandleScope handle_scope;
845
846 const String::Utf8Value str(args.Holder()->Get(String::New("name")));
847
848 const pair<uint64_t, EventImp *> p = JsGetEvent(*str);
849
850 const EventImp *evt = p.second;
851 if (!evt)
852 return Undefined();
853
854 //if (counter==cnt)
855 // return info.Holder();//Holder()->Get(String::New("data"));
856
857 Handle<Value> ret = ConvertEvent(evt, p.first, *str);
858 return ret->IsNativeError() ? ThrowException(ret) : handle_scope.Close(ret);
859}
860
861// This is a callback from the RemoteControl piping event handling
862// to the java script ---> in test phase!
863void InterpreterV8::JsHandleEvent(const EventImp &evt, uint64_t cnt, const string &service)
864{
865 if (fThreadId<0)
866 return;
867
868 Locker locker;
869
870 const auto it = fReverseMap.find(service);
871 if (it==fReverseMap.end())
872 return;
873
874 const HandleScope handle_scope;
875
876 Handle<Object> obj = it->second;
877 if (obj.IsEmpty())
878 return;
879
880 const Handle<String> onchange = String::New("onchange");
881 if (!obj->Has(onchange))
882 return;
883
884 const Handle<Value> val = obj->Get(onchange);
885 if (!val->IsFunction())
886 return;
887
888 // -------------------------------------------------------------------
889 // We are not in a context... we need to get into one for Array::New
890
891 Persistent<Context> context = Context::New();
892 if (context.IsEmpty())
893 return;
894
895 const Context::Scope scope(context);
896
897 // -------------------------------------------------------------------
898
899 TryCatch exception;
900
901 Handle<Value> ret = ConvertEvent(&evt, cnt, service.c_str());
902 if (ret->IsArray())
903 {
904 Handle<Array> data = Handle<Array>::Cast(ret);
905 Handle<Value> args[] = { data };
906
907 Handle<Function>::Cast(val)->Call(obj, 1, args);
908 }
909
910 if (exception.HasCaught())
911 ReportException(&exception);
912
913 if (ret->IsNativeError())
914 JsException(service+".onchange callback - "+*String::Utf8Value(ret));
915
916 context.Dispose();
917
918 if (ret->IsUndefined() || ret->IsNativeError() || exception.HasCaught())
919 V8::TerminateExecution(fThreadId);
920}
921
922Handle<Value> InterpreterV8::OnChangeSet(Local<String> prop, Local< Value > value, const AccessorInfo &)
923{
924 const HandleScope handle_scope;
925
926 // Returns the value if the setter intercepts the request. Otherwise, returns an empty handle.
927 const string server = *String::Utf8Value(prop);
928 auto it = fStateCallbacks.find(server);
929
930 if (it!=fStateCallbacks.end())
931 {
932 it->second.Dispose();
933 fStateCallbacks.erase(it);
934 }
935
936 if (value->IsFunction())
937 fStateCallbacks[server] = Persistent<Value>::New(value);
938
939 return Handle<Value>();
940}
941
942
943void InterpreterV8::JsHandleState(const std::string &server, const State &state)
944{
945 if (fThreadId<0)
946 return;
947
948 Locker locker;
949
950 auto it = fStateCallbacks.find(server);
951 if (it==fStateCallbacks.end())
952 {
953 it = fStateCallbacks.find("*");
954 if (it==fStateCallbacks.end())
955 return;
956 }
957
958 const HandleScope handle_scope;
959
960 if (it->second.IsEmpty() || !it->second->IsFunction())
961 return;
962
963 // -------------------------------------------------------------------
964 // We are not in a context... we need to get into one for Array::New
965
966 Persistent<Context> context = Context::New();
967 if (context.IsEmpty())
968 return;
969
970 const Context::Scope scope(context);
971
972 // -------------------------------------------------------------------
973
974 TryCatch exception;
975
976 // It is important to catch the exception thrown
977 // by Date::New in case of thread termination!
978 Local<Value> date = Date::New(state.time.JavaDate());
979
980 if (!exception.HasCaught())
981 {
982 Handle<ObjectTemplate> obj = ObjectTemplate::New();
983 obj->Set(String::New("index"), state.index<=-256?Undefined():Integer::New(state.index), ReadOnly);
984 obj->Set(String::New("name"), state.index<=-256?Undefined():String::New(state.name.c_str()), ReadOnly);
985 obj->Set(String::New("comment"), state.index<=-256?Undefined():String::New(state.comment.c_str()), ReadOnly);
986 obj->Set(String::New("server"), String::New(server.c_str()), ReadOnly);
987 if (state.index>-256)
988 obj->Set(String::New("time"), date);
989
990 Handle<Value> args[] = { obj->NewInstance() };
991
992 Handle<Function> fun = Handle<Function>::Cast(it->second);
993 fun->Call(fun, 1, args);
994 }
995
996 if (exception.HasCaught())
997 ReportException(&exception);
998
999 context.Dispose();
1000
1001 if (exception.HasCaught())
1002 V8::TerminateExecution(fThreadId);
1003}
1004
1005/*
1006void Cleanup( Persistent<Value> object, void *parameter )
1007{
1008 cout << "======================> RemoveMyObj()" << endl;
1009}*/
1010
1011Handle<Value> InterpreterV8::FuncSubscribe(const Arguments &args)
1012{
1013 if (args.Length()!=1)
1014 return ThrowException(String::New("Number of arguments must be exactly 1."));
1015
1016 if (!args[0]->IsString())
1017 return ThrowException(String::New("Argument 1 must be a string."));
1018
1019 //if (!args.IsConstructCall())
1020 // return ThrowException(String::New("Must be used as constructor."));
1021
1022 HandleScope handle_scope;
1023
1024 const String::Utf8Value str(args[0]);
1025
1026 void *ptr = JsSubscribe(*str);
1027 if (ptr==0)
1028 return ThrowException(String::New(("Subscription to '"+string(*str)+"' already exists.").c_str()));
1029
1030 Handle<ObjectTemplate> tem = ObjectTemplate::New();
1031 tem->Set(String::New("get"), FunctionTemplate::New(WrapGetData), ReadOnly);
1032 tem->Set(String::New("close"), FunctionTemplate::New(WrapClose), ReadOnly);
1033 tem->Set(String::New("name"), String::New(*str), ReadOnly);
1034 tem->Set(String::New("isOpen"), Boolean::New(true));
1035 tem->SetInternalFieldCount(1);
1036 //tem->Set(String::New("toString"), String::New(("[object Dim "+string(*str)+"]").c_str()), ReadOnly);
1037
1038 Handle<Object> obj = tem->NewInstance();
1039 obj->SetInternalField(0, External::New(ptr));
1040
1041 fReverseMap[*str] = Persistent<Object>::New(obj);
1042
1043 return handle_scope.Close(obj);
1044
1045 // Persistent<Object> p = Persistent<Object>::New(obj->NewInstance());
1046 // obj.MakeWeak((void*)1, Cleanup);
1047 // return obj;
1048}
1049
1050bool InterpreterV8::JsRun(const string &filename, const map<string, string> &map)
1051{
1052 Locker locker;
1053 fThreadId = V8::GetCurrentThreadId();
1054
1055 JsLoad(filename);
1056
1057 HandleScope handle_scope;
1058
1059 // Create a template for the global object.
1060 Handle<ObjectTemplate> dim = ObjectTemplate::New();
1061 dim->Set(String::New("print"), FunctionTemplate::New(WrapPrint), ReadOnly);
1062 dim->Set(String::New("alarm"), FunctionTemplate::New(WrapAlarm), ReadOnly);
1063 dim->Set(String::New("out"), FunctionTemplate::New(WrapOut), ReadOnly);
1064 dim->Set(String::New("wait"), FunctionTemplate::New(WrapWait), ReadOnly);
1065 dim->Set(String::New("send"), FunctionTemplate::New(WrapSend), ReadOnly);
1066 dim->Set(String::New("state"), FunctionTemplate::New(WrapState), ReadOnly);
1067 dim->Set(String::New("newState"), FunctionTemplate::New(WrapNewState), ReadOnly);
1068 dim->Set(String::New("setState"), FunctionTemplate::New(WrapSetState), ReadOnly);
1069 dim->Set(String::New("sleep"), FunctionTemplate::New(WrapSleep), ReadOnly);
1070 dim->Set(String::New("subscribe"), FunctionTemplate::New(WrapSubscribe), ReadOnly);
1071 dim->Set(String::New("database"), FunctionTemplate::New(WrapDatabase), ReadOnly);
1072
1073 Handle<ObjectTemplate> onchange = ObjectTemplate::New();
1074 onchange->SetNamedPropertyHandler(OnChangeGet, WrapOnChangeSet);
1075 dim->Set(v8::String::New("onchange"), onchange);
1076
1077 Handle<ObjectTemplate> global = ObjectTemplate::New();
1078 global->Set(String::New("dim"), dim, ReadOnly);
1079 global->Set(String::New("include"), FunctionTemplate::New(WrapInclude), ReadOnly);
1080 global->Set(String::New("exit"), FunctionTemplate::New(WrapExit), ReadOnly);
1081 global->Set(String::New("version"), FunctionTemplate::New(InterpreterV8::FuncVersion), ReadOnly);
1082
1083 // Persistent
1084 Persistent<Context> context = Context::New(NULL, global);
1085 if (context.IsEmpty())
1086 {
1087 //printf("Error creating context\n");
1088 return false;
1089 }
1090
1091 Context::Scope scope(context);
1092
1093 Handle<Array> args = Array::New(map.size());
1094 for (auto it=map.begin(); it!=map.end(); it++)
1095 args->Set(String::New(it->first.c_str()), String::New(it->second.c_str()));
1096 context->Global()->Set(String::New("$"), args, ReadOnly);
1097 context->Global()->Set(String::New("arg"), args, ReadOnly);
1098
1099 JsStart(filename);
1100
1101 //context->Enter();
1102 Locker::StartPreemption(10);
1103 const bool rc = ExecuteFile(filename);
1104
1105 // -----
1106 // This is how an exit handler could look like, but there is no way to interrupt it
1107 // -----
1108 // Handle<Object> obj = Handle<Object>::Cast(context->Global()->Get(String::New("dim")));
1109 // if (!obj.IsEmpty())
1110 // {
1111 // Handle<Value> onexit = obj->Get(String::New("onexit"));
1112 // if (!onexit->IsUndefined())
1113 // Handle<Function>::Cast(onexit)->NewInstance(0, NULL); // argc, argv
1114 // // Handle<Object> result = Handle<Function>::Cast(onexit)->NewInstance(0, NULL); // argc, argv
1115 // }
1116
1117 Locker::StopPreemption();
1118 //context->Exit();
1119
1120 for (auto it=fStateCallbacks.begin(); it!=fStateCallbacks.end(); it++)
1121 it->second.Dispose();
1122 fStateCallbacks.clear();
1123
1124 for (auto it=fReverseMap.begin(); it!=fReverseMap.end(); it++)
1125 it->second.Dispose();
1126 fReverseMap.clear();
1127
1128 context.Dispose();
1129
1130#ifdef HAVE_SQL
1131 for (auto it=fDatabases.begin(); it!=fDatabases.end(); it++)
1132 delete *it;
1133 fDatabases.clear();
1134#endif
1135
1136 JsEnd(filename);
1137
1138 return rc;
1139}
1140
1141void InterpreterV8::JsStop()
1142{
1143 Locker locker;
1144
1145 //cout << "Terminate " << fThreadId << endl;
1146 if (This->fThreadId>=0)
1147 V8::TerminateExecution(This->fThreadId);
1148 //cout << "Terminate " << fThreadId << endl;
1149
1150 //Unlocker unlocker;
1151}
1152
1153#endif
Note: See TracBrowser for help on using the repository browser.