v8embed.cpp 33 KB

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