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 | |
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 reflectObjectGet(ExecState*); |
40 | static EncodedJSValue JSC_HOST_CALL reflectObjectGetOwnPropertyDescriptor(ExecState*); |
41 | static EncodedJSValue JSC_HOST_CALL reflectObjectGetPrototypeOf(ExecState*); |
42 | static EncodedJSValue JSC_HOST_CALL reflectObjectIsExtensible(ExecState*); |
43 | static EncodedJSValue JSC_HOST_CALL reflectObjectOwnKeys(ExecState*); |
44 | static EncodedJSValue JSC_HOST_CALL reflectObjectPreventExtensions(ExecState*); |
45 | static EncodedJSValue JSC_HOST_CALL reflectObjectSet(ExecState*); |
46 | static EncodedJSValue JSC_HOST_CALL reflectObjectSetPrototypeOf(ExecState*); |
47 | |
48 | } |
49 | |
50 | #include "ReflectObject.lut.h" |
51 | |
52 | namespace JSC { |
53 | |
54 | STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(ReflectObject); |
55 | |
56 | const 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 | |
76 | ReflectObject::ReflectObject(VM& vm, Structure* structure) |
77 | : JSNonFinalObject(vm, structure) |
78 | { |
79 | } |
80 | |
81 | void 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 |
90 | EncodedJSValue JSC_HOST_CALL reflectObjectConstruct(ExecState* exec) |
91 | { |
92 | VM& vm = exec->vm(); |
93 | auto scope = DECLARE_THROW_SCOPE(vm); |
94 | |
95 | JSValue target = exec->argument(0); |
96 | if (!target.isObject()) |
97 | return JSValue::encode(throwTypeError(exec, 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(exec, scope, "Reflect.construct requires the first argument be a constructor"_s )); |
103 | |
104 | JSValue newTarget = target; |
105 | if (exec->argumentCount() >= 3) { |
106 | newTarget = exec->argument(2); |
107 | if (!newTarget.isConstructor(vm)) |
108 | return JSValue::encode(throwTypeError(exec, scope, "Reflect.construct requires the third argument be a constructor if present"_s )); |
109 | } |
110 | |
111 | MarkedArgumentBuffer arguments; |
112 | JSObject* argumentsObject = jsDynamicCast<JSObject*>(vm, exec->argument(1)); |
113 | if (!argumentsObject) |
114 | return JSValue::encode(throwTypeError(exec, scope, "Reflect.construct requires the second argument be an object"_s )); |
115 | |
116 | createListFromArrayLike(exec, 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(exec, scope); |
123 | return encodedJSValue(); |
124 | } |
125 | |
126 | RELEASE_AND_RETURN(scope, JSValue::encode(construct(exec, target, constructType, constructData, arguments, newTarget))); |
127 | } |
128 | |
129 | // https://tc39.github.io/ecma262/#sec-reflect.defineproperty |
130 | EncodedJSValue JSC_HOST_CALL reflectObjectDefineProperty(ExecState* exec) |
131 | { |
132 | VM& vm = exec->vm(); |
133 | auto scope = DECLARE_THROW_SCOPE(vm); |
134 | |
135 | JSValue target = exec->argument(0); |
136 | if (!target.isObject()) |
137 | return JSValue::encode(throwTypeError(exec, scope, "Reflect.defineProperty requires the first argument be an object"_s )); |
138 | auto propertyName = exec->argument(1).toPropertyKey(exec); |
139 | RETURN_IF_EXCEPTION(scope, encodedJSValue()); |
140 | |
141 | PropertyDescriptor descriptor; |
142 | bool success = toPropertyDescriptor(exec, exec->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, exec, propertyName, descriptor, shouldThrow)))); |
153 | } |
154 | |
155 | // https://tc39.github.io/ecma262/#sec-reflect.get |
156 | EncodedJSValue JSC_HOST_CALL reflectObjectGet(ExecState* exec) |
157 | { |
158 | VM& vm = exec->vm(); |
159 | auto scope = DECLARE_THROW_SCOPE(vm); |
160 | |
161 | JSValue target = exec->argument(0); |
162 | if (!target.isObject()) |
163 | return JSValue::encode(throwTypeError(exec, scope, "Reflect.get requires the first argument be an object"_s )); |
164 | |
165 | const Identifier propertyName = exec->argument(1).toPropertyKey(exec); |
166 | RETURN_IF_EXCEPTION(scope, encodedJSValue()); |
167 | |
168 | JSValue receiver = target; |
169 | if (exec->argumentCount() >= 3) |
170 | receiver = exec->argument(2); |
171 | |
172 | PropertySlot slot(receiver, PropertySlot::InternalMethodType::Get); |
173 | RELEASE_AND_RETURN(scope, JSValue::encode(target.get(exec, propertyName, slot))); |
174 | } |
175 | |
176 | // https://tc39.github.io/ecma262/#sec-reflect.getownpropertydescriptor |
177 | EncodedJSValue JSC_HOST_CALL reflectObjectGetOwnPropertyDescriptor(ExecState* exec) |
178 | { |
179 | VM& vm = exec->vm(); |
180 | auto scope = DECLARE_THROW_SCOPE(vm); |
181 | |
182 | JSValue target = exec->argument(0); |
183 | if (!target.isObject()) |
184 | return JSValue::encode(throwTypeError(exec, scope, "Reflect.getOwnPropertyDescriptor requires the first argument be an object"_s )); |
185 | |
186 | auto key = exec->argument(1).toPropertyKey(exec); |
187 | RETURN_IF_EXCEPTION(scope, encodedJSValue()); |
188 | |
189 | RELEASE_AND_RETURN(scope, JSValue::encode(objectConstructorGetOwnPropertyDescriptor(exec, asObject(target), key))); |
190 | } |
191 | |
192 | // https://tc39.github.io/ecma262/#sec-reflect.getprototypeof |
193 | EncodedJSValue JSC_HOST_CALL reflectObjectGetPrototypeOf(ExecState* exec) |
194 | { |
195 | VM& vm = exec->vm(); |
196 | auto scope = DECLARE_THROW_SCOPE(vm); |
197 | |
198 | JSValue target = exec->argument(0); |
199 | if (!target.isObject()) |
200 | return JSValue::encode(throwTypeError(exec, scope, "Reflect.getPrototypeOf requires the first argument be an object"_s )); |
201 | RELEASE_AND_RETURN(scope, JSValue::encode(asObject(target)->getPrototype(vm, exec))); |
202 | } |
203 | |
204 | // https://tc39.github.io/ecma262/#sec-reflect.isextensible |
205 | EncodedJSValue JSC_HOST_CALL reflectObjectIsExtensible(ExecState* exec) |
206 | { |
207 | VM& vm = exec->vm(); |
208 | auto scope = DECLARE_THROW_SCOPE(vm); |
209 | |
210 | JSValue target = exec->argument(0); |
211 | if (!target.isObject()) |
212 | return JSValue::encode(throwTypeError(exec, scope, "Reflect.isExtensible requires the first argument be an object"_s )); |
213 | |
214 | bool isExtensible = asObject(target)->isExtensible(exec); |
215 | RETURN_IF_EXCEPTION(scope, encodedJSValue()); |
216 | return JSValue::encode(jsBoolean(isExtensible)); |
217 | } |
218 | |
219 | // https://tc39.github.io/ecma262/#sec-reflect.ownkeys |
220 | EncodedJSValue JSC_HOST_CALL reflectObjectOwnKeys(ExecState* exec) |
221 | { |
222 | VM& vm = exec->vm(); |
223 | auto scope = DECLARE_THROW_SCOPE(vm); |
224 | |
225 | JSValue target = exec->argument(0); |
226 | if (!target.isObject()) |
227 | return JSValue::encode(throwTypeError(exec, scope, "Reflect.ownKeys requires the first argument be an object"_s )); |
228 | RELEASE_AND_RETURN(scope, JSValue::encode(ownPropertyKeys(exec, jsCast<JSObject*>(target), PropertyNameMode::StringsAndSymbols, DontEnumPropertiesMode::Include))); |
229 | } |
230 | |
231 | // https://tc39.github.io/ecma262/#sec-reflect.preventextensions |
232 | EncodedJSValue JSC_HOST_CALL reflectObjectPreventExtensions(ExecState* exec) |
233 | { |
234 | VM& vm = exec->vm(); |
235 | auto scope = DECLARE_THROW_SCOPE(vm); |
236 | |
237 | JSValue target = exec->argument(0); |
238 | if (!target.isObject()) |
239 | return JSValue::encode(throwTypeError(exec, scope, "Reflect.preventExtensions requires the first argument be an object"_s )); |
240 | JSObject* object = asObject(target); |
241 | bool result = object->methodTable(vm)->preventExtensions(object, exec); |
242 | RETURN_IF_EXCEPTION(scope, encodedJSValue()); |
243 | return JSValue::encode(jsBoolean(result)); |
244 | } |
245 | |
246 | // https://tc39.github.io/ecma262/#sec-reflect.set |
247 | EncodedJSValue JSC_HOST_CALL reflectObjectSet(ExecState* exec) |
248 | { |
249 | VM& vm = exec->vm(); |
250 | auto scope = DECLARE_THROW_SCOPE(vm); |
251 | |
252 | JSValue target = exec->argument(0); |
253 | if (!target.isObject()) |
254 | return JSValue::encode(throwTypeError(exec, scope, "Reflect.set requires the first argument be an object"_s )); |
255 | JSObject* targetObject = asObject(target); |
256 | |
257 | auto propertyName = exec->argument(1).toPropertyKey(exec); |
258 | RETURN_IF_EXCEPTION(scope, encodedJSValue()); |
259 | |
260 | JSValue receiver = target; |
261 | if (exec->argumentCount() >= 4) |
262 | receiver = exec->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, exec, propertyName, exec->argument(2), slot)))); |
268 | } |
269 | |
270 | // https://tc39.github.io/ecma262/#sec-reflect.setprototypeof |
271 | EncodedJSValue JSC_HOST_CALL reflectObjectSetPrototypeOf(ExecState* exec) |
272 | { |
273 | VM& vm = exec->vm(); |
274 | auto scope = DECLARE_THROW_SCOPE(vm); |
275 | |
276 | JSValue target = exec->argument(0); |
277 | if (!target.isObject()) |
278 | return JSValue::encode(throwTypeError(exec, scope, "Reflect.setPrototypeOf requires the first argument be an object"_s )); |
279 | JSValue proto = exec->argument(1); |
280 | if (!proto.isObject() && !proto.isNull()) |
281 | return JSValue::encode(throwTypeError(exec, 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, exec, proto, shouldThrowIfCantSet); |
287 | RETURN_IF_EXCEPTION(scope, encodedJSValue()); |
288 | return JSValue::encode(jsBoolean(didSetPrototype)); |
289 | } |
290 | |
291 | } // namespace JSC |
292 | |