diff --git a/Libraries/LibJS/AST.cpp b/Libraries/LibJS/AST.cpp index 833536e460..7cc263b93c 100644 --- a/Libraries/LibJS/AST.cpp +++ b/Libraries/LibJS/AST.cpp @@ -585,9 +585,25 @@ Value UnaryExpression::execute(Interpreter& interpreter) const return base_object->delete_property(reference.name()); } - auto lhs_result = m_lhs->execute(interpreter); - if (interpreter.exception()) - return {}; + Value lhs_result; + if (m_op == UnaryOp::Typeof && m_lhs->is_identifier()) { + auto reference = m_lhs->to_reference(interpreter); + if (interpreter.exception()) { + return {}; + } + // FIXME: standard recommends checking with is_unresolvable but it ALWAYS return false here + if (reference.is_local_variable() || reference.is_global_variable()) { + auto name = reference.name(); + lhs_result = interpreter.get_variable(name.to_string()).value_or(js_undefined()); + if (interpreter.exception()) + return {}; + } + } else { + lhs_result = m_lhs->execute(interpreter); + if (interpreter.exception()) + return {}; + } + switch (m_op) { case UnaryOp::BitwiseNot: return bitwise_not(interpreter, lhs_result); diff --git a/Libraries/LibJS/Tests/typeof-basic.js b/Libraries/LibJS/Tests/typeof-basic.js index a1ba3dd5f7..e07c68ae13 100644 --- a/Libraries/LibJS/Tests/typeof-basic.js +++ b/Libraries/LibJS/Tests/typeof-basic.js @@ -8,6 +8,19 @@ try { assert(typeof null === "object"); assert(typeof undefined === "undefined"); + var iExist = 1; + assert(typeof iExist === "number"); + assert(typeof iDontExist === "undefined"); + + var calls = 0; + Object.defineProperty(globalThis, "foo", { + get() { + calls++; + }, + }); + assert(typeof foo === "undefined"); + assert(calls === 1); + console.log("PASS"); } catch (e) { console.log("FAIL: " + e);