v8embed.cpp 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems®.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ############################################################################## */
  13. #include "platform.h"
  14. #include "v8.h"
  15. #include "jexcept.hpp"
  16. #include "jthread.hpp"
  17. #include "hqlplugins.hpp"
  18. #include "deftype.hpp"
  19. #include "eclrtl.hpp"
  20. #include "eclrtl_imp.hpp"
  21. #include "rtlds_imp.hpp"
  22. #include "rtlfield.hpp"
  23. #include "nbcd.hpp"
  24. #include "roxiemem.hpp"
  25. #include <vector>
  26. static const char * compatibleVersions[] = {
  27. "V8 JavaScript Embed Helper 1.0.0",
  28. NULL };
  29. static const char *version = "V8 JavaScript Embed Helper 1.0.0";
  30. extern "C" DECL_EXPORT bool getECLPluginDefinition(ECLPluginDefinitionBlock *pb)
  31. {
  32. if (pb->size == sizeof(ECLPluginDefinitionBlockEx))
  33. {
  34. ECLPluginDefinitionBlockEx * pbx = (ECLPluginDefinitionBlockEx *) pb;
  35. pbx->compatibleVersions = compatibleVersions;
  36. }
  37. else if (pb->size != sizeof(ECLPluginDefinitionBlock))
  38. return false;
  39. pb->magicVersion = PLUGIN_VERSION;
  40. pb->version = version;
  41. pb->moduleName = "javascript";
  42. pb->ECL = NULL;
  43. pb->flags = PLUGIN_MULTIPLE_VERSIONS;
  44. pb->description = "V8 JavaScript Embed Helper";
  45. return true;
  46. }
  47. __declspec(noreturn) static void UNSUPPORTED(const char *feature) __attribute__((noreturn));
  48. static void UNSUPPORTED(const char *feature)
  49. {
  50. throw MakeStringException(-1, "UNSUPPORTED feature: %s not supported in v8embed plugin", feature);
  51. }
  52. __declspec(noreturn) static void typeError(const char *expected, const RtlFieldInfo *field) __attribute__((noreturn));
  53. static void typeError(const char *expected, const RtlFieldInfo *field)
  54. {
  55. VStringBuffer msg("v8embed: type mismatch - %s expected", expected);
  56. if (field)
  57. msg.appendf(" for field %s", field->name);
  58. rtlFail(0, msg.str());
  59. }
  60. namespace javascriptLanguageHelper {
  61. // A JSRowBuilder object is used to construct an ECL row from a javascript object
  62. class JSRowBuilder : public CInterfaceOf<IFieldSource>
  63. {
  64. public:
  65. JSRowBuilder(v8::Local<v8::Object> _row, const RtlFieldInfo *_outerRow)
  66. : row(_row), outerRow(_outerRow), named(true), idx(0)
  67. {
  68. }
  69. virtual bool getBooleanResult(const RtlFieldInfo *field)
  70. {
  71. return nextField(field)->BooleanValue();
  72. }
  73. virtual void getDataResult(const RtlFieldInfo *field, size32_t &len, void * &result)
  74. {
  75. UNIMPLEMENTED;
  76. }
  77. virtual double getRealResult(const RtlFieldInfo *field)
  78. {
  79. return v8::Number::Cast(*nextField(field))->Value();
  80. }
  81. virtual __int64 getSignedResult(const RtlFieldInfo *field)
  82. {
  83. return v8::Integer::Cast(*nextField(field))->Value();
  84. }
  85. virtual unsigned __int64 getUnsignedResult(const RtlFieldInfo *field)
  86. {
  87. return v8::Integer::Cast(*nextField(field))->Value();
  88. }
  89. virtual void getStringResult(const RtlFieldInfo *field, size32_t &chars, char * &result)
  90. {
  91. v8::String::AsciiValue ascii(nextField(field));
  92. rtlStrToStrX(chars, result, ascii.length(), *ascii);
  93. }
  94. virtual void getUTF8Result(const RtlFieldInfo *field, size32_t &chars, char * &result)
  95. {
  96. v8::Local<v8::Value> value = nextField(field);
  97. if (!value->IsString())
  98. typeError("string", field);
  99. v8::String::Utf8Value utf8(value);
  100. unsigned numchars = rtlUtf8Length(utf8.length(), *utf8);
  101. rtlUtf8ToUtf8X(chars, result, numchars, *utf8);
  102. }
  103. virtual void getUnicodeResult(const RtlFieldInfo *field, size32_t &chars, UChar * &result)
  104. {
  105. v8::Local<v8::Value> value = nextField(field);
  106. if (!value->IsString())
  107. typeError("string", field);
  108. v8::String::Utf8Value utf8(value);
  109. unsigned numchars = rtlUtf8Length(utf8.length(), *utf8);
  110. rtlUtf8ToUnicodeX(chars, result, numchars, *utf8);
  111. }
  112. virtual void getDecimalResult(const RtlFieldInfo *field, Decimal &value)
  113. {
  114. value.setReal(getRealResult(field));
  115. }
  116. virtual void processBeginSet(const RtlFieldInfo * field, bool &isAll)
  117. {
  118. isAll = false;
  119. v8::Local<v8::Value> value = nextField(field);
  120. if (!value->IsArray())
  121. typeError("array", field);
  122. push(false);
  123. row = v8::Array::Cast(*value);
  124. }
  125. virtual bool processNextSet(const RtlFieldInfo * field)
  126. {
  127. assertex(!named);
  128. return row->Has(idx);
  129. }
  130. virtual void processBeginDataset(const RtlFieldInfo * field)
  131. {
  132. v8::Local<v8::Value> value = nextField(field);
  133. if (!value->IsArray())
  134. typeError("array", field);
  135. push(false);
  136. row = v8::Array::Cast(*value);
  137. }
  138. virtual void processBeginRow(const RtlFieldInfo * field)
  139. {
  140. if (field != outerRow)
  141. {
  142. v8::Local<v8::Value> value = nextField(field);
  143. if (!value->IsObject())
  144. typeError("object", field);
  145. push(true);
  146. row = v8::Object::Cast(*value);
  147. }
  148. }
  149. virtual bool processNextRow(const RtlFieldInfo * field)
  150. {
  151. assertex(!named);
  152. return row->Has(idx);
  153. }
  154. virtual void processEndSet(const RtlFieldInfo * field)
  155. {
  156. pop();
  157. }
  158. virtual void processEndDataset(const RtlFieldInfo * field)
  159. {
  160. pop();
  161. }
  162. virtual void processEndRow(const RtlFieldInfo * field)
  163. {
  164. if (field != outerRow)
  165. pop();
  166. }
  167. protected:
  168. void pop()
  169. {
  170. named = namedStack.popGet();
  171. idx = idxStack.popGet();
  172. row = stack.back();
  173. stack.pop_back();
  174. }
  175. void push(bool _named)
  176. {
  177. namedStack.append(named);
  178. idxStack.append(idx);
  179. stack.push_back(row);
  180. named = _named;
  181. idx = 0;
  182. }
  183. v8::Local<v8::Value> nextField(const RtlFieldInfo * field)
  184. {
  185. v8::Local<v8::Value> v;
  186. if (named)
  187. {
  188. v8::Local<v8::String> name = v8::String::New(field->name);
  189. if (!row->Has(name))
  190. {
  191. VStringBuffer msg("v8embed: No value for field %s", field->name);
  192. rtlFail(0, msg.str());
  193. }
  194. v = row->Get(name);
  195. }
  196. else
  197. {
  198. assertex(row->Has(idx)); // Logic in processNextXXX should have ensured
  199. v = row->Get(idx++);
  200. }
  201. return v;
  202. }
  203. v8::Local<v8::Object> row; // current row, set, or dataset...
  204. std::vector< v8::Local<v8::Object> > stack;
  205. IntArray idxStack;
  206. BoolArray namedStack;
  207. const RtlFieldInfo *outerRow;
  208. int idx;
  209. bool named;
  210. };
  211. // A JSObjectBuilder object is used to construct a JS Object from an ECL row
  212. class JSObjectBuilder : public CInterfaceOf<IFieldProcessor>
  213. {
  214. public:
  215. JSObjectBuilder(const RtlFieldInfo *_outerRow)
  216. : outerRow(_outerRow), idx(0), inDataset(false)
  217. {
  218. }
  219. virtual void processString(unsigned len, const char *value, const RtlFieldInfo * field)
  220. {
  221. size32_t utfCharCount;
  222. rtlDataAttr utfText;
  223. rtlStrToUtf8X(utfCharCount, utfText.refstr(), len, value);
  224. processUtf8(utfCharCount, utfText.getstr(), field);
  225. }
  226. virtual void processBool(bool value, const RtlFieldInfo * field)
  227. {
  228. addProp(field, v8::Boolean::New(value));
  229. }
  230. virtual void processData(unsigned len, const void *value, const RtlFieldInfo * field)
  231. {
  232. v8::Local<v8::Array> array = v8::Array::New(len);
  233. const byte *vval = (const byte *) value;
  234. for (int i = 0; i < len; i++)
  235. {
  236. array->Set(v8::Number::New(i), v8::Integer::New(vval[i])); // feels horridly inefficient, but seems to be the expected approach
  237. }
  238. addProp(field, array);
  239. }
  240. virtual void processInt(__int64 value, const RtlFieldInfo * field)
  241. {
  242. addProp(field, v8::Integer::New(value));
  243. }
  244. virtual void processUInt(unsigned __int64 value, const RtlFieldInfo * field)
  245. {
  246. addProp(field, v8::Integer::NewFromUnsigned(value));
  247. }
  248. virtual void processReal(double value, const RtlFieldInfo * field)
  249. {
  250. addProp(field, v8::Number::New(value));
  251. }
  252. virtual void processDecimal(const void *value, unsigned digits, unsigned precision, const RtlFieldInfo * field)
  253. {
  254. Decimal val;
  255. val.setDecimal(digits, precision, value);
  256. addProp(field, v8::Number::New(val.getReal()));
  257. }
  258. virtual void processUDecimal(const void *value, unsigned digits, unsigned precision, const RtlFieldInfo * field)
  259. {
  260. Decimal val;
  261. val.setUDecimal(digits, precision, value);
  262. addProp(field, v8::Number::New(val.getReal()));
  263. }
  264. virtual void processUnicode(unsigned len, const UChar *value, const RtlFieldInfo * field)
  265. {
  266. addProp(field, v8::String::New(value, len));
  267. }
  268. virtual void processQString(unsigned len, const char *value, const RtlFieldInfo * field)
  269. {
  270. size32_t charCount;
  271. rtlDataAttr text;
  272. rtlQStrToStrX(charCount, text.refstr(), len, value);
  273. processString(charCount, text.getstr(), field);
  274. }
  275. virtual void processUtf8(unsigned len, const char *value, const RtlFieldInfo * field)
  276. {
  277. addProp(field, v8::String::New(value, rtlUtf8Size(len, value)));
  278. }
  279. virtual bool processBeginSet(const RtlFieldInfo * field, unsigned numElements, bool isAll, const byte *data)
  280. {
  281. push();
  282. inDataset = true;
  283. if (isAll)
  284. rtlFail(0, "v8embed: ALL sets are not supported");
  285. obj = v8::Array::New();
  286. return true;
  287. }
  288. virtual bool processBeginDataset(const RtlFieldInfo * field, unsigned numRows)
  289. {
  290. push();
  291. inDataset = true;
  292. obj = v8::Array::New();
  293. return true;
  294. }
  295. virtual bool processBeginRow(const RtlFieldInfo * field)
  296. {
  297. if (field != outerRow)
  298. push();
  299. obj = v8::Object::New();
  300. return true;
  301. }
  302. virtual void processEndSet(const RtlFieldInfo * field)
  303. {
  304. pop(field);
  305. }
  306. virtual void processEndDataset(const RtlFieldInfo * field)
  307. {
  308. pop(field);
  309. }
  310. virtual void processEndRow(const RtlFieldInfo * field)
  311. {
  312. if (field != outerRow)
  313. {
  314. pop(field);
  315. }
  316. }
  317. v8::Local<v8::Object> getObject()
  318. {
  319. return obj;
  320. }
  321. protected:
  322. void push()
  323. {
  324. idxStack.append(idx);
  325. stack.push_back(obj);
  326. dsStack.append(inDataset);
  327. inDataset = false;
  328. idx = 0;
  329. obj.Clear();
  330. }
  331. void pop(const RtlFieldInfo * field)
  332. {
  333. inDataset = dsStack.popGet();
  334. idx = idxStack.popGet();
  335. v8::Local<v8::Object> row = obj;
  336. obj = stack.back();
  337. stack.pop_back();
  338. addProp(field, row);
  339. }
  340. void addProp(const RtlFieldInfo * field, v8::Handle<v8::Value> value)
  341. {
  342. assertex(!obj.IsEmpty());
  343. if (inDataset)
  344. obj->Set(idx++, value);
  345. else
  346. obj->Set(v8::String::New(field->name), value);
  347. }
  348. v8::Local<v8::Object> obj;
  349. std::vector< v8::Local<v8::Object> > stack;
  350. const RtlFieldInfo *outerRow;
  351. BoolArray dsStack;
  352. IntArray idxStack;
  353. int idx;
  354. bool inDataset;
  355. };
  356. static size32_t getRowResult(v8::Handle<v8::Value> result, ARowBuilder &builder)
  357. {
  358. if (result.IsEmpty() || !result->IsObject())
  359. typeError("object", NULL);
  360. v8::HandleScope scope; // Probably not needed
  361. v8::Local<v8::Object> row = v8::Object::Cast(*result);
  362. const RtlTypeInfo *typeInfo = builder.queryAllocator()->queryOutputMeta()->queryTypeInfo();
  363. assertex(typeInfo);
  364. RtlFieldStrInfo dummyField("<row>", NULL, typeInfo);
  365. JSRowBuilder jsRowBuilder(row, &dummyField);
  366. return typeInfo->build(builder, 0, &dummyField, jsRowBuilder);
  367. }
  368. // An embedded javascript function that returns a dataset will return a JSRowStream object that can be
  369. // interrogated to return each row of the result in turn
  370. class JSRowStream : public CInterfaceOf<IRowStream>
  371. {
  372. public:
  373. JSRowStream(v8::Handle<v8::Value> _result, IEngineRowAllocator *_resultAllocator)
  374. : rowIdx(0), resultAllocator(_resultAllocator)
  375. {
  376. if (_result.IsEmpty() || !_result->IsArray())
  377. typeError("array", NULL);
  378. result = v8::Persistent<v8::Array>(v8::Array::Cast(*_result));
  379. }
  380. ~JSRowStream()
  381. {
  382. result.Dispose();
  383. }
  384. virtual const void *nextRow()
  385. {
  386. if (result.IsEmpty())
  387. return NULL;
  388. v8::HandleScope scope;
  389. if (!result->Has(rowIdx))
  390. {
  391. stop();
  392. return NULL;
  393. }
  394. v8::Local<v8::Value> row = result->Get(rowIdx);
  395. rowIdx++;
  396. if (!row->IsObject())
  397. typeError("object", NULL);
  398. v8::Local<v8::Object> rowObject = v8::Object::Cast(*row);
  399. RtlDynamicRowBuilder rowBuilder(resultAllocator);
  400. size32_t len = javascriptLanguageHelper::getRowResult(rowObject, rowBuilder);
  401. return rowBuilder.finalizeRowClear(len);
  402. }
  403. virtual void stop()
  404. {
  405. resultAllocator.clear();
  406. result.Clear();
  407. }
  408. protected:
  409. Linked<IEngineRowAllocator> resultAllocator;
  410. unsigned rowIdx;
  411. v8::Persistent<v8::Array> result;
  412. };
  413. class V8JavascriptEmbedFunctionContext : public CInterfaceOf<IEmbedFunctionContext>
  414. {
  415. public:
  416. V8JavascriptEmbedFunctionContext()
  417. {
  418. isolate = v8::Isolate::New();
  419. isolate->Enter();
  420. context = v8::Context::New();
  421. context->Enter();
  422. }
  423. ~V8JavascriptEmbedFunctionContext()
  424. {
  425. script.Dispose();
  426. result.Dispose();
  427. context->Exit();
  428. context.Dispose();
  429. isolate->Exit();
  430. isolate->Dispose();
  431. }
  432. void setActivityContext(const IThorActivityContext *_activityCtx)
  433. {
  434. activityCtx = _activityCtx;
  435. }
  436. virtual IInterface *bindParamWriter(IInterface *esdl, const char *esdlservice, const char *esdltype, const char *name)
  437. {
  438. return NULL;
  439. }
  440. virtual void paramWriterCommit(IInterface *writer)
  441. {
  442. }
  443. virtual void writeResult(IInterface *esdl, const char *esdlservice, const char *esdltype, IInterface *writer)
  444. {
  445. }
  446. virtual void bindBooleanParam(const char *name, bool val)
  447. {
  448. v8::HandleScope handle_scope;
  449. context->Global()->Set(v8::String::New(name), v8::Boolean::New(val));
  450. }
  451. virtual void bindDataParam(const char *name, size32_t len, const void *val)
  452. {
  453. v8::HandleScope handle_scope;
  454. v8::Local<v8::Array> array = v8::Array::New(len);
  455. const byte *vval = (const byte *) val;
  456. for (int i = 0; i < len; i++)
  457. {
  458. array->Set(v8::Number::New(i), v8::Integer::New(vval[i])); // feels horridly inefficient, but seems to be the expected approach
  459. }
  460. context->Global()->Set(v8::String::New(name), array);
  461. }
  462. virtual void bindFloatParam(const char *name, float val)
  463. {
  464. v8::HandleScope handle_scope;
  465. context->Global()->Set(v8::String::New(name), v8::Number::New(val));
  466. }
  467. virtual void bindRealParam(const char *name, double val)
  468. {
  469. v8::HandleScope handle_scope;
  470. context->Global()->Set(v8::String::New(name), v8::Number::New(val));
  471. }
  472. virtual void bindSignedSizeParam(const char *name, int size, __int64 val)
  473. {
  474. bindSignedParam(name, val);
  475. }
  476. virtual void bindSignedParam(const char *name, __int64 val)
  477. {
  478. // MORE - might need to check does not overflow 32 bits? Or store as a real?
  479. v8::HandleScope handle_scope;
  480. context->Global()->Set(v8::String::New(name), v8::Integer::New(val));
  481. }
  482. virtual void bindUnsignedSizeParam(const char *name, int size, unsigned __int64 val)
  483. {
  484. bindUnsignedParam(name, val);
  485. }
  486. virtual void bindUnsignedParam(const char *name, unsigned __int64 val)
  487. {
  488. // MORE - might need to check does not overflow 32 bits
  489. v8::HandleScope handle_scope;
  490. context->Global()->Set(v8::String::New(name), v8::Integer::NewFromUnsigned(val));
  491. }
  492. virtual void bindStringParam(const char *name, size32_t len, const char *val)
  493. {
  494. size32_t utfCharCount;
  495. rtlDataAttr utfText;
  496. rtlStrToUtf8X(utfCharCount, utfText.refstr(), len, val);
  497. bindUTF8Param(name, utfCharCount, utfText.getstr());
  498. }
  499. virtual void bindVStringParam(const char *name, const char *val)
  500. {
  501. bindStringParam(name, strlen(val), val);
  502. }
  503. virtual void bindUTF8Param(const char *name, size32_t chars, const char *val)
  504. {
  505. v8::HandleScope handle_scope;
  506. context->Global()->Set(v8::String::New(name), v8::String::New(val, rtlUtf8Size(chars, val)));
  507. }
  508. virtual void bindUnicodeParam(const char *name, size32_t chars, const UChar *val)
  509. {
  510. v8::HandleScope handle_scope;
  511. context->Global()->Set(v8::String::New(name), v8::String::New(val, chars));
  512. }
  513. virtual void bindSetParam(const char *name, int elemType, size32_t elemSize, bool isAll, size32_t totalBytes, const void *setData)
  514. {
  515. if (isAll)
  516. rtlFail(0, "v8embed: Cannot pass ALL");
  517. v8::HandleScope handle_scope;
  518. type_t typecode = (type_t) elemType;
  519. const byte *inData = (const byte *) setData;
  520. const byte *endData = inData + totalBytes;
  521. int numElems;
  522. if (elemSize == UNKNOWN_LENGTH)
  523. {
  524. numElems = 0;
  525. // Will need 2 passes to work out how many elements there are in the set :(
  526. while (inData < endData)
  527. {
  528. int thisSize;
  529. switch (elemType)
  530. {
  531. case type_varstring:
  532. thisSize = strlen((const char *) inData) + 1;
  533. break;
  534. case type_string:
  535. thisSize = * (size32_t *) inData + sizeof(size32_t);
  536. break;
  537. case type_unicode:
  538. thisSize = (* (size32_t *) inData) * sizeof(UChar) + sizeof(size32_t);
  539. break;
  540. case type_utf8:
  541. thisSize = rtlUtf8Size(* (size32_t *) inData, inData + sizeof(size32_t)) + sizeof(size32_t);
  542. break;
  543. default:
  544. rtlFail(0, "v8embed: Unsupported parameter type");
  545. break;
  546. }
  547. inData += thisSize;
  548. numElems++;
  549. }
  550. inData = (const byte *) setData;
  551. }
  552. else
  553. numElems = totalBytes / elemSize;
  554. v8::Local<v8::Array> array = v8::Array::New(numElems);
  555. v8::Handle<v8::Value> thisItem;
  556. size32_t thisSize = elemSize;
  557. for (int idx = 0; idx < numElems; idx++)
  558. {
  559. switch (typecode)
  560. {
  561. case type_int:
  562. thisItem = v8::Integer::New(rtlReadInt(inData, elemSize));
  563. break;
  564. case type_unsigned:
  565. thisItem = v8::Integer::NewFromUnsigned(rtlReadUInt(inData, elemSize));
  566. break;
  567. case type_varstring:
  568. {
  569. size32_t numChars = strlen((const char *) inData);
  570. size32_t utfCharCount;
  571. rtlDataAttr utfText;
  572. rtlStrToUtf8X(utfCharCount, utfText.refstr(), numChars, (const char *) inData);
  573. thisItem = v8::String::New(utfText.getstr(), rtlUtf8Size(utfCharCount, utfText.getstr()));
  574. if (elemSize == UNKNOWN_LENGTH)
  575. thisSize = numChars + 1;
  576. break;
  577. }
  578. case type_string:
  579. {
  580. if (elemSize == UNKNOWN_LENGTH)
  581. {
  582. thisSize = * (size32_t *) inData;
  583. inData += sizeof(size32_t);
  584. }
  585. size32_t utfCharCount;
  586. rtlDataAttr utfText;
  587. rtlStrToUtf8X(utfCharCount, utfText.refstr(), thisSize, (const char *) inData);
  588. thisItem = v8::String::New(utfText.getstr(), rtlUtf8Size(utfCharCount, utfText.getstr()));
  589. break;
  590. }
  591. case type_real:
  592. if (elemSize == sizeof(double))
  593. thisItem = v8::Number::New(* (double *) inData);
  594. else
  595. thisItem = v8::Number::New(* (float *) inData);
  596. break;
  597. case type_boolean:
  598. assertex(elemSize == sizeof(bool));
  599. thisItem = v8::Boolean::New(* (bool *) inData);
  600. break;
  601. case type_unicode:
  602. {
  603. if (elemSize == UNKNOWN_LENGTH)
  604. {
  605. thisSize = (* (size32_t *) inData) * sizeof(UChar); // NOTE - it's in chars...
  606. inData += sizeof(size32_t);
  607. }
  608. thisItem = v8::String::New((const UChar *) inData, thisSize/sizeof(UChar));
  609. break;
  610. }
  611. case type_utf8:
  612. {
  613. assertex (elemSize == UNKNOWN_LENGTH);
  614. size32_t numChars = * (size32_t *) inData;
  615. inData += sizeof(size32_t);
  616. thisSize = rtlUtf8Size(numChars, inData);
  617. thisItem = v8::String::New((const char *) inData, thisSize);
  618. break;
  619. }
  620. default:
  621. rtlFail(0, "v8embed: Unsupported parameter type");
  622. break;
  623. }
  624. inData += thisSize;
  625. array->Set(v8::Number::New(idx), thisItem);
  626. }
  627. context->Global()->Set(v8::String::New(name), array);
  628. }
  629. virtual void bindRowParam(const char *name, IOutputMetaData & metaVal, const byte *val) override
  630. {
  631. v8::HandleScope handle_scope;
  632. const RtlTypeInfo *typeInfo = metaVal.queryTypeInfo();
  633. assertex(typeInfo);
  634. RtlFieldStrInfo dummyField("<row>", NULL, typeInfo);
  635. JSObjectBuilder objBuilder(&dummyField);
  636. typeInfo->process(val, val, &dummyField, objBuilder); // Creates a JS object from the incoming ECL row
  637. context->Global()->Set(v8::String::New(name), objBuilder.getObject());
  638. }
  639. virtual void bindDatasetParam(const char *name, IOutputMetaData & metaVal, IRowStream * val)
  640. {
  641. v8::HandleScope handle_scope;
  642. v8::Local<v8::Array> array = v8::Array::New();
  643. const RtlTypeInfo *typeInfo = metaVal.queryTypeInfo();
  644. assertex(typeInfo);
  645. RtlFieldStrInfo dummyField("<row>", NULL, typeInfo);
  646. int idx = 0;
  647. for (;;)
  648. {
  649. roxiemem::OwnedConstRoxieRow row = val->ungroupedNextRow();
  650. if (!row)
  651. break;
  652. JSObjectBuilder objBuilder(&dummyField);
  653. const byte *brow = (const byte *) row.get();
  654. typeInfo->process(brow, brow, &dummyField, objBuilder); // Creates a JS object from the incoming ECL row
  655. array->Set(idx++, objBuilder.getObject());
  656. }
  657. context->Global()->Set(v8::String::New(name), array);
  658. }
  659. virtual bool getBooleanResult()
  660. {
  661. assertex (!result.IsEmpty());
  662. return result->BooleanValue();
  663. }
  664. virtual void getDataResult(size32_t &__len, void * &__result)
  665. {
  666. assertex (!result.IsEmpty() && result->IsArray());
  667. v8::HandleScope handle_scope;
  668. v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(result);
  669. __len = array->Length();
  670. __result = rtlMalloc(__len);
  671. byte *bresult = (byte *) __result;
  672. for (size32_t i = 0; i < __len; i++)
  673. {
  674. bresult[i] = v8::Integer::Cast(*array->Get(i))->Value(); // feels horridly inefficient, but seems to be the expected approach
  675. }
  676. }
  677. virtual double getRealResult()
  678. {
  679. assertex (!result.IsEmpty());
  680. v8::HandleScope handle_scope;
  681. return v8::Number::Cast(*result)->Value();
  682. }
  683. virtual __int64 getSignedResult()
  684. {
  685. assertex (!result.IsEmpty());
  686. v8::HandleScope handle_scope;
  687. return v8::Integer::Cast(*result)->Value();
  688. }
  689. virtual unsigned __int64 getUnsignedResult()
  690. {
  691. assertex (!result.IsEmpty());
  692. v8::HandleScope handle_scope;
  693. return v8::Integer::Cast(*result)->Value();
  694. }
  695. virtual void getStringResult(size32_t &__chars, char * &__result)
  696. {
  697. assertex (!result.IsEmpty() && result->IsString());
  698. v8::HandleScope handle_scope;
  699. v8::String::AsciiValue ascii(result);
  700. rtlStrToStrX(__chars, __result, ascii.length(), *ascii);
  701. }
  702. virtual void getUTF8Result(size32_t &__chars, char * &__result)
  703. {
  704. assertex (!result.IsEmpty() && result->IsString());
  705. v8::HandleScope handle_scope;
  706. v8::String::Utf8Value utf8(result);
  707. unsigned numchars = rtlUtf8Length(utf8.length(), *utf8);
  708. rtlUtf8ToUtf8X(__chars, __result, numchars, *utf8);
  709. }
  710. virtual void getUnicodeResult(size32_t &__chars, UChar * &__result)
  711. {
  712. assertex (!result.IsEmpty() && result->IsString());
  713. v8::HandleScope handle_scope;
  714. v8::String::Utf8Value utf8(result);
  715. unsigned numchars = rtlUtf8Length(utf8.length(), *utf8);
  716. rtlUtf8ToUnicodeX(__chars, __result, numchars, *utf8);
  717. }
  718. virtual void getSetResult(bool & __isAllResult, size32_t & __resultBytes, void * & __result, int elemType, size32_t elemSize)
  719. {
  720. assertex (!result.IsEmpty());
  721. if (!result->IsArray())
  722. rtlFail(0, "v8embed: type mismatch - return value was not an array");
  723. v8::HandleScope handle_scope;
  724. v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(result);
  725. size_t numResults = array->Length();
  726. rtlRowBuilder out;
  727. byte *outData = NULL;
  728. size32_t outBytes = 0;
  729. if (elemSize != UNKNOWN_LENGTH)
  730. {
  731. out.ensureAvailable(numResults * elemSize); // MORE - check for overflow?
  732. outData = out.getbytes();
  733. }
  734. for (int i = 0; i < numResults; i++)
  735. {
  736. v8::Local<v8::Value> elem = array->Get(i);
  737. if (elem.IsEmpty())
  738. rtlFail(0, "v8embed: type mismatch - empty value in returned array");
  739. switch ((type_t) elemType)
  740. {
  741. case type_int:
  742. rtlWriteInt(outData, v8::Integer::Cast(*elem)->Value(), elemSize);
  743. break;
  744. case type_unsigned:
  745. rtlWriteInt(outData, v8::Integer::Cast(*elem)->Value(), elemSize);
  746. break;
  747. case type_real:
  748. if (elemSize == sizeof(double))
  749. * (double *) outData = (double) v8::Number::Cast(*elem)->Value();
  750. else
  751. {
  752. assertex(elemSize == sizeof(float));
  753. * (float *) outData = (float) v8::Number::Cast(*elem)->Value();
  754. }
  755. break;
  756. case type_boolean:
  757. assertex(elemSize == sizeof(bool));
  758. * (bool *) outData = elem->BooleanValue();
  759. break;
  760. case type_string:
  761. case type_varstring:
  762. {
  763. if (!elem->IsString())
  764. rtlFail(0, "v8embed: type mismatch - return value in list was not a STRING");
  765. v8::String::AsciiValue ascii(elem);
  766. const char * text = *ascii;
  767. size_t lenBytes = ascii.length();
  768. if (elemSize == UNKNOWN_LENGTH)
  769. {
  770. if (elemType == type_string)
  771. {
  772. out.ensureAvailable(outBytes + lenBytes + sizeof(size32_t));
  773. outData = out.getbytes() + outBytes;
  774. * (size32_t *) outData = lenBytes;
  775. rtlStrToStr(lenBytes, outData+sizeof(size32_t), lenBytes, text);
  776. outBytes += lenBytes + sizeof(size32_t);
  777. }
  778. else
  779. {
  780. out.ensureAvailable(outBytes + lenBytes + 1);
  781. outData = out.getbytes() + outBytes;
  782. rtlStrToVStr(0, outData, lenBytes, text);
  783. outBytes += lenBytes + 1;
  784. }
  785. }
  786. else
  787. {
  788. if (elemType == type_string)
  789. rtlStrToStr(elemSize, outData, lenBytes, text);
  790. else
  791. rtlStrToVStr(elemSize, outData, lenBytes, text); // Fixed size null terminated strings... weird.
  792. }
  793. break;
  794. }
  795. case type_unicode:
  796. case type_utf8:
  797. {
  798. if (!elem->IsString())
  799. rtlFail(0, "v8embed: type mismatch - return value in list was not a STRING");
  800. v8::String::Utf8Value utf8(elem);
  801. size_t lenBytes = utf8.length();
  802. const char * text = *utf8;
  803. size32_t numchars = rtlUtf8Length(lenBytes, text);
  804. if (elemType == type_utf8)
  805. {
  806. assertex (elemSize == UNKNOWN_LENGTH);
  807. out.ensureAvailable(outBytes + lenBytes + sizeof(size32_t));
  808. outData = out.getbytes() + outBytes;
  809. * (size32_t *) outData = numchars;
  810. rtlStrToStr(lenBytes, outData+sizeof(size32_t), lenBytes, text);
  811. outBytes += lenBytes + sizeof(size32_t);
  812. }
  813. else
  814. {
  815. if (elemSize == UNKNOWN_LENGTH)
  816. {
  817. out.ensureAvailable(outBytes + numchars*sizeof(UChar) + sizeof(size32_t));
  818. outData = out.getbytes() + outBytes;
  819. // You can't assume that number of chars in utf8 matches number in unicode16 ...
  820. size32_t numchars16;
  821. rtlDataAttr unicode16;
  822. rtlUtf8ToUnicodeX(numchars16, unicode16.refustr(), numchars, text);
  823. * (size32_t *) outData = numchars16;
  824. rtlUnicodeToUnicode(numchars16, (UChar *) (outData+sizeof(size32_t)), numchars16, unicode16.getustr());
  825. outBytes += numchars16*sizeof(UChar) + sizeof(size32_t);
  826. }
  827. else
  828. rtlUtf8ToUnicode(elemSize / sizeof(UChar), (UChar *) outData, numchars, text);
  829. }
  830. break;
  831. }
  832. default:
  833. rtlFail(0, "v8embed: type mismatch - unsupported return type");
  834. }
  835. if (elemSize != UNKNOWN_LENGTH)
  836. {
  837. outData += elemSize;
  838. outBytes += elemSize;
  839. }
  840. }
  841. __isAllResult = false;
  842. __resultBytes = outBytes;
  843. __result = out.detachdata();
  844. }
  845. virtual IRowStream *getDatasetResult(IEngineRowAllocator * _resultAllocator)
  846. {
  847. return new JSRowStream(result, _resultAllocator);
  848. }
  849. virtual byte * getRowResult(IEngineRowAllocator * _resultAllocator)
  850. {
  851. RtlDynamicRowBuilder rowBuilder(_resultAllocator);
  852. size32_t len = javascriptLanguageHelper::getRowResult(result, rowBuilder);
  853. return (byte *) rowBuilder.finalizeRowClear(len);
  854. }
  855. virtual size32_t getTransformResult(ARowBuilder & builder)
  856. {
  857. return javascriptLanguageHelper::getRowResult(result, builder);
  858. }
  859. virtual void compileEmbeddedScript(size32_t lenChars, const char *utf)
  860. {
  861. v8::HandleScope handle_scope;
  862. v8::Handle<v8::String> source = v8::String::New(utf, rtlUtf8Size(lenChars, utf));
  863. v8::Handle<v8::Script> lscript = v8::Script::Compile(source);
  864. script = v8::Persistent<v8::Script>::New(lscript);
  865. }
  866. virtual void loadCompiledScript(size32_t chars, const void *_script) override
  867. {
  868. throwUnexpected();
  869. }
  870. virtual void enter() override {}
  871. virtual void exit() override {}
  872. virtual void importFunction(size32_t lenChars, const char *utf)
  873. {
  874. UNIMPLEMENTED; // Not sure if meaningful for js
  875. }
  876. virtual void callFunction()
  877. {
  878. assertex (!script.IsEmpty());
  879. v8::HandleScope handle_scope;
  880. v8::TryCatch tryCatch;
  881. if (activityCtx)
  882. {
  883. v8::Handle<v8::Object> jsActivityCtx = v8::Object::New();
  884. jsActivityCtx->Set(v8::String::New("isLocal"), v8::Boolean::New(activityCtx->isLocal()));
  885. jsActivityCtx->Set(v8::String::New("numSlaves"), v8::Integer::NewFromUnsigned(activityCtx->numSlaves()));
  886. jsActivityCtx->Set(v8::String::New("numStrands"), v8::Integer::NewFromUnsigned(activityCtx->numStrands()));
  887. jsActivityCtx->Set(v8::String::New("slave"), v8::Integer::NewFromUnsigned(activityCtx->querySlave()));
  888. jsActivityCtx->Set(v8::String::New("strand"), v8::Integer::NewFromUnsigned(activityCtx->queryStrand()));
  889. context->Global()->Set(v8::String::New("__activity__"), jsActivityCtx);
  890. }
  891. result = v8::Persistent<v8::Value>::New(script->Run());
  892. v8::Handle<v8::Value> exception = tryCatch.Exception();
  893. if (!exception.IsEmpty())
  894. {
  895. v8::String::AsciiValue msg(exception);
  896. throw MakeStringException(MSGAUD_user, 0, "v8embed: %s", *msg);
  897. }
  898. }
  899. protected:
  900. const IThorActivityContext *activityCtx = nullptr;
  901. v8::Isolate *isolate;
  902. v8::Persistent<v8::Context> context;
  903. v8::Persistent<v8::Script> script;
  904. v8::Persistent<v8::Value> result;
  905. };
  906. static __thread V8JavascriptEmbedFunctionContext * theFunctionContext; // We reuse per thread, for speed
  907. static __thread ThreadTermFunc threadHookChain;
  908. static void releaseContext()
  909. {
  910. if (theFunctionContext)
  911. {
  912. ::Release(theFunctionContext);
  913. theFunctionContext = NULL;
  914. }
  915. if (threadHookChain)
  916. {
  917. (*threadHookChain)();
  918. threadHookChain = NULL;
  919. }
  920. }
  921. class V8JavascriptEmbedContext : public CInterfaceOf<IEmbedContext>
  922. {
  923. public:
  924. V8JavascriptEmbedContext()
  925. {
  926. }
  927. virtual IEmbedFunctionContext *createFunctionContext(unsigned flags, const char *options) override
  928. {
  929. return createFunctionContextEx(nullptr, nullptr, flags, options);
  930. }
  931. virtual IEmbedFunctionContext *createFunctionContextEx(ICodeContext * ctx, const IThorActivityContext *activityContext, unsigned flags, const char *options) override
  932. {
  933. if (flags & EFimport)
  934. UNSUPPORTED("IMPORT");
  935. if (!theFunctionContext)
  936. {
  937. theFunctionContext = new V8JavascriptEmbedFunctionContext;
  938. threadHookChain = addThreadTermFunc(releaseContext);
  939. }
  940. theFunctionContext->setActivityContext(activityContext);
  941. return LINK(theFunctionContext);
  942. }
  943. virtual IEmbedServiceContext *createServiceContext(const char *service, unsigned flags, const char *options) override
  944. {
  945. throwUnexpected();
  946. }
  947. } theEmbedContext;
  948. extern DECL_EXPORT IEmbedContext* getEmbedContext()
  949. {
  950. return LINK(&theEmbedContext);
  951. }
  952. extern DECL_EXPORT bool syntaxCheck(const char *script)
  953. {
  954. return true; // MORE
  955. }
  956. } // namespace