From: Stefan Ring Date: Wed, 26 Nov 2008 10:43:56 +0000 (+0100) Subject: Merge from subtype. X-Git-Url: http://wien.tomnetworks.com/gitweb/?p=cacao.git;a=commitdiff_plain;h=3a16b0602c73972b42f6db5280092e57e38b4287 Merge from subtype. --HG-- branch : subtype-trunk rename : src/vmcore/linker.c => src/vm/linker.cpp --- 3a16b0602c73972b42f6db5280092e57e38b4287 diff --cc src/vm/linker.cpp index a29ffaa86,000000000..50639b736 mode 100644,000000..100644 --- a/src/vm/linker.cpp +++ b/src/vm/linker.cpp @@@ -1,1408 -1,0 +1,1412 @@@ +/* src/vm/linker.c - class linker functions + + 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 +#include + +#include "vm/types.h" + +#include "mm/memory.h" + +#include "native/native.hpp" + +#include "threads/lock.hpp" +#include "threads/mutex.hpp" + +#include "toolbox/logging.h" + +#include "vm/access.hpp" +#include "vm/array.hpp" +#include "vm/class.hpp" +#include "vm/classcache.hpp" +#include "vm/exceptions.hpp" +#include "vm/globals.hpp" +#include "vm/loader.hpp" +#include "vm/options.h" +#include "vm/primitive.hpp" +#include "vm/rt-timing.h" +#include "vm/string.hpp" +#include "vm/vm.hpp" + +#include "vm/jit/asmpart.h" +#include "vm/jit/stubs.hpp" + + +/* debugging macros ***********************************************************/ + +#if !defined(NDEBUG) +# define TRACELINKCLASS(c) \ + do { \ + if (opt_TraceLinkClass) { \ + log_start(); \ + log_print("[Linking "); \ + class_print((c)); \ + log_print("]"); \ + log_finish(); \ + } \ + } while (0) +#else +# define TRACELINKCLASS(c) +#endif + + +/* #include "vm/resolve.hpp" */ +/* copied prototype to avoid bootstrapping problem: */ +classinfo *resolve_classref_or_classinfo_eager(classref_or_classinfo cls, bool checkaccess); + +#if defined(ENABLE_STATISTICS) +# include "vm/statistics.h" +#endif + +#if !defined(NDEBUG) && defined(ENABLE_INLINING) +#define INLINELOG(code) do { if (opt_TraceInlining) { code } } while (0) +#else +#define INLINELOG(code) +#endif + + +/* global variables ***********************************************************/ + +static s4 interfaceindex; /* sequential numbering of interfaces */ +static s4 classvalue; + +#if !USES_NEW_SUBTYPE +Mutex *linker_classrenumber_lock; +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +/* private functions **********************************************************/ + +static classinfo *link_class_intern(classinfo *c); +static arraydescriptor *link_array(classinfo *c); +static void linker_compute_class_values(classinfo *c); +static void linker_compute_subclasses(classinfo *c); +static bool linker_addinterface(classinfo *c, classinfo *ic); +static s4 class_highestinterface(classinfo *c); + + +/* linker_init ***************************************************************** + + Initializes the linker subsystem and links classes required for the + primitive table. + +*******************************************************************************/ + +void linker_preinit(void) +{ + TRACESUBSYSTEMINITIALIZATION("linker_preinit"); + + /* Reset interface index. */ + + interfaceindex = 0; + +#if defined(ENABLE_THREADS) && !USES_NEW_SUBTYPE + /* create the global mutex */ + + linker_classrenumber_lock = new Mutex(); +#endif + + /* Link the most basic classes. */ + + if (!link_class(class_java_lang_Object)) + vm_abort("linker_preinit: linking java/lang/Object failed"); + +#if defined(ENABLE_JAVASE) + if (!link_class(class_java_lang_Cloneable)) + vm_abort("linker_preinit: linking java/lang/Cloneable failed"); + + if (!link_class(class_java_io_Serializable)) + vm_abort("linker_preinit: linking java/io/Serializable failed"); +#endif +} + + +/* linker_init ***************************************************************** + + Links all classes required in the VM. + +*******************************************************************************/ + +void linker_init(void) +{ + TRACESUBSYSTEMINITIALIZATION("linker_init"); + + /* Link java.lang.Class as first class of the system, because we + need it's vftbl for all other classes so we can use a class as + object. */ + + if (!link_class(class_java_lang_Class)) + vm_abort("linker_init: linking java/lang/Class failed"); + + /* Now set the header.vftbl of all classes which were created + before java.lang.Class was linked. */ + + class_postset_header_vftbl(); + + /* Link primitive-type wrapping classes. */ + +#if defined(ENABLE_JAVASE) + if (!link_class(class_java_lang_Void)) + vm_abort("linker_init: linking failed"); +#endif + + if (!link_class(class_java_lang_Boolean)) + vm_abort("linker_init: linking failed"); + + if (!link_class(class_java_lang_Byte)) + vm_abort("linker_init: linking failed"); + + if (!link_class(class_java_lang_Character)) + vm_abort("linker_init: linking failed"); + + if (!link_class(class_java_lang_Short)) + vm_abort("linker_init: linking failed"); + + if (!link_class(class_java_lang_Integer)) + vm_abort("linker_init: linking failed"); + + if (!link_class(class_java_lang_Long)) + vm_abort("linker_init: linking failed"); + + if (!link_class(class_java_lang_Float)) + vm_abort("linker_init: linking failed"); + + if (!link_class(class_java_lang_Double)) + vm_abort("linker_init: linking failed"); + + /* Link important system classes. */ + + if (!link_class(class_java_lang_String)) + vm_abort("linker_init: linking java/lang/String failed"); + +#if defined(ENABLE_JAVASE) + if (!link_class(class_java_lang_ClassLoader)) + vm_abort("linker_init: linking failed"); + + if (!link_class(class_java_lang_SecurityManager)) + vm_abort("linker_init: linking failed"); +#endif + + if (!link_class(class_java_lang_System)) + vm_abort("linker_init: linking failed"); + + if (!link_class(class_java_lang_Thread)) + vm_abort("linker_init: linking failed"); + +#if defined(ENABLE_JAVASE) + if (!link_class(class_java_lang_ThreadGroup)) + vm_abort("linker_init: linking failed"); +#endif + + if (!link_class(class_java_lang_Throwable)) + vm_abort("linker_init: linking failed"); + +#if defined(WITH_JAVA_RUNTIME_LIBRARY_GNU_CLASSPATH) + if (!link_class(class_java_lang_VMSystem)) + vm_abort("linker_init: linking failed"); + + if (!link_class(class_java_lang_VMThread)) + vm_abort("linker_init: linking failed"); + + if (!link_class(class_java_lang_VMThrowable)) + vm_abort("linker_init: linking failed"); +#endif + + /* Important system exceptions. */ + + if (!link_class(class_java_lang_Exception)) + vm_abort("linker_init: linking failed"); + + if (!link_class(class_java_lang_ClassNotFoundException)) + vm_abort("linker_init: linking failed"); + + if (!link_class(class_java_lang_RuntimeException)) + vm_abort("linker_init: linking failed"); + + /* some classes which may be used more often */ + +#if defined(ENABLE_JAVASE) + if (!link_class(class_java_lang_StackTraceElement)) + vm_abort("linker_init: linking failed"); + + if (!link_class(class_java_lang_reflect_Constructor)) + vm_abort("linker_init: linking failed"); + + if (!link_class(class_java_lang_reflect_Field)) + vm_abort("linker_init: linking failed"); + + if (!link_class(class_java_lang_reflect_Method)) + vm_abort("linker_init: linking failed"); + +# if defined(WITH_JAVA_RUNTIME_LIBRARY_GNU_CLASSPATH) + if (!link_class(class_java_lang_reflect_VMConstructor)) + vm_abort("linker_init: linking failed"); + + if (!link_class(class_java_lang_reflect_VMField)) + vm_abort("linker_init: linking failed"); + + if (!link_class(class_java_lang_reflect_VMMethod)) + vm_abort("linker_init: linking failed"); +# endif + + if (!link_class(class_java_security_PrivilegedAction)) + vm_abort("linker_init: linking failed"); + + if (!link_class(class_java_util_Vector)) + vm_abort("linker_init: linking failed"); + + if (!link_class(class_java_util_HashMap)) + vm_abort("linker_init: linking failed"); + +# if defined(WITH_JAVA_RUNTIME_LIBRARY_OPENJDK) + if (!link_class(class_sun_misc_Signal)) + vm_abort("linker_init: linking failed"); + + if (!link_class(class_sun_reflect_MagicAccessorImpl)) + vm_abort("linker_init: linking failed"); +# endif + + if (!link_class(arrayclass_java_lang_Object)) + vm_abort("linker_init: linking failed"); +#endif + + + /* create pseudo classes used by the typechecker */ + + /* pseudo class for Arraystubs (extends java.lang.Object) */ + + pseudo_class_Arraystub = + class_create_classinfo(utf_new_char("$ARRAYSTUB$")); + pseudo_class_Arraystub->state |= CLASS_LOADED; + pseudo_class_Arraystub->super = class_java_lang_Object; + +#if defined(ENABLE_JAVASE) + + pseudo_class_Arraystub->interfacescount = 2; + pseudo_class_Arraystub->interfaces = MNEW(classinfo*, 2); + pseudo_class_Arraystub->interfaces[0] = class_java_lang_Cloneable; + pseudo_class_Arraystub->interfaces[1] = class_java_io_Serializable; + +#elif defined(ENABLE_JAVAME_CLDC1_1) + + pseudo_class_Arraystub->interfacescount = 0; + pseudo_class_Arraystub->interfaces = NULL; + +#else +# error unknown Java configuration +#endif + + if (!classcache_store_unique(pseudo_class_Arraystub)) + vm_abort("linker_init: could not cache pseudo_class_Arraystub"); + + if (!link_class(pseudo_class_Arraystub)) + vm_abort("linker_init: linking pseudo_class_Arraystub failed"); + + /* pseudo class representing the null type */ + + pseudo_class_Null = class_create_classinfo(utf_new_char("$NULL$")); + pseudo_class_Null->state |= CLASS_LOADED; + pseudo_class_Null->super = class_java_lang_Object; + + if (!classcache_store_unique(pseudo_class_Null)) + vm_abort("linker_init: could not cache pseudo_class_Null"); + + if (!link_class(pseudo_class_Null)) + vm_abort("linker_init: linking failed"); + + /* pseudo class representing new uninitialized objects */ + + pseudo_class_New = class_create_classinfo(utf_new_char("$NEW$")); + pseudo_class_New->state |= CLASS_LOADED; + pseudo_class_New->state |= CLASS_LINKED; /* XXX is this allright? */ + pseudo_class_New->super = class_java_lang_Object; + + if (!classcache_store_unique(pseudo_class_New)) + vm_abort("linker_init: could not cache pseudo_class_New"); + + /* Correct vftbl-entries (retarded loading and linking of class + java/lang/String). */ + + stringtable_update(); +} + + +/* link_class ****************************************************************** + + Wrapper function for link_class_intern to ease monitor enter/exit + and exception handling. + +*******************************************************************************/ + +classinfo *link_class(classinfo *c) +{ + classinfo *r; +#if defined(ENABLE_RT_TIMING) + struct timespec time_start, time_end; +#endif + + RT_TIMING_GET_TIME(time_start); + + if (c == NULL) { + exceptions_throw_nullpointerexception(); + return NULL; + } + + LOCK_MONITOR_ENTER(c); + + /* Maybe the class is currently linking or is already linked.*/ + + if ((c->state & CLASS_LINKING) || (c->state & CLASS_LINKED)) { + LOCK_MONITOR_EXIT(c); + + return c; + } + +#if defined(ENABLE_STATISTICS) + /* measure time */ + + if (opt_getcompilingtime) + compilingtime_stop(); + + if (opt_getloadingtime) + loadingtime_start(); +#endif + + /* call the internal function */ + + r = link_class_intern(c); + + /* If return value is NULL, we had a problem and the class is not + linked. */ + + if (r == NULL) + c->state &= ~CLASS_LINKING; + +#if defined(ENABLE_STATISTICS) + /* measure time */ + + if (opt_getloadingtime) + loadingtime_stop(); + + if (opt_getcompilingtime) + compilingtime_start(); +#endif + + LOCK_MONITOR_EXIT(c); + + RT_TIMING_GET_TIME(time_end); + + RT_TIMING_TIME_DIFF(time_start,time_end,RT_TIMING_LINK_TOTAL); + + return r; +} + + +/* linker_overwrite_method ***************************************************** + + Overwrite a method with another one, update method flags and check + assumptions. + + IN: + mg................the general method being overwritten + ms................the overwriting (more specialized) method + wl................worklist where to add invalidated methods + + RETURN VALUE: + true..............everything ok + false.............an exception has been thrown + +*******************************************************************************/ + +static bool linker_overwrite_method(methodinfo *mg, + methodinfo *ms, + method_worklist **wl) +{ + classinfo *cg; + classinfo *cs; + + cg = mg->clazz; + cs = ms->clazz; + + /* overriding a final method is illegal */ + + if (mg->flags & ACC_FINAL) { + exceptions_throw_verifyerror(mg, "Overriding final method"); + return false; + } + + /* method ms overwrites method mg */ + +#if defined(ENABLE_VERIFIER) + /* Add loading constraints (for the more general types of method mg). */ + /* Not for , as it is not invoked virtually. */ + + if ((ms->name != utf_init) + && !classcache_add_constraints_for_params( + cs->classloader, cg->classloader, mg)) + { + return false; + } +#endif + + /* inherit the vftbl index, and record the overwriting */ + + ms->vftblindex = mg->vftblindex; + ms->overwrites = mg; + + /* update flags and check assumptions */ + /* methods are a special case, as they are never dispatched dynamically */ + + if ((ms->flags & ACC_METHOD_IMPLEMENTED) && ms->name != utf_init) { + do { + +#if defined(ENABLE_TLH) + if (mg->flags & ACC_METHOD_MONOMORPHY_USED) { + printf("%s/%s is evil! the siner is %s/%s\n", mg->clazz->name->text, mg->name->text, + ms->clazz->name->text, ms->name->text); + ms->flags |= ACC_METHOD_PARENT_MONOMORPHY_USED; + } +#endif + + if (mg->flags & ACC_METHOD_IMPLEMENTED) { + /* this adds another implementation */ + + mg->flags &= ~ACC_METHOD_MONOMORPHIC; + + INLINELOG( printf("becomes polymorphic: "); method_println(mg); ); + + method_break_assumption_monomorphic(mg, wl); + } + else { + /* this is the first implementation */ + + mg->flags |= ACC_METHOD_IMPLEMENTED; + + INLINELOG( printf("becomes implemented: "); method_println(mg); ); + } + + ms = mg; + mg = mg->overwrites; + } while (mg != NULL); + } + + return true; +} + + +#if USES_NEW_SUBTYPE +/* build_display *************************************************************** + + Builds the entire display for a class. This entails filling the fixed part + as well as allocating and initializing the overflow part. + + See Cliff Click and John Rose: Fast subtype checking in the Hotspot JVM. + +*******************************************************************************/ + - static void build_display(classinfo *c) ++static classinfo *build_display(classinfo *c) +{ + int depth, i; + int depth_fixed; + classinfo *super; + + do { + /* Handle arrays. */ + if (c->vftbl->arraydesc) { + arraydescriptor *a = c->vftbl->arraydesc; + if (a->elementvftbl && a->elementvftbl->clazz->super) { + classinfo *cls = a->elementvftbl->clazz->super; + int n; + for (n=0; ndimension; n++) + cls = class_array_of(cls, true); + super = cls; + break; + } + if (a->componentvftbl && a->elementvftbl) { + super = a->componentvftbl->clazz; + break; + } + } + /* Normal classes. */ + super = c->super; + } while (false); + if (super) { - build_display(super); ++ if (!link_class(super)) ++ return NULL; + depth = super->vftbl->subtype_depth + 1; + } else + /* java.lang.Object doesn't have a super class. */ + depth = 0; + + /* Now copy super's display, append c->vftbl and initialize the remaining fields. */ + if (depth >= DISPLAY_SIZE) { + c->vftbl->subtype_overflow = MNEW(vftbl_t *, depth - DISPLAY_SIZE + 1); +#if defined(ENABLE_STATISTICS) + if (opt_stat) + count_vftbl_len += sizeof(vftbl_t*) * (depth - DISPLAY_SIZE + 1); +#endif + memcpy(c->vftbl->subtype_overflow, super->vftbl->subtype_overflow, sizeof(vftbl_t*) * (depth - DISPLAY_SIZE)); + c->vftbl->subtype_overflow[depth - DISPLAY_SIZE] = c->vftbl; + depth_fixed = DISPLAY_SIZE; + } + else { + depth_fixed = depth; + c->vftbl->subtype_display[depth] = c->vftbl; + } + + if (super) + memcpy(c->vftbl->subtype_display, super->vftbl->subtype_display, sizeof(vftbl_t*) * depth_fixed); + for (i=depth_fixed+1; i<=DISPLAY_SIZE; i++) + c->vftbl->subtype_display[i] = NULL; + c->vftbl->subtype_offset = OFFSET(vftbl_t, subtype_display[0]) + sizeof(vftbl_t*) * depth_fixed; + c->vftbl->subtype_depth = depth; ++ ++ return c; +} +#endif + +/* link_class_intern *********************************************************** + + Tries to link a class. The function calculates the length in bytes + that an instance of this class requires as well as the VTBL for + methods and interface methods. + +*******************************************************************************/ + +static classinfo *link_class_intern(classinfo *c) +{ + classinfo *super; /* super class */ + classinfo *tc; /* temporary class variable */ + s4 supervftbllength; /* vftbllegnth of super class */ + s4 vftbllength; /* vftbllength of current class */ + s4 interfacetablelength; /* interface table length */ + vftbl_t *v; /* vftbl of current class */ + s4 i; /* interface/method/field counter */ + arraydescriptor *arraydesc; /* descriptor for array classes */ + method_worklist *worklist; /* worklist for recompilation */ +#if defined(ENABLE_RT_TIMING) + struct timespec time_start, time_resolving, time_compute_vftbl, + time_abstract, time_compute_iftbl, time_fill_vftbl, + time_offsets, time_fill_iftbl, time_finalizer, + time_subclasses; +#endif + + RT_TIMING_GET_TIME(time_start); + + TRACELINKCLASS(c); + + /* the class must be loaded */ + + /* XXX should this be a specific exception? */ + assert(c->state & CLASS_LOADED); + + /* This is check in link_class. */ + + assert(!(c->state & CLASS_LINKED)); + + /* cache the self-reference of this class */ + /* we do this for cases where the defining loader of the class */ + /* has not yet been recorded as an initiating loader for the class */ + /* this is needed so subsequent code can assume that self-refs */ + /* will always resolve lazily */ + /* No need to do it for the bootloader - it is always registered */ + /* as initiating loader for the classes it loads. */ + if (c->classloader) + classcache_store(c->classloader,c,false); + + /* this class is currently linking */ + + c->state |= CLASS_LINKING; + + arraydesc = NULL; + worklist = NULL; + + /* Link the super interfaces. */ + + for (i = 0; i < c->interfacescount; i++) { + tc = c->interfaces[i]; + + if (!(tc->state & CLASS_LINKED)) + if (!link_class(tc)) + return NULL; + } + + /* check super class */ + + super = NULL; + + /* Check for java/lang/Object. */ + + if (c->super == NULL) { + c->index = 0; + c->instancesize = sizeof(java_object_t); + + vftbllength = supervftbllength = 0; + + c->finalizer = NULL; + } + else { + /* Get super class. */ + + super = c->super; + + /* Link the super class if necessary. */ + + if (!(super->state & CLASS_LINKED)) + if (!link_class(super)) + return NULL; + + /* OR the ACC_CLASS_HAS_POINTERS and the ACC_CLASS_REFERENCE_* + flags. */ + + c->flags |= (super->flags & + (ACC_CLASS_HAS_POINTERS | ACC_CLASS_REFERENCE_MASK)); + + /* handle array classes */ + + if (c->name->text[0] == '[') + if (!(arraydesc = link_array(c))) + return NULL; + + if (c->flags & ACC_INTERFACE) + c->index = interfaceindex++; + else + c->index = super->index + 1; + + c->instancesize = super->instancesize; + + vftbllength = supervftbllength = super->vftbl->vftbllength; + + c->finalizer = super->finalizer; + } + RT_TIMING_GET_TIME(time_resolving); + + + /* compute vftbl length */ + + for (i = 0; i < c->methodscount; i++) { + methodinfo *m = &(c->methods[i]); + + if (!(m->flags & ACC_STATIC)) { /* is instance method */ + tc = super; + + while (tc) { + s4 j; + + for (j = 0; j < tc->methodscount; j++) { + if (method_canoverwrite(m, &(tc->methods[j]))) { + if (tc->methods[j].flags & ACC_PRIVATE) + goto notfoundvftblindex; + + /* package-private methods in other packages */ + /* must not be overridden */ + /* (see Java Language Specification 8.4.8.1) */ + if ( !(tc->methods[j].flags & (ACC_PUBLIC | ACC_PROTECTED)) + && !SAME_PACKAGE(c,tc) ) + { + goto notfoundvftblindex; + } + + if (!linker_overwrite_method(&(tc->methods[j]), m, &worklist)) + return NULL; + + goto foundvftblindex; + } + } + + tc = tc->super; + } + + notfoundvftblindex: + m->vftblindex = (vftbllength++); + foundvftblindex: + ; + } + } + RT_TIMING_GET_TIME(time_compute_vftbl); + + + /* Check all interfaces of an abstract class (maybe be an + interface too) for unimplemented methods. Such methods are + called miranda-methods and are marked with the ACC_MIRANDA + flag. VMClass.getDeclaredMethods does not return such + methods. */ + + if (c->flags & ACC_ABSTRACT) { + classinfo *ic; + methodinfo *im; + s4 abstractmethodscount; + s4 j; + s4 k; + + abstractmethodscount = 0; + + /* check all interfaces of the abstract class */ + + for (i = 0; i < c->interfacescount; i++) { + ic = c->interfaces[i]; + + for (j = 0; j < ic->methodscount; j++) { + im = &(ic->methods[j]); + + /* skip `' and `' */ + + if ((im->name == utf_clinit) || (im->name == utf_init)) + continue; + + for (tc = c; tc != NULL; tc = tc->super) { + for (k = 0; k < tc->methodscount; k++) { + if (method_canoverwrite(im, &(tc->methods[k]))) + goto noabstractmethod; + } + } + + abstractmethodscount++; + + noabstractmethod: + ; + } + } + + if (abstractmethodscount > 0) { + methodinfo *am; + + /* reallocate methods memory */ + + c->methods = (methodinfo*) MREALLOC(c->methods, methodinfo, c->methodscount, + c->methodscount + abstractmethodscount); + + for (i = 0; i < c->interfacescount; i++) { + ic = c->interfaces[i]; + + for (j = 0; j < ic->methodscount; j++) { + im = &(ic->methods[j]); + + /* skip `' and `' */ + + if ((im->name == utf_clinit) || (im->name == utf_init)) + continue; + + for (tc = c; tc != NULL; tc = tc->super) { + for (k = 0; k < tc->methodscount; k++) { + if (method_canoverwrite(im, &(tc->methods[k]))) + goto noabstractmethod2; + } + } + + /* Copy the method found into the new c->methods + array and tag it as miranda-method. */ + + am = &(c->methods[c->methodscount]); + c->methodscount++; + + MCOPY(am, im, methodinfo, 1); + + am->vftblindex = (vftbllength++); + am->clazz = c; + am->flags |= ACC_MIRANDA; + + noabstractmethod2: + ; + } + } + } + } + RT_TIMING_GET_TIME(time_abstract); + + +#if defined(ENABLE_STATISTICS) + if (opt_stat) + count_vftbl_len += + sizeof(vftbl_t) + (sizeof(methodptr) * (vftbllength - 1)); +#endif + + /* compute interfacetable length */ + + interfacetablelength = 0; + + for (tc = c; tc != NULL; tc = tc->super) { + for (i = 0; i < tc->interfacescount; i++) { + s4 h = class_highestinterface(tc->interfaces[i]) + 1; + + if (h > interfacetablelength) + interfacetablelength = h; + } + } + RT_TIMING_GET_TIME(time_compute_iftbl); + + /* allocate virtual function table */ + + v = (vftbl_t *) mem_alloc(sizeof(vftbl_t) + + sizeof(methodptr) * (vftbllength - 1) + + sizeof(methodptr*) * (interfacetablelength - (interfacetablelength > 0))); + v = (vftbl_t *) (((methodptr *) v) + + (interfacetablelength - 1) * (interfacetablelength > 1)); + + c->vftbl = v; + v->clazz = c; + v->vftbllength = vftbllength; + v->interfacetablelength = interfacetablelength; + v->arraydesc = arraydesc; + + /* store interface index in vftbl */ + + if (c->flags & ACC_INTERFACE) + v->baseval = -(c->index); + + /* copy virtual function table of super class */ + + for (i = 0; i < supervftbllength; i++) + v->table[i] = super->vftbl->table[i]; + + /* Fill the remaining vftbl slots with the AbstractMethodError + stub (all after the super class slots, because they are already + initialized). */ + + for (; i < vftbllength; i++) { +#if defined(ENABLE_JIT) +# if defined(ENABLE_INTRP) + if (opt_intrp) + v->table[i] = (methodptr) (ptrint) &intrp_asm_abstractmethoderror; + else +# endif + v->table[i] = (methodptr) (ptrint) &asm_abstractmethoderror; +#else + v->table[i] = (methodptr) (ptrint) &intrp_asm_abstractmethoderror; +#endif + } + + /* add method stubs into virtual function table */ + + for (i = 0; i < c->methodscount; i++) { + methodinfo *m = &(c->methods[i]); + + assert(m->stubroutine == NULL); + + /* Don't create a compiler stub for abstract methods as they + throw an AbstractMethodError with the default stub in the + vftbl. This entry is simply copied by sub-classes. */ + + if (m->flags & ACC_ABSTRACT) + continue; + +#if defined(ENABLE_JIT) +# if defined(ENABLE_INTRP) + if (opt_intrp) + m->stubroutine = intrp_createcompilerstub(m); + else +#endif + m->stubroutine = (u1*) CompilerStub::generate(m); +#else + m->stubroutine = intrp_createcompilerstub(m); +#endif + + /* static methods are not in the vftbl */ + + if (m->flags & ACC_STATIC) + continue; + + /* insert the stubroutine into the vftbl */ + + v->table[m->vftblindex] = (methodptr) (ptrint) m->stubroutine; + } + RT_TIMING_GET_TIME(time_fill_vftbl); + + /* compute instance size and offset of each field */ + + for (i = 0; i < c->fieldscount; i++) { + s4 dsize; + fieldinfo *f = &(c->fields[i]); + + if (!(f->flags & ACC_STATIC)) { + dsize = descriptor_typesize(f->parseddesc); + c->instancesize = MEMORY_ALIGN(c->instancesize, dsize); + f->offset = c->instancesize; + c->instancesize += dsize; + } + } + RT_TIMING_GET_TIME(time_offsets); + + /* initialize interfacetable and interfacevftbllength */ + + v->interfacevftbllength = MNEW(s4, interfacetablelength); + +#if defined(ENABLE_STATISTICS) + if (opt_stat) + count_vftbl_len += (4 + sizeof(s4)) * v->interfacetablelength; +#endif + + for (i = 0; i < interfacetablelength; i++) { + v->interfacevftbllength[i] = 0; + v->interfacetable[-i] = NULL; + } + + /* add interfaces */ + + for (tc = c; tc != NULL; tc = tc->super) + for (i = 0; i < tc->interfacescount; i++) + if (!linker_addinterface(c, tc->interfaces[i])) + return NULL; + + RT_TIMING_GET_TIME(time_fill_iftbl); + + /* add finalizer method (not for java.lang.Object) */ + + if (super) { + methodinfo *fi; + + fi = class_findmethod(c, utf_finalize, utf_void__void); + + if (fi) + if (!(fi->flags & ACC_STATIC)) + c->finalizer = fi; + } + RT_TIMING_GET_TIME(time_finalizer); + + /* final tasks */ + + linker_compute_subclasses(c); + + /* FIXME: this is completely useless now */ + RT_TIMING_GET_TIME(time_subclasses); + +#if USES_NEW_SUBTYPE - build_display(c); ++ if (!build_display(c)) ++ return NULL; +#endif + + /* revert the linking state and class is linked */ + + c->state = (c->state & ~CLASS_LINKING) | CLASS_LINKED; + + /* check worklist */ + + /* XXX must this also be done in case of exception? */ + + while (worklist != NULL) { + method_worklist *wi = worklist; + + worklist = worklist->next; + + INLINELOG( printf("MUST BE RECOMPILED: "); method_println(wi->m); ); + jit_invalidate_code(wi->m); + + /* XXX put worklist into dump memory? */ + FREE(wi, method_worklist); + } + + RT_TIMING_TIME_DIFF(time_start ,time_resolving ,RT_TIMING_LINK_RESOLVE); + RT_TIMING_TIME_DIFF(time_resolving ,time_compute_vftbl,RT_TIMING_LINK_C_VFTBL); + RT_TIMING_TIME_DIFF(time_compute_vftbl,time_abstract ,RT_TIMING_LINK_ABSTRACT); + RT_TIMING_TIME_DIFF(time_abstract ,time_compute_iftbl,RT_TIMING_LINK_C_IFTBL); + RT_TIMING_TIME_DIFF(time_compute_iftbl,time_fill_vftbl ,RT_TIMING_LINK_F_VFTBL); + RT_TIMING_TIME_DIFF(time_fill_vftbl ,time_offsets ,RT_TIMING_LINK_OFFSETS); + RT_TIMING_TIME_DIFF(time_offsets ,time_fill_iftbl ,RT_TIMING_LINK_F_IFTBL); + RT_TIMING_TIME_DIFF(time_fill_iftbl ,time_finalizer ,RT_TIMING_LINK_FINALIZER); + RT_TIMING_TIME_DIFF(time_finalizer ,time_subclasses ,RT_TIMING_LINK_SUBCLASS); + + /* just return c to show that we didn't had a problem */ + + return c; +} + + +/* link_array ****************************************************************** + + This function is called by link_class to create the arraydescriptor + for an array class. + + This function returns NULL if the array cannot be linked because + the component type has not been linked yet. + +*******************************************************************************/ + +static arraydescriptor *link_array(classinfo *c) +{ + classinfo *comp; + s4 namelen; + arraydescriptor *desc; + vftbl_t *compvftbl; + utf *u; + + comp = NULL; + namelen = c->name->blength; + + /* Check the component type */ + + switch (c->name->text[1]) { + case '[': + /* c is an array of arrays. */ + u = utf_new(c->name->text + 1, namelen - 1); + if (!(comp = load_class_from_classloader(u, c->classloader))) + return NULL; + break; + + case 'L': + /* c is an array of objects. */ + u = utf_new(c->name->text + 2, namelen - 3); + if (!(comp = load_class_from_classloader(u, c->classloader))) + return NULL; + break; + } + + /* If the component type has not been linked, link it now */ + + assert(!comp || (comp->state & CLASS_LOADED)); + + if (comp && !(comp->state & CLASS_LINKED)) + if (!link_class(comp)) + return NULL; + + /* Allocate the arraydescriptor */ + + desc = NEW(arraydescriptor); + + if (comp) { + /* c is an array of references */ + desc->arraytype = ARRAYTYPE_OBJECT; + desc->componentsize = sizeof(void*); + desc->dataoffset = OFFSET(java_objectarray_t, data); + + compvftbl = comp->vftbl; + + if (!compvftbl) { + log_text("Component class has no vftbl"); + assert(0); + } + + desc->componentvftbl = compvftbl; + + if (compvftbl->arraydesc) { + desc->elementvftbl = compvftbl->arraydesc->elementvftbl; + + if (compvftbl->arraydesc->dimension >= 255) { + log_text("Creating array of dimension >255"); + assert(0); + } + + desc->dimension = compvftbl->arraydesc->dimension + 1; + desc->elementtype = compvftbl->arraydesc->elementtype; + + } else { + desc->elementvftbl = compvftbl; + desc->dimension = 1; + desc->elementtype = ARRAYTYPE_OBJECT; + } + + } else { + /* c is an array of a primitive type */ + switch (c->name->text[1]) { + case 'Z': + desc->arraytype = ARRAYTYPE_BOOLEAN; + desc->dataoffset = OFFSET(java_booleanarray_t,data); + desc->componentsize = sizeof(u1); + break; + + case 'B': + desc->arraytype = ARRAYTYPE_BYTE; + desc->dataoffset = OFFSET(java_bytearray_t,data); + desc->componentsize = sizeof(u1); + break; + + case 'C': + desc->arraytype = ARRAYTYPE_CHAR; + desc->dataoffset = OFFSET(java_chararray_t,data); + desc->componentsize = sizeof(u2); + break; + + case 'D': + desc->arraytype = ARRAYTYPE_DOUBLE; + desc->dataoffset = OFFSET(java_doublearray_t,data); + desc->componentsize = sizeof(double); + break; + + case 'F': + desc->arraytype = ARRAYTYPE_FLOAT; + desc->dataoffset = OFFSET(java_floatarray_t,data); + desc->componentsize = sizeof(float); + break; + + case 'I': + desc->arraytype = ARRAYTYPE_INT; + desc->dataoffset = OFFSET(java_intarray_t,data); + desc->componentsize = sizeof(s4); + break; + + case 'J': + desc->arraytype = ARRAYTYPE_LONG; + desc->dataoffset = OFFSET(java_longarray_t,data); + desc->componentsize = sizeof(s8); + break; + + case 'S': + desc->arraytype = ARRAYTYPE_SHORT; + desc->dataoffset = OFFSET(java_shortarray_t,data); + desc->componentsize = sizeof(s2); + break; + + default: + exceptions_throw_noclassdeffounderror(c->name); + return NULL; + } + + desc->componentvftbl = NULL; + desc->elementvftbl = NULL; + desc->dimension = 1; + desc->elementtype = desc->arraytype; + } + + return desc; +} + + +/* linker_compute_subclasses *************************************************** + + XXX + + ATTENTION: DO NOT REMOVE ANY OF THE LOCKING MECHANISMS BELOW: + This function needs to take the class renumber lock and stop the + world during class renumbering. The lock is used in C code which + is not that performance critical. Whereas JIT code uses critical + sections to atomically access the class values. + +*******************************************************************************/ + +static void linker_compute_subclasses(classinfo *c) +{ + + LOCK_CLASSRENUMBER_LOCK; + + if (!(c->flags & ACC_INTERFACE)) { + c->nextsub = NULL; + c->sub = NULL; +#if USES_NEW_SUBTYPE + c->vftbl->baseval = 1; /* so it does not look like an interface */ +#endif + } + + if (!(c->flags & ACC_INTERFACE) && (c->super != NULL)) { + c->nextsub = c->super->sub; + c->super->sub = c; + } + + classvalue = 0; + +#if !USES_NEW_SUBTYPE + /* compute class values */ + + linker_compute_class_values(class_java_lang_Object); +#endif + + UNLOCK_CLASSRENUMBER_LOCK; + +} + + +/* linker_compute_class_values ************************************************* + + XXX + +*******************************************************************************/ + +static void linker_compute_class_values(classinfo *c) +{ + classinfo *subs; + + c->vftbl->baseval = ++classvalue; + + subs = c->sub; + + while (subs) { + linker_compute_class_values(subs); + + subs = subs->nextsub; + } + + c->vftbl->diffval = classvalue - c->vftbl->baseval; +} + + +/* linker_addinterface ********************************************************* + + Is needed by link_class for adding a VTBL to a class. All + interfaces implemented by ic are added as well. + + RETURN VALUE: + true.........everything ok + false........an exception has been thrown + +*******************************************************************************/ + +static bool linker_addinterface(classinfo *c, classinfo *ic) +{ + s4 j, k; + vftbl_t *v; + s4 i; + classinfo *sc; + methodinfo *m; + + v = c->vftbl; + i = ic->index; + + if (i >= v->interfacetablelength) + vm_abort("Internal error: interfacetable overflow"); + + /* if this interface has already been added, return immediately */ + + if (v->interfacetable[-i] != NULL) + return true; + + if (ic->methodscount == 0) { /* fake entry needed for subtype test */ + v->interfacevftbllength[i] = 1; + v->interfacetable[-i] = MNEW(methodptr, 1); + v->interfacetable[-i][0] = NULL; + } + else { + v->interfacevftbllength[i] = ic->methodscount; + v->interfacetable[-i] = MNEW(methodptr, ic->methodscount); + +#if defined(ENABLE_STATISTICS) + if (opt_stat) + count_vftbl_len += sizeof(methodptr) * + (ic->methodscount + (ic->methodscount == 0)); +#endif + + for (j = 0; j < ic->methodscount; j++) { + for (sc = c; sc != NULL; sc = sc->super) { + for (k = 0; k < sc->methodscount; k++) { + m = &(sc->methods[k]); + + if (method_canoverwrite(m, &(ic->methods[j]))) { + /* method m overwrites the (abstract) method */ +#if defined(ENABLE_VERIFIER) + /* Add loading constraints (for the more + general types of the method + ic->methods[j]). */ + if (!classcache_add_constraints_for_params( + c->classloader, ic->classloader, + &(ic->methods[j]))) + { + return false; + } +#endif + + /* XXX taken from gcj */ + /* check for ACC_STATIC: IncompatibleClassChangeError */ + + /* check for !ACC_PUBLIC: IllegalAccessError */ + + /* check for ACC_ABSTRACT: AbstracMethodError, + not sure about that one */ + + v->interfacetable[-i][j] = v->table[m->vftblindex]; + goto foundmethod; + } + } + } + + /* If no method was found, insert the AbstractMethodError + stub. */ + +#if defined(ENABLE_JIT) +# if defined(ENABLE_INTRP) + if (opt_intrp) + v->interfacetable[-i][j] = + (methodptr) (ptrint) &intrp_asm_abstractmethoderror; + else +# endif + v->interfacetable[-i][j] = + (methodptr) (ptrint) &asm_abstractmethoderror; +#else + v->interfacetable[-i][j] = + (methodptr) (ptrint) &intrp_asm_abstractmethoderror; +#endif + + foundmethod: + ; + } + } + + /* add superinterfaces of this interface */ + + for (j = 0; j < ic->interfacescount; j++) + if (!linker_addinterface(c, ic->interfaces[j])) + return false; + + /* everything ok */ + + return true; +} + + +/* class_highestinterface ****************************************************** + + Used by the function link_class to determine the amount of memory + needed for the interface table. + +*******************************************************************************/ + +static s4 class_highestinterface(classinfo *c) +{ + s4 h; + s4 h2; + s4 i; + + /* check for ACC_INTERFACE bit already done in link_class_intern */ + + h = c->index; + + for (i = 0; i < c->interfacescount; i++) { + h2 = class_highestinterface(c->interfaces[i]); + + if (h2 > h) + h = h2; + } + + return h; +} + +#if defined(__cplusplus) +} +#endif + +/* + * 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: + */