From 0d7faf6f73b942126e1f45016cde8fd480fd0164 Mon Sep 17 00:00:00 2001 From: Peter Ammon Date: Fri, 15 Feb 2019 01:48:58 -0800 Subject: [PATCH] Introduce prepareJavaScript jsi API Summary: This adds a new jsi API prepareJavaScript. This accepts the same parameters as evaluateJavaScript() but does not evaluate anything; instead it returns a new object PreparedJavaScript which can itself be evaluated, via the new API evaluatePreparedJavaScript(). There is a new empty class PreparedJavaScript which may be subclassed by each Runtime variant to store its particular prepared form. Reviewed By: mhorowitz Differential Revision: D10491585 fbshipit-source-id: 702b9e23f2ff03d71a8ab17efb7e154b16dd8e87 --- React/React.xcodeproj/project.pbxproj | 8 ++++ ReactCommon/jsi/BUCK | 1 + ReactCommon/jsi/JSCRuntime.cpp | 25 ++++++++++++ ReactCommon/jsi/jsi.cpp | 2 + ReactCommon/jsi/jsi.h | 43 +++++++++++++++----- ReactCommon/jsi/jsilib.h | 57 +++++++++++++++++++++++++++ 6 files changed, 127 insertions(+), 9 deletions(-) create mode 100644 ReactCommon/jsi/jsilib.h diff --git a/React/React.xcodeproj/project.pbxproj b/React/React.xcodeproj/project.pbxproj index 2fc11518236..ec0a58585ab 100644 --- a/React/React.xcodeproj/project.pbxproj +++ b/React/React.xcodeproj/project.pbxproj @@ -794,6 +794,9 @@ 3DFE0D1B1DF8575800459392 /* YGMacros.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 130A77041DF767AF001F9587 /* YGMacros.h */; }; 3DFE0D1C1DF8575800459392 /* Yoga.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 130A77081DF767AF001F9587 /* Yoga.h */; }; 3EDCA8A51D3591E700450C31 /* RCTErrorInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 3EDCA8A41D3591E700450C31 /* RCTErrorInfo.m */; }; + 4F56C93822167A4800DB9F3F /* jsilib.h in Headers */ = {isa = PBXBuildFile; fileRef = 4F56C93722167A4800DB9F3F /* jsilib.h */; }; + 4F56C93922167A4D00DB9F3F /* jsilib.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 4F56C93722167A4800DB9F3F /* jsilib.h */; }; + 4F56C93A2216A3B700DB9F3F /* jsilib.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 4F56C93722167A4800DB9F3F /* jsilib.h */; }; 50E98FEA21460B0D00CD9289 /* RCTWKWebViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 50E98FE621460B0D00CD9289 /* RCTWKWebViewManager.m */; }; 50E98FEB21460B0D00CD9289 /* RCTWKWebView.h in Headers */ = {isa = PBXBuildFile; fileRef = 50E98FE721460B0D00CD9289 /* RCTWKWebView.h */; }; 50E98FEC21460B0D00CD9289 /* RCTWKWebView.m in Sources */ = {isa = PBXBuildFile; fileRef = 50E98FE821460B0D00CD9289 /* RCTWKWebView.m */; }; @@ -1753,6 +1756,7 @@ ED296FC7214C9B4B00B7C4FE /* instrumentation.h in Copy Headers */, ED296FC6214C9B4400B7C4FE /* JSIDynamic.h in Copy Headers */, ED296FC5214C9B3E00B7C4FE /* jsi-inl.h in Copy Headers */, + 4F56C93A2216A3B700DB9F3F /* jsilib.h in Copy Headers */, ); name = "Copy Headers"; runOnlyForDeploymentPostprocessing = 0; @@ -1780,6 +1784,7 @@ EDEBC71D214B40F900DD5AC8 /* JSIDynamic.h in Copy Headers */, EDEBC71E214B40F900DD5AC8 /* JSCRuntime.h in Copy Headers */, EDEBC71F214B40F900DD5AC8 /* jsi.h in Copy Headers */, + 4F56C93922167A4D00DB9F3F /* jsilib.h in Copy Headers */, ); name = "Copy Headers"; runOnlyForDeploymentPostprocessing = 0; @@ -2058,6 +2063,7 @@ 3EDCA8A21D3591E700450C31 /* RCTErrorCustomizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTErrorCustomizer.h; sourceTree = ""; }; 3EDCA8A31D3591E700450C31 /* RCTErrorInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTErrorInfo.h; sourceTree = ""; }; 3EDCA8A41D3591E700450C31 /* RCTErrorInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTErrorInfo.m; sourceTree = ""; }; + 4F56C93722167A4800DB9F3F /* jsilib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = jsilib.h; sourceTree = ""; }; 50E98FE621460B0D00CD9289 /* RCTWKWebViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTWKWebViewManager.m; sourceTree = ""; }; 50E98FE721460B0D00CD9289 /* RCTWKWebView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTWKWebView.h; sourceTree = ""; }; 50E98FE821460B0D00CD9289 /* RCTWKWebView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTWKWebView.m; sourceTree = ""; }; @@ -3040,6 +3046,7 @@ EDEBC6DF214B3F6800DD5AC8 /* BUCK */, EDEBC6E0214B3F6800DD5AC8 /* instrumentation.h */, EDEBC6E1214B3F6800DD5AC8 /* jsi.h */, + 4F56C93722167A4800DB9F3F /* jsilib.h */, ); path = jsi; sourceTree = ""; @@ -3590,6 +3597,7 @@ buildActionMask = 2147483647; files = ( EDEBC6E9214B3F6800DD5AC8 /* jsi.h in Headers */, + 4F56C93822167A4800DB9F3F /* jsilib.h in Headers */, EDEBC6E7214B3F6800DD5AC8 /* JSCRuntime.h in Headers */, EDEBC6E5214B3F6800DD5AC8 /* JSIDynamic.h in Headers */, EDEBC6E2214B3F6800DD5AC8 /* jsi-inl.h in Headers */, diff --git a/ReactCommon/jsi/BUCK b/ReactCommon/jsi/BUCK index b43646674f2..b36813dacc7 100644 --- a/ReactCommon/jsi/BUCK +++ b/ReactCommon/jsi/BUCK @@ -12,6 +12,7 @@ rn_xplat_cxx_library( "instrumentation.h", "jsi.h", "jsi-inl.h", + "jsilib.h", ], compiler_flags = [ "-O3", diff --git a/ReactCommon/jsi/JSCRuntime.cpp b/ReactCommon/jsi/JSCRuntime.cpp index 9c2af0e6921..0313a6d13c6 100644 --- a/ReactCommon/jsi/JSCRuntime.cpp +++ b/ReactCommon/jsi/JSCRuntime.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -36,6 +37,13 @@ class JSCRuntime : public jsi::Runtime { JSCRuntime(JSGlobalContextRef ctx); ~JSCRuntime(); + std::shared_ptr prepareJavaScript( + const std::shared_ptr &buffer, + std::string sourceURL) override; + + void evaluatePreparedJavaScript( + const std::shared_ptr& js) override; + void evaluateJavaScript( const std::shared_ptr &buffer, const std::string& sourceURL) override; @@ -318,6 +326,23 @@ JSCRuntime::~JSCRuntime() { #endif } +std::shared_ptr JSCRuntime::prepareJavaScript( + const std::shared_ptr &buffer, + std::string sourceURL) { + return std::make_shared( + buffer, std::move(sourceURL)); +} + +void JSCRuntime::evaluatePreparedJavaScript( + const std::shared_ptr& js) { + assert( + dynamic_cast(js.get()) && + "preparedJavaScript must be a SourceJavaScriptPreparation"); + auto sourceJs = + std::static_pointer_cast(js); + evaluateJavaScript(sourceJs, sourceJs->sourceURL()); +} + void JSCRuntime::evaluateJavaScript( const std::shared_ptr &buffer, const std::string& sourceURL) { diff --git a/ReactCommon/jsi/jsi.cpp b/ReactCommon/jsi/jsi.cpp index a7ddd277a6e..4392e85982d 100644 --- a/ReactCommon/jsi/jsi.cpp +++ b/ReactCommon/jsi/jsi.cpp @@ -24,6 +24,8 @@ void throwJSError(Runtime& rt, const char* msg) { Buffer::~Buffer() {} +PreparedJavaScript::~PreparedJavaScript() = default; + Value HostObject::get(Runtime&, const PropNameID&) { return Value(); } diff --git a/ReactCommon/jsi/jsi.h b/ReactCommon/jsi/jsi.h index d65e531787f..bb2759f0365 100644 --- a/ReactCommon/jsi/jsi.h +++ b/ReactCommon/jsi/jsi.h @@ -52,6 +52,18 @@ class StringBuffer : public Buffer { std::string s_; }; +/// PreparedJavaScript is a base class repesenting JavaScript which is in a form +/// optimized for execution, in a runtime-specific way. Construct one via +/// jsi::Runtime::prepareJavaScript(). +/// ** This is an experimental API that is subject to change. ** +class PreparedJavaScript { + protected: + PreparedJavaScript() = default; + + public: + virtual ~PreparedJavaScript() = 0; +}; + class Runtime; class Pointer; class PropNameID; @@ -145,6 +157,25 @@ class Runtime { virtual void evaluateJavaScript( const std::shared_ptr& buffer, const std::string& sourceURL) = 0; + + /// Prepares to evaluate the given JavaScript \c buffer by processing it into + /// a form optimized for execution. This may include pre-parsing, compiling, + /// etc. If the input is invalid (for example, cannot be parsed), a + /// JSIException will be thrown. The resulting object is tied to the + /// particular concrete type of Runtime from which it was created. It may be + /// used (via evaluatePreparedJavaScript) in any Runtime of the same concrete + /// type. + /// The PreparedJavaScript object may be passed to multiple VM instances, so + /// they can all share and benefit from the prepared script. + virtual std::shared_ptr prepareJavaScript( + const std::shared_ptr& buffer, + std::string sourceURL) = 0; + + /// Evaluates a PreparedJavaScript. If evaluation causes an error, a + /// JSIException will be thrown. + virtual void evaluatePreparedJavaScript( + const std::shared_ptr& js) = 0; + /// \return the global object virtual Object global() = 0; @@ -304,8 +335,8 @@ class PropNameID : public Pointer { public: using Pointer::Pointer; - PropNameID(Runtime &runtime, const PropNameID &other) - : Pointer(runtime.clonePropNameID(other.ptr_)) {} + PropNameID(Runtime& runtime, const PropNameID& other) + : PropNameID(runtime.clonePropNameID(other.ptr_)) {} PropNameID(PropNameID&& other) = default; PropNameID& operator=(PropNameID&& other) = default; @@ -1031,13 +1062,7 @@ class Value { ValueKind kind_; Data data_; - // In the future: Value becomes NaN-boxed. In the Hermes impl, if - // the object contains a PinnedHermesValue, we need to be able to - // get a pointer to it; this can be casted from 'this'. In the JSC - // impl, we need to be able to convert the boxed value into a JSC - // ref. This can be done by casting this, deferencing it to get a - // number, doing some bit masks, and then casting again into the - // desired JSC ref type. + // In the future: Value becomes NaN-boxed. See T40538354. }; /// Not movable and not copyable RAII marker advising the underlying diff --git a/ReactCommon/jsi/jsilib.h b/ReactCommon/jsi/jsilib.h new file mode 100644 index 00000000000..8c12a72614b --- /dev/null +++ b/ReactCommon/jsi/jsilib.h @@ -0,0 +1,57 @@ +// 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. + +#pragma once + +#include + +namespace facebook { +namespace jsi { + +class FileBuffer : public Buffer { + public: + FileBuffer(const std::string& path); + ~FileBuffer(); + + size_t size() const override { + return size_; + } + + const uint8_t* data() const override { + return data_; + } + + private: + size_t size_; + uint8_t* data_; +}; + +// A trivial implementation of PreparedJavaScript that simply stores the source +// buffer and URL. +class SourceJavaScriptPreparation final : public jsi::PreparedJavaScript, + public jsi::Buffer { + std::shared_ptr buf_; + std::string sourceURL_; + + public: + SourceJavaScriptPreparation( + std::shared_ptr buf, + std::string sourceURL) + : buf_(std::move(buf)), sourceURL_(std::move(sourceURL)) {} + + const std::string& sourceURL() const { + return sourceURL_; + } + + size_t size() const override { + return buf_->size(); + } + const uint8_t* data() const override { + return buf_->data(); + } +}; + +} // namespace jsi +} // namespace facebook