mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-05 15:49:11 +00:00
LibJS: Make typeof
a lot faster by caching all possible results
The typeof operator has a very small set of possible resulting strings, so let's make it much faster by caching those strings on the VM. ~8x speed-up on this microbenchmark: for (let i = 0; i < 10_000_000; ++i) { typeof i; }
This commit is contained in:
parent
3b7534b362
commit
d0b11af387
Notes:
github-actions[bot]
2024-07-23 09:48:32 +00:00
Author: https://github.com/awesomekling
Commit: d0b11af387
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/783
5 changed files with 46 additions and 16 deletions
|
@ -1193,7 +1193,7 @@ inline ThrowCompletionOr<void> put_by_property_key(VM& vm, Value base, Value thi
|
||||||
if (!succeeded && vm.in_strict_mode()) {
|
if (!succeeded && vm.in_strict_mode()) {
|
||||||
if (base.is_object())
|
if (base.is_object())
|
||||||
return vm.throw_completion<TypeError>(ErrorType::ReferenceNullishSetProperty, name, base.to_string_without_side_effects());
|
return vm.throw_completion<TypeError>(ErrorType::ReferenceNullishSetProperty, name, base.to_string_without_side_effects());
|
||||||
return vm.throw_completion<TypeError>(ErrorType::ReferencePrimitiveSetProperty, name, base.typeof(), base.to_string_without_side_effects());
|
return vm.throw_completion<TypeError>(ErrorType::ReferencePrimitiveSetProperty, name, base.typeof(vm)->utf8_string(), base.to_string_without_side_effects());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -2018,7 +2018,7 @@ static ThrowCompletionOr<Value> not_(VM&, Value value)
|
||||||
|
|
||||||
static ThrowCompletionOr<Value> typeof_(VM& vm, Value value)
|
static ThrowCompletionOr<Value> typeof_(VM& vm, Value value)
|
||||||
{
|
{
|
||||||
return PrimitiveString::create(vm, value.typeof());
|
return value.typeof(vm);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define JS_DEFINE_COMMON_UNARY_OP(OpTitleCase, op_snake_case) \
|
#define JS_DEFINE_COMMON_UNARY_OP(OpTitleCase, op_snake_case) \
|
||||||
|
@ -2873,7 +2873,7 @@ ThrowCompletionOr<void> TypeofBinding::execute_impl(Bytecode::Interpreter& inter
|
||||||
environment = environment->outer_environment();
|
environment = environment->outer_environment();
|
||||||
if (!environment->is_permanently_screwed_by_eval()) {
|
if (!environment->is_permanently_screwed_by_eval()) {
|
||||||
auto value = TRY(static_cast<DeclarativeEnvironment const&>(*environment).get_binding_value_direct(vm, m_cache.index));
|
auto value = TRY(static_cast<DeclarativeEnvironment const&>(*environment).get_binding_value_direct(vm, m_cache.index));
|
||||||
interpreter.set(dst(), PrimitiveString::create(vm, value.typeof()));
|
interpreter.set(dst(), value.typeof(vm));
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
m_cache = {};
|
m_cache = {};
|
||||||
|
@ -2897,7 +2897,7 @@ ThrowCompletionOr<void> TypeofBinding::execute_impl(Bytecode::Interpreter& inter
|
||||||
|
|
||||||
// 4. NOTE: This step is replaced in section B.3.6.3.
|
// 4. NOTE: This step is replaced in section B.3.6.3.
|
||||||
// 5. Return a String according to Table 41.
|
// 5. Return a String according to Table 41.
|
||||||
interpreter.set(dst(), PrimitiveString::create(vm, value.typeof()));
|
interpreter.set(dst(), value.typeof(vm));
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -70,6 +70,17 @@ VM::VM(OwnPtr<CustomData> custom_data, ErrorMessages error_messages)
|
||||||
|
|
||||||
m_empty_string = m_heap.allocate_without_realm<PrimitiveString>(String {});
|
m_empty_string = m_heap.allocate_without_realm<PrimitiveString>(String {});
|
||||||
|
|
||||||
|
typeof_strings = {
|
||||||
|
.number = m_heap.allocate_without_realm<PrimitiveString>("number"),
|
||||||
|
.undefined = m_heap.allocate_without_realm<PrimitiveString>("undefined"),
|
||||||
|
.object = m_heap.allocate_without_realm<PrimitiveString>("object"),
|
||||||
|
.string = m_heap.allocate_without_realm<PrimitiveString>("string"),
|
||||||
|
.symbol = m_heap.allocate_without_realm<PrimitiveString>("symbol"),
|
||||||
|
.boolean = m_heap.allocate_without_realm<PrimitiveString>("boolean"),
|
||||||
|
.bigint = m_heap.allocate_without_realm<PrimitiveString>("bigint"),
|
||||||
|
.function = m_heap.allocate_without_realm<PrimitiveString>("function"),
|
||||||
|
};
|
||||||
|
|
||||||
for (size_t i = 0; i < single_ascii_character_strings.size(); ++i)
|
for (size_t i = 0; i < single_ascii_character_strings.size(); ++i)
|
||||||
m_single_ascii_character_strings[i] = m_heap.allocate_without_realm<PrimitiveString>(single_ascii_character_strings[i]);
|
m_single_ascii_character_strings[i] = m_heap.allocate_without_realm<PrimitiveString>(single_ascii_character_strings[i]);
|
||||||
|
|
||||||
|
@ -192,6 +203,15 @@ void VM::gather_roots(HashMap<Cell*, HeapRoot>& roots)
|
||||||
for (auto string : m_single_ascii_character_strings)
|
for (auto string : m_single_ascii_character_strings)
|
||||||
roots.set(string, HeapRoot { .type = HeapRoot::Type::VM });
|
roots.set(string, HeapRoot { .type = HeapRoot::Type::VM });
|
||||||
|
|
||||||
|
roots.set(typeof_strings.number, HeapRoot { .type = HeapRoot::Type::VM });
|
||||||
|
roots.set(typeof_strings.undefined, HeapRoot { .type = HeapRoot::Type::VM });
|
||||||
|
roots.set(typeof_strings.object, HeapRoot { .type = HeapRoot::Type::VM });
|
||||||
|
roots.set(typeof_strings.string, HeapRoot { .type = HeapRoot::Type::VM });
|
||||||
|
roots.set(typeof_strings.symbol, HeapRoot { .type = HeapRoot::Type::VM });
|
||||||
|
roots.set(typeof_strings.boolean, HeapRoot { .type = HeapRoot::Type::VM });
|
||||||
|
roots.set(typeof_strings.bigint, HeapRoot { .type = HeapRoot::Type::VM });
|
||||||
|
roots.set(typeof_strings.function, HeapRoot { .type = HeapRoot::Type::VM });
|
||||||
|
|
||||||
#define __JS_ENUMERATE(SymbolName, snake_name) \
|
#define __JS_ENUMERATE(SymbolName, snake_name) \
|
||||||
roots.set(m_well_known_symbols.snake_name, HeapRoot { .type = HeapRoot::Type::VM });
|
roots.set(m_well_known_symbols.snake_name, HeapRoot { .type = HeapRoot::Type::VM });
|
||||||
JS_ENUMERATE_WELL_KNOWN_SYMBOLS
|
JS_ENUMERATE_WELL_KNOWN_SYMBOLS
|
||||||
|
|
|
@ -217,6 +217,16 @@ public:
|
||||||
Object& get_global_object();
|
Object& get_global_object();
|
||||||
|
|
||||||
CommonPropertyNames names;
|
CommonPropertyNames names;
|
||||||
|
struct {
|
||||||
|
GCPtr<PrimitiveString> number;
|
||||||
|
GCPtr<PrimitiveString> undefined;
|
||||||
|
GCPtr<PrimitiveString> object;
|
||||||
|
GCPtr<PrimitiveString> string;
|
||||||
|
GCPtr<PrimitiveString> symbol;
|
||||||
|
GCPtr<PrimitiveString> boolean;
|
||||||
|
GCPtr<PrimitiveString> bigint;
|
||||||
|
GCPtr<PrimitiveString> function;
|
||||||
|
} typeof_strings;
|
||||||
|
|
||||||
void run_queued_promise_jobs();
|
void run_queued_promise_jobs();
|
||||||
void enqueue_promise_job(NonnullGCPtr<HeapFunction<ThrowCompletionOr<Value>()>> job, Realm*);
|
void enqueue_promise_job(NonnullGCPtr<HeapFunction<ThrowCompletionOr<Value>()>> job, Realm*);
|
||||||
|
|
|
@ -313,42 +313,42 @@ ThrowCompletionOr<bool> Value::is_regexp(VM& vm) const
|
||||||
}
|
}
|
||||||
|
|
||||||
// 13.5.3 The typeof Operator, https://tc39.es/ecma262/#sec-typeof-operator
|
// 13.5.3 The typeof Operator, https://tc39.es/ecma262/#sec-typeof-operator
|
||||||
StringView Value::typeof() const
|
NonnullGCPtr<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 "number"sv;
|
return *vm.typeof_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 "undefined"sv;
|
return *vm.typeof_strings.undefined;
|
||||||
// 5. If val is null, return "object".
|
// 5. If val is null, return "object".
|
||||||
case NULL_TAG:
|
case NULL_TAG:
|
||||||
return "object"sv;
|
return *vm.typeof_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 "string"sv;
|
return *vm.typeof_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 "symbol"sv;
|
return *vm.typeof_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 "boolean"sv;
|
return *vm.typeof_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 "bigint"sv;
|
return *vm.typeof_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 "undefined"sv;
|
return *vm.typeof_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 "function"sv;
|
return *vm.typeof_strings.function;
|
||||||
// 14. Return "object".
|
// 14. Return "object".
|
||||||
return "object"sv;
|
return *vm.typeof_strings.object;
|
||||||
default:
|
default:
|
||||||
VERIFY_NOT_REACHED();
|
VERIFY_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
|
|
@ -418,7 +418,7 @@ public:
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
StringView typeof() const;
|
[[nodiscard]] NonnullGCPtr<PrimitiveString> typeof(VM&) const;
|
||||||
|
|
||||||
bool operator==(Value const&) const;
|
bool operator==(Value const&) const;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue