/* src/vm/jit/verify/typecheck.c - typechecking (part of bytecode verification)
- Copyright (C) 1996-2005 R. Grafl, A. Krall, C. Kruegel, C. Oates,
- R. Obermaisser, M. Platter, M. Probst, S. Ring, E. Steiner,
- C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich, J. Wenninger,
- Institut f. Computersprachen - TU Wien
+ Copyright (C) 1996-2005, 2006 R. Grafl, A. Krall, C. Kruegel,
+ C. Oates, R. Obermaisser, M. Platter, M. Probst, S. Ring,
+ E. Steiner, C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich,
+ J. Wenninger, Institut f. Computersprachen - TU Wien
This file is part of CACAO.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- 02111-1307, USA.
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
- Contact: cacao@complang.tuwien.ac.at
+ Contact: cacao@cacaojvm.org
Authors: Edwin Steiner
Changes: Christian Thalinger
- $Id: typecheck.c 3353 2005-10-05 13:30:10Z edwin $
+ $Id: typecheck.c 5725 2006-10-09 22:19:22Z edwin $
*/
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
Solution: The JVM spec describes a solution, which has been
implemented in this typechecker.
+Note that some checks mentioned in the JVM spec are unnecessary[4] and
+not performed by either the reference implementation, or this implementation.
+
--- Footnotes
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.
-
+[4] Alessandro Coglio
+ Improving the official specification of Java bytecode verification
+ Proceedings of the 3rd ECOOP Workshop on Formal Techniques for Java Programs
+ June 2001
+ citeseer.ist.psu.edu/article/coglio03improving.html
*/
#include <assert.h>
#include <string.h>
+#include "config.h"
#include "vm/types.h"
+#include "vm/global.h"
-#include "vm/global.h" /* must be here because of CACAO_TYPECHECK */
-
-#ifdef CACAO_TYPECHECK
+#ifdef ENABLE_VERIFIER
#include "mm/memory.h"
#include "toolbox/logging.h"
#include "vm/jit/patcher.h"
#include "vm/loader.h"
#include "vm/options.h"
-#include "vm/tables.h"
#include "vm/jit/jit.h"
-#include "vm/jit/stack.h"
+#include "vm/jit/show.h"
#include "vm/access.h"
#include "vm/resolve.h"
-
+#include "vm/exceptions.h"
/****************************************************************************/
/* DEBUG HELPERS */
#endif
#ifdef TYPECHECK_VERBOSE_OPT
-bool typecheckverbose = false;
-#define DOLOG(action) do { if (typecheckverbose) {action;} } while(0)
+bool opt_typecheckverbose = false;
+#define DOLOG(action) do { if (opt_typecheckverbose) {action;} } while(0)
#else
#define DOLOG(action)
#endif
#ifdef TYPECHECK_VERBOSE
#define TYPECHECK_VERBOSE_IMPORTANT
-#define LOG(str) DOLOG(log_text(str))
-#define LOG1(str,a) DOLOG(dolog(str,a))
-#define LOG2(str,a,b) DOLOG(dolog(str,a,b))
-#define LOG3(str,a,b,c) DOLOG(dolog(str,a,b,c))
-#define LOGIF(cond,str) DOLOG(do {if (cond) log_text(str);} while(0))
+#define LOGNL DOLOG(puts(""))
+#define LOG(str) DOLOG(puts(str);)
+#define LOG1(str,a) DOLOG(printf(str,a); LOGNL)
+#define LOG2(str,a,b) DOLOG(printf(str,a,b); LOGNL)
+#define LOG3(str,a,b,c) DOLOG(printf(str,a,b,c); LOGNL)
+#define LOGIF(cond,str) DOLOG(do {if (cond) { puts(str); }} while(0))
#ifdef TYPEINFO_DEBUG
-#define LOGINFO(info) DOLOG(do {typeinfo_print_short(get_logfile(),(info));log_plain("\n");} while(0))
+#define LOGINFO(info) DOLOG(do {typeinfo_print_short(stdout,(info)); LOGNL;} while(0))
#else
#define LOGINFO(info)
-#define typevectorset_print(x,y,z)
+#define typevector_print(x,y,z)
#endif
-#define LOGFLUSH DOLOG(fflush(get_logfile()))
-#define LOGNL DOLOG(log_plain("\n"))
-#define LOGSTR(str) DOLOG(log_plain(str))
-#define LOGSTR1(str,a) DOLOG(dolog_plain(str,a))
-#define LOGSTR2(str,a,b) DOLOG(dolog_plain(str,a,b))
-#define LOGSTR3(str,a,b,c) DOLOG(dolog_plain(str,a,b,c))
-#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))
+#define LOGFLUSH DOLOG(fflush(stdout))
+#define LOGSTR(str) DOLOG(printf("%s", str))
+#define LOGSTR1(str,a) DOLOG(printf(str,a))
+#define LOGSTR2(str,a,b) DOLOG(printf(str,a,b))
+#define LOGSTR3(str,a,b,c) DOLOG(printf(str,a,b,c))
+#define LOGNAME(c) DOLOG(class_classref_or_classinfo_print(c))
+#define LOGMETHOD(str,m) DOLOG(printf("%s", str); method_println(m);)
#else
#define LOG(str)
#define LOG1(str,a)
#define LOGSTR1(str,a)
#define LOGSTR2(str,a,b)
#define LOGSTR3(str,a,b,c)
-#define LOGSTRu(utf)
#define LOGNAME(c)
+#define LOGMETHOD(str,m)
#endif
#ifdef TYPECHECK_VERBOSE_IMPORTANT
-#define LOGimp(str) DOLOG(log_text(str))
-#define LOGimpSTR(str) DOLOG(log_plain(str))
-#define LOGimpSTRu(utf) DOLOG(log_plain_utf(utf))
+#define LOGimp(str) DOLOG(puts(str);LOGNL)
+#define LOGimpSTR(str) DOLOG(puts(str))
#else
#define LOGimp(str)
#define LOGimpSTR(str)
-#define LOGimpSTRu(utf)
#endif
#if defined(TYPECHECK_VERBOSE) || defined(TYPECHECK_VERBOSE_IMPORTANT)
#include <stdio.h>
-static
-void
-typestack_print(FILE *file,stackptr stack)
+static void typecheck_print_var(FILE *file, jitdata *jd, s4 index)
{
-#ifdef TYPEINFO_DEBUG
- while (stack) {
- /*fprintf(file,"<%p>",stack);*/
- typeinfo_print_stacktype(file,stack->type,&(stack->typeinfo));
- stack = stack->prev;
- if (stack) fprintf(file," ");
- }
-#endif
+ varinfo *var;
+
+ assert(index >= 0 && index < jd->varcount);
+ var = VAR(index);
+ typeinfo_print_type(file, var->type, &(var->typeinfo));
}
-static
-void
-typestate_print(FILE *file,stackptr instack,typevector *localset,int size)
+static void typecheck_print_vararray(FILE *file, jitdata *jd, s4 *vars, int len)
{
- fprintf(file,"Stack: ");
- typestack_print(file,instack);
- fprintf(file," Locals:");
- typevectorset_print(file,localset,size);
+ s4 i;
+
+ for (i=0; i<len; ++i) {
+ if (i)
+ fputc(' ', file);
+ typecheck_print_var(file, jd, *vars++);
+ }
}
#endif
+
/****************************************************************************/
/* STATISTICS */
/****************************************************************************/
#define STAT_LOCALS 16
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_merged = 0;
static int stat_merging_changed = 0;
-static int stat_backwards = 0;
static int stat_blocks[STAT_BLOCKS+1] = { 0 };
static int stat_locals[STAT_LOCALS+1] = { 0 };
static int stat_ins = 0;
+static int stat_ins_maythrow = 0;
+static int stat_ins_stack = 0;
static int stat_ins_field = 0;
+static int stat_ins_field_unresolved = 0;
+static int stat_ins_field_uninitialized = 0;
static int stat_ins_invoke = 0;
+static int stat_ins_invoke_unresolved = 0;
static int stat_ins_primload = 0;
static int stat_ins_aload = 0;
static int stat_ins_builtin = 0;
static int stat_ins_builtin_gen = 0;
static int stat_ins_branch = 0;
static int stat_ins_switch = 0;
+static int stat_ins_primitive_return = 0;
+static int stat_ins_areturn = 0;
+static int stat_ins_areturn_unresolved = 0;
+static int stat_ins_athrow = 0;
+static int stat_ins_athrow_unresolved = 0;
static int stat_ins_unchecked = 0;
static int stat_handlers_reached = 0;
static int stat_savedstack = 0;
+#define TYPECHECK_MARK(var) ((var) = true)
#define TYPECHECK_COUNT(cnt) (cnt)++
#define TYPECHECK_COUNTIF(cond,cnt) do{if(cond) (cnt)++;} while(0)
#define TYPECHECK_COUNT_FREQ(array,val,limit) \
void typecheck_print_statistics(FILE *file) {
fprintf(file,"typechecked methods: %8d\n",stat_typechecked);
- fprintf(file,"methods with JSR : %8d\n",stat_typechecked_jsr);
+ fprintf(file," with 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,"merging changed : %8d\n",stat_merging_changed);
- fprintf(file,"backwards branches : %8d\n",stat_backwards);
fprintf(file,"handlers reached : %8d\n",stat_handlers_reached);
fprintf(file,"saved stack (times): %8d\n",stat_savedstack);
fprintf(file,"instructions : %8d\n",stat_ins);
+ fprintf(file," stack : %8d\n",stat_ins_stack);
fprintf(file," field access : %8d\n",stat_ins_field);
+ fprintf(file," (unresolved) : %8d\n",stat_ins_field_unresolved);
+ fprintf(file," (uninit.) : %8d\n",stat_ins_field_uninitialized);
fprintf(file," invocations : %8d\n",stat_ins_invoke);
- fprintf(file," load primitive : %8d\n",stat_ins_primload);
+ fprintf(file," (unresolved) : %8d\n",stat_ins_invoke_unresolved);
+ fprintf(file," load primitive : (currently not counted) %8d\n",stat_ins_primload);
fprintf(file," load address : %8d\n",stat_ins_aload);
fprintf(file," builtins : %8d\n",stat_ins_builtin);
fprintf(file," generic : %8d\n",stat_ins_builtin_gen);
- fprintf(file," unchecked : %8d\n",stat_ins_unchecked);
fprintf(file," branches : %8d\n",stat_ins_branch);
fprintf(file," switches : %8d\n",stat_ins_switch);
+ fprintf(file," prim. return : %8d\n",stat_ins_primitive_return);
+ fprintf(file," areturn : %8d\n",stat_ins_areturn);
+ fprintf(file," (unresolved) : %8d\n",stat_ins_areturn_unresolved);
+ fprintf(file," athrow : %8d\n",stat_ins_athrow);
+ fprintf(file," (unresolved) : %8d\n",stat_ins_athrow_unresolved);
+ fprintf(file," unchecked : %8d\n",stat_ins_unchecked);
+ fprintf(file," maythrow : %8d\n",stat_ins_maythrow);
fprintf(file,"iterations used:\n");
print_freq(file,stat_iterations,STAT_ITERATIONS);
fprintf(file,"basic blocks per method / 10:\n");
#else
#define TYPECHECK_COUNT(cnt)
+#define TYPECHECK_MARK(var)
#define TYPECHECK_COUNTIF(cond,cnt)
#define TYPECHECK_COUNT_FREQ(array,val,limit)
#endif
/****************************************************************************/
-/* MACROS FOR STACK TYPE CHECKING */
+/* MACROS FOR THROWING EXCEPTIONS */
/****************************************************************************/
-#define TYPECHECK_VERIFYERROR_ret(m,msg,retval) \
- do { \
- *exceptionptr = new_verifyerror((m), (msg)); \
- return (retval); \
+#define TYPECHECK_VERIFYERROR_ret(m,msg,retval) \
+ do { \
+ exceptions_throw_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)
-#define TYPECHECK_CHECK_TYPE(sp,tp,msg) \
- do { \
- if ((sp)->type != (tp)) { \
- *exceptionptr = new_verifyerror(state->m, (msg)); \
- return false; \
- } \
+
+/****************************************************************************/
+/* MACROS FOR VARIABLE TYPE CHECKING */
+/****************************************************************************/
+
+#define TYPECHECK_CHECK_TYPE(i,tp,msg) \
+ do { \
+ if (VAR(i)->type != (tp)) { \
+ exceptions_throw_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")
+#define TYPECHECK_INT(i) \
+ TYPECHECK_CHECK_TYPE(i,TYPE_INT,"Expected to find integer value")
+#define TYPECHECK_LNG(i) \
+ TYPECHECK_CHECK_TYPE(i,TYPE_LNG,"Expected to find long value")
+#define TYPECHECK_FLT(i) \
+ TYPECHECK_CHECK_TYPE(i,TYPE_FLT,"Expected to find float value")
+#define TYPECHECK_DBL(i) \
+ TYPECHECK_CHECK_TYPE(i,TYPE_DBL,"Expected to find double value")
+#define TYPECHECK_ADR(i) \
+ TYPECHECK_CHECK_TYPE(i,TYPE_ADR,"Expected to find object value")
+
+#define TYPECHECK_INT_OP(o) TYPECHECK_INT((o).varindex)
+#define TYPECHECK_LNG_OP(o) TYPECHECK_LNG((o).varindex)
+#define TYPECHECK_FLT_OP(o) TYPECHECK_FLT((o).varindex)
+#define TYPECHECK_DBL_OP(o) TYPECHECK_DBL((o).varindex)
+#define TYPECHECK_ADR_OP(o) TYPECHECK_ADR((o).varindex)
+
/****************************************************************************/
/* VERIFIER STATE STRUCT */
/* 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 */
+ jitdata *jd; /* jitdata for current method */
codegendata *cd; /* codegendata for current method */
- registerdata *rd; /* registerdata for current method */
+
+ basicblock *basicblocks;
+ s4 basicblockcount;
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 */
+ s4 *reverselocalmap;
- stackptr savedstackbuf; /* buffer for saving the stack */
- stackptr savedstack; /* saved instack of current block */
+ typedescriptor returntype; /* return type of the current method */
+
+ s4 *savedindices;
+ s4 *savedinvars; /* saved invar pointer */
+
+ s4 exinvars;
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 */
/* */
/* 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 **************************************************************
+/* typecheck_copy_types ********************************************************
- Copy the types on the given stack to the destination stack.
+ Copy the types of the source variables to the destination variables.
- 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
+ srcvars..........array of variable indices to copy
+ dstvars..........array of the destination variables
+ n................number of variables to copy
RETURN VALUE:
true.............success
*******************************************************************************/
static bool
-typestack_copy(verifier_state *state,stackptr dst,stackptr y,typevector *selected)
+typecheck_copy_types(verifier_state *state, s4 *srcvars, s4 *dstvars, s4 n)
{
- typevector *sel;
- typeinfo_retaddr_set *sety;
- typeinfo_retaddr_set *new;
- typeinfo_retaddr_set **next;
- int k;
-
- for (;dst; dst=dst->prev, y=y->prev) {
- if (!y) {
- *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)) {
- /* We copy the returnAddresses from the selected
- * states only. */
-
- LOG("copying returnAddress");
- sety = TYPESTACK_RETURNADDRESSSET(y);
- next = &new;
- for (k=0,sel=selected; sel; sel=sel->alt) {
- LOG1("selected k=%d",sel->k);
- while (k<sel->k) {
- sety = sety->alt;
- k++;
- }
- *next = DNEW(typeinfo_retaddr_set);
- (*next)->addr = sety->addr;
- next = &((*next)->alt);
- }
- *next = NULL;
- TYPEINFO_INIT_RETURNADDRESS(dst->typeinfo,new);
- }
- else {
- TYPEINFO_CLONE(y->typeinfo,dst->typeinfo);
- }
- }
- }
- if (y) {
- *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
+ s4 i;
+ varinfo *sv;
+ varinfo *dv;
+ jitdata *jd = state->jd;
-*******************************************************************************/
+ for (i=0; i < n; ++i, ++srcvars, ++dstvars) {
+ sv = VAR(*srcvars);
+ dv = VAR(*dstvars);
-static void
-typestack_put_retaddr(stackptr dst,void *retaddr,typevector *loc)
-{
- TYPECHECK_ASSERT(dst->type == TYPE_ADDRESS);
-
- TYPEINFO_INIT_RETURNADDRESS(dst->typeinfo,NULL);
- for (;loc; loc=loc->alt) {
- typeinfo_retaddr_set *set = DNEW(typeinfo_retaddr_set);
- set->addr = retaddr;
- set->alt = TYPESTACK_RETURNADDRESSSET(dst);
- TYPEINFO_INIT_RETURNADDRESS(dst->typeinfo,set);
+ dv->type = sv->type;
+ if (dv->type == TYPE_ADR) {
+ TYPEINFO_CLONE(sv->typeinfo,dv->typeinfo);
+ }
}
+ return true;
}
-/* 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)
-{
- for (; dst; dst = dst->prev) {
- if (TYPESTACK_IS_RETURNADDRESS(dst))
- TYPESTACK_RETURNADDRESSSET(dst)->alt = NULL;
- }
-}
-/* typestack_merge *************************************************************
+/* typecheck_merge_types *******************************************************
- Merge the types on one stack into the destination stack.
+ Merge the types of the source variables into the destination variables.
IN:
state............current state of the verifier
- dst..............the destination stack
- y................the second stack
-
- OUT:
- *dst.............receives the result of the stack merge
+ srcvars..........source variable indices
+ dstvars..........destination variable indices
+ n................number of variables
RETURN VALUE:
- typecheck_TRUE...*dst has been modified
- typecheck_FALSE..*dst has not been modified
+ typecheck_TRUE...the destination variables have been modified
+ typecheck_FALSE..the destination variables are unchanged
typecheck_FAIL...an exception has been thrown
*******************************************************************************/
static typecheck_result
-typestack_merge(verifier_state *state,stackptr dst,stackptr y)
+typecheck_merge_types(verifier_state *state,s4 *srcvars, s4 *dstvars, s4 n)
{
+ s4 i;
+ varinfo *sv;
+ varinfo *dv;
+ jitdata *jd = state->jd;
typecheck_result r;
bool changed = false;
- for (; dst; dst = dst->prev, y=y->prev) {
- 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");
+ for (i=0; i < n; ++i, ++srcvars, ++dstvars) {
+ sv = VAR(*srcvars);
+ dv = VAR(*dstvars);
+
+ if (dv->type != sv->type) {
+ exceptions_throw_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)) {
- *exceptionptr = new_verifyerror(state->m,"Merging returnAddress with reference");
+ if (dv->type == TYPE_ADR) {
+ if (TYPEINFO_IS_PRIMITIVE(dv->typeinfo)) {
+ /* dv has returnAddress type */
+ if (!TYPEINFO_IS_PRIMITIVE(sv->typeinfo)) {
+ exceptions_throw_verifyerror(state->m,"Merging returnAddress with reference");
return typecheck_FAIL;
}
}
else {
- /* dst has reference type */
- if (TYPEINFO_IS_PRIMITIVE(y->typeinfo)) {
- *exceptionptr = new_verifyerror(state->m,"Merging reference with returnAddress");
+ /* dv has reference type */
+ if (TYPEINFO_IS_PRIMITIVE(sv->typeinfo)) {
+ exceptions_throw_verifyerror(state->m,"Merging reference with returnAddress");
return typecheck_FAIL;
}
- r = typeinfo_merge(state->m,&(dst->typeinfo),&(y->typeinfo));
+ r = typeinfo_merge(state->m,&(dv->typeinfo),&(sv->typeinfo));
if (r == typecheck_FAIL)
return r;
changed |= r;
}
}
}
- 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)
-{
- typeinfo_retaddr_set *setd;
- typeinfo_retaddr_set *sety;
-
- for (; dst; dst = dst->prev, y=y->prev) {
- if (TYPESTACK_IS_RETURNADDRESS(dst)) {
- setd = TYPESTACK_RETURNADDRESSSET(dst);
- sety = TYPESTACK_RETURNADDRESSSET(y);
- RETURNADDRESSSET_SEEK(sety,ky);
- while (setd->alt)
- setd=setd->alt;
- setd->alt = DNEW(typeinfo_retaddr_set);
- setd->alt->addr = sety->addr;
- setd->alt->alt = NULL;
- }
- }
-}
-
-/* 'a' and 'b' are assumed to have passed typestack_canmerge! */
-static bool
-typestack_separable_with(stackptr a,stackptr b,int kb)
-{
- typeinfo_retaddr_set *seta;
- typeinfo_retaddr_set *setb;
-
- for (; a; a = a->prev, b = b->prev) {
- TYPECHECK_ASSERT(b);
- if (TYPESTACK_IS_RETURNADDRESS(a)) {
- TYPECHECK_ASSERT(TYPESTACK_IS_RETURNADDRESS(b));
- seta = TYPESTACK_RETURNADDRESSSET(a);
- setb = TYPESTACK_RETURNADDRESSSET(b);
- RETURNADDRESSSET_SEEK(setb,kb);
-
- for (;seta;seta=seta->alt)
- if (seta->addr != setb->addr) return true;
- }
- }
- TYPECHECK_ASSERT(!b);
- return false;
-}
-
-/* 'a' and 'b' are assumed to have passed typestack_canmerge! */
-static bool
-typestack_separable_from(stackptr a,int ka,stackptr b,int kb)
-{
- typeinfo_retaddr_set *seta;
- typeinfo_retaddr_set *setb;
-
- for (; a; a = a->prev, b = b->prev) {
- TYPECHECK_ASSERT(b);
- if (TYPESTACK_IS_RETURNADDRESS(a)) {
- TYPECHECK_ASSERT(TYPESTACK_IS_RETURNADDRESS(b));
- seta = TYPESTACK_RETURNADDRESSSET(a);
- setb = TYPESTACK_RETURNADDRESSSET(b);
- RETURNADDRESSSET_SEEK(seta,ka);
- RETURNADDRESSSET_SEEK(setb,kb);
-
- if (seta->addr != setb->addr) return true;
- }
- }
- 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 */
-/****************************************************************************/
/* typestate_merge *************************************************************
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
+ dstvars..........indices of the destinations invars
+ dstlocals........the destinations inlocals
+ srcvars..........indices of the source's outvars
+ srclocals........the source locals
+ n................number of invars (== number of outvars)
RETURN VALUE:
typecheck_TRUE...destination state has been modified
static typecheck_result
typestate_merge(verifier_state *state,
- stackptr deststack,typevector *destloc,
- stackptr ystack,typevector *yloc)
+ s4 *srcvars, varinfo *srclocals,
+ s4 *dstvars, varinfo *dstlocals,
+ s4 n)
{
- 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,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. */
- r = typestack_merge(state,deststack,ystack);
+ r = typecheck_merge_types(state, srcvars, dstvars, n);
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;
-
- /* Check if the typestates (deststack,destloc) will be
- * separable when (ystack,yvec) is added. */
-
- if (!typestack_separable_with(deststack,ystack,ky)
- && !typevectorset_separable_with(destloc,yvec,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);
- 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
- * if we may merge (ystack,yvec) with a single state in
- * (deststack,destloc). */
-
- for (dvec=destloc,kd=0; dvec; dvec=dvec->alt, kd++) {
- if (!typestack_separable_from(ystack,ky,deststack,kd)
- && !typevector_separable_from(yvec,dvec,state->numlocals))
- {
- /* The typestate (ystack,yvec) is not separable from
- * (deststack,dvec) by any returnAddress. Thus we may
- * merge the states. */
-
- r = typevector_merge(state->m,dvec,yvec,state->numlocals);
- if (r == typecheck_FAIL)
- return r;
- changed |= r;
-
- goto merged;
- }
- }
-
- /* The typestate (ystack,yvec) is separable from all typestates
- * (deststack,destloc). Thus we must add this state to the
- * result set. */
+ /* merge the locals */
- typestack_add(deststack,ystack,ky);
- typevectorset_add(destloc,yvec,state->numlocals);
- changed = true;
- }
-
- merged:
- ;
- }
-
- LOG("result:");
- LOGSTR("dstack: "); DOLOG(typestack_print(get_logfile(),deststack)); LOGNL;
- LOGSTR("dloc : "); DOLOG(typevectorset_print(get_logfile(),destloc,state->numlocals)); LOGNL;
- LOGFLUSH;
-
- return changed;
+ r = typevector_merge(state->m, dstlocals, srclocals, state->numlocals);
+ if (r == typecheck_FAIL)
+ return r;
+ return changed | r;
}
+
/* 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
+ srcvars..........variable indices of the outvars to propagate
+ srclocals........local variables to propagate
+ n................number of srcvars
OUT:
state->repeat....set to true if the verifier must iterate again
static bool
typestate_reach(verifier_state *state,
basicblock *destblock,
- stackptr ystack,typevector *yloc)
+ s4 *srcvars, varinfo *srclocals, s4 n)
{
- typevector *destloc;
- int destidx;
+ varinfo *destloc;
bool changed = false;
typecheck_result r;
- LOG1("reaching block L%03d",destblock->debug_nr);
+ LOG1("reaching block L%03d",destblock->nr);
TYPECHECK_COUNT(stat_reached);
- 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 <= state->bptr) {
- stackptr sp;
- int i;
-
- /* 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)) {
- /*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;
- }
+ destloc = destblock->inlocals;
- for (i=0; i<state->numlocals; ++i)
- if (yloc->td[i].type == TYPE_ADR &&
- TYPEINFO_IS_NEWOBJECT(yloc->td[i].info)) {
- *exceptionptr = new_verifyerror(state->m,"Branching backwards with uninitialized object in local variable");
- return false;
- }
- }
- }
-
if (destblock->flags == BBTYPECHECK_UNDEF) {
/* The destblock has never been reached before */
TYPECHECK_COUNT(stat_copied);
- LOG1("block (index %04d) reached first time",destidx);
+ LOG1("block L%03d reached first time",destblock->nr);
- if (!typestack_copy(state,destblock->instack,ystack,yloc))
+ if (!typecheck_copy_types(state, srcvars, destblock->invars, n))
return false;
- COPY_TYPEVECTORSET(yloc,destloc,state->numlocals);
+ typevector_copy_inplace(srclocals, destloc, state->numlocals);
changed = true;
}
else {
/* The destblock has already been reached before */
TYPECHECK_COUNT(stat_merged);
- LOG1("block (index %04d) reached before",destidx);
+ LOG1("block L%03d reached before", destblock->nr);
- r = typestate_merge(state,destblock->instack,destloc,ystack,yloc);
+ r = typestate_merge(state, srcvars, srclocals,
+ destblock->invars, destblock->inlocals, n);
if (r == typecheck_FAIL)
return false;
changed = r;
return true;
}
-/* typestate_ret ***************************************************************
+
+/* typestate_save_invars *******************************************************
- Reach the destinations of a RET instruction.
+ Save the invars of the current basic block in the space reserved by
+ parse.
+
+ This function must be called before an instruction modifies a variable
+ that is an invar of the current block. In such cases the invars of the
+ block must be saved, and restored at the end of the analysis of this
+ basic block, so that the invars again reflect the *input* to this basic
+ block (and do not randomly contain types that appear within the block).
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(verifier_state *state,int retindex)
+static void
+typestate_save_invars(verifier_state *state)
{
- typevector *yvec;
- typevector *selected;
- basicblock *destblock;
+ s4 i, index;
+ s4 *pindex;
+
+ LOG("saving invars");
+
+ if (!state->savedindices) {
+ LOG("allocating savedindices buffer");
+ pindex = DMNEW(s4, state->m->maxstack);
+ state->savedindices = pindex;
+ index = state->numlocals + VERIFIER_EXTRA_VARS;
+ for (i=0; i<state->m->maxstack; ++i)
+ *pindex++ = index++;
+ }
- 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;
- }
+ /* save types */
- destblock = (basicblock*) TYPEINFO_RETURNADDRESS(yvec->td[retindex].info);
+ typecheck_copy_types(state, state->bptr->invars, state->savedindices,
+ state->bptr->indepth);
- selected = typevectorset_select(&yvec,retindex,destblock);
-
- if (!typestate_reach(state,destblock,state->curstack,selected))
- return false;
- }
- return true;
+ /* set the invars of the block to the saved variables */
+ /* and remember the original invars */
+
+ state->savedinvars = state->bptr->invars;
+ state->bptr->invars = state->savedindices;
}
-/****************************************************************************/
-/* MACROS FOR LOCAL VARIABLE CHECKING */
-/****************************************************************************/
-#define INDEX_ONEWORD(num) \
- do { if((num)<0 || (num)>=state->validlocals) \
- TYPECHECK_VERIFYERROR_bool("Invalid local variable index"); } while (0)
-#define INDEX_TWOWORD(num) \
- do { if((num)<0 || ((num)+1)>=state->validlocals) \
- TYPECHECK_VERIFYERROR_bool("Invalid local variable index"); } while (0)
+/* typestate_restore_invars ***************************************************
+
+ Restore the invars of the current basic block that have been previously
+ saved by `typestate_save_invars`.
+
+ IN:
+ state............current state of the verifier
-#define STORE_ONEWORD(num,type) \
- do {typevectorset_store(state->localset,num,type,NULL);} while(0)
+*******************************************************************************/
-#define STORE_TWOWORD(num,type) \
- do {typevectorset_store_twoword(state->localset,num,type);} while(0)
+static void
+typestate_restore_invars(verifier_state *state)
+{
+ TYPECHECK_COUNT(stat_savedstack);
+ LOG("restoring saved invars");
+ /* restore the invars pointer */
-#ifdef TYPECHECK_VERBOSE
-#define WORDCHECKFAULT \
- do { \
- dolog("localset->td index: %ld\ninstruction belongs to:%s.%s, outermethod:%s.%s\n", \
- state->iptr->op1,state->iptr->method->class->name->text, \
- state->iptr->method->name->text,state->m->class->name->text,state->m->name->text); \
- show_icmd(state->iptr++, false); \
- show_icmd(state->iptr, false); \
- } while (0)
-#else
-#define WORDCHECKFAULT
-#endif
+ state->bptr->invars = state->savedinvars;
+
+ /* copy the types back */
+ typecheck_copy_types(state, state->savedindices, state->bptr->invars,
+ state->bptr->indepth);
-#define CHECK_ONEWORD(num,tp) \
- do {TYPECHECK_COUNT(stat_ins_primload); \
- if (state->jsrencountered) { \
- if (!typevectorset_checktype(state->localset,num,tp)) { \
- WORDCHECKFAULT; \
- TYPECHECK_VERIFYERROR_bool("Variable type mismatch"); \
- } \
- } \
- else { \
- if (state->localset->td[num].type != tp) { \
- TYPECHECK_VERIFYERROR_bool("Variable type mismatch"); \
- WORDCHECKFAULT; \
- } \
- } \
- } while(0)
-
-#define CHECK_TWOWORD(num,type) \
- do {TYPECHECK_COUNT(stat_ins_primload); \
- if (!typevectorset_checktype(state->localset,num,type)) { \
- WORDCHECKFAULT; \
- TYPECHECK_VERIFYERROR_bool("Variable type mismatch"); \
- } \
- } while(0)
+ /* mark that there are no saved invars currently */
+
+ state->savedinvars = NULL;
+}
/****************************************************************************/
/* MISC MACROS */
/****************************************************************************/
-#define COPYTYPE(source,dest) \
- {if ((source)->type == TYPE_ADR) \
- TYPEINFO_COPY((source)->typeinfo,(dest)->typeinfo);}
+#define COPYTYPE(source,dest) \
+ {if (VAROP(source)->type == TYPE_ADR) \
+ TYPEINFO_COPY(VAROP(source)->typeinfo,VAROP(dest)->typeinfo);}
#define ISBUILTIN(v) (bte->fp == (functionptr) (v))
-/* TYPECHECK_LEAVE: executed when the method is exited non-abruptly
- * Input:
- * class........class of the current method
- * state........verifier state
- */
-#define TYPECHECK_LEAVE \
- do { \
- if (state->initmethod && state->m->class != class_java_lang_Object) { \
- /* check the marker variable */ \
- LOG("Checking <init> marker"); \
- if (!typevectorset_checktype(state->localset,state->numlocals-1,TYPE_INT))\
- TYPECHECK_VERIFYERROR_bool("<init> method does not initialize 'this'"); \
- } \
- } while (0)
/* verify_invocation ***********************************************************
{
unresolved_method *um; /* struct describing the called method */
constant_FMIref *mref; /* reference to the called method */
+ methodinfo *mi; /* resolved method (if any) */
methoddesc *md; /* descriptor of the called method */
+ utf *mname; /* method name */
+ utf *mclassname; /* name of the method's class */
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 */
+ s4 argindex; /* argument variable index */
+ varinfo *av; /* argument variable */
+ varinfo *dv; /* result variable of the invocation */
int i; /* counter */
u1 rtype; /* return type of called method */
+ resolve_result_t result;
+ jitdata *jd;
+ bool invokespecial;
+
+ jd = state->jd;
+
+ /* get the FMIref and the unresolved_method struct (if any) */
+ /* from the instruction */
+
+ if (INSTRUCTION_IS_UNRESOLVED(state->iptr)) {
+ /* unresolved method */
+ um = state->iptr->sx.s23.s3.um;
+ mref = um->methodref;
+ }
+ else {
+ /* resolved method */
+ um = NULL;
+ mref = state->iptr->sx.s23.s3.fmiref;
+ }
+
+ /* get method descriptor and name */
- um = (unresolved_method *) state->iptr[0].target;
- mref = um->methodref;
md = mref->parseddesc.md;
- specialmethod = (mref->name->text[0] == '<');
+ mname = mref->name;
+
+ /* get method info (if resolved) and classname */
+
+ if (IS_FMIREF_RESOLVED(mref)) {
+ mi = mref->p.method;
+ mclassname = mi->class->name;
+ }
+ else {
+ mi = NULL;
+ mclassname = mref->p.classref->name;
+ }
+
opcode = state->iptr[0].opc;
- dst = state->iptr->dst;
+ invokespecial = (opcode == ICMD_INVOKESPECIAL);
+ specialmethod = (mname->text[0] == '<');
+ dv = VAROP(state->iptr->dst);
/* prevent compiler warnings */
/* check whether we are calling <init> */
- callinginit = (opcode == ICMD_INVOKESPECIAL && mref->name == utf_init);
+ callinginit = (invokespecial && mname == utf_init);
if (specialmethod && !callinginit)
TYPECHECK_VERIFYERROR_bool("Invalid invocation of special method");
- /* record subtype constraints for parameters */
-
- if (!constrain_unresolved_method(um,state->m->class,state->m,state->iptr,state->curstack))
- return false; /* XXX maybe wrap exception */
-
- /* try to resolve the method lazily */
-
- if (!resolve_method(um,resolveLazy,(methodinfo **) &(state->iptr[0].val.a)))
- return false;
-
/* allocate parameters if necessary */
if (!md->params)
/* check parameter types */
- stack = state->curstack;
i = md->paramcount; /* number of parameters including 'this'*/
while (--i >= 0) {
LOG1("param %d",i);
+ argindex = state->iptr->sx.s23.s2.args[i];
+ av = VAR(argindex);
td = md->paramtypes + i;
- if (stack->type != td->type)
+
+ if (av->type != td->type)
TYPECHECK_VERIFYERROR_bool("Parameter type mismatch in method invocation");
- if (stack->type == TYPE_ADR) {
- LOGINFO(&(stack->typeinfo));
+
+ if (av->type == TYPE_ADR) {
+ LOGINFO(&(av->typeinfo));
if (i==0 && callinginit)
{
/* first argument to <init> method */
- if (!TYPEINFO_IS_NEWOBJECT(stack->typeinfo))
+ if (!TYPEINFO_IS_NEWOBJECT(av->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);
+ LOGINFO(&(av->typeinfo));
+ ins = (instruction *) TYPEINFO_NEWOBJECT_INSTRUCTION(av->typeinfo);
if (ins)
- initclass = CLASSREF_OR_CLASSINFO(ins[-1].val.a);
+ initclass = ins[-1].sx.val.c;
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;
- }
-
- 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 (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)
+
+ /* for all live-in and live-through variables */
+ for (i=0; i<state->iptr->s1.argcount; ++i) {
+ argindex = state->iptr->sx.s23.s2.args[i];
+ av = VAR(argindex);
+ if (av->type == TYPE_ADR
+ && TYPEINFO_IS_NEWOBJECT(av->typeinfo)
+ && TYPEINFO_NEWOBJECT_INSTRUCTION(av->typeinfo) == ins)
{
- LOG("replacing uninitialized type on stack");
+ LOG("replacing uninitialized type");
/* 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)
+ /* XXX this needs a new check */
+ if (state->bptr->invars
+ && argindex >= state->bptr->invars[0]
+ && argindex < state->bptr->varstart
+ && !state->savedinvars)
{
- stackptr sp;
- stackptr copy;
- LOG("saving input stack types");
- if (!state->savedstackbuf) {
- LOG("allocating savedstack buffer");
- state->savedstackbuf = DMNEW(stackelement, state->cd->maxstack);
- state->savedstackbuf->prev = NULL;
- for (i = 1; i < state->cd->maxstack; ++i)
- state->savedstackbuf[i].prev = state->savedstackbuf+(i-1);
- }
- sp = state->savedstack = state->bptr->instack;
- copy = state->bptr->instack = state->savedstackbuf + (state->bptr->indepth-1);
- TYPESTACK_COPY(sp,copy);
+ typestate_save_invars(state);
}
- if (!typeinfo_init_class(&(stack->typeinfo),initclass))
+ if (!typeinfo_init_class(&(av->typeinfo),initclass))
return false;
}
- stack = stack->prev;
}
+
/* replace uninitialized object type in locals */
- if (!typevectorset_init_object(state->localset,ins,initclass,state->numlocals))
+ if (!typevector_init_object(state->jd->var, ins, initclass,
+ state->numlocals))
return false;
/* 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 (mi) {
+ cls = mi->class;
+ }
+ else {
+ if (!resolve_classref(state->m,mref->p.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 */
/* set our marker variable to type int */
LOG("setting <init> marker");
- typevectorset_store(state->localset,state->numlocals-1,TYPE_INT,NULL);
+ typevector_store(jd->var, 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) {
+ if ((IS_CLASSREF(initclass) ? initclass.ref->name : initclass.cls->name) != mclassname) {
TYPECHECK_VERIFYERROR_bool("wrong <init> called for uninitialized reference");
}
}
}
- return true;
-}
+ /* try to resolve the method lazily */
-/* verify_builtin **************************************************************
-
- Verify the call of a builtin function.
-
- IN:
- state............the current state of the verifier
+ result = resolve_method_lazy(state->m, mref, invokespecial);
- RETURN VALUE:
- true.............successful verification,
- false............an exception has been thrown.
+ /* if resolved, perform verification checks */
-*******************************************************************************/
+ if (result == resolveSucceeded) {
-static bool
-verify_builtin(verifier_state *state)
-{
- builtintable_entry *bte;
- classref_or_classinfo cls;
- stackptr dst; /* output stack of current instruction */
+ assert(IS_FMIREF_RESOLVED(mref));
- bte = (builtintable_entry *) state->iptr[0].val.a;
- dst = state->iptr->dst;
+ mi = mref->p.method;
- if (ISBUILTIN(BUILTIN_new) || ISBUILTIN(PATCHER_builtin_new)) {
- if (state->iptr[-1].opc != ICMD_ACONST)
- TYPECHECK_VERIFYERROR_bool("illegal instruction: builtin_new without class");
- cls.any = state->iptr[-1].val.a;
- if (cls.any && !IS_CLASSREF(cls)) {
- /* The following check also forbids array classes and interfaces: */
- if ((cls.cls->flags & ACC_ABSTRACT) != 0)
- TYPECHECK_VERIFYERROR_bool("Invalid instruction: NEW creating instance of abstract class");
- }
- else {
- /* in this case, the patcher will perform the non-abstract check */
- TYPECHECK_ASSERT(ISBUILTIN(PATCHER_builtin_new));
- }
- 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);
+ result = resolve_method_verifier_checks(jd,
+ state->m,
+ mref,
+ mi->class,
+ mi,
+ state->iptr->opc == ICMD_INVOKESTATIC,
+ invokespecial,
+ state->iptr);
}
- else if (ISBUILTIN(BUILTIN_newarray_char)) {
- TYPECHECK_INT(state->curstack);
- TYPEINFO_INIT_PRIMITIVE_ARRAY(dst->typeinfo,ARRAYTYPE_CHAR);
+
+ /* if resolved, impose loading constraints */
+
+ if (result == resolveSucceeded) {
+ /* XXX state->m->class may have to be wrong when inlining */
+ if (!resolve_method_loading_constraints(state->m->class, mi))
+ return false;
}
- else if (ISBUILTIN(BUILTIN_newarray_float)) {
- TYPECHECK_INT(state->curstack);
- TYPEINFO_INIT_PRIMITIVE_ARRAY(dst->typeinfo,ARRAYTYPE_FLOAT);
+
+ if (result == resolveFailed)
+ return false;
+
+ if (result == resolveSucceeded) {
+ /* if this call is monomorphic, turn it into an INVOKESPECIAL */
+
+ if ((state->iptr->opc == ICMD_INVOKEVIRTUAL)
+ && (mi->flags & (ACC_FINAL | ACC_PRIVATE)))
+ {
+ state->iptr->opc = ICMD_INVOKESPECIAL;
+ }
+ }
+ else {
+ /* resolution must be deferred */
+
+ if (!um) {
+ um = resolve_create_unresolved_method(state->m->class, state->m,
+ mref,
+ state->iptr->opc == ICMD_INVOKESTATIC,
+ invokespecial);
+
+ if (!um)
+ return false;
+ }
+
+ /* record subtype constraints for parameters */
+
+ if (!constrain_unresolved_method(jd, um, state->m->class,
+ state->m, state->iptr))
+ return false; /* XXX maybe wrap exception */
+
+ /* store the unresolved_method pointer */
+
+ state->iptr->sx.s23.s3.um = um;
+ state->iptr->flags.bits |= INS_FLAG_UNRESOLVED;
+ }
+
+ rtype = md->returntype.type;
+ if (rtype != TYPE_VOID) {
+ dv->type = rtype;
+ if (!typeinfo_init_from_typedesc(&(md->returntype),NULL,&(dv->typeinfo)))
+ return false;
+ }
+
+ return true;
+}
+
+
+/* verify_generic_builtin ******************************************************
+
+ Verify the call of a generic builtin method.
+
+ IN:
+ state............the current state of the verifier
+
+ RETURN VALUE:
+ true.............successful verification,
+ false............an exception has been thrown.
+
+*******************************************************************************/
+
+static bool
+verify_generic_builtin(verifier_state *state)
+{
+ builtintable_entry *bte;
+ s4 i;
+ u1 rtype;
+ methoddesc *md;
+ varinfo *av;
+ jitdata *jd = state->jd;
+
+ TYPECHECK_COUNT(stat_ins_builtin_gen);
+
+ bte = state->iptr->sx.s23.s3.bte;
+ md = bte->md;
+ i = md->paramcount;
+
+ /* check the types of the arguments on the stack */
+
+ for (i--; i >= 0; i--) {
+ av = VAR(state->iptr->sx.s23.s2.args[i]);
+
+ if (av->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 (av->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
+ }
+
+ /* set the return type */
+
+ rtype = md->returntype.type;
+ if (rtype != TYPE_VOID) {
+ varinfo *dv;
+
+ dv = VAROP(state->iptr->dst);
+ dv->type = rtype;
+ if (!typeinfo_init_from_typedesc(&(md->returntype),NULL,&(dv->typeinfo)))
+ return false;
+ }
+
+ return true;
+}
+
+
+/* verify_builtin **************************************************************
+
+ Verify the call of a builtin method.
+
+ IN:
+ state............the current state of the verifier
+
+ RETURN VALUE:
+ true.............successful verification,
+ false............an exception has been thrown.
+
+*******************************************************************************/
+
+static bool
+verify_builtin(verifier_state *state)
+{
+ builtintable_entry *bte;
+ classref_or_classinfo cls;
+ varinfo *dv; /* output variable of current instruction */
+ jitdata *jd = state->jd;
+
+ bte = state->iptr->sx.s23.s3.bte;
+ dv = VAROP(state->iptr->dst);
+
+ /* XXX this is an ugly if-chain but twisti did not want a function */
+ /* pointer in builtintable_entry for this, so here you go.. ;) */
+
+ if (ISBUILTIN(BUILTIN_new)) {
+ if (state->iptr[-1].opc != ICMD_ACONST)
+ TYPECHECK_VERIFYERROR_bool("illegal instruction: builtin_new without class");
+ cls = state->iptr[-1].sx.val.c;
+ dv->type = TYPE_ADR;
+ TYPEINFO_INIT_NEWOBJECT(dv->typeinfo,state->iptr);
+ }
+ else if (ISBUILTIN(BUILTIN_newarray_boolean)) {
+ TYPECHECK_INT(state->iptr->sx.s23.s2.args[0]);
+ dv->type = TYPE_ADR;
+ TYPEINFO_INIT_PRIMITIVE_ARRAY(dv->typeinfo,ARRAYTYPE_BOOLEAN);
+ }
+ else if (ISBUILTIN(BUILTIN_newarray_char)) {
+ TYPECHECK_INT(state->iptr->sx.s23.s2.args[0]);
+ dv->type = TYPE_ADR;
+ TYPEINFO_INIT_PRIMITIVE_ARRAY(dv->typeinfo,ARRAYTYPE_CHAR);
+ }
+ else if (ISBUILTIN(BUILTIN_newarray_float)) {
+ TYPECHECK_INT(state->iptr->sx.s23.s2.args[0]);
+ dv->type = TYPE_ADR;
+ TYPEINFO_INIT_PRIMITIVE_ARRAY(dv->typeinfo,ARRAYTYPE_FLOAT);
}
else if (ISBUILTIN(BUILTIN_newarray_double)) {
- TYPECHECK_INT(state->curstack);
- TYPEINFO_INIT_PRIMITIVE_ARRAY(dst->typeinfo,ARRAYTYPE_DOUBLE);
+ TYPECHECK_INT(state->iptr->sx.s23.s2.args[0]);
+ dv->type = TYPE_ADR;
+ TYPEINFO_INIT_PRIMITIVE_ARRAY(dv->typeinfo,ARRAYTYPE_DOUBLE);
}
else if (ISBUILTIN(BUILTIN_newarray_byte)) {
- TYPECHECK_INT(state->curstack);
- TYPEINFO_INIT_PRIMITIVE_ARRAY(dst->typeinfo,ARRAYTYPE_BYTE);
+ TYPECHECK_INT(state->iptr->sx.s23.s2.args[0]);
+ dv->type = TYPE_ADR;
+ TYPEINFO_INIT_PRIMITIVE_ARRAY(dv->typeinfo,ARRAYTYPE_BYTE);
}
else if (ISBUILTIN(BUILTIN_newarray_short)) {
- TYPECHECK_INT(state->curstack);
- TYPEINFO_INIT_PRIMITIVE_ARRAY(dst->typeinfo,ARRAYTYPE_SHORT);
+ TYPECHECK_INT(state->iptr->sx.s23.s2.args[0]);
+ dv->type = TYPE_ADR;
+ TYPEINFO_INIT_PRIMITIVE_ARRAY(dv->typeinfo,ARRAYTYPE_SHORT);
}
else if (ISBUILTIN(BUILTIN_newarray_int)) {
- TYPECHECK_INT(state->curstack);
- TYPEINFO_INIT_PRIMITIVE_ARRAY(dst->typeinfo,ARRAYTYPE_INT);
+ TYPECHECK_INT(state->iptr->sx.s23.s2.args[0]);
+ dv->type = TYPE_ADR;
+ TYPEINFO_INIT_PRIMITIVE_ARRAY(dv->typeinfo,ARRAYTYPE_INT);
}
else if (ISBUILTIN(BUILTIN_newarray_long)) {
- TYPECHECK_INT(state->curstack);
- TYPEINFO_INIT_PRIMITIVE_ARRAY(dst->typeinfo,ARRAYTYPE_LONG);
+ TYPECHECK_INT(state->iptr->sx.s23.s2.args[0]);
+ dv->type = TYPE_ADR;
+ TYPEINFO_INIT_PRIMITIVE_ARRAY(dv->typeinfo,ARRAYTYPE_LONG);
}
else if (ISBUILTIN(BUILTIN_newarray))
{
- vftbl_t *vft;
- TYPECHECK_INT(state->curstack->prev);
+ TYPECHECK_INT(state->iptr->sx.s23.s2.args[0]);
if (state->iptr[-1].opc != ICMD_ACONST)
- TYPECHECK_VERIFYERROR_bool("illegal instruction: builtin_newarray without classinfo");
- vft = (vftbl_t *)state->iptr[-1].val.a;
- if (!vft)
- TYPECHECK_VERIFYERROR_bool("ANEWARRAY with unlinked class");
- if (!vft->arraydesc)
- TYPECHECK_VERIFYERROR_bool("ANEWARRAY with non-array class");
- TYPEINFO_INIT_CLASSINFO(dst->typeinfo,vft->class);
+ TYPECHECK_VERIFYERROR_bool("illegal instruction: builtin_newarray without class");
+ /* XXX check that it is an array class(ref) */
+ dv->type = TYPE_ADR;
+ typeinfo_init_class(&(dv->typeinfo),state->iptr[-1].sx.val.c);
}
- else if (ISBUILTIN(PATCHER_builtin_newarray))
+ else if (ISBUILTIN(BUILTIN_arrayinstanceof))
{
- TYPECHECK_INT(state->curstack->prev);
+ TYPECHECK_ADR(state->iptr->sx.s23.s2.args[0]);
if (state->iptr[-1].opc != ICMD_ACONST)
- TYPECHECK_VERIFYERROR_bool("illegal instruction: builtin_newarray without classinfo");
- if (!typeinfo_init_class(&(dst->typeinfo),CLASSREF_OR_CLASSINFO(state->iptr[-1].val.a)))
- return false;
+ TYPECHECK_VERIFYERROR_bool("illegal instruction: builtin_arrayinstanceof without class");
+ dv->type = TYPE_INT;
+ /* XXX check that it is an array class(ref) */
}
- else if (ISBUILTIN(BUILTIN_newarray))
- {
- vftbl_t *vft;
- TYPECHECK_INT(state->curstack->prev);
- if (state->iptr[-1].opc != ICMD_ACONST)
- TYPECHECK_VERIFYERROR_bool("illegal instruction: builtin_newarray without classinfo");
- vft = (vftbl_t *)state->iptr[-1].val.a;
- if (!vft)
- TYPECHECK_VERIFYERROR_bool("ANEWARRAY with unlinked class");
- if (!vft->arraydesc)
- TYPECHECK_VERIFYERROR_bool("ANEWARRAY with non-array class");
- TYPEINFO_INIT_CLASSINFO(dst->typeinfo,vft->class);
+ else {
+ return verify_generic_builtin(state);
}
- else if (ISBUILTIN(BUILTIN_arrayinstanceof))
- {
- vftbl_t *vft;
- TYPECHECK_ADR(state->curstack->prev);
- if (state->iptr[-1].opc != ICMD_ACONST)
- TYPECHECK_VERIFYERROR_bool("illegal instruction: builtin_arrayinstanceof without classinfo");
- vft = (vftbl_t *)state->iptr[-1].val.a;
- if (!vft)
- TYPECHECK_VERIFYERROR_bool("INSTANCEOF with unlinked class");
- if (!vft->arraydesc)
- TYPECHECK_VERIFYERROR_bool("internal error: builtin_arrayinstanceof with non-array class");
+ return true;
+}
+
+
+/* verify_multianewarray *******************************************************
+
+ Verify a MULTIANEWARRAY instruction.
+
+ IN:
+ state............the current state of the verifier
+
+ RETURN VALUE:
+ true.............successful verification,
+ false............an exception has been thrown.
+
+*******************************************************************************/
+
+static bool
+verify_multianewarray(verifier_state *state)
+{
+ classinfo *arrayclass;
+ arraydescriptor *desc;
+ s4 i;
+ jitdata *jd = state->jd;
+
+ /* check the array lengths on the stack */
+ i = state->iptr->s1.argcount;
+ if (i < 1)
+ TYPECHECK_VERIFYERROR_bool("Illegal dimension argument");
+
+ while (i--) {
+ TYPECHECK_INT(state->iptr->sx.s23.s2.args[i]);
+ }
+
+ /* check array descriptor */
+ if (INSTRUCTION_IS_RESOLVED(state->iptr)) {
+ /* the array class reference has already been resolved */
+ arrayclass = state->iptr->sx.s23.s3.c.cls;
+ 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->s1.argcount)
+ TYPECHECK_VERIFYERROR_bool("MULTIANEWARRAY dimension to high");
+
+ /* set the array type of the result */
+ typeinfo_init_classinfo(&(VAROP(state->iptr->dst)->typeinfo), arrayclass);
}
else {
-#if 0
- /* XXX put these checks in a function */
- TYPECHECK_COUNT(stat_ins_builtin_gen);
- builtindesc = builtin_desc;
- while (builtindesc->opcode && builtindesc->builtin
- != state->iptr->val.fp) builtindesc++;
- if (!builtindesc->opcode) {
- dolog("Builtin not in table: %s",icmd_builtin_name(state->iptr->val.fp));
- TYPECHECK_ASSERT(false);
- }
- TYPECHECK_ARGS3(builtindesc->type_s3,builtindesc->type_s2,builtindesc->type_s1);
-#endif
+ 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 = state->iptr->sx.s23.s3.c.ref;
+ 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->s1.argcount)
+ TYPECHECK_VERIFYERROR_bool("MULTIANEWARRAY dimension to high");
+
+ /* set the array type of the result */
+ if (!typeinfo_init_class(&(VAROP(state->iptr->dst)->typeinfo),CLASSREF_OR_CLASSINFO(cr)))
+ return false;
}
+
+ /* set return type */
+
+ VAROP(state->iptr->dst)->type = TYPE_ADR;
+
+ /* everything ok */
return true;
}
+
+/* typecheck_invalidate_locals *************************************************
+
+ Invalidate locals that are overwritten by writing to the given local.
+
+ IN:
+ state............the current state of the verifier
+ index............the index of the local that is written
+ twoword..........true, if a two-word type is written
+
+*******************************************************************************/
+
+static void typecheck_invalidate_locals(verifier_state *state, s4 index, bool twoword)
+{
+ s4 i;
+ s4 t;
+ s4 mapped;
+ jitdata *jd = state->jd;
+ s4 *localmap = jd->local_map;
+ varinfo *vars = jd->var;
+
+ i = state->reverselocalmap[index];
+
+ /* invalidate locals of two-word type at index i-1 */
+
+ if (i > 0) {
+ localmap += 5 * (i-1);
+ for (t=0; t<5; ++t) {
+ mapped = *localmap++;
+ if (mapped >= 0 && IS_2_WORD_TYPE(vars[mapped].type)) {
+ LOG1("invalidate local %d", mapped);
+ vars[mapped].type = TYPE_VOID;
+ }
+ }
+ }
+ else {
+ localmap += 5 * i;
+ }
+
+ /* invalidate locals at index i */
+
+ for (t=0; t<5; ++t) {
+ mapped = *localmap++;
+ if (mapped >= 0) {
+ LOG1("invalidate local %d", mapped);
+ vars[mapped].type = TYPE_VOID;
+ }
+ }
+
+ /* if a two-word type is written, invalidate locals at index i+1 */
+
+ if (twoword) {
+ for (t=0; t<5; ++t) {
+ mapped = *localmap++;
+ if (mapped >= 0) {
+ LOG1("invalidate local %d", mapped);
+ vars[mapped].type = TYPE_VOID;
+ }
+ }
+ }
+}
+
+
/* verify_basic_block **********************************************************
Perform bytecode verification of a basic block.
static bool
verify_basic_block(verifier_state *state)
{
- stackptr srcstack; /* source stack for copying and merging */
int opcode; /* current opcode */
int len; /* for counting instructions, etc. */
bool superblockend; /* true if no fallthrough to next block */
+ instruction *iptr; /* the current instruction */
basicblock *tbptr; /* temporary for target block */
- stackptr dst; /* output stack of current instruction */
- basicblock **tptr; /* pointer into target list of switch instr. */
- classinfo *cls; /* temporary */
bool maythrow; /* true if this instruction may throw */
- classinfo *myclass;
unresolved_field *uf; /* for field accesses */
- fieldinfo **fieldinfop; /* for field accesses */
+ constant_FMIref *fieldref; /* for field accesses */
s4 i;
- s4 b_index;
typecheck_result r;
-
- LOGSTR1("\n---- BLOCK %04d ------------------------------------------------\n",state->bptr->debug_nr);
+ resolve_result_t result;
+ branch_target_t *table;
+ lookup_target_t *lookup;
+ jitdata *jd = state->jd;
+ varinfo *dv;
+ exceptiontable *ex;
+
+ LOGSTR1("\n---- BLOCK %04d ------------------------------------------------\n",state->bptr->nr);
LOGFLUSH;
+ DOLOG(show_basicblock(jd, state->bptr, SHOW_STACK));
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;
+ dv = 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;
+ for (ex = state->cd->exceptiontable; ex ; ex = ex->down) {
+ if ((ex->start->nr <= state->bptr->nr) && (ex->end->nr > state->bptr->nr)) {
+ LOG1("active handler L%03d", ex->handler->nr);
+ state->handlers[len++] = ex;
}
}
state->handlers[len] = NULL;
/* init variable types at the start of this block */
- COPY_TYPEVECTORSET(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 variale: %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));
+ typevector_copy_inplace(state->bptr->inlocals, jd->var, state->numlocals);
+
+ DOLOG(typecheck_print_vararray(stdout, jd, state->bptr->invars,
+ state->bptr->indepth));
+ DOLOG(typevector_print(stdout, jd->var, state->numlocals));
LOGNL; LOGFLUSH;
/* loop over the instructions */
while (--len >= 0) {
TYPECHECK_COUNT(stat_ins);
- DOLOG(typestate_print(get_logfile(),state->curstack,state->localset,state->numlocals));
- LOGNL; LOGFLUSH;
+ iptr = state->iptr;
- DOLOG(show_icmd(state->iptr,false)); LOGNL; LOGFLUSH;
+ DOLOG(typevector_print(stdout, jd->var, state->numlocals));
+ LOGNL; LOGFLUSH;
+ DOLOG(show_icmd(jd, state->iptr, false, SHOW_STACK)); LOGNL; LOGFLUSH;
- opcode = state->iptr->opc;
- myclass = state->iptr->method->class;
- dst = state->iptr->dst;
+ opcode = iptr->opc;
+ dv = VAROP(iptr->dst);
maythrow = false;
switch (opcode) {
/* 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:
- COPYTYPE(state->curstack,dst);
- break;
-
- case ICMD_DUP_X1:
- COPYTYPE(state->curstack,dst);
- COPYTYPE(state->curstack,dst-2);
- COPYTYPE(state->curstack->prev,dst-1);
- break;
-
- case ICMD_DUP_X2:
- 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:
- COPYTYPE(state->curstack,dst);
- COPYTYPE(state->curstack->prev,dst-1);
- break;
-
- case ICMD_DUP2_X1:
- 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:
- 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:
- COPYTYPE(state->curstack,dst-1);
- COPYTYPE(state->curstack->prev,dst);
+ case ICMD_MOVE:
+ case ICMD_COPY:
+ TYPECHECK_COUNT(stat_ins_stack);
+ COPYTYPE(iptr->s1, iptr->dst);
+ dv->type = VAROP(iptr->s1)->type;
break;
/****************************************/
/* PRIMITIVE VARIABLE ACCESS */
- case ICMD_ILOAD: CHECK_ONEWORD(state->iptr->op1,TYPE_INT); break;
- case ICMD_FLOAD: CHECK_ONEWORD(state->iptr->op1,TYPE_FLOAT); break;
- case ICMD_IINC: CHECK_ONEWORD(state->iptr->op1,TYPE_INT); break;
- case ICMD_LLOAD: CHECK_TWOWORD(state->iptr->op1,TYPE_LONG); break;
- case ICMD_DLOAD: CHECK_TWOWORD(state->iptr->op1,TYPE_DOUBLE); break;
-
- case ICMD_FSTORE: STORE_ONEWORD(state->iptr->op1,TYPE_FLOAT); break;
- case ICMD_ISTORE: STORE_ONEWORD(state->iptr->op1,TYPE_INT); break;
- case ICMD_LSTORE: STORE_TWOWORD(state->iptr->op1,TYPE_LONG); break;
- case ICMD_DSTORE: STORE_TWOWORD(state->iptr->op1,TYPE_DOUBLE); break;
+ case ICMD_ILOAD: if (!typevector_checktype(jd->var,state->iptr->s1.varindex,TYPE_INT))
+ TYPECHECK_VERIFYERROR_bool("Local variable type mismatch");
+ dv->type = TYPE_INT;
+ break;
+ case ICMD_IINC: if (!typevector_checktype(jd->var,state->iptr->s1.varindex,TYPE_INT))
+ TYPECHECK_VERIFYERROR_bool("Local variable type mismatch");
+ dv->type = TYPE_INT;
+ break;
+ case ICMD_FLOAD: if (!typevector_checktype(jd->var,state->iptr->s1.varindex,TYPE_FLT))
+ TYPECHECK_VERIFYERROR_bool("Local variable type mismatch");
+ dv->type = TYPE_FLT;
+ break;
+ case ICMD_LLOAD: if (!typevector_checktype(jd->var,state->iptr->s1.varindex,TYPE_LNG))
+ TYPECHECK_VERIFYERROR_bool("Local variable type mismatch");
+ dv->type = TYPE_LNG;
+ break;
+ case ICMD_DLOAD: if (!typevector_checktype(jd->var,state->iptr->s1.varindex,TYPE_DBL))
+ TYPECHECK_VERIFYERROR_bool("Local variable type mismatch");
+ dv->type = TYPE_DBL;
+ break;
+
+ case ICMD_ISTORE:
+ typecheck_invalidate_locals(state, state->iptr->dst.varindex, false);
+ typevector_store(jd->var,state->iptr->dst.varindex,TYPE_INT,NULL);
+ break;
+ case ICMD_FSTORE:
+ typecheck_invalidate_locals(state, state->iptr->dst.varindex, false);
+ typevector_store(jd->var,state->iptr->dst.varindex,TYPE_FLT,NULL);
+ break;
+ case ICMD_LSTORE:
+ typecheck_invalidate_locals(state, state->iptr->dst.varindex, true);
+ typevector_store(jd->var,state->iptr->dst.varindex,TYPE_LNG,NULL);
+ break;
+ case ICMD_DSTORE:
+ typecheck_invalidate_locals(state, state->iptr->dst.varindex, true);
+ typevector_store(jd->var,state->iptr->dst.varindex,TYPE_DBL,NULL);
+ break;
/****************************************/
/* LOADING ADDRESS FROM VARIABLE */
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);
+ if (!TYPEDESC_IS_REFERENCE(*VAROP(state->iptr->s1))) {
+ TYPECHECK_VERIFYERROR_bool("illegal instruction: ALOAD loading non-reference");
}
+ TYPEINFO_COPY(VAROP(state->iptr->s1)->typeinfo,dv->typeinfo);
+ dv->type = TYPE_ADR;
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");
- }
+ typecheck_invalidate_locals(state, state->iptr->dst.varindex, false);
- if (TYPESTACK_IS_RETURNADDRESS(state->curstack)) {
- typevectorset_store_retaddr(state->localset,state->iptr->op1,&(state->curstack->typeinfo));
+ if (TYPEINFO_IS_PRIMITIVE(VAROP(state->iptr->s1)->typeinfo)) {
+ typevector_store_retaddr(jd->var,state->iptr->dst.varindex,&(VAROP(state->iptr->s1)->typeinfo));
}
else {
- typevectorset_store(state->localset,state->iptr->op1,TYPE_ADDRESS,
- &(state->curstack->typeinfo));
+ typevector_store(jd->var,state->iptr->dst.varindex,TYPE_ADR,
+ &(VAROP(state->iptr->s1)->typeinfo));
}
break;
/* LOADING ADDRESS FROM ARRAY */
case ICMD_AALOAD:
- if (!TYPEINFO_MAYBE_ARRAY_OF_REFS(state->curstack->prev->typeinfo))
+ if (!TYPEINFO_MAYBE_ARRAY_OF_REFS(VAROP(state->iptr->s1)->typeinfo))
TYPECHECK_VERIFYERROR_bool("illegal instruction: AALOAD on non-reference array");
- if (!typeinfo_init_component(&state->curstack->prev->typeinfo,&dst->typeinfo))
+ if (!typeinfo_init_component(&VAROP(state->iptr->s1)->typeinfo,&dv->typeinfo))
return false;
+ dv->type = TYPE_ADR;
maythrow = true;
break;
/****************************************/
/* FIELD ACCESS */
+ case ICMD_PUTFIELD:
+ case ICMD_PUTSTATIC:
case ICMD_PUTFIELDCONST:
case ICMD_PUTSTATICCONST:
- TYPECHECK_COUNT(stat_ins_field);
+ case ICMD_GETFIELD:
+ case ICMD_GETSTATIC:
+ {
+ varinfo *valueslot = NULL;
+ typeinfo *instanceti = NULL;
+ typeinfo *valueti = NULL;
+ bool isstatic = false;
+ bool isput = false;
+ typeinfo constti;
+
+ TYPECHECK_COUNT(stat_ins_field);
+
+ if (INSTRUCTION_IS_UNRESOLVED(state->iptr)) {
+ uf = state->iptr->sx.s23.s3.uf;
+ fieldref = uf->fieldref;
+ }
+ else {
+ uf = NULL;
+ fieldref = state->iptr->sx.s23.s3.fmiref;
+ }
+
+ /* get opcode dependent values */
+
+ switch (state->iptr->opc) {
+ case ICMD_PUTFIELD:
+ isput = true;
+ valueslot = VAROP(state->iptr->sx.s23.s2);
+ instanceti = &(VAROP(state->iptr->s1)->typeinfo);
+ break;
+
+ case ICMD_PUTFIELDCONST:
+ isput = true;
+ instanceti = &(VAROP(state->iptr->s1)->typeinfo);
+putfield_const_tail:
+ if (IS_ADR_TYPE(fieldref->parseddesc.fd->type)) {
+ /* XXX check for java.lang.Class constant values? */
+ if (state->iptr->sx.val.anyptr) {
+ assert(class_java_lang_String);
+ assert(class_java_lang_String->state & CLASS_LOADED);
+ assert(class_java_lang_String->state & CLASS_LINKED);
+ typeinfo_init_classinfo(&constti, class_java_lang_String);
+ }
+ else {
+ TYPEINFO_INIT_NULLTYPE(constti);
+ }
+ valueti = &constti;
+ }
+ break;
+
+ case ICMD_PUTSTATIC:
+ isput = true;
+ isstatic = true;
+ valueslot = VAROP(state->iptr->s1);
+ break;
+
+ case ICMD_PUTSTATICCONST:
+ isput = true;
+ isstatic = true;
+ goto putfield_const_tail;
+
+ case ICMD_GETFIELD:
+ instanceti = &(VAROP(state->iptr->s1)->typeinfo);
+ break;
+
+ case ICMD_GETSTATIC:
+ isstatic = true;
+ break;
+
+ default:
+ assert(false);
+ }
- uf = INSTRUCTION_PUTCONST_FIELDREF(state->iptr);
- fieldinfop = INSTRUCTION_PUTCONST_FIELDINFO_PTR(state->iptr);
+ if (valueslot && IS_ADR_TYPE(valueslot->type)) {
+ valueti = &(valueslot->typeinfo);
+ }
- goto fieldaccess_tail;
+ /* try to resolve the field reference lazily */
- case ICMD_PUTFIELD:
- case ICMD_PUTSTATIC:
- TYPECHECK_COUNT(stat_ins_field);
+ result = resolve_field_lazy(state->m, fieldref);
- uf = (unresolved_field *) state->iptr[0].target;
- fieldinfop = (fieldinfo **) &(state->iptr[0].val.a);
-
- goto fieldaccess_tail;
+ if (result == resolveSucceeded) {
+ fieldinfo *fi;
- case ICMD_GETFIELD:
- case ICMD_GETSTATIC:
- TYPECHECK_COUNT(stat_ins_field);
+ /* perform verification checks now */
+
+ fi = fieldref->p.field;
- uf = (unresolved_field *) state->iptr[0].target;
- fieldinfop = (fieldinfo **) &(state->iptr[0].val.a);
+ result = resolve_field_verifier_checks(
+ state->m, fieldref, fi->class, fi,
+ instanceti, valueti, isstatic, isput);
+ }
- /* the result is pushed on the stack */
- if (dst->type == TYPE_ADR) {
- if (!typeinfo_init_from_typedesc(uf->fieldref->parseddesc.fd,NULL,&(dst->typeinfo)))
+ if (result == resolveFailed)
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? */
+ /* if not resolved, yet, create an unresolved field */
- /* try to resolve the field reference */
- if (!resolve_field(uf,resolveLazy,fieldinfop))
- return false;
+ if (result != resolveSucceeded) {
+ if (!uf) {
+ uf = resolve_create_unresolved_field(state->m->class,
+ state->m, state->iptr);
+ if (!uf)
+ return false;
- /* we need a patcher, so this is not a leafmethod */
-#if defined(__MIPS__) || defined(__POWERPC__)
- if (!*fieldinfop || !(*fieldinfop)->class->initialized)
- state->cd->method->isleafmethod = false;
-#endif
-
- maythrow = true;
+ state->iptr->sx.s23.s3.uf = uf;
+ state->iptr->flags.bits |= INS_FLAG_UNRESOLVED;
+ }
+
+ /* record the subtype constraints for this field access */
+
+ if (!resolve_constrain_unresolved_field(
+ uf, state->m->class, state->m,
+ instanceti, valueti))
+ return false; /* XXX maybe wrap exception? */
+
+ TYPECHECK_COUNTIF(INSTRUCTION_IS_UNRESOLVED(state->iptr),stat_ins_field_unresolved);
+ TYPECHECK_COUNTIF(INSTRUCTION_IS_RESOLVED(state->iptr) &&
+ !state->iptr->sx.s23.s3.fmiref->p.field->class->initialized,
+ stat_ins_field_uninitialized);
+ }
+
+ /* write the result type */
+
+ if (iptr->opc == ICMD_GETFIELD || iptr->opc == ICMD_GETSTATIC) {
+ dv->type = fieldref->parseddesc.fd->type;
+ if (dv->type == TYPE_ADR) {
+ if (!typeinfo_init_from_typedesc(fieldref->parseddesc.fd,
+ NULL, &(dv->typeinfo)))
+ return false;
+ }
+ }
+
+ maythrow = true;
+ }
break;
/****************************************/
/* PRIMITIVE ARRAY ACCESS */
case ICMD_ARRAYLENGTH:
- if (!TYPEINFO_MAYBE_ARRAY(state->curstack->typeinfo)
- && state->curstack->typeinfo.typeclass.cls != pseudo_class_Arraystub)
+ if (!TYPEINFO_MAYBE_ARRAY(VAROP(state->iptr->s1)->typeinfo)
+ && VAROP(state->iptr->s1)->typeinfo.typeclass.cls != pseudo_class_Arraystub)
TYPECHECK_VERIFYERROR_bool("illegal instruction: ARRAYLENGTH on non-array");
+ dv->type = TYPE_INT;
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))
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(VAROP(state->iptr->s1)->typeinfo,ARRAYTYPE_BOOLEAN)
+ && !TYPEINFO_MAYBE_PRIMITIVE_ARRAY(VAROP(state->iptr->s1)->typeinfo,ARRAYTYPE_BYTE))
TYPECHECK_VERIFYERROR_bool("Array type mismatch");
+ dv->type = TYPE_INT;
maythrow = true;
break;
case ICMD_CALOAD:
- if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(state->curstack->prev->typeinfo,ARRAYTYPE_CHAR))
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(VAROP(state->iptr->s1)->typeinfo,ARRAYTYPE_CHAR))
TYPECHECK_VERIFYERROR_bool("Array type mismatch");
+ dv->type = TYPE_INT;
maythrow = true;
break;
case ICMD_DALOAD:
- if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(state->curstack->prev->typeinfo,ARRAYTYPE_DOUBLE))
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(VAROP(state->iptr->s1)->typeinfo,ARRAYTYPE_DOUBLE))
TYPECHECK_VERIFYERROR_bool("Array type mismatch");
+ dv->type = TYPE_DBL;
maythrow = true;
break;
case ICMD_FALOAD:
- if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(state->curstack->prev->typeinfo,ARRAYTYPE_FLOAT))
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(VAROP(state->iptr->s1)->typeinfo,ARRAYTYPE_FLOAT))
TYPECHECK_VERIFYERROR_bool("Array type mismatch");
+ dv->type = TYPE_FLT;
maythrow = true;
break;
case ICMD_IALOAD:
- if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(state->curstack->prev->typeinfo,ARRAYTYPE_INT))
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(VAROP(state->iptr->s1)->typeinfo,ARRAYTYPE_INT))
TYPECHECK_VERIFYERROR_bool("Array type mismatch");
+ dv->type = TYPE_INT;
maythrow = true;
break;
case ICMD_SALOAD:
- if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(state->curstack->prev->typeinfo,ARRAYTYPE_SHORT))
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(VAROP(state->iptr->s1)->typeinfo,ARRAYTYPE_SHORT))
TYPECHECK_VERIFYERROR_bool("Array type mismatch");
+ dv->type = TYPE_INT;
maythrow = true;
break;
case ICMD_LALOAD:
- if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(state->curstack->prev->typeinfo,ARRAYTYPE_LONG))
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(VAROP(state->iptr->s1)->typeinfo,ARRAYTYPE_LONG))
TYPECHECK_VERIFYERROR_bool("Array type mismatch");
+ dv->type = TYPE_LNG;
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))
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(VAROP(state->iptr->s1)->typeinfo,ARRAYTYPE_BOOLEAN)
+ && !TYPEINFO_MAYBE_PRIMITIVE_ARRAY(VAROP(state->iptr->s1)->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))
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(VAROP(state->iptr->s1)->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))
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(VAROP(state->iptr->s1)->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))
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(VAROP(state->iptr->s1)->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))
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(VAROP(state->iptr->s1)->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))
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(VAROP(state->iptr->s1)->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))
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(VAROP(state->iptr->s1)->typeinfo,ARRAYTYPE_LONG))
TYPECHECK_VERIFYERROR_bool("Array type mismatch");
maythrow = true;
break;
/* 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_ADR_OP(state->iptr->sx.s23.s3);
+ TYPECHECK_INT_OP(state->iptr->sx.s23.s2);
+ TYPECHECK_ADR_OP(state->iptr->s1);
+ if (!TYPEINFO_MAYBE_ARRAY_OF_REFS(VAROP(state->iptr->s1)->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))
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(VAROP(state->iptr->s1)->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))
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(VAROP(state->iptr->s1)->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))
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(VAROP(state->iptr->s1)->typeinfo, ARRAYTYPE_BOOLEAN)
+ && !TYPEINFO_MAYBE_PRIMITIVE_ARRAY(VAROP(state->iptr->s1)->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))
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(VAROP(state->iptr->s1)->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))
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(VAROP(state->iptr->s1)->typeinfo, ARRAYTYPE_SHORT))
TYPECHECK_VERIFYERROR_bool("Array type mismatch");
maythrow = true;
break;
/* ADDRESS CONSTANTS */
case ICMD_ACONST:
- if (state->iptr->val.a == NULL)
- TYPEINFO_INIT_NULLTYPE(dst->typeinfo);
- else
- /* string constant (or constant for builtin function) */
- TYPEINFO_INIT_CLASSINFO(dst->typeinfo,class_java_lang_String);
+ if (state->iptr->flags.bits & INS_FLAG_CLASS) {
+ /* a java.lang.Class reference */
+ TYPEINFO_INIT_JAVA_LANG_CLASS(dv->typeinfo,state->iptr->sx.val.c);
+ }
+ else {
+ if (state->iptr->sx.val.anyptr == NULL)
+ TYPEINFO_INIT_NULLTYPE(dv->typeinfo);
+ else {
+ /* string constant (or constant for builtin function) */
+ typeinfo_init_classinfo(&(dv->typeinfo),class_java_lang_String);
+ }
+ }
+ dv->type = TYPE_ADR;
break;
/****************************************/
/* CHECKCAST AND INSTANCEOF */
case ICMD_CHECKCAST:
- TYPECHECK_ADR(state->curstack);
+ TYPECHECK_ADR_OP(state->iptr->s1);
/* returnAddress is not allowed */
- if (!TYPEINFO_IS_REFERENCE(state->curstack->typeinfo))
+ if (!TYPEINFO_IS_REFERENCE(VAROP(state->iptr->s1)->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_ARRAYCHECKCAST:
- TYPECHECK_ADR(state->curstack);
- /* returnAddress is not allowed */
- if (!TYPEINFO_IS_REFERENCE(state->curstack->typeinfo))
- TYPECHECK_VERIFYERROR_bool("Illegal instruction: ARRAYCHECKCAST on non-reference");
-
- if (state->iptr[0].op1) {
- /* a resolved array class */
- cls = ((vftbl_t *)state->iptr[0].target)->class;
- TYPEINFO_INIT_CLASSINFO(dst->typeinfo,cls);
- }
- else {
- /* an unresolved array class reference */
- if (!typeinfo_init_class(&(dst->typeinfo),CLASSREF_OR_CLASSINFO(state->iptr[0].target)))
+ if (!typeinfo_init_class(&(dv->typeinfo),state->iptr->sx.s23.s3.c))
return false;
- }
+ dv->type = TYPE_ADR;
maythrow = true;
break;
case ICMD_INSTANCEOF:
- TYPECHECK_ADR(state->curstack);
+ TYPECHECK_ADR_OP(state->iptr->s1);
/* returnAddress is not allowed */
- if (!TYPEINFO_IS_REFERENCE(state->curstack->typeinfo))
+ if (!TYPEINFO_IS_REFERENCE(VAROP(state->iptr->s1)->typeinfo))
TYPECHECK_VERIFYERROR_bool("Illegal instruction: INSTANCEOF on non-reference");
+ dv->type = TYPE_INT;
break;
/****************************************/
/* BRANCH INSTRUCTIONS */
+ case ICMD_INLINE_GOTO:
+ COPYTYPE(state->iptr->s1,state->iptr->dst);
+ /* FALLTHROUGH! */
case ICMD_GOTO:
superblockend = true;
/* FALLTHROUGH! */
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:
+
+ case ICMD_IF_FCMPEQ:
+ case ICMD_IF_FCMPNE:
+
+ case ICMD_IF_FCMPL_LT:
+ case ICMD_IF_FCMPL_GE:
+ case ICMD_IF_FCMPL_GT:
+ case ICMD_IF_FCMPL_LE:
+
+ case ICMD_IF_FCMPG_LT:
+ case ICMD_IF_FCMPG_GE:
+ case ICMD_IF_FCMPG_GT:
+ case ICMD_IF_FCMPG_LE:
+
+ case ICMD_IF_DCMPEQ:
+ case ICMD_IF_DCMPNE:
+
+ case ICMD_IF_DCMPL_LT:
+ case ICMD_IF_DCMPL_GE:
+ case ICMD_IF_DCMPL_GT:
+ case ICMD_IF_DCMPL_LE:
+
+ case ICMD_IF_DCMPG_LT:
+ case ICMD_IF_DCMPG_GE:
+ case ICMD_IF_DCMPG_GT:
+ case ICMD_IF_DCMPG_LE:
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))
+ if (!typestate_reach(state, state->iptr->dst.block,
+ state->bptr->outvars, jd->var,
+ state->bptr->outdepth))
return false;
break;
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 */
+
+ table = iptr->dst.table;
+ i = iptr->sx.s23.s3.tablehigh
+ - iptr->sx.s23.s2.tablelow + 1 + 1; /* plus default */
+
+ while (--i >= 0) {
+ tbptr = (table++)->block;
+ LOG2("target %d is block %04d",i,tbptr->nr);
+ if (!typestate_reach(state, tbptr, state->bptr->outvars,
+ jd->var, state->bptr->outdepth))
+ return false;
}
- goto switch_instruction_tail;
+
+ LOG("switch done");
+ superblockend = true;
+ break;
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;
+
+ lookup = iptr->dst.lookup;
+ i = iptr->sx.s23.s2.lookupcount;
+
+ if (!typestate_reach(state,iptr->sx.s23.s3.lookupdefault.block,
+ state->bptr->outvars, jd->var,
+ state->bptr->outdepth))
+ return false;
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))
+ tbptr = (lookup++)->target.block;
+ LOG2("target %d is block %04d",i,tbptr->nr);
+ if (!typestate_reach(state, tbptr, state->bptr->outvars,
+ jd->var, state->bptr->outdepth))
return false;
}
+
LOG("switch done");
superblockend = true;
break;
+
/****************************************/
/* ADDRESS RETURNS AND THROW */
case ICMD_ATHROW:
- r = typeinfo_is_assignable_to_class(&state->curstack->typeinfo,
+ TYPECHECK_COUNT(stat_ins_athrow);
+ r = typeinfo_is_assignable_to_class(&VAROP(state->iptr->s1)->typeinfo,
CLASSREF_OR_CLASSINFO(class_java_lang_Throwable));
if (r == typecheck_FALSE)
TYPECHECK_VERIFYERROR_bool("illegal instruction: ATHROW on non-Throwable");
if (r == typecheck_FAIL)
return false;
- /* XXX handle typecheck_MAYBE */
+ if (r == typecheck_MAYBE) {
+ /* the check has to be postponed. we need a patcher */
+ TYPECHECK_COUNT(stat_ins_athrow_unresolved);
+ iptr->sx.s23.s2.uc = 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),
+ &VAROP(state->iptr->s1)->typeinfo);
+ iptr->flags.bits |= INS_FLAG_UNRESOLVED;
+ }
superblockend = true;
maythrow = true;
break;
case ICMD_ARETURN:
- if (!TYPEINFO_IS_REFERENCE(state->curstack->typeinfo))
+ TYPECHECK_COUNT(stat_ins_areturn);
+ if (!TYPEINFO_IS_REFERENCE(VAROP(state->iptr->s1)->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)))
+ if (state->returntype.type != TYPE_ADR
+ || (r = typeinfo_is_assignable(&VAROP(state->iptr->s1)->typeinfo,&(state->returntype.typeinfo)))
== typecheck_FALSE)
TYPECHECK_VERIFYERROR_bool("Return type mismatch");
if (r == typecheck_FAIL)
return false;
- /* XXX handle typecheck_MAYBE */
+ if (r == typecheck_MAYBE) {
+ /* the check has to be postponed, we need a patcher */
+ TYPECHECK_COUNT(stat_ins_areturn_unresolved);
+ iptr->sx.s23.s2.uc = create_unresolved_class(
+ state->m,
+ state->m->parseddesc->returntype.classref,
+ &VAROP(state->iptr->s1)->typeinfo);
+ iptr->flags.bits |= INS_FLAG_UNRESOLVED;
+ }
goto return_tail;
/****************************************/
goto return_tail;
case ICMD_LRETURN:
- if (state->returntype.type != TYPE_LONG) TYPECHECK_VERIFYERROR_bool("Return type mismatch");
+ if (state->returntype.type != TYPE_LNG) TYPECHECK_VERIFYERROR_bool("Return type mismatch");
goto return_tail;
case ICMD_FRETURN:
- if (state->returntype.type != TYPE_FLOAT) TYPECHECK_VERIFYERROR_bool("Return type mismatch");
+ if (state->returntype.type != TYPE_FLT) TYPECHECK_VERIFYERROR_bool("Return type mismatch");
goto return_tail;
case ICMD_DRETURN:
- if (state->returntype.type != TYPE_DOUBLE) TYPECHECK_VERIFYERROR_bool("Return type mismatch");
+ if (state->returntype.type != TYPE_DBL) 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_LEAVE;
+ TYPECHECK_COUNT(stat_ins_primitive_return);
+
+ if (state->initmethod && state->m->class != class_java_lang_Object) {
+ /* Check if the 'this' instance has been initialized. */
+ LOG("Checking <init> marker");
+ if (!typevector_checktype(jd->var,state->numlocals-1,TYPE_INT))
+ TYPECHECK_VERIFYERROR_bool("<init> method does not initialize 'this'");
+ }
+
superblockend = true;
maythrow = true;
break;
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))
+ tbptr = state->iptr->sx.s23.s3.jsrtarget.block;
+ TYPEINFO_INIT_RETURNADDRESS(dv->typeinfo, state->bptr->next);
+ if (!typestate_reach(state, tbptr, state->bptr->outvars, jd->var,
+ state->bptr->outdepth))
return false;
superblockend = true;
case ICMD_RET:
/* check returnAddress variable */
- if (!typevectorset_checkretaddr(state->localset,state->iptr->op1))
+ if (!typevector_checkretaddr(jd->var,state->iptr->s1.varindex))
TYPECHECK_VERIFYERROR_bool("illegal instruction: RET using non-returnAddress variable");
- if (!typestate_ret(state,state->iptr->op1))
+ if (!typestate_reach(state, iptr->dst.block, state->bptr->outvars, jd->var,
+ state->bptr->outdepth))
return false;
superblockend = true;
TYPECHECK_COUNT(stat_ins_invoke);
if (!verify_invocation(state))
return false;
+ TYPECHECK_COUNTIF(INSTRUCTION_IS_UNRESOLVED(iptr), stat_ins_invoke_unresolved);
maythrow = true;
break;
/* MULTIANEWARRAY */
case ICMD_MULTIANEWARRAY:
- /* XXX make this a separate function */
- {
- vftbl_t *arrayvftbl;
- arraydescriptor *desc;
-
- /* check the array lengths on the stack */
- i = state->iptr[0].op1;
- if (i < 1)
- TYPECHECK_VERIFYERROR_bool("Illegal dimension argument");
- srcstack = state->curstack;
- while (i--) {
- if (!srcstack)
- TYPECHECK_VERIFYERROR_bool("Unable to pop operand off an empty stack");
- TYPECHECK_INT(srcstack);
- srcstack = srcstack->prev;
- }
-
- /* check array descriptor */
- if (state->iptr[0].target == NULL) {
- arrayvftbl = (vftbl_t*) state->iptr[0].val.a;
- if (!arrayvftbl)
- TYPECHECK_VERIFYERROR_bool("MULTIANEWARRAY with unlinked class");
- if ((desc = arrayvftbl->arraydesc) == NULL)
- TYPECHECK_VERIFYERROR_bool("MULTIANEWARRAY with non-array class");
- if (desc->dimension < state->iptr[0].op1)
- TYPECHECK_VERIFYERROR_bool("MULTIANEWARRAY dimension to high");
-
- /* set the array type of the result */
- TYPEINFO_INIT_CLASSINFO(dst->typeinfo,arrayvftbl->class);
- }
- else {
- /* XXX do checks in patcher */
- if (!typeinfo_init_class(&(dst->typeinfo),CLASSREF_OR_CLASSINFO(state->iptr[0].val.a)))
- return false;
- }
- }
+ if (!verify_multianewarray(state))
+ return false;
maythrow = true;
break;
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
/****************************************/
* Instructions below...
* *) don't operate on local variables,
* *) don't operate on references,
- * *) don't operate on returnAddresses.
+ * *) don't operate on returnAddresses,
+ * *) don't affect control flow (except
+ * by throwing exceptions).
*
* (These instructions are typechecked in
* analyse_stack.)
case ICMD_IDIV:
case ICMD_IREM:
+ dv->type = TYPE_INT;
+ maythrow = true;
+ break;
+
case ICMD_LDIV:
case ICMD_LREM:
-
+ dv->type = TYPE_LNG;
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:
+ break;
case ICMD_ICONST:
- case ICMD_LCONST:
- case ICMD_FCONST:
- case ICMD_DCONST:
-
- case ICMD_IFEQ_ICONST:
- case ICMD_IFNE_ICONST:
- case ICMD_IFLT_ICONST:
- case ICMD_IFGE_ICONST:
- case ICMD_IFGT_ICONST:
- case ICMD_IFLE_ICONST:
- case ICMD_ELSE_ICONST:
-
case ICMD_IADD:
case ICMD_ISUB:
case ICMD_IMUL:
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_IMULPOW2:
- case ICMD_LMULPOW2:
case ICMD_IDIVPOW2:
- case ICMD_LDIVPOW2:
case ICMD_IADDCONST:
case ICMD_ISUBCONST:
case ICMD_IMULCONST:
case ICMD_ISHRCONST:
case ICMD_IUSHRCONST:
case ICMD_IREMPOW2:
+ case ICMD_INT2BYTE:
+ case ICMD_INT2CHAR:
+ case ICMD_INT2SHORT:
+ case ICMD_L2I:
+ case ICMD_F2I:
+ case ICMD_D2I:
+ case ICMD_LCMP:
+ case ICMD_LCMPCONST:
+ case ICMD_FCMPL:
+ case ICMD_FCMPG:
+ case ICMD_DCMPL:
+ case ICMD_DCMPG:
+ dv->type = TYPE_INT;
+ break;
+
+ case ICMD_LCONST:
+ 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_LMULPOW2:
+ case ICMD_LDIVPOW2:
case ICMD_LADDCONST:
case ICMD_LSUBCONST:
case ICMD_LMULCONST:
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:
+ dv->type = TYPE_LNG;
+ break;
+ case ICMD_FCONST:
+ case ICMD_I2F:
+ case ICMD_L2F:
+ case ICMD_D2F:
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:
+ dv->type = TYPE_FLT;
+ break;
+
+ case ICMD_DCONST:
+ case ICMD_I2D:
+ case ICMD_L2D:
+ case ICMD_F2D:
+ case ICMD_DADD:
+ case ICMD_DSUB:
+ case ICMD_DMUL:
+ case ICMD_DDIV:
+ case ICMD_DREM:
case ICMD_DNEG:
+ dv->type = TYPE_DBL;
+ break;
+ case ICMD_INLINE_START:
+ case ICMD_INLINE_END:
+ break;
- /*XXX What shall we do with the following ?*/
+ /* 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;
+ VAR(state->exinvars)->typeinfo.typeclass = state->handlers[i]->catchtype;
else
- state->excstack.typeinfo.typeclass.cls = class_java_lang_Throwable;
+ VAR(state->exinvars)->typeinfo.typeclass.cls = class_java_lang_Throwable;
if (!typestate_reach(state,
state->handlers[i]->handler,
- &(state->excstack),state->localset))
+ &(state->exinvars), jd->var, 1))
return false;
i++;
}
}
- LOG("next instruction");
+ LOG("\t\tnext instruction");
state->iptr++;
} /* while instructions */
LOG("instructions done");
LOGSTR("RESULT=> ");
- DOLOG(typestate_print(get_logfile(),state->curstack,state->localset,state->numlocals));
+ DOLOG(typecheck_print_vararray(stdout, jd, state->bptr->outvars,
+ state->bptr->outdepth));
+ DOLOG(typevector_print(stdout, jd->var, state->numlocals));
LOGNL; LOGFLUSH;
/* propagate stack and variables to the following block */
if (!superblockend) {
LOG("reaching following block");
- tbptr = state->bptr + 1;
+ tbptr = state->bptr->next;
while (tbptr->flags == BBDELETED) {
- tbptr++;
+ tbptr = tbptr->next;
#ifdef TYPECHECK_DEBUG
/* this must be checked in parse.c */
- if ((tbptr->debug_nr) >= state->m->basicblockcount)
+ if ((tbptr->nr) >= state->basicblockcount)
TYPECHECK_VERIFYERROR_bool("Control flow falls off the last block");
#endif
}
- if (!typestate_reach(state,tbptr,dst,state->localset))
+ if (!typestate_reach(state,tbptr,state->bptr->outvars, jd->var,
+ state->bptr->outdepth))
return false;
}
* have been saved if an <init> call inside the block has
* modified the instack types. (see INVOKESPECIAL) */
- if (state->savedstack) {
- stackptr sp = state->bptr->instack;
- stackptr copy = state->savedstack;
- TYPECHECK_COUNT(stat_savedstack);
- LOG("restoring saved instack");
- TYPESTACK_COPY(sp,copy);
- state->bptr->instack = state->savedstack;
- state->savedstack = NULL;
- }
+ if (state->savedinvars)
+ typestate_restore_invars(state);
+
return true;
}
+
/* verify_init_locals **********************************************************
Initialize the local variables in the verifier state.
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 */
+ int index;
+ varinfo *locals;
+ varinfo *v;
+ jitdata *jd = state->jd;
+ int skip = 0;
- lset = MGET_TYPEVECTOR(state->localbuf,0,state->numlocals);
- lset->k = 0;
- lset->alt = NULL;
- td = lset->td;
- i = state->validlocals;
+ locals = state->basicblocks[0].inlocals;
/* allocate parameter descriptors if necessary */
if (!descriptor_params_from_paramtypes(state->m->parseddesc,state->m->flags))
return false;
+ /* pre-initialize variables as TYPE_VOID */
+
+ i = state->numlocals;
+ v = locals;
+ while (i--) {
+ v->type = TYPE_VOID;
+ v++;
+ }
+
/* if this is an instance method initialize the "this" ref type */
if (!(state->m->flags & ACC_STATIC)) {
- if (!i)
- TYPECHECK_VERIFYERROR_bool("Not enough local variables for method arguments");
- td->type = TYPE_ADDRESS;
- if (state->initmethod)
- TYPEINFO_INIT_NEWOBJECT(td->info,NULL);
- else
- TYPEINFO_INIT_CLASSINFO(td->info, state->m->class);
- td++;
- i--;
+ index = jd->local_map[5*0 + TYPE_ADR];
+ if (index != UNUSED) {
+ if (state->validlocals < 1)
+ TYPECHECK_VERIFYERROR_bool("Not enough local variables for method arguments");
+ v = locals + index;
+ v->type = TYPE_ADR;
+ if (state->initmethod)
+ TYPEINFO_INIT_NEWOBJECT(v->typeinfo, NULL);
+ else
+ typeinfo_init_classinfo(&(v->typeinfo), state->m->class);
+ }
+
+ skip = 1;
}
LOG("'this' argument set.\n");
/* the rest of the arguments and the return type */
- i = typedescriptors_init_from_methoddesc(td, state->m->parseddesc,
- i,
- true, /* two word types use two slots */
- (td - lset->td), /* skip 'this' pointer */
- &state->returntype);
- if (i == -1)
+ if (!typeinfo_init_varinfos_from_methoddesc(locals, state->m->parseddesc,
+ state->validlocals,
+ skip, /* skip 'this' pointer */
+ jd->local_map,
+ &state->returntype))
return false;
- td += i;
-
- /* 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;
}
+
+/* typecheck_init_flags ********************************************************
+
+ Initialize the basic block flags for the following CFG traversal.
+
+ IN:
+ state............the current state of the verifier
+
+*******************************************************************************/
+
+static void
+typecheck_init_flags(verifier_state *state)
+{
+ s4 i;
+ basicblock *block;
+
+ /* set all BBFINISHED blocks to BBTYPECHECK_UNDEF. */
+
+ i = state->basicblockcount;
+ for (block = state->basicblocks; block; block = block->next) {
+
+#ifdef TYPECHECK_DEBUG
+ /* check for invalid flags */
+ if (block->flags != BBFINISHED && block->flags != BBDELETED && block->flags != BBUNDEF)
+ {
+ LOGSTR1("block flags: %d\n",block->flags); LOGFLUSH;
+ TYPECHECK_ASSERT(false);
+ }
+#endif
+
+ if (block->flags >= BBFINISHED) {
+ block->flags = BBTYPECHECK_UNDEF;
+ }
+ }
+
+ /* the first block is always reached */
+
+ if (state->basicblockcount && state->basicblocks[0].flags == BBTYPECHECK_UNDEF)
+ state->basicblocks[0].flags = BBTYPECHECK_REACHED;
+}
+
+
+/* typecheck_reset_flags *******************************************************
+
+ Reset the flags of basic blocks we have not reached.
+
+ IN:
+ state............the current state of the verifier
+
+*******************************************************************************/
+
+static void
+typecheck_reset_flags(verifier_state *state)
+{
+ basicblock *block;
+
+ /* check for invalid flags at exit */
+
+#ifdef TYPECHECK_DEBUG
+ for (block = state->basicblocks; block; block = block->next) {
+ if (block->flags != BBDELETED
+ && block->flags != BBUNDEF
+ && block->flags != BBFINISHED
+ && block->flags != BBTYPECHECK_UNDEF) /* typecheck may never reach
+ * some exception handlers,
+ * that's ok. */
+ {
+ LOG2("block L%03d has invalid flags after typecheck: %d",
+ block->nr,block->flags);
+ TYPECHECK_ASSERT(false);
+ }
+ }
+#endif
+
+ /* Delete blocks we never reached */
+
+ for (block = state->basicblocks; block; block = block->next) {
+ if (block->flags == BBTYPECHECK_UNDEF)
+ block->flags = BBDELETED;
+ }
+}
+
+
/****************************************************************************/
/* typecheck() */
/* This is the main function of the bytecode verifier. It is called */
/* rdata............registerdata for the method */
/* */
/* RETURN VALUE: */
-/* m................successful verification */
-/* NULL.............an exception has been thrown */
+/* true.............successful verification */
+/* false............an exception has been thrown */
/* */
-/* XXX TODO: */
-/* Bytecode verification has not been tested with inlining and */
-/* probably does not work correctly with inlining. */
/****************************************************************************/
#define MAXPARAMS 255
-methodinfo *typecheck(methodinfo *meth, codegendata *cdata, registerdata *rdata)
+bool typecheck(jitdata *jd)
{
- verifier_state state; /* current state of the verifier */
- int i; /* temporary counter */
+ methodinfo *meth;
+ codegendata *cd;
+ varinfo *savedlocals;
+ verifier_state state; /* current state of the verifier */
+ s4 i;
+ s4 t;
/* collect statistics */
int count_iterations = 0;
TYPECHECK_COUNT(stat_typechecked);
TYPECHECK_COUNT_FREQ(stat_locals,cdata->maxlocals,STAT_LOCALS);
- TYPECHECK_COUNT_FREQ(stat_blocks,meth->basicblockcount/10,STAT_BLOCKS);
+ TYPECHECK_COUNT_FREQ(stat_blocks,cdata->method->basicblockcount/10,STAT_BLOCKS);
+ TYPECHECK_COUNTIF(cdata->method->exceptiontablelength != 0,stat_methods_with_handlers);
+ state.stat_maythrow = false;
#endif
+ /* get required compiler data */
+
+ meth = jd->m;
+ cd = jd->cd;
+
/* some logging on entry */
-
+
+
LOGSTR("\n==============================================================================\n");
- /*DOLOG( show_icmd_method(cdata->method,cdata,rdata));*/
+ DOLOG( show_method(jd, SHOW_STACK) );
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;
+ LOGMETHOD("Entering typecheck: ",cd->method);
/* initialize the verifier state */
- state.savedstackbuf = NULL;
- state.savedstack = NULL;
- state.jsrencountered = false;
state.m = meth;
- state.cd = cdata;
- state.rd = rdata;
+ state.jd = jd;
+ state.cd = cd;
+ state.basicblockcount = jd->basicblockcount;
+ state.basicblocks = jd->basicblocks;
+ state.savedindices = NULL;
+ state.savedinvars = NULL;
/* check if this method is an instance initializer method */
state.initmethod = (state.m->name == utf_init);
- /* reset all BBFINISHED blocks to BBTYPECHECK_UNDEF. */
-
- i = state.m->basicblockcount;
- state.bptr = state.m->basicblocks;
- while (--i >= 0) {
-#ifdef TYPECHECK_DEBUG
- if (state.bptr->flags != BBFINISHED && state.bptr->flags != BBDELETED
- && state.bptr->flags != BBUNDEF)
- {
- /*show_icmd_method(state.cd->method,state.cd,state.rd);*/
- LOGSTR1("block flags: %d\n",state.bptr->flags); LOGFLUSH;
- TYPECHECK_ASSERT(false);
- }
-#endif
- if (state.bptr->flags >= BBFINISHED) {
- state.bptr->flags = BBTYPECHECK_UNDEF;
- }
- state.bptr++;
- }
-
- /* the first block is always reached */
-
- if (state.m->basicblockcount && state.m->basicblocks[0].flags == BBTYPECHECK_UNDEF)
- state.m->basicblocks[0].flags = BBTYPECHECK_REACHED;
+ /* initialize the basic block flags for the following CFG traversal */
- LOG("Blocks reset.\n");
+ typecheck_init_flags(&state);
/* number of local variables */
/* 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);
+ state.numlocals = state.jd->localcount;
+ state.validlocals = state.numlocals;
+ if (state.initmethod)
+ state.numlocals++; /* VERIFIER_EXTRA_LOCALS */
+
+ state.reverselocalmap = DMNEW(s4, state.validlocals);
+ for (i=0; i<jd->m->maxlocals; ++i)
+ for (t=0; t<5; ++t) {
+ s4 mapped = jd->local_map[5*i + t];
+ if (mapped >= 0)
+ state.reverselocalmap[mapped] = i;
+ }
- LOG("Variable buffer allocated.\n");
+ DOLOG(
+ LOG("reverselocalmap:");
+ for (i=0; i<state.validlocals; ++i) {
+ LOG2(" %i => javaindex %i", i, state.reverselocalmap[i]);
+ });
/* allocate the buffer of active exception handlers */
state.handlers = DMNEW(exceptiontable*, state.cd->exceptiontablelength + 1);
+ /* save local variables */
+
+ savedlocals = DMNEW(varinfo, state.numlocals);
+ MCOPY(savedlocals, jd->var, varinfo, state.numlocals);
+
/* initialized local variables of first block */
if (!verify_init_locals(&state))
- return NULL;
+ return false;
- /* initialize the input stack of exception handlers */
+ /* initialize invars of exception handlers */
- state.excstack.prev = NULL;
- state.excstack.type = TYPE_ADR;
- TYPEINFO_INIT_CLASSINFO(state.excstack.typeinfo,
+ state.exinvars = state.numlocals;
+ VAR(state.exinvars)->type = TYPE_ADR;
+ typeinfo_init_classinfo(&(VAR(state.exinvars)->typeinfo),
class_java_lang_Throwable); /* changed later */
LOG("Exception handler stacks set.\n");
state.repeat = false;
- i = state.m->basicblockcount;
- state.bptr = state.m->basicblocks;
+ state.bptr = state.basicblocks;
- while (--i >= 0) {
- LOGSTR1("---- BLOCK %04d, ",state.bptr->debug_nr);
+ for (; state.bptr; state.bptr = state.bptr->next) {
+ LOGSTR1("---- BLOCK %04d, ",state.bptr->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;
+ return false;
}
- state.bptr++;
- } /* while blocks */
+ } /* for blocks */
LOGIF(state.repeat,"state.repeat == true");
} while (state.repeat);
LOG1("Typechecker did %4d iterations",count_iterations);
TYPECHECK_COUNT_FREQ(stat_iterations,count_iterations,STAT_ITERATIONS);
TYPECHECK_COUNTIF(state.jsrencountered,stat_typechecked_jsr);
+ TYPECHECK_COUNTIF(state.stat_maythrow,stat_methods_maythrow);
#endif
- /* check for invalid flags at exit */
- /* XXX make this a separate function */
-
-#ifdef TYPECHECK_DEBUG
- for (i=0; i<state.m->basicblockcount; ++i) {
- if (state.m->basicblocks[i].flags != BBDELETED
- && state.m->basicblocks[i].flags != BBUNDEF
- && state.m->basicblocks[i].flags != BBFINISHED
- && state.m->basicblocks[i].flags != BBTYPECHECK_UNDEF) /* typecheck may never reach
- * some exception handlers,
- * that's ok. */
- {
- LOG2("block L%03d has invalid flags after typecheck: %d",
- state.m->basicblocks[i].debug_nr,state.m->basicblocks[i].flags);
- TYPECHECK_ASSERT(false);
- }
- }
-#endif
-
- /* Reset blocks we never reached */
-
- for (i=0; i<state.m->basicblockcount; ++i) {
- if (state.m->basicblocks[i].flags == BBTYPECHECK_UNDEF)
- state.m->basicblocks[i].flags = BBFINISHED;
- }
-
- LOGimp("exiting typecheck");
+ /* reset the flags of blocks we haven't reached */
- /* just return methodinfo* to indicate everything was ok */
+ typecheck_reset_flags(&state);
- return state.m;
-}
+ /* restore locals */
+
+ MCOPY(jd->var, savedlocals, varinfo, state.numlocals);
-#undef COPYTYPE
+ /* everything's ok */
-#endif /* CACAO_TYPECHECK */
+ LOGimp("exiting typecheck");
+ return true;
+}
+#endif /* ENABLE_VERIFIER */
/*
* These are local overrides for various environment variables in Emacs.