//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------

function testMonoInlineSlots() {
    var iterations = 10;
    var a = new Array(iterations);
    for (var i = 0; i < iterations; i++) {
        // This object will have 4 inlined slots
        var o = { a: 0 };
        WScript.Echo("...");
        // This is the field add we want the fast path for.
        o.p = 1;
        WScript.Echo("...");
        // Use o to verify the type got written correctly to the object.
        o.z = -1;
        a[i] = o;
    }
    for (var i = 0; i < iterations; i++) {
        WScript.Echo("{ a: " + a[i].a + ", p: " + a[i].p + ", z: " + a[i].z + "}");
    }
}

WScript.Echo("testMonoInlineSlots: ");
testMonoInlineSlots();
testMonoInlineSlots();
WScript.Echo();


function testMonoInlineSlotsSetOrAdd() {
    var iterations = 10;
    var a = new Array(iterations);
    for (var i = 0; i < iterations; i++) {
        // This object will have 4 inlined slots
        var o = { a: 0 };
        // This adds a property ahead of time for every other object, so the profile indicates we either set or add a property.
        if (i % 2 == 0) {
            o.p = 1;
        }
        WScript.Echo("...");
        // This is the field add we want the fast path for.
        o.p = 1;
        WScript.Echo("...");
        // Use o to verify the type got written correctly to the object.
        o.z = -1;
        a[i] = o;
    }
    for (var i = 0; i < iterations; i++) {
        WScript.Echo("{ a: " + a[i].a + ", p: " + a[i].p + ", z: " + a[i].z + "}");
    }
}

WScript.Echo("testMonoInlineSlotsSetOrAdd: ");
testMonoInlineSlotsSetOrAdd();
testMonoInlineSlotsSetOrAdd();
WScript.Echo();


function testMonoAuxSlots() {
    var iterations = 10;
    var a = new Array(iterations);
    for (var i = 0; i < iterations; i++) {
        // This object will have 0 inlined slots.
        var o = {};
        // This will add aux slot array.
        o.a = 1;
        WScript.Echo("...");
        // This is the field add we want the fast path for.
        // Adding property now will stick it into an aux slot but won't require slot array re-allocation.
        o.p = 1;
        WScript.Echo("...");
        // Use o to verify the type got written correctly to the object.
        o.z = -1;
        a[i] = o;
    }
    for (var i = 0; i < iterations; i++) {
        WScript.Echo("{ a: " + a[i].a + ", p: " + a[i].p + ", z: " + a[i].z + "}");
    }
}

WScript.Echo("testMonoAuxSlots: ");
testMonoAuxSlots();
testMonoAuxSlots();
WScript.Echo();


function testMonoAuxSlotsAdjustmentRequired1() {
    var iterations = 10;
    var a = new Array(iterations);
    for (var i = 0; i < iterations; i++) {
        // This object will have 0 inlined slots.
        var o = {};
        WScript.Echo("...");
        // This is the field add we want the fast path for.
        // Adding property now will require slot array allocation and then will stick it into an aux slot.
        o.p = 1;
        WScript.Echo("...");
        // Use o to verify the type got written correctly to the object.
        o.z = -1;
        a[i] = o;
    }
    for (var i = 0; i < iterations; i++) {
        WScript.Echo("{ p: " + a[i].p + ", z: " + a[i].z + "}");
    }
}

WScript.Echo("testMonoAuxSlotsAdjustmentRequired1: ");
testMonoAuxSlotsAdjustmentRequired1();
testMonoAuxSlotsAdjustmentRequired1();
WScript.Echo();


function testMonoAuxSlotsAdjustmentRequired2() {
    var iterations = 10;
    var a = new Array(iterations);
    for (var i = 0; i < iterations; i++) {
        // This object will have 0 inlined slots.
        var o = {};
        // This will add aux slot array of size 4, but it will fill every slot.
        o.a = 0;
        o.b = 1;
        o.c = 2;
        o.d = 3;
        WScript.Echo("...");
        // This is the field add we want the fast path for.
        // Adding property now will require slot array allocation and then will stick it into an aux slot.
        o.p = 1;
        WScript.Echo("...");
        // Use o to verify the type got written correctly to the object.
        o.z = -1;
        a[i] = o;
    }
    for (var i = 0; i < iterations; i++) {
        WScript.Echo("{ a: " + a[i].a + ", b: " + a[i].b + ", c: " + a[i].c + ", d: " + a[i].d + ", p: " + a[i].p + ", z: " + a[i].z + "}");
    }
}

WScript.Echo("testMonoAuxSlotsAdjustmentRequired2: ");
testMonoAuxSlotsAdjustmentRequired2();
testMonoAuxSlotsAdjustmentRequired2();
WScript.Echo();


function testPoly() {
    var iterations = 25;
    var a = new Array(iterations);
    for (var i = 0; i < iterations; i++) {
        var o;
        switch (i % 3) {
            case 0:
                o = {};
                break;
            case 1:
                o = { a: 0 };
                break;
            case 2:
                o = { b: 0 };
                break;
        }
        if (i % 6 >= 3) {
            o.p = 0;
        }
        WScript.Echo("...");
        // This is the field add we want the fast path for.
        o.p = 1;
        WScript.Echo("...");
        o.z = -1;
        a[i] = o;
    }
    for (var i = 0; i < iterations; i++) {
        WScript.Echo("{ a: " + a[i].a + ", b: " + a[i].b + ", p: " + a[i].p + ", z: " + a[i].z + "}");
    }
}

WScript.Echo("testPoly: ");
testPoly();
testPoly();
WScript.Echo();

(function () {
    var Blank = function () {
    }

    Blank.prototype = { a: 0, b: 0, c: 0 }

    function setUpMonoStoreFieldCacheInvalidation() {
        // Make sure properties a, b, and c are not marked as fixed on Blank's type handler, so that
        // add property inline caches can be populated.
        var o = new Blank();
        o.a = 1;
        o.b = 2;
        o.c = 3;

        var o = new Blank();
        o.a = 1;
        o.b = 2;
        o.c = 3;
    }

    function testMonoStoreFieldCacheInvalidation(o, isNative) {
        // Do this only for the JIT-ed iteration to be sure caches aren't populated and we don't do
        // object type spec.
        if (isNative) {
            o.a = 1;
            o.b = 2;
            o.c = 3;
        }
    }

    WScript.Echo("testMonoStoreFieldCacheInvalidation: ");

    setUpMonoStoreFieldCacheInvalidation();

    var o = new Blank();
    testMonoStoreFieldCacheInvalidation(o, false);
    WScript.Echo("{ a: " + o.a + ", b: " + o.b + ", c: " + o.c + "}");

    var o = new Blank();
    testMonoStoreFieldCacheInvalidation(o, true);
    WScript.Echo("{ a: " + o.a + ", b: " + o.b + ", c: " + o.c + "}");

    // Change the writable attribute without actually adding or deleting any properties.
    // This should invalidate all inline caches for property b.
    Object.defineProperty(Blank.prototype, "b", { writable: false });

    var o = new Blank();
    testMonoStoreFieldCacheInvalidation(o, true);
    WScript.Echo("{ a: " + o.a + ", b: " + o.b + ", c: " + o.c + "}");
})();

WScript.Echo();

(function () {
    var Blank = function () {
    }

    Blank.prototype = { a: 0, b: 0, c: 0 }

    function setUpPolyStoreFieldCacheInvalidation() {
        // Make sure properties a, b, and c are not marked as fixed on Blank's type handler, so that
        // add property inline caches can be populated.
        var o = new Blank();
        o.a = 1;
        o.b = 2;
        o.c = 3;
        o.d = 4;

        var o = new Blank();
        o.a = 1;
        o.b = 2;
        o.c = 3;
        o.d = 4;
    }

    function testPolyStoreFieldCacheInvalidation(objects, isNative) {
        // This loop ensures that inline caches for a and b become polymorphic on the second iteration.
        // That's because the type encountered the second time around is the final type with all properties present.
        var o;
        for (var i = 0; i < 2; i++) {
            o = objects[i];
            o.a = 1;
            o.b = 2;
            o.c = 3;
        }
    }

    function reportResults(objects) {
        for (var i = 0; i < 2; i++) {
            var o = objects[i];
            WScript.Echo("{ a: " + o.a + ", b: " + o.b + ", c: " + o.c + "}");
        }
    }

    WScript.Echo("testPolyStoreFieldCacheInvalidation: ");

    setUpPolyStoreFieldCacheInvalidation();

    var warmUpObjects = new Array(2);
    warmUpObjects[0] = new Blank();
    var o = new Blank();
    o.a = 1;
    o.b = 2;
    o.c = 3;
    warmUpObjects[1] = o;
    testPolyStoreFieldCacheInvalidation(warmUpObjects, false);
    reportResults(warmUpObjects);

    // Change the writable attribute without actually adding or deleting any properties.
    // This should invalidate all inline caches for property b, including those that are part of a
    // polymorphic inline cache - provided they got registered for invalidation properly.
    Object.defineProperty(Blank.prototype, "b", { writable: false });

    var testObjects = new Array(2);
    testObjects[0] = new Blank();
    testObjects[1] = new Blank();
    testPolyStoreFieldCacheInvalidation(testObjects, true);
    reportResults(testObjects);

})();