//===------------ VMClass.cpp - CLI class representation ------------------===//
//
//                              N3
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include <stdarg.h>
#include <vector>

#include "llvm/DerivedTypes.h"
#include "llvm/LLVMContext.h"
#include "llvm/Module.h"

#include "N3Debug.h"
#include "types.h"
#include "mvm/JIT.h"
#include "mvm/PrintBuffer.h"
#include "mvm/Threads/Locks.h"


#include "Assembly.h"
#include "CLIAccess.h"
#include "CLIJit.h"
#include "MSCorlib.h"
#include "N3.h"
#include "VMArray.h"
#include "VMClass.h"
#include "VMThread.h"

using namespace n3;

void VMCommonClass::print(mvm::PrintBuffer* buf) const {
  buf->write("CLIType<");
  nameSpace->print(buf);
  buf->write("::");
  name->print(buf);
  buf->write(">");
}

void VMCommonClass::aquire() {
  lockVar->lock(); 
}

void VMCommonClass::release() {
  lockVar->unlock();
}

void VMCommonClass::waitClass() {
  condVar->wait(lockVar);
}

void VMCommonClass::broadcastClass() {
  condVar->broadcast();
}

bool VMCommonClass::ownerClass() {
  return lockVar->selfOwner();
}


void VMClass::print(mvm::PrintBuffer* buf) const {
  buf->write("CLIType<");
  nameSpace->print(buf);
  buf->write("::");
  name->print(buf);
  buf->write(">");
}

void VMClassArray::print(mvm::PrintBuffer* buf) const {
  buf->write("CLITypeArray<");
  nameSpace->print(buf);
  buf->write("::");
  name->print(buf);
  buf->write(">");
}

void VMClassPointer::print(mvm::PrintBuffer* buf) const {
  buf->write("CLITypePointer<");
  nameSpace->print(buf);
  buf->write("::");
  name->print(buf);
  buf->write(">");
}

void VMMethod::print(mvm::PrintBuffer* buf) const {
  buf->write("CLIMethod<");
  classDef->nameSpace->print(buf);
  buf->write(".");
  classDef->name->print(buf);
  buf->write("::");
  name->print(buf);
  buf->write("(");
  std::vector<VMCommonClass*>::iterator i = ((VMMethod*)this)->parameters.begin();
  std::vector<VMCommonClass*>::iterator e = ((VMMethod*)this)->parameters.end();

  ++i;
  if (i != e) {
    while (true) {
      (*i)->nameSpace->print(buf);
      buf->write(".");
      (*i)->name->print(buf);
      ++i;
      if (i == e) break;
      else buf->write(" ,");
    }
  }
  buf->write(")");
  buf->write(">");
}

void VMGenericMethod::print(mvm::PrintBuffer* buf) const {
  buf->write("CLIGenericMethod<");
  classDef->nameSpace->print(buf);
  buf->write(".");
  classDef->name->print(buf);
  buf->write("::");
  name->print(buf);
  buf->write("(");
  std::vector<VMCommonClass*>::iterator i = ((VMMethod*)this)->parameters.begin();
  std::vector<VMCommonClass*>::iterator e = ((VMMethod*)this)->parameters.end();

  ++i;
  if (i != e) {
    while (true) {
      (*i)->nameSpace->print(buf);
      buf->write(".");
      (*i)->name->print(buf);
      ++i;
      if (i == e) break;
      else buf->write(" ,");
    }
  }
  buf->write(")");
  buf->write(">");
}

void VMField::print(mvm::PrintBuffer* buf) const {
  buf->write("CLIField<");
  classDef->nameSpace->print(buf);
  buf->write(".");
  classDef->name->print(buf);
  buf->write("::");
  name->print(buf);
  buf->write(">");
}
  
void Param::print(mvm::PrintBuffer* buf) const {
  buf->write("CLIParam<");
  name->print(buf);
  buf->write(">");
}

void Property::print(mvm::PrintBuffer* buf) const {
  buf->write("Property def with name <");
  name->print(buf);
  buf->write(">");
}


void VMCommonClass::initialise(N3* vm, bool isArray) {
  this->lockVar = new mvm::LockRecursive();
  this->condVar = new mvm::Cond();
  this->ooo_delegatee = 0;
  this->status = hashed;
  this->vm = vm;
  this->isArray = isArray;
  this->isPointer = false;
  this->isPrimitive = false;
  this->naturalType = llvm::OpaqueType::get(llvm::getGlobalContext());
}

const UTF8* VMClassArray::constructArrayName(const UTF8* name, uint32 dims) {
	mvm::PrintBuffer _asciiz(name);
  const char* asciiz = _asciiz.cString();
  char* res = (char*)alloca(strlen(asciiz) + (dims * 2) + 1);
  sprintf(res, asciiz);

  for (uint32 i = 0; i < dims; ++i) {
    sprintf(res, "%s[]", res);
  }

  return VMThread::get()->getVM()->asciizToUTF8(res);
}

const UTF8* VMClassPointer::constructPointerName(const UTF8* name, uint32 dims) {
	mvm::PrintBuffer _asciiz(name);
  const char* asciiz = _asciiz.cString();
  char* res = (char*)alloca(strlen(asciiz) + (dims * 2) + 1);
  sprintf(res, asciiz);

  for (uint32 i = 0; i < dims; ++i) {
    sprintf(res, "%s*", res);
  }

  return VMThread::get()->getVM()->asciizToUTF8(res);
}


void VMCommonClass::loadParents(VMGenericClass* genClass, VMGenericMethod* genMethod) {
  if ((0xffff & superToken) == 0) {
    depth = 0;
    display.push_back(this);
  } else {
    super = assembly->loadType((N3*)vm, superToken, true, false, false, true, genClass, genMethod);
    depth = super->depth + 1;
    for (uint32 i = 0; i < super->display.size(); ++i) {
      display.push_back(super->display[i]);
    }
    display.push_back(this);
  }
  
  for (std::vector<uint32>::iterator i = interfacesToken.begin(), 
       e = interfacesToken.end(); i!= e; ++i) {
    interfaces.push_back((VMClass*)assembly->loadType((N3*)vm, (*i), true, 
                                                      false, false, true, genClass, genMethod));
  }

}

typedef void (*clinit_t)(void);

void VMCommonClass::clinitClass(VMGenericMethod* genMethod) {
	//	printf("----- clinit: %s\n", mvm::PrintBuffer::objectToString(this));
  VMCommonClass* cl = this; 
  if (cl->status < ready) {
    cl->aquire();
    int status = cl->status;
    if (status == ready) {
      cl->release();
    } else if (status == static_resolved) {
      cl->status = clinitParent;
      cl->release();
      if (cl->super) {
        cl->super->resolveStatic(true, genMethod);
      }
      for (uint32 i = 0; i < cl->interfaces.size(); i++) {
        cl->interfaces[i]->resolveStatic(true, genMethod);
      }
      
      cl->status = inClinit;
      resolveVT();
      std::vector<VMCommonClass*> args;
      args.push_back(MSCorlib::pVoid);
      VMMethod* meth = cl->lookupMethodDontThrow(N3::clinitName, args,
                                                 true, false);
      
      PRINT_DEBUG(N3_LOAD, 0, COLOR_NORMAL, "%s", "; ");
      PRINT_DEBUG(N3_LOAD, 0, LIGHT_GREEN, "%s", "clinit ");
      PRINT_DEBUG(N3_LOAD, 0, COLOR_NORMAL, "%s::%s\n", mvm::PrintBuffer(this).cString(),
                  mvm::PrintBuffer(cl).cString());
      
      if (meth) {
        llvm::Function* pred = meth->compiledPtr(genMethod);
        clinit_t res = (clinit_t)
          (intptr_t)mvm::MvmModule::executionEngine->getPointerToGlobal(pred);
        res();
      }

      cl->status = ready;
      cl->broadcastClass();
    } else if (status < static_resolved) {
      cl->release();
      VMThread::get()->getVM()->unknownError("try to clinit a not-readed class...");
    } else {
      if (!cl->ownerClass()) {
        while (status < ready) cl->waitClass();
      } 
      cl->release();
    }
  }
}

void VMClass::resolveStaticFields(VMGenericMethod* genMethod) {
  
  VMClass* cl = this;

  std::vector<const llvm::Type*> fields;
  fields.push_back(VMObject::llvmType->getContainedType(0));
  uint64 offset = 0;
  for (std::vector<VMField*>::iterator i = cl->staticFields.begin(),
            e = cl->staticFields.end(); i!= e; ++i) {
    // preincrement because 0 is VMObject
    (*i)->offset = llvm::ConstantInt::get(llvm::Type::getInt32Ty(llvm::getGlobalContext()), ++offset);
  }
  for (std::vector<VMField*>::iterator i = cl->staticFields.begin(),
            e = cl->staticFields.end(); i!= e; ++i) {
    (*i)->signature->resolveType(false, false, genMethod);
    fields.push_back((*i)->signature->naturalType);
  }

  cl->staticType = llvm::PointerType::getUnqual(llvm::StructType::get(vm->LLVMModule->getContext(), fields, false));

  N3VirtualTable* VT = CLIJit::makeVT(cl, true);

  uint64 size = mvm::MvmModule::getTypeSize(cl->staticType->getContainedType(0));
  cl->staticInstance = (VMObject*)gc::operator new(size, VT);
	VMObject::initialise(cl->staticInstance, cl);

  for (std::vector<VMField*>::iterator i = cl->staticFields.begin(),
            e = cl->staticFields.end(); i!= e; ++i) {
    
    (*i)->initField(cl->staticInstance);
  }
}

void VMClass::unifyTypes(VMGenericClass* genClass, VMGenericMethod* genMethod) {
  llvm::PATypeHolder PA = naturalType;
  for (std::vector<VMField*>::iterator i = virtualFields.begin(), 
       e = virtualFields.end(); i!= e; ++i) {
    (*i)->signature->resolveVirtual(genClass, genMethod);
  }
  naturalType = PA.get();
}

void VMClass::resolveVirtualFields(VMGenericClass* genClass, VMGenericMethod* genMethod) {
  const llvm::Type* ResultTy = 0;
  if (hasExplicitLayout(flags)) {
    explicitLayoutSize = assembly->getExplicitLayout(token);
    ResultTy = llvm::IntegerType::get(llvm::getGlobalContext(), explicitLayoutSize);
  } else if (super != 0) {
    if (super == MSCorlib::pValue) {
      uint32 size = virtualFields.size();
      if (size == 1) {
        virtualFields[0]->offset = VMThread::get()->getVM()->module->constantZero;
        ResultTy = virtualFields[0]->signature->naturalType;
      } else if (size == 0) {
        ResultTy = llvm::Type::getVoidTy(llvm::getGlobalContext());
      } else {
        std::vector<const llvm::Type*> Elts;
        uint32 offset = -1;
        for (std::vector<VMField*>::iterator i = virtualFields.begin(), 
          e = virtualFields.end(); i!= e; ++i) {
          (*i)->offset = llvm::ConstantInt::get(llvm::Type::getInt32Ty(llvm::getGlobalContext()), ++offset);
          const llvm::Type* type = (*i)->signature->naturalType;
          Elts.push_back(type);
        }
        ResultTy = llvm::StructType::get(vm->LLVMModule->getContext(), Elts);
      }
    } else if (super == MSCorlib::pEnum) {
      ResultTy = llvm::Type::getInt32Ty(llvm::getGlobalContext()); // TODO find max
    } else {
      std::vector<const llvm::Type*> Elts;
      Elts.push_back(super->naturalType->getContainedType(0));
      uint32 offset = 0;
      for (std::vector<VMField*>::iterator i = virtualFields.begin(), 
           e = virtualFields.end(); i!= e; ++i) {
        (*i)->offset = llvm::ConstantInt::get(llvm::Type::getInt32Ty(llvm::getGlobalContext()), ++offset);
        const llvm::Type* type = (*i)->signature->naturalType;
        Elts.push_back(type);
      }
      ResultTy = llvm::PointerType::getUnqual(llvm::StructType::get(vm->LLVMModule->getContext(), Elts));
    }
  } else {
    ResultTy = VMObject::llvmType;
  }
  
  
  if (naturalType->isAbstract()) {
    const llvm::OpaqueType *OldTy = 
      llvm::dyn_cast_or_null<llvm::OpaqueType>(this->naturalType);
    if (OldTy) {
      const_cast<llvm::OpaqueType*>(OldTy)->refineAbstractTypeTo(ResultTy);
    }
    naturalType = ResultTy;
  }
  
  unifyTypes(genClass, genMethod);
  
  if (super == MSCorlib::pValue) {
    std::vector<const llvm::Type*> Elts;
    Elts.push_back(VMObject::llvmType->getContainedType(0));
    for (std::vector<VMField*>::iterator i = virtualFields.begin(), 
         e = virtualFields.end(); i!= e; ++i) {
      Elts.push_back((*i)->signature->naturalType);
    }
    virtualType = llvm::PointerType::getUnqual(llvm::StructType::get(vm->LLVMModule->getContext(), Elts)); 
  } else {
    virtualType = naturalType;
  }
  

}


void VMClassArray::makeType() {
  std::vector<const llvm::Type*> arrayFields;
  arrayFields.push_back(VMObject::llvmType->getContainedType(0));
  arrayFields.push_back(llvm::Type::getInt32Ty(llvm::getGlobalContext()));
  arrayFields.push_back(llvm::ArrayType::get(baseClass->naturalType, 0));
  const llvm::Type* type = llvm::PointerType::getUnqual(llvm::StructType::get(vm->LLVMModule->getContext(), arrayFields, false));
  ((llvm::OpaqueType*)naturalType)->refineAbstractTypeTo(type);
  naturalType = type;
  virtualType = naturalType;
}

void VMClassPointer::makeType() {
  const llvm::Type* type = (baseClass->naturalType == llvm::Type::getVoidTy(llvm::getGlobalContext())) ? llvm::Type::getInt8Ty(llvm::getGlobalContext()) : baseClass->naturalType;
  const llvm::Type* pType = llvm::PointerType::getUnqual(type);
  ((llvm::OpaqueType*)naturalType)->refineAbstractTypeTo(pType);
  naturalType = pType;
}

void VMCommonClass::resolveVirtual(VMGenericClass* genClass, VMGenericMethod *genMethod) {
	//	printf("Resolve virtual: %s\n", mvm::PrintBuffer::objectToString(this));
  VMCommonClass* cl = this;
  
  if (cl->status < virtual_resolved) {
    cl->aquire();
    int status = cl->status;
    if (status >= virtual_resolved) {
      cl->release();
    } else if (status <  loaded) {
      cl->release();
      VMThread::get()->getVM()->unknownError("try to resolve a not-readed class");
    } else if (status == loaded) {
      if (cl->isArray) {
        VMClassArray* arrayCl = (VMClassArray*)cl;
        VMCommonClass* baseClass =  arrayCl->baseClass;
				//				printf("Resolveing base class: %s\n", mvm::PrintBuffer::objectToString(baseClass));
        baseClass->resolveType(false, false, genMethod);
				//  			printf("Resolveing base class: %s done\n", mvm::PrintBuffer::objectToString(baseClass));
        arrayCl->makeType();
        cl->status = virtual_resolved;
      } else if (cl->isPointer) {
        VMClassPointer* pointerCl = (VMClassPointer*)cl;
        VMCommonClass* baseClass =  pointerCl->baseClass;
        baseClass->resolveType(false, false, genMethod);
        pointerCl->makeType();
        cl->status = virtual_resolved;
      } else {
        cl->release();
        cl->loadParents(genClass, genMethod);
        cl->aquire();
        cl->status = prepared;
        assembly->readClass(cl, genMethod);
        cl->status = readed;
        ((VMClass*)cl)->resolveVirtualFields(genClass, genMethod);
        cl->status = virtual_resolved;
      }
      cl->release();
    } else {
      if (!(cl->ownerClass())) {
        while (status < virtual_resolved) {
          cl->waitClass();
        }
      }
      cl->release();
    }
  }
}

void VMCommonClass::resolveVT() {
  VMCommonClass* cl = this;
	if (cl->isArray) {
		VMClassArray* arrayCl = (VMClassArray*)cl;
		arrayCl->baseClass->resolveVT();
		//		printf("Making vt of %s\n", mvm::PrintBuffer(this).cString());
		arrayCl->arrayVT = CLIJit::makeArrayVT(arrayCl);
	} else if (cl->isPointer) {
	} else {
		VMClass* cl = (VMClass*)this;
		if (super)
			super->resolveVT();

		// We check for virtual instance because the string class has a 
		// bigger size than the class declares.
		if (super != MSCorlib::pEnum && !cl->virtualInstance) {
			cl->vtSize = super ? ((VMClass*)super)->vtSize : sizeof(N3VirtualTable) / sizeof(uintptr_t);
      for (std::vector<VMMethod*>::iterator i = virtualMethods.begin(), 
						 e = virtualMethods.end(); i!= e; ++i) {
        (*i)->offsetInVt = cl->vtSize++;
			}

			//			printf("Making vt of %s with %d elements\n", mvm::PrintBuffer(this).cString(), cl->vtSize);
			N3VirtualTable* VT = CLIJit::makeVT(cl, false);
  
			uint64 size = mvm::MvmModule::getTypeSize(cl->virtualType->getContainedType(0));
			cl->virtualInstance = (VMObject*)gc::operator new(size, VT);
			VMObject::initialise(cl->virtualInstance, cl);
			
			for (std::vector<VMField*>::iterator i = cl->virtualFields.begin(),
						 e = cl->virtualFields.end(); i!= e; ++i) {
				
				(*i)->initField(cl->virtualInstance);
			}
		}
	}
}

void VMCommonClass::resolveType(bool stat, bool clinit, VMGenericMethod* genMethod) {
	//	printf("Resolve type: %s %d %d\n", mvm::PrintBuffer::objectToString(this), stat, clinit);
  resolveVirtual(static_cast<VMGenericClass*>(this), genMethod);
  if (stat) resolveStatic(clinit, genMethod);
}

void VMCommonClass::resolveStatic(bool clinit, VMGenericMethod* genMethod) {
	//	printf("Resolve static: %s %d\n", mvm::PrintBuffer::objectToString(this), clinit);
  VMCommonClass* cl = this;
  if (cl->status < static_resolved) {
    cl->aquire();
    int status = cl->status;
    if (status >= static_resolved) {
      cl->release();
    } else if (status < virtual_resolved) {
      cl->release();
			//			printf("Will throw an exception: %s....\n", mvm::PrintBuffer::objectToString(this));
			//			((char *)0)[0] = 22;
      VMThread::get()->getVM()->unknownError("try to resolve static of a not virtual-resolved class");
    } else if (status == virtual_resolved) {
      if (cl->isArray) {
        VMClassArray* arrayCl = (VMClassArray*)cl;
        VMCommonClass* baseClass =  arrayCl->baseClass;
        baseClass->resolveStatic(false, genMethod);
        cl->status = static_resolved;
      } else if (cl->isPointer) {
        VMClassPointer* pointerCl = (VMClassPointer*)cl;
        VMCommonClass* baseClass =  pointerCl->baseClass;
        baseClass->resolveStatic(false, genMethod);
        cl->status = static_resolved;
      } else {
        ((VMClass*)cl)->resolveStaticFields(genMethod);
        cl->status = static_resolved;
      }
      cl->release();
    } else {
      if (!(cl->ownerClass())) {
        while (status < static_resolved) {
          cl->waitClass();
        }
      }
      cl->release();
    }
  }
  if (clinit) cl->clinitClass(genMethod);
}


VMMethod* VMCommonClass::lookupMethodDontThrow(const UTF8* name,
                                               std::vector<VMCommonClass*>& args, 
                                               bool isStatic, bool recurse) {
  
  std::vector<VMMethod*>* meths = (isStatic? &staticMethods : 
                                             &virtualMethods);
  
  VMMethod *cur, *res = 0;
  int i = 0;
  int nbm = meths->size();

  while (!res && i < nbm) {
    cur = meths->at(i);
    if (cur->name == name && cur->signatureEquals(args)) {
      return cur;
    }
    ++i;
  }

  if (recurse) {
    if (super) res = super->lookupMethodDontThrow(name, args, isStatic,
                                                  recurse);
    if (!res && isStatic) {
      int nbi = interfaces.size();
      i = 0;
      while (res == 0 && i < nbi) {
        res = interfaces[i]->lookupMethodDontThrow(name, args, isStatic,
                                                   recurse);
        ++i;
      }
    }
  }

  return res;
}

VMMethod* VMCommonClass::lookupMethod(const UTF8* name, 
                                      std::vector<VMCommonClass*>& args, 
                                      bool isStatic, bool recurse) {
  
  VMMethod* res = lookupMethodDontThrow(name, args, isStatic, recurse);
  if (!res) {
    VMThread::get()->getVM()->error(N3::MissingMethodException, 
                               "unable to find %s in %s",
                               mvm::PrintBuffer(name).cString(), mvm::PrintBuffer(this).cString());
  }
  return res;
}

VMField* VMCommonClass::lookupFieldDontThrow(const UTF8* name,
                                             VMCommonClass* type,
                                             bool isStatic, bool recurse) {
  
  std::vector<VMField*>* fields = (isStatic? &staticFields : &virtualFields);
  
  VMField *cur, *res = 0;
  int i = 0;
  int nbm = fields->size();

  while (!res && i < nbm) {
    cur = fields->at(i);
    if (cur->name == name && cur->signature == type) {
      return cur;
    }
    ++i;
  }

  if (recurse) {
    if (super) res = super->lookupFieldDontThrow(name, type, isStatic,
                                                 recurse);
    if (!res && isStatic) {
      int nbi = interfaces.size();
      i = 0;
      while (res == 0 && i < nbi) {
        res = interfaces[i]->lookupFieldDontThrow(name, type, isStatic,
                                                  recurse);
        ++i;
      }
    }
  }

  return res;
}

VMField* VMCommonClass::lookupField(const UTF8* name, VMCommonClass* type,
                                    bool isStatic, bool recurse) {
  
  VMField* res = lookupFieldDontThrow(name, type, isStatic, recurse);
  if (!res) {
    VMThread::get()->getVM()->error(N3::MissingFieldException, 
                               "unable to find %s in %s",
                               mvm::PrintBuffer(name).cString(), mvm::PrintBuffer(this).cString());
  }
  return res;
}

VMObject* VMClass::initialiseObject(VMObject* obj) {
  uint64 size = mvm::MvmModule::getTypeSize(virtualType->getContainedType(0));
  memcpy(obj, virtualInstance, size);
  return obj;
}

VMObject* VMClass::doNew() {
  if (status < inClinit) resolveType(true, true, NULL);
  uint64 size = mvm::MvmModule::getTypeSize(virtualType->getContainedType(0));
  declare_gcroot(VMObject*, res) = (VMObject*)gc::operator new(size, VMObject::getN3VirtualTable(virtualInstance));
  memcpy(res, virtualInstance, size);
  return res;
}

VMObject* VMClassArray::doNew(uint32 nb) {
  if (status < inClinit) resolveType(true, true, NULL);
  uint64 size = mvm::MvmModule::getTypeSize(baseClass->naturalType);
  declare_gcroot(VMArray*, res) = (VMArray*)gc::operator new(size * nb + sizeof(VMObject) + sizeof(sint32), arrayVT);
  memset(res->elements, 0, size * nb);
	VMObject::initialise(res, this);
  res->size = nb;
  return res;
}

static void disassembleStruct(std::vector<const llvm::Type*> &args, 
                              const llvm::Type* arg) {
  const llvm::StructType* STy = llvm::dyn_cast<llvm::StructType>(arg);
  for (llvm::StructType::element_iterator I = STy->element_begin(),
       E = STy->element_end(); I != E; ++I) {
    if ((*I)->isSingleValueType()) {
      args.push_back(*I);
    } else {
      disassembleStruct(args, *I);
    }
  }
}

const llvm::FunctionType* VMMethod::resolveSignature(
                  std::vector<VMCommonClass*> & parameters, bool isVirt,
                  bool& structRet, VMGenericMethod* genMethod) {
    const llvm::Type* ret;
    std::vector<const llvm::Type*> args;
    std::vector<VMCommonClass*>::iterator i = parameters.begin(),
                                          e = parameters.end();
    if ((*i)->naturalType->isAbstract()) {
      (*i)->resolveType(false, false, genMethod);
    }
    ret = (*i)->naturalType;
    ++i;
    
    if (isVirt) {
      VMCommonClass* cur = (*i);
      ++i;
      if (cur->naturalType->isAbstract()) {
        cur->resolveType(false, false, genMethod);
      }
      if (cur->super != MSCorlib::pValue && cur->super != MSCorlib::pEnum) {
        args.push_back(cur->naturalType);
      } else {
        args.push_back(llvm::PointerType::getUnqual(cur->naturalType));
      }
    }
    
    for ( ; i!= e; ++i) {
      VMCommonClass* cur = (*i);
      if (cur->naturalType->isAbstract()) {
        cur->resolveType(false, false, genMethod);
      }
      if (cur->naturalType->isSingleValueType()) {
        args.push_back(cur->naturalType);
      } else {
        args.push_back(llvm::PointerType::getUnqual(cur->naturalType));
      }
    }

    if (!(ret->isSingleValueType()) && ret != llvm::Type::getVoidTy(llvm::getGlobalContext())) {
      args.push_back(llvm::PointerType::getUnqual(ret));
      ret = llvm::Type::getVoidTy(llvm::getGlobalContext());
      structRet = true;
    } else {
      structRet = false;
    }
    return llvm::FunctionType::get(ret, args, false);
}

const llvm::FunctionType* VMMethod::getSignature(VMGenericMethod* genMethod) {
  if (!_signature) {
    _signature = resolveSignature(parameters, !isStatic(flags), structReturn, genMethod);
  }
  return _signature;
}

const llvm::FunctionType* Property::getSignature(VMGenericMethod* genMethod) {
  if (!_signature) {
    bool structReturn = false;
    _signature = VMMethod::resolveSignature(parameters, virt, structReturn, genMethod);
  }
  return _signature;
}

bool VMCommonClass::implements(VMCommonClass* cl) {
  if (this == cl) return true;
  else {
    for (uint32 i = 0; i < interfaces.size(); i++) {
      VMCommonClass* cur = interfaces[i];
      if (cur == cl) return true;
      else if (cur->implements(cl)) return true;
    }
    if (super) {
      return super->implements(cl);
    }
  }
  return false;
}

bool VMCommonClass::instantiationOfArray(VMCommonClass* cl) {
  if (this == cl) return true;
  else {
    if (isArray && cl->isArray) {
      VMCommonClass* baseThis = ((VMClassArray*)this)->baseClass;
      VMCommonClass* baseCl = ((VMClassArray*)cl)->baseClass;

      if (isInterface(baseThis->flags) && isInterface(baseCl->flags)) {
        return baseThis->implements(baseCl);
      } else {
        return baseThis->isAssignableFrom(baseCl);
      }
    }
  }
  return false;
}

bool VMCommonClass::subclassOf(VMCommonClass* cl) {
  if (cl->depth < display.size()) {
    return display[cl->depth] == cl;
  } else {
    return false;
  }
}

bool VMCommonClass::isAssignableFrom(VMCommonClass* cl) {
  if (this == cl) {
    return true;
  } else if (isInterface(cl->flags)) {
    return this->implements(cl);
  } else if (cl->isArray) {
    return this->instantiationOfArray(cl);
  } else if (cl->isPointer){
    VMThread::get()->getVM()->error("implement me");
    return false;
  } else {
    return this->subclassOf(cl);
  }
}

VMMethod *VMMethod::compileToNative(VMGenericMethod* genMethod) {
	if(!code) {
		if (classDef->status < ready)
			classDef->resolveType(true, true, NULL);
  
		llvm::Function *methPtr = compiledPtr(genMethod);
		void* res = 
			mvm::MvmModule::executionEngine->getPointerToGlobalIfAvailable(methPtr);
		if (res == 0) {
			classDef->aquire();
			res = 
				mvm::MvmModule::executionEngine->getPointerToGlobalIfAvailable(methPtr);
			if (res == 0) {
				CLIJit::compile(classDef, this);
				void* res = mvm::MvmModule::executionEngine->getPointerToGlobal(methPtr);
				code = res;
				// N3* vm = VMThread::get()->getVM();
				// vm->addMethodInFunctionMap(this, res);
			}
			classDef->release();
			classDef->resolveStatic(true, NULL);
		}
	}
	return this;
}

bool VMMethod::signatureEquals(std::vector<VMCommonClass*>& args) {
  bool stat = isStatic(flags);
  if (args.size() != parameters.size()) return false;
  else {
    std::vector<VMCommonClass*>::iterator i = parameters.begin(), 
          a = args.begin(), e = args.end(); 
    
    if ((*i) != (*a)) return false;
    ++i; ++a;
    if (!stat) {
      ++i; ++a;
    }
    for( ; a != e; ++i, ++a) {
      if ((*i) != (*a)) return false;
    }
  }
  return true;
}

bool VMMethod::signatureEqualsGeneric(std::vector<VMCommonClass*> & args) {
	bool stat = isStatic(flags);

	if (args.size() != parameters.size())
		return false;
	else {
		std::vector<VMCommonClass*>::iterator i = parameters.begin(), a =
				args.begin(), e = args.end();

		// dummy classes for generic arguments have a NULL assembly field
		// check whether both i and a point to a dummy class
		if (((*i)->assembly == NULL && (*a)->assembly != NULL) ||
		    ((*i)->assembly != NULL && (*a)->assembly == NULL))
		  return false;
		
		// dummy classes for generic arguments contain the 
		// argument number in the token field
		// signature is only equal if the argument number matches
		if ((*i)->assembly == NULL && (*a)->assembly == NULL) {
		  if ((*i)->token != (*a)->token) {
		    return false;
		  }
		}
		
		if ((*i) != (*a))
			return false;
		++i;
		++a;

		if (!stat) {
			++i;
			++a;
		}

		for (; a != e; ++i, ++a) {
	    // dummy classes for generic arguments have a NULL assembly field
	    // check whether both i and a point to a dummy class
	    if (((*i)->assembly == NULL && (*a)->assembly != NULL) ||
	        ((*i)->assembly != NULL && (*a)->assembly == NULL))
	      return false;
	    
	    // dummy classes for generic arguments contain the 
	    // argument number in the token field
	    // signature is only equal if the argument number matches
	    if ((*i)->assembly == NULL && (*a)->assembly == NULL) {
	      if ((*i)->token != (*a)->token) {
	        return false;
	      } else {
	        continue;
	      }
	    }
	    
			if ((*i) != (*a))
				return false;
		}
	}
	return true;
}

void VMGenericClass::print(mvm::PrintBuffer* buf) const {
  buf->write("GenCLIType<");
  nameSpace->print(buf);
  buf->write("::");
  name->print(buf);
  buf->write(">");
}
