-// -*- mode: c++; c-basic-offset: 4 -*-
/*
* Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
*
#include "JSClassRef.h"
#include "APICast.h"
+#include "Identifier.h"
+#include "InitializeThreading.h"
#include "JSCallbackObject.h"
+#include "JSGlobalObject.h"
#include "JSObjectRef.h"
-#include <kjs/JSGlobalObject.h>
-#include <kjs/identifier.h>
-#include <kjs/object_object.h>
+#include "ObjectPrototype.h"
+#include "Operations.h"
+#include <wtf/text/StringHash.h>
+#include <wtf/unicode/UTF8.h>
-using namespace KJS;
+using namespace std;
+using namespace JSC;
+using namespace WTF::Unicode;
const JSClassDefinition kJSClassDefinitionEmpty = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
OpaqueJSClass::OpaqueJSClass(const JSClassDefinition* definition, OpaqueJSClass* protoClass)
- // FIXME: <rdar://problem/4949018>
- : className(definition->className)
- , parentClass(definition->parentClass)
+ : parentClass(definition->parentClass)
, prototypeClass(0)
- , staticValues(0)
- , staticFunctions(0)
, initialize(definition->initialize)
, finalize(definition->finalize)
, hasProperty(definition->hasProperty)
, callAsConstructor(definition->callAsConstructor)
, hasInstance(definition->hasInstance)
, convertToType(definition->convertToType)
- , cachedPrototype(0)
+ , m_className(String::fromUTF8(definition->className))
{
+ initializeThreading();
+
if (const JSStaticValue* staticValue = definition->staticValues) {
- staticValues = new StaticValuesTable();
+ m_staticValues = adoptPtr(new OpaqueJSClassStaticValuesTable);
while (staticValue->name) {
- // FIXME: <rdar://problem/4949018>
- staticValues->add(Identifier(staticValue->name).ustring().rep(),
- new StaticValueEntry(staticValue->getProperty, staticValue->setProperty, staticValue->attributes));
+ String valueName = String::fromUTF8(staticValue->name);
+ if (!valueName.isNull())
+ m_staticValues->set(valueName.impl(), adoptPtr(new StaticValueEntry(staticValue->getProperty, staticValue->setProperty, staticValue->attributes)));
++staticValue;
}
}
-
+
if (const JSStaticFunction* staticFunction = definition->staticFunctions) {
- staticFunctions = new StaticFunctionsTable();
+ m_staticFunctions = adoptPtr(new OpaqueJSClassStaticFunctionsTable);
while (staticFunction->name) {
- // FIXME: <rdar://problem/4949018>
- staticFunctions->add(Identifier(staticFunction->name).ustring().rep(),
- new StaticFunctionEntry(staticFunction->callAsFunction, staticFunction->attributes));
+ String functionName = String::fromUTF8(staticFunction->name);
+ if (!functionName.isNull())
+ m_staticFunctions->set(functionName.impl(), adoptPtr(new StaticFunctionEntry(staticFunction->callAsFunction, staticFunction->attributes)));
++staticFunction;
}
}
OpaqueJSClass::~OpaqueJSClass()
{
- if (staticValues) {
- deleteAllValues(*staticValues);
- delete staticValues;
+ // The empty string is shared across threads & is an identifier, in all other cases we should have done a deep copy in className(), below.
+ ASSERT(!m_className.length() || !m_className.impl()->isIdentifier());
+
+#ifndef NDEBUG
+ if (m_staticValues) {
+ OpaqueJSClassStaticValuesTable::const_iterator end = m_staticValues->end();
+ for (OpaqueJSClassStaticValuesTable::const_iterator it = m_staticValues->begin(); it != end; ++it)
+ ASSERT(!it->key->isIdentifier());
}
- if (staticFunctions) {
- deleteAllValues(*staticFunctions);
- delete staticFunctions;
+ if (m_staticFunctions) {
+ OpaqueJSClassStaticFunctionsTable::const_iterator end = m_staticFunctions->end();
+ for (OpaqueJSClassStaticFunctionsTable::const_iterator it = m_staticFunctions->begin(); it != end; ++it)
+ ASSERT(!it->key->isIdentifier());
}
+#endif
if (prototypeClass)
JSClassRelease(prototypeClass);
}
-JSClassRef OpaqueJSClass::createNoAutomaticPrototype(const JSClassDefinition* definition)
+PassRefPtr<OpaqueJSClass> OpaqueJSClass::createNoAutomaticPrototype(const JSClassDefinition* definition)
{
- return new OpaqueJSClass(definition, 0);
+ return adoptRef(new OpaqueJSClass(definition, 0));
}
-void clearReferenceToPrototype(JSObjectRef prototype)
+PassRefPtr<OpaqueJSClass> OpaqueJSClass::create(const JSClassDefinition* clientDefinition)
{
- OpaqueJSClass* jsClass = static_cast<OpaqueJSClass*>(JSObjectGetPrivate(prototype));
- ASSERT(jsClass);
- jsClass->cachedPrototype = 0;
+ JSClassDefinition definition = *clientDefinition; // Avoid modifying client copy.
+
+ JSClassDefinition protoDefinition = kJSClassDefinitionEmpty;
+ protoDefinition.finalize = 0;
+ swap(definition.staticFunctions, protoDefinition.staticFunctions); // Move static functions to the prototype.
+
+ // We are supposed to use JSClassRetain/Release but since we know that we currently have
+ // the only reference to this class object we cheat and use a RefPtr instead.
+ RefPtr<OpaqueJSClass> protoClass = adoptRef(new OpaqueJSClass(&protoDefinition, 0));
+ return adoptRef(new OpaqueJSClass(&definition, protoClass.get()));
}
-JSClassRef OpaqueJSClass::create(const JSClassDefinition* definition)
+OpaqueJSClassContextData::OpaqueJSClassContextData(JSC::VM&, OpaqueJSClass* jsClass)
+ : m_class(jsClass)
{
- if (const JSStaticFunction* staticFunctions = definition->staticFunctions) {
- // copy functions into a prototype class
- JSClassDefinition protoDefinition = kJSClassDefinitionEmpty;
- protoDefinition.staticFunctions = staticFunctions;
- protoDefinition.finalize = clearReferenceToPrototype;
- OpaqueJSClass* protoClass = new OpaqueJSClass(&protoDefinition, 0);
-
- // remove functions from the original class
- JSClassDefinition objectDefinition = *definition;
- objectDefinition.staticFunctions = 0;
- return new OpaqueJSClass(&objectDefinition, protoClass);
+ if (jsClass->m_staticValues) {
+ staticValues = adoptPtr(new OpaqueJSClassStaticValuesTable);
+ OpaqueJSClassStaticValuesTable::const_iterator end = jsClass->m_staticValues->end();
+ for (OpaqueJSClassStaticValuesTable::const_iterator it = jsClass->m_staticValues->begin(); it != end; ++it) {
+ ASSERT(!it->key->isIdentifier());
+ staticValues->add(it->key->isolatedCopy(), adoptPtr(new StaticValueEntry(it->value->getProperty, it->value->setProperty, it->value->attributes)));
+ }
}
- return new OpaqueJSClass(definition, 0);
+ if (jsClass->m_staticFunctions) {
+ staticFunctions = adoptPtr(new OpaqueJSClassStaticFunctionsTable);
+ OpaqueJSClassStaticFunctionsTable::const_iterator end = jsClass->m_staticFunctions->end();
+ for (OpaqueJSClassStaticFunctionsTable::const_iterator it = jsClass->m_staticFunctions->begin(); it != end; ++it) {
+ ASSERT(!it->key->isIdentifier());
+ staticFunctions->add(it->key->isolatedCopy(), adoptPtr(new StaticFunctionEntry(it->value->callAsFunction, it->value->attributes)));
+ }
+ }
+}
+
+OpaqueJSClassContextData& OpaqueJSClass::contextData(ExecState* exec)
+{
+ OwnPtr<OpaqueJSClassContextData>& contextData = exec->lexicalGlobalObject()->opaqueJSClassData().add(this, nullptr).iterator->value;
+ if (!contextData)
+ contextData = adoptPtr(new OpaqueJSClassContextData(exec->vm(), this));
+ return *contextData;
+}
+
+String OpaqueJSClass::className()
+{
+ // Make a deep copy, so that the caller has no chance to put the original into IdentifierTable.
+ return m_className.isolatedCopy();
+}
+
+OpaqueJSClassStaticValuesTable* OpaqueJSClass::staticValues(JSC::ExecState* exec)
+{
+ return contextData(exec).staticValues.get();
+}
+
+OpaqueJSClassStaticFunctionsTable* OpaqueJSClass::staticFunctions(JSC::ExecState* exec)
+{
+ return contextData(exec).staticFunctions.get();
}
/*!
@param jsClass A JSClass whose prototype you want to get.
@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.
*/
-JSObject* OpaqueJSClass::prototype(JSContextRef ctx)
+JSObject* OpaqueJSClass::prototype(ExecState* exec)
{
/* Class (C++) and prototype (JS) inheritance are parallel, so:
* (C++) | (JS)
* | | |
* DerivedClass | DerivedClassPrototype
*/
-
+
if (!prototypeClass)
return 0;
-
- ExecState* exec = toJS(ctx);
-
- if (!cachedPrototype) {
- // Recursive, but should be good enough for our purposes
- JSObject* parentPrototype = 0;
- if (parentClass)
- parentPrototype = parentClass->prototype(ctx); // can be null
- if (!parentPrototype)
- parentPrototype = exec->dynamicGlobalObject()->objectPrototype();
- cachedPrototype = new JSCallbackObject<JSObject>(exec, prototypeClass, parentPrototype, this); // set ourself as the object's private data, so it can clear our reference on destruction
+
+ OpaqueJSClassContextData& jsClassData = contextData(exec);
+
+ if (JSObject* prototype = jsClassData.cachedPrototype.get())
+ return prototype;
+
+ // Recursive, but should be good enough for our purposes
+ JSObject* prototype = JSCallbackObject<JSDestructibleObject>::create(exec, exec->lexicalGlobalObject(), exec->lexicalGlobalObject()->callbackObjectStructure(), prototypeClass, &jsClassData); // set jsClassData as the object's private data, so it can clear our reference on destruction
+ if (parentClass) {
+ if (JSObject* parentPrototype = parentClass->prototype(exec))
+ prototype->setPrototype(exec->vm(), parentPrototype);
}
- return cachedPrototype;
+
+ jsClassData.cachedPrototype = PassWeak<JSObject>(prototype);
+ return prototype;
}