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