/* Functions related to building classes and their related objects.
   Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001
   Free Software Foundation, Inc.

This file is part of GNU CC.

GNU CC 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.

GNU CC 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 GNU CC; see the file COPYING.  If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.

Java and all Java-based marks are trademarks or registered trademarks
of Sun Microsystems, Inc. in the United States and other countries.
The Free Software Foundation is independent of Sun Microsystems, Inc.  */

/* Written by Per Bothner <bothner@cygnus.com> */

#include "config.h"
#include "system.h"
#include "tree.h"
#include "rtl.h"
#include "flags.h"
#include "java-tree.h"
#include "jcf.h"
#include "obstack.h"
#include "toplev.h"
#include "output.h"
#include "parse.h"
#include "ggc.h"

static tree make_method_value PARAMS ((tree));
static tree build_java_method_type PARAMS ((tree, tree, int));
static int32 hashUtf8String PARAMS ((const char *, int));
static tree make_field_value PARAMS ((tree));
static tree get_dispatch_vector PARAMS ((tree));
static tree get_dispatch_table PARAMS ((tree, tree));
static void add_interface_do PARAMS ((tree, tree, int));
static tree maybe_layout_super_class PARAMS ((tree, tree));
static int assume_compiled PARAMS ((const char *));
static struct hash_entry *init_test_hash_newfunc PARAMS ((struct hash_entry *,
							  struct hash_table *,
							  hash_table_key));
static rtx registerClass_libfunc;

extern struct obstack permanent_obstack;
struct obstack temporary_obstack;

/* The compiler generates different code depending on whether or not
   it can assume certain classes have been compiled down to native
   code or not.  The compiler options -fassume-compiled= and
   -fno-assume-compiled= are used to create a tree of
   assume_compiled_node objects.  This tree is queried to determine if
   a class is assume to be compiled or not.  Each node in the tree
   represents either a package or a specific class.  */

typedef struct assume_compiled_node_struct
{
  /* The class or package name.  */
  const char *ident;

  /* Non-zero if this represents an exclusion.  */
  int excludep;

  /* Pointers to other nodes in the tree.  */
  struct assume_compiled_node_struct *parent;
  struct assume_compiled_node_struct *sibling;
  struct assume_compiled_node_struct *child;
} assume_compiled_node;

static assume_compiled_node *find_assume_compiled_node
			PARAMS ((assume_compiled_node *, const char *));

/* This is the root of the include/exclude tree.  */

static assume_compiled_node *assume_compiled_tree;

static tree class_roots[5]
= { NULL_TREE, NULL_TREE, NULL_TREE, NULL_TREE, NULL_TREE };
#define registered_class class_roots[0]
#define fields_ident class_roots[1]  /* get_identifier ("fields") */
#define info_ident class_roots[2]  /* get_identifier ("info") */
#define class_list class_roots[3]
#define class_dtable_decl class_roots[4]

/* Return the node that most closely represents the class whose name
   is IDENT.  Start the search from NODE.  Return NULL if an
   appropriate node does not exist.  */

static assume_compiled_node *
find_assume_compiled_node (node, ident)
     assume_compiled_node *node;
     const char *ident;
{
  while (node)
    {
      size_t node_ident_length = strlen (node->ident);

      /* node_ident_length is zero at the root of the tree.  If the
	 identifiers are the same length, then we have matching
	 classes.  Otherwise check if we've matched an enclosing
	 package name.  */

      if (node_ident_length == 0
	  || (strncmp (ident, node->ident, node_ident_length) == 0
	      && (strlen (ident) == node_ident_length
		  || ident[node_ident_length] == '.')))
	{
	  /* We've found a match, however, there might be a more
             specific match.  */

	  assume_compiled_node *found = find_assume_compiled_node (node->child,
								   ident);
	  if (found)
	    return found;
	  else
	    return node;
	}

      /* No match yet.  Continue through the sibling list.  */
      node = node->sibling;
    }

  /* No match at all in this tree.  */
  return NULL;
}

/* Add a new IDENT to the include/exclude tree.  It's an exclusion
   if EXCLUDEP is non-zero.  */

void
add_assume_compiled (ident, excludep)
     const char *ident;
     int excludep;
{
  assume_compiled_node *parent;
  assume_compiled_node *node = 
    (assume_compiled_node *) xmalloc (sizeof (assume_compiled_node));

  node->ident = xstrdup (ident);
  node->excludep = excludep;
  node->child = NULL;

  /* Create the root of the tree if it doesn't exist yet.  */

  if (NULL == assume_compiled_tree)
    {
      assume_compiled_tree = 
	(assume_compiled_node *) xmalloc (sizeof (assume_compiled_node));
      assume_compiled_tree->ident = "";
      assume_compiled_tree->excludep = 0;
      assume_compiled_tree->sibling = NULL;
      assume_compiled_tree->child = NULL;
      assume_compiled_tree->parent = NULL;
    }

  /* Calling the function with the empty string means we're setting
     excludep for the root of the hierarchy.  */

  if (0 == ident[0])
    {
      assume_compiled_tree->excludep = excludep;
      return;
    }

  /* Find the parent node for this new node.  PARENT will either be a
     class or a package name.  Adjust PARENT accordingly.  */

  parent = find_assume_compiled_node (assume_compiled_tree, ident);
  if (ident[strlen (parent->ident)] != '.')
    parent = parent->parent;

  /* Insert NODE into the tree.  */

  node->parent = parent;
  node->sibling = parent->child;
  parent->child = node;
}

/* Returns non-zero if IDENT is the name of a class that the compiler
   should assume has been compiled to FIXME  */

static int
assume_compiled (ident)
     const char *ident;
{
  assume_compiled_node *i;
  int result;
  
  if (NULL == assume_compiled_tree)
    return 1;

  i = find_assume_compiled_node (assume_compiled_tree,
				 ident);

  result = ! i->excludep;
  
  return (result);
}

/* Return an IDENTIFIER_NODE the same as (OLD_NAME, OLD_LENGTH).
   except that characters matching OLD_CHAR are substituted by NEW_CHAR.
   Also, PREFIX is prepended, and SUFFIX is appended. */

tree
ident_subst (old_name, old_length, prefix, old_char, new_char, suffix)
     const char* old_name;
     int old_length;
     const char *prefix;
     int old_char;
     int new_char;
     const char *suffix;
{
  int prefix_len = strlen (prefix);
  int suffix_len = strlen (suffix);
  int i = prefix_len + old_length + suffix_len + 1;
#ifdef __GNUC__
  char buffer[i];
#else
  char *buffer = (char *)alloca  (i);
#endif
  strcpy (buffer, prefix);
  for (i = 0; i < old_length; i++)
    {
      char ch = old_name[i];
      if (ch == old_char)
	ch = new_char;
      buffer[prefix_len + i] = ch;
    }
  strcpy (buffer + prefix_len + old_length, suffix);
  return get_identifier (buffer);
}

/* Return an IDENTIFIER_NODE the same as OLD_ID,
   except that characters matching OLD_CHAR are substituted by NEW_CHAR.
   Also, PREFIX is prepended, and SUFFIX is appended. */

tree
identifier_subst (old_id, prefix, old_char, new_char, suffix)
     const tree old_id;
     const char *prefix;
     int old_char;
     int new_char;
     const char *suffix;
{
  return ident_subst (IDENTIFIER_POINTER (old_id), IDENTIFIER_LENGTH (old_id),
		      prefix, old_char, new_char, suffix);
}

/* Generate a valid C identifier from the name of the class TYPE,
   prefixed by PREFIX. */

tree
mangled_classname (prefix, type)
  const char *prefix;
  tree type;
{
  tree ident = TYPE_NAME (type);
  if (TREE_CODE (ident) != IDENTIFIER_NODE)
    ident = DECL_NAME (ident);
  return identifier_subst (ident, prefix, '.', '_', "");
}

tree
make_class ()
{
  tree type;
  type = make_node (RECORD_TYPE);
#ifdef JAVA_USE_HANDLES
  tree field1 = build_decl (FIELD_DECL, get_identifier ("obj"),
			    build_pointer_type (type));
  tree field2 = build_decl (FIELD_DECL, get_identifier ("methods"),
			    methodtable_ptr_type);
  tree handle_type = make_node (RECORD_TYPE);
  TREE_CHAIN (field1) = field2;
  TYPE_FIELDS (handle_type) = field1;
  TYPE_BINFO (type) = make_tree_vec (7);
  TYPE_BINFO (handle_type) = make_tree_vec (7);
  BINFO_HANDLE (TYPE_BINFO (handle_type)) = type;
  BINFO_HANDLE (TYPE_BINFO (type)) = handle_type;
#else
  TYPE_BINFO (type) = make_tree_vec (6);
#endif
  MAYBE_CREATE_TYPE_TYPE_LANG_SPECIFIC (type);

  return type;
}

/* Given a fully-qualified classname in NAME (whose length is NAME_LENGTH),
   and where each of the constituents is separated by '/',
   return a corresponding IDENTIFIER_NODE, except using '.' as separator. */

tree
unmangle_classname (name, name_length)
     const char *name;  int name_length;
{
  tree to_return = ident_subst (name, name_length, "", '/', '.', "");
  /* It's not sufficient to compare to_return and get_identifier
     (name) to determine whether to_return is qualified. There are
     cases in signature analysis where name will be stripped of a
     trailing ';'. */
  name = IDENTIFIER_POINTER (to_return);
  while (*name)
    if (*name++ == '.') 
      {
	QUALIFIED_P (to_return) = 1;
	break;
      }
  
  return to_return;
}

tree
push_class (class_type, class_name)
     tree class_type, class_name;
{
  tree decl, signature;
  const char *save_input_filename = input_filename;
  int save_lineno = lineno;
  tree source_name = identifier_subst (class_name, "", '.', '/', ".java");
  CLASS_P (class_type) = 1;
  input_filename = IDENTIFIER_POINTER (source_name);
  lineno = 0;
  decl = build_decl (TYPE_DECL, class_name, class_type);
  input_filename = save_input_filename;
  lineno = save_lineno;
  signature = identifier_subst (class_name, "L", '.', '/', ";");
  IDENTIFIER_SIGNATURE_TYPE (signature) = build_pointer_type (class_type);

  /* Setting DECL_ARTIFICAL forces dbxout.c to specific the type is
     both a typedef and in the struct name-space.  We may want to re-visit
     this later, but for now it reduces the changes needed for gdb. */
  DECL_ARTIFICIAL (decl) = 1;

  pushdecl_top_level (decl);
#ifdef JAVA_USE_HANDLES
  {
    tree handle_name = identifier_subst (class_name,
					 "Handle$", '.', '.', "");
    tree handle_decl = build_decl (TYPE_DECL, handle_name,
				   CLASS_TO_HANDLE_TYPE (class_type));
    pushdecl (handle_decl);
  }
#endif

  return decl;
}

/* Finds the (global) class named NAME.  Creates the class if not found.
   Also creates associated TYPE_DECL.
   Does not check if the class actually exists, load the class,
   fill in field or methods, or do layout_type. */

tree
lookup_class (name)
     tree name;
{
  tree decl = IDENTIFIER_CLASS_VALUE (name);
  if (decl == NULL_TREE)
    decl = push_class (make_class (), name);
  return TREE_TYPE (decl);
}

void
set_super_info (access_flags, this_class, super_class, interfaces_count)
     int access_flags;
     tree this_class;
     tree super_class;
     int interfaces_count;
{
  int total_supers = interfaces_count;
  tree class_decl = TYPE_NAME (this_class);
  if (super_class)
    total_supers++;

  TYPE_BINFO_BASETYPES (this_class) = make_tree_vec (total_supers);
  if (super_class)
    {
      tree super_binfo = make_tree_vec (6);
      BINFO_TYPE (super_binfo) = super_class;
      BINFO_OFFSET (super_binfo) = integer_zero_node;
      TREE_VIA_PUBLIC (super_binfo) = 1;
      TREE_VEC_ELT (BINFO_BASETYPES (TYPE_BINFO (this_class)), 0)
	= super_binfo;
      CLASS_HAS_SUPER (this_class) = 1;
    }

  if (access_flags & ACC_PUBLIC)    CLASS_PUBLIC (class_decl) = 1;
  if (access_flags & ACC_FINAL)     CLASS_FINAL (class_decl) = 1;
  if (access_flags & ACC_SUPER)     CLASS_SUPER (class_decl) = 1;
  if (access_flags & ACC_INTERFACE) CLASS_INTERFACE (class_decl) = 1;
  if (access_flags & ACC_ABSTRACT)  CLASS_ABSTRACT (class_decl) = 1;
  if (access_flags & ACC_STATIC)    CLASS_STATIC (class_decl) = 1;
  if (access_flags & ACC_PRIVATE)   CLASS_PRIVATE (class_decl) = 1;
  if (access_flags & ACC_PROTECTED) CLASS_PROTECTED (class_decl) = 1;
}

/* Return length of inheritance chain of CLAS, where java.lang.Object is 0,
   direct sub-classes of Object are 1, and so on. */

int
class_depth (clas)
     tree clas;
{
  int depth = 0;
  if (! CLASS_LOADED_P (clas))
    load_class (clas, 1);
  if (TYPE_SIZE (clas) == error_mark_node)
    return -1;
  while (clas != object_type_node)
    {
      depth++;
      clas = TYPE_BINFO_BASETYPE (clas, 0);
    }
  return depth;
}

/* Return true iff TYPE2 is an interface that extends interface TYPE1 */

int
interface_of_p (type1, type2)
     tree type1, type2;
{
  int n, i;
  tree basetype_vec;

  if (!(basetype_vec = TYPE_BINFO_BASETYPES (type2)))
    return 0;
  n = TREE_VEC_LENGTH (basetype_vec);
  for (i = 0; i < n; i++)
    {
      tree vec_elt = TREE_VEC_ELT (basetype_vec, i);
      if (vec_elt && BINFO_TYPE (vec_elt) == type1)
	return 1;
    }
  for (i = 0; i < n; i++)
    {
      tree vec_elt = TREE_VEC_ELT (basetype_vec, i);
      if (vec_elt && BINFO_TYPE (vec_elt) 
	  && interface_of_p (type1, BINFO_TYPE (vec_elt)))
	return 1;
    }
  return 0;
}

/* Return true iff TYPE1 inherits from TYPE2. */

int
inherits_from_p (type1, type2)
     tree type1, type2;
{
  while (type1 != NULL_TREE && TREE_CODE (type1) == RECORD_TYPE)
    {
      if (type1 == type2)
	return 1;
      type1 = CLASSTYPE_SUPER (type1);
    }
  return 0;
}

/* Return a 1 iff TYPE1 is an enclosing context for TYPE2 */

int
enclosing_context_p (type1, type2)
     tree type1, type2;
{
  if (!INNER_CLASS_TYPE_P (type2))
    return 0;

  for (type2 = TREE_TYPE (DECL_CONTEXT (TYPE_NAME (type2)));
       type2; 
       type2 = (INNER_CLASS_TYPE_P (type2) ?
		TREE_TYPE (DECL_CONTEXT (TYPE_NAME (type2))) : NULL_TREE))
    {
      if (type2 == type1)
	return 1;
    }

  return 0;
}

/* Return 1 iff there exists a common enclosing context between TYPE1
   and TYPE2.  */

int common_enclosing_context_p (type1, type2)
     tree type1, type2;
{
  if (!PURE_INNER_CLASS_TYPE_P (type1) || !PURE_INNER_CLASS_TYPE_P (type2))
    return 0;
  
  for (type1 = TREE_TYPE (DECL_CONTEXT (TYPE_NAME (type1))); type1; 
       type1 = (PURE_INNER_CLASS_TYPE_P (type1) ?
		TREE_TYPE (DECL_CONTEXT (TYPE_NAME (type1))) : NULL_TREE))
    {
      tree current;
      for (current = TREE_TYPE (DECL_CONTEXT (TYPE_NAME (type2))); current;
	   current = (PURE_INNER_CLASS_TYPE_P (current) ?
		      TREE_TYPE (DECL_CONTEXT (TYPE_NAME (current))) : 
		      NULL_TREE))
	if (type1 == current)
	  return 1;
    }
  return 0;
}

static void
add_interface_do (basetype_vec, interface_class, i)
     tree basetype_vec, interface_class;
     int i;
{
  tree interface_binfo = make_tree_vec (6);
  BINFO_TYPE (interface_binfo) = interface_class;
  BINFO_OFFSET (interface_binfo) = integer_zero_node;
  TREE_VIA_VIRTUAL (interface_binfo) = 1;
  TREE_VIA_PUBLIC (interface_binfo) = 1;
  TREE_VEC_ELT (basetype_vec, i) = interface_binfo;
}

/* Add INTERFACE_CLASS to THIS_CLASS iff INTERFACE_CLASS can't be
   found in THIS_CLASS. Returns NULL_TREE upon success, INTERFACE_CLASS
   if attempt is made to add it twice. */

tree
maybe_add_interface (this_class, interface_class)
     tree this_class, interface_class;
{
  tree basetype_vec = TYPE_BINFO_BASETYPES (this_class);
  int i;
  int n = TREE_VEC_LENGTH (basetype_vec);
  for (i = 0; ; i++)
    {
      if (i >= n)
	{
	  error ("internal error - too many interface type");
	  return NULL_TREE;
	}
      else if (TREE_VEC_ELT (basetype_vec, i) == NULL_TREE)
	break;
      else if (BINFO_TYPE (TREE_VEC_ELT (basetype_vec, i)) == interface_class)
	return interface_class;
    } 
  add_interface_do (basetype_vec, interface_class, i);
  return NULL_TREE;
}

/* Add the INTERFACE_CLASS as one of the interfaces of THIS_CLASS. */

void
add_interface (this_class, interface_class)
     tree this_class, interface_class;
{
  tree basetype_vec = TYPE_BINFO_BASETYPES (this_class);
  int i;
  int n = TREE_VEC_LENGTH (basetype_vec);
  for (i = 0; ; i++)
    {
      if (i >= n)
	{
	  error ("internal error - too many interface type");
	  return;
	}
      else if (TREE_VEC_ELT (basetype_vec, i) == NULL_TREE)
	break;
    }
  add_interface_do (basetype_vec, interface_class, i);
}

#if 0
/* Return the address of a pointer to the first FUNCTION_DECL
   in the list (*LIST) whose DECL_NAME is NAME. */

static tree *
find_named_method (list, name)
     tree *list;
     tree name;
{
  while (*list && DECL_NAME (*list) != name)
    list = &TREE_CHAIN (*list);
  return list;
}
#endif

static tree
build_java_method_type (fntype, this_class, access_flags)
     tree fntype;
     tree this_class;
     int access_flags;
{
  if (access_flags & ACC_STATIC)
    return fntype;
  return build_method_type (CLASS_TO_HANDLE_TYPE (this_class), fntype);
}

static struct hash_entry *
init_test_hash_newfunc (entry, table, string)
     struct hash_entry *entry;
     struct hash_table *table;
     hash_table_key string ATTRIBUTE_UNUSED;
{
  struct init_test_hash_entry *ret = (struct init_test_hash_entry *) entry;
  if (ret == NULL)
    {
      ret = ((struct init_test_hash_entry *)
	     hash_allocate (table, sizeof (struct init_test_hash_entry)));
      if (ret == NULL)
	return NULL;
    }
  ret->init_test_decl = 0;
  return (struct hash_entry *) ret;
}

/* Hash table helpers. Also reused in find_applicable_accessible_methods_list
   (parse.y). The hash of a tree node is it's pointer value,
   comparison is direct. */

unsigned long
java_hash_hash_tree_node (k)
     hash_table_key k;
{
  return (long) k;
}

bool
java_hash_compare_tree_node (k1, k2)
     hash_table_key k1;
     hash_table_key k2;
{
  return ((char*) k1 == (char*) k2);
}

tree
add_method_1 (handle_class, access_flags, name, function_type)
     tree handle_class;
     int access_flags;
     tree name;
     tree function_type;
{
  tree method_type, fndecl;

  method_type = build_java_method_type (function_type,
					handle_class, access_flags);

  fndecl = build_decl (FUNCTION_DECL, name, method_type);
  DECL_CONTEXT (fndecl) = handle_class;

  DECL_LANG_SPECIFIC (fndecl)
    = (struct lang_decl *) ggc_alloc_cleared (sizeof (struct lang_decl));

  /* Initialize the static initializer test table.  */
  hash_table_init (&DECL_FUNCTION_INIT_TEST_TABLE (fndecl),
		   init_test_hash_newfunc, java_hash_hash_tree_node, 
		   java_hash_compare_tree_node);

  TREE_CHAIN (fndecl) = TYPE_METHODS (handle_class);
  TYPE_METHODS (handle_class) = fndecl;

  if (access_flags & ACC_PUBLIC) METHOD_PUBLIC (fndecl) = 1;
  if (access_flags & ACC_PROTECTED) METHOD_PROTECTED (fndecl) = 1;
  if (access_flags & ACC_PRIVATE)
    METHOD_PRIVATE (fndecl) = DECL_INLINE (fndecl) = 1;
  if (access_flags & ACC_NATIVE)
    {
      METHOD_NATIVE (fndecl) = 1;
      DECL_EXTERNAL (fndecl) = 1;
    }
  if (access_flags & ACC_STATIC) 
    METHOD_STATIC (fndecl) = DECL_INLINE (fndecl) = 1;
  if (access_flags & ACC_FINAL) 
    METHOD_FINAL (fndecl) = DECL_INLINE (fndecl) = 1;
  if (access_flags & ACC_SYNCHRONIZED) METHOD_SYNCHRONIZED (fndecl) = 1;
  if (access_flags & ACC_ABSTRACT) METHOD_ABSTRACT (fndecl) = 1;
  if (access_flags & ACC_TRANSIENT) METHOD_TRANSIENT (fndecl) = 1;
  return fndecl;
}

/* Add a method to THIS_CLASS.
   The method's name is NAME.
   Its signature (mangled type) is METHOD_SIG (an IDENTIFIER_NODE). */

tree
add_method (this_class, access_flags, name, method_sig)
     tree this_class;
     int access_flags;
     tree name;
     tree method_sig;
{
  tree handle_class = CLASS_TO_HANDLE_TYPE (this_class);
  tree function_type, fndecl;
  const unsigned char *sig
    = (const unsigned char *) IDENTIFIER_POINTER (method_sig);

  if (sig[0] != '(')
    fatal_error ("bad method signature");

  function_type = get_type_from_signature (method_sig);
  fndecl = add_method_1 (handle_class, access_flags, name, function_type);
  set_java_signature (TREE_TYPE (fndecl), method_sig);
  return fndecl;
}

tree
add_field (class, name, field_type, flags)
     tree class;
     tree name;
     tree field_type;
     int flags;
{
  int is_static = (flags & ACC_STATIC) != 0;
  tree field;
  field = build_decl (is_static ? VAR_DECL : FIELD_DECL, name, field_type);
  TREE_CHAIN (field) = TYPE_FIELDS (class);
  TYPE_FIELDS (class) = field;
  DECL_CONTEXT (field) = class;

  if (flags & ACC_PUBLIC) FIELD_PUBLIC (field) = 1;
  if (flags & ACC_PROTECTED) FIELD_PROTECTED (field) = 1;
  if (flags & ACC_PRIVATE) FIELD_PRIVATE (field) = 1;
  if (flags & ACC_FINAL) FIELD_FINAL (field) = 1;
  if (flags & ACC_VOLATILE) FIELD_VOLATILE (field) = 1;
  if (flags & ACC_TRANSIENT) FIELD_TRANSIENT (field) = 1;
  if (is_static)
    {
      FIELD_STATIC (field) = 1;
      /* Always make field externally visible.  This is required so
	 that native methods can always access the field.  */
      TREE_PUBLIC (field) = 1;
    }
  return field;
}

/* Associate a constant value CONSTANT with VAR_DECL FIELD. */

void
set_constant_value (field, constant)
     tree field, constant;
{
  if (field == NULL_TREE)
    warning ("misplaced ConstantValue attribute (not in any field)");
  else if (DECL_INITIAL (field) != NULL_TREE)
    warning ("duplicate ConstanValue atribute for field '%s'",
	     IDENTIFIER_POINTER (DECL_NAME (field)));
  else
    {
      DECL_INITIAL (field) = constant;
      if (FIELD_FINAL (field))
	DECL_FIELD_FINAL_IUD (field) = 1;
    }
}

/* Count the number of Unicode chars encoded in a given Ut8 string. */

#if 0
int
strLengthUtf8 (str, len)
     char *str;
     int len;
{
  register unsigned char* ptr = (unsigned char*) str;
  register unsigned char *limit = ptr + len;
  int str_length = 0;
  for (; ptr < limit; str_length++) {
    if (UTF8_GET (ptr, limit) < 0)
      return -1;
  }
  return str_length;
}
#endif


/* Calculate a hash value for a string encoded in Utf8 format.
 * This returns the same hash value as specified for java.lang.String.hashCode.
 */

static int32
hashUtf8String (str, len)
     const char *str;
     int len;
{
  register const unsigned char* ptr = (const unsigned char*) str;
  register const unsigned char *limit = ptr + len;
  int32 hash = 0;
  for (; ptr < limit;)
    {
      int ch = UTF8_GET (ptr, limit);
      /* Updated specification from
	 http://www.javasoft.com/docs/books/jls/clarify.html. */
      hash = (31 * hash) + ch;
    }
  return hash;
}

tree utf8_decl_list = NULL_TREE;

tree
build_utf8_ref (name)
     tree name;
{
  const char * name_ptr = IDENTIFIER_POINTER(name);
  int name_len = IDENTIFIER_LENGTH(name);
  char buf[60];
  char *buf_ptr;
  tree ctype, field = NULL_TREE, str_type, cinit, string;
  static int utf8_count = 0;
  int name_hash;
  tree ref = IDENTIFIER_UTF8_REF (name);
  tree decl;
  if (ref != NULL_TREE)
    return ref;

  ctype = make_node (RECORD_TYPE);
  str_type = build_prim_array_type (unsigned_byte_type_node,
				    name_len + 1); /* Allow for final '\0'. */
  PUSH_FIELD (ctype, field, "hash", unsigned_short_type_node);
  PUSH_FIELD (ctype, field, "length", unsigned_short_type_node);
  PUSH_FIELD (ctype, field, "data", str_type);
  FINISH_RECORD (ctype);
  START_RECORD_CONSTRUCTOR (cinit, ctype);
  name_hash = hashUtf8String (name_ptr, name_len) & 0xFFFF;
  PUSH_FIELD_VALUE (cinit, "hash", build_int_2 (name_hash, 0));
  PUSH_FIELD_VALUE (cinit, "length", build_int_2 (name_len, 0));
  string = build_string (name_len, name_ptr);
  TREE_TYPE (string) = str_type;
  PUSH_FIELD_VALUE (cinit, "data", string);
  FINISH_RECORD_CONSTRUCTOR (cinit);
  TREE_CONSTANT (cinit) = 1;

  /* Build a unique identifier based on buf. */
  sprintf(buf, "_Utf%d", ++utf8_count);
  buf_ptr = &buf[strlen (buf)];
  if (name_len > 0 && name_ptr[0] >= '0' && name_ptr[0] <= '9')
    *buf_ptr++ = '_';
  while (--name_len >= 0)
    {
      unsigned char c = *name_ptr++;
      if (c & 0x80)
	continue;
      if (!ISALPHA(c) && !ISDIGIT(c))
	c = '_';
      *buf_ptr++ = c;
      if (buf_ptr >= buf + 50)
	break;
    }
  *buf_ptr = '\0';

  decl = build_decl (VAR_DECL, get_identifier (buf), utf8const_type);
  /* FIXME get some way to force this into .text, not .data. */
  TREE_STATIC (decl) = 1;
  DECL_ARTIFICIAL (decl) = 1;
  DECL_IGNORED_P (decl) = 1;
  TREE_READONLY (decl) = 1;
  TREE_THIS_VOLATILE (decl) = 0;
  DECL_INITIAL (decl) = cinit;
  TREE_CHAIN (decl) = utf8_decl_list;
  layout_decl (decl, 0);
  pushdecl (decl);
  rest_of_decl_compilation (decl, (char*) 0, global_bindings_p (), 0);
  utf8_decl_list = decl;
  make_decl_rtl (decl, (char*) 0);
  ref = build1 (ADDR_EXPR, utf8const_ptr_type, decl);
  IDENTIFIER_UTF8_REF (name) = ref;
  return ref;
}

/* Build a reference to the class TYPE.
   Also handles primitive types and array types. */

tree
build_class_ref (type)
     tree type;
{
  int is_compiled = is_compiled_class (type);
  if (is_compiled)
    {
      tree ref, decl_name, decl;
      if (TREE_CODE (type) == POINTER_TYPE)
	type = TREE_TYPE (type);
      if (TREE_CODE (type) == RECORD_TYPE)
	{
	  if (TYPE_SIZE (type) == error_mark_node)
	    return null_pointer_node;
	  decl_name = identifier_subst (DECL_NAME (TYPE_NAME (type)),
					"", '/', '/', ".class");
	  decl = IDENTIFIER_GLOBAL_VALUE (decl_name);
	  if (decl == NULL_TREE)
	    {
	      decl = build_decl (VAR_DECL, decl_name, class_type_node);
	      DECL_SIZE (decl) = TYPE_SIZE (class_type_node);
	      DECL_SIZE_UNIT (decl) = TYPE_SIZE_UNIT (class_type_node);
	      TREE_STATIC (decl) = 1;
	      TREE_PUBLIC (decl) = 1;
	      DECL_IGNORED_P (decl) = 1;
	      DECL_ARTIFICIAL (decl) = 1;
	      SET_DECL_ASSEMBLER_NAME (decl, 
				       java_mangle_class_field
				       (&temporary_obstack, type));
	      make_decl_rtl (decl, NULL);
	      pushdecl_top_level (decl);
	      if (is_compiled == 1)
		DECL_EXTERNAL (decl) = 1;
	    }
	}
      else
	{
	  const char *name;
	  char buffer[25];
	  if (flag_emit_class_files)
	    {
	      const char *prim_class_name;
	      tree prim_class;
	      if (type == char_type_node)
		prim_class_name = "java.lang.Character";
	      else if (type == boolean_type_node)
		prim_class_name = "java.lang.Boolean";
	      else if (type == byte_type_node)
		prim_class_name = "java.lang.Byte";
	      else if (type == short_type_node)
		prim_class_name = "java.lang.Short";
	      else if (type == int_type_node)
		prim_class_name = "java.lang.Integer";
	      else if (type == long_type_node)
		prim_class_name = "java.lang.Long";
	      else if (type == float_type_node)
                prim_class_name = "java.lang.Float";
	      else if (type == double_type_node)
                prim_class_name = "java.lang.Double";
	      else if (type == void_type_node)
                prim_class_name = "java.lang.Void";
	      else
		abort ();

	      prim_class = lookup_class (get_identifier (prim_class_name));
	      return build (COMPONENT_REF, NULL_TREE,
			    prim_class, TYPE_identifier_node);
	    }
	  decl_name = TYPE_NAME (type);
	  if (TREE_CODE (decl_name) == TYPE_DECL)
	    decl_name = DECL_NAME (decl_name);
	  name = IDENTIFIER_POINTER (decl_name);
	  if (strncmp (name, "promoted_", 9) == 0)
	    name += 9;
	  sprintf (buffer, "_Jv_%sClass", name);
	  decl_name = get_identifier (buffer);
	  decl = IDENTIFIER_GLOBAL_VALUE (decl_name);
	  if (decl == NULL_TREE)
	    {
	      decl = build_decl (VAR_DECL, decl_name, class_type_node);
	      TREE_STATIC (decl) = 1;
	      TREE_PUBLIC (decl) = 1;
	      make_decl_rtl (decl, NULL);
	      pushdecl_top_level (decl);
	      if (is_compiled == 1)
		DECL_EXTERNAL (decl) = 1;
	    }
	}

      ref = build1 (ADDR_EXPR, class_ptr_type, decl);
      return ref;
    }
  else
    {
      int index;
      tree cl;
      index = alloc_class_constant (type);
      cl = build_ref_from_constant_pool (index); 
      TREE_TYPE (cl) = promote_type (class_ptr_type);
      return cl;
    }
}

tree
build_static_field_ref (fdecl)
     tree fdecl;
{
  tree fclass = DECL_CONTEXT (fdecl);
  int is_compiled = is_compiled_class (fclass);
  if (is_compiled)
    {
      if (!DECL_RTL_SET_P (fdecl))
	{
	  if (is_compiled == 1)
	    DECL_EXTERNAL (fdecl) = 1;
	  make_decl_rtl (fdecl, NULL);
	}
      return fdecl;
    }
  else
    {
      /* Compile as:
       * *(FTYPE*)build_class_ref(FCLASS)->fields[INDEX].info.addr */
      tree ref = build_class_ref (fclass);
      tree fld;
      int field_index = 0;
      ref = build1 (INDIRECT_REF, class_type_node, ref);
      ref = build (COMPONENT_REF, field_ptr_type_node, ref,
		   lookup_field (&class_type_node, fields_ident));

      for (fld = TYPE_FIELDS (fclass); ; fld = TREE_CHAIN (fld))
	{
	  if (fld == fdecl)
	    break;
	  if (fld == NULL_TREE)
	    fatal_error ("field '%s' not found in class",
			 IDENTIFIER_POINTER (DECL_NAME (fdecl)));
	  if (FIELD_STATIC (fld))
	    field_index++;
	}
      field_index *= int_size_in_bytes (field_type_node);
      ref = fold (build (PLUS_EXPR, field_ptr_type_node,
			 ref, build_int_2 (field_index, 0)));
      ref = build1 (INDIRECT_REF, field_type_node, ref);
      ref = build (COMPONENT_REF, field_info_union_node,
		   ref, lookup_field (&field_type_node, info_ident));
      ref = build (COMPONENT_REF, ptr_type_node,
		   ref, TREE_CHAIN (TYPE_FIELDS (field_info_union_node)));
      return fold (build1 (INDIRECT_REF, TREE_TYPE(fdecl), ref));
    }
}

int
get_access_flags_from_decl (decl)
     tree decl;
{
  int access_flags = 0;
  if (TREE_CODE (decl) == FIELD_DECL || TREE_CODE (decl) == VAR_DECL)
    {
      if (FIELD_STATIC (decl))
	access_flags |= ACC_STATIC;
      if (FIELD_PUBLIC (decl))
	access_flags |= ACC_PUBLIC;
      if (FIELD_PROTECTED (decl))
	access_flags |= ACC_PROTECTED;
      if (FIELD_PRIVATE (decl))
	access_flags |= ACC_PRIVATE;
      if (FIELD_FINAL (decl))
	access_flags |= ACC_FINAL;
      if (FIELD_VOLATILE (decl))
	access_flags |= ACC_VOLATILE;
      if (FIELD_TRANSIENT (decl))
	access_flags |= ACC_TRANSIENT;
      return access_flags;
    }
  if (TREE_CODE (decl) == TYPE_DECL)
    {
      if (CLASS_PUBLIC (decl))
	access_flags |= ACC_PUBLIC;
      if (CLASS_FINAL (decl))
	access_flags |= ACC_FINAL;
      if (CLASS_SUPER (decl))
	access_flags |= ACC_SUPER;
      if (CLASS_INTERFACE (decl))
	access_flags |= ACC_INTERFACE;
      if (CLASS_ABSTRACT (decl))
	access_flags |= ACC_ABSTRACT;
      if (CLASS_STATIC (decl))
	access_flags |= ACC_STATIC;
      if (CLASS_PRIVATE (decl))
	access_flags |= ACC_PRIVATE;
      if (CLASS_PROTECTED (decl))
	access_flags |= ACC_PROTECTED;
      return access_flags;
    }
  if (TREE_CODE (decl) == FUNCTION_DECL)
    {
      if (METHOD_PUBLIC (decl))
	access_flags |= ACC_PUBLIC;
      if (METHOD_PRIVATE (decl))
	access_flags |= ACC_PRIVATE;
      if (METHOD_PROTECTED (decl))
	access_flags |= ACC_PROTECTED;
      if (METHOD_STATIC (decl))
	access_flags |= ACC_STATIC;
      if (METHOD_FINAL (decl))
	access_flags |= ACC_FINAL;
      if (METHOD_SYNCHRONIZED (decl))
	access_flags |= ACC_SYNCHRONIZED;
      if (METHOD_NATIVE (decl))
	access_flags |= ACC_NATIVE;
      if (METHOD_ABSTRACT (decl))
	access_flags |= ACC_ABSTRACT;
      if (METHOD_TRANSIENT (decl))
	access_flags |= ACC_TRANSIENT;
      return access_flags;
    }
  abort ();
}

static tree
make_field_value (fdecl)
  tree fdecl;
{
  tree finit;
  int flags;
  tree type = TREE_TYPE (fdecl);
  int resolved = is_compiled_class (type);

  START_RECORD_CONSTRUCTOR (finit, field_type_node);
  PUSH_FIELD_VALUE (finit, "name", build_utf8_ref (DECL_NAME (fdecl)));
  if (resolved)
    type = build_class_ref (type);
  else
    {
      tree signature = build_java_signature (type);

      type = build_utf8_ref (unmangle_classname 
			     (IDENTIFIER_POINTER (signature),
			      IDENTIFIER_LENGTH (signature)));
    }
  PUSH_FIELD_VALUE (finit, "type", type);

  flags = get_access_flags_from_decl (fdecl);
  if (! resolved)
    flags |= 0x8000 /* FIELD_UNRESOLVED_FLAG */;

  PUSH_FIELD_VALUE (finit, "accflags", build_int_2 (flags, 0));
  PUSH_FIELD_VALUE (finit, "bsize", TYPE_SIZE_UNIT (TREE_TYPE (fdecl)));

  PUSH_FIELD_VALUE
    (finit, "info",
     build (CONSTRUCTOR, field_info_union_node, NULL_TREE,
	    build_tree_list
	    ((FIELD_STATIC (fdecl)
	      ? TREE_CHAIN (TYPE_FIELDS (field_info_union_node))
	      : TYPE_FIELDS (field_info_union_node)),
	     (FIELD_STATIC (fdecl)
	      ? build_address_of (build_static_field_ref (fdecl))
	      : byte_position (fdecl)))));

  FINISH_RECORD_CONSTRUCTOR (finit);
  return finit;
}

static tree
make_method_value (mdecl)
     tree mdecl;
{
  tree minit;
  tree code;
#define ACC_TRANSLATED          0x4000
  int accflags = get_access_flags_from_decl (mdecl) | ACC_TRANSLATED;
  code = null_pointer_node;
  if (DECL_RTL_SET_P (mdecl))
    code = build1 (ADDR_EXPR, nativecode_ptr_type_node, mdecl);
  START_RECORD_CONSTRUCTOR (minit, method_type_node);
  PUSH_FIELD_VALUE (minit, "name",
		    build_utf8_ref (DECL_CONSTRUCTOR_P (mdecl) ?
				    init_identifier_node
				    : DECL_NAME (mdecl)));
  {
    tree signature = build_java_signature (TREE_TYPE (mdecl));
    PUSH_FIELD_VALUE (minit, "signature", 
		      (build_utf8_ref 
		       (unmangle_classname 
			(IDENTIFIER_POINTER(signature),
			 IDENTIFIER_LENGTH(signature)))));
  }
  PUSH_FIELD_VALUE (minit, "accflags", build_int_2 (accflags, 0));
  PUSH_FIELD_VALUE (minit, "ncode", code);
  FINISH_RECORD_CONSTRUCTOR (minit);
  return minit;
}

static tree
get_dispatch_vector (type)
     tree type;
{
  tree vtable = TYPE_VTABLE (type);
  if (vtable == NULL)
    {
      HOST_WIDE_INT i;
      tree method;
      tree super = CLASSTYPE_SUPER (type);
      HOST_WIDE_INT nvirtuals = tree_low_cst (TYPE_NVIRTUALS (type), 0);
      vtable = make_tree_vec (nvirtuals);
      TYPE_VTABLE (type) = vtable;
      if (super != NULL_TREE)
	{
	  tree super_vtable = get_dispatch_vector (super);

	  for (i = tree_low_cst (TYPE_NVIRTUALS (super), 0); --i >= 0; )
	    TREE_VEC_ELT (vtable, i) = TREE_VEC_ELT (super_vtable, i);
	}

      for (method = TYPE_METHODS (type);  method != NULL_TREE;
	   method = TREE_CHAIN (method))
	if (DECL_VINDEX (method) != NULL_TREE
	    && host_integerp (DECL_VINDEX (method), 0))
	  TREE_VEC_ELT (vtable, tree_low_cst (DECL_VINDEX (method), 0))
	    = method;
    }

  return vtable;
}

static tree
get_dispatch_table (type, this_class_addr)
     tree type, this_class_addr;
{
  int abstract_p = CLASS_ABSTRACT (TYPE_NAME (type));
  tree vtable = get_dispatch_vector (type);
  int i;
  tree list = NULL_TREE;
  int nvirtuals = TREE_VEC_LENGTH (vtable);
  for (i = nvirtuals;  --i >= 0; )
    {
      tree method = TREE_VEC_ELT (vtable, i);
      if (METHOD_ABSTRACT (method))
	{
	  if (! abstract_p)
	    warning_with_decl (method,
			       "abstract method in non-abstract class");
	  method = null_pointer_node;
	}
      else
	{
	  if (!DECL_RTL_SET_P (method))
	    make_decl_rtl (method, NULL);
	  method = build1 (ADDR_EXPR, nativecode_ptr_type_node, method);
	}
      list = tree_cons (NULL_TREE /*DECL_VINDEX (method) + 2*/,
			method, list);
    }
  /* Dummy entry for compatibility with G++ -fvtable-thunks.  When
     using the Boehm GC we sometimes stash a GC type descriptor
     there. We set the PURPOSE to NULL_TREE not to interfere (reset)
     the emitted byte count during the output to the assembly file. */
  list = tree_cons (NULL_TREE, get_boehm_type_descriptor (type),
		    list);
  list = tree_cons (integer_zero_node, this_class_addr, list);
  return build (CONSTRUCTOR, build_prim_array_type (nativecode_ptr_type_node,
						    nvirtuals + 2),
		 NULL_TREE, list);
}

void
make_class_data (type)
     tree type;
{
  tree decl, cons, temp;
  tree field, fields_decl;
  tree static_fields = NULL_TREE;
  tree instance_fields = NULL_TREE;
  HOST_WIDE_INT static_field_count = 0;
  HOST_WIDE_INT instance_field_count = 0;
  HOST_WIDE_INT field_count;
  tree field_array_type;
  tree method;
  tree methods = NULL_TREE;
  tree dtable_decl = NULL_TREE;
  HOST_WIDE_INT method_count = 0;
  tree method_array_type;
  tree methods_decl;
  tree super;
  tree this_class_addr;
  tree constant_pool_constructor;
  tree interfaces = null_pointer_node;
  int interface_len = 0;
  tree type_decl = TYPE_NAME (type);

  this_class_addr = build_class_ref (type);
  decl = TREE_OPERAND (this_class_addr, 0);

  /* Build Field array. */
  field = TYPE_FIELDS (type);
  if (DECL_NAME (field) == NULL_TREE)
    field = TREE_CHAIN (field);  /* Skip dummy field for inherited data. */
  for ( ;  field != NULL_TREE;  field = TREE_CHAIN (field))
    {
      if (! DECL_ARTIFICIAL (field))
	{
	  tree init = make_field_value (field);
	  if (FIELD_STATIC (field))
	    {
	      tree initial = DECL_INITIAL (field);
	      static_field_count++;
	      static_fields = tree_cons (NULL_TREE, init, static_fields);
	      /* If the initial value is a string constant,
		 prevent output_constant from trying to assemble the value. */
	      if (initial != NULL_TREE
		  && TREE_TYPE (initial) == string_ptr_type_node)
		DECL_INITIAL (field) = NULL_TREE;
	      rest_of_decl_compilation (field, (char*) 0, 1, 1);
	      DECL_INITIAL (field) = initial;
	    }
	  else
	    {
	      instance_field_count++;
	      instance_fields = tree_cons (NULL_TREE, init, instance_fields);
	    }
	}
    }
  field_count = static_field_count + instance_field_count;
  if (field_count > 0)
    {
      static_fields = nreverse (static_fields);
      instance_fields = nreverse (instance_fields);
      static_fields = chainon (static_fields, instance_fields);
      field_array_type = build_prim_array_type (field_type_node, field_count);
      fields_decl = build_decl (VAR_DECL, mangled_classname ("_FL_", type),
				field_array_type);
      DECL_INITIAL (fields_decl) = build (CONSTRUCTOR, field_array_type,
					  NULL_TREE, static_fields);
      TREE_STATIC (fields_decl) = 1;
      DECL_ARTIFICIAL (fields_decl) = 1;
      DECL_IGNORED_P (fields_decl) = 1;
      rest_of_decl_compilation (fields_decl, (char*) 0, 1, 0);
    }
  else
    fields_decl = NULL_TREE;

  /* Build Method array. */
  for (method = TYPE_METHODS (CLASS_TO_HANDLE_TYPE (type));
       method != NULL_TREE; method = TREE_CHAIN (method))
    {
      tree init;
      if (METHOD_PRIVATE (method)
	  && ! flag_keep_inline_functions
	  && (flag_inline_functions || optimize))
	continue;
      init = make_method_value (method);
      method_count++;
      methods = tree_cons (NULL_TREE, init, methods);
    }
  method_array_type = build_prim_array_type (method_type_node, method_count);
  methods_decl = build_decl (VAR_DECL, mangled_classname ("_MT_", type),
			     method_array_type);
  DECL_INITIAL (methods_decl) = build (CONSTRUCTOR, method_array_type,
				       NULL_TREE, nreverse (methods));
  TREE_STATIC (methods_decl) = 1;
  DECL_ARTIFICIAL (methods_decl) = 1;
  DECL_IGNORED_P (methods_decl) = 1;
  rest_of_decl_compilation (methods_decl, (char*) 0, 1, 0);

  if (assume_compiled (IDENTIFIER_POINTER (DECL_NAME (type_decl)))
      && ! CLASS_INTERFACE (type_decl))
    {
      tree dtable = get_dispatch_table (type, this_class_addr);
      dtable_decl = build_dtable_decl (type);
      DECL_INITIAL (dtable_decl) = dtable;
      TREE_STATIC (dtable_decl) = 1;
      DECL_ARTIFICIAL (dtable_decl) = 1;
      DECL_IGNORED_P (dtable_decl) = 1;
      TREE_PUBLIC (dtable_decl) = 1;
      rest_of_decl_compilation (dtable_decl, (char*) 0, 1, 0);
      if (type == class_type_node)
	class_dtable_decl = dtable_decl;
    }

  if (class_dtable_decl == NULL_TREE)
    {
      class_dtable_decl = build_dtable_decl (class_type_node);
      TREE_STATIC (class_dtable_decl) = 1;
      DECL_ARTIFICIAL (class_dtable_decl) = 1;
      DECL_IGNORED_P (class_dtable_decl) = 1;
      if (is_compiled_class (class_type_node) != 2)
	DECL_EXTERNAL (class_dtable_decl) = 1;
      rest_of_decl_compilation (class_dtable_decl, (char*) 0, 1, 0);
    }

  super = CLASSTYPE_SUPER (type);
  if (super == NULL_TREE)
    super = null_pointer_node;
  else if (assume_compiled (IDENTIFIER_POINTER (DECL_NAME (type_decl))))
    super = build_class_ref (super);
  else
    {
      int super_index = alloc_class_constant (super);
      super = build_int_2 (super_index, 0);
      TREE_TYPE (super) = ptr_type_node;
    }

  /* Build and emit the array of implemented interfaces. */
  if (type != object_type_node)
      interface_len = TREE_VEC_LENGTH (TYPE_BINFO_BASETYPES (type)) - 1;
  if (interface_len > 0)
    {
      tree init = NULL_TREE;
      int i;
      tree interface_array_type, idecl;
      interface_array_type
	= build_prim_array_type (class_ptr_type, interface_len);
      idecl = build_decl (VAR_DECL, mangled_classname ("_IF_", type),
			  interface_array_type);
      for (i = interface_len;  i > 0; i--)
	{
	  tree child = TREE_VEC_ELT (TYPE_BINFO_BASETYPES (type), i);
	  tree iclass = BINFO_TYPE (child);
	  tree index;
	  if (assume_compiled (IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (iclass)))))
	    index = build_class_ref (iclass);
	  else
	    {
		int int_index = alloc_class_constant (iclass);
		index = build_int_2 (int_index, 0);
		TREE_TYPE (index) = ptr_type_node;
	    }
	  init = tree_cons (NULL_TREE, index, init); 
	}
      DECL_INITIAL (idecl) = build (CONSTRUCTOR, interface_array_type,
				    NULL_TREE, init);
      TREE_STATIC (idecl) = 1;
      DECL_ARTIFICIAL (idecl) = 1;
      DECL_IGNORED_P (idecl) = 1;
      interfaces = build1 (ADDR_EXPR, ptr_type_node, idecl);
      rest_of_decl_compilation (idecl,  (char*) 0, 1, 0);
    }

  constant_pool_constructor = build_constants_constructor ();

  START_RECORD_CONSTRUCTOR (temp, object_type_node);
  PUSH_FIELD_VALUE (temp, "vtable",
		    build1 (ADDR_EXPR, dtable_ptr_type, class_dtable_decl));
  if (! flag_hash_synchronization)
    PUSH_FIELD_VALUE (temp, "sync_info", null_pointer_node);
  FINISH_RECORD_CONSTRUCTOR (temp);
  START_RECORD_CONSTRUCTOR (cons, class_type_node);
  PUSH_SUPER_VALUE (cons, temp);
  PUSH_FIELD_VALUE (cons, "next", null_pointer_node);
  PUSH_FIELD_VALUE (cons, "name", build_utf8_ref (DECL_NAME (type_decl)));
  PUSH_FIELD_VALUE (cons, "accflags",
		    build_int_2 (get_access_flags_from_decl (type_decl), 0));

  PUSH_FIELD_VALUE (cons, "superclass", 
		    CLASS_INTERFACE (type_decl) ? null_pointer_node : super);
  PUSH_FIELD_VALUE (cons, "constants", constant_pool_constructor);
  PUSH_FIELD_VALUE (cons, "methods",
		    build1 (ADDR_EXPR, method_ptr_type_node, methods_decl));
  PUSH_FIELD_VALUE (cons, "method_count",  build_int_2 (method_count, 0));
  PUSH_FIELD_VALUE (cons, "vtable_method_count", TYPE_NVIRTUALS (type));
  PUSH_FIELD_VALUE (cons, "fields",
		    fields_decl == NULL_TREE ? null_pointer_node
		    : build1 (ADDR_EXPR, field_ptr_type_node, fields_decl));
  PUSH_FIELD_VALUE (cons, "size_in_bytes", size_in_bytes (type));
  PUSH_FIELD_VALUE (cons, "field_count", build_int_2 (field_count, 0));
  PUSH_FIELD_VALUE (cons, "static_field_count",
		    build_int_2 (static_field_count, 0));
  PUSH_FIELD_VALUE (cons, "vtable",
		    dtable_decl == NULL_TREE ? null_pointer_node
		    : build1 (ADDR_EXPR, dtable_ptr_type, dtable_decl));
  PUSH_FIELD_VALUE (cons, "interfaces", interfaces);
  PUSH_FIELD_VALUE (cons, "loader", null_pointer_node);
  PUSH_FIELD_VALUE (cons, "interface_count", build_int_2 (interface_len, 0));
  PUSH_FIELD_VALUE (cons, "state", integer_zero_node);

  PUSH_FIELD_VALUE (cons, "thread", null_pointer_node);
  PUSH_FIELD_VALUE (cons, "depth", integer_zero_node);
  PUSH_FIELD_VALUE (cons, "ancestors", null_pointer_node);
  PUSH_FIELD_VALUE (cons, "idt", null_pointer_node);
  PUSH_FIELD_VALUE (cons, "arrayclass", null_pointer_node);

  FINISH_RECORD_CONSTRUCTOR (cons);

  DECL_INITIAL (decl) = cons;
  rest_of_decl_compilation (decl, (char*) 0, 1, 0);
}

void
finish_class ()
{
  tree method;
  tree type_methods = TYPE_METHODS (CLASS_TO_HANDLE_TYPE (current_class));
  int saw_native_method = 0;

  /* Find out if we have any native methods.  We use this information
     later.  */
  for (method = type_methods;
       method != NULL_TREE;
       method = TREE_CHAIN (method))
    {
      if (METHOD_NATIVE (method))
	{
	  saw_native_method = 1;
	  break;
	}
    }

  /* Emit deferred inline methods. */  
  for (method = type_methods; method != NULL_TREE; )
    {
      if (! TREE_ASM_WRITTEN (method) && DECL_SAVED_INSNS (method) != 0)
	{
	  /* It's a deferred inline method.  Decide if we need to emit it. */
	  if (flag_keep_inline_functions
	      || TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (method))
	      || ! METHOD_PRIVATE (method)
	      || saw_native_method)
	    {
	      output_inline_function (method);
	      /* Scan the list again to see if there are any earlier
                 methods to emit. */
	      method = type_methods;
	      continue;
	    }
	}
      method = TREE_CHAIN (method);
    }

  current_function_decl = NULL_TREE;
  make_class_data (current_class);
  register_class ();
  rest_of_decl_compilation (TYPE_NAME (current_class), (char*) 0, 1, 0);
}

/* Return 2 if CLASS is compiled by this compilation job;
   return 1 if CLASS can otherwise be assumed to be compiled;
   return 0 if we cannot assume that CLASS is compiled.
   Returns 1 for primitive and 0 for array types.  */
int
is_compiled_class (class)
     tree class;
{
  int seen_in_zip;
  if (TREE_CODE (class) == POINTER_TYPE)
    class = TREE_TYPE (class);
  if (TREE_CODE (class) != RECORD_TYPE)  /* Primitive types are static. */
    return 1;
  if (TYPE_ARRAY_P (class))
    return 0;
  if (class == current_class)
    return 2;

  seen_in_zip = (TYPE_JCF (class) && JCF_SEEN_IN_ZIP (TYPE_JCF (class)));
  if (CLASS_FROM_CURRENTLY_COMPILED_P (class) || seen_in_zip)
    {
      /* The class was seen in the current ZIP file and will be
	 available as a compiled class in the future but may not have
	 been loaded already. Load it if necessary. This prevent
	 build_class_ref () from crashing. */

      if (seen_in_zip && !CLASS_LOADED_P (class))
        load_class (class, 1);

      /* We return 2 for class seen in ZIP and class from files
         belonging to the same compilation unit */
      return 2;
    }

  if (assume_compiled (IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (class)))))
    {
      if (!CLASS_LOADED_P (class))
	{
	  if (CLASS_FROM_SOURCE_P (class))
	    safe_layout_class (class);
	  else
	    load_class (class, 1);
	}
      return 1;
    }

  return 0;
}

/* Build a VAR_DECL for the dispatch table (vtable) for class TYPE. */

tree
build_dtable_decl (type)
     tree type;
{
  tree dtype;

  /* We need to build a new dtable type so that its size is uniquely
     computed when we're dealing with the class for real and not just
     faking it (like java.lang.Class during the initialization of the
     compiler.) We now we're not faking a class when CURRENT_CLASS is
     TYPE. */
  if (current_class == type)
    {
      tree dummy = NULL_TREE, aomt, n;

      dtype = make_node (RECORD_TYPE);
      PUSH_FIELD (dtype, dummy, "class", class_ptr_type);
      n = build_int_2 (TREE_VEC_LENGTH (get_dispatch_vector (type)), 0);
      aomt = build_array_type (ptr_type_node, build_index_type (n));
      PUSH_FIELD (dtype, dummy, "methods", aomt);
      layout_type (dtype);
    }
  else
    dtype = dtable_type;

  return build_decl (VAR_DECL, 
		     java_mangle_vtable (&temporary_obstack, type), dtype);
}

/* Pre-pend the TYPE_FIELDS of THIS_CLASS with a dummy FIELD_DECL for the
   fields inherited from SUPER_CLASS. */

void
push_super_field (this_class, super_class)
     tree this_class, super_class;
{
  tree base_decl;
  /* Don't insert the field if we're just re-laying the class out. */ 
  if (TYPE_FIELDS (this_class) && !DECL_NAME (TYPE_FIELDS (this_class)))
    return;
  base_decl = build_decl (FIELD_DECL, NULL_TREE, super_class);
  DECL_IGNORED_P (base_decl) = 1;
  TREE_CHAIN (base_decl) = TYPE_FIELDS (this_class);
  TYPE_FIELDS (this_class) = base_decl;
  DECL_SIZE (base_decl) = TYPE_SIZE (super_class);
  DECL_SIZE_UNIT (base_decl) = TYPE_SIZE_UNIT (super_class);
}

/* Handle the different manners we may have to lay out a super class.  */

static tree
maybe_layout_super_class (super_class, this_class)
     tree super_class;
     tree this_class;
{
  if (TREE_CODE (super_class) == RECORD_TYPE)
    {
      if (!CLASS_LOADED_P (super_class) && CLASS_FROM_SOURCE_P (super_class))
	safe_layout_class (super_class);
      if (!CLASS_LOADED_P (super_class))
	load_class (super_class, 1);
    }
  /* We might have to layout the class before its dependency on
     the super class gets resolved by java_complete_class  */
  else if (TREE_CODE (super_class) == POINTER_TYPE)
    {
      if (TREE_TYPE (super_class) != NULL_TREE)
	super_class = TREE_TYPE (super_class);
      else
	{
	  super_class = do_resolve_class (NULL_TREE, /* FIXME? */
					  super_class, NULL_TREE, this_class);
	  if (!super_class)
	    return NULL_TREE;	/* FIXME, NULL_TREE not checked by caller. */
	  super_class = TREE_TYPE (super_class);
	}
    }
  if (!TYPE_SIZE (super_class))
    safe_layout_class (super_class);

  return super_class;
}

void
layout_class (this_class)
     tree this_class;
{
  tree super_class = CLASSTYPE_SUPER (this_class);
  tree field;
  
  class_list = tree_cons (this_class, NULL_TREE, class_list);
  if (CLASS_BEING_LAIDOUT (this_class))
    {
      char buffer [1024];
      char *report;
      tree current;
      
      sprintf (buffer, " with `%s'",
	       IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (this_class))));
      obstack_grow (&temporary_obstack, buffer, strlen (buffer));

      for (current = TREE_CHAIN (class_list); current; 
	   current = TREE_CHAIN (current))
	{
	  tree decl = TYPE_NAME (TREE_PURPOSE (current));
	  sprintf (buffer, "\n  which inherits from `%s' (%s:%d)",
		   IDENTIFIER_POINTER (DECL_NAME (decl)),
		   DECL_SOURCE_FILE (decl),
		   DECL_SOURCE_LINE (decl));
	  obstack_grow (&temporary_obstack, buffer, strlen (buffer));
	}
      obstack_1grow (&temporary_obstack, '\0');
      report = obstack_finish (&temporary_obstack);
      cyclic_inheritance_report = ggc_strdup (report);
      obstack_free (&temporary_obstack, report);
      TYPE_SIZE (this_class) = error_mark_node;
      return;
    }
  CLASS_BEING_LAIDOUT (this_class) = 1;

  if (super_class && !CLASS_BEING_LAIDOUT (super_class))
    {
      tree maybe_super_class 
	= maybe_layout_super_class (super_class, this_class);
      if (maybe_super_class == NULL
	  || TREE_CODE (TYPE_SIZE (maybe_super_class)) == ERROR_MARK)
	{
	  TYPE_SIZE (this_class) = error_mark_node;
	  CLASS_BEING_LAIDOUT (this_class) = 0;
	  class_list = TREE_CHAIN (class_list);
	  return;
	}
      if (TYPE_SIZE (this_class) == NULL_TREE)
	push_super_field (this_class, maybe_super_class);
    }

  for (field = TYPE_FIELDS (this_class);
       field != NULL_TREE;  field = TREE_CHAIN (field))
    {
      if (FIELD_STATIC (field))
	{
	  /* Set DECL_ASSEMBLER_NAME to something suitably mangled. */
	  SET_DECL_ASSEMBLER_NAME (field,
				   java_mangle_decl
				   (&temporary_obstack, field));
	}
    }

  layout_type (this_class);

  /* Also recursively load/layout any superinterfaces, but only if class was
  loaded from bytecode. The source parser will take care of this itself. */
  if (!CLASS_FROM_SOURCE_P (this_class))
    {
      tree basetype_vec = TYPE_BINFO_BASETYPES (this_class);

      if (basetype_vec)
	{
	  int n = TREE_VEC_LENGTH (basetype_vec) - 1;
	  int i;
	  for (i = n; i > 0; i--)
	    {
	      tree vec_elt = TREE_VEC_ELT (basetype_vec, i);
	      tree super_interface = BINFO_TYPE (vec_elt);

	      tree maybe_super_interface 
		= maybe_layout_super_class (super_interface, NULL_TREE);
	      if (maybe_super_interface == NULL
		  || TREE_CODE (TYPE_SIZE (maybe_super_interface)) == ERROR_MARK)
		{
		  TYPE_SIZE (this_class) = error_mark_node;
		  CLASS_BEING_LAIDOUT (this_class) = 0;
		  class_list = TREE_CHAIN (class_list);
		  return;
		}
	    }
	}
    }

  /* Convert the size back to an SI integer value */
  TYPE_SIZE_UNIT (this_class) = 
    fold (convert (int_type_node, TYPE_SIZE_UNIT (this_class)));

  CLASS_BEING_LAIDOUT (this_class) = 0;
  class_list = TREE_CHAIN (class_list);
}

void
layout_class_methods (this_class)
     tree this_class;
{
  tree method_decl, dtable_count;
  tree super_class, handle_type;

  if (TYPE_NVIRTUALS (this_class))
    return;

  super_class = CLASSTYPE_SUPER (this_class);
  handle_type = CLASS_TO_HANDLE_TYPE (this_class);

  if (super_class)
    {
      super_class = maybe_layout_super_class (super_class, this_class);
      if (!TYPE_NVIRTUALS (super_class))
	layout_class_methods (super_class);
      dtable_count = TYPE_NVIRTUALS (super_class);
    }
  else
    dtable_count = integer_zero_node;

  TYPE_METHODS (handle_type) = nreverse (TYPE_METHODS (handle_type));

  for (method_decl = TYPE_METHODS (handle_type);
       method_decl; method_decl = TREE_CHAIN (method_decl))
    dtable_count = layout_class_method (this_class, super_class, 
					method_decl, dtable_count);

  TYPE_NVIRTUALS (this_class) = dtable_count;

#ifdef JAVA_USE_HANDLES
  layout_type (handle_type);
#endif
}

/* Return 0 if NAME is equal to STR, -1 if STR is "less" than NAME,
   and 1 if STR is "greater" than NAME.  */

/* Lay METHOD_DECL out, returning a possibly new value of
   DTABLE_COUNT. Also mangle the method's name. */

tree
layout_class_method (this_class, super_class, method_decl, dtable_count)
     tree this_class, super_class, method_decl, dtable_count;
{
  tree method_name = DECL_NAME (method_decl);

  TREE_PUBLIC (method_decl) = 1;

  /* This is a good occasion to mangle the method's name */
  SET_DECL_ASSEMBLER_NAME (method_decl,
			   java_mangle_decl (&temporary_obstack, 
					     method_decl));
  /* We don't generate a RTL for the method if it's abstract, or if
     it's an interface method that isn't clinit. */
  if (! METHOD_ABSTRACT (method_decl) 
      || (CLASS_INTERFACE (TYPE_NAME (this_class)) 
	  && (DECL_CLINIT_P (method_decl))))
    make_decl_rtl (method_decl, NULL);

  if (ID_INIT_P (method_name))
    {
      const char *p = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (this_class)));
      const char *ptr;
      for (ptr = p; *ptr; )
	{
	  if (*ptr++ == '.')
	    p = ptr;
	}
      DECL_CONSTRUCTOR_P (method_decl) = 1;
      build_java_argument_signature (TREE_TYPE (method_decl));
    }
  else if (! METHOD_STATIC (method_decl) && !DECL_ARTIFICIAL (method_decl))
    {
      tree method_sig = 
	build_java_argument_signature (TREE_TYPE (method_decl));
      tree super_method = lookup_argument_method (super_class, method_name,
						  method_sig);
      if (super_method != NULL_TREE && ! METHOD_PRIVATE (super_method))
	{
	  DECL_VINDEX (method_decl) = DECL_VINDEX (super_method);
	  if (DECL_VINDEX (method_decl) == NULL_TREE 
	      && !CLASS_FROM_SOURCE_P (this_class))
	    error_with_decl (method_decl,
			     "non-static method '%s' overrides static method");
	}
      else if (! METHOD_FINAL (method_decl)
	       && ! METHOD_PRIVATE (method_decl)
	       && ! CLASS_FINAL (TYPE_NAME (this_class))
	       && dtable_count)
	{
	  DECL_VINDEX (method_decl) = dtable_count;
	  dtable_count = fold (build (PLUS_EXPR, integer_type_node,
				      dtable_count, integer_one_node));
	}
    }

  return dtable_count;
}

void
register_class ()
{
  /* END does not need to be registered with the garbage collector
     because it always points into the list given by REGISTERED_CLASS,
     and that variable is registered with the collector.  */
  static tree end;
  tree node    = TREE_OPERAND (build_class_ref (current_class), 0);
  tree current = copy_node (node);

  XEXP (DECL_RTL (current), 0) = copy_rtx (XEXP (DECL_RTL(node), 0));
  if (!registered_class)
    registered_class = current;
  else
    TREE_CHAIN (end) = current;

  end = current;
}

/* Generate a function that gets called at start-up (static contructor) time,
   which calls registerClass for all the compiled classes. */

void
emit_register_classes ()
{
  extern tree get_file_function_name PARAMS ((int));
  tree init_name = get_file_function_name ('I');
  tree init_type = build_function_type (void_type_node, end_params_node);
  tree init_decl;
  tree t;

  init_decl = build_decl (FUNCTION_DECL, init_name, init_type);
  SET_DECL_ASSEMBLER_NAME (init_decl, init_name);
  TREE_STATIC (init_decl) = 1;
  current_function_decl = init_decl;
  DECL_RESULT (init_decl) = build_decl(RESULT_DECL, NULL_TREE, void_type_node);
  /*  DECL_EXTERNAL (init_decl) = 1;*/
  TREE_PUBLIC (init_decl) = 1;
  pushlevel (0);
  make_decl_rtl (init_decl, NULL);
  init_function_start (init_decl, input_filename, 0);
  expand_function_start (init_decl, 0);

  for ( t = registered_class; t; t = TREE_CHAIN (t))
    emit_library_call (registerClass_libfunc, 0, VOIDmode, 1,
		       XEXP (DECL_RTL (t), 0), Pmode);

  expand_function_end (input_filename, 0, 0);
  poplevel (1, 0, 1);
  { 
    /* Force generation, even with -O3 or deeper. Gross hack. FIXME */
    int saved_flag = flag_inline_functions;
    flag_inline_functions = 0;	
    rest_of_compilation (init_decl);
    flag_inline_functions = saved_flag;
  }
  current_function_decl = NULL_TREE;
  assemble_constructor (IDENTIFIER_POINTER (init_name));
}

void
init_class_processing ()
{
  registerClass_libfunc = gen_rtx (SYMBOL_REF, Pmode, "_Jv_RegisterClass");
  ggc_add_tree_root (class_roots, sizeof (class_roots) / sizeof (tree));
  fields_ident = get_identifier ("fields");
  info_ident = get_identifier ("info");
  ggc_add_rtx_root (&registerClass_libfunc, 1);
  gcc_obstack_init (&temporary_obstack);
}
