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

Last change on this file since 14690 was 14690, checked in by tbretz, 12 years ago
Bettr support for circumpolar objects added 'isUp' proprty to th object returned by th horizon functions.
File size: 66.4 KB
Line 
1#include "InterpreterV8.h"
2
3#ifdef HAVE_V8
4
5#include <fstream>
6#include <sstream>
7#include <iomanip>
8
9#include <boost/tokenizer.hpp>
10
11#ifdef HAVE_NOVA
12#include <libnova/solar.h>
13#include <libnova/lunar.h>
14#include <libnova/transform.h>
15#endif
16
17#ifdef HAVE_SQL
18#include "Database.h"
19#endif
20
21#include <v8.h>
22
23#include "dim.h"
24#include "tools.h"
25#include "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()<2)
1560 hrz = LN_SOLAR_STANDART_HORIZON;
1561 if (args.Length()==2 && args[1]->IsNumber())
1562 hrz = args[1]->NumberValue();
1563 if (args.Length()==2 && args[1]->IsString())
1564 {
1565 char *arg = *String::AsciiValue(args[1]);
1566 transform(arg, arg+strlen(arg), arg, ::tolower);
1567
1568 const string str = Tools::Trim(arg);
1569 if (str==string("civil").substr(0, str.length()))
1570 hrz = LN_SOLAR_CIVIL_HORIZON;
1571 if (str==string("nautical").substr(0, str.length()))
1572 hrz = LN_SOLAR_NAUTIC_HORIZON;
1573 if (str==string("astronomical").substr(0, str.length()))
1574 hrz = LN_SOLAR_ASTRONOMICAL_HORIZON;
1575 }
1576
1577 if (!finite(hrz))
1578 return ThrowException(String::New("Second argument did not yield a valid number."));
1579
1580 const Local<Value> date =
1581 args.Length()==0 ? Date::New(Time().JavaDate()) : args[0];
1582 if (date.IsEmpty())
1583 return Undefined();
1584
1585 const uint64_t v = uint64_t(date->NumberValue());
1586 const Time utc(v/1000, v%1000);
1587
1588 ln_lnlat_posn obs;
1589 obs.lng = -(17.+53./60+26.525/3600);
1590 obs.lat = 28.+45./60+42.462/3600;
1591
1592 // get Julian day from local time
1593 const double JD = utc.JD();
1594
1595 ln_rst_time sun;
1596 const int rc = ln_get_solar_rst_horizon(JD-0.5, &obs, hrz, &sun);
1597 Handle<Object> rst = ConstructRiseSet(date, sun, rc);
1598 rst->Set(String::New("horizon"), Number::New(hrz));
1599 return handle_scope.Close(rst);
1600};
1601
1602Handle<Value> InterpreterV8::MoonHorizon(const Arguments &args)
1603{
1604 if (args.Length()>1)
1605 return ThrowException(String::New("Moon.horizon must not be called with one argument."));
1606
1607 HandleScope handle_scope;
1608
1609 const Local<Value> date =
1610 args.Length()==0 ? Date::New(Time().JavaDate()) : args[0];
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 moon;
1625 const int rc = ln_get_lunar_rst(JD-0.5, &obs, &moon);
1626 Handle<Object> rst = ConstructRiseSet(date, moon, rc);
1627 return handle_scope.Close(rst);
1628};
1629#endif
1630
1631// ==========================================================================
1632// Process control
1633// ==========================================================================
1634
1635bool InterpreterV8::HandleException(TryCatch& try_catch, const char *where)
1636{
1637 if (!try_catch.HasCaught() || !try_catch.CanContinue())
1638 return true;
1639
1640 const HandleScope handle_scope;
1641
1642 Handle<Value> except = try_catch.Exception();
1643 if (except.IsEmpty() || except->IsNull())
1644 return true;
1645
1646 const String::AsciiValue exception(except);
1647
1648 const Handle<Message> message = try_catch.Message();
1649 if (message.IsEmpty())
1650 return false;
1651
1652 ostringstream out;
1653
1654 if (!message->GetScriptResourceName()->IsUndefined())
1655 {
1656 // Print (filename):(line number): (message).
1657 const String::AsciiValue filename(message->GetScriptResourceName());
1658
1659 out << *filename;
1660 if (message->GetLineNumber()>0)
1661 out << ": l." << message->GetLineNumber();
1662 if (*exception)
1663 out << ": ";
1664 }
1665
1666 if (*exception)
1667 out << *exception;
1668
1669 out << " [" << where << "]";
1670
1671 JsException(out.str());
1672
1673 // Print line of source code.
1674 const String::AsciiValue sourceline(message->GetSourceLine());
1675 if (*sourceline)
1676 JsException(*sourceline);
1677
1678 // Print wavy underline (GetUnderline is deprecated).
1679 const int start = message->GetStartColumn();
1680 const int end = message->GetEndColumn();
1681
1682 out.str("");
1683 if (start>0)
1684 out << setfill(' ') << setw(start) << ' ';
1685 out << setfill('^') << setw(end-start) << '^';
1686
1687 JsException(out.str());
1688
1689 const String::AsciiValue stack_trace(try_catch.StackTrace());
1690 if (stack_trace.length()<=0)
1691 return false;
1692
1693 if (!*stack_trace)
1694 return false;
1695
1696 const string trace(*stack_trace);
1697
1698 typedef boost::char_separator<char> separator;
1699 const boost::tokenizer<separator> tokenizer(trace, separator("\n"));
1700
1701 // maybe skip: " at internal:"
1702
1703 auto it = tokenizer.begin();
1704 JsException("");
1705 while (it!=tokenizer.end())
1706 JsException(*it++);
1707
1708 return false;
1709}
1710
1711Handle<Value> InterpreterV8::ExecuteInternal(const string &code)
1712{
1713 // Try/catch and re-throw hides our internal code from
1714 // the displayed exception showing the origin and shows
1715 // the user function instead.
1716 TryCatch exception;
1717
1718 const Handle<Value> result = ExecuteCode(code);
1719 if (exception.HasCaught())
1720 exception.ReThrow();
1721
1722 return result;
1723}
1724
1725Handle<Value> InterpreterV8::ExecuteCode(const string &code, const string &file, bool main)
1726{
1727 HandleScope handle_scope;
1728
1729 const Handle<String> source = String::New(code.c_str(), code.size());
1730 const Handle<String> origin = String::New(file.c_str());
1731 if (source.IsEmpty())
1732 return Handle<Value>();
1733
1734 const Handle<Script> script = Script::Compile(source, origin);
1735 if (script.IsEmpty())
1736 return Handle<Value>();
1737
1738 if (main)
1739 JsSetState(3);
1740
1741 TryCatch exception;
1742
1743 const Handle<Value> result = script->Run();
1744
1745 if (exception.HasCaught())
1746 {
1747 if (file=="internal")
1748 return exception.ReThrow();
1749
1750 HandleException(exception, "code");
1751 return Handle<Value>();
1752 }
1753
1754 // If all went well and the result wasn't undefined then print
1755 // the returned value.
1756 if (!result.IsEmpty() && result->IsUndefined())
1757 JsResult(*String::AsciiValue(result));
1758
1759 return handle_scope.Close(result);
1760}
1761
1762bool InterpreterV8::ExecuteFile(const string &name, bool main)
1763{
1764 ifstream fin(name.c_str());
1765 if (!fin)
1766 {
1767 JsException("Error - Could not open file '"+name+"'");
1768 return false;
1769 }
1770
1771 string buffer;
1772 if (!getline(fin, buffer, '\0'))
1773 return true;
1774
1775 if (fin.fail())
1776 {
1777 JsException("Error - reading file.");
1778 return false;
1779 }
1780
1781 return !ExecuteCode(buffer, name, main).IsEmpty();
1782}
1783
1784// ==========================================================================
1785// CORE
1786// ==========================================================================
1787
1788InterpreterV8::InterpreterV8() : fThreadId(-1)
1789{
1790 const string ver(V8::GetVersion());
1791
1792 typedef boost::char_separator<char> separator;
1793 const boost::tokenizer<separator> tokenizer(ver, separator("."));
1794
1795 const vector<string> tok(tokenizer.begin(), tokenizer.end());
1796
1797 const int major = tok.size()>0 ? stol(tok[0]) : -1;
1798 const int minor = tok.size()>1 ? stol(tok[1]) : -1;
1799 const int build = tok.size()>2 ? stol(tok[2]) : -1;
1800
1801 if (major>3 || (major==3 && minor>9) || (major==3 && minor==9 && build>10))
1802 {
1803 const string argv = "--use_strict";
1804 V8::SetFlagsFromString(argv.c_str(), argv.size());
1805 }
1806
1807 This = this;
1808}
1809
1810Handle<Value> InterpreterV8::Constructor(/*Handle<FunctionTemplate> T,*/ const Arguments &args)
1811{
1812 Handle<Value> argv[args.Length()];
1813
1814 for (int i=0; i<args.Length(); i++)
1815 argv[i] = args[i];
1816
1817 return args.Callee()->NewInstance(args.Length(), argv);
1818}
1819
1820
1821void InterpreterV8::AddFormatToGlobal()// const
1822{
1823 const string code =
1824 "String.form = function(str, arr)"
1825 "{"
1826 "var i = -1;"
1827 "function callback(exp, p0, p1, p2, p3, p4/*, pos, str*/)"
1828 "{"
1829 "if (exp=='%%')"
1830 "return '%';"
1831 ""
1832 "if (arr[++i]===undefined)"
1833 "return undefined;"
1834 ""
1835 "var exp = p2 ? parseInt(p2.substr(1)) : undefined;"
1836 "var base = p3 ? parseInt(p3.substr(1)) : undefined;"
1837 ""
1838 "var val;"
1839 "switch (p4)"
1840 "{"
1841 "case 's': val = arr[i]; break;"
1842 "case 'c': val = arr[i][0]; break;"
1843 "case 'f': val = parseFloat(arr[i]).toFixed(exp); break;"
1844 "case 'p': val = parseFloat(arr[i]).toPrecision(exp); break;"
1845 "case 'e': val = parseFloat(arr[i]).toExponential(exp); break;"
1846 "case 'x': val = parseInt(arr[i]).toString(base?base:16); break;"
1847 "case 'd': val = parseFloat(parseInt(arr[i], base?base:10).toPrecision(exp)).toFixed(0); break;"
1848 //"default:\n"
1849 //" throw new SyntaxError('Conversion specifier '+p4+' unknown.');\n"
1850 "}"
1851 ""
1852 "val = typeof(val)=='object' ? JSON.stringify(val) : val.toString(base);"
1853 ""
1854 "var sz = parseInt(p1); /* padding size */"
1855 "var ch = p1 && p1[0]=='0' ? '0' : ' '; /* isnull? */"
1856 "while (val.length<sz)"
1857 "val = p0 !== undefined ? val+ch : ch+val; /* isminus? */"
1858 ""
1859 "return val;"
1860 "}"
1861 ""
1862 "var regex = /%(-)?(0?[0-9]+)?([.][0-9]+)?([#][0-9]+)?([scfpexd])/g;"
1863 "return str.replace(regex, callback);"
1864 "}"
1865 "\n"
1866 "String.prototype.$ = function()"
1867 "{"
1868 "return String.form(this, Array.prototype.slice.call(arguments));"
1869 "}"/*
1870 "\n"
1871 "var format = function()"
1872 "{"
1873 "return dim.format(arguments[0], Array.prototype.slice.call(arguments,1));"
1874 "}"*/;
1875
1876 // ExcuteInternal does not work properly here...
1877 // If suring compilation an exception is thrown, it will not work
1878 Handle<Script> script = Script::New(String::New(code.c_str()), String::New("internal"));
1879 if (!script.IsEmpty())
1880 script->Run();
1881}
1882
1883bool InterpreterV8::JsRun(const string &filename, const map<string, string> &map)
1884{
1885 const Locker locker;
1886 fThreadId = V8::GetCurrentThreadId();
1887
1888 JsPrint(string("JavaScript Engine V8 ")+V8::GetVersion());
1889
1890 JsLoad(filename);
1891
1892 const HandleScope handle_scope;
1893
1894 // Create a template for the global object.
1895 Handle<ObjectTemplate> dim = ObjectTemplate::New();
1896 dim->Set(String::New("print"), FunctionTemplate::New(WrapPrint), ReadOnly);
1897 dim->Set(String::New("alarm"), FunctionTemplate::New(WrapAlarm), ReadOnly);
1898 dim->Set(String::New("wait"), FunctionTemplate::New(WrapWait), ReadOnly);
1899 dim->Set(String::New("send"), FunctionTemplate::New(WrapSend), ReadOnly);
1900 dim->Set(String::New("state"), FunctionTemplate::New(WrapState), ReadOnly);
1901 dim->Set(String::New("version"), Integer::New(DIM_VERSION_NUMBER), ReadOnly);
1902
1903 Handle<ObjectTemplate> dimctrl = ObjectTemplate::New();
1904 dimctrl->Set(String::New("defineState"), FunctionTemplate::New(WrapNewState), ReadOnly);
1905 dimctrl->Set(String::New("setState"), FunctionTemplate::New(WrapSetState), ReadOnly);
1906 dimctrl->Set(String::New("getState"), FunctionTemplate::New(WrapGetState), ReadOnly);
1907
1908 Handle<ObjectTemplate> v8 = ObjectTemplate::New();
1909 v8->Set(String::New("sleep"), FunctionTemplate::New(WrapSleep), ReadOnly);
1910 v8->Set(String::New("version"), String::New(V8::GetVersion()), ReadOnly);
1911
1912 Handle<ObjectTemplate> console = ObjectTemplate::New();
1913 console->Set(String::New("out"), FunctionTemplate::New(WrapOut), ReadOnly);
1914
1915 Handle<ObjectTemplate> onchange = ObjectTemplate::New();
1916 onchange->SetNamedPropertyHandler(OnChangeGet, WrapOnChangeSet);
1917 dim->Set(String::New("onchange"), onchange);
1918
1919 Handle<ObjectTemplate> global = ObjectTemplate::New();
1920 global->Set(String::New("v8"), v8, ReadOnly);
1921 global->Set(String::New("dim"), dim, ReadOnly);
1922 global->Set(String::New("dimctrl"), dimctrl, ReadOnly);
1923 global->Set(String::New("console"), console, ReadOnly);
1924 global->Set(String::New("include"), FunctionTemplate::New(WrapInclude), ReadOnly);
1925 global->Set(String::New("exit"), FunctionTemplate::New(WrapExit), ReadOnly);
1926
1927 Handle<FunctionTemplate> sub = FunctionTemplate::New(WrapSubscription);
1928 sub->SetClassName(String::New("Subscription"));
1929 sub->InstanceTemplate()->SetInternalFieldCount(1);
1930 global->Set(String::New("Subscription"), sub, ReadOnly);
1931
1932 Handle<FunctionTemplate> db = FunctionTemplate::New(WrapDatabase);
1933 db->SetClassName(String::New("Database"));
1934 db->InstanceTemplate()->SetInternalFieldCount(1);
1935 global->Set(String::New("Database"), db, ReadOnly);
1936
1937 Handle<FunctionTemplate> thread = FunctionTemplate::New(WrapThread);
1938 thread->SetClassName(String::New("Thread"));
1939 global->Set(String::New("Thread"), thread, ReadOnly);
1940
1941 Handle<FunctionTemplate> file = FunctionTemplate::New(WrapFile);
1942 file->SetClassName(String::New("File"));
1943 global->Set(String::New("File"), file, ReadOnly);
1944
1945 Handle<FunctionTemplate> evt = FunctionTemplate::New();
1946 evt->SetClassName(String::New("Event"));
1947 global->Set(String::New("Event"), evt, ReadOnly);
1948
1949 fTemplateEvent = evt;
1950
1951#ifdef HAVE_NOVA
1952 Handle<FunctionTemplate> sky = FunctionTemplate::New(ConstructorSky);
1953 sky->SetClassName(String::New("Sky"));
1954 global->Set(String::New("Sky"), sky, ReadOnly);
1955
1956 Handle<FunctionTemplate> loc = FunctionTemplate::New(ConstructorLocal);
1957 loc->SetClassName(String::New("Local"));
1958 loc->Set(String::New("dist"), FunctionTemplate::New(LocalDist), ReadOnly);
1959 global->Set(String::New("Local"), loc, ReadOnly);
1960
1961 Handle<FunctionTemplate> moon = FunctionTemplate::New(ConstructorMoon);
1962 moon->SetClassName(String::New("Moon"));
1963 moon->Set(String::New("disk"), FunctionTemplate::New(MoonDisk), ReadOnly);
1964 moon->Set(String::New("horizon"), FunctionTemplate::New(MoonHorizon), ReadOnly);
1965 global->Set(String::New("Moon"), moon, ReadOnly);
1966
1967 Handle<FunctionTemplate> sun = FunctionTemplate::New();
1968 sun->SetClassName(String::New("Sun"));
1969 sun->Set(String::New("horizon"), FunctionTemplate::New(SunHorizon), ReadOnly);
1970 global->Set(String::New("Sun"), sun, ReadOnly);
1971
1972 fTemplateLocal = loc;
1973 fTemplateSky = sky;
1974#endif
1975
1976 // Persistent
1977 Persistent<Context> context = Context::New(NULL, global);
1978 if (context.IsEmpty())
1979 {
1980 JsException("Creation of global context failed...");
1981 JsEnd(filename);
1982 return false;
1983 }
1984
1985 // Switch off eval(). It is not possible to track it's exceptions.
1986 context->AllowCodeGenerationFromStrings(false);
1987
1988 Context::Scope scope(context);
1989
1990 Handle<Array> args = Array::New(map.size());
1991 for (auto it=map.begin(); it!=map.end(); it++)
1992 args->Set(String::New(it->first.c_str()), String::New(it->second.c_str()));
1993 context->Global()->Set(String::New("$"), args, ReadOnly);
1994 context->Global()->Set(String::New("arg"), args, ReadOnly);
1995
1996 //V8::ResumeProfiler();
1997
1998 TryCatch exception;
1999
2000 AddFormatToGlobal();
2001
2002 bool rc = true;
2003 if (!exception.HasCaught())
2004 {
2005 JsStart(filename);
2006
2007 Locker::StartPreemption(10);
2008
2009 rc &= ExecuteFile(filename, true);
2010
2011 Locker::StopPreemption();
2012
2013 // Stop all other threads
2014 for (auto it=fThreadIds.begin(); it!=fThreadIds.end(); it++)
2015 V8::TerminateExecution(*it);
2016 fThreadIds.clear();
2017 }
2018
2019 // Handle an exception
2020 rc &= HandleException(exception, "main");
2021
2022 // IsProfilerPaused()
2023 // V8::PauseProfiler();
2024
2025 // -----
2026 // This is how an exit handler could look like, but there is no way to interrupt it
2027 // -----
2028 // Handle<Object> obj = Handle<Object>::Cast(context->Global()->Get(String::New("dim")));
2029 // if (!obj.IsEmpty())
2030 // {
2031 // Handle<Value> onexit = obj->Get(String::New("onexit"));
2032 // if (!onexit->IsUndefined())
2033 // Handle<Function>::Cast(onexit)->NewInstance(0, NULL); // argc, argv
2034 // // Handle<Object> result = Handle<Function>::Cast(onexit)->NewInstance(0, NULL); // argc, argv
2035 // }
2036
2037 //context->Exit();
2038
2039 // The threads are started already and wait to get the lock
2040 // So we have to unlock (manual preemtion) so that they get
2041 // the signal to terminate.
2042 {
2043 const Unlocker unlock;
2044
2045 for (auto it=fThreads.begin(); it!=fThreads.end(); it++)
2046 it->join();
2047 fThreads.clear();
2048 }
2049
2050 // Now we can dispose all persistent handles from state callbacks
2051 for (auto it=fStateCallbacks.begin(); it!=fStateCallbacks.end(); it++)
2052 it->second.Dispose();
2053 fStateCallbacks.clear();
2054
2055 // Now we can dispose all persistent handles from reverse maps
2056 for (auto it=fReverseMap.begin(); it!=fReverseMap.end(); it++)
2057 it->second.Dispose();
2058 fReverseMap.clear();
2059
2060#ifdef HAVE_SQL
2061 // ...and close all database handles
2062 for (auto it=fDatabases.begin(); it!=fDatabases.end(); it++)
2063 delete *it;
2064 fDatabases.clear();
2065#endif
2066
2067 fStates.clear();
2068
2069 context.Dispose();
2070
2071 JsEnd(filename);
2072
2073 return true;
2074}
2075
2076void InterpreterV8::JsStop()
2077{
2078 Locker locker;
2079 V8::TerminateExecution(This->fThreadId);
2080}
2081
2082#endif
2083
2084InterpreterV8 *InterpreterV8::This = 0;
Note: See TracBrowser for help on using the repository browser.