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

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