/* src/vm/jit/verify/typecheck.c - typechecking (part of bytecode verification)
- 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
+ Copyright (C) 1996-2005, 2006 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.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- 02111-1307, USA.
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
- Contact: cacao@complang.tuwien.ac.at
+ Contact: cacao@cacaojvm.org
Authors: Edwin Steiner
Changes: Christian Thalinger
- $Id: typecheck.c 3333 2005-10-04 18:55:12Z twisti $
+ $Id: typecheck.c 4357 2006-01-22 23:33:38Z twisti $
*/
+/*
+
+What's the purpose of the `typechecker`?
+----------------------------------------
+
+The typechecker analyses (the intermediate repr. of) the bytecode of
+each method and ensures that for each instruction the values on the
+stack and in local variables are of the correct type whenever the
+instruction is executed.
+
+type checking is a mandatory part of bytecode verification.
+
+
+How does the typechecker work?
+------------------------------
+
+The JVM stack and the local variables are not statically typed, so the
+typechecker has to *infer* the static types of stack slots and local
+variables at each point of the method. The JVM spec imposes a lot of
+restrictions on the bytecode in order to guarantee that this is always
+possible.
+
+Basically the typechecker solves the data flow equations of the method.
+This is done in the usual way for a forward data flow analysis: Starting
+from the entry point of the method the typechecker follows the CFG and
+records the type of each stack slot and local variable at each point[1].
+When two or more control flow paths merge at a point, the union of the
+types for each slot/variable is taken. The algorithm continues to follow
+all possible paths[2] until the recorded types do not change anymore (ie.
+the equations have been solved).
+
+If the solution has been reached and the resulting types are valid for
+all instructions, then type checking terminates with success, otherwise
+an exception is thrown.
+
+
+Why is this code so damn complicated?
+-------------------------------------
+
+Short answer: The devil's in the details.
+
+While the basic operation of the typechecker is no big deal, there are
+many properties of Java bytecode which make type checking hard. Some of
+them are not even addressed in the JVM spec. Some problems and their
+solutions:
+
+*) Finding a good representation of the union of two reference types is
+difficult because of multiple inheritance of interfaces.
+
+ Solution: The typeinfo system can represent such "merged" types by a
+ list of proper subclasses of a class. Example:
+
+ typeclass=java.lang.Object merged={ InterfaceA, InterfaceB }
+
+ represents the result of merging two interface types "InterfaceA"
+ and "InterfaceB".
+
+*) When the code of a method is verified, there may still be unresolved
+references to classes/methods/fields in the code, which we may not force
+to be resolved eagerly. (A similar problem arises because of the special
+checks for protected members.)
+
+ Solution: The typeinfo system knows how to deal with unresolved
+ class references. Whenever a check has to be performed for an
+ unresolved type, the type is annotated with constraints representing
+ the check. Later, when the type is resolved, the constraints are
+ checked. (See the constrain_unresolved_... and the resolve_...
+ methods.)[3]
+
+*) The boundaries of jsr subroutines are not well-defined. For a given
+instruction it may be impossible to tell whether it is part of a
+subroutine, or to which subroutine it belongs.
+
+ Solution: The typechecker implements a method developed by
+ Alessandro Coglio[4] which treats each returnAddress as a distinct
+ type that is not merged with other returnAddresses. This way, when a
+ RET instruction is reached, we know exactly which types to propagate
+ to which return target among the possible targets of the RET. The
+ downside of this method is, that for each slot/variable we must
+ store not just one type, but one type *for each possible use of the
+ returnAddresses* that currently are in a slot/variable.[5]
+
+*) Checks for uninitialized object instances are hard because after the
+invocation of <init> on an uninitialized object *all* slots/variables
+referring to this object (and exactly those slots/variables) must be
+marked as initialized.
+
+ Solution: The JVM spec describes a solution, which has been
+ implemented in this typechecker.
+
+
+--- Footnotes
+
+[1] Actually only the types of slots/variables at the start of each
+basic block are remembered. Within a basic block the algorithm only keeps
+the types of the slots/variables for the "current" instruction which is
+being analysed.
+
+[2] Actually the algorithm iterates through the basic block list until
+there are no more changes. Theoretically it would be wise to sort the
+basic blocks topologically beforehand, but the number of average/max
+iterations observed is so low, that this was not deemed necessary.
+
+[3] This is similar to a method proposed by: Alessandro Coglio et al., A
+Formal Specification of Java Class Loading, Technical Report, Kestrel
+Institute April 2000, revised July 2000
+http://www.kestrel.edu/home/people/coglio/loading.pdf
+An important difference is that Coglio's subtype constraints are checked
+after loading, while our constraints are checked when the field/method
+is accessed for the first time, so we can guarantee lexically correct
+error reporting.
+
+[4] Alessandro Coglio, Simple Verification Technique for Complex Java
+Bytecode Subroutines, 4th ECOOP Workshop on Formal Techniques for
+Java-like Programs, June 2002
+http://www.kestrel.edu/home/people/coglio/ftjp02.pdf
+
+[5] This is a major source of code complexity. The main data structures
+dealing with this are the "typevector set" and the typestack. The
+"typevector set" is a set of alternative typevectors, such that each
+typevector specifies the types of the local variables for a single
+combination of returnAddresses used. Thus we support full polymorphism
+of subroutines over the types of local variables. The typestack,
+however, does not support polymorphism, both for historical and JVM-spec
+reasons. A slot of the typestack may, however, contain multiple
+alternative returnAddresses, which is realized by a linked list hanging
+of the typeinfo of the stack slot.
+
+*/
#include <assert.h>
#include <string.h>
+#include "config.h"
#include "vm/types.h"
+#include "vm/global.h"
-#include "vm/global.h" /* must be here because of CACAO_TYPECHECK */
-
-#ifdef CACAO_TYPECHECK
+#ifdef ENABLE_VERIFIER
#include "mm/memory.h"
#include "toolbox/logging.h"
#include "vm/jit/patcher.h"
#include "vm/loader.h"
#include "vm/options.h"
-#include "vm/tables.h"
#include "vm/jit/jit.h"
#include "vm/jit/stack.h"
#include "vm/access.h"
static int stat_typechecked = 0;
static int stat_typechecked_jsr = 0;
+static int stat_methods_with_handlers = 0;
+static int stat_methods_maythrow = 0;
static int stat_iterations[STAT_ITERATIONS+1] = { 0 };
static int stat_reached = 0;
static int stat_copied = 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_maythrow = 0;
+static int stat_ins_stack = 0;
static int stat_ins_field = 0;
+static int stat_ins_field_unresolved = 0;
+static int stat_ins_field_uninitialized = 0;
static int stat_ins_invoke = 0;
+static int stat_ins_invoke_unresolved = 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_primitive_return = 0;
+static int stat_ins_areturn = 0;
+static int stat_ins_areturn_unresolved = 0;
+static int stat_ins_athrow = 0;
+static int stat_ins_athrow_unresolved = 0;
static int stat_ins_unchecked = 0;
static int stat_handlers_reached = 0;
static int stat_savedstack = 0;
+#define TYPECHECK_MARK(var) ((var) = true)
#define TYPECHECK_COUNT(cnt) (cnt)++
#define TYPECHECK_COUNTIF(cond,cnt) do{if(cond) (cnt)++;} while(0)
#define TYPECHECK_COUNT_FREQ(array,val,limit) \
void typecheck_print_statistics(FILE *file) {
fprintf(file,"typechecked methods: %8d\n",stat_typechecked);
- fprintf(file,"methods with JSR : %8d\n",stat_typechecked_jsr);
+ fprintf(file," with JSR : %8d\n",stat_typechecked_jsr);
+ fprintf(file," with handler(s): %8d\n",stat_methods_with_handlers);
+ fprintf(file," with throw(s) : %8d\n",stat_methods_maythrow);
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,"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," stack : %8d\n",stat_ins_stack);
fprintf(file," field access : %8d\n",stat_ins_field);
+ fprintf(file," (unresolved) : %8d\n",stat_ins_field_unresolved);
+ fprintf(file," (uninit.) : %8d\n",stat_ins_field_uninitialized);
fprintf(file," invocations : %8d\n",stat_ins_invoke);
- fprintf(file," load primitive : %8d\n",stat_ins_primload);
+ fprintf(file," (unresolved) : %8d\n",stat_ins_invoke_unresolved);
+ fprintf(file," load primitive : (currently not counted) %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," prim. return : %8d\n",stat_ins_primitive_return);
+ fprintf(file," areturn : %8d\n",stat_ins_areturn);
+ fprintf(file," (unresolved) : %8d\n",stat_ins_areturn_unresolved);
+ fprintf(file," athrow : %8d\n",stat_ins_athrow);
+ fprintf(file," (unresolved) : %8d\n",stat_ins_athrow_unresolved);
+ fprintf(file," unchecked : %8d\n",stat_ins_unchecked);
+ fprintf(file," maythrow : %8d\n",stat_ins_maythrow);
fprintf(file,"iterations used:\n");
print_freq(file,stat_iterations,STAT_ITERATIONS);
fprintf(file,"basic blocks per method / 10:\n");
#else
#define TYPECHECK_COUNT(cnt)
+#define TYPECHECK_MARK(var)
#define TYPECHECK_COUNTIF(cond,cnt)
#define TYPECHECK_COUNT_FREQ(array,val,limit)
#endif
-
/****************************************************************************/
-/* MACROS FOR STACK TYPE CHECKING */
+/* MACROS FOR THROWING EXCEPTIONS */
/****************************************************************************/
#define TYPECHECK_VERIFYERROR_ret(m,msg,retval) \
#define TYPECHECK_VERIFYERROR_main(msg) TYPECHECK_VERIFYERROR_ret(state.m,(msg),NULL)
#define TYPECHECK_VERIFYERROR_bool(msg) TYPECHECK_VERIFYERROR_ret(state->m,(msg),false)
+/****************************************************************************/
+/* MACROS FOR STACK SLOT TYPE CHECKING */
+/****************************************************************************/
+
#define TYPECHECK_CHECK_TYPE(sp,tp,msg) \
do { \
if ((sp)->type != (tp)) { \
bool repeat; /* if true, blocks are iterated over again */
bool initmethod; /* true if this is an "<init>" method */
bool jsrencountered; /* true if we there was a JSR */
+
+#ifdef TYPECHECK_STATISTICS
+ bool stat_maythrow; /* at least one instruction may throw */
+#endif
} verifier_state;
/****************************************************************************/
#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) \
-
/* typestack_copy **************************************************************
Copy the types on the given stack to the destination stack.
int k;
for (;dst; dst=dst->prev, y=y->prev) {
+ /* XXX only check the following two in debug mode? */
if (!y) {
*exceptionptr = new_verifyerror(state->m,"Stack depth mismatch");
return false;
}
}
-/* 'a' and 'b' are assumed to have passed typestack_canmerge! */
+/* typestack_separable_with ****************************************************
+
+ This function answers the question: If variant 'kb' of typestack 'b' is
+ added to typestack 'a', will the result be separable?
+
+ A typestack is called 'separable' if it has at least one slot of type
+ returnAddress that contains at least two different return addresses.
+ (ie. a RET using the value in this slot could go to more than one target)
+
+ IN:
+ a................the first typestack
+ b................the second typestack
+ kb...............the k-index of the variant that should be selected
+ from typestack 'b'
+
+ OUT:
+ true.............the result would be separable
+ false............the result would not be separable
+
+ PRE-CONDITION:
+ 'a' and 'b' are assumed to have passed typestack_canmerge!
+
+*******************************************************************************/
+
static bool
typestack_separable_with(stackptr a,stackptr b,int kb)
{
return false;
}
-/* 'a' and 'b' are assumed to have passed typestack_canmerge! */
+/* typestack_separable_from ****************************************************
+
+ This function answers the question: Is variant 'ka' of typestack 'a'
+ separable from variant 'kb' of typestack 'b'?
+
+ Two variants of typestacks are called 'separable' from each other, if there
+ is at least one slot for which the variants contain different return addresses.
+ (ie. a RET using the value in this slot would go to one target in the first
+ variant and to another target in the second variant)
+
+ IN:
+ a................the first typestack
+ ka...............the k-index of the variant that should be selected
+ from typestack 'a'
+ b................the second typestack
+ kb...............the k-index of the variant that should be selected
+ from typestack 'b'
+
+ OUT:
+ true.............the variants are separable
+ false............the variants are not separable
+
+ PRE-CONDITION:
+ 'a' and 'b' are assumed to have passed typestack_canmerge!
+
+*******************************************************************************/
+
static bool
typestack_separable_from(stackptr a,int ka,stackptr b,int kb)
{
if (!typestack_copy(state,destblock->instack,ystack,yloc))
return false;
- COPY_TYPEVECTORSET(yloc,destloc,state->numlocals);
+ typevectorset_copy_inplace(yloc,destloc,state->numlocals);
changed = true;
}
else {
return true;
}
-/****************************************************************************/
-/* MACROS FOR LOCAL VARIABLE CHECKING */
-/****************************************************************************/
+/* typestate_save_instack ******************************************************
+
+ Save the input stack of the current basic block in the "savedstackbuf"
+ of the verifier state.
-#define INDEX_ONEWORD(num) \
- do { if((num)<0 || (num)>=state->validlocals) \
- TYPECHECK_VERIFYERROR_bool("Invalid local variable index"); } while (0)
-#define INDEX_TWOWORD(num) \
- do { if((num)<0 || ((num)+1)>=state->validlocals) \
- TYPECHECK_VERIFYERROR_bool("Invalid local variable index"); } while (0)
+ This function must be called before an instruction modifies a stack slot
+ that happens to be part of the instack of the current block. In such
+ cases the instack of the block must be saved, and restored at the end
+ of the analysis of this basic block, so that the instack again reflects
+ the *input* to this basic block (and does not randomly contain types
+ that appear within the block).
-#define STORE_ONEWORD(num,type) \
- do {typevectorset_store(state->localset,num,type,NULL);} while(0)
+ IN:
+ state............current state of the verifier
-#define STORE_TWOWORD(num,type) \
- do {typevectorset_store_twoword(state->localset,num,type);} while(0)
+*******************************************************************************/
+static void
+typestate_save_instack(verifier_state *state)
+{
+ stackptr sp;
+ stackptr dest;
+ s4 i;
+
+ LOG("saving input stack types");
+ if (!state->savedstackbuf) {
+ LOG("allocating savedstack buffer");
+ state->savedstackbuf = DMNEW(stackelement, state->cd->maxstack);
+ state->savedstackbuf->prev = NULL;
+ for (i = 1; i < state->cd->maxstack; ++i)
+ state->savedstackbuf[i].prev = state->savedstackbuf+(i-1);
+ }
+ sp = state->savedstack = state->bptr->instack;
+ dest = state->bptr->instack = state->savedstackbuf + (state->bptr->indepth-1);
+
+ for(; sp; sp=sp->prev, dest=dest->prev) {
+ dest->type = sp->type;
+ TYPEINFO_COPY(sp->typeinfo,dest->typeinfo);
+ }
+}
-#ifdef TYPECHECK_VERBOSE
-#define WORDCHECKFAULT \
- do { \
- dolog("localset->td index: %ld\ninstruction belongs to:%s.%s, outermethod:%s.%s\n", \
- state->iptr->op1,state->iptr->method->class->name->text, \
- state->iptr->method->name->text,state->m->class->name->text,state->m->name->text); \
- show_icmd(state->iptr++, false); \
- show_icmd(state->iptr, false); \
- } while (0)
-#else
-#define WORDCHECKFAULT
-#endif
+/* typestate_restore_instack ***************************************************
+
+ Restore the input stack of the current basic block that has been previously
+ saved by `typestate_save_instack`.
+ IN:
+ state............current state of the verifier
-#define CHECK_ONEWORD(num,tp) \
- do {TYPECHECK_COUNT(stat_ins_primload); \
- if (state->jsrencountered) { \
- if (!typevectorset_checktype(state->localset,num,tp)) { \
- WORDCHECKFAULT; \
- TYPECHECK_VERIFYERROR_bool("Variable type mismatch"); \
- } \
- } \
- else { \
- if (state->localset->td[num].type != tp) { \
- TYPECHECK_VERIFYERROR_bool("Variable type mismatch"); \
- WORDCHECKFAULT; \
- } \
- } \
- } while(0)
-
-#define CHECK_TWOWORD(num,type) \
- do {TYPECHECK_COUNT(stat_ins_primload); \
- if (!typevectorset_checktype(state->localset,num,type)) { \
- WORDCHECKFAULT; \
- TYPECHECK_VERIFYERROR_bool("Variable type mismatch"); \
- } \
- } while(0)
+*******************************************************************************/
+
+static void
+typestate_restore_instack(verifier_state *state)
+{
+ stackptr sp;
+ stackptr dest;
+
+ TYPECHECK_COUNT(stat_savedstack);
+ LOG("restoring saved instack");
+
+ sp = state->bptr->instack;
+ dest = state->savedstack;
+ for(; sp; sp=sp->prev, dest=dest->prev) {
+ dest->type = sp->type;
+ TYPEINFO_COPY(sp->typeinfo,dest->typeinfo);
+ }
+ state->bptr->instack = state->savedstack;
+ state->savedstack = NULL;
+}
/****************************************************************************/
/* MISC MACROS */
#define ISBUILTIN(v) (bte->fp == (functionptr) (v))
-/* TYPECHECK_LEAVE: executed when the method is exited non-abruptly
- * Input:
- * class........class of the current method
- * state........verifier state
- */
-#define TYPECHECK_LEAVE \
- do { \
- if (state->initmethod && state->m->class != class_java_lang_Object) { \
- /* check the marker variable */ \
- LOG("Checking <init> marker"); \
- if (!typevectorset_checktype(state->localset,state->numlocals-1,TYPE_INT))\
- TYPECHECK_VERIFYERROR_bool("<init> method does not initialize 'this'"); \
- } \
- } while (0)
-
/* verify_invocation ***********************************************************
Verify an ICMD_INVOKE* instruction.
if (specialmethod && !callinginit)
TYPECHECK_VERIFYERROR_bool("Invalid invocation of special method");
- /* record subtype constraints for parameters */
-
- if (!constrain_unresolved_method(um,state->m->class,state->m,state->iptr,state->curstack))
- return false; /* XXX maybe wrap exception */
-
- /* try to resolve the method lazily */
-
- if (!resolve_method(um,resolveLazy,(methodinfo **) &(state->iptr[0].val.a)))
- return false;
-
-#if 0
- if (opcode == ICMD_INVOKESPECIAL) {
- /* XXX for INVOKESPECIAL: check if the invokation is done at all */
-
- /* (If callinginit the class is checked later.) */
- if (!callinginit) {
- /* XXX classrefs */
- if (!builtin_isanysubclass(myclass,mi->class))
- XXXTYPECHECK_VERIFYERROR_bool("Illegal instruction: INVOKESPECIAL calling non-superclass method");
- }
- }
-#endif
-
/* allocate parameters if necessary */
if (!md->params)
LOGINFO(&(stack->typeinfo));
ins = (instruction*)TYPEINFO_NEWOBJECT_INSTRUCTION(stack->typeinfo);
if (ins)
- initclass = CLASSREF_OR_CLASSINFO(ins[-1].val.a);
+ initclass = CLASSREF_OR_CLASSINFO(ins[-1].target);
else
initclass.cls = state->m->class;
LOGSTR("class: "); LOGNAME(initclass); LOGNL;
}
}
+ else {
+ /* non-adress argument. if this is the first argument and we are */
+ /* invoking an instance method, this is an error. */
+ if (i==0 && opcode != ICMD_INVOKESTATIC) {
+ TYPECHECK_VERIFYERROR_bool("Parameter type mismatch for 'this' argument");
+ }
+ }
LOG("ok");
if (i)
* we are going to replace.
*/
if (stack <= state->bptr->instack && !state->savedstack)
- {
- stackptr sp;
- stackptr copy;
- LOG("saving input stack types");
- if (!state->savedstackbuf) {
- LOG("allocating savedstack buffer");
- state->savedstackbuf = DMNEW(stackelement, state->cd->maxstack);
- state->savedstackbuf->prev = NULL;
- for (i = 1; i < state->cd->maxstack; ++i)
- state->savedstackbuf[i].prev = state->savedstackbuf+(i-1);
- }
- sp = state->savedstack = state->bptr->instack;
- copy = state->bptr->instack = state->savedstackbuf + (state->bptr->indepth-1);
- TYPESTACK_COPY(sp,copy);
- }
+ typestate_save_instack(state);
if (!typeinfo_init_class(&(stack->typeinfo),initclass))
return false;
/* initializing the 'this' reference? */
if (!ins) {
+ classinfo *cls;
TYPECHECK_ASSERT(state->initmethod);
- /* must be <init> of current class or direct superclass */
- /* XXX check with classrefs */
-#if 0
- if (mi->class != m->class && mi->class != m->class->super.cls)
+ /* { we are initializing the 'this' reference } */
+ /* must be <init> of current class or direct superclass */
+ /* the current class is linked, so must be its superclass. thus we can be */
+ /* sure that resolving will be trivial. */
+ if (!resolve_classref(state->m,mref->classref,resolveLazy,false,true,&cls))
+ return false; /* exception */
+
+ /* if lazy resolving did not succeed, it's not one of the allowed classes */
+ /* otherwise we check it directly */
+ if (cls == NULL || (cls != state->m->class && cls != state->m->class->super.cls)) {
TYPECHECK_VERIFYERROR_bool("<init> calling <init> of the wrong class");
-#endif
+ }
/* set our marker variable to type int */
LOG("setting <init> marker");
typevectorset_store(state->localset,state->numlocals-1,TYPE_INT,NULL);
}
else {
- /* initializing an instance created with NEW */
- /* XXX is this strictness ok? */
- /* XXX check with classrefs */
-#if 0
- if (mi->class != initclass.cls)
- TYPECHECK_VERIFYERROR_bool("Calling <init> method of the wrong class");
-#endif
+ /* { we are initializing an instance created with NEW } */
+ if ((IS_CLASSREF(initclass) ? initclass.ref->name : initclass.cls->name) != mref->classref->name) {
+ TYPECHECK_VERIFYERROR_bool("wrong <init> called for uninitialized reference");
+ }
+ }
+ }
+
+ /* record subtype constraints for parameters */
+
+ if (!constrain_unresolved_method(um,state->m->class,state->m,state->iptr,state->curstack))
+ return false; /* XXX maybe wrap exception */
+
+ /* try to resolve the method lazily */
+
+ if (!resolve_method(um,resolveLazy,(methodinfo **) &(state->iptr[0].val.a)))
+ return false;
+
+ return true;
+}
+
+/* verify_generic_builtin ******************************************************
+
+ Verify the call of a generic builtin method.
+
+ IN:
+ state............the current state of the verifier
+
+ RETURN VALUE:
+ true.............successful verification,
+ false............an exception has been thrown.
+
+*******************************************************************************/
+
+static bool
+verify_generic_builtin(verifier_state *state)
+{
+ builtintable_entry *bte;
+ s4 i;
+ u1 rtype;
+ methoddesc *md;
+ stackptr sp;
+
+ TYPECHECK_COUNT(stat_ins_builtin_gen);
+
+ bte = (builtintable_entry *) state->iptr[0].val.a;
+ md = bte->md;
+ i = md->paramcount;
+
+ /* check the types of the arguments on the stack */
+
+ sp = state->curstack;
+ for (i--; i >= 0; i--) {
+ if (sp->type != md->paramtypes[i].type) {
+ TYPECHECK_VERIFYERROR_bool("parameter type mismatch for builtin method");
}
+
+#ifdef TYPECHECK_DEBUG
+ /* generic builtins may only take primitive types and java.lang.Object references */
+ if (sp->type == TYPE_ADR && md->paramtypes[i].classref->name != utf_java_lang_Object) {
+ *exceptionptr = new_internalerror("generic builtin method with non-generic reference parameter");
+ return false;
+ }
+#endif
+
+ sp = sp->prev;
+ }
+
+ /* check the return type */
+
+ rtype = md->returntype.type;
+ if (rtype != TYPE_VOID) {
+ stackptr dst;
+
+ dst = state->iptr->dst;
+ if (rtype != dst->type)
+ TYPECHECK_VERIFYERROR_bool("Return type mismatch in generic builtin invocation");
+ if (!typeinfo_init_from_typedesc(&(md->returntype),NULL,&(dst->typeinfo)))
+ return false;
}
+
return true;
}
/* verify_builtin **************************************************************
- Verify the call of a builtin function.
+ Verify the call of a builtin method.
IN:
state............the current state of the verifier
verify_builtin(verifier_state *state)
{
builtintable_entry *bte;
- classinfo *cls;
+ classref_or_classinfo cls;
stackptr dst; /* output stack of current instruction */
bte = (builtintable_entry *) state->iptr[0].val.a;
dst = state->iptr->dst;
- if (ISBUILTIN(BUILTIN_new) || ISBUILTIN(PATCHER_builtin_new)) {
+ /* XXX this is an ugly if-chain but twisti did not want a function */
+ /* pointer in builtintable_entry for this, so here you go.. ;) */
+
+ if (ISBUILTIN(BUILTIN_new)) {
if (state->iptr[-1].opc != ICMD_ACONST)
- TYPECHECK_VERIFYERROR_bool("illegal instruction: builtin_new without classinfo");
- cls = (classinfo *) state->iptr[-1].val.a;
-#ifdef XXX
- TYPECHECK_ASSERT(!cls || cls->linked);
- /* The following check also forbids array classes and interfaces: */
- if ((cls->flags & ACC_ABSTRACT) != 0)
- TYPECHECK_VERIFYERROR_bool("Invalid instruction: NEW creating instance of abstract class");
-#endif
+ TYPECHECK_VERIFYERROR_bool("illegal instruction: builtin_new without class");
+ cls.any = state->iptr[-1].target;
TYPEINFO_INIT_NEWOBJECT(dst->typeinfo,state->iptr);
}
else if (ISBUILTIN(BUILTIN_newarray_boolean)) {
}
else if (ISBUILTIN(BUILTIN_newarray))
{
- vftbl_t *vft;
- TYPECHECK_INT(state->curstack->prev);
- if (state->iptr[-1].opc != ICMD_ACONST)
- TYPECHECK_VERIFYERROR_bool("illegal instruction: builtin_newarray without classinfo");
- vft = (vftbl_t *)state->iptr[-1].val.a;
- if (!vft)
- TYPECHECK_VERIFYERROR_bool("ANEWARRAY with unlinked class");
- if (!vft->arraydesc)
- TYPECHECK_VERIFYERROR_bool("ANEWARRAY with non-array class");
- TYPEINFO_INIT_CLASSINFO(dst->typeinfo,vft->class);
- }
- else if (ISBUILTIN(PATCHER_builtin_newarray))
- {
- TYPECHECK_INT(state->curstack->prev);
- if (state->iptr[-1].opc != ICMD_ACONST)
- TYPECHECK_VERIFYERROR_bool("illegal instruction: builtin_newarray without classinfo");
- if (!typeinfo_init_class(&(dst->typeinfo),CLASSREF_OR_CLASSINFO(state->iptr[-1].val.a)))
- return false;
- }
- else if (ISBUILTIN(BUILTIN_newarray))
- {
- vftbl_t *vft;
TYPECHECK_INT(state->curstack->prev);
- if (state->iptr[-1].opc != ICMD_ACONST)
- TYPECHECK_VERIFYERROR_bool("illegal instruction: builtin_newarray without classinfo");
- vft = (vftbl_t *)state->iptr[-1].val.a;
- if (!vft)
- TYPECHECK_VERIFYERROR_bool("ANEWARRAY with unlinked class");
- if (!vft->arraydesc)
- TYPECHECK_VERIFYERROR_bool("ANEWARRAY with non-array class");
- TYPEINFO_INIT_CLASSINFO(dst->typeinfo,vft->class);
+ if (state->iptr[-1].opc != ICMD_ACONST || !state->iptr[-1].target)
+ TYPECHECK_VERIFYERROR_bool("illegal instruction: builtin_newarray without class");
+ /* XXX check that it is an array class(ref) */
+ typeinfo_init_class(&(dst->typeinfo),CLASSREF_OR_CLASSINFO(state->iptr[-1].target));
}
else if (ISBUILTIN(BUILTIN_arrayinstanceof))
{
- vftbl_t *vft;
TYPECHECK_ADR(state->curstack->prev);
if (state->iptr[-1].opc != ICMD_ACONST)
- TYPECHECK_VERIFYERROR_bool("illegal instruction: builtin_arrayinstanceof without classinfo");
- vft = (vftbl_t *)state->iptr[-1].val.a;
- if (!vft)
- TYPECHECK_VERIFYERROR_bool("INSTANCEOF with unlinked class");
- if (!vft->arraydesc)
- TYPECHECK_VERIFYERROR_bool("internal error: builtin_arrayinstanceof with non-array class");
+ TYPECHECK_VERIFYERROR_bool("illegal instruction: builtin_arrayinstanceof without class");
+ /* XXX check that it is an array class(ref) */
}
else {
-#if 0
- /* XXX put these checks in a function */
- TYPECHECK_COUNT(stat_ins_builtin_gen);
- builtindesc = builtin_desc;
- while (builtindesc->opcode && builtindesc->builtin
- != state->iptr->val.fp) builtindesc++;
- if (!builtindesc->opcode) {
- dolog("Builtin not in table: %s",icmd_builtin_name(state->iptr->val.fp));
- TYPECHECK_ASSERT(false);
- }
- TYPECHECK_ARGS3(builtindesc->type_s3,builtindesc->type_s2,builtindesc->type_s1);
-#endif
+ return verify_generic_builtin(state);
+ }
+ return true;
+}
+
+/* verify_multianewarray *******************************************************
+
+ Verify a MULTIANEWARRAY instruction.
+
+ IN:
+ state............the current state of the verifier
+
+ RETURN VALUE:
+ true.............successful verification,
+ false............an exception has been thrown.
+
+*******************************************************************************/
+
+static bool
+verify_multianewarray(verifier_state *state)
+{
+ stackptr sp;
+ classinfo *arrayclass;
+ arraydescriptor *desc;
+ s4 i;
+
+ /* check the array lengths on the stack */
+ i = state->iptr[0].op1;
+ if (i < 1)
+ TYPECHECK_VERIFYERROR_bool("Illegal dimension argument");
+
+ sp = state->curstack;
+ while (i--) {
+ if (!sp)
+ TYPECHECK_VERIFYERROR_bool("Unable to pop operand off an empty stack");
+ TYPECHECK_INT(sp);
+ sp = sp->prev;
}
+
+ /* check array descriptor */
+ if (state->iptr[0].val.a != NULL) {
+ /* the array class reference has already been resolved */
+ arrayclass = (classinfo *) state->iptr[0].val.a;
+ if (!arrayclass)
+ TYPECHECK_VERIFYERROR_bool("MULTIANEWARRAY with unlinked class");
+ if ((desc = arrayclass->vftbl->arraydesc) == NULL)
+ TYPECHECK_VERIFYERROR_bool("MULTIANEWARRAY with non-array class");
+ if (desc->dimension < state->iptr[0].op1)
+ TYPECHECK_VERIFYERROR_bool("MULTIANEWARRAY dimension to high");
+
+ /* set the array type of the result */
+ typeinfo_init_classinfo(&(state->iptr->dst->typeinfo), arrayclass);
+ }
+ else {
+ const char *p;
+ constant_classref *cr;
+
+ /* the array class reference is still unresolved */
+ /* check that the reference indicates an array class of correct dimension */
+ cr = (constant_classref *) state->iptr[0].target;
+ i = 0;
+ p = cr->name->text;
+ while (p[i] == '[')
+ i++;
+ /* { the dimension of the array class == i } */
+ if (i < 1)
+ TYPECHECK_VERIFYERROR_bool("MULTIANEWARRAY with non-array class");
+ if (i < state->iptr[0].op1)
+ TYPECHECK_VERIFYERROR_bool("MULTIANEWARRAY dimension to high");
+
+ /* set the array type of the result */
+ if (!typeinfo_init_class(&(state->iptr->dst->typeinfo),CLASSREF_OR_CLASSINFO(state->iptr[0].target)))
+ return false;
+ }
+
+ /* everything ok */
return true;
}
static bool
verify_basic_block(verifier_state *state)
{
- stackptr srcstack; /* source stack for copying and merging */
int opcode; /* current opcode */
int len; /* for counting instructions, etc. */
bool superblockend; /* true if no fallthrough to next block */
state->handlers[len] = NULL;
/* init variable types at the start of this block */
- COPY_TYPEVECTORSET(MGET_TYPEVECTOR(state->localbuf,b_index,state->numlocals),
+ typevectorset_copy_inplace(MGET_TYPEVECTOR(state->localbuf,b_index,state->numlocals),
state->localset,state->numlocals);
/* XXX FIXME FOR INLINING */
- if(!useinlining) {
+ if (!useinlining) {
if (state->handlers[0])
for (i=0; i<state->numlocals; ++i)
if (state->localset->td[i].type == TYPE_ADR
/* of eclipse 3.0.2 */
if (TYPEINFO_NEWOBJECT_INSTRUCTION(state->localset->td[i].info) != NULL) {
/*show_icmd_method(state->m, state->cd, state->rd);*/
- printf("Uninitialized variale: %d, block: %d\n", i, state->bptr->debug_nr);
+ printf("Uninitialized variable: %d, block: %d\n", i, state->bptr->debug_nr);
TYPECHECK_VERIFYERROR_bool("Uninitialized object in local variable inside try block");
}
}
*/
case ICMD_DUP:
+ TYPECHECK_COUNT(stat_ins_stack);
COPYTYPE(state->curstack,dst);
break;
case ICMD_DUP_X1:
+ TYPECHECK_COUNT(stat_ins_stack);
COPYTYPE(state->curstack,dst);
COPYTYPE(state->curstack,dst-2);
COPYTYPE(state->curstack->prev,dst-1);
break;
case ICMD_DUP_X2:
+ TYPECHECK_COUNT(stat_ins_stack);
COPYTYPE(state->curstack,dst);
COPYTYPE(state->curstack,dst-3);
COPYTYPE(state->curstack->prev,dst-1);
break;
case ICMD_DUP2:
+ TYPECHECK_COUNT(stat_ins_stack);
COPYTYPE(state->curstack,dst);
COPYTYPE(state->curstack->prev,dst-1);
break;
case ICMD_DUP2_X1:
+ TYPECHECK_COUNT(stat_ins_stack);
COPYTYPE(state->curstack,dst);
COPYTYPE(state->curstack->prev,dst-1);
COPYTYPE(state->curstack,dst-3);
break;
case ICMD_DUP2_X2:
+ TYPECHECK_COUNT(stat_ins_stack);
COPYTYPE(state->curstack,dst);
COPYTYPE(state->curstack->prev,dst-1);
COPYTYPE(state->curstack,dst-4);
break;
case ICMD_SWAP:
+ TYPECHECK_COUNT(stat_ins_stack);
COPYTYPE(state->curstack,dst-1);
COPYTYPE(state->curstack->prev,dst);
break;
/****************************************/
/* PRIMITIVE VARIABLE ACCESS */
- case ICMD_ILOAD: CHECK_ONEWORD(state->iptr->op1,TYPE_INT); break;
- case ICMD_FLOAD: CHECK_ONEWORD(state->iptr->op1,TYPE_FLOAT); break;
- case ICMD_IINC: CHECK_ONEWORD(state->iptr->op1,TYPE_INT); break;
- case ICMD_LLOAD: CHECK_TWOWORD(state->iptr->op1,TYPE_LONG); break;
- case ICMD_DLOAD: CHECK_TWOWORD(state->iptr->op1,TYPE_DOUBLE); break;
-
- case ICMD_FSTORE: STORE_ONEWORD(state->iptr->op1,TYPE_FLOAT); break;
- case ICMD_ISTORE: STORE_ONEWORD(state->iptr->op1,TYPE_INT); break;
- case ICMD_LSTORE: STORE_TWOWORD(state->iptr->op1,TYPE_LONG); break;
- case ICMD_DSTORE: STORE_TWOWORD(state->iptr->op1,TYPE_DOUBLE); break;
+ case ICMD_ILOAD: if (!typevectorset_checktype(state->localset,state->iptr->op1,TYPE_INT))
+ TYPECHECK_VERIFYERROR_bool("Local variable type mismatch");
+ break;
+ case ICMD_IINC: if (!typevectorset_checktype(state->localset,state->iptr->op1,TYPE_INT))
+ TYPECHECK_VERIFYERROR_bool("Local variable type mismatch");
+ break;
+ case ICMD_FLOAD: if (!typevectorset_checktype(state->localset,state->iptr->op1,TYPE_FLOAT))
+ TYPECHECK_VERIFYERROR_bool("Local variable type mismatch");
+ break;
+ case ICMD_LLOAD: if (!typevectorset_checktype(state->localset,state->iptr->op1,TYPE_LONG))
+ TYPECHECK_VERIFYERROR_bool("Local variable type mismatch");
+ break;
+ case ICMD_DLOAD: if (!typevectorset_checktype(state->localset,state->iptr->op1,TYPE_DOUBLE))
+ TYPECHECK_VERIFYERROR_bool("Local variable type mismatch");
+ break;
+
+ case ICMD_ISTORE: typevectorset_store(state->localset,state->iptr->op1,TYPE_INT,NULL); break;
+ case ICMD_FSTORE: typevectorset_store(state->localset,state->iptr->op1,TYPE_FLOAT,NULL); break;
+ case ICMD_LSTORE: typevectorset_store_twoword(state->localset,state->iptr->op1,TYPE_LONG); break;
+ case ICMD_DSTORE: typevectorset_store_twoword(state->localset,state->iptr->op1,TYPE_DOUBLE); break;
/****************************************/
/* LOADING ADDRESS FROM VARIABLE */
if (!resolve_field(uf,resolveLazy,fieldinfop))
return false;
- /* we need a patcher, so this is not a leafmethod */
-#if defined(__MIPS__) || defined(__POWERPC__)
- if (!*fieldinfop || !(*fieldinfop)->class->initialized)
- state->cd->method->isleafmethod = false;
-#endif
+ TYPECHECK_COUNTIF(!*fieldinfop,stat_ins_field_unresolved);
+ TYPECHECK_COUNTIF(*fieldinfop && !(*fieldinfop)->class->initialized,stat_ins_field_uninitialized);
maythrow = true;
break;
/* ADDRESS CONSTANTS */
case ICMD_ACONST:
- if (state->iptr->val.a == NULL)
- TYPEINFO_INIT_NULLTYPE(dst->typeinfo);
- else
- /* string constant (or constant for builtin function) */
- TYPEINFO_INIT_CLASSINFO(dst->typeinfo,class_java_lang_String);
+ if (state->iptr->target) {
+ /* a java.lang.Class reference */
+ TYPEINFO_INIT_JAVA_LANG_CLASS(dst->typeinfo,(constant_classref *)state->iptr->target);
+ }
+ else {
+ if (state->iptr->val.a == NULL)
+ TYPEINFO_INIT_NULLTYPE(dst->typeinfo);
+ else {
+ /* string constant (or constant for builtin function) */
+ typeinfo_init_classinfo(&(dst->typeinfo),class_java_lang_String);
+ }
+ }
break;
/****************************************/
cls = (classinfo *) state->iptr[0].val.a;
if (cls)
- TYPEINFO_INIT_CLASSINFO(dst->typeinfo,cls);
+ typeinfo_init_classinfo(&(dst->typeinfo),cls);
else
if (!typeinfo_init_class(&(dst->typeinfo),CLASSREF_OR_CLASSINFO(state->iptr[0].target)))
return false;
maythrow = true;
break;
- case ICMD_ARRAYCHECKCAST:
- TYPECHECK_ADR(state->curstack);
- /* returnAddress is not allowed */
- if (!TYPEINFO_IS_REFERENCE(state->curstack->typeinfo))
- TYPECHECK_VERIFYERROR_bool("Illegal instruction: ARRAYCHECKCAST on non-reference");
-
- if (state->iptr[0].op1) {
- /* a resolved array class */
- cls = ((vftbl_t *)state->iptr[0].target)->class;
- TYPEINFO_INIT_CLASSINFO(dst->typeinfo,cls);
- }
- else {
- /* an unresolved array class reference */
- if (!typeinfo_init_class(&(dst->typeinfo),CLASSREF_OR_CLASSINFO(state->iptr[0].target)))
- return false;
- }
- maythrow = true;
- break;
-
case ICMD_INSTANCEOF:
TYPECHECK_ADR(state->curstack);
/* returnAddress is not allowed */
/* ADDRESS RETURNS AND THROW */
case ICMD_ATHROW:
+ TYPECHECK_COUNT(stat_ins_athrow);
r = typeinfo_is_assignable_to_class(&state->curstack->typeinfo,
CLASSREF_OR_CLASSINFO(class_java_lang_Throwable));
if (r == typecheck_FALSE)
TYPECHECK_VERIFYERROR_bool("illegal instruction: ATHROW on non-Throwable");
if (r == typecheck_FAIL)
return false;
- /* XXX handle typecheck_MAYBE */
+ if (r == typecheck_MAYBE) {
+ /* the check has to be postponed. we need a patcher */
+ TYPECHECK_COUNT(stat_ins_athrow_unresolved);
+ state->iptr->val.a = create_unresolved_class(
+ state->m,
+ /* XXX make this more efficient, use class_java_lang_Throwable
+ * directly */
+ class_get_classref(state->m->class,utf_java_lang_Throwable),
+ &state->curstack->typeinfo);
+ }
superblockend = true;
maythrow = true;
break;
case ICMD_ARETURN:
+ TYPECHECK_COUNT(stat_ins_areturn);
if (!TYPEINFO_IS_REFERENCE(state->curstack->typeinfo))
TYPECHECK_VERIFYERROR_bool("illegal instruction: ARETURN on non-reference");
TYPECHECK_VERIFYERROR_bool("Return type mismatch");
if (r == typecheck_FAIL)
return false;
- /* XXX handle typecheck_MAYBE */
+ if (r == typecheck_MAYBE) {
+ /* the check has to be postponed, we need a patcher */
+ TYPECHECK_COUNT(stat_ins_areturn_unresolved);
+ state->iptr->val.a = create_unresolved_class(
+ state->m,
+ state->m->parseddesc->returntype.classref,
+ &state->curstack->typeinfo);
+ }
goto return_tail;
/****************************************/
case ICMD_RETURN:
if (state->returntype.type != TYPE_VOID) TYPECHECK_VERIFYERROR_bool("Return type mismatch");
return_tail:
- TYPECHECK_LEAVE;
+ TYPECHECK_COUNT(stat_ins_primitive_return);
+
+ if (state->initmethod && state->m->class != class_java_lang_Object) {
+ /* Check if the 'this' instance has been initialized. */
+ LOG("Checking <init> marker");
+ if (!typevectorset_checktype(state->localset,state->numlocals-1,TYPE_INT))
+ TYPECHECK_VERIFYERROR_bool("<init> method does not initialize 'this'");
+ }
+
superblockend = true;
maythrow = true;
break;
TYPECHECK_COUNT(stat_ins_invoke);
if (!verify_invocation(state))
return false;
+ TYPECHECK_COUNTIF(!state->iptr[0].val.a,stat_ins_invoke_unresolved);
maythrow = true;
break;
/* MULTIANEWARRAY */
case ICMD_MULTIANEWARRAY:
- /* XXX make this a separate function */
- {
- vftbl_t *arrayvftbl;
- arraydescriptor *desc;
-
- /* check the array lengths on the stack */
- i = state->iptr[0].op1;
- if (i < 1)
- TYPECHECK_VERIFYERROR_bool("Illegal dimension argument");
- srcstack = state->curstack;
- while (i--) {
- if (!srcstack)
- TYPECHECK_VERIFYERROR_bool("Unable to pop operand off an empty stack");
- TYPECHECK_INT(srcstack);
- srcstack = srcstack->prev;
- }
-
- /* check array descriptor */
- if (state->iptr[0].target == NULL) {
- arrayvftbl = (vftbl_t*) state->iptr[0].val.a;
- if (!arrayvftbl)
- TYPECHECK_VERIFYERROR_bool("MULTIANEWARRAY with unlinked class");
- if ((desc = arrayvftbl->arraydesc) == NULL)
- TYPECHECK_VERIFYERROR_bool("MULTIANEWARRAY with non-array class");
- if (desc->dimension < state->iptr[0].op1)
- TYPECHECK_VERIFYERROR_bool("MULTIANEWARRAY dimension to high");
-
- /* set the array type of the result */
- TYPEINFO_INIT_CLASSINFO(dst->typeinfo,arrayvftbl->class);
- }
- else {
- /* XXX do checks in patcher */
- if (!typeinfo_init_class(&(dst->typeinfo),CLASSREF_OR_CLASSINFO(state->iptr[0].val.a)))
- return false;
- }
- }
+ if (!verify_multianewarray(state))
+ return false;
maythrow = true;
break;
* Instructions below...
* *) don't operate on local variables,
* *) don't operate on references,
- * *) don't operate on returnAddresses.
+ * *) don't operate on returnAddresses,
+ * *) don't affect control flow (except
+ * by throwing exceptions).
*
* (These instructions are typechecked in
* analyse_stack.)
case ICMD_LSHL:
case ICMD_LSHR:
case ICMD_LUSHR:
-#if 0
- case ICMD_IREM0X10001:
- case ICMD_LREM0X10001:
-#endif
case ICMD_IMULPOW2:
case ICMD_LMULPOW2:
case ICMD_IDIVPOW2:
/* reach exception handlers for this instruction */
if (maythrow) {
+ TYPECHECK_COUNT(stat_ins_maythrow);
+ TYPECHECK_MARK(state->stat_maythrow);
LOG("reaching exception handlers");
i = 0;
while (state->handlers[i]) {
* have been saved if an <init> call inside the block has
* modified the instack types. (see INVOKESPECIAL) */
- if (state->savedstack) {
- stackptr sp = state->bptr->instack;
- stackptr copy = state->savedstack;
- TYPECHECK_COUNT(stat_savedstack);
- LOG("restoring saved instack");
- TYPESTACK_COPY(sp,copy);
- state->bptr->instack = state->savedstack;
- state->savedstack = NULL;
- }
+ if (state->savedstack)
+ typestate_restore_instack(state);
+
return true;
}
if (state->initmethod)
TYPEINFO_INIT_NEWOBJECT(td->info,NULL);
else
- TYPEINFO_INIT_CLASSINFO(td->info, state->m->class);
+ typeinfo_init_classinfo(&(td->info), state->m->class);
td++;
i--;
}
return true;
}
+/* typecheck_init_flags ********************************************************
+
+ Initialize the basic block flags for the following CFG traversal.
+
+ IN:
+ state............the current state of the verifier
+
+*******************************************************************************/
+
+static void
+typecheck_init_flags(verifier_state *state)
+{
+ s4 i;
+ basicblock *block;
+
+ /* set all BBFINISHED blocks to BBTYPECHECK_UNDEF. */
+
+ i = state->m->basicblockcount;
+ block = state->m->basicblocks;
+
+ while (--i >= 0) {
+
+#ifdef TYPECHECK_DEBUG
+ /* check for invalid flags */
+ if (block->flags != BBFINISHED && block->flags != BBDELETED && block->flags != BBUNDEF)
+ {
+ /*show_icmd_method(state->cd->method,state->cd,state->rd);*/
+ LOGSTR1("block flags: %d\n",block->flags); LOGFLUSH;
+ TYPECHECK_ASSERT(false);
+ }
+#endif
+
+ if (block->flags >= BBFINISHED) {
+ block->flags = BBTYPECHECK_UNDEF;
+ }
+ block++;
+ }
+
+ /* the first block is always reached */
+
+ if (state->m->basicblockcount && state->m->basicblocks[0].flags == BBTYPECHECK_UNDEF)
+ state->m->basicblocks[0].flags = BBTYPECHECK_REACHED;
+}
+
+/* typecheck_reset_flags *******************************************************
+
+ Reset the flags of basic blocks we have not reached.
+
+ IN:
+ state............the current state of the verifier
+
+*******************************************************************************/
+
+static void
+typecheck_reset_flags(verifier_state *state)
+{
+ s4 i;
+
+ /* check for invalid flags at exit */
+
+#ifdef TYPECHECK_DEBUG
+ for (i=0; i<state->m->basicblockcount; ++i) {
+ if (state->m->basicblocks[i].flags != BBDELETED
+ && state->m->basicblocks[i].flags != BBUNDEF
+ && state->m->basicblocks[i].flags != BBFINISHED
+ && state->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",
+ state->m->basicblocks[i].debug_nr,state->m->basicblocks[i].flags);
+ TYPECHECK_ASSERT(false);
+ }
+ }
+#endif
+
+ /* Reset blocks we never reached */
+
+ for (i=0; i<state->m->basicblockcount; ++i) {
+ if (state->m->basicblocks[i].flags == BBTYPECHECK_UNDEF)
+ state->m->basicblocks[i].flags = BBFINISHED;
+ }
+}
+
/****************************************************************************/
/* typecheck() */
/* This is the main function of the bytecode verifier. It is called */
int count_iterations = 0;
TYPECHECK_COUNT(stat_typechecked);
TYPECHECK_COUNT_FREQ(stat_locals,cdata->maxlocals,STAT_LOCALS);
- TYPECHECK_COUNT_FREQ(stat_blocks,meth->basicblockcount/10,STAT_BLOCKS);
+ TYPECHECK_COUNT_FREQ(stat_blocks,cdata->method->basicblockcount/10,STAT_BLOCKS);
+ TYPECHECK_COUNTIF(cdata->method->exceptiontablelength != 0,stat_methods_with_handlers);
+ state.stat_maythrow = false;
#endif
/* some logging on entry */
-
+
LOGSTR("\n==============================================================================\n");
/*DOLOG( show_icmd_method(cdata->method,cdata,rdata));*/
LOGSTR("\n==============================================================================\n");
state.initmethod = (state.m->name == utf_init);
- /* reset all BBFINISHED blocks to BBTYPECHECK_UNDEF. */
-
- i = state.m->basicblockcount;
- state.bptr = state.m->basicblocks;
- while (--i >= 0) {
-#ifdef TYPECHECK_DEBUG
- if (state.bptr->flags != BBFINISHED && state.bptr->flags != BBDELETED
- && state.bptr->flags != BBUNDEF)
- {
- /*show_icmd_method(state.cd->method,state.cd,state.rd);*/
- LOGSTR1("block flags: %d\n",state.bptr->flags); LOGFLUSH;
- TYPECHECK_ASSERT(false);
- }
-#endif
- if (state.bptr->flags >= BBFINISHED) {
- state.bptr->flags = BBTYPECHECK_UNDEF;
- }
- state.bptr++;
- }
+ /* initialize the basic block flags for the following CFG traversal */
- /* the first block is always reached */
-
- if (state.m->basicblockcount && state.m->basicblocks[0].flags == BBTYPECHECK_UNDEF)
- state.m->basicblocks[0].flags = BBTYPECHECK_REACHED;
-
- LOG("Blocks reset.\n");
+ typecheck_init_flags(&state);
/* number of local variables */
state.excstack.prev = NULL;
state.excstack.type = TYPE_ADR;
- TYPEINFO_INIT_CLASSINFO(state.excstack.typeinfo,
+ typeinfo_init_classinfo(&(state.excstack.typeinfo),
class_java_lang_Throwable); /* changed later */
LOG("Exception handler stacks set.\n");
LOG1("Typechecker did %4d iterations",count_iterations);
TYPECHECK_COUNT_FREQ(stat_iterations,count_iterations,STAT_ITERATIONS);
TYPECHECK_COUNTIF(state.jsrencountered,stat_typechecked_jsr);
+ TYPECHECK_COUNTIF(state.stat_maythrow,stat_methods_maythrow);
#endif
- /* check for invalid flags at exit */
- /* XXX make this a separate function */
-
-#ifdef TYPECHECK_DEBUG
- for (i=0; i<state.m->basicblockcount; ++i) {
- if (state.m->basicblocks[i].flags != BBDELETED
- && state.m->basicblocks[i].flags != BBUNDEF
- && state.m->basicblocks[i].flags != BBFINISHED
- && state.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",
- state.m->basicblocks[i].debug_nr,state.m->basicblocks[i].flags);
- TYPECHECK_ASSERT(false);
- }
- }
-#endif
-
- /* Reset blocks we never reached */
-
- for (i=0; i<state.m->basicblockcount; ++i) {
- if (state.m->basicblocks[i].flags == BBTYPECHECK_UNDEF)
- state.m->basicblocks[i].flags = BBFINISHED;
- }
-
- LOGimp("exiting typecheck");
+ /* reset the flags of blocks we haven't reached */
- /* just return methodinfo* to indicate everything was ok */
+ typecheck_reset_flags(&state);
+ /* just return methodinfo* to indicate everything was ok */
+ LOGimp("exiting typecheck");
return state.m;
}
-#undef COPYTYPE
-
-#endif /* CACAO_TYPECHECK */
+#endif /* ENABLE_VERIFIER */
/*
* These are local overrides for various environment variables in Emacs.