for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parent) {
// optional optimization to bypass getProperty in cases when we only need to know if the property exists
if (JSObjectHasPropertyCallback hasProperty = jsClass->callbacks.hasProperty) {
- if (hasProperty(context, thisRef, propertyNameRef, toRef(exec->exceptionSlot()))) {
+ if (hasProperty(context, thisRef, propertyNameRef)) {
slot.setCustom(this, callbackGetter);
return true;
}
JSObject::getPropertyList(propertyList, recursive);
}
-bool JSCallbackObject::toBoolean(ExecState* exec) const
-{
- JSContextRef context = toRef(exec);
- JSObjectRef thisRef = toRef(this);
-
- for (JSClassRef jsClass = m_class; jsClass; jsClass = jsClass->parent)
- if (JSObjectConvertToTypeCallback convertToType = jsClass->callbacks.convertToType)
- if (JSValueRef value = convertToType(context, thisRef, kJSTypeBoolean, toRef(exec->exceptionSlot())))
- return toJS(value)->getBoolean();
-
- return JSObject::toBoolean(exec);
-}
-
double JSCallbackObject::toNumber(ExecState* exec) const
{
JSContextRef context = toRef(exec);
virtual void getPropertyList(ReferenceList& propertyList, bool recursive);
- virtual bool toBoolean(ExecState*) const;
virtual double toNumber(ExecState*) const;
virtual UString toString(ExecState*) const;
return jsObject->hasProperty(exec, Identifier(nameRep));
}
-JSValueRef JSObjectGetProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName)
+JSValueRef JSObjectGetProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception)
{
JSLock lock;
ExecState* exec = toJS(context);
JSValue* jsValue = jsObject->get(exec, Identifier(nameRep));
if (jsValue->isUndefined())
- return 0;
+ jsValue = 0;
+ if (exec->hadException()) {
+ if (exception)
+ *exception = toRef(exec->exception());
+ exec->clearException();
+ }
return toRef(jsValue);
}
-void JSObjectSetProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSPropertyAttributes attributes)
+void JSObjectSetProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSPropertyAttributes attributes, JSValueRef* exception)
{
JSLock lock;
ExecState* exec = toJS(context);
JSValue* jsValue = toJS(value);
jsObject->put(exec, Identifier(nameRep), jsValue, attributes);
+ if (exec->hadException()) {
+ if (exception)
+ *exception = toRef(exec->exception());
+ exec->clearException();
+ }
}
JSValueRef JSObjectGetPropertyAtIndex(JSContextRef context, JSObjectRef object, unsigned propertyIndex)
jsObject->put(exec, propertyIndex, jsValue);
}
-bool JSObjectDeleteProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName)
+bool JSObjectDeleteProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception)
{
JSLock lock;
ExecState* exec = toJS(context);
JSObject* jsObject = toJS(object);
UString::Rep* nameRep = toJS(propertyName);
- return jsObject->deleteProperty(exec, Identifier(nameRep));
+ bool result = jsObject->deleteProperty(exec, Identifier(nameRep));
+ if (exec->hadException()) {
+ if (exception)
+ *exception = toRef(exec->exception());
+ exec->clearException();
+ }
+ return result;
}
void* JSObjectGetPrivate(JSObjectRef object)
@param context The current execution context.
@param object The JSObject to search for the property.
@param propertyName A JSString containing the name of the property look up.
-@param exception A pointer to a JSValueRef in which to return an exception, if any.
@result true if object has the property, otherwise false.
@discussion If you named your function HasProperty, you would declare it like this:
-bool HasProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception);
+bool HasProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName);
If this function returns false, the hasProperty request forwards to object's static property table, then its parent class chain (which includes the default object class), then its prototype chain.
-This callback enables optimization in cases where only a property's existence needs to be known, not its value, and computing its value would be expensive. If this callback is NULL, the getProperty callback will be used to service hasProperty requests.
+This callback enables optimization in cases where only a property's existence needs to be known, not its value, and computing its value would be expensive.
+
+If this callback is NULL, the getProperty callback will be used to service hasProperty requests.
*/
typedef bool
-(*JSObjectHasPropertyCallback) (JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception);
+(*JSObjectHasPropertyCallback) (JSContextRef context, JSObjectRef object, JSStringRef propertyName);
/*!
@typedef JSObjectGetPropertyCallback
/*!
@typedef JSObjectHasInstanceCallback
-@abstract The callback invoked when an object is used in an 'instanceof' expression.
+@abstract hasInstance The callback invoked when an object is used as the target of an 'instanceof' expression.
@param context The current execution context.
-@param constructor The JSObject receiving the hasInstance request
+@param constructor The JSObject that is the target of the 'instanceof' expression.
@param possibleInstance The JSValue being tested to determine if it is an instance of constructor.
@param exception A pointer to a JSValueRef in which to return an exception, if any.
-@result true if possibleInstance is an instance of constructor, otherwise false
+@result true if possibleInstance is an instance of constructor, otherwise false.
@discussion If you named your function HasInstance, you would declare it like this:
bool HasInstance(JSContextRef context, JSObjectRef constructor, JSValueRef possibleInstance, JSValueRef* exception);
-If your callback were invoked by the JavaScript expression 'someValue instanceof myObject', constructor would be set to myObject and possibleInstance would be set to someValue..
+If your callback were invoked by the JavaScript expression 'someValue instanceof myObject', constructor would be set to myObject and possibleInstance would be set to someValue.
-If this callback is NULL, using your object in an 'instanceof' will always return false.
+If this callback is NULL, 'instanceof' expressions that target your object will return false.
Standard JavaScript practice calls for objects that implement the callAsConstructor callback to implement the hasInstance callback as well.
*/
JSValueRef ConvertToType(JSContextRef context, JSObjectRef object, JSType type, JSValueRef* exception);
If this function returns false, the conversion request forwards to object's parent class chain (which includes the default object class).
+
+This function is only invoked when converting an object to number or string. An object converted to boolean is 'true.' An object converted to object is itself.
*/
typedef JSValueRef
(*JSObjectConvertToTypeCallback) (JSContextRef context, JSObjectRef object, JSType type, JSValueRef* exception);
@field deleteProperty The callback invoked when deleting a given property.
@field addPropertiesToList The callback invoked when adding an object's properties to a property list.
@field callAsFunction The callback invoked when an object is called as a function.
-@field hasInstance The callback invoked when an object is used in an 'instanceof' expression.
+@field hasInstance The callback invoked when an object is used as the target of an 'instanceof' expression.
@field callAsConstructor The callback invoked when an object is used as a constructor in a 'new' expression.
@field convertToType The callback invoked when converting an object to a particular JavaScript type.
*/
@param context The execution context to use.
@param object The JSObject whose property you want to get.
@param propertyName A JSString containing the property's name.
+@param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception.
@result The property's value if object has the property, otherwise NULL.
*/
-JSValueRef JSObjectGetProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName);
+JSValueRef JSObjectGetProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception);
/*!
@function
@param object The JSObject whose property you want to set.
@param propertyName A JSString containing the property's name.
@param value A JSValue to use as the property's value.
+@param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception.
@param attributes A logically ORed set of JSPropertyAttributes to give to the property.
*/
-void JSObjectSetProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSPropertyAttributes attributes);
+void JSObjectSetProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSPropertyAttributes attributes, JSValueRef* exception);
/*!
@function
@param context The execution context to use.
@param object The JSObject whose property you want to delete.
@param propertyName A JSString containing the property's name.
+@param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception.
@result true if the delete operation succeeds, otherwise false (for example, if the property has the kJSPropertyAttributeDontDelete attribute set).
*/
-bool JSObjectDeleteProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName);
+bool JSObjectDeleteProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception);
/*!
@function
@param thisObject The object to use as "this," or NULL to use the global object as "this."
@param argc An integer count of the number of arguments in argv.
@param argv A JSValue array of the arguments to pass to the function.
-@param exception A pointer to a JSValueRef in which to store an uncaught exception, if any. Pass NULL if you do not care to store an uncaught exception.
-@result The JSValue that results from calling object as a function, or NULL if an uncaught exception is thrown or object is not a function.
+@param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception.
+@result The JSValue that results from calling object as a function, or NULL if an exception is thrown or object is not a function.
*/
JSValueRef JSObjectCallAsFunction(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argc, JSValueRef argv[], JSValueRef* exception);
/*!
@param object The JSObject to call as a constructor.
@param argc An integer count of the number of arguments in argv.
@param argv A JSValue array of the arguments to pass to the function.
-@param exception A pointer to a JSValueRef in which to store an uncaught exception, if any. Pass NULL if you do not care to store an uncaught exception.
-@result The JSObject that results from calling object as a constructor, or NULL if an uncaught exception is thrown or object is not a constructor.
+@param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception.
+@result The JSObject that results from calling object as a constructor, or NULL if an exception is thrown or object is not a constructor.
*/
JSObjectRef JSObjectCallAsConstructor(JSContextRef context, JSObjectRef object, size_t argc, JSValueRef argv[], JSValueRef* exception);
return result;
}
-bool JSValueIsInstanceOfConstructor(JSContextRef context, JSValueRef value, JSObjectRef constructor)
+bool JSValueIsInstanceOfConstructor(JSContextRef context, JSValueRef value, JSObjectRef constructor, JSValueRef* exception)
{
ExecState* exec = toJS(context);
JSValue* jsValue = toJS(value);
JSObject* jsConstructor = toJS(constructor);
if (!jsConstructor->implementsHasInstance())
return false;
- bool result = jsConstructor->hasInstance(exec, jsValue);
- if (exec->hadException())
+ bool result = jsConstructor->hasInstance(exec, jsValue); // false if an exception is thrown
+ if (exec->hadException()) {
+ if (exception)
+ *exception = toRef(exec->exception());
exec->clearException();
+ }
return result;
}
return toRef(jsNumber(value));
}
-bool JSValueToBoolean(JSContextRef context, JSValueRef value, JSValueRef* exception)
+bool JSValueToBoolean(JSContextRef context, JSValueRef value)
{
- JSLock lock;
ExecState* exec = toJS(context);
JSValue* jsValue = toJS(value);
-
- bool boolean = jsValue->toBoolean(exec);
- if (exec->hadException()) {
- if (exception)
- *exception = toRef(exec->exception());
- exec->clearException();
- boolean = false;
- }
- return boolean;
+ return jsValue->toBoolean(exec);
}
double JSValueToNumber(JSContextRef context, JSValueRef value, JSValueRef* exception)
/*!
@function
-@abstract Tests whether a JavaScript value is an object constructed by
- a given constructor, as compared by the JS instanceof operator.
-@param context The execution context to use.
-@param value The JSValue to test.
-@param object The constructor to test against.
-@result true if value is an object constructed by constructor, as compared
- by the JS instanceof operator, otherwise false.
+@abstract Tests whether a JavaScript value is an object constructed by a given constructor, as compared by the JS instanceof operator.
+@param context The execution context to use.
+@param value The JSValue to test.
+@param object The constructor to test against.
+@param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception.
+@result true if value is an object constructed by constructor, as compared by the JS instanceof operator, otherwise false.
*/
-bool JSValueIsInstanceOfConstructor(JSContextRef context, JSValueRef value, JSObjectRef constructor);
+bool JSValueIsInstanceOfConstructor(JSContextRef context, JSValueRef value, JSObjectRef constructor, JSValueRef* exception);
// Creating values
@abstract Converts a JavaScript value to boolean and returns the resulting boolean.
@param context The execution context to use.
@param value The JSValue to convert.
-@param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception.
-@result The boolean result of conversion, or false if an exception is thrown.
+@result The boolean result of conversion.
*/
-bool JSValueToBoolean(JSContextRef context, JSValueRef value, JSValueRef* exception);
+bool JSValueToBoolean(JSContextRef context, JSValueRef value);
/*!
@function
@param context The execution context to use.
@param value The JSValue to convert.
@param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception.
-@result The JSObject result of conversion, or NULL if conversion fails.
+@result The JSObject result of conversion, or NULL if an exception is thrown.
*/
JSObjectRef JSValueToObject(JSContextRef context, JSValueRef value, JSValueRef* exception);
JSObjectRef globalObject = JSContextGetGlobalObject(context);
JSStringRef printIString = JSStringCreateWithUTF8CString("print");
- JSObjectSetProperty(context, globalObject, printIString, JSObjectMakeFunction(context, print), kJSPropertyAttributeNone);
+ JSObjectSetProperty(context, globalObject, printIString, JSObjectMakeFunction(context, print), kJSPropertyAttributeNone, NULL);
JSStringRelease(printIString);
JSStringRef node = JSStringCreateWithUTF8CString("Node");
- JSObjectSetProperty(context, globalObject, node, JSObjectMakeConstructor(context, JSNode_construct), kJSPropertyAttributeNone);
+ JSObjectSetProperty(context, globalObject, node, JSObjectMakeConstructor(context, JSNode_construct), kJSPropertyAttributeNone, NULL);
JSStringRelease(node);
char* scriptUTF8 = createStringWithContentsOfFile("minidom.js");
static void assertEqualsAsBoolean(JSValueRef value, bool expectedValue)
{
- if (JSValueToBoolean(context, value, NULL) != expectedValue)
+ if (JSValueToBoolean(context, value) != expectedValue)
fprintf(stderr, "assertEqualsAsBoolean failed: %p, %d\n", value, expectedValue);
}
didInitialize = true;
}
-static bool MyObject_hasProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception)
+static bool MyObject_hasProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName)
{
UNUSED_PARAM(context);
UNUSED_PARAM(object);
if (JSStringIsEqualToUTF8CString(propertyName, "cantDelete"))
return true;
+ if (JSStringIsEqualToUTF8CString(propertyName, "throwOnDelete")) {
+ *exception = JSValueMakeNumber(2);
+ return false;
+ }
+
return false;
}
UNUSED_PARAM(context);
JSStringRef numberString = JSStringCreateWithUTF8CString("Number");
- JSObjectRef numberConstructor = JSValueToObject(context, JSObjectGetProperty(context, JSContextGetGlobalObject(context), numberString), NULL);
+ JSObjectRef numberConstructor = JSValueToObject(context, JSObjectGetProperty(context, JSContextGetGlobalObject(context), numberString, NULL), NULL);
JSStringRelease(numberString);
- return JSValueIsInstanceOfConstructor(context, possibleValue, numberConstructor);
+ return JSValueIsInstanceOfConstructor(context, possibleValue, numberConstructor, NULL);
}
static JSValueRef MyObject_convertToType(JSContextRef context, JSObjectRef object, JSType type, JSValueRef* exception)
UNUSED_PARAM(object);
switch (type) {
- case kJSTypeBoolean:
- *exception = JSValueMakeNumber(2);
- return NULL;
case kJSTypeNumber:
return JSValueMakeNumber(1);
default:
JSObjectRef result = JSObjectMake(context, NULL, 0);
if (argc > 0) {
JSStringRef value = JSStringCreateWithUTF8CString("value");
- JSObjectSetProperty(context, result, value, argv[0], kJSPropertyAttributeNone);
+ JSObjectSetProperty(context, result, value, argv[0], kJSPropertyAttributeNone, NULL);
JSStringRelease(value);
}
JSObjectRef myObject = JSObjectMake(context, MyObject_class(context), NULL);
assert(didInitialize);
JSStringRef myObjectIString = JSStringCreateWithUTF8CString("MyObject");
- JSObjectSetProperty(context, globalObject, myObjectIString, myObject, kJSPropertyAttributeNone);
+ JSObjectSetProperty(context, globalObject, myObjectIString, myObject, kJSPropertyAttributeNone, NULL);
JSStringRelease(myObjectIString);
JSValueRef exception;
assert(!JSValueToStringCopy(context, jsObjectNoProto, &exception));
assert(exception);
- exception = NULL;
- assert(!JSValueToBoolean(context, myObject, &exception));
- assert(exception);
+ assert(JSValueToBoolean(context, myObject));
exception = NULL;
assert(!JSValueIsEqual(context, jsObjectNoProto, JSValueMakeNumber(1), &exception));
assert(JSValueIsObject(exception));
JSStringRef array = JSStringCreateWithUTF8CString("Array");
- v = JSObjectGetProperty(context, globalObject, array);
+ v = JSObjectGetProperty(context, globalObject, array, NULL);
assert(v);
JSObjectRef arrayConstructor = JSValueToObject(context, v, NULL);
JSStringRelease(array);
result = JSObjectCallAsConstructor(context, arrayConstructor, 0, NULL, NULL);
assert(result);
- assert(JSValueIsInstanceOfConstructor(context, result, arrayConstructor));
- assert(!JSValueIsInstanceOfConstructor(context, JSValueMakeNull(), arrayConstructor));
+ assert(JSValueIsInstanceOfConstructor(context, result, arrayConstructor, NULL));
+ assert(!JSValueIsInstanceOfConstructor(context, JSValueMakeNull(), arrayConstructor, NULL));
JSStringRef functionBody;
JSStringRef line = JSStringCreateWithUTF8CString("line");
assert(!JSObjectMakeFunctionWithBody(context, functionBody, NULL, 1, &exception));
assert(JSValueIsObject(exception));
- v = JSObjectGetProperty(context, JSValueToObject(context, exception, NULL), line);
+ v = JSObjectGetProperty(context, JSValueToObject(context, exception, NULL), line, NULL);
assert(v);
assertEqualsAsNumber(v, 2); // FIXME: Lexer::setCode bumps startingLineNumber by 1 -- we need to change internal callers so that it doesn't have to (saying '0' to mean '1' in the API would be really confusing -- it's really confusing internally, in fact)
JSStringRelease(functionBody);
JSStringRef print = JSStringCreateWithUTF8CString("print");
JSObjectRef printFunction = JSObjectMakeFunction(context, print_callAsFunction);
- JSObjectSetProperty(context, globalObject, print, printFunction, kJSPropertyAttributeNone);
+ JSObjectSetProperty(context, globalObject, print, printFunction, kJSPropertyAttributeNone, NULL);
JSStringRelease(print);
assert(JSObjectSetPrivate(printFunction, (void*)1));
JSStringRef myConstructorIString = JSStringCreateWithUTF8CString("MyConstructor");
JSObjectRef myConstructor = JSObjectMakeConstructor(context, myConstructor_callAsConstructor);
- JSObjectSetProperty(context, globalObject, myConstructorIString, myConstructor, kJSPropertyAttributeNone);
+ JSObjectSetProperty(context, globalObject, myConstructorIString, myConstructor, kJSPropertyAttributeNone, NULL);
JSStringRelease(myConstructorIString);
assert(JSObjectSetPrivate(myConstructor, (void*)1));
assert(JSObjectGetPrivate(myConstructor) == (void*)1);
o = JSObjectMake(context, NULL, NULL);
- JSObjectSetProperty(context, o, jsOneIString, JSValueMakeNumber(1), kJSPropertyAttributeNone);
- JSObjectSetProperty(context, o, jsCFIString, JSValueMakeNumber(1), kJSPropertyAttributeDontEnum);
+ JSObjectSetProperty(context, o, jsOneIString, JSValueMakeNumber(1), kJSPropertyAttributeNone, NULL);
+ JSObjectSetProperty(context, o, jsCFIString, JSValueMakeNumber(1), kJSPropertyAttributeDontEnum, NULL);
JSPropertyEnumeratorRef enumerator = JSObjectCreatePropertyEnumerator(o);
int count = 0;
while (JSPropertyEnumeratorGetNextName(enumerator))
MyObject.cantDelete = 1;
delete MyObject.cantDelete;
shouldBe("MyObject.cantDelete", 1);
+shouldBe("delete MyObject.throwOnDelete", 2); // deleteProperty -- should throw 2
MyObject.cantSet = 1;
shouldBe("MyObject.cantSet", undefined);
shouldBe("MyObject(0)", 1);
shouldBe("MyObject()", undefined);
shouldBe("typeof new MyObject()", "object");
-shouldBe("MyObject ? 1 : 0", 2); // toBoolean -- should throw 2
+shouldBe("MyObject ? 1 : 0", true); // toBoolean
shouldBe("+MyObject", 1); // toNumber
shouldBe("(MyObject.toString())", "[object CallbackObject]"); // toString
shouldBe("MyObject - 0", NaN); // toPrimitive
+2006-07-14 Geoffrey Garen <ggaren@apple.com>
+
+ Reviewed by Maciej.
+
+ - Finalized exception handling in the API.
+
+ setProperty can throw because it throws for built-in arrays. getProperty
+ and deleteProperty can throw because setProperty can throw and we want
+ to be consistent, and also because they seem like "actions." callAsFunction,
+ callAsConstructor, and hasInstance can throw, because they caan throw for
+ all built-ins.
+
+ toBoolean can't throw because it's defined that way in the spec.
+
+ - Documented that toBoolean and toObject can't be overridden by custom
+ objects because they're defined that way in the spec.
+
=== Safari-521.17 ===
2006-07-14 Geoffrey Garen <ggaren@apple.com>
return _instance->getValueOfUndefinedField(exec, _item, hint);
}
-bool ObjcFallbackObjectImp::toBoolean(ExecState*) const
+bool ObjcFallbackObjectImp::toBoolean(ExecState *) const
{
id targetObject = _instance->getObject();