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

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