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