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

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