FACT++  1.0
InterpreterV8.cc
Go to the documentation of this file.
1 #include "InterpreterV8.h"
2 
3 #ifdef HAVE_V8
4 
5 #include <fstream>
6 #include <sstream>
7 #include <iomanip>
8 
9 #include <sys/stat.h>
10 
11 #include <boost/tokenizer.hpp>
12 #include <boost/algorithm/string/join.hpp>
13 
14 #ifdef HAVE_NOVA
15 #include "externals/nova.h"
16 #endif
17 
18 #ifdef HAVE_SQL
19 #include "Database.h"
20 #endif
21 
22 #include <v8.h>
23 
24 #include "dim.h"
25 #include "tools.h"
26 #include "Readline.h"
27 #include "externals/izstream.h"
28 
29 #include "WindowLog.h"
30 
31 using namespace std;
32 using namespace v8;
33 
34 v8::Handle<v8::FunctionTemplate> InterpreterV8::fTemplateLocal;
35 v8::Handle<v8::FunctionTemplate> InterpreterV8::fTemplateSky;
36 v8::Handle<v8::FunctionTemplate> InterpreterV8::fTemplateEvent;
37 v8::Handle<v8::FunctionTemplate> InterpreterV8::fTemplateDescription;
38 //v8::Handle<v8::FunctionTemplate> InterpreterV8::fTemplateDatabase;
39 
40 
41 // ==========================================================================
42 // Some documentation
43 // ==========================================================================
44 //
45 // Threads:
46 // --------
47 // In most cases Js* and other calls to native C++ code could be wrapped
48 // with an Unlocker to allow possible other JavaScipt 'threads' to run
49 // during that time. However, all of these calls should take much less than
50 // the preemption time of 10ms, so it would just be a waste of tim.
51 //
52 // Termination:
53 // ------------
54 // Each thread running V8 code needs to be signalled individually for
55 // termination. Therefor a list of V8 thread ids is created.
56 //
57 // If termination has already be signalled, no thread should start running
58 // anymore (thy could, e.g., wait for their locking). So after locking
59 // it has to be checked if the thread was terminated already. Note
60 // that all calls to Terminate() must be locked to ensure that fThreadId
61 // is correct when it is checked.
62 //
63 // The current thread id must be added to fThreadIds _before_ any
64 // function is called after Locking and before execution is given
65 // back to JavaScript, e.g. in script->Run(). So until the thread
66 // is added to the list Terminate will not be executed. If Terminate
67 // is then executed, it is ensured that the current thread is
68 // already in the list. If terminate has been called before
69 // the Locking, the check for the validiy of fThreadId ensures that
70 // nothing is executed.
71 //
72 // Empty handles:
73 // --------------
74 // If exceution is terminated, V8 calls might return with empty handles,
75 // e.g. Date::New(). Therefore, the returned handles of these calls have to
76 // be checked in all placed to avoid that V8 will core dump.
77 //
78 // HandleScope:
79 // ------------
80 // A handle scope is a garbage collector and collects all handles created
81 // until it goes out of scope. Handles which are not needed anymore are
82 // then deleted. To return a handle from a HandleScope you need to use
83 // Close(). E.g., String::AsciiValue does not create a new handle and
84 // hence does not need a HandleScope. Any ::New will need a handle scope.
85 // Forgetting the HandleScope could in principle fill your memory,
86 // but everything is properly deleted by the global HandleScope at
87 // script termination.
88 //
89 // Here is another good reference for v8, also containing some
90 // good explanations for the meaning of handles, persistent handles
91 // and weak handles: http://create.tpsitulsa.com/wiki/V8_Cookbook
92 //
93 // ==========================================================================
94 // Simple interface
95 // ==========================================================================
96 
97 Handle<Value> InterpreterV8::FuncExit(const Arguments &)
98 {
99  V8::TerminateExecution(fThreadId);
100 
101  // we have to throw an excption to make sure that the
102  // calling thread does not go on executing until it
103  // has realized that it should terminate
104  return ThrowException(Null());
105 }
106 
107 Handle<Value> InterpreterV8::FuncSleep(const Arguments& args)
108 {
109  if (args.Length()==0)
110  {
111  // Theoretically, the CPU usage can be reduced by maybe a factor
112  // of four using a larger value, but this also means that the
113  // JavaScript is locked for a longer time.
114  const Unlocker unlock;
115  usleep(1000);
116  return Undefined();
117  }
118 
119  if (args.Length()!=1)
120  return ThrowException(String::New("Number of arguments must be exactly 1."));
121 
122  if (!args[0]->IsUint32())
123  return ThrowException(String::New("Argument 1 must be an uint32."));
124 
125  // Using a Javascript function has the advantage that it is fully
126  // interruptable without the need of C++ code
127  const string code =
128  "(function(){"
129  "var t=new Date();"
130  "while ((new Date()-t)<"+to_string(args[0]->Int32Value())+") v8.sleep();"
131  "})();";
132 
133  return ExecuteInternal(code);
134 }
135 
136 Handle<Value> InterpreterV8::FuncTimeout(const Arguments &args)
137 {
138  if (args.Length()<2)
139  return ThrowException(String::New("Number of arguments must be at least two."));
140 
141  if (!args[0]->IsNull() && !args[0]->IsInt32())
142  return ThrowException(String::New("Argument 0 not null and not an int32."));
143 
144  if (!args[1]->IsFunction())
145  return ThrowException(String::New("Argument 1 not a function."));
146 
147  if (args.Length()>2 && !args[2]->IsObject())
148  return ThrowException(String::New("Argument 2 not an object."));
149 
150  const int32_t timeout = args[0]->IsNull() ? 0 : args[0]->Int32Value();
151  const bool null = args[0]->IsNull();
152 
153  HandleScope handle_scope;
154 
155  Handle<Function> func = Handle<Function>::Cast(args[1]);
156 
157  const int nn = args.Length()==2 ? 0 : args.Length()-3;
158 
159  Handle<Value> argv[nn];
160  for (int i=0; i<nn; i++)
161  argv[i] = args[i+3];
162 
163  Time t;
164  while (1)
165  {
166  const Handle<Value> rc = args.Length()<3 ? func->Call(func, nn, argv) : func->Call(args[2]->ToObject(), nn, argv);
167  if (rc.IsEmpty())
168  return Undefined();
169 
170  if (!rc->IsUndefined())
171  return handle_scope.Close(rc);
172 
173  if (!null && Time()-t>=boost::posix_time::milliseconds(abs(timeout)))
174  break;
175 
176  // Theoretically, the CPU usage can be reduced by maybe a factor
177  // of four using a larger value, but this also means that the
178  // JavaScript is locked for a longer time.
179  const Unlocker unlock;
180  usleep(1000);
181  }
182 
183  if (timeout<0)
184  return Undefined();
185 
186  const string str = "Waiting for func to return a defined value timed out.";
187  return ThrowException(String::New(str.c_str()));
188 }
189 
190 void InterpreterV8::Thread(int &id, Persistent<Object> _this, Persistent<Function> func, uint32_t ms)
191 {
192  const Locker lock;
193 
194  if (fThreadId<0)
195  {
196  id = -1;
197  return;
198  }
199 
200  // Warning: As soon as id is set, the parent of this thread might terminate
201  // and hance the reference to id does not exist anymore. So, id
202  // is just a kind of return value and must not be used at all
203  // otherwise.
204 
205  const int id_local = V8::GetCurrentThreadId();
206  id = id_local;
207  fThreadIds.insert(id_local);
208 
209  const HandleScope handle_scope;
210 
211  func->CreationContext()->Enter();
212 
213  TryCatch exception;
214 
215  const bool rc = ms==0 || !ExecuteInternal("v8.sleep("+to_string(ms)+");").IsEmpty();
216  if (rc)
217  {
218  if (_this.IsEmpty())
219  func->Call(func, 0, NULL);
220  else
221  func->Call(_this, 0, NULL);
222  }
223 
224  func.Dispose();
225  _this.Dispose();
226 
227  fThreadIds.erase(id_local);
228 
229  if (!HandleException(exception, "thread"))
230  V8::TerminateExecution(fThreadId);
231 
232  func->CreationContext()->Exit();
233 }
234 
235 Handle<Value> InterpreterV8::FuncThread(const Arguments& args)
236 {
237  if (!args.IsConstructCall())
238  return ThrowException(String::New("Thread must be called as constructor."));
239 
240  if (args.Length()!=2 && args.Length()!=3)
241  return ThrowException(String::New("Number of arguments must be two or three."));
242 
243  if (!args[0]->IsUint32())
244  return ThrowException(String::New("Argument 0 not an uint32."));
245 
246  if (!args[1]->IsFunction())
247  return ThrowException(String::New("Argument 1 not a function."));
248 
249  if (args.Length()==3 && !args[2]->IsObject())
250  return ThrowException(String::New("Argument 2 not an object."));
251 
252  //if (!args.IsConstructCall())
253  // return Constructor(args);
254 
255  const HandleScope handle_scope;
256 
257  Handle<Function> handle = Handle<Function>::Cast(args[1]);
258 
259  Persistent<Function> func = Persistent<Function>::New(handle);
260  Persistent<Object> _this;
261  if (args.Length()==3)
262  _this = Persistent<Object>::New(args[2]->ToObject());
263 
264  const uint32_t ms = args[0]->Uint32Value();
265 
266  int id=-2;
267  fThreads.push_back(thread(bind(&InterpreterV8::Thread, this, ref(id), _this, func, ms)));
268  {
269  // Allow the thread to lock, so we can get the thread id.
270  const Unlocker unlock;
271  while (id==-2)
272  usleep(1);
273  }
274 
275  Handle<Object> self = args.This();
276 
277  self->Set(String::New("id"), Integer::NewFromUnsigned(id), ReadOnly);
278  self->Set(String::New("kill"), FunctionTemplate::New(WrapKill)->GetFunction(), ReadOnly);
279 
280  return Undefined();
281 }
282 
283 Handle<Value> InterpreterV8::FuncKill(const Arguments& args)
284 {
285  const uint32_t id = args.This()->Get(String::New("id"))->Uint32Value();
286 
287  V8::TerminateExecution(id);
288 
289  return Boolean::New(fThreadIds.erase(id));
290 }
291 
292 Handle<Value> InterpreterV8::FuncSend(const Arguments& args)
293 {
294  if (args.Length()==0)
295  return ThrowException(String::New("Number of arguments must be at least 1."));
296 
297  if (!args[0]->IsString())
298  return ThrowException(String::New("Argument 1 must be a string."));
299 
300  const String::AsciiValue str(args[0]);
301 
302  string command = *str;
303 
304  if (command.length()==0)
305  return ThrowException(String::New("Server name empty."));
306 
307  if (args.Length()==0)
308  {
309  if (command.find_first_of('/')==string::npos)
310  command += "/";
311  }
312 
313  // Escape all string arguments. All others can be kept as they are.
314  for (int i=1; i<args.Length(); i++)
315  {
316  string arg = *String::AsciiValue(args[i]);
317 
318  // Escape string
319  if (args[i]->IsString())
320  {
321  boost::replace_all(arg, "\\", "\\\\");
322  boost::replace_all(arg, "'", "\\'");
323  boost::replace_all(arg, "\"", "\\\"");
324  }
325 
326  command += " "+arg;
327  }
328 
329  try
330  {
331  return Boolean::New(JsSend(command));
332  }
333  catch (const runtime_error &e)
334  {
335  return ThrowException(String::New(e.what()));
336  }
337 }
338 
339 // ==========================================================================
340 // State control
341 // ==========================================================================
342 
343 Handle<Value> InterpreterV8::FuncWait(const Arguments& args)
344 {
345  if (args.Length()!=2 && args.Length()!=3)
346  return ThrowException(String::New("Number of arguments must be 2 or 3."));
347 
348  if (!args[0]->IsString())
349  return ThrowException(String::New("Argument 1 not a string."));
350 
351  if (!args[1]->IsInt32() && !args[1]->IsString())
352  return ThrowException(String::New("Argument 2 not an int32 and not a string."));
353 
354  if (args.Length()==3 && !args[2]->IsInt32() && !args[2]->IsUndefined())
355  return ThrowException(String::New("Argument 3 not an int32 and not undefined."));
356 
357  // Using a Javascript function has the advantage that it is fully
358  // interruptable without the need of C++ code
359 
360  const string index = args[1]->IsInt32() ? "s.index" : "s.name";
361  const bool timeout = args.Length()==3 && !args[2]->IsUndefined();
362  const string arg0 = *String::AsciiValue(args[0]);
363  const string state = args[1]->IsString() ? *String::AsciiValue(args[1]) : "";
364  const string arg1 = args[1]->IsString() ? ("\""+state+"\"") : to_string(args[1]->Int32Value());
365  const bool isNot = arg0[0]=='!';
366  const string name = isNot ? arg0.substr(1) : arg0;
367 
368  if (arg0.find_first_of("\"'")!=string::npos)
369  return ThrowException(String::New("Server name must not contain quotation marks."));
370 
371  if (args[1]->IsString())
372  if (state.find_first_of("\"'")!=string::npos)
373  return ThrowException(String::New("State name must not contain quotation marks."));
374 
375  string code = "(function(name,state,ms)"
376  "{";
377  if (timeout)
378  code += "var t = new Date();";
379  code += "while (1)"
380  "{"
381  "var s = dim.state(name);"
382  "if(!s)throw new Error('Waiting for state "+arg1+" of server "+arg0+" failed.');";
383  if (isNot)
384  code +=
385  "if(state!="+index+")return true;";
386  else
387  code +=
388  "if(state=="+index+")return true;";
389  if (timeout)
390  code += "if((new Date()-t)>Math.abs(ms))break;";
391 
392  code += "v8.sleep();"
393  "}";
394  if (timeout)
395  code += "if(ms>0)throw new Error('Waiting for state "+arg1+" of server "+arg0+" timed out.');";
396  code += "return false;"
397  "})('"+name+"',"+arg1;
398  if (timeout)
399  code += "," + (args[2]->IsUndefined()?"undefined":to_string(args[2]->Int32Value()));
400  code += ");";
401 
402  return ExecuteInternal(code);
403 }
404 
405 Handle<Value> InterpreterV8::FuncState(const Arguments& args)
406 {
407  if (args.Length()!=1)
408  return ThrowException(String::New("Number of arguments must be exactly 1."));
409 
410  if (!args[0]->IsString())
411  return ThrowException(String::New("Argument 1 must be a string."));
412 
413  // Return state.name/state.index
414 
415  const String::AsciiValue str(args[0]);
416 
417  const State rc = JsState(*str);
418  if (rc.index<=-256)
419  return Undefined();
420 
421  HandleScope handle_scope;
422 
423  Handle<Object> obj = Object::New();
424 
425  obj->Set(String::New("server"), String::New(*str), ReadOnly);
426  obj->Set(String::New("index"), Integer::New(rc.index), ReadOnly);
427  obj->Set(String::New("name"), String::New(rc.name.c_str()), ReadOnly);
428 
429  const Local<Value> date = Date::New(rc.time.JavaDate());
430  if (rc.index>-256 && !date.IsEmpty())
431  obj->Set(String::New("time"), date);
432 
433  return handle_scope.Close(obj);
434 }
435 
436 Handle<Value> InterpreterV8::FuncNewState(const Arguments& args)
437 {
438  if (args.Length()<1 || args.Length()>3)
439  return ThrowException(String::New("Number of arguments must be 1, 2 or 3."));
440 
441  if (!args[0]->IsUint32())
442  return ThrowException(String::New("Argument 1 must be an uint32."));
443  if (args.Length()>1 && !args[1]->IsString())
444  return ThrowException(String::New("Argument 2 must be a string."));
445  if (args.Length()>2 && !args[2]->IsString())
446  return ThrowException(String::New("Argument 3 must be a string."));
447 
448  const uint32_t index = args[0]->Int32Value();
449  const string name = *String::AsciiValue(args[1]);
450  const string comment = *String::AsciiValue(args[2]);
451 
452  if (index<10 || index>255)
453  return ThrowException(String::New("State must be in the range [10, 255]."));
454 
455  if (name.empty())
456  return ThrowException(String::New("State name must not be empty."));
457 
458  if (name.find_first_of(':')!=string::npos || name.find_first_of('=')!=string::npos)
459  return ThrowException(String::New("State name must not contain : or =."));
460 
461  struct Find : State
462  {
463  Find(int idx, const string &n) : State(idx, n) { }
464  bool operator()(const pair<int, string> &p) { return index==p.first || name==p.second; }
465  };
466 
467  if (find_if(fStates.begin(), fStates.end(), Find(index, name))!=fStates.end())
468  {
469  const string what =
470  "State index ["+to_string(index)+"] or name ["+name+"] already defined.";
471 
472  return ThrowException(String::New(what.c_str()));
473  }
474 
475  return Boolean::New(JsNewState(index, name, comment));
476 }
477 
478 Handle<Value> InterpreterV8::FuncSetState(const Arguments& args)
479 {
480  if (args.Length()!=1)
481  return ThrowException(String::New("Number of arguments must be exactly 1."));
482 
483  if (!args[0]->IsUint32() && !args[0]->IsString())
484  return ThrowException(String::New("Argument must be an unint32 or a string."));
485 
486  int index = -2;
487  if (args[0]->IsUint32())
488  {
489  index = args[0]->Int32Value();
490  }
491  else
492  {
493  const string name = *String::AsciiValue(args[0]);
494  index = JsGetState(name);
495  if (index==-2)
496  return ThrowException(String::New(("State '"+name+"' not found.").c_str()));
497  }
498 
499  if (index<10 || index>255)
500  return ThrowException(String::New("State must be in the range [10, 255]."));
501 
502  return Boolean::New(JsSetState(index));
503 }
504 
505 Handle<Value> InterpreterV8::FuncGetState(const Arguments& args)
506 {
507  if (args.Length()>0)
508  return ThrowException(String::New("getState must not take arguments."));
509 
510  const State state = JsGetCurrentState();
511 
512  HandleScope handle_scope;
513 
514  Handle<Object> rc = Object::New();
515  if (rc.IsEmpty())
516  return Undefined();
517 
518  rc->Set(String::New("index"), Integer::New(state.index), ReadOnly);
519  rc->Set(String::New("name"), String::New(state.name.c_str()), ReadOnly);
520  rc->Set(String::New("description"), String::New(state.comment.c_str()), ReadOnly);
521 
522  return handle_scope.Close(rc);
523 }
524 
525 Handle<Value> InterpreterV8::FuncGetStates(const Arguments& args)
526 {
527  if (args.Length()>1)
528  return ThrowException(String::New("getStates must not take more than one arguments."));
529 
530  if (args.Length()==1 && !args[0]->IsString())
531  return ThrowException(String::New("Argument must be a string."));
532 
533  const string server = args.Length()==1 ? *String::AsciiValue(args[0]) : "DIM_CONTROL";
534 
535  const vector<State> states = JsGetStates(server);
536 
537  HandleScope handle_scope;
538 
539  Handle<Object> list = Object::New();
540  if (list.IsEmpty())
541  return Undefined();
542 
543  for (auto it=states.begin(); it!=states.end(); it++)
544  {
545  Handle<Value> entry = StringObject::New(String::New(it->name.c_str()));
546  if (entry.IsEmpty())
547  return Undefined();
548 
549  StringObject::Cast(*entry)->Set(String::New("description"), String::New(it->comment.c_str()), ReadOnly);
550  list->Set(Integer::New(it->index), entry, ReadOnly);
551  }
552 
553  return handle_scope.Close(list);
554 }
555 
556 Handle<Value> InterpreterV8::FuncGetDescription(const Arguments& args)
557 {
558  if (args.Length()!=1)
559  return ThrowException(String::New("getDescription must take exactly one argument."));
560 
561  if (args.Length()==1 && !args[0]->IsString())
562  return ThrowException(String::New("Argument must be a string."));
563 
564  const string service = *String::AsciiValue(args[0]);
565 
566  const vector<Description> descriptions = JsGetDescription(service);
567  const set<Service> services = JsGetServices();
568 
569  auto is=services.begin();
570  for (; is!=services.end(); is++)
571  if (is->name==service)
572  break;
573 
574  if (is==services.end())
575  return Undefined();
576 
577  HandleScope handle_scope;
578 
579  Handle<Object> arr = fTemplateDescription->GetFunction()->NewInstance();//Object::New();
580  if (arr.IsEmpty())
581  return Undefined();
582 
583  auto it=descriptions.begin();
584  arr->Set(String::New("name"), String::New(it->name.c_str()), ReadOnly);
585  if (!it->comment.empty())
586  arr->Set(String::New("description"), String::New(it->comment.c_str()), ReadOnly);
587  if (is!=services.end())
588  {
589  arr->Set(String::New("server"), String::New(is->server.c_str()), ReadOnly);
590  arr->Set(String::New("service"), String::New(is->service.c_str()), ReadOnly);
591  arr->Set(String::New("isCommand"), Boolean::New(is->iscmd), ReadOnly);
592  if (!is->format.empty())
593  arr->Set(String::New("format"), String::New(is->format.c_str()), ReadOnly);
594  }
595 
596  uint32_t i=0;
597  for (it++; it!=descriptions.end(); it++)
598  {
599  Handle<Object> obj = Object::New();
600  if (obj.IsEmpty())
601  return Undefined();
602 
603  if (!it->name.empty())
604  obj->Set(String::New("name"), String::New(it->name.c_str()), ReadOnly);
605  if (!it->comment.empty())
606  obj->Set(String::New("description"), String::New(it->comment.c_str()), ReadOnly);
607  if (!it->unit.empty())
608  obj->Set(String::New("unit"), String::New(it->unit.c_str()), ReadOnly);
609 
610  arr->Set(i++, obj);
611  }
612 
613  return handle_scope.Close(arr);
614 }
615 
616 Handle<Value> InterpreterV8::FuncGetServices(const Arguments& args)
617 {
618  if (args.Length()>2)
619  return ThrowException(String::New("getServices must not take more than two argument."));
620 
621  if (args.Length()>=1 && !args[0]->IsString())
622  return ThrowException(String::New("First argument must be a string."));
623 
624  if (args.Length()==2 && !args[1]->IsBoolean())
625  return ThrowException(String::New("Second argument must be a boolean."));
626 
627  string arg0 = args.Length() ? *String::AsciiValue(args[0]) : "";
628  if (arg0=="*")
629  arg0=="";
630 
631  const set<Service> services = JsGetServices();
632 
633  HandleScope handle_scope;
634 
635  Handle<Array> arr = Array::New();
636  if (arr.IsEmpty())
637  return Undefined();
638 
639  uint32_t i=0;
640  for (auto is=services.begin(); is!=services.end(); is++)
641  {
642  if (!arg0.empty() && is->name.find(arg0)!=0)
643  continue;
644 
645  if (args.Length()==2 && args[1]->BooleanValue()!=is->iscmd)
646  continue;
647 
648  Handle<Object> obj = Object::New();
649  if (obj.IsEmpty())
650  return Undefined();
651 
652  obj->Set(String::New("name"), String::New(is->name.c_str()), ReadOnly);
653  obj->Set(String::New("server"), String::New(is->server.c_str()), ReadOnly);
654  obj->Set(String::New("service"), String::New(is->service.c_str()), ReadOnly);
655  obj->Set(String::New("isCommand"), Boolean::New(is->iscmd), ReadOnly);
656  if (!is->format.empty())
657  obj->Set(String::New("format"), String::New(is->format.c_str()), ReadOnly);
658 
659  arr->Set(i++, obj);
660  }
661 
662  return handle_scope.Close(arr);
663 }
664 
665 // ==========================================================================
666 // Internal functions
667 // ==========================================================================
668 
669 
670 // The callback that is invoked by v8 whenever the JavaScript 'print'
671 // function is called. Prints its arguments on stdout separated by
672 // spaces and ending with a newline.
673 Handle<Value> InterpreterV8::FuncLog(const Arguments& args)
674 {
675  for (int i=0; i<args.Length(); i++)
676  {
677  const String::AsciiValue str(args[i]);
678  if (*str)
679  JsPrint(*str);
680  }
681 
682  if (args.Length()==0)
683  JsPrint();
684 
685  return Undefined();
686 }
687 
688 Handle<Value> InterpreterV8::FuncAlarm(const Arguments& args)
689 {
690  for (int i=0; i<args.Length(); i++)
691  {
692  const String::AsciiValue str(args[i]);
693  if (*str)
694  JsAlarm(*str);
695  }
696 
697  if (args.Length()==0)
698  JsAlarm();
699 
700  return Undefined();
701 }
702 
703 Handle<Value> InterpreterV8::FuncOut(const Arguments& args)
704 {
705  for (int i=0; i<args.Length(); i++)
706  {
707  const String::AsciiValue str(args[i]);
708  if (*str)
709  JsOut(*str);
710  }
711  return Undefined();
712 }
713 
714 Handle<Value> InterpreterV8::FuncWarn(const Arguments& args)
715 {
716  for (int i=0; i<args.Length(); i++)
717  {
718  const String::AsciiValue str(args[i]);
719  if (*str)
720  JsWarn(*str);
721  }
722  return Undefined();
723 }
724 
725 // The callback that is invoked by v8 whenever the JavaScript 'load'
726 // function is called. Loads, compiles and executes its argument
727 // JavaScript file.
728 Handle<Value> InterpreterV8::FuncInclude(const Arguments& args)
729 {
730  if (args.Length()!=1)
731  return ThrowException(String::New("Number of arguments must be one."));
732 
733  if (!args[0]->IsString())
734  return ThrowException(String::New("Argument must be a string."));
735 
736  const String::AsciiValue file(args[0]);
737  if (*file == NULL)
738  return ThrowException(String::New("File name missing."));
739 
740  if (strlen(*file)==0)
741  return ThrowException(String::New("File name empty."));
742 
743  izstream fin(*file);
744  if (!fin)
745  return ThrowException(String::New(errno!=0?strerror(errno):"Insufficient memory for decompression"));
746 
747  string buffer;
748  getline(fin, buffer, '\0');
749 
750  if ((fin.fail() && !fin.eof()) || fin.bad())
751  return ThrowException(String::New(strerror(errno)));
752 
753  if (buffer.length()>1 && buffer[0]=='#' && buffer[1]=='!')
754  buffer.insert(0, "//");
755 
756  return ExecuteCode(buffer, *file);
757 }
758 
759 Handle<Value> InterpreterV8::FuncFile(const Arguments& args)
760 {
761  if (args.Length()!=1 && args.Length()!=2)
762  return ThrowException(String::New("Number of arguments must be one or two."));
763 
764  const String::AsciiValue file(args[0]);
765  if (*file == NULL)
766  return ThrowException(String::New("File name missing"));
767 
768  if (args.Length()==2 && !args[1]->IsString())
769  return ThrowException(String::New("Second argument must be a string."));
770 
771  const string delim = args.Length()==2 ? *String::AsciiValue(args[1]) : "";
772 
773  if (args.Length()==2 && delim.size()!=1)
774  return ThrowException(String::New("Second argument must be a string of length 1."));
775 
776  HandleScope handle_scope;
777 
778  izstream fin(*file);
779  if (!fin)
780  return ThrowException(String::New(errno!=0?strerror(errno):"Insufficient memory for decompression"));
781 
782  if (args.Length()==1)
783  {
784  string buffer;
785  getline(fin, buffer, '\0');
786  if ((fin.fail() && !fin.eof()) || fin.bad())
787  return ThrowException(String::New(strerror(errno)));
788 
789  Handle<Value> str = StringObject::New(String::New(buffer.c_str()));
790  StringObject::Cast(*str)->Set(String::New("name"), String::New(*file));
791  return handle_scope.Close(str);
792  }
793 
794  Handle<Array> arr = Array::New();
795  if (arr.IsEmpty())
796  return Undefined();
797 
798  int i=0;
799  string buffer;
800  while (getline(fin, buffer, delim[0]))
801  arr->Set(i++, String::New(buffer.c_str()));
802 
803  if ((fin.fail() && !fin.eof()) || fin.bad())
804  return ThrowException(String::New(strerror(errno)));
805 
806  arr->Set(String::New("name"), String::New(*file));
807  arr->Set(String::New("delim"), String::New(delim.c_str(), 1));
808 
809  return handle_scope.Close(arr);
810 }
811 
812 // ==========================================================================
813 // Mail
814 // ==========================================================================
815 
816 Handle<Value> InterpreterV8::ConstructorMail(const Arguments &args)
817 {
818  if (!args.IsConstructCall())
819  return ThrowException(String::New("Mail must be called as constructor"));
820 
821  if (args.Length()!=1 || !args[0]->IsString())
822  return ThrowException(String::New("Constructor must be called with a single string as argument"));
823 
824  HandleScope handle_scope;
825 
826  Handle<Array> rec = Array::New();
827  Handle<Array> att = Array::New();
828  Handle<Array> bcc = Array::New();
829  Handle<Array> cc = Array::New();
830  Handle<Array> txt = Array::New();
831  if (rec.IsEmpty() || att.IsEmpty() || bcc.IsEmpty() || cc.IsEmpty() || txt.IsEmpty())
832  return Undefined();
833 
834  Handle<Object> self = args.This();
835 
836  self->Set(String::New("subject"), args[0]->ToString(), ReadOnly);
837  self->Set(String::New("recipients"), rec, ReadOnly);
838  self->Set(String::New("attachments"), att, ReadOnly);
839  self->Set(String::New("bcc"), bcc, ReadOnly);
840  self->Set(String::New("cc"), cc, ReadOnly);
841  self->Set(String::New("text"), txt, ReadOnly);
842 
843  self->Set(String::New("send"), FunctionTemplate::New(WrapSendMail)->GetFunction(), ReadOnly);
844 
845  return handle_scope.Close(self);
846 }
847 
848 vector<string> InterpreterV8::ValueToArray(const Handle<Value> &val, bool only)
849 {
850  vector<string> rc;
851 
852  Handle<Array> arr = Handle<Array>::Cast(val);
853  for (uint32_t i=0; i<arr->Length(); i++)
854  {
855  Handle<Value> obj = arr->Get(i);
856  if (obj.IsEmpty())
857  continue;
858 
859  if (obj->IsNull() || obj->IsUndefined())
860  continue;
861 
862  if (only && !obj->IsString())
863  continue;
864 
865  rc.push_back(*String::AsciiValue(obj->ToString()));
866  }
867 
868  return rc;
869 }
870 
871 Handle<Value> InterpreterV8::FuncSendMail(const Arguments& args)
872 {
873  HandleScope handle_scope;
874 
875  if (args.Length()>1)
876  return ThrowException(String::New("Only one argument allowed."));
877 
878  if (args.Length()==1 && !args[0]->IsBoolean())
879  return ThrowException(String::New("Argument must be a boolean."));
880 
881  const bool block = args.Length()==0 || args[0]->BooleanValue();
882 
883  const Handle<Value> sub = args.This()->Get(String::New("subject"));
884  const Handle<Value> rec = args.This()->Get(String::New("recipients"));
885  const Handle<Value> txt = args.This()->Get(String::New("text"));
886  const Handle<Value> att = args.This()->Get(String::New("attachments"));
887  const Handle<Value> bcc = args.This()->Get(String::New("bcc"));
888  const Handle<Value> cc = args.This()->Get(String::New("cc"));
889 
890  const vector<string> vrec = ValueToArray(rec);
891  const vector<string> vtxt = ValueToArray(txt, false);
892  const vector<string> vatt = ValueToArray(att);
893  const vector<string> vbcc = ValueToArray(bcc);
894  const vector<string> vcc = ValueToArray(cc);
895 
896  if (vrec.size()==0)
897  return ThrowException(String::New("At least one valid string is required in 'recipients'."));
898  if (vtxt.size()==0)
899  return ThrowException(String::New("At least one valid string is required in 'text'."));
900 
901  const string subject = *String::AsciiValue(sub->ToString());
902 
903  FILE *pipe = popen(("from=no-reply@fact-project.org mailx -~ "+vrec[0]).c_str(), "w");
904  if (!pipe)
905  return ThrowException(String::New(strerror(errno)));
906 
907  fprintf(pipe, "%s", ("~s"+subject+"\n").c_str());
908  for (auto it=vrec.begin()+1; it<vrec.end(); it++)
909  fprintf(pipe, "%s", ("~t"+*it+"\n").c_str());
910  for (auto it=vbcc.begin(); it<vbcc.end(); it++)
911  fprintf(pipe, "%s", ("~b"+*it+"\n").c_str());
912  for (auto it=vcc.begin(); it<vcc.end(); it++)
913  fprintf(pipe, "%s", ("~c"+*it+"\n").c_str());
914  for (auto it=vatt.begin(); it<vatt.end(); it++)
915  fprintf(pipe, "%s", ("~@"+*it+"\n").c_str()); // Must not contain white spaces
916 
917  for (auto it=vtxt.begin(); it<vtxt.end(); it++)
918  fwrite((*it+"\n").c_str(), it->length()+1, 1, pipe);
919 
920  fprintf(pipe, "\n---\nsent by dimctrl");
921 
922  if (!block)
923  return Undefined();
924 
925  const int rc = pclose(pipe);
926 
927  const Locker lock;
928  return handle_scope.Close(Integer::New(WEXITSTATUS(rc)));
929 }
930 
931 // ==========================================================================
932 // Curl
933 // ==========================================================================
934 
935 Handle<Value> InterpreterV8::ConstructorCurl(const Arguments &args)
936 {
937  if (!args.IsConstructCall())
938  return ThrowException(String::New("Curl must be called as constructor"));
939 
940  if (args.Length()!=1 || !args[0]->IsString())
941  return ThrowException(String::New("Constructor must be called with a single string as argument"));
942 
943  HandleScope handle_scope;
944 
945  Handle<Array> data = Array::New();
946  if (data.IsEmpty())
947  return Undefined();
948 
949  Handle<Object> self = args.This();
950 
951  self->Set(String::New("url"), args[0]->ToString(), ReadOnly);
952  self->Set(String::New("data"), data, ReadOnly);
953 
954  self->Set(String::New("send"), FunctionTemplate::New(WrapSendCurl)->GetFunction(), ReadOnly);
955 
956  return handle_scope.Close(self);
957 }
958 
959 Handle<Value> InterpreterV8::FuncSendCurl(const Arguments& args)
960 {
961  HandleScope handle_scope;
962 
963  if (args.Length()>1)
964  return ThrowException(String::New("Only one argument allowed."));
965 
966  if (args.Length()==1 && !args[0]->IsBoolean())
967  return ThrowException(String::New("Argument must be a boolean."));
968 
969  const bool block = args.Length()==0 || args[0]->BooleanValue();
970 
971  const Handle<Value> url = args.This()->Get(String::New("url"));
972  const Handle<Value> data = args.This()->Get(String::New("data"));
973 
974  const vector<string> vdata = ValueToArray(data);
975  const string sdata = boost::algorithm::join(vdata, "&");
976 
977  const string surl = *String::AsciiValue(url->ToString());
978 
979  string cmd = "curl -sSf ";
980  if (!sdata.empty())
981  cmd += "--data '"+sdata+"' ";
982  cmd += "'http://"+surl+"' 2>&1 ";
983 
984  FILE *pipe = popen(cmd.c_str(), "r");
985  if (!pipe)
986  return ThrowException(String::New(strerror(errno)));
987 
988  if (!block)
989  return Undefined();
990 
991  string txt;
992 
993  while (!feof(pipe))
994  {
995  char buf[1025];
996  if (fgets(buf, 1024, pipe)==NULL)
997  break;
998  txt += buf;
999  }
1000 
1001  const int rc = pclose(pipe);
1002 
1003  Handle<Object> obj = Object::New();
1004 
1005  obj->Set(String::New("cmd"), String::New(cmd.c_str()));
1006  obj->Set(String::New("data"), String::New(txt.c_str()));
1007  obj->Set(String::New("rc"), Integer::NewFromUnsigned(WEXITSTATUS(rc)));
1008 
1009  const Locker lock;
1010  return handle_scope.Close(obj);
1011 }
1012 
1013 // ==========================================================================
1014 // Database
1015 // ==========================================================================
1016 
1017 Handle<Value> InterpreterV8::FuncDbClose(const Arguments &args)
1018 {
1019  void *ptr = External::Unwrap(args.This()->GetInternalField(0));
1020  if (!ptr)
1021  return Boolean::New(false);
1022 
1023 #ifdef HAVE_SQL
1024  Database *db = reinterpret_cast<Database*>(ptr);
1025  auto it = find(fDatabases.begin(), fDatabases.end(), db);
1026  fDatabases.erase(it);
1027  delete db;
1028 #endif
1029 
1030  HandleScope handle_scope;
1031 
1032  args.This()->SetInternalField(0, External::New(0));
1033 
1034  return handle_scope.Close(Boolean::New(true));
1035 }
1036 
1037 Handle<Value> InterpreterV8::FuncDbQuery(const Arguments &args)
1038 {
1039  if (args.Length()==0)
1040  return ThrowException(String::New("Arguments expected."));
1041 
1042  void *ptr = External::Unwrap(args.This()->GetInternalField(0));
1043  if (!ptr)
1044  return Undefined();
1045 
1046  string query;
1047  for (int i=0; i<args.Length(); i++)
1048  query += string(" ") + *String::AsciiValue(args[i]);
1049  query.erase(0, 1);
1050 
1051 #ifdef HAVE_SQL
1052  try
1053  {
1054  HandleScope handle_scope;
1055 
1056  Database *db = reinterpret_cast<Database*>(ptr);
1057 
1058  const mysqlpp::StoreQueryResult res = db->query(query).store();
1059 
1060  Handle<Array> ret = Array::New();
1061  if (ret.IsEmpty())
1062  return Undefined();
1063 
1064  ret->Set(String::New("table"), String::New(res.table()), ReadOnly);
1065  ret->Set(String::New("query"), String::New(query.c_str()), ReadOnly);
1066 
1067  Handle<Array> cols = Array::New();
1068  if (cols.IsEmpty())
1069  return Undefined();
1070 
1071  int irow=0;
1072  for (vector<mysqlpp::Row>::const_iterator it=res.begin(); it<res.end(); it++)
1073  {
1074  Handle<Object> row = Object::New();
1075  if (row.IsEmpty())
1076  return Undefined();
1077 
1078  const mysqlpp::FieldNames *list = it->field_list().list;
1079 
1080  for (size_t i=0; i<it->size(); i++)
1081  {
1082  const Handle<Value> name = String::New((*list)[i].c_str());
1083  if (irow==0)
1084  cols->Set(i, name);
1085 
1086  if ((*it)[i].is_null())
1087  {
1088  row->Set(name, Undefined(), ReadOnly);
1089  continue;
1090  }
1091 
1092  const string sql_type = (*it)[i].type().sql_name();
1093 
1094  const bool uns = sql_type.find("UNSIGNED")==string::npos;
1095 
1096  if (sql_type.find("BIGINT")!=string::npos)
1097  {
1098  if (uns)
1099  {
1100  const uint64_t val = (uint64_t)(*it)[i];
1101  if (val>UINT32_MAX)
1102  row->Set(name, Number::New(val), ReadOnly);
1103  else
1104  row->Set(name, Integer::NewFromUnsigned(val), ReadOnly);
1105  }
1106  else
1107  {
1108  const int64_t val = (int64_t)(*it)[i];
1109  if (val<INT32_MIN || val>INT32_MAX)
1110  row->Set(name, Number::New(val), ReadOnly);
1111  else
1112  row->Set(name, Integer::NewFromUnsigned(val), ReadOnly);
1113  }
1114  continue;
1115  }
1116 
1117  // 32 bit
1118  if (sql_type.find("INT")!=string::npos)
1119  {
1120  if (uns)
1121  row->Set(name, Integer::NewFromUnsigned((uint32_t)(*it)[i]), ReadOnly);
1122  else
1123  row->Set(name, Integer::New((int32_t)(*it)[i]), ReadOnly);
1124  continue;
1125  }
1126 
1127  if (sql_type.find("BOOL")!=string::npos )
1128  {
1129  row->Set(name, Boolean::New((bool)(*it)[i]), ReadOnly);
1130  continue;
1131  }
1132 
1133  if (sql_type.find("FLOAT")!=string::npos)
1134  {
1135  ostringstream val;
1136  val << setprecision(7) << (float)(*it)[i];
1137  row->Set(name, Number::New(stod(val.str())), ReadOnly);
1138  continue;
1139 
1140  }
1141  if (sql_type.find("DOUBLE")!=string::npos)
1142  {
1143  row->Set(name, Number::New((double)(*it)[i]), ReadOnly);
1144  continue;
1145  }
1146 
1147  if (sql_type.find("CHAR")!=string::npos ||
1148  sql_type.find("TEXT")!=string::npos)
1149  {
1150  row->Set(name, String::New((const char*)(*it)[i]), ReadOnly);
1151  continue;
1152  }
1153 
1154  time_t date = 0;
1155  if (sql_type.find("TIMESTAMP")!=string::npos)
1156  date = mysqlpp::Time((*it)[i]);
1157 
1158  if (sql_type.find("DATETIME")!=string::npos)
1159  date = mysqlpp::DateTime((*it)[i]);
1160 
1161  if (sql_type.find(" DATE ")!=string::npos)
1162  date = mysqlpp::Date((*it)[i]);
1163 
1164  if (date>0)
1165  {
1166  // It is important to catch the exception thrown
1167  // by Date::New in case of thread termination!
1168  const Local<Value> val = Date::New(date*1000);
1169  if (val.IsEmpty())
1170  return Undefined();
1171 
1172  row->Set(name, val, ReadOnly);
1173  }
1174  }
1175 
1176  ret->Set(irow++, row);
1177  }
1178 
1179  if (irow>0)
1180  ret->Set(String::New("cols"), cols, ReadOnly);
1181 
1182  return handle_scope.Close(ret);
1183  }
1184  catch (const exception &e)
1185  {
1186  return ThrowException(String::New(e.what()));
1187  }
1188 #endif
1189 }
1190 
1191 Handle<Value> InterpreterV8::FuncDatabase(const Arguments &args)
1192 {
1193  if (!args.IsConstructCall())
1194  return ThrowException(String::New("Database must be called as constructor."));
1195 
1196  if (args.Length()!=1)
1197  return ThrowException(String::New("Number of arguments must be 1."));
1198 
1199  if (!args[0]->IsString())
1200  return ThrowException(String::New("Argument 1 not a string."));
1201 
1202 #ifdef HAVE_SQL
1203  try
1204  {
1205  HandleScope handle_scope;
1206 
1207  //if (!args.IsConstructCall())
1208  // return Constructor(fTemplateDatabase, args);
1209 
1210  Database *db = new Database(*String::AsciiValue(args[0]));
1211  fDatabases.push_back(db);
1212 
1213  Handle<Object> self = args.This();
1214  self->Set(String::New("user"), String::New(db->user.c_str()), ReadOnly);
1215  self->Set(String::New("server"), String::New(db->server.c_str()), ReadOnly);
1216  self->Set(String::New("database"), String::New(db->db.c_str()), ReadOnly);
1217  self->Set(String::New("port"), db->port==0?Undefined():Integer::NewFromUnsigned(db->port), ReadOnly);
1218  self->Set(String::New("query"), FunctionTemplate::New(WrapDbQuery)->GetFunction(), ReadOnly);
1219  self->Set(String::New("close"), FunctionTemplate::New(WrapDbClose)->GetFunction(), ReadOnly);
1220  self->SetInternalField(0, External::New(db));
1221 
1222  return handle_scope.Close(self);
1223  }
1224  catch (const exception &e)
1225  {
1226  return ThrowException(String::New(e.what()));
1227  }
1228 #endif
1229 }
1230 
1231 // ==========================================================================
1232 // Services
1233 // ==========================================================================
1234 
1235 Handle<Value> InterpreterV8::Convert(char type, const char* &ptr)
1236 {
1237  // Dim values are always unsigned per (FACT++) definition
1238  switch (type)
1239  {
1240  case 'F':
1241  {
1242  // Remove the "imprecision" effect coming from casting a float to
1243  // a double and then showing it with double precision
1244  ostringstream val;
1245  val << setprecision(7) << *reinterpret_cast<const float*>(ptr);
1246  ptr += 4;
1247  return Number::New(stod(val.str()));
1248  }
1249  case 'D': { Handle<Value> v=Number::New(*reinterpret_cast<const double*>(ptr)); ptr+=8; return v; }
1250  case 'I':
1251  case 'L': { Handle<Value> v=Integer::NewFromUnsigned(*reinterpret_cast<const uint32_t*>(ptr)); ptr += 4; return v; }
1252  case 'X':
1253  {
1254  const int64_t val = *reinterpret_cast<const int64_t*>(ptr);
1255  ptr += 8;
1256  if (val>=0 && val<=UINT32_MAX)
1257  return Integer::NewFromUnsigned(val);
1258  if (val>=INT32_MIN && val<0)
1259  return Integer::New(val);
1260  return Number::New(val);
1261  }
1262  case 'S': { Handle<Value> v=Integer::NewFromUnsigned(*reinterpret_cast<const uint16_t*>(ptr)); ptr += 2; return v; }
1263  case 'C': { Handle<Value> v=Integer::NewFromUnsigned((uint16_t)*reinterpret_cast<const uint8_t*>(ptr)); ptr += 1; return v; }
1264  }
1265  return Undefined();
1266 }
1267 
1268 Handle<Value> InterpreterV8::FuncClose(const Arguments &args)
1269 {
1270  HandleScope handle_scope;
1271 
1272  //const void *ptr = Local<External>::Cast(args.Holder()->GetInternalField(0))->Value();
1273 
1274  const String::AsciiValue str(args.This()->Get(String::New("name")));
1275 
1276  const auto it = fReverseMap.find(*str);
1277  if (it!=fReverseMap.end())
1278  {
1279  it->second.Dispose();
1280  fReverseMap.erase(it);
1281  }
1282 
1283  args.This()->Set(String::New("isOpen"), Boolean::New(false), ReadOnly);
1284 
1285  return handle_scope.Close(Boolean::New(JsUnsubscribe(*str)));
1286 }
1287 
1288 Handle<Value> InterpreterV8::ConvertEvent(const EventImp *evt, uint64_t counter, const char *str)
1289 {
1290  const vector<Description> vec = JsDescription(str);
1291 
1292  Handle<Object> ret = fTemplateEvent->GetFunction()->NewInstance();//Object::New();
1293  if (ret.IsEmpty())
1294  return Undefined();
1295 
1296  const Local<Value> date = Date::New(evt->GetJavaDate());
1297  if (date.IsEmpty())
1298  return Undefined();
1299 
1300  ret->Set(String::New("name"), String::New(str), ReadOnly);
1301  ret->Set(String::New("format"), String::New(evt->GetFormat().c_str()), ReadOnly);
1302  ret->Set(String::New("qos"), Integer::New(evt->GetQoS()), ReadOnly);
1303  ret->Set(String::New("size"), Integer::New(evt->GetSize()), ReadOnly);
1304  ret->Set(String::New("counter"), Integer::New(counter), ReadOnly);
1305  if (evt->GetJavaDate()>0)
1306  ret->Set(String::New("time"), date, ReadOnly);
1307 
1308  // If names are available data will also be provided as an
1309  // object. If an empty event was received, but names are available,
1310  // the object will be empty. Otherwise 'obj' will be undefined.
1311  // obj===undefined: no data received
1312  // obj!==undefined, length==0: names for event available
1313  // obj!==undefined, obj.length>0: names available, data received
1314  Handle<Object> named = Object::New();
1315  if (vec.size()>0)
1316  ret->Set(String::New("obj"), named, ReadOnly);
1317 
1318  // If no event was received (usually a disconnection event in
1319  // the context of FACT++), no data is returned
1320  if (evt->IsEmpty())
1321  return ret;
1322 
1323  // If valid data was received, but the size was zero, then
1324  // null is returned as data
1325  // data===undefined: no data received
1326  // data===null: event received, but no data
1327  // data.length>0: event received, contains data
1328  if (evt->GetSize()==0 || evt->GetFormat().empty())
1329  {
1330  ret->Set(String::New("data"), Null(), ReadOnly);
1331  return ret;
1332  }
1333 
1334  // It seems a copy is required either in the boost which comes with
1335  // Ubuntu 16.04 or in gcc5 ?!
1336  const string fmt = evt->GetFormat();
1337 
1338  typedef boost::char_separator<char> separator;
1339  const boost::tokenizer<separator> tokenizer(fmt, separator(";:"));
1340 
1341  const vector<string> tok(tokenizer.begin(), tokenizer.end());
1342 
1343  Handle<Object> arr = tok.size()>1 ? Array::New() : ret;
1344  if (arr.IsEmpty())
1345  return Undefined();
1346 
1347  const char *ptr = evt->GetText();
1348  const char *end = evt->GetText()+evt->GetSize();
1349 
1350  try
1351  {
1352  size_t pos = 1;
1353  for (auto it=tok.begin(); it<tok.end() && ptr<end; it++, pos++)
1354  {
1355  char type = (*it)[0];
1356  it++;
1357 
1358  string name = pos<vec.size() ? vec[pos].name : "";
1359  if (tok.size()==1)
1360  name = "data";
1361 
1362  // Get element size
1363  uint32_t sz = 1;
1364  switch (type)
1365  {
1366  case 'X':
1367  case 'D': sz = 8; break;
1368  case 'F':
1369  case 'I':
1370  case 'L': sz = 4; break;
1371  case 'S': sz = 2; break;
1372  case 'C': sz = 1; break;
1373  }
1374 
1375  // Check if no number is attached if the size of the
1376  // received data is consistent with the format string
1377  if (it==tok.end() && (end-ptr)%sz>0)
1378  return Exception::Error(String::New(("Number of received bytes ["+to_string(evt->GetSize())+"] does not match format ["+evt->GetFormat()+"]").c_str()));
1379 
1380  // Check if format has a number attached.
1381  // If no number is attached calculate number of elements
1382  const uint32_t cnt = it==tok.end() ? (end-ptr)/sz : stoi(it->c_str());
1383 
1384  // is_str: Array of type C but unknown size (String)
1385  // is_one: Array of known size, but size is 1 (I:1)
1386  const bool is_str = type=='C' && it==tok.end();
1387  const bool is_one = cnt==1 && it!=tok.end();
1388 
1389  Handle<Value> v;
1390 
1391  if (is_str)
1392  v = String::New(ptr);
1393  if (is_one)
1394  v = Convert(type, ptr);
1395 
1396  // Array of known (I:5) or unknown size (I), but no string
1397  if (!is_str && !is_one)
1398  {
1399  Handle<Object> a = Array::New(cnt);
1400  if (a.IsEmpty())
1401  return Undefined();
1402 
1403  for (uint32_t i=0; i<cnt; i++)
1404  a->Set(i, Convert(type, ptr));
1405 
1406  v = a;
1407  }
1408 
1409  if (tok.size()>1)
1410  arr->Set(pos-1, v);
1411  else
1412  ret->Set(String::New("data"), v, ReadOnly);
1413 
1414  if (!name.empty())
1415  {
1416  const Handle<String> n = String::New(name.c_str());
1417  named->Set(n, v);
1418  }
1419  }
1420 
1421  if (tok.size()>1)
1422  ret->Set(String::New("data"), arr, ReadOnly);
1423 
1424  return ret;
1425  }
1426  catch (...)
1427  {
1428  return Exception::Error(String::New(("Format string conversion '"+evt->GetFormat()+"' failed.").c_str()));
1429  }
1430 }
1431 /*
1432 Handle<Value> InterpreterV8::FuncGetData(const Arguments &args)
1433 {
1434  HandleScope handle_scope;
1435 
1436  const String::AsciiValue str(args.Holder()->Get(String::New("name")));
1437 
1438  const pair<uint64_t, EventImp *> p = JsGetEvent(*str);
1439 
1440  const EventImp *evt = p.second;
1441  if (!evt)
1442  return Undefined();
1443 
1444  //if (counter==cnt)
1445  // return info.Holder();//Holder()->Get(String::New("data"));
1446 
1447  Handle<Value> ret = ConvertEvent(evt, p.first, *str);
1448  return ret->IsNativeError() ? ThrowException(ret) : handle_scope.Close(ret);
1449 }
1450 */
1451 Handle<Value> InterpreterV8::FuncGetData(const Arguments &args)
1452 {
1453  if (args.Length()>2)
1454  return ThrowException(String::New("Number of arguments must not be greater than 2."));
1455 
1456  if (args.Length()>=1 && !args[0]->IsInt32() && !args[0]->IsNull())
1457  return ThrowException(String::New("Argument 1 not an uint32."));
1458 
1459  if (args.Length()==2 && !args[1]->IsBoolean())
1460  return ThrowException(String::New("Argument 2 not a boolean."));
1461 
1462  // Using a Javascript function has the advantage that it is fully
1463  // interruptable without the need of C++ code
1464  const bool null = args.Length()>=1 && args[0]->IsNull();
1465  const int32_t timeout = args.Length()>=1 ? args[0]->Int32Value() : 0;
1466  const bool named = args.Length()<2 || args[1]->BooleanValue();
1467 
1468  HandleScope handle_scope;
1469 
1470  const Handle<String> data = String::New("data");
1471  const Handle<String> object = String::New("obj");
1472 
1473  const String::AsciiValue name(args.Holder()->Get(String::New("name")));
1474 
1475  TryCatch exception;
1476 
1477  Time t;
1478  while (!exception.HasCaught())
1479  {
1480  const pair<uint64_t, EventImp *> p = JsGetEvent(*name);
1481 
1482  const EventImp *evt = p.second;
1483  if (evt)
1484  {
1485  const Handle<Value> val = ConvertEvent(evt, p.first, *name);
1486  if (val->IsNativeError())
1487  return ThrowException(val);
1488 
1489  // Protect against the return of an exception
1490  if (val->IsObject())
1491  {
1492  const Handle<Object> event = val->ToObject();
1493  const Handle<Value> obj = event->Get(named?object:data);
1494  if (!obj.IsEmpty())
1495  {
1496  if (!named)
1497  {
1498  // No names (no 'obj'), but 'data'
1499  if (!obj->IsUndefined())
1500  return handle_scope.Close(val);
1501  }
1502  else
1503  {
1504  // Has names and data was received?
1505  if (obj->IsObject() && obj->ToObject()->GetOwnPropertyNames()->Length()>0)
1506  return handle_scope.Close(val);
1507  }
1508  }
1509  }
1510  }
1511 
1512  if (args.Length()==0)
1513  break;
1514 
1515  if (!null && Time()-t>=boost::posix_time::milliseconds(abs(timeout)))
1516  break;
1517 
1518  // Theoretically, the CPU usage can be reduced by maybe a factor
1519  // of four using a larger value, but this also means that the
1520  // JavaScript is locked for a longer time.
1521  const Unlocker unlock;
1522  usleep(1000);
1523  }
1524 
1525  // This hides the location of the exception, which is wanted.
1526  if (exception.HasCaught())
1527  return exception.ReThrow();
1528 
1529  if (timeout<0)
1530  return Undefined();
1531 
1532  const string str = "Waiting for a valid event of "+string(*name)+" timed out.";
1533  return ThrowException(String::New(str.c_str()));
1534 }
1535 
1536 
1537 // This is a callback from the RemoteControl piping event handling
1538 // to the java script ---> in test phase!
1539 void InterpreterV8::JsHandleEvent(const EventImp &evt, uint64_t cnt, const string &service)
1540 {
1541  // FIXME: This blocks service updates, we have to run this
1542  // in a dedicated thread.
1543  const Locker locker;
1544 
1545  if (fThreadId<0)
1546  return;
1547 
1548  const auto it = fReverseMap.find(service);
1549  if (it==fReverseMap.end())
1550  return;
1551 
1552  const HandleScope handle_scope;
1553 
1554  Handle<Object> obj = it->second;
1555 
1556  const Handle<String> onchange = String::New("onchange");
1557  if (!obj->Has(onchange))
1558  return;
1559 
1560  const Handle<Value> val = obj->Get(onchange);
1561  if (!val->IsFunction())
1562  return;
1563 
1564  obj->CreationContext()->Enter();
1565 
1566  // -------------------------------------------------------------------
1567 
1568  TryCatch exception;
1569 
1570  const int id = V8::GetCurrentThreadId();
1571  fThreadIds.insert(id);
1572 
1573  Handle<Value> ret = ConvertEvent(&evt, cnt, service.c_str());
1574  if (ret->IsObject())
1575  Handle<Function>::Cast(val)->Call(obj, 1, &ret);
1576 
1577  fThreadIds.erase(id);
1578 
1579  if (!HandleException(exception, "Service.onchange"))
1580  V8::TerminateExecution(fThreadId);
1581 
1582  if (ret->IsNativeError())
1583  {
1584  JsException(service+".onchange callback - "+*String::AsciiValue(ret));
1585  V8::TerminateExecution(fThreadId);
1586  }
1587 
1588  obj->CreationContext()->Exit();
1589 }
1590 
1591 Handle<Value> InterpreterV8::OnChangeSet(Local<String> prop, Local<Value> value, const AccessorInfo &)
1592 {
1593  // Returns the value if the setter intercepts the request. Otherwise, returns an empty handle.
1594  const string server = *String::AsciiValue(prop);
1595  auto it = fStateCallbacks.find(server);
1596 
1597  if (it!=fStateCallbacks.end())
1598  {
1599  it->second.Dispose();
1600  fStateCallbacks.erase(it);
1601  }
1602 
1603  if (value->IsFunction())
1604  fStateCallbacks[server] = Persistent<Object>::New(value->ToObject());
1605 
1606  return Handle<Value>();
1607 }
1608 
1609 void InterpreterV8::JsHandleState(const std::string &server, const State &state)
1610 {
1611  // FIXME: This blocks service updates, we have to run this
1612  // in a dedicated thread.
1613  const Locker locker;
1614 
1615  if (fThreadId<0)
1616  return;
1617 
1618  auto it = fStateCallbacks.find(server);
1619  if (it==fStateCallbacks.end())
1620  {
1621  it = fStateCallbacks.find("*");
1622  if (it==fStateCallbacks.end())
1623  return;
1624  }
1625 
1626  const HandleScope handle_scope;
1627 
1628  it->second->CreationContext()->Enter();
1629 
1630  // -------------------------------------------------------------------
1631 
1632  Handle<ObjectTemplate> obj = ObjectTemplate::New();
1633  obj->Set(String::New("server"), String::New(server.c_str()), ReadOnly);
1634 
1635  if (state.index>-256)
1636  {
1637  obj->Set(String::New("index"), Integer::New(state.index), ReadOnly);
1638  obj->Set(String::New("name"), String::New(state.name.c_str()), ReadOnly);
1639  obj->Set(String::New("comment"), String::New(state.comment.c_str()), ReadOnly);
1640  const Local<Value> date = Date::New(state.time.JavaDate());
1641  if (!date.IsEmpty())
1642  obj->Set(String::New("time"), date);
1643  }
1644 
1645  // -------------------------------------------------------------------
1646 
1647  TryCatch exception;
1648 
1649  const int id = V8::GetCurrentThreadId();
1650  fThreadIds.insert(id);
1651 
1652  Handle<Value> args[] = { obj->NewInstance() };
1653  Handle<Function> fun = Handle<Function>(Function::Cast(*it->second));
1654  fun->Call(fun, 1, args);
1655 
1656  fThreadIds.erase(id);
1657 
1658  if (!HandleException(exception, "dim.onchange"))
1659  V8::TerminateExecution(fThreadId);
1660 
1661  it->second->CreationContext()->Exit();
1662 }
1663 
1664 // ==========================================================================
1665 // Interrupt handling
1666 // ==========================================================================
1667 
1668 Handle<Value> InterpreterV8::FuncSetInterrupt(const Arguments &args)
1669 {
1670  if (args.Length()!=1)
1671  return ThrowException(String::New("Number of arguments must be 1."));
1672 
1673  if (!args[0]->IsNull() && !args[0]->IsUndefined() && !args[0]->IsFunction())
1674  return ThrowException(String::New("Argument not a function, null or undefined."));
1675 
1676  if (args[0]->IsNull() || args[0]->IsUndefined())
1677  {
1678  fInterruptCallback.Dispose();
1679  fInterruptCallback.Clear();
1680  return Undefined();
1681  }
1682 
1683  // Returns the value if the setter intercepts the request. Otherwise, returns an empty handle.
1684  fInterruptCallback = Persistent<Object>::New(args[0]->ToObject());
1685  return Undefined();
1686 }
1687 
1688 Handle<Value> InterpreterV8::HandleInterruptImp(string str, uint64_t time)
1689 {
1690  if (fInterruptCallback.IsEmpty())
1691  return Handle<Value>();
1692 
1693  const size_t p = str.find_last_of('\n');
1694 
1695  const string usr = p==string::npos?"":str.substr(p+1);
1696 
1697  string irq = p==string::npos?str:str.substr(0, p);
1698  const map<string,string> data = Tools::Split(irq, true);
1699 
1700  Local<Value> irq_str = String::New(irq.c_str());
1701  Local<Value> usr_str = String::New(usr.c_str());
1702  Local<Value> date = Date::New(time);
1703  Handle<Object> arr = Array::New(data.size());
1704 
1705  if (date.IsEmpty() || arr.IsEmpty())
1706  return Handle<Value>();
1707 
1708  for (auto it=data.begin(); it!=data.end(); it++)
1709  arr->Set(String::New(it->first.c_str()), String::New(it->second.c_str()));
1710 
1711  Handle<Value> args[] = { irq_str, arr, date, usr_str };
1712  Handle<Function> fun = Handle<Function>(Function::Cast(*fInterruptCallback));
1713 
1714  return fun->Call(fun, 4, args);
1715 }
1716 
1718 {
1719  // FIXME: This blocks service updates, we have to run this
1720  // in a dedicated thread.
1721  const Locker locker;
1722 
1723  if (fThreadId<0 || fInterruptCallback.IsEmpty())
1724  return -42;
1725 
1726  const HandleScope handle_scope;
1727 
1728  fInterruptCallback->CreationContext()->Enter();
1729 
1730  // -------------------------------------------------------------------
1731 
1732  TryCatch exception;
1733 
1734  const int id = V8::GetCurrentThreadId();
1735  fThreadIds.insert(id);
1736 
1737  const Handle<Value> val = HandleInterruptImp(evt.GetString(), evt.GetJavaDate());
1738 
1739  fThreadIds.erase(id);
1740 
1741  const int rc = !val.IsEmpty() && val->IsInt32() ? val->Int32Value() : 0;
1742 
1743  if (!HandleException(exception, "interrupt"))
1744  V8::TerminateExecution(fThreadId);
1745 
1746  fInterruptCallback->CreationContext()->Exit();
1747 
1748  return rc<10 || rc>255 ? -42 : rc;
1749 }
1750 
1751 Handle<Value> InterpreterV8::FuncTriggerInterrupt(const Arguments &args)
1752 {
1753  string data;
1754  for (int i=0; i<args.Length(); i++)
1755  {
1756  const String::AsciiValue str(args[i]);
1757 
1758  if (string(*str).find_first_of('\n')!=string::npos)
1759  return ThrowException(String::New("No argument must contain line breaks."));
1760 
1761  if (!*str)
1762  continue;
1763 
1764  data += *str;
1765  data += ' ';
1766  }
1767 
1768  HandleScope handle_scope;
1769 
1770  const Handle<Value> rc = HandleInterruptImp(Tools::Trim(data), Time().JavaDate());
1771  return handle_scope.Close(rc);
1772 }
1773 
1774 // ==========================================================================
1775 // Class 'Subscription'
1776 // ==========================================================================
1777 
1778 Handle<Value> InterpreterV8::FuncSubscription(const Arguments &args)
1779 {
1780  if (args.Length()!=1 && args.Length()!=2)
1781  return ThrowException(String::New("Number of arguments must be one or two."));
1782 
1783  if (!args[0]->IsString())
1784  return ThrowException(String::New("Argument 1 must be a string."));
1785 
1786  if (args.Length()==2 && !args[1]->IsFunction())
1787  return ThrowException(String::New("Argument 2 must be a function."));
1788 
1789  const String::AsciiValue str(args[0]);
1790 
1791  if (!args.IsConstructCall())
1792  {
1793  const auto it = fReverseMap.find(*str);
1794  if (it!=fReverseMap.end())
1795  return it->second;
1796 
1797  return Undefined();
1798  }
1799 
1800  const HandleScope handle_scope;
1801 
1802  Handle<Object> self = args.This();
1803  self->Set(String::New("get"), FunctionTemplate::New(WrapGetData)->GetFunction(), ReadOnly);
1804  self->Set(String::New("close"), FunctionTemplate::New(WrapClose)->GetFunction(), ReadOnly);
1805  self->Set(String::New("name"), String::New(*str), ReadOnly);
1806  self->Set(String::New("isOpen"), Boolean::New(true));
1807 
1808  if (args.Length()==2)
1809  self->Set(String::New("onchange"), args[1]);
1810 
1811  fReverseMap[*str] = Persistent<Object>::New(self);
1812 
1813  void *ptr = JsSubscribe(*str);
1814  if (ptr==0)
1815  return ThrowException(String::New(("Subscription to '"+string(*str)+"' already exists.").c_str()));
1816 
1817  self->SetInternalField(0, External::New(ptr));
1818 
1819  return Undefined();
1820 
1821  // Persistent<Object> p = Persistent<Object>::New(obj->NewInstance());
1822  // obj.MakeWeak((void*)1, Cleanup);
1823  // return obj;
1824 }
1825 
1826 // ==========================================================================
1827 // Astrometry
1828 // ==========================================================================
1829 #ifdef HAVE_NOVA
1830 
1831 double InterpreterV8::GetDataMember(const Arguments &args, const char *name)
1832 {
1833  return args.This()->Get(String::New(name))->NumberValue();
1834 }
1835 
1836 Handle<Value> InterpreterV8::CalcDist(const Arguments &args, const bool local)
1837 {
1838  if (args.Length()!=2)
1839  return ThrowException(String::New("dist must not be called with two arguments."));
1840 
1841  if (!args[0]->IsObject() || !args[1]->IsObject())
1842  return ThrowException(String::New("at least one argument not an object."));
1843 
1844  HandleScope handle_scope;
1845 
1846  Handle<Object> obj[2] =
1847  {
1848  Handle<Object>::Cast(args[0]),
1849  Handle<Object>::Cast(args[1])
1850  };
1851 
1852  const Handle<String> s_theta = String::New(local?"zd":"dec"); // was: zd
1853  const Handle<String> s_phi = String::New(local?"az":"ra"); // was: az
1854 
1855  const double conv_t = M_PI/180;
1856  const double conv_p = local ? -M_PI/180 : M_PI/12;
1857  const double offset = local ? 0 : M_PI;
1858 
1859  const double theta0 = offset - obj[0]->Get(s_theta)->NumberValue() * conv_t;
1860  const double phi0 = obj[0]->Get(s_phi )->NumberValue() * conv_p;
1861  const double theta1 = offset - obj[1]->Get(s_theta)->NumberValue() * conv_t;
1862  const double phi1 = obj[1]->Get(s_phi )->NumberValue() * conv_p;
1863 
1864  if (!finite(theta0) || !finite(theta1) || !finite(phi0) || !finite(phi1))
1865  return ThrowException(String::New("some values not valid or not finite."));
1866 
1867  /*
1868  const double x0 = sin(zd0) * cos(az0); // az0 -= az0
1869  const double y0 = sin(zd0) * sin(az0); // az0 -= az0
1870  const double z0 = cos(zd0);
1871 
1872  const double x1 = sin(zd1) * cos(az1); // az1 -= az0
1873  const double y1 = sin(zd1) * sin(az1); // az1 -= az0
1874  const double z1 = cos(zd1);
1875 
1876  const double res = acos(x0*x1 + y0*y1 + z0*z1) * 180/M_PI;
1877  */
1878 
1879  // cos(az1-az0) = cos(az1)*cos(az0) + sin(az1)*sin(az0)
1880 
1881  const double x = sin(theta0) * sin(theta1) * cos(phi1-phi0);
1882  const double y = cos(theta0) * cos(theta1);
1883 
1884  const double res = acos(x + y) * 180/M_PI;
1885 
1886  return handle_scope.Close(Number::New(res));
1887 }
1888 
1889 Handle<Value> InterpreterV8::LocalDist(const Arguments &args)
1890 {
1891  return CalcDist(args, true);
1892 }
1893 
1894 Handle<Value> InterpreterV8::SkyDist(const Arguments &args)
1895 {
1896  return CalcDist(args, false);
1897 }
1898 
1899 Handle<Value> InterpreterV8::MoonDisk(const Arguments &args)
1900 {
1901  if (args.Length()>1)
1902  return ThrowException(String::New("disk must not be called with more than one argument."));
1903 
1904  const uint64_t v = uint64_t(args[0]->NumberValue());
1905  const Time utc = args.Length()==0 ? Time() : Time(v/1000, v%1000);
1906 
1907  return Number::New(Nova::GetLunarDisk(utc.JD()));
1908 }
1909 
1910 Handle<Value> InterpreterV8::LocalToSky(const Arguments &args)
1911 {
1912  if (args.Length()>1)
1913  return ThrowException(String::New("toSky must not be called with more than one argument."));
1914 
1915  if (args.Length()==1 && !args[0]->IsDate())
1916  return ThrowException(String::New("Argument must be a Date"));
1917 
1918  Nova::ZdAzPosn hrz;
1919  hrz.zd = GetDataMember(args, "zd");
1920  hrz.az = GetDataMember(args, "az");
1921 
1922  if (!finite(hrz.zd) || !finite(hrz.az))
1923  return ThrowException(String::New("zd and az must be finite."));
1924 
1925  HandleScope handle_scope;
1926 
1927  const Local<Value> date =
1928  args.Length()==0 ? Date::New(Time().JavaDate()) : args[0];
1929  if (date.IsEmpty())
1930  return Undefined();
1931 
1932  const uint64_t v = uint64_t(date->NumberValue());
1933  const Time utc(v/1000, v%1000);
1934 
1935  const Nova::EquPosn equ = Nova::GetEquFromHrz(hrz, utc.JD());
1936 
1937  // -----------------------------
1938 
1939  Handle<Value> arg[] = { Number::New(equ.ra/15), Number::New(equ.dec), date };
1940  return handle_scope.Close(fTemplateSky->GetFunction()->NewInstance(3, arg));
1941 }
1942 
1943 Handle<Value> InterpreterV8::SkyToLocal(const Arguments &args)
1944 {
1945  if (args.Length()>1)
1946  return ThrowException(String::New("toLocal must not be called with more than one argument."));
1947 
1948  if (args.Length()==1 && !args[0]->IsDate())
1949  return ThrowException(String::New("Argument must be a Date"));
1950 
1951  Nova::EquPosn equ;
1952  equ.ra = GetDataMember(args, "ra")*15;
1953  equ.dec = GetDataMember(args, "dec");
1954 
1955  if (!finite(equ.ra) || !finite(equ.dec))
1956  return ThrowException(String::New("Ra and dec must be finite."));
1957 
1958  HandleScope handle_scope;
1959 
1960  const Local<Value> date =
1961  args.Length()==0 ? Date::New(Time().JavaDate()) : args[0];
1962  if (date.IsEmpty())
1963  return Undefined();
1964 
1965  const uint64_t v = uint64_t(date->NumberValue());
1966  const Time utc(v/1000, v%1000);
1967 
1968  const Nova::ZdAzPosn hrz = Nova::GetHrzFromEqu(equ, utc.JD());
1969 
1970  Handle<Value> arg[] = { Number::New(hrz.zd), Number::New(hrz.az), date };
1971  return handle_scope.Close(fTemplateLocal->GetFunction()->NewInstance(3, arg));
1972 }
1973 
1974 Handle<Value> InterpreterV8::MoonToLocal(const Arguments &args)
1975 {
1976  if (args.Length()>0)
1977  return ThrowException(String::New("toLocal must not be called with arguments."));
1978 
1979  Nova::EquPosn equ;
1980  equ.ra = GetDataMember(args, "ra")*15;
1981  equ.dec = GetDataMember(args, "dec");
1982 
1983  if (!finite(equ.ra) || !finite(equ.dec))
1984  return ThrowException(String::New("ra and dec must be finite."));
1985 
1986  HandleScope handle_scope;
1987 
1988  const Local<Value> date = args.This()->Get(String::New("time"));
1989  if (date.IsEmpty() || date->IsUndefined() )
1990  return Undefined();
1991 
1992  const uint64_t v = uint64_t(date->NumberValue());
1993  const Time utc(v/1000, v%1000);
1994 
1995  const Nova::ZdAzPosn hrz = Nova::GetHrzFromEqu(equ, utc.JD());
1996 
1997  Handle<Value> arg[] = { Number::New(hrz.zd), Number::New(hrz.az), date };
1998  return handle_scope.Close(fTemplateLocal->GetFunction()->NewInstance(3, arg));
1999 }
2000 
2001 Handle<Value> InterpreterV8::ConstructorMoon(const Arguments &args)
2002 {
2003  if (args.Length()>1)
2004  return ThrowException(String::New("Moon constructor must not be called with more than one argument."));
2005 
2006  if (args.Length()==1 && !args[0]->IsDate())
2007  return ThrowException(String::New("Argument must be a Date"));
2008 
2009  HandleScope handle_scope;
2010 
2011  const Local<Value> date =
2012  args.Length()==0 ? Date::New(Time().JavaDate()) : args[0];
2013  if (date.IsEmpty())
2014  return Undefined();
2015 
2016  const uint64_t v = uint64_t(date->NumberValue());
2017  const Time utc(v/1000, v%1000);
2018 
2019  const Nova::EquPosn equ = Nova::GetLunarEquCoords(utc.JD(), 0.01);
2020 
2021  // ----------------------------
2022 
2023  if (!args.IsConstructCall())
2024  return handle_scope.Close(Constructor(args));
2025 
2026  Handle<Function> function =
2027  FunctionTemplate::New(MoonToLocal)->GetFunction();
2028  if (function.IsEmpty())
2029  return Undefined();
2030 
2031  Handle<Object> self = args.This();
2032  self->Set(String::New("ra"), Number::New(equ.ra/15), ReadOnly);
2033  self->Set(String::New("dec"), Number::New(equ.dec), ReadOnly);
2034  self->Set(String::New("toLocal"), function, ReadOnly);
2035  self->Set(String::New("time"), date, ReadOnly);
2036 
2037  return handle_scope.Close(self);
2038 }
2039 
2040 Handle<Value> InterpreterV8::ConstructorSky(const Arguments &args)
2041 {
2042  if (args.Length()<2 || args.Length()>3)
2043  return ThrowException(String::New("Sky constructor takes two or three arguments."));
2044 
2045  if (args.Length()==3 && !args[2]->IsDate())
2046  return ThrowException(String::New("Third argument must be a Date."));
2047 
2048  const double ra = args[0]->NumberValue();
2049  const double dec = args[1]->NumberValue();
2050 
2051  if (!finite(ra) || !finite(dec))
2052  return ThrowException(String::New("Both arguments to Sky must be valid numbers."));
2053 
2054  // ----------------------------
2055 
2056  HandleScope handle_scope;
2057 
2058  if (!args.IsConstructCall())
2059  return handle_scope.Close(Constructor(args));
2060 
2061  Handle<Function> function =
2062  FunctionTemplate::New(SkyToLocal)->GetFunction();
2063  if (function.IsEmpty())
2064  return Undefined();
2065 
2066  Handle<Object> self = args.This();
2067  self->Set(String::New("ra"), Number::New(ra), ReadOnly);
2068  self->Set(String::New("dec"), Number::New(dec), ReadOnly);
2069  self->Set(String::New("toLocal"), function, ReadOnly);
2070  if (args.Length()==3)
2071  self->Set(String::New("time"), args[2], ReadOnly);
2072 
2073  return handle_scope.Close(self);
2074 }
2075 
2076 Handle<Value> InterpreterV8::ConstructorLocal(const Arguments &args)
2077 {
2078  if (args.Length()<2 || args.Length()>3)
2079  return ThrowException(String::New("Local constructor takes two or three arguments."));
2080 
2081  if (args.Length()==3 && !args[2]->IsDate())
2082  return ThrowException(String::New("Third argument must be a Date."));
2083 
2084  const double zd = args[0]->NumberValue();
2085  const double az = args[1]->NumberValue();
2086 
2087  if (!finite(zd) || !finite(az))
2088  return ThrowException(String::New("Both arguments to Local must be valid numbers."));
2089 
2090  // --------------------
2091 
2092  HandleScope handle_scope;
2093 
2094  if (!args.IsConstructCall())
2095  return handle_scope.Close(Constructor(args));
2096 
2097  Handle<Function> function =
2098  FunctionTemplate::New(LocalToSky)->GetFunction();
2099  if (function.IsEmpty())
2100  return Undefined();
2101 
2102  Handle<Object> self = args.This();
2103  self->Set(String::New("zd"), Number::New(zd), ReadOnly);
2104  self->Set(String::New("az"), Number::New(az), ReadOnly);
2105  self->Set(String::New("toSky"), function, ReadOnly);
2106  if (args.Length()==3)
2107  self->Set(String::New("time"), args[2], ReadOnly);
2108 
2109  return handle_scope.Close(self);
2110 }
2111 
2112 Handle<Object> InterpreterV8::ConstructRiseSet(const Handle<Value> time, const Nova::RstTime &rst, const bool &rc)
2113 {
2114  Handle<Object> obj = Object::New();
2115  obj->Set(String::New("time"), time, ReadOnly);
2116 
2117  const uint64_t v = uint64_t(time->NumberValue());
2118  const double jd = Time(v/1000, v%1000).JD();
2119 
2120  const bool isUp = rc>0 ||
2121  (rst.rise<rst.set && (jd>rst.rise && jd<rst.set)) ||
2122  (rst.rise>rst.set && (jd<rst.set || jd>rst.rise));
2123 
2124  obj->Set(String::New("isUp"), Boolean::New(rc>=0 && isUp), ReadOnly);
2125 
2126  if (rc!=0)
2127  return obj;
2128 
2129  Handle<Value> rise = Date::New(Time(rst.rise).JavaDate());
2130  Handle<Value> set = Date::New(Time(rst.set).JavaDate());
2131  Handle<Value> trans = Date::New(Time(rst.transit).JavaDate());
2132  if (rise.IsEmpty() || set.IsEmpty() || trans.IsEmpty())
2133  return Handle<Object>();
2134 
2135  obj->Set(String::New("rise"), rise, ReadOnly);
2136  obj->Set(String::New("set"), set, ReadOnly);
2137  obj->Set(String::New("transit"), trans, ReadOnly);
2138 
2139  return obj;
2140 }
2141 
2142 Handle<Value> InterpreterV8::SunHorizon(const Arguments &args)
2143 {
2144  if (args.Length()>2)
2145  return ThrowException(String::New("Sun.horizon must not be called with one or two arguments."));
2146 
2147  if (args.Length()==2 && !args[1]->IsDate())
2148  return ThrowException(String::New("Second argument must be a Date"));
2149 
2150  HandleScope handle_scope;
2151 
2152  double hrz = NAN;
2153  if (args.Length()==0 || args[0]->IsNull())
2154  hrz = LN_SOLAR_STANDART_HORIZON;
2155  if (args.Length()>0 && args[0]->IsNumber())
2156  hrz = args[0]->NumberValue();
2157  if (args.Length()>0 && args[0]->IsString())
2158  {
2159  string arg(Tools::Trim(*String::AsciiValue(args[0])));
2160  transform(arg.begin(), arg.end(), arg.begin(), ::tolower);
2161 
2162  if (arg==string("horizon").substr(0, arg.length()))
2163  hrz = LN_SOLAR_STANDART_HORIZON;
2164  if (arg==string("civil").substr(0, arg.length()))
2165  hrz = LN_SOLAR_CIVIL_HORIZON;
2166  if (arg==string("nautical").substr(0, arg.length()))
2167  hrz = LN_SOLAR_NAUTIC_HORIZON;
2168  if (arg==string("fact").substr(0, arg.length()))
2169  hrz = -13;
2170  if (arg==string("astronomical").substr(0, arg.length()))
2171  hrz = LN_SOLAR_ASTRONOMICAL_HORIZON;
2172  }
2173 
2174  if (!finite(hrz))
2175  return ThrowException(String::New("Second argument did not yield a valid number."));
2176 
2177  const Local<Value> date =
2178  args.Length()<2 ? Date::New(Time().JavaDate()) : args[1];
2179  if (date.IsEmpty())
2180  return Undefined();
2181 
2182  const uint64_t v = uint64_t(date->NumberValue());
2183  const Time utc(v/1000, v%1000);
2184 
2185  Nova::LnLatPosn obs = Nova::ORM();
2186 
2187  ln_rst_time sun;
2188  const int rc = ln_get_solar_rst_horizon(utc.JD()-0.5, &obs, hrz, &sun);
2189  Handle<Object> rst = ConstructRiseSet(date, sun, rc);
2190  rst->Set(String::New("horizon"), Number::New(hrz));
2191  return handle_scope.Close(rst);
2192 };
2193 
2194 Handle<Value> InterpreterV8::MoonHorizon(const Arguments &args)
2195 {
2196  if (args.Length()>1)
2197  return ThrowException(String::New("Moon.horizon must not be called with one argument."));
2198 
2199  if (args.Length()==1 && !args[0]->IsDate())
2200  return ThrowException(String::New("Argument must be a Date"));
2201 
2202  HandleScope handle_scope;
2203 
2204  const Local<Value> date =
2205  args.Length()==0 ? Date::New(Time().JavaDate()) : args[0];
2206  if (date.IsEmpty())
2207  return Undefined();
2208 
2209  const uint64_t v = uint64_t(date->NumberValue());
2210  const Time utc(v/1000, v%1000);
2211 
2212  Nova::LnLatPosn obs = Nova::ORM();
2213 
2214  ln_rst_time moon;
2215  const int rc = ln_get_lunar_rst(utc.JD()-0.5, &obs, &moon);
2216  Handle<Object> rst = ConstructRiseSet(date, moon, rc);
2217  return handle_scope.Close(rst);
2218 };
2219 #endif
2220 
2221 // ==========================================================================
2222 // Process control
2223 // ==========================================================================
2224 
2225 bool InterpreterV8::HandleException(TryCatch& try_catch, const char *where)
2226 {
2227  if (!try_catch.HasCaught() || !try_catch.CanContinue())
2228  return true;
2229 
2230  const HandleScope handle_scope;
2231 
2232  Handle<Value> except = try_catch.Exception();
2233  if (except.IsEmpty() || except->IsNull())
2234  return true;
2235 
2236  const String::AsciiValue exception(except);
2237 
2238  const Handle<Message> message = try_catch.Message();
2239  if (message.IsEmpty())
2240  return false;
2241 
2242  ostringstream out;
2243 
2244  if (!message->GetScriptResourceName()->IsUndefined())
2245  {
2246  // Print (filename):(line number): (message).
2247  const String::AsciiValue filename(message->GetScriptResourceName());
2248  if (filename.length()>0)
2249  {
2250  out << *filename;
2251  if (message->GetLineNumber()>0)
2252  out << ": l." << message->GetLineNumber();
2253  if (*exception)
2254  out << ": ";
2255  }
2256  }
2257 
2258  if (*exception)
2259  out << *exception;
2260 
2261  out << " [" << where << "]";
2262 
2263  JsException(out.str());
2264 
2265  // Print line of source code.
2266  const String::AsciiValue sourceline(message->GetSourceLine());
2267  if (*sourceline)
2268  JsException(*sourceline);
2269 
2270  // Print wavy underline (GetUnderline is deprecated).
2271  const int start = message->GetStartColumn();
2272  const int end = message->GetEndColumn();
2273 
2274  out.str("");
2275  if (start>0)
2276  out << setfill(' ') << setw(start) << ' ';
2277  out << setfill('^') << setw(end-start) << '^';
2278 
2279  JsException(out.str());
2280 
2281  const String::AsciiValue stack_trace(try_catch.StackTrace());
2282  if (stack_trace.length()<=0)
2283  return false;
2284 
2285  if (!*stack_trace)
2286  return false;
2287 
2288  const string trace(*stack_trace);
2289 
2290  typedef boost::char_separator<char> separator;
2291  const boost::tokenizer<separator> tokenizer(trace, separator("\n"));
2292 
2293  // maybe skip: " at internal:"
2294  // maybe skip: " at unknown source:"
2295 
2296  auto it = tokenizer.begin();
2297  JsException("");
2298  while (it!=tokenizer.end())
2299  JsException(*it++);
2300 
2301  return false;
2302 }
2303 
2304 Handle<Value> InterpreterV8::ExecuteInternal(const string &code)
2305 {
2306  // Try/catch and re-throw hides our internal code from
2307  // the displayed exception showing the origin and shows
2308  // the user function instead.
2309  TryCatch exception;
2310 
2311  const Handle<Value> result = ExecuteCode(code);
2312 
2313  // This hides the location of the exception in the internal code,
2314  // which is wanted.
2315  if (exception.HasCaught())
2316  exception.ReThrow();
2317 
2318  return result;
2319 }
2320 
2321 Handle<Value> InterpreterV8::ExecuteCode(const string &code, const string &file)
2322 {
2323  HandleScope handle_scope;
2324 
2325  const Handle<String> source = String::New(code.c_str(), code.size());
2326  const Handle<String> origin = String::New(file.c_str());
2327  if (source.IsEmpty())
2328  return Undefined();
2329 
2330  const Handle<Script> script = Script::Compile(source, origin);
2331  if (script.IsEmpty())
2332  return Undefined();
2333 
2334  const Handle<String> __date__ = String::New("__DATE__");
2335  const Handle<String> __file__ = String::New("__FILE__");
2336 
2337  Handle<Value> save_date;
2338  Handle<Value> save_file;
2339 
2340  Handle<Object> global = Context::GetCurrent()->Global();
2341  if (!global.IsEmpty())
2342  {
2343  struct stat attrib;
2344  if (stat(file.c_str(), &attrib)==0)
2345  {
2346  save_date = global->Get(__date__);
2347  save_file = global->Get(__file__);
2348 
2349  global->Set(__file__, String::New(file.c_str()));
2350 
2351  const Local<Value> date = Date::New(attrib.st_mtime*1000);
2352  if (!date.IsEmpty())
2353  global->Set(__date__, date);
2354  }
2355  }
2356 
2357  const Handle<Value> rc = script->Run();
2358  if (rc.IsEmpty())
2359  return Undefined();
2360 
2361  if (!global.IsEmpty() && !save_date.IsEmpty())
2362  {
2363  global->ForceSet(__date__, save_date);
2364  global->ForceSet(__file__, save_file);
2365  }
2366 
2367  return handle_scope.Close(rc);
2368 }
2369 
2370 void InterpreterV8::ExecuteConsole()
2371 {
2372  JsSetState(3);
2373 
2374  WindowLog lout;
2375  lout << "\n " << kUnderline << " JavaScript interpreter " << kReset << " (enter '.q' to quit)\n" << endl;
2376 
2377  Readline::StaticPushHistory("java.his");
2378 
2379  string command;
2380  while (1)
2381  {
2382  // Create a local handle scope so that left-overs from single
2383  // console inputs will not fill up the memory
2384  const HandleScope handle_scope;
2385 
2386  // Unlocking is necessary for the preemption to work
2387  const Unlocker global_unlock;
2388 
2389  const string buffer = Tools::Trim(Readline::StaticPrompt(command.empty() ? "JS> " : " \\> "));
2390  if (buffer==".q")
2391  break;
2392 
2393  // buffer empty, do nothing
2394  if (buffer.empty())
2395  continue;
2396 
2397  // Compose command
2398  if (!command.empty())
2399  command += ' ';
2400  command += buffer;
2401 
2402  // If line ends with a backslash, allow addition of next line
2403  auto back = command.rbegin();
2404  if (*back=='\\')
2405  {
2406  *back = ' ';
2407  command = Tools::Trim(command);
2408  continue;
2409  }
2410 
2411  // Locking is necessary to be able to execute java script code
2412  const Locker lock;
2413 
2414  // Catch exceptions during code compilation
2415  TryCatch exception;
2416 
2417  // Execute code which was entered
2418  const Handle<Value> rc = ExecuteCode(command, "console");
2419 
2420  // If all went well and the result wasn't undefined then print
2421  // the returned value.
2422  if (!rc->IsUndefined() && !rc->IsFunction())
2423  JsResult(*String::AsciiValue(rc));
2424 
2425  if (!HandleException(exception, "console"))
2426  lout << endl;
2427 
2428  // Stop all other threads
2429  for (auto it=fThreadIds.begin(); it!=fThreadIds.end(); it++)
2430  V8::TerminateExecution(*it);
2431 
2432  // Allow the java scripts (threads) to run and hence to terminate
2433  const Unlocker unlock;
2434 
2435  // Wait until all threads are terminated
2436  while (!fThreadIds.empty())
2437  usleep(1000);
2438 
2439  // command has been executed, collect new command
2440  command = "";
2441  }
2442 
2443  lout << endl;
2444 
2445  Readline::StaticPopHistory("java.his");
2446 }
2447 
2448 // ==========================================================================
2449 // CORE
2450 // ==========================================================================
2451 
2452 InterpreterV8::InterpreterV8() : fThreadId(-1)
2453 {
2454  const string ver(V8::GetVersion());
2455 
2456  typedef boost::char_separator<char> separator;
2457  const boost::tokenizer<separator> tokenizer(ver, separator("."));
2458 
2459  const vector<string> tok(tokenizer.begin(), tokenizer.end());
2460 
2461  const int major = tok.size()>0 ? stol(tok[0]) : -1;
2462  const int minor = tok.size()>1 ? stol(tok[1]) : -1;
2463  const int build = tok.size()>2 ? stol(tok[2]) : -1;
2464 
2465  if (major>3 || (major==3 && minor>9) || (major==3 && minor==9 && build>10))
2466  {
2467  const string argv = "--use_strict";
2468  V8::SetFlagsFromString(argv.c_str(), argv.size());
2469  }
2470 
2471  /*
2472  const string argv1 = "--prof";
2473  const string argv2 = "--noprof-lazy";
2474 
2475  V8::SetFlagsFromString(argv1.c_str(), argv1.size());
2476  V8::SetFlagsFromString(argv2.c_str(), argv2.size());
2477  */
2478 
2479  This = this;
2480 }
2481 
2482 Handle<Value> InterpreterV8::Constructor(/*Handle<FunctionTemplate> T,*/ const Arguments &args)
2483 {
2484  Handle<Value> argv[args.Length()];
2485 
2486  for (int i=0; i<args.Length(); i++)
2487  argv[i] = args[i];
2488 
2489  return args.Callee()->NewInstance(args.Length(), argv);
2490 }
2491 
2492 
2493 void InterpreterV8::AddFormatToGlobal()// const
2494 {
2495  const string code =
2496  "String.form = function(str, arr)"
2497  "{"
2498  "var i = -1;"
2499  "function callback(exp, p0, p1, p2, p3, p4/*, pos, str*/)"
2500  "{"
2501  "if (exp=='%%')"
2502  "return '%';"
2503  ""
2504  "if (arr[++i]===undefined)"
2505  "return undefined;"
2506  ""
2507  "var exp = p2 ? parseInt(p2.substr(1)) : undefined;"
2508  "var base = p3 ? parseInt(p3.substr(1)) : undefined;"
2509  ""
2510  "var val;"
2511  "switch (p4)"
2512  "{"
2513  "case 's': val = arr[i]; break;"
2514  "case 'c': val = arr[i][0]; break;"
2515  "case 'f': val = parseFloat(arr[i]).toFixed(exp); break;"
2516  "case 'p': val = parseFloat(arr[i]).toPrecision(exp); break;"
2517  "case 'e': val = parseFloat(arr[i]).toExponential(exp); break;"
2518  "case 'x': val = parseInt(arr[i]).toString(base?base:16); break;"
2519  "case 'd': val = parseFloat(parseInt(arr[i], base?base:10).toPrecision(exp)).toFixed(0); break;"
2520  //"default:\n"
2521  //" throw new SyntaxError('Conversion specifier '+p4+' unknown.');\n"
2522  "}"
2523  ""
2524  "val = typeof(val)=='object' ? JSON.stringify(val) : val.toString(base);"
2525  ""
2526  "var sz = parseInt(p1); /* padding size */"
2527  "var ch = p1 && p1[0]=='0' ? '0' : ' '; /* isnull? */"
2528  "while (val.length<sz)"
2529  "val = p0 !== undefined ? val+ch : ch+val; /* isminus? */"
2530  ""
2531  "return val;"
2532  "}"
2533  ""
2534  "var regex = /%(-)?(0?[0-9]+)?([.][0-9]+)?([#][0-9]+)?([scfpexd])/g;"
2535  "return str.replace(regex, callback);"
2536  "}"
2537  "\n"
2538  "String.prototype.$ = function()"
2539  "{"
2540  "return String.form(this, Array.prototype.slice.call(arguments));"
2541  "}"
2542  "\n"
2543  "String.prototype.count = function(c,i)"
2544  "{"
2545  "return (this.match(new RegExp(c,i?'gi':'g'))||[]).length;"
2546  "}"/*
2547  "\n"
2548  "var format = function()"
2549  "{"
2550  "return dim.format(arguments[0], Array.prototype.slice.call(arguments,1));"
2551  "}"*/;
2552 
2553  // ExcuteInternal does not work properly here...
2554  // If suring compilation an exception is thrown, it will not work
2555  Handle<Script> script = Script::New(String::New(code.c_str()), String::New("internal"));
2556  if (!script.IsEmpty())
2557  script->Run();
2558 }
2559 
2560 void InterpreterV8::JsLoad(const std::string &)
2561 {
2563 }
2564 
2565 void InterpreterV8::JsEnd(const std::string &)
2566 {
2568 }
2569 
2570 bool InterpreterV8::JsRun(const string &filename, const map<string, string> &map)
2571 {
2572  const Locker locker;
2573  fThreadId = V8::GetCurrentThreadId();
2574 
2575  JsPrint(string("JavaScript Engine V8 ")+V8::GetVersion());
2576 
2577  JsLoad(filename);
2578 
2579  const HandleScope handle_scope;
2580 
2581  // Create a template for the global object.
2582  Handle<ObjectTemplate> dim = ObjectTemplate::New();
2583  dim->Set(String::New("log"), FunctionTemplate::New(WrapLog), ReadOnly);
2584  dim->Set(String::New("alarm"), FunctionTemplate::New(WrapAlarm), ReadOnly);
2585  dim->Set(String::New("wait"), FunctionTemplate::New(WrapWait), ReadOnly);
2586  dim->Set(String::New("send"), FunctionTemplate::New(WrapSend), ReadOnly);
2587  dim->Set(String::New("state"), FunctionTemplate::New(WrapState), ReadOnly);
2588  dim->Set(String::New("version"), Integer::New(DIM_VERSION_NUMBER), ReadOnly);
2589  dim->Set(String::New("getStates"), FunctionTemplate::New(WrapGetStates), ReadOnly);
2590  dim->Set(String::New("getDescription"), FunctionTemplate::New(WrapGetDescription), ReadOnly);
2591  dim->Set(String::New("getServices"), FunctionTemplate::New(WrapGetServices), ReadOnly);
2592 
2593  Handle<ObjectTemplate> dimctrl = ObjectTemplate::New();
2594  dimctrl->Set(String::New("defineState"), FunctionTemplate::New(WrapNewState), ReadOnly);
2595  dimctrl->Set(String::New("setState"), FunctionTemplate::New(WrapSetState), ReadOnly);
2596  dimctrl->Set(String::New("getState"), FunctionTemplate::New(WrapGetState), ReadOnly);
2597  dimctrl->Set(String::New("setInterruptHandler"), FunctionTemplate::New(WrapSetInterrupt), ReadOnly);
2598  dimctrl->Set(String::New("triggerInterrupt"), FunctionTemplate::New(WrapTriggerInterrupt), ReadOnly);
2599 
2600  Handle<ObjectTemplate> v8 = ObjectTemplate::New();
2601  v8->Set(String::New("sleep"), FunctionTemplate::New(WrapSleep), ReadOnly);
2602  v8->Set(String::New("timeout"), FunctionTemplate::New(WrapTimeout), ReadOnly);
2603  v8->Set(String::New("version"), String::New(V8::GetVersion()), ReadOnly);
2604 
2605  Handle<ObjectTemplate> console = ObjectTemplate::New();
2606  console->Set(String::New("out"), FunctionTemplate::New(WrapOut), ReadOnly);
2607  console->Set(String::New("warn"), FunctionTemplate::New(WrapWarn), ReadOnly);
2608 
2609  Handle<ObjectTemplate> onchange = ObjectTemplate::New();
2610  onchange->SetNamedPropertyHandler(OnChangeGet, WrapOnChangeSet);
2611  dim->Set(String::New("onchange"), onchange);
2612 
2613  Handle<ObjectTemplate> global = ObjectTemplate::New();
2614  global->Set(String::New("v8"), v8, ReadOnly);
2615  global->Set(String::New("dim"), dim, ReadOnly);
2616  global->Set(String::New("dimctrl"), dimctrl, ReadOnly);
2617  global->Set(String::New("console"), console, ReadOnly);
2618  global->Set(String::New("include"), FunctionTemplate::New(WrapInclude), ReadOnly);
2619  global->Set(String::New("exit"), FunctionTemplate::New(WrapExit), ReadOnly);
2620 
2621  Handle<FunctionTemplate> sub = FunctionTemplate::New(WrapSubscription);
2622  sub->SetClassName(String::New("Subscription"));
2623  sub->InstanceTemplate()->SetInternalFieldCount(1);
2624  global->Set(String::New("Subscription"), sub, ReadOnly);
2625 
2626 #ifdef HAVE_SQL
2627  Handle<FunctionTemplate> db = FunctionTemplate::New(WrapDatabase);
2628  db->SetClassName(String::New("Database"));
2629  db->InstanceTemplate()->SetInternalFieldCount(1);
2630  global->Set(String::New("Database"), db, ReadOnly);
2631 #endif
2632 
2633  Handle<FunctionTemplate> thread = FunctionTemplate::New(WrapThread);
2634  thread->SetClassName(String::New("Thread"));
2635  global->Set(String::New("Thread"), thread, ReadOnly);
2636 
2637  Handle<FunctionTemplate> file = FunctionTemplate::New(WrapFile);
2638  file->SetClassName(String::New("File"));
2639  global->Set(String::New("File"), file, ReadOnly);
2640 
2641  Handle<FunctionTemplate> evt = FunctionTemplate::New();
2642  evt->SetClassName(String::New("Event"));
2643  global->Set(String::New("Event"), evt, ReadOnly);
2644 
2645  Handle<FunctionTemplate> desc = FunctionTemplate::New();
2646  desc->SetClassName(String::New("Description"));
2647  global->Set(String::New("Description"), desc, ReadOnly);
2648 
2649  fTemplateEvent = evt;
2650  fTemplateDescription = desc;
2651 
2652 #ifdef HAVE_MAILX
2653  Handle<FunctionTemplate> mail = FunctionTemplate::New(ConstructorMail);
2654  mail->SetClassName(String::New("Mail"));
2655  global->Set(String::New("Mail"), mail, ReadOnly);
2656 #endif
2657 
2658 #ifdef HAVE_CURL
2659  Handle<FunctionTemplate> curl = FunctionTemplate::New(ConstructorCurl);
2660  mail->SetClassName(String::New("Curl"));
2661  global->Set(String::New("Curl"), curl, ReadOnly);
2662 #endif
2663 
2664 #ifdef HAVE_NOVA
2665  Handle<FunctionTemplate> sky = FunctionTemplate::New(ConstructorSky);
2666  sky->SetClassName(String::New("Sky"));
2667  sky->Set(String::New("dist"), FunctionTemplate::New(SkyDist), ReadOnly);
2668  global->Set(String::New("Sky"), sky, ReadOnly);
2669 
2670  Handle<FunctionTemplate> loc = FunctionTemplate::New(ConstructorLocal);
2671  loc->SetClassName(String::New("Local"));
2672  loc->Set(String::New("dist"), FunctionTemplate::New(LocalDist), ReadOnly);
2673  global->Set(String::New("Local"), loc, ReadOnly);
2674 
2675  Handle<FunctionTemplate> moon = FunctionTemplate::New(ConstructorMoon);
2676  moon->SetClassName(String::New("Moon"));
2677  moon->Set(String::New("disk"), FunctionTemplate::New(MoonDisk), ReadOnly);
2678  moon->Set(String::New("horizon"), FunctionTemplate::New(MoonHorizon), ReadOnly);
2679  global->Set(String::New("Moon"), moon, ReadOnly);
2680 
2681  Handle<FunctionTemplate> sun = FunctionTemplate::New();
2682  sun->SetClassName(String::New("Sun"));
2683  sun->Set(String::New("horizon"), FunctionTemplate::New(SunHorizon), ReadOnly);
2684  global->Set(String::New("Sun"), sun, ReadOnly);
2685 
2686  fTemplateLocal = loc;
2687  fTemplateSky = sky;
2688 #endif
2689 
2690  // Persistent
2691  Persistent<Context> context = Context::New(NULL, global);
2692  if (context.IsEmpty())
2693  {
2694  JsException("Creation of global context failed...");
2695  JsEnd(filename);
2696  return false;
2697  }
2698 
2699  // Switch off eval(). It is not possible to track it's exceptions.
2700  context->AllowCodeGenerationFromStrings(false);
2701 
2702  Context::Scope scope(context);
2703 
2704  Handle<Array> args = Array::New(map.size());
2705  for (auto it=map.begin(); it!=map.end(); it++)
2706  args->Set(String::New(it->first.c_str()), String::New(it->second.c_str()));
2707  context->Global()->Set(String::New("$"), args, ReadOnly);
2708  context->Global()->Set(String::New("arg"), args, ReadOnly);
2709 
2710  const Local<Value> starttime = Date::New(Time().JavaDate());
2711  if (!starttime.IsEmpty())
2712  context->Global()->Set(String::New("__START__"), starttime, ReadOnly);
2713 
2714  //V8::ResumeProfiler();
2715 
2716  TryCatch exception;
2717 
2718  AddFormatToGlobal();
2719 
2720  if (!exception.HasCaught())
2721  {
2722  JsStart(filename);
2723 
2724  Locker::StartPreemption(10);
2725 
2726  if (filename.empty())
2727  ExecuteConsole();
2728  else
2729  {
2730  // We call script->Run because it is the only way to
2731  // catch exceptions.
2732  const Handle<String> source = String::New(("include('"+filename+"');").c_str());
2733  const Handle<String> origin = String::New("main");
2734  const Handle<Script> script = Script::Compile(source, origin);
2735  if (!script.IsEmpty())
2736  {
2737  JsSetState(3);
2738  script->Run();
2739  }
2740  }
2741 
2742  Locker::StopPreemption();
2743 
2744  // Stop all other threads
2745  for (auto it=fThreadIds.begin(); it!=fThreadIds.end(); it++)
2746  V8::TerminateExecution(*it);
2747  fThreadIds.clear();
2748  }
2749 
2750  // Handle an exception
2751  /*const bool rc =*/ HandleException(exception, "main");
2752 
2753  // IsProfilerPaused()
2754  // V8::PauseProfiler();
2755 
2756  // -----
2757  // This is how an exit handler could look like, but there is no way to interrupt it
2758  // -----
2759  // Handle<Object> obj = Handle<Object>::Cast(context->Global()->Get(String::New("dim")));
2760  // if (!obj.IsEmpty())
2761  // {
2762  // Handle<Value> onexit = obj->Get(String::New("onexit"));
2763  // if (!onexit->IsUndefined())
2764  // Handle<Function>::Cast(onexit)->NewInstance(0, NULL); // argc, argv
2765  // // Handle<Object> result = Handle<Function>::Cast(onexit)->NewInstance(0, NULL); // argc, argv
2766  // }
2767 
2768  //context->Exit();
2769 
2770  // The threads are started already and wait to get the lock
2771  // So we have to unlock (manual preemtion) so that they get
2772  // the signal to terminate.
2773  {
2774  const Unlocker unlock;
2775 
2776  for (auto it=fThreads.begin(); it!=fThreads.end(); it++)
2777  it->join();
2778  fThreads.clear();
2779  }
2780 
2781  // Now we can dispose all persistent handles from state callbacks
2782  for (auto it=fStateCallbacks.begin(); it!=fStateCallbacks.end(); it++)
2783  it->second.Dispose();
2784  fStateCallbacks.clear();
2785 
2786  // Now we can dispose the persistent interrupt handler
2787  fInterruptCallback.Dispose();
2788  fInterruptCallback.Clear();
2789 
2790  // Now we can dispose all persistent handles from reverse maps
2791  for (auto it=fReverseMap.begin(); it!=fReverseMap.end(); it++)
2792  it->second.Dispose();
2793  fReverseMap.clear();
2794 
2795 #ifdef HAVE_SQL
2796  // ...and close all database handles
2797  for (auto it=fDatabases.begin(); it!=fDatabases.end(); it++)
2798  delete *it;
2799  fDatabases.clear();
2800 #endif
2801 
2802  fStates.clear();
2803 
2804  context.Dispose();
2805 
2806  JsEnd(filename);
2807 
2808  return true;
2809 }
2810 
2811 void InterpreterV8::JsStop()
2812 {
2813  Locker locker;
2814  V8::TerminateExecution(This->fThreadId);
2815 }
2816 
2817 vector<string> InterpreterV8::JsGetCommandList(const char *, int) const
2818 {
2819  vector<string> rc;
2820 
2821  rc.emplace_back("for (");
2822  rc.emplace_back("while (");
2823  rc.emplace_back("if (");
2824  rc.emplace_back("switch (");
2825  rc.emplace_back("case ");
2826  rc.emplace_back("var ");
2827  rc.emplace_back("function ");
2828  rc.emplace_back("Date(");
2829  rc.emplace_back("new Date(");
2830  rc.emplace_back("'use strict';");
2831  rc.emplace_back("undefined");
2832  rc.emplace_back("null");
2833  rc.emplace_back("delete ");
2834 
2835  rc.emplace_back("dim.log(");
2836  rc.emplace_back("dim.alarm(");
2837  rc.emplace_back("dim.wait(");
2838  rc.emplace_back("dim.send(");
2839  rc.emplace_back("dim.state(");
2840  rc.emplace_back("dim.version");
2841  rc.emplace_back("dim.getStates(");
2842  rc.emplace_back("dim.getDescription(");
2843  rc.emplace_back("dim.getServices(");
2844 
2845  rc.emplace_back("dimctrl.defineState(");
2846  rc.emplace_back("dimctrl.setState(");
2847  rc.emplace_back("dimctrl.getState(");
2848  rc.emplace_back("dimctrl.setInterruptHandler(");
2849  rc.emplace_back("dimctrl.triggerInterrupt(");
2850 
2851  rc.emplace_back("v8.sleep(");
2852  rc.emplace_back("v8.timeout(");
2853  rc.emplace_back("v8.version()");
2854 
2855  rc.emplace_back("console.out(");
2856  rc.emplace_back("console.warn(");
2857 
2858  rc.emplace_back("include(");
2859  rc.emplace_back("exit()");
2860 
2861 #ifdef HAVE_SQL
2862  rc.emplace_back("Database(");
2863  rc.emplace_back("new Database(");
2864 
2865  rc.emplace_back(".table");
2866  rc.emplace_back(".user");
2867  rc.emplace_back(".database");
2868  rc.emplace_back(".port");
2869  rc.emplace_back(".query");
2870 #endif
2871 
2872  rc.emplace_back("Subscription(");
2873  rc.emplace_back("new Subscription(");
2874 
2875  rc.emplace_back("Thread(");
2876  rc.emplace_back("new Thread(");
2877 
2878  rc.emplace_back("File(");
2879  rc.emplace_back("new File(");
2880 
2881  rc.emplace_back("Event(");
2882  rc.emplace_back("new Event(");
2883 
2884  rc.emplace_back("Description(");
2885  rc.emplace_back("new Description(");
2886 
2887 #ifdef HAVE_MAILX
2888  rc.emplace_back("Mail(");
2889  rc.emplace_back("new Mail(");
2890 
2891  rc.emplace_back(".subject");
2892  rc.emplace_back(".receipients");
2893  rc.emplace_back(".attachments");
2894  rc.emplace_back(".bcc");
2895  rc.emplace_back(".cc");
2896  rc.emplace_back(".text");
2897  rc.emplace_back(".send(");
2898 #endif
2899 
2900 #ifdef HAVE_CURL
2901  rc.emplace_back("Curl(");
2902  rc.emplace_back("new Curl(");
2903 
2904  rc.emplace_back(".url");
2905  rc.emplace_back(".user");
2906  rc.emplace_back(".data");
2907 // rc.emplace_back(".send("); -> MAILX
2908 #endif
2909 
2910 #ifdef HAVE_NOVA
2911  rc.emplace_back("Sky(");
2912  rc.emplace_back("new Sky(");
2913 
2914  rc.emplace_back("Sky.dist");
2915  rc.emplace_back("Local(");
2916 
2917  rc.emplace_back("new Local(");
2918  rc.emplace_back("Local.dist");
2919 
2920  rc.emplace_back("Moon(");
2921  rc.emplace_back("new Moon(");
2922  rc.emplace_back("Moon.disk(");
2923  rc.emplace_back("Moon.horizon(");
2924 
2925  rc.emplace_back("Sun.horizon(");
2926 
2927  rc.emplace_back(".zd");
2928  rc.emplace_back(".az");
2929  rc.emplace_back(".ra");
2930  rc.emplace_back(".dec");
2931 
2932  rc.emplace_back(".toLocal(");
2933  rc.emplace_back(".toSky(");
2934  rc.emplace_back(".rise");
2935  rc.emplace_back(".set");
2936  rc.emplace_back(".transit");
2937  rc.emplace_back(".isUp");
2938 
2939  rc.emplace_back("horizon");
2940  rc.emplace_back("civil");
2941  rc.emplace_back("nautical");
2942  rc.emplace_back("astronomical");
2943 #endif
2944 
2945  rc.emplace_back(".server");
2946  rc.emplace_back(".service");
2947  rc.emplace_back(".name");
2948  rc.emplace_back(".isCommand");
2949  rc.emplace_back(".format");
2950  rc.emplace_back(".description");
2951  rc.emplace_back(".unit");
2952  rc.emplace_back(".delim");
2953  rc.emplace_back(".isOpen");
2954 
2955  rc.emplace_back(".qos");
2956  rc.emplace_back(".size");
2957  rc.emplace_back(".counter");
2958  rc.emplace_back(".type");
2959  rc.emplace_back(".obj");
2960  rc.emplace_back(".data");
2961  rc.emplace_back(".comment");
2962  rc.emplace_back(".index");
2963  rc.emplace_back(".time");
2964  rc.emplace_back(".close()");
2965  rc.emplace_back(".onchange");
2966  rc.emplace_back(".get(");
2967 
2968 
2969  rc.emplace_back("__DATE__");
2970  rc.emplace_back("__FILE__");
2971 
2972  return rc;
2973 }
2974 
2975 #endif
2976 
int start(int initState)
Definition: feeserver.c:1740
void JsHandleState(const std::string &, const State &)
static v8::Handle< v8::FunctionTemplate > fTemplateEvent
Definition: InterpreterV8.h:54
static std::string StaticPrompt(const std::string &prompt)
Definition: Readline.cc:1346
uint64_t JavaDate() const
Definition: Time.h:111
A general base-class describing events issues in a state machine.
Definition: EventImp.h:11
const char * GetText() const
Definition: EventImp.h:88
std::string comment
Name (e.g. &#39;Connected&#39;)
Definition: State.h:13
EquPosn GetLunarEquCoords(double jd, double precision=0)
Definition: nova.h:136
int i
Definition: db_dim_client.c:21
const LnLatPosn & ORM()
Definition: nova.h:66
Adds some functionality to boost::posix_time::ptime for our needs.
Definition: Time.h:30
char str[80]
Definition: test_client.c:7
A C++ ostream to an ncurses window supporting attributes and colors.
Definition: WindowLog.h:50
static v8::Handle< v8::FunctionTemplate > fTemplateDescription
Definition: InterpreterV8.h:55
virtual void JsEnd(const std::string &="")
STL namespace.
void AddFormatToGlobal()
void JsHandleEvent(const EventImp &, uint64_t, const std::string &)
std::string GetString() const
Definition: EventImp.cc:194
double GetLunarDisk(double jd)
Definition: nova.h:121
static void StaticPopHistory(const std::string &fname)
Definition: Readline.cc:1322
int index
Definition: State.h:11
double zd
Definition: nova.h:30
Set attribute Underline.
Definition: WindowLog.h:32
double JD() const
Definition: Time.h:87
static void StaticPushHistory(const std::string &fname)
Definition: Readline.cc:1303
int JsHandleInterrupt(const EventImp &)
static v8::Handle< v8::FunctionTemplate > fTemplateLocal
Definition: InterpreterV8.h:52
std::string user
Definition: Database.h:11
int type
static v8::Handle< v8::FunctionTemplate > fTemplateSky
Definition: InterpreterV8.h:53
static void SetScriptDepth(unsigned int d)
Definition: Readline.h:115
std::string db
Definition: Database.h:14
virtual std::string GetFormat() const
Definition: EventImp.h:52
static void JsStop()
std::vector< std::string > JsGetCommandList(const char *, int) const
static Pixmap unlock
Definition: dui_util.c:165
virtual int GetQoS() const
Definition: EventImp.h:58
bool JsRun(const std::string &, const std::map< std::string, std::string > &=std::map< std::string, std::string >())
void Thread(MainImp *io_service, bool dummy, int &rc)
Definition: Main.h:79
Warning because the service this data corrsponds to might have been last updated longer ago than Local time
Definition: smartfact.txt:92
std::map< std::string, std::string > Split(std::string &, bool=false)
Definition: tools.cc:230
double end
uint64_t GetJavaDate() const
Definition: EventImp.cc:303
int buffer[BUFFSIZE]
Definition: db_dim_client.c:14
Warning states
Definition: smartfact.txt:92
ln_lnlat_posn LnLatPosn
Definition: nova.h:12
virtual void JsLoad(const std::string &="")
float data[4 *1440]
HrzPosn GetHrzFromEqu(const EquPosn &equ, const LnLatPosn &obs, double jd)
Definition: nova.h:75
int counter
Definition: db_dim_client.c:19
v8::Handle< v8::Value > HandleInterruptImp(std::string, uint64_t)
double az
Definition: nova.h:31
#define DIM_VERSION_NUMBER
Definition: dim.h:16
TT t
Definition: test_client.c:26
Error()
Definition: HeadersFTM.h:197
EquPosn GetEquFromHrz(const HrzPosn &hrz, const LnLatPosn &obs, double jd)
Definition: nova.h:86
ln_rst_time RstTime
Definition: nova.h:13
std::string name
Index (e.g. 1)
Definition: State.h:12
std::string Trim(const std::string &str)
Definition: tools.cc:68
Time time
Description (e.g. &#39;Connection to hardware established.&#39;)
Definition: State.h:14
virtual bool IsEmpty() const
Definition: EventImp.h:59
Reset all attributes.
Definition: WindowLog.h:29
char message[1024]
Definition: db_dim_client.c:16
static InterpreterV8 * This
Definition: InterpreterV8.h:27
std::string server
Definition: Database.h:13
virtual size_t GetSize() const
Definition: EventImp.h:55