diff options
author | Linus Groh <mail@linusgroh.de> | 2021-01-23 23:49:04 +0100 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-01-24 00:09:18 +0100 |
commit | 766f30f593c109a17bf4e57e034607cf1165a482 (patch) | |
tree | ca8c052324613538d6f9e6c22fa21c69b2998203 /Userland | |
parent | 397f432aed616aaf0b226095e630ba259c54f84d (diff) | |
download | serenity-766f30f593c109a17bf4e57e034607cf1165a482.zip |
LibJS: Check if class extends value has a valid prototype
If we have a function as class extends value, we still cannot assume
that it has a prototype property and that property has a function or
null as its value - blindly calling to_object() on it may fail.
Fixes #5075.
Diffstat (limited to 'Userland')
-rw-r--r-- | Userland/Libraries/LibJS/AST.cpp | 13 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Runtime/ErrorTypes.h | 3 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Tests/classes/class-advanced-extends.js | 14 |
3 files changed, 27 insertions, 3 deletions
diff --git a/Userland/Libraries/LibJS/AST.cpp b/Userland/Libraries/LibJS/AST.cpp index 2fb0ee1364..bd1818aab2 100644 --- a/Userland/Libraries/LibJS/AST.cpp +++ b/Userland/Libraries/LibJS/AST.cpp @@ -810,7 +810,7 @@ Value ClassExpression::execute(Interpreter& interpreter, GlobalObject& global_ob if (interpreter.exception()) return {}; if (!super_constructor.is_function() && !super_constructor.is_null()) { - interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::ClassDoesNotExtendAConstructorOrNull, super_constructor.to_string_without_side_effects()); + interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::ClassExtendsValueNotAConstructorOrNull, super_constructor.to_string_without_side_effects()); return {}; } class_constructor->set_constructor_kind(Function::ConstructorKind::Derived); @@ -818,9 +818,18 @@ Value ClassExpression::execute(Interpreter& interpreter, GlobalObject& global_ob Object* super_constructor_prototype = nullptr; if (!super_constructor.is_null()) { - super_constructor_prototype = &super_constructor.as_object().get(vm.names.prototype).as_object(); + auto super_constructor_prototype_value = super_constructor.as_object().get(vm.names.prototype); if (interpreter.exception()) return {}; + if (!super_constructor_prototype_value.is_object() && !super_constructor_prototype_value.is_null()) { + interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::ClassExtendsValueInvalidPrototype, super_constructor_prototype_value.to_string_without_side_effects()); + return {}; + } + if (super_constructor_prototype_value.is_object()) { + super_constructor_prototype = &super_constructor_prototype_value.as_object(); + if (interpreter.exception()) + return {}; + } } prototype->set_prototype(super_constructor_prototype); diff --git a/Userland/Libraries/LibJS/Runtime/ErrorTypes.h b/Userland/Libraries/LibJS/Runtime/ErrorTypes.h index 65b23c6a63..30ff25ec26 100644 --- a/Userland/Libraries/LibJS/Runtime/ErrorTypes.h +++ b/Userland/Libraries/LibJS/Runtime/ErrorTypes.h @@ -36,8 +36,9 @@ M(BigIntIntArgument, "BigInt argument must be an integer") \ M(BigIntInvalidValue, "Invalid value for BigInt: {}") \ M(ClassConstructorWithoutNew, "Class constructor {} must be called with 'new'") \ + M(ClassExtendsValueNotAConstructorOrNull, "Class extends value {} is not a constructor or null") \ + M(ClassExtendsValueInvalidPrototype, "Class extends value has an invalid prototype {}") \ M(ClassIsAbstract, "Abstract class {} cannot be constructed directly") \ - M(ClassDoesNotExtendAConstructorOrNull, "Class extends value {} is not a constructor or null") \ M(ConstructorWithoutNew, "{} constructor must be called with 'new'") \ M(Convert, "Cannot convert {} to {}") \ M(ConvertUndefinedToObject, "Cannot convert undefined to object") \ diff --git a/Userland/Libraries/LibJS/Tests/classes/class-advanced-extends.js b/Userland/Libraries/LibJS/Tests/classes/class-advanced-extends.js index 5cbeb90404..ece57fe404 100644 --- a/Userland/Libraries/LibJS/Tests/classes/class-advanced-extends.js +++ b/Userland/Libraries/LibJS/Tests/classes/class-advanced-extends.js @@ -33,3 +33,17 @@ test("extending String", () => { const ms2 = new MyString2("abc"); expect(ms2.charAt(1)).toBe("#b"); }); + +test("class extends value is invalid", () => { + expect(() => { + class A extends 123 {} + }).toThrowWithMessage(TypeError, "Class extends value 123 is not a constructor or null"); +}); + +test("class extends value has invalid prototype", () => { + function f() {} + f.prototype = 123; + expect(() => { + class A extends f {} + }).toThrowWithMessage(TypeError, "Class extends value has an invalid prototype 123"); +}); |