mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 04:47:35 +00:00
LibJS: Use empty value for Reference unresolvable state, not undefined
This fixes an issue where `undefined.foo = "bar"` would throw a ReferenceError instead of a TypeError as undefined was also used for truly unresolvable references (e.g. `foo() = "bar"`). I also made the various error messages here a bit nicer, just "primitive value" is not very helpful.
This commit is contained in:
parent
d6cffb82a2
commit
e875513ff7
4 changed files with 39 additions and 15 deletions
|
@ -146,7 +146,8 @@
|
|||
"target is non-extensible") \
|
||||
M(ProxyTwoArguments, "Proxy constructor requires at least two arguments") \
|
||||
M(ReduceNoInitial, "Reduce of empty array with no initial value") \
|
||||
M(ReferencePrimitiveAssignment, "Cannot assign property {} to primitive value") \
|
||||
M(ReferenceNullishAssignment, "Cannot set property '{}' of {}") \
|
||||
M(ReferencePrimitiveAssignment, "Cannot set property '{}' of {} '{}'") \
|
||||
M(ReferenceUnresolvable, "Unresolvable reference") \
|
||||
M(ReflectArgumentMustBeAFunction, "First argument of Reflect.{}() must be a function") \
|
||||
M(ReflectArgumentMustBeAnObject, "First argument of Reflect.{}() must be an object") \
|
||||
|
|
|
@ -47,12 +47,23 @@ void Reference::put(GlobalObject& global_object, Value value)
|
|||
return;
|
||||
}
|
||||
|
||||
if (!base().is_object() && vm.in_strict_mode()) {
|
||||
vm.throw_exception<TypeError>(global_object, ErrorType::ReferencePrimitiveAssignment, m_name.to_value(global_object.vm()).to_string_without_side_effects());
|
||||
auto base = this->base();
|
||||
|
||||
if (!base.is_object() && vm.in_strict_mode()) {
|
||||
if (base.is_nullish())
|
||||
vm.throw_exception<TypeError>(global_object, ErrorType::ReferenceNullishAssignment, m_name.to_value(global_object.vm()).to_string_without_side_effects(), base.to_string_without_side_effects());
|
||||
else
|
||||
vm.throw_exception<TypeError>(global_object, ErrorType::ReferencePrimitiveAssignment, m_name.to_value(global_object.vm()).to_string_without_side_effects(), base.typeof(), base.to_string_without_side_effects());
|
||||
return;
|
||||
}
|
||||
|
||||
auto* object = base().to_object(global_object);
|
||||
if (base.is_nullish()) {
|
||||
// This will always fail the to_object() call below, let's throw the TypeError ourselves with a nice message instead.
|
||||
vm.throw_exception<TypeError>(global_object, ErrorType::ReferenceNullishAssignment, m_name.to_value(global_object.vm()).to_string_without_side_effects(), base.to_string_without_side_effects());
|
||||
return;
|
||||
}
|
||||
|
||||
auto* object = base.to_object(global_object);
|
||||
if (!object)
|
||||
return;
|
||||
|
||||
|
@ -61,13 +72,11 @@ void Reference::put(GlobalObject& global_object, Value value)
|
|||
|
||||
void Reference::throw_reference_error(GlobalObject& global_object)
|
||||
{
|
||||
auto property_name = m_name.to_string();
|
||||
String message;
|
||||
if (property_name.is_empty()) {
|
||||
global_object.vm().throw_exception<ReferenceError>(global_object, ErrorType::ReferenceUnresolvable);
|
||||
} else {
|
||||
global_object.vm().throw_exception<ReferenceError>(global_object, ErrorType::UnknownIdentifier, property_name);
|
||||
}
|
||||
auto& vm = global_object.vm();
|
||||
if (!m_name.is_valid())
|
||||
vm.throw_exception<ReferenceError>(global_object, ErrorType::ReferenceUnresolvable);
|
||||
else
|
||||
vm.throw_exception<ReferenceError>(global_object, ErrorType::UnknownIdentifier, m_name.to_string_or_symbol().to_display_string());
|
||||
}
|
||||
|
||||
Value Reference::get(GlobalObject& global_object)
|
||||
|
|
|
@ -64,7 +64,7 @@ public:
|
|||
const PropertyName& name() const { return m_name; }
|
||||
bool is_strict() const { return m_strict; }
|
||||
|
||||
bool is_unresolvable() const { return m_base.is_undefined(); }
|
||||
bool is_unresolvable() const { return m_base.is_empty(); }
|
||||
bool is_property() const
|
||||
{
|
||||
return m_base.is_object() || has_primitive_base();
|
||||
|
@ -91,7 +91,7 @@ public:
|
|||
private:
|
||||
void throw_reference_error(GlobalObject&);
|
||||
|
||||
Value m_base { js_undefined() };
|
||||
Value m_base;
|
||||
PropertyName m_name;
|
||||
bool m_strict { false };
|
||||
bool m_local_variable { false };
|
||||
|
|
|
@ -4,12 +4,26 @@ test("basic functionality", () => {
|
|||
[true, false, "foo", 123].forEach(primitive => {
|
||||
expect(() => {
|
||||
primitive.foo = "bar";
|
||||
}).toThrowWithMessage(TypeError, "Cannot assign property foo to primitive value");
|
||||
}).toThrowWithMessage(
|
||||
TypeError,
|
||||
`Cannot set property 'foo' of ${typeof primitive} '${primitive}'`
|
||||
);
|
||||
expect(() => {
|
||||
primitive[Symbol.hasInstance] = 123;
|
||||
}).toThrowWithMessage(
|
||||
TypeError,
|
||||
"Cannot assign property Symbol(Symbol.hasInstance) to primitive value"
|
||||
`Cannot set property 'Symbol(Symbol.hasInstance)' of ${typeof primitive} '${primitive}'`
|
||||
);
|
||||
});
|
||||
[null, undefined].forEach(primitive => {
|
||||
expect(() => {
|
||||
primitive.foo = "bar";
|
||||
}).toThrowWithMessage(TypeError, `Cannot set property 'foo' of ${primitive}`);
|
||||
expect(() => {
|
||||
primitive[Symbol.hasInstance] = 123;
|
||||
}).toThrowWithMessage(
|
||||
TypeError,
|
||||
`Cannot set property 'Symbol(Symbol.hasInstance)' of ${primitive}`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue