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);
}
}