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