load(libdir + "asm.js");
load(libdir + "asserts.js");

function matchStack(stackString, stackArray)
{
    var match = 0;
    for (name of stackArray) {
        match = stackString.indexOf(name, match);
        if (match === -1)
            throw name + " not found in the stack " + stack;
    }
}

var stack;
function dumpStack()
{
    stack = new Error().stack
}

setJitCompilerOption("ion.warmup.trigger", 10);
setJitCompilerOption("baseline.warmup.trigger", 0);
setJitCompilerOption("offthread-compilation.enable", 0);

var callFFI = asmCompile('global', 'ffis', USE_ASM + "var ffi=ffis.ffi; function f() { return ffi()|0 } return f");

var f = asmLink(callFFI, null, {ffi:dumpStack});
for (var i = 0; i < 15; i++) {
    stack = null;
    f();
    matchStack(stack, ['dumpStack', 'f']);
}

if (isAsmJSCompilationAvailable()) {
    var callFFI = asmCompile('global', 'ffis', USE_ASM + "var ffi=ffis.ffi; function f() { return ffi()|0 } return f");
    assertEq(isAsmJSModule(callFFI), true);
    stack = null;
    f();
    matchStack(stack, ['dumpStack', 'f']);
}

var f1 = asmLink(callFFI, null, {ffi:dumpStack});
var f2 = asmLink(callFFI, null, {ffi:function middle() { f1() }});
stack = null;
(function outer() { f2() })();
matchStack(stack, ["dumpStack", "f", "middle", "f"]);

function returnStackDumper() { return { valueOf:function() { stack = new Error().stack } } }
var f = asmLink(callFFI, null, {ffi:returnStackDumper});
for (var i = 0; i < 15; i++) {
    stack = null;
    f();
    matchStack(stack, ['valueOf', 'f']);
}

var caught = false;
try {
    stack = null;
    asmLink(asmCompile(USE_ASM + "function asmRec() { asmRec() } return asmRec"))();
} catch (e) {
    caught = true;
    matchStack(e.stack, ['asmRec', 'asmRec', 'asmRec', 'asmRec']);
}
assertEq(caught, true);

var caught = false;
try {
    callFFI(null, {ffi:Object.defineProperty})();
} catch (e) {
    caught = true;
}
assertEq(caught, true);

asmLink(callFFI, null, {ffi:eval})();
asmLink(callFFI, null, {ffi:Function})();
asmLink(callFFI, null, {ffi:Error})();

var manyCalls = asmCompile('global', 'ffis',
    USE_ASM +
    "var ffi=ffis.ffi;\
     function f1(a,b,c,d,e,f,g,h,i,j,k) { \
       a=a|0;b=b|0;c=c|0;d=d|0;e=e|0;f=f|0;g=g|0;h=h|0;i=i|0;j=j|0;k=k|0; \
       ffi(); \
       return (a+b+c+d+e+f+g+h+i+j+k)|0; \
     } \
     function f2() { \
       return f1(1,2,3,4,5,6,7,8,f1(1,2,3,4,5,6,7,8,9,10,11)|0,10,11)|0; \
     } \
     function f3() { return 13 } \
     function f4(i) { \
       i=i|0; \
       return TBL[i&3]()|0; \
     } \
     var TBL=[f3, f3, f2, f3]; \
     return f4;");
stack = null;
assertEq(asmLink(manyCalls, null, {ffi:dumpStack})(2), 123);
matchStack(stack, ['dumpStack', 'f1', 'f2', 'f4']);
