jsmoz.ecl 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. /* Example of calling JavaScript (Mozilla SpiderMonkey) from ECL code via embedded C++
  2. *
  3. * This example evalues a JavaScript expression within an ECL transform
  4. *
  5. */
  6. #option('compileOptions', '-I/usr/include/js/')
  7. #option('linkOptions', '-lmozjs185')
  8. // Embedded C++ that makes a call to a JavaScript function
  9. string jseval(varstring script, varstring a, varstring b) := BEGINC++
  10. // This section of the code should probably move to a plugin, or somesuch
  11. /* Include the JSAPI header file to get access to SpiderMonkey. */
  12. #include "jsapi.h"
  13. #include <pthread.h>
  14. /* The class of the global object. */
  15. static JSClass global_class = {
  16. "global", JSCLASS_GLOBAL_FLAGS,
  17. JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
  18. JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
  19. JSCLASS_NO_OPTIONAL_MEMBERS
  20. };
  21. class MozJsContext
  22. {
  23. JSRuntime *rt;
  24. JSContext *cx;
  25. JSObject *global;
  26. public:
  27. MozJsContext()
  28. {
  29. // Initialize to NULL so that initFailed can be used
  30. rt = NULL;
  31. cx = NULL;
  32. global = NULL;
  33. // We use a separate runtime each time - this may be a bad idea
  34. rt = JS_NewRuntime(8 * 1024 * 1024);
  35. if (!rt)
  36. initFailed("Could not create JavaScript runtime");
  37. // We need a context per thread - we COULD share between JS calls on a thread (using TLS, for example)
  38. cx = JS_NewContext(rt, 8192);
  39. if (cx == NULL)
  40. initFailed("Could not create JavaScript context");
  41. JS_SetOptions(cx, JSOPTION_VAROBJFIX | JSOPTION_JIT | JSOPTION_METHODJIT);
  42. JS_SetVersion(cx, JSVERSION_LATEST);
  43. JS_SetErrorReporter(cx, reportError);
  44. /*
  45. * Create the global object in a new compartment.
  46. * You always need a global object per context.
  47. */
  48. global = JS_NewCompartmentAndGlobalObject(cx, &global_class, NULL);
  49. if (global == NULL)
  50. initFailed("Could not create JavaScript global object");
  51. /*
  52. * Populate the global object with the standard JavaScript
  53. * function and object classes, such as Object, Array, Date.
  54. */
  55. if (!JS_InitStandardClasses(cx, global))
  56. initFailed("Could not populate JavaScript global object");
  57. JS_BeginRequest(cx); // Probably not really necessary with a separate runtime per instance, but will be as soon as we change that.
  58. }
  59. ~MozJsContext()
  60. {
  61. if (global)
  62. JS_EndRequest(cx);
  63. if (cx)
  64. JS_DestroyContext(cx);
  65. if (rt)
  66. JS_DestroyRuntime(rt);
  67. }
  68. inline operator JSContext *() const { return cx; }
  69. inline JSObject *queryGlobal() const { return global; }
  70. private:
  71. void cleanup()
  72. {
  73. if (cx)
  74. JS_DestroyContext(cx);
  75. if (rt)
  76. JS_DestroyRuntime(rt);
  77. }
  78. void initFailed(const char *why)
  79. {
  80. cleanup();
  81. rtlFail(0, why);
  82. }
  83. static void reportError(JSContext *cx, const char *message, JSErrorReport *report)
  84. {
  85. // MORE - need to think about what is appropriate here!
  86. fprintf(stderr, "%s:%u:%s\n",
  87. report->filename ? report->filename : "<no filename=\"filename\">",
  88. (unsigned int) report->lineno,
  89. message);
  90. }
  91. };
  92. //#body
  93. // extern void user1(size32_t & __lenResult,char * & __result, const char * script, const char *a, const char *b) {
  94. {
  95. MozJsContext c;
  96. JSString * aa = JS_NewStringCopyZ(c, a);
  97. JSString * bb = JS_NewStringCopyZ(c, b);
  98. jsval rval;
  99. JS_DefineProperty(c, c.queryGlobal(), "a", STRING_TO_JSVAL(aa), NULL, NULL, JSPROP_READONLY);
  100. JS_DefineProperty(c, c.queryGlobal(), "b", STRING_TO_JSVAL(bb), NULL, NULL, JSPROP_READONLY);
  101. JSBool ok = JS_EvaluateScript(c, c.queryGlobal(), script, strlen(script), __FILE__, __LINE__, &rval);
  102. if (rval == NULL | rval == JS_FALSE)
  103. rtlFail(0, "Error in JavaScript evaluation");
  104. JSString *str = JS_ValueToString(c, rval);
  105. const char * chars = JS_EncodeString(c, str);
  106. __lenResult = strlen(chars);
  107. __result = (char *) rtlMalloc(__lenResult);
  108. memcpy(__result, chars, __lenResult);
  109. }
  110. ENDC++;
  111. //--------------------------------------------------------
  112. // ECL code - an input dataset with 2 records, each containing 2 strings
  113. // Note that it uses the threaded concat operation, to test that multi-threaded access to the JS engine works
  114. inrec := RECORD
  115. string f1;
  116. string f2;
  117. END;
  118. infile1 := DATASET([{'a', 'b'}, {'c', 'd'}], inrec);
  119. infile2 := DATASET([{'e', 'f'}, {'g', 'h'}], inrec);
  120. // Output record has just one string, filled in from the result of the JavaScript function
  121. outrec := RECORD
  122. string c;
  123. END;
  124. outrec t(inrec L) := TRANSFORM
  125. SELF.c := jseval('a + b', L.f1, L.f2) // Evaluates JavaScript expression to concatenate strings
  126. END;
  127. outfile := project(infile1, t(LEFT))+project(infile2, t(LEFT)); // threaded concat operation
  128. outfile;