/*
 * Copyright (C) 2018-2020 Western Digital Corporation or its affiliates
 * Copyright (C) 2017-2018 Wearable, Inc.
 * Copyright (C) 2000-2012 leJOS Contributors
 * Copyright (C) 2000 Jose H. Solorzano and TinyVM Contributors
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
 * 
 * This Source Code Form is “Incompatible With Secondary Licenses”,
 * as defined by the Mozilla Public License, v. 2.0.
 */

#include "types.h"
#include "classes.h"
#include "memory.h"
#include "state.h"

#ifndef _LANGUAGE_H
#define _LANGUAGE_H

// Class flags:
#define C_INITIALIZED  0x01
#define C_ARRAY        0x02
#define C_HASCLINIT    0x04
#define C_INTERFACE    0x08
#define C_NOREFS       0x10
#define C_PRIMITIVE    0x20
#define C_INITIALIZING 0x40

typedef struct S_MasterRecord
{
    uint16_t magicNumber;
    uint16_t runtimeOptions;
    nvm_binary_size_t entryClassesOffset;
    nvm_binary_size_t methodTableOffset;
    nvm_binary_size_t instanceFieldTableOffset;
    nvm_binary_size_t constantTableOffset;
    nvm_binary_size_t constantValuesOffset;
    nvm_binary_size_t staticFieldsOffset;
    nvm_binary_size_t indirectIndexTableOffset;
    nvm_binary_size_t interfaceMapsOffset;
    uint16_t numConstants;
    uint16_t staticStateLength;
    uint16_t numStaticFields;
    uint16_t numEntryClasses;
    nvm_class_index_t lastClass;
    uint16_t magicNumberAgain;
} MasterRecord;

_Static_assert(sizeof(MasterRecord) == 48, "sizeof(MasterRecord) == 48");

// Method flags:
#define M_NATIVE       0x01
#define M_SYNCHRONIZED 0x02
#define M_STATIC       0x04

typedef struct S_MethodRecord
{
    // Unique id for the signature of the method
    uint16_t signatureId;
    // Number of 32-bit locals (long is counted as 2 locals).
    byte numLocals;
    // Maximum size of local operand stack, in 32-bit words.
    byte maxOperands;
    // It should be such that stackTop-numParameters unwinds the stack.
    // The receiver in non-static methods is counted as one word.
    byte numParameters;
    // Number of exception handlers
    byte numExceptionHandlers;
    byte mflags;
    byte unused;
    // Offset to table of exception information
    nvm_binary_size_t exceptionTable;
    nvm_binary_size_t codeOffset;
} MethodRecord;

_Static_assert(sizeof(MethodRecord) == 16, "sizeof(MethodRecord) == 16");

typedef struct S_ExceptionRecord
{
  uint16_t start;
  uint16_t end;
  uint16_t handler;
  // The index of a Throwable class.
  nvm_class_index_t class_index;
} ExceptionRecord;

typedef uint16_t STATICFIELD;

typedef struct {
    nvm_class_index_t class_index;
    nvm_method_index_t method_index;
} IndirectIndexRecord;

_Static_assert(sizeof(IndirectIndexRecord) == 4, "sizeof(IndirectIndexrecord) == 4");

#define get_master_record(state)         ((MasterRecord *)(void*)state->installed_binary)
#define get_magic_number(state)          get_master_record(state)->magicNumber
#define get_binary_base(state)           ((byte *) state->installed_binary)
#define get_method_table_start(state)    ((MethodRecord*) (get_binary_base(state) + get_master_record(state)->methodTableOffset))
#define get_field_table_start(state)     (get_binary_base(state) + get_master_record(state)->instanceFieldTableOffset)
#define __get_class_base(state)          ((ClassRecord*)(void*)(get_binary_base(state) + sizeof(MasterRecord)))
#define get_class_base(state)            ((ClassRecord *) (state->class_base))

#define get_class_record(state, CLASSIDX_) (get_class_base(state) + (CLASSIDX_))
#define get_class_number(state, CREC_)     ((STACKWORD) ((CREC_) - get_class_base(state)))
#define get_method_table(state, CREC_)     (get_method_table_start(state) + (CREC_)->CIAData1)

#define get_field_table(state, CREC_)      (get_field_table_start(state) + (CREC_)->CIAData2)

#define get_field_type(state, CR_,I_)      (*(get_field_table(state, CR_) + (I_)))

#define get_method_record(state, CR_,I_)   (get_method_table(state, CR_) + (I_)) 

#define __get_constant_base(state)       ((ConstantRecord *)(void*)(get_binary_base(state) + get_master_record(state)->constantTableOffset))
#define get_constant_base(state)         (state->constant_table_base)

#define get_constant_record(state, IDX_)   (get_constant_base(state) + (IDX_))

#define get_constant_ptr(state, CR_)       (get_binary_base(state) + (CR_)->offset)
#define __get_constant_values_base(state) (get_binary_base(state) + get_master_record(state)->constantValuesOffset)
#define get_constant_values_base(state)  (state->constant_values_base)

static inline IndirectIndexRecord* get_indirect_index_record(nvm_state_t* state, int index)
{
    IndirectIndexRecord* table = (void*)(get_binary_base(state) + get_master_record(state)->indirectIndexTableOffset);
    return &table[index];
}

#define class_size(CLASSIDX_)       (get_class_record(CLASSIDX_)->classSize)

#define get_class_status(state, CREC_)     (state->class_status_base[ CREC_ - get_class_base(state)])

#define is_array_class(CREC_)       (((CREC_)->cflags & C_ARRAY) != 0)
#define has_clinit(CREC_)           (((CREC_)->cflags & C_HASCLINIT) != 0)
#define is_interface(CREC_)         (((CREC_)->cflags & C_INTERFACE) != 0)
#define has_norefs(CREC_)           (((CREC_)->cflags & C_NOREFS) != 0)

#define get_dim(CREC_)              ((CREC_)->CIAData1)
#define get_element_class(CREC_)    ((CREC_)->CIAData2)
#define get_method_cnt(CREC_)        ((CREC_)->CIACnt1)
#define get_field_cnt(CREC_)        ((CREC_)->CIACnt2)
#define get_interface_map(state, CREC_)    ((byte*)(get_binary_base(state) + get_master_record(state)->interfaceMapsOffset + (CREC_)->CIAData2))
#define get_interface_map_base(CREC_) ((CREC_)->CIACnt1)
#define get_interface_map_len(CREC_) ((CREC_)->CIACnt2)
#define is_primitive(CLASSIDX_)     ((CLASSIDX_) >= BOOLEAN && (CLASSIDX_) <= LONG )
#define set_init_state(sx, CREC_, state)(get_class_status(sx, CREC_) |= (state))
#define get_init_state_idx(state, IDX_)    (state->class_status_base[IDX_])
#define get_init_state(state, CREC_)       (get_class_status(state, CREC_))
#define is_initialized_idx(state, IDX_)    (get_init_state_idx(state, IDX_) & C_INITIALIZED)
#define is_initialized(CREC_)       (get_init_state(CREC_) & C_INITIALIZED)

#define is_synchronized(MREC_)      (((MREC_)->mflags & M_SYNCHRONIZED) != 0)
#define is_static(MREC_)      (((MREC_)->mflags & M_STATIC) != 0)
#define is_native(MREC_)            (((MREC_)->mflags & M_NATIVE) != 0)
#define get_code_ptr(state, MREC_)         (get_binary_base(state) + (MREC_)->codeOffset)

#define __get_static_fields_base(state)  (get_binary_base(state) + get_master_record(state)->staticFieldsOffset)
#define get_static_fields_base(state)    (state->static_fields_base)

#define get_static_state_base(state)     (state->class_static_state_base)

#define get_static_field_offset(R_) ((R_) & 0x0FFF)

// return codes used to indicate the state of byte code execution
#define EXEC_RETRY   -1 /* a retry of the current instrucion is required */
#define EXEC_CONTINUE 0 /* No action required simply return/continue */
#define EXEC_RUN      1 /* Execute from the new value now in program_counter */
#define EXEC_EXCEPTION 2 /* An exception has been thrown PC will be correct */

void dispatch_virtual (nvm_state_t* pState, Object *obj, int signature, const byte *rAddr);
MethodRecord *find_method (nvm_state_t* pState, ClassRecord *classRec, int signature);
boolean instance_of (nvm_state_t* pState, Object *obj, const nvm_class_index_t class_index);
void do_return (nvm_state_t* pState, int numWords);
int dispatch_static_initializer (nvm_state_t* pState, ClassRecord *aRec, const byte *rAddr);
boolean dispatch_special (nvm_state_t* pState, MethodRecord *methodRecord, const byte *retAddr);
void dispatch_special_checked (nvm_state_t* pState, nvm_class_index_t class_index, nvm_method_index_t methodIndex, const byte *retAddr, const byte *btAddr);
void dispatch_special_checked_indirect(nvm_state_t* pState, nvm_class_index_t index, const byte *retAddr, const byte *btAddr);

Object *create_stack_trace(nvm_state_t* pState, Thread *thread, Object *ignore);
int execute_program(nvm_state_t* pState);
int call_exception_handler(nvm_state_t* pState, Throwable *exception, STACKWORD method, STACKWORD pc);
void call_run(nvm_state_t* pState, Thread* thread);
boolean is_assignable(nvm_state_t* pState, const nvm_class_index_t srcSig, const nvm_class_index_t dstSig);
byte get_base_type(ClassRecord *classRec);

void install_binary(nvm_state_t* pState, const byte *ptr);
boolean is_valid_executable(byte * start, int len);
void empty_stacks(nvm_state_t* pState);
void dispatch_java(nvm_state_t* pState, nvm_class_index_t class_index, byte sig, const byte *retAddr, const byte *btAddr);

#endif
