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

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