Explorar o código

HPCC-8720 Add support for SET OF simpletype to embed plugins

Add support for set parameters/results.

V8 Javascript ans R support for set parameters now complete.

Also fix handling of set of unsigned, and address some build issues.

Signed-off-by: Richard Chapman <rchapman@hpccsystems.com>
Richard Chapman %!s(int64=12) %!d(string=hai) anos
pai
achega
39e18a0cbe

+ 310 - 0
cmake_modules/FindJNI.cmake

@@ -0,0 +1,310 @@
+# - Find JNI java libraries.
+# This module finds if Java is installed and determines where the
+# include files and libraries are. It also determines what the name of
+# the library is. This code sets the following variables:
+#
+#  JNI_INCLUDE_DIRS      = the include dirs to use
+#  JNI_LIBRARIES         = the libraries to use
+#  JNI_FOUND             = TRUE if JNI headers and libraries were found.
+#  JAVA_AWT_LIBRARY      = the path to the jawt library
+#  JAVA_JVM_LIBRARY      = the path to the jvm library
+#  JAVA_INCLUDE_PATH     = the include path to jni.h
+#  JAVA_INCLUDE_PATH2    = the include path to jni_md.h
+#  JAVA_AWT_INCLUDE_PATH = the include path to jawt.h
+#
+
+# Derived from the version included with CMake 2.8.9, with fixes for
+# the location that openjdk installs to on Ubuntu 12.10
+#=============================================================================
+#CMake - Cross Platform Makefile Generator
+#Copyright 2000-2011 Kitware, Inc., Insight Software Consortium
+#All rights reserved.
+#
+#Redistribution and use in source and binary forms, with or without
+#modification, are permitted provided that the following conditions
+#are met:
+#
+#* Redistributions of source code must retain the above copyright
+#  notice, this list of conditions and the following disclaimer.
+#
+#* Redistributions in binary form must reproduce the above copyright
+#  notice, this list of conditions and the following disclaimer in the
+#  documentation and/or other materials provided with the distribution.
+#
+#* Neither the names of Kitware, Inc., the Insight Software Consortium,
+#  nor the names of their contributors may be used to endorse or promote
+#  products derived from this software without specific prior written
+#  permission.
+#
+#THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+#"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+#LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+#A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+#HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+#SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+#LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+#DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+#THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+#(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+#OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+#------------------------------------------------------------------------------
+#
+#The above copyright and license notice applies to distributions of
+#CMake in source and binary form.  Some source files contain additional
+#notices of original copyright by their contributors; see each source
+#for details.  Third-party software packages supplied with CMake under
+#compatible licenses provide their own copyright notices documented in
+#corresponding subdirectories.
+#
+#------------------------------------------------------------------------------
+#
+#CMake was initially developed by Kitware with the following sponsorship:
+#
+# * National Library of Medicine at the National Institutes of Health
+#   as part of the Insight Segmentation and Registration Toolkit (ITK).
+#
+# * US National Labs (Los Alamos, Livermore, Sandia) ASC Parallel
+#   Visualization Initiative.
+#
+# * National Alliance for Medical Image Computing (NAMIC) is funded by the
+#   National Institutes of Health through the NIH Roadmap for Medical Research,
+#   Grant U54 EB005149.
+#
+# * Kitware, Inc.
+#=============================================================================
+
+# Expand {libarch} occurences to java_libarch subdirectory(-ies) and set ${_var}
+MACRO(java_append_library_directories _var)
+    # Determine java arch-specific library subdir
+    # Mostly based on openjdk/jdk/make/common/shared/Platform.gmk as of openjdk
+    # 1.6.0_18 + icedtea patches. However, it would be much better to base the
+    # guess on the first part of the GNU config.guess platform triplet.
+    IF(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
+        SET(_java_libarch "amd64")
+    ELSEIF(CMAKE_SYSTEM_PROCESSOR MATCHES "^i.86$")
+        SET(_java_libarch "i386")
+    ELSEIF(CMAKE_SYSTEM_PROCESSOR MATCHES "^alpha")
+        SET(_java_libarch "alpha")
+    ELSEIF(CMAKE_SYSTEM_PROCESSOR MATCHES "^arm")
+        # Subdir is "arm" for both big-endian (arm) and little-endian (armel).
+        SET(_java_libarch "arm")
+    ELSEIF(CMAKE_SYSTEM_PROCESSOR MATCHES "^mips")
+        # mips* machines are bi-endian mostly so processor does not tell
+        # endianess of the underlying system.
+        SET(_java_libarch "${CMAKE_SYSTEM_PROCESSOR}" "mips" "mipsel" "mipseb")
+    ELSEIF(CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)64")
+        SET(_java_libarch "ppc64")
+    ELSEIF(CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)")
+        SET(_java_libarch "ppc")
+    ELSEIF(CMAKE_SYSTEM_PROCESSOR MATCHES "^sparc")
+        # Both flavours can run on the same processor
+        SET(_java_libarch "${CMAKE_SYSTEM_PROCESSOR}" "sparc" "sparcv9")
+    ELSEIF(CMAKE_SYSTEM_PROCESSOR MATCHES "^(parisc|hppa)")
+        SET(_java_libarch "parisc" "parisc64")
+    ELSEIF(CMAKE_SYSTEM_PROCESSOR MATCHES "^s390")
+        # s390 binaries can run on s390x machines
+        SET(_java_libarch "${CMAKE_SYSTEM_PROCESSOR}" "s390" "s390x")
+    ELSEIF(CMAKE_SYSTEM_PROCESSOR MATCHES "^sh")
+        SET(_java_libarch "sh")
+    ELSE(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
+        SET(_java_libarch "${CMAKE_SYSTEM_PROCESSOR}")
+    ENDIF(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
+
+    # Append default list architectures if CMAKE_SYSTEM_PROCESSOR was empty or
+    # system is non-Linux (where the code above has not been well tested)
+    IF(NOT _java_libarch OR NOT (CMAKE_SYSTEM_NAME MATCHES "Linux"))
+        LIST(APPEND _java_libarch "i386" "amd64" "ppc")
+    ENDIF(NOT _java_libarch OR NOT (CMAKE_SYSTEM_NAME MATCHES "Linux"))
+
+    # Sometimes ${CMAKE_SYSTEM_PROCESSOR} is added to the list to prefer
+    # current value to a hardcoded list. Remove possible duplicates.
+    LIST(REMOVE_DUPLICATES _java_libarch)
+
+    FOREACH(_path ${ARGN})
+        IF(_path MATCHES "{libarch}")
+            FOREACH(_libarch ${_java_libarch})
+                STRING(REPLACE "{libarch}" "${_libarch}" _newpath "${_path}")
+                LIST(APPEND ${_var} "${_newpath}")
+            ENDFOREACH(_libarch)
+        ELSE(_path MATCHES "{libarch}")
+            LIST(APPEND ${_var} "${_path}")
+        ENDIF(_path MATCHES "{libarch}")
+    ENDFOREACH(_path)
+ENDMACRO(java_append_library_directories)
+
+GET_FILENAME_COMPONENT(java_install_version
+  "[HKEY_LOCAL_MACHINE\\SOFTWARE\\JavaSoft\\Java Development Kit;CurrentVersion]" NAME)
+
+SET(JAVA_AWT_LIBRARY_DIRECTORIES
+  "[HKEY_LOCAL_MACHINE\\SOFTWARE\\JavaSoft\\Java Development Kit\\1.4;JavaHome]/lib"
+  "[HKEY_LOCAL_MACHINE\\SOFTWARE\\JavaSoft\\Java Development Kit\\1.3;JavaHome]/lib"
+  "[HKEY_LOCAL_MACHINE\\SOFTWARE\\JavaSoft\\Java Development Kit\\${java_install_version};JavaHome]/lib"
+  )
+
+FILE(TO_CMAKE_PATH "$ENV{JAVA_HOME}" _JAVA_HOME)
+
+JAVA_APPEND_LIBRARY_DIRECTORIES(JAVA_AWT_LIBRARY_DIRECTORIES
+  ${_JAVA_HOME}/jre/lib/{libarch}
+  ${_JAVA_HOME}/jre/lib
+  ${_JAVA_HOME}/lib
+  ${_JAVA_HOME}
+  /usr/lib
+  /usr/local/lib
+  /usr/lib/jvm/java/lib
+  /usr/lib/java/jre/lib/{libarch}
+  /usr/lib/jvm/jre/lib/{libarch}
+  /usr/local/lib/java/jre/lib/{libarch}
+  /usr/local/share/java/jre/lib/{libarch}
+  /usr/lib/j2sdk1.4-sun/jre/lib/{libarch}
+  /usr/lib/j2sdk1.5-sun/jre/lib/{libarch}
+  /opt/sun-jdk-1.5.0.04/jre/lib/{libarch}
+  /usr/lib/jvm/java-6-sun/jre/lib/{libarch}
+  /usr/lib/jvm/java-1.5.0-sun/jre/lib/{libarch}
+  /usr/lib/jvm/java-6-sun-1.6.0.00/jre/lib/{libarch}       # can this one be removed according to #8821 ? Alex
+  /usr/lib/jvm/java-6-openjdk-{libarch}/jre/lib/{libarch}  # Ubuntu 12.10 location
+  /usr/lib/jvm/java-6-openjdk/jre/lib/{libarch}
+  /usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0/jre/lib/{libarch}        # fedora
+  # Debian specific paths for default JVM
+  /usr/lib/jvm/default-java/jre/lib/{libarch}
+  /usr/lib/jvm/default-java/jre/lib
+  /usr/lib/jvm/default-java/lib
+  )
+
+SET(JAVA_JVM_LIBRARY_DIRECTORIES)
+FOREACH(dir ${JAVA_AWT_LIBRARY_DIRECTORIES})
+  SET(JAVA_JVM_LIBRARY_DIRECTORIES
+    ${JAVA_JVM_LIBRARY_DIRECTORIES}
+    "${dir}"
+    "${dir}/client"
+    "${dir}/server"
+    )
+ENDFOREACH(dir)
+
+
+SET(JAVA_AWT_INCLUDE_DIRECTORIES
+  "[HKEY_LOCAL_MACHINE\\SOFTWARE\\JavaSoft\\Java Development Kit\\1.4;JavaHome]/include"
+  "[HKEY_LOCAL_MACHINE\\SOFTWARE\\JavaSoft\\Java Development Kit\\1.3;JavaHome]/include"
+  "[HKEY_LOCAL_MACHINE\\SOFTWARE\\JavaSoft\\Java Development Kit\\${java_install_version};JavaHome]/include"
+  ${_JAVA_HOME}/include
+  /usr/include
+  /usr/local/include
+  /usr/lib/java/include
+  /usr/local/lib/java/include
+  /usr/lib/jvm/java/include
+  /usr/lib/jvm/java-6-sun/include
+  /usr/lib/jvm/java-1.5.0-sun/include
+  /usr/lib/jvm/java-6-sun-1.6.0.00/include       # can this one be removed according to #8821 ? Alex
+  /usr/lib/jvm/java-6-openjdk-amd64/jre/lib/{libarch}  # Ubuntu 12.10 location
+  /usr/lib/jvm/java-6-openjdk/include
+  /usr/local/share/java/include
+  /usr/lib/j2sdk1.4-sun/include
+  /usr/lib/j2sdk1.5-sun/include
+  /opt/sun-jdk-1.5.0.04/include
+  # Debian specific path for default JVM
+  /usr/lib/jvm/default-java/include
+  )
+
+FOREACH(JAVA_PROG "${JAVA_RUNTIME}" "${JAVA_COMPILE}" "${JAVA_ARCHIVE}")
+  GET_FILENAME_COMPONENT(jpath "${JAVA_PROG}" PATH)
+  FOREACH(JAVA_INC_PATH ../include ../java/include ../share/java/include)
+    IF(EXISTS ${jpath}/${JAVA_INC_PATH})
+      SET(JAVA_AWT_INCLUDE_DIRECTORIES ${JAVA_AWT_INCLUDE_DIRECTORIES} "${jpath}/${JAVA_INC_PATH}")
+    ENDIF(EXISTS ${jpath}/${JAVA_INC_PATH})
+  ENDFOREACH(JAVA_INC_PATH)
+  FOREACH(JAVA_LIB_PATH
+    ../lib ../jre/lib ../jre/lib/i386
+    ../java/lib ../java/jre/lib ../java/jre/lib/i386
+    ../share/java/lib ../share/java/jre/lib ../share/java/jre/lib/i386)
+    IF(EXISTS ${jpath}/${JAVA_LIB_PATH})
+      SET(JAVA_AWT_LIBRARY_DIRECTORIES ${JAVA_AWT_LIBRARY_DIRECTORIES} "${jpath}/${JAVA_LIB_PATH}")
+    ENDIF(EXISTS ${jpath}/${JAVA_LIB_PATH})
+  ENDFOREACH(JAVA_LIB_PATH)
+ENDFOREACH(JAVA_PROG)
+
+IF(APPLE)
+  IF(EXISTS ~/Library/Frameworks/JavaVM.framework)
+    SET(JAVA_HAVE_FRAMEWORK 1)
+  ENDIF(EXISTS ~/Library/Frameworks/JavaVM.framework)
+  IF(EXISTS /Library/Frameworks/JavaVM.framework)
+    SET(JAVA_HAVE_FRAMEWORK 1)
+  ENDIF(EXISTS /Library/Frameworks/JavaVM.framework)
+  IF(EXISTS /System/Library/Frameworks/JavaVM.framework)
+    SET(JAVA_HAVE_FRAMEWORK 1)
+  ENDIF(EXISTS /System/Library/Frameworks/JavaVM.framework)
+
+  IF(JAVA_HAVE_FRAMEWORK)
+    IF(NOT JAVA_AWT_LIBRARY)
+      SET (JAVA_AWT_LIBRARY "-framework JavaVM" CACHE FILEPATH "Java Frameworks" FORCE)
+    ENDIF(NOT JAVA_AWT_LIBRARY)
+
+    IF(NOT JAVA_JVM_LIBRARY)
+      SET (JAVA_JVM_LIBRARY "-framework JavaVM" CACHE FILEPATH "Java Frameworks" FORCE)
+    ENDIF(NOT JAVA_JVM_LIBRARY)
+
+    IF(NOT JAVA_AWT_INCLUDE_PATH)
+      IF(EXISTS /System/Library/Frameworks/JavaVM.framework/Headers/jawt.h)
+        SET (JAVA_AWT_INCLUDE_PATH "/System/Library/Frameworks/JavaVM.framework/Headers" CACHE FILEPATH "jawt.h location" FORCE)
+      ENDIF(EXISTS /System/Library/Frameworks/JavaVM.framework/Headers/jawt.h)
+    ENDIF(NOT JAVA_AWT_INCLUDE_PATH)
+
+    # If using "-framework JavaVM", prefer its headers *before* the others in
+    # JAVA_AWT_INCLUDE_DIRECTORIES... (*prepend* to the list here)
+    #
+    SET(JAVA_AWT_INCLUDE_DIRECTORIES
+      ~/Library/Frameworks/JavaVM.framework/Headers
+      /Library/Frameworks/JavaVM.framework/Headers
+      /System/Library/Frameworks/JavaVM.framework/Headers
+      ${JAVA_AWT_INCLUDE_DIRECTORIES}
+      )
+  ENDIF(JAVA_HAVE_FRAMEWORK)
+ELSE(APPLE)
+  FIND_LIBRARY(JAVA_AWT_LIBRARY jawt
+    PATHS ${JAVA_AWT_LIBRARY_DIRECTORIES}
+  )
+  FIND_LIBRARY(JAVA_JVM_LIBRARY NAMES jvm JavaVM
+    PATHS ${JAVA_JVM_LIBRARY_DIRECTORIES}
+  )
+ENDIF(APPLE)
+
+# add in the include path
+FIND_PATH(JAVA_INCLUDE_PATH jni.h
+  ${JAVA_AWT_INCLUDE_DIRECTORIES}
+)
+
+FIND_PATH(JAVA_INCLUDE_PATH2 jni_md.h
+  ${JAVA_INCLUDE_PATH}
+  ${JAVA_INCLUDE_PATH}/win32
+  ${JAVA_INCLUDE_PATH}/linux
+  ${JAVA_INCLUDE_PATH}/freebsd
+  ${JAVA_INCLUDE_PATH}/solaris
+  ${JAVA_INCLUDE_PATH}/hp-ux
+  ${JAVA_INCLUDE_PATH}/alpha
+)
+
+FIND_PATH(JAVA_AWT_INCLUDE_PATH jawt.h
+  ${JAVA_INCLUDE_PATH}
+)
+
+#INCLUDE(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake)
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(JNI  DEFAULT_MSG  JAVA_AWT_LIBRARY JAVA_JVM_LIBRARY
+                                                    JAVA_INCLUDE_PATH  JAVA_INCLUDE_PATH2 JAVA_AWT_INCLUDE_PATH)
+
+MARK_AS_ADVANCED(
+  JAVA_AWT_LIBRARY
+  JAVA_JVM_LIBRARY
+  JAVA_AWT_INCLUDE_PATH
+  JAVA_INCLUDE_PATH
+  JAVA_INCLUDE_PATH2
+)
+
+SET(JNI_LIBRARIES
+  ${JAVA_AWT_LIBRARY}
+  ${JAVA_JVM_LIBRARY}
+)
+
+SET(JNI_INCLUDE_DIRS
+  ${JAVA_INCLUDE_PATH}
+  ${JAVA_INCLUDE_PATH2}
+  ${JAVA_AWT_INCLUDE_PATH}
+)

+ 15 - 3
ecl/hqlcpp/hqlcpp.cpp

@@ -11499,10 +11499,16 @@ void HqlCppTranslator::buildScriptFunctionDefinition(BuildCtx &funcctx, IHqlExpr
             bindFunc = bindDataParamAtom;
             break;
         case type_set:
+        {
             bindFunc = bindSetParamAtom;
-            args.append(*createIntConstant(paramType->queryChildType()->getTypeCode()));
-            args.append(*createIntConstant(paramType->queryChildType()->getSize()));
+            ITypeInfo *childType = paramType->queryChildType();
+            type_t typeCode = childType->getTypeCode();
+            if (childType->isInteger() && !childType->isSigned())
+                typeCode = type_unsigned;
+            args.append(*createIntConstant(typeCode));
+            args.append(*createIntConstant(childType->getSize()));
             break;
+        }
         default:
             StringBuffer typeText;
             getFriendlyTypeStr(paramType, typeText);
@@ -11540,10 +11546,16 @@ void HqlCppTranslator::buildScriptFunctionDefinition(BuildCtx &funcctx, IHqlExpr
         returnFunc = getDataResultAtom;
         break;
     case type_set:
+    {
         returnFunc = getSetResultAtom;
-        retargs.append(*createIntConstant(returnType->queryChildType()->getTypeCode()));
+        ITypeInfo *childType = returnType->queryChildType();
+        type_t typeCode = childType->getTypeCode();
+        if (childType->isInteger() && !childType->isSigned())
+            typeCode = type_unsigned;
+        retargs.append(*createIntConstant(typeCode));
         retargs.append(*createIntConstant(returnType->queryChildType()->getSize()));
         break;
+    }
     default:
         StringBuffer typeText;
         getFriendlyTypeStr(returnType, typeText);

+ 1 - 0
plugins/Rembed/CMakeLists.txt

@@ -39,6 +39,7 @@ include_directories (
          ./../../system/include
          ./../../rtl/eclrtl
          ./../../rtl/include
+         ./../../common/deftype
          ./../../system/jlib
     )
 

+ 33 - 0
plugins/Rembed/README

@@ -0,0 +1,33 @@
+################################################################################
+#    HPCC SYSTEMS software Copyright (C) 2013 HPCC Systems.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+################################################################################
+
+Building the Rembed plugin
+--------------------------
+
+Building the plugin to embed R in ECL code is not enabled by default - this is
+because it depends on a component (RInside) that can't be installed via standard
+package-management utilities on most distros.
+
+To install the prequisites for building R support, use the following (Ubuntu 12.04)
+or the equivalent for your distro.
+
+sudo apt-get install r-base r-cran-rcpp
+wget http://cran.r-project.org/src/contrib/RInside_0.2.10.tar.gz
+sudo R CMD INSTALL RInside_0.2.10.tar.gz
+
+Then you can enable building the Rembed plugin using
+
+cmake <path_to_source> -DMAKE_REMBED=1

+ 193 - 8
plugins/Rembed/Rembed.cpp

@@ -17,10 +17,12 @@
 
 #include "platform.h"
 #include "RInside.h"
-#include "eclrtl.hpp"
 #include "jexcept.hpp"
 #include "jthread.hpp"
 #include "hqlplugins.hpp"
+#include "deftype.hpp"
+#include "eclrtl.hpp"
+#include "eclrtl_imp.hpp"
 
 #ifdef _WIN32
 #define EXPORT __declspec(dllexport)
@@ -159,9 +161,112 @@ public:
     {
         throw MakeStringException(MSGAUD_user, 0, "Rembed: %s: Unicode/UTF8 results not supported", func.c_str());
     }
-    virtual void getSetResult(bool & __isAllResult, size32_t & __resultBytes, void * & __result, int elemType, size32_t elemSize)
+    virtual void getSetResult(bool & __isAllResult, size32_t & __resultBytes, void * & __result, int _elemType, size32_t elemSize)
     {
-        UNIMPLEMENTED;
+        type_t elemType = (type_t) _elemType;
+        __isAllResult = false;
+        switch(elemType)
+        {
+
+#define FETCH_ARRAY(type) \
+{  \
+    std::vector<type> vval = ::Rcpp::as< std::vector<type> >(result); \
+    rtlStrToDataX(__resultBytes, __result, vval.size()*elemSize, (const void *) vval.data()); \
+}
+
+        case type_boolean:
+        {
+            std::vector<bool> vval = ::Rcpp::as< std::vector<bool> >(result);
+            size32_t size = vval.size();
+            // Vector of bool is odd, and can't be retrieved via data()
+            // Instead we need to iterate, I guess
+            rtlDataAttr out(size);
+            bool *outData = (bool *) out.getdata();
+            for (std::vector<bool>::iterator iter = vval.begin(); iter < vval.end(); iter++)
+            {
+                *outData++ = *iter;
+            }
+            __resultBytes = size;
+            __result = out.detachdata();
+            break;
+        }
+        case type_int:
+            if (elemSize == sizeof(byte))
+                FETCH_ARRAY(byte)
+            else if (elemSize == sizeof(short))
+                FETCH_ARRAY(short)
+            else if (elemSize == sizeof(int))
+                FETCH_ARRAY(int)
+            else if (elemSize == sizeof(long))    // __int64 / long long does not work...
+                FETCH_ARRAY(long)
+            else
+                rtlFail(0, "Rembed: Unsupported result type");
+            break;
+        case type_real:
+            if (elemSize == sizeof(float))
+                FETCH_ARRAY(float)
+            else if (elemSize == sizeof(double))
+                FETCH_ARRAY(double)
+            else
+                rtlFail(0, "Rembed: Unsupported result type");
+            break;
+        case type_string:
+        case type_varstring:
+        {
+            std::vector<std::string> vval = ::Rcpp::as< std::vector<std::string> >(result);
+            size32_t numResults = vval.size();
+            rtlRowBuilder out;
+            byte *outData = NULL;
+            size32_t outBytes = 0;
+            if (elemSize != UNKNOWN_LENGTH)
+            {
+                outBytes = numResults * elemSize;  // MORE - check for overflow?
+                out.ensureAvailable(outBytes);
+                outData = out.getbytes();
+            }
+            for (std::vector<std::string>::iterator iter = vval.begin(); iter < vval.end(); iter++)
+            {
+                size32_t lenBytes = (*iter).size();
+                const char *text = (*iter).data();
+                if (elemType == type_string)
+                {
+                    if (elemSize == UNKNOWN_LENGTH)
+                    {
+                        out.ensureAvailable(outBytes + lenBytes + sizeof(size32_t));
+                        outData = out.getbytes() + outBytes;
+                        * (size32_t *) outData = lenBytes;
+                        rtlStrToStr(lenBytes, outData+sizeof(size32_t), lenBytes, text);
+                        outBytes += lenBytes + sizeof(size32_t);
+                    }
+                    else
+                    {
+                        rtlStrToStr(elemSize, outData, lenBytes, text);
+                        outData += elemSize;
+                    }
+                }
+                else
+                {
+                    if (elemSize == UNKNOWN_LENGTH)
+                    {
+                        out.ensureAvailable(outBytes + lenBytes + 1);
+                        outData = out.getbytes() + outBytes;
+                        rtlStrToVStr(0, outData, lenBytes, text);
+                        outBytes += lenBytes + 1;
+                    }
+                    else
+                    {
+                        rtlStrToVStr(elemSize, outData, lenBytes, text);  // Fixed size null terminated strings... weird.
+                        outData += elemSize;
+                    }
+                }
+            }
+            __resultBytes = outBytes;
+            __result = out.detachdata();
+            break;
+        }
+        default:
+            rtlFail(0, "REmbed: Unsupported result type");
+        }
     }
 
     virtual void bindBooleanParam(const char *name, bool val)
@@ -198,18 +303,98 @@ public:
     }
     virtual void bindUTF8Param(const char *name, size32_t chars, const char *val)
     {
-        UNIMPLEMENTED;
+        rtlFail(0, "Rembed: Unsupported parameter type UTF8");
     }
     virtual void bindUnicodeParam(const char *name, size32_t chars, const UChar *val)
     {
-        UNIMPLEMENTED;
+        rtlFail(0, "Rembed: Unsupported parameter type UNICODE");
     }
 
-    virtual void bindSetParam(const char *name, int elemType, size32_t elemSize, bool isAll, size32_t totalBytes, void *setData)
+    virtual void bindSetParam(const char *name, int _elemType, size32_t elemSize, bool isAll, size32_t totalBytes, void *setData)
     {
-        UNIMPLEMENTED;
-    }
+        if (isAll)
+            rtlFail(0, "Rembed: Unsupported parameter type ALL");
+        type_t elemType = (type_t) _elemType;
+        int numElems = totalBytes / elemSize;
+        switch(elemType)
+        {
+
+#define BIND_ARRAY(type) \
+{  \
+    std::vector<type> vval; \
+    const type *start = (const type *) setData; \
+    vval.assign(start, start+numElems); \
+    R[name] = vval; \
+}
 
+        case type_boolean:
+            BIND_ARRAY(bool)
+            break;
+        case type_int:
+            /* if (elemSize == sizeof(signed char))  // No binding exists in rcpp
+                BIND_ARRAY(signed char)
+            else */ if (elemSize == sizeof(short))
+                BIND_ARRAY(short)
+            else if (elemSize == sizeof(int))
+                BIND_ARRAY(int)
+            else if (elemSize == sizeof(long))    // __int64 / long long does not work...
+                BIND_ARRAY(long)
+            else
+                rtlFail(0, "Rembed: Unsupported parameter type");
+            break;
+        case type_unsigned:
+            if (elemSize == sizeof(unsigned char))
+                BIND_ARRAY(unsigned char)
+            else if (elemSize == sizeof(unsigned short))
+                BIND_ARRAY(unsigned short)
+            else if (elemSize == sizeof(unsigned int))
+                BIND_ARRAY(unsigned int)
+            else if (elemSize == sizeof(unsigned long))    // __int64 / long long does not work...
+                BIND_ARRAY(unsigned long)
+            else
+                rtlFail(0, "Rembed: Unsupported parameter type");
+            break;
+        case type_real:
+            if (elemSize == sizeof(float))
+                BIND_ARRAY(float)
+            else if (elemSize == sizeof(double))
+                BIND_ARRAY(double)
+            else
+                rtlFail(0, "Rembed: Unsupported parameter type");
+            break;
+        case type_string:
+        case type_varstring:
+        {
+            std::vector<std::string> vval;
+            const byte *inData = (const byte *) setData;
+            const byte *endData = inData + totalBytes;
+            while (inData < endData)
+            {
+                int thisSize;
+                if (elemSize == UNKNOWN_LENGTH)
+                {
+                    if (elemType==type_varstring)
+                        thisSize = strlen((const char *) inData) + 1;
+                    else
+                    {
+                        thisSize = * (size32_t *) inData;
+                        inData += sizeof(size32_t);
+                    }
+                }
+                else
+                    thisSize = elemSize;
+                std::string s((const char *) inData, thisSize);
+                vval.push_back(s);
+                inData += thisSize;
+                numElems++;
+            }
+            R[name] = vval;
+            break;
+        }
+        default:
+            rtlFail(0, "REmbed: Unsupported parameter type");
+        }
+    }
 
     virtual void importFunction(size32_t lenChars, const char *utf)
     {

+ 6 - 0
plugins/pyembed/pyembed.cpp

@@ -378,6 +378,9 @@ public:
             case type_int:
                 rtlWriteInt(outData, PyLong_AsLongLong(elem), elemSize);
                 break;
+            case type_unsigned:
+                rtlWriteInt(outData, PyLong_AsUnsignedLongLong(elem), elemSize);
+                break;
             case type_real:
                 if (!PyFloat_Check(elem))
                     rtlFail(0, "pyembed: type mismatch - return value in list was not a REAL");
@@ -567,6 +570,9 @@ public:
             case type_int:
                 thisElem.setown(PyLong_FromLongLong(rtlReadInt(inData, elemSize)));
                 break;
+            case type_unsigned:
+                thisElem.setown(PyLong_FromUnsignedLongLong(rtlReadUInt(inData, elemSize)));
+                break;
             case type_varstring:
             {
                 size32_t numChars = strlen((const char *) inData);

+ 1 - 0
plugins/v8embed/CMakeLists.txt

@@ -40,6 +40,7 @@ include_directories (
          ./../../system/include
          ./../../rtl/eclrtl
          ./../../rtl/include
+         ./../../common/deftype
          ./../../system/jlib
     )
 

+ 243 - 7
plugins/v8embed/v8embed.cpp

@@ -17,10 +17,12 @@
 
 #include "platform.h"
 #include "v8.h"
-#include "eclrtl.hpp"
 #include "jexcept.hpp"
 #include "jthread.hpp"
 #include "hqlplugins.hpp"
+#include "deftype.hpp"
+#include "eclrtl.hpp"
+#include "eclrtl_imp.hpp"
 
 #ifdef _WIN32
 #define EXPORT __declspec(dllexport)
@@ -119,10 +121,9 @@ public:
     virtual void bindStringParam(const char *name, size32_t len, const char *val)
     {
         size32_t utfCharCount;
-        char *utfText;
-        rtlStrToUtf8X(utfCharCount, utfText, len, val);
-        bindUTF8Param(name, utfCharCount, utfText);
-        rtlFree(utfText);
+        rtlDataAttr utfText;
+        rtlStrToUtf8X(utfCharCount, utfText.refstr(), len, val);
+        bindUTF8Param(name, utfCharCount, utfText.getstr());
     }
     virtual void bindVStringParam(const char *name, const char *val)
     {
@@ -140,7 +141,119 @@ public:
     }
     virtual void bindSetParam(const char *name, int elemType, size32_t elemSize, bool isAll, size32_t totalBytes, void *setData)
     {
-        UNIMPLEMENTED;
+        if (isAll)
+            rtlFail(0, "v8embed: Cannot pass ALL");
+        v8::HandleScope handle_scope;
+        type_t typecode = (type_t) elemType;
+        const byte *inData = (const byte *) setData;
+        const byte *endData = inData + totalBytes;
+        int numElems;
+        if (elemSize == UNKNOWN_LENGTH)
+        {
+            numElems = 0;
+            // Will need 2 passes to work out how many elements there are in the set :(
+            while (inData < endData)
+            {
+                int thisSize;
+                switch (elemType)
+                {
+                case type_varstring:
+                    thisSize = strlen((const char *) inData) + 1;
+                    break;
+                case type_string:
+                    thisSize = * (size32_t *) inData + sizeof(size32_t);
+                    break;
+                case type_unicode:
+                    thisSize = (* (size32_t *) inData) * sizeof(UChar) + sizeof(size32_t);
+                    break;
+                case type_utf8:
+                    thisSize = rtlUtf8Size(* (size32_t *) inData, inData + sizeof(size32_t)) + sizeof(size32_t);;
+                    break;
+                default:
+                    rtlFail(0, "v8embed: Unsupported parameter type");
+                    break;
+                }
+                inData += thisSize;
+                numElems++;
+            }
+            inData = (const byte *) setData;
+        }
+        else
+            numElems = totalBytes / elemSize;
+        v8::Local<v8::Array> array = v8::Array::New(numElems);
+        v8::Handle<v8::Value> thisItem;
+        size32_t thisSize = elemSize;
+        for (int idx = 0; idx < numElems; idx++)
+        {
+            switch (typecode)
+            {
+            case type_int:
+                thisItem = v8::Integer::New(rtlReadInt(inData, elemSize));
+                break;
+            case type_unsigned:
+                thisItem = v8::Integer::NewFromUnsigned(rtlReadUInt(inData, elemSize));
+                break;
+            case type_varstring:
+            {
+                size32_t numChars = strlen((const char *) inData);
+                size32_t utfCharCount;
+                rtlDataAttr utfText;
+                rtlStrToUtf8X(utfCharCount, utfText.refstr(), numChars, (const char *) inData);
+                thisItem = v8::String::New(utfText.getstr(), rtlUtf8Size(utfCharCount, utfText.getstr()));
+                if (elemSize == UNKNOWN_LENGTH)
+                    thisSize = numChars + 1;
+                break;
+            }
+            case type_string:
+            {
+                if (elemSize == UNKNOWN_LENGTH)
+                {
+                    thisSize = * (size32_t *) inData;
+                    inData += sizeof(size32_t);
+                }
+                size32_t utfCharCount;
+                rtlDataAttr utfText;
+                rtlStrToUtf8X(utfCharCount, utfText.refstr(), thisSize, (const char *) inData);
+                thisItem = v8::String::New(utfText.getstr(), rtlUtf8Size(utfCharCount, utfText.getstr()));
+                break;
+            }
+            case type_real:
+                if (elemSize == sizeof(double))
+                    thisItem = v8::Number::New(* (double *) inData);
+                else
+                    thisItem = v8::Number::New(* (float *) inData);
+                break;
+            case type_boolean:
+                assertex(elemSize == sizeof(bool));
+                thisItem = v8::Boolean::New(* (bool *) inData);
+                break;
+            case type_unicode:
+            {
+                if (elemSize == UNKNOWN_LENGTH)
+                {
+                    thisSize = (* (size32_t *) inData) * sizeof(UChar); // NOTE - it's in chars...
+                    inData += sizeof(size32_t);
+                }
+                thisItem = v8::String::New((const UChar *) inData, thisSize/sizeof(UChar));
+                break;
+            }
+            case type_utf8:
+            {
+                assertex (elemSize == UNKNOWN_LENGTH);
+                size32_t numChars = * (size32_t *) inData;
+                inData += sizeof(size32_t);
+                thisSize = rtlUtf8Size(numChars, inData);
+                thisItem = v8::String::New((const char *) inData, thisSize);
+                break;
+            }
+            default:
+                rtlFail(0, "v8embed: Unsupported parameter type");
+                break;
+            }
+            inData += thisSize;
+            array->Set(v8::Number::New(idx), thisItem);
+        }
+        context->Global()->Set(v8::String::New(name), array);
     }
 
     virtual bool getBooleanResult()
@@ -205,7 +318,130 @@ public:
     }
     virtual void getSetResult(bool & __isAllResult, size32_t & __resultBytes, void * & __result, int elemType, size32_t elemSize)
     {
-        UNIMPLEMENTED;
+        assertex (!result.IsEmpty());
+        if (!result->IsArray())
+            rtlFail(0, "v8embed: type mismatch - return value was not an array");
+        v8::HandleScope handle_scope;
+        v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(result);
+        size_t numResults = array->Length();
+        rtlRowBuilder out;
+        byte *outData = NULL;
+        size32_t outBytes = 0;
+        if (elemSize != UNKNOWN_LENGTH)
+        {
+            out.ensureAvailable(numResults * elemSize); // MORE - check for overflow?
+            outData = out.getbytes();
+        }
+        for (int i = 0; i < numResults; i++)
+        {
+            v8::Local<v8::Value> elem = array->Get(i);
+            if (elem.IsEmpty())
+                rtlFail(0, "v8embed: type mismatch - empty value in returned array");
+            switch ((type_t) elemType)
+            {
+            case type_int:
+                rtlWriteInt(outData, v8::Integer::Cast(*elem)->Value(), elemSize);
+                break;
+            case type_unsigned:
+                rtlWriteInt(outData, v8::Integer::Cast(*elem)->Value(), elemSize);
+                break;
+            case type_real:
+                if (elemSize == sizeof(double))
+                    * (double *) outData = (double) v8::Number::Cast(*elem)->Value();
+                else
+                {
+                    assertex(elemSize == sizeof(float));
+                    * (float *) outData = (float) v8::Number::Cast(*elem)->Value();
+                }
+                break;
+            case type_boolean:
+                assertex(elemSize == sizeof(bool));
+                * (bool *) outData = elem->BooleanValue();
+                break;
+            case type_string:
+            case type_varstring:
+            {
+                if (!elem->IsString())
+                    rtlFail(0, "v8embed: type mismatch - return value in list was not a STRING");
+                v8::String::AsciiValue ascii(elem);
+                const char * text =  *ascii;
+                size_t lenBytes = ascii.length();
+                if (elemSize == UNKNOWN_LENGTH)
+                {
+                    if (elemType == type_string)
+                    {
+                        out.ensureAvailable(outBytes + lenBytes + sizeof(size32_t));
+                        outData = out.getbytes() + outBytes;
+                        * (size32_t *) outData = lenBytes;
+                        rtlStrToStr(lenBytes, outData+sizeof(size32_t), lenBytes, text);
+                        outBytes += lenBytes + sizeof(size32_t);
+                    }
+                    else
+                    {
+                        out.ensureAvailable(outBytes + lenBytes + 1);
+                        outData = out.getbytes() + outBytes;
+                        rtlStrToVStr(0, outData, lenBytes, text);
+                        outBytes += lenBytes + 1;
+                    }
+                }
+                else
+                {
+                    if (elemType == type_string)
+                        rtlStrToStr(elemSize, outData, lenBytes, text);
+                    else
+                        rtlStrToVStr(elemSize, outData, lenBytes, text);  // Fixed size null terminated strings... weird.
+                }
+                break;
+            }
+            case type_unicode:
+            case type_utf8:
+            {
+                if (!elem->IsString())
+                    rtlFail(0, "v8embed: type mismatch - return value in list was not a STRING");
+                v8::String::Utf8Value utf8(elem);
+                size_t lenBytes = utf8.length();
+                const char * text =  *utf8;
+                size32_t numchars = rtlUtf8Length(lenBytes, text);
+                if (elemType == type_utf8)
+                {
+                    assertex (elemSize == UNKNOWN_LENGTH);
+                    out.ensureAvailable(outBytes + lenBytes + sizeof(size32_t));
+                    outData = out.getbytes() + outBytes;
+                    * (size32_t *) outData = numchars;
+                    rtlStrToStr(lenBytes, outData+sizeof(size32_t), lenBytes, text);
+                    outBytes += lenBytes + sizeof(size32_t);
+                }
+                else
+                {
+                    if (elemSize == UNKNOWN_LENGTH)
+                    {
+                        out.ensureAvailable(outBytes + numchars*sizeof(UChar) + sizeof(size32_t));
+                        outData = out.getbytes() + outBytes;
+                        // You can't assume that number of chars in utf8 matches number in unicode16 ...
+                        size32_t numchars16;
+                        rtlDataAttr unicode16;
+                        rtlUtf8ToUnicodeX(numchars16, unicode16.refustr(), numchars, text);
+                        * (size32_t *) outData = numchars16;
+                        rtlUnicodeToUnicode(numchars16, (UChar *) (outData+sizeof(size32_t)), numchars16, unicode16.getustr());
+                        outBytes += numchars16*sizeof(UChar) + sizeof(size32_t);
+                    }
+                    else
+                        rtlUtf8ToUnicode(elemSize / sizeof(UChar), (UChar *) outData, numchars, text);
+                }
+                break;
+            }
+            default:
+                rtlFail(0, "v8embed: type mismatch - unsupported return type");
+            }
+            if (elemSize != UNKNOWN_LENGTH)
+            {
+                outData += elemSize;
+                outBytes += elemSize;
+            }
+        }
+        __isAllResult = false;
+        __resultBytes = outBytes;
+        __result = out.detachdata();
     }
 
     virtual void compileEmbeddedScript(size32_t lenChars, const char *utf)

+ 74 - 0
testing/ecl/embedR.ecl

@@ -13,9 +13,83 @@ val[1] = val[2];
 val;
 ENDEMBED;
 
+set of integer testSet(set of integer val) := EMBED(R)
+t = val [1];
+val[1] = val[2];
+val[2] = t;
+val;
+ENDEMBED;
+
+set of unsigned2 testSet0(set of unsigned2 val) := EMBED(R)
+sort(val);
+ENDEMBED;
+
+set of string testSet2(set of string val) := EMBED(R)
+t = val [1];
+val[1] = val[2];
+val[2] = t;
+val;
+ENDEMBED;
+
+set of string testSet3(set of string8 val) := EMBED(R)
+t = val [1];
+val[1] = val[2];
+val[2] = t;
+val;
+ENDEMBED;
+
+set of varstring testSet4(set of varstring val) := EMBED(R)
+t = val [1];
+val[1] = val[2];
+val[2] = t;
+val;
+ENDEMBED;
+
+set of varstring8 testSet5(set of varstring8 val) := EMBED(R)
+t = val [1];
+val[1] = val[2];
+val[2] = t;
+val;
+ENDEMBED;
+
+set of boolean testSet6(set of boolean val) := EMBED(R)
+t = val [1];
+val[1] = val[2];
+val[2] = t;
+val;
+ENDEMBED;
+
+set of real4 testSet7(set of real4 val) := EMBED(R)
+t = val [1];
+val[1] = val[2];
+val[2] = t;
+val;
+ENDEMBED;
+
+set of real8 testSet8(set of real8 val) := EMBED(R)
+t = val [1];
+val[1] = val[2];
+val[2] = t;
+val;
+ENDEMBED;
+
+set of integer2 testSet9(set of integer2 val) := EMBED(R)
+sort(val);
+ENDEMBED;
+
 add1(10);
 cat('Hello', 'World');
 testData(D'ab');
+testSet([1,2,3]);
+testSet0([30000,40000,50000]);
+testSet2(['one','two','three']);
+testSet3(['uno','dos','tre']);
+testSet4(['un','deux','trois']);
+testSet5(['ein','zwei','drei']);
+testSet6([false,true,false,true]);
+testSet7([1.1,2.2,3.3]);
+testSet8([1.2,2.3,3.4]);
+testSet9([-111,0,113]);
 
 s1 :=DATASET(250000, TRANSFORM({ integer a }, SELF.a := add1(COUNTER)));
 s2 :=DATASET(250000, TRANSFORM({ integer a }, SELF.a := add1(COUNTER/2)));

+ 79 - 0
testing/ecl/embedjs.ecl

@@ -10,6 +10,75 @@ unicode add5(unicode val) := EMBED(javascript, U' val+\' at Oh là là Straße\'
 
 integer testThrow(integer val) := EMBED(javascript) throw new Error("Error from JavaScript"); ENDEMBED;
 data testData(data val) := EMBED(javascript) val[0] = val[0] + 1; val; ENDEMBED;
+set of integer testSet(set of integer val) := EMBED(javascript)
+t = val [1];
+val[1] = val[2];
+val[2] = t;
+val;
+ENDEMBED;
+
+set of unsigned2 testSet0(set of unsigned2 val) := EMBED(javascript)
+t = val [1];
+val[1] = val[2];
+val[2] = t;
+val;
+ENDEMBED;
+
+set of string testSet2(set of string val) := EMBED(javascript)
+t = val [1];
+val[1] = val[2];
+val[2] = t;
+val;
+ENDEMBED;
+
+set of string testSet3(set of string8 val) := EMBED(javascript)
+t = val [1];
+val[1] = val[2];
+val[2] = t;
+val;
+ENDEMBED;
+
+set of varstring testSet4(set of varstring val) := EMBED(javascript)
+t = val [1];
+val[1] = val[2];
+val[2] = t;
+val;
+ENDEMBED;
+
+set of varstring8 testSet5(set of varstring8 val) := EMBED(javascript)
+t = val [1];
+val[1] = val[2];
+val[2] = t;
+val;
+ENDEMBED;
+
+set of boolean testSet6(set of boolean val) := EMBED(javascript)
+t = val [1];
+val[1] = val[2];
+val[2] = t;
+val;
+ENDEMBED;
+
+set of real4 testSet7(set of real4 val) := EMBED(javascript)
+t = val [1];
+val[1] = val[2];
+val[2] = t;
+val;
+ENDEMBED;
+
+set of real8 testSet8(set of real8 val) := EMBED(javascript)
+t = val [1];
+val[1] = val[2];
+val[2] = t;
+val;
+ENDEMBED;
+
+set of integer2 testSet9(set of integer2 val) := EMBED(javascript)
+t = val [1];
+val[1] = val[2];
+val[2] = t;
+val;
+ENDEMBED;
 
 add1(10);
 add2('Hello');
@@ -30,6 +99,16 @@ end;
 
 catch(d(testThrow(a) = a), onfail(t));
 testdata(D'aa');
+testSet([1,2,3]);
+testSet0([30000,40000,50000]);
+testSet2(['one','two','three']);
+testSet3(['uno','dos','tre']);
+testSet4(['un','deux','trois']);
+testSet5(['ein','zwei','drei']);
+testSet6([false,true,false,true]);
+testSet7([1.1,2.2,3.3]);
+testSet8([1.2,2.3,3.4]);
+testSet9([-111,0,113]);
 
 s1 :=DATASET(250000, TRANSFORM({ integer a }, SELF.a := add1(COUNTER)));
 s2 :=DATASET(250000, TRANSFORM({ integer a }, SELF.a := add1(COUNTER/2)));

+ 29 - 2
testing/ecl/key/embedR.xml

@@ -8,8 +8,35 @@
  <Row><Result_3>6262</Result_3></Row>
 </Dataset>
 <Dataset name='Result 4'>
- <Row><Result_4>46875625000</Result_4></Row>
+ <Row><Result_4><Item>2</Item><Item>1</Item><Item>3</Item></Result_4></Row>
 </Dataset>
 <Dataset name='Result 5'>
- <Row><Result_5>46875625000</Result_5></Row>
+ <Row><Result_5><Item>two</Item><Item>one</Item><Item>three</Item></Result_5></Row>
+</Dataset>
+<Dataset name='Result 6'>
+ <Row><Result_6><Item>dos     </Item><Item>uno     </Item><Item>tre     </Item></Result_6></Row>
+</Dataset>
+<Dataset name='Result 7'>
+ <Row><Result_7><Item>deux</Item><Item>un</Item><Item>trois</Item></Result_7></Row>
+</Dataset>
+<Dataset name='Result 8'>
+ <Row><Result_8><Item>zwei</Item><Item>ein</Item><Item>drei</Item></Result_8></Row>
+</Dataset>
+<Dataset name='Result 9'>
+ <Row><Result_9><Item>true</Item><Item>false</Item><Item>false</Item><Item>true</Item></Result_9></Row>
+</Dataset>
+<Dataset name='Result 10'>
+ <Row><Result_10><Item>2.200000047683716</Item><Item>1.100000023841858</Item><Item>3.299999952316284</Item></Result_10></Row>
+</Dataset>
+<Dataset name='Result 11'>
+ <Row><Result_11><Item>2.3</Item><Item>1.2</Item><Item>3.4</Item></Result_11></Row>
+</Dataset>
+<Dataset name='Result 12'>
+ <Row><Result_12><Item>12</Item><Item>11</Item><Item>13</Item></Result_12></Row>
+</Dataset>
+<Dataset name='Result 13'>
+ <Row><Result_13>46875625000</Result_13></Row>
+</Dataset>
+<Dataset name='Result 14'>
+ <Row><Result_14>46875625000</Result_14></Row>
 </Dataset>

+ 34 - 4
testing/ecl/key/embedjs.xml

@@ -26,14 +26,44 @@
  <Row><Result_9>6261</Result_9></Row>
 </Dataset>
 <Dataset name='Result 10'>
- <Row><Result_10>46875625000</Result_10></Row>
+ <Row><Result_10><Item>1</Item><Item>3</Item><Item>2</Item></Result_10></Row>
 </Dataset>
 <Dataset name='Result 11'>
- <Row><Result_11>328126500000</Result_11></Row>
+ <Row><Result_11><Item>30000</Item><Item>50000</Item><Item>40000</Item></Result_11></Row>
 </Dataset>
 <Dataset name='Result 12'>
- <Row><Result_12>46875625000</Result_12></Row>
+ <Row><Result_12><Item>one</Item><Item>three</Item><Item>two</Item></Result_12></Row>
 </Dataset>
 <Dataset name='Result 13'>
- <Row><Result_13>328126500000</Result_13></Row>
+ <Row><Result_13><Item>uno     </Item><Item>tre     </Item><Item>dos     </Item></Result_13></Row>
+</Dataset>
+<Dataset name='Result 14'>
+ <Row><Result_14><Item>un</Item><Item>trois</Item><Item>deux</Item></Result_14></Row>
+</Dataset>
+<Dataset name='Result 15'>
+ <Row><Result_15><Item>ein</Item><Item>drei</Item><Item>zwei</Item></Result_15></Row>
+</Dataset>
+<Dataset name='Result 16'>
+ <Row><Result_16><Item>false</Item><Item>false</Item><Item>true</Item><Item>true</Item></Result_16></Row>
+</Dataset>
+<Dataset name='Result 17'>
+ <Row><Result_17><Item>1.100000023841858</Item><Item>3.299999952316284</Item><Item>2.200000047683716</Item></Result_17></Row>
+</Dataset>
+<Dataset name='Result 18'>
+ <Row><Result_18><Item>1.2</Item><Item>3.4</Item><Item>2.3</Item></Result_18></Row>
+</Dataset>
+<Dataset name='Result 19'>
+ <Row><Result_19><Item>-111</Item><Item>113</Item><Item>0</Item></Result_19></Row>
+</Dataset>
+<Dataset name='Result 20'>
+ <Row><Result_20>46875625000</Result_20></Row>
+</Dataset>
+<Dataset name='Result 21'>
+ <Row><Result_21>328126500000</Result_21></Row>
+</Dataset>
+<Dataset name='Result 22'>
+ <Row><Result_22>46875625000</Result_22></Row>
+</Dataset>
+<Dataset name='Result 23'>
+ <Row><Result_23>328126500000</Result_23></Row>
 </Dataset>