| //------------------------------------------------------------------------------------------------------- |
| // Copyright (C) Microsoft. All rights reserved. |
| // Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved. |
| // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. |
| //------------------------------------------------------------------------------------------------------- |
| #include "stdafx.h" |
| #pragma warning(disable:26434) // Function definition hides non-virtual function in base class |
| #pragma warning(disable:26439) // Implicit noexcept |
| #pragma warning(disable:26451) // Arithmetic overflow |
| #pragma warning(disable:26495) // Uninitialized member variable |
| #include "catch.hpp" |
| #include <array> |
| #include <process.h> |
| #include <suppress.h> |
| |
| #pragma warning(disable:4100) // unreferenced formal parameter |
| #pragma warning(disable:6387) // suppressing preFAST which raises warning for passing null to the JsRT APIs |
| #pragma warning(disable:6262) // CATCH is using stack variables to report errors, suppressing the preFAST warning. |
| |
| namespace JsRTApiTest |
| { |
| bool TestSetup(JsRuntimeAttributes attributes, JsRuntimeHandle *runtime) |
| { |
| JsValueRef context = JS_INVALID_REFERENCE; |
| JsValueRef setContext = JS_INVALID_REFERENCE; |
| |
| // Create runtime, context and set current context |
| REQUIRE(JsCreateRuntime(attributes, nullptr, runtime) == JsNoError); |
| REQUIRE(JsCreateContext(*runtime, &context) == JsNoError); |
| REQUIRE(JsSetCurrentContext(context) == JsNoError); |
| REQUIRE(((JsGetCurrentContext(&setContext) == JsNoError) || setContext == context)); |
| |
| return true; |
| } |
| |
| bool TestCleanup(JsRuntimeHandle runtime) |
| { |
| if (runtime != nullptr) |
| { |
| JsSetCurrentContext(nullptr); |
| JsDisposeRuntime(runtime); |
| } |
| return true; |
| } |
| |
| JsValueRef GetUndefined() |
| { |
| JsValueRef undefined = JS_INVALID_REFERENCE; |
| REQUIRE(JsGetUndefinedValue(&undefined) == JsNoError); |
| return undefined; |
| } |
| |
| template <class Handler> |
| void WithSetup(JsRuntimeAttributes attributes, Handler handler) |
| { |
| JsRuntimeHandle runtime = JS_INVALID_RUNTIME_HANDLE; |
| if (!TestSetup(attributes, &runtime)) |
| { |
| REQUIRE(false); |
| return; |
| } |
| |
| handler(attributes, runtime); |
| |
| TestCleanup(runtime); |
| } |
| |
| template <class Handler> |
| void RunWithAttributes(Handler handler) |
| { |
| WithSetup(JsRuntimeAttributeNone, handler); |
| WithSetup(JsRuntimeAttributeDisableBackgroundWork, handler); |
| WithSetup(JsRuntimeAttributeAllowScriptInterrupt, handler); |
| WithSetup(JsRuntimeAttributeEnableIdleProcessing, handler); |
| WithSetup(JsRuntimeAttributeDisableNativeCodeGeneration, handler); |
| WithSetup(JsRuntimeAttributeDisableExecutablePageAllocation, handler); |
| WithSetup(JsRuntimeAttributeDisableEval, handler); |
| WithSetup((JsRuntimeAttributes)(JsRuntimeAttributeDisableBackgroundWork | JsRuntimeAttributeAllowScriptInterrupt | JsRuntimeAttributeEnableIdleProcessing), handler); |
| } |
| |
| void ReferenceCountingTest(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) |
| { |
| JsContextRef context = JS_INVALID_REFERENCE; |
| REQUIRE(JsGetCurrentContext(&context) == JsNoError); |
| |
| CHECK(JsAddRef(context, nullptr) == JsNoError); |
| CHECK(JsRelease(context, nullptr) == JsNoError); |
| |
| JsValueRef undefined = JS_INVALID_REFERENCE; |
| |
| REQUIRE(JsGetUndefinedValue(&undefined) == JsNoError); |
| |
| REQUIRE(JsSetCurrentContext(nullptr) == JsNoError); |
| CHECK(JsAddRef(undefined, nullptr) == JsErrorNoCurrentContext); |
| CHECK(JsRelease(undefined, nullptr) == JsErrorNoCurrentContext); |
| |
| REQUIRE(JsSetCurrentContext(context) == JsNoError); |
| CHECK(JsAddRef(undefined, nullptr) == JsNoError); |
| CHECK(JsRelease(undefined, nullptr) == JsNoError); |
| |
| JsPropertyIdRef foo = JS_INVALID_REFERENCE; |
| |
| REQUIRE(JsGetPropertyIdFromName(_u("foo"), &foo) == JsNoError); |
| CHECK(JsAddRef(foo, nullptr) == JsNoError); |
| CHECK(JsRelease(foo, nullptr) == JsNoError); |
| } |
| |
| TEST_CASE("ApiTest_ReferenceCountingTest", "[ApiTest]") |
| { |
| JsRTApiTest::RunWithAttributes(JsRTApiTest::ReferenceCountingTest); |
| } |
| |
| void WeakReferenceTest(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) |
| { |
| JsValueRef valueRef = JS_INVALID_REFERENCE; |
| REQUIRE(JsCreateString("test", strlen("test"), &valueRef) == JsNoError); |
| |
| JsWeakRef weakRef = JS_INVALID_REFERENCE; |
| REQUIRE(JsCreateWeakReference(valueRef, &weakRef) == JsNoError); |
| |
| // JsGetWeakReferenceValue should return the original value reference. |
| JsValueRef valueRefFromWeakRef = JS_INVALID_REFERENCE; |
| CHECK(JsGetWeakReferenceValue(weakRef, &valueRefFromWeakRef) == JsNoError); |
| CHECK(valueRefFromWeakRef != JS_INVALID_REFERENCE); |
| CHECK(valueRefFromWeakRef == valueRef); |
| |
| // Clear the references on the stack, so that the value will be GC'd. |
| valueRef = JS_INVALID_REFERENCE; |
| valueRefFromWeakRef = JS_INVALID_REFERENCE; |
| |
| CHECK(JsCollectGarbage(runtime) == JsNoError); |
| |
| // JsGetWeakReferenceValue should return an invalid reference after the value was GC'd. |
| JsValueRef valueRefAfterGC = JS_INVALID_REFERENCE; |
| CHECK(JsGetWeakReferenceValue(weakRef, &valueRefAfterGC) == JsNoError); |
| CHECK(valueRefAfterGC == JS_INVALID_REFERENCE); |
| } |
| |
| TEST_CASE("ApiTest_WeakReferenceTest", "[ApiTest]") |
| { |
| JsRTApiTest::RunWithAttributes(JsRTApiTest::WeakReferenceTest); |
| } |
| |
| void ObjectsAndPropertiesTest1(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) |
| { |
| JsValueRef object = JS_INVALID_REFERENCE; |
| REQUIRE(JsCreateObject(&object) == JsNoError); |
| |
| JsPropertyIdRef name1 = JS_INVALID_REFERENCE; |
| const WCHAR* name = nullptr; |
| REQUIRE(JsGetPropertyIdFromName(_u("stringProperty1"), &name1) == JsNoError); |
| REQUIRE(JsGetPropertyNameFromId(name1, &name) == JsNoError); |
| CHECK(!wcscmp(name, _u("stringProperty1"))); |
| |
| JsPropertyIdType propertyIdType; |
| REQUIRE(JsGetPropertyIdType(name1, &propertyIdType) == JsNoError); |
| CHECK(propertyIdType == JsPropertyIdTypeString); |
| |
| JsPropertyIdRef name2 = JS_INVALID_REFERENCE; |
| REQUIRE(JsGetPropertyIdFromName(_u("stringProperty2"), &name2) == JsNoError); |
| |
| JsValueRef value1 = JS_INVALID_REFERENCE; |
| REQUIRE(JsPointerToString(_u("value1"), wcslen(_u("value1")), &value1) == JsNoError); |
| |
| JsValueRef value2 = JS_INVALID_REFERENCE; |
| REQUIRE(JsPointerToString(_u("value1"), wcslen(_u("value1")), &value2) == JsNoError); |
| |
| REQUIRE(JsSetProperty(object, name1, value1, true) == JsNoError); |
| REQUIRE(JsSetProperty(object, name2, value2, true) == JsNoError); |
| |
| JsValueRef value1Check = JS_INVALID_REFERENCE; |
| REQUIRE(JsGetProperty(object, name1, &value1Check) == JsNoError); |
| CHECK(value1 == value1Check); |
| |
| JsValueRef value2Check = JS_INVALID_REFERENCE; |
| REQUIRE(JsGetProperty(object, name2, &value2Check) == JsNoError); |
| CHECK(value1 == value1Check); |
| } |
| |
| TEST_CASE("ApiTest_ObjectsAndPropertiesTest1", "[ApiTest]") |
| { |
| JsRTApiTest::RunWithAttributes(JsRTApiTest::ObjectsAndPropertiesTest1); |
| } |
| |
| void ObjectsAndPropertiesTest2(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) |
| { |
| // Run a script to setup some globals |
| JsValueRef function = JS_INVALID_REFERENCE; |
| |
| LPCWSTR script = nullptr; |
| REQUIRE(FileLoadHelpers::LoadScriptFromFile("ObjectsAndProperties2.js", script) == S_OK); |
| REQUIRE(script != nullptr); |
| |
| REQUIRE(JsParseScript(script, JS_SOURCE_CONTEXT_NONE, _u(""), &function) == JsNoError); |
| |
| JsValueRef args[] = { JS_INVALID_REFERENCE }; |
| REQUIRE(JsCallFunction(function, nullptr, 10, nullptr) == JsErrorInvalidArgument); |
| REQUIRE(JsCallFunction(function, args, 0, nullptr) == JsErrorInvalidArgument); |
| REQUIRE(JsCallFunction(function, args, _countof(args), nullptr) == JsErrorInvalidArgument); |
| args[0] = GetUndefined(); |
| REQUIRE(JsCallFunction(function, args, _countof(args), nullptr) == JsNoError); |
| |
| // Get proto properties |
| JsValueRef circle = JS_INVALID_REFERENCE; |
| REQUIRE(JsRunScript(_u("new Circle()"), JS_SOURCE_CONTEXT_NONE, _u(""), &circle) == JsNoError); |
| |
| JsPropertyIdRef name = JS_INVALID_REFERENCE; |
| REQUIRE(JsGetPropertyIdFromName(_u("color"), &name) == JsNoError); |
| |
| JsValueRef value = JS_INVALID_REFERENCE; |
| REQUIRE(JsGetProperty(circle, name, &value) == JsNoError); |
| |
| JsValueRef asString = JS_INVALID_REFERENCE; |
| REQUIRE(JsConvertValueToString(value, &asString) == JsNoError); |
| |
| LPCWSTR str = nullptr; |
| size_t length; |
| REQUIRE(JsStringToPointer(asString, &str, &length) == JsNoError); |
| REQUIRE(!wcscmp(str, _u("white"))); |
| } |
| |
| TEST_CASE("ApiTest_ObjectsAndPropertiesTest2", "[ApiTest]") |
| { |
| JsRTApiTest::RunWithAttributes(JsRTApiTest::ObjectsAndPropertiesTest2); |
| } |
| |
| void DeleteObjectIndexedPropertyBug(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) |
| { |
| JsValueRef object; |
| REQUIRE(JsRunScript(_u("({a: 'a', 1: 1, 100: 100})"), JS_SOURCE_CONTEXT_NONE, _u(""), &object) == JsNoError); |
| |
| JsPropertyIdRef idRef = JS_INVALID_REFERENCE; |
| JsValueRef result = JS_INVALID_REFERENCE; |
| // delete property "a" triggers PathTypeHandler -> SimpleDictionaryTypeHandler |
| REQUIRE(JsGetPropertyIdFromName(_u("a"), &idRef) == JsNoError); |
| REQUIRE(JsDeleteProperty(object, idRef, false, &result) == JsNoError); |
| // Now delete property "100". Bug causes we always delete "1" instead. |
| REQUIRE(JsGetPropertyIdFromName(_u("100"), &idRef) == JsNoError); |
| REQUIRE(JsDeleteProperty(object, idRef, false, &result) == JsNoError); |
| |
| bool has; |
| JsValueRef indexRef = JS_INVALID_REFERENCE; |
| REQUIRE(JsIntToNumber(100, &indexRef) == JsNoError); |
| REQUIRE(JsHasIndexedProperty(object, indexRef, &has) == JsNoError); |
| CHECK(!has); // index 100 should be deleted |
| REQUIRE(JsIntToNumber(1, &indexRef) == JsNoError); |
| REQUIRE(JsHasIndexedProperty(object, indexRef, &has) == JsNoError); |
| CHECK(has); // index 1 should be intact |
| } |
| |
| TEST_CASE("ApiTest_DeleteObjectIndexedPropertyBug", "[ApiTest]") |
| { |
| JsRTApiTest::RunWithAttributes(JsRTApiTest::DeleteObjectIndexedPropertyBug); |
| } |
| |
| void HasOwnItemTest(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) |
| { |
| JsValueRef object; |
| REQUIRE(JsRunScript(_u("var obj = {a: [1,2], \"1\": 111}; obj.__proto__[3] = 333; obj;"), JS_SOURCE_CONTEXT_NONE, _u(""), &object) == JsNoError); |
| |
| JsPropertyIdRef idRef = JS_INVALID_REFERENCE; |
| JsValueRef result = JS_INVALID_REFERENCE; |
| // delete property "a" triggers PathTypeHandler -> SimpleDictionaryTypeHandler |
| REQUIRE(JsGetPropertyIdFromName(_u("a"), &idRef) == JsNoError); |
| REQUIRE(JsGetProperty(object, idRef, &result) == JsNoError); |
| bool hasOwnItem = false; |
| REQUIRE(JsHasOwnItem(result, 0, &hasOwnItem) == JsNoError); |
| CHECK(hasOwnItem); |
| |
| REQUIRE(JsHasOwnItem(result, 1, &hasOwnItem) == JsNoError); |
| CHECK(hasOwnItem); |
| |
| REQUIRE(JsHasOwnItem(result, 2, &hasOwnItem) == JsNoError); |
| CHECK(!hasOwnItem); // It does not have item on index 2 - so we should not be able to find that. |
| |
| REQUIRE(JsHasOwnItem(object, 1, &hasOwnItem) == JsNoError); |
| CHECK(hasOwnItem); |
| |
| REQUIRE(JsHasOwnItem(object, 3, &hasOwnItem) == JsNoError); |
| CHECK(!hasOwnItem); // index 3 is on prototype. |
| |
| bool has = false; |
| JsValueRef indexRef = JS_INVALID_REFERENCE; |
| REQUIRE(JsIntToNumber(3, &indexRef) == JsNoError); |
| REQUIRE(JsHasIndexedProperty(object, indexRef, &has) == JsNoError); |
| CHECK(has); // index 3 is prototype - so it should be able to find that. |
| } |
| |
| TEST_CASE("ApiTest_HasOwnItemTest", "[ApiTest]") |
| { |
| JsRTApiTest::RunWithAttributes(JsRTApiTest::HasOwnItemTest); |
| } |
| |
| void CALLBACK ExternalObjectFinalizeCallback(void *data) |
| { |
| CHECK(data == (void *)0xdeadbeef); |
| } |
| |
| void CALLBACK ExternalObjectTraceCallback(void *data) |
| { |
| CHECK(data == (void *)0xdeadbeef); |
| } |
| |
| void CrossContextSetPropertyTest(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) |
| { |
| bool hasExternalData; |
| JsContextRef oldContext = JS_INVALID_REFERENCE, secondContext = JS_INVALID_REFERENCE, testContext = JS_INVALID_REFERENCE; |
| JsValueRef secondValueRef = JS_INVALID_REFERENCE, secondObjectRef = JS_INVALID_REFERENCE, jsrtExternalObjectRef = JS_INVALID_REFERENCE, mainObjectRef = JS_INVALID_REFERENCE; |
| JsPropertyIdRef idRef = JS_INVALID_REFERENCE; |
| JsValueRef indexRef = JS_INVALID_REFERENCE; |
| REQUIRE(JsGetCurrentContext(&oldContext) == JsNoError); |
| REQUIRE(JsCreateObject(&mainObjectRef) == JsNoError); |
| REQUIRE(JsGetPropertyIdFromName(_u("prop1"), &idRef) == JsNoError); |
| REQUIRE(JsCreateContext(runtime, &secondContext) == JsNoError); |
| REQUIRE(JsSetCurrentContext(secondContext) == JsNoError); |
| REQUIRE(JsCreateObject(&secondObjectRef) == JsNoError); |
| |
| // Verify the main object is from first context |
| REQUIRE(JsGetContextOfObject(mainObjectRef, &testContext) == JsNoError); |
| REQUIRE(testContext == oldContext); |
| |
| // Create external Object in 2nd context which will be accessed in main context. |
| REQUIRE(JsCreateExternalObject((void *)0xdeadbeef, ExternalObjectFinalizeCallback, &jsrtExternalObjectRef) == JsNoError); |
| REQUIRE(JsIntToNumber(1, &secondValueRef) == JsNoError); |
| REQUIRE(JsIntToNumber(2, &indexRef) == JsNoError); |
| REQUIRE(JsSetCurrentContext(oldContext) == JsNoError); |
| |
| // Verify the second object is from second context |
| REQUIRE(JsGetContextOfObject(secondObjectRef, &testContext) == JsNoError); |
| CHECK(testContext == secondContext); |
| |
| REQUIRE(JsSetProperty(mainObjectRef, idRef, secondValueRef, false) == JsNoError); |
| REQUIRE(JsSetProperty(mainObjectRef, idRef, secondObjectRef, false) == JsNoError); |
| REQUIRE(JsSetIndexedProperty(mainObjectRef, indexRef, secondValueRef) == JsNoError); |
| REQUIRE(JsSetIndexedProperty(mainObjectRef, indexRef, secondObjectRef) == JsNoError); |
| REQUIRE(JsSetPrototype(jsrtExternalObjectRef, mainObjectRef) == JsNoError); |
| REQUIRE(JsHasExternalData(jsrtExternalObjectRef, &hasExternalData) == JsNoError); |
| REQUIRE(hasExternalData); |
| |
| JsValueRef object3 = JS_INVALID_REFERENCE; |
| JsGetterSetterInterceptor * interceptor3 = nullptr; |
| JsValueRef prototype2 = JS_INVALID_REFERENCE; |
| REQUIRE(JsCreateCustomExternalObject((void *)0xdeadbeef, 0, ExternalObjectTraceCallback, ExternalObjectFinalizeCallback, &interceptor3, prototype2, &object3) == JsNoError); |
| } |
| |
| TEST_CASE("ApiTest_CrossContextSetPropertyTest", "[ApiTest]") |
| { |
| JsRTApiTest::RunWithAttributes(JsRTApiTest::CrossContextSetPropertyTest); |
| } |
| |
| void CrossContextFunctionCall(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) |
| { |
| /* |
| 1. function changeFoo() { foo = 100 } |
| 2. CreateContext |
| 3. Set f : changeFoo in newContext |
| 4. Call f() from newContext |
| */ |
| JsContextRef oldContext = JS_INVALID_REFERENCE, secondContext = JS_INVALID_REFERENCE; |
| JsValueRef functionRef = JS_INVALID_REFERENCE, functionResultRef = JS_INVALID_REFERENCE, globalRef = JS_INVALID_REFERENCE, globalNewCtxRef = JS_INVALID_REFERENCE, valueRef = JS_INVALID_REFERENCE; |
| JsPropertyIdRef propertyIdFRef = JS_INVALID_REFERENCE, propertyIdFooRef = JS_INVALID_REFERENCE, propertyIdChangeFooRef = JS_INVALID_REFERENCE; |
| int answer; |
| |
| REQUIRE(JsGetCurrentContext(&oldContext) == JsNoError); |
| |
| REQUIRE(JsGetPropertyIdFromName(_u("f"), &propertyIdFRef) == JsNoError); |
| REQUIRE(JsGetPropertyIdFromName(_u("foo"), &propertyIdFooRef) == JsNoError); |
| REQUIRE(JsGetPropertyIdFromName(_u("changeFoo"), &propertyIdChangeFooRef) == JsNoError); |
| |
| //1. function changeFoo() { foo = 100 } |
| REQUIRE(JsRunScript(_u("foo = 3; function changeFoo() { foo = 100 }"), JS_SOURCE_CONTEXT_NONE, _u(""), &functionResultRef) == JsNoError); |
| |
| REQUIRE(JsGetGlobalObject(&globalRef) == JsNoError); |
| REQUIRE(JsGetProperty(globalRef, propertyIdChangeFooRef, &functionRef) == JsNoError); |
| |
| //2. CreateContext |
| REQUIRE(JsCreateContext(runtime, &secondContext) == JsNoError); |
| REQUIRE(JsSetCurrentContext(secondContext) == JsNoError); |
| |
| //3. Set f : changeFoo in newContext |
| REQUIRE(JsGetGlobalObject(&globalNewCtxRef) == JsNoError); |
| REQUIRE(JsSetProperty(globalNewCtxRef, propertyIdFRef, functionRef, false) == JsNoError); |
| |
| //4. Call 'f()' from newContext |
| REQUIRE(JsRunScript(_u("f()"), JS_SOURCE_CONTEXT_NONE, _u(""), &functionResultRef) == JsNoError); |
| |
| //5. Change context to oldContext |
| REQUIRE(JsSetCurrentContext(oldContext) == JsNoError); |
| |
| //6. Verify foo == 100 |
| REQUIRE(JsGetProperty(globalRef, propertyIdFooRef, &valueRef) == JsNoError); |
| REQUIRE(JsNumberToInt(valueRef, &answer) == JsNoError); |
| CHECK(answer == 100); |
| } |
| |
| TEST_CASE("ApiTest_CrossContextFunctionCall", "[ApiTest]") |
| { |
| JsRTApiTest::RunWithAttributes(JsRTApiTest::CrossContextSetPropertyTest); |
| } |
| |
| void ExternalDataOnJsrtContextTest(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) |
| { |
| int i = 5; |
| void *j; |
| JsContextRef currentContext = JS_INVALID_REFERENCE; |
| |
| REQUIRE(JsGetCurrentContext(¤tContext) == JsNoError); |
| REQUIRE(JsSetContextData(currentContext, &i) == JsNoError); |
| REQUIRE(JsGetContextData(currentContext, &j) == JsNoError); |
| CHECK(static_cast<int*>(j) == &i); |
| } |
| |
| TEST_CASE("ApiTest_ExternalDataOnJsrtContextTest", "[ApiTest]") |
| { |
| JsRTApiTest::RunWithAttributes(JsRTApiTest::ExternalDataOnJsrtContextTest); |
| } |
| |
| void ArrayAndItemTest(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) |
| { |
| // Create some arrays |
| JsValueRef array1 = JS_INVALID_REFERENCE; |
| JsValueRef array2 = JS_INVALID_REFERENCE; |
| |
| REQUIRE(JsCreateArray(0, &array1) == JsNoError); |
| REQUIRE(JsCreateArray(100, &array2) == JsNoError); |
| |
| // Create an object we'll treat like an array |
| JsValueRef object = JS_INVALID_REFERENCE; |
| REQUIRE(JsCreateObject(&object) == JsNoError); |
| |
| // Create an index value to use |
| JsValueRef index = JS_INVALID_REFERENCE; |
| REQUIRE(JsDoubleToNumber(3, &index) == JsNoError); |
| |
| JsValueRef value1 = JS_INVALID_REFERENCE; |
| REQUIRE(JsPointerToString(_u("value1"), wcslen(_u("value1")), &value1) == JsNoError); |
| |
| // Do some index-based manipulation |
| bool result; |
| REQUIRE(JsHasIndexedProperty(array1, index, &result) == JsNoError); |
| REQUIRE(result == false); |
| REQUIRE(JsHasIndexedProperty(array2, index, &result) == JsNoError); |
| REQUIRE(result == false); |
| REQUIRE(JsHasIndexedProperty(object, index, &result) == JsNoError); |
| REQUIRE(result == false); |
| |
| REQUIRE(JsSetIndexedProperty(array1, index, value1) == JsNoError); |
| REQUIRE(JsSetIndexedProperty(array2, index, value1) == JsNoError); |
| REQUIRE(JsSetIndexedProperty(object, index, value1) == JsNoError); |
| |
| REQUIRE(JsHasIndexedProperty(array1, index, &result) == JsNoError); |
| REQUIRE(result == true); |
| REQUIRE(JsHasIndexedProperty(array2, index, &result) == JsNoError); |
| REQUIRE(result == true); |
| REQUIRE(JsHasIndexedProperty(object, index, &result) == JsNoError); |
| REQUIRE(result == true); |
| |
| JsValueRef value2 = JS_INVALID_REFERENCE; |
| REQUIRE(JsGetIndexedProperty(array1, index, &value2) == JsNoError); |
| REQUIRE(JsStrictEquals(value1, value2, &result) == JsNoError); |
| REQUIRE(result == true); |
| REQUIRE(JsGetIndexedProperty(array2, index, &value2) == JsNoError); |
| REQUIRE(JsStrictEquals(value1, value2, &result) == JsNoError); |
| REQUIRE(result == true); |
| REQUIRE(JsGetIndexedProperty(object, index, &value2) == JsNoError); |
| REQUIRE(JsStrictEquals(value1, value2, &result) == JsNoError); |
| REQUIRE(result == true); |
| |
| REQUIRE(JsDeleteIndexedProperty(array1, index) == JsNoError); |
| REQUIRE(JsDeleteIndexedProperty(array2, index) == JsNoError); |
| REQUIRE(JsDeleteIndexedProperty(object, index) == JsNoError); |
| |
| REQUIRE(JsHasIndexedProperty(array1, index, &result) == JsNoError); |
| REQUIRE(result == false); |
| REQUIRE(JsHasIndexedProperty(array2, index, &result) == JsNoError); |
| REQUIRE(result == false); |
| REQUIRE(JsHasIndexedProperty(object, index, &result) == JsNoError); |
| REQUIRE(result == false); |
| } |
| |
| TEST_CASE("ApiTest_ArrayAndItemTest", "[ApiTest]") |
| { |
| JsRTApiTest::RunWithAttributes(JsRTApiTest::ArrayAndItemTest); |
| } |
| |
| void EqualsTest(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) |
| { |
| // Create some values |
| JsValueRef number1 = JS_INVALID_REFERENCE; |
| REQUIRE(JsDoubleToNumber(1, &number1) == JsNoError); |
| JsValueRef number2 = JS_INVALID_REFERENCE; |
| REQUIRE(JsDoubleToNumber(2, &number2) == JsNoError); |
| JsValueRef stringa = JS_INVALID_REFERENCE; |
| REQUIRE(JsPointerToString(_u("1"), wcslen(_u("1")), &stringa) == JsNoError); |
| JsValueRef stringb = JS_INVALID_REFERENCE; |
| REQUIRE(JsPointerToString(_u("1"), wcslen(_u("1")), &stringb) == JsNoError); |
| |
| bool result; |
| REQUIRE(JsEquals(number1, number1, &result) == JsNoError); |
| CHECK(result == true); |
| REQUIRE(JsEquals(number1, number2, &result) == JsNoError); |
| CHECK(result == false); |
| REQUIRE(JsEquals(number1, stringa, &result) == JsNoError); |
| CHECK(result == true); |
| REQUIRE(JsEquals(number1, stringb, &result) == JsNoError); |
| CHECK(result == true); |
| REQUIRE(JsEquals(number2, stringa, &result) == JsNoError); |
| CHECK(result == false); |
| REQUIRE(JsEquals(number2, stringb, &result) == JsNoError); |
| CHECK(result == false); |
| REQUIRE(JsEquals(stringa, stringb, &result) == JsNoError); |
| CHECK(result == true); |
| |
| REQUIRE(JsStrictEquals(number1, number1, &result) == JsNoError); |
| CHECK(result == true); |
| REQUIRE(JsStrictEquals(number1, number2, &result) == JsNoError); |
| CHECK(result == false); |
| REQUIRE(JsStrictEquals(number1, stringa, &result) == JsNoError); |
| CHECK(result == false); |
| REQUIRE(JsStrictEquals(number1, stringb, &result) == JsNoError); |
| CHECK(result == false); |
| REQUIRE(JsStrictEquals(number2, stringa, &result) == JsNoError); |
| CHECK(result == false); |
| REQUIRE(JsStrictEquals(number2, stringb, &result) == JsNoError); |
| CHECK(result == false); |
| REQUIRE(JsStrictEquals(stringa, stringb, &result) == JsNoError); |
| CHECK(result == true); |
| } |
| |
| TEST_CASE("ApiTest_EqualsTest", "[ApiTest]") |
| { |
| JsRTApiTest::RunWithAttributes(JsRTApiTest::EqualsTest); |
| } |
| |
| void InstanceOfTest(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) |
| { |
| JsValueRef F = JS_INVALID_REFERENCE; |
| REQUIRE(JsRunScript(_u("F = function(){}"), JS_SOURCE_CONTEXT_NONE, _u(""), &F) == JsNoError); |
| JsValueRef x = JS_INVALID_REFERENCE; |
| REQUIRE(JsRunScript(_u("new F()"), JS_SOURCE_CONTEXT_NONE, _u(""), &x) == JsNoError); |
| |
| bool instanceOf; |
| REQUIRE(JsInstanceOf(x, F, &instanceOf) == JsNoError); |
| REQUIRE(instanceOf); |
| |
| REQUIRE(JsCreateObject(&x) == JsNoError); |
| REQUIRE(JsInstanceOf(x, F, &instanceOf) == JsNoError); |
| CHECK(instanceOf == false); |
| } |
| |
| TEST_CASE("ApiTest_InstanceOfTest", "[ApiTest]") |
| { |
| JsRTApiTest::RunWithAttributes(JsRTApiTest::InstanceOfTest); |
| } |
| |
| void LanguageTypeConversionTest(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) |
| { |
| JsValueRef value = JS_INVALID_REFERENCE; |
| JsValueType type; |
| JsValueRef asString = JS_INVALID_REFERENCE; |
| LPCWSTR str = nullptr; |
| size_t length; |
| |
| // Number |
| |
| REQUIRE(JsDoubleToNumber(3.141592, &value) == JsNoError); |
| REQUIRE(JsGetValueType(value, &type) == JsNoError); |
| REQUIRE(type == JsNumber); |
| |
| double dbl = 0.0; |
| REQUIRE(JsNumberToDouble(value, &dbl) == JsNoError); |
| REQUIRE(dbl == 3.141592); |
| |
| REQUIRE(JsPointerToString(_u("3.141592"), wcslen(_u("3.141592")), &asString) == JsNoError); |
| REQUIRE(JsConvertValueToNumber(asString, &value) == JsNoError); |
| REQUIRE(JsNumberToDouble(value, &dbl) == JsNoError); |
| REQUIRE(dbl == 3.141592); |
| |
| int intValue; |
| REQUIRE(JsNumberToInt(value, &intValue) == JsNoError); |
| CHECK(3 == intValue); |
| |
| REQUIRE(JsDoubleToNumber(2147483648.1, &value) == JsNoError); |
| REQUIRE(JsNumberToInt(value, &intValue) == JsNoError); |
| CHECK(INT_MIN == intValue); |
| REQUIRE(JsDoubleToNumber(-2147483649.1, &value) == JsNoError); |
| REQUIRE(JsNumberToInt(value, &intValue) == JsNoError); |
| CHECK(2147483647 == intValue); |
| |
| // String |
| |
| REQUIRE(JsDoubleToNumber(3.141592, &value) == JsNoError); |
| REQUIRE(JsConvertValueToString(value, &asString) == JsNoError); |
| REQUIRE(JsStringToPointer(asString, &str, &length) == JsNoError); |
| CHECK(!wcscmp(str, _u("3.141592"))); |
| |
| REQUIRE(JsGetTrueValue(&value) == JsNoError); |
| |
| REQUIRE(JsConvertValueToString(value, &asString) == JsNoError); |
| REQUIRE(JsGetValueType(asString, &type) == JsNoError); |
| CHECK(type == JsString); |
| REQUIRE(JsStringToPointer(asString, &str, &length) == JsNoError); |
| CHECK(!wcscmp(str, _u("true"))); |
| |
| // Undefined |
| |
| REQUIRE(JsGetUndefinedValue(&value) == JsNoError); |
| REQUIRE(JsGetValueType(value, &type) == JsNoError); |
| CHECK(type == JsUndefined); |
| |
| // Null |
| |
| REQUIRE(JsGetNullValue(&value) == JsNoError); |
| REQUIRE(JsGetValueType(value, &type) == JsNoError); |
| CHECK(type == JsNull); |
| |
| // Boolean |
| |
| REQUIRE(JsGetTrueValue(&value) == JsNoError); |
| REQUIRE(JsGetValueType(value, &type) == JsNoError); |
| CHECK(type == JsBoolean); |
| |
| REQUIRE(JsGetFalseValue(&value) == JsNoError); |
| REQUIRE(JsGetValueType(value, &type) == JsNoError); |
| CHECK(type == JsBoolean); |
| |
| bool boolValue; |
| |
| REQUIRE(JsBoolToBoolean(true, &value) == JsNoError); |
| REQUIRE(JsGetValueType(value, &type) == JsNoError); |
| CHECK(type == JsBoolean); |
| REQUIRE(JsBooleanToBool(value, &boolValue) == JsNoError); |
| CHECK(boolValue == true); |
| |
| REQUIRE(JsPointerToString(_u("true"), wcslen(_u("true")), &asString) == JsNoError); |
| REQUIRE(JsConvertValueToBoolean(asString, &value) == JsNoError); |
| REQUIRE(JsGetValueType(value, &type) == JsNoError); |
| CHECK(type == JsBoolean); |
| REQUIRE(JsBooleanToBool(value, &boolValue) == JsNoError); |
| CHECK(boolValue == true); |
| |
| // Object |
| |
| REQUIRE(JsCreateObject(&value) == JsNoError); |
| REQUIRE(JsGetValueType(value, &type) == JsNoError); |
| CHECK(type == JsObject); |
| } |
| |
| TEST_CASE("ApiTest_LanguageTypeConversionTest", "[ApiTest]") |
| { |
| JsRTApiTest::RunWithAttributes(JsRTApiTest::LanguageTypeConversionTest); |
| } |
| |
| JsValueRef CALLBACK ExternalFunctionCallback(JsValueRef /* function */, bool /* isConstructCall */, JsValueRef * /* args */, USHORT /* cargs */, void * /* callbackState */) |
| { |
| return nullptr; |
| } |
| |
| void CALLBACK FinalizeCallback(JsValueRef object) |
| { |
| CHECK(object != JS_INVALID_REFERENCE); |
| } |
| |
| void CALLBACK OldFinalizeCallback(void *data) |
| { |
| CHECK(data == nullptr); |
| } |
| |
| JsValueRef CALLBACK ExternalFunctionPreScriptAbortionCallback(JsValueRef /* function */, bool /* isConstructCall */, JsValueRef * args /* args */, USHORT /* cargs */, void * /* callbackState */) |
| { |
| JsValueRef result = JS_INVALID_REFERENCE; |
| const WCHAR *scriptText = nullptr; |
| size_t scriptTextLen; |
| |
| REQUIRE(JsStringToPointer(args[0], &scriptText, &scriptTextLen) == JsNoError); |
| REQUIRE(JsRunScript(scriptText, JS_SOURCE_CONTEXT_NONE, _u(""), &result) == JsErrorScriptTerminated); |
| return nullptr; |
| } |
| |
| JsValueRef CALLBACK ExternalFunctionPostScriptAbortionCallback(JsValueRef /* function */, bool /* isConstructCall */, JsValueRef * args /* args */, USHORT /* cargs */, void * /* callbackState */) |
| { |
| JsValueRef result = JS_INVALID_REFERENCE; |
| const WCHAR *scriptText = nullptr; |
| size_t scriptTextLen; |
| |
| REQUIRE(JsStringToPointer(args[0], &scriptText, &scriptTextLen) == JsNoError); |
| REQUIRE(JsRunScript(scriptText, JS_SOURCE_CONTEXT_NONE, _u(""), &result) == JsErrorInDisabledState); |
| return nullptr; |
| } |
| |
| JsValueRef CALLBACK ErrorHandlingCallback(JsValueRef /* function */, bool /* isConstructCall */, JsValueRef * /* args */, USHORT /* cargs */, void * /* callbackState */) |
| { |
| JsValueRef result = JS_INVALID_REFERENCE; |
| |
| REQUIRE(JsRunScript(_u("new Error()"), JS_SOURCE_CONTEXT_NONE, _u(""), &result) == JsNoError); |
| REQUIRE(JsSetException(result) == JsNoError); |
| |
| return nullptr; |
| } |
| |
| |
| void ExternalFunctionTest(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) |
| { |
| JsValueRef function = JS_INVALID_REFERENCE; |
| REQUIRE(JsCreateFunction(ExternalFunctionCallback, nullptr, &function) == JsNoError); |
| JsValueRef undefined = JS_INVALID_REFERENCE; |
| REQUIRE(JsGetUndefinedValue(&undefined) == JsNoError); |
| JsValueRef args[] = { undefined }; |
| REQUIRE(JsCallFunction(function, args, 1, nullptr) == JsNoError); |
| JsValueRef result = JS_INVALID_REFERENCE; |
| REQUIRE(JsConstructObject(function, args, 1, &result) == JsNoError); |
| } |
| |
| TEST_CASE("ApiTest_ExternalFunctionTest", "[ApiTest]") |
| { |
| JsRTApiTest::RunWithAttributes(JsRTApiTest::ExternalFunctionTest); |
| } |
| |
| JsValueRef CALLBACK ExternalEnhancedFunctionTestCallback(JsValueRef callee, JsValueRef *arguments, unsigned short argumentCount, JsNativeFunctionInfo *info, void *callbackData) |
| { |
| REQUIRE(callbackData != nullptr); |
| REQUIRE(*static_cast<int*>(callbackData) == 123); |
| REQUIRE(argumentCount == 2); |
| |
| bool success = false; |
| JsValueRef _true; |
| REQUIRE(JsGetTrueValue(&_true) == JsNoError); |
| JsValueRef _false; |
| REQUIRE(JsGetFalseValue(&_false) == JsNoError); |
| |
| |
| REQUIRE(JsStrictEquals(_true, arguments[0], &success) == JsNoError); |
| REQUIRE(success); |
| REQUIRE(JsStrictEquals(_false, arguments[1], &success) == JsNoError); |
| REQUIRE(success); |
| |
| REQUIRE(!info->isConstructCall); |
| REQUIRE(info->thisArg == arguments[0]); |
| |
| JsValueRef undefined; |
| REQUIRE(JsGetUndefinedValue(&undefined) == JsNoError); |
| REQUIRE(JsStrictEquals(undefined, info->newTargetArg, &success) == JsNoError); |
| REQUIRE(success); |
| |
| JsValueRef _null; |
| REQUIRE(JsGetNullValue(&_null) == JsNoError); |
| return _null; |
| } |
| |
| JsValueRef CALLBACK ExternalEnhancedConstructorFunctionTestCallback(JsValueRef callee, JsValueRef *arguments, unsigned short argumentCount, JsNativeFunctionInfo *info, void *callbackData) |
| { |
| REQUIRE(callbackData != nullptr); |
| REQUIRE(*static_cast<int*>(callbackData) == 456); |
| REQUIRE(argumentCount == 3); |
| |
| bool success = false; |
| JsValueRef _true; |
| REQUIRE(JsGetTrueValue(&_true) == JsNoError); |
| JsValueRef _false; |
| REQUIRE(JsGetFalseValue(&_false) == JsNoError); |
| JsValueRef _null; |
| REQUIRE(JsGetNullValue(&_null) == JsNoError); |
| |
| REQUIRE(info->thisArg == arguments[0]); |
| REQUIRE(JsStrictEquals(_true, arguments[1], &success) == JsNoError); |
| REQUIRE(success); |
| REQUIRE(JsStrictEquals(_false, arguments[2], &success) == JsNoError); |
| REQUIRE(success); |
| |
| REQUIRE(info->isConstructCall); |
| |
| JsValueType t; |
| REQUIRE(JsGetValueType(info->newTargetArg, &t) == JsNoError); |
| REQUIRE(t == JsFunction); |
| REQUIRE(JsGetValueType(info->thisArg, &t) == JsNoError); |
| REQUIRE(t == JsObject); |
| |
| return info->thisArg; |
| } |
| |
| void ExternalEnhancedFunctionTest(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) |
| { |
| int sentinel = 123; |
| JsValueRef function = JS_INVALID_REFERENCE; |
| REQUIRE(JsCreateEnhancedFunction(ExternalEnhancedFunctionTestCallback, nullptr, &sentinel, &function) == JsNoError); |
| JsValueRef _true; |
| REQUIRE(JsGetTrueValue(&_true) == JsNoError); |
| JsValueRef _false; |
| REQUIRE(JsGetFalseValue(&_false) == JsNoError); |
| JsValueRef args[2] = { _true, _false }; |
| JsValueRef _null; |
| REQUIRE(JsGetNullValue(&_null) == JsNoError); |
| JsValueRef result; |
| REQUIRE(JsCallFunction(function, args, 2, &result) == JsNoError); |
| bool success; |
| REQUIRE(JsStrictEquals(_null, result, &success) == JsNoError); |
| REQUIRE(success); |
| |
| sentinel = 456; |
| function = JS_INVALID_REFERENCE; |
| REQUIRE(JsCreateEnhancedFunction(ExternalEnhancedConstructorFunctionTestCallback, nullptr, &sentinel, &function) == JsNoError); |
| JsValueRef ctorArgs[3] = { _null, _true, _false }; |
| REQUIRE(JsConstructObject(function, ctorArgs, 3, &result) == JsNoError); |
| JsValueType t; |
| REQUIRE(JsGetValueType(result, &t) == JsNoError); |
| REQUIRE(t == JsObject); |
| } |
| |
| TEST_CASE("ApiTest_ExternalEnhancedFunctionTest", "[ApiTest]") |
| { |
| JsRTApiTest::RunWithAttributes(JsRTApiTest::ExternalEnhancedFunctionTest); |
| } |
| |
| struct ExternalEnhancedBaseClassFunctionTestInfo |
| { |
| JsValueRef derived; |
| JsValueRef base; |
| }; |
| |
| JsValueRef CALLBACK ExternalEnhancedBaseClassFunctionTestCallback(JsValueRef callee, JsValueRef *arguments, unsigned short argumentCount, JsNativeFunctionInfo *info, void *callbackData) |
| { |
| REQUIRE(callbackData != nullptr); |
| |
| ExternalEnhancedBaseClassFunctionTestInfo* testinfo = (ExternalEnhancedBaseClassFunctionTestInfo*)callbackData; |
| JsValueType t; |
| REQUIRE(JsGetValueType(testinfo->derived, &t) == JsNoError); |
| REQUIRE(t == JsFunction); |
| REQUIRE(JsGetValueType(testinfo->base, &t) == JsNoError); |
| REQUIRE(t == JsFunction); |
| REQUIRE(argumentCount == 2); |
| |
| JsPropertyIdRef propId; |
| bool success = false; |
| JsValueRef _true; |
| REQUIRE(JsGetTrueValue(&_true) == JsNoError); |
| JsValueRef _false; |
| REQUIRE(JsGetFalseValue(&_false) == JsNoError); |
| |
| REQUIRE(info->thisArg == arguments[0]); |
| REQUIRE(JsStrictEquals(_true, arguments[1], &success) == JsNoError); |
| REQUIRE(success); |
| |
| REQUIRE(info->isConstructCall); |
| REQUIRE(JsGetValueType(info->newTargetArg, &t) == JsNoError); |
| REQUIRE(t == JsFunction); |
| REQUIRE(JsGetValueType(info->thisArg, &t) == JsNoError); |
| REQUIRE(t == JsObject); |
| |
| // new.target === Derived |
| REQUIRE(JsStrictEquals(info->newTargetArg, testinfo->derived, &success) == JsNoError); |
| REQUIRE(success); |
| |
| // this.constructor === Derived |
| REQUIRE(JsGetPropertyIdFromName(_u("constructor"), &propId) == JsNoError); |
| JsValueRef thisCtor = JS_INVALID_REFERENCE; |
| REQUIRE(JsGetProperty(info->thisArg, propId, &thisCtor) == JsNoError); |
| REQUIRE(JsStrictEquals(thisCtor, testinfo->derived, &success) == JsNoError); |
| REQUIRE(success); |
| |
| // this.__proto__ === Derived.prototype |
| JsValueRef thisProto = JS_INVALID_REFERENCE; |
| REQUIRE(JsGetPrototype(info->thisArg, &thisProto) == JsNoError); |
| JsValueRef derivedPrototype = JS_INVALID_REFERENCE; |
| REQUIRE(JsGetPropertyIdFromName(_u("prototype"), &propId) == JsNoError); |
| REQUIRE(JsGetProperty(testinfo->derived, propId, &derivedPrototype) == JsNoError); |
| REQUIRE(JsStrictEquals(thisProto, derivedPrototype, &success) == JsNoError); |
| REQUIRE(success); |
| |
| return info->thisArg; |
| } |
| |
| void ExternalEnhancedBaseClassFunctionTest(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) |
| { |
| ExternalEnhancedBaseClassFunctionTestInfo info = { nullptr, nullptr }; |
| JsValueRef name = JS_INVALID_REFERENCE; |
| REQUIRE(JsCreateString("BaseClass", 10, &name) == JsNoError); |
| JsValueRef base = JS_INVALID_REFERENCE; |
| REQUIRE(JsCreateEnhancedFunction(ExternalEnhancedBaseClassFunctionTestCallback, name, &info, &base) == JsNoError); |
| info.base = base; |
| |
| JsValueRef global = JS_INVALID_REFERENCE; |
| REQUIRE(JsGetGlobalObject(&global) == JsNoError); |
| JsPropertyIdRef propId; |
| REQUIRE(JsGetPropertyIdFromName(_u("BaseClass"), &propId) == JsNoError); |
| REQUIRE(JsSetProperty(global, propId, base, false) == JsNoError); |
| |
| bool success = false; |
| JsValueType t; |
| JsValueRef derived = JS_INVALID_REFERENCE; |
| REQUIRE(JsRunScript( |
| _u("class Derived extends BaseClass {") \ |
| _u(" constructor() {") \ |
| _u(" super(true);") \ |
| _u(" }") \ |
| _u("};"), JS_SOURCE_CONTEXT_NONE, _u(""), &derived) == JsNoError); |
| |
| info.derived = derived; |
| REQUIRE(JsGetValueType(derived, &t) == JsNoError); |
| REQUIRE(t == JsFunction); |
| |
| JsValueRef instance = JS_INVALID_REFERENCE; |
| REQUIRE(JsRunScript( |
| _u("new Derived();"), JS_SOURCE_CONTEXT_NONE, _u(""), &instance) == JsNoError); |
| |
| REQUIRE(JsGetValueType(instance, &t) == JsNoError); |
| REQUIRE(t == JsObject); |
| |
| // instance.constructor === Derived |
| REQUIRE(JsGetPropertyIdFromName(_u("constructor"), &propId) == JsNoError); |
| JsValueRef instanceCtor = JS_INVALID_REFERENCE; |
| REQUIRE(JsGetProperty(instance, propId, &instanceCtor) == JsNoError); |
| REQUIRE(JsStrictEquals(instanceCtor, derived, &success) == JsNoError); |
| REQUIRE(success); |
| |
| // instance.__proto__ === Derived.prototype |
| JsValueRef instanceProto = JS_INVALID_REFERENCE; |
| REQUIRE(JsGetPrototype(instance, &instanceProto) == JsNoError); |
| JsValueRef derivedPrototype = JS_INVALID_REFERENCE; |
| REQUIRE(JsGetPropertyIdFromName(_u("prototype"), &propId) == JsNoError); |
| REQUIRE(JsGetProperty(derived, propId, &derivedPrototype) == JsNoError); |
| REQUIRE(JsStrictEquals(instanceProto, derivedPrototype, &success) == JsNoError); |
| REQUIRE(success); |
| } |
| |
| TEST_CASE("ApiTest_ExternalEnhancedBaseClassFunctionTest", "[ApiTest]") |
| { |
| JsRTApiTest::RunWithAttributes(JsRTApiTest::ExternalEnhancedBaseClassFunctionTest); |
| } |
| |
| void ExternalFunctionNameTest(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) |
| { |
| auto testConstructorName = [=](JsValueRef function, PCWCHAR expectedName, size_t expectedNameLength) |
| { |
| JsValueRef undefined = JS_INVALID_REFERENCE; |
| REQUIRE(JsGetUndefinedValue(&undefined) == JsNoError); |
| JsValueRef args[] = { undefined }; |
| |
| JsValueRef obj = JS_INVALID_REFERENCE, constructor = JS_INVALID_REFERENCE, name = JS_INVALID_REFERENCE; |
| JsPropertyIdRef propId = JS_INVALID_REFERENCE; |
| REQUIRE(JsConstructObject(function, args, 1, &obj) == JsNoError); |
| REQUIRE(JsGetPropertyIdFromName(_u("constructor"), &propId) == JsNoError); |
| REQUIRE(JsGetProperty(obj, propId, &constructor) == JsNoError); |
| REQUIRE(JsGetPropertyIdFromName(_u("name"), &propId) == JsNoError); |
| REQUIRE(JsGetProperty(constructor, propId, &name) == JsNoError); |
| |
| PCWCHAR actualName = nullptr; |
| size_t actualNameLength; |
| REQUIRE(JsStringToPointer(name, &actualName, &actualNameLength) == JsNoError); |
| CHECK(expectedNameLength == actualNameLength); |
| CHECK(wcscmp(expectedName, actualName) == 0); |
| }; |
| |
| auto testToStringResult = [=](JsValueRef function, PCWCHAR expectedResult, size_t expectedResultLength) |
| { |
| JsValueRef actualResult = JS_INVALID_REFERENCE; |
| REQUIRE(JsConvertValueToString(function, &actualResult) == JsNoError); |
| |
| PCWCHAR actualResultString = nullptr; |
| size_t actualResultLength; |
| REQUIRE(JsStringToPointer(actualResult, &actualResultString, &actualResultLength) == JsNoError); |
| CHECK(expectedResultLength == actualResultLength); |
| CHECK(wcscmp(expectedResult, actualResultString) == 0); |
| }; |
| |
| JsValueRef function = JS_INVALID_REFERENCE; |
| REQUIRE(JsCreateFunction(ExternalFunctionCallback, nullptr, &function) == JsNoError); |
| testConstructorName(function, _u(""), 0); |
| |
| WCHAR name[] = _u("FooName"); |
| JsValueRef nameString = JS_INVALID_REFERENCE; |
| REQUIRE(JsPointerToString(name, _countof(name) - 1, &nameString) == JsNoError); |
| REQUIRE(JsCreateNamedFunction(nameString, ExternalFunctionCallback, nullptr, &function) == JsNoError); |
| testConstructorName(function, name, _countof(name) - 1); |
| |
| WCHAR toStringExpectedResult[] = _u("function FooName() { [native code] }"); |
| testToStringResult(function, toStringExpectedResult, _countof(toStringExpectedResult) - 1); |
| // Calling toString multiple times should return the same result. |
| testToStringResult(function, toStringExpectedResult, _countof(toStringExpectedResult) - 1); |
| } |
| |
| TEST_CASE("ApiTest_ExternalFunctionNameTest", "[ApiTest]") |
| { |
| JsRTApiTest::RunWithAttributes(JsRTApiTest::ExternalFunctionNameTest); |
| } |
| |
| void ErrorHandlingTest(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) |
| { |
| // Run a script to setup some globals |
| LPCWSTR script = nullptr; |
| REQUIRE(FileLoadHelpers::LoadScriptFromFile("ErrorHandling.js", script) == S_OK); |
| REQUIRE(script != nullptr); |
| |
| REQUIRE(JsRunScript(script, JS_SOURCE_CONTEXT_NONE, _u(""), nullptr) == JsNoError); |
| |
| JsValueRef global = JS_INVALID_REFERENCE; |
| REQUIRE(JsGetGlobalObject(&global) == JsNoError); |
| |
| JsPropertyIdRef name = JS_INVALID_REFERENCE; |
| JsValueRef result = JS_INVALID_REFERENCE; |
| JsValueRef exception = JS_INVALID_REFERENCE; |
| JsValueRef function = JS_INVALID_REFERENCE; |
| JsValueType type; |
| |
| JsValueRef args[] = { GetUndefined() }; |
| |
| // throw from script, handle in host |
| REQUIRE(JsGetPropertyIdFromName(_u("throwAtHost"), &name) == JsNoError); |
| REQUIRE(JsGetProperty(global, name, &function) == JsNoError); |
| REQUIRE(JsCallFunction(function, args, _countof(args), &result) == JsErrorScriptException); |
| REQUIRE(JsGetAndClearException(&exception) == JsNoError); |
| |
| // setup a host callback for the next couple of tests |
| REQUIRE(JsCreateFunction(ErrorHandlingCallback, nullptr, &result) == JsNoError); |
| REQUIRE(JsGetValueType(result, &type) == JsNoError); |
| REQUIRE(type == JsFunction); |
| REQUIRE(JsGetPropertyIdFromName(_u("callHost"), &name) == JsNoError); |
| REQUIRE(JsSetProperty(global, name, result, true) == JsNoError); |
| |
| // throw from host callback, catch in script |
| REQUIRE(JsGetPropertyIdFromName(_u("callHostWithTryCatch"), &name) == JsNoError); |
| REQUIRE(JsGetProperty(global, name, &function) == JsNoError); |
| REQUIRE(JsCallFunction(function, args, _countof(args), &result) == JsNoError); |
| |
| // throw from host callback, through script, handle in host |
| REQUIRE(JsGetPropertyIdFromName(_u("callHostWithNoTryCatch"), &name) == JsNoError); |
| REQUIRE(JsGetProperty(global, name, &function) == JsNoError); |
| REQUIRE(JsCallFunction(function, args, _countof(args), &result) == JsErrorScriptException); |
| } |
| |
| TEST_CASE("ApiTest_ErrorHandlingTest", "[ApiTest]") |
| { |
| JsRTApiTest::RunWithAttributes(JsRTApiTest::ErrorHandlingTest); |
| } |
| |
| void EngineFlagTest(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) |
| { |
| JsValueRef ret = JS_INVALID_REFERENCE; |
| REQUIRE(JsRunScript(_u("new ActiveXObject('Scripting.FileSystemObject')"), JS_SOURCE_CONTEXT_NONE, _u(""), &ret) == JsErrorScriptException); |
| REQUIRE(JsGetAndClearException(&ret) == JsNoError); |
| } |
| |
| TEST_CASE("ApiTest_EngineFlagTest", "[ApiTest]") |
| { |
| JsRTApiTest::RunWithAttributes(JsRTApiTest::EngineFlagTest); |
| } |
| |
| void CheckExceptionMetadata(JsValueRef exceptionMetadata) |
| { |
| JsPropertyIdRef property = JS_INVALID_REFERENCE; |
| JsValueRef metadataValue = JS_INVALID_REFERENCE; |
| JsValueType type; |
| REQUIRE(JsGetPropertyIdFromName(_u("exception"), &property) == JsNoError); |
| REQUIRE(JsGetProperty(exceptionMetadata, property, &metadataValue) == JsNoError); |
| REQUIRE(JsGetValueType(metadataValue, &type) == JsNoError); |
| CHECK(type == JsError); |
| |
| REQUIRE(JsGetPropertyIdFromName(_u("line"), &property) == JsNoError); |
| REQUIRE(JsGetProperty(exceptionMetadata, property, &metadataValue) == JsNoError); |
| REQUIRE(JsGetValueType(metadataValue, &type) == JsNoError); |
| CHECK(type == JsNumber); |
| |
| REQUIRE(JsGetPropertyIdFromName(_u("column"), &property) == JsNoError); |
| REQUIRE(JsGetProperty(exceptionMetadata, property, &metadataValue) == JsNoError); |
| REQUIRE(JsGetValueType(metadataValue, &type) == JsNoError); |
| CHECK(type == JsNumber); |
| |
| REQUIRE(JsGetPropertyIdFromName(_u("length"), &property) == JsNoError); |
| REQUIRE(JsGetProperty(exceptionMetadata, property, &metadataValue) == JsNoError); |
| REQUIRE(JsGetValueType(metadataValue, &type) == JsNoError); |
| CHECK(type == JsNumber); |
| |
| REQUIRE(JsGetPropertyIdFromName(_u("url"), &property) == JsNoError); |
| REQUIRE(JsGetProperty(exceptionMetadata, property, &metadataValue) == JsNoError); |
| REQUIRE(JsGetValueType(metadataValue, &type) == JsNoError); |
| CHECK(type == JsString); |
| |
| REQUIRE(JsGetPropertyIdFromName(_u("source"), &property) == JsNoError); |
| REQUIRE(JsGetProperty(exceptionMetadata, property, &metadataValue) == JsNoError); |
| REQUIRE(JsGetValueType(metadataValue, &type) == JsNoError); |
| CHECK(type == JsString); |
| } |
| void ExceptionHandlingTest(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) |
| { |
| bool value; |
| JsValueRef exception = JS_INVALID_REFERENCE; |
| JsValueRef exceptionMetadata = JS_INVALID_REFERENCE; |
| JsValueRef metadataValue = JS_INVALID_REFERENCE; |
| JsPropertyIdRef property = JS_INVALID_REFERENCE; |
| JsValueType type; |
| |
| REQUIRE(JsHasException(&value) == JsNoError); |
| CHECK(value == false); |
| REQUIRE(JsRunScript(_u("throw new Error()"), JS_SOURCE_CONTEXT_NONE, _u(""), nullptr) == JsErrorScriptException); |
| REQUIRE(JsHasException(&value) == JsNoError); |
| CHECK(value == true); |
| |
| REQUIRE(JsGetAndClearException(&exception) == JsNoError); |
| REQUIRE(JsHasException(&value) == JsNoError); |
| CHECK(value == false); |
| |
| REQUIRE(JsGetValueType(exception, &type) == JsNoError); |
| CHECK(type == JsError); |
| |
| REQUIRE(JsSetException(exception) == JsNoError); |
| REQUIRE(JsHasException(&value) == JsNoError); |
| CHECK(value == true); |
| |
| REQUIRE(JsGetAndClearExceptionWithMetadata(&exceptionMetadata) == JsNoError); |
| REQUIRE(JsHasException(&value) == JsNoError); |
| CHECK(value == false); |
| |
| REQUIRE(JsGetPropertyIdFromName(_u("exception"), &property) == JsNoError); |
| REQUIRE(JsGetProperty(exceptionMetadata, property, &metadataValue) == JsNoError); |
| CHECK(metadataValue == exception); |
| |
| CheckExceptionMetadata(exceptionMetadata); |
| |
| REQUIRE(JsHasException(&value) == JsNoError); |
| CHECK(value == false); |
| REQUIRE(JsGetAndClearExceptionWithMetadata(&exceptionMetadata) == JsErrorInvalidArgument); |
| CHECK(exceptionMetadata == JS_INVALID_REFERENCE); |
| |
| |
| REQUIRE(JsRunScript(_u("@ bad syntax"), JS_SOURCE_CONTEXT_NONE, _u(""), nullptr) == JsErrorScriptCompile); |
| REQUIRE(JsHasException(&value) == JsNoError); |
| CHECK(value == true); |
| |
| REQUIRE(JsGetAndClearExceptionWithMetadata(&exceptionMetadata) == JsNoError); |
| REQUIRE(JsHasException(&value) == JsNoError); |
| CHECK(value == false); |
| |
| CheckExceptionMetadata(exceptionMetadata); |
| |
| // Test unicode characters |
| REQUIRE(JsRunScript(_u("function main() {\n var x = '\u20ac' + test();\n}\nmain();"), JS_SOURCE_CONTEXT_NONE, _u(""), nullptr) == JsErrorScriptException); |
| REQUIRE(JsHasException(&value) == JsNoError); |
| CHECK(value == true); |
| |
| REQUIRE(JsGetAndClearExceptionWithMetadata(&exceptionMetadata) == JsNoError); |
| REQUIRE(JsHasException(&value) == JsNoError); |
| CHECK(value == false); |
| |
| CheckExceptionMetadata(exceptionMetadata); |
| |
| // Following requires eval to be enabled - no point in testing it if we've disabled eval |
| if (!(attributes & JsRuntimeAttributeDisableEval)) |
| { |
| REQUIRE(JsRunScript(_u("eval('var a = b');"), JS_SOURCE_CONTEXT_NONE, _u(""), nullptr) == JsErrorScriptException); |
| REQUIRE(JsHasException(&value) == JsNoError); |
| CHECK(value == true); |
| |
| REQUIRE(JsGetAndClearExceptionWithMetadata(&exceptionMetadata) == JsNoError); |
| REQUIRE(JsHasException(&value) == JsNoError); |
| CHECK(value == false); |
| |
| CheckExceptionMetadata(exceptionMetadata); |
| } |
| } |
| |
| TEST_CASE("ApiTest_ExceptionHandlingTest", "[ApiTest]") |
| { |
| JsRTApiTest::RunWithAttributes(JsRTApiTest::ExceptionHandlingTest); |
| } |
| |
| void ScriptCompileErrorTest(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) |
| { |
| JsValueRef error = JS_INVALID_REFERENCE; |
| |
| REQUIRE(JsRunScript( |
| _u("if (x > 0) { \n") \ |
| _u(" x = 1; \n") \ |
| _u("}}"), |
| JS_SOURCE_CONTEXT_NONE, _u(""), nullptr) == JsErrorScriptCompile); |
| |
| REQUIRE(JsGetAndClearException(&error) == JsNoError); |
| |
| JsPropertyIdRef property = JS_INVALID_REFERENCE; |
| JsValueRef value = JS_INVALID_REFERENCE; |
| LPCWSTR str = nullptr; |
| size_t length; |
| |
| REQUIRE(JsGetPropertyIdFromName(_u("message"), &property) == JsNoError); |
| REQUIRE(JsGetProperty(error, property, &value) == JsNoError); |
| REQUIRE(JsStringToPointer(value, &str, &length) == JsNoError); |
| CHECK(wcscmp(_u("Syntax error"), str) == 0); |
| |
| double dbl; |
| |
| REQUIRE(JsGetPropertyIdFromName(_u("line"), &property) == JsNoError); |
| REQUIRE(JsGetProperty(error, property, &value) == JsNoError); |
| REQUIRE(JsNumberToDouble(value, &dbl) == JsNoError); |
| CHECK(2 == dbl); |
| |
| REQUIRE(JsGetPropertyIdFromName(_u("column"), &property) == JsNoError); |
| REQUIRE(JsGetProperty(error, property, &value) == JsNoError); |
| REQUIRE(JsNumberToDouble(value, &dbl) == JsNoError); |
| CHECK(1 == dbl); |
| |
| REQUIRE(JsGetPropertyIdFromName(_u("length"), &property) == JsNoError); |
| REQUIRE(JsGetProperty(error, property, &value) == JsNoError); |
| REQUIRE(JsNumberToDouble(value, &dbl) == JsNoError); |
| CHECK(1 == dbl); |
| |
| REQUIRE(JsGetPropertyIdFromName(_u("source"), &property) == JsNoError); |
| REQUIRE(JsGetProperty(error, property, &value) == JsNoError); |
| REQUIRE(JsStringToPointer(value, &str, &length) == JsNoError); |
| CHECK(wcscmp(_u("}}"), str) == 0); |
| |
| REQUIRE(JsRunScript( |
| _u("var for = 10;\n"), |
| JS_SOURCE_CONTEXT_NONE, _u(""), nullptr) == JsErrorScriptCompile); |
| REQUIRE(JsGetAndClearException(&error) == JsNoError); |
| |
| REQUIRE(JsGetPropertyIdFromName(_u("message"), &property) == JsNoError); |
| REQUIRE(JsGetProperty(error, property, &value) == JsNoError); |
| REQUIRE(JsStringToPointer(value, &str, &length) == JsNoError); |
| CHECK(wcscmp(_u("The use of a keyword for an identifier is invalid"), str) == 0); |
| |
| REQUIRE(JsGetPropertyIdFromName(_u("line"), &property) == JsNoError); |
| REQUIRE(JsGetProperty(error, property, &value) == JsNoError); |
| REQUIRE(JsNumberToDouble(value, &dbl) == JsNoError); |
| CHECK(0 == dbl); |
| |
| REQUIRE(JsGetPropertyIdFromName(_u("column"), &property) == JsNoError); |
| REQUIRE(JsGetProperty(error, property, &value) == JsNoError); |
| REQUIRE(JsNumberToDouble(value, &dbl) == JsNoError); |
| CHECK(4 == dbl); |
| |
| REQUIRE(JsGetPropertyIdFromName(_u("length"), &property) == JsNoError); |
| REQUIRE(JsGetProperty(error, property, &value) == JsNoError); |
| REQUIRE(JsNumberToDouble(value, &dbl) == JsNoError); |
| CHECK(3 == dbl); |
| |
| REQUIRE(JsGetPropertyIdFromName(_u("source"), &property) == JsNoError); |
| REQUIRE(JsGetProperty(error, property, &value) == JsNoError); |
| REQUIRE(JsStringToPointer(value, &str, &length) == JsNoError); |
| CHECK(wcscmp(_u("var for = 10;"), str) == 0); |
| } |
| |
| TEST_CASE("ApiTest_ScriptCompileErrorTest", "[ApiTest]") |
| { |
| JsRTApiTest::RunWithAttributes(JsRTApiTest::ScriptCompileErrorTest); |
| } |
| |
| void ObjectTests(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) |
| { |
| LPCWSTR script = _u("x = { a: \"abc\", b: \"def\", c: \"ghi\" }; x"); |
| JsValueRef result = JS_INVALID_REFERENCE; |
| JsValueRef propertyNames = JS_INVALID_REFERENCE; |
| |
| REQUIRE(JsRunScript(script, JS_SOURCE_CONTEXT_NONE, _u(""), &result) == JsNoError); |
| REQUIRE(JsGetOwnPropertyNames(result, &propertyNames) == JsNoError); |
| |
| for (int index = 0; index < 3; index++) |
| { |
| JsValueRef indexValue = JS_INVALID_REFERENCE; |
| REQUIRE(JsDoubleToNumber(index, &indexValue) == JsNoError); |
| |
| JsValueRef nameValue = JS_INVALID_REFERENCE; |
| REQUIRE(JsGetIndexedProperty(propertyNames, indexValue, &nameValue) == JsNoError); |
| |
| const WCHAR *name = nullptr; |
| size_t length; |
| REQUIRE(JsStringToPointer(nameValue, &name, &length) == JsNoError); |
| |
| REQUIRE(length == 1); |
| #pragma prefast(suppress:__WARNING_MAYBE_UNINIT_VAR, "The require on the previous line should ensure that name[0] is initialized") |
| CHECK(name[0] == ('a' + index)); |
| } |
| } |
| |
| TEST_CASE("ApiTest_ObjectTests", "[ApiTest]") |
| { |
| JsRTApiTest::RunWithAttributes(JsRTApiTest::ObjectTests); |
| } |
| |
| void SymbolTests(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) |
| { |
| JsValueRef object = JS_INVALID_REFERENCE; |
| JsValueRef symbol1 = JS_INVALID_REFERENCE; |
| JsValueRef string1 = JS_INVALID_REFERENCE; |
| JsValueRef symbol2 = JS_INVALID_REFERENCE; |
| JsValueRef value = JS_INVALID_REFERENCE; |
| JsPropertyIdRef propertyId = JS_INVALID_REFERENCE; |
| JsValueRef outValue = JS_INVALID_REFERENCE; |
| JsValueRef propertySymbols = JS_INVALID_REFERENCE; |
| const WCHAR* name = nullptr; |
| JsPropertyIdType propertyIdType; |
| |
| REQUIRE(JsCreateObject(&object) == JsNoError); |
| REQUIRE(JsGetPropertyIdFromSymbol(object, &propertyId) == JsErrorPropertyNotSymbol); |
| |
| REQUIRE(JsPointerToString(_u("abc"), 3, &string1) == JsNoError); |
| REQUIRE(JsCreateSymbol(string1, &symbol1) == JsNoError); |
| |
| REQUIRE(JsCreateSymbol(JS_INVALID_REFERENCE, &symbol2) == JsNoError); |
| |
| REQUIRE(JsIntToNumber(1, &value) == JsNoError); |
| REQUIRE(JsGetPropertyIdFromSymbol(symbol1, &propertyId) == JsNoError); |
| REQUIRE(JsGetPropertyNameFromId(propertyId, &name) == JsErrorPropertyNotString); |
| REQUIRE(JsGetPropertyIdType(propertyId, &propertyIdType) == JsNoError); |
| CHECK(propertyIdType == JsPropertyIdTypeSymbol); |
| |
| JsValueRef symbol11 = JS_INVALID_REFERENCE; |
| bool resultBool; |
| REQUIRE(JsGetSymbolFromPropertyId(propertyId, &symbol11) == JsNoError); |
| REQUIRE(JsEquals(symbol1, symbol11, &resultBool) == JsNoError); |
| CHECK(resultBool); |
| REQUIRE(JsStrictEquals(symbol1, symbol11, &resultBool) == JsNoError); |
| CHECK(resultBool); |
| |
| REQUIRE(JsSetProperty(object, propertyId, value, true) == JsNoError); |
| REQUIRE(JsGetProperty(object, propertyId, &outValue) == JsNoError); |
| CHECK(value == outValue); |
| |
| REQUIRE(JsGetPropertyIdFromSymbol(symbol2, &propertyId) == JsNoError); |
| REQUIRE(JsSetProperty(object, propertyId, value, true) == JsNoError); |
| REQUIRE(JsGetProperty(object, propertyId, &outValue) == JsNoError); |
| CHECK(value == outValue); |
| |
| JsValueType type; |
| JsValueRef index = JS_INVALID_REFERENCE; |
| |
| REQUIRE(JsGetOwnPropertySymbols(object, &propertySymbols) == JsNoError); |
| |
| REQUIRE(JsIntToNumber(0, &index) == JsNoError); |
| REQUIRE(JsGetIndexedProperty(propertySymbols, index, &outValue) == JsNoError); |
| REQUIRE(JsGetValueType(outValue, &type) == JsNoError); |
| CHECK(type == JsSymbol); |
| |
| REQUIRE(JsGetPropertyIdFromSymbol(outValue, &propertyId) == JsNoError); |
| REQUIRE(JsGetProperty(object, propertyId, &outValue) == JsNoError); |
| CHECK(value == outValue); |
| |
| REQUIRE(JsIntToNumber(1, &index) == JsNoError); |
| REQUIRE(JsGetIndexedProperty(propertySymbols, index, &outValue) == JsNoError); |
| REQUIRE(JsGetValueType(outValue, &type) == JsNoError); |
| CHECK(type == JsSymbol); |
| |
| REQUIRE(JsGetPropertyIdFromSymbol(outValue, &propertyId) == JsNoError); |
| REQUIRE(JsGetProperty(object, propertyId, &outValue) == JsNoError); |
| CHECK(value == outValue); |
| } |
| |
| TEST_CASE("ApiTest_SymbolTests", "[ApiTest]") |
| { |
| JsRTApiTest::RunWithAttributes(JsRTApiTest::SymbolTests); |
| } |
| |
| void ByteCodeTest(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) |
| { |
| LPCWSTR script = _u("function test() { return true; }; test();"); |
| JsValueRef result = JS_INVALID_REFERENCE; |
| JsValueType type; |
| bool boolValue; |
| BYTE *compiledScript = nullptr; |
| unsigned int scriptSize = 0; |
| |
| REQUIRE(JsSerializeScript(script, compiledScript, &scriptSize) == JsNoError); |
| compiledScript = new BYTE[scriptSize]; |
| unsigned int newScriptSize = scriptSize; |
| REQUIRE(JsSerializeScript(script, compiledScript, &newScriptSize) == JsNoError); |
| CHECK(newScriptSize == scriptSize); |
| REQUIRE(JsRunSerializedScript(script, compiledScript, JS_SOURCE_CONTEXT_NONE, _u(""), &result) == JsNoError); |
| REQUIRE(JsGetValueType(result, &type) == JsNoError); |
| REQUIRE(JsBooleanToBool(result, &boolValue) == JsNoError); |
| CHECK(boolValue); |
| |
| JsRuntimeHandle second = JS_INVALID_RUNTIME_HANDLE; |
| JsContextRef secondContext = JS_INVALID_REFERENCE, current = JS_INVALID_REFERENCE; |
| |
| REQUIRE(JsCreateRuntime(attributes, NULL, &second) == JsNoError); |
| REQUIRE(JsCreateContext(second, &secondContext) == JsNoError); |
| REQUIRE(JsGetCurrentContext(¤t) == JsNoError); |
| REQUIRE(JsSetCurrentContext(secondContext) == JsNoError); |
| |
| REQUIRE(JsRunSerializedScript(script, compiledScript, JS_SOURCE_CONTEXT_NONE, _u(""), &result) == JsNoError); |
| REQUIRE(JsGetValueType(result, &type) == JsNoError); |
| REQUIRE(JsBooleanToBool(result, &boolValue) == JsNoError); |
| CHECK(boolValue); |
| |
| REQUIRE(JsSetCurrentContext(current) == JsNoError); |
| REQUIRE(JsDisposeRuntime(second) == JsNoError); |
| |
| delete[] compiledScript; |
| } |
| |
| TEST_CASE("ApiTest_ByteCodeTest", "[ApiTest]") |
| { |
| JsRTApiTest::RunWithAttributes(JsRTApiTest::ByteCodeTest); |
| } |
| |
| #define BYTECODEWITHCALLBACK_METHODBODY _u("function test() { return true; }") |
| typedef struct _ByteCodeCallbackTracker |
| { |
| bool loadedScript; |
| bool unloadedScript; |
| LPCWSTR script; |
| } ByteCodeCallbackTracker; |
| |
| void ByteCodeWithCallbackTest(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) |
| { |
| LPCWSTR fn_decl = BYTECODEWITHCALLBACK_METHODBODY; |
| LPCWSTR script = BYTECODEWITHCALLBACK_METHODBODY _u("; test();"); |
| LPCWSTR scriptFnToString = BYTECODEWITHCALLBACK_METHODBODY _u("; test.toString();"); |
| JsValueRef result = JS_INVALID_REFERENCE; |
| JsValueType type; |
| bool boolValue; |
| BYTE *compiledScript = nullptr; |
| unsigned int scriptSize = 0; |
| const WCHAR *stringValue; |
| size_t stringLength; |
| ByteCodeCallbackTracker tracker = {}; |
| |
| JsRuntimeHandle first = JS_INVALID_RUNTIME_HANDLE, second = JS_INVALID_RUNTIME_HANDLE; |
| JsContextRef firstContext = JS_INVALID_REFERENCE, secondContext = JS_INVALID_REFERENCE, current = JS_INVALID_REFERENCE; |
| |
| REQUIRE(JsCreateRuntime(attributes, NULL, &first) == JsNoError); |
| REQUIRE(JsCreateContext(first, &firstContext) == JsNoError); |
| REQUIRE(JsGetCurrentContext(¤t) == JsNoError); |
| |
| // First run the script returning a boolean. This should not require the source code. |
| REQUIRE(JsSerializeScript(script, compiledScript, &scriptSize) == JsNoError); |
| compiledScript = (BYTE*)VirtualAlloc(nullptr, scriptSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); |
| |
| unsigned int newScriptSize = scriptSize; |
| REQUIRE(JsSerializeScript(script, compiledScript, &newScriptSize) == JsNoError); |
| CHECK(newScriptSize == scriptSize); |
| |
| /*Change protection to READONLY as serialized byte code is supposed to be in READONLY region*/ |
| |
| DWORD oldProtect; |
| VirtualProtect(compiledScript, scriptSize, PAGE_READONLY, &oldProtect); |
| CHECK(oldProtect == PAGE_READWRITE); |
| |
| tracker.script = script; |
| REQUIRE(JsRunSerializedScriptWithCallback( |
| [](JsSourceContext sourceContext, const WCHAR** scriptBuffer) |
| { |
| ((ByteCodeCallbackTracker*)sourceContext)->loadedScript = true; |
| *scriptBuffer = ((ByteCodeCallbackTracker*)sourceContext)->script; |
| return true; |
| }, |
| [](JsSourceContext sourceContext) |
| { |
| // unless we can force unloaded before popping the stack we cant touch tracker. |
| //((ByteCodeCallbackTracker*)sourceContext)->unloadedScript = true; |
| }, |
| compiledScript, (JsSourceContext)&tracker, _u(""), &result) == JsNoError); |
| |
| CHECK(tracker.loadedScript == false); |
| |
| REQUIRE(JsGetValueType(result, &type) == JsNoError); |
| REQUIRE(JsBooleanToBool(result, &boolValue) == JsNoError); |
| CHECK(boolValue); |
| |
| REQUIRE(JsSetCurrentContext(current) == JsNoError); |
| REQUIRE(JsDisposeRuntime(first) == JsNoError); |
| |
| // Create a second runtime. |
| REQUIRE(JsCreateRuntime(attributes, nullptr, &second) == JsNoError); |
| REQUIRE(JsCreateContext(second, &secondContext) == JsNoError); |
| REQUIRE(JsSetCurrentContext(secondContext) == JsNoError); |
| |
| |
| tracker.loadedScript = false; |
| tracker.unloadedScript = false; |
| VirtualFree(compiledScript, 0, MEM_RELEASE); |
| compiledScript = nullptr; |
| scriptSize = 0; |
| |
| // Second run the script returning function.toString(). This should require the source code. |
| REQUIRE(JsSerializeScript(scriptFnToString, compiledScript, &scriptSize) == JsNoError); |
| compiledScript = (BYTE*)VirtualAlloc(nullptr, scriptSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); |
| |
| newScriptSize = scriptSize; |
| REQUIRE(JsSerializeScript(scriptFnToString, compiledScript, &newScriptSize) == JsNoError); |
| CHECK(newScriptSize == scriptSize); |
| |
| /*Change protection to READONLY as serialized byte code is supposed to be in READONLY region*/ |
| |
| oldProtect; |
| VirtualProtect(compiledScript, scriptSize, PAGE_READONLY, &oldProtect); |
| REQUIRE(oldProtect == PAGE_READWRITE); |
| tracker.script = scriptFnToString; |
| REQUIRE(JsRunSerializedScriptWithCallback( |
| [](JsSourceContext sourceContext, const WCHAR** scriptBuffer) |
| { |
| ((ByteCodeCallbackTracker*)sourceContext)->loadedScript = true; |
| *scriptBuffer = ((ByteCodeCallbackTracker*)sourceContext)->script; |
| return true; |
| }, |
| [](JsSourceContext sourceContext) |
| { |
| // unless we can force unloaded before popping the stack we cant touch tracker. |
| |
| }, |
| compiledScript, (JsSourceContext)&tracker, _u(""), &result) == JsNoError); |
| |
| CHECK(tracker.loadedScript); |
| |
| REQUIRE(JsGetValueType(result, &type) == JsNoError); |
| REQUIRE(JsStringToPointer(result, &stringValue, &stringLength) == JsNoError); |
| CHECK(wcscmp(fn_decl, stringValue) == 0); |
| |
| REQUIRE(JsSetCurrentContext(current) == JsNoError); |
| REQUIRE(JsDisposeRuntime(second) == JsNoError); |
| VirtualFree(compiledScript, 0, MEM_RELEASE); |
| } |
| |
| TEST_CASE("ApiTest_ByteCodeWithCallbackTest", "[ApiTest]") |
| { |
| JsRTApiTest::RunWithAttributes(JsRTApiTest::ByteCodeWithCallbackTest); |
| } |
| |
| void ContextCleanupTest(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) |
| { |
| JsRuntimeHandle rt; |
| REQUIRE(JsCreateRuntime(attributes, nullptr, &rt) == JsNoError); |
| |
| JsContextRef c1 = JS_INVALID_REFERENCE, |
| c2 = JS_INVALID_REFERENCE, |
| c3 = JS_INVALID_REFERENCE, |
| c4 = JS_INVALID_REFERENCE, |
| c5 = JS_INVALID_REFERENCE, |
| c6 = JS_INVALID_REFERENCE, |
| c7 = JS_INVALID_REFERENCE; |
| |
| // Create a bunch of contexts |
| |
| REQUIRE(JsCreateContext(rt, &c1) == JsNoError); |
| REQUIRE(JsCreateContext(rt, &c2) == JsNoError); |
| REQUIRE(JsCreateContext(rt, &c3) == JsNoError); |
| REQUIRE(JsCreateContext(rt, &c4) == JsNoError); |
| REQUIRE(JsCreateContext(rt, &c5) == JsNoError); |
| REQUIRE(JsCreateContext(rt, &c6) == JsNoError); |
| REQUIRE(JsCreateContext(rt, &c7) == JsNoError); |
| |
| // Clear references to some, then collect, causing them to be collected |
| c1 = nullptr; |
| c3 = nullptr; |
| c5 = nullptr; |
| c7 = nullptr; |
| |
| REQUIRE(JsCollectGarbage(rt) == JsNoError); |
| |
| // Now dispose |
| REQUIRE(JsDisposeRuntime(rt) == JsNoError); |
| } |
| |
| TEST_CASE("ApiTest_ContextCleanupTest", "[ApiTest]") |
| { |
| JsRTApiTest::RunWithAttributes(JsRTApiTest::ContextCleanupTest); |
| } |
| |
| void ObjectMethodTest(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) |
| { |
| JsValueRef proto = JS_INVALID_REFERENCE; |
| JsValueRef object = JS_INVALID_REFERENCE; |
| |
| REQUIRE(JsCreateObject(&proto) == JsNoError); |
| REQUIRE(JsCreateObject(&object) == JsNoError); |
| REQUIRE(JsSetPrototype(object, proto) == JsNoError); |
| |
| JsValueRef objectProto = JS_INVALID_REFERENCE; |
| |
| REQUIRE(JsGetPrototype(object, &objectProto) == JsNoError); |
| CHECK(proto == objectProto); |
| |
| JsPropertyIdRef propertyId = JS_INVALID_REFERENCE; |
| bool hasProperty; |
| JsValueRef deleteResult = JS_INVALID_REFERENCE; |
| |
| REQUIRE(JsGetPropertyIdFromName(_u("foo"), &propertyId) == JsNoError); |
| REQUIRE(JsSetProperty(object, propertyId, object, true) == JsNoError); |
| REQUIRE(JsHasProperty(object, propertyId, &hasProperty) == JsNoError); |
| CHECK(hasProperty); |
| REQUIRE(JsDeleteProperty(object, propertyId, true, &deleteResult) == JsNoError); |
| REQUIRE(JsHasProperty(object, propertyId, &hasProperty) == JsNoError); |
| CHECK(!hasProperty); |
| |
| bool canExtend; |
| REQUIRE(JsGetExtensionAllowed(object, &canExtend) == JsNoError); |
| CHECK(canExtend); |
| REQUIRE(JsPreventExtension(object) == JsNoError); |
| REQUIRE(JsGetExtensionAllowed(object, &canExtend) == JsNoError); |
| CHECK(!canExtend); |
| REQUIRE(JsSetProperty(object, propertyId, object, true) == JsErrorScriptException); |
| JsValueRef exception = JS_INVALID_REFERENCE; |
| REQUIRE(JsGetAndClearException(&exception) == JsNoError); |
| REQUIRE(JsHasProperty(object, propertyId, &hasProperty) == JsNoError); |
| CHECK(!hasProperty); |
| } |
| |
| TEST_CASE("ApiTest_ObjectMethodTest", "[ApiTest]") |
| { |
| JsRTApiTest::RunWithAttributes(JsRTApiTest::ObjectMethodTest); |
| } |
| |
| void SetPrototypeTest(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) |
| { |
| JsValueRef proto = JS_INVALID_REFERENCE; |
| JsValueRef object1 = JS_INVALID_REFERENCE; |
| JsValueRef object2 = JS_INVALID_REFERENCE; |
| JsPropertyIdRef obj1_a_pid = JS_INVALID_REFERENCE; |
| JsPropertyIdRef obj1_b_pid = JS_INVALID_REFERENCE; |
| JsPropertyIdRef obj2_x_pid = JS_INVALID_REFERENCE; |
| JsPropertyIdRef obj2_y_pid = JS_INVALID_REFERENCE; |
| JsPropertyIdRef obj2_z_pid = JS_INVALID_REFERENCE; |
| JsValueRef obj1_a_value = JS_INVALID_REFERENCE; |
| JsValueRef obj1_b_value = JS_INVALID_REFERENCE; |
| JsValueRef obj2_x_value = JS_INVALID_REFERENCE; |
| JsValueRef obj2_y_value = JS_INVALID_REFERENCE; |
| JsValueRef obj2_z_value = JS_INVALID_REFERENCE; |
| |
| // var obj1 = {a : "obj1.a", b : "obj1.b"}; |
| // var obj2 = {x : "obj2.x", y : "obj2.y", z : "obj2.z"} |
| REQUIRE(JsCreateObject(&proto) == JsNoError); |
| REQUIRE(JsCreateExternalObject((void *)0xdeadbeef, ExternalObjectFinalizeCallback, &object1) == JsNoError); |
| REQUIRE(JsCreateExternalObject((void *)0xdeadbeef, ExternalObjectFinalizeCallback, &object2) == JsNoError); |
| |
| size_t propNameLength = wcslen(_u("obj1.a")); |
| REQUIRE(JsPointerToString(_u("obj1.a"), propNameLength, &obj1_a_value) == JsNoError); |
| REQUIRE(JsGetPropertyIdFromName(_u("a"), &obj1_a_pid) == JsNoError); |
| REQUIRE(JsSetProperty(object1, obj1_a_pid, obj1_a_value, true) == JsNoError); |
| |
| REQUIRE(JsPointerToString(_u("obj1.b"), propNameLength, &obj1_b_value) == JsNoError); |
| REQUIRE(JsGetPropertyIdFromName(_u("b"), &obj1_b_pid) == JsNoError); |
| REQUIRE(JsSetProperty(object1, obj1_b_pid, obj1_b_value, true) == JsNoError); |
| |
| REQUIRE(JsPointerToString(_u("obj2.x"), propNameLength, &obj2_x_value) == JsNoError); |
| REQUIRE(JsGetPropertyIdFromName(_u("x"), &obj2_x_pid) == JsNoError); |
| REQUIRE(JsSetProperty(object2, obj2_x_pid, obj2_x_value, true) == JsNoError); |
| |
| REQUIRE(JsPointerToString(_u("obj1.y"), propNameLength, &obj2_y_value) == JsNoError); |
| REQUIRE(JsGetPropertyIdFromName(_u("y"), &obj2_y_pid) == JsNoError); |
| REQUIRE(JsSetProperty(object2, obj2_y_pid, obj2_y_value, true) == JsNoError); |
| |
| REQUIRE(JsPointerToString(_u("obj1.z"), propNameLength, &obj2_z_value) == JsNoError); |
| REQUIRE(JsGetPropertyIdFromName(_u("z"), &obj2_z_pid) == JsNoError); |
| REQUIRE(JsSetProperty(object2, obj2_z_pid, obj2_z_value, true) == JsNoError); |
| |
| |
| REQUIRE(JsSetPrototype(object1, proto) == JsNoError); |
| REQUIRE(JsSetPrototype(object2, proto) == JsNoError); |
| |
| JsValueRef objectProto = JS_INVALID_REFERENCE; |
| |
| REQUIRE(JsGetPrototype(object1, &objectProto) == JsNoError); |
| CHECK(proto == objectProto); |
| REQUIRE(JsGetPrototype(object2, &objectProto) == JsNoError); |
| CHECK(proto == objectProto); |
| |
| JsValueRef value = JS_INVALID_REFERENCE; |
| REQUIRE(JsGetProperty(object1, obj1_a_pid, &value) == JsNoError); |
| CHECK(value == obj1_a_value); |
| |
| REQUIRE(JsGetProperty(object1, obj1_b_pid, &value) == JsNoError); |
| CHECK(value == obj1_b_value); |
| |
| REQUIRE(JsGetProperty(object2, obj2_x_pid, &value) == JsNoError); |
| CHECK(value == obj2_x_value); |
| |
| REQUIRE(JsGetProperty(object2, obj2_y_pid, &value) == JsNoError); |
| CHECK(value == obj2_y_value); |
| |
| REQUIRE(JsGetProperty(object2, obj2_z_pid, &value) == JsNoError); |
| CHECK(value == obj2_z_value); |
| } |
| |
| TEST_CASE("ApiTest_SetPrototypeTest", "[ApiTest]") |
| { |
| JsRTApiTest::RunWithAttributes(JsRTApiTest::SetPrototypeTest); |
| } |
| |
| void DisableEval(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) |
| { |
| JsValueRef result = JS_INVALID_REFERENCE; |
| JsErrorCode error = JsRunScript(_u("eval(\"1 + 2\")"), JS_SOURCE_CONTEXT_NONE, _u(""), &result); |
| |
| if (!(attributes & JsRuntimeAttributeDisableEval)) |
| { |
| CHECK(error == JsNoError); |
| } |
| else |
| { |
| CHECK(error == JsErrorScriptEvalDisabled); |
| } |
| |
| error = JsRunScript(_u("new Function(\"return 1 + 2\")"), JS_SOURCE_CONTEXT_NONE, _u(""), &result); |
| |
| if (!(attributes & JsRuntimeAttributeDisableEval)) |
| { |
| CHECK(error == JsNoError); |
| } |
| else |
| { |
| CHECK(error == JsErrorScriptEvalDisabled); |
| } |
| } |
| |
| TEST_CASE("ApiTest_DisableEval", "[ApiTest]") |
| { |
| JsRTApiTest::RunWithAttributes(JsRTApiTest::DisableEval); |
| } |
| |
| static void CALLBACK PromiseContinuationCallback(JsValueRef task, void *callbackState) |
| { |
| CHECK(callbackState != nullptr); |
| |
| // This simply saves the given task into the callback state |
| // so that we can verify it in the test |
| CHECK(*(JsValueRef *)callbackState == JS_INVALID_REFERENCE); |
| *(JsValueRef *)callbackState = task; |
| } |
| |
| void PromisesTest(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) |
| { |
| JsValueRef result = JS_INVALID_REFERENCE; |
| JsValueType valueType; |
| JsValueRef task = JS_INVALID_REFERENCE; |
| JsValueRef callback = JS_INVALID_REFERENCE; |
| REQUIRE(JsSetPromiseContinuationCallback(PromiseContinuationCallback, &callback) == JsNoError); |
| REQUIRE(JsRunScript( |
| _u("new Promise(") \ |
| _u(" function(resolve, reject) {") \ |
| _u(" resolve('basic:success');") \ |
| _u(" }") \ |
| _u(").then (") \ |
| _u(" function () { return new Promise(") \ |
| _u(" function(resolve, reject) { ") \ |
| _u(" resolve('second:success'); ") \ |
| _u(" })") \ |
| _u(" }") \ |
| _u(");"), JS_SOURCE_CONTEXT_NONE, _u(""), &result) == JsNoError); |
| CHECK(callback != nullptr); |
| |
| JsValueRef args[] = { GetUndefined() }; |
| |
| // first then handler was queued |
| task = callback; |
| callback = JS_INVALID_REFERENCE; |
| REQUIRE(JsGetValueType(task, &valueType) == JsNoError); |
| CHECK(valueType == JsFunction); |
| REQUIRE(JsCallFunction(task, args, _countof(args), &result) == JsNoError); |
| |
| // the second promise resolution was queued |
| task = callback; |
| callback = JS_INVALID_REFERENCE; |
| REQUIRE(JsGetValueType(task, &valueType) == JsNoError); |
| CHECK(valueType == JsFunction); |
| REQUIRE(JsCallFunction(task, args, _countof(args), &result) == JsNoError); |
| |
| // second then handler was queued. |
| task = callback; |
| callback = JS_INVALID_REFERENCE; |
| REQUIRE(JsGetValueType(task, &valueType) == JsNoError); |
| CHECK(valueType == JsFunction); |
| REQUIRE(JsCallFunction(task, args, _countof(args), &result) == JsNoError); |
| |
| // we are done; no more new task are queue. |
| CHECK(callback == JS_INVALID_REFERENCE); |
| } |
| |
| TEST_CASE("ApiTest_PromisesTest", "[ApiTest]") |
| { |
| JsRTApiTest::RunWithAttributes(JsRTApiTest::PromisesTest); |
| } |
| |
| void UnsetPromiseContinuation(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) |
| { |
| JsValueRef result = JS_INVALID_REFERENCE, callbackState = JS_INVALID_REFERENCE, exception = JS_INVALID_REFERENCE; |
| JsValueType cbStateType = JsUndefined; |
| const wchar_t *script = _u("new Promise((res, rej) => res()).then(() => 1)"); |
| |
| // script with no promise continuation callback should error |
| REQUIRE(JsRunScript(script, JS_SOURCE_CONTEXT_NONE, _u(""), &result) == JsErrorScriptException); |
| CHECK(result == JS_INVALID_REFERENCE); |
| REQUIRE(JsGetAndClearException(&exception) == JsNoError); |
| |
| // script with promise continuation callback should run successfully |
| result = JS_INVALID_REFERENCE; |
| callbackState = JS_INVALID_REFERENCE; |
| REQUIRE(JsSetPromiseContinuationCallback(PromiseContinuationCallback, &callbackState) == JsNoError); |
| REQUIRE(JsRunScript(script, JS_SOURCE_CONTEXT_NONE, _u(""), &result) == JsNoError); |
| CHECK(result != JS_INVALID_REFERENCE); |
| REQUIRE(JsGetValueType(callbackState, &cbStateType) == JsNoError); |
| CHECK(cbStateType == JsFunction); |
| |
| // unsetting the promise continuation callback should make promise scripts error |
| result = JS_INVALID_REFERENCE; |
| callbackState = JS_INVALID_REFERENCE; |
| REQUIRE(JsSetPromiseContinuationCallback(nullptr, nullptr) == JsNoError); |
| REQUIRE(JsRunScript(script, JS_SOURCE_CONTEXT_NONE, _u(""), &result) == JsErrorScriptException); |
| CHECK(result == JS_INVALID_REFERENCE); |
| REQUIRE(JsGetAndClearException(&exception) == JsNoError); |
| |
| // resetting promise continuation callback should run successfully |
| result = JS_INVALID_REFERENCE; |
| callbackState = JS_INVALID_REFERENCE; |
| REQUIRE(JsSetPromiseContinuationCallback(PromiseContinuationCallback, &callbackState) == JsNoError); |
| REQUIRE(JsRunScript(script, JS_SOURCE_CONTEXT_NONE, _u(""), &result) == JsNoError); |
| CHECK(result != JS_INVALID_REFERENCE); |
| REQUIRE(JsGetValueType(callbackState, &cbStateType) == JsNoError); |
| CHECK(cbStateType == JsFunction); |
| } |
| |
| TEST_CASE("ApiTest_UnsetPromiseContinuation", "[ApiTest]") |
| { |
| JsRTApiTest::RunWithAttributes(JsRTApiTest::UnsetPromiseContinuation); |
| } |
| |
| void ArrayBufferTest(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) |
| { |
| for (int type = JsArrayTypeInt8; type <= JsArrayTypeFloat64; type++) |
| { |
| unsigned int size = 0; |
| |
| switch (type) |
| { |
| case JsArrayTypeInt16: |
| size = sizeof(__int16); |
| break; |
| case JsArrayTypeInt8: |
| size = sizeof(__int8); |
| break; |
| case JsArrayTypeUint8: |
| case JsArrayTypeUint8Clamped: |
| size = sizeof(unsigned __int8); |
| break; |
| case JsArrayTypeUint16: |
| size = sizeof(unsigned __int16); |
| break; |
| case JsArrayTypeInt32: |
| size = sizeof(__int32); |
| break; |
| case JsArrayTypeUint32: |
| size = sizeof(unsigned __int32); |
| break; |
| case JsArrayTypeFloat32: |
| size = sizeof(float); |
| break; |
| case JsArrayTypeFloat64: |
| size = sizeof(double); |
| break; |
| } |
| |
| // ArrayBuffer |
| JsValueRef arrayBuffer = JS_INVALID_REFERENCE; |
| JsValueType valueType; |
| BYTE *originBuffer = nullptr; |
| unsigned int originBufferLength; |
| |
| REQUIRE(JsCreateArrayBuffer(16 * size, &arrayBuffer) == JsNoError); |
| REQUIRE(JsGetValueType(arrayBuffer, &valueType) == JsNoError); |
| CHECK(JsValueType::JsArrayBuffer == valueType); |
| |
| REQUIRE(JsGetArrayBufferStorage(arrayBuffer, &originBuffer, &originBufferLength) == JsNoError); |
| CHECK(16 * size == originBufferLength); |
| |
| // TypedArray |
| JsValueRef typedArray = JS_INVALID_REFERENCE; |
| |
| REQUIRE(JsCreateTypedArray((JsTypedArrayType)type, arrayBuffer, /*byteOffset*/size, /*length*/12, &typedArray) == JsNoError); |
| REQUIRE(JsGetValueType(typedArray, &valueType) == JsNoError); |
| CHECK(JsValueType::JsTypedArray == valueType); |
| |
| JsTypedArrayType arrayType; |
| JsValueRef tmpArrayBuffer = JS_INVALID_REFERENCE; |
| unsigned int tmpByteOffset, tmpByteLength; |
| REQUIRE(JsGetTypedArrayInfo(typedArray, &arrayType, &tmpArrayBuffer, &tmpByteOffset, &tmpByteLength) == JsNoError); |
| CHECK(type == arrayType); |
| CHECK(arrayBuffer == tmpArrayBuffer); |
| CHECK(size == tmpByteOffset); |
| CHECK(12 * size == tmpByteLength); |
| |
| BYTE *buffer = nullptr; |
| unsigned int bufferLength; |
| int elementSize; |
| REQUIRE(JsGetTypedArrayStorage(typedArray, &buffer, &bufferLength, &arrayType, &elementSize) == JsNoError); |
| CHECK(originBuffer + size == buffer); |
| CHECK(12 * size == bufferLength); |
| CHECK(type == arrayType); |
| CHECK(size == (size_t)elementSize); |
| |
| // DataView |
| JsValueRef dataView = JS_INVALID_REFERENCE; |
| |
| REQUIRE(JsCreateDataView(arrayBuffer, /*byteOffset*/3, /*byteLength*/13, &dataView) == JsNoError); |
| REQUIRE(JsGetValueType(dataView, &valueType) == JsNoError); |
| CHECK(JsValueType::JsDataView == valueType); |
| |
| REQUIRE(JsGetDataViewStorage(dataView, &buffer, &bufferLength) == JsNoError); |
| CHECK(originBuffer + 3 == buffer); |
| CHECK(13 == (int)bufferLength); |
| |
| // InvalidArgs Get... |
| JsValueRef bad = JS_INVALID_REFERENCE; |
| REQUIRE(JsIntToNumber(5, &bad) == JsNoError); |
| |
| REQUIRE(JsGetArrayBufferStorage(typedArray, &buffer, &bufferLength) == JsErrorInvalidArgument); |
| REQUIRE(JsGetArrayBufferStorage(dataView, &buffer, &bufferLength) == JsErrorInvalidArgument); |
| REQUIRE(JsGetArrayBufferStorage(bad, &buffer, &bufferLength) == JsErrorInvalidArgument); |
| |
| REQUIRE(JsGetTypedArrayStorage(arrayBuffer, &buffer, &bufferLength, &arrayType, &elementSize) == JsErrorInvalidArgument); |
| REQUIRE(JsGetTypedArrayStorage(dataView, &buffer, &bufferLength, &arrayType, &elementSize) == JsErrorInvalidArgument); |
| REQUIRE(JsGetTypedArrayStorage(bad, &buffer, &bufferLength, &arrayType, &elementSize) == JsErrorInvalidArgument); |
| |
| REQUIRE(JsGetDataViewStorage(arrayBuffer, &buffer, &bufferLength) == JsErrorInvalidArgument); |
| REQUIRE(JsGetDataViewStorage(typedArray, &buffer, &bufferLength) == JsErrorInvalidArgument); |
| REQUIRE(JsGetDataViewStorage(bad, &buffer, &bufferLength) == JsErrorInvalidArgument); |
| |
| // no base array |
| REQUIRE(JsCreateTypedArray((JsTypedArrayType)type, JS_INVALID_REFERENCE, /*byteOffset*/0, /*length*/0, &typedArray) == JsNoError); // no base array |
| REQUIRE(JsGetTypedArrayInfo(typedArray, &arrayType, &tmpArrayBuffer, &tmpByteOffset, &tmpByteLength) == JsNoError); |
| CHECK(type == arrayType); |
| CHECK(tmpArrayBuffer != nullptr); |
| CHECK(tmpByteOffset == 0); |
| CHECK(tmpByteLength == 0); |
| |
| // InvalidArgs Create... |
| REQUIRE(JsCreateTypedArray((JsTypedArrayType)(type + 100), arrayBuffer, /*byteOffset*/size, /*length*/12, &typedArray) == JsErrorInvalidArgument); // bad array type |
| REQUIRE(JsCreateTypedArray((JsTypedArrayType)type, JS_INVALID_REFERENCE, /*byteOffset*/size, /*length*/12, &typedArray) == JsErrorInvalidArgument); // byteOffset should be 0 |
| REQUIRE(JsCreateTypedArray((JsTypedArrayType)type, dataView, /*byteOffset*/size, /*length*/12, &typedArray) == JsErrorInvalidArgument); // byteOffset should be 0 |
| REQUIRE(JsCreateTypedArray((JsTypedArrayType)type, bad, /*byteOffset*/size, /*length*/12, &typedArray) == JsErrorInvalidArgument); // byteOffset should be 0 |
| REQUIRE(JsCreateTypedArray((JsTypedArrayType)type, dataView, /*byteOffset*/0, /*length*/12, &typedArray) == JsErrorInvalidArgument); // length should be 0 |
| REQUIRE(JsCreateTypedArray((JsTypedArrayType)type, bad, /*byteOffset*/0, /*length*/12, &typedArray) == JsErrorInvalidArgument); // length should be 0 |
| REQUIRE(JsCreateDataView(typedArray, /*byteOffset*/size, /*byteLength*/12, &dataView) == JsErrorInvalidArgument); // must from arrayBuffer |
| REQUIRE(JsCreateDataView(dataView, /*byteOffset*/size, /*byteLength*/12, &dataView) == JsErrorInvalidArgument); // must from arrayBuffer |
| REQUIRE(JsCreateDataView(bad, /*byteOffset*/size, /*byteLength*/12, &dataView) == JsErrorInvalidArgument); // must from arrayBuffer |
| } |
| } |
| |
| TEST_CASE("ApiTest_ArrayBufferTest", "[ApiTest]") |
| { |
| JsRTApiTest::RunWithAttributes(JsRTApiTest::ArrayBufferTest); |
| } |
| |
| struct ThreadArgsData |
| { |
| JsRuntimeHandle runtime; |
| HANDLE hMonitor; |
| BOOL isScriptActive; |
| JsErrorCode disableExecutionResult; |
| |
| static const int waitTime = 1000; |
| |
| void BeginScriptExecution() { isScriptActive = true; } |
| void EndScriptExecution() { isScriptActive = false; } |
| void SignalMonitor() { SetEvent(hMonitor); } |
| |
| // CATCH is not thread-safe. Call this in main thread only. |
| void CheckDisableExecutionResult() |
| { |
| REQUIRE(disableExecutionResult == JsNoError); |
| } |
| |
| unsigned int ThreadProc() |
| { |
| while (1) |
| { |
| WaitForSingleObject(hMonitor, INFINITE); |
| // TODO: have a generic stopping mechanism. |
| if (isScriptActive) |
| { |
| Sleep(waitTime); |
| |
| // CATCH is not thread-safe. Do not verify in this thread. |
| disableExecutionResult = JsDisableRuntimeExecution(runtime); |
| if (disableExecutionResult == JsNoError) |
| { |
| continue; // done, wait for next signal |
| } |
| } |
| |
| CloseHandle(hMonitor); |
| break; |
| } |
| return 0; |
| } |
| }; |
| |
| static unsigned int CALLBACK StaticThreadProc(LPVOID lpParameter) |
| { |
| DWORD ret = (DWORD)-1; |
| ThreadArgsData * args = (ThreadArgsData *)lpParameter; |
| ret = args->ThreadProc(); |
| return ret; |
| } |
| |
| #define TERMINATION_TESTS \ |
| _u("for (i=0; i<200; i = 20) {") \ |
| _u(" var a = new Int8Array(800);") \ |
| _u("}"), \ |
| \ |
| _u("function nextFunc() { ") \ |
| _u(" throw 'hello'") \ |
| _u("};") \ |
| _u("for (i=0; i<200; i = 20) { ") \ |
| _u(" try {") \ |
| _u(" nextFunc();") \ |
| _u(" } ") \ |
| _u(" catch(e) {}") \ |
| _u("}"), \ |
| \ |
| _u("function nextFunc() {") \ |
| _u(" bar = bar + nextFunc.toString();") \ |
| _u("};") \ |
| _u("bar = '';") \ |
| _u("for (i=0; i<200; i = 20) {") \ |
| _u(" nextFunc()") \ |
| _u("}"), \ |
| \ |
| _u("while(1);"), \ |
| \ |
| _u("function foo(){}") \ |
| _u("do{") \ |
| _u(" foo();") \ |
| _u("}while(1);"), \ |
| \ |
| _u("(function foo(){") \ |
| _u(" do {") \ |
| _u(" if (foo) continue;") \ |
| _u(" if (!foo) break;") \ |
| _u(" } while(1); ") \ |
| _u("})();"), \ |
| \ |
| _u("(function foo(a){") \ |
| _u(" while (a){") \ |
| _u(" L1:") \ |
| _u(" do {") \ |
| _u(" while(1) {") \ |
| _u(" continue L1;") \ |
| _u(" }") \ |
| _u(" a = 0;") \ |
| _u(" } while(0);") \ |
| _u(" }") \ |
| _u("})(1);"), \ |
| \ |
| _u("(function (){") \ |
| _u(" while (1) {") \ |
| _u(" try {") \ |
| _u(" throw 0;") \ |
| _u(" break;") \ |
| _u(" }") \ |
| _u(" catch(e) {") \ |
| _u(" if (!e) continue;") \ |
| _u(" }") \ |
| _u(" break;") \ |
| _u(" }") \ |
| _u("})();") |
| |
| static const LPCWSTR terminationTests[] = { TERMINATION_TESTS }; |
| |
| void ExternalFunctionWithScriptAbortionTest(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) |
| { |
| if (!(attributes & JsRuntimeAttributeAllowScriptInterrupt)) |
| { |
| REQUIRE(JsDisableRuntimeExecution(runtime) == JsErrorCannotDisableExecution); |
| return; |
| } |
| |
| ThreadArgsData threadArgs = {}; |
| threadArgs.runtime = runtime; |
| threadArgs.hMonitor = CreateEvent(nullptr, FALSE, FALSE, nullptr); |
| HANDLE threadHandle = reinterpret_cast<HANDLE>(_beginthreadex(nullptr, 0, &StaticThreadProc, &threadArgs, 0, nullptr)); |
| REQUIRE(threadHandle != nullptr); |
| if (threadHandle == nullptr) |
| { |
| // This is to satisfy preFAST, above REQUIRE call ensuring that it will report exception when threadHandle is null. |
| return; |
| } |
| JsValueRef preScriptAbortFunction = JS_INVALID_REFERENCE, postScriptAbortFunction = JS_INVALID_REFERENCE; |
| JsValueRef exception = JS_INVALID_REFERENCE; |
| |
| for (int i = 0; i < _countof(terminationTests); i++) |
| { |
| threadArgs.BeginScriptExecution(); |
| threadArgs.SignalMonitor(); |
| REQUIRE(JsCreateFunction(ExternalFunctionPreScriptAbortionCallback, nullptr, &preScriptAbortFunction) == JsNoError); |
| REQUIRE(JsCreateFunction(ExternalFunctionPostScriptAbortionCallback, nullptr, &postScriptAbortFunction) == JsNoError); |
| JsValueRef scriptTextArg = JS_INVALID_REFERENCE; |
| |
| WCHAR *scriptText = const_cast<WCHAR *>(terminationTests[i]); |
| REQUIRE(JsPointerToString(scriptText, wcslen(scriptText), &scriptTextArg) == JsNoError); |
| JsValueRef args[] = { scriptTextArg }; |
| |
| REQUIRE(JsCallFunction(preScriptAbortFunction, args, 1, nullptr) == JsErrorScriptTerminated); |
| |
| bool isDisabled; |
| REQUIRE(JsIsRuntimeExecutionDisabled(runtime, &isDisabled) == JsNoError); |
| CHECK(isDisabled); |
| |
| #ifdef NTBUILD |
| REQUIRE(JsCallFunction(postScriptAbortFunction, args, 1, nullptr) == JsErrorInDisabledState); |
| #else // !JSRT_VERIFY_RUNTIME_STATE |
| bool hasException = false; |
| REQUIRE(JsHasException(&hasException) == JsErrorInDisabledState); |
| #endif |
| REQUIRE(JsGetAndClearException(&exception) == JsErrorInDisabledState); |
| REQUIRE(JsEnableRuntimeExecution(runtime) == JsNoError); |
| threadArgs.CheckDisableExecutionResult(); |
| threadArgs.EndScriptExecution(); |
| } |
| threadArgs.SignalMonitor(); |
| WaitForSingleObject(threadHandle, INFINITE); |
| threadArgs.hMonitor = nullptr; |
| } |
| |
| TEST_CASE("ApiTest_ExternalFunctionWithScriptAbortionTest", "[ApiTest]") |
| { |
| JsRTApiTest::RunWithAttributes(JsRTApiTest::ExternalFunctionWithScriptAbortionTest); |
| } |
| |
| void ScriptTerminationTest(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) |
| { |
| // can't interrupt if scriptinterrupt is disabled. |
| if (!(attributes & JsRuntimeAttributeAllowScriptInterrupt)) |
| { |
| REQUIRE(JsDisableRuntimeExecution(runtime) == JsErrorCannotDisableExecution); |
| return; |
| } |
| ThreadArgsData threadArgs = {}; |
| threadArgs.runtime = runtime; |
| threadArgs.hMonitor = CreateEvent(nullptr, FALSE, FALSE, nullptr); |
| HANDLE threadHandle = reinterpret_cast<HANDLE>(_beginthreadex(nullptr, 0, &StaticThreadProc, &threadArgs, 0, nullptr)); |
| REQUIRE(threadHandle != nullptr); |
| if (threadHandle == nullptr) |
| { |
| // This is to satisfy preFAST, above REQUIRE call ensuring that it will report exception when threadHandle is null. |
| return; |
| } |
| |
| JsValueRef result; |
| JsValueRef exception; |
| for (int i = 0; i < _countof(terminationTests); i++) |
| { |
| threadArgs.BeginScriptExecution(); |
| threadArgs.SignalMonitor(); |
| REQUIRE(JsRunScript(terminationTests[i], JS_SOURCE_CONTEXT_NONE, _u(""), &result) == JsErrorScriptTerminated); |
| bool isDisabled; |
| REQUIRE(JsIsRuntimeExecutionDisabled(runtime, &isDisabled) == JsNoError); |
| CHECK(isDisabled); |
| #ifdef NTBUILD |
| REQUIRE(JsRunScript(terminationTests[i], JS_SOURCE_CONTEXT_NONE, _u(""), &result) == JsErrorInDisabledState); |
| #else // !JSRT_VERIFY_RUNTIME_STATE |
| bool hasException = false; |
| REQUIRE(JsHasException(&hasException) == JsErrorInDisabledState); |
| #endif |
| REQUIRE(JsGetAndClearException(&exception) == JsErrorInDisabledState); |
| REQUIRE(JsEnableRuntimeExecution(runtime) == JsNoError); |
| threadArgs.CheckDisableExecutionResult(); |
| threadArgs.EndScriptExecution(); |
| } |
| threadArgs.SignalMonitor(); |
| WaitForSingleObject(threadHandle, INFINITE); |
| threadArgs.hMonitor = nullptr; |
| } |
| |
| TEST_CASE("ApiTest_ScriptTerminationTest", "[ApiTest]") |
| { |
| JsRTApiTest::RunWithAttributes(JsRTApiTest::ScriptTerminationTest); |
| } |
| |
| struct ModuleResponseData |
| { |
| ModuleResponseData() |
| : mainModule(JS_INVALID_REFERENCE), childModule(JS_INVALID_REFERENCE), mainModuleException(JS_INVALID_REFERENCE), mainModuleReady(false) |
| { |
| } |
| JsModuleRecord mainModule; |
| JsModuleRecord childModule; |
| JsValueRef mainModuleException; |
| bool mainModuleReady; |
| }; |
| ModuleResponseData successTest; |
| |
| static JsErrorCode CALLBACK Success_FIMC(_In_ JsModuleRecord referencingModule, _In_ JsValueRef specifier, _Outptr_result_maybenull_ JsModuleRecord* dependentModuleRecord) |
| { |
| JsModuleRecord moduleRecord = JS_INVALID_REFERENCE; |
| LPCWSTR specifierStr; |
| size_t length; |
| |
| JsErrorCode errorCode = JsStringToPointer(specifier, &specifierStr, &length); |
| REQUIRE(errorCode == JsNoError); |
| REQUIRE(!wcscmp(specifierStr, _u("foo.js"))); |
| |
| errorCode = JsInitializeModuleRecord(referencingModule, specifier, &moduleRecord); |
| REQUIRE(errorCode == JsNoError); |
| *dependentModuleRecord = moduleRecord; |
| successTest.childModule = moduleRecord; |
| return JsNoError; |
| } |
| |
| static JsErrorCode CALLBACK Success_NMRC(_In_opt_ JsModuleRecord referencingModule, _In_opt_ JsValueRef exceptionVar) |
| { |
| if (successTest.mainModule == referencingModule) |
| { |
| successTest.mainModuleReady = true; |
| successTest.mainModuleException = exceptionVar; |
| } |
| return JsNoError; |
| } |
| |
| static JsErrorCode CALLBACK Success_IIMC(_In_opt_ JsModuleRecord referencingModule, _In_opt_ JsValueRef importMetaVar) |
| { |
| return JsNoError; |
| } |
| |
| void ModuleSuccessTest(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) |
| { |
| JsModuleRecord requestModule = JS_INVALID_REFERENCE; |
| JsValueRef specifier; |
| |
| REQUIRE(JsPointerToString(_u(""), 1, &specifier) == JsNoError); |
| REQUIRE(JsInitializeModuleRecord(nullptr, specifier, &requestModule) == JsNoError); |
| successTest.mainModule = requestModule; |
| REQUIRE(JsSetModuleHostInfo(requestModule, JsModuleHostInfo_FetchImportedModuleCallback, Success_FIMC) == JsNoError); |
| REQUIRE(JsSetModuleHostInfo(requestModule, JsModuleHostInfo_FetchImportedModuleFromScriptCallback, Success_FIMC) == JsNoError); |
| REQUIRE(JsSetModuleHostInfo(requestModule, JsModuleHostInfo_NotifyModuleReadyCallback, Success_NMRC) == JsNoError); |
| REQUIRE(JsSetModuleHostInfo(requestModule, JsModuleHostInfo_InitializeImportMetaCallback, Success_IIMC) == JsNoError); |
| |
| JsValueRef errorObject = JS_INVALID_REFERENCE; |
| const char* fileContent = "import {x} from 'foo.js'"; |
| JsErrorCode errorCode = JsParseModuleSource(requestModule, 0, (LPBYTE)fileContent, |
| (unsigned int)strlen(fileContent), JsParseModuleSourceFlags_DataIsUTF8, &errorObject); |
| |
| CHECK(errorCode == JsNoError); |
| CHECK(errorObject == JS_INVALID_REFERENCE); |
| CHECK(successTest.mainModuleReady == false); |
| REQUIRE(successTest.childModule != JS_INVALID_REFERENCE); |
| |
| errorObject = JS_INVALID_REFERENCE; |
| fileContent = "/*error code*/ var x x"; |
| |
| errorCode = JsParseModuleSource(successTest.childModule, 1, (LPBYTE)fileContent, |
| (unsigned int)strlen(fileContent), JsParseModuleSourceFlags_DataIsUTF8, &errorObject); |
| |
| CHECK(errorCode == JsErrorScriptCompile); |
| CHECK(errorObject != JS_INVALID_REFERENCE); |
| |
| CHECK(successTest.mainModuleReady == true); |
| REQUIRE(successTest.mainModuleException != JS_INVALID_REFERENCE); |
| JsPropertyIdRef message = JS_INVALID_REFERENCE; |
| |
| REQUIRE(JsGetPropertyIdFromName(_u("message"), &message) == JsNoError); |
| |
| JsValueRef value1Check = JS_INVALID_REFERENCE; |
| REQUIRE(JsGetProperty(successTest.mainModuleException, message, &value1Check) == JsNoError); |
| |
| JsValueRef asString = JS_INVALID_REFERENCE; |
| REQUIRE(JsConvertValueToString(value1Check, &asString) == JsNoError); |
| |
| LPCWSTR str = nullptr; |
| size_t length; |
| REQUIRE(JsStringToPointer(asString, &str, &length) == JsNoError); |
| REQUIRE(!wcscmp(str, _u("Expected ';'"))); |
| } |
| |
| TEST_CASE("ApiTest_ModuleSuccessTest", "[ApiTest]") |
| { |
| JsRTApiTest::WithSetup(JsRuntimeAttributeEnableExperimentalFeatures, ModuleSuccessTest); |
| } |
| |
| void JsIsCallableTest(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) |
| { |
| JsValueRef callables, callable, index, nonCallables, nonCallable; |
| bool check; |
| |
| REQUIRE(JsRunScript(_u("[function(){},function*(){},async function(){},async function*(){},_=>_,async _=>_]"), |
| JS_SOURCE_CONTEXT_NONE, _u(""), &callables) == JsNoError); |
| |
| for (int i = 0; i < 6; i++) |
| { |
| REQUIRE(JsIntToNumber(i, &index) == JsNoError); |
| REQUIRE(JsGetIndexedProperty(callables, index, &callable) == JsNoError); |
| REQUIRE(JsIsCallable(callable, &check) == JsNoError); |
| CHECK(check); |
| } |
| |
| |
| REQUIRE(JsRunScript(_u("[class{},Math,Reflect,{}]"), JS_SOURCE_CONTEXT_NONE, _u(""), &nonCallables) == JsNoError); |
| |
| for (int i = 0; i < 4; i++) |
| { |
| REQUIRE(JsIntToNumber(i, &index) == JsNoError); |
| REQUIRE(JsGetIndexedProperty(nonCallables, index, &nonCallable) == JsNoError); |
| REQUIRE(JsIsCallable(nonCallable, &check) == JsNoError); |
| CHECK(!check); |
| } |
| } |
| |
| TEST_CASE("ApiTest_JsIsCallableTest", "[ApiTest]") { |
| JsRTApiTest::RunWithAttributes(JsIsCallableTest); |
| } |
| |
| void JsIsConstructorTest(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) |
| { |
| JsValueRef constructables, constructable, index, nonConstructables, nonConstructable; |
| bool check; |
| |
| REQUIRE(JsRunScript(_u("[class{},function(){}]"), JS_SOURCE_CONTEXT_NONE, _u(""), &constructables) == JsNoError); |
| |
| for (int i = 0; i < 2; i++) |
| { |
| REQUIRE(JsIntToNumber(i, &index) == JsNoError); |
| REQUIRE(JsGetIndexedProperty(constructables, index, &constructable) == JsNoError); |
| REQUIRE(JsIsConstructor(constructable, &check) == JsNoError); |
| CHECK(check); |
| } |
| |
| |
| REQUIRE(JsRunScript(_u("[Math,Reflect,{},function*(){},async function(){},async function*(){},_=>_,async _=>_]"), |
| JS_SOURCE_CONTEXT_NONE, _u(""), &nonConstructables) == JsNoError); |
| |
| for (int i = 0; i < 8; i++) |
| { |
| REQUIRE(JsIntToNumber(i, &index) == JsNoError); |
| REQUIRE(JsGetIndexedProperty(nonConstructables, index, &nonConstructable) == JsNoError); |
| REQUIRE(JsIsConstructor(nonConstructable, &check) == JsNoError); |
| CHECK(!check); |
| } |
| } |
| |
| TEST_CASE("ApiTest_JsIsConstructorTest", "[ApiTest]") { |
| JsRTApiTest::RunWithAttributes(JsIsConstructorTest); |
| } |
| |
| void SetModuleHostInfoTest(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) |
| { |
| JsModuleRecord requestModule = JS_INVALID_REFERENCE; |
| JsValueRef specifier = nullptr; |
| |
| REQUIRE(JsPointerToString(_u("mod1.js"), wcslen(_u("mod1.js")), &specifier) == JsNoError); |
| REQUIRE(JsInitializeModuleRecord(nullptr, specifier, &requestModule) == JsNoError); |
| JsValueRef error = nullptr, errorMsg = nullptr; |
| REQUIRE(JsPointerToString(_u("test error"), wcslen(_u("test error")), &errorMsg) == JsNoError); |
| REQUIRE(JsCreateError(errorMsg, &error) == JsNoError); |
| |
| REQUIRE(JsSetModuleHostInfo(requestModule, JsModuleHostInfo_Exception, error) == JsNoError); |
| |
| JsValueRef errorOut = nullptr; |
| JsGetModuleHostInfo(requestModule, JsModuleHostInfo_Exception, &errorOut); |
| REQUIRE(errorOut == error); |
| |
| //REQUIRE(JsSetModuleHostInfo(requestModule, JsModuleHostInfo_Exception, nullptr) == JsNoError); |
| |
| REQUIRE(JsPointerToString(_u("mod2.js"), wcslen(_u("mod2.js")), &specifier) == JsNoError); |
| REQUIRE(JsInitializeModuleRecord(nullptr, specifier, &requestModule) == JsNoError); |
| |
| successTest.mainModule = requestModule; |
| REQUIRE(JsSetModuleHostInfo(requestModule, JsModuleHostInfo_NotifyModuleReadyCallback, Success_NMRC) == JsNoError); |
| |
| // Parsing |
| JsValueRef errorObject1 = JS_INVALID_REFERENCE; |
| const char* fileContent = "var x = 10"; |
| REQUIRE(JsParseModuleSource(requestModule, 0, (LPBYTE)fileContent, |
| (unsigned int)strlen(fileContent), JsParseModuleSourceFlags_DataIsUTF8, &errorObject1) == JsNoError); |
| |
| // This should not pass |
| REQUIRE(JsSetModuleHostInfo(requestModule, JsModuleHostInfo_Exception, error) != JsNoError); |
| } |
| |
| TEST_CASE("ApiTest_SetModuleHostInfoTest", "[ApiTest]") |
| { |
| JsRTApiTest::WithSetup(JsRuntimeAttributeEnableExperimentalFeatures, SetModuleHostInfoTest); |
| |
| } |
| |
| static JsErrorCode CALLBACK Success_FIMC1(_In_ JsModuleRecord referencingModule, _In_ JsValueRef specifier, _Outptr_result_maybenull_ JsModuleRecord* dependentModuleRecord) |
| { |
| JsModuleRecord moduleRecord = JS_INVALID_REFERENCE; |
| LPCWSTR specifierStr; |
| size_t length; |
| |
| JsErrorCode errorCode = JsStringToPointer(specifier, &specifierStr, &length); |
| REQUIRE(errorCode == JsNoError); |
| REQUIRE(!wcscmp(specifierStr, _u("foo.js"))); |
| |
| JsValueRef specifier1 = nullptr; |
| REQUIRE(JsPointerToString(_u("./foo.js"), wcslen(_u("./foo.js")), &specifier1) == JsNoError); |
| |
| errorCode = JsInitializeModuleRecord(referencingModule, specifier1, &moduleRecord); |
| REQUIRE(errorCode == JsNoError); |
| *dependentModuleRecord = moduleRecord; |
| successTest.childModule = moduleRecord; |
| return JsNoError; |
| } |
| |
| void PassingDifferentModuleSpecifierTest(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) |
| { |
| JsModuleRecord requestModule = JS_INVALID_REFERENCE; |
| JsValueRef specifier; |
| |
| REQUIRE(JsPointerToString(_u(""), 1, &specifier) == JsNoError); |
| REQUIRE(JsInitializeModuleRecord(nullptr, specifier, &requestModule) == JsNoError); |
| successTest.mainModule = requestModule; |
| REQUIRE(JsSetModuleHostInfo(requestModule, JsModuleHostInfo_FetchImportedModuleCallback, Success_FIMC1) == JsNoError); |
| REQUIRE(JsSetModuleHostInfo(requestModule, JsModuleHostInfo_FetchImportedModuleFromScriptCallback, Success_FIMC1) == JsNoError); |
| REQUIRE(JsSetModuleHostInfo(requestModule, JsModuleHostInfo_NotifyModuleReadyCallback, Success_NMRC) == JsNoError); |
| |
| JsValueRef errorObject = JS_INVALID_REFERENCE; |
| const char* fileContent = "import {x} from 'foo.js'"; |
| JsErrorCode errorCode = JsParseModuleSource(requestModule, 0, (LPBYTE)fileContent, |
| (unsigned int)strlen(fileContent), JsParseModuleSourceFlags_DataIsUTF8, &errorObject); |
| |
| CHECK(errorCode == JsNoError); |
| CHECK(errorObject == JS_INVALID_REFERENCE); |
| REQUIRE(successTest.childModule != JS_INVALID_REFERENCE); |
| } |
| |
| TEST_CASE("ApiTest_PassingDifferentModuleSpecifierTest", "[ApiTest]") |
| { |
| JsRTApiTest::WithSetup(JsRuntimeAttributeEnableExperimentalFeatures, PassingDifferentModuleSpecifierTest); |
| |
| } |
| |
| ModuleResponseData reentrantParseData; |
| static JsErrorCode CALLBACK ReentrantParse_FIMC(_In_ JsModuleRecord referencingModule, _In_ JsValueRef specifier, _Outptr_result_maybenull_ JsModuleRecord* dependentModuleRecord) |
| { |
| JsModuleRecord moduleRecord = JS_INVALID_REFERENCE; |
| LPCWSTR specifierStr; |
| size_t length; |
| |
| JsErrorCode errorCode = JsStringToPointer(specifier, &specifierStr, &length); |
| REQUIRE(!wcscmp(specifierStr, _u("foo.js"))); |
| |
| REQUIRE(errorCode == JsNoError); |
| errorCode = JsInitializeModuleRecord(referencingModule, specifier, &moduleRecord); |
| REQUIRE(errorCode == JsNoError); |
| *dependentModuleRecord = moduleRecord; |
| reentrantParseData.childModule = moduleRecord; |
| |
| // directly make a call to parsemodulesource |
| JsValueRef errorObject = JS_INVALID_REFERENCE; |
| const char* fileContent = "/*error code*/ var x x"; |
| |
| // Not checking the error code. |
| JsParseModuleSource(moduleRecord, 1, (LPBYTE)fileContent, |
| (unsigned int)strlen(fileContent), JsParseModuleSourceFlags_DataIsUTF8, &errorObject); |
| |
| // There must be an error |
| CHECK(errorObject != JS_INVALID_REFERENCE); |
| |
| // Passed everything is valid. |
| return JsNoError; |
| } |
| |
| static JsErrorCode CALLBACK ReentrantParse_NMRC(_In_opt_ JsModuleRecord referencingModule, _In_opt_ JsValueRef exceptionVar) |
| { |
| if (reentrantParseData.mainModule == referencingModule) |
| { |
| reentrantParseData.mainModuleReady = true; |
| reentrantParseData.mainModuleException = exceptionVar; |
| } |
| return JsNoError; |
| } |
| |
| void ReentrantParseModuleTest(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) |
| { |
| JsModuleRecord requestModule = JS_INVALID_REFERENCE; |
| JsValueRef specifier; |
| |
| REQUIRE(JsPointerToString(_u(""), 1, &specifier) == JsNoError); |
| REQUIRE(JsInitializeModuleRecord(nullptr, specifier, &requestModule) == JsNoError); |
| reentrantParseData.mainModule = requestModule; |
| REQUIRE(JsSetModuleHostInfo(requestModule, JsModuleHostInfo_FetchImportedModuleCallback, ReentrantParse_FIMC) == JsNoError); |
| REQUIRE(JsSetModuleHostInfo(requestModule, JsModuleHostInfo_FetchImportedModuleFromScriptCallback, ReentrantParse_FIMC) == JsNoError); |
| REQUIRE(JsSetModuleHostInfo(requestModule, JsModuleHostInfo_NotifyModuleReadyCallback, ReentrantParse_NMRC) == JsNoError); |
| |
| JsValueRef errorObject = JS_INVALID_REFERENCE; |
| const char* fileContent = "import {x} from 'foo.js'"; |
| JsParseModuleSource(requestModule, 0, (LPBYTE)fileContent, |
| (unsigned int)strlen(fileContent), JsParseModuleSourceFlags_DataIsUTF8, &errorObject); |
| |
| CHECK(reentrantParseData.mainModuleReady == true); |
| REQUIRE(reentrantParseData.mainModuleException != JS_INVALID_REFERENCE); |
| |
| JsPropertyIdRef message = JS_INVALID_REFERENCE; |
| |
| REQUIRE(JsGetPropertyIdFromName(_u("message"), &message) == JsNoError); |
| |
| JsValueRef value1Check = JS_INVALID_REFERENCE; |
| REQUIRE(JsGetProperty(reentrantParseData.mainModuleException, message, &value1Check) == JsNoError); |
| |
| JsValueRef asString = JS_INVALID_REFERENCE; |
| REQUIRE(JsConvertValueToString(value1Check, &asString) == JsNoError); |
| |
| LPCWSTR str = nullptr; |
| size_t length; |
| REQUIRE(JsStringToPointer(asString, &str, &length) == JsNoError); |
| REQUIRE(!wcscmp(str, _u("Expected ';'"))); |
| } |
| |
| TEST_CASE("ApiTest_ReentrantParseModuleTest", "[ApiTest]") |
| { |
| JsRTApiTest::WithSetup(JsRuntimeAttributeEnableExperimentalFeatures, ReentrantParseModuleTest); |
| } |
| |
| |
| ModuleResponseData reentrantNoErrorParseData; |
| static JsErrorCode CALLBACK reentrantNoErrorParse_FIMC(_In_ JsModuleRecord referencingModule, _In_ JsValueRef specifier, _Outptr_result_maybenull_ JsModuleRecord* dependentModuleRecord) |
| { |
| JsModuleRecord moduleRecord = JS_INVALID_REFERENCE; |
| LPCWSTR specifierStr; |
| size_t length; |
| |
| JsErrorCode errorCode = JsStringToPointer(specifier, &specifierStr, &length); |
| REQUIRE(!wcscmp(specifierStr, _u("foo.js"))); |
| |
| REQUIRE(errorCode == JsNoError); |
| errorCode = JsInitializeModuleRecord(referencingModule, specifier, &moduleRecord); |
| REQUIRE(errorCode == JsNoError); |
| *dependentModuleRecord = moduleRecord; |
| reentrantNoErrorParseData.childModule = moduleRecord; |
| |
| JsValueRef errorObject = JS_INVALID_REFERENCE; |
| const char* fileContent = "export var x = 10;"; |
| |
| // Not checking the error code. |
| JsParseModuleSource(moduleRecord, 1, (LPBYTE)fileContent, |
| (unsigned int)strlen(fileContent), JsParseModuleSourceFlags_DataIsUTF8, &errorObject); |
| |
| // There must be an error |
| CHECK(errorObject == JS_INVALID_REFERENCE); |
| |
| return JsNoError; |
| } |
| |
| static JsErrorCode CALLBACK reentrantNoErrorParse_NMRC(_In_opt_ JsModuleRecord referencingModule, _In_opt_ JsValueRef exceptionVar) |
| { |
| if (reentrantNoErrorParseData.mainModule == referencingModule) |
| { |
| reentrantNoErrorParseData.mainModuleReady = true; |
| reentrantNoErrorParseData.mainModuleException = exceptionVar; |
| } |
| return JsNoError; |
| } |
| |
| void ReentrantNoErrorParseModuleTest(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) |
| { |
| JsModuleRecord requestModule = JS_INVALID_REFERENCE; |
| JsValueRef specifier; |
| |
| REQUIRE(JsPointerToString(_u(""), 1, &specifier) == JsNoError); |
| REQUIRE(JsInitializeModuleRecord(nullptr, specifier, &requestModule) == JsNoError); |
| reentrantNoErrorParseData.mainModule = requestModule; |
| REQUIRE(JsSetModuleHostInfo(requestModule, JsModuleHostInfo_FetchImportedModuleCallback, reentrantNoErrorParse_FIMC) == JsNoError); |
| REQUIRE(JsSetModuleHostInfo(requestModule, JsModuleHostInfo_FetchImportedModuleFromScriptCallback, reentrantNoErrorParse_FIMC) == JsNoError); |
| REQUIRE(JsSetModuleHostInfo(requestModule, JsModuleHostInfo_NotifyModuleReadyCallback, reentrantNoErrorParse_NMRC) == JsNoError); |
| |
| JsValueRef errorObject = JS_INVALID_REFERENCE; |
| const char* fileContent = "import {x} from 'foo.js'"; |
| JsErrorCode errorCode = JsParseModuleSource(requestModule, 0, (LPBYTE)fileContent, |
| (unsigned int)strlen(fileContent), JsParseModuleSourceFlags_DataIsUTF8, &errorObject); |
| |
| // This is no error in this module parse. |
| CHECK(errorCode == JsNoError); |
| CHECK(errorObject == JS_INVALID_REFERENCE); |
| CHECK(reentrantNoErrorParseData.mainModuleReady == true); |
| REQUIRE(reentrantNoErrorParseData.mainModuleException == JS_INVALID_REFERENCE); |
| } |
| |
| TEST_CASE("ApiTest_ReentrantNoErrorParseModuleTest", "[ApiTest]") |
| { |
| JsRTApiTest::WithSetup(JsRuntimeAttributeEnableExperimentalFeatures, ReentrantNoErrorParseModuleTest); |
| } |
| |
| static JsErrorCode CALLBACK FIMC1(_In_ JsModuleRecord referencingModule, _In_ JsValueRef specifier, _Outptr_result_maybenull_ JsModuleRecord* dependentModuleRecord) |
| { |
| JsModuleRecord moduleRecord = JS_INVALID_REFERENCE; |
| LPCWSTR specifierStr; |
| size_t length; |
| JsErrorCode errorCode = JsStringToPointer(specifier, &specifierStr, &length); |
| REQUIRE(errorCode == JsNoError); |
| |
| if (wcscmp(specifierStr, _u("foo.js")) == 0) |
| { |
| errorCode = JsInitializeModuleRecord(referencingModule, specifier, &moduleRecord); |
| REQUIRE(errorCode == JsNoError); |
| *dependentModuleRecord = moduleRecord; |
| } |
| else |
| { |
| *dependentModuleRecord = nullptr; |
| } |
| return JsNoError; |
| } |
| |
| static JsErrorCode CALLBACK NMRC1(_In_opt_ JsModuleRecord referencingModule, _In_opt_ JsValueRef exceptionVar) |
| { |
| // NotifyModuleReadyCallback handling. |
| |
| return JsNoError; |
| } |
| |
| void SomebugTest(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) |
| { |
| JsModuleRecord rec; |
| JsInitializeModuleRecord(nullptr, nullptr, &rec); |
| JsSetModuleHostInfo(rec, JsModuleHostInfo_FetchImportedModuleCallback, FIMC1); |
| JsSetModuleHostInfo(rec, JsModuleHostInfo_FetchImportedModuleFromScriptCallback, FIMC1); |
| JsSetModuleHostInfo(rec, JsModuleHostInfo_NotifyModuleReadyCallback, NMRC1); |
| |
| JsValueRef F = JS_INVALID_REFERENCE; |
| JsErrorCode err = JsRunScript(_u("var j = import('foo.js').then(mod => { mod.bar(); })"), 0, _u(""), &F); |
| |
| CHECK(err == JsNoError); |
| } |
| |
| TEST_CASE("ApiTest_SomebugTest", "[ApiTest]") |
| { |
| JsRTApiTest::WithSetup(JsRuntimeAttributeEnableExperimentalFeatures, SomebugTest); |
| |
| } |
| void ObjectHasOwnPropertyMethodTest(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) |
| { |
| JsValueRef proto = JS_INVALID_REFERENCE; |
| JsValueRef object = JS_INVALID_REFERENCE; |
| |
| REQUIRE(JsCreateObject(&proto) == JsNoError); |
| REQUIRE(JsCreateObject(&object) == JsNoError); |
| REQUIRE(JsSetPrototype(object, proto) == JsNoError); |
| |
| JsPropertyIdRef propertyIdFoo = JS_INVALID_REFERENCE; |
| JsPropertyIdRef propertyIdBar = JS_INVALID_REFERENCE; |
| bool hasProperty = false; |
| |
| REQUIRE(JsGetPropertyIdFromName(_u("foo"), &propertyIdFoo) == JsNoError); |
| REQUIRE(JsGetPropertyIdFromName(_u("bar"), &propertyIdBar) == JsNoError); |
| |
| REQUIRE(JsSetProperty(object, propertyIdFoo, object, true) == JsNoError); |
| REQUIRE(JsSetProperty(proto, propertyIdBar, object, true) == JsNoError); |
| |
| REQUIRE(JsHasProperty(object, propertyIdFoo, &hasProperty) == JsNoError); |
| CHECK(hasProperty); |
| |
| REQUIRE(JsHasOwnProperty(object, propertyIdFoo, &hasProperty) == JsNoError); |
| CHECK(hasProperty); |
| |
| REQUIRE(JsHasProperty(object, propertyIdBar, &hasProperty) == JsNoError); |
| CHECK(hasProperty); |
| |
| REQUIRE(JsHasOwnProperty(object, propertyIdBar, &hasProperty) == JsNoError); |
| CHECK(!hasProperty); |
| } |
| |
| TEST_CASE("ApiTest_ObjectHasOwnPropertyMethodTest", "[ApiTest]") |
| { |
| JsRTApiTest::RunWithAttributes(JsRTApiTest::ObjectHasOwnPropertyMethodTest); |
| } |
| |
| void JsCopyStringOneByteMethodTest(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) |
| { |
| size_t written = 0; |
| char buf[10] = {0}; |
| JsValueRef value; |
| REQUIRE(JsCreateStringUtf16(reinterpret_cast<const uint16_t*>(_u("0\x10\x80\xa9\uabcd\U000104377")), 8, &value) == JsNoError); |
| REQUIRE(JsCopyStringOneByte(value, 0, -1, nullptr, &written) == JsNoError); |
| CHECK(written == 8); |
| buf[written] = '\xff'; |
| |
| REQUIRE(JsCopyStringOneByte(value, 0, 10, buf, &written) == JsNoError); |
| CHECK(written == 8); |
| CHECK(buf[0] == '0'); |
| CHECK(buf[1] == '\x10'); |
| CHECK(buf[2] == '\x80'); |
| CHECK(buf[3] == '\xA9'); |
| CHECK(buf[4] == '\xcd'); |
| CHECK(buf[5] == '\x01'); |
| CHECK(buf[6] == '\x37'); |
| CHECK(buf[7] == '7'); |
| CHECK(buf[8] == '\xff'); |
| } |
| |
| TEST_CASE("ApiTest_JsCopyStringOneByteMethodTest", "[ApiTest]") |
| { |
| JsRTApiTest::RunWithAttributes(JsRTApiTest::JsCopyStringOneByteMethodTest); |
| } |
| |
| void JsLessThanTest(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) |
| { |
| // Create some values |
| JsValueRef number1 = JS_INVALID_REFERENCE; // number1 = 1 |
| REQUIRE(JsDoubleToNumber(1, &number1) == JsNoError); |
| JsValueRef number2 = JS_INVALID_REFERENCE; // number2 = 2 |
| REQUIRE(JsDoubleToNumber(2, &number2) == JsNoError); |
| JsValueRef stringa = JS_INVALID_REFERENCE; // stringa = "1" |
| REQUIRE(JsPointerToString(_u("1"), wcslen(_u("1")), &stringa) == JsNoError); |
| JsValueRef undefined = GetUndefined(); |
| JsValueRef nullValue = JS_INVALID_REFERENCE; |
| REQUIRE(JsGetNullValue(&nullValue) == JsNoError); |
| JsValueRef trueValue = JS_INVALID_REFERENCE; |
| REQUIRE(JsGetTrueValue(&trueValue) == JsNoError); |
| JsValueRef falseValue = JS_INVALID_REFERENCE; |
| REQUIRE(JsGetFalseValue(&falseValue) == JsNoError); |
| |
| bool result; |
| REQUIRE(JsLessThan(number1, number2, &result) == JsNoError); |
| CHECK(result == true); |
| REQUIRE(JsLessThan(number1, stringa, &result) == JsNoError); |
| CHECK(result == false); |
| REQUIRE(JsLessThan(number1, undefined, &result) == JsNoError); |
| CHECK(result == false); |
| REQUIRE(JsLessThan(falseValue, trueValue, &result) == JsNoError); |
| CHECK(result == true); |
| REQUIRE(JsLessThan(undefined, undefined, &result) == JsNoError); |
| CHECK(result == false); |
| REQUIRE(JsLessThan(nullValue, undefined, &result) == JsNoError); |
| CHECK(result == false); |
| |
| REQUIRE(JsLessThanOrEqual(number1, number2, &result) == JsNoError); |
| CHECK(result == true); |
| REQUIRE(JsLessThanOrEqual(number1, number1, &result) == JsNoError); |
| CHECK(result == true); |
| REQUIRE(JsLessThanOrEqual(number1, stringa, &result) == JsNoError); |
| CHECK(result == true); |
| REQUIRE(JsLessThanOrEqual(trueValue, trueValue, &result) == JsNoError); |
| CHECK(result == true); |
| REQUIRE(JsLessThanOrEqual(falseValue, nullValue, &result) == JsNoError); |
| CHECK(result == true); |
| REQUIRE(JsLessThanOrEqual(falseValue, undefined, &result) == JsNoError); |
| CHECK(result == false); |
| REQUIRE(JsLessThanOrEqual(undefined, undefined, &result) == JsNoError); |
| CHECK(result == false); |
| REQUIRE(JsLessThanOrEqual(nullValue, undefined, &result) == JsNoError); |
| CHECK(result == false); |
| } |
| |
| TEST_CASE("ApiTest_JsLessThanTest", "[ApiTest]") |
| { |
| JsRTApiTest::RunWithAttributes(JsRTApiTest::JsLessThanTest); |
| } |
| |
| void JsCreateStringTest(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) |
| { |
| // Passing in invalid utf8 sequences should result in the unicode replacement character |
| const char invalidUtf8[] = { -127 /* 0x80 */, '\0' }; |
| JsValueRef result; |
| REQUIRE(JsCreateString(invalidUtf8, 1, &result) == JsNoError); |
| uint16_t utf16Result[2]; |
| size_t written; |
| REQUIRE(JsCopyStringUtf16(result, 0, 1, utf16Result, &written) == JsNoError); |
| CHECK(written == 1); |
| CHECK(utf16Result[0] == 0xFFFD); |
| |
| // Creating a utf8 string and then copying it back out should give an identical string |
| // Specifying -1 as the length should result in using strlen as the length |
| const char validUtf8Input[] = {'T', 'e', 's', 't', ' ', -30 /* 0xe2 */, -104 /* 0x98 */, -125 /* 0x83 */, 0}; |
| REQUIRE(JsCreateString(validUtf8Input, static_cast<size_t>(-1), &result) == JsNoError); |
| char utf8Result[10]; |
| REQUIRE(JsCopyString(result,utf8Result, 10, &written) == JsNoError); |
| CHECK(written == strlen(validUtf8Input)); |
| CHECK(memcmp(utf8Result, validUtf8Input, written) == 0); |
| } |
| |
| TEST_CASE("ApiTest_JsCreateStringTest", "[ApiTest]") |
| { |
| JsRTApiTest::RunWithAttributes(JsRTApiTest::JsCreateStringTest); |
| } |
| |
| void ApiTest_JsSerializeArrayTest(JsRuntimeAttributes /*attributes*/, JsRuntimeHandle /*runtime*/) |
| { |
| LPCSTR raw_script = "(function (){return true;})();"; |
| LPCWSTR raw_wscript = L"(function (){return true;})();"; |
| |
| // JsSerializeScript has good test coverage and can be used as an oracle for JsSerialize |
| unsigned int bcBufferSize_Expected = 0; |
| REQUIRE(JsSerializeScript(raw_wscript, nullptr, &bcBufferSize_Expected) == JsNoError); |
| BYTE *bcBuffer_Expected = new BYTE[bcBufferSize_Expected]; |
| REQUIRE(JsSerializeScript(raw_wscript, bcBuffer_Expected, &bcBufferSize_Expected) == JsNoError); |
| REQUIRE(bcBuffer_Expected != nullptr); |
| |
| // JsSerialize from an external array |
| JsValueRef scriptSource = JS_INVALID_REFERENCE; |
| REQUIRE(JsCreateExternalArrayBuffer( |
| (void*)raw_script, (unsigned int)strlen(raw_script), nullptr, (void*)raw_script, &scriptSource) == JsNoError); |
| |
| JsValueRef buffer = JS_INVALID_REFERENCE; |
| REQUIRE(JsSerialize(scriptSource, &buffer, JsParseScriptAttributeNone) == JsNoError); |
| |
| BYTE *bcBuffer = nullptr; |
| unsigned int bcBufferSize = 0; |
| REQUIRE(JsGetArrayBufferStorage(buffer, &bcBuffer, &bcBufferSize) == JsNoError); |
| |
| REQUIRE(bcBufferSize_Expected == bcBufferSize); |
| CHECK(memcmp(bcBuffer_Expected, bcBuffer, bcBufferSize_Expected) == 0); |
| } |
| |
| TEST_CASE("ApiTest_JsSerialize_Array", "[ApiTest]") |
| { |
| JsRTApiTest::RunWithAttributes(JsRTApiTest::ApiTest_JsSerializeArrayTest); |
| } |
| |
| void ApiTest_JsSerializeStringTest(JsRuntimeAttributes /*attributes*/, JsRuntimeHandle /*runtime*/) |
| { |
| LPCSTR raw_script = "(function (){return true;})();"; |
| LPCWSTR raw_wscript = L"(function (){return true;})();"; |
| |
| // JsSerializeScript has good test coverage and can be used as an oracle for JsSerialize |
| unsigned int bcBufferSize_Expected = 0; |
| REQUIRE(JsSerializeScript(raw_wscript, nullptr, &bcBufferSize_Expected) == JsNoError); |
| BYTE* bcBuffer_Expected = new BYTE[bcBufferSize_Expected]; |
| REQUIRE(JsSerializeScript(raw_wscript, bcBuffer_Expected, &bcBufferSize_Expected) == JsNoError); |
| REQUIRE(bcBuffer_Expected != nullptr); |
| |
| // JsSerialize from a string |
| JsValueRef script = JS_INVALID_REFERENCE; |
| REQUIRE(JsCreateString(raw_script, static_cast<size_t>(-1), &script) == JsNoError); |
| |
| JsValueRef buffer = JS_INVALID_REFERENCE; |
| REQUIRE(JsSerialize(script, &buffer, JsParseScriptAttributeNone) == JsNoError); |
| |
| BYTE *bcBuffer = nullptr; |
| unsigned int bcBufferSize = 0; |
| REQUIRE(JsGetArrayBufferStorage(buffer, &bcBuffer, &bcBufferSize) == JsNoError); |
| |
| REQUIRE(bcBufferSize_Expected == bcBufferSize); |
| CHECK(memcmp(bcBuffer_Expected, bcBuffer, bcBufferSize_Expected) == 0); |
| |
| delete[] bcBuffer_Expected; |
| } |
| |
| TEST_CASE("ApiTest_JsSerialize_String", "[ApiTest]") |
| { |
| JsRTApiTest::RunWithAttributes(JsRTApiTest::ApiTest_JsSerializeStringTest); |
| } |
| |
| void ApiTest_JsSerializeParseErrorTest(JsRuntimeAttributes /*attributes*/, JsRuntimeHandle /*runtime*/) |
| { |
| LPCSTR raw_script = "(function (){return true;})(;"; |
| |
| JsValueRef script = JS_INVALID_REFERENCE; |
| REQUIRE(JsCreateString(raw_script, static_cast<size_t>(-1), &script) == JsNoError); |
| |
| JsValueRef buffer = JS_INVALID_REFERENCE; |
| CHECK(JsSerialize(script, &buffer, JsParseScriptAttributeNone) == JsErrorScriptCompile); |
| } |
| |
| TEST_CASE("ApiTest_JsSerialize_FailParse", "[ApiTest]") |
| { |
| JsRTApiTest::RunWithAttributes(JsRTApiTest::ApiTest_JsSerializeParseErrorTest); |
| } |
| |
| void JsCreatePromiseTest(JsRuntimeAttributes attributes, JsRuntimeHandle runtime) |
| { |
| JsValueRef result = JS_INVALID_REFERENCE; |
| |
| JsValueRef promise = JS_INVALID_REFERENCE; |
| JsValueRef resolve = JS_INVALID_REFERENCE; |
| JsValueRef reject = JS_INVALID_REFERENCE; |
| |
| // Create resolvable promise |
| REQUIRE(JsCreatePromise(&promise, &resolve, &reject) == JsNoError); |
| |
| JsPromiseState state = JsPromiseStatePending; |
| REQUIRE(JsGetPromiseState(promise, &state) == JsNoError); |
| CHECK(state == JsPromiseStatePending); |
| |
| result = JS_INVALID_REFERENCE; |
| CHECK(JsGetPromiseResult(promise, &result) == JsErrorPromisePending); |
| CHECK(result == JS_INVALID_REFERENCE); |
| |
| JsValueRef num = JS_INVALID_REFERENCE; |
| REQUIRE(JsIntToNumber(42, &num) == JsNoError); |
| |
| std::array<JsValueRef, 2> args{ GetUndefined(), num }; |
| REQUIRE(JsCallFunction(resolve, args.data(), static_cast<unsigned short>(args.size()), &result) == JsNoError); |
| |
| state = JsPromiseStatePending; |
| REQUIRE(JsGetPromiseState(promise, &state) == JsNoError); |
| CHECK(state == JsPromiseStateFulfilled); |
| |
| result = JS_INVALID_REFERENCE; |
| REQUIRE(JsGetPromiseResult(promise, &result) == JsNoError); |
| |
| int resultNum = 0; |
| REQUIRE(JsNumberToInt(result, &resultNum) == JsNoError); |
| CHECK(resultNum == 42); |
| |
| // Create rejectable promise |
| REQUIRE(JsCreatePromise(&promise, &resolve, &reject) == JsNoError); |
| |
| state = JsPromiseStatePending; |
| REQUIRE(JsGetPromiseState(promise, &state) == JsNoError); |
| CHECK(state == JsPromiseStatePending); |
| |
| result = JS_INVALID_REFERENCE; |
| CHECK(JsGetPromiseResult(promise, &result) == JsErrorPromisePending); |
| CHECK(result == JS_INVALID_REFERENCE); |
| |
| num = JS_INVALID_REFERENCE; |
| REQUIRE(JsIntToNumber(43, &num) == JsNoError); |
| |
| args = { GetUndefined(), num }; |
| REQUIRE(JsCallFunction(reject, args.data(), static_cast<unsigned short>(args.size()), &result) == JsNoError); |
| |
| state = JsPromiseStatePending; |
| REQUIRE(JsGetPromiseState(promise, &state) == JsNoError); |
| CHECK(state == JsPromiseStateRejected); |
| |
| result = JS_INVALID_REFERENCE; |
| REQUIRE(JsGetPromiseResult(promise, &result) == JsNoError); |
| |
| resultNum = 0; |
| REQUIRE(JsNumberToInt(result, &resultNum) == JsNoError); |
| CHECK(resultNum == 43); |
| } |
| |
| TEST_CASE("ApiTest_JsCreatePromiseTest", "[ApiTest]") |
| { |
| JsRTApiTest::RunWithAttributes(JsRTApiTest::JsCreatePromiseTest); |
| } |
| } |