Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 0136d96

Browse files
Jenny Messerlycommit-bot@chromium.org
authored andcommitted
[dartdevc] fix #36532, adds DDC support for CFE constant flag
This fixes DDC to work when CFE's constants flag is enabled. The fixes are primarily due to how CFE inlines constants, and how it handles "unevaluated constants" (these arise from modular compilation and outline kernel files). Specifically this CL addresses the following issues: - private names now work across modules (landed in this CL: https://dart-review.googlesource.com/c/sdk/+/98926) - correctly emit tearoffs regardless of how they're represeted (constant or StaticGet) - correctly recognize the internal SDK and JS interop annotations, regardless of how they're represented (constants or expressions) - change how DDC represents `undefined` in its internal SDK, so its detected regardless of constant inlining - emit numeric constants as integers in JS when possible Change-Id: I0fbda9d24578075f655ea945ed1b2ceed124af48 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/99240 Reviewed-by: Mark Zhou <markzipan@google.com> Commit-Queue: Jenny Messerly <jmesserly@google.com>
1 parent 022400e commit 0136d96

File tree

14 files changed

+209
-165
lines changed

14 files changed

+209
-165
lines changed

pkg/dev_compiler/lib/src/analyzer/code_generator.dart

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2449,12 +2449,12 @@ class CodeGenerator extends Object
24492449

24502450
if (param.isOptional) {
24512451
JS.Expression defaultValue;
2452-
if (paramNode != null) {
2452+
if (findAnnotation(param, isUndefinedAnnotation) != null) {
2453+
defaultValue = null;
2454+
} else if (paramNode != null) {
24532455
var paramDefault = (paramNode as DefaultFormalParameter).defaultValue;
24542456
if (paramDefault == null) {
24552457
defaultValue = JS.LiteralNull();
2456-
} else if (_isJSUndefined(paramDefault)) {
2457-
defaultValue = null;
24582458
} else {
24592459
defaultValue = _visitExpression(paramDefault);
24602460
}
@@ -2513,16 +2513,6 @@ class CodeGenerator extends Object
25132513
(_classProperties?.covariantParameters?.contains(p) ?? false);
25142514
}
25152515

2516-
bool _isJSUndefined(Expression expr) {
2517-
expr = expr is AsExpression ? expr.expression : expr;
2518-
if (expr is Identifier) {
2519-
var element = expr.staticElement;
2520-
return isSdkInternalRuntime(element.library) &&
2521-
element.name == 'undefined';
2522-
}
2523-
return false;
2524-
}
2525-
25262516
JS.Fun _emitNativeFunctionBody(MethodDeclaration node) {
25272517
String name = getAnnotationName(node.declaredElement, isJSAnnotation) ??
25282518
node.name.name;
@@ -4270,10 +4260,6 @@ class CodeGenerator extends Object
42704260
List<VariableDeclaration> fields) {
42714261
var lazyFields = <VariableDeclaration>[];
42724262
for (var field in fields) {
4273-
// Skip our magic undefined constant.
4274-
var element = field.declaredElement as TopLevelVariableElement;
4275-
if (element.name == 'undefined') continue;
4276-
42774263
var init = field.initializer;
42784264
if (init == null ||
42794265
init is Literal ||

pkg/dev_compiler/lib/src/analyzer/js_interop.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ bool isNotNullAnnotation(DartObjectImpl value) =>
6565
bool isNullCheckAnnotation(DartObjectImpl value) =>
6666
isBuiltinAnnotation(value, '_js_helper', '_NullCheck');
6767

68+
bool isUndefinedAnnotation(DartObjectImpl value) =>
69+
isBuiltinAnnotation(value, '_js_helper', '_Undefined');
70+
6871
/// Returns the name value of the `JSExportName` annotation (when compiling
6972
/// the SDK), or `null` if there's none. This is used to control the name
7073
/// under which functions are compiled and exported.

pkg/dev_compiler/lib/src/kernel/compiler.dart

Lines changed: 33 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import 'dart:math' show max, min;
88
import 'package:front_end/src/api_unstable/ddc.dart' show TypeSchemaEnvironment;
99
import 'package:kernel/class_hierarchy.dart';
1010
import 'package:kernel/core_types.dart';
11-
import 'package:kernel/kernel.dart';
11+
import 'package:kernel/kernel.dart' hide MapEntry;
1212
import 'package:kernel/library_index.dart';
1313
import 'package:kernel/type_algebra.dart';
1414
import 'package:kernel/type_environment.dart';
@@ -1808,7 +1808,7 @@ class ProgramCompiler extends Object
18081808

18091809
/// Emits a Dart factory constructor to a JS static method.
18101810
JS.Method _emitFactoryConstructor(Procedure node) {
1811-
if (isUnsupportedFactoryConstructor(node)) return null;
1811+
if (node.isExternal || isUnsupportedFactoryConstructor(node)) return null;
18121812

18131813
var function = node.function;
18141814

@@ -1983,9 +1983,6 @@ class ProgramCompiler extends Object
19831983
var lazyFields = <Field>[];
19841984
var savedUri = _currentUri;
19851985
for (var field in fields) {
1986-
// Skip our magic undefined constant.
1987-
if (field.name.name == 'undefined') continue;
1988-
19891986
var init = field.initializer;
19901987
if (init == null ||
19911988
init is BasicLiteral ||
@@ -2954,23 +2951,15 @@ class ProgramCompiler extends Object
29542951
}
29552952

29562953
JS.Expression _defaultParamValue(VariableDeclaration p) {
2957-
if (p.initializer != null) {
2958-
var value = p.initializer;
2959-
return _isJSUndefined(value) ? null : _visitExpression(value);
2954+
if (p.annotations.any(isUndefinedAnnotation)) {
2955+
return null;
2956+
} else if (p.initializer != null) {
2957+
return _visitExpression(p.initializer);
29602958
} else {
29612959
return JS.LiteralNull();
29622960
}
29632961
}
29642962

2965-
bool _isJSUndefined(Expression expr) {
2966-
expr = expr is AsExpression ? expr.operand : expr;
2967-
if (expr is StaticGet) {
2968-
var t = expr.target;
2969-
return isSdkInternalRuntime(getLibrary(t)) && t.name.name == 'undefined';
2970-
}
2971-
return false;
2972-
}
2973-
29742963
void _emitCovarianceBoundsCheck(
29752964
List<TypeParameter> typeFormals, List<JS.Statement> body) {
29762965
for (var t in typeFormals) {
@@ -3786,15 +3775,11 @@ class ProgramCompiler extends Object
37863775
}
37873776

37883777
@override
3789-
visitStaticGet(StaticGet node) {
3790-
var target = node.target;
3778+
visitStaticGet(StaticGet node) => _emitStaticGet(node.target);
37913779

3780+
JS.Expression _emitStaticGet(Member target) {
37923781
// TODO(vsm): Re-inline constants. See:
37933782
// https://github.com/dart-lang/sdk/issues/36285
3794-
3795-
// TODO(markzipan): reifyTearOff check can be removed when we enable
3796-
// front-end constant evaluation because static tear-offs will be
3797-
// treated as constants and handled by visitTearOffConstant.
37983783
var result = _emitStaticTarget(target);
37993784
if (_reifyTearoff(target)) {
38003785
// TODO(jmesserly): we could tag static/top-level function types once
@@ -5092,13 +5077,8 @@ class ProgramCompiler extends Object
50925077
isBuiltinAnnotation(a, '_js_helper', 'ReifyFunctionTypes');
50935078
while (parent != null) {
50945079
var a = findAnnotation(parent, reifyFunctionTypes);
5095-
if (a is ConstructorInvocation) {
5096-
var args = a.arguments.positional;
5097-
if (args.length == 1) {
5098-
var arg = args[0];
5099-
if (arg is BoolLiteral) return arg.value;
5100-
}
5101-
}
5080+
var value = _constants.getFieldValueFromAnnotation(a, 'value');
5081+
if (value is bool) return value;
51025082
parent = parent.parent;
51035083
}
51045084
return true;
@@ -5128,7 +5108,8 @@ class ProgramCompiler extends Object
51285108
///
51295109
/// Calls [findAnnotation] followed by [getNameFromAnnotation].
51305110
String getAnnotationName(NamedNode node, bool test(Expression value)) {
5131-
return _constants.getNameFromAnnotation(findAnnotation(node, test));
5111+
return _constants.getFieldValueFromAnnotation(
5112+
findAnnotation(node, test), 'name');
51325113
}
51335114

51345115
JS.Expression visitConstant(Constant node) => node.accept(this);
@@ -5139,7 +5120,23 @@ class ProgramCompiler extends Object
51395120
@override
51405121
visitIntConstant(IntConstant node) => js.number(node.value);
51415122
@override
5142-
visitDoubleConstant(DoubleConstant node) => js.number(node.value);
5123+
visitDoubleConstant(DoubleConstant node) {
5124+
var value = node.value;
5125+
5126+
// Emit the constant as an integer, if possible.
5127+
if (value.isFinite) {
5128+
var intValue = value.toInt();
5129+
const int _MIN_INT32 = -0x80000000;
5130+
const int _MAX_INT32 = 0x7FFFFFFF;
5131+
if (intValue.toDouble() == value &&
5132+
intValue >= _MIN_INT32 &&
5133+
intValue <= _MAX_INT32) {
5134+
return js.number(intValue);
5135+
}
5136+
}
5137+
return js.number(value);
5138+
}
5139+
51435140
@override
51445141
visitStringConstant(StringConstant node) => js.escapedString(node.value, '"');
51455142

@@ -5174,13 +5171,11 @@ class ProgramCompiler extends Object
51745171

51755172
@override
51765173
visitInstanceConstant(node) {
5177-
entryToProperty(entry) {
5178-
var field = entry.key.asField.name.name;
5174+
entryToProperty(MapEntry<Reference, Constant> entry) {
51795175
var constant = entry.value.accept(this);
51805176
var member = entry.key.asField;
5181-
var result =
5182-
JS.Property(_emitMemberName(field, member: member), constant);
5183-
return result;
5177+
return JS.Property(
5178+
_emitMemberName(member.name.name, member: member), constant);
51845179
}
51855180

51865181
var type = visitInterfaceType(node.getType(types) as InterfaceType);
@@ -5192,14 +5187,7 @@ class ProgramCompiler extends Object
51925187
}
51935188

51945189
@override
5195-
visitTearOffConstant(node) {
5196-
var target = node.procedure;
5197-
var result = _emitStaticTarget(target);
5198-
// TODO(jmesserly): we could tag static/top-level function types once
5199-
// in the module initialization, rather than at the point where they
5200-
// escape.
5201-
return _emitFunctionTagged(result, target.function.functionType);
5202-
}
5190+
visitTearOffConstant(node) => _emitStaticGet(node.procedure);
52035191

52045192
@override
52055193
visitTypeLiteralConstant(node) => _emitTypeLiteral(node.type);

pkg/dev_compiler/lib/src/kernel/constants.dart

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,10 @@ class DevCompilerConstants {
4141
return result is UnevaluatedConstant ? null : result;
4242
}
4343

44-
/// If [node] is an annotation with a field named `name`, returns that field's
44+
/// If [node] is an annotation with a field named [name], returns that field's
4545
/// value.
4646
///
47-
/// This assumes the `name` field is populated from a named argument `name:`
47+
/// This assumes the field is populated from a named argument with that name,
4848
/// or from the first positional argument.
4949
///
5050
/// For example:
@@ -59,36 +59,56 @@ class DevCompilerConstants {
5959
/// main() { ... }
6060
///
6161
/// Given the node for `@MyAnnotation('FooBar')` this will return `'FooBar'`.
62-
String getNameFromAnnotation(ConstructorInvocation node) {
63-
if (node == null) return null;
62+
Object getFieldValueFromAnnotation(Expression node, String name) {
63+
node = unwrapUnevaluatedConstant(node);
64+
if (node is ConstantExpression) {
65+
var constant = node.constant;
66+
if (constant is InstanceConstant) {
67+
var value = constant.fieldValues.entries
68+
.firstWhere((e) => e.key.asField.name.name == name,
69+
orElse: () => null)
70+
?.value;
71+
if (value is PrimitiveConstant) return value.value;
72+
if (value is UnevaluatedConstant) {
73+
return _evaluateAnnotationArgument(value.expression);
74+
}
75+
return null;
76+
}
77+
}
6478

6579
// TODO(jmesserly): this does not use the normal evaluation engine, because
6680
// it won't work if we don't have the const constructor body available.
6781
//
6882
// We may need to address this in the kernel outline files.
69-
Expression first;
70-
var named = node.arguments.named;
71-
if (named.isNotEmpty) {
72-
first =
73-
named.firstWhere((n) => n.name == 'name', orElse: () => null)?.value;
74-
}
75-
var positional = node.arguments.positional;
76-
if (positional.isNotEmpty) first ??= positional[0];
77-
if (first != null) {
78-
first = _followConstFields(first);
79-
if (first is StringLiteral) return first.value;
83+
if (node is ConstructorInvocation) {
84+
Expression first;
85+
var named = node.arguments.named;
86+
if (named.isNotEmpty) {
87+
first =
88+
named.firstWhere((n) => n.name == name, orElse: () => null)?.value;
89+
}
90+
var positional = node.arguments.positional;
91+
if (positional.isNotEmpty) first ??= positional[0];
92+
if (first != null) {
93+
return _evaluateAnnotationArgument(first);
94+
}
8095
}
8196
return null;
8297
}
8398

84-
Expression _followConstFields(Expression expr) {
85-
if (expr is StaticGet) {
86-
var target = expr.target;
99+
Object _evaluateAnnotationArgument(Expression node) {
100+
node = unwrapUnevaluatedConstant(node);
101+
if (node is ConstantExpression) {
102+
var constant = node.constant;
103+
if (constant is PrimitiveConstant) return constant.value;
104+
}
105+
if (node is StaticGet) {
106+
var target = node.target;
87107
if (target is Field) {
88-
return _followConstFields(target.initializer);
108+
return _evaluateAnnotationArgument(target.initializer);
89109
}
90110
}
91-
return expr;
111+
return node is BasicLiteral ? node.value : null;
92112
}
93113
}
94114

pkg/dev_compiler/lib/src/kernel/js_interop.dart

Lines changed: 11 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,7 @@ bool _isJSLibrary(Library library) {
1717
}
1818

1919
bool _annotationIsFromJSLibrary(String expectedName, Expression value) {
20-
Class c;
21-
if (value is ConstructorInvocation) {
22-
c = value.target.enclosingClass;
23-
} else if (value is StaticGet) {
24-
var type = value.target.getterType;
25-
if (type is InterfaceType) c = type.classNode;
26-
}
20+
var c = getAnnotationClass(value);
2721
return c != null &&
2822
c.name == expectedName &&
2923
_isJSLibrary(c.enclosingLibrary);
@@ -48,42 +42,35 @@ bool isPublicJSAnnotation(Expression value) =>
4842
bool _isJSAnonymousAnnotation(Expression value) =>
4943
_annotationIsFromJSLibrary('_Anonymous', value);
5044

51-
bool _isBuiltinAnnotation(
52-
Expression value, String libraryName, String annotationName) {
53-
if (value is ConstructorInvocation) {
54-
var c = value.target.enclosingClass;
55-
if (c.name == annotationName) {
56-
var uri = c.enclosingLibrary.importUri;
57-
return uri.scheme == 'dart' && uri.pathSegments[0] == libraryName;
58-
}
59-
}
60-
return false;
61-
}
62-
6345
/// Whether [value] is a `@JSExportName` (internal annotation used in SDK
6446
/// instead of `@JS` from `package:js`).
6547
bool isJSExportNameAnnotation(Expression value) =>
66-
_isBuiltinAnnotation(value, '_foreign_helper', 'JSExportName');
48+
isBuiltinAnnotation(value, '_foreign_helper', 'JSExportName');
6749

6850
/// Whether [i] is a `spread` invocation (to be used on function arguments
6951
/// to have them compiled as `...` spread args in ES6 outputs).
7052
bool isJSSpreadInvocation(Procedure target) =>
7153
target.name.name == 'spread' && _isJSLibrary(target.enclosingLibrary);
7254

7355
bool isJSName(Expression value) =>
74-
_isBuiltinAnnotation(value, '_js_helper', 'JSName');
56+
isBuiltinAnnotation(value, '_js_helper', 'JSName');
7557

7658
bool isJsPeerInterface(Expression value) =>
77-
_isBuiltinAnnotation(value, '_js_helper', 'JsPeerInterface');
59+
isBuiltinAnnotation(value, '_js_helper', 'JsPeerInterface');
7860

7961
bool isNativeAnnotation(Expression value) =>
80-
_isBuiltinAnnotation(value, '_js_helper', 'Native');
62+
isBuiltinAnnotation(value, '_js_helper', 'Native');
8163

8264
bool isJSAnonymousType(Class namedClass) {
83-
return hasJSInteropAnnotation(namedClass) &&
65+
var hasJSInterop = hasJSInteropAnnotation(namedClass);
66+
var isAnonymous =
8467
findAnnotation(namedClass, _isJSAnonymousAnnotation) != null;
68+
return hasJSInterop && isAnonymous;
8569
}
8670

71+
bool isUndefinedAnnotation(Expression value) =>
72+
isBuiltinAnnotation(value, '_js_helper', '_Undefined');
73+
8774
/// Returns true iff the class has an `@JS(...)` annotation from `package:js`.
8875
///
8976
/// Note: usually [_usesJSInterop] should be used instead of this.

0 commit comments

Comments
 (0)