diff options
author | Matthew Olsson <matthewcolsson@gmail.com> | 2020-07-11 15:27:28 -0700 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-07-14 20:15:19 +0200 |
commit | dd49ec17a2a3600134722de4f24c995174959f30 (patch) | |
tree | 91de0a77e923f80f77c6f6c31575cfc6b649bd52 /Libraries/LibJS/Runtime | |
parent | 7bf4b1c9f065b50c07b4d1d59b483032f5802617 (diff) | |
download | serenity-dd49ec17a2a3600134722de4f24c995174959f30.zip |
LibJS: Implement spec-complient instance_of operation
Diffstat (limited to 'Libraries/LibJS/Runtime')
-rw-r--r-- | Libraries/LibJS/Runtime/ErrorTypes.h | 2 | ||||
-rw-r--r-- | Libraries/LibJS/Runtime/Value.cpp | 53 | ||||
-rw-r--r-- | Libraries/LibJS/Runtime/Value.h | 1 |
3 files changed, 50 insertions, 6 deletions
diff --git a/Libraries/LibJS/Runtime/ErrorTypes.h b/Libraries/LibJS/Runtime/ErrorTypes.h index 48a63e2b91..7be359176b 100644 --- a/Libraries/LibJS/Runtime/ErrorTypes.h +++ b/Libraries/LibJS/Runtime/ErrorTypes.h @@ -42,7 +42,7 @@ M(DescChangeNonConfigurable, "Cannot change attributes of non-configurable property '%s'") \ M(FunctionArgsNotObject, "Argument array must be an object") \ M(InOperatorWithObject, "'in' operator must be used on an object") \ - M(InstanceOfOperatorBadPrototype, "Prototype property of %s is not an object") \ + M(InstanceOfOperatorBadPrototype, "'prototype' property of %s is not an object") \ M(InvalidAssignToConst, "Invalid assignment to const variable") \ M(InvalidLeftHandAssignment, "Invalid left-hand side in assignment") \ M(IsNotA, "%s is not a %s") \ diff --git a/Libraries/LibJS/Runtime/Value.cpp b/Libraries/LibJS/Runtime/Value.cpp index c1b67bcb6d..a9fc4fb488 100644 --- a/Libraries/LibJS/Runtime/Value.cpp +++ b/Libraries/LibJS/Runtime/Value.cpp @@ -38,6 +38,7 @@ #include <LibJS/Runtime/BigInt.h> #include <LibJS/Runtime/BigIntObject.h> #include <LibJS/Runtime/BooleanObject.h> +#include <LibJS/Runtime/BoundFunction.h> #include <LibJS/Runtime/Error.h> #include <LibJS/Runtime/Function.h> #include <LibJS/Runtime/NumberObject.h> @@ -685,14 +686,56 @@ Value in(Interpreter& interpreter, Value lhs, Value rhs) Value instance_of(Interpreter& interpreter, Value lhs, Value rhs) { - if (!lhs.is_object() || !rhs.is_object()) + if (!rhs.is_object()) + return interpreter.throw_exception<TypeError>(ErrorType::NotAnObject, rhs.to_string_without_side_effects().characters()); + + auto has_instance_method = rhs.as_object().get(interpreter.well_known_symbol_has_instance()); + if (!has_instance_method.is_empty()) { + if (!has_instance_method.is_function()) + return interpreter.throw_exception<TypeError>(ErrorType::NotAFunction, has_instance_method.to_string_without_side_effects().characters()); + + MarkedValueList arguments(interpreter.heap()); + arguments.append(lhs); + return Value(interpreter.call(has_instance_method.as_function(), rhs, move(arguments)).to_boolean()); + } + + if (!rhs.is_function()) + return interpreter.throw_exception<TypeError>(ErrorType::NotAFunction, rhs.to_string_without_side_effects().characters()); + + return ordinary_has_instance(interpreter, lhs, rhs); +} + +Value ordinary_has_instance(Interpreter& interpreter, Value lhs, Value rhs) +{ + if (!rhs.is_function()) + return Value(false); + auto& rhs_function = rhs.as_function(); + + if (rhs_function.is_bound_function()) { + auto& bound_target = static_cast<BoundFunction&>(rhs_function); + return instance_of(interpreter, lhs, Value(&bound_target.target_function())); + } + + if (!lhs.is_object()) return Value(false); - auto constructor_prototype_property = rhs.as_object().get("prototype"); + + Object* lhs_object = &lhs.as_object(); + auto rhs_prototype = rhs_function.get("prototype"); if (interpreter.exception()) return {}; - if (!constructor_prototype_property.is_object()) - return Value(false); - return Value(lhs.as_object().has_prototype(&constructor_prototype_property.as_object())); + + if (!rhs_prototype.is_object()) + return interpreter.throw_exception<TypeError>(ErrorType::InstanceOfOperatorBadPrototype, rhs_prototype.to_string_without_side_effects().characters()); + + while (true) { + lhs_object = lhs_object->prototype(); + if (interpreter.exception()) + return {}; + if (!lhs_object) + return Value(false); + if (same_value(interpreter, rhs_prototype, lhs_object)) + return Value(true); + } } const LogStream& operator<<(const LogStream& stream, const Value& value) diff --git a/Libraries/LibJS/Runtime/Value.h b/Libraries/LibJS/Runtime/Value.h index 0092232b72..b3a800ecd2 100644 --- a/Libraries/LibJS/Runtime/Value.h +++ b/Libraries/LibJS/Runtime/Value.h @@ -323,6 +323,7 @@ Value mod(Interpreter&, Value lhs, Value rhs); Value exp(Interpreter&, Value lhs, Value rhs); Value in(Interpreter&, Value lhs, Value rhs); Value instance_of(Interpreter&, Value lhs, Value rhs); +Value ordinary_has_instance(Interpreter& interpreter, Value lhs, Value rhs); bool abstract_eq(Interpreter&, Value lhs, Value rhs); bool strict_eq(Interpreter&, Value lhs, Value rhs); |