-/* jit/typecheck.c - typechecking (part of bytecode verification)
+/* vm/jit/verify/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
+ Copyright (C) 1996-2005 R. Grafl, A. Krall, C. Kruegel, C. Oates,
+ R. Obermaisser, M. Platter, M. Probst, S. Ring, E. Steiner,
+ C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich, J. Wenninger,
+ Institut f. Computersprachen - TU Wien
This file is part of CACAO.
Authors: Edwin Steiner
- $Id: typecheck.c 719 2003-12-08 14:26:05Z edwin $
+ $Id: typecheck.c 1735 2004-12-07 14:33:27Z twisti $
*/
-#include "global.h" /* must be here because of CACAO_TYPECHECK */
+#include <string.h>
+
+#include "vm/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)
+/* #include "main.h" */
+#include "mm/memory.h"
+#include "toolbox/logging.h"
+#include "native/native.h"
+#include "vm/builtin.h"
+#include "vm/loader.h"
+#include "vm/options.h"
+#include "vm/tables.h"
+#include "vm/jit/jit.h"
+#include "vm/jit/stack.h"
-#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(get_logfile(),info);log_plain("\n");} while(0))
+#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(dolog_plain(str))
+#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))
#ifdef TYPECHECK_VERBOSE_IMPORTANT
#define LOGimp(str) DOLOG(log_text(str))
-#define LOGimpSTR(str) DOLOG(dolog_plain(str))
+#define LOGimpSTR(str) DOLOG(log_plain(str))
#define LOGimpSTRu(utf) DOLOG(log_plain_utf(utf))
#else
#define LOGimp(str)
#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; i<num; ++i) {
- if (touched)
- fprintf(file," %d%s=",i,
- (touched[i]==TOUCHED_YES) ? "*"
- : ((touched[i]==TOUCHED_NO) ? "" : "~"));
- else
- fprintf(file," %d=",i);
- typeinfo_print_type(file,vtype[i],vinfo+i);
- }
-}
+#include <stdio.h>
static
void
-typeinfo_print_stack(FILE *file,stackptr stack)
+typestack_print(FILE *file,stackptr stack)
{
while (stack) {
- typeinfo_print_type(file,stack->type,&stack->typeinfo);
+ typeinfo_print_stacktype(file,stack->type,&stack->typeinfo);
stack = stack->prev;
if (stack) fprintf(file," ");
}
static
void
-typeinfo_print_block(FILE *file,stackptr instack,
- int vnum,u1 *vtype,typeinfo *vinfo,u1 *touched)
+typestate_print(FILE *file,stackptr instack,typevector *localset,int size)
{
fprintf(file,"Stack: ");
- typeinfo_print_stack(file,instack);
+ typestack_print(file,instack);
fprintf(file," Locals:");
- typeinfo_print_locals(file,vtype,vinfo,touched,vnum);
+ typevectorset_print(file,localset,size);
}
+#endif
-static
-void
-typeinfo_print_blocks(FILE *file,int vnum,u1 *vtype,typeinfo *vinfo)
+/****************************************************************************/
+/* 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 bi;
- /* int j;*/
+ int i;
+ for (i=0; i<limit; ++i)
+ fprintf(file," %3d: %8d\n",i,array[i]);
+ fprintf(file," =>%3d: %8d\n",limit,array[limit]);
+}
- for (bi=0; bi<block_count; ++bi) {
- fprintf(file,"%04d: (%3d) ",bi,block[bi].flags);
- typeinfo_print_block(file,block[bi].instack,
- vnum,vtype+vnum*bi,vinfo+vnum*bi,NULL);
- fprintf(file,"\n");
+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
-/* for (j=0; j<block[bi].icount; ++j) { */
-/* fprintf(file,"\t%s\n",icmd_names[block[bi].iinstr[j].opc]); */
-/* } */
+/****************************************************************************/
+/* TYPESTACK FUNCTIONS */
+/****************************************************************************/
- show_icmd_block(block+bi);
- }
+#define TYPESTACK_IS_RETURNADDRESS(sptr) \
+ TYPE_IS_RETURNADDRESS((sptr)->type,(sptr)->typeinfo)
+
+#define TYPESTACK_IS_REFERENCE(sptr) \
+ TYPE_IS_REFERENCE((sptr)->type,(sptr)->typeinfo)
+
+#define TYPESTACK_RETURNADDRESSSET(sptr) \
+ ((typeinfo_retaddr_set*)TYPEINFO_RETURNADDRESS((sptr)->typeinfo))
+
+#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)
+{
+ typevector *sel;
+ typeinfo_retaddr_set *sety;
+ typeinfo_retaddr_set *new;
+ typeinfo_retaddr_set **next;
+ int k;
+
+ for (;dst; dst=dst->prev, y=y->prev) {
+ if (!y) panic("Stack depth mismatch 1");
+ if (dst->type != y->type)
+ panic("Stack type mismatch 1");
+ LOG3("copy %p -> %p (type %d)",y,dst,dst->type);
+ if (dst->type == TYPE_ADDRESS) {
+ if (TYPEINFO_IS_PRIMITIVE(y->typeinfo)) {
+ /* We copy the returnAddresses from the selected
+ * states only. */
+
+ LOG("copying returnAddress");
+ sety = TYPESTACK_RETURNADDRESSSET(y);
+ next = &new;
+ for (k=0,sel=selected; sel; sel=sel->alt) {
+ LOG1("selected k=%d",sel->k);
+ while (k<sel->k) {
+ sety = sety->alt;
+ k++;
+ }
+ *next = DNEW(typeinfo_retaddr_set);
+ (*next)->addr = sety->addr;
+ next = &((*next)->alt);
+ }
+ *next = NULL;
+ TYPEINFO_INIT_RETURNADDRESS(dst->typeinfo,new);
+ }
+ else {
+ TYPEINFO_CLONE(y->typeinfo,dst->typeinfo);
+ }
+ }
+ }
+ if (y) panic("Stack depth mismatch 2");
}
+static void
+typestack_put_retaddr(stackptr dst,void *retaddr,typevector *loc)
+{
+#ifdef TYPECHECK_DEBUG
+ if (dst->type != TYPE_ADDRESS)
+ panic("Internal error: Storing returnAddress in non-address slot");
#endif
+
+ TYPEINFO_INIT_RETURNADDRESS(dst->typeinfo,NULL);
+ for (;loc; loc=loc->alt) {
+ typeinfo_retaddr_set *set = DNEW(typeinfo_retaddr_set);
+ set->addr = retaddr;
+ set->alt = TYPESTACK_RETURNADDRESSSET(dst);
+ TYPEINFO_INIT_RETURNADDRESS(dst->typeinfo,set);
+ }
+}
+
+static void
+typestack_collapse(stackptr dst)
+{
+ for (; dst; dst = dst->prev) {
+ if (TYPESTACK_IS_RETURNADDRESS(dst))
+ TYPESTACK_RETURNADDRESSSET(dst)->alt = NULL;
+ }
+}
+
+static bool
+typestack_merge(stackptr dst,stackptr y)
+{
+ bool changed = false;
+ for (; dst; dst = dst->prev, y=y->prev) {
+ if (!y)
+ panic("Stack depth mismatch 3");
+ if (dst->type != y->type) panic("Stack type mismatch 2");
+ 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 4");
+ return changed;
+}
+
+static void
+typestack_add(stackptr dst,stackptr y,int ky)
+{
+ typeinfo_retaddr_set *setd;
+ typeinfo_retaddr_set *sety;
+
+ for (; dst; dst = dst->prev, y=y->prev) {
+ if (TYPESTACK_IS_RETURNADDRESS(dst)) {
+ setd = TYPESTACK_RETURNADDRESSSET(dst);
+ sety = TYPESTACK_RETURNADDRESSSET(y);
+ RETURNADDRESSSET_SEEK(sety,ky);
+ while (setd->alt)
+ setd=setd->alt;
+ setd->alt = DNEW(typeinfo_retaddr_set);
+ setd->alt->addr = sety->addr;
+ setd->alt->alt = NULL;
+ }
+ }
+}
+
+/* 'a' and 'b' are assumed to have passed typestack_canmerge! */
+static bool
+typestack_separable_with(stackptr a,stackptr b,int kb)
+{
+ typeinfo_retaddr_set *seta;
+ typeinfo_retaddr_set *setb;
+
+ for (; a; a = a->prev, b = b->prev) {
+#ifdef TYPECHECK_DEBUG
+ if (!b) panic("Internal error: typestack_separable_from: different depth");
+#endif
+ if (TYPESTACK_IS_RETURNADDRESS(a)) {
+#ifdef TYPECHECK_DEBUG
+ if (!TYPESTACK_IS_RETURNADDRESS(b))
+ panic("Internal error: typestack_separable_from: unmergable stacks");
+#endif
+ seta = TYPESTACK_RETURNADDRESSSET(a);
+ setb = TYPESTACK_RETURNADDRESSSET(b);
+ RETURNADDRESSSET_SEEK(setb,kb);
+
+ for (;seta;seta=seta->alt)
+ if (seta->addr != setb->addr) return true;
+ }
+ }
+#ifdef TYPECHECK_DEBUG
+ if (b) panic("Internal error: typestack_separable_from: different depth");
+#endif
+ return false;
+}
+
+/* 'a' and 'b' are assumed to have passed typestack_canmerge! */
+static bool
+typestack_separable_from(stackptr a,int ka,stackptr b,int kb)
+{
+ typeinfo_retaddr_set *seta;
+ typeinfo_retaddr_set *setb;
+
+ for (; a; a = a->prev, b = b->prev) {
+#ifdef TYPECHECK_DEBUG
+ if (!b) panic("Internal error: typestack_separable_from: different depth");
+#endif
+ if (TYPESTACK_IS_RETURNADDRESS(a)) {
+#ifdef TYPECHECK_DEBUG
+ if (!TYPESTACK_IS_RETURNADDRESS(b))
+ panic("Internal error: typestack_separable_from: unmergable stacks");
+#endif
+ seta = TYPESTACK_RETURNADDRESSSET(a);
+ setb = TYPESTACK_RETURNADDRESSSET(b);
+ RETURNADDRESSSET_SEEK(seta,ka);
+ RETURNADDRESSSET_SEEK(setb,kb);
+
+ if (seta->addr != setb->addr) return true;
+ }
+ }
+#ifdef TYPECHECK_DEBUG
+ if (b) panic("Internal error: typestack_separable_from: different depth");
+#endif
+ return false;
+}
/****************************************************************************/
-/* INTERNAL DATA STRUCTURES */
+/* TYPESTATE FUNCTIONS */
/****************************************************************************/
-typedef struct jsr_record jsr_record;
+static bool
+typestate_merge(stackptr deststack,typevector *destloc,
+ stackptr ystack,typevector *yloc,
+ int locsize,bool jsrencountered)
+{
+ typevector *dvec,*yvec;
+ int kd,ky;
+ bool changed = false;
+
+ LOG("merge:");
+ LOGSTR("dstack: "); DOLOG(typestack_print(get_logfile(),deststack)); LOGNL;
+ LOGSTR("ystack: "); DOLOG(typestack_print(get_logfile(),ystack)); LOGNL;
+ LOGSTR("dloc : "); DOLOG(typevectorset_print(get_logfile(),destloc,locsize)); LOGNL;
+ LOGSTR("yloc : "); DOLOG(typevectorset_print(get_logfile(),yloc,locsize)); LOGNL;
+ LOGFLUSH;
+
+ /* 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;
+
+ /* Check if the typestates (deststack,destloc) will be
+ * separable when (ystack,yvec) is added. */
+
+ if (!typestack_separable_with(deststack,ystack,ky)
+ && !typevectorset_separable_with(destloc,yvec,locsize))
+ {
+ /* No, the resulting set won't be separable, thus we
+ * may merge all states in (deststack,destloc) and
+ * (ystack,yvec). */
+
+ typestack_collapse(deststack);
+ typevectorset_collapse(destloc,locsize);
+ typevector_merge(destloc,yvec,locsize);
+ }
+ else {
+ /* Yes, the resulting set will be separable. Thus we check
+ * if we may merge (ystack,yvec) with a single state in
+ * (deststack,destloc). */
+
+ for (dvec=destloc,kd=0; dvec; dvec=dvec->alt, kd++) {
+ if (!typestack_separable_from(ystack,ky,deststack,kd)
+ && !typevector_separable_from(yvec,dvec,locsize))
+ {
+ /* The typestate (ystack,yvec) is not separable from
+ * (deststack,dvec) by any returnAddress. Thus we may
+ * merge the states. */
+
+ changed |= typevector_merge(dvec,yvec,locsize);
+
+ goto merged;
+ }
+ }
+
+ /* The typestate (ystack,yvec) is separable from all typestates
+ * (deststack,destloc). Thus we must add this state to the
+ * result set. */
+
+ typestack_add(deststack,ystack,ky);
+ typevectorset_add(destloc,yvec,locsize);
+ changed = true;
+ }
+
+ merged:
+ ;
+ }
+
+ LOG("result:");
+ LOGSTR("dstack: "); DOLOG(typestack_print(get_logfile(),deststack)); LOGNL;
+ LOGSTR("dloc : "); DOLOG(typevectorset_print(get_logfile(),destloc,locsize)); LOGNL;
+ LOGFLUSH;
+
+ return changed;
+}
+
+
+static bool
+typestate_reach(codegendata *cd, registerdata *rd,void *localbuf,
+ basicblock *current,
+ basicblock *destblock,
+ stackptr ystack,typevector *yloc,
+ 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 - cd->method->basicblocks;
+ destloc = MGET_TYPEVECTOR(localbuf,destidx,locsize);
+
+ /* When branching backwards we have to check for uninitialized objects */
+
+ if (destblock <= current) {
+ stackptr sp;
+ int i;
+#if defined(__GNUC__)
+#warning FIXME FOR INLINING
+#endif
+ if (!useinlining) {
+ TYPECHECK_COUNT(stat_backwards);
+ LOG("BACKWARDS!");
+ for (sp = ystack; sp; sp=sp->prev)
+ if (sp->type == TYPE_ADR &&
+ TYPEINFO_IS_NEWOBJECT(sp->typeinfo)) {
+ show_icmd_method(cd->method,cd,rd);
+ printf("current: %ld, dest: %ld\n",current->debug_nr,destblock->debug_nr);
+ panic("Branching backwards with uninitialized object on stack");
+ }
+
+ for (i=0; i<locsize; ++i)
+ if (yloc->td[i].type == TYPE_ADR &&
+ TYPEINFO_IS_NEWOBJECT(yloc->td[i].info))
+ panic("Branching backwards with uninitialized object in local variable");
+ }
+ }
+
+ 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);
+ COPY_TYPEVECTORSET(yloc,destloc,locsize);
+ changed = true;
+ }
+ 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,
+ jsrencountered);
+ TYPECHECK_COUNTIF(changed,stat_merging_changed);
+ }
+
+ if (changed) {
+ LOG("changed!");
+ destblock->flags = BBTYPECHECK_REACHED;
+ if (destblock <= current) {LOG("REPEAT!"); return true;}
+ }
+ return false;
+}
-/*
- * For each basic block we store the chain of JSR instructions which
- * were used to reach the block (usually zero or one). For more
- * details on verifying JSR and RET instructions see the Java VM
- * Specification.
- *
- * CAUTION: The fields starting with sbr_ are only valid for the
- * jsr_record of the first block of the subroutine.
- */
-struct jsr_record {
- basicblock *target; /* target of the JSR instruction (first block of subroutine) */
- jsr_record *next; /* for chaining in nested try ... finally */ /* XXX make it sbr_next? */
- u1 *sbr_touched; /* specifies which variables the subroutine touches */
- u1 *sbr_vtype; /* Types of local variables after RET */
- typeinfo *sbr_vinfo; /* Types of local variables after RET */
- u1 touched[1]; /* touched flags for local variables */
-};
+
+static bool
+typestate_ret(codegendata *cd,registerdata *rd, void *localbuf,
+ basicblock *current,
+ stackptr ystack,typevector *yloc,
+ int retindex,int locsize)
+{
+ typevector *yvec;
+ typevector *selected;
+ basicblock *destblock;
+ bool repeat = false;
+
+ for (yvec=yloc; yvec; ) {
+ if (!TYPEDESC_IS_RETURNADDRESS(yvec->td[retindex]))
+ panic("Illegal instruction: RET on non-returnAddress");
+
+ destblock = (basicblock*) TYPEINFO_RETURNADDRESS(yvec->td[retindex].info);
+
+ selected = typevectorset_select(&yvec,retindex,destblock);
+
+ repeat |= typestate_reach(cd, rd, localbuf,current,destblock,
+ ystack,selected,locsize,true);
+ }
+ return repeat;
+}
/****************************************************************************/
-/* MACROS USED INTERNALLY IN typecheck() */
+/* HELPER FUNCTIONS */
/****************************************************************************/
-#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)
+/* If a field is checked, definingclass == implementingclass */
+static bool
+is_accessible(int flags,classinfo *definingclass,classinfo *implementingclass, classinfo *methodclass,
+ typeinfo *instance)
+{
+ /* check access rights */
+ 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:
+ if (definingclass->packagename != methodclass->packagename)
+ return false;
+ break;
+ case ACC_PROTECTED:
+ 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 != methodclass) {
+ LOG("private access");
+ return false;
+ }
+ break;
+ default:
+ panic("Invalid access flags");
+ }
+ }
+
+ if (instance) {
+ if ((flags & ACC_STATIC) != 0) {
+ LOG("accessing STATIC member with instance");
+ return false;
+ }
+
+ if (implementingclass
+ && !TYPEINFO_IS_NULLTYPE(*instance)
+ && !TYPEINFO_IS_NEWOBJECT(*instance))
+ {
+ if (!typeinfo_is_assignable_to_classinfo(instance,
+ implementingclass))
+ {
+ LOG("instance not assignable");
+ LOGINFO(instance);
+ LOGSTRu(implementingclass->name); LOGNL; LOGFLUSH;
+ return false;
+ }
+ }
+ }
+ else {
+ if ((flags & ACC_STATIC) == 0) {
+ LOG("accessing non-STATIC member without instance");
+ return false;
+ }
+ }
+
+ return true;
+}
-/* 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);}
+/****************************************************************************/
+/* MACROS FOR LOCAL VARIABLE CHECKING */
+/****************************************************************************/
-#define CHECKVARTYPE(num,type) \
- {if (vtype[(num)] != (type)) panic("Variable type mismatch"); TOUCH_VARIABLE(num);}
+#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 STORE_ONEWORD(num,type) \
+ do {typevectorset_store(localset,num,type,NULL);} while(0)
+
+#define STORE_TWOWORD(num,type) \
+ do {typevectorset_store_twoword(localset,num,type);} while(0)
+
+
+#define WORDCHECKFAULT \
+ do { \
+ show_icmd_method(m, cd, rd); \
+ dolog("localset->td index: %ld\ninstruction belongs to:%s.%s, outermethod:%s.%s\n", \
+ iptr->op1,iptr->method->class->name->text, \
+ iptr->method->name->text,m->class->name->text,m->name->text); \
+ show_icmd(iptr++, false); \
+ show_icmd(iptr, false); \
+ } while (0)
+
+
+#define CHECK_ONEWORD(num,tp) \
+ do {TYPECHECK_COUNT(stat_ins_primload); \
+ if (jsrencountered) { \
+ if (!typevectorset_checktype(localset,num,tp)) { \
+ WORDCHECKFAULT; \
+ panic("Variable type mismatch"); \
+ } \
+ } \
+ else { \
+ if (localset->td[num].type != tp) { \
+ panic("Variable type mismatch"); \
+ WORDCHECKFAULT; \
+ } \
+ } \
+ } while(0)
+
+#define CHECK_TWOWORD(num,type) \
+ do {TYPECHECK_COUNT(stat_ins_primload); \
+ if (!typevectorset_checktype(localset,num,type)) { \
+ WORDCHECKFAULT; \
+ 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);}
+/****************************************************************************/
+/* MACROS FOR STACK TYPE CHECKING */
+/****************************************************************************/
-#define ISBUILTIN(v) (iptr->val.a == (functionptr)(v))
+/* These macros are for basic typechecks which were not done in stack.c */
-/* 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_MERGEJSR:
- *
- * Input:
- * Ouput:
- * tbptr......target block
- * changed....set to true if any typeinfo has changed
- * numlocals..number of local variables
- * touched....current touched flags of local variables
- * Used:
- * macro_i, jsrtemp, jsrtemp2
- */
-#define TYPECHECK_MERGEJSR \
- do { \
- LOG("TYPECHECK_MERGEJSR"); \
- jsrtemp = jsrbuffer[tbptr-block]; \
- 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; \
- } \
- jsrtemp = jsrbuffer[tbptr-block]; \
- if (jsrtemp) \
- for (macro_i=0; macro_i<numlocals; ++macro_i) { \
- jsrtemp->touched[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 \
- 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)
+#define TYPECHECK_STACK(sp,tp) \
+ do { if ((sp)->type != (tp)) \
+ panic("Wrong data type on stack"); } while(0)
-/* 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)
+#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_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
- * 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)+(numlocals-1)*sizeof(u1)); \
- jsrtemp->target = (tbptr); \
- jsrtemp->next = jsrchain; \
- jsrtemp->sbr_touched = NULL; \
- memset(&jsrtemp->touched,TOUCHED_NO,sizeof(u1)*numlocals); \
- jsrbuffer[tbptr-block] = jsrtemp; \
- } while (0)
+/****************************************************************************/
+/* MISC MACROS */
+/****************************************************************************/
-/* 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
- * numlocals..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)+(numlocals-1)*sizeof(u1)); \
- jsrtemp->target = (chain)->target; \
- jsrtemp->next = (chain)->next; \
- jsrtemp->sbr_touched = NULL; \
- memcpy(&jsrtemp->touched,touched,sizeof(u1)*numlocals); \
- jsrbuffer[tbptr-block] = jsrtemp; \
- } \
- else \
- jsrbuffer[tbptr-block] = NULL; \
- } while (0)
+#define COPYTYPE(source,dest) \
+ {if ((source)->type == TYPE_ADR) \
+ TYPEINFO_COPY((source)->typeinfo,(dest)->typeinfo);}
-/* 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)
+#define ISBUILTIN(v) (iptr->val.fp == (functionptr) (v))
/* TYPECHECK_REACH: executed, when the target block (tbptr) can be reached
* from the current block (bptr). The types of local variables and
* Input:
* bptr.......current block
* tbptr......target block
- * dst........current output stack pointer (not needed for REACH_THROW)
+ * dst........current output stack pointer
* numlocals..number of local variables
- * 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
+ * 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(way) \
+#define TYPECHECK_REACH \
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 + 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_ADD_JSR; \
- TYPECHECK_COPYVARS; \
- } \
- else { \
- TYPECHECK_COPYJSR(jsrchain); \
- TYPECHECK_COPYVARS; \
- } \
- if (way != REACH_THROW) TYPECHECK_COPYSTACK; \
- changed = true; \
- } else { \
- /* This block has been reached before */ \
- changed = false; \
- if (way == REACH_JSR) \
- TYPECHECK_CHECK_JSR_CHAIN; \
- else \
- TYPECHECK_MERGEJSR; \
- TYPECHECK_MERGEVARS; \
- if (way != REACH_THROW) TYPECHECK_MERGESTACK; \
- } \
- if (changed) { \
- LOG("REACHED!"); \
- tbptr->flags = BBTYPECHECK_REACHED; \
- if (tbptr <= bptr) {repeat = true; LOG("REPEAT!");} \
- } \
+ repeat |= typestate_reach(cd,rd, localbuf,bptr,tbptr,dst, \
+ 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 \
do { \
- if (initmethod && class != class_java_lang_Object) { \
+ if (initmethod && m->class != class_java_lang_Object) { \
/* check the marker variable */ \
LOG("Checking <init> marker"); \
- if (vtype[numlocals-1] == TYPE_VOID) \
+ if (!typevectorset_checktype(localset,numlocals-1,TYPE_INT))\
panic("<init> method does not initialize 'this'"); \
} \
} while (0)
#define MAXPARAMS 255
/* typecheck is called directly after analyse_stack */
-void
-typecheck()
+
+methodinfo *typecheck(methodinfo *m, codegendata *cd, registerdata *rd)
{
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 */
+
int numlocals; /* number of local variables */
- 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 */
+ int validlocals; /* number of valid local variable indices */
+ void *localbuf; /* local variable types for each block start */
+ typevector *localset; /* typevector set for local variables */
+ typevector *lset; /* temporary pointer */
+ typedescriptor *td; /* temporary pointer */
+
+ 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 */
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 */
+ basicblock **tptr; /* pointer into target list of switch instr. */
+ exceptiontable **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; /* 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,m->codegendata->maxlocals,STAT_LOCALS);
+ TYPECHECK_COUNT_FREQ(stat_blocks,m->basicblockcount/10,STAT_BLOCKS);
+#endif
LOGSTR("\n==============================================================================\n");
DOLOG(show_icmd_method());
LOGimpSTR(" (class ");
LOGimpSTRu(method->class->name);
LOGimpSTR(")\n");
+ LOGFLUSH;
- name_init = utf_new_char("<init>");
- initmethod = (method->name == name_init);
+ if (!name_init)
+ name_init = utf_new_char("<init>");
+ initmethod = (m->name == name_init);
- /* XXX allocate buffers for method arguments */
+ /* Allocate buffer 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;
+ b_count = m->basicblockcount;
+ bptr = m->basicblocks;
while (--b_count >= 0) {
#ifdef TYPECHECK_DEBUG
if (bptr->flags != BBFINISHED && bptr->flags != BBDELETED
}
/* The first block is always reached */
- if (block_count && block[0].flags == BBTYPECHECK_UNDEF)
- block[0].flags = BBTYPECHECK_REACHED;
+ if (m->basicblockcount && m->basicblocks[0].flags == BBTYPECHECK_UNDEF)
+ m->basicblocks[0].flags = BBTYPECHECK_REACHED;
LOG("Blocks reset.\n");
/* In <init> methods we use an extra local variable to signal if
* the 'this' reference has been initialized. */
- numlocals = maxlocals;
+ numlocals = cd->maxlocals;
+ validlocals = numlocals;
if (initmethod) numlocals++;
/* allocate the buffers for local variables */
- 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");
-
- /* 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");
+ localbuf = DMNEW_TYPEVECTOR(m->basicblockcount+1, numlocals);
+ localset = MGET_TYPEVECTOR(localbuf,m->basicblockcount,numlocals);
+
+ LOG("Variable buffer allocated.\n");
/* allocate the buffer of active exception handlers */
- handlers = DMNEW(xtable*,method->exceptiontablelength + 1);
+ handlers = DMNEW(exceptiontable*, cd->exceptiontablelength + 1);
/* initialize the variable types of the first block */
/* to the types of the arguments */
- ttype = vartype;
- tinfo = vartypeinfo;
+ lset = MGET_TYPEVECTOR(localbuf,0,numlocals);
+ lset->k = 0;
+ lset->alt = NULL;
+ td = lset->td;
+ i = validlocals;
/* if this is an instance method initialize the "this" ref type */
- if (!(method->flags & ACC_STATIC)) {
- *ttype++ = TYPE_ADDRESS;
+ if (!(m->flags & ACC_STATIC)) {
+ if (!i)
+ panic("Not enough local variables for method arguments");
+ td->type = TYPE_ADDRESS;
if (initmethod)
- TYPEINFO_INIT_NEWOBJECT(*tinfo,NULL);
+ TYPEINFO_INIT_NEWOBJECT(td->info,NULL);
else
- TYPEINFO_INIT_CLASSINFO(*tinfo,class);
- tinfo++;
+ TYPEINFO_INIT_CLASSINFO(td->info, m->class);
+ td++;
+ i--;
}
LOG("'this' argument set.\n");
/* the rest of the arguments and the return type */
- typeinfo_init_from_method_args(method->descriptor,ttype,tinfo,
- numlocals - (tinfo-vartypeinfo),
- true, /* two word types use two slots */
- &returntype,&returntypeinfo);
+ i = typedescriptors_init_from_method_args(td, m->descriptor,
+ i,
+ true, /* two word types use two slots */
+ &returntype);
+ td += i;
+ i = numlocals - (td - lset->td);
+ while (i--) {
+ td->type = TYPE_VOID;
+ td++;
+ }
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);
- }
+ excstack.prev = NULL;
+ excstack.type = TYPE_ADR;
+ TYPEINFO_INIT_CLASSINFO(excstack.typeinfo,
+ class_java_lang_Throwable); /* changed later */
LOG("Exception handler stacks set.\n");
/* loop while there are still blocks to be checked */
do {
+ TYPECHECK_COUNT(count_iterations);
repeat = false;
- b_count = block_count;
- bptr = block;
+ b_count = m->basicblockcount;
+ bptr = m->basicblocks;
while (--b_count >= 0) {
LOGSTR1("---- BLOCK %04d, ",bptr-block);
superblockend = false;
bptr->flags = BBFINISHED;
- b_index = bptr - block;
+ b_index = bptr - m->basicblocks;
/* init stack at the start of this block */
curstack = bptr->instack;
/* 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;
+ for (i = 0; i < cd->exceptiontablelength; ++i) {
+ if ((cd->exceptiontable[i].start <= bptr) && (cd->exceptiontable[i].end > bptr)) {
+ LOG1("active handler L%03d", cd->exceptiontable[i].handler->debug_nr);
+ handlers[len++] = cd->exceptiontable + i;
}
}
handlers[len] = NULL;
/* init variable types at the start of this block */
- 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 */
- if ((jsrchain = jsrbuffer[b_index]) != NULL) {
-#ifdef TYPECHECK_VERBOSE
- if (typecheckverbose) {
- LOGSTR("jsr chain:");
- jsrtemp = jsrchain;
- while (jsrtemp) {
- LOGSTR1(" L%03d",jsrtemp->target->debug_nr);
- jsrtemp = jsrtemp->next;
- }
- LOGNL;
- LOGFLUSH;
- }
-#endif
-
- subroutine = jsrbuffer[jsrchain->target - block];
- 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(get_logfile(),curstack,numlocals,vtype,vinfo,(jsrchain) ? touched : NULL);
- LOGNL; LOGFLUSH;
- }
+ COPY_TYPEVECTORSET(MGET_TYPEVECTOR(localbuf,b_index,numlocals),
+ localset,numlocals);
+#if defined(__GNUC__)
+#warning FIXME FOR INLINING
#endif
+ if(!useinlining) {
+ if (handlers[0])
+ for (i=0; i<numlocals; ++i)
+ if (localset->td[i].type == TYPE_ADR
+ && TYPEINFO_IS_NEWOBJECT(localset->td[i].info)) {
+ show_icmd_method(m, cd, rd);
+ printf("Uninitialized variale:%ld, block:%ld\n",i,bptr->debug_nr);
+ panic("Uninitialized object in local variable inside try block");
+ }
+ }
+ DOLOG(typestate_print(get_logfile(),curstack,localset,numlocals));
+ LOGNL; LOGFLUSH;
/* loop over the instructions */
len = bptr->icount;
iptr = bptr->iinstr;
while (--len >= 0) {
- DOLOG(show_icmd(iptr,false));
- LOGNL;
- LOGFLUSH;
+ TYPECHECK_COUNT(stat_ins);
+ DOLOG(show_icmd(iptr,false)); LOGNL; LOGFLUSH;
opcode = iptr->opc;
+ myclass = iptr->method->class;
dst = iptr->dst;
maythrow = false;
COPYTYPE(curstack->prev,dst);
break;
- /* XXX only add these cases in debug mode? */
- case ICMD_POP:
- break;
-
- case ICMD_POP2:
- break;
+ /****************************************/
+
+
+
+
+
+
+
+
+ /* PRIMITIVE VARIABLE ACCESS */
+
+ 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_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;
+
/****************************************/
/* LOADING ADDRESS FROM VARIABLE */
case ICMD_ALOAD:
- CHECKVARTYPE(iptr->op1,TYPE_ADR);
+ TYPECHECK_COUNT(stat_ins_aload);
/* 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);
+ if (jsrencountered) {
+ if (!typevectorset_checkreference(localset,iptr->op1))
+ panic("illegal instruction: ALOAD loading non-reference");
+
+ typevectorset_copymergedtype(localset,iptr->op1,&(dst->typeinfo));
+ }
+ else {
+ if (!TYPEDESC_IS_REFERENCE(localset->td[iptr->op1])) {
+ show_icmd_method(m, cd, rd);
+ dolog("localset->td index: %ld\ninstruction belongs to:%s.%s, outermethod:%s.%s\n",
+ iptr->op1,iptr->method->class->name->text,
+ iptr->method->name->text,m->class->name->text,m->name->text);
+ show_icmd(iptr, false);
+ panic("illegal instruction: ALOAD loading non-reference");
+ }
+ TYPEINFO_COPY(localset->td[iptr->op1].info,dst->typeinfo);
+ }
break;
/****************************************/
/* STORING ADDRESS TO VARIABLE */
case ICMD_ASTORE:
- /* TYPE_ADR has already been checked. */
-
if (handlers[0] &&
TYPEINFO_IS_NEWOBJECT(curstack->typeinfo))
panic("Storing uninitialized object in local variable inside try block");
-
- STORE_TYPE(iptr->op1,TYPE_ADDRESS);
- TYPEINFO_COPY(curstack->typeinfo,vinfo[iptr->op1]);
+
+ if (TYPESTACK_IS_RETURNADDRESS(curstack))
+ typevectorset_store_retaddr(localset,iptr->op1,&(curstack->typeinfo));
+ else
+ typevectorset_store(localset,iptr->op1,TYPE_ADDRESS,
+ &(curstack->typeinfo));
break;
/****************************************/
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:
+ 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");
-
- /* XXX */
+ /* check if the value is assignable to the field */
+ {
+ fieldinfo *fi = (fieldinfo*) iptr[0].val.a;
+
+ if (TYPEINFO_IS_NEWOBJECT(curstack->prev->typeinfo)) {
+ if (initmethod
+ && !TYPEINFO_NEWOBJECT_INSTRUCTION(curstack->prev->typeinfo))
+ {
+ /* uninitialized "this" instance */
+ if (fi->class != m->class || (fi->flags & ACC_STATIC) != 0)
+ panic("Setting unaccessible field in uninitialized object");
+ }
+ else {
+ panic("PUTFIELD on uninitialized object");
+ }
+ }
+ else {
+ if (!is_accessible(fi->flags,fi->class,fi->class, myclass,
+ &(curstack->prev->typeinfo)))
+ panic("PUTFIELD: field is not accessible");
+ }
+
+ 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 */
+ 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,myclass,NULL))
+ panic("PUTSTATIC: field is not accessible");
+
+ 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:
+ 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);
- /* XXX check non-static? */
+
+ 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);
- /* XXX check static? */
+
+ 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;
-
+
case ICMD_BALOAD:
if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(curstack->prev->typeinfo,ARRAYTYPE_BOOLEAN)
&& !TYPEINFO_MAYBE_PRIMITIVE_ARRAY(curstack->prev->typeinfo,ARRAYTYPE_BYTE))
maythrow = true;
break;
+ case ICMD_IASTORECONST:
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(curstack->prev->typeinfo, ARRAYTYPE_INT))
+ panic("Array type mismatch");
+ maythrow = true;
+ break;
+
+ case ICMD_LASTORECONST:
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(curstack->prev->typeinfo, ARRAYTYPE_LONG))
+ panic("Array type mismatch");
+ maythrow = true;
+ break;
+
+ case ICMD_BASTORECONST:
+ 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_CASTORECONST:
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(curstack->prev->typeinfo, ARRAYTYPE_CHAR))
+ panic("Array type mismatch");
+ maythrow = true;
+ break;
+
+ case ICMD_SASTORECONST:
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(curstack->prev->typeinfo, ARRAYTYPE_SHORT))
+ panic("Array type mismatch");
+ maythrow = true;
+ break;
+
+
/****************************************/
- /* OPERATIONS WITH UNCHECKED INPUT */
+ /* ADDRESS CONSTANTS */
+
+ case ICMD_ACONST:
+ if (iptr->val.a == NULL)
+ TYPEINFO_INIT_NULLTYPE(dst->typeinfo);
+ else
+ /* string constants (or constant for builtin function) */
+ TYPEINFO_INIT_CLASSINFO(dst->typeinfo,class_java_lang_String);
+ break;
+
+ /****************************************/
+ /* CHECKCAST AND INSTANCEOF */
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 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_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(REACH_STD);
- /* XXX */
+ TYPECHECK_REACH;
break;
/****************************************/
/* SWITCHES */
case ICMD_TABLESWITCH:
+ TYPECHECK_COUNT(stat_ins_switch);
{
s4 *s4ptr = iptr->val.a;
- s4ptr++; /* skip default */
- i = *s4ptr++; /* low */
+ s4ptr++; /* skip default */
+ i = *s4ptr++; /* low */
i = *s4ptr++ - i + 2; /* +1 for default target */
}
goto switch_instruction_tail;
case ICMD_LOOKUPSWITCH:
+ TYPECHECK_COUNT(stat_ins_switch);
{
s4 *s4ptr = iptr->val.a;
- s4ptr++; /* skip default */
- i = *s4ptr++ + 1; /* count +1 for default target */
+ s4ptr++; /* skip default */
+ i = *s4ptr++ + 1; /* count +1 for default */
}
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);
+ TYPECHECK_REACH;
}
LOG("switch done");
superblockend = true;
/****************************************/
/* RETURNS AND THROW */
- /* XXX returns may 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;
if (!TYPEINFO_IS_REFERENCE(curstack->typeinfo))
panic("illegal instruction: ARETURN on non-reference");
- if (returntype != TYPE_ADDRESS
- || !typeinfo_is_assignable(&curstack->typeinfo,&returntypeinfo))
+ if (returntype.type != TYPE_ADDRESS
+ || !typeinfo_is_assignable(&curstack->typeinfo,&(returntype.info)))
panic("Return type mismatch");
-
- TYPECHECK_LEAVE;
- superblockend = true;
- break;
-
+ goto return_tail;
+
case ICMD_IRETURN:
- if (returntype != TYPE_INT)
- panic("Return type mismatch");
- TYPECHECK_LEAVE;
- superblockend = true;
- break;
+ if (returntype.type != TYPE_INT) panic("Return type mismatch");
+ goto return_tail;
+
case ICMD_LRETURN:
- if (returntype != TYPE_LONG)
- panic("Return type mismatch");
- TYPECHECK_LEAVE;
- superblockend = true;
- break;
+ if (returntype.type != TYPE_LONG) panic("Return type mismatch");
+ goto return_tail;
+
case ICMD_FRETURN:
- if (returntype != TYPE_FLOAT)
- panic("Return type mismatch");
- TYPECHECK_LEAVE;
- superblockend = true;
- break;
+ if (returntype.type != TYPE_FLOAT) panic("Return type mismatch");
+ goto return_tail;
+
case ICMD_DRETURN:
- if (returntype != TYPE_DOUBLE)
- panic("Return type mismatch");
- TYPECHECK_LEAVE;
- superblockend = true;
- break;
+ if (returntype.type != TYPE_DOUBLE) panic("Return type mismatch");
+ goto return_tail;
+
case ICMD_RETURN:
- if (returntype != TYPE_VOID)
- panic("Return type mismatch");
+ if (returntype.type != TYPE_VOID) panic("Return type mismatch");
+ return_tail:
TYPECHECK_LEAVE;
superblockend = true;
+ maythrow = true;
break;
/****************************************/
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;
- /* 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);
+ if (bptr + 1 == (m->basicblocks + m->basicblockcount + 1))
+ panic("Illegal instruction: JSR at end of bytecode");
+ typestack_put_retaddr(dst,bptr+1,localset);
+ repeat |= typestate_reach(cd, rd,localbuf,bptr,tbptr,dst,
+ localset,numlocals,true);
- /* 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; i<numlocals; ++i)
- if (jsrtemp->sbr_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? */
+ superblockend = true;
break;
case ICMD_RET:
/* check returnAddress variable */
- CHECKVARTYPE(iptr->op1,TYPE_ADR);
-
- if (!TYPEINFO_IS_PRIMITIVE(vinfo[iptr->op1]))
+ if (!typevectorset_checkretaddr(localset,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; 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,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(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;
- 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);*/
- }
-
+ repeat |= typestate_ret(cd,rd, localbuf,bptr,curstack,
+ localset,iptr->op1,numlocals);
+
superblockend = true;
break;
case ICMD_INVOKESPECIAL:
case ICMD_INVOKESTATIC:
case ICMD_INVOKEINTERFACE:
+ TYPECHECK_COUNT(stat_ins_invoke);
{
- /* XXX check access rights */
-
methodinfo *mi = (methodinfo*) iptr->val.a;
+ bool specialmethod = (mi->name->text[0] == '<');
bool callinginit = (opcode == ICMD_INVOKESPECIAL && mi->name == name_init);
instruction *ins;
classinfo *initclass;
- /* XXX for INVOKESPECIAL: check if the invokation is done at all */
+ if (specialmethod && !callinginit)
+ panic("Invalid invocation of special method");
+
+ if (opcode == ICMD_INVOKESPECIAL) {
+ /* XXX for INVOKESPECIAL: check if the invokation is done at all */
+
+ /* (If callinginit the class is checked later.) */
+ if (!callinginit) {
+ if (!builtin_isanysubclass(myclass,mi->class))
+ panic("Illegal instruction: INVOKESPECIAL calling non-superclass method");
+ }
+ }
/* 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;
/* 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;
+ initclass = (ins) ? (classinfo*)ins[-1].val.a : m->class;
LOGSTR("class: "); LOGSTRu(initclass->name); LOGNL;
- /* XXX check type */
+ /* check type */
+ /* (This is checked below.) */
+/* TYPEINFO_INIT_CLASSINFO(tempinfo,initclass); */
+/* if (!typeinfo_is_assignable(&tempinfo,pinfo+0)) */
+/* panic("Parameter reference type mismatch in <init> invocation"); */
}
else {
if (!typeinfo_is_assignable(&(srcstack->typeinfo),pinfo+i))
}
LOG("ok");
- srcstack = srcstack->prev;
+ if (i) srcstack = srcstack->prev;
}
+ /* XXX We should resolve the method and pass its
+ * class as implementingclass to is_accessible. */
+ if (!is_accessible(mi->flags,mi->class,NULL, myclass,
+ (opcode == ICMD_INVOKESTATIC) ? NULL
+ : &(srcstack->typeinfo)))
+ panic("Invoking unaccessible method");
+
+ LOG("checking return type");
if (rtype != TYPE_VOID) {
if (rtype != dst->type)
panic("Return type mismatch in method invocation");
}
if (callinginit) {
+ LOG("replacing uninitialized object");
/* replace uninitialized object type on stack */
srcstack = dst;
while (srcstack) {
&& 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, cd->maxstack);
+ savedstackbuf->prev = NULL;
+ for (i = 1; i < cd->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;
}
/* 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);
- }
- }
+ typevectorset_init_object(localset,ins,initclass,numlocals);
/* initializing the 'this' reference? */
- if (initmethod && !ins) {
+ if (!ins) {
+#ifdef TYPECHECK_DEBUG
+ if (!initmethod)
+ panic("Internal error: calling <init> on this in non-<init> method.");
+#endif
+ /* must be <init> of current class or direct superclass */
+ if (mi->class != m->class && mi->class != m->class->super)
+ panic("<init> calling <init> of the wrong class");
+
/* set our marker variable to type int */
LOG("setting <init> marker");
- STORE_PRIMITIVE(numlocals-1,TYPE_INT);
+ typevectorset_store(localset,numlocals-1,TYPE_INT,NULL);
}
+ else {
+ /* initializing an instance created with NEW */
+ /* XXX is this strictness ok? */
+ if (mi->class != initclass)
+ panic("Calling <init> method of the wrong class");
+ }
}
}
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_t *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_t*) 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 */
+ 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");
- */
}
- /* XXX check for missed builtins in debug mode? */
- maythrow = true; /* XXX better safe than sorry */
+ else {
+ /* XXX put these checks in a function */
+ TYPECHECK_COUNT(stat_ins_builtin_gen);
+ builtindesc = builtin_desc;
+ while (builtindesc->opcode && builtindesc->builtin
+ != iptr->val.fp) builtindesc++;
+ if (!builtindesc->opcode) {
+ dolog("Builtin not in table: %s",icmd_builtin_name(iptr->val.fp));
+ panic("Internal error: builtin not found in table");
+ }
+ TYPECHECK_ARGS3(builtindesc->type_s3,builtindesc->type_s2,builtindesc->type_s1);
+ }
+ maythrow = true;
break;
case ICMD_BUILTIN2:
- if (
-#if defined(__I386__)
- ISBUILTIN(asm_builtin_newarray)
-#else
- ISBUILTIN(builtin_newarray)
-#endif
- )
+ TYPECHECK_COUNT(stat_ins_builtin);
+ if (ISBUILTIN(BUILTIN_newarray))
{
+ vftbl_t *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_t *)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_t *vft;
+ TYPECHECK_ADR(curstack->prev);
+ if (iptr[-1].opc != ICMD_ACONST)
+ panic("illegal instruction: builtin_arrayinstanceof without classinfo");
+ vft = (vftbl_t *)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_t *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_t *)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? */
- maythrow = true; /* XXX better safe than sorry */
+ else {
+ TYPECHECK_COUNT(stat_ins_builtin_gen);
+ builtindesc = builtin_desc;
+ while (builtindesc->opcode && builtindesc->builtin
+ != iptr->val.fp) builtindesc++;
+ if (!builtindesc->opcode) {
+ dolog("Builtin not in table: %s",icmd_builtin_name(iptr->val.fp));
+ panic("Internal error: builtin not found in table");
+ }
+ TYPECHECK_ARGS2(builtindesc->type_s2,builtindesc->type_s1);
+ }
+ maythrow = true;
break;
case ICMD_BUILTIN1:
- if (ISBUILTIN(builtin_new)) {
+ TYPECHECK_COUNT(stat_ins_builtin);
+ if (ISBUILTIN(BUILTIN_new)) {
+
if (iptr[-1].opc != ICMD_ACONST)
panic("illegal instruction: builtin_new without classinfo");
+ cls = (classinfo *) iptr[-1].val.a;
+ if (!cls->linked)
+ panic("Internal error: NEW with unlinked class");
+ /* The following check also forbids array classes and interfaces: */
+ if ((cls->flags & ACC_ABSTRACT) != 0)
+ panic("Invalid instruction: NEW creating instance of abstract class");
TYPEINFO_INIT_NEWOBJECT(dst->typeinfo,iptr);
}
- else if (ISBUILTIN(builtin_newarray_boolean)) {
+ 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? */
- maythrow = true; /* XXX better safe than sorry */
+ else {
+ TYPECHECK_COUNT(stat_ins_builtin_gen);
+ builtindesc = builtin_desc;
+ while (builtindesc->opcode && builtindesc->builtin
+ != iptr->val.fp) builtindesc++;
+ if (!builtindesc->opcode) {
+ dolog("Builtin not in table: %s",icmd_builtin_name(iptr->val.fp));
+ panic("Internal error: builtin not found in table");
+ }
+ TYPECHECK_ARGS1(builtindesc->type_s1);
+ }
+ maythrow = true;
break;
/****************************************/
- /* PRIMITIVE VARIABLE ACCESS */
+ /* SIMPLE EXCEPTION THROWING TESTS */
+
+ case ICMD_CHECKASIZE:
+ /* The argument to CHECKASIZE is typechecked by
+ * typechecking the array creation instructions. */
+
+ /* FALLTHROUGH! */
+ case ICMD_NULLCHECKPOP:
+ /* NULLCHECKPOP just requires that the stack top
+ * is an address. This is checked in stack.c */
+
+ maythrow = true;
+ break;
- 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 */
+ /* REPLACED BY OTHER OPCODES */
+#ifdef TYPECHECK_DEBUG
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));
+ case ICMD_AASTORE:
+ 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;
- /****************************************/
- /* 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;
+ case ICMD_READONLY_ARG:
+ case ICMD_CLEAR_ARGREN:
+ 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 */
- /* (These instructions are typechecked in analyse_stack.) */
+ /* UNCHECKED OPERATIONS */
- /* The following instructions may throw a runtime exception: */
+ /*********************************************
+ * Instructions below...
+ * *) don't operate on local variables,
+ * *) don't operate on references,
+ * *) don't operate on returnAddresses.
+ *
+ * (These instructions are typechecked in
+ * analyse_stack.)
+ ********************************************/
+
+ /* Instructions which may throw a runtime exception: */
case ICMD_IDIV:
case ICMD_IREM:
maythrow = true;
break;
- /* The following instructions never throw a runtime exception: */
- /* XXX only add cases for them in debug mode? */
-
+ /* Instructions which never throw a runtime exception: */
+#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 */
curstack = dst;
LOG("reaching exception handlers");
i = 0;
while (handlers[i]) {
- tbptr = handlers[i]->handler;
- TYPECHECK_REACH(REACH_THROW); /* XXX jsr chain? */
+ TYPECHECK_COUNT(stat_handlers_reached);
+ cls = handlers[i]->catchtype;
+ excstack.typeinfo.typeclass = (cls) ? cls
+ : class_java_lang_Throwable;
+ repeat |= typestate_reach(cd,rd, localbuf,bptr,
+ handlers[i]->handler,
+ &excstack,localset,
+ numlocals,
+ jsrencountered);
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(get_logfile(),curstack,numlocals,vtype,vinfo,(jsrchain) ? touched : NULL));
+ DOLOG(typestate_print(get_logfile(),curstack,localset,numlocals));
LOGNL; LOGFLUSH;
/* propagate stack and variables to the following block */
while (tbptr->flags == BBDELETED) {
tbptr++;
#ifdef TYPECHECK_DEBUG
- if ((tbptr-block) >= block_count)
+ if ((tbptr-block) >= m->basicblockcount)
panic("Control flow falls off the last block");
#endif
}
- TYPECHECK_REACH(REACH_STD);
+ 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);
- /* 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);
+ TYPECHECK_COUNT_FREQ(stat_iterations,count_iterations,STAT_ITERATIONS);
+ TYPECHECK_COUNTIF(jsrencountered,stat_typechecked_jsr);
+#endif
+#ifdef TYPECHECK_DEBUG
+ for (i=0; i<m->basicblockcount; ++i) {
+ if (m->basicblocks[i].flags != BBDELETED
+ && m->basicblocks[i].flags != BBUNDEF
+ && m->basicblocks[i].flags != BBFINISHED
+ && m->basicblocks[i].flags != BBTYPECHECK_UNDEF) /* typecheck may never reach
+ * some exception handlers,
+ * that's ok. */
+ {
+ LOG2("block L%03d has invalid flags after typecheck: %d",
+ m->basicblocks[i].debug_nr,m->basicblocks[i].flags);
+ panic("Invalid block flags after typecheck");
+ }
+ }
+#endif
+
+ /* Reset blocks we never reached */
+ for (i=0; i<m->basicblockcount; ++i) {
+ if (m->basicblocks[i].flags == BBTYPECHECK_UNDEF)
+ m->basicblocks[i].flags = BBFINISHED;
+ }
+
LOGimp("exiting typecheck");
+
+ /* just return methodinfo* to signal everything was ok */
+
+ return m;
}
#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:
+ */