Commit 94b4391d authored by Marja Hölttä's avatar Marja Hölttä Committed by V8 LUCI CQ

[web snap] Support inheriting from builtins

Side product: enable null as __proto__.

Bug: v8:11525,v8:12820
Change-Id: I2b9508d0f3563d9000ddede24e7684aab18c2b5e
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3637791Reviewed-by: 's avatarCamillo Bruni <cbruni@chromium.org>
Commit-Queue: Marja Hölttä <marja@chromium.org>
Cr-Commit-Position: refs/heads/main@{#80474}
parent 6b4a541c
This diff is collapsed.
......@@ -99,7 +99,7 @@ class WebSnapshotSerializerDeserializer {
void IterateBuiltinObjects(std::function<void(String, HeapObject)> func);
static constexpr int kBuiltinObjectCount = 2;
static constexpr int kBuiltinObjectCount = 4;
inline Factory* factory() const { return isolate_->factory(); }
......@@ -397,13 +397,16 @@ class V8_EXPORT WebSnapshotDeserializer
void DeserializeArrays();
void DeserializeObjects();
void DeserializeExports(bool skip_exports);
void DeserializeObjectPrototype(Handle<Map> map, uint32_t prototype_id);
void DeserializeObjectPrototype(Handle<Map> map);
Handle<Map> DeserializeObjectPrototypeAndCreateEmptyMap();
void SetPrototype(Handle<Map> map, Handle<Object> prototype);
template <typename T>
void DeserializeObjectPropertiesWithDictionaryMap(
T dict, uint32_t property_count, bool has_custom_property_attributes);
Object ReadValue(
// Return value: (object, was_deferred)
std::tuple<Object, bool> ReadValue(
Handle<HeapObject> object_for_deferred_reference = Handle<HeapObject>(),
uint32_t index_for_deferred_reference = 0,
InternalizeStrings internalize_strings = InternalizeStrings::kNo);
......@@ -415,10 +418,14 @@ class V8_EXPORT WebSnapshotDeserializer
String ReadInPlaceString(
InternalizeStrings internalize_strings = InternalizeStrings::kNo);
Object ReadSymbol();
Object ReadArray(Handle<HeapObject> container, uint32_t container_index);
Object ReadObject(Handle<HeapObject> container, uint32_t container_index);
Object ReadFunction(Handle<HeapObject> container, uint32_t container_index);
Object ReadClass(Handle<HeapObject> container, uint32_t container_index);
std::tuple<Object, bool> ReadArray(Handle<HeapObject> container,
uint32_t container_index);
std::tuple<Object, bool> ReadObject(Handle<HeapObject> container,
uint32_t container_index);
std::tuple<Object, bool> ReadFunction(Handle<HeapObject> container,
uint32_t container_index);
std::tuple<Object, bool> ReadClass(Handle<HeapObject> container,
uint32_t container_index);
Object ReadRegexp();
Object ReadBuiltinObjectReference();
Object ReadExternalReference();
......
......@@ -90,9 +90,9 @@ TEST(Minimal) {
const char* snapshot_source = "var foo = {'key': 'lol'};";
const char* test_source = "foo.key";
const char* expected_result = "lol";
uint32_t kStringCount = 1; // 'foo'; 'key' is in-place.
uint32_t kStringCount = 2; // 'foo', 'Object.prototype'; 'key' is in-place.
uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0;
uint32_t kBuiltinObjectCount = 1;
uint32_t kMapCount = 1;
uint32_t kContextCount = 0;
uint32_t kFunctionCount = 0;
......@@ -106,9 +106,9 @@ TEST(Minimal) {
TEST(EmptyObject) {
const char* snapshot_source = "var foo = {}";
const char* test_source = "foo";
uint32_t kStringCount = 1; // 'foo'
uint32_t kStringCount = 2; // 'foo', 'Object.prototype'
uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0;
uint32_t kBuiltinObjectCount = 1;
uint32_t kMapCount = 1;
uint32_t kContextCount = 0;
uint32_t kFunctionCount = 0;
......@@ -138,9 +138,10 @@ TEST(Numbers) {
" 'f': Number.NEGATIVE_INFINITY,\n"
"}";
const char* test_source = "foo";
uint32_t kStringCount = 1; // 'foo'; 'a'...'f' are in-place.
uint32_t kStringCount =
2; // 'foo', 'Object.prototype'; 'a'...'f' are in-place.
uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0;
uint32_t kBuiltinObjectCount = 1;
uint32_t kMapCount = 1;
uint32_t kContextCount = 0;
uint32_t kFunctionCount = 0;
......@@ -195,9 +196,10 @@ TEST(Oddballs) {
" 'd': undefined,\n"
"}";
const char* test_source = "foo";
uint32_t kStringCount = 1; // 'foo'; 'a'...'d' are in-place.
// 'foo', 'Object.prototype'; 'a'...'d' are in-place.
uint32_t kStringCount = 2;
uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0;
uint32_t kBuiltinObjectCount = 1;
uint32_t kMapCount = 1;
uint32_t kContextCount = 0;
uint32_t kFunctionCount = 0;
......@@ -226,9 +228,10 @@ TEST(Function) {
"var foo = {'key': function() { return '11525'; }};";
const char* test_source = "foo.key()";
const char* expected_result = "11525";
uint32_t kStringCount = 2; // 'foo', function source code. 'key' is in-place.
// 'foo', 'Object.prototype', function source code. 'key' is in-place.
uint32_t kStringCount = 3;
uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0;
uint32_t kBuiltinObjectCount = 1;
uint32_t kMapCount = 1;
uint32_t kContextCount = 0;
uint32_t kFunctionCount = 1;
......@@ -248,10 +251,11 @@ TEST(InnerFunctionWithContext) {
" })()};";
const char* test_source = "foo.key()";
const char* expected_result = "11525";
// Strings: 'foo', 'result', function source code (inner). 'key' is in-place.
uint32_t kStringCount = 3;
// Strings: 'foo', 'result', 'Object.prototype'. function source code (inner).
// 'key' is in-place.
uint32_t kStringCount = 4;
uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0;
uint32_t kBuiltinObjectCount = 1;
uint32_t kMapCount = 1;
uint32_t kContextCount = 1;
uint32_t kFunctionCount = 1;
......@@ -277,10 +281,11 @@ TEST(InnerFunctionWithContextAndParentContext) {
" })()};";
const char* test_source = "foo.key()";
const char* expected_result = "11525";
// Strings: 'foo', function source code (innerinner), 'part1', 'part2'.
uint32_t kStringCount = 4;
// Strings: 'foo', 'Object.prototype', function source code (innerinner),
// 'part1', 'part2'.
uint32_t kStringCount = 5;
uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0;
uint32_t kBuiltinObjectCount = 1;
uint32_t kMapCount = 1;
uint32_t kContextCount = 2;
uint32_t kFunctionCount = 1;
......@@ -294,9 +299,10 @@ TEST(InnerFunctionWithContextAndParentContext) {
TEST(RegExp) {
const char* snapshot_source = "var foo = {'re': /ab+c/gi}";
const char* test_source = "foo";
uint32_t kStringCount = 3; // 'foo', RegExp pattern, RegExp flags
// 'foo', 'Object.prototype', RegExp pattern, RegExp flags
uint32_t kStringCount = 4;
uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0;
uint32_t kBuiltinObjectCount = 1;
uint32_t kMapCount = 1;
uint32_t kContextCount = 0;
uint32_t kFunctionCount = 0;
......@@ -327,9 +333,10 @@ TEST(RegExp) {
TEST(RegExpNoFlags) {
const char* snapshot_source = "var foo = {'re': /ab+c/}";
const char* test_source = "foo";
uint32_t kStringCount = 3; // 'foo', RegExp pattern, RegExp flags
// 'foo', , 'Object.prototype RegExp pattern, RegExp flags
uint32_t kStringCount = 4;
uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0;
uint32_t kBuiltinObjectCount = 1;
uint32_t kMapCount = 1;
uint32_t kContextCount = 0;
uint32_t kFunctionCount = 0;
......@@ -719,9 +726,10 @@ TEST(FunctionKinds) {
" f: async function*() {}\n"
"}";
const char* test_source = "foo";
uint32_t kStringCount = 2; // 'foo', source code. 'a'...'f' in-place.
// 'foo', 'Object.prototype', source code. 'a'...'f' in-place.
uint32_t kStringCount = 3;
uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0;
uint32_t kBuiltinObjectCount = 1;
uint32_t kMapCount = 1;
uint32_t kContextCount = 0;
uint32_t kFunctionCount = 6;
......@@ -931,10 +939,10 @@ TEST(InPlaceStringsInObjects) {
const char* snapshot_source = "var foo = {a: 'one', b: 'two', c: 'three'};";
const char* test_source = "foo.a + foo.b + foo.c;";
const char* expected_result = "onetwothree";
// 'foo'. Other strings are in-place.
uint32_t kStringCount = 1;
// 'foo', 'Object.prototype'. Other strings are in-place.
uint32_t kStringCount = 2;
uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0;
uint32_t kBuiltinObjectCount = 1;
uint32_t kMapCount = 1;
uint32_t kContextCount = 0;
uint32_t kFunctionCount = 0;
......@@ -949,10 +957,10 @@ TEST(RepeatedInPlaceStringsInObjects) {
const char* snapshot_source = "var foo = {a: 'one', b: 'two', c: 'one'};";
const char* test_source = "foo.a + foo.b + foo.c;";
const char* expected_result = "onetwoone";
// 'foo', 'one'. Other strings are in-place.
uint32_t kStringCount = 2;
// 'foo', 'one', 'Object.prototype'. Other strings are in-place.
uint32_t kStringCount = 3;
uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0;
uint32_t kBuiltinObjectCount = 1;
uint32_t kMapCount = 1;
uint32_t kContextCount = 0;
uint32_t kFunctionCount = 0;
......@@ -963,14 +971,14 @@ TEST(RepeatedInPlaceStringsInObjects) {
kFunctionCount, kObjectCount, kArrayCount);
}
TEST(builtin_objects) {
TEST(BuiltinObjects) {
const char* snapshot_source = "var foo = {a: Error.prototype};";
const char* test_source = "foo.a == Error.prototype ? \"pass\" : \"fail\"";
const char* expected_result = "pass";
// 'foo', 'Error.prototype'. Other strings are in-place.
uint32_t kStringCount = 2;
// 'foo', 'Error.prototype', 'Object.prototype'. Other strings are in-place.
uint32_t kStringCount = 3;
uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 1;
uint32_t kBuiltinObjectCount = 2;
uint32_t kMapCount = 1;
uint32_t kContextCount = 0;
uint32_t kFunctionCount = 0;
......@@ -981,15 +989,15 @@ TEST(builtin_objects) {
kFunctionCount, kObjectCount, kArrayCount);
}
TEST(builtin_objectsDeduplicated) {
TEST(BuiltinObjectsDeduplicated) {
const char* snapshot_source =
"var foo = {a: Error.prototype, b: Error.prototype}";
const char* test_source = "foo.a === Error.prototype ? \"pass\" : \"fail\"";
const char* expected_result = "pass";
// 'foo', 'Error.prototype'. Other strings are in-place.
uint32_t kStringCount = 2;
// 'foo', 'Error.prototype', 'Object.prototype'. Other strings are in-place.
uint32_t kStringCount = 3;
uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 1;
uint32_t kBuiltinObjectCount = 2;
uint32_t kMapCount = 1;
uint32_t kContextCount = 0;
uint32_t kFunctionCount = 0;
......
......@@ -41,3 +41,49 @@ d8.file.execute('test/mjsunit/web-snapshot/web-snapshot-helpers.js');
assertEquals(1, obj.__proto__.x);
assertSame(Realm.eval(realm, 'Object.prototype'), obj.__proto__.__proto__);
})();
(function TestDictionaryObjectPrototype() {
function createObjects() {
const obj = {};
// Create an object with dictionary map.
for (let i = 0; i < 2000; i++){
obj[`key${i}`] = `value${i}`;
}
obj.__proto__ = {x: 1};
globalThis.foo = obj;
}
const { foo } = takeAndUseWebSnapshot(createObjects, ['foo']);
assertEquals(2000, Object.keys(foo).length);
assertEquals(2000, Object.values(foo).length);
for (let i = 0; i < 2000; i++){
assertEquals(`value${i}`, foo[`key${i}`]);
}
assertEquals(1, foo.x);
assertEquals(1, foo.__proto__.x);
})();
(function TestNullPrototype() {
function createObjects() {
globalThis.foo = Object.create(null);
}
const { foo } = takeAndUseWebSnapshot(createObjects, ['foo']);
assertEquals(null, Object.getPrototypeOf(foo));
})();
(function TestInheritFromBuiltin() {
function createObjects() {
function inherit(subclass, superclass) {
function middle() {}
middle.prototype = superclass.prototype;
subclass.prototype = new middle();
subclass.prototype.constructor = subclass;
};
function MyError() {}
inherit(MyError, Error);
globalThis.MyError = MyError;
}
const realm = Realm.create();
const {MyError} = takeAndUseWebSnapshot(createObjects, ['MyError'], realm);
const obj = new MyError();
assertTrue(obj.__proto__.__proto__ === Realm.eval(realm, "Error.prototype"));
})();
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