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

Last change on this file since 14693 was 14693, checked in by tbretz, 12 years ago
Exchanged the arguments to Sun.horizon
File size: 66.5 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::LocalDist(const Arguments &args)
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_zd = String::New("zd");
1269 const Handle<String> s_az = String::New("az");
1270
1271 const double zd0 = obj[0]->Get(s_zd)->NumberValue() * M_PI/180;
1272 const double az0 = obj[0]->Get(s_az)->NumberValue() * M_PI/180;
1273 const double zd1 = obj[1]->Get(s_zd)->NumberValue() * M_PI/180;
1274 const double az1 = obj[1]->Get(s_az)->NumberValue() * M_PI/180;
1275
1276 if (!finite(zd0) || !finite(zd1) || !finite(az0) || !finite(az1))
1277 return ThrowException(String::New("some values not valid or not finite."));
1278
1279 /*
1280 const double x0 = sin(zd0) * cos(az0); // az0 -= az0
1281 const double y0 = sin(zd0) * sin(az0); // az0 -= az0
1282 const double z0 = cos(zd0);
1283
1284 const double x1 = sin(zd1) * cos(az1); // az1 -= az0
1285 const double y1 = sin(zd1) * sin(az1); // az1 -= az0
1286 const double z1 = cos(zd1);
1287
1288 const double res = acos(x0*x1 + y0*y1 + z0*z1) * 180/M_PI;
1289 */
1290
1291 // cos(az1-az0) = cos(az1)*cos(az0) + sin(az1)*sin(az0)
1292
1293 const double x = sin(zd0) * sin(zd1) * cos(az1-az0);
1294 const double y = cos(zd0) * cos(zd1);
1295
1296 const double res = acos(x + y) * 180/M_PI;
1297
1298 return handle_scope.Close(Number::New(res));
1299}
1300
1301Handle<Value> InterpreterV8::MoonDisk(const Arguments &args)
1302{
1303 if (args.Length()>1)
1304 return ThrowException(String::New("disk must not be called with more than one argument."));
1305
1306 const uint64_t v = uint64_t(args[0]->NumberValue());
1307 const Time utc = args.Length()==0 ? Time() : Time(v/1000, v%1000);
1308
1309 return Number::New(ln_get_lunar_disk(utc.JD()));
1310}
1311
1312Handle<Value> InterpreterV8::LocalToSky(const Arguments &args)
1313{
1314 if (args.Length()>1)
1315 return ThrowException(String::New("toSky must not be called with more than one argument."));
1316
1317 ln_hrz_posn hrz;
1318 hrz.alt = 90-GetDataMember(args, "zd");
1319 hrz.az = GetDataMember(args, "az");
1320
1321 if (!finite(hrz.alt) || !finite(hrz.az))
1322 return ThrowException(String::New("zd and az must be finite."));
1323
1324 HandleScope handle_scope;
1325
1326 const Local<Value> date =
1327 args.Length()==0 ? Date::New(Time().JavaDate()) : args[0];
1328 if (date.IsEmpty())
1329 return Undefined();
1330
1331 const uint64_t v = uint64_t(date->NumberValue());
1332 const Time utc(v/1000, v%1000);
1333
1334 ln_lnlat_posn obs;
1335 obs.lng = -(17.+53./60+26.525/3600);
1336 obs.lat = 28.+45./60+42.462/3600;
1337
1338 ln_equ_posn equ;
1339 ln_get_equ_from_hrz(&hrz, &obs, utc.JD(), &equ);
1340
1341 // -----------------------------
1342
1343 Handle<Value> arg[] = { Number::New(equ.ra/15), Number::New(equ.dec), date };
1344 return handle_scope.Close(fTemplateSky->GetFunction()->NewInstance(3, arg));
1345}
1346
1347Handle<Value> InterpreterV8::SkyToLocal(const Arguments &args)
1348{
1349 if (args.Length()>1)
1350 return ThrowException(String::New("toLocal must not be called with more than one argument."));
1351
1352 ln_equ_posn equ;
1353 equ.ra = GetDataMember(args, "ra")*15;
1354 equ.dec = GetDataMember(args, "dec");
1355
1356 if (!finite(equ.ra) || !finite(equ.dec))
1357 return ThrowException(String::New("Ra and dec must be finite."));
1358
1359 HandleScope handle_scope;
1360
1361 const Local<Value> date =
1362 args.Length()==0 ? Date::New(Time().JavaDate()) : args[0];
1363 if (date.IsEmpty())
1364 return Undefined();
1365
1366 const uint64_t v = uint64_t(date->NumberValue());
1367 const Time utc(v/1000, v%1000);
1368
1369 ln_lnlat_posn obs;
1370 obs.lng = -(17.+53./60+26.525/3600);
1371 obs.lat = 28.+45./60+42.462/3600;
1372
1373 ln_hrz_posn hrz;
1374 ln_get_hrz_from_equ(&equ, &obs, utc.JD(), &hrz);
1375
1376 Handle<Value> arg[] = { Number::New(90-hrz.alt), Number::New(hrz.az), date };
1377 return handle_scope.Close(fTemplateLocal->GetFunction()->NewInstance(3, arg));
1378}
1379
1380Handle<Value> InterpreterV8::MoonToLocal(const Arguments &args)
1381{
1382 if (args.Length()>0)
1383 return ThrowException(String::New("toLocal must not be called with arguments."));
1384
1385 ln_equ_posn equ;
1386 equ.ra = GetDataMember(args, "ra")*15;
1387 equ.dec = GetDataMember(args, "dec");
1388
1389 if (!finite(equ.ra) || !finite(equ.dec))
1390 return ThrowException(String::New("ra and dec must be finite."));
1391
1392 HandleScope handle_scope;
1393
1394 const Local<Value> date = args.This()->Get(String::New("time"));
1395 if (date.IsEmpty() || date->IsUndefined() )
1396 return Undefined();
1397
1398 const uint64_t v = uint64_t(date->NumberValue());
1399 const Time utc(v/1000, v%1000);
1400
1401 ln_lnlat_posn obs;
1402 obs.lng = -(17.+53./60+26.525/3600);
1403 obs.lat = 28.+45./60+42.462/3600;
1404
1405 ln_hrz_posn hrz;
1406 ln_get_hrz_from_equ(&equ, &obs, utc.JD(), &hrz);
1407
1408 Handle<Value> arg[] = { Number::New(90-hrz.alt), Number::New(hrz.az), date };
1409 return handle_scope.Close(fTemplateLocal->GetFunction()->NewInstance(3, arg));
1410}
1411
1412Handle<Value> InterpreterV8::ConstructorMoon(const Arguments &args)
1413{
1414 if (args.Length()>1)
1415 return ThrowException(String::New("Moon constructor must not be called with more than one argument."));
1416
1417 HandleScope handle_scope;
1418
1419 const Local<Value> date =
1420 args.Length()==0 ? Date::New(Time().JavaDate()) : args[0];
1421 if (date.IsEmpty())
1422 return Undefined();
1423
1424 const uint64_t v = uint64_t(date->NumberValue());
1425 const Time utc(v/1000, v%1000);
1426
1427 ln_equ_posn equ;
1428 ln_get_lunar_equ_coords_prec(utc.JD(), &equ, 0.01);
1429
1430 // ----------------------------
1431
1432 if (!args.IsConstructCall())
1433 return Constructor(args);
1434
1435 Handle<Function> function =
1436 FunctionTemplate::New(MoonToLocal)->GetFunction();
1437 if (function.IsEmpty())
1438 return Undefined();
1439
1440 Handle<Object> self = args.This();
1441 self->Set(String::New("ra"), Number::New(equ.ra/15), ReadOnly);
1442 self->Set(String::New("dec"), Number::New(equ.dec), ReadOnly);
1443 self->Set(String::New("toLocal"), function, ReadOnly);
1444 self->Set(String::New("time"), date, ReadOnly);
1445
1446 return handle_scope.Close(self);
1447}
1448
1449Handle<Value> InterpreterV8::ConstructorSky(const Arguments &args)
1450{
1451 if (args.Length()<2 || args.Length()>3)
1452 return ThrowException(String::New("Sky constructor takes two or three arguments."));
1453
1454 if (args.Length()==3 && !args[2]->IsDate())
1455 return ThrowException(String::New("Third argument must be a Date."));
1456
1457 const double ra = args[0]->NumberValue();
1458 const double dec = args[1]->NumberValue();
1459
1460 if (!finite(ra) || !finite(dec))
1461 return ThrowException(String::New("Both arguments to Sky must be valid numbers."));
1462
1463 // ----------------------------
1464
1465 HandleScope handle_scope;
1466
1467 if (!args.IsConstructCall())
1468 return Constructor(args);
1469
1470 Handle<Function> function =
1471 FunctionTemplate::New(SkyToLocal)->GetFunction();
1472 if (function.IsEmpty())
1473 return Undefined();
1474
1475 Handle<Object> self = args.This();
1476 self->Set(String::New("ra"), Number::New(ra), ReadOnly);
1477 self->Set(String::New("dec"), Number::New(dec), ReadOnly);
1478 self->Set(String::New("toLocal"), function, ReadOnly);
1479 if (args.Length()==3)
1480 self->Set(String::New("time"), args[2], ReadOnly);
1481
1482 return handle_scope.Close(self);
1483}
1484
1485Handle<Value> InterpreterV8::ConstructorLocal(const Arguments &args)
1486{
1487 if (args.Length()<2 || args.Length()>3)
1488 return ThrowException(String::New("Local constructor takes two or three arguments."));
1489
1490 if (args.Length()==3 && !args[2]->IsDate())
1491 return ThrowException(String::New("Third argument must be a Date."));
1492
1493 const double zd = args[0]->NumberValue();
1494 const double az = args[1]->NumberValue();
1495
1496 if (!finite(zd) || !finite(az))
1497 return ThrowException(String::New("Both arguments to Local must be valid numbers."));
1498
1499 // --------------------
1500
1501 HandleScope handle_scope;
1502
1503 if (!args.IsConstructCall())
1504 return Constructor(args);
1505
1506 Handle<Function> function =
1507 FunctionTemplate::New(LocalToSky)->GetFunction();
1508 if (function.IsEmpty())
1509 return Undefined();
1510
1511 Handle<Object> self = args.This();
1512 self->Set(String::New("zd"), Number::New(zd), ReadOnly);
1513 self->Set(String::New("az"), Number::New(az), ReadOnly);
1514 self->Set(String::New("toSky"), function, ReadOnly);
1515 if (args.Length()==3)
1516 self->Set(String::New("time"), args[2], ReadOnly);
1517
1518 return handle_scope.Close(self);
1519}
1520
1521Handle<Object> InterpreterV8::ConstructRiseSet(const Handle<Value> time, const ln_rst_time &rst, const bool &rc)
1522{
1523 Handle<Object> obj = Object::New();
1524 obj->Set(String::New("time"), time, ReadOnly);
1525
1526 const uint64_t v = uint64_t(time->NumberValue());
1527 const double jd = Time(v/1000, v%1000).JD();
1528
1529 const bool isUp = rc>0 ||
1530 (rst.rise<rst.set && (jd>rst.rise && jd<rst.set)) ||
1531 (rst.rise>rst.set && (jd<rst.set || jd>rst.rise));
1532
1533 obj->Set(String::New("isUp"), Boolean::New(rc>=0 && isUp), ReadOnly);
1534
1535 if (rc!=0)
1536 return obj;
1537
1538 Handle<Value> rise = Date::New(Time(rst.rise).JavaDate());
1539 Handle<Value> set = Date::New(Time(rst.set).JavaDate());
1540 Handle<Value> trans = Date::New(Time(rst.transit).JavaDate());
1541 if (rise.IsEmpty() || set.IsEmpty() || trans.IsEmpty())
1542 return Handle<Object>();
1543
1544 obj->Set(String::New("rise"), rise, ReadOnly);
1545 obj->Set(String::New("set"), set, ReadOnly);
1546 obj->Set(String::New("transit"), trans, ReadOnly);
1547
1548 return obj;
1549}
1550
1551Handle<Value> InterpreterV8::SunHorizon(const Arguments &args)
1552{
1553 if (args.Length()>2)
1554 return ThrowException(String::New("Sun.horizon must not be called with one or two arguments."));
1555
1556 HandleScope handle_scope;
1557
1558 double hrz = NAN;
1559 if (args.Length()==0 || args[0]->IsNull())
1560 hrz = LN_SOLAR_STANDART_HORIZON;
1561 if (args.Length()>0 && args[0]->IsNumber())
1562 hrz = args[0]->NumberValue();
1563 if (args.Length()>0 && args[0]->IsString())
1564 {
1565 string arg(Tools::Trim(*String::AsciiValue(args[0])));
1566 transform(arg.begin(), arg.end(), arg.begin(), ::tolower);
1567
1568 if (arg==string("horizon").substr(0, arg.length()))
1569 hrz = LN_SOLAR_STANDART_HORIZON;
1570 if (arg==string("civil").substr(0, arg.length()))
1571 hrz = LN_SOLAR_CIVIL_HORIZON;
1572 if (arg==string("nautical").substr(0, arg.length()))
1573 hrz = LN_SOLAR_NAUTIC_HORIZON;
1574 if (arg==string("astronomical").substr(0, arg.length()))
1575 hrz = LN_SOLAR_ASTRONOMICAL_HORIZON;
1576 }
1577
1578 if (!finite(hrz))
1579 return ThrowException(String::New("Second argument did not yield a valid number."));
1580
1581 const Local<Value> date =
1582 args.Length()<2 ? Date::New(Time().JavaDate()) : args[1];
1583 if (date.IsEmpty())
1584 return Undefined();
1585
1586 const uint64_t v = uint64_t(date->NumberValue());
1587 const Time utc(v/1000, v%1000);
1588
1589 ln_lnlat_posn obs;
1590 obs.lng = -(17.+53./60+26.525/3600);
1591 obs.lat = 28.+45./60+42.462/3600;
1592
1593 // get Julian day from local time
1594 const double JD = utc.JD();
1595
1596 ln_rst_time sun;
1597 const int rc = ln_get_solar_rst_horizon(JD-0.5, &obs, hrz, &sun);
1598 Handle<Object> rst = ConstructRiseSet(date, sun, rc);
1599 rst->Set(String::New("horizon"), Number::New(hrz));
1600 return handle_scope.Close(rst);
1601};
1602
1603Handle<Value> InterpreterV8::MoonHorizon(const Arguments &args)
1604{
1605 if (args.Length()>1)
1606 return ThrowException(String::New("Moon.horizon must not be called with one argument."));
1607
1608 HandleScope handle_scope;
1609
1610 const Local<Value> date =
1611 args.Length()==0 ? Date::New(Time().JavaDate()) : args[0];
1612 if (date.IsEmpty())
1613 return Undefined();
1614
1615 const uint64_t v = uint64_t(date->NumberValue());
1616 const Time utc(v/1000, v%1000);
1617
1618 ln_lnlat_posn obs;
1619 obs.lng = -(17.+53./60+26.525/3600);
1620 obs.lat = 28.+45./60+42.462/3600;
1621
1622 // get Julian day from local time
1623 const double JD = utc.JD();
1624
1625 ln_rst_time moon;
1626 const int rc = ln_get_lunar_rst(JD-0.5, &obs, &moon);
1627 Handle<Object> rst = ConstructRiseSet(date, moon, rc);
1628 return handle_scope.Close(rst);
1629};
1630#endif
1631
1632// ==========================================================================
1633// Process control
1634// ==========================================================================
1635
1636bool InterpreterV8::HandleException(TryCatch& try_catch, const char *where)
1637{
1638 if (!try_catch.HasCaught() || !try_catch.CanContinue())
1639 return true;
1640
1641 const HandleScope handle_scope;
1642
1643 Handle<Value> except = try_catch.Exception();
1644 if (except.IsEmpty() || except->IsNull())
1645 return true;
1646
1647 const String::AsciiValue exception(except);
1648
1649 const Handle<Message> message = try_catch.Message();
1650 if (message.IsEmpty())
1651 return false;
1652
1653 ostringstream out;
1654
1655 if (!message->GetScriptResourceName()->IsUndefined())
1656 {
1657 // Print (filename):(line number): (message).
1658 const String::AsciiValue filename(message->GetScriptResourceName());
1659
1660 out << *filename;
1661 if (message->GetLineNumber()>0)
1662 out << ": l." << message->GetLineNumber();
1663 if (*exception)
1664 out << ": ";
1665 }
1666
1667 if (*exception)
1668 out << *exception;
1669
1670 out << " [" << where << "]";
1671
1672 JsException(out.str());
1673
1674 // Print line of source code.
1675 const String::AsciiValue sourceline(message->GetSourceLine());
1676 if (*sourceline)
1677 JsException(*sourceline);
1678
1679 // Print wavy underline (GetUnderline is deprecated).
1680 const int start = message->GetStartColumn();
1681 const int end = message->GetEndColumn();
1682
1683 out.str("");
1684 if (start>0)
1685 out << setfill(' ') << setw(start) << ' ';
1686 out << setfill('^') << setw(end-start) << '^';
1687
1688 JsException(out.str());
1689
1690 const String::AsciiValue stack_trace(try_catch.StackTrace());
1691 if (stack_trace.length()<=0)
1692 return false;
1693
1694 if (!*stack_trace)
1695 return false;
1696
1697 const string trace(*stack_trace);
1698
1699 typedef boost::char_separator<char> separator;
1700 const boost::tokenizer<separator> tokenizer(trace, separator("\n"));
1701
1702 // maybe skip: " at internal:"
1703
1704 auto it = tokenizer.begin();
1705 JsException("");
1706 while (it!=tokenizer.end())
1707 JsException(*it++);
1708
1709 return false;
1710}
1711
1712Handle<Value> InterpreterV8::ExecuteInternal(const string &code)
1713{
1714 // Try/catch and re-throw hides our internal code from
1715 // the displayed exception showing the origin and shows
1716 // the user function instead.
1717 TryCatch exception;
1718
1719 const Handle<Value> result = ExecuteCode(code);
1720 if (exception.HasCaught())
1721 exception.ReThrow();
1722
1723 return result;
1724}
1725
1726Handle<Value> InterpreterV8::ExecuteCode(const string &code, const string &file, bool main)
1727{
1728 HandleScope handle_scope;
1729
1730 const Handle<String> source = String::New(code.c_str(), code.size());
1731 const Handle<String> origin = String::New(file.c_str());
1732 if (source.IsEmpty())
1733 return Handle<Value>();
1734
1735 const Handle<Script> script = Script::Compile(source, origin);
1736 if (script.IsEmpty())
1737 return Handle<Value>();
1738
1739 if (main)
1740 JsSetState(3);
1741
1742 TryCatch exception;
1743
1744 const Handle<Value> result = script->Run();
1745
1746 if (exception.HasCaught())
1747 {
1748 if (file=="internal")
1749 return exception.ReThrow();
1750
1751 HandleException(exception, "code");
1752 return Handle<Value>();
1753 }
1754
1755 // If all went well and the result wasn't undefined then print
1756 // the returned value.
1757 if (!result.IsEmpty() && result->IsUndefined())
1758 JsResult(*String::AsciiValue(result));
1759
1760 return handle_scope.Close(result);
1761}
1762
1763bool InterpreterV8::ExecuteFile(const string &name, bool main)
1764{
1765 ifstream fin(name.c_str());
1766 if (!fin)
1767 {
1768 JsException("Error - Could not open file '"+name+"'");
1769 return false;
1770 }
1771
1772 string buffer;
1773 if (!getline(fin, buffer, '\0'))
1774 return true;
1775
1776 if (fin.fail())
1777 {
1778 JsException("Error - reading file.");
1779 return false;
1780 }
1781
1782 return !ExecuteCode(buffer, name, main).IsEmpty();
1783}
1784
1785// ==========================================================================
1786// CORE
1787// ==========================================================================
1788
1789InterpreterV8::InterpreterV8() : fThreadId(-1)
1790{
1791 const string ver(V8::GetVersion());
1792
1793 typedef boost::char_separator<char> separator;
1794 const boost::tokenizer<separator> tokenizer(ver, separator("."));
1795
1796 const vector<string> tok(tokenizer.begin(), tokenizer.end());
1797
1798 const int major = tok.size()>0 ? stol(tok[0]) : -1;
1799 const int minor = tok.size()>1 ? stol(tok[1]) : -1;
1800 const int build = tok.size()>2 ? stol(tok[2]) : -1;
1801
1802 if (major>3 || (major==3 && minor>9) || (major==3 && minor==9 && build>10))
1803 {
1804 const string argv = "--use_strict";
1805 V8::SetFlagsFromString(argv.c_str(), argv.size());
1806 }
1807
1808 This = this;
1809}
1810
1811Handle<Value> InterpreterV8::Constructor(/*Handle<FunctionTemplate> T,*/ const Arguments &args)
1812{
1813 Handle<Value> argv[args.Length()];
1814
1815 for (int i=0; i<args.Length(); i++)
1816 argv[i] = args[i];
1817
1818 return args.Callee()->NewInstance(args.Length(), argv);
1819}
1820
1821
1822void InterpreterV8::AddFormatToGlobal()// const
1823{
1824 const string code =
1825 "String.form = function(str, arr)"
1826 "{"
1827 "var i = -1;"
1828 "function callback(exp, p0, p1, p2, p3, p4/*, pos, str*/)"
1829 "{"
1830 "if (exp=='%%')"
1831 "return '%';"
1832 ""
1833 "if (arr[++i]===undefined)"
1834 "return undefined;"
1835 ""
1836 "var exp = p2 ? parseInt(p2.substr(1)) : undefined;"
1837 "var base = p3 ? parseInt(p3.substr(1)) : undefined;"
1838 ""
1839 "var val;"
1840 "switch (p4)"
1841 "{"
1842 "case 's': val = arr[i]; break;"
1843 "case 'c': val = arr[i][0]; break;"
1844 "case 'f': val = parseFloat(arr[i]).toFixed(exp); break;"
1845 "case 'p': val = parseFloat(arr[i]).toPrecision(exp); break;"
1846 "case 'e': val = parseFloat(arr[i]).toExponential(exp); break;"
1847 "case 'x': val = parseInt(arr[i]).toString(base?base:16); break;"
1848 "case 'd': val = parseFloat(parseInt(arr[i], base?base:10).toPrecision(exp)).toFixed(0); break;"
1849 //"default:\n"
1850 //" throw new SyntaxError('Conversion specifier '+p4+' unknown.');\n"
1851 "}"
1852 ""
1853 "val = typeof(val)=='object' ? JSON.stringify(val) : val.toString(base);"
1854 ""
1855 "var sz = parseInt(p1); /* padding size */"
1856 "var ch = p1 && p1[0]=='0' ? '0' : ' '; /* isnull? */"
1857 "while (val.length<sz)"
1858 "val = p0 !== undefined ? val+ch : ch+val; /* isminus? */"
1859 ""
1860 "return val;"
1861 "}"
1862 ""
1863 "var regex = /%(-)?(0?[0-9]+)?([.][0-9]+)?([#][0-9]+)?([scfpexd])/g;"
1864 "return str.replace(regex, callback);"
1865 "}"
1866 "\n"
1867 "String.prototype.$ = function()"
1868 "{"
1869 "return String.form(this, Array.prototype.slice.call(arguments));"
1870 "}"/*
1871 "\n"
1872 "var format = function()"
1873 "{"
1874 "return dim.format(arguments[0], Array.prototype.slice.call(arguments,1));"
1875 "}"*/;
1876
1877 // ExcuteInternal does not work properly here...
1878 // If suring compilation an exception is thrown, it will not work
1879 Handle<Script> script = Script::New(String::New(code.c_str()), String::New("internal"));
1880 if (!script.IsEmpty())
1881 script->Run();
1882}
1883
1884bool InterpreterV8::JsRun(const string &filename, const map<string, string> &map)
1885{
1886 const Locker locker;
1887 fThreadId = V8::GetCurrentThreadId();
1888
1889 JsPrint(string("JavaScript Engine V8 ")+V8::GetVersion());
1890
1891 JsLoad(filename);
1892
1893 const HandleScope handle_scope;
1894
1895 // Create a template for the global object.
1896 Handle<ObjectTemplate> dim = ObjectTemplate::New();
1897 dim->Set(String::New("print"), FunctionTemplate::New(WrapPrint), ReadOnly);
1898 dim->Set(String::New("alarm"), FunctionTemplate::New(WrapAlarm), ReadOnly);
1899 dim->Set(String::New("wait"), FunctionTemplate::New(WrapWait), ReadOnly);
1900 dim->Set(String::New("send"), FunctionTemplate::New(WrapSend), ReadOnly);
1901 dim->Set(String::New("state"), FunctionTemplate::New(WrapState), ReadOnly);
1902 dim->Set(String::New("version"), Integer::New(DIM_VERSION_NUMBER), ReadOnly);
1903
1904 Handle<ObjectTemplate> dimctrl = ObjectTemplate::New();
1905 dimctrl->Set(String::New("defineState"), FunctionTemplate::New(WrapNewState), ReadOnly);
1906 dimctrl->Set(String::New("setState"), FunctionTemplate::New(WrapSetState), ReadOnly);
1907 dimctrl->Set(String::New("getState"), FunctionTemplate::New(WrapGetState), ReadOnly);
1908
1909 Handle<ObjectTemplate> v8 = ObjectTemplate::New();
1910 v8->Set(String::New("sleep"), FunctionTemplate::New(WrapSleep), ReadOnly);
1911 v8->Set(String::New("version"), String::New(V8::GetVersion()), ReadOnly);
1912
1913 Handle<ObjectTemplate> console = ObjectTemplate::New();
1914 console->Set(String::New("out"), FunctionTemplate::New(WrapOut), ReadOnly);
1915
1916 Handle<ObjectTemplate> onchange = ObjectTemplate::New();
1917 onchange->SetNamedPropertyHandler(OnChangeGet, WrapOnChangeSet);
1918 dim->Set(String::New("onchange"), onchange);
1919
1920 Handle<ObjectTemplate> global = ObjectTemplate::New();
1921 global->Set(String::New("v8"), v8, ReadOnly);
1922 global->Set(String::New("dim"), dim, ReadOnly);
1923 global->Set(String::New("dimctrl"), dimctrl, ReadOnly);
1924 global->Set(String::New("console"), console, ReadOnly);
1925 global->Set(String::New("include"), FunctionTemplate::New(WrapInclude), ReadOnly);
1926 global->Set(String::New("exit"), FunctionTemplate::New(WrapExit), ReadOnly);
1927
1928 Handle<FunctionTemplate> sub = FunctionTemplate::New(WrapSubscription);
1929 sub->SetClassName(String::New("Subscription"));
1930 sub->InstanceTemplate()->SetInternalFieldCount(1);
1931 global->Set(String::New("Subscription"), sub, ReadOnly);
1932
1933 Handle<FunctionTemplate> db = FunctionTemplate::New(WrapDatabase);
1934 db->SetClassName(String::New("Database"));
1935 db->InstanceTemplate()->SetInternalFieldCount(1);
1936 global->Set(String::New("Database"), db, ReadOnly);
1937
1938 Handle<FunctionTemplate> thread = FunctionTemplate::New(WrapThread);
1939 thread->SetClassName(String::New("Thread"));
1940 global->Set(String::New("Thread"), thread, ReadOnly);
1941
1942 Handle<FunctionTemplate> file = FunctionTemplate::New(WrapFile);
1943 file->SetClassName(String::New("File"));
1944 global->Set(String::New("File"), file, ReadOnly);
1945
1946 Handle<FunctionTemplate> evt = FunctionTemplate::New();
1947 evt->SetClassName(String::New("Event"));
1948 global->Set(String::New("Event"), evt, ReadOnly);
1949
1950 fTemplateEvent = evt;
1951
1952#ifdef HAVE_NOVA
1953 Handle<FunctionTemplate> sky = FunctionTemplate::New(ConstructorSky);
1954 sky->SetClassName(String::New("Sky"));
1955 global->Set(String::New("Sky"), sky, ReadOnly);
1956
1957 Handle<FunctionTemplate> loc = FunctionTemplate::New(ConstructorLocal);
1958 loc->SetClassName(String::New("Local"));
1959 loc->Set(String::New("dist"), FunctionTemplate::New(LocalDist), ReadOnly);
1960 global->Set(String::New("Local"), loc, ReadOnly);
1961
1962 Handle<FunctionTemplate> moon = FunctionTemplate::New(ConstructorMoon);
1963 moon->SetClassName(String::New("Moon"));
1964 moon->Set(String::New("disk"), FunctionTemplate::New(MoonDisk), ReadOnly);
1965 moon->Set(String::New("horizon"), FunctionTemplate::New(MoonHorizon), ReadOnly);
1966 global->Set(String::New("Moon"), moon, ReadOnly);
1967
1968 Handle<FunctionTemplate> sun = FunctionTemplate::New();
1969 sun->SetClassName(String::New("Sun"));
1970 sun->Set(String::New("horizon"), FunctionTemplate::New(SunHorizon), ReadOnly);
1971 global->Set(String::New("Sun"), sun, ReadOnly);
1972
1973 fTemplateLocal = loc;
1974 fTemplateSky = sky;
1975#endif
1976
1977 // Persistent
1978 Persistent<Context> context = Context::New(NULL, global);
1979 if (context.IsEmpty())
1980 {
1981 JsException("Creation of global context failed...");
1982 JsEnd(filename);
1983 return false;
1984 }
1985
1986 // Switch off eval(). It is not possible to track it's exceptions.
1987 context->AllowCodeGenerationFromStrings(false);
1988
1989 Context::Scope scope(context);
1990
1991 Handle<Array> args = Array::New(map.size());
1992 for (auto it=map.begin(); it!=map.end(); it++)
1993 args->Set(String::New(it->first.c_str()), String::New(it->second.c_str()));
1994 context->Global()->Set(String::New("$"), args, ReadOnly);
1995 context->Global()->Set(String::New("arg"), args, ReadOnly);
1996
1997 //V8::ResumeProfiler();
1998
1999 TryCatch exception;
2000
2001 AddFormatToGlobal();
2002
2003 bool rc = true;
2004 if (!exception.HasCaught())
2005 {
2006 JsStart(filename);
2007
2008 Locker::StartPreemption(10);
2009
2010 rc &= ExecuteFile(filename, true);
2011
2012 Locker::StopPreemption();
2013
2014 // Stop all other threads
2015 for (auto it=fThreadIds.begin(); it!=fThreadIds.end(); it++)
2016 V8::TerminateExecution(*it);
2017 fThreadIds.clear();
2018 }
2019
2020 // Handle an exception
2021 rc &= HandleException(exception, "main");
2022
2023 // IsProfilerPaused()
2024 // V8::PauseProfiler();
2025
2026 // -----
2027 // This is how an exit handler could look like, but there is no way to interrupt it
2028 // -----
2029 // Handle<Object> obj = Handle<Object>::Cast(context->Global()->Get(String::New("dim")));
2030 // if (!obj.IsEmpty())
2031 // {
2032 // Handle<Value> onexit = obj->Get(String::New("onexit"));
2033 // if (!onexit->IsUndefined())
2034 // Handle<Function>::Cast(onexit)->NewInstance(0, NULL); // argc, argv
2035 // // Handle<Object> result = Handle<Function>::Cast(onexit)->NewInstance(0, NULL); // argc, argv
2036 // }
2037
2038 //context->Exit();
2039
2040 // The threads are started already and wait to get the lock
2041 // So we have to unlock (manual preemtion) so that they get
2042 // the signal to terminate.
2043 {
2044 const Unlocker unlock;
2045
2046 for (auto it=fThreads.begin(); it!=fThreads.end(); it++)
2047 it->join();
2048 fThreads.clear();
2049 }
2050
2051 // Now we can dispose all persistent handles from state callbacks
2052 for (auto it=fStateCallbacks.begin(); it!=fStateCallbacks.end(); it++)
2053 it->second.Dispose();
2054 fStateCallbacks.clear();
2055
2056 // Now we can dispose all persistent handles from reverse maps
2057 for (auto it=fReverseMap.begin(); it!=fReverseMap.end(); it++)
2058 it->second.Dispose();
2059 fReverseMap.clear();
2060
2061#ifdef HAVE_SQL
2062 // ...and close all database handles
2063 for (auto it=fDatabases.begin(); it!=fDatabases.end(); it++)
2064 delete *it;
2065 fDatabases.clear();
2066#endif
2067
2068 fStates.clear();
2069
2070 context.Dispose();
2071
2072 JsEnd(filename);
2073
2074 return true;
2075}
2076
2077void InterpreterV8::JsStop()
2078{
2079 Locker locker;
2080 V8::TerminateExecution(This->fThreadId);
2081}
2082
2083#endif
2084
2085InterpreterV8 *InterpreterV8::This = 0;
Note: See TracBrowser for help on using the repository browser.