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

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