mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Implement globalEvalWithSourceUrl
Summary: Implements a new host function on the global object in debug builds, called `globalEvalWithSourceUrl`. This performs a global `eval()` and attaches a URL/filename to the evaluated script (in stack traces, debuggers, etc). It serves a similar purpose to the `//# sourceURL=` directive (which most JS engines support, but JSC doesn't) and to the old `nativeInjectHMRUpdate` function which was dropped in the JSC->JSI migration. Reviewed By: cpojer Differential Revision: D16491506 fbshipit-source-id: bd9a89311dcbb1d0baece77ead16b9ecfb13bfe3
This commit is contained in:
committed by
Facebook Github Bot
parent
2c136192a3
commit
71c84cf6be
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @format
|
||||
* @flow strict-local
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const React = require('react');
|
||||
const ReactNative = require('react-native');
|
||||
const parseErrorStack = require('react-native/Libraries/Core/Devtools/parseErrorStack');
|
||||
const {View} = ReactNative;
|
||||
|
||||
const {TestModule} = ReactNative.NativeModules;
|
||||
|
||||
class GlobalEvalWithSourceUrlTest extends React.Component<{}> {
|
||||
componentDidMount() {
|
||||
if (typeof global.globalEvalWithSourceUrl !== 'function') {
|
||||
throw new Error(
|
||||
'Expected to find globalEvalWithSourceUrl function on global object but found ' +
|
||||
typeof global.globalEvalWithSourceUrl,
|
||||
);
|
||||
}
|
||||
const value = global.globalEvalWithSourceUrl('42');
|
||||
if (value !== 42) {
|
||||
throw new Error(
|
||||
'Expected globalEvalWithSourceUrl(expression) to return a value',
|
||||
);
|
||||
}
|
||||
let syntaxError;
|
||||
try {
|
||||
global.globalEvalWithSourceUrl('{');
|
||||
} catch (e) {
|
||||
syntaxError = e;
|
||||
}
|
||||
if (!syntaxError) {
|
||||
throw new Error(
|
||||
'Expected globalEvalWithSourceUrl to throw on a syntax error',
|
||||
);
|
||||
}
|
||||
if (!(syntaxError instanceof SyntaxError)) {
|
||||
throw new Error(
|
||||
'Expected globalEvalWithSourceUrl to throw SyntaxError on a syntax error',
|
||||
);
|
||||
}
|
||||
const url = 'http://example.com/foo.js';
|
||||
let error;
|
||||
try {
|
||||
global.globalEvalWithSourceUrl('throw new Error()', url);
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
if (!error) {
|
||||
throw new Error(
|
||||
'Expected globalEvalWithSourceUrl to throw an Error object',
|
||||
);
|
||||
}
|
||||
const parsedStack = parseErrorStack(error);
|
||||
if (parsedStack[0].file !== url) {
|
||||
throw new Error(
|
||||
`Expected first eval stack frame to be in ${url} but found ${
|
||||
parsedStack[0].file
|
||||
}`,
|
||||
);
|
||||
}
|
||||
TestModule.markTestCompleted();
|
||||
}
|
||||
|
||||
render(): React.Node {
|
||||
return <View />;
|
||||
}
|
||||
}
|
||||
|
||||
GlobalEvalWithSourceUrlTest.displayName = 'GlobalEvalWithSourceUrlTest';
|
||||
|
||||
module.exports = GlobalEvalWithSourceUrlTest;
|
||||
@@ -36,6 +36,7 @@ const TESTS = [
|
||||
require('./SyncMethodTest'),
|
||||
require('./WebSocketTest'),
|
||||
require('./AccessibilityManagerTest'),
|
||||
require('./GlobalEvalWithSourceUrlTest'),
|
||||
];
|
||||
|
||||
TESTS.forEach(
|
||||
|
||||
@@ -74,6 +74,6 @@ RCT_TEST(SyncMethodTest)
|
||||
RCT_TEST(PromiseTest)
|
||||
RCT_TEST_ONLY_WITH_PACKAGER(WebSocketTest) // Requires a WebSocket test server, see scripts/objc-test.sh
|
||||
RCT_TEST(AccessibilityManagerTest)
|
||||
RCT_TEST(GlobalEvalWithSourceUrlTest)
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -113,6 +113,21 @@ void JSIExecutor::loadApplicationScript(
|
||||
const jsi::Value *args,
|
||||
size_t count) { return nativeCallSyncHook(args, count); }));
|
||||
|
||||
#if DEBUG
|
||||
runtime_->global().setProperty(
|
||||
*runtime_,
|
||||
"globalEvalWithSourceUrl",
|
||||
Function::createFromHostFunction(
|
||||
*runtime_,
|
||||
PropNameID::forAscii(*runtime_, "globalEvalWithSourceUrl"),
|
||||
1,
|
||||
[this](
|
||||
jsi::Runtime &,
|
||||
const jsi::Value &,
|
||||
const jsi::Value *args,
|
||||
size_t count) { return globalEvalWithSourceUrl(args, count); }));
|
||||
#endif
|
||||
|
||||
if (runtimeInstaller_) {
|
||||
runtimeInstaller_(*runtime_);
|
||||
}
|
||||
@@ -358,6 +373,24 @@ Value JSIExecutor::nativeCallSyncHook(const Value *args, size_t count) {
|
||||
return valueFromDynamic(*runtime_, result.value());
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
Value JSIExecutor::globalEvalWithSourceUrl(const Value *args, size_t count) {
|
||||
if (count != 1 && count != 2) {
|
||||
throw std::invalid_argument(
|
||||
"globalEvalWithSourceUrl arg count must be 1 or 2");
|
||||
}
|
||||
|
||||
auto code = args[0].asString(*runtime_).utf8(*runtime_);
|
||||
std::string url;
|
||||
if (count > 1 && args[1].isString()) {
|
||||
url = args[1].asString(*runtime_).utf8(*runtime_);
|
||||
}
|
||||
|
||||
return runtime_->evaluateJavaScript(
|
||||
std::make_unique<StringBuffer>(std::move(code)), url);
|
||||
}
|
||||
#endif
|
||||
|
||||
void bindNativeLogger(Runtime &runtime, Logger logger) {
|
||||
runtime.global().setProperty(
|
||||
runtime,
|
||||
|
||||
@@ -112,6 +112,9 @@ class JSIExecutor : public JSExecutor {
|
||||
void callNativeModules(const jsi::Value &queue, bool isEndOfBatch);
|
||||
jsi::Value nativeCallSyncHook(const jsi::Value *args, size_t count);
|
||||
jsi::Value nativeRequire(const jsi::Value *args, size_t count);
|
||||
#ifdef DEBUG
|
||||
jsi::Value globalEvalWithSourceUrl(const jsi::Value *args, size_t count);
|
||||
#endif
|
||||
|
||||
std::shared_ptr<jsi::Runtime> runtime_;
|
||||
std::shared_ptr<ExecutorDelegate> delegate_;
|
||||
|
||||
Reference in New Issue
Block a user