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