1 /* 2 * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #include "config.h" 27 #include "JSClassRef.h" 28 29 #include "APICast.h" 30 #include "JSCallbackObject.h" 31 #include "JSObjectRef.h" 32 #include <runtime/InitializeThreading.h> 33 #include <runtime/JSGlobalObject.h> 34 #include <runtime/ObjectPrototype.h> 35 #include <runtime/Identifier.h> 36 37 using namespace JSC; 38 39 const JSClassDefinition kJSClassDefinitionEmpty = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; 40 OpaqueJSClass(const JSClassDefinition * definition,OpaqueJSClass * protoClass)41 OpaqueJSClass::OpaqueJSClass(const JSClassDefinition* definition, OpaqueJSClass* protoClass) 42 : parentClass(definition->parentClass) 43 , prototypeClass(0) 44 , initialize(definition->initialize) 45 , finalize(definition->finalize) 46 , hasProperty(definition->hasProperty) 47 , getProperty(definition->getProperty) 48 , setProperty(definition->setProperty) 49 , deleteProperty(definition->deleteProperty) 50 , getPropertyNames(definition->getPropertyNames) 51 , callAsFunction(definition->callAsFunction) 52 , callAsConstructor(definition->callAsConstructor) 53 , hasInstance(definition->hasInstance) 54 , convertToType(definition->convertToType) 55 , m_className(UString::Rep::createFromUTF8(definition->className)) 56 , m_staticValues(0) 57 , m_staticFunctions(0) 58 { 59 initializeThreading(); 60 61 if (const JSStaticValue* staticValue = definition->staticValues) { 62 m_staticValues = new OpaqueJSClassStaticValuesTable(); 63 while (staticValue->name) { 64 m_staticValues->add(UString::Rep::createFromUTF8(staticValue->name), 65 new StaticValueEntry(staticValue->getProperty, staticValue->setProperty, staticValue->attributes)); 66 ++staticValue; 67 } 68 } 69 70 if (const JSStaticFunction* staticFunction = definition->staticFunctions) { 71 m_staticFunctions = new OpaqueJSClassStaticFunctionsTable(); 72 while (staticFunction->name) { 73 m_staticFunctions->add(UString::Rep::createFromUTF8(staticFunction->name), 74 new StaticFunctionEntry(staticFunction->callAsFunction, staticFunction->attributes)); 75 ++staticFunction; 76 } 77 } 78 79 if (protoClass) 80 prototypeClass = JSClassRetain(protoClass); 81 } 82 ~OpaqueJSClass()83 OpaqueJSClass::~OpaqueJSClass() 84 { 85 ASSERT(!m_className.rep()->identifierTable()); 86 87 if (m_staticValues) { 88 OpaqueJSClassStaticValuesTable::const_iterator end = m_staticValues->end(); 89 for (OpaqueJSClassStaticValuesTable::const_iterator it = m_staticValues->begin(); it != end; ++it) { 90 ASSERT(!it->first->identifierTable()); 91 delete it->second; 92 } 93 delete m_staticValues; 94 } 95 96 if (m_staticFunctions) { 97 OpaqueJSClassStaticFunctionsTable::const_iterator end = m_staticFunctions->end(); 98 for (OpaqueJSClassStaticFunctionsTable::const_iterator it = m_staticFunctions->begin(); it != end; ++it) { 99 ASSERT(!it->first->identifierTable()); 100 delete it->second; 101 } 102 delete m_staticFunctions; 103 } 104 105 if (prototypeClass) 106 JSClassRelease(prototypeClass); 107 } 108 createNoAutomaticPrototype(const JSClassDefinition * definition)109 PassRefPtr<OpaqueJSClass> OpaqueJSClass::createNoAutomaticPrototype(const JSClassDefinition* definition) 110 { 111 return adoptRef(new OpaqueJSClass(definition, 0)); 112 } 113 clearReferenceToPrototype(JSObjectRef prototype)114 static void clearReferenceToPrototype(JSObjectRef prototype) 115 { 116 OpaqueJSClassContextData* jsClassData = static_cast<OpaqueJSClassContextData*>(JSObjectGetPrivate(prototype)); 117 ASSERT(jsClassData); 118 jsClassData->cachedPrototype = 0; 119 } 120 create(const JSClassDefinition * definition)121 PassRefPtr<OpaqueJSClass> OpaqueJSClass::create(const JSClassDefinition* definition) 122 { 123 if (const JSStaticFunction* staticFunctions = definition->staticFunctions) { 124 // copy functions into a prototype class 125 JSClassDefinition protoDefinition = kJSClassDefinitionEmpty; 126 protoDefinition.staticFunctions = staticFunctions; 127 protoDefinition.finalize = clearReferenceToPrototype; 128 129 // We are supposed to use JSClassRetain/Release but since we know that we currently have 130 // the only reference to this class object we cheat and use a RefPtr instead. 131 RefPtr<OpaqueJSClass> protoClass = adoptRef(new OpaqueJSClass(&protoDefinition, 0)); 132 133 // remove functions from the original class 134 JSClassDefinition objectDefinition = *definition; 135 objectDefinition.staticFunctions = 0; 136 137 return adoptRef(new OpaqueJSClass(&objectDefinition, protoClass.get())); 138 } 139 140 return adoptRef(new OpaqueJSClass(definition, 0)); 141 } 142 OpaqueJSClassContextData(OpaqueJSClass * jsClass)143 OpaqueJSClassContextData::OpaqueJSClassContextData(OpaqueJSClass* jsClass) 144 : m_class(jsClass) 145 , cachedPrototype(0) 146 { 147 if (jsClass->m_staticValues) { 148 staticValues = new OpaqueJSClassStaticValuesTable; 149 OpaqueJSClassStaticValuesTable::const_iterator end = jsClass->m_staticValues->end(); 150 for (OpaqueJSClassStaticValuesTable::const_iterator it = jsClass->m_staticValues->begin(); it != end; ++it) { 151 ASSERT(!it->first->identifierTable()); 152 staticValues->add(UString::Rep::createCopying(it->first->data(), it->first->size()), 153 new StaticValueEntry(it->second->getProperty, it->second->setProperty, it->second->attributes)); 154 } 155 156 } else 157 staticValues = 0; 158 159 160 if (jsClass->m_staticFunctions) { 161 staticFunctions = new OpaqueJSClassStaticFunctionsTable; 162 OpaqueJSClassStaticFunctionsTable::const_iterator end = jsClass->m_staticFunctions->end(); 163 for (OpaqueJSClassStaticFunctionsTable::const_iterator it = jsClass->m_staticFunctions->begin(); it != end; ++it) { 164 ASSERT(!it->first->identifierTable()); 165 staticFunctions->add(UString::Rep::createCopying(it->first->data(), it->first->size()), 166 new StaticFunctionEntry(it->second->callAsFunction, it->second->attributes)); 167 } 168 169 } else 170 staticFunctions = 0; 171 } 172 ~OpaqueJSClassContextData()173 OpaqueJSClassContextData::~OpaqueJSClassContextData() 174 { 175 if (staticValues) { 176 deleteAllValues(*staticValues); 177 delete staticValues; 178 } 179 180 if (staticFunctions) { 181 deleteAllValues(*staticFunctions); 182 delete staticFunctions; 183 } 184 } 185 contextData(ExecState * exec)186 OpaqueJSClassContextData& OpaqueJSClass::contextData(ExecState* exec) 187 { 188 OpaqueJSClassContextData*& contextData = exec->globalData().opaqueJSClassData.add(this, 0).first->second; 189 if (!contextData) 190 contextData = new OpaqueJSClassContextData(this); 191 return *contextData; 192 } 193 className()194 UString OpaqueJSClass::className() 195 { 196 // Make a deep copy, so that the caller has no chance to put the original into IdentifierTable. 197 return UString(m_className.data(), m_className.size()); 198 } 199 staticValues(JSC::ExecState * exec)200 OpaqueJSClassStaticValuesTable* OpaqueJSClass::staticValues(JSC::ExecState* exec) 201 { 202 OpaqueJSClassContextData& jsClassData = contextData(exec); 203 return jsClassData.staticValues; 204 } 205 staticFunctions(JSC::ExecState * exec)206 OpaqueJSClassStaticFunctionsTable* OpaqueJSClass::staticFunctions(JSC::ExecState* exec) 207 { 208 OpaqueJSClassContextData& jsClassData = contextData(exec); 209 return jsClassData.staticFunctions; 210 } 211 212 /*! 213 // Doc here in case we make this public. (Hopefully we won't.) 214 @function 215 @abstract Returns the prototype that will be used when constructing an object with a given class. 216 @param ctx The execution context to use. 217 @param jsClass A JSClass whose prototype you want to get. 218 @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. 219 */ prototype(ExecState * exec)220 JSObject* OpaqueJSClass::prototype(ExecState* exec) 221 { 222 /* Class (C++) and prototype (JS) inheritance are parallel, so: 223 * (C++) | (JS) 224 * ParentClass | ParentClassPrototype 225 * ^ | ^ 226 * | | | 227 * DerivedClass | DerivedClassPrototype 228 */ 229 230 if (!prototypeClass) 231 return 0; 232 233 OpaqueJSClassContextData& jsClassData = contextData(exec); 234 235 if (!jsClassData.cachedPrototype) { 236 // Recursive, but should be good enough for our purposes 237 jsClassData.cachedPrototype = new (exec) JSCallbackObject<JSObject>(exec, exec->lexicalGlobalObject()->callbackObjectStructure(), prototypeClass, &jsClassData); // set jsClassData as the object's private data, so it can clear our reference on destruction 238 if (parentClass) { 239 if (JSObject* prototype = parentClass->prototype(exec)) 240 jsClassData.cachedPrototype->setPrototype(prototype); 241 } 242 } 243 return jsClassData.cachedPrototype; 244 } 245