blob: 9df70d3c25bf7916a5a691f356f85cf28a2b9920 [file] [log] [blame]
// Copyright (c) 2024, 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.
// This test verifies different corner cases around calling functions in
// Dart.
import 'package:expect/expect.dart';
import 'package:vm/testing/il_matchers.dart';
abstract class Base {
final String o;
Base(this.o);
String f(String v);
String fOptional([String v = 'ok!']);
String fNamed({String v = 'ok!'});
int fIntInt(int a, int b);
double fIntDouble(int a, double b);
double fDoubleDouble(double a, double b);
int fIntOptionalInt(int a, [int b = 0]);
double fIntOptionalDouble(int a, [double b = 0.0]);
double fDoubleOptionalDouble(double a, [double b = 0.0]);
int fIntNamedInt(int a, {int b = 0});
double fIntNamedDouble(int a, {double b = 0.0});
double fDoubleNamedDouble(double a, {double b = 0.0});
}
class ChildSimple extends Base {
ChildSimple(super.o);
@override
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
String f(String v) {
Expect.equals('ok', o.substring(0, 2));
Expect.equals('ok', v.substring(0, 2));
return o + v;
}
@override
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
String fOptional([String v = 'ok!']) {
Expect.equals('ok', o.substring(0, 2));
Expect.equals('ok', v.substring(0, 2));
return o + v;
}
@override
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
String fNamed({String v = 'ok!'}) {
Expect.equals('ok', o.substring(0, 2));
Expect.equals('ok', v.substring(0, 2));
return o + v;
}
@override
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
int fIntInt(int a, int b) {
Expect.equals('ok', o.substring(0, 2));
return a + b;
}
@override
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
double fIntDouble(int a, double b) {
Expect.equals('ok', o.substring(0, 2));
return a + b;
}
@override
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
double fDoubleDouble(double a, double b) {
Expect.equals('ok', o.substring(0, 2));
return a + b;
}
@override
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
int fIntOptionalInt(int a, [int b = 0]) {
Expect.equals('ok', o.substring(0, 2));
return a + b;
}
@override
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
double fIntOptionalDouble(int a, [double b = 0.0]) {
Expect.equals('ok', o.substring(0, 2));
return a + b;
}
@override
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
double fDoubleOptionalDouble(double a, [double b = 0.0]) {
Expect.equals('ok', o.substring(0, 2));
return a + b;
}
@override
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
int fIntNamedInt(int a, {int b = 0}) {
Expect.equals('ok', o.substring(0, 2));
return a + b;
}
@override
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
double fIntNamedDouble(int a, {double b = 0.0}) {
Expect.equals('ok', o.substring(0, 2));
return a + b;
}
@override
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
double fDoubleNamedDouble(double a, {double b = 0.0}) {
Expect.equals('ok', o.substring(0, 2));
return a + b;
}
}
class ChildConvertingParametersToOptional extends Base {
ChildConvertingParametersToOptional(super.o);
@override
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
String f([String v = 'ok!']) {
Expect.equals('ok', o.substring(0, 2));
Expect.equals('ok', v.substring(0, 2));
return o + v;
}
@override
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
String fOptional([String v = 'ok!']) {
Expect.equals('ok', o.substring(0, 2));
Expect.equals('ok', v.substring(0, 2));
return o + v;
}
@override
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
String fNamed({String v = 'ok!'}) {
Expect.equals('ok', o.substring(0, 2));
Expect.equals('ok', v.substring(0, 2));
return o + v;
}
@override
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
int fIntInt(int a, [int b = 0]) {
Expect.equals('ok', o.substring(0, 2));
return a + b;
}
@override
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
double fIntDouble(int a, [double b = 0.0]) {
Expect.equals('ok', o.substring(0, 2));
return a + b;
}
@override
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
double fDoubleDouble(double a, [double b = 0.0]) {
Expect.equals('ok', o.substring(0, 2));
return a + b;
}
@override
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
int fIntOptionalInt([int a = 0, int b = 0]) {
Expect.equals('ok', o.substring(0, 2));
return a + b;
}
@override
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
double fIntOptionalDouble([int a = 0, double b = 0.0]) {
Expect.equals('ok', o.substring(0, 2));
return a + b;
}
@override
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
double fDoubleOptionalDouble([double a = 0.0, double b = 0.0]) {
Expect.equals('ok', o.substring(0, 2));
return a + b;
}
@override
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
int fIntNamedInt(int a, {int b = 0}) {
Expect.equals('ok', o.substring(0, 2));
return a + b;
}
@override
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
double fIntNamedDouble(int a, {double b = 0.0}) {
Expect.equals('ok', o.substring(0, 2));
return a + b;
}
@override
@pragma('vm:never-inline')
@pragma('vm:testing:print-flow-graph')
double fDoubleNamedDouble(double a, {double b = 0.0}) {
Expect.equals('ok', o.substring(0, 2));
return a + b;
}
}
void testDirectCalls1(String str, int ia, int ib) {
final da = ia.toDouble();
final db = ib.toDouble();
final child = ChildSimple(str);
Expect.equals("ok+ok+", child.f(str));
Expect.equals("ok+ok!", child.fOptional());
Expect.equals("ok+ok+", child.fOptional(str));
Expect.equals("ok+ok!", child.fNamed());
Expect.equals("ok+ok+", child.fNamed(v: str));
Expect.equals(142, child.fIntInt(ia, ib));
Expect.equals(142, child.fIntDouble(ia, db));
Expect.equals(142, child.fDoubleDouble(da, db));
Expect.equals(42, child.fIntOptionalInt(ia));
Expect.equals(142, child.fIntOptionalInt(ia, ib));
Expect.equals(42, child.fIntOptionalDouble(ia));
Expect.equals(142, child.fIntOptionalDouble(ia, db));
Expect.equals(42, child.fDoubleOptionalDouble(da));
Expect.equals(142, child.fDoubleOptionalDouble(da, db));
Expect.equals(42, child.fIntNamedInt(ia));
Expect.equals(142, child.fIntNamedInt(ia, b: ib));
Expect.equals(42, child.fIntNamedDouble(ia));
Expect.equals(142, child.fIntNamedDouble(ia, b: db));
Expect.equals(42, child.fDoubleNamedDouble(da));
Expect.equals(142, child.fDoubleNamedDouble(da, b: db));
}
void testDirectCalls2(String str, int ia, int ib) {
final da = ia.toDouble();
final db = ib.toDouble();
final child = ChildConvertingParametersToOptional(str);
Expect.equals("ok+ok!", child.f());
Expect.equals("ok+ok+", child.f(str));
Expect.equals("ok+ok!", child.fOptional());
Expect.equals("ok+ok+", child.fOptional(str));
Expect.equals("ok+ok!", child.fNamed());
Expect.equals("ok+ok+", child.fNamed(v: str));
Expect.equals(42, child.fIntInt(ia));
Expect.equals(142, child.fIntInt(ia, ib));
Expect.equals(42, child.fIntDouble(ia));
Expect.equals(142, child.fIntDouble(ia, db));
Expect.equals(42, child.fDoubleDouble(da));
Expect.equals(142, child.fDoubleDouble(da, db));
Expect.equals(0, child.fIntOptionalInt());
Expect.equals(42, child.fIntOptionalInt(ia));
Expect.equals(142, child.fIntOptionalInt(ia, ib));
Expect.equals(0, child.fIntOptionalDouble());
Expect.equals(42, child.fIntOptionalDouble(ia));
Expect.equals(142, child.fIntOptionalDouble(ia, db));
Expect.equals(0, child.fDoubleOptionalDouble());
Expect.equals(42, child.fDoubleOptionalDouble(da));
Expect.equals(142, child.fDoubleOptionalDouble(da, db));
Expect.equals(42, child.fIntNamedInt(ia));
Expect.equals(142, child.fIntNamedInt(ia, b: ib));
Expect.equals(42, child.fIntNamedDouble(ia));
Expect.equals(142, child.fIntNamedDouble(ia, b: db));
Expect.equals(42, child.fDoubleNamedDouble(da));
Expect.equals(142, child.fDoubleNamedDouble(da, b: db));
}
void testVirtualCalls(Base child, int ia, int ib) {
final da = ia.toDouble();
final db = ib.toDouble();
Expect.equals("ok+ok+", child.f(child.o));
Expect.equals("ok+ok!", child.fOptional());
Expect.equals("ok+ok+", child.fOptional(child.o));
Expect.equals("ok+ok!", child.fNamed());
Expect.equals("ok+ok+", child.fNamed(v: child.o));
Expect.equals(142, child.fIntInt(ia, ib));
Expect.equals(142, child.fIntDouble(ia, db));
Expect.equals(142, child.fDoubleDouble(da, db));
Expect.equals(42, child.fIntOptionalInt(ia));
Expect.equals(142, child.fIntOptionalInt(ia, ib));
Expect.equals(42, child.fIntOptionalDouble(ia));
Expect.equals(142, child.fIntOptionalDouble(ia, db));
Expect.equals(42, child.fDoubleOptionalDouble(da));
Expect.equals(142, child.fDoubleOptionalDouble(da, db));
Expect.equals(42, child.fIntNamedInt(ia));
Expect.equals(142, child.fIntNamedInt(ia, b: ib));
Expect.equals(42, child.fIntNamedDouble(ia));
Expect.equals(142, child.fIntNamedDouble(ia, b: db));
Expect.equals(42, child.fDoubleNamedDouble(da));
Expect.equals(142, child.fDoubleNamedDouble(da, b: db));
}
void runTests(
List<String> args,
List<void Function(String, int, int)> directCallsTests,
List<Base Function(String)> childClassFactories,
) {
final ia = args.length >= 1 ? int.parse(args[0]) : 42;
final ib = args.length >= 2 ? int.parse(args[1]) : 100;
final str = args.length >= 3 ? args[2] : 'ok+';
for (var test in directCallsTests) {
test(str, ia, ib);
}
for (var f in childClassFactories) {
testVirtualCalls(f(str), ia, ib);
}
}
final directCallsTests = [testDirectCalls1, testDirectCalls2];
final childClassFactories = [
ChildSimple.new,
ChildConvertingParametersToOptional.new,
];
void main(List<String> args) {
runTests(args, directCallsTests, childClassFactories);
}
void _matchIL(
FlowGraph graph, {
required List<String?> parameters,
bool argDesc = false,
}) {
graph.match([
match.block('Graph'),
match.block('Function', [
for (var i = 0; i < parameters.length; i++)
if (parameters[i] != null)
match.Parameter(index: i, location: parameters[i]),
if (argDesc)
match.Parameter(index: parameters.length, location: 'reg(cpu)'),
]),
]);
}
void matchIL$ChildSimple$f(FlowGraph graph) {
_matchIL(graph, parameters: ['reg(cpu)', 'stack(word)']);
}
void matchIL$ChildSimple$fOptional(FlowGraph graph) {
_matchIL(graph, parameters: ['reg(cpu)', null], argDesc: true);
}
void matchIL$ChildSimple$fNamed(FlowGraph graph) {
_matchIL(graph, parameters: ['reg(cpu)', null], argDesc: true);
}
void matchIL$ChildSimple$fIntInt(FlowGraph graph) {
_matchIL(
graph,
parameters: [
'reg(cpu)',
is32BitConfiguration ? '(reg(cpu), reg(cpu))' : 'reg(cpu)',
'stack(word)',
],
);
}
void matchIL$ChildSimple$fIntDouble(FlowGraph graph) {
_matchIL(
graph,
parameters: [
'reg(cpu)',
is32BitConfiguration ? '(reg(cpu), reg(cpu))' : 'reg(cpu)',
'stack(word)',
],
);
}
void matchIL$ChildSimple$fDoubleDouble(FlowGraph graph) {
_matchIL(graph, parameters: ['reg(cpu)', 'reg(fpu)', 'stack(word)']);
}
void matchIL$ChildSimple$fIntOptionalInt(FlowGraph graph) {
_matchIL(graph, parameters: ['reg(cpu)', null, null], argDesc: true);
}
void matchIL$ChildSimple$fIntOptionalDouble(FlowGraph graph) {
_matchIL(graph, parameters: ['reg(cpu)', null, null], argDesc: true);
}
void matchIL$ChildSimple$fDoubleOptionalDouble(FlowGraph graph) {
_matchIL(graph, parameters: ['reg(cpu)', null, null], argDesc: true);
}
void matchIL$ChildSimple$fIntNamedInt(FlowGraph graph) {
_matchIL(graph, parameters: ['reg(cpu)', null, null], argDesc: true);
}
void matchIL$ChildSimple$fIntNamedDouble(FlowGraph graph) {
_matchIL(graph, parameters: ['reg(cpu)', null, null], argDesc: true);
}
void matchIL$ChildSimple$fDoubleNamedDouble(FlowGraph graph) {
_matchIL(graph, parameters: ['reg(cpu)', null, null], argDesc: true);
}
void matchIL$ChildConvertingParametersToOptional$f(FlowGraph graph) {
_matchIL(graph, parameters: ['reg(cpu)', null], argDesc: true);
}
void matchIL$ChildConvertingParametersToOptional$fOptional(FlowGraph graph) {
_matchIL(graph, parameters: ['reg(cpu)', null], argDesc: true);
}
void matchIL$ChildConvertingParametersToOptional$fNamed(FlowGraph graph) {
_matchIL(graph, parameters: ['reg(cpu)', null], argDesc: true);
}
void matchIL$ChildConvertingParametersToOptional$fIntInt(FlowGraph graph) {
_matchIL(
graph,
parameters: [
'reg(cpu)',
is32BitConfiguration ? '(reg(cpu), reg(cpu))' : 'reg(cpu)',
null,
],
argDesc: true,
);
}
void matchIL$ChildConvertingParametersToOptional$fIntDouble(FlowGraph graph) {
_matchIL(
graph,
parameters: [
'reg(cpu)',
is32BitConfiguration ? '(reg(cpu), reg(cpu))' : 'reg(cpu)',
null,
],
argDesc: true,
);
}
void matchIL$ChildConvertingParametersToOptional$fDoubleDouble(
FlowGraph graph,
) {
_matchIL(graph, parameters: ['reg(cpu)', 'reg(fpu)', null], argDesc: true);
}
void matchIL$ChildConvertingParametersToOptional$fIntOptionalInt(
FlowGraph graph,
) {
matchIL$ChildSimple$fIntOptionalInt(graph);
}
void matchIL$ChildConvertingParametersToOptional$fIntOptionalDouble(
FlowGraph graph,
) {
matchIL$ChildSimple$fIntOptionalDouble(graph);
}
void matchIL$ChildConvertingParametersToOptional$fDoubleOptionalDouble(
FlowGraph graph,
) {
matchIL$ChildSimple$fDoubleOptionalDouble(graph);
}
void matchIL$ChildConvertingParametersToOptional$fIntNamedInt(FlowGraph graph) {
matchIL$ChildSimple$fIntNamedInt(graph);
}
void matchIL$ChildConvertingParametersToOptional$fIntNamedDouble(
FlowGraph graph,
) {
matchIL$ChildSimple$fIntNamedDouble(graph);
}
void matchIL$ChildConvertingParametersToOptional$fDoubleNamedDouble(
FlowGraph graph,
) {
matchIL$ChildSimple$fDoubleNamedDouble(graph);
}