diff --git a/make-it/EmMakefile b/make-it/EmMakefile index 9c50e49c1..3c3ade6a3 100644 --- a/make-it/EmMakefile +++ b/make-it/EmMakefile @@ -106,6 +106,8 @@ EM_EXPORTS = -s EXPORTED_FUNCTIONS="[\ '_EggShell_REPL',\ '_EggShell_ExecuteSlices',\ '_EggShell_Delete',\ + '_EggShell_FindValue',\ + '_EggShell_FindSubValue',\ '_EggShell_ReadDouble',\ '_EggShell_WriteDouble',\ '_EggShell_ReadValueString',\ @@ -140,11 +142,32 @@ EM_EXPORTS = -s EXPORTED_FUNCTIONS="[\ '_Data_WriteDouble',\ '_Data_WriteJavaScriptRefNum',\ '_Data_GetArrayMetadata',\ + '_Data_GetArrayBegin',\ + '_Data_GetArrayDimensions',\ + '_Data_GetArrayLength',\ '_Data_GetArrayDimLength',\ '_Data_ResizeArray',\ + '_TypeRef_TopAQSize',\ + '_TypeRef_Name',\ + '_TypeRef_ElementName',\ + '_TypeRef_SubElementCount',\ + '_TypeRef_GetSubElementByIndex',\ + '_TypeRef_IsCluster',\ + '_TypeRef_IsArray',\ + '_TypeRef_IsBoolean',\ + '_TypeRef_IsInteger',\ + '_TypeRef_IsSigned',\ + '_TypeRef_IsEnum',\ + '_TypeRef_IsFloat',\ + '_TypeRef_IsString',\ + '_TypeRef_IsPath',\ + '_TypeRef_IsTimestamp',\ + '_TypeRef_IsComplex',\ + '_TypeRef_IsAnalogWaveform',\ '_JavaScriptInvoke_GetParameterType',\ '_JavaScriptInvoke_GetParameterPointer',\ - '_JavaScriptInvoke_GetArrayElementType'\ + '_JavaScriptInvoke_GetArrayElementType',\ + '_TypeRef_Rank'\ ]" EM_RUNTIME_EXPORTS = -s EXTRA_EXPORTED_RUNTIME_METHODS="[\ diff --git a/source/core/CEntryPoints.cpp b/source/core/CEntryPoints.cpp index ade53258d..b425921f2 100644 --- a/source/core/CEntryPoints.cpp +++ b/source/core/CEntryPoints.cpp @@ -118,67 +118,93 @@ VIREO_EXPORT Int32 EggShell_PokeMemory(TypeManagerRef tm, } } //------------------------------------------------------------ -//! Write a numeric value to a symbol. Value will be coerced as needed. -VIREO_EXPORT void EggShell_WriteDouble(TypeManagerRef tm, const char* viName, const char* eltName, Double d) +//! Get a reference to the type pointer and data for a symbol. +VIREO_EXPORT EggShellResult EggShell_FindValue(TypeManagerRef tm, const char* viName, const char* eltName, TypeRef* typeRefLocation, void** dataRefLocation) { - void *pData = nullptr; - + TypeManagerScope scope(tm); SubString objectName(viName); SubString path(eltName); - TypeRef actualType = tm->GetObjectElementAddressFromPath(&objectName, &path, &pData, true); - if (actualType == nullptr) - return; + *typeRefLocation = tm->GetObjectElementAddressFromPath(&objectName, &path, dataRefLocation, true); + if (*typeRefLocation == nullptr) + return kEggShellResult_ObjectNotFoundAtPath; - WriteDoubleToMemory(actualType, pData, d); + return kEggShellResult_Success; } //------------------------------------------------------------ -//! Read a numeric value from a symbol. Value will be coerced as needed. -VIREO_EXPORT Double EggShell_ReadDouble(TypeManagerRef tm, const char* viName, const char* eltName) +//! Get a reference to the type pointer and data for a sub element +VIREO_EXPORT EggShellResult EggShell_FindSubValue(TypeManagerRef tm, + const TypeRef typeRef, void * pData, const char* eltName, TypeRef* typeRefLocation, void** dataRefLocation) { - void *pData = nullptr; - SubString objectName(viName); + if (typeRef == nullptr || !typeRef->IsValid()) + return kEggShellResult_InvalidTypeRef; + + TypeManagerScope scope(tm); SubString path(eltName); - TypeRef actualType = tm->GetObjectElementAddressFromPath(&objectName, &path, &pData, true); - if (actualType == nullptr) - return -1; + *typeRefLocation = typeRef->GetSubElementAddressFromPath(&path, pData, dataRefLocation, true); + if (*typeRefLocation == nullptr) + return kEggShellResult_ObjectNotFoundAtPath; - return ReadDoubleFromMemory(actualType, pData); + return kEggShellResult_Success; } //------------------------------------------------------------ -// Write a string value to a symbol. Value will be parsed according to format designated. -VIREO_EXPORT void EggShell_WriteValueString(TypeManagerRef tm, - const char* viName, const char* eltName, const char* format, const char* value) +//! Write a numeric value to a symbol. Value will be coerced as needed. +VIREO_EXPORT EggShellResult EggShell_WriteDouble(TypeManagerRef tm, const TypeRef typeRef, void* pData, Double value) { TypeManagerScope scope(tm); + if (typeRef == nullptr || !typeRef->IsValid()) + return kEggShellResult_InvalidTypeRef; - void *pData = nullptr; + NIError error = WriteDoubleToMemory(typeRef, pData, value); + if (error) + return kEggShellResult_UnexpectedObjectType; + return kEggShellResult_Success; +} +//------------------------------------------------------------ +//! Read a numeric value from a symbol. Value will be coerced as needed. +VIREO_EXPORT EggShellResult EggShell_ReadDouble(TypeManagerRef tm, const TypeRef typeRef, const void* pData, Double* result) +{ + TypeManagerScope scope(tm); + if (typeRef == nullptr || !typeRef->IsValid()) + return kEggShellResult_InvalidTypeRef; + + if (result == nullptr) + return kEggShellResult_InvalidResultPointer; + + NIError error = kNIError_Success; + *result = ReadDoubleFromMemory(typeRef, pData, &error); + if (error) + return kEggShellResult_UnexpectedObjectType; + return kEggShellResult_Success; +} +//------------------------------------------------------------ +// Write a string value to a symbol. Value will be parsed according to format designated. +VIREO_EXPORT EggShellResult EggShell_WriteValueString(TypeManagerRef tm, const TypeRef typeRef, void* pData, const char* format, const char* value) +{ + TypeManagerScope scope(tm); - SubString objectName(viName); - SubString path(eltName); SubString valueString(value); - TypeRef actualType = tm->GetObjectElementAddressFromPath(&objectName, &path, &pData, true); - if (actualType == nullptr) - return; + if (typeRef == nullptr || !typeRef->IsValid()) + return kEggShellResult_InvalidTypeRef; EventLog log(EventLog::DevNull); SubString formatss(format); TDViaParser parser(tm, &valueString, &log, 1, &formatss, true, true, true); - parser.ParseData(actualType, pData); + Int32 error = parser.ParseData(typeRef, pData); + if (error) { + return kEggShellResult_UnableToParseData; + } + + return kEggShellResult_Success; } //------------------------------------------------------------ -//! Read a symbol's value as a string. Value will be formatted according to the format designated. -VIREO_EXPORT const char* EggShell_ReadValueString(TypeManagerRef tm, - const char* viName, const char* eltName, const char* format) +//! Read a symbol's value as a string. Value will be formatted according to designated format. +VIREO_EXPORT EggShellResult EggShell_ReadValueString(TypeManagerRef tm, const TypeRef typeRef, void* pData, const char* format, UInt8** valueString) { TypeManagerScope scope(tm); - void *pData = nullptr; - SubString objectName(viName); - SubString path(eltName); - TypeRef actualType = tm->GetObjectElementAddressFromPath(&objectName, &path, &pData, true); - if (actualType == nullptr) - return nullptr; + if (typeRef == nullptr || !typeRef->IsValid()) + return kEggShellResult_InvalidTypeRef; static StringRef returnBuffer = nullptr; if (returnBuffer == nullptr) { @@ -193,12 +219,14 @@ VIREO_EXPORT const char* EggShell_ReadValueString(TypeManagerRef tm, if (returnBuffer) { SubString formatss(format); TDViaFormatter formatter(returnBuffer, true, 0, &formatss, kJSONEncodingEggShell); - formatter.FormatData(actualType, pData); - // Add an explicit nullptr terminator so it looks like a C string. + formatter.FormatData(typeRef, pData); + // Add an explicit null terminator so it looks like a C string. returnBuffer->Append((Utf8Char)'\0'); - return (const char*) returnBuffer->Begin(); + *valueString = returnBuffer->Begin(); + return kEggShellResult_Success; } - return ""; + + return kEggShellResult_UnableToCreateReturnBuffer; } void CopyArrayTypeNameStringToBuffer(StringRef arrayTypeNameBuffer, SubString arrayTypeName) { @@ -257,20 +285,26 @@ VIREO_EXPORT Int32 EggShell_GetArrayDimLength(TypeManagerRef tm, const char* viN } //------------------------------------------------------------ //! Resizes a variable size Array symbol to have new dimension lengths specified by newLengths, it also initializes cells for non-flat data. -//! Returns -1 if the symbols is not found, -2 if was not possible to resize the array and 0 if resizing was successful. -VIREO_EXPORT Int32 EggShell_ResizeArray(TypeManagerRef tm, const char* viName, const char* eltName, Int32 rank, Int32* newLengths) +VIREO_EXPORT EggShellResult EggShell_ResizeArray(TypeManagerRef tm, const TypeRef typeRef, const void* pData, + Int32 rank, Int32 dimensionLengths[]) { - SubString objectName(viName); - SubString path(eltName); - void *pData = nullptr; + TypeManagerScope scope(tm); + if (typeRef == nullptr || !typeRef->IsValid()) + return kEggShellResult_InvalidTypeRef; - TypeRef actualType = tm->GetObjectElementAddressFromPath(&objectName, &path, &pData, true); - if (actualType == nullptr || !actualType->IsArray()) { - return kLVError_ArgError; - } + if (!typeRef->IsArray()) + return kEggShellResult_UnexpectedObjectType; - TypedArrayCoreRef actualArray = *(TypedArrayCoreRef*)pData; - return Data_ResizeArray(tm, actualArray, rank, newLengths); + if (typeRef->Rank() != rank) + return kEggShellResult_MismatchedArrayRank; + + TypedArrayCoreRef arrayObject = *(TypedArrayCoreRef*)pData; + VIREO_ASSERT(TypedArrayCore::ValidateHandle(arrayObject)); + + if (!arrayObject->ResizeDimensions(rank, dimensionLengths, true, false)) { + return kEggShellResult_UnableToCreateReturnBuffer; + } + return kEggShellResult_Success; } //------------------------------------------------------------ VIREO_EXPORT void* Data_GetStringBegin(StringRef stringObject) @@ -329,6 +363,34 @@ VIREO_EXPORT EggShellResult Data_GetArrayMetadata(TypeManagerRef tm, return kEggShellResult_Success; } //------------------------------------------------------------ +//! Get the starting location of the first element of an Array / String type in memory +// This function returns the start address of where elements would appear in memory (returns address even if length zero) +VIREO_EXPORT void* Data_GetArrayBegin(const void* pData) +{ + TypedArrayCoreRef arrayObject = *(TypedArrayCoreRef*)pData; + VIREO_ASSERT(TypedArrayCore::ValidateHandle(arrayObject)); + return arrayObject->BeginAt(0); +} +//------------------------------------------------------------ +//! Get the values for dimensions of the array. Assumes dimensions target is of length equal to rank +//! Caller is expected to allocate an array dimensions of size array rank for the duration of function invocation. +VIREO_EXPORT void Data_GetArrayDimensions(const void* pData, IntIndex dimensionsLengths[]) +{ + TypedArrayCoreRef arrayObject = *(TypedArrayCoreRef*)pData; + VIREO_ASSERT(TypedArrayCore::ValidateHandle(arrayObject)); + for (int i = 0; i < arrayObject->Rank(); i++) { + dimensionsLengths[i] = arrayObject->GetLength(i); + } +} +//------------------------------------------------------------ +//! Get the total length for an array +VIREO_EXPORT Int32 Data_GetArrayLength(const void* pData) +{ + TypedArrayCoreRef arrayObject = *(TypedArrayCoreRef*)pData; + VIREO_ASSERT(TypedArrayCore::ValidateHandle(arrayObject)); + return arrayObject->Length(); +} +//------------------------------------------------------------ //! Get the Length of a dimension in an Array Symbol. Returns -1 if the Symbol is not found or not //! an Array or dimension requested is out of the bounds of the rank. VIREO_EXPORT Int32 Data_GetArrayDimLength(TypeManagerRef tm, TypedArrayCoreRef arrayObject, Int32 dim) @@ -505,10 +567,54 @@ VIREO_EXPORT Int32 TypeRef_Alignment(TypeRef typeRef) return typeRef->AQAlignment(); } //------------------------------------------------------------ -VIREO_EXPORT void TypeRef_Name(TypeRef typeRef, Int32* bufferSize, char* buffer) +VIREO_EXPORT const char* TypeRef_Name(TypeManagerRef tm, TypeRef typeRef) { + TypeManagerScope scope(tm); SubString name = typeRef->Name(); - *bufferSize = name.CopyToBoundedBuffer(*bufferSize, reinterpret_cast(buffer)); + + static StringRef returnBuffer = nullptr; + if (returnBuffer == nullptr) { + // Allocate a string the first time it is used. + // After that it will be resized as needed. + STACK_VAR(String, tempReturn); + returnBuffer = tempReturn.DetachValue(); + } else { + returnBuffer->Resize1D(name.Length() + 1); + } + + if (returnBuffer) { + returnBuffer->CopyFromSubString(&name); + // Add an explicit null terminator so it looks like a C string. + returnBuffer->Append((Utf8Char)'\0'); + return (const char*) returnBuffer->Begin(); + } + + return ""; +} +//------------------------------------------------------------ +VIREO_EXPORT const char* TypeRef_ElementName(TypeManagerRef tm, TypeRef typeRef) +{ + TypeManagerScope scope(tm); + SubString name = typeRef->ElementName(); + + static StringRef returnBuffer = nullptr; + if (returnBuffer == nullptr) { + // Allocate a string the first time it is used. + // After that it will be resized as needed. + STACK_VAR(String, tempReturn); + returnBuffer = tempReturn.DetachValue(); + } else { + returnBuffer->Resize1D(name.Length() + 1); + } + + if (returnBuffer) { + returnBuffer->CopyFromSubString(&name); + // Add an explicit null terminator so it looks like a C string. + returnBuffer->Append((Utf8Char)'\0'); + return (const char*) returnBuffer->Begin(); + } + + return ""; } //------------------------------------------------------------ VIREO_EXPORT Int32 TypeRef_ElementOffset(TypeRef typeRef) @@ -546,6 +652,66 @@ VIREO_EXPORT TypeRef TypeRef_GetSubElementByIndex(TypeRef typeRef, Int32 index) return typeRef->GetSubElement(index); } //------------------------------------------------------------ +VIREO_EXPORT Boolean TypeRef_IsCluster(TypeRef typeRef) +{ + return typeRef->IsCluster(); +} +//------------------------------------------------------------ +VIREO_EXPORT Boolean TypeRef_IsArray(TypeRef typeRef) +{ + return typeRef->IsArray(); +} +//------------------------------------------------------------ +VIREO_EXPORT Boolean TypeRef_IsBoolean(TypeRef typeRef) +{ + return typeRef->IsBoolean(); +} +//------------------------------------------------------------ +VIREO_EXPORT Boolean TypeRef_IsInteger(TypeRef typeRef) +{ + return typeRef->IsInteger(); +} +//------------------------------------------------------------ +VIREO_EXPORT Boolean TypeRef_IsSigned(TypeRef typeRef) +{ + return typeRef->IsSignedInteger(); +} +//------------------------------------------------------------ +VIREO_EXPORT Boolean TypeRef_IsEnum(TypeRef typeRef) +{ + return typeRef->IsEnum(); +} +//------------------------------------------------------------ +VIREO_EXPORT Boolean TypeRef_IsFloat(TypeRef typeRef) +{ + return typeRef->IsFloat(); +} +//------------------------------------------------------------ +VIREO_EXPORT Boolean TypeRef_IsString(TypeRef typeRef) +{ + return typeRef->IsString(); +} +//------------------------------------------------------------ +VIREO_EXPORT Boolean TypeRef_IsPath(TypeRef typeRef) +{ + return typeRef->IsPath(); +} +//------------------------------------------------------------ +VIREO_EXPORT Boolean TypeRef_IsTimestamp(TypeRef typeRef) +{ + return typeRef->IsTimestamp(); +} +//------------------------------------------------------------ +VIREO_EXPORT Boolean TypeRef_IsComplex(TypeRef typeRef) +{ + return typeRef->IsComplex(); +} +//------------------------------------------------------------ +VIREO_EXPORT Boolean TypeRef_IsAnalogWaveform(TypeRef typeRef) +{ + return typeRef->IsAnalogWaveform(); +} +//------------------------------------------------------------ //------------------------------------------------------------ VIREO_EXPORT Int32 Data_RawBlockSize(TypedBlock* object) { diff --git a/source/core/StringUtilities.cpp b/source/core/StringUtilities.cpp index 0be75e1f4..6cbb4d673 100644 --- a/source/core/StringUtilities.cpp +++ b/source/core/StringUtilities.cpp @@ -1073,7 +1073,7 @@ void SubString::TrimQuotedString(TokenTraits tt) } } //------------------------------------------------------------ -IntIndex SubString::FindFirstMatch(SubString* searchString, IntIndex offset, Boolean ignoreCase) +IntIndex SubString::FindFirstMatch(const SubString* searchString, IntIndex offset, Boolean ignoreCase) { IntIndex searchStringLength = searchString->Length(); if (Length() == 0 || searchStringLength > Length()) diff --git a/source/core/TypeAndDataManager.cpp b/source/core/TypeAndDataManager.cpp index c341aafe7..fdf4bc326 100644 --- a/source/core/TypeAndDataManager.cpp +++ b/source/core/TypeAndDataManager.cpp @@ -602,6 +602,8 @@ const SubString TypeCommon::TypeTimestamp = SubString("Timestamp"); const SubString TypeCommon::TypeComplexSingle = SubString("ComplexSingle"); const SubString TypeCommon::TypeComplexDouble = SubString("ComplexDouble"); const SubString TypeCommon::TypeJavaScriptRefNum = SubString(tsJavaScriptRefNumToken); +const SubString TypeCommon::TypePath = SubString("NIPath"); +const SubString TypeCommon::TypeAnalogWaveform = SubString("AnalogWaveform"); const SubString TypeCommon::TypeStaticTypeAndData = SubString("StaticTypeAndData"); TypeCommon::TypeCommon(TypeManagerRef typeManager) @@ -857,14 +859,38 @@ Boolean TypeCommon::IsA(const SubString *otherTypeName) } //------------------------------------------------------------ Boolean TypeCommon::IsNumeric() +{ + TypeRef t = this; + while (t) { + if (t->Name().Compare(&TypeInt8) || t->Name().Compare(&TypeInt16) || t->Name().Compare(&TypeInt32) || t->Name().Compare(&TypeInt64) + || t->Name().Compare(&TypeUInt8) || t->Name().Compare(&TypeUInt16) || t->Name().Compare(&TypeUInt32) || t->Name().Compare(&TypeUInt64) + || t->Name().Compare(&TypeSingle) || t->Name().Compare(&TypeDouble)) { + return true; + } + t = t->BaseType(); + } + return false; +} +//------------------------------------------------------------ +Boolean TypeCommon::IsInteger() +{ + TypeRef t = this; + while (t) { + if (t->Name().Compare(&TypeInt8) || t->Name().Compare(&TypeInt16) || t->Name().Compare(&TypeInt32) || t->Name().Compare(&TypeInt64) + || t->Name().Compare(&TypeUInt8) || t->Name().Compare(&TypeUInt16) || t->Name().Compare(&TypeUInt32) || t->Name().Compare(&TypeUInt64)) { + return true; + } + t = t->BaseType(); + } + return false; +} +//------------------------------------------------------------ +Boolean TypeCommon::IsSignedInteger() { TypeRef t = this; while (t) { if (t->Name().Compare(&TypeInt8) || t->Name().Compare(&TypeInt16) || t->Name().Compare(&TypeInt32) - || t->Name().Compare(&TypeInt64) || t->Name().Compare(&TypeUInt8) - || t->Name().Compare(&TypeUInt16) || t->Name().Compare(&TypeUInt32) - || t->Name().Compare(&TypeUInt64) || t->Name().Compare(&TypeSingle) - || t->Name().Compare(&TypeDouble)) { + || t->Name().Compare(&TypeInt64)) { return true; } t = t->BaseType(); @@ -922,6 +948,18 @@ Boolean TypeCommon::IsString() return false; } +//------------------------------------------------------------ +Boolean TypeCommon::IsPath() +{ + TypeRef t = this; + while (t) { + if (t->Name().Compare(&TypePath)) { + return true; + } + t = t->BaseType(); + } + return false; +} //------------------------------------------------------------ Boolean TypeCommon::IsTimestamp() { @@ -959,6 +997,19 @@ Boolean TypeCommon::IsJavaScriptRefNum() return false; } //------------------------------------------------------------ +Boolean TypeCommon::IsAnalogWaveform() +{ + TypeRef t = this; + while (t) { + SubString typeName = t->Name(); + if (typeName.FindFirstMatch(&TypeAnalogWaveform, 0, false) == 0) { + return true; + } + t = t->BaseType(); + } + return false; +} +//------------------------------------------------------------ Boolean TypeCommon::IsIntrinsicClusterDataType(SubString *foundTypeName) { TypeRef t = this; while (t) { @@ -2640,9 +2691,9 @@ NIError WriteIntToMemory(TypeRef type, void* pData, IntMax value) } //------------------------------------------------------------ //! Read a IEEE754 double value from memory converting as necessary. -Double ReadDoubleFromMemory(TypeRef type, void* pData) +Double ReadDoubleFromMemory(TypeRef type, const void* pData, NIError* errResult) { - Boolean isErr = false; + NIError err = kNIError_Success; Double value = 0.0; EncodingEnum encoding = type->BitEncoding(); Int32 aqSize = type->TopAQSize(); @@ -2651,55 +2702,61 @@ Double ReadDoubleFromMemory(TypeRef type, void* pData) switch (aqSize) { case 4: value = *(Single*)pData; break; case 8: value = *(Double*)pData; break; - default: isErr = true; break; + default: err = kNIError_kCantDecode; break; } break; case kEncoding_S2CInt: case kEncoding_DimInt: switch (aqSize) { - case 1: value = *(Int8*)pData; break; - case 2: value = *(Int16*)pData; break; - case 4: value = *(Int32*)pData; break; - case 8: value = static_cast(*(Int64*)pData); break; - default: isErr = true; break; + case 1: value = *(Int8*)pData; break; + case 2: value = *(Int16*)pData; break; + case 4: value = *(Int32*)pData; break; + case 8: value = static_cast(*(Int64*)pData); break; + default: err = kNIError_kCantDecode; break; } break; case kEncoding_UInt: case kEncoding_Enum: switch (aqSize) { - case 1: value = *(UInt8*)pData; break; - case 2: value = *(UInt16*)pData; break; - case 4: value = *(UInt32*)pData; break; - case 8: value = static_cast(*(UInt64*)pData); break; - default: isErr = true; break; + case 1: value = *(UInt8*)pData; break; + case 2: value = *(UInt16*)pData; break; + case 4: value = *(UInt32*)pData; break; + case 8: value = static_cast(*(UInt64*)pData); break; + default: err = kNIError_kCantDecode; break; } break; case kEncoding_Boolean: switch (aqSize) { case 1: value = (*(UInt8*)pData) ? 1.0 : 0.0; break; - default: isErr = true; break; + default: err = kNIError_kCantDecode; break; } break; case kEncoding_Cluster: if (type->IsTimestamp()) { Timestamp* t = (Timestamp*) pData; value = t->ToDouble(); + } else { + err = kNIError_kCantDecode; } break; default: - isErr = true; + err = kNIError_kCantDecode; break; } - if (isErr) { + if (err != kNIError_Success) { gPlatform.IO.Printf("(Error \"ReadDoubleFromMemory encoding:%d aqSize:%d\")\n", (int)encoding, (int)aqSize); } + if (errResult != nullptr) { + *errResult = err; + } + return value; } //------------------------------------------------------------ //! Write a IEEE754 double value to memory converting as necessary. -NIError WriteDoubleToMemory(TypeRef type, void* pData, Double value) +NIError WriteDoubleToMemory(TypeRef type, void* pData, const Double value) { NIError err = kNIError_Success; EncodingEnum encoding = type->BitEncoding(); @@ -2736,13 +2793,15 @@ NIError WriteDoubleToMemory(TypeRef type, void* pData, Double value) switch (aqSize) { // Beware that anything that's not exactly 0.0 will be true case 1: *(UInt8*)pData = value != 0.0 ? 1 : 0; break; - default: err = kNIError_kCantEncode; break; + default: err = kNIError_kCantEncode; break; } break; case kEncoding_Cluster: if (type->IsTimestamp()) { Timestamp* t = (Timestamp*) pData; *t = Timestamp(value); + } else { + err = kNIError_kCantEncode; } break; default: err = kNIError_kCantDecode; break; diff --git a/source/core/module_coreHelpers.js b/source/core/module_coreHelpers.js index cdcefedb5..be07f7905 100644 --- a/source/core/module_coreHelpers.js +++ b/source/core/module_coreHelpers.js @@ -56,6 +56,7 @@ fpSync = fn; }; + // Returns the length of a C string (excluding null terminator) Module.coreHelpers.findCStringLength = function (u8Array, startIndex) { var i, end = u8Array.length; @@ -211,6 +212,81 @@ } }; + // Source adapted from https://github.com/kripken/emscripten/blob/bd050e64bb0d9952df1344b8ea9356252328ad83/src/preamble.js#L488 + // Copies the given Javascript String object 'str' to the given byte array at address 'startIndex' encoded in UTF8 form. + // Use the function lengthBytesUTF8 to compute the exact number of bytes that this function will write. + // Parameters: + // str: the Javascript string to copy. + // outU8Array: the array to copy to. Each index in this array is assumed to be one 8-byte element. + // startIndex: The starting offset in the array to begin the copying. + // maxBytesToWrite: The maximum number of bytes this function can write to the array. maxBytesToWrite=0 does not write any bytes to the output. + // Returns the number of bytes written. + Module.coreHelpers.jsStringToSizedUTF8Array = function (str, outU8Array, startIndex, maxBytesToWrite) { + /* eslint-disable no-plusplus, id-length */ + // Parameter maxBytesToWrite is not optional. Negative values, 0, null, undefined and false each don't write out any bytes. + if (!(maxBytesToWrite > 0)) { + return 0; + } + var outIdx = startIndex; + var endIdx = outIdx + maxBytesToWrite; + for (var i = 0; i < str.length; ++i) { + // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! So decode UTF16->UTF32->UTF8. + // See http://unicode.org/faq/utf_bom.html#utf16-3 + // For UTF8 byte structure, see http://en.wikipedia.org/wiki/UTF-8#Description and https://www.ietf.org/rfc/rfc2279.txt and https://tools.ietf.org/html/rfc3629 + var u = str.charCodeAt(i); // possibly a lead surrogate + if (u >= 0xD800 && u <= 0xDFFF) { + u = 0x10000 + ((u & 0x3FF) << 10) | (str.charCodeAt(++i) & 0x3FF); + } + if (u <= 0x7F) { + if (outIdx >= endIdx) { + break; + } + outU8Array[outIdx++] = u; + } else if (u <= 0x7FF) { + if (outIdx + 1 >= endIdx) { + break; + } + outU8Array[outIdx++] = 0xC0 | (u >> 6); + outU8Array[outIdx++] = 0x80 | (u & 63); + } else if (u <= 0xFFFF) { + if (outIdx + 2 >= endIdx) { + break; + } + outU8Array[outIdx++] = 0xE0 | (u >> 12); + outU8Array[outIdx++] = 0x80 | ((u >> 6) & 63); + outU8Array[outIdx++] = 0x80 | (u & 63); + } else if (u <= 0x1FFFFF) { + if (outIdx + 3 >= endIdx) { + break; + } + outU8Array[outIdx++] = 0xF0 | (u >> 18); + outU8Array[outIdx++] = 0x80 | ((u >> 12) & 63); + outU8Array[outIdx++] = 0x80 | ((u >> 6) & 63); + outU8Array[outIdx++] = 0x80 | (u & 63); + } else if (u <= 0x3FFFFFF) { + if (outIdx + 4 >= endIdx) { + break; + } + outU8Array[outIdx++] = 0xF8 | (u >> 24); + outU8Array[outIdx++] = 0x80 | ((u >> 18) & 63); + outU8Array[outIdx++] = 0x80 | ((u >> 12) & 63); + outU8Array[outIdx++] = 0x80 | ((u >> 6) & 63); + outU8Array[outIdx++] = 0x80 | (u & 63); + } else { + if (outIdx + 5 >= endIdx) { + break; + } + outU8Array[outIdx++] = 0xFC | (u >> 30); + outU8Array[outIdx++] = 0x80 | ((u >> 24) & 63); + outU8Array[outIdx++] = 0x80 | ((u >> 18) & 63); + outU8Array[outIdx++] = 0x80 | ((u >> 12) & 63); + outU8Array[outIdx++] = 0x80 | ((u >> 6) & 63); + outU8Array[outIdx++] = 0x80 | (u & 63); + } + } + return outIdx - startIndex; + }; + Module.coreHelpers.jsCurrentBrowserFPS = function () { if (trackingFPS === false) { trackingFPS = true; diff --git a/source/core/module_typeHelpers.js b/source/core/module_typeHelpers.js new file mode 100644 index 000000000..31f348566 --- /dev/null +++ b/source/core/module_typeHelpers.js @@ -0,0 +1,347 @@ +// Using a modified UMD module format. Specifically a modified returnExports (no dependencies) version +(function (root, globalName, factory) { + 'use strict'; + var buildGlobalNamespace = function () { + var buildArgs = Array.prototype.slice.call(arguments); + return globalName.split('.').reduce(function (currObj, subNamespace, currentIndex, globalNameParts) { + var nextValue = currentIndex === globalNameParts.length - 1 ? factory.apply(undefined, buildArgs) : {}; + return currObj[subNamespace] === undefined ? (currObj[subNamespace] = nextValue) : currObj[subNamespace]; + }, root); + }; + + if (typeof define === 'function' && define.amd) { + // AMD. Register as a named module. + define(globalName, [], factory); + } else if (typeof module === 'object' && module.exports) { + // Node. "CommonJS-like" for environments like Node but not strict CommonJS + module.exports = factory(); + } else { + // Browser globals (root is window) + buildGlobalNamespace(); + } +}(this, 'NationalInstruments.Vireo.Core.assignTypeHelpers', function () { + 'use strict'; + + var assignTypeHelpers = function (Module) { + // Disable new-cap for the cwrap functions so the names can be the same in C and JS + /* eslint 'new-cap': ['error', {'capIsNewExceptions': [ + 'TypeRef_Name', + 'TypeRef_ElementName' + ]}], */ + Module.typeHelpers = {}; + + var TypeRef_Name = Module.cwrap('TypeRef_Name', 'string', ['number']); + var TypeRef_ElementName = Module.cwrap('TypeRef_ElementName', 'string', ['number']); + + // Private instance functions + var validateVisitMethod = function (fn, fnName) { + if (typeof fn !== 'function') { + throw new Error('Visitor must have a method named `' + fnName + '`. Found: ' + fn); + } + }; + + var dispatchVisitBoolean = function (typeVisitor, valueRef, data) { + var visitFn = typeVisitor.visitBoolean; + validateVisitMethod(visitFn, 'visitBoolean'); + return visitFn.call(typeVisitor, valueRef, data); + }; + + var dispatchVisitEnum = function (typeVisitor, valueRef, data) { + var sizeOfEnum = Module.typeHelpers.topAQSize(valueRef.typeRef); + var visitFn = undefined; + var fnName = ''; + switch (sizeOfEnum) { + case 1: + visitFn = typeVisitor.visitEnum8; + fnName = 'visitEnum8'; + break; + case 2: + visitFn = typeVisitor.visitEnum16; + fnName = 'visitEnum16'; + break; + case 4: + visitFn = typeVisitor.visitEnum32; + fnName = 'visitEnum32'; + break; + default: + throw new Error('Unexpected size for Enum. Found: ' + sizeOfEnum); + } + + validateVisitMethod(visitFn, fnName); + return visitFn.call(typeVisitor, valueRef, data); + }; + + var dispatchVisitInteger = function (typeVisitor, valueRef, data) { + var typeRef = valueRef.typeRef; + var isSignedInteger = Module.typeHelpers.isSigned(typeRef); + var sizeOfInteger = Module.typeHelpers.topAQSize(typeRef); + var visitFn = undefined; + var fnName = ''; + if (isSignedInteger === true) { + switch (sizeOfInteger) { + case 1: + visitFn = typeVisitor.visitInt8; + fnName = 'visitInt8'; + break; + case 2: + visitFn = typeVisitor.visitInt16; + fnName = 'visitInt16'; + break; + case 4: + visitFn = typeVisitor.visitInt32; + fnName = 'visitInt32'; + break; + case 8: + visitFn = typeVisitor.visitInt64; + fnName = 'visitInt64'; + break; + default: + throw new Error('Unexpected size for Integer. Found: ' + sizeOfInteger); + } + } else { + switch (sizeOfInteger) { + case 1: + visitFn = typeVisitor.visitUInt8; + fnName = 'visitUInt8'; + break; + case 2: + visitFn = typeVisitor.visitUInt16; + fnName = 'visitUInt16'; + break; + case 4: + visitFn = typeVisitor.visitUInt32; + fnName = 'visitUInt32'; + break; + case 8: + visitFn = typeVisitor.visitUInt64; + fnName = 'visitUInt64'; + break; + default: + throw new Error('Unexpected size for Unsigned Integer. Found: ' + sizeOfInteger); + } + } + + validateVisitMethod(visitFn, fnName); + return visitFn.call(typeVisitor, valueRef, data); + }; + + var dispatchVisitFloat = function (typeVisitor, valueRef, data) { + var typeRef = valueRef.typeRef; + var sizeOfFloat = Module.typeHelpers.topAQSize(typeRef); + var visitFn; + var fnName = ''; + switch (sizeOfFloat) { + case 4: + visitFn = typeVisitor.visitSingle; + fnName = 'visitSingle'; + break; + case 8: + visitFn = typeVisitor.visitDouble; + fnName = 'visitDouble'; + break; + default: + throw new Error('Unexpected size for a Float value. Found: ' + sizeOfFloat); + } + + validateVisitMethod(visitFn, fnName); + return visitFn.call(typeVisitor, valueRef, data); + }; + + var dispatchVisitString = function (typeVisitor, valueRef, data) { + var visitFn = typeVisitor.visitString; + validateVisitMethod(visitFn, 'visitString'); + return visitFn.call(typeVisitor, valueRef, data); + }; + + var dispatchVisitComplex = function (typeVisitor, valueRef, data) { + var typeRef = valueRef.typeRef, + sizeOfComplex = Module.typeHelpers.topAQSize(typeRef), + visitFn, + fnName; + switch (sizeOfComplex) { + case 8: + visitFn = typeVisitor.visitComplexSingle; + fnName = 'visitComplexSingle'; + break; + case 16: + visitFn = typeVisitor.visitComplexDouble; + fnName = 'visitComplexDouble'; + break; + default: + throw new Error('Unexpected size for a Complex value. Found: ' + sizeOfComplex); + } + + validateVisitMethod(visitFn, fnName); + return visitFn.call(typeVisitor, valueRef, data); + }; + + var dispatchVisitAnalogWaveform = function (typeVisitor, valueRef, data) { + var visitFn = typeVisitor.visitAnalogWaveform; + validateVisitMethod(visitFn, 'visitAnalogWaveform'); + return visitFn.call(typeVisitor, valueRef, data); + }; + + var dispatchVisitTimestamp = function (typeVisitor, valueRef, data) { + var visitFn = typeVisitor.visitTimestamp; + validateVisitMethod(visitFn, 'visitTimestamp'); + return visitFn.call(typeVisitor, valueRef, data); + }; + + var dispatchVisitPath = function (typeVisitor, valueRef, data) { + var visitFn = typeVisitor.visitPath; + validateVisitMethod(visitFn, 'visitPath'); + return visitFn.call(typeVisitor, valueRef, data); + }; + + var dispatchVisitArray = function (typeVisitor, valueRef, data) { + var visitFn = typeVisitor.visitArray; + validateVisitMethod(visitFn, 'visitArray'); + return visitFn.call(typeVisitor, valueRef, data); + }; + + var dispatchVisitCluster = function (typeVisitor, valueRef, data) { + var visitFn = typeVisitor.visitCluster; + validateVisitMethod(visitFn, 'visitCluster'); + return visitFn.call(typeVisitor, valueRef, data); + }; + + // Exported functions + Module.typeHelpers.topAQSize = function (typeRef) { + return Module._TypeRef_TopAQSize(typeRef); + }; + + Module.typeHelpers.typeName = function (typeRef) { + return TypeRef_Name(Module.eggShell.v_userShell, typeRef); + }; + + Module.typeHelpers.typeRank = function (typeRef) { + return Module._TypeRef_Rank(typeRef); + }; + + Module.typeHelpers.elementName = function (typeRef) { + return TypeRef_ElementName(Module.eggShell.v_userShell, typeRef); + }; + + Module.typeHelpers.subElementCount = function (typeRef) { + return Module._TypeRef_SubElementCount(typeRef); + }; + + Module.typeHelpers.subElementByIndex = function (typeRef, index) { + return Module._TypeRef_GetSubElementByIndex(typeRef, index); + }; + + Module.typeHelpers.isCluster = function (typeRef) { + return Module._TypeRef_IsCluster(typeRef) !== 0; + }; + + Module.typeHelpers.isArray = function (typeRef) { + return Module._TypeRef_IsArray(typeRef) !== 0; + }; + + Module.typeHelpers.isBoolean = function (typeRef) { + return Module._TypeRef_IsBoolean(typeRef) !== 0; + }; + + Module.typeHelpers.isInteger = function (typeRef) { + return Module._TypeRef_IsInteger(typeRef) !== 0; + }; + + Module.typeHelpers.isSigned = function (typeRef) { + return Module._TypeRef_IsSigned(typeRef) !== 0; + }; + + Module.typeHelpers.isEnum = function (typeRef) { + return Module._TypeRef_IsEnum(typeRef) !== 0; + }; + + Module.typeHelpers.isFloat = function (typeRef) { + return Module._TypeRef_IsFloat(typeRef) !== 0; + }; + + Module.typeHelpers.isString = function (typeRef) { + return Module._TypeRef_IsString(typeRef) !== 0; + }; + + Module.typeHelpers.isPath = function (typeRef) { + return Module._TypeRef_IsPath(typeRef) !== 0; + }; + + Module.typeHelpers.isTimestamp = function (typeRef) { + return Module._TypeRef_IsTimestamp(typeRef) !== 0; + }; + + Module.typeHelpers.isComplex = function (typeRef) { + return Module._TypeRef_IsComplex(typeRef) !== 0; + }; + + Module.typeHelpers.isAnalogWaveform = function (typeRef) { + return Module._TypeRef_IsAnalogWaveform(typeRef) !== 0; + }; + + var typeHandlers = [ + { + typeChecker: Module.typeHelpers.isBoolean, + dispatcher: dispatchVisitBoolean + }, + { + // Enum is wrapping an integer, so it needs to be evaluated first. + typeChecker: Module.typeHelpers.isEnum, + dispatcher: dispatchVisitEnum + }, + { + typeChecker: Module.typeHelpers.isInteger, + dispatcher: dispatchVisitInteger + }, + { + typeChecker: Module.typeHelpers.isFloat, + dispatcher: dispatchVisitFloat + }, + { + // String is an array of UTF-8 chars so it is evaluated before array. + typeChecker: Module.typeHelpers.isString, + dispatcher: dispatchVisitString + }, + { + typeChecker: Module.typeHelpers.isComplex, + dispatcher: dispatchVisitComplex + }, + { + typeChecker: Module.typeHelpers.isAnalogWaveform, + dispatcher: dispatchVisitAnalogWaveform + }, + { + typeChecker: Module.typeHelpers.isTimestamp, + dispatcher: dispatchVisitTimestamp + }, + { + typeChecker: Module.typeHelpers.isPath, + dispatcher: dispatchVisitPath + }, + { + typeChecker: Module.typeHelpers.isArray, + dispatcher: dispatchVisitArray + }, + { + // Cluster is evaluated last because Complex, AnalogWaveform, Path and Timestamps + // are internally also clusters. + typeChecker: Module.typeHelpers.isCluster, + dispatcher: dispatchVisitCluster + } + ]; + + Module.typeHelpers.findTypeDispatcher = function (typeRef) { + var i = 0, + typeHandler; + + for (i = 0; i < typeHandlers.length; i += 1) { + typeHandler = typeHandlers[i]; + if (typeHandler.typeChecker(typeRef) === true) { + return typeHandler.dispatcher; + } + } + return undefined; + }; + }; + + return assignTypeHelpers; +})); diff --git a/source/core/vireo.loader.js b/source/core/vireo.loader.js index 6ea8bd06c..2047dba2a 100644 --- a/source/core/vireo.loader.js +++ b/source/core/vireo.loader.js @@ -16,6 +16,7 @@ define(globalName, [ 'NationalInstruments.Vireo.Core.createVireoCore', 'NationalInstruments.Vireo.Core.assignCoreHelpers', + 'NationalInstruments.Vireo.Core.assignTypeHelpers', 'NationalInstruments.Vireo.ModuleBuilders.assignEggShell', 'NationalInstruments.Vireo.ModuleBuilders.assignHttpClient', 'NationalInstruments.Vireo.ModuleBuilders.assignJavaScriptInvoke', @@ -33,6 +34,7 @@ module.exports = factory( vireoCore, require('../../source/core/module_coreHelpers.js'), + require('../../source/core/module_typeHelpers.js'), require('../../source/io/module_eggShell.js'), require('../../source/io/module_httpClient.js'), require('../../source/io/module_javaScriptInvoke.js'), @@ -44,6 +46,7 @@ buildGlobalNamespace( root.NationalInstruments.Vireo.Core.createVireoCore, root.NationalInstruments.Vireo.Core.assignCoreHelpers, + root.NationalInstruments.Vireo.Core.assignTypeHelpers, root.NationalInstruments.Vireo.ModuleBuilders.assignEggShell, root.NationalInstruments.Vireo.ModuleBuilders.assignHttpClient, root.NationalInstruments.Vireo.ModuleBuilders.assignJavaScriptInvoke, @@ -86,6 +89,11 @@ if (typeof createVireoCore !== 'function') { throw new Error('createVireoCore could not be found, make sure to build and include vireo.js before using'); } + + // Save a Vireo class reference on the module instance so the instance can access static functions on the class + // TODO(mraj) Instead use the static functions directly when we switch to ES6 modules and can resolve the Vireo class correctly + Module.Vireo = Vireo; + createVireoCore(Module); moduleBuilders.forEach(function (currBuilder) { currBuilder(Module, that); diff --git a/source/include/CEntryPoints.h b/source/include/CEntryPoints.h index 5f3b567fd..78874138b 100644 --- a/source/include/CEntryPoints.h +++ b/source/include/CEntryPoints.h @@ -21,6 +21,9 @@ typedef enum { kEggShellResult_UnexpectedObjectType = 2, kEggShellResult_InvalidResultPointer = 3, kEggShellResult_UnableToCreateReturnBuffer = 4, + kEggShellResult_InvalidTypeRef = 5, + kEggShellResult_MismatchedArrayRank = 6, + kEggShellResult_UnableToParseData = 7, } EggShellResult; //------------------------------------------------------------ //! TypeManager functions @@ -35,22 +38,26 @@ VIREO_EXPORT Int32 EggShell_PeekMemory(TypeManagerRef tm, const char* viName, co Int32 bufferSize, char* buffer); VIREO_EXPORT Int32 EggShell_PokeMemory(TypeManagerRef tm, const char* viName, const char* eltName, Int32 bufferSize, char* buffer); -VIREO_EXPORT void EggShell_WriteDouble(TypeManagerRef tm, const char* viName, const char* eltName, Double d); -VIREO_EXPORT Double EggShell_ReadDouble(TypeManagerRef tm, const char* viName, const char* eltName); -VIREO_EXPORT void EggShell_WriteValueString(TypeManagerRef tm, const char* viName, const char* eltName, - const char* format, const char* value); -VIREO_EXPORT const char* EggShell_ReadValueString(TypeManagerRef tm, const char* viName, const char* eltName, - const char* format); +VIREO_EXPORT EggShellResult EggShell_FindValue(TypeManagerRef tm, const char* viName, const char* eltName, TypeRef* typeRefLocation, void** dataRefLocation); +VIREO_EXPORT EggShellResult EggShell_FindSubValue(TypeManagerRef tm, const TypeRef type, void *start, const char* eltName, + TypeRef* typeRefLocation, void** dataRefLocation); +VIREO_EXPORT EggShellResult EggShell_WriteDouble(TypeManagerRef tm, const TypeRef actualType, void* pData, Double value); +VIREO_EXPORT EggShellResult EggShell_ReadDouble(TypeManagerRef tm, const TypeRef actualType, const void* pData, Double* result); +VIREO_EXPORT EggShellResult EggShell_WriteValueString(TypeManagerRef tm, TypeRef typeRef, void* pData, const char* format, const char* value); +VIREO_EXPORT EggShellResult EggShell_ReadValueString(TypeManagerRef tm, TypeRef typeRef, void* pData, const char* format, UInt8** valueString); VIREO_EXPORT EggShellResult EggShell_GetPointer(TypeManagerRef tm, const char* viName, const char* elementName, void** dataPointer, void** typePointer); VIREO_EXPORT Int32 EggShell_GetArrayDimLength(TypeManagerRef tm, const char* viName, const char* eltName, Int32 dim); -VIREO_EXPORT Int32 EggShell_ResizeArray(TypeManagerRef tm, const char* viName, const char* eltName, - Int32 rank, Int32* newLengths); +VIREO_EXPORT EggShellResult EggShell_ResizeArray(TypeManagerRef tm, const TypeRef actualType, const void* pData, + Int32 rank, Int32 dimensionLengths[]); VIREO_EXPORT EggShellResult Data_ValidateArrayType(TypeManagerRef tm, TypeRef typeRef); VIREO_EXPORT void* Data_GetStringBegin(StringRef stringObject); VIREO_EXPORT Int32 Data_GetStringLength(StringRef stringObject); VIREO_EXPORT EggShellResult Data_GetArrayMetadata(TypeManagerRef tm, TypedArrayCoreRef arrayObject, char** arrayTypeName, Int32* arrayRank, unsigned char** arrayBegin); +VIREO_EXPORT void* Data_GetArrayBegin(const void* pData); +VIREO_EXPORT void Data_GetArrayDimensions(const void* pData, IntIndex dimensionsLengths[]); +VIREO_EXPORT Int32 Data_GetArrayLength(const void* pData); VIREO_EXPORT Int32 Data_GetArrayDimLength(TypeManagerRef tm, TypedArrayCoreRef arrayObject, Int32 dim); VIREO_EXPORT Int32 Data_ResizeArray(TypeManagerRef tm, TypedArrayCoreRef arrayObject, Int32 rank, Int32* newLengths); VIREO_EXPORT void Data_WriteString(TypeManagerRef tm, StringRef stringObject, const unsigned char* buffer, @@ -69,7 +76,8 @@ VIREO_EXPORT Boolean TypeRef_IsValid(TypeRef typeRef); VIREO_EXPORT Boolean TypeRef_HasCustomDefault(TypeRef typeRef); VIREO_EXPORT EncodingEnum TypeRef_BitEncoding(TypeRef typeRef); VIREO_EXPORT Int32 TypeRef_Alignment(TypeRef typeRef); -VIREO_EXPORT void TypeRef_Name(TypeRef typeRef, Int32* bufferSize, char* buffer); +VIREO_EXPORT const char* TypeRef_Name(TypeManagerRef typeManager, TypeRef typeRef); +VIREO_EXPORT const char* TypeRef_ElementName(TypeManagerRef typeManager, TypeRef typeRef); VIREO_EXPORT Int32 TypeRef_ElementOffset(TypeRef typeRef); VIREO_EXPORT Int32 TypeRef_Rank(TypeRef typeRef); VIREO_EXPORT PointerTypeEnum TypeRef_PointerType(TypeRef typeRef); @@ -77,6 +85,18 @@ VIREO_EXPORT TypeRef TypeRef_Next(TypeRef typeRef); VIREO_EXPORT UsageTypeEnum TypeRef_ElementUsageType(TypeRef typeRef); VIREO_EXPORT Int32 TypeRef_SubElementCount(TypeRef typeRef); VIREO_EXPORT TypeRef TypeRef_GetSubElementByIndex(TypeRef typeRef, Int32 index); +VIREO_EXPORT Boolean TypeRef_IsCluster(TypeRef typeRef); +VIREO_EXPORT Boolean TypeRef_IsArray(TypeRef typeRef); +VIREO_EXPORT Boolean TypeRef_IsBoolean(TypeRef typeRef); +VIREO_EXPORT Boolean TypeRef_IsInteger(TypeRef typeRef); +VIREO_EXPORT Boolean TypeRef_IsSigned(TypeRef typeRef); +VIREO_EXPORT Boolean TypeRef_IsEnum(TypeRef typeRef); +VIREO_EXPORT Boolean TypeRef_IsFloat(TypeRef typeRef); +VIREO_EXPORT Boolean TypeRef_IsString(TypeRef typeRef); +VIREO_EXPORT Boolean TypeRef_IsPath(TypeRef typeRef); +VIREO_EXPORT Boolean TypeRef_IsTimestamp(TypeRef typeRef); +VIREO_EXPORT Boolean TypeRef_IsComplex(TypeRef typeRef); +VIREO_EXPORT Boolean TypeRef_IsAnalogWaveform(TypeRef typeRef); //------------------------------------------------------------ //! TypedBlock functions VIREO_EXPORT Int32 Data_RawBlockSize(TypedBlock* object); diff --git a/source/include/StringUtilities.h b/source/include/StringUtilities.h index ed665ebc1..447243954 100644 --- a/source/include/StringUtilities.h +++ b/source/include/StringUtilities.h @@ -328,7 +328,7 @@ class SubString : public SubVector //! based on previously determined token trait. void TrimQuotedString(TokenTraits tt); - IntIndex FindFirstMatch(SubString* searchString, IntIndex offset, Boolean ignoreCase); + IntIndex FindFirstMatch(const SubString* searchString, IntIndex offset, Boolean ignoreCase); }; //------------------------------------------------------------ diff --git a/source/include/TypeAndDataManager.h b/source/include/TypeAndDataManager.h index 06ce4e4a8..138b52465 100644 --- a/source/include/TypeAndDataManager.h +++ b/source/include/TypeAndDataManager.h @@ -347,8 +347,8 @@ class TypeManager // Utility functions to read and write numbers to non aligned memory based on size and encoding IntMax ReadIntFromMemory(TypeRef type, void* pData); NIError WriteIntToMemory(TypeRef type, void* pData, IntMax value); -Double ReadDoubleFromMemory(TypeRef type, void* pData); -NIError WriteDoubleToMemory(TypeRef type, void* pData, Double value); +Double ReadDoubleFromMemory(TypeRef type, const void* pData, NIError* errResult = nullptr); +NIError WriteDoubleToMemory(TypeRef type, void* pData, const Double value); IntMax ConvertNumericRange(EncodingEnum encoding, Int32 size, IntMax input); //------------------------------------------------------------ //! Banker's rounding for Doubles. @@ -463,6 +463,8 @@ class TypeCommon static const SubString TypeComplexSingle; static const SubString TypeComplexDouble; static const SubString TypeJavaScriptRefNum; + static const SubString TypePath; + static const SubString TypeAnalogWaveform; static const SubString TypeStaticTypeAndData; explicit TypeCommon(TypeManagerRef typeManager); @@ -610,13 +612,17 @@ class TypeCommon Boolean IsA(TypeRef otherType); Boolean IsA(TypeRef otherType, Boolean compatibleArrays); Boolean IsNumeric(); + Boolean IsInteger(); + Boolean IsSignedInteger(); Boolean IsInteger64(); Boolean IsFloat(); Boolean IsBoolean(); Boolean IsString(); + Boolean IsPath(); Boolean IsTimestamp(); Boolean IsComplex(); Boolean IsJavaScriptRefNum(); + Boolean IsAnalogWaveform(); Boolean IsIntrinsicClusterDataType(SubString *foundTypeName); // Returns true for builtin data types such as Timestamp, Complex, etc //! Size of the type in bits including padding. If the type is bit level it's the raw bit size with no padding. diff --git a/source/io/HttpClient.cpp b/source/io/HttpClient.cpp index 94d73a685..0a344afbe 100644 --- a/source/io/HttpClient.cpp +++ b/source/io/HttpClient.cpp @@ -37,7 +37,7 @@ extern "C" { extern void jsHttpClientGetHeader(UInt32, StringRef, StringRef, Boolean *, Int32 *, StringRef); extern void jsHttpClientHeaderExists(UInt32, StringRef, UInt32 *, StringRef, Boolean *, Int32 *, StringRef); extern void jsHttpClientListHeaders(UInt32, StringRef, Boolean *, Int32 *, StringRef); - extern void jsHttpClientMethod(HttpClientMethodId, UInt32, StringRef, StringRef, StringRef, + extern void jsHttpClientMethod(HttpClientMethodId, UInt32, StringRef, StringRef, TypeRef, StringRef*, Int32 *, StringRef, StringRef, UInt32 *, Boolean *, Int32 *, StringRef, OccurrenceRef); extern void jsHttpClientConfigCORS(UInt32, UInt32, Boolean *, Int32 *, StringRef); } @@ -293,6 +293,7 @@ VIREO_FUNCTION_SIGNATURE9(HttpClientGet, UInt32, StringRef, StringRef, Int32, St _Param(1), _Param(2), nullptr, + nullptr, _ParamPointer(3), _Param(4), _Param(5), @@ -344,6 +345,7 @@ VIREO_FUNCTION_SIGNATURE7(HttpClientHead, UInt32, StringRef, Int32, StringRef, U _Param(1), nullptr, nullptr, + nullptr, _ParamPointer(2), _Param(3), nullptr, @@ -395,7 +397,8 @@ VIREO_FUNCTION_SIGNATURE10(HttpClientPut, UInt32, StringRef, StringRef, StringRe _Param(0), _Param(1), _Param(2), - _Param(3), + _Param(3)->Type(), + _ParamPointer(3), _ParamPointer(4), _Param(5), _Param(6), @@ -448,6 +451,7 @@ VIREO_FUNCTION_SIGNATURE9(HttpClientDelete, UInt32, StringRef, StringRef, Int32, _Param(1), _Param(2), nullptr, + nullptr, _ParamPointer(3), _Param(4), _Param(5), @@ -499,7 +503,8 @@ VIREO_FUNCTION_SIGNATURE10(HttpClientPost, UInt32, StringRef, StringRef, StringR _Param(0), _Param(1), _Param(2), - _Param(3), + _Param(3)->Type(), + _ParamPointer(3), _ParamPointer(4), _Param(5), _Param(6), diff --git a/source/io/module_eggShell.js b/source/io/module_eggShell.js index 6b47a8173..12afbc6e1 100644 --- a/source/io/module_eggShell.js +++ b/source/io/module_eggShell.js @@ -32,12 +32,8 @@ 'Vireo_MaxExecWakeUpTime', 'EggShell_Create', 'EggShell_Delete', - 'EggShell_ReadDouble', - 'EggShell_WriteDouble', - 'EggShell_WriteValueString', 'EggShell_GetPointer', 'EggShell_GetArrayDimLength', - 'EggShell_ResizeArray', 'Data_ValidateArrayType', 'Data_GetStringBegin', 'Data_GetStringLength', @@ -75,16 +71,49 @@ // Private Instance Variables (per vireo instance) var NULL = 0; + var POINTER_SIZE = 4; + var DOUBLE_SIZE = 8; + var LENGTH_SIZE = 4; + + // Keep in sync with EggShellResult in CEntryPoints.h + var EGGSHELL_RESULT = { + SUCCESS: 0, + OBJECT_NOT_FOUND_AT_PATH: 1, + UNEXPECTED_OBJECT_TYPE: 2, + INVALID_RESULT_POINTER: 3, + UNABLE_TO_CREATE_RETURN_BUFFER: 4, + INVALID_TYPE_REF: 5, + MISMATCHED_ARRAY_RANK: 6, + UNABLE_TO_PARSE_DATA: 7 + }; + var eggShellResultEnum = {}; + eggShellResultEnum[EGGSHELL_RESULT.SUCCESS] = 'Success'; + eggShellResultEnum[EGGSHELL_RESULT.OBJECT_NOT_FOUND_AT_PATH] = 'ObjectNotFoundAtPath'; + eggShellResultEnum[EGGSHELL_RESULT.UNEXPECTED_OBJECT_TYPE] = 'UnexpectedObjectType'; + eggShellResultEnum[EGGSHELL_RESULT.INVALID_RESULT_POINTER] = 'InvalidResultPointer'; + eggShellResultEnum[EGGSHELL_RESULT.UNABLE_TO_CREATE_RETURN_BUFFER] = 'UnableToCreateReturnBuffer'; + eggShellResultEnum[EGGSHELL_RESULT.INVALID_TYPE_REF] = 'InvalidTypeRef'; + eggShellResultEnum[EGGSHELL_RESULT.MISMATCHED_ARRAY_RANK] = 'MismatchedArrayRank'; + eggShellResultEnum[EGGSHELL_RESULT.UNABLE_TO_PARSE_DATA] = 'UnableToParseData'; + + // Keep in sync with NIError in DataTypes.h + var niErrorEnum = { + 0: 'Success', + 1: 'InsufficientResources', + 2: 'ResourceNotFound', + 3: 'ArrayRankMismatch', + 4: 'CantDecode', + 5: 'CantEncode', + 6: 'LogicFailure', + 7: 'ValueTruncated' + }; + var Vireo_Version = Module.cwrap('Vireo_Version', 'number', []); var Vireo_MaxExecWakeUpTime = Module.cwrap('Vireo_MaxExecWakeUpTime', 'number', []); var EggShell_Create = Module.cwrap('EggShell_Create', 'number', ['number']); var EggShell_Delete = Module.cwrap('EggShell_Delete', 'number', ['number']); - var EggShell_ReadDouble = Module.cwrap('EggShell_ReadDouble', 'number', ['number', 'string', 'string']); - var EggShell_WriteDouble = Module.cwrap('EggShell_WriteDouble', 'void', ['number', 'string', 'string', 'number']); - var EggShell_WriteValueString = Module.cwrap('EggShell_WriteValueString', 'void', ['number', 'string', 'string', 'string', 'string']); var EggShell_GetPointer = Module.cwrap('EggShell_GetPointer', 'number', ['number', 'string', 'string', 'number', 'number']); var EggShell_GetArrayDimLength = Module.cwrap('EggShell_GetArrayDimLength', 'number', ['number', 'string', 'string', 'number']); - var EggShell_ResizeArray = Module.cwrap('EggShell_ResizeArray', 'number', ['number', 'string', 'string', 'number', 'number']); var Data_ValidateArrayType = Module.cwrap('Data_ValidateArrayType', 'number', ['number', 'number']); var Data_GetStringBegin = Module.cwrap('Data_GetStringBegin', 'number', []); var Data_GetStringLength = Module.cwrap('Data_GetStringLength', 'number', []); @@ -114,8 +143,8 @@ var Occurrence_Set = Module.cwrap('Occurrence_Set', 'void', ['number']); // Create shell for vireo instance - var v_root = EggShell_Create(0); - var v_userShell = EggShell_Create(v_root); + Module.eggShell.v_root = EggShell_Create(0); + Module.eggShell.v_userShell = EggShell_Create(Module.eggShell.v_root); // Exported functions Module.print = function (text) { @@ -162,38 +191,181 @@ Module.eggShell.maxExecWakeUpTime = publicAPI.eggShell.maxExecWakeUpTime = Vireo_MaxExecWakeUpTime; Module.eggShell.reboot = publicAPI.eggShell.reboot = function () { - EggShell_Delete(v_userShell); - EggShell_Delete(v_root); - v_root = EggShell_Create(0); - v_userShell = EggShell_Create(v_root); + EggShell_Delete(Module.eggShell.v_userShell); + EggShell_Delete(Module.eggShell.v_root); + Module.eggShell.v_root = EggShell_Create(0); + Module.eggShell.v_userShell = EggShell_Create(Module.eggShell.v_root); + }; + + Module.eggShell.createValueRef = function (typeRef, dataRef) { + return Object.freeze({ + typeRef: typeRef, + dataRef: dataRef + }); }; - Module.eggShell.readDouble = publicAPI.eggShell.readDouble = function (vi, path) { - return EggShell_ReadDouble(v_userShell, vi, path); + Module.eggShell.findValueRef = publicAPI.eggShell.findValueRef = function (vi, path) { + var stack = Module.stackSave(); + + var viStackPointer = Module.coreHelpers.writeJSStringToStack(vi); + var pathStackPointer = Module.coreHelpers.writeJSStringToStack(path); + var typeStackPointer = Module.stackAlloc(POINTER_SIZE); + var dataStackPointer = Module.stackAlloc(POINTER_SIZE); + + var eggShellResult = Module._EggShell_FindValue(Module.eggShell.v_userShell, viStackPointer, pathStackPointer, typeStackPointer, dataStackPointer); + if (eggShellResult !== EGGSHELL_RESULT.SUCCESS) { + throw new Error('A ValueRef could not be made for the following reason: ' + eggShellResultEnum[eggShellResult] + + ' (error code: ' + eggShellResult + ')' + + ' (vi name: ' + vi + ')' + + ' (path: ' + path + ')'); + } + + var typeRef = Module.getValue(typeStackPointer, 'i32'); + var dataRef = Module.getValue(dataStackPointer, 'i32'); + var valueRef = Module.eggShell.createValueRef(typeRef, dataRef); + + Module.stackRestore(stack); + return valueRef; }; - Module.eggShell.writeDouble = publicAPI.eggShell.writeDouble = function (vi, path, value) { - EggShell_WriteDouble(v_userShell, vi, path, value); + Module.eggShell.findSubValueRef = publicAPI.eggShell.findSubValueRef = function (valueRef, subPath) { + var stack = Module.stackSave(); + + var subPathStackPointer = Module.coreHelpers.writeJSStringToStack(subPath); + var typeStackPointer = Module.stackAlloc(POINTER_SIZE); + var dataStackPointer = Module.stackAlloc(POINTER_SIZE); + + var eggShellResult = Module._EggShell_FindSubValue(Module.eggShell.v_userShell, valueRef.typeRef, valueRef.dataRef, subPathStackPointer, typeStackPointer, dataStackPointer); + if (eggShellResult !== EGGSHELL_RESULT.SUCCESS) { + throw new Error('A ValueRef could not be made for the following reason: ' + eggShellResultEnum[eggShellResult] + + ' (error code: ' + eggShellResult + ')' + + ' (type name: ' + Module.typeHelpers.typeName(valueRef.typeRef) + ')' + + ' (subpath: ' + subPath + ')'); + } + + var typeRef = Module.getValue(typeStackPointer, 'i32'); + var dataRef = Module.getValue(dataStackPointer, 'i32'); + var subValueRef = Module.eggShell.createValueRef(typeRef, dataRef); + + Module.stackRestore(stack); + return subValueRef; + }; + + Module.eggShell.readValueRefObject = publicAPI.eggShell.readValueRefObject = function (valueRef) { + var typeRef = valueRef.typeRef; + var valueRefs = {}; + + if (Module.typeHelpers.isCluster(typeRef) === false) { + throw new Error('A ValueRefObject could not be made for the following reason: ' + eggShellResultEnum[EGGSHELL_RESULT.UNEXPECTED_OBJECT_TYPE] + + ' (error code: ' + EGGSHELL_RESULT.UNEXPECTED_OBJECT_TYPE + ')' + + ' (type name: ' + Module.typeHelpers.typeName(typeRef) + ')'); + } + + var fieldCount = Module.typeHelpers.subElementCount(typeRef); + + var fieldTypeRef, fieldNameEncoded, fieldName; + for (var i = 0; i < fieldCount; i += 1) { + fieldTypeRef = Module.typeHelpers.subElementByIndex(typeRef, i); + fieldNameEncoded = Module.typeHelpers.elementName(fieldTypeRef); + fieldName = Module.Vireo.decodeIdentifier(fieldNameEncoded); + valueRefs[fieldName] = Module.eggShell.findSubValueRef(valueRef, fieldNameEncoded); + } + + return valueRefs; + }; + + Module.eggShell.reflectOnValueRef = publicAPI.eggShell.reflectOnValueRef = function (typeVisitor, valueRef, data) { + if (typeof valueRef !== 'object' || valueRef === null) { + throw new Error('valueRef must be an object. Found: ' + valueRef); + } + + if (typeof typeVisitor !== 'object' || typeVisitor === null) { + throw new Error('typeVisitor must be an object. Found: ' + typeVisitor); + } + + var typeRef = valueRef.typeRef, + dispatchFunction = Module.typeHelpers.findTypeDispatcher(typeRef); + + if (dispatchFunction === undefined) { + throw new Error('Unexpected type. Is typeRef pointing to a valid type?. Type found: ' + typeRef === 0 ? 'invalid type' : Module.typeHelpers.typeName(typeRef)); + } + + return dispatchFunction(typeVisitor, valueRef, data); + }; + + Module.eggShell.readDouble = publicAPI.eggShell.readDouble = function (valueRef) { + var stack = Module.stackSave(); + var resultPointer = Module.stackAlloc(DOUBLE_SIZE); + + // TODO mraj should we try to resolve the typeref name on error for more context? + var eggShellResult = Module._EggShell_ReadDouble(Module.eggShell.v_userShell, valueRef.typeRef, valueRef.dataRef, resultPointer); + if (eggShellResult !== EGGSHELL_RESULT.SUCCESS) { + throw new Error('Could not run readDouble for the following reason: ' + eggShellResultEnum[eggShellResult] + + ' (error code: ' + eggShellResult + ')' + + ' (typeRef: ' + valueRef.typeRef + ')' + + ' (dataRef: ' + valueRef.dataRef + ')'); + } + var result = Module.getValue(resultPointer, 'double'); + + Module.stackRestore(stack); + return result; + }; + + Module.eggShell.writeDouble = publicAPI.eggShell.writeDouble = function (valueRef, value) { + if (typeof value !== 'number') { + throw new Error('Expected value to write to be of type number, instead got: ' + value); + } + + var eggShellResult = Module._EggShell_WriteDouble(Module.eggShell.v_userShell, valueRef.typeRef, valueRef.dataRef, value); + if (eggShellResult !== EGGSHELL_RESULT.SUCCESS) { + throw new Error('Could not run writeDouble for the following reason: ' + eggShellResultEnum[eggShellResult] + + ' (error code: ' + eggShellResult + ')' + + ' (typeRef: ' + valueRef.typeRef + ')' + + ' (dataRef: ' + valueRef.dataRef + ')'); + } }; - Module.eggShell.readJSON = publicAPI.eggShell.readJSON = function (vi, path) { + Module.eggShell.readJSON = publicAPI.eggShell.readJSON = function (valueRef) { var stack = Module.stackSave(); // Stack save only needed for input parameter string or array var type = 'JSON'; - var viStackPointer = Module.coreHelpers.writeJSStringToStack(vi); - var pathStackPointer = Module.coreHelpers.writeJSStringToStack(path); + var jsonStackDoublePointer = Module.stackAlloc(POINTER_SIZE); var typeStackPointer = Module.coreHelpers.writeJSStringToStack(type); - var responsePointer = Module._EggShell_ReadValueString(v_userShell, viStackPointer, pathStackPointer, typeStackPointer); - var responseLength = Module.coreHelpers.findCStringLength(Module.HEAPU8, responsePointer); - var response = Module.coreHelpers.sizedUtf8ArrayToJSString(Module.HEAPU8, responsePointer, responseLength); + var eggShellError = Module._EggShell_ReadValueString(Module.eggShell.v_userShell, valueRef.typeRef, valueRef.dataRef, typeStackPointer, jsonStackDoublePointer); + + if (eggShellError !== 0) { + throw new Error('Performing readJSON failed for the following reason: ' + eggShellResultEnum[eggShellError] + + ' (error code: ' + eggShellError + ')' + + ' (typeRef: ' + valueRef.typeRef + ')' + + ' (dataRef: ' + valueRef.dataRef + ')'); + } + + var jsonStackPointer = Module.getValue(jsonStackDoublePointer, 'i32'); + var responseLength = Module.coreHelpers.findCStringLength(Module.HEAPU8, jsonStackPointer); + var response = Module.coreHelpers.sizedUtf8ArrayToJSString(Module.HEAPU8, jsonStackPointer, responseLength); Module.stackRestore(stack); return response; }; - Module.eggShell.writeJSON = publicAPI.eggShell.writeJSON = function (vi, path, value) { - EggShell_WriteValueString(v_userShell, vi, path, 'JSON', value); + Module.eggShell.writeJSON = publicAPI.eggShell.writeJSON = function (valueRef, value) { + var stack = Module.stackSave(); + + var type = 'JSON'; + var valueStackPointer = Module.coreHelpers.writeJSStringToHeap(value); + var typeStackPointer = Module.coreHelpers.writeJSStringToStack(type); + + var eggShellError = Module._EggShell_WriteValueString(Module.eggShell.v_userShell, valueRef.typeRef, valueRef.dataRef, typeStackPointer, valueStackPointer); + if (eggShellError !== 0) { + throw new Error('Performing writeJSON failed for the following reason: ' + eggShellResultEnum[eggShellError] + + ' (error code: ' + eggShellError + ')' + + ' (typeRef: ' + valueRef.typeRef + ')' + + ' (dataRef: ' + valueRef.dataRef + ')'); + } + + Module._free(valueStackPointer); + Module.stackRestore(stack); }; var supportedArrayTypeConfig = { @@ -231,25 +403,136 @@ } }; - // Keep in sync with EggShellResult in CEntryPoints.h - var eggShellResultEnum = { - 0: 'Success', - 1: 'ObjectNotFoundAtPath', - 2: 'UnexpectedObjectType', - 3: 'InvalidResultPointer', - 4: 'UnableToCreateReturnBuffer' + Module.eggShell.dataGetArrayBegin = function (dataRef) { + return Module._Data_GetArrayBegin(dataRef); }; - // Keep in sync with NIError in DataTypes.h - var niErrorEnum = { - 0: 'Success', - 1: 'InsufficientResources', - 2: 'ResourceNotFound', - 3: 'ArrayRankMismatch', - 4: 'CantDecode', - 5: 'CantEncode', - 6: 'LogicFailure', - 7: 'ValueTruncated' + Module.eggShell.dataGetArrayLength = function (dataRef) { + return Module._Data_GetArrayLength(dataRef); + }; + + Module.eggShell.readString = publicAPI.eggShell.readString = function (valueRef) { + if (Module.typeHelpers.isString(valueRef.typeRef) === false) { + throw new Error('Performing readString failed for the following reason: ' + eggShellResultEnum[EGGSHELL_RESULT.UNEXPECTED_OBJECT_TYPE] + + ' (error code: ' + EGGSHELL_RESULT.UNEXPECTED_OBJECT_TYPE + ')' + + ' (typeRef: ' + valueRef.typeRef + ')' + + ' (dataRef: ' + valueRef.dataRef + ')'); + } + var arrayBegin = Module.eggShell.dataGetArrayBegin(valueRef.dataRef); + var totalLength = Module.eggShell.dataGetArrayLength(valueRef.dataRef); + var result = Module.coreHelpers.sizedUtf8ArrayToJSString(Module.HEAPU8, arrayBegin, totalLength); + return result; + }; + + Module.eggShell.writeString = publicAPI.eggShell.writeString = function (valueRef, inputString) { + if (Module.typeHelpers.isString(valueRef.typeRef) === false) { + throw new Error('Performing writeString failed for the following reason: ' + eggShellResultEnum[EGGSHELL_RESULT.UNEXPECTED_OBJECT_TYPE] + + ' (error code: ' + EGGSHELL_RESULT.UNEXPECTED_OBJECT_TYPE + ')' + + ' (typeRef: ' + valueRef.typeRef + ')' + + ' (dataRef: ' + valueRef.dataRef + ')'); + } + + if (typeof inputString !== 'string') { + throw new Error('Expected string input to be of type string, instead got: ' + inputString); + } + + var strLength = Module.lengthBytesUTF8(inputString); + Module.eggShell.resizeArray(valueRef, [strLength]); + var typedArray = Module.eggShell.readTypedArray(valueRef); + var bytesWritten = Module.coreHelpers.jsStringToSizedUTF8Array(inputString, typedArray, 0, strLength); + if (bytesWritten !== strLength) { + throw new Error('Could not write JS string to memory'); + } + }; + + var findCompatibleTypedArrayConstructor = function (typeRef) { + var subTypeRef, isSigned, size; + // String will go down the Array code path a bit as is so check before array checks + if (Module.typeHelpers.isString(typeRef)) { + return Uint8Array; // exposes UTF-8 encoded array to client + } else if (Module.typeHelpers.isArray(typeRef)) { + subTypeRef = Module.typeHelpers.subElementByIndex(typeRef, 0); + if (Module.typeHelpers.isBoolean(subTypeRef)) { + return Uint8Array; + } else if (Module.typeHelpers.isInteger(subTypeRef)) { // Used for Enums and Integers + isSigned = Module.typeHelpers.isSigned(subTypeRef); + size = Module.typeHelpers.topAQSize(subTypeRef); + if (isSigned === true) { + switch (size) { + case 1: + return Int8Array; + case 2: + return Int16Array; + case 4: + return Int32Array; + default: + return undefined; + } + } else { + switch (size) { + case 1: + return Uint8Array; + case 2: + return Uint16Array; + case 4: + return Uint32Array; + default: + return undefined; + } + } + } else if (Module.typeHelpers.isFloat(subTypeRef)) { + size = Module.typeHelpers.topAQSize(subTypeRef); + switch (size) { + case 4: + return Float32Array; + case 8: + return Float64Array; + default: + return undefined; + } + } + } + return undefined; + }; + + Module.eggShell.isTypedArrayCompatible = publicAPI.eggShell.isTypedArrayCompatible = function (valueRef) { + return findCompatibleTypedArrayConstructor(valueRef.typeRef) !== undefined; + }; + + Module.eggShell.getArrayDimensions = publicAPI.eggShell.getArrayDimensions = function (valueRef) { + if (!Module.typeHelpers.isArray(valueRef.typeRef)) { + throw new Error('Performing getArrayDimensions failed for the following reason: ' + eggShellResultEnum[EGGSHELL_RESULT.UNEXPECTED_OBJECT_TYPE] + + ' (error code: ' + EGGSHELL_RESULT.UNEXPECTED_OBJECT_TYPE + ')' + + ' (typeRef: ' + valueRef.typeRef + ')' + + ' (dataRef: ' + valueRef.dataRef + ')'); + } + + var rank = Module.typeHelpers.typeRank(valueRef.typeRef); + var stack = Module.stackSave(); + var dimensionsPointer = Module.stackAlloc(rank * LENGTH_SIZE); + Module._Data_GetArrayDimensions(valueRef.dataRef, dimensionsPointer); + var dimensions = []; + var i; + for (i = 0; i < rank; i += 1) { + dimensions.push(Module.getValue(dimensionsPointer + (i * LENGTH_SIZE), 'i32')); + } + Module.stackRestore(stack); + + return dimensions; + }; + + Module.eggShell.readTypedArray = publicAPI.eggShell.readTypedArray = function (valueRef) { + var TypedArrayConstructor = findCompatibleTypedArrayConstructor(valueRef.typeRef); + if (TypedArrayConstructor === undefined) { + throw new Error('Performing readTypedArray failed for the following reason: ' + eggShellResultEnum[EGGSHELL_RESULT.UNEXPECTED_OBJECT_TYPE] + + ' (error code: ' + EGGSHELL_RESULT.UNEXPECTED_OBJECT_TYPE + ')' + + ' (typeRef: ' + valueRef.typeRef + ')' + + ' (dataRef: ' + valueRef.dataRef + ')'); + } + var arrayBegin = Module.eggShell.dataGetArrayBegin(valueRef.dataRef); + var totalLength = Module.eggShell.dataGetArrayLength(valueRef.dataRef); + var typedArray = new TypedArrayConstructor(Module.buffer, arrayBegin, totalLength); + return typedArray; }; var groupByDimensionLength = function (arr, startIndex, arrLength, dimensionLength) { @@ -321,8 +604,9 @@ var vireoObjectPointer = Module._malloc(4); var vireoTypePointer = Module._malloc(4); + // **DEPRECATED** Module.eggShell.getNumericArray = publicAPI.eggShell.getNumericArray = function (vi, path) { - var eggShellResult = EggShell_GetPointer(v_userShell, vi, path, vireoObjectPointer, vireoTypePointer); + var eggShellResult = EggShell_GetPointer(Module.eggShell.v_userShell, vi, path, vireoObjectPointer, vireoTypePointer); if (eggShellResult !== 0) { throw new Error('Getting the array pointer failed for the following reason: ' + eggShellResultEnum[eggShellResult] + @@ -333,7 +617,7 @@ var arrayVireoPointer = Module.getValue(vireoObjectPointer, 'i32'); var typePointer = Module.getValue(vireoTypePointer, 'i32'); - eggShellResult = Data_ValidateArrayType(v_userShell, typePointer); + eggShellResult = Data_ValidateArrayType(Module.eggShell.v_userShell, typePointer); if (eggShellResult !== 0) { throw new Error('Getting the array pointer failed for the following reason: ' + eggShellResultEnum[eggShellResult] + @@ -359,26 +643,38 @@ return convertFlatArraytoNArray(actualArray, arrayInfo.dimensionLengths); }; + // **DEPRECATED** Module.eggShell.getArrayDimLength = publicAPI.eggShell.getArrayDimLength = function (vi, path, dim) { - return EggShell_GetArrayDimLength(v_userShell, vi, path, dim); + return EggShell_GetArrayDimLength(Module.eggShell.v_userShell, vi, path, dim); }; - Module.eggShell.resizeArray = publicAPI.eggShell.resizeArray = function (vi, path, newDimensionSizes) { - var int32Byte = 4; - var rank = newDimensionSizes.length; - var newLengths = Module._malloc(rank * int32Byte); - - for (var i = 0; i < rank; i += 1) { - Module.setValue(newLengths + (i * int32Byte), newDimensionSizes[i], 'i32'); + Module.eggShell.resizeArray = publicAPI.eggShell.resizeArray = function (valueRef, newDimensions) { + if (!Array.isArray(newDimensions)) { + throw new Error('Expected newDimensions to be an array of dimension lengths, instead got: ' + newDimensions); } - - var success = EggShell_ResizeArray(v_userShell, vi, path, rank, newLengths); - - Module._free(newLengths); - - return success; + var stack = Module.stackSave(); + var newDimensionsLength = newDimensions.length; + var dimensionsPointer = Module.stackAlloc(newDimensionsLength * LENGTH_SIZE); + var i, currentDimension; + for (i = 0; i < newDimensionsLength; i += 1) { + currentDimension = newDimensions[i]; + + if (typeof currentDimension !== 'number') { + throw new Error('Expected all dimensions of newDimensions to be numeric values for dimension length, instead got' + currentDimension); + } + Module.setValue(dimensionsPointer + (i * LENGTH_SIZE), currentDimension, 'i32'); + } + var eggShellResult = Module._EggShell_ResizeArray(Module.eggShell.v_userShell, valueRef.typeRef, valueRef.dataRef, newDimensionsLength, dimensionsPointer); + if (eggShellResult !== EGGSHELL_RESULT.SUCCESS) { + throw new Error('Resizing the array failed for the following reason: ' + eggShellResultEnum[eggShellResult] + + ' (error code: ' + eggShellResult + ')' + + ' (typeRef: ' + valueRef.typeRef + ')' + + ' (dataRef: ' + valueRef.dataRef + ')'); + } + Module.stackRestore(stack); }; + // **DEPRECATED** Module.eggShell.dataReadString = function (stringPointer) { var begin = Data_GetStringBegin(stringPointer); var length = Data_GetStringLength(stringPointer); @@ -387,6 +683,7 @@ }; // Note this function is tied to the underlying buffer, a copy is not made + // **DEPRECATED** Module.eggShell.dataReadStringAsArray_NoCopy = function (stringPointer) { var begin = Data_GetStringBegin(stringPointer); var length = Data_GetStringLength(stringPointer); @@ -394,70 +691,83 @@ }; // Source should be a JS String + // **DEPRECATED** Module.eggShell.dataWriteString = function (destination, source) { var sourceLength = Module.lengthBytesUTF8(source); - Data_WriteString(v_userShell, destination, source, sourceLength); + Data_WriteString(Module.eggShell.v_userShell, destination, source, sourceLength); }; // Source should be a JS array of numbers or a TypedArray of Uint8Array or Int8Array + // **DEPRECATED** Module.eggShell.dataWriteStringFromArray = function (destination, source) { var sourceHeapPointer = Module._malloc(source.length); Module.writeArrayToMemory(source, sourceHeapPointer); - Module._Data_WriteString(v_userShell, destination, sourceHeapPointer, source.length); + Module._Data_WriteString(Module.eggShell.v_userShell, destination, sourceHeapPointer, source.length); Module._free(sourceHeapPointer); }; + // **DEPRECATED** Module.eggShell.dataReadBoolean = function (booleanPointer) { var numericValue = Data_ReadBoolean(booleanPointer); return numericValue !== 0; }; + // **DEPRECATED** Module.eggShell.dataReadInt8 = function (intPointer) { var numericValue = Data_ReadInt8(intPointer); return numericValue; }; + // **DEPRECATED** Module.eggShell.dataReadInt16 = function (intPointer) { var numericValue = Data_ReadInt16(intPointer); return numericValue; }; + // **DEPRECATED** Module.eggShell.dataReadInt32 = function (intPointer) { var numericValue = Data_ReadInt32(intPointer); return numericValue; }; + // **DEPRECATED** Module.eggShell.dataReadUInt8 = function (intPointer) { var numericValue = Data_ReadUInt8(intPointer); return numericValue; }; + // **DEPRECATED** Module.eggShell.dataReadUInt16 = function (intPointer) { var numericValue = Data_ReadUInt16(intPointer); return numericValue; }; + // **DEPRECATED** Module.eggShell.dataReadUInt32 = function (intPointer) { var numericValue = Data_ReadUInt32(intPointer); return numericValue; }; + // **DEPRECATED** Module.eggShell.dataReadSingle = function (singlePointer) { var numericValue = Data_ReadSingle(singlePointer); return numericValue; }; + // **DEPRECATED** Module.eggShell.dataReadDouble = function (doublePointer) { var numericValue = Data_ReadDouble(doublePointer); return numericValue; }; + // **DEPRECATED** Module.eggShell.dataReadTypedArray = function (arrayPointer) { return Module.eggShell.dataReadNumericArrayAsTypedArray(arrayPointer).array; }; + // **DEPRECATED** Module.eggShell.dataReadNumericArrayAsTypedArray = function (arrayPointer) { - var eggShellResult = Data_GetArrayMetadata(v_userShell, arrayPointer, arrayTypeNameDoublePointer, arrayRankPointer, arrayBeginPointer); + var eggShellResult = Data_GetArrayMetadata(Module.eggShell.v_userShell, arrayPointer, arrayTypeNameDoublePointer, arrayRankPointer, arrayBeginPointer); if (eggShellResult !== 0) { throw new Error('Querying Array Metadata failed for the following reason: ' + eggShellResultEnum[eggShellResult] + @@ -495,7 +805,7 @@ var arrayLength = 1; dimensionLengths = []; for (var j = 0; j < arrayRank; j += 1) { - dimensionLengths[j] = Data_GetArrayDimLength(v_userShell, arrayPointer, j); + dimensionLengths[j] = Data_GetArrayDimLength(Module.eggShell.v_userShell, arrayPointer, j); arrayLength *= dimensionLengths[j]; } @@ -507,53 +817,63 @@ }; }; + // **DEPRECATED** Module.eggShell.dataWriteBoolean = function (booleanPointer, booleanValue) { var numericValue = booleanValue ? 1 : 0; Data_WriteBoolean(booleanPointer, numericValue); }; + // **DEPRECATED** Module.eggShell.dataWriteInt8 = function (destination, value) { Data_WriteInt8(destination, value); }; + // **DEPRECATED** Module.eggShell.dataWriteInt16 = function (destination, value) { Data_WriteInt16(destination, value); }; + // **DEPRECATED** Module.eggShell.dataWriteInt32 = function (destination, value) { Data_WriteInt32(destination, value); }; + // **DEPRECATED** Module.eggShell.dataWriteUInt8 = function (destination, value) { Data_WriteUInt8(destination, value); }; + // **DEPRECATED** Module.eggShell.dataWriteUInt16 = function (destination, value) { Data_WriteUInt16(destination, value); }; + // **DEPRECATED** Module.eggShell.dataWriteUInt32 = function (destination, value) { Data_WriteUInt32(destination, value); }; + // **DEPRECATED** Module.eggShell.dataWriteSingle = function (destination, value) { Data_WriteSingle(destination, value); }; + // **DEPRECATED** Module.eggShell.dataWriteDouble = function (destination, value) { Data_WriteDouble(destination, value); }; + // **DEPRECATED** Module.eggShell.dataWriteTypedArray = function (destination, value) { var int32Byte = 4; var rank = 1; var newLengths = Module._malloc(rank * int32Byte); Module.setValue(newLengths, value.length, 'i32'); - Data_ResizeArray(v_userShell, destination, rank, newLengths); + Data_ResizeArray(Module.eggShell.v_userShell, destination, rank, newLengths); Module._free(newLengths); - var eggShellResult = Data_GetArrayMetadata(v_userShell, destination, arrayTypeNameDoublePointer, arrayRankPointer, arrayBeginPointer); + var eggShellResult = Data_GetArrayMetadata(Module.eggShell.v_userShell, destination, arrayTypeNameDoublePointer, arrayRankPointer, arrayBeginPointer); if (eggShellResult !== 0) { throw new Error('Querying Array Metadata failed for the following reason: ' + eggShellResultEnum[eggShellResult] + @@ -596,7 +916,7 @@ origPrintErr(textErr); }; - var result = Module._EggShell_REPL(v_userShell, viaTextPointer, viaTextLength); + var result = Module._EggShell_REPL(Module.eggShell.v_userShell, viaTextPointer, viaTextLength); Module._free(viaTextPointer); Module.print = origPrint; Module.printErr = origPrintErr; @@ -618,7 +938,7 @@ // returns < 0 if should be called again ASAP, 0 if nothing to run, or positive value N if okay // to delay up to N milliseconds before calling again Module.eggShell.executeSlicesUntilWait = publicAPI.eggShell.executeSlicesUntilWait = function (numSlices, millisecondsToRun) { - return EggShell_ExecuteSlices(v_userShell, numSlices, millisecondsToRun); + return EggShell_ExecuteSlices(Module.eggShell.v_userShell, numSlices, millisecondsToRun); }; // Pumps vireo asynchronously until the currently loaded via has finished all clumps diff --git a/source/io/module_httpClient.js b/source/io/module_httpClient.js index 02b9e4520..8b4fe2ee4 100644 --- a/source/io/module_httpClient.js +++ b/source/io/module_httpClient.js @@ -677,7 +677,7 @@ Module.eggShell.dataWriteString(listPointer, list); }; - Module.httpClient.jsHttpClientMethod = function (methodId, handle, urlPointer, outputFilePointer, bufferPointer, timeoutPointer, headersPointer, bodyPointer, statusCodePointer, errorStatusPointer, errorCodePointer, errorSourcePointer, occurrencePointer) { + Module.httpClient.jsHttpClientMethod = function (methodId, handle, urlPointer, outputFilePointer, bufferTypeRef, bufferDataRef, timeoutPointer, headersPointer, bodyPointer, statusCodePointer, errorStatusPointer, errorCodePointer, errorSourcePointer, occurrencePointer) { var setDefaultOutputs = function () { Module.eggShell.dataWriteString(headersPointer, ''); Module.eggShell.dataWriteUInt32(statusCodePointer, 0); @@ -707,9 +707,10 @@ } } - var buffer, typedArrayBuffer; - if (bufferPointer !== NULL) { - typedArrayBuffer = Module.eggShell.dataReadStringAsArray_NoCopy(bufferPointer); + var valueRef, buffer, typedArrayBuffer; + if (bufferDataRef !== NULL) { + valueRef = Module.eggShell.createValueRef(bufferTypeRef, bufferDataRef); + typedArrayBuffer = Module.eggShell.readTypedArray(valueRef); // Blob API does not exist in node.js if (typeof Blob === 'undefined') { diff --git a/test-it/HelloNode.js b/test-it/HelloNode.js deleted file mode 100644 index 593b485f1..000000000 --- a/test-it/HelloNode.js +++ /dev/null @@ -1,79 +0,0 @@ -(function () { - 'use strict'; - var Vireo = require('../'); - - var vireo = new Vireo(); - var text = - 'define(c0 dv(.String "wubbalubbadubdub"))\n' + - 'define(HelloWorld dv(.VirtualInstrument (\n' + - 'Locals: c(' + - 'e(dv(.String "Hello, world. I can fly.") variable1)' + - ')\n' + - 'clump (' + - 'Println(variable1)' + - 'FPSync(c0)' + - ')' + - ') ) )\n' + - 'enqueue(HelloWorld)\n'; - - var currFPID = ''; - - vireo.coreHelpers.setFPSyncFunction(function (fpId) { - currFPID = 'fpsync called with (' + fpId + ')'; - }); - - vireo.eggShell.loadVia(text); - vireo.eggShell.executeSlicesUntilWait(); - - var testResult = false; - var testString = ''; - var preTestString = ''; - - console.log('test1'); - testString = 'fpsync called with (wubbalubbadubdub)'; - testResult = currFPID === testString; - console.assert(testResult, 'FPSync function called from vireo and passes value 90'); - - console.log('test2'); - testString = 'Hello, world. I can fly.'; - testResult = JSON.parse(vireo.eggShell.readJSON('HelloWorld', 'variable1')) === testString; - console.assert(testResult, 'Read a value after execution is done'); - - console.log('test3'); - testString = 'Hello, world. I can fly.你好世界。我能飛。'; - vireo.eggShell.writeJSON('HelloWorld', 'variable1', JSON.stringify(testString)); - testResult = JSON.parse(vireo.eggShell.readJSON('HelloWorld', 'variable1')) === testString; - console.assert(testResult, 'Read a value with unicode characters'); - - console.log('test4'); - testString = 'May it be a good Day!'; - vireo.eggShell.writeJSON('HelloWorld', 'variable1', JSON.stringify(testString)); - testResult = JSON.parse(vireo.eggShell.readJSON('HelloWorld', 'variable1')) === testString; - console.assert(testResult, 'Write a value and get it back'); - - console.log('test5'); - testString = 'multi\nline with \'single\' and "double" quotes'; - vireo.eggShell.writeJSON('HelloWorld', 'variable1', JSON.stringify(testString)); - testResult = JSON.parse(vireo.eggShell.readJSON('HelloWorld', 'variable1')) === testString; - console.assert(testResult, 'Write some special characters'); - - console.log('test6'); - preTestString = 'multi\nline with \'single\' and "double" quotes'; - vireo.eggShell.writeJSON('HelloWorld', 'variable1', JSON.stringify(preTestString)); - console.assert(JSON.parse(vireo.eggShell.readJSON('HelloWorld', 'variable1')) === preTestString, 'The initial valid JSON is written'); - testString = 'Buenas Dias'; - vireo.eggShell.writeJSON('HelloWorld', 'variable1', testString); // JSON.stringify intentionally left off - testResult = JSON.parse(vireo.eggShell.readJSON('HelloWorld', 'variable1')) === preTestString; - console.assert(testResult, 'Write string that is not in JSON format is ignored'); - - console.log('test7'); - preTestString = 'multi\nline with \'single\' and "double" quotes'; - vireo.eggShell.writeJSON('HelloWorld', 'variable1', JSON.stringify(preTestString)); - console.assert(JSON.parse(vireo.eggShell.readJSON('HelloWorld', 'variable1')) === preTestString, 'The initial valid JSON is written'); - testString = 'Buenas Dias'; - vireo.eggShell.writeJSON('HelloWorld', 'variable1', JSON.stringify(testString)); // JSON.stringify intentionally added - testResult = JSON.parse(vireo.eggShell.readJSON('HelloWorld', 'variable1')) === testString; - console.assert(testResult, 'Write string that has been fixed'); - - console.log('test end'); -}()); diff --git a/test-it/ViaTests/ArrayDemo.via b/test-it/ViaTests/ArrayDemo.via index e2584cd97..590fd7472 100644 --- a/test-it/ViaTests/ArrayDemo.via +++ b/test-it/ViaTests/ArrayDemo.via @@ -10,6 +10,7 @@ define(ArrayDemo dv(.VirtualInstrument ( e(a(.Int32 * *) variableArray2d) e(a(.Int32 2 3) fixedArray2d) + e(a(.Int32 0 0) fixedArray2dEmpty) e(a(.Int32 1 2 3) fixedArray3d) ) clump(1 diff --git a/test-it/httptest1/testhttp.js b/test-it/httptest1/testhttp.js index 1830a9f3c..4ab3f468e 100644 --- a/test-it/httptest1/testhttp.js +++ b/test-it/httptest1/testhttp.js @@ -13,7 +13,8 @@ var vireo = new Vireo(); vireo.eggShell.loadVia(viaCode); vireo.eggShell.executeSlicesUntilClumpsFinished(function () { - console.log(JSON.parse(vireo.eggShell.readJSON('%3AWeb%20Server%3AInteractive%3AApplication%3AMain%2Egviweb', 'dataItem_Body'))); + var valueRef = vireo.eggShell.findValueRef('%3AWeb%20Server%3AInteractive%3AApplication%3AMain%2Egviweb', 'dataItem_Body'); + console.log(JSON.parse(vireo.eggShell.readJSON(valueRef))); console.log('finished :D'); }); }; diff --git a/test-it/karma/fixtures/publicapi/ArrayTypesOptimized.via b/test-it/karma/fixtures/publicapi/ArrayTypesOptimized.via index 397d1458e..f73b6dfa9 100644 --- a/test-it/karma/fixtures/publicapi/ArrayTypesOptimized.via +++ b/test-it/karma/fixtures/publicapi/ArrayTypesOptimized.via @@ -1,5 +1,13 @@ define(ArrayTypesOptimized dv(.VirtualInstrument ( - c( + c( + // Supported non-array types + e(dv(.String 'Hello')stringHello) + e(dv(.String '\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008\u0009\u000A\u000B\u000C\u000D\u000E\u000F\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001A\u001B\u001C\u001D\u001E\u001F') stringControlCharacters) + + // Unsupported non-array types + e(dv(.UInt32 10) scalarUInt32) + + // Supported array types e(dv(a(.Int8 *) (8 6 7 5 3 0 9 0 -128 127)) arrayInt8) e(dv(a(.Int16 *) (8 6 7 5 3 0 9 0 -32768 32767)) arrayInt16) e(dv(a(.Int32 *) (8 6 7 5 3 0 9 0 -2147483648 2147483647)) arrayInt32) @@ -10,12 +18,16 @@ define(ArrayTypesOptimized dv(.VirtualInstrument ( e(dv(a(.Double *) (1.1 2.2 0 -0 inf nan -inf -9007199254740992 9007199254740992)) arrayDouble) e(dv(a(.Int32 * *) ((1 2 3 )(4 5 6 )(7 8 9 ))) array2DInt32) e(dv(a(.Int32 * * *) (((1 2 3 )(4 5 6 )(7 8 9 ))((10 11 12 )(13 14 15 )(16 17 18 )))) array3DInt32) - e(dv(a(.String *) ('aaa' 'bbb' 'ccc')) arrayString) - e(dv(a(.Boolean *) (true false true false)) arrayBoolean) - e(dv(.UInt32 10) scalarNumber) e(dv(a(.Int8 *) ()) arrayInt8Empty) e(dv(a(.Int8 * *) ()) array2DInt8Empty) e(dv(a(.Int8 * * *) ()) array3DInt8Empty) + e(dv(a(Enum8(a b c d e f g h) *) (3 2 1))arrayEnum8) + e(dv(a(Enum16(zero one two three four) *) (3 2 1)) arrayEnum16) + e(dv(a(Enum32(red orange yellow green blue purple) *) (3 2 1)) arrayEnum32) + e(dv(a(.Boolean *) (true false true false)) arrayBoolean) + + // Unsupported array types + e(dv(a(.String *) ('aaa' 'bbb' 'ccc')) arrayString) ) clump( ) diff --git a/test-it/karma/fixtures/publicapi/MultipleTypes.via b/test-it/karma/fixtures/publicapi/MultipleTypes.via index 6b63818d5..61d3fec52 100644 --- a/test-it/karma/fixtures/publicapi/MultipleTypes.via +++ b/test-it/karma/fixtures/publicapi/MultipleTypes.via @@ -36,6 +36,18 @@ define (MyVI dv(.VirtualInstrument ( ) (true 'first' 3.14159 42 -72057594037927936 9223372041149743104 (3.4 -5.9) (3564057536 7811758927381448193) ) ) dataItem_ClusterOfScalars) + e(dv(c( + e(.Boolean bool%20fun) + e(.String string%20fun) + e(.Double double%20fun) + e(.Int32 int32%20fun) + e(.Int64 int64%20fun) + e(.UInt64 uint64%20fun) + e(.ComplexDouble complex%20fun) + e(.Timestamp time%20fun) + ) (true 'first' 3.14159 42 -72057594037927936 9223372041149743104 (3.4 -5.9) (3564057536 7811758927381448193) ) + ) dataItem_ClusterOfEncodedScalars) + e(dv(c( e(a(.Boolean *) booleans) e(a(.String *) strings) @@ -124,6 +136,7 @@ define (MyVI dv(.VirtualInstrument ( e(dv('\xF0\x8F\xBF\xBF') dataItem_utf8sequence_overlonglargest4byte) // U+FFFF e(dv(.String '\\"') dataItem_StringOtherJSONEscapedCharacters) + e(dv(.Single 123.5) dataItem_NumericSingle) e(dv(.Double 123.456)dataItem_NumericDouble) e(dv(.Double nan)dataItem_NumericDoubleNaN) e(dv(.Double inf)dataItem_NumericDoublePositiveInfinity) @@ -133,10 +146,42 @@ define (MyVI dv(.VirtualInstrument ( e(dv(.Int32 -1073741824)dataItem_Numeric32) e(dv(.Int64 -1152921504606846976)dataItem_Numeric64) e(dv(.UInt64 18446744073709551615) dataItem_NumericU64) + e(dv(.ComplexSingle (123.5 -20.75)) dataItem_ComplexSingle) e(dv(.ComplexDouble (1337.73 -9283.12))dataItem_Complex) + e(dv(.Boolean true) booleanTrueValue) + e(dv(.Boolean false) booleanFalseValue) + e(dv(.Int8 -128) int8MinValue) + e(dv(.Int8 127) int8MaxValue) + e(dv(.Int16 -32768) int16MinValue) + e(dv(.Int16 32767) int16MaxValue) + e(dv(.Int32 -2147483648) int32MinValue) + e(dv(.Int32 2147483647) int32MaxValue) + e(dv(.Int64 -9007199254740991) int64MinSafeInteger) + e(dv(.Int64 9007199254740991) int64MaxSafeInteger) + e(dv(.Int64 -9223372036854775808) int64MinValue) + e(dv(.Int64 9223372036854775807) int64MaxValue) + e(dv(.UInt8 0) uInt8MinValue) + e(dv(.UInt8 255) uInt8MaxValue) + e(dv(.UInt16 0) uInt16MinValue) + e(dv(.UInt16 65535) uInt16MaxValue) + e(dv(.UInt32 0) uInt32MinValue) + e(dv(.UInt32 4294967295) uInt32MaxValue) + e(dv(.UInt64 0) uInt64MinSafeInteger) + e(dv(.UInt64 9007199254740991) uInt64MaxSafeInteger) + e(dv(.UInt64 0) uInt64MinValue) + e(dv(.UInt64 18446744073709551615) uInt64MaxValue) e(dv(.Boolean true)dataItem_Boolean) e(dv(.Timestamp (3564057536 7811758927381448193))dataItem_Timestamp) e(dv(.AnalogWaveform<.Double> ((300 123) 8.8 (5.5 6.6 7.7 8.8) ) ) wave_Double) + + e(dv(Enum8 (a b c d e f g h) 6) enum8alphabet) + e(dv(Enum16 (zero one two three four) 3) enum16numbers) + e(dv(Enum32 (red orange yellow green blue purple) 2) enum32colors) + e(dv(Enum64 (altair bajor cardassiaA cardassiaB armadillo cobra lynx marmot panda whiskyjack) 5) enum64releases) + + e(dv(.NIPath (() '')) notAPath) + e(dv(.NIPath (() 'ABSOLUTE')) emptyPath) + e(dv(.NIPath (('C' 'Windows' 'System32') 'ABSOLUTE')) win32Path) ) clump( ) diff --git a/test-it/karma/fixtures/publicapi/ReadWriteErrorCluster.via b/test-it/karma/fixtures/publicapi/ReadWriteErrorCluster.via new file mode 100644 index 000000000..d1093b303 --- /dev/null +++ b/test-it/karma/fixtures/publicapi/ReadWriteErrorCluster.via @@ -0,0 +1,22 @@ +define (MyVI dv(.VirtualInstrument ( + Locals: c( + e(.ErrorCluster uninitializedError) + e(dv(.ErrorCluster (false 0 '')) initializedError) + e(c( + e(.Boolean status) + e(.Int32 code) + e(.String source) + ) expandedError) + e(dv(c( + e(.Boolean status) + e(.Int32 code) + e(.String source) + ) (true 12345 'Is this the real life?')) initializedExpandedError) + ) + clump ( + + ) +))) + +enqueue (MyVI) +// Finished! diff --git a/test-it/karma/propertynode/PropertyNode.Test.js b/test-it/karma/propertynode/PropertyNode.Test.js index a9d9a2c68..b3e1fb2a8 100644 --- a/test-it/karma/propertynode/PropertyNode.Test.js +++ b/test-it/karma/propertynode/PropertyNode.Test.js @@ -136,7 +136,7 @@ describe('The Vireo PropertyNode', function () { fraction: '0' }; var valuesToRead = [true, -123, -4321, -987654321, '-9876543210', 123, 4321, 987654321, '9876543210', - 3.5, 6.28, complexToRead, complexToRead, 'Lorem ipsum', '0:0', 1.618, 3.236, true]; + 3.5, 6.28, complexToRead, complexToRead, 'Lorem ipsum', expectedTimestamp, 1.618, 3.236, true]; var readFunction = generateReadFunction(valuesToRead); vireo.propertyNode.setPropertyReadFunction(readFunction); diff --git a/test-it/karma/publicapi/Double.Test.js b/test-it/karma/publicapi/Double.Test.js new file mode 100644 index 000000000..139b04357 --- /dev/null +++ b/test-it/karma/publicapi/Double.Test.js @@ -0,0 +1,194 @@ +describe('The Vireo EggShell Double api can', function () { + 'use strict'; + // Reference aliases + var Vireo = window.NationalInstruments.Vireo.Vireo; + var vireoRunner = window.testHelpers.vireoRunner; + var fixtures = window.testHelpers.fixtures; + + var vireo = new Vireo(); + + var publicApiMultipleTypesViaUrl = fixtures.convertToAbsoluteFromFixturesDir('publicapi/MultipleTypes.via'); + var viName = 'MyVI'; + + var readDouble = function (path) { + return vireo.eggShell.readDouble(vireo.eggShell.findValueRef(viName, path)); + }; + + var tryReadDouble = function (path) { + return function () { + readDouble(path); + }; + }; + + var writeDouble = function (path, value) { + vireo.eggShell.writeDouble(vireo.eggShell.findValueRef(viName, path), value); + }; + + var tryWriteDouble = function (path, value) { + return function () { + writeDouble(path, value); + }; + }; + + var testWriteDouble = function (path, initialValue, newValue) { + expect(readDouble(path)).toMatchIEEE754Number(initialValue); + writeDouble(path, newValue); + expect(readDouble(path)).toMatchIEEE754Number(newValue); + writeDouble(path, initialValue); + expect(readDouble(path)).toMatchIEEE754Number(initialValue); + }; + + var testWriteDoubleCoerced = function (path, initialValue, attemptedNewValue, coercedNewValue) { + expect(readDouble(path)).toMatchIEEE754Number(initialValue); + writeDouble(path, attemptedNewValue); + expect(readDouble(path)).toMatchIEEE754Number(coercedNewValue); + writeDouble(path, initialValue); + expect(readDouble(path)).toMatchIEEE754Number(initialValue); + }; + + // Expected coerced values of Doubles in Vireo should have the same behavior as assignment to typed arrays + // This is so a user assigning a double to a typedarray using getTypedArray will see the same effect as writing to a double using writeDouble + var expectedCoercedValue = function (TypedArrayConstructor, value) { + return (new TypedArrayConstructor([value])[0]); + }; + + beforeAll(function (done) { + fixtures.preloadAbsoluteUrls([ + publicApiMultipleTypesViaUrl + ], done); + }); + + beforeAll(function () { + vireoRunner.rebootAndLoadVia(vireo, publicApiMultipleTypesViaUrl); + }); + + describe('use readDouble', function () { + it('to read different double values from memory', function () { + expect(readDouble('dataItem_NumericDouble')).toMatchIEEE754Number(123.456); + expect(readDouble('dataItem_NumericDoubleNaN')).toMatchIEEE754Number(NaN); + expect(readDouble('dataItem_NumericDoublePositiveInfinity')).toMatchIEEE754Number(Infinity); + expect(readDouble('dataItem_NumericDoubleNegativeInfinity')).toMatchIEEE754Number(-Infinity); + expect(readDouble('dataItem_NumericDoublePositiveZero')).toMatchIEEE754Number(0); + expect(readDouble('dataItem_NumericDoubleNegativeZero')).toMatchIEEE754Number(-0); + }); + + it('to read different integer values from memory', function () { + expect(readDouble('int8MinValue')).toBe(-128); + expect(readDouble('int8MaxValue')).toBe(127); + expect(readDouble('int16MinValue')).toBe(-32768); + expect(readDouble('int16MaxValue')).toBe(32767); + expect(readDouble('int32MinValue')).toBe(-2147483648); + expect(readDouble('int32MaxValue')).toBe(2147483647); + expect(readDouble('int64MinSafeInteger')).toBe(-9007199254740991); + expect(readDouble('int64MaxSafeInteger')).toBe(9007199254740991); + expect(readDouble('int64MinValue')).toBe(-9223372036854776000); // Expected precision loss, full value -9223372036854775808 + expect(readDouble('int64MaxValue')).toBe(9223372036854776000); // Expected precision loss, full value 9223372036854775807 + expect(readDouble('uInt8MinValue')).toBe(0); + expect(readDouble('uInt8MaxValue')).toBe(255); + expect(readDouble('uInt16MinValue')).toBe(0); + expect(readDouble('uInt16MaxValue')).toBe(65535); + expect(readDouble('uInt32MinValue')).toBe(0); + expect(readDouble('uInt32MaxValue')).toBe(4294967295); + expect(readDouble('uInt64MinSafeInteger')).toBe(0); + expect(readDouble('uInt64MaxSafeInteger')).toBe(9007199254740991); + expect(readDouble('uInt64MinValue')).toBe(0); + expect(readDouble('uInt64MaxValue')).toBe(18446744073709552000); // Expected precision loss, full value 18446744073709551615 + }); + + it('to read different enum values from memory', function () { + expect(readDouble('enum8alphabet')).toBe(6); + expect(readDouble('enum16numbers')).toBe(3); + expect(readDouble('enum32colors')).toBe(2); + expect(readDouble('enum64releases')).toBe(5); + }); + + it('to read different boolean values from memory', function () { + expect(readDouble('booleanTrueValue')).toBe(1); + expect(readDouble('booleanFalseValue')).toBe(0); + }); + + it('to read different timestamp values from memory', function () { + expect(readDouble('dataItem_Timestamp')).toBe(3564057536.423476); + }); + + it('to error for unsupported types', function () { + expect(tryReadDouble('dataItem_Complex')).toThrowError(/UnexpectedObjectType/); + expect(tryReadDouble('dataItem_String')).toThrowError(/UnexpectedObjectType/); + }); + }); + + describe('use writeDouble', function () { + it('to write double values to memory', function () { + testWriteDouble('dataItem_NumericDouble', 123.456, 42); + testWriteDouble('dataItem_NumericDouble', 123.456, NaN); + testWriteDouble('dataItem_NumericDouble', 123.456, Infinity); + testWriteDouble('dataItem_NumericDouble', 123.456, -Infinity); + testWriteDouble('dataItem_NumericDouble', 123.456, 0); + testWriteDouble('dataItem_NumericDouble', 123.456, -0); + }); + + it('to write integer values to memory', function () { + testWriteDoubleCoerced('int8MinValue', -128, 812, expectedCoercedValue(Int8Array, 812)); + testWriteDoubleCoerced('int8MinValue', -128, -812, expectedCoercedValue(Int8Array, -812)); + testWriteDoubleCoerced('int16MinValue', -32768, 81234, expectedCoercedValue(Int16Array, 81234)); + testWriteDoubleCoerced('int16MinValue', -32768, -81234, expectedCoercedValue(Int16Array, -81234)); + testWriteDoubleCoerced('int32MinValue', -2147483648, 8123456789, expectedCoercedValue(Int32Array, 8123456789)); + testWriteDoubleCoerced('int32MinValue', -2147483648, -8123456789, expectedCoercedValue(Int32Array, -8123456789)); + testWriteDoubleCoerced('uInt8MinValue', 0, 812, expectedCoercedValue(Uint8Array, 812)); + testWriteDoubleCoerced('uInt8MinValue', 0, -812, expectedCoercedValue(Uint8Array, -812)); + testWriteDoubleCoerced('uInt16MinValue', 0, 81234, expectedCoercedValue(Uint16Array, 81234)); + testWriteDoubleCoerced('uInt16MinValue', 0, -81234, expectedCoercedValue(Uint16Array, -81234)); + testWriteDoubleCoerced('uInt32MinValue', 0, 8123456789, expectedCoercedValue(Uint32Array, 8123456789)); + testWriteDoubleCoerced('uInt32MinValue', 0, -8123456789, expectedCoercedValue(Uint32Array, -8123456789)); + + expect(expectedCoercedValue(Int8Array, 812)).toBe(44); + expect(expectedCoercedValue(Int8Array, -812)).toBe(-44); + expect(expectedCoercedValue(Int16Array, 81234)).toBe(15698); + expect(expectedCoercedValue(Int16Array, -81234)).toBe(-15698); + expect(expectedCoercedValue(Int32Array, 8123456789)).toBe(-466477803); + expect(expectedCoercedValue(Int32Array, -8123456789)).toBe(466477803); + expect(expectedCoercedValue(Uint8Array, 812)).toBe(44); + expect(expectedCoercedValue(Uint8Array, -812)).toBe(212); + expect(expectedCoercedValue(Uint16Array, 81234)).toBe(15698); + expect(expectedCoercedValue(Uint16Array, -81234)).toBe(49838); + expect(expectedCoercedValue(Uint32Array, 8123456789)).toBe(3828489493); + expect(expectedCoercedValue(Uint32Array, -8123456789)).toBe(466477803); + }); + + it('to coerce iee754 special values to memory', function () { + testWriteDoubleCoerced('int8MinValue', -128, NaN, expectedCoercedValue(Int8Array, NaN)); + testWriteDoubleCoerced('int8MinValue', -128, Infinity, expectedCoercedValue(Int8Array, Infinity)); + testWriteDoubleCoerced('int8MinValue', -128, -Infinity, expectedCoercedValue(Int8Array, -Infinity)); + testWriteDoubleCoerced('int8MinValue', -128, -0, expectedCoercedValue(Int8Array, -0)); + testWriteDoubleCoerced('int8MinValue', -128, 0, expectedCoercedValue(Int8Array, 0)); + + expect(expectedCoercedValue(Int8Array, NaN)).toBe(0); + expect(expectedCoercedValue(Int8Array, Infinity)).toBe(0); + expect(expectedCoercedValue(Int8Array, -Infinity)).toBe(0); + expect(expectedCoercedValue(Int8Array, -0)).toBe(0); + expect(expectedCoercedValue(Int8Array, 0)).toBe(0); + }); + + it('to write different enum values to memory', function () { + testWriteDouble('enum8alphabet', 6, 4); + testWriteDouble('enum16numbers', 3, 4); + testWriteDouble('enum32colors', 2, 4); + testWriteDouble('enum64releases', 5, 4); + }); + + it('to write different boolean values to memory', function () { + testWriteDouble('booleanTrueValue', 1, 0); + testWriteDoubleCoerced('booleanTrueValue', 1, 70, 1); + testWriteDoubleCoerced('booleanTrueValue', 1, -70, 1); + }); + + it('to write different timestamp values to memory', function () { + testWriteDouble('dataItem_Timestamp', 3564057536.423476, 7); + }); + + it('to error for unsupported types', function () { + expect(tryWriteDouble('dataItem_Complex', 42)).toThrowError(/UnexpectedObjectType/); + expect(tryWriteDouble('dataItem_String', 42)).toThrowError(/UnexpectedObjectType/); + }); + }); +}); diff --git a/test-it/karma/publicapi/DoubleInAggregate.Test.js b/test-it/karma/publicapi/DoubleInAggregate.Test.js new file mode 100644 index 000000000..13112677e --- /dev/null +++ b/test-it/karma/publicapi/DoubleInAggregate.Test.js @@ -0,0 +1,75 @@ +describe('The Vireo EggShell Double and FindSubValueRef api over aggregate types can', function () { + 'use strict'; + // Reference aliases + var Vireo = window.NationalInstruments.Vireo.Vireo; + var vireoRunner = window.testHelpers.vireoRunner; + var fixtures = window.testHelpers.fixtures; + + var vireo = new Vireo(); + + var publicApiMultipleTypesViaUrl = fixtures.convertToAbsoluteFromFixturesDir('publicapi/MultipleTypes.via'); + var viName = 'MyVI'; + + var readDouble = function (path, subPath) { + var valueRef = vireo.eggShell.findValueRef(viName, path); + var subValueRef = vireo.eggShell.findSubValueRef(valueRef, subPath); + return vireo.eggShell.readDouble(subValueRef); + }; + + beforeAll(function (done) { + fixtures.preloadAbsoluteUrls([ + publicApiMultipleTypesViaUrl + ], done); + }); + + beforeAll(function () { + vireoRunner.rebootAndLoadVia(vireo, publicApiMultipleTypesViaUrl); + }); + + describe('use readDouble', function () { + it('to read different double values from 1-D arrays', function () { + expect(readDouble('dataItem_ArrayOfBoolean', '3')).toBe(1); + expect(readDouble('dataItem_ArrayOfDouble', '4')).toMatchIEEE754Number(1234.5678); + expect(readDouble('dataItem_ArrayOfInt32', '3')).toBe(9876543); + expect(readDouble('dataItem_ArrayOfInt64', '1')).toBe(9090); + expect(readDouble('dataItem_ArrayOfTimestamp', '0')).toMatchIEEE754Number(3564057536.423476); + }); + + it('to read different double values from N-dimensional arrays', function () { + expect(readDouble('dataItem_2DArrayOfBoolean', '2,1')).toBe(1); + expect(readDouble('dataItem_2DArrayOfDouble', '1,2')).toMatchIEEE754Number(-6.789); + expect(readDouble('dataItem_3DArrayOfInt32', '1,1,2')).toBe(223); + expect(readDouble('dataItem_2DArrayOfInt64', '0,0')).toBe(9090); + expect(readDouble('dataItem_2DArrayOfTimestamp', '0,1')).toMatchIEEE754Number(3564057542.904824); + }); + + it('to read different double values from clusters', function () { + var path = 'dataItem_ClusterOfScalars'; + expect(readDouble(path, 'bool')).toBe(1); + expect(readDouble(path, 'double')).toMatchIEEE754Number(3.14159); + expect(readDouble(path, 'int32')).toBe(42); + expect(readDouble(path, 'int64')).toBe(-72057594037927936); + expect(readDouble(path, 'uint64')).toBe(9223372041149743104); + expect(readDouble(path, 'time')).toMatchIEEE754Number(3564057536.423476); + }); + + it('to read different double values from array of clusters', function () { + var path = 'dataItem_ArrayOfClusters'; + expect(readDouble(path, '0.bool')).toBe(1); + expect(readDouble(path, '1.double')).toMatchIEEE754Number(6.2831); + expect(readDouble(path, '2.int32')).toBe(144); + expect(readDouble(path, '0.int64')).toBe(72057594037927936); + expect(readDouble(path, '1.time')).toMatchIEEE754Number(3564059871.423476); + }); + + it('to read different double values from cluster of arrays', function () { + var path = 'dataItem_ClusterOfArrays'; + expect(readDouble(path, 'booleans.2')).toBe(1); + expect(readDouble(path, 'doubles.3')).toMatchIEEE754Number(7.89); + expect(readDouble(path, 'int32s.0')).toBe(-1000); + expect(readDouble(path, 'int64s.1')).toBe(9090); + expect(readDouble(path, 'uint64s.2')).toBe(9223376434901286912); + expect(readDouble(path, 'times.1')).toMatchIEEE754Number(3564057542.904824); + }); + }); +}); diff --git a/test-it/karma/publicapi/ExecuteSlicesUntilClumpsFinshed.Test.js b/test-it/karma/publicapi/ExecuteSlicesUntilClumpsFinshed.Test.js index c00d13c5a..376cba95a 100644 --- a/test-it/karma/publicapi/ExecuteSlicesUntilClumpsFinshed.Test.js +++ b/test-it/karma/publicapi/ExecuteSlicesUntilClumpsFinshed.Test.js @@ -56,7 +56,8 @@ describe('The Vireo EggShell executeSlicesUntilClumpsFinished api', function () vireo.eggShell.loadVia(viaCode); var maxExecWakeUpTime = vireo.eggShell.maxExecWakeUpTime(); var numIterations = 100; - vireo.eggShell.writeJSON('MyVI', 'numIterations', JSON.stringify(numIterations)); + var valueRef = vireo.eggShell.findValueRef('MyVI', 'numIterations'); + vireo.eggShell.writeJSON(valueRef, JSON.stringify(numIterations)); var maxTimewithBuffer = maxExecWakeUpTime * numIterations * 0.5; @@ -87,7 +88,8 @@ describe('The Vireo EggShell executeSlicesUntilClumpsFinished api', function () var maxExecWakeUpTime = vireo.eggShell.maxExecWakeUpTime(); var maxExecuteSlicesCalls = 20; var waitTime = maxExecuteSlicesCalls * maxExecWakeUpTime; - vireo.eggShell.writeJSON('MyVI', 'waitTime', JSON.stringify(waitTime)); + var valueRef = vireo.eggShell.findValueRef('MyVI', 'waitTime'); + vireo.eggShell.writeJSON(valueRef, JSON.stringify(waitTime)); var internalModule = vireo.eggShell.internal_module_do_not_use_or_you_will_be_fired; spyOn(internalModule.eggShell, 'executeSlicesUntilWait').and.callThrough(); diff --git a/test-it/karma/publicapi/FPSync.Test.js b/test-it/karma/publicapi/FPSync.Test.js index 22360d3cc..6b11dd3df 100644 --- a/test-it/karma/publicapi/FPSync.Test.js +++ b/test-it/karma/publicapi/FPSync.Test.js @@ -41,7 +41,8 @@ describe('The Vireo CoreHelpers setFPSyncFunction api', function () { var trackerCalls = []; var tracker = function (fpSyncString) { - var myDouble = vireo.eggShell.readDouble(viName, 'myDouble'); + var valueRef = vireo.eggShell.findValueRef(viName, 'myDouble'); + var myDouble = vireo.eggShell.readDouble(valueRef); trackerCalls.push({ myDouble: myDouble, fpSyncString: fpSyncString diff --git a/test-it/karma/publicapi/FindSubValueRef.Test.js b/test-it/karma/publicapi/FindSubValueRef.Test.js new file mode 100644 index 000000000..531139839 --- /dev/null +++ b/test-it/karma/publicapi/FindSubValueRef.Test.js @@ -0,0 +1,203 @@ +describe('The Vireo EggShell findSubValueRef', function () { + 'use strict'; + + // Reference aliases + var Vireo = window.NationalInstruments.Vireo.Vireo; + var vireoRunner = window.testHelpers.vireoRunner; + var fixtures = window.testHelpers.fixtures; + + var vireo = new Vireo(); + + var publicApiMultipleTypesViaUrl = fixtures.convertToAbsoluteFromFixturesDir('publicapi/MultipleTypes.via'); + var viName = 'MyVI'; + + var expectValidValueRef = function (valueRef) { + expect(valueRef).toBeNonEmptyObject(); + expect(valueRef.typeRef).toBeNumber(); + expect(valueRef.typeRef).not.toBe(0); + expect(valueRef.dataRef).toBeNumber(); + expect(valueRef.dataRef).not.toBe(0); + }; + + beforeAll(function (done) { + fixtures.preloadAbsoluteUrls([ + publicApiMultipleTypesViaUrl + ], done); + }); + + beforeAll(function () { + vireoRunner.rebootAndLoadVia(vireo, publicApiMultipleTypesViaUrl); + }); + + describe('for a cluster of scalars', function () { + var valueRef, + clusterOfScalarsPath = 'dataItem_ClusterOfScalars'; + + beforeAll(function () { + valueRef = vireo.eggShell.findValueRef(viName, clusterOfScalarsPath); + }); + + it('throws for an object with no typeRef', function () { + var invalidValueRef = function () { + vireo.eggShell.findSubValueRef({}, 'dataItem_ClusterOfScalars'); + }; + + expect(invalidValueRef).toThrow(); + }); + + it('throws for an object with invalid typeRef', function () { + var invalidValueRef = function () { + var invalidTypeRef = { + typeRef: 0 + }; + vireo.eggShell.findSubValueRef(invalidTypeRef, 'dataItem_ClusterOfScalars'); + }; + + expect(invalidValueRef).toThrow(); + }); + + it('throws for a nonexistant path', function () { + var invalidPath = function () { + vireo.eggShell.findSubValueRef(valueRef, 'nonexistantpath'); + }; + + expect(invalidPath).toThrow(); + }); + + it('finds values of cluster elements', function () { + expectValidValueRef(vireo.eggShell.findSubValueRef(valueRef, 'bool')); + expectValidValueRef(vireo.eggShell.findSubValueRef(valueRef, 'string')); + expectValidValueRef(vireo.eggShell.findSubValueRef(valueRef, 'double')); + expectValidValueRef(vireo.eggShell.findSubValueRef(valueRef, 'int32')); + expectValidValueRef(vireo.eggShell.findSubValueRef(valueRef, 'int64')); + expectValidValueRef(vireo.eggShell.findSubValueRef(valueRef, 'uint64')); + expectValidValueRef(vireo.eggShell.findSubValueRef(valueRef, 'complex')); + expectValidValueRef(vireo.eggShell.findSubValueRef(valueRef, 'time')); + }); + }); + + describe('for a 1D array', function () { + var valueRef, + arrayOfBooleansPath = 'dataItem_ArrayOfBoolean'; + + beforeAll(function () { + valueRef = vireo.eggShell.findValueRef(viName, arrayOfBooleansPath); + }); + + it('throws for invalid index', function () { + var invalidIndex = function () { + vireo.eggShell.findSubValueRef(valueRef, '-1'); + }; + + expect(invalidIndex).toThrow(); + }); + + it('throws for index out of bounds', function () { + var invalidIndex = function () { + vireo.eggShell.findSubValueRef(valueRef, '10'); + }; + + expect(invalidIndex).toThrow(); + }); + + it('finds a value', function () { + expectValidValueRef(vireo.eggShell.findSubValueRef(valueRef, '0')); + }); + }); + + describe('for a multidimensional array', function () { + var valueRef, + ndimArrayPath = 'dataItem_3DArrayOfInt32'; // Dimensions: [1, 2, 3] + + beforeAll(function () { + valueRef = vireo.eggShell.findValueRef(viName, ndimArrayPath); + }); + + it('throws for an invalid path format', function () { + var invalidPathFormat = function () { + vireo.eggShell.findSubValueRef(valueRef, '0,0.0'); + }; + + expect(invalidPathFormat).toThrow(); + }); + + it('throws for index out of bounds', function () { + var indexOutOfBounds = function () { + vireo.eggShell.findSubValueRef(valueRef, '2,0,0'); + }; + + expect(indexOutOfBounds).toThrow(); + }); + + it('finds values for comma-separated indexes', function () { + expectValidValueRef(vireo.eggShell.findSubValueRef(valueRef, '0,0,0')); + expectValidValueRef(vireo.eggShell.findSubValueRef(valueRef, '0,0,2')); + expectValidValueRef(vireo.eggShell.findSubValueRef(valueRef, '0,1,2')); + expectValidValueRef(vireo.eggShell.findSubValueRef(valueRef, '1,0,0')); + expectValidValueRef(vireo.eggShell.findSubValueRef(valueRef, '1,0,2')); + expectValidValueRef(vireo.eggShell.findSubValueRef(valueRef, '1,1,2')); + }); + }); + + describe('for an array of clusters', function () { + var valueRef, + arrayOfClustersPath = 'dataItem_ArrayOfClusters'; + + beforeAll(function () { + valueRef = vireo.eggShell.findValueRef(viName, arrayOfClustersPath); + }); + + it('throws for invalid path format', function () { + var invalidPathFormat = function () { + vireo.eggShell.findSubValueRef(valueRef, '0,bool'); + }; + + expect(invalidPathFormat).toThrow(); + }); + + it('finds values for indexes followed by "." field name', function () { + expectValidValueRef(vireo.eggShell.findSubValueRef(valueRef, '0.bool')); + expectValidValueRef(vireo.eggShell.findSubValueRef(valueRef, '0.string')); + expectValidValueRef(vireo.eggShell.findSubValueRef(valueRef, '1.double')); + expectValidValueRef(vireo.eggShell.findSubValueRef(valueRef, '1.int32')); + expectValidValueRef(vireo.eggShell.findSubValueRef(valueRef, '2.int64')); + expectValidValueRef(vireo.eggShell.findSubValueRef(valueRef, '2.complex')); + expectValidValueRef(vireo.eggShell.findSubValueRef(valueRef, '2.time')); + }); + }); + + describe('for a cluster of arrays', function () { + var valueRef, + clusterOfArraysPath = 'dataItem_ClusterOfArrays'; + + beforeAll(function () { + valueRef = vireo.eggShell.findValueRef(viName, clusterOfArraysPath); + }); + + it('throws for invalid path format', function () { + var invalidPathFormat = function () { + vireo.eggShell.findSubValueRef(valueRef, 'booleans,0'); + }; + + expect(invalidPathFormat).toThrow(); + }); + + it('throws for index out of bounds', function () { + var indexOutOfBounds = function () { + vireo.eggShell.findSubValueRef(valueRef, 'booleans.3'); + }; + + expect(indexOutOfBounds).toThrow(); + }); + + it('finds values for fields followed by "." index', function () { + expectValidValueRef(vireo.eggShell.findSubValueRef(valueRef, 'booleans.0')); + expectValidValueRef(vireo.eggShell.findSubValueRef(valueRef, 'strings.1')); + expectValidValueRef(vireo.eggShell.findSubValueRef(valueRef, 'doubles.2')); + expectValidValueRef(vireo.eggShell.findSubValueRef(valueRef, 'int32s.3')); + expectValidValueRef(vireo.eggShell.findSubValueRef(valueRef, 'int64s.3')); + expectValidValueRef(vireo.eggShell.findSubValueRef(valueRef, 'complexes.2')); + expectValidValueRef(vireo.eggShell.findSubValueRef(valueRef, 'times.0')); + }); + }); +}); diff --git a/test-it/karma/publicapi/FindValueRef.Test.js b/test-it/karma/publicapi/FindValueRef.Test.js new file mode 100644 index 000000000..c373dd882 --- /dev/null +++ b/test-it/karma/publicapi/FindValueRef.Test.js @@ -0,0 +1,62 @@ +describe('The Vireo EggShell findValueRef api can', function () { + 'use strict'; + // Reference aliases + var Vireo = window.NationalInstruments.Vireo.Vireo; + var vireoRunner = window.testHelpers.vireoRunner; + var fixtures = window.testHelpers.fixtures; + + var vireo = new Vireo(); + + var publicApiMultipleTypesViaUrl = fixtures.convertToAbsoluteFromFixturesDir('publicapi/MultipleTypes.via'); + var viName = 'MyVI'; + var pathName = 'dataItem_NumericDouble'; + + beforeAll(function (done) { + fixtures.preloadAbsoluteUrls([ + publicApiMultipleTypesViaUrl + ], done); + }); + + beforeAll(function () { + vireoRunner.rebootAndLoadVia(vireo, publicApiMultipleTypesViaUrl); + }); + + it('find a value in memory', function () { + var valueRef = vireo.eggShell.findValueRef(viName, pathName); + expect(valueRef).toBeNonEmptyObject(); + expect(valueRef.typeRef).toBeNumber(); + expect(valueRef.typeRef).not.toBe(0); + expect(valueRef.dataRef).toBeNumber(); + expect(valueRef.dataRef).not.toBe(0); + }); + + it('to throw for a nonexistant vi name', function () { + var invalidViName = function () { + vireo.eggShell.findValueRef('nonexistantvi', pathName); + }; + expect(invalidViName).toThrowError(/ObjectNotFoundAtPath/); + }); + + it('to throw for an empty vi name', function () { + var invalidViName = function () { + vireo.eggShell.findValueRef('', pathName); + }; + expect(invalidViName).toThrowError(/ObjectNotFoundAtPath/); + }); + + it('to throw for a nonexistant path', function () { + var invalidPath = function () { + vireo.eggShell.findValueRef(viName, 'nonexistantvalue'); + }; + expect(invalidPath).toThrowError(/ObjectNotFoundAtPath/); + }); + + it('to return a typeRef for the the local scope of a VI for an empty path', function () { + var valueRef = vireo.eggShell.findValueRef(viName, ''); + expect(valueRef).toBeNonEmptyObject(); + expect(valueRef.typeRef).toBeNumber(); + expect(valueRef.typeRef).not.toBe(0); + expect(valueRef.dataRef).toBeNumber(); + expect(valueRef.dataRef).not.toBe(0); + }); +}); diff --git a/test-it/karma/publicapi/GetArrayDimensions.Test.js b/test-it/karma/publicapi/GetArrayDimensions.Test.js new file mode 100644 index 000000000..8d4325926 --- /dev/null +++ b/test-it/karma/publicapi/GetArrayDimensions.Test.js @@ -0,0 +1,62 @@ +describe('The Vireo EggShell getArrayDimensions api', function () { + 'use strict'; + // Reference aliases + var Vireo = window.NationalInstruments.Vireo.Vireo; + var vireoRunner = window.testHelpers.vireoRunner; + var fixtures = window.testHelpers.fixtures; + + var vireo; + + var testsArrayDemoViaUrl = fixtures.convertToAbsoluteFromViaTestsDir('ArrayDemo.via'); + var publicApiMultipleTypesViaUrl = fixtures.convertToAbsoluteFromFixturesDir('publicapi/MultipleTypes.via'); + var viNameArrayDemo = 'ArrayDemo'; + var viNameMultipleTypes = 'MyVI'; + var getArrayDimensionsHelper = function (viName, path) { + var valueRef = vireo.eggShell.findValueRef(viName, path); + var dimensions = vireo.eggShell.getArrayDimensions(valueRef); + return dimensions; + }; + + beforeAll(function (done) { + fixtures.preloadAbsoluteUrls([ + testsArrayDemoViaUrl, + publicApiMultipleTypesViaUrl + ], done); + }); + + beforeEach(function () { + vireo = new Vireo(); + }); + + it('can expose array lengths', function (done) { + var getArrayDimensions = getArrayDimensionsHelper.bind(undefined, viNameArrayDemo); + var runSlicesAsync = vireoRunner.rebootAndLoadVia(vireo, testsArrayDemoViaUrl); + + runSlicesAsync(function (rawPrint, rawPrintError) { + expect(rawPrint).toBeNonEmptyString(); + expect(rawPrintError).toBeEmptyString(); + expect(getArrayDimensions('variableArray1d')).toEqual([0]); + expect(getArrayDimensions('fixedArray1d')).toEqual([5]); + expect(getArrayDimensions('boundedArray1d')).toEqual([0]); + expect(getArrayDimensions('variableArray1dwithDefaults')).toEqual([4]); + expect(getArrayDimensions('fixedArray1dwithDefaults')).toEqual([5]); + expect(getArrayDimensions('boundedArray1dwithDefaults')).toEqual([4]); + expect(getArrayDimensions('fixedArray2d')).toEqual([2, 3]); + expect(getArrayDimensions('fixedArray2dEmpty')).toEqual([0, 0]); + expect(getArrayDimensions('fixedArray3d')).toEqual([1, 2, 3]); + done(); + }); + }); + + it('errors for unsupported types', function (done) { + var getArrayDimensions = getArrayDimensionsHelper.bind(undefined, viNameMultipleTypes); + var runSlicesAsync = vireoRunner.rebootAndLoadVia(vireo, publicApiMultipleTypesViaUrl); + + runSlicesAsync(function (rawPrint, rawPrintError) { + expect(rawPrint).toBeEmptyString(); + expect(rawPrintError).toBeEmptyString(); + expect(getArrayDimensions.bind(undefined, 'dataItem_Boolean')).toThrowError(/UnexpectedObjectType/); + done(); + }); + }); +}); diff --git a/test-it/karma/publicapi/GetNumericArray.Test.js b/test-it/karma/publicapi/GetNumericArray.Test.js index 5f7584e3a..2e96858e8 100644 --- a/test-it/karma/publicapi/GetNumericArray.Test.js +++ b/test-it/karma/publicapi/GetNumericArray.Test.js @@ -68,7 +68,7 @@ describe('The Vireo EggShell getNumericArray api', function () { }).toThrowError(/ObjectNotFoundAtPath/); expect(function () { - vireo.eggShell.getNumericArray(viName, 'scalarNumber'); + vireo.eggShell.getNumericArray(viName, 'scalarUInt32'); }).toThrowError(/UnexpectedObjectType/); done(); diff --git a/test-it/karma/publicapi/ReadJson.Test.js b/test-it/karma/publicapi/ReadJson.Test.js index d44861394..a3fc6f164 100644 --- a/test-it/karma/publicapi/ReadJson.Test.js +++ b/test-it/karma/publicapi/ReadJson.Test.js @@ -10,14 +10,20 @@ describe('The Vireo EggShell readJSON api can read', function () { var publicApiMultipleTypesViaUrl = fixtures.convertToAbsoluteFromFixturesDir('publicapi/MultipleTypes.via'); var viName = 'MyVI'; + var readActualJSON = function (viName, path) { + var valueRef = vireo.eggShell.findValueRef(viName, path); + var valJSON = vireo.eggShell.readJSON(valueRef); + return valJSON; + }; + var readTest = function (path, expectedVal) { - var valJSON = vireo.eggShell.readJSON(viName, path); + var valJSON = readActualJSON(viName, path); var val = JSON.parse(valJSON); expect(val).toMatchIEEE754Number(expectedVal); }; var readTestWithJSON = function (path, expectedValJSON, expectedVal) { - var valJSON = vireo.eggShell.readJSON(viName, path); + var valJSON = readActualJSON(viName, path); expect(valJSON).toMatchIEEE754Number(expectedValJSON); var val = JSON.parse(valJSON); @@ -266,14 +272,14 @@ describe('The Vireo EggShell readJSON api can read', function () { }); it('Int64', function () { - var actualJSON = vireo.eggShell.readJSON(viName, 'dataItem_Numeric64'); + var actualJSON = readActualJSON(viName, 'dataItem_Numeric64'); var actual = JSON.parse(actualJSON); expect(actual).toBe('-1152921504606846976'); }); it('UInt64', function () { - var actualJSON = vireo.eggShell.readJSON(viName, 'dataItem_NumericU64'); + var actualJSON = readActualJSON(viName, 'dataItem_NumericU64'); var actual = JSON.parse(actualJSON); expect(actual).toBe('18446744073709551615'); @@ -287,7 +293,7 @@ describe('The Vireo EggShell readJSON api can read', function () { }); it('Timestamp', function () { - var actualJSON = vireo.eggShell.readJSON(viName, 'dataItem_Timestamp'); + var actualJSON = readActualJSON(viName, 'dataItem_Timestamp'); var actual = JSON.parse(actualJSON); expect(actual).toEqual({ @@ -315,7 +321,7 @@ describe('The Vireo EggShell readJSON api can read', function () { }); it('Int64', function () { - var actualJSON = vireo.eggShell.readJSON(viName, 'dataItem_ArrayOfInt64'); + var actualJSON = readActualJSON(viName, 'dataItem_ArrayOfInt64'); var actual = JSON.parse(actualJSON); expect(actual).toEqual([ @@ -342,7 +348,7 @@ describe('The Vireo EggShell readJSON api can read', function () { }); it('Timestamp', function () { - var actualJSON = vireo.eggShell.readJSON(viName, 'dataItem_ArrayOfTimestamp'); + var actualJSON = readActualJSON(viName, 'dataItem_ArrayOfTimestamp'); var actual = JSON.parse(actualJSON); expect(actual).toEqual([ @@ -357,7 +363,7 @@ describe('The Vireo EggShell readJSON api can read', function () { }); it('Cluster', function () { - var actualJSON = vireo.eggShell.readJSON(viName, 'dataItem_ArrayOfClusters'); + var actualJSON = readActualJSON(viName, 'dataItem_ArrayOfClusters'); var actual = JSON.parse(actualJSON); expect(actual).toEqual([ @@ -435,7 +441,7 @@ describe('The Vireo EggShell readJSON api can read', function () { }); it('Int64', function () { - var actualJSON = vireo.eggShell.readJSON(viName, 'dataItem_2DArrayOfInt64'); + var actualJSON = readActualJSON(viName, 'dataItem_2DArrayOfInt64'); var actual = JSON.parse(actualJSON); expect(actual).toEqual([ @@ -469,7 +475,7 @@ describe('The Vireo EggShell readJSON api can read', function () { }); it('Timestamp', function () { - var actualJSON = vireo.eggShell.readJSON(viName, 'dataItem_2DArrayOfTimestamp'); + var actualJSON = readActualJSON(viName, 'dataItem_2DArrayOfTimestamp'); var actual = JSON.parse(actualJSON); expect(actual).toEqual([ @@ -521,7 +527,7 @@ describe('The Vireo EggShell readJSON api can read', function () { describe('composite types of', function () { it('clusters with scalars', function () { - var actualJSON = vireo.eggShell.readJSON(viName, 'dataItem_ClusterOfScalars'); + var actualJSON = readActualJSON(viName, 'dataItem_ClusterOfScalars'); var actual = JSON.parse(actualJSON); expect(actual).toEqual({ @@ -543,7 +549,7 @@ describe('The Vireo EggShell readJSON api can read', function () { }); it('clusters with 1D arrays', function () { - var actualJSON = vireo.eggShell.readJSON(viName, 'dataItem_ClusterOfArrays'); + var actualJSON = readActualJSON(viName, 'dataItem_ClusterOfArrays'); var actual = JSON.parse(actualJSON); expect(actual).toEqual({ diff --git a/test-it/karma/publicapi/ReadValueRefObject.Test.js b/test-it/karma/publicapi/ReadValueRefObject.Test.js new file mode 100644 index 000000000..d117b8a64 --- /dev/null +++ b/test-it/karma/publicapi/ReadValueRefObject.Test.js @@ -0,0 +1,169 @@ +describe('The Vireo EggShell readValueRefObject', function () { + 'use strict'; + + // Reference aliases + var Vireo = window.NationalInstruments.Vireo.Vireo; + var vireoRunner = window.testHelpers.vireoRunner; + var fixtures = window.testHelpers.fixtures; + + var vireo = new Vireo(); + + var publicApiMultipleTypesViaUrl = fixtures.convertToAbsoluteFromFixturesDir('publicapi/MultipleTypes.via'); + var viName = 'MyVI'; + + var expectValidValueRef = function (valueRef) { + expect(valueRef).toBeNonEmptyObject(); + expect(valueRef.typeRef).toBeNumber(); + expect(valueRef.typeRef).not.toBe(0); + expect(valueRef.dataRef).toBeNumber(); + expect(valueRef.dataRef).not.toBe(0); + }; + + beforeAll(function (done) { + fixtures.preloadAbsoluteUrls([ + publicApiMultipleTypesViaUrl + ], done); + }); + + beforeAll(function () { + vireoRunner.rebootAndLoadVia(vireo, publicApiMultipleTypesViaUrl); + }); + + describe('for a cluster of scalars', function () { + var valueRef, + clusterOfScalarsPath = 'dataItem_ClusterOfScalars'; + + beforeAll(function () { + valueRef = vireo.eggShell.findValueRef(viName, clusterOfScalarsPath); + }); + + it('returns an object with field names as keys and valueRefs as values', function () { + var objectRef = vireo.eggShell.readValueRefObject(valueRef); + expectValidValueRef(objectRef.bool); + expectValidValueRef(objectRef.string); + expectValidValueRef(objectRef.double); + expectValidValueRef(objectRef.int32); + expectValidValueRef(objectRef.int64); + expectValidValueRef(objectRef.uint64); + expectValidValueRef(objectRef.complex); + expectValidValueRef(objectRef.time); + }); + + it('valueRefObject can be used to read from it', function () { + var objectValueRef = vireo.eggShell.readValueRefObject(valueRef); + expect(vireo.eggShell.readDouble(objectValueRef.bool)).toBe(1); + expect(vireo.eggShell.readDouble(objectValueRef.double)).toMatchIEEE754Number(3.14159); + expect(vireo.eggShell.readDouble(objectValueRef.int32)).toBe(42); + expect(vireo.eggShell.readDouble(objectValueRef.int64)).toBe(-72057594037927936); + expect(vireo.eggShell.readDouble(objectValueRef.uint64)).toBe(9223372041149743104); + expect(vireo.eggShell.readDouble(objectValueRef.time)).toBe(3564057536.423476); + }); + }); + + describe('for a cluster of scalars with encoded field names', function () { + var valueRef, + clusterOfScalarsPath = 'dataItem_ClusterOfEncodedScalars'; + + beforeAll(function () { + valueRef = vireo.eggShell.findValueRef(viName, clusterOfScalarsPath); + }); + + it('returns an object with decoded field names as keys', function () { + var objectRef = vireo.eggShell.readValueRefObject(valueRef); + expect(objectRef['bool fun']).toBeDefined(); + expect(objectRef['string fun']).toBeDefined(); + expect(objectRef['double fun']).toBeDefined(); + expect(objectRef['int32 fun']).toBeDefined(); + expect(objectRef['int64 fun']).toBeDefined(); + expect(objectRef['uint64 fun']).toBeDefined(); + expect(objectRef['complex fun']).toBeDefined(); + expect(objectRef['time fun']).toBeDefined(); + }); + }); + + describe('for a timestamp', function () { + var valueRef, + timestampPath = 'dataItem_Timestamp'; + + beforeAll(function () { + valueRef = vireo.eggShell.findValueRef(viName, timestampPath); + }); + + it('returns an object with keys "seconds" and "fraction" and valueRefs as values', function () { + var objectValueRef = vireo.eggShell.readValueRefObject(valueRef); + expectValidValueRef(objectValueRef.seconds); + expectValidValueRef(objectValueRef.fraction); + }); + }); + + describe('for an analogwaveform', function () { + var valueRef, + analogWaveformPath = 'wave_Double'; + + beforeAll(function () { + valueRef = vireo.eggShell.findValueRef(viName, analogWaveformPath); + }); + + it('returns an object with keys "dt", "t0" and "Y" and valueRefs as values', function () { + var objectValueRef = vireo.eggShell.readValueRefObject(valueRef); + expectValidValueRef(objectValueRef.t0); + expectValidValueRef(objectValueRef.dt); + expectValidValueRef(objectValueRef.Y); + }); + }); + + describe('for complex', function () { + var valueRef, + complexSinglePath = 'dataItem_ComplexSingle'; + + beforeAll(function () { + valueRef = vireo.eggShell.findValueRef(viName, complexSinglePath); + }); + + it('returns an object with keys "real" and "imaginary" and valueRefs as values', function () { + var objectValueRef = vireo.eggShell.readValueRefObject(valueRef); + expectValidValueRef(objectValueRef.real); + expectValidValueRef(objectValueRef.imaginary); + }); + }); + + describe('for path', function () { + var valueRef, + nipathPath = 'win32Path'; + + beforeAll(function () { + valueRef = vireo.eggShell.findValueRef(viName, nipathPath); + }); + + it('returns an object with keys "components" and "type" and valueReds as values', function () { + var objectValueRef = vireo.eggShell.readValueRefObject(valueRef); + expectValidValueRef(objectValueRef.components); + expectValidValueRef(objectValueRef.type); + }); + }); + + it('throws for any other type', function () { + var tryReadValueRefObject = function (path) { + return function () { + vireo.eggShell.readValueRefObject(vireo.eggShell.findValueRef(viName, path)); + }; + }; + + expect(tryReadValueRefObject('dataItem_ArrayOfBoolean')).toThrowError(/ValueRefObject/); + expect(tryReadValueRefObject('dataItem_ArrayOfClusters')).toThrowError(/ValueRefObject/); + expect(tryReadValueRefObject('dataItem_NumericSingle')).toThrowError(/ValueRefObject/); + expect(tryReadValueRefObject('dataItem_NumericDouble')).toThrowError(/ValueRefObject/); + expect(tryReadValueRefObject('booleanTrueValue')).toThrowError(/ValueRefObject/); + expect(tryReadValueRefObject('int8MinValue')).toThrowError(/ValueRefObject/); + expect(tryReadValueRefObject('int16MinValue')).toThrowError(/ValueRefObject/); + expect(tryReadValueRefObject('int32MinValue')).toThrowError(/ValueRefObject/); + expect(tryReadValueRefObject('int64MinSafeInteger')).toThrowError(/ValueRefObject/); + expect(tryReadValueRefObject('int64MinValue')).toThrowError(/ValueRefObject/); + expect(tryReadValueRefObject('uInt8MinValue')).toThrowError(/ValueRefObject/); + expect(tryReadValueRefObject('uInt16MinValue')).toThrowError(/ValueRefObject/); + expect(tryReadValueRefObject('uInt32MinValue')).toThrowError(/ValueRefObject/); + expect(tryReadValueRefObject('uInt64MinSafeInteger')).toThrowError(/ValueRefObject/); + expect(tryReadValueRefObject('dataItem_String')).toThrowError(/ValueRefObject/); + expect(tryReadValueRefObject('enum8alphabet')).toThrowError(/ValueRefObject/); + }); +}); diff --git a/test-it/karma/publicapi/ReadWriteErrorCluster.Test.js b/test-it/karma/publicapi/ReadWriteErrorCluster.Test.js new file mode 100644 index 000000000..361ee224b --- /dev/null +++ b/test-it/karma/publicapi/ReadWriteErrorCluster.Test.js @@ -0,0 +1,136 @@ +describe('The Vireo EggShell public api can', function () { + 'use strict'; + // Reference aliases + var Vireo = window.NationalInstruments.Vireo.Vireo; + var vireoRunner = window.testHelpers.vireoRunner; + var fixtures = window.testHelpers.fixtures; + + var vireo = new Vireo(); + + var publicApiReadWriteJSONViaUrl = fixtures.convertToAbsoluteFromFixturesDir('publicapi/ReadWriteErrorCluster.via'); + var viName = 'MyVI'; + + var readValue = function (valueRef) { + var json = vireo.eggShell.readJSON(valueRef); + return JSON.parse(json); + }; + + var readSubValue = function (valueRef, path) { + var subValueRef = vireo.eggShell.findSubValueRef(valueRef, path); + return readValue(subValueRef); + }; + + var writeValue = function (valueRef, value) { + var json = JSON.stringify(value); + vireo.eggShell.writeJSON(valueRef, json); + }; + + var writeSubValue = function (valueRef, subPath, value) { + var subValueRef = vireo.eggShell.findSubValueRef(valueRef, subPath); + writeValue(subValueRef, value); + }; + + var errorValue = { + status: true, + code: 12345, + source: 'Is this just fantasy?' + }; + + beforeAll(function (done) { + fixtures.preloadAbsoluteUrls([ + publicApiReadWriteJSONViaUrl + ], done); + }); + + beforeEach(function () { + vireo = new Vireo(); + vireoRunner.rebootAndLoadVia(vireo, publicApiReadWriteJSONViaUrl); + }); + + var testCases = [ + { + testName: 'error cluster with no default values', + path: 'uninitializedError' + }, + { + testName: 'error cluster with default values', + path: 'initializedError' + }, + { + testName: 'expanded error cluster with default values', + path: 'initializedExpandedError' + }, + { + testName: 'expanded error cluster with no default values', + path: 'expandedError' + } + ]; + + describe('write JSON and read each field with findSubValueRef of', function () { + testCases.forEach(function (testConfig) { + it(testConfig.testName, function () { + var valueRef = vireo.eggShell.findValueRef(viName, testConfig.path); + writeValue(valueRef, errorValue); + + expect(readSubValue(valueRef, 'status')).toEqual(errorValue.status); + expect(readSubValue(valueRef, 'code')).toEqual(errorValue.code); + expect(readSubValue(valueRef, 'source')).toEqual(errorValue.source); + + expect(readValue(valueRef)).toEqual(errorValue); + }); + }); + }); + + describe('write JSON and read each field with valueRefObject of', function () { + testCases.forEach(function (testConfig) { + it(testConfig.testName, function () { + var valueRef = vireo.eggShell.findValueRef(viName, testConfig.path); + writeValue(valueRef, errorValue); + var valueRefObject = vireo.eggShell.readValueRefObject(valueRef); + + expect(readValue(valueRefObject.status)).toEqual(errorValue.status); + expect(readValue(valueRefObject.code)).toEqual(errorValue.code); + expect(readValue(valueRefObject.source)).toEqual(errorValue.source); + + expect(readValue(valueRef)).toEqual(errorValue); + }); + }); + }); + + describe('write and read each field with valueRefObject of', function () { + testCases.forEach(function (testConfig) { + it(testConfig.testName, function () { + var valueRef = vireo.eggShell.findValueRef(viName, testConfig.path); + var valueRefObject = vireo.eggShell.readValueRefObject(valueRef); + + writeValue(valueRefObject.status, errorValue.status); + writeValue(valueRefObject.code, errorValue.code); + writeValue(valueRefObject.source, errorValue.source); + + expect(readValue(valueRefObject.status)).toEqual(errorValue.status); + expect(readValue(valueRefObject.code)).toEqual(errorValue.code); + expect(readValue(valueRefObject.source)).toEqual(errorValue.source); + + expect(readValue(valueRef)).toEqual(errorValue); + }); + }); + }); + + describe('write and read each field with findSubValueRef of', function () { + testCases.forEach(function (testConfig) { + it(testConfig.testName, function () { + var valueRef = vireo.eggShell.findValueRef(viName, testConfig.path); + + writeSubValue(valueRef, 'status', errorValue.status); + writeSubValue(valueRef, 'code', errorValue.code); + writeSubValue(valueRef, 'source', errorValue.source); + + expect(readSubValue(valueRef, 'status')).toEqual(errorValue.status); + expect(readSubValue(valueRef, 'code')).toEqual(errorValue.code); + expect(readSubValue(valueRef, 'source')).toEqual(errorValue.source); + + expect(readValue(valueRef)).toEqual(errorValue); + }); + }); + }); +}); diff --git a/test-it/karma/publicapi/ResizeArray.Test.js b/test-it/karma/publicapi/ResizeArray.Test.js index ad72cbadb..52a5fc424 100644 --- a/test-it/karma/publicapi/ResizeArray.Test.js +++ b/test-it/karma/publicapi/ResizeArray.Test.js @@ -5,84 +5,134 @@ describe('Arrays in Vireo', function () { var vireoRunner = window.testHelpers.vireoRunner; var fixtures = window.testHelpers.fixtures; - // Sharing Vireo instances across tests make them run soooo much faster var vireo = new Vireo(); - var viaPath = fixtures.convertToAbsoluteFromViaTestsDir('ArrayDemo.via'); - var viName = 'ArrayDemo'; + var testsArrayDemoViaUrl = fixtures.convertToAbsoluteFromViaTestsDir('ArrayDemo.via'); + var publicApiMultipleTypesViaUrl = fixtures.convertToAbsoluteFromFixturesDir('publicapi/MultipleTypes.via'); var runSlicesAsync; - beforeEach(function () { - runSlicesAsync = vireoRunner.rebootAndLoadVia(vireo, viaPath); + var resizeArrayHelper = function (viName, path, dimensions) { + var valueRef = vireo.eggShell.findValueRef(viName, path); + vireo.eggShell.resizeArray(valueRef, dimensions); + }; + + var getArrayDimensionsHelper = function (viName, path) { + var valueRef = vireo.eggShell.findValueRef(viName, path); + return vireo.eggShell.getArrayDimensions(valueRef); + }; + + beforeAll(function (done) { + fixtures.preloadAbsoluteUrls([ + testsArrayDemoViaUrl, + publicApiMultipleTypesViaUrl + ], done); }); - it('can be resized when they are variable size 1D array', function (done) { - runSlicesAsync(function (rawPrint, rawPrintError) { - expect(rawPrint).toBeNonEmptyString(); - expect(rawPrintError).toBeEmptyString(); + describe('when using array types', function () { + var viName = 'ArrayDemo'; + var resizeArray = resizeArrayHelper.bind(undefined, viName); + var getArrayDimensions = getArrayDimensionsHelper.bind(undefined, viName); - var resized = vireo.eggShell.resizeArray(viName, 'variableArray1d', [5]); - expect(resized).toBe(0); - expect(vireo.eggShell.getArrayDimLength(viName, 'variableArray1d', 0)).toBe(5); - done(); + beforeAll(function () { + runSlicesAsync = vireoRunner.rebootAndLoadVia(vireo, testsArrayDemoViaUrl); }); - }); - it('can be resized when they are 1d array with defaults', function (done) { - var variableName = 'variableArray1dwithDefaults'; - runSlicesAsync(function (rawPrint, rawPrintError) { - expect(rawPrint).toBeNonEmptyString(); - expect(rawPrintError).toBeEmptyString(); + it('can be resized when they are variable size 1D array', function (done) { + runSlicesAsync(function (rawPrint, rawPrintError) { + expect(rawPrint).toBeNonEmptyString(); + expect(rawPrintError).toBeEmptyString(); + + resizeArray('variableArray1d', [5]); + expect(getArrayDimensions('variableArray1d')).toEqual([5]); + done(); + }); + }); - var resized = vireo.eggShell.resizeArray(viName, variableName, [6]); + it('can be resized when they are 1d array with defaults', function (done) { + runSlicesAsync(function (rawPrint, rawPrintError) { + expect(rawPrint).toBeNonEmptyString(); + expect(rawPrintError).toBeEmptyString(); - expect(resized).toBe(0); - expect(vireo.eggShell.getArrayDimLength(viName, variableName, 0)).toBe(6); + resizeArray('variableArray1dwithDefaults', [6]); + expect(getArrayDimensions('variableArray1dwithDefaults')).toEqual([6]); - done(); + done(); + }); }); - }); - it('returns error code 1 if the array does not exist', function (done) { - var variableName = 'imaginaryArrayThatDoesNotExist'; - runSlicesAsync(function (rawPrint, rawPrintError) { - expect(rawPrint).toBeNonEmptyString(); - expect(rawPrintError).toBeEmptyString(); + it('cannot be resized if they are fixed 2d arrays', function (done) { + runSlicesAsync(function (rawPrint, rawPrintError) { + expect(rawPrint).toBeNonEmptyString(); + expect(rawPrintError).toBeEmptyString(); + + expect(resizeArray.bind(undefined, 'fixedArray2d', [3, 4])).toThrowError(/UnableToCreateReturnBuffer/); - var resized = vireo.eggShell.resizeArray(viName, variableName, [6]); - expect(resized).toBe(1); - done(); + done(); + }); }); - }); - it('cannot be resized if they are fixed 2d arrays', function (done) { - var variableName = 'fixedArray2d'; - runSlicesAsync(function (rawPrint, rawPrintError) { - expect(rawPrint).toBeNonEmptyString(); - expect(rawPrintError).toBeEmptyString(); + it('can be resized when they are variable 2d array', function (done) { + runSlicesAsync(function (rawPrint, rawPrintError) { + expect(rawPrint).toBeNonEmptyString(); + expect(rawPrintError).toBeEmptyString(); + + resizeArray('variableArray2d', [3, 4]); + expect(getArrayDimensions('variableArray2d')).toEqual([3, 4]); - var resized = vireo.eggShell.resizeArray(viName, variableName, [3, 4]); + done(); + }); + }); - expect(resized).toBe(2); - expect(vireo.eggShell.getArrayDimLength(viName, variableName, 0)).toBe(2); - expect(vireo.eggShell.getArrayDimLength(viName, variableName, 1)).toBe(3); + it('throws for dimension arrays that are invalid arrays', function (done) { + runSlicesAsync(function (rawPrint, rawPrintError) { + expect(rawPrint).toBeNonEmptyString(); + expect(rawPrintError).toBeEmptyString(); - done(); + expect(resizeArray.bind(undefined, 'variableArray2d', 'notAnArray')).toThrowError(/to be an array/); + + done(); + }); + }); + + it('throws for dimension arrays with non-numeric dimensions', function (done) { + runSlicesAsync(function (rawPrint, rawPrintError) { + expect(rawPrint).toBeNonEmptyString(); + expect(rawPrintError).toBeEmptyString(); + + expect(resizeArray.bind(undefined, 'variableArray2d', ['pen', 'pineapple'])).toThrowError(/numeric values/); + + done(); + }); + }); + + it('throws for new dimensions with wrong rank', function (done) { + runSlicesAsync(function (rawPrint, rawPrintError) { + expect(rawPrint).toBeNonEmptyString(); + expect(rawPrintError).toBeEmptyString(); + + expect(resizeArray.bind(undefined, 'variableArray2d', [1])).toThrowError(/MismatchedArrayRank/); + + done(); + }); }); }); - it('can be resized when they are variable 2d array', function (done) { - var variableName = 'variableArray2d'; - runSlicesAsync(function (rawPrint, rawPrintError) { - expect(rawPrint).toBeNonEmptyString(); - expect(rawPrintError).toBeEmptyString(); + describe('when using non-array types', function () { + var viName = 'MyVI'; + var resizeArray = resizeArrayHelper.bind(undefined, viName); + + beforeAll(function () { + runSlicesAsync = vireoRunner.rebootAndLoadVia(vireo, publicApiMultipleTypesViaUrl); + }); - var resized = vireo.eggShell.resizeArray(viName, variableName, [3, 4]); + it('will throw for a boolean type', function (done) { + runSlicesAsync(function (rawPrint, rawPrintError) { + expect(rawPrint).toBeEmptyString(); + expect(rawPrintError).toBeEmptyString(); - expect(resized).toBe(0); - expect(vireo.eggShell.getArrayDimLength(viName, variableName, 0)).toBe(3); - expect(vireo.eggShell.getArrayDimLength(viName, variableName, 1)).toBe(4); + expect(resizeArray.bind(undefined, 'dataItem_Boolean', [3, 4])).toThrowError(/UnexpectedObjectType/); - done(); + done(); + }); }); }); }); diff --git a/test-it/karma/publicapi/String.Test.js b/test-it/karma/publicapi/String.Test.js new file mode 100644 index 000000000..e0c7659df --- /dev/null +++ b/test-it/karma/publicapi/String.Test.js @@ -0,0 +1,241 @@ +describe('The Vireo EggShell String api can', function () { + 'use strict'; + // Reference aliases + var Vireo = window.NationalInstruments.Vireo.Vireo; + var vireoRunner = window.testHelpers.vireoRunner; + var fixtures = window.testHelpers.fixtures; + + var vireo = new Vireo(); + + var publicApiMultipleTypesViaUrl = fixtures.convertToAbsoluteFromFixturesDir('publicapi/MultipleTypes.via'); + var viName = 'MyVI'; + + var readString = function (path) { + return vireo.eggShell.readString(vireo.eggShell.findValueRef(viName, path)); + }; + + var tryReadString = function (path) { + return function () { + readString(path); + }; + }; + + var readTest = function (path, result) { + expect(readString(path)).toBe(result); + }; + + var writeString = function (path, str) { + vireo.eggShell.writeString(vireo.eggShell.findValueRef(viName, path), str); + }; + + var tryWriteString = function (path, value) { + return function () { + writeString(path, value); + }; + }; + + var writeTest = function (path, initialValue, newValue) { + expect(readString(path)).toBe(initialValue); + writeString(path, newValue); + expect(readString(path)).toBe(newValue); + writeString(path, initialValue); + expect(readString(path)).toBe(initialValue); + }; + + beforeAll(function (done) { + fixtures.preloadAbsoluteUrls([ + publicApiMultipleTypesViaUrl + ], done); + }); + + beforeAll(function () { + vireoRunner.rebootAndLoadVia(vireo, publicApiMultipleTypesViaUrl); + }); + + describe('use readString', function () { + it('to throw on unsupported types', function () { + expect(tryReadString('dataItem_NumericDouble')).toThrowError(/UnexpectedObjectType/); + }); + + it('String', function () { + readTest('dataItem_String', 'Hello'); + }); + + it('String Control Characters', function () { + readTest('dataItem_StringControlCharacters', + '\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008\u0009\u000A\u000B\u000C\u000D\u000E\u000F\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001A\u001B\u001C\u001D\u001E\u001F' + ); + }); + + it('String Other JSON Escaped Characters', function () { + readTest('dataItem_StringOtherJSONEscapedCharacters', + '\\"' + ); + }); + + it('String UTF-8 sequence ranges', function () { + readTest('dataItem_utf8sequence_firstinsequence1byte', + '\x00' + ); + readTest('dataItem_utf8sequence_firstinsequence2byte', + '\u0080' + ); + readTest('dataItem_utf8sequence_firstinsequence3byte', + '\u0800' + ); + readTest('dataItem_utf8sequence_firstinsequence4byte', + '\uD800\uDC00' + ); + readTest('dataItem_utf8sequence_firstinsequence5byte', + '\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD' + ); + readTest('dataItem_utf8sequence_lastinsequence1byte', + '\u007F' + ); + readTest('dataItem_utf8sequence_lastinsequence2byte', + '\u07FF' + ); + readTest('dataItem_utf8sequence_lastinsequence3byte', + '\uFFFF' + ); + readTest('dataItem_utf8sequence_lastinsequence4byte_lastvalidunicode', + '\uDBFF\uDFFF' + ); + readTest('dataItem_utf8sequence_lastinsequence4byte_firsthighinvalidutf8', + '\uFFFD\uFFFD\uFFFD\uFFFD' + ); + readTest('dataItem_utf8sequence_lastinsequence4byte_lasthighinvalidutf8', + '\uFFFD\uFFFD\uFFFD\uFFFD' + ); + }); + + it('String UTF-8 around surrogate border', function () { + readTest('dataItem_utf8sequence_lastbeforesurrogate', + '\uD7FF' + ); + readTest('dataItem_utf8sequence_firstinsurrogate', + '\uD800' + ); + readTest('dataItem_utf8sequence_lastinsurrogate', + '\uDFFF' + ); + readTest('dataItem_utf8sequence_firstaftersurrogate', + '\uE000' + ); + }); + + it('Invalid continuation bytes', function () { + readTest('dataItem_utf8sequence_firstcontinuationbyte', + '\uFFFD' + ); + readTest('dataItem_utf8sequence_lastcontinuationbyte', + '\uFFFD' + ); + readTest('dataItem_utf8sequence_2continuationbytes', + '\uFFFD\uFFFD' + ); + readTest('dataItem_utf8sequence_3continuationbytes', + '\uFFFD\uFFFD\uFFFD' + ); + readTest('dataItem_utf8sequence_4continuationbytes', + '\uFFFD\uFFFD\uFFFD\uFFFD' + ); + readTest('dataItem_utf8sequence_5continuationbytes', + '\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD' + ); + readTest('dataItem_utf8sequence_6continuationbytes', + '\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD' + ); + readTest('dataItem_utf8sequence_7continuationbytes', + '\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD' + ); + readTest('dataItem_utf8sequence_allcontinuationbytes', + Array(65).join('\uFFFD') + ); + }); + + it('Invalid start bytes', function () { + readTest('dataItem_utf8sequence_allstartbytesfor2bytes', + Array(33).join('\uFFFD ').trim() + ); + readTest('dataItem_utf8sequence_allstartbytesfor3bytes', + Array(17).join('\uFFFD ').trim() + ); + readTest('dataItem_utf8sequence_allstartbytesfor4bytes', + Array(9).join('\uFFFD ').trim() + ); + readTest('dataItem_utf8sequence_allstartbytesfor5bytes', + Array(5).join('\uFFFD ').trim() + ); + readTest('dataItem_utf8sequence_allstartbytesfor6bytes', + Array(3).join('\uFFFD ').trim() + ); + }); + + it('Missing last byte in sequence', function () { + readTest('dataItem_utf8sequence_2bytesequencewithlastbytemissing', + '\uFFFD' + ); + readTest('dataItem_utf8sequence_3bytesequencewithlastbytemissing', + '\uFFFD\uFFFD' + ); + readTest('dataItem_utf8sequence_4bytesequencewithlastbytemissing', + '\uFFFD\uFFFD\uFFFD' + ); + readTest('dataItem_utf8sequence_concatenatedtruncatedsequences', + '\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD' + ); + }); + + it('Impossible UTF-8 sequences', function () { + readTest('dataItem_utf8sequence_impossible1', + '\uFFFD' + ); + readTest('dataItem_utf8sequence_impossible2', + '\uFFFD' + ); + readTest('dataItem_utf8sequence_impossible3', + '\uFFFD\uFFFD\uFFFD\uFFFD' + ); + }); + + it('Overlong UTF-8 sequences', function () { + readTest('dataItem_utf8sequence_overlongnull2byte', + '\uFFFD\uFFFD' + ); + readTest('dataItem_utf8sequence_overlongnull3byte', + '\uFFFD\uFFFD\uFFFD' + ); + readTest('dataItem_utf8sequence_overlongnull4byte', + '\uFFFD\uFFFD\uFFFD\uFFFD' + ); + readTest('dataItem_utf8sequence_overlonglargest2byte', + '\uFFFD\uFFFD' + ); + readTest('dataItem_utf8sequence_overlonglargest3byte', + '\uFFFD\uFFFD\uFFFD' + ); + readTest('dataItem_utf8sequence_overlonglargest4byte', + '\uFFFD\uFFFD\uFFFD\uFFFD' + ); + }); + }); + + describe('use writeString', function () { + it('to throw on unsupported types', function () { + expect(tryWriteString('dataItem_NumericDouble', 'Test')).toThrowError(/UnexpectedObjectType/); + expect(tryWriteString('dataItem_String', 42)).toThrowError(/type string/); + }); + + it('to write different string values from memory', function () { + writeTest('dataItem_String', 'Hello', 'Hello World! :D'); + writeTest('dataItem_String', 'Hello', 'Iñtërnâtiônàlizætiøn☃💩'); + }); + + it('String Control Characters', function () { + writeTest('dataItem_String', 'Hello', + '\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008\u0009\u000A\u000B\u000C\u000D\u000E\u000F\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001A\u001B\u001C\u001D\u001E\u001F' + ); + }); + }); +}); diff --git a/test-it/karma/publicapi/TypeReflection.Test.js b/test-it/karma/publicapi/TypeReflection.Test.js new file mode 100644 index 000000000..76bb5f768 --- /dev/null +++ b/test-it/karma/publicapi/TypeReflection.Test.js @@ -0,0 +1,200 @@ +describe('The Vireo EggShell Reflection API', function () { + 'use strict'; + + var Vireo = window.NationalInstruments.Vireo.Vireo; + var vireoRunner = window.testHelpers.vireoRunner; + var fixtures = window.testHelpers.fixtures; + + var vireo = new Vireo(); + + var publicApiMultipleTypesViaUrl = fixtures.convertToAbsoluteFromFixturesDir('publicapi/MultipleTypes.via'); + var viName = 'MyVI'; + + var typeDescriptor = (function () { + var returnTypeName = function (name) { + return function () { + return name; + }; + }; + + return { + visitBoolean: returnTypeName('Boolean'), + visitEnum8: returnTypeName('Enum8'), + visitEnum16: returnTypeName('Enum16'), + visitEnum32: returnTypeName('Enum32'), + visitInt8: returnTypeName('Int8'), + visitInt16: returnTypeName('Int16'), + visitInt32: returnTypeName('Int32'), + visitInt64: returnTypeName('Int64'), + visitUInt8: returnTypeName('UInt8'), + visitUInt16: returnTypeName('UInt16'), + visitUInt32: returnTypeName('UInt32'), + visitUInt64: returnTypeName('UInt64'), + visitSingle: returnTypeName('Single'), + visitDouble: returnTypeName('Double'), + visitString: returnTypeName('String'), + visitComplexSingle: returnTypeName('ComplexSingle'), + visitComplexDouble: returnTypeName('ComplexDouble'), + visitAnalogWaveform: returnTypeName('AnalogWaveform'), + visitTimestamp: returnTypeName('Timestamp'), + visitPath: returnTypeName('Path'), + visitArray: returnTypeName('Array'), + visitCluster: returnTypeName('Cluster') + }; + }()); + + var validateValueRef = function (valueRef) { + expect(valueRef).toBeNonEmptyObject(); + expect(valueRef.typeRef).toBeNumber(); + expect(valueRef.typeRef).not.toBe(0); + expect(valueRef.dataRef).toBeNumber(); + expect(valueRef.dataRef).not.toBe(0); + }; + + beforeAll(function (done) { + fixtures.preloadAbsoluteUrls([ + publicApiMultipleTypesViaUrl + ], done); + }); + + beforeAll(function () { + vireoRunner.rebootAndLoadVia(vireo, publicApiMultipleTypesViaUrl); + }); + + var getTypeName = function (path) { + return vireo.eggShell.reflectOnValueRef(typeDescriptor, vireo.eggShell.findValueRef(viName, path)); + }; + + var reflectOnEmptyVisitor = function (path) { + var thePath = path; + return function () { + vireo.eggShell.reflectOnValueRef({}, vireo.eggShell.findValueRef(viName, thePath)); + }; + }; + + describe('reflectOnValueRef', function () { + describe('error handling', function () { + it('throws when typeDescriptor is not an object', function () { + var throwingFunction = function () { + vireo.eggShell.reflectOnValueRef([]); + }; + + expect(throwingFunction).toThrow(); + }); + + it('throws when valueRef is not an object', function () { + var throwingFunction = function () { + vireo.eggShell.reflectOnValueRef({}, []); + }; + + expect(throwingFunction).toThrow(); + }); + + it('throws when visitor does not have visit methods for all types', function () { + expect(reflectOnEmptyVisitor('int8MinValue')).toThrowError(/visitInt8/); + expect(reflectOnEmptyVisitor('int16MinValue')).toThrowError(/visitInt16/); + expect(reflectOnEmptyVisitor('int32MinValue')).toThrowError(/visitInt32/); + expect(reflectOnEmptyVisitor('int64MinSafeInteger')).toThrowError(/visitInt64/); + expect(reflectOnEmptyVisitor('uInt8MinValue')).toThrowError(/visitUInt8/); + expect(reflectOnEmptyVisitor('uInt16MinValue')).toThrowError(/visitUInt16/); + expect(reflectOnEmptyVisitor('uInt32MinValue')).toThrowError(/visitUInt32/); + expect(reflectOnEmptyVisitor('uInt64MinSafeInteger')).toThrowError(/visitUInt64/); + expect(reflectOnEmptyVisitor('dataItem_NumericSingle')).toThrowError(/visitSingle/); + expect(reflectOnEmptyVisitor('dataItem_NumericDouble')).toThrowError(/visitDouble/); + expect(reflectOnEmptyVisitor('dataItem_ComplexSingle')).toThrowError(/visitComplexSingle/); + expect(reflectOnEmptyVisitor('dataItem_Complex')).toThrowError(/visitComplexDouble/); + expect(reflectOnEmptyVisitor('dataItem_String')).toThrowError(/visitString/); + expect(reflectOnEmptyVisitor('dataItem_Boolean')).toThrowError(/visitBoolean/); + expect(reflectOnEmptyVisitor('dataItem_Timestamp')).toThrowError(/visitTimestamp/); + expect(reflectOnEmptyVisitor('enum8alphabet')).toThrowError(/visitEnum8/); + expect(reflectOnEmptyVisitor('enum16numbers')).toThrowError(/visitEnum16/); + expect(reflectOnEmptyVisitor('enum32colors')).toThrowError(/visitEnum32/); + expect(reflectOnEmptyVisitor('wave_Double')).toThrowError(/visitAnalogWaveform/); + expect(reflectOnEmptyVisitor('dataItem_ClusterOfScalars')).toThrowError(/visitCluster/); + expect(reflectOnEmptyVisitor('dataItem_ArrayOfBoolean')).toThrowError(/visitArray/); + }); + + it('throws for unsupported enum types', function () { + expect(reflectOnEmptyVisitor('enum64releases')).toThrowError(/Unexpected size/); + }); + }); + + describe('dispatches a call on visitor', function () { + it('for all numerics', function () { + expect(getTypeName('int8MinValue')).toEqual('Int8'); + expect(getTypeName('int16MinValue')).toEqual('Int16'); + expect(getTypeName('int32MinValue')).toEqual('Int32'); + expect(getTypeName('int64MinSafeInteger')).toEqual('Int64'); + expect(getTypeName('uInt8MinValue')).toEqual('UInt8'); + expect(getTypeName('uInt16MinValue')).toEqual('UInt16'); + expect(getTypeName('uInt32MinValue')).toEqual('UInt32'); + expect(getTypeName('uInt64MinSafeInteger')).toEqual('UInt64'); + expect(getTypeName('dataItem_NumericSingle')).toEqual('Single'); + expect(getTypeName('dataItem_NumericDouble')).toEqual('Double'); + expect(getTypeName('dataItem_ComplexSingle')).toEqual('ComplexSingle'); + expect(getTypeName('dataItem_Complex')).toEqual('ComplexDouble'); + }); + + it('for strings', function () { + expect(getTypeName('dataItem_String')).toEqual('String'); + }); + + it('for booleans', function () { + expect(getTypeName('dataItem_Boolean')).toEqual('Boolean'); + }); + + it('for aggregate types', function () { + expect(getTypeName('dataItem_Timestamp')).toEqual('Timestamp'); + expect(getTypeName('enum8alphabet')).toEqual('Enum8'); + expect(getTypeName('enum16numbers')).toEqual('Enum16'); + expect(getTypeName('enum32colors')).toEqual('Enum32'); + expect(getTypeName('wave_Double')).toEqual('AnalogWaveform'); + expect(getTypeName('dataItem_ClusterOfScalars')).toEqual('Cluster'); + expect(getTypeName('dataItem_ArrayOfBoolean')).toEqual('Array'); + }); + + it('with "this" bound to the visitor a valid valueRef and data as arguments', function () { + var dummyData = {}; + var visitor = {}; + var validateVisitArgs = function (valueRef, data) { + /* eslint-disable no-invalid-this */ + expect(this).toBe(visitor); + validateValueRef(valueRef); + expect(data).toBe(dummyData); + }; + + for (var visitkey in typeDescriptor) { + if (typeDescriptor.hasOwnProperty(visitkey)) { + visitor[visitkey] = validateVisitArgs; + } + } + + var visitorArgsTest = function (path) { + vireo.eggShell.reflectOnValueRef(visitor, vireo.eggShell.findValueRef(viName, path), dummyData); + }; + + visitorArgsTest('int8MinValue'); + visitorArgsTest('int16MinValue'); + visitorArgsTest('int32MinValue'); + visitorArgsTest('int64MinSafeInteger'); + visitorArgsTest('uInt8MinValue'); + visitorArgsTest('uInt16MinValue'); + visitorArgsTest('uInt32MinValue'); + visitorArgsTest('uInt64MinSafeInteger'); + visitorArgsTest('dataItem_NumericSingle'); + visitorArgsTest('dataItem_NumericDouble'); + visitorArgsTest('dataItem_ComplexSingle'); + visitorArgsTest('dataItem_Complex'); + visitorArgsTest('dataItem_String'); + visitorArgsTest('dataItem_Boolean'); + visitorArgsTest('dataItem_Timestamp'); + visitorArgsTest('enum8alphabet'); + visitorArgsTest('enum16numbers'); + visitorArgsTest('enum32colors'); + visitorArgsTest('wave_Double'); + visitorArgsTest('dataItem_ClusterOfScalars'); + visitorArgsTest('dataItem_ArrayOfBoolean'); + }); + }); + }); +}); diff --git a/test-it/karma/publicapi/TypeWaveform.test.js b/test-it/karma/publicapi/TypeWaveform.Test.js similarity index 86% rename from test-it/karma/publicapi/TypeWaveform.test.js rename to test-it/karma/publicapi/TypeWaveform.Test.js index 07ea5af7c..7b500c404 100644 --- a/test-it/karma/publicapi/TypeWaveform.test.js +++ b/test-it/karma/publicapi/TypeWaveform.Test.js @@ -24,8 +24,8 @@ describe('Peek/Poke different datatypes', function () { expect(viPathParser('wave_dbl_1')).toEqual({ t0: { - seconds: 3566073600, - fraction: 123 + seconds: '3566073600', + fraction: '123' }, dt: 5.8, Y: [1.2, 1.3, 1, -0.5] // eslint-disable-line id-length @@ -33,8 +33,8 @@ describe('Peek/Poke different datatypes', function () { expect(viPathParser('wave_i32_1')).toEqual({ t0: { - seconds: 0, - fraction: 0 + seconds: '0', + fraction: '0' }, dt: 0, Y: [] // eslint-disable-line id-length @@ -42,8 +42,8 @@ describe('Peek/Poke different datatypes', function () { var newValue = { t0: { - seconds: 50000, - fraction: 456 + seconds: '50000', + fraction: '456' }, dt: 10.5, Y: [5, 25] // eslint-disable-line id-length @@ -53,8 +53,8 @@ describe('Peek/Poke different datatypes', function () { var newValue2 = { t0: { - seconds: 60000, - fraction: 656 + seconds: '60000', + fraction: '656' }, dt: 20.5, Y: [45, 55] // eslint-disable-line id-length @@ -67,8 +67,8 @@ describe('Peek/Poke different datatypes', function () { runSlicesAsync(function () { expect(viPathParser('wave_i32_1')).toEqual({ t0: { - seconds: 456, - fraction: 123 + seconds: '456', + fraction: '123' }, dt: 6.8, Y: [10, 20, 30] // eslint-disable-line id-length diff --git a/test-it/karma/publicapi/TypedArray.Test.js b/test-it/karma/publicapi/TypedArray.Test.js new file mode 100644 index 000000000..5d5ff0fbf --- /dev/null +++ b/test-it/karma/publicapi/TypedArray.Test.js @@ -0,0 +1,100 @@ +describe('The Vireo EggShell Typed Array api', function () { + 'use strict'; + // Reference aliases + var Vireo = window.NationalInstruments.Vireo.Vireo; + var vireoRunner = window.testHelpers.vireoRunner; + var fixtures = window.testHelpers.fixtures; + + var vireo; + + var publicApiArrayTypesOptimizedViaUrl = fixtures.convertToAbsoluteFromFixturesDir('publicapi/ArrayTypesOptimized.via'); + + beforeAll(function (done) { + fixtures.preloadAbsoluteUrls([ + publicApiArrayTypesOptimizedViaUrl + ], done); + }); + + beforeEach(function () { + vireo = new Vireo(); + }); + + var viName = 'ArrayTypesOptimized'; + var readTypedArray = function (path) { + var valueRef = vireo.eggShell.findValueRef(viName, path); + var typedArray = vireo.eggShell.readTypedArray(valueRef); + return typedArray; + }; + + it('can read arrays for specific optimized types', function (done) { + var runSlicesAsync = vireoRunner.rebootAndLoadVia(vireo, publicApiArrayTypesOptimizedViaUrl); + + runSlicesAsync(function (rawPrint, rawPrintError) { + expect(rawPrint).toBeEmptyString(); + expect(rawPrintError).toBeEmptyString(); + + expect(readTypedArray('arrayInt8') instanceof Int8Array).toBeTrue(); + expect(readTypedArray('arrayInt8')).toEqual(new Int8Array([8, 6, 7, 5, 3, 0, 9, 0, -128, 127])); + expect(readTypedArray('arrayInt16') instanceof Int16Array).toBeTrue(); + expect(readTypedArray('arrayInt16')).toEqual(new Int16Array([8, 6, 7, 5, 3, 0, 9, 0, -32768, 32767])); + expect(readTypedArray('arrayInt32') instanceof Int32Array).toBeTrue(); + expect(readTypedArray('arrayInt32')).toEqual(new Int32Array([8, 6, 7, 5, 3, 0, 9, 0, -2147483648, 2147483647])); + expect(readTypedArray('arrayUInt8') instanceof Uint8Array).toBeTrue(); + expect(readTypedArray('arrayUInt8')).toEqual(new Uint8Array([8, 6, 7, 5, 3, 0, 9, 0, 255])); + expect(readTypedArray('arrayUInt16') instanceof Uint16Array).toBeTrue(); + expect(readTypedArray('arrayUInt16')).toEqual(new Uint16Array([8, 6, 7, 5, 3, 0, 9, 0, 65535])); + expect(readTypedArray('arrayUInt32') instanceof Uint32Array).toBeTrue(); + expect(readTypedArray('arrayUInt32')).toEqual(new Uint32Array([8, 6, 7, 5, 3, 0, 9, 0, 4294967295])); + expect(readTypedArray('arraySingle') instanceof Float32Array).toBeTrue(); + expect(readTypedArray('arraySingle')).toEqual(new Float32Array([Math.fround(1.1), Math.fround(2.2), +0, -0, Infinity, NaN, -Infinity, -16777216, 16777216])); + expect(readTypedArray('arrayDouble') instanceof Float64Array).toBeTrue(); + expect(readTypedArray('arrayDouble')).toEqual(new Float64Array([1.1, 2.2, +0, -0, Infinity, NaN, -Infinity, -9007199254740992, 9007199254740992])); + expect(readTypedArray('array2DInt32') instanceof Int32Array).toBeTrue(); + expect(readTypedArray('array2DInt32')).toEqual(new Int32Array([1, 2, 3, 4, 5, 6, 7, 8, 9])); + expect(readTypedArray('array3DInt32') instanceof Int32Array).toBeTrue(); + expect(readTypedArray('array3DInt32')).toEqual(new Int32Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18])); + expect(readTypedArray('arrayInt8Empty') instanceof Int8Array).toBeTrue(); + expect(readTypedArray('arrayInt8Empty')).toEqual(new Int8Array([])); + expect(readTypedArray('array2DInt8Empty') instanceof Int8Array).toBeTrue(); + expect(readTypedArray('array2DInt8Empty')).toEqual(new Int8Array([])); + expect(readTypedArray('array3DInt8Empty') instanceof Int8Array).toBeTrue(); + expect(readTypedArray('array3DInt8Empty')).toEqual(new Int8Array([])); + expect(readTypedArray('arrayEnum8') instanceof Uint8Array).toBeTrue(); + expect(readTypedArray('arrayEnum8')).toEqual(new Uint8Array([3, 2, 1])); + expect(readTypedArray('arrayEnum16') instanceof Uint16Array).toBeTrue(); + expect(readTypedArray('arrayEnum16')).toEqual(new Uint16Array([3, 2, 1])); + expect(readTypedArray('arrayEnum32') instanceof Uint32Array).toBeTrue(); + expect(readTypedArray('arrayEnum32')).toEqual(new Uint32Array([3, 2, 1])); + expect(readTypedArray('arrayBoolean') instanceof Uint8Array).toBeTrue(); + expect(readTypedArray('arrayBoolean')).toEqual(new Uint8Array([1, 0, 1, 0])); + expect(readTypedArray('stringHello') instanceof Uint8Array).toBeTrue(); + expect(readTypedArray('stringHello')).toEqual(new Uint8Array([0x48, 0x65, 0x6C, 0x6C, 0x6F])); + expect(readTypedArray('stringControlCharacters') instanceof Uint8Array).toBeTrue(); + expect(readTypedArray('stringControlCharacters')).toEqual(new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31])); + done(); + }); + }); + + it('errors with unsupported types', function (done) { + var runSlicesAsync = vireoRunner.rebootAndLoadVia(vireo, publicApiArrayTypesOptimizedViaUrl); + + runSlicesAsync(function (rawPrint, rawPrintError) { + expect(rawPrint).toBeEmptyString(); + expect(rawPrintError).toBeEmptyString(); + + expect(function () { + readTypedArray('arrayString'); + }).toThrowError(/UnexpectedObjectType/); + + expect(function () { + readTypedArray('nonExistantPath'); + }).toThrowError(/ObjectNotFoundAtPath/); + + expect(function () { + readTypedArray('scalarUInt32'); + }).toThrowError(/UnexpectedObjectType/); + + done(); + }); + }); +}); diff --git a/test-it/karma/publicapi/WriteJson.Test.js b/test-it/karma/publicapi/WriteJson.Test.js index 90d6ef105..c2b2e1b53 100644 --- a/test-it/karma/publicapi/WriteJson.Test.js +++ b/test-it/karma/publicapi/WriteJson.Test.js @@ -11,7 +11,8 @@ describe('The Vireo EggShell writeJSON api can write', function () { var publicApiMultipleTypesViaUrl = fixtures.convertToAbsoluteFromFixturesDir('publicapi/MultipleTypes.via'); var writeTest = function (path, oldVal, newVal) { - var oldValJSON = vireo.eggShell.readJSON(viName, path); + var valueRef = vireo.eggShell.findValueRef(viName, path); + var oldValJSON = vireo.eggShell.readJSON(valueRef); var oldValActual = JSON.parse(oldValJSON); expect(oldValActual).toMatchIEEE754Number(oldVal); @@ -24,9 +25,10 @@ describe('The Vireo EggShell writeJSON api can write', function () { return value; }); - vireo.eggShell.writeJSON(viName, path, newValToWriteJSON); + vireo.eggShell.writeJSON(valueRef, newValToWriteJSON); - var newValJSON = vireo.eggShell.readJSON(viName, path); + var newValueRef = vireo.eggShell.findValueRef(viName, path); + var newValJSON = vireo.eggShell.readJSON(newValueRef); var newValActual = JSON.parse(newValJSON); if (typeof newValActual === 'string' && typeof newVal === 'number') { // we're writing as a JS number, but reading always returns a string. @@ -35,8 +37,9 @@ describe('The Vireo EggShell writeJSON api can write', function () { expect(newValActual).toMatchIEEE754Number(newVal); } - vireo.eggShell.writeJSON(viName, path, oldValJSON); - var oldValRewriteJSON = vireo.eggShell.readJSON(viName, path); + vireo.eggShell.writeJSON(valueRef, oldValJSON); + var newNewValueRef = vireo.eggShell.findValueRef(viName, path); + var oldValRewriteJSON = vireo.eggShell.readJSON(newNewValueRef); var oldValRewrite = JSON.parse(oldValRewriteJSON); expect(oldValRewrite).toMatchIEEE754Number(oldVal); }; diff --git a/test-it/karma/utilities/TestHelpers.VireoRunner.js b/test-it/karma/utilities/TestHelpers.VireoRunner.js index 88d3eb567..c076bc16f 100644 --- a/test-it/karma/utilities/TestHelpers.VireoRunner.js +++ b/test-it/karma/utilities/TestHelpers.VireoRunner.js @@ -51,13 +51,16 @@ var createVIPathParser = function (vireo, viName) { return function (path) { - return JSON.parse(vireo.eggShell.readJSON(viName, path)); + var valueRef = vireo.eggShell.findValueRef(viName, path); + var json = vireo.eggShell.readJSON(valueRef); + return JSON.parse(json); }; }; var createVIPathWriter = function (vireo, viName) { return function (path, value) { - vireo.eggShell.writeJSON(viName, path, JSON.stringify(value)); + var valueRef = vireo.eggShell.findValueRef(viName, path); + vireo.eggShell.writeJSON(valueRef, JSON.stringify(value)); }; };