Authors: Edwin Steiner
- $Id: typecheck.c 869 2004-01-10 21:30:06Z edwin $
+ $Id: typecheck.c 974 2004-03-25 17:31:13Z jowenn $
*/
#endif
+/****************************************************************************/
+/* STATISTICS */
+/****************************************************************************/
+
+#ifdef TYPECHECK_DEBUG
+/*#define TYPECHECK_STATISTICS*/
+#endif
+
+#ifdef TYPECHECK_STATISTICS
+#define STAT_ITERATIONS 10
+#define STAT_BLOCKS 10
+#define STAT_LOCALS 16
+
+static int stat_typechecked = 0;
+static int stat_typechecked_jsr = 0;
+static int stat_iterations[STAT_ITERATIONS+1] = { 0 };
+static int stat_reached = 0;
+static int stat_copied = 0;
+static int stat_merged = 0;
+static int stat_merging_changed = 0;
+static int stat_backwards = 0;
+static int stat_blocks[STAT_BLOCKS+1] = { 0 };
+static int stat_locals[STAT_LOCALS+1] = { 0 };
+static int stat_ins = 0;
+static int stat_ins_field = 0;
+static int stat_ins_invoke = 0;
+static int stat_ins_primload = 0;
+static int stat_ins_aload = 0;
+static int stat_ins_builtin = 0;
+static int stat_ins_builtin_gen = 0;
+static int stat_ins_branch = 0;
+static int stat_ins_switch = 0;
+static int stat_ins_unchecked = 0;
+static int stat_handlers_reached = 0;
+static int stat_savedstack = 0;
+
+#define TYPECHECK_COUNT(cnt) (cnt)++
+#define TYPECHECK_COUNTIF(cond,cnt) do{if(cond) (cnt)++;} while(0)
+#define TYPECHECK_COUNT_FREQ(array,val,limit) \
+ do { \
+ if ((val) < (limit)) (array)[val]++; \
+ else (array)[limit]++; \
+ } while (0)
+
+static void print_freq(FILE *file,int *array,int limit)
+{
+ int i;
+ for (i=0; i<limit; ++i)
+ fprintf(file," %3d: %8d\n",i,array[i]);
+ fprintf(file," =>%3d: %8d\n",limit,array[limit]);
+}
+
+void typecheck_print_statistics(FILE *file) {
+ fprintf(file,"typechecked methods: %8d\n",stat_typechecked);
+ fprintf(file,"methods with JSR : %8d\n",stat_typechecked_jsr);
+ fprintf(file,"reached blocks : %8d\n",stat_reached);
+ fprintf(file,"copied states : %8d\n",stat_copied);
+ fprintf(file,"merged states : %8d\n",stat_merged);
+ fprintf(file,"merging changed : %8d\n",stat_merging_changed);
+ fprintf(file,"backwards branches : %8d\n",stat_backwards);
+ fprintf(file,"handlers reached : %8d\n",stat_handlers_reached);
+ fprintf(file,"saved stack (times): %8d\n",stat_savedstack);
+ fprintf(file,"instructions : %8d\n",stat_ins);
+ fprintf(file," field access : %8d\n",stat_ins_field);
+ fprintf(file," invocations : %8d\n",stat_ins_invoke);
+ fprintf(file," load primitive : %8d\n",stat_ins_primload);
+ fprintf(file," load address : %8d\n",stat_ins_aload);
+ fprintf(file," builtins : %8d\n",stat_ins_builtin);
+ fprintf(file," generic : %8d\n",stat_ins_builtin_gen);
+ fprintf(file," unchecked : %8d\n",stat_ins_unchecked);
+ fprintf(file," branches : %8d\n",stat_ins_branch);
+ fprintf(file," switches : %8d\n",stat_ins_switch);
+ fprintf(file,"iterations used:\n");
+ print_freq(file,stat_iterations,STAT_ITERATIONS);
+ fprintf(file,"basic blocks per method / 10:\n");
+ print_freq(file,stat_blocks,STAT_BLOCKS);
+ fprintf(file,"locals:\n");
+ print_freq(file,stat_locals,STAT_LOCALS);
+}
+
+#else
+
+#define TYPECHECK_COUNT(cnt)
+#define TYPECHECK_COUNTIF(cond,cnt)
+#define TYPECHECK_COUNT_FREQ(array,val,limit)
+#endif
+
/****************************************************************************/
/* TYPESTACK FUNCTIONS */
/****************************************************************************/
#define RETURNADDRESSSET_SEEK(set,pos) \
do {int i; for (i=pos;i--;) set=set->alt;} while(0)
+#define TYPESTACK_COPY(sp,copy) \
+ do {for(; sp; sp=sp->prev, copy=copy->prev) { \
+ copy->type = sp->type; \
+ TYPEINFO_COPY(sp->typeinfo,copy->typeinfo); \
+ }} while (0) \
+
static void
typestack_copy(stackptr dst,stackptr y,typevector *selected)
{
}
}
-/* XXX */
-static bool
-typestack_canmerge(stackptr a,stackptr b)
-{
- for (; a; a = a->prev, b = b->prev) {
- if (!b) return false;
- if (a->type != b->type) return false;
- if (a->type == TYPE_ADDRESS) {
- if (((TYPEINFO_IS_PRIMITIVE(a->typeinfo)) ? 1 : 0)
- ^
- ((TYPEINFO_IS_PRIMITIVE(b->typeinfo)) ? 1 : 0))
- return false;
- }
- }
- if (b) return false;
- return true;
-}
-
static void
typestack_collapse(stackptr dst)
{
}
}
-/* 'dst' and 'y' are assumed to have passed typestack_canmerge! */
static bool
typestack_merge(stackptr dst,stackptr y)
{
bool changed = false;
for (; dst; dst = dst->prev, y=y->prev) {
- if (TYPESTACK_IS_REFERENCE(dst))
- changed |= typeinfo_merge(&(dst->typeinfo),&(y->typeinfo));
+ if (!y) panic("Stack depth mismatch");
+ if (dst->type != y->type) panic("Stack type mismatch");
+ if (dst->type == TYPE_ADDRESS) {
+ if (TYPEINFO_IS_PRIMITIVE(dst->typeinfo)) {
+ /* dst has returnAddress type */
+ if (!TYPEINFO_IS_PRIMITIVE(y->typeinfo))
+ panic("Merging returnAddress with reference");
+ }
+ else {
+ /* dst has reference type */
+ if (TYPEINFO_IS_PRIMITIVE(y->typeinfo))
+ panic("Merging reference with returnAddress");
+ changed |= typeinfo_merge(&(dst->typeinfo),&(y->typeinfo));
+ }
+ }
}
+ if (y) panic("Stack depth mismatch");
return changed;
}
static bool
typestate_merge(stackptr deststack,typevector *destloc,
stackptr ystack,typevector *yloc,
- int locsize)
+ int locsize,bool jsrencountered)
{
typevector *dvec,*yvec;
int kd,ky;
LOGSTR("yloc : "); DOLOG(typevectorset_print(get_logfile(),yloc,locsize)); LOGNL;
LOGFLUSH;
- /* Check if the stack types of deststack and ystack match */
-
- /* XXX move this check into typestack_merge? */
- if (!typestack_canmerge(deststack,ystack))
- panic("Stack depth or stack type mismatch");
-
/* The stack is always merged. If there are returnAddresses on
* the stack they are ignored in this step. */
changed |= typestack_merge(deststack,ystack);
+ if (!jsrencountered)
+ return typevector_merge(destloc,yloc,locsize);
+
for (yvec=yloc; yvec; yvec=yvec->alt) {
ky = yvec->k;
}
merged:
-
+ ;
}
LOG("result:");
basicblock *current,
basicblock *destblock,
stackptr ystack,typevector *yloc,
- int locsize)
+ int locsize,bool jsrencountered)
{
typevector *destloc;
int destidx;
bool changed = false;
LOG1("reaching block L%03d",destblock->debug_nr);
+ TYPECHECK_COUNT(stat_reached);
destidx = destblock - block;
destloc = MGET_TYPEVECTOR(localbuf,destidx,locsize);
stackptr sp;
int i;
+ TYPECHECK_COUNT(stat_backwards);
LOG("BACKWARDS!");
for (sp = ystack; sp; sp=sp->prev)
if (sp->type == TYPE_ADR &&
if (destblock->flags == BBTYPECHECK_UNDEF) {
/* The destblock has never been reached before */
+ TYPECHECK_COUNT(stat_copied);
LOG1("block (index %04d) reached first time",destidx);
typestack_copy(destblock->instack,ystack,yloc);
else {
/* The destblock has already been reached before */
+ TYPECHECK_COUNT(stat_merged);
LOG1("block (index %04d) reached before",destidx);
changed = typestate_merge(destblock->instack,destloc,
- ystack,yloc,locsize);
+ ystack,yloc,locsize,
+ jsrencountered);
+ TYPECHECK_COUNTIF(changed,stat_merging_changed);
}
if (changed) {
selected = typevectorset_select(&yvec,retindex,destblock);
repeat |= typestate_reach(localbuf,current,destblock,
- ystack,selected,locsize);
+ ystack,selected,locsize,true);
}
return repeat;
}
-static bool
-typestate_jsr(void *localbuf,
- basicblock *current,basicblock *destblock,
- stackptr ystack,typevector *yloc,
- int locsize)
-{
- typestack_put_retaddr(ystack,current+1,yloc);
- return typestate_reach(localbuf,current,destblock,ystack,yloc,locsize);
-}
-
/****************************************************************************/
/* HELPER FUNCTIONS */
/****************************************************************************/
/* If a field is checked, definingclass == implementingclass */
static bool
-is_accessible(int flags,classinfo *definingclass,classinfo *implementingclass,
+is_accessible(int flags,classinfo *definingclass,classinfo *implementingclass, classinfo *methodclass,
typeinfo *instance)
{
/* check access rights */
- if (class != definingclass) {
+ if (methodclass != definingclass) {
switch (flags & (ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED)) {
case ACC_PUBLIC:
break;
/* In the cases below, definingclass cannot be an interface */
case 0:
- /* XXX check package access */
+ if (definingclass->packagename != methodclass->packagename)
+ return false;
break;
case ACC_PROTECTED:
- /* XXX check package access and superclass access */
+ if (definingclass->packagename != methodclass->packagename) {
+ if (!builtin_isanysubclass(methodclass,implementingclass))
+ return false;
+
+ /* For protected access of super class members in another
+ * package the instance must be a subclass of or the same
+ * as the current class. */
+ LOG("protected access into other package");
+ implementingclass = methodclass;
+ }
break;
case ACC_PRIVATE:
- if (definingclass != class) {
+ if (definingclass != methodclass) {
LOG("private access");
return false;
}
break;
- /* XXX check package access */
default:
panic("Invalid access flags");
}
&& !TYPEINFO_IS_NULLTYPE(*instance)
&& !TYPEINFO_IS_NEWOBJECT(*instance))
{
- typeinfo tempinfo;
-
- if (((flags & ACC_PROTECTED) != 0)
- && builtin_isanysubclass(class,implementingclass))
+ if (!typeinfo_is_assignable_to_classinfo(instance,
+ implementingclass))
{
- /* For protected access of super class members
- * the instance must be a subclass of or the same
- * as the current class. */
-
- /* XXX maybe we only are allowed to do this, if we
- * don't have package access? */
- /* implementingclass = class; */ /* XXX does not work */
- }
-
- /* XXX use classinfo directly? */
- TYPEINFO_INIT_CLASSINFO(tempinfo,implementingclass);
- if (!typeinfo_is_assignable(instance,&tempinfo)) {
LOG("instance not assignable");
LOGINFO(instance);
- LOGINFO(&tempinfo);
+ LOGSTRu(implementingclass->name); LOGNL; LOGFLUSH;
return false;
}
}
}
/****************************************************************************/
-/* 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() */
+/* MACROS FOR LOCAL VARIABLE CHECKING */
/****************************************************************************/
#define INDEX_ONEWORD(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); \
- typevectorset_store(localset,num,type,NULL);} while(0)
-
-#define STORE_TWOWORD(num,type) \
- do {INDEX_TWOWORD(num); \
- typevectorset_store_twoword(localset,num,type);} while(0)
-
-#define CHECK_ONEWORD(num,type) \
- do {INDEX_ONEWORD(num); \
- if (!typevectorset_checktype(localset,num,type)) \
- panic("Variable type mismatch"); \
+ do {typevectorset_store(localset,num,type,NULL);} while(0)
+
+#define STORE_TWOWORD(num,type) \
+ do {typevectorset_store_twoword(localset,num,type);} while(0)
+
+#define CHECK_ONEWORD(num,tp) \
+ do {TYPECHECK_COUNT(stat_ins_primload); \
+ if (jsrencountered) { \
+ if (!typevectorset_checktype(localset,num,tp)) \
+ panic("Variable type mismatch"); \
+ } \
+ else { \
+ if (localset->td[num].type != tp) \
+ panic("Variable type mismatch"); \
+ } \
} while(0)
#define CHECK_TWOWORD(num,type) \
- do {INDEX_TWOWORD(num); \
+ do {TYPECHECK_COUNT(stat_ins_primload); \
if (!typevectorset_checktype(localset,num,type)) \
panic("Variable type mismatch"); \
} while(0)
-/* XXX maybe it's faster to copy always */
-#define COPYTYPE(source,dest) \
- {if ((source)->type == TYPE_ADR) \
- TYPEINFO_COPY((source)->typeinfo,(dest)->typeinfo);}
-
-#define ISBUILTIN(v) (iptr->val.a == (functionptr)(v))
+/****************************************************************************/
+/* MACROS FOR STACK TYPE CHECKING */
+/****************************************************************************/
-/* Macros for basic typechecks which were not done in stack.c */
+/* These macros are for basic typechecks which were not done in stack.c */
#define TYPECHECK_STACK(sp,tp) \
do { if ((sp)->type != (tp)) \
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:
- * vtype......current local variable types
- * vinfo......current local variable typeinfos
- * ttype......local variable types of target block
- * tinfo......local variable typeinfos of target block
- * numlocals..number of local variables
- * Used:
- * macro_i
- */
-#define TYPECHECK_COPYVARS \
- do { \
- LOG("TYPECHECK_COPYVARS"); \
- 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.
- * 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
- * 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<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; \
- changed = true; \
- } else if (ttype[macro_i] == TYPE_ADR) { \
- if ( ((TYPEINFO_IS_PRIMITIVE(tinfo[macro_i])) ? 1 : 0) \
- ^ \
- ((TYPEINFO_IS_PRIMITIVE(vinfo[macro_i])) ? 1 : 0)) { \
- LOG1("var %d: primitive + reference merge",macro_i); \
- ttype[macro_i] = TYPE_VOID; \
- changed = true; \
- } \
- else { \
- LOG1("var %d:",macro_i); \
- LOGINFO(tinfo+macro_i); \
- LOGINFO(vinfo+macro_i); \
- changed |= typeinfo_merge(tinfo+macro_i,vinfo+macro_i); \
- LOGINFO(tinfo+macro_i); \
- LOGIF(changed,"vars have changed"); \
- } \
- }; \
- } } 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 \
- do { \
- 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"); \
- } while (0)
+/****************************************************************************/
+/* MISC MACROS */
+/****************************************************************************/
-/* 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 \
- do { \
- 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"); \
- } 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
- * 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 (localset->td[macro_i].type == TYPE_ADR && \
- TYPEINFO_IS_NEWOBJECT(localset->td[macro_i].info)) \
- panic("Branching backwards with uninitialized object in local variable"); \
- } while(0)
+#define COPYTYPE(source,dest) \
+ {if ((source)->type == TYPE_ADR) \
+ TYPEINFO_COPY((source)->typeinfo,(dest)->typeinfo);}
+
+#define ISBUILTIN(v) (iptr->val.a == (functionptr)(v))
-/* XXX convert this into a function */
/* 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.
* tbptr......target block
* dst........current output stack pointer
* numlocals..number of local variables
+ * localset...current local variable vectorset
+ * localbuf...local variable vectorset buffer
+ * jsrencountered...true if a JSR has been seen
* 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 \
do { \
repeat |= typestate_reach(localbuf,bptr,tbptr,dst, \
- localset,numlocals); \
+ localset,numlocals,jsrencountered); \
LOG("done."); \
} while (0)
* Input:
* class........class of the current method
* numlocals....number of local variables
- * vtype........current local variable types
- * vinfo........current local variable typeinfos
+ * localset.....current local variable vectorset
* initmethod...true if this is an <init> method
*/
#define TYPECHECK_LEAVE \
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, i; /* temporary counters */
+ int i; /* temporary counter */
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; /* used in macros */
instruction *iptr; /* pointer to current instruction */
basicblock *bptr; /* pointer to current basic block */
basicblock *tbptr; /* temporary for target block */
typevector *lset; /* temporary pointer */
typedescriptor *td; /* temporary pointer */
- typeinfo tempinfo; /* temporary */
+ stackptr savedstackbuf = NULL; /* buffer for saving the stack */
+ stackptr savedstack = NULL; /* saved instack of current block */
+
+ stackelement excstack; /* instack for exception handlers */
+
typedescriptor returntype; /* return type of current method */
u1 *ptype; /* parameter types of called method */
typeinfo *pinfo; /* parameter typeinfos of called method */
xtable **handlers; /* active exception handlers */
classinfo *cls; /* temporary */
bool maythrow; /* true if this instruction may throw */
- utf *name_init; /* "<init>" */
+ static utf *name_init; /* "<init>" */
bool initmethod; /* true if this is an "<init>" method */
- builtin_descriptor *builtindesc;
+ builtin_descriptor *builtindesc; /* temp. descriptor of builtin */
+ bool jsrencountered = false; /* true if we there was a JSR */
+
+ classinfo *myclass;
#ifdef TYPECHECK_STATISTICS
int count_iterations = 0;
+ TYPECHECK_COUNT(stat_typechecked);
+ TYPECHECK_COUNT_FREQ(stat_locals,maxlocals,STAT_LOCALS);
+ TYPECHECK_COUNT_FREQ(stat_blocks,block_count/10,STAT_BLOCKS);
#endif
LOGSTR("\n==============================================================================\n");
LOGimpSTR(")\n");
LOGFLUSH;
- name_init = utf_new_char("<init>");
+ if (!name_init)
+ name_init = utf_new_char("<init>");
initmethod = (method->name == name_init);
/* Allocate buffer for method arguments */
/* allocate the buffers for local variables */
localbuf = DMNEW_TYPEVECTOR(block_count+1, numlocals);
localset = MGET_TYPEVECTOR(localbuf,block_count,numlocals);
- memset(localbuf,0,(block_count+1) * TYPEVECTOR_SIZE(numlocals));
LOG("Variable buffer allocated.\n");
/* initialize the variable types of the first block */
/* to the types of the arguments */
lset = MGET_TYPEVECTOR(localbuf,0,numlocals);
+ lset->k = 0;
+ lset->alt = NULL;
td = lset->td;
- i = numlocals;
+ i = validlocals;
/* if this is an instance method initialize the "this" ref type */
if (!(method->flags & ACC_STATIC)) {
+ if (!i)
+ panic("Not enough local variables for method arguments");
td->type = TYPE_ADDRESS;
if (initmethod)
TYPEINFO_INIT_NEWOBJECT(td->info,NULL);
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);
-
- /* XXX What about unreached exception handlers? */
- }
+ excstack.prev = NULL;
+ excstack.type = TYPE_ADR;
+ TYPEINFO_INIT_CLASSINFO(excstack.typeinfo,
+ class_java_lang_Throwable); /* changed later */
LOG("Exception handler stacks set.\n");
len = bptr->icount;
iptr = bptr->iinstr;
while (--len >= 0) {
+ TYPECHECK_COUNT(stat_ins);
DOLOG(show_icmd(iptr,false)); LOGNL; LOGFLUSH;
opcode = iptr->opc;
+ myclass = iptr->clazz;
dst = iptr->dst;
maythrow = false;
break;
/****************************************/
+
+
+
+
+
+
+
+
+
/* PRIMITIVE VARIABLE ACCESS */
case ICMD_ILOAD: CHECK_ONEWORD(iptr->op1,TYPE_INT); break;
/* LOADING ADDRESS FROM VARIABLE */
case ICMD_ALOAD:
- INDEX_ONEWORD(iptr->op1);
+ TYPECHECK_COUNT(stat_ins_aload);
/* loading a returnAddress is not allowed */
- if (!typevectorset_checkreference(localset,iptr->op1))
- panic("illegal instruction: ALOAD loading non-reference");
+ if (jsrencountered) {
+ if (!typevectorset_checkreference(localset,iptr->op1))
+ panic("illegal instruction: ALOAD loading non-reference");
- typevectorset_copymergedtype(localset,iptr->op1,&(dst->typeinfo));
+ typevectorset_copymergedtype(localset,iptr->op1,&(dst->typeinfo));
+ }
+ else {
+ if (!TYPEDESC_IS_REFERENCE(localset->td[iptr->op1]))
+ panic("illegal instruction: ALOAD loading non-reference");
+ TYPEINFO_COPY(localset->td[iptr->op1].info,dst->typeinfo);
+ }
break;
/****************************************/
/* STORING ADDRESS TO VARIABLE */
case ICMD_ASTORE:
- INDEX_ONEWORD(iptr->op1);
-
if (handlers[0] &&
TYPEINFO_IS_NEWOBJECT(curstack->typeinfo))
panic("Storing uninitialized object in local variable inside try block");
/* FIELD ACCESS */
case ICMD_PUTFIELD:
+ TYPECHECK_COUNT(stat_ins_field);
if (!TYPEINFO_IS_REFERENCE(curstack->prev->typeinfo))
panic("illegal instruction: PUTFIELD on non-reference");
- if (TYPEINFO_IS_ARRAY(curstack->prev->typeinfo)) /* XXX arraystub */
+ if (TYPEINFO_IS_ARRAY(curstack->prev->typeinfo))
panic("illegal instruction: PUTFIELD on array");
/* check if the value is assignable to the field */
}
}
else {
- if (!is_accessible(fi->flags,fi->class,fi->class,
+ if (!is_accessible(fi->flags,fi->class,fi->class, myclass,
&(curstack->prev->typeinfo)))
panic("PUTFIELD: field is not accessible");
}
- /* XXX ---> unify with ICMD_PUTSTATIC? */
-
if (curstack->type != fi->type)
panic("PUTFIELD type mismatch");
if (fi->type == TYPE_ADR) {
break;
case ICMD_PUTSTATIC:
+ TYPECHECK_COUNT(stat_ins_field);
/* check if the value is assignable to the field */
{
fieldinfo *fi = (fieldinfo*) iptr[0].val.a;
- if (!is_accessible(fi->flags,fi->class,fi->class,NULL))
+ if (!is_accessible(fi->flags,fi->class,fi->class,myclass,NULL))
panic("PUTSTATIC: field is not accessible");
if (curstack->type != fi->type)
break;
case ICMD_GETFIELD:
+ TYPECHECK_COUNT(stat_ins_field);
if (!TYPEINFO_IS_REFERENCE(curstack->typeinfo))
panic("illegal instruction: GETFIELD on non-reference");
if (TYPEINFO_IS_ARRAY(curstack->typeinfo))
{
fieldinfo *fi = (fieldinfo *)(iptr->val.a);
- if (!is_accessible(fi->flags,fi->class,fi->class,
+ if (!is_accessible(fi->flags,fi->class,fi->class,myclass,
&(curstack->typeinfo)))
panic("GETFIELD: field is not accessible");
-
+
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:
+ TYPECHECK_COUNT(stat_ins_field);
{
fieldinfo *fi = (fieldinfo *)(iptr->val.a);
- if (!is_accessible(fi->flags,fi->class,fi->class,NULL))
+ if (!is_accessible(fi->flags,fi->class,fi->class,myclass,NULL)) {
+ printf("---------\n");
+ utf_display(fi->class->name);
+ printf("\n");
+ utf_display(myclass->name);
+ printf("\n");
+
+
panic("GETSTATIC: field is not accessible");
+ }
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;
/* PRIMITIVE ARRAY ACCESS */
case ICMD_ARRAYLENGTH:
- /* XXX should this also work on arraystubs? */
- if (!TYPEINFO_MAYBE_ARRAY(curstack->typeinfo))
+ if (!TYPEINFO_MAYBE_ARRAY(curstack->typeinfo)
+ && curstack->typeinfo.typeclass != pseudo_class_Arraystub)
panic("illegal instruction: ARRAYLENGTH on non-array");
maythrow = true;
break;
- /* XXX unify cases? */
case ICMD_BALOAD:
if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(curstack->prev->typeinfo,ARRAYTYPE_BOOLEAN)
&& !TYPEINFO_MAYBE_PRIMITIVE_ARRAY(curstack->prev->typeinfo,ARRAYTYPE_BYTE))
if (iptr->val.a == NULL)
TYPEINFO_INIT_NULLTYPE(dst->typeinfo);
else
- /* XXX constants for builtin functions */
- /* string constants */
+ /* string constants (or constant for builtin function) */
TYPEINFO_INIT_CLASSINFO(dst->typeinfo,class_java_lang_String);
break;
if (!TYPEINFO_IS_REFERENCE(curstack->typeinfo))
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;
/* returnAddress is not allowed */
if (!TYPEINFO_IS_REFERENCE(curstack->typeinfo))
panic("Illegal instruction: INSTANCEOF on non-reference");
-
- /* XXX optimize statically? */
break;
/****************************************/
case ICMD_IF_LCMPGE:
case ICMD_IF_LCMPGT:
case ICMD_IF_LCMPLE:
+ TYPECHECK_COUNT(stat_ins_branch);
tbptr = (basicblock *) iptr->target;
/* propagate stack and variables to the target block */
TYPECHECK_REACH;
- /* XXX */
break;
/****************************************/
/* SWITCHES */
case ICMD_TABLESWITCH:
+ TYPECHECK_COUNT(stat_ins_switch);
{
s4 *s4ptr = iptr->val.a;
s4ptr++; /* skip default */
goto switch_instruction_tail;
case ICMD_LOOKUPSWITCH:
+ TYPECHECK_COUNT(stat_ins_switch);
{
s4 *s4ptr = iptr->val.a;
s4ptr++; /* skip default */
/* RETURNS AND THROW */
case ICMD_ATHROW:
- TYPEINFO_INIT_CLASSINFO(tempinfo,class_java_lang_Throwable);
- if (!typeinfo_is_assignable(&curstack->typeinfo,&tempinfo))
+ if (!typeinfo_is_assignable_to_classinfo(
+ &curstack->typeinfo,class_java_lang_Throwable))
panic("illegal instruction: ATHROW on non-Throwable");
superblockend = true;
maythrow = true;
case ICMD_JSR:
LOG("jsr");
+ jsrencountered = true;
- /* XXX This is a dirty hack. It is needed
- * because of the special handling of ICMD_JSR in stack.c
+ /* This is a dirty hack. It is needed
+ * because of the special handling of
+ * ICMD_JSR in stack.c
*/
dst = (stackptr) iptr->val.a;
panic("Illegal instruction: JSR at end of bytecode");
typestack_put_retaddr(dst,bptr+1,localset);
repeat |= typestate_reach(localbuf,bptr,tbptr,dst,
- localset,numlocals);
+ localset,numlocals,true);
- /* XXX may throw? I don't think so. */
superblockend = true;
break;
case ICMD_RET:
/* check returnAddress variable */
- INDEX_ONEWORD(iptr->op1);
if (!typevectorset_checkretaddr(localset,iptr->op1))
panic("illegal instruction: RET using non-returnAddress variable");
case ICMD_INVOKESPECIAL:
case ICMD_INVOKESTATIC:
case ICMD_INVOKEINTERFACE:
+ TYPECHECK_COUNT(stat_ins_invoke);
{
methodinfo *mi = (methodinfo*) iptr->val.a;
bool specialmethod = (mi->name->text[0] == '<');
}
/* fetch parameter types and return type */
- /* XXX might use dst->typeinfo directly if non void */
i = 0;
if (opcode != ICMD_INVOKESTATIC) {
ptype[0] = TYPE_ADR;
LOGINFO(pinfo + i);
if (i==0 && callinginit)
{
- /* typeinfo tempinfo; */
-
/* first argument to <init> method */
if (!TYPEINFO_IS_NEWOBJECT(srcstack->typeinfo))
panic("Calling <init> on initialized object");
/* XXX We should resolve the method and pass its
* class as implementingclass to is_accessible. */
- if (!is_accessible(mi->flags,mi->class,NULL,
+ if (!is_accessible(mi->flags,mi->class,NULL, myclass,
(opcode == ICMD_INVOKESTATIC) ? NULL
: &(srcstack->typeinfo)))
panic("Invoking unaccessible method");
&& TYPEINFO_NEWOBJECT_INSTRUCTION(srcstack->typeinfo) == ins)
{
LOG("replacing uninitialized type on stack");
+
+ /* If this stackslot is in the instack of
+ * this basic block we must save the type(s)
+ * we are going to replace.
+ */
+ if (srcstack <= bptr->instack && !savedstack)
+ {
+ stackptr sp;
+ stackptr copy;
+ LOG("saving input stack types");
+ if (!savedstackbuf) {
+ LOG("allocating savedstack buffer");
+ savedstackbuf = DMNEW(stackelement,maxstack);
+ savedstackbuf->prev = NULL;
+ for (i=1; i<maxstack; ++i)
+ savedstackbuf[i].prev = savedstackbuf+(i-1);
+ }
+ sp = savedstack = bptr->instack;
+ copy = bptr->instack = savedstackbuf + (bptr->indepth-1);
+ TYPESTACK_COPY(sp,copy);
+ }
+
TYPEINFO_INIT_CLASSINFO(srcstack->typeinfo,initclass);
}
srcstack = srcstack->prev;
break;
case ICMD_BUILTIN3:
+ TYPECHECK_COUNT(stat_ins_builtin);
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");
-
- /* XXX optimize */
- /*
- typeinfo_init_component(&curstack->prev->prev->typeinfo,&tempinfo);
- if (!typeinfo_is_assignable(&curstack->typeinfo,&tempinfo))
- panic("illegal instruction: AASTORE to incompatible type");
- */
}
else {
/* XXX put these checks in a function */
+ TYPECHECK_COUNT(stat_ins_builtin_gen);
builtindesc = builtin_desc;
while (builtindesc->opcode && builtindesc->builtin
!= (functionptr) iptr->val.a) builtindesc++;
}
TYPECHECK_ARGS3(builtindesc->type_s3,builtindesc->type_s2,builtindesc->type_s1);
}
- maythrow = true; /* XXX better safe than sorry */
+ maythrow = true;
break;
case ICMD_BUILTIN2:
+ TYPECHECK_COUNT(stat_ins_builtin);
if (ISBUILTIN(BUILTIN_newarray))
{
vftbl *vft;
TYPEINFO_INIT_CLASSINFO(dst->typeinfo,vft->class);
}
else {
+ TYPECHECK_COUNT(stat_ins_builtin_gen);
builtindesc = builtin_desc;
while (builtindesc->opcode && builtindesc->builtin
!= (functionptr) iptr->val.a) builtindesc++;
}
TYPECHECK_ARGS2(builtindesc->type_s2,builtindesc->type_s1);
}
- maythrow = true; /* XXX better safe than sorry */
+ maythrow = true;
break;
case ICMD_BUILTIN1:
+ TYPECHECK_COUNT(stat_ins_builtin);
if (ISBUILTIN(BUILTIN_new)) {
if (iptr[-1].opc != ICMD_ACONST)
panic("Invalid instruction: NEW creating instance of abstract class");
TYPEINFO_INIT_NEWOBJECT(dst->typeinfo,iptr);
}
- /* XXX unify the following cases? */
else if (ISBUILTIN(BUILTIN_newarray_boolean)) {
TYPECHECK_INT(curstack);
TYPEINFO_INIT_PRIMITIVE_ARRAY(dst->typeinfo,ARRAYTYPE_BOOLEAN);
TYPEINFO_INIT_PRIMITIVE_ARRAY(dst->typeinfo,ARRAYTYPE_LONG);
}
else {
+ TYPECHECK_COUNT(stat_ins_builtin_gen);
builtindesc = builtin_desc;
while (builtindesc->opcode && builtindesc->builtin
!= (functionptr) iptr->val.a) builtindesc++;
}
TYPECHECK_ARGS1(builtindesc->type_s1);
}
- maythrow = true; /* XXX better safe than sorry */
+ maythrow = true;
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_POP:
- case ICMD_POP2:
- break;
-
/****************************************/
/* SIMPLE EXCEPTION THROWING TESTS */
/* INSTRUCTIONS WHICH SHOULD HAVE BEEN */
/* REPLACED BY OTHER OPCODES */
+#ifdef TYPECHECK_DEBUG
case ICMD_NEW:
case ICMD_NEWARRAY:
case ICMD_ANEWARRAY:
case ICMD_MONITORENTER:
case ICMD_MONITOREXIT:
case ICMD_AASTORE:
- /* XXX only check this in debug mode? */
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");
case ICMD_READONLY_ARG:
case ICMD_CLEAR_ARGREN:
- /* XXX only check this in debug mode? */
LOG2("ICMD %d at %d\n", iptr->opc, (int)(iptr-instr));
LOG("Should have been replaced in stack.c.");
panic("Internal error: unexpected pseudo instruction encountered");
break;
+#endif
/****************************************/
- /* ARITHMETIC AND CONVERSION */
+ /* UNCHECKED OPERATIONS */
/*********************************************
* Instructions below...
break;
/* Instructions which never throw a runtime exception: */
- /* XXX only add cases for them in debug mode? */
-
+#if defined(TYPECHECK_DEBUG) || defined(TYPECHECK_STATISTICS)
+ case ICMD_NOP:
+ case ICMD_POP:
+ case ICMD_POP2:
+
case ICMD_ICONST:
case ICMD_LCONST:
case ICMD_FCONST:
case ICMD_FNEG:
case ICMD_DNEG:
+ TYPECHECK_COUNT(stat_ins_unchecked);
break;
/****************************************/
default:
LOG2("ICMD %d at %d\n", iptr->opc, (int)(iptr-instr));
panic("Missing ICMD code during typecheck");
+#endif
}
/* the output of this instruction becomes the current stack */
LOG("reaching exception handlers");
i = 0;
while (handlers[i]) {
- tbptr = handlers[i]->handler;
- repeat |= typestate_reach(localbuf,bptr,tbptr,
- tbptr->instack,localset,numlocals);
+ TYPECHECK_COUNT(stat_handlers_reached);
+ cls = handlers[i]->catchtype;
+ excstack.typeinfo.typeclass = (cls) ? cls
+ : class_java_lang_Throwable;
+ repeat |= typestate_reach(localbuf,bptr,
+ handlers[i]->handler,
+ &excstack,localset,
+ numlocals,
+ jsrencountered);
i++;
}
}
}
TYPECHECK_REACH;
}
+
+ /* We may have to restore the types of the instack slots. They
+ * have been saved if an <init> call inside the block has
+ * modified the instack types. (see INVOKESPECIAL) */
+
+ if (savedstack) {
+ stackptr sp = bptr->instack;
+ stackptr copy = savedstack;
+ TYPECHECK_COUNT(stat_savedstack);
+ LOG("restoring saved instack");
+ TYPESTACK_COPY(sp,copy);
+ bptr->instack = savedstack;
+ savedstack = NULL;
+ }
} /* if block has to be checked */
bptr++;
} /* while blocks */
-
+
LOGIF(repeat,"repeat=true");
} while (repeat);
#ifdef TYPECHECK_STATISTICS
dolog("Typechecker did %4d iterations",count_iterations);
+ TYPECHECK_COUNT_FREQ(stat_iterations,count_iterations,STAT_ITERATIONS);
+ TYPECHECK_COUNTIF(jsrencountered,stat_typechecked_jsr);
#endif
#ifdef TYPECHECK_DEBUG