Skip to content

Commit

Permalink
Add test about evaluation order
Browse files Browse the repository at this point in the history
This CL adds a test of the evaluation order in the case where a function
invocation `g(a)` or `r.g(a)` involves the invocation of a getter `g` that
returns a function object, and that function object is invoked with an
actual argument `a`.

The expectation in the test is that evaluation occurs left-to-right in
every case, with one exception: when `g` is a class instance getter (this
does not apply to extension instance getters) the actual argument list
is evaluated before the getter. This is the actually implemented behavior,
and the specification is being updated to specify this behavior
(cf. dart-lang/language#2182).

------- Old description:

A piece of technical debt which has been around for several years is the
fact that the specified left-to-right evaluation order isn't implemented
everywhere.

In particular, with an ordinary invocation like `r.m(a)` where `m` is a
getter that returns a function, the argument is evaluated before the
getter is called, which is not a left-to-right ordering.

This CL adds a test (with 2 libraries) where the evaluation order is
detected, such that we can decide how to proceed.

Change-Id: Ia2619fe6b4c4cf4cec63bac9c9f834306bdefe52
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/238903
Reviewed-by: Lasse Nielsen <[email protected]>
Commit-Queue: Erik Ernst <[email protected]>
  • Loading branch information
eernstg authored and Commit Bot committed Apr 8, 2022
1 parent ca59d05 commit b71bd25
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 0 deletions.
14 changes: 14 additions & 0 deletions tests/language/call/evaluation_order_lib.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright (c) 2022, 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.

// Used from 'evaluation_order_test.dart'.

import 'evaluation_order_test.dart';

get arg => argumentEffect();

void Function(void) get m {
getterEffect();
return (_) {};
}
115 changes: 115 additions & 0 deletions tests/language/call/evaluation_order_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// Copyright (c) 2022, 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 tests the evaluation order in the case where a function
// invocation `m(a)` or `r.m(a)` involves the invocation of a getter `m`
// that returns a function object, and that function object is invoked
// with an actual argument `arg`.
//
// The expectation is that evaluation occurs left-to-right in every case,
// with one exception: when `m` is a class instance getter (this does not
// even apply to extension instance getters) the actual argument list is
// evaluated before the getter.

import 'package:expect/expect.dart';
import 'evaluation_order_lib.dart' as lib;

String effects = '';

void clearEffects() {
effects = '';
}

void getterEffect() {
effects += 'G';
}

void argumentEffect() {
effects += 'A';
}

get arg => argumentEffect();

class A {
void Function(void) get m {
getterEffect();
return (_) {};
}

static void Function(void) get n {
getterEffect();
return (_) {};
}
}

class B extends A {
void doTest() {
test('Instance getter on explicit this', 'AG', () => this.m(arg));
test('Instance getter on implicit this', 'GA', () => m(arg));
test('Instance getter on super', 'GA', () => super.m(arg));
}
}

mixin M on A {
void doTest() {
test('Instance getter on explicit this', 'AG', () => this.m(arg));
test('Instance getter on implicit this', 'GA', () => m(arg));
test('Instance getter on super', 'GA', () => super.m(arg));
}
}

class AM = A with M;
class MockAM = MockA with M;

class MockA implements A {
noSuchMethod(Invocation i) {
getterEffect();
return (_) {};
}

void Function(void) get m;
}

void Function(void) get m {
getterEffect();
return (_) {};
}

extension E on int {
void Function(void) get m {
getterEffect();
return (_) {};
}

static void Function(void) get n {
getterEffect();
return (_) {};
}
}

void test(String name, String expectation, void Function() code) {
clearEffects();
code();
Expect.equals(expectation, effects, name);
}

main() {
var a = A();
dynamic d = a;
A mockA = MockA();
dynamic mockD = mockA;

test('Instance getter on A', 'AG', () => a.m(arg));
test('Instance getter on dynamic A', 'AG', () => d.m(arg));
test('Instance getter on MockA', 'AG', () => mockA.m(arg));
test('Instance getter on dynamic MockA', 'AG', () => mockD.m(arg));
test('Static getter', 'GA', () => A.n(arg));
test('Top-level getter', 'GA', () => m(arg));
test('Prefix-imported getter', 'GA', () => lib.m(arg));
test('Extension instance getter', 'GA', () => 1.m(arg));
test('Extension static getter', 'GA', () => E.n(arg));
B().doTest();
AM().doTest();
MockAM().doTest();
}

0 comments on commit b71bd25

Please sign in to comment.