// 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. import "package:expect/expect.dart"; // Invocation and noSuchMethod testing. Map listToNamedArguments(list) { var iterator = list.iterator; var result = new Map(); while (iterator.moveNext()) { Symbol key = iterator.current; Expect.isTrue(iterator.moveNext()); result[key] = iterator.current; } return result; } /** 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(Invocation m) => last = m; flif(int x) { Expect.fail("never get here"); } flaf([int x]) { Expect.fail("never get here"); } flof({int y}) { Expect.fail("never get here"); } get wut => this; 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 Invocation. * * Call without optionals for getters, with only positional for setters, * and with both optionals for everything else. */ testInvocationMirror(Invocation im, Symbol name, [List positional, List named]) { Expect.isTrue(im is Invocation, "is Invocation"); 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(0, im.positionalArguments.length, "$name:#positional"); Expect.equals(0, im.namedArguments.length, "$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(0, im.namedArguments.length, "$name:#named"); return; } Map namedArguments = listToNamedArguments(named); 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.listEquals(positional, im.positionalArguments); Expect.equals( namedArguments.length, im.namedArguments.length, "$name:#named"); namedArguments.forEach((k, v) { Expect.isTrue( im.namedArguments.containsKey(k), "$name:?namedArguments[$k]"); Expect.equals(v, im.namedArguments[k], "$name:namedArguments[$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, const Symbol('bar')); testInvocationMirror((n..bar = 42).last, const Symbol('bar='), [42]); testInvocationMirror(n.bar(), const Symbol('bar'), [], []); testInvocationMirror(n.bar(42), const Symbol('bar'), [42], []); testInvocationMirror( n.bar(x: 42), const Symbol('bar'), [], [const Symbol("x"), 42]); testInvocationMirror( n.bar(37, x: 42), const Symbol('bar'), [37], [const Symbol("x"), 42]); // Missing operator access. testInvocationMirror(n + 4, const Symbol('+'), [4], []); testInvocationMirror(n - 4, const Symbol('-'), [4], []); testInvocationMirror(-n, const Symbol('unary-'), [], []); testInvocationMirror(n[42], const Symbol('[]'), [42], []); testInvocationMirror((n..[37] = 42).last, const Symbol('[]='), [37, 42], []); // Calling as function when it's not. testInvocationMirror(n(), const Symbol('call'), [], []); testInvocationMirror(n(42), const Symbol('call'), [42], []); testInvocationMirror( n(x: 42), const Symbol('call'), [], [const Symbol("x"), 42]); testInvocationMirror( n(37, x: 42), const Symbol('call'), [37], [const Symbol("x"), 42]); // Calling with arguments not matching existing call method. testInvocationMirror(c(), const Symbol('call'), [], []); testInvocationMirror(c(37, 42), const Symbol('call'), [37, 42], []); testInvocationMirror( c(x: 42), const Symbol('call'), [], [const Symbol("x"), 42]); testInvocationMirror( c(37, x: 42), const Symbol('call'), [37], [const Symbol("x"), 42]); // Wrong arguments to existing function. testInvocationMirror(n.flif(), const Symbol("flif"), [], []); testInvocationMirror(n.flif(37, 42), const Symbol("flif"), [37, 42], []); testInvocationMirror( n.flif(x: 42), const Symbol("flif"), [], [const Symbol("x"), 42]); testInvocationMirror( n.flif(37, x: 42), const Symbol("flif"), [37], [const Symbol("x"), 42]); testInvocationMirror((n..flif = 42).last, const Symbol("flif="), [42]); testInvocationMirror(n.flaf(37, 42), const Symbol("flaf"), [37, 42], []); testInvocationMirror( n.flaf(x: 42), const Symbol("flaf"), [], [const Symbol("x"), 42]); testInvocationMirror( n.flaf(37, x: 42), const Symbol("flaf"), [37], [const Symbol("x"), 42]); testInvocationMirror((n..flaf = 42).last, const Symbol("flaf="), [42]); testInvocationMirror(n.flof(37, 42), const Symbol("flof"), [37, 42], []); testInvocationMirror( n.flof(x: 42), const Symbol("flof"), [], [const Symbol("x"), 42]); testInvocationMirror( n.flof(37, y: 42), const Symbol("flof"), [37], [const Symbol("y"), 42]); testInvocationMirror((n..flof = 42).last, const Symbol("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, const Symbol("wut="), [42]); testInvocationMirror((n..plif = 42).last, const Symbol("plif="), [42]); testInvocationMirror((n..plaf = 42).last, const Symbol("plaf="), [42]); // Trick call to n.call - wut is a getter returning n again. testInvocationMirror(n.wut(42), const Symbol("call"), [42], []); // Calling noSuchMethod itself, badly. testInvocationMirror(n.noSuchMethod(), const Symbol("noSuchMethod"), [], []); testInvocationMirror( n.noSuchMethod(37, 42), const Symbol("noSuchMethod"), [37, 42], []); testInvocationMirror(n.noSuchMethod(37, x: 42), const Symbol("noSuchMethod"), [37], [const Symbol("x"), 42]); testInvocationMirror(n.noSuchMethod(x: 42), const Symbol("noSuchMethod"), [], [const Symbol("x"), 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); } class M extends N { noSuchMethod(Invocation m) { throw "never get here"; } testSuperCalls() { // Missing property/method access. testInvocationMirror(super.bar, const Symbol('bar')); testInvocationMirror(() { super.bar = 42; return last; }(), const Symbol('bar='), [42]); testInvocationMirror(super.bar(), const Symbol('bar'), [], []); testInvocationMirror(super.bar(42), const Symbol('bar'), [42], []); testInvocationMirror( super.bar(x: 42), const Symbol('bar'), [], [const Symbol("x"), 42]); testInvocationMirror(super.bar(37, x: 42), const Symbol('bar'), [37], [const Symbol("x"), 42]); // Missing operator access. testInvocationMirror(super + 4, const Symbol('+'), [4], []); testInvocationMirror(super - 4, const Symbol('-'), [4], []); testInvocationMirror(-super, const Symbol('unary-'), [], []); testInvocationMirror(super[42], const Symbol('[]'), [42], []); testInvocationMirror(() { super[37] = 42; return last; }(), const Symbol('[]='), [37, 42], []); // Wrong arguments to existing function. testInvocationMirror(super.flif(), const Symbol("flif"), [], []); testInvocationMirror( super.flif(37, 42), const Symbol("flif"), [37, 42], []); testInvocationMirror( super.flif(x: 42), const Symbol("flif"), [], [const Symbol("x"), 42]); testInvocationMirror(super.flif(37, x: 42), const Symbol("flif"), [37], [const Symbol("x"), 42]); testInvocationMirror(() { super.flif = 42; return last; }(), const Symbol("flif="), [42]); testInvocationMirror( super.flaf(37, 42), const Symbol("flaf"), [37, 42], []); testInvocationMirror( super.flaf(x: 42), const Symbol("flaf"), [], [const Symbol("x"), 42]); testInvocationMirror(super.flaf(37, x: 42), const Symbol("flaf"), [37], [const Symbol("x"), 42]); testInvocationMirror(() { super.flaf = 42; return last; }(), const Symbol("flaf="), [42]); testInvocationMirror( super.flof(37, 42), const Symbol("flof"), [37, 42], []); testInvocationMirror( super.flof(x: 42), const Symbol("flof"), [], [const Symbol("x"), 42]); testInvocationMirror(super.flof(37, y: 42), const Symbol("flof"), [37], [const Symbol("y"), 42]); testInvocationMirror(() { super.flof = 42; return last; }(), const Symbol("flof="), [42]); // Reading works. Expect.isTrue(super.flif is Function); Expect.isTrue(super.flaf is Function); Expect.isTrue(super.flof is Function); // Writing to read-only fields. testInvocationMirror(() { super.wut = 42; return last; }(), const Symbol("wut="), [42]); testInvocationMirror(() { super.plif = 42; return last; }(), const Symbol("plif="), [42]); testInvocationMirror(() { super.plaf = 42; return last; }(), const Symbol("plaf="), [42]); // Calling noSuchMethod itself, badly. testInvocationMirror( super.noSuchMethod(), const Symbol("noSuchMethod"), [], []); testInvocationMirror( super.noSuchMethod(37, 42), const Symbol("noSuchMethod"), [37, 42], []); testInvocationMirror(super.noSuchMethod(37, x: 42), const Symbol("noSuchMethod"), [37], [const Symbol("x"), 42]); testInvocationMirror(super.noSuchMethod(x: 42), const Symbol("noSuchMethod"), [], [const Symbol("x"), 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 = super.flif; 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(); new M().testSuperCalls(); }