• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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