RealmFuses.h (15853B)
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 7 #ifndef vm_RealmFuses_h 8 #define vm_RealmFuses_h 9 10 #include "vm/GuardFuse.h" 11 #include "vm/InvalidatingFuse.h" 12 13 namespace js { 14 15 class NativeObject; 16 struct RealmFuses; 17 18 // [SMDOC] RealmFuses: 19 // 20 // Realm fuses are fuses associated with a specific realm. As a result, 21 // popFuse for realmFuses has another argument, the set of realmFuses related to 22 // the fuse being popped. This is used to find any dependent fuses in the realm 23 // (rather than using the context). 24 class RealmFuse : public GuardFuse { 25 public: 26 virtual void popFuse(JSContext* cx, RealmFuses& realmFuses) { popFuse(cx); } 27 28 protected: 29 virtual void popFuse(JSContext* cx) override { GuardFuse::popFuse(cx); } 30 }; 31 32 class InvalidatingRealmFuse : public InvalidatingFuse { 33 public: 34 virtual void popFuse(JSContext* cx, RealmFuses& realmFuses); 35 virtual bool addFuseDependency(JSContext* cx, 36 const jit::IonScriptKey& ionScript) override; 37 38 protected: 39 virtual void popFuse(JSContext* cx) override { 40 InvalidatingFuse::popFuse(cx); 41 } 42 }; 43 44 // Fuse guarding against changes to `Array.prototype[@@iterator]` and 45 // `%ArrayIteratorPrototype%` that affect the iterator protocol for packed 46 // arrays. 47 // 48 // Popped when one of the following fuses is popped: 49 // - ArrayPrototypeIteratorFuse (for `Array.prototype[@@iterator]`) 50 // - OptimizeArrayIteratorPrototypeFuse (for `%ArrayIteratorPrototype%`) 51 struct OptimizeGetIteratorFuse final : public InvalidatingRealmFuse { 52 virtual const char* name() override { return "OptimizeGetIteratorFuse"; } 53 virtual bool checkInvariant(JSContext* cx) override; 54 virtual void popFuse(JSContext* cx, RealmFuses& realmFuses) override; 55 }; 56 57 struct PopsOptimizedGetIteratorFuse : public RealmFuse { 58 virtual void popFuse(JSContext* cx, RealmFuses& realmFuses) override; 59 }; 60 61 // Fuse guarding against changes to `%ArrayIteratorPrototype%` (and its 62 // prototype chain) that affect the iterator protocol. 63 // 64 // Popped when one of the following fuses is popped: 65 // - ArrayPrototypeIteratorNextFuse 66 // - ArrayIteratorPrototypeHasNoReturnProperty 67 // - ArrayIteratorPrototypeHasIteratorProto 68 // - IteratorPrototypeHasNoReturnProperty 69 // - IteratorPrototypeHasObjectProto 70 // - ObjectPrototypeHasNoReturnProperty 71 struct OptimizeArrayIteratorPrototypeFuse final 72 : public PopsOptimizedGetIteratorFuse { 73 virtual const char* name() override { 74 return "OptimizeArrayIteratorPrototypeFuse"; 75 } 76 virtual bool checkInvariant(JSContext* cx) override; 77 }; 78 79 struct PopsOptimizedArrayIteratorPrototypeFuse : public RealmFuse { 80 virtual void popFuse(JSContext* cx, RealmFuses& realmFuses) override; 81 }; 82 83 struct ArrayPrototypeIteratorFuse final : public PopsOptimizedGetIteratorFuse { 84 virtual const char* name() override { return "ArrayPrototypeIteratorFuse"; } 85 virtual bool checkInvariant(JSContext* cx) override; 86 }; 87 88 struct ArrayPrototypeIteratorNextFuse final 89 : public PopsOptimizedArrayIteratorPrototypeFuse { 90 virtual const char* name() override { 91 return "ArrayPrototypeIteratorNextFuse"; 92 } 93 virtual bool checkInvariant(JSContext* cx) override; 94 }; 95 96 // This fuse covers ArrayIteratorPrototype not having a return property; 97 // however the fuse doesn't pop if a prototype acquires the return property. 98 struct ArrayIteratorPrototypeHasNoReturnProperty final 99 : public PopsOptimizedArrayIteratorPrototypeFuse { 100 virtual const char* name() override { 101 return "ArrayIteratorPrototypeHasNoReturnProperty"; 102 } 103 virtual bool checkInvariant(JSContext* cx) override; 104 }; 105 106 // This fuse covers IteratorPrototype not having a return property; 107 // however the fuse doesn't pop if a prototype acquires the return property. 108 struct IteratorPrototypeHasNoReturnProperty final 109 : public PopsOptimizedArrayIteratorPrototypeFuse { 110 virtual const char* name() override { 111 return "IteratorPrototypeHasNoReturnProperty"; 112 } 113 virtual bool checkInvariant(JSContext* cx) override; 114 }; 115 116 struct ArrayIteratorPrototypeHasIteratorProto final 117 : public PopsOptimizedArrayIteratorPrototypeFuse { 118 virtual const char* name() override { 119 return "ArrayIteratorPrototypeHasIteratorProto"; 120 } 121 virtual bool checkInvariant(JSContext* cx) override; 122 }; 123 124 struct IteratorPrototypeHasObjectProto final 125 : public PopsOptimizedArrayIteratorPrototypeFuse { 126 virtual const char* name() override { 127 return "IteratorPrototypeHasObjectProto"; 128 } 129 virtual bool checkInvariant(JSContext* cx) override; 130 }; 131 132 struct ObjectPrototypeHasNoReturnProperty final 133 : public PopsOptimizedArrayIteratorPrototypeFuse { 134 virtual const char* name() override { 135 return "ObjectPrototypeHasNoReturnProperty"; 136 } 137 virtual bool checkInvariant(JSContext* cx) override; 138 }; 139 140 // Fuse used to optimize @@species lookups for arrays. If this fuse is intact, 141 // the following invariants must hold: 142 // 143 // - The builtin `Array.prototype` object has a `constructor` property that's 144 // the builtin `Array` constructor. 145 // - This `Array` constructor has a `Symbol.species` property that's the 146 // original accessor. 147 struct OptimizeArraySpeciesFuse final : public InvalidatingRealmFuse { 148 virtual const char* name() override { return "OptimizeArraySpeciesFuse"; } 149 virtual bool checkInvariant(JSContext* cx) override; 150 virtual void popFuse(JSContext* cx, RealmFuses& realmFuses) override; 151 }; 152 153 // Fuse used to optimize @@species lookups for ArrayBuffers. If this fuse is 154 // intact, the following invariants must hold: 155 // 156 // - The builtin `ArrayBuffer.prototype` object has a `constructor` property 157 // that's the builtin `ArrayBuffer` constructor. 158 // - This `ArrayBuffer` constructor has a `Symbol.species` property that's the 159 // original accessor. 160 struct OptimizeArrayBufferSpeciesFuse final : public RealmFuse { 161 virtual const char* name() override { 162 return "OptimizeArrayBufferSpeciesFuse"; 163 } 164 virtual bool checkInvariant(JSContext* cx) override; 165 }; 166 167 // Fuse used to optimize @@species lookups for SharedArrayBuffers. If this fuse 168 // is intact, the following invariants must hold: 169 // 170 // - The builtin `SharedArrayBuffer.prototype` object has a `constructor` 171 // property that's the builtin `SharedArrayBuffer` constructor. 172 // - This `SharedArrayBuffer` constructor has a `Symbol.species` property that's 173 // the original accessor. 174 struct OptimizeSharedArrayBufferSpeciesFuse final : public RealmFuse { 175 virtual const char* name() override { 176 return "OptimizeSharedArrayBufferSpeciesFuse"; 177 } 178 virtual bool checkInvariant(JSContext* cx) override; 179 }; 180 181 // Fuse used to optimize @@species lookups for TypedArrays. If this fuse is 182 // intact, the following invariants must hold: 183 // 184 // - The builtin `%TypedArray%.prototype` object has a `constructor` property 185 // that's the builtin `%TypedArray%` constructor. 186 // - This `%TypedArray%` constructor has a `Symbol.species` property that's the 187 // original accessor. 188 // - The builtin `<TypedArray>.prototype` object has a `constructor` property 189 // that's the builtin `<TypedArray>` constructor and the prototype of 190 // `<TypedArray>.prototype` is %TypedArray%.prototype. 191 // Where `<TypedArray>` is all concrete built-in TypedArray types: 192 // - Int8Array 193 // - Uint8Array 194 // - Uint8ClampedArray 195 // - Int16Array 196 // - Uint16Array 197 // - Int32Array 198 // - Uint32Array 199 // - BigInt64Array 200 // - BigUint64Array 201 // - Float16Array 202 // - Float32Array 203 // - Float64Array 204 struct OptimizeTypedArraySpeciesFuse final : public InvalidatingRealmFuse { 205 virtual const char* name() override { 206 return "OptimizeTypedArraySpeciesFuse"; 207 } 208 virtual bool checkInvariant(JSContext* cx) override; 209 }; 210 211 // Fuse used to optimize various property lookups for promises. If this fuse is 212 // intact, the following invariants must hold: 213 // 214 // - The builtin `Promise.prototype` object has unchanged `constructor` and 215 // `then` properties. 216 // - The builtin `Promise` constructor has unchanged `Symbol.species` and 217 // `resolve` properties. 218 struct OptimizePromiseLookupFuse final : public RealmFuse { 219 virtual const char* name() override { return "OptimizePromiseLookupFuse"; } 220 virtual bool checkInvariant(JSContext* cx) override; 221 virtual void popFuse(JSContext* cx, RealmFuses& realmFuses) override; 222 }; 223 224 // Fuse used to guard against changes to various properties on RegExp.prototype. 225 // 226 // If this fuse is intact, RegExp.prototype must have the following original 227 // getter properties: 228 // - .flags ($RegExpFlagsGetter) 229 // - .global (regexp_global) 230 // - .hasIndices (regexp_hasIndices) 231 // - .ignoreCase (regexp_ignoreCase) 232 // - .multiline (regexp_multiline) 233 // - .sticky (regexp_sticky) 234 // - .unicode (regexp_unicode) 235 // - .unicodeSets (regexp_unicodeSets) 236 // - .dotAll (regexp_dotAll) 237 // 238 // And the following unchanged data properties: 239 // - .exec (RegExp_prototype_Exec) 240 // - [@@match] (RegExpMatch) 241 // - [@@matchAll] (RegExpMatchAll) 242 // - [@@replace] (RegExpReplace) 243 // - [@@search] (RegExpSearch) 244 // - [@@split] (RegExpSplit) 245 struct OptimizeRegExpPrototypeFuse final : public InvalidatingRealmFuse { 246 virtual const char* name() override { return "OptimizeRegExpPrototypeFuse"; } 247 virtual bool checkInvariant(JSContext* cx) override; 248 }; 249 250 // Guard used to optimize iterating over Map objects. If this fuse is intact, 251 // the following invariants must hold: 252 // 253 // - The builtin `Map.prototype` object has a `Symbol.iterator` property that's 254 // the original `%Map.prototype.entries%` function. 255 // - The builtin `%MapIteratorPrototype%` object has a `next` property that's 256 // the original `MapIteratorNext` self-hosted function. 257 // 258 // Note: because this doesn't guard against `return` properties on the iterator 259 // prototype, this should only be used in places where we don't have to call 260 // `IteratorClose`. 261 struct OptimizeMapObjectIteratorFuse final : public RealmFuse { 262 virtual const char* name() override { 263 return "OptimizeMapObjectIteratorFuse"; 264 } 265 virtual bool checkInvariant(JSContext* cx) override; 266 }; 267 268 // Guard used to optimize iterating over Set objects. If this fuse is intact, 269 // the following invariants must hold: 270 // 271 // - The builtin `Set.prototype` object has a `Symbol.iterator` property that's 272 // the original `%Set.prototype.values%` function. 273 // - The builtin `%SetIteratorPrototype%` object has a `next` property that's 274 // the original `SetIteratorNext` self-hosted function. 275 // 276 // Note: because this doesn't guard against `return` properties on the iterator 277 // prototype, this should only be used in places where we don't have to call 278 // `IteratorClose`. 279 struct OptimizeSetObjectIteratorFuse final : public RealmFuse { 280 virtual const char* name() override { 281 return "OptimizeSetObjectIteratorFuse"; 282 } 283 virtual bool checkInvariant(JSContext* cx) override; 284 }; 285 286 // This fuse is popped when the `Map.prototype.set` property is mutated. 287 struct OptimizeMapPrototypeSetFuse final : public RealmFuse { 288 virtual const char* name() override { return "OptimizeMapPrototypeSetFuse"; } 289 virtual bool checkInvariant(JSContext* cx) override; 290 }; 291 292 // This fuse is popped when the `Set.prototype.add` property is mutated. 293 struct OptimizeSetPrototypeAddFuse final : public RealmFuse { 294 virtual const char* name() override { return "OptimizeSetPrototypeAddFuse"; } 295 virtual bool checkInvariant(JSContext* cx) override; 296 }; 297 298 // This fuse is popped when the `WeakMap.prototype.set` property is mutated. 299 struct OptimizeWeakMapPrototypeSetFuse final : public RealmFuse { 300 virtual const char* name() override { 301 return "OptimizeWeakMapPrototypeSetFuse"; 302 } 303 virtual bool checkInvariant(JSContext* cx) override; 304 }; 305 306 // This fuse is popped when the `WeakSet.prototype.add` property is mutated. 307 struct OptimizeWeakSetPrototypeAddFuse final : public RealmFuse { 308 virtual const char* name() override { 309 return "OptimizeWeakSetPrototypeAddFuse"; 310 } 311 virtual bool checkInvariant(JSContext* cx) override; 312 }; 313 314 #define FOR_EACH_REALM_FUSE(FUSE) \ 315 FUSE(OptimizeGetIteratorFuse, optimizeGetIteratorFuse) \ 316 FUSE(OptimizeArrayIteratorPrototypeFuse, optimizeArrayIteratorPrototypeFuse) \ 317 FUSE(ArrayPrototypeIteratorFuse, arrayPrototypeIteratorFuse) \ 318 FUSE(ArrayPrototypeIteratorNextFuse, arrayPrototypeIteratorNextFuse) \ 319 FUSE(ArrayIteratorPrototypeHasNoReturnProperty, \ 320 arrayIteratorPrototypeHasNoReturnProperty) \ 321 FUSE(IteratorPrototypeHasNoReturnProperty, \ 322 iteratorPrototypeHasNoReturnProperty) \ 323 FUSE(ArrayIteratorPrototypeHasIteratorProto, \ 324 arrayIteratorPrototypeHasIteratorProto) \ 325 FUSE(IteratorPrototypeHasObjectProto, iteratorPrototypeHasObjectProto) \ 326 FUSE(ObjectPrototypeHasNoReturnProperty, objectPrototypeHasNoReturnProperty) \ 327 FUSE(OptimizeArraySpeciesFuse, optimizeArraySpeciesFuse) \ 328 FUSE(OptimizeArrayBufferSpeciesFuse, optimizeArrayBufferSpeciesFuse) \ 329 FUSE(OptimizeSharedArrayBufferSpeciesFuse, \ 330 optimizeSharedArrayBufferSpeciesFuse) \ 331 FUSE(OptimizeTypedArraySpeciesFuse, optimizeTypedArraySpeciesFuse) \ 332 FUSE(OptimizePromiseLookupFuse, optimizePromiseLookupFuse) \ 333 FUSE(OptimizeRegExpPrototypeFuse, optimizeRegExpPrototypeFuse) \ 334 FUSE(OptimizeMapObjectIteratorFuse, optimizeMapObjectIteratorFuse) \ 335 FUSE(OptimizeSetObjectIteratorFuse, optimizeSetObjectIteratorFuse) \ 336 FUSE(OptimizeMapPrototypeSetFuse, optimizeMapPrototypeSetFuse) \ 337 FUSE(OptimizeSetPrototypeAddFuse, optimizeSetPrototypeAddFuse) \ 338 FUSE(OptimizeWeakMapPrototypeSetFuse, optimizeWeakMapPrototypeSetFuse) \ 339 FUSE(OptimizeWeakSetPrototypeAddFuse, optimizeWeakSetPrototypeAddFuse) 340 341 struct RealmFuses { 342 RealmFuses() = default; 343 344 #define FUSE(Name, LowerName) Name LowerName{}; 345 FOR_EACH_REALM_FUSE(FUSE) 346 #undef FUSE 347 348 void assertInvariants(JSContext* cx) { 349 // Generate the invariant checking calls. 350 #define FUSE(Name, LowerName) LowerName.assertInvariant(cx); 351 FOR_EACH_REALM_FUSE(FUSE) 352 #undef FUSE 353 } 354 355 // Code Generation Code: 356 enum class FuseIndex : uint8_t { 357 // Generate Fuse Indexes 358 #define FUSE(Name, LowerName) Name, 359 FOR_EACH_REALM_FUSE(FUSE) 360 #undef FUSE 361 LastFuseIndex 362 }; 363 364 GuardFuse* getFuseByIndex(FuseIndex index) { 365 switch (index) { 366 // Return fuses. 367 #define FUSE(Name, LowerName) \ 368 case FuseIndex::Name: \ 369 return &this->LowerName; 370 FOR_EACH_REALM_FUSE(FUSE) 371 #undef FUSE 372 default: 373 break; 374 } 375 MOZ_CRASH("Fuse Not Found"); 376 } 377 378 DependentIonScriptGroup fuseDependencies; 379 380 static int32_t fuseOffsets[]; 381 static const char* fuseNames[]; 382 383 static int32_t offsetOfFuseWordRelativeToRealm(FuseIndex index); 384 static const char* getFuseName(FuseIndex index); 385 386 #ifdef DEBUG 387 static bool isInvalidatingFuse(FuseIndex index) { 388 switch (index) { 389 # define FUSE(Name, LowerName) \ 390 case FuseIndex::Name: \ 391 static_assert(std::is_base_of_v<RealmFuse, Name> || \ 392 std::is_base_of_v<InvalidatingRealmFuse, Name>); \ 393 return std::is_base_of_v<InvalidatingRealmFuse, Name>; 394 FOR_EACH_REALM_FUSE(FUSE) 395 # undef FUSE 396 default: 397 break; 398 } 399 MOZ_CRASH("Fuse Not Found"); 400 } 401 #endif 402 }; 403 404 } // namespace js 405 406 #endif