Home | History | Annotate | Download | only in API
      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 
     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 
     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 
    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 
    142 PassRefPtr<OpaqueJSClass> OpaqueJSClass::createNoAutomaticPrototype(const JSClassDefinition* definition)
    143 {
    144     return adoptRef(new OpaqueJSClass(definition, 0));
    145 }
    146 
    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 
    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 
    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 
    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 
    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 
    217 OpaqueJSClassStaticValuesTable* OpaqueJSClass::staticValues(JSC::ExecState* exec)
    218 {
    219     OpaqueJSClassContextData& jsClassData = contextData(exec);
    220     return jsClassData.staticValues;
    221 }
    222 
    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 */
    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