WKWebViewConfiguration._corsDisablingPatterns should also disable CORS for non-Docume...
[WebKit.git] / Source / JavaScriptCore / runtime / ObjectConstructor.cpp
1 /*
2  *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3  *  Copyright (C) 2008-2019 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 "JSImmutableButterfly.h"
34 #include "Lookup.h"
35 #include "ObjectPrototype.h"
36 #include "PropertyDescriptor.h"
37 #include "PropertyNameArray.h"
38 #include "StackVisitor.h"
39 #include "Symbol.h"
40
41 namespace JSC {
42
43 EncodedJSValue JSC_HOST_CALL objectConstructorAssign(JSGlobalObject*, CallFrame*);
44 EncodedJSValue JSC_HOST_CALL objectConstructorValues(JSGlobalObject*, CallFrame*);
45 EncodedJSValue JSC_HOST_CALL objectConstructorGetPrototypeOf(JSGlobalObject*, CallFrame*);
46 EncodedJSValue JSC_HOST_CALL objectConstructorSetPrototypeOf(JSGlobalObject*, CallFrame*);
47 EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyNames(JSGlobalObject*, CallFrame*);
48 EncodedJSValue JSC_HOST_CALL objectConstructorDefineProperty(JSGlobalObject*, CallFrame*);
49 EncodedJSValue JSC_HOST_CALL objectConstructorDefineProperties(JSGlobalObject*, CallFrame*);
50 EncodedJSValue JSC_HOST_CALL objectConstructorCreate(JSGlobalObject*, CallFrame*);
51 EncodedJSValue JSC_HOST_CALL objectConstructorSeal(JSGlobalObject*, CallFrame*);
52 EncodedJSValue JSC_HOST_CALL objectConstructorFreeze(JSGlobalObject*, CallFrame*);
53 EncodedJSValue JSC_HOST_CALL objectConstructorPreventExtensions(JSGlobalObject*, CallFrame*);
54 EncodedJSValue JSC_HOST_CALL objectConstructorIsSealed(JSGlobalObject*, CallFrame*);
55 EncodedJSValue JSC_HOST_CALL objectConstructorIsFrozen(JSGlobalObject*, CallFrame*);
56 EncodedJSValue JSC_HOST_CALL objectConstructorIsExtensible(JSGlobalObject*, CallFrame*);
57 EncodedJSValue JSC_HOST_CALL objectConstructorIs(JSGlobalObject*, CallFrame*);
58
59 }
60
61 #include "ObjectConstructor.lut.h"
62
63 namespace JSC {
64
65 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(ObjectConstructor);
66
67 const ClassInfo ObjectConstructor::s_info = { "Function", &InternalFunction::s_info, &objectConstructorTable, nullptr, CREATE_METHOD_TABLE(ObjectConstructor) };
68
69 /* Source for ObjectConstructor.lut.h
70 @begin objectConstructorTable
71   getPrototypeOf            objectConstructorGetPrototypeOf             DontEnum|Function 1 ObjectGetPrototypeOfIntrinsic
72   setPrototypeOf            objectConstructorSetPrototypeOf             DontEnum|Function 2
73   getOwnPropertyDescriptor  objectConstructorGetOwnPropertyDescriptor   DontEnum|Function 2
74   getOwnPropertyDescriptors objectConstructorGetOwnPropertyDescriptors  DontEnum|Function 1
75   getOwnPropertyNames       objectConstructorGetOwnPropertyNames        DontEnum|Function 1
76   getOwnPropertySymbols     objectConstructorGetOwnPropertySymbols      DontEnum|Function 1
77   keys                      objectConstructorKeys                       DontEnum|Function 1 ObjectKeysIntrinsic
78   defineProperty            objectConstructorDefineProperty             DontEnum|Function 3
79   defineProperties          objectConstructorDefineProperties           DontEnum|Function 2
80   create                    objectConstructorCreate                     DontEnum|Function 2 ObjectCreateIntrinsic
81   seal                      objectConstructorSeal                       DontEnum|Function 1
82   freeze                    objectConstructorFreeze                     DontEnum|Function 1
83   preventExtensions         objectConstructorPreventExtensions          DontEnum|Function 1
84   isSealed                  objectConstructorIsSealed                   DontEnum|Function 1
85   isFrozen                  objectConstructorIsFrozen                   DontEnum|Function 1
86   isExtensible              objectConstructorIsExtensible               DontEnum|Function 1
87   is                        objectConstructorIs                         DontEnum|Function 2 ObjectIsIntrinsic
88   assign                    objectConstructorAssign                     DontEnum|Function 2
89   values                    objectConstructorValues                     DontEnum|Function 1
90   entries                   JSBuiltin                                   DontEnum|Function 1
91   fromEntries               JSBuiltin                                   DontEnum|Function 1
92 @end
93 */
94
95
96 static EncodedJSValue JSC_HOST_CALL callObjectConstructor(JSGlobalObject*, CallFrame*);
97 static EncodedJSValue JSC_HOST_CALL constructWithObjectConstructor(JSGlobalObject*, CallFrame*);
98
99 ObjectConstructor::ObjectConstructor(VM& vm, Structure* structure)
100     : InternalFunction(vm, structure, callObjectConstructor, constructWithObjectConstructor)
101 {
102 }
103
104 void ObjectConstructor::finishCreation(VM& vm, JSGlobalObject* globalObject, ObjectPrototype* objectPrototype)
105 {
106     Base::finishCreation(vm, vm.propertyNames->Object.string(), NameAdditionMode::WithoutStructureTransition);
107
108     putDirectWithoutTransition(vm, vm.propertyNames->prototype, objectPrototype, PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly);
109     putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(1), PropertyAttribute::ReadOnly | PropertyAttribute::DontEnum);
110
111     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().createPrivateName(), objectConstructorCreate, static_cast<unsigned>(PropertyAttribute::DontEnum), 2);
112     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().definePropertyPrivateName(), objectConstructorDefineProperty, static_cast<unsigned>(PropertyAttribute::DontEnum), 3);
113     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().getPrototypeOfPrivateName(), objectConstructorGetPrototypeOf, static_cast<unsigned>(PropertyAttribute::DontEnum), 1);
114     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().getOwnPropertyNamesPrivateName(), objectConstructorGetOwnPropertyNames, static_cast<unsigned>(PropertyAttribute::DontEnum), 1);
115 }
116
117 // ES 19.1.1.1 Object([value])
118 static ALWAYS_INLINE JSObject* constructObjectWithNewTarget(JSGlobalObject* globalObject, CallFrame* callFrame, JSValue newTarget)
119 {
120     VM& vm = globalObject->vm();
121     ObjectConstructor* objectConstructor = jsCast<ObjectConstructor*>(callFrame->jsCallee());
122     auto scope = DECLARE_THROW_SCOPE(vm);
123
124     // We need to check newTarget condition in this caller side instead of InternalFunction::createSubclassStructure side.
125     // Since if we found this condition is met, we should not fall into the type conversion in the step 3.
126
127     // 1. If NewTarget is neither undefined nor the active function, then
128     if (newTarget && newTarget != objectConstructor) {
129         // a. Return ? OrdinaryCreateFromConstructor(NewTarget, "%ObjectPrototype%").
130         Structure* objectStructure = InternalFunction::createSubclassStructure(globalObject, objectConstructor, newTarget, globalObject->objectStructureForObjectConstructor());
131         RETURN_IF_EXCEPTION(scope, nullptr);
132         return constructEmptyObject(vm, objectStructure);
133     }
134
135     // 2. If value is null, undefined or not supplied, return ObjectCreate(%ObjectPrototype%).
136     JSValue argument = callFrame->argument(0);
137     if (argument.isUndefinedOrNull())
138         return constructEmptyObject(vm, globalObject->objectStructureForObjectConstructor());
139
140     // 3. Return ToObject(value).
141     RELEASE_AND_RETURN(scope, argument.toObject(globalObject));
142 }
143
144 static EncodedJSValue JSC_HOST_CALL constructWithObjectConstructor(JSGlobalObject* globalObject, CallFrame* callFrame)
145 {
146     return JSValue::encode(constructObjectWithNewTarget(globalObject, callFrame, callFrame->newTarget()));
147 }
148
149 static EncodedJSValue JSC_HOST_CALL callObjectConstructor(JSGlobalObject* globalObject, CallFrame* callFrame)
150 {
151     return JSValue::encode(constructObjectWithNewTarget(globalObject, callFrame, JSValue()));
152 }
153
154 EncodedJSValue JSC_HOST_CALL objectConstructorGetPrototypeOf(JSGlobalObject* globalObject, CallFrame* callFrame)
155 {
156     VM& vm = globalObject->vm();
157     auto scope = DECLARE_THROW_SCOPE(vm);
158     JSObject* object = callFrame->argument(0).toObject(globalObject);
159     RETURN_IF_EXCEPTION(scope, encodedJSValue());
160     RELEASE_AND_RETURN(scope, JSValue::encode(object->getPrototype(vm, globalObject)));
161 }
162
163 EncodedJSValue JSC_HOST_CALL objectConstructorSetPrototypeOf(JSGlobalObject* globalObject, CallFrame* callFrame)
164 {
165     VM& vm = globalObject->vm();
166     auto scope = DECLARE_THROW_SCOPE(vm);
167
168     JSValue objectValue = callFrame->argument(0);
169     if (objectValue.isUndefinedOrNull())
170         return throwVMTypeError(globalObject, scope, "Cannot set prototype of undefined or null"_s);
171
172     JSValue protoValue = callFrame->argument(1);
173     if (!protoValue.isObject() && !protoValue.isNull())
174         return throwVMTypeError(globalObject, scope, "Prototype value can only be an object or null"_s);
175
176     JSObject* object = objectValue.toObject(globalObject);
177     RETURN_IF_EXCEPTION(scope, encodedJSValue());
178
179     bool shouldThrowIfCantSet = true;
180     bool didSetPrototype = object->setPrototype(vm, globalObject, protoValue, shouldThrowIfCantSet);
181     EXCEPTION_ASSERT_UNUSED(didSetPrototype, scope.exception() || didSetPrototype);
182     return JSValue::encode(objectValue);
183 }
184
185 JSValue objectConstructorGetOwnPropertyDescriptor(JSGlobalObject* globalObject, JSObject* object, const Identifier& propertyName)
186 {
187     VM& vm = globalObject->vm();
188     auto scope = DECLARE_THROW_SCOPE(vm);
189     PropertyDescriptor descriptor;
190     if (!object->getOwnPropertyDescriptor(globalObject, propertyName, descriptor))
191         RELEASE_AND_RETURN(scope, jsUndefined());
192     RETURN_IF_EXCEPTION(scope, { });
193
194     JSObject* result = constructObjectFromPropertyDescriptor(globalObject, descriptor);
195     EXCEPTION_ASSERT(!!scope.exception() == !result);
196     if (!result)
197         return jsUndefined();
198     return result;
199 }
200
201 JSValue objectConstructorGetOwnPropertyDescriptors(JSGlobalObject* globalObject, JSObject* object)
202 {
203     VM& vm = globalObject->vm();
204     auto scope = DECLARE_THROW_SCOPE(vm);
205     PropertyNameArray properties(vm, PropertyNameMode::StringsAndSymbols, PrivateSymbolMode::Exclude);
206     object->methodTable(vm)->getOwnPropertyNames(object, globalObject, properties, EnumerationMode(DontEnumPropertiesMode::Include));
207     RETURN_IF_EXCEPTION(scope, { });
208
209     JSObject* descriptors = constructEmptyObject(globalObject);
210     RETURN_IF_EXCEPTION(scope, { });
211
212     for (auto& propertyName : properties) {
213         PropertyDescriptor descriptor;
214         bool didGetDescriptor = object->getOwnPropertyDescriptor(globalObject, propertyName, descriptor);
215         RETURN_IF_EXCEPTION(scope, { });
216
217         if (!didGetDescriptor)
218             continue;
219
220         JSObject* fromDescriptor = constructObjectFromPropertyDescriptor(globalObject, descriptor);
221         EXCEPTION_ASSERT(!!scope.exception() == !fromDescriptor);
222         if (!fromDescriptor)
223             return jsUndefined();
224
225         PutPropertySlot slot(descriptors);
226         descriptors->putOwnDataPropertyMayBeIndex(globalObject, propertyName, fromDescriptor, slot);
227         scope.assertNoException();
228     }
229
230     return descriptors;
231 }
232
233 EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyDescriptor(JSGlobalObject* globalObject, CallFrame* callFrame)
234 {
235     VM& vm = globalObject->vm();
236     auto scope = DECLARE_THROW_SCOPE(vm);
237     JSObject* object = callFrame->argument(0).toObject(globalObject);
238     RETURN_IF_EXCEPTION(scope, encodedJSValue());
239     auto propertyName = callFrame->argument(1).toPropertyKey(globalObject);
240     RETURN_IF_EXCEPTION(scope, encodedJSValue());
241     RELEASE_AND_RETURN(scope, JSValue::encode(objectConstructorGetOwnPropertyDescriptor(globalObject, object, propertyName)));
242 }
243
244 EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyDescriptors(JSGlobalObject* globalObject, CallFrame* callFrame)
245 {
246     VM& vm = globalObject->vm();
247     auto scope = DECLARE_THROW_SCOPE(vm);
248     JSObject* object = callFrame->argument(0).toObject(globalObject);
249     RETURN_IF_EXCEPTION(scope, encodedJSValue());
250     RELEASE_AND_RETURN(scope, JSValue::encode(objectConstructorGetOwnPropertyDescriptors(globalObject, object)));
251 }
252
253 // FIXME: Use the enumeration cache.
254 EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyNames(JSGlobalObject* globalObject, CallFrame* callFrame)
255 {
256     VM& vm = globalObject->vm();
257     auto scope = DECLARE_THROW_SCOPE(vm);
258     JSObject* object = callFrame->argument(0).toObject(globalObject);
259     RETURN_IF_EXCEPTION(scope, encodedJSValue());
260     RELEASE_AND_RETURN(scope, JSValue::encode(ownPropertyKeys(globalObject, object, PropertyNameMode::Strings, DontEnumPropertiesMode::Include)));
261 }
262
263 // FIXME: Use the enumeration cache.
264 EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertySymbols(JSGlobalObject* globalObject, CallFrame* callFrame)
265 {
266     VM& vm = globalObject->vm();
267     auto scope = DECLARE_THROW_SCOPE(vm);
268     JSObject* object = callFrame->argument(0).toObject(globalObject);
269     RETURN_IF_EXCEPTION(scope, encodedJSValue());
270     RELEASE_AND_RETURN(scope, JSValue::encode(ownPropertyKeys(globalObject, object, PropertyNameMode::Symbols, DontEnumPropertiesMode::Include)));
271 }
272
273 EncodedJSValue JSC_HOST_CALL objectConstructorKeys(JSGlobalObject* globalObject, CallFrame* callFrame)
274 {
275     VM& vm = globalObject->vm();
276     auto scope = DECLARE_THROW_SCOPE(vm);
277     JSObject* object = callFrame->argument(0).toObject(globalObject);
278     RETURN_IF_EXCEPTION(scope, encodedJSValue());
279     RELEASE_AND_RETURN(scope, JSValue::encode(ownPropertyKeys(globalObject, object, PropertyNameMode::Strings, DontEnumPropertiesMode::Exclude)));
280 }
281
282 EncodedJSValue JSC_HOST_CALL objectConstructorAssign(JSGlobalObject* globalObject, CallFrame* callFrame)
283 {
284     VM& vm = globalObject->vm();
285     auto scope = DECLARE_THROW_SCOPE(vm);
286
287     JSValue targetValue = callFrame->argument(0);
288     if (targetValue.isUndefinedOrNull())
289         return throwVMTypeError(globalObject, scope, "Object.assign requires that input parameter not be null or undefined"_s);
290     JSObject* target = targetValue.toObject(globalObject);
291     RETURN_IF_EXCEPTION(scope, { });
292
293     // FIXME: Extend this for non JSFinalObject. For example, we would like to use this fast path for function objects too.
294     // https://bugs.webkit.org/show_bug.cgi?id=185358
295     bool targetCanPerformFastPut = jsDynamicCast<JSFinalObject*>(vm, target) && target->canPerformFastPutInlineExcludingProto(vm);
296
297     Vector<RefPtr<UniquedStringImpl>, 8> properties;
298     MarkedArgumentBuffer values;
299     unsigned argsCount = callFrame->argumentCount();
300     for (unsigned i = 1; i < argsCount; ++i) {
301         JSValue sourceValue = callFrame->uncheckedArgument(i);
302         if (sourceValue.isUndefinedOrNull())
303             continue;
304         JSObject* source = sourceValue.toObject(globalObject);
305         RETURN_IF_EXCEPTION(scope, { });
306
307         if (targetCanPerformFastPut) {
308             if (!source->staticPropertiesReified(vm)) {
309                 source->reifyAllStaticProperties(globalObject);
310                 RETURN_IF_EXCEPTION(scope, { });
311             }
312
313             auto canPerformFastPropertyEnumerationForObjectAssign = [] (Structure* structure) {
314                 if (structure->typeInfo().overridesGetOwnPropertySlot())
315                     return false;
316                 if (structure->typeInfo().overridesGetPropertyNames())
317                     return false;
318                 // FIXME: Indexed properties can be handled.
319                 // https://bugs.webkit.org/show_bug.cgi?id=185358
320                 if (hasIndexedProperties(structure->indexingType()))
321                     return false;
322                 if (structure->hasGetterSetterProperties())
323                     return false;
324                 if (structure->isUncacheableDictionary())
325                     return false;
326                 // Cannot perform fast [[Put]] to |target| if the property names of the |source| contain "__proto__".
327                 if (structure->hasUnderscoreProtoPropertyExcludingOriginalProto())
328                     return false;
329                 return true;
330             };
331
332             if (canPerformFastPropertyEnumerationForObjectAssign(source->structure(vm))) {
333                 // |source| Structure does not have any getters. And target can perform fast put.
334                 // So enumerating properties and putting properties are non observable.
335
336                 // FIXME: It doesn't seem like we should have to do this in two phases, but
337                 // we're running into crashes where it appears that source is transitioning
338                 // under us, and even ends up in a state where it has a null butterfly. My
339                 // leading hypothesis here is that we fire some value replacement watchpoint
340                 // that ends up transitioning the structure underneath us.
341                 // https://bugs.webkit.org/show_bug.cgi?id=187837
342
343                 // Do not clear since Vector::clear shrinks the backing store.
344                 properties.resize(0);
345                 values.clear();
346                 source->structure(vm)->forEachProperty(vm, [&] (const PropertyMapEntry& entry) -> bool {
347                     if (entry.attributes & PropertyAttribute::DontEnum)
348                         return true;
349
350                     PropertyName propertyName(entry.key);
351                     if (propertyName.isPrivateName())
352                         return true;
353
354                     properties.append(entry.key);
355                     values.appendWithCrashOnOverflow(source->getDirect(entry.offset));
356
357                     return true;
358                 });
359
360                 for (size_t i = 0; i < properties.size(); ++i) {
361                     // FIXME: We could put properties in a batching manner to accelerate Object.assign more.
362                     // https://bugs.webkit.org/show_bug.cgi?id=185358
363                     PutPropertySlot putPropertySlot(target, true);
364                     target->putOwnDataProperty(vm, properties[i].get(), values.at(i), putPropertySlot);
365                 }
366                 continue;
367             }
368         }
369
370         // [[GetOwnPropertyNames]], [[Get]] etc. could modify target object and invalidate this assumption.
371         // For example, [[Get]] of source object could configure setter to target object. So disable the fast path.
372         targetCanPerformFastPut = false;
373
374         PropertyNameArray properties(vm, PropertyNameMode::StringsAndSymbols, PrivateSymbolMode::Exclude);
375         source->methodTable(vm)->getOwnPropertyNames(source, globalObject, properties, EnumerationMode(DontEnumPropertiesMode::Include));
376         RETURN_IF_EXCEPTION(scope, { });
377
378         auto assign = [&] (PropertyName propertyName) {
379             PropertySlot slot(source, PropertySlot::InternalMethodType::GetOwnProperty);
380             bool hasProperty = source->methodTable(vm)->getOwnPropertySlot(source, globalObject, propertyName, slot);
381             RETURN_IF_EXCEPTION(scope, void());
382             if (!hasProperty)
383                 return;
384             if (slot.attributes() & PropertyAttribute::DontEnum)
385                 return;
386
387             JSValue value;
388             if (LIKELY(!slot.isTaintedByOpaqueObject()))
389                 value = slot.getValue(globalObject, propertyName);
390             else
391                 value = source->get(globalObject, propertyName);
392             RETURN_IF_EXCEPTION(scope, void());
393
394             PutPropertySlot putPropertySlot(target, true);
395             target->putInline(globalObject, propertyName, value, putPropertySlot);
396         };
397
398         // First loop is for strings. Second loop is for symbols to keep standardized order requirement in the spec.
399         // https://tc39.github.io/ecma262/#sec-ordinaryownpropertykeys
400         bool foundSymbol = false;
401         unsigned numProperties = properties.size();
402         for (unsigned j = 0; j < numProperties; j++) {
403             const auto& propertyName = properties[j];
404             if (propertyName.isSymbol()) {
405                 foundSymbol = true;
406                 continue;
407             }
408
409             assign(propertyName);
410             RETURN_IF_EXCEPTION(scope, { });
411         }
412
413         if (foundSymbol) {
414             for (unsigned j = 0; j < numProperties; j++) {
415                 const auto& propertyName = properties[j];
416                 if (propertyName.isSymbol()) {
417                     ASSERT(!propertyName.isPrivateName());
418                     assign(propertyName);
419                     RETURN_IF_EXCEPTION(scope, { });
420                 }
421             }
422         }
423     }
424     return JSValue::encode(target);
425 }
426
427 EncodedJSValue JSC_HOST_CALL objectConstructorValues(JSGlobalObject* globalObject, CallFrame* callFrame)
428 {
429     VM& vm = globalObject->vm();
430     auto scope = DECLARE_THROW_SCOPE(vm);
431
432     JSValue targetValue = callFrame->argument(0);
433     if (targetValue.isUndefinedOrNull())
434         return throwVMTypeError(globalObject, scope, "Object.values requires that input parameter not be null or undefined"_s);
435     JSObject* target = targetValue.toObject(globalObject);
436     RETURN_IF_EXCEPTION(scope, { });
437
438     JSArray* values = constructEmptyArray(globalObject, nullptr);
439     RETURN_IF_EXCEPTION(scope, { });
440
441     PropertyNameArray properties(vm, PropertyNameMode::Strings, PrivateSymbolMode::Exclude);
442     target->methodTable(vm)->getOwnPropertyNames(target, globalObject, properties, EnumerationMode(DontEnumPropertiesMode::Include));
443     RETURN_IF_EXCEPTION(scope, { });
444
445     unsigned index = 0;
446     auto addValue = [&] (PropertyName propertyName) {
447         PropertySlot slot(target, PropertySlot::InternalMethodType::GetOwnProperty);
448         bool hasProperty = target->methodTable(vm)->getOwnPropertySlot(target, globalObject, propertyName, slot);
449         RETURN_IF_EXCEPTION(scope, void());
450         if (!hasProperty)
451             return;
452         if (slot.attributes() & PropertyAttribute::DontEnum)
453             return;
454
455         JSValue value;
456         if (LIKELY(!slot.isTaintedByOpaqueObject()))
457             value = slot.getValue(globalObject, propertyName);
458         else
459             value = target->get(globalObject, propertyName);
460         RETURN_IF_EXCEPTION(scope, void());
461
462         values->putDirectIndex(globalObject, index++, value);
463     };
464
465     for (unsigned i = 0, numProperties = properties.size(); i < numProperties; i++) {
466         const auto& propertyName = properties[i];
467         if (propertyName.isSymbol())
468             continue;
469
470         addValue(propertyName);
471         RETURN_IF_EXCEPTION(scope, { });
472     }
473
474     return JSValue::encode(values);
475 }
476
477
478 // ES6 6.2.4.5 ToPropertyDescriptor
479 // https://tc39.github.io/ecma262/#sec-topropertydescriptor
480 bool toPropertyDescriptor(JSGlobalObject* globalObject, JSValue in, PropertyDescriptor& desc)
481 {
482     VM& vm = globalObject->vm();
483     auto scope = DECLARE_THROW_SCOPE(vm);
484
485     if (!in.isObject()) {
486         throwTypeError(globalObject, scope, "Property description must be an object."_s);
487         return false;
488     }
489     JSObject* description = asObject(in);
490
491     bool hasProperty = description->hasProperty(globalObject, vm.propertyNames->enumerable);
492     EXCEPTION_ASSERT(!scope.exception() || !hasProperty);
493     if (hasProperty) {
494         JSValue value = description->get(globalObject, vm.propertyNames->enumerable);
495         RETURN_IF_EXCEPTION(scope, false);
496         desc.setEnumerable(value.toBoolean(globalObject));
497     } else
498         RETURN_IF_EXCEPTION(scope, false);
499
500     hasProperty = description->hasProperty(globalObject, vm.propertyNames->configurable);
501     EXCEPTION_ASSERT(!scope.exception() || !hasProperty);
502     if (hasProperty) {
503         JSValue value = description->get(globalObject, vm.propertyNames->configurable);
504         RETURN_IF_EXCEPTION(scope, false);
505         desc.setConfigurable(value.toBoolean(globalObject));
506     } else
507         RETURN_IF_EXCEPTION(scope, false);
508
509     JSValue value;
510     hasProperty = description->hasProperty(globalObject, vm.propertyNames->value);
511     EXCEPTION_ASSERT(!scope.exception() || !hasProperty);
512     if (hasProperty) {
513         JSValue value = description->get(globalObject, vm.propertyNames->value);
514         RETURN_IF_EXCEPTION(scope, false);
515         desc.setValue(value);
516     } else
517         RETURN_IF_EXCEPTION(scope, false);
518
519     hasProperty = description->hasProperty(globalObject, vm.propertyNames->writable);
520     EXCEPTION_ASSERT(!scope.exception() || !hasProperty);
521     if (hasProperty) {
522         JSValue value = description->get(globalObject, vm.propertyNames->writable);
523         RETURN_IF_EXCEPTION(scope, false);
524         desc.setWritable(value.toBoolean(globalObject));
525     } else
526         RETURN_IF_EXCEPTION(scope, false);
527
528     hasProperty = description->hasProperty(globalObject, vm.propertyNames->get);
529     EXCEPTION_ASSERT(!scope.exception() || !hasProperty);
530     if (hasProperty) {
531         JSValue get = description->get(globalObject, vm.propertyNames->get);
532         RETURN_IF_EXCEPTION(scope, false);
533         if (!get.isUndefined()) {
534             CallData callData;
535             if (getCallData(vm, get, callData) == CallType::None) {
536                 throwTypeError(globalObject, scope, "Getter must be a function."_s);
537                 return false;
538             }
539         }
540         desc.setGetter(get);
541     } else
542         RETURN_IF_EXCEPTION(scope, false);
543
544     hasProperty = description->hasProperty(globalObject, vm.propertyNames->set);
545     EXCEPTION_ASSERT(!scope.exception() || !hasProperty);
546     if (hasProperty) {
547         JSValue set = description->get(globalObject, vm.propertyNames->set);
548         RETURN_IF_EXCEPTION(scope, false);
549         if (!set.isUndefined()) {
550             CallData callData;
551             if (getCallData(vm, set, callData) == CallType::None) {
552                 throwTypeError(globalObject, scope, "Setter must be a function."_s);
553                 return false;
554             }
555         }
556         desc.setSetter(set);
557     } else
558         RETURN_IF_EXCEPTION(scope, false);
559
560     if (!desc.isAccessorDescriptor())
561         return true;
562
563     if (desc.value()) {
564         throwTypeError(globalObject, scope, "Invalid property.  'value' present on property with getter or setter."_s);
565         return false;
566     }
567
568     if (desc.writablePresent()) {
569         throwTypeError(globalObject, scope, "Invalid property.  'writable' present on property with getter or setter."_s);
570         return false;
571     }
572     return true;
573 }
574
575 EncodedJSValue JSC_HOST_CALL objectConstructorDefineProperty(JSGlobalObject* globalObject, CallFrame* callFrame)
576 {
577     VM& vm = globalObject->vm();
578     auto scope = DECLARE_THROW_SCOPE(vm);
579
580     if (!callFrame->argument(0).isObject())
581         return throwVMTypeError(globalObject, scope, "Properties can only be defined on Objects."_s);
582     JSObject* obj = asObject(callFrame->argument(0));
583     auto propertyName = callFrame->argument(1).toPropertyKey(globalObject);
584     RETURN_IF_EXCEPTION(scope, encodedJSValue());
585     PropertyDescriptor descriptor;
586     auto success = toPropertyDescriptor(globalObject, callFrame->argument(2), descriptor);
587     EXCEPTION_ASSERT(!scope.exception() == success);
588     if (!success)
589         return JSValue::encode(jsNull());
590     ASSERT((descriptor.attributes() & PropertyAttribute::Accessor) || (!descriptor.isAccessorDescriptor()));
591     scope.assertNoException();
592     obj->methodTable(vm)->defineOwnProperty(obj, globalObject, propertyName, descriptor, true);
593     RELEASE_AND_RETURN(scope, JSValue::encode(obj));
594 }
595
596 static JSValue defineProperties(JSGlobalObject* globalObject, JSObject* object, JSObject* properties)
597 {
598     VM& vm = globalObject->vm();
599     auto scope = DECLARE_THROW_SCOPE(vm);
600
601     PropertyNameArray propertyNames(vm, PropertyNameMode::StringsAndSymbols, PrivateSymbolMode::Exclude);
602     asObject(properties)->methodTable(vm)->getOwnPropertyNames(asObject(properties), globalObject, propertyNames, EnumerationMode(DontEnumPropertiesMode::Exclude));
603     RETURN_IF_EXCEPTION(scope, { });
604     size_t numProperties = propertyNames.size();
605     Vector<PropertyDescriptor> descriptors;
606     MarkedArgumentBuffer markBuffer;
607 #define RETURN_IF_EXCEPTION_CLEARING_OVERFLOW(value) do { \
608     if (scope.exception()) { \
609         markBuffer.overflowCheckNotNeeded(); \
610         return value; \
611     } \
612 } while (false)
613     for (size_t i = 0; i < numProperties; i++) {
614         JSValue prop = properties->get(globalObject, propertyNames[i]);
615         RETURN_IF_EXCEPTION_CLEARING_OVERFLOW({ });
616         PropertyDescriptor descriptor;
617         toPropertyDescriptor(globalObject, prop, descriptor);
618         RETURN_IF_EXCEPTION_CLEARING_OVERFLOW({ });
619         descriptors.append(descriptor);
620         // Ensure we mark all the values that we're accumulating
621         if (descriptor.isDataDescriptor() && descriptor.value())
622             markBuffer.append(descriptor.value());
623         if (descriptor.isAccessorDescriptor()) {
624             if (descriptor.getter())
625                 markBuffer.append(descriptor.getter());
626             if (descriptor.setter())
627                 markBuffer.append(descriptor.setter());
628         }
629     }
630     RELEASE_ASSERT(!markBuffer.hasOverflowed());
631 #undef RETURN_IF_EXCEPTION_CLEARING_OVERFLOW
632     for (size_t i = 0; i < numProperties; i++) {
633         auto& propertyName = propertyNames[i];
634         ASSERT(!propertyName.isPrivateName());
635
636         object->methodTable(vm)->defineOwnProperty(object, globalObject, propertyName, descriptors[i], true);
637         RETURN_IF_EXCEPTION(scope, { });
638     }
639     return object;
640 }
641
642 EncodedJSValue JSC_HOST_CALL objectConstructorDefineProperties(JSGlobalObject* globalObject, CallFrame* callFrame)
643 {
644     VM& vm = globalObject->vm();
645     auto scope = DECLARE_THROW_SCOPE(vm);
646
647     if (!callFrame->argument(0).isObject())
648         return throwVMTypeError(globalObject, scope, "Properties can only be defined on Objects."_s);
649     JSObject* targetObj = asObject(callFrame->argument(0));
650     JSObject* props = callFrame->argument(1).toObject(globalObject);
651     EXCEPTION_ASSERT(!!scope.exception() == !props);
652     if (UNLIKELY(!props))
653         return encodedJSValue();
654     RELEASE_AND_RETURN(scope, JSValue::encode(defineProperties(globalObject, targetObj, props)));
655 }
656
657 EncodedJSValue JSC_HOST_CALL objectConstructorCreate(JSGlobalObject* globalObject, CallFrame* callFrame)
658 {
659     VM& vm = globalObject->vm();
660     auto scope = DECLARE_THROW_SCOPE(vm);
661
662     JSValue proto = callFrame->argument(0);
663     if (!proto.isObject() && !proto.isNull())
664         return throwVMTypeError(globalObject, scope, "Object prototype may only be an Object or null."_s);
665     JSObject* newObject = proto.isObject()
666         ? constructEmptyObject(globalObject, asObject(proto))
667         : constructEmptyObject(vm, globalObject->nullPrototypeObjectStructure());
668     if (callFrame->argument(1).isUndefined())
669         return JSValue::encode(newObject);
670     JSObject* properties = callFrame->uncheckedArgument(1).toObject(globalObject);
671     RETURN_IF_EXCEPTION(scope, { });
672
673     RELEASE_AND_RETURN(scope, JSValue::encode(defineProperties(globalObject, newObject, properties)));
674 }
675
676 enum class IntegrityLevel {
677     Sealed,
678     Frozen
679 };
680
681 template<IntegrityLevel level>
682 bool setIntegrityLevel(JSGlobalObject* globalObject, VM& vm, JSObject* object)
683 {
684     // See https://tc39.github.io/ecma262/#sec-setintegritylevel.
685     auto scope = DECLARE_THROW_SCOPE(vm);
686
687     bool success = object->methodTable(vm)->preventExtensions(object, globalObject);
688     RETURN_IF_EXCEPTION(scope, false);
689     if (UNLIKELY(!success))
690         return false;
691
692     PropertyNameArray properties(vm, PropertyNameMode::StringsAndSymbols, PrivateSymbolMode::Exclude);
693     object->methodTable(vm)->getOwnPropertyNames(object, globalObject, properties, EnumerationMode(DontEnumPropertiesMode::Include));
694     RETURN_IF_EXCEPTION(scope, false);
695
696     PropertyNameArray::const_iterator end = properties.end();
697     for (PropertyNameArray::const_iterator iter = properties.begin(); iter != end; ++iter) {
698         auto& propertyName = *iter;
699         ASSERT(!propertyName.isPrivateName());
700
701         PropertyDescriptor desc;
702         if (level == IntegrityLevel::Sealed)
703             desc.setConfigurable(false);
704         else {
705             bool hasPropertyDescriptor = object->getOwnPropertyDescriptor(globalObject, propertyName, desc);
706             RETURN_IF_EXCEPTION(scope, false);
707             if (!hasPropertyDescriptor)
708                 continue;
709
710             if (desc.isDataDescriptor())
711                 desc.setWritable(false);
712
713             desc.setConfigurable(false);
714         }
715
716         object->methodTable(vm)->defineOwnProperty(object, globalObject, propertyName, desc, true);
717         RETURN_IF_EXCEPTION(scope, false);
718     }
719     return true;
720 }
721
722 template<IntegrityLevel level>
723 bool testIntegrityLevel(JSGlobalObject* globalObject, VM& vm, JSObject* object)
724 {
725     auto scope = DECLARE_THROW_SCOPE(vm);
726
727     // 1. Assert: Type(O) is Object.
728     // 2. Assert: level is either "sealed" or "frozen".
729
730     // 3. Let status be ?IsExtensible(O).
731     bool status = object->isExtensible(globalObject);
732     RETURN_IF_EXCEPTION(scope, { });
733
734     // 4. If status is true, return false.
735     if (status)
736         return false;
737
738     // 6. Let keys be ? O.[[OwnPropertyKeys]]().
739     PropertyNameArray keys(vm, PropertyNameMode::StringsAndSymbols, PrivateSymbolMode::Exclude);
740     object->methodTable(vm)->getOwnPropertyNames(object, globalObject, keys, EnumerationMode(DontEnumPropertiesMode::Include));
741     RETURN_IF_EXCEPTION(scope, { });
742
743     // 7. For each element k of keys, do
744     PropertyNameArray::const_iterator end = keys.end();
745     for (PropertyNameArray::const_iterator iter = keys.begin(); iter != end; ++iter) {
746         auto& propertyName = *iter;
747         ASSERT(!propertyName.isPrivateName());
748
749         // a. Let currentDesc be ? O.[[GetOwnProperty]](k)
750         PropertyDescriptor desc;
751         bool didGetDescriptor = object->getOwnPropertyDescriptor(globalObject, propertyName, desc);
752         RETURN_IF_EXCEPTION(scope, { });
753
754         // b. If currentDesc is not undefined, then
755         if (!didGetDescriptor)
756             continue;
757
758         // i. If currentDesc.[[Configurable]] is true, return false.
759         if (desc.configurable())
760             return false;
761
762         // ii. If level is "frozen" and IsDataDescriptor(currentDesc) is true, then
763         // 1. If currentDesc.[[Writable]] is true, return false.
764         if (level == IntegrityLevel::Frozen && desc.isDataDescriptor() && desc.writable())
765             return false;
766     }
767
768     return true;
769 }
770
771 JSObject* objectConstructorSeal(JSGlobalObject* globalObject, JSObject* object)
772 {
773     VM& vm = globalObject->vm();
774     auto scope = DECLARE_THROW_SCOPE(vm);
775
776     if (jsDynamicCast<JSFinalObject*>(vm, object) && !hasIndexedProperties(object->indexingType())) {
777         object->seal(vm);
778         return object;
779     }
780
781     bool success = setIntegrityLevel<IntegrityLevel::Sealed>(globalObject, vm, object);
782     RETURN_IF_EXCEPTION(scope, nullptr);
783     if (UNLIKELY(!success)) {
784         throwTypeError(globalObject, scope, "Unable to prevent extension in Object.seal"_s);
785         return nullptr;
786     }
787
788     return object;
789 }
790
791 EncodedJSValue JSC_HOST_CALL objectConstructorSeal(JSGlobalObject* globalObject, CallFrame* callFrame)
792 {
793     VM& vm = globalObject->vm();
794     auto scope = DECLARE_THROW_SCOPE(vm);
795
796     // 1. If Type(O) is not Object, return O.
797     JSValue obj = callFrame->argument(0);
798     if (!obj.isObject())
799         return JSValue::encode(obj);
800
801     RELEASE_AND_RETURN(scope, JSValue::encode(objectConstructorSeal(globalObject, asObject(obj))));
802 }
803
804 JSObject* objectConstructorFreeze(JSGlobalObject* globalObject, JSObject* object)
805 {
806     VM& vm = globalObject->vm();
807     auto scope = DECLARE_THROW_SCOPE(vm);
808
809     if (jsDynamicCast<JSFinalObject*>(vm, object) && !hasIndexedProperties(object->indexingType())) {
810         object->freeze(vm);
811         return object;
812     }
813
814     bool success = setIntegrityLevel<IntegrityLevel::Frozen>(globalObject, vm, object);
815     RETURN_IF_EXCEPTION(scope, nullptr);
816     if (UNLIKELY(!success)) {
817         throwTypeError(globalObject, scope, "Unable to prevent extension in Object.freeze"_s);
818         return nullptr;
819     }
820     return object;
821 }
822
823 EncodedJSValue JSC_HOST_CALL objectConstructorFreeze(JSGlobalObject* globalObject, CallFrame* callFrame)
824 {
825     VM& vm = globalObject->vm();
826     auto scope = DECLARE_THROW_SCOPE(vm);
827     // 1. If Type(O) is not Object, return O.
828     JSValue obj = callFrame->argument(0);
829     if (!obj.isObject())
830         return JSValue::encode(obj);
831     JSObject* result = objectConstructorFreeze(globalObject, asObject(obj));
832     RETURN_IF_EXCEPTION(scope, encodedJSValue());
833     return JSValue::encode(result);
834 }
835
836 EncodedJSValue JSC_HOST_CALL objectConstructorPreventExtensions(JSGlobalObject* globalObject, CallFrame* callFrame)
837 {
838     VM& vm = globalObject->vm();
839     auto scope = DECLARE_THROW_SCOPE(vm);
840
841     JSValue argument = callFrame->argument(0);
842     if (!argument.isObject())
843         return JSValue::encode(argument);
844     JSObject* object = asObject(argument);
845     bool status = object->methodTable(vm)->preventExtensions(object, globalObject);
846     RETURN_IF_EXCEPTION(scope, { });
847     if (UNLIKELY(!status))
848         return throwVMTypeError(globalObject, scope, "Unable to prevent extension in Object.preventExtensions"_s);
849     return JSValue::encode(object);
850 }
851
852 EncodedJSValue JSC_HOST_CALL objectConstructorIsSealed(JSGlobalObject* globalObject, CallFrame* callFrame)
853 {
854     VM& vm = globalObject->vm();
855
856     // 1. If Type(O) is not Object, return true.
857     JSValue obj = callFrame->argument(0);
858     if (!obj.isObject())
859         return JSValue::encode(jsBoolean(true));
860     JSObject* object = asObject(obj);
861
862     // Quick check for final objects.
863     if (jsDynamicCast<JSFinalObject*>(vm, object) && !hasIndexedProperties(object->indexingType()))
864         return JSValue::encode(jsBoolean(object->isSealed(vm)));
865
866     // 2. Return ? TestIntegrityLevel(O, "sealed").
867     return JSValue::encode(jsBoolean(testIntegrityLevel<IntegrityLevel::Sealed>(globalObject, vm, object)));
868 }
869
870 EncodedJSValue JSC_HOST_CALL objectConstructorIsFrozen(JSGlobalObject* globalObject, CallFrame* callFrame)
871 {
872     VM& vm = globalObject->vm();
873
874     // 1. If Type(O) is not Object, return true.
875     JSValue obj = callFrame->argument(0);
876     if (!obj.isObject())
877         return JSValue::encode(jsBoolean(true));
878     JSObject* object = asObject(obj);
879
880     // Quick check for final objects.
881     if (jsDynamicCast<JSFinalObject*>(vm, object) && !hasIndexedProperties(object->indexingType()))
882         return JSValue::encode(jsBoolean(object->isFrozen(vm)));
883
884     // 2. Return ? TestIntegrityLevel(O, "frozen").
885     return JSValue::encode(jsBoolean(testIntegrityLevel<IntegrityLevel::Frozen>(globalObject, vm, object)));
886 }
887
888 EncodedJSValue JSC_HOST_CALL objectConstructorIsExtensible(JSGlobalObject* globalObject, CallFrame* callFrame)
889 {
890     VM& vm = globalObject->vm();
891     auto scope = DECLARE_THROW_SCOPE(vm);
892     JSValue obj = callFrame->argument(0);
893     if (!obj.isObject())
894         return JSValue::encode(jsBoolean(false));
895     JSObject* object = asObject(obj);
896     bool isExtensible = object->isExtensible(globalObject);
897     RETURN_IF_EXCEPTION(scope, encodedJSValue());
898     return JSValue::encode(jsBoolean(isExtensible));
899 }
900
901 EncodedJSValue JSC_HOST_CALL objectConstructorIs(JSGlobalObject* globalObject, CallFrame* callFrame)
902 {
903     return JSValue::encode(jsBoolean(sameValue(globalObject, callFrame->argument(0), callFrame->argument(1))));
904 }
905
906 JSArray* ownPropertyKeys(JSGlobalObject* globalObject, JSObject* object, PropertyNameMode propertyNameMode, DontEnumPropertiesMode dontEnumPropertiesMode)
907 {
908     VM& vm = globalObject->vm();
909     auto scope = DECLARE_THROW_SCOPE(vm);
910
911     bool isObjectKeys = propertyNameMode == PropertyNameMode::Strings && dontEnumPropertiesMode == DontEnumPropertiesMode::Exclude;
912     // We attempt to look up own property keys cache in Object.keys case.
913     if (isObjectKeys) {
914         if (LIKELY(!globalObject->isHavingABadTime())) {
915             if (auto* immutableButterfly = object->structure(vm)->cachedOwnKeys()) {
916                 Structure* arrayStructure = globalObject->originalArrayStructureForIndexingType(immutableButterfly->indexingMode());
917                 return JSArray::createWithButterfly(vm, nullptr, arrayStructure, immutableButterfly->toButterfly());
918             }
919         }
920     }
921
922     PropertyNameArray properties(vm, propertyNameMode, PrivateSymbolMode::Exclude);
923     object->methodTable(vm)->getOwnPropertyNames(object, globalObject, properties, EnumerationMode(dontEnumPropertiesMode));
924     RETURN_IF_EXCEPTION(scope, nullptr);
925
926     if (propertyNameMode != PropertyNameMode::StringsAndSymbols) {
927         ASSERT(propertyNameMode == PropertyNameMode::Strings || propertyNameMode == PropertyNameMode::Symbols);
928         if (properties.size() < MIN_SPARSE_ARRAY_INDEX) {
929             if (LIKELY(!globalObject->isHavingABadTime())) {
930                 if (isObjectKeys) {
931                     Structure* structure = object->structure(vm);
932                     if (structure->canCacheOwnKeys()) {
933                         auto* cachedButterfly = structure->cachedOwnKeysIgnoringSentinel();
934                         if (cachedButterfly == StructureRareData::cachedOwnKeysSentinel()) {
935                             size_t numProperties = properties.size();
936                             auto* newButterfly = JSImmutableButterfly::create(vm, CopyOnWriteArrayWithContiguous, numProperties);
937                             for (size_t i = 0; i < numProperties; i++) {
938                                 const auto& identifier = properties[i];
939                                 ASSERT(!identifier.isSymbol());
940                                 newButterfly->setIndex(vm, i, jsOwnedString(vm, identifier.string()));
941                             }
942
943                             structure->setCachedOwnKeys(vm, newButterfly);
944                             Structure* arrayStructure = globalObject->originalArrayStructureForIndexingType(newButterfly->indexingMode());
945                             return JSArray::createWithButterfly(vm, nullptr, arrayStructure, newButterfly->toButterfly());
946                         }
947
948                         if (cachedButterfly == nullptr)
949                             structure->setCachedOwnKeys(vm, StructureRareData::cachedOwnKeysSentinel());
950                     }
951                 }
952
953                 size_t numProperties = properties.size();
954                 JSArray* keys = JSArray::create(vm, globalObject->originalArrayStructureForIndexingType(ArrayWithContiguous), numProperties);
955                 WriteBarrier<Unknown>* buffer = keys->butterfly()->contiguous().data();
956                 for (size_t i = 0; i < numProperties; i++) {
957                     const auto& identifier = properties[i];
958                     if (propertyNameMode == PropertyNameMode::Strings) {
959                         ASSERT(!identifier.isSymbol());
960                         buffer[i].set(vm, keys, jsOwnedString(vm, identifier.string()));
961                     } else {
962                         ASSERT(identifier.isSymbol());
963                         buffer[i].set(vm, keys, Symbol::create(vm, static_cast<SymbolImpl&>(*identifier.impl())));
964                     }
965                 }
966                 return keys;
967             }
968         }
969     }
970
971     JSArray* keys = constructEmptyArray(globalObject, nullptr);
972     RETURN_IF_EXCEPTION(scope, nullptr);
973
974     unsigned index = 0;
975     auto pushDirect = [&] (JSGlobalObject* globalObject, JSArray* array, JSValue value) {
976         array->putDirectIndex(globalObject, index++, value);
977     };
978
979     switch (propertyNameMode) {
980     case PropertyNameMode::Strings: {
981         size_t numProperties = properties.size();
982         for (size_t i = 0; i < numProperties; i++) {
983             const auto& identifier = properties[i];
984             ASSERT(!identifier.isSymbol());
985             pushDirect(globalObject, keys, jsOwnedString(vm, identifier.string()));
986             RETURN_IF_EXCEPTION(scope, nullptr);
987         }
988         break;
989     }
990
991     case PropertyNameMode::Symbols: {
992         size_t numProperties = properties.size();
993         for (size_t i = 0; i < numProperties; i++) {
994             const auto& identifier = properties[i];
995             ASSERT(identifier.isSymbol());
996             ASSERT(!identifier.isPrivateName());
997             pushDirect(globalObject, keys, Symbol::create(vm, static_cast<SymbolImpl&>(*identifier.impl())));
998             RETURN_IF_EXCEPTION(scope, nullptr);
999         }
1000         break;
1001     }
1002
1003     case PropertyNameMode::StringsAndSymbols: {
1004         Vector<Identifier, 16> propertySymbols;
1005         size_t numProperties = properties.size();
1006         for (size_t i = 0; i < numProperties; i++) {
1007             const auto& identifier = properties[i];
1008             if (identifier.isSymbol()) {
1009                 ASSERT(!identifier.isPrivateName());
1010                 propertySymbols.append(identifier);
1011                 continue;
1012             }
1013
1014             pushDirect(globalObject, keys, jsOwnedString(vm, identifier.string()));
1015             RETURN_IF_EXCEPTION(scope, nullptr);
1016         }
1017
1018         // To ensure the order defined in the spec (9.1.12), we append symbols at the last elements of keys.
1019         for (const auto& identifier : propertySymbols) {
1020             pushDirect(globalObject, keys, Symbol::create(vm, static_cast<SymbolImpl&>(*identifier.impl())));
1021             RETURN_IF_EXCEPTION(scope, nullptr);
1022         }
1023
1024         break;
1025     }
1026     }
1027
1028     return keys;
1029 }
1030
1031 } // namespace JSC