mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 18:22:45 +00:00 
			
		
		
		
	LibJS: Pass Interpreter& to Value::to_number() et al.
This patch is unfortunately rather large and might make some things feel
bloated, but it is necessary to fix a few flaws in LibJS, primarily
blindly coercing values to numbers without exception checks - i.e.
interpreter.argument(0).to_i32();  // can fail!!!
Some examples where the interpreter would actually crash:
var o = { toString: () => { throw Error() } };
+o;
o - 1;
"foo".charAt(o);
"bar".repeat(o);
To fix this, we now have the following...
to_double(Interpreter&)
to_i32()
to_i32(Interpreter&)
to_size_t()
to_size_t(Interpreter&)
...and a whole lot of exception checking.
There's intentionally no to_double(), use as_double() directly instead.
This way we still can use these convenient utility functions but don't
need to check for exceptions if we are sure the value already is a
number.
Fixes #2267.
			
			
This commit is contained in:
		
							parent
							
								
									1a1394f7a2
								
							
						
					
					
						commit
						476094922b
					
				
					 17 changed files with 491 additions and 187 deletions
				
			
		|  | @ -941,11 +941,12 @@ Value UpdateExpression::execute(Interpreter& interpreter) const | |||
|     auto reference = m_argument->to_reference(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
| 
 | ||||
|     auto old_value = reference.get(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     old_value = old_value.to_number(); | ||||
|     old_value = old_value.to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
| 
 | ||||
|     int op_result = 0; | ||||
|     switch (m_op) { | ||||
|  |  | |||
|  | @ -74,7 +74,9 @@ void Array::length_setter(Interpreter& interpreter, Value value) | |||
|     auto* array = array_from(interpreter); | ||||
|     if (!array) | ||||
|         return; | ||||
|     auto length = value.to_number(); | ||||
|     auto length = value.to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return; | ||||
|     if (length.is_nan() || length.is_infinity() || length.as_double() < 0) { | ||||
|         interpreter.throw_exception<RangeError>("Invalid array length"); | ||||
|         return; | ||||
|  |  | |||
|  | @ -297,7 +297,9 @@ Value ArrayPrototype::slice(Interpreter& interpreter) | |||
|     } | ||||
| 
 | ||||
|     ssize_t array_size = static_cast<ssize_t>(array->elements().size()); | ||||
|     auto start_slice = interpreter.argument(0).to_i32(); | ||||
|     auto start_slice = interpreter.argument(0).to_i32(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     auto end_slice = array_size; | ||||
| 
 | ||||
|     if (start_slice > array_size) | ||||
|  | @ -307,8 +309,9 @@ Value ArrayPrototype::slice(Interpreter& interpreter) | |||
|         start_slice = end_slice + start_slice; | ||||
| 
 | ||||
|     if (interpreter.argument_count() >= 2) { | ||||
|         end_slice = interpreter.argument(1).to_i32(); | ||||
| 
 | ||||
|         end_slice = interpreter.argument(1).to_i32(interpreter); | ||||
|         if (interpreter.exception()) | ||||
|             return {}; | ||||
|         if (end_slice < 0) | ||||
|             end_slice = array_size + end_slice; | ||||
|         else if (end_slice > array_size) | ||||
|  | @ -336,11 +339,11 @@ Value ArrayPrototype::index_of(Interpreter& interpreter) | |||
| 
 | ||||
|     i32 from_index = 0; | ||||
|     if (interpreter.argument_count() >= 2) { | ||||
|         from_index = interpreter.argument(1).to_number().to_i32(); | ||||
| 
 | ||||
|         from_index = interpreter.argument(1).to_i32(interpreter); | ||||
|         if (interpreter.exception()) | ||||
|             return {}; | ||||
|         if (from_index >= array_size) | ||||
|             return Value(-1); | ||||
| 
 | ||||
|         auto negative_min_index = ((array_size - 1) * -1); | ||||
|         if (from_index < negative_min_index) | ||||
|             from_index = 0; | ||||
|  | @ -390,11 +393,11 @@ Value ArrayPrototype::last_index_of(Interpreter& interpreter) | |||
| 
 | ||||
|     i32 from_index = 0; | ||||
|     if (interpreter.argument_count() >= 2) { | ||||
|         from_index = interpreter.argument(1).to_number().to_i32(); | ||||
| 
 | ||||
|         from_index = interpreter.argument(1).to_i32(interpreter); | ||||
|         if (interpreter.exception()) | ||||
|             return {}; | ||||
|         if (from_index >= array_size) | ||||
|             return Value(-1); | ||||
| 
 | ||||
|         auto negative_min_index = ((array_size - 1) * -1); | ||||
|         if (from_index < negative_min_index) | ||||
|             from_index = 0; | ||||
|  | @ -424,11 +427,11 @@ Value ArrayPrototype::includes(Interpreter& interpreter) | |||
| 
 | ||||
|     i32 from_index = 0; | ||||
|     if (interpreter.argument_count() >= 2) { | ||||
|         from_index = interpreter.argument(1).to_i32(); | ||||
| 
 | ||||
|         from_index = interpreter.argument(1).to_i32(interpreter); | ||||
|         if (interpreter.exception()) | ||||
|             return {}; | ||||
|         if (from_index >= array_size) | ||||
|             return Value(false); | ||||
| 
 | ||||
|         auto negative_min_index = ((array_size - 1) * -1); | ||||
|         if (from_index < negative_min_index) | ||||
|             from_index = 0; | ||||
|  |  | |||
|  | @ -90,17 +90,17 @@ Value ErrorPrototype::to_string(Interpreter& interpreter) | |||
|     auto& this_object = interpreter.this_value().as_object(); | ||||
| 
 | ||||
|     String name = "Error"; | ||||
|     auto object_name_property = this_object.get("name"); | ||||
|     if (!object_name_property.is_empty() && !object_name_property.is_undefined()) { | ||||
|         name = object_name_property.to_string(interpreter); | ||||
|     auto name_property = this_object.get("name"); | ||||
|     if (!name_property.is_empty() && !name_property.is_undefined()) { | ||||
|         name = name_property.to_string(interpreter); | ||||
|         if (interpreter.exception()) | ||||
|             return {}; | ||||
|     } | ||||
| 
 | ||||
|     String message = ""; | ||||
|     auto object_message_property = this_object.get("message"); | ||||
|     if (!object_message_property.is_empty() && !object_message_property.is_undefined()) { | ||||
|         message = object_message_property.to_string(interpreter); | ||||
|     auto message_property = this_object.get("message"); | ||||
|     if (!message_property.is_empty() && !message_property.is_undefined()) { | ||||
|         message = message_property.to_string(interpreter); | ||||
|         if (interpreter.exception()) | ||||
|             return {}; | ||||
|     } | ||||
|  |  | |||
|  | @ -73,7 +73,11 @@ Value FunctionPrototype::apply(Interpreter& interpreter) | |||
|     if (!arg_array.is_object()) | ||||
|         return interpreter.throw_exception<TypeError>("argument array must be an object"); | ||||
|     auto length_property = arg_array.as_object().get("length"); | ||||
|     auto length = length_property.to_size_t(); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     auto length = length_property.to_size_t(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     MarkedValueList arguments(interpreter.heap()); | ||||
|     for (size_t i = 0; i < length; ++i) { | ||||
|         auto element = arg_array.as_object().get(String::number(i)); | ||||
|  |  | |||
|  | @ -137,12 +137,18 @@ Value GlobalObject::gc(Interpreter& interpreter) | |||
| 
 | ||||
| Value GlobalObject::is_nan(Interpreter& interpreter) | ||||
| { | ||||
|     return Value(interpreter.argument(0).to_number().is_nan()); | ||||
|     auto number = interpreter.argument(0).to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     return Value(number.is_nan()); | ||||
| } | ||||
| 
 | ||||
| Value GlobalObject::is_finite(Interpreter& interpreter) | ||||
| { | ||||
|     return Value(interpreter.argument(0).to_number().is_finite_number()); | ||||
|     auto number = interpreter.argument(0).to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     return Value(number.is_finite_number()); | ||||
| } | ||||
| 
 | ||||
| Value GlobalObject::parse_float(Interpreter& interpreter) | ||||
|  | @ -151,7 +157,8 @@ Value GlobalObject::parse_float(Interpreter& interpreter) | |||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     for (size_t length = string.length(); length > 0; --length) { | ||||
|         auto number = Value(js_string(interpreter, string.substring(0, length))).to_number(); | ||||
|         // This can't throw, so no exception check is fine.
 | ||||
|         auto number = Value(js_string(interpreter, string.substring(0, length))).to_number(interpreter); | ||||
|         if (!number.is_nan()) | ||||
|             return number; | ||||
|     } | ||||
|  |  | |||
|  | @ -67,7 +67,9 @@ MathObject::~MathObject() | |||
| 
 | ||||
| Value MathObject::abs(Interpreter& interpreter) | ||||
| { | ||||
|     auto number = interpreter.argument(0).to_number(); | ||||
|     auto number = interpreter.argument(0).to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     if (number.is_nan()) | ||||
|         return js_nan(); | ||||
|     return Value(number.as_double() >= 0 ? number.as_double() : -number.as_double()); | ||||
|  | @ -85,7 +87,9 @@ Value MathObject::random(Interpreter&) | |||
| 
 | ||||
| Value MathObject::sqrt(Interpreter& interpreter) | ||||
| { | ||||
|     auto number = interpreter.argument(0).to_number(); | ||||
|     auto number = interpreter.argument(0).to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     if (number.is_nan()) | ||||
|         return js_nan(); | ||||
|     return Value(::sqrt(number.as_double())); | ||||
|  | @ -93,7 +97,9 @@ Value MathObject::sqrt(Interpreter& interpreter) | |||
| 
 | ||||
| Value MathObject::floor(Interpreter& interpreter) | ||||
| { | ||||
|     auto number = interpreter.argument(0).to_number(); | ||||
|     auto number = interpreter.argument(0).to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     if (number.is_nan()) | ||||
|         return js_nan(); | ||||
|     return Value(::floor(number.as_double())); | ||||
|  | @ -101,7 +107,9 @@ Value MathObject::floor(Interpreter& interpreter) | |||
| 
 | ||||
| Value MathObject::ceil(Interpreter& interpreter) | ||||
| { | ||||
|     auto number = interpreter.argument(0).to_number(); | ||||
|     auto number = interpreter.argument(0).to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     if (number.is_nan()) | ||||
|         return js_nan(); | ||||
|     return Value(::ceil(number.as_double())); | ||||
|  | @ -109,7 +117,9 @@ Value MathObject::ceil(Interpreter& interpreter) | |||
| 
 | ||||
| Value MathObject::round(Interpreter& interpreter) | ||||
| { | ||||
|     auto number = interpreter.argument(0).to_number(); | ||||
|     auto number = interpreter.argument(0).to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     if (number.is_nan()) | ||||
|         return js_nan(); | ||||
|     return Value(::round(number.as_double())); | ||||
|  | @ -120,12 +130,13 @@ Value MathObject::max(Interpreter& interpreter) | |||
|     if (!interpreter.argument_count()) | ||||
|         return js_negative_infinity(); | ||||
| 
 | ||||
|     if (interpreter.argument_count() == 1) | ||||
|         return interpreter.argument(0).to_number(); | ||||
| 
 | ||||
|     Value max = interpreter.argument(0).to_number(); | ||||
|     auto max = interpreter.argument(0).to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     for (size_t i = 1; i < interpreter.argument_count(); ++i) { | ||||
|         Value cur = interpreter.argument(i).to_number(); | ||||
|         auto cur = interpreter.argument(i).to_number(interpreter); | ||||
|         if (interpreter.exception()) | ||||
|             return {}; | ||||
|         max = Value(cur.as_double() > max.as_double() ? cur : max); | ||||
|     } | ||||
|     return max; | ||||
|  | @ -136,12 +147,13 @@ Value MathObject::min(Interpreter& interpreter) | |||
|     if (!interpreter.argument_count()) | ||||
|         return js_infinity(); | ||||
| 
 | ||||
|     if (interpreter.argument_count() == 1) | ||||
|         return interpreter.argument(0).to_number(); | ||||
| 
 | ||||
|     Value min = interpreter.argument(0).to_number(); | ||||
|     auto min = interpreter.argument(0).to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     for (size_t i = 1; i < interpreter.argument_count(); ++i) { | ||||
|         Value cur = interpreter.argument(i).to_number(); | ||||
|         auto cur = interpreter.argument(i).to_number(interpreter); | ||||
|         if (interpreter.exception()) | ||||
|             return {}; | ||||
|         min = Value(cur.as_double() < min.as_double() ? cur : min); | ||||
|     } | ||||
|     return min; | ||||
|  | @ -149,10 +161,11 @@ Value MathObject::min(Interpreter& interpreter) | |||
| 
 | ||||
| Value MathObject::trunc(Interpreter& interpreter) | ||||
| { | ||||
|     auto number = interpreter.argument(0).to_number(); | ||||
|     auto number = interpreter.argument(0).to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     if (number.is_nan()) | ||||
|         return js_nan(); | ||||
| 
 | ||||
|     if (number.as_double() < 0) | ||||
|         return MathObject::ceil(interpreter); | ||||
|     return MathObject::floor(interpreter); | ||||
|  | @ -160,7 +173,9 @@ Value MathObject::trunc(Interpreter& interpreter) | |||
| 
 | ||||
| Value MathObject::sin(Interpreter& interpreter) | ||||
| { | ||||
|     auto number = interpreter.argument(0).to_number(); | ||||
|     auto number = interpreter.argument(0).to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     if (number.is_nan()) | ||||
|         return js_nan(); | ||||
|     return Value(::sin(number.as_double())); | ||||
|  | @ -168,7 +183,9 @@ Value MathObject::sin(Interpreter& interpreter) | |||
| 
 | ||||
| Value MathObject::cos(Interpreter& interpreter) | ||||
| { | ||||
|     auto number = interpreter.argument(0).to_number(); | ||||
|     auto number = interpreter.argument(0).to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     if (number.is_nan()) | ||||
|         return js_nan(); | ||||
|     return Value(::cos(number.as_double())); | ||||
|  | @ -176,7 +193,9 @@ Value MathObject::cos(Interpreter& interpreter) | |||
| 
 | ||||
| Value MathObject::tan(Interpreter& interpreter) | ||||
| { | ||||
|     auto number = interpreter.argument(0).to_number(); | ||||
|     auto number = interpreter.argument(0).to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     if (number.is_nan()) | ||||
|         return js_nan(); | ||||
|     return Value(::tan(number.as_double())); | ||||
|  |  | |||
|  | @ -64,16 +64,17 @@ Value NumberConstructor::call(Interpreter& interpreter) | |||
| { | ||||
|     if (!interpreter.argument_count()) | ||||
|         return Value(0); | ||||
|     return interpreter.argument(0).to_number(); | ||||
|     return interpreter.argument(0).to_number(interpreter); | ||||
| } | ||||
| 
 | ||||
| Value NumberConstructor::construct(Interpreter& interpreter) | ||||
| { | ||||
|     double number; | ||||
|     if (!interpreter.argument_count()) | ||||
|         number = 0; | ||||
|     else | ||||
|         number = interpreter.argument(0).to_number().as_double(); | ||||
|     double number = 0; | ||||
|     if (interpreter.argument_count()) { | ||||
|         number = interpreter.argument(0).to_double(interpreter); | ||||
|         if (interpreter.exception()) | ||||
|             return {}; | ||||
|     } | ||||
|     return NumberObject::create(interpreter.global_object(), number); | ||||
| } | ||||
| 
 | ||||
|  | @ -96,7 +97,7 @@ Value NumberConstructor::is_safe_integer(Interpreter& interpreter) | |||
| { | ||||
|     if (!interpreter.argument(0).is_number()) | ||||
|         return Value(false); | ||||
|     auto value = interpreter.argument(0).to_number().as_double(); | ||||
|     auto value = interpreter.argument(0).as_double(); | ||||
|     return Value((int64_t)value == value && value >= MIN_SAFE_INTEGER && value <= MAX_SAFE_INTEGER); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -64,7 +64,9 @@ static void prepare_arguments_list(Interpreter& interpreter, Value value, Marked | |||
|     auto length_property = arguments_list.get("length"); | ||||
|     if (interpreter.exception()) | ||||
|         return; | ||||
|     auto length = length_property.to_size_t(); | ||||
|     auto length = length_property.to_size_t(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return; | ||||
|     for (size_t i = 0; i < length; ++i) { | ||||
|         auto element = arguments_list.get(String::number(i)); | ||||
|         if (interpreter.exception()) | ||||
|  | @ -156,8 +158,11 @@ Value ReflectObject::delete_property(Interpreter& interpreter) | |||
|     auto property_name = PropertyName(property_key.to_string(interpreter)); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     if (property_key.to_number().is_finite_number()) { | ||||
|         auto property_key_as_double = property_key.to_double(); | ||||
|     auto property_key_number = property_key.to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     if (property_key_number.is_finite_number()) { | ||||
|         auto property_key_as_double = property_key_number.as_double(); | ||||
|         if (property_key_as_double >= 0 && (i32)property_key_as_double == property_key_as_double) | ||||
|             property_name = PropertyName(property_key_as_double); | ||||
|     } | ||||
|  |  | |||
|  | @ -94,8 +94,11 @@ Value StringPrototype::char_at(Interpreter& interpreter) | |||
|     if (string.is_null()) | ||||
|         return {}; | ||||
|     i32 index = 0; | ||||
|     if (interpreter.argument_count()) | ||||
|         index = interpreter.argument(0).to_i32(); | ||||
|     if (interpreter.argument_count()) { | ||||
|         index = interpreter.argument(0).to_i32(interpreter); | ||||
|         if (interpreter.exception()) | ||||
|             return {}; | ||||
|     } | ||||
|     if (index < 0 || index >= static_cast<i32>(string.length())) | ||||
|         return js_string(interpreter, String::empty()); | ||||
|     return js_string(interpreter, string.substring(index, 1)); | ||||
|  | @ -108,7 +111,9 @@ Value StringPrototype::repeat(Interpreter& interpreter) | |||
|         return {}; | ||||
|     if (!interpreter.argument_count()) | ||||
|         return js_string(interpreter, String::empty()); | ||||
|     auto count_value = interpreter.argument(0).to_number(); | ||||
|     auto count_value = interpreter.argument(0).to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     if (count_value.as_double() < 0) | ||||
|         return interpreter.throw_exception<RangeError>("repeat count must be a positive number"); | ||||
|     if (count_value.is_infinity()) | ||||
|  | @ -134,9 +139,11 @@ Value StringPrototype::starts_with(Interpreter& interpreter) | |||
|     auto search_string_length = search_string.length(); | ||||
|     size_t start = 0; | ||||
|     if (interpreter.argument_count() > 1) { | ||||
|         auto number = interpreter.argument(1).to_number(); | ||||
|         auto number = interpreter.argument(1).to_number(interpreter); | ||||
|         if (interpreter.exception()) | ||||
|             return {}; | ||||
|         if (!number.is_nan()) | ||||
|             start = min(number.to_size_t(), string_length); | ||||
|             start = min(number.to_size_t(interpreter), string_length); | ||||
|     } | ||||
|     if (start + search_string_length > string_length) | ||||
|         return Value(false); | ||||
|  | @ -195,7 +202,9 @@ enum class PadPlacement { | |||
| 
 | ||||
| static Value pad_string(Interpreter& interpreter, const String& string, PadPlacement placement) | ||||
| { | ||||
|     auto max_length = interpreter.argument(0).to_size_t(); | ||||
|     auto max_length = interpreter.argument(0).to_size_t(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     if (max_length <= string.length()) | ||||
|         return js_string(interpreter, string); | ||||
| 
 | ||||
|  | @ -285,11 +294,15 @@ Value StringPrototype::substring(Interpreter& interpreter) | |||
|         return js_string(interpreter, string); | ||||
| 
 | ||||
|     auto string_length = string.length(); | ||||
|     auto index_start = min(interpreter.argument(0).to_size_t(), string_length); | ||||
|     auto index_start = min(interpreter.argument(0).to_size_t(interpreter), string_length); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     auto index_end = string_length; | ||||
| 
 | ||||
|     if (interpreter.argument_count() >= 2) | ||||
|         index_end = min(interpreter.argument(1).to_size_t(), string_length); | ||||
|     if (interpreter.argument_count() >= 2) { | ||||
|         index_end = min(interpreter.argument(1).to_size_t(interpreter), string_length); | ||||
|         if (interpreter.exception()) | ||||
|             return {}; | ||||
|     } | ||||
| 
 | ||||
|     if (index_start == index_end) | ||||
|         return js_string(interpreter, String("")); | ||||
|  | @ -318,7 +331,9 @@ Value StringPrototype::includes(Interpreter& interpreter) | |||
| 
 | ||||
|     size_t position = 0; | ||||
|     if (interpreter.argument_count() >= 2) { | ||||
|         position = interpreter.argument(1).to_size_t(); | ||||
|         position = interpreter.argument(1).to_size_t(interpreter); | ||||
|         if (interpreter.exception()) | ||||
|             return {}; | ||||
|         if (position >= string.length()) | ||||
|             return Value(false); | ||||
|     } | ||||
|  | @ -341,7 +356,9 @@ Value StringPrototype::slice(Interpreter& interpreter) | |||
|         return js_string(interpreter, string); | ||||
| 
 | ||||
|     auto string_length = static_cast<i32>(string.length()); | ||||
|     auto index_start = interpreter.argument(0).to_i32(); | ||||
|     auto index_start = interpreter.argument(0).to_i32(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     auto index_end = string_length; | ||||
| 
 | ||||
|     auto negative_min_index = -(string_length - 1); | ||||
|  | @ -351,7 +368,9 @@ Value StringPrototype::slice(Interpreter& interpreter) | |||
|         index_start = string_length + index_start; | ||||
| 
 | ||||
|     if (interpreter.argument_count() >= 2) { | ||||
|         index_end = interpreter.argument(1).to_i32(); | ||||
|         index_end = interpreter.argument(1).to_i32(interpreter); | ||||
|         if (interpreter.exception()) | ||||
|             return {}; | ||||
| 
 | ||||
|         if (index_end < negative_min_index) | ||||
|             return js_string(interpreter, String::empty()); | ||||
|  | @ -387,8 +406,11 @@ Value StringPrototype::last_index_of(Interpreter& interpreter) | |||
| 
 | ||||
|     auto max_index = string.length() - search_string.length(); | ||||
|     auto from_index = max_index; | ||||
|     if (interpreter.argument_count() >= 2) | ||||
|         from_index = min(interpreter.argument(1).to_size_t(), max_index); | ||||
|     if (interpreter.argument_count() >= 2) { | ||||
|         from_index = min(interpreter.argument(1).to_size_t(interpreter), max_index); | ||||
|         if (interpreter.exception()) | ||||
|             return {}; | ||||
|     } | ||||
| 
 | ||||
|     for (i32 i = from_index; i >= 0; --i) { | ||||
|         auto part_view = string.substring_view(i, search_string.length()); | ||||
|  |  | |||
|  | @ -70,7 +70,10 @@ bool Uint8ClampedArray::put_by_index(i32 property_index, Value value, u8) | |||
|     // FIXME: Use attributes
 | ||||
|     ASSERT(property_index >= 0); | ||||
|     ASSERT(property_index < m_length); | ||||
|     m_data[property_index] = clamp(value.to_i32(), 0, 255); | ||||
|     auto number = value.to_i32(interpreter()); | ||||
|     if (interpreter().exception()) | ||||
|         return {}; | ||||
|     m_data[property_index] = clamp(number, 0, 255); | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -36,8 +36,8 @@ | |||
| #include <LibJS/Runtime/NumberObject.h> | ||||
| #include <LibJS/Runtime/Object.h> | ||||
| #include <LibJS/Runtime/PrimitiveString.h> | ||||
| #include <LibJS/Runtime/Symbol.h> | ||||
| #include <LibJS/Runtime/StringObject.h> | ||||
| #include <LibJS/Runtime/Symbol.h> | ||||
| #include <LibJS/Runtime/SymbolObject.h> | ||||
| #include <LibJS/Runtime/Value.h> | ||||
| #include <math.h> | ||||
|  | @ -96,7 +96,7 @@ String Value::to_string_without_side_effects() const | |||
|     return String::format("[object %s]", as_object().class_name()); | ||||
| } | ||||
| 
 | ||||
| PrimitiveString* Value::to_primitive_string(Interpreter & interpreter) | ||||
| PrimitiveString* Value::to_primitive_string(Interpreter& interpreter) | ||||
| { | ||||
|     if (is_string()) | ||||
|         return &as_string(); | ||||
|  | @ -202,7 +202,7 @@ Object* Value::to_object(Interpreter& interpreter) const | |||
|     ASSERT_NOT_REACHED(); | ||||
| } | ||||
| 
 | ||||
| Value Value::to_number() const | ||||
| Value Value::to_number(Interpreter& interpreter) const | ||||
| { | ||||
|     switch (m_type) { | ||||
|     case Type::Empty: | ||||
|  | @ -234,58 +234,110 @@ Value Value::to_number() const | |||
|         // FIXME: Get access to the interpreter and throw a TypeError
 | ||||
|         ASSERT_NOT_REACHED(); | ||||
|     case Type::Object: | ||||
|         return m_value.as_object->to_primitive(Object::PreferredType::Number).to_number(); | ||||
|         auto primitive = m_value.as_object->to_primitive(Object::PreferredType::Number); | ||||
|         if (interpreter.exception()) | ||||
|             return {}; | ||||
|         return primitive.to_number(interpreter); | ||||
|     } | ||||
| 
 | ||||
|     ASSERT_NOT_REACHED(); | ||||
| } | ||||
| 
 | ||||
| i32 Value::to_i32() const | ||||
| double Value::to_double(Interpreter& interpreter) const | ||||
| { | ||||
|     return static_cast<i32>(to_number().as_double()); | ||||
|     auto number = to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return 0; | ||||
|     return number.as_double(); | ||||
| } | ||||
| 
 | ||||
| double Value::to_double() const | ||||
| i32 Value::to_i32() const | ||||
| { | ||||
|     return to_number().as_double(); | ||||
|     return static_cast<i32>(as_double()); | ||||
| } | ||||
| 
 | ||||
| i32 Value::to_i32(Interpreter& interpreter) const | ||||
| { | ||||
|     auto number = to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return 0; | ||||
|     return number.to_i32(); | ||||
| } | ||||
| 
 | ||||
| size_t Value::to_size_t() const | ||||
| { | ||||
|     ASSERT(type() == Type::Number); | ||||
|     if (is_nan() || as_double() <= 0) | ||||
|         return 0; | ||||
|     return min((double)(i32)as_double(), MAX_ARRAY_LIKE_INDEX); | ||||
| } | ||||
| 
 | ||||
| size_t Value::to_size_t(Interpreter& interpreter) const | ||||
| { | ||||
|     if (is_empty()) | ||||
|         return 0; | ||||
|     auto number = to_number(); | ||||
|     if (number.is_nan() || number.as_double() <= 0) | ||||
|     auto number = to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return 0; | ||||
|     return min((double)number.to_i32(), MAX_ARRAY_LIKE_INDEX); | ||||
|     return number.to_size_t(); | ||||
| } | ||||
| 
 | ||||
| Value greater_than(Interpreter&, Value lhs, Value rhs) | ||||
| Value greater_than(Interpreter& interpreter, Value lhs, Value rhs) | ||||
| { | ||||
|     return Value(lhs.to_number().as_double() > rhs.to_number().as_double()); | ||||
|     auto lhs_number = lhs.to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     auto rhs_number = rhs.to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     return Value(lhs_number.as_double() > rhs_number.as_double()); | ||||
| } | ||||
| 
 | ||||
| Value greater_than_equals(Interpreter&, Value lhs, Value rhs) | ||||
| Value greater_than_equals(Interpreter& interpreter, Value lhs, Value rhs) | ||||
| { | ||||
|     return Value(lhs.to_number().as_double() >= rhs.to_number().as_double()); | ||||
|     auto lhs_number = lhs.to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     auto rhs_number = rhs.to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     return Value(lhs_number.as_double() >= rhs_number.as_double()); | ||||
| } | ||||
| 
 | ||||
| Value less_than(Interpreter&, Value lhs, Value rhs) | ||||
| Value less_than(Interpreter& interpreter, Value lhs, Value rhs) | ||||
| { | ||||
|     return Value(lhs.to_number().as_double() < rhs.to_number().as_double()); | ||||
|     auto lhs_number = lhs.to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     auto rhs_number = rhs.to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     return Value(lhs_number.as_double() < rhs_number.as_double()); | ||||
| } | ||||
| 
 | ||||
| Value less_than_equals(Interpreter&, Value lhs, Value rhs) | ||||
| Value less_than_equals(Interpreter& interpreter, Value lhs, Value rhs) | ||||
| { | ||||
|     return Value(lhs.to_number().as_double() <= rhs.to_number().as_double()); | ||||
|     auto lhs_number = lhs.to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     auto rhs_number = rhs.to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     return Value(lhs_number.as_double() <= rhs_number.as_double()); | ||||
| } | ||||
| 
 | ||||
| Value bitwise_and(Interpreter&, Value lhs, Value rhs) | ||||
| Value bitwise_and(Interpreter& interpreter, Value lhs, Value rhs) | ||||
| { | ||||
|     return Value((i32)lhs.to_number().as_double() & (i32)rhs.to_number().as_double()); | ||||
|     auto lhs_number = lhs.to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     auto rhs_number = rhs.to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     return Value((i32)lhs_number.as_double() & (i32)rhs_number.as_double()); | ||||
| } | ||||
| 
 | ||||
| Value bitwise_or(Interpreter&, Value lhs, Value rhs) | ||||
| Value bitwise_or(Interpreter& interpreter, Value lhs, Value rhs) | ||||
| { | ||||
|     bool lhs_invalid = lhs.is_undefined() || lhs.is_null() || lhs.is_nan() || lhs.is_infinity(); | ||||
|     bool rhs_invalid = rhs.is_undefined() || rhs.is_null() || rhs.is_nan() || rhs.is_infinity(); | ||||
|  | @ -294,64 +346,94 @@ Value bitwise_or(Interpreter&, Value lhs, Value rhs) | |||
|         return Value(0); | ||||
| 
 | ||||
|     if (lhs_invalid || rhs_invalid) | ||||
|         return lhs_invalid ? rhs.to_number() : lhs.to_number(); | ||||
|         return lhs_invalid ? rhs.to_number(interpreter) : lhs.to_number(interpreter); | ||||
| 
 | ||||
|     if (!rhs.is_number() && !lhs.is_number()) | ||||
|         return Value(0); | ||||
| 
 | ||||
|     return Value((i32)lhs.to_number().as_double() | (i32)rhs.to_number().as_double()); | ||||
|     auto lhs_number = lhs.to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     auto rhs_number = rhs.to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     return Value((i32)lhs_number.as_double() | (i32)rhs_number.as_double()); | ||||
| } | ||||
| 
 | ||||
| Value bitwise_xor(Interpreter&, Value lhs, Value rhs) | ||||
| Value bitwise_xor(Interpreter& interpreter, Value lhs, Value rhs) | ||||
| { | ||||
|     return Value((i32)lhs.to_number().as_double() ^ (i32)rhs.to_number().as_double()); | ||||
|     auto lhs_number = lhs.to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     auto rhs_number = rhs.to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     return Value((i32)lhs_number.as_double() ^ (i32)rhs_number.as_double()); | ||||
| } | ||||
| 
 | ||||
| Value bitwise_not(Interpreter&, Value lhs) | ||||
| Value bitwise_not(Interpreter& interpreter, Value lhs) | ||||
| { | ||||
|     return Value(~(i32)lhs.to_number().as_double()); | ||||
|     auto lhs_number = lhs.to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     return Value(~(i32)lhs_number.as_double()); | ||||
| } | ||||
| 
 | ||||
| Value unary_plus(Interpreter&, Value lhs) | ||||
| Value unary_plus(Interpreter& interpreter, Value lhs) | ||||
| { | ||||
|     return lhs.to_number(); | ||||
|     return lhs.to_number(interpreter); | ||||
| } | ||||
| 
 | ||||
| Value unary_minus(Interpreter&, Value lhs) | ||||
| Value unary_minus(Interpreter& interpreter, Value lhs) | ||||
| { | ||||
|     if (lhs.to_number().is_nan()) | ||||
|     auto lhs_number = lhs.to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     if (lhs_number.is_nan()) | ||||
|         return js_nan(); | ||||
|     return Value(-lhs.to_number().as_double()); | ||||
|     return Value(-lhs_number.as_double()); | ||||
| } | ||||
| 
 | ||||
| Value left_shift(Interpreter&, Value lhs, Value rhs) | ||||
| Value left_shift(Interpreter& interpreter, Value lhs, Value rhs) | ||||
| { | ||||
|     auto lhs_number = lhs.to_number(); | ||||
|     auto lhs_number = lhs.to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     if (!lhs_number.is_finite_number()) | ||||
|         return Value(0); | ||||
|     auto rhs_number = rhs.to_number(); | ||||
|     auto rhs_number = rhs.to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     if (!rhs_number.is_finite_number()) | ||||
|         return lhs_number; | ||||
|     return Value((i32)lhs_number.as_double() << (i32)rhs_number.as_double()); | ||||
| } | ||||
| 
 | ||||
| Value right_shift(Interpreter&, Value lhs, Value rhs) | ||||
| Value right_shift(Interpreter& interpreter, Value lhs, Value rhs) | ||||
| { | ||||
|     auto lhs_number = lhs.to_number(); | ||||
|     auto lhs_number = lhs.to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     if (!lhs_number.is_finite_number()) | ||||
|         return Value(0); | ||||
|     auto rhs_number = rhs.to_number(); | ||||
|     auto rhs_number = rhs.to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     if (!rhs_number.is_finite_number()) | ||||
|         return lhs_number; | ||||
|     return Value((i32)lhs_number.as_double() >> (i32)rhs_number.as_double()); | ||||
| } | ||||
| 
 | ||||
| Value unsigned_right_shift(Interpreter&, Value lhs, Value rhs) | ||||
| Value unsigned_right_shift(Interpreter& interpreter, Value lhs, Value rhs) | ||||
| { | ||||
|     auto lhs_number = lhs.to_number(); | ||||
|     auto lhs_number = lhs.to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     if (!lhs_number.is_finite_number()) | ||||
|         return Value(0); | ||||
|     auto rhs_number = rhs.to_number(); | ||||
|     auto rhs_number = rhs.to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     if (!rhs_number.is_finite_number()) | ||||
|         return lhs_number; | ||||
|     return Value((unsigned)lhs_number.as_double() >> (i32)rhs_number.as_double()); | ||||
|  | @ -379,50 +461,82 @@ Value add(Interpreter& interpreter, Value lhs, Value rhs) | |||
|         return js_string(interpreter, builder.to_string()); | ||||
|     } | ||||
| 
 | ||||
|     return Value(lhs_primitive.to_number().as_double() + rhs_primitive.to_number().as_double()); | ||||
|     auto lhs_number = lhs_primitive.to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     auto rhs_number = rhs_primitive.to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     return Value(lhs_number.as_double() + rhs_number.as_double()); | ||||
| } | ||||
| 
 | ||||
| Value sub(Interpreter&, Value lhs, Value rhs) | ||||
| Value sub(Interpreter& interpreter, Value lhs, Value rhs) | ||||
| { | ||||
|     return Value(lhs.to_number().as_double() - rhs.to_number().as_double()); | ||||
|     auto lhs_number = lhs.to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     auto rhs_number = rhs.to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     return Value(lhs_number.as_double() - rhs_number.as_double()); | ||||
| } | ||||
| 
 | ||||
| Value mul(Interpreter&, Value lhs, Value rhs) | ||||
| Value mul(Interpreter& interpreter, Value lhs, Value rhs) | ||||
| { | ||||
|     return Value(lhs.to_number().as_double() * rhs.to_number().as_double()); | ||||
|     auto lhs_number = lhs.to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     auto rhs_number = rhs.to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     return Value(lhs_number.as_double() * rhs_number.as_double()); | ||||
| } | ||||
| 
 | ||||
| Value div(Interpreter&, Value lhs, Value rhs) | ||||
| Value div(Interpreter& interpreter, Value lhs, Value rhs) | ||||
| { | ||||
|     return Value(lhs.to_number().as_double() / rhs.to_number().as_double()); | ||||
|     auto lhs_number = lhs.to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     auto rhs_number = rhs.to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     return Value(lhs_number.as_double() / rhs_number.as_double()); | ||||
| } | ||||
| 
 | ||||
| Value mod(Interpreter&, Value lhs, Value rhs) | ||||
| Value mod(Interpreter& interpreter, Value lhs, Value rhs) | ||||
| { | ||||
|     if (lhs.to_number().is_nan() || rhs.to_number().is_nan()) | ||||
|     auto lhs_number = lhs.to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     auto rhs_number = rhs.to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     if (lhs_number.is_nan() || rhs_number.is_nan()) | ||||
|         return js_nan(); | ||||
| 
 | ||||
|     double index = lhs.to_number().as_double(); | ||||
|     double period = rhs.to_number().as_double(); | ||||
|     double trunc = (double)(i32)(index / period); | ||||
| 
 | ||||
|     auto index = lhs_number.as_double(); | ||||
|     auto period = rhs_number.as_double(); | ||||
|     auto trunc = (double)(i32)(index / period); | ||||
|     return Value(index - trunc * period); | ||||
| } | ||||
| 
 | ||||
| Value exp(Interpreter&, Value lhs, Value rhs) | ||||
| Value exp(Interpreter& interpreter, Value lhs, Value rhs) | ||||
| { | ||||
|     return Value(pow(lhs.to_number().as_double(), rhs.to_number().as_double())); | ||||
|     auto lhs_number = lhs.to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     auto rhs_number = rhs.to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     return Value(pow(lhs_number.as_double(), rhs_number.as_double())); | ||||
| } | ||||
| 
 | ||||
| Value in(Interpreter& interpreter, Value lhs, Value rhs) | ||||
| { | ||||
|     if (!rhs.is_object()) | ||||
|         return interpreter.throw_exception<TypeError>("'in' operator must be used on object"); | ||||
| 
 | ||||
|     auto lhs_string = lhs.to_string(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
| 
 | ||||
|     return Value(!rhs.as_object().get(lhs_string).is_empty()); | ||||
| } | ||||
| 
 | ||||
|  | @ -430,11 +544,9 @@ Value instance_of(Interpreter&, Value lhs, Value rhs) | |||
| { | ||||
|     if (!lhs.is_object() || !rhs.is_object()) | ||||
|         return Value(false); | ||||
| 
 | ||||
|     auto constructor_prototype_property = rhs.as_object().get("prototype"); | ||||
|     if (!constructor_prototype_property.is_object()) | ||||
|         return Value(false); | ||||
| 
 | ||||
|     return Value(lhs.as_object().has_prototype(&constructor_prototype_property.as_object())); | ||||
| } | ||||
| 
 | ||||
|  | @ -455,7 +567,7 @@ bool same_value(Interpreter& interpreter, Value lhs, Value rhs) | |||
|             return false; | ||||
|         if (lhs.is_negative_zero() && rhs.is_positive_zero()) | ||||
|             return false; | ||||
|         return lhs.to_double() == rhs.to_double(); | ||||
|         return lhs.as_double() == rhs.as_double(); | ||||
|     } | ||||
| 
 | ||||
|     return same_value_non_numeric(interpreter, lhs, rhs); | ||||
|  | @ -471,7 +583,7 @@ bool same_value_zero(Interpreter& interpreter, Value lhs, Value rhs) | |||
|             return true; | ||||
|         if ((lhs.is_positive_zero() || lhs.is_negative_zero()) && (rhs.is_positive_zero() || rhs.is_negative_zero())) | ||||
|             return true; | ||||
|         return lhs.to_double() == rhs.to_double(); | ||||
|         return lhs.as_double() == rhs.as_double(); | ||||
|     } | ||||
| 
 | ||||
|     return same_value_non_numeric(interpreter, lhs, rhs); | ||||
|  | @ -509,7 +621,7 @@ bool strict_eq(Interpreter& interpreter, Value lhs, Value rhs) | |||
|     if (lhs.is_number()) { | ||||
|         if (lhs.is_nan() || rhs.is_nan()) | ||||
|             return false; | ||||
|         if (lhs.to_double() == rhs.to_double()) | ||||
|         if (lhs.as_double() == rhs.as_double()) | ||||
|             return true; | ||||
|         if ((lhs.is_positive_zero() || lhs.is_negative_zero()) && (rhs.is_positive_zero() || rhs.is_negative_zero())) | ||||
|             return true; | ||||
|  | @ -528,16 +640,16 @@ bool abstract_eq(Interpreter& interpreter, Value lhs, Value rhs) | |||
|         return true; | ||||
| 
 | ||||
|     if (lhs.is_number() && rhs.is_string()) | ||||
|         return abstract_eq(interpreter, lhs, rhs.to_number()); | ||||
|         return abstract_eq(interpreter, lhs, rhs.to_number(interpreter)); | ||||
| 
 | ||||
|     if (lhs.is_string() && rhs.is_number()) | ||||
|         return abstract_eq(interpreter, lhs.to_number(), rhs); | ||||
|         return abstract_eq(interpreter, lhs.to_number(interpreter), rhs); | ||||
| 
 | ||||
|     if (lhs.is_boolean()) | ||||
|         return abstract_eq(interpreter, lhs.to_number(), rhs); | ||||
|         return abstract_eq(interpreter, lhs.to_number(interpreter), rhs); | ||||
| 
 | ||||
|     if (rhs.is_boolean()) | ||||
|         return abstract_eq(interpreter, lhs, rhs.to_number()); | ||||
|         return abstract_eq(interpreter, lhs, rhs.to_number(interpreter)); | ||||
| 
 | ||||
|     if ((lhs.is_string() || lhs.is_number() || lhs.is_symbol()) && rhs.is_object()) | ||||
|         return abstract_eq(interpreter, lhs, rhs.to_primitive(interpreter)); | ||||
|  |  | |||
|  | @ -188,11 +188,13 @@ public: | |||
|     PrimitiveString* to_primitive_string(Interpreter&); | ||||
|     Value to_primitive(Interpreter&) const; | ||||
|     Object* to_object(Interpreter&) const; | ||||
|     bool to_boolean() const; | ||||
|     Value to_number() const; | ||||
|     Value to_number(Interpreter&) const; | ||||
|     double to_double(Interpreter&) const; | ||||
|     i32 to_i32() const; | ||||
|     double to_double() const; | ||||
|     i32 to_i32(Interpreter&) const; | ||||
|     size_t to_size_t() const; | ||||
|     size_t to_size_t(Interpreter&) const; | ||||
|     bool to_boolean() const; | ||||
| 
 | ||||
|     Value value_or(Value fallback) const | ||||
|     { | ||||
|  |  | |||
							
								
								
									
										38
									
								
								Libraries/LibJS/Tests/to-number-exception.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								Libraries/LibJS/Tests/to-number-exception.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,38 @@ | |||
| load("test-common.js"); | ||||
| 
 | ||||
| try { | ||||
|     const message = "oops, Value::to_number() failed"; | ||||
|     const o = { toString() { throw new Error(message); } }; | ||||
| 
 | ||||
|     assertThrowsError(() => { | ||||
|         +o; | ||||
|     }, { | ||||
|         error: Error, | ||||
|         message | ||||
|     }); | ||||
| 
 | ||||
|     assertThrowsError(() => { | ||||
|         o - 1; | ||||
|     }, { | ||||
|         error: Error, | ||||
|         message | ||||
|     }); | ||||
| 
 | ||||
|     assertThrowsError(() => { | ||||
|         "foo".charAt(o); | ||||
|     }, { | ||||
|         error: Error, | ||||
|         message | ||||
|     }); | ||||
| 
 | ||||
|     assertThrowsError(() => { | ||||
|         "bar".repeat(o); | ||||
|     }, { | ||||
|         error: Error, | ||||
|         message | ||||
|     }); | ||||
| 
 | ||||
|     console.log("PASS"); | ||||
| } catch (e) { | ||||
|     console.log("FAIL: " + e); | ||||
| } | ||||
|  | @ -91,8 +91,21 @@ JS::Value CanvasRenderingContext2DWrapper::fill_rect(JS::Interpreter& interprete | |||
|     if (!impl) | ||||
|         return {}; | ||||
|     auto& arguments = interpreter.call_frame().arguments; | ||||
|     if (arguments.size() >= 4) | ||||
|         impl->fill_rect(arguments[0].to_double(), arguments[1].to_double(), arguments[2].to_double(), arguments[3].to_double()); | ||||
|     if (arguments.size() >= 4) { | ||||
|         auto x = arguments[0].to_double(interpreter); | ||||
|         if (interpreter.exception()) | ||||
|             return {}; | ||||
|         auto y = arguments[1].to_double(interpreter); | ||||
|         if (interpreter.exception()) | ||||
|             return {}; | ||||
|         auto width = arguments[2].to_double(interpreter); | ||||
|         if (interpreter.exception()) | ||||
|             return {}; | ||||
|         auto height = arguments[3].to_double(interpreter); | ||||
|         if (interpreter.exception()) | ||||
|             return {}; | ||||
|         impl->fill_rect(x, y, width, height); | ||||
|     } | ||||
|     return JS::js_undefined(); | ||||
| } | ||||
| 
 | ||||
|  | @ -102,8 +115,22 @@ JS::Value CanvasRenderingContext2DWrapper::stroke_rect(JS::Interpreter& interpre | |||
|     if (!impl) | ||||
|         return {}; | ||||
|     auto& arguments = interpreter.call_frame().arguments; | ||||
|     if (arguments.size() >= 4) | ||||
|         impl->stroke_rect(arguments[0].to_double(), arguments[1].to_double(), arguments[2].to_double(), arguments[3].to_double()); | ||||
|     if (arguments.size() >= 4) { | ||||
| 
 | ||||
|         auto x = arguments[0].to_double(interpreter); | ||||
|         if (interpreter.exception()) | ||||
|             return {}; | ||||
|         auto y = arguments[1].to_double(interpreter); | ||||
|         if (interpreter.exception()) | ||||
|             return {}; | ||||
|         auto width = arguments[2].to_double(interpreter); | ||||
|         if (interpreter.exception()) | ||||
|             return {}; | ||||
|         auto height = arguments[3].to_double(interpreter); | ||||
|         if (interpreter.exception()) | ||||
|             return {}; | ||||
|         impl->stroke_rect(x, y, width, height); | ||||
|     } | ||||
|     return JS::js_undefined(); | ||||
| } | ||||
| 
 | ||||
|  | @ -122,9 +149,12 @@ JS::Value CanvasRenderingContext2DWrapper::draw_image(JS::Interpreter& interpret | |||
|     if (StringView(image_argument->class_name()) != "HTMLImageElementWrapper") | ||||
|         return interpreter.throw_exception<JS::TypeError>(String::format("Image is not an HTMLImageElement, it's an %s", image_argument->class_name())); | ||||
| 
 | ||||
|     auto x = arguments[1].to_double(); | ||||
|     auto y = arguments[2].to_double(); | ||||
| 
 | ||||
|     auto x = arguments[1].to_double(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     auto y = arguments[2].to_double(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     impl->draw_image(static_cast<const HTMLImageElementWrapper&>(*image_argument).node(), x, y); | ||||
|     return JS::js_undefined(); | ||||
| } | ||||
|  | @ -135,8 +165,15 @@ JS::Value CanvasRenderingContext2DWrapper::scale(JS::Interpreter& interpreter) | |||
|     if (!impl) | ||||
|         return {}; | ||||
|     auto& arguments = interpreter.call_frame().arguments; | ||||
|     if (arguments.size() >= 2) | ||||
|         impl->scale(arguments[0].to_double(), arguments[1].to_double()); | ||||
|     if (arguments.size() >= 2) { | ||||
|         auto sx = arguments[0].to_double(interpreter); | ||||
|         if (interpreter.exception()) | ||||
|             return {}; | ||||
|         auto sy = arguments[1].to_double(interpreter); | ||||
|         if (interpreter.exception()) | ||||
|             return {}; | ||||
|         impl->scale(sx, sy); | ||||
|     } | ||||
|     return JS::js_undefined(); | ||||
| } | ||||
| 
 | ||||
|  | @ -146,8 +183,15 @@ JS::Value CanvasRenderingContext2DWrapper::translate(JS::Interpreter& interprete | |||
|     if (!impl) | ||||
|         return {}; | ||||
|     auto& arguments = interpreter.call_frame().arguments; | ||||
|     if (arguments.size() >= 2) | ||||
|         impl->translate(arguments[0].to_double(), arguments[1].to_double()); | ||||
|     if (arguments.size() >= 2) { | ||||
|         auto tx = arguments[0].to_double(interpreter); | ||||
|         if (interpreter.exception()) | ||||
|             return {}; | ||||
|         auto ty = arguments[1].to_double(interpreter); | ||||
|         if (interpreter.exception()) | ||||
|             return {}; | ||||
|         impl->translate(tx, ty); | ||||
|     } | ||||
|     return JS::js_undefined(); | ||||
| } | ||||
| 
 | ||||
|  | @ -161,12 +205,13 @@ JS::Value CanvasRenderingContext2DWrapper::fill_style_getter(JS::Interpreter& in | |||
| 
 | ||||
| void CanvasRenderingContext2DWrapper::fill_style_setter(JS::Interpreter& interpreter, JS::Value value) | ||||
| { | ||||
|     if (auto* impl = impl_from(interpreter)) { | ||||
|         auto string = value.to_string(interpreter); | ||||
|         if (interpreter.exception()) | ||||
|             return; | ||||
|         impl->set_fill_style(string); | ||||
|     } | ||||
|     auto* impl = impl_from(interpreter); | ||||
|     if (!impl) | ||||
|         return; | ||||
|     auto string = value.to_string(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return; | ||||
|     impl->set_fill_style(string); | ||||
| } | ||||
| 
 | ||||
| JS::Value CanvasRenderingContext2DWrapper::stroke_style_getter(JS::Interpreter& interpreter) | ||||
|  | @ -179,12 +224,13 @@ JS::Value CanvasRenderingContext2DWrapper::stroke_style_getter(JS::Interpreter& | |||
| 
 | ||||
| void CanvasRenderingContext2DWrapper::stroke_style_setter(JS::Interpreter& interpreter, JS::Value value) | ||||
| { | ||||
|     if (auto* impl = impl_from(interpreter)){ | ||||
|         auto string = value.to_string(interpreter); | ||||
|         if (interpreter.exception()) | ||||
|             return; | ||||
|         impl->set_stroke_style(string); | ||||
|     } | ||||
|     auto* impl = impl_from(interpreter); | ||||
|     if (!impl) | ||||
|         return; | ||||
|     auto string = value.to_string(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return; | ||||
|     impl->set_stroke_style(string); | ||||
| } | ||||
| 
 | ||||
| JS::Value CanvasRenderingContext2DWrapper::line_width_getter(JS::Interpreter& interpreter) | ||||
|  | @ -197,8 +243,13 @@ JS::Value CanvasRenderingContext2DWrapper::line_width_getter(JS::Interpreter& in | |||
| 
 | ||||
| void CanvasRenderingContext2DWrapper::line_width_setter(JS::Interpreter& interpreter, JS::Value value) | ||||
| { | ||||
|     if (auto* impl = impl_from(interpreter)) | ||||
|         impl->set_line_width(value.to_double()); | ||||
|     auto* impl = impl_from(interpreter); | ||||
|     if (!impl) | ||||
|         return; | ||||
|     auto line_width = value.to_double(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return; | ||||
|     impl->set_line_width(line_width); | ||||
| } | ||||
| 
 | ||||
| JS::Value CanvasRenderingContext2DWrapper::begin_path(JS::Interpreter& interpreter) | ||||
|  | @ -260,8 +311,12 @@ JS::Value CanvasRenderingContext2DWrapper::move_to(JS::Interpreter& interpreter) | |||
|     auto* impl = impl_from(interpreter); | ||||
|     if (!impl) | ||||
|         return {}; | ||||
|     double x = interpreter.argument(0).to_double(); | ||||
|     double y = interpreter.argument(1).to_double(); | ||||
|     auto x = interpreter.argument(0).to_double(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     auto y = interpreter.argument(1).to_double(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     impl->move_to(x, y); | ||||
|     return JS::js_undefined(); | ||||
| } | ||||
|  | @ -271,8 +326,12 @@ JS::Value CanvasRenderingContext2DWrapper::line_to(JS::Interpreter& interpreter) | |||
|     auto* impl = impl_from(interpreter); | ||||
|     if (!impl) | ||||
|         return {}; | ||||
|     double x = interpreter.argument(0).to_double(); | ||||
|     double y = interpreter.argument(1).to_double(); | ||||
|     auto x = interpreter.argument(0).to_double(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     auto y = interpreter.argument(1).to_double(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     impl->line_to(x, y); | ||||
|     return JS::js_undefined(); | ||||
| } | ||||
|  | @ -282,10 +341,18 @@ JS::Value CanvasRenderingContext2DWrapper::quadratic_curve_to(JS::Interpreter& i | |||
|     auto* impl = impl_from(interpreter); | ||||
|     if (!impl) | ||||
|         return {}; | ||||
|     double cx = interpreter.argument(0).to_double(); | ||||
|     double cy = interpreter.argument(1).to_double(); | ||||
|     double x = interpreter.argument(2).to_double(); | ||||
|     double y = interpreter.argument(3).to_double(); | ||||
|     auto cx = interpreter.argument(0).to_double(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     auto cy = interpreter.argument(1).to_double(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     auto x = interpreter.argument(2).to_double(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     auto y = interpreter.argument(3).to_double(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     impl->quadratic_curve_to(cx, cy, x, y); | ||||
|     return JS::js_undefined(); | ||||
| } | ||||
|  | @ -295,8 +362,12 @@ JS::Value CanvasRenderingContext2DWrapper::create_image_data(JS::Interpreter& in | |||
|     auto* impl = impl_from(interpreter); | ||||
|     if (!impl) | ||||
|         return {}; | ||||
|     i32 width = interpreter.argument(0).to_i32(); | ||||
|     i32 height = interpreter.argument(1).to_i32(); | ||||
|     auto width = interpreter.argument(0).to_i32(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     auto height = interpreter.argument(1).to_i32(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     auto image_data = impl->create_image_data(interpreter.global_object(), width, height); | ||||
|     return wrap(interpreter.heap(), *image_data); | ||||
| } | ||||
|  | @ -316,8 +387,12 @@ JS::Value CanvasRenderingContext2DWrapper::put_image_data(JS::Interpreter& inter | |||
|     } | ||||
| 
 | ||||
|     auto& image_data = static_cast<ImageDataWrapper*>(image_data_object)->impl(); | ||||
|     auto x = interpreter.argument(1).to_double(); | ||||
|     auto y = interpreter.argument(2).to_double(); | ||||
|     auto x = interpreter.argument(1).to_double(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     auto y = interpreter.argument(2).to_double(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     impl->put_image_data(image_data, x, y); | ||||
|     return JS::js_undefined(); | ||||
| } | ||||
|  |  | |||
|  | @ -134,7 +134,10 @@ JS::Value WindowObject::set_interval(JS::Interpreter& interpreter) | |||
|         return {}; | ||||
|     if (!callback_object->is_function()) | ||||
|         return interpreter.throw_exception<JS::TypeError>("Not a function"); | ||||
|     impl->set_interval(*static_cast<JS::Function*>(callback_object), arguments[1].to_i32()); | ||||
|     auto interval = arguments[1].to_i32(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     impl->set_interval(*static_cast<JS::Function*>(callback_object), interval); | ||||
|     return JS::js_undefined(); | ||||
| } | ||||
| 
 | ||||
|  | @ -153,8 +156,11 @@ JS::Value WindowObject::set_timeout(JS::Interpreter& interpreter) | |||
|         return interpreter.throw_exception<JS::TypeError>("Not a function"); | ||||
| 
 | ||||
|     i32 interval = 0; | ||||
|     if (interpreter.argument_count() >= 2) | ||||
|         interval = arguments[1].to_i32(); | ||||
|     if (interpreter.argument_count() >= 2) { | ||||
|         interval = arguments[1].to_i32(interpreter); | ||||
|         if (interpreter.exception()) | ||||
|             return {}; | ||||
|     } | ||||
| 
 | ||||
|     impl->set_timeout(*static_cast<JS::Function*>(callback_object), interval); | ||||
|     return JS::js_undefined(); | ||||
|  | @ -184,7 +190,10 @@ JS::Value WindowObject::cancel_animation_frame(JS::Interpreter& interpreter) | |||
|     auto& arguments = interpreter.call_frame().arguments; | ||||
|     if (arguments.size() < 1) | ||||
|         return {}; | ||||
|     impl->cancel_animation_frame(arguments[0].to_i32()); | ||||
|     auto id = arguments[0].to_i32(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     impl->cancel_animation_frame(id); | ||||
|     return JS::js_undefined(); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -328,9 +328,10 @@ JS::Value ReplObject::exit_interpreter(JS::Interpreter& interpreter) | |||
| { | ||||
|     if (!interpreter.argument_count()) | ||||
|         exit(0); | ||||
|     int exit_code = interpreter.argument(0).to_number().as_double(); | ||||
|     exit(exit_code); | ||||
|     return JS::js_undefined(); | ||||
|     auto exit_code = interpreter.argument(0).to_number(interpreter); | ||||
|     if (interpreter.exception()) | ||||
|         return {}; | ||||
|     exit(exit_code.as_double()); | ||||
| } | ||||
| 
 | ||||
| JS::Value ReplObject::repl_help(JS::Interpreter&) | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Linus Groh
						Linus Groh