c73e11a834264371fe04c70582b0adbc23852e84
[WebKit-https.git] / Source / JavaScriptCore / runtime / ObjectConstructor.cpp
1 /*
2  *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3  *  Copyright (C) 2008-2017 Apple Inc. All rights reserved.
4  *
5  *  This library is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Lesser General Public
7  *  License as published by the Free Software Foundation; either
8  *  version 2 of the License, or (at your option) any later version.
9  *
10  *  This library is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *  Lesser General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Lesser General Public
16  *  License along with this library; if not, write to the Free Software
17  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18  *
19  */
20
21 #include "config.h"
22 #include "ObjectConstructor.h"
23
24 #include "BuiltinNames.h"
25 #include "ButterflyInlines.h"
26 #include "Error.h"
27 #include "ExceptionHelpers.h"
28 #include "JSArray.h"
29 #include "JSCInlines.h"
30 #include "JSFunction.h"
31 #include "JSGlobalObject.h"
32 #include "JSGlobalObjectFunctions.h"
33 #include "Lookup.h"
34 #include "ObjectPrototype.h"
35 #include "PropertyDescriptor.h"
36 #include "PropertyNameArray.h"
37 #include "StackVisitor.h"
38 #include "Symbol.h"
39
40 namespace JSC {
41
42 EncodedJSValue JSC_HOST_CALL objectConstructorGetPrototypeOf(ExecState*);
43 EncodedJSValue JSC_HOST_CALL objectConstructorSetPrototypeOf(ExecState*);
44 EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyNames(ExecState*);
45 EncodedJSValue JSC_HOST_CALL objectConstructorDefineProperty(ExecState*);
46 EncodedJSValue JSC_HOST_CALL objectConstructorDefineProperties(ExecState*);
47 EncodedJSValue JSC_HOST_CALL objectConstructorCreate(ExecState*);
48 EncodedJSValue JSC_HOST_CALL objectConstructorSeal(ExecState*);
49 EncodedJSValue JSC_HOST_CALL objectConstructorFreeze(ExecState*);
50 EncodedJSValue JSC_HOST_CALL objectConstructorPreventExtensions(ExecState*);
51 EncodedJSValue JSC_HOST_CALL objectConstructorIsSealed(ExecState*);
52 EncodedJSValue JSC_HOST_CALL objectConstructorIsFrozen(ExecState*);
53 EncodedJSValue JSC_HOST_CALL objectConstructorIsExtensible(ExecState*);
54 EncodedJSValue JSC_HOST_CALL objectConstructorIs(ExecState*);
55
56 }
57
58 #include "ObjectConstructor.lut.h"
59
60 namespace JSC {
61
62 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(ObjectConstructor);
63
64 const ClassInfo ObjectConstructor::s_info = { "Function", &InternalFunction::s_info, &objectConstructorTable, CREATE_METHOD_TABLE(ObjectConstructor) };
65
66 /* Source for ObjectConstructor.lut.h
67 @begin objectConstructorTable
68   getPrototypeOf            objectConstructorGetPrototypeOf             DontEnum|Function 1
69   setPrototypeOf            objectConstructorSetPrototypeOf             DontEnum|Function 2
70   getOwnPropertyDescriptor  objectConstructorGetOwnPropertyDescriptor   DontEnum|Function 2
71   getOwnPropertyDescriptors objectConstructorGetOwnPropertyDescriptors  DontEnum|Function 1
72   getOwnPropertyNames       objectConstructorGetOwnPropertyNames        DontEnum|Function 1
73   getOwnPropertySymbols     objectConstructorGetOwnPropertySymbols      DontEnum|Function 1
74   keys                      objectConstructorKeys                       DontEnum|Function 1
75   defineProperty            objectConstructorDefineProperty             DontEnum|Function 3
76   defineProperties          objectConstructorDefineProperties           DontEnum|Function 2
77   create                    objectConstructorCreate                     DontEnum|Function 2
78   seal                      objectConstructorSeal                       DontEnum|Function 1
79   freeze                    objectConstructorFreeze                     DontEnum|Function 1
80   preventExtensions         objectConstructorPreventExtensions          DontEnum|Function 1
81   isSealed                  objectConstructorIsSealed                   DontEnum|Function 1
82   isFrozen                  objectConstructorIsFrozen                   DontEnum|Function 1
83   isExtensible              objectConstructorIsExtensible               DontEnum|Function 1
84   is                        objectConstructorIs                         DontEnum|Function 2
85   assign                    JSBuiltin                                   DontEnum|Function 2
86   values                    JSBuiltin                                   DontEnum|Function 1
87   entries                   JSBuiltin                                   DontEnum|Function 1
88 @end
89 */
90
91 ObjectConstructor::ObjectConstructor(VM& vm, Structure* structure)
92     : InternalFunction(vm, structure)
93 {
94 }
95
96 void ObjectConstructor::finishCreation(VM& vm, JSGlobalObject* globalObject, ObjectPrototype* objectPrototype)
97 {
98     Base::finishCreation(vm, objectPrototype->classInfo(vm)->className);
99
100     putDirectWithoutTransition(vm, vm.propertyNames->prototype, objectPrototype, DontEnum | DontDelete | ReadOnly);
101     putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(1), ReadOnly | DontEnum);
102
103     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().createPrivateName(), objectConstructorCreate, DontEnum, 2);
104     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().definePropertyPrivateName(), objectConstructorDefineProperty, DontEnum, 3);
105     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().getPrototypeOfPrivateName(), objectConstructorGetPrototypeOf, DontEnum, 1);
106     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().getOwnPropertyNamesPrivateName(), objectConstructorGetOwnPropertyNames, DontEnum, 1);
107     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().getOwnPropertyDescriptorPrivateName(), objectConstructorGetOwnPropertyDescriptor, DontEnum, 1);
108 }
109
110 // ES 19.1.1.1 Object([value])
111 static ALWAYS_INLINE JSObject* constructObject(ExecState* exec, JSValue newTarget)
112 {
113     ObjectConstructor* objectConstructor = jsCast<ObjectConstructor*>(exec->jsCallee());
114     JSGlobalObject* globalObject = objectConstructor->globalObject();
115     VM& vm = globalObject->vm();
116     auto scope = DECLARE_THROW_SCOPE(vm);
117
118     // We need to check newTarget condition in this caller side instead of InternalFunction::createSubclassStructure side.
119     // Since if we found this condition is met, we should not fall into the type conversion in the step 3.
120
121     // 1. If NewTarget is neither undefined nor the active function, then
122     if (newTarget && newTarget != objectConstructor) {
123         // a. Return ? OrdinaryCreateFromConstructor(NewTarget, "%ObjectPrototype%").
124         Structure* objectStructure = InternalFunction::createSubclassStructure(exec, newTarget, globalObject->objectStructureForObjectConstructor());
125         RETURN_IF_EXCEPTION(scope, nullptr);
126         return constructEmptyObject(exec, objectStructure);
127     }
128
129     // 2. If value is null, undefined or not supplied, return ObjectCreate(%ObjectPrototype%).
130     ArgList args(exec);
131     JSValue arg = args.at(0);
132     if (arg.isUndefinedOrNull())
133         return constructEmptyObject(exec, globalObject->objectStructureForObjectConstructor());
134
135     // 3. Return ToObject(value).
136     scope.release();
137     return arg.toObject(exec, globalObject);
138 }
139
140 static EncodedJSValue JSC_HOST_CALL constructWithObjectConstructor(ExecState* exec)
141 {
142     return JSValue::encode(constructObject(exec, exec->newTarget()));
143 }
144
145 ConstructType ObjectConstructor::getConstructData(JSCell*, ConstructData& constructData)
146 {
147     constructData.native.function = constructWithObjectConstructor;
148     return ConstructType::Host;
149 }
150
151 static EncodedJSValue JSC_HOST_CALL callObjectConstructor(ExecState* exec)
152 {
153     return JSValue::encode(constructObject(exec, JSValue()));
154 }
155
156 CallType ObjectConstructor::getCallData(JSCell*, CallData& callData)
157 {
158     callData.native.function = callObjectConstructor;
159     return CallType::Host;
160 }
161
162 EncodedJSValue JSC_HOST_CALL objectConstructorGetPrototypeOf(ExecState* exec)
163 {
164     VM& vm = exec->vm();
165     auto scope = DECLARE_THROW_SCOPE(vm);
166     JSObject* object = exec->argument(0).toObject(exec);
167     RETURN_IF_EXCEPTION(scope, encodedJSValue());
168     scope.release();
169     return JSValue::encode(object->getPrototype(vm, exec));
170 }
171
172 EncodedJSValue JSC_HOST_CALL objectConstructorSetPrototypeOf(ExecState* exec)
173 {
174     VM& vm = exec->vm();
175     auto scope = DECLARE_THROW_SCOPE(vm);
176
177     JSValue objectValue = exec->argument(0);
178     if (objectValue.isUndefinedOrNull())
179         return throwVMTypeError(exec, scope, ASCIILiteral("Cannot set prototype of undefined or null"));
180
181     JSValue protoValue = exec->argument(1);
182     if (!protoValue.isObject() && !protoValue.isNull())
183         return throwVMTypeError(exec, scope, ASCIILiteral("Prototype value can only be an object or null"));
184
185     JSObject* object = objectValue.toObject(exec);
186     RETURN_IF_EXCEPTION(scope, encodedJSValue());
187
188     bool shouldThrowIfCantSet = true;
189     bool didSetPrototype = object->setPrototype(vm, exec, protoValue, shouldThrowIfCantSet);
190     ASSERT_UNUSED(didSetPrototype, scope.exception() || didSetPrototype);
191     return JSValue::encode(objectValue);
192 }
193
194 JSValue objectConstructorGetOwnPropertyDescriptor(ExecState* exec, JSObject* object, const Identifier& propertyName)
195 {
196     VM& vm = exec->vm();
197     auto scope = DECLARE_THROW_SCOPE(vm);
198     PropertyDescriptor descriptor;
199     if (!object->getOwnPropertyDescriptor(exec, propertyName, descriptor)) {
200         scope.release();
201         return jsUndefined();
202     }
203     RETURN_IF_EXCEPTION(scope, { });
204
205     JSObject* result = constructObjectFromPropertyDescriptor(exec, descriptor);
206     ASSERT(!!scope.exception() == !result);
207     if (!result)
208         return jsUndefined();
209     return result;
210 }
211
212 JSValue objectConstructorGetOwnPropertyDescriptors(ExecState* exec, JSObject* object)
213 {
214     VM& vm = exec->vm();
215     auto scope = DECLARE_THROW_SCOPE(vm);
216     PropertyNameArray properties(exec, PropertyNameMode::StringsAndSymbols);
217     object->methodTable(vm)->getOwnPropertyNames(object, exec, properties, EnumerationMode(DontEnumPropertiesMode::Include));
218     RETURN_IF_EXCEPTION(scope, { });
219
220     JSObject* descriptors = constructEmptyObject(exec);
221     RETURN_IF_EXCEPTION(scope, { });
222
223     for (auto& propertyName : properties) {
224         PropertyDescriptor descriptor;
225         bool didGetDescriptor = object->getOwnPropertyDescriptor(exec, propertyName, descriptor);
226         RETURN_IF_EXCEPTION(scope, { });
227
228         if (!didGetDescriptor)
229             continue;
230
231         JSObject* fromDescriptor = constructObjectFromPropertyDescriptor(exec, descriptor);
232         ASSERT(!!scope.exception() == !fromDescriptor);
233         if (!fromDescriptor)
234             return jsUndefined();
235
236         PutPropertySlot slot(descriptors);
237         descriptors->putOwnDataPropertyMayBeIndex(exec, propertyName, fromDescriptor, slot);
238         scope.assertNoException();
239     }
240
241     return descriptors;
242 }
243
244 EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyDescriptor(ExecState* exec)
245 {
246     VM& vm = exec->vm();
247     auto scope = DECLARE_THROW_SCOPE(vm);
248     JSObject* object = exec->argument(0).toObject(exec);
249     RETURN_IF_EXCEPTION(scope, encodedJSValue());
250     auto propertyName = exec->argument(1).toPropertyKey(exec);
251     RETURN_IF_EXCEPTION(scope, encodedJSValue());
252     scope.release();
253     return JSValue::encode(objectConstructorGetOwnPropertyDescriptor(exec, object, propertyName));
254 }
255
256 EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyDescriptors(ExecState* exec)
257 {
258     VM& vm = exec->vm();
259     auto scope = DECLARE_THROW_SCOPE(vm);
260     JSObject* object = exec->argument(0).toObject(exec);
261     RETURN_IF_EXCEPTION(scope, encodedJSValue());
262     scope.release();
263     return JSValue::encode(objectConstructorGetOwnPropertyDescriptors(exec, object));
264 }
265
266 // FIXME: Use the enumeration cache.
267 EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyNames(ExecState* exec)
268 {
269     VM& vm = exec->vm();
270     auto scope = DECLARE_THROW_SCOPE(vm);
271     JSObject* object = exec->argument(0).toObject(exec);
272     RETURN_IF_EXCEPTION(scope, encodedJSValue());
273     scope.release();
274     return JSValue::encode(ownPropertyKeys(exec, object, PropertyNameMode::Strings, DontEnumPropertiesMode::Include));
275 }
276
277 // FIXME: Use the enumeration cache.
278 EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertySymbols(ExecState* exec)
279 {
280     VM& vm = exec->vm();
281     auto scope = DECLARE_THROW_SCOPE(vm);
282     JSObject* object = exec->argument(0).toObject(exec);
283     RETURN_IF_EXCEPTION(scope, encodedJSValue());
284     scope.release();
285     return JSValue::encode(ownPropertyKeys(exec, object, PropertyNameMode::Symbols, DontEnumPropertiesMode::Include));
286 }
287
288 // FIXME: Use the enumeration cache.
289 EncodedJSValue JSC_HOST_CALL objectConstructorKeys(ExecState* exec)
290 {
291     VM& vm = exec->vm();
292     auto scope = DECLARE_THROW_SCOPE(vm);
293     JSObject* object = exec->argument(0).toObject(exec);
294     RETURN_IF_EXCEPTION(scope, encodedJSValue());
295     scope.release();
296     return JSValue::encode(ownPropertyKeys(exec, object, PropertyNameMode::Strings, DontEnumPropertiesMode::Exclude));
297 }
298
299 EncodedJSValue JSC_HOST_CALL ownEnumerablePropertyKeys(ExecState* exec)
300 {
301     VM& vm = exec->vm();
302     auto scope = DECLARE_THROW_SCOPE(vm);
303     JSObject* object = exec->argument(0).toObject(exec);
304     RETURN_IF_EXCEPTION(scope, encodedJSValue());
305     scope.release();
306     return JSValue::encode(ownPropertyKeys(exec, object, PropertyNameMode::StringsAndSymbols, DontEnumPropertiesMode::Exclude));
307 }
308
309 // ES6 6.2.4.5 ToPropertyDescriptor
310 // https://tc39.github.io/ecma262/#sec-topropertydescriptor
311 bool toPropertyDescriptor(ExecState* exec, JSValue in, PropertyDescriptor& desc)
312 {
313     VM& vm = exec->vm();
314     auto scope = DECLARE_THROW_SCOPE(vm);
315
316     if (!in.isObject()) {
317         throwTypeError(exec, scope, ASCIILiteral("Property description must be an object."));
318         return false;
319     }
320     JSObject* description = asObject(in);
321
322     bool hasProperty = description->hasProperty(exec, vm.propertyNames->enumerable);
323     ASSERT(!scope.exception() || !hasProperty);
324     if (hasProperty) {
325         JSValue value = description->get(exec, vm.propertyNames->enumerable);
326         RETURN_IF_EXCEPTION(scope, false);
327         desc.setEnumerable(value.toBoolean(exec));
328     } else
329         RETURN_IF_EXCEPTION(scope, false);
330
331     hasProperty = description->hasProperty(exec, vm.propertyNames->configurable);
332     ASSERT(!scope.exception() || !hasProperty);
333     if (hasProperty) {
334         JSValue value = description->get(exec, vm.propertyNames->configurable);
335         RETURN_IF_EXCEPTION(scope, false);
336         desc.setConfigurable(value.toBoolean(exec));
337     } else
338         RETURN_IF_EXCEPTION(scope, false);
339
340     JSValue value;
341     hasProperty = description->hasProperty(exec, vm.propertyNames->value);
342     ASSERT(!scope.exception() || !hasProperty);
343     if (hasProperty) {
344         JSValue value = description->get(exec, vm.propertyNames->value);
345         RETURN_IF_EXCEPTION(scope, false);
346         desc.setValue(value);
347     } else
348         RETURN_IF_EXCEPTION(scope, false);
349
350     hasProperty = description->hasProperty(exec, vm.propertyNames->writable);
351     ASSERT(!scope.exception() || !hasProperty);
352     if (hasProperty) {
353         JSValue value = description->get(exec, vm.propertyNames->writable);
354         RETURN_IF_EXCEPTION(scope, false);
355         desc.setWritable(value.toBoolean(exec));
356     } else
357         RETURN_IF_EXCEPTION(scope, false);
358
359     hasProperty = description->hasProperty(exec, vm.propertyNames->get);
360     ASSERT(!scope.exception() || !hasProperty);
361     if (hasProperty) {
362         JSValue get = description->get(exec, vm.propertyNames->get);
363         RETURN_IF_EXCEPTION(scope, false);
364         if (!get.isUndefined()) {
365             CallData callData;
366             if (getCallData(get, callData) == CallType::None) {
367                 throwTypeError(exec, scope, ASCIILiteral("Getter must be a function."));
368                 return false;
369             }
370         }
371         desc.setGetter(get);
372     } else
373         RETURN_IF_EXCEPTION(scope, false);
374
375     hasProperty = description->hasProperty(exec, vm.propertyNames->set);
376     ASSERT(!scope.exception() || !hasProperty);
377     if (hasProperty) {
378         JSValue set = description->get(exec, vm.propertyNames->set);
379         RETURN_IF_EXCEPTION(scope, false);
380         if (!set.isUndefined()) {
381             CallData callData;
382             if (getCallData(set, callData) == CallType::None) {
383                 throwTypeError(exec, scope, ASCIILiteral("Setter must be a function."));
384                 return false;
385             }
386         }
387         desc.setSetter(set);
388     } else
389         RETURN_IF_EXCEPTION(scope, false);
390
391     if (!desc.isAccessorDescriptor())
392         return true;
393
394     if (desc.value()) {
395         throwTypeError(exec, scope, ASCIILiteral("Invalid property.  'value' present on property with getter or setter."));
396         return false;
397     }
398
399     if (desc.writablePresent()) {
400         throwTypeError(exec, scope, ASCIILiteral("Invalid property.  'writable' present on property with getter or setter."));
401         return false;
402     }
403     return true;
404 }
405
406 EncodedJSValue JSC_HOST_CALL objectConstructorDefineProperty(ExecState* exec)
407 {
408     VM& vm = exec->vm();
409     auto scope = DECLARE_THROW_SCOPE(vm);
410
411     if (!exec->argument(0).isObject())
412         return throwVMTypeError(exec, scope, ASCIILiteral("Properties can only be defined on Objects."));
413     JSObject* obj = asObject(exec->argument(0));
414     auto propertyName = exec->argument(1).toPropertyKey(exec);
415     RETURN_IF_EXCEPTION(scope, encodedJSValue());
416     PropertyDescriptor descriptor;
417     auto success = toPropertyDescriptor(exec, exec->argument(2), descriptor);
418     ASSERT(!scope.exception() == success);
419     if (!success)
420         return JSValue::encode(jsNull());
421     ASSERT((descriptor.attributes() & Accessor) || (!descriptor.isAccessorDescriptor()));
422     scope.assertNoException();
423     obj->methodTable(vm)->defineOwnProperty(obj, exec, propertyName, descriptor, true);
424     scope.release();
425     return JSValue::encode(obj);
426 }
427
428 static JSValue defineProperties(ExecState* exec, JSObject* object, JSObject* properties)
429 {
430     VM& vm = exec->vm();
431     auto scope = DECLARE_THROW_SCOPE(vm);
432
433     PropertyNameArray propertyNames(exec, PropertyNameMode::StringsAndSymbols);
434     asObject(properties)->methodTable(vm)->getOwnPropertyNames(asObject(properties), exec, propertyNames, EnumerationMode(DontEnumPropertiesMode::Exclude));
435     RETURN_IF_EXCEPTION(scope, { });
436     size_t numProperties = propertyNames.size();
437     Vector<PropertyDescriptor> descriptors;
438     MarkedArgumentBuffer markBuffer;
439     for (size_t i = 0; i < numProperties; i++) {
440         JSValue prop = properties->get(exec, propertyNames[i]);
441         RETURN_IF_EXCEPTION(scope, { });
442         PropertyDescriptor descriptor;
443         bool success = toPropertyDescriptor(exec, prop, descriptor);
444         ASSERT(!scope.exception() || !success);
445         if (UNLIKELY(!success))
446             return jsNull();
447         descriptors.append(descriptor);
448         // Ensure we mark all the values that we're accumulating
449         if (descriptor.isDataDescriptor() && descriptor.value())
450             markBuffer.append(descriptor.value());
451         if (descriptor.isAccessorDescriptor()) {
452             if (descriptor.getter())
453                 markBuffer.append(descriptor.getter());
454             if (descriptor.setter())
455                 markBuffer.append(descriptor.setter());
456         }
457     }
458     for (size_t i = 0; i < numProperties; i++) {
459         Identifier propertyName = propertyNames[i];
460         if (vm.propertyNames->isPrivateName(propertyName))
461             continue;
462         object->methodTable(vm)->defineOwnProperty(object, exec, propertyName, descriptors[i], true);
463         RETURN_IF_EXCEPTION(scope, { });
464     }
465     return object;
466 }
467
468 EncodedJSValue JSC_HOST_CALL objectConstructorDefineProperties(ExecState* exec)
469 {
470     VM& vm = exec->vm();
471     auto scope = DECLARE_THROW_SCOPE(vm);
472
473     if (!exec->argument(0).isObject())
474         return throwVMTypeError(exec, scope, ASCIILiteral("Properties can only be defined on Objects."));
475     JSObject* targetObj = asObject(exec->argument(0));
476     JSObject* props = exec->argument(1).toObject(exec);
477     ASSERT(!!scope.exception() == !props);
478     if (UNLIKELY(!props))
479         return encodedJSValue();
480     scope.release();
481     return JSValue::encode(defineProperties(exec, targetObj, props));
482 }
483
484 EncodedJSValue JSC_HOST_CALL objectConstructorCreate(ExecState* exec)
485 {
486     VM& vm = exec->vm();
487     auto scope = DECLARE_THROW_SCOPE(vm);
488
489     JSValue proto = exec->argument(0);
490     if (!proto.isObject() && !proto.isNull())
491         return throwVMTypeError(exec, scope, ASCIILiteral("Object prototype may only be an Object or null."));
492     JSObject* newObject = proto.isObject()
493         ? constructEmptyObject(exec, asObject(proto))
494         : constructEmptyObject(exec, exec->lexicalGlobalObject()->nullPrototypeObjectStructure());
495     if (exec->argument(1).isUndefined())
496         return JSValue::encode(newObject);
497     if (!exec->argument(1).isObject())
498         return throwVMTypeError(exec, scope, ASCIILiteral("Property descriptor list must be an Object."));
499     scope.release();
500     return JSValue::encode(defineProperties(exec, newObject, asObject(exec->argument(1))));
501 }
502
503 enum class IntegrityLevel {
504     Sealed,
505     Frozen
506 };
507
508 template<IntegrityLevel level>
509 bool setIntegrityLevel(ExecState* exec, VM& vm, JSObject* object)
510 {
511     // See https://tc39.github.io/ecma262/#sec-setintegritylevel.
512     auto scope = DECLARE_THROW_SCOPE(vm);
513
514     bool success = object->methodTable(vm)->preventExtensions(object, exec);
515     RETURN_IF_EXCEPTION(scope, false);
516     if (UNLIKELY(!success))
517         return false;
518
519     PropertyNameArray properties(exec, PropertyNameMode::StringsAndSymbols);
520     object->methodTable(vm)->getOwnPropertyNames(object, exec, properties, EnumerationMode(DontEnumPropertiesMode::Include));
521     RETURN_IF_EXCEPTION(scope, false);
522
523     PropertyNameArray::const_iterator end = properties.end();
524     for (PropertyNameArray::const_iterator iter = properties.begin(); iter != end; ++iter) {
525         Identifier propertyName = *iter;
526         if (vm.propertyNames->isPrivateName(propertyName))
527             continue;
528
529         PropertyDescriptor desc;
530         if (level == IntegrityLevel::Sealed)
531             desc.setConfigurable(false);
532         else {
533             bool hasPropertyDescriptor = object->getOwnPropertyDescriptor(exec, propertyName, desc);
534             RETURN_IF_EXCEPTION(scope, false);
535             if (!hasPropertyDescriptor)
536                 continue;
537
538             if (desc.isDataDescriptor())
539                 desc.setWritable(false);
540
541             desc.setConfigurable(false);
542         }
543
544         object->methodTable(vm)->defineOwnProperty(object, exec, propertyName, desc, true);
545         RETURN_IF_EXCEPTION(scope, false);
546     }
547     return true;
548 }
549
550 template<IntegrityLevel level>
551 bool testIntegrityLevel(ExecState* exec, VM& vm, JSObject* object)
552 {
553     auto scope = DECLARE_THROW_SCOPE(vm);
554
555     // 1. Assert: Type(O) is Object.
556     // 2. Assert: level is either "sealed" or "frozen".
557
558     // 3. Let status be ?IsExtensible(O).
559     bool status = object->isExtensible(exec);
560     RETURN_IF_EXCEPTION(scope, { });
561
562     // 4. If status is true, return false.
563     if (status)
564         return false;
565
566     // 6. Let keys be ? O.[[OwnPropertyKeys]]().
567     PropertyNameArray keys(exec, PropertyNameMode::StringsAndSymbols);
568     object->methodTable(vm)->getOwnPropertyNames(object, exec, keys, EnumerationMode(DontEnumPropertiesMode::Include));
569     RETURN_IF_EXCEPTION(scope, { });
570
571     // 7. For each element k of keys, do
572     PropertyNameArray::const_iterator end = keys.end();
573     for (PropertyNameArray::const_iterator iter = keys.begin(); iter != end; ++iter) {
574         Identifier propertyName = *iter;
575         if (vm.propertyNames->isPrivateName(propertyName))
576             continue;
577
578         // a. Let currentDesc be ? O.[[GetOwnProperty]](k)
579         PropertyDescriptor desc;
580         bool didGetDescriptor = object->getOwnPropertyDescriptor(exec, propertyName, desc);
581         RETURN_IF_EXCEPTION(scope, { });
582
583         // b. If currentDesc is not undefined, then
584         if (!didGetDescriptor)
585             continue;
586
587         // i. If currentDesc.[[Configurable]] is true, return false.
588         if (desc.configurable())
589             return false;
590
591         // ii. If level is "frozen" and IsDataDescriptor(currentDesc) is true, then
592         // 1. If currentDesc.[[Writable]] is true, return false.
593         if (level == IntegrityLevel::Frozen && desc.isDataDescriptor() && desc.writable())
594             return false;
595     }
596
597     return true;
598 }
599
600 EncodedJSValue JSC_HOST_CALL objectConstructorSeal(ExecState* exec)
601 {
602     VM& vm = exec->vm();
603     auto scope = DECLARE_THROW_SCOPE(vm);
604
605     // 1. If Type(O) is not Object, return O.
606     JSValue obj = exec->argument(0);
607     if (!obj.isObject())
608         return JSValue::encode(obj);
609     JSObject* object = asObject(obj);
610
611     if (isJSFinalObject(object) && !hasIndexedProperties(object->indexingType())) {
612         object->seal(vm);
613         return JSValue::encode(obj);
614     }
615
616     bool success = setIntegrityLevel<IntegrityLevel::Sealed>(exec, vm, object);
617     RETURN_IF_EXCEPTION(scope, encodedJSValue());
618     if (UNLIKELY(!success)) {
619         throwTypeError(exec, scope, ASCIILiteral("Unable to prevent extension in Object.seal"));
620         return encodedJSValue();
621     }
622
623     return JSValue::encode(obj);
624 }
625
626 JSObject* objectConstructorFreeze(ExecState* exec, JSObject* object)
627 {
628     VM& vm = exec->vm();
629     auto scope = DECLARE_THROW_SCOPE(vm);
630
631     if (isJSFinalObject(object) && !hasIndexedProperties(object->indexingType())) {
632         object->freeze(vm);
633         return object;
634     }
635
636     bool success = setIntegrityLevel<IntegrityLevel::Frozen>(exec, vm, object);
637     RETURN_IF_EXCEPTION(scope, nullptr);
638     if (!success)
639         return throwTypeError(exec, scope, ASCIILiteral("Unable to prevent extension in Object.freeze"));
640     return object;
641 }
642
643 EncodedJSValue JSC_HOST_CALL objectConstructorFreeze(ExecState* exec)
644 {
645     VM& vm = exec->vm();
646     auto scope = DECLARE_THROW_SCOPE(vm);
647     // 1. If Type(O) is not Object, return O.
648     JSValue obj = exec->argument(0);
649     if (!obj.isObject())
650         return JSValue::encode(obj);
651     JSObject* result = objectConstructorFreeze(exec, asObject(obj));
652     RETURN_IF_EXCEPTION(scope, encodedJSValue());
653     return JSValue::encode(result);
654 }
655
656 EncodedJSValue JSC_HOST_CALL objectConstructorPreventExtensions(ExecState* exec)
657 {
658     VM& vm = exec->vm();
659     JSValue argument = exec->argument(0);
660     if (!argument.isObject())
661         return JSValue::encode(argument);
662     JSObject* object = asObject(argument);
663     object->methodTable(vm)->preventExtensions(object, exec);
664     return JSValue::encode(object);
665 }
666
667 EncodedJSValue JSC_HOST_CALL objectConstructorIsSealed(ExecState* exec)
668 {
669     VM& vm = exec->vm();
670
671     // 1. If Type(O) is not Object, return true.
672     JSValue obj = exec->argument(0);
673     if (!obj.isObject())
674         return JSValue::encode(jsBoolean(true));
675     JSObject* object = asObject(obj);
676
677     // Quick check for final objects.
678     if (isJSFinalObject(object) && !hasIndexedProperties(object->indexingType()))
679         return JSValue::encode(jsBoolean(object->isSealed(vm)));
680
681     // 2. Return ? TestIntegrityLevel(O, "sealed").
682     return JSValue::encode(jsBoolean(testIntegrityLevel<IntegrityLevel::Sealed>(exec, vm, object)));
683 }
684
685 EncodedJSValue JSC_HOST_CALL objectConstructorIsFrozen(ExecState* exec)
686 {
687     VM& vm = exec->vm();
688
689     // 1. If Type(O) is not Object, return true.
690     JSValue obj = exec->argument(0);
691     if (!obj.isObject())
692         return JSValue::encode(jsBoolean(true));
693     JSObject* object = asObject(obj);
694
695     // Quick check for final objects.
696     if (isJSFinalObject(object) && !hasIndexedProperties(object->indexingType()))
697         return JSValue::encode(jsBoolean(object->isFrozen(vm)));
698
699     // 2. Return ? TestIntegrityLevel(O, "frozen").
700     return JSValue::encode(jsBoolean(testIntegrityLevel<IntegrityLevel::Frozen>(exec, vm, object)));
701 }
702
703 EncodedJSValue JSC_HOST_CALL objectConstructorIsExtensible(ExecState* exec)
704 {
705     VM& vm = exec->vm();
706     auto scope = DECLARE_THROW_SCOPE(vm);
707     JSValue obj = exec->argument(0);
708     if (!obj.isObject())
709         return JSValue::encode(jsBoolean(false));
710     JSObject* object = asObject(obj);
711     bool isExtensible = object->isExtensible(exec);
712     RETURN_IF_EXCEPTION(scope, encodedJSValue());
713     return JSValue::encode(jsBoolean(isExtensible));
714 }
715
716 EncodedJSValue JSC_HOST_CALL objectConstructorIs(ExecState* exec)
717 {
718     return JSValue::encode(jsBoolean(sameValue(exec, exec->argument(0), exec->argument(1))));
719 }
720
721 // FIXME: Use the enumeration cache.
722 JSArray* ownPropertyKeys(ExecState* exec, JSObject* object, PropertyNameMode propertyNameMode, DontEnumPropertiesMode dontEnumPropertiesMode)
723 {
724     VM& vm = exec->vm();
725     auto scope = DECLARE_THROW_SCOPE(vm);
726     PropertyNameArray properties(exec, propertyNameMode);
727     object->methodTable(vm)->getOwnPropertyNames(object, exec, properties, EnumerationMode(dontEnumPropertiesMode));
728     RETURN_IF_EXCEPTION(scope, nullptr);
729
730     JSArray* keys = constructEmptyArray(exec, 0);
731     RETURN_IF_EXCEPTION(scope, nullptr);
732
733     // https://tc39.github.io/ecma262/#sec-enumerableownproperties
734     // If {object} is a Proxy, an explicit and observable [[GetOwnProperty]] op is required to filter out non-enumerable properties.
735     // In other cases, filtering has already been performed.
736     const bool mustFilterProperty = dontEnumPropertiesMode == DontEnumPropertiesMode::Exclude && object->type() == ProxyObjectType;
737     auto filterPropertyIfNeeded = [exec, object, mustFilterProperty](const Identifier& identifier) {
738         if (!mustFilterProperty)
739             return true;
740         PropertyDescriptor descriptor;
741         PropertyName name(identifier);
742         return object->getOwnPropertyDescriptor(exec, name, descriptor) && descriptor.enumerable();
743     };
744
745     switch (propertyNameMode) {
746     case PropertyNameMode::Strings: {
747         size_t numProperties = properties.size();
748         for (size_t i = 0; i < numProperties; i++) {
749             const auto& identifier = properties[i];
750             ASSERT(!identifier.isSymbol());
751             if (filterPropertyIfNeeded(identifier))
752                 keys->push(exec, jsOwnedString(exec, identifier.string()));
753             RETURN_IF_EXCEPTION(scope, nullptr);
754         }
755         break;
756     }
757
758     case PropertyNameMode::Symbols: {
759         size_t numProperties = properties.size();
760         for (size_t i = 0; i < numProperties; i++) {
761             const auto& identifier = properties[i];
762             ASSERT(identifier.isSymbol());
763             if (!vm.propertyNames->isPrivateName(identifier)) {
764                 if (filterPropertyIfNeeded(identifier))
765                     keys->push(exec, Symbol::create(vm, static_cast<SymbolImpl&>(*identifier.impl())));
766                 RETURN_IF_EXCEPTION(scope, nullptr);
767             }
768         }
769         break;
770     }
771
772     case PropertyNameMode::StringsAndSymbols: {
773         Vector<Identifier, 16> propertySymbols;
774         size_t numProperties = properties.size();
775         for (size_t i = 0; i < numProperties; i++) {
776             const auto& identifier = properties[i];
777             if (identifier.isSymbol()) {
778                 if (!vm.propertyNames->isPrivateName(identifier))
779                     propertySymbols.append(identifier);
780             } else {
781                 if (filterPropertyIfNeeded(identifier))
782                     keys->push(exec, jsOwnedString(exec, identifier.string()));
783                 RETURN_IF_EXCEPTION(scope, nullptr);
784             }
785         }
786
787         // To ensure the order defined in the spec (9.1.12), we append symbols at the last elements of keys.
788         for (const auto& identifier : propertySymbols) {
789             if (filterPropertyIfNeeded(identifier))
790                 keys->push(exec, Symbol::create(vm, static_cast<SymbolImpl&>(*identifier.impl())));
791             RETURN_IF_EXCEPTION(scope, nullptr);
792         }
793
794         break;
795     }
796     }
797
798     return keys;
799 }
800
801 } // namespace JSC