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