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