• 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 #include <wtf/text/StringHash.h>
37 #include <wtf/unicode/UTF8.h>
38 
39 using namespace std;
40 using namespace JSC;
41 using namespace WTF::Unicode;
42 
43 const JSClassDefinition kJSClassDefinitionEmpty = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
44 
tryCreateStringFromUTF8(const char * string)45 static inline UString tryCreateStringFromUTF8(const char* string)
46 {
47     if (!string)
48         return UString();
49 
50     size_t length = strlen(string);
51     Vector<UChar, 1024> buffer(length);
52     UChar* p = buffer.data();
53     if (conversionOK != convertUTF8ToUTF16(&string, string + length, &p, p + length))
54         return UString();
55 
56     return UString(buffer.data(), p - buffer.data());
57 }
58 
OpaqueJSClass(const JSClassDefinition * definition,OpaqueJSClass * protoClass)59 OpaqueJSClass::OpaqueJSClass(const JSClassDefinition* definition, OpaqueJSClass* protoClass)
60     : parentClass(definition->parentClass)
61     , prototypeClass(0)
62     , initialize(definition->initialize)
63     , finalize(definition->finalize)
64     , hasProperty(definition->hasProperty)
65     , getProperty(definition->getProperty)
66     , setProperty(definition->setProperty)
67     , deleteProperty(definition->deleteProperty)
68     , getPropertyNames(definition->getPropertyNames)
69     , callAsFunction(definition->callAsFunction)
70     , callAsConstructor(definition->callAsConstructor)
71     , hasInstance(definition->hasInstance)
72     , convertToType(definition->convertToType)
73     , m_className(tryCreateStringFromUTF8(definition->className))
74     , m_staticValues(0)
75     , m_staticFunctions(0)
76 {
77     initializeThreading();
78 
79     if (const JSStaticValue* staticValue = definition->staticValues) {
80         m_staticValues = new OpaqueJSClassStaticValuesTable();
81         while (staticValue->name) {
82             UString valueName = tryCreateStringFromUTF8(staticValue->name);
83             if (!valueName.isNull()) {
84                 // Use a local variable here to sidestep an RVCT compiler bug.
85                 StaticValueEntry* entry = new StaticValueEntry(staticValue->getProperty, staticValue->setProperty, staticValue->attributes);
86                 StringImpl* impl = valueName.impl();
87                 StaticValueEntry* existingEntry = m_staticValues->get(impl);
88                 m_staticValues->set(impl, entry);
89                 delete existingEntry;
90             }
91             ++staticValue;
92         }
93     }
94 
95     if (const JSStaticFunction* staticFunction = definition->staticFunctions) {
96         m_staticFunctions = new OpaqueJSClassStaticFunctionsTable();
97         while (staticFunction->name) {
98             UString functionName = tryCreateStringFromUTF8(staticFunction->name);
99             if (!functionName.isNull()) {
100                 // Use a local variable here to sidestep an RVCT compiler bug.
101                 StaticFunctionEntry* entry = new StaticFunctionEntry(staticFunction->callAsFunction, staticFunction->attributes);
102                 StringImpl* impl = functionName.impl();
103                 StaticFunctionEntry* existingEntry = m_staticFunctions->get(impl);
104                 m_staticFunctions->set(impl, entry);
105                 delete existingEntry;
106             }
107             ++staticFunction;
108         }
109     }
110 
111     if (protoClass)
112         prototypeClass = JSClassRetain(protoClass);
113 }
114 
~OpaqueJSClass()115 OpaqueJSClass::~OpaqueJSClass()
116 {
117     // The empty string is shared across threads & is an identifier, in all other cases we should have done a deep copy in className(), below.
118     ASSERT(!m_className.length() || !m_className.impl()->isIdentifier());
119 
120     if (m_staticValues) {
121         OpaqueJSClassStaticValuesTable::const_iterator end = m_staticValues->end();
122         for (OpaqueJSClassStaticValuesTable::const_iterator it = m_staticValues->begin(); it != end; ++it) {
123             ASSERT(!it->first->isIdentifier());
124             delete it->second;
125         }
126         delete m_staticValues;
127     }
128 
129     if (m_staticFunctions) {
130         OpaqueJSClassStaticFunctionsTable::const_iterator end = m_staticFunctions->end();
131         for (OpaqueJSClassStaticFunctionsTable::const_iterator it = m_staticFunctions->begin(); it != end; ++it) {
132             ASSERT(!it->first->isIdentifier());
133             delete it->second;
134         }
135         delete m_staticFunctions;
136     }
137 
138     if (prototypeClass)
139         JSClassRelease(prototypeClass);
140 }
141 
createNoAutomaticPrototype(const JSClassDefinition * definition)142 PassRefPtr<OpaqueJSClass> OpaqueJSClass::createNoAutomaticPrototype(const JSClassDefinition* definition)
143 {
144     return adoptRef(new OpaqueJSClass(definition, 0));
145 }
146 
create(const JSClassDefinition * clientDefinition)147 PassRefPtr<OpaqueJSClass> OpaqueJSClass::create(const JSClassDefinition* clientDefinition)
148 {
149     JSClassDefinition definition = *clientDefinition; // Avoid modifying client copy.
150 
151     JSClassDefinition protoDefinition = kJSClassDefinitionEmpty;
152     protoDefinition.finalize = 0;
153     swap(definition.staticFunctions, protoDefinition.staticFunctions); // Move static functions to the prototype.
154 
155     // We are supposed to use JSClassRetain/Release but since we know that we currently have
156     // the only reference to this class object we cheat and use a RefPtr instead.
157     RefPtr<OpaqueJSClass> protoClass = adoptRef(new OpaqueJSClass(&protoDefinition, 0));
158     return adoptRef(new OpaqueJSClass(&definition, protoClass.get()));
159 }
160 
OpaqueJSClassContextData(JSC::JSGlobalData &,OpaqueJSClass * jsClass)161 OpaqueJSClassContextData::OpaqueJSClassContextData(JSC::JSGlobalData&, OpaqueJSClass* jsClass)
162     : m_class(jsClass)
163 {
164     if (jsClass->m_staticValues) {
165         staticValues = new OpaqueJSClassStaticValuesTable;
166         OpaqueJSClassStaticValuesTable::const_iterator end = jsClass->m_staticValues->end();
167         for (OpaqueJSClassStaticValuesTable::const_iterator it = jsClass->m_staticValues->begin(); it != end; ++it) {
168             ASSERT(!it->first->isIdentifier());
169             // Use a local variable here to sidestep an RVCT compiler bug.
170             StaticValueEntry* entry = new StaticValueEntry(it->second->getProperty, it->second->setProperty, it->second->attributes);
171             staticValues->add(StringImpl::create(it->first->characters(), it->first->length()), entry);
172         }
173     } else
174         staticValues = 0;
175 
176     if (jsClass->m_staticFunctions) {
177         staticFunctions = new OpaqueJSClassStaticFunctionsTable;
178         OpaqueJSClassStaticFunctionsTable::const_iterator end = jsClass->m_staticFunctions->end();
179         for (OpaqueJSClassStaticFunctionsTable::const_iterator it = jsClass->m_staticFunctions->begin(); it != end; ++it) {
180             ASSERT(!it->first->isIdentifier());
181             // Use a local variable here to sidestep an RVCT compiler bug.
182             StaticFunctionEntry* entry = new StaticFunctionEntry(it->second->callAsFunction, it->second->attributes);
183             staticFunctions->add(StringImpl::create(it->first->characters(), it->first->length()), entry);
184         }
185 
186     } else
187         staticFunctions = 0;
188 }
189 
~OpaqueJSClassContextData()190 OpaqueJSClassContextData::~OpaqueJSClassContextData()
191 {
192     if (staticValues) {
193         deleteAllValues(*staticValues);
194         delete staticValues;
195     }
196 
197     if (staticFunctions) {
198         deleteAllValues(*staticFunctions);
199         delete staticFunctions;
200     }
201 }
202 
contextData(ExecState * exec)203 OpaqueJSClassContextData& OpaqueJSClass::contextData(ExecState* exec)
204 {
205     OpaqueJSClassContextData*& contextData = exec->globalData().opaqueJSClassData.add(this, 0).first->second;
206     if (!contextData)
207         contextData = new OpaqueJSClassContextData(exec->globalData(), this);
208     return *contextData;
209 }
210 
className()211 UString OpaqueJSClass::className()
212 {
213     // Make a deep copy, so that the caller has no chance to put the original into IdentifierTable.
214     return UString(m_className.characters(), m_className.length());
215 }
216 
staticValues(JSC::ExecState * exec)217 OpaqueJSClassStaticValuesTable* OpaqueJSClass::staticValues(JSC::ExecState* exec)
218 {
219     OpaqueJSClassContextData& jsClassData = contextData(exec);
220     return jsClassData.staticValues;
221 }
222 
staticFunctions(JSC::ExecState * exec)223 OpaqueJSClassStaticFunctionsTable* OpaqueJSClass::staticFunctions(JSC::ExecState* exec)
224 {
225     OpaqueJSClassContextData& jsClassData = contextData(exec);
226     return jsClassData.staticFunctions;
227 }
228 
229 /*!
230 // Doc here in case we make this public. (Hopefully we won't.)
231 @function
232  @abstract Returns the prototype that will be used when constructing an object with a given class.
233  @param ctx The execution context to use.
234  @param jsClass A JSClass whose prototype you want to get.
235  @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.
236 */
prototype(ExecState * exec)237 JSObject* OpaqueJSClass::prototype(ExecState* exec)
238 {
239     /* Class (C++) and prototype (JS) inheritance are parallel, so:
240      *     (C++)      |        (JS)
241      *   ParentClass  |   ParentClassPrototype
242      *       ^        |          ^
243      *       |        |          |
244      *  DerivedClass  |  DerivedClassPrototype
245      */
246 
247     if (!prototypeClass)
248         return 0;
249 
250     OpaqueJSClassContextData& jsClassData = contextData(exec);
251 
252     if (!jsClassData.cachedPrototype) {
253         // Recursive, but should be good enough for our purposes
254         jsClassData.cachedPrototype.set(exec->globalData(), new (exec) JSCallbackObject<JSObjectWithGlobalObject>(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
255         if (parentClass) {
256             if (JSObject* prototype = parentClass->prototype(exec))
257                 jsClassData.cachedPrototype->setPrototype(exec->globalData(), prototype);
258         }
259     }
260     return jsClassData.cachedPrototype.get();
261 }
262