diff --git a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp index 5c811f4d7259..c2e7d38b9cef 100644 --- a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp @@ -1193,7 +1193,7 @@ inline ThrowCompletionOr put_by_property_key(VM& vm, Value base, Value thi if (!succeeded && vm.in_strict_mode()) { if (base.is_object()) return vm.throw_completion(ErrorType::ReferenceNullishSetProperty, name, base.to_string_without_side_effects()); - return vm.throw_completion(ErrorType::ReferencePrimitiveSetProperty, name, base.typeof(), base.to_string_without_side_effects()); + return vm.throw_completion(ErrorType::ReferencePrimitiveSetProperty, name, base.typeof(vm)->utf8_string(), base.to_string_without_side_effects()); } break; } @@ -2018,7 +2018,7 @@ static ThrowCompletionOr not_(VM&, Value value) static ThrowCompletionOr 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) \ @@ -2873,7 +2873,7 @@ ThrowCompletionOr TypeofBinding::execute_impl(Bytecode::Interpreter& inter environment = environment->outer_environment(); if (!environment->is_permanently_screwed_by_eval()) { auto value = TRY(static_cast(*environment).get_binding_value_direct(vm, m_cache.index)); - interpreter.set(dst(), PrimitiveString::create(vm, value.typeof())); + interpreter.set(dst(), value.typeof(vm)); return {}; } m_cache = {}; @@ -2897,7 +2897,7 @@ ThrowCompletionOr TypeofBinding::execute_impl(Bytecode::Interpreter& inter // 4. NOTE: This step is replaced in section B.3.6.3. // 5. Return a String according to Table 41. - interpreter.set(dst(), PrimitiveString::create(vm, value.typeof())); + interpreter.set(dst(), value.typeof(vm)); return {}; } diff --git a/Userland/Libraries/LibJS/Runtime/VM.cpp b/Userland/Libraries/LibJS/Runtime/VM.cpp index 8d66a0f47c56..57f3a6734d39 100644 --- a/Userland/Libraries/LibJS/Runtime/VM.cpp +++ b/Userland/Libraries/LibJS/Runtime/VM.cpp @@ -70,6 +70,17 @@ VM::VM(OwnPtr custom_data, ErrorMessages error_messages) m_empty_string = m_heap.allocate_without_realm(String {}); + typeof_strings = { + .number = m_heap.allocate_without_realm("number"), + .undefined = m_heap.allocate_without_realm("undefined"), + .object = m_heap.allocate_without_realm("object"), + .string = m_heap.allocate_without_realm("string"), + .symbol = m_heap.allocate_without_realm("symbol"), + .boolean = m_heap.allocate_without_realm("boolean"), + .bigint = m_heap.allocate_without_realm("bigint"), + .function = m_heap.allocate_without_realm("function"), + }; + for (size_t i = 0; i < single_ascii_character_strings.size(); ++i) m_single_ascii_character_strings[i] = m_heap.allocate_without_realm(single_ascii_character_strings[i]); @@ -192,6 +203,15 @@ void VM::gather_roots(HashMap& roots) for (auto string : m_single_ascii_character_strings) 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) \ roots.set(m_well_known_symbols.snake_name, HeapRoot { .type = HeapRoot::Type::VM }); JS_ENUMERATE_WELL_KNOWN_SYMBOLS diff --git a/Userland/Libraries/LibJS/Runtime/VM.h b/Userland/Libraries/LibJS/Runtime/VM.h index 7c776ad3083f..d636cccca4ef 100644 --- a/Userland/Libraries/LibJS/Runtime/VM.h +++ b/Userland/Libraries/LibJS/Runtime/VM.h @@ -217,6 +217,16 @@ class VM : public RefCounted { Object& get_global_object(); CommonPropertyNames names; + struct { + GCPtr number; + GCPtr undefined; + GCPtr object; + GCPtr string; + GCPtr symbol; + GCPtr boolean; + GCPtr bigint; + GCPtr function; + } typeof_strings; void run_queued_promise_jobs(); void enqueue_promise_job(NonnullGCPtr()>> job, Realm*); diff --git a/Userland/Libraries/LibJS/Runtime/Value.cpp b/Userland/Libraries/LibJS/Runtime/Value.cpp index 2ec8e3b65d16..8d9abc729ef5 100644 --- a/Userland/Libraries/LibJS/Runtime/Value.cpp +++ b/Userland/Libraries/LibJS/Runtime/Value.cpp @@ -313,42 +313,42 @@ ThrowCompletionOr Value::is_regexp(VM& vm) const } // 13.5.3 The typeof Operator, https://tc39.es/ecma262/#sec-typeof-operator -StringView Value::typeof() const +NonnullGCPtr Value::typeof(VM& vm) const { // 9. If val is a Number, return "number". if (is_number()) - return "number"sv; + return *vm.typeof_strings.number; switch (m_value.tag) { // 4. If val is undefined, return "undefined". case UNDEFINED_TAG: - return "undefined"sv; + return *vm.typeof_strings.undefined; // 5. If val is null, return "object". case NULL_TAG: - return "object"sv; + return *vm.typeof_strings.object; // 6. If val is a String, return "string". case STRING_TAG: - return "string"sv; + return *vm.typeof_strings.string; // 7. If val is a Symbol, return "symbol". case SYMBOL_TAG: - return "symbol"sv; + return *vm.typeof_strings.symbol; // 8. If val is a Boolean, return "boolean". case BOOLEAN_TAG: - return "boolean"sv; + return *vm.typeof_strings.boolean; // 10. If val is a BigInt, return "bigint". case BIGINT_TAG: - return "bigint"sv; + return *vm.typeof_strings.bigint; // 11. Assert: val is an Object. case OBJECT_TAG: // 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". if (as_object().is_htmldda()) - return "undefined"sv; + return *vm.typeof_strings.undefined; // 13. If val has a [[Call]] internal slot, return "function". if (is_function()) - return "function"sv; + return *vm.typeof_strings.function; // 14. Return "object". - return "object"sv; + return *vm.typeof_strings.object; default: VERIFY_NOT_REACHED(); } diff --git a/Userland/Libraries/LibJS/Runtime/Value.h b/Userland/Libraries/LibJS/Runtime/Value.h index 4af26eee27e5..6609a4e0bb07 100644 --- a/Userland/Libraries/LibJS/Runtime/Value.h +++ b/Userland/Libraries/LibJS/Runtime/Value.h @@ -418,7 +418,7 @@ class Value { return *this; } - StringView typeof() const; + [[nodiscard]] NonnullGCPtr typeof(VM&) const; bool operator==(Value const&) const;