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


#include "Assembly.h"
#include "MSCorlib.h"
#include "N3.h"
#include "Reader.h"
#include "VMClass.h"
#include "VMThread.h"

#include "SignatureNames.def"

using namespace n3;

// ECMA 335: page 150 23.1.16 Element types used in signatures 


static VMCommonClass* METHOD_ElementTypeEnd(uint32 op, Assembly* ass,
    uint32& offset, VMGenericClass* genClass, VMGenericMethod* genMethod) {
  VMThread::get()->getVM()->error("implement me");
  return 0;
}

static VMCommonClass* METHOD_ElementTypeVoid(uint32 op, Assembly* ass,
    uint32& offset, VMGenericClass* genClass, VMGenericMethod* genMethod) {
  return MSCorlib::pVoid;
}

static VMCommonClass* METHOD_ElementTypeBoolean(uint32 op, Assembly* ass,
    uint32& offset, VMGenericClass* genClass, VMGenericMethod* genMethod) {
  return MSCorlib::pBoolean;
}

static VMCommonClass* METHOD_ElementTypeChar(uint32 op, Assembly* ass,
    uint32& offset, VMGenericClass* genClass, VMGenericMethod* genMethod) {
  return MSCorlib::pChar;
}

static VMCommonClass* METHOD_ElementTypeI1(uint32 op, Assembly* ass,
    uint32& offset, VMGenericClass* genClass, VMGenericMethod* genMethod) {
  return MSCorlib::pSInt8;
}

static VMCommonClass* METHOD_ElementTypeU1(uint32 op, Assembly* ass,
    uint32& offset, VMGenericClass* genClass, VMGenericMethod* genMethod) {
  return MSCorlib::pUInt8;
}

static VMCommonClass* METHOD_ElementTypeI2(uint32 op, Assembly* ass,
    uint32& offset, VMGenericClass* genClass, VMGenericMethod* genMethod) {
  return MSCorlib::pSInt16;
}

static VMCommonClass* METHOD_ElementTypeU2(uint32 op, Assembly* ass,
    uint32& offset, VMGenericClass* genClass, VMGenericMethod* genMethod) {
  return MSCorlib::pUInt16;
}

static VMCommonClass* METHOD_ElementTypeI4(uint32 op, Assembly* ass,
    uint32& offset, VMGenericClass* genClass, VMGenericMethod* genMethod) {
  return MSCorlib::pSInt32;
}

static VMCommonClass* METHOD_ElementTypeU4(uint32 op, Assembly* ass,
    uint32& offset, VMGenericClass* genClass, VMGenericMethod* genMethod) {
  return MSCorlib::pUInt32;
}

static VMCommonClass* METHOD_ElementTypeI8(uint32 op, Assembly* ass,
    uint32& offset, VMGenericClass* genClass, VMGenericMethod* genMethod) {
  return MSCorlib::pSInt64;
}

static VMCommonClass* METHOD_ElementTypeU8(uint32 op, Assembly* ass,
    uint32& offset, VMGenericClass* genClass, VMGenericMethod* genMethod) {
  return MSCorlib::pUInt64;
}

static VMCommonClass* METHOD_ElementTypeR4(uint32 op, Assembly* ass,
    uint32& offset, VMGenericClass* genClass, VMGenericMethod* genMethod) {
  return MSCorlib::pFloat;
}

static VMCommonClass* METHOD_ElementTypeR8(uint32 op, Assembly* ass,
    uint32& offset, VMGenericClass* genClass, VMGenericMethod* genMethod) {
  return MSCorlib::pDouble;
}

static VMCommonClass* METHOD_ElementTypeString(uint32 op, Assembly* ass,
    uint32& offset, VMGenericClass* genClass, VMGenericMethod* genMethod) {
  return MSCorlib::pString;
}

static VMCommonClass* METHOD_ElementTypePtr(uint32 op, Assembly* ass,
    uint32& offset, VMGenericClass* genClass, VMGenericMethod* genMethod) {
  VMCommonClass* contains = ass->exploreType(offset, genClass, genMethod);
  return ass->constructPointer(contains, 1);
}

static VMCommonClass* METHOD_ElementTypeByRef(uint32 op, Assembly* ass,
    uint32& offset, VMGenericClass* genClass, VMGenericMethod* genMethod) {
  VMCommonClass* contains = ass->exploreType(offset, genClass, genMethod);
  return ass->constructPointer(contains, 1);
}

static VMCommonClass* METHOD_ElementTypeValueType(uint32 op, Assembly* ass,
    uint32& offset, VMGenericClass* genClass, VMGenericMethod* genMethod) {
  uint32 value = ass->uncompressSignature(offset);
  uint32 table = value & 3;
  uint32 index = value >> 2;
  uint32 token = 0;

  switch (table) {
  case 0:
    table = CONSTANT_TypeDef;
    break;
  case 1:
    table = CONSTANT_TypeRef;
    break;
  case 2:
    table = CONSTANT_TypeSpec;
    break;
  default:
    VMThread::get()->getVM()->error("unknown TypeDefOrRefEncoded %d", index);
    break;
  }

  token = (table << 24) + index;
  VMCommonClass* cl = ass->loadType((N3*) (VMThread::get()->getVM()), token, false,
      false, false, true, genClass, genMethod);
  return cl;
}

static VMCommonClass* METHOD_ElementTypeClass(uint32 op, Assembly* ass,
    uint32& offset, VMGenericClass* genClass, VMGenericMethod* genMethod) {
  uint32 value = ass->uncompressSignature(offset);
  uint32 table = value & 3;
  uint32 index = value >> 2;
  uint32 token = 0;

  switch (table) {
  case 0:
    table = CONSTANT_TypeDef;
    break;
  case 1:
    table = CONSTANT_TypeRef;
    break;
  case 2:
    table = CONSTANT_TypeSpec;
    break;
  default:
    VMThread::get()->getVM()->error("unknown TypeDefOrRefEncoded %d", index);
    break;
  }

  token = (table << 24) + index;
  VMCommonClass* cl = ass->loadType((N3*) (VMThread::get()->getVM()), token, false,
      false, false, true, genClass, genMethod);
  return cl;
}

static VMCommonClass* METHOD_ElementTypeVar(uint32 op, Assembly* ass,
    uint32& offset, VMGenericClass* genClass, VMGenericMethod* genMethod) {
  uint32 number = ass->uncompressSignature(offset);

  assert(genClass != NULL && "Current Generic Class not set!");

  return genClass->genericParams[number];
}

static VMCommonClass* METHOD_ElementTypeArray(uint32 op, Assembly* ass,
    uint32& offset, VMGenericClass* genClass, VMGenericMethod* genMethod) {
  VMCommonClass* cl = ass->exploreType(offset, genClass, genMethod);
  uint32 rank = ass->uncompressSignature(offset);
  uint32 numSizes = ass->uncompressSignature(offset);

  if (numSizes != 0) {
    printf("type = %s\n", mvm::PrintBuffer(cl).cString());
    VMThread::get()->getVM()->error("implement me");
  }

  for (uint32 i = 0; i < numSizes; ++i) {
    ass->uncompressSignature(offset);
  }

  uint32 numObounds = ass->uncompressSignature(offset);
  if (numObounds != 0)
    VMThread::get()->getVM()->error("implement me");

  for (uint32 i = 0; i < numObounds; ++i) {
    ass->uncompressSignature(offset);
  }

  VMClassArray* array = ass->constructArray(cl, rank);
  return array;
}

static VMCommonClass* METHOD_ElementTypeGenericInst(uint32 op, Assembly* ass,
    uint32& offset, VMGenericClass* genClass, VMGenericMethod* genMethod) {
  // offset points to (CLASS | VALUETYPE) TypeDefOrRefEncoded

  // skip generic type definition
  offset++; // this is (CLASS | VALUETYPE)

  // save starting offset for later use
  uint32 genericTypeOffset = offset;

  ass->uncompressSignature(offset); // TypeDefOrRefEncoded

  //VMCommonClass* cl = ass->exploreType(offset);

  uint32 argCount = ass->uncompressSignature(offset);

  std::vector<VMCommonClass*> args;

  // Get generic arguments.
  for (uint32 i = 0; i < argCount; ++i) {
    args.push_back(ass->exploreType(offset, genClass, genMethod));
  }

  // save offset
  uint32 endOffset = offset;
  // restore starting offset
  offset = genericTypeOffset;

  // TypeDefOrRefEncoded
  uint32 value = ass->uncompressSignature(offset);
  uint32 table = value & 3;
  uint32 index = value >> 2;
  uint32 token = 0;

  switch (table) {
  case 0:
    table = CONSTANT_TypeDef;
    break;
  case 1:
    table = CONSTANT_TypeRef;
    break;
  case 2:
    table = CONSTANT_TypeSpec;
    break;
  default:
    VMThread::get()->getVM()->error("unknown TypeDefOrRefEncoded %d", index);
    break;
  }

  token = (table << 24) + index;
  VMCommonClass* cl = ass->loadType((N3*) (VMThread::get()->getVM()), token, false,
      false, false, true, args, genClass, genMethod);
  // restore endOffset
  offset = endOffset;

  return cl;
}

static VMCommonClass* METHOD_ElementTypeTypedByRef(uint32 op, Assembly* ass,
    uint32& offset, VMGenericClass* genClass, VMGenericMethod* genMethod) {
  return MSCorlib::typedReference;
}

static VMCommonClass* METHOD_ElementTypeI(uint32 op, Assembly* ass,
    uint32& offset, VMGenericClass* genClass, VMGenericMethod* genMethod) {
  return MSCorlib::pIntPtr;
}

static VMCommonClass* METHOD_ElementTypeU(uint32 op, Assembly* ass,
    uint32& offset, VMGenericClass* genClass, VMGenericMethod* genMethod) {
  return MSCorlib::pUIntPtr;
}

static VMCommonClass* METHOD_ElementTypeFnptr(uint32 op, Assembly* ass,
    uint32& offset, VMGenericClass* genClass, VMGenericMethod* genMethod) {
  VMThread::get()->getVM()->error("implement me");
  return 0;
}

static VMCommonClass* METHOD_ElementTypeObject(uint32 op, Assembly* ass,
    uint32& offset, VMGenericClass* genClass, VMGenericMethod* genMethod) {
  return MSCorlib::pObject;
}

static VMCommonClass* METHOD_ElementTypeSzarray(uint32 op, Assembly* ass,
    uint32& offset, VMGenericClass* genClass, VMGenericMethod* genMethod) {
  VMCommonClass* contains = ass->exploreType(offset, genClass, genMethod);
  VMClassArray* res = ass->constructArray(contains, 1);
  return res;
}

static VMCommonClass* METHOD_ElementTypeMvar(uint32 op, Assembly* ass,
    uint32& offset, VMGenericClass* genClass, VMGenericMethod *currGenericMethod) {
  uint32 number = ass->uncompressSignature(offset);

  if (currGenericMethod == NULL) {
    // when reading in signatures which contain references to generic arguments
    // of generic methods we need create a placeholder for each of them,
    // this is done by creating a dummy VMClass which has the assembly field
    // set to NULL, the token field is used to store the generic argument number
    VMClass* cl = new(ass->allocator, "VMClass") VMClass();
    cl->token = number;
    cl->assembly = ass;
    cl->nameSpace = ass->name;
    char *tmp = (char *) alloca(100);
    snprintf(tmp, 100, "!!%d", number);
    cl->name = VMThread::get()->getVM()->asciizToUTF8(tmp);
    return cl;
  } else {
    return currGenericMethod->genericParams[number];
  }
}

static VMCommonClass* METHOD_ElementTypeCmodReqd(uint32 op, Assembly* ass,
    uint32& offset, VMGenericClass* genClass, VMGenericMethod* genMethod) {
  VMThread::get()->getVM()->error("implement me");
  return 0;
}

static VMCommonClass* METHOD_ElementTypeCmodOpt(uint32 op, Assembly* ass,
    uint32& offset, VMGenericClass* genClass, VMGenericMethod* genMethod) {
  VMThread::get()->getVM()->error("implement me");
  return 0;
}

static VMCommonClass* METHOD_ElementTypeInternal(uint32 op, Assembly* ass,
    uint32& offset, VMGenericClass* genClass, VMGenericMethod* genMethod) {
  VMThread::get()->getVM()->error("implement me");
  return 0;
}

static VMCommonClass* METHOD_ElementTypeModifier(uint32 op, Assembly* ass,
    uint32& offset, VMGenericClass* genClass, VMGenericMethod* genMethod) {
  VMThread::get()->getVM()->error("implement me");
  return 0;
}

static VMCommonClass* METHOD_ElementTypeSentinel(uint32 op, Assembly* ass,
    uint32& offset, VMGenericClass* genClass, VMGenericMethod* genMethod) {
  VMThread::get()->getVM()->error("implement me");
  return 0;
}

static VMCommonClass* METHOD_ElementTypePinned(uint32 op, Assembly* ass,
    uint32& offset, VMGenericClass* genClass, VMGenericMethod* genMethod) {
  return 0;
}

static VMCommonClass* unimplemented(uint32 op, Assembly* ass, uint32& offset,
    VMGenericClass* genClass, VMGenericMethod* genMethod) {
  VMThread::get()->getVM()->error("unknown signature");
  return 0;
}

signatureVector_t Assembly::signatureVector[0x46] = { METHOD_ElementTypeEnd, // 0x00
    METHOD_ElementTypeVoid, // 0x01
    METHOD_ElementTypeBoolean, // 0x02
    METHOD_ElementTypeChar, // 0x03
    METHOD_ElementTypeI1, // 0x04
    METHOD_ElementTypeU1, // 0x05
    METHOD_ElementTypeI2, // 0x06
    METHOD_ElementTypeU2, // 0x07
    METHOD_ElementTypeI4, // 0x08
    METHOD_ElementTypeU4, // 0x09
    METHOD_ElementTypeI8, // 0x0A
    METHOD_ElementTypeU8, // 0x0B
    METHOD_ElementTypeR4, // 0x0C
    METHOD_ElementTypeR8, // 0x0D
    METHOD_ElementTypeString, // 0x0E
    METHOD_ElementTypePtr, // 0x1F
    METHOD_ElementTypeByRef, // 0x10
    METHOD_ElementTypeValueType, // 0x11
    METHOD_ElementTypeClass, // 0x12
    METHOD_ElementTypeVar, // 0x13
    METHOD_ElementTypeArray, // 0x14
    METHOD_ElementTypeGenericInst, // 0x15
    METHOD_ElementTypeTypedByRef, // 0x16
    unimplemented, // 0x17
    METHOD_ElementTypeI, // 0x18
    METHOD_ElementTypeU, // 0x19
    unimplemented, // 0x1A
    METHOD_ElementTypeFnptr, // 0x1B
    METHOD_ElementTypeObject, // 0x1C
    METHOD_ElementTypeSzarray, // 0x1D
    METHOD_ElementTypeMvar, // 0x1E
    METHOD_ElementTypeCmodReqd, // 0x1F
    METHOD_ElementTypeCmodOpt, // 0x20
    METHOD_ElementTypeInternal, // 0x21
    METHOD_ElementTypeModifier, // 0x22
    unimplemented, // 0x23
    unimplemented, // 0x24
    unimplemented, // 0x25
    unimplemented, // 0x26
    unimplemented, // 0x27
    unimplemented, // 0x28
    unimplemented, // 0x29
    unimplemented, // 0x2A
    unimplemented, // 0x2B
    unimplemented, // 0x2C
    unimplemented, // 0x2D
    unimplemented, // 0x2E
    unimplemented, // 0x2F
    unimplemented, // 0x30
    unimplemented, // 0x31
    unimplemented, // 0x32
    unimplemented, // 0x33
    unimplemented, // 0x34
    unimplemented, // 0x35
    unimplemented, // 0x36
    unimplemented, // 0x37
    unimplemented, // 0x38
    unimplemented, // 0x39
    unimplemented, // 0x3A
    unimplemented, // 0x3B
    unimplemented, // 0x3C
    unimplemented, // 0x3D
    unimplemented, // 0x3E
    unimplemented, // 0x3F
    unimplemented, // 0x40
    METHOD_ElementTypeSentinel, // 0x41
    unimplemented, // 0x42
    unimplemented, // 0x43
    unimplemented, // 0x44
    METHOD_ElementTypePinned // 0x45
    };

bool Assembly::extractMethodSignature(uint32& offset, VMCommonClass* cl,
    std::vector<VMCommonClass*>& types, VMGenericClass* genClass, VMGenericMethod* genMethod) {
  //uint32 count      = 
  uncompressSignature(offset);
  uint32 call = uncompressSignature(offset);

  if (call & CONSTANT_Generic) {
    //uint32 genArgCount =
    uncompressSignature(offset);
  }

  uint32 paramCount = uncompressSignature(offset);

  uint32 hasThis = call & CONSTANT_HasThis ? 1 : 0;
  uint32 realCount = paramCount + hasThis;

  VMCommonClass* ret = exploreType(offset, genClass, genMethod);
  types.push_back(ret);

  if (hasThis) {
    types.push_back(cl);
  }

  for (uint32 i = hasThis; i < realCount; ++i) {
    VMCommonClass* cur = exploreType(offset, genClass, genMethod);
    types.push_back(cur);
  }

  return hasThis != 0;
}

// checks whether the MethodDefSig at offset contains generic parameters
bool Assembly::isGenericMethod(uint32& offset) {
  uncompressSignature(offset); // count

  uint32 callingConvention = READ_U1(bytes, offset);

  return callingConvention & CONSTANT_Generic ? true : false;
}

void Assembly::methodSpecSignature(uint32& offset,
    std::vector<VMCommonClass*>& genArgs, VMGenericClass* genClass, VMGenericMethod* genMethod) {
  uncompressSignature(offset); // count
  uint32 genericSig = uncompressSignature(offset);

  if (genericSig != 0x0a) {
    VMThread::get()->getVM()->error("unknown methodSpec sig %x", genericSig);
  }

  uint32 genArgCount = uncompressSignature(offset);

  for (uint32 i = 0; i < genArgCount; i++) {
    genArgs.push_back(exploreType(offset, genClass, genMethod));
  }
}

void Assembly::localVarSignature(uint32& offset,
    std::vector<VMCommonClass*>& locals, VMGenericClass* genClass, VMGenericMethod* genMethod) {
  //uint32 count      = 
  uncompressSignature(offset);
  uint32 localSig = uncompressSignature(offset);
  uint32 nbLocals = uncompressSignature(offset);

  if (localSig != 0x7) {
    VMThread::get()->getVM()->error("unknown local sig %x", localSig);
  }

  for (uint32 i = 0; i < nbLocals; ++i) {
    VMCommonClass* cl = exploreType(offset, genClass, genMethod);
    if (!cl)
      --i; // PINNED
    else
      locals.push_back(cl);
  }
}

VMCommonClass* Assembly::extractFieldSignature(uint32& offset, VMGenericClass* genClass, VMGenericMethod* genMethod) {
  //uint32 count      = 
  uncompressSignature(offset);
  uint32 fieldSig = uncompressSignature(offset);

  if (fieldSig != 0x6) {
    VMThread::get()->getVM()->error("unknown field sig %x", fieldSig);
  }

  // TODO implement support for custom modifiers
  //      see ECMA 335 23.2.4, 23.2.7 

  return exploreType(offset, genClass, genMethod);

}

VMCommonClass* Assembly::extractTypeInSignature(uint32& offset, VMGenericClass* genClass, VMGenericMethod* genMethod) {
  //uint32 count      = 
  uncompressSignature(offset);
  return exploreType(offset, genClass, genMethod);
}

VMCommonClass* Assembly::exploreType(uint32& offset, VMGenericClass* genClass, VMGenericMethod* genMethod) {
  uint32 op = READ_U1(bytes, offset);
  assert(op < 0x46 && "unknown signature type");
  return (signatureVector[op]) (op, this, offset, genClass, genMethod);
}

uint32 Assembly::uncompressSignature(uint32& offset) {
  uint32 value = READ_U1(bytes, offset);

  if ((value & 0x80) == 0) {
    return value;
  } else if ((value & 0x40) == 0) {
    uint32 val2 = READ_U1(bytes, offset);
    return (((value & 0x3f) << 8) | val2);
  } else {
    uint32 val2 = READ_U1(bytes, offset);
    uint32 val3 = READ_U1(bytes, offset);
    uint32 val4 = READ_U1(bytes, offset);
    return ((value & 0x1f) << 24) | (val2 << 16) | (val3 << 8) | val4;
  }
}
