//===-------- JavaClass.h - Java class representation -------------------===//
//
//                            The VMKit project
//
// This file is distributed under the University of Illinois Open Source 
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#ifndef JNJVM_JAVA_CLASS_H
#define JNJVM_JAVA_CLASS_H


#include "types.h"

#include "vmkit/Allocator.h"
#include "vmkit/MethodInfo.h"
#include "vmkit/Cond.h"
#include "vmkit/Locks.h"

#include "JavaAccess.h"
#include "JavaObject.h"
#include "JnjvmClassLoader.h"
#include "JnjvmConfig.h"
#include "Jnjvm.h"
#include "JavaTypes.h"
#include "JavaThread.h"

#include <cassert>
#include <set>

namespace j3 {

class ArrayUInt8;
class ArrayUInt16;
class Class;
class ClassArray;
class ClassBytes;
class JavaArray;
class JavaConstantPool;
class JavaField;
class JavaMethod;
class JavaObject;
class JavaVirtualTable;
class Reader;
class Signdef;
class Typedef;

template <class T> class TJavaArray;
typedef TJavaArray<JavaObject*> ArrayObject;

/// JavaState - List of states a Java class can have. A class is ready to be
/// used (i.e allocating instances of the class, calling methods of the class
/// and accessing static fields of the class) when it is in the ready state.
///
#define loaded 0       /// The .class file has been found.
#define resolving 1    /// The .class file is being resolved.
#define resolved 2     /// The class has been resolved.
#define vmjc 3         /// The class is defined in a shared library.
#define inClinit 4     /// The class is cliniting.
#define ready 5        /// The class is ready to be used.
#define erroneous 6    /// The class is in an erroneous state.


extern "C" bool IsSubtypeIntrinsic(JavaVirtualTable* obj, JavaVirtualTable* clazz);
extern "C" bool CheckIfObjectIsAssignableToArrayPosition(JavaObject * obj, JavaObject* array);

class AnnotationReader {
public:
  Reader& reader;
  Class* cl;
  uint16 AnnotationNameIndex;

  AnnotationReader(Reader& R, Class* C) : reader(R), cl(C),
    AnnotationNameIndex(0) {}
  void readAnnotation();
  void readElementValue();
  
  
  void readAnnotationElementValues();

  // createAnnotation - search for a specific annotation in annotation 
  // attributes.
  //
  JavaObject* createAnnotationMapValues(JavaObject* type);
  
  // createElementValue - create the Java type associated with the value
  // of the current annotation key.
  //
  JavaObject* createElementValue(bool nextParameterIsTypeOfMethod, JavaObject* type, const UTF8* lastKey);

  void fillArray(JavaObject* res, int numValues, UserClassArray* classArray);
};

/// Attribute - This class represents JVM attributes to Java class, methods and
/// fields located in the .class file.
///
class JavaAttribute : public vmkit::PermanentObject {
public:
  
  /// name - The name of the attribute. These are specified in the JVM book.
  /// Experimental attributes exist, but the JnJVM does nor parse them.
  ///
  const UTF8* name;

  /// start - The offset in the class of this attribute.
  ///
  uint32 start;

  /// nbb - The size of the attribute.
  ///
  uint32 nbb;

  /// Attribute - Create an attribute at the given length and offset.
  ///
  JavaAttribute(const UTF8* name, uint32 length, uint32 offset);
  JavaAttribute() {}

  /// codeAttribute - The "Code" JVM attribute. This is a method attribute for
  /// finding the bytecode of a method in the .class file.
  //
  static const UTF8* codeAttribute;
  
  /// annotationsAttribute - The "RuntimeVisibleAnnotations" JVM attribute.
  /// This is a method attribute for getting the runtime annotations.
  //
  static const UTF8* annotationsAttribute;

  /// exceptionsAttribute - The "Exceptions" attribute. This is a method
  /// attribute for finding the exception table of a method in the .class
  /// file.
  ///
  static const UTF8* exceptionsAttribute;

  /// constantAttribute - The "ConstantValue" attribute. This is a field attribute
  /// when the field has a static constant value.
  ///
  static const UTF8* constantAttribute;

  /// lineNumberTableAttribute - The "LineNumberTable" attribute. This is used
  /// for corresponding JVM bytecode to source line in the .java file.
  ///
  static const UTF8* lineNumberTableAttribute;

  /// innerClassAttribute - The "InnerClasses" attribute. This is a class attribute
  /// for knowing the inner/outer informations of a Java class.
  ///
  static const UTF8* innerClassesAttribute;

  /// sourceFileAttribute - The "SourceFile" attribute. This is a class attribute
  /// and gives the correspondence between a class and the name of its Java
  /// file.
  ///
  static const UTF8* sourceFileAttribute;

  /// signatureAttribute - The "Signature" attribute.  This is used to record
  /// generics information about a class or method.
  ///
  static const UTF8* signatureAttribute;

  /// enclosingMEthodAttribute - The "EnclosingMethod" attribute.  This is a class
  /// attribute that identifies the method defining a local or anonymous class
  ///
  static const UTF8* enclosingMethodAttribute;

  /// paramAnnotationsAttribute - Annotations for parameters attribute
  ///
  static const UTF8* paramAnnotationsAttribute;

  /// annotationDefaultAttribute - The "AnnotationDefault" attribute
  ///
  static const UTF8* annotationDefaultAttribute;
};

/// TaskClassMirror - The isolate specific class information: the initialization
/// state and the static instance. In a non-isolate environment, there is only
/// one instance of a TaskClassMirror per Class.
class TaskClassMirror {
public:
  
  /// status - The state.
  ///
  uint8 status;

  /// initialized - Is the class initialized?
  bool initialized;

  /// staticInstance - Memory that holds the static variables of the class.
  ///
  void* staticInstance;
};

/// CommonClass - This class is the root class of all Java classes. It is
/// GC-allocated because CommonClasses have to be traceable. A java/lang/Class
/// object that stays in memory has a reference to the class. Same for
/// super or interfaces.
///
class CommonClass : public vmkit::PermanentObject {

public:
  
//===----------------------------------------------------------------------===//
//
// If you want to add new fields or modify the types of fields, you must also
// change their LLVM representation in LLVMRuntime/runtime-*.ll, and their
// offsets in JnjvmModule.cpp.
//
//===----------------------------------------------------------------------===//
 
  /// delegatees - The java/lang/Class delegatee.
  ///
  JavaObject* delegatee[NR_ISOLATES];

  /// access - {public, private, protected}.
  ///
  uint32 access;
  
  /// interfaces - The interfaces this class implements.
  ///
  Class** interfaces; 
  uint16 nbInterfaces;
  
  /// name - The name of the class.
  ///
  const UTF8* name;
   
  /// super - The parent of this class.
  ///
  Class * super;
   
  /// classLoader - The Jnjvm class loader that loaded the class.
  ///
  JnjvmClassLoader* classLoader;
  
  /// virtualVT - The virtual table of instances of this class.
  ///
  JavaVirtualTable* virtualVT;
  

//===----------------------------------------------------------------------===//
//
// End field declaration.
//
//===----------------------------------------------------------------------===//

  bool isSecondaryClass() {
    return virtualVT->offset == JavaVirtualTable::getCacheIndex();
  }

  // Assessor methods.
  uint32 getAccess() const      { return access & 0xFFFF; }
  Class** getInterfaces() const { return interfaces; }
  const UTF8* getName() const   { return name; }
  Class* getSuper() const       { return super; }
  
  std::string& getName(std::string& nameBuffer, bool linkageName = false) const;

  /// isArray - Is the class an array class?
  ///
  bool isArray() const {
    return j3::isArray(access);
  }
  
  /// isPrimitive - Is the class a primitive class?
  ///
  bool isPrimitive() const {
    return j3::isPrimitive(access);
  }
  
  /// isInterface - Is the class an interface?
  ///
  bool isInterface() const {
    return j3::isInterface(access);
  }
  
  /// isClass - Is the class a real, instantiable class?
  ///
  bool isClass() const {
    return j3::isClass(access);
  }

  /// asClass - Returns the class as a user-defined class
  /// if it is not a primitive or an array.
  ///
  UserClass* asClass() {
    if (isClass()) return (UserClass*)this;
    return 0;
  }
  
  /// asClass - Returns the class as a user-defined class
  /// if it is not a primitive or an array.
  ///
  const UserClass* asClass() const {
    if (isClass()) return (const UserClass*)this;
    return 0;
  }
  
  /// asPrimitiveClass - Returns the class if it's a primitive class.
  ///
  UserClassPrimitive* asPrimitiveClass() {
    if (isPrimitive()) return (UserClassPrimitive*)this;
    return 0;
  }
  
  const UserClassPrimitive* asPrimitiveClass() const {
    if (isPrimitive()) return (const UserClassPrimitive*)this;
    return 0;
  }
  
  /// asArrayClass - Returns the class if it's an array class.
  ///
  UserClassArray* asArrayClass() {
    if (isArray()) return (UserClassArray*)this;
    return 0;
  }
  
  const UserClassArray* asArrayClass() const {
    if (isArray()) return (const UserClassArray*)this;
    return 0;
  }

  /// tracer - The tracer of this GC-allocated class.
  ///
  void tracer(word_t closure);
  
  /// inheritName - Does this class in its class hierarchy inherits
  /// the given name? Equality is on the name. This function does not take
  /// into account array classes.
  ///
  bool inheritName(const uint16* buf, uint32 len);

  /// isOfTypeName - Does this class inherits the given name? Equality is on
  /// the name. This function takes into account array classes.
  ///
  bool isOfTypeName(const UTF8* Tname);

  /// isSubclassOf - Is this class assignable from the given class? The
  /// classes may be of any type.
  ///
  bool isSubclassOf(CommonClass* cl);

  /// getClassDelegatee - Return the java/lang/Class representation of this
  /// class.
  ///
  JavaObject* getClassDelegatee(Jnjvm* vm, JavaObject* pd = NULL);
  
  /// getClassDelegateePtr - Return a pointer on the java/lang/Class
  /// representation of this class. Used for JNI.
  ///
  JavaObject* const* getClassDelegateePtr(Jnjvm* vm, JavaObject* pd = NULL);
  
  /// CommonClass - Create a class with th given name.
  ///
  CommonClass(JnjvmClassLoader* loader, const UTF8* name);
  
  /// ~CommonClass - Free memory used by this class, and remove it from
  /// metadata.
  ///
  ~CommonClass();

  /// setInterfaces - Set the interfaces of the class.
  ///
  void setInterfaces(Class** I) {
    interfaces = I;
  }
 
  /// toPrimitive - Returns the primitive class which represents
  /// this class, ie void for java/lang/Void.
  ///
  UserClassPrimitive* toPrimitive(Jnjvm* vm) const;
 
  /// getInternal - Return the class.
  ///
  CommonClass* getInternal() {
    return this;
  }
 
  /// setDelegatee - Set the java/lang/Class object of this class.
  ///
  JavaObject* setDelegatee(JavaObject* val);

  /// getDelegatee - Get the java/lang/Class object representing this class.
  ///
  JavaObject* getDelegatee() const {
    return delegatee[0];
  }
  
  /// getDelegatee - Get a pointer on the java/lang/Class object
  /// representing this class.
  ///
  JavaObject* const* getDelegateePtr() const {
    return delegatee;
  }

  /// resolvedImplClass - Return the internal representation of the
  /// java.lang.Class object. The class must be resolved.
  //
  static UserCommonClass* resolvedImplClass(Jnjvm* vm, JavaObject* delegatee,
                                            bool doClinit);

  void dump() const __attribute__((noinline));
  friend std::ostream& operator << (std::ostream& os, const CommonClass& ccl);
};

/// ClassPrimitive - This class represents internal classes for primitive
/// types, e.g. java/lang/Integer.TYPE.
///
class ClassPrimitive : public CommonClass {
public:
  
  /// logSize - The log size of this class, eg 2 for int.
  ///
  uint32 logSize;
  
  
  /// ClassPrimitive - Constructs a primitive class. Only called at boot
  /// time.
  ///
  ClassPrimitive(JnjvmClassLoader* loader, const UTF8* name, uint32 nb);

  /// byteIdToPrimitive - Get the primitive class from its byte representation,
  /// ie int for I.
  ///
  static UserClassPrimitive* byteIdToPrimitive(char id, Classpath* upcalls);
  
};


/// Class - This class is the representation of Java regular classes (i.e not
/// array or primitive). Theses classes have a constant pool.
///
class Class : public CommonClass {

public:
  
  /// virtualSize - The size of instances of this class.
  /// 
  uint32 virtualSize;

  /// aligment - Alignment of instances of this class.
  ///
  uint32 alignment;

  /// IsolateInfo - Per isolate informations for static instances and
  /// initialization state.
  ///
  TaskClassMirror IsolateInfo[NR_ISOLATES];
   
  /// virtualFields - List of all the virtual fields defined in this class.
  /// This does not contain non-redefined super fields.
  JavaField* virtualFields;
  
  /// nbVirtualFields - The number of virtual fields.
  ///
  uint16 nbVirtualFields;

  /// staticFields - List of all the static fields defined in this class.
  ///
  JavaField* staticFields;

  /// nbStaticFields - The number of static fields.
  ///
  uint16 nbStaticFields;
  
  /// virtualMethods - List of all the virtual methods defined by this class.
  /// This does not contain non-redefined super methods.
  JavaMethod* virtualMethods;

  /// nbVirtualMethods - The number of virtual methods.
  ///
  uint32 nbVirtualMethods;
  
  /// staticMethods - List of all the static methods defined by this class.
  ///
  JavaMethod* staticMethods;

  /// nbStaticMethods - The number of static methods.
  ///
  uint16 nbStaticMethods;
  
  /// ownerClass - Who is initializing this class.
  ///
  vmkit::Thread* ownerClass;
  
  /// bytes - The .class file of this class.
  ///
  ClassBytes* bytes;

  /// ctpInfo - The constant pool info of this class.
  ///
  JavaConstantPool* ctpInfo;

  /// attributes - JVM attributes of this class.
  ///
  JavaAttribute* attributes;

  /// nbAttributes - The number of attributes.
  ///
  uint16 nbAttributes;
  
  /// innerClasses - The inner classes of this class.
  ///
  Class** innerClasses;
  
  /// nbInnerClasses - The number of inner classes.
  ///
  uint16 nbInnerClasses;

  /// outerClass - The outer class, if this class is an inner class.
  ///
  Class* outerClass;
  
  /// innerAccess - The access of this class, if this class is an inner class.
  ///
  uint16 innerAccess;

  /// innerOuterResolved - Is the inner/outer resolution done?
  ///
  bool innerOuterResolved;
  
  /// isAnonymous - Is the class an anonymous class?
  ///
  bool isAnonymous;

  /// virtualTableSize - The size of the virtual table of this class.
  ///
  uint32 virtualTableSize;
  
  /// staticSize - The size of the static instance of this class.
  ///
  uint32 staticSize;

  uint16_t minJDKVersionMajor, minJDKVersionMinor, minJDKVersionBuild;

  /// getVirtualSize - Get the virtual size of instances of this class.
  ///
  uint32 getVirtualSize() const { return virtualSize; }
  
  /// getVirtualVT - Get the virtual VT of instances of this class.
  ///
  JavaVirtualTable* getVirtualVT() const { return virtualVT; }

  /// getOwnerClass - Get the thread that is currently initializing the class.
  ///
  vmkit::Thread* getOwnerClass() const {
    return ownerClass;
  }

  /// setOwnerClass - Set the thread that is currently initializing the class.
  ///
  void setOwnerClass(vmkit::Thread* th) {
    ownerClass = th;
  }
 
  /// getOuterClass - Get the class that contains the definition of this class.
  ///
  Class* getOuterClass() const {
    return outerClass;
  }

  /// getInnterClasses - Get the classes that this class defines.
  ///
  Class** getInnerClasses() const {
    return innerClasses;
  }

  /// lookupMethodDontThrow - Lookup a method in the method map of this class.
  /// Do not throw if the method is not found.
  ///
  JavaMethod* lookupMethodDontThrow(const UTF8* name, const UTF8* type,
                                    bool isStatic, bool recurse, Class** cl);
  
  /// lookupInterfaceMethodDontThrow - Lookup a method in the interfaces of
  /// this class.
  /// Do not throw if the method is not found.
  ///
  JavaMethod* lookupInterfaceMethodDontThrow(const UTF8* name,
                                             const UTF8* type);
  
  /// lookupSpecialMethodDontThrow - Lookup a method following the
  /// invokespecial specification.
  /// Do not throw if the method is not found.
  ///
  JavaMethod* lookupSpecialMethodDontThrow(const UTF8* name,
                                           const UTF8* type,
                                           Class* current);
  
  /// lookupMethod - Lookup a method and throw an exception if not found.
  ///
  JavaMethod* lookupMethod(const UTF8* name, const UTF8* type, bool isStatic,
                           bool recurse, Class** cl);
  
  /// lookupInterfaceMethodDontThrow - Lookup a method in the interfaces of
  /// this class.
  /// Throws a MethodNotFoundError if the method can not ne found.
  ///
  JavaMethod* lookupInterfaceMethod(const UTF8* name, const UTF8* type);

  /// lookupFieldDontThrow - Lookup a field in the field map of this class. Do
  /// not throw if the field is not found.
  ///
  JavaField* lookupFieldDontThrow(const UTF8* name, const UTF8* type,
                                  bool isStatic, bool recurse,
                                  Class** definingClass);
  
  /// lookupField - Lookup a field and throw an exception if not found.
  ///
  JavaField* lookupField(const UTF8* name, const UTF8* type, bool isStatic,
                         bool recurse, Class** definingClass);
   
  /// Assessor methods.
  JavaField* getStaticFields() const    { return staticFields; }
  JavaField* getVirtualFields() const   { return virtualFields; }
  JavaMethod* getStaticMethods() const  { return staticMethods; }
  JavaMethod* getVirtualMethods() const { return virtualMethods; }

  
  /// setInnerAccess - Set the access flags of this inner class.
  ///
  void setInnerAccess(uint16 access) {
    innerAccess = access;
  }
   
  /// getStaticSize - Get the size of the static instance.
  ///
  uint32 getStaticSize() const {
    return staticSize;
  }
  
  /// doNew - Allocates a Java object whose class is this class.
  ///
  JavaObject* doNew(Jnjvm* vm);
  
  /// tracer - Tracer function of instances of Class.
  ///
  void tracer(word_t closure);
  
  ~Class();
  Class();
  
  /// lookupAttribute - Look up a JVM attribute of this class.
  ///
  JavaAttribute* lookupAttribute(const UTF8* key);
  
  /// allocateStaticInstance - Allocate the static instance of this class.
  ///
  void* allocateStaticInstance(Jnjvm* vm);
  
  /// Class - Create a class in the given virtual machine and with the given
  /// name.
  Class(JnjvmClassLoader* loader, const UTF8* name, ClassBytes* bytes);
  
  /// readParents - Reads the parents, i.e. super and interfaces, of the class.
  ///
  void readParents(Reader& reader);

  /// loadExceptions - Loads and resolves the exception classes used in catch 
  /// clauses of methods defined in this class.
  ///
  void loadExceptions();

  /// readAttributes - Reads the attributes of the class.
  ///
  JavaAttribute* readAttributes(Reader& reader, uint16& size);

  /// readFields - Reads the fields of the class.
  ///
  void readFields(Reader& reader);

  /// readMethods - Reads the methods of the class.
  ///
  void readMethods(Reader& reader);
  
  /// readClass - Reads the class.
  ///
  void readClass();
 
  /// getConstantPool - Get the constant pool of the class.
  ///
  JavaConstantPool* getConstantPool() const {
    return ctpInfo;
  }

  /// getBytes - Get the bytes of the class file.
  ///
  ClassBytes* getBytes() const {
    return bytes;
  }
  
  /// resolveInnerOuterClasses - Resolve the inner/outer information.
  ///
  void resolveInnerOuterClasses();

  /// resolveClass - If the class has not been resolved yet, resolve it.
  ///
  void resolveClass();
  void resolveParents();

  /// initialiseClass - If the class has not been initialized yet,
  /// initialize it.
  ///
  void initialiseClass(Jnjvm* vm);
  
  /// acquire - Acquire this class lock.
  ///
  void acquire();
  
  /// release - Release this class lock.
  ///
  void release();

  /// waitClass - Wait for the class to be loaded/initialized/resolved.
  ///
  void waitClass();
  
  /// broadcastClass - Unblock threads that were waiting on the class being
  /// loaded/initialized/resolved.
  ///
  void broadcastClass();
  
  /// getCurrentTaskClassMirror - Get the class task mirror of the executing
  /// isolate.
  ///
  TaskClassMirror& getCurrentTaskClassMirror() {
    return IsolateInfo[0];
  }
  
  /// isReadyForCompilation - Can this class be inlined when JITing?
  ///
  bool isReadyForCompilation() {
    return isReady();
  }
  
  /// setResolved - Set the status of the class as resolved.
  ///
  void setResolved() {
    getCurrentTaskClassMirror().status = resolved;
  }
  
  /// setErroneous - Set the class as erroneous.
  ///
  void setErroneous() {
    getCurrentTaskClassMirror().status = erroneous;
  }
  
  /// setIsResolving - The class file is being resolved.
  ///
  void setIsResolving() {
    getCurrentTaskClassMirror().status = resolving;
  }
  
  /// getStaticInstance - Get the memory that holds static variables.
  ///
  void* getStaticInstance() {
    return getCurrentTaskClassMirror().staticInstance;
  }
  
  /// setStaticInstance - Set the memory that holds static variables.
  ///
  void setStaticInstance(void* val) {
    assert(getCurrentTaskClassMirror().staticInstance == NULL);
    getCurrentTaskClassMirror().staticInstance = val;
  }
  
  /// getInitializationState - Get the state of the class.
  ///
  uint8 getInitializationState() {
    return getCurrentTaskClassMirror().status;
  }

  /// setInitializationState - Set the state of the class.
  ///
  void setInitializationState(uint8 st) {
    TaskClassMirror& TCM = getCurrentTaskClassMirror();
    TCM.status = st;
    if (st == ready) TCM.initialized = true;
  }
  
  /// isReady - Has this class been initialized?
  ///
  bool isReady() {
    return getCurrentTaskClassMirror().status == ready;
  }
  
  /// isInitializing - Is the class currently being initialized?
  ///
  bool isInitializing() {
    return getCurrentTaskClassMirror().status >= inClinit;
  }
  
  /// isResolved - Has this class been resolved?
  ///
  bool isResolved() {
    uint8 stat = getCurrentTaskClassMirror().status;
    return (stat >= resolved && stat != erroneous);
  }
  
  /// isErroneous - Is the class in an erroneous state.
  ///
  bool isErroneous() {
    return getCurrentTaskClassMirror().status == erroneous;
  }

  /// isResolving - Is the class currently being resolved?
  ///
  bool isResolving() {
    return getCurrentTaskClassMirror().status == resolving;
  }

  /// isNativeOverloaded - Is the method overloaded with a native function?
  ///
  bool isNativeOverloaded(JavaMethod* meth);

  /// needsInitialisationCheck - Does the method need an initialisation check?
  ///
  bool needsInitialisationCheck();

  /// fillIMT - Fill the vector with vectors of methods with the same IMT
  /// index.
  ///
  void fillIMT(std::set<JavaMethod*>* meths);

  /// makeVT - Create the virtual table of this class.
  ///
  void makeVT();

  static void getMinimalJDKVersion(uint16_t major, uint16_t minor, uint16_t& JDKMajor, uint16_t& JDKMinor, uint16_t& JDKBuild);
  bool isClassVersionSupported(uint16_t major, uint16_t minor);
};

/// ClassArray - This class represents Java array classes.
///
class ClassArray : public CommonClass {

public:
  
  /// _baseClass - The base class of the array.
  ///
  CommonClass*  _baseClass;

  /// baseClass - Get the base class of this array class.
  ///
  CommonClass* baseClass() const {
    return _baseClass;
  }

  /// doNew - Allocate a new array in the given vm.
  ///
  JavaObject* doNew(sint32 n, Jnjvm* vm);

  /// ClassArray - Construct a Java array class with the given name.
  ///
  ClassArray(JnjvmClassLoader* loader, const UTF8* name,
             UserCommonClass* baseClass);
  
  /// SuperArray - The super of class arrays. Namely java/lang/Object.
  ///
  static Class* SuperArray;

  /// InterfacesArray - The list of interfaces for array classes.
  ///
  static Class** InterfacesArray;

  /// initialiseVT - Initialise the primitive and reference array VT.
  /// super is the java/lang/Object class.
  ///
  static void initialiseVT(Class* javaLangObject);
  
};

/// JavaMethod - This class represents Java methods.
///
class JavaMethod : public vmkit::PermanentObject {
private:

  /// _signature - The signature of this method. Null if not resolved.
  ///
  Signdef* _signature;

public:
  
  enum Type {
    Static,
    Special,
    Interface,
    Virtual
  };

  /// initialise - Create a new method.
  ///
  void initialise(Class* cl, const UTF8* name, const UTF8* type, uint16 access);
   
  /// compiledPtr - Return a pointer to the compiled code of this Java method,
  /// compiling it if necessary.
  ///
  void* compiledPtr(Class* customizeFor = NULL);

  /// setNative - Set the method as native.
  ///
  void setNative();
  
  /// JavaMethod - Delete the method as well as the cache enveloppes and
  /// attributes of the method.
  ///
  ~JavaMethod();

  /// access - Java access type of this method (e.g. private, public...).
  ///
  uint16 access;

  /// attributes - List of Java attributes of this method.
  ///
  JavaAttribute* attributes;
  
  /// nbAttributes - The number of attributes.
  ///
  uint16 nbAttributes;

  /// classDef - The Java class where the method is defined.
  ///
  Class* classDef;

  /// name - The name of the method.
  ///
  const UTF8* name;

  /// type - The UTF8 signature of the method.
  ///
  const UTF8* type;

  /// isCustomizable - Can the method be customizable?
  ///
  bool isCustomizable;

  /// code - Pointer to the compiled code of this method.
  ///
  void* code;
 
  /// offset - The index of the method in the virtual table.
  ///
  uint32 offset;

  /// lookupAttribute - Look up an attribute in the method's attributes. Returns
  /// null if the attribute is not found.
  ///
  JavaAttribute* lookupAttribute(const UTF8* key);

  /// lookupLineNumber - Find the line number based on the given frame info.
  ///
  uint16 lookupLineNumber(vmkit::FrameInfo* FI);
  
  /// lookupCtpIndex - Lookup the constant pool index pointed by the opcode
  /// related to the given frame info.
  ///
  uint16 lookupCtpIndex(vmkit::FrameInfo* FI);
  
  /// getSignature - Get the signature of the method, resolving it if
  /// necessary.
  ///
  Signdef* getSignature() {
    if(!_signature)
      _signature = classDef->classLoader->constructSign(type);
    return _signature;
  }
  
  /// toString - Return an array of chars, suitable for creating a string.
  ///
  ArrayUInt16* toString() const;
  
  /// jniConsFromMeth - Construct the JNI name of this method as if
  /// there is no other function in the class with the same name.
  ///
  void jniConsFromMeth(char* buf) const {
    jniConsFromMeth(buf, classDef->name, name, type, isSynthetic(access));
  }

  /// jniConsFromMethOverloaded - Construct the JNI name of this method
  /// as if its name is overloaded in the class.
  ///
  void jniConsFromMethOverloaded(char* buf) const {
    jniConsFromMethOverloaded(buf, classDef->name, name, type,
                              isSynthetic(access));
  }
  
  /// jniConsFromMeth - Construct the non-overloaded JNI name with
  /// the given name and type.
  ///
  static void jniConsFromMeth(char* buf, const UTF8* clName, const UTF8* name,
                              const UTF8* sign, bool synthetic);

  /// jniConsFromMethOverloaded - Construct the overloaded JNI name with
  /// the given name and type.
  ///
  static void jniConsFromMethOverloaded(char* buf, const UTF8* clName,
                                        const UTF8* name, const UTF8* sign,
                                        bool synthetic);
  
  /// getParameterTypes - Get the java.lang.Class of the parameters of
  /// the method, with the given class loader.
  ///
  ArrayObject* getParameterTypes(JnjvmClassLoader* loader);

  /// getExceptionTypes - Get the java.lang.Class of the exceptions of the
  /// method, with the given class loader.
  ///
  ArrayObject* getExceptionTypes(JnjvmClassLoader* loader);

  /// getReturnType - Get the java.lang.Class of the result of the method,
  /// with the given class loader.
  ///
  JavaObject* getReturnType(JnjvmClassLoader* loader);

//===----------------------------------------------------------------------===//
//
// Upcalls from JnJVM code to Java code. 
//
//===----------------------------------------------------------------------===//
  
#define DO_TRY
#define DO_CATCH if (th->pendingException) { th->throwFromJava(); }

private:
	jvalue* marshalArguments(vmkit::ThreadAllocator& allocator, va_list ap);

	template<class TYPE, class FUNC_TYPE_VIRTUAL_BUF>
	TYPE invokeSpecialBuf(Jnjvm* vm, UserClass* cl, JavaObject* obj, void* buf) __attribute__((noinline)) {
		llvm_gcroot(obj, 0);
		verifyNull(obj);

		void* func = this->compiledPtr();
		FUNC_TYPE_VIRTUAL_BUF call = (FUNC_TYPE_VIRTUAL_BUF)getSignature()->getVirtualCallBuf();

		JavaThread* th = JavaThread::get();
		th->startJava();
		TYPE res;

		DO_TRY
			res = call(cl->getConstantPool(), func, obj, buf);
		DO_CATCH

		th->endJava();
		return res;
	}

	template<class TYPE, class FUNC_TYPE_VIRTUAL_BUF>
	TYPE invokeVirtualBuf(Jnjvm* vm, UserClass* cl, JavaObject* obj, void* buf) __attribute__((noinline)) {
		llvm_gcroot(obj, 0);

		UserCommonClass* theClass = JavaObject::getClass(obj);
		UserClass* objCl = theClass->isArray() ? theClass->super : theClass->asClass();

		JavaMethod* meth = this;
		if ((objCl != classDef) && !isFinal(access)) {
			meth = objCl->lookupMethodDontThrow(name, type, false, true, &cl);
			assert(meth && "No method found");
		}

		assert(objCl->isSubclassOf(meth->classDef) && "Wrong type");

		return meth->invokeSpecialBuf<TYPE, FUNC_TYPE_VIRTUAL_BUF>(vm, cl, obj, buf);
	}

	template<class TYPE, class FUNC_TYPE_STATIC_BUF>
	TYPE invokeStaticBuf(Jnjvm* vm, UserClass* cl, void* buf) __attribute__((noinline)) {
		if (!cl->isReady()) {
			cl->resolveClass();
			cl->initialiseClass(vm);
		}

		void* func = this->compiledPtr();
		FUNC_TYPE_STATIC_BUF call = (FUNC_TYPE_STATIC_BUF)getSignature()->getStaticCallBuf();

		JavaThread* th = JavaThread::get();
		th->startJava();
		TYPE res;

		DO_TRY
			res = call(cl->getConstantPool(), func, buf);
		DO_CATCH

		th->endJava();
		return res;
	}

	template<class TYPE, class FUNC_TYPE_VIRTUAL_BUF>
	TYPE invokeVirtualAP(Jnjvm* vm, UserClass* cl, JavaObject* obj, va_list ap) __attribute__((noinline)) {
		llvm_gcroot(obj, 0);
		assert(cl && "Class is NULL");
		vmkit::ThreadAllocator allocator;
		jvalue* buffer = marshalArguments(allocator, ap);
		return invokeVirtualBuf<TYPE, FUNC_TYPE_VIRTUAL_BUF>(vm, cl, obj, buffer);
	}

	template<class TYPE, class FUNC_TYPE_VIRTUAL_BUF>
	TYPE invokeSpecialAP(Jnjvm* vm, UserClass* cl, JavaObject* obj, va_list ap) __attribute__((noinline)) {
		llvm_gcroot(obj, 0);
		assert(cl && "Class is NULL");
		vmkit::ThreadAllocator allocator;
		jvalue* buffer = marshalArguments(allocator, ap);
		return invokeSpecialBuf<TYPE, FUNC_TYPE_VIRTUAL_BUF>(vm, cl, obj, buffer);
	}

	template<class TYPE, class FUNC_TYPE_STATIC_BUF>
	TYPE invokeStaticAP(Jnjvm* vm, UserClass* cl, va_list ap) __attribute__((noinline)) {
		assert(cl && "Class is NULL");
		vmkit::ThreadAllocator allocator;
		jvalue* buffer = marshalArguments(allocator, ap);
		return invokeStaticBuf<TYPE, FUNC_TYPE_STATIC_BUF>(vm, cl, buffer);
	}

#define JavaMethod_DECL_INVOKE_VA(TYPE, TYPE_NAME) \
	TYPE invoke##TYPE_NAME##Virtual(Jnjvm* vm, UserClass* cl, JavaObject* obj, ...) __attribute__ ((noinline));	\
	TYPE invoke##TYPE_NAME##Special(Jnjvm* vm, UserClass* cl, JavaObject* obj, ...) __attribute__ ((noinline));	\
	TYPE invoke##TYPE_NAME##Static(Jnjvm* vm, UserClass* cl, ...) __attribute__ ((noinline));

#define JavaMethod_DECL_INVOKE_AP(TYPE, TYPE_NAME)	\
	TYPE invoke##TYPE_NAME##VirtualAP(Jnjvm* vm, UserClass* cl, JavaObject* obj, va_list ap) __attribute__ ((noinline));	\
	TYPE invoke##TYPE_NAME##SpecialAP(Jnjvm* vm, UserClass* cl, JavaObject* obj, va_list ap) __attribute__ ((noinline));	\
	TYPE invoke##TYPE_NAME##StaticAP(Jnjvm* vm, UserClass* cl, va_list ap) __attribute__ ((noinline));

#define JavaMethod_DECL_INVOKE_BUF(TYPE, TYPE_NAME)	\
	TYPE invoke##TYPE_NAME##VirtualBuf(Jnjvm* vm, UserClass* cl, JavaObject* obj, void* buf) __attribute__ ((noinline));	\
	TYPE invoke##TYPE_NAME##SpecialBuf(Jnjvm* vm, UserClass* cl, JavaObject* obj, void* buf) __attribute__ ((noinline));	\
 	TYPE invoke##TYPE_NAME##StaticBuf(Jnjvm* vm, UserClass* cl, void* buf) __attribute__ ((noinline));

#define JavaMethod_DECL_INVOKE(TYPE, TYPE_NAME) \
	JavaMethod_DECL_INVOKE_BUF(TYPE, TYPE_NAME)	\
	JavaMethod_DECL_INVOKE_AP(TYPE, TYPE_NAME)	\
	JavaMethod_DECL_INVOKE_VA(TYPE, TYPE_NAME)

public:
	JavaMethod_DECL_INVOKE(uint32_t, Int)
	JavaMethod_DECL_INVOKE(int64_t, Long)
	JavaMethod_DECL_INVOKE(float,  Float)
	JavaMethod_DECL_INVOKE(double, Double)
	JavaMethod_DECL_INVOKE(JavaObject*, JavaObject)

	std::string& getName(std::string& nameBuffer, bool linkageName = false) const;
	friend std::ostream& operator << (std::ostream&, const JavaMethod&);
	void dump() const __attribute__((noinline));
  
  #define JNI_NAME_PRE "Java_"
  #define JNI_NAME_PRE_LEN 5
  
};

/// JavaField - This class represents a Java field.
///
class JavaField  : public vmkit::PermanentObject {
private:
  /// _signature - The signature of the field. Null if not resolved.
  ///
  Typedef* _signature;
  
  /// InitField - Set an initial value to the field.
  ///
  void InitStaticField(uint64 val);
  void InitStaticField(JavaObject* val);
  void InitStaticField(double val);
  void InitStaticField(float val);
  void InitNullStaticField();

public:
  
  /// constructField - Create a new field.
  ///
  void initialise(Class* cl, const UTF8* name, const UTF8* type, uint16 access);

  /// ~JavaField - Destroy the field as well as its attributes.
  ///
  ~JavaField();

  /// access - The Java access type of this field (e.g. public, private).
  ///
  uint16 access;

  /// name - The name of the field.
  ///
  const UTF8* name;

  /// type - The UTF8 type name of the field.
  ///
  const UTF8* type;

  /// attributes - List of Java attributes for this field.
  ///
  JavaAttribute* attributes;
  
  /// nbAttributes - The number of attributes.
  ///
  uint16 nbAttributes;

  /// classDef - The class where the field is defined.
  ///
  Class* classDef;

  /// ptrOffset - The offset of the field when the object containing
  /// the field is casted to an array of bytes.
  ///
  uint32 ptrOffset;
  
  /// num - The index of the field in the field list.
  ///
  uint16 num;
  
  /// getSignature - Get the signature of this field, resolving it if
  /// necessary.
  ///
  Typedef* getSignature() {
    if(!_signature)
      _signature = classDef->classLoader->constructType(type);
    return _signature;
  }

  /// InitStaticField - Init the value of the field in the given object. This is
  /// used for static fields which have a default value.
  ///
  void InitStaticField(Jnjvm* vm);

  /// lookupAttribute - Look up the attribute in the field's list of attributes.
  ///
  JavaAttribute* lookupAttribute(const UTF8* key);

  JavaObject** getStaticObjectFieldPtr() {
    assert(classDef->getStaticInstance());
    return (JavaObject**)((uint64)classDef->getStaticInstance() + ptrOffset);
  }

  JavaObject** getInstanceObjectFieldPtr(JavaObject* obj) {
    llvm_gcroot(obj, 0);
    return (JavaObject**)((uint64)obj + ptrOffset);
  }

private:
  /// getStatic*Field - Get a static field.
  ///
  template <class TYPE>
  TYPE getStaticField() __attribute__ ((noinline)) {
    assert(classDef->isResolved());
    void* ptr = (void*)((uint64)classDef->getStaticInstance() + ptrOffset);
    return *static_cast<TYPE *>(ptr);
  }

  /*
    The struct FieldSetter is a workaround in C++ to enable
    template-based method specialization in a non-template
    class. See specialization below the class declaration.
  */
  template<class TYPE>
  struct FieldSetter {
	  /// setStatic*Field - Set a field of an object.
	  ///
	  static void setStaticField(const JavaField* field, TYPE val) __attribute__ ((noinline)) {
	    assert(field->classDef->isResolved());
	    void* ptr = (void*)((uint64)field->classDef->getStaticInstance() + field->ptrOffset);
	    *static_cast<TYPE *>(ptr) = val;
	  }
      
	  /// setInstance*Field - Set an instance field.
	  ///
	  static void setInstanceField(const JavaField* field, JavaObject* obj, TYPE val) __attribute__ ((noinline)) {
	    llvm_gcroot(obj, 0);
	    assert(field->classDef->isResolved());
	    void* ptr = (void*)((uint64)obj + field->ptrOffset);
	    *static_cast<TYPE *>(ptr) = val;
	  }
  };

  /// setStatic*Field - Set a field of an object.
  ///
  template <class TYPE>
  void setStaticField(TYPE val) __attribute__ ((noinline)) {
    FieldSetter<TYPE>::setStaticField(this, val);
  }

  /// getInstance*Field - Get an instance field.
  ///
  template<class TYPE>
  TYPE getInstanceField(JavaObject* obj) __attribute__ ((noinline)) {
    llvm_gcroot(obj, 0);
    assert(classDef->isResolved());
    void* ptr = (void*)((uint64)obj + ptrOffset);
    return *static_cast<TYPE *>(ptr);
  }

  /// setInstance*Field - Set an instance field.
  ///
  template<class TYPE>
  void setInstanceField(JavaObject* obj, TYPE val) __attribute__ ((noinline)) {
    llvm_gcroot(obj, 0);
    FieldSetter<TYPE>::setInstanceField(this, obj, val);
  }

#define JavaField_DECL_ASSESSORS(TYPE, TYPE_NAME)			\
	TYPE getStatic##TYPE_NAME##Field() __attribute__ ((noinline));						\
	void setStatic##TYPE_NAME##Field(TYPE val) __attribute__ ((noinline));				\
	TYPE getInstance##TYPE_NAME##Field(JavaObject* obj) __attribute__ ((noinline));	\
	void setInstance##TYPE_NAME##Field(JavaObject* obj, TYPE val) __attribute__ ((noinline));

#define JavaField_IMPL_ASSESSORS(TYPE, TYPE_NAME)																			\
	TYPE JavaField::getStatic##TYPE_NAME##Field() {									\
  		return getStaticField<TYPE>();}												\
	void JavaField::setStatic##TYPE_NAME##Field(TYPE val) {							\
		return setStaticField<TYPE>(val);}											\
	TYPE JavaField::getInstance##TYPE_NAME##Field(JavaObject* obj) {				\
		llvm_gcroot(obj, 0);														\
		return this->getInstanceField<TYPE>(obj);}									\
	void JavaField::setInstance##TYPE_NAME##Field(JavaObject* obj, TYPE val) {		\
		llvm_gcroot(obj, 0);														\
		return this->setInstanceField<TYPE>(obj, val);}

public:
  JavaField_DECL_ASSESSORS(float, Float)
  JavaField_DECL_ASSESSORS(double, Double)
  JavaField_DECL_ASSESSORS(uint8, Int8)
  JavaField_DECL_ASSESSORS(uint16, Int16)
  JavaField_DECL_ASSESSORS(uint32, Int32)
  JavaField_DECL_ASSESSORS(sint64, Long)
  JavaField_DECL_ASSESSORS(JavaObject*, Object)
  
  bool isReference() {
    uint16 val = type->elements[0];
    return (val == '[' || val == 'L');
  }
  
  bool isDouble() {
    return (type->elements[0] == 'D');
  }

  bool isLong() {
    return (type->elements[0] == 'J');
  }

  bool isInt() {
    return (type->elements[0] == 'I');
  }

  bool isFloat() {
    return (type->elements[0] == 'F');
  }

  bool isShort() {
    return (type->elements[0] == 'S');
  }

  bool isChar() {
    return (type->elements[0] == 'C');
  }

  bool isByte() {
    return (type->elements[0] == 'B');
  }

  bool isBoolean() {
    return (type->elements[0] == 'Z');
  }

};


// Specialization for TYPE=JavaObject*
template<>
struct JavaField::FieldSetter<JavaObject*> {

  static void setStaticField(const JavaField* field, JavaObject* val) __attribute__ ((noinline)) {
	llvm_gcroot(val, 0);
	if (val != NULL) assert(val->getVirtualTable());
	assert(field->classDef->isResolved());
	JavaObject** ptr = (JavaObject**)((uint64)field->classDef->getStaticInstance() + field->ptrOffset);
	vmkit::Collector::objectReferenceNonHeapWriteBarrier((gc**)ptr, (gc*)val);
  }

  static void setInstanceField(const JavaField* field, JavaObject* obj, JavaObject* val) __attribute__ ((noinline)) {
	llvm_gcroot(obj, 0);
	llvm_gcroot(val, 0);
	if (val != NULL) assert(val->getVirtualTable());
	assert(field->classDef->isResolved());
	JavaObject** ptr = (JavaObject**)((uint64)obj + field->ptrOffset);
	vmkit::Collector::objectReferenceWriteBarrier((gc*)obj, (gc**)ptr, (gc*)val);
  }
};

template <>
void JavaField::setStaticField(JavaObject* val) __attribute__ ((noinline));

template<>
void JavaField::setInstanceField(JavaObject* obj, JavaObject* val) __attribute__ ((noinline));


} // end namespace j3

#endif
