diff options
author | Luke Wilde <lukew@serenityos.org> | 2022-06-11 23:12:22 +0100 |
---|---|---|
committer | Ali Mohammad Pur <Ali.mpfard@gmail.com> | 2022-06-13 07:13:03 +0430 |
commit | 482a8273462afd2d74ba17c71fb17d94c1f87678 (patch) | |
tree | 5bc5b54751fdf87412f2642527cf2b5a5426f0dd /Userland/Libraries/LibJS/Bytecode | |
parent | c0fadfb9b73b6cc3e415c43e0f9ab8231aa74433 (diff) | |
download | serenity-482a8273462afd2d74ba17c71fb17d94c1f87678.zip |
LibJS/Bytecode: Make typeof return "undefined" on unresolvable IDs
Previously it would throw instead of returning "undefined" for
`typeof Identifier` if Identifier does not exist.
Diffstat (limited to 'Userland/Libraries/LibJS/Bytecode')
-rw-r--r-- | Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp | 11 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Bytecode/Instruction.h | 1 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Bytecode/Op.cpp | 28 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Bytecode/Op.h | 16 |
4 files changed, 55 insertions, 1 deletions
diff --git a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp index 8e4e322b60..e183a9115e 100644 --- a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp +++ b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp @@ -436,7 +436,9 @@ Bytecode::CodeGenerationErrorOr<void> UnaryExpression::generate_bytecode(Bytecod if (m_op == UnaryOp::Delete) return generator.emit_delete_reference(m_lhs); - TRY(m_lhs->generate_bytecode(generator)); + // Typeof needs some special handling for when the LHS is an Identifier. Namely, it shouldn't throw on unresolvable references, but instead return "undefined". + if (m_op != UnaryOp::Typeof) + TRY(m_lhs->generate_bytecode(generator)); switch (m_op) { case UnaryOp::BitwiseNot: @@ -452,6 +454,13 @@ Bytecode::CodeGenerationErrorOr<void> UnaryExpression::generate_bytecode(Bytecod generator.emit<Bytecode::Op::UnaryMinus>(); break; case UnaryOp::Typeof: + if (is<Identifier>(*m_lhs)) { + auto& identifier = static_cast<Identifier const&>(*m_lhs); + generator.emit<Bytecode::Op::TypeofVariable>(generator.intern_identifier(identifier.string())); + break; + } + + TRY(m_lhs->generate_bytecode(generator)); generator.emit<Bytecode::Op::Typeof>(); break; case UnaryOp::Void: diff --git a/Userland/Libraries/LibJS/Bytecode/Instruction.h b/Userland/Libraries/LibJS/Bytecode/Instruction.h index c56e25c796..d5accd5586 100644 --- a/Userland/Libraries/LibJS/Bytecode/Instruction.h +++ b/Userland/Libraries/LibJS/Bytecode/Instruction.h @@ -82,6 +82,7 @@ O(Sub) \ O(Throw) \ O(Typeof) \ + O(TypeofVariable) \ O(UnaryMinus) \ O(UnaryPlus) \ O(UnsignedRightShift) \ diff --git a/Userland/Libraries/LibJS/Bytecode/Op.cpp b/Userland/Libraries/LibJS/Bytecode/Op.cpp index 1623a31063..7393c62995 100644 --- a/Userland/Libraries/LibJS/Bytecode/Op.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Op.cpp @@ -764,6 +764,29 @@ ThrowCompletionOr<void> NewClass::execute_impl(Bytecode::Interpreter& interprete return {}; } +// 13.5.3.1 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-typeof-operator-runtime-semantics-evaluation +ThrowCompletionOr<void> TypeofVariable::execute_impl(Bytecode::Interpreter& interpreter) const +{ + // 1. Let val be the result of evaluating UnaryExpression. + auto const& string = interpreter.current_executable().get_identifier(m_identifier); + auto reference = TRY(interpreter.vm().resolve_binding(string)); + + // 2. If val is a Reference Record, then + // a. If IsUnresolvableReference(val) is true, return "undefined". + if (reference.is_unresolvable()) { + interpreter.accumulator() = js_string(interpreter.vm(), "undefined"sv); + return {}; + } + + // 3. Set val to ? GetValue(val). + auto value = TRY(reference.get_value(interpreter.global_object())); + + // 4. NOTE: This step is replaced in section B.3.6.3. + // 5. Return a String according to Table 41. + interpreter.accumulator() = js_string(interpreter.vm(), value.typeof()); + return {}; +} + String Load::to_string_impl(Bytecode::Executable const&) const { return String::formatted("Load {}", m_src); @@ -1076,4 +1099,9 @@ String GetNewTarget::to_string_impl(Bytecode::Executable const&) const return "GetNewTarget"sv; } +String TypeofVariable::to_string_impl(Bytecode::Executable const& executable) const +{ + return String::formatted("TypeofVariable {} ({})", m_identifier, executable.identifier_table->get(m_identifier)); +} + } diff --git a/Userland/Libraries/LibJS/Bytecode/Op.h b/Userland/Libraries/LibJS/Bytecode/Op.h index 6f2f80bf80..293ee76889 100644 --- a/Userland/Libraries/LibJS/Bytecode/Op.h +++ b/Userland/Libraries/LibJS/Bytecode/Op.h @@ -910,6 +910,22 @@ public: void replace_references_impl(BasicBlock const&, BasicBlock const&) { } }; +class TypeofVariable final : public Instruction { +public: + explicit TypeofVariable(IdentifierTableIndex identifier) + : Instruction(Type::TypeofVariable) + , m_identifier(identifier) + { + } + + ThrowCompletionOr<void> execute_impl(Bytecode::Interpreter&) const; + String to_string_impl(Bytecode::Executable const&) const; + void replace_references_impl(BasicBlock const&, BasicBlock const&) { } + +private: + IdentifierTableIndex m_identifier; +}; + } namespace JS::Bytecode { |