-/* vm/jit/verify/typecheck.c - typechecking (part of bytecode verification)
+/* 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
- $Id: typecheck.c 2045 2005-03-20 14:29:12Z edwin $
+ Changes: Christian Thalinger
+
+ $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 "vm/global.h" /* must be here because of CACAO_TYPECHECK */
+#include "config.h"
+#include "vm/types.h"
+#include "vm/global.h"
-#ifdef CACAO_TYPECHECK
+#ifdef ENABLE_VERIFIER
-#include "types.h"
-/* #include "main.h" */
#include "mm/memory.h"
#include "toolbox/logging.h"
#include "native/native.h"
#include "vm/builtin.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"
+#include "vm/resolve.h"
/****************************************************************************/
/* DEBUG HELPERS */
/****************************************************************************/
+#ifdef TYPECHECK_DEBUG
+#define TYPECHECK_ASSERT(cond) assert(cond)
+#else
+#define TYPECHECK_ASSERT(cond)
+#endif
+
#ifdef TYPECHECK_VERBOSE_OPT
bool typecheckverbose = false;
#define DOLOG(action) do { if (typecheckverbose) {action;} } while(0)
#define LOGSTR2(str,a,b) DOLOG(dolog_plain(str,a,b))
#define LOGSTR3(str,a,b,c) DOLOG(dolog_plain(str,a,b,c))
#define LOGSTRu(utf) DOLOG(log_plain_utf(utf))
+#define LOGNAME(c) DOLOG(do {log_plain_utf(IS_CLASSREF(c) ? c.ref->name : c.cls->name);} while(0))
#else
#define LOG(str)
#define LOG1(str,a)
#define LOGSTR2(str,a,b)
#define LOGSTR3(str,a,b,c)
#define LOGSTRu(utf)
+#define LOGNAME(c)
#endif
#ifdef TYPECHECK_VERBOSE_IMPORTANT
{
#ifdef TYPEINFO_DEBUG
while (stack) {
- typeinfo_print_stacktype(file,stack->type,&stack->typeinfo);
+ /*fprintf(file,"<%p>",stack);*/
+ typeinfo_print_stacktype(file,stack->type,&(stack->typeinfo));
stack = stack->prev;
if (stack) fprintf(file," ");
}
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) \
int i;
for (i=0; i<limit; ++i)
fprintf(file," %3d: %8d\n",i,array[i]);
- fprintf(file," =>%3d: %8d\n",limit,array[limit]);
+ fprintf(file," >=%3d: %8d\n",limit,array[limit]);
}
void typecheck_print_statistics(FILE *file) {
fprintf(file,"typechecked methods: %8d\n",stat_typechecked);
- fprintf(file,"methods with JSR : %8d\n",stat_typechecked_jsr);
+ fprintf(file," 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
/****************************************************************************/
-/* TYPESTACK FUNCTIONS */
+/* MACROS FOR THROWING EXCEPTIONS */
+/****************************************************************************/
+
+#define TYPECHECK_VERIFYERROR_ret(m,msg,retval) \
+ do { \
+ *exceptionptr = new_verifyerror((m), (msg)); \
+ return (retval); \
+ } while (0)
+
+#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)) { \
+ *exceptionptr = new_verifyerror(state->m, (msg)); \
+ return false; \
+ } \
+ } while (0)
+
+#define TYPECHECK_INT(sp) TYPECHECK_CHECK_TYPE(sp,TYPE_INT,"Expected to find integer on stack")
+#define TYPECHECK_LNG(sp) TYPECHECK_CHECK_TYPE(sp,TYPE_LNG,"Expected to find long on stack")
+#define TYPECHECK_FLT(sp) TYPECHECK_CHECK_TYPE(sp,TYPE_FLT,"Expected to find float on stack")
+#define TYPECHECK_DBL(sp) TYPECHECK_CHECK_TYPE(sp,TYPE_DBL,"Expected to find double on stack")
+#define TYPECHECK_ADR(sp) TYPECHECK_CHECK_TYPE(sp,TYPE_ADR,"Expected to find object on stack")
+
+/****************************************************************************/
+/* VERIFIER STATE STRUCT */
+/****************************************************************************/
+
+/* verifier_state - This structure keeps the current state of the */
+/* bytecode verifier for passing it between verifier functions. */
+
+typedef struct verifier_state {
+ stackptr curstack; /* input stack top for current instruction */
+ instruction *iptr; /* pointer to current instruction */
+ basicblock *bptr; /* pointer to current basic block */
+
+ methodinfo *m; /* the current method */
+ codegendata *cd; /* codegendata for current method */
+ registerdata *rd; /* registerdata for current method */
+
+ s4 numlocals; /* number of local variables */
+ s4 validlocals; /* number of Java-accessible locals */
+ void *localbuf; /* local variable types for each block start */
+ typevector *localset; /* typevector set for local variables */
+ typedescriptor returntype; /* return type of the current method */
+
+ stackptr savedstackbuf; /* buffer for saving the stack */
+ stackptr savedstack; /* saved instack of current block */
+
+ exceptiontable **handlers; /* active exception handlers */
+ stackelement excstack; /* instack for exception handlers */
+
+ 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;
+
+/****************************************************************************/
+/* TYPESTACK MACROS AND FUNCTIONS */
+/* */
+/* These macros and functions act on the 'type stack', which is a shorthand */
+/* for the types of the stackslots of the current stack. The type of a */
+/* stack slot is usually described by a TYPE_* constant and -- for TYPE_ADR */
+/* -- by the typeinfo of the slot. The only thing that makes the type stack */
+/* more complicated are returnAddresses of local subroutines, because a */
+/* single stack slot may contain a set of more than one possible return */
+/* address. This is handled by 'return address sets'. A return address set */
+/* is kept as a linked list dangling off the typeinfo of the stack slot. */
/****************************************************************************/
#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.
-static void
-typestack_copy(stackptr dst,stackptr y,typevector *selected)
+ This function does a straight forward copy except for returnAddress types.
+ For returnAddress slots only the return addresses corresponding to
+ typevectors in the SELECTED set are copied.
+
+ IN:
+ state............current verifier state
+ y................stack with types to copy
+ selected.........set of selected typevectors
+
+ OUT:
+ *dst.............the destination stack
+
+ RETURN VALUE:
+ true.............success
+ false............an exception has been thrown
+
+*******************************************************************************/
+
+static bool
+typestack_copy(verifier_state *state,stackptr dst,stackptr y,typevector *selected)
{
typevector *sel;
typeinfo_retaddr_set *sety;
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");
+ /* XXX only check the following two in debug mode? */
+ if (!y) {
+ *exceptionptr = new_verifyerror(state->m,"Stack depth mismatch");
+ return false;
+ }
+ if (dst->type != y->type) {
+ *exceptionptr = new_verifyerror(state->m,"Stack type mismatch");
+ return false;
+ }
LOG3("copy %p -> %p (type %d)",y,dst,dst->type);
if (dst->type == TYPE_ADDRESS) {
if (TYPEINFO_IS_PRIMITIVE(y->typeinfo)) {
}
}
}
- if (y) panic("Stack depth mismatch 2");
+ if (y) {
+ *exceptionptr = new_verifyerror(state->m,"Stack depth mismatch");
+ return false;
+ }
+ return true;
}
+/* typestack_put_retaddr *******************************************************
+
+ Put a returnAddress into a stack slot.
+
+ The stack slot receives a set of return addresses with as many members as
+ there are typevectors in the local variable set.
+
+ IN:
+ retaddr..........the returnAddress to set (a basicblock *)
+ loc..............the local variable typevector set
+
+ OUT:
+ *dst.............the destination stack slot
+
+*******************************************************************************/
+
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
+ TYPECHECK_ASSERT(dst->type == TYPE_ADDRESS);
TYPEINFO_INIT_RETURNADDRESS(dst->typeinfo,NULL);
for (;loc; loc=loc->alt) {
}
}
+/* typestack_collapse **********************************************************
+
+ Collapse the given stack by shortening all return address sets to a single
+ member.
+
+ OUT:
+ *dst.............the destination stack to collapse
+
+*******************************************************************************/
+
static void
typestack_collapse(stackptr dst)
{
}
}
-static bool
-typestack_merge(stackptr dst,stackptr y)
+/* typestack_merge *************************************************************
+
+ Merge the types on one stack into the destination stack.
+
+ IN:
+ state............current state of the verifier
+ dst..............the destination stack
+ y................the second stack
+
+ OUT:
+ *dst.............receives the result of the stack merge
+
+ RETURN VALUE:
+ typecheck_TRUE...*dst has been modified
+ typecheck_FALSE..*dst has not been modified
+ typecheck_FAIL...an exception has been thrown
+
+*******************************************************************************/
+
+static typecheck_result
+typestack_merge(verifier_state *state,stackptr dst,stackptr y)
{
+ typecheck_result r;
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 (!y) {
+ *exceptionptr = new_verifyerror(state->m,"Stack depth mismatch");
+ return typecheck_FAIL;
+ }
+ if (dst->type != y->type) {
+ *exceptionptr = new_verifyerror(state->m,"Stack type mismatch");
+ return typecheck_FAIL;
+ }
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");
+ if (!TYPEINFO_IS_PRIMITIVE(y->typeinfo)) {
+ *exceptionptr = new_verifyerror(state->m,"Merging returnAddress with reference");
+ return typecheck_FAIL;
+ }
}
else {
/* dst has reference type */
- if (TYPEINFO_IS_PRIMITIVE(y->typeinfo))
- panic("Merging reference with returnAddress");
- changed |= typeinfo_merge(&(dst->typeinfo),&(y->typeinfo));
+ if (TYPEINFO_IS_PRIMITIVE(y->typeinfo)) {
+ *exceptionptr = new_verifyerror(state->m,"Merging reference with returnAddress");
+ return typecheck_FAIL;
+ }
+ r = typeinfo_merge(state->m,&(dst->typeinfo),&(y->typeinfo));
+ if (r == typecheck_FAIL)
+ return r;
+ changed |= r;
}
}
}
- if (y) panic("Stack depth mismatch 4");
+ if (y) {
+ *exceptionptr = new_verifyerror(state->m,"Stack depth mismatch");
+ return typecheck_FAIL;
+ }
return changed;
}
+/* typestack_add ***************************************************************
+
+ Add the return addresses in the given stack at a given k-index to the
+ corresponding return address sets in the destination stack.
+
+ IN:
+ dst..............the destination stack
+ y................the second stack
+ ky...............the k-index which should be selected from the Y stack
+
+ OUT:
+ *dst.............receives the result of adding the addresses
+
+*******************************************************************************/
+
static void
typestack_add(stackptr dst,stackptr y,int ky)
{
}
}
-/* '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)
{
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
+ TYPECHECK_ASSERT(b);
if (TYPESTACK_IS_RETURNADDRESS(a)) {
-#ifdef TYPECHECK_DEBUG
- if (!TYPESTACK_IS_RETURNADDRESS(b))
- panic("Internal error: typestack_separable_from: unmergable stacks");
-#endif
+ TYPECHECK_ASSERT(TYPESTACK_IS_RETURNADDRESS(b));
seta = TYPESTACK_RETURNADDRESSSET(a);
setb = TYPESTACK_RETURNADDRESSSET(b);
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
+ TYPECHECK_ASSERT(!b);
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)
{
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
+ TYPECHECK_ASSERT(b);
if (TYPESTACK_IS_RETURNADDRESS(a)) {
-#ifdef TYPECHECK_DEBUG
- if (!TYPESTACK_IS_RETURNADDRESS(b))
- panic("Internal error: typestack_separable_from: unmergable stacks");
-#endif
+ TYPECHECK_ASSERT(TYPESTACK_IS_RETURNADDRESS(b));
seta = TYPESTACK_RETURNADDRESSSET(a);
setb = TYPESTACK_RETURNADDRESSSET(b);
RETURNADDRESSSET_SEEK(seta,ka);
if (seta->addr != setb->addr) return true;
}
}
-#ifdef TYPECHECK_DEBUG
- if (b) panic("Internal error: typestack_separable_from: different depth");
-#endif
+ TYPECHECK_ASSERT(!b);
return false;
}
/****************************************************************************/
/* TYPESTATE FUNCTIONS */
+/* */
+/* These functions act on the 'type state', which comprises: */
+/* - the types of the stack slots of the current stack */
+/* - the set of type vectors describing the local variables */
/****************************************************************************/
-static bool
-typestate_merge(stackptr deststack,typevector *destloc,
- stackptr ystack,typevector *yloc,
- int locsize,bool jsrencountered)
+/* typestate_merge *************************************************************
+
+ Merge the types of one state into the destination state.
+
+ IN:
+ state............current state of the verifier
+ deststack........the destination stack
+ destloc..........the destination set of local variable typevectors
+ ystack...........the second stack
+ yloc.............the second set of local variable typevectors
+
+ OUT:
+ *deststack.......receives the result of the stack merge
+ *destloc.........receives the result of the local variable merge
+
+ RETURN VALUE:
+ typecheck_TRUE...destination state has been modified
+ typecheck_FALSE..destination state has not been modified
+ typecheck_FAIL...an exception has been thrown
+
+*******************************************************************************/
+
+static typecheck_result
+typestate_merge(verifier_state *state,
+ stackptr deststack,typevector *destloc,
+ stackptr ystack,typevector *yloc)
{
typevector *dvec,*yvec;
int kd,ky;
bool changed = false;
+ typecheck_result r;
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;
+ LOGSTR("dloc : "); DOLOG(typevectorset_print(get_logfile(),destloc,state->numlocals)); LOGNL;
+ LOGSTR("yloc : "); DOLOG(typevectorset_print(get_logfile(),yloc,state->numlocals)); 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);
+ r = typestack_merge(state,deststack,ystack);
+ if (r == typecheck_FAIL)
+ return r;
+ changed |= r;
+
+ /* If there have not been any JSRs we just have a single typevector merge */
+ if (!state->jsrencountered) {
+ r = typevector_merge(state->m,destloc,yloc,state->numlocals);
+ if (r == typecheck_FAIL)
+ return r;
+ return changed | r;
+ }
for (yvec=yloc; yvec; yvec=yvec->alt) {
ky = yvec->k;
* separable when (ystack,yvec) is added. */
if (!typestack_separable_with(deststack,ystack,ky)
- && !typevectorset_separable_with(destloc,yvec,locsize))
+ && !typevectorset_separable_with(destloc,yvec,state->numlocals))
{
/* 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);
+ if (typevectorset_collapse(state->m,destloc,state->numlocals) == typecheck_FAIL)
+ return typecheck_FAIL;
+ if (typevector_merge(state->m,destloc,yvec,state->numlocals) == typecheck_FAIL)
+ return typecheck_FAIL;
}
else {
/* Yes, the resulting set will be separable. Thus we check
for (dvec=destloc,kd=0; dvec; dvec=dvec->alt, kd++) {
if (!typestack_separable_from(ystack,ky,deststack,kd)
- && !typevector_separable_from(yvec,dvec,locsize))
+ && !typevector_separable_from(yvec,dvec,state->numlocals))
{
/* 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);
+ r = typevector_merge(state->m,dvec,yvec,state->numlocals);
+ if (r == typecheck_FAIL)
+ return r;
+ changed |= r;
goto merged;
}
* result set. */
typestack_add(deststack,ystack,ky);
- typevectorset_add(destloc,yvec,locsize);
+ typevectorset_add(destloc,yvec,state->numlocals);
changed = true;
}
LOG("result:");
LOGSTR("dstack: "); DOLOG(typestack_print(get_logfile(),deststack)); LOGNL;
- LOGSTR("dloc : "); DOLOG(typevectorset_print(get_logfile(),destloc,locsize)); LOGNL;
+ LOGSTR("dloc : "); DOLOG(typevectorset_print(get_logfile(),destloc,state->numlocals)); LOGNL;
LOGFLUSH;
return changed;
}
+/* typestate_reach *************************************************************
+
+ Reach a destination block and propagate stack and local variable types
+
+ IN:
+ state............current state of the verifier
+ destblock........destination basic block
+ ystack...........stack to propagate
+ yloc.............set of local variable typevectors to propagate
+
+ OUT:
+ state->repeat....set to true if the verifier must iterate again
+ over the basic blocks
+
+ RETURN VALUE:
+ true.............success
+ false............an exception has been thrown
+
+*******************************************************************************/
static bool
-typestate_reach(codegendata *cd, registerdata *rd,void *localbuf,
- basicblock *current,
+typestate_reach(verifier_state *state,
basicblock *destblock,
- stackptr ystack,typevector *yloc,
- int locsize,bool jsrencountered)
+ stackptr ystack,typevector *yloc)
{
typevector *destloc;
int destidx;
bool changed = false;
+ typecheck_result r;
LOG1("reaching block L%03d",destblock->debug_nr);
TYPECHECK_COUNT(stat_reached);
- destidx = destblock - cd->method->basicblocks;
- destloc = MGET_TYPEVECTOR(localbuf,destidx,locsize);
+ destidx = destblock - state->cd->method->basicblocks;
+ destloc = MGET_TYPEVECTOR(state->localbuf,destidx,state->numlocals);
/* When branching backwards we have to check for uninitialized objects */
- if (destblock <= current) {
+ if (destblock <= state->bptr) {
stackptr sp;
int i;
-#if defined(__GNUC__)
-#warning FIXME FOR INLINING
-#endif
+
+ /* XXX FIXME FOR INLINING */
+
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: %d, dest: %d\n", current->debug_nr, destblock->debug_nr);
- panic("Branching backwards with uninitialized object on stack");
- }
+ /*printf("current: %d, dest: %d\n", state->bptr->debug_nr, destblock->debug_nr);*/
+ *exceptionptr = new_verifyerror(state->m,"Branching backwards with uninitialized object on stack");
+ return false;
+ }
- for (i=0; i<locsize; ++i)
+ for (i=0; i<state->numlocals; ++i)
if (yloc->td[i].type == TYPE_ADR &&
- TYPEINFO_IS_NEWOBJECT(yloc->td[i].info))
- panic("Branching backwards with uninitialized object in local variable");
+ TYPEINFO_IS_NEWOBJECT(yloc->td[i].info)) {
+ *exceptionptr = new_verifyerror(state->m,"Branching backwards with uninitialized object in local variable");
+ return false;
+ }
}
}
TYPECHECK_COUNT(stat_copied);
LOG1("block (index %04d) reached first time",destidx);
- typestack_copy(destblock->instack,ystack,yloc);
- COPY_TYPEVECTORSET(yloc,destloc,locsize);
+ if (!typestack_copy(state,destblock->instack,ystack,yloc))
+ return false;
+ typevectorset_copy_inplace(yloc,destloc,state->numlocals);
changed = true;
}
else {
TYPECHECK_COUNT(stat_merged);
LOG1("block (index %04d) reached before",destidx);
- changed = typestate_merge(destblock->instack,destloc,
- ystack,yloc,locsize,
- jsrencountered);
+ r = typestate_merge(state,destblock->instack,destloc,ystack,yloc);
+ if (r == typecheck_FAIL)
+ return false;
+ changed = r;
TYPECHECK_COUNTIF(changed,stat_merging_changed);
}
if (changed) {
LOG("changed!");
destblock->flags = BBTYPECHECK_REACHED;
- if (destblock <= current) {LOG("REPEAT!"); return true;}
+ if (destblock <= state->bptr) {
+ LOG("REPEAT!");
+ state->repeat = true;
+ }
}
- return false;
+ return true;
}
+/* typestate_ret ***************************************************************
+
+ Reach the destinations of a RET instruction.
+
+ IN:
+ state............current state of the verifier
+ retindex.........index of local variable containing the returnAddress
+
+ OUT:
+ state->repeat....set to true if the verifier must iterate again
+ over the basic blocks
+
+ RETURN VALUE:
+ true.............success
+ false............an exception has been thrown
+
+*******************************************************************************/
static bool
-typestate_ret(codegendata *cd,registerdata *rd, void *localbuf,
- basicblock *current,
- stackptr ystack,typevector *yloc,
- int retindex,int locsize)
+typestate_ret(verifier_state *state,int retindex)
{
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");
+ for (yvec=state->localset; yvec; ) {
+ if (!TYPEDESC_IS_RETURNADDRESS(yvec->td[retindex])) {
+ *exceptionptr = new_verifyerror(state->m,"Illegal instruction: RET on non-returnAddress");
+ return false;
+ }
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);
+ if (!typestate_reach(state,destblock,state->curstack,selected))
+ return false;
+ }
+ return true;
+}
+
+/* typestate_save_instack ******************************************************
+
+ Save the input stack of the current basic block in the "savedstackbuf"
+ of the verifier state.
+
+ 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).
+
+ IN:
+ state............current state of the verifier
+
+*******************************************************************************/
+
+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);
+ }
+}
+
+/* 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
+
+*******************************************************************************/
+
+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);
}
- return repeat;
+
+ state->bptr->instack = state->savedstack;
+ state->savedstack = NULL;
}
/****************************************************************************/
-/* HELPER FUNCTIONS */
+/* MISC MACROS */
/****************************************************************************/
-/* If a field is checked, definingclass == implementingclass */
+#define COPYTYPE(source,dest) \
+ {if ((source)->type == TYPE_ADR) \
+ TYPEINFO_COPY((source)->typeinfo,(dest)->typeinfo);}
+
+#define ISBUILTIN(v) (bte->fp == (functionptr) (v))
+
+/* verify_invocation ***********************************************************
+
+ Verify an ICMD_INVOKE* instruction.
+
+ IN:
+ state............the current state of the verifier
+
+ RETURN VALUE:
+ true.............successful verification,
+ false............an exception has been thrown.
+
+*******************************************************************************/
+
static bool
-is_accessible(int flags,classinfo *definingclass,classinfo *implementingclass, classinfo *methodclass,
- typeinfo *instance)
+verify_invocation(verifier_state *state)
{
- /* 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 = 0; /*dont't fail, don't really get it (was: methodclass)
- This code was never triggered before, since all packagenames had been 0-pointers
- anybody an idea ?*/
- }
- break;
- case ACC_PRIVATE:
- if (definingclass != methodclass) {
- LOG("private access");
- return false;
- }
- break;
- default:
- panic("Invalid access flags");
+ unresolved_method *um; /* struct describing the called method */
+ constant_FMIref *mref; /* reference to the called method */
+ methoddesc *md; /* descriptor of the called method */
+ bool specialmethod; /* true if a <...> method is called */
+ int opcode; /* invocation opcode */
+ bool callinginit; /* true if <init> is called */
+ instruction *ins;
+ classref_or_classinfo initclass;
+ typedesc *td;
+ stackelement *stack; /* temporary stack pointer */
+ stackelement *dst; /* result stack of the invocation */
+ int i; /* counter */
+ u1 rtype; /* return type of called method */
+
+ um = (unresolved_method *) state->iptr[0].target;
+ mref = um->methodref;
+ md = mref->parseddesc.md;
+ specialmethod = (mref->name->text[0] == '<');
+ opcode = state->iptr[0].opc;
+ dst = state->iptr->dst;
+
+ /* prevent compiler warnings */
+
+ ins = NULL;
+
+ /* check whether we are calling <init> */
+
+ callinginit = (opcode == ICMD_INVOKESPECIAL && mref->name == utf_init);
+ if (specialmethod && !callinginit)
+ TYPECHECK_VERIFYERROR_bool("Invalid invocation of special method");
+
+ /* allocate parameters if necessary */
+
+ if (!md->params)
+ if (!descriptor_params_from_paramtypes(md,
+ (opcode == ICMD_INVOKESTATIC) ? ACC_STATIC : ACC_NONE))
+ return false;
+
+ /* check parameter types */
+
+ stack = state->curstack;
+ i = md->paramcount; /* number of parameters including 'this'*/
+ while (--i >= 0) {
+ LOG1("param %d",i);
+ td = md->paramtypes + i;
+ if (stack->type != td->type)
+ TYPECHECK_VERIFYERROR_bool("Parameter type mismatch in method invocation");
+ if (stack->type == TYPE_ADR) {
+ LOGINFO(&(stack->typeinfo));
+ if (i==0 && callinginit)
+ {
+ /* first argument to <init> method */
+ if (!TYPEINFO_IS_NEWOBJECT(stack->typeinfo))
+ TYPECHECK_VERIFYERROR_bool("Calling <init> on initialized object");
+
+ /* get the address of the NEW instruction */
+ LOGINFO(&(stack->typeinfo));
+ ins = (instruction*)TYPEINFO_NEWOBJECT_INSTRUCTION(stack->typeinfo);
+ if (ins)
+ 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)
+ stack = stack->prev;
}
- if (instance) {
- if ((flags & ACC_STATIC) != 0) {
- LOG("accessing STATIC member with instance");
+ LOG("checking return type");
+ rtype = md->returntype.type;
+ if (rtype != TYPE_VOID) {
+ if (rtype != dst->type)
+ TYPECHECK_VERIFYERROR_bool("Return type mismatch in method invocation");
+ if (!typeinfo_init_from_typedesc(&(md->returntype),NULL,&(dst->typeinfo)))
return false;
- }
-
- if (implementingclass
- && !TYPEINFO_IS_NULLTYPE(*instance)
- && !TYPEINFO_IS_NEWOBJECT(*instance))
- {
- if (!typeinfo_is_assignable_to_classinfo(instance,
- implementingclass))
+ }
+
+ if (callinginit) {
+ LOG("replacing uninitialized object");
+ /* replace uninitialized object type on stack */
+ stack = dst;
+ while (stack) {
+ if (stack->type == TYPE_ADR
+ && TYPEINFO_IS_NEWOBJECT(stack->typeinfo)
+ && TYPEINFO_NEWOBJECT_INSTRUCTION(stack->typeinfo) == ins)
{
- LOG("instance not assignable");
- LOGINFO(instance);
- LOGSTRu(implementingclass->name); LOGNL; LOGFLUSH;
- return false;
+ 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 (stack <= state->bptr->instack && !state->savedstack)
+ typestate_save_instack(state);
+
+ if (!typeinfo_init_class(&(stack->typeinfo),initclass))
+ return false;
}
+ stack = stack->prev;
}
- }
- else {
- if ((flags & ACC_STATIC) == 0) {
- LOG("accessing non-STATIC member without instance");
+ /* replace uninitialized object type in locals */
+ if (!typevectorset_init_object(state->localset,ins,initclass,state->numlocals))
return false;
+
+ /* initializing the 'this' reference? */
+ if (!ins) {
+ classinfo *cls;
+ TYPECHECK_ASSERT(state->initmethod);
+ /* { 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");
+ }
+
+ /* set our marker variable to type int */
+ LOG("setting <init> marker");
+ typevectorset_store(state->localset,state->numlocals-1,TYPE_INT,NULL);
+ }
+ else {
+ /* { 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;
}
-/****************************************************************************/
-/* MACROS FOR LOCAL VARIABLE CHECKING */
-/****************************************************************************/
+/* verify_generic_builtin ******************************************************
+
+ Verify the call of a generic builtin method.
+
+ IN:
+ state............the current state of the verifier
-#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)
+ RETURN VALUE:
+ true.............successful verification,
+ false............an exception has been thrown.
-#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)
+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);
-#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)
+ 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;
+ }
-#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)
+ /* check the return type */
-/****************************************************************************/
-/* MACROS FOR STACK TYPE CHECKING */
-/****************************************************************************/
+ rtype = md->returntype.type;
+ if (rtype != TYPE_VOID) {
+ stackptr dst;
-/* These macros are for basic typechecks which were not done in stack.c */
+ 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;
+ }
-#define TYPECHECK_STACK(sp,tp) \
- do { if ((sp)->type != (tp)) \
- panic("Wrong data type on stack"); } while(0)
+ return true;
+}
-#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)
+/* verify_builtin **************************************************************
+
+ Verify the call of a builtin method.
+
+ IN:
+ state............the current state of the verifier
-#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)
+ RETURN VALUE:
+ true.............successful verification,
+ false............an exception has been thrown.
-/****************************************************************************/
-/* MISC MACROS */
-/****************************************************************************/
+*******************************************************************************/
-#define INSTRUCTION_PUTCONST_TYPE(iptr) \
- ((iptr)[1].op1)
+static bool
+verify_builtin(verifier_state *state)
+{
+ builtintable_entry *bte;
+ classref_or_classinfo cls;
+ stackptr dst; /* output stack of current instruction */
-#define INSTRUCTION_PUTCONST_FIELDINFO(iptr) \
- ((fieldinfo *)((iptr)[1].val.a))
+ bte = (builtintable_entry *) state->iptr[0].val.a;
+ dst = state->iptr->dst;
-#define COPYTYPE(source,dest) \
- {if ((source)->type == TYPE_ADR) \
- TYPEINFO_COPY((source)->typeinfo,(dest)->typeinfo);}
+ /* XXX this is an ugly if-chain but twisti did not want a function */
+ /* pointer in builtintable_entry for this, so here you go.. ;) */
-#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
- * stack slots are propagated to the target block.
- * Input:
- * bptr.......current block
- * tbptr......target block
- * dst........current output stack pointer
- * numlocals..number of local variables
- * localset...current local variable vectorset
- * localbuf...local variable vectorset buffer
- * jsrencountered...true if a JSR has been seen
- * Output:
- * repeat.....changed to true if a block before the current
- * block has changed
- */
-#define TYPECHECK_REACH \
- do { \
- repeat |= typestate_reach(cd,rd, localbuf,bptr,tbptr,dst, \
- localset,numlocals,jsrencountered); \
- LOG("done."); \
- } while (0)
+ if (ISBUILTIN(BUILTIN_new)) {
+ if (state->iptr[-1].opc != ICMD_ACONST)
+ 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)) {
+ TYPECHECK_INT(state->curstack);
+ TYPEINFO_INIT_PRIMITIVE_ARRAY(dst->typeinfo,ARRAYTYPE_BOOLEAN);
+ }
+ else if (ISBUILTIN(BUILTIN_newarray_char)) {
+ TYPECHECK_INT(state->curstack);
+ TYPEINFO_INIT_PRIMITIVE_ARRAY(dst->typeinfo,ARRAYTYPE_CHAR);
+ }
+ else if (ISBUILTIN(BUILTIN_newarray_float)) {
+ TYPECHECK_INT(state->curstack);
+ TYPEINFO_INIT_PRIMITIVE_ARRAY(dst->typeinfo,ARRAYTYPE_FLOAT);
+ }
+ else if (ISBUILTIN(BUILTIN_newarray_double)) {
+ TYPECHECK_INT(state->curstack);
+ TYPEINFO_INIT_PRIMITIVE_ARRAY(dst->typeinfo,ARRAYTYPE_DOUBLE);
+ }
+ else if (ISBUILTIN(BUILTIN_newarray_byte)) {
+ TYPECHECK_INT(state->curstack);
+ TYPEINFO_INIT_PRIMITIVE_ARRAY(dst->typeinfo,ARRAYTYPE_BYTE);
+ }
+ else if (ISBUILTIN(BUILTIN_newarray_short)) {
+ TYPECHECK_INT(state->curstack);
+ TYPEINFO_INIT_PRIMITIVE_ARRAY(dst->typeinfo,ARRAYTYPE_SHORT);
+ }
+ else if (ISBUILTIN(BUILTIN_newarray_int)) {
+ TYPECHECK_INT(state->curstack);
+ TYPEINFO_INIT_PRIMITIVE_ARRAY(dst->typeinfo,ARRAYTYPE_INT);
+ }
+ else if (ISBUILTIN(BUILTIN_newarray_long)) {
+ TYPECHECK_INT(state->curstack);
+ TYPEINFO_INIT_PRIMITIVE_ARRAY(dst->typeinfo,ARRAYTYPE_LONG);
+ }
+ else if (ISBUILTIN(BUILTIN_newarray))
+ {
+ TYPECHECK_INT(state->curstack->prev);
+ 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))
+ {
+ TYPECHECK_ADR(state->curstack->prev);
+ if (state->iptr[-1].opc != ICMD_ACONST)
+ TYPECHECK_VERIFYERROR_bool("illegal instruction: builtin_arrayinstanceof without class");
+ /* XXX check that it is an array class(ref) */
+ }
+ else {
+ return verify_generic_builtin(state);
+ }
+ return true;
+}
-/* 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 <init> method
- */
-#define TYPECHECK_LEAVE \
- do { \
- if (initmethod && m->class != class_java_lang_Object) { \
- /* check the marker variable */ \
- LOG("Checking <init> marker"); \
- if (!typevectorset_checktype(localset,numlocals-1,TYPE_INT))\
- panic("<init> method does not initialize 'this'"); \
- } \
- } while (0)
+/* verify_multianewarray *******************************************************
+
+ Verify a MULTIANEWARRAY instruction.
+
+ IN:
+ state............the current state of the verifier
-/****************************************************************************/
-/* typecheck() */
-/****************************************************************************/
+ RETURN VALUE:
+ true.............successful verification,
+ false............an exception has been thrown.
-#define MAXPARAMS 255
+*******************************************************************************/
+
+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;
+}
+
+/* verify_basic_block **********************************************************
+
+ Perform bytecode verification of a basic block.
+
+ IN:
+ state............the current state of the verifier
-/* typecheck is called directly after analyse_stack */
+ RETURN VALUE:
+ true.............successful verification,
+ false............an exception has been thrown.
-methodinfo *typecheck(methodinfo *m, codegendata *cd, registerdata *rd)
+*******************************************************************************/
+
+static bool
+verify_basic_block(verifier_state *state)
{
- int b_count, b_index;
- stackptr curstack; /* input stack top for current instruction */
- stackptr srcstack; /* source stack for copying and merging */
int opcode; /* current opcode */
- 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 */
- instruction *iptr; /* pointer to current instruction */
- basicblock *bptr; /* pointer to current basic block */
basicblock *tbptr; /* temporary for target block */
-
- int numlocals; /* number of local variables */
- 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 */
- typeinfo tempti; /* temporary typeinfo */
-
- 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 instr. */
- exceptiontable **handlers; /* active exception handlers */
classinfo *cls; /* temporary */
bool maythrow; /* true if this instruction may throw */
- static utf *name_init; /* "<init>" */
- bool initmethod; /* true if this is an "<init>" method */
- builtin_descriptor *builtindesc; /* temp. descriptor of builtin */
- bool jsrencountered = false; /* true if we there was a JSR */
-
classinfo *myclass;
+ unresolved_field *uf; /* for field accesses */
+ fieldinfo **fieldinfop; /* for field accesses */
+ s4 i;
+ s4 b_index;
+ typecheck_result r;
-#ifdef TYPECHECK_STATISTICS
- int count_iterations = 0;
- TYPECHECK_COUNT(stat_typechecked);
- TYPECHECK_COUNT_FREQ(stat_locals,cd->maxlocals,STAT_LOCALS);
- TYPECHECK_COUNT_FREQ(stat_blocks,m->basicblockcount/10,STAT_BLOCKS);
+ LOGSTR1("\n---- BLOCK %04d ------------------------------------------------\n",state->bptr->debug_nr);
+ LOGFLUSH;
+
+ superblockend = false;
+ state->bptr->flags = BBFINISHED;
+ b_index = state->bptr - state->m->basicblocks;
+
+ /* init stack at the start of this block */
+ state->curstack = state->bptr->instack;
+
+ /* prevent compiler warnings */
+
+ dst = NULL;
+
+ /* determine the active exception handlers for this block */
+ /* XXX could use a faster algorithm with sorted lists or */
+ /* something? */
+ len = 0;
+ for (i = 0; i < state->cd->exceptiontablelength; ++i) {
+ if ((state->cd->exceptiontable[i].start <= state->bptr) && (state->cd->exceptiontable[i].end > state->bptr)) {
+ LOG1("active handler L%03d", state->cd->exceptiontable[i].handler->debug_nr);
+ state->handlers[len++] = state->cd->exceptiontable + i;
+ }
+ }
+ state->handlers[len] = NULL;
+
+ /* init variable types at the start of this block */
+ typevectorset_copy_inplace(MGET_TYPEVECTOR(state->localbuf,b_index,state->numlocals),
+ state->localset,state->numlocals);
+
+ /* XXX FIXME FOR INLINING */
+ if (!useinlining) {
+ if (state->handlers[0])
+ for (i=0; i<state->numlocals; ++i)
+ if (state->localset->td[i].type == TYPE_ADR
+ && TYPEINFO_IS_NEWOBJECT(state->localset->td[i].info)) {
+ /* XXX we do not check this for the uninitialized 'this' instance in */
+ /* <init> methods. Otherwise there are problems with try blocks in */
+ /* <init>. The spec seems to indicate that we should perform the test*/
+ /* in all cases, but this fails with real code. */
+ /* Example: org/eclipse/ui/internal/PerspectiveBarNewContributionItem*/
+ /* 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 variable: %d, block: %d\n", i, state->bptr->debug_nr);
+ TYPECHECK_VERIFYERROR_bool("Uninitialized object in local variable inside try block");
+ }
+ }
+ }
+ DOLOG(typestate_print(get_logfile(),state->curstack,state->localset,state->numlocals));
+ LOGNL; LOGFLUSH;
+
+ /* loop over the instructions */
+ len = state->bptr->icount;
+ state->iptr = state->bptr->iinstr;
+ while (--len >= 0) {
+ TYPECHECK_COUNT(stat_ins);
+
+ DOLOG(typestate_print(get_logfile(),state->curstack,state->localset,state->numlocals));
+ LOGNL; LOGFLUSH;
+
+ DOLOG(show_icmd(state->iptr,false)); LOGNL; LOGFLUSH;
+
+ opcode = state->iptr->opc;
+ myclass = state->iptr->method->class;
+ dst = state->iptr->dst;
+ maythrow = false;
+
+ switch (opcode) {
+
+ /****************************************/
+ /* STACK MANIPULATIONS */
+
+ /* We just need to copy the typeinfo */
+ /* for slots containing addresses. */
+
+ /* CAUTION: We assume that the destination stack
+ * slots were continuously allocated in
+ * memory! (The current implementation in
+ * stack.c)
+ */
+
+ 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);
+ COPYTYPE(state->curstack->prev->prev,dst-2);
+ 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);
+ COPYTYPE(state->curstack->prev,dst-4);
+ COPYTYPE(state->curstack->prev->prev,dst-2);
+ 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);
+ COPYTYPE(state->curstack->prev,dst-5);
+ COPYTYPE(state->curstack->prev->prev,dst-2);
+ COPYTYPE(state->curstack->prev->prev->prev,dst-3);
+ 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: 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 */
+
+ case ICMD_ALOAD:
+ TYPECHECK_COUNT(stat_ins_aload);
+
+ /* loading a returnAddress is not allowed */
+ if (state->jsrencountered) {
+ if (!typevectorset_checkreference(state->localset,state->iptr->op1)) {
+ TYPECHECK_VERIFYERROR_bool("illegal instruction: ALOAD loading non-reference");
+ }
+ if (typevectorset_copymergedtype(state->m,state->localset,state->iptr->op1,&(dst->typeinfo)) == -1)
+ return false;
+ }
+ else {
+ if (!TYPEDESC_IS_REFERENCE(state->localset->td[state->iptr->op1])) {
+ TYPECHECK_VERIFYERROR_bool("illegal instruction: ALOAD loading non-reference");
+ }
+ TYPEINFO_COPY(state->localset->td[state->iptr->op1].info,dst->typeinfo);
+ }
+ break;
+
+ /****************************************/
+ /* STORING ADDRESS TO VARIABLE */
+
+ case ICMD_ASTORE:
+ if (state->handlers[0] && TYPEINFO_IS_NEWOBJECT(state->curstack->typeinfo)) {
+ TYPECHECK_VERIFYERROR_bool("Storing uninitialized object in local variable inside try block");
+ }
+
+ if (TYPESTACK_IS_RETURNADDRESS(state->curstack)) {
+ typevectorset_store_retaddr(state->localset,state->iptr->op1,&(state->curstack->typeinfo));
+ }
+ else {
+ typevectorset_store(state->localset,state->iptr->op1,TYPE_ADDRESS,
+ &(state->curstack->typeinfo));
+ }
+ break;
+
+ /****************************************/
+ /* LOADING ADDRESS FROM ARRAY */
+
+ case ICMD_AALOAD:
+ if (!TYPEINFO_MAYBE_ARRAY_OF_REFS(state->curstack->prev->typeinfo))
+ TYPECHECK_VERIFYERROR_bool("illegal instruction: AALOAD on non-reference array");
+
+ if (!typeinfo_init_component(&state->curstack->prev->typeinfo,&dst->typeinfo))
+ return false;
+ maythrow = true;
+ break;
+
+ /****************************************/
+ /* FIELD ACCESS */
+
+ case ICMD_PUTFIELDCONST:
+ case ICMD_PUTSTATICCONST:
+ TYPECHECK_COUNT(stat_ins_field);
+
+ uf = INSTRUCTION_PUTCONST_FIELDREF(state->iptr);
+ fieldinfop = INSTRUCTION_PUTCONST_FIELDINFO_PTR(state->iptr);
+
+ goto fieldaccess_tail;
+
+ case ICMD_PUTFIELD:
+ case ICMD_PUTSTATIC:
+ TYPECHECK_COUNT(stat_ins_field);
+
+ uf = (unresolved_field *) state->iptr[0].target;
+ fieldinfop = (fieldinfo **) &(state->iptr[0].val.a);
+
+ goto fieldaccess_tail;
+
+ case ICMD_GETFIELD:
+ case ICMD_GETSTATIC:
+ TYPECHECK_COUNT(stat_ins_field);
+
+ uf = (unresolved_field *) state->iptr[0].target;
+ fieldinfop = (fieldinfo **) &(state->iptr[0].val.a);
+
+ /* the result is pushed on the stack */
+ if (dst->type == TYPE_ADR) {
+ if (!typeinfo_init_from_typedesc(uf->fieldref->parseddesc.fd,NULL,&(dst->typeinfo)))
+ return false;
+ }
+
+fieldaccess_tail:
+ /* record the subtype constraints for this field access */
+ if (!constrain_unresolved_field(uf,state->m->class,state->m,state->iptr,state->curstack))
+ return false; /* XXX maybe wrap exception? */
+
+ /* try to resolve the field reference */
+ if (!resolve_field(uf,resolveLazy,fieldinfop))
+ return false;
+
+ TYPECHECK_COUNTIF(!*fieldinfop,stat_ins_field_unresolved);
+ TYPECHECK_COUNTIF(*fieldinfop && !(*fieldinfop)->class->initialized,stat_ins_field_uninitialized);
+
+ maythrow = true;
+ break;
+
+ /****************************************/
+ /* PRIMITIVE ARRAY ACCESS */
+
+ case ICMD_ARRAYLENGTH:
+ if (!TYPEINFO_MAYBE_ARRAY(state->curstack->typeinfo)
+ && state->curstack->typeinfo.typeclass.cls != pseudo_class_Arraystub)
+ TYPECHECK_VERIFYERROR_bool("illegal instruction: ARRAYLENGTH on non-array");
+ maythrow = true;
+ break;
+
+ case ICMD_BALOAD:
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(state->curstack->prev->typeinfo,ARRAYTYPE_BOOLEAN)
+ && !TYPEINFO_MAYBE_PRIMITIVE_ARRAY(state->curstack->prev->typeinfo,ARRAYTYPE_BYTE))
+ TYPECHECK_VERIFYERROR_bool("Array type mismatch");
+ maythrow = true;
+ break;
+ case ICMD_CALOAD:
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(state->curstack->prev->typeinfo,ARRAYTYPE_CHAR))
+ TYPECHECK_VERIFYERROR_bool("Array type mismatch");
+ maythrow = true;
+ break;
+ case ICMD_DALOAD:
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(state->curstack->prev->typeinfo,ARRAYTYPE_DOUBLE))
+ TYPECHECK_VERIFYERROR_bool("Array type mismatch");
+ maythrow = true;
+ break;
+ case ICMD_FALOAD:
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(state->curstack->prev->typeinfo,ARRAYTYPE_FLOAT))
+ TYPECHECK_VERIFYERROR_bool("Array type mismatch");
+ maythrow = true;
+ break;
+ case ICMD_IALOAD:
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(state->curstack->prev->typeinfo,ARRAYTYPE_INT))
+ TYPECHECK_VERIFYERROR_bool("Array type mismatch");
+ maythrow = true;
+ break;
+ case ICMD_SALOAD:
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(state->curstack->prev->typeinfo,ARRAYTYPE_SHORT))
+ TYPECHECK_VERIFYERROR_bool("Array type mismatch");
+ maythrow = true;
+ break;
+ case ICMD_LALOAD:
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(state->curstack->prev->typeinfo,ARRAYTYPE_LONG))
+ TYPECHECK_VERIFYERROR_bool("Array type mismatch");
+ maythrow = true;
+ break;
+
+ case ICMD_BASTORE:
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(state->curstack->prev->prev->typeinfo,ARRAYTYPE_BOOLEAN)
+ && !TYPEINFO_MAYBE_PRIMITIVE_ARRAY(state->curstack->prev->prev->typeinfo,ARRAYTYPE_BYTE))
+ TYPECHECK_VERIFYERROR_bool("Array type mismatch");
+ maythrow = true;
+ break;
+ case ICMD_CASTORE:
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(state->curstack->prev->prev->typeinfo,ARRAYTYPE_CHAR))
+ TYPECHECK_VERIFYERROR_bool("Array type mismatch");
+ maythrow = true;
+ break;
+ case ICMD_DASTORE:
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(state->curstack->prev->prev->typeinfo,ARRAYTYPE_DOUBLE))
+ TYPECHECK_VERIFYERROR_bool("Array type mismatch");
+ maythrow = true;
+ break;
+ case ICMD_FASTORE:
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(state->curstack->prev->prev->typeinfo,ARRAYTYPE_FLOAT))
+ TYPECHECK_VERIFYERROR_bool("Array type mismatch");
+ maythrow = true;
+ break;
+ case ICMD_IASTORE:
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(state->curstack->prev->prev->typeinfo,ARRAYTYPE_INT))
+ TYPECHECK_VERIFYERROR_bool("Array type mismatch");
+ maythrow = true;
+ break;
+ case ICMD_SASTORE:
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(state->curstack->prev->prev->typeinfo,ARRAYTYPE_SHORT))
+ TYPECHECK_VERIFYERROR_bool("Array type mismatch");
+ maythrow = true;
+ break;
+ case ICMD_LASTORE:
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(state->curstack->prev->prev->typeinfo,ARRAYTYPE_LONG))
+ TYPECHECK_VERIFYERROR_bool("Array type mismatch");
+ maythrow = true;
+ break;
+
+ case ICMD_AASTORE:
+ /* we just check the basic input types and that the */
+ /* destination is an array of references. Assignability to */
+ /* the actual array must be checked at runtime, each time the */
+ /* instruction is performed. (See builtin_canstore.) */
+ TYPECHECK_ADR(state->curstack);
+ TYPECHECK_INT(state->curstack->prev);
+ TYPECHECK_ADR(state->curstack->prev->prev);
+ if (!TYPEINFO_MAYBE_ARRAY_OF_REFS(state->curstack->prev->prev->typeinfo))
+ TYPECHECK_VERIFYERROR_bool("illegal instruction: AASTORE to non-reference array");
+ maythrow = true;
+ break;
+
+ case ICMD_IASTORECONST:
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(state->curstack->prev->typeinfo, ARRAYTYPE_INT))
+ TYPECHECK_VERIFYERROR_bool("Array type mismatch");
+ maythrow = true;
+ break;
+
+ case ICMD_LASTORECONST:
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(state->curstack->prev->typeinfo, ARRAYTYPE_LONG))
+ TYPECHECK_VERIFYERROR_bool("Array type mismatch");
+ maythrow = true;
+ break;
+
+ case ICMD_BASTORECONST:
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(state->curstack->prev->typeinfo, ARRAYTYPE_BOOLEAN)
+ && !TYPEINFO_MAYBE_PRIMITIVE_ARRAY(state->curstack->prev->typeinfo, ARRAYTYPE_BYTE))
+ TYPECHECK_VERIFYERROR_bool("Array type mismatch");
+ maythrow = true;
+ break;
+
+ case ICMD_CASTORECONST:
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(state->curstack->prev->typeinfo, ARRAYTYPE_CHAR))
+ TYPECHECK_VERIFYERROR_bool("Array type mismatch");
+ maythrow = true;
+ break;
+
+ case ICMD_SASTORECONST:
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(state->curstack->prev->typeinfo, ARRAYTYPE_SHORT))
+ TYPECHECK_VERIFYERROR_bool("Array type mismatch");
+ maythrow = true;
+ break;
+
+ /****************************************/
+ /* ADDRESS CONSTANTS */
+
+ case ICMD_ACONST:
+ 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;
+
+ /****************************************/
+ /* CHECKCAST AND INSTANCEOF */
+
+ case ICMD_CHECKCAST:
+ TYPECHECK_ADR(state->curstack);
+ /* returnAddress is not allowed */
+ if (!TYPEINFO_IS_REFERENCE(state->curstack->typeinfo))
+ TYPECHECK_VERIFYERROR_bool("Illegal instruction: CHECKCAST on non-reference");
+
+ cls = (classinfo *) state->iptr[0].val.a;
+ if (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_INSTANCEOF:
+ TYPECHECK_ADR(state->curstack);
+ /* returnAddress is not allowed */
+ if (!TYPEINFO_IS_REFERENCE(state->curstack->typeinfo))
+ TYPECHECK_VERIFYERROR_bool("Illegal instruction: INSTANCEOF on non-reference");
+ break;
+
+ /****************************************/
+ /* BRANCH INSTRUCTIONS */
+
+ case ICMD_GOTO:
+ superblockend = true;
+ /* FALLTHROUGH! */
+ case ICMD_IFNULL:
+ case ICMD_IFNONNULL:
+ case ICMD_IFEQ:
+ case ICMD_IFNE:
+ case ICMD_IFLT:
+ case ICMD_IFGE:
+ case ICMD_IFGT:
+ case ICMD_IFLE:
+ case ICMD_IF_ICMPEQ:
+ case ICMD_IF_ICMPNE:
+ case ICMD_IF_ICMPLT:
+ case ICMD_IF_ICMPGE:
+ case ICMD_IF_ICMPGT:
+ case ICMD_IF_ICMPLE:
+ case ICMD_IF_ACMPEQ:
+ case ICMD_IF_ACMPNE:
+ case ICMD_IF_LEQ:
+ case ICMD_IF_LNE:
+ case ICMD_IF_LLT:
+ case ICMD_IF_LGE:
+ case ICMD_IF_LGT:
+ case ICMD_IF_LLE:
+ case ICMD_IF_LCMPEQ:
+ case ICMD_IF_LCMPNE:
+ case ICMD_IF_LCMPLT:
+ case ICMD_IF_LCMPGE:
+ case ICMD_IF_LCMPGT:
+ case ICMD_IF_LCMPLE:
+ TYPECHECK_COUNT(stat_ins_branch);
+ tbptr = (basicblock *) state->iptr->target;
+
+ /* propagate stack and variables to the target block */
+ if (!typestate_reach(state,tbptr,dst,state->localset))
+ return false;
+ break;
+
+ /****************************************/
+ /* SWITCHES */
+
+ case ICMD_TABLESWITCH:
+ TYPECHECK_COUNT(stat_ins_switch);
+ {
+ s4 *s4ptr = state->iptr->val.a;
+ 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 = state->iptr->val.a;
+ s4ptr++; /* skip default */
+ i = *s4ptr++ + 1; /* count +1 for default */
+ }
+switch_instruction_tail:
+ tptr = (basicblock **)state->iptr->target;
+
+ while (--i >= 0) {
+ tbptr = *tptr++;
+ LOG2("target %d is block %04d",(tptr-(basicblock **)state->iptr->target)-1,tbptr->debug_nr);
+ if (!typestate_reach(state,tbptr,dst,state->localset))
+ return false;
+ }
+ LOG("switch done");
+ superblockend = true;
+ break;
+
+ /****************************************/
+ /* 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;
+ 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");
+
+ if (state->returntype.type != TYPE_ADDRESS
+ || (r = typeinfo_is_assignable(&state->curstack->typeinfo,&(state->returntype.info)))
+ == typecheck_FALSE)
+ TYPECHECK_VERIFYERROR_bool("Return type mismatch");
+ if (r == typecheck_FAIL)
+ return false;
+ 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;
+
+ /****************************************/
+ /* PRIMITIVE RETURNS */
+
+ case ICMD_IRETURN:
+ if (state->returntype.type != TYPE_INT) TYPECHECK_VERIFYERROR_bool("Return type mismatch");
+ goto return_tail;
+
+ case ICMD_LRETURN:
+ if (state->returntype.type != TYPE_LONG) TYPECHECK_VERIFYERROR_bool("Return type mismatch");
+ goto return_tail;
+
+ case ICMD_FRETURN:
+ if (state->returntype.type != TYPE_FLOAT) TYPECHECK_VERIFYERROR_bool("Return type mismatch");
+ goto return_tail;
+
+ case ICMD_DRETURN:
+ if (state->returntype.type != TYPE_DOUBLE) TYPECHECK_VERIFYERROR_bool("Return type mismatch");
+ goto return_tail;
+
+ case ICMD_RETURN:
+ if (state->returntype.type != TYPE_VOID) TYPECHECK_VERIFYERROR_bool("Return type mismatch");
+return_tail:
+ 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;
+
+ /****************************************/
+ /* SUBROUTINE INSTRUCTIONS */
+
+ case ICMD_JSR:
+ LOG("jsr");
+ state->jsrencountered = true;
+
+ /* This is a dirty hack. It is needed
+ * because of the special handling of
+ * ICMD_JSR in stack.c
+ */
+ dst = (stackptr) state->iptr->val.a;
+
+ tbptr = (basicblock *) state->iptr->target;
+ if (state->bptr + 1 == (state->m->basicblocks + state->m->basicblockcount + 1))
+ TYPECHECK_VERIFYERROR_bool("Illegal instruction: JSR at end of bytecode");
+ typestack_put_retaddr(dst,state->bptr+1,state->localset);
+ if (!typestate_reach(state,tbptr,dst,state->localset))
+ return false;
+
+ superblockend = true;
+ break;
+
+ case ICMD_RET:
+ /* check returnAddress variable */
+ if (!typevectorset_checkretaddr(state->localset,state->iptr->op1))
+ TYPECHECK_VERIFYERROR_bool("illegal instruction: RET using non-returnAddress variable");
+
+ if (!typestate_ret(state,state->iptr->op1))
+ return false;
+
+ superblockend = true;
+ break;
+
+ /****************************************/
+ /* INVOKATIONS */
+
+ case ICMD_INVOKEVIRTUAL:
+ case ICMD_INVOKESPECIAL:
+ case ICMD_INVOKESTATIC:
+ case ICMD_INVOKEINTERFACE:
+ 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:
+ if (!verify_multianewarray(state))
+ return false;
+ maythrow = true;
+ break;
+
+ /****************************************/
+ /* BUILTINS */
+
+ case ICMD_BUILTIN:
+ TYPECHECK_COUNT(stat_ins_builtin);
+ if (!verify_builtin(state))
+ return false;
+ maythrow = true;
+ break;
+
+ /****************************************/
+ /* SIMPLE EXCEPTION THROWING TESTS */
+
+ case ICMD_CHECKNULL:
+ /* CHECKNULL just requires that the stack top
+ * is an address. This is checked in stack.c */
+ maythrow = true;
+ break;
+
+ /****************************************/
+ /* INSTRUCTIONS WHICH SHOULD HAVE BEEN */
+ /* REPLACED BY OTHER OPCODES */
+
+#ifdef TYPECHECK_DEBUG
+ case ICMD_NEW:
+ case ICMD_NEWARRAY:
+ case ICMD_ANEWARRAY:
+ case ICMD_MONITORENTER:
+ case ICMD_MONITOREXIT:
+ LOG2("ICMD %d at %d\n", state->iptr->opc, (int)(state->iptr-state->bptr->iinstr));
+ LOG("Should have been converted to builtin function call.");
+ TYPECHECK_ASSERT(false);
+ break;
+
+ case ICMD_READONLY_ARG:
+ case ICMD_CLEAR_ARGREN:
+ LOG2("ICMD %d at %d\n", state->iptr->opc, (int)(state->iptr-state->bptr->iinstr));
+ LOG("Should have been replaced in stack.c.");
+ TYPECHECK_ASSERT(false);
+ break;
#endif
- LOGSTR("\n==============================================================================\n");
- /*DOLOG( show_icmd_method(cd->method,cd,rd));*/
- LOGSTR("\n==============================================================================\n");
- LOGimpSTR("Entering typecheck: ");
- LOGimpSTRu(cd->method->name);
- LOGimpSTR(" ");
- LOGimpSTRu(cd->method->descriptor);
- LOGimpSTR(" (class ");
- LOGimpSTRu(cd->method->class->name);
- LOGimpSTR(")\n");
- LOGFLUSH;
+ /****************************************/
+ /* UNCHECKED OPERATIONS */
- if (!name_init)
- name_init = utf_new_char("<init>");
- initmethod = (m->name == name_init);
+ /*********************************************
+ * Instructions below...
+ * *) don't operate on local variables,
+ * *) don't operate on references,
+ * *) don't operate on returnAddresses,
+ * *) don't affect control flow (except
+ * by throwing exceptions).
+ *
+ * (These instructions are typechecked in
+ * analyse_stack.)
+ ********************************************/
- /* Allocate buffer for method arguments */
-
- ptype = DMNEW(u1,MAXPARAMS);
- pinfo = DMNEW(typeinfo,MAXPARAMS);
-
- LOG("Buffer allocated.\n");
+ /* Instructions which may throw a runtime exception: */
- /* reset all BBFINISHED blocks to BBTYPECHECK_UNDEF. */
- b_count = m->basicblockcount;
- bptr = m->basicblocks;
- while (--b_count >= 0) {
+ 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:
+
+ case ICMD_ICONST:
+ case ICMD_LCONST:
+ case ICMD_FCONST:
+ case ICMD_DCONST:
+
+ case ICMD_IFEQ_ICONST:
+ case ICMD_IFNE_ICONST:
+ case ICMD_IFLT_ICONST:
+ case ICMD_IFGE_ICONST:
+ case ICMD_IFGT_ICONST:
+ case ICMD_IFLE_ICONST:
+ case ICMD_ELSE_ICONST:
+
+ case ICMD_IADD:
+ case ICMD_ISUB:
+ case ICMD_IMUL:
+ case ICMD_INEG:
+ case ICMD_IAND:
+ case ICMD_IOR:
+ case ICMD_IXOR:
+ case ICMD_ISHL:
+ case ICMD_ISHR:
+ case ICMD_IUSHR:
+ case ICMD_LADD:
+ case ICMD_LSUB:
+ case ICMD_LMUL:
+ case ICMD_LNEG:
+ case ICMD_LAND:
+ case ICMD_LOR:
+ case ICMD_LXOR:
+ case ICMD_LSHL:
+ case ICMD_LSHR:
+ case ICMD_LUSHR:
+ case ICMD_IMULPOW2:
+ case ICMD_LMULPOW2:
+ case ICMD_IDIVPOW2:
+ case ICMD_LDIVPOW2:
+ case ICMD_IADDCONST:
+ case ICMD_ISUBCONST:
+ case ICMD_IMULCONST:
+ case ICMD_IANDCONST:
+ case ICMD_IORCONST:
+ case ICMD_IXORCONST:
+ case ICMD_ISHLCONST:
+ case ICMD_ISHRCONST:
+ case ICMD_IUSHRCONST:
+ case ICMD_IREMPOW2:
+ case ICMD_LADDCONST:
+ case ICMD_LSUBCONST:
+ case ICMD_LMULCONST:
+ case ICMD_LANDCONST:
+ case ICMD_LORCONST:
+ case ICMD_LXORCONST:
+ case ICMD_LSHLCONST:
+ case ICMD_LSHRCONST:
+ case ICMD_LUSHRCONST:
+ case ICMD_LREMPOW2:
+
+ case ICMD_I2L:
+ case ICMD_I2F:
+ case ICMD_I2D:
+ case ICMD_L2I:
+ case ICMD_L2F:
+ case ICMD_L2D:
+ case ICMD_F2I:
+ case ICMD_F2L:
+ case ICMD_F2D:
+ case ICMD_D2I:
+ case ICMD_D2L:
+ case ICMD_D2F:
+ case ICMD_INT2BYTE:
+ case ICMD_INT2CHAR:
+ case ICMD_INT2SHORT:
+
+ case ICMD_LCMP:
+ case ICMD_LCMPCONST:
+ case ICMD_FCMPL:
+ case ICMD_FCMPG:
+ case ICMD_DCMPL:
+ case ICMD_DCMPG:
+
+ case ICMD_FADD:
+ case ICMD_DADD:
+ case ICMD_FSUB:
+ case ICMD_DSUB:
+ case ICMD_FMUL:
+ case ICMD_DMUL:
+ case ICMD_FDIV:
+ case ICMD_DDIV:
+ case ICMD_FREM:
+ case ICMD_DREM:
+ case ICMD_FNEG:
+ case ICMD_DNEG:
+
+
+ /*XXX What shall we do with the following ?*/
+ case ICMD_AASTORECONST:
+ TYPECHECK_COUNT(stat_ins_unchecked);
+ break;
+
+ /****************************************/
+
+ default:
+ LOG2("ICMD %d at %d\n", state->iptr->opc, (int)(state->iptr-state->bptr->iinstr));
+ TYPECHECK_VERIFYERROR_bool("Missing ICMD code during typecheck");
+#endif
+ }
+
+ /* the output of this instruction becomes the current stack */
+ state->curstack = dst;
+
+ /* 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]) {
+ TYPECHECK_COUNT(stat_handlers_reached);
+ if (state->handlers[i]->catchtype.any)
+ state->excstack.typeinfo.typeclass = state->handlers[i]->catchtype;
+ else
+ state->excstack.typeinfo.typeclass.cls = class_java_lang_Throwable;
+ if (!typestate_reach(state,
+ state->handlers[i]->handler,
+ &(state->excstack),state->localset))
+ return false;
+ i++;
+ }
+ }
+
+ LOG("next instruction");
+ state->iptr++;
+ } /* while instructions */
+
+ LOG("instructions done");
+ LOGSTR("RESULT=> ");
+ DOLOG(typestate_print(get_logfile(),state->curstack,state->localset,state->numlocals));
+ LOGNL; LOGFLUSH;
+
+ /* propagate stack and variables to the following block */
+ if (!superblockend) {
+ LOG("reaching following block");
+ tbptr = state->bptr + 1;
+ while (tbptr->flags == BBDELETED) {
+ tbptr++;
#ifdef TYPECHECK_DEBUG
- if (bptr->flags != BBFINISHED && bptr->flags != BBDELETED
- && bptr->flags != BBUNDEF)
- {
- /*show_icmd_method(cd->method,cd,rd);*/
- LOGSTR1("block flags: %d\n",bptr->flags); LOGFLUSH;
- panic("Internal error: Unexpected block flags in typecheck()");
- }
+ /* this must be checked in parse.c */
+ if ((tbptr->debug_nr) >= state->m->basicblockcount)
+ TYPECHECK_VERIFYERROR_bool("Control flow falls off the last block");
#endif
- if (bptr->flags >= BBFINISHED) {
- bptr->flags = BBTYPECHECK_UNDEF;
- }
- bptr++;
- }
+ }
+ if (!typestate_reach(state,tbptr,dst,state->localset))
+ return false;
+ }
- /* The first block is always reached */
- if (m->basicblockcount && m->basicblocks[0].flags == BBTYPECHECK_UNDEF)
- m->basicblocks[0].flags = BBTYPECHECK_REACHED;
+ /* We may have to restore the types of the instack slots. They
+ * have been saved if an <init> call inside the block has
+ * modified the instack types. (see INVOKESPECIAL) */
- LOG("Blocks reset.\n");
+ if (state->savedstack)
+ typestate_restore_instack(state);
- /* number of local variables */
-
- /* In <init> methods we use an extra local variable to signal if
- * the 'this' reference has been initialized. */
- numlocals = cd->maxlocals;
- validlocals = numlocals;
- if (initmethod) numlocals++;
+ return true;
+}
- /* allocate the buffers for local variables */
- localbuf = DMNEW_TYPEVECTOR(m->basicblockcount+1, numlocals);
- localset = MGET_TYPEVECTOR(localbuf,m->basicblockcount,numlocals);
+/* verify_init_locals **********************************************************
+
+ Initialize the local variables in the verifier state.
+
+ IN:
+ state............the current state of the verifier
- LOG("Variable buffer allocated.\n");
+ RETURN VALUE:
+ true.............success,
+ false............an exception has been thrown.
- /* allocate the buffer of active exception handlers */
- handlers = DMNEW(exceptiontable*, cd->exceptiontablelength + 1);
+*******************************************************************************/
+
+static bool
+verify_init_locals(verifier_state *state)
+{
+ int i;
+ typedescriptor *td;
+ typevector *lset;
/* initialize the variable types of the first block */
/* to the types of the arguments */
- lset = MGET_TYPEVECTOR(localbuf,0,numlocals);
+
+ lset = MGET_TYPEVECTOR(state->localbuf,0,state->numlocals);
lset->k = 0;
lset->alt = NULL;
td = lset->td;
- i = validlocals;
+ i = state->validlocals;
+
+ /* allocate parameter descriptors if necessary */
+
+ if (!state->m->parseddesc->params)
+ if (!descriptor_params_from_paramtypes(state->m->parseddesc,state->m->flags))
+ return false;
/* if this is an instance method initialize the "this" ref type */
- if (!(m->flags & ACC_STATIC)) {
+
+ if (!(state->m->flags & ACC_STATIC)) {
if (!i)
- panic("Not enough local variables for method arguments");
+ TYPECHECK_VERIFYERROR_bool("Not enough local variables for method arguments");
td->type = TYPE_ADDRESS;
- if (initmethod)
+ if (state->initmethod)
TYPEINFO_INIT_NEWOBJECT(td->info,NULL);
else
- TYPEINFO_INIT_CLASSINFO(td->info, m->class);
+ typeinfo_init_classinfo(&(td->info), state->m->class);
td++;
i--;
}
LOG("'this' argument set.\n");
/* the rest of the arguments and the return type */
- i = typedescriptors_init_from_method_args(td, m->descriptor,
+
+ i = typedescriptors_init_from_methoddesc(td, state->m->parseddesc,
i,
true, /* two word types use two slots */
- &returntype);
+ (td - lset->td), /* skip 'this' pointer */
+ &state->returntype);
+ if (i == -1)
+ return false;
td += i;
- i = numlocals - (td - lset->td);
+
+ /* variables not used for arguments are initialized to TYPE_VOID */
+
+ i = state->numlocals - (td - lset->td);
while (i--) {
td->type = TYPE_VOID;
td++;
}
LOG("Arguments set.\n");
+ return true;
+}
- /* initialize the input stack of exception handlers */
- excstack.prev = NULL;
- excstack.type = TYPE_ADR;
- TYPEINFO_INIT_CLASSINFO(excstack.typeinfo,
- class_java_lang_Throwable); /* changed later */
+/* typecheck_init_flags ********************************************************
+
+ Initialize the basic block flags for the following CFG traversal.
+
+ IN:
+ state............the current state of the verifier
- LOG("Exception handler stacks set.\n");
+*******************************************************************************/
- /* loop while there are still blocks to be checked */
- do {
- TYPECHECK_COUNT(count_iterations);
+static void
+typecheck_init_flags(verifier_state *state)
+{
+ s4 i;
+ basicblock *block;
- repeat = false;
-
- b_count = m->basicblockcount;
- bptr = m->basicblocks;
+ /* set all BBFINISHED blocks to BBTYPECHECK_UNDEF. */
+
+ i = state->m->basicblockcount;
+ block = state->m->basicblocks;
- while (--b_count >= 0) {
- LOGSTR1("---- BLOCK %04d, ",bptr->debug_nr);
- LOGSTR1("blockflags: %d\n",bptr->flags);
- LOGFLUSH;
-
- if (bptr->flags == BBTYPECHECK_REACHED) {
- LOGSTR1("\n---- BLOCK %04d ------------------------------------------------\n",bptr->debug_nr);
- LOGFLUSH;
-
- superblockend = false;
- bptr->flags = BBFINISHED;
- b_index = bptr - m->basicblocks;
-
- /* init stack at the start of this block */
- curstack = bptr->instack;
-
- /* determine the active exception handlers for this block */
- /* XXX could use a faster algorithm with sorted lists or
- * something? */
- len = 0;
- for (i = 0; i < 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; i<numlocals; ++i)
- if (localset->td[i].type == TYPE_ADR
- && TYPEINFO_IS_NEWOBJECT(localset->td[i].info)) {
- show_icmd_method(m, cd, rd);
- printf("Uninitialized variale: %d, block: %d\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) {
- TYPECHECK_COUNT(stat_ins);
- DOLOG(show_icmd(iptr,false)); LOGNL; LOGFLUSH;
-
- opcode = iptr->opc;
- myclass = iptr->method->class;
- dst = iptr->dst;
- maythrow = false;
-
- switch (opcode) {
-
- /****************************************/
- /* STACK MANIPULATIONS */
-
- /* 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;
-
- case ICMD_DUP_X1:
- COPYTYPE(curstack,dst);
- COPYTYPE(curstack,dst-2);
- COPYTYPE(curstack->prev,dst-1);
- break;
-
- case ICMD_DUP_X2:
- COPYTYPE(curstack,dst);
- COPYTYPE(curstack,dst-3);
- COPYTYPE(curstack->prev,dst-1);
- COPYTYPE(curstack->prev->prev,dst-2);
- break;
-
- case ICMD_DUP2:
- COPYTYPE(curstack,dst);
- COPYTYPE(curstack->prev,dst-1);
- break;
-
- case ICMD_DUP2_X1:
- COPYTYPE(curstack,dst);
- COPYTYPE(curstack->prev,dst-1);
- COPYTYPE(curstack,dst-3);
- COPYTYPE(curstack->prev,dst-4);
- COPYTYPE(curstack->prev->prev,dst-2);
- break;
-
- case ICMD_DUP2_X2:
- COPYTYPE(curstack,dst);
- COPYTYPE(curstack->prev,dst-1);
- COPYTYPE(curstack,dst-4);
- COPYTYPE(curstack->prev,dst-5);
- COPYTYPE(curstack->prev->prev,dst-2);
- COPYTYPE(curstack->prev->prev->prev,dst-3);
- break;
-
- case ICMD_SWAP:
- COPYTYPE(curstack,dst-1);
- COPYTYPE(curstack->prev,dst);
- 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:
- TYPECHECK_COUNT(stat_ins_aload);
-
- /* loading a returnAddress is not allowed */
- 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:
- 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;
-
- /****************************************/
- /* LOADING ADDRESS FROM ARRAY */
-
- case ICMD_AALOAD:
- if (!TYPEINFO_MAYBE_ARRAY_OF_REFS(curstack->prev->typeinfo))
- panic("illegal instruction: AALOAD on non-reference array");
-
- typeinfo_init_component(&curstack->prev->typeinfo,&dst->typeinfo);
- maythrow = true;
- break;
-
- /****************************************/
- /* FIELD ACCESS */
-
- case ICMD_PUTFIELD:
- case ICMD_PUTFIELDCONST:
- TYPECHECK_COUNT(stat_ins_field);
- {
- fieldinfo *fi;
- int type;
- typeinfo *temptip;
- stackelement *invocant;
-
- /* determine the invocant of this instruction */
- /* and type and typeinfo of the value to be set */
- if (opcode == ICMD_PUTFIELDCONST) {
- fi = INSTRUCTION_PUTCONST_FIELDINFO(iptr);
- invocant = curstack;
- type = INSTRUCTION_PUTCONST_TYPE(iptr);
- if (type == TYPE_ADR) {
- temptip = &tempti;
- if (!iptr[0].op1)
- TYPEINFO_INIT_NULLTYPE(tempti);
- else
- TYPEINFO_INIT_CLASSINFO(tempti,class_java_lang_String);
- }
- }
- else {
- /* ICMD_PUTFIELD */
- fi = (fieldinfo*) iptr[0].val.a;
- invocant = curstack->prev;
- type = curstack->type;
- if (type == TYPE_ADR)
- temptip = &(curstack->typeinfo);
- }
-
- /* check the type of the invocant */
- if (!TYPEINFO_IS_REFERENCE(invocant->typeinfo))
- panic("illegal instruction: PUTFIELD(CONST) on non-reference");
- if (TYPEINFO_IS_ARRAY(invocant->typeinfo))
- panic("illegal instruction: PUTFIELD(CONST) on array");
-
- /* check if the field is accessible */
- if (TYPEINFO_IS_NEWOBJECT(invocant->typeinfo)) {
- if (initmethod
- && !TYPEINFO_NEWOBJECT_INSTRUCTION(invocant->typeinfo))
- {
- /* uninitialized "this" instance */
- if (fi->class != m->class || (fi->flags & ACC_STATIC) != 0)
- panic("Setting unaccessible field in uninitialized object");
- }
- else {
- panic("PUTFIELD(CONST) on uninitialized object");
- }
- }
- else {
- if (!is_accessible(fi->flags,fi->class,fi->class, myclass,
- &(invocant->typeinfo)))
- panic("PUTFIELD(CONST): field is not accessible");
- }
-
- /* check if the value is assignable to the field */
- if (type != fi->type)
- panic("PUTFIELD(CONST) type mismatch");
- if (type == TYPE_ADR) {
- TYPEINFO_INIT_FROM_FIELDINFO(rinfo,fi);
- if (!typeinfo_is_assignable(temptip,
- &rinfo))
- panic("PUTFIELD(CONST) reference type not assignable");
- }
- }
- maythrow = true;
- break;
-
- case ICMD_PUTSTATIC:
- case ICMD_PUTSTATICCONST:
- TYPECHECK_COUNT(stat_ins_field);
- {
- fieldinfo *fi;
- int type;
- typeinfo *temptip;
-
- /* determine type and typeinfo of the value to be set */
- if (opcode == ICMD_PUTSTATICCONST) {
- fi = INSTRUCTION_PUTCONST_FIELDINFO(iptr);
- type = INSTRUCTION_PUTCONST_TYPE(iptr);
- if (type == TYPE_ADR) {
- temptip = &tempti;
- if (!iptr[0].op1)
- TYPEINFO_INIT_NULLTYPE(tempti);
- else
- TYPEINFO_INIT_CLASSINFO(tempti,class_java_lang_String);
- }
- }
- else {
- /* ICMD_PUTSTATIC */
- fi = (fieldinfo*) iptr[0].val.a;
- type = curstack->type;
- if (type == TYPE_ADR)
- temptip = &(curstack->typeinfo);
- }
-
- /* check if the field is accessible */
- if (!is_accessible(fi->flags,fi->class,fi->class,myclass,NULL))
- panic("PUTSTATIC(CONST): field is not accessible");
-
- /* check if the value is assignable to the field */
- if (type != fi->type)
- panic("PUTSTATIC(CONST) type mismatch");
- if (type == TYPE_ADR) {
- TYPEINFO_INIT_FROM_FIELDINFO(rinfo,fi);
- if (!typeinfo_is_assignable(temptip,
- &rinfo))
- panic("PUTSTATIC(CONST) 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))
- panic("illegal instruction: GETFIELD on array");
-
- {
- fieldinfo *fi = (fieldinfo *)(iptr->val.a);
-
- 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);
- }
- }
- maythrow = true;
- break;
-
- case ICMD_GETSTATIC:
- TYPECHECK_COUNT(stat_ins_field);
- {
- fieldinfo *fi = (fieldinfo *)(iptr->val.a);
-
- if (!is_accessible(fi->flags,fi->class,fi->class,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);
- }
- }
- maythrow = true;
- break;
-
- /****************************************/
- /* PRIMITIVE ARRAY ACCESS */
-
- case ICMD_ARRAYLENGTH:
- 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))
- panic("Array type mismatch");
- maythrow = true;
- break;
- case ICMD_CALOAD:
- if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(curstack->prev->typeinfo,ARRAYTYPE_CHAR))
- panic("Array type mismatch");
- maythrow = true;
- break;
- case ICMD_DALOAD:
- if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(curstack->prev->typeinfo,ARRAYTYPE_DOUBLE))
- panic("Array type mismatch");
- maythrow = true;
- break;
- case ICMD_FALOAD:
- if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(curstack->prev->typeinfo,ARRAYTYPE_FLOAT))
- panic("Array type mismatch");
- maythrow = true;
- break;
- case ICMD_IALOAD:
- if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(curstack->prev->typeinfo,ARRAYTYPE_INT))
- panic("Array type mismatch");
- maythrow = true;
- break;
- case ICMD_SALOAD:
- if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(curstack->prev->typeinfo,ARRAYTYPE_SHORT))
- panic("Array type mismatch");
- maythrow = true;
- break;
- case ICMD_LALOAD:
- if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(curstack->prev->typeinfo,ARRAYTYPE_LONG))
- panic("Array type mismatch");
- maythrow = true;
- break;
-
- case ICMD_BASTORE:
- if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(curstack->prev->prev->typeinfo,ARRAYTYPE_BOOLEAN)
- && !TYPEINFO_MAYBE_PRIMITIVE_ARRAY(curstack->prev->prev->typeinfo,ARRAYTYPE_BYTE))
- panic("Array type mismatch");
- maythrow = true;
- break;
- case ICMD_CASTORE:
- if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(curstack->prev->prev->typeinfo,ARRAYTYPE_CHAR))
- panic("Array type mismatch");
- maythrow = true;
- break;
- case ICMD_DASTORE:
- if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(curstack->prev->prev->typeinfo,ARRAYTYPE_DOUBLE))
- panic("Array type mismatch");
- maythrow = true;
- break;
- case ICMD_FASTORE:
- if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(curstack->prev->prev->typeinfo,ARRAYTYPE_FLOAT))
- panic("Array type mismatch");
- maythrow = true;
- break;
- case ICMD_IASTORE:
- if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(curstack->prev->prev->typeinfo,ARRAYTYPE_INT))
- panic("Array type mismatch");
- maythrow = true;
- break;
- case ICMD_SASTORE:
- if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(curstack->prev->prev->typeinfo,ARRAYTYPE_SHORT))
- panic("Array type mismatch");
- maythrow = true;
- break;
- case ICMD_LASTORE:
- if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(curstack->prev->prev->typeinfo,ARRAYTYPE_LONG))
- panic("Array type mismatch");
- maythrow = true;
- break;
-
- 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;
-
- /****************************************/
- /* CHECKCAST AND INSTANCEOF */
-
- case ICMD_CHECKCAST:
- TYPECHECK_ADR(curstack);
- /* returnAddress is not allowed */
- if (!TYPEINFO_IS_REFERENCE(curstack->typeinfo))
- panic("Illegal instruction: CHECKCAST on non-reference");
-
- TYPEINFO_INIT_CLASSINFO(dst->typeinfo,(classinfo *)iptr[0].val.a);
- 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");
- break;
-
- /****************************************/
- /* BRANCH INSTRUCTIONS */
-
- case ICMD_GOTO:
- superblockend = true;
- /* FALLTHROUGH! */
- case ICMD_IFNULL:
- case ICMD_IFNONNULL:
- case ICMD_IFEQ:
- case ICMD_IFNE:
- case ICMD_IFLT:
- case ICMD_IFGE:
- case ICMD_IFGT:
- case ICMD_IFLE:
- case ICMD_IF_ICMPEQ:
- case ICMD_IF_ICMPNE:
- case ICMD_IF_ICMPLT:
- case ICMD_IF_ICMPGE:
- case ICMD_IF_ICMPGT:
- case ICMD_IF_ICMPLE:
- case ICMD_IF_ACMPEQ:
- case ICMD_IF_ACMPNE:
- case ICMD_IF_LEQ:
- case ICMD_IF_LNE:
- case ICMD_IF_LLT:
- case ICMD_IF_LGE:
- case ICMD_IF_LGT:
- case ICMD_IF_LLE:
- case ICMD_IF_LCMPEQ:
- case ICMD_IF_LCMPNE:
- case ICMD_IF_LCMPLT:
- 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;
- break;
-
- /****************************************/
- /* SWITCHES */
-
- case ICMD_TABLESWITCH:
- TYPECHECK_COUNT(stat_ins_switch);
- {
- s4 *s4ptr = iptr->val.a;
- 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 */
- }
- switch_instruction_tail:
- tptr = (basicblock **)iptr->target;
-
- while (--i >= 0) {
- tbptr = *tptr++;
- LOG2("target %d is block %04d",(tptr-(basicblock **)iptr->target)-1,tbptr->debug_nr);
- TYPECHECK_REACH;
- }
- LOG("switch done");
- superblockend = true;
- break;
-
- /****************************************/
- /* RETURNS AND THROW */
-
- case ICMD_ATHROW:
- if (!typeinfo_is_assignable_to_classinfo(
- &curstack->typeinfo,class_java_lang_Throwable))
- panic("illegal instruction: ATHROW on non-Throwable");
- superblockend = true;
- maythrow = true;
- break;
-
- case ICMD_ARETURN:
- if (!TYPEINFO_IS_REFERENCE(curstack->typeinfo))
- panic("illegal instruction: ARETURN on non-reference");
-
- if (returntype.type != TYPE_ADDRESS
- || !typeinfo_is_assignable(&curstack->typeinfo,&(returntype.info)))
- panic("Return type mismatch");
- goto return_tail;
-
- case ICMD_IRETURN:
- if (returntype.type != TYPE_INT) panic("Return type mismatch");
- goto return_tail;
-
- case ICMD_LRETURN:
- if (returntype.type != TYPE_LONG) panic("Return type mismatch");
- goto return_tail;
-
- case ICMD_FRETURN:
- if (returntype.type != TYPE_FLOAT) panic("Return type mismatch");
- goto return_tail;
-
- case ICMD_DRETURN:
- if (returntype.type != TYPE_DOUBLE) panic("Return type mismatch");
- goto return_tail;
-
- case ICMD_RETURN:
- if (returntype.type != TYPE_VOID) panic("Return type mismatch");
- return_tail:
- TYPECHECK_LEAVE;
- superblockend = true;
- maythrow = true;
- break;
-
- /****************************************/
- /* SUBROUTINE INSTRUCTIONS */
-
- case ICMD_JSR:
- LOG("jsr");
- jsrencountered = true;
-
- /* This is a dirty hack. It is needed
- * because of the special handling of
- * ICMD_JSR in stack.c
- */
- dst = (stackptr) iptr->val.a;
-
- tbptr = (basicblock *) iptr->target;
- 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);
-
- superblockend = true;
- break;
-
- case ICMD_RET:
- /* check returnAddress variable */
- if (!typevectorset_checkretaddr(localset,iptr->op1))
- panic("illegal instruction: RET using non-returnAddress variable");
-
- repeat |= typestate_ret(cd,rd, localbuf,bptr,curstack,
- localset,iptr->op1,numlocals);
-
- superblockend = true;
- break;
-
- /****************************************/
- /* INVOKATIONS */
-
- case ICMD_INVOKEVIRTUAL:
- case ICMD_INVOKESPECIAL:
- case ICMD_INVOKESTATIC:
- case ICMD_INVOKEINTERFACE:
- TYPECHECK_COUNT(stat_ins_invoke);
- {
- methodinfo *mi = (methodinfo*) iptr->val.a;
- bool specialmethod = (mi->name->text[0] == '<');
- 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 */
- i = 0;
- if (opcode != ICMD_INVOKESTATIC) {
- ptype[0] = TYPE_ADR;
- TYPEINFO_INIT_CLASSINFO(pinfo[0],mi->class);
- i++;
- }
- typeinfo_init_from_method_args(mi->descriptor,ptype+i,pinfo+i,
- MAXPARAMS-i,false,
- &rtype,&rinfo);
-
- /* check parameter types */
- srcstack = curstack;
- i = mi->paramcount; /* number of parameters including 'this'*/
- while (--i >= 0) {
- LOG1("param %d",i);
- if (srcstack->type != ptype[i])
- panic("Parameter type mismatch in method invocation");
- if (srcstack->type == TYPE_ADR) {
- LOGINFO(&(srcstack->typeinfo));
- LOGINFO(pinfo + i);
- if (i==0 && callinginit)
- {
- /* first argument to <init> method */
- if (!TYPEINFO_IS_NEWOBJECT(srcstack->typeinfo))
- panic("Calling <init> on initialized object");
-
- /* get the address of the NEW instruction */
- LOGINFO(&(srcstack->typeinfo));
- ins = (instruction*)TYPEINFO_NEWOBJECT_INSTRUCTION(srcstack->typeinfo);
- initclass = (ins) ? (classinfo*)ins[-1].val.a : 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 <init> invocation"); */
- }
- else {
- if (!typeinfo_is_assignable(&(srcstack->typeinfo),pinfo+i))
- panic("Parameter reference type mismatch in method invocation");
- }
- }
- LOG("ok");
-
- if (i) srcstack = srcstack->prev;
- }
-
- /* XXX We should resolve the method and pass its
- * class as implementingclass to is_accessible. */
- /* JOWENN: FIXME (and on other invokation places of is_accessible too) */
- if (!is_accessible(mi->flags,mi->class,/*NULL have to check what here should really be*/ mi->class /*dont't crash right now*/, 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) {
+ while (--i >= 0) {
+
#ifdef TYPECHECK_DEBUG
- if (!initmethod)
- panic("Internal error: calling <init> on this in non-<init> method.");
+ /* 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
- /* must be <init> of current class or direct superclass */
- if (mi->class != m->class && mi->class != m->class->super)
- panic("<init> calling <init> of the wrong class");
-
- /* set our marker variable to type int */
- LOG("setting <init> marker");
- typevectorset_store(localset,numlocals-1,TYPE_INT,NULL);
- }
- else {
- /* initializing an instance created with NEW */
- /* XXX is this strictness ok? */
- if (mi->class != initclass)
- panic("Calling <init> method of the wrong class");
- }
- }
- }
- maythrow = true;
- break;
-
- case ICMD_MULTIANEWARRAY:
- {
- 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:
- 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");
- }
- 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:
- 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");
- 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(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: 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);
- }
- 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:
- TYPECHECK_COUNT(stat_ins_builtin);
- if (ISBUILTIN(BUILTIN_new)) {
-
- if (iptr[-1].opc != ICMD_ACONST)
- panic("illegal instruction: builtin_new without classinfo");
- cls = (classinfo *) iptr[-1].val.a;
- if (!cls->linked)
- panic("Internal error: NEW with unlinked class");
- /* The following check also forbids array classes and interfaces: */
- if ((cls->flags & ACC_ABSTRACT) != 0)
- panic("Invalid instruction: NEW creating instance of abstract class");
- TYPEINFO_INIT_NEWOBJECT(dst->typeinfo,iptr);
- }
- else if (ISBUILTIN(BUILTIN_newarray_boolean)) {
- TYPECHECK_INT(curstack);
- TYPEINFO_INIT_PRIMITIVE_ARRAY(dst->typeinfo,ARRAYTYPE_BOOLEAN);
- }
- else if (ISBUILTIN(BUILTIN_newarray_char)) {
- TYPECHECK_INT(curstack);
- TYPEINFO_INIT_PRIMITIVE_ARRAY(dst->typeinfo,ARRAYTYPE_CHAR);
- }
- else if (ISBUILTIN(BUILTIN_newarray_float)) {
- TYPECHECK_INT(curstack);
- TYPEINFO_INIT_PRIMITIVE_ARRAY(dst->typeinfo,ARRAYTYPE_FLOAT);
- }
- else if (ISBUILTIN(BUILTIN_newarray_double)) {
- TYPECHECK_INT(curstack);
- TYPEINFO_INIT_PRIMITIVE_ARRAY(dst->typeinfo,ARRAYTYPE_DOUBLE);
- }
- else if (ISBUILTIN(BUILTIN_newarray_byte)) {
- TYPECHECK_INT(curstack);
- TYPEINFO_INIT_PRIMITIVE_ARRAY(dst->typeinfo,ARRAYTYPE_BYTE);
- }
- else if (ISBUILTIN(BUILTIN_newarray_short)) {
- TYPECHECK_INT(curstack);
- TYPEINFO_INIT_PRIMITIVE_ARRAY(dst->typeinfo,ARRAYTYPE_SHORT);
- }
- else if (ISBUILTIN(BUILTIN_newarray_int)) {
- TYPECHECK_INT(curstack);
- TYPEINFO_INIT_PRIMITIVE_ARRAY(dst->typeinfo,ARRAYTYPE_INT);
- }
- else if (ISBUILTIN(BUILTIN_newarray_long)) {
- TYPECHECK_INT(curstack);
- TYPEINFO_INIT_PRIMITIVE_ARRAY(dst->typeinfo,ARRAYTYPE_LONG);
- }
- 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;
-
- /****************************************/
- /* 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;
-
- /****************************************/
- /* INSTRUCTIONS WHICH SHOULD HAVE BEEN */
- /* REPLACED BY OTHER OPCODES */
-#ifdef TYPECHECK_DEBUG
- case ICMD_NEW:
- case ICMD_NEWARRAY:
- case ICMD_ANEWARRAY:
- case ICMD_MONITORENTER:
- case ICMD_MONITOREXIT:
- case ICMD_AASTORE:
- LOG2("ICMD %d at %d\n", iptr->opc, (int)(iptr-bptr->iinstr));
- 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-bptr->iinstr));
- LOG("Should have been replaced in stack.c.");
- panic("Internal error: unexpected pseudo instruction encountered");
- break;
-#endif
-
- /****************************************/
- /* UNCHECKED OPERATIONS */
-
- /*********************************************
- * 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:
-
- case ICMD_ICONST:
- case ICMD_LCONST:
- case ICMD_FCONST:
- case ICMD_DCONST:
-
- case ICMD_IFEQ_ICONST:
- case ICMD_IFNE_ICONST:
- case ICMD_IFLT_ICONST:
- case ICMD_IFGE_ICONST:
- case ICMD_IFGT_ICONST:
- case ICMD_IFLE_ICONST:
- case ICMD_ELSE_ICONST:
-
- case ICMD_IADD:
- case ICMD_ISUB:
- case ICMD_IMUL:
- case ICMD_INEG:
- case ICMD_IAND:
- case ICMD_IOR:
- case ICMD_IXOR:
- case ICMD_ISHL:
- case ICMD_ISHR:
- case ICMD_IUSHR:
- case ICMD_LADD:
- case ICMD_LSUB:
- case ICMD_LMUL:
- case ICMD_LNEG:
- case ICMD_LAND:
- case ICMD_LOR:
- case ICMD_LXOR:
- case ICMD_LSHL:
- case ICMD_LSHR:
- case ICMD_LUSHR:
-#if 0
- case ICMD_IREM0X10001:
- case ICMD_LREM0X10001:
-#endif
- case ICMD_IDIVPOW2:
- case ICMD_LDIVPOW2:
- case ICMD_IADDCONST:
- case ICMD_ISUBCONST:
- case ICMD_IMULCONST:
- case ICMD_IANDCONST:
- case ICMD_IORCONST:
- case ICMD_IXORCONST:
- case ICMD_ISHLCONST:
- case ICMD_ISHRCONST:
- case ICMD_IUSHRCONST:
- case ICMD_IREMPOW2:
- case ICMD_LADDCONST:
- case ICMD_LSUBCONST:
- case ICMD_LMULCONST:
- case ICMD_LANDCONST:
- case ICMD_LORCONST:
- case ICMD_LXORCONST:
- case ICMD_LSHLCONST:
- case ICMD_LSHRCONST:
- case ICMD_LUSHRCONST:
- case ICMD_LREMPOW2:
-
- case ICMD_I2L:
- case ICMD_I2F:
- case ICMD_I2D:
- case ICMD_L2I:
- case ICMD_L2F:
- case ICMD_L2D:
- case ICMD_F2I:
- case ICMD_F2L:
- case ICMD_F2D:
- case ICMD_D2I:
- case ICMD_D2L:
- case ICMD_D2F:
- case ICMD_INT2BYTE:
- case ICMD_INT2CHAR:
- case ICMD_INT2SHORT:
-
- case ICMD_LCMP:
- case ICMD_LCMPCONST:
- case ICMD_FCMPL:
- case ICMD_FCMPG:
- case ICMD_DCMPL:
- case ICMD_DCMPG:
-
- case ICMD_FADD:
- case ICMD_DADD:
- case ICMD_FSUB:
- case ICMD_DSUB:
- case ICMD_FMUL:
- case ICMD_DMUL:
- case ICMD_FDIV:
- case ICMD_DDIV:
- case ICMD_FREM:
- case ICMD_DREM:
- case ICMD_FNEG:
- case ICMD_DNEG:
-
-
- /*What shall we do with the following ?*/
- case ICMD_CHECKEXCEPTION:
- case ICMD_AASTORECONST:
- TYPECHECK_COUNT(stat_ins_unchecked);
- break;
-
- /****************************************/
-
- default:
- LOG2("ICMD %d at %d\n", iptr->opc, (int)(iptr-bptr->iinstr));
- panic("Missing ICMD code during typecheck");
-#endif
- }
+ if (block->flags >= BBFINISHED) {
+ block->flags = BBTYPECHECK_UNDEF;
+ }
+ block++;
+ }
- /* the output of this instruction becomes the current stack */
- curstack = dst;
-
- /* reach exception handlers for this instruction */
- if (maythrow) {
- LOG("reaching exception handlers");
- i = 0;
- while (handlers[i]) {
- 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(typestate_print(get_logfile(),curstack,localset,numlocals));
- LOGNL; LOGFLUSH;
-
- /* propagate stack and variables to the following block */
- if (!superblockend) {
- LOG("reaching following block");
- tbptr = bptr + 1;
- while (tbptr->flags == BBDELETED) {
- tbptr++;
-#ifdef TYPECHECK_DEBUG
- if ((tbptr->debug_nr) >= m->basicblockcount)
- panic("Control flow falls off the last block");
-#endif
- }
- TYPECHECK_REACH;
- }
+ /* the first block is always reached */
+
+ if (state->m->basicblockcount && state->m->basicblocks[0].flags == BBTYPECHECK_UNDEF)
+ state->m->basicblocks[0].flags = BBTYPECHECK_REACHED;
+}
- /* We may have to restore the types of the instack slots. They
- * have been saved if an <init> call inside the block has
- * modified the instack types. (see INVOKESPECIAL) */
-
- if (savedstack) {
- stackptr sp = bptr->instack;
- stackptr copy = savedstack;
- TYPECHECK_COUNT(stat_savedstack);
- LOG("restoring saved instack");
- TYPESTACK_COPY(sp,copy);
- bptr->instack = savedstack;
- savedstack = NULL;
- }
-
- } /* if block has to be checked */
- bptr++;
- } /* while blocks */
+/* typecheck_reset_flags *******************************************************
+
+ Reset the flags of basic blocks we have not reached.
+
+ IN:
+ state............the current state of the verifier
- LOGIF(repeat,"repeat=true");
- } while (repeat);
+*******************************************************************************/
-#ifdef TYPECHECK_STATISTICS
- dolog("Typechecker did %4d iterations",count_iterations);
- TYPECHECK_COUNT_FREQ(stat_iterations,count_iterations,STAT_ITERATIONS);
- TYPECHECK_COUNTIF(jsrencountered,stat_typechecked_jsr);
-#endif
+static void
+typecheck_reset_flags(verifier_state *state)
+{
+ s4 i;
+ /* check for invalid flags at exit */
+
#ifdef TYPECHECK_DEBUG
- for (i=0; i<m->basicblockcount; ++i) {
- if (m->basicblocks[i].flags != BBDELETED
- && m->basicblocks[i].flags != BBUNDEF
- && m->basicblocks[i].flags != BBFINISHED
- && m->basicblocks[i].flags != BBTYPECHECK_UNDEF) /* typecheck may never reach
+ 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",
- m->basicblocks[i].debug_nr,m->basicblocks[i].flags);
- panic("Invalid block flags after typecheck");
+ state->m->basicblocks[i].debug_nr,state->m->basicblocks[i].flags);
+ TYPECHECK_ASSERT(false);
}
}
#endif
/* Reset blocks we never reached */
- for (i=0; i<m->basicblockcount; ++i) {
- if (m->basicblocks[i].flags == BBTYPECHECK_UNDEF)
- m->basicblocks[i].flags = BBFINISHED;
+
+ 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");
+/****************************************************************************/
+/* typecheck() */
+/* This is the main function of the bytecode verifier. It is called */
+/* directly after analyse_stack. */
+/* */
+/* IN: */
+/* meth.............the method to verify */
+/* cdata............codegendata for the method */
+/* rdata............registerdata for the method */
+/* */
+/* RETURN VALUE: */
+/* m................successful verification */
+/* NULL.............an exception has been thrown */
+/* */
+/* XXX TODO: */
+/* Bytecode verification has not been tested with inlining and */
+/* probably does not work correctly with inlining. */
+/****************************************************************************/
- /* just return methodinfo* to signal everything was ok */
+#define MAXPARAMS 255
- return m;
-}
+methodinfo *typecheck(methodinfo *meth, codegendata *cdata, registerdata *rdata)
+{
+ verifier_state state; /* current state of the verifier */
+ int i; /* temporary counter */
+
+ /* collect statistics */
+
+#ifdef TYPECHECK_STATISTICS
+ int count_iterations = 0;
+ TYPECHECK_COUNT(stat_typechecked);
+ TYPECHECK_COUNT_FREQ(stat_locals,cdata->maxlocals,STAT_LOCALS);
+ 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");
+ LOGimpSTR("Entering typecheck: ");
+ LOGimpSTRu(cdata->method->name);
+ LOGimpSTR(" ");
+ LOGimpSTRu(cdata->method->descriptor);
+ LOGimpSTR(" (class ");
+ LOGimpSTRu(cdata->method->class->name);
+ LOGimpSTR(")\n");
+ LOGFLUSH;
+
+ /* initialize the verifier state */
+
+ state.savedstackbuf = NULL;
+ state.savedstack = NULL;
+ state.jsrencountered = false;
+ state.m = meth;
+ state.cd = cdata;
+ state.rd = rdata;
+
+ /* check if this method is an instance initializer method */
+
+ state.initmethod = (state.m->name == utf_init);
+
+ /* initialize the basic block flags for the following CFG traversal */
+
+ typecheck_init_flags(&state);
+
+ /* number of local variables */
+
+ /* In <init> methods we use an extra local variable to indicate whether */
+ /* the 'this' reference has been initialized. */
+ /* TYPE_VOID...means 'this' has not been initialized, */
+ /* TYPE_INT....means 'this' has been initialized. */
+ state.numlocals = state.cd->maxlocals;
+ state.validlocals = state.numlocals;
+ if (state.initmethod) state.numlocals++;
+
+ /* allocate the buffers for local variables */
+
+ state.localbuf = DMNEW_TYPEVECTOR(state.m->basicblockcount+1, state.numlocals);
+ state.localset = MGET_TYPEVECTOR(state.localbuf,state.m->basicblockcount,state.numlocals);
-#undef COPYTYPE
+ LOG("Variable buffer allocated.\n");
+
+ /* allocate the buffer of active exception handlers */
+
+ state.handlers = DMNEW(exceptiontable*, state.cd->exceptiontablelength + 1);
+
+ /* initialized local variables of first block */
+
+ if (!verify_init_locals(&state))
+ return NULL;
+
+ /* initialize the input stack of exception handlers */
+
+ state.excstack.prev = NULL;
+ state.excstack.type = TYPE_ADR;
+ typeinfo_init_classinfo(&(state.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);
+
+ state.repeat = false;
+
+ i = state.m->basicblockcount;
+ state.bptr = state.m->basicblocks;
+
+ while (--i >= 0) {
+ LOGSTR1("---- BLOCK %04d, ",state.bptr->debug_nr);
+ LOGSTR1("blockflags: %d\n",state.bptr->flags);
+ LOGFLUSH;
+
+ /* verify reached block */
+ if (state.bptr->flags == BBTYPECHECK_REACHED) {
+ if (!verify_basic_block(&state))
+ return NULL;
+ }
+ state.bptr++;
+ } /* while blocks */
+
+ LOGIF(state.repeat,"state.repeat == true");
+ } while (state.repeat);
+
+ /* statistics */
+
+#ifdef TYPECHECK_STATISTICS
+ 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
+
+ /* reset the flags of blocks we haven't reached */
+
+ typecheck_reset_flags(&state);
+
+ /* just return methodinfo* to indicate everything was ok */
+ LOGimp("exiting typecheck");
+ return state.m;
+}
-#endif /* CACAO_TYPECHECK */
+#endif /* ENABLE_VERIFIER */
/*
* These are local overrides for various environment variables in Emacs.
* c-basic-offset: 4
* tab-width: 4
* End:
+ * vim:noexpandtab:sw=4:ts=4:
*/