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