1/*
2 * Copyright (C) 2003, 2008-2009, 2016 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 "runtime_object.h"
28
29#include "JSDOMBinding.h"
30#include "runtime_method.h"
31#include <JavaScriptCore/Error.h>
32
33using namespace WebCore;
34
35namespace JSC {
36namespace Bindings {
37
38WEBCORE_EXPORT const ClassInfo RuntimeObject::s_info = { "RuntimeObject", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(RuntimeObject) };
39
40RuntimeObject::RuntimeObject(VM& vm, Structure* structure, RefPtr<Instance>&& instance)
41 : JSDestructibleObject(vm, structure)
42 , m_instance(WTFMove(instance))
43{
44}
45
46void RuntimeObject::finishCreation(VM& vm)
47{
48 Base::finishCreation(vm);
49 ASSERT(inherits(vm, info()));
50}
51
52void RuntimeObject::destroy(JSCell* cell)
53{
54 static_cast<RuntimeObject*>(cell)->RuntimeObject::~RuntimeObject();
55}
56
57void RuntimeObject::invalidate()
58{
59 ASSERT(m_instance);
60 if (m_instance)
61 m_instance->willInvalidateRuntimeObject();
62 m_instance = nullptr;
63}
64
65EncodedJSValue RuntimeObject::fallbackObjectGetter(ExecState* exec, EncodedJSValue thisValue, PropertyName propertyName)
66{
67 VM& vm = exec->vm();
68 auto scope = DECLARE_THROW_SCOPE(vm);
69
70 RuntimeObject* thisObj = jsCast<RuntimeObject*>(JSValue::decode(thisValue));
71 RefPtr<Instance> instance = thisObj->m_instance;
72
73 if (!instance)
74 return JSValue::encode(throwInvalidAccessError(exec, scope));
75
76 instance->begin();
77
78 Class *aClass = instance->getClass();
79 JSValue result = aClass->fallbackObject(exec, instance.get(), propertyName);
80
81 instance->end();
82
83 return JSValue::encode(result);
84}
85
86EncodedJSValue RuntimeObject::fieldGetter(ExecState* exec, EncodedJSValue thisValue, PropertyName propertyName)
87{
88 VM& vm = exec->vm();
89 auto scope = DECLARE_THROW_SCOPE(vm);
90
91 RuntimeObject* thisObj = jsCast<RuntimeObject*>(JSValue::decode(thisValue));
92 RefPtr<Instance> instance = thisObj->m_instance;
93
94 if (!instance)
95 return JSValue::encode(throwInvalidAccessError(exec, scope));
96
97 instance->begin();
98
99 Class *aClass = instance->getClass();
100 Field* aField = aClass->fieldNamed(propertyName, instance.get());
101 JSValue result = aField->valueFromInstance(exec, instance.get());
102
103 instance->end();
104
105 return JSValue::encode(result);
106}
107
108EncodedJSValue RuntimeObject::methodGetter(ExecState* exec, EncodedJSValue thisValue, PropertyName propertyName)
109{
110 VM& vm = exec->vm();
111 auto scope = DECLARE_THROW_SCOPE(vm);
112
113 RuntimeObject* thisObj = jsCast<RuntimeObject*>(JSValue::decode(thisValue));
114 RefPtr<Instance> instance = thisObj->m_instance;
115
116 if (!instance)
117 return JSValue::encode(throwInvalidAccessError(exec, scope));
118
119 instance->begin();
120
121 JSValue method = instance->getMethod(exec, propertyName);
122
123 instance->end();
124
125 return JSValue::encode(method);
126}
127
128bool RuntimeObject::getOwnPropertySlot(JSObject* object, ExecState *exec, PropertyName propertyName, PropertySlot& slot)
129{
130 VM& vm = exec->vm();
131 auto scope = DECLARE_THROW_SCOPE(vm);
132
133 RuntimeObject* thisObject = jsCast<RuntimeObject*>(object);
134 if (!thisObject->m_instance) {
135 throwInvalidAccessError(exec, scope);
136 return false;
137 }
138
139 RefPtr<Instance> instance = thisObject->m_instance;
140
141 instance->begin();
142
143 Class *aClass = instance->getClass();
144
145 if (aClass) {
146 // See if the instance has a field with the specified name.
147 Field *aField = aClass->fieldNamed(propertyName, instance.get());
148 if (aField) {
149 slot.setCustom(thisObject, static_cast<unsigned>(JSC::PropertyAttribute::DontDelete), thisObject->fieldGetter);
150 instance->end();
151 return true;
152 } else {
153 // Now check if a method with specified name exists, if so return a function object for
154 // that method.
155 if (aClass->methodNamed(propertyName, instance.get())) {
156 slot.setCustom(thisObject, PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly, thisObject->methodGetter);
157
158 instance->end();
159 return true;
160 }
161 }
162
163 // Try a fallback object.
164 if (!aClass->fallbackObject(exec, instance.get(), propertyName).isUndefined()) {
165 slot.setCustom(thisObject, PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly | PropertyAttribute::DontEnum, thisObject->fallbackObjectGetter);
166 instance->end();
167 return true;
168 }
169 }
170
171 instance->end();
172
173 return instance->getOwnPropertySlot(thisObject, exec, propertyName, slot);
174}
175
176bool RuntimeObject::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
177{
178 VM& vm = exec->vm();
179 auto scope = DECLARE_THROW_SCOPE(vm);
180
181 RuntimeObject* thisObject = jsCast<RuntimeObject*>(cell);
182 if (!thisObject->m_instance) {
183 throwInvalidAccessError(exec, scope);
184 return false;
185 }
186
187 RefPtr<Instance> instance = thisObject->m_instance;
188 instance->begin();
189
190 // Set the value of the property.
191 bool result = false;
192 Field *aField = instance->getClass()->fieldNamed(propertyName, instance.get());
193 if (aField)
194 result = aField->setValueToInstance(exec, instance.get(), value);
195 else if (!instance->setValueOfUndefinedField(exec, propertyName, value))
196 result = instance->put(thisObject, exec, propertyName, value, slot);
197
198 instance->end();
199 return result;
200}
201
202bool RuntimeObject::deleteProperty(JSCell*, ExecState*, PropertyName)
203{
204 // Can never remove a property of a RuntimeObject.
205 return false;
206}
207
208JSValue RuntimeObject::defaultValue(const JSObject* object, ExecState* exec, PreferredPrimitiveType hint)
209{
210 VM& vm = exec->vm();
211 auto scope = DECLARE_THROW_SCOPE(vm);
212
213 const RuntimeObject* thisObject = jsCast<const RuntimeObject*>(object);
214 if (!thisObject->m_instance)
215 return throwInvalidAccessError(exec, scope);
216
217 RefPtr<Instance> instance = thisObject->m_instance;
218
219 instance->begin();
220 JSValue result = instance->defaultValue(exec, hint);
221 instance->end();
222 return result;
223}
224
225static EncodedJSValue JSC_HOST_CALL callRuntimeObject(ExecState* exec)
226{
227 ASSERT(exec->jsCallee()->inherits<RuntimeObject>(exec->vm()));
228 RefPtr<Instance> instance(static_cast<RuntimeObject*>(exec->jsCallee())->getInternalInstance());
229 instance->begin();
230 JSValue result = instance->invokeDefaultMethod(exec);
231 instance->end();
232 return JSValue::encode(result);
233}
234
235CallType RuntimeObject::getCallData(JSCell* cell, CallData& callData)
236{
237 RuntimeObject* thisObject = jsCast<RuntimeObject*>(cell);
238 if (!thisObject->m_instance)
239 return CallType::None;
240
241 RefPtr<Instance> instance = thisObject->m_instance;
242 if (!instance->supportsInvokeDefaultMethod())
243 return CallType::None;
244
245 callData.native.function = callRuntimeObject;
246 return CallType::Host;
247}
248
249static EncodedJSValue JSC_HOST_CALL callRuntimeConstructor(ExecState* exec)
250{
251 JSObject* constructor = exec->jsCallee();
252 ASSERT(constructor->inherits<RuntimeObject>(exec->vm()));
253 RefPtr<Instance> instance(static_cast<RuntimeObject*>(exec->jsCallee())->getInternalInstance());
254 instance->begin();
255 ArgList args(exec);
256 JSValue result = instance->invokeConstruct(exec, args);
257 instance->end();
258
259 ASSERT(result);
260 return JSValue::encode(result.isObject() ? jsCast<JSObject*>(result.asCell()) : constructor);
261}
262
263ConstructType RuntimeObject::getConstructData(JSCell* cell, ConstructData& constructData)
264{
265 RuntimeObject* thisObject = jsCast<RuntimeObject*>(cell);
266 if (!thisObject->m_instance)
267 return ConstructType::None;
268
269 RefPtr<Instance> instance = thisObject->m_instance;
270 if (!instance->supportsConstruct())
271 return ConstructType::None;
272
273 constructData.native.function = callRuntimeConstructor;
274 return ConstructType::Host;
275}
276
277void RuntimeObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode)
278{
279 VM& vm = exec->vm();
280 auto scope = DECLARE_THROW_SCOPE(vm);
281
282 RuntimeObject* thisObject = jsCast<RuntimeObject*>(object);
283 if (!thisObject->m_instance) {
284 throwInvalidAccessError(exec, scope);
285 return;
286 }
287
288 RefPtr<Instance> instance = thisObject->m_instance;
289
290 instance->begin();
291 instance->getPropertyNames(exec, propertyNames);
292 instance->end();
293}
294
295Exception* RuntimeObject::throwInvalidAccessError(ExecState* exec, ThrowScope& scope)
296{
297 return throwException(exec, scope, createReferenceError(exec, "Trying to access object from destroyed plug-in."));
298}
299
300}
301}
302