diff --git a/test/cctest/test-serialize.cc b/test/cctest/test-serialize.cc index 7f15f6e5f4..9f402b5c72 100644 --- a/test/cctest/test-serialize.cc +++ b/test/cctest/test-serialize.cc @@ -29,6 +29,7 @@ #include #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 receiver) { + FastApiReceiver* receiver_ptr = static_cast( + receiver->GetAlignedPointerFromInternalField(0)); + CHECK_EQ(receiver_ptr, &instance); + receiver_ptr->result_ |= ApiCheckerResult::kFastCalled; + } + + static void SlowCallback(const v8::FunctionCallbackInfo& info) { + v8::Object* receiver = v8::Object::Cast(*info.Holder()); + FastApiReceiver* receiver_ptr = static_cast( + receiver->GetAlignedPointerFromInternalField(0)); + CHECK_EQ(receiver_ptr, &instance); + receiver_ptr->result_ |= ApiCheckerResult::kSlowCalled; + } + + static v8::StartupData SerializeInternalFields(v8::Local 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 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(FastApiReceiver::FastCallback), + reinterpret_cast(FastApiReceiver::SlowCallback), + reinterpret_cast( + 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 context = v8::Context::New(isolate); + v8::Context::Scope context_scope(context); + + v8::Local callback = v8::FunctionTemplate::New( + isolate, FastApiReceiver::SlowCallback, v8::Local(), + v8::Local(), 0, v8::ConstructorBehavior::kThrow, + v8::SideEffectType::kHasSideEffect, + &(FastApiReceiver::instance.c_function)); + + v8::Local object_template = + v8::ObjectTemplate::New(isolate); + object_template->SetInternalFieldCount(1); + object_template->Set(isolate, "api_func", callback); + + v8::Local 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 context = v8::Context::New( + isolate, nullptr, v8::MaybeLocal(), + v8::MaybeLocal(), + 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();