adding new __replaceFunctionImplementation_unsafe built-in (#2533)

Summary:
Release notes: providing __replaceFunctionImplementation_unsafe built-in

This new built-in allows replacing the method implementation (capture environment, body, ...) of source functions.

As a result, all method calls executed by the Prepack interpreter will be redirected,
and the replacement will carry over to the prepacked code.
Pull Request resolved: https://github.com/facebook/prepack/pull/2533

Reviewed By: hermanventer

Differential Revision: D9693654

Pulled By: NTillmann

fbshipit-source-id: bd28997965e641f58f89f7119fa477c535c0e539
This commit is contained in:
Nikolai Tillmann 2018-09-06 18:43:09 -07:00 committed by Facebook Github Bot
parent 96174b5ebf
commit 1d4bd237d4
2 changed files with 75 additions and 0 deletions

View File

@ -551,6 +551,67 @@ export default function(realm: Realm): void {
})
);
// Helper function that replaces the implementation of a source function with
// the details from another source function body, including the captured
// environment, the actual code, etc.
// This realizes a form of monkey-patching, enabling mocking a function if
// one doesn't control all existing references to that function,
// or if the storage location to those references cannot be easily updated.
// NOTE: This function affects un-tracked state, so care must be taken
// that this helper function is executed at the right time; typically, one
// would want to execute this function before any call is executed to that
// function. Care must be taken not to make reachable conditionally
// defined values. Because of this limitations, this helper function
// should be considered only as a last resort.
global.$DefineOwnProperty(
"__replaceFunctionImplementation_unsafe",
new PropertyDescriptor({
value: new NativeFunctionValue(
realm,
"global.__replaceFunctionImplementation_unsafe",
"__replaceFunctionImplementation_unsafe",
2,
(context, [target, source]) => {
if (!(target instanceof ECMAScriptSourceFunctionValue)) {
throw realm.createErrorThrowCompletion(
realm.intrinsics.TypeError,
"first argument is not a function with source code"
);
}
if (!(source instanceof ECMAScriptSourceFunctionValue)) {
throw realm.createErrorThrowCompletion(
realm.intrinsics.TypeError,
"second argument is not a function with source code"
);
}
// relevant properties for functionValue
target.$Environment = source.$Environment;
target.$ScriptOrModule = source.$ScriptOrModule;
// properties for ECMAScriptFunctionValue
target.$ConstructorKind = source.$ConstructorKind;
target.$ThisMode = source.$ThisMode;
target.$HomeObject = source.$HomeObject;
target.$FunctionKind = source.$FunctionKind;
// properties for ECMAScriptSourceFunctionValue
target.$Strict = source.$Strict;
target.$FormalParameters = source.$FormalParameters;
target.$ECMAScriptCode = source.$ECMAScriptCode;
target.$HasComputedName = source.$HasComputedName;
target.$HasEmptyConstructor = source.$HasEmptyConstructor;
target.loc = source.loc;
return context.$Realm.intrinsics.undefined;
}
),
writable: true,
enumerable: false,
configurable: true,
})
);
global.$DefineOwnProperty(
"__IntrospectionError",
new PropertyDescriptor({

View File

@ -0,0 +1,14 @@
(function() {
function f() {
return 23;
}
function g() {
return 42;
}
if (global.__abstract) {
global.__replaceFunctionImplementation_unsafe(f, g);
global.inspect = f;
} else {
global.inspect = g;
}
})();