diff --git a/Libraries/LibJS/AST.cpp b/Libraries/LibJS/AST.cpp index b2c1802a38..fb3ca318e4 100644 --- a/Libraries/LibJS/AST.cpp +++ b/Libraries/LibJS/AST.cpp @@ -478,10 +478,10 @@ void ForStatement::dump(int indent) const Value Identifier::execute(Interpreter& interpreter) const { - auto value = interpreter.get_variable(string()); - if (value.is_undefined()) + auto variable = interpreter.get_variable(string()); + if (!variable.has_value()) return interpreter.throw_exception("ReferenceError", String::format("'%s' not known", string().characters())); - return value; + return variable.value(); } void Identifier::dump(int indent) const @@ -536,7 +536,9 @@ Value UpdateExpression::execute(Interpreter& interpreter) const ASSERT(m_argument->is_identifier()); auto name = static_cast(*m_argument).string(); - auto previous_value = interpreter.get_variable(name); + auto previous_variable = interpreter.get_variable(name); + ASSERT(previous_variable.has_value()); + auto previous_value = previous_variable.value(); ASSERT(previous_value.is_number()); int op_result = 0; @@ -687,7 +689,8 @@ Value MemberExpression::execute(Interpreter& interpreter) const { auto object_result = m_object->execute(interpreter).to_object(interpreter.heap()); ASSERT(object_result.is_object()); - return object_result.as_object()->get(computed_property_name(interpreter)); + auto result = object_result.as_object()->get(computed_property_name(interpreter)); + return result.value_or({}); } Value StringLiteral::execute(Interpreter& interpreter) const diff --git a/Libraries/LibJS/Interpreter.cpp b/Libraries/LibJS/Interpreter.cpp index 5c3b486fab..ee5802f833 100644 --- a/Libraries/LibJS/Interpreter.cpp +++ b/Libraries/LibJS/Interpreter.cpp @@ -142,7 +142,7 @@ void Interpreter::set_variable(const FlyString& name, Value value, bool first_as global_object().put(move(name), move(value)); } -Value Interpreter::get_variable(const FlyString& name) +Optional Interpreter::get_variable(const FlyString& name) { for (ssize_t i = m_scope_stack.size() - 1; i >= 0; --i) { auto& scope = m_scope_stack.at(i); diff --git a/Libraries/LibJS/Interpreter.h b/Libraries/LibJS/Interpreter.h index fe806f9a4f..f65ae60386 100644 --- a/Libraries/LibJS/Interpreter.h +++ b/Libraries/LibJS/Interpreter.h @@ -79,7 +79,7 @@ public: void unwind(ScopeType type) { m_unwind_until = type; } - Value get_variable(const FlyString& name); + Optional get_variable(const FlyString& name); void set_variable(const FlyString& name, Value, bool first_assignment = false); void declare_variable(const FlyString& name, DeclarationType); diff --git a/Libraries/LibJS/Runtime/Object.cpp b/Libraries/LibJS/Runtime/Object.cpp index 75c11b767b..3be6803712 100644 --- a/Libraries/LibJS/Runtime/Object.cpp +++ b/Libraries/LibJS/Runtime/Object.cpp @@ -63,7 +63,7 @@ bool Object::put_own_property(Object& this_object, const FlyString& property_nam return true; } -Value Object::get(const FlyString& property_name) const +Optional Object::get(const FlyString& property_name) const { const Object* object = this; while (object) { @@ -72,7 +72,7 @@ Value Object::get(const FlyString& property_name) const return value.value(); object = object->prototype(); } - return js_undefined(); + return {}; } void Object::put(const FlyString& property_name, Value value) diff --git a/Libraries/LibJS/Runtime/Object.h b/Libraries/LibJS/Runtime/Object.h index 149255139e..041ea089ba 100644 --- a/Libraries/LibJS/Runtime/Object.h +++ b/Libraries/LibJS/Runtime/Object.h @@ -40,7 +40,7 @@ public: Object(); virtual ~Object(); - Value get(const FlyString& property_name) const; + Optional get(const FlyString& property_name) const; void put(const FlyString& property_name, Value); virtual Optional get_own_property(const Object& this_object, const FlyString& property_name) const; diff --git a/Libraries/LibJS/Tests/function-missing-arg.js b/Libraries/LibJS/Tests/function-missing-arg.js new file mode 100644 index 0000000000..8bef98e4f6 --- /dev/null +++ b/Libraries/LibJS/Tests/function-missing-arg.js @@ -0,0 +1,13 @@ +function assert(x) { if (!x) throw 1; } + +function foo(a, b) { return a + b; } + +try { + assert(isNaN(foo()) === true); + assert(isNaN(foo(1)) === true); + assert(foo(2, 3) === 5); + assert(foo(2, 3, 4) === 5); + console.log("PASS"); +} catch (e) { + console.log("FAIL: " + e); +} diff --git a/Libraries/LibJS/Tests/variable-undefined.js b/Libraries/LibJS/Tests/variable-undefined.js new file mode 100644 index 0000000000..61ee23a0d5 --- /dev/null +++ b/Libraries/LibJS/Tests/variable-undefined.js @@ -0,0 +1,19 @@ +function assert(x) { if (!x) throw 1; } + +function foo(a) { + return a; +} + +try { + var x = undefined; + assert(x === undefined); + assert(foo(x) === undefined); + + var o = {}; + o.x = x; + assert(o.x === undefined); + assert(o.x === x); + console.log("PASS"); +} catch (e) { + console.log("FAIL: " + e); +}