LibJS: Throw real TypeError, ReferenceError, etc objects

Instead of just throwing Error objects with a name string, we now throw
the real Error subclass types. :^)
This commit is contained in:
Andreas Kling 2020-04-10 12:48:31 +02:00
parent 58ab76269c
commit e5da1cc566
Notes: sideshowbarker 2024-07-19 07:44:57 +09:00
13 changed files with 32 additions and 29 deletions

View file

@ -97,11 +97,11 @@ Value CallExpression::execute(Interpreter& interpreter) const
|| !callee.as_object().is_function() || !callee.as_object().is_function()
|| (callee.as_object().is_native_function() || (callee.as_object().is_native_function()
&& !static_cast<NativeFunction&>(callee.as_object()).has_constructor())) && !static_cast<NativeFunction&>(callee.as_object()).has_constructor()))
return interpreter.throw_exception<Error>("TypeError", String::format("%s is not a constructor", callee.to_string().characters())); return interpreter.throw_exception<TypeError>(String::format("%s is not a constructor", callee.to_string().characters()));
} }
if (!callee.is_object() || !callee.as_object().is_function()) if (!callee.is_object() || !callee.as_object().is_function())
return interpreter.throw_exception<Error>("TypeError", String::format("%s is not a function", callee.to_string().characters())); return interpreter.throw_exception<TypeError>(String::format("%s is not a function", callee.to_string().characters()));
auto& function = static_cast<Function&>(callee.as_object()); auto& function = static_cast<Function&>(callee.as_object());
@ -651,7 +651,7 @@ Value Identifier::execute(Interpreter& interpreter) const
{ {
auto variable = interpreter.get_variable(string()); auto variable = interpreter.get_variable(string());
if (!variable.has_value()) if (!variable.has_value())
return interpreter.throw_exception<Error>("ReferenceError", String::format("'%s' not known", string().characters())); return interpreter.throw_exception<ReferenceError>(String::format("'%s' not known", string().characters()));
return variable.value(); return variable.value();
} }
@ -711,7 +711,7 @@ Value AssignmentExpression::execute(Interpreter& interpreter) const
object->put(property_name, rhs_result); object->put(property_name, rhs_result);
} }
} else { } else {
return interpreter.throw_exception<Error>("ReferenceError", "Invalid left-hand side in assignment"); return interpreter.throw_exception<ReferenceError>("Invalid left-hand side in assignment");
} }
return rhs_result; return rhs_result;

View file

@ -41,7 +41,7 @@
#endif #endif
#ifdef __serenity__ #ifdef __serenity__
#define HEAP_DEBUG //#define HEAP_DEBUG
#endif #endif
namespace JS { namespace JS {

View file

@ -66,7 +66,7 @@ Value Array::length_getter(Interpreter& interpreter)
if (!this_object) if (!this_object)
return {}; return {};
if (!this_object->is_array()) if (!this_object->is_array())
return interpreter.throw_exception<Error>("TypeError", "Not an array"); return interpreter.throw_exception<TypeError>("Not an array");
return Value(static_cast<const Array*>(this_object)->length()); return Value(static_cast<const Array*>(this_object)->length());
} }

View file

@ -54,7 +54,7 @@ static Array* array_from(Interpreter& interpreter)
if (!this_object) if (!this_object)
return {}; return {};
if (!this_object->is_array()) { if (!this_object->is_array()) {
interpreter.throw_exception<Error>("TypeError", "Not an Array"); interpreter.throw_exception<TypeError>("Not an Array");
return nullptr; return nullptr;
} }
return static_cast<Array*>(this_object); return static_cast<Array*>(this_object);

View file

@ -47,7 +47,7 @@ Value BooleanPrototype::to_string(Interpreter& interpreter)
return js_string(interpreter.heap(), this_object.as_bool() ? "true" : "false"); return js_string(interpreter.heap(), this_object.as_bool() ? "true" : "false");
} }
if (!this_object.is_object() || !this_object.as_object().is_boolean()) { if (!this_object.is_object() || !this_object.as_object().is_boolean()) {
interpreter.throw_exception<Error>("TypeError", "Not a Boolean"); interpreter.throw_exception<TypeError>("Not a Boolean");
return {}; return {};
} }
@ -62,7 +62,7 @@ Value BooleanPrototype::value_of(Interpreter& interpreter)
return this_object; return this_object;
} }
if (!this_object.is_object() || !this_object.as_object().is_boolean()) { if (!this_object.is_object() || !this_object.as_object().is_boolean()) {
interpreter.throw_exception<Error>("TypeError", "Not a Boolean"); interpreter.throw_exception<TypeError>("Not a Boolean");
return {}; return {};
} }

View file

@ -41,7 +41,7 @@ static Date* this_date_from_interpreter(Interpreter& interpreter)
if (!this_object) if (!this_object)
return nullptr; return nullptr;
if (!this_object->is_date()) { if (!this_object->is_date()) {
interpreter.throw_exception<Error>("TypeError", "object must be of type Date"); interpreter.throw_exception<TypeError>("object must be of type Date");
return nullptr; return nullptr;
} }
return static_cast<Date*>(this_object); return static_cast<Date*>(this_object);

View file

@ -51,7 +51,7 @@ Value ErrorPrototype::name_getter(Interpreter& interpreter)
if (!this_object) if (!this_object)
return {}; return {};
if (!this_object->is_error()) if (!this_object->is_error())
return interpreter.throw_exception<Error>("TypeError", "Not an Error object"); return interpreter.throw_exception<TypeError>("Not an Error object");
return js_string(interpreter, static_cast<const Error*>(this_object)->name()); return js_string(interpreter, static_cast<const Error*>(this_object)->name());
} }
@ -61,14 +61,14 @@ Value ErrorPrototype::message_getter(Interpreter& interpreter)
if (!this_object) if (!this_object)
return {}; return {};
if (!this_object->is_error()) if (!this_object->is_error())
return interpreter.throw_exception<Error>("TypeError", "Not an Error object"); return interpreter.throw_exception<TypeError>("Not an Error object");
return js_string(interpreter, static_cast<const Error*>(this_object)->message()); return js_string(interpreter, static_cast<const Error*>(this_object)->message());
} }
Value ErrorPrototype::to_string(Interpreter& interpreter) Value ErrorPrototype::to_string(Interpreter& interpreter)
{ {
if (!interpreter.this_value().is_object()) if (!interpreter.this_value().is_object())
return interpreter.throw_exception<Error>("TypeError", "Not an object"); return interpreter.throw_exception<TypeError>("Not an object");
auto& this_object = interpreter.this_value().as_object(); auto& this_object = interpreter.this_value().as_object();
String name = "Error"; String name = "Error";
@ -89,7 +89,10 @@ Value ErrorPrototype::to_string(Interpreter& interpreter)
} }
#define DEFINE_ERROR_SUBCLASS_PROTOTYPE(TitleCase, snake_case) \ #define DEFINE_ERROR_SUBCLASS_PROTOTYPE(TitleCase, snake_case) \
TitleCase::TitleCase() {} \ TitleCase::TitleCase() \
{ \
set_prototype(interpreter().error_prototype()); \
} \
TitleCase::~TitleCase() {} \ TitleCase::~TitleCase() {} \
const char* TitleCase::class_name() const { return #TitleCase; } const char* TitleCase::class_name() const { return #TitleCase; }

View file

@ -54,14 +54,14 @@ Value FunctionPrototype::apply(Interpreter& interpreter)
if (!this_object) if (!this_object)
return {}; return {};
if (!this_object->is_function()) if (!this_object->is_function())
return interpreter.throw_exception<Error>("TypeError", "Not a Function object"); return interpreter.throw_exception<TypeError>("Not a Function object");
auto function = static_cast<Function*>(this_object); auto function = static_cast<Function*>(this_object);
auto this_arg = interpreter.argument(0); auto this_arg = interpreter.argument(0);
auto arg_array = interpreter.argument(1); auto arg_array = interpreter.argument(1);
if (arg_array.is_null() || arg_array.is_undefined()) if (arg_array.is_null() || arg_array.is_undefined())
return interpreter.call(function, this_arg); return interpreter.call(function, this_arg);
if (!arg_array.is_object()) if (!arg_array.is_object())
return interpreter.throw_exception<Error>("TypeError", "argument array must be an object"); return interpreter.throw_exception<TypeError>("argument array must be an object");
size_t length = 0; size_t length = 0;
auto length_property = arg_array.as_object().get("length"); auto length_property = arg_array.as_object().get("length");
if (length_property.has_value()) if (length_property.has_value())
@ -87,7 +87,7 @@ Value FunctionPrototype::call(Interpreter& interpreter)
if (!this_object) if (!this_object)
return {}; return {};
if (!this_object->is_function()) if (!this_object->is_function())
return interpreter.throw_exception<Error>("TypeError", "Not a Function object"); return interpreter.throw_exception<TypeError>("Not a Function object");
auto function = static_cast<Function*>(this_object); auto function = static_cast<Function*>(this_object);
auto this_arg = interpreter.argument(0); auto this_arg = interpreter.argument(0);
Vector<Value> arguments; Vector<Value> arguments;
@ -104,7 +104,7 @@ Value FunctionPrototype::to_string(Interpreter& interpreter)
if (!this_object) if (!this_object)
return {}; return {};
if (!this_object->is_function()) if (!this_object->is_function())
return interpreter.throw_exception<Error>("TypeError", "Not a Function object"); return interpreter.throw_exception<TypeError>("Not a Function object");
// FIXME: Functions should be able to know their name, if any // FIXME: Functions should be able to know their name, if any
if (this_object->is_native_function()) { if (this_object->is_native_function()) {
auto function_source = String::format("function () {\n [%s]\n}", this_object->class_name()); auto function_source = String::format("function () {\n [%s]\n}", this_object->class_name());

View file

@ -110,7 +110,7 @@ void Object::put_own_property(Object& this_object, const FlyString& property_nam
if (mode == PutOwnPropertyMode::DefineProperty && !(metadata.value().attributes & Attribute::Configurable) && attributes != metadata.value().attributes) { if (mode == PutOwnPropertyMode::DefineProperty && !(metadata.value().attributes & Attribute::Configurable) && attributes != metadata.value().attributes) {
dbg() << "Disallow reconfig of non-configurable property"; dbg() << "Disallow reconfig of non-configurable property";
interpreter().throw_exception<Error>("TypeError", String::format("Cannot redefine property '%s'", property_name.characters())); interpreter().throw_exception<TypeError>(String::format("Cannot redefine property '%s'", property_name.characters()));
return; return;
} }

View file

@ -101,9 +101,9 @@ Value ObjectConstructor::set_prototype_of(Interpreter& interpreter)
Value ObjectConstructor::get_own_property_descriptor(Interpreter& interpreter) Value ObjectConstructor::get_own_property_descriptor(Interpreter& interpreter)
{ {
if (interpreter.argument_count() < 2) if (interpreter.argument_count() < 2)
return interpreter.throw_exception<Error>("TypeError", "Object.getOwnPropertyDescriptor() needs 2 arguments"); return interpreter.throw_exception<TypeError>("Object.getOwnPropertyDescriptor() needs 2 arguments");
if (!interpreter.argument(0).is_object()) if (!interpreter.argument(0).is_object())
return interpreter.throw_exception<Error>("TypeError", "Object argument is not an object"); return interpreter.throw_exception<TypeError>("Object argument is not an object");
auto& object = interpreter.argument(0).as_object(); auto& object = interpreter.argument(0).as_object();
auto metadata = object.shape().lookup(interpreter.argument(1).to_string()); auto metadata = object.shape().lookup(interpreter.argument(1).to_string());
if (!metadata.has_value()) if (!metadata.has_value())
@ -119,11 +119,11 @@ Value ObjectConstructor::get_own_property_descriptor(Interpreter& interpreter)
Value ObjectConstructor::define_property(Interpreter& interpreter) Value ObjectConstructor::define_property(Interpreter& interpreter)
{ {
if (interpreter.argument_count() < 3) if (interpreter.argument_count() < 3)
return interpreter.throw_exception<Error>("TypeError", "Object.defineProperty() needs 3 arguments"); return interpreter.throw_exception<TypeError>("Object.defineProperty() needs 3 arguments");
if (!interpreter.argument(0).is_object()) if (!interpreter.argument(0).is_object())
return interpreter.throw_exception<Error>("TypeError", "Object argument is not an object"); return interpreter.throw_exception<TypeError>("Object argument is not an object");
if (!interpreter.argument(2).is_object()) if (!interpreter.argument(2).is_object())
return interpreter.throw_exception<Error>("TypeError", "Descriptor argument is not an object"); return interpreter.throw_exception<TypeError>("Descriptor argument is not an object");
auto& object = interpreter.argument(0).as_object(); auto& object = interpreter.argument(0).as_object();
auto& descriptor = interpreter.argument(2).as_object(); auto& descriptor = interpreter.argument(2).as_object();

View file

@ -70,7 +70,7 @@ Value ScriptFunction::length_getter(Interpreter& interpreter)
if (!this_object) if (!this_object)
return {}; return {};
if (!this_object->is_function()) if (!this_object->is_function())
return interpreter.throw_exception<Error>("TypeError", "Not a function"); return interpreter.throw_exception<TypeError>("Not a function");
return Value(static_cast<i32>(static_cast<const ScriptFunction*>(this_object)->parameters().size())); return Value(static_cast<i32>(static_cast<const ScriptFunction*>(this_object)->parameters().size()));
} }

View file

@ -120,7 +120,7 @@ Value StringPrototype::index_of(Interpreter& interpreter)
if (!this_object) if (!this_object)
return {}; return {};
if (!this_object->is_string_object()) if (!this_object->is_string_object())
return interpreter.throw_exception<Error>("TypeError", "Not a String object"); return interpreter.throw_exception<TypeError>("Not a String object");
Value needle_value = js_undefined(); Value needle_value = js_undefined();
if (interpreter.argument_count() >= 1) if (interpreter.argument_count() >= 1)
@ -141,7 +141,7 @@ static StringObject* string_object_from(Interpreter& interpreter)
if (!this_object) if (!this_object)
return nullptr; return nullptr;
if (!this_object->is_string_object()) { if (!this_object->is_string_object()) {
interpreter.throw_exception<Error>("TypeError", "Not a String object"); interpreter.throw_exception<TypeError>("Not a String object");
return nullptr; return nullptr;
} }
return static_cast<StringObject*>(this_object); return static_cast<StringObject*>(this_object);
@ -169,7 +169,7 @@ Value StringPrototype::length_getter(Interpreter& interpreter)
if (!this_object) if (!this_object)
return {}; return {};
if (!this_object->is_string_object()) if (!this_object->is_string_object())
return interpreter.throw_exception<Error>("TypeError", "Not a String object"); return interpreter.throw_exception<TypeError>("Not a String object");
return Value((i32) static_cast<const StringObject*>(this_object)->primitive_string()->string().length()); return Value((i32) static_cast<const StringObject*>(this_object)->primitive_string()->string().length());
} }

View file

@ -115,7 +115,7 @@ Object* Value::to_object(Heap& heap) const
return heap.allocate<BooleanObject>(m_value.as_bool); return heap.allocate<BooleanObject>(m_value.as_bool);
if (is_null() || is_undefined()) { if (is_null() || is_undefined()) {
heap.interpreter().throw_exception<Error>("TypeError", "ToObject on null or undefined."); heap.interpreter().throw_exception<TypeError>("ToObject on null or undefined.");
return nullptr; return nullptr;
} }