[JSC] Add SameValue DFG node
[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     ObjectConstructor* objectConstructor = jsCast<ObjectConstructor*>(exec->jsCallee());
119     JSGlobalObject* globalObject = objectConstructor->globalObject();
120     VM& vm = 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     unsigned argsCount = exec->argumentCount();
304     for (unsigned i = 1; i < argsCount; ++i) {
305         JSValue sourceValue = exec->uncheckedArgument(i);
306         if (sourceValue.isUndefinedOrNull())
307             continue;
308         JSObject* source = sourceValue.toObject(exec);
309         RETURN_IF_EXCEPTION(scope, { });
310
311         PropertyNameArray properties(&vm, PropertyNameMode::StringsAndSymbols, PrivateSymbolMode::Exclude);
312         source->methodTable(vm)->getOwnPropertyNames(source, exec, properties, EnumerationMode(DontEnumPropertiesMode::Include));
313         RETURN_IF_EXCEPTION(scope, { });
314
315         auto assign = [&] (PropertyName propertyName) {
316             PropertySlot slot(source, PropertySlot::InternalMethodType::GetOwnProperty);
317             bool hasProperty = source->methodTable(vm)->getOwnPropertySlot(source, exec, propertyName, slot);
318             RETURN_IF_EXCEPTION(scope, void());
319             if (!hasProperty)
320                 return;
321             if (slot.attributes() & PropertyAttribute::DontEnum)
322                 return;
323
324             JSValue value;
325             if (LIKELY(!slot.isTaintedByOpaqueObject()))
326                 value = slot.getValue(exec, propertyName);
327             else
328                 value = source->get(exec, propertyName);
329             RETURN_IF_EXCEPTION(scope, void());
330
331             PutPropertySlot putPropertySlot(target, true);
332             target->putInline(exec, propertyName, value, putPropertySlot);
333         };
334
335         // First loop is for strings. Second loop is for symbols to keep standardized order requirement in the spec.
336         // https://tc39.github.io/ecma262/#sec-ordinaryownpropertykeys
337         bool foundSymbol = false;
338         unsigned numProperties = properties.size();
339         for (unsigned j = 0; j < numProperties; j++) {
340             const auto& propertyName = properties[j];
341             if (propertyName.isSymbol()) {
342                 foundSymbol = true;
343                 continue;
344             }
345
346             assign(propertyName);
347             RETURN_IF_EXCEPTION(scope, { });
348         }
349
350         if (foundSymbol) {
351             for (unsigned j = 0; j < numProperties; j++) {
352                 const auto& propertyName = properties[j];
353                 if (propertyName.isSymbol()) {
354                     ASSERT(!propertyName.isPrivateName());
355                     assign(propertyName);
356                     RETURN_IF_EXCEPTION(scope, { });
357                 }
358             }
359         }
360     }
361     return JSValue::encode(target);
362 }
363
364 EncodedJSValue JSC_HOST_CALL objectConstructorValues(ExecState* exec)
365 {
366     VM& vm = exec->vm();
367     auto scope = DECLARE_THROW_SCOPE(vm);
368
369     JSValue targetValue = exec->argument(0);
370     if (targetValue.isUndefinedOrNull())
371         return throwVMTypeError(exec, scope, ASCIILiteral("Object.values requires that input parameter not be null or undefined"));
372     JSObject* target = targetValue.toObject(exec);
373     RETURN_IF_EXCEPTION(scope, { });
374
375     JSArray* values = constructEmptyArray(exec, nullptr);
376     RETURN_IF_EXCEPTION(scope, { });
377
378     PropertyNameArray properties(&vm, PropertyNameMode::Strings, PrivateSymbolMode::Exclude);
379     target->methodTable(vm)->getOwnPropertyNames(target, exec, properties, EnumerationMode(DontEnumPropertiesMode::Include));
380     RETURN_IF_EXCEPTION(scope, { });
381
382     unsigned index = 0;
383     auto addValue = [&] (PropertyName propertyName) {
384         PropertySlot slot(target, PropertySlot::InternalMethodType::GetOwnProperty);
385         bool hasProperty = target->methodTable(vm)->getOwnPropertySlot(target, exec, propertyName, slot);
386         RETURN_IF_EXCEPTION(scope, void());
387         if (!hasProperty)
388             return;
389         if (slot.attributes() & PropertyAttribute::DontEnum)
390             return;
391
392         JSValue value;
393         if (LIKELY(!slot.isTaintedByOpaqueObject()))
394             value = slot.getValue(exec, propertyName);
395         else
396             value = target->get(exec, propertyName);
397         RETURN_IF_EXCEPTION(scope, void());
398
399         values->putDirectIndex(exec, index++, value);
400     };
401
402     for (unsigned i = 0, numProperties = properties.size(); i < numProperties; i++) {
403         const auto& propertyName = properties[i];
404         if (propertyName.isSymbol())
405             continue;
406
407         addValue(propertyName);
408         RETURN_IF_EXCEPTION(scope, { });
409     }
410
411     return JSValue::encode(values);
412 }
413
414
415 // ES6 6.2.4.5 ToPropertyDescriptor
416 // https://tc39.github.io/ecma262/#sec-topropertydescriptor
417 bool toPropertyDescriptor(ExecState* exec, JSValue in, PropertyDescriptor& desc)
418 {
419     VM& vm = exec->vm();
420     auto scope = DECLARE_THROW_SCOPE(vm);
421
422     if (!in.isObject()) {
423         throwTypeError(exec, scope, ASCIILiteral("Property description must be an object."));
424         return false;
425     }
426     JSObject* description = asObject(in);
427
428     bool hasProperty = description->hasProperty(exec, vm.propertyNames->enumerable);
429     EXCEPTION_ASSERT(!scope.exception() || !hasProperty);
430     if (hasProperty) {
431         JSValue value = description->get(exec, vm.propertyNames->enumerable);
432         RETURN_IF_EXCEPTION(scope, false);
433         desc.setEnumerable(value.toBoolean(exec));
434     } else
435         RETURN_IF_EXCEPTION(scope, false);
436
437     hasProperty = description->hasProperty(exec, vm.propertyNames->configurable);
438     EXCEPTION_ASSERT(!scope.exception() || !hasProperty);
439     if (hasProperty) {
440         JSValue value = description->get(exec, vm.propertyNames->configurable);
441         RETURN_IF_EXCEPTION(scope, false);
442         desc.setConfigurable(value.toBoolean(exec));
443     } else
444         RETURN_IF_EXCEPTION(scope, false);
445
446     JSValue value;
447     hasProperty = description->hasProperty(exec, vm.propertyNames->value);
448     EXCEPTION_ASSERT(!scope.exception() || !hasProperty);
449     if (hasProperty) {
450         JSValue value = description->get(exec, vm.propertyNames->value);
451         RETURN_IF_EXCEPTION(scope, false);
452         desc.setValue(value);
453     } else
454         RETURN_IF_EXCEPTION(scope, false);
455
456     hasProperty = description->hasProperty(exec, vm.propertyNames->writable);
457     EXCEPTION_ASSERT(!scope.exception() || !hasProperty);
458     if (hasProperty) {
459         JSValue value = description->get(exec, vm.propertyNames->writable);
460         RETURN_IF_EXCEPTION(scope, false);
461         desc.setWritable(value.toBoolean(exec));
462     } else
463         RETURN_IF_EXCEPTION(scope, false);
464
465     hasProperty = description->hasProperty(exec, vm.propertyNames->get);
466     EXCEPTION_ASSERT(!scope.exception() || !hasProperty);
467     if (hasProperty) {
468         JSValue get = description->get(exec, vm.propertyNames->get);
469         RETURN_IF_EXCEPTION(scope, false);
470         if (!get.isUndefined()) {
471             CallData callData;
472             if (getCallData(get, callData) == CallType::None) {
473                 throwTypeError(exec, scope, ASCIILiteral("Getter must be a function."));
474                 return false;
475             }
476         }
477         desc.setGetter(get);
478     } else
479         RETURN_IF_EXCEPTION(scope, false);
480
481     hasProperty = description->hasProperty(exec, vm.propertyNames->set);
482     EXCEPTION_ASSERT(!scope.exception() || !hasProperty);
483     if (hasProperty) {
484         JSValue set = description->get(exec, vm.propertyNames->set);
485         RETURN_IF_EXCEPTION(scope, false);
486         if (!set.isUndefined()) {
487             CallData callData;
488             if (getCallData(set, callData) == CallType::None) {
489                 throwTypeError(exec, scope, ASCIILiteral("Setter must be a function."));
490                 return false;
491             }
492         }
493         desc.setSetter(set);
494     } else
495         RETURN_IF_EXCEPTION(scope, false);
496
497     if (!desc.isAccessorDescriptor())
498         return true;
499
500     if (desc.value()) {
501         throwTypeError(exec, scope, ASCIILiteral("Invalid property.  'value' present on property with getter or setter."));
502         return false;
503     }
504
505     if (desc.writablePresent()) {
506         throwTypeError(exec, scope, ASCIILiteral("Invalid property.  'writable' present on property with getter or setter."));
507         return false;
508     }
509     return true;
510 }
511
512 EncodedJSValue JSC_HOST_CALL objectConstructorDefineProperty(ExecState* exec)
513 {
514     VM& vm = exec->vm();
515     auto scope = DECLARE_THROW_SCOPE(vm);
516
517     if (!exec->argument(0).isObject())
518         return throwVMTypeError(exec, scope, ASCIILiteral("Properties can only be defined on Objects."));
519     JSObject* obj = asObject(exec->argument(0));
520     auto propertyName = exec->argument(1).toPropertyKey(exec);
521     RETURN_IF_EXCEPTION(scope, encodedJSValue());
522     PropertyDescriptor descriptor;
523     auto success = toPropertyDescriptor(exec, exec->argument(2), descriptor);
524     EXCEPTION_ASSERT(!scope.exception() == success);
525     if (!success)
526         return JSValue::encode(jsNull());
527     ASSERT((descriptor.attributes() & PropertyAttribute::Accessor) || (!descriptor.isAccessorDescriptor()));
528     scope.assertNoException();
529     obj->methodTable(vm)->defineOwnProperty(obj, exec, propertyName, descriptor, true);
530     scope.release();
531     return JSValue::encode(obj);
532 }
533
534 static JSValue defineProperties(ExecState* exec, JSObject* object, JSObject* properties)
535 {
536     VM& vm = exec->vm();
537     auto scope = DECLARE_THROW_SCOPE(vm);
538
539     PropertyNameArray propertyNames(&vm, PropertyNameMode::StringsAndSymbols, PrivateSymbolMode::Exclude);
540     asObject(properties)->methodTable(vm)->getOwnPropertyNames(asObject(properties), exec, propertyNames, EnumerationMode(DontEnumPropertiesMode::Exclude));
541     RETURN_IF_EXCEPTION(scope, { });
542     size_t numProperties = propertyNames.size();
543     Vector<PropertyDescriptor> descriptors;
544     MarkedArgumentBuffer markBuffer;
545     for (size_t i = 0; i < numProperties; i++) {
546         JSValue prop = properties->get(exec, propertyNames[i]);
547         RETURN_IF_EXCEPTION(scope, { });
548         PropertyDescriptor descriptor;
549         bool success = toPropertyDescriptor(exec, prop, descriptor);
550         EXCEPTION_ASSERT(!scope.exception() || !success);
551         if (UNLIKELY(!success)) {
552             markBuffer.overflowCheckNotNeeded();
553             return jsNull();
554         }
555         descriptors.append(descriptor);
556         // Ensure we mark all the values that we're accumulating
557         if (descriptor.isDataDescriptor() && descriptor.value())
558             markBuffer.append(descriptor.value());
559         if (descriptor.isAccessorDescriptor()) {
560             if (descriptor.getter())
561                 markBuffer.append(descriptor.getter());
562             if (descriptor.setter())
563                 markBuffer.append(descriptor.setter());
564         }
565     }
566     RELEASE_ASSERT(!markBuffer.hasOverflowed());
567     for (size_t i = 0; i < numProperties; i++) {
568         auto& propertyName = propertyNames[i];
569         ASSERT(!propertyName.isPrivateName());
570
571         object->methodTable(vm)->defineOwnProperty(object, exec, propertyName, descriptors[i], true);
572         RETURN_IF_EXCEPTION(scope, { });
573     }
574     return object;
575 }
576
577 EncodedJSValue JSC_HOST_CALL objectConstructorDefineProperties(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, ASCIILiteral("Properties can only be defined on Objects."));
584     JSObject* targetObj = asObject(exec->argument(0));
585     JSObject* props = exec->argument(1).toObject(exec);
586     EXCEPTION_ASSERT(!!scope.exception() == !props);
587     if (UNLIKELY(!props))
588         return encodedJSValue();
589     scope.release();
590     return JSValue::encode(defineProperties(exec, targetObj, props));
591 }
592
593 EncodedJSValue JSC_HOST_CALL objectConstructorCreate(ExecState* exec)
594 {
595     VM& vm = exec->vm();
596     auto scope = DECLARE_THROW_SCOPE(vm);
597
598     JSValue proto = exec->argument(0);
599     if (!proto.isObject() && !proto.isNull())
600         return throwVMTypeError(exec, scope, ASCIILiteral("Object prototype may only be an Object or null."));
601     JSObject* newObject = proto.isObject()
602         ? constructEmptyObject(exec, asObject(proto))
603         : constructEmptyObject(exec, exec->lexicalGlobalObject()->nullPrototypeObjectStructure());
604     if (exec->argument(1).isUndefined())
605         return JSValue::encode(newObject);
606     if (!exec->argument(1).isObject())
607         return throwVMTypeError(exec, scope, ASCIILiteral("Property descriptor list must be an Object."));
608     scope.release();
609     return JSValue::encode(defineProperties(exec, newObject, asObject(exec->argument(1))));
610 }
611
612 enum class IntegrityLevel {
613     Sealed,
614     Frozen
615 };
616
617 template<IntegrityLevel level>
618 bool setIntegrityLevel(ExecState* exec, VM& vm, JSObject* object)
619 {
620     // See https://tc39.github.io/ecma262/#sec-setintegritylevel.
621     auto scope = DECLARE_THROW_SCOPE(vm);
622
623     bool success = object->methodTable(vm)->preventExtensions(object, exec);
624     RETURN_IF_EXCEPTION(scope, false);
625     if (UNLIKELY(!success))
626         return false;
627
628     PropertyNameArray properties(&vm, PropertyNameMode::StringsAndSymbols, PrivateSymbolMode::Exclude);
629     object->methodTable(vm)->getOwnPropertyNames(object, exec, properties, EnumerationMode(DontEnumPropertiesMode::Include));
630     RETURN_IF_EXCEPTION(scope, false);
631
632     PropertyNameArray::const_iterator end = properties.end();
633     for (PropertyNameArray::const_iterator iter = properties.begin(); iter != end; ++iter) {
634         auto& propertyName = *iter;
635         ASSERT(!propertyName.isPrivateName());
636
637         PropertyDescriptor desc;
638         if (level == IntegrityLevel::Sealed)
639             desc.setConfigurable(false);
640         else {
641             bool hasPropertyDescriptor = object->getOwnPropertyDescriptor(exec, propertyName, desc);
642             RETURN_IF_EXCEPTION(scope, false);
643             if (!hasPropertyDescriptor)
644                 continue;
645
646             if (desc.isDataDescriptor())
647                 desc.setWritable(false);
648
649             desc.setConfigurable(false);
650         }
651
652         object->methodTable(vm)->defineOwnProperty(object, exec, propertyName, desc, true);
653         RETURN_IF_EXCEPTION(scope, false);
654     }
655     return true;
656 }
657
658 template<IntegrityLevel level>
659 bool testIntegrityLevel(ExecState* exec, VM& vm, JSObject* object)
660 {
661     auto scope = DECLARE_THROW_SCOPE(vm);
662
663     // 1. Assert: Type(O) is Object.
664     // 2. Assert: level is either "sealed" or "frozen".
665
666     // 3. Let status be ?IsExtensible(O).
667     bool status = object->isExtensible(exec);
668     RETURN_IF_EXCEPTION(scope, { });
669
670     // 4. If status is true, return false.
671     if (status)
672         return false;
673
674     // 6. Let keys be ? O.[[OwnPropertyKeys]]().
675     PropertyNameArray keys(&vm, PropertyNameMode::StringsAndSymbols, PrivateSymbolMode::Exclude);
676     object->methodTable(vm)->getOwnPropertyNames(object, exec, keys, EnumerationMode(DontEnumPropertiesMode::Include));
677     RETURN_IF_EXCEPTION(scope, { });
678
679     // 7. For each element k of keys, do
680     PropertyNameArray::const_iterator end = keys.end();
681     for (PropertyNameArray::const_iterator iter = keys.begin(); iter != end; ++iter) {
682         auto& propertyName = *iter;
683         ASSERT(!propertyName.isPrivateName());
684
685         // a. Let currentDesc be ? O.[[GetOwnProperty]](k)
686         PropertyDescriptor desc;
687         bool didGetDescriptor = object->getOwnPropertyDescriptor(exec, propertyName, desc);
688         RETURN_IF_EXCEPTION(scope, { });
689
690         // b. If currentDesc is not undefined, then
691         if (!didGetDescriptor)
692             continue;
693
694         // i. If currentDesc.[[Configurable]] is true, return false.
695         if (desc.configurable())
696             return false;
697
698         // ii. If level is "frozen" and IsDataDescriptor(currentDesc) is true, then
699         // 1. If currentDesc.[[Writable]] is true, return false.
700         if (level == IntegrityLevel::Frozen && desc.isDataDescriptor() && desc.writable())
701             return false;
702     }
703
704     return true;
705 }
706
707 EncodedJSValue JSC_HOST_CALL objectConstructorSeal(ExecState* exec)
708 {
709     VM& vm = exec->vm();
710     auto scope = DECLARE_THROW_SCOPE(vm);
711
712     // 1. If Type(O) is not Object, return O.
713     JSValue obj = exec->argument(0);
714     if (!obj.isObject())
715         return JSValue::encode(obj);
716     JSObject* object = asObject(obj);
717
718     if (jsDynamicCast<JSFinalObject*>(vm, object) && !hasIndexedProperties(object->indexingType())) {
719         object->seal(vm);
720         return JSValue::encode(obj);
721     }
722
723     bool success = setIntegrityLevel<IntegrityLevel::Sealed>(exec, vm, object);
724     RETURN_IF_EXCEPTION(scope, encodedJSValue());
725     if (UNLIKELY(!success)) {
726         throwTypeError(exec, scope, ASCIILiteral("Unable to prevent extension in Object.seal"));
727         return encodedJSValue();
728     }
729
730     return JSValue::encode(obj);
731 }
732
733 JSObject* objectConstructorFreeze(ExecState* exec, JSObject* object)
734 {
735     VM& vm = exec->vm();
736     auto scope = DECLARE_THROW_SCOPE(vm);
737
738     if (jsDynamicCast<JSFinalObject*>(vm, object) && !hasIndexedProperties(object->indexingType())) {
739         object->freeze(vm);
740         return object;
741     }
742
743     bool success = setIntegrityLevel<IntegrityLevel::Frozen>(exec, vm, object);
744     RETURN_IF_EXCEPTION(scope, nullptr);
745     if (!success)
746         return throwTypeError(exec, scope, ASCIILiteral("Unable to prevent extension in Object.freeze"));
747     return object;
748 }
749
750 EncodedJSValue JSC_HOST_CALL objectConstructorFreeze(ExecState* exec)
751 {
752     VM& vm = exec->vm();
753     auto scope = DECLARE_THROW_SCOPE(vm);
754     // 1. If Type(O) is not Object, return O.
755     JSValue obj = exec->argument(0);
756     if (!obj.isObject())
757         return JSValue::encode(obj);
758     JSObject* result = objectConstructorFreeze(exec, asObject(obj));
759     RETURN_IF_EXCEPTION(scope, encodedJSValue());
760     return JSValue::encode(result);
761 }
762
763 EncodedJSValue JSC_HOST_CALL objectConstructorPreventExtensions(ExecState* exec)
764 {
765     VM& vm = exec->vm();
766     JSValue argument = exec->argument(0);
767     if (!argument.isObject())
768         return JSValue::encode(argument);
769     JSObject* object = asObject(argument);
770     object->methodTable(vm)->preventExtensions(object, exec);
771     return JSValue::encode(object);
772 }
773
774 EncodedJSValue JSC_HOST_CALL objectConstructorIsSealed(ExecState* exec)
775 {
776     VM& vm = exec->vm();
777
778     // 1. If Type(O) is not Object, return true.
779     JSValue obj = exec->argument(0);
780     if (!obj.isObject())
781         return JSValue::encode(jsBoolean(true));
782     JSObject* object = asObject(obj);
783
784     // Quick check for final objects.
785     if (jsDynamicCast<JSFinalObject*>(vm, object) && !hasIndexedProperties(object->indexingType()))
786         return JSValue::encode(jsBoolean(object->isSealed(vm)));
787
788     // 2. Return ? TestIntegrityLevel(O, "sealed").
789     return JSValue::encode(jsBoolean(testIntegrityLevel<IntegrityLevel::Sealed>(exec, vm, object)));
790 }
791
792 EncodedJSValue JSC_HOST_CALL objectConstructorIsFrozen(ExecState* exec)
793 {
794     VM& vm = exec->vm();
795
796     // 1. If Type(O) is not Object, return true.
797     JSValue obj = exec->argument(0);
798     if (!obj.isObject())
799         return JSValue::encode(jsBoolean(true));
800     JSObject* object = asObject(obj);
801
802     // Quick check for final objects.
803     if (jsDynamicCast<JSFinalObject*>(vm, object) && !hasIndexedProperties(object->indexingType()))
804         return JSValue::encode(jsBoolean(object->isFrozen(vm)));
805
806     // 2. Return ? TestIntegrityLevel(O, "frozen").
807     return JSValue::encode(jsBoolean(testIntegrityLevel<IntegrityLevel::Frozen>(exec, vm, object)));
808 }
809
810 EncodedJSValue JSC_HOST_CALL objectConstructorIsExtensible(ExecState* exec)
811 {
812     VM& vm = exec->vm();
813     auto scope = DECLARE_THROW_SCOPE(vm);
814     JSValue obj = exec->argument(0);
815     if (!obj.isObject())
816         return JSValue::encode(jsBoolean(false));
817     JSObject* object = asObject(obj);
818     bool isExtensible = object->isExtensible(exec);
819     RETURN_IF_EXCEPTION(scope, encodedJSValue());
820     return JSValue::encode(jsBoolean(isExtensible));
821 }
822
823 EncodedJSValue JSC_HOST_CALL objectConstructorIs(ExecState* exec)
824 {
825     return JSValue::encode(jsBoolean(sameValue(exec, exec->argument(0), exec->argument(1))));
826 }
827
828 // FIXME: Use the enumeration cache.
829 JSArray* ownPropertyKeys(ExecState* exec, JSObject* object, PropertyNameMode propertyNameMode, DontEnumPropertiesMode dontEnumPropertiesMode)
830 {
831     VM& vm = exec->vm();
832     auto scope = DECLARE_THROW_SCOPE(vm);
833     PropertyNameArray properties(&vm, propertyNameMode, PrivateSymbolMode::Exclude);
834     object->methodTable(vm)->getOwnPropertyNames(object, exec, properties, EnumerationMode(dontEnumPropertiesMode));
835     RETURN_IF_EXCEPTION(scope, nullptr);
836
837     // https://tc39.github.io/ecma262/#sec-enumerableownproperties
838     // If {object} is a Proxy, an explicit and observable [[GetOwnProperty]] op is required to filter out non-enumerable properties.
839     // In other cases, filtering has already been performed.
840     const bool mustFilterProperty = dontEnumPropertiesMode == DontEnumPropertiesMode::Exclude && object->type() == ProxyObjectType;
841     auto filterPropertyIfNeeded = [exec, object, mustFilterProperty](const Identifier& identifier) {
842         if (!mustFilterProperty)
843             return true;
844         PropertyDescriptor descriptor;
845         PropertyName name(identifier);
846         return object->getOwnPropertyDescriptor(exec, name, descriptor) && descriptor.enumerable();
847     };
848
849     // If !mustFilterProperty and PropertyNameMode::Strings mode, we do not need to filter out any entries in PropertyNameArray.
850     // We can use fast allocation and initialization.
851     if (propertyNameMode != PropertyNameMode::StringsAndSymbols) {
852         ASSERT(propertyNameMode == PropertyNameMode::Strings || propertyNameMode == PropertyNameMode::Symbols);
853         if (!mustFilterProperty && properties.size() < MIN_SPARSE_ARRAY_INDEX) {
854             auto* globalObject = exec->lexicalGlobalObject();
855             if (LIKELY(!globalObject->isHavingABadTime())) {
856                 size_t numProperties = properties.size();
857                 JSArray* keys = JSArray::create(vm, globalObject->originalArrayStructureForIndexingType(ArrayWithContiguous), numProperties);
858                 WriteBarrier<Unknown>* buffer = keys->butterfly()->contiguous().data();
859                 for (size_t i = 0; i < numProperties; i++) {
860                     const auto& identifier = properties[i];
861                     if (propertyNameMode == PropertyNameMode::Strings) {
862                         ASSERT(!identifier.isSymbol());
863                         buffer[i].set(vm, keys, jsOwnedString(&vm, identifier.string()));
864                     } else {
865                         ASSERT(identifier.isSymbol());
866                         buffer[i].set(vm, keys, Symbol::create(vm, static_cast<SymbolImpl&>(*identifier.impl())));
867                     }
868                 }
869                 return keys;
870             }
871         }
872     }
873
874     JSArray* keys = constructEmptyArray(exec, nullptr);
875     RETURN_IF_EXCEPTION(scope, nullptr);
876
877     unsigned index = 0;
878     auto pushDirect = [&] (ExecState* exec, JSArray* array, JSValue value) {
879         array->putDirectIndex(exec, index++, value);
880     };
881
882     switch (propertyNameMode) {
883     case PropertyNameMode::Strings: {
884         size_t numProperties = properties.size();
885         for (size_t i = 0; i < numProperties; i++) {
886             const auto& identifier = properties[i];
887             ASSERT(!identifier.isSymbol());
888             bool hasProperty = filterPropertyIfNeeded(identifier);
889             EXCEPTION_ASSERT(!scope.exception() || !hasProperty);
890             if (hasProperty)
891                 pushDirect(exec, keys, jsOwnedString(exec, identifier.string()));
892             RETURN_IF_EXCEPTION(scope, nullptr);
893         }
894         break;
895     }
896
897     case PropertyNameMode::Symbols: {
898         size_t numProperties = properties.size();
899         for (size_t i = 0; i < numProperties; i++) {
900             const auto& identifier = properties[i];
901             ASSERT(identifier.isSymbol());
902             ASSERT(!identifier.isPrivateName());
903             bool hasProperty = filterPropertyIfNeeded(identifier);
904             EXCEPTION_ASSERT(!scope.exception() || !hasProperty);
905             if (hasProperty)
906                 pushDirect(exec, keys, Symbol::create(vm, static_cast<SymbolImpl&>(*identifier.impl())));
907             RETURN_IF_EXCEPTION(scope, nullptr);
908         }
909         break;
910     }
911
912     case PropertyNameMode::StringsAndSymbols: {
913         Vector<Identifier, 16> propertySymbols;
914         size_t numProperties = properties.size();
915         for (size_t i = 0; i < numProperties; i++) {
916             const auto& identifier = properties[i];
917             if (identifier.isSymbol()) {
918                 ASSERT(!identifier.isPrivateName());
919                 propertySymbols.append(identifier);
920                 continue;
921             }
922
923             bool hasProperty = filterPropertyIfNeeded(identifier);
924             EXCEPTION_ASSERT(!scope.exception() || !hasProperty);
925             if (hasProperty)
926                 pushDirect(exec, keys, jsOwnedString(exec, identifier.string()));
927             RETURN_IF_EXCEPTION(scope, nullptr);
928         }
929
930         // To ensure the order defined in the spec (9.1.12), we append symbols at the last elements of keys.
931         for (const auto& identifier : propertySymbols) {
932             bool hasProperty = filterPropertyIfNeeded(identifier);
933             EXCEPTION_ASSERT(!scope.exception() || !hasProperty);
934             if (hasProperty)
935                 pushDirect(exec, keys, Symbol::create(vm, static_cast<SymbolImpl&>(*identifier.impl())));
936             RETURN_IF_EXCEPTION(scope, nullptr);
937         }
938
939         break;
940     }
941     }
942
943     return keys;
944 }
945
946 } // namespace JSC