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

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