Add tests for serialization of v8::CFunction

The tests are modeled after another patch that includes
v8::CFunctions into Node.js's builtin snapshot.

Refs: https://github.com/nodejs/node/pull/40649
Change-Id: I5a91682f7944ef06a0d3caf7333b09f974bcd64b
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3251138
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Maya Lekova <mslekova@chromium.org>
Commit-Queue: Joyee Cheung <joyee@igalia.com>
Cr-Commit-Position: refs/heads/main@{#77726}
This commit is contained in:
Joyee Cheung 2021-11-03 01:31:59 +08:00 committed by V8 LUCI CQ
parent 1b4d3b6393
commit 5dd16ca0fb

View File

@ -29,6 +29,7 @@
#include <sys/stat.h>
#include "include/v8-extension.h"
#include "include/v8-fast-api-calls.h"
#include "include/v8-function.h"
#include "include/v8-locker.h"
#include "src/api/api-inl.h"
@ -3102,6 +3103,160 @@ UNINITIALIZED_TEST(SnapshotCreatorShortExternalReferences) {
FreeCurrentEmbeddedBlob();
}
class FastApiReceiver {
public:
static void FastCallback(v8::Local<v8::Object> receiver) {
FastApiReceiver* receiver_ptr = static_cast<FastApiReceiver*>(
receiver->GetAlignedPointerFromInternalField(0));
CHECK_EQ(receiver_ptr, &instance);
receiver_ptr->result_ |= ApiCheckerResult::kFastCalled;
}
static void SlowCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
v8::Object* receiver = v8::Object::Cast(*info.Holder());
FastApiReceiver* receiver_ptr = static_cast<FastApiReceiver*>(
receiver->GetAlignedPointerFromInternalField(0));
CHECK_EQ(receiver_ptr, &instance);
receiver_ptr->result_ |= ApiCheckerResult::kSlowCalled;
}
static v8::StartupData SerializeInternalFields(v8::Local<v8::Object> holder,
int index, void* data) {
void* ptr = holder->GetAlignedPointerFromInternalField(index);
if (ptr != &instance) {
return {nullptr, 0};
}
// Use a 1-byte payload to tell that it's a FastApiReceiver.
char* payload = new char[1];
return {payload, 1};
}
static void DeserializeInternalFields(v8::Local<v8::Object> holder, int index,
v8::StartupData payload, void* data) {
if (payload.raw_size == 0) {
holder->SetAlignedPointerInInternalField(index, nullptr);
return;
}
// Reset the state for testing.
instance.result_ = ApiCheckerResult::kNotCalled;
holder->SetAlignedPointerInInternalField(index, &instance);
}
bool DidCallFast() const { return (result_ & ApiCheckerResult::kFastCalled); }
bool DidCallSlow() const { return (result_ & ApiCheckerResult::kSlowCalled); }
static FastApiReceiver instance;
v8::CFunction c_function = v8::CFunction::Make(FastCallback);
private:
ApiCheckerResultFlags result_ = ApiCheckerResult::kNotCalled;
};
FastApiReceiver FastApiReceiver::instance;
// A CFunction comes with three external references: the fast calback,
// the slow callback, and the type info.
intptr_t c_function_external_references[] = {
reinterpret_cast<intptr_t>(FastApiReceiver::FastCallback),
reinterpret_cast<intptr_t>(FastApiReceiver::SlowCallback),
reinterpret_cast<intptr_t>(
FastApiReceiver::instance.c_function.GetTypeInfo()),
0};
UNINITIALIZED_TEST(CFunction) {
#ifndef V8_LITE_MODE
if (i::FLAG_jitless) return;
if (!i::FLAG_opt) return;
i::FLAG_turbo_fast_api_calls = true;
i::FLAG_allow_natives_syntax = true;
// Disable --always_opt, otherwise we haven't generated the necessary
// feedback to go down the "best optimization" path for the fast call.
// No optimization should be done before serialization, but after
// deserialization we need optimization to check the fast calls.
i::FLAG_always_opt = false;
i::FlagList::EnforceFlagImplications();
DisableEmbeddedBlobRefcounting();
v8::StartupData blob;
{
v8::SnapshotCreator creator(c_function_external_references);
v8::Isolate* isolate = creator.GetIsolate();
{
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
v8::Context::Scope context_scope(context);
v8::Local<v8::FunctionTemplate> callback = v8::FunctionTemplate::New(
isolate, FastApiReceiver::SlowCallback, v8::Local<v8::Value>(),
v8::Local<v8::Signature>(), 0, v8::ConstructorBehavior::kThrow,
v8::SideEffectType::kHasSideEffect,
&(FastApiReceiver::instance.c_function));
v8::Local<v8::ObjectTemplate> object_template =
v8::ObjectTemplate::New(isolate);
object_template->SetInternalFieldCount(1);
object_template->Set(isolate, "api_func", callback);
v8::Local<v8::Object> object =
object_template->NewInstance(context).ToLocalChecked();
object->SetAlignedPointerInInternalField(0, &(FastApiReceiver::instance));
CHECK(context->Global()
->Set(context, v8_str("receiver"), object)
.FromJust());
CHECK(!FastApiReceiver::instance.DidCallFast());
CHECK(!FastApiReceiver::instance.DidCallSlow());
CompileRun("receiver.api_func();");
CHECK(FastApiReceiver::instance.DidCallSlow());
creator.SetDefaultContext(
context, v8::SerializeInternalFieldsCallback(
FastApiReceiver::SerializeInternalFields, nullptr));
}
blob =
creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
}
{
v8::Isolate::CreateParams params;
params.snapshot_blob = &blob;
params.array_buffer_allocator = CcTest::array_buffer_allocator();
params.external_references = c_function_external_references;
// Test-appropriate equivalent of v8::Isolate::New.
v8::Isolate* isolate = TestSerializer::NewIsolate(params);
{
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(
isolate, nullptr, v8::MaybeLocal<v8::ObjectTemplate>(),
v8::MaybeLocal<v8::Value>(),
v8::DeserializeInternalFieldsCallback(
FastApiReceiver::DeserializeInternalFields, nullptr));
v8::Context::Scope context_scope(context);
// Deserialize callback should reset the state of the instance.
CHECK(!FastApiReceiver::instance.DidCallFast());
CHECK(!FastApiReceiver::instance.DidCallSlow());
CompileRun(
"function foo(arg) {"
" for (let i = 0; i < arg; ++i) { receiver.api_func(); }"
"}"
"%PrepareFunctionForOptimization(foo);"
"foo(42); foo(42);"
"%OptimizeFunctionOnNextCall(foo);"
"foo(42);");
CHECK(FastApiReceiver::instance.DidCallFast());
}
isolate->Dispose();
}
delete[] blob.data;
FreeCurrentEmbeddedBlob();
#endif
}
v8::StartupData CreateSnapshotWithDefaultAndCustom() {
v8::SnapshotCreator creator(original_external_references);
v8::Isolate* isolate = creator.GetIsolate();