/* jit/typecheck.c - typechecking (part of bytecode verification) Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003 R. Grafl, A. Krall, C. Kruegel, C. Oates, R. Obermaisser, M. Probst, S. Ring, E. Steiner, C. Thalinger, D. Thuernbeck, P. Tomsich, J. Wenninger This file is part of CACAO. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Contact: cacao@complang.tuwien.ac.at Authors: Edwin Steiner $Id: typecheck.c 697 2003-12-07 12:45:27Z edwin $ */ #include "global.h" /* must be here because of CACAO_TYPECHECK */ #ifdef CACAO_TYPECHECK #include "jit.h" #include "builtin.h" #include "tables.h" #include "loader.h" #include "types.h" #include "toolbox/loging.h" #include "toolbox/memory.h" #define TOUCHED_YES 0x01 #define TOUCHED_NO 0x02 #define TOUCHED_MAYBE (TOUCHED_YES | TOUCHED_NO) #define REACH_STD 0 #define REACH_JSR 1 #define REACH_RET 2 /****************************************************************************/ /* DEBUG HELPERS */ /****************************************************************************/ #ifdef TYPECHECK_VERBOSE_OPT bool typecheckverbose = false; #define DOLOG(action) do { if (typecheckverbose) {action;} } while(0) #else #define DOLOG(action) #endif #ifdef TYPECHECK_VERBOSE #define TYPECHECK_VERBOSE_IMPORTANT #define LOG(str) DOLOG(log_text(str)) #define LOG1(str,a) DOLOG(dolog(str,a)) #define LOG2(str,a,b) DOLOG(dolog(str,a,b)) #define LOG3(str,a,b,c) DOLOG(dolog(str,a,b,c)) #define LOGIF(cond,str) DOLOG(do {if (cond) log_text(str);} while(0)) #define LOGINFO(info) DOLOG(do {typeinfo_print_short(stdout,info);printf("\n");} while(0)) #define LOGFLUSH DOLOG(fflush(stdout)) #define LOGNL DOLOG(printf("\n")) #define LOGSTR(str) DOLOG(printf(str)) #define LOGSTR1(str,a) DOLOG(printf(str,a)) #define LOGSTR2(str,a,b) DOLOG(printf(str,a,b)) #define LOGSTR3(str,a,b,c) DOLOG(printf(str,a,b,c)) #define LOGSTRu(utf) DOLOG(utf_display(utf)) #else #define LOG(str) #define LOG1(str,a) #define LOG2(str,a,b) #define LOG3(str,a,b,c) #define LOGIF(cond,str) #define LOGINFO(info) #define LOGFLUSH #define LOGNL #define LOGSTR(str) #define LOGSTR1(str,a) #define LOGSTR2(str,a,b) #define LOGSTR3(str,a,b,c) #endif #ifdef TYPECHECK_VERBOSE_IMPORTANT #define LOGimp(str) DOLOG(log_text(str)) #define LOGimpSTR(str) DOLOG(printf(str)) #define LOGimpSTRu(utf) DOLOG(utf_display(utf)) #else #define LOGimp(str) #define LOGimpSTR(str) #define LOGimpSTRu(utf) #endif #if defined(TYPECHECK_VERBOSE) || defined(TYPECHECK_VERBOSE_IMPORTANT) static void typeinfo_print_locals(FILE *file,u1 *vtype,typeinfo *vinfo,u1 *touched,int num) { int i; for (i=0; itype == TYPE_ADR) \ TYPEINFO_COPY((source)->typeinfo,(dest)->typeinfo);} #define ISBUILTIN(v) (iptr->val.a == (functionptr)(v)) /* TYPECHECK_COPYVARS: copy the types and typeinfos of the current local * variables to the local variables of the target block. * Input: * vtype......current local variable types * vinfo......current local variable typeinfos * ttype......local variable types of target block * tinfo......local variable typeinfos of target block * maxlocals..number of local variables * Used: * macro_i */ #define TYPECHECK_COPYVARS \ LOG("TYPECHECK_COPYVARS"); \ for (macro_i=0; macro_itarget != jsrtemp2->target) \ panic("Merging different JSR subroutines"); \ jsrtemp = jsrtemp->next; \ jsrtemp2 = jsrtemp2->next; \ } \ jsrtemp = jsrbuffer[tbptr-block]; \ if (jsrtemp) \ for (macro_i=0; macro_itouched[i] |= touched[i]; \ } } while (0) /* TYPECHECK_COPYSTACK: copy the typeinfos of the current stack to * the input stack of the target block. * Input: * srcstack...current stack * dststack...input stack of target block */ #define TYPECHECK_COPYSTACK \ LOG("TYPECHECK_COPYSTACK"); \ while (srcstack) { \ LOG1("copy %d",srcstack->type); \ if (!dststack) panic("Stack depth mismatch"); \ if (srcstack->type != dststack->type) \ panic("Type mismatch on stack"); \ if (srcstack->type == TYPE_ADR) { \ TYPEINFO_CLONE(srcstack->typeinfo,dststack->typeinfo); \ } \ dststack = dststack->prev; \ srcstack = srcstack->prev; \ } \ if (dststack) panic("Stack depth mismatch"); /* TYPECHECK_MERGESTACK: merge the input stack of the target block * with the current stack. * Input: * srcstack...current stack * dststack...input stack of target block * Ouput: * changed....set to true if any typeinfo has changed */ #define TYPECHECK_MERGESTACK \ LOG("TYPECHECK_MERGESTACK"); \ while (srcstack) { \ if (!dststack) panic("Stack depth mismatch"); \ if (srcstack->type != dststack->type) \ panic("Type mismatch on stack"); \ if (srcstack->type == TYPE_ADR) { \ LOGINFO(&dststack->typeinfo); \ LOGINFO(&srcstack->typeinfo); LOGFLUSH; \ changed |= typeinfo_merge(&dststack->typeinfo, \ &srcstack->typeinfo); \ LOGINFO(&dststack->typeinfo); \ LOG((changed)?"CHANGED!\n":"not changed.\n"); \ } \ dststack = dststack->prev; \ srcstack = srcstack->prev; \ } \ if (dststack) panic("Stack depth mismatch"); /* TYPECHECK_CHECK_JSR_CHAIN: checks if the target block is reached by * the same JSR targets on all control paths. * * Input: * tbptr......target block * jsrchain...current JSR target chain * jsrbuffer..JSR target chain for each basic block * Output: * panic if the JSR target chains don't match * Used: * jsrtemp, jsrtemp2 */ #define TYPECHECK_CHECK_JSR_CHAIN \ do { \ jsrtemp = jsrbuffer[tbptr-block]; \ if (!jsrtemp) panic("non-subroutine called by JSR"); \ if (jsrtemp->target != tbptr) \ panic("Merging different JSR subroutines"); \ jsrtemp = jsrtemp->next; \ jsrtemp2 = jsrchain; \ while (jsrtemp || jsrtemp2) { \ if (!jsrtemp || !jsrtemp2) \ panic("Merging JSR subroutines of different depth"); \ if (jsrtemp->target != jsrtemp2->target) \ panic("Merging different JSR subroutines"); \ jsrtemp = jsrtemp->next; \ jsrtemp2 = jsrtemp2->next; \ } } while (0) /* TYPECHECK_ADD_JSR: add a JSR target to the current JSR target chain * and store the resulting chain in the target block. * * Input: * jsrchain...current JSR target chain * tbptr.....the basic block targeted by the JSR * maxlocals..number of local variables * jsrbuffer..JSR target chain for each basic block * Used: * jsrtemp */ #define TYPECHECK_ADD_JSR \ do { \ LOG1("adding JSR to block %04d",(tbptr)-block); \ jsrtemp = (jsr_record *) dump_alloc(sizeof(jsr_record)+(maxlocals-1)*sizeof(u1)); \ jsrtemp->target = (tbptr); \ jsrtemp->next = jsrchain; \ jsrtemp->sbr_touched = NULL; \ memset(&jsrtemp->touched,TOUCHED_NO,sizeof(u1)*maxlocals); \ jsrbuffer[tbptr-block] = jsrtemp; \ } while (0) /* TYPECHECK_COPYJSR: copy the current JSR chain to the target block. * * Input: * chain......current JSR target chain * tbptr.....the basic block targeted by the JSR * maxlocals..number of local variables * jsrbuffer..JSR target chain for each basic block * touched....current touched flags of local variables * Used: * jsrtemp */ #define TYPECHECK_COPYJSR(chain) \ do { \ LOG("TYPECHECK_COPYJSR"); \ if (chain) { \ jsrtemp = (jsr_record *) dump_alloc(sizeof(jsr_record)+(maxlocals-1)*sizeof(u1)); \ jsrtemp->target = (chain)->target; \ jsrtemp->next = (chain)->next; \ jsrtemp->sbr_touched = NULL; \ memcpy(&jsrtemp->touched,touched,sizeof(u1)*maxlocals); \ jsrbuffer[tbptr-block] = jsrtemp; \ } \ else \ jsrbuffer[tbptr-block] = NULL; \ } while (0) /* TYPECHECK_REACH: executed, when the target block (tbptr) can be reached * from the current block (bptr). The types of local variables and * stack slots are propagated to the target block. * Input: * bptr.......current block * tbptr......target block * dst........current output stack pointer * vtype......current local variable types * vinfo......current local variable typeinfos * jsrchain...current JSR target chain * jsrbuffer..JSR target chain for each basic block * way........in which way the block is reached (REACH_ constant) * touched....current touched flags of local variables * Output: * repeat.....changed to true if a block before the current * block has changed * Used: * ttype, tinfo, srcstack, dststack, changed, macro_i */ #define TYPECHECK_REACH(way) \ LOG1("reaching block %04d",tbptr-block); \ srcstack = dst; \ dststack = tbptr->instack; \ ttype = vartype + maxlocals*(tbptr-block); \ tinfo = vartypeinfo + maxlocals*(tbptr-block); \ if (tbptr->flags == BBTYPECHECK_UNDEF) { \ /* This block is reached for the first time */ \ if (way == REACH_JSR) { \ TYPECHECK_ADD_JSR; \ TYPECHECK_COPYVARS; \ } \ else { \ TYPECHECK_COPYJSR(jsrchain); \ TYPECHECK_COPYVARS; \ } \ TYPECHECK_COPYSTACK; \ changed = true; \ } else { \ /* This block has been reached before */ \ changed = false; \ if (way == REACH_JSR) \ TYPECHECK_CHECK_JSR_CHAIN; \ else \ TYPECHECK_MERGEJSR; \ LOGIF(changed,"changed jsr"); \ TYPECHECK_MERGEVARS; \ LOGIF(changed,"changed vars"); \ TYPECHECK_MERGESTACK; \ LOGIF(changed,"changed stack"); \ } \ if (changed) { \ LOG("REACHED!"); \ tbptr->flags = BBTYPECHECK_REACHED; \ if (tbptr <= bptr) {repeat = true; LOG("MUST REPEAT!");} \ } LOG("done."); /****************************************************************************/ /* typecheck() */ /****************************************************************************/ #define MAXPARAMS 255 /* typecheck is called directly after analyse_stack */ void typecheck() { int b_count, b_index; stackptr curstack; /* input stack top for current instruction */ stackptr srcstack; /* source stack for copying and merging */ stackptr dststack; /* target stack for copying and merging */ int opcode; /* current opcode */ int macro_i, len, i; /* temporary counters */ bool superblockend; /* true if no fallthrough to next block */ bool repeat; /* if true, blocks are iterated over again */ bool changed; /* used in macros */ instruction *iptr; /* pointer to current instruction */ basicblock *bptr; /* pointer to current basic block */ basicblock *tbptr; /* temporary for target block */ u1 *vartype; /* type of each local for each basic block */ typeinfo *vartypeinfo; /* type of each local for each basic block */ u1 *vtype; /* type of each local for current instruction */ typeinfo *vinfo; /* type of each local for current instruction */ u1 *ttype; /* temporary pointer */ typeinfo *tinfo; /* temporary pointer */ typeinfo tempinfo; /* temporary */ int returntype; /* return type of current method */ typeinfo returntypeinfo; /* typeinfo for return type */ u1 *ptype; /* parameter types of called method */ typeinfo *pinfo; /* parameter typeinfos of called method */ int rtype; /* return type of called method */ typeinfo rinfo; /* typeinfo for return type of called method */ stackptr dst; /* output stack of current instruction */ basicblock **tptr; /* pointer into target list of switch instructions */ jsr_record **jsrbuffer; /* JSR target chain for each basic block */ jsr_record *jsrchain; /* JSR chain for current block */ jsr_record *jsrtemp,*jsrtemp2; /* temporary variables */ jsr_record *subroutine; /* jsr_record of the current subroutine */ u1 *touched; /* touched flags for local variables */ xtable **handlers; /* active exception handlers */ classinfo *cls; /* temporary */ bool maythrow; /* true if this instruction may throw */ LOGSTR("\n==============================================================================\n"); DOLOG(show_icmd_method()); LOGSTR("\n==============================================================================\n"); LOGimpSTR("Entering typecheck: "); LOGimpSTRu(method->name); LOGimpSTR(" "); LOGimpSTRu(method->descriptor); LOGimpSTR(" (class "); LOGimpSTRu(method->class->name); LOGimpSTR(")\n"); /* XXX allocate buffers for method arguments */ ptype = DMNEW(u1,MAXPARAMS); pinfo = DMNEW(typeinfo,MAXPARAMS); LOG("Buffer allocated.\n"); /* reset all BBFINISHED blocks to BBTYPECHECK_UNDEF. */ b_count = block_count; bptr = block; while (--b_count >= 0) { #ifdef TYPECHECK_DEBUG if (bptr->flags != BBFINISHED && bptr->flags != BBDELETED && bptr->flags != BBUNDEF) { show_icmd_method(); LOGSTR1("block flags: %d\n",bptr->flags); LOGFLUSH; panic("Internal error: Unexpected block flags in typecheck()"); } #endif if (bptr->flags >= BBFINISHED) { bptr->flags = BBTYPECHECK_UNDEF; } bptr++; } /* The first block is always reached */ if (block_count && block[0].flags == BBTYPECHECK_UNDEF) block[0].flags = BBTYPECHECK_REACHED; LOG("Blocks reset.\n"); /* allocate the buffers for local variables */ vartype = DMNEW(u1,maxlocals * (block_count+1)); vartypeinfo = DMNEW(typeinfo,maxlocals * (block_count+1)); touched = DMNEW(u1,maxlocals); vtype = vartype + maxlocals * block_count; vinfo = vartypeinfo + maxlocals * block_count; memset(vartype,TYPE_VOID,maxlocals * (block_count+1) * sizeof(typeinfo)); memset(vartypeinfo,0,maxlocals * (block_count+1) * sizeof(typeinfo)); LOG("Variable buffer initialized.\n"); /* allocate the buffer for storing JSR target chains */ jsrbuffer = DMNEW(jsr_record*,block_count); memset(jsrbuffer,0,block_count * sizeof(jsr_record*)); jsrchain = NULL; LOG("jsrbuffer initialized.\n"); /* allocate the buffer of active exception handlers */ handlers = DMNEW(xtable*,method->exceptiontablelength + 1); /* initialize the variable types of the first block */ /* to the types of the arguments */ ttype = vartype; tinfo = vartypeinfo; /* if this is an instance method initialize the "this" ref type */ if (!(method->flags & ACC_STATIC)) { *ttype++ = TYPE_ADDRESS; TYPEINFO_INIT_CLASSINFO(*tinfo,class); tinfo++; } LOG("'this' argument set.\n"); /* the rest of the arguments and the return type */ typeinfo_init_from_method_args(method->descriptor,ttype,tinfo, maxlocals - (tinfo-vartypeinfo), true, /* two word types use two slots */ &returntype,&returntypeinfo); LOG("Arguments set.\n"); /* initialize the input stack of exception handlers */ for (i=0; iexceptiontablelength; ++i) { cls = extable[i].catchtype; if (!cls) cls = class_java_lang_Throwable; LOGSTR1("handler %i: ",i); LOGSTRu(cls->name); LOGNL; TYPEINFO_INIT_CLASSINFO(extable[i].handler->instack->typeinfo,cls); } LOG("Exception handler stacks set.\n"); /* loop while there are still blocks to be checked */ do { repeat = false; b_count = block_count; bptr = block; while (--b_count >= 0) { LOGSTR1("---- BLOCK %04d, ",bptr-block); LOGSTR1("blockflags: %d\n",bptr->flags); LOGFLUSH; if (bptr->flags == BBTYPECHECK_REACHED) { LOGSTR1("\n---- BLOCK %04d ------------------------------------------------\n",bptr-block); LOGFLUSH; superblockend = false; bptr->flags = BBFINISHED; b_index = bptr - block; /* init stack at the start of this block */ curstack = bptr->instack; /* init variable types at the start of this block */ for (i=0; itarget->debug_nr); jsrtemp = jsrtemp->next; } LOGNL; LOGFLUSH; } #endif subroutine = jsrbuffer[jsrchain->target - block]; memcpy(touched,jsrchain->touched,sizeof(u1)*maxlocals); } else subroutine = NULL; #ifdef TYPECHECK_VERBOSE if (typecheckverbose) { if (subroutine) {LOGSTR1("subroutine L%03d\n",subroutine->target->debug_nr);LOGFLUSH;} typeinfo_print_block(stdout,curstack,vtype,vinfo,(jsrchain) ? touched : NULL); LOGNL; LOGFLUSH; } #endif /* determine the active exception handlers for this block */ /* XXX could use a faster algorithm with sorted lists or * something? */ len = 0; for (i=0; iexceptiontablelength; ++i) { if ((extable[i].start <= bptr) && (extable[i].end > bptr)) { LOG1("active handler L%03d",extable[i].handler->debug_nr); handlers[len++] = extable + i; } } handlers[len] = NULL; /* loop over the instructions */ len = bptr->icount; iptr = bptr->iinstr; while (--len >= 0) { DOLOG(show_icmd(iptr,false)); LOGNL; LOGFLUSH; opcode = iptr->opc; dst = iptr->dst; maythrow = false; switch (opcode) { /****************************************/ /* STACK MANIPULATIONS */ /* We just need to copy the typeinfo */ /* for slots containing addresses. */ case ICMD_DUP: COPYTYPE(curstack,dst); break; case ICMD_DUP_X1: COPYTYPE(curstack,dst); COPYTYPE(curstack,dst-2); COPYTYPE(curstack->prev,dst-1); break; case ICMD_DUP_X2: COPYTYPE(curstack,dst); COPYTYPE(curstack,dst-3); COPYTYPE(curstack->prev,dst-1); COPYTYPE(curstack->prev->prev,dst-2); break; case ICMD_DUP2: COPYTYPE(curstack,dst); COPYTYPE(curstack->prev,dst-1); break; case ICMD_DUP2_X1: COPYTYPE(curstack,dst); COPYTYPE(curstack->prev,dst-1); COPYTYPE(curstack,dst-3); COPYTYPE(curstack->prev,dst-4); COPYTYPE(curstack->prev->prev,dst-2); break; case ICMD_DUP2_X2: COPYTYPE(curstack,dst); COPYTYPE(curstack->prev,dst-1); COPYTYPE(curstack,dst-4); COPYTYPE(curstack->prev,dst-5); COPYTYPE(curstack->prev->prev,dst-2); COPYTYPE(curstack->prev->prev->prev,dst-3); break; case ICMD_SWAP: COPYTYPE(curstack,dst-1); COPYTYPE(curstack->prev,dst); break; /* XXX only add these cases in debug mode? */ case ICMD_POP: break; case ICMD_POP2: break; /****************************************/ /* LOADING ADDRESS FROM VARIABLE */ case ICMD_ALOAD: CHECKVARTYPE(iptr->op1,TYPE_ADR); /* loading a returnAddress is not allowed */ if (TYPEINFO_IS_PRIMITIVE(vinfo[iptr->op1])) panic("illegal instruction: ALOAD loading returnAddress"); TYPEINFO_COPY(vinfo[iptr->op1],dst->typeinfo); break; /****************************************/ /* STORING ADDRESS TO VARIABLE */ case ICMD_ASTORE: /* TYPE_ADR has already been checked. */ STORE_TYPE(iptr->op1,TYPE_ADDRESS); TYPEINFO_COPY(curstack->typeinfo,vinfo[iptr->op1]); break; /****************************************/ /* LOADING ADDRESS FROM ARRAY */ case ICMD_AALOAD: if (!TYPEINFO_MAYBE_ARRAY_OF_REFS(curstack->prev->typeinfo)) panic("illegal instruction: AALOAD on non-reference array"); typeinfo_init_component(&curstack->prev->typeinfo,&dst->typeinfo); maythrow = true; break; /****************************************/ /* STORING ADDRESS TO ARRAY */ case ICMD_AASTORE: /* XXX also handled by builtin3 */ /* XXX move this to unexpected instructions? */ if (!TYPEINFO_MAYBE_ARRAY_OF_REFS(curstack->prev->prev->typeinfo)) panic("illegal instruction: AASTORE to non-reference array"); /* XXX optimize */ /* typeinfo_init_component(&curstack->prev->prev->typeinfo,&tempinfo); if (!typeinfo_is_assignable(&curstack->typeinfo,&tempinfo)) panic("illegal instruction: AASTORE to incompatible type"); */ maythrow = true; break; /****************************************/ /* FIELD ACCESS */ case ICMD_PUTFIELD: if (!TYPEINFO_IS_REFERENCE(curstack->prev->typeinfo)) panic("illegal instruction: PUTFIELD on non-reference"); if (TYPEINFO_IS_ARRAY(curstack->prev->typeinfo)) /* XXX arraystub */ panic("illegal instruction: PUTFIELD on array"); /* XXX */ maythrow = true; break; case ICMD_PUTSTATIC: /* XXX */ maythrow = true; /* XXX ? */ break; case ICMD_GETFIELD: if (!TYPEINFO_IS_REFERENCE(curstack->typeinfo)) panic("illegal instruction: GETFIELD on non-reference"); if (TYPEINFO_IS_ARRAY(curstack->typeinfo)) panic("illegal instruction: GETFIELD on array"); { fieldinfo *fi = (fieldinfo *)(iptr->val.a); /* XXX check non-static? */ if (dst->type == TYPE_ADR) { TYPEINFO_INIT_FROM_FIELDINFO(dst->typeinfo,fi); } else { /* XXX check field type? */ TYPEINFO_INIT_PRIMITIVE(dst->typeinfo); } } maythrow = true; break; case ICMD_GETSTATIC: { fieldinfo *fi = (fieldinfo *)(iptr->val.a); /* XXX check static? */ if (dst->type == TYPE_ADR) { TYPEINFO_INIT_FROM_FIELDINFO(dst->typeinfo,fi); } else { /* XXX check field type? */ TYPEINFO_INIT_PRIMITIVE(dst->typeinfo); } } /* XXX may throw? */ break; /****************************************/ /* PRIMITIVE ARRAY ACCESS */ case ICMD_ARRAYLENGTH: /* XXX should this also work on arraystubs? */ if (!TYPEINFO_MAYBE_ARRAY(curstack->typeinfo)) panic("illegal instruction: ARRAYLENGTH on non-array"); maythrow = true; break; case ICMD_BALOAD: if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(curstack->prev->typeinfo,ARRAYTYPE_BOOLEAN) && !TYPEINFO_MAYBE_PRIMITIVE_ARRAY(curstack->prev->typeinfo,ARRAYTYPE_BYTE)) panic("Array type mismatch"); maythrow = true; break; case ICMD_CALOAD: if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(curstack->prev->typeinfo,ARRAYTYPE_CHAR)) panic("Array type mismatch"); maythrow = true; break; case ICMD_DALOAD: if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(curstack->prev->typeinfo,ARRAYTYPE_DOUBLE)) panic("Array type mismatch"); maythrow = true; break; case ICMD_FALOAD: if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(curstack->prev->typeinfo,ARRAYTYPE_FLOAT)) panic("Array type mismatch"); maythrow = true; break; case ICMD_IALOAD: if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(curstack->prev->typeinfo,ARRAYTYPE_INT)) panic("Array type mismatch"); maythrow = true; break; case ICMD_SALOAD: if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(curstack->prev->typeinfo,ARRAYTYPE_SHORT)) panic("Array type mismatch"); maythrow = true; break; case ICMD_LALOAD: if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(curstack->prev->typeinfo,ARRAYTYPE_LONG)) panic("Array type mismatch"); maythrow = true; break; case ICMD_BASTORE: if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(curstack->prev->prev->typeinfo,ARRAYTYPE_BOOLEAN) && !TYPEINFO_MAYBE_PRIMITIVE_ARRAY(curstack->prev->prev->typeinfo,ARRAYTYPE_BYTE)) panic("Array type mismatch"); maythrow = true; break; case ICMD_CASTORE: if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(curstack->prev->prev->typeinfo,ARRAYTYPE_CHAR)) panic("Array type mismatch"); maythrow = true; break; case ICMD_DASTORE: if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(curstack->prev->prev->typeinfo,ARRAYTYPE_DOUBLE)) panic("Array type mismatch"); maythrow = true; break; case ICMD_FASTORE: if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(curstack->prev->prev->typeinfo,ARRAYTYPE_FLOAT)) panic("Array type mismatch"); maythrow = true; break; case ICMD_IASTORE: if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(curstack->prev->prev->typeinfo,ARRAYTYPE_INT)) panic("Array type mismatch"); maythrow = true; break; case ICMD_SASTORE: if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(curstack->prev->prev->typeinfo,ARRAYTYPE_SHORT)) panic("Array type mismatch"); maythrow = true; break; case ICMD_LASTORE: if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(curstack->prev->prev->typeinfo,ARRAYTYPE_LONG)) panic("Array type mismatch"); maythrow = true; break; /****************************************/ /* OPERATIONS WITH UNCHECKED INPUT */ case ICMD_CHECKCAST: /* returnAddress is not allowed */ if (!TYPEINFO_IS_REFERENCE(curstack->typeinfo)) panic("Illegal instruction: INSTANCEOF on non-reference"); /* XXX check if the cast can be done statically */ TYPEINFO_INIT_CLASSINFO(dst->typeinfo,(classinfo *)iptr[0].val.a); /* XXX */ maythrow = true; break; case ICMD_INSTANCEOF: /* returnAddress is not allowed */ if (!TYPEINFO_IS_REFERENCE(curstack->typeinfo)) panic("Illegal instruction: INSTANCEOF on non-reference"); /* XXX may throw ? */ break; case ICMD_ACONST: if (iptr->val.a == NULL) TYPEINFO_INIT_NULLTYPE(dst->typeinfo) else /* XXX constants for builtin functions */ /* string constants */ TYPEINFO_INIT_CLASSINFO(dst->typeinfo,class_java_lang_String); break; /****************************************/ /* BRANCH INSTRUCTIONS */ case ICMD_GOTO: superblockend = true; /* FALLTHROUGH! */ case ICMD_IFNULL: case ICMD_IFNONNULL: case ICMD_IFEQ: case ICMD_IFNE: case ICMD_IFLT: case ICMD_IFGE: case ICMD_IFGT: case ICMD_IFLE: case ICMD_IF_ICMPEQ: case ICMD_IF_ICMPNE: case ICMD_IF_ICMPLT: case ICMD_IF_ICMPGE: case ICMD_IF_ICMPGT: case ICMD_IF_ICMPLE: case ICMD_IF_ACMPEQ: case ICMD_IF_ACMPNE: case ICMD_IF_LEQ: case ICMD_IF_LNE: case ICMD_IF_LLT: case ICMD_IF_LGE: case ICMD_IF_LGT: case ICMD_IF_LLE: case ICMD_IF_LCMPEQ: case ICMD_IF_LCMPNE: case ICMD_IF_LCMPLT: case ICMD_IF_LCMPGE: case ICMD_IF_LCMPGT: case ICMD_IF_LCMPLE: tbptr = (basicblock *) iptr->target; /* propagate stack and variables to the target block */ TYPECHECK_REACH(REACH_STD); /* XXX */ break; /****************************************/ /* SWITCHES */ case ICMD_TABLESWITCH: { s4 *s4ptr = iptr->val.a; s4ptr++; /* skip default */ i = *s4ptr++; /* low */ i = *s4ptr++ - i + 2; /* +1 for default target */ } goto switch_instruction_tail; case ICMD_LOOKUPSWITCH: { s4 *s4ptr = iptr->val.a; s4ptr++; /* skip default */ i = *s4ptr++ + 1; /* count +1 for default target */ } switch_instruction_tail: tptr = (basicblock **)iptr->target; while (--i >= 0) { tbptr = *tptr++; LOG2("target %d is block %04d",(tptr-(basicblock **)iptr->target)-1,tbptr-block); TYPECHECK_REACH(REACH_STD); } LOG("switch done"); superblockend = true; break; /****************************************/ /* RETURNS AND THROW */ case ICMD_ATHROW: TYPEINFO_INIT_CLASSINFO(tempinfo,class_java_lang_Throwable); if (!typeinfo_is_assignable(&curstack->typeinfo,&tempinfo)) panic("illegal instruction: ATHROW on non-Throwable"); superblockend = true; maythrow = true; break; case ICMD_ARETURN: if (!TYPEINFO_IS_REFERENCE(curstack->typeinfo)) panic("illegal instruction: ARETURN on non-reference"); if (returntype != TYPE_ADDRESS || !typeinfo_is_assignable(&curstack->typeinfo,&returntypeinfo)) panic("Return type mismatch"); superblockend = true; break; case ICMD_IRETURN: if (returntype != TYPE_INT) panic("Return type mismatch"); superblockend = true; break; case ICMD_LRETURN: if (returntype != TYPE_LONG) panic("Return type mismatch"); superblockend = true; break; case ICMD_FRETURN: if (returntype != TYPE_FLOAT) panic("Return type mismatch"); superblockend = true; break; case ICMD_DRETURN: if (returntype != TYPE_DOUBLE) panic("Return type mismatch"); superblockend = true; break; case ICMD_RETURN: if (returntype != TYPE_VOID) panic("Return type mismatch"); superblockend = true; break; /****************************************/ /* SUBROUTINE INSTRUCTIONS */ case ICMD_JSR: LOG("jsr"); /* XXX This is a dirty hack. It is needed * because of the special handling of ICMD_JSR in stack.c */ dst = (stackptr) iptr->val.a; /* push return address */ TYPEINFO_INIT_PRIMITIVE(dst->typeinfo); LOG("reaching block..."); /* add the target to the JSR target chain and */ /* propagate stack and variables to the target block */ tbptr = (basicblock *) iptr->target; TYPECHECK_REACH(REACH_JSR); /* set dst to the stack after the subroutine execution */ /* XXX We assume (as in stack.c) that the * subroutine returns the stack as it was * before the JSR instruction. Is this * correct? */ dst = iptr->dst; /* Find the jsr_record of the called subroutine */ jsrtemp = jsrbuffer[tbptr - block]; /* Check if we already calculated (at least * for one RET) which variables the * subroutine touches. */ if (jsrtemp->sbr_touched) { /* Calculate the local variables after the subroutine call */ for (i=0; isbr_touched[i] != TOUCHED_NO) { TOUCH_VARIABLE(i); if ((vtype[i] = jsrtemp->sbr_vtype[i]) == TYPE_ADR) TYPEINFO_CLONE(jsrtemp->sbr_vinfo[i],vinfo[i]); } /* continue after the JSR call */ superblockend = false; } else { /* We cannot proceed until the subroutine has been typechecked. */ /* XXX actually we would not have to check this block again */ bptr->flags = BBTYPECHECK_REACHED; repeat = true; superblockend = true; } /* XXX may throw? */ break; case ICMD_RET: /* check returnAddress variable */ CHECKVARTYPE(iptr->op1,TYPE_ADR); if (!TYPEINFO_IS_PRIMITIVE(vinfo[iptr->op1])) panic("illegal instruction: RET using non-returnAddress variable"); /* check if we are inside a subroutine */ if (!subroutine) panic("RET outside of subroutine"); /* determine which variables are touched by this subroutine */ /* and their types */ if (subroutine->sbr_touched) { for (i=0; isbr_touched[i] |= touched[i]; ttype = subroutine->sbr_vtype; tinfo = subroutine->sbr_vinfo; TYPECHECK_MERGEVARS; } else { subroutine->sbr_touched = DMNEW(u1,maxlocals); memcpy(subroutine->sbr_touched,touched,sizeof(u1)*maxlocals); subroutine->sbr_vtype = DMNEW(u1,maxlocals); memcpy(subroutine->sbr_vtype,vtype,sizeof(u1)*maxlocals); subroutine->sbr_vinfo = DMNEW(typeinfo,maxlocals); for (i=0; isbr_vinfo[i]); } /* XXX check if subroutine changed types? */ LOGSTR("subroutine touches:"); DOLOG(typeinfo_print_locals(stdout,subroutine->sbr_vtype,subroutine->sbr_vinfo, subroutine->sbr_touched,maxlocals)); LOGNL; LOGFLUSH; /* reach blocks after JSR statements */ for (i=0; idebug_nr); if (tbptr->iinstr[tbptr->icount - 1].opc != ICMD_JSR) continue; LOG("ends with JSR"); if ((basicblock*) tbptr->iinstr[tbptr->icount - 1].target != subroutine->target) continue; tbptr++; LOG1("RET reaches block %04d",tbptr-block); /*TYPECHECK_REACH(REACH_RET);*/ } superblockend = true; break; /****************************************/ /* INVOKATIONS */ case ICMD_INVOKEVIRTUAL: case ICMD_INVOKESPECIAL: case ICMD_INVOKESTATIC: case ICMD_INVOKEINTERFACE: { /* XXX check access rights */ methodinfo *mi = (methodinfo*) iptr->val.a; /* XXX might use dst->typeinfo directly if non void */ typeinfo_init_from_method_args(mi->descriptor,ptype,pinfo,MAXPARAMS,false, &rtype,&rinfo); /* XXX compare rtype and dst->type? */ if (rtype != TYPE_VOID) { TYPEINFO_COPY(rinfo,dst->typeinfo); } } maythrow = true; break; case ICMD_MULTIANEWARRAY: /* check the array lengths on the stack */ i = iptr[0].op1; if (i<1) panic("MULTIANEWARRAY with dimensions < 1"); srcstack = curstack; while (i--) { if (!srcstack) panic("MULTIANEWARRAY missing array length"); if (srcstack->type != TYPE_INT) panic("MULTIANEWARRAY using non-int as array length"); srcstack = srcstack->prev; } /* set the array type of the result */ TYPEINFO_INIT_CLASSINFO(dst->typeinfo,((vftbl *)iptr[0].val.a)->class); maythrow = true; break; case ICMD_BUILTIN3: if (ISBUILTIN(asm_builtin_aastore)) { /* XXX also handled by ICMD_AASTORE */ if (!TYPEINFO_MAYBE_ARRAY_OF_REFS(curstack->prev->prev->typeinfo)) panic("illegal instruction: AASTORE to non-reference array"); /* XXX optimize */ /* typeinfo_init_component(&curstack->prev->prev->typeinfo,&tempinfo); if (!typeinfo_is_assignable(&curstack->typeinfo,&tempinfo)) panic("illegal instruction: AASTORE to incompatible type"); */ } /* XXX check for missed builtins in debug mode? */ maythrow = true; /* XXX better safe than sorry */ break; case ICMD_BUILTIN2: if ( #if defined(__I386__) ISBUILTIN(asm_builtin_newarray) #else ISBUILTIN(builtin_newarray) #endif ) { if (iptr[-1].opc != ICMD_ACONST) panic("illegal instruction: builtin_newarray without classinfo"); TYPEINFO_INIT_CLASSINFO(dst->typeinfo,((vftbl *)iptr[-1].val.a)->class); } else if (ISBUILTIN(asm_builtin_checkarraycast)) { if (iptr[-1].opc != ICMD_ACONST) panic("illegal instruction: asm_builtin_checkarraycast without classinfo"); TYPEINFO_INIT_CLASSINFO(dst->typeinfo,((vftbl *)iptr[-1].val.a)->class); } /* XXX check for missed builtins in debug mode? */ maythrow = true; /* XXX better safe than sorry */ break; case ICMD_BUILTIN1: if (ISBUILTIN(builtin_new)) { if (iptr[-1].opc != ICMD_ACONST) panic("illegal instruction: builtin_new without classinfo"); TYPEINFO_INIT_CLASSINFO(dst->typeinfo,(classinfo *)iptr[-1].val.a); } else if (ISBUILTIN(builtin_newarray_boolean)) { TYPEINFO_INIT_PRIMITIVE_ARRAY(dst->typeinfo,ARRAYTYPE_BOOLEAN); } else if (ISBUILTIN(builtin_newarray_char)) { TYPEINFO_INIT_PRIMITIVE_ARRAY(dst->typeinfo,ARRAYTYPE_CHAR); } else if (ISBUILTIN(builtin_newarray_float)) { TYPEINFO_INIT_PRIMITIVE_ARRAY(dst->typeinfo,ARRAYTYPE_FLOAT); } else if (ISBUILTIN(builtin_newarray_double)) { TYPEINFO_INIT_PRIMITIVE_ARRAY(dst->typeinfo,ARRAYTYPE_DOUBLE); } else if (ISBUILTIN(builtin_newarray_byte)) { TYPEINFO_INIT_PRIMITIVE_ARRAY(dst->typeinfo,ARRAYTYPE_BYTE); } else if (ISBUILTIN(builtin_newarray_short)) { TYPEINFO_INIT_PRIMITIVE_ARRAY(dst->typeinfo,ARRAYTYPE_SHORT); } else if (ISBUILTIN(builtin_newarray_int)) { TYPEINFO_INIT_PRIMITIVE_ARRAY(dst->typeinfo,ARRAYTYPE_INT); } else if (ISBUILTIN(builtin_newarray_long)) { TYPEINFO_INIT_PRIMITIVE_ARRAY(dst->typeinfo,ARRAYTYPE_LONG); } /* XXX check for missed builtins in debug mode? */ maythrow = true; /* XXX better safe than sorry */ break; /****************************************/ /* PRIMITIVE VARIABLE ACCESS */ case ICMD_ILOAD: CHECKVARTYPE(iptr->op1,TYPE_INT); break; case ICMD_LLOAD: CHECKVARTYPE(iptr->op1,TYPE_LONG); break; case ICMD_FLOAD: CHECKVARTYPE(iptr->op1,TYPE_FLOAT); break; case ICMD_DLOAD: CHECKVARTYPE(iptr->op1,TYPE_DOUBLE); break; case ICMD_IINC: CHECKVARTYPE(iptr->op1,TYPE_INT); /*TOUCH_VARIABLE(iptr->op1);*/ break; case ICMD_FSTORE: STORE_PRIMITIVE(iptr->op1,TYPE_FLOAT); break; case ICMD_ISTORE: STORE_PRIMITIVE(iptr->op1,TYPE_INT); break; case ICMD_LSTORE: STORE_TWOWORD(iptr->op1,TYPE_LONG); break; case ICMD_DSTORE: STORE_TWOWORD(iptr->op1,TYPE_DOUBLE); break; /****************************************/ /* INSTRUCTIONS WHICH SHOULD HAVE BEEN */ /* REPLACED BY BUILTIN CALLS */ case ICMD_NEW: case ICMD_NEWARRAY: case ICMD_ANEWARRAY: case ICMD_MONITORENTER: case ICMD_MONITOREXIT: /* XXX only check this in debug mode? */ LOGSTR2("ICMD %d at %d\n", iptr->opc, (int)(iptr-instr)); panic("Internal error: unexpected instruction encountered"); break; /****************************************/ /* UNCHECKED OPERATIONS */ /* These ops have no input or output to be checked */ /* (apart from the checks done in analyse_stack). */ /* XXX only add cases for them in debug mode? */ case ICMD_NOP: case ICMD_READONLY_ARG: /* XXX ? */ case ICMD_CLEAR_ARGREN: /* XXX ? */ break; case ICMD_CHECKASIZE: case ICMD_NULLCHECKPOP: maythrow = true; break; /****************************************/ /* ARITHMETIC AND CONVERSION */ /* These instructions are typechecked in analyse_stack. */ /* XXX only add cases for them in debug mode? */ case ICMD_IADD: case ICMD_ISUB: case ICMD_IMUL: case ICMD_IDIV: case ICMD_IREM: case ICMD_INEG: case ICMD_IAND: case ICMD_IOR: case ICMD_IXOR: case ICMD_ISHL: case ICMD_ISHR: case ICMD_IUSHR: case ICMD_LADD: case ICMD_LSUB: case ICMD_LMUL: case ICMD_LDIV: case ICMD_LREM: case ICMD_LNEG: case ICMD_LAND: case ICMD_LOR: case ICMD_LXOR: case ICMD_LSHL: case ICMD_LSHR: case ICMD_LUSHR: case ICMD_IREM0X10001: case ICMD_LREM0X10001: case ICMD_IDIVPOW2: case ICMD_LDIVPOW2: case ICMD_IADDCONST: case ICMD_ISUBCONST: case ICMD_IMULCONST: case ICMD_IANDCONST: case ICMD_IORCONST: case ICMD_IXORCONST: case ICMD_ISHLCONST: case ICMD_ISHRCONST: case ICMD_IUSHRCONST: case ICMD_IREMPOW2: case ICMD_LADDCONST: case ICMD_LSUBCONST: case ICMD_LMULCONST: case ICMD_LANDCONST: case ICMD_LORCONST: case ICMD_LXORCONST: case ICMD_LSHLCONST: case ICMD_LSHRCONST: case ICMD_LUSHRCONST: case ICMD_LREMPOW2: case ICMD_LCMP: case ICMD_LCMPCONST: case ICMD_FCMPL: case ICMD_FCMPG: case ICMD_DCMPL: case ICMD_DCMPG: case ICMD_FADD: case ICMD_DADD: case ICMD_FSUB: case ICMD_DSUB: case ICMD_FMUL: case ICMD_DMUL: case ICMD_FDIV: case ICMD_DDIV: case ICMD_FREM: case ICMD_DREM: case ICMD_FNEG: case ICMD_DNEG: case ICMD_I2L: case ICMD_I2F: case ICMD_I2D: case ICMD_L2I: case ICMD_L2F: case ICMD_L2D: case ICMD_F2I: case ICMD_F2L: case ICMD_F2D: case ICMD_D2I: case ICMD_D2L: case ICMD_D2F: case ICMD_INT2BYTE: case ICMD_INT2CHAR: case ICMD_INT2SHORT: maythrow = true; /* XXX be more selective here */ break; case ICMD_ICONST: case ICMD_LCONST: case ICMD_FCONST: case ICMD_DCONST: case ICMD_IFEQ_ICONST: case ICMD_IFNE_ICONST: case ICMD_IFLT_ICONST: case ICMD_IFGE_ICONST: case ICMD_IFGT_ICONST: case ICMD_IFLE_ICONST: case ICMD_ELSE_ICONST: break; /****************************************/ default: LOGSTR2("ICMD %d at %d\n", iptr->opc, (int)(iptr-instr)); panic("Missing ICMD code during typecheck"); } /* the output of this instruction becomes the current stack */ curstack = dst; /* reach exception handlers for this instruction */ if (maythrow) { LOG("reaching exception handlers"); i = 0; while (handlers[i]) { tbptr = handlers[i]->handler; dst = tbptr->instack; TYPECHECK_REACH(REACH_STD); /* XXX jsr chain? */ i++; } dst = curstack; /* restore dst */ } iptr++; } /* while instructions */ LOG("instructions done"); LOGSTR("RESULT=> "); DOLOG(typeinfo_print_block(stdout,curstack,vtype,vinfo,(jsrchain) ? touched : NULL)); LOGNL; LOGFLUSH; /* propagate stack and variables to the following block */ if (!superblockend) { LOG("reaching following block"); tbptr = bptr + 1; while (tbptr->flags == BBDELETED) { tbptr++; #ifdef TYPECHECK_DEBUG if ((tbptr-block) >= block_count) panic("Control flow falls off the last block"); #endif } TYPECHECK_REACH(REACH_STD); } } /* if block has to be checked */ bptr++; } /* while blocks */ LOGIF(repeat,"repeat=true"); } while (repeat); /* XXX reset BB... to BBFINISHED */ /* XXX free vartype */ /* XXX free vartypeinfo */ /* XXX free buffers for method arguments. */ /* XXX add debug check if all non-dead blocks have been checked */ LOGimp("exiting typecheck"); } #undef COPYTYPE #endif /* CACAO_TYPECHECK */