c4a4905272ab2ba86137be8ec38729c65c12e07e
[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, objectPrototype->classInfo(vm)->className);
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     for (size_t i = 0; i < numProperties; i++) {
610         JSValue prop = properties->get(exec, propertyNames[i]);
611         RETURN_IF_EXCEPTION(scope, { });
612         PropertyDescriptor descriptor;
613         bool success = toPropertyDescriptor(exec, prop, descriptor);
614         EXCEPTION_ASSERT(!scope.exception() || !success);
615         if (UNLIKELY(!success)) {
616             markBuffer.overflowCheckNotNeeded();
617             return jsNull();
618         }
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     for (size_t i = 0; i < numProperties; i++) {
632         auto& propertyName = propertyNames[i];
633         ASSERT(!propertyName.isPrivateName());
634
635         object->methodTable(vm)->defineOwnProperty(object, exec, propertyName, descriptors[i], true);
636         RETURN_IF_EXCEPTION(scope, { });
637     }
638     return object;
639 }
640
641 EncodedJSValue JSC_HOST_CALL objectConstructorDefineProperties(ExecState* exec)
642 {
643     VM& vm = exec->vm();
644     auto scope = DECLARE_THROW_SCOPE(vm);
645
646     if (!exec->argument(0).isObject())
647         return throwVMTypeError(exec, scope, "Properties can only be defined on Objects."_s);
648     JSObject* targetObj = asObject(exec->argument(0));
649     JSObject* props = exec->argument(1).toObject(exec);
650     EXCEPTION_ASSERT(!!scope.exception() == !props);
651     if (UNLIKELY(!props))
652         return encodedJSValue();
653     RELEASE_AND_RETURN(scope, JSValue::encode(defineProperties(exec, targetObj, props)));
654 }
655
656 EncodedJSValue JSC_HOST_CALL objectConstructorCreate(ExecState* exec)
657 {
658     VM& vm = exec->vm();
659     auto scope = DECLARE_THROW_SCOPE(vm);
660
661     JSValue proto = exec->argument(0);
662     if (!proto.isObject() && !proto.isNull())
663         return throwVMTypeError(exec, scope, "Object prototype may only be an Object or null."_s);
664     JSObject* newObject = proto.isObject()
665         ? constructEmptyObject(exec, asObject(proto))
666         : constructEmptyObject(exec, exec->lexicalGlobalObject()->nullPrototypeObjectStructure());
667     if (exec->argument(1).isUndefined())
668         return JSValue::encode(newObject);
669     if (!exec->argument(1).isObject())
670         return throwVMTypeError(exec, scope, "Property descriptor list must be an Object."_s);
671     RELEASE_AND_RETURN(scope, JSValue::encode(defineProperties(exec, newObject, asObject(exec->argument(1)))));
672 }
673
674 enum class IntegrityLevel {
675     Sealed,
676     Frozen
677 };
678
679 template<IntegrityLevel level>
680 bool setIntegrityLevel(ExecState* exec, VM& vm, JSObject* object)
681 {
682     // See https://tc39.github.io/ecma262/#sec-setintegritylevel.
683     auto scope = DECLARE_THROW_SCOPE(vm);
684
685     bool success = object->methodTable(vm)->preventExtensions(object, exec);
686     RETURN_IF_EXCEPTION(scope, false);
687     if (UNLIKELY(!success))
688         return false;
689
690     PropertyNameArray properties(&vm, PropertyNameMode::StringsAndSymbols, PrivateSymbolMode::Exclude);
691     object->methodTable(vm)->getOwnPropertyNames(object, exec, properties, EnumerationMode(DontEnumPropertiesMode::Include));
692     RETURN_IF_EXCEPTION(scope, false);
693
694     PropertyNameArray::const_iterator end = properties.end();
695     for (PropertyNameArray::const_iterator iter = properties.begin(); iter != end; ++iter) {
696         auto& propertyName = *iter;
697         ASSERT(!propertyName.isPrivateName());
698
699         PropertyDescriptor desc;
700         if (level == IntegrityLevel::Sealed)
701             desc.setConfigurable(false);
702         else {
703             bool hasPropertyDescriptor = object->getOwnPropertyDescriptor(exec, propertyName, desc);
704             RETURN_IF_EXCEPTION(scope, false);
705             if (!hasPropertyDescriptor)
706                 continue;
707
708             if (desc.isDataDescriptor())
709                 desc.setWritable(false);
710
711             desc.setConfigurable(false);
712         }
713
714         object->methodTable(vm)->defineOwnProperty(object, exec, propertyName, desc, true);
715         RETURN_IF_EXCEPTION(scope, false);
716     }
717     return true;
718 }
719
720 template<IntegrityLevel level>
721 bool testIntegrityLevel(ExecState* exec, VM& vm, JSObject* object)
722 {
723     auto scope = DECLARE_THROW_SCOPE(vm);
724
725     // 1. Assert: Type(O) is Object.
726     // 2. Assert: level is either "sealed" or "frozen".
727
728     // 3. Let status be ?IsExtensible(O).
729     bool status = object->isExtensible(exec);
730     RETURN_IF_EXCEPTION(scope, { });
731
732     // 4. If status is true, return false.
733     if (status)
734         return false;
735
736     // 6. Let keys be ? O.[[OwnPropertyKeys]]().
737     PropertyNameArray keys(&vm, PropertyNameMode::StringsAndSymbols, PrivateSymbolMode::Exclude);
738     object->methodTable(vm)->getOwnPropertyNames(object, exec, keys, EnumerationMode(DontEnumPropertiesMode::Include));
739     RETURN_IF_EXCEPTION(scope, { });
740
741     // 7. For each element k of keys, do
742     PropertyNameArray::const_iterator end = keys.end();
743     for (PropertyNameArray::const_iterator iter = keys.begin(); iter != end; ++iter) {
744         auto& propertyName = *iter;
745         ASSERT(!propertyName.isPrivateName());
746
747         // a. Let currentDesc be ? O.[[GetOwnProperty]](k)
748         PropertyDescriptor desc;
749         bool didGetDescriptor = object->getOwnPropertyDescriptor(exec, propertyName, desc);
750         RETURN_IF_EXCEPTION(scope, { });
751
752         // b. If currentDesc is not undefined, then
753         if (!didGetDescriptor)
754             continue;
755
756         // i. If currentDesc.[[Configurable]] is true, return false.
757         if (desc.configurable())
758             return false;
759
760         // ii. If level is "frozen" and IsDataDescriptor(currentDesc) is true, then
761         // 1. If currentDesc.[[Writable]] is true, return false.
762         if (level == IntegrityLevel::Frozen && desc.isDataDescriptor() && desc.writable())
763             return false;
764     }
765
766     return true;
767 }
768
769 JSObject* objectConstructorSeal(ExecState* exec, JSObject* object)
770 {
771     VM& vm = exec->vm();
772     auto scope = DECLARE_THROW_SCOPE(vm);
773
774     if (jsDynamicCast<JSFinalObject*>(vm, object) && !hasIndexedProperties(object->indexingType())) {
775         object->seal(vm);
776         return object;
777     }
778
779     bool success = setIntegrityLevel<IntegrityLevel::Sealed>(exec, vm, object);
780     RETURN_IF_EXCEPTION(scope, nullptr);
781     if (UNLIKELY(!success))
782         return throwTypeError(exec, scope, "Unable to prevent extension in Object.seal"_s);
783
784     return object;
785 }
786
787 EncodedJSValue JSC_HOST_CALL objectConstructorSeal(ExecState* exec)
788 {
789     VM& vm = exec->vm();
790     auto scope = DECLARE_THROW_SCOPE(vm);
791
792     // 1. If Type(O) is not Object, return O.
793     JSValue obj = exec->argument(0);
794     if (!obj.isObject())
795         return JSValue::encode(obj);
796
797     RELEASE_AND_RETURN(scope, JSValue::encode(objectConstructorSeal(exec, asObject(obj))));
798 }
799
800 JSObject* objectConstructorFreeze(ExecState* exec, JSObject* object)
801 {
802     VM& vm = exec->vm();
803     auto scope = DECLARE_THROW_SCOPE(vm);
804
805     if (jsDynamicCast<JSFinalObject*>(vm, object) && !hasIndexedProperties(object->indexingType())) {
806         object->freeze(vm);
807         return object;
808     }
809
810     bool success = setIntegrityLevel<IntegrityLevel::Frozen>(exec, vm, object);
811     RETURN_IF_EXCEPTION(scope, nullptr);
812     if (!success)
813         return throwTypeError(exec, scope, "Unable to prevent extension in Object.freeze"_s);
814     return object;
815 }
816
817 EncodedJSValue JSC_HOST_CALL objectConstructorFreeze(ExecState* exec)
818 {
819     VM& vm = exec->vm();
820     auto scope = DECLARE_THROW_SCOPE(vm);
821     // 1. If Type(O) is not Object, return O.
822     JSValue obj = exec->argument(0);
823     if (!obj.isObject())
824         return JSValue::encode(obj);
825     JSObject* result = objectConstructorFreeze(exec, asObject(obj));
826     RETURN_IF_EXCEPTION(scope, encodedJSValue());
827     return JSValue::encode(result);
828 }
829
830 EncodedJSValue JSC_HOST_CALL objectConstructorPreventExtensions(ExecState* exec)
831 {
832     VM& vm = exec->vm();
833     JSValue argument = exec->argument(0);
834     if (!argument.isObject())
835         return JSValue::encode(argument);
836     JSObject* object = asObject(argument);
837     object->methodTable(vm)->preventExtensions(object, exec);
838     return JSValue::encode(object);
839 }
840
841 EncodedJSValue JSC_HOST_CALL objectConstructorIsSealed(ExecState* exec)
842 {
843     VM& vm = exec->vm();
844
845     // 1. If Type(O) is not Object, return true.
846     JSValue obj = exec->argument(0);
847     if (!obj.isObject())
848         return JSValue::encode(jsBoolean(true));
849     JSObject* object = asObject(obj);
850
851     // Quick check for final objects.
852     if (jsDynamicCast<JSFinalObject*>(vm, object) && !hasIndexedProperties(object->indexingType()))
853         return JSValue::encode(jsBoolean(object->isSealed(vm)));
854
855     // 2. Return ? TestIntegrityLevel(O, "sealed").
856     return JSValue::encode(jsBoolean(testIntegrityLevel<IntegrityLevel::Sealed>(exec, vm, object)));
857 }
858
859 EncodedJSValue JSC_HOST_CALL objectConstructorIsFrozen(ExecState* exec)
860 {
861     VM& vm = exec->vm();
862
863     // 1. If Type(O) is not Object, return true.
864     JSValue obj = exec->argument(0);
865     if (!obj.isObject())
866         return JSValue::encode(jsBoolean(true));
867     JSObject* object = asObject(obj);
868
869     // Quick check for final objects.
870     if (jsDynamicCast<JSFinalObject*>(vm, object) && !hasIndexedProperties(object->indexingType()))
871         return JSValue::encode(jsBoolean(object->isFrozen(vm)));
872
873     // 2. Return ? TestIntegrityLevel(O, "frozen").
874     return JSValue::encode(jsBoolean(testIntegrityLevel<IntegrityLevel::Frozen>(exec, vm, object)));
875 }
876
877 EncodedJSValue JSC_HOST_CALL objectConstructorIsExtensible(ExecState* exec)
878 {
879     VM& vm = exec->vm();
880     auto scope = DECLARE_THROW_SCOPE(vm);
881     JSValue obj = exec->argument(0);
882     if (!obj.isObject())
883         return JSValue::encode(jsBoolean(false));
884     JSObject* object = asObject(obj);
885     bool isExtensible = object->isExtensible(exec);
886     RETURN_IF_EXCEPTION(scope, encodedJSValue());
887     return JSValue::encode(jsBoolean(isExtensible));
888 }
889
890 EncodedJSValue JSC_HOST_CALL objectConstructorIs(ExecState* exec)
891 {
892     return JSValue::encode(jsBoolean(sameValue(exec, exec->argument(0), exec->argument(1))));
893 }
894
895 JSArray* ownPropertyKeys(ExecState* exec, JSObject* object, PropertyNameMode propertyNameMode, DontEnumPropertiesMode dontEnumPropertiesMode)
896 {
897     VM& vm = exec->vm();
898     auto scope = DECLARE_THROW_SCOPE(vm);
899
900     auto* globalObject = exec->lexicalGlobalObject();
901     bool isObjectKeys = propertyNameMode == PropertyNameMode::Strings && dontEnumPropertiesMode == DontEnumPropertiesMode::Exclude;
902     // We attempt to look up own property keys cache in Object.keys case.
903     if (isObjectKeys) {
904         if (LIKELY(!globalObject->isHavingABadTime())) {
905             if (auto* immutableButterfly = object->structure(vm)->cachedOwnKeys()) {
906                 Structure* arrayStructure = globalObject->originalArrayStructureForIndexingType(immutableButterfly->indexingMode());
907                 return JSArray::createWithButterfly(vm, nullptr, arrayStructure, immutableButterfly->toButterfly());
908             }
909         }
910     }
911
912     PropertyNameArray properties(&vm, propertyNameMode, PrivateSymbolMode::Exclude);
913     object->methodTable(vm)->getOwnPropertyNames(object, exec, properties, EnumerationMode(dontEnumPropertiesMode));
914     RETURN_IF_EXCEPTION(scope, nullptr);
915
916     // https://tc39.github.io/ecma262/#sec-enumerableownproperties
917     // If {object} is a Proxy, an explicit and observable [[GetOwnProperty]] op is required to filter out non-enumerable properties.
918     // In other cases, filtering has already been performed.
919     const bool mustFilterProperty = dontEnumPropertiesMode == DontEnumPropertiesMode::Exclude && object->type() == ProxyObjectType;
920     auto filterPropertyIfNeeded = [exec, object, mustFilterProperty](const Identifier& identifier) {
921         if (!mustFilterProperty)
922             return true;
923         PropertyDescriptor descriptor;
924         PropertyName name(identifier);
925         return object->getOwnPropertyDescriptor(exec, name, descriptor) && descriptor.enumerable();
926     };
927
928     // If !mustFilterProperty and PropertyNameMode::Strings mode, we do not need to filter out any entries in PropertyNameArray.
929     // We can use fast allocation and initialization.
930     if (propertyNameMode != PropertyNameMode::StringsAndSymbols) {
931         ASSERT(propertyNameMode == PropertyNameMode::Strings || propertyNameMode == PropertyNameMode::Symbols);
932         if (!mustFilterProperty && properties.size() < MIN_SPARSE_ARRAY_INDEX) {
933             if (LIKELY(!globalObject->isHavingABadTime())) {
934                 if (isObjectKeys) {
935                     Structure* structure = object->structure(vm);
936                     if (structure->canCacheOwnKeys()) {
937                         auto* cachedButterfly = structure->cachedOwnKeysIgnoringSentinel();
938                         if (cachedButterfly == StructureRareData::cachedOwnKeysSentinel()) {
939                             size_t numProperties = properties.size();
940                             auto* newButterfly = JSImmutableButterfly::create(vm, CopyOnWriteArrayWithContiguous, numProperties);
941                             for (size_t i = 0; i < numProperties; i++) {
942                                 const auto& identifier = properties[i];
943                                 ASSERT(!identifier.isSymbol());
944                                 newButterfly->setIndex(vm, i, jsOwnedString(&vm, identifier.string()));
945                             }
946
947                             structure->setCachedOwnKeys(vm, newButterfly);
948                             Structure* arrayStructure = globalObject->originalArrayStructureForIndexingType(newButterfly->indexingMode());
949                             return JSArray::createWithButterfly(vm, nullptr, arrayStructure, newButterfly->toButterfly());
950                         }
951
952                         if (cachedButterfly == nullptr)
953                             structure->setCachedOwnKeys(vm, StructureRareData::cachedOwnKeysSentinel());
954                     }
955                 }
956
957                 size_t numProperties = properties.size();
958                 JSArray* keys = JSArray::create(vm, globalObject->originalArrayStructureForIndexingType(ArrayWithContiguous), numProperties);
959                 WriteBarrier<Unknown>* buffer = keys->butterfly()->contiguous().data();
960                 for (size_t i = 0; i < numProperties; i++) {
961                     const auto& identifier = properties[i];
962                     if (propertyNameMode == PropertyNameMode::Strings) {
963                         ASSERT(!identifier.isSymbol());
964                         buffer[i].set(vm, keys, jsOwnedString(&vm, identifier.string()));
965                     } else {
966                         ASSERT(identifier.isSymbol());
967                         buffer[i].set(vm, keys, Symbol::create(vm, static_cast<SymbolImpl&>(*identifier.impl())));
968                     }
969                 }
970                 return keys;
971             }
972         }
973     }
974
975     JSArray* keys = constructEmptyArray(exec, nullptr);
976     RETURN_IF_EXCEPTION(scope, nullptr);
977
978     unsigned index = 0;
979     auto pushDirect = [&] (ExecState* exec, JSArray* array, JSValue value) {
980         array->putDirectIndex(exec, index++, value);
981     };
982
983     switch (propertyNameMode) {
984     case PropertyNameMode::Strings: {
985         size_t numProperties = properties.size();
986         for (size_t i = 0; i < numProperties; i++) {
987             const auto& identifier = properties[i];
988             ASSERT(!identifier.isSymbol());
989             bool hasProperty = filterPropertyIfNeeded(identifier);
990             EXCEPTION_ASSERT(!scope.exception() || !hasProperty);
991             if (hasProperty)
992                 pushDirect(exec, keys, jsOwnedString(exec, identifier.string()));
993             RETURN_IF_EXCEPTION(scope, nullptr);
994         }
995         break;
996     }
997
998     case PropertyNameMode::Symbols: {
999         size_t numProperties = properties.size();
1000         for (size_t i = 0; i < numProperties; i++) {
1001             const auto& identifier = properties[i];
1002             ASSERT(identifier.isSymbol());
1003             ASSERT(!identifier.isPrivateName());
1004             bool hasProperty = filterPropertyIfNeeded(identifier);
1005             EXCEPTION_ASSERT(!scope.exception() || !hasProperty);
1006             if (hasProperty)
1007                 pushDirect(exec, keys, Symbol::create(vm, static_cast<SymbolImpl&>(*identifier.impl())));
1008             RETURN_IF_EXCEPTION(scope, nullptr);
1009         }
1010         break;
1011     }
1012
1013     case PropertyNameMode::StringsAndSymbols: {
1014         Vector<Identifier, 16> propertySymbols;
1015         size_t numProperties = properties.size();
1016         for (size_t i = 0; i < numProperties; i++) {
1017             const auto& identifier = properties[i];
1018             if (identifier.isSymbol()) {
1019                 ASSERT(!identifier.isPrivateName());
1020                 propertySymbols.append(identifier);
1021                 continue;
1022             }
1023
1024             bool hasProperty = filterPropertyIfNeeded(identifier);
1025             EXCEPTION_ASSERT(!scope.exception() || !hasProperty);
1026             if (hasProperty)
1027                 pushDirect(exec, keys, jsOwnedString(exec, identifier.string()));
1028             RETURN_IF_EXCEPTION(scope, nullptr);
1029         }
1030
1031         // To ensure the order defined in the spec (9.1.12), we append symbols at the last elements of keys.
1032         for (const auto& identifier : propertySymbols) {
1033             bool hasProperty = filterPropertyIfNeeded(identifier);
1034             EXCEPTION_ASSERT(!scope.exception() || !hasProperty);
1035             if (hasProperty)
1036                 pushDirect(exec, keys, Symbol::create(vm, static_cast<SymbolImpl&>(*identifier.impl())));
1037             RETURN_IF_EXCEPTION(scope, nullptr);
1038         }
1039
1040         break;
1041     }
1042     }
1043
1044     return keys;
1045 }
1046
1047 } // namespace JSC