-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Create JSI implementation for IJSValueReader and IJSValueWriter #5009
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| { | ||
| "type": "none", | ||
| "comment": "Create JSI implementation for IJSValueReader and IJSValueWriter", | ||
| "packageName": "react-native-windows", | ||
| "email": "zihanc@microsoft.com", | ||
| "dependentChangeType": "none", | ||
| "date": "2020-05-26T20:47:12.811Z" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,133 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT License. | ||
|
|
||
| #include "pch.h" | ||
| #include "JsiReader.h" | ||
| #include <crash/verifyElseCrash.h> | ||
|
|
||
| namespace winrt::Microsoft::ReactNative { | ||
|
|
||
| //=========================================================================== | ||
| // JsiReader implementation | ||
| //=========================================================================== | ||
|
|
||
| JsiReader::JsiReader(facebook::jsi::Runtime &runtime, const facebook::jsi::Value &root) noexcept | ||
| : m_runtime(runtime), m_root(root) { | ||
| SetValue(root); | ||
| } | ||
|
|
||
| JSValueType JsiReader::ValueType() noexcept { | ||
| if (m_currentPrimitiveValue) { | ||
| if (m_currentPrimitiveValue.value().isString()) { | ||
| return JSValueType::String; | ||
| } else if (m_currentPrimitiveValue.value().isBool()) { | ||
| return JSValueType::Boolean; | ||
| } else if (m_currentPrimitiveValue.value().isNumber()) { | ||
| double number = m_currentPrimitiveValue.value().getNumber(); | ||
|
|
||
| // unfortunately JSI doesn't differentiate int and double | ||
| // here we test if the double value can be converted to int without data loss | ||
| // treat it like an int if we succeeded | ||
|
|
||
| if (floor(number) == number) { | ||
| return JSValueType::Int64; | ||
| } else { | ||
| return JSValueType::Double; | ||
| } | ||
| } | ||
| } else if (m_containers.size() > 0) { | ||
| return m_containers[m_containers.size() - 1].Type == ContainerType::Object ? JSValueType::Object | ||
| : JSValueType::Array; | ||
| } | ||
| return JSValueType::Null; | ||
| } | ||
|
|
||
| bool JsiReader::GetNextObjectProperty(hstring &propertyName) noexcept { | ||
| if (m_containers.size() == 0) { | ||
| return false; | ||
| } | ||
|
|
||
| auto &top = m_containers[m_containers.size() - 1]; | ||
| if (top.Type != ContainerType::Object) { | ||
| return false; | ||
| } | ||
|
|
||
| top.Index++; | ||
| if (top.Index < static_cast<int>(top.PropertyNames.value().size(m_runtime))) { | ||
| auto propertyId = | ||
| top.PropertyNames.value().getValueAtIndex(m_runtime, static_cast<size_t>(top.Index)).getString(m_runtime); | ||
| propertyName = winrt::to_hstring(propertyId.utf8(m_runtime)); | ||
| SetValue(top.CurrentObject.value().getProperty(m_runtime, propertyId)); | ||
| return true; | ||
| } else { | ||
| m_containers.pop_back(); | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| bool JsiReader::GetNextArrayItem() noexcept { | ||
| if (m_containers.size() == 0) { | ||
| return false; | ||
| } | ||
|
|
||
| auto &top = m_containers[m_containers.size() - 1]; | ||
| if (top.Type != ContainerType::Array) { | ||
| return false; | ||
| } | ||
|
|
||
| top.Index++; | ||
| if (top.Index < static_cast<int>(top.CurrentArray.value().size(m_runtime))) { | ||
| SetValue(top.CurrentArray.value().getValueAtIndex(m_runtime, static_cast<size_t>(top.Index))); | ||
| return true; | ||
| } else { | ||
| m_containers.pop_back(); | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| hstring JsiReader::GetString() noexcept { | ||
| if (ValueType() != JSValueType::String) { | ||
| return {}; | ||
| } | ||
| return winrt::to_hstring(m_currentPrimitiveValue.value().getString(m_runtime).utf8(m_runtime)); | ||
| } | ||
|
|
||
| bool JsiReader::GetBoolean() noexcept { | ||
| if (ValueType() != JSValueType::Boolean) { | ||
| return false; | ||
| } | ||
| return m_currentPrimitiveValue.value().getBool(); | ||
| } | ||
|
|
||
| int64_t JsiReader::GetInt64() noexcept { | ||
| if (ValueType() != JSValueType::Int64) { | ||
| return 0; | ||
| } | ||
| return static_cast<int64_t>(m_currentPrimitiveValue.value().getNumber()); | ||
| } | ||
|
|
||
| double JsiReader::GetDouble() noexcept { | ||
| auto valueType = ValueType(); | ||
| if (valueType != JSValueType::Int64 && valueType != JSValueType::Double) { | ||
| return 0; | ||
| } | ||
| return m_currentPrimitiveValue.value().getNumber(); | ||
| } | ||
|
|
||
| void JsiReader::SetValue(const facebook::jsi::Value &value) noexcept { | ||
| if (value.isObject()) { | ||
| auto obj = value.getObject(m_runtime); | ||
| if (obj.isArray(m_runtime)) { | ||
| m_containers.push_back(obj.getArray(m_runtime)); | ||
| } else { | ||
| m_containers.push_back({m_runtime, std::move(obj)}); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not sure about the design, but functions would fall into this case as well. Should we have a check for that?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In this case properties of Function will get enumerated. There are still other cases, you are not able to check all system classes. But this is fine, if users use our codegen, they won't be able to do this in TypeScript. |
||
| } | ||
| m_currentPrimitiveValue = std::nullopt; | ||
| } else if (value.isString() || value.isBool() || value.isNumber()) { | ||
| m_currentPrimitiveValue = {m_runtime, value}; | ||
| } else { | ||
| m_currentPrimitiveValue = facebook::jsi::Value::null(); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we assert that we have exhaused the options?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. in |
||
| } | ||
| } | ||
|
|
||
| } // namespace winrt::Microsoft::ReactNative | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT License. | ||
|
|
||
| #pragma once | ||
|
|
||
| #include "jsi/jsi.h" | ||
| #include "winrt/Microsoft.ReactNative.h" | ||
|
|
||
| namespace winrt::Microsoft::ReactNative { | ||
|
|
||
| struct JsiReader : implements<JsiReader, IJSValueReader> { | ||
| JsiReader(facebook::jsi::Runtime &runtime, const facebook::jsi::Value &root) noexcept; | ||
|
|
||
| public: // IJSValueReader | ||
| JSValueType ValueType() noexcept; | ||
| bool GetNextObjectProperty(hstring &propertyName) noexcept; | ||
| bool GetNextArrayItem() noexcept; | ||
| hstring GetString() noexcept; | ||
| bool GetBoolean() noexcept; | ||
| int64_t GetInt64() noexcept; | ||
| double GetDouble() noexcept; | ||
|
|
||
| private: | ||
| enum class ContainerType { | ||
| Object, | ||
| Array, | ||
| }; | ||
|
|
||
| struct Container { | ||
| ContainerType Type; | ||
| std::optional<facebook::jsi::Object> CurrentObject; // valid for object | ||
| std::optional<facebook::jsi::Array> PropertyNames; // valid for object | ||
| std::optional<facebook::jsi::Array> CurrentArray; // valid for array | ||
| int Index = -1; | ||
|
|
||
| Container(facebook::jsi::Runtime &runtime, facebook::jsi::Object &&value) noexcept | ||
| : Type(ContainerType::Object), CurrentObject(std::make_optional<facebook::jsi::Object>(std::move(value))) { | ||
| PropertyNames = CurrentObject.value().getPropertyNames(runtime); | ||
| } | ||
|
|
||
| Container(facebook::jsi::Array &&value) noexcept | ||
| : Type(ContainerType::Array), CurrentArray(std::make_optional<facebook::jsi::Array>(std::move(value))) {} | ||
|
|
||
| Container(const Container &) = delete; | ||
| Container(Container &&) = default; | ||
| }; | ||
|
|
||
| private: | ||
| void SetValue(const facebook::jsi::Value &value) noexcept; | ||
|
|
||
| private: | ||
| facebook::jsi::Runtime &m_runtime; | ||
| const facebook::jsi::Value &m_root; | ||
|
|
||
| // when m_currentPrimitiveValue is not null, the current value is a primitive value | ||
| // when m_currentPrimitiveValue is null, the current value is the top value of m_nonPrimitiveValues | ||
| std::optional<facebook::jsi::Value> m_currentPrimitiveValue; | ||
| std::vector<Container> m_containers; | ||
| }; | ||
|
|
||
| } // namespace winrt::Microsoft::ReactNative |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,134 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT License. | ||
|
|
||
| #include "pch.h" | ||
| #include "JsiWriter.h" | ||
| #include <crash/verifyElseCrash.h> | ||
|
|
||
| namespace winrt::Microsoft::ReactNative { | ||
|
|
||
| //=========================================================================== | ||
| // JsiWriter implementation | ||
| //=========================================================================== | ||
|
|
||
| JsiWriter::JsiWriter(facebook::jsi::Runtime &runtime) noexcept : m_runtime(runtime) { | ||
| Push({ContainerState::AcceptValueAndFinish}); | ||
| } | ||
|
|
||
| facebook::jsi::Value JsiWriter::MoveResult() noexcept { | ||
| VerifyElseCrash(m_containers.size() == 0); | ||
| return std::move(m_result); | ||
| } | ||
|
|
||
| void JsiWriter::WriteNull() noexcept { | ||
| WriteValue(facebook::jsi::Value::null()); | ||
| } | ||
|
|
||
| void JsiWriter::WriteBoolean(bool value) noexcept { | ||
| WriteValue({value}); | ||
| } | ||
|
|
||
| void JsiWriter::WriteInt64(int64_t value) noexcept { | ||
| WriteValue({static_cast<double>(value)}); | ||
| } | ||
|
|
||
| void JsiWriter::WriteDouble(double value) noexcept { | ||
| WriteValue({value}); | ||
| } | ||
|
|
||
| void JsiWriter::WriteString(const winrt::hstring &value) noexcept { | ||
| WriteValue({m_runtime, facebook::jsi::String::createFromUtf8(m_runtime, winrt::to_string(value))}); | ||
ZihanChen-MSFT marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| void JsiWriter::WriteObjectBegin() noexcept { | ||
| // legal to create an object when it is accepting a value | ||
| VerifyElseCrash(Top().State != ContainerState::AcceptPropertyName); | ||
| Push({ContainerState::AcceptPropertyName, facebook::jsi::Object(m_runtime)}); | ||
| } | ||
|
|
||
| void JsiWriter::WritePropertyName(const winrt::hstring &name) noexcept { | ||
| // legal to set a property name only when AcceptPropertyName | ||
| auto &top = Top(); | ||
| VerifyElseCrash(top.State == ContainerState::AcceptPropertyName); | ||
| top.State = ContainerState::AcceptPropertyValue; | ||
| top.PropertyName = winrt::to_string(name); | ||
| } | ||
|
|
||
| void JsiWriter::WriteObjectEnd() noexcept { | ||
| // legal to finish an object only when AcceptPropertyName | ||
| VerifyElseCrash(Top().State == ContainerState::AcceptPropertyName); | ||
| WriteValue(Pop().CurrentObject.value()); | ||
| } | ||
|
|
||
| void JsiWriter::WriteArrayBegin() noexcept { | ||
| // legal to create an array only when it is accepting a value | ||
| VerifyElseCrash(Top().State != ContainerState::AcceptPropertyName); | ||
| Push({ContainerState::AcceptArrayElement}); | ||
| } | ||
|
|
||
| void JsiWriter::WriteArrayEnd() noexcept { | ||
| // legal to finish an array only when AcceptArrayElement | ||
| auto &top = Top(); | ||
| VerifyElseCrash(top.State == ContainerState::AcceptArrayElement); | ||
|
|
||
| facebook::jsi::Array createdArray(m_runtime, top.CurrentArrayElements.size()); | ||
| for (size_t i = 0; i < top.CurrentArrayElements.size(); i++) { | ||
| createdArray.setValueAtIndex(m_runtime, i, std::move(top.CurrentArrayElements.at(i))); | ||
| } | ||
| Pop(); | ||
| WriteValue({m_runtime, createdArray}); | ||
| } | ||
|
|
||
| void JsiWriter::WriteValue(facebook::jsi::Value &&value) noexcept { | ||
| auto &top = Top(); | ||
| switch (top.State) { | ||
| case ContainerState::AcceptValueAndFinish: { | ||
| m_result = std::move(value); | ||
| Pop(); | ||
| VerifyElseCrash(m_containers.size() == 0); | ||
| break; | ||
| } | ||
| case ContainerState::AcceptArrayElement: { | ||
| top.CurrentArrayElements.push_back(std::move(value)); | ||
| break; | ||
| } | ||
| case ContainerState::AcceptPropertyValue: { | ||
| auto &createdObject = top.CurrentObject.value(); | ||
| createdObject.setProperty(m_runtime, top.PropertyName.c_str(), std::move(value)); | ||
| top.State = ContainerState::AcceptPropertyName; | ||
| top.PropertyName = {}; | ||
| break; | ||
| } | ||
| default: | ||
| VerifyElseCrash(false); | ||
| } | ||
| } | ||
|
|
||
| JsiWriter::Container &JsiWriter::Top() noexcept { | ||
| VerifyElseCrash(m_containers.size() > 0); | ||
| return m_containers[m_containers.size() - 1]; | ||
| } | ||
|
|
||
| JsiWriter::Container JsiWriter::Pop() noexcept { | ||
| auto top = std::move(Top()); | ||
| m_containers.pop_back(); | ||
| return top; | ||
| } | ||
|
|
||
| void JsiWriter::Push(Container &&container) noexcept { | ||
| m_containers.push_back(std::move(container)); | ||
| } | ||
|
|
||
| /*static*/ facebook::jsi::Value JsiWriter::ToJsiValue( | ||
| facebook::jsi::Runtime &runtime, | ||
| JSValueArgWriter const &argWriter) noexcept { | ||
| if (argWriter) { | ||
| IJSValueWriter jsiWriter = winrt::make<JsiWriter>(runtime); | ||
| argWriter(jsiWriter); | ||
| return jsiWriter.as<JsiWriter>()->MoveResult(); | ||
| } | ||
|
|
||
| return {}; | ||
| } | ||
|
|
||
| } // namespace winrt::Microsoft::ReactNative | ||
Uh oh!
There was an error while loading. Please reload this page.