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 | |
41 | namespace JSC { |
42 | |
43 | EncodedJSValue JSC_HOST_CALL objectConstructorAssign(ExecState*); |
44 | EncodedJSValue JSC_HOST_CALL objectConstructorValues(ExecState*); |
45 | EncodedJSValue JSC_HOST_CALL objectConstructorGetPrototypeOf(ExecState*); |
46 | EncodedJSValue JSC_HOST_CALL objectConstructorSetPrototypeOf(ExecState*); |
47 | EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyNames(ExecState*); |
48 | EncodedJSValue JSC_HOST_CALL objectConstructorDefineProperty(ExecState*); |
49 | EncodedJSValue JSC_HOST_CALL objectConstructorDefineProperties(ExecState*); |
50 | EncodedJSValue JSC_HOST_CALL objectConstructorCreate(ExecState*); |
51 | EncodedJSValue JSC_HOST_CALL objectConstructorSeal(ExecState*); |
52 | EncodedJSValue JSC_HOST_CALL objectConstructorFreeze(ExecState*); |
53 | EncodedJSValue JSC_HOST_CALL objectConstructorPreventExtensions(ExecState*); |
54 | EncodedJSValue JSC_HOST_CALL objectConstructorIsSealed(ExecState*); |
55 | EncodedJSValue JSC_HOST_CALL objectConstructorIsFrozen(ExecState*); |
56 | EncodedJSValue JSC_HOST_CALL objectConstructorIsExtensible(ExecState*); |
57 | EncodedJSValue JSC_HOST_CALL objectConstructorIs(ExecState*); |
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(ExecState*); |
97 | static EncodedJSValue JSC_HOST_CALL constructWithObjectConstructor(ExecState*); |
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(), 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]) |
118 | static 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 | |
146 | static EncodedJSValue JSC_HOST_CALL constructWithObjectConstructor(ExecState* exec) |
147 | { |
148 | return JSValue::encode(constructObject(exec, exec->newTarget())); |
149 | } |
150 | |
151 | static EncodedJSValue JSC_HOST_CALL callObjectConstructor(ExecState* exec) |
152 | { |
153 | return JSValue::encode(constructObject(exec, JSValue())); |
154 | } |
155 | |
156 | EncodedJSValue 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 | |
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, "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 | |
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 | 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 | |
203 | JSValue 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 | |
235 | EncodedJSValue 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 | |
246 | EncodedJSValue 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. |
256 | EncodedJSValue 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. |
266 | EncodedJSValue 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 | |
275 | EncodedJSValue 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 | |
284 | EncodedJSValue 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 | |
429 | EncodedJSValue 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 |
482 | bool 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 | |
577 | EncodedJSValue 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 | |
598 | static 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 | |
644 | EncodedJSValue 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 | |
659 | EncodedJSValue 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 | |
677 | enum class IntegrityLevel { |
678 | Sealed, |
679 | Frozen |
680 | }; |
681 | |
682 | template<IntegrityLevel level> |
683 | bool 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 | |
723 | template<IntegrityLevel level> |
724 | bool 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 | |
772 | JSObject* 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 | |
792 | EncodedJSValue 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 | |
805 | JSObject* 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 | |
824 | EncodedJSValue 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 | |
837 | EncodedJSValue 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 | |
848 | EncodedJSValue 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 | |
866 | EncodedJSValue 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 | |
884 | EncodedJSValue 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 | |
897 | EncodedJSValue JSC_HOST_CALL objectConstructorIs(ExecState* exec) |
898 | { |
899 | return JSValue::encode(jsBoolean(sameValue(exec, exec->argument(0), exec->argument(1)))); |
900 | } |
901 | |
902 | JSArray* 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 | |