Browse Source

HPCC-13173 Constant folding should not call plugin functions

Only specifically whitelisted functions should be constant-folded.

Note that this does not prevent people from declaring their own services and
marking the functions in them as ,fold, so it does not close the security
loophole just yet - that will require some work on the code signing issue
HPCC-14185 - but this is a necessary step towards it (and a useful feature
in its own right).

Added ,NOFOLD attribute to override ,FOLD on individual functions within a
service.

Marked appropriate existing service functions foldable.

Refactored the external function call folding code to work around a compiler
bug on OSX.

Signed-off-by: Richard Chapman <rchapman@hpccsystems.com>
Richard Chapman 9 years ago
parent
commit
38c11654e4

+ 4 - 2
ecl/hql/hqlatoms.cpp

@@ -178,7 +178,7 @@ IAtom * fixedAtom;
 IAtom * flagAtom;
 IAtom * flagsAtom;
 IAtom * flatAtom;
-IAtom * _folded_Atom;
+IAtom * foldAtom;
 IAtom * formatAtom;
 IAtom * forwardAtom;
 IAtom * fullonlyAtom;
@@ -278,6 +278,7 @@ IAtom * newSetAtom;
 IAtom * _nlpParse_Atom;
 IAtom * noBoundCheckAtom;
 IAtom * noCaseAtom;
+IAtom * nofoldAtom;
 IAtom * _noHoist_Atom;
 IAtom * noLocalAtom;
 IAtom * _nonEmpty_Atom;
@@ -615,7 +616,7 @@ MODULE_INIT(INIT_PRIORITY_HQLATOM)
     MAKEATOM(flag);
     MAKEATOM(flags);
     MAKEATOM(flat);
-    MAKESYSATOM(folded);
+    MAKEATOM(fold);
     MAKEATOM(format);
     MAKEATOM(forward);
     fullonlyAtom = createLowerCaseAtom("full only");        // different to get the ECL regeneration correct..
@@ -716,6 +717,7 @@ MODULE_INIT(INIT_PRIORITY_HQLATOM)
     MAKESYSATOM(nlpParse);
     MAKEATOM(noBoundCheck);
     MAKEATOM(noCase);
+    MAKEATOM(nofold);
     MAKESYSATOM(noHoist);
     MAKEATOM(noLocal);
     MAKESYSATOM(nonEmpty);

+ 2 - 1
ecl/hql/hqlatoms.hpp

@@ -180,7 +180,7 @@ extern HQL_API IAtom * fixedAtom;
 extern HQL_API IAtom * flagAtom;
 extern HQL_API IAtom * flagsAtom;
 extern HQL_API IAtom * flatAtom;
-extern HQL_API IAtom * _folded_Atom;
+extern HQL_API IAtom * foldAtom;
 extern HQL_API IAtom * formatAtom;
 extern HQL_API IAtom * forwardAtom;
 extern HQL_API IAtom * fullonlyAtom;
@@ -280,6 +280,7 @@ extern HQL_API IAtom * newSetAtom;
 extern HQL_API IAtom * _nlpParse_Atom;
 extern HQL_API IAtom * noBoundCheckAtom;
 extern HQL_API IAtom * noCaseAtom;
+extern HQL_API IAtom * nofoldAtom;
 extern HQL_API IAtom * _noHoist_Atom;
 extern HQL_API IAtom * noLocalAtom;
 extern HQL_API IAtom * _nonEmpty_Atom;

+ 1 - 0
ecl/hql/hqlerrors.hpp

@@ -255,6 +255,7 @@
 #define ERR_TMPLT_NONPUREFUNC       2212 /* Can not fold non pure function */
 #define ERR_TMPLT_NONEXTERNCFUNC    2213 /* Can not fold non c function */
 #define ERR_TMPLT_EXTRAELIF         2214 /* #ELIF does not match a #IF */
+#define ERR_TMPLT_NOFOLDFUNC        2215 /* Can not fold function not marked as foldable */
 
 /* security */
 #define ERR_SECURITY_BADFORMAT      2216  /* Bad format for access token */

+ 58 - 38
ecl/hql/hqlfold.cpp

@@ -557,73 +557,84 @@ static bool isSimpleComparisonArg(IHqlExpression * expr)
  *       functions need to be declared with extern "C".
  *********************************************************/
  //MORE: This function never unloads the plugin dll - this may cause problems in the long run.
-IValue * foldExternalCall(IHqlExpression* expr, unsigned foldOptions, ITemplateContext *templateContext) 
+
+bool checkExternFoldable(IHqlExpression* expr, unsigned foldOptions, StringBuffer &library, StringBuffer &entry)
 {
     IHqlExpression * funcdef = expr->queryExternalDefinition();
-    if(!funcdef) 
-        return NULL;
+    if(!funcdef)
+        return false;
     IHqlExpression *body = funcdef->queryChild(0);
-    if(!body) 
-        return NULL;
+    if(!body)
+        return false;
 
     //Check all parameters are constant - saves dll load etc.
     unsigned numParam = expr->numChildren();
-    for(unsigned iparam = 0; iparam < numParam; iparam++) 
+    for(unsigned iparam = 0; iparam < numParam; iparam++)
     {
         if (!expr->queryChild(iparam)->queryValue())            //NB: Already folded...
-            return NULL;
+            return false;
     }
 
     IHqlExpression * formals = funcdef->queryChild(1);
     unsigned numArg = formals->numChildren();
-    if(numParam > numArg) 
+    if(numParam > numArg)
     {
         if (foldOptions & HFOthrowerror)
-            throw MakeStringException(ERR_PARAM_TOOMANY,"Too many parameters passed to function '%s': expected %d, given %d", 
+            throw MakeStringException(ERR_PARAM_TOOMANY,"Too many parameters passed to function '%s': expected %d, given %d",
                                       str(expr->queryName()), numParam, numArg);
-        return NULL;
+        return false;
     }
-    else if(numParam < numArg) 
+    else if(numParam < numArg)
     {
         if (foldOptions & HFOthrowerror)
-            throw MakeStringException(ERR_PARAM_TOOFEW,"Not enough parameters passed to function '%s': expected %d, given %d", 
+            throw MakeStringException(ERR_PARAM_TOOFEW,"Not enough parameters passed to function '%s': expected %d, given %d",
                                       str(expr->queryName()), numParam, numArg);
-        return NULL;
+        return false;
     }
 
-    StringBuffer entry;
     StringBuffer mangledEntry;
-    StringBuffer lib;
     getAttribute(body, entrypointAtom, entry);
-    getAttribute(body, libraryAtom, lib);
-    if (!lib.length())
-        getAttribute(body, pluginAtom, lib);
+    getAttribute(body, libraryAtom, library);
+    if (!library.length())
+        getAttribute(body, pluginAtom, library);
     if(entry.length() == 0)
     {
         if (foldOptions & HFOthrowerror)
             throw MakeStringException(ERR_SVC_NOENTRYPOINT,"Missing entrypoint for function folding");
-        return NULL;
+        return false;
     }
-    if (lib.length() == 0) 
+    if (library.length() == 0)
     {
         if (foldOptions & HFOthrowerror)
             throw MakeStringException(ERR_SVC_NOLIBRARY,"Missing library for function folding");
-        return NULL;
+        return false;
     }
 
-    if (!pathExtension(lib))
+    if (!pathExtension(library))
     {
-        lib.insert(0, SharedObjectPrefix);
-        ensureFileExtension(lib, SharedObjectExtension);
+        library.insert(0, SharedObjectPrefix);
+        ensureFileExtension(library, SharedObjectExtension);
     }
 
-    const char * entrypoint = entry.str();
-    const char * library = lib.str();
+    if (!body->hasAttribute(foldAtom) || body->hasAttribute(nofoldAtom))
+    {
+        if (foldOptions & HFOthrowerror)
+            throw MakeStringException(ERR_TMPLT_NOFOLDFUNC, "%s does not have FOLD specified, can't constant fold it", str(expr->queryName()));
+        return false;
+    }
     if(!body->hasAttribute(pureAtom) && !body->hasAttribute(templateAtom) && !(foldOptions & (HFOfoldimpure|HFOforcefold)))
     {
         if (foldOptions & HFOthrowerror)
-            throw MakeStringException(ERR_TMPLT_NONPUREFUNC, "%s/%s is not a pure function, can't constant fold it", library, entrypoint);
-        return NULL;
+            throw MakeStringException(ERR_TMPLT_NONPUREFUNC, "%s/%s is not a pure function, can't constant fold it", library.str(), entry.str());
+        return false;
+    }
+
+    if(body->hasAttribute(contextAtom) || body->hasAttribute(globalContextAtom) ||
+       body->hasAttribute(gctxmethodAtom) || body->hasAttribute(ctxmethodAtom) || body->hasAttribute(omethodAtom))
+    {
+        if (foldOptions & HFOthrowerror)
+            throw MakeStringException(ERR_TMPLT_NONEXTERNCFUNC, "%s/%s requires a runtime context to be executed, can't constant fold it", library.str(), entry.str());
+        return false;
     }
 
     if(!body->hasAttribute(cAtom))
@@ -631,19 +642,18 @@ IValue * foldExternalCall(IHqlExpression* expr, unsigned foldOptions, ITemplateC
         if (!createMangledFunctionName(mangledEntry, funcdef))
         {
             if (foldOptions & HFOthrowerror)
-                throw MakeStringException(ERR_TMPLT_NONEXTERNCFUNC, "%s/%s is not declared as extern C, can't constant fold it", library, entrypoint);
-            return NULL;
+                throw MakeStringException(ERR_TMPLT_NONEXTERNCFUNC, "%s/%s is not declared as extern C, can't constant fold it", library.str(), entry.str());
+            return false;
         }
-        entrypoint = mangledEntry.str();
+        entry.set(mangledEntry);
     }
+    return true;
+}
 
-    if(body->hasAttribute(contextAtom) || body->hasAttribute(globalContextAtom) ||
-       body->hasAttribute(gctxmethodAtom) || body->hasAttribute(ctxmethodAtom) || body->hasAttribute(omethodAtom))
-    {
-        if (foldOptions & HFOthrowerror)
-            throw MakeStringException(ERR_TMPLT_NONEXTERNCFUNC, "%s/%s requires a runtime context to be executed, can't constant fold it", library, entrypoint);
-        return NULL;
-    }
+IValue * doFoldExternalCall(IHqlExpression* expr, unsigned foldOptions, ITemplateContext *templateContext, const char *library, const char *entrypoint)
+{
+    IHqlExpression * funcdef = expr->queryExternalDefinition();
+    IHqlExpression *body = funcdef->queryChild(0);
 
     // Get the handle to the library and procedure.
     HINSTANCE hDLL=LoadSharedObject(library, false, false);
@@ -750,6 +760,8 @@ IValue * foldExternalCall(IHqlExpression* expr, unsigned foldOptions, ITemplateC
     }
 
     // process all the parameters passed in
+    unsigned numParam = expr->numChildren();
+    IHqlExpression * formals = funcdef->queryChild(1);
     for(unsigned i = 0; i < numParam; i++) 
     {
         IHqlExpression * curParam = expr->queryChild(i);            //NB: Already folded...
@@ -1288,6 +1300,14 @@ IValue * foldExternalCall(IHqlExpression* expr, unsigned foldOptions, ITemplateC
     return result;
 }
 
+IValue * foldExternalCall(IHqlExpression* expr, unsigned foldOptions, ITemplateContext *templateContext)
+{
+    StringBuffer library;
+    StringBuffer entry;
+    if (!checkExternFoldable(expr, foldOptions, library, entry))
+        return NULL;
+    return doFoldExternalCall(expr, foldOptions, templateContext, library.str(), entry.str());
+}
 //------------------------------------------------------------------------------------------
 
 // optimize ((a BAND b) <> 0) OR ((a BAND c) <> 0) to ((a BAND (b BOR c)) <> 0)

+ 1 - 1
ecl/hql/hqlgram2.cpp

@@ -3711,7 +3711,7 @@ IHqlExpression* HqlGram::checkServiceDef(IHqlScope* serviceScope,IIdAtom * name,
                 bcdApi = true;
                 checkSvcAttrNoValue(attr, errpos);
             }
-            else if (name == pureAtom || name == templateAtom || name == volatileAtom || name == onceAtom || name == actionAtom)
+            else if (name == pureAtom || name == templateAtom || name == volatileAtom || name == onceAtom || name == actionAtom || name == foldAtom || name == nofoldAtom)
             {
                 checkSvcAttrNoValue(attr, errpos);
             }

+ 1 - 1
ecl/hqlcpp/hqlcppsys.ecl

@@ -29,7 +29,7 @@ const char * cppSystemText[]  = {
     "shared IOutputMetaData := boolean;",
     "shared size32_t := unsigned4;",
     
-    "export InternalCppService := SERVICE",
+    "export InternalCppService := SERVICE:fold",
     //  searchTableStringN(unsigned4 num, string table, string search) : library='eclrtl';
     //  memcpy(void * target, void * src, unsigned len);
     //internal code generation helper functions...

+ 1 - 1
plugins/examplelib/examplelib.cpp

@@ -27,7 +27,7 @@ static const char * HoleDefinition = NULL;
 
 static const char * EclDefinition =
 "export ExampleLib := SERVICE\n"
-"  string EchoString(const string src) : c, pure,entrypoint='elEchoString'; \n"
+"  string EchoString(const string src) : c, pure,fold,entrypoint='elEchoString'; \n"
 "END;";
 
 EXAMPLELIB_API bool getECLPluginDefinition(ECLPluginDefinitionBlock *pb) 

+ 1 - 1
plugins/proxies/lib_metaphone.ecllib

@@ -17,7 +17,7 @@
 
 /* Proxy plugin definition for dmetaphone library version DMETAPHONE 1.1.05 */
 
-export MetaphoneLib := SERVICE : plugin('dmetaphone')
+export MetaphoneLib := SERVICE : plugin('dmetaphone'),fold
   string DMetaphone1(const string src) : c,pure,entrypoint='mpDMetaphone1';
   string DMetaphone2(const string src) : c,pure,entrypoint='mpDMetaphone2';
   string DMetaphoneBoth(const string src) : c,pure,entrypoint='mpDMetaphoneBoth';

+ 1 - 1
plugins/proxies/lib_metaphone3.ecllib

@@ -17,7 +17,7 @@
 
 /* Proxy plugin definition for (EE only) metaphone3 library */
 
-export Metaphone3Lib := SERVICE : plugin('metaphone3')
+export Metaphone3Lib := SERVICE : plugin('metaphone3'),fold
   string Metaphone3(const string src, boolean encodeVowels=true, boolean encodeExact=true, unsigned4 maxLength=0) : c,pure,entrypoint='m3DMetaphone1';
   string Metaphone3Alt(const string src, boolean encodeVowels=true, boolean encodeExact=true, unsigned4 maxLength=0) : c,pure,entrypoint='m3DMetaphone2';
   string Metaphone3Both(const string src, boolean encodeVowels=true, boolean encodeExact=true, unsigned4 maxLength=0) : c,pure,entrypoint='m3DMetaphoneBoth';

+ 1 - 1
plugins/proxies/lib_saltlib.ecllib

@@ -22,7 +22,7 @@ EXPORT GeohashDecodeResultRecord := RECORD
     REAL latitude_err;
     REAL longitude_err;
 END;
-export SaltLib := SERVICE : plugin('saltlib')
+export SaltLib := SERVICE : plugin('saltlib'),fold
   boolean UnicodeLocaleWithinEditN(const unicode left, const unicode right, unsigned4 distance,  const varstring localename) : c, pure,entrypoint='ulUnicodeLocaleWithinEditN', hole;
   boolean UnicodeWithinEditN(const unicode left, const unicode right, unsigned4 distance) : c, pure,entrypoint='ulUnicodeWithinEditN', hole;
   unsigned4 UnicodeLocaleHyphenMatch(const unicode left, const unicode right, unsigned4 minlen,  const varstring localename) : c, pure,entrypoint='ulUnicodeLocaleHyphenMatch', hole;

+ 5 - 3
plugins/stringlib/stringlib.cpp

@@ -41,7 +41,7 @@ static const char * compatibleVersions[] = {
 #define STRINGLIB_VERSION "STRINGLIB 1.1.14"
 
 static const char * EclDefinition =
-"export StringLib := SERVICE\n"
+"export StringLib := SERVICE:fold\n"
 "  string StringFilterOut(const string src, const string _within) : c, pure,entrypoint='slStringFilterOut'; \n"
 "  string StringFilter(const string src, const string _within) : c, pure,entrypoint='slStringFilter'; \n"
 "  string StringSubstituteOut(const string src, const string _within, const string _newchar) : c, pure,entrypoint='slStringSubsOut'; \n"
@@ -49,11 +49,13 @@ static const char * EclDefinition =
 "  string StringRepad(const string src, unsigned4 size) : c, pure,entrypoint='slStringRepad'; \n"
 "  string StringTranslate(const string src, const string _within, const string _mapping) : c, pure,entrypoint='slStringTranslate'; \n"
 "  unsigned integer4 StringFind(const string src, const string tofind, unsigned4 instance ) : c, pure,entrypoint='slStringFind'; \n"
-"  unsigned integer4 StringUnboundedUnsafeFind(const string src, const string tofind ) : c, pure,entrypoint='slStringFind2'; \n"
+"  unsigned integer4 StringUnboundedUnsafeFind(const string src, const string tofind ) : c,pure,nofold,entrypoint='slStringFind2'; \n"
 "  unsigned integer4 StringFindCount(const string src, const string tofind) : c, pure,entrypoint='slStringFindCount'; \n"
 "  unsigned integer4 EbcdicStringFind(const ebcdic string src, const ebcdic string tofind , unsigned4 instance ) : c,pure,entrypoint='slStringFind'; \n"
-"  unsigned integer4 EbcdicStringUnboundedUnsafeFind(const ebcdic string src, const ebcdic string tofind ) : c,pure,entrypoint='slStringFind2'; \n"
+"  unsigned integer4 EbcdicStringUnboundedUnsafeFind(const ebcdic string src, const ebcdic string tofind ) : c,pure,nofold,entrypoint='slStringFind2'; \n"
 "  string StringExtract(const string src, unsigned4 instance) : c,pure,entrypoint='slStringExtract'; \n"
+// NOTE - the next 2 are foldable but not pure, meaning it will only be folded if found in a #IF or similar
+// This is because you usually want them to be executed at runtime
 "  string8 GetDateYYYYMMDD() : c,once,entrypoint='slGetDateYYYYMMDD2';\n"
 "  varstring GetBuildInfo() : c,once,entrypoint='slGetBuildInfo';\n"
 "  string Data2String(const data src) : c,pure,entrypoint='slData2String';\n"

+ 6 - 4
plugins/timelib/timelib.cpp

@@ -48,7 +48,7 @@ static const char * EclDefinition =
 "  UNSIGNED4 startDate; \n"
 "  UNSIGNED4 endDate; \n"
 "END;"
-"EXPORT TimeLib := SERVICE\n"
+"EXPORT TimeLib := SERVICE : fold\n"
 "  INTEGER8 SecondsFromParts(integer2 year, unsigned1 month, unsigned1 day, unsigned1 hour, unsigned1 minute, unsigned1 second, boolean is_local_time) : c,pure,entrypoint='tlSecondsFromParts'; \n"
 "  TRANSFORM(TMPartsRec) SecondsToParts(INTEGER8 seconds) : c,pure,entrypoint='tlSecondsToParts'; \n"
 "  UNSIGNED2 GetDayOfYear(INTEGER2 year, UNSIGNED1 month, UNSIGNED1 day) : c,pure,entrypoint='tlGetDayOfYear'; \n"
@@ -63,13 +63,15 @@ static const char * EclDefinition =
 "  INTEGER4 AdjustSeconds(INTEGER8 seconds, INTEGER2 year_delta, INTEGER4 month_delta, INTEGER4 day_delta, INTEGER2 hour_delta, INTEGER4 minute_delta, INTEGER4 second_delta) : c,pure,entrypoint='tlAdjustSeconds'; \n"
 "  UNSIGNED4 AdjustCalendar(UNSIGNED4 date, INTEGER2 year_delta, INTEGER4 month_delta, INTEGER4 day_delta) : c,pure,entrypoint='tlAdjustCalendar'; \n"
 "  BOOLEAN IsLocalDaylightSavingsInEffect() : c,pure,entrypoint='tlIsLocalDaylightSavingsInEffect'; \n"
-"  INTEGER4 LocalTimeZoneOffset() : c,pure,entrypoint='tlLocalTimeZoneOffset'; \n"
+"  UNSIGNED4 GetLastDayOfMonth(UNSIGNED4 date) : c,pure,entrypoint='tlGetLastDayOfMonth'; \n"
+"  TRANSFORM(TMDateRangeRec) DatesForWeek(UNSIGNED4 date) : c,pure,entrypoint='tlDatesForWeek'; \n"
+// NOTE - the next 5 are foldable but not pure, meaning it will only be folded if found in a #IF or similar
+// This is because you usually want them to be executed at runtime
+ "  INTEGER4 LocalTimeZoneOffset() : c,once,entrypoint='tlLocalTimeZoneOffset'; \n"
 "  UNSIGNED4 CurrentDate(BOOLEAN in_local_time) : c,once,entrypoint='tlCurrentDate'; \n"
 "  UNSIGNED4 CurrentTime(BOOLEAN in_local_time) : c,entrypoint='tlCurrentTime'; \n"
 "  INTEGER8 CurrentSeconds(BOOLEAN in_local_time) : c,entrypoint='tlCurrentSeconds'; \n"
 "  INTEGER8 CurrentTimestamp(BOOLEAN in_local_time) : c,entrypoint='tlCurrentTimestamp'; \n"
-"  UNSIGNED4 GetLastDayOfMonth(UNSIGNED4 date) : c,pure,entrypoint='tlGetLastDayOfMonth'; \n"
-"  TRANSFORM(TMDateRangeRec) DatesForWeek(UNSIGNED4 date) : c,pure,entrypoint='tlDatesForWeek'; \n"
 "END;";
 
 TIMELIB_API bool getECLPluginDefinition(ECLPluginDefinitionBlock *pb)

+ 1 - 1
plugins/unicodelib/unicodelib.cpp

@@ -38,7 +38,7 @@ static UChar const u16query = '?';
 static UChar const u16space = ' ';
 
 static const char * EclDefinition =
-"export UnicodeLib := SERVICE\n"
+"export UnicodeLib := SERVICE:fold\n"
 "  unicode UnicodeFilterOut(const unicode src, const unicode _within) : c, pure,entrypoint='ulUnicodeFilterOut'; \n"
 "  unicode UnicodeFilter(const unicode src, const unicode _within) : c, pure,entrypoint='ulUnicodeFilter'; \n"
 "  unicode UnicodeSubstituteOut(const unicode src, const unicode _within, const unicode _newchar) : c, pure,entrypoint='ulUnicodeSubsOut'; \n"

+ 15 - 21
testing/regress/ecl/key/stringlib3.xml

@@ -20,53 +20,47 @@
  <Row><Result_7>StringLib.EbcdicStringFind(const ebcdic string src, const ebcdic string tofind , unsigned4 instance )</Result_7></Row>
 </Dataset>
 <Dataset name='Result 8'>
- <Row><Result_8>Str.UnboundedUnsafeFind(const string src, const string tofind )</Result_8></Row>
+ <Row><Result_8>Str.Extract(const string src, unsigned4 instance)</Result_8></Row>
 </Dataset>
 <Dataset name='Result 9'>
- <Row><Result_9>StringLib.EbcdicStringUnboundedUnsafeFind(const ebcdic string src, const ebcdic string tofind )</Result_9></Row>
+ <Row><Result_9>StringLib.GetDateYYYYMMDD()</Result_9></Row>
 </Dataset>
 <Dataset name='Result 10'>
- <Row><Result_10>Str.Extract(const string src, unsigned4 instance)</Result_10></Row>
+ <Row><Result_10>StringLib.GetBuildInfo()</Result_10></Row>
 </Dataset>
 <Dataset name='Result 11'>
- <Row><Result_11>StringLib.GetDateYYYYMMDD()</Result_11></Row>
+ <Row><Result_11>StringLib.Data2String(const data src)</Result_11></Row>
 </Dataset>
 <Dataset name='Result 12'>
- <Row><Result_12>StringLib.GetBuildInfo()</Result_12></Row>
+ <Row><Result_12>StringLib.String2Data(const string src)</Result_12></Row>
 </Dataset>
 <Dataset name='Result 13'>
- <Row><Result_13>StringLib.Data2String(const data src)</Result_13></Row>
+ <Row><Result_13>Str.ToLowerCase(const string src)</Result_13></Row>
 </Dataset>
 <Dataset name='Result 14'>
- <Row><Result_14>StringLib.String2Data(const string src)</Result_14></Row>
+ <Row><Result_14>Str.ToUpperCase(const string src)</Result_14></Row>
 </Dataset>
 <Dataset name='Result 15'>
- <Row><Result_15>Str.ToLowerCase(const string src)</Result_15></Row>
+ <Row><Result_15>Str.ToCapitalCase(const string src)</Result_15></Row>
 </Dataset>
 <Dataset name='Result 16'>
- <Row><Result_16>Str.ToUpperCase(const string src)</Result_16></Row>
+ <Row><Result_16>Str.CompareIgnoreCase(const string src1, string src2)</Result_16></Row>
 </Dataset>
 <Dataset name='Result 17'>
- <Row><Result_17>Str.ToCapitalCase(const string src)</Result_17></Row>
+ <Row><Result_17>Str.Reverse(const string src)</Result_17></Row>
 </Dataset>
 <Dataset name='Result 18'>
- <Row><Result_18>Str.CompareIgnoreCase(const string src1, string src2)</Result_18></Row>
+ <Row><Result_18>Str.FindReplace(const string src, const string stok, const string rtok)</Result_18></Row>
 </Dataset>
 <Dataset name='Result 19'>
- <Row><Result_19>Str.Reverse(const string src)</Result_19></Row>
+ <Row><Result_19>Str.CleanSpaces(const string src)</Result_19></Row>
 </Dataset>
 <Dataset name='Result 20'>
- <Row><Result_20>Str.FindReplace(const string src, const string stok, const string rtok)</Result_20></Row>
+ <Row><Result_20>Str.WildMatch(const string src, const string _pattern, boolean _noCase)</Result_20></Row>
 </Dataset>
 <Dataset name='Result 21'>
- <Row><Result_21>Str.CleanSpaces(const string src)</Result_21></Row>
+ <Row><Result_21>Str.Contains(const string src, const string _pattern, boolean _noCase)</Result_21></Row>
 </Dataset>
 <Dataset name='Result 22'>
- <Row><Result_22>Str.WildMatch(const string src, const string _pattern, boolean _noCase)</Result_22></Row>
-</Dataset>
-<Dataset name='Result 23'>
- <Row><Result_23>Str.Contains(const string src, const string _pattern, boolean _noCase)</Result_23></Row>
-</Dataset>
-<Dataset name='Result 24'>
- <Row><Result_24>Str.ExtractMultiple(commastring, bitmap)</Result_24></Row>
+ <Row><Result_22>Str.ExtractMultiple(commastring, bitmap)</Result_22></Row>
 </Dataset>

+ 0 - 12
testing/regress/ecl/stringlib3.ecl

@@ -68,18 +68,6 @@ output('StringLib.EbcdicStringFind(const ebcdic string src, const ebcdic string
 output('FAILED StringLib.EbcdicStringFind(const ebcdic string src, const ebcdic string tofind , unsigned4 instance )'); 
 #END
 
-#IF(lib_stringlib.StringLib.StringUnboundedUnsafeFind('ABCDE', 'BC') = 2)
-output('Str.UnboundedUnsafeFind(const string src, const string tofind )');                              
-#ELSE
-output('FAILED Str.UnboundedUnsafeFind(const string src, const string tofind )');                               
-#END
-
-#IF(lib_stringlib.StringLib.EbcdicStringUnboundedUnsafeFind(a, b) = 2)
-output('StringLib.EbcdicStringUnboundedUnsafeFind(const ebcdic string src, const ebcdic string tofind )');          
-#ELSE
-output('FAILED StringLib.EbcdicStringUnboundedUnsafeFind(const ebcdic string src, const ebcdic string tofind )');           
-#END
-
 #IF(Str.Extract('AB,CD,E', 2) = 'CD')
 output('Str.Extract(const string src, unsigned4 instance)');                                
 #ELSE