Commit 1ac6b3a6 authored by Marja Hölttä's avatar Marja Hölttä Committed by V8 LUCI CQ

Reland: [web snap] Support Symbols

Fix: copy-paste error in raw ptr GC update function.

Bug: v8:11525
Change-Id: I915ae92191b2add60962395a2d0ad28f57e02fd4
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3622915
Commit-Queue: Marja Hölttä <marja@chromium.org>
Reviewed-by: 's avatarCamillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/main@{#80328}
parent 8062598f
......@@ -490,6 +490,7 @@ class RuntimeCallTimer final {
V(WebSnapshotDeserialize_Maps) \
V(WebSnapshotDeserialize_Objects) \
V(WebSnapshotDeserialize_Strings) \
V(WebSnapshotDeserialize_Symbols) \
V(WrappedFunctionLengthGetter) \
V(WrappedFunctionNameGetter)
......
......@@ -197,6 +197,7 @@ WebSnapshotSerializer::WebSnapshotSerializer(v8::Isolate* isolate)
WebSnapshotSerializer::WebSnapshotSerializer(Isolate* isolate)
: WebSnapshotSerializerDeserializer(isolate),
string_serializer_(isolate_, nullptr),
symbol_serializer_(isolate_, nullptr),
map_serializer_(isolate_, nullptr),
context_serializer_(isolate_, nullptr),
function_serializer_(isolate_, nullptr),
......@@ -206,6 +207,7 @@ WebSnapshotSerializer::WebSnapshotSerializer(Isolate* isolate)
export_serializer_(isolate_, nullptr),
external_objects_ids_(isolate_->heap()),
string_ids_(isolate_->heap()),
symbol_ids_(isolate_->heap()),
map_ids_(isolate_->heap()),
context_ids_(isolate_->heap()),
function_ids_(isolate_->heap()),
......@@ -214,13 +216,14 @@ WebSnapshotSerializer::WebSnapshotSerializer(Isolate* isolate)
object_ids_(isolate_->heap()),
all_strings_(isolate_->heap()) {
auto empty_array_list = factory()->empty_array_list();
strings_ = empty_array_list;
symbols_ = empty_array_list;
maps_ = empty_array_list;
contexts_ = empty_array_list;
functions_ = empty_array_list;
classes_ = empty_array_list;
arrays_ = empty_array_list;
objects_ = empty_array_list;
strings_ = empty_array_list;
maps_ = empty_array_list;
}
WebSnapshotSerializer::~WebSnapshotSerializer() {}
......@@ -316,6 +319,11 @@ void WebSnapshotSerializer::SerializePendingItems() {
SerializeString(string, string_serializer_);
}
for (int i = 0; i < symbols_->Length(); ++i) {
Handle<Symbol> symbol = handle(Symbol::cast(symbols_->Get(i)), isolate_);
SerializeSymbol(symbol);
}
for (int i = 0; i < maps_->Length(); ++i) {
Handle<Map> map = handle(Map::cast(maps_->Get(i)), isolate_);
SerializeMap(map);
......@@ -355,6 +363,9 @@ void WebSnapshotSerializer::SerializePendingItems() {
// - String count
// - For each string:
// - Serialized string
// - Symbol count
// - For each symbol:
// - Serialized symbol
// - Shape count
// - For each shape:
// - Serialized shape
......@@ -380,10 +391,11 @@ void WebSnapshotSerializer::WriteSnapshot(uint8_t*& buffer,
ValueSerializer total_serializer(isolate_, nullptr);
size_t needed_size =
sizeof(kMagicNumber) + string_serializer_.buffer_size_ +
map_serializer_.buffer_size_ + context_serializer_.buffer_size_ +
function_serializer_.buffer_size_ + class_serializer_.buffer_size_ +
array_serializer_.buffer_size_ + object_serializer_.buffer_size_ +
export_serializer_.buffer_size_ + 8 * sizeof(uint32_t);
symbol_serializer_.buffer_size_ + map_serializer_.buffer_size_ +
context_serializer_.buffer_size_ + function_serializer_.buffer_size_ +
class_serializer_.buffer_size_ + array_serializer_.buffer_size_ +
object_serializer_.buffer_size_ + export_serializer_.buffer_size_ +
8 * sizeof(uint32_t);
if (total_serializer.ExpandBuffer(needed_size).IsNothing()) {
Throw("Out of memory");
return;
......@@ -391,6 +403,7 @@ void WebSnapshotSerializer::WriteSnapshot(uint8_t*& buffer,
total_serializer.WriteRawBytes(kMagicNumber, 4);
WriteObjects(total_serializer, string_count(), string_serializer_, "strings");
WriteObjects(total_serializer, symbol_count(), symbol_serializer_, "symbols");
WriteObjects(total_serializer, map_count(), map_serializer_, "maps");
WriteObjects(total_serializer, context_count(), context_serializer_,
"contexts");
......@@ -458,6 +471,23 @@ void WebSnapshotSerializer::SerializeString(Handle<String> string,
}
}
// Format (serialized symbol):
// - 0 if the symbol is non-global and there's no description, 1 if the symbol
// is non-global and there is a description, 2 if the symbol is global (there
// must be a description).
void WebSnapshotSerializer::SerializeSymbol(Handle<Symbol> symbol) {
if (symbol->description().IsUndefined()) {
CHECK(!symbol->is_in_public_symbol_table());
symbol_serializer_.WriteUint32(SymbolType::kNonGlobalNoDesription);
} else {
symbol_serializer_.WriteUint32(symbol->is_in_public_symbol_table()
? SymbolType::kGlobal
: SymbolType::kNonGlobal);
WriteStringId(handle(String::cast(symbol->description()), isolate_),
symbol_serializer_);
}
}
// Format (serialized shape):
// - PropertyAttributesType
// - 0 if the __proto__ is Object.prototype, 1 + object id for the __proto__
......@@ -649,6 +679,9 @@ void WebSnapshotSerializer::Discover(Handle<HeapObject> start_object) {
case JS_ARRAY_TYPE:
DiscoverArray(Handle<JSArray>::cast(object));
break;
case SYMBOL_TYPE:
DiscoverSymbol(Handle<Symbol>::cast(object));
break;
case ODDBALL_TYPE:
case HEAP_NUMBER_TYPE:
// Can't contain references to other objects.
......@@ -896,6 +929,18 @@ void WebSnapshotSerializer::DiscoverObject(Handle<JSObject> object) {
}
}
void WebSnapshotSerializer::DiscoverSymbol(Handle<Symbol> symbol) {
uint32_t id;
if (InsertIntoIndexMap(symbol_ids_, *symbol, id)) return;
DCHECK_EQ(id, symbols_->Length());
symbols_ = ArrayList::Add(isolate_, symbols_, symbol);
if (!symbol->description().IsUndefined()) {
DiscoverString(handle(String::cast(symbol->description()), isolate_));
}
}
// Format (serialized function):
// - 0 if there's no context, 1 + context id otherwise
// - String id (source snippet)
......@@ -1128,6 +1173,10 @@ void WebSnapshotSerializer::WriteValue(Handle<Object> object,
serializer.WriteUint32(ValueType::ARRAY_ID);
serializer.WriteUint32(GetArrayId(JSArray::cast(*heap_object)));
break;
case SYMBOL_TYPE:
serializer.WriteUint32(ValueType::SYMBOL_ID);
serializer.WriteUint32(GetSymbolId(Symbol::cast(*heap_object)));
break;
case JS_REG_EXP_TYPE: {
Handle<JSRegExp> regexp = Handle<JSRegExp>::cast(heap_object);
if (regexp->map() != isolate_->regexp_function()->initial_map()) {
......@@ -1191,6 +1240,14 @@ uint32_t WebSnapshotSerializer::GetStringId(Handle<String> string,
return static_cast<uint32_t>(id);
}
uint32_t WebSnapshotSerializer::GetSymbolId(Symbol symbol) {
int id;
bool return_value = symbol_ids_.Lookup(symbol, &id);
DCHECK(return_value);
USE(return_value);
return static_cast<uint32_t>(id);
}
uint32_t WebSnapshotSerializer::GetMapId(Map map) {
int id;
bool return_value = map_ids_.Lookup(map, &id);
......@@ -1272,6 +1329,7 @@ WebSnapshotDeserializer::WebSnapshotDeserializer(
roots_(isolate) {
Handle<FixedArray> empty_array = factory()->empty_fixed_array();
strings_handle_ = empty_array;
symbols_handle_ = empty_array;
maps_handle_ = empty_array;
contexts_handle_ = empty_array;
functions_handle_ = empty_array;
......@@ -1289,6 +1347,7 @@ WebSnapshotDeserializer::~WebSnapshotDeserializer() {
void WebSnapshotDeserializer::UpdatePointers() {
strings_ = *strings_handle_;
symbols_ = *symbols_handle_;
maps_ = *maps_handle_;
contexts_ = *contexts_handle_;
functions_ = *functions_handle_;
......@@ -1353,6 +1412,7 @@ base::Vector<const uint8_t> WebSnapshotDeserializer::ExtractScriptBuffer(
void WebSnapshotDeserializer::Throw(const char* message) {
string_count_ = 0;
symbol_count_ = 0;
map_count_ = 0;
context_count_ = 0;
class_count_ = 0;
......@@ -1412,6 +1472,7 @@ bool WebSnapshotDeserializer::DeserializeSnapshot(bool skip_exports) {
}
DeserializeStrings();
DeserializeSymbols();
DeserializeMaps();
DeserializeContexts();
DeserializeFunctions();
......@@ -1512,6 +1573,54 @@ String WebSnapshotDeserializer::ReadInPlaceString(bool internalize) {
return *string;
}
Object WebSnapshotDeserializer::ReadSymbol() {
DCHECK(!strings_handle_->is_null());
uint32_t symbol_id;
if (!deserializer_.ReadUint32(&symbol_id) || symbol_id >= symbol_count_) {
Throw("malformed symbol id\n");
return roots_.undefined_value();
}
return symbols_.get(symbol_id);
}
void WebSnapshotDeserializer::DeserializeSymbols() {
RCS_SCOPE(isolate_, RuntimeCallCounterId::kWebSnapshotDeserialize_Symbols);
if (!deserializer_.ReadUint32(&symbol_count_) ||
symbol_count_ > kMaxItemCount) {
Throw("Malformed symbol table");
return;
}
STATIC_ASSERT(kMaxItemCount <= FixedArray::kMaxLength);
symbols_handle_ = factory()->NewFixedArray(symbol_count_);
symbols_ = *symbols_handle_;
for (uint32_t i = 0; i < symbol_count_; ++i) {
uint32_t symbol_type;
if (!deserializer_.ReadUint32(&symbol_type) || symbol_type > 2) {
Throw("malformed symbol\n");
}
Handle<Symbol> symbol;
if (symbol_type == SymbolType::kNonGlobalNoDesription) {
symbol = factory()->NewSymbol();
} else { // Symbol with description
uint32_t string_id;
if (!deserializer_.ReadUint32(&string_id) || string_id >= string_count_) {
Throw("malformed string id\n");
}
if (symbol_type == SymbolType::kNonGlobal) {
symbol = factory()->NewSymbol();
symbol->set_description(String::cast(strings_.get(string_id)));
} else {
DCHECK_EQ(SymbolType::kGlobal, symbol_type);
symbol = isolate_->SymbolFor(
RootIndex::kPublicSymbolTable,
handle(String::cast(strings_.get(string_id)), isolate_), false);
}
}
symbols_.set(i, *symbol);
}
}
void WebSnapshotDeserializer::DeserializeMaps() {
RCS_SCOPE(isolate_, RuntimeCallCounterId::kWebSnapshotDeserialize_Maps);
if (!deserializer_.ReadUint32(&map_count_) || map_count_ > kMaxItemCount) {
......@@ -2213,6 +2322,8 @@ Object WebSnapshotDeserializer::ReadValue(Handle<HeapObject> container,
return ReadClass(container, container_index);
case ValueType::REGEXP:
return ReadRegexp();
case ValueType::SYMBOL_ID:
return ReadSymbol();
case ValueType::EXTERNAL_ID:
return ReadExternalReference();
case ValueType::IN_PLACE_STRING_ID:
......
......@@ -49,16 +49,23 @@ class WebSnapshotSerializerDeserializer {
NO_ELEMENT_CONSTANT,
INTEGER,
DOUBLE,
REGEXP,
STRING_ID,
ARRAY_ID,
OBJECT_ID,
FUNCTION_ID,
CLASS_ID,
REGEXP,
SYMBOL_ID,
EXTERNAL_ID,
IN_PLACE_STRING_ID
};
enum SymbolType : uint8_t {
kNonGlobalNoDesription = 0,
kNonGlobal = 1,
kGlobal = 2
};
static constexpr uint8_t kMagicNumber[4] = {'+', '+', '+', ';'};
enum ContextType : uint8_t { FUNCTION, BLOCK };
......@@ -133,6 +140,10 @@ class V8_EXPORT WebSnapshotSerializer
return static_cast<uint32_t>(string_ids_.size());
}
uint32_t symbol_count() const {
return static_cast<uint32_t>(symbol_ids_.size());
}
uint32_t map_count() const { return static_cast<uint32_t>(map_ids_.size()); }
uint32_t context_count() const {
......@@ -183,6 +194,7 @@ class V8_EXPORT WebSnapshotSerializer
void Discover(Handle<HeapObject> object);
void DiscoverString(Handle<String> string,
AllowInPlace can_be_in_place = AllowInPlace::No);
void DiscoverSymbol(Handle<Symbol> symbol);
void DiscoverMap(Handle<Map> map);
void DiscoverFunction(Handle<JSFunction> function);
void DiscoverClass(Handle<JSFunction> function);
......@@ -197,6 +209,7 @@ class V8_EXPORT WebSnapshotSerializer
Handle<JSFunction> function);
void SerializeString(Handle<String> string, ValueSerializer& serializer);
void SerializeSymbol(Handle<Symbol> symbol);
void SerializeMap(Handle<Map> map);
void SerializeFunction(Handle<JSFunction> function);
void SerializeClass(Handle<JSFunction> function);
......@@ -211,6 +224,7 @@ class V8_EXPORT WebSnapshotSerializer
void WriteStringId(Handle<String> string, ValueSerializer& serializer);
uint32_t GetStringId(Handle<String> string, bool& in_place);
uint32_t GetSymbolId(Symbol symbol);
uint32_t GetMapId(Map map);
uint32_t GetFunctionId(JSFunction function);
uint32_t GetClassId(JSFunction function);
......@@ -220,6 +234,7 @@ class V8_EXPORT WebSnapshotSerializer
uint32_t GetExternalId(HeapObject object);
ValueSerializer string_serializer_;
ValueSerializer symbol_serializer_;
ValueSerializer map_serializer_;
ValueSerializer context_serializer_;
ValueSerializer function_serializer_;
......@@ -229,13 +244,14 @@ class V8_EXPORT WebSnapshotSerializer
ValueSerializer export_serializer_;
// These are needed for being able to serialize items in order.
Handle<ArrayList> strings_;
Handle<ArrayList> symbols_;
Handle<ArrayList> maps_;
Handle<ArrayList> contexts_;
Handle<ArrayList> functions_;
Handle<ArrayList> classes_;
Handle<ArrayList> arrays_;
Handle<ArrayList> objects_;
Handle<ArrayList> strings_;
Handle<ArrayList> maps_;
// IndexMap to keep track of explicitly blocked external objects and
// non-serializable/not-supported objects (e.g. API Objects).
......@@ -246,6 +262,7 @@ class V8_EXPORT WebSnapshotSerializer
// them in the reverse order. This ensures that the items this item points to
// have a lower ID and will be deserialized first.
ObjectCacheIndexMap string_ids_;
ObjectCacheIndexMap symbol_ids_;
ObjectCacheIndexMap map_ids_;
ObjectCacheIndexMap context_ids_;
ObjectCacheIndexMap function_ids_;
......@@ -285,6 +302,7 @@ class V8_EXPORT WebSnapshotDeserializer
// For inspecting the state after deserializing a snapshot.
uint32_t string_count() const { return string_count_; }
uint32_t symbol_count() const { return symbol_count_; }
uint32_t map_count() const { return map_count_; }
uint32_t context_count() const { return context_count_; }
uint32_t function_count() const { return function_count_; }
......@@ -314,6 +332,7 @@ class V8_EXPORT WebSnapshotDeserializer
WebSnapshotDeserializer& operator=(const WebSnapshotDeserializer&) = delete;
void DeserializeStrings();
void DeserializeSymbols();
void DeserializeMaps();
void DeserializeContexts();
Handle<ScopeInfo> CreateScopeInfo(uint32_t variable_count, bool has_parent,
......@@ -337,6 +356,7 @@ class V8_EXPORT WebSnapshotDeserializer
Object ReadNumber();
String ReadString(bool internalize = false);
String ReadInPlaceString(bool internalize = false);
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);
......@@ -357,6 +377,9 @@ class V8_EXPORT WebSnapshotDeserializer
Handle<FixedArray> strings_handle_;
FixedArray strings_;
Handle<FixedArray> symbols_handle_;
FixedArray symbols_;
Handle<FixedArray> maps_handle_;
FixedArray maps_;
......@@ -391,6 +414,7 @@ class V8_EXPORT WebSnapshotDeserializer
Handle<Object> return_value_;
uint32_t string_count_ = 0;
uint32_t symbol_count_ = 0;
uint32_t map_count_ = 0;
uint32_t context_count_ = 0;
uint32_t function_count_ = 0;
......
......@@ -335,3 +335,26 @@ d8.file.execute('test/mjsunit/web-snapshot/web-snapshot-helpers.js');
const {foo} = takeAndUseWebSnapshot(createObjects, ['foo']);
assertEquals('bar', foo());
})();
(function TestNonGlobalSymbol() {
function createObjects() {
const s = Symbol('description');
globalThis.foo = {mySymbol: s, innerObject: { symbolHereToo: s}};
}
const {foo} = takeAndUseWebSnapshot(createObjects, ['foo']);
assertEquals(foo.mySymbol, foo.innerObject.symbolHereToo);
assertEquals('description', foo.mySymbol.description);
assertNotEquals(foo.mySymbol, Symbol('description'));
assertNotEquals(foo.mySymbol, Symbol.for('description'));
})();
(function TestGlobalSymbol() {
function createObjects() {
const s = Symbol.for('this is global');
globalThis.foo = {mySymbol: s, innerObject: { symbolHereToo: s}};
}
const {foo} = takeAndUseWebSnapshot(createObjects, ['foo']);
assertEquals(foo.mySymbol, foo.innerObject.symbolHereToo);
assertEquals('this is global', foo.mySymbol.description);
assertEquals(Symbol.for('this is global'), foo.mySymbol);
})();
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