]> git.saurik.com Git - apple/javascriptcore.git/blobdiff - API/JSClassRef.cpp
JavaScriptCore-1218.tar.gz
[apple/javascriptcore.git] / API / JSClassRef.cpp
index 98aaaf5290350574b200c388c4de05d25ea11f5b..c77f63cf905661920a456071d18e18667478f1d8 100644 (file)
@@ -1,4 +1,3 @@
-// -*- mode: c++; c-basic-offset: 4 -*-
 /*
  * Copyright (C) 2006, 2007 Apple Inc.  All rights reserved.
  *
 #include "JSClassRef.h"
 
 #include "APICast.h"
+#include "Identifier.h"
+#include "InitializeThreading.h"
 #include "JSCallbackObject.h"
+#include "JSGlobalObject.h"
 #include "JSObjectRef.h"
-#include <kjs/JSGlobalObject.h>
-#include <kjs/identifier.h>
-#include <kjs/object_object.h>
+#include "ObjectPrototype.h"
+#include "Operations.h"
+#include <wtf/text/StringHash.h>
+#include <wtf/unicode/UTF8.h>
 
-using namespace KJS;
+using namespace std;
+using namespace JSC;
+using namespace WTF::Unicode;
 
 const JSClassDefinition kJSClassDefinitionEmpty = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
 
 OpaqueJSClass::OpaqueJSClass(const JSClassDefinition* definition, OpaqueJSClass* protoClass) 
-    // FIXME: <rdar://problem/4949018>
-    : className(definition->className)
-    , parentClass(definition->parentClass)
+    : parentClass(definition->parentClass)
     , prototypeClass(0)
-    , staticValues(0)
-    , staticFunctions(0)
     , initialize(definition->initialize)
     , finalize(definition->finalize)
     , hasProperty(definition->hasProperty)
@@ -56,24 +57,26 @@ OpaqueJSClass::OpaqueJSClass(const JSClassDefinition* definition, OpaqueJSClass*
     , callAsConstructor(definition->callAsConstructor)
     , hasInstance(definition->hasInstance)
     , convertToType(definition->convertToType)
-    , cachedPrototype(0)
+    , m_className(String::fromUTF8(definition->className))
 {
+    initializeThreading();
+
     if (const JSStaticValue* staticValue = definition->staticValues) {
-        staticValues = new StaticValuesTable();
+        m_staticValues = adoptPtr(new OpaqueJSClassStaticValuesTable);
         while (staticValue->name) {
-            // FIXME: <rdar://problem/4949018>
-            staticValues->add(Identifier(staticValue->name).ustring().rep(), 
-                              new StaticValueEntry(staticValue->getProperty, staticValue->setProperty, staticValue->attributes));
+            String valueName = String::fromUTF8(staticValue->name);
+            if (!valueName.isNull())
+                m_staticValues->set(valueName.impl(), adoptPtr(new StaticValueEntry(staticValue->getProperty, staticValue->setProperty, staticValue->attributes)));
             ++staticValue;
         }
     }
-    
+
     if (const JSStaticFunction* staticFunction = definition->staticFunctions) {
-        staticFunctions = new StaticFunctionsTable();
+        m_staticFunctions = adoptPtr(new OpaqueJSClassStaticFunctionsTable);
         while (staticFunction->name) {
-            // FIXME: <rdar://problem/4949018>
-            staticFunctions->add(Identifier(staticFunction->name).ustring().rep(), 
-                                 new StaticFunctionEntry(staticFunction->callAsFunction, staticFunction->attributes));
+            String functionName = String::fromUTF8(staticFunction->name);
+            if (!functionName.isNull())
+                m_staticFunctions->set(functionName.impl(), adoptPtr(new StaticFunctionEntry(staticFunction->callAsFunction, staticFunction->attributes)));
             ++staticFunction;
         }
     }
@@ -84,48 +87,90 @@ OpaqueJSClass::OpaqueJSClass(const JSClassDefinition* definition, OpaqueJSClass*
 
 OpaqueJSClass::~OpaqueJSClass()
 {
-    if (staticValues) {
-        deleteAllValues(*staticValues);
-        delete staticValues;
+    // The empty string is shared across threads & is an identifier, in all other cases we should have done a deep copy in className(), below. 
+    ASSERT(!m_className.length() || !m_className.impl()->isIdentifier());
+
+#ifndef NDEBUG
+    if (m_staticValues) {
+        OpaqueJSClassStaticValuesTable::const_iterator end = m_staticValues->end();
+        for (OpaqueJSClassStaticValuesTable::const_iterator it = m_staticValues->begin(); it != end; ++it)
+            ASSERT(!it->key->isIdentifier());
     }
 
-    if (staticFunctions) {
-        deleteAllValues(*staticFunctions);
-        delete staticFunctions;
+    if (m_staticFunctions) {
+        OpaqueJSClassStaticFunctionsTable::const_iterator end = m_staticFunctions->end();
+        for (OpaqueJSClassStaticFunctionsTable::const_iterator it = m_staticFunctions->begin(); it != end; ++it)
+            ASSERT(!it->key->isIdentifier());
     }
+#endif
     
     if (prototypeClass)
         JSClassRelease(prototypeClass);
 }
 
-JSClassRef OpaqueJSClass::createNoAutomaticPrototype(const JSClassDefinition* definition)
+PassRefPtr<OpaqueJSClass> OpaqueJSClass::createNoAutomaticPrototype(const JSClassDefinition* definition)
 {
-    return new OpaqueJSClass(definition, 0);
+    return adoptRef(new OpaqueJSClass(definition, 0));
 }
 
-void clearReferenceToPrototype(JSObjectRef prototype)
+PassRefPtr<OpaqueJSClass> OpaqueJSClass::create(const JSClassDefinition* clientDefinition)
 {
-    OpaqueJSClass* jsClass = static_cast<OpaqueJSClass*>(JSObjectGetPrivate(prototype));
-    ASSERT(jsClass);
-    jsClass->cachedPrototype = 0;
+    JSClassDefinition definition = *clientDefinition; // Avoid modifying client copy.
+
+    JSClassDefinition protoDefinition = kJSClassDefinitionEmpty;
+    protoDefinition.finalize = 0;
+    swap(definition.staticFunctions, protoDefinition.staticFunctions); // Move static functions to the prototype.
+    
+    // We are supposed to use JSClassRetain/Release but since we know that we currently have
+    // the only reference to this class object we cheat and use a RefPtr instead.
+    RefPtr<OpaqueJSClass> protoClass = adoptRef(new OpaqueJSClass(&protoDefinition, 0));
+    return adoptRef(new OpaqueJSClass(&definition, protoClass.get()));
 }
 
-JSClassRef OpaqueJSClass::create(const JSClassDefinition* definition)
+OpaqueJSClassContextData::OpaqueJSClassContextData(JSC::VM&, OpaqueJSClass* jsClass)
+    : m_class(jsClass)
 {
-    if (const 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 class
-        JSClassDefinition objectDefinition = *definition;
-        objectDefinition.staticFunctions = 0;
-        return new OpaqueJSClass(&objectDefinition, protoClass);
+    if (jsClass->m_staticValues) {
+        staticValues = adoptPtr(new OpaqueJSClassStaticValuesTable);
+        OpaqueJSClassStaticValuesTable::const_iterator end = jsClass->m_staticValues->end();
+        for (OpaqueJSClassStaticValuesTable::const_iterator it = jsClass->m_staticValues->begin(); it != end; ++it) {
+            ASSERT(!it->key->isIdentifier());
+            staticValues->add(it->key->isolatedCopy(), adoptPtr(new StaticValueEntry(it->value->getProperty, it->value->setProperty, it->value->attributes)));
+        }
     }
 
-    return new OpaqueJSClass(definition, 0);
+    if (jsClass->m_staticFunctions) {
+        staticFunctions = adoptPtr(new OpaqueJSClassStaticFunctionsTable);
+        OpaqueJSClassStaticFunctionsTable::const_iterator end = jsClass->m_staticFunctions->end();
+        for (OpaqueJSClassStaticFunctionsTable::const_iterator it = jsClass->m_staticFunctions->begin(); it != end; ++it) {
+            ASSERT(!it->key->isIdentifier());
+            staticFunctions->add(it->key->isolatedCopy(), adoptPtr(new StaticFunctionEntry(it->value->callAsFunction, it->value->attributes)));
+        }
+    }
+}
+
+OpaqueJSClassContextData& OpaqueJSClass::contextData(ExecState* exec)
+{
+    OwnPtr<OpaqueJSClassContextData>& contextData = exec->lexicalGlobalObject()->opaqueJSClassData().add(this, nullptr).iterator->value;
+    if (!contextData)
+        contextData = adoptPtr(new OpaqueJSClassContextData(exec->vm(), this));
+    return *contextData;
+}
+
+String OpaqueJSClass::className()
+{
+    // Make a deep copy, so that the caller has no chance to put the original into IdentifierTable.
+    return m_className.isolatedCopy();
+}
+
+OpaqueJSClassStaticValuesTable* OpaqueJSClass::staticValues(JSC::ExecState* exec)
+{
+    return contextData(exec).staticValues.get();
+}
+
+OpaqueJSClassStaticFunctionsTable* OpaqueJSClass::staticFunctions(JSC::ExecState* exec)
+{
+    return contextData(exec).staticFunctions.get();
 }
 
 /*!
@@ -136,7 +181,7 @@ JSClassRef OpaqueJSClass::create(const JSClassDefinition* definition)
  @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)
+JSObject* OpaqueJSClass::prototype(ExecState* exec)
 {
     /* Class (C++) and prototype (JS) inheritance are parallel, so:
      *     (C++)      |        (JS)
@@ -145,20 +190,22 @@ JSObject* OpaqueJSClass::prototype(JSContextRef ctx)
      *       |        |          |
      *  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->dynamicGlobalObject()->objectPrototype();
-        cachedPrototype = new JSCallbackObject<JSObject>(exec, prototypeClass, parentPrototype, this); // set ourself as the object's private data, so it can clear our reference on destruction
+
+    OpaqueJSClassContextData& jsClassData = contextData(exec);
+
+    if (JSObject* prototype = jsClassData.cachedPrototype.get())
+        return prototype;
+
+    // Recursive, but should be good enough for our purposes
+    JSObject* prototype = JSCallbackObject<JSDestructibleObject>::create(exec, exec->lexicalGlobalObject(), exec->lexicalGlobalObject()->callbackObjectStructure(), prototypeClass, &jsClassData); // set jsClassData as the object's private data, so it can clear our reference on destruction
+    if (parentClass) {
+        if (JSObject* parentPrototype = parentClass->prototype(exec))
+            prototype->setPrototype(exec->vm(), parentPrototype);
     }
-    return cachedPrototype;
+
+    jsClassData.cachedPrototype = PassWeak<JSObject>(prototype);
+    return prototype;
 }