Browse Source

HPCC-21224 Compile-time syntax check for R plugin

Also fixes a lurking bug in the demangler used when constant-folding external
functions.

Signed-off-by: Richard Chapman <rchapman@hpccsystems.com>
Richard Chapman 6 years ago
parent
commit
8ebe6fd4a5
4 changed files with 34 additions and 8 deletions
  1. 8 6
      ecl/hql/hqlutil.cpp
  2. 7 0
      ecl/regress/checkR.ecl
  3. 2 0
      plugins/Rembed/R.ecllib
  4. 17 2
      plugins/Rembed/Rembed.cpp

+ 8 - 6
ecl/hql/hqlutil.cpp

@@ -8187,8 +8187,10 @@ public:
         StringBuffer namespaceValue;
         getAttribute(body, namespaceAtom, namespaceValue);
         if (namespaceValue.length())
-            mangled.append("N").append(namespaceValue.length()).append(lookupRepeat(namespaceValue));
-
+        {
+            VStringBuffer nsName("%d%s", namespaceValue.length(), namespaceValue.str());
+            mangled.append("N").append(lookupRepeat(nsName));
+        }
         mangled.append(entrypoint.length()).append(entrypoint);
         if (namespaceValue.length())
             mangled.append("E");
@@ -8198,13 +8200,13 @@ public:
         mangleFunctionReturnType(mangledReturn, mangledReturnParameters, retType);
 
         if (functionBodyUsesContext(body))
-            mangled.append("P12ICodeContext");
+            mangled.append(lookupRepeat("P12ICodeContext"));
         else if (body->hasAttribute(globalContextAtom) )
-            mangled.append("P18IGlobalCodeContext");
+            mangled.append(lookupRepeat("P18IGlobalCodeContext"));
         else if (body->hasAttribute(userMatchFunctionAtom))
-            mangled.append("P12IMatchWalker");
+            mangled.append(lookupRepeat("P12IMatchWalker"));
         if (functionBodyIsActivity(body))
-            mangled.append("P20IThorActivityContext");
+            mangled.append(lookupRepeat("P20IThorActivityContext"));
 
         mangled.append(mangledReturnParameters);
 

+ 7 - 0
ecl/regress/checkR.ecl

@@ -0,0 +1,7 @@
+IMPORT R;
+
+integer testError(integer val) := EMBED(R)
+  print val
+ENDEMBED;
+
+testError(10)

+ 2 - 0
plugins/Rembed/R.ecllib

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

+ 17 - 2
plugins/Rembed/Rembed.cpp

@@ -1396,9 +1396,24 @@ extern DECL_EXPORT IEmbedContext* getEmbedContext()
     return new REmbedContext;
 }
 
-extern DECL_EXPORT bool syntaxCheck(const char *script)
+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)
 {
-    return true; // MORE
+    StringBuffer result;
+    try
+    {
+        Owned<REmbedFunctionContext> ctx =  new REmbedFunctionContext(*queryGlobalState()->R);
+        // MORE - could check supplied persistOptions are valid
+        StringBuffer text;
+        text.append(rtlUtf8Size(charsBody, body), body);
+        text.stripChar('\r');
+        Rcpp::ExpressionVector exp(text);
+    }
+    catch (std::exception &E)
+    {
+        result.append("Rembed: Parse error from R while checking embedded code"); // Unfortunately we don't get any info about the error position or nature, just "Parse error."
+    }
+    __lenResult = result.length();
+    __result = result.detach();
 }
 
 } // namespace