Support compilation with the trivial abstract value domain
Change-Id: I1b42ee48f440db9e424448e794a8f8cc9d8f5f3a
Reviewed-on: https://dart-review.googlesource.com/c/86921
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Commit-Queue: Johnni Winther <johnniwinther@google.com>
diff --git a/pkg/compiler/lib/src/commandline_options.dart b/pkg/compiler/lib/src/commandline_options.dart
index a3cf93f..eaeee62 100644
--- a/pkg/compiler/lib/src/commandline_options.dart
+++ b/pkg/compiler/lib/src/commandline_options.dart
@@ -13,6 +13,8 @@
static const String disableDiagnosticColors = '--disable-diagnostic-colors';
static const String disableNativeLiveTypeAnalysis =
'--disable-native-live-type-analysis';
+ static const String useTrivialAbstractValueDomain =
+ '--use-trivial-abstract-value-domain';
static const String disableTypeInference = '--disable-type-inference';
static const String disableRtiOptimization = '--disable-rti-optimization';
static const String dumpInfo = '--dump-info';
diff --git a/pkg/compiler/lib/src/common_elements.dart b/pkg/compiler/lib/src/common_elements.dart
index 48e63f6..e0b0c54 100644
--- a/pkg/compiler/lib/src/common_elements.dart
+++ b/pkg/compiler/lib/src/common_elements.dart
@@ -1238,7 +1238,7 @@
return selector.applies(_jsStringSplit) &&
(receiver == null ||
abstractValueDomain
- .isTargetingMember(receiver, jsStringSplit, selector)
+ .isTargetingMember(receiver, jsStringSplit, selector.memberName)
.isPotentiallyTrue);
}
diff --git a/pkg/compiler/lib/src/compiler.dart b/pkg/compiler/lib/src/compiler.dart
index 818f565..96ac042 100644
--- a/pkg/compiler/lib/src/compiler.dart
+++ b/pkg/compiler/lib/src/compiler.dart
@@ -27,6 +27,7 @@
import 'environment.dart';
import 'frontend_strategy.dart';
import 'inferrer/abstract_value_domain.dart' show AbstractValueStrategy;
+import 'inferrer/trivial.dart' show TrivialAbstractValueStrategy;
import 'inferrer/typemasks/masks.dart' show TypeMaskStrategy;
import 'inferrer/types.dart'
show GlobalTypeInferenceResults, GlobalTypeInferenceTask;
@@ -105,7 +106,7 @@
JavaScriptBackend backend;
CodegenWorldBuilder _codegenWorldBuilder;
- AbstractValueStrategy abstractValueStrategy = const TypeMaskStrategy();
+ AbstractValueStrategy abstractValueStrategy;
GenericTask selfTask;
@@ -144,6 +145,10 @@
: this.options = options {
options.deriveOptions();
options.validate();
+
+ abstractValueStrategy = options.useTrivialAbstractValueDomain
+ ? const TrivialAbstractValueStrategy()
+ : const TypeMaskStrategy();
CompilerTask kernelFrontEndTask;
selfTask = new GenericTask('self', measurer);
_outputProvider = new _CompilerOutput(this, outputProvider);
diff --git a/pkg/compiler/lib/src/dart2js.dart b/pkg/compiler/lib/src/dart2js.dart
index 472aeac..9ad51d6 100644
--- a/pkg/compiler/lib/src/dart2js.dart
+++ b/pkg/compiler/lib/src/dart2js.dart
@@ -373,6 +373,7 @@
new OptionHandler(Flags.disableInlining, passThrough),
new OptionHandler(Flags.disableProgramSplit, passThrough),
new OptionHandler(Flags.disableTypeInference, passThrough),
+ new OptionHandler(Flags.useTrivialAbstractValueDomain, passThrough),
new OptionHandler(Flags.disableRtiOptimization, passThrough),
new OptionHandler(Flags.terse, passThrough),
new OptionHandler('--deferred-map=.+', passThrough),
diff --git a/pkg/compiler/lib/src/inferrer/abstract_value_domain.dart b/pkg/compiler/lib/src/inferrer/abstract_value_domain.dart
index ee075e1..784e018 100644
--- a/pkg/compiler/lib/src/inferrer/abstract_value_domain.dart
+++ b/pkg/compiler/lib/src/inferrer/abstract_value_domain.dart
@@ -6,6 +6,7 @@
import '../constants/values.dart' show ConstantValue, PrimitiveConstantValue;
import '../elements/entities.dart';
+import '../elements/names.dart';
import '../serialization/serialization.dart';
import '../universe/selector.dart';
import '../universe/world_builder.dart';
@@ -466,10 +467,10 @@
AbstractValue computeReceiver(Iterable<MemberEntity> members);
/// Returns an [AbstractBool] that describes whether [member] is a potential
- /// target when being invoked on a [receiver]. [selector] is used to ensure
+ /// target when being invoked on a [receiver]. [name] is used to ensure
/// library privacy is taken into account.
AbstractBool isTargetingMember(
- AbstractValue receiver, MemberEntity member, Selector selector);
+ AbstractValue receiver, MemberEntity member, Name name);
/// Returns an [AbstractBool] that describes whether [selector] invoked on a
/// [receiver] can hit a [noSuchMethod].
diff --git a/pkg/compiler/lib/src/inferrer/trivial.dart b/pkg/compiler/lib/src/inferrer/trivial.dart
index c37d493..63d0b8e 100644
--- a/pkg/compiler/lib/src/inferrer/trivial.dart
+++ b/pkg/compiler/lib/src/inferrer/trivial.dart
@@ -4,12 +4,18 @@
import '../constants/values.dart' show ConstantValue, PrimitiveConstantValue;
import '../elements/entities.dart';
+import '../elements/names.dart';
import '../serialization/serialization.dart';
import '../universe/selector.dart';
+import '../universe/world_builder.dart';
+import '../universe/use.dart';
+import '../world.dart';
import 'abstract_value_domain.dart';
class TrivialAbstractValue implements AbstractValue {
const TrivialAbstractValue();
+
+ String toString() => '?';
}
class TrivialAbstractValueDomain implements AbstractValueDomain {
@@ -58,7 +64,7 @@
@override
AbstractBool isTargetingMember(
- AbstractValue receiver, MemberEntity member, Selector selector) =>
+ AbstractValue receiver, MemberEntity member, Name name) =>
AbstractBool.Maybe;
@override
@@ -395,3 +401,49 @@
@override
AbstractValue get typeType => const TrivialAbstractValue();
}
+
+class TrivialAbstractValueStrategy implements AbstractValueStrategy {
+ const TrivialAbstractValueStrategy();
+
+ @override
+ AbstractValueDomain createDomain(JClosedWorld closedWorld) {
+ return const TrivialAbstractValueDomain();
+ }
+
+ @override
+ SelectorConstraintsStrategy createSelectorStrategy() {
+ return const TrivialSelectorStrategy();
+ }
+}
+
+class TrivialSelectorStrategy implements SelectorConstraintsStrategy {
+ const TrivialSelectorStrategy();
+
+ @override
+ UniverseSelectorConstraints createSelectorConstraints(
+ Selector selector, Object initialConstraint) {
+ return const TrivialUniverseSelectorConstraints();
+ }
+
+ @override
+ bool appliedUnnamed(DynamicUse dynamicUse, MemberEntity member,
+ covariant JClosedWorld world) {
+ return dynamicUse.selector.appliesUnnamed(member);
+ }
+}
+
+class TrivialUniverseSelectorConstraints
+ implements UniverseSelectorConstraints {
+ const TrivialUniverseSelectorConstraints();
+
+ @override
+ bool addReceiverConstraint(Object constraint) => false;
+
+ @override
+ bool needsNoSuchMethodHandling(Selector selector, World world) => true;
+
+ @override
+ bool canHit(MemberEntity element, Name name, World world) => true;
+
+ String toString() => 'TrivialUniverseSelectorConstraints:$hashCode';
+}
diff --git a/pkg/compiler/lib/src/inferrer/typemasks/flat_type_mask.dart b/pkg/compiler/lib/src/inferrer/typemasks/flat_type_mask.dart
index c74600a..2dfe38a 100644
--- a/pkg/compiler/lib/src/inferrer/typemasks/flat_type_mask.dart
+++ b/pkg/compiler/lib/src/inferrer/typemasks/flat_type_mask.dart
@@ -524,28 +524,27 @@
* invoked on this type mask. [selector] is used to ensure library
* privacy is taken into account.
*/
- bool canHit(
- MemberEntity element, Selector selector, JClosedWorld closedWorld) {
+ bool canHit(MemberEntity element, Name name, JClosedWorld closedWorld) {
CommonElements commonElements = closedWorld.commonElements;
- assert(element.name == selector.name);
+ assert(element.name == name.text);
if (isEmpty) return false;
if (isNull) {
return closedWorld.hasElementIn(
- commonElements.jsNullClass, selector, element);
+ commonElements.jsNullClass, name, element);
}
ClassEntity other = element.enclosingClass;
if (other == commonElements.jsNullClass) {
return isNullable;
} else if (isExact) {
- return closedWorld.hasElementIn(base, selector, element);
+ return closedWorld.hasElementIn(base, name, element);
} else if (isSubclass) {
- return closedWorld.hasElementIn(base, selector, element) ||
+ return closedWorld.hasElementIn(base, name, element) ||
closedWorld.classHierarchy.isSubclassOf(other, base) ||
closedWorld.hasAnySubclassThatMixes(base, other);
} else {
assert(isSubtype);
- bool result = closedWorld.hasElementIn(base, selector, element) ||
+ bool result = closedWorld.hasElementIn(base, name, element) ||
closedWorld.classHierarchy.isSubtypeOf(other, base) ||
closedWorld.hasAnySubclassThatImplements(other, base) ||
closedWorld.hasAnySubclassOfMixinUseThatImplements(other, base);
@@ -554,7 +553,7 @@
// can be hit from any of the mixin applications.
Iterable<ClassEntity> mixinUses = closedWorld.mixinUsesOf(base);
return mixinUses.any((mixinApplication) =>
- closedWorld.hasElementIn(mixinApplication, selector, element) ||
+ closedWorld.hasElementIn(mixinApplication, name, element) ||
closedWorld.classHierarchy.isSubclassOf(other, mixinApplication) ||
closedWorld.hasAnySubclassThatMixes(mixinApplication, other));
}
diff --git a/pkg/compiler/lib/src/inferrer/typemasks/forwarding_type_mask.dart b/pkg/compiler/lib/src/inferrer/typemasks/forwarding_type_mask.dart
index 82b6bd0..781a51e 100644
--- a/pkg/compiler/lib/src/inferrer/typemasks/forwarding_type_mask.dart
+++ b/pkg/compiler/lib/src/inferrer/typemasks/forwarding_type_mask.dart
@@ -98,9 +98,8 @@
return forwardTo.needsNoSuchMethodHandling(selector, closedWorld);
}
- bool canHit(
- MemberEntity element, Selector selector, JClosedWorld closedWorld) {
- return forwardTo.canHit(element, selector, closedWorld);
+ bool canHit(MemberEntity element, Name name, JClosedWorld closedWorld) {
+ return forwardTo.canHit(element, name, closedWorld);
}
MemberEntity locateSingleMember(Selector selector, JClosedWorld closedWorld) {
diff --git a/pkg/compiler/lib/src/inferrer/typemasks/masks.dart b/pkg/compiler/lib/src/inferrer/typemasks/masks.dart
index 2960a7b..17794f1 100644
--- a/pkg/compiler/lib/src/inferrer/typemasks/masks.dart
+++ b/pkg/compiler/lib/src/inferrer/typemasks/masks.dart
@@ -10,6 +10,7 @@
import '../../common_elements.dart' show CommonElements;
import '../../constants/values.dart';
import '../../elements/entities.dart';
+import '../../elements/names.dart';
import '../../serialization/serialization.dart';
import '../../universe/class_hierarchy.dart';
import '../../universe/selector.dart' show Selector;
@@ -628,9 +629,9 @@
@override
AbstractBool isTargetingMember(
- covariant TypeMask receiver, MemberEntity member, Selector selector) {
+ covariant TypeMask receiver, MemberEntity member, Name name) {
return AbstractBool.maybeOrFalse(
- receiver.canHit(member, selector, _closedWorld));
+ receiver.canHit(member, name, _closedWorld));
}
@override
diff --git a/pkg/compiler/lib/src/inferrer/typemasks/type_mask.dart b/pkg/compiler/lib/src/inferrer/typemasks/type_mask.dart
index a08d320..eb06347 100644
--- a/pkg/compiler/lib/src/inferrer/typemasks/type_mask.dart
+++ b/pkg/compiler/lib/src/inferrer/typemasks/type_mask.dart
@@ -12,11 +12,11 @@
Set<TypeMask> _masks;
@override
- bool applies(MemberEntity element, Selector selector, JClosedWorld world) {
+ bool canHit(MemberEntity element, Name name, JClosedWorld world) {
if (isAll) return true;
if (_masks == null) return false;
for (TypeMask mask in _masks) {
- if (mask.canHit(element, selector, world)) return true;
+ if (mask.canHit(element, name, world)) return true;
}
return false;
}
@@ -77,8 +77,10 @@
const TypeMaskSelectorStrategy();
@override
- UniverseSelectorConstraints createSelectorConstraints(Selector selector) {
- return new IncreasingTypeMaskSet();
+ UniverseSelectorConstraints createSelectorConstraints(
+ Selector selector, Object initialConstraint) {
+ return new IncreasingTypeMaskSet()
+ ..addReceiverConstraint(initialConstraint);
}
@override
@@ -87,7 +89,7 @@
Selector selector = dynamicUse.selector;
TypeMask mask = dynamicUse.receiverConstraint;
return selector.appliesUnnamed(member) &&
- (mask == null || mask.canHit(member, selector, world));
+ (mask == null || mask.canHit(member, selector.memberName, world));
}
}
@@ -413,13 +415,9 @@
*/
TypeMask intersection(TypeMask other, JClosedWorld closedWorld);
- /**
- * Returns whether [element] is a potential target when being
- * invoked on this type mask. [selector] is used to ensure library
- * privacy is taken into account.
- */
- bool canHit(
- MemberEntity element, Selector selector, JClosedWorld closedWorld);
+ /// Returns whether [element] is a potential target when being invoked on this
+ /// type mask. [name] is used to ensure library privacy is taken into account.
+ bool canHit(MemberEntity element, Name name, JClosedWorld closedWorld);
/// Returns whether this [TypeMask] applied to [selector] can hit a
/// [noSuchMethod].
diff --git a/pkg/compiler/lib/src/inferrer/typemasks/union_type_mask.dart b/pkg/compiler/lib/src/inferrer/typemasks/union_type_mask.dart
index c409d07..0d252f0 100644
--- a/pkg/compiler/lib/src/inferrer/typemasks/union_type_mask.dart
+++ b/pkg/compiler/lib/src/inferrer/typemasks/union_type_mask.dart
@@ -357,9 +357,8 @@
.any((e) => e.needsNoSuchMethodHandling(selector, closedWorld));
}
- bool canHit(
- MemberEntity element, Selector selector, JClosedWorld closedWorld) {
- return disjointMasks.any((e) => e.canHit(element, selector, closedWorld));
+ bool canHit(MemberEntity element, Name name, JClosedWorld closedWorld) {
+ return disjointMasks.any((e) => e.canHit(element, name, closedWorld));
}
MemberEntity locateSingleMember(Selector selector, JClosedWorld closedWorld) {
diff --git a/pkg/compiler/lib/src/js_backend/interceptor_data.dart b/pkg/compiler/lib/src/js_backend/interceptor_data.dart
index 0e364c0..06c66ae 100644
--- a/pkg/compiler/lib/src/js_backend/interceptor_data.dart
+++ b/pkg/compiler/lib/src/js_backend/interceptor_data.dart
@@ -183,7 +183,7 @@
return selector.applies(element) &&
(mask == null ||
closedWorld.abstractValueDomain
- .isTargetingMember(mask, element, selector)
+ .isTargetingMember(mask, element, selector.memberName)
.isPotentiallyTrue);
});
}
diff --git a/pkg/compiler/lib/src/js_emitter/class_stub_generator.dart b/pkg/compiler/lib/src/js_emitter/class_stub_generator.dart
index e088e03..73a5d1c 100644
--- a/pkg/compiler/lib/src/js_emitter/class_stub_generator.dart
+++ b/pkg/compiler/lib/src/js_emitter/class_stub_generator.dart
@@ -4,7 +4,7 @@
library dart2js.js_emitter.class_stub_generator;
-import '../common/names.dart' show Identifiers;
+import '../common/names.dart' show Identifiers, Selectors;
import '../common_elements.dart' show CommonElements;
import '../elements/entities.dart';
import '../js/js.dart' as jsAst;
@@ -125,7 +125,8 @@
for (Selector selector in selectors.keys) {
if (generatedSelectors.contains(selector)) continue;
if (!selector.appliesUnnamed(member)) continue;
- if (selectors[selector].applies(member, selector, _closedWorld)) {
+ if (selectors[selector]
+ .canHit(member, selector.memberName, _closedWorld)) {
generatedSelectors.add(selector);
jsAst.Name invocationName = _namer.invocationName(selector);
@@ -165,6 +166,16 @@
void addNoSuchMethodHandlers(
String ignore, Map<Selector, SelectorConstraints> selectors) {
for (Selector selector in selectors.keys) {
+ if (selector == Selectors.runtimeType_ ||
+ selector == Selectors.equals ||
+ selector == Selectors.toString_ ||
+ selector == Selectors.hashCode_ ||
+ selector == Selectors.noSuchMethod_) {
+ // Skip Object methods since these need no noSuchMethod handling
+ // regardless of the precision of the selector constraints.
+ continue;
+ }
+
SelectorConstraints maskSet = selectors[selector];
if (maskSet.needsNoSuchMethodHandling(selector, _closedWorld)) {
jsAst.Name jsName = _namer.invocationMirrorInternalName(selector);
diff --git a/pkg/compiler/lib/src/js_emitter/parameter_stub_generator.dart b/pkg/compiler/lib/src/js_emitter/parameter_stub_generator.dart
index 7076c68..ac44280 100644
--- a/pkg/compiler/lib/src/js_emitter/parameter_stub_generator.dart
+++ b/pkg/compiler/lib/src/js_emitter/parameter_stub_generator.dart
@@ -378,7 +378,8 @@
for (Selector selector in liveSelectors.keys) {
if (renamedCallSelectors.contains(selector)) continue;
if (!selector.appliesUnnamed(member)) continue;
- if (!liveSelectors[selector].applies(member, selector, _closedWorld)) {
+ if (!liveSelectors[selector]
+ .canHit(member, selector.memberName, _closedWorld)) {
continue;
}
diff --git a/pkg/compiler/lib/src/js_emitter/program_builder/field_visitor.dart b/pkg/compiler/lib/src/js_emitter/program_builder/field_visitor.dart
index c5f8d9a..e1b0d46 100644
--- a/pkg/compiler/lib/src/js_emitter/program_builder/field_visitor.dart
+++ b/pkg/compiler/lib/src/js_emitter/program_builder/field_visitor.dart
@@ -146,7 +146,7 @@
assert(field.isField);
if (fieldAccessNeverThrows(field)) return false;
return field.enclosingClass != null &&
- _codegenWorldBuilder.hasInvokedGetter(field, _closedWorld);
+ _codegenWorldBuilder.hasInvokedGetter(field);
}
bool fieldNeedsSetter(FieldEntity field) {
@@ -154,7 +154,7 @@
if (fieldAccessNeverThrows(field)) return false;
if (!field.isAssignable) return false;
return field.enclosingClass != null &&
- _codegenWorldBuilder.hasInvokedSetter(field, _closedWorld);
+ _codegenWorldBuilder.hasInvokedSetter(field);
}
static bool fieldAccessNeverThrows(FieldEntity field) {
diff --git a/pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart b/pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart
index d4f6901..ea3c298 100644
--- a/pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart
+++ b/pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart
@@ -884,7 +884,7 @@
isClosureCallMethod = true;
} else {
// Careful with operators.
- canTearOff = _worldBuilder.hasInvokedGetter(element, _closedWorld);
+ canTearOff = _worldBuilder.hasInvokedGetter(element);
assert(canTearOff ||
!_worldBuilder.methodsNeedingSuperGetter.contains(element));
tearOffName = _namer.getterForElement(element);
diff --git a/pkg/compiler/lib/src/js_model/js_world.dart b/pkg/compiler/lib/src/js_model/js_world.dart
index 4979d38..e277dd8 100644
--- a/pkg/compiler/lib/src/js_model/js_world.dart
+++ b/pkg/compiler/lib/src/js_model/js_world.dart
@@ -14,6 +14,7 @@
import '../diagnostics/diagnostic_listener.dart';
import '../elements/entities.dart';
import '../elements/entity_utils.dart' as utils;
+import '../elements/names.dart';
import '../elements/types.dart';
import '../environment.dart';
import '../inferrer/abstract_value_domain.dart';
@@ -498,15 +499,13 @@
}
@override
- bool hasElementIn(ClassEntity cls, Selector selector, Entity element) {
+ bool hasElementIn(ClassEntity cls, Name name, Entity element) {
while (cls != null) {
- MemberEntity member = elementEnvironment.lookupLocalClassMember(
- cls, selector.name,
- setter: selector.isSetter);
+ MemberEntity member = elementEnvironment
+ .lookupLocalClassMember(cls, name.text, setter: name.isSetter);
if (member != null &&
!member.isAbstract &&
- (!selector.memberName.isPrivate ||
- member.library == selector.library)) {
+ (!name.isPrivate || member.library == name.library)) {
return member == element;
}
cls = elementEnvironment.getSuperClass(cls);
@@ -529,7 +528,7 @@
return _hasConcreteMatch(
elementEnvironment.getSuperClass(enclosingClass), selector);
}
- return selector.appliesUntyped(element);
+ return selector.appliesUnnamed(element);
}
bool _isNamedMixinApplication(ClassEntity cls) {
diff --git a/pkg/compiler/lib/src/options.dart b/pkg/compiler/lib/src/options.dart
index 7b10c51..c3bbb08 100644
--- a/pkg/compiler/lib/src/options.dart
+++ b/pkg/compiler/lib/src/options.dart
@@ -143,6 +143,9 @@
/// Whether to disable global type inference.
bool disableTypeInference = false;
+ /// Whether to use the trivial abstract value domain.
+ bool useTrivialAbstractValueDomain = false;
+
/// Whether to disable optimization for need runtime type information.
bool disableRtiOptimization = false;
@@ -313,6 +316,8 @@
..disableInlining = _hasOption(options, Flags.disableInlining)
..disableProgramSplit = _hasOption(options, Flags.disableProgramSplit)
..disableTypeInference = _hasOption(options, Flags.disableTypeInference)
+ ..useTrivialAbstractValueDomain =
+ _hasOption(options, Flags.useTrivialAbstractValueDomain)
..disableRtiOptimization =
_hasOption(options, Flags.disableRtiOptimization)
..dumpInfo = _hasOption(options, Flags.dumpInfo)
diff --git a/pkg/compiler/lib/src/ssa/builder_kernel.dart b/pkg/compiler/lib/src/ssa/builder_kernel.dart
index d5a20c0..8bcaf1d 100644
--- a/pkg/compiler/lib/src/ssa/builder_kernel.dart
+++ b/pkg/compiler/lib/src/ssa/builder_kernel.dart
@@ -3415,7 +3415,7 @@
HInstruction lengthInput = arguments.first;
if (lengthInput.isNumber(abstractValueDomain).isPotentiallyFalse) {
HTypeConversion conversion = new HTypeConversion(
- null,
+ commonElements.numType,
HTypeConversion.ARGUMENT_TYPE_CHECK,
abstractValueDomain.numType,
lengthInput,
@@ -4971,7 +4971,7 @@
if (!selector.applies(function)) return false;
if (mask != null &&
abstractValueDomain
- .isTargetingMember(mask, function, selector)
+ .isTargetingMember(mask, function, selector.memberName)
.isDefinitelyFalse) {
return false;
}
diff --git a/pkg/compiler/lib/src/ssa/codegen.dart b/pkg/compiler/lib/src/ssa/codegen.dart
index d39b92a..fb80e88 100644
--- a/pkg/compiler/lib/src/ssa/codegen.dart
+++ b/pkg/compiler/lib/src/ssa/codegen.dart
@@ -3029,49 +3029,24 @@
}
js.Expression generateReceiverOrArgumentTypeTest(HTypeConversion node) {
+ DartType type = node.typeExpression;
HInstruction input = node.checkedInput;
- AbstractValue inputType = node.inputType ?? input.instructionType;
AbstractValue checkedType = node.checkedType;
// This path is no longer used for indexable primitive types.
assert(_abstractValueDomain.isJsIndexable(checkedType).isPotentiallyFalse);
// Figure out if it is beneficial to use a null check. V8 generally prefers
// 'typeof' checks, but for integers we cannot compile this test into a
// single typeof check so the null check is cheaper.
- bool isIntCheck =
- _abstractValueDomain.isIntegerOrNull(checkedType).isDefinitelyTrue;
- bool turnIntoNumCheck = isIntCheck &&
- _abstractValueDomain.isIntegerOrNull(inputType).isDefinitelyTrue;
- bool turnIntoNullCheck = !turnIntoNumCheck &&
- (_abstractValueDomain.includeNull(checkedType) == inputType) &&
- isIntCheck;
-
- if (turnIntoNullCheck) {
- use(input);
- return new js.Binary("==", pop(), new js.LiteralNull())
- .withSourceInformation(input.sourceInformation);
- } else if (isIntCheck && !turnIntoNumCheck) {
- // input is !int
- checkBigInt(input, '!==', input.sourceInformation);
- return pop();
- } else if (turnIntoNumCheck ||
- _abstractValueDomain.isNumberOrNull(checkedType).isDefinitelyTrue) {
+ if (type == _commonElements.numType) {
// input is !num
checkNum(input, '!==', input.sourceInformation);
return pop();
- } else if (_abstractValueDomain
- .isBooleanOrNull(checkedType)
- .isDefinitelyTrue) {
+ } else if (type == _commonElements.boolType) {
// input is !bool
checkBool(input, '!==', input.sourceInformation);
return pop();
- } else if (_abstractValueDomain
- .isStringOrNull(checkedType)
- .isDefinitelyTrue) {
- // input is !string
- checkString(input, '!==', input.sourceInformation);
- return pop();
}
- throw failedAt(input, 'Unexpected check: $checkedType.');
+ throw failedAt(input, 'Unexpected check: $type.');
}
void visitTypeConversion(HTypeConversion node) {
diff --git a/pkg/compiler/lib/src/ssa/codegen_helpers.dart b/pkg/compiler/lib/src/ssa/codegen_helpers.dart
index 8a13587..db168fa 100644
--- a/pkg/compiler/lib/src/ssa/codegen_helpers.dart
+++ b/pkg/compiler/lib/src/ssa/codegen_helpers.dart
@@ -142,7 +142,12 @@
return node;
}
- void tryReplaceInterceptorWithDummy(
+ HInstruction visitOneShotInterceptor(HOneShotInterceptor node) {
+ // The receiver parameter should never be replaced with a dummy constant.
+ return node;
+ }
+
+ bool tryReplaceInterceptorWithDummy(
HInvoke node, Selector selector, AbstractValue mask) {
// Calls of the form
//
@@ -166,7 +171,7 @@
// TODO(15933): Make automatically generated property extraction closures
// work with the dummy receiver optimization.
- if (selector.isGetter) return;
+ if (selector.isGetter) return false;
// This assignment of inputs is uniform for HInvokeDynamic and HInvokeSuper.
HInstruction interceptor = node.inputs[0];
@@ -183,8 +188,10 @@
receiverArgument.usedBy.remove(node);
node.inputs[1] = dummy;
dummy.usedBy.add(node);
+ return true;
}
}
+ return false;
}
HInstruction visitFieldSet(HFieldSet setter) {
diff --git a/pkg/compiler/lib/src/ssa/nodes.dart b/pkg/compiler/lib/src/ssa/nodes.dart
index ed1be86..432eb78 100644
--- a/pkg/compiler/lib/src/ssa/nodes.dart
+++ b/pkg/compiler/lib/src/ssa/nodes.dart
@@ -2830,6 +2830,7 @@
class HOneShotInterceptor extends HInvokeDynamic {
List<DartType> typeArguments;
Set<ClassEntity> interceptedClasses;
+
HOneShotInterceptor(
AbstractValueDomain domain,
Selector selector,
@@ -2840,7 +2841,7 @@
this.interceptedClasses)
: super(selector, mask, null, inputs, true, type) {
assert(inputs[0] is HConstant);
- assert(inputs[0].isNull(domain).isDefinitelyTrue);
+ assert(inputs[0].instructionType == domain.nullType);
assert(selector.callStructure.typeArgumentCount == typeArguments.length);
}
bool isCallOnInterceptor(JClosedWorld closedWorld) => true;
@@ -3128,6 +3129,7 @@
super(<HInstruction>[input], type) {
assert(!isReceiverTypeCheck || receiverTypeCheckSelector != null);
assert(typeExpression == null || !typeExpression.isTypedef);
+ assert(!isControlFlow() || typeExpression != null);
sourceElement = input.sourceElement;
this.sourceInformation = sourceInformation;
}
diff --git a/pkg/compiler/lib/src/ssa/optimize.dart b/pkg/compiler/lib/src/ssa/optimize.dart
index 822a583..c223dd6 100644
--- a/pkg/compiler/lib/src/ssa/optimize.dart
+++ b/pkg/compiler/lib/src/ssa/optimize.dart
@@ -550,7 +550,7 @@
return selector.applies(element) &&
(mask == null ||
_abstractValueDomain
- .isTargetingMember(mask, element, selector)
+ .isTargetingMember(mask, element, selector.memberName)
.isPotentiallyTrue);
}
diff --git a/pkg/compiler/lib/src/ssa/types_propagation.dart b/pkg/compiler/lib/src/ssa/types_propagation.dart
index b95f1e0..492e6ea 100644
--- a/pkg/compiler/lib/src/ssa/types_propagation.dart
+++ b/pkg/compiler/lib/src/ssa/types_propagation.dart
@@ -4,6 +4,7 @@
import '../common_elements.dart' show CommonElements;
import '../elements/entities.dart';
+import '../elements/types.dart';
import '../inferrer/abstract_value_domain.dart';
import '../inferrer/types.dart';
import '../options.dart';
@@ -270,12 +271,12 @@
}
void convertInput(HInvokeDynamic instruction, HInstruction input,
- AbstractValue type, int kind) {
+ AbstractValue type, int kind, DartType typeExpression) {
Selector selector = (kind == HTypeConversion.RECEIVER_TYPE_CHECK)
? instruction.selector
: null;
HTypeConversion converted = new HTypeConversion(
- null, kind, type, input, instruction.sourceInformation,
+ typeExpression, kind, type, input, instruction.sourceInformation,
receiverTypeCheckSelector: selector);
instruction.block.addBefore(instruction, converted);
input.replaceAllUsersDominatedBy(instruction, converted);
@@ -308,7 +309,8 @@
instruction,
receiver,
abstractValueDomain.excludeNull(receiver.instructionType),
- HTypeConversion.RECEIVER_TYPE_CHECK);
+ HTypeConversion.RECEIVER_TYPE_CHECK,
+ commonElements.numType);
return true;
} else if (instruction.element == null) {
if (closedWorld.includesClosureCall(
@@ -322,14 +324,18 @@
ClassEntity cls = target.enclosingClass;
AbstractValue type = abstractValueDomain.createNonNullSubclass(cls);
// We currently only optimize on some primitive types.
- if (abstractValueDomain.isNumberOrNull(type).isPotentiallyFalse &&
- abstractValueDomain.isBooleanOrNull(type).isPotentiallyFalse) {
+ DartType typeExpression;
+ if (abstractValueDomain.isNumberOrNull(type).isDefinitelyTrue) {
+ typeExpression = commonElements.numType;
+ } else if (abstractValueDomain.isBooleanOrNull(type).isDefinitelyTrue) {
+ typeExpression = commonElements.boolType;
+ } else {
return false;
}
if (!isCheckEnoughForNsmOrAe(receiver, type)) return false;
instruction.element = target;
- convertInput(
- instruction, receiver, type, HTypeConversion.RECEIVER_TYPE_CHECK);
+ convertInput(instruction, receiver, type,
+ HTypeConversion.RECEIVER_TYPE_CHECK, typeExpression);
return true;
}
}
@@ -357,8 +363,8 @@
// variant and will do the check in their method anyway. We
// still add a check because it allows to GVN these operations,
// but we should find a better way.
- convertInput(
- instruction, right, type, HTypeConversion.ARGUMENT_TYPE_CHECK);
+ convertInput(instruction, right, type,
+ HTypeConversion.ARGUMENT_TYPE_CHECK, commonElements.numType);
return true;
}
return false;
diff --git a/pkg/compiler/lib/src/universe/codegen_world_builder.dart b/pkg/compiler/lib/src/universe/codegen_world_builder.dart
index 5a42501..395639e 100644
--- a/pkg/compiler/lib/src/universe/codegen_world_builder.dart
+++ b/pkg/compiler/lib/src/universe/codegen_world_builder.dart
@@ -71,9 +71,9 @@
ConstantValue getConstantFieldInitializer(covariant FieldEntity field);
/// Returns `true` if [member] is invoked as a setter.
- bool hasInvokedSetter(MemberEntity member, JClosedWorld world);
+ bool hasInvokedSetter(MemberEntity member);
- bool hasInvokedGetter(MemberEntity member, JClosedWorld world);
+ bool hasInvokedGetter(MemberEntity member);
Map<Selector, SelectorConstraints> invocationsByName(String name);
@@ -260,7 +260,7 @@
for (Selector selector in selectors.keys) {
if (selector.appliesUnnamed(member)) {
SelectorConstraints masks = selectors[selector];
- if (masks.applies(member, selector, world)) {
+ if (masks.canHit(member, selector.memberName, world)) {
return true;
}
}
@@ -268,17 +268,17 @@
return false;
}
- bool hasInvocation(MemberEntity member, JClosedWorld world) {
- return _hasMatchingSelector(_invokedNames[member.name], member, world);
+ bool hasInvocation(MemberEntity member) {
+ return _hasMatchingSelector(_invokedNames[member.name], member, _world);
}
- bool hasInvokedGetter(MemberEntity member, JClosedWorld world) {
- return _hasMatchingSelector(_invokedGetters[member.name], member, world) ||
+ bool hasInvokedGetter(MemberEntity member) {
+ return _hasMatchingSelector(_invokedGetters[member.name], member, _world) ||
member.isFunction && methodsNeedingSuperGetter.contains(member);
}
- bool hasInvokedSetter(MemberEntity member, JClosedWorld world) {
- return _hasMatchingSelector(_invokedSetters[member.name], member, world);
+ bool hasInvokedSetter(MemberEntity member) {
+ return _hasMatchingSelector(_invokedSetters[member.name], member, _world);
}
bool registerDynamicUse(
@@ -289,8 +289,9 @@
void _process(Map<String, Set<MemberUsage>> memberMap,
EnumSet<MemberUse> action(MemberUsage usage)) {
_processSet(memberMap, methodName, (MemberUsage usage) {
- if (selectorConstraintsStrategy.appliedUnnamed(
- dynamicUse, usage.entity, _world)) {
+ if (selector.appliesUnnamed(usage.entity) &&
+ selectorConstraintsStrategy.appliedUnnamed(
+ dynamicUse, usage.entity, _world)) {
memberUsed(usage.entity, action(usage));
return true;
}
@@ -331,8 +332,12 @@
Object constraint = dynamicUse.receiverConstraint;
Map<Selector, SelectorConstraints> selectors =
selectorMap[name] ??= new Maplet<Selector, SelectorConstraints>();
- UniverseSelectorConstraints constraints = selectors[selector] ??=
- selectorConstraintsStrategy.createSelectorConstraints(selector);
+ UniverseSelectorConstraints constraints = selectors[selector];
+ if (constraints == null) {
+ selectors[selector] = selectorConstraintsStrategy
+ .createSelectorConstraints(selector, constraint);
+ return true;
+ }
return constraints.addReceiverConstraint(constraint);
}
@@ -504,13 +509,13 @@
MemberUsage usage = new MemberUsage(member, isNative: isNative);
EnumSet<MemberUse> useSet = new EnumSet<MemberUse>();
useSet.addAll(usage.appliedUse);
- if (!usage.hasRead && hasInvokedGetter(member, _world)) {
+ if (!usage.hasRead && hasInvokedGetter(member)) {
useSet.addAll(usage.read());
}
- if (!usage.hasWrite && hasInvokedSetter(member, _world)) {
+ if (!usage.hasWrite && hasInvokedSetter(member)) {
useSet.addAll(usage.write());
}
- if (!usage.hasInvoke && hasInvocation(member, _world)) {
+ if (!usage.hasInvoke && hasInvocation(member)) {
useSet.addAll(usage.invoke());
}
diff --git a/pkg/compiler/lib/src/universe/function_set.dart b/pkg/compiler/lib/src/universe/function_set.dart
index f489158..e8edd4b 100644
--- a/pkg/compiler/lib/src/universe/function_set.dart
+++ b/pkg/compiler/lib/src/universe/function_set.dart
@@ -109,7 +109,7 @@
bool applies(MemberEntity element, AbstractValueDomain domain) {
if (!selector.appliesUnnamed(element)) return false;
return domain
- .isTargetingMember(receiver, element, selector)
+ .isTargetingMember(receiver, element, selector.memberName)
.isPotentiallyTrue;
}
diff --git a/pkg/compiler/lib/src/universe/resolution_world_builder.dart b/pkg/compiler/lib/src/universe/resolution_world_builder.dart
index 6e75f80..6119207 100644
--- a/pkg/compiler/lib/src/universe/resolution_world_builder.dart
+++ b/pkg/compiler/lib/src/universe/resolution_world_builder.dart
@@ -589,7 +589,7 @@
for (Selector selector in selectors.keys) {
if (selector.appliesUnnamed(member)) {
SelectorConstraints masks = selectors[selector];
- if (masks.applies(member, selector, this)) {
+ if (masks.canHit(member, selector.memberName, this)) {
return true;
}
}
@@ -623,8 +623,9 @@
void _process(Map<String, Set<MemberUsage>> memberMap,
EnumSet<MemberUse> action(MemberUsage usage)) {
_processSet(memberMap, methodName, (MemberUsage usage) {
- if (_selectorConstraintsStrategy.appliedUnnamed(
- dynamicUse, usage.entity, this)) {
+ if (selector.appliesUnnamed(usage.entity) &&
+ _selectorConstraintsStrategy.appliedUnnamed(
+ dynamicUse, usage.entity, this)) {
memberUsed(usage.entity, action(usage));
return true;
}
@@ -661,10 +662,12 @@
Object constraint = dynamicUse.receiverConstraint;
Map<Selector, SelectorConstraints> selectors = selectorMap.putIfAbsent(
name, () => new Maplet<Selector, SelectorConstraints>());
- UniverseSelectorConstraints constraints =
- selectors.putIfAbsent(selector, () {
- return _selectorConstraintsStrategy.createSelectorConstraints(selector);
- });
+ UniverseSelectorConstraints constraints = selectors[selector];
+ if (constraints == null) {
+ selectors[selector] = _selectorConstraintsStrategy
+ .createSelectorConstraints(selector, constraint);
+ return true;
+ }
return constraints.addReceiverConstraint(constraint);
}
diff --git a/pkg/compiler/lib/src/universe/selector.dart b/pkg/compiler/lib/src/universe/selector.dart
index 42cac09..47f974e 100644
--- a/pkg/compiler/lib/src/universe/selector.dart
+++ b/pkg/compiler/lib/src/universe/selector.dart
@@ -250,11 +250,6 @@
bool appliesUnnamed(MemberEntity element) {
assert(name == element.name);
- return appliesUntyped(element);
- }
-
- bool appliesUntyped(MemberEntity element) {
- assert(name == element.name);
if (memberName.isPrivate && memberName.library != element.library) {
// TODO(johnniwinther): Maybe this should be
// `memberName != element.memberName`.
diff --git a/pkg/compiler/lib/src/universe/world_builder.dart b/pkg/compiler/lib/src/universe/world_builder.dart
index 9eb4560..e80c180 100644
--- a/pkg/compiler/lib/src/universe/world_builder.dart
+++ b/pkg/compiler/lib/src/universe/world_builder.dart
@@ -6,6 +6,7 @@
import '../common_elements.dart';
import '../elements/entities.dart';
+import '../elements/names.dart';
import '../elements/types.dart';
import '../ir/static_type.dart';
import '../js_backend/native_data.dart' show NativeBasicData;
@@ -33,7 +34,7 @@
/// the selector constraints for dynamic calls to 'foo' with two positional
/// arguments could be 'receiver of exact instance `A` or `B`'.
abstract class SelectorConstraints {
- /// Returns `true` if [selector] applies to [element] under these constraints
+ /// Returns `true` if [name] applies to [element] under these constraints
/// given the closed [world].
///
/// Consider for instance in this world:
@@ -48,7 +49,7 @@
///
/// Ideally the selector constraints for calls `foo` with two positional
/// arguments apply to `A.foo` but `B.foo`.
- bool applies(MemberEntity element, Selector selector, covariant World world);
+ bool canHit(MemberEntity element, Name name, covariant World world);
/// Returns `true` if at least one of the receivers matching these constraints
/// in the closed [world] have no implementation matching [selector].
@@ -76,7 +77,8 @@
abstract class SelectorConstraintsStrategy {
/// Create a [UniverseSelectorConstraints] to represent the global receiver
/// constraints for dynamic call sites with [selector].
- UniverseSelectorConstraints createSelectorConstraints(Selector selector);
+ UniverseSelectorConstraints createSelectorConstraints(
+ Selector selector, Object initialConstraint);
/// Returns `true` if [member] is a potential target of [dynamicUse].
bool appliedUnnamed(DynamicUse dynamicUse, MemberEntity member, World world);
@@ -89,8 +91,10 @@
class StrongModeWorldStrategy implements SelectorConstraintsStrategy {
const StrongModeWorldStrategy();
- StrongModeWorldConstraints createSelectorConstraints(Selector selector) {
- return new StrongModeWorldConstraints();
+ StrongModeWorldConstraints createSelectorConstraints(
+ Selector selector, Object initialConstraint) {
+ return new StrongModeWorldConstraints()
+ ..addReceiverConstraint(initialConstraint);
}
@override
@@ -99,7 +103,8 @@
Selector selector = dynamicUse.selector;
StrongModeConstraint constraint = dynamicUse.receiverConstraint;
return selector.appliesUnnamed(member) &&
- (constraint == null || constraint.canHit(member, selector, world));
+ (constraint == null ||
+ constraint.canHit(member, selector.memberName, world));
}
}
@@ -108,11 +113,11 @@
Set<StrongModeConstraint> _constraints;
@override
- bool applies(MemberEntity element, Selector selector, World world) {
+ bool canHit(MemberEntity element, Name name, World world) {
if (isAll) return true;
if (_constraints == null) return false;
for (StrongModeConstraint constraint in _constraints) {
- if (constraint.canHit(element, selector, world)) {
+ if (constraint.canHit(element, name, world)) {
return true;
}
}
@@ -177,7 +182,7 @@
bool needsNoSuchMethodHandling(Selector selector, World world) => true;
- bool canHit(MemberEntity element, Selector selector, OpenWorld world) {
+ bool canHit(MemberEntity element, Name name, OpenWorld world) {
return world.isInheritedIn(element, cls, relation);
}
diff --git a/pkg/compiler/lib/src/world.dart b/pkg/compiler/lib/src/world.dart
index 73446b6..b2bcb10 100644
--- a/pkg/compiler/lib/src/world.dart
+++ b/pkg/compiler/lib/src/world.dart
@@ -16,6 +16,7 @@
import 'deferred_load.dart';
import 'diagnostics/diagnostic_listener.dart';
import 'elements/entities.dart';
+import 'elements/names.dart';
import 'elements/types.dart';
import 'inferrer/abstract_value_domain.dart';
import 'ir/static_type.dart';
@@ -166,10 +167,9 @@
bool needsNoSuchMethod(ClassEntity cls, Selector selector, ClassQuery query);
/// Returns whether [element] will be the one used at runtime when being
- /// invoked on an instance of [cls]. [selector] is used to ensure library
+ /// invoked on an instance of [cls]. [name] is used to ensure library
/// privacy is taken into account.
- bool hasElementIn(
- covariant ClassEntity cls, Selector selector, covariant Entity element);
+ bool hasElementIn(ClassEntity cls, Name name, MemberEntity element);
/// Returns `true` if the field [element] is known to be effectively final.
bool fieldNeverChanges(MemberEntity element);
diff --git a/tests/compiler/dart2js/inference/trivial_abstract_value_domain_test.dart b/tests/compiler/dart2js/inference/trivial_abstract_value_domain_test.dart
new file mode 100644
index 0000000..6a8f389
--- /dev/null
+++ b/tests/compiler/dart2js/inference/trivial_abstract_value_domain_test.dart
@@ -0,0 +1,28 @@
+// Copyright (c) 2018, 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 'package:args/args.dart';
+import 'package:async_helper/async_helper.dart';
+import 'package:compiler/src/commandline_options.dart';
+
+import '../helpers/args_helper.dart';
+import '../helpers/memory_compiler.dart';
+
+main(List<String> args) {
+ ArgParser argParser = createArgParser();
+
+ asyncTest(() async {
+ ArgResults argResults = argParser.parse(args);
+ Uri entryPoint = getEntryPoint(argResults) ??
+ Uri.base.resolve('samples-dev/swarm/swarm.dart');
+ Uri librariesSpecificationUri = getLibrariesSpec(argResults);
+ Uri packageConfig = getPackages(argResults);
+ List<String> options = getOptions(argResults);
+ await runCompiler(
+ entryPoint: entryPoint,
+ packageConfig: packageConfig,
+ librariesSpecificationUri: librariesSpecificationUri,
+ options: [Flags.useTrivialAbstractValueDomain]..addAll(options));
+ });
+}
diff --git a/tests/compiler/dart2js_native/native_method_inlining_test.dart b/tests/compiler/dart2js_native/native_method_inlining_test.dart
index 1dd30c1..f673eeb 100644
--- a/tests/compiler/dart2js_native/native_method_inlining_test.dart
+++ b/tests/compiler/dart2js_native/native_method_inlining_test.dart
@@ -82,13 +82,13 @@
void match(String s, String pattern1) {
var pattern2 = pattern1.replaceAll(' ', '');
Expect.isTrue(s.contains(pattern1) || s.contains(pattern2),
- "expected $pattern1 or $pattern2");
+ "expected $pattern1 or $pattern2 in '$s'");
}
void nomatch(String s, String pattern1) {
var pattern2 = pattern1.replaceAll(' ', '');
Expect.isFalse(s.contains(pattern1) || s.contains(pattern2),
- "should not have $pattern1 or $pattern2");
+ "should not have $pattern1 or $pattern2 in '$s'");
}
test1() {
diff --git a/tests/compiler/dart2js_native/native_mixin_field2_test.dart b/tests/compiler/dart2js_native/native_mixin_field2_test.dart
new file mode 100644
index 0000000..408bdb6
--- /dev/null
+++ b/tests/compiler/dart2js_native/native_mixin_field2_test.dart
@@ -0,0 +1,87 @@
+// Copyright (c) 2013, 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";
+
+// Test that native classes can use ordinary Dart classes with fields
+// as mixins.
+
+@Native("A")
+class A {
+ var foo;
+}
+
+class A1 {
+ get foo => 42;
+ set foo(value) {}
+}
+
+@Native("B")
+class B extends A with M1, M2 {
+ var bar;
+}
+
+class B1 extends A1 with M1, M2 {
+ get bar => 42;
+ set bar(value) {}
+}
+
+class M1 {
+ var baz; // This field is not a native field, even when mixed in.
+}
+
+class M2 {
+ var bar;
+ var buz;
+}
+
+A makeA() native;
+B makeB() native;
+
+void setup() {
+ JS('', r"""
+(function(){
+ function A() {this.foo='A-foo';}
+ function B() {A.call(this);this.bar='B-bar';this.baz='M1-baz';}
+ makeA = function(){return new A()};
+ makeB = function(){return new B()};
+
+ self.nativeConstructor(A);
+ self.nativeConstructor(B);
+})()""");
+}
+
+@AssumeDynamic()
+confuse(x) => x;
+
+main() {
+ nativeTesting();
+ setup();
+ dynamic a = makeA();
+ a ??= new A1();
+ Expect.equals("A-foo", confuse(a).foo);
+ Expect.throws(() => confuse(a).bar, (e) => e is NoSuchMethodError);
+ Expect.throws(() => confuse(a).baz, (e) => e is NoSuchMethodError);
+ Expect.throws(() => confuse(a).buz, (e) => e is NoSuchMethodError);
+
+ dynamic b = makeB();
+ b ??= new B1();
+ Expect.equals("A-foo", confuse(b).foo);
+ Expect.equals("B-bar", confuse(b).bar);
+ // Expect.equals("M1-baz", b.baz); // not true, see M1.
+ Expect.isNull(confuse(b).baz); // native b.baz is not the same as dart b.baz.
+ Expect.isNull(confuse(b).buz);
+
+ M1 m1 = new M1();
+ Expect.throws(() => confuse(m1).foo, (e) => e is NoSuchMethodError);
+ Expect.throws(() => confuse(m1).bar, (e) => e is NoSuchMethodError);
+ Expect.isNull(m1.baz);
+ Expect.throws(() => confuse(m1).buz, (e) => e is NoSuchMethodError);
+
+ M2 m2 = new M2();
+ Expect.throws(() => confuse(m2).foo, (e) => e is NoSuchMethodError);
+ Expect.isNull(confuse(m2).bar);
+ Expect.throws(() => confuse(m2).baz, (e) => e is NoSuchMethodError);
+ Expect.isNull(confuse(m2).buz);
+}