c878108cc8
All non-pubspec changes generated by `dart format pkg/compiler`. Change-Id: Iadaa70974816d96ccb47813fe72d5a2bb83d6bda Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/400181 Reviewed-by: Nate Biggs <natebiggs@google.com> Commit-Queue: Mayank Patke <fishythefish@google.com>
225 lines
6.7 KiB
Dart
225 lines
6.7 KiB
Dart
// 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:compiler/src/commandline_options.dart';
|
|
import 'package:compiler/src/common/elements.dart';
|
|
import 'package:compiler/src/compiler.dart';
|
|
import 'package:compiler/src/elements/entities.dart';
|
|
import 'package:compiler/src/elements/names.dart';
|
|
import 'package:compiler/src/js_backend/runtime_types_resolution.dart';
|
|
import 'package:compiler/src/js_emitter/model.dart';
|
|
import 'package:compiler/src/js_model/js_strategy.dart';
|
|
import 'package:compiler/src/js/js.dart' as js;
|
|
import 'package:compiler/src/js_model/js_world.dart' show JClosedWorld;
|
|
import 'package:compiler/src/universe/call_structure.dart';
|
|
import 'package:compiler/src/universe/selector.dart';
|
|
import 'package:expect/async_helper.dart';
|
|
import 'package:expect/expect.dart';
|
|
import '../helpers/program_lookup.dart';
|
|
import 'package:compiler/src/util/memory_compiler.dart';
|
|
|
|
const String code = '''
|
|
class A {
|
|
// Both method1 implementations need type arguments.
|
|
@pragma('dart2js:noInline')
|
|
method1<T>(T t) => t is T;
|
|
|
|
// One of the method2 implementations need type arguments.
|
|
@pragma('dart2js:noInline')
|
|
method2<T>(T t) => t is T;
|
|
|
|
// None of the method3 implementations need type arguments.
|
|
@pragma('dart2js:noInline')
|
|
method3<T>(T t) => false;
|
|
}
|
|
|
|
class B {
|
|
@pragma('dart2js:noInline')
|
|
method1<T>(T t) => t is T;
|
|
@pragma('dart2js:noInline')
|
|
method2<T>(T t) => true;
|
|
@pragma('dart2js:noInline')
|
|
method3<T>(T t) => true;
|
|
}
|
|
|
|
// A call to either A.method1 or B.method1.
|
|
@pragma('dart2js:noInline')
|
|
call1(c) => c.method1<int>(0);
|
|
|
|
// A call to A.method1.
|
|
@pragma('dart2js:noInline')
|
|
call1a() => A().method1<int>(0);
|
|
|
|
// A call to B.method1.
|
|
@pragma('dart2js:noInline')
|
|
call1b() => B().method1<int>(0);
|
|
|
|
// A call to either A.method2 or B.method2.
|
|
@pragma('dart2js:noInline')
|
|
call2(c) => c.method2<int>(0);
|
|
|
|
// A call to A.method2.
|
|
@pragma('dart2js:noInline')
|
|
call2a() => A().method2<int>(0);
|
|
|
|
// A call to B.method2.
|
|
@pragma('dart2js:noInline')
|
|
call2b() => B().method2<int>(0);
|
|
|
|
// A call to either A.method3 or B.method3.
|
|
@pragma('dart2js:noInline')
|
|
call3(c) => c.method3<int>(0);
|
|
|
|
// A call to A.method3.
|
|
@pragma('dart2js:noInline')
|
|
call3a() => A().method3<int>(0);
|
|
|
|
// A call to B.method3.
|
|
@pragma('dart2js:noInline')
|
|
call3b() => B().method3<int>(0);
|
|
|
|
main() {
|
|
call1(new A());
|
|
call1(new B());
|
|
call1a();
|
|
call1b();
|
|
call2(new A());
|
|
call2(new B());
|
|
call2a();
|
|
call2b();
|
|
call3(new A());
|
|
call3(new B());
|
|
call3a();
|
|
call3b();
|
|
}
|
|
''';
|
|
|
|
main() {
|
|
asyncTest(() async {
|
|
CompilationResult result = await runCompiler(
|
|
memorySourceFiles: {'main.dart': code},
|
|
options: [Flags.omitImplicitChecks],
|
|
);
|
|
Expect.isTrue(result.isSuccess);
|
|
Compiler compiler = result.compiler!;
|
|
JsBackendStrategy backendStrategy = compiler.backendStrategy;
|
|
JClosedWorld closedWorld = compiler.backendClosedWorldForTesting!;
|
|
RuntimeTypesNeed rtiNeed = closedWorld.rtiNeed;
|
|
ElementEnvironment elementEnvironment = closedWorld.elementEnvironment;
|
|
ProgramLookup programLookup = ProgramLookup(backendStrategy);
|
|
|
|
js.Name getName(String name, int typeArguments) {
|
|
return backendStrategy.namerForTesting.invocationName(
|
|
new Selector.call(
|
|
PublicName(name),
|
|
CallStructure(1, const <String>[], typeArguments),
|
|
),
|
|
);
|
|
}
|
|
|
|
void checkParameters(
|
|
String name, {
|
|
required int expectedParameterCount,
|
|
required bool needsTypeArguments,
|
|
}) {
|
|
final function = lookupMember(elementEnvironment, name) as FunctionEntity;
|
|
|
|
Expect.equals(
|
|
needsTypeArguments,
|
|
rtiNeed.methodNeedsTypeArguments(function),
|
|
"Unexpected type argument need for $function.",
|
|
);
|
|
Method method = programLookup.getMethod(function)!;
|
|
|
|
final fun = method.code as js.Fun;
|
|
Expect.equals(
|
|
expectedParameterCount,
|
|
fun.params.length,
|
|
"Unexpected parameter count on $function: ${js.nodeToString(fun)}",
|
|
);
|
|
}
|
|
|
|
// The declarations should have type parameters only when needed.
|
|
checkParameters(
|
|
'A.method1',
|
|
expectedParameterCount: 2,
|
|
needsTypeArguments: true,
|
|
);
|
|
checkParameters(
|
|
'B.method1',
|
|
expectedParameterCount: 2,
|
|
needsTypeArguments: true,
|
|
);
|
|
checkParameters(
|
|
'A.method2',
|
|
expectedParameterCount: 2,
|
|
needsTypeArguments: true,
|
|
);
|
|
checkParameters(
|
|
'B.method2',
|
|
expectedParameterCount: 1,
|
|
needsTypeArguments: false,
|
|
);
|
|
checkParameters(
|
|
'A.method3',
|
|
expectedParameterCount: 1,
|
|
needsTypeArguments: false,
|
|
);
|
|
checkParameters(
|
|
'B.method3',
|
|
expectedParameterCount: 1,
|
|
needsTypeArguments: false,
|
|
);
|
|
|
|
checkArguments(
|
|
String name,
|
|
String targetName, {
|
|
required int expectedTypeArguments,
|
|
}) {
|
|
final function = lookupMember(elementEnvironment, name) as FunctionEntity;
|
|
Method method = programLookup.getMethod(function)!;
|
|
|
|
final fun = method.code as js.Fun;
|
|
|
|
js.Name selector = getName(targetName, expectedTypeArguments);
|
|
bool callFound = false;
|
|
forEachNode(
|
|
fun,
|
|
onCall: (js.Call node) {
|
|
js.Node target = js.undefer(node.target);
|
|
if (target is js.PropertyAccess) {
|
|
js.Node targetSelector = js.undefer(target.selector);
|
|
if (targetSelector is js.Name &&
|
|
targetSelector.key == selector.key) {
|
|
callFound = true;
|
|
Expect.equals(
|
|
1 + expectedTypeArguments,
|
|
node.arguments.length,
|
|
"Unexpected argument count in $function call to $targetName: "
|
|
"${js.nodeToString(fun)}",
|
|
);
|
|
}
|
|
}
|
|
},
|
|
);
|
|
Expect.isTrue(
|
|
callFound,
|
|
"No call to $targetName as '${selector.key}' in $function found.",
|
|
);
|
|
}
|
|
|
|
// The declarations should have type parameters only when needed by the
|
|
// selector.
|
|
checkArguments('call1', 'method1', expectedTypeArguments: 1);
|
|
checkArguments('call1a', 'method1', expectedTypeArguments: 1);
|
|
checkArguments('call1b', 'method1', expectedTypeArguments: 1);
|
|
checkArguments('call2', 'method2', expectedTypeArguments: 1);
|
|
checkArguments('call2a', 'method2', expectedTypeArguments: 1);
|
|
checkArguments('call2b', 'method2', expectedTypeArguments: 1);
|
|
checkArguments('call3', 'method3', expectedTypeArguments: 0);
|
|
checkArguments('call3a', 'method3', expectedTypeArguments: 0);
|
|
checkArguments('call3b', 'method3', expectedTypeArguments: 0);
|
|
});
|
|
}
|