Skip to content

Commit 766f30f

Browse files
linusgawesomekling
authored andcommitted
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.
1 parent 397f432 commit 766f30f

File tree

3 files changed

+27
-3
lines changed

3 files changed

+27
-3
lines changed

Userland/Libraries/LibJS/AST.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -810,17 +810,26 @@ Value ClassExpression::execute(Interpreter& interpreter, GlobalObject& global_ob
810810
if (interpreter.exception())
811811
return {};
812812
if (!super_constructor.is_function() && !super_constructor.is_null()) {
813-
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::ClassDoesNotExtendAConstructorOrNull, super_constructor.to_string_without_side_effects());
813+
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::ClassExtendsValueNotAConstructorOrNull, super_constructor.to_string_without_side_effects());
814814
return {};
815815
}
816816
class_constructor->set_constructor_kind(Function::ConstructorKind::Derived);
817817
Object* prototype = Object::create_empty(global_object);
818818

819819
Object* super_constructor_prototype = nullptr;
820820
if (!super_constructor.is_null()) {
821-
super_constructor_prototype = &super_constructor.as_object().get(vm.names.prototype).as_object();
821+
auto super_constructor_prototype_value = super_constructor.as_object().get(vm.names.prototype);
822822
if (interpreter.exception())
823823
return {};
824+
if (!super_constructor_prototype_value.is_object() && !super_constructor_prototype_value.is_null()) {
825+
interpreter.vm().throw_exception<TypeError>(global_object, ErrorType::ClassExtendsValueInvalidPrototype, super_constructor_prototype_value.to_string_without_side_effects());
826+
return {};
827+
}
828+
if (super_constructor_prototype_value.is_object()) {
829+
super_constructor_prototype = &super_constructor_prototype_value.as_object();
830+
if (interpreter.exception())
831+
return {};
832+
}
824833
}
825834
prototype->set_prototype(super_constructor_prototype);
826835

Userland/Libraries/LibJS/Runtime/ErrorTypes.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,9 @@
3636
M(BigIntIntArgument, "BigInt argument must be an integer") \
3737
M(BigIntInvalidValue, "Invalid value for BigInt: {}") \
3838
M(ClassConstructorWithoutNew, "Class constructor {} must be called with 'new'") \
39+
M(ClassExtendsValueNotAConstructorOrNull, "Class extends value {} is not a constructor or null") \
40+
M(ClassExtendsValueInvalidPrototype, "Class extends value has an invalid prototype {}") \
3941
M(ClassIsAbstract, "Abstract class {} cannot be constructed directly") \
40-
M(ClassDoesNotExtendAConstructorOrNull, "Class extends value {} is not a constructor or null") \
4142
M(ConstructorWithoutNew, "{} constructor must be called with 'new'") \
4243
M(Convert, "Cannot convert {} to {}") \
4344
M(ConvertUndefinedToObject, "Cannot convert undefined to object") \

Userland/Libraries/LibJS/Tests/classes/class-advanced-extends.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,17 @@ test("extending String", () => {
3333
const ms2 = new MyString2("abc");
3434
expect(ms2.charAt(1)).toBe("#b");
3535
});
36+
37+
test("class extends value is invalid", () => {
38+
expect(() => {
39+
class A extends 123 {}
40+
}).toThrowWithMessage(TypeError, "Class extends value 123 is not a constructor or null");
41+
});
42+
43+
test("class extends value has invalid prototype", () => {
44+
function f() {}
45+
f.prototype = 123;
46+
expect(() => {
47+
class A extends f {}
48+
}).toThrowWithMessage(TypeError, "Class extends value has an invalid prototype 123");
49+
});

0 commit comments

Comments
 (0)