[JSC] Don't reference the properties of @Reflect directly
[WebKit-https.git] / Source / JavaScriptCore / runtime / ReflectObject.cpp
1 /*
2  * Copyright (C) 2015 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 "ReflectObject.h"
28
29 #include "JSCInlines.h"
30 #include "JSGlobalObjectFunctions.h"
31 #include "JSPropertyNameIterator.h"
32 #include "Lookup.h"
33 #include "ObjectConstructor.h"
34
35 namespace JSC {
36
37 static EncodedJSValue JSC_HOST_CALL reflectObjectConstruct(ExecState*);
38 static EncodedJSValue JSC_HOST_CALL reflectObjectDefineProperty(ExecState*);
39 static EncodedJSValue JSC_HOST_CALL reflectObjectEnumerate(ExecState*);
40 static EncodedJSValue JSC_HOST_CALL reflectObjectGet(ExecState*);
41 static EncodedJSValue JSC_HOST_CALL reflectObjectGetOwnPropertyDescriptor(ExecState*);
42 static EncodedJSValue JSC_HOST_CALL reflectObjectGetPrototypeOf(ExecState*);
43 static EncodedJSValue JSC_HOST_CALL reflectObjectIsExtensible(ExecState*);
44 static EncodedJSValue JSC_HOST_CALL reflectObjectOwnKeys(ExecState*);
45 static EncodedJSValue JSC_HOST_CALL reflectObjectPreventExtensions(ExecState*);
46 static EncodedJSValue JSC_HOST_CALL reflectObjectSet(ExecState*);
47 static EncodedJSValue JSC_HOST_CALL reflectObjectSetPrototypeOf(ExecState*);
48
49 }
50
51 #include "ReflectObject.lut.h"
52
53 namespace JSC {
54
55 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(ReflectObject);
56
57 const ClassInfo ReflectObject::s_info = { "Reflect", &Base::s_info, &reflectObjectTable, CREATE_METHOD_TABLE(ReflectObject) };
58
59 /* Source for ReflectObject.lut.h
60 @begin reflectObjectTable
61     apply                    JSBuiltin                             DontEnum|Function 3
62     construct                reflectObjectConstruct                DontEnum|Function 2
63     defineProperty           reflectObjectDefineProperty           DontEnum|Function 3
64     deleteProperty           JSBuiltin                             DontEnum|Function 2
65     enumerate                reflectObjectEnumerate                DontEnum|Function 1
66     get                      reflectObjectGet                      DontEnum|Function 2
67     getOwnPropertyDescriptor reflectObjectGetOwnPropertyDescriptor DontEnum|Function 2
68     getPrototypeOf           reflectObjectGetPrototypeOf           DontEnum|Function 1
69     has                      JSBuiltin                             DontEnum|Function 2
70     isExtensible             reflectObjectIsExtensible             DontEnum|Function 1
71     ownKeys                  reflectObjectOwnKeys                  DontEnum|Function 1
72     preventExtensions        reflectObjectPreventExtensions        DontEnum|Function 1
73     set                      reflectObjectSet                      DontEnum|Function 3
74     setPrototypeOf           reflectObjectSetPrototypeOf           DontEnum|Function 2
75 @end
76 */
77
78 ReflectObject::ReflectObject(VM& vm, Structure* structure)
79     : JSNonFinalObject(vm, structure)
80 {
81 }
82
83 void ReflectObject::finishCreation(VM& vm, JSGlobalObject* globalObject)
84 {
85     Base::finishCreation(vm);
86     ASSERT(inherits(info()));
87
88     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->ownKeysPrivateName, reflectObjectOwnKeys, DontEnum | DontDelete | ReadOnly, 1);
89     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->getOwnPropertyDescriptorPrivateName, reflectObjectGetOwnPropertyDescriptor, DontEnum | DontDelete | ReadOnly, 2);
90 }
91
92 bool ReflectObject::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot &slot)
93 {
94     return getStaticFunctionSlot<Base>(exec, reflectObjectTable, jsCast<ReflectObject*>(object), propertyName, slot);
95 }
96
97 // ------------------------------ Functions --------------------------------
98
99 // https://tc39.github.io/ecma262/#sec-reflect.construct
100 EncodedJSValue JSC_HOST_CALL reflectObjectConstruct(ExecState* exec)
101 {
102     JSValue target = exec->argument(0);
103     if (!target.isObject())
104         return JSValue::encode(throwTypeError(exec, ASCIILiteral("Reflect.construct requires the first argument be a constructor")));
105
106     ConstructData constructData;
107     ConstructType constructType;
108     if (!target.isConstructor(constructType, constructData))
109         return JSValue::encode(throwTypeError(exec, ASCIILiteral("Reflect.construct requires the first argument be a constructor")));
110
111     JSValue newTarget = target;
112     if (exec->argumentCount() >= 3) {
113         newTarget = exec->argument(2);
114         if (!newTarget.isConstructor())
115             return JSValue::encode(throwTypeError(exec, ASCIILiteral("Reflect.construct requires the third argument be a constructor if present")));
116     }
117
118     MarkedArgumentBuffer arguments;
119     JSObject* argumentsObject = jsDynamicCast<JSObject*>(exec->argument(1));
120     if (!argumentsObject)
121         return JSValue::encode(throwTypeError(exec, ASCIILiteral("Reflect.construct requires the second argument be an object")));
122
123     createListFromArrayLike(exec, argumentsObject, RuntimeTypeMaskAllTypes, ASCIILiteral("This error must not be raised"), [&] (JSValue value, RuntimeType) -> bool {
124         arguments.append(value);
125         return false;
126     });
127     if (exec->hadException())
128         return JSValue::encode(jsUndefined());
129
130     return JSValue::encode(construct(exec, target, constructType, constructData, arguments, newTarget));
131 }
132
133 // https://tc39.github.io/ecma262/#sec-reflect.defineproperty
134 EncodedJSValue JSC_HOST_CALL reflectObjectDefineProperty(ExecState* exec)
135 {
136     JSValue target = exec->argument(0);
137     if (!target.isObject())
138         return JSValue::encode(throwTypeError(exec, ASCIILiteral("Reflect.defineProperty requires the first argument be an object")));
139     auto propertyName = exec->argument(1).toPropertyKey(exec);
140     if (exec->hadException())
141         return JSValue::encode(jsUndefined());
142
143     PropertyDescriptor descriptor;
144     if (!toPropertyDescriptor(exec, exec->argument(2), descriptor))
145         return JSValue::encode(jsUndefined());
146     ASSERT((descriptor.attributes() & Accessor) || (!descriptor.isAccessorDescriptor()));
147     ASSERT(!exec->hadException());
148
149     // Reflect.defineProperty should not throw an error when the defineOwnProperty operation fails.
150     bool shouldThrow = false;
151     JSObject* targetObject = asObject(target);
152     return JSValue::encode(jsBoolean(targetObject->methodTable(exec->vm())->defineOwnProperty(targetObject, exec, propertyName, descriptor, shouldThrow)));
153 }
154
155 // FIXME: Reflect.enumerate is removed in ECMA 2016 draft.
156 // http://www.ecma-international.org/ecma-262/6.0/#sec-reflect.enumerate
157 EncodedJSValue JSC_HOST_CALL reflectObjectEnumerate(ExecState* exec)
158 {
159     JSValue target = exec->argument(0);
160     if (!target.isObject())
161         return JSValue::encode(throwTypeError(exec, ASCIILiteral("Reflect.enumerate requires the first argument be an object")));
162     return JSValue::encode(JSPropertyNameIterator::create(exec, exec->lexicalGlobalObject()->propertyNameIteratorStructure(), asObject(target)));
163 }
164
165 // https://tc39.github.io/ecma262/#sec-reflect.get
166 EncodedJSValue JSC_HOST_CALL reflectObjectGet(ExecState* exec)
167 {
168     JSValue target = exec->argument(0);
169     if (!target.isObject())
170         return JSValue::encode(throwTypeError(exec, ASCIILiteral("Reflect.get requires the first argument be an object")));
171
172     const Identifier propertyName = exec->argument(1).toPropertyKey(exec);
173     if (exec->hadException())
174         return JSValue::encode(jsNull());
175
176     JSValue receiver = target;
177     if (exec->argumentCount() >= 3)
178         receiver = exec->argument(2);
179
180     PropertySlot slot(receiver, PropertySlot::InternalMethodType::Get);
181     return JSValue::encode(target.get(exec, propertyName, slot));
182 }
183
184 // https://tc39.github.io/ecma262/#sec-reflect.getownpropertydescriptor
185 EncodedJSValue JSC_HOST_CALL reflectObjectGetOwnPropertyDescriptor(ExecState* exec)
186 {
187     JSValue target = exec->argument(0);
188     if (!target.isObject())
189         return JSValue::encode(throwTypeError(exec, ASCIILiteral("Reflect.getOwnPropertyDescriptor requires the first argument be an object")));
190
191     auto key = exec->argument(1).toPropertyKey(exec);
192     if (exec->hadException())
193         return JSValue::encode(jsUndefined());
194
195     return JSValue::encode(objectConstructorGetOwnPropertyDescriptor(exec, asObject(target), key));
196 }
197
198 // https://tc39.github.io/ecma262/#sec-reflect.getprototypeof
199 EncodedJSValue JSC_HOST_CALL reflectObjectGetPrototypeOf(ExecState* exec)
200 {
201     JSValue target = exec->argument(0);
202     if (!target.isObject())
203         return JSValue::encode(throwTypeError(exec, ASCIILiteral("Reflect.getPrototypeOf requires the first argument be an object")));
204     return JSValue::encode(objectConstructorGetPrototypeOf(exec, asObject(target)));
205 }
206
207 // https://tc39.github.io/ecma262/#sec-reflect.isextensible
208 EncodedJSValue JSC_HOST_CALL reflectObjectIsExtensible(ExecState* exec)
209 {
210     JSValue target = exec->argument(0);
211     if (!target.isObject())
212         return JSValue::encode(throwTypeError(exec, ASCIILiteral("Reflect.isExtensible requires the first argument be an object")));
213
214     bool isExtensible = asObject(target)->isExtensible(exec);
215     if (exec->hadException())
216         return JSValue::encode(JSValue());
217     return JSValue::encode(jsBoolean(isExtensible));
218 }
219
220 // https://tc39.github.io/ecma262/#sec-reflect.ownkeys
221 EncodedJSValue JSC_HOST_CALL reflectObjectOwnKeys(ExecState* exec)
222 {
223     JSValue target = exec->argument(0);
224     if (!target.isObject())
225         return JSValue::encode(throwTypeError(exec, ASCIILiteral("Reflect.ownKeys requires the first argument be an object")));
226     return JSValue::encode(ownPropertyKeys(exec, jsCast<JSObject*>(target), PropertyNameMode::StringsAndSymbols, DontEnumPropertiesMode::Include));
227 }
228
229 // https://tc39.github.io/ecma262/#sec-reflect.preventextensions
230 EncodedJSValue JSC_HOST_CALL reflectObjectPreventExtensions(ExecState* exec)
231 {
232     JSValue target = exec->argument(0);
233     if (!target.isObject())
234         return JSValue::encode(throwTypeError(exec, ASCIILiteral("Reflect.preventExtensions requires the first argument be an object")));
235     JSObject* object = asObject(target);
236     bool result = object->methodTable(exec->vm())->preventExtensions(object, exec);
237     if (exec->hadException())
238         return JSValue::encode(JSValue());
239     return JSValue::encode(jsBoolean(result));
240 }
241
242 // https://tc39.github.io/ecma262/#sec-reflect.set
243 EncodedJSValue JSC_HOST_CALL reflectObjectSet(ExecState* exec)
244 {
245     JSValue target = exec->argument(0);
246     if (!target.isObject())
247         return JSValue::encode(throwTypeError(exec, ASCIILiteral("Reflect.set requires the first argument be an object")));
248     JSObject* targetObject = asObject(target);
249
250     auto propertyName = exec->argument(1).toPropertyKey(exec);
251     if (exec->hadException())
252         return JSValue::encode(jsUndefined());
253
254     JSValue receiver = target;
255     if (exec->argumentCount() >= 4)
256         receiver = exec->argument(3);
257
258     // Do not raise any readonly errors that happen in strict mode.
259     bool isStrictMode = false;
260     PutPropertySlot slot(receiver, isStrictMode);
261     return JSValue::encode(jsBoolean(targetObject->methodTable(exec->vm())->put(targetObject, exec, propertyName, exec->argument(2), slot)));
262 }
263
264 // https://tc39.github.io/ecma262/#sec-reflect.setprototypeof
265 EncodedJSValue JSC_HOST_CALL reflectObjectSetPrototypeOf(ExecState* exec)
266 {
267     JSValue target = exec->argument(0);
268     if (!target.isObject())
269         return JSValue::encode(throwTypeError(exec, ASCIILiteral("Reflect.setPrototypeOf requires the first argument be an object")));
270     JSValue proto = exec->argument(1);
271     if (!proto.isObject() && !proto.isNull())
272         return JSValue::encode(throwTypeError(exec, ASCIILiteral("Reflect.setPrototypeOf requires the second argument be either an object or null")));
273
274     JSObject* object = asObject(target);
275
276     if (!checkProtoSetterAccessAllowed(exec, object))
277         return JSValue::encode(jsBoolean(false));
278
279     VM& vm = exec->vm();
280     bool shouldThrowIfCantSet = false;
281     bool didSetPrototype = object->setPrototype(vm, exec, proto, shouldThrowIfCantSet);
282     if (vm.exception())
283         return JSValue::encode(JSValue());
284     return JSValue::encode(jsBoolean(didSetPrototype));
285 }
286
287 } // namespace JSC