/*
 * Copyright (c) 1998, 2005, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

#ifndef JDWP_UTIL_H
#define JDWP_UTIL_H

#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>

#ifdef DEBUG
    /* Just to make sure these interfaces are not used here. */
    #undef free
    #define free(p) Do not use this interface.
    #undef malloc
    #define malloc(p) Do not use this interface.
    #undef calloc
    #define calloc(p) Do not use this interface.
    #undef realloc
    #define realloc(p) Do not use this interface.
    #undef strdup
    #define strdup(p) Do not use this interface.
#endif

#include "log_messages.h"
#include "vm_interface.h"
#include "JDWP.h"
#include "util_md.h"
#include "error_messages.h"
#include "debugInit.h"

/* Get access to Native Platform Toolkit functions */
#include "npt.h"

/* ANDROID-CHANGED: We want to avoid allocating jweaks on android so if !isStrong we will use the
 * node-pointer tag as the weak-reference.
 */
/* Definition of a CommonRef tracked by the backend for the frontend */
typedef struct RefNode {
    jlong        seqNum;        /* ID of reference, also key for hash table */
    jobject      ref;           /* ANDROID-CHANGED: Always the strong reference if isStrong, NULL
                                 * otherwise.
                                 */
    struct RefNode *next;       /* next RefNode* in bucket chain */
    struct RefNode *prev;       /* ANDROID-CHANGED: Previous RefNode* in bucket chain. Used to allow
                                 * us to remove arbitrary elements. */
    jint         count;         /* count of references */
    unsigned     isStrong : 1;  /* 1 means this is a strong reference */
} RefNode;

/* Value of a NULL ID */
#define NULL_OBJECT_ID  ((jlong)0)

/*
 * Globals used throughout the back end
 */

typedef jint FrameNumber;

// ANDROID-CHANGED: support for DDMS extension apis.
typedef jvmtiError (*DdmProcessChunk)(jvmtiEnv* jvmti,
                                      jint type_in,
                                      jint length_in,
                                      const jbyte* data_in,
                                      jint* type_out,
                                      jint* length_out,
                                      jbyte** data_out);
typedef jvmtiError (*RawMonitorEnterNoSuspend)(jvmtiEnv* env, jrawMonitorID mon);

typedef struct {
    jvmtiEnv *jvmti;
    JavaVM   *jvm;
    volatile jboolean vmDead; /* Once VM is dead it stays that way - don't put in init */
    jboolean assertOn;
    jboolean assertFatal;
    jboolean doerrorexit;
    jboolean modifiedUtf8;
    jboolean quiet;

    /* Debug flags (bit mask) */
    int      debugflags;

    /* Possible debug flags */
    #define USE_ITERATE_THROUGH_HEAP 0X001

    char * options;

    jclass              classClass;
    jclass              threadClass;
    jclass              threadGroupClass;
    jclass              classLoaderClass;
    jclass              stringClass;
    jclass              systemClass;
    jmethodID           threadConstructor;
    jmethodID           threadSetDaemon;
    jmethodID           threadResume;
    jmethodID           systemGetProperty;
    jmethodID           setProperty;
    jthreadGroup        systemThreadGroup;
    jobject             agent_properties;

    jint                cachedJvmtiVersion;
    jvmtiCapabilities   cachedJvmtiCapabilities;
    jboolean            haveCachedJvmtiCapabilities;
    jvmtiEventCallbacks callbacks;

    /* Various property values we should grab on initialization */
    char* property_java_version;          /* UTF8 java.version */
    char* property_java_vm_name;          /* UTF8 java.vm.name */
    char* property_java_vm_info;          /* UTF8 java.vm.info */
    char* property_java_class_path;       /* UTF8 java.class.path */
    char* property_sun_boot_class_path;   /* UTF8 sun.boot.class.path */
    char* property_sun_boot_library_path; /* UTF8 sun.boot.library.path */
    char* property_path_separator;        /* UTF8 path.separator */
    char* property_user_dir;              /* UTF8 user.dir */

    unsigned log_flags;

    /* The Native Platform Toolkit access */
    NptEnv *npt;

    /* Common References static data */
    jrawMonitorID refLock;
    jlong         nextSeqNum;
    RefNode     **objectsByID;
    int           objectsByIDsize;
    int           objectsByIDcount;

     /* Indication that the agent has been loaded */
     jboolean isLoaded;

     /* ANDROID-CHANGED: com.android.art.internal.ddm.process_chunk extension function */
     DdmProcessChunk ddm_process_chunk;
     RawMonitorEnterNoSuspend raw_monitor_enter_no_suspend;

     /* ANDROID-CHANGED: Need to keep track of if ddm is initially active. */
     jboolean ddmInitiallyActive;

} BackendGlobalData;

extern BackendGlobalData * gdata;

/*
 * Event Index for handlers
 */

typedef enum {
        EI_min                  =  1,

        EI_SINGLE_STEP          =  1,
        EI_BREAKPOINT           =  2,
        EI_FRAME_POP            =  3,
        EI_EXCEPTION            =  4,
        EI_THREAD_START         =  5,
        EI_THREAD_END           =  6,
        EI_CLASS_PREPARE        =  7,
        EI_GC_FINISH            =  8,
        EI_CLASS_LOAD           =  9,
        EI_FIELD_ACCESS         = 10,
        EI_FIELD_MODIFICATION   = 11,
        EI_EXCEPTION_CATCH      = 12,
        EI_METHOD_ENTRY         = 13,
        EI_METHOD_EXIT          = 14,
        EI_MONITOR_CONTENDED_ENTER = 15,
        EI_MONITOR_CONTENDED_ENTERED = 16,
        EI_MONITOR_WAIT         = 17,
        EI_MONITOR_WAITED       = 18,
        EI_VM_INIT              = 19,
        EI_VM_DEATH             = 20,
        EI_max                  = 20
} EventIndex;

/* Agent errors that might be in a jvmtiError for JDWP or internal.
 *    (Done this way so that compiler allows it's use as a jvmtiError)
 */
#define _AGENT_ERROR(x)                 ((jvmtiError)(JVMTI_ERROR_MAX+64+x))
#define AGENT_ERROR_INTERNAL                    _AGENT_ERROR(1)
#define AGENT_ERROR_VM_DEAD                     _AGENT_ERROR(2)
#define AGENT_ERROR_NO_JNI_ENV                  _AGENT_ERROR(3)
#define AGENT_ERROR_JNI_EXCEPTION               _AGENT_ERROR(4)
#define AGENT_ERROR_JVMTI_INTERNAL              _AGENT_ERROR(5)
#define AGENT_ERROR_JDWP_INTERNAL               _AGENT_ERROR(6)
#define AGENT_ERROR_NOT_CURRENT_FRAME           _AGENT_ERROR(7)
#define AGENT_ERROR_OUT_OF_MEMORY               _AGENT_ERROR(8)
#define AGENT_ERROR_INVALID_TAG                 _AGENT_ERROR(9)
#define AGENT_ERROR_ALREADY_INVOKING            _AGENT_ERROR(10)
#define AGENT_ERROR_INVALID_INDEX               _AGENT_ERROR(11)
#define AGENT_ERROR_INVALID_LENGTH              _AGENT_ERROR(12)
#define AGENT_ERROR_INVALID_STRING              _AGENT_ERROR(13)
#define AGENT_ERROR_INVALID_CLASS_LOADER        _AGENT_ERROR(14)
#define AGENT_ERROR_INVALID_ARRAY               _AGENT_ERROR(15)
#define AGENT_ERROR_TRANSPORT_LOAD              _AGENT_ERROR(16)
#define AGENT_ERROR_TRANSPORT_INIT              _AGENT_ERROR(17)
#define AGENT_ERROR_NATIVE_METHOD               _AGENT_ERROR(18)
#define AGENT_ERROR_INVALID_COUNT               _AGENT_ERROR(19)
#define AGENT_ERROR_INVALID_FRAMEID             _AGENT_ERROR(20)
#define AGENT_ERROR_NULL_POINTER                _AGENT_ERROR(21)
#define AGENT_ERROR_ILLEGAL_ARGUMENT            _AGENT_ERROR(22)
#define AGENT_ERROR_INVALID_THREAD              _AGENT_ERROR(23)
#define AGENT_ERROR_INVALID_EVENT_TYPE          _AGENT_ERROR(24)
#define AGENT_ERROR_INVALID_OBJECT              _AGENT_ERROR(25)
#define AGENT_ERROR_NO_MORE_FRAMES              _AGENT_ERROR(26)

/* Combined event information */

typedef struct {

    EventIndex  ei;
    jthread     thread;
    jclass      clazz;
    jmethodID   method;
    jlocation   location;
    jobject     object; /* possibly an exception or user object */

    union {

        /* ei = EI_FIELD_ACCESS */
        struct {
            jclass      field_clazz;
            jfieldID    field;
        } field_access;

        /* ei = EI_FIELD_MODIFICATION */
        struct {
            jclass      field_clazz;
            jfieldID    field;
            char        signature_type;
            jvalue      new_value;
        } field_modification;

        /* ei = EI_EXCEPTION */
        struct {
            jclass      catch_clazz;
            jmethodID   catch_method;
            jlocation   catch_location;
        } exception;

        /* ei = EI_METHOD_EXIT */
        struct {
            jvalue      return_value;
        } method_exit;

        /* For monitor wait events */
        union {
            /* ei = EI_MONITOR_WAIT */
            jlong timeout;
            /* ei = EI_MONITOR_WAITED */
            jboolean timed_out;
        } monitor;
    } u;

} EventInfo;

/* Structure to hold dynamic array of objects */
typedef struct ObjectBatch {
    jobject *objects;
    jint     count;
} ObjectBatch;

/*
 * JNI signature constants, beyond those defined in JDWP_TAG(*)
 */
#define SIGNATURE_BEGIN_ARGS    '('
#define SIGNATURE_END_ARGS      ')'
#define SIGNATURE_END_CLASS     ';'

/*
 * Modifier flags for classes, fields, methods
 */
#define MOD_PUBLIC       0x0001     /* visible to everyone */
#define MOD_PRIVATE      0x0002     /* visible only to the defining class */
#define MOD_PROTECTED    0x0004     /* visible to subclasses */
#define MOD_STATIC       0x0008     /* instance variable is static */
#define MOD_FINAL        0x0010     /* no further subclassing, overriding */
#define MOD_SYNCHRONIZED 0x0020     /* wrap method call in monitor lock */
#define MOD_VOLATILE     0x0040     /* can cache in registers */
#define MOD_TRANSIENT    0x0080     /* not persistant */
#define MOD_NATIVE       0x0100     /* implemented in C */
#define MOD_INTERFACE    0x0200     /* class is an interface */
#define MOD_ABSTRACT     0x0400     /* no definition provided */
/*
 * Additional modifiers not defined as such in the JVM spec
 */
#define MOD_SYNTHETIC    0xf0000000  /* not in source code */

/*
 * jlong conversion macros
 */
#define jlong_zero       ((jlong) 0)
#define jlong_one        ((jlong) 1)

#define jlong_to_ptr(a)  ((void*)(intptr_t)(a))
#define ptr_to_jlong(a)  ((jlong)(intptr_t)(a))
#define jint_to_jlong(a) ((jlong)(a))
#define jlong_to_jint(a) ((jint)(a))


/*
 * util funcs
 */
void util_initialize(JNIEnv *env);
void util_reset(void);

struct PacketInputStream;
struct PacketOutputStream;

jint uniqueID(void);
jbyte referenceTypeTag(jclass clazz);
jbyte specificTypeKey(JNIEnv *env, jobject object);
jboolean isObjectTag(jbyte tag);
jvmtiError spawnNewThread(jvmtiStartFunction func, void *arg, char *name);
void convertSignatureToClassname(char *convert);
void writeCodeLocation(struct PacketOutputStream *out, jclass clazz,
                       jmethodID method, jlocation location);

jvmtiError classInstances(jclass klass, ObjectBatch *instances, int maxInstances);
jvmtiError classInstanceCounts(jint classCount, jclass *classes, jlong *counts);
jvmtiError objectReferrers(jobject obj, ObjectBatch *referrers, int maxObjects);

// ANDROID-CHANGED: Helper function to get current time in milliseconds on CLOCK_MONOTONIC
jlong milliTime(void);

/*
 * Command handling helpers shared among multiple command sets
 */
int filterDebugThreads(jthread *threads, int count);


void sharedGetFieldValues(struct PacketInputStream *in,
                          struct PacketOutputStream *out,
                          jboolean isStatic);
jboolean sharedInvoke(struct PacketInputStream *in,
                      struct PacketOutputStream *out);

jvmtiError fieldSignature(jclass, jfieldID, char **, char **, char **);
jvmtiError fieldModifiers(jclass, jfieldID, jint *);
jvmtiError methodSignature(jmethodID, char **, char **, char **);
jvmtiError methodReturnType(jmethodID, char *);
jvmtiError methodModifiers(jmethodID, jint *);
jvmtiError methodClass(jmethodID, jclass *);
jvmtiError methodLocation(jmethodID, jlocation*, jlocation*);
jvmtiError classLoader(jclass, jobject *);

/*
 * Thin wrappers on top of JNI
 */
JNIEnv *getEnv(void);
jboolean isClass(jobject object);
jboolean isThread(jobject object);
jboolean isThreadGroup(jobject object);
jboolean isString(jobject object);
jboolean isClassLoader(jobject object);
jboolean isArray(jobject object);

/*
 * Thin wrappers on top of JVMTI
 */
jvmtiError jvmtiGetCapabilities(jvmtiCapabilities *caps);
jint jvmtiMajorVersion(void);
jint jvmtiMinorVersion(void);
jint jvmtiMicroVersion(void);
jvmtiError getSourceDebugExtension(jclass clazz, char **extensionPtr);
jboolean canSuspendResumeThreadLists(void);

jrawMonitorID debugMonitorCreate(char *name);
void debugMonitorEnter(jrawMonitorID theLock);
void debugMonitorExit(jrawMonitorID theLock);

/* ANDROID-CHANGED: extension functions that will enter and exit a mutex without allowing suspension
 * to occur. Caller must not use monitor-wait.
 */
void debugMonitorEnterNoSuspend(jrawMonitorID theLock);

void debugMonitorWait(jrawMonitorID theLock);
void debugMonitorTimedWait(jrawMonitorID theLock, jlong millis);
void debugMonitorNotify(jrawMonitorID theLock);
void debugMonitorNotifyAll(jrawMonitorID theLock);
void debugMonitorDestroy(jrawMonitorID theLock);

jthread *allThreads(jint *count);

void threadGroupInfo(jthreadGroup, jvmtiThreadGroupInfo *info);

/* ANDROID-CHANGED: Add isArrayClass */
jboolean isArrayClass(jclass);

char *getClassname(jclass);
jvmtiError classSignature(jclass, char**, char**);
jint classStatus(jclass);
void writeGenericSignature(struct PacketOutputStream *, char *);
jboolean isMethodNative(jmethodID);
jboolean isMethodObsolete(jmethodID);
jvmtiError isMethodSynthetic(jmethodID, jboolean*);
jvmtiError isFieldSynthetic(jclass, jfieldID, jboolean*);

jboolean isSameObject(JNIEnv *env, jobject o1, jobject o2);

jint objectHashCode(jobject);

jvmtiError allInterfaces(jclass clazz, jclass **ppinterfaces, jint *count);
jvmtiError allLoadedClasses(jclass **ppclasses, jint *count);
jvmtiError allClassLoaderClasses(jobject loader, jclass **ppclasses, jint *count);
jvmtiError allNestedClasses(jclass clazz, jclass **ppnested, jint *pcount);

void setAgentPropertyValue(JNIEnv *env, char *propertyName, char* propertyValue);

void *jvmtiAllocate(jint numBytes);
void jvmtiDeallocate(void *buffer);

void             eventIndexInit(void);
jdwpEvent        eventIndex2jdwp(EventIndex i);
jvmtiEvent       eventIndex2jvmti(EventIndex i);
EventIndex       jdwp2EventIndex(jdwpEvent eventType);
EventIndex       jvmti2EventIndex(jvmtiEvent kind);

jvmtiError       map2jvmtiError(jdwpError);
jdwpError        map2jdwpError(jvmtiError);
jdwpThreadStatus map2jdwpThreadStatus(jint state);
jint             map2jdwpSuspendStatus(jint state);
jint             map2jdwpClassStatus(jint);

void log_debugee_location(const char *func,
                jthread thread, jmethodID method, jlocation location);

/*
 * Local Reference management. The two macros below are used
 * throughout the back end whenever space for JNI local references
 * is needed in the current frame.
 */

void createLocalRefSpace(JNIEnv *env, jint capacity);

#define WITH_LOCAL_REFS(env, number) \
    createLocalRefSpace(env, number); \
    { /* BEGINNING OF WITH SCOPE */

#define END_WITH_LOCAL_REFS(env) \
        JNI_FUNC_PTR(env,PopLocalFrame)(env, NULL); \
    } /* END OF WITH SCOPE */

void saveGlobalRef(JNIEnv *env, jobject obj, jobject *pobj);
void tossGlobalRef(JNIEnv *env, jobject *pobj);

/* ANDROID_CHANGED: Expose this method publicly.
 * This returns a newly allocated jvmtiEnv* with the can_tag_objects capability.
 */
jvmtiEnv *getSpecialJvmti(void);

#endif
