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

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