Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Track static class fields and improve handling of class getters/setters
This aims to improve tree-shaking of code that uses static class properties (#3989) and to improve detection of side effects through class getters/setters (#4016). The first part works by keeping a map of positively known static properties (methods and simple getters) in `ClassNode.staticPropertyMap`, along with a flag (`ClassNode.deoptimizedStatic`) that indicates that something happened that removed our confidence that we know anything about the class object. Access and calls to these known static properties are handled by routing the calls to `getLiteralValueAtPath`, `getReturnExpressionWhenCalledAtPath`, and `hasEffectsWhenCalledAtPath` to the known values in the properties. In contrast to `ObjectExpression`, this class does not try to keep track of multiple expressions associated with a property, since that doesn't come up a lot on classes. The handling of side effect detection through getters and setters is done by, _if_ the entire class object (or its prototype in case of access to the prototype) hasn't been deoptimized, scanning through the directly defined getters and setters to see if one exists (calling through to superclasses as appropriate). I believe that this is solid because any code that would be able to change the set of getters and setters on a class would cause the entire object to be deoptimized.
- Loading branch information
Showing
10 changed files
with
428 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
module.exports = { | ||
description: 'tracks literal values in class static fields' | ||
}; |
13 changes: 13 additions & 0 deletions
13
test/form/samples/literals-from-class-statics/_expected.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
log("t"); | ||
log("x"); | ||
|
||
class Undef { | ||
static y; | ||
} | ||
if (Undef.y) log("y"); | ||
|
||
class Deopt { | ||
static z = false; | ||
} | ||
unknown(Deopt); | ||
if (Deopt.z) log("z"); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
class Static { | ||
static t() { return true; } | ||
static f() { return false; } | ||
static x = 10; | ||
} | ||
|
||
if (Static.t()) log("t"); | ||
if (Static.f()) log("f"); | ||
if (!Static.t()) log("!t"); | ||
if (Static.x) log("x"); | ||
|
||
class Undef { | ||
static y; | ||
} | ||
if (Undef.y) log("y"); | ||
|
||
class Deopt { | ||
static z = false; | ||
} | ||
unknown(Deopt); | ||
if (Deopt.z) log("z"); |
3 changes: 3 additions & 0 deletions
3
test/form/samples/side-effects-class-getters-setters/_config.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
module.exports = { | ||
description: 'treat getters and setters on classes as function calls' | ||
}; |
35 changes: 35 additions & 0 deletions
35
test/form/samples/side-effects-class-getters-setters/_expected.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
class RetainedByGetter { | ||
get a() { log(); } | ||
} | ||
RetainedByGetter.prototype.a; | ||
|
||
class RetainedBySetter { | ||
set a(v) { log(); } | ||
} | ||
RetainedBySetter.prototype.a = 10; | ||
|
||
class RetainedByStaticGetter { | ||
static get a() { log(); } | ||
} | ||
RetainedByStaticGetter.a; | ||
|
||
class RetainedByStaticSetter { | ||
static set a(v) { log(); } | ||
} | ||
RetainedByStaticSetter.a = 10; | ||
|
||
class RetainedSuper { | ||
static get a() { log(); } | ||
} | ||
class RetainedSub extends RetainedSuper {} | ||
RetainedSub.a; | ||
|
||
class DeoptProto {} | ||
unknown(DeoptProto.prototype); | ||
DeoptProto.prototype.a; | ||
|
||
class DeoptComputed { | ||
static get a() {} | ||
static get [unknown]() { log(); } | ||
} | ||
DeoptComputed.a; |
70 changes: 70 additions & 0 deletions
70
test/form/samples/side-effects-class-getters-setters/main.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
class Removed { | ||
get a() { log(); } | ||
set a(v) { log(); } | ||
static get a() { log(); } | ||
static set a(v) { log(); } | ||
} | ||
|
||
class RemovedNoEffect { | ||
get a() {} | ||
set a(v) {} | ||
static get a() {} | ||
static set a(v) {} | ||
} | ||
RemovedNoEffect.prototype.a; | ||
RemovedNoEffect.prototype.a = 1; | ||
RemovedNoEffect.a; | ||
RemovedNoEffect.a = 1; | ||
|
||
class RetainedByGetter { | ||
get a() { log(); } | ||
} | ||
RetainedByGetter.prototype.a; | ||
|
||
class RetainedBySetter { | ||
set a(v) { log(); } | ||
} | ||
RetainedBySetter.prototype.a = 10; | ||
|
||
class RetainedByStaticGetter { | ||
static get a() { log(); } | ||
} | ||
RetainedByStaticGetter.a; | ||
|
||
class RetainedByStaticSetter { | ||
static set a(v) { log(); } | ||
} | ||
RetainedByStaticSetter.a = 10; | ||
|
||
class RemovedSetters { | ||
set a(v) { log(); } | ||
static set a(v) { log(); } | ||
} | ||
RemovedSetters.prototype.a; | ||
RemovedSetters.a; | ||
|
||
class RemovedWrongProp { | ||
get a() { log(); } | ||
static get a() { log(); } | ||
} | ||
RemovedWrongProp.prototype.b | ||
RemovedWrongProp.b | ||
|
||
class RetainedSuper { | ||
static get a() { log(); } | ||
} | ||
class RetainedSub extends RetainedSuper {} | ||
RetainedSub.a; | ||
|
||
class RemovedSub extends RetainedSuper {} | ||
RemovedSub.b; | ||
|
||
class DeoptProto {} | ||
unknown(DeoptProto.prototype); | ||
DeoptProto.prototype.a; | ||
|
||
class DeoptComputed { | ||
static get a() {} | ||
static get [unknown]() { log(); } | ||
} | ||
DeoptComputed.a; |
Oops, something went wrong.