Introduce Function::FunctionProtoToString()
Add a new function on the public API to allow serializing a function to a string using the built-in toString() implementation, allowing serialization without worrying about untrusted author script overriding the toString() implementation. This is similar in nature to Object::ObjectProtoToString() (but that only returns "[object Function]" for any passed function). Add tests for the same. Bug: chromium:1144841 Change-Id: Ie4c29b870034c0817c23bf91f9424f956098823d Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2514768 Reviewed-by: Toon Verwaest <verwaest@chromium.org> Commit-Queue: Devlin <rdevlin.cronin@chromium.org> Cr-Commit-Position: refs/heads/master@{#70976}
This commit is contained in:
parent
a3e67fea74
commit
2ccd4dc564
@ -4621,6 +4621,15 @@ class V8_EXPORT Function : public Object {
|
||||
*/
|
||||
Local<Value> GetBoundFunction() const;
|
||||
|
||||
/**
|
||||
* Calls builtin Function.prototype.toString on this function.
|
||||
* This is different from Value::ToString() that may call a user-defined
|
||||
* toString() function, and different than Object::ObjectProtoToString() which
|
||||
* always serializes "[object Function]".
|
||||
*/
|
||||
V8_WARN_UNUSED_RESULT MaybeLocal<String> FunctionProtoToString(
|
||||
Local<Context> context);
|
||||
|
||||
ScriptOrigin GetScriptOrigin() const;
|
||||
V8_INLINE static Function* Cast(Value* obj);
|
||||
static const int kLineOffsetNotFound;
|
||||
|
@ -5126,6 +5126,18 @@ Local<v8::Value> Function::GetBoundFunction() const {
|
||||
return v8::Undefined(reinterpret_cast<v8::Isolate*>(self->GetIsolate()));
|
||||
}
|
||||
|
||||
MaybeLocal<String> v8::Function::FunctionProtoToString(Local<Context> context) {
|
||||
PREPARE_FOR_EXECUTION(context, Function, FunctionProtoToString, String);
|
||||
auto self = Utils::OpenHandle(this);
|
||||
Local<Value> result;
|
||||
has_pending_exception = !ToLocal<Value>(
|
||||
i::Execution::CallBuiltin(isolate, isolate->function_to_string(), self, 0,
|
||||
nullptr),
|
||||
&result);
|
||||
RETURN_ON_FAILED_EXECUTION(String);
|
||||
RETURN_ESCAPED(Local<String>::Cast(result));
|
||||
}
|
||||
|
||||
int Name::GetIdentityHash() {
|
||||
auto self = Utils::OpenHandle(this);
|
||||
return static_cast<int>(self->Hash());
|
||||
|
@ -1571,8 +1571,10 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
|
||||
Builtins::kFastFunctionPrototypeBind, 1, false);
|
||||
SimpleInstallFunction(isolate_, prototype, "call",
|
||||
Builtins::kFunctionPrototypeCall, 1, false);
|
||||
SimpleInstallFunction(isolate_, prototype, "toString",
|
||||
Builtins::kFunctionPrototypeToString, 0, false);
|
||||
Handle<JSFunction> function_to_string =
|
||||
SimpleInstallFunction(isolate_, prototype, "toString",
|
||||
Builtins::kFunctionPrototypeToString, 0, false);
|
||||
native_context()->set_function_to_string(*function_to_string);
|
||||
|
||||
// Install the @@hasInstance function.
|
||||
Handle<JSFunction> has_instance = InstallFunctionAtSymbol(
|
||||
|
@ -739,6 +739,7 @@ class RuntimeCallTimer final {
|
||||
V(Float64Array_New) \
|
||||
V(Function_Call) \
|
||||
V(Function_New) \
|
||||
V(Function_FunctionProtoToString) \
|
||||
V(Function_NewInstance) \
|
||||
V(FunctionTemplate_GetFunction) \
|
||||
V(FunctionTemplate_New) \
|
||||
|
@ -311,6 +311,7 @@ enum ContextLookupFlags {
|
||||
V(FINALIZATION_REGISTRY_CLEANUP_SOME, JSFunction, \
|
||||
finalization_registry_cleanup_some) \
|
||||
V(FUNCTION_HAS_INSTANCE_INDEX, JSFunction, function_has_instance) \
|
||||
V(FUNCTION_TO_STRING_INDEX, JSFunction, function_to_string) \
|
||||
V(OBJECT_TO_STRING, JSFunction, object_to_string) \
|
||||
V(OBJECT_VALUE_OF_FUNCTION_INDEX, JSFunction, object_value_of_function) \
|
||||
V(PROMISE_ALL_INDEX, JSFunction, promise_all) \
|
||||
|
@ -17566,6 +17566,33 @@ THREADED_TEST(FunctionGetBoundFunction) {
|
||||
original_function->GetScriptColumnNumber());
|
||||
}
|
||||
|
||||
THREADED_TEST(FunctionProtoToString) {
|
||||
LocalContext context;
|
||||
v8::Isolate* isolate = CcTest::isolate();
|
||||
v8::HandleScope scope(isolate);
|
||||
|
||||
// Replace Function.prototype.toString.
|
||||
CompileRun(R"(
|
||||
Function.prototype.toString = function() {
|
||||
return 'customized toString';
|
||||
})");
|
||||
|
||||
constexpr char kTestFunction[] = "function testFunction() { return 7; }";
|
||||
std::string wrapped_function("(");
|
||||
wrapped_function.append(kTestFunction).append(")");
|
||||
Local<Function> function =
|
||||
CompileRun(wrapped_function.c_str()).As<Function>();
|
||||
|
||||
Local<String> value = function->ToString(context.local()).ToLocalChecked();
|
||||
CHECK(value->IsString());
|
||||
CHECK(
|
||||
value->Equals(context.local(), v8_str("customized toString")).FromJust());
|
||||
|
||||
// FunctionProtoToString() should not call the replaced toString function.
|
||||
value = function->FunctionProtoToString(context.local()).ToLocalChecked();
|
||||
CHECK(value->IsString());
|
||||
CHECK(value->Equals(context.local(), v8_str(kTestFunction)).FromJust());
|
||||
}
|
||||
|
||||
static void GetterWhichReturns42(
|
||||
Local<String> name,
|
||||
|
Loading…
Reference in New Issue
Block a user