/* src/vm/jit/verify/typecheck-invoke.inc - type checking for invocations Copyright (C) 1996-2011 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. */ { unresolved_method *um; /* struct describing the called method */ constant_FMIref *mref; /* reference to the called method */ methoddesc *md; /* descriptor of the called method */ utf *mname; /* method name */ u1 rtype; /* return type of called method */ #if !defined(TYPECHECK_TYPEINFERER) methodinfo *mi; /* resolved method (if any) */ utf *mclassname; /* name of the method's class */ bool specialmethod; /* true if a <...> method is called */ int opcode; /* invocation opcode */ bool callinginit; /* true if is called */ instruction *ins; classref_or_classinfo initclass; typedesc *td; #if defined(TYPECHECK_VARIABLESBASED) s4 argindex; /* argument variable index */ varinfo *av; /* argument variable */ #else typedescriptor_t *av; /* argument stack slot */ #endif int i; /* counter */ resolve_result_t result; bool invokestatic; bool invokespecial; #endif /* !defined(TYPECHECK_TYPEINFERER) */ /* 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 */ md = mref->parseddesc.md; mname = mref->name; #if !defined(TYPECHECK_TYPEINFERER) /* get method info (if resolved) and classname */ if (IS_FMIREF_RESOLVED(mref)) { mi = mref->p.method; mclassname = mi->clazz->name; } else { mi = NULL; mclassname = mref->p.classref->name; } /* gather some info about this instruction */ opcode = state->iptr->opc; invokestatic = (opcode == ICMD_INVOKESTATIC); invokespecial = (opcode == ICMD_INVOKESPECIAL); specialmethod = (mname->text[0] == '<'); /* prevent compiler warnings */ ins = NULL; /* check whether we are calling */ callinginit = (invokespecial && mname == utf_init); if (specialmethod && !callinginit) TYPECHECK_VERIFYERROR_bool("Invalid invocation of special method"); /* allocate parameters if necessary */ if (!md->params) descriptor_params_from_paramtypes( md, (invokestatic) ? ACC_STATIC : ACC_NONE); /* check parameter types */ #if defined(TYPECHECK_STACKBASED) av = stack - (md->paramslots - 1); for (i=0; iparamcount; ++i) { #else i = md->paramcount; /* number of parameters including 'this'*/ while (--i >= 0) { argindex = state->iptr->sx.s23.s2.args[i]; av = VAR(argindex); #endif LOG1("\t\tparam %d",i); td = md->paramtypes + i; if (av->type != td->type) TYPECHECK_VERIFYERROR_bool("Parameter type mismatch in method invocation"); if (av->type == TYPE_ADR) { LOGSTR("\t\t"); LOGINFO(&(av->typeinfo)); if (i==0 && callinginit) { /* first argument to method */ if (!TYPEINFO_IS_NEWOBJECT(av->typeinfo)) TYPECHECK_VERIFYERROR_bool("Calling on initialized object"); /* get the address of the NEW instruction */ LOGSTR("\t\t"); LOGINFO(&(av->typeinfo)); ins = (instruction *) TYPEINFO_NEWOBJECT_INSTRUCTION(av->typeinfo); if (ins) initclass = ins[-1].sx.val.c; else initclass.cls = state->m->clazz; LOGSTR("\t\tclass: "); 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 && !invokestatic) { TYPECHECK_VERIFYERROR_bool("Parameter type mismatch for 'this' argument"); } } LOG("\t\tok"); #if defined(TYPECHECK_STACKBASED) av += (IS_2_WORD_TYPE(av->type)) ? 2 : 1; #endif } if (callinginit) { LOG("\treplacing uninitialized object"); /* replace uninitialized object type on stack */ /* for all live-in and live-through variables */ #if defined(TYPECHECK_VARIABLESBASED) for (i=0; iiptr->s1.argcount; ++i) { argindex = state->iptr->sx.s23.s2.args[i]; av = VAR(argindex); #else for (av=stackfloor; av <= stack; ++av) { #endif if (av->type == TYPE_ADR && TYPEINFO_IS_NEWOBJECT(av->typeinfo) && TYPEINFO_NEWOBJECT_INSTRUCTION(av->typeinfo) == ins) { LOG("\treplacing uninitialized type"); #if defined(TYPECHECK_VARIABLESBASED) /* If this stackslot is in the instack of * this basic block we must save the type(s) * we are going to replace. */ /* XXX this needs a new check */ if (state->bptr->invars && argindex >= state->bptr->invars[0] && argindex < state->bptr->varstart && !state->savedinvars) { typestate_save_invars(state); } #endif /* defined(TYPECHECK_VARIABLESBASED) */ if (!typeinfo_init_class(&(av->typeinfo),initclass)) return false; } } /* replace uninitialized object type in locals */ #if defined(TYPECHECK_VARIABLESBASED) if (!typevector_init_object(state->jd->var, ins, initclass, state->numlocals)) return false; #else /* XXX should reuse typevector code */ for (i=0; inumlocals; ++i) { if (state->locals[i].type == TYPE_ADR && TYPEINFO_IS_NEWOBJECT(state->locals[i].typeinfo) && TYPEINFO_NEWOBJECT_INSTRUCTION(state->locals[i].typeinfo) == ins) { if (!typeinfo_init_class(&(state->locals[i].typeinfo), initclass)) return false; } } #endif /* initializing the 'this' reference? */ if (!ins) { classinfo *cls; TYPECHECK_ASSERT(state->initmethod); /* { we are initializing the 'this' reference } */ /* must be 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 (mi) { cls = mi->clazz; } 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 */ if (cls == NULL || (cls != state->m->clazz && cls != state->m->clazz->super)) { TYPECHECK_VERIFYERROR_bool(" calling of the wrong class"); } /* set our marker variable to type int */ LOG("\tsetting marker"); #if defined(TYPECHECK_VARIABLESBASED) typevector_store(jd->var, state->numlocals-1, TYPE_INT, NULL); #else state->locals[state->numlocals-1].type = TYPE_INT; #endif } else { /* { we are initializing an instance created with NEW } */ if ((IS_CLASSREF(initclass) ? initclass.ref->name : initclass.cls->name) != mclassname) { TYPECHECK_VERIFYERROR_bool("wrong called for uninitialized reference"); } } } /* end if (callinginit) */ /* try to resolve the method lazily */ result = resolve_method_lazy(state->m, mref, invokespecial); /* perform verification checks */ if (result == resolveSucceeded) { assert(IS_FMIREF_RESOLVED(mref)); mi = mref->p.method; result = resolve_method_verifier_checks(state->m, mref, mi, invokestatic); } /* check types of parameters */ if (result == resolveSucceeded && !invokestatic) result = resolve_method_instance_type_checks( state->m, mi, &(OP1->typeinfo), invokespecial); #if defined(TYPECHECK_VARIABLESBASED) if (result == resolveSucceeded) result = resolve_method_param_type_checks( jd, state->m, state->iptr, mi, invokestatic); #else if (result == resolveSucceeded) result = resolve_method_param_type_checks_stackbased( state->m, mi, invokestatic, stack); #endif /* impose loading constraints */ if (result == resolveSucceeded) { /* XXX state->m->clazz may have to be wrong when inlining */ if (!resolve_method_loading_constraints(state->m->clazz, mi)) return false; } 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; state->iptr->flags.bits |= INS_FLAG_CHECK; } } else { /* resolution must be deferred */ if (!um) { um = resolve_create_unresolved_method(state->m->clazz, state->m, mref, invokestatic, invokespecial); if (!um) return false; } /* record subtype constraints for parameters */ if (!invokestatic && !resolve_constrain_unresolved_method_instance( um, state->m, &(OP1->typeinfo), invokespecial)) return false; /* XXX maybe wrap exception */ #if defined(TYPECHECK_VARIABLESBASED) if (!resolve_constrain_unresolved_method_params( jd, um, state->m, state->iptr)) return false; /* XXX maybe wrap exception */ #else if (!resolve_constrain_unresolved_method_params_stackbased( um, state->m, stack)) return false; /* XXX maybe wrap exception */ #endif /* store the unresolved_method pointer */ state->iptr->sx.s23.s3.um = um; state->iptr->flags.bits |= INS_FLAG_UNRESOLVED; } #endif /* !defined(TYPECHECK_TYPEINFERER) */ /* set the return type */ rtype = md->returntype.type; if (rtype != TYPE_VOID) { dv->type = rtype; if (!typeinfo_init_from_typedesc(&(md->returntype), NULL, &(dv->typeinfo))) return false; } } /* * 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:filetype=c: */