//===-- Resolver.cpp - Class resolver for Java classes ----------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file was developed by the LLVM research group and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file contains the implementation of the Java class resolver.
//
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "javaresolver"

#include "Resolver.h"
#include <llvm/Java/ClassFile.h>
#include <llvm/Constants.h>
#include <llvm/DerivedTypes.h>
#include <llvm/Support/Debug.h>
#include <iostream>

using namespace llvm;
using namespace llvm::Java;

Resolver::Resolver(Module* module)
  : module_(module),
    nextInterfaceIndex_(0),
    objectBaseLayoutType_(OpaqueType::get()),
    objectBaseType_(PointerType::get(objectBaseLayoutType_)),
    classRecordType_(OpaqueType::get()),
    classRecordPtrType_(PointerType::get(classRecordType_))
{
  module_->addTypeName("struct.llvm_java_object_base",
                       getObjectBaseLayoutType());

  // Compute the class record type. A class record looks like:
  //
  // struct class_record {
  //   struct type_info;
  // };
  //
  // struct type_info {
  //   char* name;
  //   int depth;
  //   struct class_record** superclasses;
  //   int interfaceIndex;
  //   struct class_record** interfaces;
  //   struct class_record* component;
  //   int elementSize;
  //   char** fieldDescriptors;
  //   unsigned* fieldOffsets;
  //   char** staticFieldDescriptors;
  //   void** staticFields;
  //   char** MethodDescriptors;
  //   void** Methods;
  //   char** staticMethodDescriptors;
  //   void** staticMethods;
  // };

  // Compute the type_info type.
  std::vector<const Type*> elements;
  elements.push_back(PointerType::get(Type::SByteTy));
  elements.push_back(Type::IntTy);
  elements.push_back(PointerType::get(PointerType::get(classRecordType_)));
  elements.push_back(Type::IntTy);
  elements.push_back(PointerType::get(PointerType::get(classRecordType_)));
  elements.push_back(PointerType::get(classRecordType_));
  elements.push_back(Type::IntTy);
  elements.push_back(PointerType::get(PointerType::get(Type::SByteTy)));
  elements.push_back(PointerType::get(Type::UIntTy));
  elements.push_back(PointerType::get(PointerType::get(Type::SByteTy)));
  elements.push_back(PointerType::get(PointerType::get(Type::SByteTy)));
  elements.push_back(PointerType::get(PointerType::get(Type::SByteTy)));
  elements.push_back(PointerType::get(PointerType::get(Type::SByteTy)));
  elements.push_back(PointerType::get(PointerType::get(Type::SByteTy)));
  elements.push_back(PointerType::get(PointerType::get(Type::SByteTy)));
  typeInfoType_ = StructType::get(elements);

  module_->addTypeName("struct.llvm_java_typeinfo", getTypeInfoType());

  // Compute the class_record type.
  PATypeHolder holder = classRecordType_;
  cast<OpaqueType>(const_cast<Type*>(classRecordType_))->refineAbstractTypeTo(
    StructType::get(std::vector<const Type*>(1, getTypeInfoType())));
  classRecordType_ = holder.get();

  module_->addTypeName("struct.llvm_java_class_record", getClassRecordType());

  classRecordPtrType_ = PointerType::get(classRecordType_);
}

const Type* Resolver::getType(const std::string& descriptor,
                              bool memberMethod) const
{
  unsigned i = 0;
  return getTypeHelper(descriptor, i, memberMethod);
}

const Type* Resolver::getTypeHelper(const std::string& descr,
                                    unsigned& i,
                                    bool memberMethod) const
{
  assert(i < descr.size());
  switch (descr[i++]) {
  case 'B': return Type::SByteTy;
  case 'C': return Type::UShortTy;
  case 'D': return Type::DoubleTy;
  case 'F': return Type::FloatTy;
  case 'I': return Type::IntTy;
  case 'J': return Type::LongTy;
  case 'S': return Type::ShortTy;
  case 'Z': return Type::BoolTy;
  case 'V': return Type::VoidTy;
  case 'L': {
    unsigned e = descr.find(';', i);
    i = e + 1;
    return getObjectBaseType();
  }
  case '[':
    // Skip '['s.
    if (descr[i] == '[')
      do { ++i; } while (descr[i] == '[');
    // Consume the element type
    getTypeHelper(descr, i);
    return getObjectBaseType();
  case '(': {
    std::vector<const Type*> params;
    if (memberMethod)
      params.push_back(getObjectBaseType());
    while (descr[i] != ')')
      params.push_back(getTypeHelper(descr, i));
    return FunctionType::get(getTypeHelper(descr, ++i), params, false);
  }
    // FIXME: Throw something
  default:  assert(0 && "Cannot parse type descriptor!");
  }
  return 0; // not reached
}

const VMClass* Resolver::getClassForDesc(const std::string& descriptor)
{
  ClassMap::iterator it = classMap_.lower_bound(descriptor);
  if (it == classMap_.end() || it->first != descriptor) {
    switch (descriptor[0]) {
    case 'B':
      it = insertClass(it, VMClass(this, Type::SByteTy));
      break;
    case 'C':
      it = insertClass(it, VMClass(this, Type::UShortTy));
      break;
    case 'D':
      it = insertClass(it, VMClass(this, Type::DoubleTy));
      break;
    case 'F':
      it = insertClass(it, VMClass(this, Type::FloatTy));
      break;
    case 'I':
      it = insertClass(it, VMClass(this, Type::IntTy));
      break;
    case 'J':
      it = insertClass(it, VMClass(this, Type::LongTy));
      break;
    case 'S':
      it = insertClass(it, VMClass(this, Type::ShortTy));
      break;
    case 'Z':
      it = insertClass(it, VMClass(this, Type::BoolTy));
      break;
    case 'V':
      it = insertClass(it, VMClass(this, Type::VoidTy));
      break;
    case 'L': {
      unsigned pos = descriptor.find(';', 1);
      const std::string& className = descriptor.substr(1, pos - 1);
      it = insertClass(it, VMClass(this, className));
      break;
    }
    case '[': {
      const std::string& componentDescriptor = descriptor.substr(1);
      it = insertClass(it, VMClass(this, getClassForDesc(componentDescriptor)));
      break;
    }
    default:
      assert(0 && "Cannot parse type descriptor!");
      abort();
    }
    it->second.link();
    if (!it->second.isPrimitive() && !it->second.isInterface())
      module_->addTypeName("struct." + descriptor, it->second.getLayoutType());
    DEBUG(std::cerr << "Loaded class: " << it->second.getName());
    DEBUG(std::cerr << " (" << it->second.getInterfaceIndex() << ")\n");
  }

  return &it->second;
}

const VMClass* Resolver::getClass(JType type)
{
  switch (type) {
  case BOOLEAN: return getClassForDesc("Z");
  case CHAR: return getClassForDesc("C");
  case FLOAT: return getClassForDesc("F");
  case DOUBLE: return getClassForDesc("D");
  case BYTE: return getClassForDesc("B");
  case SHORT: return getClassForDesc("S");
  case INT: return getClassForDesc("I");
  case LONG: return getClassForDesc("J");
  default: assert(0 && "Unhandled JType!"); abort();
  }
}

const Type* Resolver::getStorageType(const Type* type) const
{
  if (isa<PointerType>(type))
    return getObjectBaseType();
  else if (type == Type::BoolTy ||
           type == Type::UByteTy ||
           type == Type::SByteTy ||
           type == Type::UShortTy ||
           type == Type::ShortTy ||
           type == Type::UIntTy)
    return Type::IntTy;
  else if (type == Type::ULongTy)
    return Type::LongTy;
  else
    return type;
}

void Resolver::emitClassRecordsArray() const
{
  std::vector<llvm::Constant*> init;
  init.reserve(classMap_.size() + 1);

  for (ClassMap::const_iterator i = classMap_.begin(), e = classMap_.end();
       i != e; ++i)
    init.push_back(ConstantExpr::getCast(i->second.getClassRecord(),
                                         classRecordPtrType_));

  // Null terminate the array.
  init.push_back(llvm::Constant::getNullValue(classRecordPtrType_));

  const ArrayType* arrayType = ArrayType::get(classRecordPtrType_, init.size());

  new GlobalVariable(
    arrayType,
    true,
    GlobalVariable::ExternalLinkage,
    ConstantArray::get(arrayType, init),
    "llvm_java_class_records",
    module_);                       
}
