RealmFuses.cpp (22052B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 #include "vm/RealmFuses.h" 7 8 #include <array> 9 10 #include "builtin/MapObject.h" 11 #include "builtin/Promise.h" 12 #include "builtin/RegExp.h" 13 #include "builtin/WeakMapObject.h" 14 #include "builtin/WeakSetObject.h" 15 #include "js/experimental/TypedData.h" 16 #include "vm/GlobalObject.h" 17 #include "vm/NativeObject.h" 18 #include "vm/ObjectOperations.h" 19 #include "vm/Realm.h" 20 #include "vm/SelfHosting.h" 21 22 #include "vm/JSObject-inl.h" 23 24 using namespace js; 25 26 void js::InvalidatingRealmFuse::popFuse(JSContext* cx, RealmFuses& realmFuses) { 27 // Return early if the fuse is already popped. 28 if (!intact()) { 29 return; 30 } 31 32 InvalidatingFuse::popFuse(cx); 33 34 for (auto& fd : realmFuses.fuseDependencies) { 35 fd.invalidateForFuse(cx, this); 36 } 37 } 38 39 bool js::InvalidatingRealmFuse::addFuseDependency( 40 JSContext* cx, const jit::IonScriptKey& ionScript) { 41 MOZ_ASSERT(ionScript.script()->realm() == cx->realm()); 42 auto* scriptSet = 43 cx->realm()->realmFuses.fuseDependencies.getOrCreateDependentScriptSet( 44 cx, this); 45 if (!scriptSet) { 46 return false; 47 } 48 49 return scriptSet->addScriptForFuse(this, ionScript); 50 } 51 52 void js::PopsOptimizedGetIteratorFuse::popFuse(JSContext* cx, 53 RealmFuses& realmFuses) { 54 // Pop Self. 55 RealmFuse::popFuse(cx); 56 57 // Pop associated fuse in same realm as current object. 58 realmFuses.optimizeGetIteratorFuse.popFuse(cx, realmFuses); 59 } 60 61 void js::PopsOptimizedArrayIteratorPrototypeFuse::popFuse( 62 JSContext* cx, RealmFuses& realmFuses) { 63 // Pop Self. 64 RealmFuse::popFuse(cx); 65 66 // Pop associated fuse in same realm as current object. 67 realmFuses.optimizeArrayIteratorPrototypeFuse.popFuse(cx, realmFuses); 68 } 69 70 int32_t js::RealmFuses::fuseOffsets[uint8_t( 71 RealmFuses::FuseIndex::LastFuseIndex)] = { 72 #define FUSE(Name, LowerName) offsetof(RealmFuses, LowerName), 73 FOR_EACH_REALM_FUSE(FUSE) 74 #undef FUSE 75 }; 76 77 // static 78 int32_t js::RealmFuses::offsetOfFuseWordRelativeToRealm( 79 RealmFuses::FuseIndex index) { 80 int32_t base_offset = offsetof(Realm, realmFuses); 81 int32_t fuse_offset = RealmFuses::fuseOffsets[uint8_t(index)]; 82 int32_t fuseWordOffset = GuardFuse::fuseOffset(); 83 84 return base_offset + fuse_offset + fuseWordOffset; 85 } 86 87 const char* js::RealmFuses::fuseNames[] = { 88 #define FUSE(Name, LowerName) #LowerName, 89 FOR_EACH_REALM_FUSE(FUSE) 90 #undef FUSE 91 }; 92 93 // TODO: It is not elegant that we have both this mechanism, but also 94 // GuardFuse::name, and all the overrides for naming fuses. The issue is 95 // that this method is static to handle consumers that don't have a 96 // RealmFuses around but work with indexes (e.g. spew code). 97 // 98 // I'd love it if we had a better answer. 99 const char* js::RealmFuses::getFuseName(RealmFuses::FuseIndex index) { 100 uint8_t rawIndex = uint8_t(index); 101 MOZ_ASSERT(index < RealmFuses::FuseIndex::LastFuseIndex); 102 return fuseNames[rawIndex]; 103 } 104 105 bool js::OptimizeGetIteratorFuse::checkInvariant(JSContext* cx) { 106 // Simple invariant: this fuse merely reflects the conjunction of a group of 107 // fuses, so if this fuse is intact, then the invariant it asserts is that 108 // these two realm fuses are also intact. 109 auto& realmFuses = cx->realm()->realmFuses; 110 return realmFuses.arrayPrototypeIteratorFuse.intact() && 111 realmFuses.optimizeArrayIteratorPrototypeFuse.intact(); 112 } 113 114 void js::OptimizeGetIteratorFuse::popFuse(JSContext* cx, 115 RealmFuses& realmFuses) { 116 InvalidatingRealmFuse::popFuse(cx, realmFuses); 117 MOZ_ASSERT(cx->global()); 118 cx->runtime()->setUseCounter(cx->global(), 119 JSUseCounter::OPTIMIZE_GET_ITERATOR_FUSE); 120 } 121 122 bool js::OptimizeArrayIteratorPrototypeFuse::checkInvariant(JSContext* cx) { 123 // Simple invariant: this fuse merely reflects the conjunction of a group of 124 // fuses, so if this fuse is intact, then the invariant it asserts is that 125 // these realm fuses are also intact. 126 auto& realmFuses = cx->realm()->realmFuses; 127 return realmFuses.arrayPrototypeIteratorNextFuse.intact() && 128 realmFuses.arrayIteratorPrototypeHasNoReturnProperty.intact() && 129 realmFuses.iteratorPrototypeHasNoReturnProperty.intact() && 130 realmFuses.arrayIteratorPrototypeHasIteratorProto.intact() && 131 realmFuses.iteratorPrototypeHasObjectProto.intact() && 132 realmFuses.objectPrototypeHasNoReturnProperty.intact(); 133 } 134 135 static bool ObjectHasDataProperty(NativeObject* obj, PropertyKey key, 136 Value* val) { 137 mozilla::Maybe<PropertyInfo> prop = obj->lookupPure(key); 138 if (prop.isNothing() || !prop->isDataProperty()) { 139 return false; 140 } 141 *val = obj->getSlot(prop->slot()); 142 return true; 143 } 144 145 // Returns true if `obj` has a data property with the given `key` and its value 146 // is `expectedValue`. 147 static bool ObjectHasDataPropertyValue(NativeObject* obj, PropertyKey key, 148 const Value& expectedValue) { 149 Value v; 150 if (!ObjectHasDataProperty(obj, key, &v)) { 151 return false; 152 } 153 return v == expectedValue; 154 } 155 156 // Returns true if `obj` has a data property with the given `key` and its value 157 // is a native function that matches `expectedFunction`. 158 static bool ObjectHasDataPropertyFunction(NativeObject* obj, PropertyKey key, 159 JSNative expectedFunction) { 160 Value v; 161 if (!ObjectHasDataProperty(obj, key, &v)) { 162 return false; 163 } 164 if (!IsNativeFunction(v, expectedFunction)) { 165 return false; 166 } 167 if (obj->realm() != v.toObject().as<JSFunction>().realm()) { 168 return false; 169 } 170 return true; 171 } 172 173 // Returns true if `obj` has a data property with the given `key` and its value 174 // is a self-hosted function with `selfHostedName`. 175 static bool ObjectHasDataPropertyFunction(NativeObject* obj, PropertyKey key, 176 PropertyName* selfHostedName) { 177 Value v; 178 if (!ObjectHasDataProperty(obj, key, &v)) { 179 return false; 180 } 181 if (!IsSelfHostedFunctionWithName(v, selfHostedName)) { 182 return false; 183 } 184 if (obj->realm() != v.toObject().as<JSFunction>().realm()) { 185 return false; 186 } 187 return true; 188 } 189 190 static bool ObjectHasGetterProperty(NativeObject* obj, PropertyKey key, 191 JSFunction** getter) { 192 mozilla::Maybe<PropertyInfo> prop = obj->lookupPure(key); 193 if (prop.isNothing() || !prop->isAccessorProperty()) { 194 return false; 195 } 196 JSObject* getterObject = obj->getGetter(*prop); 197 if (!getterObject || !getterObject->is<JSFunction>()) { 198 return false; 199 } 200 if (obj->realm() != getterObject->as<JSFunction>().realm()) { 201 return false; 202 } 203 *getter = &getterObject->as<JSFunction>(); 204 return true; 205 } 206 207 // Returns true if `obj` has an accessor property with the given `key` and the 208 // getter is a native function that matches `expectedFunction`. 209 static bool ObjectHasGetterFunction(NativeObject* obj, PropertyKey key, 210 JSNative expectedGetter) { 211 JSFunction* getter; 212 if (!ObjectHasGetterProperty(obj, key, &getter)) { 213 return false; 214 } 215 return IsNativeFunction(getter, expectedGetter); 216 } 217 218 // Returns true if `obj` has an accessor property with the given `key` and the 219 // getter is a self-hosted function with `selfHostedName`. 220 static bool ObjectHasGetterFunction(NativeObject* obj, PropertyKey key, 221 PropertyName* selfHostedName) { 222 JSFunction* getter; 223 if (!ObjectHasGetterProperty(obj, key, &getter)) { 224 return false; 225 } 226 return IsSelfHostedFunctionWithName(getter, selfHostedName); 227 } 228 229 bool js::ArrayPrototypeIteratorFuse::checkInvariant(JSContext* cx) { 230 // Prototype must be Array.prototype. 231 auto* proto = cx->global()->maybeGetArrayPrototype(); 232 if (!proto) { 233 // No proto, invariant still holds 234 return true; 235 } 236 237 PropertyKey iteratorKey = 238 PropertyKey::Symbol(cx->wellKnownSymbols().iterator); 239 240 // Ensure that Array.prototype's @@iterator slot is unchanged. 241 return ObjectHasDataPropertyFunction(proto, iteratorKey, 242 cx->names().dollar_ArrayValues_); 243 } 244 245 /* static */ 246 bool js::ArrayPrototypeIteratorNextFuse::checkInvariant(JSContext* cx) { 247 auto* proto = cx->global()->maybeGetArrayIteratorPrototype(); 248 249 if (!proto) { 250 // Invariant holds if there is no array iterator proto. 251 return true; 252 } 253 254 // Ensure that %ArrayIteratorPrototype%'s "next" slot is unchanged. 255 return ObjectHasDataPropertyFunction(proto, NameToId(cx->names().next), 256 cx->names().ArrayIteratorNext); 257 } 258 259 static bool HasNoReturnName(JSContext* cx, JS::HandleObject proto) { 260 if (!proto) { 261 // Invariant holds if there is no array iterator proto. 262 return true; 263 } 264 265 JS::RootedId returnName(cx, NameToId(cx->names().return_)); 266 267 // An alternative design here would chain together all the has-return-property 268 // fuses such that the fuses each express a stronger invariant; for now these 269 // fuses have only the invariant that each object -itself- has no return 270 // property. 271 bool found = true; 272 if (!HasOwnProperty(cx, proto, returnName, &found)) { 273 cx->recoverFromOutOfMemory(); 274 return true; 275 } 276 277 return !found; 278 } 279 280 /* static */ 281 bool js::ArrayIteratorPrototypeHasNoReturnProperty::checkInvariant( 282 JSContext* cx) { 283 RootedObject proto(cx, cx->global()->maybeGetArrayIteratorPrototype()); 284 285 if (!proto) { 286 // Invariant holds if there is no array iterator proto. 287 return true; 288 } 289 290 return HasNoReturnName(cx, proto); 291 } 292 293 /* static */ 294 bool js::IteratorPrototypeHasNoReturnProperty::checkInvariant(JSContext* cx) { 295 RootedObject proto(cx, cx->global()->maybeGetIteratorPrototype()); 296 297 if (!proto) { 298 // Invariant holds if there is no array iterator proto. 299 return true; 300 } 301 302 return HasNoReturnName(cx, proto); 303 } 304 305 /* static */ 306 bool js::ArrayIteratorPrototypeHasIteratorProto::checkInvariant(JSContext* cx) { 307 RootedObject proto(cx, cx->global()->maybeGetArrayIteratorPrototype()); 308 if (!proto) { 309 // Invariant holds if there is no array iterator proto. 310 return true; 311 } 312 313 RootedObject iterProto(cx, cx->global()->maybeGetIteratorPrototype()); 314 if (!iterProto) { 315 MOZ_CRASH("Can we have the array iter proto without the iterator proto?"); 316 return true; 317 } 318 319 return proto->staticPrototype() == iterProto; 320 } 321 322 /* static */ 323 bool js::IteratorPrototypeHasObjectProto::checkInvariant(JSContext* cx) { 324 RootedObject proto(cx, cx->global()->maybeGetIteratorPrototype()); 325 if (!proto) { 326 // Invariant holds if there is no array iterator proto. 327 return true; 328 } 329 330 return proto->staticPrototype() == &cx->global()->getObjectPrototype(); 331 } 332 333 /* static */ 334 bool js::ObjectPrototypeHasNoReturnProperty::checkInvariant(JSContext* cx) { 335 RootedObject proto(cx, &cx->global()->getObjectPrototype()); 336 return HasNoReturnName(cx, proto); 337 } 338 339 void js::OptimizeArraySpeciesFuse::popFuse(JSContext* cx, 340 RealmFuses& realmFuses) { 341 InvalidatingRealmFuse::popFuse(cx, realmFuses); 342 MOZ_ASSERT(cx->global()); 343 cx->runtime()->setUseCounter(cx->global(), 344 JSUseCounter::OPTIMIZE_ARRAY_SPECIES_FUSE); 345 } 346 347 static bool SpeciesFuseCheckInvariant(JSContext* cx, JSProtoKey protoKey, 348 PropertyName* selfHostedSpeciesAccessor) { 349 // Prototype must be initialized. 350 auto* proto = cx->global()->maybeGetPrototype<NativeObject>(protoKey); 351 if (!proto) { 352 // No proto, invariant still holds 353 return true; 354 } 355 356 auto* ctor = cx->global()->maybeGetConstructor<NativeObject>(protoKey); 357 MOZ_ASSERT(ctor); 358 359 // Ensure the prototype's `constructor` slot is the original constructor. 360 if (!ObjectHasDataPropertyValue(proto, NameToId(cx->names().constructor), 361 ObjectValue(*ctor))) { 362 return false; 363 } 364 365 // Ensure constructor's `@@species` slot is the original species getter. 366 PropertyKey speciesKey = PropertyKey::Symbol(cx->wellKnownSymbols().species); 367 return ObjectHasGetterFunction(ctor, speciesKey, selfHostedSpeciesAccessor); 368 } 369 370 bool js::OptimizeArraySpeciesFuse::checkInvariant(JSContext* cx) { 371 return SpeciesFuseCheckInvariant(cx, JSProto_Array, 372 cx->names().dollar_ArraySpecies_); 373 } 374 375 bool js::OptimizeArrayBufferSpeciesFuse::checkInvariant(JSContext* cx) { 376 return SpeciesFuseCheckInvariant(cx, JSProto_ArrayBuffer, 377 cx->names().dollar_ArrayBufferSpecies_); 378 } 379 380 bool js::OptimizeSharedArrayBufferSpeciesFuse::checkInvariant(JSContext* cx) { 381 return SpeciesFuseCheckInvariant( 382 cx, JSProto_SharedArrayBuffer, 383 cx->names().dollar_SharedArrayBufferSpecies_); 384 } 385 386 bool js::OptimizeTypedArraySpeciesFuse::checkInvariant(JSContext* cx) { 387 // Check `constructor` and `@@species` on %TypedArray%. 388 if (!SpeciesFuseCheckInvariant(cx, JSProto_TypedArray, 389 cx->names().dollar_TypedArraySpecies_)) { 390 return false; 391 } 392 393 auto typedArrayProtoKeys = std::array{ 394 #define PROTO_KEY(_, T, N) JSProto_##N##Array, 395 JS_FOR_EACH_TYPED_ARRAY(PROTO_KEY) 396 #undef PROTO_KEY 397 }; 398 399 auto* typedArrayproto = 400 cx->global()->maybeGetPrototype<NativeObject>(JSProto_TypedArray); 401 402 // Check all concrete TypedArray prototypes. 403 for (auto protoKey : typedArrayProtoKeys) { 404 // Prototype must be initialized. 405 auto* proto = cx->global()->maybeGetPrototype<NativeObject>(protoKey); 406 if (!proto) { 407 // No proto, invariant still holds 408 continue; 409 } 410 MOZ_ASSERT(typedArrayproto, 411 "%TypedArray%.prototype must be initialized when TypedArray " 412 "subclass is initialized"); 413 414 // Ensure the prototype's prototype is %TypedArray%.prototype. 415 if (proto->staticPrototype() != typedArrayproto) { 416 return false; 417 } 418 419 auto* ctor = cx->global()->maybeGetConstructor<NativeObject>(protoKey); 420 MOZ_ASSERT(ctor); 421 422 // Ensure the prototype's `constructor` slot is the original constructor. 423 if (!ObjectHasDataPropertyValue(proto, NameToId(cx->names().constructor), 424 ObjectValue(*ctor))) { 425 return false; 426 } 427 } 428 429 return true; 430 } 431 432 void js::OptimizePromiseLookupFuse::popFuse(JSContext* cx, 433 RealmFuses& realmFuses) { 434 RealmFuse::popFuse(cx, realmFuses); 435 MOZ_ASSERT(cx->global()); 436 cx->runtime()->setUseCounter(cx->global(), 437 JSUseCounter::OPTIMIZE_PROMISE_LOOKUP_FUSE); 438 } 439 440 bool js::OptimizePromiseLookupFuse::checkInvariant(JSContext* cx) { 441 // Prototype must be Promise.prototype. 442 auto* proto = cx->global()->maybeGetPrototype<NativeObject>(JSProto_Promise); 443 if (!proto) { 444 // No proto, invariant still holds. 445 return true; 446 } 447 448 auto* ctor = cx->global()->maybeGetConstructor<NativeObject>(JSProto_Promise); 449 MOZ_ASSERT(ctor); 450 451 // Ensure Promise.prototype's `constructor` slot is the `Promise` constructor. 452 if (!ObjectHasDataPropertyValue(proto, NameToId(cx->names().constructor), 453 ObjectValue(*ctor))) { 454 return false; 455 } 456 457 // Ensure Promise.prototype's `then` slot is the original function. 458 if (!ObjectHasDataPropertyFunction(proto, NameToId(cx->names().then), 459 js::Promise_then)) { 460 return false; 461 } 462 463 // Ensure Promise's `@@species` slot is the original getter. 464 PropertyKey speciesKey = PropertyKey::Symbol(cx->wellKnownSymbols().species); 465 if (!ObjectHasGetterFunction(ctor, speciesKey, js::Promise_static_species)) { 466 return false; 467 } 468 469 // Ensure Promise's `resolve` slot is the original function. 470 if (!ObjectHasDataPropertyFunction(ctor, NameToId(cx->names().resolve), 471 js::Promise_static_resolve)) { 472 return false; 473 } 474 475 return true; 476 } 477 478 bool js::OptimizeRegExpPrototypeFuse::checkInvariant(JSContext* cx) { 479 auto* proto = cx->global()->maybeGetPrototype<NativeObject>(JSProto_RegExp); 480 if (!proto) { 481 // No proto, invariant still holds. 482 return true; 483 } 484 485 // Check getters are unchanged. 486 if (!ObjectHasGetterFunction(proto, NameToId(cx->names().flags), 487 cx->names().dollar_RegExpFlagsGetter_)) { 488 return false; 489 } 490 if (!ObjectHasGetterFunction(proto, NameToId(cx->names().global), 491 regexp_global)) { 492 return false; 493 } 494 if (!ObjectHasGetterFunction(proto, NameToId(cx->names().hasIndices), 495 regexp_hasIndices)) { 496 return false; 497 } 498 if (!ObjectHasGetterFunction(proto, NameToId(cx->names().ignoreCase), 499 regexp_ignoreCase)) { 500 return false; 501 } 502 if (!ObjectHasGetterFunction(proto, NameToId(cx->names().multiline), 503 regexp_multiline)) { 504 return false; 505 } 506 if (!ObjectHasGetterFunction(proto, NameToId(cx->names().sticky), 507 regexp_sticky)) { 508 return false; 509 } 510 if (!ObjectHasGetterFunction(proto, NameToId(cx->names().unicode), 511 regexp_unicode)) { 512 return false; 513 } 514 if (!ObjectHasGetterFunction(proto, NameToId(cx->names().unicodeSets), 515 regexp_unicodeSets)) { 516 return false; 517 } 518 if (!ObjectHasGetterFunction(proto, NameToId(cx->names().dotAll), 519 regexp_dotAll)) { 520 return false; 521 } 522 523 // Check data properties are unchanged. 524 if (!ObjectHasDataPropertyFunction(proto, NameToId(cx->names().exec), 525 cx->names().RegExp_prototype_Exec)) { 526 return false; 527 } 528 if (!ObjectHasDataPropertyFunction( 529 proto, PropertyKey::Symbol(cx->wellKnownSymbols().match), 530 cx->names().RegExpMatch)) { 531 return false; 532 } 533 if (!ObjectHasDataPropertyFunction( 534 proto, PropertyKey::Symbol(cx->wellKnownSymbols().matchAll), 535 cx->names().RegExpMatchAll)) { 536 return false; 537 } 538 if (!ObjectHasDataPropertyFunction( 539 proto, PropertyKey::Symbol(cx->wellKnownSymbols().replace), 540 cx->names().RegExpReplace)) { 541 return false; 542 } 543 if (!ObjectHasDataPropertyFunction( 544 proto, PropertyKey::Symbol(cx->wellKnownSymbols().search), 545 cx->names().RegExpSearch)) { 546 return false; 547 } 548 if (!ObjectHasDataPropertyFunction( 549 proto, PropertyKey::Symbol(cx->wellKnownSymbols().split), 550 cx->names().RegExpSplit)) { 551 return false; 552 } 553 554 return true; 555 } 556 557 bool js::OptimizeMapObjectIteratorFuse::checkInvariant(JSContext* cx) { 558 // Ensure Map.prototype's @@iterator slot is unchanged. 559 auto* proto = cx->global()->maybeGetPrototype<NativeObject>(JSProto_Map); 560 if (!proto) { 561 // No proto, invariant still holds 562 return true; 563 } 564 PropertyKey iteratorKey = 565 PropertyKey::Symbol(cx->wellKnownSymbols().iterator); 566 if (!ObjectHasDataPropertyFunction(proto, iteratorKey, MapObject::entries)) { 567 return false; 568 } 569 570 // Ensure %MapIteratorPrototype%'s `next` slot is unchanged. 571 auto* iterProto = cx->global()->maybeBuiltinProto( 572 GlobalObject::ProtoKind::MapIteratorProto); 573 if (!iterProto) { 574 // No proto, invariant still holds 575 return true; 576 } 577 return ObjectHasDataPropertyFunction(&iterProto->as<NativeObject>(), 578 NameToId(cx->names().next), 579 cx->names().MapIteratorNext); 580 } 581 582 bool js::OptimizeSetObjectIteratorFuse::checkInvariant(JSContext* cx) { 583 // Ensure Set.prototype's @@iterator slot is unchanged. 584 auto* proto = cx->global()->maybeGetPrototype<NativeObject>(JSProto_Set); 585 if (!proto) { 586 // No proto, invariant still holds 587 return true; 588 } 589 PropertyKey iteratorKey = 590 PropertyKey::Symbol(cx->wellKnownSymbols().iterator); 591 if (!ObjectHasDataPropertyFunction(proto, iteratorKey, SetObject::values)) { 592 return false; 593 } 594 595 // Ensure %SetIteratorPrototype%'s `next` slot is unchanged. 596 auto* iterProto = cx->global()->maybeBuiltinProto( 597 GlobalObject::ProtoKind::SetIteratorProto); 598 if (!iterProto) { 599 // No proto, invariant still holds 600 return true; 601 } 602 return ObjectHasDataPropertyFunction(&iterProto->as<NativeObject>(), 603 NameToId(cx->names().next), 604 cx->names().SetIteratorNext); 605 } 606 607 bool js::OptimizeMapPrototypeSetFuse::checkInvariant(JSContext* cx) { 608 auto* proto = cx->global()->maybeGetPrototype<NativeObject>(JSProto_Map); 609 if (!proto) { 610 // No proto, invariant still holds 611 return true; 612 } 613 return ObjectHasDataPropertyFunction(proto, NameToId(cx->names().set), 614 MapObject::set); 615 } 616 617 bool js::OptimizeSetPrototypeAddFuse::checkInvariant(JSContext* cx) { 618 auto* proto = cx->global()->maybeGetPrototype<NativeObject>(JSProto_Set); 619 if (!proto) { 620 // No proto, invariant still holds 621 return true; 622 } 623 return ObjectHasDataPropertyFunction(proto, NameToId(cx->names().add), 624 SetObject::add); 625 } 626 627 bool js::OptimizeWeakMapPrototypeSetFuse::checkInvariant(JSContext* cx) { 628 auto* proto = cx->global()->maybeGetPrototype<NativeObject>(JSProto_WeakMap); 629 if (!proto) { 630 // No proto, invariant still holds 631 return true; 632 } 633 return ObjectHasDataPropertyFunction(proto, NameToId(cx->names().set), 634 WeakMapObject::set); 635 } 636 637 bool js::OptimizeWeakSetPrototypeAddFuse::checkInvariant(JSContext* cx) { 638 auto* proto = cx->global()->maybeGetPrototype<NativeObject>(JSProto_WeakSet); 639 if (!proto) { 640 // No proto, invariant still holds 641 return true; 642 } 643 return ObjectHasDataPropertyFunction(proto, NameToId(cx->names().add), 644 WeakSetObject::add); 645 }