/*! @typedef JSPropertyNameArrayRef An array of JavaScript property names. */
typedef struct OpaqueJSPropertyNameArray* JSPropertyNameArrayRef;
-/*! @typedef JSPropertyNameAccumulatorRef A data type used to collect a JavaScript object's property names. */
+/*! @typedef JSPropertyNameAccumulatorRef An ordered set used to collect the names of a JavaScript object's properties. */
typedef struct OpaqueJSPropertyNameAccumulator* JSPropertyNameAccumulatorRef;
const ClassInfo JSCallbackConstructor::info = { "CallbackConstructor", 0, 0, 0 };
-JSCallbackConstructor::JSCallbackConstructor(ExecState* exec, JSObjectCallAsConstructorCallback callback)
+JSCallbackConstructor::JSCallbackConstructor(ExecState* exec, JSClassRef jsClass, JSObjectCallAsConstructorCallback callback)
: JSObject(exec->lexicalInterpreter()->builtinObjectPrototype())
+ , m_class(jsClass)
, m_callback(callback)
{
+ if (m_class)
+ JSClassRetain(jsClass);
+}
+
+JSCallbackConstructor::~JSCallbackConstructor()
+{
+ if (m_class)
+ JSClassRelease(m_class);
}
bool JSCallbackConstructor::implementsHasInstance() const
JSObject* JSCallbackConstructor::construct(ExecState* exec, const List &args)
{
- JSContextRef execRef = toRef(exec);
+ JSContextRef ctx = toRef(exec);
JSObjectRef thisRef = toRef(this);
- size_t argumentCount = args.size();
- JSValueRef arguments[argumentCount];
- for (size_t i = 0; i < argumentCount; i++)
- arguments[i] = toRef(args[i]);
- return toJS(m_callback(execRef, thisRef, argumentCount, arguments, toRef(exec->exceptionSlot())));
+ if (m_callback) {
+ size_t argumentCount = args.size();
+ JSValueRef arguments[argumentCount];
+ for (size_t i = 0; i < argumentCount; i++)
+ arguments[i] = toRef(args[i]);
+ return toJS(m_callback(ctx, thisRef, argumentCount, arguments, toRef(exec->exceptionSlot())));
+ }
+
+ return toJS(JSObjectMake(ctx, m_class, 0));
}
} // namespace KJS
class JSCallbackConstructor : public JSObject
{
public:
- JSCallbackConstructor(ExecState* exec, JSObjectCallAsConstructorCallback callback);
+ JSCallbackConstructor(ExecState* exec, JSClassRef jsClass, JSObjectCallAsConstructorCallback callback);
+ virtual ~JSCallbackConstructor();
virtual bool implementsHasInstance() const;
private:
JSCallbackConstructor(); // prevent default construction
JSCallbackConstructor(const JSCallbackConstructor&);
-
+
+ JSClassRef m_class;
JSObjectCallAsConstructorCallback m_callback;
};
if (StaticValueEntry* entry = staticValues->get(propertyName.ustring().rep())) {
if (entry->attributes & kJSPropertyAttributeReadOnly)
return;
- if (JSObjectSetPropertyCallback setProperty = entry->setProperty)
- setProperty(ctx, thisRef, propertyNameRef, valueRef, toRef(exec->exceptionSlot()));
- else
+ if (JSObjectSetPropertyCallback setProperty = entry->setProperty) {
+ if (setProperty(ctx, thisRef, propertyNameRef, valueRef, toRef(exec->exceptionSlot())))
+ return;
+ } else
throwError(exec, ReferenceError, "Writable static value property defined with NULL setProperty callback.");
}
}
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include "APICast.h"
+#include "JSCallbackObject.h"
#include "JSClassRef.h"
#include "JSObjectRef.h"
#include "identifier.h"
using namespace KJS;
-const JSClassDefinition kJSClassDefinitionNull = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+const JSClassDefinition kJSClassDefinitionEmpty = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
-OpaqueJSClass::OpaqueJSClass(JSClassDefinition* definition)
+OpaqueJSClass::OpaqueJSClass(const JSClassDefinition* definition, OpaqueJSClass* protoClass)
: refCount(0)
, className(definition->className)
, parentClass(definition->parentClass)
+ , prototypeClass(0)
, staticValues(0)
, staticFunctions(0)
, initialize(definition->initialize)
++staticFunction;
}
}
+
+ if (protoClass)
+ prototypeClass = JSClassRetain(protoClass);
}
OpaqueJSClass::~OpaqueJSClass()
deleteAllValues(*staticFunctions);
delete staticFunctions;
}
+
+ if (prototypeClass)
+ JSClassRelease(prototypeClass);
+}
+
+JSClassRef OpaqueJSClass::createNoPrototype(const JSClassDefinition* definition)
+{
+ return new OpaqueJSClass(definition, 0);
+}
+
+void clearReferenceToPrototype(JSObjectRef prototype)
+{
+ OpaqueJSClass* jsClass = static_cast<OpaqueJSClass*>(JSObjectGetPrivate(prototype));
+ ASSERT(jsClass);
+ jsClass->cachedPrototype = 0;
+}
+
+JSClassRef OpaqueJSClass::create(const JSClassDefinition* definition)
+{
+ if (JSStaticFunction* staticFunctions = definition->staticFunctions) {
+ // copy functions into a prototype class
+ JSClassDefinition protoDefinition = kJSClassDefinitionEmpty;
+ protoDefinition.staticFunctions = staticFunctions;
+ protoDefinition.finalize = clearReferenceToPrototype;
+ OpaqueJSClass* protoClass = new OpaqueJSClass(&protoDefinition, 0);
+
+ // remove functions from the original definition
+ JSClassDefinition objectDefinition = *definition;
+ objectDefinition.staticFunctions = 0;
+ return new OpaqueJSClass(&objectDefinition, protoClass);
+ }
+
+ return new OpaqueJSClass(definition, 0);
+}
+
+/*!
+// Doc here in case we make this public. (Hopefully we won't.)
+@function
+ @abstract Returns the prototype that will be used when constructing an object with a given class.
+ @param ctx The execution context to use.
+ @param jsClass A JSClass whose prototype you want to get.
+ @result The JSObject prototype that was automatically generated for jsClass, or NULL if no prototype was automatically generated. This is the prototype that will be used when constructing an object using jsClass.
+*/
+JSObject* OpaqueJSClass::prototype(JSContextRef ctx)
+{
+ /* Class (C++) and prototype (JS) inheritance are parallel, so:
+ * (C++) | (JS)
+ * ParentClass | ParentClassPrototype
+ * ^ | ^
+ * | | |
+ * DerivedClass | DerivedClassPrototype
+ */
+
+ if (!prototypeClass)
+ return 0;
+
+ ExecState* exec = toJS(ctx);
+
+ if (!cachedPrototype) {
+ // Recursive, but should be good enough for our purposes
+ JSObject* parentPrototype = 0;
+ if (parentClass)
+ parentPrototype = parentClass->prototype(ctx); // can be null
+ if (!parentPrototype)
+ parentPrototype = exec->dynamicInterpreter()->builtinObjectPrototype();
+ cachedPrototype = new JSCallbackObject(exec, prototypeClass, parentPrototype, this); // set ourself as the object's private data, so it can clear our reference on destruction
+ }
+ return cachedPrototype;
}
#define JSClassRef_h
#include "JSObjectRef.h"
-#include "HashMap.h"
-#include "ustring.h"
+
+#include <kjs/object.h>
+#include <kjs/protect.h>
+#include <kjs/ustring.h>
+#include <wtf/HashMap.h>
struct StaticValueEntry {
StaticValueEntry(JSObjectGetPropertyCallback _getProperty, JSObjectSetPropertyCallback _setProperty, JSPropertyAttributes _attributes)
};
struct OpaqueJSClass {
- OpaqueJSClass(JSClassDefinition*);
+ static OpaqueJSClass* create(const JSClassDefinition*);
+ static OpaqueJSClass* createNoPrototype(const JSClassDefinition*);
~OpaqueJSClass();
+ KJS::JSObject* prototype(JSContextRef ctx);
+
typedef HashMap<RefPtr<KJS::UString::Rep>, StaticValueEntry*> StaticValuesTable;
typedef HashMap<RefPtr<KJS::UString::Rep>, StaticFunctionEntry*> StaticFunctionsTable;
KJS::UString className;
OpaqueJSClass* parentClass;
-
+ OpaqueJSClass* prototypeClass;
+
StaticValuesTable* staticValues;
StaticFunctionsTable* staticFunctions;
JSObjectCallAsConstructorCallback callAsConstructor;
JSObjectHasInstanceCallback hasInstance;
JSObjectConvertToTypeCallback convertToType;
+
+private:
+ OpaqueJSClass();
+ OpaqueJSClass(const OpaqueJSClass&);
+ OpaqueJSClass(const JSClassDefinition*, OpaqueJSClass* protoClass);
+
+ friend void clearReferenceToPrototype(JSObjectRef prototype);
+ KJS::JSObject* cachedPrototype;
};
#endif // JSClassRef_h
#include "NodeList.h"
#include "UnusedParam.h"
-static JSClassRef JSNode_class(JSContextRef context);
-
-static JSValueRef JSNodePrototype_appendChild(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+static JSValueRef JSNode_appendChild(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
UNUSED_PARAM(context);
UNUSED_PARAM(function);
return JSValueMakeUndefined(context);
}
-static JSValueRef JSNodePrototype_removeChild(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+static JSValueRef JSNode_removeChild(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
UNUSED_PARAM(context);
UNUSED_PARAM(function);
return JSValueMakeUndefined(context);
}
-static JSValueRef JSNodePrototype_replaceChild(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+static JSValueRef JSNode_replaceChild(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
UNUSED_PARAM(context);
UNUSED_PARAM(function);
return JSValueMakeUndefined(context);
}
-static JSStaticFunction JSNodePrototype_staticFunctions[] = {
- { "appendChild", JSNodePrototype_appendChild, kJSPropertyAttributeDontDelete },
- { "removeChild", JSNodePrototype_removeChild, kJSPropertyAttributeDontDelete },
- { "replaceChild", JSNodePrototype_replaceChild, kJSPropertyAttributeDontDelete },
+static JSStaticFunction JSNode_staticFunctions[] = {
+ { "appendChild", JSNode_appendChild, kJSPropertyAttributeDontDelete },
+ { "removeChild", JSNode_removeChild, kJSPropertyAttributeDontDelete },
+ { "replaceChild", JSNode_replaceChild, kJSPropertyAttributeDontDelete },
{ 0, 0, 0 }
};
-static JSClassRef JSNodePrototype_class(JSContextRef context)
-{
- static JSClassRef jsClass;
- if (!jsClass) {
- JSClassDefinition definition = kJSClassDefinitionNull;
- definition.staticFunctions = JSNodePrototype_staticFunctions;
- jsClass = JSClassCreate(&definition);
- }
- return jsClass;
-}
-
static JSValueRef JSNode_getNodeType(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception)
{
UNUSED_PARAM(context);
Node_deref(node);
}
-static JSClassRef JSNode_class(JSContextRef context)
+JSClassRef JSNode_class(JSContextRef context)
{
static JSClassRef jsClass;
if (!jsClass) {
- JSClassDefinition definition = kJSClassDefinitionNull;
+ JSClassDefinition definition = kJSClassDefinitionEmpty;
definition.staticValues = JSNode_staticValues;
+ definition.staticFunctions = JSNode_staticFunctions;
definition.initialize = JSNode_initialize;
definition.finalize = JSNode_finalize;
return jsClass;
}
-JSObjectRef JSNode_prototype(JSContextRef context)
-{
- static JSObjectRef prototype;
- if (!prototype) {
- prototype = JSObjectMake(context, JSNodePrototype_class(context), NULL);
- JSValueProtect(context, prototype);
- }
- return prototype;
-}
-
JSObjectRef JSNode_new(JSContextRef context, Node* node)
{
- return JSObjectMakeWithData(context, JSNode_class(context), JSNode_prototype(context), node);
+ return JSObjectMake(context, JSNode_class(context), node);
}
JSObjectRef JSNode_construct(JSContextRef context, JSObjectRef object, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
#include "Node.h"
extern JSObjectRef JSNode_new(JSContextRef context, Node* node);
-extern JSObjectRef JSNode_prototype(JSContextRef context);
+extern JSClassRef JSNode_class(JSContextRef context);
extern JSObjectRef JSNode_construct(JSContextRef context, JSObjectRef object, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);
#endif // JSNode_h
#include "JSNodeList.h"
#include "UnusedParam.h"
-static JSValueRef JSNodeListPrototype_item(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+static JSValueRef JSNodeList_item(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
if (argumentCount > 0) {
NodeList* nodeList = JSObjectGetPrivate(thisObject);
return JSValueMakeUndefined(context);
}
-static JSStaticFunction JSNodeListPrototype_staticFunctions[] = {
- { "item", JSNodeListPrototype_item, kJSPropertyAttributeDontDelete },
+static JSStaticFunction JSNodeList_staticFunctions[] = {
+ { "item", JSNodeList_item, kJSPropertyAttributeDontDelete },
{ 0, 0, 0 }
};
-static JSClassRef JSNodeListPrototype_class(JSContextRef context)
-{
- static JSClassRef jsClass;
- if (!jsClass) {
- JSClassDefinition definition = kJSClassDefinitionNull;
- definition.staticFunctions = JSNodeListPrototype_staticFunctions;
- jsClass = JSClassCreate(&definition);
- }
-
- return jsClass;
-}
-
static JSValueRef JSNodeList_length(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception)
{
UNUSED_PARAM(context);
{
static JSClassRef jsClass;
if (!jsClass) {
- JSClassDefinition definition = kJSClassDefinitionNull;
+ JSClassDefinition definition = kJSClassDefinitionEmpty;
definition.staticValues = JSNodeList_staticValues;
+ definition.staticFunctions = JSNodeList_staticFunctions;
definition.getProperty = JSNodeList_getProperty;
definition.initialize = JSNodeList_initialize;
definition.finalize = JSNodeList_finalize;
return jsClass;
}
-static JSObjectRef JSNodeList_prototype(JSContextRef context)
-{
- static JSObjectRef prototype;
- if (!prototype) {
- prototype = JSObjectMake(context, JSNodeListPrototype_class(context), NULL);
- JSValueProtect(context, prototype);
- }
- return prototype;
-}
-
JSObjectRef JSNodeList_new(JSContextRef context, NodeList* nodeList)
{
- return JSObjectMakeWithData(context, JSNodeList_class(context), JSNodeList_prototype(context), nodeList);
+ return JSObjectMake(context, JSNodeList_class(context), nodeList);
}
JSClassRef JSClassCreate(JSClassDefinition* definition)
{
- JSClassRef jsClass = new OpaqueJSClass(definition);
+ JSClassRef jsClass = (definition->attributes & kJSClassAttributeNoPrototype)
+ ? OpaqueJSClass::createNoPrototype(definition)
+ : OpaqueJSClass::create(definition);
+
return JSClassRetain(jsClass);
}
delete jsClass;
}
-JSObjectRef JSObjectMake(JSContextRef ctx, JSClassRef jsClass, JSValueRef prototype)
+JSObjectRef JSObjectMake(JSContextRef ctx, JSClassRef jsClass, void* data)
{
- return JSObjectMakeWithData(ctx, jsClass, prototype, 0);
+ JSLock lock;
+ ExecState* exec = toJS(ctx);
+
+ JSValue* jsPrototype = jsClass
+ ? jsClass->prototype(ctx)
+ : exec->lexicalInterpreter()->builtinObjectPrototype();
+
+ return JSObjectMakeWithPrototype(ctx, jsClass, data, toRef(jsPrototype));
}
-JSObjectRef JSObjectMakeWithData(JSContextRef ctx, JSClassRef jsClass, JSValueRef prototype, void* data)
+JSObjectRef JSObjectMakeWithPrototype(JSContextRef ctx, JSClassRef jsClass, void* data, JSValueRef prototype)
{
JSLock lock;
if (!jsClass)
return toRef(new JSObject(jsPrototype)); // slightly more efficient
- return toRef(new JSCallbackObject(exec, jsClass, jsPrototype, data)); // initialize can't throw
+ return toRef(new JSCallbackObject(exec, jsClass, jsPrototype, data));
}
JSObjectRef JSObjectMakeFunctionWithCallback(JSContextRef ctx, JSStringRef name, JSObjectCallAsFunctionCallback callAsFunction)
return toRef(new JSCallbackFunction(exec, callAsFunction, nameID));
}
-JSObjectRef JSObjectMakeConstructorWithCallback(JSContextRef ctx, JSValueRef prototype, JSObjectCallAsConstructorCallback callAsConstructor)
+JSObjectRef JSObjectMakeConstructor(JSContextRef ctx, JSClassRef jsClass, JSObjectCallAsConstructorCallback callAsConstructor)
{
JSLock lock;
ExecState* exec = toJS(ctx);
- JSValue* jsPrototype = toJS(prototype);
- if (!jsPrototype)
- jsPrototype = exec->dynamicInterpreter()->builtinObjectPrototype();
+ JSValue* jsPrototype = jsClass
+ ? jsClass->prototype(ctx)
+ : exec->dynamicInterpreter()->builtinObjectPrototype();
- JSObject* constructor = new JSCallbackConstructor(exec, callAsConstructor);
+ JSObject* constructor = new JSCallbackConstructor(exec, jsClass, callAsConstructor);
constructor->put(exec, prototypePropertyName, jsPrototype, DontEnum|DontDelete|ReadOnly);
return toRef(constructor);
}
*/
typedef unsigned JSPropertyAttributes;
+/*!
+@enum JSClassAttribute
+@constant kJSClassAttributeNone Specifies that a class has no special attributes.
+@constant kJSClassAttributeNoPrototype Specifies that a class should not generate a prototype object. Use kJSClassAttributeNoPrototype in combination with JSObjectMakeWithPrototype to manage prototypes manually.
+*/
+enum {
+ kJSClassAttributeNone = 0,
+ kJSClassAttributeNoPrototype = 1 << 1
+};
+
+/*!
+@typedef JSClassAttributes
+@abstract A set of JSClassAttributes. Combine multiple attributes by logically ORing them together.
+*/
+typedef unsigned JSClassAttributes;
+
/*!
@typedef JSObjectInitializeCallback
@abstract The callback invoked when an object is first created.
/*!
@typedef JSObjectGetPropertyNamesCallback
-@abstract The callback invoked to get the names of an object's properties.
+@abstract The callback invoked when collecting the names of an object's properties.
@param ctx The execution context to use.
-@param object The JSObject whose property names need to be appended to propertyNames.
-@param accumulator A JavaScript property name accumulator, to which the object should add the names of its properties.
+@param object The JSObject whose property names are being collected.
+@param accumulator A JavaScript property name accumulator in which to accumulate the names of object's properties.
@discussion If you named your function GetPropertyNames, you would declare it like this:
void GetPropertyNames(JSContextRef ctx, JSObjectRef object, JSPropertyNameAccumulatorRef accumulator);
-Use JSPropertyNameAccumulatorAddName to add property names to accumulator.
-
-Property lists are used by JSPropertyEnumerators and JavaScript for...in loops.
+Property name accumulators are used by JSObjectCopyPropertyNames and JavaScript for...in loops.
-It's only necessary to add names of properties that you handle
-specially in your own get / set callbacks. Static property names,
-names of standard JS properties, and properties from the prototype
-will be added automatically.
+Use JSPropertyNameAccumulatorAddName to add property names to accumulator. A class's getPropertyNames callback only needs to provide the names of properties that the class vends through a custom getProperty or setProperty callback. Other properties, including statically declared properties, properties vended by other classes, and properties belonging to object's prototype, are added independently.
*/
typedef void
(*JSObjectGetPropertyNamesCallback) (JSContextRef ctx, JSObjectRef object, JSPropertyNameAccumulatorRef propertyNames);
JSValueRef CallAsFunction(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);
-If your callback were invoked by the JavaScript expression 'myObject.myMemberFunction()', function would be set to myMemberFunction, and thisObject would be set to myObject.
+If your callback were invoked by the JavaScript expression 'myObject.myFunction()', function would be set to myFunction, and thisObject would be set to myObject.
If this callback is NULL, calling your object as a function will throw an exception.
*/
JSObjectRef CallAsConstructor(JSContextRef ctx, JSObjectRef constructor, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);
-If your callback were invoked by the JavaScript expression 'new myConstructorFunction()', constructor would be set to myConstructorFunction.
+If your callback were invoked by the JavaScript expression 'new myConstructor()', constructor would be set to myConstructor.
If this callback is NULL, using your object as a constructor in a 'new' expression will throw an exception.
*/
/*!
@struct JSClassDefinition
-@abstract This structure contains properties and callbacks that define a type of object. All fields are optional. Any field may be NULL.
+@abstract This structure contains properties and callbacks that define a type of object. All fields other than the version field are optional. Any pointer may be NULL.
@field version The version number of this structure. The current version is 0.
+@field attributes A logically ORed set of JSClassAttributes to give to the class.
@field className A null-terminated UTF8 string containing the class's name.
@field parentClass A JSClass to set as the class's parent class. Pass NULL use the default object class.
@field staticValues A JSStaticValue array containing the class's statically declared value properties. Pass NULL to specify no statically declared value properties. The array must be terminated by a JSStaticValue whose name field is NULL.
@field getProperty The callback invoked when getting a property's value.
@field setProperty The callback invoked when setting a property's value.
@field deleteProperty The callback invoked when deleting a property.
-@field addPropertiesToList The callback invoked when adding an object's properties to a property list.
+@field getPropertyNames The callback invoked when collecting the names of an object's properties.
@field callAsFunction The callback invoked when an object is called as a function.
@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.
-@discussion The staticValues and staticFunctions arrays are the simplest and most efficient means for vending custom properties. Statically declared properties autmatically service requests like get, set, and enumerate. Property access callbacks are required only to implement unusual properties, like array indexes, whose names are not known at compile-time.
+@discussion The staticValues and staticFunctions arrays are the simplest and most efficient means for vending custom properties. Statically declared properties autmatically service requests like getProperty, setProperty, and getPropertyNames. Property access callbacks are required only to implement unusual properties, like array indexes, whose names are not known at compile-time.
If you named your getter function "GetX" and your setter function "SetX", you would declare a JSStaticValue array containing "X" like this:
{ 0, 0, 0, 0 }
};
-Standard JavaScript practice calls for storing functions in prototype objects, so derived objects can share them. Therefore, it is common for prototype classes to have function properties but no value properties, and for object classes to have value properties but no function properties.
+Standard JavaScript practice calls for storing functions in prototype objects, so derived objects can share them. Therefore, it is common for prototypes to have function properties but no value properties, and for objects to have value properties but no function properties. The default behavior of JSClassCreate is to follow this idiom, automatically generating a prototype in which to store the class's function properties. The kJSClassAttributeNoPrototype attribute overrides the idiom, specifying that all supplied function and value properties should be stored directly in the object.
A NULL callback specifies that the default object callback should substitute, except in the case of hasProperty, where it specifies that getProperty should substitute.
*/
typedef struct {
int version; // current (and only) version is 0
+ JSClassAttributes attributes;
const char* className;
JSClassRef parentClass;
} JSClassDefinition;
/*!
-@const kJSClassDefinitionNull
-@abstract A JSClassDefinition structure of the current version, filled with NULL pointers.
+@const kJSClassDefinitionEmpty
+@abstract A JSClassDefinition structure of the current version, filled with NULL pointers and having no attributes.
@discussion Use this constant as a convenience when creating class definitions. For example, to create a class definition with only a finalize method:
-JSClassDefinition definition = kJSClassDefinitionNull;
+JSClassDefinition definition = kJSClassDefinitionEmpty;
definition.finalize = Finalize;
*/
-extern const JSClassDefinition kJSClassDefinitionNull;
+extern const JSClassDefinition kJSClassDefinitionEmpty;
/*!
@function
@abstract Creates a JavaScript class suitable for use with JSObjectMake.
@param definition A JSClassDefinition that defines the class.
-@result A JSClass with the given definition's name, properties, callbacks, and parent class. Ownership follows the Create Rule.
+@result A JSClass with the given definition. Ownership follows the Create Rule.
*/
JSClassRef JSClassCreate(JSClassDefinition* definition);
@result A JSClass that is the same as jsClass.
*/
JSClassRef JSClassRetain(JSClassRef jsClass);
+
/*!
@function
@abstract Releases a JavaScript class.
@abstract Creates a JavaScript object.
@param ctx The execution context to use.
@param jsClass The JSClass to assign to the object. Pass NULL to use the default object class.
-@param prototype The prototype to assign to the object. Pass NULL to use the default object prototype.
-@result A JSObject with the given class, prototype, and private data.
-@discussion The default object class does not allocate storage for private data, so you must provide a non-NULL JSClass if you want your object to be able to store private data.
+@param data A void* to set as the object's private data. Pass NULL to specify no private data.
+@result A JSObject with the given class and private data.
+@discussion JSObjectMake assigns jsClass's automatically generated prototype to the object it creates. If jsClass has no automatically generated prototype, JSObjectMake uses the default object prototype.
+
+data is set on the created object before the intialize methods in its class chain are called. This enables the initialize methods to retrieve and manipulate data through JSObjectGetPrivate.
+
+The default object class does not allocate storage for private data, so you must provide a non-NULL jsClass if you want your object to be able to store private data.
*/
-JSObjectRef JSObjectMake(JSContextRef ctx, JSClassRef jsClass, JSValueRef prototype);
+JSObjectRef JSObjectMake(JSContextRef ctx, JSClassRef jsClass, void* data);
/*!
@function
-@abstract Creates a JavaScript object.
+@abstract Creates a JavaScript object with a given prototype.
@param ctx The execution context to use.
@param jsClass The JSClass to assign to the object. Pass NULL to use the default object class.
@param prototype The prototype to assign to the object. Pass NULL to use the default object prototype.
@param data A void* to set as the object's private data. Pass NULL to specify no private data.
-@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 A JSObject with the given class, prototype, and private data.
-@discussion The default object class does not allocate storage for private data, so you must provide a non-NULL JSClass if you want your object to be able to store private data.
+@result A JSObject with the given class, private data, and prototype.
+@discussion Use JSObjectMakeWithPrototype in combination with kJSClassAttributeNoPrototype to manage prototypes manually.
-data will be set on the created object before the intialize methods in its class chain are called. This enables the initialize methods to retrieve and manipulate data through JSObjectGetPrivate.
+data is set on the created object before the intialize methods in its class chain are called. This enables the initialize methods to retrieve and manipulate data through JSObjectGetPrivate.
+
+The default object class does not allocate storage for private data, so you must provide a non-NULL JSClass if you want your object to be able to store private data.
*/
-JSObjectRef JSObjectMakeWithData(JSContextRef ctx, JSClassRef jsClass, JSValueRef prototype, void* data);
+JSObjectRef JSObjectMakeWithPrototype(JSContextRef ctx, JSClassRef jsClass, void* data, JSValueRef prototype);
/*!
@function
/*!
@function
-@abstract Convenience method for creating a JavaScript constructor with a given callback as its implementation.
+@abstract Convenience method for creating a JavaScript constructor.
@param ctx The execution context to use.
-@param prototype A JSValue to use as the constructor's .prototype property. This should be the same value your constructor will set as the prototype of the objects it constructs.
-@param callAsConstructor The JSObjectCallAsConstructorCallback to invoke when the constructor is used in a 'new' expression.
+@param jsClass A JSClass that is the class your constructor will assign to the objects its constructs. jsClass will be used to set the constructor's .prototype property, and to evaluate 'instanceof' expressions. Pass NULL to use the default object class.
+@param callAsConstructor A JSObjectCallAsConstructorCallback to invoke when your constructor is used in a 'new' expression. Pass NULL to use the default object constructor.
@result A JSObject that is a constructor. The object's prototype will be the default object prototype.
+@discussion The default object constructor takes no arguments and constructs an object of class jsClass with no private data.
*/
-JSObjectRef JSObjectMakeConstructorWithCallback(JSContextRef ctx, JSValueRef prototype, JSObjectCallAsConstructorCallback callAsConstructor);
+JSObjectRef JSObjectMakeConstructor(JSContextRef ctx, JSClassRef jsClass, JSObjectCallAsConstructorCallback callAsConstructor);
/*!
@function
@abstract Gets an object's prototype.
@param ctx The execution context to use.
@param object A JSObject whose prototype you want to get.
-@result A JSValue containing the object's prototype.
+@result A JSValue that is the object's prototype.
*/
JSValueRef JSObjectGetPrototype(JSContextRef ctx, JSObjectRef object);
@abstract Gets a property from an object by numeric index.
@param ctx The execution context to use.
@param object The JSObject whose property you want to get.
-@param propertyIndex The property's name as a number.
+@param propertyIndex An integer value that is 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 the undefined value.
-@discussion Calling JSObjectGetPropertyAtIndex is equivalent to calling JSObjectGetProperty with a string containing propertyIndex, but it enables optimized access to JavaScript arrays.
+@discussion Calling JSObjectGetPropertyAtIndex is equivalent to calling JSObjectGetProperty with a string containing propertyIndex, but JSObjectGetPropertyAtIndex provides optimized access to numeric properties.
*/
JSValueRef JSObjectGetPropertyAtIndex(JSContextRef ctx, JSObjectRef object, unsigned propertyIndex, JSValueRef* exception);
@param propertyIndex The property's name as a number.
@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.
-@discussion Calling JSObjectSetPropertyAtIndex is equivalent to calling JSObjectSetProperty with a string containing propertyIndex, but it enables optimized access to JavaScript arrays.
+@discussion Calling JSObjectSetPropertyAtIndex is equivalent to calling JSObjectSetProperty with a string containing propertyIndex, but JSObjectSetPropertyAtIndex provides optimized access to numeric properties.
*/
void JSObjectSetPropertyAtIndex(JSContextRef ctx, JSObjectRef object, unsigned propertyIndex, JSValueRef value, JSValueRef* exception);
/*!
@function
@abstract Sets a pointer to private data on an object.
-@param object A JSObject whose private data you want to set.
+@param object The JSObject whose private data you want to set.
@param data A void* to set as the object's private data.
-@result true if the object can store private data, otherwise false.
+@result true if object can store private data, otherwise false.
@discussion The default object class does not allocate storage for private data. Only objects created with a non-NULL JSClass can store private data.
*/
bool JSObjectSetPrivate(JSObjectRef object, void* data);
@param object The JSObject to call as a function.
@param thisObject The object to use as "this," or NULL to use the global object as "this."
@param argumentCount An integer count of the number of arguments in arguments.
-@param arguments A JSValue array of the arguments to pass to the function. Pass NULL if argumentCount is 0.
+@param arguments A JSValue array of arguments to pass to the function. Pass NULL if argumentCount is 0.
@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.
*/
@param ctx The execution context to use.
@param object The JSObject to call as a constructor.
@param argumentCount An integer count of the number of arguments in arguments.
-@param arguments A JSValue array of the arguments to pass to the function. Pass NULL if argumentCount is 0.
+@param arguments A JSValue array of arguments to pass to the constructor. Pass NULL if argumentCount is 0.
@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.
*/
/*!
@function
-@abstract Get the names of all enumerable properties of an object.
+@abstract Gets the names of an object's enumerable properties.
@param ctx The execution context to use.
-@param object The object from which to get property names.
-@result A JSPropertyNameArray containing the names of all the object's enumerable properties.
+@param object The object whose property names you want to get.
+@result A JSPropertyNameArray containing the names object's enumerable properties.
*/
JSPropertyNameArrayRef JSObjectCopyPropertyNames(JSContextRef ctx, JSObjectRef object);
/*!
@function
-@abstract Get the number of items in a JavaScript property name array.
+@abstract Gets a count of the number of items in a JavaScript property name array.
@param array The array from which to retrieve the count.
-@result The count of items in the array.
+@result An integer count of the number of names in array.
*/
size_t JSPropertyNameArrayGetCount(JSPropertyNameArrayRef array);
/*!
@function
-@abstract Get a single item from a JavaScript property name array.
-@param array The array from which to retrieve a property name.
+@abstract Gets a property name at a given index in a JavaScript property name array.
+@param array The array from which to retrieve the property name.
@param index The index of the property name to retrieve.
-@result A JSStringRef containing the name of the property.
+@result A JSStringRef containing the property name.
*/
JSStringRef JSPropertyNameArrayGetNameAtIndex(JSPropertyNameArrayRef array, size_t index);
/*!
@function
-@abstract Add a property name - useful while getting the property names for an object.
-@param accumulator The accumulator object to which to add the property.
-@param propertyName The new property to add.
+@abstract Adds a property name to a JavaScript property name accumulator.
+@param accumulator The accumulator object to which to add the property name.
+@param propertyName The property name to add.
*/
void JSPropertyNameAccumulatorAddName(JSPropertyNameAccumulatorRef accumulator, JSStringRef propertyName);
JSStringRef JSStringCreateWithCFString(CFStringRef string);
/*!
@function
-@abstract Creates a CFString form a JavaScript string.
+@abstract Creates a CFString from a JavaScript string.
@param alloc The alloc parameter to pass to CFStringCreate.
@param string The JSString to copy into the new CFString.
@result A CFString containing string. Ownership follows the Create Rule.
/*!
@function
-@abstract Tests whether a JavaScript value is an object with a given
-@param ctx The execution context to use.
- class in its class chain.
-@param value The JSValue to test.
- @result true if value is an object and has jsClass in its class chain,
- otherwise false.
+@abstract Tests whether a JavaScript value is an object with a given class in its class chain.
+@param ctx The execution context to use.
+@param value The JSValue to test.
+@param jsClass The JSClass to test against.
+@result true if value is an object and has jsClass in its class chain, otherwise false.
*/
bool JSValueIsObjectOfClass(JSContextRef ctx, JSValueRef value, JSClassRef jsClass);
// Garbage collection
/*!
@function
-@abstract Protects a JavaScript value from garbage collection.
-@param ctx The execution context to use.
-@param value The JSValue to protect.
-@discussion A value may be protected multiple times and must be unprotected an
- equal number of times before becoming eligible for garbage collection.
+@abstract Protects a JavaScript value from garbage collection.
+@param ctx The execution context to use.
+@param value The JSValue to protect.
+@discussion Use this method when you want to store a JSValue in a global or on the heap, where the garbage collector will not be able to discover your reference to it.
+
+A value may be protected multiple times and must be unprotected an equal number of times before becoming eligible for garbage collection.
*/
void JSValueProtect(JSContextRef ctx, JSValueRef value);
JSStringRelease(printIString);
JSStringRef node = JSStringCreateWithUTF8CString("Node");
- JSObjectSetProperty(context, globalObject, node, JSObjectMakeConstructorWithCallback(context, JSNode_prototype(context), JSNode_construct), kJSPropertyAttributeNone, NULL);
+ JSObjectSetProperty(context, globalObject, node, JSObjectMakeConstructor(context, JSNode_class(context), JSNode_construct), kJSPropertyAttributeNone, NULL);
JSStringRelease(node);
char* scriptUTF8 = createStringWithContentsOfFile("minidom.js");
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-// For browser-based testing
-if (typeof window != 'undefined') {
- /*
- The methods and constructors below approximate what that the native host should provide
-
- print(string);
-
- Node
- |-- TextNode
- |-- Element
- |-- RootElement
- |-- HeroElement
- |-- VillainElement
- |-- NameElement
- |-- WeaponElement
- |-- Document
- */
-
- function print(string, indentLevel)
- {
- document.getElementById('pre').appendChild(document.createTextNode(string));
- }
-
- Node = function()
- {
- this.__defineGetter__("childNodes", function() {
- if (!this._childNodes)
- this._childNodes = new Array();
- return this._childNodes;
- });
- this.__defineGetter__("firstChild", function () {
- return this.childNodes[0];
- });
- }
-
- Node.prototype.nodeType = "Node";
-
- Node.prototype.appendChild = function(child) {
- this.childNodes.push(child);
- }
-
- Node.prototype.serialize = function(numSpaces) {
- function printSpaces(n)
- {
- for (var i = 0; i < n; i++) // >
- print(" ");
- }
-
- printSpaces(numSpaces);
- print('<' + this.nodeType + '>' + '\n');
-
- var childNodesLength = this.childNodes.length;
- for (var i = 0; i < childNodesLength; i++) //>
- this.childNodes[i].serialize(numSpaces + 4);
-
- printSpaces(numSpaces);
- print('</' + this.nodeType + '>\n');
- }
-
- TextNode = function(text)
- {
- this.text = text;
- }
-
- TextNode.prototype = new Node();
-
- TextNode.prototype.nodeType = "Text";
-
- TextNode.prototype.serialize = function(numSpaces) {
- for (var i = 0; i < numSpaces; i++) // >
- print(" ");
- print(this.text + '\n');
- }
-
- Element = function()
- {
- }
-
- Element.prototype = new Node();
-
- Element.prototype.nodeType = "Element";
-
- RootElement = function()
- {
- }
-
- RootElement.prototype = new Element();
-
- RootElement.prototype.nodeType = "Root";
-
- HeroElement = function()
- {
- }
-
- HeroElement.prototype = new Element();
-
- HeroElement.prototype.nodeType = "Hero";
-
- VillainElement = function()
- {
- }
-
- VillainElement.prototype = new Element();
-
- VillainElement.prototype.nodeType = "Villain";
-
- NameElement = function()
- {
- }
-
- NameElement.prototype = new Element();
-
- NameElement.prototype.nodeType = "Name";
-
- WeaponElement = function()
- {
- }
-
- WeaponElement.prototype = new Element();
-
- WeaponElement.prototype.nodeType = "Weapon";
-
- Document = function()
- {
- }
-
- Document.prototype = new Node();
-
- Document.prototype.serialize = function() {
- this.firstChild.serialize(0);
- }
-
- Document.prototype.createElement = function(elementType) {
- return eval('new ' + elementType + 'Element()');
- }
-
- Document.prototype.createTextNode = function(text) {
- return new TextNode(text);
- }
-}
-
function shouldBe(a, b)
{
var evalA;
shouldBe("new Object() instanceof Node", false);
print(Node);
-
- /*
- var element, name, weapon;
-
- var document = new Document();
- document.appendChild(document.createElement('Root'));
-
- // Tank Girl
- element = document.createElement('Hero');
-
- name = document.createElement('Name');
- name.appendChild(document.createTextNode('Tank Girl'));
- element.appendChild(name);
-
- weapon = document.createElement('Weapon');
- weapon.appendChild(document.createTextNode('Tank'));
- element.appendChild(weapon);
-
- weapon = document.createElement('Weapon');
- weapon.appendChild(document.createTextNode('Attitude'));
- element.appendChild(weapon);
-
- weapon = document.createElement('Weapon');
- weapon.appendChild(document.createTextNode('Army of genetically engineered super-kangaroos'));
- element.appendChild(weapon);
-
- document.firstChild.appendChild(element);
-
-
- // Skeletor
- element = document.createElement('Villain');
-
- name = document.createElement('Name');
- name.appendChild(document.createTextNode('Skeletor'));
- element.appendChild(name);
-
- weapon = document.createElement('Weapon');
- weapon.appendChild(document.createTextNode('Havok Staff'));
- element.appendChild(weapon);
-
- weapon = document.createElement('Weapon');
- weapon.appendChild(document.createTextNode('Motley crew of henchmen'));
- element.appendChild(weapon);
-
- document.firstChild.appendChild(element);
-
- // Serialize
- document.serialize();
- */
}
test();
#include <assert.h>
#include <math.h>
+#include <setjmp.h>
static JSGlobalContextRef context = 0;
/* MyObject pseudo-class */
-static bool didInitialize = false;
-static void MyObject_initialize(JSContextRef context, JSObjectRef object)
-{
- UNUSED_PARAM(context);
- UNUSED_PARAM(object);
- didInitialize = true;
-}
-
static bool MyObject_hasProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName)
{
UNUSED_PARAM(context);
return NULL;
}
-static bool MyObject_didFinalize = false;
-static void MyObject_finalize(JSObjectRef object)
-{
- UNUSED_PARAM(context);
- UNUSED_PARAM(object);
- MyObject_didFinalize = true;
-}
-
static JSStaticValue evilStaticValues[] = {
{ "nullGetSet", 0, 0, kJSPropertyAttributeNone },
{ 0, 0, 0, 0 }
JSClassDefinition MyObject_definition = {
0,
+ kJSClassAttributeNone,
"MyObject",
NULL,
evilStaticValues,
evilStaticFunctions,
- MyObject_initialize,
- MyObject_finalize,
+ NULL,
+ NULL,
MyObject_hasProperty,
MyObject_getProperty,
MyObject_setProperty,
return jsClass;
}
+static JSValueRef Base_get(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception)
+{
+ UNUSED_PARAM(ctx);
+ UNUSED_PARAM(object);
+ UNUSED_PARAM(propertyName);
+
+ return JSValueMakeNumber(ctx, 1); // distinguish base get form derived get
+}
+
+static bool Base_set(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* exception)
+{
+ UNUSED_PARAM(ctx);
+ UNUSED_PARAM(object);
+ UNUSED_PARAM(propertyName);
+ UNUSED_PARAM(value);
+
+ *exception = JSValueMakeNumber(ctx, 1); // distinguish base set from derived set
+ return true;
+}
+
+static JSValueRef Base_callAsFunction(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ UNUSED_PARAM(ctx);
+ UNUSED_PARAM(function);
+ UNUSED_PARAM(thisObject);
+ UNUSED_PARAM(argumentCount);
+ UNUSED_PARAM(arguments);
+
+ return JSValueMakeNumber(ctx, 1); // distinguish base call from derived call
+}
+
+static JSStaticFunction Base_staticFunctions[] = {
+ { "baseProtoDup", NULL, kJSPropertyAttributeNone },
+ { "baseProto", Base_callAsFunction, kJSPropertyAttributeNone },
+ { 0, 0, 0 }
+};
+
+static JSStaticValue Base_staticValues[] = {
+ { "baseDup", Base_get, Base_set, kJSPropertyAttributeNone },
+ { "baseOnly", Base_get, Base_set, kJSPropertyAttributeNone },
+ { 0, 0, 0, 0 }
+};
+
+static bool TestInitializeFinalize;
static void Base_initialize(JSContextRef context, JSObjectRef object)
{
- assert(!JSObjectGetPrivate(object));
- JSObjectSetPrivate(object, (void*)1);
+ if (TestInitializeFinalize) {
+ assert((void*)1 == JSObjectGetPrivate(object));
+ JSObjectSetPrivate(object, (void*)2);
+ }
}
-static bool Base_didFinalize;
+static unsigned Base_didFinalize;
static void Base_finalize(JSObjectRef object)
{
- assert((void*)3 == JSObjectGetPrivate(object));
- Base_didFinalize = true;
+ if (TestInitializeFinalize) {
+ assert((void*)4 == JSObjectGetPrivate(object));
+ Base_didFinalize = true;
+ }
}
static JSClassRef Base_class(JSContextRef context)
{
static JSClassRef jsClass;
if (!jsClass) {
- JSClassDefinition definition = kJSClassDefinitionNull;
+ JSClassDefinition definition = kJSClassDefinitionEmpty;
+ definition.staticValues = Base_staticValues;
+ definition.staticFunctions = Base_staticFunctions;
definition.initialize = Base_initialize;
definition.finalize = Base_finalize;
jsClass = JSClassCreate(&definition);
return jsClass;
}
+static JSValueRef Derived_get(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception)
+{
+ UNUSED_PARAM(ctx);
+ UNUSED_PARAM(object);
+ UNUSED_PARAM(propertyName);
+
+ return JSValueMakeNumber(ctx, 2); // distinguish base get form derived get
+}
+
+static bool Derived_set(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* exception)
+{
+ UNUSED_PARAM(ctx);
+ UNUSED_PARAM(object);
+ UNUSED_PARAM(propertyName);
+ UNUSED_PARAM(value);
+
+ *exception = JSValueMakeNumber(ctx, 2); // distinguish base set from derived set
+ return true;
+}
+
+static JSValueRef Derived_callAsFunction(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ UNUSED_PARAM(ctx);
+ UNUSED_PARAM(function);
+ UNUSED_PARAM(thisObject);
+ UNUSED_PARAM(argumentCount);
+ UNUSED_PARAM(arguments);
+
+ return JSValueMakeNumber(ctx, 2); // distinguish base call from derived call
+}
+
+static JSStaticFunction Derived_staticFunctions[] = {
+ { "protoOnly", Derived_callAsFunction, kJSPropertyAttributeNone },
+ { "protoDup", NULL, kJSPropertyAttributeNone },
+ { "baseProtoDup", Derived_callAsFunction, kJSPropertyAttributeNone },
+ { 0, 0, 0 }
+};
+
+static JSStaticValue Derived_staticValues[] = {
+ { "derivedOnly", Derived_get, Derived_set, kJSPropertyAttributeNone },
+ { "protoDup", Derived_get, Derived_set, kJSPropertyAttributeNone },
+ { "baseDup", Derived_get, Derived_set, kJSPropertyAttributeNone },
+ { 0, 0, 0, 0 }
+};
+
static void Derived_initialize(JSContextRef context, JSObjectRef object)
{
- assert((void*)1 == JSObjectGetPrivate(object));
- JSObjectSetPrivate(object, (void*)2);
+ if (TestInitializeFinalize) {
+ assert((void*)2 == JSObjectGetPrivate(object));
+ JSObjectSetPrivate(object, (void*)3);
+ }
}
static void Derived_finalize(JSObjectRef object)
{
- assert((void*)2 == JSObjectGetPrivate(object));
- JSObjectSetPrivate(object, (void*)3);
+ if (TestInitializeFinalize) {
+ assert((void*)3 == JSObjectGetPrivate(object));
+ JSObjectSetPrivate(object, (void*)4);
+ }
}
static JSClassRef Derived_class(JSContextRef context)
{
static JSClassRef jsClass;
if (!jsClass) {
- JSClassDefinition definition = kJSClassDefinitionNull;
+ JSClassDefinition definition = kJSClassDefinitionEmpty;
definition.parentClass = Base_class(context);
+ definition.staticValues = Derived_staticValues;
+ definition.staticFunctions = Derived_staticFunctions;
definition.initialize = Derived_initialize;
definition.finalize = Derived_finalize;
jsClass = JSClassCreate(&definition);
static char* createStringWithContentsOfFile(const char* fileName);
+static void testInitializeFinalize()
+{
+ JSObjectRef o = JSObjectMake(context, Derived_class(context), (void*)1);
+ assert(JSObjectGetPrivate(o) == (void*)3);
+}
+
int main(int argc, char* argv[])
{
UNUSED_PARAM(argc);
UNUSED_PARAM(argv);
+ // Test garbage collection with a fresh context
+ context = JSGlobalContextCreate(NULL);
+ TestInitializeFinalize = true;
+ testInitializeFinalize();
+ JSGlobalContextRelease(context);
+ JSGarbageCollect(context);
+ TestInitializeFinalize = false;
+
+ assert(Base_didFinalize);
+
context = JSGlobalContextCreate(NULL);
JSObjectRef globalObject = JSContextGetGlobalObject(context);
JSValueRef jsZero = JSValueMakeNumber(context, 0);
JSValueRef jsOne = JSValueMakeNumber(context, 1);
JSValueRef jsOneThird = JSValueMakeNumber(context, 1.0 / 3.0);
- JSObjectRef jsObjectNoProto = JSObjectMake(context, NULL, JSValueMakeNull(context));
+ JSObjectRef jsObjectNoProto = JSObjectMakeWithPrototype(context, NULL, NULL, JSValueMakeNull(context));
// FIXME: test funny utf8 characters
JSStringRef jsEmptyIString = JSStringCreateWithUTF8CString("");
#endif // __APPLE__
JSObjectRef myObject = JSObjectMake(context, MyObject_class(context), NULL);
- assert(didInitialize);
JSStringRef myObjectIString = JSStringCreateWithUTF8CString("MyObject");
JSObjectSetProperty(context, globalObject, myObjectIString, myObject, kJSPropertyAttributeNone, NULL);
JSStringRelease(myObjectIString);
assert(!JSObjectGetPrivate(printFunction));
JSStringRef myConstructorIString = JSStringCreateWithUTF8CString("MyConstructor");
- JSObjectRef myConstructor = JSObjectMakeConstructorWithCallback(context, NULL, myConstructor_callAsConstructor);
+ JSObjectRef myConstructor = JSObjectMakeConstructor(context, NULL, myConstructor_callAsConstructor);
JSObjectSetProperty(context, globalObject, myConstructorIString, myConstructor, kJSPropertyAttributeNone, NULL);
JSStringRelease(myConstructorIString);
assert(!JSObjectSetPrivate(myConstructor, (void*)1));
assert(!JSObjectGetPrivate(myConstructor));
+ string = JSStringCreateWithUTF8CString("Derived");
+ JSObjectRef derivedConstructor = JSObjectMakeConstructor(context, Derived_class(context), NULL);
+ JSObjectSetProperty(context, globalObject, string, derivedConstructor, kJSPropertyAttributeNone, NULL);
+ JSStringRelease(string);
+
o = JSObjectMake(context, NULL, NULL);
JSObjectSetProperty(context, o, jsOneIString, JSValueMakeNumber(context, 1), kJSPropertyAttributeNone, NULL);
JSObjectSetProperty(context, o, jsCFIString, JSValueMakeNumber(context, 1), kJSPropertyAttributeDontEnum, NULL);
JSPropertyNameArrayRelease(nameArray);
assert(count == 1); // jsCFString should not be enumerated
- JSClassDefinition nullDefinition = kJSClassDefinitionNull;
+ JSClassDefinition nullDefinition = kJSClassDefinitionEmpty;
+ nullDefinition.attributes = kJSClassAttributeNoPrototype;
JSClassRef nullClass = JSClassCreate(&nullDefinition);
JSClassRelease(nullClass);
+ nullDefinition = kJSClassDefinitionEmpty;
+ nullClass = JSClassCreate(&nullDefinition);
+ JSClassRelease(nullClass);
+
functionBody = JSStringCreateWithUTF8CString("return this;");
function = JSObjectMakeFunction(context, NULL, 0, NULL, functionBody, NULL, 1, NULL);
JSStringRelease(functionBody);
v = JSObjectCallAsFunction(context, function, o, 0, NULL, NULL);
assert(JSValueIsEqual(context, v, o, NULL));
- o = JSObjectMake(context, Derived_class(context), NULL);
- assert(JSObjectGetPrivate(o) == (void*)2);
- o = NULL;
-
char* scriptUTF8 = createStringWithContentsOfFile("testapi.js");
JSStringRef script = JSStringCreateWithUTF8CString(scriptUTF8);
result = JSEvaluateScript(context, script, NULL, NULL, 1, &exception);
JSStringRelease(script);
free(scriptUTF8);
- // Allocate a few dummies so that at least one will be collected
- JSObjectMake(context, MyObject_class(context), NULL);
- JSObjectMake(context, MyObject_class(context), NULL);
- JSGarbageCollect(context);
- assert(MyObject_didFinalize);
- assert(Base_didFinalize);
-
JSStringRelease(jsEmptyIString);
JSStringRelease(jsOneIString);
#if defined(__APPLE__)
JSStringRelease(badSyntax);
JSGlobalContextRelease(context);
+ JSGarbageCollect(context);
+
printf("PASS: Program exited normally.\n");
return 0;
}
shouldThrow("MyObject.nullGetSet");
shouldThrow("MyObject.nullCall()");
shouldThrow("MyObject.hasPropertyLie");
+
+derived = new Derived();
+
+// base properties and functions return 1 when called/gotten; derived, 2
+shouldBe("derived.baseProtoDup()", 2);
+shouldBe("derived.baseProto()", 1);
+shouldBe("derived.baseDup", 2);
+shouldBe("derived.baseOnly", 1);
+shouldBe("derived.protoOnly()", 2);
+shouldBe("derived.protoDup", 2);
+shouldBe("derived.derivedOnly", 2)
+
+// base properties throw 1 when set; derived, 2
+shouldBe("derived.baseDup = 0", 2);
+shouldBe("derived.baseOnly = 0", 1);
+shouldBe("derived.derivedOnly = 0", 2)
+shouldBe("derived.protoDup = 0", 2);
+2006-07-17 Geoffrey Garen <ggaren@apple.com>
+
+ Reviewed by Maciej.
+
+ - Added automatic prototype creation for classes.
+
+ A class stores a weak reference to a prototype, which is cleared when
+ the prototype is garbage collected, to avoid a reference cycle.
+
+ We now have an attributes field in JSClassDefinition, that currently is
+ used only to override automatic prototype creation when you want to manage your
+ own prototypes, but can be extended in the future for other nefarious purposes.
+
+ Similarly, we have JSObjectMake and JSObjectMakeWithPrototype, the latter
+ allowing you to manage your own prototypes.
+
+ JSObjectMakeConstructor is more interesting now, able to make a constructor
+ on your behalf if you just give it a class.
+
+ - Removed bogus old code from minidom.js.
+
+ - Tweaked the headerdocs.
+
+ - Added more GC testing, which caught some leaks, and tested more funny
+ edge cases in lookup, which caught a lookup bug. Removed some testing
+ we used to do with MyObject because it was redundant with the new, cool
+ stuff.
+
+ While fixing the lookup bug I retracted this change:
+
+ "If a static setProperty callback returns 'false', to indicate that the
+ property was not set, we no longer forward the set request up the class
+ chain, because that's almost certainly not what the programmer expected."
+
+ Returning false when setting a static property is a little silly, but you can see
+ it being useful when shadowing a base class's static properties, and, regardless
+ of usefullness, this is the defined behavior of the setProperty callback.
+
+ - Plus a little ASCII art, for the kids.
+
2006-07-17 Timothy Hatcher <timothy@apple.com>
Reviewed by Maciej.
_JSObjectIsConstructor
_JSObjectIsFunction
_JSObjectMake
-_JSObjectMakeConstructorWithCallback
+_JSObjectMakeConstructor
+_JSObjectMakeFunction
_JSObjectMakeFunction
_JSObjectMakeFunctionWithCallback
-_JSObjectMakeWithData
+_JSObjectMakeWithPrototype
_JSObjectSetPrivate
_JSObjectSetProperty
_JSObjectSetPropertyAtIndex
__ZNK3KJS9ExecState18lexicalInterpreterEv
__ZTVN3KJS19InternalFunctionImpE
__ZTVN3KJS8JSObjectE
-_kJSClassDefinitionNull
+_kJSClassDefinitionEmpty
_kjs_pcre_compile
_kjs_pcre_exec
_kjs_pcre_free