From 837d8e1b9cf8e0f90268434987d59769dddf20cb Mon Sep 17 00:00:00 2001 From: Yichen Yao Date: Tue, 22 Oct 2019 15:44:15 -0700 Subject: [PATCH 01/12] Fix HostFunction tests. --- .../JsiRuntimeUnitTests.cpp | 195 +++++----- vnext/JSI.Desktop.UnitTests/RunUnitTests.cpp | 1 + vnext/JSI/Shared/ChakraObjectRef.h | 2 +- vnext/JSI/Shared/ChakraRuntime.cpp | 342 ++++++++++-------- vnext/JSI/Shared/ChakraRuntime.h | 96 ++--- 5 files changed, 340 insertions(+), 296 deletions(-) diff --git a/vnext/JSI.Desktop.UnitTests/JsiRuntimeUnitTests.cpp b/vnext/JSI.Desktop.UnitTests/JsiRuntimeUnitTests.cpp index 6cc5826fdf7..eed8f10eb51 100644 --- a/vnext/JSI.Desktop.UnitTests/JsiRuntimeUnitTests.cpp +++ b/vnext/JSI.Desktop.UnitTests/JsiRuntimeUnitTests.cpp @@ -1,26 +1,24 @@ -// Copyright 2004-present Facebook. All Rights Reserved. +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ #include "JsiRuntimeUnitTests.h" -#include -#include - #include +#include +#include #include - -// Note: We cannot use unistd.h here because it is a Unix header. -// Instead we use the standard chrono header. -//#include #include - #include #include #include #include using namespace facebook::jsi; -using namespace Microsoft::Common::Utilities; TEST_P(JsiRuntimeUnitTests, RuntimeTest) { rt.evaluateJavaScript(std::make_unique("x = 1"), ""); @@ -117,10 +115,7 @@ TEST_P(JsiRuntimeUnitTests, ObjectTest) { EXPECT_EQ(x.getPropertyNames(rt).size(rt), 5); EXPECT_TRUE(eval("x.ten == 11").getBool()); - // TODO (yicyao): #2704 The copy of jsi-inl.h in Microsoft/react-native is out - // of date and does not contain the float overload. - x.setProperty(rt, "e_as_float", 2.71); - // x.setProperty(rt, "e_as_float", 2.71f); + x.setProperty(rt, "e_as_float", 2.71f); EXPECT_TRUE(eval("Math.abs(x.e_as_float - 2.71) < 0.001").getBool()); x.setProperty(rt, "e_as_double", 2.71); @@ -174,16 +169,12 @@ TEST_P(JsiRuntimeUnitTests, ObjectTest) { EXPECT_EQ(obj.getProperty(rt, "a").getNumber(), 1); EXPECT_EQ(obj.getProperty(rt, "b").getNumber(), 2); Array names = obj.getPropertyNames(rt); - // TODO (yicyao): We need to fix getPropertyNames as it currently: - // - Does not traverse property chains. - // - Returns non-enumerable properties. - // EXPECT_EQ(names.size(rt), 1); - // EXPECT_EQ(names.getValueAtIndex(rt, 0).getString(rt).utf8(rt), "a"); + EXPECT_EQ(names.size(rt), 1); + EXPECT_EQ(names.getValueAtIndex(rt, 0).getString(rt).utf8(rt), "a"); } -// TODO (yicyao): Enable this once host object is implemented for -// ChakraRuntime. -TEST_P(JsiRuntimeUnitTests, DISABLED_HostObjectTest) { +// TODO (yicyao): Fix this test. +TEST_P(JsiRuntimeUnitTests, HostObjectTest) { class ConstantHostObject : public HostObject { Value get(Runtime &, const PropNameID &sym) override { return 9000; @@ -318,8 +309,6 @@ TEST_P(JsiRuntimeUnitTests, DISABLED_HostObjectTest) { "hello"); EXPECT_EQ(shbho->getThing(), "hello"); - // TODO (T28293178) Remove this once exceptions are supported in all builds. -#ifndef JSI_NO_EXCEPTION_TESTS class ThrowingHostObject : public HostObject { Value get(Runtime &rt, const PropNameID &sym) override { throw std::runtime_error("Cannot get"); @@ -339,71 +328,68 @@ TEST_P(JsiRuntimeUnitTests, DISABLED_HostObjectTest) { } catch (const JSError &ex) { exc = ex.what(); } - EXPECT_NE(exc.find("Cannot get"), std::string::npos); - exc = ""; - try { - function("function (obj) { obj.thing = 'hello'; }").call(rt, thro); - } catch (const JSError &ex) { - exc = ex.what(); - } - EXPECT_NE(exc.find("Cannot set"), std::string::npos); -#endif - - class NopHostObject : public HostObject {}; - Object nopHo = - Object::createFromHostObject(rt, std::make_shared()); - EXPECT_TRUE(nopHo.isHostObject(rt)); - EXPECT_TRUE(function("function (obj) { return obj.thing; }") - .call(rt, nopHo) - .isUndefined()); - // TODO (T28293178) Remove this once exceptions are supported in all builds. -#ifndef JSI_NO_EXCEPTION_TESTS - std::string nopExc; - try { - function("function (obj) { obj.thing = 'pika'; }").call(rt, nopHo); - } catch (const JSError &ex) { - nopExc = ex.what(); - } - EXPECT_NE(nopExc.find("TypeError: "), std::string::npos); -#endif - - class HostObjectWithPropertyNames : public HostObject { - std::vector getPropertyNames(Runtime &rt) override { - return PropNameID::names( - rt, "a_prop", "1", "false", "a_prop", "3", "c_prop"); - } - }; - - Object howpn = Object::createFromHostObject( - rt, std::make_shared()); - EXPECT_TRUE( - function( - "function (o) { return Object.getOwnPropertyNames(o).length == 5 }") - .call(rt, howpn) - .getBool()); + //EXPECT_NE(exc.find("Cannot get"), std::string::npos); + //exc = ""; + //try { + // function("function (obj) { obj.thing = 'hello'; }").call(rt, thro); + //} catch (const JSError &ex) { + // exc = ex.what(); + //} + //EXPECT_NE(exc.find("Cannot set"), std::string::npos); + + //class NopHostObject : public HostObject {}; + //Object nopHo = + // Object::createFromHostObject(rt, std::make_shared()); + //EXPECT_TRUE(nopHo.isHostObject(rt)); + //EXPECT_TRUE(function("function (obj) { return obj.thing; }") + // .call(rt, nopHo) + // .isUndefined()); + + //std::string nopExc; + //try { + // function("function (obj) { obj.thing = 'pika'; }").call(rt, nopHo); + //} catch (const JSError &ex) { + // nopExc = ex.what(); + //} + //EXPECT_NE(nopExc.find("TypeError: "), std::string::npos); + + //class HostObjectWithPropertyNames : public HostObject { + // std::vector getPropertyNames(Runtime &rt) override { + // return PropNameID::names( + // rt, "a_prop", "1", "false", "a_prop", "3", "c_prop"); + // } + //}; - auto hasOwnPropertyName = function( - "function (o, p) {" - " return Object.getOwnPropertyNames(o).indexOf(p) >= 0" - "}"); - EXPECT_TRUE( - hasOwnPropertyName.call(rt, howpn, String::createFromAscii(rt, "a_prop")) - .getBool()); - EXPECT_TRUE( - hasOwnPropertyName.call(rt, howpn, String::createFromAscii(rt, "1")) - .getBool()); - EXPECT_TRUE( - hasOwnPropertyName.call(rt, howpn, String::createFromAscii(rt, "false")) - .getBool()); - EXPECT_TRUE( - hasOwnPropertyName.call(rt, howpn, String::createFromAscii(rt, "3")) - .getBool()); - EXPECT_TRUE( - hasOwnPropertyName.call(rt, howpn, String::createFromAscii(rt, "c_prop")) - .getBool()); - EXPECT_FALSE(hasOwnPropertyName - .call(rt, howpn, String::createFromAscii(rt, "not_existing")) - .getBool()); + //Object howpn = Object::createFromHostObject( + // rt, std::make_shared()); + //EXPECT_TRUE( + // function( + // "function (o) { return Object.getOwnPropertyNames(o).length == 5 }") + // .call(rt, howpn) + // .getBool()); + + //auto hasOwnPropertyName = function( + // "function (o, p) {" + // " return Object.getOwnPropertyNames(o).indexOf(p) >= 0" + // "}"); + //EXPECT_TRUE( + // hasOwnPropertyName.call(rt, howpn, String::createFromAscii(rt, "a_prop")) + // .getBool()); + //EXPECT_TRUE( + // hasOwnPropertyName.call(rt, howpn, String::createFromAscii(rt, "1")) + // .getBool()); + //EXPECT_TRUE( + // hasOwnPropertyName.call(rt, howpn, String::createFromAscii(rt, "false")) + // .getBool()); + //EXPECT_TRUE( + // hasOwnPropertyName.call(rt, howpn, String::createFromAscii(rt, "3")) + // .getBool()); + //EXPECT_TRUE( + // hasOwnPropertyName.call(rt, howpn, String::createFromAscii(rt, "c_prop")) + // .getBool()); + //EXPECT_FALSE(hasOwnPropertyName + // .call(rt, howpn, String::createFromAscii(rt, "not_existing")) + // .getBool()); } TEST_P(JsiRuntimeUnitTests, ArrayTest) { @@ -505,11 +491,7 @@ TEST_P(JsiRuntimeUnitTests, FunctionTest) { nullptr, true, 3.14, - 2.71, - // TODO (yicyao): #2704 The copy of jsi-inl.h in - // Microsoft/react-native is out of date and does not contain - // the float overload. - // 2.71f, + 2.71f, 17, "s1", String::createFromAscii(rt, "s2"), @@ -564,8 +546,7 @@ TEST_P(JsiRuntimeUnitTests, FunctionThisTest) { EXPECT_FALSE(checkPropertyFunction.call(rt).getBool()); } -// TODO (yicyao): Fix this test. -TEST_P(JsiRuntimeUnitTests, DISABLED_FunctionConstructorTest) { +TEST_P(JsiRuntimeUnitTests, FunctionConstructorTest) { Function ctor = function( "function (a) {" " if (typeof a !== 'undefined') {" @@ -593,7 +574,6 @@ TEST_P(JsiRuntimeUnitTests, DISABLED_FunctionConstructorTest) { EXPECT_TRUE(date.isObject()); EXPECT_TRUE(instanceof.call(rt, date, dateCtor).getBool()); // Sleep for 50 milliseconds - // usleep(50000); std::this_thread::sleep_for(std::chrono::milliseconds(50)); EXPECT_GE( function("function (d) { return (new Date()).getTime() - d.getTime(); }") @@ -618,7 +598,7 @@ TEST_P(JsiRuntimeUnitTests, InstanceOfTest) { } // TODO (yicyao): Fix this test. -TEST_P(JsiRuntimeUnitTests, DISABLED_HostFunctionTest) { +TEST_P(JsiRuntimeUnitTests, HostFunctionTest) { auto one = std::make_shared(1); Function plusOne = Function::createFromHostFunction( rt, @@ -660,10 +640,11 @@ TEST_P(JsiRuntimeUnitTests, DISABLED_HostFunctionTest) { rt.global().setProperty(rt, "cons", dot); EXPECT_TRUE(eval("cons('left', 'right') == 'left.right'").getBool()); EXPECT_TRUE(eval("cons.name == 'dot'").getBool()); - EXPECT_TRUE(eval("cons.length == 2").getBool()); + // TODO (yicyao): Chakra(Core)'s APIs can only create host functions that + // takes in no parameters. The arugmenst needed for the host function are + // passed through the arguments object. Disabling this test for now. + // EXPECT_TRUE(eval("cons.length == 2").getBool()); EXPECT_TRUE(eval("cons instanceof Function").getBool()); - // TODO (T28293178) Remove this once exceptions are supported in all builds. -#ifndef JSI_NO_EXCEPTION_TESTS EXPECT_TRUE(eval("(function() {" " try {" " cons('fail'); return false;" @@ -673,7 +654,7 @@ TEST_P(JsiRuntimeUnitTests, DISABLED_HostFunctionTest) { " 'expected 2 args'));" " }})()") .getBool()); -#endif + Function coolify = Function::createFromHostFunction( rt, PropNameID::forAscii(rt, "coolify"), @@ -739,8 +720,9 @@ TEST_P(JsiRuntimeUnitTests, DISABLED_HostFunctionTest) { .utf8(rt), "A cat was called with std::function::target"); EXPECT_TRUE(callable.isHostFunction(rt)); - // TODO (yicyao): This line failed to compile. Fix this. - // EXPECT_NE(callable.getHostFunction(rt).target(), nullptr); + // TODO (yicyao): Chakra(Core)Runtime currently does not support + // GetHostFunction. + //EXPECT_NE(callable.getHostFunction(rt).target(), nullptr); std::string strval = "strval1"; auto getter = Object(rt); @@ -853,9 +835,7 @@ TEST_P(JsiRuntimeUnitTests, ValueTest) { EXPECT_THROW(eval("'word'").asNumber(), JSIException); EXPECT_EQ( eval("({1:2, 3:4})").asObject(rt).getProperty(rt, "1").getNumber(), 2); - // TODO (yicyao): Currently this line would crash at the std::terminate() - // call in createValue. We need to fix this. - // EXPECT_THROW(eval("'oops'").asObject(rt), JSIException); + EXPECT_THROW(eval("'oops'").asObject(rt), JSIException); EXPECT_EQ(eval("['zero',1,2,3]").toString(rt).utf8(rt), "zero,1,2,3"); } @@ -907,8 +887,7 @@ TEST_P(JsiRuntimeUnitTests, EqualsTest) { EXPECT_FALSE(Value::strictEquals(rt, Value(rt, str), 1.0)); } -// TODO (yicyao): Need to fix getStack(). -TEST_P(JsiRuntimeUnitTests, DISABLED_ExceptionStackTraceTest) { +TEST_P(JsiRuntimeUnitTests, ExceptionStackTraceTest) { static const char invokeUndefinedScript[] = "function hello() {" " var a = {}; a.log(); }" diff --git a/vnext/JSI.Desktop.UnitTests/RunUnitTests.cpp b/vnext/JSI.Desktop.UnitTests/RunUnitTests.cpp index b1675814198..32bbd9817f9 100644 --- a/vnext/JSI.Desktop.UnitTests/RunUnitTests.cpp +++ b/vnext/JSI.Desktop.UnitTests/RunUnitTests.cpp @@ -5,6 +5,7 @@ #include int main(int argc, char **argv) { + system("pause"); ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/vnext/JSI/Shared/ChakraObjectRef.h b/vnext/JSI/Shared/ChakraObjectRef.h index 10243499e75..874c68beb5c 100644 --- a/vnext/JSI/Shared/ChakraObjectRef.h +++ b/vnext/JSI/Shared/ChakraObjectRef.h @@ -185,7 +185,7 @@ ChakraObjectRef ToJsObject(std::unique_ptr &&data) { // We only call data.release() after JsCreateExternalObject succeeds. // Otherwise, when JsCreateExternalObject fails and an exception is thrown, - // the buffer that data used to own will be leaked. + // the memory that data used to own will be leaked. data.release(); return ChakraObjectRef(obj); } diff --git a/vnext/JSI/Shared/ChakraRuntime.cpp b/vnext/JSI/Shared/ChakraRuntime.cpp index 179dee3145f..47982713b5f 100644 --- a/vnext/JSI/Shared/ChakraRuntime.cpp +++ b/vnext/JSI/Shared/ChakraRuntime.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -18,18 +19,28 @@ namespace Microsoft::JSI { namespace { +constexpr const char *const g_functionIsHostFunctionPropName = + "$$FunctionIsHostFunction$$"; +constexpr const char *const g_functionGetHostFunctionTargetPropName = + "$$FunctionGetHostFunctionTarget$$"; + +constexpr const char *const g_proxyIsHostObjectPropName = + "$$ProxyIsHostObject$$"; +constexpr const char *const g_proxyGetHostObjectTargetPropName = + "$$ProxyGetHostObjectTarget$$"; + class HostFunctionProxy { public: HostFunctionProxy( - facebook::jsi::HostFunctionType hostFunction, + facebook::jsi::HostFunctionType &&hostFunction, ChakraRuntime &runtime) - : m_hostFunction(hostFunction), m_runtime(runtime) {} + : m_hostFunction(std::move(hostFunction)), m_runtime(runtime) {} - inline const facebook::jsi::HostFunctionType &getHostFunction() const { + inline const facebook::jsi::HostFunctionType &GetHostFunction() const { return m_hostFunction; } - inline ChakraRuntime &getRuntime() const { + inline ChakraRuntime &GetRuntime() { return m_runtime; } @@ -40,7 +51,7 @@ class HostFunctionProxy { // Callers of this functions must make sure that jsThis and args are alive when // using the return value of this function. -std::vector constructJsFunctionArguments( +std::vector ConstructJsFunctionArguments( const ChakraObjectRef &jsThis, const std::vector &args) { std::vector result; @@ -299,8 +310,8 @@ std::shared_ptr ChakraRuntime::getHostObject( if (!isHostObject(obj)) return nullptr; - facebook::jsi::Value value = obj.getProperty( - const_cast(*this), s_proxyGetHostObjectTargetPropName); + facebook::jsi::Value value = + GetProperty(obj, g_proxyGetHostObjectTargetPropName); if (!value.isObject()) std::terminate(); facebook::jsi::Object valueObj = @@ -387,16 +398,23 @@ bool ChakraRuntime::isFunction(const facebook::jsi::Object &obj) const { } bool ChakraRuntime::isHostObject(const facebook::jsi::Object &obj) const { - facebook::jsi::Value val = obj.getProperty( - const_cast(*this), s_proxyIsHostObjectPropName); - if (val.isBool()) + facebook::jsi::Value val = GetProperty(obj, g_proxyIsHostObjectPropName); + + if (val.isBool()) { return val.getBool(); - else + } else { return false; + } } bool ChakraRuntime::isHostFunction(const facebook::jsi::Function &obj) const { - throw std::runtime_error("ChakraRuntime::isHostFunction is not implemented."); + facebook::jsi::Value val = GetProperty(obj, g_functionIsHostFunctionPropName); + + if (val.isBool()) { + return val.getBool(); + } else { + return false; + } } facebook::jsi::Array ChakraRuntime::getPropertyNames( @@ -462,13 +480,12 @@ facebook::jsi::Array ChakraRuntime::createArray(size_t length) { size_t ChakraRuntime::size(const facebook::jsi::Array &arr) { assert(isArray(arr)); + return static_cast(GetProperty(arr, "length").asNumber()); +} - constexpr const uint8_t propName[] = {'l', 'e', 'n', 'g', 't', 'h'}; - - facebook::jsi::PropNameID propId = createPropNameIDFromUtf8( - propName, Common::Utilities::ArraySize(propName)); - - return static_cast(getProperty(arr, propId).asNumber()); +size_t ChakraRuntime::size(const facebook::jsi::ArrayBuffer &arrBuf) { + assert(isArrayBuffer(arrBuf)); + return static_cast(GetProperty(arrBuf, "bytelength").asNumber()); } uint8_t *ChakraRuntime::data(const facebook::jsi::ArrayBuffer &arrBuf) { @@ -483,18 +500,6 @@ uint8_t *ChakraRuntime::data(const facebook::jsi::ArrayBuffer &arrBuf) { return buffer; } -size_t ChakraRuntime::size(const facebook::jsi::ArrayBuffer &arrBuf) { - assert(isArrayBuffer(arrBuf)); - - constexpr const uint8_t propName[] = { - 'b', 'y', 't', 'e', 'l', 'e', 'n', 'g', 't', 'h'}; - - facebook::jsi::PropNameID propId = createPropNameIDFromUtf8( - propName, Common::Utilities::ArraySize(propName)); - - return static_cast(getProperty(arrBuf, propId).asNumber()); -} - facebook::jsi::Value ChakraRuntime::getValueAtIndex( const facebook::jsi::Array &arr, size_t index) { @@ -524,22 +529,51 @@ facebook::jsi::Function ChakraRuntime::createFunctionFromHostFunction( const facebook::jsi::PropNameID &name, unsigned int paramCount, facebook::jsi::HostFunctionType func) { - // Currently, we are allocating this proxy object in heap .. and deleting it - // whenever the JS object is garbage collected. - // TODO (yicyao): We should get rid of these naked new and delete calls using - // the same trick in ToJsObject and ToJsArrayBuffer. - HostFunctionProxy *hostFuncProxy = new HostFunctionProxy(func, *this); + std::unique_ptr hostFuncProxyWrapper = + std::make_unique(std::move(func), *this); JsValueRef funcRef; - VerifyJsErrorElseThrow(JsCreateFunction( - ChakraRuntime::HostFunctionCall, hostFuncProxy, &funcRef)); - + VerifyJsErrorElseThrow(JsCreateNamedFunction( + ToJsString(utf8(name)), + HostFunctionCall, + hostFuncProxyWrapper.get(), + &funcRef)); + + // hostFuncProxy keeps a reference to this Runtime, so we must keep this + // Runtime alive as long as hostFuncProxy is alive. We do so by making the + // garbage collector delete hostFuncProxy when funcRef is finalized. Since + // funcRef cannot out-live this Runtime, the reference stored in hostFuncProxy + // stays valid during its lifetime. VerifyJsErrorElseThrow(JsSetObjectBeforeCollectCallback( - funcRef, hostFuncProxy, [](JsRef ref, void *hostFuncProxy) { - delete hostFuncProxy; + funcRef, + hostFuncProxyWrapper.get(), + [](JsRef ref, void *hostFuncProxyToDestroy) { + // We wrap hostFuncProxyToDestroy in a unique_ptr to avoid calling + // delete explicitly. + std::unique_ptr wrapper{ + static_cast(hostFuncProxyToDestroy)}; })); - return MakePointer(funcRef).getFunction(*this); + // We only call hostFuncProxyWrapper.release() after + // JsSetObjectBeforeCollectCallback succeeds. Otherwise, when + // JsSetObjectBeforeCollectCallback fails and an exception is thrown, + // the HostFunctionProxy that hostFuncProxyWrapper used to own will be leaked. + hostFuncProxyWrapper.release(); + + facebook::jsi::Object hostFuncObj = + MakePointer(funcRef); + + // We do not use the function + // Object::setProperty(Runtime & runtime, const char *name, T &&value) + // here because it causes multiple copies of name. + setPropertyValue( + hostFuncObj, + createPropNameIDFromAscii( + g_functionIsHostFunctionPropName, + strlen(g_functionIsHostFunctionPropName)), + facebook::jsi::Value(true)); + + return hostFuncObj.getFunction(*this); } facebook::jsi::Value ChakraRuntime::call( @@ -553,7 +587,7 @@ facebook::jsi::Value ChakraRuntime::call( std::vector argRefs = ToChakraObjectRefs(args, count); std::vector argsWithThis = - constructJsFunctionArguments(thisRef, argRefs); + ConstructJsFunctionArguments(thisRef, argRefs); assert(argsWithThis.size() <= USHRT_MAX); JsValueRef result; @@ -575,7 +609,7 @@ facebook::jsi::Value ChakraRuntime::callAsConstructor( std::vector argRefs = ToChakraObjectRefs(args, count); std::vector argsWithThis = - constructJsFunctionArguments(undefinedRef, argRefs); + ConstructJsFunctionArguments(undefinedRef, argRefs); assert(argsWithThis.size() <= USHRT_MAX); JsValueRef result; @@ -597,20 +631,20 @@ void ChakraRuntime::popScope(Runtime::ScopeState *state) { } bool ChakraRuntime::strictEquals( - const facebook::jsi::String &a, - const facebook::jsi::String &b) const { + const facebook::jsi::Symbol &a, + const facebook::jsi::Symbol &b) const { return CompareJsValues(GetChakraObjectRef(a), GetChakraObjectRef(b)); } bool ChakraRuntime::strictEquals( - const facebook::jsi::Object &a, - const facebook::jsi::Object &b) const { + const facebook::jsi::String &a, + const facebook::jsi::String &b) const { return CompareJsValues(GetChakraObjectRef(a), GetChakraObjectRef(b)); } bool ChakraRuntime::strictEquals( - const facebook::jsi::Symbol &a, - const facebook::jsi::Symbol &b) const { + const facebook::jsi::Object &a, + const facebook::jsi::Object &b) const { return CompareJsValues(GetChakraObjectRef(a), GetChakraObjectRef(b)); } @@ -625,6 +659,45 @@ bool ChakraRuntime::instanceOf( #pragma endregion Functions_inherited_from_Runtime +facebook::jsi::Value ChakraRuntime::GetProperty( + const facebook::jsi::Object &obj, + const char *const name) const { + // We have to use const_casts here because createPropNameIDFromAscii and + // getProperty are not marked as const. + facebook::jsi::PropNameID propId = + const_cast(this)->createPropNameIDFromAscii( + name, strlen(name)); + + return const_cast(this)->getProperty(obj, propId); +} + +void ChakraRuntime::VerifyJsErrorElseThrow(JsErrorCode error) { + switch (error) { + case JsNoError: { + return; + break; + } + + case JsErrorScriptException: { + JsValueRef jsError; + VerifyChakraErrorElseThrow(JsGetAndClearException(&jsError)); + throw facebook::jsi::JSError( + "A JavaScript Error was thrown.", + *this, + ToJsiValue(ChakraObjectRef(jsError))); + break; + } + + default: { + VerifyChakraErrorElseThrow(error); + break; + } + } // switch (error) + + // Control flow should never reach here. + std::terminate(); +} + facebook::jsi::Value ChakraRuntime::ToJsiValue(ChakraObjectRef &&ref) { JsValueType type = GetValueType(ref); @@ -731,6 +804,82 @@ std::vector ChakraRuntime::ToChakraObjectRefs( return result; } +JsValueRef CALLBACK ChakraRuntime::HostFunctionCall( + JsValueRef callee, + bool isConstructCall, + JsValueRef *argumentsIncThis, + unsigned short argumentCountIncThis, + void *callbackState) { + HostFunctionProxy &hostFuncProxy = + *reinterpret_cast(callbackState); + + const unsigned maxStackArgCount = 8; + facebook::jsi::Value stackArgs[maxStackArgCount]; + std::unique_ptr heapArgs; + facebook::jsi::Value *args; + + // Accounting for 'this' object at 0 + unsigned short argumentCount = argumentCountIncThis - 1; + + if (argumentCount > maxStackArgCount) { + heapArgs = std::make_unique(argumentCount); + + for (size_t i = 1; i < argumentCountIncThis; i++) { + heapArgs[i - 1] = hostFuncProxy.GetRuntime().ToJsiValue( + ChakraObjectRef(argumentsIncThis[i])); + } + args = heapArgs.get(); + + } else { + for (size_t i = 1; i < argumentCountIncThis; i++) { + stackArgs[i - 1] = hostFuncProxy.GetRuntime().ToJsiValue( + ChakraObjectRef(argumentsIncThis[i])); + } + args = stackArgs; + } + + JsValueRef res{JS_INVALID_REFERENCE}; + facebook::jsi::Value thisVal = hostFuncProxy.GetRuntime().ToJsiValue( + ChakraObjectRef(argumentsIncThis[0])); + + try { + facebook::jsi::Value retVal = hostFuncProxy.GetHostFunction()( + hostFuncProxy.GetRuntime(), thisVal, args, argumentCount); + res = hostFuncProxy.GetRuntime().ToChakraObjectRef(retVal); + + } catch (const facebook::jsi::JSError &error) { + JsSetException(hostFuncProxy.GetRuntime().ToChakraObjectRef(error.value())); + + } catch (const std::exception &exn) { + std::string message = "Exception in HostFunction: "; + message += exn.what(); + ChakraObjectRef messageRef = + ToJsString(std::string_view{message.c_str(), message.length()}); + + JsValueRef error = nullptr; + hostFuncProxy.GetRuntime().VerifyJsErrorElseThrow( + JsCreateError(messageRef, &error)); + + std::string otherMessage = + ToStdString(hostFuncProxy.GetRuntime().ToChakraObjectRef( + hostFuncProxy.GetRuntime().GetProperty( + hostFuncProxy.GetRuntime().MakePointer( + error), + "message"))); + + JsSetException(error); + + } catch (...) { + std::string exceptionString("Exception in HostFunction: "); + JsValueRef exn; + exn = ToJsString( + std::string_view{exceptionString.c_str(), exceptionString.size()}); + JsSetException(exn); + } + + return res; +} + // clang-format off template /* static */ facebook::jsi::Object @@ -758,33 +907,6 @@ T *ChakraRuntime::ObjectWithExternalData::getExternalData() { return externalData; } -void ChakraRuntime::VerifyJsErrorElseThrow(JsErrorCode error) { - switch (error) { - case JsNoError: { - return; - break; - } - - case JsErrorScriptException: { - JsValueRef jsError; - VerifyChakraErrorElseThrow(JsGetAndClearException(&jsError)); - throw facebook::jsi::JSError( - "A JavaScript Error was thrown.", - *this, - ToJsiValue(ChakraObjectRef(jsError))); - break; - } - - default: { - VerifyChakraErrorElseThrow(error); - break; - } - } // switch (error) - - // Control flow should never reach here. - std::terminate(); -} - facebook::jsi::Object ChakraRuntime::createProxy( facebook::jsi::Object &&target, facebook::jsi::Object &&handler) noexcept { @@ -833,11 +955,11 @@ facebook::jsi::Object ChakraRuntime::createHostObjectProxyHandler() noexcept { facebook::jsi::Object targetObj = args[0].getObject(*this); facebook::jsi::String propStr = args[1].getString(*this); - if (propStr.utf8(rt) == s_proxyGetHostObjectTargetPropName) { + if (propStr.utf8(rt) == g_proxyGetHostObjectTargetPropName) { return targetObj; } - if (propStr.utf8(rt) == s_proxyIsHostObjectPropName) { + if (propStr.utf8(rt) == g_proxyIsHostObjectPropName) { return true; } @@ -945,70 +1067,6 @@ void ChakraRuntime::setupMemoryTracker() noexcept { } } -JsValueRef CALLBACK ChakraRuntime::HostFunctionCall( - JsValueRef callee, - bool isConstructCall, - JsValueRef *argumentsIncThis, - unsigned short argumentCountIncThis, - void *callbackState) { - const HostFunctionProxy &hostFuncProxy = - *reinterpret_cast(callbackState); - - const unsigned maxStackArgCount = 8; - facebook::jsi::Value stackArgs[maxStackArgCount]; - std::unique_ptr heapArgs; - facebook::jsi::Value *args; - - // Accounting for 'this' object at 0 - unsigned short argumentCount = argumentCountIncThis - 1; - - if (argumentCount > maxStackArgCount) { - heapArgs = std::make_unique(argumentCount); - - for (size_t i = 1; i < argumentCountIncThis; i++) { - heapArgs[i - 1] = hostFuncProxy.getRuntime().ToJsiValue( - ChakraObjectRef(argumentsIncThis[i])); - } - args = heapArgs.get(); - - } else { - for (size_t i = 1; i < argumentCountIncThis; i++) { - stackArgs[i - 1] = hostFuncProxy.getRuntime().ToJsiValue( - ChakraObjectRef(argumentsIncThis[i])); - } - args = stackArgs; - } - - JsValueRef res{JS_INVALID_REFERENCE}; - facebook::jsi::Value thisVal( - hostFuncProxy.getRuntime().MakePointer( - argumentsIncThis[0])); - - try { - facebook::jsi::Value retVal = hostFuncProxy.getHostFunction()( - hostFuncProxy.getRuntime(), thisVal, args, argumentCount); - res = hostFuncProxy.getRuntime().ToChakraObjectRef(retVal); - - } catch (const facebook::jsi::JSError &error) { - JsSetException(hostFuncProxy.getRuntime().ToChakraObjectRef(error.value())); - - } catch (const std::exception &ex) { - std::string exwhat(ex.what()); - JsValueRef exn; - exn = ToJsString(std::string_view{exwhat.c_str(), exwhat.size()}); - JsSetException(exn); - - } catch (...) { - std::string exceptionString("Exception in HostFunction: "); - JsValueRef exn; - exn = ToJsString( - std::string_view{exceptionString.c_str(), exceptionString.size()}); - JsSetException(exn); - } - - return res; -} - /*static*/ std::once_flag ChakraRuntime::s_runtimeVersionInitFlag; /*static*/ uint64_t ChakraRuntime::s_runtimeVersion = 0; diff --git a/vnext/JSI/Shared/ChakraRuntime.h b/vnext/JSI/Shared/ChakraRuntime.h index 33d7a471b97..7dba282f016 100644 --- a/vnext/JSI/Shared/ChakraRuntime.h +++ b/vnext/JSI/Shared/ChakraRuntime.h @@ -187,20 +187,25 @@ class ChakraRuntime : public facebook::jsi::Runtime { #pragma endregion Functions_inherited_from_Runtime - public: - // These three functions only performs shallow copies. - facebook::jsi::Value ToJsiValue(ChakraObjectRef &&ref); - ChakraObjectRef ToChakraObjectRef(const facebook::jsi::Value &value); - std::vector ToChakraObjectRefs( - const facebook::jsi::Value *value, - size_t count); - protected: ChakraRuntimeArgs &runtimeArgs() { return m_args; } private: + // Since the function + // Object::getProperty(Runtime& runtime, const char* name) + // causes mulitple copies of name, we do not want to use it when implementing + // ChakraRuntime methods. This function does the same thing as + // Object::getProperty, but without the extra overhead. This function is + // declared as const so that it can be used when implementing + // isHostFunction and isHostObject. + facebook::jsi::Value GetProperty( + const facebook::jsi::Object &obj, + const char *const name) const; + + void VerifyJsErrorElseThrow(JsErrorCode error); + // ChakraPointerValue is needed for working with Facebook's jsi::Pointer class // and must only be used for this purpose. Every instance of // ChakraPointerValue should be allocated on the heap and be used as an @@ -287,6 +292,44 @@ class ChakraRuntime : public facebook::jsi::Runtime { ->GetRef(); } + // These three functions only performs shallow copies. + facebook::jsi::Value ToJsiValue(ChakraObjectRef &&ref); + ChakraObjectRef ToChakraObjectRef(const facebook::jsi::Value &value); + std::vector ToChakraObjectRefs( + const facebook::jsi::Value *value, + size_t count); + + static JsValueRef CALLBACK HostFunctionCall( + JsValueRef callee, + bool isConstructCall, + JsValueRef *argumentsIncThis, + unsigned short argumentCountIncThis, + void *callbackState); + + template + class ObjectWithExternalData : public facebook::jsi::Object { + public: + static facebook::jsi::Object create( + ChakraRuntime &runtime, + std::unique_ptr &&externalData); + + static ObjectWithExternalData fromExisting( + ChakraRuntime &runtime, + facebook::jsi::Object &&obj); + + public: + T *getExternalData(); + ObjectWithExternalData(const Runtime::PointerValue *value) + : Object(const_cast(value)) { + } // TODO :: const_cast + + ObjectWithExternalData(ObjectWithExternalData &&other) = default; + ObjectWithExternalData &operator=(ObjectWithExternalData &&other) = default; + }; + + template + friend class ObjectWithExternalData; + class HostObjectProxy { public: facebook::jsi::Value Get(const facebook::jsi::PropNameID &propNameId) { @@ -316,32 +359,6 @@ class ChakraRuntime : public facebook::jsi::Runtime { std::shared_ptr m_hostObject; }; - template - class ObjectWithExternalData : public facebook::jsi::Object { - public: - static facebook::jsi::Object create( - ChakraRuntime &runtime, - std::unique_ptr &&externalData); - - static ObjectWithExternalData fromExisting( - ChakraRuntime &runtime, - facebook::jsi::Object &&obj); - - public: - T *getExternalData(); - ObjectWithExternalData(const Runtime::PointerValue *value) - : Object(const_cast(value)) { - } // TODO :: const_cast - - ObjectWithExternalData(ObjectWithExternalData &&other) = default; - ObjectWithExternalData &operator=(ObjectWithExternalData &&other) = default; - }; - - template - friend class ObjectWithExternalData; - - void VerifyJsErrorElseThrow(JsErrorCode error); - facebook::jsi::Object createProxy( facebook::jsi::Object &&target, facebook::jsi::Object &&handler) noexcept; @@ -398,12 +415,6 @@ class ChakraRuntime : public facebook::jsi::Runtime { const facebook::jsi::Buffer &scriptBuffer, const facebook::jsi::Buffer &serializedScriptBuffer, const std::string &sourceURL); - static JsValueRef CALLBACK HostFunctionCall( - JsValueRef callee, - bool isConstructCall, - JsValueRef *argumentsIncThis, - unsigned short argumentCountIncThis, - void *callbackState); static std::once_flag s_runtimeVersionInitFlag; static uint64_t s_runtimeVersion; @@ -430,11 +441,6 @@ class ChakraRuntime : public facebook::jsi::Runtime { std::vector> m_pinnedPreparedScripts; - static constexpr const char *const s_proxyGetHostObjectTargetPropName = - "$$ProxyGetHostObjectTarget$$"; - static constexpr const char *const s_proxyIsHostObjectPropName = - "$$ProxyIsHostObject$$"; - std::string m_debugRuntimeName; int m_debugPort{0}; std::unique_ptr m_debugProtocolHandler; From ae76b5b3b18d8d1c84ffbad976e7a16d0c27f61f Mon Sep 17 00:00:00 2001 From: Yichen Yao Date: Wed, 23 Oct 2019 18:06:49 -0700 Subject: [PATCH 02/12] Refactor Host Object implementation for better readability. --- .../JsiRuntimeUnitTests.cpp | 6 +- vnext/JSI/Shared/ChakraObjectRef.cpp | 6 + vnext/JSI/Shared/ChakraObjectRef.h | 45 ++- vnext/JSI/Shared/ChakraRuntime.cpp | 366 +++++++++--------- vnext/JSI/Shared/ChakraRuntime.h | 73 +--- 5 files changed, 245 insertions(+), 251 deletions(-) diff --git a/vnext/JSI.Desktop.UnitTests/JsiRuntimeUnitTests.cpp b/vnext/JSI.Desktop.UnitTests/JsiRuntimeUnitTests.cpp index eed8f10eb51..458739f6b9e 100644 --- a/vnext/JSI.Desktop.UnitTests/JsiRuntimeUnitTests.cpp +++ b/vnext/JSI.Desktop.UnitTests/JsiRuntimeUnitTests.cpp @@ -328,7 +328,7 @@ TEST_P(JsiRuntimeUnitTests, HostObjectTest) { } catch (const JSError &ex) { exc = ex.what(); } - //EXPECT_NE(exc.find("Cannot get"), std::string::npos); + EXPECT_NE(exc.find("Cannot get"), std::string::npos); //exc = ""; //try { // function("function (obj) { obj.thing = 'hello'; }").call(rt, thro); @@ -597,7 +597,6 @@ TEST_P(JsiRuntimeUnitTests, InstanceOfTest) { .instanceOf(rt, ctor)); } -// TODO (yicyao): Fix this test. TEST_P(JsiRuntimeUnitTests, HostFunctionTest) { auto one = std::make_shared(1); Function plusOne = Function::createFromHostFunction( @@ -969,9 +968,8 @@ TEST_P(JsiRuntimeUnitTests, ScopeDoesNotCrashWhenValueEscapes) { EXPECT_EQ(v.getObject(rt).getProperty(rt, "a").getNumber(), 5); } -// TODO (yicyao): Enable this test. // Verifies you can have a host object that emulates a normal object -TEST_P(JsiRuntimeUnitTests, DISABLED_HostObjectWithValueMembers) { +TEST_P(JsiRuntimeUnitTests, HostObjectWithValueMembers) { class Bag : public HostObject { public: Bag() = default; diff --git a/vnext/JSI/Shared/ChakraObjectRef.cpp b/vnext/JSI/Shared/ChakraObjectRef.cpp index 1772905dd41..358a1358e63 100644 --- a/vnext/JSI/Shared/ChakraObjectRef.cpp +++ b/vnext/JSI/Shared/ChakraObjectRef.cpp @@ -319,4 +319,10 @@ bool CompareJsPropertyIds( std::terminate(); } +void ThrowJsException(const std::string_view &message) { + JsValueRef error = nullptr; + VerifyChakraErrorElseThrow(JsCreateError(ToJsString(message), &error)); + VerifyChakraErrorElseThrow(JsSetException(error)); +} + } // namespace Microsoft::JSI diff --git a/vnext/JSI/Shared/ChakraObjectRef.h b/vnext/JSI/Shared/ChakraObjectRef.h index 874c68beb5c..65f0f20d963 100644 --- a/vnext/JSI/Shared/ChakraObjectRef.h +++ b/vnext/JSI/Shared/ChakraObjectRef.h @@ -160,6 +160,15 @@ ChakraObjectRef ToJsString(const ChakraObjectRef &jsValue); */ ChakraObjectRef ToJsNumber(int num); +/** + * @returns A ChakraObjectRef managing a JS ArrayBuffer. + * + * @remarks The returned ArrayBuffer is backed by buffer and keeps buffer alive + * till the garbage collector finalizes it. + */ +ChakraObjectRef ToJsArrayBuffer( + const std::shared_ptr &buffer); + /** * @returns A ChakraObjectRef managing a JS Object. * @@ -167,37 +176,44 @@ ChakraObjectRef ToJsNumber(int num); * garbage collector finalizes it. */ template -ChakraObjectRef ToJsObject(std::unique_ptr &&data) { +ChakraObjectRef ToJsObject(const std::shared_ptr &data) { if (!data) { throw facebook::jsi::JSINativeException( "Cannot create an external JS Object without backing data."); } JsValueRef obj = nullptr; + auto dataWrapper = std::make_unique>(data); + + // We allocate a copy of data on the heap, a shared_ptr which is deleted when + // the JavaScript garbage collecotr releases the created external Object. This + // ensures that data stays alive while the JavaScript engine is using it. VerifyChakraErrorElseThrow(JsCreateExternalObject( - data.get(), + dataWrapper.get(), [](void *dataToDestroy) { // We wrap dataToDestroy in a unique_ptr to avoid calling delete // explicitly. - std::unique_ptr wrapper{static_cast(dataToDestroy)}; + std::unique_ptr> wrapper{ + static_cast *>(dataToDestroy)}; }, &obj)); - // We only call data.release() after JsCreateExternalObject succeeds. + // We only call dataWrapper.release() after JsCreateExternalObject succeeds. // Otherwise, when JsCreateExternalObject fails and an exception is thrown, - // the memory that data used to own will be leaked. - data.release(); + // the memory that dataWrapper used to own will be leaked. + dataWrapper.release(); return ChakraObjectRef(obj); } /** - * @returns A ChakraObjectRef managing a JS ArrayBuffer. - * - * @remarks The returned ArrayBuffer is backed by buffer and keeps buffer alive - * till the garbage collector finalizes it. + * TODO (yicyao): Add documentation here. */ -ChakraObjectRef ToJsArrayBuffer( - const std::shared_ptr &buffer); +template +T *GetExternalData(const ChakraObjectRef &object) { + void *data; + VerifyChakraErrorElseThrow(JsGetExternalData(object, &data)); + return static_cast(data); +} /** * @param jsValue1 A ChakraObjectRef managing a JsValueRef. @@ -221,4 +237,9 @@ bool CompareJsPropertyIds( const ChakraObjectRef &jsPropId1, const ChakraObjectRef &jsPropId2); +/** + * TODO (yicyao): Add documentation here. + */ +void ThrowJsException(const std::string_view &message); + } // namespace Microsoft::JSI diff --git a/vnext/JSI/Shared/ChakraRuntime.cpp b/vnext/JSI/Shared/ChakraRuntime.cpp index 47982713b5f..1b6285fe4b0 100644 --- a/vnext/JSI/Shared/ChakraRuntime.cpp +++ b/vnext/JSI/Shared/ChakraRuntime.cpp @@ -21,8 +21,6 @@ namespace { constexpr const char *const g_functionIsHostFunctionPropName = "$$FunctionIsHostFunction$$"; -constexpr const char *const g_functionGetHostFunctionTargetPropName = - "$$FunctionGetHostFunctionTarget$$"; constexpr const char *const g_proxyIsHostObjectPropName = "$$ProxyIsHostObject$$"; @@ -299,32 +297,45 @@ facebook::jsi::Object ChakraRuntime::createObject() { facebook::jsi::Object ChakraRuntime::createObject( std::shared_ptr hostObject) { - facebook::jsi::Object proxyTarget = - ObjectWithExternalData::create( - *this, std::make_unique(*this, hostObject)); - return createProxy(std::move(proxyTarget), createHostObjectProxyHandler()); + constexpr const char *const jsProxyConstructorWrapperSource = + "(function()\n" + "{\n" + " return function(target, handler)\n" + " {\n" + " return new Proxy(target, handler)\n" + " }\n" + "})()"; + + static facebook::jsi::StringBuffer jsProxyConstructorWrapperSourceBuffer{ + jsProxyConstructorWrapperSource}; + + facebook::jsi::Function jsProxyConstructorWrapper = + evaluateJavaScriptSimple( + jsProxyConstructorWrapperSourceBuffer, + "ChakraRuntime_createObject.js") + .asObject(*this) + .asFunction(*this); + + return jsProxyConstructorWrapper + .call( + *this, + MakePointer(ToJsObject(hostObject)), + createHostObjectProxyHandler()) + .asObject(*this); } std::shared_ptr ChakraRuntime::getHostObject( const facebook::jsi::Object &obj) { - if (!isHostObject(obj)) - return nullptr; - - facebook::jsi::Value value = - GetProperty(obj, g_proxyGetHostObjectTargetPropName); - if (!value.isObject()) - std::terminate(); - facebook::jsi::Object valueObj = - value.getObject(const_cast(*this)); + if (!isHostObject(obj)) { + throw facebook::jsi::JSINativeException( + "getHostObject() can only be called with HostObjects."); + } - ObjectWithExternalData extObject = - ObjectWithExternalData::fromExisting( - *this, std::move(valueObj)); - HostObjectProxy *externalData = extObject.getExternalData(); + facebook::jsi::Object target = + obj.getPropertyAsObject(*this, g_proxyGetHostObjectTargetPropName); - if (!externalData) - std::terminate(); - return externalData->getHostObject(); + return *GetExternalData>( + GetChakraObjectRef(target)); } facebook::jsi::HostFunctionType &ChakraRuntime::getHostFunction( @@ -437,7 +448,8 @@ facebook::jsi::Array ChakraRuntime::getPropertyNames( jsGetPropertyNamesSource}; facebook::jsi::Function jsGetPropertyNames = - evaluateJavaScriptSimple(jsGetPropertyNamesSourceBuffer, "") + evaluateJavaScriptSimple( + jsGetPropertyNamesSourceBuffer, "ChakraRuntime_getPropertyNames.js") .asObject(*this) .asFunction(*this); @@ -605,7 +617,8 @@ facebook::jsi::Value ChakraRuntime::callAsConstructor( size_t count) { // We must store these ChakraObjectRefs on the stack to make sure that they do // not go out of scope when JsConstructObject is called. - ChakraObjectRef undefinedRef = ToChakraObjectRef(facebook::jsi::Value()); + ChakraObjectRef undefinedRef = + ToChakraObjectRef(facebook::jsi::Value::undefined()); std::vector argRefs = ToChakraObjectRefs(args, count); std::vector argsWithThis = @@ -703,11 +716,11 @@ facebook::jsi::Value ChakraRuntime::ToJsiValue(ChakraObjectRef &&ref) { switch (type) { case JsUndefined: { - return facebook::jsi::Value(); + return facebook::jsi::Value::undefined(); break; } case JsNull: { - return facebook::jsi::Value(nullptr); + return facebook::jsi::Value::null(); break; } case JsNumber: { @@ -853,182 +866,177 @@ JsValueRef CALLBACK ChakraRuntime::HostFunctionCall( } catch (const std::exception &exn) { std::string message = "Exception in HostFunction: "; message += exn.what(); - ChakraObjectRef messageRef = - ToJsString(std::string_view{message.c_str(), message.length()}); - - JsValueRef error = nullptr; - hostFuncProxy.GetRuntime().VerifyJsErrorElseThrow( - JsCreateError(messageRef, &error)); - - std::string otherMessage = - ToStdString(hostFuncProxy.GetRuntime().ToChakraObjectRef( - hostFuncProxy.GetRuntime().GetProperty( - hostFuncProxy.GetRuntime().MakePointer( - error), - "message"))); - - JsSetException(error); + ThrowJsException(message); } catch (...) { - std::string exceptionString("Exception in HostFunction: "); - JsValueRef exn; - exn = ToJsString( - std::string_view{exceptionString.c_str(), exceptionString.size()}); - JsSetException(exn); + ThrowJsException("Exception in HostFunction: "); } return res; } -// clang-format off -template -/* static */ facebook::jsi::Object -ChakraRuntime::ObjectWithExternalData::create( - ChakraRuntime &runtime, - std::unique_ptr &&externalData) { - return runtime.MakePointer>( - ToJsObject(std::move(externalData))); -} +facebook::jsi::Value ChakraRuntime::HostObjectGetTrap( + Runtime &runtime, + const facebook::jsi::Value & /*thisVal*/, + const facebook::jsi::Value *args, + size_t count) { + // thisVal should always be bound to the hander returned by + // createHostObjectProxyHandler() and is unused. + + // Three parameters are passed to the get() Proxy hander: + // arg[0] is the target object. + // arg[1] is the name of the property to get. (We do not support property + // Symbols at this point.) + // arg[2] is either the Proxy or an object that inherits from the proxy. + // We don't use this parameter. + if (count != 3) { + throw facebook::jsi::JSINativeException( + "The get() Proxy handler requires three arguments."); + } -template -/* static */ ChakraRuntime::ObjectWithExternalData -ChakraRuntime::ObjectWithExternalData::fromExisting( - ChakraRuntime &runtime, - facebook::jsi::Object &&obj) { - return ObjectWithExternalData(runtime.cloneObject(getPointerValue(obj))); -} -// clang-format on + ChakraRuntime &chakraRuntime = static_cast(runtime); + ChakraObjectRef target = chakraRuntime.ToChakraObjectRef(args[0]); + std::string propName = ToStdString(chakraRuntime.ToChakraObjectRef(args[1])); + + if (propName == g_proxyIsHostObjectPropName) { + return facebook::jsi::Value{true}; -template -T *ChakraRuntime::ObjectWithExternalData::getExternalData() { - T *externalData; - VerifyChakraErrorElseThrow(JsGetExternalData( - GetChakraObjectRef(*this), reinterpret_cast(&externalData))); - return externalData; + } else if (propName == g_proxyGetHostObjectTargetPropName) { + // We need to make a copy of target here for the returned jsi::Value + return chakraRuntime.ToJsiValue(std::move(target)); + + } else { + std::shared_ptr &hostObject = + *GetExternalData>(target); + return hostObject->get( + chakraRuntime, + chakraRuntime.createPropNameIDFromAscii( + propName.c_str(), propName.length())); + } + + // Control flow should never reach here. + std::terminate(); } -facebook::jsi::Object ChakraRuntime::createProxy( - facebook::jsi::Object &&target, - facebook::jsi::Object &&handler) noexcept { - // TODO :: Avoid creating the constuctor on each call. - facebook::jsi::Function proxyConstructor = createProxyConstructor(); - facebook::jsi::Value hostObjectProxy = - proxyConstructor.call(*this, target, handler); +facebook::jsi::Value ChakraRuntime::HostObjectSetTrap( + Runtime &runtime, + const facebook::jsi::Value & /*thisVal*/, + const facebook::jsi::Value *args, + size_t count) { + // thisVal should always be bound to the hander returned by + // createHostObjectProxyHandler() and is unused. + + // Four parameters are passed to the set() Proxy hander: + // arg[0] is the target object. + // arg[1] is the name of the property to set. (We do not support property + // Symbols at this point.) + // arg[2] is the new value of the property to set. + // arg[3] is the object to which the assignment was originally directed. + // We don't use this parameter. + if (count != 4) { + throw facebook::jsi::JSINativeException( + "The set() Proxy handler requires four arguments."); + } - if (!hostObjectProxy.isObject()) - std::terminate(); + ChakraRuntime &chakraRuntime = static_cast(runtime); + std::string propName = ToStdString(chakraRuntime.ToChakraObjectRef(args[1])); + + if (propName == g_proxyIsHostObjectPropName) { + throw facebook::jsi::JSINativeException( + std ::string{g_proxyIsHostObjectPropName} + + " is a reserved property and must not be changed."); + + } else if (propName == g_proxyGetHostObjectTargetPropName) { + throw facebook::jsi::JSINativeException( + std ::string{g_proxyGetHostObjectTargetPropName} + + " is a reserved property and must not be changed."); + + } else { + ChakraObjectRef target = chakraRuntime.ToChakraObjectRef(args[0]); + + std::shared_ptr &hostObject = + *GetExternalData>(target); - return hostObjectProxy.getObject(*this); + hostObject->set( + chakraRuntime, + chakraRuntime.createPropNameIDFromAscii( + propName.c_str(), propName.length()), + args[2]); + + return facebook::jsi::Value::undefined(); + } + + // Control flow should never reach here. + std::terminate(); } -facebook::jsi::Function ChakraRuntime::createProxyConstructor() noexcept { - auto buffer = std::make_unique( - "var ctr=function(target, handler) { return new Proxy(target, handler);};ctr;"); - facebook::jsi::Value hostObjectProxyConstructor = - evaluateJavaScriptSimple(*buffer, "proxy_constructor.js"); +facebook::jsi::Value ChakraRuntime::HostObjectOwnKeysTrap( + Runtime &runtime, + const facebook::jsi::Value & /*thisVal*/, + const facebook::jsi::Value *args, + size_t count) { + // thisVal should always be bound to the hander returned by + // createHostObjectProxyHandler() and is unused. + + if (count != 1) { + throw facebook::jsi::JSINativeException( + "HostObjectOwnKeysTrap() requires one argument."); + } - if (!hostObjectProxyConstructor.isObject() || - !hostObjectProxyConstructor.getObject(*this).isFunction(*this)) - std::terminate(); + ChakraRuntime &chakraRuntime = static_cast(runtime); + ChakraObjectRef target = chakraRuntime.ToChakraObjectRef(args[0]); - return hostObjectProxyConstructor.getObject(*this).getFunction(*this); + std::shared_ptr &hostObject = + *GetExternalData>(target); + + auto ownKeys = hostObject->getPropertyNames(chakraRuntime); + size_t numKeys = ownKeys.size(); + + facebook::jsi::Array result = chakraRuntime.createArray(numKeys); + + for (size_t i = 0; i < numKeys; ++i) { + std::string keyStr = ownKeys[i].utf8(chakraRuntime); + result.setValueAtIndex( + chakraRuntime, + i, + facebook::jsi::String::createFromUtf8(chakraRuntime, keyStr)); + } + + return result; } facebook::jsi::Object ChakraRuntime::createHostObjectProxyHandler() noexcept { - // TODO :: This object can be cached and reused for multiple host objects. + // TODO (yicyao): handler can be cached and reused for multiple HostObjects. + + facebook::jsi::Object handler = createObject(); + + constexpr const char *const getPropName = "get"; + constexpr const char *const setPropName = "set"; + constexpr const char *const ownKeysPropName = "ownKeys"; - facebook::jsi::Object handlerObj = createObject(); - std::string getPropName("get"), setPropName("set"), - enumeratePropName("enumerate"); + facebook::jsi::PropNameID getPropId = + createPropNameIDFromAscii(getPropName, strlen(getPropName)); + facebook::jsi::PropNameID setPropId = + createPropNameIDFromAscii(setPropName, strlen(setPropName)); + facebook::jsi::PropNameID ownKeysPropId = + createPropNameIDFromAscii(ownKeysPropName, strlen(ownKeysPropName)); - handlerObj.setProperty( + handler.setProperty( *this, - getPropName.c_str(), - createFunctionFromHostFunction( - createPropNameIDFromAscii(getPropName.c_str(), getPropName.size()), - 2, - [this]( - Runtime &rt, - const facebook::jsi::Value &thisVal, - const facebook::jsi::Value *args, - size_t count) -> facebook::jsi::Value { - facebook::jsi::Object targetObj = args[0].getObject(*this); - facebook::jsi::String propStr = args[1].getString(*this); - - if (propStr.utf8(rt) == g_proxyGetHostObjectTargetPropName) { - return targetObj; - } - - if (propStr.utf8(rt) == g_proxyIsHostObjectPropName) { - return true; - } - - ObjectWithExternalData extObject = - ObjectWithExternalData::fromExisting( - *this, std::move(targetObj)); - HostObjectProxy *externalData = extObject.getExternalData(); - return externalData->Get( - facebook::jsi::PropNameID::forString(*this, propStr)); - })); - - handlerObj.setProperty( + getPropName, + createFunctionFromHostFunction(getPropId, 2, HostObjectGetTrap)); + + handler.setProperty( *this, - setPropName.c_str(), - createFunctionFromHostFunction( - createPropNameIDFromAscii(setPropName.c_str(), setPropName.size()), - 3, - [this]( - Runtime &rt, - const facebook::jsi::Value &thisVal, - const facebook::jsi::Value *args, - size_t count) -> facebook::jsi::Value { - facebook::jsi::Object targetObj = args[0].getObject(*this); - facebook::jsi::String propStr = args[1].getString(*this); - const facebook::jsi::Value &propVal = args[2]; - - ObjectWithExternalData extObject = - ObjectWithExternalData::fromExisting( - *this, std::move(targetObj)); - HostObjectProxy *externalData = extObject.getExternalData(); - externalData->Set( - facebook::jsi::PropNameID::forString(*this, propStr), propVal); - return facebook::jsi::Value::undefined(); - })); - - handlerObj.setProperty( + setPropName, + createFunctionFromHostFunction(setPropId, 3, HostObjectSetTrap)); + + handler.setProperty( *this, - enumeratePropName.c_str(), - createFunctionFromHostFunction( - createPropNameIDFromAscii( - enumeratePropName.c_str(), enumeratePropName.size()), - 1, - [this]( - Runtime &rt, - const facebook::jsi::Value &thisVal, - const facebook::jsi::Value *args, - size_t count) -> facebook::jsi::Value { - facebook::jsi::Object targetObj = args[0].getObject(*this); - - ObjectWithExternalData extObject = - ObjectWithExternalData::fromExisting( - *this, std::move(targetObj)); - HostObjectProxy *externalData = extObject.getExternalData(); - auto keys = externalData->Enumerator(); - - auto result = createArray(keys.size()); - - for (size_t i = 0; i < count; i++) { - std::string keyStr = keys[i].utf8(*this); - result.setValueAtIndex( - *this, - i, - facebook::jsi::String::createFromUtf8(*this, keyStr)); - } - - return result; - })); - - return handlerObj; + ownKeysPropName, + createFunctionFromHostFunction(ownKeysPropId, 1, HostObjectOwnKeysTrap)); + + return handler; } void ChakraRuntime::setupMemoryTracker() noexcept { @@ -1067,8 +1075,8 @@ void ChakraRuntime::setupMemoryTracker() noexcept { } } -/*static*/ std::once_flag ChakraRuntime::s_runtimeVersionInitFlag; -/*static*/ uint64_t ChakraRuntime::s_runtimeVersion = 0; +std::once_flag ChakraRuntime::s_runtimeVersionInitFlag; +uint64_t ChakraRuntime::s_runtimeVersion = 0; std::unique_ptr makeChakraRuntime( ChakraRuntimeArgs &&args) noexcept { diff --git a/vnext/JSI/Shared/ChakraRuntime.h b/vnext/JSI/Shared/ChakraRuntime.h index 7dba282f016..a339139317a 100644 --- a/vnext/JSI/Shared/ChakraRuntime.h +++ b/vnext/JSI/Shared/ChakraRuntime.h @@ -299,6 +299,7 @@ class ChakraRuntime : public facebook::jsi::Runtime { const facebook::jsi::Value *value, size_t count); + // Host function and host object helpers static JsValueRef CALLBACK HostFunctionCall( JsValueRef callee, bool isConstructCall, @@ -306,63 +307,23 @@ class ChakraRuntime : public facebook::jsi::Runtime { unsigned short argumentCountIncThis, void *callbackState); - template - class ObjectWithExternalData : public facebook::jsi::Object { - public: - static facebook::jsi::Object create( - ChakraRuntime &runtime, - std::unique_ptr &&externalData); - - static ObjectWithExternalData fromExisting( - ChakraRuntime &runtime, - facebook::jsi::Object &&obj); - - public: - T *getExternalData(); - ObjectWithExternalData(const Runtime::PointerValue *value) - : Object(const_cast(value)) { - } // TODO :: const_cast - - ObjectWithExternalData(ObjectWithExternalData &&other) = default; - ObjectWithExternalData &operator=(ObjectWithExternalData &&other) = default; - }; - - template - friend class ObjectWithExternalData; - - class HostObjectProxy { - public: - facebook::jsi::Value Get(const facebook::jsi::PropNameID &propNameId) { - return m_hostObject->get(m_runtime, propNameId); - } - - void Set( - const facebook::jsi::PropNameID &propNameId, - const facebook::jsi::Value &value) { - m_hostObject->set(m_runtime, propNameId, value); - } - - std::vector Enumerator() { - return m_hostObject->getPropertyNames(m_runtime); - } - - HostObjectProxy( - ChakraRuntime &rt, - const std::shared_ptr &hostObject) - : m_runtime(rt), m_hostObject(hostObject) {} - std::shared_ptr getHostObject() { - return m_hostObject; - } - - private: - ChakraRuntime &m_runtime; - std::shared_ptr m_hostObject; - }; + // For the following functions, runtime must be referring to a ChakraRuntime. + static facebook::jsi::Value HostObjectGetTrap( + Runtime &runtime, + const facebook::jsi::Value & /*thisVal*/, + const facebook::jsi::Value *args, + size_t count); + static facebook::jsi::Value HostObjectSetTrap( + Runtime &runtime, + const facebook::jsi::Value & /*thisVal*/, + const facebook::jsi::Value *args, + size_t count); + static facebook::jsi::Value HostObjectOwnKeysTrap( + Runtime &runtime, + const facebook::jsi::Value & /*thisVal*/, + const facebook::jsi::Value *args, + size_t count); - facebook::jsi::Object createProxy( - facebook::jsi::Object &&target, - facebook::jsi::Object &&handler) noexcept; - facebook::jsi::Function createProxyConstructor() noexcept; facebook::jsi::Object createHostObjectProxyHandler() noexcept; // Promise Helpers From 6172b2a532401c30267ab322f438e543de8070ce Mon Sep 17 00:00:00 2001 From: Yichen Yao Date: Wed, 23 Oct 2019 18:16:42 -0700 Subject: [PATCH 03/12] Fix more of HostObjectTest. --- .../JsiRuntimeUnitTests.cpp | 108 +++++++++--------- vnext/JSI/Shared/ChakraRuntime.cpp | 4 +- 2 files changed, 55 insertions(+), 57 deletions(-) diff --git a/vnext/JSI.Desktop.UnitTests/JsiRuntimeUnitTests.cpp b/vnext/JSI.Desktop.UnitTests/JsiRuntimeUnitTests.cpp index 458739f6b9e..1cac89d0b3f 100644 --- a/vnext/JSI.Desktop.UnitTests/JsiRuntimeUnitTests.cpp +++ b/vnext/JSI.Desktop.UnitTests/JsiRuntimeUnitTests.cpp @@ -329,67 +329,67 @@ TEST_P(JsiRuntimeUnitTests, HostObjectTest) { exc = ex.what(); } EXPECT_NE(exc.find("Cannot get"), std::string::npos); - //exc = ""; - //try { - // function("function (obj) { obj.thing = 'hello'; }").call(rt, thro); - //} catch (const JSError &ex) { - // exc = ex.what(); - //} - //EXPECT_NE(exc.find("Cannot set"), std::string::npos); - - //class NopHostObject : public HostObject {}; - //Object nopHo = - // Object::createFromHostObject(rt, std::make_shared()); - //EXPECT_TRUE(nopHo.isHostObject(rt)); - //EXPECT_TRUE(function("function (obj) { return obj.thing; }") - // .call(rt, nopHo) - // .isUndefined()); - - //std::string nopExc; - //try { - // function("function (obj) { obj.thing = 'pika'; }").call(rt, nopHo); - //} catch (const JSError &ex) { - // nopExc = ex.what(); - //} - //EXPECT_NE(nopExc.find("TypeError: "), std::string::npos); - - //class HostObjectWithPropertyNames : public HostObject { - // std::vector getPropertyNames(Runtime &rt) override { - // return PropNameID::names( - // rt, "a_prop", "1", "false", "a_prop", "3", "c_prop"); - // } - //}; + exc = ""; + try { + function("function (obj) { obj.thing = 'hello'; }").call(rt, thro); + } catch (const JSError &ex) { + exc = ex.what(); + } + EXPECT_NE(exc.find("Cannot set"), std::string::npos); + + class NopHostObject : public HostObject {}; + Object nopHo = + Object::createFromHostObject(rt, std::make_shared()); + EXPECT_TRUE(nopHo.isHostObject(rt)); + EXPECT_TRUE(function("function (obj) { return obj.thing; }") + .call(rt, nopHo) + .isUndefined()); - //Object howpn = Object::createFromHostObject( - // rt, std::make_shared()); + std::string nopExc; + try { + function("function (obj) { obj.thing = 'pika'; }").call(rt, nopHo); + } catch (const JSError &ex) { + nopExc = ex.what(); + } + EXPECT_NE(nopExc.find("TypeError: "), std::string::npos); + + class HostObjectWithPropertyNames : public HostObject { + std::vector getPropertyNames(Runtime &rt) override { + return PropNameID::names( + rt, "a_prop", "1", "false", "a_prop", "3", "c_prop"); + } + }; + + Object howpn = Object::createFromHostObject( + rt, std::make_shared()); //EXPECT_TRUE( // function( // "function (o) { return Object.getOwnPropertyNames(o).length == 5 }") // .call(rt, howpn) // .getBool()); - //auto hasOwnPropertyName = function( - // "function (o, p) {" - // " return Object.getOwnPropertyNames(o).indexOf(p) >= 0" - // "}"); - //EXPECT_TRUE( - // hasOwnPropertyName.call(rt, howpn, String::createFromAscii(rt, "a_prop")) - // .getBool()); - //EXPECT_TRUE( - // hasOwnPropertyName.call(rt, howpn, String::createFromAscii(rt, "1")) - // .getBool()); - //EXPECT_TRUE( - // hasOwnPropertyName.call(rt, howpn, String::createFromAscii(rt, "false")) - // .getBool()); - //EXPECT_TRUE( - // hasOwnPropertyName.call(rt, howpn, String::createFromAscii(rt, "3")) - // .getBool()); - //EXPECT_TRUE( - // hasOwnPropertyName.call(rt, howpn, String::createFromAscii(rt, "c_prop")) - // .getBool()); - //EXPECT_FALSE(hasOwnPropertyName - // .call(rt, howpn, String::createFromAscii(rt, "not_existing")) - // .getBool()); + auto hasOwnPropertyName = function( + "function (o, p) {" + " return Object.getOwnPropertyNames(o).indexOf(p) >= 0" + "}"); + EXPECT_TRUE( + hasOwnPropertyName.call(rt, howpn, String::createFromAscii(rt, "a_prop")) + .getBool()); + EXPECT_TRUE( + hasOwnPropertyName.call(rt, howpn, String::createFromAscii(rt, "1")) + .getBool()); + EXPECT_TRUE( + hasOwnPropertyName.call(rt, howpn, String::createFromAscii(rt, "false")) + .getBool()); + EXPECT_TRUE( + hasOwnPropertyName.call(rt, howpn, String::createFromAscii(rt, "3")) + .getBool()); + EXPECT_TRUE( + hasOwnPropertyName.call(rt, howpn, String::createFromAscii(rt, "c_prop")) + .getBool()); + EXPECT_FALSE(hasOwnPropertyName + .call(rt, howpn, String::createFromAscii(rt, "not_existing")) + .getBool()); } TEST_P(JsiRuntimeUnitTests, ArrayTest) { diff --git a/vnext/JSI/Shared/ChakraRuntime.cpp b/vnext/JSI/Shared/ChakraRuntime.cpp index 1b6285fe4b0..e0da0313874 100644 --- a/vnext/JSI/Shared/ChakraRuntime.cpp +++ b/vnext/JSI/Shared/ChakraRuntime.cpp @@ -695,9 +695,7 @@ void ChakraRuntime::VerifyJsErrorElseThrow(JsErrorCode error) { JsValueRef jsError; VerifyChakraErrorElseThrow(JsGetAndClearException(&jsError)); throw facebook::jsi::JSError( - "A JavaScript Error was thrown.", - *this, - ToJsiValue(ChakraObjectRef(jsError))); + "", *this, ToJsiValue(ChakraObjectRef(jsError))); break; } From 440e09927e1cc292a02f59c0edb5f9eafa7d4b60 Mon Sep 17 00:00:00 2001 From: Yichen Yao Date: Thu, 24 Oct 2019 11:41:10 -0700 Subject: [PATCH 04/12] Fix HostObjectTest. --- .../JsiRuntimeUnitTests.cpp | 10 +++++----- vnext/JSI/Shared/ChakraRuntime.cpp | 17 ++++++++++++----- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/vnext/JSI.Desktop.UnitTests/JsiRuntimeUnitTests.cpp b/vnext/JSI.Desktop.UnitTests/JsiRuntimeUnitTests.cpp index 1cac89d0b3f..af653a08493 100644 --- a/vnext/JSI.Desktop.UnitTests/JsiRuntimeUnitTests.cpp +++ b/vnext/JSI.Desktop.UnitTests/JsiRuntimeUnitTests.cpp @@ -362,11 +362,11 @@ TEST_P(JsiRuntimeUnitTests, HostObjectTest) { Object howpn = Object::createFromHostObject( rt, std::make_shared()); - //EXPECT_TRUE( - // function( - // "function (o) { return Object.getOwnPropertyNames(o).length == 5 }") - // .call(rt, howpn) - // .getBool()); + EXPECT_TRUE( + function( + "function (o) { return Object.getOwnPropertyNames(o).length == 5 }") + .call(rt, howpn) + .getBool()); auto hasOwnPropertyName = function( "function (o, p) {" diff --git a/vnext/JSI/Shared/ChakraRuntime.cpp b/vnext/JSI/Shared/ChakraRuntime.cpp index e0da0313874..cb9e6954e12 100644 --- a/vnext/JSI/Shared/ChakraRuntime.cpp +++ b/vnext/JSI/Shared/ChakraRuntime.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include namespace Microsoft::JSI { @@ -988,16 +989,22 @@ facebook::jsi::Value ChakraRuntime::HostObjectOwnKeysTrap( *GetExternalData>(target); auto ownKeys = hostObject->getPropertyNames(chakraRuntime); - size_t numKeys = ownKeys.size(); + std::set dedupedOwnKeys{}; + for (size_t i = 0; i < ownKeys.size(); ++i) { + dedupedOwnKeys.insert(ownKeys[i].utf8(chakraRuntime)); + } + + size_t numKeys = dedupedOwnKeys.size(); facebook::jsi::Array result = chakraRuntime.createArray(numKeys); - for (size_t i = 0; i < numKeys; ++i) { - std::string keyStr = ownKeys[i].utf8(chakraRuntime); + size_t index = 0; + for (const std::string &key : dedupedOwnKeys) { result.setValueAtIndex( chakraRuntime, - i, - facebook::jsi::String::createFromUtf8(chakraRuntime, keyStr)); + index, + facebook::jsi::String::createFromUtf8(chakraRuntime, key)); + ++index; } return result; From 531bdb85f6e522e8b754e1c2d8f97518a4a95aca Mon Sep 17 00:00:00 2001 From: Yichen Yao Date: Thu, 24 Oct 2019 13:42:25 -0700 Subject: [PATCH 05/12] Some code clean up. --- vnext/JSI.Desktop.UnitTests/RunUnitTests.cpp | 1 - vnext/JSI/Shared/ChakraObjectRef.cpp | 20 ++++---- vnext/JSI/Shared/ChakraObjectRef.h | 2 +- vnext/JSI/Shared/ChakraRuntime.cpp | 48 ++++++++++---------- 4 files changed, 36 insertions(+), 35 deletions(-) diff --git a/vnext/JSI.Desktop.UnitTests/RunUnitTests.cpp b/vnext/JSI.Desktop.UnitTests/RunUnitTests.cpp index 32bbd9817f9..b1675814198 100644 --- a/vnext/JSI.Desktop.UnitTests/RunUnitTests.cpp +++ b/vnext/JSI.Desktop.UnitTests/RunUnitTests.cpp @@ -5,7 +5,6 @@ #include int main(int argc, char **argv) { - system("pause"); ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/vnext/JSI/Shared/ChakraObjectRef.cpp b/vnext/JSI/Shared/ChakraObjectRef.cpp index 358a1358e63..75b24a0a28d 100644 --- a/vnext/JSI/Shared/ChakraObjectRef.cpp +++ b/vnext/JSI/Shared/ChakraObjectRef.cpp @@ -77,7 +77,7 @@ void ChakraObjectRef::Invalidate() { } case State::Initialized: { VerifyChakraErrorElseThrow(JsRelease(m_ref, nullptr)); - m_ref = nullptr; + m_ref = JS_INVALID_REFERENCE; m_state = State::Invalidated; break; } @@ -127,7 +127,7 @@ ChakraObjectRef GetPropertySymbol(const ChakraObjectRef &id) { throw facebook::jsi::JSINativeException( "It is llegal to retrieve the symbol associated with a property name."); } - JsValueRef symbol = nullptr; + JsValueRef symbol = JS_INVALID_REFERENCE; VerifyChakraErrorElseThrow(JsGetSymbolFromPropertyId(id, &symbol)); return ChakraObjectRef{symbol}; } @@ -141,7 +141,7 @@ ChakraObjectRef GetPropertyId(const std::string_view &utf8) { // We use a #ifdef here because we can avoid a UTF-8 to UTF-16 conversion // using ChakraCore's JsCreatePropertyId API. #ifdef CHAKRACORE - JsPropertyIdRef id = nullptr; + JsPropertyIdRef id = JS_INVALID_REFERENCE; VerifyChakraErrorElseThrow( JsCreatePropertyId(utf8.data(), utf8.length(), &id)); return ChakraObjectRef(id); @@ -153,7 +153,7 @@ ChakraObjectRef GetPropertyId(const std::string_view &utf8) { } ChakraObjectRef GetPropertyId(const std::wstring &utf16) { - JsPropertyIdRef id = nullptr; + JsPropertyIdRef id = JS_INVALID_REFERENCE; VerifyChakraErrorElseThrow(JsGetPropertyIdFromName(utf16.c_str(), &id)); return ChakraObjectRef(id); } @@ -207,7 +207,7 @@ ChakraObjectRef ToJsString(const std::string_view &utf8) { // We use a #ifdef here because we can avoid a UTF-8 to UTF-16 conversion // using ChakraCore's JsCreateString API. #ifdef CHAKRACORE - JsValueRef result = nullptr; + JsValueRef result = JS_INVALID_REFERENCE; VerifyChakraErrorElseThrow( JsCreateString(utf8.data(), utf8.length(), &result)); return ChakraObjectRef(result); @@ -224,7 +224,7 @@ ChakraObjectRef ToJsString(const std::wstring_view &utf16) { "Cannot convert a nullptr to a JS string."); } - JsValueRef result = nullptr; + JsValueRef result = JS_INVALID_REFERENCE; VerifyChakraErrorElseThrow( JsPointerToString(utf16.data(), utf16.length(), &result)); @@ -232,13 +232,13 @@ ChakraObjectRef ToJsString(const std::wstring_view &utf16) { } ChakraObjectRef ToJsString(const ChakraObjectRef &ref) { - JsValueRef str = nullptr; + JsValueRef str = JS_INVALID_REFERENCE; VerifyChakraErrorElseThrow(JsConvertValueToString(ref, &str)); return ChakraObjectRef(str); } ChakraObjectRef ToJsNumber(int num) { - JsValueRef result = nullptr; + JsValueRef result = JS_INVALID_REFERENCE; VerifyChakraErrorElseThrow(JsIntToNumber(num, &result)); return ChakraObjectRef(result); } @@ -257,7 +257,7 @@ ChakraObjectRef ToJsArrayBuffer( "The external backing buffer for a JS ArrayBuffer is too large."); } - JsValueRef arrayBuffer = nullptr; + JsValueRef arrayBuffer = JS_INVALID_REFERENCE; auto bufferWrapper = std::make_unique>(buffer); @@ -320,7 +320,7 @@ bool CompareJsPropertyIds( } void ThrowJsException(const std::string_view &message) { - JsValueRef error = nullptr; + JsValueRef error = JS_INVALID_REFERENCE; VerifyChakraErrorElseThrow(JsCreateError(ToJsString(message), &error)); VerifyChakraErrorElseThrow(JsSetException(error)); } diff --git a/vnext/JSI/Shared/ChakraObjectRef.h b/vnext/JSI/Shared/ChakraObjectRef.h index 65f0f20d963..89264694a47 100644 --- a/vnext/JSI/Shared/ChakraObjectRef.h +++ b/vnext/JSI/Shared/ChakraObjectRef.h @@ -64,7 +64,7 @@ class ChakraObjectRef { enum class State { Uninitialized, Initialized, Invalidated }; - JsRef m_ref = nullptr; + JsRef m_ref = JS_INVALID_REFERENCE; State m_state = State::Uninitialized; }; diff --git a/vnext/JSI/Shared/ChakraRuntime.cpp b/vnext/JSI/Shared/ChakraRuntime.cpp index cb9e6954e12..560b28d0bb0 100644 --- a/vnext/JSI/Shared/ChakraRuntime.cpp +++ b/vnext/JSI/Shared/ChakraRuntime.cpp @@ -78,7 +78,7 @@ ChakraRuntime::ChakraRuntime(ChakraRuntimeArgs &&args) noexcept setupMemoryTracker(); - JsContextRef context = nullptr; + JsContextRef context = JS_INVALID_REFERENCE; VerifyChakraErrorElseThrow(JsCreateContext(m_runtime, &context)); m_context.Initialize(context); @@ -341,14 +341,14 @@ std::shared_ptr ChakraRuntime::getHostObject( facebook::jsi::HostFunctionType &ChakraRuntime::getHostFunction( const facebook::jsi::Function &obj) { - throw std::runtime_error( + throw facebook::jsi::JSINativeException( "ChakraRuntime::getHostFunction is not implemented."); } facebook::jsi::Value ChakraRuntime::getProperty( const facebook::jsi::Object &obj, const facebook::jsi::PropNameID &name) { - JsValueRef result = nullptr; + JsValueRef result = JS_INVALID_REFERENCE; VerifyJsErrorElseThrow(JsGetProperty( GetChakraObjectRef(obj), GetChakraObjectRef(name), &result)); return ToJsiValue(ChakraObjectRef(result)); @@ -483,7 +483,7 @@ facebook::jsi::Value ChakraRuntime::lockWeakObject( facebook::jsi::Array ChakraRuntime::createArray(size_t length) { assert(length <= UINT_MAX); - JsValueRef result = nullptr; + JsValueRef result = JS_INVALID_REFERENCE; VerifyJsErrorElseThrow( JsCreateArray(static_cast(length), &result)); @@ -519,7 +519,7 @@ facebook::jsi::Value ChakraRuntime::getValueAtIndex( assert(isArray(arr)); assert(index <= INT_MAX); - JsValueRef result = nullptr; + JsValueRef result = JS_INVALID_REFERENCE; VerifyJsErrorElseThrow(JsGetIndexedProperty( GetChakraObjectRef(arr), ToJsNumber(static_cast(index)), &result)); return ToJsiValue(ChakraObjectRef(result)); @@ -822,45 +822,47 @@ JsValueRef CALLBACK ChakraRuntime::HostFunctionCall( JsValueRef *argumentsIncThis, unsigned short argumentCountIncThis, void *callbackState) { - HostFunctionProxy &hostFuncProxy = - *reinterpret_cast(callbackState); + HostFunctionProxy *hostFuncProxy = + static_cast(callbackState); + ChakraRuntime &runtime = hostFuncProxy->GetRuntime(); + const facebook::jsi::HostFunctionType &hostFunc = + hostFuncProxy->GetHostFunction(); - const unsigned maxStackArgCount = 8; + constexpr uint32_t maxStackArgCount = 8; facebook::jsi::Value stackArgs[maxStackArgCount]; - std::unique_ptr heapArgs; - facebook::jsi::Value *args; + std::unique_ptr heapArgs = nullptr; + facebook::jsi::Value *args = nullptr; // Accounting for 'this' object at 0 unsigned short argumentCount = argumentCountIncThis - 1; if (argumentCount > maxStackArgCount) { heapArgs = std::make_unique(argumentCount); - for (size_t i = 1; i < argumentCountIncThis; i++) { - heapArgs[i - 1] = hostFuncProxy.GetRuntime().ToJsiValue( - ChakraObjectRef(argumentsIncThis[i])); + heapArgs[i - 1] = + runtime.ToJsiValue(ChakraObjectRef(argumentsIncThis[i])); } args = heapArgs.get(); } else { for (size_t i = 1; i < argumentCountIncThis; i++) { - stackArgs[i - 1] = hostFuncProxy.GetRuntime().ToJsiValue( - ChakraObjectRef(argumentsIncThis[i])); + stackArgs[i - 1] = + runtime.ToJsiValue(ChakraObjectRef(argumentsIncThis[i])); } args = stackArgs; } - JsValueRef res{JS_INVALID_REFERENCE}; - facebook::jsi::Value thisVal = hostFuncProxy.GetRuntime().ToJsiValue( - ChakraObjectRef(argumentsIncThis[0])); + JsValueRef result = JS_INVALID_REFERENCE; + facebook::jsi::Value thisVal = + runtime.ToJsiValue(ChakraObjectRef(argumentsIncThis[0])); try { - facebook::jsi::Value retVal = hostFuncProxy.GetHostFunction()( - hostFuncProxy.GetRuntime(), thisVal, args, argumentCount); - res = hostFuncProxy.GetRuntime().ToChakraObjectRef(retVal); + result = runtime.ToChakraObjectRef( + hostFunc(runtime, thisVal, args, argumentCount)); } catch (const facebook::jsi::JSError &error) { - JsSetException(hostFuncProxy.GetRuntime().ToChakraObjectRef(error.value())); + runtime.VerifyJsErrorElseThrow( + JsSetException(runtime.ToChakraObjectRef(error.value()))); } catch (const std::exception &exn) { std::string message = "Exception in HostFunction: "; @@ -871,7 +873,7 @@ JsValueRef CALLBACK ChakraRuntime::HostFunctionCall( ThrowJsException("Exception in HostFunction: "); } - return res; + return result; } facebook::jsi::Value ChakraRuntime::HostObjectGetTrap( From 1413e2f189dd7d59511c6220823c37c251b5de89 Mon Sep 17 00:00:00 2001 From: Yichen Yao Date: Thu, 24 Oct 2019 13:50:29 -0700 Subject: [PATCH 06/12] Clang format. --- .../JsiRuntimeUnitTests.cpp | 299 +++++--------- vnext/JSI/Shared/ChakraObjectRef.cpp | 80 ++-- vnext/JSI/Shared/ChakraObjectRef.h | 17 +- vnext/JSI/Shared/ChakraRuntime.cpp | 366 +++++------------- vnext/JSI/Shared/ChakraRuntime.h | 141 ++----- 5 files changed, 266 insertions(+), 637 deletions(-) diff --git a/vnext/JSI.Desktop.UnitTests/JsiRuntimeUnitTests.cpp b/vnext/JSI.Desktop.UnitTests/JsiRuntimeUnitTests.cpp index af653a08493..c7bf7a37aa5 100644 --- a/vnext/JSI.Desktop.UnitTests/JsiRuntimeUnitTests.cpp +++ b/vnext/JSI.Desktop.UnitTests/JsiRuntimeUnitTests.cpp @@ -40,32 +40,23 @@ TEST_P(JsiRuntimeUnitTests, PropNameIDTest) { EXPECT_TRUE(PropNameID::compare(rt, movedQuux, copiedQuux)); EXPECT_TRUE(PropNameID::compare(rt, movedQuux, movedQuux)); - EXPECT_TRUE(PropNameID::compare( - rt, movedQuux, PropNameID::forAscii(rt, std::string("quux2")))); - EXPECT_FALSE(PropNameID::compare( - rt, movedQuux, PropNameID::forAscii(rt, std::string("foo")))); + EXPECT_TRUE(PropNameID::compare(rt, movedQuux, PropNameID::forAscii(rt, std::string("quux2")))); + EXPECT_FALSE(PropNameID::compare(rt, movedQuux, PropNameID::forAscii(rt, std::string("foo")))); uint8_t utf8[] = {0xF0, 0x9F, 0x86, 0x97}; PropNameID utf8PropNameID = PropNameID::forUtf8(rt, utf8, sizeof(utf8)); EXPECT_EQ(utf8PropNameID.utf8(rt), u8"\U0001F197"); - EXPECT_TRUE(PropNameID::compare( - rt, utf8PropNameID, PropNameID::forUtf8(rt, utf8, sizeof(utf8)))); + EXPECT_TRUE(PropNameID::compare(rt, utf8PropNameID, PropNameID::forUtf8(rt, utf8, sizeof(utf8)))); PropNameID nonUtf8PropNameID = PropNameID::forUtf8(rt, "meow"); - EXPECT_TRUE(PropNameID::compare( - rt, nonUtf8PropNameID, PropNameID::forAscii(rt, "meow"))); + EXPECT_TRUE(PropNameID::compare(rt, nonUtf8PropNameID, PropNameID::forAscii(rt, "meow"))); EXPECT_EQ(nonUtf8PropNameID.utf8(rt), "meow"); - PropNameID strPropNameID = - PropNameID::forString(rt, String::createFromAscii(rt, "meow")); + PropNameID strPropNameID = PropNameID::forString(rt, String::createFromAscii(rt, "meow")); EXPECT_TRUE(PropNameID::compare(rt, nonUtf8PropNameID, strPropNameID)); - auto names = PropNameID::names( - rt, "Ala", std::string("ma"), PropNameID::forAscii(rt, "kota")); + auto names = PropNameID::names(rt, "Ala", std::string("ma"), PropNameID::forAscii(rt, "kota")); EXPECT_EQ(names.size(), 3); - EXPECT_TRUE( - PropNameID::compare(rt, names[0], PropNameID::forAscii(rt, "Ala"))); - EXPECT_TRUE( - PropNameID::compare(rt, names[1], PropNameID::forAscii(rt, "ma"))); - EXPECT_TRUE( - PropNameID::compare(rt, names[2], PropNameID::forAscii(rt, "kota"))); + EXPECT_TRUE(PropNameID::compare(rt, names[0], PropNameID::forAscii(rt, "Ala"))); + EXPECT_TRUE(PropNameID::compare(rt, names[1], PropNameID::forAscii(rt, "ma"))); + EXPECT_TRUE(PropNameID::compare(rt, names[2], PropNameID::forAscii(rt, "kota"))); } TEST_P(JsiRuntimeUnitTests, StringTest) { @@ -76,8 +67,7 @@ TEST_P(JsiRuntimeUnitTests, StringTest) { EXPECT_TRUE(checkValue(String::createFromAscii(rt, baz), "'baz'")); uint8_t utf8[] = {0xF0, 0x9F, 0x86, 0x97}; - EXPECT_TRUE(checkValue( - String::createFromUtf8(rt, utf8, sizeof(utf8)), "'\\uD83C\\uDD97'")); + EXPECT_TRUE(checkValue(String::createFromUtf8(rt, utf8, sizeof(utf8)), "'\\uD83C\\uDD97'")); EXPECT_EQ(eval("'quux'").getString(rt).utf8(rt), "quux"); EXPECT_EQ(eval("'\\u20AC'").getString(rt).utf8(rt), "\xe2\x82\xac"); @@ -105,11 +95,7 @@ TEST_P(JsiRuntimeUnitTests, ObjectTest) { EXPECT_EQ(x.getProperty(rt, PropNameID::forAscii(rt, "1")).getNumber(), 2); EXPECT_EQ(x.getProperty(rt, "3").getNumber(), 4); Value five = 5; - EXPECT_EQ( - x.getProperty(rt, PropNameID::forString(rt, five.toString(rt))) - .getString(rt) - .utf8(rt), - "six"); + EXPECT_EQ(x.getProperty(rt, PropNameID::forString(rt, five.toString(rt))).getString(rt).utf8(rt), "six"); x.setProperty(rt, "ten", 11); EXPECT_EQ(x.getPropertyNames(rt).size(rt), 5); @@ -183,11 +169,8 @@ TEST_P(JsiRuntimeUnitTests, HostObjectTest) { void set(Runtime &, const PropNameID &, const Value &) override {} }; - Object cho = - Object::createFromHostObject(rt, std::make_shared()); - EXPECT_TRUE(function("function (obj) { return obj.someRandomProp == 9000; }") - .call(rt, cho) - .getBool()); + Object cho = Object::createFromHostObject(rt, std::make_shared()); + EXPECT_TRUE(function("function (obj) { return obj.someRandomProp == 9000; }").call(rt, cho).getBool()); EXPECT_TRUE(cho.isHostObject(rt)); EXPECT_TRUE(cho.getHostObject(rt).get() != nullptr); @@ -211,8 +194,7 @@ TEST_P(JsiRuntimeUnitTests, HostObjectTest) { Runtime &rt_; }; - Object srho = Object::createFromHostObject( - rt, std::make_shared(rt)); + Object srho = Object::createFromHostObject(rt, std::make_shared(rt)); // Test get's Runtime is as expected function("function (obj) { return obj.isSame; }").call(rt, srho); // ... and set @@ -228,21 +210,12 @@ TEST_P(JsiRuntimeUnitTests, HostObjectTest) { void set(Runtime &, const PropNameID &, const Value &) override {} }; - Object tho = - Object::createFromHostObject(rt, std::make_shared()); - EXPECT_TRUE(function("function (obj) { return obj.abc == 'abcabc'; }") - .call(rt, tho) - .getBool()); - EXPECT_TRUE(function("function (obj) { return obj['def'] == 'defdef'; }") - .call(rt, tho) - .getBool()); - EXPECT_TRUE(function("function (obj) { return obj[12] === '1212'; }") - .call(rt, tho) - .getBool()); + Object tho = Object::createFromHostObject(rt, std::make_shared()); + EXPECT_TRUE(function("function (obj) { return obj.abc == 'abcabc'; }").call(rt, tho).getBool()); + EXPECT_TRUE(function("function (obj) { return obj['def'] == 'defdef'; }").call(rt, tho).getBool()); + EXPECT_TRUE(function("function (obj) { return obj[12] === '1212'; }").call(rt, tho).getBool()); EXPECT_TRUE(tho.isHostObject(rt)); - EXPECT_TRUE( - std::dynamic_pointer_cast(tho.getHostObject(rt)) == - nullptr); + EXPECT_TRUE(std::dynamic_pointer_cast(tho.getHostObject(rt)) == nullptr); EXPECT_TRUE(tho.getHostObject(rt).get() != nullptr); class PropNameIDHostObject : public HostObject { @@ -250,26 +223,18 @@ TEST_P(JsiRuntimeUnitTests, HostObjectTest) { if (PropNameID::compare(rt, sym, PropNameID::forAscii(rt, "undef"))) { return Value::undefined(); } else { - return PropNameID::compare( - rt, sym, PropNameID::forAscii(rt, "somesymbol")); + return PropNameID::compare(rt, sym, PropNameID::forAscii(rt, "somesymbol")); } } void set(Runtime &, const PropNameID &, const Value &) override {} }; - Object sho = Object::createFromHostObject( - rt, std::make_shared()); + Object sho = Object::createFromHostObject(rt, std::make_shared()); EXPECT_TRUE(sho.isHostObject(rt)); - EXPECT_TRUE(function("function (obj) { return obj.undef; }") - .call(rt, sho) - .isUndefined()); - EXPECT_TRUE(function("function (obj) { return obj.somesymbol; }") - .call(rt, sho) - .getBool()); - EXPECT_FALSE(function("function (obj) { return obj.notsomuch; }") - .call(rt, sho) - .getBool()); + EXPECT_TRUE(function("function (obj) { return obj.undef; }").call(rt, sho).isUndefined()); + EXPECT_TRUE(function("function (obj) { return obj.somesymbol; }").call(rt, sho).getBool()); + EXPECT_FALSE(function("function (obj) { return obj.notsomuch; }").call(rt, sho).getBool()); class BagHostObject : public HostObject { public: @@ -298,14 +263,9 @@ TEST_P(JsiRuntimeUnitTests, HostObjectTest) { std::shared_ptr shbho = std::make_shared(); Object bho = Object::createFromHostObject(rt, shbho); EXPECT_TRUE(bho.isHostObject(rt)); - EXPECT_TRUE(function("function (obj) { return obj.undef; }") - .call(rt, bho) - .isUndefined()); + EXPECT_TRUE(function("function (obj) { return obj.undef; }").call(rt, bho).isUndefined()); EXPECT_EQ( - function("function (obj) { obj.thing = 'hello'; return obj.thing; }") - .call(rt, bho) - .toString(rt) - .utf8(rt), + function("function (obj) { obj.thing = 'hello'; return obj.thing; }").call(rt, bho).toString(rt).utf8(rt), "hello"); EXPECT_EQ(shbho->getThing(), "hello"); @@ -319,8 +279,7 @@ TEST_P(JsiRuntimeUnitTests, HostObjectTest) { } }; - Object thro = - Object::createFromHostObject(rt, std::make_shared()); + Object thro = Object::createFromHostObject(rt, std::make_shared()); EXPECT_TRUE(thro.isHostObject(rt)); std::string exc; try { @@ -338,12 +297,9 @@ TEST_P(JsiRuntimeUnitTests, HostObjectTest) { EXPECT_NE(exc.find("Cannot set"), std::string::npos); class NopHostObject : public HostObject {}; - Object nopHo = - Object::createFromHostObject(rt, std::make_shared()); + Object nopHo = Object::createFromHostObject(rt, std::make_shared()); EXPECT_TRUE(nopHo.isHostObject(rt)); - EXPECT_TRUE(function("function (obj) { return obj.thing; }") - .call(rt, nopHo) - .isUndefined()); + EXPECT_TRUE(function("function (obj) { return obj.thing; }").call(rt, nopHo).isUndefined()); std::string nopExc; try { @@ -355,41 +311,23 @@ TEST_P(JsiRuntimeUnitTests, HostObjectTest) { class HostObjectWithPropertyNames : public HostObject { std::vector getPropertyNames(Runtime &rt) override { - return PropNameID::names( - rt, "a_prop", "1", "false", "a_prop", "3", "c_prop"); + return PropNameID::names(rt, "a_prop", "1", "false", "a_prop", "3", "c_prop"); } }; - Object howpn = Object::createFromHostObject( - rt, std::make_shared()); - EXPECT_TRUE( - function( - "function (o) { return Object.getOwnPropertyNames(o).length == 5 }") - .call(rt, howpn) - .getBool()); + Object howpn = Object::createFromHostObject(rt, std::make_shared()); + EXPECT_TRUE(function("function (o) { return Object.getOwnPropertyNames(o).length == 5 }").call(rt, howpn).getBool()); auto hasOwnPropertyName = function( "function (o, p) {" " return Object.getOwnPropertyNames(o).indexOf(p) >= 0" "}"); - EXPECT_TRUE( - hasOwnPropertyName.call(rt, howpn, String::createFromAscii(rt, "a_prop")) - .getBool()); - EXPECT_TRUE( - hasOwnPropertyName.call(rt, howpn, String::createFromAscii(rt, "1")) - .getBool()); - EXPECT_TRUE( - hasOwnPropertyName.call(rt, howpn, String::createFromAscii(rt, "false")) - .getBool()); - EXPECT_TRUE( - hasOwnPropertyName.call(rt, howpn, String::createFromAscii(rt, "3")) - .getBool()); - EXPECT_TRUE( - hasOwnPropertyName.call(rt, howpn, String::createFromAscii(rt, "c_prop")) - .getBool()); - EXPECT_FALSE(hasOwnPropertyName - .call(rt, howpn, String::createFromAscii(rt, "not_existing")) - .getBool()); + EXPECT_TRUE(hasOwnPropertyName.call(rt, howpn, String::createFromAscii(rt, "a_prop")).getBool()); + EXPECT_TRUE(hasOwnPropertyName.call(rt, howpn, String::createFromAscii(rt, "1")).getBool()); + EXPECT_TRUE(hasOwnPropertyName.call(rt, howpn, String::createFromAscii(rt, "false")).getBool()); + EXPECT_TRUE(hasOwnPropertyName.call(rt, howpn, String::createFromAscii(rt, "3")).getBool()); + EXPECT_TRUE(hasOwnPropertyName.call(rt, howpn, String::createFromAscii(rt, "c_prop")).getBool()); + EXPECT_FALSE(hasOwnPropertyName.call(rt, howpn, String::createFromAscii(rt, "not_existing")).getBool()); } TEST_P(JsiRuntimeUnitTests, ArrayTest) { @@ -428,11 +366,7 @@ TEST_P(JsiRuntimeUnitTests, ArrayTest) { seven.setProperty(rt, "1", "modified"); EXPECT_EQ(seven.getProperty(rt, "1").getString(rt).utf8(rt), "modified"); EXPECT_EQ(arr.getValueAtIndex(rt, 1).getString(rt).utf8(rt), "modified"); - EXPECT_EQ( - seven.getProperty(rt, PropNameID::forAscii(rt, "0")) - .getString(rt) - .utf8(rt), - "eight"); + EXPECT_EQ(seven.getProperty(rt, PropNameID::forAscii(rt, "0")).getString(rt).utf8(rt), "eight"); seven.setProperty(rt, PropNameID::forAscii(rt, "0"), "modified2"); EXPECT_EQ(arr.getValueAtIndex(rt, 0).getString(rt).utf8(rt), "modified2"); @@ -447,13 +381,11 @@ TEST_P(JsiRuntimeUnitTests, ArrayTest) { alpha.setValueAtIndex(rt, 3, "d"); EXPECT_EQ(alpha.size(rt), 4); - EXPECT_TRUE( - function( - "function (arr) { return " - "arr.length == 4 && " - "['a','b','c','d'].every(function(v,i) { return v === arr[i]}); }") - .call(rt, alpha) - .getBool()); + EXPECT_TRUE(function("function (arr) { return " + "arr.length == 4 && " + "['a','b','c','d'].every(function(v,i) { return v === arr[i]}); }") + .call(rt, alpha) + .getBool()); Array alpha2 = Array(rt, 1); alpha2 = std::move(alpha); @@ -524,8 +456,7 @@ TEST_P(JsiRuntimeUnitTests, FunctionTest) { } TEST_P(JsiRuntimeUnitTests, FunctionThisTest) { - Function checkPropertyFunction = - function("function() { return this.a === 'a_property' }"); + Function checkPropertyFunction = function("function() { return this.a === 'a_property' }"); Object jsObject = Object(rt); jsObject.setProperty(rt, "a", String::createFromUtf8(rt, "a_property")); @@ -553,9 +484,7 @@ TEST_P(JsiRuntimeUnitTests, FunctionConstructorTest) { " this.pika = a;" " }" "}"); - ctor.getProperty(rt, "prototype") - .getObject(rt) - .setProperty(rt, "pika", "chu"); + ctor.getProperty(rt, "prototype").getObject(rt).setProperty(rt, "pika", "chu"); auto empty = ctor.callAsConstructor(rt); ASSERT_TRUE(empty.isObject()); auto emptyObj = std::move(empty).getObject(rt); @@ -576,9 +505,7 @@ TEST_P(JsiRuntimeUnitTests, FunctionConstructorTest) { // Sleep for 50 milliseconds std::this_thread::sleep_for(std::chrono::milliseconds(50)); EXPECT_GE( - function("function (d) { return (new Date()).getTime() - d.getTime(); }") - .call(rt, date) - .getNumber(), + function("function (d) { return (new Date()).getTime() - d.getTime(); }").call(rt, date).getNumber(), // We use 48 here because sleep_for is not very precise, // and this test is not about timing precision. 48); @@ -589,12 +516,9 @@ TEST_P(JsiRuntimeUnitTests, InstanceOfTest) { auto newObj = function("function (ctor) { return new ctor(); }"); auto instance = newObj.call(rt, ctor).getObject(rt); EXPECT_TRUE(instance.instanceOf(rt, ctor)); - EXPECT_EQ( - instance.getProperty(rt, "say").getString(rt).utf8(rt), "wubalubadubdub"); + EXPECT_EQ(instance.getProperty(rt, "say").getString(rt).utf8(rt), "wubalubadubdub"); EXPECT_FALSE(Object(rt).instanceOf(rt, ctor)); - EXPECT_TRUE(ctor.callAsConstructor(rt, nullptr, 0) - .getObject(rt) - .instanceOf(rt, ctor)); + EXPECT_TRUE(ctor.callAsConstructor(rt, nullptr, 0).getObject(rt).instanceOf(rt, ctor)); } TEST_P(JsiRuntimeUnitTests, HostFunctionTest) { @@ -603,14 +527,11 @@ TEST_P(JsiRuntimeUnitTests, HostFunctionTest) { rt, PropNameID::forAscii(rt, "plusOne"), 2, - [one, savedRt = &rt]( - Runtime &rt, const Value &thisVal, const Value *args, size_t count) { + [one, savedRt = &rt](Runtime &rt, const Value &thisVal, const Value *args, size_t count) { EXPECT_EQ(savedRt, &rt); // We don't know if we're in strict mode or not, so it's either global // or undefined. - EXPECT_TRUE( - Value::strictEquals(rt, thisVal, rt.global()) || - thisVal.isUndefined()); + EXPECT_TRUE(Value::strictEquals(rt, thisVal, rt.global()) || thisVal.isUndefined()); return *one + args[0].getNumber() + args[1].getNumber(); }); @@ -620,20 +541,13 @@ TEST_P(JsiRuntimeUnitTests, HostFunctionTest) { EXPECT_TRUE(eval("plusOne(20, 300) == 321").getBool()); Function dot = Function::createFromHostFunction( - rt, - PropNameID::forAscii(rt, "dot"), - 2, - [](Runtime &rt, const Value &thisVal, const Value *args, size_t count) { - EXPECT_TRUE( - Value::strictEquals(rt, thisVal, rt.global()) || - thisVal.isUndefined()); + rt, PropNameID::forAscii(rt, "dot"), 2, [](Runtime &rt, const Value &thisVal, const Value *args, size_t count) { + EXPECT_TRUE(Value::strictEquals(rt, thisVal, rt.global()) || thisVal.isUndefined()); if (count != 2) { throw std::runtime_error("expected 2 args"); } - std::string ret = args[0].getString(rt).utf8(rt) + "." + - args[1].getString(rt).utf8(rt); - return String::createFromUtf8( - rt, reinterpret_cast(ret.data()), ret.size()); + std::string ret = args[0].getString(rt).utf8(rt) + "." + args[1].getString(rt).utf8(rt); + return String::createFromUtf8(rt, reinterpret_cast(ret.data()), ret.size()); }); rt.global().setProperty(rt, "cons", dot); @@ -661,8 +575,7 @@ TEST_P(JsiRuntimeUnitTests, HostFunctionTest) { [](Runtime &rt, const Value &thisVal, const Value *args, size_t count) { EXPECT_EQ(count, 0); std::string ret = thisVal.toString(rt).utf8(rt) + " is cool"; - return String::createFromUtf8( - rt, reinterpret_cast(ret.data()), ret.size()); + return String::createFromUtf8(rt, reinterpret_cast(ret.data()), ret.size()); }); rt.global().setProperty(rt, "coolify", coolify); EXPECT_TRUE(eval("coolify.name == 'coolify'").getBool()); @@ -678,15 +591,9 @@ TEST_P(JsiRuntimeUnitTests, HostFunctionTest) { rt, PropNameID::forAscii(rt, "lookAtMe"), 0, - [](Runtime &rt, const Value &thisVal, const Value *args, size_t count) - -> Value { + [](Runtime &rt, const Value &thisVal, const Value *args, size_t count) -> Value { EXPECT_TRUE(thisVal.isObject()); - EXPECT_EQ( - thisVal.getObject(rt) - .getProperty(rt, "name") - .getString(rt) - .utf8(rt), - "mr.meeseeks"); + EXPECT_EQ(thisVal.getObject(rt).getProperty(rt, "name").getString(rt).utf8(rt), "mr.meeseeks"); return Value(); }); rt.global().setProperty(rt, "lookAtMe", lookAtMe); @@ -695,33 +602,25 @@ TEST_P(JsiRuntimeUnitTests, HostFunctionTest) { struct Callable { Callable(std::string s) : str(s) {} - Value - operator()(Runtime &rt, const Value &, const Value *args, size_t count) { + Value operator()(Runtime &rt, const Value &, const Value *args, size_t count) { if (count != 1) { return Value(); } - return String::createFromUtf8( - rt, args[0].toString(rt).utf8(rt) + " was called with " + str); + return String::createFromUtf8(rt, args[0].toString(rt).utf8(rt) + " was called with " + str); } std::string str; }; - Function callable = Function::createFromHostFunction( - rt, - PropNameID::forAscii(rt, "callable"), - 1, - Callable("std::function::target")); + Function callable = + Function::createFromHostFunction(rt, PropNameID::forAscii(rt, "callable"), 1, Callable("std::function::target")); EXPECT_EQ( - function("function (f) { return f('A cat'); }") - .call(rt, callable) - .getString(rt) - .utf8(rt), + function("function (f) { return f('A cat'); }").call(rt, callable).getString(rt).utf8(rt), "A cat was called with std::function::target"); EXPECT_TRUE(callable.isHostFunction(rt)); // TODO (yicyao): Chakra(Core)Runtime currently does not support // GetHostFunction. - //EXPECT_NE(callable.getHostFunction(rt).target(), nullptr); + // EXPECT_NE(callable.getHostFunction(rt).target(), nullptr); std::string strval = "strval1"; auto getter = Object(rt); @@ -732,11 +631,7 @@ TEST_P(JsiRuntimeUnitTests, HostFunctionTest) { rt, PropNameID::forAscii(rt, "getter"), 1, - [&strval]( - Runtime &rt, - const Value &thisVal, - const Value *args, - size_t count) -> Value { + [&strval](Runtime &rt, const Value &thisVal, const Value *args, size_t count) -> Value { return String::createFromUtf8(rt, strval); })); auto obj = Object(rt); @@ -744,13 +639,9 @@ TEST_P(JsiRuntimeUnitTests, HostFunctionTest) { .getPropertyAsObject(rt, "Object") .getPropertyAsFunction(rt, "defineProperty") .call(rt, obj, "prop", getter); - EXPECT_TRUE(function("function(value) { return value.prop == 'strval1'; }") - .call(rt, obj) - .getBool()); + EXPECT_TRUE(function("function(value) { return value.prop == 'strval1'; }").call(rt, obj).getBool()); strval = "strval2"; - EXPECT_TRUE(function("function(value) { return value.prop == 'strval2'; }") - .call(rt, obj) - .getBool()); + EXPECT_TRUE(function("function(value) { return value.prop == 'strval2'; }").call(rt, obj).getBool()); } TEST_P(JsiRuntimeUnitTests, ValueTest) { @@ -795,23 +686,20 @@ TEST_P(JsiRuntimeUnitTests, ValueTest) { } Object o = Object(rt); - EXPECT_TRUE(function("function(value) { return typeof(value) == 'object'; }") - .call(rt, Value(rt, o)) - .getBool()); + EXPECT_TRUE(function("function(value) { return typeof(value) == 'object'; }").call(rt, Value(rt, o)).getBool()); uint8_t utf8[] = "[null, 2, \"c\", \"emoji: \xf0\x9f\x86\x97\", {}]"; - EXPECT_TRUE( - function("function (arr) { return " - "Array.isArray(arr) && " - "arr.length == 5 && " - "arr[0] === null && " - "arr[1] == 2 && " - "arr[2] == 'c' && " - "arr[3] == 'emoji: \\uD83C\\uDD97' && " - "typeof arr[4] == 'object'; }") - .call(rt, Value::createFromJsonUtf8(rt, utf8, sizeof(utf8) - 1)) - .getBool()); + EXPECT_TRUE(function("function (arr) { return " + "Array.isArray(arr) && " + "arr.length == 5 && " + "arr[0] === null && " + "arr[1] == 2 && " + "arr[2] == 'c' && " + "arr[3] == 'emoji: \\uD83C\\uDD97' && " + "typeof arr[4] == 'object'; }") + .call(rt, Value::createFromJsonUtf8(rt, utf8, sizeof(utf8) - 1)) + .getBool()); EXPECT_TRUE(eval("undefined").isUndefined()); EXPECT_TRUE(eval("null").isNull()); @@ -832,8 +720,7 @@ TEST_P(JsiRuntimeUnitTests, ValueTest) { EXPECT_EQ(eval("456").asNumber(), 456); EXPECT_THROW(eval("'word'").asNumber(), JSIException); - EXPECT_EQ( - eval("({1:2, 3:4})").asObject(rt).getProperty(rt, "1").getNumber(), 2); + EXPECT_EQ(eval("({1:2, 3:4})").asObject(rt).getProperty(rt, "1").getNumber(), 2); EXPECT_THROW(eval("'oops'").asObject(rt), JSIException); EXPECT_EQ(eval("['zero',1,2,3]").toString(rt).utf8(rt), "zero,1,2,3"); @@ -849,22 +736,15 @@ TEST_P(JsiRuntimeUnitTests, EqualsTest) { EXPECT_TRUE(Value::strictEquals(rt, nullptr, nullptr)); EXPECT_TRUE(Value::strictEquals(rt, Value::undefined(), Value())); EXPECT_TRUE(Value::strictEquals(rt, rt.global(), Value(rt.global()))); + EXPECT_FALSE( + Value::strictEquals(rt, std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN())); EXPECT_FALSE(Value::strictEquals( - rt, - std::numeric_limits::quiet_NaN(), - std::numeric_limits::quiet_NaN())); - EXPECT_FALSE(Value::strictEquals( - rt, - std::numeric_limits::signaling_NaN(), - std::numeric_limits::signaling_NaN())); + rt, std::numeric_limits::signaling_NaN(), std::numeric_limits::signaling_NaN())); EXPECT_TRUE(Value::strictEquals(rt, +0.0, -0.0)); EXPECT_TRUE(Value::strictEquals(rt, -0.0, +0.0)); Function noop = Function::createFromHostFunction( - rt, - PropNameID::forAscii(rt, "noop"), - 0, - [](const Runtime &, const Value &, const Value *, size_t) { + rt, PropNameID::forAscii(rt, "noop"), 0, [](const Runtime &, const Value &, const Value *, size_t) { return Value(); }); auto noopDup = Value(rt, noop).getObject(rt); @@ -894,8 +774,7 @@ TEST_P(JsiRuntimeUnitTests, ExceptionStackTraceTest) { "world()"; std::string stack; try { - rt.evaluateJavaScript( - std::make_unique(invokeUndefinedScript), ""); + rt.evaluateJavaScript(std::make_unique(invokeUndefinedScript), ""); } catch (JSError &e) { stack = e.getStack(); } @@ -907,8 +786,7 @@ namespace { unsigned countOccurences(const std::string &of, const std::string &in) { unsigned occurences = 0; std::string::size_type lastOccurence = -1; - while ((lastOccurence = in.find(of, lastOccurence + 1)) != - std::string::npos) { + while ((lastOccurence = in.find(of, lastOccurence + 1)) != std::string::npos) { occurences++; } return occurences; @@ -931,8 +809,7 @@ TEST_P(JsiRuntimeUnitTests, JSErrorsArePropagatedNicely) { rt, PropNameID::forAscii(rt, "callback"), 0, - [&sometimesThrows, &callsBeforeError]( - Runtime &rt, const Value &thisVal, const Value *args, size_t count) { + [&sometimesThrows, &callsBeforeError](Runtime &rt, const Value &thisVal, const Value *args, size_t count) { return sometimesThrows.call(rt, --callsBeforeError == 0, args[0]); }); @@ -1027,7 +904,5 @@ TEST_P(JsiRuntimeUnitTests, HostObjectWithValueMembers) { EXPECT_EQ(bag["count"].getNumber(), 37); EXPECT_TRUE(bag["nul"].isNull()); EXPECT_TRUE(bag["iscool"].getBool()); - EXPECT_EQ( - bag["obj"].getObject(rt).getProperty(rt, "foo").getString(rt).utf8(rt), - "bar"); + EXPECT_EQ(bag["obj"].getObject(rt).getProperty(rt, "foo").getString(rt).utf8(rt), "bar"); } diff --git a/vnext/JSI/Shared/ChakraObjectRef.cpp b/vnext/JSI/Shared/ChakraObjectRef.cpp index 75b24a0a28d..f60647712ae 100644 --- a/vnext/JSI/Shared/ChakraObjectRef.cpp +++ b/vnext/JSI/Shared/ChakraObjectRef.cpp @@ -17,8 +17,7 @@ namespace Microsoft::JSI { void VerifyChakraErrorElseThrow(JsErrorCode error) { if (error != JsNoError) { std::ostringstream errorString; - errorString << "A call to Chakra(Core) API returned error code 0x" - << std::hex << error << '.'; + errorString << "A call to Chakra(Core) API returned error code 0x" << std::hex << error << '.'; throw facebook::jsi::JSINativeException(errorString.str()); } } @@ -34,8 +33,7 @@ ChakraObjectRef::ChakraObjectRef(ChakraObjectRef &&original) noexcept { Swap(original); } -ChakraObjectRef &ChakraObjectRef::operator=( - const ChakraObjectRef &rhs) noexcept { +ChakraObjectRef &ChakraObjectRef::operator=(const ChakraObjectRef &rhs) noexcept { ChakraObjectRef rhsCopy(rhs); Swap(rhsCopy); return *this; @@ -54,13 +52,11 @@ ChakraObjectRef::~ChakraObjectRef() noexcept { void ChakraObjectRef::Initialize(JsRef ref) { if (m_state != State::Uninitialized) { - throw facebook::jsi::JSINativeException( - "A ChakraObjectRef can only be initialzed once."); + throw facebook::jsi::JSINativeException("A ChakraObjectRef can only be initialzed once."); } if (!ref) { - throw facebook::jsi::JSINativeException( - "Cannot initialize a ChakraObjectRef with a null reference."); + throw facebook::jsi::JSINativeException("Cannot initialize a ChakraObjectRef with a null reference."); } VerifyChakraErrorElseThrow(JsAddRef(ref, nullptr)); @@ -71,8 +67,7 @@ void ChakraObjectRef::Initialize(JsRef ref) { void ChakraObjectRef::Invalidate() { switch (m_state) { case State::Uninitialized: { - throw facebook::jsi::JSINativeException( - "Cannot invalidate a ChakraObjectRef that has not been initialized."); + throw facebook::jsi::JSINativeException("Cannot invalidate a ChakraObjectRef that has not been initialized."); break; } case State::Initialized: { @@ -114,8 +109,7 @@ JsPropertyIdType GetPropertyIdType(const ChakraObjectRef &jsPropId) { std::wstring GetPropertyName(const ChakraObjectRef &id) { if (GetPropertyIdType(id) != JsPropertyIdTypeString) { - throw facebook::jsi::JSINativeException( - "It is llegal to retrieve the name of a property symbol."); + throw facebook::jsi::JSINativeException("It is llegal to retrieve the name of a property symbol."); } const wchar_t *propertyName = nullptr; VerifyChakraErrorElseThrow(JsGetPropertyNameFromId(id, &propertyName)); @@ -124,8 +118,7 @@ std::wstring GetPropertyName(const ChakraObjectRef &id) { ChakraObjectRef GetPropertySymbol(const ChakraObjectRef &id) { if (GetPropertyIdType(id) != JsPropertyIdTypeSymbol) { - throw facebook::jsi::JSINativeException( - "It is llegal to retrieve the symbol associated with a property name."); + throw facebook::jsi::JSINativeException("It is llegal to retrieve the symbol associated with a property name."); } JsValueRef symbol = JS_INVALID_REFERENCE; VerifyChakraErrorElseThrow(JsGetSymbolFromPropertyId(id, &symbol)); @@ -134,16 +127,14 @@ ChakraObjectRef GetPropertySymbol(const ChakraObjectRef &id) { ChakraObjectRef GetPropertyId(const std::string_view &utf8) { if (!utf8.data()) { - throw facebook::jsi::JSINativeException( - "Property name cannot be a nullptr."); + throw facebook::jsi::JSINativeException("Property name cannot be a nullptr."); } // We use a #ifdef here because we can avoid a UTF-8 to UTF-16 conversion // using ChakraCore's JsCreatePropertyId API. #ifdef CHAKRACORE JsPropertyIdRef id = JS_INVALID_REFERENCE; - VerifyChakraErrorElseThrow( - JsCreatePropertyId(utf8.data(), utf8.length(), &id)); + VerifyChakraErrorElseThrow(JsCreatePropertyId(utf8.data(), utf8.length(), &id)); return ChakraObjectRef(id); #else @@ -160,8 +151,7 @@ ChakraObjectRef GetPropertyId(const std::wstring &utf16) { std::string ToStdString(const ChakraObjectRef &jsString) { if (GetValueType(jsString) != JsString) { - throw facebook::jsi::JSINativeException( - "Cannot convert a non JS string ChakraObjectRef to a std::string."); + throw facebook::jsi::JSINativeException("Cannot convert a non JS string ChakraObjectRef to a std::string."); } // We use a #ifdef here because we can avoid a UTF-8 to UTF-16 conversion @@ -171,12 +161,10 @@ std::string ToStdString(const ChakraObjectRef &jsString) { VerifyChakraErrorElseThrow(JsCopyString(jsString, nullptr, 0, &length)); std::string result(length, 'a'); - VerifyChakraErrorElseThrow( - JsCopyString(jsString, result.data(), result.length(), &length)); + VerifyChakraErrorElseThrow(JsCopyString(jsString, result.data(), result.length(), &length)); if (length != result.length()) { - throw facebook::jsi::JSINativeException( - "Failed to convert a JS string to a std::string."); + throw facebook::jsi::JSINativeException("Failed to convert a JS string to a std::string."); } return result; @@ -187,8 +175,7 @@ std::string ToStdString(const ChakraObjectRef &jsString) { std::wstring ToStdWstring(const ChakraObjectRef &jsString) { if (GetValueType(jsString) != JsString) { - throw facebook::jsi::JSINativeException( - "Cannot convert a non JS string ChakraObjectRef to a std::wstring."); + throw facebook::jsi::JSINativeException("Cannot convert a non JS string ChakraObjectRef to a std::wstring."); } const wchar_t *utf16 = nullptr; @@ -200,16 +187,14 @@ std::wstring ToStdWstring(const ChakraObjectRef &jsString) { ChakraObjectRef ToJsString(const std::string_view &utf8) { if (!utf8.data()) { - throw facebook::jsi::JSINativeException( - "Cannot convert a nullptr to a JS string."); + throw facebook::jsi::JSINativeException("Cannot convert a nullptr to a JS string."); } // We use a #ifdef here because we can avoid a UTF-8 to UTF-16 conversion // using ChakraCore's JsCreateString API. #ifdef CHAKRACORE JsValueRef result = JS_INVALID_REFERENCE; - VerifyChakraErrorElseThrow( - JsCreateString(utf8.data(), utf8.length(), &result)); + VerifyChakraErrorElseThrow(JsCreateString(utf8.data(), utf8.length(), &result)); return ChakraObjectRef(result); #else @@ -220,13 +205,11 @@ ChakraObjectRef ToJsString(const std::string_view &utf8) { ChakraObjectRef ToJsString(const std::wstring_view &utf16) { if (!utf16.data()) { - throw facebook::jsi::JSINativeException( - "Cannot convert a nullptr to a JS string."); + throw facebook::jsi::JSINativeException("Cannot convert a nullptr to a JS string."); } JsValueRef result = JS_INVALID_REFERENCE; - VerifyChakraErrorElseThrow( - JsPointerToString(utf16.data(), utf16.length(), &result)); + VerifyChakraErrorElseThrow(JsPointerToString(utf16.data(), utf16.length(), &result)); return ChakraObjectRef(result); } @@ -243,38 +226,32 @@ ChakraObjectRef ToJsNumber(int num) { return ChakraObjectRef(result); } -ChakraObjectRef ToJsArrayBuffer( - const std::shared_ptr &buffer) { +ChakraObjectRef ToJsArrayBuffer(const std::shared_ptr &buffer) { if (!buffer) { - throw facebook::jsi::JSINativeException( - "Cannot create an external JS ArrayBuffer without backing buffer."); + throw facebook::jsi::JSINativeException("Cannot create an external JS ArrayBuffer without backing buffer."); } size_t size = buffer->size(); if (size > UINT_MAX) { - throw facebook::jsi::JSINativeException( - "The external backing buffer for a JS ArrayBuffer is too large."); + throw facebook::jsi::JSINativeException("The external backing buffer for a JS ArrayBuffer is too large."); } JsValueRef arrayBuffer = JS_INVALID_REFERENCE; - auto bufferWrapper = - std::make_unique>(buffer); + auto bufferWrapper = std::make_unique>(buffer); // We allocate a copy of buffer on the heap, a shared_ptr which is deleted // when the JavaScript garbage collecotr releases the created external array // buffer. This ensures that buffer stays alive while the JavaScript engine is // using it. VerifyChakraErrorElseThrow(JsCreateExternalArrayBuffer( - Common::Utilities::CheckedReinterpretCast( - const_cast(buffer->data())), + Common::Utilities::CheckedReinterpretCast(const_cast(buffer->data())), static_cast(size), [](void *bufferToDestroy) { // We wrap bufferToDestroy in a unique_ptr to avoid calling delete // explicitly. std::unique_ptr> wrapper{ - static_cast *>( - bufferToDestroy)}; + static_cast *>(bufferToDestroy)}; }, bufferWrapper.get(), &arrayBuffer)); @@ -286,9 +263,7 @@ ChakraObjectRef ToJsArrayBuffer( return ChakraObjectRef(arrayBuffer); } -bool CompareJsValues( - const ChakraObjectRef &jsValue1, - const ChakraObjectRef &jsValue2) { +bool CompareJsValues(const ChakraObjectRef &jsValue1, const ChakraObjectRef &jsValue2) { bool result = false; // Note that JsStrictEquals should only be used for JsValueRefs and not for // other types of JsRefs (e.g. JsPropertyIdRef, etc.). @@ -296,9 +271,7 @@ bool CompareJsValues( return result; } -bool CompareJsPropertyIds( - const ChakraObjectRef &jsPropId1, - const ChakraObjectRef &jsPropId2) { +bool CompareJsPropertyIds(const ChakraObjectRef &jsPropId1, const ChakraObjectRef &jsPropId2) { JsPropertyIdType type1 = GetPropertyIdType(jsPropId1); JsPropertyIdType type2 = GetPropertyIdType(jsPropId2); @@ -311,8 +284,7 @@ bool CompareJsPropertyIds( } if (type1 == JsPropertyIdTypeSymbol) { - return CompareJsValues( - GetPropertySymbol(jsPropId1), GetPropertySymbol(jsPropId2)); + return CompareJsValues(GetPropertySymbol(jsPropId1), GetPropertySymbol(jsPropId2)); } // Control should never reach here. diff --git a/vnext/JSI/Shared/ChakraObjectRef.h b/vnext/JSI/Shared/ChakraObjectRef.h index 89264694a47..bea640515ca 100644 --- a/vnext/JSI/Shared/ChakraObjectRef.h +++ b/vnext/JSI/Shared/ChakraObjectRef.h @@ -166,8 +166,7 @@ ChakraObjectRef ToJsNumber(int num); * @remarks The returned ArrayBuffer is backed by buffer and keeps buffer alive * till the garbage collector finalizes it. */ -ChakraObjectRef ToJsArrayBuffer( - const std::shared_ptr &buffer); +ChakraObjectRef ToJsArrayBuffer(const std::shared_ptr &buffer); /** * @returns A ChakraObjectRef managing a JS Object. @@ -178,8 +177,7 @@ ChakraObjectRef ToJsArrayBuffer( template ChakraObjectRef ToJsObject(const std::shared_ptr &data) { if (!data) { - throw facebook::jsi::JSINativeException( - "Cannot create an external JS Object without backing data."); + throw facebook::jsi::JSINativeException("Cannot create an external JS Object without backing data."); } JsValueRef obj = nullptr; @@ -193,8 +191,7 @@ ChakraObjectRef ToJsObject(const std::shared_ptr &data) { [](void *dataToDestroy) { // We wrap dataToDestroy in a unique_ptr to avoid calling delete // explicitly. - std::unique_ptr> wrapper{ - static_cast *>(dataToDestroy)}; + std::unique_ptr> wrapper{static_cast *>(dataToDestroy)}; }, &obj)); @@ -222,9 +219,7 @@ T *GetExternalData(const ChakraObjectRef &object) { * @returns A boolean indicating whether jsValue1 and jsValue2 are strictly * equal. */ -bool CompareJsValues( - const ChakraObjectRef &jsValue1, - const ChakraObjectRef &jsValue2); +bool CompareJsValues(const ChakraObjectRef &jsValue1, const ChakraObjectRef &jsValue2); /** * @param jsPropId1 A ChakraObjectRef managing a JsPropertyIdRef. @@ -233,9 +228,7 @@ bool CompareJsValues( * @returns A boolean indicating whether jsPropId1 and jsPropId2 are strictly * equal. */ -bool CompareJsPropertyIds( - const ChakraObjectRef &jsPropId1, - const ChakraObjectRef &jsPropId2); +bool CompareJsPropertyIds(const ChakraObjectRef &jsPropId1, const ChakraObjectRef &jsPropId2); /** * TODO (yicyao): Add documentation here. diff --git a/vnext/JSI/Shared/ChakraRuntime.cpp b/vnext/JSI/Shared/ChakraRuntime.cpp index 560b28d0bb0..ea5b1f1d77f 100644 --- a/vnext/JSI/Shared/ChakraRuntime.cpp +++ b/vnext/JSI/Shared/ChakraRuntime.cpp @@ -20,19 +20,14 @@ namespace Microsoft::JSI { namespace { -constexpr const char *const g_functionIsHostFunctionPropName = - "$$FunctionIsHostFunction$$"; +constexpr const char *const g_functionIsHostFunctionPropName = "$$FunctionIsHostFunction$$"; -constexpr const char *const g_proxyIsHostObjectPropName = - "$$ProxyIsHostObject$$"; -constexpr const char *const g_proxyGetHostObjectTargetPropName = - "$$ProxyGetHostObjectTarget$$"; +constexpr const char *const g_proxyIsHostObjectPropName = "$$ProxyIsHostObject$$"; +constexpr const char *const g_proxyGetHostObjectTargetPropName = "$$ProxyGetHostObjectTarget$$"; class HostFunctionProxy { public: - HostFunctionProxy( - facebook::jsi::HostFunctionType &&hostFunction, - ChakraRuntime &runtime) + HostFunctionProxy(facebook::jsi::HostFunctionType &&hostFunction, ChakraRuntime &runtime) : m_hostFunction(std::move(hostFunction)), m_runtime(runtime) {} inline const facebook::jsi::HostFunctionType &GetHostFunction() const { @@ -63,8 +58,7 @@ std::vector ConstructJsFunctionArguments( } // namespace -ChakraRuntime::ChakraRuntime(ChakraRuntimeArgs &&args) noexcept - : m_args{std::move(args)} { +ChakraRuntime::ChakraRuntime(ChakraRuntimeArgs &&args) noexcept : m_args{std::move(args)} { JsRuntimeAttributes runtimeAttributes = JsRuntimeAttributeNone; if (!m_args.enableJITCompilation) { @@ -73,8 +67,7 @@ ChakraRuntime::ChakraRuntime(ChakraRuntimeArgs &&args) noexcept JsRuntimeAttributeDisableExecutablePageAllocation); } - VerifyChakraErrorElseThrow( - JsCreateRuntime(runtimeAttributes, nullptr, &m_runtime)); + VerifyChakraErrorElseThrow(JsCreateRuntime(runtimeAttributes, nullptr, &m_runtime)); setupMemoryTracker(); @@ -125,8 +118,7 @@ facebook::jsi::Value ChakraRuntime::evaluateJavaScript( scriptBuffer = std::move(buffer); scriptVersion = runtimeArgs().scriptStore->getScriptVersion(sourceURL); } else { - auto versionedScript = - runtimeArgs().scriptStore->getVersionedScript(sourceURL); + auto versionedScript = runtimeArgs().scriptStore->getVersionedScript(sourceURL); scriptBuffer = std::move(versionedScript.buffer); scriptVersion = versionedScript.version; } @@ -141,30 +133,25 @@ facebook::jsi::Value ChakraRuntime::evaluateJavaScript( return facebook::jsi::Value::undefined(); } - auto sharedScriptBuffer = - std::shared_ptr(std::move(scriptBuffer)); + auto sharedScriptBuffer = std::shared_ptr(std::move(scriptBuffer)); facebook::jsi::ScriptSignature scriptSignature = {sourceURL, scriptVersion}; - facebook::jsi::JSRuntimeSignature runtimeSignature = {description().c_str(), - getRuntimeVersion()}; + facebook::jsi::JSRuntimeSignature runtimeSignature = {description().c_str(), getRuntimeVersion()}; - auto preparedScript = runtimeArgs().preparedScriptStore->tryGetPreparedScript( - scriptSignature, runtimeSignature, nullptr); + auto preparedScript = + runtimeArgs().preparedScriptStore->tryGetPreparedScript(scriptSignature, runtimeSignature, nullptr); std::shared_ptr sharedPreparedScript; if (preparedScript) { - sharedPreparedScript = - std::shared_ptr(std::move(preparedScript)); + sharedPreparedScript = std::shared_ptr(std::move(preparedScript)); } else { - auto genPreparedScript = - generatePreparedScript(sourceURL, *sharedScriptBuffer); + auto genPreparedScript = generatePreparedScript(sourceURL, *sharedScriptBuffer); if (!genPreparedScript) std::terminate(); // Cache generation can't fail unless something really // wrong. but we should get rid of this abort before // shipping. - sharedPreparedScript = std::shared_ptr( - std::move(genPreparedScript)); + sharedPreparedScript = std::shared_ptr(std::move(genPreparedScript)); runtimeArgs().preparedScriptStore->persistPreparedScript( sharedPreparedScript, scriptSignature, runtimeSignature, nullptr); } @@ -177,8 +164,7 @@ facebook::jsi::Value ChakraRuntime::evaluateJavaScript( m_pinnedPreparedScripts.push_back(sharedPreparedScript); m_pinnedScripts.push_back(sharedScriptBuffer); - if (evaluateSerializedScript( - *sharedScriptBuffer, *sharedPreparedScript, sourceURL)) { + if (evaluateSerializedScript(*sharedScriptBuffer, *sharedPreparedScript, sourceURL)) { return facebook::jsi::Value::undefined(); } @@ -186,8 +172,7 @@ facebook::jsi::Value ChakraRuntime::evaluateJavaScript( return evaluateJavaScriptSimple(*sharedScriptBuffer, sourceURL); } -std::shared_ptr -ChakraRuntime::prepareJavaScript( +std::shared_ptr ChakraRuntime::prepareJavaScript( const std::shared_ptr &, std::string) { throw facebook::jsi::JSINativeException("Not implemented!"); @@ -232,22 +217,16 @@ facebook::jsi::Runtime::PointerValue *ChakraRuntime::clonePropNameID( return CloneChakraPointerValue(pointerValue); } -facebook::jsi::PropNameID ChakraRuntime::createPropNameIDFromAscii( - const char *str, - size_t length) { +facebook::jsi::PropNameID ChakraRuntime::createPropNameIDFromAscii(const char *str, size_t length) { ChakraObjectRef propId = GetPropertyId(std::string_view{str, length}); return MakePointer(std::move(propId)); } -facebook::jsi::PropNameID ChakraRuntime::createPropNameIDFromUtf8( - const uint8_t *utf8, - size_t length) { - return createPropNameIDFromAscii( - Common::Utilities::CheckedReinterpretCast(utf8), length); +facebook::jsi::PropNameID ChakraRuntime::createPropNameIDFromUtf8(const uint8_t *utf8, size_t length) { + return createPropNameIDFromAscii(Common::Utilities::CheckedReinterpretCast(utf8), length); } -facebook::jsi::PropNameID ChakraRuntime::createPropNameIDFromString( - const facebook::jsi::String &str) { +facebook::jsi::PropNameID ChakraRuntime::createPropNameIDFromString(const facebook::jsi::String &str) { // We don not use the functions: // std::string ChakraRuntime::utf8(const String& str), and // std::string ToStdString(const ChakraObjectRef &jsString) @@ -260,9 +239,7 @@ std::string ChakraRuntime::utf8(const facebook::jsi::PropNameID &id) { return Common::Unicode::Utf16ToUtf8(GetPropertyName(GetChakraObjectRef(id))); } -bool ChakraRuntime::compare( - const facebook::jsi::PropNameID &lhs, - const facebook::jsi::PropNameID &rhs) { +bool ChakraRuntime::compare(const facebook::jsi::PropNameID &lhs, const facebook::jsi::PropNameID &rhs) { return CompareJsPropertyIds(GetChakraObjectRef(lhs), GetChakraObjectRef(rhs)); } @@ -270,20 +247,15 @@ std::string ChakraRuntime::symbolToString(const facebook::jsi::Symbol &s) { return ToStdString(ToJsString(GetChakraObjectRef(s))); } -facebook::jsi::String ChakraRuntime::createStringFromAscii( - const char *str, - size_t length) { +facebook::jsi::String ChakraRuntime::createStringFromAscii(const char *str, size_t length) { // Unfortunately due to the typing used by JSI and Chakra, we have to do a // double reinterpret cast here. - return createStringFromUtf8( - Common::Utilities::CheckedReinterpretCast(str), length); + return createStringFromUtf8(Common::Utilities::CheckedReinterpretCast(str), length); } -facebook::jsi::String ChakraRuntime::createStringFromUtf8( - const uint8_t *str, - size_t length) { - return MakePointer(ToJsString(std::string_view{ - Common::Utilities::CheckedReinterpretCast(str), length})); +facebook::jsi::String ChakraRuntime::createStringFromUtf8(const uint8_t *str, size_t length) { + return MakePointer( + ToJsString(std::string_view{Common::Utilities::CheckedReinterpretCast(str), length})); } std::string ChakraRuntime::utf8(const facebook::jsi::String &str) { @@ -296,8 +268,7 @@ facebook::jsi::Object ChakraRuntime::createObject() { return MakePointer(obj); } -facebook::jsi::Object ChakraRuntime::createObject( - std::shared_ptr hostObject) { +facebook::jsi::Object ChakraRuntime::createObject(std::shared_ptr hostObject) { constexpr const char *const jsProxyConstructorWrapperSource = "(function()\n" "{\n" @@ -307,71 +278,51 @@ facebook::jsi::Object ChakraRuntime::createObject( " }\n" "})()"; - static facebook::jsi::StringBuffer jsProxyConstructorWrapperSourceBuffer{ - jsProxyConstructorWrapperSource}; + static facebook::jsi::StringBuffer jsProxyConstructorWrapperSourceBuffer{jsProxyConstructorWrapperSource}; facebook::jsi::Function jsProxyConstructorWrapper = - evaluateJavaScriptSimple( - jsProxyConstructorWrapperSourceBuffer, - "ChakraRuntime_createObject.js") + evaluateJavaScriptSimple(jsProxyConstructorWrapperSourceBuffer, "ChakraRuntime_createObject.js") .asObject(*this) .asFunction(*this); return jsProxyConstructorWrapper - .call( - *this, - MakePointer(ToJsObject(hostObject)), - createHostObjectProxyHandler()) + .call(*this, MakePointer(ToJsObject(hostObject)), createHostObjectProxyHandler()) .asObject(*this); } -std::shared_ptr ChakraRuntime::getHostObject( - const facebook::jsi::Object &obj) { +std::shared_ptr ChakraRuntime::getHostObject(const facebook::jsi::Object &obj) { if (!isHostObject(obj)) { - throw facebook::jsi::JSINativeException( - "getHostObject() can only be called with HostObjects."); + throw facebook::jsi::JSINativeException("getHostObject() can only be called with HostObjects."); } - facebook::jsi::Object target = - obj.getPropertyAsObject(*this, g_proxyGetHostObjectTargetPropName); + facebook::jsi::Object target = obj.getPropertyAsObject(*this, g_proxyGetHostObjectTargetPropName); - return *GetExternalData>( - GetChakraObjectRef(target)); + return *GetExternalData>(GetChakraObjectRef(target)); } -facebook::jsi::HostFunctionType &ChakraRuntime::getHostFunction( - const facebook::jsi::Function &obj) { - throw facebook::jsi::JSINativeException( - "ChakraRuntime::getHostFunction is not implemented."); +facebook::jsi::HostFunctionType &ChakraRuntime::getHostFunction(const facebook::jsi::Function &obj) { + throw facebook::jsi::JSINativeException("ChakraRuntime::getHostFunction is not implemented."); } facebook::jsi::Value ChakraRuntime::getProperty( const facebook::jsi::Object &obj, const facebook::jsi::PropNameID &name) { JsValueRef result = JS_INVALID_REFERENCE; - VerifyJsErrorElseThrow(JsGetProperty( - GetChakraObjectRef(obj), GetChakraObjectRef(name), &result)); + VerifyJsErrorElseThrow(JsGetProperty(GetChakraObjectRef(obj), GetChakraObjectRef(name), &result)); return ToJsiValue(ChakraObjectRef(result)); } -facebook::jsi::Value ChakraRuntime::getProperty( - const facebook::jsi::Object &obj, - const facebook::jsi::String &name) { +facebook::jsi::Value ChakraRuntime::getProperty(const facebook::jsi::Object &obj, const facebook::jsi::String &name) { return getProperty(obj, createPropNameIDFromString(name)); } -bool ChakraRuntime::hasProperty( - const facebook::jsi::Object &obj, - const facebook::jsi::PropNameID &name) { +bool ChakraRuntime::hasProperty(const facebook::jsi::Object &obj, const facebook::jsi::PropNameID &name) { bool result = false; - VerifyJsErrorElseThrow(JsHasProperty( - GetChakraObjectRef(obj), GetChakraObjectRef(name), &result)); + VerifyJsErrorElseThrow(JsHasProperty(GetChakraObjectRef(obj), GetChakraObjectRef(name), &result)); return result; } -bool ChakraRuntime::hasProperty( - const facebook::jsi::Object &obj, - const facebook::jsi::String &name) { +bool ChakraRuntime::hasProperty(const facebook::jsi::Object &obj, const facebook::jsi::String &name) { return hasProperty(obj, createPropNameIDFromString(name)); } @@ -384,10 +335,7 @@ void ChakraRuntime::setPropertyValue( // property, assignment to a getter-only property, assignment to a new // property on a non-extensible object) will throw. VerifyJsErrorElseThrow(JsSetProperty( - GetChakraObjectRef(object), - GetChakraObjectRef(name), - ToChakraObjectRef(value), - true /* useStrictRules */)); + GetChakraObjectRef(object), GetChakraObjectRef(name), ToChakraObjectRef(value), true /* useStrictRules */)); } void ChakraRuntime::setPropertyValue( @@ -429,8 +377,7 @@ bool ChakraRuntime::isHostFunction(const facebook::jsi::Function &obj) const { } } -facebook::jsi::Array ChakraRuntime::getPropertyNames( - const facebook::jsi::Object &object) { +facebook::jsi::Array ChakraRuntime::getPropertyNames(const facebook::jsi::Object &object) { constexpr const char *const jsGetPropertyNamesSource = "(function()\n" "{\n" @@ -445,36 +392,25 @@ facebook::jsi::Array ChakraRuntime::getPropertyNames( " }\n" "})()"; - static facebook::jsi::StringBuffer jsGetPropertyNamesSourceBuffer{ - jsGetPropertyNamesSource}; + static facebook::jsi::StringBuffer jsGetPropertyNamesSourceBuffer{jsGetPropertyNamesSource}; facebook::jsi::Function jsGetPropertyNames = - evaluateJavaScriptSimple( - jsGetPropertyNamesSourceBuffer, "ChakraRuntime_getPropertyNames.js") + evaluateJavaScriptSimple(jsGetPropertyNamesSourceBuffer, "ChakraRuntime_getPropertyNames.js") .asObject(*this) .asFunction(*this); facebook::jsi::Value objAsValue(*this, object); - return call( - jsGetPropertyNames, - facebook::jsi::Value::undefined(), - &objAsValue, - 1) - .asObject(*this) - .asArray(*this); + return call(jsGetPropertyNames, facebook::jsi::Value::undefined(), &objAsValue, 1).asObject(*this).asArray(*this); } // Only ChakraCore supports weak reference semantics, so ChakraRuntime // WeakObjects are in fact strong references. -facebook::jsi::WeakObject ChakraRuntime::createWeakObject( - const facebook::jsi::Object &object) { - return make( - CloneChakraPointerValue(getPointerValue(object))); +facebook::jsi::WeakObject ChakraRuntime::createWeakObject(const facebook::jsi::Object &object) { + return make(CloneChakraPointerValue(getPointerValue(object))); } -facebook::jsi::Value ChakraRuntime::lockWeakObject( - const facebook::jsi::WeakObject &weakObject) { +facebook::jsi::Value ChakraRuntime::lockWeakObject(const facebook::jsi::WeakObject &weakObject) { // We need to make a copy of the ChakraObjectRef held within weakObj's // member PointerValue for the returned jsi::Value here. ChakraObjectRef ref = GetChakraObjectRef(weakObject); @@ -485,8 +421,7 @@ facebook::jsi::Array ChakraRuntime::createArray(size_t length) { assert(length <= UINT_MAX); JsValueRef result = JS_INVALID_REFERENCE; - VerifyJsErrorElseThrow( - JsCreateArray(static_cast(length), &result)); + VerifyJsErrorElseThrow(JsCreateArray(static_cast(length), &result)); return MakePointer(result).asArray(*this); } @@ -507,50 +442,37 @@ uint8_t *ChakraRuntime::data(const facebook::jsi::ArrayBuffer &arrBuf) { uint8_t *buffer = nullptr; unsigned int size = 0; - VerifyJsErrorElseThrow( - JsGetArrayBufferStorage(GetChakraObjectRef(arrBuf), &buffer, &size)); + VerifyJsErrorElseThrow(JsGetArrayBufferStorage(GetChakraObjectRef(arrBuf), &buffer, &size)); return buffer; } -facebook::jsi::Value ChakraRuntime::getValueAtIndex( - const facebook::jsi::Array &arr, - size_t index) { +facebook::jsi::Value ChakraRuntime::getValueAtIndex(const facebook::jsi::Array &arr, size_t index) { assert(isArray(arr)); assert(index <= INT_MAX); JsValueRef result = JS_INVALID_REFERENCE; - VerifyJsErrorElseThrow(JsGetIndexedProperty( - GetChakraObjectRef(arr), ToJsNumber(static_cast(index)), &result)); + VerifyJsErrorElseThrow(JsGetIndexedProperty(GetChakraObjectRef(arr), ToJsNumber(static_cast(index)), &result)); return ToJsiValue(ChakraObjectRef(result)); } -void ChakraRuntime::setValueAtIndexImpl( - facebook::jsi::Array &arr, - size_t index, - const facebook::jsi::Value &value) { +void ChakraRuntime::setValueAtIndexImpl(facebook::jsi::Array &arr, size_t index, const facebook::jsi::Value &value) { assert(isArray(arr)); assert(index <= INT_MAX); - VerifyJsErrorElseThrow(JsSetIndexedProperty( - GetChakraObjectRef(arr), - ToJsNumber(static_cast(index)), - ToChakraObjectRef(value))); + VerifyJsErrorElseThrow( + JsSetIndexedProperty(GetChakraObjectRef(arr), ToJsNumber(static_cast(index)), ToChakraObjectRef(value))); } facebook::jsi::Function ChakraRuntime::createFunctionFromHostFunction( const facebook::jsi::PropNameID &name, unsigned int paramCount, facebook::jsi::HostFunctionType func) { - std::unique_ptr hostFuncProxyWrapper = - std::make_unique(std::move(func), *this); + std::unique_ptr hostFuncProxyWrapper = std::make_unique(std::move(func), *this); JsValueRef funcRef; - VerifyJsErrorElseThrow(JsCreateNamedFunction( - ToJsString(utf8(name)), - HostFunctionCall, - hostFuncProxyWrapper.get(), - &funcRef)); + VerifyJsErrorElseThrow( + JsCreateNamedFunction(ToJsString(utf8(name)), HostFunctionCall, hostFuncProxyWrapper.get(), &funcRef)); // hostFuncProxy keeps a reference to this Runtime, so we must keep this // Runtime alive as long as hostFuncProxy is alive. We do so by making the @@ -558,13 +480,10 @@ facebook::jsi::Function ChakraRuntime::createFunctionFromHostFunction( // funcRef cannot out-live this Runtime, the reference stored in hostFuncProxy // stays valid during its lifetime. VerifyJsErrorElseThrow(JsSetObjectBeforeCollectCallback( - funcRef, - hostFuncProxyWrapper.get(), - [](JsRef ref, void *hostFuncProxyToDestroy) { + funcRef, hostFuncProxyWrapper.get(), [](JsRef ref, void *hostFuncProxyToDestroy) { // We wrap hostFuncProxyToDestroy in a unique_ptr to avoid calling // delete explicitly. - std::unique_ptr wrapper{ - static_cast(hostFuncProxyToDestroy)}; + std::unique_ptr wrapper{static_cast(hostFuncProxyToDestroy)}; })); // We only call hostFuncProxyWrapper.release() after @@ -573,17 +492,14 @@ facebook::jsi::Function ChakraRuntime::createFunctionFromHostFunction( // the HostFunctionProxy that hostFuncProxyWrapper used to own will be leaked. hostFuncProxyWrapper.release(); - facebook::jsi::Object hostFuncObj = - MakePointer(funcRef); + facebook::jsi::Object hostFuncObj = MakePointer(funcRef); // We do not use the function // Object::setProperty(Runtime & runtime, const char *name, T &&value) // here because it causes multiple copies of name. setPropertyValue( hostFuncObj, - createPropNameIDFromAscii( - g_functionIsHostFunctionPropName, - strlen(g_functionIsHostFunctionPropName)), + createPropNameIDFromAscii(g_functionIsHostFunctionPropName, strlen(g_functionIsHostFunctionPropName)), facebook::jsi::Value(true)); return hostFuncObj.getFunction(*this); @@ -599,39 +515,28 @@ facebook::jsi::Value ChakraRuntime::call( ChakraObjectRef thisRef = ToChakraObjectRef(jsThis); std::vector argRefs = ToChakraObjectRefs(args, count); - std::vector argsWithThis = - ConstructJsFunctionArguments(thisRef, argRefs); + std::vector argsWithThis = ConstructJsFunctionArguments(thisRef, argRefs); assert(argsWithThis.size() <= USHRT_MAX); JsValueRef result; VerifyJsErrorElseThrow(JsCallFunction( - GetChakraObjectRef(func), - argsWithThis.data(), - static_cast(argsWithThis.size()), - &result)); + GetChakraObjectRef(func), argsWithThis.data(), static_cast(argsWithThis.size()), &result)); return ToJsiValue(ChakraObjectRef(result)); } -facebook::jsi::Value ChakraRuntime::callAsConstructor( - const facebook::jsi::Function &func, - const facebook::jsi::Value *args, - size_t count) { +facebook::jsi::Value +ChakraRuntime::callAsConstructor(const facebook::jsi::Function &func, const facebook::jsi::Value *args, size_t count) { // We must store these ChakraObjectRefs on the stack to make sure that they do // not go out of scope when JsConstructObject is called. - ChakraObjectRef undefinedRef = - ToChakraObjectRef(facebook::jsi::Value::undefined()); + ChakraObjectRef undefinedRef = ToChakraObjectRef(facebook::jsi::Value::undefined()); std::vector argRefs = ToChakraObjectRefs(args, count); - std::vector argsWithThis = - ConstructJsFunctionArguments(undefinedRef, argRefs); + std::vector argsWithThis = ConstructJsFunctionArguments(undefinedRef, argRefs); assert(argsWithThis.size() <= USHRT_MAX); JsValueRef result; VerifyJsErrorElseThrow(JsConstructObject( - GetChakraObjectRef(func), - argsWithThis.data(), - static_cast(argsWithThis.size()), - &result)); + GetChakraObjectRef(func), argsWithThis.data(), static_cast(argsWithThis.size()), &result)); return ToJsiValue(ChakraObjectRef(result)); } @@ -644,43 +549,30 @@ void ChakraRuntime::popScope(Runtime::ScopeState *state) { VerifyJsErrorElseThrow(JsCollectGarbage(m_runtime)); } -bool ChakraRuntime::strictEquals( - const facebook::jsi::Symbol &a, - const facebook::jsi::Symbol &b) const { +bool ChakraRuntime::strictEquals(const facebook::jsi::Symbol &a, const facebook::jsi::Symbol &b) const { return CompareJsValues(GetChakraObjectRef(a), GetChakraObjectRef(b)); } -bool ChakraRuntime::strictEquals( - const facebook::jsi::String &a, - const facebook::jsi::String &b) const { +bool ChakraRuntime::strictEquals(const facebook::jsi::String &a, const facebook::jsi::String &b) const { return CompareJsValues(GetChakraObjectRef(a), GetChakraObjectRef(b)); } -bool ChakraRuntime::strictEquals( - const facebook::jsi::Object &a, - const facebook::jsi::Object &b) const { +bool ChakraRuntime::strictEquals(const facebook::jsi::Object &a, const facebook::jsi::Object &b) const { return CompareJsValues(GetChakraObjectRef(a), GetChakraObjectRef(b)); } -bool ChakraRuntime::instanceOf( - const facebook::jsi::Object &obj, - const facebook::jsi::Function &func) { +bool ChakraRuntime::instanceOf(const facebook::jsi::Object &obj, const facebook::jsi::Function &func) { bool result; - VerifyJsErrorElseThrow( - JsInstanceOf(GetChakraObjectRef(obj), GetChakraObjectRef(func), &result)); + VerifyJsErrorElseThrow(JsInstanceOf(GetChakraObjectRef(obj), GetChakraObjectRef(func), &result)); return result; } #pragma endregion Functions_inherited_from_Runtime -facebook::jsi::Value ChakraRuntime::GetProperty( - const facebook::jsi::Object &obj, - const char *const name) const { +facebook::jsi::Value ChakraRuntime::GetProperty(const facebook::jsi::Object &obj, const char *const name) const { // We have to use const_casts here because createPropNameIDFromAscii and // getProperty are not marked as const. - facebook::jsi::PropNameID propId = - const_cast(this)->createPropNameIDFromAscii( - name, strlen(name)); + facebook::jsi::PropNameID propId = const_cast(this)->createPropNameIDFromAscii(name, strlen(name)); return const_cast(this)->getProperty(obj, propId); } @@ -695,8 +587,7 @@ void ChakraRuntime::VerifyJsErrorElseThrow(JsErrorCode error) { case JsErrorScriptException: { JsValueRef jsError; VerifyChakraErrorElseThrow(JsGetAndClearException(&jsError)); - throw facebook::jsi::JSError( - "", *this, ToJsiValue(ChakraObjectRef(jsError))); + throw facebook::jsi::JSError("", *this, ToJsiValue(ChakraObjectRef(jsError))); break; } @@ -729,8 +620,7 @@ facebook::jsi::Value ChakraRuntime::ToJsiValue(ChakraObjectRef &&ref) { break; } case JsString: { - return facebook::jsi::Value( - *this, MakePointer(std::move(ref))); + return facebook::jsi::Value(*this, MakePointer(std::move(ref))); break; } case JsBoolean: { @@ -740,8 +630,7 @@ facebook::jsi::Value ChakraRuntime::ToJsiValue(ChakraObjectRef &&ref) { break; } case JsSymbol: { - return facebook::jsi::Value( - *this, MakePointer(std::move(ref))); + return facebook::jsi::Value(*this, MakePointer(std::move(ref))); break; } case JsObject: @@ -751,8 +640,7 @@ facebook::jsi::Value ChakraRuntime::ToJsiValue(ChakraObjectRef &&ref) { case JsArrayBuffer: case JsTypedArray: case JsDataView: { - return facebook::jsi::Value( - *this, MakePointer(std::move(ref))); + return facebook::jsi::Value(*this, MakePointer(std::move(ref))); break; } default: { @@ -766,8 +654,7 @@ facebook::jsi::Value ChakraRuntime::ToJsiValue(ChakraObjectRef &&ref) { std::terminate(); } -ChakraObjectRef ChakraRuntime::ToChakraObjectRef( - const facebook::jsi::Value &value) { +ChakraObjectRef ChakraRuntime::ToChakraObjectRef(const facebook::jsi::Value &value) { if (value.isUndefined()) { JsValueRef ref; VerifyJsErrorElseThrow(JsGetUndefinedValue(&ref)); @@ -803,9 +690,7 @@ ChakraObjectRef ChakraRuntime::ToChakraObjectRef( } } -std::vector ChakraRuntime::ToChakraObjectRefs( - const facebook::jsi::Value *value, - size_t count) { +std::vector ChakraRuntime::ToChakraObjectRefs(const facebook::jsi::Value *value, size_t count) { std::vector result{}; for (unsigned int i = 0; i < count; ++i) { @@ -822,11 +707,9 @@ JsValueRef CALLBACK ChakraRuntime::HostFunctionCall( JsValueRef *argumentsIncThis, unsigned short argumentCountIncThis, void *callbackState) { - HostFunctionProxy *hostFuncProxy = - static_cast(callbackState); + HostFunctionProxy *hostFuncProxy = static_cast(callbackState); ChakraRuntime &runtime = hostFuncProxy->GetRuntime(); - const facebook::jsi::HostFunctionType &hostFunc = - hostFuncProxy->GetHostFunction(); + const facebook::jsi::HostFunctionType &hostFunc = hostFuncProxy->GetHostFunction(); constexpr uint32_t maxStackArgCount = 8; facebook::jsi::Value stackArgs[maxStackArgCount]; @@ -839,30 +722,25 @@ JsValueRef CALLBACK ChakraRuntime::HostFunctionCall( if (argumentCount > maxStackArgCount) { heapArgs = std::make_unique(argumentCount); for (size_t i = 1; i < argumentCountIncThis; i++) { - heapArgs[i - 1] = - runtime.ToJsiValue(ChakraObjectRef(argumentsIncThis[i])); + heapArgs[i - 1] = runtime.ToJsiValue(ChakraObjectRef(argumentsIncThis[i])); } args = heapArgs.get(); } else { for (size_t i = 1; i < argumentCountIncThis; i++) { - stackArgs[i - 1] = - runtime.ToJsiValue(ChakraObjectRef(argumentsIncThis[i])); + stackArgs[i - 1] = runtime.ToJsiValue(ChakraObjectRef(argumentsIncThis[i])); } args = stackArgs; } JsValueRef result = JS_INVALID_REFERENCE; - facebook::jsi::Value thisVal = - runtime.ToJsiValue(ChakraObjectRef(argumentsIncThis[0])); + facebook::jsi::Value thisVal = runtime.ToJsiValue(ChakraObjectRef(argumentsIncThis[0])); try { - result = runtime.ToChakraObjectRef( - hostFunc(runtime, thisVal, args, argumentCount)); + result = runtime.ToChakraObjectRef(hostFunc(runtime, thisVal, args, argumentCount)); } catch (const facebook::jsi::JSError &error) { - runtime.VerifyJsErrorElseThrow( - JsSetException(runtime.ToChakraObjectRef(error.value()))); + runtime.VerifyJsErrorElseThrow(JsSetException(runtime.ToChakraObjectRef(error.value()))); } catch (const std::exception &exn) { std::string message = "Exception in HostFunction: "; @@ -891,8 +769,7 @@ facebook::jsi::Value ChakraRuntime::HostObjectGetTrap( // arg[2] is either the Proxy or an object that inherits from the proxy. // We don't use this parameter. if (count != 3) { - throw facebook::jsi::JSINativeException( - "The get() Proxy handler requires three arguments."); + throw facebook::jsi::JSINativeException("The get() Proxy handler requires three arguments."); } ChakraRuntime &chakraRuntime = static_cast(runtime); @@ -909,10 +786,7 @@ facebook::jsi::Value ChakraRuntime::HostObjectGetTrap( } else { std::shared_ptr &hostObject = *GetExternalData>(target); - return hostObject->get( - chakraRuntime, - chakraRuntime.createPropNameIDFromAscii( - propName.c_str(), propName.length())); + return hostObject->get(chakraRuntime, chakraRuntime.createPropNameIDFromAscii(propName.c_str(), propName.length())); } // Control flow should never reach here. @@ -935,8 +809,7 @@ facebook::jsi::Value ChakraRuntime::HostObjectSetTrap( // arg[3] is the object to which the assignment was originally directed. // We don't use this parameter. if (count != 4) { - throw facebook::jsi::JSINativeException( - "The set() Proxy handler requires four arguments."); + throw facebook::jsi::JSINativeException("The set() Proxy handler requires four arguments."); } ChakraRuntime &chakraRuntime = static_cast(runtime); @@ -944,13 +817,11 @@ facebook::jsi::Value ChakraRuntime::HostObjectSetTrap( if (propName == g_proxyIsHostObjectPropName) { throw facebook::jsi::JSINativeException( - std ::string{g_proxyIsHostObjectPropName} + - " is a reserved property and must not be changed."); + std ::string{g_proxyIsHostObjectPropName} + " is a reserved property and must not be changed."); } else if (propName == g_proxyGetHostObjectTargetPropName) { throw facebook::jsi::JSINativeException( - std ::string{g_proxyGetHostObjectTargetPropName} + - " is a reserved property and must not be changed."); + std ::string{g_proxyGetHostObjectTargetPropName} + " is a reserved property and must not be changed."); } else { ChakraObjectRef target = chakraRuntime.ToChakraObjectRef(args[0]); @@ -959,10 +830,7 @@ facebook::jsi::Value ChakraRuntime::HostObjectSetTrap( *GetExternalData>(target); hostObject->set( - chakraRuntime, - chakraRuntime.createPropNameIDFromAscii( - propName.c_str(), propName.length()), - args[2]); + chakraRuntime, chakraRuntime.createPropNameIDFromAscii(propName.c_str(), propName.length()), args[2]); return facebook::jsi::Value::undefined(); } @@ -980,8 +848,7 @@ facebook::jsi::Value ChakraRuntime::HostObjectOwnKeysTrap( // createHostObjectProxyHandler() and is unused. if (count != 1) { - throw facebook::jsi::JSINativeException( - "HostObjectOwnKeysTrap() requires one argument."); + throw facebook::jsi::JSINativeException("HostObjectOwnKeysTrap() requires one argument."); } ChakraRuntime &chakraRuntime = static_cast(runtime); @@ -1002,10 +869,7 @@ facebook::jsi::Value ChakraRuntime::HostObjectOwnKeysTrap( size_t index = 0; for (const std::string &key : dedupedOwnKeys) { - result.setValueAtIndex( - chakraRuntime, - index, - facebook::jsi::String::createFromUtf8(chakraRuntime, key)); + result.setValueAtIndex(chakraRuntime, index, facebook::jsi::String::createFromUtf8(chakraRuntime, key)); ++index; } @@ -1021,27 +885,15 @@ facebook::jsi::Object ChakraRuntime::createHostObjectProxyHandler() noexcept { constexpr const char *const setPropName = "set"; constexpr const char *const ownKeysPropName = "ownKeys"; - facebook::jsi::PropNameID getPropId = - createPropNameIDFromAscii(getPropName, strlen(getPropName)); - facebook::jsi::PropNameID setPropId = - createPropNameIDFromAscii(setPropName, strlen(setPropName)); - facebook::jsi::PropNameID ownKeysPropId = - createPropNameIDFromAscii(ownKeysPropName, strlen(ownKeysPropName)); + facebook::jsi::PropNameID getPropId = createPropNameIDFromAscii(getPropName, strlen(getPropName)); + facebook::jsi::PropNameID setPropId = createPropNameIDFromAscii(setPropName, strlen(setPropName)); + facebook::jsi::PropNameID ownKeysPropId = createPropNameIDFromAscii(ownKeysPropName, strlen(ownKeysPropName)); - handler.setProperty( - *this, - getPropName, - createFunctionFromHostFunction(getPropId, 2, HostObjectGetTrap)); + handler.setProperty(*this, getPropName, createFunctionFromHostFunction(getPropId, 2, HostObjectGetTrap)); - handler.setProperty( - *this, - setPropName, - createFunctionFromHostFunction(setPropId, 3, HostObjectSetTrap)); + handler.setProperty(*this, setPropName, createFunctionFromHostFunction(setPropId, 3, HostObjectSetTrap)); - handler.setProperty( - *this, - ownKeysPropName, - createFunctionFromHostFunction(ownKeysPropId, 1, HostObjectOwnKeysTrap)); + handler.setProperty(*this, ownKeysPropName, createFunctionFromHostFunction(ownKeysPropId, 1, HostObjectOwnKeysTrap)); return handler; } @@ -1058,11 +910,8 @@ void ChakraRuntime::setupMemoryTracker() noexcept { JsSetRuntimeMemoryAllocationCallback( m_runtime, runtimeArgs().memoryTracker.get(), - [](void *callbackState, - JsMemoryEventType allocationEvent, - size_t allocationSize) -> bool { - auto memoryTrackerPtr = - static_cast(callbackState); + [](void *callbackState, JsMemoryEventType allocationEvent, size_t allocationSize) -> bool { + auto memoryTrackerPtr = static_cast(callbackState); switch (allocationEvent) { case JsMemoryAllocate: memoryTrackerPtr->OnAllocation(allocationSize); @@ -1085,8 +934,7 @@ void ChakraRuntime::setupMemoryTracker() noexcept { std::once_flag ChakraRuntime::s_runtimeVersionInitFlag; uint64_t ChakraRuntime::s_runtimeVersion = 0; -std::unique_ptr makeChakraRuntime( - ChakraRuntimeArgs &&args) noexcept { +std::unique_ptr makeChakraRuntime(ChakraRuntimeArgs &&args) noexcept { return std::make_unique(std::move(args)); } diff --git a/vnext/JSI/Shared/ChakraRuntime.h b/vnext/JSI/Shared/ChakraRuntime.h index a339139317a..9dbdf49c029 100644 --- a/vnext/JSI/Shared/ChakraRuntime.h +++ b/vnext/JSI/Shared/ChakraRuntime.h @@ -45,8 +45,7 @@ class ChakraRuntime : public facebook::jsi::Runtime { std::string sourceURL) override; facebook::jsi::Value evaluatePreparedJavaScript( - const std::shared_ptr &js) - override; + const std::shared_ptr &js) override; facebook::jsi::Object global() override; @@ -70,49 +69,29 @@ class ChakraRuntime : public facebook::jsi::Runtime { PointerValue *cloneObject(const PointerValue *pointerValue) override; PointerValue *clonePropNameID(const PointerValue *pointerValue) override; - facebook::jsi::PropNameID createPropNameIDFromAscii( - const char *str, - size_t length) override; - facebook::jsi::PropNameID createPropNameIDFromUtf8( - const uint8_t *utf8, - size_t length) override; - facebook::jsi::PropNameID createPropNameIDFromString( - const facebook::jsi::String &str) override; + facebook::jsi::PropNameID createPropNameIDFromAscii(const char *str, size_t length) override; + facebook::jsi::PropNameID createPropNameIDFromUtf8(const uint8_t *utf8, size_t length) override; + facebook::jsi::PropNameID createPropNameIDFromString(const facebook::jsi::String &str) override; std::string utf8(const facebook::jsi::PropNameID &id) override; - bool compare( - const facebook::jsi::PropNameID &lhs, - const facebook::jsi::PropNameID &rhs) override; + bool compare(const facebook::jsi::PropNameID &lhs, const facebook::jsi::PropNameID &rhs) override; std::string symbolToString(const facebook::jsi::Symbol &s) override; // Despite its name, createPropNameIDFromAscii is the same function as // createStringFromUtf8. - facebook::jsi::String createStringFromAscii(const char *str, size_t length) - override; - facebook::jsi::String createStringFromUtf8(const uint8_t *utf8, size_t length) - override; + facebook::jsi::String createStringFromAscii(const char *str, size_t length) override; + facebook::jsi::String createStringFromUtf8(const uint8_t *utf8, size_t length) override; std::string utf8(const facebook::jsi::String &str) override; facebook::jsi::Object createObject() override; - facebook::jsi::Object createObject( - std::shared_ptr ho) override; - std::shared_ptr getHostObject( - const facebook::jsi::Object &) override; - facebook::jsi::HostFunctionType &getHostFunction( - const facebook::jsi::Function &) override; - - facebook::jsi::Value getProperty( - const facebook::jsi::Object &obj, - const facebook::jsi::PropNameID &name) override; - facebook::jsi::Value getProperty( - const facebook::jsi::Object &obj, - const facebook::jsi::String &name) override; - bool hasProperty( - const facebook::jsi::Object &obj, - const facebook::jsi::PropNameID &name) override; - bool hasProperty( - const facebook::jsi::Object &obj, - const facebook::jsi::String &name) override; + facebook::jsi::Object createObject(std::shared_ptr ho) override; + std::shared_ptr getHostObject(const facebook::jsi::Object &) override; + facebook::jsi::HostFunctionType &getHostFunction(const facebook::jsi::Function &) override; + + facebook::jsi::Value getProperty(const facebook::jsi::Object &obj, const facebook::jsi::PropNameID &name) override; + facebook::jsi::Value getProperty(const facebook::jsi::Object &obj, const facebook::jsi::String &name) override; + bool hasProperty(const facebook::jsi::Object &obj, const facebook::jsi::PropNameID &name) override; + bool hasProperty(const facebook::jsi::Object &obj, const facebook::jsi::String &name) override; void setPropertyValue( facebook::jsi::Object &obj, const facebook::jsi::PropNameID &name, @@ -129,13 +108,10 @@ class ChakraRuntime : public facebook::jsi::Runtime { bool isHostFunction(const facebook::jsi::Function &func) const override; // Returns the names of all enumerable properties of an object. This // corresponds the properties iterated through by the JavaScript for..in loop. - facebook::jsi::Array getPropertyNames( - const facebook::jsi::Object &obj) override; + facebook::jsi::Array getPropertyNames(const facebook::jsi::Object &obj) override; - facebook::jsi::WeakObject createWeakObject( - const facebook::jsi::Object &obj) override; - facebook::jsi::Value lockWeakObject( - const facebook::jsi::WeakObject &weakObj) override; + facebook::jsi::WeakObject createWeakObject(const facebook::jsi::Object &obj) override; + facebook::jsi::Value lockWeakObject(const facebook::jsi::WeakObject &weakObj) override; facebook::jsi::Array createArray(size_t length) override; size_t size(const facebook::jsi::Array &arr) override; @@ -144,13 +120,8 @@ class ChakraRuntime : public facebook::jsi::Runtime { // ArrayBuffer. The returned buffer pointer does not count as a reference to // the ArrayBuffer for the purpose of garbage collection. uint8_t *data(const facebook::jsi::ArrayBuffer &arrBuf) override; - facebook::jsi::Value getValueAtIndex( - const facebook::jsi::Array &arr, - size_t index) override; - void setValueAtIndexImpl( - facebook::jsi::Array &arr, - size_t index, - const facebook::jsi::Value &value) override; + facebook::jsi::Value getValueAtIndex(const facebook::jsi::Array &arr, size_t index) override; + void setValueAtIndexImpl(facebook::jsi::Array &arr, size_t index, const facebook::jsi::Value &value) override; facebook::jsi::Function createFunctionFromHostFunction( const facebook::jsi::PropNameID &name, @@ -161,29 +132,19 @@ class ChakraRuntime : public facebook::jsi::Runtime { const facebook::jsi::Value &jsThis, const facebook::jsi::Value *args, size_t count) override; - facebook::jsi::Value callAsConstructor( - const facebook::jsi::Function &func, - const facebook::jsi::Value *args, - size_t count) override; + facebook::jsi::Value + callAsConstructor(const facebook::jsi::Function &func, const facebook::jsi::Value *args, size_t count) override; // For now, pushing a scope does nothing, and popping a scope forces the // JavaScript garbage collector to run. ScopeState *pushScope() override; void popScope(ScopeState *) override; - bool strictEquals( - const facebook::jsi::Symbol &a, - const facebook::jsi::Symbol &b) const override; - bool strictEquals( - const facebook::jsi::String &a, - const facebook::jsi::String &b) const override; - bool strictEquals( - const facebook::jsi::Object &a, - const facebook::jsi::Object &b) const override; + bool strictEquals(const facebook::jsi::Symbol &a, const facebook::jsi::Symbol &b) const override; + bool strictEquals(const facebook::jsi::String &a, const facebook::jsi::String &b) const override; + bool strictEquals(const facebook::jsi::Object &a, const facebook::jsi::Object &b) const override; - bool instanceOf( - const facebook::jsi::Object &obj, - const facebook::jsi::Function &func) override; + bool instanceOf(const facebook::jsi::Object &obj, const facebook::jsi::Function &func) override; #pragma endregion Functions_inherited_from_Runtime @@ -200,9 +161,7 @@ class ChakraRuntime : public facebook::jsi::Runtime { // Object::getProperty, but without the extra overhead. This function is // declared as const so that it can be used when implementing // isHostFunction and isHostObject. - facebook::jsi::Value GetProperty( - const facebook::jsi::Object &obj, - const char *const name) const; + facebook::jsi::Value GetProperty(const facebook::jsi::Object &obj, const char *const name) const; void VerifyJsErrorElseThrow(JsErrorCode error); @@ -237,14 +196,10 @@ class ChakraRuntime : public facebook::jsi::Runtime { // Declaring ~ChakraPointerValueTemplate() private prevents the compiler // from implicitly generating the following functions, so we have to tell // the compiler to do so. - ChakraPointerValueTemplate( - const ChakraPointerValueTemplate &other) noexcept = default; - ChakraPointerValueTemplate(ChakraPointerValueTemplate &&other) noexcept = - default; - ChakraPointerValueTemplate &operator=( - const ChakraPointerValueTemplate &rhs) noexcept = default; - ChakraPointerValueTemplate &operator=( - ChakraPointerValueTemplate &&rhs) noexcept = default; + ChakraPointerValueTemplate(const ChakraPointerValueTemplate &other) noexcept = default; + ChakraPointerValueTemplate(ChakraPointerValueTemplate &&other) noexcept = default; + ChakraPointerValueTemplate &operator=(const ChakraPointerValueTemplate &rhs) noexcept = default; + ChakraPointerValueTemplate &operator=(ChakraPointerValueTemplate &&rhs) noexcept = default; inline void invalidate() noexcept override { delete this; @@ -279,25 +234,19 @@ class ChakraRuntime : public facebook::jsi::Runtime { } // The pointer passed to this function must point to a ChakraPointerValue. - inline static ChakraPointerValue *CloneChakraPointerValue( - const PointerValue *pointerValue) { - return new ChakraPointerValue( - *(static_cast(pointerValue))); + inline static ChakraPointerValue *CloneChakraPointerValue(const PointerValue *pointerValue) { + return new ChakraPointerValue(*(static_cast(pointerValue))); } // The jsi::Pointer passed to this function must hold a ChakraPointerValue. - inline static const ChakraObjectRef &GetChakraObjectRef( - const facebook::jsi::Pointer &p) { - return static_cast(getPointerValue(p)) - ->GetRef(); + inline static const ChakraObjectRef &GetChakraObjectRef(const facebook::jsi::Pointer &p) { + return static_cast(getPointerValue(p))->GetRef(); } // These three functions only performs shallow copies. facebook::jsi::Value ToJsiValue(ChakraObjectRef &&ref); ChakraObjectRef ToChakraObjectRef(const facebook::jsi::Value &value); - std::vector ToChakraObjectRefs( - const facebook::jsi::Value *value, - size_t count); + std::vector ToChakraObjectRefs(const facebook::jsi::Value *value, size_t count); // Host function and host object helpers static JsValueRef CALLBACK HostFunctionCall( @@ -327,17 +276,12 @@ class ChakraRuntime : public facebook::jsi::Runtime { facebook::jsi::Object createHostObjectProxyHandler() noexcept; // Promise Helpers + static void CALLBACK PromiseContinuationCallback(JsValueRef funcRef, void *callbackState) noexcept; static void CALLBACK - PromiseContinuationCallback(JsValueRef funcRef, void *callbackState) noexcept; - static void CALLBACK PromiseRejectionTrackerCallback( - JsValueRef promise, - JsValueRef reason, - bool handled, - void *callbackState); + PromiseRejectionTrackerCallback(JsValueRef promise, JsValueRef reason, bool handled, void *callbackState); void PromiseContinuation(JsValueRef value) noexcept; - void - PromiseRejectionTracker(JsValueRef promise, JsValueRef reason, bool handled); + void PromiseRejectionTracker(JsValueRef promise, JsValueRef reason, bool handled); void setupNativePromiseContinuation() noexcept; @@ -369,9 +313,7 @@ class ChakraRuntime : public facebook::jsi::Runtime { std::unique_ptr generatePreparedScript( const std::string &sourceURL, const facebook::jsi::Buffer &sourceBuffer) noexcept; - facebook::jsi::Value evaluateJavaScriptSimple( - const facebook::jsi::Buffer &buffer, - const std::string &sourceURL); + facebook::jsi::Value evaluateJavaScriptSimple(const facebook::jsi::Buffer &buffer, const std::string &sourceURL); bool evaluateSerializedScript( const facebook::jsi::Buffer &scriptBuffer, const facebook::jsi::Buffer &serializedScriptBuffer, @@ -399,8 +341,7 @@ class ChakraRuntime : public facebook::jsi::Runtime { // These buffers back the external array buffers that we handover to // ChakraCore. - std::vector> - m_pinnedPreparedScripts; + std::vector> m_pinnedPreparedScripts; std::string m_debugRuntimeName; int m_debugPort{0}; From 459fdce3ba23e83c33c4afe97e984901e60029c9 Mon Sep 17 00:00:00 2001 From: Yichen Yao Date: Thu, 24 Oct 2019 13:59:20 -0700 Subject: [PATCH 07/12] Change files --- ...indows-2019-10-24-13-59-20-ImproveHostObjAndFunc.json | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 change/react-native-windows-2019-10-24-13-59-20-ImproveHostObjAndFunc.json diff --git a/change/react-native-windows-2019-10-24-13-59-20-ImproveHostObjAndFunc.json b/change/react-native-windows-2019-10-24-13-59-20-ImproveHostObjAndFunc.json new file mode 100644 index 00000000000..81b358ae934 --- /dev/null +++ b/change/react-native-windows-2019-10-24-13-59-20-ImproveHostObjAndFunc.json @@ -0,0 +1,9 @@ +{ + "type": "none", + "comment": "Fix remaining JSI unit tests and some code clean up.", + "packageName": "react-native-windows", + "email": "yicyao@microsoft.com", + "commit": "1413e2f189dd7d59511c6220823c37c251b5de89", + "date": "2019-10-24T20:59:20.310Z", + "file": "D:\\Git\\hansenyy-react-native-windows-3\\change\\react-native-windows-2019-10-24-13-59-20-ImproveHostObjAndFunc.json" +} \ No newline at end of file From 9925f5007088669104e4911a004a8bff78f76367 Mon Sep 17 00:00:00 2001 From: Yichen Yao Date: Thu, 24 Oct 2019 14:31:12 -0700 Subject: [PATCH 08/12] Readying for code review. --- .../JsiRuntimeUnitTests.cpp | 4 +- vnext/JSI/Shared/ChakraObjectRef.h | 12 +- vnext/JSI/Shared/ChakraRuntime.cpp | 117 +++++++++--------- 3 files changed, 66 insertions(+), 67 deletions(-) diff --git a/vnext/JSI.Desktop.UnitTests/JsiRuntimeUnitTests.cpp b/vnext/JSI.Desktop.UnitTests/JsiRuntimeUnitTests.cpp index c7bf7a37aa5..60b2dab89b4 100644 --- a/vnext/JSI.Desktop.UnitTests/JsiRuntimeUnitTests.cpp +++ b/vnext/JSI.Desktop.UnitTests/JsiRuntimeUnitTests.cpp @@ -159,7 +159,6 @@ TEST_P(JsiRuntimeUnitTests, ObjectTest) { EXPECT_EQ(names.getValueAtIndex(rt, 0).getString(rt).utf8(rt), "a"); } -// TODO (yicyao): Fix this test. TEST_P(JsiRuntimeUnitTests, HostObjectTest) { class ConstantHostObject : public HostObject { Value get(Runtime &, const PropNameID &sym) override { @@ -618,8 +617,7 @@ TEST_P(JsiRuntimeUnitTests, HostFunctionTest) { function("function (f) { return f('A cat'); }").call(rt, callable).getString(rt).utf8(rt), "A cat was called with std::function::target"); EXPECT_TRUE(callable.isHostFunction(rt)); - // TODO (yicyao): Chakra(Core)Runtime currently does not support - // GetHostFunction. + // TODO (yicyao): Chakra(Core)Runtime currently does not support GetHostFunction. // EXPECT_NE(callable.getHostFunction(rt).target(), nullptr); std::string strval = "strval1"; diff --git a/vnext/JSI/Shared/ChakraObjectRef.h b/vnext/JSI/Shared/ChakraObjectRef.h index bea640515ca..3ccab6bb4d5 100644 --- a/vnext/JSI/Shared/ChakraObjectRef.h +++ b/vnext/JSI/Shared/ChakraObjectRef.h @@ -203,13 +203,15 @@ ChakraObjectRef ToJsObject(const std::shared_ptr &data) { } /** - * TODO (yicyao): Add documentation here. + * @param object A ChakraObjectRef returned by ToJsObject. + * + * @returns The backing external data for object. */ template -T *GetExternalData(const ChakraObjectRef &object) { +const std::shared_ptr &GetExternalData(const ChakraObjectRef &object) { void *data; VerifyChakraErrorElseThrow(JsGetExternalData(object, &data)); - return static_cast(data); + return *static_cast *>(data); } /** @@ -231,7 +233,9 @@ bool CompareJsValues(const ChakraObjectRef &jsValue1, const ChakraObjectRef &jsV bool CompareJsPropertyIds(const ChakraObjectRef &jsPropId1, const ChakraObjectRef &jsPropId2); /** - * TODO (yicyao): Add documentation here. + * @brief Cause a JS Error to be thrown in the engine's current context. + * + * @param message The message of the JS Error thrown. */ void ThrowJsException(const std::string_view &message); diff --git a/vnext/JSI/Shared/ChakraRuntime.cpp b/vnext/JSI/Shared/ChakraRuntime.cpp index ea5b1f1d77f..6a225775af1 100644 --- a/vnext/JSI/Shared/ChakraRuntime.cpp +++ b/vnext/JSI/Shared/ChakraRuntime.cpp @@ -297,7 +297,7 @@ std::shared_ptr ChakraRuntime::getHostObject(const fa facebook::jsi::Object target = obj.getPropertyAsObject(*this, g_proxyGetHostObjectTargetPropName); - return *GetExternalData>(GetChakraObjectRef(target)); + return GetExternalData(GetChakraObjectRef(target)); } facebook::jsi::HostFunctionType &ChakraRuntime::getHostFunction(const facebook::jsi::Function &obj) { @@ -701,59 +701,6 @@ std::vector ChakraRuntime::ToChakraObjectRefs(const facebook::j return result; } -JsValueRef CALLBACK ChakraRuntime::HostFunctionCall( - JsValueRef callee, - bool isConstructCall, - JsValueRef *argumentsIncThis, - unsigned short argumentCountIncThis, - void *callbackState) { - HostFunctionProxy *hostFuncProxy = static_cast(callbackState); - ChakraRuntime &runtime = hostFuncProxy->GetRuntime(); - const facebook::jsi::HostFunctionType &hostFunc = hostFuncProxy->GetHostFunction(); - - constexpr uint32_t maxStackArgCount = 8; - facebook::jsi::Value stackArgs[maxStackArgCount]; - std::unique_ptr heapArgs = nullptr; - facebook::jsi::Value *args = nullptr; - - // Accounting for 'this' object at 0 - unsigned short argumentCount = argumentCountIncThis - 1; - - if (argumentCount > maxStackArgCount) { - heapArgs = std::make_unique(argumentCount); - for (size_t i = 1; i < argumentCountIncThis; i++) { - heapArgs[i - 1] = runtime.ToJsiValue(ChakraObjectRef(argumentsIncThis[i])); - } - args = heapArgs.get(); - - } else { - for (size_t i = 1; i < argumentCountIncThis; i++) { - stackArgs[i - 1] = runtime.ToJsiValue(ChakraObjectRef(argumentsIncThis[i])); - } - args = stackArgs; - } - - JsValueRef result = JS_INVALID_REFERENCE; - facebook::jsi::Value thisVal = runtime.ToJsiValue(ChakraObjectRef(argumentsIncThis[0])); - - try { - result = runtime.ToChakraObjectRef(hostFunc(runtime, thisVal, args, argumentCount)); - - } catch (const facebook::jsi::JSError &error) { - runtime.VerifyJsErrorElseThrow(JsSetException(runtime.ToChakraObjectRef(error.value()))); - - } catch (const std::exception &exn) { - std::string message = "Exception in HostFunction: "; - message += exn.what(); - ThrowJsException(message); - - } catch (...) { - ThrowJsException("Exception in HostFunction: "); - } - - return result; -} - facebook::jsi::Value ChakraRuntime::HostObjectGetTrap( Runtime &runtime, const facebook::jsi::Value & /*thisVal*/, @@ -784,8 +731,7 @@ facebook::jsi::Value ChakraRuntime::HostObjectGetTrap( return chakraRuntime.ToJsiValue(std::move(target)); } else { - std::shared_ptr &hostObject = - *GetExternalData>(target); + const std::shared_ptr &hostObject = GetExternalData(target); return hostObject->get(chakraRuntime, chakraRuntime.createPropNameIDFromAscii(propName.c_str(), propName.length())); } @@ -826,8 +772,7 @@ facebook::jsi::Value ChakraRuntime::HostObjectSetTrap( } else { ChakraObjectRef target = chakraRuntime.ToChakraObjectRef(args[0]); - std::shared_ptr &hostObject = - *GetExternalData>(target); + const std::shared_ptr &hostObject = GetExternalData(target); hostObject->set( chakraRuntime, chakraRuntime.createPropNameIDFromAscii(propName.c_str(), propName.length()), args[2]); @@ -854,8 +799,7 @@ facebook::jsi::Value ChakraRuntime::HostObjectOwnKeysTrap( ChakraRuntime &chakraRuntime = static_cast(runtime); ChakraObjectRef target = chakraRuntime.ToChakraObjectRef(args[0]); - std::shared_ptr &hostObject = - *GetExternalData>(target); + const std::shared_ptr &hostObject = GetExternalData(target); auto ownKeys = hostObject->getPropertyNames(chakraRuntime); @@ -931,6 +875,59 @@ void ChakraRuntime::setupMemoryTracker() noexcept { } } +JsValueRef CALLBACK ChakraRuntime::HostFunctionCall( + JsValueRef callee, + bool isConstructCall, + JsValueRef *argumentsIncThis, + unsigned short argumentCountIncThis, + void *callbackState) { + HostFunctionProxy *hostFuncProxy = static_cast(callbackState); + ChakraRuntime &runtime = hostFuncProxy->GetRuntime(); + const facebook::jsi::HostFunctionType &hostFunc = hostFuncProxy->GetHostFunction(); + + constexpr uint32_t maxStackArgCount = 8; + facebook::jsi::Value stackArgs[maxStackArgCount]; + std::unique_ptr heapArgs = nullptr; + facebook::jsi::Value *args = nullptr; + + // Accounting for 'this' object at 0 + unsigned short argumentCount = argumentCountIncThis - 1; + + if (argumentCount > maxStackArgCount) { + heapArgs = std::make_unique(argumentCount); + for (size_t i = 1; i < argumentCountIncThis; i++) { + heapArgs[i - 1] = runtime.ToJsiValue(ChakraObjectRef(argumentsIncThis[i])); + } + args = heapArgs.get(); + + } else { + for (size_t i = 1; i < argumentCountIncThis; i++) { + stackArgs[i - 1] = runtime.ToJsiValue(ChakraObjectRef(argumentsIncThis[i])); + } + args = stackArgs; + } + + JsValueRef result = JS_INVALID_REFERENCE; + facebook::jsi::Value thisVal = runtime.ToJsiValue(ChakraObjectRef(argumentsIncThis[0])); + + try { + result = runtime.ToChakraObjectRef(hostFunc(runtime, thisVal, args, argumentCount)); + + } catch (const facebook::jsi::JSError &error) { + runtime.VerifyJsErrorElseThrow(JsSetException(runtime.ToChakraObjectRef(error.value()))); + + } catch (const std::exception &exn) { + std::string message = "Exception in HostFunction: "; + message += exn.what(); + ThrowJsException(message); + + } catch (...) { + ThrowJsException("Exception in HostFunction: "); + } + + return result; +} + std::once_flag ChakraRuntime::s_runtimeVersionInitFlag; uint64_t ChakraRuntime::s_runtimeVersion = 0; From 158ab7a7d31aa48669ac96564d74eb0a2d59fb05 Mon Sep 17 00:00:00 2001 From: Yichen Yao Date: Thu, 24 Oct 2019 14:34:52 -0700 Subject: [PATCH 09/12] Reorder functions in ChakraRuntime.cpp for better formatting in CodeFlow. --- vnext/JSI/Shared/ChakraRuntime.cpp | 216 ++++++++++++++--------------- 1 file changed, 108 insertions(+), 108 deletions(-) diff --git a/vnext/JSI/Shared/ChakraRuntime.cpp b/vnext/JSI/Shared/ChakraRuntime.cpp index 6a225775af1..dad4cfd5e14 100644 --- a/vnext/JSI/Shared/ChakraRuntime.cpp +++ b/vnext/JSI/Shared/ChakraRuntime.cpp @@ -701,6 +701,114 @@ std::vector ChakraRuntime::ToChakraObjectRefs(const facebook::j return result; } +facebook::jsi::Object ChakraRuntime::createHostObjectProxyHandler() noexcept { + // TODO (yicyao): handler can be cached and reused for multiple HostObjects. + + facebook::jsi::Object handler = createObject(); + + constexpr const char *const getPropName = "get"; + constexpr const char *const setPropName = "set"; + constexpr const char *const ownKeysPropName = "ownKeys"; + + facebook::jsi::PropNameID getPropId = createPropNameIDFromAscii(getPropName, strlen(getPropName)); + facebook::jsi::PropNameID setPropId = createPropNameIDFromAscii(setPropName, strlen(setPropName)); + facebook::jsi::PropNameID ownKeysPropId = createPropNameIDFromAscii(ownKeysPropName, strlen(ownKeysPropName)); + + handler.setProperty(*this, getPropName, createFunctionFromHostFunction(getPropId, 2, HostObjectGetTrap)); + + handler.setProperty(*this, setPropName, createFunctionFromHostFunction(setPropId, 3, HostObjectSetTrap)); + + handler.setProperty(*this, ownKeysPropName, createFunctionFromHostFunction(ownKeysPropId, 1, HostObjectOwnKeysTrap)); + + return handler; +} + +void ChakraRuntime::setupMemoryTracker() noexcept { + if (runtimeArgs().memoryTracker) { + size_t initialMemoryUsage = 0; + JsGetRuntimeMemoryUsage(m_runtime, &initialMemoryUsage); + runtimeArgs().memoryTracker->Initialize(initialMemoryUsage); + + if (runtimeArgs().runtimeMemoryLimit > 0) + JsSetRuntimeMemoryLimit(m_runtime, runtimeArgs().runtimeMemoryLimit); + + JsSetRuntimeMemoryAllocationCallback( + m_runtime, + runtimeArgs().memoryTracker.get(), + [](void *callbackState, JsMemoryEventType allocationEvent, size_t allocationSize) -> bool { + auto memoryTrackerPtr = static_cast(callbackState); + switch (allocationEvent) { + case JsMemoryAllocate: + memoryTrackerPtr->OnAllocation(allocationSize); + break; + + case JsMemoryFree: + memoryTrackerPtr->OnDeallocation(allocationSize); + break; + + case JsMemoryFailure: + default: + break; + } + + return true; + }); + } +} + +JsValueRef CALLBACK ChakraRuntime::HostFunctionCall( + JsValueRef callee, + bool isConstructCall, + JsValueRef *argumentsIncThis, + unsigned short argumentCountIncThis, + void *callbackState) { + HostFunctionProxy *hostFuncProxy = static_cast(callbackState); + ChakraRuntime &runtime = hostFuncProxy->GetRuntime(); + const facebook::jsi::HostFunctionType &hostFunc = hostFuncProxy->GetHostFunction(); + + constexpr uint32_t maxStackArgCount = 8; + facebook::jsi::Value stackArgs[maxStackArgCount]; + std::unique_ptr heapArgs = nullptr; + facebook::jsi::Value *args = nullptr; + + // Accounting for 'this' object at 0 + unsigned short argumentCount = argumentCountIncThis - 1; + + if (argumentCount > maxStackArgCount) { + heapArgs = std::make_unique(argumentCount); + for (size_t i = 1; i < argumentCountIncThis; i++) { + heapArgs[i - 1] = runtime.ToJsiValue(ChakraObjectRef(argumentsIncThis[i])); + } + args = heapArgs.get(); + + } else { + for (size_t i = 1; i < argumentCountIncThis; i++) { + stackArgs[i - 1] = runtime.ToJsiValue(ChakraObjectRef(argumentsIncThis[i])); + } + args = stackArgs; + } + + JsValueRef result = JS_INVALID_REFERENCE; + facebook::jsi::Value thisVal = runtime.ToJsiValue(ChakraObjectRef(argumentsIncThis[0])); + + try { + result = runtime.ToChakraObjectRef(hostFunc(runtime, thisVal, args, argumentCount)); + + } catch (const facebook::jsi::JSError &error) { + runtime.VerifyJsErrorElseThrow(JsSetException(runtime.ToChakraObjectRef(error.value()))); + + } catch (const std::exception &exn) { + std::string message = "Exception in HostFunction: "; + message += exn.what(); + ThrowJsException(message); + + } catch (...) { + ThrowJsException("Exception in HostFunction: "); + } + + return result; +} + facebook::jsi::Value ChakraRuntime::HostObjectGetTrap( Runtime &runtime, const facebook::jsi::Value & /*thisVal*/, @@ -820,114 +928,6 @@ facebook::jsi::Value ChakraRuntime::HostObjectOwnKeysTrap( return result; } -facebook::jsi::Object ChakraRuntime::createHostObjectProxyHandler() noexcept { - // TODO (yicyao): handler can be cached and reused for multiple HostObjects. - - facebook::jsi::Object handler = createObject(); - - constexpr const char *const getPropName = "get"; - constexpr const char *const setPropName = "set"; - constexpr const char *const ownKeysPropName = "ownKeys"; - - facebook::jsi::PropNameID getPropId = createPropNameIDFromAscii(getPropName, strlen(getPropName)); - facebook::jsi::PropNameID setPropId = createPropNameIDFromAscii(setPropName, strlen(setPropName)); - facebook::jsi::PropNameID ownKeysPropId = createPropNameIDFromAscii(ownKeysPropName, strlen(ownKeysPropName)); - - handler.setProperty(*this, getPropName, createFunctionFromHostFunction(getPropId, 2, HostObjectGetTrap)); - - handler.setProperty(*this, setPropName, createFunctionFromHostFunction(setPropId, 3, HostObjectSetTrap)); - - handler.setProperty(*this, ownKeysPropName, createFunctionFromHostFunction(ownKeysPropId, 1, HostObjectOwnKeysTrap)); - - return handler; -} - -void ChakraRuntime::setupMemoryTracker() noexcept { - if (runtimeArgs().memoryTracker) { - size_t initialMemoryUsage = 0; - JsGetRuntimeMemoryUsage(m_runtime, &initialMemoryUsage); - runtimeArgs().memoryTracker->Initialize(initialMemoryUsage); - - if (runtimeArgs().runtimeMemoryLimit > 0) - JsSetRuntimeMemoryLimit(m_runtime, runtimeArgs().runtimeMemoryLimit); - - JsSetRuntimeMemoryAllocationCallback( - m_runtime, - runtimeArgs().memoryTracker.get(), - [](void *callbackState, JsMemoryEventType allocationEvent, size_t allocationSize) -> bool { - auto memoryTrackerPtr = static_cast(callbackState); - switch (allocationEvent) { - case JsMemoryAllocate: - memoryTrackerPtr->OnAllocation(allocationSize); - break; - - case JsMemoryFree: - memoryTrackerPtr->OnDeallocation(allocationSize); - break; - - case JsMemoryFailure: - default: - break; - } - - return true; - }); - } -} - -JsValueRef CALLBACK ChakraRuntime::HostFunctionCall( - JsValueRef callee, - bool isConstructCall, - JsValueRef *argumentsIncThis, - unsigned short argumentCountIncThis, - void *callbackState) { - HostFunctionProxy *hostFuncProxy = static_cast(callbackState); - ChakraRuntime &runtime = hostFuncProxy->GetRuntime(); - const facebook::jsi::HostFunctionType &hostFunc = hostFuncProxy->GetHostFunction(); - - constexpr uint32_t maxStackArgCount = 8; - facebook::jsi::Value stackArgs[maxStackArgCount]; - std::unique_ptr heapArgs = nullptr; - facebook::jsi::Value *args = nullptr; - - // Accounting for 'this' object at 0 - unsigned short argumentCount = argumentCountIncThis - 1; - - if (argumentCount > maxStackArgCount) { - heapArgs = std::make_unique(argumentCount); - for (size_t i = 1; i < argumentCountIncThis; i++) { - heapArgs[i - 1] = runtime.ToJsiValue(ChakraObjectRef(argumentsIncThis[i])); - } - args = heapArgs.get(); - - } else { - for (size_t i = 1; i < argumentCountIncThis; i++) { - stackArgs[i - 1] = runtime.ToJsiValue(ChakraObjectRef(argumentsIncThis[i])); - } - args = stackArgs; - } - - JsValueRef result = JS_INVALID_REFERENCE; - facebook::jsi::Value thisVal = runtime.ToJsiValue(ChakraObjectRef(argumentsIncThis[0])); - - try { - result = runtime.ToChakraObjectRef(hostFunc(runtime, thisVal, args, argumentCount)); - - } catch (const facebook::jsi::JSError &error) { - runtime.VerifyJsErrorElseThrow(JsSetException(runtime.ToChakraObjectRef(error.value()))); - - } catch (const std::exception &exn) { - std::string message = "Exception in HostFunction: "; - message += exn.what(); - ThrowJsException(message); - - } catch (...) { - ThrowJsException("Exception in HostFunction: "); - } - - return result; -} - std::once_flag ChakraRuntime::s_runtimeVersionInitFlag; uint64_t ChakraRuntime::s_runtimeVersion = 0; From 0e16a78e59c98d2ab1b24d387932c7871b870253 Mon Sep 17 00:00:00 2001 From: Yichen Yao Date: Thu, 24 Oct 2019 14:39:36 -0700 Subject: [PATCH 10/12] Fix minor typo. --- vnext/JSI.Desktop.UnitTests/JsiRuntimeUnitTests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vnext/JSI.Desktop.UnitTests/JsiRuntimeUnitTests.cpp b/vnext/JSI.Desktop.UnitTests/JsiRuntimeUnitTests.cpp index 60b2dab89b4..372adb6e7d0 100644 --- a/vnext/JSI.Desktop.UnitTests/JsiRuntimeUnitTests.cpp +++ b/vnext/JSI.Desktop.UnitTests/JsiRuntimeUnitTests.cpp @@ -617,7 +617,7 @@ TEST_P(JsiRuntimeUnitTests, HostFunctionTest) { function("function (f) { return f('A cat'); }").call(rt, callable).getString(rt).utf8(rt), "A cat was called with std::function::target"); EXPECT_TRUE(callable.isHostFunction(rt)); - // TODO (yicyao): Chakra(Core)Runtime currently does not support GetHostFunction. + // TODO (yicyao): Chakra(Core)Runtime currently does not support getHostFunction. // EXPECT_NE(callable.getHostFunction(rt).target(), nullptr); std::string strval = "strval1"; From ec2d847287bbe00e0c4e1c12ab690e2b28f2a156 Mon Sep 17 00:00:00 2001 From: Yichen Yao Date: Thu, 24 Oct 2019 16:03:22 -0700 Subject: [PATCH 11/12] Add a bootstrap bundle for JS snippets needed in ChakraRuntime. --- vnext/JSI/Shared/ChakraRuntime.cpp | 66 +++++++++++++----------------- 1 file changed, 28 insertions(+), 38 deletions(-) diff --git a/vnext/JSI/Shared/ChakraRuntime.cpp b/vnext/JSI/Shared/ChakraRuntime.cpp index dad4cfd5e14..ef0b596b944 100644 --- a/vnext/JSI/Shared/ChakraRuntime.cpp +++ b/vnext/JSI/Shared/ChakraRuntime.cpp @@ -20,10 +20,28 @@ namespace Microsoft::JSI { namespace { -constexpr const char *const g_functionIsHostFunctionPropName = "$$FunctionIsHostFunction$$"; +constexpr const char *const g_bootstrapBundleSource = + "function $$ChakraRuntimeGetPropertyNames$$(obj)\n" + "{\n" + " var propertyNames = []\n" + " for (propertyName in obj) \n" + " {\n" + " propertyNames.push(propertyName)\n" + " }\n" + " return propertyNames\n" + "}\n" + "function $$ChakraRuntimeProxyConstructor$$(target, handler)\n" + "{\n" + " return new Proxy(target, handler)\n" + "}"; + +constexpr const char *const g_getPropertyNamesBootstrapFuncName = "$$ChakraRuntimeGetPropertyNames$$"; +constexpr const char *const g_proxyConstructorBootstrapFuncName = "$$ChakraRuntimeProxyConstructor$$"; -constexpr const char *const g_proxyIsHostObjectPropName = "$$ProxyIsHostObject$$"; constexpr const char *const g_proxyGetHostObjectTargetPropName = "$$ProxyGetHostObjectTarget$$"; +constexpr const char *const g_proxyIsHostObjectPropName = "$$ProxyIsHostObject$$"; + +constexpr const char *const g_functionIsHostFunctionPropName = "$$FunctionIsHostFunction$$"; class HostFunctionProxy { public: @@ -84,6 +102,9 @@ ChakraRuntime::ChakraRuntime(ChakraRuntimeArgs &&args) noexcept : m_args{std::mo setupNativePromiseContinuation(); std::call_once(s_runtimeVersionInitFlag, initRuntimeVersion); + + static facebook::jsi::StringBuffer bootstrapBundleSourceBuffer{g_bootstrapBundleSource}; + evaluateJavaScriptSimple(bootstrapBundleSourceBuffer, "ChakraRuntime_bootstrap.bundle"); } ChakraRuntime::~ChakraRuntime() noexcept { @@ -269,23 +290,10 @@ facebook::jsi::Object ChakraRuntime::createObject() { } facebook::jsi::Object ChakraRuntime::createObject(std::shared_ptr hostObject) { - constexpr const char *const jsProxyConstructorWrapperSource = - "(function()\n" - "{\n" - " return function(target, handler)\n" - " {\n" - " return new Proxy(target, handler)\n" - " }\n" - "})()"; - - static facebook::jsi::StringBuffer jsProxyConstructorWrapperSourceBuffer{jsProxyConstructorWrapperSource}; - - facebook::jsi::Function jsProxyConstructorWrapper = - evaluateJavaScriptSimple(jsProxyConstructorWrapperSourceBuffer, "ChakraRuntime_createObject.js") - .asObject(*this) - .asFunction(*this); - - return jsProxyConstructorWrapper + facebook::jsi::Function jsProxyConstructor = + global().getPropertyAsFunction(*this, g_proxyConstructorBootstrapFuncName); + + return jsProxyConstructor .call(*this, MakePointer(ToJsObject(hostObject)), createHostObjectProxyHandler()) .asObject(*this); } @@ -378,26 +386,8 @@ bool ChakraRuntime::isHostFunction(const facebook::jsi::Function &obj) const { } facebook::jsi::Array ChakraRuntime::getPropertyNames(const facebook::jsi::Object &object) { - constexpr const char *const jsGetPropertyNamesSource = - "(function()\n" - "{\n" - " return function(obj)\n" - " {\n" - " var propertyNames = []\n" - " for (propertyName in obj) \n" - " {\n" - " propertyNames.push(propertyName)\n" - " }\n" - " return propertyNames\n" - " }\n" - "})()"; - - static facebook::jsi::StringBuffer jsGetPropertyNamesSourceBuffer{jsGetPropertyNamesSource}; - facebook::jsi::Function jsGetPropertyNames = - evaluateJavaScriptSimple(jsGetPropertyNamesSourceBuffer, "ChakraRuntime_getPropertyNames.js") - .asObject(*this) - .asFunction(*this); + global().getPropertyAsFunction(*this, g_getPropertyNamesBootstrapFuncName); facebook::jsi::Value objAsValue(*this, object); return call(jsGetPropertyNames, facebook::jsi::Value::undefined(), &objAsValue, 1).asObject(*this).asArray(*this); From 507922388a2f7d6d3b14434b557a414f37cc9b6d Mon Sep 17 00:00:00 2001 From: Yichen Yao Date: Thu, 24 Oct 2019 16:10:10 -0700 Subject: [PATCH 12/12] Reorder functions in ChakraRuntime.cpp to correspond to their order in ChakraRuntime.h. --- vnext/JSI/Shared/ChakraRuntime.cpp | 111 ++++++++++++++--------------- 1 file changed, 55 insertions(+), 56 deletions(-) diff --git a/vnext/JSI/Shared/ChakraRuntime.cpp b/vnext/JSI/Shared/ChakraRuntime.cpp index ef0b596b944..342d9b1174d 100644 --- a/vnext/JSI/Shared/ChakraRuntime.cpp +++ b/vnext/JSI/Shared/ChakraRuntime.cpp @@ -563,7 +563,6 @@ facebook::jsi::Value ChakraRuntime::GetProperty(const facebook::jsi::Object &obj // We have to use const_casts here because createPropNameIDFromAscii and // getProperty are not marked as const. facebook::jsi::PropNameID propId = const_cast(this)->createPropNameIDFromAscii(name, strlen(name)); - return const_cast(this)->getProperty(obj, propId); } @@ -691,61 +690,6 @@ std::vector ChakraRuntime::ToChakraObjectRefs(const facebook::j return result; } -facebook::jsi::Object ChakraRuntime::createHostObjectProxyHandler() noexcept { - // TODO (yicyao): handler can be cached and reused for multiple HostObjects. - - facebook::jsi::Object handler = createObject(); - - constexpr const char *const getPropName = "get"; - constexpr const char *const setPropName = "set"; - constexpr const char *const ownKeysPropName = "ownKeys"; - - facebook::jsi::PropNameID getPropId = createPropNameIDFromAscii(getPropName, strlen(getPropName)); - facebook::jsi::PropNameID setPropId = createPropNameIDFromAscii(setPropName, strlen(setPropName)); - facebook::jsi::PropNameID ownKeysPropId = createPropNameIDFromAscii(ownKeysPropName, strlen(ownKeysPropName)); - - handler.setProperty(*this, getPropName, createFunctionFromHostFunction(getPropId, 2, HostObjectGetTrap)); - - handler.setProperty(*this, setPropName, createFunctionFromHostFunction(setPropId, 3, HostObjectSetTrap)); - - handler.setProperty(*this, ownKeysPropName, createFunctionFromHostFunction(ownKeysPropId, 1, HostObjectOwnKeysTrap)); - - return handler; -} - -void ChakraRuntime::setupMemoryTracker() noexcept { - if (runtimeArgs().memoryTracker) { - size_t initialMemoryUsage = 0; - JsGetRuntimeMemoryUsage(m_runtime, &initialMemoryUsage); - runtimeArgs().memoryTracker->Initialize(initialMemoryUsage); - - if (runtimeArgs().runtimeMemoryLimit > 0) - JsSetRuntimeMemoryLimit(m_runtime, runtimeArgs().runtimeMemoryLimit); - - JsSetRuntimeMemoryAllocationCallback( - m_runtime, - runtimeArgs().memoryTracker.get(), - [](void *callbackState, JsMemoryEventType allocationEvent, size_t allocationSize) -> bool { - auto memoryTrackerPtr = static_cast(callbackState); - switch (allocationEvent) { - case JsMemoryAllocate: - memoryTrackerPtr->OnAllocation(allocationSize); - break; - - case JsMemoryFree: - memoryTrackerPtr->OnDeallocation(allocationSize); - break; - - case JsMemoryFailure: - default: - break; - } - - return true; - }); - } -} - JsValueRef CALLBACK ChakraRuntime::HostFunctionCall( JsValueRef callee, bool isConstructCall, @@ -918,6 +862,61 @@ facebook::jsi::Value ChakraRuntime::HostObjectOwnKeysTrap( return result; } +facebook::jsi::Object ChakraRuntime::createHostObjectProxyHandler() noexcept { + // TODO (yicyao): handler can be cached and reused for multiple HostObjects. + + facebook::jsi::Object handler = createObject(); + + constexpr const char *const getPropName = "get"; + constexpr const char *const setPropName = "set"; + constexpr const char *const ownKeysPropName = "ownKeys"; + + facebook::jsi::PropNameID getPropId = createPropNameIDFromAscii(getPropName, strlen(getPropName)); + facebook::jsi::PropNameID setPropId = createPropNameIDFromAscii(setPropName, strlen(setPropName)); + facebook::jsi::PropNameID ownKeysPropId = createPropNameIDFromAscii(ownKeysPropName, strlen(ownKeysPropName)); + + handler.setProperty(*this, getPropName, createFunctionFromHostFunction(getPropId, 2, HostObjectGetTrap)); + + handler.setProperty(*this, setPropName, createFunctionFromHostFunction(setPropId, 3, HostObjectSetTrap)); + + handler.setProperty(*this, ownKeysPropName, createFunctionFromHostFunction(ownKeysPropId, 1, HostObjectOwnKeysTrap)); + + return handler; +} + +void ChakraRuntime::setupMemoryTracker() noexcept { + if (runtimeArgs().memoryTracker) { + size_t initialMemoryUsage = 0; + JsGetRuntimeMemoryUsage(m_runtime, &initialMemoryUsage); + runtimeArgs().memoryTracker->Initialize(initialMemoryUsage); + + if (runtimeArgs().runtimeMemoryLimit > 0) + JsSetRuntimeMemoryLimit(m_runtime, runtimeArgs().runtimeMemoryLimit); + + JsSetRuntimeMemoryAllocationCallback( + m_runtime, + runtimeArgs().memoryTracker.get(), + [](void *callbackState, JsMemoryEventType allocationEvent, size_t allocationSize) -> bool { + auto memoryTrackerPtr = static_cast(callbackState); + switch (allocationEvent) { + case JsMemoryAllocate: + memoryTrackerPtr->OnAllocation(allocationSize); + break; + + case JsMemoryFree: + memoryTrackerPtr->OnDeallocation(allocationSize); + break; + + case JsMemoryFailure: + default: + break; + } + + return true; + }); + } +} + std::once_flag ChakraRuntime::s_runtimeVersionInitFlag; uint64_t ChakraRuntime::s_runtimeVersion = 0;