Changeset 271343 in webkit
- Timestamp:
- Jan 8, 2021 8:31:12 PM (19 months ago)
- Location:
- trunk
- Files:
-
- 2 added
- 12 edited
-
JSTests/ChangeLog (modified) (1 diff)
-
JSTests/microbenchmarks/object-rest-destructuring.js (added)
-
JSTests/microbenchmarks/object-spread.js (added)
-
JSTests/stress/object-rest-deconstruct.js (modified) (5 diffs)
-
JSTests/stress/object-spread.js (modified) (6 diffs)
-
Source/JavaScriptCore/ChangeLog (modified) (1 diff)
-
Source/JavaScriptCore/builtins/BuiltinNames.h (modified) (2 diffs)
-
Source/JavaScriptCore/builtins/GlobalOperations.js (modified) (1 diff)
-
Source/JavaScriptCore/bytecode/BytecodeIntrinsicRegistry.h (modified) (1 diff)
-
Source/JavaScriptCore/bytecode/LinkTimeConstant.h (modified) (1 diff)
-
Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp (modified) (3 diffs)
-
Source/JavaScriptCore/runtime/JSGlobalObject.cpp (modified) (1 diff)
-
Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp (modified) (1 diff)
-
Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.h (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
trunk/JSTests/ChangeLog
r271305 r271343 1 2021-01-08 Alexey Shvayka <shvaikalesh@gmail.com> 2 3 Implement @copyDataProperties in C++ to optimize object rest / spread 4 https://bugs.webkit.org/show_bug.cgi?id=193618 5 6 Reviewed by Yusuke Suzuki. 7 8 * microbenchmarks/object-rest-destructuring.js: Added. 9 * microbenchmarks/object-spread.js: Added. 10 * stress/object-rest-deconstruct.js: 11 * stress/object-spread.js: 12 1 13 2021-01-08 Alexey Shvayka <shvaikalesh@gmail.com> 2 14 -
trunk/JSTests/stress/object-rest-deconstruct.js
r267440 r271343 4 4 } 5 5 6 let assertPropDescriptor = (restObj, prop) => { 6 function shouldBe(actual, expected) { 7 if (actual !== expected) 8 throw new Error(`Bad value: ${actual}!`); 9 } 10 11 function assertDataProperty(restObj, prop, value) { 12 shouldBe(restObj[prop], value); 7 13 let desc = Object.getOwnPropertyDescriptor(restObj, prop); 14 shouldBe(desc.value, value); 8 15 assert(desc.enumerable); 9 16 assert(desc.writable); … … 20 27 assert(b === 3); 21 28 22 assert(rest.x === 1); 23 assert(rest.y === 2); 24 25 assertPropDescriptor(rest, 'x'); 26 assertPropDescriptor(rest, 'y'); 29 assertDataProperty(rest, 'x', 1); 30 assertDataProperty(rest, 'y', 2); 27 31 })(); 28 32 … … 102 106 assert(b === 4); 103 107 104 assert(rest.x === 3); 105 assertPropDescriptor(rest, 'x'); 108 assertDataProperty(rest, 'x', 3); 106 109 })(); 107 110 … … 129 132 assert(rest.b === 4); 130 133 131 assert PropDescriptor(rest, 'a');132 assert PropDescriptor(rest, 'b');134 assertDataProperty(rest, 'a', 3); 135 assertDataProperty(rest, 'b', 4); 133 136 })(); 134 137 … … 236 239 })(); 237 240 241 (function nonEnumerableSymbol() { 242 var __s0 = Symbol("s0"); 243 var __s1 = Symbol("s1"); 244 245 var source = {}; 246 Object.defineProperties(source, { 247 [__s0]: {value: 0, enumerable: false}, 248 [__s1]: {value: 1, enumerable: false}, 249 }); 250 251 var {[__s0]: s0, ...target} = source; 252 253 shouldBe(s0, 0); 254 assert(!Object.getOwnPropertySymbols(target).length); 255 })(); 256 257 (function dunderProto() { 258 var source = {}; 259 var protoValue = {}; 260 Object.defineProperty(source, "__proto__", {value: protoValue, enumerable: true}); 261 var {...target} = source; 262 263 assertDataProperty(target, "__proto__", protoValue); 264 shouldBe(Object.getPrototypeOf(target), Object.prototype); 265 })(); 266 267 (function stringPrimitive() { 268 var source = "012"; 269 var {0: a, ["2"]: c, ...target} = source; 270 271 shouldBe(a, "0"); 272 shouldBe(c, "2"); 273 assertDataProperty(target, 1, "1"); 274 shouldBe(Object.keys(target).join(), "1"); 275 })(); 276 277 (function ProxyObject() { 278 var __s0 = Symbol("s0"); 279 var __s1 = Symbol("s1"); 280 281 var ownKeysCalls = 0; 282 var gopdCalls = []; 283 var getCalls = []; 284 285 var source = new Proxy({ 286 [__s0]: "s0", [__s1]: "s1", 287 a: 0, b: 1, c: 2, d: 3, 288 }, { 289 ownKeys: (t) => { 290 ++ownKeysCalls; 291 return Reflect.ownKeys(t); 292 }, 293 getOwnPropertyDescriptor: (t, key) => { 294 gopdCalls.push(key); 295 var desc = Reflect.getOwnPropertyDescriptor(t, key); 296 if (key === "b" || key === __s0) 297 desc.enumerable = false; 298 return desc; 299 }, 300 get: (t, key, receiver) => { 301 getCalls.push(key); 302 return Reflect.get(t, key, receiver); 303 }, 304 }); 305 306 var {c, ["d"]: d, ...target} = source; 307 308 shouldBe(ownKeysCalls, 1); 309 shouldBe(gopdCalls.map(String).join(), "a,b,Symbol(s0),Symbol(s1)"); 310 shouldBe(getCalls.map(String).join(), "c,d,a,Symbol(s1)"); 311 312 assertDataProperty(target, "a", 0); 313 shouldBe(c, 2); 314 shouldBe(d, 3); 315 shouldBe(Object.keys(target).join(), "a"); 316 317 var symbols = Object.getOwnPropertySymbols(target); 318 shouldBe(symbols[0], __s1); 319 shouldBe(symbols.length, 1); 320 })(); 321 322 (function indexedProperties() { 323 var source = [0, 1, 2, 3]; 324 Object.defineProperty(source, "1", {enumerable: false}); 325 var {2: c, ["3"]: d, ...target} = source; 326 327 assertDataProperty(target, "0", 0); 328 shouldBe(Object.keys(target).join(), "0"); 329 })(); 330 331 (function CustomAccessor() { 332 var source = $vm.createCustomTestGetterSetter(); 333 var {customValueGlobalObject, ...target} = source; 334 335 assertDataProperty(target, "customValue", source); 336 assert(!target.hasOwnProperty("customValueGlobalObject")); 337 shouldBe(customValueGlobalObject[Symbol.toStringTag], "global"); 338 assertDataProperty(target, "customAccessor", source); 339 assertDataProperty(target, "customAccessorGlobalObject", customValueGlobalObject); 340 })(); 341 342 (function CustomAccessorInNonReifiedPropertyTable() { 343 // make sure we reifyAllStaticProperties() before deciding on the fast/slow path 344 345 var source = $vm.createStaticCustomAccessor(); 346 source.testField = 42; 347 var {...target} = source; 348 349 assertDataProperty(target, "testField", 42); 350 assertDataProperty(target, "testStaticAccessor", 42); 351 })(); 352 353 (function uncacheableDictionary() { 354 var source = {a: 0, b: 1, c: 2, d: 3}; 355 Object.defineProperty(source, "c", {enumerable: false}); 356 $vm.toUncacheableDictionary(source); 357 var {b, ["d"]: d, ...target} = source; 358 359 assertDataProperty(target, "a", 0); 360 shouldBe(b, 1); 361 shouldBe(d, 3); 362 shouldBe(Object.keys(target).join(), "a"); 363 })(); -
trunk/JSTests/stress/object-spread.js
r219443 r271343 8 8 } 9 9 10 function validatePropertyDescriptor(o, p) { 11 let desc = Object.getOwnPropertyDescriptor(o, p); 12 10 function shouldBe(actual, expected) { 11 if (actual !== expected) 12 throw new Error(`Bad value: ${actual}!`); 13 } 14 15 function assertDataProperty(target, prop, value) { 16 shouldBe(target[prop], value); 17 let desc = Object.getOwnPropertyDescriptor(target, prop); 18 shouldBe(desc.value, value); 13 19 assert(desc.enumerable); 20 assert(desc.writable); 14 21 assert(desc.configurable); 15 assert(desc.writable);16 22 } 17 23 … … 23 29 assert.sameValue(obj.a, 1); 24 30 assert(obj.b, 2); 25 assert(obj.c, 3); 26 assert(obj.d, 4); 27 validatePropertyDescriptor(obj, "c"); 28 validatePropertyDescriptor(obj, "d"); 31 assertDataProperty(obj, "c", 3); 32 assertDataProperty(obj, "d", 4); 29 33 assert(Object.keys(obj), 2); 30 34 })(); … … 34 38 let obj = {a: 1, b: 2, ...o}; 35 39 36 assert .sameValue(obj.a, 1);37 assert .sameValue(obj.b, 2);38 assert .sameValue(obj.c, 3);39 assert .sameValue(obj.d, 4);40 assertDataProperty(obj, "a", 1); 41 assertDataProperty(obj, "b", 2); 42 assertDataProperty(obj, "c", 3); 43 assertDataProperty(obj, "d", 4); 40 44 assert.sameValue(Object.keys(obj).length, 4); 41 42 validatePropertyDescriptor(obj, "a");43 validatePropertyDescriptor(obj, "b");44 validatePropertyDescriptor(obj, "c");45 validatePropertyDescriptor(obj, "d");46 45 })(); 47 46 … … 129 128 let obj = {...o, c: 4, d: 5}; 130 129 131 assert .sameValue(Object.getOwnPropertyDescriptor(obj, "a").value, 42);130 assertDataProperty(obj, "a", 42); 132 131 assert.sameValue(obj.c, 4); 133 132 assert.sameValue(obj.d, 5); 134 133 assert.sameValue(Object.keys(obj).length, 3); 135 136 validatePropertyDescriptor(obj, "a");137 134 })(); 138 135 … … 224 221 let obj = {...o, a: 3}; 225 222 226 assert.sameValue(obj.a, 3) 227 assert.sameValue(obj.b, 2); 228 validatePropertyDescriptor(obj, "a"); 229 validatePropertyDescriptor(obj, "b"); 223 assertDataProperty(obj, "a", 3); 224 assertDataProperty(obj, "b", 2); 230 225 })(); 231 226 … … 312 307 })(); 313 308 309 (function nonEnumerableSymbol() { 310 var __s0 = Symbol("s0"); 311 var source = {}; 312 Object.defineProperties(source, { 313 [__s0]: {value: 0, enumerable: false}, 314 }); 315 316 var target = {[__s0]: 1, ...target}; 317 assertDataProperty(target, __s0, 1); 318 })(); 319 320 (function dunderProto() { 321 var source = {}; 322 var protoValue = {}; 323 Object.defineProperty(source, "__proto__", {value: protoValue, enumerable: true}); 324 var target = {...source}; 325 326 assertDataProperty(target, "__proto__", protoValue); 327 shouldBe(Object.getPrototypeOf(target), Object.prototype); 328 })(); 329 330 (function stringPrimitive() { 331 var source = "012"; 332 var target = {get 0() {}, ["2"]: 22, ...source}; 333 334 assertDataProperty(target, 0, "0"); 335 assertDataProperty(target, 1, "1"); 336 assertDataProperty(target, 2, "2"); 337 shouldBe(Object.keys(target).join(), "0,1,2"); 338 })(); 339 340 (function ProxyObject() { 341 var __s0 = Symbol("s0"); 342 var __s1 = Symbol("s1"); 343 344 var ownKeysCalls = 0; 345 var gopdCalls = []; 346 var getCalls = []; 347 348 var source = new Proxy({ 349 [__s0]: "s0", [__s1]: "s1", 350 a: 0, b: 1, c: 2, d: 3, 351 }, { 352 ownKeys: (t) => { 353 ++ownKeysCalls; 354 return Reflect.ownKeys(t); 355 }, 356 getOwnPropertyDescriptor: (t, key) => { 357 gopdCalls.push(key); 358 var desc = Reflect.getOwnPropertyDescriptor(t, key); 359 if (key === "b" || key === __s0) 360 desc.enumerable = false; 361 return desc; 362 }, 363 get: (t, key, receiver) => { 364 getCalls.push(key); 365 return Reflect.get(t, key, receiver); 366 }, 367 }); 368 369 var target = {a() {}, b: 11, [__s1]: "foo", ...source, ["d"]: 33}; 370 371 shouldBe(ownKeysCalls, 1); 372 shouldBe(gopdCalls.map(String).join(), "a,b,c,d,Symbol(s0),Symbol(s1)"); 373 shouldBe(getCalls.map(String).join(), "a,c,d,Symbol(s1)"); 374 375 assertDataProperty(target, "a", 0); 376 assertDataProperty(target, "b", 11); 377 assertDataProperty(target, "c", 2); 378 assertDataProperty(target, "d", 33); 379 380 shouldBe(Reflect.ownKeys(target).map(String).join(), "a,b,c,d,Symbol(s1)"); 381 })(); 382 383 (function indexedProperties() { 384 var source = [0, 1, 2]; 385 Object.defineProperty(source, "1", {enumerable: false}); 386 var target = {set ["2"](_v) {}, ...source}; 387 388 assertDataProperty(target, "0", 0); 389 assertDataProperty(target, "2", 2); 390 shouldBe(Object.keys(target).join(), "0,2"); 391 })(); 392 393 (function CustomAccessor() { 394 var source = $vm.createCustomTestGetterSetter(); 395 var target = {...source}; 396 397 assertDataProperty(target, "customValue", source); 398 shouldBe(target.customValueGlobalObject, target.customAccessorGlobalObject); 399 shouldBe(target.customValueGlobalObject[Symbol.toStringTag], "global"); 400 assertDataProperty(target, "customAccessor", source); 401 })(); 402 403 (function CustomAccessorInNonReifiedPropertyTable() { 404 // make sure we reifyAllStaticProperties() before deciding on the fast/slow path 405 406 var source = $vm.createStaticCustomAccessor(); 407 source.testField = 42; 408 var target = {...source}; 409 410 assertDataProperty(target, "testField", 42); 411 assertDataProperty(target, "testStaticAccessor", 42); 412 })(); 413 414 (function uncacheableDictionary() { 415 var source = {a: 0, b: 1, c: 2}; 416 Object.defineProperty(source, "c", {enumerable: false}); 417 $vm.toUncacheableDictionary(source); 418 var b = 11; 419 var target = {get a() {}, b, ...source}; 420 421 assertDataProperty(target, "a", 0); 422 assertDataProperty(target, "b", 1); 423 shouldBe(Object.keys(target).join(), "a,b"); 424 })(); -
trunk/Source/JavaScriptCore/ChangeLog
r271332 r271343 1 2021-01-08 Alexey Shvayka <shvaikalesh@gmail.com> 2 3 Implement @copyDataProperties in C++ to optimize object rest / spread 4 https://bugs.webkit.org/show_bug.cgi?id=193618 5 6 Reviewed by Yusuke Suzuki. 7 8 Since @copyDataProperties is inherently polymorphic, implementing it in JS is not beneficial. 9 This patch: 10 11 1. Merges almost identical @copyDataProperties variants and moves them to C++, avoiding 12 allocations of JSArray instances and Identifier wrappers. 13 2. Skips non-observable [[Get]] calls, leveraging `slot.isTaintedByOpaqueObject()`. 14 3. Performs [[DefineOwnProperty]] via putDirectMayBeIndex(), since the spec guarantees 15 property creation to be successful [1]: `target` is an newly created object that is 16 not yet accessible to userland code. It's impossible for `target` to be non-extensible 17 nor have a non-configurable property. 18 4. Introduces a fast path similar to Object.assign, but: 19 a) with no checks on `target`, because it's guaranteed to be an extensible JSFinalObject; 20 b) with less checks on `source`, since we are performing putDirect() and don't care about 21 read-only properties nor __proto__. 22 23 Altogether, these changes result in 3.1x speed-up for object rest / spread. 24 Also, this patch removes unnecessary `target` return and @isObject check. 25 26 [1]: https://tc39.es/ecma262/#sec-copydataproperties (step 6.c.ii.2, note the "!" prefix) 27 28 * builtins/BuiltinNames.h: 29 * builtins/GlobalOperations.js: 30 (globalPrivate.speciesConstructor): 31 (globalPrivate.copyDataProperties): Deleted. 32 (globalPrivate.copyDataPropertiesNoExclusions): Deleted. 33 * bytecode/BytecodeIntrinsicRegistry.h: 34 * bytecode/LinkTimeConstant.h: 35 * bytecompiler/NodesCodegen.cpp: 36 (JSC::ObjectPatternNode::bindValue const): 37 (JSC::ObjectSpreadExpressionNode::emitBytecode): 38 (JSC::BytecodeIntrinsicNode::emit_intrinsic_defineEnumerableWritableConfigurableDataProperty): Deleted. 39 * runtime/JSGlobalObject.cpp: 40 (JSC::JSGlobalObject::init): 41 * runtime/JSGlobalObjectFunctions.cpp: 42 (JSC::canPerformFastPropertyEnumerationForCopyDataProperties): 43 (JSC::JSC_DEFINE_HOST_FUNCTION): 44 * runtime/JSGlobalObjectFunctions.h: 45 1 46 2021-01-08 Yusuke Suzuki <ysuzuki@apple.com> 2 47 -
trunk/Source/JavaScriptCore/builtins/BuiltinNames.h
r269531 r271343 68 68 macro(defaultPromiseThen) \ 69 69 macro(getOwnPropertyNames) \ 70 macro(ownKeys) \71 70 macro(Set) \ 72 71 macro(throwTypeErrorFunction) \ … … 170 169 macro(importModule) \ 171 170 macro(propertyIsEnumerable) \ 171 macro(copyDataProperties) \ 172 172 macro(meta) \ 173 173 macro(webAssemblyCompileStreamingInternal) \ -
trunk/Source/JavaScriptCore/builtins/GlobalOperations.js
r262017 r271343 76 76 @throwTypeError("|this|.constructor[Symbol.species] is not a constructor"); 77 77 } 78 79 @globalPrivate80 function copyDataProperties(target, source, excludedSet)81 {82 "use strict";83 84 if (!@isObject(target))85 @throwTypeError("target needs to be an object");86 87 if (@isUndefinedOrNull(source))88 return target;89 90 var from = @toObject(source);91 var keys = @ownKeys(from);92 var keysLength = keys.length;93 for (var i = 0; i < keysLength; i++) {94 var nextKey = keys[i];95 if (!excludedSet.@has(nextKey)) {96 if (@propertyIsEnumerable(from, nextKey)) {97 var propValue = from[nextKey];98 @defineEnumerableWritableConfigurableDataProperty(target, nextKey, propValue);99 }100 }101 }102 103 return target;104 }105 106 @globalPrivate107 function copyDataPropertiesNoExclusions(target, source)108 {109 "use strict";110 111 if (!@isObject(target))112 @throwTypeError("target needs to be an object");113 114 if (@isUndefinedOrNull(source))115 return target;116 117 var from = @toObject(source);118 var keys = @ownKeys(from);119 var keysLength = keys.length;120 for (var i = 0; i < keysLength; i++) {121 var nextKey = keys[i];122 if (@propertyIsEnumerable(from, nextKey)) {123 var propValue = from[nextKey];124 @defineEnumerableWritableConfigurableDataProperty(target, nextKey, propValue);125 }126 }127 128 return target;129 } -
trunk/Source/JavaScriptCore/bytecode/BytecodeIntrinsicRegistry.h
r268489 r271343 93 93 macro(createPromise) \ 94 94 macro(createArgumentsButterfly) \ 95 macro(defineEnumerableWritableConfigurableDataProperty) \96 95 97 96 #define JSC_COMMON_BYTECODE_INTRINSIC_CONSTANTS_EACH_NAME(macro) \ -
trunk/Source/JavaScriptCore/bytecode/LinkTimeConstant.h
r269531 r271343 46 46 v(setPrototypeDirect, nullptr) \ 47 47 v(propertyIsEnumerable, nullptr) \ 48 v( ownKeys, nullptr) \48 v(copyDataProperties, nullptr) \ 49 49 v(enqueueJob, nullptr) \ 50 50 v(makeTypeError, nullptr) \ -
trunk/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
r271265 r271343 1777 1777 ASSERT(!m_args->m_listNode); 1778 1778 return generator.emitCreateArgumentsButterfly(generator.finalDestination(dst)); 1779 }1780 1781 RegisterID* BytecodeIntrinsicNode::emit_intrinsic_defineEnumerableWritableConfigurableDataProperty(JSC::BytecodeGenerator& generator, JSC::RegisterID* dst)1782 {1783 ArgumentListNode* node = m_args->m_listNode;1784 RefPtr<RegisterID> newObj = generator.emitNode(node);1785 node = node->m_next;1786 RefPtr<RegisterID> propertyNameRegister = generator.emitNode(node);1787 node = node->m_next;1788 RefPtr<RegisterID> value = generator.emitNode(node);1789 ASSERT(!node->m_next);1790 1791 generator.emitCallDefineProperty(newObj.get(), propertyNameRegister.get(), value.get(), nullptr, nullptr, BytecodeGenerator::PropertyConfigurable | BytecodeGenerator::PropertyWritable | BytecodeGenerator::PropertyEnumerable, m_position);1792 return dst;1793 1779 } 1794 1780 … … 5359 5345 } 5360 5346 5361 RefPtr<RegisterID> result = generator.newTemporary(); 5362 generator.emitCall(result.get(), copyDataProperties.get(), NoExpectedFunction, args, divot(), divotStart(), divotEnd(), DebuggableCall::No); 5363 target.pattern->bindValue(generator, result.get()); 5347 generator.emitCall(generator.newTemporary(), copyDataProperties.get(), NoExpectedFunction, args, divot(), divotStart(), divotEnd(), DebuggableCall::No); 5348 target.pattern->bindValue(generator, newObject.get()); 5364 5349 } 5365 5350 } … … 5570 5555 generator.emitNode(src.get(), m_expression); 5571 5556 5572 // load and call @copyDataPropertiesNoExclusions 5573 RefPtr<RegisterID> copyDataProperties = generator.moveLinkTimeConstant(nullptr, LinkTimeConstant::copyDataPropertiesNoExclusions); 5557 RefPtr<RegisterID> copyDataProperties = generator.moveLinkTimeConstant(nullptr, LinkTimeConstant::copyDataProperties); 5574 5558 5575 5559 CallArguments args(generator, nullptr, 2); -
trunk/Source/JavaScriptCore/runtime/JSGlobalObject.cpp
r269574 r271343 1190 1190 init.set(JSFunction::create(init.vm, jsCast<JSGlobalObject*>(init.owner), 0, String(), globalFuncPropertyIsEnumerable)); 1191 1191 }); 1192 m_linkTimeConstants[static_cast<unsigned>(LinkTimeConstant:: ownKeys)].initLater([] (const Initializer<JSCell>& init) {1193 init.set(JSFunction::create(init.vm, jsCast<JSGlobalObject*>(init.owner), 0, String(), globalFuncOwnKeys));1192 m_linkTimeConstants[static_cast<unsigned>(LinkTimeConstant::copyDataProperties)].initLater([] (const Initializer<JSCell>& init) { 1193 init.set(JSFunction::create(init.vm, jsCast<JSGlobalObject*>(init.owner), 2, String(), globalFuncCopyDataProperties)); 1194 1194 }); 1195 1195 m_linkTimeConstants[static_cast<unsigned>(LinkTimeConstant::enqueueJob)].initLater([] (const Initializer<JSCell>& init) { -
trunk/Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp
r267594 r271343 840 840 } 841 841 842 JSC_DEFINE_HOST_FUNCTION(globalFuncOwnKeys, (JSGlobalObject* globalObject, CallFrame* callFrame)) 843 { 844 VM& vm = globalObject->vm(); 845 auto scope = DECLARE_THROW_SCOPE(vm); 846 JSObject* object = callFrame->argument(0).toObject(globalObject); 847 RETURN_IF_EXCEPTION(scope, encodedJSValue()); 848 RELEASE_AND_RETURN(scope, JSValue::encode(ownPropertyKeys(globalObject, object, PropertyNameMode::StringsAndSymbols, DontEnumPropertiesMode::Include, WTF::nullopt))); 842 static bool canPerformFastPropertyEnumerationForCopyDataProperties(Structure* structure) 843 { 844 if (structure->typeInfo().overridesGetOwnPropertySlot()) 845 return false; 846 if (structure->typeInfo().overridesAnyFormOfGetOwnPropertyNames()) 847 return false; 848 // FIXME: Indexed properties can be handled. 849 // https://bugs.webkit.org/show_bug.cgi?id=185358 850 if (hasIndexedProperties(structure->indexingType())) 851 return false; 852 if (structure->hasGetterSetterProperties()) 853 return false; 854 if (structure->hasCustomGetterSetterProperties()) 855 return false; 856 if (structure->isUncacheableDictionary()) 857 return false; 858 return true; 859 }; 860 861 // https://tc39.es/ecma262/#sec-copydataproperties 862 JSC_DEFINE_HOST_FUNCTION(globalFuncCopyDataProperties, (JSGlobalObject* globalObject, CallFrame* callFrame)) 863 { 864 VM& vm = globalObject->vm(); 865 auto scope = DECLARE_THROW_SCOPE(vm); 866 867 JSFinalObject* target = jsCast<JSFinalObject*>(callFrame->uncheckedArgument(0)); 868 ASSERT(target->isStructureExtensible(vm)); 869 870 JSValue sourceValue = callFrame->uncheckedArgument(1); 871 if (sourceValue.isUndefinedOrNull()) 872 return JSValue::encode(jsUndefined()); 873 874 JSObject* source = sourceValue.toObject(globalObject); 875 scope.assertNoException(); 876 877 JSSet* excludedSet = nullptr; 878 if (callFrame->argumentCount() > 2) 879 excludedSet = jsCast<JSSet*>(callFrame->uncheckedArgument(2)); 880 881 auto isPropertyNameExcluded = [&] (JSGlobalObject* globalObject, PropertyName propertyName) -> bool { 882 ASSERT(!propertyName.isPrivateName()); 883 if (!excludedSet) 884 return false; 885 886 JSValue propertyNameValue = identifierToJSValue(vm, Identifier::fromUid(vm, propertyName.uid())); 887 RETURN_IF_EXCEPTION(scope, false); 888 return excludedSet->has(globalObject, propertyNameValue); 889 }; 890 891 if (!source->staticPropertiesReified(vm)) { 892 source->reifyAllStaticProperties(globalObject); 893 RETURN_IF_EXCEPTION(scope, { }); 894 } 895 896 if (canPerformFastPropertyEnumerationForCopyDataProperties(source->structure(vm))) { 897 Vector<RefPtr<UniquedStringImpl>, 8> properties; 898 MarkedArgumentBuffer values; 899 900 // FIXME: It doesn't seem like we should have to do this in two phases, but 901 // we're running into crashes where it appears that source is transitioning 902 // under us, and even ends up in a state where it has a null butterfly. My 903 // leading hypothesis here is that we fire some value replacement watchpoint 904 // that ends up transitioning the structure underneath us. 905 // https://bugs.webkit.org/show_bug.cgi?id=187837 906 907 source->structure(vm)->forEachProperty(vm, [&] (const PropertyMapEntry& entry) -> bool { 908 PropertyName propertyName(entry.key); 909 if (propertyName.isPrivateName()) 910 return true; 911 912 bool excluded = isPropertyNameExcluded(globalObject, propertyName); 913 RETURN_IF_EXCEPTION(scope, false); 914 if (excluded) 915 return true; 916 if (entry.attributes & PropertyAttribute::DontEnum) 917 return true; 918 919 properties.append(entry.key); 920 values.appendWithCrashOnOverflow(source->getDirect(entry.offset)); 921 return true; 922 }); 923 924 RETURN_IF_EXCEPTION(scope, { }); 925 926 for (size_t i = 0; i < properties.size(); ++i) { 927 // FIXME: We could put properties in a batching manner to accelerate CopyDataProperties more. 928 // https://bugs.webkit.org/show_bug.cgi?id=185358 929 target->putDirect(vm, properties[i].get(), values.at(i)); 930 } 931 } else { 932 PropertyNameArray propertyNames(vm, PropertyNameMode::StringsAndSymbols, PrivateSymbolMode::Exclude); 933 source->methodTable(vm)->getOwnPropertyNames(source, globalObject, propertyNames, DontEnumPropertiesMode::Include); 934 RETURN_IF_EXCEPTION(scope, { }); 935 936 for (const auto& propertyName : propertyNames) { 937 bool excluded = isPropertyNameExcluded(globalObject, propertyName); 938 RETURN_IF_EXCEPTION(scope, false); 939 if (excluded) 940 continue; 941 942 PropertySlot slot(source, PropertySlot::InternalMethodType::GetOwnProperty); 943 bool hasProperty = source->methodTable(vm)->getOwnPropertySlot(source, globalObject, propertyName, slot); 944 RETURN_IF_EXCEPTION(scope, { }); 945 if (!hasProperty) 946 continue; 947 if (slot.attributes() & PropertyAttribute::DontEnum) 948 continue; 949 950 JSValue value; 951 if (LIKELY(!slot.isTaintedByOpaqueObject())) 952 value = slot.getValue(globalObject, propertyName); 953 else 954 value = source->get(globalObject, propertyName); 955 RETURN_IF_EXCEPTION(scope, { }); 956 957 target->putDirectMayBeIndex(globalObject, propertyName, value); 958 } 959 } 960 961 return JSValue::encode(jsUndefined()); 849 962 } 850 963 -
trunk/Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.h
r268640 r271343 58 58 JSC_DECLARE_HOST_FUNCTION(globalFuncImportModule); 59 59 JSC_DECLARE_HOST_FUNCTION(globalFuncPropertyIsEnumerable); 60 JSC_DECLARE_HOST_FUNCTION(globalFunc OwnKeys);60 JSC_DECLARE_HOST_FUNCTION(globalFuncCopyDataProperties); 61 61 JSC_DECLARE_HOST_FUNCTION(globalFuncDateTimeFormat); 62 62
Note: See TracChangeset
for help on using the changeset viewer.