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

Last change on this file since 14602 was 14602, checked in by tbretz, 12 years ago
Some fixes from the latest changes; added some comments.
File size: 51.8 KB
Line 
1#include "InterpreterV8.h"
2
3#ifdef HAVE_V8
4
5#include <fstream>
6#include <sstream>
7#include <iomanip>
8
9#include <boost/tokenizer.hpp>
10
11#ifdef HAVE_NOVA
12#include <libnova/lunar.h>
13#include <libnova/transform.h>
14#endif
15
16#ifdef HAVE_SQL
17#include "Database.h"
18#endif
19
20#include <v8.h>
21
22using namespace std;
23using namespace v8;
24
25
26// ==========================================================================
27// Some documentation
28// ==========================================================================
29//
30// Threads:
31// --------
32// In most cases Js* and other calls to native C++ code could be wrapped
33// with an Unlocker to allow possible other JavaScipt 'threads' to run
34// during that time. However, all of these calls should take much less than
35// the preemption time of 10ms, so it would just be a waste of tim.
36//
37// Termination:
38// ------------
39// Each thread running V8 code needs to be signalled individually for
40// termination. Therefor a list of V8 thread ids is created.
41//
42// If termination has already be signalled, no thread should start running
43// anymore (thy could, e.g., wait for their locking). So after locking
44// it has to be checked if the thread was terminated already. Note
45// that all calls to Terminate() must be locked to ensure that fThreadId
46// is correct when it is checked.
47//
48// The current thread id must be added to fThreadIds _before_ any
49// function is called after Locking and before execution is given
50// back to JavaScript, e.g. in script->Run(). So until the thread
51// is added to the list Terminate will not be executed. If Terminate
52// is then executed, it is ensured that the current thread is
53// already in the list. If terminate has been called before
54// the Locking, the check for the validiy of fThreadId ensures that
55// nothing is executed.
56//
57// Empty handles:
58// --------------
59// If exceution is terminated, V8 calls might return with empty handles,
60// e.g. Date::New(). Therefore, the returned handles of these calls have to
61// be checked in all placed to avoid that V8 will core dump.
62//
63
64// ==========================================================================
65// Simple interface
66// ==========================================================================
67
68void InterpreterV8::Terminate()
69{
70 if (!Locker::IsLocked())
71 JsException("***** InterprterV8::Terminate call not locked *****");
72
73 for (auto it=fThreadIds.begin(); it!=fThreadIds.end(); it++)
74 V8::TerminateExecution(*it);
75 fThreadIds.clear();
76
77 if (fThreadId>=0)
78 {
79 V8::TerminateExecution(fThreadId);
80 fThreadId = -1;
81 }
82}
83
84Handle<Value> InterpreterV8::FuncExit(const Arguments &)
85{
86 Terminate();
87 return ThrowException(String::New("exit"));
88}
89
90Handle<Value> InterpreterV8::FuncSleep(const Arguments& args)
91{
92 if (args.Length()==0)
93 {
94 // Theoretically, the CPU usage can be reduced by maybe a factor
95 // of four using a larger value, but this also means that the
96 // JavaScript is locked for a longer time.
97 const Unlocker unlock;
98 usleep(1000);
99 return Undefined();
100 }
101
102 if (args.Length()!=1)
103 return ThrowException(String::New("Number of arguments must be exactly 1."));
104
105 if (!args[0]->IsUint32())
106 return ThrowException(String::New("Argument 1 must be an uint32."));
107
108 // Using a Javascript function has the advantage that it is fully
109 // interruptable without the need of C++ code
110
111 const string code =
112 "(function(){"
113 "var t=new Date();"
114 "while ((new Date()-t)<"+to_string(args[0]->Int32Value())+") dim.sleep();"
115 "})();";
116
117 HandleScope handle_scope;
118
119 const Handle<Script> script = Script::Compile(String::New(code.c_str()));
120 if (script.IsEmpty())
121 return Undefined();
122
123 return handle_scope.Close(script->Run());
124}
125
126void InterpreterV8::ThreadTimeout(Persistent<Function> func, uint32_t ms)
127{
128 const Locker lock;
129
130 if (fThreadId<0)
131 return;
132
133 const int id = V8::GetCurrentThreadId();
134 fThreadIds.insert(id);
135
136 const HandleScope handle_scope;
137
138 fGlobalContext->Enter();
139
140 TryCatch exception;
141
142 const Handle<Script> sleep = Script::Compile(String::New(("dim.sleep("+to_string(ms)+");").c_str()));
143 if (ms==0 || (!sleep.IsEmpty() && !sleep->Run().IsEmpty()))
144 {
145 Handle<Value> args[] = { };
146 func->Call(func, 0, args);
147 }
148
149 func.Dispose();
150 fThreadIds.erase(id);
151
152 if (!exception.HasCaught())
153 return;
154
155 ReportException(&exception);
156 Terminate();
157}
158
159Handle<Value> InterpreterV8::FuncTimeout(const Arguments& args)
160{
161 if (args.Length()!=2)
162 return ThrowException(String::New("Number of arguments must be at least 1."));
163
164 if (args.Length()==0)
165 return ThrowException(String::New("Number of arguments must be at least 1."));
166
167 if (!args[1]->IsFunction())
168 return ThrowException(String::New("Argument 1 not a function."));
169
170 if (!args[0]->IsUint32())
171 return ThrowException(String::New("Argument 0 not an uint32."));
172
173 Handle<Function> handle = Handle<Function>::Cast(args[1]);
174
175 Persistent<Function> func = Persistent<Function>::New(handle);
176
177 const uint32_t ms = args[0]->Uint32Value();
178
179 fTimeout.push_back(thread(bind(&InterpreterV8::ThreadTimeout, this, func, ms)));
180 return Undefined();
181}
182
183Handle<Value> InterpreterV8::FuncSend(const Arguments& args)
184{
185 if (args.Length()==0)
186 return ThrowException(String::New("Number of arguments must be at least 1."));
187
188 if (!args[0]->IsString())
189 return ThrowException(String::New("Argument 1 must be a string."));
190
191 HandleScope handle_scope;
192
193 const String::Utf8Value str(args[0]);
194
195 string command = *str;
196
197 // Escape all string arguments. All others can be kept as they are.
198 for (int i=1; i<args.Length(); i++)
199 {
200 const String::Utf8Value arg(args[i]);
201 if (args[i]->IsString())
202 command += " \""+string(*arg)+"\"";
203 else
204 command += " "+string(*arg);
205 }
206
207 return handle_scope.Close(Boolean::New(JsSend(command)));
208}
209
210// ==========================================================================
211// State control
212// ==========================================================================
213
214Handle<Value> InterpreterV8::FuncWait(const Arguments& args)
215{
216 if (args.Length()!=2 && args.Length()!=3)
217 return ThrowException(String::New("Number of arguments must be 2 or 3."));
218
219 if (!args[0]->IsString())
220 return ThrowException(String::New("Argument 1 not a string."));
221
222 if (!args[1]->IsInt32() && !args[1]->IsString())
223 return ThrowException(String::New("Argument 2 not an int32 and not a string."));
224
225 if (args.Length()==3 && !args[2]->IsUint32())
226 return ThrowException(String::New("Argument 3 not an uint32."));
227
228 // Using a Javascript function has the advantage that it is fully
229 // interruptable without the need of C++ code
230
231 const string index = args[1]->IsInt32() ? "s.index" : "s.name";
232 const bool timeout = args.Length()==3;
233 const string arg0 = *String::Utf8Value(args[0]);
234 const string state = args[1]->IsString() ? *String::Utf8Value(args[1]) : "";
235 const string arg1 = args[1]->IsString() ? ("\""+state+"\"") : to_string(args[1]->Int32Value());
236
237 if (arg0.find_first_of("\"'")!=string::npos)
238 return ThrowException(String::New("Server name must not contain quotation marks."));
239
240 if (args[1]->IsString())
241 if (state.find_first_of("\"'")!=string::npos)
242 return ThrowException(String::New("State name must not contain quotation marks."));
243
244 string code = "(function(name,state,ms)"
245 "{";
246 if (timeout)
247 code += "var t = new Date();";
248 code += "while (1)"
249 "{"
250 "var s = dim.state(name);"
251 "if(!"+index+")throw 'Waitig for state "+arg1+" of server "+arg0+" failed.';"
252 "if(state=="+index+")return true;";
253 if (timeout)
254 code += "if((new Date()-t)>ms)return false;";
255
256 code += "dim.sleep();"
257 "}"
258 "})('"+arg0+"',"+arg1;
259 if (timeout)
260 code += "," + to_string(args[2]->Int32Value());
261 code += ");";
262
263 HandleScope handle_scope;
264
265 // It is not strictly necessary to catch the exception, instead
266 // script->Run() could just be returned, but catching the
267 // exception allow to print the position in the file in
268 // the exception handler instead of just the posiiton in the script.
269 TryCatch exception;
270
271 const Handle<Script> script = Script::Compile(String::New(code.c_str()));
272 if (script.IsEmpty())
273 return Undefined();
274
275 const Handle<Value> result = script->Run();
276
277 return exception.HasCaught() ? exception.ReThrow() : handle_scope.Close(result);
278
279 /*
280 const string server = *String::Utf8Value(args[0]);
281 const int32_t state = args[1]->Int32Value();
282 const uint32_t millisec = args.Length()==3 ? args[2]->Int32Value() : 0;
283
284 const int rc = JsWait(server, state, millisec);
285
286 if (rc==0 || rc==1)
287 return Boolean::New(rc);
288
289 return ThrowException(String::New(("Waitig for state "+to_string(state)+" of server '"+server+"' failed.").c_str()));
290 */
291}
292
293Handle<Value> InterpreterV8::FuncState(const Arguments& args)
294{
295 if (args.Length()!=1)
296 return ThrowException(String::New("Number of arguments must be exactly 1."));
297
298 if (!args[0]->IsString())
299 return ThrowException(String::New("Argument 1 must be a string."));
300
301 // Return state.name/state.index
302
303 const String::Utf8Value str(args[0]);
304
305 const State rc = JsState(*str);
306
307 //if (rc.first<=-256)
308 // return Undefined();
309
310 HandleScope handle_scope;
311
312 Handle<ObjectTemplate> obj = ObjectTemplate::New();
313 obj->Set(String::New("index"), rc.index<=-256?Undefined():Integer::New(rc.index), ReadOnly);
314 obj->Set(String::New("name"), rc.index<=-256?Undefined():String::New(rc.name.c_str()), ReadOnly);
315
316 const Local<Value> date = Date::New(rc.time.JavaDate());
317 if (rc.index>-256 && !date.IsEmpty())
318 obj->Set(String::New("time"), date);
319
320 //obj->Set(String::New("toString"), String::New(("[Object state "+string(*str)+":"+to_string(rc.index)+"]").c_str()));
321
322 return handle_scope.Close(obj->NewInstance());
323}
324
325Handle<Value> InterpreterV8::FuncNewState(const Arguments& args)
326{
327 if (args.Length()<1 || args.Length()>3)
328 return ThrowException(String::New("Number of arguments must be 1, 2 or 3."));
329
330 if (!args[0]->IsUint32())
331 return ThrowException(String::New("Argument 1 must be an uint32."));
332 if (args.Length()>1 && !args[1]->IsString())
333 return ThrowException(String::New("Argument 2 must be a string."));
334 if (args.Length()>2 && !args[2]->IsString())
335 return ThrowException(String::New("Argument 3 must be a string."));
336
337 HandleScope handle_scope;
338
339 const uint32_t index = args[0]->Int32Value();
340 const string name = *String::Utf8Value(args[1]);
341 const string comment = *String::Utf8Value(args[2]);
342
343 if (index<10 || index>255)
344 return ThrowException(String::New("State must be in the range [10, 255]."));
345
346 if (name.empty())
347 return ThrowException(String::New("State name must not be empty."));
348
349 if (name.find_first_of(':')!=string::npos || name.find_first_of('=')!=string::npos)
350 return ThrowException(String::New("State name must not contain : or =."));
351
352 if (JsHasState(index) || JsHasState(name))
353 {
354 const string what =
355 "State index ["+to_string(index)+"] or name ["+name+"] already defined.";
356
357 return ThrowException(String::New(what.c_str()));
358 }
359
360 return handle_scope.Close(Boolean::New(JsNewState(index, name, comment)));
361}
362
363Handle<Value> InterpreterV8::FuncSetState(const Arguments& args)
364{
365 if (args.Length()!=1)
366 return ThrowException(String::New("Number of arguments must be exactly 1."));
367
368 if (!args[0]->IsUint32() && !args[0]->IsString())
369 return ThrowException(String::New("Argument must be an unint32 or a string."));
370
371 HandleScope handle_scope;
372
373 int index = -2;
374 if (args[0]->IsUint32())
375 {
376 index = args[0]->Int32Value();
377 }
378 else
379 {
380 const string name = *String::Utf8Value(args[0]);
381 index = JsGetState(name);
382 if (index==-2)
383 return ThrowException(String::New(("State '"+name+"' not found.").c_str()));
384 }
385
386 if (index<10 || index>255)
387 return ThrowException(String::New("State must be in the range [10, 255]."));
388
389 return handle_scope.Close(Boolean::New(JsSetState(index)));
390}
391
392// ==========================================================================
393// Internal functions
394// ==========================================================================
395
396
397// The callback that is invoked by v8 whenever the JavaScript 'print'
398// function is called. Prints its arguments on stdout separated by
399// spaces and ending with a newline.
400Handle<Value> InterpreterV8::FuncPrint(const Arguments& args)
401{
402 for (int i=0; i<args.Length(); i++)
403 {
404 const HandleScope handle_scope;
405
406 const String::Utf8Value str(args[i]);
407 if (*str)
408 JsPrint(*str);
409 }
410 return Undefined();
411}
412
413Handle<Value> InterpreterV8::FuncAlarm(const Arguments& args)
414{
415 for (int i=0; i<args.Length(); i++)
416 {
417 const HandleScope handle_scope;
418
419 const String::Utf8Value str(args[i]);
420 if (*str)
421 JsAlarm(*str);
422 }
423
424 if (args.Length()==0)
425 JsAlarm();
426
427 return Undefined();
428}
429
430Handle<Value> InterpreterV8::FuncOut(const Arguments& args)
431{
432 for (int i=0; i<args.Length(); i++)
433 {
434 const HandleScope handle_scope;
435
436 const String::Utf8Value str(args[i]);
437 if (*str)
438 JsOut(*str);
439 }
440 return Undefined();
441}
442
443// The callback that is invoked by v8 whenever the JavaScript 'load'
444// function is called. Loads, compiles and executes its argument
445// JavaScript file.
446Handle<Value> InterpreterV8::FuncInclude(const Arguments& args)
447{
448 for (int i=0; i<args.Length(); i++)
449 {
450 const HandleScope handle_scope;
451
452 const String::Utf8Value file(args[i]);
453 if (*file == NULL)
454 return ThrowException(String::New(("Error loading file '"+string(*file)+"'").c_str()));
455
456 if (!ExecuteFile(*file))
457 return ThrowException(String::New(("Execution of '"+string(*file)+"' failed").c_str()));
458 }
459 return Undefined();
460}
461
462Handle<Value> InterpreterV8::FuncVersion(const Arguments&)
463{
464 return String::New(V8::GetVersion());
465}
466
467// ==========================================================================
468// Database
469// ==========================================================================
470
471Handle<Value> InterpreterV8::FuncDbClose(const Arguments &args)
472{
473 HandleScope handle_scope;
474
475 void *ptr = External::Unwrap(args.This()->GetInternalField(0));
476 if (!ptr)
477 return handle_scope.Close(Boolean::New(false));
478
479#ifdef HAVE_SQL
480 Database *db = reinterpret_cast<Database*>(ptr);
481 auto it = find(fDatabases.begin(), fDatabases.end(), db);
482 fDatabases.erase(it);
483 delete db;
484#endif
485
486 args.This()->SetInternalField(0, External::New(0));
487
488 return handle_scope.Close(Boolean::New(true));
489}
490Handle<Value> InterpreterV8::FuncDbQuery(const Arguments &args)
491{
492 if (args.Length()==0)
493 return ThrowException(String::New("Arguments expected."));
494
495 HandleScope handle_scope;
496
497 void *ptr = External::Unwrap(args.This()->GetInternalField(0));
498 if (!ptr)
499 return Undefined();
500
501 string query;
502 for (int i=0; i<args.Length(); i++)
503 query += string(" ") + *String::Utf8Value(args[i]);
504 query.erase(0, 1);
505
506#ifdef HAVE_SQL
507 try
508 {
509 Database *db = reinterpret_cast<Database*>(ptr);
510
511 const mysqlpp::StoreQueryResult res = db->query(query).store();
512
513 Handle<Array> ret = Array::New();
514 if (ret.IsEmpty())
515 return Undefined();
516
517 ret->Set(String::New("table"), String::New(res.table()), ReadOnly);
518 ret->Set(String::New("query"), String::New(query.c_str()), ReadOnly);
519
520 Handle<Array> cols = Array::New();
521 if (cols.IsEmpty())
522 return Undefined();
523
524 int irow=0;
525 for (vector<mysqlpp::Row>::const_iterator it=res.begin(); it<res.end(); it++)
526 {
527 Handle<Array> row = Array::New();
528 if (row.IsEmpty())
529 return Undefined();
530
531 const mysqlpp::FieldNames *list = it->field_list().list;
532
533 for (size_t i=0; i<it->size(); i++)
534 {
535 const Handle<Value> name = String::New((*list)[i].c_str());
536 if (irow==0)
537 cols->Set(i, name);
538
539 if ((*it)[i].is_null())
540 {
541 row->Set(name, Undefined(), ReadOnly);
542 continue;
543 }
544
545 const string sql_type = (*it)[i].type().sql_name();
546
547 const bool uns = sql_type.find("UNSIGNED")==string::npos;
548
549 if (sql_type.find("BIGINT")!=string::npos)
550 {
551 if (uns)
552 {
553 const uint64_t val = (uint64_t)(*it)[i];
554 if (val>UINT32_MAX)
555 row->Set(name, Number::New(val), ReadOnly);
556 else
557 row->Set(name, Integer::NewFromUnsigned(val), ReadOnly);
558 }
559 else
560 {
561 const int64_t val = (int64_t)(*it)[i];
562 if (val<INT32_MIN || val>INT32_MAX)
563 row->Set(name, Number::New(val), ReadOnly);
564 else
565 row->Set(name, Integer::NewFromUnsigned(val), ReadOnly);
566 }
567 continue;
568 }
569
570 // 32 bit
571 if (sql_type.find("INT")!=string::npos)
572 {
573 if (uns)
574 row->Set(name, Integer::NewFromUnsigned((uint32_t)(*it)[i]), ReadOnly);
575 else
576 row->Set(name, Integer::New((int32_t)(*it)[i]), ReadOnly);
577 }
578
579 if (sql_type.find("BOOL")!=string::npos )
580 {
581 row->Set(name, Boolean::New((bool)(*it)[i]), ReadOnly);
582 continue;
583 }
584
585 if (sql_type.find("FLOAT")!=string::npos)
586 {
587 ostringstream val;
588 val << setprecision(7) << (float)(*it)[i];
589 row->Set(name, Number::New(stod(val.str())), ReadOnly);
590 continue;
591
592 }
593 if (sql_type.find("DOUBLE")!=string::npos)
594 {
595 row->Set(name, Number::New((double)(*it)[i]), ReadOnly);
596 continue;
597 }
598
599 if (sql_type.find("CHAR")!=string::npos ||
600 sql_type.find("TEXT")!=string::npos)
601 {
602 row->Set(name, String::New((const char*)(*it)[i]), ReadOnly);
603 continue;
604 }
605
606 time_t date = 0;
607 if (sql_type.find("TIMESTAMP")!=string::npos)
608 date = mysqlpp::Time((*it)[i]);
609
610 if (sql_type.find("DATETIME")!=string::npos)
611 date = mysqlpp::DateTime((*it)[i]);
612
613 if (sql_type.find(" DATE ")!=string::npos)
614 date = mysqlpp::Date((*it)[i]);
615
616 if (date>0)
617 {
618 // It is important to catch the exception thrown
619 // by Date::New in case of thread termination!
620 const Local<Value> val = Date::New(date*1000);
621 if (val.IsEmpty())
622 return Undefined();
623
624 row->Set(name, val, ReadOnly);
625 }
626 }
627
628 ret->Set(irow++, row);
629 }
630
631 if (irow>0)
632 ret->Set(String::New("cols"), cols, ReadOnly);
633
634 return handle_scope.Close(ret);
635 }
636 catch (const exception &e)
637 {
638 return ThrowException(String::New(e.what()));
639 }
640#endif
641}
642
643Handle<Value> InterpreterV8::FuncDatabase(const Arguments &args)
644{
645 if (args.Length()!=1)
646 return ThrowException(String::New("Number of arguments must be 1."));
647
648 if (!args[0]->IsString())
649 return ThrowException(String::New("Argument 1 not a string."));
650
651 HandleScope handle_scope;
652
653#ifdef HAVE_SQL
654 try
655 {
656 Database *db = new Database(*String::Utf8Value(args[0]));
657 fDatabases.push_back(db);
658
659 Handle<ObjectTemplate> tem = ObjectTemplate::New();
660 tem->Set(String::New("user"), String::New(db->user.c_str()), ReadOnly);
661 tem->Set(String::New("server"), String::New(db->server.c_str()), ReadOnly);
662 tem->Set(String::New("database"), String::New(db->db.c_str()), ReadOnly);
663 tem->Set(String::New("port"), db->port==0?Undefined():Integer::NewFromUnsigned(db->port), ReadOnly);
664 tem->Set(String::New("query"), FunctionTemplate::New(WrapDbQuery), ReadOnly);
665 tem->Set(String::New("close"), FunctionTemplate::New(WrapDbClose), ReadOnly);
666 tem->SetInternalFieldCount(1);
667
668 Handle<Object> obj = tem->NewInstance();
669 if (obj.IsEmpty())
670 return Undefined();
671
672 obj->SetInternalField(0, External::New(db));
673
674 return handle_scope.Close(obj);
675 }
676 catch (const exception &e)
677 {
678 return ThrowException(String::New(e.what()));
679 }
680#endif
681}
682
683// ==========================================================================
684// Services
685// ==========================================================================
686
687Handle<Value> InterpreterV8::Convert(char type, const char* &ptr)
688{
689 // Dim values are always unsigned per (FACT++) definition
690 switch (type)
691 {
692 case 'F':
693 {
694 // Remove the "imprecision" effect coming from casting a float to
695 // a double and then showing it with double precision
696 ostringstream val;
697 val << setprecision(7) << *reinterpret_cast<const float*>(ptr);
698 Handle<Value> v=Number::New(stod(val.str()));
699 ptr+=4;
700 return v;
701 }
702 case 'D': { Handle<Value> v=Number::New(*reinterpret_cast<const double*>(ptr)); ptr+=8; return v; }
703 case 'I':
704 case 'L': { Handle<Value> v=Integer::NewFromUnsigned(*reinterpret_cast<const uint32_t*>(ptr)); ptr += 4; return v; }
705 case 'X':
706 {
707 const uint64_t val = *reinterpret_cast<const uint64_t*>(ptr);
708 ptr += 8;
709 if (val>UINT32_MAX)
710 return Number::New(val);
711 return Integer::NewFromUnsigned(val);
712 }
713 case 'S': { Handle<Value> v=Integer::NewFromUnsigned(*reinterpret_cast<const uint16_t*>(ptr)); ptr += 2; return v; }
714 case 'C': { Handle<Value> v=Integer::NewFromUnsigned((uint16_t)*reinterpret_cast<const uint8_t*>(ptr)); ptr += 1; return v; }
715 case ':': { Handle<Value> v=String::New(ptr); return v; }
716 }
717 return Undefined();
718}
719
720Handle<Value> InterpreterV8::FuncClose(const Arguments &args)
721{
722 HandleScope handle_scope;
723
724 //const void *ptr = Local<External>::Cast(args.Holder()->GetInternalField(0))->Value();
725
726 const String::Utf8Value str(args.Holder()->Get(String::New("name")));
727
728 const auto it = fReverseMap.find(*str);
729 if (it!=fReverseMap.end())
730 {
731 it->second.Dispose();
732 fReverseMap.erase(it);
733 }
734
735 args.Holder()->Set(String::New("isOpen"), Boolean::New(false), ReadOnly);
736
737 return handle_scope.Close(Boolean::New(JsUnsubscribe(*str)));
738}
739
740Handle<Value> InterpreterV8::ConvertEvent(const EventImp *evt, uint64_t counter, const char *str)
741{
742 const vector<Description> vec = JsDescription(str);
743
744 Handle<Array> ret = Array::New();
745 if (ret.IsEmpty())
746 return Undefined();
747
748 const Local<Value> date = Date::New(evt->GetJavaDate());
749 if (date.IsEmpty())
750 return Undefined();
751
752 ret->Set(String::New("name"), String::New(str), ReadOnly);
753 ret->Set(String::New("format"), String::New(evt->GetFormat().c_str()), ReadOnly);
754 ret->Set(String::New("named"), Boolean::New(vec.size()>0), ReadOnly);
755 ret->Set(String::New("qos"), Integer::New(evt->GetQoS()), ReadOnly);
756 ret->Set(String::New("size"), Integer::New(evt->GetSize()), ReadOnly);
757 ret->Set(String::New("counter"), Integer::New(counter), ReadOnly);
758 ret->Set(String::New("time"), date, ReadOnly);
759
760 // If no event was received (usually a disconnection event in
761 // the context of FACT++), no data is returned
762 if (evt->IsEmpty())
763 return ret;
764
765 // If valid data was received, but the size was zero, then
766 // null is returned as data
767 if (evt->GetSize()==0 || evt->GetFormat().empty())
768 {
769 ret->Set(String::New("data"), Null(), ReadOnly);
770 return ret;
771 }
772
773 typedef boost::char_separator<char> separator;
774 const boost::tokenizer<separator> tokenizer(evt->GetFormat(), separator(";:"));
775
776 const vector<string> tok(tokenizer.begin(), tokenizer.end());
777
778 Handle<Array> obj = tok.size()>1 ? Array::New() : ret;
779 if (obj.IsEmpty())
780 return Undefined();
781
782 const char *ptr = evt->GetText();
783 const char *end = evt->GetText()+evt->GetSize();
784
785 try
786 {
787 size_t pos = 1;
788 for (auto it=tok.begin(); it!=tok.end() && ptr<end; it++, pos++)
789 {
790 char type = (*it)[0];
791 it++;
792
793 if (it==tok.end() && type=='C')
794 type = ':';
795
796 if (it==tok.end() && type!=':')
797 return Exception::Error(String::New(("Format string invalid '"+evt->GetFormat()+"'").c_str()));
798
799 string name = pos<vec.size() ? vec[pos].name : "";
800 if (tok.size()==1)
801 name = "data";
802
803 const uint32_t cnt = it==tok.end() ? 1 : stoi(it->c_str());
804
805 if (cnt==1)
806 {
807 const Handle<Value> v = Convert(type, ptr);
808 if (tok.size()>1)
809 obj->Set(pos-1, v);
810 if (!name.empty())
811 obj->Set(String::New(name.c_str()), v);
812 }
813 else
814 {
815 Handle<Array> a = Array::New(cnt);
816 if (a.IsEmpty())
817 return Undefined();
818
819 for (uint32_t i=0; i<cnt; i++)
820 a->Set(i, Convert(type, ptr));
821 if (tok.size()>1)
822 obj->Set(pos-1, a);
823 if (!name.empty())
824 obj->Set(String::New(name.c_str()), a);
825 }
826
827 if (it==tok.end())
828 break;
829 }
830
831 if (tok.size()>1)
832 ret->Set(String::New("data"), obj, ReadOnly);
833
834 return ret;
835 }
836 catch (...)
837 {
838 return Exception::Error(String::New(("Format string conversion '"+evt->GetFormat()+"' failed.").c_str()));
839 }
840}
841/*
842Handle<Value> InterpreterV8::FuncGetData(const Arguments &args)
843{
844 HandleScope handle_scope;
845
846 const String::Utf8Value str(args.Holder()->Get(String::New("name")));
847
848 const pair<uint64_t, EventImp *> p = JsGetEvent(*str);
849
850 const EventImp *evt = p.second;
851 if (!evt)
852 return Undefined();
853
854 //if (counter==cnt)
855 // return info.Holder();//Holder()->Get(String::New("data"));
856
857 Handle<Value> ret = ConvertEvent(evt, p.first, *str);
858 return ret->IsNativeError() ? ThrowException(ret) : handle_scope.Close(ret);
859}
860*/
861Handle<Value> InterpreterV8::FuncGetData(const Arguments &args)
862{
863 if (args.Length()>1)
864 return ThrowException(String::New("Number of arguments must not be greatr than 1."));
865
866 if (args.Length()==1 && !args[0]->IsInt32() && !args[0]->IsNull())
867 return ThrowException(String::New("Argument 1 not an int32."));
868
869 // Using a Javascript function has the advantage that it is fully
870 // interruptable without the need of C++ code
871 const bool null = args.Length()==1 && args[0]->IsNull();
872 const int32_t timeout = args.Length()==1 ? args[0]->Int32Value() : 0;
873
874 HandleScope handle_scope;
875
876 const Handle<Script> sleep = Script::Compile(String::New("dim.sleep();"));
877 if (sleep.IsEmpty())
878 return Undefined();
879
880 const Handle<String> data = String::New("data");
881 const Handle<String> named = String::New("named");
882
883 const String::Utf8Value name(args.Holder()->Get(String::New("name")));
884
885 TryCatch exception;
886
887 Time t;
888 while (!exception.HasCaught())
889 {
890 const pair<uint64_t, EventImp *> p = JsGetEvent(*name);
891
892 const EventImp *evt = p.second;
893 if (evt)
894 {
895 const Handle<Value> val = ConvertEvent(evt, p.first, *name);
896 if (val->IsNativeError())
897 return ThrowException(val);
898
899 // Protect against the return of an exception
900 if (!val.IsEmpty() && val->IsObject())
901 {
902 const Handle<Object> obj = val->ToObject();
903 if (obj->Has(data) && obj->Get(named)->ToBoolean()->Value())
904 return handle_scope.Close(val);
905 }
906 }
907
908 if (args.Length()==0)
909 break;
910
911 if (!null && Time()-t>=boost::posix_time::milliseconds(abs(timeout)))
912 break;
913
914 // We cannot sleep directly because we have to give control back to
915 // JavaScript ever now and then. This also allows us to catch
916 // exceptions, either from the preemption or ConvertEvent
917 sleep->Run();
918 }
919
920 if (exception.HasCaught())
921 return exception.ReThrow();
922
923 if (timeout>=0)
924 return Undefined();
925
926 const string str = "Waiting for a valid event of "+string(*name)+" timed out.";
927 return ThrowException(String::New(str.c_str()));
928}
929
930
931// This is a callback from the RemoteControl piping event handling
932// to the java script ---> in test phase!
933void InterpreterV8::JsHandleEvent(const EventImp &evt, uint64_t cnt, const string &service)
934{
935 const Locker locker;
936
937 if (fThreadId<0)
938 return;
939
940 const auto it = fReverseMap.find(service);
941 if (it==fReverseMap.end())
942 return;
943
944 const HandleScope handle_scope;
945
946 fGlobalContext->Enter();
947
948 Handle<Object> obj = it->second;
949 if (obj.IsEmpty())
950 return;
951
952 const Handle<String> onchange = String::New("onchange");
953 if (!obj->Has(onchange))
954 return;
955
956 const Handle<Value> val = obj->Get(onchange);
957 if (!val->IsFunction())
958 return;
959
960 // -------------------------------------------------------------------
961 TryCatch exception;
962
963 const int id = V8::GetCurrentThreadId();
964 fThreadIds.insert(id);
965
966 Handle<Value> ret = ConvertEvent(&evt, cnt, service.c_str());
967 if (ret->IsArray())
968 {
969 Handle<Array> data = Handle<Array>::Cast(ret);
970 Handle<Value> args[] = { data };
971
972 Handle<Function>::Cast(val)->Call(obj, 1, args);
973 }
974
975 fThreadIds.erase(id);
976
977 if (exception.HasCaught())
978 ReportException(&exception);
979
980 if (ret->IsNativeError())
981 JsException(service+".onchange callback - "+*String::Utf8Value(ret));
982
983 if (ret->IsUndefined() || ret->IsNativeError() || exception.HasCaught())
984 Terminate();
985}
986
987Handle<Value> InterpreterV8::OnChangeSet(Local<String> prop, Local< Value > value, const AccessorInfo &)
988{
989 const HandleScope handle_scope;
990
991 // Returns the value if the setter intercepts the request. Otherwise, returns an empty handle.
992 const string server = *String::Utf8Value(prop);
993 auto it = fStateCallbacks.find(server);
994
995 if (it!=fStateCallbacks.end())
996 {
997 it->second.Dispose();
998 fStateCallbacks.erase(it);
999 }
1000
1001 if (value->IsFunction())
1002 fStateCallbacks[server] = Persistent<Value>::New(value);
1003
1004 return Handle<Value>();
1005}
1006
1007
1008void InterpreterV8::JsHandleState(const std::string &server, const State &state)
1009{
1010 const Locker locker;
1011
1012 if (fThreadId<0)
1013 return;
1014
1015 auto it = fStateCallbacks.find(server);
1016 if (it==fStateCallbacks.end())
1017 {
1018 it = fStateCallbacks.find("*");
1019 if (it==fStateCallbacks.end())
1020 return;
1021 }
1022
1023 const HandleScope handle_scope;
1024
1025 if (it->second.IsEmpty() || !it->second->IsFunction())
1026 return;
1027
1028 fGlobalContext->Enter();
1029
1030 // -------------------------------------------------------------------
1031
1032 Handle<ObjectTemplate> obj = ObjectTemplate::New();
1033 obj->Set(String::New("index"), state.index<=-256?Undefined():Integer::New(state.index), ReadOnly);
1034 obj->Set(String::New("name"), state.index<=-256?Undefined():String::New(state.name.c_str()), ReadOnly);
1035 obj->Set(String::New("comment"), state.index<=-256?Undefined():String::New(state.comment.c_str()), ReadOnly);
1036 obj->Set(String::New("server"), String::New(server.c_str()), ReadOnly);
1037
1038 const Local<Value> date = Date::New(state.time.JavaDate());
1039 if (state.index>-256 && !date.IsEmpty())
1040 obj->Set(String::New("time"), date);
1041
1042 // -------------------------------------------------------------------
1043
1044 TryCatch exception;
1045
1046 const int id = V8::GetCurrentThreadId();
1047 fThreadIds.insert(id);
1048
1049 Handle<Value> args[] = { obj->NewInstance() };
1050 Handle<Function> fun = Handle<Function>::Cast(it->second);
1051 fun->Call(fun, 1, args);
1052
1053 fThreadIds.erase(id);
1054
1055 if (!exception.HasCaught())
1056 return;
1057
1058 ReportException(&exception);
1059 Terminate();
1060}
1061
1062/*
1063void Cleanup( Persistent<Value> object, void *parameter )
1064{
1065 cout << "======================> RemoveMyObj()" << endl;
1066}*/
1067
1068Handle<Value> InterpreterV8::FuncSubscribe(const Arguments &args)
1069{
1070 if (args.Length()!=1)
1071 return ThrowException(String::New("Number of arguments must be exactly 1."));
1072
1073 if (!args[0]->IsString())
1074 return ThrowException(String::New("Argument 1 must be a string."));
1075
1076 //if (!args.IsConstructCall())
1077 // return ThrowException(String::New("Must be used as constructor."));
1078
1079 HandleScope handle_scope;
1080
1081 const String::Utf8Value str(args[0]);
1082
1083 const auto it = fReverseMap.find(*str);
1084 if (it!=fReverseMap.end())
1085 return handle_scope.Close(it->second);
1086
1087 void *ptr = JsSubscribe(*str);
1088 if (ptr==0)
1089 return ThrowException(String::New(("Subscription to '"+string(*str)+"' already exists.").c_str()));
1090
1091 Handle<ObjectTemplate> tem = ObjectTemplate::New();
1092 tem->Set(String::New("get"), FunctionTemplate::New(WrapGetData), ReadOnly);
1093 tem->Set(String::New("close"), FunctionTemplate::New(WrapClose), ReadOnly);
1094 tem->Set(String::New("name"), String::New(*str), ReadOnly);
1095 tem->Set(String::New("isOpen"), Boolean::New(true));
1096 tem->SetInternalFieldCount(1);
1097 //tem->Set(String::New("toString"), String::New(("[object Dim "+string(*str)+"]").c_str()), ReadOnly);
1098
1099 Handle<Object> obj = tem->NewInstance();
1100 if (obj.IsEmpty())
1101 return Undefined();
1102
1103 obj->SetInternalField(0, External::New(ptr));
1104
1105 fReverseMap[*str] = Persistent<Object>::New(obj);
1106
1107 return handle_scope.Close(obj);
1108
1109 // Persistent<Object> p = Persistent<Object>::New(obj->NewInstance());
1110 // obj.MakeWeak((void*)1, Cleanup);
1111 // return obj;
1112}
1113
1114// ==========================================================================
1115// Astrometry
1116// ==========================================================================
1117#ifdef HAVE_NOVA
1118
1119double InterpreterV8::GetDataMember(const Arguments &args, const char *name)
1120{
1121 return args.This()->Get(String::New(name))->NumberValue();
1122}
1123
1124Handle<Value> InterpreterV8::LocalToString(const Arguments &/*args*/)
1125{
1126 return String::New("[object Local]");
1127 /*
1128 HandleScope handle_scope;
1129
1130 const Handle<Object> This = args.This();
1131
1132 Handle<String> zd = This->Get(String::New("zd"))->ToString();
1133 Handle<String> az = This->Get(String::New("az"))->ToString();
1134
1135 return String::New("TEST");
1136 */
1137}
1138
1139Handle<Value> InterpreterV8::SkyToString(const Arguments &/*args*/)
1140{
1141 return String::New("[object Sky]");
1142}
1143
1144Handle<Value> InterpreterV8::MoonToString(const Arguments &/*args*/)
1145{
1146 return String::New("[object Moon]");
1147}
1148
1149Handle<Value> InterpreterV8::ConstructLocal(double zd, double az, Handle<Value> time)
1150{
1151 Handle<ObjectTemplate> loc = ObjectTemplate::New();
1152
1153 loc->Set(String::New("zd"), Number::New(zd), ReadOnly);
1154 loc->Set(String::New("az"), Number::New(az), ReadOnly);
1155 loc->Set(String::New("toSky"), FunctionTemplate::New(LocalToSky), ReadOnly);
1156 loc->Set(String::New("toString"), FunctionTemplate::New(LocalToString), ReadOnly);
1157 if (!time.IsEmpty())
1158 loc->Set(String::New("time"), time);
1159
1160 return loc->NewInstance();
1161}
1162
1163Handle<Value> InterpreterV8::ConstructSky(double ra, double dec, Handle<Value> time, bool ismoon)
1164{
1165 Handle<ObjectTemplate> sky = ObjectTemplate::New();
1166
1167 sky->Set(String::New("ra"), Number::New(ra), ReadOnly);
1168 sky->Set(String::New("dec"), Number::New(dec), ReadOnly);
1169 sky->Set(String::New("toLocal"), FunctionTemplate::New(ismoon?MoonToLocal :SkyToLocal), ReadOnly);
1170 sky->Set(String::New("toString"), FunctionTemplate::New(ismoon?MoonToString:SkyToString), ReadOnly);
1171 if (!time.IsEmpty())
1172 sky->Set(String::New("time"), time);
1173
1174 return sky->NewInstance();
1175}
1176
1177Handle<Value> InterpreterV8::LocalDist(const Arguments &args)
1178{
1179 HandleScope handle_scope;
1180
1181 if (args.Length()!=2)
1182 return ThrowException(String::New("dist must not be called with two arguments."));
1183
1184 if (!args[0]->IsObject() || !args[1]->IsObject())
1185 return ThrowException(String::New("at least one argument not an object."));
1186
1187 Handle<Object> obj[2] =
1188 {
1189 Handle<Object>::Cast(args[0]),
1190 Handle<Object>::Cast(args[1])
1191 };
1192
1193 const Handle<String> s_zd = String::New("zd");
1194 const Handle<String> s_az = String::New("az");
1195
1196 const double zd0 = obj[0]->Get(s_zd)->NumberValue() * M_PI/180;
1197 const double az0 = obj[0]->Get(s_az)->NumberValue() * M_PI/180;
1198 const double zd1 = obj[1]->Get(s_zd)->NumberValue() * M_PI/180;
1199 const double az1 = obj[1]->Get(s_az)->NumberValue() * M_PI/180;
1200
1201 if (!finite(zd0) || !finite(zd1) || !finite(az0) || !finite(az1))
1202 return ThrowException(String::New("some values not valid or not finite."));
1203
1204 /*
1205 const double x0 = sin(zd0) * cos(az0); // az0 -= az0
1206 const double y0 = sin(zd0) * sin(az0); // az0 -= az0
1207 const double z0 = cos(zd0);
1208
1209 const double x1 = sin(zd1) * cos(az1); // az1 -= az0
1210 const double y1 = sin(zd1) * sin(az1); // az1 -= az0
1211 const double z1 = cos(zd1);
1212
1213 const double res = acos(x0*x1 + y0*y1 + z0*z1) * 180/M_PI;
1214 */
1215
1216 // cos(az1-az0) = cos(az1)*cos(az0) + sin(az1)*sin(az0)
1217
1218 const double x = sin(zd0) * sin(zd1) * cos(az1-az0);
1219 const double y = cos(zd0) * cos(zd1);
1220
1221 const double res = acos(x + y) * 180/M_PI;
1222
1223 return handle_scope.Close(Number::New(res));
1224}
1225
1226Handle<Value> InterpreterV8::MoonDisk(const Arguments &args)
1227{
1228 HandleScope handle_scope;
1229
1230 if (args.Length()>1)
1231 return ThrowException(String::New("disk must not be called with more than one argument."));
1232
1233 const uint64_t v = uint64_t(args[0]->NumberValue());
1234 const Time utc = args.Length()==0 ? Time() : Time(v/1000, v%1000);
1235
1236 return handle_scope.Close(Number::New(ln_get_lunar_disk(utc.JD())));
1237}
1238
1239Handle<Value> InterpreterV8::LocalToSky(const Arguments &args)
1240{
1241 HandleScope handle_scope;
1242
1243 if (args.Length()>1)
1244 return ThrowException(String::New("toSky must not be called with more than one argument."));
1245
1246 ln_hrz_posn hrz;
1247 hrz.alt = 90-GetDataMember(args, "zd");
1248 hrz.az = GetDataMember(args, "az");
1249
1250 if (!finite(hrz.alt) || !finite(hrz.az))
1251 return ThrowException(String::New("zd and az must be finite."));
1252
1253 const Local<Value> date =
1254 args.Length()==0 ? Date::New(Time().JavaDate()) : args[0];
1255 if (date.IsEmpty())
1256 return Undefined();
1257
1258 const uint64_t v = uint64_t(date->NumberValue());
1259 const Time utc(v/1000, v%1000);
1260
1261 ln_lnlat_posn obs;
1262 obs.lng = -(17.+53./60+26.525/3600);
1263 obs.lat = 28.+45./60+42.462/3600;
1264
1265 ln_equ_posn equ;
1266 ln_get_equ_from_hrz(&hrz, &obs, utc.JD(), &equ);
1267
1268 return handle_scope.Close(ConstructSky(equ.ra/15, equ.dec, date));
1269}
1270
1271Handle<Value> InterpreterV8::SkyToLocal(const Arguments &args)
1272{
1273 HandleScope handle_scope;
1274
1275 if (args.Length()>1)
1276 return ThrowException(String::New("toLocal must not be called with more than one argument."));
1277
1278 ln_equ_posn equ;
1279 equ.ra = GetDataMember(args, "ra")*15;
1280 equ.dec = GetDataMember(args, "dec");
1281
1282 if (!finite(equ.ra) || !finite(equ.dec))
1283 return ThrowException(String::New("Ra and dec must be finite."));
1284
1285 const Local<Value> date =
1286 args.Length()==0 ? Date::New(Time().JavaDate()) : args[0];
1287 if (date.IsEmpty())
1288 return Undefined();
1289
1290 const uint64_t v = uint64_t(date->NumberValue());
1291 const Time utc(v/1000, v%1000);
1292
1293 ln_lnlat_posn obs;
1294 obs.lng = -(17.+53./60+26.525/3600);
1295 obs.lat = 28.+45./60+42.462/3600;
1296
1297 ln_hrz_posn hrz;
1298 ln_get_hrz_from_equ(&equ, &obs, utc.JD(), &hrz);
1299
1300 return handle_scope.Close(ConstructLocal(90-hrz.alt, hrz.az, date));
1301}
1302
1303Handle<Value> InterpreterV8::MoonToLocal(const Arguments &args)
1304{
1305 HandleScope handle_scope;
1306
1307 if (args.Length()>0)
1308 return ThrowException(String::New("toLocal must not be called with arguments."));
1309
1310 ln_equ_posn equ;
1311 equ.ra = GetDataMember(args, "ra")*15;
1312 equ.dec = GetDataMember(args, "dec");
1313
1314 if (!finite(equ.ra) || !finite(equ.dec))
1315 return ThrowException(String::New("ra and dec must be finite."));
1316
1317 const Local<Value> date =
1318 args.Length()==0 ? Date::New(Time().JavaDate()) : args.This()->Get(String::New("time"));
1319 if (date.IsEmpty())
1320 return Undefined();
1321
1322 const uint64_t v = uint64_t(date->NumberValue());
1323 const Time utc(v/1000, v%1000);
1324
1325 ln_lnlat_posn obs;
1326 obs.lng = -(17.+53./60+26.525/3600);
1327 obs.lat = 28.+45./60+42.462/3600;
1328
1329 ln_hrz_posn hrz;
1330 ln_get_hrz_from_equ(&equ, &obs, utc.JD(), &hrz);
1331
1332 return handle_scope.Close(ConstructLocal(90-hrz.alt, hrz.az, date));
1333}
1334
1335Handle<Value> InterpreterV8::ConstructorMoon(const Arguments &args)
1336{
1337 HandleScope handle_scope;
1338
1339 if (args.Length()>1)
1340 return ThrowException(String::New("Moon constructor must not be called with more than one argument."));
1341
1342 const Local<Value> date =
1343 args.Length()==0 ? Date::New(Time().JavaDate()) : args[0];
1344 if (date.IsEmpty())
1345 return Undefined();
1346
1347 const uint64_t v = uint64_t(date->NumberValue());
1348 const Time utc(v/1000, v%1000);
1349
1350 ln_equ_posn equ;
1351 ln_get_lunar_equ_coords_prec(utc.JD(), &equ, 0.01);
1352
1353 return handle_scope.Close(ConstructSky(equ.ra/15, equ.dec, date, true));
1354}
1355
1356Handle<Value> InterpreterV8::ConstructorSky(const Arguments &args)
1357{
1358 HandleScope handle_scope;
1359
1360 if (args.Length()!=2)
1361 return ThrowException(String::New("Sky constructor takes two arguments."));
1362
1363 const double ra = args[0]->NumberValue();
1364 const double dec = args[1]->NumberValue();
1365
1366 if (!finite(ra) || !finite(dec))
1367 return ThrowException(String::New("Both arguments to Sky must be valid numbers."));
1368
1369 return handle_scope.Close(ConstructSky(ra, dec));
1370}
1371
1372Handle<Value> InterpreterV8::ConstructorLocal(const Arguments &args)
1373{
1374 HandleScope handle_scope;
1375
1376 if (args.Length()!=2)
1377 return ThrowException(String::New("Local constructor takes two arguments."));
1378
1379 const double zd = args[0]->NumberValue();
1380 const double az = args[1]->NumberValue();
1381
1382 if (!finite(zd) || !finite(az))
1383 return ThrowException(String::New("Both arguments to Local must be valid numbers."));
1384
1385 return handle_scope.Close(ConstructLocal(zd, az));
1386}
1387#endif
1388
1389// ==========================================================================
1390// Process control
1391// ==========================================================================
1392
1393bool InterpreterV8::ReportException(TryCatch* try_catch)
1394{
1395 if (!try_catch->CanContinue())
1396 return false;
1397
1398 const HandleScope handle_scope;
1399
1400 const String::Utf8Value exception(try_catch->Exception());
1401
1402 if (*exception && string(*exception)=="exit")
1403 return true;
1404 if (*exception && string(*exception)=="null")
1405 return false;
1406
1407 const Handle<Message> message = try_catch->Message();
1408 if (message.IsEmpty())
1409 return false;
1410
1411 // Print (filename):(line number): (message).
1412 const String::Utf8Value filename(message->GetScriptResourceName());
1413
1414 ostringstream out;
1415
1416 if (*filename)
1417 out << *filename << ": ";
1418 out << "l." << message->GetLineNumber();
1419 if (*exception)
1420 out << ": " << *exception;
1421
1422 JsException(out.str());
1423
1424 // Print line of source code.
1425 const String::Utf8Value sourceline(message->GetSourceLine());
1426 if (*sourceline)
1427 JsException(*sourceline);
1428
1429 // Print wavy underline (GetUnderline is deprecated).
1430 const int start = message->GetStartColumn();
1431 const int end = message->GetEndColumn();
1432
1433 out.str("");
1434 if (start>0)
1435 out << setfill(' ') << setw(start) << ' ';
1436 out << setfill('^') << setw(end-start) << '^';
1437
1438 JsException(out.str());
1439
1440 String::Utf8Value stack_trace(try_catch->StackTrace());
1441 if (stack_trace.length()<=0)
1442 return false;
1443
1444 //if (*stack_trace)
1445 // JsException(string("\n")+*stack_trace);
1446
1447 return false;
1448}
1449
1450// Executes a string within the current v8 context.
1451bool InterpreterV8::ExecuteStringNT(const Handle<String> &code, const Handle<Value> &file)
1452{
1453 if (code.IsEmpty())
1454 return true;
1455
1456 const HandleScope handle_scope;
1457
1458 const Handle<Script> script = Script::Compile(code, file);
1459 if (script.IsEmpty())
1460 return false;
1461
1462 JsSetState(3);
1463
1464 const Handle<Value> result = script->Run();
1465 if (result.IsEmpty())
1466 return false;
1467
1468 // If all went well and the result wasn't undefined then print
1469 // the returned value.
1470 if (!result->IsUndefined())
1471 JsResult(*String::Utf8Value(result));
1472
1473 return true;
1474}
1475
1476bool InterpreterV8::ExecuteCode(const Handle<String> &code, const Handle<Value> &file)
1477{
1478 TryCatch exception;
1479
1480 const bool rc = ExecuteStringNT(code, file);
1481
1482 // Check if this is a termination exception
1483 //if (!exception.CanContinue())
1484 // return false;
1485
1486 if (exception.HasCaught())
1487 return ReportException(&exception);
1488
1489 return rc;
1490}
1491
1492bool InterpreterV8::ExecuteCode(const string &code, const string &file)
1493{
1494 return ExecuteCode(String::New(code.c_str(), code.size()),
1495 String::New(file.c_str()));
1496}
1497
1498bool InterpreterV8::ExecuteFile(const string &name)
1499{
1500 ifstream fin(name.c_str());
1501 if (!fin)
1502 {
1503 JsException("Error - Could not open file '"+name+"'");
1504 return false;
1505 }
1506
1507 string buffer;
1508 if (!getline(fin, buffer, '\0'))
1509 return true;
1510
1511 if (fin.fail())
1512 {
1513 JsException("Error - reading file.");
1514 return false;
1515 }
1516
1517 return ExecuteCode(buffer, name);
1518}
1519
1520// ==========================================================================
1521// CORE
1522// ==========================================================================
1523
1524bool InterpreterV8::JsRun(const string &filename, const map<string, string> &map)
1525{
1526 const Locker locker;
1527 fThreadId = V8::GetCurrentThreadId();
1528
1529 JsPrint(string("JavaScript Engine V8 ")+V8::GetVersion());
1530
1531 JsLoad(filename);
1532
1533 HandleScope handle_scope;
1534
1535 // Create a template for the global object.
1536 Handle<ObjectTemplate> dim = ObjectTemplate::New();
1537 dim->Set(String::New("print"), FunctionTemplate::New(WrapPrint), ReadOnly);
1538 dim->Set(String::New("alarm"), FunctionTemplate::New(WrapAlarm), ReadOnly);
1539 dim->Set(String::New("out"), FunctionTemplate::New(WrapOut), ReadOnly);
1540 dim->Set(String::New("wait"), FunctionTemplate::New(WrapWait), ReadOnly);
1541 dim->Set(String::New("send"), FunctionTemplate::New(WrapSend), ReadOnly);
1542 dim->Set(String::New("state"), FunctionTemplate::New(WrapState), ReadOnly);
1543 dim->Set(String::New("newState"), FunctionTemplate::New(WrapNewState), ReadOnly);
1544 dim->Set(String::New("setState"), FunctionTemplate::New(WrapSetState), ReadOnly);
1545 dim->Set(String::New("sleep"), FunctionTemplate::New(WrapSleep), ReadOnly);
1546 dim->Set(String::New("timeout"), FunctionTemplate::New(WrapTimeout), ReadOnly);
1547 dim->Set(String::New("subscribe"), FunctionTemplate::New(WrapSubscribe), ReadOnly);
1548 dim->Set(String::New("database"), FunctionTemplate::New(WrapDatabase), ReadOnly);
1549
1550 Handle<ObjectTemplate> onchange = ObjectTemplate::New();
1551 onchange->SetNamedPropertyHandler(OnChangeGet, WrapOnChangeSet);
1552 dim->Set(v8::String::New("onchange"), onchange);
1553
1554 Handle<ObjectTemplate> global = ObjectTemplate::New();
1555 global->Set(String::New("dim"), dim, ReadOnly);
1556 global->Set(String::New("include"), FunctionTemplate::New(WrapInclude), ReadOnly);
1557 global->Set(String::New("exit"), FunctionTemplate::New(WrapExit), ReadOnly);
1558 global->Set(String::New("version"), FunctionTemplate::New(InterpreterV8::FuncVersion), ReadOnly);
1559
1560#ifdef HAVE_NOVA
1561 Handle<FunctionTemplate> sky = FunctionTemplate::New(ConstructorSky);
1562 global->Set(String::New("Sky"), sky, ReadOnly);
1563
1564 Handle<FunctionTemplate> loc = FunctionTemplate::New(ConstructorLocal);
1565 loc->Set(String::New("dist"), FunctionTemplate::New(LocalDist), ReadOnly);
1566 global->Set(String::New("Local"), loc, ReadOnly);
1567
1568 Handle<FunctionTemplate> moon = FunctionTemplate::New(ConstructorMoon);
1569 moon->Set(String::New("disk"), FunctionTemplate::New(MoonDisk), ReadOnly);
1570 global->Set(String::New("Moon"), moon, ReadOnly);
1571#endif
1572
1573 // Persistent
1574 fGlobalContext = Context::New(NULL, global);
1575 if (fGlobalContext.IsEmpty())
1576 {
1577 //printf("Error creating context\n");
1578 return false;
1579 }
1580
1581 Context::Scope scope(fGlobalContext);
1582
1583 Handle<Array> args = Array::New(map.size());
1584 for (auto it=map.begin(); it!=map.end(); it++)
1585 args->Set(String::New(it->first.c_str()), String::New(it->second.c_str()));
1586 fGlobalContext->Global()->Set(String::New("$"), args, ReadOnly);
1587 fGlobalContext->Global()->Set(String::New("arg"), args, ReadOnly);
1588
1589 JsStart(filename);
1590
1591 //context->Enter();
1592 Locker::StartPreemption(10);
1593 const bool rc = ExecuteFile(filename);
1594 Terminate();
1595
1596 // -----
1597 // This is how an exit handler could look like, but there is no way to interrupt it
1598 // -----
1599 // Handle<Object> obj = Handle<Object>::Cast(context->Global()->Get(String::New("dim")));
1600 // if (!obj.IsEmpty())
1601 // {
1602 // Handle<Value> onexit = obj->Get(String::New("onexit"));
1603 // if (!onexit->IsUndefined())
1604 // Handle<Function>::Cast(onexit)->NewInstance(0, NULL); // argc, argv
1605 // // Handle<Object> result = Handle<Function>::Cast(onexit)->NewInstance(0, NULL); // argc, argv
1606 // }
1607
1608 Locker::StopPreemption();
1609 //context->Exit();
1610
1611 // Thre threads are started already and wait to get the lock
1612 // So we have to unlock (manual preemtion) so they they get
1613 // the signal to terminate. After they are all successfully
1614 // terminated, just to be sure... we lock again
1615 {
1616 const Unlocker unlock;
1617
1618 for (auto it=fTimeout.begin(); it!=fTimeout.end(); it++)
1619 it->join();
1620 fTimeout.clear();
1621 }
1622
1623 // Now we can dispose all persistent handles from state callbacks
1624 for (auto it=fStateCallbacks.begin(); it!=fStateCallbacks.end(); it++)
1625 it->second.Dispose();
1626 fStateCallbacks.clear();
1627
1628 // Now we can dispose all persistent handles from reverse maps
1629 for (auto it=fReverseMap.begin(); it!=fReverseMap.end(); it++)
1630 it->second.Dispose();
1631 fReverseMap.clear();
1632
1633#ifdef HAVE_SQL
1634 // ...and close all database handles
1635 for (auto it=fDatabases.begin(); it!=fDatabases.end(); it++)
1636 delete *it;
1637 fDatabases.clear();
1638#endif
1639
1640 JsEnd(filename);
1641
1642 return rc;
1643}
1644
1645void InterpreterV8::JsStop()
1646{
1647 Locker locker;
1648 This->Terminate();
1649}
1650
1651#endif
1652
1653InterpreterV8 *InterpreterV8::This = 0;
Note: See TracBrowser for help on using the repository browser.