Skip to content

Commit 67b2493

Browse files
author
John Messerly
committed
refactor/simplify nullable inference code
Some of the high level changes are: * visit catch body, fixes #463 * handle temps created by the compiler. These were in some cases treated incorrectly as non-null (see tests diff) * compute nullable in the same pass as visiting assignments * simplify visiting += and ++, fix ++ (it was dead code, #463) * simplify _isNullable * if we fail to see a variable declaration, treat it as nullable * stack trace in catch is treated as non-null [email protected] Review URL: https://codereview.chromium.org/1751963002 .
1 parent 243a037 commit 67b2493

File tree

9 files changed

+338
-393
lines changed

9 files changed

+338
-393
lines changed

pkg/dev_compiler/lib/runtime/dart/collection.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -987,12 +987,12 @@ dart_library.library('dart/collection', null, /* Imports */[
987987
dart.assert(core.identical(IterableBase$()._toStringVisiting[dartx.last], iterable));
988988
IterableBase$()._toStringVisiting[dartx.removeLast]();
989989
}
990-
return dart.toString((() => {
990+
return (() => {
991991
let _ = new core.StringBuffer(leftDelimiter);
992992
_.writeAll(parts, ", ");
993993
_.write(rightDelimiter);
994994
return _;
995-
})());
995+
})().toString();
996996
}
997997
static iterableToFullString(iterable, leftDelimiter, rightDelimiter) {
998998
if (leftDelimiter === void 0) leftDelimiter = '(';
@@ -1735,7 +1735,7 @@ dart_library.library('dart/collection', null, /* Imports */[
17351735
if (this[dartx.length] == 0) return "";
17361736
let buffer = new core.StringBuffer();
17371737
buffer.writeAll(this, separator);
1738-
return dart.toString(buffer);
1738+
return buffer.toString();
17391739
}
17401740
[dartx.where](test) {
17411741
dart.as(test, dart.functionType(core.bool, [E]));

pkg/dev_compiler/lib/src/codegen/assignments_index.dart

Lines changed: 0 additions & 145 deletions
This file was deleted.

pkg/dev_compiler/lib/src/codegen/js_codegen.dart

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ import 'js_names.dart';
4040
import 'js_printer.dart' show writeJsLibrary;
4141
import 'js_typeref_codegen.dart';
4242
import 'module_builder.dart';
43-
import 'nullability_inferrer.dart';
43+
import 'nullable_type_inference.dart';
4444
import 'side_effect_analysis.dart';
4545

4646
// Various dynamic helpers we call.
@@ -111,6 +111,8 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
111111

112112
bool _isDartRuntime;
113113

114+
NullableTypeInference _nullInference;
115+
114116
JSCodegenVisitor(AbstractCompiler compiler, this.rules, this.currentLibrary,
115117
this._extensionTypes, this._fieldsNeedingStorage)
116118
: compiler = compiler,
@@ -127,8 +129,6 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
127129

128130
TypeProvider get types => _types;
129131

130-
NullableExpressionPredicate _isNullable;
131-
132132
JS.Program emitLibrary(LibraryUnit library) {
133133
// Modify the AST to make coercions explicit.
134134
new CoercionReifier(library, rules).reify();
@@ -142,24 +142,25 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
142142

143143
library.library.directives.forEach(_visit);
144144

145+
var units = library.partsThenLibrary;
146+
145147
// Rather than directly visit declarations, we instead use [_loader] to
146148
// visit them. It has the ability to sort elements on demand, so
147149
// dependencies between top level items are handled with a minimal
148150
// reordering of the user's input code. The loader will call back into
149151
// this visitor via [_emitModuleItem] when it's ready to visit the item
150152
// for real.
151-
_loader.collectElements(currentLibrary, library.partsThenLibrary);
153+
_loader.collectElements(currentLibrary, units);
152154

153-
var units = library.partsThenLibrary;
154-
// TODO(ochafik): Move this down to smaller scopes (method, top-levels) to
155-
// save memory.
156-
_isNullable = new NullabilityInferrer(units,
157-
getStaticType: getStaticType, isJSBuiltinType: _isJSBuiltinType)
158-
.buildNullabilityPredicate();
155+
// TODO(jmesserly): ideally we could do this at a smaller granularity.
156+
// We'll need to be consistent about when we're generating functions, and
157+
// only run this on the outermost function.
158+
_nullInference =
159+
new NullableTypeInference.forLibrary(_isPrimitiveType, units);
159160

160-
for (var unit in units) {
161-
_constField = new ConstFieldVisitor(types, unit);
161+
_constField = new ConstFieldVisitor(types, library.library.element.source);
162162

163+
for (var unit in units) {
163164
for (var decl in unit.declarations) {
164165
if (decl is TopLevelVariableDeclaration) {
165166
visitTopLevelVariableDeclaration(decl);
@@ -1245,8 +1246,6 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
12451246
JS.Statement _initializeFields(
12461247
ClassDeclaration cls, List<FieldDeclaration> fieldDecls,
12471248
[ConstructorDeclaration ctor]) {
1248-
var unit = cls.getAncestor((a) => a is CompilationUnit) as CompilationUnit;
1249-
var constField = new ConstFieldVisitor(types, unit);
12501249
bool isConst = ctor != null && ctor.constKeyword != null;
12511250
if (isConst) _loader.startTopLevel(cls.element);
12521251

@@ -1256,7 +1255,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
12561255
for (var declaration in fieldDecls) {
12571256
for (var fieldNode in declaration.fields.variables) {
12581257
var element = fieldNode.element;
1259-
if (constField.isFieldInitConstant(fieldNode)) {
1258+
if (_constField.isFieldInitConstant(fieldNode)) {
12601259
unsetFields[element as FieldElement] = fieldNode;
12611260
} else {
12621261
fields[element as FieldElement] = _visitInitializer(fieldNode);
@@ -1617,7 +1616,6 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
16171616

16181617
if (body is ExpressionFunctionBody && !destructureNamed) {
16191618
// An arrow function can use the expression directly.
1620-
// Note: this only works if we don't need to emit argument initializers.
16211619
jsBody = _visit(body.expression);
16221620
} else {
16231621
jsBody = _visit(body);
@@ -2642,7 +2640,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
26422640
/// For these types we generate a calling convention via static
26432641
/// "extension methods". This allows types to be extended without adding
26442642
/// extensions directly on the prototype.
2645-
bool _isJSBuiltinType(DartType t) =>
2643+
bool _isPrimitiveType(DartType t) =>
26462644
typeIsPrimitiveInJS(t) || t == _types.stringType;
26472645

26482646
bool typeIsPrimitiveInJS(DartType t) =>
@@ -2660,6 +2658,13 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
26602658
return js.call('dart.notNull(#)', jsExpr);
26612659
}
26622660

2661+
/// Returns true if [expr] can be null, optionally using [localIsNullable]
2662+
/// for locals.
2663+
///
2664+
/// This analysis is conservative and incomplete, but it can optimize many
2665+
/// common patterns.
2666+
bool _isNullable(Expression expr) => _nullInference.isNullable(expr);
2667+
26632668
@override
26642669
JS.Expression visitBinaryExpression(BinaryExpression node) {
26652670
var op = node.operator;
@@ -2732,12 +2737,13 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
27322737

27332738
var leftType = _canonicalizeNumTypes(getStaticType(left));
27342739
var rightType = _canonicalizeNumTypes(getStaticType(right));
2735-
return _isJSBuiltinType(leftType) && leftType == rightType;
2740+
return _isPrimitiveType(leftType) && leftType == rightType;
27362741
}
27372742

27382743
bool _isNull(Expression expr) => expr is NullLiteral;
27392744

2740-
SimpleIdentifier _createTemporary(String name, DartType type) {
2745+
SimpleIdentifier _createTemporary(String name, DartType type,
2746+
{bool nullable: true}) {
27412747
// We use an invalid source location to signal that this is a temporary.
27422748
// See [_isTemporary].
27432749
// TODO(jmesserly): alternatives are
@@ -2750,6 +2756,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
27502756
id.staticElement = new TemporaryVariableElement.forNode(id);
27512757
id.staticType = type;
27522758
DynamicInvoke.set(id, type.isDynamic);
2759+
_nullInference.addVariable(id.staticElement, nullable: nullable);
27532760
return id;
27542761
}
27552762

@@ -2847,7 +2854,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
28472854
/// // psuedocode mix of Scheme and JS:
28482855
/// (let* (x1=expr1, x2=expr2, t=expr1[expr2]) { x1[x2] = t + 1; t })
28492856
///
2850-
/// The [JS.JS.MetaLet] nodes automatically simplify themselves if they can.
2857+
/// The [JS.MetaLet] nodes automatically simplify themselves if they can.
28512858
/// For example, if the result value is not used, then `t` goes away.
28522859
@override
28532860
JS.Expression visitPostfixExpression(PostfixExpression node) {
@@ -2992,7 +2999,8 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
29922999
break;
29933000
}
29943001

2995-
var param = _createTemporary('_', nodeTarget.staticType);
3002+
var param =
3003+
_createTemporary('_', nodeTarget.staticType, nullable: false);
29963004
var baseNode = _stripNullAwareOp(node, param);
29973005
tail.add(new JS.ArrowFun([_visit(param)], _visit(baseNode)));
29983006
node = nodeTarget;
@@ -3227,7 +3235,8 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
32273235
null,
32283236
AstBuilder.argumentList([node.iterable]),
32293237
false);
3230-
var iter = _visit(_createTemporary('it', StreamIterator_T));
3238+
var iter =
3239+
_visit(_createTemporary('it', StreamIterator_T, nullable: false));
32313240

32323241
var init = _visit(node.identifier);
32333242
if (init == null) {
@@ -3643,9 +3652,6 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
36433652
library, () => new JS.TemporaryId(jsLibraryName(library)));
36443653
}
36453654

3646-
DartType getStaticType(Expression e) =>
3647-
e.staticType ?? DynamicTypeImpl.instance;
3648-
36493655
JS.Node annotate(JS.Node node, AstNode original, [Element element]) {
36503656
if (options.closure && element != null) {
36513657
node = node.withClosureAnnotation(closureAnnotationFor(

0 commit comments

Comments
 (0)