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 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 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 "Identifier.h"
31#include "InitializeThreading.h"
32#include "JSCallbackObject.h"
33#include "JSGlobalObject.h"
34#include "JSObjectRef.h"
35#include "ObjectPrototype.h"
36#include "JSCInlines.h"
37#include <wtf/text/StringHash.h>
38#include <wtf/unicode/UTF8Conversion.h>
39
40using namespace JSC;
41using namespace WTF::Unicode;
42
43const JSClassDefinition kJSClassDefinitionEmpty = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
44
45OpaqueJSClass::OpaqueJSClass(const JSClassDefinition* definition, OpaqueJSClass* protoClass)
46 : parentClass(definition->parentClass)
47 , prototypeClass(0)
48 , initialize(definition->initialize)
49 , finalize(definition->finalize)
50 , hasProperty(definition->hasProperty)
51 , getProperty(definition->getProperty)
52 , setProperty(definition->setProperty)
53 , deleteProperty(definition->deleteProperty)
54 , getPropertyNames(definition->getPropertyNames)
55 , callAsFunction(definition->callAsFunction)
56 , callAsConstructor(definition->callAsConstructor)
57 , hasInstance(definition->hasInstance)
58 , convertToType(definition->convertToType)
59 , m_className(String::fromUTF8(definition->className))
60{
61 initializeThreading();
62
63 if (const JSStaticValue* staticValue = definition->staticValues) {
64 m_staticValues = std::make_unique<OpaqueJSClassStaticValuesTable>();
65 while (staticValue->name) {
66 String valueName = String::fromUTF8(staticValue->name);
67 if (!valueName.isNull())
68 m_staticValues->set(valueName.impl(), std::make_unique<StaticValueEntry>(staticValue->getProperty, staticValue->setProperty, staticValue->attributes, valueName));
69 ++staticValue;
70 }
71 }
72
73 if (const JSStaticFunction* staticFunction = definition->staticFunctions) {
74 m_staticFunctions = std::make_unique<OpaqueJSClassStaticFunctionsTable>();
75 while (staticFunction->name) {
76 String functionName = String::fromUTF8(staticFunction->name);
77 if (!functionName.isNull())
78 m_staticFunctions->set(functionName.impl(), std::make_unique<StaticFunctionEntry>(staticFunction->callAsFunction, staticFunction->attributes));
79 ++staticFunction;
80 }
81 }
82
83 if (protoClass)
84 prototypeClass = JSClassRetain(protoClass);
85}
86
87OpaqueJSClass::~OpaqueJSClass()
88{
89 // The empty string is shared across threads & is an identifier, in all other cases we should have done a deep copy in className(), below.
90 ASSERT(!m_className.length() || !m_className.impl()->isAtomic());
91
92#ifndef NDEBUG
93 if (m_staticValues) {
94 OpaqueJSClassStaticValuesTable::const_iterator end = m_staticValues->end();
95 for (OpaqueJSClassStaticValuesTable::const_iterator it = m_staticValues->begin(); it != end; ++it)
96 ASSERT(!it->key->isAtomic());
97 }
98
99 if (m_staticFunctions) {
100 OpaqueJSClassStaticFunctionsTable::const_iterator end = m_staticFunctions->end();
101 for (OpaqueJSClassStaticFunctionsTable::const_iterator it = m_staticFunctions->begin(); it != end; ++it)
102 ASSERT(!it->key->isAtomic());
103 }
104#endif
105
106 if (prototypeClass)
107 JSClassRelease(prototypeClass);
108}
109
110Ref<OpaqueJSClass> OpaqueJSClass::createNoAutomaticPrototype(const JSClassDefinition* definition)
111{
112 return adoptRef(*new OpaqueJSClass(definition, 0));
113}
114
115Ref<OpaqueJSClass> OpaqueJSClass::create(const JSClassDefinition* clientDefinition)
116{
117 JSClassDefinition definition = *clientDefinition; // Avoid modifying client copy.
118
119 JSClassDefinition protoDefinition = kJSClassDefinitionEmpty;
120 protoDefinition.finalize = 0;
121 std::swap(definition.staticFunctions, protoDefinition.staticFunctions); // Move static functions to the prototype.
122
123 // We are supposed to use JSClassRetain/Release but since we know that we currently have
124 // the only reference to this class object we cheat and use a RefPtr instead.
125 RefPtr<OpaqueJSClass> protoClass = adoptRef(new OpaqueJSClass(&protoDefinition, 0));
126 return adoptRef(*new OpaqueJSClass(&definition, protoClass.get()));
127}
128
129OpaqueJSClassContextData::OpaqueJSClassContextData(JSC::VM&, OpaqueJSClass* jsClass)
130 : m_class(jsClass)
131{
132 if (jsClass->m_staticValues) {
133 staticValues = std::make_unique<OpaqueJSClassStaticValuesTable>();
134 OpaqueJSClassStaticValuesTable::const_iterator end = jsClass->m_staticValues->end();
135 for (OpaqueJSClassStaticValuesTable::const_iterator it = jsClass->m_staticValues->begin(); it != end; ++it) {
136 ASSERT(!it->key->isAtomic());
137 String valueName = it->key->isolatedCopy();
138 staticValues->add(valueName.impl(), std::make_unique<StaticValueEntry>(it->value->getProperty, it->value->setProperty, it->value->attributes, valueName));
139 }
140 }
141
142 if (jsClass->m_staticFunctions) {
143 staticFunctions = std::make_unique<OpaqueJSClassStaticFunctionsTable>();
144 OpaqueJSClassStaticFunctionsTable::const_iterator end = jsClass->m_staticFunctions->end();
145 for (OpaqueJSClassStaticFunctionsTable::const_iterator it = jsClass->m_staticFunctions->begin(); it != end; ++it) {
146 ASSERT(!it->key->isAtomic());
147 staticFunctions->add(it->key->isolatedCopy(), std::make_unique<StaticFunctionEntry>(it->value->callAsFunction, it->value->attributes));
148 }
149 }
150}
151
152OpaqueJSClassContextData& OpaqueJSClass::contextData(ExecState* exec)
153{
154 std::unique_ptr<OpaqueJSClassContextData>& contextData = exec->lexicalGlobalObject()->opaqueJSClassData().add(this, nullptr).iterator->value;
155 if (!contextData)
156 contextData = std::make_unique<OpaqueJSClassContextData>(exec->vm(), this);
157 return *contextData;
158}
159
160String OpaqueJSClass::className()
161{
162 // Make a deep copy, so that the caller has no chance to put the original into AtomicStringTable.
163 return m_className.isolatedCopy();
164}
165
166OpaqueJSClassStaticValuesTable* OpaqueJSClass::staticValues(JSC::ExecState* exec)
167{
168 return contextData(exec).staticValues.get();
169}
170
171OpaqueJSClassStaticFunctionsTable* OpaqueJSClass::staticFunctions(JSC::ExecState* exec)
172{
173 return contextData(exec).staticFunctions.get();
174}
175
176JSObject* OpaqueJSClass::prototype(ExecState* exec)
177{
178 /* Class (C++) and prototype (JS) inheritance are parallel, so:
179 * (C++) | (JS)
180 * ParentClass | ParentClassPrototype
181 * ^ | ^
182 * | | |
183 * DerivedClass | DerivedClassPrototype
184 */
185
186 if (!prototypeClass)
187 return 0;
188
189 OpaqueJSClassContextData& jsClassData = contextData(exec);
190
191 if (JSObject* prototype = jsClassData.cachedPrototype.get())
192 return prototype;
193
194 // Recursive, but should be good enough for our purposes
195 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
196 if (parentClass) {
197 if (JSObject* parentPrototype = parentClass->prototype(exec))
198 prototype->setPrototypeDirect(exec->vm(), parentPrototype);
199 }
200
201 jsClassData.cachedPrototype = Weak<JSObject>(prototype);
202 return prototype;
203}
204