source.cpp (13251B)
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 #include <utility> 8 9 #define ANNOTATE(property) __attribute__((annotate(property))) 10 11 // MarkVariableAsGCSafe is a magic function name used as an 12 // explicit annotation. 13 14 namespace JS { 15 namespace detail { 16 template <typename T> 17 static void MarkVariableAsGCSafe(T&) { 18 asm(""); 19 } 20 } // namespace detail 21 } // namespace JS 22 23 #define JS_HAZ_VARIABLE_IS_GC_SAFE(var) JS::detail::MarkVariableAsGCSafe(var) 24 25 struct Cell { 26 int f; 27 } ANNOTATE("GC Thing"); 28 29 template <typename T, typename U> 30 struct UntypedContainer { 31 char data[sizeof(T) + sizeof(U)]; 32 } ANNOTATE("moz_inherit_type_annotations_from_template_args"); 33 34 struct RootedCell { 35 RootedCell(Cell*) {} 36 } ANNOTATE("Rooted Pointer"); 37 38 class AutoSuppressGC_Base { 39 public: 40 AutoSuppressGC_Base() {} 41 ~AutoSuppressGC_Base() {} 42 } ANNOTATE("Suppress GC"); 43 44 class AutoSuppressGC_Child : public AutoSuppressGC_Base { 45 public: 46 AutoSuppressGC_Child() : AutoSuppressGC_Base() {} 47 }; 48 49 class AutoSuppressGC { 50 AutoSuppressGC_Child helpImBeingSuppressed; 51 52 public: 53 AutoSuppressGC() {} 54 }; 55 56 class AutoCheckCannotGC { 57 public: 58 AutoCheckCannotGC() {} 59 ~AutoCheckCannotGC() { asm(""); } 60 } ANNOTATE("Invalidated by GC"); 61 62 extern void GC() ANNOTATE("GC Call"); 63 extern void invisible(); 64 65 void GC() { 66 // If the implementation is too trivial, the function body won't be emitted at 67 // all. 68 asm(""); 69 invisible(); 70 } 71 72 extern Cell* makecell(); 73 74 extern void usecell(Cell*); 75 76 extern bool flipcoin(); 77 78 void suppressedFunction() { 79 GC(); // Calls GC, but is always called within AutoSuppressGC 80 } 81 82 void halfSuppressedFunction() { 83 GC(); // Calls GC, but is sometimes called within AutoSuppressGC 84 } 85 86 void unsuppressedFunction() { 87 GC(); // Calls GC, never within AutoSuppressGC 88 } 89 90 class IDL_Interface { 91 public: 92 ANNOTATE("Can run script") virtual void canScriptThis() {} 93 virtual void cannotScriptThis() {} 94 ANNOTATE("Can run script") virtual void overridden_canScriptThis() = 0; 95 virtual void overridden_cannotScriptThis() = 0; 96 }; 97 98 class IDL_Subclass : public IDL_Interface { 99 ANNOTATE("Can run script") void overridden_canScriptThis() override {} 100 void overridden_cannotScriptThis() override {} 101 }; 102 103 volatile static int x = 3; 104 volatile static int* xp = &x; 105 struct GCInDestructor { 106 ~GCInDestructor() { 107 invisible(); 108 asm(""); 109 *xp = 4; 110 GC(); 111 } 112 }; 113 114 template <typename T> 115 void usecontainer(T* value) { 116 if (value) asm(""); 117 } 118 119 Cell* cell() { 120 static Cell c; 121 return &c; 122 } 123 124 Cell* f() { 125 GCInDestructor kaboom; 126 127 Cell* cell1 = cell(); 128 Cell* cell2 = cell(); 129 Cell* cell3 = cell(); 130 Cell* cell4 = cell(); 131 { 132 AutoSuppressGC nogc; 133 suppressedFunction(); 134 halfSuppressedFunction(); 135 } 136 usecell(cell1); 137 halfSuppressedFunction(); 138 usecell(cell2); 139 unsuppressedFunction(); 140 { 141 // Old bug: it would look from the first AutoSuppressGC constructor it 142 // found to the last destructor. This statement *should* have no effect. 143 AutoSuppressGC nogc; 144 } 145 usecell(cell3); 146 Cell* cell5 = cell(); 147 usecell(cell5); 148 149 { 150 // Templatized container that inherits attributes from Cell*, should 151 // report a hazard. 152 UntypedContainer<int, Cell*> container1; 153 usecontainer(&container1); 154 GC(); 155 usecontainer(&container1); 156 } 157 158 { 159 // As above, but with a non-GC type. 160 UntypedContainer<int, double> container2; 161 usecontainer(&container2); 162 GC(); 163 usecontainer(&container2); 164 } 165 166 // Hazard in return value due to ~GCInDestructor 167 Cell* cell6 = cell(); 168 return cell6; 169 } 170 171 Cell* copy_and_gc(Cell* src) { 172 GC(); 173 return reinterpret_cast<Cell*>(88); 174 } 175 176 void use(Cell* cell) { 177 static int x = 0; 178 if (cell) x++; 179 } 180 181 struct CellContainer { 182 Cell* cell; 183 CellContainer() { asm(""); } 184 }; 185 186 void loopy() { 187 Cell cell; 188 189 // No hazard: haz1 is not live during call to copy_and_gc. 190 Cell* haz1; 191 for (int i = 0; i < 10; i++) { 192 haz1 = copy_and_gc(haz1); 193 } 194 195 // No hazard: haz2 is live up to just before the GC, and starting at the 196 // next statement after it, but not across the GC. 197 Cell* haz2 = &cell; 198 for (int j = 0; j < 10; j++) { 199 use(haz2); 200 GC(); 201 haz2 = &cell; 202 } 203 204 // Hazard: haz3 is live from the final statement in one iteration, across 205 // the GC in the next, to the use in the 2nd statement. 206 Cell* haz3; 207 for (int k = 0; k < 10; k++) { 208 GC(); 209 use(haz3); 210 haz3 = &cell; 211 } 212 213 // Hazard: haz4 is live across a GC hidden in a loop. 214 Cell* haz4 = &cell; 215 for (int i2 = 0; i2 < 10; i2++) { 216 GC(); 217 } 218 use(haz4); 219 220 // Hazard: haz5 is live from within a loop across a GC. 221 Cell* haz5; 222 for (int i3 = 0; i3 < 10; i3++) { 223 haz5 = &cell; 224 } 225 GC(); 226 use(haz5); 227 228 // No hazard: similar to the haz3 case, but verifying that we do not get 229 // into an infinite loop. 230 Cell* haz6; 231 for (int i4 = 0; i4 < 10; i4++) { 232 GC(); 233 haz6 = &cell; 234 } 235 236 // No hazard: haz7 is constructed within the body, so it can't make a 237 // hazard across iterations. Note that this requires CellContainer to have 238 // a constructor, because otherwise the analysis doesn't see where 239 // variables are declared. (With the constructor, it knows that 240 // construction of haz7 obliterates any previous value it might have had. 241 // Not that that's possible given its scope, but the analysis doesn't get 242 // that information.) 243 for (int i5 = 0; i5 < 10; i5++) { 244 GC(); 245 CellContainer haz7; 246 use(haz7.cell); 247 haz7.cell = &cell; 248 } 249 250 // Hazard: make sure we *can* see hazards across iterations involving 251 // CellContainer; 252 CellContainer haz8; 253 for (int i6 = 0; i6 < 10; i6++) { 254 GC(); 255 use(haz8.cell); 256 haz8.cell = &cell; 257 } 258 } 259 260 namespace mozilla { 261 template <typename T> 262 class UniquePtr { 263 T* val; 264 265 public: 266 UniquePtr() : val(nullptr) { asm(""); } 267 UniquePtr(T* p) : val(p) {} 268 UniquePtr(UniquePtr<T>&& u) : val(u.val) { u.val = nullptr; } 269 ~UniquePtr() { use(val); } 270 T* get() { return val; } 271 void reset() { val = nullptr; } 272 } ANNOTATE("moz_inherit_type_annotations_from_template_args"); 273 } // namespace mozilla 274 275 extern void consume(mozilla::UniquePtr<Cell> uptr); 276 277 void safevals() { 278 Cell cell; 279 280 // Simple hazard. 281 Cell* unsafe1 = &cell; 282 GC(); 283 use(unsafe1); 284 285 // Safe because it's known to be nullptr. 286 Cell* safe2 = &cell; 287 safe2 = nullptr; 288 GC(); 289 use(safe2); 290 291 // Unsafe because it may not be nullptr. 292 Cell* unsafe3 = &cell; 293 if (reinterpret_cast<long>(&cell) & 0x100) { 294 unsafe3 = nullptr; 295 } 296 GC(); 297 use(unsafe3); 298 299 // Unsafe because it's not nullptr anymore. 300 Cell* unsafe3b = &cell; 301 unsafe3b = nullptr; 302 unsafe3b = &cell; 303 GC(); 304 use(unsafe3b); 305 306 // Hazard involving UniquePtr. 307 { 308 mozilla::UniquePtr<Cell> unsafe4(&cell); 309 GC(); 310 // Destructor uses unsafe4. 311 } 312 313 // reset() to safe value before the GC. 314 { 315 mozilla::UniquePtr<Cell> safe5(&cell); 316 safe5.reset(); 317 GC(); 318 } 319 320 // reset() to safe value after the GC. 321 { 322 mozilla::UniquePtr<Cell> safe6(&cell); 323 GC(); 324 safe6.reset(); 325 } 326 327 // reset() to safe value after the GC -- but we've already used it, so it's 328 // too late. 329 { 330 mozilla::UniquePtr<Cell> unsafe7(&cell); 331 GC(); 332 use(unsafe7.get()); 333 unsafe7.reset(); 334 } 335 336 // initialized to safe value. 337 { 338 mozilla::UniquePtr<Cell> safe8; 339 GC(); 340 } 341 342 // passed to a function that takes ownership before GC. 343 { 344 mozilla::UniquePtr<Cell> safe9(&cell); 345 consume(std::move(safe9)); 346 GC(); 347 } 348 349 // passed to a function that takes ownership after GC. 350 { 351 mozilla::UniquePtr<Cell> unsafe10(&cell); 352 GC(); 353 consume(std::move(unsafe10)); 354 } 355 356 // annotated to be safe before the GC. (This doesn't make 357 // a lot of sense here; the annotation is for when some 358 // type is known to only contain safe values, eg it is 359 // initialized as empty, or it is a union and we know 360 // that the GC pointer variants are not in use.) 361 { 362 mozilla::UniquePtr<Cell> safe11(&cell); 363 JS_HAZ_VARIABLE_IS_GC_SAFE(safe11); 364 GC(); 365 } 366 367 // annotate as safe value after the GC -- since nothing else 368 // has touched the variable, that means it was already safe 369 // during the GC. 370 { 371 mozilla::UniquePtr<Cell> safe12(&cell); 372 GC(); 373 JS_HAZ_VARIABLE_IS_GC_SAFE(safe12); 374 } 375 376 // annotate as safe after the GC -- but we've already used it, so it's 377 // too late. 378 { 379 mozilla::UniquePtr<Cell> unsafe13(&cell); 380 GC(); 381 use(unsafe13.get()); 382 JS_HAZ_VARIABLE_IS_GC_SAFE(unsafe13); 383 } 384 385 // Check JS_HAZ_CAN_RUN_SCRIPT annotation handling. 386 IDL_Subclass sub; 387 IDL_Subclass* subp = ⊂ 388 IDL_Interface* base = ⊂ 389 { 390 Cell* unsafe14 = &cell; 391 base->canScriptThis(); 392 use(unsafe14); 393 } 394 { 395 Cell* unsafe15 = &cell; 396 subp->canScriptThis(); 397 use(unsafe15); 398 } 399 { 400 // Almost the same as the last one, except call using the actual object, not 401 // a pointer. The type is known, so there is no danger of the actual type 402 // being a subclass that has overridden the method with an implementation 403 // that calls script. 404 Cell* safe16 = &cell; 405 sub.canScriptThis(); 406 use(safe16); 407 } 408 { 409 Cell* safe17 = &cell; 410 base->cannotScriptThis(); 411 use(safe17); 412 } 413 { 414 Cell* safe18 = &cell; 415 subp->cannotScriptThis(); 416 use(safe18); 417 } 418 { 419 // A use after a GC, but not before. (This does not initialize safe19 by 420 // setting it to a value, because assignment would start its live range, and 421 // this test is to see if a variable with no known live range start requires 422 // a use before the GC or not. It should.) 423 Cell* safe19; 424 GC(); 425 extern void initCellPtr(Cell**); 426 initCellPtr(&safe19); 427 } 428 } 429 430 // Make sure `this` is live at the beginning of a function. 431 class Subcell : public Cell { 432 int method() { 433 GC(); 434 return f; // this->f 435 } 436 }; 437 438 template <typename T> 439 struct RefPtr { 440 ~RefPtr() { GC(); } 441 bool forget() { return true; } 442 bool use() { return true; } 443 void assign_with_AddRef(T* aRawPtr) { asm(""); } 444 }; 445 446 extern bool flipcoin(); 447 448 Cell* refptr_test1() { 449 static Cell cell; 450 RefPtr<float> v1; 451 Cell* ref_unsafe1 = &cell; 452 return ref_unsafe1; 453 } 454 455 Cell* refptr_test2() { 456 static Cell cell; 457 RefPtr<float> v2; 458 Cell* ref_safe2 = &cell; 459 v2.forget(); 460 return ref_safe2; 461 } 462 463 Cell* refptr_test3() { 464 static Cell cell; 465 RefPtr<float> v3; 466 Cell* ref_unsafe3 = &cell; 467 if (x) { 468 v3.forget(); 469 } 470 return ref_unsafe3; 471 } 472 473 Cell* refptr_test4() { 474 static Cell cell; 475 RefPtr<int> r; 476 return &cell; // hazard in return value 477 } 478 479 Cell* refptr_test5() { 480 static Cell cell; 481 RefPtr<int> r; 482 return nullptr; // returning immobile value, so no hazard 483 } 484 485 float somefloat = 1.2; 486 487 Cell* refptr_test6() { 488 static Cell cell; 489 RefPtr<float> v6; 490 Cell* ref_unsafe6 = &cell; 491 // v6 can be used without an intervening forget() before the end of the 492 // function, even though forget() will be called at least once. 493 v6.forget(); 494 if (x) { 495 v6.forget(); 496 v6.assign_with_AddRef(&somefloat); 497 } 498 return ref_unsafe6; 499 } 500 501 Cell* refptr_test7() { 502 static Cell cell; 503 RefPtr<float> v7; 504 Cell* ref_unsafe7 = &cell; 505 // Similar to above, but with a loop. 506 while (flipcoin()) { 507 v7.forget(); 508 v7.assign_with_AddRef(&somefloat); 509 } 510 return ref_unsafe7; 511 } 512 513 Cell* refptr_test8() { 514 static Cell cell; 515 RefPtr<float> v8; 516 Cell* ref_unsafe8 = &cell; 517 // If the loop is traversed, forget() will be called. But that doesn't 518 // matter, because even on the last iteration v8.use() will have been called 519 // (and potentially dropped the refcount or whatever.) 520 while (v8.use()) { 521 v8.forget(); 522 } 523 return ref_unsafe8; 524 } 525 526 Cell* refptr_test9() { 527 static Cell cell; 528 RefPtr<float> v9; 529 Cell* ref_safe9 = &cell; 530 // Even when not going through the loop, forget() will be called and so the 531 // dtor will not Release. 532 while (v9.forget()) { 533 v9.assign_with_AddRef(&somefloat); 534 } 535 return ref_safe9; 536 } 537 538 Cell* refptr_test10() { 539 static Cell cell; 540 RefPtr<float> v10; 541 Cell* ref_unsafe10 = &cell; 542 // The destructor has a backwards path that skips the loop body. 543 v10.assign_with_AddRef(&somefloat); 544 while (flipcoin()) { 545 v10.forget(); 546 } 547 return ref_unsafe10; 548 } 549 550 std::pair<bool, AutoCheckCannotGC> pair_returning_function() { 551 return std::make_pair(true, AutoCheckCannotGC()); 552 } 553 554 void aggr_init_unsafe() { 555 // nogc will be live after the call, so across the GC. 556 auto [ok, nogc] = pair_returning_function(); 557 GC(); 558 } 559 560 void aggr_init_safe() { 561 // The analysis should be able to tell that nogc is only live after the call, 562 // not before. (This is to check for a problem where the return value was 563 // getting stored into a different temporary than the local nogc variable, 564 // and so its initialization was never seen and so it was assumed to be live 565 // throughout the function.) 566 GC(); 567 auto [ok, nogc] = pair_returning_function(); 568 } 569 570 void stack_array() { 571 Cell* array[] = {makecell(), makecell()}; 572 Cell* array2[] = {makecell(), makecell()}; 573 GC(); 574 usecell(array[1]); 575 // Never use array2. 576 }