Reviewed by Maciej.
authorggaren <ggaren@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 18 Jul 2006 04:33:46 +0000 (04:33 +0000)
committerggaren <ggaren@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 18 Jul 2006 04:33:46 +0000 (04:33 +0000)
        - 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.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@15497 268f45cc-cd09-0410-ab3c-d52691b4dbfc

19 files changed:
JavaScriptCore/API/JSBase.h
JavaScriptCore/API/JSCallbackConstructor.cpp
JavaScriptCore/API/JSCallbackConstructor.h
JavaScriptCore/API/JSCallbackObject.cpp
JavaScriptCore/API/JSClassRef.cpp
JavaScriptCore/API/JSClassRef.h
JavaScriptCore/API/JSNode.c
JavaScriptCore/API/JSNode.h
JavaScriptCore/API/JSNodeList.c
JavaScriptCore/API/JSObjectRef.cpp
JavaScriptCore/API/JSObjectRef.h
JavaScriptCore/API/JSStringRef.h
JavaScriptCore/API/JSValueRef.h
JavaScriptCore/API/minidom.c
JavaScriptCore/API/minidom.js
JavaScriptCore/API/testapi.c
JavaScriptCore/API/testapi.js
JavaScriptCore/ChangeLog
JavaScriptCore/JavaScriptCore.exp

index 8b451b830fe0081180b573595e19d2f7e1ab6ccf..be15927f92bd8e77eb574d43b2ff5bc6406c8d56 100644 (file)
@@ -46,7 +46,7 @@ typedef struct OpaqueJSClass* JSClassRef;
 /*! @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;
 
 
index 28557e74b872d5893ee9d52f79c302a4b19ce991..faf33a5dec52978a1e0bf21ee4cb2d2d886e5587 100644 (file)
@@ -31,10 +31,19 @@ namespace KJS {
 
 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
@@ -49,14 +58,18 @@ bool JSCallbackConstructor::implementsConstruct() 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
index 32dd3062372f70b60db09987921c7c2cb1fe518c..fc45cb5e3be905f42acceeafba93cd5eba60a617 100644 (file)
@@ -35,7 +35,8 @@ 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;
     
@@ -48,7 +49,8 @@ public:
 private:
     JSCallbackConstructor(); // prevent default construction
     JSCallbackConstructor(const JSCallbackConstructor&);
-    
+
+    JSClassRef m_class;
     JSObjectCallAsConstructorCallback m_callback;
 };
 
index e58894b3ed7d45dde01eab32caf7b59c4cb8cd64..df0afa74553c5a472d6fac26f83771384a999be2 100644 (file)
@@ -143,9 +143,10 @@ void JSCallbackObject::put(ExecState* exec, const Identifier& propertyName, JSVa
             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.");
             }
         }
index 3e417edf0b511e4e7bf2a179032d4c3ca084e9d1..b6882abf91eeb01da15cd24bd6365726f9cd9df9 100644 (file)
  * 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)
@@ -67,6 +70,9 @@ OpaqueJSClass::OpaqueJSClass(JSClassDefinition* definition)
             ++staticFunction;
         }
     }
+        
+    if (protoClass)
+        prototypeClass = JSClassRetain(protoClass);
 }
 
 OpaqueJSClass::~OpaqueJSClass()
@@ -80,4 +86,72 @@ 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;
 }
index b080cc602ad3247470ce34a8ae2b48461a8451d5..f622a879c5c0a35bcbb0e8aae772dd1fa3a01395 100644 (file)
 #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)
@@ -53,9 +56,12 @@ struct StaticFunctionEntry {
 };
 
 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;
 
@@ -63,7 +69,8 @@ struct OpaqueJSClass {
 
     KJS::UString className;
     OpaqueJSClass* parentClass;
-        
+    OpaqueJSClass* prototypeClass;
+
     StaticValuesTable* staticValues;
     StaticFunctionsTable* staticFunctions;
     
@@ -78,6 +85,14 @@ struct OpaqueJSClass {
     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
index d5eb357671b89ae444ef58fb1d48e6e32de69b4b..95a8b4c50d95fb7cfba163c77037ccead9f76150 100644 (file)
@@ -30,9 +30,7 @@
 #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);
@@ -56,7 +54,7 @@ static JSValueRef JSNodePrototype_appendChild(JSContextRef context, JSObjectRef
     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);
@@ -76,7 +74,7 @@ static JSValueRef JSNodePrototype_removeChild(JSContextRef context, JSObjectRef
     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);
@@ -98,24 +96,13 @@ static JSValueRef JSNodePrototype_replaceChild(JSContextRef context, JSObjectRef
     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);
@@ -172,12 +159,13 @@ static void JSNode_finalize(JSObjectRef object)
     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;
 
@@ -186,19 +174,9 @@ static JSClassRef JSNode_class(JSContextRef context)
     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)
index 38ab0e53a4e89dadd61ffd3b2b69a9b69a4a21b1..493dbba5356dd290d56f4dfa1877490b7f0b52b4 100644 (file)
@@ -31,7 +31,7 @@
 #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
index d77584083bca3fdeb71827c0aad2550330bb112a..273de7136dd7e1994d28b93535ef8d095f9a1299 100644 (file)
@@ -28,7 +28,7 @@
 #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);
@@ -41,23 +41,11 @@ static JSValueRef JSNodeListPrototype_item(JSContextRef context, JSObjectRef obj
     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);
@@ -108,8 +96,9 @@ static JSClassRef JSNodeList_class(JSContextRef 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;
@@ -120,17 +109,7 @@ static JSClassRef JSNodeList_class(JSContextRef context)
     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);
 }
index 691750ac517fbc30246043cd85ec2cb3936c8bc7..8d6a211d186b1299e615c4c82d2f15148471d8be 100644 (file)
@@ -43,7 +43,10 @@ using namespace KJS;
 
 JSClassRef JSClassCreate(JSClassDefinition* definition)
 {
-    JSClassRef jsClass = new OpaqueJSClass(definition);
+    JSClassRef jsClass = (definition->attributes & kJSClassAttributeNoPrototype)
+        ? OpaqueJSClass::createNoPrototype(definition)
+        : OpaqueJSClass::create(definition);
+    
     return JSClassRetain(jsClass);
 }
 
@@ -59,12 +62,19 @@ void JSClassRelease(JSClassRef 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;
 
@@ -77,7 +87,7 @@ JSObjectRef JSObjectMakeWithData(JSContextRef ctx, JSClassRef jsClass, JSValueRe
     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)
@@ -89,16 +99,16 @@ JSObjectRef JSObjectMakeFunctionWithCallback(JSContextRef ctx, JSStringRef name,
     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);
 }
index c91f43746052d8764b3c63e51e6c73808f5da439..30372b8c2dea666457a529dfdebbf170928dea86 100644 (file)
@@ -57,6 +57,22 @@ enum {
 */
 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.
@@ -160,22 +176,17 @@ typedef bool
 
 /*! 
 @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);
@@ -194,7 +205,7 @@ typedef void
 
 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.
 */
@@ -214,7 +225,7 @@ typedef JSValueRef
 
 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.
 */
@@ -291,8 +302,9 @@ typedef struct {
 
 /*!
 @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.
@@ -303,12 +315,12 @@ typedef struct {
 @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:
 
@@ -317,12 +329,13 @@ JSStaticValue StaticValueArray[] = {
     { 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;
@@ -344,21 +357,21 @@ typedef struct {
 } 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);
 
@@ -369,6 +382,7 @@ JSClassRef JSClassCreate(JSClassDefinition* definition);
 @result A JSClass that is the same as jsClass.
 */
 JSClassRef JSClassRetain(JSClassRef jsClass);
+
 /*!
 @function
 @abstract Releases a JavaScript class.
@@ -381,26 +395,31 @@ void JSClassRelease(JSClassRef jsClass);
 @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
@@ -414,13 +433,14 @@ JSObjectRef JSObjectMakeFunctionWithCallback(JSContextRef ctx, JSStringRef name,
 
 /*!
 @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
@@ -443,7 +463,7 @@ JSObjectRef JSObjectMakeFunction(JSContextRef ctx, JSStringRef name, unsigned pa
 @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);
 
@@ -504,10 +524,10 @@ bool JSObjectDeleteProperty(JSContextRef ctx, JSObjectRef object, JSStringRef pr
 @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);
 
@@ -519,7 +539,7 @@ JSValueRef JSObjectGetPropertyAtIndex(JSContextRef ctx, JSObjectRef object, unsi
 @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);
 
@@ -534,9 +554,9 @@ void* JSObjectGetPrivate(JSObjectRef object);
 /*!
 @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);
@@ -557,7 +577,7 @@ bool JSObjectIsFunction(JSContextRef ctx, JSObjectRef object);
 @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.
 */
@@ -578,7 +598,7 @@ bool JSObjectIsConstructor(JSContextRef ctx, JSObjectRef object);
 @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.
 */
@@ -586,10 +606,10 @@ JSObjectRef JSObjectCallAsConstructor(JSContextRef ctx, JSObjectRef object, size
 
 /*!
 @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);
 
@@ -610,26 +630,26 @@ void JSPropertyNameArrayRelease(JSPropertyNameArrayRef array);
 
 /*!
 @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);
 
index 3fb2ec4034c57d60e9248d5ef625655b875b2833..100e52292999222c8af4766d0493762ead1686d0 100644 (file)
@@ -150,7 +150,7 @@ bool JSStringIsEqualToUTF8CString(JSStringRef a, const char* b);
 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.
index fc75b2272b9632e4e3f4a22df6cd2fe44008ee5e..7046d4940dffce44f87abb91361995e81f8cfded 100644 (file)
@@ -119,12 +119,11 @@ bool JSValueIsObject(JSContextRef ctx, JSValueRef value);
 
 /*!
 @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);
 
@@ -252,11 +251,12 @@ JSObjectRef JSValueToObject(JSContextRef ctx, JSValueRef value, JSValueRef* exce
 // 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);
 
index 9f535e464d53614af23ed97118e9ad9e949cc98c..b2b3307d0c0286a7c7761ff967f0e06560e27c26 100644 (file)
@@ -44,7 +44,7 @@ int main(int argc, char* argv[])
     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");
index 3a058c0d589d8e9b5d8b9b677f1ec6cd2b3bd3b8..7624d397e0ced8909cb8e20ca72d601775ab5586 100644 (file)
  * 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;
@@ -247,55 +106,6 @@ function test()
     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();
index 582654f01219fcf6178dc50620442caf5a516c22..040c9244d224a5368541af39a509fe9aca633b9f 100644 (file)
@@ -33,6 +33,7 @@
 
 #include <assert.h>
 #include <math.h>
+#include <setjmp.h>
 
 static JSGlobalContextRef context = 0;
 
@@ -99,14 +100,6 @@ static JSValueRef jsGlobalValue; // non-stack value for testing JSValueProtect()
 
 /* 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);
@@ -241,14 +234,6 @@ static JSValueRef MyObject_convertToType(JSContextRef context, JSObjectRef objec
     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 }
@@ -261,6 +246,7 @@ static JSStaticFunction evilStaticFunctions[] = {
 
 JSClassDefinition MyObject_definition = {
     0,
+    kJSClassAttributeNone,
     
     "MyObject",
     NULL,
@@ -268,8 +254,8 @@ JSClassDefinition MyObject_definition = {
     evilStaticValues,
     evilStaticFunctions,
     
-    MyObject_initialize,
-    MyObject_finalize,
+    NULL,
+    NULL,
     MyObject_hasProperty,
     MyObject_getProperty,
     MyObject_setProperty,
@@ -290,24 +276,74 @@ static JSClassRef MyObject_class(JSContextRef context)
     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);
@@ -315,24 +351,75 @@ static JSClassRef Base_class(JSContextRef context)
     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);
@@ -373,11 +460,27 @@ static JSObjectRef myConstructor_callAsConstructor(JSContextRef context, JSObjec
 
 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);
@@ -390,7 +493,7 @@ int main(int argc, char* argv[])
     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("");
@@ -445,7 +548,6 @@ int main(int argc, char* argv[])
 #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);
@@ -659,13 +761,18 @@ int main(int argc, char* argv[])
     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);
@@ -677,10 +784,15 @@ int main(int argc, char* argv[])
     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);
@@ -689,10 +801,6 @@ int main(int argc, char* argv[])
     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);
@@ -709,13 +817,6 @@ int main(int argc, char* argv[])
     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__)
@@ -728,6 +829,8 @@ int main(int argc, char* argv[])
     JSStringRelease(badSyntax);
     
     JSGlobalContextRelease(context);
+    JSGarbageCollect(context);
+
     printf("PASS: Program exited normally.\n");
     return 0;
 }
index 0f8d75341fe51dbd502a97dd8018a84dd7ab1a12..b64c532c6d99df8b02331f0da05b03421a3dfef4 100644 (file)
@@ -105,3 +105,20 @@ shouldThrow("MyObject.nullGetSet = 1");
 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);
index 786969d589acf9236686e17f3a5c8558a3405b98..f668ca64f634bc11ef2e181596c877532c03dc13 100644 (file)
@@ -1,3 +1,43 @@
+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.
index 37e8b875db7b817968cb5b043e3dd006d7444a1e..cbdcb2eccfe940638f18625055b7aa2fe2de66b7 100644 (file)
@@ -20,10 +20,11 @@ _JSObjectHasProperty
 _JSObjectIsConstructor
 _JSObjectIsFunction
 _JSObjectMake
-_JSObjectMakeConstructorWithCallback
+_JSObjectMakeConstructor
+_JSObjectMakeFunction
 _JSObjectMakeFunction
 _JSObjectMakeFunctionWithCallback
-_JSObjectMakeWithData
+_JSObjectMakeWithPrototype
 _JSObjectSetPrivate
 _JSObjectSetProperty
 _JSObjectSetPropertyAtIndex
@@ -260,7 +261,7 @@ __ZNK3KJS8JSObject9toBooleanEPNS_9ExecStateE
 __ZNK3KJS9ExecState18lexicalInterpreterEv
 __ZTVN3KJS19InternalFunctionImpE
 __ZTVN3KJS8JSObjectE
-_kJSClassDefinitionNull
+_kJSClassDefinitionEmpty
 _kjs_pcre_compile
 _kjs_pcre_exec
 _kjs_pcre_free