X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=src%2Fvm%2Fjit%2Fverify%2Ftypecheck.c;h=900262a3512d3c92cffcbffb930e3ca1dfa70732;hb=d75b6037acf17c342166b9c9bd6e657dfdd12cd9;hp=98bd6cc445ed77e23e6226a9a03ed5a218960594;hpb=b94ab414dc75e0dab1033c4e8f2d64c73fd69066;p=cacao.git diff --git a/src/vm/jit/verify/typecheck.c b/src/vm/jit/verify/typecheck.c index 98bd6cc44..900262a35 100644 --- a/src/vm/jit/verify/typecheck.c +++ b/src/vm/jit/verify/typecheck.c @@ -1,9 +1,9 @@ -/* 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. @@ -26,30 +26,28 @@ Authors: Edwin Steiner - $Id: typecheck.c 701 2003-12-07 16:26:58Z edwin $ + $Id: typecheck.c 1735 2004-12-07 14:33:27Z twisti $ */ -#include "global.h" /* must be here because of CACAO_TYPECHECK */ +#include + +#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 */ @@ -69,10 +67,10 @@ bool typecheckverbose = false; #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)) @@ -95,7 +93,7 @@ bool typecheckverbose = false; #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) @@ -105,29 +103,14 @@ bool typecheckverbose = false; #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%3d: %8d\n",limit,array[limit]); +} - for (bi=0; bitype,(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 (kk) { + 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; +} -/* - * 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_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; itd[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; +} + + +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 - * maxlocals..number of local variables - * Used: - * macro_i - */ -#define TYPECHECK_COPYVARS \ - do { \ - LOG("TYPECHECK_COPYVARS"); \ - for (macro_i=0; macro_itarget != jsrtemp2->target) \ - panic("Merging different JSR subroutines"); \ - jsrtemp = jsrtemp->next; \ - jsrtemp2 = jsrtemp2->next; \ - } \ - jsrtemp = jsrbuffer[tbptr-block]; \ - if (jsrtemp) \ - for (macro_i=0; macro_itouched[i] |= touched[i]; \ - } } while (0) - -/* TYPECHECK_COPYSTACK: copy the typeinfos of the current stack to - * the input stack of the target block. - * Input: - * srcstack...current stack - * dststack...input stack of target block - */ -#define TYPECHECK_COPYSTACK \ - 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 - * maxlocals..number of local variables - * jsrbuffer..JSR target chain for each basic block - * Used: - * jsrtemp - */ -#define TYPECHECK_ADD_JSR \ - do { \ - LOG1("adding JSR to block %04d",(tbptr)-block); \ - jsrtemp = (jsr_record *) dump_alloc(sizeof(jsr_record)+(maxlocals-1)*sizeof(u1)); \ - jsrtemp->target = (tbptr); \ - jsrtemp->next = jsrchain; \ - jsrtemp->sbr_touched = NULL; \ - memset(&jsrtemp->touched,TOUCHED_NO,sizeof(u1)*maxlocals); \ - jsrbuffer[tbptr-block] = jsrtemp; \ - } while (0) +/****************************************************************************/ +/* 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 - * maxlocals..number of local variables - * jsrbuffer..JSR target chain for each basic block - * touched....current touched flags of local variables - * Used: - * jsrtemp - */ -#define TYPECHECK_COPYJSR(chain) \ - do { \ - LOG("TYPECHECK_COPYJSR"); \ - if (chain) { \ - jsrtemp = (jsr_record *) dump_alloc(sizeof(jsr_record)+(maxlocals-1)*sizeof(u1)); \ - jsrtemp->target = (chain)->target; \ - jsrtemp->next = (chain)->next; \ - jsrtemp->sbr_touched = NULL; \ - memcpy(&jsrtemp->touched,touched,sizeof(u1)*maxlocals); \ - jsrbuffer[tbptr-block] = jsrtemp; \ - } \ - else \ - jsrbuffer[tbptr-block] = NULL; \ - } while (0) +#define COPYTYPE(source,dest) \ + {if ((source)->type == TYPE_ADR) \ + TYPEINFO_COPY((source)->typeinfo,(dest)->typeinfo);} + +#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 @@ -441,56 +765,39 @@ struct jsr_record { * Input: * bptr.......current block * tbptr......target block - * dst........current output stack pointer (not needed for REACH_THROW) - * 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 + * 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(way) \ +#define TYPECHECK_REACH \ do { \ - LOG2("reaching block %04d (%d)",tbptr-block,way); \ - srcstack = dst; \ - dststack = tbptr->instack; \ - ttype = vartype + maxlocals*(tbptr-block); \ - tinfo = vartypeinfo + maxlocals*(tbptr-block); \ - if (tbptr->flags == BBTYPECHECK_UNDEF) { \ - /* This block is reached for the first time */ \ - if (way == REACH_JSR) { \ - TYPECHECK_ADD_JSR; \ - TYPECHECK_COPYVARS; \ - } \ - else { \ - TYPECHECK_COPYJSR(jsrchain); \ - TYPECHECK_COPYVARS; \ - } \ - 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("MUST REPEAT!");} \ - } \ + repeat |= typestate_reach(cd,rd, localbuf,bptr,tbptr,dst, \ + localset,numlocals,jsrencountered); \ LOG("done."); \ } while (0) +/* TYPECHECK_LEAVE: executed when the method is exited non-abruptly + * Input: + * class........class of the current method + * numlocals....number of local variables + * localset.....current local variable vectorset + * initmethod...true if this is an method + */ +#define TYPECHECK_LEAVE \ + do { \ + if (initmethod && m->class != class_java_lang_Object) { \ + /* check the marker variable */ \ + LOG("Checking marker"); \ + if (!typevectorset_checktype(localset,numlocals-1,TYPE_INT))\ + panic(" method does not initialize 'this'"); \ + } \ + } while (0) + /****************************************************************************/ /* typecheck() */ /****************************************************************************/ @@ -498,45 +805,57 @@ struct jsr_record { #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 */ - 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 numlocals; /* number of local variables */ + 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 */ + static utf *name_init; /* "" */ + bool initmethod; /* true if this is an "" 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()); @@ -548,16 +867,22 @@ typecheck() LOGimpSTR(" (class "); LOGimpSTRu(method->class->name); LOGimpSTR(")\n"); + LOGFLUSH; + + if (!name_init) + name_init = utf_new_char(""); + 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 @@ -575,71 +900,81 @@ typecheck() } /* 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"); - /* allocate the buffers for local variables */ - vartype = DMNEW(u1,maxlocals * (block_count+1)); - vartypeinfo = DMNEW(typeinfo,maxlocals * (block_count+1)); - touched = DMNEW(u1,maxlocals); - vtype = vartype + maxlocals * block_count; - vinfo = vartypeinfo + maxlocals * block_count; - memset(vartype,TYPE_VOID,maxlocals * (block_count+1) * sizeof(typeinfo)); - memset(vartypeinfo,0,maxlocals * (block_count+1) * sizeof(typeinfo)); - - LOG("Variable buffer initialized.\n"); - - /* allocate the buffer for storing JSR target chains */ - jsrbuffer = DMNEW(jsr_record*,block_count); - memset(jsrbuffer,0,block_count * sizeof(jsr_record*)); - jsrchain = NULL; + /* number of local variables */ - LOG("jsrbuffer initialized.\n"); + /* In methods we use an extra local variable to signal if + * the 'this' reference has been initialized. */ + numlocals = cd->maxlocals; + validlocals = numlocals; + if (initmethod) numlocals++; + + /* allocate the buffers for local variables */ + 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; - TYPEINFO_INIT_CLASSINFO(*tinfo,class); - tinfo++; + if (!(m->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); + else + 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, - maxlocals - (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; iexceptiontablelength; ++i) { - cls = extable[i].catchtype; - if (!cls) cls = class_java_lang_Throwable; - LOGSTR1("handler %i: ",i); LOGSTRu(cls->name); LOGNL; - TYPEINFO_INIT_CLASSINFO(extable[i].handler->instack->typeinfo,cls); - } + 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); @@ -652,66 +987,51 @@ typecheck() 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; - /* init variable types at the start of this block */ - for (i=0; itarget->debug_nr); - jsrtemp = jsrtemp->next; - } - LOGNL; - LOGFLUSH; - } -#endif - - subroutine = jsrbuffer[jsrchain->target - block]; - memcpy(touched,jsrchain->touched,sizeof(u1)*maxlocals); - } - else - subroutine = NULL; -#ifdef TYPECHECK_VERBOSE - if (typecheckverbose) { - if (subroutine) {LOGSTR1("subroutine L%03d\n",subroutine->target->debug_nr);LOGFLUSH;} - typeinfo_print_block(get_logfile(),curstack,vtype,vinfo,(jsrchain) ? touched : NULL); - LOGNL; LOGFLUSH; - } -#endif - /* determine the active exception handlers for this block */ /* XXX could use a faster algorithm with sorted lists or * something? */ len = 0; - for (i=0; iexceptiontablelength; ++i) { - if ((extable[i].start <= bptr) && (extable[i].end > bptr)) { - LOG1("active handler L%03d",extable[i].handler->debug_nr); - handlers[len++] = extable + i; + 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 */ + 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; itd[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; @@ -723,6 +1043,12 @@ typecheck() /* We just need to copy the typeinfo */ /* for slots containing addresses. */ + /* XXX We assume that the destination stack + * slots were continuously allocated in + * memory. (The current implementation in + * stack.c) + */ + case ICMD_DUP: COPYTYPE(curstack,dst); break; @@ -767,33 +1093,68 @@ typecheck() 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. */ - STORE_TYPE(iptr->op1,TYPE_ADDRESS); - TYPEINFO_COPY(curstack->typeinfo,vinfo[iptr->op1]); + if (handlers[0] && + TYPEINFO_IS_NEWOBJECT(curstack->typeinfo)) + panic("Storing uninitialized object in local variable inside try block"); + + if (TYPESTACK_IS_RETURNADDRESS(curstack)) + typevectorset_store_retaddr(localset,iptr->op1,&(curstack->typeinfo)); + else + typevectorset_store(localset,iptr->op1,TYPE_ADDRESS, + &(curstack->typeinfo)); break; /****************************************/ @@ -807,44 +1168,73 @@ typecheck() 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 */ - maythrow = true; /* 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)) @@ -852,43 +1242,51 @@ typecheck() { 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); - } } - /* XXX may throw? */ + 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)) @@ -963,37 +1361,69 @@ typecheck() 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; + + + /****************************************/ + /* 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; + /****************************************/ - /* OPERATIONS WITH UNCHECKED INPUT */ + /* 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 */ @@ -1028,30 +1458,32 @@ typecheck() 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; @@ -1059,7 +1491,7 @@ typecheck() 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; @@ -1069,8 +1501,8 @@ typecheck() /* 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; @@ -1080,37 +1512,33 @@ typecheck() 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"); - - superblockend = true; - break; - + goto return_tail; + case ICMD_IRETURN: - if (returntype != TYPE_INT) - panic("Return type mismatch"); - 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"); - 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"); - 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"); - 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; /****************************************/ @@ -1118,112 +1546,32 @@ typecheck() 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); - - /* set dst to the stack after the subroutine execution */ - /* XXX We assume (as in stack.c) that the - * subroutine returns the stack as it was - * before the JSR instruction. Is this - * correct? - */ - dst = iptr->dst; - - /* Find the jsr_record of the called subroutine */ - jsrtemp = jsrbuffer[tbptr - block]; - - /* Check if we already calculated (at least - * for one RET) which variables the - * subroutine touches. - */ - if (jsrtemp->sbr_touched) { - /* Calculate the local variables after the subroutine call */ - for (i=0; isbr_touched[i] != TOUCHED_NO) { - TOUCH_VARIABLE(i); - if ((vtype[i] = jsrtemp->sbr_vtype[i]) == TYPE_ADR) - TYPEINFO_CLONE(jsrtemp->sbr_vinfo[i],vinfo[i]); - } + 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); - /* 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; isbr_touched[i] |= touched[i]; - ttype = subroutine->sbr_vtype; - tinfo = subroutine->sbr_vinfo; - TYPECHECK_MERGEVARS; - } - else { - subroutine->sbr_touched = DMNEW(u1,maxlocals); - memcpy(subroutine->sbr_touched,touched,sizeof(u1)*maxlocals); - subroutine->sbr_vtype = DMNEW(u1,maxlocals); - memcpy(subroutine->sbr_vtype,vtype,sizeof(u1)*maxlocals); - subroutine->sbr_vinfo = DMNEW(typeinfo,maxlocals); - for (i=0; isbr_vinfo[i]); - } - /* XXX check if subroutine changed types? */ - - LOGSTR("subroutine touches:"); - DOLOG(typeinfo_print_locals(get_logfile(),subroutine->sbr_vtype,subroutine->sbr_vinfo, - subroutine->sbr_touched,maxlocals)); - LOGNL; LOGFLUSH; - - /* reach blocks after JSR statements */ - for (i=0; idebug_nr); - if (tbptr->iinstr[tbptr->icount - 1].opc != ICMD_JSR) - continue; - LOG("ends with JSR"); - if ((basicblock*) tbptr->iinstr[tbptr->icount - 1].target != subroutine->target) - continue; - tbptr++; - - LOG1("RET reaches block %04d",tbptr-block); - - /*TYPECHECK_REACH(REACH_RET);*/ - } - + repeat |= typestate_ret(cd,rd, localbuf,bptr,curstack, + localset,iptr->op1,numlocals); + superblockend = true; break; @@ -1234,15 +1582,28 @@ typecheck() case ICMD_INVOKESPECIAL: case ICMD_INVOKESTATIC: case ICMD_INVOKEINTERFACE: + TYPECHECK_COUNT(stat_ins_invoke); { - /* XXX check access rights */ - methodinfo *mi = (methodinfo*) iptr->val.a; - - /* XXX for INVOKESPECIAL: check if the invokation is done at all */ + bool specialmethod = (mi->name->text[0] == '<'); + bool callinginit = (opcode == ICMD_INVOKESPECIAL && mi->name == name_init); + instruction *ins; + classinfo *initclass; + + 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; @@ -1263,170 +1624,365 @@ typecheck() if (srcstack->type == TYPE_ADR) { LOGINFO(&(srcstack->typeinfo)); LOGINFO(pinfo + i); - if (!typeinfo_is_assignable(&(srcstack->typeinfo),pinfo+i)) - panic("Parameter reference type mismatch in method invocation"); + if (i==0 && callinginit) + { + /* first argument to method */ + if (!TYPEINFO_IS_NEWOBJECT(srcstack->typeinfo)) + panic("Calling on initialized object"); + + /* get the address of the NEW instruction */ + LOGINFO(&(srcstack->typeinfo)); + ins = (instruction*)TYPEINFO_NEWOBJECT_INSTRUCTION(srcstack->typeinfo); + initclass = (ins) ? (classinfo*)ins[-1].val.a : m->class; + LOGSTR("class: "); LOGSTRu(initclass->name); LOGNL; + + /* check type */ + /* (This is checked below.) */ +/* TYPEINFO_INIT_CLASSINFO(tempinfo,initclass); */ +/* if (!typeinfo_is_assignable(&tempinfo,pinfo+0)) */ +/* panic("Parameter reference type mismatch in invocation"); */ + } + else { + if (!typeinfo_is_assignable(&(srcstack->typeinfo),pinfo+i)) + panic("Parameter reference type mismatch in method invocation"); + } } 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"); TYPEINFO_COPY(rinfo,dst->typeinfo); } + + if (callinginit) { + LOG("replacing uninitialized object"); + /* replace uninitialized object type on stack */ + srcstack = dst; + while (srcstack) { + if (srcstack->type == TYPE_ADR + && TYPEINFO_IS_NEWOBJECT(srcstack->typeinfo) + && TYPEINFO_NEWOBJECT_INSTRUCTION(srcstack->typeinfo) == ins) + { + LOG("replacing uninitialized type on stack"); + + /* 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 */ + typevectorset_init_object(localset,ins,initclass,numlocals); + + /* initializing the 'this' reference? */ + if (!ins) { +#ifdef TYPECHECK_DEBUG + if (!initmethod) + panic("Internal error: calling on this in non- method."); +#endif + /* must be of current class or direct superclass */ + if (mi->class != m->class && mi->class != m->class->super) + panic(" calling of the wrong class"); + + /* set our marker variable to type int */ + LOG("setting marker"); + 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 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"); - TYPEINFO_INIT_CLASSINFO(dst->typeinfo,(classinfo *)iptr[-1].val.a); + 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; + 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 + /****************************************/ /* 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: + /********************************************* + * 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: + case ICMD_LDIV: + case ICMD_LREM: + maythrow = true; break; + + /* Instructions which never throw a runtime exception: */ +#if defined(TYPECHECK_DEBUG) || defined(TYPECHECK_STATISTICS) + case ICMD_NOP: + case ICMD_POP: + case ICMD_POP2: - /****************************************/ - /* ARITHMETIC AND CONVERSION */ + case ICMD_ICONST: + case ICMD_LCONST: + case ICMD_FCONST: + case ICMD_DCONST: - /* These instructions are typechecked in analyse_stack. */ - /* XXX only add cases for them in debug mode? */ + case ICMD_IFEQ_ICONST: + case ICMD_IFNE_ICONST: + case ICMD_IFLT_ICONST: + case ICMD_IFGE_ICONST: + case ICMD_IFGT_ICONST: + case ICMD_IFLE_ICONST: + case ICMD_ELSE_ICONST: case ICMD_IADD: case ICMD_ISUB: case ICMD_IMUL: - case ICMD_IDIV: - case ICMD_IREM: case ICMD_INEG: case ICMD_IAND: case ICMD_IOR: @@ -1437,8 +1993,6 @@ typecheck() case ICMD_LADD: case ICMD_LSUB: case ICMD_LMUL: - case ICMD_LDIV: - case ICMD_LREM: case ICMD_LNEG: case ICMD_LAND: case ICMD_LOR: @@ -1471,6 +2025,22 @@ typecheck() case ICMD_LUSHRCONST: case ICMD_LREMPOW2: + case ICMD_I2L: + case ICMD_I2F: + case ICMD_I2D: + case ICMD_L2I: + case ICMD_L2F: + case ICMD_L2D: + case ICMD_F2I: + case ICMD_F2L: + case ICMD_F2D: + case ICMD_D2I: + case ICMD_D2L: + case ICMD_D2F: + case ICMD_INT2BYTE: + case ICMD_INT2CHAR: + case ICMD_INT2SHORT: + case ICMD_LCMP: case ICMD_LCMPCONST: case ICMD_FCMPL: @@ -1491,46 +2061,16 @@ typecheck() case ICMD_FNEG: case ICMD_DNEG: - case ICMD_I2L: - case ICMD_I2F: - case ICMD_I2D: - case ICMD_L2I: - case ICMD_L2F: - case ICMD_L2D: - case ICMD_F2I: - case ICMD_F2L: - case ICMD_F2D: - case ICMD_D2I: - case ICMD_D2L: - case ICMD_D2F: - case ICMD_INT2BYTE: - case ICMD_INT2CHAR: - case ICMD_INT2SHORT: - - maythrow = true; /* XXX be more selective here */ - break; - - case ICMD_ICONST: - case ICMD_LCONST: - case ICMD_FCONST: - case ICMD_DCONST: - - case ICMD_IFEQ_ICONST: - case ICMD_IFNE_ICONST: - case ICMD_IFLT_ICONST: - case ICMD_IFGE_ICONST: - case ICMD_IFGT_ICONST: - case ICMD_IFLE_ICONST: - case ICMD_ELSE_ICONST: - + TYPECHECK_COUNT(stat_ins_unchecked); break; /****************************************/ default: - LOGSTR2("ICMD %d at %d\n", iptr->opc, (int)(iptr-instr)); + LOG2("ICMD %d at %d\n", iptr->opc, (int)(iptr-instr)); panic("Missing ICMD code during typecheck"); - } +#endif + } /* the output of this instruction becomes the current stack */ curstack = dst; @@ -1540,18 +2080,25 @@ typecheck() 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++; } } - + iptr++; } /* while instructions */ LOG("instructions done"); LOGSTR("RESULT=> "); - DOLOG(typeinfo_print_block(get_logfile(),curstack,vtype,vinfo,(jsrchain) ? touched : NULL)); + DOLOG(typestate_print(get_logfile(),curstack,localset,numlocals)); LOGNL; LOGFLUSH; /* propagate stack and variables to the following block */ @@ -1561,31 +2108,82 @@ typecheck() 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 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; ibasicblockcount; ++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; ibasicblockcount; ++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: + */