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