Browse Source

HPCC-21142 Add persistence options to Java embed

Also fixes:

HPCC-21052 Use thread local variables for IEmbedFunctionContext
HPCC-21137 Problems with embedded java tests on master
HPCC-21127 Incorrect name mangling rules (additional fix)

Signed-off-by: Richard Chapman <rchapman@hpccsystems.com>
Richard Chapman 6 years ago
parent
commit
ec0362f97b

+ 10 - 4
ecl/hql/hqlatoms.cpp

@@ -69,6 +69,7 @@ IIdAtom * substituteEmbeddedScriptId;
 IIdAtom * supportsImportId;
 IIdAtom * supportsScriptId;
 IIdAtom * syntaxCheckId;
+IIdAtom * threadlocalId;
 IIdAtom * unknownId;
 IIdAtom * unnamedId;
 IIdAtom * valueId;
@@ -115,6 +116,7 @@ IAtom * clusterAtom;
 IAtom * _codehash_Atom;
 IAtom * _colocal_Atom;
 IAtom * commonAtom;
+IAtom * compileAtom;
 IAtom * _complexKeyed_Atom;
 IAtom * compressedAtom;
 IAtom * __compressed__Atom;
@@ -342,8 +344,8 @@ IAtom * _payload_Atom;
 IAtom * persistAtom;
 IAtom * physicalFilenameAtom;
 IAtom * pluginAtom;
-IAtom * prebindAtom;
-IAtom * precompileAtom;
+IAtom * _prebind_Atom;
+IAtom * _precompile_Atom;
 IAtom * prefetchAtom;
 IAtom * preloadAtom;
 IAtom * priorityAtom;
@@ -437,6 +439,7 @@ IAtom * templateAtom;
 IAtom * terminateAtom;
 IAtom * terminatorAtom;
 IAtom * thorAtom;
+IAtom * _threadlocal_Atom;
 IAtom * thresholdAtom;
 IAtom * throwAtom;
 IAtom * timeAtom;
@@ -538,6 +541,7 @@ MODULE_INIT(INIT_PRIORITY_HQLATOM)
     MAKEID(supportsImport);
     MAKEID(supportsScript);
     MAKEID(syntaxCheck);
+    MAKEID(threadlocal);
     MAKEID(unknown);
     unnamedId = createIdAtom("<unnamed>");
     MAKEID(value);
@@ -584,6 +588,7 @@ MODULE_INIT(INIT_PRIORITY_HQLATOM)
     MAKESYSATOM(codehash);
     MAKESYSATOM(colocal);
     MAKEATOM(common);
+    MAKEATOM(compile);
     MAKESYSATOM(complexKeyed);
     MAKEATOM(compressed);
     MAKEATOM(__compressed__);
@@ -812,8 +817,8 @@ MODULE_INIT(INIT_PRIORITY_HQLATOM)
     MAKEATOM(persist);
     MAKEATOM(physicalFilename);
     MAKEATOM(plugin);
-    MAKEATOM(prebind);
-    MAKEATOM(precompile);
+    MAKESYSATOM(prebind);
+    MAKESYSATOM(precompile);
     MAKEATOM(prefetch);
     MAKEATOM(preload);
     MAKEATOM(priority);
@@ -906,6 +911,7 @@ MODULE_INIT(INIT_PRIORITY_HQLATOM)
     MAKEATOM(terminator);
     MAKEATOM(escape);
     MAKEATOM(thor);
+    MAKESYSATOM(threadlocal);
     MAKEATOM(threshold);
     MAKEATOM(throw);
     MAKEATOM(time);

+ 5 - 2
ecl/hql/hqlatoms.hpp

@@ -71,6 +71,7 @@ extern HQL_API IIdAtom * substituteEmbeddedScriptId;
 extern HQL_API IIdAtom * supportsImportId;
 extern HQL_API IIdAtom * supportsScriptId;
 extern HQL_API IIdAtom * syntaxCheckId;
+extern HQL_API IIdAtom * threadlocalId;
 extern HQL_API IIdAtom * unknownId;
 extern HQL_API IIdAtom * unnamedId;
 extern HQL_API IIdAtom * valueId;
@@ -119,6 +120,7 @@ extern HQL_API IAtom * clusterAtom;
 extern HQL_API IAtom * _codehash_Atom;
 extern HQL_API IAtom * _colocal_Atom;
 extern HQL_API IAtom * commonAtom;
+extern HQL_API IAtom * compileAtom;
 extern HQL_API IAtom * _complexKeyed_Atom;
 extern HQL_API IAtom * compressedAtom;
 extern HQL_API IAtom * __compressed__Atom;
@@ -347,8 +349,8 @@ extern HQL_API IAtom * _payload_Atom;
 extern HQL_API IAtom * persistAtom;
 extern HQL_API IAtom * physicalFilenameAtom;
 extern HQL_API IAtom * pluginAtom;
-extern HQL_API IAtom * prebindAtom;
-extern HQL_API IAtom * precompileAtom;
+extern HQL_API IAtom * _prebind_Atom;
+extern HQL_API IAtom * _precompile_Atom;
 extern HQL_API IAtom * prefetchAtom;
 extern HQL_API IAtom * preloadAtom;
 extern HQL_API IAtom * priorityAtom;
@@ -442,6 +444,7 @@ extern HQL_API IAtom * terminateAtom;
 extern HQL_API IAtom * terminatorAtom;
 extern HQL_API IAtom * escapeAtom;
 extern HQL_API IAtom * thorAtom;
+extern HQL_API IAtom * _threadlocal_Atom;
 extern HQL_API IAtom * thresholdAtom;
 extern HQL_API IAtom * throwAtom;
 extern HQL_API IAtom * timeAtom;

+ 1 - 0
ecl/hql/hqlerrors.hpp

@@ -71,6 +71,7 @@
 #define WRN_REQUIRES_SIGNED         1054
 #define WRN_DISALLOWED              1055
 #define WRN_EMBEDWARNING            1056
+#define WRN_EMBEDFOLD               1057
 
 //Do not define any warnings > 1099 - use the range below instead
 

+ 8 - 5
ecl/hql/hqlfold.cpp

@@ -1366,7 +1366,10 @@ IValue * foldExternalCall(IHqlExpression* expr, unsigned foldOptions, ITemplateC
     HINSTANCE hDll;
     void *funcptr = loadExternalEntryPoint(expr, foldOptions, templateContext, library.str(), entry.str(), hDll);
     if (!funcptr)
+    {
+        DBGLOG("Failed to load function %s", entry.str());
         return NULL;
+    }
     return doFoldExternalCall(expr, foldOptions, templateContext, library.str(), entry.str(), funcptr);
 }
 
@@ -1663,12 +1666,12 @@ IHqlExpression * foldEmbeddedCall(IHqlExpression* expr, unsigned foldOptions, IT
     Owned<IEmbedContext> __plugin = (IEmbedContext *) plugin->getIntValue();  // We declared as int since ecl has no pointer type - not sure what the clean fix is here...
     DummyContext dummyContext;
     Owned<IEmbedFunctionContext> __ctx = __plugin->createFunctionContextEx(&dummyContext,nullptr,flags,optionsStr.str());
-
+    EmbedContextBlock b(__ctx);
     IValue *query = body->queryChild(0)->queryValue();
     assertex(query);
-    if (!body->hasAttribute(prebindAtom))
+    if (!body->hasAttribute(_prebind_Atom))
     {
-        if (body->hasAttribute(precompileAtom))
+        if (body->hasAttribute(_precompile_Atom))
             __ctx->loadCompiledScript(query->getSize(), query->queryValue());
         else
         {
@@ -1787,9 +1790,9 @@ IHqlExpression * foldEmbeddedCall(IHqlExpression* expr, unsigned foldOptions, IT
             return NULL;
         }
     }
-    if (body->hasAttribute(prebindAtom))
+    if (body->hasAttribute(_prebind_Atom))
     {
-        if (body->hasAttribute(precompileAtom))
+        if (body->hasAttribute(_precompile_Atom))
             __ctx->loadCompiledScript(query->getSize(), query->queryValue());
         else
         {

+ 2 - 1
ecl/hql/hqlgram.hpp

@@ -588,6 +588,7 @@ public:
     IHqlExpression * processIndexBuild(const attribute &err, attribute & indexAttr, attribute * recordAttr, attribute * payloadAttr, attribute & filenameAttr, attribute & flagsAttr);
     IHqlExpression * processCompoundFunction(attribute & result, bool outOfLine);
     IHqlExpression * processEmbedBody(const attribute & errpos, IHqlExpression * embedText, IHqlExpression * language, IHqlExpression *attribs);
+    IHqlExpression * checkEmbedBody(const attribute & errpos, DefineIdSt * defineid, IHqlExpression *body, HqlExprArray & params);
     IHqlExpression * getGpgSignature();
     void processEnum(attribute & idAttr, IHqlExpression * value);
     void processError(bool full);
@@ -861,7 +862,7 @@ protected:
     void defineSymbolProduction(attribute & nameattr, attribute & paramattr, attribute & assignattr, attribute * valueattr, attribute * failattr, attribute & semiattr);
     void definePatternSymbolProduction(attribute & nameattr, attribute & paramattr, const attribute & assignAttr, attribute & valueAttr, attribute & workflowAttr, const attribute & semiattr);
     void cloneInheritedAttributes(IHqlScope * scope, const attribute & errpos);
-    IHqlExpression * normalizeFunctionExpression(DefineIdSt * defineid, IHqlExpression * expr, IHqlExpression * failure, bool isParametered, HqlExprArray & parameters, IHqlExpression * defaults, IHqlExpression * modifiers);
+    IHqlExpression * normalizeFunctionExpression(const attribute & idattr, DefineIdSt * defineid, IHqlExpression * expr, IHqlExpression * failure, bool isParametered, HqlExprArray & parameters, IHqlExpression * defaults, IHqlExpression * modifiers);
 
     IHqlExpression * createEvaluateOutputModule(const attribute & errpos, IHqlExpression * scopeExpr, IHqlExpression * ifaceExpr, node_operator outputOp, IIdAtom *matchId);
     IHqlExpression * createStoredModule(const attribute & errpos, IHqlExpression * scopeExpr);

+ 155 - 95
ecl/hql/hqlgram2.cpp

@@ -943,94 +943,14 @@ IHqlExpression * HqlGram::processEmbedBody(const attribute & errpos, IHqlExpress
             reportError(ERR_PluginNoScripting, errpos, "Module %s does not support %s", str(moduleId), isImport ? "import" : "script");
         OwnedHqlExpr prebind = pluginScope->lookupSymbol(prebindId, LSFpublic, lookupCtx);
         if (matchesBoolean(prebind, true))
-            args.append(*createAttribute(prebindAtom));
+            args.append(*createAttribute(_prebind_Atom));
+        OwnedHqlExpr threadlocal = pluginScope->lookupSymbol(threadlocalId, LSFpublic, lookupCtx);
+        if (matchesBoolean(threadlocal, true))
+            args.append(*createAttribute(_threadlocal_Atom));
         OwnedHqlExpr syntaxCheckFunc = pluginScope->lookupSymbol(syntaxCheckId, LSFpublic, lookupCtx);
-        bool failedSyntaxCheck = false;
-        if (syntaxCheckFunc && !isImport)
-        {
-            HqlExprArray syntaxCheckArgs;
-            embedText->unwindList(syntaxCheckArgs, no_comma);
-            if (matchesBoolean(prebind, true))
-            {
-                // To syntax-check functions with prebind set, we need to pass in the parameter names
-                StringBuffer argnames;
-                HqlExprArray & params = defineScopes.tos().activeParameters;
-                ForEachItemIn(i, params)
-                {
-                    IHqlExpression * param = &params.item(i);
-                    IAtom *name = param->queryName();
-                    if (name)
-                        argnames.append(',').append(name->queryStr());
-                }
-                argnames.append(',').append("__activity__");   // Special parameter indicating activity context - not always passed but won't break anything if we pretend it is (I hope!)
-                if (argnames.length())
-                    syntaxCheckArgs.append(*createConstant(argnames.str()+1));
-                else
-                    syntaxCheckArgs.append(*createConstant(""));  //passing nullptr might be better but not sure how
-            }
-            OwnedHqlExpr syntax = createBoundFunction(this, syntaxCheckFunc, syntaxCheckArgs, lookupCtx.functionCache, true);
-            OwnedHqlExpr folded = foldHqlExpression(syntax);
-            if (folded->queryValue())
-            {
-                StringBuffer errors;
-                folded->queryValue()->getStringValue(errors);
-                if (errors.length())
-                {
-                    StringArray errlines;
-                    errlines.appendList(errors, "\n");
-                    ForEachItemIn(idx, errlines)
-                    {
-                        const char *err = errlines.item(idx);
-                        if (strlen(err))
-                        {
-                            ECLlocation pos(errpos.pos);
-                            unsigned line, col;
-                            char dummy;
-                            if (sscanf(err, "(%u,%u):%c", &line, &col, &dummy)==3)
-                            {
-                                err = strchr(err, ':') + 1;
-                                while (isspace(*err))
-                                    err++;
-                                pos.lineno = embedText->getStartLine()+line-1;
-                                pos.column = col;
-                                pos.position = 0;
-                            }
-                            if (strnicmp(err, "warning:", 8)==0)
-                            {
-                                err = strchr(err, ':') + 1;
-                                while (isspace(*err))
-                                    err++;
-                                reportWarning(CategoryEmbed, WRN_EMBEDWARNING, pos, "%s", err);
-                            }
-                            else
-                            {
-                                if (strnicmp(err, "error:", 6)==0)
-                                {
-                                    err = strchr(err, ':') + 1;
-                                    while (isspace(*err))
-                                        err++;
-                                }
-                                reportError(ERR_EMBEDERROR, pos, "%s", err);
-                                failedSyntaxCheck = true;
-                            }
-                        }
-                    }
-                }
-            }
-            else
-            {
-                DBGLOG("INTERNAL: syntaxCheck was not foldable");  // Ignore as no fatal? Warning? Terminate? Warning that defaults to error?
-            }
-        }
         OwnedHqlExpr precompile = pluginScope->lookupSymbol(precompileId, LSFpublic, lookupCtx);
-        if (precompile && !failedSyntaxCheck && !lookupCtx.syntaxChecking())
-        {
-            HqlExprArray precompileArgs;
-            embedText->unwindList(precompileArgs, no_comma);
-            // Replace queryText with compiled version of it
-            args.replace(*createBoundFunction(this, precompile, precompileArgs, lookupCtx.functionCache, true), 0);
-            args.append(*createAttribute(precompileAtom));
-        }
+        if (!isImport & (syntaxCheckFunc || precompile))
+            args.append(*createExprAttribute(_original_Atom, LINK(language)));  // Add this so that we can complete the syntax check/precompile later (in checkEmbedBody), as we don't know func name until then
         args.append(*createExprAttribute(languageAtom, getEmbedContextFunc.getClear()));
         IHqlExpression *projectedAttr = queryAttribute(projectedAtom, args);
         if (projectedAttr)
@@ -9656,7 +9576,6 @@ IHqlExpression * HqlGram::associateSideEffects(IHqlExpression * expr, const ECLl
     return LINK(expr);
 }
 
-
 void HqlGram::doDefineSymbol(DefineIdSt * defineid, IHqlExpression * _expr, IHqlExpression * failure, const attribute & idattr, int assignPos, int semiColonPos, bool isParametered, IHqlExpression * modifiers)
 {
     OwnedHqlExpr expr = _expr;
@@ -9743,7 +9662,7 @@ void HqlGram::doDefineSymbol(DefineIdSt * defineid, IHqlExpression * _expr, IHql
         reportError(HQLERR_CannotDefineFunctionFunction, idattr.pos, HQLERR_CannotDefineFunctionFunction_Text);
     }
 
-    OwnedHqlExpr normalized = normalizeFunctionExpression(defineid, expr, failure, isParametered, activeScope.activeParameters, activeScope.createDefaults(), modifiers);
+    OwnedHqlExpr normalized = normalizeFunctionExpression(idattr, defineid, expr, failure, isParametered, activeScope.activeParameters, activeScope.createDefaults(), modifiers);
     defineSymbolInScope(targetScope, defineid, normalized, idattr, assignPos, semiColonPos);
 
     ::Release(failure);
@@ -9779,25 +9698,166 @@ IHqlExpression * HqlGram::attachMetaAttributes(IHqlExpression * ownedExpr, HqlEx
     return ownedExpr;
 }
 
-IHqlExpression * HqlGram::normalizeFunctionExpression(DefineIdSt * defineid, IHqlExpression * expr, IHqlExpression * failure, bool isParametered, HqlExprArray & parameters, IHqlExpression * defaults, IHqlExpression * modifiers)
+IHqlExpression * HqlGram::checkEmbedBody(const attribute & errpos, DefineIdSt * defineid, IHqlExpression *body, HqlExprArray & params)
+{
+    IHqlExpression *language = body->queryAttribute(_original_Atom);  // Did we save away the module that defines this plugin, so that we can check/precompile later?
+    if (language)
+    {
+        HqlExprArray bodyArgs;
+        unwindChildren(bodyArgs, body);
+        removeAttribute(bodyArgs, _original_Atom);
+        LinkedHqlExpr compilerOptions = queryAttributeChild(body, compileAtom, 0);
+        if (!compilerOptions || !compilerOptions->isConstant())
+            compilerOptions.setown(createBlankString());    //NULL would be preferable
+        LinkedHqlExpr persistOptions = queryAttributeChild(body, persistAtom, 0);
+        if (!persistOptions || !persistOptions->isConstant())
+            persistOptions.setown(createBlankString());    //NULL would be preferable
+
+        StringBuffer argnames;
+        if (body->hasAttribute(activityAtom))
+        {
+            argnames.append("__activity__");
+        }
+        ForEachItemIn(i, params)
+        {
+            IHqlExpression * param = &params.item(i);
+            IAtom *name = param->queryName();
+            if (name)
+            {
+                if (argnames.length())
+                    argnames.append(',');
+                argnames.append(name->queryStr());
+            }
+        }
+        OwnedHqlExpr argNamesParam  = createConstant(argnames.str());
+
+        language = language->queryChild(0);
+        IHqlExpression *embedText = body->queryChild(0);
+        IHqlScope *pluginScope = language->queryScope();
+        OwnedHqlExpr syntaxCheckFunc = pluginScope->lookupSymbol(syntaxCheckId, LSFpublic, lookupCtx);
+        bool failedSyntaxCheck = false;
+        if (syntaxCheckFunc)
+        {
+            HqlExprArray syntaxCheckArgs;
+            syntaxCheckArgs.append(*createConstant(defineid->id->queryStr()));
+            embedText->unwindList(syntaxCheckArgs, no_comma);
+            syntaxCheckArgs.append(*argNamesParam.getLink());
+            syntaxCheckArgs.append(*compilerOptions.getLink());
+            syntaxCheckArgs.append(*persistOptions.getLink());
+            OwnedHqlExpr syntax = createBoundFunction(this, syntaxCheckFunc, syntaxCheckArgs, lookupCtx.functionCache, true);
+            OwnedHqlExpr folded = foldHqlExpression(syntax);
+            if (folded->queryValue())
+            {
+                StringBuffer errors;
+                folded->queryValue()->getStringValue(errors);
+                if (errors.length())
+                {
+                    StringArray errlines;
+                    errlines.appendList(errors, "\n");
+                    ForEachItemIn(idx, errlines)
+                    {
+                        const char *err = errlines.item(idx);
+                        if (strlen(err))
+                        {
+                            ECLlocation pos(errpos.pos);
+                            unsigned line, col;
+                            char dummy;
+                            if (sscanf(err, "(%u,%u):%c", &line, &col, &dummy)==3)
+                            {
+                                err = strchr(err, ':') + 1;
+                                while (isspace(*err))
+                                    err++;
+                                pos.lineno = embedText->getStartLine()+line-1;
+                                pos.column = col;
+                                pos.position = 0;
+                            }
+                            if (strnicmp(err, "warning:", 8)==0)
+                            {
+                                err = strchr(err, ':') + 1;
+                                while (isspace(*err))
+                                    err++;
+                                reportWarning(CategoryEmbed, WRN_EMBEDWARNING, pos, "%s", err);
+                            }
+                            else
+                            {
+                                if (strnicmp(err, "error:", 6)==0)
+                                {
+                                    err = strchr(err, ':') + 1;
+                                    while (isspace(*err))
+                                        err++;
+                                }
+                                reportError(ERR_EMBEDERROR, pos, "%s", err);
+                                failedSyntaxCheck = true;
+                            }
+                        }
+                    }
+                }
+            }
+            else
+            {
+                reportWarning(CategoryError, WRN_EMBEDFOLD, errpos.pos, "Embedded syntax check function could not be called");
+            }
+        }
+        OwnedHqlExpr precompile = pluginScope->lookupSymbol(precompileId, LSFpublic, lookupCtx);
+        if (precompile && !failedSyntaxCheck && !lookupCtx.syntaxChecking())
+        {
+            HqlExprArray precompileArgs;
+            precompileArgs.append(*createConstant(defineid->id->queryStr()));
+            embedText->unwindList(precompileArgs, no_comma);
+            // Replace queryText with compiled version of it
+            precompileArgs.append(*argNamesParam.getLink());
+            precompileArgs.append(*compilerOptions.getLink());
+            precompileArgs.append(*persistOptions.getLink());
+            OwnedHqlExpr compiled = createBoundFunction(this, precompile, precompileArgs, lookupCtx.functionCache, true);
+            OwnedHqlExpr folded = foldHqlExpression(compiled);  // Best to fold here if we can, as the syntax check may cache on the assumption that next call is the compile. There may be a better way
+            if (folded)
+                bodyArgs.replace(*folded.getClear(), 0);
+            else
+                bodyArgs.replace(*compiled.getClear(), 0);
+            bodyArgs.append(*createAttribute(_precompile_Atom));
+        }
+        return body->clone(bodyArgs);
+    }
+    return nullptr;
+}
+
+IHqlExpression * HqlGram::normalizeFunctionExpression(const attribute &idattr, DefineIdSt * defineid, IHqlExpression *_expr, IHqlExpression * failure, bool isParametered, HqlExprArray & parameters, IHqlExpression * defaults, IHqlExpression * modifiers)
 {
+    OwnedHqlExpr expr;
+    expr.set(_expr);
+    if (expr->getOperator() == no_outofline)
+    {
+        IHqlExpression * body = expr->queryChild(0);
+        if (body->getOperator()==no_embedbody)
+        {
+            OwnedHqlExpr newbody = checkEmbedBody(idattr, defineid, body, parameters);
+            if (newbody)
+                expr.setown(replaceChild(expr, 0, newbody));
+        }
+    }
+    else if (expr->getOperator() == no_embedbody)
+    {
+        OwnedHqlExpr newbody = checkEmbedBody(idattr, defineid, expr, parameters);
+        if (newbody)
+            expr.setown(newbody.getClear());
+    }
     HqlExprCopyArray activeParameters;
     gatherActiveParameters(activeParameters);
     HqlExprArray meta;
-    expr = attachWorkflowOwn(meta, LINK(expr), failure, &activeParameters);
+    expr.setown(attachWorkflowOwn(meta, expr.getLink(), failure, &activeParameters));
     if (isParametered)
     {
         IHqlExpression * formals = createValue(no_sortlist, makeSortListType(NULL), parameters);
-        expr = createFunctionDefinition(defineid->id, expr, formals, defaults, modifiers);
+        expr.setown(createFunctionDefinition(defineid->id, expr.getClear(), formals, defaults, modifiers));
     }
     else
         ::Release(modifiers);
-    expr = attachPendingWarnings(expr);
-    expr = attachMetaAttributes(expr, meta);
+    expr.setown(attachPendingWarnings(expr.getClear()));
+    expr.setown(attachMetaAttributes(expr.getClear(), meta));
     IPropertyTree * doc = defineid->queryDoc();
     if (doc)
-        expr = createJavadocAnnotation(expr, LINK(doc));
-    return expr;
+        expr.setown(createJavadocAnnotation(expr.getClear(), LINK(doc)));
+    return expr.getClear();
 }
 
 void HqlGram::defineSymbolInScope(IHqlScope * scope, DefineIdSt * defineid, IHqlExpression * expr, const attribute & idattr, int assignPos, int semiColonPos)

+ 2 - 2
ecl/hql/hqlutil.cpp

@@ -8290,9 +8290,9 @@ protected:
             if (streq(repeatsSeen.item(idx), typeStr))
             {
                 if (idx)
-                    return thisRepeat.appendf("S%d_", idx-1).str();
+                    return thisRepeat.clear().appendf("S%d_", idx-1).str();
                 else
-                    return thisRepeat.append("S_").str();
+                    return thisRepeat.clear().append("S_").str();
             }
         }
         // For compounds we need to add subtypes into the table too

+ 2 - 2
ecl/hql/hqlutil.hpp

@@ -256,8 +256,8 @@ extern HQL_API bool isTimed(IHqlExpression * expr);
 inline bool isInternalEmbedAttr(IAtom *name)
 {
     return name == languageAtom || name == projectedAtom || name == streamedAtom || name == _linkCounted_Atom || 
-           name == importAtom || name==foldAtom || name==timeAtom || name==prebindAtom || name==precompileAtom ||
-           name == activityAtom || name == localAtom || name == parallelAtom;
+           name == importAtom || name==foldAtom || name==timeAtom || name==_prebind_Atom || name==_precompile_Atom || name==_original_Atom ||
+           name == _threadlocal_Atom || name == activityAtom || name == localAtom || name == parallelAtom;
 }
 
 

+ 47 - 36
ecl/hqlcpp/hqlcpp.cpp

@@ -11997,12 +11997,44 @@ void HqlCppTranslator::buildScriptFunctionDefinition(BuildCtx &ctx, IHqlExpressi
     funcctx.associateExpr(codeContextMarkerExpr, codeContextMarkerExpr);
     funcctx.associateExpr(globalContextMarkerExpr, globalContextMarkerExpr);
 
+    IHqlExpression *optionsParam = nullptr;
+    IHqlExpression *queryParam = nullptr;
+    unsigned numRealParams = 0;
+    ForEachChild(formalIdx, formals)
+    {
+        IHqlExpression *formal = formals->queryChild(formalIdx);
+        if (formal->queryId()==__optionsId)
+            optionsParam = formal;
+        else if (formal->queryId()==__queryId)
+            queryParam = formal;  // Query text is being passed in to the function
+        else
+            numRealParams++;
+    }
+    OwnedHqlExpr query;  // The text of the query (or the name of the import)
+    if (queryParam)
+        query.setown(createActualFromFormal(queryParam));
+    else
+        query.set(bodyCode->queryChild(0));
+    IValue *queryVal = query->queryValue();
+
+    OwnedHqlExpr embedOptions;
+    if (optionsParam)
+        embedOptions.setown(createActualFromFormal(optionsParam));
+    else
+        embedOptions.setown(getEmbedOptionString(bodyCode));
+    IValue *optionVal = embedOptions->queryValue();
+
+    bool threadlocal = bodyCode->hasAttribute(_threadlocal_Atom) && queryVal != nullptr && optionVal != nullptr;
     HqlExprArray noargs;
     OwnedHqlExpr getPlugin = bindFunctionCall(language, noargs);
-    OwnedHqlExpr pluginPtr = createQuoted("Owned<IEmbedContext> __plugin", getPlugin->getType());
+    OwnedHqlExpr pluginPtr = createQuoted(threadlocal ? "static thread_local Owned<IEmbedContext> __plugin" : "Owned<IEmbedContext> __plugin", getPlugin->getType());
     buildAssignToTemp(funcctx, pluginPtr, getPlugin);
+
     StringBuffer createParam;
-    createParam.append("Owned<IEmbedFunctionContext> __ctx = __plugin->createFunctionContextEx(ctx,");
+    if (threadlocal)
+        createParam.append("static thread_local IEmbedFunctionContext *__ctx = __plugin->createFunctionContextEx(ctx,");
+    else
+        createParam.append("Owned<IEmbedFunctionContext> __ctx = __plugin->createFunctionContextEx(ctx,");
 
     if (functionBodyIsActivity(bodyCode))
         createParam.append("activity,");
@@ -12012,28 +12044,16 @@ void HqlCppTranslator::buildScriptFunctionDefinition(BuildCtx &ctx, IHqlExpressi
     createParam.append(isImport ? "EFimport" : "EFembed");
     if (returnType->getTypeCode()==type_void)
         createParam.append("|EFnoreturn");
+    if (threadlocal)
+        createParam.append("|EFthreadlocal");
 
-    IHqlExpression *optionsParam = nullptr;
-    IHqlExpression *queryParam = nullptr;
-    unsigned numRealParams = 0;
-    ForEachChild(formalIdx, formals)
-    {
-        IHqlExpression *formal = formals->queryChild(formalIdx);
-        if (formal->queryId()==__optionsId)
-            optionsParam = formal;
-        else if (formal->queryId()==__queryId)
-            queryParam = formal;
-        else
-            numRealParams++;
-    }
     if (!numRealParams)
         createParam.append("|EFnoparams");
 
-    if (optionsParam)
+    if (embedOptions)
     {
-        OwnedHqlExpr folded = createActualFromFormal(optionsParam);
         CHqlBoundExpr bound;
-        buildExpr(funcctx, folded, bound);
+        buildExpr(funcctx, embedOptions, bound);
         createParam.append(",");
         generateExprCpp(createParam, bound.expr);
     }
@@ -12041,6 +12061,7 @@ void HqlCppTranslator::buildScriptFunctionDefinition(BuildCtx &ctx, IHqlExpressi
         createParam.append(",NULL");
     createParam.append(");");
     funcctx.addQuoted(createParam);
+    funcctx.addQuoted("EmbedContextBlock __b(__ctx);");
     OwnedHqlExpr ctxVar = createVariable("__ctx", makeBoolType());
 
     HqlExprArray scriptArgs;
@@ -12064,23 +12085,19 @@ void HqlCppTranslator::buildScriptFunctionDefinition(BuildCtx &ctx, IHqlExpressi
         LinkedHqlExpr substSearch = queryAttributeChild(bodyCode, projectedAtom, 0);
         assertex (substSearch);
         IValue *substValue = substSearch->queryValue();
-        if (queryParam || !substValue)
+        if (!queryVal || !substValue)
         {
+            // Have to do the substitution at runtime if query or substId are not constant
             HqlExprArray args;
-            if (queryParam)
-                args.append(*createActualFromFormal(queryParam));
-            else
-                args.append(*LINK(bodyCode->queryChild(0)));
+            args.append(*query.getLink());
             args.append(*createConstant(createUtf8Value(fieldlist.length()-1, fieldlist.str()+1, makeUtf8Type(UNKNOWN_LENGTH, NULL))));
             args.append(*LINK(substSearch));
             scriptArgs.append(*bindFunctionCall(substituteEmbeddedScriptId, args,makeUtf8Type(UNKNOWN_LENGTH, NULL)));
         }
         else
         {
-            IValue *query = bodyCode->queryChild(0)->queryValue();
-            assertex(query);
             StringBuffer origBody;
-            query->getUTF8Value(origBody);
+            queryVal->getUTF8Value(origBody);
             StringBuffer search;
             substValue->getUTF8Value(search);
             rtlDataAttr result;
@@ -12091,22 +12108,16 @@ void HqlCppTranslator::buildScriptFunctionDefinition(BuildCtx &ctx, IHqlExpressi
     }
     else
     {
-        if (queryParam)
-        {
-            OwnedHqlExpr query = createActualFromFormal(queryParam);
-            scriptArgs.append(*query.getClear());
-        }
-        else
-            scriptArgs.append(*LINK(bodyCode->queryChild(0)));
+        scriptArgs.append(*query.getLink());
     }
     IIdAtom *id;
     if (isImport)
         id = importId;
-    else if (bodyCode->hasAttribute(precompileAtom))
+    else if (bodyCode->hasAttribute(_precompile_Atom))
         id = loadCompiledScriptId;
     else
         id = compileEmbeddedScriptId;
-    if (!bodyCode->hasAttribute(prebindAtom))
+    if (!bodyCode->hasAttribute(_prebind_Atom))
         buildFunctionCall(funcctx, id, scriptArgs);
     ForEachChild(i, formals)
     {
@@ -12184,7 +12195,7 @@ void HqlCppTranslator::buildScriptFunctionDefinition(BuildCtx &ctx, IHqlExpressi
         args.append(*createActualFromFormal(param));
         buildFunctionCall(funcctx, bindFunc, args);
     }
-    if (bodyCode->hasAttribute(prebindAtom))
+    if (bodyCode->hasAttribute(_prebind_Atom))
         buildFunctionCall(funcctx, id, scriptArgs);
     funcctx.addQuotedLiteral("__ctx->callFunction();");
     IIdAtom * returnFunc;

+ 19 - 12
ecl/hqlcpp/hqlttcpp.cpp

@@ -2204,10 +2204,8 @@ static IHqlExpression * normalizeIndexBuild(IHqlExpression * expr, bool sortInde
     return NULL;
 }
 
-static IHqlExpression *getEmbedOptionString(IHqlExpression *funcdef)
+IHqlExpression *getEmbedOptionString(IHqlExpression *bodyCode)
 {
-    IHqlExpression * outofline = funcdef->queryChild(0);
-    IHqlExpression * bodyCode = outofline->queryChild(0);
     HqlExprArray attrArgs;
     ForEachChild(idx, bodyCode)
     {
@@ -2345,10 +2343,11 @@ IHqlExpression * ThorHqlTransformer::createTransformed(IHqlExpression * expr)
                     args.append(*createAttribute(allocatorAtom));
             }
             inheritAttribute(args, expr, activityAtom);
-
             OwnedHqlExpr body = createWrapper(no_outofline, expr->queryType(), args);
             HqlExprArray newFormals;
-            if (expr->hasAttribute(languageAtom))
+            OwnedHqlExpr options = getEmbedOptionString(expr);
+            bool constOptions = options->isConstant();
+            if (expr->hasAttribute(languageAtom) && !constOptions)
             {
                 HqlExprArray attrs;
                 attrs.append(*createAttribute(_hidden_Atom));
@@ -2357,8 +2356,8 @@ IHqlExpression * ThorHqlTransformer::createTransformed(IHqlExpression * expr)
             IHqlExpression * formals = createValue(no_sortlist, makeSortListType(NULL), newFormals);
             OwnedHqlExpr funcdef = createFunctionDefinition(createIdAtom(funcname), body.getClear(), formals, NULL, NULL);
             HqlExprArray actuals;
-            if (expr->hasAttribute(languageAtom))
-                actuals.append(*getEmbedOptionString(funcdef));
+            if (expr->hasAttribute(languageAtom) && !constOptions)
+                actuals.append(*options.getClear());
             return createBoundFunction(NULL, funcdef, actuals, nullptr, true);
         }
     }
@@ -6541,23 +6540,31 @@ IHqlExpression * WorkflowTransformer::transformInternalFunction(IHqlExpression *
         assertex(outofline->getOperator() == no_outofline);
         IHqlExpression * bodyCode = outofline->queryChild(0);
 
+        OwnedHqlExpr options = getEmbedOptionString(bodyCode);
+        bool constOptions = options->isConstant();
+
+        HqlExprArray attrs;
+        attrs.append(*createAttribute(_hidden_Atom));  // Applied to any new params we add
+
         HqlExprArray newFormals;
         unwindChildren(newFormals, formals);
-        HqlExprArray attrs;
-        attrs.append(*createAttribute(_hidden_Atom));
-        newFormals.append(*createParameter(__optionsId, newFormals.length(), LINK(unknownVarStringType), attrs));
+        if (!constOptions)
+        {
+            newFormals.append(*createParameter(__optionsId, newFormals.length(), LINK(unknownVarStringType), attrs));
+        }
 
         HqlExprArray newDefaults;
         if (defaults)
             unwindChildren(newDefaults, defaults, 0);
         while (newDefaults.length() < formals->numChildren())
             newDefaults.append(*createOmittedValue());
-        newDefaults.append(*getEmbedOptionString(newFuncDef));
+        if (!constOptions)
+            newDefaults.append(*options.getClear());
 
         IHqlExpression *query = bodyCode->queryChild(0);
         if (!query->queryValue())
         {
-            if (bodyCode->hasAttribute(precompileAtom))
+            if (bodyCode->hasAttribute(_precompile_Atom))
                 newFormals.append(*createParameter(__queryId, newFormals.length(), LINK(unknownDataType), attrs));
             else
                 newFormals.append(*createParameter(__queryId, newFormals.length(), LINK(unknownUtf8Type), attrs));

+ 2 - 0
ecl/hqlcpp/hqlttcpp.ipp

@@ -1303,6 +1303,8 @@ void optimizeActivities(unsigned wfid, HqlExprArray & exprs, bool optimizeCountC
 
 bool reportSemanticErrors(IHqlExpression * expr, IErrorReceiver & errors);
 
+IHqlExpression *getEmbedOptionString(IHqlExpression *embedBody);
+
 //------------------------------------------------------------------------
 
 #endif

+ 0 - 2
plugins/Rembed/R.ecllib

@@ -17,11 +17,9 @@
 
 EXPORT Language := SERVICE : plugin('Rembed')
   boolean getEmbedContext():cpp,pure,namespace='Rembed',entrypoint='getEmbedContext',prototype='IEmbedContext* getEmbedContext()';
-  boolean syntaxCheck(const varstring src):cpp,pure,namespace='Rembed',entrypoint='syntaxCheck';
   unload():cpp,pure,namespace='Rembed',entrypoint='unload';
 END;
 
 EXPORT getEmbedContext := Language.getEmbedContext;
-EXPORT syntaxCheck := Language.syntaxCheck;
 EXPORT boolean supportsImport := false;
 EXPORT boolean supportsScript := true;

+ 2 - 0
plugins/Rembed/Rembed.cpp

@@ -1349,6 +1349,8 @@ public:
     {
         throwUnexpected();
     }
+    virtual void enter() override {}
+    virtual void exit() override {}
 
     virtual void callFunction()
     {

+ 0 - 2
plugins/cassandra/cassandra.ecllib

@@ -17,9 +17,7 @@
 
 EXPORT Language := SERVICE : plugin('cassandraembed')
   boolean getEmbedContext():cpp,pure,namespace='cassandraembed',entrypoint='getEmbedContext',prototype='IEmbedContext* getEmbedContext()';
-  boolean syntaxCheck(const varstring src):cpp,pure,namespace='cassandraembed',entrypoint='syntaxCheck';
 END;
 EXPORT getEmbedContext := Language.getEmbedContext;
-EXPORT syntaxCheck := Language.syntaxCheck;
 EXPORT boolean supportsImport := false;
 EXPORT boolean supportsScript := true;

+ 3 - 0
plugins/cassandra/cassandraembed.cpp

@@ -1810,6 +1810,9 @@ public:
     {
         throwUnexpected();
     }
+    virtual void enter() override {}
+    virtual void exit() override {}
+
 protected:
     void lazyExecute()
     {

+ 0 - 2
plugins/couchbase/couchbase.ecllib

@@ -17,10 +17,8 @@
 
 EXPORT Language := SERVICE : plugin('couchbaseembed')
   boolean getEmbedContext():cpp,pure,namespace='couchbaseembed',entrypoint='getEmbedContext',prototype='IEmbedContext* getEmbedContext()';
-  boolean syntaxCheck(const varstring src):cpp,pure,namespace='couchbaseembed',entrypoint='syntaxCheck';
 END;
 
 EXPORT getEmbedContext := Language.getEmbedContext;
-EXPORT syntaxCheck := Language.syntaxCheck;
 EXPORT boolean supportsImport := false;
 EXPORT boolean supportsScript := true;

+ 2 - 0
plugins/couchbase/couchbaseembed.hpp

@@ -456,6 +456,8 @@ namespace couchbaseembed
            {
                UNSUPPORTED("loadCompiledScript");
            }
+           virtual void enter() override {}
+           virtual void exit() override {}
        protected:
            void execute();
            unsigned countBindings(const char *query);

+ 2 - 0
plugins/javaembed/CMakeLists.txt

@@ -40,6 +40,8 @@ if(JAVAEMBED)
             ./../../rtl/nbcd
             ./../../common/deftype
             ./../../system/jlib
+             ./../../dali/base
+            ./../../system/mp
             ./../../system/security/shared
             ./../../roxie/roxiemem
             ${HPCC_SOURCE_DIR}/esp/esdllib

BIN
plugins/javaembed/HpccClassLoader.class


+ 10 - 2
plugins/javaembed/HpccClassLoader.java

@@ -18,11 +18,13 @@
 package com.HPCCSystems;
 
 import java.net.*;
+import java.util.Hashtable;
 public class HpccClassLoader extends java.net.URLClassLoader
 {
     private long bytecode;
     private int bytecodeLen;
     private native Class defineClassForEmbed(int bytecodeLen, long bytecode, String name);
+    private Hashtable<String, Class> classes = new Hashtable<>();
     private HpccClassLoader(java.net.URL [] urls, ClassLoader parent, int _bytecodeLen, long _bytecode, String dllname)
     {
         super(urls, parent);
@@ -30,9 +32,15 @@ public class HpccClassLoader extends java.net.URLClassLoader
         bytecodeLen = _bytecodeLen;
         bytecode = _bytecode;
     }
-    public Class<?> findClass(String className) throws ClassNotFoundException
+    public synchronized Class<?> findClass(String className) throws ClassNotFoundException
     {
-        return defineClassForEmbed(bytecodeLen, bytecode, className.replace(".","/"));
+        Class result = (Class) classes.get(className);
+        if (result == null)
+        {
+            result = defineClassForEmbed(bytecodeLen, bytecode, className.replace(".","/"));
+            classes.put(className, result);
+        }
+        return result; 
     }
     public static HpccClassLoader newInstance(java.net.URL [] urls, ClassLoader parent, int _bytecodeLen, long _bytecode, String dllname)
     {

+ 3 - 2
plugins/javaembed/java.ecllib

@@ -17,11 +17,12 @@
 
 EXPORT Language := SERVICE : plugin('javaembed')
   integer getEmbedContext():cpp,pure,namespace='javaembed',entrypoint='getEmbedContext',prototype='IEmbedContext* getEmbedContext()',fold;
-  DATA precompile(UTF8 body):cpp,pure,namespace='javaembed',entrypoint='precompile',fold;
-  STRING syntaxCheck(UTF8 body):cpp,pure,namespace='javaembed',entrypoint='syntaxCheck',fold;
+  DATA precompile(const varstring funcname, UTF8 body, const varstring argnames, const varstring compileOptions, const varstring persistOptions):cpp,pure,namespace='javaembed',entrypoint='precompile',fold;
+  STRING syntaxCheck(const varstring funcname, UTF8 body, const varstring argnames, const varstring compileOptions, const varstring persistOptions):cpp,pure,namespace='javaembed',entrypoint='syntaxCheck',fold;
 END;
 EXPORT getEmbedContext := Language.getEmbedContext;
 EXPORT precompile := Language.precompile;
 EXPORT syntaxCheck := Language.syntaxCheck;
 EXPORT boolean supportsImport := true;
 EXPORT boolean supportsScript := true;
+EXPORT boolean threadlocal := true;

File diff suppressed because it is too large
+ 1531 - 1311
plugins/javaembed/javaembed.cpp


+ 0 - 2
plugins/mysql/mysql.ecllib

@@ -17,9 +17,7 @@
 
 EXPORT Language := SERVICE : plugin('mysqlembed')
   boolean getEmbedContext():cpp,pure,namespace='mysqlembed',entrypoint='getEmbedContext',prototype='IEmbedContext* getEmbedContext()';
-  boolean syntaxCheck(const varstring src):cpp,pure,namespace='mysqlembed',entrypoint='syntaxCheck';
 END;
 EXPORT getEmbedContext := Language.getEmbedContext;
-EXPORT syntaxCheck := Language.syntaxCheck;
 EXPORT boolean supportsImport := false;
 EXPORT boolean supportsScript := true;

+ 2 - 0
plugins/mysql/mysqlembed.cpp

@@ -1636,6 +1636,8 @@ public:
     {
         throwUnexpected();
     }
+    virtual void enter() override {}
+    virtual void exit() override {}
 protected:
     void lazyExecute()
     {

+ 6 - 2
plugins/py3embed/py3embed.cpp

@@ -1716,6 +1716,8 @@ public:
     {
         throwUnexpected();
     }
+    virtual void enter() override {}
+    virtual void exit() override {}
     virtual void setActivityOptions(const IThorActivityContext *ctx) override
     {
         Python3xEmbedContextBase::setActivityOptions(ctx);
@@ -1789,6 +1791,8 @@ public:
     {
         throwUnexpected();
     }
+    virtual void enter() override {}
+    virtual void exit() override {}
     virtual void callFunction()
     {
         result.setown(PyObject_CallObject(script, args));
@@ -1838,14 +1842,14 @@ extern DECL_EXPORT IEmbedContext* getEmbedContext()
     return new Python3xEmbedContext;
 }
 
-extern DECL_EXPORT void syntaxCheck(size32_t & __lenResult,char * & __result,size32_t charsBody,const char * body, const char * parms)
+extern DECL_EXPORT void syntaxCheck(size32_t & __lenResult, char * & __result, const char *funcname, size32_t charsBody, const char * body, const char *argNames, const char *compilerOptions, const char *persistOptions)
 {
     StringBuffer result;
     try
     {
         checkThreadContext();
         Owned<Python3xEmbedScriptContext> ctx = new Python3xEmbedScriptContext(threadContext);
-        ctx->setargs(parms);
+        ctx->setargs(argNames);
         ctx->compileEmbeddedScript(charsBody, body);
     }
     catch (IException *E)

+ 1 - 1
plugins/py3embed/python3.ecllib

@@ -17,7 +17,7 @@
 
 EXPORT Language := SERVICE : plugin('py3embed')
   integer getEmbedContext():cpp,pure,fold,namespace='py3embed',entrypoint='getEmbedContext',prototype='IEmbedContext* getEmbedContext()';
-  STRING syntaxCheck(UTF8 body, const VARSTRING params):cpp,pure,namespace='py3embed',entrypoint='syntaxCheck',fold;
+  STRING syntaxCheck(const varstring funcname, UTF8 body, const varstring argnames, const varstring compileOptions, const varstring persistOptions):cpp,pure,namespace='py3embed',entrypoint='syntaxCheck',fold;
 END;
 EXPORT getEmbedContext := Language.getEmbedContext;
 EXPORT syntaxCheck := Language.syntaxCheck;

+ 6 - 2
plugins/pyembed/pyembed.cpp

@@ -1691,6 +1691,8 @@ public:
     {
         throwUnexpected();
     }
+    virtual void enter() override {}
+    virtual void exit() override {}
     virtual void setActivityOptions(const IThorActivityContext *ctx) override
     {
         Python27EmbedContextBase::setActivityOptions(ctx);
@@ -1764,6 +1766,8 @@ public:
     {
         throwUnexpected();
     }
+    virtual void enter() override {}
+    virtual void exit() override {}
     virtual void callFunction()
     {
         result.setown(PyObject_CallObject(script, args));
@@ -1813,14 +1817,14 @@ extern DECL_EXPORT IEmbedContext* getEmbedContext()
     return new Python27EmbedContext;
 }
 
-extern DECL_EXPORT void syntaxCheck(size32_t & __lenResult,char * & __result,size32_t charsBody,const char * body, const char * parms)
+extern DECL_EXPORT void syntaxCheck(size32_t & __lenResult, char * & __result, const char *funcname, size32_t charsBody, const char * body, const char *argNames, const char *compilerOptions, const char *persistOptions)
 {
     StringBuffer result;
     try
     {
         checkThreadContext();
         Owned<Python27EmbedScriptContext> ctx = new Python27EmbedScriptContext(threadContext);
-        ctx->setargs(parms);
+        ctx->setargs(argNames);
         ctx->compileEmbeddedScript(charsBody, body);
     }
     catch (IException *E)

+ 1 - 1
plugins/pyembed/python.ecllib

@@ -17,7 +17,7 @@
 
 EXPORT Language := SERVICE : plugin('py2embed')
   integer getEmbedContext():cpp,pure,fold,namespace='py2embed',entrypoint='getEmbedContext',prototype='IEmbedContext* getEmbedContext()';
-  STRING syntaxCheck(UTF8 body, const VARSTRING params):cpp,pure,namespace='py2embed',entrypoint='syntaxCheck',fold;
+  STRING syntaxCheck(const varstring funcname, UTF8 body, const varstring argnames, const varstring compileOptions, const varstring persistOptions):cpp,pure,namespace='py2embed',entrypoint='syntaxCheck',fold;
 END;
 EXPORT getEmbedContext := Language.getEmbedContext;
 EXPORT syntaxCheck := Language.syntaxCheck;

+ 2 - 0
plugins/sqlite3/sqlite3.cpp

@@ -570,6 +570,8 @@ public:
     {
         throwUnexpected();
     }
+    virtual void enter() override {}
+    virtual void exit() override {}
 protected:
     sqlite3_value *getScalarResult()
     {

+ 0 - 2
plugins/sqlite3/sqlite3.ecllib

@@ -17,9 +17,7 @@
 
 EXPORT Language := SERVICE : plugin('sqlite3embed')
   boolean getEmbedContext():cpp,pure,namespace='sqlite3embed',entrypoint='getEmbedContext',prototype='IEmbedContext* getEmbedContext()';
-  boolean syntaxCheck(const varstring src):cpp,pure,namespace='sqlite3embed',entrypoint='syntaxCheck';
 END;
 EXPORT getEmbedContext := Language.getEmbedContext;
-EXPORT syntaxCheck := Language.syntaxCheck;
 EXPORT boolean supportsImport := false;
 EXPORT boolean supportsScript := true;

+ 0 - 3
plugins/v8embed/javascript.ecllib

@@ -17,9 +17,6 @@
 
 EXPORT Language := SERVICE : plugin('v8embed')
   integer getEmbedContext():cpp,pure,namespace='javascriptLanguageHelper',fold,entrypoint='getEmbedContext',prototype='IEmbedContext* getEmbedContext()';
-  boolean syntaxCheck(const varstring src):cpp,pure,namespace='javascriptLanguageHelper',entrypoint='syntaxCheck';
 END;
-EXPORT getEmbedContext := Language.getEmbedContext;
-EXPORT syntaxCheck := Language.syntaxCheck;
 EXPORT boolean supportsImport := false;
 EXPORT boolean supportsScript := true;

+ 2 - 0
plugins/v8embed/v8embed.cpp

@@ -896,6 +896,8 @@ public:
     {
         throwUnexpected();
     }
+    virtual void enter() override {}
+    virtual void exit() override {}
     virtual void importFunction(size32_t lenChars, const char *utf)
     {
         UNIMPLEMENTED; // Not sure if meaningful for js

+ 19 - 2
rtl/eclrtl/eclrtl.hpp

@@ -843,6 +843,24 @@ interface IEmbedFunctionContext : extends IInterface
     virtual void paramWriterCommit(IInterface *writer)=0;
     virtual void writeResult(IInterface *esdl, const char *esdlservice, const char *esdltype, IInterface *writer)=0;
     virtual void loadCompiledScript(size32_t len, const void *script) = 0;
+    // If reusing a context, need to call these before using/after using
+    virtual void enter() = 0;
+    virtual void exit() = 0;
+};
+
+class EmbedContextBlock
+{
+public:
+    EmbedContextBlock(IEmbedFunctionContext *_ctx) : ctx(_ctx)
+    {
+        ctx->enter();
+    }
+    ~EmbedContextBlock()
+    {
+        ctx->exit();
+    }
+private:
+    IEmbedFunctionContext *ctx;
 };
 
 interface IEmbedServiceContext : extends IInterface
@@ -850,7 +868,7 @@ interface IEmbedServiceContext : extends IInterface
     virtual IEmbedFunctionContext *createFunctionContext(const char *function) = 0;
 };
 
-enum EmbedFlags { EFembed = 1, EFimport = 2, EFnoreturn = 4, EFnoparams = 8 }; // For createFunctionContext flags
+enum EmbedFlags { EFembed = 1, EFimport = 2, EFnoreturn = 4, EFnoparams = 8, EFthreadlocal = 16 }; // For createFunctionContext flags
 
 interface ICodeContext;
 interface IThorActivityContext;
@@ -859,7 +877,6 @@ interface IEmbedContext : extends IInterface
     virtual IEmbedFunctionContext *createFunctionContext(unsigned flags, const char *options) = 0; // legacy
     virtual IEmbedFunctionContext *createFunctionContextEx(ICodeContext * ctx, const IThorActivityContext * activityCtx, unsigned flags, const char *options) = 0;
     virtual IEmbedServiceContext *createServiceContext(const char *service, unsigned flags, const char *options) = 0;
-    // MORE - add syntax checked here!
 };
 
 typedef IEmbedContext * (* GetEmbedContextFunction)();

+ 7 - 7
testing/regress/ecl/javaembed.ecl

@@ -20,26 +20,26 @@
 
 import java;
 string jcat(string a, string b) := EMBED(java)
-  public static String cat(String a, String b)
+  public static String jcat(String a, String b)
   {
     return a + b;
   }
 ENDEMBED;
 
 integer jadd(integer a, integer b) := EMBED(java)
-  public static int add(int a, int b)
+  public static int jadd(int a, int b)
   {
     return a + b;
   }
 ENDEMBED;
-integer jaddl(integer a, integer b) := EMBED(java)
-  public static long addL(int a, int b)
+integer jaddL(integer a, integer b) := EMBED(java)
+  public static long jaddL(int a, int b)
   {
     return a + b;
   }
 ENDEMBED;
-integer jaddi(integer a, integer b) := EMBED(java)
-  public static Integer addI(int a, int b)
+integer jaddI(integer a, integer b) := EMBED(java)
+  public static Integer jaddI(int a, int b)
   {
     return a + b;
   }
@@ -193,7 +193,7 @@ public class myClass {
       n = new NestedClass("nest2");
     }
   };
-  public static MyRecord transform(MyRecord in, int lim)
+  public static MyRecord testTransform(MyRecord in, int lim)
   {
     return new MyRecord(in.bfield, lim, in.dfield);
   }