void validateFunction()

in libs/core---vm/vm.cpp [653:892]


void validateFunction(VMImage *img, VMImageSection *sect, int debug) {
    uint16_t stackDepth[sect->size / 2];
    memset(stackDepth, 0, sizeof(stackDepth));
    int baseStack = 1; // 1 is the return address; also zero in the array above means unknown yet
    int currStack = baseStack;
    unsigned pc = 0;
    auto code = (uint16_t *)((uint8_t *)sect + VM_FUNCTION_CODE_OFFSET);
    auto lastPC = (sect->size - VM_FUNCTION_CODE_OFFSET) >> 1;
    auto atEnd = false;

    RefAction *ra = (RefAction *)vmLiteralVal(sect);

    if (ra->vtable != &pxt::RefAction_vtable)
        FNERR(1251);
    if ((uint8_t *)img->dataStart + ra->func != (uint8_t *)code)
        FNERR(1252);

    unsigned numArgs = ra->numArgs;
    unsigned numCaps = ra->initialLen;

    if (numCaps > 200)
        FNERR(1239);

    while (pc < lastPC) {
        if (currStack > VM_MAX_FUNCTION_STACK)
            FNERR(1204);

        FORCE_STACK(currStack, 1201, pc);

        uint16_t opcode = code[pc++];
        if (opcode == 0 && atEnd)
            continue; // allow padding at the end

        atEnd = false;
        OpFun fn;
        unsigned arg;
        unsigned opIdx;
        bool isRtCall = false;
        bool hasPush = false;

        if (opcode >> 15 == 0) {
            opIdx = opcode & VM_OPCODE_BASE_MASK;
            arg = opcode >> VM_OPCODE_ARG_POS;
            hasPush = !!(opcode & VM_OPCODE_PUSH_MASK);
        } else if (opcode >> 14 == 0b10) {
            opIdx = opcode & 0x1fff;
            arg = 0;
            isRtCall = true;
            hasPush = !!(opcode & VM_RTCALL_PUSH_MASK);
        } else {
            unsigned tmp = ((int32_t)opcode << (16 + 2)) >> (2 + VM_OPCODE_ARG_POS);
            FORCE_STACK(0xffff, 1200, pc); // cannot jump here!
            opcode = code[pc++];
            opIdx = opcode & VM_OPCODE_BASE_MASK;
            arg = (opcode >> VM_OPCODE_ARG_POS) + tmp;
            hasPush = !!(opcode & VM_OPCODE_PUSH_MASK);
        }

        if (opIdx >= img->numOpcodes)
            FNERR(1227);
        auto opd = img->opcodeDescs[opIdx];

        if (debug)
            DMESG("%4d/%d -> %04x idx=%d arg=%d st=%d %s", pc, lastPC, opcode, opIdx, arg,
                  currStack, opd ? opd->name : "NA");

        if (!opd)
            FNERR(1228);

        fn = img->opcodes[opIdx];

        if (isRtCall) {
            if (opd->numArgs > 1) {
                currStack -= opd->numArgs - 1;
                if (currStack < baseStack)
                    FNERR(1229);
            }
        } else if (fn == op_pushmany) {
            if (currStack == 1 && baseStack == 1)
                baseStack = currStack = arg + 1;
            else
                currStack += arg;
        } else if (fn == op_popmany) {
            currStack -= arg;
            if (currStack < baseStack)
                FNERR(1205);
        } else if (fn == op_push) {
            currStack++;
        } else if (fn == op_pop) {
            if (arg)
                FNERR(1243);
            currStack--;
            if (currStack < baseStack)
                FNERR(1206);
        } else if (fn == op_mapget) {
            if (arg)
                FNERR(1244);
            currStack--;
            if (currStack < baseStack)
                FNERR(1245);
        } else if (fn == op_mapset) {
            if (arg)
                FNERR(1246);
            currStack -= 2;
            if (currStack < baseStack)
                FNERR(1247);
        } else if (fn == op_ret) {
            SPLIT_ARG(retNumArgs, numTmps);
            if (currStack != baseStack)
                FNERR(1207);
            if (numTmps + 1 != (unsigned)baseStack)
                FNERR(1208);
            if (retNumArgs != numArgs)
                FNERR(1209);
            currStack = baseStack;
            atEnd = true;
        } else if (fn == op_ldloc || fn == op_stloc) {
            if (arg == (unsigned)currStack - 1 || arg == (unsigned)currStack)
                FNERR(1210); // trying to load return address/function
            if (arg > (unsigned)currStack + numArgs)
                FNERR(1211);
        } else if (fn == op_ldcap) {
            if (arg >= numCaps)
                FNERR(1212);
        } else if (fn == op_ldglb || fn == op_stglb) {
            if (arg >= img->infoHeader->allocGlobals)
                FNERR(1213);
            // not supported (yet?)
            if (arg < img->infoHeader->nonPointerGlobals)
                FNERR(1214);
        } else if (fn == op_ldfld || fn == op_stfld) {
            SPLIT_ARG2(fldId, classId);

            if (classId >= img->numSections)
                FNERR(1236);
            auto fsec = img->sections[classId];
            if (fsec->type != SectionType::VTable)
                FNERR(1234);

            auto vt = getStaticVTable(img, classId);
            if (fldId * 8 + 8 >= vt->numbytes)
                FNERR(1235);

            if (fn == op_stfld) {
                currStack--;
                if (currStack < baseStack)
                    FNERR(1232);
            }
        } else if (fn == op_ldlit) {
            if (arg >= img->numSections)
                FNERR(1215);
            auto fsec = img->sections[arg];
            if (fsec->type != SectionType::Literal && fsec->type != SectionType::Function)
                FNERR(1237);
        } else if (fn == op_newobj || fn == op_checkinst) {
            if (arg >= img->numSections)
                FNERR(1219);
            auto fsec = img->sections[arg];
            if (fsec->type != SectionType::VTable)
                FNERR(1238);
        } else if (fn == op_ldnumber) {
            if (arg >= img->numNumberLiterals)
                FNERR(1217);
        } else if (fn == op_callproc) {
            if (arg >= img->numSections)
                FNERR(1218);
            auto fsec = img->sections[arg];
            if (fsec->type != SectionType::Function)
                FNERR(1220);
            auto ra = (RefAction *)img->pointerLiterals[arg];
            unsigned calledArgs = ra->numArgs;
            currStack -= calledArgs;
            if (currStack < baseStack)
                FNERR(1221);
        } else if (fn == op_callind) {
            currStack -= arg;
            if (currStack < baseStack)
                FNERR(1223);
        } else if (fn == op_calliface) {
            SPLIT_ARG(numArgs, ifaceIdx);
            if (ifaceIdx == 0 || ifaceIdx >= img->numIfaceMemberNames)
                FNERR(1240);
            currStack -= numArgs;
            if (currStack < baseStack)
                FNERR(1230);
        } else if (fn == op_callget) {
            if (arg == 0 || arg >= img->numIfaceMemberNames)
                FNERR(1241);
            currStack -= 1;
            if (currStack < baseStack)
                FNERR(1230);
        } else if (fn == op_callset) {
            if (arg == 0 || arg >= img->numIfaceMemberNames)
                FNERR(1242);
            currStack -= 2;
            if (currStack < baseStack)
                FNERR(1230);
        } else if (fn == op_ldspecial) {
            auto a = (TValue)(uintptr_t)arg;
            if (a != TAG_TRUE && a != TAG_FALSE && a != TAG_UNDEFINED && a != TAG_NULL &&
                a != TAG_NAN)
                FNERR(1224);
        } else if (fn == op_ldint || fn == op_ldintneg) {
            // nothing to check!
        } else if (fn == op_bitconv) {
            if (arg & ~0x1f)
                FNERR(1253);
            auto sz = arg & 0xf;
            if (sz != 1 && sz != 2 && sz != 4)
                FNERR(1254);
        } else if (fn == op_jmp || fn == op_jmpnz || fn == op_jmpz) {
            unsigned newPC = pc + arg; // will overflow for backjump, but this is fine
            if (newPC >= lastPC)
                FNERR(1202);
            FORCE_STACK(currStack, 1226, newPC);
            if (fn == op_jmp) {
                if (currStack != baseStack)
                    FNERR(1203);
                atEnd = true;
            }
        } else if (fn == op_try) {
            unsigned newPC = pc + arg; // will overflow for backjump, but this is fine
            if (newPC >= lastPC)
                FNERR(1248);
            if (currStack != baseStack)
                FNERR(1249);
            FORCE_STACK(currStack, 1250, newPC);
        } else {
            FNERR(1225);
        }

        if (hasPush)
            currStack++;
    }

    if (!atEnd) {
        pc--;
        FNERR(1210);
    }
}