Version 2.6.0-dev.8.1
* Cherry-pick dee702264b4ab6f6b634b2e346de5ac7ea08b042 to dev
* Cherry-pick 9f1b0fe102c0ad4599b695974621530f07684290 to dev
* Cherry-pick 30719b37a9397cc38610e71a804236ec314535ec to dev
* Cherry-pick d2b39d1a07a18572bdafedb79ce68ef5bf9e64bf to dev
* Cherry-pick c19adc635542004d2c2cdac55b447e7175391bf8 to dev
* Cherry-pick 8d8faa7dea152cce83681bad4467dac87a30f7de to dev
* Cherry-pick a9fc9f7fd4bbd9c211fbe2b1329dce82e0c1d0a2 to dev
diff --git a/pkg/analyzer/lib/src/generated/ffi_verifier.dart b/pkg/analyzer/lib/src/generated/ffi_verifier.dart
index 9e6b40d..467f542 100644
--- a/pkg/analyzer/lib/src/generated/ffi_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/ffi_verifier.dart
@@ -488,7 +488,8 @@
if (_isPointerInterfaceType(nativeType)) {
final nativeArgumentType = nativeType.typeArguments.single;
return _isValidFfiNativeType(nativeArgumentType, true) ||
- _isStructClass(nativeArgumentType);
+ _isStructClass(nativeArgumentType) ||
+ _isNativeTypeInterfaceType(nativeArgumentType);
}
} else if (nativeType is FunctionType) {
return _isValidFfiNativeFunctionType(nativeType);
@@ -539,6 +540,17 @@
return false;
}
+ // Returns `true` iff [nativeType] is a `ffi.NativeType` type.
+ bool _isNativeTypeInterfaceType(DartType nativeType) {
+ if (nativeType is InterfaceType) {
+ final element = nativeType.element;
+ if (element.library.name == 'dart.ffi') {
+ return element.name == 'NativeType';
+ }
+ }
+ return false;
+ }
+
// Returns `true` iff [nativeType] is a struct type.
_isStructClass(DartType nativeType) {
if (nativeType is InterfaceType) {
diff --git a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
index 8f24710..b99e4e9 100644
--- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
@@ -1496,53 +1496,73 @@
if (optional(".", token) ||
optional("..", token) ||
optional("?..", token)) {
- return doDotOrCascadeExpression(token);
+ doDotOrCascadeExpression(token);
+ } else if (optional("&&", token) || optional("||", token)) {
+ doLogicalExpression(token);
+ } else if (optional("??", token)) {
+ doIfNull(token);
+ } else if (optional("?.", token)) {
+ doIfNotNull(token);
+ } else {
+ doBinaryExpression(token);
}
- if (optional("&&", token) || optional("||", token)) {
- return doLogicalExpression(token);
- }
- if (optional("??", token)) return doIfNull(token);
- if (optional("?.", token)) return doIfNotNull(token);
- Expression argument = popForValue();
- Object receiver = pop();
- bool isSuper = false;
- if (receiver is ThisAccessGenerator && receiver.isSuper) {
- ThisAccessGenerator thisAccessorReceiver = receiver;
- isSuper = true;
- receiver = forest.createThisExpression(thisAccessorReceiver.fileOffset);
- }
- push(buildBinaryOperator(toValue(receiver), token, argument, isSuper));
}
- Expression buildBinaryOperator(
- Expression a, Token token, Expression b, bool isSuper) {
+ void doBinaryExpression(Token token) {
+ assert(checkState(token, <ValueKind>[
+ unionOfKinds([ValueKind.Expression, ValueKind.Generator]),
+ unionOfKinds([ValueKind.Expression, ValueKind.Generator]),
+ ]));
+ Expression right = popForValue();
+ Object left = pop();
bool negate = false;
String operator = token.stringValue;
if (identical("!=", operator)) {
operator = "==";
negate = true;
}
+ int fileOffset = offsetForToken(token);
+ Name name = new Name(operator);
if (!isBinaryOperator(operator) && !isMinusOperator(operator)) {
if (isUserDefinableOperator(operator)) {
- return buildProblem(
- fasta.templateNotBinaryOperator.withArguments(token),
- token.charOffset,
- token.length);
+ push(buildProblem(fasta.templateNotBinaryOperator.withArguments(token),
+ token.charOffset, token.length));
} else {
- return buildProblem(fasta.templateInvalidOperator.withArguments(token),
- token.charOffset, token.length);
+ push(buildProblem(fasta.templateInvalidOperator.withArguments(token),
+ token.charOffset, token.length));
}
+ } else if (left is ExplicitExtensionAccessGenerator) {
+ // TODO(johnniwinther): Use this code path for all generators. Currently
+ // TypeUseGenerator starts complaining about `Map<` not being a method
+ // instead of `<` not defined on `Type`.
+ Object result = left.buildPropertyAccess(
+ new SendAccessGenerator(this, token, name,
+ forest.createArguments(fileOffset, <Expression>[right]),
+ isPotentiallyConstant: true),
+ fileOffset,
+ false);
+ push(negate ? forest.createNot(fileOffset, toValue(result)) : result);
} else {
+ bool isSuper = false;
+ Expression leftValue;
+ if (left is ThisAccessGenerator && left.isSuper) {
+ ThisAccessGenerator thisAccessorReceiver = left;
+ isSuper = true;
+ leftValue =
+ forest.createThisExpression(thisAccessorReceiver.fileOffset);
+ } else {
+ leftValue = toValue(left);
+ }
Expression result = buildMethodInvocation(
- a,
- new Name(operator),
- forest.createArguments(token.charOffset, <Expression>[b]),
+ leftValue,
+ name,
+ forest.createArguments(fileOffset, <Expression>[right]),
token.charOffset,
// This *could* be a constant expression, we can't know without
- // evaluating [a] and [b].
+ // evaluating [left] and [right].
isConstantExpression: !isSuper,
isSuper: isSuper);
- return negate ? forest.createNot(token.charOffset, result) : result;
+ push(negate ? forest.createNot(fileOffset, result) : result);
}
}
@@ -3341,29 +3361,51 @@
@override
void handleUnaryPrefixExpression(Token token) {
+ assert(checkState(token, <ValueKind>[
+ unionOfKinds(<ValueKind>[
+ ValueKind.Expression,
+ ValueKind.Generator,
+ ]),
+ ]));
debugEvent("UnaryPrefixExpression");
Object receiver = pop();
if (optional("!", token)) {
push(forest.createNot(offsetForToken(token), toValue(receiver)));
} else {
String operator = token.stringValue;
- Expression receiverValue;
if (optional("-", token)) {
operator = "unary-";
}
- bool isSuper = false;
- if (receiver is ThisAccessGenerator && receiver.isSuper) {
- isSuper = true;
- receiverValue = forest.createThisExpression(receiver.fileOffset);
+ int fileOffset = offsetForToken(token);
+ Name name = new Name(operator);
+ if (receiver is ExplicitExtensionAccessGenerator) {
+ // TODO(johnniwinther): Use this code path for all generators. Currently
+ // TypeUseGenerator starts complaining about `-Map` not being a method
+ // instead of `unary-` not defined on `Type`.
+ push(receiver.buildPropertyAccess(
+ new SendAccessGenerator(
+ this, token, name, forest.createArgumentsEmpty(fileOffset),
+ isPotentiallyConstant: true),
+ fileOffset,
+ false));
} else {
- receiverValue = toValue(receiver);
+ bool isSuper = false;
+ Expression receiverValue;
+ if (receiver is ThisAccessGenerator && receiver.isSuper) {
+ ThisAccessGenerator thisAccessorReceiver = receiver;
+ isSuper = true;
+ receiverValue =
+ forest.createThisExpression(thisAccessorReceiver.fileOffset);
+ } else {
+ receiverValue = toValue(receiver);
+ }
+ push(buildMethodInvocation(receiverValue, name,
+ forest.createArgumentsEmpty(fileOffset), fileOffset,
+ // This *could* be a constant expression, we can't know without
+ // evaluating [receiver].
+ isConstantExpression: !isSuper,
+ isSuper: isSuper));
}
- push(buildMethodInvocation(receiverValue, new Name(operator),
- forest.createArgumentsEmpty(noLocation), token.charOffset,
- // This *could* be a constant expression, we can't know without
- // evaluating [receiver].
- isConstantExpression: !isSuper,
- isSuper: isSuper));
}
}
diff --git a/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart b/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart
index 05fc8ed..9840652 100644
--- a/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart
@@ -14,6 +14,7 @@
import '../builder/builder.dart';
import '../builder/declaration_builder.dart';
import '../builder/extension_builder.dart';
+import '../builder/function_builder.dart';
import '../builder/invalid_type_declaration_builder.dart';
import '../builder/member_builder.dart';
import '../builder/named_type_builder.dart';
@@ -230,7 +231,8 @@
if (send is SendAccessGenerator) {
return _helper.buildMethodInvocation(buildSimpleRead(), send.name,
send.arguments, offsetForToken(send.token),
- isNullAware: isNullAware);
+ isNullAware: isNullAware,
+ isConstantExpression: send.isPotentiallyConstant);
} else {
if (_helper.constantContext != ConstantContext.none &&
send.name != lengthName) {
@@ -1645,7 +1647,8 @@
this.extensionThis,
this.extensionTypeParameters)
: assert(targetName != null),
- assert(readTarget != null || writeTarget != null),
+ assert(
+ readTarget != null || invokeTarget != null || writeTarget != null),
assert(extensionThis != null),
super(helper, token);
@@ -1682,6 +1685,9 @@
MemberBuilder procedureBuilder = getterBuilder;
readTarget = procedureBuilder.extensionTearOff;
invokeTarget = procedureBuilder.procedure;
+ } else if (getterBuilder is FunctionBuilder && getterBuilder.isOperator) {
+ assert(!getterBuilder.isStatic);
+ invokeTarget = getterBuilder.target;
}
}
Procedure writeTarget;
@@ -1956,7 +1962,8 @@
this.extensionTypeParameterCount,
{this.isNullAware})
: assert(targetName != null),
- assert(readTarget != null || writeTarget != null),
+ assert(
+ readTarget != null || invokeTarget != null || writeTarget != null),
assert(receiver != null),
assert(isNullAware != null),
super(helper, token);
@@ -1995,6 +2002,11 @@
readTarget = procedureBuilder.extensionTearOff;
invokeTarget = procedureBuilder.procedure;
targetName = procedureBuilder.name;
+ } else if (getterBuilder is FunctionBuilder && getterBuilder.isOperator) {
+ assert(!getterBuilder.isStatic);
+ MemberBuilder memberBuilder = getterBuilder;
+ invokeTarget = memberBuilder.member;
+ targetName = memberBuilder.name;
} else {
return unhandled(
"${getterBuilder.runtimeType}",
@@ -4386,8 +4398,11 @@
@override
final Arguments arguments;
+ final bool isPotentiallyConstant;
+
SendAccessGenerator(
- ExpressionGeneratorHelper helper, Token token, this.name, this.arguments)
+ ExpressionGeneratorHelper helper, Token token, this.name, this.arguments,
+ {this.isPotentiallyConstant: false})
: super(helper, token) {
assert(arguments != null);
}
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
index 35f613a..af09eba 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
@@ -1838,7 +1838,7 @@
receiverType != null,
"No receiver type provided for implicit extension member "
"invocation.");
- inferredType = receiverType;
+ return;
} else {
ExpressionInferenceResult result = inferExpression(
expression,
diff --git a/pkg/front_end/test/spell_checking_list_common.txt b/pkg/front_end/test/spell_checking_list_common.txt
index 58d08a6..188bf4d 100644
--- a/pkg/front_end/test/spell_checking_list_common.txt
+++ b/pkg/front_end/test/spell_checking_list_common.txt
@@ -484,6 +484,7 @@
compiles
compiling
complain
+complaining
complete
completed
completely
@@ -1168,6 +1169,7 @@
generating
generative
generator
+generators
generic
generics
get
diff --git a/pkg/front_end/testcases/extensions/issue38915.dart b/pkg/front_end/testcases/extensions/issue38915.dart
new file mode 100644
index 0000000..5d4752d
--- /dev/null
+++ b/pkg/front_end/testcases/extensions/issue38915.dart
@@ -0,0 +1,32 @@
+// Copyright (c) 2019, 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.
+
+class Class {}
+
+extension Extension on Class {
+ void method1({bool b = false, String s = ', '}) => null;
+ void method2([bool b = false, String s = ', ']) => null;
+ void method3(int i, {bool b = false, String s = ', '}) {}
+ void method4(int i, [bool b = false, String s = ', ']) {}
+}
+
+main() {
+ var c = new Class();
+ c.method1();
+ c.method1(s: 'foo');
+ c.method1(b: true);
+ c.method1(b: true, s: 'foo');
+ c.method1(s: 'foo', b: true);
+ c.method2();
+ c.method2(true);
+ c.method2(true, 'foo');
+ c.method3(42);
+ c.method3(42, s: 'foo');
+ c.method3(42, b: true);
+ c.method3(42, b: true, s: 'foo');
+ c.method3(42, s: 'foo', b: true);
+ c.method4(42);
+ c.method4(42, true);
+ c.method4(42, true, 'foo');
+}
diff --git a/pkg/front_end/testcases/extensions/issue38915.dart.outline.expect b/pkg/front_end/testcases/extensions/issue38915.dart.outline.expect
new file mode 100644
index 0000000..56b644e
--- /dev/null
+++ b/pkg/front_end/testcases/extensions/issue38915.dart.outline.expect
@@ -0,0 +1,36 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+class Class extends core::Object {
+ synthetic constructor •() → self::Class*
+ ;
+}
+extension Extension on self::Class* {
+ method method1 = self::Extension|method1;
+ tearoff method1 = self::Extension|get#method1;
+ method method2 = self::Extension|method2;
+ tearoff method2 = self::Extension|get#method2;
+ method method3 = self::Extension|method3;
+ tearoff method3 = self::Extension|get#method3;
+ method method4 = self::Extension|method4;
+ tearoff method4 = self::Extension|get#method4;
+}
+static method Extension|method1(final self::Class* #this, {core::bool* b, core::String* s}) → void
+ ;
+static method Extension|get#method1(final self::Class* #this) → ({b: core::bool*, s: core::String*}) →* void
+ return ({core::bool* b, core::String* s}) → void => self::Extension|method1(#this, b: b, s: s);
+static method Extension|method2(final self::Class* #this, [core::bool* b, core::String* s]) → void
+ ;
+static method Extension|get#method2(final self::Class* #this) → ([core::bool*, core::String*]) →* void
+ return ([core::bool* b, core::String* s]) → void => self::Extension|method2(#this, b, s);
+static method Extension|method3(final self::Class* #this, core::int* i, {core::bool* b, core::String* s}) → void
+ ;
+static method Extension|get#method3(final self::Class* #this) → (core::int*, {b: core::bool*, s: core::String*}) →* void
+ return (core::int* i, {core::bool* b, core::String* s}) → void => self::Extension|method3(#this, i, b: b, s: s);
+static method Extension|method4(final self::Class* #this, core::int* i, [core::bool* b, core::String* s]) → void
+ ;
+static method Extension|get#method4(final self::Class* #this) → (core::int*, [core::bool*, core::String*]) →* void
+ return (core::int* i, [core::bool* b, core::String* s]) → void => self::Extension|method4(#this, i, b, s);
+static method main() → dynamic
+ ;
diff --git a/pkg/front_end/testcases/extensions/issue38915.dart.strong.expect b/pkg/front_end/testcases/extensions/issue38915.dart.strong.expect
new file mode 100644
index 0000000..fdc8d05
--- /dev/null
+++ b/pkg/front_end/testcases/extensions/issue38915.dart.strong.expect
@@ -0,0 +1,57 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+class Class extends core::Object {
+ synthetic constructor •() → self::Class*
+ : super core::Object::•()
+ ;
+}
+extension Extension on self::Class* {
+ method method1 = self::Extension|method1;
+ tearoff method1 = self::Extension|get#method1;
+ method method2 = self::Extension|method2;
+ tearoff method2 = self::Extension|get#method2;
+ method method3 = self::Extension|method3;
+ tearoff method3 = self::Extension|get#method3;
+ method method4 = self::Extension|method4;
+ tearoff method4 = self::Extension|get#method4;
+}
+static method Extension|method1(final self::Class* #this, {core::bool* b = #C1, core::String* s = #C2}) → void
+ return null;
+static method Extension|get#method1(final self::Class* #this) → ({b: core::bool*, s: core::String*}) →* void
+ return ({core::bool* b = #C1, core::String* s = #C2}) → void => self::Extension|method1(#this, b: b, s: s);
+static method Extension|method2(final self::Class* #this, [core::bool* b = #C1, core::String* s = #C2]) → void
+ return null;
+static method Extension|get#method2(final self::Class* #this) → ([core::bool*, core::String*]) →* void
+ return ([core::bool* b = #C1, core::String* s = #C2]) → void => self::Extension|method2(#this, b, s);
+static method Extension|method3(final self::Class* #this, core::int* i, {core::bool* b = #C1, core::String* s = #C2}) → void {}
+static method Extension|get#method3(final self::Class* #this) → (core::int*, {b: core::bool*, s: core::String*}) →* void
+ return (core::int* i, {core::bool* b = #C1, core::String* s = #C2}) → void => self::Extension|method3(#this, i, b: b, s: s);
+static method Extension|method4(final self::Class* #this, core::int* i, [core::bool* b = #C1, core::String* s = #C2]) → void {}
+static method Extension|get#method4(final self::Class* #this) → (core::int*, [core::bool*, core::String*]) →* void
+ return (core::int* i, [core::bool* b = #C1, core::String* s = #C2]) → void => self::Extension|method4(#this, i, b, s);
+static method main() → dynamic {
+ self::Class* c = new self::Class::•();
+ self::Extension|method1(c);
+ self::Extension|method1(c, s: "foo");
+ self::Extension|method1(c, b: true);
+ self::Extension|method1(c, b: true, s: "foo");
+ self::Extension|method1(c, s: "foo", b: true);
+ self::Extension|method2(c);
+ self::Extension|method2(c, true);
+ self::Extension|method2(c, true, "foo");
+ self::Extension|method3(c, 42);
+ self::Extension|method3(c, 42, s: "foo");
+ self::Extension|method3(c, 42, b: true);
+ self::Extension|method3(c, 42, b: true, s: "foo");
+ self::Extension|method3(c, 42, s: "foo", b: true);
+ self::Extension|method4(c, 42);
+ self::Extension|method4(c, 42, true);
+ self::Extension|method4(c, 42, true, "foo");
+}
+
+constants {
+ #C1 = false
+ #C2 = ", "
+}
diff --git a/pkg/front_end/testcases/extensions/issue38915.dart.strong.transformed.expect b/pkg/front_end/testcases/extensions/issue38915.dart.strong.transformed.expect
new file mode 100644
index 0000000..fdc8d05
--- /dev/null
+++ b/pkg/front_end/testcases/extensions/issue38915.dart.strong.transformed.expect
@@ -0,0 +1,57 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+class Class extends core::Object {
+ synthetic constructor •() → self::Class*
+ : super core::Object::•()
+ ;
+}
+extension Extension on self::Class* {
+ method method1 = self::Extension|method1;
+ tearoff method1 = self::Extension|get#method1;
+ method method2 = self::Extension|method2;
+ tearoff method2 = self::Extension|get#method2;
+ method method3 = self::Extension|method3;
+ tearoff method3 = self::Extension|get#method3;
+ method method4 = self::Extension|method4;
+ tearoff method4 = self::Extension|get#method4;
+}
+static method Extension|method1(final self::Class* #this, {core::bool* b = #C1, core::String* s = #C2}) → void
+ return null;
+static method Extension|get#method1(final self::Class* #this) → ({b: core::bool*, s: core::String*}) →* void
+ return ({core::bool* b = #C1, core::String* s = #C2}) → void => self::Extension|method1(#this, b: b, s: s);
+static method Extension|method2(final self::Class* #this, [core::bool* b = #C1, core::String* s = #C2]) → void
+ return null;
+static method Extension|get#method2(final self::Class* #this) → ([core::bool*, core::String*]) →* void
+ return ([core::bool* b = #C1, core::String* s = #C2]) → void => self::Extension|method2(#this, b, s);
+static method Extension|method3(final self::Class* #this, core::int* i, {core::bool* b = #C1, core::String* s = #C2}) → void {}
+static method Extension|get#method3(final self::Class* #this) → (core::int*, {b: core::bool*, s: core::String*}) →* void
+ return (core::int* i, {core::bool* b = #C1, core::String* s = #C2}) → void => self::Extension|method3(#this, i, b: b, s: s);
+static method Extension|method4(final self::Class* #this, core::int* i, [core::bool* b = #C1, core::String* s = #C2]) → void {}
+static method Extension|get#method4(final self::Class* #this) → (core::int*, [core::bool*, core::String*]) →* void
+ return (core::int* i, [core::bool* b = #C1, core::String* s = #C2]) → void => self::Extension|method4(#this, i, b, s);
+static method main() → dynamic {
+ self::Class* c = new self::Class::•();
+ self::Extension|method1(c);
+ self::Extension|method1(c, s: "foo");
+ self::Extension|method1(c, b: true);
+ self::Extension|method1(c, b: true, s: "foo");
+ self::Extension|method1(c, s: "foo", b: true);
+ self::Extension|method2(c);
+ self::Extension|method2(c, true);
+ self::Extension|method2(c, true, "foo");
+ self::Extension|method3(c, 42);
+ self::Extension|method3(c, 42, s: "foo");
+ self::Extension|method3(c, 42, b: true);
+ self::Extension|method3(c, 42, b: true, s: "foo");
+ self::Extension|method3(c, 42, s: "foo", b: true);
+ self::Extension|method4(c, 42);
+ self::Extension|method4(c, 42, true);
+ self::Extension|method4(c, 42, true, "foo");
+}
+
+constants {
+ #C1 = false
+ #C2 = ", "
+}
diff --git a/pkg/front_end/testcases/extensions/operators.dart b/pkg/front_end/testcases/extensions/operators.dart
index cbb3b41..6927bfe 100644
--- a/pkg/front_end/testcases/extensions/operators.dart
+++ b/pkg/front_end/testcases/extensions/operators.dart
@@ -39,6 +39,11 @@
}
main() {
+ implicit();
+ explicit();
+}
+
+implicit() {
Complex c_m2 = new Complex(-2, 2);
Complex c_m1 = new Complex(-1, 1);
Complex c0 = new Complex(0, 0);
@@ -77,8 +82,47 @@
expect(c0, -c0);
}
+explicit() {
+ Complex c_m2 = new Complex(-2, 2);
+ Complex c_m1 = new Complex(-1, 1);
+ Complex c0 = new Complex(0, 0);
+ Complex c1 = new Complex(1, -1);
+ Complex c2 = new Complex(2, -2);
+
+ expect(c_m2, Operators(c0) + c_m2);
+ expect(c_m2, Operators(c_m2) + c0);
+ expect(c_m2, Operators(c_m1) + c_m1);
+ expect(c_m1, Operators(c0) + c_m1);
+ expect(c_m1, Operators(c_m1) + c0);
+ expect(c0, Operators(c_m2) + c2);
+ expect(c0, Operators(c2) + c_m2);
+ expect(c0, Operators(c_m1) + c1);
+ expect(c0, Operators(c1) + c_m1);
+ expect(c0, Operators(c0) + c0);
+ expect(c1, Operators(c0) + c1);
+ expect(c1, Operators(c1) + c0);
+ expect(c2, Operators(c0) + c2);
+ expect(c2, Operators(c2) + c0);
+ expect(c2, Operators(c1) + c1);
+
+ expect(c_m2, Operators(c0) - c2);
+ expect(c2, Operators(c2) - c0);
+ expect(c_m2, -Operators(c2));
+ expect(c_m1, Operators(c1) - c2);
+ expect(c1, Operators(c2) - c1);
+ expect(c_m1, Operators(c0) - c1);
+ expect(c1, Operators(c1) - c0);
+ expect(c_m1, -Operators(c1));
+ expect(c0, Operators(c2) - c2);
+ expect(c0, Operators(c1) - c1);
+ expect(c0, Operators(c0) - c0);
+ expect(c0, Operators(c_m1) - c_m1);
+ expect(c0, Operators(c_m2) - c_m2);
+ expect(c0, -c0);
+}
+
expect(expected, actual) {
if (expected != actual) {
throw 'Mismatch: expected=$expected, actual=$actual';
}
-}
\ No newline at end of file
+}
diff --git a/pkg/front_end/testcases/extensions/operators.dart.outline.expect b/pkg/front_end/testcases/extensions/operators.dart.outline.expect
index bfa897a..c38c397 100644
--- a/pkg/front_end/testcases/extensions/operators.dart.outline.expect
+++ b/pkg/front_end/testcases/extensions/operators.dart.outline.expect
@@ -34,5 +34,9 @@
;
static method main() → dynamic
;
+static method implicit() → dynamic
+ ;
+static method explicit() → dynamic
+ ;
static method expect(dynamic expected, dynamic actual) → dynamic
;
diff --git a/pkg/front_end/testcases/extensions/operators.dart.strong.expect b/pkg/front_end/testcases/extensions/operators.dart.strong.expect
index 936cc18..3453a24 100644
--- a/pkg/front_end/testcases/extensions/operators.dart.strong.expect
+++ b/pkg/front_end/testcases/extensions/operators.dart.strong.expect
@@ -39,6 +39,46 @@
static method Operators|unary-(final self::Complex* #this) → self::Complex*
return #this.{self::Complex::negate}();
static method main() → dynamic {
+ self::implicit();
+ self::explicit();
+}
+static method implicit() → dynamic {
+ self::Complex* c_m2 = new self::Complex::•(-2.0, 2.0);
+ self::Complex* c_m1 = new self::Complex::•(-1.0, 1.0);
+ self::Complex* c0 = new self::Complex::•(0.0, 0.0);
+ self::Complex* c1 = new self::Complex::•(1.0, -1.0);
+ self::Complex* c2 = new self::Complex::•(2.0, -2.0);
+ self::expect(c_m2, self::Operators|+(c0, c_m2));
+ self::expect(c_m2, self::Operators|+(c_m2, c0));
+ self::expect(c_m2, self::Operators|+(c_m1, c_m1));
+ self::expect(c_m1, self::Operators|+(c0, c_m1));
+ self::expect(c_m1, self::Operators|+(c_m1, c0));
+ self::expect(c0, self::Operators|+(c_m2, c2));
+ self::expect(c0, self::Operators|+(c2, c_m2));
+ self::expect(c0, self::Operators|+(c_m1, c1));
+ self::expect(c0, self::Operators|+(c1, c_m1));
+ self::expect(c0, self::Operators|+(c0, c0));
+ self::expect(c1, self::Operators|+(c0, c1));
+ self::expect(c1, self::Operators|+(c1, c0));
+ self::expect(c2, self::Operators|+(c0, c2));
+ self::expect(c2, self::Operators|+(c2, c0));
+ self::expect(c2, self::Operators|+(c1, c1));
+ self::expect(c_m2, self::Operators|-(c0, c2));
+ self::expect(c2, self::Operators|-(c2, c0));
+ self::expect(c_m2, self::Operators|unary-(c2));
+ self::expect(c_m1, self::Operators|-(c1, c2));
+ self::expect(c1, self::Operators|-(c2, c1));
+ self::expect(c_m1, self::Operators|-(c0, c1));
+ self::expect(c1, self::Operators|-(c1, c0));
+ self::expect(c_m1, self::Operators|unary-(c1));
+ self::expect(c0, self::Operators|-(c2, c2));
+ self::expect(c0, self::Operators|-(c1, c1));
+ self::expect(c0, self::Operators|-(c0, c0));
+ self::expect(c0, self::Operators|-(c_m1, c_m1));
+ self::expect(c0, self::Operators|-(c_m2, c_m2));
+ self::expect(c0, self::Operators|unary-(c0));
+}
+static method explicit() → dynamic {
self::Complex* c_m2 = new self::Complex::•(-2.0, 2.0);
self::Complex* c_m1 = new self::Complex::•(-1.0, 1.0);
self::Complex* c0 = new self::Complex::•(0.0, 0.0);
diff --git a/pkg/front_end/testcases/extensions/operators.dart.strong.transformed.expect b/pkg/front_end/testcases/extensions/operators.dart.strong.transformed.expect
index 936cc18..3453a24 100644
--- a/pkg/front_end/testcases/extensions/operators.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/extensions/operators.dart.strong.transformed.expect
@@ -39,6 +39,46 @@
static method Operators|unary-(final self::Complex* #this) → self::Complex*
return #this.{self::Complex::negate}();
static method main() → dynamic {
+ self::implicit();
+ self::explicit();
+}
+static method implicit() → dynamic {
+ self::Complex* c_m2 = new self::Complex::•(-2.0, 2.0);
+ self::Complex* c_m1 = new self::Complex::•(-1.0, 1.0);
+ self::Complex* c0 = new self::Complex::•(0.0, 0.0);
+ self::Complex* c1 = new self::Complex::•(1.0, -1.0);
+ self::Complex* c2 = new self::Complex::•(2.0, -2.0);
+ self::expect(c_m2, self::Operators|+(c0, c_m2));
+ self::expect(c_m2, self::Operators|+(c_m2, c0));
+ self::expect(c_m2, self::Operators|+(c_m1, c_m1));
+ self::expect(c_m1, self::Operators|+(c0, c_m1));
+ self::expect(c_m1, self::Operators|+(c_m1, c0));
+ self::expect(c0, self::Operators|+(c_m2, c2));
+ self::expect(c0, self::Operators|+(c2, c_m2));
+ self::expect(c0, self::Operators|+(c_m1, c1));
+ self::expect(c0, self::Operators|+(c1, c_m1));
+ self::expect(c0, self::Operators|+(c0, c0));
+ self::expect(c1, self::Operators|+(c0, c1));
+ self::expect(c1, self::Operators|+(c1, c0));
+ self::expect(c2, self::Operators|+(c0, c2));
+ self::expect(c2, self::Operators|+(c2, c0));
+ self::expect(c2, self::Operators|+(c1, c1));
+ self::expect(c_m2, self::Operators|-(c0, c2));
+ self::expect(c2, self::Operators|-(c2, c0));
+ self::expect(c_m2, self::Operators|unary-(c2));
+ self::expect(c_m1, self::Operators|-(c1, c2));
+ self::expect(c1, self::Operators|-(c2, c1));
+ self::expect(c_m1, self::Operators|-(c0, c1));
+ self::expect(c1, self::Operators|-(c1, c0));
+ self::expect(c_m1, self::Operators|unary-(c1));
+ self::expect(c0, self::Operators|-(c2, c2));
+ self::expect(c0, self::Operators|-(c1, c1));
+ self::expect(c0, self::Operators|-(c0, c0));
+ self::expect(c0, self::Operators|-(c_m1, c_m1));
+ self::expect(c0, self::Operators|-(c_m2, c_m2));
+ self::expect(c0, self::Operators|unary-(c0));
+}
+static method explicit() → dynamic {
self::Complex* c_m2 = new self::Complex::•(-2.0, 2.0);
self::Complex* c_m1 = new self::Complex::•(-1.0, 1.0);
self::Complex* c0 = new self::Complex::•(0.0, 0.0);
diff --git a/pkg/front_end/testcases/text_serialization.status b/pkg/front_end/testcases/text_serialization.status
index c15719f..d617f89 100644
--- a/pkg/front_end/testcases/text_serialization.status
+++ b/pkg/front_end/testcases/text_serialization.status
@@ -47,6 +47,7 @@
extensions/issue38745: TextSerializationFailure
extensions/issue38750: TextSerializationFailure
extensions/issue38755: TextSerializationFailure
+extensions/issue38915: TextSerializationFailure
extensions/nested_on_types: TextSerializationFailure
extensions/null_aware: TextSerializationFailure
extensions/on_function_type: TextSerializationFailure
diff --git a/pkg/vm/lib/transformations/ffi_use_sites.dart b/pkg/vm/lib/transformations/ffi_use_sites.dart
index 3cb0dfc..4bf6586 100644
--- a/pkg/vm/lib/transformations/ffi_use_sites.dart
+++ b/pkg/vm/lib/transformations/ffi_use_sites.dart
@@ -344,17 +344,18 @@
}
void _ensureNativeTypeToDartType(
- DartType containerTypeArg, DartType elementType, Expression node,
+ DartType nativeType, DartType dartType, Expression node,
{bool allowStructs: false}) {
- final DartType shouldBeElementType =
- convertNativeTypeToDartType(containerTypeArg, allowStructs);
- if (elementType == shouldBeElementType) return;
- // We disable implicit downcasts, they will go away when NNBD lands.
- if (env.isSubtypeOf(elementType, shouldBeElementType,
- SubtypeCheckMode.ignoringNullabilities)) return;
+ final DartType correspondingDartType =
+ convertNativeTypeToDartType(nativeType, allowStructs);
+ if (dartType == correspondingDartType) return;
+ if (env.isSubtypeOf(correspondingDartType, dartType,
+ SubtypeCheckMode.ignoringNullabilities)) {
+ return;
+ }
diagnosticReporter.report(
templateFfiTypeMismatch.withArguments(
- elementType, shouldBeElementType, containerTypeArg),
+ dartType, correspondingDartType, nativeType),
node.fileOffset,
1,
node.location.file);
diff --git a/pkg/vm/lib/transformations/type_flow/transformer.dart b/pkg/vm/lib/transformations/type_flow/transformer.dart
index f634d26..bde9684 100644
--- a/pkg/vm/lib/transformations/type_flow/transformer.dart
+++ b/pkg/vm/lib/transformations/type_flow/transformer.dart
@@ -929,6 +929,8 @@
return !shaker.isClassUsed(node);
} else if (node is Typedef) {
return !shaker.isTypedefUsed(node);
+ } else if (node is Extension) {
+ return !shaker.isExtensionUsed(node);
} else {
return !shaker.isMemberUsed(node as Member);
}
diff --git a/runtime/bin/ffi_test/ffi_test_functions.cc b/runtime/bin/ffi_test/ffi_test_functions.cc
index 85145b1..784093e 100644
--- a/runtime/bin/ffi_test/ffi_test_functions.cc
+++ b/runtime/bin/ffi_test/ffi_test_functions.cc
@@ -855,4 +855,31 @@
#endif // defined(TARGET_OS_LINUX)
+// Receives some pointer (Pointer<NativeType> in Dart) and writes some bits.
+DART_EXPORT void NativeTypePointerParam(void* p) {
+ uint8_t* p2 = reinterpret_cast<uint8_t*>(p);
+ p2[0] = 42;
+}
+
+// Manufactures some pointer (Pointer<NativeType> in Dart) with a bogus address.
+DART_EXPORT void* NativeTypePointerReturn() {
+ uint64_t bogus_address = 0x13370000;
+ return reinterpret_cast<void*>(bogus_address);
+}
+
+// Passes some pointer (Pointer<NativeType> in Dart) to Dart as argument.
+DART_EXPORT void CallbackNativeTypePointerParam(void (*f)(void*)) {
+ void* pointer = malloc(sizeof(int64_t));
+ f(pointer);
+ free(pointer);
+}
+
+// Receives some pointer (Pointer<NativeType> in Dart) from Dart as return
+// value.
+DART_EXPORT void CallbackNativeTypePointerReturn(void* (*f)()) {
+ void* p = f();
+ uint8_t* p2 = reinterpret_cast<uint8_t*>(p);
+ p2[0] = 42;
+}
+
} // namespace dart
diff --git a/runtime/bin/file_android.cc b/runtime/bin/file_android.cc
index 6fa50ad..681c46f 100644
--- a/runtime/bin/file_android.cc
+++ b/runtime/bin/file_android.cc
@@ -597,11 +597,16 @@
errno = ENOENT;
return -1;
}
- const size_t target_size =
+ size_t target_size =
TEMP_FAILURE_RETRY(readlink(pathname, result, result_size));
if (target_size <= 0) {
return -1;
}
+ // readlink returns non-zero terminated strings. Append.
+ if (target_size < result_size) {
+ result[target_size] = '\0';
+ target_size++;
+ }
return target_size;
}
@@ -615,10 +620,9 @@
if (target_size <= 0) {
return NULL;
}
- char* target_name = DartUtils::ScopedCString(target_size + 1);
+ char* target_name = DartUtils::ScopedCString(target_size);
ASSERT(target_name != NULL);
memmove(target_name, target, target_size);
- target_name[target_size] = '\0';
return target_name;
}
diff --git a/runtime/bin/file_linux.cc b/runtime/bin/file_linux.cc
index 190b195..e478173 100644
--- a/runtime/bin/file_linux.cc
+++ b/runtime/bin/file_linux.cc
@@ -590,11 +590,16 @@
errno = ENOENT;
return -1;
}
- const size_t target_size =
+ size_t target_size =
TEMP_FAILURE_RETRY(readlink(pathname, result, result_size));
if (target_size <= 0) {
return -1;
}
+ // readlink returns non-zero terminated strings. Append.
+ if (target_size < result_size) {
+ result[target_size] = '\0';
+ target_size++;
+ }
return target_size;
}
@@ -608,10 +613,9 @@
if (target_size <= 0) {
return NULL;
}
- char* target_name = DartUtils::ScopedCString(target_size + 1);
+ char* target_name = DartUtils::ScopedCString(target_size);
ASSERT(target_name != NULL);
memmove(target_name, target, target_size);
- target_name[target_size] = '\0';
return target_name;
}
diff --git a/runtime/bin/main.cc b/runtime/bin/main.cc
index fbc69b3..8e76803 100644
--- a/runtime/bin/main.cc
+++ b/runtime/bin/main.cc
@@ -1088,16 +1088,21 @@
// snapshot, load and run that.
// Any arguments passed to such an executable are meant for the actual
// application so skip all Dart VM flag parsing.
- app_snapshot = Snapshot::TryReadAppendedAppSnapshotElf(argv[0]);
- if (app_snapshot != nullptr) {
- script_name = argv[0];
- // Store the executable name.
- Platform::SetExecutableName(argv[0]);
+ const size_t kPathBufSize = PATH_MAX + 1;
+ char executable_path[kPathBufSize];
+ if (Platform::ResolveExecutablePathInto(executable_path, kPathBufSize) > 0) {
+ app_snapshot = Snapshot::TryReadAppendedAppSnapshotElf(executable_path);
+ if (app_snapshot != nullptr) {
+ script_name = argv[0];
- // Parse out options to be passed to dart main.
- for (int i = 1; i < argc; i++) {
- dart_options.AddArgument(argv[i]);
+ // Store the executable name.
+ Platform::SetExecutableName(argv[0]);
+
+ // Parse out options to be passed to dart main.
+ for (int i = 1; i < argc; i++) {
+ dart_options.AddArgument(argv[i]);
+ }
}
}
#endif
diff --git a/runtime/bin/platform.h b/runtime/bin/platform.h
index b0b3aa2..50b9a10 100644
--- a/runtime/bin/platform.h
+++ b/runtime/bin/platform.h
@@ -64,13 +64,12 @@
static const char* ResolveExecutablePath();
- // On Linux and Android this has the same effect as calling
- // ResolveExecutablePath except that Dart_ScopeAllocate is not called and that
- // the result goes into the given parameters.
- // On all other platforms it returns -1, i.e. doesn't work.
+ // This has the same effect as calling ResolveExecutablePath except that
+ // Dart_ScopeAllocate is not called and that the result goes into the given
+ // parameters.
+ // WARNING: On Fuchsia it returns -1, i.e. doesn't work.
// Note that `result` should be pre-allocated with size `result_size`.
// The return-value is the length read into `result` or -1 on failure.
- // The content of `result` is not guranteed to be null-terminated.
static intptr_t ResolveExecutablePathInto(char* result, size_t result_size);
// Stores the executable name.
diff --git a/runtime/bin/platform_macos.cc b/runtime/bin/platform_macos.cc
index 2be3f30..bacb7a75 100644
--- a/runtime/bin/platform_macos.cc
+++ b/runtime/bin/platform_macos.cc
@@ -252,7 +252,18 @@
}
intptr_t Platform::ResolveExecutablePathInto(char* result, size_t result_size) {
- return -1;
+ // Get the required length of the buffer.
+ uint32_t path_size = 0;
+ if (_NSGetExecutablePath(nullptr, &path_size) == 0) {
+ return -1;
+ }
+ if (path_size > result_size) {
+ return -1;
+ }
+ if (_NSGetExecutablePath(result, &path_size) != 0) {
+ return -1;
+ }
+ return path_size;
}
void Platform::Exit(int exit_code) {
diff --git a/runtime/bin/platform_win.cc b/runtime/bin/platform_win.cc
index e4f20a2..552e3c2 100644
--- a/runtime/bin/platform_win.cc
+++ b/runtime/bin/platform_win.cc
@@ -275,7 +275,7 @@
// Ensure no last error before calling GetModuleFileNameW.
SetLastError(ERROR_SUCCESS);
// Get the required length of the buffer.
- int path_length = GetModuleFileNameW(NULL, tmp_buffer, kTmpBufferSize);
+ GetModuleFileNameW(nullptr, tmp_buffer, kTmpBufferSize);
if (GetLastError() != ERROR_SUCCESS) {
return NULL;
}
@@ -286,6 +286,20 @@
}
intptr_t Platform::ResolveExecutablePathInto(char* result, size_t result_size) {
+ // Ensure no last error before calling GetModuleFileNameW.
+ SetLastError(ERROR_SUCCESS);
+ const int kTmpBufferSize = 32768;
+ wchar_t tmp_buffer[kTmpBufferSize];
+ // Get the required length of the buffer.
+ GetModuleFileNameW(nullptr, tmp_buffer, kTmpBufferSize);
+ if (GetLastError() != ERROR_SUCCESS) {
+ return -1;
+ }
+ WideToUtf8Scope wide_to_utf8_scope(tmp_buffer);
+ if (wide_to_utf8_scope.length() <= result_size) {
+ strncpy(result, wide_to_utf8_scope.utf8(), result_size);
+ return wide_to_utf8_scope.length();
+ }
return -1;
}
diff --git a/runtime/platform/globals.h b/runtime/platform/globals.h
index 2dd7547..d754abf 100644
--- a/runtime/platform/globals.h
+++ b/runtime/platform/globals.h
@@ -722,6 +722,11 @@
#define STDERR_FILENO 2
#endif
+#ifndef PATH_MAX
+// Most platforms use PATH_MAX, but in Windows it's called MAX_PATH.
+#define PATH_MAX MAX_PATH
+#endif
+
} // namespace dart
#endif // RUNTIME_PLATFORM_GLOBALS_H_
diff --git a/runtime/tests/vm/dart/run_appended_aot_snapshot_test.dart b/runtime/tests/vm/dart/run_appended_aot_snapshot_test.dart
index 1594605..26b7a9c 100644
--- a/runtime/tests/vm/dart/run_appended_aot_snapshot_test.dart
+++ b/runtime/tests/vm/dart/run_appended_aot_snapshot_test.dart
@@ -22,9 +22,10 @@
'runtime', 'tests', 'vm', 'dart', 'run_appended_aot_snapshot_test.dart');
await withTempDir((String tmp) async {
+ final String exeName = 'test.exe';
final String dillPath = path.join(tmp, 'test.dill');
final String aotPath = path.join(tmp, 'test.aot');
- final String exePath = path.join(tmp, 'test.exe');
+ final String exePath = path.join(tmp, exeName);
{
final result = await generateAotKernel(checkedInDartVM, genKernel,
@@ -51,8 +52,33 @@
Expect.equals(result.stdout, '');
}
- final runResult =
- await runBinary('run appended aot snapshot', exePath, ['--child']);
- expectOutput('Hello, Appended AOT', runResult);
+ {
+ final runResult =
+ await runBinary('run appended aot snapshot', exePath, ['--child']);
+ expectOutput('Hello, Appended AOT', runResult);
+ }
+
+ {
+ // Test that it runs when invoked via PATH as well.
+ Map<String, String> environment = {'PATH': tmp};
+ final runResult = await runBinary(
+ 'run appended aot snapshot from PATH', exeName, ['--child'],
+ environment: environment, runInShell: true);
+ expectOutput('Hello, Appended AOT', runResult);
+ }
+
+ // Windows allows leaving out .exe. Make sure we can load that as well.
+ if (Platform.isWindows) {
+ final String exeNameWithoutExt =
+ exeName.replaceFirst(new RegExp(r'.exe$'), '');
+ Map<String, String> environment = {'PATH': tmp};
+ final runResult = await runBinary(
+ 'run appended aot snapshot without extension',
+ exeNameWithoutExt,
+ ['--child'],
+ environment: environment,
+ runInShell: true);
+ expectOutput('Hello, Appended AOT', runResult);
+ }
});
}
diff --git a/runtime/tests/vm/dart/snapshot_test_helper.dart b/runtime/tests/vm/dart/snapshot_test_helper.dart
index 6f42b07..8ffd309 100644
--- a/runtime/tests/vm/dart/snapshot_test_helper.dart
+++ b/runtime/tests/vm/dart/snapshot_test_helper.dart
@@ -72,10 +72,11 @@
return runBinary(prefix, genSnapshot, arguments);
}
-Future<Result> runBinary(
- String prefix, String binary, List<String> arguments) async {
+Future<Result> runBinary(String prefix, String binary, List<String> arguments,
+ {Map<String, String> environment, bool runInShell: false}) async {
print("+ $binary " + arguments.join(" "));
- final processResult = await Process.run(binary, arguments);
+ final processResult = await Process.run(binary, arguments,
+ environment: environment, runInShell: runInShell);
final result =
new Result('[$prefix] ${binary} ${arguments.join(' ')}', processResult);
diff --git a/tests/ffi/function_test.dart b/tests/ffi/function_test.dart
index 51cc2fe..38c36a9 100644
--- a/tests/ffi/function_test.dart
+++ b/tests/ffi/function_test.dart
@@ -13,8 +13,6 @@
// VMOptions=--write-protect-code --no-dual-map-code --stacktrace-every=100
// SharedObjects=ffi_test_functions
-library FfiTest;
-
import 'dart:ffi';
import 'dylib_utils.dart';
@@ -44,6 +42,7 @@
testVoidReturn();
testNoArgs();
testException();
+ testLookupFunctionPointerNativeType();
}
}
@@ -405,6 +404,17 @@
Expect.approxEquals(1337.0, result);
}
+typedef NativeTypeNFT = Pointer<NativeType> Function(
+ Pointer<Pointer<NativeType>>, Int8);
+typedef NativeTypeFT = Pointer<NativeType> Function(
+ Pointer<Pointer<NativeType>>, int);
+
+void testLookupFunctionPointerNativeType() {
+ // The function signature does not match up, but that does not matter since
+ // this test does not use the trampoline.
+ ffiTestFunctions.lookupFunction<NativeTypeNFT, NativeTypeFT>("LargePointer");
+}
+
// Throw an exception from within the trampoline and collect a stacktrace
// include its frame.
void testException() {
diff --git a/tests/ffi/variance_function_checks_test.dart b/tests/ffi/variance_function_checks_test.dart
new file mode 100644
index 0000000..c17488c
--- /dev/null
+++ b/tests/ffi/variance_function_checks_test.dart
@@ -0,0 +1,67 @@
+// Copyright (c) 2019, 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.
+//
+// SharedObjects=ffi_test_functions
+
+import 'dart:ffi';
+
+import 'dylib_utils.dart';
+
+// ============================================
+// Tests checks on Dart to native (asFunction).
+// ============================================
+
+typedef Int64PointerParamOpDart = void Function(Pointer<Int64>);
+typedef Int64PointerParamOp = Void Function(Pointer<Int64>);
+typedef Int64PointerReturnOp = Pointer<Int64> Function();
+typedef NaTyPointerReturnOp = Pointer<NativeType> Function();
+
+final paramOpName = "NativeTypePointerParam";
+final returnOpName = "NativeTypePointerReturn";
+
+final DynamicLibrary ffiTestFunctions =
+ dlopenPlatformSpecific("ffi_test_functions");
+
+final p1 =
+ ffiTestFunctions.lookup<NativeFunction<Int64PointerParamOp>>(paramOpName);
+final nonInvariantBinding1 = //# 1: compile-time error
+ p1.asFunction<NaTyPointerParamOpDart>(); //# 1: continued
+
+final p2 =
+ ffiTestFunctions.lookup<NativeFunction<NaTyPointerReturnOp>>(returnOpName);
+final nonInvariantBinding2 = //# 2: compile-time error
+ p2.asFunction<Int64PointerReturnOp>(); //# 2: continued
+
+final p3 = Pointer<
+ NativeFunction<
+ Pointer<NativeFunction<Pointer<NativeType> Function()>>
+ Function()>>.fromAddress(0x1234);
+final f3 = p3 //# 10: compile-time error
+ .asFunction< //# 10: continued
+ Pointer< //# 10: continued
+ NativeFunction< //# 10: continued
+ Pointer<Int8> Function()>> //# 10: continued
+ Function()>(); //# 10: continued
+
+// ===========================================================
+// Test check on callbacks from native to Dart (fromFunction).
+// ===========================================================
+
+void naTyPointerParamOp(Pointer<NativeType> p) {
+ final Pointer<Int8> asInt8 = p.cast();
+ asInt8.value = 42;
+}
+
+Pointer<Int64> int64PointerReturnOp() {
+ return Pointer.fromAddress(0x13370000);
+}
+
+final implictDowncast1 = //# 3: compile-time error
+ Pointer.fromFunction<Int64PointerParamOp>(//# 3: continued
+ naTyPointerParamOp); //# 3: continued
+final implictDowncast2 = //# 4: compile-time error
+ Pointer.fromFunction<NaTyPointerReturnOp>(//# 4: continued
+ int64PointerReturnOp); //# 4: continued
+
+void main() {}
diff --git a/tests/ffi/variance_function_test.dart b/tests/ffi/variance_function_test.dart
new file mode 100644
index 0000000..d6e6378
--- /dev/null
+++ b/tests/ffi/variance_function_test.dart
@@ -0,0 +1,238 @@
+// Copyright (c) 2019, 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.
+//
+// VMOptions=
+// VMOptions=--deterministic --optimization-counter-threshold=10
+// VMOptions=--use-slow-path
+// SharedObjects=ffi_test_functions
+//
+// This file tests subtyping relationships (at compile time and at runtime) of
+// parameters and return types of ffi trampolines and ffi callback trampolines.
+
+import 'dart:ffi';
+
+import 'dylib_utils.dart';
+
+import "package:expect/expect.dart";
+import "package:ffi/ffi.dart";
+
+typedef Int64PointerParamOpDart = void Function(Pointer<Int64>);
+typedef Int64PointerParamOp = Void Function(Pointer<Int64>);
+typedef NaTyPointerParamOpDart = void Function(Pointer<NativeType>);
+typedef NaTyPointerParamOp = Void Function(Pointer<NativeType>);
+typedef Int64PointerReturnOp = Pointer<Int64> Function();
+typedef NaTyPointerReturnOp = Pointer<NativeType> Function();
+
+final paramOpName = "NativeTypePointerParam";
+final returnOpName = "NativeTypePointerReturn";
+
+final DynamicLibrary ffiTestFunctions =
+ dlopenPlatformSpecific("ffi_test_functions");
+
+// =============================================
+// Tests calls from Dart to native (asFunction).
+// =============================================
+
+void paramInvariant1() {
+ final fp =
+ ffiTestFunctions.lookup<NativeFunction<Int64PointerParamOp>>(paramOpName);
+ final f = fp.asFunction<Int64PointerParamOpDart>();
+ final arg = allocate<Int64>();
+ f(arg);
+ free(arg);
+}
+
+void paramInvariant2() {
+ final fp =
+ ffiTestFunctions.lookup<NativeFunction<NaTyPointerParamOp>>(paramOpName);
+ final f = fp.asFunction<NaTyPointerParamOpDart>();
+ final arg = allocate<Int64>().cast<NativeType>();
+ Expect.type<Pointer<NativeType>>(arg);
+ f(arg);
+ free(arg);
+}
+
+// Pass a statically and dynamically subtyped argument.
+void paramSubtype1() {
+ final fp =
+ ffiTestFunctions.lookup<NativeFunction<NaTyPointerParamOp>>(paramOpName);
+ final f = fp.asFunction<NaTyPointerParamOpDart>();
+ final arg = allocate<Int64>();
+ Expect.type<Pointer<Int64>>(arg);
+ f(arg);
+ free(arg);
+}
+
+// Pass a statically subtyped but dynamically invariant argument.
+void paramSubtype2() {
+ final fp =
+ ffiTestFunctions.lookup<NativeFunction<NaTyPointerParamOp>>(paramOpName);
+ final f = fp.asFunction<NaTyPointerParamOpDart>();
+ final Pointer<NativeType> arg = allocate<Int64>();
+ Expect.type<Pointer<Int64>>(arg);
+ f(arg);
+ free(arg);
+}
+
+void returnInvariant1() {
+ final fp = ffiTestFunctions
+ .lookup<NativeFunction<Int64PointerReturnOp>>(returnOpName);
+ final f = fp.asFunction<Int64PointerReturnOp>();
+ final result = f();
+ Expect.type<Pointer<Int64>>(result);
+}
+
+void returnInvariant2() {
+ final fp = ffiTestFunctions
+ .lookup<NativeFunction<NaTyPointerReturnOp>>(returnOpName);
+ final f = fp.asFunction<NaTyPointerReturnOp>();
+ final result = f();
+ Expect.type<Pointer<NativeType>>(result);
+}
+
+void returnSubtype() {
+ final fp = ffiTestFunctions
+ .lookup<NativeFunction<Int64PointerReturnOp>>(returnOpName);
+ final f = fp.asFunction<Int64PointerReturnOp>();
+ final NaTyPointerReturnOp f2 = f;
+ Expect.type<Int64PointerReturnOp>(f2);
+ final result = f2();
+ Expect.type<Pointer<NativeType>>(result);
+}
+
+void functionArgumentVariance() {
+ final p = Pointer<
+ NativeFunction<
+ Pointer<NativeFunction<Pointer<Int8> Function(Pointer<NativeType>)>> Function(
+ Pointer<
+ NativeFunction<
+ Pointer<NativeType> Function(
+ Pointer<Int8>)>>)>>.fromAddress(0x1234);
+ final f = p.asFunction<
+ Pointer<NativeFunction<Pointer<NativeType> Function(Pointer<Int8>)>> Function(
+ Pointer<
+ NativeFunction<Pointer<Int8> Function(Pointer<NativeType>)>>)>();
+}
+
+void asFunctionTests() {
+ for (int i = 0; i < 100; ++i) {
+ paramInvariant1(); // Parameter invariant: Pointer<Int64>.
+ paramInvariant2(); // Parameter invariant: Pointer<NativeType>.
+ paramSubtype1(); // Parameter statically and dynamically subtyped.
+ paramSubtype2(); // Parameter statically invariant, dynamically subtyped.
+ returnInvariant1(); // Return value invariant: Pointer<Int64>.
+ returnInvariant2(); // Return value invariant: Pointer<NativeType>.
+ returnSubtype(); // Return value static subtyped, dynamically invariant.
+ functionArgumentVariance(); // Check nested function signatures.
+ }
+}
+
+// =======================================================
+// Test with callbacks from native to Dart (fromFunction).
+// =======================================================
+
+typedef CallbackInt64PointerParamOpDart = void Function(
+ Pointer<NativeFunction<Int64PointerParamOp>>);
+typedef CallbackInt64PointerParamOp = Void Function(
+ Pointer<NativeFunction<Int64PointerParamOp>>);
+
+typedef CallbackNaTyPointerParamOpDart = void Function(
+ Pointer<NativeFunction<NaTyPointerParamOp>>);
+typedef CallbackNaTyPointerParamOp = Void Function(
+ Pointer<NativeFunction<NaTyPointerParamOp>>);
+
+typedef CallbackInt64PointerReturnOpDart = void Function(
+ Pointer<NativeFunction<Int64PointerReturnOp>>);
+typedef CallbackInt64PointerReturnOp = Void Function(
+ Pointer<NativeFunction<Int64PointerReturnOp>>);
+
+typedef CallbackNaTyPointerReturnOpDart = void Function(
+ Pointer<NativeFunction<NaTyPointerReturnOp>>);
+typedef CallbackNaTyPointerReturnOp = Void Function(
+ Pointer<NativeFunction<NaTyPointerReturnOp>>);
+
+final callbackParamOpName = "CallbackNativeTypePointerParam";
+final callbackReturnOpName = "CallbackNativeTypePointerReturn";
+
+void int64PointerParamOp(Pointer<Int64> p) {
+ p.value = 42;
+}
+
+void naTyPointerParamOp(Pointer<NativeType> p) {
+ final Pointer<Int8> asInt8 = p.cast();
+ asInt8.value = 42;
+}
+
+// Pointer to return to C when C calls back into Dart and asks for a Pointer.
+Pointer<Int64> data;
+
+Pointer<Int64> int64PointerReturnOp() {
+ return data;
+}
+
+Pointer<NativeType> naTyPointerReturnOp() {
+ return data;
+}
+
+void callbackParamInvariant1() {
+ final callback = ffiTestFunctions.lookupFunction<CallbackInt64PointerParamOp,
+ CallbackInt64PointerParamOpDart>(callbackParamOpName);
+ final fp = Pointer.fromFunction<Int64PointerParamOp>(int64PointerParamOp);
+ callback(fp);
+}
+
+void callbackParamInvariant2() {
+ final callback = ffiTestFunctions.lookupFunction<CallbackNaTyPointerParamOp,
+ CallbackNaTyPointerParamOpDart>(callbackParamOpName);
+ final fp = Pointer.fromFunction<NaTyPointerParamOp>(naTyPointerParamOp);
+ callback(fp);
+}
+
+void callbackParamImplictDowncast1() {
+ final callback = ffiTestFunctions.lookupFunction<CallbackNaTyPointerParamOp,
+ CallbackNaTyPointerParamOpDart>(callbackParamOpName);
+ final fp = Pointer.fromFunction<Int64PointerParamOp>(int64PointerParamOp);
+ Expect.throws(() {
+ callback(fp);
+ });
+}
+
+void callbackParamSubtype1() {
+ final callback = ffiTestFunctions.lookupFunction<CallbackNaTyPointerParamOp,
+ CallbackNaTyPointerParamOpDart>(callbackParamOpName);
+ final fp = Pointer.fromFunction<NaTyPointerParamOp>(int64PointerParamOp);
+ callback(fp);
+}
+
+void callbackReturnInvariant1() {
+ final callback = ffiTestFunctions.lookupFunction<CallbackInt64PointerReturnOp,
+ CallbackInt64PointerReturnOpDart>(callbackReturnOpName);
+ final fp = Pointer.fromFunction<Int64PointerReturnOp>(int64PointerReturnOp);
+ callback(fp);
+}
+
+void callbackReturnInvariant2() {
+ final callback = ffiTestFunctions.lookupFunction<CallbackNaTyPointerReturnOp,
+ CallbackNaTyPointerReturnOpDart>(callbackReturnOpName);
+ final fp = Pointer.fromFunction<NaTyPointerReturnOp>(naTyPointerReturnOp);
+ callback(fp);
+}
+
+void fromFunctionTests() {
+ data = allocate();
+ for (int i = 0; i < 100; ++i) {
+ callbackParamInvariant1(); // Pointer<Int64> invariant
+ callbackParamInvariant2(); // Pointer<NativeType> invariant
+ callbackParamImplictDowncast1(); // static and dynamically supertyped
+ callbackParamSubtype1(); // static and dynamically subtyped
+ callbackReturnInvariant1(); // Pointer<Int64> invariant
+ callbackReturnInvariant2(); // Pointer<NativeType> invariant
+ }
+ free(data);
+}
+
+void main() {
+ asFunctionTests();
+ fromFunctionTests();
+}
diff --git a/tests/language_2/vm/regress_flutter_42845_lib.dart b/tests/language_2/vm/regress_flutter_42845_lib.dart
new file mode 100644
index 0000000..f22d832
--- /dev/null
+++ b/tests/language_2/vm/regress_flutter_42845_lib.dart
@@ -0,0 +1,12 @@
+// Copyright (c) 2019, 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.
+
+extension TestExtension on int {
+ bool get isPositive => this > 0;
+ bool get isNegative => this < 0;
+}
+
+extension UnusedExtension on int {
+ bool get isReallyZero => this == 0;
+}
diff --git a/tests/language_2/vm/regress_flutter_42845_test.dart b/tests/language_2/vm/regress_flutter_42845_test.dart
new file mode 100644
index 0000000..ac05e97
--- /dev/null
+++ b/tests/language_2/vm/regress_flutter_42845_test.dart
@@ -0,0 +1,19 @@
+// Copyright (c) 2019, 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.
+
+// SharedOptions=--enable-experiment=extension-methods
+
+// Tests exported extensions.
+
+import "regress_flutter_42845_lib.dart";
+export "regress_flutter_42845_lib.dart" show TestExtension, UnusedExtension;
+
+import "package:expect/expect.dart";
+
+int i = 42;
+
+void main() {
+ Expect.isTrue(i.isPositive);
+ Expect.isFalse(i.isNegative);
+}
diff --git a/tools/VERSION b/tools/VERSION
index 528e6b2..3393977 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -34,6 +34,6 @@
MINOR 6
PATCH 0
PRERELEASE 8
-PRERELEASE_PATCH 0
+PRERELEASE_PATCH 1
ABI_VERSION 19
OLDEST_SUPPORTED_ABI_VERSION 18