/* src/vm/linker.cpp - class linker functions

   Copyright (C) 1996-2013
   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 <stdint.h>

#include "vm/types.h"

#include "mm/memory.hpp"

#include "native/native.hpp"

#include "threads/lock.hpp"
#include "threads/mutex.hpp"

#include "toolbox/logging.hpp"

#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/hook.hpp"
#include "vm/loader.hpp"
#include "vm/options.h"
#include "vm/primitive.hpp"
#include "vm/rt-timing.hpp"
#include "vm/string.hpp"
#include "vm/vm.hpp"

#include "vm/jit/asmpart.h"
#include "vm/jit/stubs.hpp"

#include <vector>
#include <utility>


/* 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);


typedef std::vector<std::pair<java_object_t**, utf*> > deferred_strings_vec_t;
static deferred_strings_vec_t deferred_strings;

/* 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");

	if (!link_class(class_sun_reflect_MethodAccessorImpl))
		vm_abort("linker_init: linking failed");

	if (!link_class(class_sun_reflect_ConstructorAccessorImpl))
		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 0;
	}

	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);

	// Hook point just after a class was linked.
	if (!Hook::class_linked(r))
		return 0;

	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 <init>, 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 */
	/* <init> 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 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; n<a->dimension; 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) {
		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 `<clinit>' and `<init>' */

				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 `<clinit>' and `<init>' */

					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
	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) {
				exceptions_throw_illegalargumentexception();
				return NULL;
			}

			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_create_string_later **************************************************

   A hack so we can initialize java.lang.String objects during initialization.

*******************************************************************************/
void linker_create_string_later(java_object_t **a, utf *u)
{
	deferred_strings.push_back(std::make_pair(a, u));
}

void linker_initialize_deferred_strings()
{
	deferred_strings_vec_t::const_iterator it = deferred_strings.begin();
	for (; it != deferred_strings.end(); ++it)
		*it->first = literalstring_new(it->second);
	deferred_strings.clear();
}


/* 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:
 */
