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