Authors: Edwin Steiner
- $Id: typecheck.c 696 2003-12-06 20:10:05Z edwin $
+ $Id: typecheck.c 730 2003-12-11 21:23:31Z edwin $
*/
#ifdef CACAO_TYPECHECK
+#include <string.h>
#include "jit.h"
#include "builtin.h"
#include "tables.h"
#define TOUCHED_NO 0x02
#define TOUCHED_MAYBE (TOUCHED_YES | TOUCHED_NO)
-#define REACH_STD 0
-#define REACH_JSR 1
-#define REACH_RET 2
+#define REACH_STD 0 /* reached by branch or fallthrough */
+#define REACH_JSR 1 /* reached by JSR */
+#define REACH_RET 2 /* reached by RET */ /* XXX ? */
+#define REACH_THROW 3 /* reached by THROW (exception handler) */
/****************************************************************************/
/* DEBUG HELPERS */
#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))
+#define LOGINFO(info) DOLOG(do {typeinfo_print_short(get_logfile(),info);log_plain("\n");} while(0))
+#define LOGFLUSH DOLOG(fflush(get_logfile()))
+#define LOGNL DOLOG(log_plain("\n"))
+#define LOGSTR(str) DOLOG(log_plain(str))
+#define LOGSTR1(str,a) DOLOG(dolog_plain(str,a))
+#define LOGSTR2(str,a,b) DOLOG(dolog_plain(str,a,b))
+#define LOGSTR3(str,a,b,c) DOLOG(dolog_plain(str,a,b,c))
+#define LOGSTRu(utf) DOLOG(log_plain_utf(utf))
#else
#define LOG(str)
#define LOG1(str,a)
#define LOGSTR1(str,a)
#define LOGSTR2(str,a,b)
#define LOGSTR3(str,a,b,c)
+#define LOGSTRu(utf)
#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))
+#define LOGimpSTR(str) DOLOG(log_plain(str))
+#define LOGimpSTRu(utf) DOLOG(log_plain_utf(utf))
#else
#define LOGimp(str)
#define LOGimpSTR(str)
#if defined(TYPECHECK_VERBOSE) || defined(TYPECHECK_VERBOSE_IMPORTANT)
+#include <stdio.h>
+
static
void
typeinfo_print_locals(FILE *file,u1 *vtype,typeinfo *vinfo,u1 *touched,int num)
static
void
typeinfo_print_block(FILE *file,stackptr instack,
- u1 *vtype,typeinfo *vinfo,u1 *touched)
+ int vnum,u1 *vtype,typeinfo *vinfo,u1 *touched)
{
fprintf(file,"Stack: ");
typeinfo_print_stack(file,instack);
fprintf(file," Locals:");
- typeinfo_print_locals(file,vtype,vinfo,touched,maxlocals);
+ typeinfo_print_locals(file,vtype,vinfo,touched,vnum);
}
static
void
-typeinfo_print_blocks(FILE *file,u1 *vtype,typeinfo *vinfo)
+typeinfo_print_blocks(FILE *file,int vnum,u1 *vtype,typeinfo *vinfo)
{
int bi;
/* int j;*/
for (bi=0; bi<block_count; ++bi) {
fprintf(file,"%04d: (%3d) ",bi,block[bi].flags);
typeinfo_print_block(file,block[bi].instack,
- vtype+maxlocals*bi,vinfo+maxlocals*bi,NULL);
+ vnum,vtype+vnum*bi,vinfo+vnum*bi,NULL);
fprintf(file,"\n");
/* for (j=0; j<block[bi].icount; ++j) { */
#endif
+/****************************************************************************/
+/* STATISTICS */
+/****************************************************************************/
+
+#ifdef TYPECHECK_DEBUG
+/*#define TYPECHECK_STATISTICS*/
+#endif
+
+#ifdef TYPECHECK_STATISTICS
+#define TYPECHECK_COUNT(cnt) (cnt)++
+#else
+#define TYPECHECK_COUNT(cnt)
+#endif
+
/****************************************************************************/
/* INTERNAL DATA STRUCTURES */
/****************************************************************************/
/* MACROS USED INTERNALLY IN typecheck() */
/****************************************************************************/
-#define TOUCH_VARIABLE(num) do {if (jsrchain) touched[num] = TOUCHED_YES;} while (0)
-#define TOUCH_TWOWORD(num) do {TOUCH_VARIABLE(num);TOUCH_VARIABLE((num)+1);} while (0)
-
-/* XXX should check num in range? */
-/* XXX invalidate two word variables on store in second half! */
-#define STORE_TYPE(num,type) do {vtype[(num)] = (type); TOUCH_VARIABLE(num);} while(0)
-#define STORE_INVALID(num) STORE_TYPE((num),TYPE_VOID)
-#define STORE_PRIMITIVE(num,type) STORE_TYPE((num),(type))
-#define STORE_TWOWORD(num,type) {STORE_PRIMITIVE((num),(type));STORE_INVALID((num)+1);}
-
-#define CHECKVARTYPE(num,type) \
- {if (vtype[(num)] != (type)) panic("Variable type mismatch"); TOUCH_VARIABLE(num);}
+#define TOUCH_VARIABLE(num) \
+ do {if (jsrchain) touched[num] = TOUCHED_YES;} while (0)
+#define TOUCH_TWOWORD(num) \
+ do {TOUCH_VARIABLE(num);TOUCH_VARIABLE((num)+1);} while (0)
+
+#define INDEX_ONEWORD(num) \
+ do { if((num)<0 || (num)>=validlocals) \
+ panic("Invalid local variable index"); } while (0)
+#define INDEX_TWOWORD(num) \
+ do { if((num)<0 || ((num)+1)>=validlocals) \
+ panic("Invalid local variable index"); } while (0)
+
+#define SET_VARIABLE(num,type) \
+ do {vtype[num] = (type); \
+ if ((num)>0 && IS_2_WORD_TYPE(vtype[(num)-1])) { \
+ vtype[(num)-1] = TYPE_VOID; \
+ TOUCH_VARIABLE((num)-1); \
+ } \
+ TOUCH_VARIABLE(num);} while(0)
+
+#define STORE_ONEWORD(num,type) \
+ do {INDEX_ONEWORD(num); \
+ SET_VARIABLE(num,type);} while(0)
+
+#define STORE_TWOWORD(num,type) \
+ do {INDEX_TWOWORD(num); \
+ SET_VARIABLE(num,type); \
+ vtype[(num)+1] = TYPE_VOID; \
+ TOUCH_VARIABLE((num)+1);} while(0)
+
+#define CHECK_ONEWORD(num,type) \
+ do {INDEX_ONEWORD(num); \
+ if (vtype[(num)] != (type)) panic("Variable type mismatch"); \
+ TOUCH_VARIABLE(num);} while(0)
+
+#define CHECK_TWOWORD(num,type) \
+ do {INDEX_TWOWORD(num); \
+ if (vtype[(num)] != (type)) panic("Variable type mismatch"); \
+ TOUCH_VARIABLE(num); \
+ TOUCH_VARIABLE((num)+1);} while(0)
/* XXX maybe it's faster to copy always */
#define COPYTYPE(source,dest) \
#define ISBUILTIN(v) (iptr->val.a == (functionptr)(v))
+#define TYPECHECK_STACK(sp,tp) \
+ do { if ((sp)->type != (tp)) \
+ panic("Wrong data type on stack"); } while(0)
+
+#define TYPECHECK_ADR(sp) TYPECHECK_STACK(sp,TYPE_ADR)
+#define TYPECHECK_INT(sp) TYPECHECK_STACK(sp,TYPE_INT)
+#define TYPECHECK_LNG(sp) TYPECHECK_STACK(sp,TYPE_LNG)
+#define TYPECHECK_FLT(sp) TYPECHECK_STACK(sp,TYPE_FLT)
+#define TYPECHECK_DBL(sp) TYPECHECK_STACK(sp,TYPE_DBL)
+
+#define TYPECHECK_ARGS1(t1) \
+ do {TYPECHECK_STACK(curstack,t1);} while (0)
+#define TYPECHECK_ARGS2(t1,t2) \
+ do {TYPECHECK_ARGS1(t1); \
+ TYPECHECK_STACK(curstack->prev,t2);} while (0)
+#define TYPECHECK_ARGS3(t1,t2,t3) \
+ do {TYPECHECK_ARGS2(t1,t2); \
+ TYPECHECK_STACK(curstack->prev->prev,t3);} while (0)
+
/* TYPECHECK_COPYVARS: copy the types and typeinfos of the current local
* variables to the local variables of the target block.
* Input:
* vinfo......current local variable typeinfos
* ttype......local variable types of target block
* tinfo......local variable typeinfos of target block
- * maxlocals..number of local variables
+ * numlocals..number of local variables
* Used:
* macro_i
*/
#define TYPECHECK_COPYVARS \
+ do { \
LOG("TYPECHECK_COPYVARS"); \
- for (macro_i=0; macro_i<maxlocals; ++macro_i) { \
+ for (macro_i=0; macro_i<numlocals; ++macro_i) { \
if ((ttype[macro_i] = vtype[macro_i]) == TYPE_ADR) \
TYPEINFO_CLONE(vinfo[macro_i],tinfo[macro_i]); \
- }
+ } } while(0)
/* TYPECHECK_MERGEVARS: merge the local variables of the target block
* with the current local variables.
* vinfo......current local variable typeinfos
* ttype......local variable types of target block
* tinfo......local variable typeinfos of target block
- * maxlocals..number of local variables
+ * numlocals..number of local variables
* Ouput:
* changed....set to true if any typeinfo has changed
* Used:
* macro_i
*/
#define TYPECHECK_MERGEVARS \
+ do { \
LOG("TYPECHECK_MERGEVARS"); \
- for (macro_i=0; macro_i<maxlocals; ++macro_i) { \
+ for (macro_i=0; macro_i<numlocals; ++macro_i) { \
if ((ttype[macro_i] != TYPE_VOID) && (vtype[macro_i] != ttype[macro_i])) { \
LOG3("var %d: type %d + type %d = void",macro_i,ttype[macro_i],vtype[macro_i]); \
ttype[macro_i] = TYPE_VOID; \
LOGIF(changed,"vars have changed"); \
} \
}; \
- }
+ } } while (0)
/* TYPECHECK_MERGEJSR:
*
* Ouput:
* tbptr......target block
* changed....set to true if any typeinfo has changed
- * maxlocals..number of local variables
+ * numlocals..number of local variables
* touched....current touched flags of local variables
* Used:
* macro_i, jsrtemp, jsrtemp2
} \
jsrtemp = jsrbuffer[tbptr-block]; \
if (jsrtemp) \
- for (macro_i=0; macro_i<maxlocals; ++macro_i) { \
+ for (macro_i=0; macro_i<numlocals; ++macro_i) { \
jsrtemp->touched[i] |= touched[i]; \
} } while (0)
* dststack...input stack of target block
*/
#define TYPECHECK_COPYSTACK \
+ do { \
LOG("TYPECHECK_COPYSTACK"); \
while (srcstack) { \
LOG1("copy %d",srcstack->type); \
dststack = dststack->prev; \
srcstack = srcstack->prev; \
} \
- if (dststack) panic("Stack depth mismatch");
+ if (dststack) panic("Stack depth mismatch"); \
+ } while (0)
/* TYPECHECK_MERGESTACK: merge the input stack of the target block
* with the current stack.
* changed....set to true if any typeinfo has changed
*/
#define TYPECHECK_MERGESTACK \
+ do { \
LOG("TYPECHECK_MERGESTACK"); \
while (srcstack) { \
if (!dststack) panic("Stack depth mismatch"); \
dststack = dststack->prev; \
srcstack = srcstack->prev; \
} \
- if (dststack) panic("Stack depth mismatch");
+ if (dststack) panic("Stack depth mismatch"); \
+ } while(0)
/* TYPECHECK_CHECK_JSR_CHAIN: checks if the target block is reached by
* Input:
* jsrchain...current JSR target chain
* tbptr.....the basic block targeted by the JSR
- * maxlocals..number of local variables
+ * numlocals..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 = (jsr_record *) dump_alloc(sizeof(jsr_record)+(numlocals-1)*sizeof(u1)); \
jsrtemp->target = (tbptr); \
jsrtemp->next = jsrchain; \
jsrtemp->sbr_touched = NULL; \
- memset(&jsrtemp->touched,TOUCHED_NO,sizeof(u1)*maxlocals); \
+ memset(&jsrtemp->touched,TOUCHED_NO,sizeof(u1)*numlocals); \
jsrbuffer[tbptr-block] = jsrtemp; \
} while (0)
* Input:
* chain......current JSR target chain
* tbptr.....the basic block targeted by the JSR
- * maxlocals..number of local variables
+ * numlocals..number of local variables
* jsrbuffer..JSR target chain for each basic block
* touched....current touched flags of local variables
* Used:
do { \
LOG("TYPECHECK_COPYJSR"); \
if (chain) { \
- jsrtemp = (jsr_record *) dump_alloc(sizeof(jsr_record)+(maxlocals-1)*sizeof(u1)); \
+ jsrtemp = (jsr_record *) dump_alloc(sizeof(jsr_record)+(numlocals-1)*sizeof(u1)); \
jsrtemp->target = (chain)->target; \
jsrtemp->next = (chain)->next; \
jsrtemp->sbr_touched = NULL; \
- memcpy(&jsrtemp->touched,touched,sizeof(u1)*maxlocals); \
+ memcpy(&jsrtemp->touched,touched,sizeof(u1)*numlocals); \
jsrbuffer[tbptr-block] = jsrtemp; \
} \
else \
jsrbuffer[tbptr-block] = NULL; \
} while (0)
+/* TYPECHECK_BRANCH_BACKWARDS: executed when control flow moves
+ * backwards. Checks if there are uninitialized objects on the
+ * stack or in local variables.
+ * Input:
+ * dst........current output stack pointer (not needed for REACH_THROW)
+ * vtype......current local variable types
+ * vinfo......current local variable typeinfos
+ * Used:
+ * srcstack, macro_i
+ */
+#define TYPECHECK_BRANCH_BACKWARDS \
+ do { \
+ LOG("BACKWARDS!"); \
+ srcstack = dst; \
+ while (srcstack) { \
+ if (srcstack->type == TYPE_ADR && \
+ TYPEINFO_IS_NEWOBJECT(srcstack->typeinfo)) \
+ panic("Branching backwards with uninitialized object on stack"); \
+ srcstack = srcstack->prev; \
+ } \
+ for (macro_i=0; macro_i<numlocals; ++macro_i) \
+ if (vtype[macro_i] == TYPE_ADR && \
+ TYPEINFO_IS_NEWOBJECT(vinfo[macro_i])) \
+ panic("Branching backwards with uninitialized object in local variable"); \
+ } 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
+ * dst........current output stack pointer (not needed for REACH_THROW)
+ * numlocals..number of local variables
* vtype......current local variable types
* vinfo......current local variable typeinfos
* jsrchain...current JSR target chain
* ttype, tinfo, srcstack, dststack, changed, macro_i
*/
#define TYPECHECK_REACH(way) \
- LOG1("reaching block %04d",tbptr-block); \
+ do { \
+ LOG2("reaching block %04d (%d)",tbptr-block,way); \
+ if (tbptr <= bptr && way != REACH_THROW) \
+ TYPECHECK_BRANCH_BACKWARDS; \
srcstack = dst; \
dststack = tbptr->instack; \
- ttype = vartype + maxlocals*(tbptr-block); \
- tinfo = vartypeinfo + maxlocals*(tbptr-block); \
+ ttype = vartype + numlocals*(tbptr-block); \
+ tinfo = vartypeinfo + numlocals*(tbptr-block); \
if (tbptr->flags == BBTYPECHECK_UNDEF) { \
/* This block is reached for the first time */ \
if (way == REACH_JSR) { \
TYPECHECK_COPYJSR(jsrchain); \
TYPECHECK_COPYVARS; \
} \
- TYPECHECK_COPYSTACK; \
+ if (way != REACH_THROW) TYPECHECK_COPYSTACK; \
changed = true; \
} else { \
/* This block has been reached before */ \
TYPECHECK_CHECK_JSR_CHAIN; \
else \
TYPECHECK_MERGEJSR; \
- LOGIF(changed,"changed jsr"); \
TYPECHECK_MERGEVARS; \
- LOGIF(changed,"changed vars"); \
- TYPECHECK_MERGESTACK; \
- LOGIF(changed,"changed stack"); \
+ if (way != REACH_THROW) TYPECHECK_MERGESTACK; \
} \
if (changed) { \
LOG("REACHED!"); \
tbptr->flags = BBTYPECHECK_REACHED; \
- if (tbptr <= bptr) {repeat = true; LOG("MUST REPEAT!");} \
- } LOG("done.");
+ if (tbptr <= bptr) {repeat = true; LOG("REPEAT!");} \
+ } \
+ LOG("done."); \
+ } while (0)
+
+/* TYPECHECK_LEAVE: executed when the method is exited non-abruptly
+ * Input:
+ * class........class of the current method
+ * numlocals....number of local variables
+ * vtype........current local variable types
+ * vinfo........current local variable typeinfos
+ * initmethod...true if this is an <init> method
+ */
+#define TYPECHECK_LEAVE \
+ do { \
+ if (initmethod && class != class_java_lang_Object) { \
+ /* check the marker variable */ \
+ LOG("Checking <init> marker"); \
+ if (vtype[numlocals-1] == TYPE_VOID) \
+ panic("<init> method does not initialize 'this'"); \
+ } \
+ } while (0)
/****************************************************************************/
/* 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, macro_i, len, i;
+ stackptr srcstack; /* source stack for copying and merging */
+ stackptr dststack; /* target stack for copying and merging */
+ int opcode; /* current opcode */
+ int macro_i, i; /* temporary counters */
+ int len; /* for counting instructions, etc. */
bool superblockend; /* true if no fallthrough to next block */
bool repeat; /* if true, blocks are iterated over again */
- bool changed;
- instruction *iptr = instr; /* pointer to current instruction */
+ bool changed; /* used in macros */
+ instruction *iptr; /* pointer to current instruction */
basicblock *bptr; /* pointer to current basic block */
- basicblock *tbptr;
+ basicblock *tbptr; /* temporary for target block */
+ int numlocals; /* number of local variables */
+ int validlocals; /* number of valid local variable indices */
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 */
int rtype; /* return type of called method */
typeinfo rinfo; /* typeinfo for return type of called method */
stackptr dst; /* output stack of current instruction */
- int changeddepth; /* depth to which the stack has changed */ /* XXX */
- bool fulltypecheck; /* false == check only changed types */ /* XXX */
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,*jsrold; /* temporary variables */
+ 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 */
+ utf *name_init; /* "<init>" */
+ bool initmethod; /* true if this is an "<init>" method */
+ builtin_descriptor *builtindesc;
+
+#ifdef TYPECHECK_STATISTICS
+ int count_iterations = 0;
+#endif
LOGSTR("\n==============================================================================\n");
DOLOG(show_icmd_method());
LOGimpSTRu(method->class->name);
LOGimpSTR(")\n");
- /* XXX allocate buffers for method arguments */
+ name_init = utf_new_char("<init>");
+ initmethod = (method->name == name_init);
+
ptype = DMNEW(u1,MAXPARAMS);
pinfo = DMNEW(typeinfo,MAXPARAMS);
LOG("Blocks reset.\n");
+ /* number of local variables */
+
+ /* In <init> methods we use an extra local variable to signal if
+ * the 'this' reference has been initialized. */
+ numlocals = maxlocals;
+ validlocals = numlocals;
+ if (initmethod) numlocals++;
+
/* 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));
+ vartype = DMNEW(u1,numlocals * (block_count+1));
+ vartypeinfo = DMNEW(typeinfo,numlocals * (block_count+1));
+ touched = DMNEW(u1,numlocals);
+ vtype = vartype + numlocals * block_count;
+ vinfo = vartypeinfo + numlocals * block_count;
+ memset(vartype,TYPE_VOID,numlocals * (block_count+1) * sizeof(typeinfo));
+ memset(vartypeinfo,0,numlocals * (block_count+1) * sizeof(typeinfo));
LOG("Variable buffer initialized.\n");
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;
/* if this is an instance method initialize the "this" ref type */
if (!(method->flags & ACC_STATIC)) {
*ttype++ = TYPE_ADDRESS;
- TYPEINFO_INIT_CLASSINFO(*tinfo,class);
+ if (initmethod)
+ TYPEINFO_INIT_NEWOBJECT(*tinfo,NULL);
+ else
+ TYPEINFO_INIT_CLASSINFO(*tinfo,class);
tinfo++;
}
/* the rest of the arguments and the return type */
typeinfo_init_from_method_args(method->descriptor,ttype,tinfo,
- maxlocals - (tinfo-vartypeinfo),
+ numlocals - (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; i<method->exceptiontablelength; ++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 */
- fulltypecheck = true; /* XXX */
do {
+ TYPECHECK_COUNT(count_iterations);
repeat = false;
/* init stack at the start of this block */
curstack = bptr->instack;
+ /* determine the active exception handlers for this block */
+ /* XXX could use a faster algorithm with sorted lists or
+ * something? */
+ len = 0;
+ for (i=0; i<method->exceptiontablelength; ++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;
+
/* init variable types at the start of this block */
- for (i=0; i<maxlocals; ++i) {
- vtype[i] = vartype[maxlocals*b_index + i];
- TYPEINFO_COPY(vartypeinfo[maxlocals*b_index + i],vinfo[i]);
+ for (i=0; i<numlocals; ++i) {
+ vtype[i] = vartype[numlocals*b_index + i];
+ TYPEINFO_COPY(vartypeinfo[numlocals*b_index + i],vinfo[i]);
+
+ if (handlers[0] &&
+ vtype[i] == TYPE_ADR && TYPEINFO_IS_NEWOBJECT(vinfo[i]))
+ panic("Uninitialized object in local variable inside try block");
}
/* init JSR target chain */
#endif
subroutine = jsrbuffer[jsrchain->target - block];
- memcpy(touched,jsrchain->touched,sizeof(u1)*maxlocals);
+ memcpy(touched,jsrchain->touched,sizeof(u1)*numlocals);
}
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);
+ typeinfo_print_block(get_logfile(),curstack,numlocals,vtype,vinfo,(jsrchain) ? touched : NULL);
LOGNL; LOGFLUSH;
}
#endif
-
+
/* loop over the instructions */
len = bptr->icount;
iptr = bptr->iinstr;
opcode = iptr->opc;
dst = iptr->dst;
+ maythrow = false;
switch (opcode) {
/* We just need to copy the typeinfo */
/* for slots containing addresses. */
+ /* XXX We assume that the destination stack
+ * slots were continuously allocated in
+ * memory. (The current implementation in
+ * stack.c)
+ */
+
case ICMD_DUP:
COPYTYPE(curstack,dst);
break;
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);
+ CHECK_ONEWORD(iptr->op1,TYPE_ADR);
/* loading a returnAddress is not allowed */
if (TYPEINFO_IS_PRIMITIVE(vinfo[iptr->op1]))
/* STORING ADDRESS TO VARIABLE */
case ICMD_ASTORE:
- /* TYPE_ADR has already been checked. */
- STORE_TYPE(iptr->op1,TYPE_ADDRESS);
+ if (handlers[0] &&
+ TYPEINFO_IS_NEWOBJECT(curstack->typeinfo))
+ panic("Storing uninitialized object in local variable inside try block");
+
+ STORE_ONEWORD(iptr->op1,TYPE_ADDRESS);
TYPEINFO_COPY(curstack->typeinfo,vinfo[iptr->op1]);
break;
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 */
- 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");
- */
- break;
-
/****************************************/
/* FIELD ACCESS */
if (TYPEINFO_IS_ARRAY(curstack->prev->typeinfo)) /* XXX arraystub */
panic("illegal instruction: PUTFIELD on array");
-
- /* XXX */
+ /* check if the value is assignable to the field */
+ {
+ fieldinfo *fi = (fieldinfo*) iptr[0].val.a;
+
+ if (!TYPEINFO_IS_NULLTYPE(curstack->prev->typeinfo)) {
+ cls = fi->class;
+ /* XXX treat uinitialized objects specially? */
+ if (!class_issubclass(curstack->prev->typeinfo.typeclass,
+ cls))
+ panic("PUTFIELD reference type does not support field");
+ }
+
+ /* XXX check flags */
+
+ /* XXX ---> unify with ICMD_PUTSTATIC? */
+
+ /* XXX check access rights */
+
+ if (curstack->type != fi->type)
+ panic("PUTFIELD type mismatch");
+ if (fi->type == TYPE_ADR) {
+ TYPEINFO_INIT_FROM_FIELDINFO(rinfo,fi);
+ if (!typeinfo_is_assignable(&(curstack->typeinfo),
+ &rinfo))
+ panic("PUTFIELD reference type not assignable");
+ }
+ }
+ maythrow = true;
break;
case ICMD_PUTSTATIC:
- /* XXX */
+ /* check if the value is assignable to the field */
+ {
+ fieldinfo *fi = (fieldinfo*) iptr[0].val.a;
+
+ /* check flags */
+ /* XXX check access rights */
+
+ if (curstack->type != fi->type)
+ panic("PUTSTATIC type mismatch");
+ if (fi->type == TYPE_ADR) {
+ TYPEINFO_INIT_FROM_FIELDINFO(rinfo,fi);
+ if (!typeinfo_is_assignable(&(curstack->typeinfo),
+ &rinfo))
+ panic("PUTSTATIC reference type not assignable");
+ }
+ }
+ maythrow = true;
break;
case ICMD_GETFIELD:
{
fieldinfo *fi = (fieldinfo *)(iptr->val.a);
- /* XXX check non-static? */
+
+ if (!TYPEINFO_IS_NULLTYPE(curstack->typeinfo)) {
+ cls = fi->class;
+ if (!class_issubclass(curstack->typeinfo.typeclass,
+ cls))
+ panic("GETFIELD reference type does not support field");
+ }
+
+ /* XXX check flags */
+ /* XXX check access rights */
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? */
+ /* XXX check flags */
+ /* XXX check access rights */
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;
/****************************************/
/* 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:
+ TYPECHECK_ADR(curstack);
/* returnAddress is not allowed */
if (!TYPEINFO_IS_REFERENCE(curstack->typeinfo))
- panic("Illegal instruction: INSTANCEOF on non-reference");
+ panic("Illegal instruction: CHECKCAST 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:
+ TYPECHECK_ADR(curstack);
/* returnAddress is not allowed */
if (!TYPEINFO_IS_REFERENCE(curstack->typeinfo))
panic("Illegal instruction: INSTANCEOF on non-reference");
- /* XXX */
+ /* XXX optimize statically? */
break;
case ICMD_ACONST:
if (iptr->val.a == NULL)
- TYPEINFO_INIT_NULLTYPE(dst->typeinfo)
+ TYPEINFO_INIT_NULLTYPE(dst->typeinfo);
else
/* XXX constants for builtin functions */
/* string constants */
if (!typeinfo_is_assignable(&curstack->typeinfo,&tempinfo))
panic("illegal instruction: ATHROW on non-Throwable");
superblockend = true;
+ maythrow = true;
break;
case ICMD_ARETURN:
if (returntype != TYPE_ADDRESS
|| !typeinfo_is_assignable(&curstack->typeinfo,&returntypeinfo))
panic("Return type mismatch");
-
- superblockend = true;
- break;
-
+ goto return_tail;
+
case ICMD_IRETURN:
- if (returntype != TYPE_INT)
- panic("Return type mismatch");
- superblockend = true;
- break;
+ if (returntype != TYPE_INT) panic("Return type mismatch");
+ goto return_tail;
+
case ICMD_LRETURN:
- if (returntype != TYPE_LONG)
- panic("Return type mismatch");
- superblockend = true;
- break;
+ if (returntype != TYPE_LONG) panic("Return type mismatch");
+ goto return_tail;
+
case ICMD_FRETURN:
- if (returntype != TYPE_FLOAT)
- panic("Return type mismatch");
- superblockend = true;
- break;
+ if (returntype != TYPE_FLOAT) panic("Return type mismatch");
+ goto return_tail;
+
case ICMD_DRETURN:
- if (returntype != TYPE_DOUBLE)
- panic("Return type mismatch");
- superblockend = true;
- break;
+ if (returntype != TYPE_DOUBLE) panic("Return type mismatch");
+ goto return_tail;
+
case ICMD_RETURN:
- if (returntype != TYPE_VOID)
- panic("Return type mismatch");
+ if (returntype != TYPE_VOID) panic("Return type mismatch");
+ return_tail:
+ TYPECHECK_LEAVE;
superblockend = true;
+ maythrow = true;
break;
/****************************************/
*/
if (jsrtemp->sbr_touched) {
/* Calculate the local variables after the subroutine call */
- for (i=0; i<maxlocals; ++i)
+ for (i=0; i<numlocals; ++i)
if (jsrtemp->sbr_touched[i] != TOUCHED_NO) {
TOUCH_VARIABLE(i);
if ((vtype[i] = jsrtemp->sbr_vtype[i]) == TYPE_ADR)
repeat = true;
superblockend = true;
}
+ /* XXX may throw? I don't think. */
break;
case ICMD_RET:
/* check returnAddress variable */
- CHECKVARTYPE(iptr->op1,TYPE_ADR);
+ CHECK_ONEWORD(iptr->op1,TYPE_ADR);
if (!TYPEINFO_IS_PRIMITIVE(vinfo[iptr->op1]))
panic("illegal instruction: RET using non-returnAddress variable");
/* determine which variables are touched by this subroutine */
/* and their types */
if (subroutine->sbr_touched) {
- for (i=0; i<maxlocals; ++i)
+ for (i=0; i<numlocals; ++i)
subroutine->sbr_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; i<maxlocals; ++i)
+ subroutine->sbr_touched = DMNEW(u1,numlocals);
+ memcpy(subroutine->sbr_touched,touched,sizeof(u1)*numlocals);
+ subroutine->sbr_vtype = DMNEW(u1,numlocals);
+ memcpy(subroutine->sbr_vtype,vtype,sizeof(u1)*numlocals);
+ subroutine->sbr_vinfo = DMNEW(typeinfo,numlocals);
+ for (i=0; i<numlocals; ++i)
if (vtype[i] == TYPE_ADR)
TYPEINFO_CLONE(vinfo[i],subroutine->sbr_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));
+ DOLOG(typeinfo_print_locals(get_logfile(),subroutine->sbr_vtype,subroutine->sbr_vinfo,
+ subroutine->sbr_touched,numlocals));
LOGNL; LOGFLUSH;
/* reach blocks after JSR statements */
for (i=0; i<block_count; ++i) {
tbptr = block + i;
+ LOG1("block L%03d",tbptr->debug_nr);
if (tbptr->iinstr[tbptr->icount - 1].opc != ICMD_JSR)
continue;
- if ((basicblock*) tbptr->iinstr[tbptr->icount - 1].target != jsrold->target)
+ LOG("ends with JSR");
+ if ((basicblock*) tbptr->iinstr[tbptr->icount - 1].target != subroutine->target)
continue;
tbptr++;
case ICMD_INVOKESTATIC:
case ICMD_INVOKEINTERFACE:
{
+ /* XXX check if this opcode may invoke this method */
/* XXX check access rights */
methodinfo *mi = (methodinfo*) iptr->val.a;
+ bool callinginit = (opcode == ICMD_INVOKESPECIAL && mi->name == name_init);
+ instruction *ins;
+ classinfo *initclass;
+
+ /* XXX for INVOKESPECIAL: check if the invokation is done at all */
+
+ /* fetch parameter types and return type */
/* XXX might use dst->typeinfo directly if non void */
- typeinfo_init_from_method_args(mi->descriptor,ptype,pinfo,MAXPARAMS,false,
+ i = 0;
+ if (opcode != ICMD_INVOKESTATIC) {
+ ptype[0] = TYPE_ADR;
+ TYPEINFO_INIT_CLASSINFO(pinfo[0],mi->class);
+ i++;
+ }
+ typeinfo_init_from_method_args(mi->descriptor,ptype+i,pinfo+i,
+ MAXPARAMS-i,false,
&rtype,&rinfo);
- /* XXX compare rtype and dst->type? */
+ /* check parameter types */
+ srcstack = curstack;
+ i = mi->paramcount; /* number of parameters including 'this'*/
+ while (--i >= 0) {
+ LOG1("param %d",i);
+ if (srcstack->type != ptype[i])
+ panic("Parameter type mismatch in method invocation");
+ if (srcstack->type == TYPE_ADR) {
+ LOGINFO(&(srcstack->typeinfo));
+ LOGINFO(pinfo + i);
+ if (i==0 && callinginit)
+ {
+ /* first argument to <init> method */
+ if (!TYPEINFO_IS_NEWOBJECT(srcstack->typeinfo))
+ panic("Calling <init> on initialized object");
+
+ /* get the address of the NEW instruction */
+ LOGINFO(&(srcstack->typeinfo));
+ ins = (instruction*)TYPEINFO_NEWOBJECT_INSTRUCTION(srcstack->typeinfo);
+ initclass = (ins) ? (classinfo*)ins[-1].val.a : method->class;
+ LOGSTR("class: "); LOGSTRu(initclass->name); LOGNL;
+
+ /* XXX check type */
+ }
+ else {
+ if (!typeinfo_is_assignable(&(srcstack->typeinfo),pinfo+i))
+ panic("Parameter reference type mismatch in method invocation");
+ }
+ }
+ LOG("ok");
+
+ srcstack = srcstack->prev;
+ }
+
+ LOG("checking return type");
if (rtype != TYPE_VOID) {
+ if (rtype != dst->type)
+ panic("Return type mismatch in method invocation");
TYPEINFO_COPY(rinfo,dst->typeinfo);
}
+
+ if (callinginit) {
+ LOG("replacing uninitialized object");
+ /* replace uninitialized object type on stack */
+ srcstack = dst;
+ while (srcstack) {
+ if (srcstack->type == TYPE_ADR
+ && TYPEINFO_IS_NEWOBJECT(srcstack->typeinfo)
+ && TYPEINFO_NEWOBJECT_INSTRUCTION(srcstack->typeinfo) == ins)
+ {
+ LOG("replacing uninitialized type on stack");
+ TYPEINFO_INIT_CLASSINFO(srcstack->typeinfo,initclass);
+ }
+ srcstack = srcstack->prev;
+ }
+ /* replace uninitialized object type in locals */
+ for (i=0; i<numlocals; ++i) {
+ if (vtype[i] == TYPE_ADR
+ && TYPEINFO_IS_NEWOBJECT(vinfo[i])
+ && TYPEINFO_NEWOBJECT_INSTRUCTION(vinfo[i]) == ins)
+ {
+ LOG1("replacing uninitialized type in local %d",i);
+ TYPEINFO_INIT_CLASSINFO(vinfo[i],initclass);
+ }
+ }
+
+ /* initializing the 'this' reference? */
+ if (initmethod && !ins) {
+ /* set our marker variable to type int */
+ LOG("setting <init> marker");
+ vtype[numlocals-1] = TYPE_INT;
+ }
+ }
}
+ 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);
+ {
+ vftbl *arrayvftbl;
+ arraydescriptor *desc;
+
+ /* 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;
+ }
+
+ /* check array descriptor */
+ arrayvftbl = (vftbl*) iptr[0].val.a;
+ if (!arrayvftbl)
+ panic("MULTIANEWARRAY with unlinked class");
+ if ((desc = arrayvftbl->arraydesc) == NULL)
+ panic("MULTIANEWARRAY with non-array class");
+ if (desc->dimension < iptr[0].op1)
+ panic("MULTIANEWARRAY dimension to high");
+
+ /* set the array type of the result */
+ TYPEINFO_INIT_CLASSINFO(dst->typeinfo,arrayvftbl->class);
+ }
+ maythrow = true;
break;
case ICMD_BUILTIN3:
- if (ISBUILTIN(asm_builtin_aastore)) {
- /* XXX also handled by ICMD_AASTORE */
+ if (ISBUILTIN(BUILTIN_aastore)) {
+ TYPECHECK_ADR(curstack);
+ TYPECHECK_INT(curstack->prev);
+ TYPECHECK_ADR(curstack->prev->prev);
if (!TYPEINFO_MAYBE_ARRAY_OF_REFS(curstack->prev->prev->typeinfo))
panic("illegal instruction: AASTORE to non-reference array");
panic("illegal instruction: AASTORE to incompatible type");
*/
}
- /* XXX check for missed builtins in debug mode? */
+ else {
+ builtindesc = builtin_desc;
+ while (builtindesc->opcode && builtindesc->builtin
+ != (functionptr) iptr->val.a) builtindesc++;
+ if (!builtindesc->opcode) {
+ dolog("Builtin not in table: %s",icmd_builtin_name((functionptr) iptr->val.a));
+ panic("Internal error: builtin not found in table");
+ }
+ TYPECHECK_ARGS3(builtindesc->type_s3,builtindesc->type_s2,builtindesc->type_s1);
+ }
+ maythrow = true; /* XXX better safe than sorry */
break;
case ICMD_BUILTIN2:
- if (
-#if defined(__I386__)
- ISBUILTIN(asm_builtin_newarray)
-#else
- ISBUILTIN(builtin_newarray)
-#endif
- )
+ /* XXX use BUILTIN_ macros */
+ if (ISBUILTIN(BUILTIN_newarray))
{
+ vftbl *vft;
+ TYPECHECK_INT(curstack->prev);
if (iptr[-1].opc != ICMD_ACONST)
panic("illegal instruction: builtin_newarray without classinfo");
- TYPEINFO_INIT_CLASSINFO(dst->typeinfo,((vftbl *)iptr[-1].val.a)->class);
+ vft = (vftbl *)iptr[-1].val.a;
+ if (!vft)
+ panic("ANEWARRAY with unlinked class");
+ if (!vft->arraydesc)
+ panic("ANEWARRAY with non-array class");
+ TYPEINFO_INIT_CLASSINFO(dst->typeinfo,vft->class);
}
- else if (ISBUILTIN(asm_builtin_checkarraycast)) {
+ else if (ISBUILTIN(BUILTIN_arrayinstanceof))
+ {
+ vftbl *vft;
+ TYPECHECK_ADR(curstack->prev);
+ if (iptr[-1].opc != ICMD_ACONST)
+ panic("illegal instruction: builtin_arrayinstanceof without classinfo");
+ vft = (vftbl *)iptr[-1].val.a;
+ if (!vft)
+ panic("INSTANCEOF with unlinked class");
+ if (!vft->arraydesc)
+ panic("internal error: builtin_arrayinstanceof with non-array class");
+ }
+ else if (ISBUILTIN(BUILTIN_checkarraycast)) {
+ vftbl *vft;
+ TYPECHECK_ADR(curstack->prev);
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);
+ panic("illegal instruction: BUILTIN_checkarraycast without classinfo");
+ vft = (vftbl *)iptr[-1].val.a;
+ if (!vft)
+ panic("CHECKCAST with unlinked class");
+ if (!vft->arraydesc)
+ panic("internal error: builtin_checkarraycast with non-array class");
+ TYPEINFO_INIT_CLASSINFO(dst->typeinfo,vft->class);
}
- /* XXX check for missed builtins in debug mode? */
+ else {
+ builtindesc = builtin_desc;
+ while (builtindesc->opcode && builtindesc->builtin
+ != (functionptr) iptr->val.a) builtindesc++;
+ if (!builtindesc->opcode) {
+ dolog("Builtin not in table: %s",icmd_builtin_name((functionptr) iptr->val.a));
+ panic("Internal error: builtin not found in table");
+ }
+ TYPECHECK_ARGS2(builtindesc->type_s2,builtindesc->type_s1);
+ }
+ maythrow = true; /* XXX better safe than sorry */
break;
case ICMD_BUILTIN1:
- if (ISBUILTIN(builtin_new)) {
+ 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);
+ TYPEINFO_INIT_NEWOBJECT(dst->typeinfo,iptr);
}
- else if (ISBUILTIN(builtin_newarray_boolean)) {
+ /* XXX unify the following cases */
+ else if (ISBUILTIN(BUILTIN_newarray_boolean)) {
+ TYPECHECK_INT(curstack);
TYPEINFO_INIT_PRIMITIVE_ARRAY(dst->typeinfo,ARRAYTYPE_BOOLEAN);
}
- else if (ISBUILTIN(builtin_newarray_char)) {
+ else if (ISBUILTIN(BUILTIN_newarray_char)) {
+ TYPECHECK_INT(curstack);
TYPEINFO_INIT_PRIMITIVE_ARRAY(dst->typeinfo,ARRAYTYPE_CHAR);
}
- else if (ISBUILTIN(builtin_newarray_float)) {
+ else if (ISBUILTIN(BUILTIN_newarray_float)) {
+ TYPECHECK_INT(curstack);
TYPEINFO_INIT_PRIMITIVE_ARRAY(dst->typeinfo,ARRAYTYPE_FLOAT);
}
- else if (ISBUILTIN(builtin_newarray_double)) {
+ else if (ISBUILTIN(BUILTIN_newarray_double)) {
+ TYPECHECK_INT(curstack);
TYPEINFO_INIT_PRIMITIVE_ARRAY(dst->typeinfo,ARRAYTYPE_DOUBLE);
}
- else if (ISBUILTIN(builtin_newarray_byte)) {
+ else if (ISBUILTIN(BUILTIN_newarray_byte)) {
+ TYPECHECK_INT(curstack);
TYPEINFO_INIT_PRIMITIVE_ARRAY(dst->typeinfo,ARRAYTYPE_BYTE);
}
- else if (ISBUILTIN(builtin_newarray_short)) {
+ else if (ISBUILTIN(BUILTIN_newarray_short)) {
+ TYPECHECK_INT(curstack);
TYPEINFO_INIT_PRIMITIVE_ARRAY(dst->typeinfo,ARRAYTYPE_SHORT);
}
- else if (ISBUILTIN(builtin_newarray_int)) {
+ else if (ISBUILTIN(BUILTIN_newarray_int)) {
+ TYPECHECK_INT(curstack);
TYPEINFO_INIT_PRIMITIVE_ARRAY(dst->typeinfo,ARRAYTYPE_INT);
}
- else if (ISBUILTIN(builtin_newarray_long)) {
+ else if (ISBUILTIN(BUILTIN_newarray_long)) {
+ TYPECHECK_INT(curstack);
TYPEINFO_INIT_PRIMITIVE_ARRAY(dst->typeinfo,ARRAYTYPE_LONG);
}
- /* XXX check for missed builtins in debug mode? */
+ else {
+ builtindesc = builtin_desc;
+ while (builtindesc->opcode && builtindesc->builtin
+ != (functionptr) iptr->val.a) builtindesc++;
+ if (!builtindesc->opcode) {
+ dolog("Builtin not in table: %s",icmd_builtin_name((functionptr) iptr->val.a));
+ panic("Internal error: builtin not found in table");
+ }
+ TYPECHECK_ARGS1(builtindesc->type_s1);
+ }
+ 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_ILOAD: CHECK_ONEWORD(iptr->op1,TYPE_INT); break;
+ case ICMD_FLOAD: CHECK_ONEWORD(iptr->op1,TYPE_FLOAT); break;
+ case ICMD_IINC: CHECK_ONEWORD(iptr->op1,TYPE_INT); break;
+ case ICMD_LLOAD: CHECK_TWOWORD(iptr->op1,TYPE_LONG); break;
+ case ICMD_DLOAD: CHECK_TWOWORD(iptr->op1,TYPE_DOUBLE); break;
- case ICMD_FSTORE: STORE_PRIMITIVE(iptr->op1,TYPE_FLOAT); break;
- case ICMD_ISTORE: STORE_PRIMITIVE(iptr->op1,TYPE_INT); break;
+ case ICMD_FSTORE: STORE_ONEWORD(iptr->op1,TYPE_FLOAT); break;
+ case ICMD_ISTORE: STORE_ONEWORD(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;
case ICMD_ANEWARRAY:
case ICMD_MONITORENTER:
case ICMD_MONITOREXIT:
+ case ICMD_AASTORE:
/* XXX only check this in debug mode? */
- LOGSTR2("ICMD %d at %d\n", iptr->opc, (int)(iptr-instr));
+ LOG2("ICMD %d at %d\n", iptr->opc, (int)(iptr-instr));
+ LOG("Should have been converted to builtin function call.");
panic("Internal error: unexpected instruction encountered");
break;
/* XXX only add cases for them in debug mode? */
case ICMD_NOP:
- case ICMD_CHECKASIZE: /* XXX ? */
- case ICMD_NULLCHECKPOP: /* XXX ? */
+ case ICMD_POP:
+ case ICMD_POP2:
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.) */
- /* These instructions are typechecked in analyse_stack. */
- /* XXX only add cases for them in debug mode? */
+ /* The following instructions may throw a runtime exception: */
+
+ case ICMD_IDIV:
+ case ICMD_IREM:
+ case ICMD_LDIV:
+ case ICMD_LREM:
+
+ maythrow = true;
+ break;
+
+ /* The following instructions never throw a runtime exception: */
+ /* XXX only add cases for them in debug mode? */
+
+ 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:
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_LADD:
case ICMD_LSUB:
case ICMD_LMUL:
- case ICMD_LDIV:
- case ICMD_LREM:
case ICMD_LNEG:
case ICMD_LAND:
case ICMD_LOR:
case ICMD_LUSHRCONST:
case ICMD_LREMPOW2:
+ 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:
+
case ICMD_LCMP:
case ICMD_LCMPCONST:
case ICMD_FCMPL:
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:
-
- 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));
+ LOG2("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;
+ TYPECHECK_REACH(REACH_THROW); /* XXX jsr chain? */
+ i++;
+ }
+ }
+
+ /*
+ DOLOG(typeinfo_print_block(get_logfile(),curstack,numlocals,vtype,vinfo,(jsrchain) ? touched : NULL));
+ LOGNL; LOGFLUSH;
+ */
+
iptr++;
} /* while instructions */
LOG("instructions done");
LOGSTR("RESULT=> ");
- DOLOG(typeinfo_print_block(stdout,curstack,vtype,vinfo,(jsrchain) ? touched : NULL));
+ DOLOG(typeinfo_print_block(get_logfile(),curstack,numlocals,vtype,vinfo,(jsrchain) ? touched : NULL));
LOGNL; LOGFLUSH;
/* propagate stack and variables to the following block */
bptr++;
} /* while blocks */
- /* the following iterations only check if any types changed */
- fulltypecheck = false; /* XXX */
-
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 */
+#ifdef TYPECHECK_STATISTICS
+ dolog("Typechecker did %4d iterations",count_iterations);
+#endif
+#ifdef TYPECHECK_DEBUG
+ for (i=0; i<block_count; ++i) {
+ if (block[i].flags != BBDELETED
+ && block[i].flags != BBUNDEF
+ && block[i].flags != BBFINISHED
+ && block[i].flags != BBTYPECHECK_UNDEF) /* typecheck may never reach
+ * some exception handlers,
+ * that's ok. */
+ {
+ LOG2("block L%03d has invalid flags after typecheck: %d",
+ block[i].debug_nr,block[i].flags);
+ panic("Invalid block flags after typecheck");
+ }
+ }
+#endif
+
+ /* Reset blocks we never reached */
+ for (i=0; i<block_count; ++i) {
+ if (block[i].flags == BBTYPECHECK_UNDEF)
+ block[i].flags = BBFINISHED;
+ }
+
LOGimp("exiting typecheck");
}
#undef COPYTYPE
#endif /* CACAO_TYPECHECK */
+
+/*
+ * These are local overrides for various environment variables in Emacs.
+ * Please do not remove this and leave it at the end of the file, where
+ * Emacs will automagically detect them.
+ * ---------------------------------------------------------------------
+ * Local variables:
+ * mode: c
+ * indent-tabs-mode: t
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ */