-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Update Chakra JSI implementation #5710
Conversation
vnext/Microsoft.ReactNative.sln
Outdated
@@ -138,20 +138,19 @@ Global | |||
Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems*{da8b35b3-da00-4b02-bde6-6a397b3fd46b}*SharedItemsImports = 9 | |||
include\Include.vcxitems*{ef074ba1-2d54-4d49-a28e-5e040b47cd2e}*SharedItemsImports = 9 | |||
Chakra\Chakra.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4 | |||
JSI\Shared\JSI.Shared.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shared [](start = 17, length = 6)
You might want to check with @julio 's change, he has a PR to remove JSI.Shared #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, we have discussed it with him yesterday: I have moved the new unit tests to Microsoft.ReactNative.ComponentTests. He will move the other files if I merge before his PR. #Resolved
vnext/JSI/Shared/ChakraRuntime.h
Outdated
if (JsRef ref = GetRef()) { | ||
// JSI allows the destructor to be run on a random thread. | ||
// To handle it correctly we must schedule a task to the thread associated | ||
// with the Chakra context. Fir now we just leak the value. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fir [](start = 36, length = 3)
Typo:
// with the Chakra context. Fir now we just leak the value. | |
For | |
``` #Resolved |
vnext/JSI/Shared/ChakraRuntime.h
Outdated
} catch (std::exception const &ex) { | ||
ChakraVerifyElseThrow(false, (std::string{"Exception in "} + methodName + ": " + ex.what()).c_str()); | ||
} catch (...) { | ||
ChakraVerifyElseThrow(false, (std::string{"Exception in "} + methodName + ": <unknown>").c_str()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ChakraVerifyElseThrow [](start = 6, length = 21)
Should there be a shortcut for this: ChakraThrow
#Resolved
vnext/JSI/Shared/ChakraRuntime.cpp
Outdated
std::vector<JsValueRef> argsWithThis = ConstructJsFunctionArguments(thisRef, argRefs); | ||
assert(argsWithThis.size() <= (std::numeric_limits<unsigned short>::max)()); | ||
size_t jsArgCount = count + 1; // for 'this' argument. | ||
ChakraVerifyElseThrow(jsArgCount <= MaxCallArgCount, "Argument count exceeds MaxCallArgCount"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MaxCallArgCount [](start = 38, length = 15)
Shouldn't this be a bit more dynamic? I.e. does JSI (or any egine for that) have a limit of 32 args?
Perhaps the code should be optimized for a few cases:
< 8 args, => on stack size 8
< 32 args => on stack size 32
= 32 args => on heap #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was able to specialize it for <= 8 and > 8 case
In reply to: 470186502 [](ancestors = 470186502,470106454)
vnext/JSI/Shared/ChakraRuntime.cpp
Outdated
std::array<JsValueRef, MaxCallArgCount> jsArgs; | ||
jsArgs[0] = m_undefinedValue; | ||
for (size_t i = 0; i < count; ++i) { | ||
jsArgs[i + 1] = ToJsValueRef(args[i]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
perhaps helper function to share with regular call... #Resolved
throw facebook::jsi::JSError(*this, ToJsiValue(jsError)); | ||
} else { | ||
std::ostringstream errorString; | ||
errorString << "A call to Chakra API returned error code 0x" << std::hex << errorCode << '.'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
std::hex << errorCode [](start = 68, length = 21)
Should this be in parenthesis? Or is <<
right associative? #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a correct syntax. It is left associate. To format number as hex we must first put std::hex into the stream. I think it changes the number format mode.
In reply to: 470111422 [](ancestors = 470111422)
if (GetValueType(message) == JsValueType::JsString) { | ||
// JSI unit tests expect V8 or JSC like message for stack overflow. | ||
if (StringToPointer(message) == L"Out of stack space") { | ||
SetProperty(jsError, m_propertyId.message, PointerToString(L"RangeError : Maximum call stack size exceeded")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just sharing: Is it me or is JSI inconsistent in the strings.. I see char in the method above (ThrowJSExceptoinOverride) and here we're using WCHAR for properties... #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Chakra is mostly UTF-16. We use it whenever possible to avoid the extra conversions. The standard exceptions and JSI API in general is mostly UTF-8. This is why we use UTF-8 to throw exceptions.
In reply to: 470114270 [](ancestors = 470114270)
JsValueRef message = GetProperty(jsError, m_propertyId.message); | ||
if (GetValueType(message) == JsValueType::JsString) { | ||
// JSI unit tests expect V8 or JSC like message for stack overflow. | ||
if (StringToPointer(message) == L"Out of stack space") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ut of stack spa [](start = 39, length = 15)
Bigger question on RN: Will all developer errors be EN-US/1033 or will we have to localize these? #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code is only to allow to pass the JSI tests. Localized messages are not used there. I do not know what is our approach to localized messages. I think RN or RNW do not support localization. Otherwise we would need to use a completely different set of APIs.
In reply to: 470115349 [](ancestors = 470115349)
#define ChakraVerifyElseCrash(condition, message) \ | ||
do { \ | ||
if (!(condition)) { \ | ||
assert(false && "Failed: " #condition && (message)); \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
&& [](start = 19, length = 2)
For my understanding: What does the '&&' represent here? #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a common way to provide a message for the assert macro. It accepts only boolean and && allows to add a message because the string is always true. The assert then show the whole text given to it.
In reply to: 470172679 [](ancestors = 470172679)
@@ -30,13 +27,12 @@ | |||
<ClInclude Include="$(MSBuildThisFileDirectory)ChakraRuntimeFactory.h"> | |||
<Filter>Header Files</Filter> | |||
</ClInclude> | |||
<ClInclude Include="$(MSBuildThisFileDirectory)ChakraApi.h" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Associate with "Header Files" filter? #Resolved
@@ -105,6 +105,8 @@ | |||
</AdditionalIncludeDirectories> | |||
<UseFullPaths>true</UseFullPaths> | |||
<CallingConvention>Cdecl</CallingConvention> | |||
<!-- RuntimeTypeInfo is required by JSI --> | |||
<RuntimeTypeInfo>true</RuntimeTypeInfo> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did your change introduce that, or was that always the case? #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is required by JSI. The MS.RN already uses RTTI. This adds the same to the RN.dll. We need to change the JSI to avoid RTTI.
In reply to: 470186379 [](ancestors = 470186379)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see MS.RN using RTTI via RuntimeTypeInfo, do you know where this is set?
In reply to: 470243854 [](ancestors = 470243854,470186379)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right: MS.RN explicitly disables RTTI. But, JSI needs it... I think it is an unpleasant JSI bug. Let me see how I can reduce the scope of RTTI here.
In reply to: 470472946 [](ancestors = 470472946,470243854,470186379)
C4245 - 'initializing': conversion from 'int' to 'unsigned __int64', signed/unsigned mismatch | ||
C4389 - '==': signed/unsigned mismatch | ||
--> | ||
<DisableSpecificWarnings>4245;4389;$(DisableSpecificWarnings)</DisableSpecificWarnings> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
4245;4389 [](start = 31, length = 9)
OOC, couldn't these warnings be quelled via static_casts in code? If so, what makes disabling them the better choice? #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The testlib.cpp is used from the RN npm. To fix it we need to change Hermes and RN code. This is why it is easier just to disable these warnings for now.
In reply to: 470189825 [](ancestors = 470189825)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we just disable them at the point where they are produced instead of disabling them everywhere in the project?
In reply to: 470244479 [](ancestors = 470244479,470189825)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file is coming from the NPM package. It is not part of our source tree. We cannot change the file. So, we are doing the next best thing: disable them for this file only in the project file. These warnings are not disabled for the whole project, they are disabled only for one file which comes from the RN NPM package. Note that they are inside of the ClCompile for testlib.cpp file only.
In reply to: 470473232 [](ancestors = 470473232,470244479,470189825)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
gotcha, github was only showing me a snippet and i hadn't clicked the little arrows to show where this was in #Resolved
@@ -158,6 +164,13 @@ | |||
</ClCompile> | |||
<ClInclude Include="CommonReaderTest.h" /> | |||
<ClInclude Include="pch/pch.h" /> | |||
<ClCompile Include="$(JSI_SourcePath)\jsi\test\testlib.cpp"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIRC we have a separate source copy of JSI tests in our repo, from before they were published. Consider deleting those and changing them to include from JSI SourcePath like this is doing.
Related to that, I'm not familiar enough with existing project structure, but could we reconcile having multiple projects running JSI tests? #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I want to address it in future. The issue is that the old tests that we run for the Desktop environment are also used for V8 and to replace them with the stock tests there will require fixing the V8 JSI. It seems to be out of scope of this PR.
We will reconcile the tests, but we should do it in future after Julio removes the JSI projects. I think it will be done as we go towards the single RN core DLL.
In reply to: 470192372 [](ancestors = 470192372)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Macros provide more flexibility to show assert and provide failure context. | ||
|
||
// Check condition and crash process if it fails. | ||
#define ChakraVerifyElseCrash(condition, message) \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ChakraVerifyElseCrash [](start = 8, length = 21)
Why not call it "VerifyElseCrash"? #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do not want to have a conflict here. As we are going toward reduced project structure - the effort driven by Julio - we can replace this one with the VerifyElseCrash. I cannot do it now because this code has no dependency on the Mso library.
In reply to: 470235462 [](ancestors = 470235462)
vnext/JSI/Shared/ChakraRuntime.h
Outdated
void invalidate() noexcept override {} | ||
|
||
JsRef GetRef() const noexcept { | ||
return m_ref; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
m_ref [](start = 13, length = 5)
(super nit) can we spell it out as m_jsRef? #Resolved
bool isConstructCall, | ||
JsValueRef *args, | ||
unsigned short argCount, | ||
void *callbackState) noexcept; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
; [](start = 35, length = 1)
(nit) can we add empty line between these for readability? #Resolved
// It avoids extra memory allocation by using an in-place storage. | ||
// It uses ChakraPointerValueView that does nothing in the invalidate() method. | ||
struct JsiValueView { | ||
JsiValueView(JsValueRef jsValue); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
final? #Resolved
// If number of arguments is below or equal to MaxStackArgCount, | ||
// then they are kept on call stack, otherwise arguments are allocated on heap. | ||
struct JsValueArgs { | ||
JsValueArgs(ChakraRuntime &rt, facebook::jsi::Value const &firstArg, Span<facebook::jsi::Value const> args); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
final? #Resolved
vnext/JSI/Shared/ChakraRuntime.h
Outdated
|
||
private: | ||
size_t m_count{}; | ||
std::array<JsValueRef, MaxStackArgCount> m_stackArgs; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does this ever mutate? #Resolved
// This class helps to use stack storage for passing arguments that must be temporary converted from | ||
// JsValueRef to facebook::jsi::Value. | ||
struct JsiValueViewArgs { | ||
JsiValueViewArgs(JsValueRef *args, size_t argCount) noexcept; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
final? #Resolved
// PropNameIDView helps to use the stack storage for temporary conversion from | ||
// JsPropertyIdRef to facebook::jsi::PropNameID. | ||
struct PropNameIDView { | ||
PropNameIDView(JsPropertyIdRef propertyId) noexcept; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
final? #Resolved
# Conflicts: # vnext/JSI/Shared/ScriptStore.h
/azp run PR |
Azure Pipelines successfully started running 1 pipeline(s). |
# Conflicts: # vnext/JSI/Desktop/ChakraJsiRuntime_core.cpp # vnext/JSI/Shared/ChakraRuntime.cpp # vnext/JSI/Shared/ChakraRuntime.h # vnext/JSI/Universal/ChakraJsiRuntime_edgemode.cpp
# Conflicts: # vnext/JSI/Shared/ChakraRuntime.cpp
* Move UIManager to NM2 infra * formatting * Update x86 def file * Change files * Add win32 as a valid platform to platformcolorexample.win32 (#6255) * Fix switch events (#6264) * Switch was generating spurious events which could result on two events (on/off) being sent before the first event was fully handled * Change files * Implement code review recommendations * Make sure that m_updating is not left in wrong state if toggle switch null * Address further review comment * Update Chakra JSI implementation (#5710) * Update Chakra JSI implementation * Change files * Move new Chakra JSI Universal tests into MS.RN.ComponentTests * Address PR feedback * Address PR feedback * Fix x64 release compilation error * Add CreateRuntime and DisposeRuntime * Confirm to JSI API changes * Remove JSI_SourcePath * Allow having empty strings as JS object property name * applying package updates ***NO_CI*** * Bump @types/react-native from 0.63.25 to 0.63.26 (#6273) * Bump beachball from 1.37.0 to 1.38.0 (#6276) * Bump @types/jest from 26.0.14 to 26.0.15 (#6274) * Drop Issue Requirement from Override Manifest Schema Validation (#6263) * Drop Issue Requirement from Override Manifest Schema Validation Most types of overrrides require an issue number. This is done on the schema level, and is done incosistently per-type, where some allow no issues or LEGACY_FIXME, where others allow none. This was done to add barriers to forking, but ends up being a bit to broad/heavy-handed, and the differences between requirements of override types is confusing. This changes makes the schema uniform, dropping the special LEGACY_FIXME issue and universally allowing missing issues. To guide engineers to the right path, we still do require issues depending on types in the override prompt, but these can be altered manually. * Change files * Update copies to undefined issue during manifest generation * serializeBase cleanup * Bump babel-jest from 26.5.2 to 26.6.0 (#6272) * Add yargs-parser and node-fetch resolutions to pass component governance (#6270) Closes #6266 Closes #6267 * Fix Crash when Logbox shown on pre-19H1 (#6283) * Fix Crash when Logbox shown on pre-19H1 ActualHeight was added to UIElement in 19H1, but is being used in native code for Logbox/legacy redbox. This will also cause integration tests to crash on redbox, since they're running on debug build on Windows Server 2019. Replace usages with ActualHeight/ActualWidth. Initial thoughts are to not backport this, since it's dev only and has existed for a couple of versions. * Change files * Fix callbacks * formatting * Logging test fires an error, which shouldnt fail the integration tests * TextInput and ScrollViewer use some non-standard event names that I missed Co-authored-by: Patrick Boyd <[email protected]> Co-authored-by: gillpeacegood <[email protected]> Co-authored-by: Vladimir Morozov <[email protected]> Co-authored-by: React-Native-Windows Bot <[email protected]> Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> Co-authored-by: Nick Gerleman <[email protected]> Co-authored-by: Jon Thysell <[email protected]>
While working on the ABI-safe JSI I have noticed a number of inefficiencies and bugs in the Chakra JSI implementation. Rather than adding them to the ABI-safe JSI implementation PR, I have put them instead into this PR.
The key issues/changes in this PR:
facebook::jsi::JSError
andfacebook::jsi::JSINativeException
exceptions.Outstanding issue: We currently leak memory when a JSI object is released on a wrong thread. JSI allows to release object in a different thread. Supporting it for Chakra will require using a DispatchQueue and a weak pointer to ChakraRuntime. We will fix it as a part of a different PR. The previous Chakra JSI crashes when it happens.
I have tried to measure the new Chakra JSI perf against the old one by running the Playground app. The results are inconclusive. We probably won 0.3-0.5%, but it is difficult to proof because timings maybe different 20% between the runs. The JSI seems to be a very small part of the overall work that the RN app does. But we know for sure that now we do less memory allocations for each JSI API call.
The key part of this change is that we have the full JSI interface implementation for Chakra and that it can pass all JSI unit tests.
Microsoft Reviewers: Open in CodeFlow