• 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