// 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.

// This test verifies that NoSuchMethodError thrown from null checks
// corresponding to devirtualized calls in AOT mode have detailed messages
// (dartbug.com/32863).

import "package:expect/expect.dart";

class A {
  @pragma("vm:entry-point") // Prevent obfuscation
  void foo() {
    Expect.fail('A.foo should not be reachable');
  }

  @pragma("vm:entry-point") // Prevent obfuscation
  dynamic get bar {
    Expect.fail('A.bar should not be reachable');
  }

  @pragma("vm:entry-point") // Prevent obfuscation
  set bazz(int x) {
    Expect.fail('A.bazz should not be reachable');
  }
}

dynamic myNull;
dynamic doubleNull;
dynamic intNull;

main(List<String> args) {
  // Make sure value of `myNull` is not a compile-time null and
  // devirtualization happens.
  if (args.length > 42) {
    myNull = new A();
    doubleNull = 3.14;
    intNull = 2;
  }

  Expect.throws(
    () => myNull.foo(),
    (e) =>
        e is NoSuchMethodError &&
        e.toString().startsWith(
          'NoSuchMethodError: The method \'foo\' was called on null.',
        ),
  );

  Expect.throws(
    () => myNull.foo,
    (e) =>
        e is NoSuchMethodError &&
        e.toString().startsWith(
          'NoSuchMethodError: The getter \'foo\' was called on null.',
        ),
  );

  Expect.throws(
    () => myNull.bar,
    (e) =>
        e is NoSuchMethodError &&
        e.toString().startsWith(
          'NoSuchMethodError: The getter \'bar\' was called on null.',
        ),
  );

  Expect.throws(
    () => myNull.bar(),
    (e) =>
        e is NoSuchMethodError &&
        e.toString().startsWith(
          'NoSuchMethodError: The method \'bar\' was called on null.',
        ),
  );

  Expect.throws(
    () => myNull!,
    (e) =>
        e is TypeError &&
        e.toString().contains('Null check operator used on a null value'),
  );

  Expect.throws(
    () {
      myNull.bazz = 3;
    },
    (e) =>
        e is NoSuchMethodError &&
        e.toString().startsWith(
          'NoSuchMethodError: The setter \'bazz=\' was called on null.',
        ),
  );

  Expect.throws(
    () => doubleNull + 2.17,
    (e) =>
        e is NoSuchMethodError &&
        e.toString().startsWith(
          'NoSuchMethodError: The method \'+\' was called on null.',
        ),
  );

  Expect.throws(() => 9.81 - doubleNull, (e) => e is TypeError);

  Expect.throws(
    () => intNull * 7,
    (e) =>
        e is NoSuchMethodError &&
        e.toString().startsWith(
          'NoSuchMethodError: The method \'*\' was called on null.',
        ),
  );
}
