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

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