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 { ...@@ -99,7 +99,7 @@ class WebSnapshotSerializerDeserializer {
void IterateBuiltinObjects(std::function<void(String, HeapObject)> func); 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(); } inline Factory* factory() const { return isolate_->factory(); }
...@@ -397,13 +397,16 @@ class V8_EXPORT WebSnapshotDeserializer ...@@ -397,13 +397,16 @@ class V8_EXPORT WebSnapshotDeserializer
void DeserializeArrays(); void DeserializeArrays();
void DeserializeObjects(); void DeserializeObjects();
void DeserializeExports(bool skip_exports); 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> template <typename T>
void DeserializeObjectPropertiesWithDictionaryMap( void DeserializeObjectPropertiesWithDictionaryMap(
T dict, uint32_t property_count, bool has_custom_property_attributes); 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>(), Handle<HeapObject> object_for_deferred_reference = Handle<HeapObject>(),
uint32_t index_for_deferred_reference = 0, uint32_t index_for_deferred_reference = 0,
InternalizeStrings internalize_strings = InternalizeStrings::kNo); InternalizeStrings internalize_strings = InternalizeStrings::kNo);
...@@ -415,10 +418,14 @@ class V8_EXPORT WebSnapshotDeserializer ...@@ -415,10 +418,14 @@ class V8_EXPORT WebSnapshotDeserializer
String ReadInPlaceString( String ReadInPlaceString(
InternalizeStrings internalize_strings = InternalizeStrings::kNo); InternalizeStrings internalize_strings = InternalizeStrings::kNo);
Object ReadSymbol(); Object ReadSymbol();
Object ReadArray(Handle<HeapObject> container, uint32_t container_index); std::tuple<Object, bool> ReadArray(Handle<HeapObject> container,
Object ReadObject(Handle<HeapObject> container, uint32_t container_index); uint32_t container_index);
Object ReadFunction(Handle<HeapObject> container, uint32_t container_index); std::tuple<Object, bool> ReadObject(Handle<HeapObject> container,
Object ReadClass(Handle<HeapObject> container, uint32_t container_index); 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 ReadRegexp();
Object ReadBuiltinObjectReference(); Object ReadBuiltinObjectReference();
Object ReadExternalReference(); Object ReadExternalReference();
......
...@@ -90,9 +90,9 @@ TEST(Minimal) { ...@@ -90,9 +90,9 @@ TEST(Minimal) {
const char* snapshot_source = "var foo = {'key': 'lol'};"; const char* snapshot_source = "var foo = {'key': 'lol'};";
const char* test_source = "foo.key"; const char* test_source = "foo.key";
const char* expected_result = "lol"; 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 kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0; uint32_t kBuiltinObjectCount = 1;
uint32_t kMapCount = 1; uint32_t kMapCount = 1;
uint32_t kContextCount = 0; uint32_t kContextCount = 0;
uint32_t kFunctionCount = 0; uint32_t kFunctionCount = 0;
...@@ -106,9 +106,9 @@ TEST(Minimal) { ...@@ -106,9 +106,9 @@ TEST(Minimal) {
TEST(EmptyObject) { TEST(EmptyObject) {
const char* snapshot_source = "var foo = {}"; const char* snapshot_source = "var foo = {}";
const char* test_source = "foo"; const char* test_source = "foo";
uint32_t kStringCount = 1; // 'foo' uint32_t kStringCount = 2; // 'foo', 'Object.prototype'
uint32_t kSymbolCount = 0; uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0; uint32_t kBuiltinObjectCount = 1;
uint32_t kMapCount = 1; uint32_t kMapCount = 1;
uint32_t kContextCount = 0; uint32_t kContextCount = 0;
uint32_t kFunctionCount = 0; uint32_t kFunctionCount = 0;
...@@ -138,9 +138,10 @@ TEST(Numbers) { ...@@ -138,9 +138,10 @@ TEST(Numbers) {
" 'f': Number.NEGATIVE_INFINITY,\n" " 'f': Number.NEGATIVE_INFINITY,\n"
"}"; "}";
const char* test_source = "foo"; 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 kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0; uint32_t kBuiltinObjectCount = 1;
uint32_t kMapCount = 1; uint32_t kMapCount = 1;
uint32_t kContextCount = 0; uint32_t kContextCount = 0;
uint32_t kFunctionCount = 0; uint32_t kFunctionCount = 0;
...@@ -195,9 +196,10 @@ TEST(Oddballs) { ...@@ -195,9 +196,10 @@ TEST(Oddballs) {
" 'd': undefined,\n" " 'd': undefined,\n"
"}"; "}";
const char* test_source = "foo"; 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 kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0; uint32_t kBuiltinObjectCount = 1;
uint32_t kMapCount = 1; uint32_t kMapCount = 1;
uint32_t kContextCount = 0; uint32_t kContextCount = 0;
uint32_t kFunctionCount = 0; uint32_t kFunctionCount = 0;
...@@ -226,9 +228,10 @@ TEST(Function) { ...@@ -226,9 +228,10 @@ TEST(Function) {
"var foo = {'key': function() { return '11525'; }};"; "var foo = {'key': function() { return '11525'; }};";
const char* test_source = "foo.key()"; const char* test_source = "foo.key()";
const char* expected_result = "11525"; 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 kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0; uint32_t kBuiltinObjectCount = 1;
uint32_t kMapCount = 1; uint32_t kMapCount = 1;
uint32_t kContextCount = 0; uint32_t kContextCount = 0;
uint32_t kFunctionCount = 1; uint32_t kFunctionCount = 1;
...@@ -248,10 +251,11 @@ TEST(InnerFunctionWithContext) { ...@@ -248,10 +251,11 @@ TEST(InnerFunctionWithContext) {
" })()};"; " })()};";
const char* test_source = "foo.key()"; const char* test_source = "foo.key()";
const char* expected_result = "11525"; const char* expected_result = "11525";
// Strings: 'foo', 'result', function source code (inner). 'key' is in-place. // Strings: 'foo', 'result', 'Object.prototype'. function source code (inner).
uint32_t kStringCount = 3; // 'key' is in-place.
uint32_t kStringCount = 4;
uint32_t kSymbolCount = 0; uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0; uint32_t kBuiltinObjectCount = 1;
uint32_t kMapCount = 1; uint32_t kMapCount = 1;
uint32_t kContextCount = 1; uint32_t kContextCount = 1;
uint32_t kFunctionCount = 1; uint32_t kFunctionCount = 1;
...@@ -277,10 +281,11 @@ TEST(InnerFunctionWithContextAndParentContext) { ...@@ -277,10 +281,11 @@ TEST(InnerFunctionWithContextAndParentContext) {
" })()};"; " })()};";
const char* test_source = "foo.key()"; const char* test_source = "foo.key()";
const char* expected_result = "11525"; const char* expected_result = "11525";
// Strings: 'foo', function source code (innerinner), 'part1', 'part2'. // Strings: 'foo', 'Object.prototype', function source code (innerinner),
uint32_t kStringCount = 4; // 'part1', 'part2'.
uint32_t kStringCount = 5;
uint32_t kSymbolCount = 0; uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0; uint32_t kBuiltinObjectCount = 1;
uint32_t kMapCount = 1; uint32_t kMapCount = 1;
uint32_t kContextCount = 2; uint32_t kContextCount = 2;
uint32_t kFunctionCount = 1; uint32_t kFunctionCount = 1;
...@@ -294,9 +299,10 @@ TEST(InnerFunctionWithContextAndParentContext) { ...@@ -294,9 +299,10 @@ TEST(InnerFunctionWithContextAndParentContext) {
TEST(RegExp) { TEST(RegExp) {
const char* snapshot_source = "var foo = {'re': /ab+c/gi}"; const char* snapshot_source = "var foo = {'re': /ab+c/gi}";
const char* test_source = "foo"; 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 kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0; uint32_t kBuiltinObjectCount = 1;
uint32_t kMapCount = 1; uint32_t kMapCount = 1;
uint32_t kContextCount = 0; uint32_t kContextCount = 0;
uint32_t kFunctionCount = 0; uint32_t kFunctionCount = 0;
...@@ -327,9 +333,10 @@ TEST(RegExp) { ...@@ -327,9 +333,10 @@ TEST(RegExp) {
TEST(RegExpNoFlags) { TEST(RegExpNoFlags) {
const char* snapshot_source = "var foo = {'re': /ab+c/}"; const char* snapshot_source = "var foo = {'re': /ab+c/}";
const char* test_source = "foo"; 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 kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0; uint32_t kBuiltinObjectCount = 1;
uint32_t kMapCount = 1; uint32_t kMapCount = 1;
uint32_t kContextCount = 0; uint32_t kContextCount = 0;
uint32_t kFunctionCount = 0; uint32_t kFunctionCount = 0;
...@@ -719,9 +726,10 @@ TEST(FunctionKinds) { ...@@ -719,9 +726,10 @@ TEST(FunctionKinds) {
" f: async function*() {}\n" " f: async function*() {}\n"
"}"; "}";
const char* test_source = "foo"; 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 kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0; uint32_t kBuiltinObjectCount = 1;
uint32_t kMapCount = 1; uint32_t kMapCount = 1;
uint32_t kContextCount = 0; uint32_t kContextCount = 0;
uint32_t kFunctionCount = 6; uint32_t kFunctionCount = 6;
...@@ -931,10 +939,10 @@ TEST(InPlaceStringsInObjects) { ...@@ -931,10 +939,10 @@ TEST(InPlaceStringsInObjects) {
const char* snapshot_source = "var foo = {a: 'one', b: 'two', c: 'three'};"; const char* snapshot_source = "var foo = {a: 'one', b: 'two', c: 'three'};";
const char* test_source = "foo.a + foo.b + foo.c;"; const char* test_source = "foo.a + foo.b + foo.c;";
const char* expected_result = "onetwothree"; const char* expected_result = "onetwothree";
// 'foo'. Other strings are in-place. // 'foo', 'Object.prototype'. Other strings are in-place.
uint32_t kStringCount = 1; uint32_t kStringCount = 2;
uint32_t kSymbolCount = 0; uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0; uint32_t kBuiltinObjectCount = 1;
uint32_t kMapCount = 1; uint32_t kMapCount = 1;
uint32_t kContextCount = 0; uint32_t kContextCount = 0;
uint32_t kFunctionCount = 0; uint32_t kFunctionCount = 0;
...@@ -949,10 +957,10 @@ TEST(RepeatedInPlaceStringsInObjects) { ...@@ -949,10 +957,10 @@ TEST(RepeatedInPlaceStringsInObjects) {
const char* snapshot_source = "var foo = {a: 'one', b: 'two', c: 'one'};"; const char* snapshot_source = "var foo = {a: 'one', b: 'two', c: 'one'};";
const char* test_source = "foo.a + foo.b + foo.c;"; const char* test_source = "foo.a + foo.b + foo.c;";
const char* expected_result = "onetwoone"; const char* expected_result = "onetwoone";
// 'foo', 'one'. Other strings are in-place. // 'foo', 'one', 'Object.prototype'. Other strings are in-place.
uint32_t kStringCount = 2; uint32_t kStringCount = 3;
uint32_t kSymbolCount = 0; uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 0; uint32_t kBuiltinObjectCount = 1;
uint32_t kMapCount = 1; uint32_t kMapCount = 1;
uint32_t kContextCount = 0; uint32_t kContextCount = 0;
uint32_t kFunctionCount = 0; uint32_t kFunctionCount = 0;
...@@ -963,14 +971,14 @@ TEST(RepeatedInPlaceStringsInObjects) { ...@@ -963,14 +971,14 @@ TEST(RepeatedInPlaceStringsInObjects) {
kFunctionCount, kObjectCount, kArrayCount); kFunctionCount, kObjectCount, kArrayCount);
} }
TEST(builtin_objects) { TEST(BuiltinObjects) {
const char* snapshot_source = "var foo = {a: Error.prototype};"; const char* snapshot_source = "var foo = {a: Error.prototype};";
const char* test_source = "foo.a == Error.prototype ? \"pass\" : \"fail\""; const char* test_source = "foo.a == Error.prototype ? \"pass\" : \"fail\"";
const char* expected_result = "pass"; const char* expected_result = "pass";
// 'foo', 'Error.prototype'. Other strings are in-place. // 'foo', 'Error.prototype', 'Object.prototype'. Other strings are in-place.
uint32_t kStringCount = 2; uint32_t kStringCount = 3;
uint32_t kSymbolCount = 0; uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 1; uint32_t kBuiltinObjectCount = 2;
uint32_t kMapCount = 1; uint32_t kMapCount = 1;
uint32_t kContextCount = 0; uint32_t kContextCount = 0;
uint32_t kFunctionCount = 0; uint32_t kFunctionCount = 0;
...@@ -981,15 +989,15 @@ TEST(builtin_objects) { ...@@ -981,15 +989,15 @@ TEST(builtin_objects) {
kFunctionCount, kObjectCount, kArrayCount); kFunctionCount, kObjectCount, kArrayCount);
} }
TEST(builtin_objectsDeduplicated) { TEST(BuiltinObjectsDeduplicated) {
const char* snapshot_source = const char* snapshot_source =
"var foo = {a: Error.prototype, b: Error.prototype}"; "var foo = {a: Error.prototype, b: Error.prototype}";
const char* test_source = "foo.a === Error.prototype ? \"pass\" : \"fail\""; const char* test_source = "foo.a === Error.prototype ? \"pass\" : \"fail\"";
const char* expected_result = "pass"; const char* expected_result = "pass";
// 'foo', 'Error.prototype'. Other strings are in-place. // 'foo', 'Error.prototype', 'Object.prototype'. Other strings are in-place.
uint32_t kStringCount = 2; uint32_t kStringCount = 3;
uint32_t kSymbolCount = 0; uint32_t kSymbolCount = 0;
uint32_t kBuiltinObjectCount = 1; uint32_t kBuiltinObjectCount = 2;
uint32_t kMapCount = 1; uint32_t kMapCount = 1;
uint32_t kContextCount = 0; uint32_t kContextCount = 0;
uint32_t kFunctionCount = 0; uint32_t kFunctionCount = 0;
......
...@@ -41,3 +41,49 @@ d8.file.execute('test/mjsunit/web-snapshot/web-snapshot-helpers.js'); ...@@ -41,3 +41,49 @@ d8.file.execute('test/mjsunit/web-snapshot/web-snapshot-helpers.js');
assertEquals(1, obj.__proto__.x); assertEquals(1, obj.__proto__.x);
assertSame(Realm.eval(realm, 'Object.prototype'), obj.__proto__.__proto__); 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