Logo Search packages:      
Sourcecode: cacao-source version File versions  Download package

typecheck-stackbased.c

/* src/vm/jit/verify/typecheck-stackbased.c - stack-based verifier

   Copyright (C) 1996-2005, 2006, 2007, 2008
   CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO

   This file is part of CACAO.

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License as
   published by the Free Software Foundation; either version 2, or (at
   your option) any later version.

   This program is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   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., 51 Franklin Street, Fifth Floor, Boston, MA
   02110-1301, USA.

*/


#include "config.h"

#include <assert.h>

#include "vm/types.h"

#include "vm/builtin.h"
#include "mm/memory.h"

#include "vm/array.h"
#include "vm/global.h"
#include "vm/primitive.h"

#include "vm/jit/parse.h"
#include "vm/jit/show.h"
#include "vm/jit/stack.h"
#include "vm/jit/verify/typecheck-common.h"


/* this #if runs over the whole file: */
#if defined(ENABLE_VERIFIER)

typedef typedescriptor_t verifier_slot_t;

#if defined(TYPECHECK_VERBOSE)
static void typecheck_stackbased_show_state(verifier_state *state,
                                                                  typedescriptor *stack,
                                                                  typedescriptor *stackfloor,
                                                                  bool showins);
#endif


#define CHECK_STACK_DEPTH(d)                                         \
    if (((u1*)stack - (u1*)stackfloor)                               \
            < (((d)-1) * (int)sizeof(verifier_slot_t)))              \
        goto throw_stack_underflow;

/* XXX don't need to check against ACONST for every ICMD */
#define CHECK_STACK_SPACE(d)                                         \
    if (((u1*)STATE->stackceiling - (u1*)stack)                      \
            < (((d)+1) * (int)sizeof(verifier_slot_t)))              \
        if (STATE->iptr->opc != ICMD_ACONST                          \
                || INSTRUCTION_MUST_CHECK(STATE->iptr))              \
            goto throw_stack_overflow;

#define CHECK_STACK_TYPE(s, t)                                       \
    if ((s).type != (t))                                             \
        goto throw_stack_type_error;

/* XXX inefficient */
#define CHECK_LOCAL_TYPE(index, t)                                   \
    do {                                                             \
        if (state.locals[(index)].type != (t))                       \
            goto throw_local_type_error;                             \
        if (STATE->topjsr)                                           \
            STATE->topjsr->usedlocals[(index)] = 1;                  \
        if (STATE->topjsr && IS_2_WORD_TYPE(t))                      \
            STATE->topjsr->usedlocals[(index) + 1] = 1;              \
    } while(0)

/* XXX inefficient */
#define STORE_LOCAL(t, index)                                        \
    do {                                                             \
        state.locals[(index)].type = (t);                            \
        if ((index) && IS_2_WORD_TYPE(state.locals[(index)-1].type)) \
            state.locals[(index-1)].type = TYPE_VOID;                \
        if (STATE->topjsr)                                           \
            STATE->topjsr->usedlocals[(index)] = 1;                  \
    } while (0)

/* XXX inefficient */
#define STORE_LOCAL_2_WORD(t, index)                                 \
    do {                                                             \
        STORE_LOCAL(t, index);                                       \
        state.locals[(index)+1].type = TYPE_VOID;                    \
        if (STATE->topjsr)                                           \
            STATE->topjsr->usedlocals[(index)] = 1;                  \
    } while (0)

#define VERIFY_ERROR(msg)                                            \
    do {                                                             \
        LOG1("VerifyError: %s", msg);                                \
        exceptions_throw_verifyerror(STATE->m, msg);                 \
        return false;                                                \
    } while (0)

#define IS_CAT1(slot)                                                \
    ((slot).type != TYPE_VOID && !IS_2_WORD_TYPE((slot).type))

#define IS_CAT2(slot)                                                \
    ((slot).type != TYPE_VOID && IS_2_WORD_TYPE((slot).type))

#define CHECK_CAT1(slot)                                             \
    do {                                                             \
        if (!IS_CAT1(slot))                                          \
            goto throw_stack_category_error;                         \
    } while (0)

#define CHECK_CAT2(slot)                                             \
    do {                                                             \
        if (!IS_CAT2(slot))                                          \
            goto throw_stack_category_error;                         \
    } while (0)

#define COPY_SLOT(s, d)                                              \
    do { (d) = (s); } while (0)

#define REACH_BLOCK(target)                                          \
    do {                                                             \
        if (!typecheck_stackbased_reach(STATE, (target), stack,      \
                    (stack - stackfloor) + 1))                       \
            return false;                                            \
    } while (0)

#define REACH(target)                                                \
    do {                                                             \
        REACH_BLOCK((target).block);                                 \
    } while (0)

#undef TYPECHECK_INT
#undef TYPECHECK_LNG
#undef TYPECHECK_FLT
#undef TYPECHECK_DBL
#undef TYPECHECK_ADR

/* XXX should reuse typevector code */
static typecheck_result typecheck_stackbased_merge_locals(methodinfo *m,
                                                                                      typedescriptor_t *dst,
                                                                                      typedescriptor_t *y,
                                                                                      int size)
{
      bool changed = false;
      typecheck_result r;

      typedescriptor_t *a = dst;
      typedescriptor_t *b = y;
      while (size--) {
            if (a->type != TYPE_VOID && a->type != b->type) {
                  a->type = TYPE_VOID;
                  changed = true;
            }
            else if (a->type == TYPE_ADR) {
                  if (TYPEINFO_IS_PRIMITIVE(a->typeinfo)) {
                        /* 'a' is a returnAddress */
                        if (!TYPEINFO_IS_PRIMITIVE(b->typeinfo)
                              || (TYPEINFO_RETURNADDRESS(a->typeinfo)
                                    != TYPEINFO_RETURNADDRESS(b->typeinfo)))
                        {
                              a->type = TYPE_VOID;
                              changed = true;
                        }
                  }
                  else {
                        /* 'a' is a reference */
                        if (TYPEINFO_IS_PRIMITIVE(b->typeinfo)) {
                              a->type = TYPE_VOID;
                              changed = true;
                        }
                        else {
                              /* two reference types are merged. There cannot be */
                              /* a merge error. In the worst case we get j.l.O.  */
                              r = typeinfo_merge(m,&(a->typeinfo),&(b->typeinfo));
                              if (r == typecheck_FAIL)
                                    return r;
                              changed |= r;
                        }
                  }
            }
            a++;
            b++;
      }
      return changed;
}

static typecheck_result typecheck_stackbased_merge(verifier_state *state,
                                                                           basicblock *destblock,
                                                                           typedescriptor_t *stack,
                                                                           s4 stackdepth)
{
      s4 i;
      s4 destidx;
      typedescriptor_t *stackfloor;
      typedescriptor_t *sp;
      typedescriptor_t *dp;
      typecheck_result r;
      bool changed = false;

      destidx = destblock->nr;

      if (stackdepth != state->indepth[destidx]) {
            exceptions_throw_verifyerror(state->m, "Stack depth mismatch");
            return typecheck_FAIL;
      }

      stackfloor = stack - (stackdepth - 1);

      sp = stackfloor;
      dp = state->startstack + (destidx * state->m->maxstack);

      for (i=0; i<stackdepth; ++i, ++sp, ++dp) {
            if (sp->type != dp->type) {
                  exceptions_throw_verifyerror(state->m, "Mismatched stack types");
                  return typecheck_FAIL;
            }
            if (dp->type == TYPE_ADR) {
                  if (TYPEINFO_IS_PRIMITIVE(dp->typeinfo)) {
                        /* dp has returnAddress type */
                        if (TYPEINFO_IS_PRIMITIVE(sp->typeinfo)) {
                              if (TYPEINFO_RETURNADDRESS(dp->typeinfo) != TYPEINFO_RETURNADDRESS(sp->typeinfo)) {
                                    exceptions_throw_verifyerror(state->m, "Mismatched stack types");
                                    return typecheck_FAIL;
                              }
                        }
                        else {
                              exceptions_throw_verifyerror(state->m,"Merging returnAddress with reference");
                              return typecheck_FAIL;
                        }
                  }
                  else {
                        /* dp has reference type */
                        if (TYPEINFO_IS_PRIMITIVE(sp->typeinfo)) {
                              exceptions_throw_verifyerror(state->m,"Merging reference with returnAddress");
                              return typecheck_FAIL;
                        }
                        r = typeinfo_merge(state->m,&(dp->typeinfo),&(sp->typeinfo));
                        if (r == typecheck_FAIL)
                              return r;
                        changed |= r;
                  }
            }
      }

      dp = state->startlocals + (destidx * state->numlocals);
      r = typecheck_stackbased_merge_locals(state->m, dp, state->locals, state->numlocals);
      if (r == typecheck_FAIL)
            return r;
      changed |= r;

      return changed;
}

static bool typecheck_stackbased_reach(verifier_state *state,
                                                         basicblock *destblock,
                                                         typedescriptor_t *stack,
                                                         s4 stackdepth)
{
      bool changed = false;
      typecheck_result r;

      assert(destblock);

      if (destblock->flags == BBTYPECHECK_UNDEF) {
            /* The destblock has never been reached before */

            TYPECHECK_COUNT(stat_copied);
            LOG1("block L%03d reached first time",destblock->nr); LOGSTR("\t");
            DOLOG( typecheck_stackbased_show_state(state, stack, stack - (stackdepth - 1), false); );

            state->indepth[destblock->nr] = stackdepth;

            MCOPY(state->startstack + (destblock->nr * state->m->maxstack),
                    stack - (stackdepth - 1),
                    typedescriptor_t,
                    stackdepth);

            MCOPY(state->startlocals + (destblock->nr * state->numlocals),
                    state->locals,
                    typedescriptor_t,
                    state->numlocals);

            changed = true;
      }
      else {
            /* The destblock has already been reached before */

            TYPECHECK_COUNT(stat_merged);
            LOG1("block L%03d reached before", destblock->nr); LOGSTR("\t");
            DOLOG( typecheck_stackbased_show_state(state, stack, stack - (stackdepth - 1), false); );

            r = typecheck_stackbased_merge(state, destblock, stack, stackdepth);
            if (r == typecheck_FAIL)
                  return false;
            changed = r;

            TYPECHECK_COUNTIF(changed,stat_merging_changed);
      }

      if (changed) {
            LOG("\tchanged!");
            destblock->flags = BBTYPECHECK_REACHED;
            /* XXX is this check ok? */
            if (destblock->nr <= state->bptr->nr) {
                  LOG("\tREPEAT!");
                  state->repeat = true;
            }
      }
      return true;
}


/* typecheck_stackbased_verify_fieldaccess *************************************

   Verify an ICMD_{GET,PUT}{STATIC,FIELD}

   IN:
       state............the current state of the verifier
         instance.........the instance slot, or NULL
         value............the value slot, or NULL
         stack............stack after popping the arguments

   RETURN VALUE:
       stack pointer....successful verification,
         NULL.............an exception has been thrown.

*******************************************************************************/

static typedescriptor_t *typecheck_stackbased_verify_fieldaccess(
            verifier_state *state,
            typedescriptor_t *instance,
            typedescriptor_t *value,
            typedescriptor_t *stack)
{
      jitdata *jd;

      jd = state->jd;

#define TYPECHECK_STACKBASED
#define EXCEPTION  do { return NULL; } while (0)
#define STATE  state
#include <typecheck-fields.inc>
#undef  EXCEPTION
#undef  STATE
#undef  TYPECHECK_STACKBASED

      return stack;

throw_stack_overflow:
      LOG("STACK OVERFLOW!");
      exceptions_throw_verifyerror(state->m, "Stack size too large");
      return NULL;
}

static bool typecheck_stackbased_verify_invocation(verifier_state *state,
                                                                           typedescriptor_t *stack,
                                                                           typedescriptor_t *stackfloor)
{
      s4 paramslots;
      methoddesc *md;
      typedescriptor_t *dv;

      /* check stack depth */

      /* XXX parse params */

      INSTRUCTION_GET_METHODDESC(state->iptr, md);

      paramslots = md->paramslots;

      if ((stack - stackfloor) + 1 < paramslots) {
            exceptions_throw_verifyerror(state->m, 
                        "Trying to pop operand of an empty stack");
            return false;
      }

      dv = stack - (paramslots - 1);

#define TYPECHECK_STACKBASED
#define OP1   dv
#include <typecheck-invoke.inc>
#undef  OP1
#undef  TYPECHECK_STACKBASED

      return true;
}

static bool typecheck_stackbased_verify_builtin(verifier_state *state,
                                                                        typedescriptor_t *stack,
                                                                        typedescriptor_t *stackfloor)
{
      s4 paramslots;
      typedescriptor_t *dv;

      /* check stack depth */

      /* XXX parse params */

      paramslots = state->iptr->sx.s23.s3.bte->md->paramslots;

      if ((stack - stackfloor) + 1 < paramslots) {
            exceptions_throw_verifyerror(state->m, 
                        "Trying to pop operand of an empty stack");
            return false;
      }

      dv = stack - (paramslots - 1);

#define TYPECHECK_STACKBASED
#define OP1   dv
#define TYPECHECK_INT(s)  CHECK_STACK_TYPE(*(s), TYPE_INT)
#define TYPECHECK_ADR(s)  CHECK_STACK_TYPE(*(s), TYPE_ADR)
#define TYPECHECK_LNG(s)  CHECK_STACK_TYPE(*(s), TYPE_LNG)
#define TYPECHECK_FLT(s)  CHECK_STACK_TYPE(*(s), TYPE_FLT)
#define TYPECHECK_DBL(s)  CHECK_STACK_TYPE(*(s), TYPE_DBL)
#include <typecheck-builtins.inc>
#undef  OP1
#undef  TYPECHECK_STACKBASED

      return true;

throw_stack_type_error:
      exceptions_throw_verifyerror(state->m, "Wrong type on stack"); /* XXX */
      return false;
}

static bool typecheck_stackbased_multianewarray(verifier_state *state,
                                                                        typedescriptor_t *stack,
                                                                        typedescriptor_t *stackfloor)
{
      /* XXX recombine with verify_multianewarray */

      classinfo *arrayclass;
      arraydescriptor *desc;
      s4 i;
      typedescriptor_t *sp;
      typedescriptor_t *dst;

      /* destination slot */

      i = state->iptr->s1.argcount;

      dst = stack - (i-1);

      /* check the array lengths on the stack */

      if ((stack - stackfloor) + 1 < i) {
            exceptions_throw_verifyerror(state->m, 
                        "Trying to pop operand of an empty stack");
            return false;
      }

      if (i < 1)
            TYPECHECK_VERIFYERROR_bool("Illegal dimension argument");

      for (sp = dst; sp <= stack; ++sp) {
            if (sp->type != TYPE_INT) {
                  exceptions_throw_verifyerror_for_stack(state->m, TYPE_INT);
                  return false;
            }
      }

      /* 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(&(dst->typeinfo), arrayclass);
      }
      else {
            const char *p;
            constant_classref *cr;

            /* the array class reference is still unresolved */
            /* check that the reference indicates an array class of correct dimension */
            cr = 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(&(dst->typeinfo),CLASSREF_OR_CLASSINFO(cr)))
                  return false;
      }

      /* everything ok */
      return true;

}

static void typecheck_stackbased_add_jsr_caller(typecheck_jsr_t *jsr,
                                                                        basicblock *bptr)
{
      typecheck_jsr_caller_t *jc;

      for (jc = jsr->callers; jc; jc = jc->next)
            if (jc->callblock == bptr)
                  return;

      jc = DNEW(typecheck_jsr_caller_t);
      jc->next = jsr->callers;
      jc->callblock = bptr;
      jsr->callers = jc;
}

static typedescriptor_t *typecheck_stackbased_jsr(verifier_state *state,
                                                                        typedescriptor_t *stack,
                                                                        typedescriptor_t *stackfloor)
{
      typecheck_jsr_t *jsr;
      basicblock *tbptr;
      jitdata *jd;
      s4 i;

      jd = state->jd;

      tbptr = state->iptr->sx.s23.s3.jsrtarget.block;
      jsr = state->jsrinfos[tbptr->nr];

      if (jsr && tbptr->flags == BBFINISHED) {

            LOG1("another JSR to analysed subroutine L%03d", tbptr->nr);
            if (jsr->active) {
                  exceptions_throw_verifyerror(state->m, "Recursive JSR");
                  return NULL;
            }

            assert(jsr->callers);
            assert(jsr->callers->callblock);

            /* copy the stack of the RET edge */

            MCOPY(stackfloor, jsr->retstack, typedescriptor_t, jsr->retdepth);
            stack = stackfloor + (jsr->retdepth - 1);

            /* copy variables that were used in the subroutine from the RET edge */

            for (i=0; i<state->numlocals; ++i)
                  if (jsr->usedlocals[i])
                        state->locals[i] = jsr->retlocals[i];

            /* reach the following block */

            if (!typecheck_stackbased_reach(state, state->bptr->next, stack, 
                              (stack - stackfloor) + 1))
                  return NULL;
      }
      else {
            if (!jsr) {
                  LOG1("first JSR to block L%03d", tbptr->nr);

                  jsr = DNEW(typecheck_jsr_t);
                  state->jsrinfos[tbptr->nr] = jsr;
                  jsr->callers = NULL;
                  jsr->blockflags = DMNEW(char, state->basicblockcount);
                  jsr->retblock = NULL;
                  jsr->start = tbptr;
                  jsr->usedlocals = DMNEW(char, state->numlocals);
                  MZERO(jsr->usedlocals, char, state->numlocals);
                  jsr->retlocals = DMNEW(typedescriptor_t, state->numlocals);
                  jsr->retstack = DMNEW(typedescriptor_t, state->m->maxstack);
                  jsr->retdepth = 0;
            }
            else {
                  LOG1("re-analysing JSR to block L%03d", tbptr->nr);
            }

            jsr->active = true;
            jsr->next = state->topjsr;
            state->topjsr = jsr;

            assert(state->iptr->sx.s23.s3.jsrtarget.block->flags == BBTYPECHECK_REACHED);

            tbptr->flags = BBFINISHED;

            for (tbptr = state->basicblocks; tbptr != NULL; tbptr = tbptr->next) {
                  jsr->blockflags[tbptr->nr] = tbptr->flags;

                  if (tbptr->flags == BBTYPECHECK_REACHED)
                        tbptr->flags = BBFINISHED;
            }

            state->iptr->sx.s23.s3.jsrtarget.block->flags = BBTYPECHECK_REACHED;
      }

      /* register this block as a caller, if not already done */

      typecheck_stackbased_add_jsr_caller(jsr, state->bptr);

      return stack;
}

static bool typecheck_stackbased_ret(verifier_state *state,
                                                       typedescriptor_t *stack,
                                                       typedescriptor_t *stackfloor)
{
      basicblock *tbptr;
      typecheck_jsr_caller_t *jsrcaller;
      typecheck_jsr_t *jsr;
      s4 i;

      /* get the subroutine we are RETurning from */

      tbptr = TYPEINFO_RETURNADDRESS(state->locals[state->iptr->s1.varindex].typeinfo);
      if (tbptr == NULL) {
            exceptions_throw_verifyerror(state->m, "Illegal RET");
            return false;
      }

      LOG1("RET from subroutine L%03d", tbptr->nr);
      jsr = state->jsrinfos[tbptr->nr];
      assert(jsr);

      /* check against recursion */

      if (!jsr->active) {
            exceptions_throw_verifyerror(state->m, "RET outside of local subroutine");
            return false;
      }

      /* check against multiple RETs for one subroutine */

      if (jsr->retblock && jsr->retblock != state->bptr) {
            exceptions_throw_verifyerror(state->m, "Multiple RETs from local subroutine");
            return false;
      }

      /* store data-flow of the RET edge */

      jsr->retblock = state->bptr;
      jsr->retdepth = (stack - stackfloor) + 1;
      MCOPY(jsr->retstack, stackfloor, typedescriptor_t, jsr->retdepth);
      MCOPY(jsr->retlocals, state->locals, typedescriptor_t, state->numlocals);

      /* invalidate the returnAddress used by this RET */
      /* XXX should we also invalidate the returnAddresses of JSRs that are skipped by this RET? */

      for (i=0; i<state->numlocals; ++i) {
            typedescriptor_t *lc = &(jsr->retlocals[i]);
            if (TYPE_IS_RETURNADDRESS(lc->type, lc->typeinfo))
                  if (TYPEINFO_RETURNADDRESS(lc->typeinfo) == tbptr) {
                        LOG1("invalidating returnAddress in local %d", i);
                        TYPEINFO_INIT_RETURNADDRESS(lc->typeinfo, NULL);
                  }
      }

      /* touch all callers of the subroutine, so they are analysed again */

      for (jsrcaller = jsr->callers; jsrcaller != NULL; jsrcaller = jsrcaller->next) {
            tbptr = jsrcaller->callblock;
            LOG1("touching caller L%03d from RET", tbptr->nr);
            assert(jsr->blockflags[tbptr->nr] >= BBFINISHED);
            jsr->blockflags[tbptr->nr] = BBTYPECHECK_REACHED; /* XXX repeat? */
      }

      return true;
}

bool typecheck_stackbased(jitdata *jd)
{
      register verifier_slot_t *stack;
      verifier_slot_t *stackfloor;
      s4 len;
      methoddesc *md;
      bool maythrow;
      bool superblockend;
      verifier_slot_t temp;
      branch_target_t *table;
      lookup_target_t *lookup;
      s4 i;
      typecheck_result r;
      verifier_slot_t *dst;
      verifier_state state;
      basicblock *tbptr;
      exception_entry *ex;
      typedescriptor_t exstack;
      s4 skip = 0;

      DOLOG( show_method(jd, SHOW_PARSE); );

      /* initialize verifier state */

      state.jd = jd;
      state.m = jd->m;
      state.cd = jd->cd;
      state.basicblocks = jd->basicblocks;
      state.basicblockcount = jd->basicblockcount;
      state.topjsr = NULL;
#   define STATE (&state)

      /* check that the basicblock numbers are valid */

#if !defined(NDEBUG)
      jit_check_basicblock_numbers(jd);
#endif

      /* check if this method is an instance initializer method */

    state.initmethod = (state.m->name == utf_init);

      /* allocate parameter descriptors if necessary */

      if (!state.m->parseddesc->params)
            if (!descriptor_params_from_paramtypes(state.m->parseddesc,state.m->flags))
                  return false;

      /* allocate the stack buffers */

      stackfloor = DMNEW(verifier_slot_t, state.m->maxstack + 1);
      state.stackceiling = stackfloor + state.m->maxstack;
      stack = stackfloor - 1;
      state.indepth = DMNEW(s4, state.basicblockcount);
      state.startstack = DMNEW(verifier_slot_t, state.m->maxstack * state.basicblockcount);

      /* allocate the local variables buffers */

      state.numlocals = state.m->maxlocals;
      state.validlocals = state.m->maxlocals;
    if (state.initmethod)
            state.numlocals++; /* extra marker variable */

      state.locals = DMNEW(verifier_slot_t, state.numlocals);
      state.startlocals = DMNEW(verifier_slot_t, state.numlocals * state.basicblockcount);

    /* allocate the buffer of active exception handlers */

    state.handlers = DMNEW(exception_entry*, state.jd->exceptiontablelength + 1);

    /* initialize instack of exception handlers */

      exstack.type = TYPE_ADR;
      typeinfo_init_classinfo(&(exstack.typeinfo),
                                          class_java_lang_Throwable); /* changed later */

    LOG("Exception handler stacks set.\n");

      /* initialize jsr info buffer */

      state.jsrinfos = DMNEW(typecheck_jsr_t *, state.basicblockcount);
      MZERO(state.jsrinfos, typecheck_jsr_t *, state.basicblockcount);

      /* initialize stack of first block */

      state.indepth[0] = 0;

      /* initialize locals of first block */

    /* if this is an instance method initialize the "this" ref type */

    if (!(state.m->flags & ACC_STATIC)) {
            if (state.validlocals < 1)
                  VERIFY_ERROR("Not enough local variables for method arguments");
            dst = state.startlocals;
            dst->type = TYPE_ADR;
            if (state.initmethod)
                  TYPEINFO_INIT_NEWOBJECT(dst->typeinfo, NULL);
            else
                  typeinfo_init_classinfo(&(dst->typeinfo), state.m->clazz);

            skip = 1;
    }

    LOG("'this' argument set.\n");

      len = typedescriptors_init_from_methoddesc(state.startlocals + skip,
                  state.m->parseddesc,
                  state.validlocals, true, skip, &state.returntype);
      if (len < 0)
            return false;

      /* set remaining locals to void */

      for (i = skip + len; i<state.numlocals; ++i)
            state.startlocals[i].type = TYPE_VOID;

      /* initialize block flags */

      typecheck_init_flags(&state, BBUNDEF);

      /* iterate until fixpoint reached */

      do {

            state.repeat = false;

            /* iterate over the basic blocks */

            for (state.bptr = state.basicblocks; state.bptr != NULL; state.bptr = state.bptr->next) {

                  if (state.bptr->flags != BBTYPECHECK_REACHED)
                        continue;

                  DOLOG( show_basicblock(jd, state.bptr, SHOW_PARSE); );

                  /* mark this block as analysed */

                  state.bptr->flags = BBFINISHED;

                  /* determine the active exception handlers for this block */
                  /* XXX could use a faster algorithm with sorted lists or  */
                  /* something?                                             */
                  /* XXX reuse code from variables based verifer? */
                  len = 0;
                  for (ex = STATE->jd->exceptiontable; ex ; ex = ex->down) {
                        if ((ex->start->nr <= STATE->bptr->nr) && (ex->end->nr > STATE->bptr->nr)) {
                              LOG1("\tactive handler L%03d", ex->handler->nr);
                              STATE->handlers[len++] = ex;
                        }
                  }
                  STATE->handlers[len] = NULL;

                  /* initialize the locals */

                  MCOPY(state.locals,
                          state.startlocals + (state.bptr->nr * state.numlocals),
                          verifier_slot_t, state.numlocals);

                  /* initialize the stack */

                  len = state.indepth[state.bptr->nr];

                  MCOPY(stackfloor,
                          state.startstack + (state.bptr->nr * state.m->maxstack),
                          verifier_slot_t, len);

                  stack = stackfloor + (len - 1);

                  /* iterate over the instructions in this block */

                  state.iptr = state.bptr->iinstr;
                  len = state.bptr->icount;

                  superblockend = false;

                  for (; len--; state.iptr++) {

                        maythrow = false;

                        LOGNL;
                        DOLOG( typecheck_stackbased_show_state(&state, stack, stackfloor, true); );

                        switch (state.iptr->opc) {
#define TYPECHECK_STACKBASED 1
#define STATE (&state)
#define IPTR state.iptr
#define BPTR state.bptr
#define METHOD state.m
#define LOCAL_SLOT(index)  (state.locals + (index))
#define EXCEPTION                                                    \
    do {                                                             \
        LOG("EXCEPTION THROWN!\n");                                  \
        return false;                                                \
    } while (0)

#include <typecheck-stackbased-gen.inc>
#undef  TYPECHECK_STACKBASED
                        }

                        /* reach exception handlers for this instruction */

                        if (maythrow) {
                              TYPECHECK_COUNT(stat_ins_maythrow);
                              TYPECHECK_MARK(STATE->stat_maythrow);
                              LOG("\treaching exception handlers");
                              i = 0;
                              while (STATE->handlers[i]) {
                                    TYPECHECK_COUNT(stat_handlers_reached);
                                    if (STATE->handlers[i]->catchtype.any)
                                          exstack.typeinfo.typeclass = STATE->handlers[i]->catchtype;
                                    else
                                          exstack.typeinfo.typeclass.cls = class_java_lang_Throwable;
                                    if (!typecheck_stackbased_reach(
                                                STATE,
                                                STATE->handlers[i]->handler,
                                                &exstack, 1))
                                          EXCEPTION;
                                    i++;
                              }
                        }
                  }

                  /* propagate types to the following block */

                  if (!superblockend) {
                        if (!typecheck_stackbased_reach(&state, state.bptr->next,
                                                                        stack, stack - stackfloor + 1))
                              EXCEPTION;
                  }
            } /* end loop over blocks */

            while (!state.repeat && state.topjsr) {
                  LOG1("done analysing subroutine L%03d", state.topjsr->start->nr);

                  /* propagate down used locals */

                  if (state.topjsr->next) {
                        for (i=0; i<state.numlocals; ++i)
                              state.topjsr->next->usedlocals[i] |= state.topjsr->usedlocals[i];
                  }

                  /* restore REACHED flags */

                  for (tbptr = state.basicblocks; tbptr != NULL; tbptr = tbptr->next) {
                        assert(tbptr->flags != BBTYPECHECK_REACHED);
                        if (state.topjsr->blockflags[tbptr->nr] == BBTYPECHECK_REACHED) {
                              tbptr->flags = BBTYPECHECK_REACHED;
                              state.repeat = true;
                        }
                  }

                  /* dactivate the subroutine */

                  state.topjsr->active = false;
                  state.topjsr = state.topjsr->next;
            }
      } while (state.repeat);

      /* reset block flags */

      typecheck_reset_flags(&state);

      LOG("typecheck_stackbased successful");

      return true;

throw_stack_underflow:
      LOG("STACK UNDERFLOW!");
      exceptions_throw_verifyerror(state.m, "Unable to pop operand off an empty stack");
      return false;

throw_stack_overflow:
      LOG("STACK OVERFLOW!");
      exceptions_throw_verifyerror(state.m, "Stack size too large");
      return false;

throw_stack_type_error:
      LOG("STACK TYPE ERROR!");
      exceptions_throw_verifyerror(state.m, "Mismatched stack types");
      return false;

throw_local_type_error:
      LOG("LOCAL TYPE ERROR!");
      exceptions_throw_verifyerror(state.m, "Local variable type mismatch");
      return false;

throw_stack_category_error:
      LOG("STACK CATEGORY ERROR!");
      exceptions_throw_verifyerror(state.m, "Attempt to split long or double on the stack");
      return false;
}


#if defined(TYPECHECK_VERBOSE)
static void typecheck_stackbased_show_state(verifier_state *state,
                                                                  typedescriptor_t *stack,
                                                                  typedescriptor_t *stackfloor,
                                                                  bool showins)
{
      typedescriptor_t *sp;
      s4 i;

      LOGSTR1("stackdepth %d stack [", (stack - stackfloor) + 1);
      for (sp=stackfloor; sp <= stack; sp++) {
            LOGSTR(" ");
            DOLOG( typedescriptor_print(stdout, sp); );
      }
      LOGSTR(" ] locals [");
      for (i=0; i<state->numlocals; ++i) {
            LOGSTR(" ");
            DOLOG( typedescriptor_print(stdout, state->locals + i); );
      }
      LOGSTR(" ]");
      LOGNL;
      if (showins && state->iptr < (state->bptr->iinstr + state->bptr->icount)) {
            LOGSTR("\t");
            DOLOG( show_icmd(state->jd, state->iptr, false, SHOW_PARSE); );
            LOGNL;
      }
}
#endif

#endif /* defined(ENABLE_VERIFIER) */


/*
 * These are local overrides for various environment variables in Emacs.
 * Please do not remove this and leave it at the end of the file, where
 * Emacs will automagically detect them.
 * ---------------------------------------------------------------------
 * Local variables:
 * mode: c
 * indent-tabs-mode: t
 * c-basic-offset: 4
 * tab-width: 4
 * End:
 * vim:noexpandtab:sw=4:ts=4:
 */


Generated by  Doxygen 1.6.0   Back to index