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

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