diff options
Diffstat (limited to 'js/src/devtools/rootAnalysis/t/virtual')
-rw-r--r-- | js/src/devtools/rootAnalysis/t/virtual/source.cpp | 292 | ||||
-rw-r--r-- | js/src/devtools/rootAnalysis/t/virtual/test.py | 91 |
2 files changed, 383 insertions, 0 deletions
diff --git a/js/src/devtools/rootAnalysis/t/virtual/source.cpp b/js/src/devtools/rootAnalysis/t/virtual/source.cpp new file mode 100644 index 0000000000..83633a3436 --- /dev/null +++ b/js/src/devtools/rootAnalysis/t/virtual/source.cpp @@ -0,0 +1,292 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#define ANNOTATE(property) __attribute__((annotate(property))) + +extern void GC() ANNOTATE("GC Call"); + +void GC() { + // If the implementation is too trivial, the function body won't be emitted at + // all. + asm(""); +} + +// Special-cased function -- code that can run JS has an artificial edge to +// js::RunScript. +namespace js { +void RunScript() { GC(); } +} // namespace js + +struct Cell { + int f; +} ANNOTATE("GC Thing"); + +extern void foo(); + +void bar() { GC(); } + +typedef void (*func_t)(); + +class Base { + public: + int ANNOTATE("field annotation") dummy; + virtual void someGC() ANNOTATE("Base pure virtual method") = 0; + virtual void someGC(int) ANNOTATE("overloaded Base pure virtual method") = 0; + virtual void sibGC() = 0; + virtual void onBase() { bar(); } + func_t functionField; + + // For now, this is just to verify that the plugin doesn't crash. The + // analysis code does not yet look at this annotation or output it anywhere + // (though it *is* being recorded.) + static float testAnnotations() ANNOTATE("static func"); + + // Similar, though sixgill currently completely ignores parameter annotations. + static double testParamAnnotations(Cell& ANNOTATE("param annotation") + ANNOTATE("second param annot") cell) + ANNOTATE("static func") ANNOTATE("second func"); +}; + +float Base::testAnnotations() { + asm(""); + return 1.1; +} + +double Base::testParamAnnotations(Cell& cell) { + asm(""); + return 1.2; +} + +class Super : public Base { + public: + virtual void ANNOTATE("Super pure virtual") noneGC() = 0; + virtual void allGC() = 0; + virtual void onSuper() { asm(""); } + void nonVirtualFunc() { asm(""); } +}; + +class Sub1 : public Super { + public: + void noneGC() override { foo(); } + void someGC() override ANNOTATE("Sub1 override") ANNOTATE("second attr") { + foo(); + } + void someGC(int) override ANNOTATE("Sub1 override for int overload") { + foo(); + } + void allGC() override { + foo(); + bar(); + } + void sibGC() override { foo(); } + void onBase() override { foo(); } +} ANNOTATE("CSU1") ANNOTATE("CSU2"); + +class Sub2 : public Super { + public: + void noneGC() override { foo(); } + void someGC() override { + foo(); + bar(); + } + void someGC(int) override { + foo(); + bar(); + } + void allGC() override { + foo(); + bar(); + } + void sibGC() override { foo(); } +}; + +class Sibling : public Base { + public: + virtual void noneGC() { foo(); } + void someGC() override { + foo(); + bar(); + } + void someGC(int) override { + foo(); + bar(); + } + virtual void allGC() { + foo(); + bar(); + } + void sibGC() override { bar(); } +}; + +class AutoSuppressGC { + public: + AutoSuppressGC() {} + ~AutoSuppressGC() {} +} ANNOTATE("Suppress GC"); + +void use(Cell*) { asm(""); } + +class nsISupports { + public: + virtual ANNOTATE("Can run script") void danger() { asm(""); } + + virtual ~nsISupports() = 0; +}; + +class nsIPrincipal : public nsISupports { + public: + ~nsIPrincipal() override{}; +}; + +struct JSPrincipals { + int debugToken; + JSPrincipals() = default; + virtual ~JSPrincipals() { GC(); } +}; + +class nsJSPrincipals : public nsIPrincipal, public JSPrincipals { + public: + void Release() { delete this; } +}; + +class SafePrincipals : public nsIPrincipal { + public: + ~SafePrincipals() { foo(); } +}; + +void f() { + Sub1 s1; + Sub2 s2; + + static Cell cell; + { + Cell* c1 = &cell; + s1.noneGC(); + use(c1); + } + { + Cell* c2 = &cell; + s2.someGC(); + use(c2); + } + { + Cell* c3 = &cell; + s1.allGC(); + use(c3); + } + { + Cell* c4 = &cell; + s2.noneGC(); + use(c4); + } + { + Cell* c5 = &cell; + s2.someGC(); + use(c5); + } + { + Cell* c6 = &cell; + s2.allGC(); + use(c6); + } + + Super* super = &s2; + { + Cell* c7 = &cell; + super->noneGC(); + use(c7); + } + { + Cell* c8 = &cell; + super->someGC(); + use(c8); + } + { + Cell* c9 = &cell; + super->allGC(); + use(c9); + } + + { + Cell* c10 = &cell; + s1.functionField(); + use(c10); + } + { + Cell* c11 = &cell; + super->functionField(); + use(c11); + } + { + Cell* c12 = &cell; + super->sibGC(); + use(c12); + } + + Base* base = &s2; + { + Cell* c13 = &cell; + base->sibGC(); + use(c13); + } + + nsJSPrincipals pals; + { + Cell* c14 = &cell; + nsISupports* p = &pals; + p->danger(); + use(c14); + } + + // Base defines, Sub1 overrides, static Super can call either. + { + Cell* c15 = &cell; + super->onBase(); + use(c15); + } + + { + Cell* c16 = &cell; + s2.someGC(7); + use(c16); + } + + { + Cell* c17 = &cell; + super->someGC(7); + use(c17); + } + + { + nsJSPrincipals* princ = new nsJSPrincipals(); + Cell* c18 = &cell; + delete princ; // Can GC + use(c18); + } + + { + nsJSPrincipals* princ = new nsJSPrincipals(); + nsISupports* supp = static_cast<nsISupports*>(princ); + Cell* c19 = &cell; + delete supp; // Can GC + use(c19); + } + + { + auto* safe = new SafePrincipals(); + Cell* c20 = &cell; + delete safe; // Cannot GC + use(c20); + } + + { + auto* safe = new SafePrincipals(); + nsISupports* supp = static_cast<nsISupports*>(safe); + Cell* c21 = &cell; + delete supp; // Compiler thinks destructor can GC. + use(c21); + } +} diff --git a/js/src/devtools/rootAnalysis/t/virtual/test.py b/js/src/devtools/rootAnalysis/t/virtual/test.py new file mode 100644 index 0000000000..e8474ae28b --- /dev/null +++ b/js/src/devtools/rootAnalysis/t/virtual/test.py @@ -0,0 +1,91 @@ +# 'test' is provided by the calling script. +# flake8: noqa: F821 + +test.compile("source.cpp") +test.run_analysis_script("gcTypes") + +info = test.load_typeInfo() + +assert "Sub1" in info["OtherCSUTags"] +assert ["CSU1", "CSU2"] == sorted(info["OtherCSUTags"]["Sub1"]) +assert "Base" in info["OtherFieldTags"] +assert "someGC" in info["OtherFieldTags"]["Base"] +assert "Sub1" in info["OtherFieldTags"] +assert "someGC" in info["OtherFieldTags"]["Sub1"] + +# For now, fields with the same name (eg overloaded virtual methods) just +# accumulate attributes. +assert ["Sub1 override", "Sub1 override for int overload", "second attr"] == sorted( + info["OtherFieldTags"]["Sub1"]["someGC"] +) + +gcFunctions = test.load_gcFunctions() + +assert "void Sub1::noneGC()" not in gcFunctions +assert "void Sub1::someGC()" not in gcFunctions +assert "void Sub1::someGC(int32)" not in gcFunctions +assert "void Sub1::allGC()" in gcFunctions +assert "void Sub2::noneGC()" not in gcFunctions +assert "void Sub2::someGC()" in gcFunctions +assert "void Sub2::someGC(int32)" in gcFunctions +assert "void Sub2::allGC()" in gcFunctions + +callgraph = test.load_callgraph() + +assert callgraph.calleeGraph["void f()"]["Super.noneGC:0"] +assert callgraph.calleeGraph["Super.noneGC:0"]["Sub1.noneGC:0"] +assert callgraph.calleeGraph["Super.noneGC:0"]["Sub2.noneGC:0"] +assert callgraph.calleeGraph["Sub1.noneGC:0"]["void Sub1::noneGC()"] +assert callgraph.calleeGraph["Sub2.noneGC:0"]["void Sub2::noneGC()"] +assert "void Sibling::noneGC()" not in callgraph.calleeGraph["Super.noneGC:0"] +assert callgraph.calleeGraph["Super.onBase:0"]["Sub1.onBase:0"] +assert callgraph.calleeGraph["Sub1.onBase:0"]["void Sub1::onBase()"] +assert callgraph.calleeGraph["Super.onBase:0"]["void Base::onBase()"] +assert "void Sibling::onBase()" not in callgraph.calleeGraph["Super.onBase:0"] + +hazards = test.load_hazards() +hazmap = {haz.variable: haz for haz in hazards} + +assert "c1" not in hazmap +assert "c2" in hazmap +assert "c3" in hazmap +assert "c4" not in hazmap +assert "c5" in hazmap +assert "c6" in hazmap +assert "c7" not in hazmap +assert "c8" in hazmap +assert "c9" in hazmap +assert "c10" in hazmap +assert "c11" in hazmap + +# Virtual resolution should take the static type into account: the only method +# implementations considered should be those of descendants, even if the +# virtual method is inherited and not overridden in the static class. (Base +# defines sibGC() as pure virtual, Super inherits it without overriding, +# Sibling and Sub2 both implement it.) + +# Call Base.sibGC on a Super pointer: can only call Sub2.sibGC(), which does not GC. +# In particular, PEdgeCallInstance.Exp.Field.FieldCSU.Type = {Kind: "CSU", Name="Super"} +assert "c12" not in hazmap +# Call Base.sibGC on a Base pointer; can call Sibling.sibGC(), which GCs. +assert "c13" in hazmap + +# Call nsISupports.danger() which is annotated to be overridable and hence can GC. +assert "c14" in hazmap + +# someGC(int) overload +assert "c16" in hazmap +assert "c17" in hazmap + +# Super.onBase() could call the GC'ing Base::onBase(). +assert "c15" in hazmap + +# virtual ~nsJSPrincipals calls ~JSPrincipals calls GC. +assert "c18" in hazmap +assert "c19" in hazmap + +# ~SafePrincipals does not GC. +assert "c20" not in hazmap + +# ...but when cast to a nsISupports*, the compiler can't tell that it won't. +assert "c21" in hazmap |