mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-01 13:49:16 +00:00
LibJS: Add cache for the string "[object Object]"
This is very frequently returned by Object.prototype.toString(), so we benefit from caching it instead of recreating it every time. Takes Speedometer2.1/EmberJS-Debug-TodoMVC from ~4000ms to ~3700ms on my M3 MacBook Pro.
This commit is contained in:
parent
e8c351505e
commit
81d4ed27d8
Notes:
github-actions[bot]
2025-04-15 11:09:47 +00:00
Author: https://github.com/awesomekling
Commit: 81d4ed27d8
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/4359
4 changed files with 27 additions and 20 deletions
|
@ -193,6 +193,10 @@ JS_DEFINE_NATIVE_FUNCTION(ObjectPrototype::to_string)
|
||||||
tag = to_string_tag.as_string().utf8_string_view();
|
tag = to_string_tag.as_string().utf8_string_view();
|
||||||
|
|
||||||
// 17. Return the string-concatenation of "[object ", tag, and "]".
|
// 17. Return the string-concatenation of "[object ", tag, and "]".
|
||||||
|
|
||||||
|
// OPTIMIZATION: The VM has a cache for the extremely common "[object Object]" string.
|
||||||
|
if (tag == "Object"sv)
|
||||||
|
return vm.cached_strings.object_Object;
|
||||||
return PrimitiveString::create(vm, MUST(String::formatted("[object {}]", tag)));
|
return PrimitiveString::create(vm, MUST(String::formatted("[object {}]", tag)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -74,7 +74,7 @@ VM::VM(OwnPtr<CustomData> custom_data, ErrorMessages error_messages)
|
||||||
|
|
||||||
m_empty_string = m_heap.allocate<PrimitiveString>(String {});
|
m_empty_string = m_heap.allocate<PrimitiveString>(String {});
|
||||||
|
|
||||||
typeof_strings = {
|
cached_strings = {
|
||||||
.number = m_heap.allocate<PrimitiveString>("number"_string),
|
.number = m_heap.allocate<PrimitiveString>("number"_string),
|
||||||
.undefined = m_heap.allocate<PrimitiveString>("undefined"_string),
|
.undefined = m_heap.allocate<PrimitiveString>("undefined"_string),
|
||||||
.object = m_heap.allocate<PrimitiveString>("object"_string),
|
.object = m_heap.allocate<PrimitiveString>("object"_string),
|
||||||
|
@ -83,6 +83,7 @@ VM::VM(OwnPtr<CustomData> custom_data, ErrorMessages error_messages)
|
||||||
.boolean = m_heap.allocate<PrimitiveString>("boolean"_string),
|
.boolean = m_heap.allocate<PrimitiveString>("boolean"_string),
|
||||||
.bigint = m_heap.allocate<PrimitiveString>("bigint"_string),
|
.bigint = m_heap.allocate<PrimitiveString>("bigint"_string),
|
||||||
.function = m_heap.allocate<PrimitiveString>("function"_string),
|
.function = m_heap.allocate<PrimitiveString>("function"_string),
|
||||||
|
.object_Object = m_heap.allocate<PrimitiveString>("[object Object]"_string),
|
||||||
};
|
};
|
||||||
|
|
||||||
for (size_t i = 0; i < single_ascii_character_strings.size(); ++i)
|
for (size_t i = 0; i < single_ascii_character_strings.size(); ++i)
|
||||||
|
@ -239,14 +240,15 @@ void VM::gather_roots(HashMap<GC::Cell*, GC::HeapRoot>& roots)
|
||||||
for (auto string : m_single_ascii_character_strings)
|
for (auto string : m_single_ascii_character_strings)
|
||||||
roots.set(string, GC::HeapRoot { .type = GC::HeapRoot::Type::VM });
|
roots.set(string, GC::HeapRoot { .type = GC::HeapRoot::Type::VM });
|
||||||
|
|
||||||
roots.set(typeof_strings.number, GC::HeapRoot { .type = GC::HeapRoot::Type::VM });
|
roots.set(cached_strings.number, GC::HeapRoot { .type = GC::HeapRoot::Type::VM });
|
||||||
roots.set(typeof_strings.undefined, GC::HeapRoot { .type = GC::HeapRoot::Type::VM });
|
roots.set(cached_strings.undefined, GC::HeapRoot { .type = GC::HeapRoot::Type::VM });
|
||||||
roots.set(typeof_strings.object, GC::HeapRoot { .type = GC::HeapRoot::Type::VM });
|
roots.set(cached_strings.object, GC::HeapRoot { .type = GC::HeapRoot::Type::VM });
|
||||||
roots.set(typeof_strings.string, GC::HeapRoot { .type = GC::HeapRoot::Type::VM });
|
roots.set(cached_strings.string, GC::HeapRoot { .type = GC::HeapRoot::Type::VM });
|
||||||
roots.set(typeof_strings.symbol, GC::HeapRoot { .type = GC::HeapRoot::Type::VM });
|
roots.set(cached_strings.symbol, GC::HeapRoot { .type = GC::HeapRoot::Type::VM });
|
||||||
roots.set(typeof_strings.boolean, GC::HeapRoot { .type = GC::HeapRoot::Type::VM });
|
roots.set(cached_strings.boolean, GC::HeapRoot { .type = GC::HeapRoot::Type::VM });
|
||||||
roots.set(typeof_strings.bigint, GC::HeapRoot { .type = GC::HeapRoot::Type::VM });
|
roots.set(cached_strings.bigint, GC::HeapRoot { .type = GC::HeapRoot::Type::VM });
|
||||||
roots.set(typeof_strings.function, GC::HeapRoot { .type = GC::HeapRoot::Type::VM });
|
roots.set(cached_strings.function, GC::HeapRoot { .type = GC::HeapRoot::Type::VM });
|
||||||
|
roots.set(cached_strings.object_Object, GC::HeapRoot { .type = GC::HeapRoot::Type::VM });
|
||||||
|
|
||||||
#define __JS_ENUMERATE(SymbolName, snake_name) \
|
#define __JS_ENUMERATE(SymbolName, snake_name) \
|
||||||
roots.set(m_well_known_symbols.snake_name, GC::HeapRoot { .type = GC::HeapRoot::Type::VM });
|
roots.set(m_well_known_symbols.snake_name, GC::HeapRoot { .type = GC::HeapRoot::Type::VM });
|
||||||
|
|
|
@ -229,7 +229,8 @@ public:
|
||||||
GC::Ptr<PrimitiveString> boolean;
|
GC::Ptr<PrimitiveString> boolean;
|
||||||
GC::Ptr<PrimitiveString> bigint;
|
GC::Ptr<PrimitiveString> bigint;
|
||||||
GC::Ptr<PrimitiveString> function;
|
GC::Ptr<PrimitiveString> function;
|
||||||
} typeof_strings;
|
GC::Ptr<PrimitiveString> object_Object;
|
||||||
|
} cached_strings;
|
||||||
|
|
||||||
void run_queued_promise_jobs();
|
void run_queued_promise_jobs();
|
||||||
void enqueue_promise_job(GC::Ref<GC::Function<ThrowCompletionOr<Value>()>> job, Realm*);
|
void enqueue_promise_job(GC::Ref<GC::Function<ThrowCompletionOr<Value>()>> job, Realm*);
|
||||||
|
|
|
@ -326,38 +326,38 @@ GC::Ref<PrimitiveString> Value::typeof_(VM& vm) const
|
||||||
{
|
{
|
||||||
// 9. If val is a Number, return "number".
|
// 9. If val is a Number, return "number".
|
||||||
if (is_number())
|
if (is_number())
|
||||||
return *vm.typeof_strings.number;
|
return *vm.cached_strings.number;
|
||||||
|
|
||||||
switch (m_value.tag) {
|
switch (m_value.tag) {
|
||||||
// 4. If val is undefined, return "undefined".
|
// 4. If val is undefined, return "undefined".
|
||||||
case UNDEFINED_TAG:
|
case UNDEFINED_TAG:
|
||||||
return *vm.typeof_strings.undefined;
|
return *vm.cached_strings.undefined;
|
||||||
// 5. If val is null, return "object".
|
// 5. If val is null, return "object".
|
||||||
case NULL_TAG:
|
case NULL_TAG:
|
||||||
return *vm.typeof_strings.object;
|
return *vm.cached_strings.object;
|
||||||
// 6. If val is a String, return "string".
|
// 6. If val is a String, return "string".
|
||||||
case STRING_TAG:
|
case STRING_TAG:
|
||||||
return *vm.typeof_strings.string;
|
return *vm.cached_strings.string;
|
||||||
// 7. If val is a Symbol, return "symbol".
|
// 7. If val is a Symbol, return "symbol".
|
||||||
case SYMBOL_TAG:
|
case SYMBOL_TAG:
|
||||||
return *vm.typeof_strings.symbol;
|
return *vm.cached_strings.symbol;
|
||||||
// 8. If val is a Boolean, return "boolean".
|
// 8. If val is a Boolean, return "boolean".
|
||||||
case BOOLEAN_TAG:
|
case BOOLEAN_TAG:
|
||||||
return *vm.typeof_strings.boolean;
|
return *vm.cached_strings.boolean;
|
||||||
// 10. If val is a BigInt, return "bigint".
|
// 10. If val is a BigInt, return "bigint".
|
||||||
case BIGINT_TAG:
|
case BIGINT_TAG:
|
||||||
return *vm.typeof_strings.bigint;
|
return *vm.cached_strings.bigint;
|
||||||
// 11. Assert: val is an Object.
|
// 11. Assert: val is an Object.
|
||||||
case OBJECT_TAG:
|
case OBJECT_TAG:
|
||||||
// B.3.6.3 Changes to the typeof Operator, https://tc39.es/ecma262/#sec-IsHTMLDDA-internal-slot-typeof
|
// B.3.6.3 Changes to the typeof Operator, https://tc39.es/ecma262/#sec-IsHTMLDDA-internal-slot-typeof
|
||||||
// 12. If val has an [[IsHTMLDDA]] internal slot, return "undefined".
|
// 12. If val has an [[IsHTMLDDA]] internal slot, return "undefined".
|
||||||
if (as_object().is_htmldda())
|
if (as_object().is_htmldda())
|
||||||
return *vm.typeof_strings.undefined;
|
return *vm.cached_strings.undefined;
|
||||||
// 13. If val has a [[Call]] internal slot, return "function".
|
// 13. If val has a [[Call]] internal slot, return "function".
|
||||||
if (is_function())
|
if (is_function())
|
||||||
return *vm.typeof_strings.function;
|
return *vm.cached_strings.function;
|
||||||
// 14. Return "object".
|
// 14. Return "object".
|
||||||
return *vm.typeof_strings.object;
|
return *vm.cached_strings.object;
|
||||||
default:
|
default:
|
||||||
VERIFY_NOT_REACHED();
|
VERIFY_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue