blob: 765fe08f3ff9c029916a39b9750df56baa7fdc1d [file] [log] [blame]
// Copyright (c) 2012, 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.
// InvocationMirror and noSuchMethod testing.
/** Class with noSuchMethod that returns the mirror */
class N {
// Storage for the last argument to noSuchMethod.
// Needed for setters, which don't evaluate to the return value.
var last;
noSuchMethod(InvocationMirror m) => last = m;
get wut => this;
flif(int x) { Expect.fail("never get here"); }
flaf([int x]) { Expect.fail("never get here"); }
flof({int y}) { Expect.fail("never get here"); }
final int plif = 99;
int get plaf { Expect.fail("never get here"); return 0; }
}
/** As [N] but also implements 'call', so we can call it with wrong arguments.*/
class C extends N {
call(int x) { Expect.fail("never get here"); }
}
/**
* Checks the data of an InvocationMirror.
*
* Call without optionals for getters, with only positional for setters,
* and with both optionals for everything else.
*/
testInvocationMirror(InvocationMirror im, String name,
[List positional, Map named]) {
Expect.isTrue(im is InvocationMirror, "is InvocationMirror");
Expect.equals(name, im.memberName, "name");
if (named == null) {
Expect.isTrue(im.isAccessor, "$name:isAccessor");
Expect.isFalse(im.isMethod, "$name:isMethod");
if (positional == null) {
Expect.isTrue(im.isGetter, "$name:isGetter");
Expect.isFalse(im.isSetter, "$name:isSetter");
Expect.equals(null, im.positionalArguments, "$name:positional");
Expect.equals(null, im.namedArguments, "$name:named");
return;
}
Expect.isTrue(im.isSetter, "$name:isSetter");
Expect.isFalse(im.isGetter, "$name:isGetter");
Expect.equals(1, im.positionalArguments.length, "$name:#positional");
Expect.equals(positional[0], im.positionalArguments[0],
"$name:positional[0]");
Expect.equals(null, im.namedArguments, "$name:named");
return;
}
Expect.isTrue(im.isMethod, "$name:isMethod");
Expect.isFalse(im.isAccessor, "$name:isAccessor");
Expect.isFalse(im.isSetter, "$name:isSetter");
Expect.isFalse(im.isGetter, "$name:isGetter");
Expect.equals(positional.length, im.positionalArguments.length);
for (int i = 0; i < positional.length; i++) {
Expect.equals(positional[i], im.positionalArguments[i],
"$name:positional[$i]");
}
Expect.equals(named.length, im.namedArguments.length, "$name:#named");
named.forEach((k, v) {
Expect.isTrue(im.namedArguments.containsKey(k), "$name:?named[$k]");
Expect.equals(v, im.namedArguments[k], "$name:named[$k]");
});
}
// Test different ways that noSuchMethod can be called.
testInvocationMirrors() {
var n = new N();
var c = new C();
// Missing property/method access.
testInvocationMirror(n.bar, 'bar');
testInvocationMirror((n..bar = 42).last, 'bar=', [42]);
testInvocationMirror(n.bar(), 'bar', [], {});
testInvocationMirror(n.bar(42), 'bar', [42], {});
testInvocationMirror(n.bar(x: 42), 'bar', [], {"x": 42});
testInvocationMirror(n.bar(37, x: 42), 'bar', [37], {"x": 42});
// Missing operator access.
testInvocationMirror(n + 4, '+', [4], {});
testInvocationMirror(n - 4, '-', [4], {});
testInvocationMirror(-n, 'unary-', [], {});
testInvocationMirror(n[42], '[]', [42], {});
testInvocationMirror((n..[37] = 42).last, '[]=', [37, 42], {});
// Calling as function when it's not.
testInvocationMirror(n(), 'call', [], {});
testInvocationMirror(n(42), 'call', [42], {});
testInvocationMirror(n(x: 42), 'call', [], {"x": 42});
testInvocationMirror(n(37, x: 42), 'call', [37], {"x": 42});
// Calling with arguments not matching existing call method.
testInvocationMirror(c(), 'call', [], {});
testInvocationMirror(c(37, 42), 'call', [37, 42], {});
testInvocationMirror(c(x: 42), 'call', [], {"x": 42});
testInvocationMirror(c(37, x: 42), 'call', [37], {"x": 42});
// Wrong arguments to existing function.
testInvocationMirror(n.flif(), "flif", [], {});
testInvocationMirror(n.flif(37, 42), "flif", [37, 42], {});
testInvocationMirror(n.flif(x: 42), "flif", [], {"x": 42});
testInvocationMirror(n.flif(37, x: 42), "flif", [37], {"x": 42});
testInvocationMirror((n..flif = 42).last, "flif=", [42]);
testInvocationMirror(n.flaf(37, 42), "flaf", [37, 42], {});
testInvocationMirror(n.flaf(x: 42), "flaf", [], {"x": 42});
testInvocationMirror(n.flaf(37, x: 42), "flaf", [37], {"x": 42});
testInvocationMirror((n..flaf = 42).last, "flaf=", [42]);
testInvocationMirror(n.flof(37, 42), "flof", [37, 42], {});
testInvocationMirror(n.flof(x: 42), "flof", [], {"x": 42});
testInvocationMirror(n.flof(37, y: 42), "flof", [37], {"y": 42});
testInvocationMirror((n..flof = 42).last, "flof=", [42]);
// Reading works.
Expect.isTrue(n.flif is Function);
Expect.isTrue(n.flaf is Function);
Expect.isTrue(n.flof is Function);
// Writing to read-only fields.
testInvocationMirror((n..wut = 42).last, "wut=", [42]);
testInvocationMirror((n..plif = 42).last, "plif=", [42]);
testInvocationMirror((n..plaf = 42).last, "plaf=", [42]);
// Trick call to n.call - wut is a getter returning n again.
testInvocationMirror(n.wut(42), "call", [42], {});
// Closurizing a method means that calling it badly will not hit the
// original receivers noSuchMethod, only the one inherited from Object
// by the closure object.
Expect.throws(() { var x = n.flif; x(37, 42); },
(e) => e is NoSuchMethodError);
Expect.throws(() { var x = c.call; x(37, 42); },
(e) => e is NoSuchMethodError);
}
// Test the NoSuchMethodError thrown by different incorrect calls.
testNoSuchMethodErrors() {
test(Function block) {
Expect.throws(block, (e) => e is NoSuchMethodError);
}
var n = new N();
var o = new Object();
test(() => o.bar);
test(() => o.bar = 42);
test(() => o.bar());
test(() => o + 2);
test(() => -o);
test(() => o[0]);
test(() => o[0] = 42);
test(() => o());
test(() => o.toString = 42);
test(() => o.toString(42));
test(() => o.toString(x: 37));
test(() => o.hashCode = 42);
test(() => o.hashCode()); // Thrown by int.noSuchMethod.
test(n.flif); // Extracted method has no noSuchMethod.
}
main() {
testInvocationMirrors();
testNoSuchMethodErrors();
}