Commit 2cb5a08f authored by Marja Hölttä's avatar Marja Hölttä Committed by V8 LUCI CQ

[web snapshot] Add support for derived constructors

Especially, this requires having the __proto__s of functions serialized.

Drive-by fix (gc stress): Handlify IterateBuiltinObjects.

Bug: v8:11525
Change-Id: I8dc50b9144d17134a7c9b8fdbabe23f5e44f197f
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3644613Reviewed-by: 's avatarCamillo Bruni <cbruni@chromium.org>
Commit-Queue: Marja Hölttä <marja@chromium.org>
Cr-Commit-Position: refs/heads/main@{#80608}
parent a1d642be
This diff is collapsed.
......@@ -97,9 +97,10 @@ class WebSnapshotSerializerDeserializer {
// Not virtual, on purpose (because it doesn't need to be).
void Throw(const char* message);
void IterateBuiltinObjects(std::function<void(String, HeapObject)> func);
void IterateBuiltinObjects(
std::function<void(Handle<String>, Handle<HeapObject>)> func);
static constexpr int kBuiltinObjectCount = 4;
static constexpr int kBuiltinObjectCount = 12;
inline Factory* factory() const { return isolate_->factory(); }
......@@ -220,8 +221,8 @@ class V8_EXPORT WebSnapshotSerializer
void DiscoverObjectPropertiesWithDictionaryMap(T dict);
void ConstructSource();
void SerializeFunctionInfo(ValueSerializer* serializer,
Handle<JSFunction> function);
void SerializeFunctionInfo(Handle<JSFunction> function,
ValueSerializer& serializer);
void SerializeString(Handle<String> string, ValueSerializer& serializer);
void SerializeSymbol(Handle<Symbol> symbol);
......@@ -403,6 +404,7 @@ class V8_EXPORT WebSnapshotDeserializer
void DeserializeExports(bool skip_exports);
void DeserializeObjectPrototype(Handle<Map> map);
Handle<Map> DeserializeObjectPrototypeAndCreateEmptyMap();
void DeserializeObjectPrototypeForFunction(Handle<JSFunction> function);
void SetPrototype(Handle<Map> map, Handle<Object> prototype);
template <typename T>
......
......@@ -228,10 +228,11 @@ TEST(Function) {
"var foo = {'key': function() { return '11525'; }};";
const char* test_source = "foo.key()";
const char* expected_result = "11525";
// 'foo', 'Object.prototype', function source code. 'key' is in-place.
uint32_t kStringCount = 3;
// 'foo', 'Object.prototype', 'Function.prototype', function source code.
// 'key' is in-place.
uint32_t kStringCount = 4;
uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 1;
uint32_t kBuiltinObjectCount = 2;
uint32_t kMapCount = 1;
uint32_t kContextCount = 0;
uint32_t kFunctionCount = 1;
......@@ -251,11 +252,11 @@ TEST(InnerFunctionWithContext) {
" })()};";
const char* test_source = "foo.key()";
const char* expected_result = "11525";
// Strings: 'foo', 'result', 'Object.prototype'. function source code (inner).
// 'key' is in-place.
uint32_t kStringCount = 4;
// Strings: 'foo', 'result', 'Object.prototype', 'Function.prototype'.
// function source code (inner). 'key' is in-place.
uint32_t kStringCount = 5;
uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 1;
uint32_t kBuiltinObjectCount = 2;
uint32_t kMapCount = 1;
uint32_t kContextCount = 1;
uint32_t kFunctionCount = 1;
......@@ -281,11 +282,11 @@ TEST(InnerFunctionWithContextAndParentContext) {
" })()};";
const char* test_source = "foo.key()";
const char* expected_result = "11525";
// Strings: 'foo', 'Object.prototype', function source code (innerinner),
// 'part1', 'part2'.
uint32_t kStringCount = 5;
// Strings: 'foo', 'Object.prototype', 'Function.prototype', function source
// code (innerinner), 'part1', 'part2'.
uint32_t kStringCount = 6;
uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 1;
uint32_t kBuiltinObjectCount = 2;
uint32_t kMapCount = 1;
uint32_t kContextCount = 2;
uint32_t kFunctionCount = 1;
......@@ -726,10 +727,12 @@ TEST(FunctionKinds) {
" f: async function*() {}\n"
"}";
const char* test_source = "foo";
// 'foo', 'Object.prototype', source code. 'a'...'f' in-place.
uint32_t kStringCount = 3;
// 'foo', 'Object.prototype', 'Function.prototype', 'AsyncFunction.prototype',
// 'AsyncGeneratorFunction.prototype", "GeneratorFunction.prototype", source
// code. 'a'...'f' in-place.
uint32_t kStringCount = 7;
uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 1;
uint32_t kBuiltinObjectCount = 5;
uint32_t kMapCount = 1;
uint32_t kContextCount = 0;
uint32_t kFunctionCount = 6;
......@@ -1008,5 +1011,73 @@ TEST(BuiltinObjectsDeduplicated) {
kFunctionCount, kObjectCount, kArrayCount);
}
TEST(ConstructorFunctionKinds) {
CcTest::InitializeVM();
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
WebSnapshotData snapshot_data;
{
v8::Local<v8::Context> new_context = CcTest::NewContext();
v8::Context::Scope context_scope(new_context);
const char* snapshot_source =
"class Base { constructor() {} };\n"
"class Derived extends Base { constructor() {} };\n"
"class BaseDefault {};\n"
"class DerivedDefault extends BaseDefault {};\n";
CompileRun(snapshot_source);
v8::Local<v8::PrimitiveArray> exports = v8::PrimitiveArray::New(isolate, 4);
exports->Set(isolate, 0,
v8::String::NewFromUtf8(isolate, "Base").ToLocalChecked());
exports->Set(isolate, 1,
v8::String::NewFromUtf8(isolate, "Derived").ToLocalChecked());
exports->Set(
isolate, 2,
v8::String::NewFromUtf8(isolate, "BaseDefault").ToLocalChecked());
exports->Set(
isolate, 3,
v8::String::NewFromUtf8(isolate, "DerivedDefault").ToLocalChecked());
WebSnapshotSerializer serializer(isolate);
CHECK(serializer.TakeSnapshot(new_context, exports, snapshot_data));
CHECK(!serializer.has_error());
CHECK_NOT_NULL(snapshot_data.buffer);
}
{
v8::Local<v8::Context> new_context = CcTest::NewContext();
v8::Context::Scope context_scope(new_context);
WebSnapshotDeserializer deserializer(isolate, snapshot_data.buffer,
snapshot_data.buffer_size);
CHECK(deserializer.Deserialize());
CHECK(!deserializer.has_error());
v8::Local<v8::Function> v8_base = CompileRun("Base").As<v8::Function>();
Handle<JSFunction> base =
Handle<JSFunction>::cast(Utils::OpenHandle(*v8_base));
CHECK_EQ(FunctionKind::kBaseConstructor, base->shared().kind());
v8::Local<v8::Function> v8_derived =
CompileRun("Derived").As<v8::Function>();
Handle<JSFunction> derived =
Handle<JSFunction>::cast(Utils::OpenHandle(*v8_derived));
CHECK_EQ(FunctionKind::kDerivedConstructor, derived->shared().kind());
v8::Local<v8::Function> v8_base_default =
CompileRun("BaseDefault").As<v8::Function>();
Handle<JSFunction> base_default =
Handle<JSFunction>::cast(Utils::OpenHandle(*v8_base_default));
CHECK_EQ(FunctionKind::kDefaultBaseConstructor,
base_default->shared().kind());
v8::Local<v8::Function> v8_derived_default =
CompileRun("DerivedDefault").As<v8::Function>();
Handle<JSFunction> derived_default =
Handle<JSFunction>::cast(Utils::OpenHandle(*v8_derived_default));
CHECK_EQ(FunctionKind::kDefaultDerivedConstructor,
derived_default->shared().kind());
}
}
} // namespace internal
} // namespace v8
......@@ -341,6 +341,35 @@ d8.file.execute('test/mjsunit/web-snapshot/web-snapshot-helpers.js');
assertEquals(7, x.f());
})();
(function TestDerivedClass() {
function createObjects() {
globalThis.Base = class { f() { return 8; }};
globalThis.Foo = class extends Base { };
}
const realm = Realm.create();
const { Foo, Base } = takeAndUseWebSnapshot(createObjects, ['Foo', 'Base'], realm);
assertEquals(Base.prototype, Foo.prototype.__proto__);
assertEquals(Base, Foo.__proto__);
const x = new Foo();
assertEquals(8, x.f());
})();
(function TestDerivedClassWithConstructor() {
function createObjects() {
globalThis.Base = class { constructor() {this.m = 43;}};
globalThis.Foo = class extends Base{
constructor() {
super();
this.n = 42;
}
};
}
const { Foo } = takeAndUseWebSnapshot(createObjects, ['Foo']);
const x = new Foo();
assertEquals(42, x.n);
assertEquals(43, x.m);
})();
(async function TestClassWithAsyncMethods() {
function createObjects() {
globalThis.Foo = class {
......@@ -444,8 +473,14 @@ d8.file.execute('test/mjsunit/web-snapshot/web-snapshot-helpers.js');
globalThis.Sub.prototype = Object.create(Super.prototype);
globalThis.Sub.prototype.subfunc = function() { return 'subfunc'; };
}
const { Sub } = takeAndUseWebSnapshot(createObjects, ['Sub']);
const realm = Realm.create();
const { Sub, Super } =
takeAndUseWebSnapshot(createObjects, ['Sub', 'Super'], realm);
const o = new Sub();
assertEquals('superfunc', o.superfunc());
assertEquals('subfunc', o.subfunc());
assertSame(Super.prototype, Sub.prototype.__proto__);
const realmFunctionPrototype = Realm.eval(realm, 'Function.prototype');
assertSame(realmFunctionPrototype, Super.__proto__);
assertSame(realmFunctionPrototype, Sub.__proto__);
})();
......@@ -87,3 +87,39 @@ d8.file.execute('test/mjsunit/web-snapshot/web-snapshot-helpers.js');
const obj = new MyError();
assertTrue(obj.__proto__.__proto__ === Realm.eval(realm, "Error.prototype"));
})();
(function TestFunctionKinds() {
function createObjects() {
globalThis.normalFunction = function() {}
globalThis.asyncFunction = async function() {}
globalThis.generatorFunction = function*() {}
globalThis.asyncGeneratorFunction = async function*() {}
}
const realm = Realm.create();
const {normalFunction, asyncFunction, generatorFunction,
asyncGeneratorFunction} =
takeAndUseWebSnapshot(createObjects, ['normalFunction', 'asyncFunction',
'generatorFunction', 'asyncGeneratorFunction'], realm);
const newNormalFunction = Realm.eval(realm, 'f1 = function() {}');
const newAsyncFunction = Realm.eval(realm, 'f2 = async function() {}');
const newGeneratorFunction = Realm.eval(realm, 'f3 = function*() {}');
const newAsyncGeneratorFunction =
Realm.eval(realm, 'f4 = async function*() {}');
assertSame(newNormalFunction.__proto__, normalFunction.__proto__);
assertSame(newNormalFunction.prototype.__proto__,
normalFunction.prototype.__proto__);
assertSame(newAsyncFunction.__proto__, asyncFunction.__proto__);
assertEquals(undefined, asyncFunction.prototype);
assertEquals(undefined, newAsyncFunction.prototype);
assertSame(newGeneratorFunction.__proto__, generatorFunction.__proto__);
assertSame(newGeneratorFunction.prototype.__proto__,
generatorFunction.prototype.__proto__);
assertSame(newAsyncGeneratorFunction.__proto__,
asyncGeneratorFunction.__proto__);
assertSame(newAsyncGeneratorFunction.prototype.__proto__,
asyncGeneratorFunction.prototype.__proto__);
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment