source: trunk/FACT++/src/InterpreterV8-4.cc@ 20071

Last change on this file since 20071 was 20024, checked in by tbretz, 4 years ago
For the moment, threads are disabled.
File size: 116.8 KB
Line 
1#include "InterpreterV8.h"
2
3#ifdef HAVE_V8
4
5#include <fstream>
6#include <sstream>
7#include <iomanip>
8
9#include <sys/stat.h>
10
11#include <boost/tokenizer.hpp>
12#include <boost/filesystem.hpp>
13#include <boost/algorithm/string/join.hpp>
14
15#ifdef HAVE_NOVA
16#include "nova.h"
17#endif
18
19#ifdef HAVE_SQL
20#include "Database.h"
21#endif
22
23#include <v8.h>
24#include <libplatform/libplatform.h>
25
26#include "dim.h"
27#include "tools.h"
28#include "Readline.h"
29#include "izstream.h"
30
31#include "WindowLog.h"
32
33using namespace std;
34using namespace v8;
35
36v8::Handle<v8::FunctionTemplate> InterpreterV8::fTemplateLocal;
37v8::Handle<v8::FunctionTemplate> InterpreterV8::fTemplateSky;
38v8::Handle<v8::FunctionTemplate> InterpreterV8::fTemplateEvent;
39v8::Handle<v8::FunctionTemplate> InterpreterV8::fTemplateDescription;
40//v8::Handle<v8::FunctionTemplate> InterpreterV8::fTemplateDatabase;
41
42
43// ==========================================================================
44// Some documentation
45// ==========================================================================
46//
47// Threads:
48// --------
49// In most cases Js* and other calls to native C++ code could be wrapped
50// with an Unlocker to allow possible other JavaScipt 'threads' to run
51// during that time. However, all of these calls should take much less than
52// the preemption time of 10ms, so it would just be a waste of tim.
53//
54// Termination:
55// ------------
56// Each thread running V8 code needs to be signalled individually for
57// termination. Therefor a list of V8 thread ids is created.
58//
59// If termination has already be signalled, no thread should start running
60// anymore (thy could, e.g., wait for their locking). So after locking
61// it has to be checked if the thread was terminated already. Note
62// that all calls to Terminate() must be locked to ensure that fThreadId
63// is correct when it is checked.
64//
65// The current thread id must be added to fThreadIds _before_ any
66// function is called after Locking and before execution is given
67// back to JavaScript, e.g. in script->Run(). So until the thread
68// is added to the list Terminate will not be executed. If Terminate
69// is then executed, it is ensured that the current thread is
70// already in the list. If terminate has been called before
71// the Locking, the check for the validiy of fThreadId ensures that
72// nothing is executed.
73//
74// Empty handles:
75// --------------
76// If exceution is terminated, V8 calls might return with empty handles,
77// e.g. Date::New(isolate). Therefore, the returned handles of these calls have to
78// be checked in all placed to avoid that V8 will core dump.
79//
80// HandleScope:
81// ------------
82// A handle scope is a garbage collector and collects all handles created
83// until it goes out of scope. Handles which are not needed anymore are
84// then deleted. To return a handle from a HandleScope you need to use
85// Close(). E.g., String::Utf8Value does not create a new handle and
86// hence does not need a HandleScope. Any ::New will need a handle scope.
87// Forgetting the HandleScope could in principle fill your memory,
88// but everything is properly deleted by the global HandleScope at
89// script termination.
90//
91// Here is another good reference for v8, also containing some
92// good explanations for the meaning of handles, persistent handles
93// and weak handles: http://create.tpsitulsa.com/wiki/V8_Cookbook
94//
95// ==========================================================================
96// Simple interface
97// ==========================================================================
98
99typedef FunctionCallbackInfo<Value> Arguments;
100
101void InterpreterV8::FuncExit(const Arguments &)
102{
103 fMainThread->TerminateExecution();
104
105 // we have to throw an excption to make sure that the
106 // calling thread does not go on executing until it
107 // has realized that it should terminate
108
109 //*NEw* FIXME: Which Thread???
110 Isolate* isolate = Isolate::GetCurrent();
111 isolate->ThrowException(Null(isolate));
112}
113
114void InterpreterV8::FuncSleep(const Arguments& args)
115{
116 Isolate* isolate = Isolate::GetCurrent();
117
118 if (args.Length()==0)
119 {
120 // Theoretically, the CPU usage can be reduced by maybe a factor
121 // of four using a larger value, but this also means that the
122 // JavaScript is locked for a longer time.
123 const Unlocker unlock(isolate);
124 usleep(1000);
125 return;
126 }
127
128 if (args.Length()!=1)
129 {
130 isolate->ThrowException(String::NewFromUtf8(isolate, "Number of arguments must be exactly 1."));
131 return;
132 }
133
134 if (!args[0]->IsUint32())
135 {
136 isolate->ThrowException(String::NewFromUtf8(isolate, "Argument 1 must be an uint32."));
137 return;
138 }
139
140 // Using a Javascript function has the advantage that it is fully
141 // interruptable without the need of C++ code
142 const string code =
143 "(function(){"
144 "var t=new Date();"
145 "while ((new Date()-t)<"+to_string(args[0]->Int32Value())+") v8.sleep();"
146 "})();";
147
148 args.GetReturnValue().Set(ExecuteInternal(code));
149}
150
151void InterpreterV8::FuncTimeout(const Arguments &args)
152{
153 Isolate* isolate = Isolate::GetCurrent();
154
155 if (args.Length()<2)
156 {
157 isolate->ThrowException(String::NewFromUtf8(isolate, "Number of arguments must be at least two."));
158 return;
159 }
160
161 if (!args[0]->IsNull() && !args[0]->IsInt32())
162 {
163 isolate->ThrowException(String::NewFromUtf8(isolate, "Argument 0 not null and not an int32."));
164 return;
165 }
166
167 if (!args[1]->IsFunction())
168 {
169 isolate->ThrowException(String::NewFromUtf8(isolate, "Argument 1 not a function."));
170 return;
171 }
172
173 if (args.Length()>2 && !args[2]->IsObject())
174 {
175 isolate->ThrowException(String::NewFromUtf8(isolate, "Argument 2 not an object."));
176 return;
177 }
178
179 const int32_t timeout = args[0]->IsNull() ? 0 : args[0]->Int32Value();
180 const bool null = args[0]->IsNull();
181
182 HandleScope handle_scope(isolate);
183
184 Handle<Function> func = Handle<Function>::Cast(args[1]);
185
186 const int nn = args.Length()==2 ? 0 : args.Length()-3;
187
188 vector<Handle<Value>> argv(nn);
189 for (int i=0; i<nn; i++)
190 argv[i] = args[i+3];
191
192 Time t;
193 while (1)
194 {
195 const Handle<Value> rc = args.Length()<3 ?
196 func->Call(func, nn, argv.data()) :
197 func->Call(args[2]->ToObject(), nn, argv.data());
198
199 if (rc.IsEmpty())
200 return;
201
202 if (!rc->IsUndefined())
203 {
204 args.GetReturnValue().Set(rc);
205 return;
206 }
207
208 if (!null && Time()-t>=boost::posix_time::milliseconds(abs(timeout)))
209 break;
210
211 // Theoretically, the CPU usage can be reduced by maybe a factor
212 // of four using a larger value, but this also means that the
213 // JavaScript is locked for a longer time.
214 const Unlocker unlock(isolate);
215 usleep(1000);
216 }
217
218 if (timeout<0)
219 return;
220
221 isolate->ThrowException(String::NewFromUtf8(isolate, "Waiting for func to return a defined value timed out."));
222}
223
224void InterpreterV8::Thread(int &id, Persistent<Object> _this, Persistent<Function> func, uint32_t ms)
225{
226 /*
227 Isolate *isolate = Isolate::GetCurrent();
228
229 const Locker lock(isolate);
230
231 if (fThreadId<0)
232 {
233 id = -1;
234 return;
235 }
236
237 // Warning: As soon as id is set, the parent of this thread might terminate
238 // and hance the reference to id does not exist anymore. So, id
239 // is just a kind of return value and must not be used at all
240 // otherwise.
241
242 const int id_local = V8::GetCurrentThreadId();
243 id = id_local;
244 fThreadIds.insert(id_local);
245
246 const HandleScope handle_scope(isolate);
247
248 func->CreationContext()->Enter();
249
250 TryCatch exception;
251
252 const bool rc = ms==0 || !ExecuteInternal("v8.sleep("+to_string(ms)+");").IsEmpty();
253 if (rc)
254 {
255 if (_this.IsEmpty())
256 func->Call(func, 0, NULL);
257 else
258 func->Call(_this, 0, NULL);
259 }
260
261 func.Dispose();
262 _this.Dispose();
263
264 fThreadIds.erase(id_local);
265
266 if (!HandleException(exception, "thread"))
267 V8::TerminateExecution(isolate);
268
269 func->CreationContext()->Exit();
270 */
271}
272
273void InterpreterV8::FuncThread(const Arguments& args)
274{
275 Isolate *isolate = Isolate::GetCurrent();
276
277 if (!args.IsConstructCall())
278 {
279 isolate->ThrowException(String::NewFromUtf8(isolate, "Thread must be called as constructor."));
280 return;
281 }
282
283 if (args.Length()!=2 && args.Length()!=3)
284 {
285 isolate->ThrowException(String::NewFromUtf8(isolate, "Number of arguments must be two or three."));
286 return;
287 }
288
289
290 if (!args[0]->IsUint32())
291 {
292 isolate->ThrowException(String::NewFromUtf8(isolate, "Argument 0 not an uint32."));
293 return;
294 }
295
296
297 if (!args[1]->IsFunction())
298 {
299 isolate->ThrowException(String::NewFromUtf8(isolate, "Argument 1 not a function."));
300 return;
301 }
302
303 if (args.Length()==3 && !args[2]->IsObject())
304 {
305 isolate->ThrowException(String::NewFromUtf8(isolate, "Argument 2 not an object."));
306 return;
307 }
308
309 //*NEW or OLD?*
310 //if (!args.IsConstructCall())
311 // return Constructor(args);
312/*
313 const HandleScope handle_scope(isolate);
314
315 Handle<Function> handle = Handle<Function>::Cast(args[1]);
316
317 Persistent<Function> func = Persistent<Function>::New(isolate, handle);
318 Persistent<Object> _this;
319 if (args.Length()==3)
320 _this = Persistent<Object>::New(args[2]->ToObject());
321
322 const uint32_t ms = args[0]->Uint32Value();
323
324 int id=-2;
325 fThreads.push_back(thread(bind(&InterpreterV8::Thread, this, ref(id), _this, func, ms)));
326 {
327 // Allow the thread to lock, so we can get the thread id.
328 const Unlocker unlock(isolate);
329 while (id==-2)
330 usleep(1);
331 }
332*/
333// Handle<Object> self = args.This();
334// self->Set(String::NewFromUtf8(isolate, "id"), Integer::NewFromUnsigned(isolate, id)/*, ReadOnly*/);
335// self->Set(String::NewFromUtf8(isolate, "kill"), FunctionTemplate::New(isolate, WrapKill)->GetFunction()/*, ReadOnly*/);
336// args.GetReturnValue().Set(self);
337}
338
339void InterpreterV8::FuncKill(const Arguments& args)
340{
341 /*
342 Isolate *isolate = Isolate::GetCurrent();
343
344 const uint32_t id = args.This()->Get(String::NewFromUtf8(isolate, "id"))->Uint32Value();
345
346 V8::TerminateExecution(id);
347
348 // DISPOSE?
349
350 return Boolean::New(fThreadIds.erase(id));
351 */
352}
353
354void InterpreterV8::FuncSend(const Arguments& args)
355{
356 Isolate *isolate = Isolate::GetCurrent();
357
358 if (args.Length()==0)
359 {
360 isolate->ThrowException(String::NewFromUtf8(isolate, "Number of arguments must be at least 1."));
361 return;
362 }
363
364 if (!args[0]->IsString())
365 {
366 isolate->ThrowException(String::NewFromUtf8(isolate, "Argument 1 must be a string."));
367 return;
368 }
369
370 string command = *String::Utf8Value(args[0]);
371
372 if (command.length()==0)
373 {
374 isolate->ThrowException(String::NewFromUtf8(isolate, "Server name empty."));
375 return;
376 }
377
378 if (args.Length()==0)
379 {
380 if (command.find_first_of('/')==string::npos)
381 command += "/";
382 }
383
384 // Escape all string arguments. All others can be kept as they are.
385 for (int i=1; i<args.Length(); i++)
386 {
387 string arg = *String::Utf8Value(args[i]);
388
389 // Escape string
390 if (args[i]->IsString())
391 {
392 boost::replace_all(arg, "\\", "\\\\");
393 boost::replace_all(arg, "'", "\\'");
394 boost::replace_all(arg, "\"", "\\\"");
395 }
396
397 command += " "+arg;
398 }
399
400 try
401 {
402 args.GetReturnValue().Set(JsSend(command));
403 }
404 catch (const runtime_error &e)
405 {
406 isolate->ThrowException(String::NewFromUtf8(isolate, e.what()));
407 }
408}
409
410// ==========================================================================
411// State control
412// ==========================================================================
413
414void InterpreterV8::FuncWait(const Arguments& args)
415{
416 Isolate *isolate = Isolate::GetCurrent();
417
418 if (args.Length()!=2 && args.Length()!=3)
419 {
420 isolate->ThrowException(String::NewFromUtf8(isolate, "Number of arguments must be 2 or 3."));
421 return;
422 }
423
424 if (!args[0]->IsString())
425 {
426 isolate->ThrowException(String::NewFromUtf8(isolate, "Argument 1 not a string."));
427 return;
428 }
429
430 if (!args[1]->IsInt32() && !args[1]->IsString())
431 {
432 isolate->ThrowException(String::NewFromUtf8(isolate, "Argument 2 not an int32 and not a string."));
433 return;
434 }
435
436 if (args.Length()==3 && !args[2]->IsInt32() && !args[2]->IsUndefined())
437 {
438 isolate->ThrowException(String::NewFromUtf8(isolate, "Argument 3 not an int32 and not undefined."));
439 return;
440 }
441
442 // Using a Javascript function has the advantage that it is fully
443 // interruptable without the need of C++ code
444
445 const string index = args[1]->IsInt32() ? "s.index" : "s.name";
446 const bool timeout = args.Length()==3 && !args[2]->IsUndefined();
447 const string arg0 = *String::Utf8Value(args[0]);
448 const string state = args[1]->IsString() ? *String::Utf8Value(args[1]) : "";
449 const string arg1 = args[1]->IsString() ? ("\""+state+"\"") : to_string(args[1]->Int32Value());
450 const bool isNot = arg0[0]=='!';
451 const string name = isNot ? arg0.substr(1) : arg0;
452
453 if (arg0.find_first_of("\"'")!=string::npos)
454 {
455 isolate->ThrowException(String::NewFromUtf8(isolate, "Server name must not contain quotation marks."));
456 return;
457 }
458
459 if (args[1]->IsString())
460 if (state.find_first_of("\"'")!=string::npos)
461 {
462 isolate->ThrowException(String::NewFromUtf8(isolate, "State name must not contain quotation marks."));
463 return;
464 }
465
466 string code = "(function(name,state,ms)"
467 "{";
468 if (timeout)
469 code += "var t = new Date();";
470 code += "var s;"
471 "while (1)"
472 "{"
473 "s = dim.state(name);"
474 "if(!s)throw new Error('Waiting for state "+arg1+" of server "+arg0+" failed.');";
475 if (isNot)
476 code +=
477 "if(state!="+index+")return true;";
478 else
479 code +=
480 "if(state=="+index+")return true;";
481 if (timeout)
482 code += "if((new Date()-t)>Math.abs(ms))break;";
483
484 code += "v8.sleep();"
485 "}";
486 if (timeout)
487 code += "if(ms>0)throw new Error('Waiting for state "+arg1+" of server "+arg0+" ['+"+index+"+'] timed out.');";
488 code += "return false;"
489 "})('"+name+"',"+arg1;
490 if (timeout)
491 code += "," + (args[2]->IsUndefined()?"undefined":to_string(args[2]->Int32Value()));
492 code += ");";
493
494 args.GetReturnValue().Set(ExecuteInternal(code));
495}
496
497void InterpreterV8::FuncState(const Arguments& args)
498{
499 Isolate *isolate = Isolate::GetCurrent();
500
501 if (args.Length()!=1)
502 {
503 isolate->ThrowException(String::NewFromUtf8(isolate, "Number of arguments must be exactly 1."));
504 return;
505 }
506
507 if (!args[0]->IsString())
508 {
509 isolate->ThrowException(String::NewFromUtf8(isolate, "Argument 1 must be a string."));
510 return;
511 }
512
513 // Return state.name/state.index
514
515 const String::Utf8Value str(args[0]);
516
517 const State rc = JsState(*str);
518 if (rc.index<=-256)
519 return;
520
521 const HandleScope handle_scope(isolate);
522
523 Handle<ObjectTemplate> obj = ObjectTemplate::New(isolate);
524 if (obj.IsEmpty())
525 return;
526
527 obj->Set(String::NewFromUtf8(isolate, "server"), String::NewFromUtf8(isolate, *str), ReadOnly);
528 obj->Set(String::NewFromUtf8(isolate, "index"), Integer::New(isolate, rc.index), ReadOnly);
529 obj->Set(String::NewFromUtf8(isolate, "name"), String::NewFromUtf8(isolate, rc.name.c_str()), ReadOnly);
530
531 MaybeLocal<Value> date = Date::New(isolate->GetCurrentContext(), rc.time.JavaDate());
532 if (rc.index>-256 && !date.IsEmpty())
533 obj->Set(String::NewFromUtf8(isolate, "time"), date.ToLocalChecked());
534
535 args.GetReturnValue().Set(obj->NewInstance());
536}
537
538void InterpreterV8::FuncNewState(const Arguments& args)
539{
540 Isolate *isolate = Isolate::GetCurrent();
541
542 if (args.Length()<1 || args.Length()>3)
543 {
544 isolate->ThrowException(String::NewFromUtf8(isolate, "Number of arguments must be 1, 2 or 3."));
545 return;
546 }
547
548 if (!args[0]->IsUint32())
549 {
550 isolate->ThrowException(String::NewFromUtf8(isolate, "Argument 1 must be an uint32."));
551 return;
552 }
553 if (args.Length()>1 && !args[1]->IsString())
554 {
555 isolate->ThrowException(String::NewFromUtf8(isolate, "Argument 2 must be a string."));
556 return;
557 }
558 if (args.Length()>2 && !args[2]->IsString())
559 {
560 isolate->ThrowException(String::NewFromUtf8(isolate, "Argument 3 must be a string."));
561 return;
562 }
563
564 const uint32_t index = args[0]->Int32Value();
565 const string name = *String::Utf8Value(args[1]);
566 const string comment = *String::Utf8Value(args[2]);
567
568 if (index<10 || index>255)
569 {
570 isolate->ThrowException(String::NewFromUtf8(isolate, "State must be in the range [10, 255]."));
571 return;
572 }
573
574 if (name.empty())
575 {
576 isolate->ThrowException(String::NewFromUtf8(isolate, "State name must not be empty."));
577 return;
578 }
579
580 if (name.find_first_of(':')!=string::npos || name.find_first_of('=')!=string::npos)
581 {
582 isolate->ThrowException(String::NewFromUtf8(isolate, "State name must not contain : or =."));
583 return;
584 }
585
586 struct Find : State
587 {
588 Find(int idx, const string &n) : State(idx, n) { }
589 bool operator()(const pair<int, string> &p) { return index==p.first || name==p.second; }
590 };
591
592 if (find_if(fStates.begin(), fStates.end(), Find(index, name))!=fStates.end())
593 {
594 const string what =
595 "State index ["+to_string(index)+"] or name ["+name+"] already defined.";
596
597 isolate->ThrowException(String::NewFromUtf8(isolate, what.c_str()));
598 return;
599 }
600
601 args.GetReturnValue().Set(JsNewState(index, name, comment));
602}
603
604void InterpreterV8::FuncSetState(const Arguments& args)
605{
606 Isolate *isolate = Isolate::GetCurrent();
607
608 if (args.Length()!=1)
609 {
610 isolate->ThrowException(String::NewFromUtf8(isolate, "Number of arguments must be exactly 1."));
611 return;
612 }
613
614 if (!args[0]->IsUint32() && !args[0]->IsString())
615 {
616 isolate->ThrowException(String::NewFromUtf8(isolate, "Argument must be an uint32 or a string."));
617 return;
618 }
619
620 int index = -2;
621 if (args[0]->IsUint32())
622 {
623 index = args[0]->Int32Value();
624 }
625 else
626 {
627 const string name = *String::Utf8Value(args[0]);
628 index = JsGetState(name);
629 if (index==-2)
630 {
631 isolate->ThrowException(String::NewFromUtf8(isolate, ("State '"+name+"' not found.").c_str()));
632 return;
633 }
634 }
635
636 if (index<10 || index>255)
637 {
638 isolate->ThrowException(String::NewFromUtf8(isolate, "State must be in the range [10, 255]."));
639 return;
640 }
641
642 args.GetReturnValue().Set(JsSetState(index));
643}
644
645void InterpreterV8::FuncGetState(const Arguments& args)
646{
647 Isolate *isolate = Isolate::GetCurrent();
648
649 if (args.Length()>0)
650 {
651 isolate->ThrowException(String::NewFromUtf8(isolate, "getState must not take arguments."));
652 return;
653 }
654
655 const State state = JsGetCurrentState();
656
657 const HandleScope handle_scope(isolate);
658
659 Handle<ObjectTemplate> obj = ObjectTemplate::New(isolate);
660 if (obj.IsEmpty())
661 return;
662
663 obj->Set(String::NewFromUtf8(isolate, "index"), Integer::New(isolate, state.index), ReadOnly);
664 obj->Set(String::NewFromUtf8(isolate, "name"), String::NewFromUtf8(isolate, state.name.c_str()), ReadOnly);
665 obj->Set(String::NewFromUtf8(isolate, "description"), String::NewFromUtf8(isolate, state.comment.c_str()), ReadOnly);
666
667 args.GetReturnValue().Set(obj->NewInstance());
668}
669
670void InterpreterV8::FuncGetStates(const Arguments& args)
671{
672 Isolate *isolate = Isolate::GetCurrent();
673
674 if (args.Length()>1)
675 {
676 isolate->ThrowException(String::NewFromUtf8(isolate, "getStates must not take more than one arguments."));
677 return;
678 }
679
680 if (args.Length()==1 && !args[0]->IsString())
681 {
682 isolate->ThrowException(String::NewFromUtf8(isolate, "Argument must be a string."));
683 return;
684 }
685
686 const string server = args.Length()==1 ? *String::Utf8Value(args[0]) : "DIM_CONTROL";
687
688 const vector<State> states = JsGetStates(server);
689
690 const HandleScope handle_scope(isolate);
691
692 Handle<Object> obj = Object::New(isolate);
693 if (obj.IsEmpty())
694 return;
695
696 for (auto it=states.begin(); it!=states.end(); it++)
697 {
698 Handle<Value> entry = StringObject::New(String::NewFromUtf8(isolate, it->name.c_str()));
699 if (entry.IsEmpty())
700 return;
701
702 StringObject::Cast(*entry)->Set(String::NewFromUtf8(isolate, "description"), String::NewFromUtf8(isolate, it->comment.c_str())/*, ReadOnly*/);
703 obj->Set(Integer::New(isolate, it->index), entry/*, ReadOnly*/);
704 }
705
706 args.GetReturnValue().Set(obj);
707}
708
709void InterpreterV8::FuncGetDescription(const Arguments& args)
710{
711 Isolate *isolate = Isolate::GetCurrent();
712
713 if (args.Length()!=1)
714 {
715 isolate->ThrowException(String::NewFromUtf8(isolate, "getDescription must take exactly one argument."));
716 return;
717 }
718
719 if (args.Length()==1 && !args[0]->IsString())
720 {
721 isolate->ThrowException(String::NewFromUtf8(isolate, "Argument must be a string."));
722 return;
723 }
724
725 const string service = *String::Utf8Value(args[0]);
726
727 const vector<Description> descriptions = JsGetDescription(service);
728 const set<Service> services = JsGetServices();
729
730 auto is=services.begin();
731 for (; is!=services.end(); is++)
732 if (is->name==service)
733 break;
734
735 if (is==services.end())
736 return;
737
738 const HandleScope handle_scope(isolate);
739
740 Local<Context> context = isolate->GetCurrentContext();
741 MaybeLocal<Object> marr = fTemplateDescription->GetFunction()->NewInstance(context);
742 if (marr.IsEmpty())
743 return;
744
745 Local<Object> arr = marr.ToLocalChecked();
746
747 auto it=descriptions.begin();
748 arr->Set(String::NewFromUtf8(isolate, "name"), String::NewFromUtf8(isolate, it->name.c_str())/*, ReadOnly*/);
749 if (!it->comment.empty())
750 arr->Set(String::NewFromUtf8(isolate, "description"), String::NewFromUtf8(isolate, it->comment.c_str())/*, ReadOnly*/);
751 if (is!=services.end())
752 {
753 arr->Set(String::NewFromUtf8(isolate, "server"), String::NewFromUtf8(isolate, is->server.c_str())/*, ReadOnly*/);
754 arr->Set(String::NewFromUtf8(isolate, "service"), String::NewFromUtf8(isolate, is->service.c_str())/*, ReadOnly*/);
755 arr->Set(String::NewFromUtf8(isolate, "isCommand"), Boolean::New(isolate, is->iscmd)/*, ReadOnly*/);
756 if (!is->format.empty())
757 arr->Set(String::NewFromUtf8(isolate, "format"), String::NewFromUtf8(isolate, is->format.c_str())/*, ReadOnly*/);
758 }
759
760 uint32_t i=0;
761 for (it++; it!=descriptions.end(); it++)
762 {
763 Handle<Object> obj = Object::New(isolate);
764 if (obj.IsEmpty())
765 return;
766
767 if (!it->name.empty())
768 obj->Set(String::NewFromUtf8(isolate, "name"), String::NewFromUtf8(isolate, it->name.c_str())/*, ReadOnly*/);
769 if (!it->comment.empty())
770 obj->Set(String::NewFromUtf8(isolate, "description"), String::NewFromUtf8(isolate, it->comment.c_str())/*, ReadOnly*/);
771 if (!it->unit.empty())
772 obj->Set(String::NewFromUtf8(isolate, "unit"), String::NewFromUtf8(isolate, it->unit.c_str())/*, ReadOnly*/);
773
774 arr->Set(i++, obj);
775 }
776
777 args.GetReturnValue().Set(arr);
778}
779
780void InterpreterV8::FuncGetServices(const Arguments& args)
781{
782 Isolate *isolate = Isolate::GetCurrent();
783
784 if (args.Length()>2)
785 {
786 isolate->ThrowException(String::NewFromUtf8(isolate, "getServices must not take more than two argument."));
787 return;
788 }
789
790 if (args.Length()>=1 && !args[0]->IsString())
791 {
792 isolate->ThrowException(String::NewFromUtf8(isolate, "First argument must be a string."));
793 return;
794 }
795
796 if (args.Length()==2 && !args[1]->IsBoolean())
797 {
798 isolate->ThrowException(String::NewFromUtf8(isolate, "Second argument must be a boolean."));
799 return;
800 }
801
802 string arg0 = args.Length() ? *String::Utf8Value(args[0]) : "";
803 if (arg0=="*")
804 arg0="";
805
806 const set<Service> services = JsGetServices();
807
808 const HandleScope handle_scope(isolate);
809
810 Handle<Array> arr = Array::New(isolate);
811 if (arr.IsEmpty())
812 return;
813
814 uint32_t i=0;
815 for (auto is=services.begin(); is!=services.end(); is++)
816 {
817 if (!arg0.empty() && is->name.find(arg0)!=0)
818 continue;
819
820 if (args.Length()==2 && args[1]->BooleanValue()!=is->iscmd)
821 continue;
822
823 Handle<Object> obj = Object::New(isolate);
824 if (obj.IsEmpty())
825 return;
826
827 obj->Set(String::NewFromUtf8(isolate, "name"), String::NewFromUtf8(isolate, is->name.c_str())/*, ReadOnly*/);
828 obj->Set(String::NewFromUtf8(isolate, "server"), String::NewFromUtf8(isolate, is->server.c_str())/*, ReadOnly*/);
829 obj->Set(String::NewFromUtf8(isolate, "service"), String::NewFromUtf8(isolate, is->service.c_str())/*, ReadOnly*/);
830 obj->Set(String::NewFromUtf8(isolate, "isCommand"), Boolean::New(isolate, is->iscmd)/*, ReadOnly*/);
831 if (!is->format.empty())
832 obj->Set(String::NewFromUtf8(isolate, "format"), String::NewFromUtf8(isolate, is->format.c_str())/*, ReadOnly*/);
833
834 arr->Set(i++, obj);
835 }
836
837 args.GetReturnValue().Set(arr);
838}
839
840// ==========================================================================
841// Internal functions
842// ==========================================================================
843
844
845// The callback that is invoked by v8 whenever the JavaScript 'print'
846// function is called. Prints its arguments on stdout separated by
847// spaces and ending with a newline.
848void InterpreterV8::FuncLog(const Arguments& args)
849{
850 for (int i=0; i<args.Length(); i++)
851 {
852 const String::Utf8Value str(args[i]);
853 if (*str)
854 JsPrint(*str);
855 }
856
857 if (args.Length()==0)
858 JsPrint();
859}
860
861void InterpreterV8::FuncAlarm(const Arguments& args)
862{
863 for (int i=0; i<args.Length(); i++)
864 {
865 const String::Utf8Value str(args[i]);
866 if (*str)
867 JsAlarm(*str);
868 }
869
870 if (args.Length()==0)
871 JsAlarm();
872}
873
874void InterpreterV8::FuncOut(const Arguments& args)
875{
876 for (int i=0; i<args.Length(); i++)
877 {
878 const String::Utf8Value str(args[i]);
879 if (*str)
880 JsOut(*str);
881 }
882}
883
884void InterpreterV8::FuncWarn(const Arguments& args)
885{
886 for (int i=0; i<args.Length(); i++)
887 {
888 const String::Utf8Value str(args[i]);
889 if (*str)
890 JsWarn(*str);
891 }
892}
893
894// The callback that is invoked by v8 whenever the JavaScript 'load'
895// function is called. Loads, compiles and executes its argument
896// JavaScript file.
897void InterpreterV8::FuncInclude(const Arguments& args)
898{
899 Isolate *isolate = Isolate::GetCurrent();
900
901 if (args.Length()!=1)
902 {
903 isolate->ThrowException(String::NewFromUtf8(isolate, "Number of arguments must be one."));
904 return;
905 }
906 if (!args[0]->IsString())
907 {
908 isolate->ThrowException(String::NewFromUtf8(isolate, "Argument must be a string."));
909 return;
910 }
911
912 const String::Utf8Value file(args[0]);
913 if (*file == NULL)
914 {
915 isolate->ThrowException(String::NewFromUtf8(isolate, "File name missing."));
916 return;
917 }
918
919 if (strlen(*file)==0)
920 {
921 isolate->ThrowException(String::NewFromUtf8(isolate, "File name empty."));
922 return;
923 }
924
925 const auto path = boost::filesystem::path(*file);
926
927 const auto f = path.is_absolute() ? path : boost::filesystem::path(fIncludePath)/path;
928
929 izstream fin(*file);//f.string().c_str());
930 if (!fin)
931 {
932 isolate->ThrowException(String::NewFromUtf8(isolate, errno!=0?strerror(errno):"Insufficient memory for decompression"));
933 return;
934 }
935
936 string buffer;
937 getline(fin, buffer, '\0');
938
939 if ((fin.fail() && !fin.eof()) || fin.bad())
940 {
941 isolate->ThrowException(String::NewFromUtf8(isolate, strerror(errno)));
942 return;
943 }
944
945 if (buffer.length()>1 && buffer[0]=='#' && buffer[1]=='!')
946 buffer.insert(0, "//");
947
948 args.GetReturnValue().Set(ExecuteCode(buffer, *file));
949}
950
951void InterpreterV8::FuncFile(const Arguments& args)
952{
953 Isolate *isolate = Isolate::GetCurrent();
954
955 if (args.Length()!=1 && args.Length()!=2)
956 {
957 isolate->ThrowException(String::NewFromUtf8(isolate, "Number of arguments must be one or two."));
958 return;
959 }
960
961 const String::Utf8Value file(args[0]);
962 if (*file == NULL)
963 {
964 isolate->ThrowException(String::NewFromUtf8(isolate, "File name missing"));
965 return;
966 }
967
968 if (args.Length()==2 && !args[1]->IsString())
969 {
970 isolate->ThrowException(String::NewFromUtf8(isolate, "Second argument must be a string."));
971 return;
972 }
973
974 const string delim = args.Length()==2 ? *String::Utf8Value(args[1]) : "";
975
976 if (args.Length()==2 && delim.size()!=1)
977 {
978 isolate->ThrowException(String::NewFromUtf8(isolate, "Second argument must be a string of length 1."));
979 return;
980 }
981
982 const HandleScope handle_scope(isolate);
983
984 const auto path = boost::filesystem::path(*file);
985
986 const auto f = path.is_absolute() ? path : boost::filesystem::path(fIncludePath)/path;
987
988 izstream fin(*file);//f.string().c_str());
989 if (!fin)
990 {
991 isolate->ThrowException(String::NewFromUtf8(isolate, errno!=0?strerror(errno):"Insufficient memory for decompression"));
992 return;
993 }
994
995 if (args.Length()==1)
996 {
997 string buffer;
998 getline(fin, buffer, '\0');
999 if ((fin.fail() && !fin.eof()) || fin.bad())
1000 {
1001 isolate->ThrowException(String::NewFromUtf8(isolate, strerror(errno)));
1002 return;
1003 }
1004
1005 Handle<Value> str = StringObject::New(String::NewFromUtf8(isolate, buffer.c_str()));
1006 StringObject::Cast(*str)->Set(String::NewFromUtf8(isolate, "name"), String::NewFromUtf8(isolate, *file));
1007 args.GetReturnValue().Set(str);
1008 return;
1009 }
1010
1011 Handle<Array> arr = Array::New(isolate);
1012 if (arr.IsEmpty())
1013 return;
1014
1015 int i=0;
1016 string buffer;
1017 while (getline(fin, buffer, delim[0]))
1018 arr->Set(i++, String::NewFromUtf8(isolate, buffer.c_str()));
1019
1020 if ((fin.fail() && !fin.eof()) || fin.bad())
1021 {
1022 isolate->ThrowException(String::NewFromUtf8(isolate, strerror(errno)));
1023 return;
1024 }
1025
1026 arr->Set(String::NewFromUtf8(isolate, "name"), String::NewFromUtf8(isolate, *file));
1027 arr->Set(String::NewFromUtf8(isolate, "delim"), String::NewFromUtf8(isolate, delim.c_str(), NewStringType::kNormal, 1).ToLocalChecked());
1028
1029 args.GetReturnValue().Set(arr);
1030}
1031
1032// ==========================================================================
1033// Mail
1034// ==========================================================================
1035
1036void InterpreterV8::ConstructorMail(const Arguments &args)
1037{
1038 Isolate *isolate = Isolate::GetCurrent();
1039
1040 if (!args.IsConstructCall())
1041 {
1042 isolate->ThrowException(String::NewFromUtf8(isolate, "Mail must be called as constructor"));
1043 return;
1044 }
1045
1046 if (args.Length()!=1 || !args[0]->IsString())
1047 {
1048 isolate->ThrowException(String::NewFromUtf8(isolate, "Constructor must be called with a single string as argument"));
1049 return;
1050 }
1051
1052 const HandleScope handle_scope(isolate);
1053
1054 Handle<Array> rec = Array::New(isolate);
1055 Handle<Array> att = Array::New(isolate);
1056 Handle<Array> bcc = Array::New(isolate);
1057 Handle<Array> cc = Array::New(isolate);
1058 Handle<Array> txt = Array::New(isolate);
1059 if (rec.IsEmpty() || att.IsEmpty() || bcc.IsEmpty() || cc.IsEmpty() || txt.IsEmpty())
1060 return;
1061
1062 Handle<Object> self = args.This();
1063
1064 self->Set(String::NewFromUtf8(isolate, "subject"), args[0]->ToString()/*, ReadOnly*/);
1065 self->Set(String::NewFromUtf8(isolate, "recipients"), rec/*, ReadOnly*/);
1066 self->Set(String::NewFromUtf8(isolate, "attachments"), att/*, ReadOnly*/);
1067 self->Set(String::NewFromUtf8(isolate, "bcc"), bcc/*, ReadOnly*/);
1068 self->Set(String::NewFromUtf8(isolate, "cc"), cc/*, ReadOnly*/);
1069 self->Set(String::NewFromUtf8(isolate, "text"), txt/*, ReadOnly*/);
1070
1071 self->Set(String::NewFromUtf8(isolate, "send"), FunctionTemplate::New(isolate, WrapSendMail)->GetFunction()/*, ReadOnly*/);
1072
1073 args.GetReturnValue().Set(self);
1074}
1075
1076vector<string> InterpreterV8::ValueToArray(const Handle<Value> &val, bool only)
1077{
1078 vector<string> rc;
1079
1080 Handle<Array> arr = Handle<Array>::Cast(val);
1081 for (uint32_t i=0; i<arr->Length(); i++)
1082 {
1083 Handle<Value> obj = arr->Get(i);
1084 if (obj.IsEmpty())
1085 continue;
1086
1087 if (obj->IsNull() || obj->IsUndefined())
1088 continue;
1089
1090 if (only && !obj->IsString())
1091 continue;
1092
1093 rc.push_back(*String::Utf8Value(obj->ToString()));
1094 }
1095
1096 return rc;
1097}
1098
1099void InterpreterV8::FuncSendMail(const Arguments& args)
1100{
1101 Isolate *isolate = Isolate::GetCurrent();
1102
1103 const HandleScope handle_scope(isolate);
1104
1105 if (args.Length()>1)
1106 {
1107 isolate->ThrowException(String::NewFromUtf8(isolate, "Only one argument allowed."));
1108 return;
1109 }
1110
1111 if (args.Length()==1 && !args[0]->IsBoolean())
1112 {
1113 isolate->ThrowException(String::NewFromUtf8(isolate, "Argument must be a boolean."));
1114 return;
1115 }
1116
1117 const bool block = args.Length()==0 || args[0]->BooleanValue();
1118
1119 const Handle<Value> sub = args.This()->Get(String::NewFromUtf8(isolate, "subject"));
1120 const Handle<Value> rec = args.This()->Get(String::NewFromUtf8(isolate, "recipients"));
1121 const Handle<Value> txt = args.This()->Get(String::NewFromUtf8(isolate, "text"));
1122 const Handle<Value> att = args.This()->Get(String::NewFromUtf8(isolate, "attachments"));
1123 const Handle<Value> bcc = args.This()->Get(String::NewFromUtf8(isolate, "bcc"));
1124 const Handle<Value> cc = args.This()->Get(String::NewFromUtf8(isolate, "cc"));
1125
1126 const vector<string> vrec = ValueToArray(rec);
1127 const vector<string> vtxt = ValueToArray(txt, false);
1128 const vector<string> vatt = ValueToArray(att);
1129 const vector<string> vbcc = ValueToArray(bcc);
1130 const vector<string> vcc = ValueToArray(cc);
1131
1132 if (vrec.size()==0)
1133 {
1134 isolate->ThrowException(String::NewFromUtf8(isolate, "At least one valid string is required in 'recipients'."));
1135 return;
1136 }
1137 if (vtxt.size()==0)
1138 {
1139 isolate->ThrowException(String::NewFromUtf8(isolate, "At least one valid string is required in 'text'."));
1140 return;
1141 }
1142
1143 const string subject = *String::Utf8Value(sub->ToString());
1144
1145 FILE *pipe = popen(("from=no-reply@fact-project.org mailx -~ "+vrec[0]).c_str(), "w");
1146 if (!pipe)
1147 {
1148 isolate->ThrowException(String::NewFromUtf8(isolate, strerror(errno)));
1149 return;
1150 }
1151
1152
1153 fprintf(pipe, "%s", ("~s"+subject+"\n").c_str());
1154 for (auto it=vrec.begin()+1; it<vrec.end(); it++)
1155 fprintf(pipe, "%s", ("~t"+*it+"\n").c_str());
1156 for (auto it=vbcc.begin(); it<vbcc.end(); it++)
1157 fprintf(pipe, "%s", ("~b"+*it+"\n").c_str());
1158 for (auto it=vcc.begin(); it<vcc.end(); it++)
1159 fprintf(pipe, "%s", ("~c"+*it+"\n").c_str());
1160 for (auto it=vatt.begin(); it<vatt.end(); it++)
1161 fprintf(pipe, "%s", ("~@"+*it+"\n").c_str()); // Must not contain white spaces
1162
1163 for (auto it=vtxt.begin(); it<vtxt.end(); it++)
1164 fwrite((*it+"\n").c_str(), it->length()+1, 1, pipe);
1165
1166 fprintf(pipe, "\n---\nsent by dimctrl");
1167
1168 if (!block)
1169 return;
1170
1171 const int rc = pclose(pipe);
1172
1173 const Locker lock(isolate);
1174 args.GetReturnValue().Set(WEXITSTATUS(rc));
1175}
1176
1177// =========================================================================
1178// Curl
1179// ==========================================================================
1180
1181void InterpreterV8::ConstructorCurl(const Arguments &args)
1182{
1183 Isolate *isolate = Isolate::GetCurrent();
1184
1185 if (!args.IsConstructCall())
1186 {
1187 isolate->ThrowException(String::NewFromUtf8(isolate, "Curl must be called as constructor"));
1188 return;
1189 }
1190
1191 if (args.Length()!=1 || !args[0]->IsString())
1192 {
1193 isolate->ThrowException(String::NewFromUtf8(isolate, "Constructor must be called with a single string as argument"));
1194 return;
1195 }
1196
1197 const HandleScope handle_scope(isolate);
1198
1199 Handle<Array> data = Array::New(isolate);
1200 if (data.IsEmpty())
1201 return;
1202
1203 Handle<Object> self = args.This();
1204
1205 self->Set(String::NewFromUtf8(isolate, "url"), args[0]->ToString()/*, ReadOnly*/);
1206 self->Set(String::NewFromUtf8(isolate, "data"), data/*, ReadOnly*/);
1207
1208 self->Set(String::NewFromUtf8(isolate, "send"), FunctionTemplate::New(isolate, WrapSendCurl)->GetFunction()/*, ReadOnly*/);
1209
1210 args.GetReturnValue().Set(self);
1211}
1212
1213void InterpreterV8::FuncSendCurl(const Arguments& args)
1214{
1215 Isolate *isolate = Isolate::GetCurrent();
1216
1217 if (args.Length()>1)
1218 {
1219 isolate->ThrowException(String::NewFromUtf8(isolate, "Only one argument allowed."));
1220 return;
1221 }
1222
1223 if (args.Length()==1 && !args[0]->IsBoolean())
1224 {
1225 isolate->ThrowException(String::NewFromUtf8(isolate, "Argument must be a boolean."));
1226 return;
1227 }
1228
1229 const HandleScope handle_scope(isolate);
1230
1231 const bool block = args.Length()==0 || args[0]->BooleanValue();
1232
1233 const Handle<Value> url = args.This()->Get(String::NewFromUtf8(isolate, "url"));
1234 const Handle<Value> data = args.This()->Get(String::NewFromUtf8(isolate, "data"));
1235
1236 const vector<string> vdata = ValueToArray(data);
1237 const string sdata = boost::algorithm::join(vdata, "&");
1238
1239 const string surl = *String::Utf8Value(url->ToString());
1240
1241 string cmd = "curl -sSf ";
1242 if (!sdata.empty())
1243 cmd += "--data '"+sdata+"' ";
1244 cmd += "'http://"+surl+"' 2>&1 ";
1245
1246 FILE *pipe = popen(cmd.c_str(), "r");
1247 if (!pipe)
1248 {
1249 isolate->ThrowException(String::NewFromUtf8(isolate, strerror(errno)));
1250 return;
1251 }
1252
1253 if (!block)
1254 return;
1255
1256 string txt;
1257
1258 while (!feof(pipe))
1259 {
1260 char buf[1025];
1261 if (fgets(buf, 1024, pipe)==NULL)
1262 break;
1263 txt += buf;
1264 }
1265
1266 const int rc = pclose(pipe);
1267
1268 Handle<Object> obj = Object::New(isolate);
1269
1270 obj->Set(String::NewFromUtf8(isolate, "cmd"), String::NewFromUtf8(isolate, cmd.c_str()));
1271 obj->Set(String::NewFromUtf8(isolate, "data"), String::NewFromUtf8(isolate, txt.c_str()));
1272 obj->Set(String::NewFromUtf8(isolate, "rc"), Integer::NewFromUnsigned(isolate, WEXITSTATUS(rc)));
1273
1274 const Locker lock(isolate);
1275 args.GetReturnValue().Set(obj);
1276}
1277
1278// ==========================================================================
1279// Database
1280// ==========================================================================
1281
1282void InterpreterV8::FuncDbClose(const Arguments &args)
1283{
1284 Isolate* isolate = Isolate::GetCurrent();
1285
1286 void *ptr = args.This()->GetInternalField(0).As<v8::External>()->Value();
1287 if (!ptr)
1288 {
1289 args.GetReturnValue().Set(false);
1290 return;
1291 }
1292
1293#ifdef HAVE_SQL
1294 Database *db = reinterpret_cast<Database*>(ptr);
1295 auto it = find(fDatabases.begin(), fDatabases.end(), db);
1296 fDatabases.erase(it);
1297 delete db;
1298#endif
1299
1300 const HandleScope handle_scope(isolate);
1301
1302 args.This()->SetInternalField(0, External::New(isolate, 0));
1303
1304 args.GetReturnValue().Set(true);
1305}
1306
1307void InterpreterV8::FuncDbQuery(const Arguments &args)
1308{
1309 Isolate* isolate = Isolate::GetCurrent();
1310
1311 if (args.Length()==0)
1312 {
1313 isolate->ThrowException(String::NewFromUtf8(isolate, "Arguments expected."));
1314 return;
1315 }
1316
1317 void *ptr = args.This()->GetInternalField(0).As<v8::External>()->Value();
1318 if (!ptr)
1319 return;
1320
1321 string query;
1322 for (int i=0; i<args.Length(); i++)
1323 query += string(" ") + *String::Utf8Value(args[i]);
1324 query.erase(0, 1);
1325
1326#ifdef HAVE_SQL
1327 try
1328 {
1329 const HandleScope handle_scope(isolate);
1330
1331 Database *db = reinterpret_cast<Database*>(ptr);
1332
1333 const mysqlpp::StoreQueryResult res = db->query(query).store();
1334
1335 Handle<Array> ret = Array::New(isolate);
1336 if (ret.IsEmpty())
1337 return;
1338
1339 ret->Set(String::NewFromUtf8(isolate, "table"), String::NewFromUtf8(isolate, res.table())/*, ReadOnly*/);
1340 ret->Set(String::NewFromUtf8(isolate, "query"), String::NewFromUtf8(isolate, query.c_str())/*, ReadOnly*/);
1341
1342 Handle<Array> cols = Array::New(isolate);
1343 if (cols.IsEmpty())
1344 return;
1345
1346 int irow=0;
1347 for (vector<mysqlpp::Row>::const_iterator it=res.begin(); it<res.end(); it++)
1348 {
1349 Handle<Object> row = Object::New(isolate);
1350 if (row.IsEmpty())
1351 return;
1352
1353 const mysqlpp::FieldNames *list = it->field_list().list;
1354
1355 for (size_t i=0; i<it->size(); i++)
1356 {
1357 const Handle<Value> name = String::NewFromUtf8(isolate, (*list)[i].c_str());
1358 if (irow==0)
1359 cols->Set(i, name);
1360
1361 if ((*it)[i].is_null())
1362 {
1363 row->Set(name, Undefined(isolate)/*, ReadOnly*/);
1364 continue;
1365 }
1366
1367 const string sql_type = (*it)[i].type().sql_name();
1368
1369 const bool uns = sql_type.find("UNSIGNED")==string::npos;
1370
1371 if (sql_type.find("BIGINT")!=string::npos)
1372 {
1373 if (uns)
1374 {
1375 const uint64_t val = (uint64_t)(*it)[i];
1376 if (val>UINT32_MAX)
1377 row->Set(name, Number::New(isolate, val)/*, ReadOnly*/);
1378 else
1379 row->Set(name, Integer::NewFromUnsigned(isolate, val)/*, ReadOnly*/);
1380 }
1381 else
1382 {
1383 const int64_t val = (int64_t)(*it)[i];
1384 if (val<INT32_MIN || val>INT32_MAX)
1385 row->Set(name, Number::New(isolate, val)/*, ReadOnly*/);
1386 else
1387 row->Set(name, Integer::NewFromUnsigned(isolate, val)/*, ReadOnly*/);
1388 }
1389 continue;
1390 }
1391
1392 // 32 bit
1393 if (sql_type.find("INT")!=string::npos)
1394 {
1395 if (uns)
1396 row->Set(name, Integer::NewFromUnsigned(isolate, (uint32_t)(*it)[i])/*, ReadOnly*/);
1397 else
1398 row->Set(name, Integer::New(isolate, (int32_t)(*it)[i])/*, ReadOnly*/);
1399 continue;
1400 }
1401
1402 if (sql_type.find("BOOL")!=string::npos )
1403 {
1404 row->Set(name, Boolean::New(isolate, (bool)(*it)[i])/*, ReadOnly*/);
1405 continue;
1406 }
1407
1408 if (sql_type.find("FLOAT")!=string::npos)
1409 {
1410 ostringstream val;
1411 val << setprecision(7) << (float)(*it)[i];
1412 row->Set(name, Number::New(isolate, stod(val.str()))/*, ReadOnly*/);
1413 continue;
1414
1415 }
1416 if (sql_type.find("DOUBLE")!=string::npos)
1417 {
1418 row->Set(name, Number::New(isolate, (double)(*it)[i])/*, ReadOnly*/);
1419 continue;
1420 }
1421
1422 if (sql_type.find("CHAR")!=string::npos ||
1423 sql_type.find("TEXT")!=string::npos)
1424 {
1425 row->Set(name, String::NewFromUtf8(isolate, (const char*)(*it)[i])/*, ReadOnly*/);
1426 continue;
1427 }
1428
1429 time_t date = 0;
1430 if (sql_type.find("TIMESTAMP")!=string::npos)
1431 date = mysqlpp::Time((*it)[i]);
1432
1433 if (sql_type.find("DATETIME")!=string::npos)
1434 date = mysqlpp::DateTime((*it)[i]);
1435
1436 if (sql_type.find(" DATE ")!=string::npos)
1437 date = mysqlpp::Date((*it)[i]);
1438
1439 if (date>0)
1440 {
1441 // It is important to catch the exception thrown
1442 // by Date::New in case of thread termination!
1443 const Local<Value> val = Date::New(isolate, date*1000);
1444 if (val.IsEmpty())
1445 return;
1446
1447 row->Set(name, val/*, ReadOnly*/);
1448 }
1449 }
1450
1451 ret->Set(irow++, row);
1452 }
1453
1454 if (irow>0)
1455 ret->Set(String::NewFromUtf8(isolate, "cols"), cols/*, ReadOnly*/);
1456
1457 args.GetReturnValue().Set(ret);
1458 }
1459 catch (const exception &e)
1460 {
1461 isolate->ThrowException(String::NewFromUtf8(isolate, e.what()));
1462 }
1463#endif
1464}
1465
1466void InterpreterV8::FuncDatabase(const Arguments &args)
1467{
1468 Isolate *isolate = Isolate::GetCurrent();
1469
1470 if (!args.IsConstructCall())
1471 {
1472 isolate->ThrowException(String::NewFromUtf8(isolate, "Database must be called as constructor."));
1473 return;
1474 }
1475
1476 if (args.Length()!=1)
1477 {
1478 isolate->ThrowException(String::NewFromUtf8(isolate, "Number of arguments must be 1."));
1479 return;
1480 }
1481
1482 if (!args[0]->IsString())
1483 {
1484 isolate->ThrowException(String::NewFromUtf8(isolate, "Argument 1 not a string."));
1485 return;
1486 }
1487
1488#ifdef HAVE_SQL
1489 try
1490 {
1491 const HandleScope handle_scope(isolate);
1492
1493 // if (!args.IsConstructCall())
1494 // return Constructor(fTemplateDatabase, args);
1495
1496 Database *db = new Database(*String::Utf8Value(args[0]));
1497 fDatabases.push_back(db);
1498
1499 Handle<Object> self = args.This();
1500 self->Set(String::NewFromUtf8(isolate, "user"), String::NewFromUtf8(isolate, db->user.c_str())/*, ReadOnly*/);
1501 self->Set(String::NewFromUtf8(isolate, "server"), String::NewFromUtf8(isolate, db->server.c_str())/*, ReadOnly*/);
1502 self->Set(String::NewFromUtf8(isolate, "database"), String::NewFromUtf8(isolate, db->db.c_str())/*, ReadOnly*/);
1503 if (db->port)
1504 self->Set(String::NewFromUtf8(isolate, "port"), Integer::NewFromUnsigned(isolate, db->port));
1505 self->Set(String::NewFromUtf8(isolate, "query"), FunctionTemplate::New(isolate, WrapDbQuery)->GetFunction()/*, ReadOnly*/);
1506 self->Set(String::NewFromUtf8(isolate, "close"), FunctionTemplate::New(isolate, WrapDbClose)->GetFunction()/*, ReadOnly*/);
1507 self->SetInternalField(0, External::New(isolate, db));
1508
1509 args.GetReturnValue().Set(self);
1510 }
1511 catch (const exception &e)
1512 {
1513 isolate->ThrowException(String::NewFromUtf8(isolate, e.what()));
1514 }
1515#endif
1516}
1517
1518// ==========================================================================
1519// Services
1520// ==========================================================================
1521
1522Handle<Value> InterpreterV8::Convert(char type, const char* &ptr)
1523{
1524 Isolate *isolate = Isolate::GetCurrent();
1525
1526 // Dim values are always unsigned per (FACT++) definition
1527 switch (type)
1528 {
1529 case 'F':
1530 {
1531 // Remove the "imprecision" effect coming from casting a float to
1532 // a double and then showing it with double precision
1533 ostringstream val;
1534 val << setprecision(7) << *reinterpret_cast<const float*>(ptr);
1535 ptr += 4;
1536 return Number::New(isolate, stod(val.str()));
1537 }
1538 case 'D': { Handle<Value> v=Number::New(isolate, *reinterpret_cast<const double*>(ptr)); ptr+=8; return v; }
1539 case 'I':
1540 case 'L': { Handle<Value> v=Integer::NewFromUnsigned(isolate, *reinterpret_cast<const uint32_t*>(ptr)); ptr += 4; return v; }
1541 case 'X':
1542 {
1543 const int64_t val = *reinterpret_cast<const int64_t*>(ptr);
1544 ptr += 8;
1545 if (val>=0 && val<=UINT32_MAX)
1546 return Integer::NewFromUnsigned(isolate, val);
1547 if (val>=INT32_MIN && val<0)
1548 return Integer::New(isolate, val);
1549 return Number::New(isolate, val);
1550 }
1551 case 'S': { Handle<Value> v=Integer::NewFromUnsigned(isolate, *reinterpret_cast<const uint16_t*>(ptr)); ptr += 2; return v; }
1552 case 'C': { Handle<Value> v=Integer::NewFromUnsigned(isolate, (uint16_t)*reinterpret_cast<const uint8_t*>(ptr)); ptr += 1; return v; }
1553 }
1554 return Undefined(isolate);
1555}
1556
1557void InterpreterV8::FuncClose(const Arguments &args)
1558{
1559 Isolate *isolate = Isolate::GetCurrent();
1560
1561 const HandleScope handle_scope(isolate);
1562
1563 //const void *ptr = Local<External>::Cast(args.Holder()->GetInternalField(0))->Value();
1564
1565 const String::Utf8Value str(args.This()->Get(String::NewFromUtf8(isolate, "name")));
1566
1567 const auto it = fReverseMap.find(*str);
1568 if (it!=fReverseMap.end())
1569 fReverseMap.erase(it);
1570
1571 args.This()->Set(String::NewFromUtf8(isolate, "isOpen"), Boolean::New(isolate, false)/*, ReadOnly*/);
1572
1573 args.GetReturnValue().Set(JsUnsubscribe(*str));
1574}
1575
1576Handle<Value> InterpreterV8::ConvertEvent(const EventImp *evt, uint64_t counter, const char *str)
1577{
1578 Isolate *isolate = Isolate::GetCurrent();
1579
1580 const vector<Description> vec = JsDescription(str);
1581
1582 Local<Context> context = isolate->GetCurrentContext();
1583 MaybeLocal<Object> mret = fTemplateEvent->GetFunction()->NewInstance(context);
1584 if (mret.IsEmpty())
1585 return Undefined(isolate);
1586
1587 const Local<Value> date = Date::New(isolate, evt->GetJavaDate());
1588 if (date.IsEmpty())
1589 return Undefined(isolate);
1590
1591 Local<Object> ret = mret.ToLocalChecked();
1592
1593 ret->Set(String::NewFromUtf8(isolate, "name"), String::NewFromUtf8(isolate, str)/*, ReadOnly*/);
1594 ret->Set(String::NewFromUtf8(isolate, "format"), String::NewFromUtf8(isolate, evt->GetFormat().c_str())/*, ReadOnly*/);
1595 ret->Set(String::NewFromUtf8(isolate, "qos"), Integer::New(isolate, evt->GetQoS())/*, ReadOnly*/);
1596 ret->Set(String::NewFromUtf8(isolate, "size"), Integer::New(isolate, evt->GetSize())/*, ReadOnly*/);
1597 ret->Set(String::NewFromUtf8(isolate, "counter"), Integer::New(isolate, counter)/*, ReadOnly*/);
1598 if (evt->GetJavaDate()>0)
1599 ret->Set(String::NewFromUtf8(isolate, "time"), date/*, ReadOnly*/);
1600
1601 // If names are available data will also be provided as an
1602 // object. If an empty event was received, but names are available,
1603 // the object will be empty. Otherwise 'obj' will be undefined.
1604 // obj===undefined: no data received
1605 // obj!==undefined, length==0: names for event available
1606 // obj!==undefined, obj.length>0: names available, data received
1607 Handle<Object> named = Object::New(isolate);
1608 if (vec.size()>0)
1609 ret->Set(String::NewFromUtf8(isolate, "obj"), named/*, ReadOnly*/);
1610
1611 // If no event was received (usually a disconnection event in
1612 // the context of FACT++), no data is returned
1613 if (evt->IsEmpty())
1614 return ret;
1615
1616 // If valid data was received, but the size was zero, then
1617 // null is returned as data
1618 // data===undefined: no data received
1619 // data===null: event received, but no data
1620 // data.length>0: event received, contains data
1621 if (evt->GetSize()==0 || evt->GetFormat().empty())
1622 {
1623 ret->Set(String::NewFromUtf8(isolate, "data"), Null(isolate)/*, ReadOnly*/);
1624 return ret;
1625 }
1626
1627 // It seems a copy is required either in the boost which comes with
1628 // Ubuntu 16.04 or in gcc5 ?!
1629 const string fmt = evt->GetFormat();
1630
1631 typedef boost::char_separator<char> separator;
1632 const boost::tokenizer<separator> tokenizer(fmt, separator(";:"));
1633
1634 const vector<string> tok(tokenizer.begin(), tokenizer.end());
1635
1636 //Handle<Object> arr = tok.size()>1 ? Array::New(isolate) : ret;
1637
1638 Handle<Object> arr;
1639 if (tok.size()>1)
1640 arr = Array::New(isolate);
1641 else
1642 arr = ret;
1643
1644 if (arr.IsEmpty())
1645 return Undefined(isolate);
1646
1647 const char *ptr = evt->GetText();
1648 const char *end = evt->GetText()+evt->GetSize();
1649
1650 try
1651 {
1652 size_t pos = 1;
1653 for (auto it=tok.begin(); it<tok.end() && ptr<end; it++, pos++)
1654 {
1655 char type = (*it)[0];
1656 it++;
1657
1658 string name = pos<vec.size() ? vec[pos].name : "";
1659 if (tok.size()==1)
1660 name = "data";
1661
1662 // Get element size
1663 uint32_t sz = 1;
1664 switch (type)
1665 {
1666 case 'X':
1667 case 'D': sz = 8; break;
1668 case 'F':
1669 case 'I':
1670 case 'L': sz = 4; break;
1671 case 'S': sz = 2; break;
1672 case 'C': sz = 1; break;
1673 }
1674
1675 // Check if no number is attached if the size of the
1676 // received data is consistent with the format string
1677 if (it==tok.end() && (end-ptr)%sz>0)
1678 return Exception::Error(String::NewFromUtf8(isolate, ("Number of received bytes ["+to_string(evt->GetSize())+"] does not match format ["+evt->GetFormat()+"]").c_str()));
1679
1680 // Check if format has a number attached.
1681 // If no number is attached calculate number of elements
1682 const uint32_t cnt = it==tok.end() ? (end-ptr)/sz : stoi(it->c_str());
1683
1684 // is_str: Array of type C but unknown size (String)
1685 // is_one: Array of known size, but size is 1 (I:1)
1686 const bool is_str = type=='C' && it==tok.end();
1687 const bool is_one = cnt==1 && it!=tok.end();
1688
1689 Handle<Value> v;
1690
1691 if (is_str)
1692 v = String::NewFromUtf8(isolate, ptr);
1693 if (is_one)
1694 v = Convert(type, ptr);
1695
1696 // Array of known (I:5) or unknown size (I), but no string
1697 if (!is_str && !is_one)
1698 {
1699 Handle<Object> a = Array::New(isolate, cnt);
1700 if (a.IsEmpty())
1701 return Undefined(isolate);
1702
1703 for (uint32_t i=0; i<cnt; i++)
1704 a->Set(i, Convert(type, ptr));
1705
1706 v = a;
1707 }
1708
1709 if (tok.size()>1)
1710 arr->Set(pos-1, v);
1711 else
1712 ret->Set(String::NewFromUtf8(isolate, "data"), v/*, ReadOnly*/);
1713
1714 if (!name.empty())
1715 {
1716 const Handle<String> n = String::NewFromUtf8(isolate, name.c_str());
1717 named->Set(n, v);
1718 }
1719 }
1720
1721 if (tok.size()>1)
1722 ret->Set(String::NewFromUtf8(isolate, "data"), arr/*, ReadOnly*/);
1723
1724 return ret;
1725 }
1726 catch (...)
1727 {
1728 return Exception::Error(String::NewFromUtf8(isolate, ("Format string conversion '"+evt->GetFormat()+"' failed.").c_str()));
1729 }
1730}
1731/*
1732Handle<Value> InterpreterV8::FuncGetData(const Arguments &args)
1733{
1734 HandleScope handle_scope;
1735
1736 const String::Utf8Value str(args.Holder()->Get(String::NewFromUtf8(isolate, "name")));
1737
1738 const pair<uint64_t, EventImp *> p = JsGetEvent(*str);
1739
1740 const EventImp *evt = p.second;
1741 if (!evt)
1742 return Undefined(isolate);
1743
1744 //if (counter==cnt)
1745 // return info.Holder();//Holder()->Get(String::NewFromUtf8(isolate, "data"));
1746
1747 Handle<Value> ret = ConvertEvent(evt, p.first, *str);
1748 return ret->IsNativeError() ? isolate->ThrowException(ret) : handle_scope.Escape(ret);
1749}
1750*/
1751void InterpreterV8::FuncGetData(const Arguments &args)
1752{
1753 Isolate *isolate = Isolate::GetCurrent();
1754
1755 if (args.Length()>2)
1756 {
1757 isolate->ThrowException(String::NewFromUtf8(isolate, "Number of arguments must not be greater than 2."));
1758 return;
1759 }
1760
1761 if (args.Length()>=1 && !args[0]->IsInt32() && !args[0]->IsNull())
1762 {
1763 isolate->ThrowException(String::NewFromUtf8(isolate, "Argument 1 not an uint32."));
1764 return;
1765 }
1766
1767 if (args.Length()==2 && !args[1]->IsBoolean())
1768 {
1769 isolate->ThrowException(String::NewFromUtf8(isolate, "Argument 2 not a boolean."));
1770 return;
1771 }
1772
1773 // Using a Javascript function has the advantage that it is fully
1774 // interruptable without the need of C++ code
1775 const bool null = args.Length()>=1 && args[0]->IsNull();
1776 const int32_t timeout = args.Length()>=1 ? args[0]->Int32Value() : 0;
1777 const bool named = args.Length()<2 || args[1]->BooleanValue();
1778
1779 const HandleScope handle_scope(isolate);
1780
1781 const Handle<String> data = String::NewFromUtf8(isolate, "data");
1782 const Handle<String> object = String::NewFromUtf8(isolate, "obj");
1783
1784 const String::Utf8Value name(args.Holder()->Get(String::NewFromUtf8(isolate, "name")));
1785
1786 TryCatch exception(isolate);
1787
1788 Time t;
1789 while (!exception.HasCaught())
1790 {
1791 const pair<uint64_t, EventImp *> p = JsGetEvent(*name);
1792
1793 const EventImp *evt = p.second;
1794 if (evt)
1795 {
1796 const Handle<Value> val = ConvertEvent(evt, p.first, *name);
1797 if (val->IsNativeError())
1798 {
1799 isolate->ThrowException(val);
1800 return;
1801 }
1802
1803
1804 // Protect against the return of an exception
1805 if (val->IsObject())
1806 {
1807 const Handle<Object> event = val->ToObject();
1808 const Handle<Value> obj = event->Get(named?object:data);
1809 if (!obj.IsEmpty())
1810 {
1811 if (!named)
1812 {
1813 // No names (no 'obj'), but 'data'
1814 if (!obj->IsUndefined())
1815 {
1816 args.GetReturnValue().Set(val);
1817 return;
1818 }
1819 }
1820 else
1821 {
1822 // Has names and data was received?
1823 if (obj->IsObject() && obj->ToObject()->GetOwnPropertyNames()->Length()>0)
1824 {
1825 args.GetReturnValue().Set(val);
1826 return;
1827 }
1828 }
1829 }
1830 }
1831 }
1832
1833 if (args.Length()==0)
1834 break;
1835
1836 if (!null && Time()-t>=boost::posix_time::milliseconds(abs(timeout)))
1837 break;
1838
1839 // Theoretically, the CPU usage can be reduced by maybe a factor
1840 // of four using a larger value, but this also means that the
1841 // JavaScript is locked for a longer time.
1842 const Unlocker unlock(isolate);
1843 usleep(1000);
1844 }
1845
1846 // This hides the location of the exception, which is wanted.
1847 if (exception.HasCaught())
1848 {
1849 exception.ReThrow();
1850 return;
1851 }
1852
1853 if (timeout<0)
1854 return;
1855
1856 const string str = "Waiting for a valid event of "+string(*name)+" timed out.";
1857 isolate->ThrowException(String::NewFromUtf8(isolate, str.c_str()));
1858}
1859
1860void InterpreterV8::TerminateExecution(Isolate* isolate)
1861{
1862 // This makes all threads/contexts sooner or later terminate
1863 isolate->TerminateExecution();
1864}
1865
1866// This is a callback from the RemoteControl piping event handling
1867// to the java script ---> in test phase!
1868void InterpreterV8::JsHandleEvent(const EventImp &evt, uint64_t cnt, const string &service)
1869{
1870 // FIXME: This is not thread safe!
1871 // This is copied as fMainThread=0 is used to signal
1872 // that no execution should take place anymore
1873 Isolate *isolate = fMainThread;
1874 if (!isolate)
1875 return;
1876
1877 //{
1878
1879 // We want to run as an interrupt in the main thread
1880 const Locker locker(isolate);
1881 if (!fMainThread)
1882 return;
1883
1884 const Isolate::Scope isolate_scope(isolate);
1885 const HandleScope handle_scope(isolate);
1886
1887 const auto it = fReverseMap.find(service);
1888 if (it==fReverseMap.end())
1889 return;
1890
1891 Handle<Object> fun = it->second.Get(isolate);
1892 const Context::Scope scope(fun->CreationContext());
1893
1894 const Handle<String> onchange = String::NewFromUtf8(isolate, "onchange");
1895 if (!fun->Has(onchange))
1896 return;
1897
1898 const Handle<Value> val = fun->Get(onchange);
1899 if (!val->IsFunction())
1900 return;
1901
1902 TryCatch exception(isolate);
1903
1904 Handle<Value> ret = ConvertEvent(&evt, cnt, service.c_str());
1905 if (ret->IsObject())
1906 Handle<Function>::Cast(val)->Call(fun->CreationContext(), fun, 1, &ret).IsEmpty();
1907
1908 if (!HandleException(exception, "Service.onchange"))
1909 isolate->AddBeforeCallEnteredCallback(TerminateExecution);
1910
1911 if (ret->IsNativeError())
1912 {
1913 JsException(service+".onchange callback - "+*String::Utf8Value(ret));
1914 isolate->AddBeforeCallEnteredCallback(TerminateExecution);
1915 }
1916
1917 // isolate->DiscardThreadSpecificMetadata();
1918
1919/*
1920 // The Isolation context isolates (keeps local) interrupts and termination requests!
1921 // Terminating execution doesn't work while the JS is still runnning in this script
1922 if (term)
1923 {
1924 // There is no point in calling TerminateExecution before (after releasing
1925 // the lock) the scheduler has entered the main thread again, as the
1926 // TerminateExecution would just be lost.
1927 const Unlocker unlock(isolate);
1928 for (int i=0; i<10 && !fMainThread->InContext(); i++)
1929 usleep(50);
1930 isolate->TerminateExecution();
1931 }
1932*/
1933}
1934
1935void InterpreterV8::OnChangeSet(Local<Name> name, Local<Value> value, const PropertyCallbackInfo<Value> &)
1936{
1937 Isolate *isolate = Isolate::GetCurrent();
1938
1939 // Returns the value if the setter intercepts the request.
1940 // Otherwise, returns an empty handle.
1941 const string server = *String::Utf8Value(name);
1942 auto it = fStateCallbacks.find(server);
1943
1944 if (it!=fStateCallbacks.end())
1945 fStateCallbacks.erase(it);
1946
1947 if (value->IsFunction())
1948 fStateCallbacks[server] = PersistentCopy<Object>(isolate, value->ToObject());
1949}
1950
1951void InterpreterV8::JsHandleState(const std::string &server, const State &state)
1952{
1953 // FIXME: This is not thread safe!
1954 // This is copied as fMainThread=0 is used to signal
1955 // that no execution should take place anymore
1956 Isolate *isolate = fMainThread;
1957 if (!isolate)
1958 return;
1959
1960 //{
1961
1962 // We want to run as an interrupt in the main thread
1963 const Locker locker(isolate);
1964 if (!fMainThread)
1965 return;
1966
1967 const Isolate::Scope isolate_scope(isolate);
1968 const HandleScope handle_scope(isolate);
1969
1970
1971 auto it = fStateCallbacks.find(server);
1972 if (it==fStateCallbacks.end())
1973 {
1974 it = fStateCallbacks.find("*");
1975 if (it==fStateCallbacks.end())
1976 return;
1977 }
1978
1979 Handle<Object> fun = it->second.Get(isolate);
1980 const Context::Scope scope(fun->CreationContext());
1981
1982 // -------------------------------------------------------------------
1983
1984 Handle<ObjectTemplate> obj = ObjectTemplate::New(isolate);
1985 obj->Set(String::NewFromUtf8(isolate, "server"), String::NewFromUtf8(isolate, server.c_str()), ReadOnly);
1986
1987 if (state.index>-256)
1988 {
1989 obj->Set(String::NewFromUtf8(isolate, "index"), Integer::New(isolate, state.index), ReadOnly);
1990 obj->Set(String::NewFromUtf8(isolate, "name"), String::NewFromUtf8(isolate, state.name.c_str()), ReadOnly);
1991 obj->Set(String::NewFromUtf8(isolate, "comment"), String::NewFromUtf8(isolate, state.comment.c_str()), ReadOnly);
1992
1993 MaybeLocal<Value> date = Date::New(isolate->GetCurrentContext(), state.time.JavaDate());
1994 if (!date.IsEmpty())
1995 {
1996 obj->Set(String::NewFromUtf8(isolate, "time"), date.ToLocalChecked()->ToString(), ReadOnly);
1997
1998 // This crahshes when called a secont time. I don't know why.
1999 // obj->Set(String::NewFromUtf8(isolate, "time"), date.ToLocalChecked(), ReadOnly);
2000 }
2001 }
2002
2003 // -------------------------------------------------------------------
2004
2005 TryCatch exception(isolate);
2006
2007 Handle<Value> args[] = { obj->NewInstance() };
2008 Handle<Function>::Cast(fun)->Call(fun->CreationContext(), fun, 1, args).IsEmpty();
2009
2010 if (!HandleException(exception, "dim.onchange"))
2011 isolate->AddBeforeCallEnteredCallback(TerminateExecution);
2012
2013 // isolate->DiscardThreadSpecificMetadata();
2014}
2015
2016// ==========================================================================
2017// Interrupt handling
2018// ==========================================================================
2019
2020void InterpreterV8::FuncSetInterrupt(const Arguments &args)
2021{
2022 Isolate *isolate = Isolate::GetCurrent();
2023
2024 if (args.Length()!=1)
2025 {
2026 isolate->ThrowException(String::NewFromUtf8(isolate, "Number of arguments must be 1."));
2027 return;
2028 }
2029
2030 if (!args[0]->IsNull() && !args[0]->IsUndefined() && !args[0]->IsFunction())
2031 {
2032 isolate->ThrowException(String::NewFromUtf8(isolate, "Argument not a function, null or undefined."));
2033 return;
2034 }
2035
2036 if (args[0]->IsNull() || args[0]->IsUndefined())
2037 {
2038 fInterruptCallback.Reset();
2039 return;
2040 }
2041
2042 // Returns the value if the setter intercepts the request. Otherwise, returns an empty handle.
2043 fInterruptCallback = PersistentCopy<Object>(isolate, args[0]->ToObject());
2044}
2045
2046Handle<Value> InterpreterV8::HandleInterruptImp(string str, uint64_t time)
2047{
2048 Isolate *isolate = Isolate::GetCurrent();
2049
2050 if (fInterruptCallback.IsEmpty())
2051 return Handle<Value>();
2052
2053 const size_t p = str.find_last_of('\n');
2054
2055 const string usr = p==string::npos?"":str.substr(p+1);
2056
2057 string irq = p==string::npos?str:str.substr(0, p);
2058
2059 map<string,string> data;
2060 try
2061 {
2062 data = Tools::Split(irq, true);
2063 }
2064 catch (const exception &e)
2065 {
2066 irq = "ERROR";
2067 data["0"] = e.what();
2068 JsWarn("Couldn't parse interrupt: "+irq+" ["+string(e.what())+"]");
2069 }
2070
2071 Local<Value> irq_str = String::NewFromUtf8(isolate, irq.c_str());
2072 Local<Value> usr_str = String::NewFromUtf8(isolate, usr.c_str());
2073 Local<Value> date = Date::New(isolate, time);
2074 Handle<Object> arr = Array::New(isolate, data.size());
2075
2076 if (date.IsEmpty() || arr.IsEmpty())
2077 return Handle<Value>();
2078
2079 for (auto it=data.begin(); it!=data.end(); it++)
2080 arr->Set(String::NewFromUtf8(isolate, it->first.c_str()), String::NewFromUtf8(isolate, it->second.c_str()));
2081
2082 Handle<Value> args[] = { irq_str, arr, date, usr_str };
2083
2084 Handle<Object> fun = fInterruptCallback.Get(isolate);
2085 //const Context::Scope scope(obj->CreationContext());
2086
2087 MaybeLocal<Value> ret = Handle<Function>::Cast(fun)->Call(fun->CreationContext(), fun, 4, args);
2088 return ret.IsEmpty() ? Handle<Value>() : ret.ToLocalChecked();
2089}
2090
2091int InterpreterV8::JsHandleInterrupt(const EventImp &evt)
2092{
2093 // FIXME: This is not thread safe!
2094 // This is copied as fMainThread=0 is used to signal
2095 // that no execution should take place anymore
2096 Isolate *isolate = fMainThread;
2097 if (!isolate)
2098 return -42;
2099
2100 //{
2101
2102 // We want to run as an interrupt in the main thread
2103 const Locker locker(isolate);
2104 if (!fMainThread)
2105 return -42;
2106
2107 const Isolate::Scope isolate_scope(isolate);
2108 const HandleScope handle_scope(isolate);
2109
2110 if (fInterruptCallback.IsEmpty())
2111 return -42;
2112
2113 Handle<Object> fun = fInterruptCallback.Get(isolate);
2114 const Context::Scope scope(fun->CreationContext());
2115
2116 // -------------------------------------------------------------------
2117
2118 TryCatch exception(isolate);
2119
2120 const Handle<Value> val = HandleInterruptImp(evt.GetString(), evt.GetJavaDate());
2121
2122 const int rc = !val.IsEmpty() && val->IsInt32() ? val->Int32Value() : 0;
2123
2124 if (!HandleException(exception, "interrupt"))
2125 isolate->AddBeforeCallEnteredCallback(TerminateExecution);
2126
2127 return rc<10 || rc>255 ? -42 : rc;
2128}
2129
2130void InterpreterV8::FuncTriggerInterrupt(const Arguments &args)
2131{
2132 Isolate *isolate = Isolate::GetCurrent();
2133
2134 string data;
2135 for (int i=0; i<args.Length(); i++)
2136 {
2137 const String::Utf8Value str(args[i]);
2138
2139 if (string(*str).find_first_of('\n')!=string::npos)
2140 {
2141 isolate->ThrowException(String::NewFromUtf8(isolate, "No argument must contain line breaks."));
2142 return;
2143 }
2144
2145 if (!*str)
2146 continue;
2147
2148 data += *str;
2149 data += ' ';
2150 }
2151
2152 const HandleScope handle_scope(isolate);
2153
2154 const Handle<Value> rc = HandleInterruptImp(Tools::Trim(data), Time().JavaDate());
2155 args.GetReturnValue().Set(rc);
2156}
2157
2158// ==========================================================================
2159// Class 'Subscription'
2160// ==========================================================================
2161
2162void InterpreterV8::FuncSubscription(const Arguments &args)
2163{
2164 Isolate *isolate = Isolate::GetCurrent();
2165
2166 if (args.Length()!=1 && args.Length()!=2)
2167 {
2168 isolate->ThrowException(String::NewFromUtf8(isolate, "Number of arguments must be one or two."));
2169 return;
2170 }
2171
2172 if (!args[0]->IsString())
2173 {
2174 isolate->ThrowException(String::NewFromUtf8(isolate, "Argument 1 must be a string."));
2175 return;
2176 }
2177
2178 if (args.Length()==2 && !args[1]->IsFunction())
2179 {
2180 isolate->ThrowException(String::NewFromUtf8(isolate, "Argument 2 must be a function."));
2181 return;
2182 }
2183
2184 const String::Utf8Value str(args[0]);
2185
2186 if (!args.IsConstructCall())
2187 {
2188 const auto it = fReverseMap.find(*str);
2189 if (it!=fReverseMap.end())
2190 args.GetReturnValue().Set(it->second.Get(isolate)/*Local<Object>::New(isolate, it->second)*/);
2191
2192 return;
2193 }
2194
2195 const HandleScope handle_scope(isolate);
2196
2197 Handle<Object> self = args.This();
2198 self->Set(String::NewFromUtf8(isolate, "get"), FunctionTemplate::New(isolate, WrapGetData)->GetFunction()/*, ReadOnly*/);
2199 self->Set(String::NewFromUtf8(isolate, "close"), FunctionTemplate::New(isolate, WrapClose)->GetFunction()/*, ReadOnly*/);
2200 self->Set(String::NewFromUtf8(isolate, "name"), String::NewFromUtf8(isolate, *str)/*, ReadOnly*/);
2201 self->Set(String::NewFromUtf8(isolate, "isOpen"), Boolean::New(isolate, true));
2202
2203 if (args.Length()==2)
2204 self->Set(String::NewFromUtf8(isolate, "onchange"), args[1]);
2205
2206 fReverseMap[*str] = PersistentCopy<Object>(isolate, self);
2207
2208 void *ptr = JsSubscribe(*str);
2209 if (ptr==0)
2210 {
2211 isolate->ThrowException(String::NewFromUtf8(isolate, ("Subscription to '"+string(*str)+"' already exists.").c_str()));
2212 return;
2213 }
2214
2215 self->SetInternalField(0, External::New(isolate, ptr));
2216
2217 // Persistent<Object> p = Persistent<Object>::New(obj->NewInstance());
2218 // obj.MakeWeak((void*)1, Cleanup);
2219 // return obj;
2220}
2221
2222// ==========================================================================
2223// Astrometry
2224// ==========================================================================
2225#ifdef HAVE_NOVA
2226
2227double InterpreterV8::GetDataMember(const Arguments &args, const char *name)
2228{
2229 Isolate *isolate = Isolate::GetCurrent();
2230 return args.This()->Get(String::NewFromUtf8(isolate, name))->NumberValue();
2231}
2232
2233void InterpreterV8::CalcDist(const Arguments &args, const bool local)
2234{
2235 Isolate *isolate = Isolate::GetCurrent();
2236
2237 if (args.Length()!=2)
2238 {
2239 isolate->ThrowException(String::NewFromUtf8(isolate, "dist must be called with exactly two arguments."));
2240 return;
2241 }
2242
2243 if (!args[0]->IsObject() || !args[1]->IsObject())
2244 {
2245 isolate->ThrowException(String::NewFromUtf8(isolate, "At least one argument not an object."));
2246 return;
2247 }
2248
2249 // FiXME: Add a check for the argument type
2250
2251 const HandleScope handle_scope(isolate);
2252
2253 Handle<Object> obj[2] =
2254 {
2255 Handle<Object>::Cast(args[0]),
2256 Handle<Object>::Cast(args[1])
2257 };
2258
2259 const Handle<String> s_theta = String::NewFromUtf8(isolate, local?"zd":"dec"); // was: zd
2260 const Handle<String> s_phi = String::NewFromUtf8(isolate, local?"az":"ra"); // was: az
2261
2262 const double conv_t = M_PI/180;
2263 const double conv_p = local ? -M_PI/180 : M_PI/12;
2264 const double offset = local ? 0 : M_PI;
2265
2266 const double theta0 = offset - obj[0]->Get(s_theta)->NumberValue() * conv_t;
2267 const double phi0 = obj[0]->Get(s_phi )->NumberValue() * conv_p;
2268 const double theta1 = offset - obj[1]->Get(s_theta)->NumberValue() * conv_t;
2269 const double phi1 = obj[1]->Get(s_phi )->NumberValue() * conv_p;
2270
2271 if (!finite(theta0) || !finite(theta1) || !finite(phi0) || !finite(phi1))
2272 {
2273 isolate->ThrowException(String::NewFromUtf8(isolate, "some values not valid or not finite."));
2274 return;
2275 }
2276
2277 /*
2278 const double x0 = sin(zd0) * cos(az0); // az0 -= az0
2279 const double y0 = sin(zd0) * sin(az0); // az0 -= az0
2280 const double z0 = cos(zd0);
2281
2282 const double x1 = sin(zd1) * cos(az1); // az1 -= az0
2283 const double y1 = sin(zd1) * sin(az1); // az1 -= az0
2284 const double z1 = cos(zd1);
2285
2286 const double res = acos(x0*x1 + y0*y1 + z0*z1) * 180/M_PI;
2287 */
2288
2289 // cos(az1-az0) = cos(az1)*cos(az0) + sin(az1)*sin(az0)
2290
2291 const double x = sin(theta0) * sin(theta1) * cos(phi1-phi0);
2292 const double y = cos(theta0) * cos(theta1);
2293
2294 const double res = acos(x + y) * 180/M_PI;
2295
2296 args.GetReturnValue().Set(res);
2297}
2298
2299void InterpreterV8::LocalDist(const Arguments &args)
2300{
2301 CalcDist(args, true);
2302}
2303
2304void InterpreterV8::SkyDist(const Arguments &args)
2305{
2306 CalcDist(args, false);
2307}
2308
2309void InterpreterV8::MoonDisk(const Arguments &args)
2310{
2311 Isolate *isolate = Isolate::GetCurrent();
2312
2313 if (args.Length()>1)
2314 {
2315 isolate->ThrowException(String::NewFromUtf8(isolate, "disk must not be called with more than one argument."));
2316 return;
2317 }
2318
2319 const uint64_t v = uint64_t(args[0]->NumberValue());
2320 const Time utc = args.Length()==0 ? Time() : Time(v/1000, v%1000);
2321
2322 args.GetReturnValue().Set(Nova::GetLunarDisk(utc.JD()));
2323}
2324
2325struct AstroArgs
2326{
2327 string obs;
2328 Nova::LnLatPosn posn;
2329 double jd;
2330 uint64_t jsdate;
2331
2332 AstroArgs() : jsdate(0) { }
2333};
2334
2335AstroArgs EvalAstroArgs(int offset, const Arguments &args, int8_t type=2)
2336{
2337 const uint8_t max = abs(type);
2338
2339 if (args.Length()>offset+max)
2340 throw runtime_error("Number of arguments must not exceed "+to_string(offset+max)+".");
2341
2342 if (type==1 && args.Length()==offset+1 && !args[offset]->IsString())
2343 throw runtime_error("Argument "+to_string(offset+1)+" must be a string.");
2344 if (type==-1 && args.Length()==offset+1 && !args[offset]->IsDate())
2345 throw runtime_error("Argument "+to_string(offset+1)+" must be a date.");
2346
2347 if (args.Length()==offset+1 && !(args[offset]->IsDate() || args[offset]->IsString()))
2348 throw runtime_error("Argument "+to_string(offset+1)+" must be a string or Date.");
2349
2350 if (args.Length()==offset+2 &&
2351 !(args[offset+0]->IsDate() && args[offset+1]->IsString()) &&
2352 !(args[offset+1]->IsDate() && args[offset+0]->IsString()))
2353 throw runtime_error("Arguments "+to_string(offset+1)+" and "+to_string(offset+2)+" must be a string/Date or Date/string.");
2354
2355 Isolate *isolate = Isolate::GetCurrent();
2356
2357 const HandleScope handle_scope(isolate);
2358
2359 Local<Value> obs = args.This()->Get(String::NewFromUtf8(isolate, "observatory"));
2360 if (args.Length()>offset && args[offset]->IsString())
2361 obs = args[offset];
2362 if (args.Length()>offset+1 && args[offset+1]->IsString())
2363 obs = args[offset+1];
2364
2365 AstroArgs rc;
2366
2367 // For constructors, observatory can stay empty if not explicitly given
2368 if (offset<2)
2369 rc.obs = Nova::LnLatPosn::preset();
2370
2371 if (!obs.IsEmpty() && !obs->IsUndefined())
2372 rc.obs = *String::Utf8Value(obs);
2373
2374 rc.posn = rc.obs;
2375
2376 if ((!rc.obs.empty() || offset==0) && !rc.posn.isValid())
2377 throw runtime_error("Observatory "+rc.obs+" unknown.");
2378
2379 Local<Value> date = args.This()->Get(String::NewFromUtf8(isolate, "time"));
2380 if (args.Length()>offset && args[offset]->IsDate())
2381 date = args[offset];
2382 if (args.Length()>offset+1 && args[offset+1]->IsDate())
2383 date = args[offset+1];
2384
2385 // For constructors, time can stay empty if not explicitly given
2386 if (offset<2)
2387 rc.jsdate = Time().JavaDate();
2388
2389 if (!date.IsEmpty() && !date->IsUndefined())
2390 rc.jsdate = uint64_t(date->NumberValue());
2391
2392 rc.jd = Time(rc.jsdate/1000, rc.jsdate%1000).JD();
2393
2394 return rc;
2395}
2396
2397void InterpreterV8::LocalToSky(const Arguments &args)
2398{
2399 Isolate *isolate = Isolate::GetCurrent();
2400
2401 AstroArgs local;
2402 try
2403 {
2404 local = EvalAstroArgs(0, args, 2);
2405 }
2406 catch (const exception &e)
2407 {
2408 isolate->ThrowException(String::NewFromUtf8(isolate, e.what()));
2409 return;
2410 }
2411
2412 Nova::ZdAzPosn hrz;
2413 hrz.zd = GetDataMember(args, "zd");
2414 hrz.az = GetDataMember(args, "az");
2415
2416 if (!finite(hrz.zd) || !finite(hrz.az))
2417 {
2418 isolate->ThrowException(String::NewFromUtf8(isolate, "Zd and az must be finite."));
2419 return;
2420 }
2421
2422 const Nova::EquPosn equ = Nova::GetEquFromHrz(hrz, local.posn, local.jd);
2423
2424 const HandleScope handle_scope(isolate);
2425
2426 Local<Context> context = isolate->GetCurrentContext();
2427
2428 Handle<Value> arg_loc[] = { Number::New(isolate, hrz.zd), Number::New(isolate, hrz.az), String::NewFromUtf8(isolate, local.obs.c_str()), Date::New(isolate, local.jsdate) };
2429 MaybeLocal<Object> loc = fTemplateLocal->GetFunction()->NewInstance(context, 4, arg_loc);
2430
2431 Handle<Value> arg_sky[] = { Number::New(isolate, equ.ra/15), Number::New(isolate, equ.dec), loc.ToLocalChecked() };
2432 args.GetReturnValue().Set(fTemplateSky->GetFunction()->NewInstance(context, 3, arg_sky).ToLocalChecked());
2433}
2434
2435void InterpreterV8::SkyToLocal(const Arguments &args)
2436{
2437 Isolate *isolate = Isolate::GetCurrent();
2438
2439 AstroArgs local;
2440 try
2441 {
2442 local = EvalAstroArgs(0, args, 2);
2443 }
2444 catch (const exception &e)
2445 {
2446 isolate->ThrowException(String::NewFromUtf8(isolate, e.what()));
2447 return;
2448 }
2449
2450 Nova::EquPosn equ;
2451 equ.ra = GetDataMember(args, "ra")*15;
2452 equ.dec = GetDataMember(args, "dec");
2453
2454 if (!finite(equ.ra) || !finite(equ.dec))
2455 {
2456 isolate->ThrowException(String::NewFromUtf8(isolate, "Ra and dec must be finite."));
2457 return;
2458 }
2459
2460 const HandleScope handle_scope(isolate);
2461
2462 Local<Context> context = isolate->GetCurrentContext();
2463
2464 const Nova::ZdAzPosn hrz = Nova::GetHrzFromEqu(equ, local.posn, local.jd);
2465
2466 Handle<Value> arg[] = { Number::New(isolate, hrz.zd), Number::New(isolate, hrz.az), String::NewFromUtf8(isolate, local.obs.c_str()), Date::New(isolate, local.jsdate) };
2467 args.GetReturnValue().Set(fTemplateLocal->GetFunction()->NewInstance(context, 4, arg).ToLocalChecked());
2468}
2469
2470void InterpreterV8::MoonToLocal(const Arguments &args)
2471{
2472 Isolate *isolate = Isolate::GetCurrent();
2473
2474 AstroArgs local;
2475 try
2476 {
2477 local = EvalAstroArgs(0, args, 1);
2478 }
2479 catch (const exception &e)
2480 {
2481 isolate->ThrowException(String::NewFromUtf8(isolate, e.what()));
2482 return;
2483 }
2484
2485 Nova::EquPosn equ;
2486 equ.ra = GetDataMember(args, "ra")*15;
2487 equ.dec = GetDataMember(args, "dec");
2488
2489 if (!finite(equ.ra) || !finite(equ.dec))
2490 {
2491 isolate->ThrowException(String::NewFromUtf8(isolate, "Ra and dec must be finite."));
2492 return;
2493 }
2494
2495 const HandleScope handle_scope(isolate);
2496
2497 Local<Context> context = isolate->GetCurrentContext();
2498
2499 const Nova::ZdAzPosn hrz = Nova::GetHrzFromEqu(equ, local.posn, local.jd);
2500
2501 Handle<Value> arg[] = { Number::New(isolate, hrz.zd), Number::New(isolate, hrz.az), String::NewFromUtf8(isolate, local.obs.c_str()), Date::New(isolate, local.jsdate) };
2502 args.GetReturnValue().Set(fTemplateLocal->GetFunction()->NewInstance(context, 4, arg).ToLocalChecked());
2503}
2504
2505void InterpreterV8::ConstructorMoon(const Arguments &args)
2506{
2507 Isolate *isolate = Isolate::GetCurrent();
2508
2509 AstroArgs local;
2510 try
2511 {
2512 local = EvalAstroArgs(0, args, -1);
2513 }
2514 catch (const exception &e)
2515 {
2516 isolate->ThrowException(String::NewFromUtf8(isolate, e.what()));
2517 return;
2518 }
2519
2520 const Nova::EquPosn equ = Nova::GetLunarEquCoords(local.jd, 0.01);
2521
2522 const HandleScope handle_scope(isolate);
2523
2524 // ----------------------------
2525
2526 if (!args.IsConstructCall())
2527 {
2528 args.GetReturnValue().Set(Constructor(args));
2529 return;
2530 }
2531
2532 Handle<Function> function =
2533 FunctionTemplate::New(isolate, MoonToLocal)->GetFunction();
2534 if (function.IsEmpty())
2535 return;
2536
2537 Handle<Object> self = args.This();
2538 self->Set(String::NewFromUtf8(isolate, "ra"), Number::New(isolate, equ.ra/15)/*, ReadOnly*/);
2539 self->Set(String::NewFromUtf8(isolate, "dec"), Number::New(isolate, equ.dec)/*, ReadOnly*/);
2540 self->Set(String::NewFromUtf8(isolate, "toLocal"), function/*, ReadOnly*/);
2541 self->Set(String::NewFromUtf8(isolate, "time"), Date::New(isolate, local.jsdate)/*, ReadOnly*/);
2542
2543 args.GetReturnValue().Set(self);
2544}
2545
2546void InterpreterV8::ConstructorSky(const Arguments &args)
2547{
2548 Isolate *isolate = Isolate::GetCurrent();
2549
2550 if (args.Length()<2 || args.Length()>3)
2551 {
2552 isolate->ThrowException(String::NewFromUtf8(isolate, "Sky constructor takes two or three arguments."));
2553 return;
2554 }
2555
2556 if (args.Length()>2 && !args[2]->IsObject())
2557 {
2558 const string n = *String::Utf8Value(args[2]->ToObject()->GetConstructorName());
2559 if (n!="Local")
2560 {
2561 isolate->ThrowException(String::NewFromUtf8(isolate, "Third argument must be of type Local."));
2562 return;
2563 }
2564 }
2565
2566 const double ra = args[0]->NumberValue();
2567 const double dec = args[1]->NumberValue();
2568
2569 if (!finite(ra) || !finite(dec))
2570 {
2571 isolate->ThrowException(String::NewFromUtf8(isolate, "The first two arguments to Sky must be valid numbers."));
2572 return;
2573 }
2574
2575
2576 // ----------------------------
2577
2578 const HandleScope handle_scope(isolate);
2579
2580 if (!args.IsConstructCall())
2581 {
2582 args.GetReturnValue().Set(Constructor(args));
2583 return;
2584 }
2585
2586 Handle<Function> function =
2587 FunctionTemplate::New(isolate, SkyToLocal)->GetFunction();
2588 if (function.IsEmpty())
2589 return;
2590
2591 Handle<Object> self = args.This();
2592 self->Set(String::NewFromUtf8(isolate, "ra"), Number::New(isolate, ra)/*, ReadOnly*/);
2593 self->Set(String::NewFromUtf8(isolate, "dec"), Number::New(isolate, dec)/*, ReadOnly*/);
2594 self->Set(String::NewFromUtf8(isolate, "toLocal"), function/*, ReadOnly*/);
2595 if (args.Length()==3)
2596 self->Set(String::NewFromUtf8(isolate, "local"), args[2]/*, ReadOnly*/);
2597
2598 args.GetReturnValue().Set(self);
2599}
2600
2601void InterpreterV8::ConstructorLocal(const Arguments &args)
2602{
2603 Isolate *isolate = Isolate::GetCurrent();
2604
2605 AstroArgs local;
2606 try
2607 {
2608 local = EvalAstroArgs(2, args, 2);
2609 }
2610 catch (const exception &e)
2611 {
2612 isolate->ThrowException(String::NewFromUtf8(isolate, e.what()));
2613 return;
2614 }
2615
2616 const double zd = args[0]->NumberValue();
2617 const double az = args[1]->NumberValue();
2618
2619 if (!finite(zd) || !finite(az))
2620 {
2621 isolate->ThrowException(String::NewFromUtf8(isolate, "The first two arguments to Local must be valid numbers."));
2622 return;
2623 }
2624
2625 // --------------------
2626
2627 const HandleScope handle_scope(isolate);
2628
2629 if (!args.IsConstructCall())
2630 {
2631 args.GetReturnValue().Set(Constructor(args));
2632 return;
2633 }
2634
2635 Handle<Function> function = FunctionTemplate::New(isolate, LocalToSky)->GetFunction();
2636 if (function.IsEmpty())
2637 return;
2638
2639 Handle<Object> self = args.This();
2640 self->Set(String::NewFromUtf8(isolate, "zd"), Number::New(isolate, zd)/*, ReadOnly*/);
2641 self->Set(String::NewFromUtf8(isolate, "az"), Number::New(isolate, az)/*, ReadOnly*/);
2642 self->Set(String::NewFromUtf8(isolate, "toSky"), function/*, ReadOnly*/);
2643 if (!local.obs.empty())
2644 self->Set(String::NewFromUtf8(isolate, "observatory"), String::NewFromUtf8(isolate, local.obs.c_str())/*, ReadOnly*/);
2645 if (local.jsdate>0)
2646 self->Set(String::NewFromUtf8(isolate, "time"), Date::New(isolate, local.jsdate)/*, ReadOnly*/);
2647
2648 args.GetReturnValue().Set(self);
2649}
2650
2651Handle<Object> ConstructRiseSet(const AstroArgs &args, const Nova::RstTime &rst, const bool &rc)
2652{
2653 Isolate *isolate = Isolate::GetCurrent();
2654
2655 Handle<Object> obj = Object::New(isolate);
2656 obj->Set(String::NewFromUtf8(isolate, "time"), Date::New(isolate, args.jsdate)/*, ReadOnly*/);
2657 obj->Set(String::NewFromUtf8(isolate, "observatory"), String::NewFromUtf8(isolate, args.obs.c_str())/*, ReadOnly*/);
2658
2659 const bool isUp =
2660 (rst.rise<rst.set && (args.jd>rst.rise && args.jd<rst.set)) ||
2661 (rst.rise>rst.set && (args.jd<rst.set || args.jd>rst.rise));
2662
2663 obj->Set(String::NewFromUtf8(isolate, "isUp"), Boolean::New(isolate, isUp)/*, ReadOnly*/);
2664
2665 if (!rc) // circumpolar
2666 return obj;
2667
2668 Handle<Value> rise = Date::New(isolate, Time(rst.rise).JavaDate());
2669 Handle<Value> set = Date::New(isolate, Time(rst.set).JavaDate());
2670 Handle<Value> trans = Date::New(isolate, Time(rst.transit).JavaDate());
2671 if (rise.IsEmpty() || set.IsEmpty() || trans.IsEmpty())
2672 return Handle<Object>();
2673
2674 obj->Set(String::NewFromUtf8(isolate, "rise"), rise/*, ReadOnly*/);
2675 obj->Set(String::NewFromUtf8(isolate, "set"), set/*, ReadOnly*/);
2676 obj->Set(String::NewFromUtf8(isolate, "transit"), trans/*, ReadOnly*/);
2677
2678 return obj;
2679}
2680
2681void InterpreterV8::SunHorizon(const Arguments &args)
2682{
2683 Isolate *isolate = Isolate::GetCurrent();
2684
2685 AstroArgs local;
2686 try
2687 {
2688 local = EvalAstroArgs(1, args, 2);
2689 }
2690 catch (const exception &e)
2691 {
2692 isolate->ThrowException(String::NewFromUtf8(isolate, e.what()));
2693 return;
2694 }
2695
2696 const HandleScope handle_scope(isolate);
2697
2698 double hrz = NAN;
2699 if (args.Length()==0 || args[0]->IsNull())
2700 hrz = LN_SOLAR_STANDART_HORIZON;
2701 if (args.Length()>0 && args[0]->IsNumber())
2702 hrz = args[0]->NumberValue();
2703 if (args.Length()>0 && args[0]->IsString())
2704 {
2705 string arg(Tools::Trim(*String::Utf8Value(args[0])));
2706 transform(arg.begin(), arg.end(), arg.begin(), ::tolower);
2707
2708 if (arg==string("horizon").substr(0, arg.length()))
2709 hrz = LN_SOLAR_STANDART_HORIZON;
2710 if (arg==string("civil").substr(0, arg.length()))
2711 hrz = LN_SOLAR_CIVIL_HORIZON;
2712 if (arg==string("nautical").substr(0, arg.length()))
2713 hrz = LN_SOLAR_NAUTIC_HORIZON;
2714 if (arg==string("fact").substr(0, arg.length()))
2715 hrz = -13;
2716 if (arg==string("astronomical").substr(0, arg.length()))
2717 hrz = LN_SOLAR_ASTRONOMICAL_HORIZON;
2718 }
2719
2720 if (!finite(hrz))
2721 {
2722 isolate->ThrowException(String::NewFromUtf8(isolate, "First argument did not yield a valid number."));
2723 return;
2724 }
2725
2726 ln_rst_time sun;
2727 const bool rc = ln_get_solar_rst_horizon(local.jd-0.5, &local.posn, hrz, &sun)==0;
2728 Handle<Object> rst = ConstructRiseSet(local, sun, rc);
2729 rst->Set(String::NewFromUtf8(isolate, "horizon"), Number::New(isolate, hrz));
2730 args.GetReturnValue().Set(rst);
2731};
2732
2733void InterpreterV8::MoonHorizon(const Arguments &args)
2734{
2735 Isolate *isolate = Isolate::GetCurrent();
2736
2737 AstroArgs local;
2738 try
2739 {
2740 local = EvalAstroArgs(0, args, 2);
2741 }
2742 catch (const exception &e)
2743 {
2744 isolate->ThrowException(String::NewFromUtf8(isolate, e.what()));
2745 return;
2746 }
2747
2748 const HandleScope handle_scope(isolate);
2749
2750 ln_rst_time moon;
2751 const bool rc = ln_get_lunar_rst(local.jd-0.5, &local.posn, &moon)==0;
2752 Handle<Object> rst = ConstructRiseSet(local, moon, rc);
2753 args.GetReturnValue().Set(rst);
2754};
2755#endif
2756
2757// ==========================================================================
2758// Process control
2759// ==========================================================================
2760
2761bool InterpreterV8::HandleException(TryCatch& try_catch, const char *where)
2762{
2763 if (!try_catch.HasCaught() || !try_catch.CanContinue())
2764 return true;
2765
2766 Isolate *isolate = Isolate::GetCurrent();
2767
2768 const HandleScope handle_scope(isolate);
2769
2770 Handle<Value> except = try_catch.Exception();
2771 if (except.IsEmpty() || except->IsNull())
2772 return true;
2773
2774 const String::Utf8Value exception(except);
2775
2776 const Handle<Message> message = try_catch.Message();
2777 if (message.IsEmpty())
2778 return false;
2779
2780 ostringstream out;
2781
2782 if (!message->GetScriptResourceName()->IsUndefined())
2783 {
2784 // Print (filename):(line number): (message).
2785 const String::Utf8Value filename(message->GetScriptResourceName());
2786 if (filename.length()>0)
2787 {
2788 out << *filename;
2789 if (message->GetLineNumber()>0)
2790 out << ": l." << message->GetLineNumber();
2791 if (*exception)
2792 out << ": ";
2793 }
2794 }
2795
2796 if (*exception)
2797 out << *exception;
2798
2799 out << " [" << where << "]";
2800
2801 JsException(out.str());
2802
2803 // Print line of source code.
2804 const String::Utf8Value sourceline(message->GetSourceLine());
2805 if (*sourceline)
2806 JsException(*sourceline);
2807
2808 // Print wavy underline (GetUnderline is deprecated).
2809 const int start = message->GetStartColumn();
2810 const int end = message->GetEndColumn();
2811
2812 out.str("");
2813 if (start>0)
2814 out << setfill(' ') << setw(start) << ' ';
2815 out << setfill('^') << setw(end-start) << '^';
2816
2817 JsException(out.str());
2818
2819 const String::Utf8Value stack_trace(try_catch.StackTrace());
2820 if (stack_trace.length()<=0)
2821 return false;
2822
2823 if (!*stack_trace)
2824 return false;
2825
2826 const string trace(*stack_trace);
2827
2828 typedef boost::char_separator<char> separator;
2829 const boost::tokenizer<separator> tokenizer(trace, separator("\n"));
2830
2831 // maybe skip: " at internal:"
2832 // maybe skip: " at unknown source:"
2833
2834 auto it = tokenizer.begin();
2835 JsException("");
2836 while (it!=tokenizer.end())
2837 JsException(*it++);
2838
2839 return false;
2840}
2841
2842Handle<Value> InterpreterV8::ExecuteInternal(const string &code)
2843{
2844 Isolate *isolate = Isolate::GetCurrent();
2845
2846 // Try/catch and re-throw hides our internal code from
2847 // the displayed exception showing the origin and shows
2848 // the user function instead.
2849 TryCatch exception(isolate);
2850
2851 const Handle<Value> result = ExecuteCode(code);
2852
2853 // This hides the location of the exception in the internal code,
2854 // which is wanted.
2855 if (exception.HasCaught())
2856 exception.ReThrow();
2857
2858 return result;
2859}
2860
2861Handle<Value> InterpreterV8::ExecuteCode(const string &code, const string &file)
2862{
2863 Isolate *isolate = Isolate::GetCurrent();
2864
2865 EscapableHandleScope handle_scope(isolate);
2866
2867 MaybeLocal<String> source = String::NewFromUtf8(isolate, code.c_str(), NewStringType::kNormal, code.size());
2868 const Handle<String> origin = String::NewFromUtf8(isolate, file.c_str());
2869 if (source.IsEmpty())
2870 return Undefined(isolate);
2871
2872 ScriptOrigin sorigin(origin);
2873
2874 Local<Context> context = isolate->GetCurrentContext();
2875 MaybeLocal<Script> script = Script::Compile(context, source.ToLocalChecked(), &sorigin);
2876 if (script.IsEmpty())
2877 return Undefined(isolate);
2878
2879 const Handle<String> __date__ = String::NewFromUtf8(isolate, "__DATE__");
2880 const Handle<String> __file__ = String::NewFromUtf8(isolate, "__FILE__");
2881
2882 Handle<Value> save_date;
2883 Handle<Value> save_file;
2884
2885 //*NEW* FIMXE: context->Global() or isolate->GetCurrentContext()
2886 Handle<Object> global = /*Context::GetCurrent()*/context->Global();
2887 if (!global.IsEmpty())
2888 {
2889 struct stat attrib;
2890 if (stat(file.c_str(), &attrib)==0)
2891 {
2892 save_date = global->Get(__date__);
2893 save_file = global->Get(__file__);
2894
2895 global->Set(__file__, String::NewFromUtf8(isolate, file.c_str()));
2896
2897 const Local<Value> date = Date::New(isolate, attrib.st_mtime*1000);
2898 if (!date.IsEmpty())
2899 global->Set(__date__, date);
2900 }
2901 }
2902
2903 MaybeLocal<Value> rc = script.ToLocalChecked()->Run(context);
2904 if (rc.IsEmpty())
2905 return Undefined(isolate);
2906
2907 if (!global.IsEmpty() && !save_date.IsEmpty())
2908 {
2909 global->/*Force*/Set(__date__, save_date);
2910 global->/*Force*/Set(__file__, save_file);
2911 }
2912
2913 return handle_scope.Escape(rc.ToLocalChecked());
2914}
2915
2916void InterpreterV8::ExecuteConsole()
2917{
2918 WindowLog lout;
2919 lout << "\n " << kUnderline << " JavaScript interpreter " << kReset << " (enter '.q' to quit)\n" << endl;
2920
2921 Readline::StaticPushHistory("java.his");
2922
2923 string command;
2924 while (1)
2925 {
2926 Isolate *isolate = Isolate::GetCurrent();
2927
2928 // Create a local handle scope so that left-overs from single
2929 // console inputs will not fill up the memory
2930 const HandleScope handle_scope(isolate);
2931
2932 // Unlocking is necessary for the preemption to work
2933 const Unlocker global_unlock(isolate);
2934
2935 const string buffer = Tools::Trim(Readline::StaticPrompt(command.empty() ? "JS> " : " \\> "));
2936 if (buffer==".q")
2937 break;
2938
2939 // buffer empty, do nothing
2940 if (buffer.empty())
2941 continue;
2942
2943 // Compose command
2944 if (!command.empty())
2945 command += ' ';
2946 command += buffer;
2947
2948 // If line ends with a backslash, allow addition of next line
2949 auto back = command.rbegin();
2950 if (*back=='\\')
2951 {
2952 *back = ' ';
2953 command = Tools::Trim(command);
2954 continue;
2955 }
2956
2957 // Locking is necessary to be able to execute java script code
2958 const Locker lock(isolate);
2959
2960 // Catch exceptions during code compilation
2961 TryCatch exception(isolate);
2962
2963 // Execute code which was entered
2964 const Handle<Value> rc = ExecuteCode(command, "console");
2965
2966 // If all went well and the result wasn't undefined then print
2967 // the returned value.
2968 if (!rc->IsUndefined() && !rc->IsFunction())
2969 JsResult(*String::Utf8Value(rc));
2970
2971 if (!HandleException(exception, "console"))
2972 lout << endl;
2973
2974 // Stop all other threads
2975 for (auto it=fThreadIsolates.begin(); it!=fThreadIsolates.end(); it++)
2976 (*it)->TerminateExecution();
2977
2978 // Allow the java scripts (threads) to run and hence to terminate
2979 const Unlocker unlock(isolate);
2980
2981 // Wait until all threads are terminated
2982 while (!fThreadIsolates.empty())
2983 usleep(1000);
2984
2985 // command has been executed, collect new command
2986 command = "";
2987 }
2988
2989 lout << endl;
2990
2991 Readline::StaticPopHistory("java.his");
2992}
2993
2994// ==========================================================================
2995// CORE
2996// ==========================================================================
2997
2998InterpreterV8::InterpreterV8() : fMainThread(nullptr)
2999{
3000 const string ver(V8::GetVersion());
3001
3002 typedef boost::char_separator<char> separator;
3003 const boost::tokenizer<separator> tokenizer(ver, separator("."));
3004
3005 const vector<string> tok(tokenizer.begin(), tokenizer.end());
3006
3007 const int major = tok.size()>0 ? stol(tok[0]) : -1;
3008 const int minor = tok.size()>1 ? stol(tok[1]) : -1;
3009 const int build = tok.size()>2 ? stol(tok[2]) : -1;
3010
3011 if (major>3 || (major==3 && minor>9) || (major==3 && minor==9 && build>10))
3012 {
3013 const string argv = "--use_strict";
3014 V8::SetFlagsFromString(argv.c_str(), argv.size());
3015 }
3016
3017 /*
3018 const string argv1 = "--prof";
3019 const string argv2 = "--noprof-lazy";
3020
3021 V8::SetFlagsFromString(argv1.c_str(), argv1.size());
3022 V8::SetFlagsFromString(argv2.c_str(), argv2.size());
3023 */
3024
3025 if (!fPlatform)
3026 {
3027 // Initialize V8.
3028 V8::InitializeICU();
3029 //v8::V8::InitializeExternalStartupData(argv[0]);
3030 fPlatform = platform::NewDefaultPlatform();
3031 V8::InitializePlatform(fPlatform.get());
3032 V8::Initialize();
3033
3034 // Releases any resources used by v8 and stops any utility threads
3035 // that may be running. Note that disposing v8 is permanent,
3036 // it cannot be reinitialized.
3037 // It should generally not be necessary to dispose v8 before exiting
3038 // a process, this should happen automatically. It is only necessary
3039 // to use if the process needs the resources taken up by v8.
3040 //
3041 // v8::V8::Dispose();
3042 // v8::V8::ShutdownPlatform();
3043 }
3044
3045 This = this;
3046}
3047
3048Handle<Value> InterpreterV8::Constructor(const Arguments &args)
3049{
3050 vector<Handle<Value>> argv(args.Length());
3051
3052 for (int i=0; i<args.Length(); i++)
3053 argv[i] = args[i];
3054
3055 Local<Context> context = Isolate::GetCurrent()->GetCurrentContext();
3056 MaybeLocal<Value> obj = args.This()->CallAsConstructor(context, args.Length(), argv.data());
3057 if (obj.IsEmpty())
3058 return Undefined(Isolate::GetCurrent());
3059
3060 return obj.ToLocalChecked();
3061}
3062
3063
3064void InterpreterV8::AddFormatToGlobal()// const
3065{
3066 const string code =
3067 "String.form = function(str, arr)"
3068 "{"
3069 "var i = -1;"
3070 "function callback(exp, p0, p1, p2, p3, p4/*, pos, str*/)"
3071 "{"
3072 "if (exp=='%%')"
3073 "return '%';"
3074 ""
3075 "if (arr[++i]===undefined)"
3076 "return undefined;"
3077 ""
3078 "var exp = p2 ? parseInt(p2.substr(1)) : undefined;"
3079 "var base = p3 ? parseInt(p3.substr(1)) : undefined;"
3080 ""
3081 "var val;"
3082 "switch (p4)"
3083 "{"
3084 "case 's': val = arr[i]; break;"
3085 "case 'c': val = arr[i][0]; break;"
3086 "case 'f': val = parseFloat(arr[i]).toFixed(exp); break;"
3087 "case 'p': val = parseFloat(arr[i]).toPrecision(exp); break;"
3088 "case 'e': val = parseFloat(arr[i]).toExponential(exp); break;"
3089 "case 'x': val = parseInt(arr[i]).toString(base?base:16); break;"
3090 "case 'd': val = parseFloat(parseInt(arr[i], base?base:10).toPrecision(exp)).toFixed(0); break;"
3091 //"default:\n"
3092 //" throw new SyntaxError('Conversion specifier '+p4+' unknown.');\n"
3093 "}"
3094 ""
3095 "val = val==undefined || typeof(val)=='object' ? JSON.stringify(val) : val.toString(base);"
3096 ""
3097 "var sz = parseInt(p1); /* padding size */"
3098 "var ch = p1 && p1[0]=='0' ? '0' : ' '; /* isnull? */"
3099 "while (val.length<sz)"
3100 "val = p0 !== undefined ? val+ch : ch+val; /* isminus? */"
3101 ""
3102 "return val;"
3103 "}"
3104 ""
3105 "var regex = /%(-)?(0?[0-9]+)?([.][0-9]+)?([#][0-9]+)?([scfpexd])/g;"
3106 "return str.replace(regex, callback);"
3107 "}"
3108 "\n"
3109 "String.prototype.$ = function()"
3110 "{"
3111 "return String.form(this, Array.prototype.slice.call(arguments));"
3112 "}"
3113 "\n"
3114 "String.prototype.count = function(c,i)"
3115 "{"
3116 "return (this.match(new RegExp(c,i?'gi':'g'))||[]).length;"
3117 "}"/*
3118 "\n"
3119 "var format = function()"
3120 "{"
3121 "return dim.format(arguments[0], Array.prototype.slice.call(arguments,1));"
3122 "}"*/;
3123
3124 Isolate *isolate = Isolate::GetCurrent();
3125
3126 // Create a string containing the JavaScript source code.
3127 const Local<String> source = String::NewFromUtf8(isolate, code.c_str(), v8::NewStringType::kNormal).ToLocalChecked();;
3128
3129 // Compile the source code.
3130 const Local<Script> script = Script::Compile(isolate->GetCurrentContext(), source).ToLocalChecked();
3131
3132 // Run the script to get the result.
3133 if (!script.IsEmpty())
3134 script->Run(isolate->GetCurrentContext()).IsEmpty(); // Is empty to avoid warning on usused return value
3135}
3136
3137void InterpreterV8::JsLoad(const std::string &)
3138{
3139 Readline::SetScriptDepth(1);
3140}
3141
3142void InterpreterV8::JsEnd(const std::string &)
3143{
3144 Readline::SetScriptDepth(0);
3145}
3146
3147// For debugging purposes only
3148// void BeforeCallEnteredCallbackF(Isolate*isolate) { cout << ">> " << isolate << endl; }
3149// void CallCompletedCallbackF(Isolate*isolate) { cout << "<< " << isolate << endl; }
3150
3151bool InterpreterV8::JsRun(const string &filename, const map<string, string> &map)
3152{
3153 JsPrint(string("JavaScript Engine V8 ")+V8::GetVersion());
3154 JsLoad(filename);
3155
3156 // Create a new Isolate and make it the current one.
3157 Isolate::CreateParams create_params;
3158 create_params.array_buffer_allocator = ArrayBuffer::Allocator::NewDefaultAllocator();
3159
3160 Isolate* isolate = Isolate::New(create_params);
3161
3162 // For debugging purposes only
3163 // isolate->AddBeforeCallEnteredCallback ( BeforeCallEnteredCallbackF);
3164 // isolate->AddCallCompletedCallback ( CallCompletedCallbackF);
3165
3166 {
3167 // #
3168 // # Fatal error in HandleScope::HandleScope
3169 // # Entering the V8 API without proper locking in place
3170 // #
3171 const Locker lock(isolate);
3172
3173 const Isolate::Scope isolate_scope(isolate);
3174
3175 // Create a stack-allocated handle scope.
3176 const HandleScope handle_scope(isolate);
3177
3178 // ===================================================================================
3179
3180 // Create a template for the global object.
3181 Handle<ObjectTemplate> dim = ObjectTemplate::New(isolate);
3182 dim->Set(String::NewFromUtf8(isolate, "log"), FunctionTemplate::New(isolate, WrapLog), ReadOnly);
3183 dim->Set(String::NewFromUtf8(isolate, "alarm"), FunctionTemplate::New(isolate, WrapAlarm), ReadOnly);
3184 dim->Set(String::NewFromUtf8(isolate, "wait"), FunctionTemplate::New(isolate, WrapWait), ReadOnly);
3185 dim->Set(String::NewFromUtf8(isolate, "send"), FunctionTemplate::New(isolate, WrapSend), ReadOnly);
3186 dim->Set(String::NewFromUtf8(isolate, "state"), FunctionTemplate::New(isolate, WrapState), ReadOnly);
3187 dim->Set(String::NewFromUtf8(isolate, "version"), Integer::New(isolate, DIM_VERSION_NUMBER), ReadOnly);
3188 dim->Set(String::NewFromUtf8(isolate, "getStates"), FunctionTemplate::New(isolate, WrapGetStates), ReadOnly);
3189 dim->Set(String::NewFromUtf8(isolate, "getDescription"), FunctionTemplate::New(isolate, WrapGetDescription), ReadOnly);
3190 dim->Set(String::NewFromUtf8(isolate, "getServices"), FunctionTemplate::New(isolate, WrapGetServices), ReadOnly);
3191
3192 Handle<ObjectTemplate> dimctrl = ObjectTemplate::New(isolate);
3193 dimctrl->Set(String::NewFromUtf8(isolate, "defineState"), FunctionTemplate::New(isolate, WrapNewState), ReadOnly);
3194 dimctrl->Set(String::NewFromUtf8(isolate, "setState"), FunctionTemplate::New(isolate, WrapSetState), ReadOnly);
3195 dimctrl->Set(String::NewFromUtf8(isolate, "getState"), FunctionTemplate::New(isolate, WrapGetState), ReadOnly);
3196 dimctrl->Set(String::NewFromUtf8(isolate, "setInterruptHandler"), FunctionTemplate::New(isolate, WrapSetInterrupt), ReadOnly);
3197 dimctrl->Set(String::NewFromUtf8(isolate, "triggerInterrupt"), FunctionTemplate::New(isolate, WrapTriggerInterrupt), ReadOnly);
3198
3199 Handle<ObjectTemplate> v8 = ObjectTemplate::New(isolate);
3200 v8->Set(String::NewFromUtf8(isolate, "sleep"), FunctionTemplate::New(isolate, WrapSleep), ReadOnly);
3201 v8->Set(String::NewFromUtf8(isolate, "timeout"), FunctionTemplate::New(isolate, WrapTimeout), ReadOnly);
3202 v8->Set(String::NewFromUtf8(isolate, "version"), String::NewFromUtf8(isolate, V8::GetVersion()), ReadOnly);
3203
3204 Handle<ObjectTemplate> console = ObjectTemplate::New(isolate);
3205 console->Set(String::NewFromUtf8(isolate, "out"), FunctionTemplate::New(isolate, WrapOut), ReadOnly);
3206 console->Set(String::NewFromUtf8(isolate, "warn"), FunctionTemplate::New(isolate, WrapWarn), ReadOnly);
3207
3208 Handle<ObjectTemplate> onchange = ObjectTemplate::New(isolate);
3209 //onchange->SetNamedPropertyHandler(OnChangeGet, WrapOnChangeSet);
3210 onchange->SetHandler(v8::NamedPropertyHandlerConfiguration(OnChangeGet, WrapOnChangeSet));
3211 dim->Set(String::NewFromUtf8(isolate, "onchange"), onchange);
3212
3213 Handle<ObjectTemplate> global = ObjectTemplate::New(isolate);
3214 global->Set(String::NewFromUtf8(isolate, "v8"), v8, ReadOnly);
3215 global->Set(String::NewFromUtf8(isolate, "dim"), dim, ReadOnly);
3216 global->Set(String::NewFromUtf8(isolate, "dimctrl"), dimctrl, ReadOnly);
3217 global->Set(String::NewFromUtf8(isolate, "console"), console, ReadOnly);
3218 global->Set(String::NewFromUtf8(isolate, "include"), FunctionTemplate::New(isolate, WrapInclude), ReadOnly);
3219 global->Set(String::NewFromUtf8(isolate, "exit"), FunctionTemplate::New(isolate, WrapExit), ReadOnly);
3220
3221 Handle<FunctionTemplate> sub = FunctionTemplate::New(isolate, WrapSubscription);
3222 sub->SetClassName(String::NewFromUtf8(isolate, "Subscription"));
3223 sub->InstanceTemplate()->SetInternalFieldCount(1);
3224 global->Set(String::NewFromUtf8(isolate, "Subscription"), sub, ReadOnly);
3225
3226#ifdef HAVE_SQL
3227 Handle<FunctionTemplate> db = FunctionTemplate::New(isolate, WrapDatabase);
3228 db->SetClassName(String::NewFromUtf8(isolate, "Database"));
3229 db->InstanceTemplate()->SetInternalFieldCount(1);
3230 global->Set(String::NewFromUtf8(isolate, "Database"), db, ReadOnly);
3231#endif
3232
3233 Handle<FunctionTemplate> thread = FunctionTemplate::New(isolate, WrapThread);
3234 thread->SetClassName(String::NewFromUtf8(isolate, "Thread"));
3235#if 0
3236 global->Set(String::NewFromUtf8(isolate, "Thread"), thread, ReadOnly);
3237#endif
3238
3239 Handle<FunctionTemplate> file = FunctionTemplate::New(isolate, WrapFile);
3240 file->SetClassName(String::NewFromUtf8(isolate, "File"));
3241 global->Set(String::NewFromUtf8(isolate, "File"), file, ReadOnly);
3242
3243 Handle<FunctionTemplate> evt = FunctionTemplate::New(isolate);
3244 evt->SetClassName(String::NewFromUtf8(isolate, "Event"));
3245 global->Set(String::NewFromUtf8(isolate, "Event"), evt, ReadOnly);
3246
3247 Handle<FunctionTemplate> desc = FunctionTemplate::New(isolate);
3248 desc->SetClassName(String::NewFromUtf8(isolate, "Description"));
3249 global->Set(String::NewFromUtf8(isolate, "Description"), desc, ReadOnly);
3250
3251 fTemplateEvent = evt;
3252 fTemplateDescription = desc;
3253
3254#ifdef HAVE_MAILX
3255 Handle<FunctionTemplate> mail = FunctionTemplate::New(isolate, ConstructorMail);
3256 mail->SetClassName(String::NewFromUtf8(isolate, "Mail"));
3257 global->Set(String::NewFromUtf8(isolate, "Mail"), mail, ReadOnly);
3258#endif
3259
3260#ifdef HAVE_CURL
3261 Handle<FunctionTemplate> curl = FunctionTemplate::New(isolate, ConstructorCurl);
3262 curl->SetClassName(String::NewFromUtf8(isolate, "Curl"));
3263 global->Set(String::NewFromUtf8(isolate, "Curl"), curl, ReadOnly);
3264#endif
3265
3266#ifdef HAVE_NOVA
3267 Handle<FunctionTemplate> sky = FunctionTemplate::New(isolate, ConstructorSky);
3268 sky->SetClassName(String::NewFromUtf8(isolate, "Sky"));
3269 sky->Set(String::NewFromUtf8(isolate, "dist"), FunctionTemplate::New(isolate, SkyDist), ReadOnly);
3270 global->Set(String::NewFromUtf8(isolate, "Sky"), sky, ReadOnly);
3271
3272 Handle<FunctionTemplate> loc = FunctionTemplate::New(isolate, ConstructorLocal);
3273 loc->SetClassName(String::NewFromUtf8(isolate, "Local"));
3274 loc->Set(String::NewFromUtf8(isolate, "dist"), FunctionTemplate::New(isolate, LocalDist), ReadOnly);
3275 global->Set(String::NewFromUtf8(isolate, "Local"), loc, ReadOnly);
3276
3277 Handle<FunctionTemplate> moon = FunctionTemplate::New(isolate, ConstructorMoon);
3278 moon->SetClassName(String::NewFromUtf8(isolate, "Moon"));
3279 moon->Set(String::NewFromUtf8(isolate, "disk"), FunctionTemplate::New(isolate, MoonDisk), ReadOnly);
3280 moon->Set(String::NewFromUtf8(isolate, "horizon"), FunctionTemplate::New(isolate, MoonHorizon), ReadOnly);
3281 global->Set(String::NewFromUtf8(isolate, "Moon"), moon, ReadOnly);
3282
3283 Handle<FunctionTemplate> sun = FunctionTemplate::New(isolate);
3284 sun->SetClassName(String::NewFromUtf8(isolate, "Sun"));
3285 sun->Set(String::NewFromUtf8(isolate, "horizon"), FunctionTemplate::New(isolate, SunHorizon), ReadOnly);
3286 global->Set(String::NewFromUtf8(isolate, "Sun"), sun, ReadOnly);
3287
3288 fTemplateLocal = loc;
3289 fTemplateSky = sky;
3290#endif
3291
3292 // ===================================================================================
3293
3294 // Create a new context with "global" as contents
3295 Local<Context> context = Context::New(isolate, nullptr, global);
3296 if (context.IsEmpty())
3297 {
3298 JsException("Creation of global context failed...");
3299 JsEnd(filename);
3300 return false;
3301 }
3302
3303 // Switch off eval(). It is not possible to track it's exceptions.
3304 context->AllowCodeGenerationFromStrings(false);
3305
3306 // Enter the context for compiling and running the hello world script.
3307 const Context::Scope scope(context);
3308
3309 // ===================================================================================
3310
3311 Handle<Array> args = Array::New(isolate, map.size());
3312 for (auto it=map.begin(); it!=map.end(); it++)
3313 args->Set(String::NewFromUtf8(isolate, it->first.c_str()), String::NewFromUtf8(isolate, it->second.c_str()));
3314
3315 context->Global()->Set(String::NewFromUtf8(isolate, "$"), args/*, ReadOnly*/);
3316 context->Global()->Set(String::NewFromUtf8(isolate, "arg"), args/*, ReadOnly*/);
3317
3318 const Local<Value> starttime = Date::New(isolate, Time().JavaDate());
3319 if (!starttime.IsEmpty())
3320 context->Global()->Set(String::NewFromUtf8(isolate, "__START__"), starttime/*, ReadOnly*/);
3321
3322 //V8::ResumeProfiler();
3323
3324 TryCatch exception(isolate);
3325
3326 AddFormatToGlobal();
3327
3328 // ===================================================================================
3329
3330 if (!exception.HasCaught())
3331 {
3332#if 0
3333 // Create a string containing the JavaScript source code.
3334 v8::Local<v8::String> source =
3335 v8::String::NewFromUtf8(isolate, "var x=5; 'Hello' + ', World!'+x", v8::NewStringType::kNormal).ToLocalChecked();
3336
3337 // Compile the source code.
3338 v8::Local<v8::Script> script =
3339 v8::Script::Compile(context, source).ToLocalChecked();
3340
3341 // Run the script to get the result.
3342 v8::Local<v8::Value> result =
3343 script->Run(context).ToLocalChecked();
3344
3345 // Convert the result to an UTF8 string and print it.
3346 v8::String::Utf8Value utf8(isolate, result);
3347 printf("%s\n", *utf8);
3348#endif
3349
3350 JsStart(filename);
3351
3352// Locker::StartPreemption(10);
3353
3354 // We are ready to go an can allow other threads to run...
3355 fMainThread = isolate;
3356
3357 if (filename.empty())
3358 {
3359 JsSetState(3);
3360 ExecuteConsole();
3361 }
3362 else
3363 {
3364 // We call script->Run because it is the only way to
3365 // catch exceptions.
3366 const Handle<String> source = String::NewFromUtf8(isolate, ("include('"+filename+"');").c_str());
3367 const Handle<String> origin = String::NewFromUtf8(isolate, "main");
3368 const Handle<Script> script = Script::Compile(source, origin);
3369 if (!script.IsEmpty())
3370 {
3371 JsSetState(3);
3372 script->Run();
3373 }
3374 }
3375
3376 // Before releasing the Lock, signal all other threads not to run again
3377 fMainThread = 0;
3378
3379 // Locker::StopPreemption();
3380/*OLD*/
3381 // Stop all other threads
3382 // for (auto it=fThreadIsolates.begin(); it!=fThreadIsolates.end(); it++)
3383 // (*it)->TerminateExecution();
3384 // fThreadIsolates.clear();
3385 }
3386
3387 // Handle an exception
3388 HandleException(exception, "main");
3389 }
3390
3391 // Just to be on the safe side:
3392 // Terminate all other threads once they try to run
3393 isolate->AddBeforeCallEnteredCallback(TerminateExecution);
3394
3395 // Wait until all threads have finished so that we can safely(?) proceed
3396 while (isolate->IsInUse())
3397 usleep(50);
3398
3399 // The threads are started already and wait to get the lock
3400 // So we have to unlock (manual preemtion) so that they get
3401 // the signal to terminate.
3402 /*
3403 {
3404 const Unlocker unlock(isolate);
3405
3406 for (auto it=fThreads.begin(); it!=fThreads.end(); it++)
3407 {
3408 it->join();
3409 fThreads.clear();
3410 }*/
3411
3412 // Now we can dispose the persistent interrupt handler
3413 fInterruptCallback.Reset();
3414
3415 // Now we can dispose all persistent handles from state callbacks
3416 fStateCallbacks.clear();
3417
3418 // ... and from the onchange callbacks
3419 fReverseMap.clear();
3420
3421#ifdef HAVE_SQL
3422 // ...and close all database handles
3423 for (auto it=fDatabases.begin(); it!=fDatabases.end(); it++)
3424 delete *it;
3425 fDatabases.clear();
3426#endif
3427
3428 //fStates.clear();
3429
3430 isolate->Dispose();
3431
3432 delete create_params.array_buffer_allocator;
3433 create_params.array_buffer_allocator = 0;
3434
3435 JsEnd(filename);
3436
3437 return 0;
3438}
3439
3440void InterpreterV8::JsStop()
3441{
3442 Isolate *isolate = This ? This->fMainThread : nullptr;
3443 if (!isolate)
3444 return;
3445
3446 // Terminate the main thread immediately
3447 isolate->TerminateExecution();
3448 // Terminate all other threads once they try to run
3449 isolate->AddBeforeCallEnteredCallback(TerminateExecution);
3450}
3451
3452vector<string> InterpreterV8::JsGetCommandList(const char *, int) const
3453{
3454 vector<string> rc;
3455
3456 rc.emplace_back("for (");
3457 rc.emplace_back("while (");
3458 rc.emplace_back("if (");
3459 rc.emplace_back("switch (");
3460 rc.emplace_back("case ");
3461 rc.emplace_back("var ");
3462 rc.emplace_back("function ");
3463 rc.emplace_back("Date(");
3464 rc.emplace_back("new Date(");
3465 rc.emplace_back("'use strict';");
3466 rc.emplace_back("undefined");
3467 rc.emplace_back("null");
3468 rc.emplace_back("delete ");
3469 rc.emplace_back("JSON.stringify(");
3470 rc.emplace_back("JSON.parse(");
3471
3472 rc.emplace_back("dim.log(");
3473 rc.emplace_back("dim.alarm(");
3474 rc.emplace_back("dim.wait(");
3475 rc.emplace_back("dim.send(");
3476 rc.emplace_back("dim.state(");
3477 rc.emplace_back("dim.version");
3478 rc.emplace_back("dim.getStates(");
3479 rc.emplace_back("dim.getDescription(");
3480 rc.emplace_back("dim.getServices(");
3481
3482 rc.emplace_back("dimctrl.defineState(");
3483 rc.emplace_back("dimctrl.setState(");
3484 rc.emplace_back("dimctrl.getState(");
3485 rc.emplace_back("dimctrl.setInterruptHandler(");
3486 rc.emplace_back("dimctrl.triggerInterrupt(");
3487
3488 rc.emplace_back("v8.sleep(");
3489 rc.emplace_back("v8.timeout(");
3490 rc.emplace_back("v8.version");
3491
3492 rc.emplace_back("console.out(");
3493 rc.emplace_back("console.warn(");
3494
3495 rc.emplace_back("include(");
3496 rc.emplace_back("exit()");
3497
3498#ifdef HAVE_SQL
3499 rc.emplace_back("Database(");
3500 rc.emplace_back("new Database(");
3501
3502 rc.emplace_back(".table");
3503 rc.emplace_back(".user");
3504 rc.emplace_back(".database");
3505 rc.emplace_back(".port");
3506 rc.emplace_back(".query");
3507#endif
3508
3509 rc.emplace_back("Subscription(");
3510 rc.emplace_back("new Subscription(");
3511
3512 rc.emplace_back("Thread(");
3513 rc.emplace_back("new Thread(");
3514
3515 rc.emplace_back("File(");
3516 rc.emplace_back("new File(");
3517
3518 rc.emplace_back("Event(");
3519 rc.emplace_back("new Event(");
3520
3521 rc.emplace_back("Description(");
3522 rc.emplace_back("new Description(");
3523
3524#ifdef HAVE_MAILX
3525 rc.emplace_back("Mail(");
3526 rc.emplace_back("new Mail(");
3527
3528 rc.emplace_back(".subject");
3529 rc.emplace_back(".receipients");
3530 rc.emplace_back(".attachments");
3531 rc.emplace_back(".bcc");
3532 rc.emplace_back(".cc");
3533 rc.emplace_back(".text");
3534 rc.emplace_back(".send(");
3535#endif
3536
3537#ifdef HAVE_CURL
3538 rc.emplace_back("Curl(");
3539 rc.emplace_back("new Curl(");
3540
3541 rc.emplace_back(".url");
3542 rc.emplace_back(".user");
3543 rc.emplace_back(".data");
3544// rc.emplace_back(".send("); -> MAILX
3545#endif
3546
3547#ifdef HAVE_NOVA
3548 rc.emplace_back("Sky(");
3549 rc.emplace_back("new Sky(");
3550
3551 rc.emplace_back("Sky.dist");
3552 rc.emplace_back("Local(");
3553
3554 rc.emplace_back("new Local(");
3555 rc.emplace_back("Local.dist");
3556
3557 rc.emplace_back("Moon(");
3558 rc.emplace_back("new Moon(");
3559 rc.emplace_back("Moon.disk(");
3560 rc.emplace_back("Moon.horizon(");
3561
3562 rc.emplace_back("Sun.horizon(");
3563
3564 rc.emplace_back(".zd");
3565 rc.emplace_back(".az");
3566 rc.emplace_back(".ra");
3567 rc.emplace_back(".dec");
3568
3569 rc.emplace_back(".toLocal(");
3570 rc.emplace_back(".toSky(");
3571 rc.emplace_back(".rise");
3572 rc.emplace_back(".set");
3573 rc.emplace_back(".transit");
3574 rc.emplace_back(".isUp");
3575
3576 rc.emplace_back("horizon");
3577 rc.emplace_back("civil");
3578 rc.emplace_back("nautical");
3579 rc.emplace_back("astronomical");
3580#endif
3581
3582 rc.emplace_back(".server");
3583 rc.emplace_back(".service");
3584 rc.emplace_back(".name");
3585 rc.emplace_back(".isCommand");
3586 rc.emplace_back(".format");
3587 rc.emplace_back(".description");
3588 rc.emplace_back(".unit");
3589 rc.emplace_back(".delim");
3590 rc.emplace_back(".isOpen");
3591
3592 rc.emplace_back(".qos");
3593 rc.emplace_back(".size");
3594 rc.emplace_back(".counter");
3595 rc.emplace_back(".type");
3596 rc.emplace_back(".obj");
3597 rc.emplace_back(".data");
3598 rc.emplace_back(".comment");
3599 rc.emplace_back(".index");
3600 rc.emplace_back(".time");
3601 rc.emplace_back(".close()");
3602 rc.emplace_back(".onchange");
3603 rc.emplace_back(".get(");
3604
3605
3606 rc.emplace_back("__DATE__");
3607 rc.emplace_back("__FILE__");
3608
3609 return rc;
3610}
3611
3612#endif
3613
3614InterpreterV8 *InterpreterV8::This = 0;
3615unique_ptr<v8::Platform> InterpreterV8::fPlatform;
Note: See TracBrowser for help on using the repository browser.