Version 2.10.0-40.0.dev
Merge commit 'd2057a0b5785a526e6f73b8f5f0c49004499d767' into 'dev'
diff --git a/.dart_tool/package_config.json b/.dart_tool/package_config.json
index 3136266..515b734 100644
--- a/.dart_tool/package_config.json
+++ b/.dart_tool/package_config.json
@@ -11,7 +11,7 @@
"constraint, update this by running tools/generate_package_config.dart."
],
"configVersion": 2,
- "generated": "2020-08-13T10:20:32.864803",
+ "generated": "2020-08-17T14:05:33.104579",
"generator": "tools/generate_package_config.dart",
"packages": [
{
@@ -202,7 +202,7 @@
"name": "dart_internal",
"rootUri": "../pkg/dart_internal",
"packageUri": "lib/",
- "languageVersion": "2.0"
+ "languageVersion": "2.10"
},
{
"name": "dart_style",
diff --git a/pkg/analyzer/lib/src/dart/element/type_system.dart b/pkg/analyzer/lib/src/dart/element/type_system.dart
index 1a89506..37ee42d 100644
--- a/pkg/analyzer/lib/src/dart/element/type_system.dart
+++ b/pkg/analyzer/lib/src/dart/element/type_system.dart
@@ -313,7 +313,7 @@
/// left operand has the type [leftType] and whose right operand has the type
/// [rightType], given that resolution has so far produced the [currentType].
DartType refineBinaryExpressionType(DartType leftType, TokenType operator,
- DartType rightType, DartType currentType);
+ DartType rightType, DartType currentType, MethodElement operatorElement);
@override
DartType resolveToBound(DartType type) {
@@ -1346,7 +1346,7 @@
@override
DartType refineBinaryExpressionType(DartType leftType, TokenType operator,
- DartType rightType, DartType currentType) {
+ DartType rightType, DartType currentType, MethodElement operatorElement) {
if (isNonNullableByDefault) {
return _refineBinaryExpressionTypeNullSafe(
leftType, operator, rightType, currentType);
diff --git a/pkg/analyzer/lib/src/dart/error/hint_codes.dart b/pkg/analyzer/lib/src/dart/error/hint_codes.dart
index 70cc4ba..b9ce65f 100644
--- a/pkg/analyzer/lib/src/dart/error/hint_codes.dart
+++ b/pkg/analyzer/lib/src/dart/error/hint_codes.dart
@@ -20,7 +20,7 @@
*/
static const HintCode CAN_BE_NULL_AFTER_NULL_AWARE = HintCode(
'CAN_BE_NULL_AFTER_NULL_AWARE',
- "The target expression uses '?.', so its value can be null.",
+ "The receiver uses '?.', so its value can be null.",
correction: "Replace the '.' with a '?.' in the invocation.");
/**
@@ -1533,10 +1533,9 @@
*/
static const HintCode RECEIVER_OF_TYPE_NEVER = HintCode(
'RECEIVER_OF_TYPE_NEVER',
- 'The receiver expression is of type Never, and will never complete '
- 'with a value.',
- correction: 'Try checking for throw expressions or type errors in the'
- ' target expression');
+ "The receiver is of type 'Never', and will never complete with a value.",
+ correction: "Try checking for throw expressions or type errors in the "
+ "receiver");
/**
* No parameters.
diff --git a/pkg/analyzer/lib/src/dart/resolver/assignment_expression_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/assignment_expression_resolver.dart
index dcfb6a0..4490f2a 100644
--- a/pkg/analyzer/lib/src/dart/resolver/assignment_expression_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/assignment_expression_resolver.dart
@@ -313,6 +313,7 @@
operator,
rightType,
type,
+ operatorElement,
);
_inferenceHelper.recordStaticType(node, type);
diff --git a/pkg/analyzer/lib/src/dart/resolver/binary_expression_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/binary_expression_resolver.dart
index db0d9c9..ccc0ab7 100644
--- a/pkg/analyzer/lib/src/dart/resolver/binary_expression_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/binary_expression_resolver.dart
@@ -363,6 +363,7 @@
node.operator.type,
node.rightOperand.staticType,
staticType,
+ node.staticElement,
);
}
_inferenceHelper.recordStaticType(node, staticType);
diff --git a/pkg/analyzer/lib/src/error/codes.dart b/pkg/analyzer/lib/src/error/codes.dart
index a0b721c..11d59aa 100644
--- a/pkg/analyzer/lib/src/error/codes.dart
+++ b/pkg/analyzer/lib/src/error/codes.dart
@@ -2788,7 +2788,7 @@
// #### Description
//
// The analyzer produces this diagnostic when an extension override is the
- // target of the invocation of a static member. Similar to static members in
+ // receiver of the invocation of a static member. Similar to static members in
// classes, the static members of an extension should be accessed using the
// name of the extension, not an extension override.
//
@@ -2867,8 +2867,8 @@
// ```
//
// If there's a different extension that's valid for the type of the argument,
- // then either replace the name of the extension or unwrap the target so that
- // the correct extension is found.
+ // then either replace the name of the extension or unwrap the argument so
+ // that the correct extension is found.
static const CompileTimeErrorCode EXTENSION_OVERRIDE_ARGUMENT_NOT_ASSIGNABLE =
CompileTimeErrorCode(
'EXTENSION_OVERRIDE_ARGUMENT_NOT_ASSIGNABLE',
@@ -2882,8 +2882,8 @@
// #### Description
//
// The analyzer produces this diagnostic when an extension override is used as
- // the target of a cascade expression. The value of a cascade expression
- // `e..m` is the value of the target `e`, but extension overrides aren't
+ // the receiver of a cascade expression. The value of a cascade expression
+ // `e..m` is the value of the receiver `e`, but extension overrides aren't
// expressions and don't have a value.
//
// #### Examples
@@ -2919,7 +2919,7 @@
CompileTimeErrorCode(
'EXTENSION_OVERRIDE_WITH_CASCADE',
"Extension overrides have no value so they can't be used as the "
- "target of a cascade expression.",
+ "receiver of a cascade expression.",
correction: "Try using '.' instead of '..'.",
hasPublishedDocs: true);
@@ -2963,7 +2963,7 @@
// }
// ```
//
- // If you don't want to invoke a member, then unwrap the target:
+ // If you don't want to invoke a member, then unwrap the argument:
//
// ```dart
// extension E on int {
@@ -3544,8 +3544,8 @@
static const CompileTimeErrorCode GENERIC_FUNCTION_TYPE_CANNOT_BE_BOUND =
CompileTimeErrorCode('GENERIC_FUNCTION_TYPE_CANNOT_BE_BOUND',
"Generic function types can't be used as type parameter bounds",
- correction: "Try making the free variable in the function type part"
- " of the larger declaration signature");
+ correction: "Try making the free variable in the function type part "
+ "of the larger declaration signature");
/**
* It is a compile-time error if a generic function type is used as an actual
@@ -5862,12 +5862,12 @@
static const CompileTimeErrorCode NO_GENERATIVE_CONSTRUCTORS_IN_SUPERCLASS =
CompileTimeErrorCode(
'NO_GENERATIVE_CONSTRUCTOR_IN_SUPERCLASS',
- "The class '{0}' cannot extend '{1}' because '{1}' only has factory"
- " constructors (no generative constructors), and '{0}' has at"
- ' least one generative constructor.',
- correction: 'Try implementing the class instead, adding a generative'
- " (not factory) constructor to the superclass {0}, or a factory"
- ' constructor to the subclass.');
+ "The class '{0}' cannot extend '{1}' because '{1}' only has factory "
+ "constructors (no generative constructors), and '{0}' has at "
+ "least one generative constructor.",
+ correction: "Try implementing the class instead, adding a generative "
+ "(not factory) constructor to the superclass {0}, or a factory "
+ "constructor to the subclass.");
/**
* Parameters:
@@ -6608,8 +6608,8 @@
static const CompileTimeErrorCode NON_GENERATIVE_CONSTRUCTOR =
CompileTimeErrorCode(
'NON_GENERATIVE_CONSTRUCTOR',
- "The constructor '{0}' is a factory constructor, but must be a"
- " generative constructor to be a valid superinitializer.",
+ "The constructor '{0}' is a factory constructor, but must be a "
+ "generative constructor to be a valid superinitializer.",
correction:
"Try calling a different constructor of the superclass, or "
"making the called constructor not be a factory constructor.");
@@ -6627,12 +6627,12 @@
static const CompileTimeErrorCode NON_GENERATIVE_IMPLICIT_CONSTRUCTOR =
CompileTimeErrorCode(
'NON_GENERATIVE_IMPLICIT_CONSTRUCTOR',
- "The default constructor of superclass '{0}' (called by the implicit"
- " default constructor of '{1}') must be a generative constructor,"
- " but factory found.",
- correction: 'Try adding an explicit constructor that has a different'
- " superinitializer or changing the superclass constructor '{2}'"
- " to not be a factory constructor.");
+ "The default constructor of superclass '{0}' (called by the implicit "
+ "default constructor of '{1}') must be a generative constructor, "
+ "but factory found.",
+ correction: "Try adding an explicit constructor that has a different "
+ "superinitializer or changing the superclass constructor '{2}' "
+ "to not be a factory constructor.");
static const CompileTimeErrorCode NON_SYNC_FACTORY = CompileTimeErrorCode(
'NON_SYNC_FACTORY',
@@ -7483,8 +7483,8 @@
"Library is unnamed. Expected a URI not a library name '{0}' in the "
"part-of directive.",
correction:
- "Try changing the part-of directive to a URI, or try including a"
- " different part.");
+ "Try changing the part-of directive to a URI, or try including a "
+ "different part.");
/**
* 14.1 Imports: It is a compile-time error if the current library declares a
@@ -9493,7 +9493,7 @@
// ```
//
// If a subclass adds a parameter with the name in question, then cast the
- // target to the subclass:
+ // receiver to the subclass:
//
// ```dart
// class C {
@@ -10259,8 +10259,8 @@
'WRONG_TYPE_PARAMETER_VARIANCE_POSITION',
"The '{0}' type parameter '{1}' can't be used in an '{2}' position.",
correction: "Try removing the type parameter or change the explicit "
- "variance modifier declaration for the type parameter to another one of"
- " 'in', 'out', or 'inout'.",
+ "variance modifier declaration for the type parameter to another one "
+ "of 'in', 'out', or 'inout'.",
);
/**
@@ -10450,7 +10450,7 @@
// #### Description
//
// The analyzer produces this diagnostic when a null-aware operator (`?.`,
- // `?..`, `?[`, `?..[`, or `...?`) is used on a target that's known to be
+ // `?..`, `?[`, `?..[`, or `...?`) is used on a receiver that's known to be
// non-nullable.
//
// #### Example
@@ -10489,8 +10489,8 @@
static const StaticWarningCode INVALID_NULL_AWARE_OPERATOR =
StaticWarningCode(
'INVALID_NULL_AWARE_OPERATOR',
- "The target expression can't be null, so the null-aware operator "
- "'{0}' can't be used.",
+ "The receiver can't be null, so the null-aware operator '{0}' can't "
+ "be used.",
correction: "Try replacing the operator '{0}' with '{1}'.",
hasPublishedDocs: true);
@@ -10504,8 +10504,8 @@
StaticWarningCodeWithUniqueName(
'INVALID_NULL_AWARE_OPERATOR',
'INVALID_NULL_AWARE_OPERATOR_AFTER_SHORT_CIRCUIT',
- "The target expression can't be null because of short-circuiting, so "
- "the null-aware operator '{0}' can't be used.",
+ "The receiver can't be null because of short-circuiting, so the "
+ "null-aware operator '{0}' can't be used.",
correction: "Try replacing the operator '{0}' with '{1}'.",
hasPublishedDocs: true);
@@ -10638,12 +10638,9 @@
// }
// ```
static const StaticWarningCode UNNECESSARY_NON_NULL_ASSERTION =
- StaticWarningCode(
- 'UNNECESSARY_NON_NULL_ASSERTION',
- "The '!' will have no effect because the target expression can't be"
- " null.",
- correction: "Try removing the '!' operator.",
- hasPublishedDocs: true);
+ StaticWarningCode('UNNECESSARY_NON_NULL_ASSERTION',
+ "The '!' will have no effect because the receiver can't be null.",
+ correction: "Try removing the '!' operator.", hasPublishedDocs: true);
/**
* Initialize a newly created error code to have the given [name]. The message
diff --git a/pkg/analyzer/lib/src/task/strong/checker.dart b/pkg/analyzer/lib/src/task/strong/checker.dart
index 33db715..f036ec1 100644
--- a/pkg/analyzer/lib/src/task/strong/checker.dart
+++ b/pkg/analyzer/lib/src/task/strong/checker.dart
@@ -629,6 +629,7 @@
op,
rhsType,
functionType.returnType,
+ methodElement,
);
// Check the argument for an implicit cast.
@@ -749,6 +750,7 @@
TokenType.PLUS,
rhsType,
functionType.returnType,
+ element,
);
// Skip the argument check - `int` cannot be downcast.
diff --git a/pkg/analyzer/tool/diagnostics/diagnostics.md b/pkg/analyzer/tool/diagnostics/diagnostics.md
index 8d007fc..28e0d25 100644
--- a/pkg/analyzer/tool/diagnostics/diagnostics.md
+++ b/pkg/analyzer/tool/diagnostics/diagnostics.md
@@ -2214,7 +2214,7 @@
#### Description
The analyzer produces this diagnostic when an extension override is the
-target of the invocation of a static member. Similar to static members in
+receiver of the invocation of a static member. Similar to static members in
classes, the static members of an extension should be accessed using the
name of the extension, not an extension override.
@@ -2286,8 +2286,8 @@
{% endprettify %}
If there's a different extension that's valid for the type of the argument,
-then either replace the name of the extension or unwrap the target so that
-the correct extension is found.
+then either replace the name of the extension or unwrap the argument so
+that the correct extension is found.
### extension_override_without_access
@@ -2330,7 +2330,7 @@
}
{% endprettify %}
-If you don't want to invoke a member, then unwrap the target:
+If you don't want to invoke a member, then unwrap the argument:
{% prettify dart tag=pre+code %}
extension E on int {
@@ -2344,14 +2344,14 @@
### extension_override_with_cascade
-_Extension overrides have no value so they can't be used as the target of a
+_Extension overrides have no value so they can't be used as the receiver of a
cascade expression._
#### Description
The analyzer produces this diagnostic when an extension override is used as
-the target of a cascade expression. The value of a cascade expression
-`e..m` is the value of the target `e`, but extension overrides aren't
+the receiver of a cascade expression. The value of a cascade expression
+`e..m` is the value of the receiver `e`, but extension overrides aren't
expressions and don't have a value.
#### Examples
@@ -3445,16 +3445,15 @@
### invalid_null_aware_operator
-_The target expression can't be null because of short-circuiting, so the
-null-aware operator '{0}' can't be used._
+_The receiver can't be null because of short-circuiting, so the null-aware
+operator '{0}' can't be used._
-_The target expression can't be null, so the null-aware operator '{0}' can't be
-used._
+_The receiver can't be null, so the null-aware operator '{0}' can't be used._
#### Description
The analyzer produces this diagnostic when a null-aware operator (`?.`,
-`?..`, `?[`, `?..[`, or `...?`) is used on a target that's known to be
+`?..`, `?[`, `?..[`, or `...?`) is used on a receiver that's known to be
non-nullable.
#### Example
@@ -7646,7 +7645,7 @@
{% endprettify %}
If a subclass adds a parameter with the name in question, then cast the
-target to the subclass:
+receiver to the subclass:
{% prettify dart tag=pre+code %}
class C {
@@ -7895,7 +7894,7 @@
### unnecessary_non_null_assertion
-_The '!' will have no effect because the target expression can't be null._
+_The '!' will have no effect because the receiver can't be null._
#### Description
diff --git a/pkg/compiler/lib/src/options.dart b/pkg/compiler/lib/src/options.dart
index 2871956..f4bc3a6 100644
--- a/pkg/compiler/lib/src/options.dart
+++ b/pkg/compiler/lib/src/options.dart
@@ -250,6 +250,10 @@
/// operate with a stronger guarantee.
bool enableNullAssertions = false;
+ /// Whether to generate code asserting that non-nullable return values of
+ /// `@Native` methods are checked for being non-null.
+ bool enableNativeReturnNullAssertions = false;
+
/// Whether to generate a source-map file together with the output program.
bool generateSourceMap = true;
@@ -616,6 +620,11 @@
if (_deferClassTypes) deferClassTypes = true;
if (_noDeferClassTypes) deferClassTypes = false;
+
+ if (enableNullAssertions) {
+ // TODO(sra): Add a command-line flag to control this independently.
+ enableNativeReturnNullAssertions = true;
+ }
}
/// Returns `true` if warnings and hints are shown for all packages.
diff --git a/pkg/compiler/lib/src/ssa/builder_kernel.dart b/pkg/compiler/lib/src/ssa/builder_kernel.dart
index 24104e5..dfc216c 100644
--- a/pkg/compiler/lib/src/ssa/builder_kernel.dart
+++ b/pkg/compiler/lib/src/ssa/builder_kernel.dart
@@ -1540,8 +1540,18 @@
push(HInvokeExternal(targetElement, inputs, returnType, nativeBehavior,
sourceInformation: null));
- // TODO(johnniwinther): Provide source information.
HInstruction value = pop();
+ // TODO(johnniwinther): Provide source information.
+ if (options.enableNativeReturnNullAssertions) {
+ if (_isNonNullableByDefault(functionNode)) {
+ DartType type = _getDartTypeIfValid(functionNode.returnType);
+ if (dartTypes.isNonNullableIfSound(type)) {
+ push(HNullCheck(value, _abstractValueDomain.excludeNull(returnType),
+ sticky: true));
+ value = pop();
+ }
+ }
+ }
if (targetElement.isSetter) {
_closeAndGotoExit(HGoto(_abstractValueDomain));
} else {
diff --git a/pkg/compiler/lib/src/ssa/codegen.dart b/pkg/compiler/lib/src/ssa/codegen.dart
index 661962a..7c50116 100644
--- a/pkg/compiler/lib/src/ssa/codegen.dart
+++ b/pkg/compiler/lib/src/ssa/codegen.dart
@@ -8,6 +8,7 @@
import 'package:front_end/src/api_unstable/dart2js.dart' show Link;
import '../common.dart';
+import '../common/metrics.dart';
import '../common/names.dart';
import '../common/codegen.dart' show CodegenRegistry;
import '../common/tasks.dart' show Measurer, CompilerTask;
@@ -52,6 +53,7 @@
class SsaCodeGeneratorTask extends CompilerTask {
final CompilerOptions _options;
final SourceInformationStrategy sourceInformationStrategy;
+ final _CodegenMetrics _metrics = _CodegenMetrics();
SsaCodeGeneratorTask(
Measurer measurer, this._options, this.sourceInformationStrategy)
@@ -60,6 +62,9 @@
@override
String get name => 'SSA code generator';
+ @override
+ Metrics get metrics => _metrics;
+
js.Fun buildJavaScriptFunction(bool needsAsyncRewrite, FunctionEntity element,
List<js.Parameter> parameters, js.Block body) {
js.Fun finish(js.AsyncModifier asyncModifier) {
@@ -115,6 +120,7 @@
SsaCodeGenerator codeGenerator = SsaCodeGenerator(
this,
_options,
+ _metrics,
emitter,
codegen.rtiSubstitutions,
codegen.rtiRecipeEncoder,
@@ -143,6 +149,7 @@
SsaCodeGenerator codeGenerator = SsaCodeGenerator(
this,
_options,
+ _metrics,
emitter,
codegen.rtiSubstitutions,
codegen.rtiRecipeEncoder,
@@ -158,6 +165,34 @@
}
}
+class _CodegenMetrics extends MetricsBase {
+ int countHIf = 0;
+ int countHIfConstant = 0;
+ final countHInterceptor = CountMetric('count.HInterceptor');
+ final countHInterceptorGet = CountMetric('count.HInterceptor.getInterceptor');
+ final countHInterceptorOneshot = CountMetric('count.HInterceptor.oneShot');
+ final countHInterceptorConditionalConstant =
+ CountMetric('count.HInterceptor.conditionalConstant');
+
+ _CodegenMetrics();
+
+ @override
+ String get namespace => 'codegen';
+
+ @override
+ Iterable<Metric> get primary => [];
+
+ @override
+ Iterable<Metric> get secondary => [
+ CountMetric('count.HIf')..add(countHIf),
+ CountMetric('count.HIf.constant')..add(countHIfConstant),
+ countHInterceptor,
+ countHInterceptorGet,
+ countHInterceptorConditionalConstant,
+ countHInterceptorOneshot
+ ];
+}
+
class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
/// Returned by [expressionType] to tell how code can be generated for
/// a subgraph.
@@ -186,6 +221,7 @@
final Tracer _tracer;
final JClosedWorld _closedWorld;
final CodegenRegistry _registry;
+ final _CodegenMetrics _metrics;
final Set<HInstruction> generateAtUseSite;
final Set<HInstruction> controlFlowOperators;
@@ -232,6 +268,7 @@
SsaCodeGenerator(
this._codegenTask,
this._options,
+ this._metrics,
this._emitter,
this._rtiSubstitutions,
this._rtiRecipeEncoder,
@@ -1791,9 +1828,12 @@
@override
visitIf(HIf node) {
+ _metrics.countHIf++;
+ HInstruction condition = node.inputs[0];
+ if (condition.isConstant()) _metrics.countHIfConstant++;
+
if (tryControlFlowOperation(node)) return;
- HInstruction condition = node.inputs[0];
HIfBlockInformation info = node.blockInformation.body;
if (condition.isConstant()) {
@@ -1827,7 +1867,9 @@
@override
void visitInterceptor(HInterceptor node) {
+ _metrics.countHInterceptor.add();
if (node.isConditionalConstantInterceptor) {
+ _metrics.countHInterceptorConditionalConstant.add();
assert(node.inputs.length == 2);
use(node.receiver);
js.Expression receiverExpression = pop();
@@ -1835,6 +1877,7 @@
js.Expression constant = pop();
push(js.js('# && #', [receiverExpression, constant]));
} else {
+ _metrics.countHInterceptorGet.add();
assert(node.inputs.length == 1);
_registry.registerSpecializedGetInterceptor(node.interceptedClasses);
js.Name name = _namer.nameForGetInterceptor(node.interceptedClasses);
@@ -1922,6 +1965,8 @@
@override
void visitOneShotInterceptor(HOneShotInterceptor node) {
+ _metrics.countHInterceptor.add();
+ _metrics.countHInterceptorOneshot.add();
List<js.Expression> arguments = visitArguments(node.inputs);
js.Expression isolate =
_namer.readGlobalObjectForLibrary(_commonElements.interceptorsLibrary);
diff --git a/pkg/compiler/lib/src/ssa/nodes.dart b/pkg/compiler/lib/src/ssa/nodes.dart
index 9e5cdc5..72631d5 100644
--- a/pkg/compiler/lib/src/ssa/nodes.dart
+++ b/pkg/compiler/lib/src/ssa/nodes.dart
@@ -1548,18 +1548,20 @@
: super(inputs, type);
}
-/// A [HCheck] instruction is an instruction that might do a dynamic
-/// check at runtime on another instruction. To have proper instruction
-/// dependencies in the graph, instructions that depend on the check
-/// being done reference the [HCheck] instruction instead of the
-/// instruction itself.
+/// A [HCheck] instruction is an instruction that might do a dynamic check at
+/// runtime on an input instruction. To have proper instruction dependencies in
+/// the graph, instructions that depend on the check being done reference the
+/// [HCheck] instruction instead of the input instruction.
abstract class HCheck extends HInstruction {
HCheck(inputs, type) : super(inputs, type) {
setUseGvn();
}
+
HInstruction get checkedInput => inputs[0];
+
@override
bool isJsStatement() => true;
+
@override
bool canThrow(AbstractValueDomain domain) => true;
@@ -3607,10 +3609,12 @@
/// field getter or setter when the receiver might be null. In these cases, the
/// [selector] and [field] members are assigned.
class HNullCheck extends HCheck {
+ // A sticky check is not optimized away on the basis of the input type.
+ final bool sticky;
Selector selector;
FieldEntity field;
- HNullCheck(HInstruction input, AbstractValue type)
+ HNullCheck(HInstruction input, AbstractValue type, {this.sticky = false})
: super(<HInstruction>[input], type);
@override
@@ -3632,6 +3636,7 @@
bool dataEquals(HNullCheck other) => true;
bool isRedundant(JClosedWorld closedWorld) {
+ if (sticky) return false;
AbstractValueDomain abstractValueDomain = closedWorld.abstractValueDomain;
AbstractValue inputType = checkedInput.instructionType;
return abstractValueDomain.isNull(inputType).isDefinitelyFalse;
diff --git a/pkg/compiler/lib/src/ssa/optimize.dart b/pkg/compiler/lib/src/ssa/optimize.dart
index 9ff7388..0d00347 100644
--- a/pkg/compiler/lib/src/ssa/optimize.dart
+++ b/pkg/compiler/lib/src/ssa/optimize.dart
@@ -908,7 +908,7 @@
AbstractValueFactory.fromNativeBehavior(nativeBehavior, _closedWorld);
HInstruction receiver = node.inputs.last; // Drop interceptor.
receiver = maybeGuardWithNullCheck(receiver, node, null);
- HInvokeExternal result = HInvokeExternal(
+ HInstruction result = HInvokeExternal(
method, [receiver], returnType, nativeBehavior,
sourceInformation: node.sourceInformation);
_registry.registerStaticUse(StaticUse.methodInlining(method, null));
@@ -919,7 +919,25 @@
result.sideEffects.setDependsOnSomething();
result.sideEffects.clearAllSideEffects();
result.setUseGvn();
- return result;
+
+ return maybeAddNativeReturnNullCheck(node, result, method);
+ }
+
+ HInstruction maybeAddNativeReturnNullCheck(
+ HInstruction node, HInstruction replacement, FunctionEntity method) {
+ if (_options.enableNativeReturnNullAssertions) {
+ if (method.library.isNonNullableByDefault) {
+ FunctionType type =
+ _closedWorld.elementEnvironment.getFunctionType(method);
+ if (_closedWorld.dartTypes.isNonNullableIfSound(type.returnType)) {
+ node.block.addBefore(node, replacement);
+ replacement = HNullCheck(replacement,
+ _abstractValueDomain.excludeNull(replacement.instructionType),
+ sticky: true);
+ }
+ }
+ }
+ return replacement;
}
// Try to 'inline' an instance setter call to a known native or js-interop
@@ -1025,14 +1043,15 @@
AbstractValueFactory.fromNativeBehavior(nativeBehavior, _closedWorld);
HInstruction receiver = inputs[1];
receiver = maybeGuardWithNullCheck(receiver, node, null);
- HInvokeExternal result = HInvokeExternal(
+ HInstruction result = HInvokeExternal(
method,
[receiver, ...inputs.skip(2)], // '2': Drop interceptor and receiver.
returnType,
nativeBehavior,
sourceInformation: node.sourceInformation);
_registry.registerStaticUse(StaticUse.methodInlining(method, null));
- return result;
+
+ return maybeAddNativeReturnNullCheck(node, result, method);
}
@override
diff --git a/pkg/dart_internal/lib/extract_type_arguments.dart b/pkg/dart_internal/lib/extract_type_arguments.dart
index 218d075..4275e70 100644
--- a/pkg/dart_internal/lib/extract_type_arguments.dart
+++ b/pkg/dart_internal/lib/extract_type_arguments.dart
@@ -22,8 +22,8 @@
/// print(extractIterableTypeArgument(iterable, <T>() => new Set<T>());
/// // Prints "Instance of 'Set<int>'".
/// ```
-Object extractIterableTypeArgument(
- Iterable iterable, Object Function<T>() extract) =>
+Object? extractIterableTypeArgument(
+ Iterable iterable, Object? Function<T>() extract) =>
internal.extractTypeArguments<Iterable>(iterable, extract);
/// Given a [Map], invokes [extract], passing the [map]'s key and value type
@@ -40,5 +40,5 @@
/// // Prints "Instance of 'Two<String, int>'".
/// }
/// ```
-Object extractMapTypeArguments(Map map, Object Function<K, V>() extract) =>
+Object? extractMapTypeArguments(Map map, Object? Function<K, V>() extract) =>
internal.extractTypeArguments<Map>(map, extract);
diff --git a/pkg/dart_internal/pubspec.yaml b/pkg/dart_internal/pubspec.yaml
index d499299..757bdf3 100644
--- a/pkg/dart_internal/pubspec.yaml
+++ b/pkg/dart_internal/pubspec.yaml
@@ -1,5 +1,7 @@
name: dart_internal
-version: 0.1.10
+version: 0.1.11-nullsafety
+author: "Dart Team <misc@dartlang.org>"
+homepage: http://www.dartlang.org
repository: https://github.com/dart-lang/sdk/tree/master/pkg/dart_internal
description: >
This package is not intended for wide use. It provides a temporary API to
@@ -16,4 +18,4 @@
environment:
# Restrict the upper bound so that we can remove support for this in a later
# version of the SDK without it being a breaking change.
- sdk: ">=2.0.0 <2.10.0"
+ sdk: ">=2.10.0-0.0 <2.10.0"
diff --git a/pkg/nnbd_migration/lib/src/fix_builder.dart b/pkg/nnbd_migration/lib/src/fix_builder.dart
index 52f6983..ff2d412 100644
--- a/pkg/nnbd_migration/lib/src/fix_builder.dart
+++ b/pkg/nnbd_migration/lib/src/fix_builder.dart
@@ -752,6 +752,7 @@
combinerType,
rhsType,
combiner.returnType,
+ combiner,
);
if (!fixBuilder._typeSystem.isSubtypeOf(combinerReturnType, writeType)) {
(fixBuilder._getChange(node) as NodeChangeForAssignmentLike)
diff --git a/runtime/bin/gen_snapshot.cc b/runtime/bin/gen_snapshot.cc
index f2d33c5..187cf92 100644
--- a/runtime/bin/gen_snapshot.cc
+++ b/runtime/bin/gen_snapshot.cc
@@ -603,11 +603,26 @@
const char* path) {
TextBuffer line(128);
if (id != 1) {
- line.Printf(",");
+ line.AddString(",\n");
}
line.Printf("{ \"id\": %" Pd ", \"path\": \"", id);
line.AddEscapedString(path);
- line.Printf("\" }");
+ line.AddString("\", \"libraries\": [\n");
+ Dart_Handle uris = Dart_LoadingUnitLibraryUris(id);
+ CHECK_RESULT(uris);
+ intptr_t length;
+ CHECK_RESULT(Dart_ListLength(uris, &length));
+ for (intptr_t i = 0; i < length; i++) {
+ const char* uri;
+ CHECK_RESULT(Dart_StringToCString(Dart_ListGetAt(uris, i), &uri));
+ if (i != 0) {
+ line.AddString(",\n");
+ }
+ line.AddString("\"");
+ line.AddEscapedString(uri);
+ line.AddString("\"");
+ }
+ line.AddString("]}");
if (!manifest_file->Print("%s\n", line.buffer())) {
PrintErrAndExit("Error: Unable to write file: %s\n\n",
loading_unit_manifest_filename);
diff --git a/runtime/include/dart_api.h b/runtime/include/dart_api.h
index 8eebaed..d12d6c7 100644
--- a/runtime/include/dart_api.h
+++ b/runtime/include/dart_api.h
@@ -3685,6 +3685,8 @@
intptr_t size);
typedef void (*Dart_StreamingCloseCallback)(void* callback_data);
+DART_EXPORT Dart_Handle Dart_LoadingUnitLibraryUris(intptr_t loading_unit_id);
+
// On Darwin systems, 'dlsym' adds an '_' to the beginning of the symbol name.
// Use the '...CSymbol' definitions for resolving through 'dlsym'. The actual
// symbol names in the objects are given by the '...AsmSymbol' definitions.
diff --git a/runtime/tests/vm/dart/incompatible_loading_unit_test.dart b/runtime/tests/vm/dart/incompatible_loading_unit_test.dart
index 1663e3a..5facc47 100644
--- a/runtime/tests/vm/dart/incompatible_loading_unit_test.dart
+++ b/runtime/tests/vm/dart/incompatible_loading_unit_test.dart
@@ -27,6 +27,25 @@
throw "Cannot run test as $genSnapshot not available";
}
+ sanitizedPartitioning(manifest) {
+ // Filter core libraries, relativize URIs, and sort to make the results less
+ // sensitive to compiler or test harness changes.
+ print(manifest);
+ var units = <List<String>>[];
+ for (var unit in manifest['loadingUnits']) {
+ var uris = <String>[];
+ for (var uri in unit['libraries']) {
+ if (uri.startsWith("dart:")) continue;
+ uris.add(Uri.file(uri).pathSegments.last);
+ }
+ uris.sort((a, b) => a.compareTo(b));
+ units.add(uris);
+ }
+ units.sort((a, b) => a.first.compareTo(b.first));
+ print(units);
+ return units;
+ }
+
await withTempDir("incompatible-loading-unit-test", (String tempDir) async {
final source1 = path.join(
sdkDir, "runtime/tests/vm/dart_2/incompatible_loading_unit_1.dart");
@@ -67,6 +86,11 @@
]);
var manifest = jsonDecode(await new File(manifest1).readAsString());
Expect.equals(2, manifest["loadingUnits"].length);
+ // Note package:expect doesn't do deep equals on collections.
+ Expect.equals(
+ "[[incompatible_loading_unit_1.dart],"
+ " [incompatible_loading_unit_1_deferred.dart]]",
+ sanitizedPartitioning(manifest).toString());
Expect.isTrue(await new File(deferredSnapshot1).exists());
await run(genSnapshot, <String>[
@@ -76,8 +100,12 @@
"--loading-unit-manifest=$manifest2",
dill2,
]);
- manifest = jsonDecode(await new File(manifest1).readAsString());
+ manifest = jsonDecode(await new File(manifest2).readAsString());
Expect.equals(2, manifest["loadingUnits"].length);
+ Expect.equals(
+ "[[incompatible_loading_unit_2.dart],"
+ " [incompatible_loading_unit_2_deferred.dart]]",
+ sanitizedPartitioning(manifest).toString());
Expect.isTrue(await new File(deferredSnapshot2).exists());
// Works when used normally.
diff --git a/runtime/tests/vm/dart_2/incompatible_loading_unit_test.dart b/runtime/tests/vm/dart_2/incompatible_loading_unit_test.dart
index 1663e3a..5facc47 100644
--- a/runtime/tests/vm/dart_2/incompatible_loading_unit_test.dart
+++ b/runtime/tests/vm/dart_2/incompatible_loading_unit_test.dart
@@ -27,6 +27,25 @@
throw "Cannot run test as $genSnapshot not available";
}
+ sanitizedPartitioning(manifest) {
+ // Filter core libraries, relativize URIs, and sort to make the results less
+ // sensitive to compiler or test harness changes.
+ print(manifest);
+ var units = <List<String>>[];
+ for (var unit in manifest['loadingUnits']) {
+ var uris = <String>[];
+ for (var uri in unit['libraries']) {
+ if (uri.startsWith("dart:")) continue;
+ uris.add(Uri.file(uri).pathSegments.last);
+ }
+ uris.sort((a, b) => a.compareTo(b));
+ units.add(uris);
+ }
+ units.sort((a, b) => a.first.compareTo(b.first));
+ print(units);
+ return units;
+ }
+
await withTempDir("incompatible-loading-unit-test", (String tempDir) async {
final source1 = path.join(
sdkDir, "runtime/tests/vm/dart_2/incompatible_loading_unit_1.dart");
@@ -67,6 +86,11 @@
]);
var manifest = jsonDecode(await new File(manifest1).readAsString());
Expect.equals(2, manifest["loadingUnits"].length);
+ // Note package:expect doesn't do deep equals on collections.
+ Expect.equals(
+ "[[incompatible_loading_unit_1.dart],"
+ " [incompatible_loading_unit_1_deferred.dart]]",
+ sanitizedPartitioning(manifest).toString());
Expect.isTrue(await new File(deferredSnapshot1).exists());
await run(genSnapshot, <String>[
@@ -76,8 +100,12 @@
"--loading-unit-manifest=$manifest2",
dill2,
]);
- manifest = jsonDecode(await new File(manifest1).readAsString());
+ manifest = jsonDecode(await new File(manifest2).readAsString());
Expect.equals(2, manifest["loadingUnits"].length);
+ Expect.equals(
+ "[[incompatible_loading_unit_2.dart],"
+ " [incompatible_loading_unit_2_deferred.dart]]",
+ sanitizedPartitioning(manifest).toString());
Expect.isTrue(await new File(deferredSnapshot2).exists());
// Works when used normally.
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index a9dfc83..a7882d7 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -6745,14 +6745,20 @@
for (intptr_t id = 1; id < loading_units.Length(); id++) {
void* write_callback_data = nullptr;
void* write_debug_callback_data = nullptr;
- next_callback(next_callback_data, id, &write_callback_data,
- &write_debug_callback_data);
+ {
+ TransitionVMToNative transition(T);
+ next_callback(next_callback_data, id, &write_callback_data,
+ &write_debug_callback_data);
+ }
CreateAppAOTSnapshot(write_callback, write_callback_data, strip, as_elf,
write_debug_callback_data, &data, data[id],
program_hash);
- close_callback(write_callback_data);
- if (write_debug_callback_data != nullptr) {
- close_callback(write_debug_callback_data);
+ {
+ TransitionVMToNative transition(T);
+ close_callback(write_callback_data);
+ if (write_debug_callback_data != nullptr) {
+ close_callback(write_debug_callback_data);
+ }
}
}
}
@@ -6899,6 +6905,37 @@
#endif
}
+DART_EXPORT Dart_Handle Dart_LoadingUnitLibraryUris(intptr_t loading_unit_id) {
+#if defined(TARGET_ARCH_IA32)
+ return Api::NewError("AOT compilation is not supported on IA32.");
+#elif !defined(DART_PRECOMPILER)
+ return Api::NewError(
+ "This VM was built without support for AOT compilation.");
+#else
+ DARTSCOPE(Thread::Current());
+ API_TIMELINE_DURATION(T);
+
+ const GrowableObjectArray& result =
+ GrowableObjectArray::Handle(Z, GrowableObjectArray::New());
+ const GrowableObjectArray& libs =
+ GrowableObjectArray::Handle(Z, T->isolate()->object_store()->libraries());
+ Library& lib = Library::Handle(Z);
+ LoadingUnit& unit = LoadingUnit::Handle(Z);
+ String& uri = String::Handle(Z);
+ for (intptr_t i = 0; i < libs.Length(); i++) {
+ lib ^= libs.At(i);
+ unit = lib.loading_unit();
+ if (unit.IsNull() || (unit.id() != loading_unit_id)) {
+ continue;
+ }
+ uri = lib.url();
+ result.Add(uri);
+ }
+
+ return Api::NewHandle(T, Array::MakeFixedLength(result));
+#endif
+}
+
#if (!defined(TARGET_ARCH_IA32) && !defined(DART_PRECOMPILED_RUNTIME))
// Any flag that affects how we compile code might cause a problem when the
diff --git a/sdk/lib/_internal/js_runtime/lib/internal_patch.dart b/sdk/lib/_internal/js_runtime/lib/internal_patch.dart
index e2922ca..2dc838d 100644
--- a/sdk/lib/_internal/js_runtime/lib/internal_patch.dart
+++ b/sdk/lib/_internal/js_runtime/lib/internal_patch.dart
@@ -57,7 +57,7 @@
@patch
@pragma('dart2js:noInline')
-Object extractTypeArguments<T>(T instance, Function extract) {
+Object? extractTypeArguments<T>(T instance, Function extract) {
// This function is recognized and replaced with calls to js_runtime.
// This call to [extract] is required to model that the function is called and
diff --git a/sdk/lib/_internal/vm/lib/internal_patch.dart b/sdk/lib/_internal/vm/lib/internal_patch.dart
index b904f9e..fa16bc6 100644
--- a/sdk/lib/_internal/vm/lib/internal_patch.dart
+++ b/sdk/lib/_internal/vm/lib/internal_patch.dart
@@ -32,7 +32,7 @@
native "Internal_makeFixedListUnmodifiable";
@patch
-Object extractTypeArguments<T>(T instance, Function extract)
+Object? extractTypeArguments<T>(T instance, Function extract)
native "Internal_extractTypeArguments";
/// The returned string is a [_OneByteString] with uninitialized content.
diff --git a/tests/dart2js/native/null_assertions_opt_in_lib.dart b/tests/dart2js/native/null_assertions_opt_in_lib.dart
new file mode 100644
index 0000000..e08c788
--- /dev/null
+++ b/tests/dart2js/native/null_assertions_opt_in_lib.dart
@@ -0,0 +1,58 @@
+// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'native_testing.dart';
+
+abstract class Interface {
+ int get size;
+ String get name;
+ String? get optName;
+ int method1();
+ String method2();
+}
+
+@Native("AAA")
+class AAA implements Interface {
+ int get size native;
+ String get name native;
+ String? get optName native;
+ int method1() native;
+ String method2() native;
+}
+
+/// Returns an 'AAA' object that satisfies the interface.
+AAA makeA() native;
+
+/// Returns an 'AAA' object where each method breaks the interface's contract.
+AAA makeAX() native;
+
+void setup() {
+ JS('', r"""
+(function(){
+ function AAA(s,n,m1,m2) {
+ this.size = s;
+ this.name = n;
+ this.optName = n;
+ this._m1 = m1;
+ this._m2 = m2;
+ }
+ AAA.prototype.method1 = function(){return this._m1};
+ AAA.prototype.method2 = function(){return this._m2};
+
+ makeA = function() {return new AAA(100, 'Albert', 200, 'amazing!')};
+ makeAX = function() {return new AAA(void 0, void 0, void 0, void 0)};
+
+ self.nativeConstructor(AAA);
+})()""");
+}
+
+class BBB implements Interface {
+ int get size => 300;
+ String get name => 'Brenda';
+ String? get optName => name;
+ int method1() => 400;
+ String method2() => 'brilliant!';
+}
+
+List<Interface> items = [makeA(), BBB()];
diff --git a/tests/dart2js/native/null_assertions_test.dart b/tests/dart2js/native/null_assertions_test.dart
new file mode 100644
index 0000000..0a07785
--- /dev/null
+++ b/tests/dart2js/native/null_assertions_test.dart
@@ -0,0 +1,74 @@
+// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Requirements=nnbd-weak
+// dart2jsOptions=--null-assertions
+
+// @dart=2.8
+
+// Test that `--null-assertions` injects null-checks on the returned value of
+// native methods with a non-nullable return type in an opt-in library.
+
+import 'native_testing.dart';
+import 'null_assertions_opt_in_lib.dart' as lib;
+
+// The 'Interface' version of the code is passed both native and Dart objects,
+// so there will be an interceptor dispatch to the method. This tests that the
+// null-check exists in the forwarding method.
+//
+// The 'AAA' version of the code is passed only objects of a single native
+// class, so the native method can be inlined (which happens in the optimizer).
+// This tests that the null-check exists in the 'inlined' code.
+
+@pragma('dart2js:noInline')
+String describeInterface(lib.Interface o) {
+ return '${o.name} ${o.method2()} ${o.size} ${o.method1()}';
+}
+
+@pragma('dart2js:noInline')
+String describeAAA(lib.AAA o) {
+ return '${o.name} ${o.method2()} ${o.size} ${o.method1()}';
+}
+
+@pragma('dart2js:noInline')
+void checkOptNameInterface(lib.Interface o, dynamic expected) {
+ Expect.equals(expected, o.optName);
+}
+
+@pragma('dart2js:noInline')
+void checkOptNameAAA(lib.AAA o, dynamic expected) {
+ Expect.equals(expected, o.optName);
+}
+
+const expectedA = 'Albert amazing! 100 200';
+const expectedB = 'Brenda brilliant! 300 400';
+
+void main() {
+ nativeTesting();
+ lib.setup();
+ lib.AAA a = lib.makeA();
+ lib.BBB b = lib.BBB();
+
+ Expect.equals(expectedA, describeInterface(a));
+ Expect.equals(expectedB, describeInterface(b));
+
+ Expect.equals(expectedA, describeAAA(a));
+
+ lib.AAA x = lib.makeAX(); // This object returns `null`!
+ Expect.throws(() => describeInterface(x));
+ Expect.throws(() => describeAAA(x));
+
+ Expect.throws(() => x.name);
+ Expect.throws(() => x.size);
+ Expect.throws(() => x.method1());
+ Expect.throws(() => x.method2());
+
+ // Now test that a nullable return type does not have a check.
+ checkOptNameInterface(a, 'Albert');
+ checkOptNameInterface(b, 'Brenda');
+ checkOptNameInterface(x, null);
+
+ checkOptNameAAA(a, 'Albert');
+ checkOptNameAAA(x, null);
+}
diff --git a/tests/language/unsorted/extract_type_arguments_test.dart b/tests/language/unsorted/extract_type_arguments_test.dart
index 52939c7..233dac4 100644
--- a/tests/language/unsorted/extract_type_arguments_test.dart
+++ b/tests/language/unsorted/extract_type_arguments_test.dart
@@ -39,7 +39,7 @@
Expect.isTrue(called);
// Returns result of function.
- Object result = extractIterableTypeArgument(object, <T>() => new Set<T>());
+ Object? result = extractIterableTypeArgument(object, <T>() => new Set<T>());
Expect.isTrue(result is Set<int>);
Expect.isFalse(result is Set<bool>);
@@ -63,7 +63,7 @@
Expect.isTrue(called);
// Returns result of function.
- Object result = extractMapTypeArguments(object, <K, V>() => new Two<K, V>());
+ Object? result = extractMapTypeArguments(object, <K, V>() => new Two<K, V>());
Expect.isTrue(result is Two<String, int>);
Expect.isFalse(result is Two<int, String>);
diff --git a/tools/VERSION b/tools/VERSION
index d5b4582..7306b60 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 10
PATCH 0
-PRERELEASE 39
+PRERELEASE 40
PRERELEASE_PATCH 0
\ No newline at end of file