
/* FIXME: bound function cases */

/*
 *  Parsing tests for 'new' expressions.  These are rather detailed because
 *  the grammar for the following productions is a bit awkward:
 *
 *    - LeftHandSideExpression
 *    - CallExpression
 *    - NewExpression
 *    - MemberExpression
 */

var t;

/*===
Basic1
Basic1
Basic2 arg1 arg2
Basic3 arg1 arg2
Basic3 arg1 arg2
Basic4 arg1 arg2
===*/

/* Basic cases where constructor is from a MemberExpression (but not another 'new') */

function Basic1() {
}
Basic1.prototype = { 'name': 'Basic1' };

function Basic2(x,y) {
    this.x = x;
    this.y = y;
}
Basic2.prototype = { 'name': 'Basic2' };

basic3 = {
    'constructor': function(x,y) {
                      this.x = x;
                      this.y = y;
                   }
};
basic3.constructor.prototype = { 'name': 'Basic3' };

try {
    // without parenthesis (arguments)
    eval("t = new Basic1; print(t.name);");
} catch (e) {
    print(e.name);
}

try {
    // with no arguments (semantically same as above)
    eval("t = new Basic1(); print(t.name);");
} catch (e) {
    print(e.name);
}

try {
    // with arguments
    eval("t = new Basic2('arg1', 'arg2'); print(t.name, t.x, t.y);");
} catch (e) {
    print(e.name);
}

try {
    // constructor from a property access (MemberExpression)
    eval("t = new basic3.constructor ('arg1', 'arg2'); print(t.name, t.x, t.y);");
} catch (e) {
    print(e.name);
}

try {
    // constructor from a property access (MemberExpression), bracket syntax
    eval("t = new basic3['constructor'] ('arg1', 'arg2'); print(t.name, t.x, t.y);");
} catch (e) {
    print(e.name);
}

try {
    // constructor from a function expression (from MemberExpression)
    // Note: here the 'name' property is not inherited from prototype
    eval("t = new function(x,y) {this.x=x;this.y=y;this.name='Basic4';} ('arg1', 'arg2'); print(t.name, t.x, t.y);");
} catch (e) {
    print(e.name);
}

/*===
TypeError
getTarget/f
===*/

/* The target for 'new' may be any MemberExpression, but it cannot be a
 * CallExpression.  Thus:
 *
 *   new foo() ()
 *
 * Will be parsed as ( new foo() ) ().  For the intended effect, parenthesis
 * are needed:
 *
 *   new (foo()) ()
 */

function getTarget() {
    var f = function() {
    }
    f.prototype = { 'name': 'getTarget/f' };
    return f;
}

try {
    // this will evaluate "new getTarget ()" and then attempt to call the
    // instance, which is not callable -> TypeError
    eval("t = new getTarget() (); print(t.name);");
} catch (e) {
    print(e.name);
}

try {
    // this will first evaluate "getTarget()" to get the constructor,
    // and then call the constructor
    eval("t = new (getTarget()) (); print(t.name);");
} catch (e) {
    print(e.name);
}

/*===
NewNew2 arg1
===*/

/* The 'new' operator may occur multiple times, although this is
 * quite unusual.  It binds more loosely than MemberExpressions
 * and acts right associative.
 *
 * Example below:
 *
 *      new new Foo()
 *  === new (new Foo())
 *
 * This is derived as (using parenthesis for illustration):
 *
 *  NewExpression => new NewExpression
 *                => new (MemberExpression)
 *                => new (new MemberExpression Arguments)
 *                => new (new Foo ())
 */

function NewNew1(x) {
    function f() {
        // snatch 'x' from closure
        this.x = x;
    }
    f.prototype = { 'name': 'NewNew2' };
    return f;
}
NewNew1.prototype = { 'name': 'NewNew1' };

try {
    eval("t = new new NewNew1 ('arg1'); print(t.name, t.x);");
} catch (e) {
    print(e.name);
}

/*===
Bal1A called, this.name=Bal1A
Bal1A
Bal1A called, this.name=Bal1A
Bal1A
Bal1B called, this.name=Bal1B
Bal1B/f called, this.Number.POSITIVE_INFINITY=Infinity
Bal1B/f return value
Bal1C called, this.name=Bal1C
Bal1C/f called, this.Number.POSITIVE_INFINITY=Infinity
Bal1C/f/g called, this.Number.POSITIVE_INFINITY=Infinity
Bal1C/f/g return value
===*/

/*
 *  Parenthesis "balance tests".
 *
 *  new Foo                      basic case
 *  new Foo ()                   parens associated with 'new', not a func call
 *  new Foo () ()                (new Foo ()) ()
 *  new Foo () () ()             ((new Foo ()) ()) ()
 */

function Bal1A() {
    // printing out this.name indicates whether we're being
    // called as a constructor or as a function
    print("Bal1A called, this.name=" + this.name);
}
Bal1A.prototype = { 'name': 'Bal1A' };

function Bal1B() {
    print("Bal1B called, this.name=" + this.name);

    function f() {
        // f() is called as a normal function, this is bound to the
        // global object; printing out a specific value accessible
        // through the global object proves the point.

        print("Bal1B/f called, this.Number.POSITIVE_INFINITY=" + this.Number.POSITIVE_INFINITY);
        return "Bal1B/f return value";
    }
    f.name = "Bal1B/f";  // not inherited

    // Returned value replaces default constructed object; this return
    // value does not have Bal1B.prototype as its internal prototype.
    return f;
}
Bal1B.prototype = { 'name': 'Bal1B' };  // has no effect

function Bal1C() {
    print("Bal1C called, this.name=" + this.name);

    function f() {
        print("Bal1C/f called, this.Number.POSITIVE_INFINITY=" + this.Number.POSITIVE_INFINITY);
        function g() {
            print("Bal1C/f/g called, this.Number.POSITIVE_INFINITY=" + this.Number.POSITIVE_INFINITY);
            return "Bal1C/f/g return value";
        }
        return g;
    }
    f.name = "Bal1C/f";  // not inherited
    return f;
}
Bal1C.prototype = { 'name': 'Bal1C' };  // has no effect

try {
    eval("var t = new Bal1A; print(t.name);");
} catch (e) {
    print(e.name);
}

try {
    eval("var t = new Bal1A (); print(t.name);");
} catch (e) {
    print(e.name);
}

try {
    eval("var t = new Bal1B () (); print(t);");     // here 't' is return value from Bal1B/f
} catch (e) {
    print(e.name);
}

try {
    eval("var t = new Bal1C () () (); print(t);");
} catch (e) {
    print(e.name);
}

/*===
Bal2A called, this.name=Bal2A
Bal2A/f called, this.name=Bal2A/f
Bal2A/f
Bal2B called, this.name=Bal2B
Bal2B/f called, this.name=Bal2B/f
Bal2B/f/g called, this.name=Bal2B/f/g
Bal2B/f/g
Bal2B called, this.name=Bal2B
Bal2B/f called, this.name=Bal2B/f
Bal2B/f/g called, this.name=Bal2B/f/g
Bal2B/f/g
Bal2B called, this.name=Bal2B
Bal2B/f called, this.name=Bal2B/f
Bal2B/f/g called, this.name=Bal2B/f/g
Bal2B/f/g
Bal2B called, this.name=Bal2B
Bal2B/f called, this.name=Bal2B/f
Bal2B/f/g called, this.name=Bal2B/f/g
Bal2B/f/g
Bal2C called, this.name=Bal2C
Bal2C/f called, this.name=Bal2C/f
Bal2C/f/g called, this.name=Bal2C/f/g
Bal2C/f/g/h called, this.Number.POSITIVE_INFINITY=Infinity
Bal2C/f/g/h return value
===*/

/*
 *  Parenthesis and 'new' "balance tests".
 *
 *  new new Foo                  new (new Foo)
 *  new new new Foo              new (new (new Foo))
 *  new new new Foo ()           new (new (new Foo ()))
 *  new new new Foo () ()        new (new (new Foo ()) ())
 *  new new new Foo () () ()     new (new (new Foo ()) ()) ()         up to this point, parens associated with 'new' calls
 *  new new new Foo () () () ()  (new (new (new Foo ()) ()) ()) ()    last is a func call
 */

function Bal2A() {
    print('Bal2A called, this.name=' + this.name);

    function f() {
        print("Bal2A/f called, this.name=" + this.name);
    }
    f.prototype = { 'name': 'Bal2A/f' };

    // return value (instance) of inner ("new Bal2A") is used as the
    // constructor for the outer "new" call.
    return f;
}
Bal2A.prototype = { 'name': 'Bal2A' };

function Bal2B() {
    print('Bal2B called, this.name=' + this.name);

    function f() {
        print('Bal2B/f called, this.name=' + this.name);

        function g() {
            print('Bal2B/f/g called, this.name=' + this.name);
        }
        g.prototype = { 'name': 'Bal2B/f/g' };

        return g;
    }
    f.prototype = { 'name': 'Bal2B/f' };

    return f;
}
Bal2B.prototype = { 'name': 'Bal2B' };

function Bal2C() {
    print('Bal2C called, this.name=' + this.name);

    function f() {
        print('Bal2C/f called, this.name=' + this.name);

        function g() {
            print('Bal2C/f/g called, this.name=' + this.name);

            function h() {
                print('Bal2C/f/g/h called, this.Number.POSITIVE_INFINITY=' + this.Number.POSITIVE_INFINITY);
                return 'Bal2C/f/g/h return value';
            }

            return h;
        }
        g.prototype = { 'name': 'Bal2C/f/g' };

        return g;
    }
    f.prototype = { 'name': 'Bal2C/f' };

    return f;
}
Bal2C.prototype = { 'name': 'Bal2C' };

try {
    eval("t = new new Bal2A; print(t.name);");
} catch (e) {
    print(e.name);
}

try {
    eval("t = new new new Bal2B; print(t.name);");
} catch (e) {
    print(e.name);
}

try {
    eval("t = new new new Bal2B (); print(t.name);");
} catch (e) {
    print(e.name);
}

try {
    eval("t = new new new Bal2B () (); print(t.name);");
} catch (e) {
    print(e.name);
}

try {
    eval("t = new new new Bal2B () () (); print(t.name);");
} catch (e) {
    print(e.name);
}

try {
    eval("t = new new new Bal2C () () () (); print(t);");
} catch (e) {
    print(e.name);
}

/*===
Bal3A called, this.name=Bal3A, x=1, y=2, z=3
Bal3A/f called, this.name=Bal3A/f, x=4, y=5
z is from outer scope: z=3
Bal3A/f/g called, this.Number.POSITIVE_INFINITY=Infinity, a=6, b=7
the following are from outer scope: x=4, y=5, z=3
Bal3A/f/g return value
===*/

/* Parenthesis association test with argument values:
 *
 * new new Foo (1,2,3) (4,5) (6,7)    last parens are a call
 */

function Bal3A(x,y,z) {
    print("Bal3A called, this.name=" + this.name +
          ", x=" + x + ", y=" + y + ", z=" + z);

    function f(x,y) {
        print("Bal3A/f called, this.name=" + this.name +
              ", x=" + x + ", y=" + y);
        print("z is from outer scope: z=" + z);

        function g(a,b) {
            print("Bal3A/f/g called, this.Number.POSITIVE_INFINITY=" + this.Number.POSITIVE_INFINITY +
                  ", a=" + a + ", b=" + b);
            print("the following are from outer scope: " +
                  "x=" + x + ", y=" + y + ", z=" + z);

            return "Bal3A/f/g return value";
        }

        // replace created object with function g
        return g;
    }
    f.prototype = { 'name': 'Bal3A/f' };

    return f;
}
Bal3A.prototype = { 'name': 'Bal3A' };

try {
    eval("t = new new Bal3A (1,2,3) (4,5) (6,7); print(t);");
} catch (e) {
    print(e.name);
}

/*===
Misc1 called, this.name=Misc1
foo getter called
Misc1/f called, this.name=Misc1/f
Misc1/f
Misc1 called, this.name=Misc1
foo getter called
Misc1/f called, this.name=Misc1/f
Misc1/f
Misc2 called, this.name=Misc2
foo getter called
Misc2/f called, this.name=Misc2/f, x=1, y=2
Misc2/f
===*/

/*
 *  Consider the following:
 *
 *    new new Foo () . foo
 *
 *  If we start from LeftHandSideExpression and choose CallExpression,
 *  it is not possible to create any 'new' expression without arguments,
 *  like the outer 'new' above.  More specifically:
 *
 *    LeftHandSideExpression => CallExpression
 *                           => CallExpression '.' IdentifierName
 *                           => MemberExpression '.' IdentifierName
 *
 *    At this point there is no way to expand MemberExpression and create
 *    two 'new' tokens and only one set of arguments.
 *
 *  If we start from LeftHandSideExpression and choose NewExpression
 *  (brackets for emphasis):
 *
 *    LeftHandSideExpression => NewExpression
 *                           => 'new' NewExpression
 *                           => 'new' MemberExpression
 *                           => 'new' [ MemberExpression '.' IdentifierName ]
 *                           => 'new' [ [ 'new' MemberExpression Arguments ] '.' IdentifierName ]
 *                           => 'new' [ [ 'new' 'Foo' '(' ')' ] '.' 'foo' ]
 *
 *  So, in other words, we get:
 *
 *    new new Foo () . foo == new ((new Foo ()) . foo)
 *
 *  And:
 *
 *    new new Foo () . foo () == new ((new Foo ()) . foo) ()
 *
 *  where the last parens are related to the outer 'new'.
 */

function Misc1() {
    print('Misc1 called, this.name=' + this.name);

    function f() {
        print('Misc1/f called, this.name=' + this.name);
    }
    f.prototype = { 'name': 'Misc1/f' };

    // return an object whose 'foo' property is read by the caller
    // and used as the next constructor
    return { get foo() { print ('foo getter called'); return f } };
}
Misc1.prototype = { 'name': 'Misc1' };

function Misc2() {
    print('Misc2 called, this.name=' + this.name);

    function f(x,y) {
        print('Misc2/f called, this.name=' + this.name +
              ', x=' + x + ', y=' + y);
    }
    f.prototype = { 'name': 'Misc2/f' };

    // return an object whose 'foo' property is read by the caller
    // and used as the next constructor
    return { get foo() { print ('foo getter called'); return f } };
}
Misc2.prototype = { 'name': 'Misc2' };

try {
    eval("t = new new Misc1 () . foo; print(t.name);");
} catch (e) {
    print(e.name);
}

try {
    // exactly same, explicit parens
    eval("t = new ((new Misc1 ()) . foo); print(t.name);");
} catch (e) {
    print(e.name);
}

try {
    // add arguments to outer 'new'
    eval("t = new new Misc2 () . foo (1,2); print(t.name);");
} catch (e) {
    print(e.name);
}

/*===
1 2 3 4
1 2 undefined undefined
true
===*/

/* If a constructor returns a replacement value, the internal prototype
 * of that object will not be set by the 'new' call.
 */
function Cons1() {
    this.foo = 1;
    this.bar = 2;
}
Cons1.prototype = { quux: 3, baz: 4 };

function Cons2() {
    return { foo: 1, bar: 2 };
}
Cons2.prototype = { quux: 3, baz: 4 };

try {
    t = new Cons1();
    print(t.foo, t.bar, t.quux, t.baz);

    // the internal prototype of 't' will be Object.prototype here
    t = new Cons2();
    print(t.foo, t.bar, t.quux, t.baz);
    print(Object.getPrototypeOf(t) === Object.prototype);
} catch (e) {
    print(e.name);
}

