]> git.saurik.com Git - apple/javascriptcore.git/blobdiff - API/JSClassRef.cpp
JavaScriptCore-1097.3.tar.gz
[apple/javascriptcore.git] / API / JSClassRef.cpp
index 98aaaf5290350574b200c388c4de05d25ea11f5b..08fa5c5e0b0084173e0dcb2df8e49cb7076f6214 100644 (file)
@@ -1,4 +1,3 @@
-// -*- mode: c++; c-basic-offset: 4 -*-
 /*
  * Copyright (C) 2006, 2007 Apple Inc.  All rights reserved.
  *
 #include "APICast.h"
 #include "JSCallbackObject.h"
 #include "JSObjectRef.h"
-#include <kjs/JSGlobalObject.h>
-#include <kjs/identifier.h>
-#include <kjs/object_object.h>
+#include <runtime/InitializeThreading.h>
+#include <runtime/JSGlobalObject.h>
+#include <runtime/ObjectPrototype.h>
+#include <runtime/Identifier.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 };
 
+static inline UString tryCreateStringFromUTF8(const char* string)
+{
+    if (!string)
+        return UString();
+
+    size_t length = strlen(string);
+    Vector<UChar, 1024> buffer(length);
+    UChar* p = buffer.data();
+    if (conversionOK != convertUTF8ToUTF16(&string, string + length, &p, p + length))
+        return UString();
+
+    return UString(buffer.data(), p - buffer.data());
+}
+
 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 +70,26 @@ OpaqueJSClass::OpaqueJSClass(const JSClassDefinition* definition, OpaqueJSClass*
     , callAsConstructor(definition->callAsConstructor)
     , hasInstance(definition->hasInstance)
     , convertToType(definition->convertToType)
-    , cachedPrototype(0)
+    , m_className(tryCreateStringFromUTF8(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));
+            UString valueName = tryCreateStringFromUTF8(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));
+            UString functionName = tryCreateStringFromUTF8(staticFunction->name);
+            if (!functionName.isNull())
+                m_staticFunctions->set(functionName.impl(), adoptPtr(new StaticFunctionEntry(staticFunction->callAsFunction, staticFunction->attributes)));
             ++staticFunction;
         }
     }
@@ -84,48 +100,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->first->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->first->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::JSGlobalData&, 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->first->isIdentifier());
+            staticValues->add(StringImpl::create(it->first->characters(), it->first->length()), adoptPtr(new StaticValueEntry(it->second->getProperty, it->second->setProperty, it->second->attributes)));
+        }
+    }
+
+    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->first->isIdentifier());
+            staticFunctions->add(StringImpl::create(it->first->characters(), it->first->length()), adoptPtr(new StaticFunctionEntry(it->second->callAsFunction, it->second->attributes)));
+        }
     }
+}
 
-    return new OpaqueJSClass(definition, 0);
+OpaqueJSClassContextData& OpaqueJSClass::contextData(ExecState* exec)
+{
+    OwnPtr<OpaqueJSClassContextData>& contextData = exec->globalData().opaqueJSClassData.add(this, nullptr).iterator->second;
+    if (!contextData)
+        contextData = adoptPtr(new OpaqueJSClassContextData(exec->globalData(), this));
+    return *contextData;
+}
+
+UString OpaqueJSClass::className()
+{
+    // Make a deep copy, so that the caller has no chance to put the original into IdentifierTable.
+    return UString(m_className.characters(), m_className.length());
+}
+
+OpaqueJSClassStaticValuesTable* OpaqueJSClass::staticValues(JSC::ExecState* exec)
+{
+    return contextData(exec).staticValues.get();
+}
+
+OpaqueJSClassStaticFunctionsTable* OpaqueJSClass::staticFunctions(JSC::ExecState* exec)
+{
+    return contextData(exec).staticFunctions.get();
 }
 
 /*!
@@ -136,7 +194,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 +203,19 @@ JSObject* OpaqueJSClass::prototype(JSContextRef ctx)
      *       |        |          |
      *  DerivedClass  |  DerivedClassPrototype
      */
-    
+
     if (!prototypeClass)
         return 0;
-    
-    ExecState* exec = toJS(ctx);
-    
-    if (!cachedPrototype) {
+
+    OpaqueJSClassContextData& jsClassData = contextData(exec);
+
+    if (!jsClassData.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
+        jsClassData.cachedPrototype = PassWeak<JSObject>(JSCallbackObject<JSNonFinalObject>::create(exec, exec->lexicalGlobalObject(), exec->lexicalGlobalObject()->callbackObjectStructure(), prototypeClass, &jsClassData), 0); // set jsClassData as the object's private data, so it can clear our reference on destruction
+        if (parentClass) {
+            if (JSObject* prototype = parentClass->prototype(exec))
+                jsClassData.cachedPrototype->setPrototype(exec->globalData(), prototype);
+        }
     }
-    return cachedPrototype;
+    return jsClassData.cachedPrototype.get();
 }