#!/usr/bin/env qore
# -*- mode: qore; indent-tabs-mode: nil -*-

%new-style
%enable-all-warnings
%require-types
%strict-args

%requires ../../../../qlib/QUnit.qm

%exec-class ClassesTest

class Issue4605 {
    abstract memberGate(string m);
}

class Issue4605Child inherits Issue4605 {
    string memberGate(string m) {
        return m;
    }
}

# class initialization
class InitTest {
    public {
        softlist<string> l = ".";
        softlist l0 = ".";
    }
}

class Issue4175_BaseA {
    private:internal {
        int i;
    }

    constructor() {
        i = 2;
    }

    int getA() {
        return i;
    }
}

class Issue4175_BaseB {
    private:internal {
        int i;
    }

    constructor() {
        i = 3;
    }

    int getB() {
        return i;
    }
}

class Issue4175_Base {
    public {
        int i;
    }

    constructor(int i) {
        self.i = i;
    }
}

class Issue4175_1 inherits public Issue4175_BaseA, Issue4175_BaseB, Issue4175_Base {
    constructor(int i) : Issue4175_Base(i) {
    }

    int get() {
        return self.i;
    }
}

class Issue4175_2 inherits public Issue4175_BaseA, Issue4175_Base, Issue4175_BaseB {
    constructor(int i) : Issue4175_Base(i) {
    }

    int get() {
        return self.i;
    }
}

class Issue4175_3 inherits public Issue4175_Base, Issue4175_BaseA, Issue4175_BaseB {
    constructor(int i) : Issue4175_Base(i) {
    }

    int get() {
        return self.i;
    }
}

class Issue_3901 {
    private:internal {
        int i = 1;
    }

    *int get() {
        return i;
    }
}

class Issue3869_A {
    test(reference<int> i) {
        ++i;
    }

    code get() {
        return \test();
    }
}

class Issue3869_B {
    private:internal {
        int i = 0;
        code ctest;
        Counter cnt;
    }

    constructor(Counter cnt, Issue3869_A a) {
        ctest = a.get();
        self.cnt = cnt;
        background test();
    }

    test() {
        ctest(\i);
        cnt.dec();
    }

    int get() {
        return i;
    }
}

class Issue3644_B {
    abstract t(bool c = True, *hash h);
}

class Issue3644_C inherits Issue3644_B {
    t(bool c = True, *hash<auto> h) {
    }
}

class Issue3637 {
    Issue3637 getCopy() {
        return copy();
    }
}

class Issue3070_AB {
    abstract int get1();
    abstract int get2();
}

class Issue3070_C1 inherits Issue3070_AB {
    int get1() {
        return 1;
    }

    int test1() {
        return get1();
    }

    int test2() {
        return get2();
    }
}

class Issue3070_C2 inherits Issue3070_AB {
    int get2() {
        return 2;
    }

    int test1() {
        return get1();
    }

    int test2() {
        return get2();
    }
}

class Issue3070_G1 inherits Issue3070_C1, Issue3070_C2 {
}

class Issue3070_G2 inherits Issue3070_C2, Issue3070_C1 {
}

class Issue2970_F0 {
    public {
        int c0 = 0;
        int c1 = 0;
        int c2 = 0;
    }

    int get0b() {
        return c0;
    }
}

class Issue2970_B0 inherits private:internal Issue2970_F0 {
    public {
        int i = 1;
    }

    private:internal {
        int c0 = 1;
    }

    int get0a() {
        return c0;
    }

    int get0a1() {
        return c1;
    }

    int get0b() {
        return Issue2970_F0::get0b();
    }

    int get2() {
        return c2;
    }
}

class Issue2970_F1 {
    public {
        int c0 = 10;
        int c1 = 10;
        int c2 = 10;
    }

    int get1b() {
        return c0;
    }
}

class Issue2970_B1 inherits private:internal Issue2970_F1 {
    private:internal {
        int c0 = 11;
    }

    int get1a() {
        return c0;
    }

    int get1a1() {
        return c1;
    }

    int get1b() {
        return Issue2970_F1::get1b();
    }

    int get2() {
        return c2;
    }
}

class Issue2970_A inherits private:internal Issue2970_B0, private:internal Issue2970_B1 {
    public {
        int c0 = 2;
    }

    constructor() {
    }

    int get0a() {
        return Issue2970_B0::get0a();
    }

    int get0a1() {
        return Issue2970_B0::get0a1();
    }

    int get0b() {
        return Issue2970_B0::get0b();
    }

    int get1a() {
        return Issue2970_B1::get1a();
    }

    int get1a1() {
        return Issue2970_B1::get1a1();
    }

    int get1b() {
        return Issue2970_B1::get1b();
    }

    int get2a() {
        return Issue2970_B0::get2();
    }

    int get2b() {
        return Issue2970_B1::get2();
    }
}

class Issue2970_Foundation {
    private:internal {
        int i = 3;
        static int si = 3;

        int x1 = 3;
        static int sx1 = 3;
    }

    private {
        string str = "test1";
        static string sstr = "test1";
    }

    public {
        int ia = 0;
    }

    int get6() {
        return ia;
    }
}

class Issue2970_Base inherits private:internal Issue2970_Foundation {
    public {
        int x1 = 2;
        static int sx1 = 2;
    }

    private {
        int x2 = 2;
        static int sx2 = 2;
    }

    private:internal {
        int i = 2;
        static int si = 2;
    }

    int get() {
        return x1;
    }

    int get2(int i0) {
        return i0 + i;
    }

    string get3() {
        return str;
    }

    int get6() {
        return ia;
    }

    int get6a() {
        return Issue2970_Foundation::get6();
    }
}

class Issue2970_Child inherits private:internal Issue2970_Base {
    public {
        int i = 1;
        static int si = 1;
        int ia = 2;
    }

    private {
        string str = "test";
        static string sstr = "test";
    }

    private:internal {
        bool flag = False;
    }

    int get() {
        return Issue2970_Base::get();
    }

    int get2() {
        return get2(1);
    }

    string get3() {
        return Issue2970_Base::get3();
    }

    bool get4() {
        return get5(self);
    }

    static bool get5(Issue2970_Child self) {
        return self.flag;
    }

    int get6() {
        return Issue2970_Base::get6();
    }

    int get6a() {
        return Issue2970_Base::get6a();
    }

    int get6bis() {
        return ia;
    }
}

class Issue2741_B {
    int t(int i) {
        return t1(i);
    }
    abstract int t0(int i);
    abstract int t1(int i);
}

class Issue2741_N inherits Issue2741_B {
}

class Issue2741_C1 inherits Issue2741_B {
    private:internal {
        int cv = 1;
    }
    int t0(int i) {
        return i;
    }
    int t1(int i) {
        return i + cv;
    }
    abstract int t2(int i);
}

class Issue2741_C2 inherits Issue2741_C1 {
    int ta() {
        return t1(2);
    }
}

class Issue2741_C3 inherits Issue2741_N, Issue2741_C1, Issue2741_C2 {
    int t2(int i) {
        return i + 2;
    }
}

class Issue2885 {
    public {
        static Mutex m();
    }
}

class Test1 inherits Socket {
    private {
        int a;
        int b;
    }
    public {
        list data;
        hash t;
        int x;
    }

    constructor(any n_a, any n_b, any n_c) {
        a = 1;
        b = 2;
        data = (n_a, n_b, n_c);
    }
    any getData(int elem) {
        if (exists elem)
            return data[elem];
        return data;
    }
    string methodGate(string m) {
        return m;
    }
    string memberGate(string m) {
        return "memberGate-" + m;
    }
    memberNotification(string m) {
        t{m} = self{m};
    }
    code closure(any x) {
        int a = 1;
        # return a closure encapsulating the state of the object
        return string sub (any y) {
            return sprintf("%s-%n-%n-%n", data[1], x, y, ++a);
        };
    }
    any argTest() {
        return argv;
    }
}

class Test2 {
    private {
        any a;
    }

    private int priv() {
        return 1;
    }

    int pub() {
        return call_object_method(self, "priv");
    }

    test() {
        print("ERROR\n");
    }

    doit() {
        delete self;
        test();
    }
}

class Test3 {
    public {
        Test3 t();
    }
}

# issue #2030
class B2030_1 {
    public {
        *list l0;
    }

    constructor() {
        l0 = argv[0];
    }
}

class B2030_2 {
    public {
        *list l1;
    }

    constructor() {
        l1 = argv[0];
    }
}

class C2030 inherits B2030_1, B2030_2 {
    constructor() : B2030_1(argv), B2030_2(argv) {
    }
}

int sub throw_exception() {
    throw "TEST", "test";
}

class Base {
    constructor(int i) {
    }
}

class BaseClassExceptionTest inherits Base {
    constructor() : Base(throw_exception()) {
    }
}

class Base1 {
    public {
        int i;
    }

    constructor(int i) {
        self.i = i;
    }
}

class Child1 inherits Base1 {
    constructor(Base2 b) : Base1(b.doInt(1)) {
    }
}

class Base2 {
    int doInt(int i) {
        return i;
    }

    int doInt(float f) {
        return f.toInt();
    }

    int doOther(string str) {
        return 1;
    }

    int doOther() {
        return 2;
    }

    int m(*string str) {
        return 1;
    }

    int m1() {
        delete argv;
        return 1;
    }

    int m2(int i) {
        delete argv;
        return i + 1;
    }
}

class Child2 inherits Base2 {
    int doInt(int i) {
        return i + 1;
    }

    int doOther() {
        return 3;
    }

    int m2(int i) {
        return i;
    }
}

class Issue1576 {
    public {}
}

# https://github.com/qorelanguage/qore/issues/2380
class Issue2380 {
    private:internal int x() {
        return 1;
    }

    int y(int i) {
        return x();
    }

    z() {}
}

class Issue2380Child inherits Issue2380 {
    int z() {
        return y(1);
    }

    int y(string str) {
        return 2;
    }
}

namespace Issue2744_A {
    namespace Issue2744_B {
        our int v;
    }
}

class Test_3240 {
    public {}

    private {
        int x;
    }

    constructor(int x) {
        self.x = x;
        check();
    }

    int get() {
        return x;
    }

    ref(reference<int> i) {
    }

    private check(int val = x) {
        if (val != 0) {
            throw "ERR";
        }
    }
}

class Issue_3193_3240 {
    public {
        int i = 1;

        int x = get0();

        Test_3240 t;

        static int si = 2;
    }

    constructor() {
        t = new Test_3240(0);
    }

    destructor() {
        if (get2() != 0) {
            throw "ERR";
        }
    }

    int get0(int x = i) {
        return x;
    }

    int get1(int x = si) {
        return x;
    }

    testRefCall() {
        t.ref(\i);
    }

    private:internal int get2(int x = t.get()) {
        return x;
    }
}

class Issue3368 {
    public {
        int i = 1;
    }
}

class Issue4045_A {
    public {
        static int i = Issue4045_B::i + 1;
        static string str = "testA";
        const cstr = Issue4045_B::str;
    }
}

class Issue4045_B {
    public {
        static int i = 1;
        static string str = "testB";
        const cstr = Issue4045_A::str;
    }
}

class Issue4051_A {
    private x(Queue q) {
        q.push(1);
    }

    static test(Issue4051_A obj, Queue q) {
        background sub () {
            obj.x(q);
        }();
    }
}

class Issue4051_B inherits Issue4051_A;

class Issue_4419_B0;
class Issue_4419_C0 inherits Issue_4419_B0;

class Issue_4419_T {
    t0(auto x) {
        throw "X";
    }

    t0(Issue_4419_B0 x) {
    }

    t1(hash<auto> x) {
        throw "X";
    }

    t1(hash<string, Issue_4419_B0> x) {
    }

    t2(list<auto> x) {
        throw "X";
    }

    t2(list<Issue_4419_B0> x) {
    }
}

public class ClassesTest inherits QUnit::Test {
    constructor() : Test("Classes test", "1.0") {
        v = 1;
        addTestCase("issue 4605", \issue4605());
        addTestCase("another abstract test", \anotherAbstractTest());
        addTestCase("issue 4419", \issue4419());
        addTestCase("init test", \initTest());
        addTestCase("issue 4175", \issue4175());
        addTestCase("issue 4051", \issue4051());
        addTestCase("issue 4045", \issue4045());
        addTestCase("issue 3901", \issue3901());
        addTestCase("issue 3869", \issue3869());
        addTestCase("issue 3644", \issue3644());
        addTestCase("issue 3637", \issue3637());
        addTestCase("issue 3368", \issue3368());
        addTestCase("issues 3193 and 3240", \issues_3193_and_3240());
        addTestCase("issue 3070", \issue3070());
        addTestCase("issue 3073", \issue3073());
        addTestCase("issue 2970", \issue2970());
        addTestCase("issue 2885", \issue2885());
        addTestCase("issue 2744", \issue2744());
        addTestCase("issue 2741", \issue2741());
        addTestCase("Class library test", \classLibraryTest());
        addTestCase("Deprecated method test", \deprecatedMethodTest());
        addTestCase("Deletion test", \deletionTest());
        addTestCase("recursive test", \recursiveTest());
        addTestCase("Built-in class w/o methods", \builtinNoMethods());
        addTestCase("Base class tests", \baseClassTests());
        addTestCase("misc tests", \miscTests());
        addTestCase("issue 2380", \issue2380());
        addTestCase("issue 2657", \issue2657());

        # Return for compatibility with test harness that checks return value.
        set_return_value(main());
    }

    issue4605() {
        Issue4605Child c();
        assertEq("x", c.x);
    }

    anotherAbstractTest() {
        Program p(PO_NEW_STYLE);
        assertThrows("ABSTRACT-CLASS-ERROR", \p.parse(), ("
class AB;
class AC inherits AB;
class A {
    abstract AC test();
}
class C inherits A {
    AB test() {}
}

sub test() {
    C c();
}
", "test"));
    }

    issue4419() {
        Issue_4419_T t();
        Issue_4419_C0 v();
        {
            auto x = v;
            assertNothing(t.t0(x));
        }
        {
            hash<auto> x = {
                "a": v,
            };
            assertNothing(t.t1(x));
        }
        {
            list<auto> x = (v,);
            assertNothing(t.t2(x));
        }
    }

    initTest() {
        InitTest t();
        assertEq((".",), t.l);
        assertEq((".",), t.l0);
    }

    issue4175() {
        Issue4175_1 obj1(1);
        assertEq(1, obj1.get());
        assertEq(2, obj1.getA());
        assertEq(3, obj1.getB());

        Issue4175_2 obj2(1);
        assertEq(1, obj2.get());
        assertEq(2, obj2.getA());
        assertEq(3, obj2.getB());

        Issue4175_1 obj3(1);
        assertEq(1, obj3.get());
        assertEq(2, obj3.getA());
        assertEq(3, obj3.getB());
    }

    issue4051() {
        Issue4051_B obj();
        Queue q();
        Issue4051_A::test(obj, q);
        assertEq(1, q.get(2s));
    }

    issue4045() {
        assertEq(Issue4045_A::str, Issue4045_B::cstr);
        assertEq(Issue4045_B::str, Issue4045_A::cstr);
        assertEq(2, Issue4045_A::i);
        assertEq(1, Issue4045_B::i);
    }

    issue3901() {
        Issue_3901 o0();
        Issue_3901 o1 = o0.copy();
        assertEq(1, o1.get());
    }

    issue3869() {
        Issue3869_A a();
        Counter cnt(1);
        Issue3869_B b(cnt, a);
        cnt.waitForZero();
        assertEq(1, b.get());
    }

    issue3644() {
        Issue3644_C c();
        assertTrue(c instanceof Issue3644_C);
    }

    issue3637() {
        Issue3637 o();
        assertTrue(o.getCopy() instanceof Issue3637);
    }

    issue3368() {
        {
            Program p(PO_NEW_STYLE);
            p.importClass("Issue3368");
            p.parse("int sub test() { Issue3368 i = new Issue3368(); return i.i; }", "");
            assertEq(1, p.callFunction("test"));
        }
        {
            Program p(PO_NEW_STYLE);
            p.importClass("Issue3368");
            p.parse("int sub test() { Issue3368 i(); return i.i; }", "");
            assertEq(1, p.callFunction("test"));
        }
    }

    issues_3193_and_3240() {
        Issue_3193_3240 i();
        assertEq(1, i.get0());
        assertEq(2, i.get1());
        assertEq(1, i.x);

        {
            Program p(PO_NEW_STYLE);
            assertThrows("PARSE-EXCEPTION", \p.parse(),
                ("class T { public { int x = 1; } static int test(int i = x) { return i; } }", ""));
        }

        assertNothing(i.testRefCall());
    }

    issue3070() {
        Issue3070_G1 g1();
        Issue3070_G2 g2();
        assertEq(1, g1.test1());
        assertEq(1, g1.get1());
        assertEq(2, g1.test2());
        assertEq(2, g1.get2());
        assertEq(1, g2.test1());
        assertEq(1, g2.get1());
        assertEq(2, g2.test2());
        assertEq(2, g2.get2());
    }

    issue3073() {
        string fn = get_script_path();
        assertEq(Type::Hash, call_static_method("ReadOnlyFile", "hstat", fn).type());
        assertEq(Type::Hash, call_static_method("Qore::ReadOnlyFile", "hstat", fn).type());
        assertEq(Type::Hash, call_static_method_args("ReadOnlyFile", "hstat", fn).type());
        assertEq(Type::Hash, call_static_method_args("Qore::ReadOnlyFile", "hstat", fn).type());

        assertThrows("UNKNOWN-CLASS", \call_static_method(), ("XXX", "xxx"));
        assertThrows("UNKNOWN-CLASS", \call_static_method(), ("XXX::ReadOnlyFile", "xxx"));
        assertThrows("UNKNOWN-METHOD", \call_static_method(), ("ReadOnlyFile", "xxx"));
        assertThrows("UNKNOWN-CLASS", \call_static_method_args(), ("XXX", "xxx"));
        assertThrows("UNKNOWN-CLASS", \call_static_method_args(), ("XXX::ReadOnlyFile", "xxx"));
        assertThrows("UNKNOWN-METHOD", \call_static_method_args(), ("ReadOnlyFile", "xxx"));
    }

    issue2970() {
        Issue2970_Child c();
        assertEq(2, c.get());
        assertEq(3, c.get2());
        assertEq("test1", c.get3());
        assertFalse(c.get4());
        assertEq(0, c.get6());
        assertEq(0, c.get6a());
        assertEq(2, c.get6bis());

        Issue2970_A a();
        assertEq(2, a.c0);
        assertEq(1, a.get0a());
        assertEq(0, a.get0a1());
        assertEq(0, a.get0b());
        assertEq(11, a.get1a());
        assertEq(10, a.get1a1());
        assertEq(10, a.get1b());
        assertEq(0, a.get2a());
        assertEq(10, a.get2b());
    }

    issue2885() {
        Program p(PO_NEW_STYLE);
        p.importClass("Issue2885");
        # this call will abort in debug builds
        p.parse("Issue2885 sub t() { return new Issue2885(); }", "");
        Issue2885 i = p.callFunction("t");
        assertEq(True, i instanceof Issue2885);
    }

    issue2744() {
        assertEq(1, v);
        assertEq(1, Issue2744_B::v);
        assertEq(1, Issue2744_A::Issue2744_B::v);
    }

    issue2741() {
        Issue2741_C3 c();
        assertEq(3, c.t2(1));
        assertEq(3, c.ta());
        assertEq(2, c.t(1));

        {
            Program p(PO_NEW_STYLE);
            p.importClass("Issue2741_N");
            assertThrows("ABSTRACT-CLASS-ERROR", \p.parse(), ("sub t() {new Issue2741_N();}", ""));
        }

        {
            Program p(PO_NEW_STYLE);
            p.importClass("Issue2741_B");
            assertThrows("ABSTRACT-CLASS-ERROR", \p.parse(), ("sub t() {new Issue2741_B();}", ""));
        }
    }

    issue2380() {
        Issue2380Child c();
        assertEq(1, c.z());
    }

    issue2657() {
        Program p(PO_NEW_STYLE);
        # crashes without the fix for #2657
        assertThrows("PARSE-EXCEPTION", \p.parse(), ("class T inherits Err;", ""));
    }

    classLibraryTest() {
        Test1 t(1, "gee", 2);
        assertEq("gee", t.getData(1), "first object");
        assertTrue(exists t.testing, "memberGate() existence");
        assertEq("memberGate-testing", t.testing, "memberGate() value");
        assertEq("test", t.test(), "methodGate() value");
        assertTrue(t instanceof Test1, "first instanceof");
        assertTrue(t instanceof Qore::Socket, "second instanceof");

        # verify private member access protection

        # object -= private member
        assertThrows("PRIVATE-MEMBER", sub () { t -= "a"; });
        # object -= list of private members
        assertThrows("PRIVATE-MEMBER", sub () { t -= ("a", "b"); });

        any t1 = new Test1(1, "gee", 2);
        # delete object's private member
        assertThrows("PRIVATE-MEMBER", sub () { delete t1.a; });
        # reassign object's private member
        assertThrows("PRIVATE-MEMBER", sub () { t1.a = 3; });

        any t2 = new Test2();
        # read object's private member
        assertThrows("PRIVATE-MEMBER", sub () { any x = t2.a + x; });

        # test memberGate
        assertEq("memberGate-a", t.a, "object memberGate() methods");

        # test memberNotification()
        t.x = 1;
        # test object closure
        code c = t.closure(1);
        assertEq("gee-1-2-2", c(2), "first object closure");
        assertEq("gee-1-2-3", c(2), "second object closure");
        assertEq(1, t.t.x, "memberNotification() method");

        # test call_object_method*()
        assertEq((1, 2),  call_object_method(t1, "argTest", 1, 2), "call_object_method()");
        assertEq(NOTHING, call_object_method_args(t1, "argTest"), "first call_object_method_args()");
        assertEq(list(1), call_object_method_args(t1, "argTest", 1), "second call_object_method_args()");
        assertEq((1, 2),  call_object_method_args(t1, "argTest", (1, 2)), "third call_object_method_args()");

        assertThrows("METHOD-IS-PRIVATE", \call_object_method(), (t2, "priv"));
        assertThrows("METHOD-IS-PRIVATE", \t2.pub());
    }

    deprecatedMethodTest() {
        Program p(PO_NEW_STYLE|PO_REQUIRE_TYPES);

        *hash wh = p.parse("class X {deprecated meth() {} other() { meth(); }}", "", WARN_ALL);
        assertEq("DEPRECATED", wh.err);
        assertEq("System", wh.type);
        assertEq(1, wh.line);
        assertEq(1, wh.endline);
    }

    deletionTest() {
        Test2 t();
        assertThrows("OBJECT-ALREADY-DELETED", \t.doit());
    }

    builtinNoMethods() {
        AbstractQuantifiedBidirectionalIterator a = new HashIterator({'a': 1});
        assertTrue(a.next());
        assertEq(1, a.getValue());
    }

    baseClassTests() {
        hash<ExceptionInfo> ex;
        try {
            BaseClassExceptionTest b();
        } catch (hash<ExceptionInfo> nex) {
            ex = nex;
        }
        assertEq(564, ex.callstack[0].line);

        Child2 c2();
        Child1 c1(c2);
        assertEq(2, c1.i);
        assertEq(1, c2.doOther("str"));
        assertEq(1, c2.m());
        assertEq(1, c2.m1(1));
        assertEq(1, c2.m2(1));
        assertEq(2, c2.m2(1, 2));
        assertEq(1, call_object_method(c2, "m1", 1));
        assertEq(1, call_object_method(c2, "m2", 1));
        assertEq(2, call_object_method(c2, "m2", 1, 2));

        {
            Program p(PO_NEW_STYLE|PO_STRICT_ARGS);
            assertThrows("PARSE-TYPE-ERROR", \p.parse(), ("class C { m() {} } sub t() { C c(); c.m(1); }", ""));
        }

        {
            # ensure proper parse-time resolution of scoped method calls
            Program p(PO_NEW_STYLE);
            p.parse("class B{int m(int i){return i+1;}}class C inherits B{m(string str){}int m(any i){return i;}int t(int i){return C::m(i);}}int sub t(int i){C c();return c.t(i);}", "");
            assertEq(1, p.callFunction("t", 1));
        }

        {
            # ensure proper parse-time resolution of scoped static method calls
            Program p(PO_NEW_STYLE);
            p.parse("class B{static int m(int i){return i+1;}}class C inherits B{static m(string str){}static int m(any i){return i;}int t(int i){return C::m(i);}}int sub t(int i){C c();return c.t(i);}", "");
            assertEq(1, p.callFunction("t", 1));
        }

        {
            # ensure proper runtime resolution of scoped method calls
            Program p(PO_NEW_STYLE);
            p.parse("class B{int m(int i){return i+1;}}class C inherits B{m(string str){}int m(any i){return i;}int t(any i){return C::m(i);}}int sub t(any i){C c();return c.t(i);}", "");
            assertEq(1, p.callFunction("t", 1));
        }

        {
            # ensure proper runtime resolution of scoped static method calls
            Program p(PO_NEW_STYLE);
            p.parse("class B{static int m(int i){return i+1;}}class C inherits B{static m(string str){}static int m(any i){return i;}int t(any i){return C::m(i);}}int sub t(any i){C c();return c.t(i);}", "");
            assertEq(1, p.callFunction("t", 1));
        }

        {
            # ensure proper runtime resolution of scoped method call references
            Program p(PO_NEW_STYLE);
            p.parse("class B{int m(int i){return i+1;}}class C inherits B{m(string str){}int m(any i){return i;}code t(){return \\C::m();}}int sub t(int i){C c();code cd = c.t(); return cd(i);}", "");
            assertEq(1, p.callFunction("t", 1));
        }

        {
            # ensure proper runtime resolution of scoped method call references
            Program p(PO_NEW_STYLE);
            p.parse("class B{static int m(int i){return i+1;}}class C inherits B{static m(string str){}static int m(any i){return i;}code t(){return \\m();}}int sub t(int i){C c();code cd = c.t(); return cd(i);}", "");
            assertEq(1, p.callFunction("t", 1));
        }

        {
            # test for resolution in the current class only with scoped method calls
            Program p(PO_NEW_STYLE);
            p.parse("class B{int m(int i){return i+1;}}class C inherits B{int m(string str){return str.toInt();}int t(int i){return C::m(i);}}int sub t(int i){C c();return c.t(i);}", "");
            assertEq(2, p.callFunction("t", 1));
        }

        {
            # test for resolution in the current class only with scoped static method calls
            Program p(PO_NEW_STYLE);
            p.parse("class B{static int m(int i){return i+1;}}class C inherits B{static int m(string str){return str.toInt();}int t(int i){return C::m(i);}}int sub t(int i){C c();return c.t(i);}", "");
            assertEq(2, p.callFunction("t", 1));
        }

        {
            # ensure that methods can be directly accessed
            Program p(PO_NEW_STYLE);
            p.parse("class B{m(){}}class C inherits B{code t(){return\\m();}}code sub t(){C c();return c.t();}", "");
            assertEq(Type::CallReference, p.callFunction("t").type());
        }

        {
            Program p(PO_NEW_STYLE|PO_REQUIRE_TYPES);
            # ensure that private methods cannot be directly accessed
            assertThrows("PARSE-EXCEPTION", \p.parse(), ("class B{private:internal m(){}}class C inherits B{code t(){return\\m();}}", ""));
        }
        {
            Program p(PO_NEW_STYLE|PO_REQUIRE_TYPES);
            # ensure that private methods cannot be directly accessed
            assertThrows("PARSE-ERROR", \p.parse(), ("class B{private m(){}}class C inherits B{}code sub t(){C c();return\\c.m();}", ""));
        }
        {
            Program p(PO_NEW_STYLE|PO_REQUIRE_TYPES);
            # ensure that private methods cannot be directly accessed
            assertThrows("PARSE-ERROR", \p.parse(), ("class B{private:internal m(){}}class C inherits B{}code sub t(){C c();return\\c.m();}", ""));
        }
        {
            Program p(PO_NEW_STYLE|PO_REQUIRE_TYPES);
            # ensure that special methods cannot be directly accessed
            assertThrows("PARSE-EXCEPTION", \p.parse(), ("class B{constructor(){}}class C inherits B{constructor(){}code t(){return\\constructor();}}", ""));
        }
        {
            Program p(PO_NEW_STYLE|PO_REQUIRE_TYPES);
            # ensure that call references cannot be taken of constructors
            assertThrows("PARSE-ERROR", \p.parse(), ("class B{constructor(){}}class C inherits B{constructor(){}}sub t(){C c();code cd=\\c.constructor();cd();}", ""));
        }
        {
            Program p(PO_NEW_STYLE|PO_REQUIRE_TYPES);
            # ensure that call references cannot be taken of constructors
            assertThrows("PARSE-EXCEPTION", \p.parse(), ("class B{constructor(){}}class C inherits B{constructor(){}code t(){return \\constructor();}}", ""));
        }
        {
            Program p(PO_NEW_STYLE|PO_REQUIRE_TYPES);
            # ensure that call references cannot be taken of constructors
            assertThrows("INVALID-METHOD", \p.parse(), ("class B{constructor(){}}class C inherits B{constructor(){}code t(){return \\C::constructor();}}", ""));
        }
        {
            Program p(PO_NEW_STYLE|PO_REQUIRE_TYPES);
            # ensure that call references cannot be taken of constructors
            assertThrows("INVALID-METHOD", \p.parse(), ("class B{constructor(){}}class C inherits B{constructor(){}code t(){return \\B::constructor();}}", ""));
        }
        {
            Program p(PO_NEW_STYLE|PO_REQUIRE_TYPES);
            # ensure that call references cannot be taken of constructors
            assertThrows("PARSE-EXCEPTION", \p.parse(), ("class B{}class C inherits B{code t(){return \\constructor();}}", ""));
        }
        {
            Program p(PO_NEW_STYLE|PO_REQUIRE_TYPES);
            # ensure that call references cannot be taken of constructors
            assertThrows("INVALID-METHOD", \p.parse(), ("class B{}class C inherits B{code t(){return \\C::constructor();}}", ""));
        }
        {
            Program p(PO_NEW_STYLE|PO_REQUIRE_TYPES);
            # ensure that call references cannot be taken of constructors
            assertThrows("INVALID-METHOD", \p.parse(), ("class B{}class C inherits B{code t(){return \\B::constructor();}}", ""));
        }
        {
            Program p(PO_NEW_STYLE|PO_REQUIRE_TYPES);
            # ensure that call references cannot be taken of constructors
            assertThrows("PARSE-ERROR", \p.parse(), ("class B{}class C inherits B{}sub t(){C c();code cd=\\c.constructor();cd();}", ""));
        }
        {
            Program p(PO_NEW_STYLE|PO_REQUIRE_TYPES);
            # ensure that call references cannot be taken of destructors
            assertThrows("PARSE-ERROR", \p.parse(), ("class B{destructor(){}}class C inherits B{destructor(){}}sub t(){C c();code cd=\\c.destructor();cd();}", ""));
        }
        {
            Program p(PO_NEW_STYLE|PO_REQUIRE_TYPES);
            # ensure that call references cannot be taken of destructors
            assertThrows("PARSE-EXCEPTION", \p.parse(), ("class B{destructor(){}}class C inherits B{destructor(){}code t(){return \\destructor();}}", ""));
        }
        {
            Program p(PO_NEW_STYLE|PO_REQUIRE_TYPES);
            # ensure that call references cannot be taken of destructors
            assertThrows("INVALID-METHOD", \p.parse(), ("class B{destructor(){}}class C inherits B{destructor(){}code t(){return \\C::destructor();}}", ""));
        }
        {
            Program p(PO_NEW_STYLE|PO_REQUIRE_TYPES);
            # ensure that call references cannot be taken of destructors
            assertThrows("INVALID-METHOD", \p.parse(), ("class B{destructor(){}}class C inherits B{destructor(){}code t(){return \\B::destructor();}}", ""));
        }
        {
            Program p(PO_NEW_STYLE|PO_REQUIRE_TYPES);
            # ensure that call references cannot be taken of destructors
            assertThrows("PARSE-EXCEPTION", \p.parse(), ("class B{}class C inherits B{code t(){return \\destructor();}}", ""));
        }
        {
            Program p(PO_NEW_STYLE|PO_REQUIRE_TYPES);
            # ensure that call references cannot be taken of destructors
            assertThrows("INVALID-METHOD", \p.parse(), ("class B{}class C inherits B{code t(){return \\C::destructor();}}", ""));
        }
        {
            Program p(PO_NEW_STYLE|PO_REQUIRE_TYPES);
            # ensure that call references cannot be taken of destructors
            assertThrows("INVALID-METHOD", \p.parse(), ("class B{}class C inherits B{code t(){return \\B::destructor();}}", ""));
        }
        {
            Program p(PO_NEW_STYLE|PO_REQUIRE_TYPES);
            # ensure that call references cannot be taken of destructors
            assertThrows("PARSE-ERROR", \p.parse(), ("class B{}class C inherits B{}sub t(){C c();code cd=\\c.destructor();cd();}", ""));
        }
        {
            Program p(PO_NEW_STYLE|PO_REQUIRE_TYPES);
            # ensure that call references cannot be taken of copy methods
            assertThrows("PARSE-EXCEPTION", \p.parse(), ("class B{copy(){}}class C inherits B{copy(){}}sub t(){C c();code cd=\\c.copy();cd();}", ""));
        }
        {
            Program p(PO_NEW_STYLE|PO_REQUIRE_TYPES);
            # ensure that call references cannot be taken of copy methods
            assertThrows("PARSE-EXCEPTION", \p.parse(), ("class B{copy(){}}class C inherits B{copy(){}code t(){return \\copy();}}", ""));
        }
        {
            Program p(PO_NEW_STYLE|PO_REQUIRE_TYPES);
            # ensure that call references cannot be taken of copy methods
            assertThrows("INVALID-METHOD", \p.parse(), ("class B{copy(){}}class C inherits B{copy(){}code t(){return \\C::copy();}}", ""));
        }
        {
            Program p(PO_NEW_STYLE|PO_REQUIRE_TYPES);
            # ensure that call references cannot be taken of copy methods
            assertThrows("INVALID-METHOD", \p.parse(), ("class B{copy(){}}class C inherits B{copy(){}code t(){return \\B::copy();}}", ""));
        }
        {
            Program p(PO_NEW_STYLE|PO_REQUIRE_TYPES);
            # ensure that call references cannot be taken of copy methods
            assertThrows("PARSE-EXCEPTION", \p.parse(), ("class B{}class C inherits B{code t(){return \\copy();}}", ""));
        }
        {
            Program p(PO_NEW_STYLE|PO_REQUIRE_TYPES);
            # ensure that call references cannot be taken of copy methods
            assertThrows("INVALID-METHOD", \p.parse(), ("class B{}class C inherits B{code t(){return \\C::copy();}}", ""));
        }
        {
            Program p(PO_NEW_STYLE|PO_REQUIRE_TYPES);
            # ensure that call references cannot be taken of copy methods
            assertThrows("INVALID-METHOD", \p.parse(), ("class B{}class C inherits B{code t(){return \\B::copy();}}", ""));
        }
        {
            Program p(PO_NEW_STYLE|PO_REQUIRE_TYPES);
            # ensure that call references cannot be taken of copy methods
            assertThrows("PARSE-EXCEPTION", \p.parse(), ("class B{copy(){}}class C inherits B{copy(){}}sub t(){C c();code cd=\\c.copy();cd();}", ""));
        }
        {
            Program p(PO_NEW_STYLE|PO_REQUIRE_TYPES);
            # ensure that circular references in class hierarchies are found
            assertThrows("PARSE-EXCEPTION", \p.parse(), ("class B inherits A; class C inherits B; class A inherits C;", ""));
        }
        {
            Program p(PO_NEW_STYLE|PO_REQUIRE_TYPES);
            assertThrows("PARSE-EXCEPTION", \p.parse(), ("class B inherits A; class C inherits B, A; class A inherits C;", ""));
        }
        {
            # ensure that multiple inheritance allows the case when a single class is inherited more than once
            Program p(PO_NEW_STYLE|PO_REQUIRE_TYPES);
            p.parse("class A; class B inherits A; class E inherits A, B;", "");
            assertTrue(True);
        }
    }

    miscTests() {
        Issue1576 i();
        assertEq(True, i instanceof Issue1576);

        Program p(PO_NEW_STYLE | PO_REQUIRE_TYPES);
        assertThrows("ILLEGAL-ABSTRACT-METHOD", \p.parse(), ("class X{static abstract m();}", ""));
        {
            C2030 c(1, 2, 3);
            assertEq(Type::Object, c.type());
            assertEq((1, 2, 3), c.l0);
            assertEq((1, 2, 3), c.l1);
        }
    }

    recursiveTest() {
        if (!Option::HAVE_STACK_GUARD)
            testSkip("Qore library was not built with stack protection support");

        assertThrows("STACK-LIMIT-EXCEEDED", sub () { Test3 t(); });
    }
}
