Avoid throwing exceptions in non-error property reads

These make debugging annoying (and probably slow things down, but that's a
secondary benefit).
This commit is contained in:
Thomas Goyne 2017-09-05 15:33:28 -07:00
parent 18dea2bc6c
commit 2a28a29341
2 changed files with 48 additions and 27 deletions

View File

@ -106,14 +106,12 @@ typename T::Object RealmObjectClass<T>::create_instance(ContextType ctx, realm::
template<typename T> template<typename T>
void RealmObjectClass<T>::get_property(ContextType ctx, ObjectType object, const String &property, ReturnValue &return_value) { void RealmObjectClass<T>::get_property(ContextType ctx, ObjectType object, const String &property, ReturnValue &return_value) {
try { auto realm_object = get_internal<T, RealmObjectClass<T>>(object);
auto realm_object = get_internal<T, RealmObjectClass<T>>(object); std::string name = property;
if (realm_object->get_object_schema().property_for_name(name)) {
NativeAccessor<T> accessor(ctx, realm_object->realm(), realm_object->get_object_schema()); NativeAccessor<T> accessor(ctx, realm_object->realm(), realm_object->get_object_schema());
std::string name = property;
auto result = realm_object->template get_property_value<ValueType>(accessor, name); auto result = realm_object->template get_property_value<ValueType>(accessor, name);
return_value.set(result); return_value.set(result);
} catch (InvalidPropertyException &ex) {
// getters for nonexistent properties in JS should always return undefined
} }
} }

View File

@ -300,20 +300,43 @@ inline void ObjectWrap<ClassType>::get_property_names(JSContextRef ctx, JSObject
} }
} }
static inline bool try_get_int(JSStringRef property, int64_t& value) {
value = 0;
auto str = JSStringGetCharactersPtr(property);
auto end = str + JSStringGetLength(property);
while (str != end && iswspace(*str)) {
++str;
}
bool negative = false;
if (str != end && *str == '-') {
negative = true;
++str;
}
while (str != end && *str >= '0' && *str <= '9') {
if (int_multiply_with_overflow_detect(value, 10)) {
return false;
}
value += *str - '0';
++str;
}
if (negative) {
value *= -1;
}
return str == end;
}
template<typename ClassType> template<typename ClassType>
inline JSValueRef ObjectWrap<ClassType>::get_property(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef* exception) { inline JSValueRef ObjectWrap<ClassType>::get_property(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef* exception) {
if (auto index_getter = s_class.index_accessor.getter) { if (auto index_getter = s_class.index_accessor.getter) {
try { int64_t num;
uint32_t index = validated_positive_index(jsc::String(property)); if (try_get_int(property, num)) {
uint32_t index;
if (num < 0 || util::int_cast_with_overflow_detect(num, index)) {
// Out-of-bounds index getters should just return undefined in JS.
return Value::from_undefined(ctx);
}
return index_getter(ctx, object, index, exception); return index_getter(ctx, object, index, exception);
} }
catch (std::out_of_range &) {
// Out-of-bounds index getters should just return undefined in JS.
return Value::from_undefined(ctx);
}
catch (std::invalid_argument &) {
// Property is not a number.
}
} }
if (auto string_getter = s_class.string_accessor.getter) { if (auto string_getter = s_class.string_accessor.getter) {
return string_getter(ctx, object, property, exception); return string_getter(ctx, object, property, exception);
@ -326,24 +349,24 @@ inline bool ObjectWrap<ClassType>::set_property(JSContextRef ctx, JSObjectRef ob
auto index_setter = s_class.index_accessor.setter; auto index_setter = s_class.index_accessor.setter;
if (index_setter || s_class.index_accessor.getter) { if (index_setter || s_class.index_accessor.getter) {
try { int64_t num;
uint32_t index = validated_positive_index(jsc::String(property)); if (try_get_int(property, num)) {
if (num < 0) {
*exception = Exception::value(ctx, util::format("Index %1 cannot be less than zero.", num));
return false;
}
int32_t index;
if (util::int_cast_with_overflow_detect(num, index)) {
*exception = Exception::value(ctx, util::format("Index %1 cannot be greater than %2.",
num, std::numeric_limits<uint32_t>::max()));
return false;
}
if (index_setter) { if (index_setter) {
return index_setter(ctx, object, index, value, exception); return index_setter(ctx, object, index, value, exception);
} }
else { *exception = Exception::value(ctx, util::format("Cannot assign to read only index %1", index));
*exception = Exception::value(ctx, std::string("Cannot assign to read only index ") + util::to_string(index));
return false;
}
}
catch (std::out_of_range &e) {
*exception = Exception::value(ctx, e);
return false; return false;
} }
catch (std::invalid_argument &) {
// Property is not a number.
}
} }
if (auto string_setter = s_class.string_accessor.setter) { if (auto string_setter = s_class.string_accessor.setter) {
return string_setter(ctx, object, property, value, exception); return string_setter(ctx, object, property, value, exception);