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

Last change on this file since 19456 was 19456, checked in by tbretz, 8 weeks 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.