alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (java_crw_demo.c)

This example Java source code file (java_crw_demo.c) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

bytecode, byteoffset, classopcode, crw_assert, crw_assert_ci, crw_assert_mi, crw_fatal, crwclassimage, crwcpoolindex, crwposition, jni_false, jni_true, next_4byte_boundary, null

The java_crw_demo.c Java example source code

/*
 * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *   - Neither the name of Oracle nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * This source code is provided to illustrate the usage of a given feature
 * or technique and has been deliberately simplified. Additional steps
 * required for a production-quality application, such as security checks,
 * input validation and proper error handling, might not be present in
 * this sample code.
 */


/* Class reader writer (java_crw_demo) for instrumenting bytecodes */

/*
 * As long as the callbacks allow for it and the class number is unique,
 *     this code is completely re-entrant and any number of classfile
 *     injections can happen at the same time.
 *
 *     The current logic requires a unique number for this class instance
 *     or (jclass,jobject loader) pair, this is done via the ClassIndex
 *     in hprof, which is passed in as the 'unsigned cnum' to java_crw_demo().
 *     It's up to the user of this interface if it wants to use this
 *     feature.
 *
 */

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

/* Get Java and class file and bytecode information. */

#include <jni.h>

#include "classfile_constants.h"


/* Include our own interface for cross check */

#include "java_crw_demo.h"

/* Macros over error functions to capture line numbers */

/* Fatal error used in all builds. */

/* Use THIS_FILE when it is available. */
#ifndef THIS_FILE
    #define THIS_FILE "java_crw.demo.c" /* Never use __FILE__ */
#endif

#define CRW_FATAL(ci, message) fatal_error(ci, message, THIS_FILE, __LINE__)

#if defined(DEBUG) || !defined(NDEBUG)

  /* This assert macro is only used in the debug builds. */
  #define CRW_ASSERT(ci, cond) \
        ((cond)?(void)0:assert_error(ci, #cond, THIS_FILE, __LINE__))

#else

  #define CRW_ASSERT(ci, cond)

#endif

#define CRW_ASSERT_MI(mi) CRW_ASSERT((mi)?(mi)->ci:NULL,(mi)!=NULL)

#define CRW_ASSERT_CI(ci) CRW_ASSERT(ci, ( (ci) != NULL && \
                         (ci)->input_position <= (ci)->input_len && \
                         (ci)->output_position <= (ci)->output_len) )

#define BUFSIZE 256

#ifdef _WIN32
#define snprintf(buffer, count, format, ...) _snprintf_s(buffer, count, _TRUNCATE, format, ##__VA_ARGS__)
#endif

/* Typedefs for various integral numbers, just for code clarity */

typedef unsigned       ClassOpcode;             /* One opcode */
typedef unsigned char  ByteCode;                /* One byte from bytecodes */
typedef int            ByteOffset;              /* Byte offset */
typedef int            ClassConstant;           /* Constant pool kind */
typedef long           CrwPosition;             /* Position in class image */
typedef unsigned short CrwCpoolIndex;           /* Index into constant pool */

/* Misc support macros */

/* Given the position of an opcode, find the next 4byte boundary position */
#define NEXT_4BYTE_BOUNDARY(opcode_pos) (((opcode_pos)+4) & (~3))

#define LARGEST_INJECTION               (12*3) /* 3 injections at same site */
#define MAXIMUM_NEW_CPOOL_ENTRIES       64 /* don't add more than 32 entries */

/* Constant Pool Entry (internal table that mirrors pool in file image) */

typedef struct {
    const char *        ptr;            /* Pointer to any string */
    unsigned short      len;            /* Length of string */
    unsigned int        index1;         /* 1st 16 bit index or 32bit value. */
    unsigned int        index2;         /* 2nd 16 bit index or 32bit value. */
    ClassConstant       tag;            /* Tag or kind of entry. */
} CrwConstantPoolEntry;

struct MethodImage;

/* Class file image storage structure */

typedef struct CrwClassImage {

    /* Unique class number for this class */
    unsigned                    number;

    /* Name of class, given or gotten out of class image */
    const char *                name;

    /* Input and Output class images tracking */
    const unsigned char *       input;
    unsigned char *             output;
    CrwPosition                 input_len;
    CrwPosition                 output_len;
    CrwPosition                 input_position;
    CrwPosition                 output_position;

    /* Mirrored constant pool */
    CrwConstantPoolEntry *      cpool;
    CrwCpoolIndex               cpool_max_elements;             /* Max count */
    CrwCpoolIndex               cpool_count_plus_one;

    /* Input flags about class (e.g. is it a system class) */
    int                         system_class;

    /* Class access flags gotten from file. */
    unsigned                    access_flags;

    /* Names of classes and methods. */
    char* tclass_name;          /* Name of class that has tracker methods. */
    char* tclass_sig;           /* Signature of class */
    char* call_name;            /* Method name to call at offset 0 */
    char* call_sig;             /* Signature of this method */
    char* return_name;          /* Method name to call before any return */
    char* return_sig;           /* Signature of this method */
    char* obj_init_name;        /* Method name to call in Object <init> */
    char* obj_init_sig;         /* Signature of this method */
    char* newarray_name;        /* Method name to call after newarray opcodes */
    char* newarray_sig;         /* Signature of this method */

    /* Constant pool index values for new entries */
    CrwCpoolIndex               tracker_class_index;
    CrwCpoolIndex               object_init_tracker_index;
    CrwCpoolIndex               newarray_tracker_index;
    CrwCpoolIndex               call_tracker_index;
    CrwCpoolIndex               return_tracker_index;
    CrwCpoolIndex               class_number_index; /* Class number in pool */

    /* Count of injections made into this class */
    int                         injection_count;

    /* This class must be the java.lang.Object class */
    jboolean                    is_object_class;

    /* This class must be the java.lang.Thread class */
    jboolean                    is_thread_class;

    /* Callback functions */
    FatalErrorHandler           fatal_error_handler;
    MethodNumberRegister        mnum_callback;

    /* Table of method names and descr's */
    int                         method_count;
    const char **               method_name;
    const char **               method_descr;
    struct MethodImage *        current_mi;

} CrwClassImage;

/* Injection bytecodes (holds injected bytecodes for each code position) */

typedef struct {
    ByteCode *  code;
    ByteOffset  len;
} Injection;

/* Method transformation data (allocated/freed as each method is processed) */

typedef struct MethodImage {

    /* Back reference to Class image data. */
    CrwClassImage *     ci;

    /* Unique method number for this class. */
    unsigned            number;

    /* Method name and descr */
    const char *        name;
    const char *        descr;

    /* Map of input bytecode offsets to output bytecode offsets */
    ByteOffset *        map;

    /* Bytecode injections for each input bytecode offset */
    Injection *         injections;

    /* Widening setting for each input bytecode offset */
    signed char *       widening;

    /* Length of original input bytecodes, and new bytecodes. */
    ByteOffset          code_len;
    ByteOffset          new_code_len;

    /* Location in input where bytecodes are located. */
    CrwPosition         start_of_input_bytecodes;

    /* Original max_stack and new max stack */
    unsigned            max_stack;
    unsigned            new_max_stack;

    jboolean            object_init_method;
    jboolean            skip_call_return_sites;

    /* Method access flags gotten from file. */
    unsigned            access_flags;

} MethodImage;

/* ----------------------------------------------------------------- */
/* General support functions (memory and error handling) */

static void
fatal_error(CrwClassImage *ci, const char *message, const char *file, int line)
{
    if ( ci != NULL && ci->fatal_error_handler != NULL ) {
        (*ci->fatal_error_handler)(message, file, line);
    } else {
        /* Normal operation should NEVER reach here */
        /* NO CRW FATAL ERROR HANDLER! */
        (void)fprintf(stderr, "CRW: %s [%s:%d]\n", message, file, line);
    }
    abort();
}

#if defined(DEBUG) || !defined(NDEBUG)
static void
assert_error(CrwClassImage *ci, const char *condition,
                 const char *file, int line)
{
    char buf[512];
    MethodImage *mi;
    ByteOffset byte_code_offset;

    mi = ci->current_mi;
    if ( mi != NULL ) {
        byte_code_offset = (ByteOffset)(mi->ci->input_position - mi->start_of_input_bytecodes);
    } else {
        byte_code_offset=-1;
    }

    (void)sprintf(buf,
                "CRW ASSERTION FAILURE: %s (%s:%s:%d)",
                condition,
                ci->name==NULL?"?":ci->name,
                (mi==NULL||mi->name==NULL)?"?":mi->name,
                byte_code_offset);
    fatal_error(ci, buf, file, line);
}
#endif

static void *
allocate(CrwClassImage *ci, int nbytes)
{
    void * ptr;

    if ( nbytes <= 0 ) {
        CRW_FATAL(ci, "Cannot allocate <= 0 bytes");
    }
    ptr = malloc(nbytes);
    if ( ptr == NULL ) {
        CRW_FATAL(ci, "Ran out of malloc memory");
    }
    return ptr;
}

static void *
reallocate(CrwClassImage *ci, void *optr, int nbytes)
{
    void * ptr;

    if ( optr == NULL ) {
        CRW_FATAL(ci, "Cannot deallocate NULL");
    }
    if ( nbytes <= 0 ) {
        CRW_FATAL(ci, "Cannot reallocate <= 0 bytes");
    }
    ptr = realloc(optr, nbytes);
    if ( ptr == NULL ) {
        CRW_FATAL(ci, "Ran out of malloc memory");
    }
    return ptr;
}

static void *
allocate_clean(CrwClassImage *ci, int nbytes)
{
    void * ptr;

    if ( nbytes <= 0 ) {
        CRW_FATAL(ci, "Cannot allocate <= 0 bytes");
    }
    ptr = calloc(nbytes, 1);
    if ( ptr == NULL ) {
        CRW_FATAL(ci, "Ran out of malloc memory");
    }
    return ptr;
}

static const char *
duplicate(CrwClassImage *ci, const char *str, int len)
{
    char *copy;

    copy = (char*)allocate(ci, len+1);
    (void)memcpy(copy, str, len);
    copy[len] = 0;
    return (const char *)copy;
}

static void
deallocate(CrwClassImage *ci, void *ptr)
{
    if ( ptr == NULL ) {
        CRW_FATAL(ci, "Cannot deallocate NULL");
    }
    (void)free(ptr);
}

/* ----------------------------------------------------------------- */
/* Functions for reading/writing bytes to/from the class images */

static unsigned
readU1(CrwClassImage *ci)
{
    CRW_ASSERT_CI(ci);
    return ((unsigned)(ci->input[ci->input_position++])) & 0xFF;
}

static unsigned
readU2(CrwClassImage *ci)
{
    unsigned res;

    res = readU1(ci);
    return (res << 8) + readU1(ci);
}

static signed short
readS2(CrwClassImage *ci)
{
    unsigned res;

    res = readU1(ci);
    return ((res << 8) + readU1(ci)) & 0xFFFF;
}

static unsigned
readU4(CrwClassImage *ci)
{
    unsigned res;

    res = readU2(ci);
    return (res << 16) + readU2(ci);
}

static void
writeU1(CrwClassImage *ci, unsigned val)  /* Only writes out lower 8 bits */
{
    CRW_ASSERT_CI(ci);
    if ( ci->output != NULL ) {
        ci->output[ci->output_position++] = val & 0xFF;
    }
}

static void
writeU2(CrwClassImage *ci, unsigned val)
{
    writeU1(ci, val >> 8);
    writeU1(ci, val);
}

static void
writeU4(CrwClassImage *ci, unsigned val)
{
    writeU2(ci, val >> 16);
    writeU2(ci, val);
}

static unsigned
copyU1(CrwClassImage *ci)
{
    unsigned value;

    value = readU1(ci);
    writeU1(ci, value);
    return value;
}

static unsigned
copyU2(CrwClassImage *ci)
{
    unsigned value;

    value = readU2(ci);
    writeU2(ci, value);
    return value;
}

static unsigned
copyU4(CrwClassImage *ci)
{
    unsigned value;

    value = readU4(ci);
    writeU4(ci, value);
    return value;
}

static void
copy(CrwClassImage *ci, unsigned count)
{
    CRW_ASSERT_CI(ci);
    if ( ci->output != NULL ) {
        (void)memcpy(ci->output+ci->output_position,
                     ci->input+ci->input_position, count);
        ci->output_position += count;
    }
    ci->input_position += count;
    CRW_ASSERT_CI(ci);
}

static void
skip(CrwClassImage *ci, unsigned count)
{
    CRW_ASSERT_CI(ci);
    ci->input_position += count;
}

static void
read_bytes(CrwClassImage *ci, void *bytes, unsigned count)
{
    CRW_ASSERT_CI(ci);
    CRW_ASSERT(ci, bytes!=NULL);
    (void)memcpy(bytes, ci->input+ci->input_position, count);
    ci->input_position += count;
}

static void
write_bytes(CrwClassImage *ci, void *bytes, unsigned count)
{
    CRW_ASSERT_CI(ci);
    CRW_ASSERT(ci, bytes!=NULL);
    if ( ci->output != NULL ) {
        (void)memcpy(ci->output+ci->output_position, bytes, count);
        ci->output_position += count;
    }
}

static void
random_writeU2(CrwClassImage *ci, CrwPosition pos, unsigned val)
{
    CrwPosition save_position;

    CRW_ASSERT_CI(ci);
    save_position = ci->output_position;
    ci->output_position = pos;
    writeU2(ci, val);
    ci->output_position = save_position;
}

static void
random_writeU4(CrwClassImage *ci, CrwPosition pos, unsigned val)
{
    CrwPosition save_position;

    CRW_ASSERT_CI(ci);
    save_position = ci->output_position;
    ci->output_position = pos;
    writeU4(ci, val);
    ci->output_position = save_position;
}

/* ----------------------------------------------------------------- */
/* Constant Pool handling functions. */

static void
fillin_cpool_entry(CrwClassImage *ci, CrwCpoolIndex i,
                   ClassConstant tag,
                   unsigned int index1, unsigned int index2,
                   const char *ptr, int len)
{
    CRW_ASSERT_CI(ci);
    CRW_ASSERT(ci, i > 0 && i < ci->cpool_count_plus_one);
    ci->cpool[i].tag    = tag;
    ci->cpool[i].index1 = index1;
    ci->cpool[i].index2 = index2;
    ci->cpool[i].ptr    = ptr;
    ci->cpool[i].len    = (unsigned short)len;
}

static CrwCpoolIndex
add_new_cpool_entry(CrwClassImage *ci, ClassConstant tag,
                    unsigned int index1, unsigned int index2,
                    const char *str, int len)
{
    CrwCpoolIndex i;
    char *utf8 = NULL;

    CRW_ASSERT_CI(ci);
    i = ci->cpool_count_plus_one++;

    /* NOTE: This implementation does not automatically expand the
     *       constant pool table beyond the expected number needed
     *       to handle this particular CrwTrackerInterface injections.
     *       See MAXIMUM_NEW_CPOOL_ENTRIES
     */
    CRW_ASSERT(ci,  ci->cpool_count_plus_one < ci->cpool_max_elements );

    writeU1(ci, tag);
    switch (tag) {
        case JVM_CONSTANT_Class:
            writeU2(ci, index1);
            break;
        case JVM_CONSTANT_String:
            writeU2(ci, index1);
            break;
        case JVM_CONSTANT_Fieldref:
        case JVM_CONSTANT_Methodref:
        case JVM_CONSTANT_InterfaceMethodref:
        case JVM_CONSTANT_Integer:
        case JVM_CONSTANT_Float:
        case JVM_CONSTANT_NameAndType:
            writeU2(ci, index1);
            writeU2(ci, index2);
            break;
        case JVM_CONSTANT_Long:
        case JVM_CONSTANT_Double:
            writeU4(ci, index1);
            writeU4(ci, index2);
            ci->cpool_count_plus_one++;
            CRW_ASSERT(ci,  ci->cpool_count_plus_one < ci->cpool_max_elements );
            break;
        case JVM_CONSTANT_Utf8:
            CRW_ASSERT(ci, len==(len & 0xFFFF));
            writeU2(ci, len);
            write_bytes(ci, (void*)str, len);
            utf8 = (char*)duplicate(ci, str, len);
            break;
        default:
            CRW_FATAL(ci, "Unknown constant");
            break;
    }
    fillin_cpool_entry(ci, i, tag, index1, index2, (const char *)utf8, len);
    CRW_ASSERT(ci, i > 0 && i < ci->cpool_count_plus_one);
    return i;
}

static CrwCpoolIndex
add_new_class_cpool_entry(CrwClassImage *ci, const char *class_name)
{
    CrwCpoolIndex name_index;
    CrwCpoolIndex class_index;
    int           len;

    CRW_ASSERT_CI(ci);
    CRW_ASSERT(ci, class_name!=NULL);

    len = (int)strlen(class_name);
    name_index = add_new_cpool_entry(ci, JVM_CONSTANT_Utf8, len, 0,
                        class_name, len);
    class_index = add_new_cpool_entry(ci, JVM_CONSTANT_Class, name_index, 0,
                        NULL, 0);
    return class_index;
}

static CrwCpoolIndex
add_new_method_cpool_entry(CrwClassImage *ci, CrwCpoolIndex class_index,
                     const char *name, const char *descr)
{
    CrwCpoolIndex name_index;
    CrwCpoolIndex descr_index;
    CrwCpoolIndex name_type_index;
    int len;

    CRW_ASSERT_CI(ci);
    CRW_ASSERT(ci, name!=NULL);
    CRW_ASSERT(ci, descr!=NULL);
    len = (int)strlen(name);
    name_index =
        add_new_cpool_entry(ci, JVM_CONSTANT_Utf8, len, 0, name, len);
    len = (int)strlen(descr);
    descr_index =
        add_new_cpool_entry(ci, JVM_CONSTANT_Utf8, len, 0, descr, len);
    name_type_index =
        add_new_cpool_entry(ci, JVM_CONSTANT_NameAndType,
                                name_index, descr_index, NULL, 0);
    return add_new_cpool_entry(ci, JVM_CONSTANT_Methodref,
                                class_index, name_type_index, NULL, 0);
}

static CrwConstantPoolEntry
cpool_entry(CrwClassImage *ci, CrwCpoolIndex c_index)
{
    CRW_ASSERT_CI(ci);
    CRW_ASSERT(ci, c_index > 0 && c_index < ci->cpool_count_plus_one);
    return ci->cpool[c_index];
}

static void
cpool_setup(CrwClassImage *ci)
{
    CrwCpoolIndex i;
    CrwPosition cpool_output_position;
    int count_plus_one;

    CRW_ASSERT_CI(ci);
    cpool_output_position = ci->output_position;
    count_plus_one = copyU2(ci);
    CRW_ASSERT(ci, count_plus_one>1);
    ci->cpool_max_elements = count_plus_one+MAXIMUM_NEW_CPOOL_ENTRIES;
    ci->cpool = (CrwConstantPoolEntry*)allocate_clean(ci,
                (int)((ci->cpool_max_elements)*sizeof(CrwConstantPoolEntry)));
    ci->cpool_count_plus_one = (CrwCpoolIndex)count_plus_one;

    /* Index zero not in class file */
    for (i = 1; i < count_plus_one; ++i) {
        CrwCpoolIndex   ipos;
        ClassConstant   tag;
        unsigned int    index1;
        unsigned int    index2;
        unsigned        len;
        char *          utf8;
        char message[BUFSIZE];

        ipos    = i;
        index1  = 0;
        index2  = 0;
        len     = 0;
        utf8    = NULL;

        tag = copyU1(ci);
        switch (tag) {
            case JVM_CONSTANT_Class:
                index1 = copyU2(ci);
                break;
            case JVM_CONSTANT_String:
                index1 = copyU2(ci);
                break;
            case JVM_CONSTANT_Fieldref:
            case JVM_CONSTANT_Methodref:
            case JVM_CONSTANT_InterfaceMethodref:
            case JVM_CONSTANT_Integer:
            case JVM_CONSTANT_Float:
            case JVM_CONSTANT_NameAndType:
                index1 = copyU2(ci);
                index2 = copyU2(ci);
                break;
            case JVM_CONSTANT_Long:
            case JVM_CONSTANT_Double:
                index1 = copyU4(ci);
                index2 = copyU4(ci);
                ++i;  /* // these take two CP entries - duh! */
                break;
            case JVM_CONSTANT_Utf8:
                len     = copyU2(ci);
                index1  = (unsigned short)len;
                utf8    = (char*)allocate(ci, len+1);
                read_bytes(ci, (void*)utf8, len);
                utf8[len] = 0;
                write_bytes(ci, (void*)utf8, len);
                break;
            case JVM_CONSTANT_MethodType:
                index1 = copyU2(ci);
                break;
            case JVM_CONSTANT_MethodHandle:
                index1 = copyU1(ci);
                index2 = copyU2(ci);
                break;
            case JVM_CONSTANT_InvokeDynamic:
                index1 = copyU2(ci);
                index2 = copyU2(ci);
                break;
            default:
                snprintf(message, BUFSIZE, "Unknown tag: %d, at ipos %hu", tag, ipos);
                CRW_FATAL(ci, message);
                break;
        }
        fillin_cpool_entry(ci, ipos, tag, index1, index2, (const char *)utf8, len);
    }

    if (ci->call_name != NULL || ci->return_name != NULL) {
        if ( ci->number != (ci->number & 0x7FFF) ) {
            ci->class_number_index =
                add_new_cpool_entry(ci, JVM_CONSTANT_Integer,
                    (ci->number>>16) & 0xFFFF, ci->number & 0xFFFF, NULL, 0);
        }
    }

    if (  ci->tclass_name != NULL ) {
        ci->tracker_class_index =
                add_new_class_cpool_entry(ci, ci->tclass_name);
    }
    if (ci->obj_init_name != NULL) {
        ci->object_init_tracker_index = add_new_method_cpool_entry(ci,
                    ci->tracker_class_index,
                    ci->obj_init_name,
                    ci->obj_init_sig);
    }
    if (ci->newarray_name != NULL) {
        ci->newarray_tracker_index = add_new_method_cpool_entry(ci,
                    ci->tracker_class_index,
                    ci->newarray_name,
                    ci->newarray_sig);
    }
    if (ci->call_name != NULL) {
        ci->call_tracker_index = add_new_method_cpool_entry(ci,
                    ci->tracker_class_index,
                    ci->call_name,
                    ci->call_sig);
    }
    if (ci->return_name != NULL) {
        ci->return_tracker_index = add_new_method_cpool_entry(ci,
                    ci->tracker_class_index,
                    ci->return_name,
                    ci->return_sig);
    }

    random_writeU2(ci, cpool_output_position, ci->cpool_count_plus_one);
}

/* ----------------------------------------------------------------- */
/* Functions that create the bytecodes to inject */

static ByteOffset
push_pool_constant_bytecodes(ByteCode *bytecodes, CrwCpoolIndex index)
{
    ByteOffset nbytes = 0;

    if ( index == (index&0x7F) ) {
        bytecodes[nbytes++] = (ByteCode)JVM_OPC_ldc;
    } else {
        bytecodes[nbytes++] = (ByteCode)JVM_OPC_ldc_w;
        bytecodes[nbytes++] = (ByteCode)((index >> 8) & 0xFF);
    }
    bytecodes[nbytes++] = (ByteCode)(index & 0xFF);
    return nbytes;
}

static ByteOffset
push_short_constant_bytecodes(ByteCode *bytecodes, unsigned number)
{
    ByteOffset nbytes = 0;

    if ( number <= 5 ) {
        bytecodes[nbytes++] = (ByteCode)(JVM_OPC_iconst_0+number);
    } else if ( number == (number&0x7F) ) {
        bytecodes[nbytes++] = (ByteCode)JVM_OPC_bipush;
        bytecodes[nbytes++] = (ByteCode)(number & 0xFF);
    } else {
        bytecodes[nbytes++] = (ByteCode)JVM_OPC_sipush;
        bytecodes[nbytes++] = (ByteCode)((number >> 8) & 0xFF);
        bytecodes[nbytes++] = (ByteCode)(number & 0xFF);
    }
    return nbytes;
}

static ByteOffset
injection_template(MethodImage *mi, ByteCode *bytecodes, ByteOffset max_nbytes,
                        CrwCpoolIndex method_index)
{
    CrwClassImage *     ci;
    ByteOffset nbytes = 0;
    unsigned max_stack;
    int add_dup;
    int add_aload;
    int push_cnum;
    int push_mnum;

    ci = mi->ci;

    CRW_ASSERT(ci, bytecodes!=NULL);

    if ( method_index == 0 )  {
        return 0;
    }

    if ( method_index == ci->newarray_tracker_index) {
        max_stack       = mi->max_stack + 1;
        add_dup         = JNI_TRUE;
        add_aload       = JNI_FALSE;
        push_cnum       = JNI_FALSE;
        push_mnum       = JNI_FALSE;
    } else if ( method_index == ci->object_init_tracker_index) {
        max_stack       = mi->max_stack + 1;
        add_dup         = JNI_FALSE;
        add_aload       = JNI_TRUE;
        push_cnum       = JNI_FALSE;
        push_mnum       = JNI_FALSE;
    } else {
        max_stack       = mi->max_stack + 2;
        add_dup         = JNI_FALSE;
        add_aload       = JNI_FALSE;
        push_cnum       = JNI_TRUE;
        push_mnum       = JNI_TRUE;
    }

    if ( add_dup ) {
        bytecodes[nbytes++] = (ByteCode)JVM_OPC_dup;
    }
    if ( add_aload ) {
        bytecodes[nbytes++] = (ByteCode)JVM_OPC_aload_0;
    }
    if ( push_cnum ) {
        if ( ci->number == (ci->number & 0x7FFF) ) {
            nbytes += push_short_constant_bytecodes(bytecodes+nbytes,
                                                ci->number);
        } else {
            CRW_ASSERT(ci, ci->class_number_index!=0);
            nbytes += push_pool_constant_bytecodes(bytecodes+nbytes,
                                                ci->class_number_index);
        }
    }
    if ( push_mnum ) {
        nbytes += push_short_constant_bytecodes(bytecodes+nbytes,
                                            mi->number);
    }
    bytecodes[nbytes++] = (ByteCode)JVM_OPC_invokestatic;
    bytecodes[nbytes++] = (ByteCode)(method_index >> 8);
    bytecodes[nbytes++] = (ByteCode)method_index;
    bytecodes[nbytes]   = 0;
    CRW_ASSERT(ci, nbytes<max_nbytes);

    /* Make sure the new max_stack is appropriate */
    if ( max_stack > mi->new_max_stack ) {
        mi->new_max_stack = max_stack;
    }
    return nbytes;
}

/* Called to create injection code at entry to a method */
static ByteOffset
entry_injection_code(MethodImage *mi, ByteCode *bytecodes, ByteOffset len)
{
    CrwClassImage *     ci;
    ByteOffset nbytes = 0;

    CRW_ASSERT_MI(mi);

    ci = mi->ci;

    if ( mi->object_init_method ) {
        nbytes = injection_template(mi,
                            bytecodes, len, ci->object_init_tracker_index);
    }
    if ( !mi->skip_call_return_sites ) {
        nbytes += injection_template(mi,
                    bytecodes+nbytes, len-nbytes, ci->call_tracker_index);
    }
    return nbytes;
}

/* Called to create injection code before an opcode */
static ByteOffset
before_injection_code(MethodImage *mi, ClassOpcode opcode,
                      ByteCode *bytecodes, ByteOffset len)
{
    ByteOffset nbytes = 0;


    CRW_ASSERT_MI(mi);
    switch ( opcode ) {
        case JVM_OPC_return:
        case JVM_OPC_ireturn:
        case JVM_OPC_lreturn:
        case JVM_OPC_freturn:
        case JVM_OPC_dreturn:
        case JVM_OPC_areturn:
            if ( !mi->skip_call_return_sites ) {
                nbytes = injection_template(mi,
                            bytecodes, len, mi->ci->return_tracker_index);
            }
            break;
        default:
            break;
    }
    return nbytes;
}

/* Called to create injection code after an opcode */
static ByteOffset
after_injection_code(MethodImage *mi, ClassOpcode opcode,
                     ByteCode *bytecodes, ByteOffset len)
{
    CrwClassImage* ci;
    ByteOffset nbytes;

    ci = mi->ci;
    nbytes = 0;

    CRW_ASSERT_MI(mi);
    switch ( opcode ) {
        case JVM_OPC_new:
            /* Can't inject here cannot pass around uninitialized object */
            break;
        case JVM_OPC_newarray:
        case JVM_OPC_anewarray:
        case JVM_OPC_multianewarray:
            nbytes = injection_template(mi,
                                bytecodes, len, ci->newarray_tracker_index);
            break;
        default:
            break;
    }
    return nbytes;
}

/* Actually inject the bytecodes */
static void
inject_bytecodes(MethodImage *mi, ByteOffset at,
                 ByteCode *bytecodes, ByteOffset len)
{
    Injection injection;
    CrwClassImage *ci;

    ci = mi->ci;
    CRW_ASSERT_MI(mi);
    CRW_ASSERT(ci, at <= mi->code_len);

    injection = mi->injections[at];

    CRW_ASSERT(ci, len <= LARGEST_INJECTION/2);
    CRW_ASSERT(ci, injection.len+len <= LARGEST_INJECTION);

    /* Either start an injection area or concatenate to what is there */
    if ( injection.code == NULL ) {
        CRW_ASSERT(ci, injection.len==0);
        injection.code = (ByteCode *)allocate_clean(ci, LARGEST_INJECTION+1);
    }

    (void)memcpy(injection.code+injection.len, bytecodes, len);
    injection.len += len;
    injection.code[injection.len] = 0;
    mi->injections[at] = injection;
    ci->injection_count++;
}

/* ----------------------------------------------------------------- */
/* Method handling functions */

static MethodImage *
method_init(CrwClassImage *ci, unsigned mnum, ByteOffset code_len)
{
    MethodImage *       mi;
    ByteOffset          i;

    mi                  = (MethodImage*)allocate_clean(ci, (int)sizeof(MethodImage));
    mi->ci              = ci;
    mi->name            = ci->method_name[mnum];
    mi->descr           = ci->method_descr[mnum];
    mi->code_len        = code_len;
    mi->map             = (ByteOffset*)allocate_clean(ci,
                                (int)((code_len+1)*sizeof(ByteOffset)));
    for(i=0; i<=code_len; i++) {
        mi->map[i] = i;
    }
    mi->widening        = (signed char*)allocate_clean(ci, code_len+1);
    mi->injections      = (Injection *)allocate_clean(ci,
                                (int)((code_len+1)*sizeof(Injection)));
    mi->number          = mnum;
    ci->current_mi      = mi;
    return mi;
}

static void
method_term(MethodImage *mi)
{
    CrwClassImage *ci;

    ci = mi->ci;
    CRW_ASSERT_MI(mi);
    if ( mi->map != NULL ) {
        deallocate(ci, (void*)mi->map);
        mi->map = NULL;
    }
    if ( mi->widening != NULL ) {
        deallocate(ci, (void*)mi->widening);
        mi->widening = NULL;
    }
    if ( mi->injections != NULL ) {
        ByteOffset i;
        for(i=0; i<= mi->code_len; i++) {
            if ( mi->injections[i].code != NULL ) {
                deallocate(ci, (void*)mi->injections[i].code);
                mi->injections[i].code = NULL;
            }
        }
        deallocate(ci, (void*)mi->injections);
        mi->injections = NULL;
    }
    ci->current_mi = NULL;
    deallocate(ci, (void*)mi);
}

static ByteOffset
input_code_offset(MethodImage *mi)
{
    CRW_ASSERT_MI(mi);
    return (ByteOffset)(mi->ci->input_position - mi->start_of_input_bytecodes);
}

static void
rewind_to_beginning_of_input_bytecodes(MethodImage *mi)
{
    CRW_ASSERT_MI(mi);
    mi->ci->input_position = mi->start_of_input_bytecodes;
}

/* Starting at original byte position 'at', add 'offset' to it's new
 *   location. This may be a negative value.
 *   NOTE: That this map is not the new bytecode location of the opcode
 *         but the new bytecode location that should be used when
 *         a goto or jump instruction was targeting the old bytecode
 *         location.
 */
static void
adjust_map(MethodImage *mi, ByteOffset at, ByteOffset offset)
{
    ByteOffset i;

    CRW_ASSERT_MI(mi);
    for (i = at; i <= mi->code_len; ++i) {
        mi->map[i] += offset;
    }
}

static void
widen(MethodImage *mi, ByteOffset at, ByteOffset len)
{
    int delta;

    CRW_ASSERT(mi->ci, at <= mi->code_len);
    delta = len - mi->widening[at];
    /* Adjust everything from the current input location by delta */
    adjust_map(mi, input_code_offset(mi), delta);
    /* Mark at beginning of instruction */
    mi->widening[at] = (signed char)len;
}

static void
verify_opc_wide(CrwClassImage *ci, ClassOpcode wopcode)
{
    switch (wopcode) {
        case JVM_OPC_aload: case JVM_OPC_astore:
        case JVM_OPC_fload: case JVM_OPC_fstore:
        case JVM_OPC_iload: case JVM_OPC_istore:
        case JVM_OPC_lload: case JVM_OPC_lstore:
        case JVM_OPC_dload: case JVM_OPC_dstore:
        case JVM_OPC_ret:   case JVM_OPC_iinc:
            break;
        default:
            CRW_FATAL(ci, "Invalid opcode supplied to wide opcode");
            break;
    }
}

static unsigned
opcode_length(CrwClassImage *ci, ClassOpcode opcode)
{
    /* Define array that holds length of an opcode */
    static unsigned char _opcode_length[JVM_OPC_MAX+1] =
                          JVM_OPCODE_LENGTH_INITIALIZER;

    if ( opcode > JVM_OPC_MAX ) {
        CRW_FATAL(ci, "Invalid opcode supplied to opcode_length()");
    }
    return _opcode_length[opcode];
}

/* Walk one instruction and inject instrumentation */
static void
inject_for_opcode(MethodImage *mi)
{
    CrwClassImage *  ci;
    ClassOpcode      opcode;
    int              pos;

    CRW_ASSERT_MI(mi);
    ci = mi->ci;
    pos = input_code_offset(mi);
    opcode = readU1(ci);

    if (opcode == JVM_OPC_wide) {
        ClassOpcode     wopcode;

        wopcode = readU1(ci);
        /* lvIndex not used */
        (void)readU2(ci);
        verify_opc_wide(ci, wopcode);
        if ( wopcode==JVM_OPC_iinc ) {
            (void)readU1(ci);
            (void)readU1(ci);
        }
    } else {

        ByteCode        bytecodes[LARGEST_INJECTION+1];
        int             header;
        int             instr_len;
        int             low;
        int             high;
        int             npairs;
        ByteOffset      len;

        /* Get bytecodes to inject before this opcode */
        len = before_injection_code(mi, opcode, bytecodes, (int)sizeof(bytecodes));
        if ( len > 0 ) {
            inject_bytecodes(mi, pos, bytecodes, len);
            /* Adjust map after processing this opcode */
        }

        /* Process this opcode */
        switch (opcode) {
            case JVM_OPC_tableswitch:
                header = NEXT_4BYTE_BOUNDARY(pos);
                skip(ci, header - (pos+1));
                (void)readU4(ci);
                low = readU4(ci);
                high = readU4(ci);
                skip(ci, (high+1-low) * 4);
                break;
            case JVM_OPC_lookupswitch:
                header = NEXT_4BYTE_BOUNDARY(pos);
                skip(ci, header - (pos+1));
                (void)readU4(ci);
                npairs = readU4(ci);
                skip(ci, npairs * 8);
                break;
            default:
                instr_len = opcode_length(ci, opcode);
                skip(ci, instr_len-1);
                break;
        }

        /* Get position after this opcode is processed */
        pos = input_code_offset(mi);

        /* Adjust for any before_injection_code() */
        if ( len > 0 ) {
            /* Adjust everything past this opcode.
             *   Why past it? Because we want any jumps to this bytecode loc
             *   to go to the injected code, not where the opcode
             *   was moved too.
             *   Consider a 'return' opcode that is jumped too.
             *   NOTE: This may not be correct in all cases, but will
             *         when we are only dealing with non-variable opcodes
             *         like the return opcodes. Be careful if the
             *         before_injection_code() changes to include other
             *         opcodes that have variable length.
             */
            adjust_map(mi, pos, len);
        }

        /* Get bytecodes to inject after this opcode */
        len = after_injection_code(mi, opcode, bytecodes, (int)sizeof(bytecodes));
        if ( len > 0 ) {
            inject_bytecodes(mi, pos, bytecodes, len);

            /* Adjust for any after_injection_code() */
            adjust_map(mi, pos, len);
        }

    }
}

/* Map original bytecode location to it's new location. (See adjust_map()). */
static ByteOffset
method_code_map(MethodImage *mi, ByteOffset pos)
{
    CRW_ASSERT_MI(mi);
    CRW_ASSERT(mi->ci, pos <= mi->code_len);
    return mi->map[pos];
}

static int
adjust_instruction(MethodImage *mi)
{
    CrwClassImage *     ci;
    ClassOpcode         opcode;
    int                 pos;
    int                 new_pos;

    CRW_ASSERT_MI(mi);
    ci = mi->ci;
    pos = input_code_offset(mi);
    new_pos = method_code_map(mi,pos);

    opcode = readU1(ci);

    if (opcode == JVM_OPC_wide) {
        ClassOpcode wopcode;

        wopcode = readU1(ci);
        /* lvIndex not used */
        (void)readU2(ci);
        verify_opc_wide(ci, wopcode);
        if ( wopcode==JVM_OPC_iinc ) {
            (void)readU1(ci);
            (void)readU1(ci);
        }
    } else {

        int widened;
        int header;
        int newHeader;
        int low;
        int high;
        int new_pad;
        int old_pad;
        int delta;
        int new_delta;
        int delta_pad;
        int npairs;
        int instr_len;

        switch (opcode) {

        case JVM_OPC_tableswitch:
            widened     = mi->widening[pos];
            header      = NEXT_4BYTE_BOUNDARY(pos);
            newHeader   = NEXT_4BYTE_BOUNDARY(new_pos);

            skip(ci, header - (pos+1));

            delta       = readU4(ci);
            low         = readU4(ci);
            high        = readU4(ci);
            skip(ci, (high+1-low) * 4);
            new_pad     = newHeader - new_pos;
            old_pad     = header - pos;
            delta_pad   = new_pad - old_pad;
            if (widened != delta_pad) {
                widen(mi, pos, delta_pad);
                return 0;
            }
            break;

        case JVM_OPC_lookupswitch:
            widened     = mi->widening[pos];
            header      = NEXT_4BYTE_BOUNDARY(pos);
            newHeader   = NEXT_4BYTE_BOUNDARY(new_pos);

            skip(ci, header - (pos+1));

            delta       = readU4(ci);
            npairs      = readU4(ci);
            skip(ci, npairs * 8);
            new_pad     = newHeader - new_pos;
            old_pad     = header - pos;
            delta_pad   = new_pad - old_pad;
            if (widened != delta_pad) {
                widen(mi, pos, delta_pad);
                return 0;
            }
            break;

        case JVM_OPC_jsr: case JVM_OPC_goto:
        case JVM_OPC_ifeq: case JVM_OPC_ifge: case JVM_OPC_ifgt:
        case JVM_OPC_ifle: case JVM_OPC_iflt: case JVM_OPC_ifne:
        case JVM_OPC_if_icmpeq: case JVM_OPC_if_icmpne: case JVM_OPC_if_icmpge:
        case JVM_OPC_if_icmpgt: case JVM_OPC_if_icmple: case JVM_OPC_if_icmplt:
        case JVM_OPC_if_acmpeq: case JVM_OPC_if_acmpne:
        case JVM_OPC_ifnull: case JVM_OPC_ifnonnull:
            widened     = mi->widening[pos];
            delta       = readS2(ci);
            if (widened == 0) {
                new_delta = method_code_map(mi,pos+delta) - new_pos;
                if ((new_delta < -32768) || (new_delta > 32767)) {
                    switch (opcode) {
                        case JVM_OPC_jsr: case JVM_OPC_goto:
                            widen(mi, pos, 2);
                            break;
                        default:
                            widen(mi, pos, 5);
                            break;
                    }
                    return 0;
                }
            }
            break;

        case JVM_OPC_jsr_w:
        case JVM_OPC_goto_w:
            (void)readU4(ci);
            break;

        default:
            instr_len = opcode_length(ci, opcode);
            skip(ci, instr_len-1);
            break;
        }
    }
    return 1;
}

static void
write_instruction(MethodImage *mi)
{
    CrwClassImage *     ci;
    ClassOpcode         opcode;
    ByteOffset          new_code_len;
    int                 pos;
    int                 new_pos;

    CRW_ASSERT_MI(mi);
    ci = mi->ci;
    pos = input_code_offset(mi);
    new_pos = method_code_map(mi,pos);
    new_code_len = mi->injections[pos].len;
    if (new_code_len > 0) {
        write_bytes(ci, (void*)mi->injections[pos].code, new_code_len);
    }

    opcode = readU1(ci);
    if (opcode == JVM_OPC_wide) {
        ClassOpcode     wopcode;

        writeU1(ci, opcode);

        wopcode = copyU1(ci);
        /* lvIndex not used */
        (void)copyU2(ci);
        verify_opc_wide(ci, wopcode);
        if ( wopcode==JVM_OPC_iinc ) {
            (void)copyU1(ci);
            (void)copyU1(ci);
        }
    } else {

        ClassOpcode new_opcode;
        int             header;
        int             newHeader;
        int             low;
        int             high;
        int             i;
        int             npairs;
        int             widened;
        int             instr_len;
        int             delta;
        int             new_delta;

        switch (opcode) {

            case JVM_OPC_tableswitch:
                header = NEXT_4BYTE_BOUNDARY(pos);
                newHeader = NEXT_4BYTE_BOUNDARY(new_pos);

                skip(ci, header - (pos+1));

                delta = readU4(ci);
                new_delta = method_code_map(mi,pos+delta) - new_pos;
                low = readU4(ci);
                high = readU4(ci);

                writeU1(ci, opcode);
                for (i = new_pos+1; i < newHeader; ++i) {
                    writeU1(ci, 0);
                }
                writeU4(ci, new_delta);
                writeU4(ci, low);
                writeU4(ci, high);

                for (i = low; i <= high; ++i) {
                    delta = readU4(ci);
                    new_delta = method_code_map(mi,pos+delta) - new_pos;
                    writeU4(ci, new_delta);
                }
                break;

            case JVM_OPC_lookupswitch:
                header = NEXT_4BYTE_BOUNDARY(pos);
                newHeader = NEXT_4BYTE_BOUNDARY(new_pos);

                skip(ci, header - (pos+1));

                delta = readU4(ci);
                new_delta = method_code_map(mi,pos+delta) - new_pos;
                npairs = readU4(ci);
                writeU1(ci, opcode);
                for (i = new_pos+1; i < newHeader; ++i) {
                    writeU1(ci, 0);
                }
                writeU4(ci, new_delta);
                writeU4(ci, npairs);
                for (i = 0; i< npairs; ++i) {
                    unsigned match = readU4(ci);
                    delta = readU4(ci);
                    new_delta = method_code_map(mi,pos+delta) - new_pos;
                    writeU4(ci, match);
                    writeU4(ci, new_delta);
                }
                break;

            case JVM_OPC_jsr: case JVM_OPC_goto:
            case JVM_OPC_ifeq: case JVM_OPC_ifge: case JVM_OPC_ifgt:
            case JVM_OPC_ifle: case JVM_OPC_iflt: case JVM_OPC_ifne:
            case JVM_OPC_if_icmpeq: case JVM_OPC_if_icmpne: case JVM_OPC_if_icmpge:
            case JVM_OPC_if_icmpgt: case JVM_OPC_if_icmple: case JVM_OPC_if_icmplt:
            case JVM_OPC_if_acmpeq: case JVM_OPC_if_acmpne:
            case JVM_OPC_ifnull: case JVM_OPC_ifnonnull:
                widened = mi->widening[pos];
                delta = readS2(ci);
                new_delta = method_code_map(mi,pos+delta) - new_pos;
                new_opcode = opcode;
                if (widened == 0) {
                    writeU1(ci, opcode);
                    writeU2(ci, new_delta);
                } else if (widened == 2) {
                    switch (opcode) {
                        case JVM_OPC_jsr:
                            new_opcode = JVM_OPC_jsr_w;
                            break;
                        case JVM_OPC_goto:
                            new_opcode = JVM_OPC_goto_w;
                            break;
                        default:
                            CRW_FATAL(ci, "unexpected opcode");
                            break;
                    }
                    writeU1(ci, new_opcode);
                    writeU4(ci, new_delta);
                } else if (widened == 5) {
                    switch (opcode) {
                        case JVM_OPC_ifeq:
                            new_opcode = JVM_OPC_ifne;
                            break;
                        case JVM_OPC_ifge:
                            new_opcode = JVM_OPC_iflt;
                            break;
                        case JVM_OPC_ifgt:
                            new_opcode = JVM_OPC_ifle;
                            break;
                        case JVM_OPC_ifle:
                            new_opcode = JVM_OPC_ifgt;
                            break;
                        case JVM_OPC_iflt:
                            new_opcode = JVM_OPC_ifge;
                            break;
                        case JVM_OPC_ifne:
                            new_opcode = JVM_OPC_ifeq;
                            break;
                        case JVM_OPC_if_icmpeq:
                            new_opcode = JVM_OPC_if_icmpne;
                            break;
                        case JVM_OPC_if_icmpne:
                            new_opcode = JVM_OPC_if_icmpeq;
                            break;
                        case JVM_OPC_if_icmpge:
                            new_opcode = JVM_OPC_if_icmplt;
                            break;
                        case JVM_OPC_if_icmpgt:
                            new_opcode = JVM_OPC_if_icmple;
                            break;
                        case JVM_OPC_if_icmple:
                            new_opcode = JVM_OPC_if_icmpgt;
                            break;
                        case JVM_OPC_if_icmplt:
                            new_opcode = JVM_OPC_if_icmpge;
                            break;
                        case JVM_OPC_if_acmpeq:
                            new_opcode = JVM_OPC_if_acmpne;
                            break;
                        case JVM_OPC_if_acmpne:
                            new_opcode = JVM_OPC_if_acmpeq;
                            break;
                        case JVM_OPC_ifnull:
                            new_opcode = JVM_OPC_ifnonnull;
                            break;
                        case JVM_OPC_ifnonnull:
                            new_opcode = JVM_OPC_ifnull;
                            break;
                        default:
                            CRW_FATAL(ci, "Unexpected opcode");
                        break;
                    }
                    writeU1(ci, new_opcode);    /* write inverse branch */
                    writeU2(ci, 3 + 5);         /* beyond if and goto_w */
                    writeU1(ci, JVM_OPC_goto_w);    /* add a goto_w */
                    writeU4(ci, new_delta-3); /* write new and wide delta */
                } else {
                    CRW_FATAL(ci, "Unexpected widening");
                }
                break;

            case JVM_OPC_jsr_w:
            case JVM_OPC_goto_w:
                delta = readU4(ci);
                new_delta = method_code_map(mi,pos+delta) - new_pos;
                writeU1(ci, opcode);
                writeU4(ci, new_delta);
                break;

            default:
                instr_len = opcode_length(ci, opcode);
                writeU1(ci, opcode);
                copy(ci, instr_len-1);
                break;
        }
    }
}

static void
method_inject_and_write_code(MethodImage *mi)
{
    ByteCode bytecodes[LARGEST_INJECTION+1];
    ByteOffset   len;

    CRW_ASSERT_MI(mi);

    /* Do injections */
    rewind_to_beginning_of_input_bytecodes(mi);
    len = entry_injection_code(mi, bytecodes, (int)sizeof(bytecodes));
    if ( len > 0 ) {
        int pos;

        pos = 0;
        inject_bytecodes(mi, pos, bytecodes, len);
        /* Adjust pos 0 to map to new pos 0, you never want to
         *  jump into this entry code injection. So the new pos 0
         *  will be past this entry_injection_code().
         */
        adjust_map(mi, pos, len); /* Inject before behavior */
    }
    while (input_code_offset(mi) < mi->code_len) {
        inject_for_opcode(mi);
    }

    /* Adjust instructions */
    rewind_to_beginning_of_input_bytecodes(mi);
    while (input_code_offset(mi) < mi->code_len) {
        if (!adjust_instruction(mi)) {
            rewind_to_beginning_of_input_bytecodes(mi);
        }
    }

    /* Write new instructions */
    rewind_to_beginning_of_input_bytecodes(mi);
    while (input_code_offset(mi) < mi->code_len) {
        write_instruction(mi);
    }
}

static void
copy_attribute(CrwClassImage *ci)
{
    int len;

    (void)copyU2(ci);
    len = copyU4(ci);
    copy(ci, len);
}

static void
copy_attributes(CrwClassImage *ci)
{
    unsigned i;
    unsigned count;

    count = copyU2(ci);
    for (i = 0; i < count; ++i) {
        copy_attribute(ci);
    }
}

static void
copy_all_fields(CrwClassImage *ci)
{
    unsigned i;
    unsigned count;

    count = copyU2(ci);
    for (i = 0; i < count; ++i) {
        /* access, name, descriptor */
        copy(ci, 6);
        copy_attributes(ci);
    }
}

static void
write_line_table(MethodImage *mi)
{
    unsigned             i;
    unsigned             count;
    CrwClassImage *      ci;

    CRW_ASSERT_MI(mi);
    ci = mi->ci;
    (void)copyU4(ci);
    count = copyU2(ci);
    for(i=0; i<count; i++) {
        ByteOffset start_pc;
        ByteOffset new_start_pc;

        start_pc = readU2(ci);

        if ( start_pc == 0 ) {
            new_start_pc = 0; /* Don't skip entry injection code. */
        } else {
            new_start_pc = method_code_map(mi, start_pc);
        }

        writeU2(ci, new_start_pc);
        (void)copyU2(ci);
    }
}

/* Used for LocalVariableTable and LocalVariableTypeTable attributes */
static void
write_var_table(MethodImage *mi)
{
    unsigned             i;
    unsigned             count;
    CrwClassImage *      ci;

    CRW_ASSERT_MI(mi);
    ci = mi->ci;
    (void)copyU4(ci);
    count = copyU2(ci);
    for(i=0; i<count; i++) {
        ByteOffset start_pc;
        ByteOffset new_start_pc;
        ByteOffset length;
        ByteOffset new_length;
        ByteOffset end_pc;
        ByteOffset new_end_pc;

        start_pc        = readU2(ci);
        length          = readU2(ci);

        if ( start_pc == 0 ) {
            new_start_pc = 0; /* Don't skip entry injection code. */
        } else {
            new_start_pc = method_code_map(mi, start_pc);
        }
        end_pc          = start_pc + length;
        new_end_pc      = method_code_map(mi, end_pc);
        new_length      = new_end_pc - new_start_pc;

        writeU2(ci, new_start_pc);
        writeU2(ci, new_length);
        (void)copyU2(ci);
        (void)copyU2(ci);
        (void)copyU2(ci);
    }
}

/* The uoffset field is u2 or u4 depending on the code_len.
 *   Note that the code_len is likely changing, so be careful here.
 */
static unsigned
readUoffset(MethodImage *mi)
{
    if ( mi->code_len > 65535 ) {
        return readU4(mi->ci);
    }
    return readU2(mi->ci);
}

static void
writeUoffset(MethodImage *mi, unsigned val)
{
    if ( mi->new_code_len > 65535 ) {
        writeU4(mi->ci, val);
    }
    writeU2(mi->ci, val);
}

static unsigned
copyUoffset(MethodImage *mi)
{
    unsigned uoffset;

    uoffset = readUoffset(mi);
    writeUoffset(mi, uoffset);
    return uoffset;
}

/* Copy over verification_type_info structure */
static void
copy_verification_types(MethodImage *mi, int ntypes)
{
    /* If there were ntypes, we just copy that over, no changes */
    if ( ntypes > 0 ) {
        int j;

        for ( j = 0 ; j < ntypes ; j++ ) {
            unsigned tag;

            tag = copyU1(mi->ci);
            switch ( tag ) {
                case JVM_ITEM_Object:
                    (void)copyU2(mi->ci); /* Constant pool entry */
                    break;
                case JVM_ITEM_Uninitialized:
                    /* Code offset for 'new' opcode is for this object */
                    writeUoffset(mi, method_code_map(mi, readUoffset(mi)));
                    break;
            }
        }
    }
}

/* Process the StackMapTable attribute. We didn't add any basic blocks
 *   so the frame count remains the same but we may need to process the
 *   frame types due to offset changes putting things out of range.
 */
static void
write_stackmap_table(MethodImage *mi)
{
    CrwClassImage *ci;
    CrwPosition    save_position;
    ByteOffset     last_pc;
    ByteOffset     last_new_pc;
    unsigned       i;
    unsigned       attr_len;
    unsigned       new_attr_len;
    unsigned       count;
    unsigned       delta_adj;

    CRW_ASSERT_MI(mi);
    ci = mi->ci;

    /* Save the position of the attribute length so we can fix it later */
    save_position = ci->output_position;
    attr_len      = copyU4(ci);
    count         = copyUoffset(mi);  /* uoffset: number_of_entries */
    if ( count == 0 ) {
        CRW_ASSERT(ci, attr_len==2);
        return;
    }

    /* Process entire stackmap */
    last_pc     = 0;
    last_new_pc = 0;
    delta_adj   = 0;
    for ( i = 0 ; i < count ; i++ ) {
        ByteOffset new_pc=0;    /* new pc in instrumented code */
        unsigned   ft;        /* frame_type */
        int        delta=0;     /* pc delta */
        int        new_delta=0; /* new pc delta */

        ft = readU1(ci);
        if ( ft <= 63 ) {
            /* Frame Type: same_frame ([0,63]) */
            unsigned   new_ft;    /* new frame_type */

            delta     = (delta_adj + ft);
            new_pc    = method_code_map(mi, last_pc + delta);
            new_delta = new_pc - last_new_pc;
            new_ft    = (new_delta - delta_adj);
            if ( new_ft > 63 ) {
                /* Change to same_frame_extended (251) */
                new_ft = 251;
                writeU1(ci, new_ft);
                writeUoffset(mi, (new_delta - delta_adj));
            } else {
                writeU1(ci, new_ft);
            }
        } else if ( ft >= 64 && ft <= 127 ) {
            /* Frame Type: same_locals_1_stack_item_frame ([64,127]) */
            unsigned   new_ft;    /* new frame_type */

            delta     = (delta_adj + ft - 64);
            new_pc    = method_code_map(mi, last_pc + delta);
            new_delta = new_pc - last_new_pc;
            if ( (new_delta - delta_adj) > 63 ) {
                /* Change to same_locals_1_stack_item_frame_extended (247) */
                new_ft = 247;
                writeU1(ci, new_ft);
                writeUoffset(mi, (new_delta - delta_adj));
            } else {
                new_ft = (new_delta - delta_adj) + 64;
                writeU1(ci, new_ft);
            }
            copy_verification_types(mi, 1);
        } else if ( ft >= 128 && ft <= 246 ) {
            /* Frame Type: reserved_for_future_use ([128,246]) */
            CRW_FATAL(ci, "Unknown frame type in StackMapTable attribute");
        } else if ( ft == 247 ) {
            /* Frame Type: same_locals_1_stack_item_frame_extended (247) */
            delta     = (delta_adj + readUoffset(mi));
            new_pc    = method_code_map(mi, last_pc + delta);
            new_delta = new_pc - last_new_pc;
            writeU1(ci, ft);
            writeUoffset(mi, (new_delta - delta_adj));
            copy_verification_types(mi, 1);
        } else if ( ft >= 248 && ft <= 250 ) {
            /* Frame Type: chop_frame ([248,250]) */
            delta     = (delta_adj + readUoffset(mi));
            new_pc    = method_code_map(mi, last_pc + delta);
            new_delta = new_pc - last_new_pc;
            writeU1(ci, ft);
            writeUoffset(mi, (new_delta - delta_adj));
        } else if ( ft == 251 ) {
            /* Frame Type: same_frame_extended (251) */
            delta     = (delta_adj + readUoffset(mi));
            new_pc    = method_code_map(mi, last_pc + delta);
            new_delta = new_pc - last_new_pc;
            writeU1(ci, ft);
            writeUoffset(mi, (new_delta - delta_adj));
        } else if ( ft >= 252 && ft <= 254 ) {
            /* Frame Type: append_frame ([252,254]) */
            delta     = (delta_adj + readUoffset(mi));
            new_pc    = method_code_map(mi, last_pc + delta);
            new_delta = new_pc - last_new_pc;
            writeU1(ci, ft);
            writeUoffset(mi, (new_delta - delta_adj));
            copy_verification_types(mi, (ft - 251));
        } else if ( ft == 255 ) {
            unsigned   ntypes;

            /* Frame Type: full_frame (255) */
            delta     = (delta_adj + readUoffset(mi));
            new_pc    = method_code_map(mi, last_pc + delta);
            new_delta = new_pc - last_new_pc;
            writeU1(ci, ft);
            writeUoffset(mi, (new_delta - delta_adj));
            ntypes    = copyU2(ci); /* ulocalvar */
            copy_verification_types(mi, ntypes);
            ntypes    = copyU2(ci); /* ustack */
            copy_verification_types(mi, ntypes);
        }

        /* Update last_pc and last_new_pc (save on calls to method_code_map) */
        CRW_ASSERT(ci, delta >= 0);
        CRW_ASSERT(ci, new_delta >= 0);
        last_pc    += delta;
        last_new_pc = new_pc;
        CRW_ASSERT(ci, last_pc <= mi->code_len);
        CRW_ASSERT(ci, last_new_pc <= mi->new_code_len);

        /* Delta adjustment, all deltas are -1 now in attribute */
        delta_adj = 1;
    }

    /* Update the attribute length */
    new_attr_len = ci->output_position - (save_position + 4);
    CRW_ASSERT(ci, new_attr_len >= attr_len);
    random_writeU4(ci, save_position, new_attr_len);
}

/* Process the CLDC StackMap attribute. We didn't add any basic blocks
 *   so the frame count remains the same but we may need to process the
 *   frame types due to offset changes putting things out of range.
 */
static void
write_cldc_stackmap_table(MethodImage *mi)
{
    CrwClassImage *ci;
    CrwPosition    save_position;
    unsigned       i;
    unsigned       attr_len;
    unsigned       new_attr_len;
    unsigned       count;

    CRW_ASSERT_MI(mi);
    ci = mi->ci;

    /* Save the position of the attribute length so we can fix it later */
    save_position = ci->output_position;
    attr_len      = copyU4(ci);
    count         = copyUoffset(mi);  /* uoffset: number_of_entries */
    if ( count == 0 ) {
        CRW_ASSERT(ci, attr_len==2);
        return;
    }

    /* Process entire stackmap */
    for ( i = 0 ; i < count ; i++ ) {
        unsigned   ntypes;

        writeUoffset(mi, method_code_map(mi, readUoffset(mi)));
        ntypes    = copyU2(ci); /* ulocalvar */
        copy_verification_types(mi, ntypes);
        ntypes    = copyU2(ci); /* ustack */
        copy_verification_types(mi, ntypes);
    }

    /* Update the attribute length */
    new_attr_len = ci->output_position - (save_position + 4);
    CRW_ASSERT(ci, new_attr_len >= attr_len);
    random_writeU4(ci, save_position, new_attr_len);
}

static void
method_write_exception_table(MethodImage *mi)
{
    unsigned            i;
    unsigned            count;
    CrwClassImage *     ci;

    CRW_ASSERT_MI(mi);
    ci = mi->ci;
    count = copyU2(ci);
    for(i=0; i<count; i++) {
        ByteOffset start_pc;
        ByteOffset new_start_pc;
        ByteOffset end_pc;
        ByteOffset new_end_pc;
        ByteOffset handler_pc;
        ByteOffset new_handler_pc;

        start_pc        = readU2(ci);
        end_pc          = readU2(ci);
        handler_pc      = readU2(ci);

        new_start_pc    = method_code_map(mi, start_pc);
        new_end_pc      = method_code_map(mi, end_pc);
        new_handler_pc  = method_code_map(mi, handler_pc);

        writeU2(ci, new_start_pc);
        writeU2(ci, new_end_pc);
        writeU2(ci, new_handler_pc);
        (void)copyU2(ci);
    }
}

static int
attribute_match(CrwClassImage *ci, CrwCpoolIndex name_index, const char *name)
{
    CrwConstantPoolEntry cs;
    int                  len;

    CRW_ASSERT_CI(ci);
    CRW_ASSERT(ci, name!=NULL);
    len = (int)strlen(name);
    cs = cpool_entry(ci, name_index);
    if ( cs.len==len && strncmp(cs.ptr, name, len)==0) {
       return 1;
    }
    return 0;
}

static void
method_write_code_attribute(MethodImage *mi)
{
    CrwClassImage *     ci;
    CrwCpoolIndex       name_index;

    CRW_ASSERT_MI(mi);
    ci = mi->ci;
    name_index = copyU2(ci);
    if ( attribute_match(ci, name_index, "LineNumberTable") ) {
        write_line_table(mi);
    } else if ( attribute_match(ci, name_index, "LocalVariableTable") ) {
        write_var_table(mi);
    } else if ( attribute_match(ci, name_index, "LocalVariableTypeTable") ) {
        write_var_table(mi); /* Exact same format as the LocalVariableTable */
    } else if ( attribute_match(ci, name_index, "StackMapTable") ) {
        write_stackmap_table(mi);
    } else if ( attribute_match(ci, name_index, "StackMap") ) {
        write_cldc_stackmap_table(mi);
    } else {
        unsigned len;
        len = copyU4(ci);
        copy(ci, len);
    }
}

static int
is_init_method(const char *name)
{
    if ( name!=NULL && strcmp(name,"<init>")==0 ) {
        return JNI_TRUE;
    }
    return JNI_FALSE;
}

static int
is_clinit_method(const char *name)
{
    if ( name!=NULL && strcmp(name,"<clinit>")==0 ) {
        return JNI_TRUE;
    }
    return JNI_FALSE;
}

static int
is_finalize_method(const char *name)
{
    if ( name!=NULL && strcmp(name,"finalize")==0 ) {
        return JNI_TRUE;
    }
    return JNI_FALSE;
}

static int
skip_method(CrwClassImage *ci, const char *name,
                unsigned access_flags, ByteOffset code_len,
                int system_class, jboolean *pskip_call_return_sites)
{
    *pskip_call_return_sites = JNI_FALSE;
    if ( system_class ) {
        if ( code_len == 1 && is_init_method(name) ) {
            return JNI_TRUE;
        } else if ( code_len == 1 && is_finalize_method(name) ) {
            return JNI_TRUE;
        } else if ( is_clinit_method(name) ) {
            return JNI_TRUE;
        } else if ( ci->is_thread_class && strcmp(name,"currentThread")==0 ) {
            return JNI_TRUE;
        }
        /*
        if ( access_flags & JVM_ACC_PRIVATE ) {
            *pskip_call_return_sites = JNI_TRUE;
        }
        */
    }
    return JNI_FALSE;
}

/* Process all code attributes */
static void
method_write_bytecodes(CrwClassImage *ci, unsigned mnum, unsigned access_flags)
{
    CrwPosition         output_attr_len_position;
    CrwPosition         output_max_stack_position;
    CrwPosition         output_code_len_position;
    CrwPosition         start_of_output_bytecodes;
    unsigned            i;
    unsigned            attr_len;
    unsigned            max_stack;
    ByteOffset          code_len;
    unsigned            attr_count;
    unsigned            new_attr_len;
    MethodImage *       mi;
    jboolean            object_init_method;
    jboolean            skip_call_return_sites;

    CRW_ASSERT_CI(ci);

    /* Attribute Length */
    output_attr_len_position = ci->output_position;
    attr_len = copyU4(ci);

    /* Max Stack */
    output_max_stack_position = ci->output_position;
    max_stack = copyU2(ci);

    /* Max Locals */
    (void)copyU2(ci);

    /* Code Length */
    output_code_len_position = ci->output_position;
    code_len = copyU4(ci);
    start_of_output_bytecodes = ci->output_position;

    /* Some methods should not be instrumented */
    object_init_method = JNI_FALSE;
    skip_call_return_sites = JNI_FALSE;
    if ( ci->is_object_class &&
         is_init_method(ci->method_name[mnum]) &&
         strcmp(ci->method_descr[mnum],"()V")==0 ) {
        object_init_method = JNI_TRUE;
        skip_call_return_sites = JNI_TRUE;
    } else if ( skip_method(ci, ci->method_name[mnum], access_flags,
                code_len, ci->system_class, &skip_call_return_sites) ) {
        /* Copy remainder minus already copied, the U2 max_stack,
         *   U2 max_locals, and U4 code_length fields have already
         *   been processed.
         */
        copy(ci, attr_len - (2+2+4));
        return;
    }

    /* Start Injection */
    mi = method_init(ci, mnum, code_len);
    mi->object_init_method = object_init_method;
    mi->access_flags = access_flags;
    mi->skip_call_return_sites = skip_call_return_sites;

    /* Save the current position as the start of the input bytecodes */
    mi->start_of_input_bytecodes = ci->input_position;

    /* The max stack may increase */
    mi->max_stack = max_stack;
    mi->new_max_stack = max_stack;

    /* Adjust all code offsets */
    method_inject_and_write_code(mi);

    /* Fix up code length (save new_code_len for later attribute processing) */
    mi->new_code_len = (int)(ci->output_position - start_of_output_bytecodes);
    random_writeU4(ci, output_code_len_position, mi->new_code_len);

    /* Fixup max stack */
    CRW_ASSERT(ci, mi->new_max_stack <= 0xFFFF);
    random_writeU2(ci, output_max_stack_position, mi->new_max_stack);

    /* Copy exception table */
    method_write_exception_table(mi);

    /* Copy code attributes (needs mi->new_code_len) */
    attr_count = copyU2(ci);
    for (i = 0; i < attr_count; ++i) {
        method_write_code_attribute(mi);
    }

    /* Fix up attribute length */
    new_attr_len = (int)(ci->output_position - (output_attr_len_position + 4));
    random_writeU4(ci, output_attr_len_position, new_attr_len);

    /* Free method data */
    method_term(mi);
    mi = NULL;

}

static void
method_write(CrwClassImage *ci, unsigned mnum)
{
    unsigned            i;
    unsigned            access_flags;
    CrwCpoolIndex       name_index;
    CrwCpoolIndex       descr_index;
    unsigned            attr_count;

    access_flags = copyU2(ci);
    name_index = copyU2(ci);
    ci->method_name[mnum] = cpool_entry(ci, name_index).ptr;
    descr_index = copyU2(ci);
    ci->method_descr[mnum] = cpool_entry(ci, descr_index).ptr;
    attr_count = copyU2(ci);

    for (i = 0; i < attr_count; ++i) {
        CrwCpoolIndex name_index;

        name_index = copyU2(ci);
        if ( attribute_match(ci, name_index, "Code") ) {
            method_write_bytecodes(ci, mnum, access_flags);
        } else {
            unsigned len;
            len = copyU4(ci);
            copy(ci, len);
        }
    }
}

static void
method_write_all(CrwClassImage *ci)
{
    unsigned i;
    unsigned count;

    count = copyU2(ci);
    ci->method_count = count;
    if ( count > 0 ) {
        ci->method_name = (const char **)allocate_clean(ci, count*(int)sizeof(const char*));
        ci->method_descr = (const char **)allocate_clean(ci, count*(int)sizeof(const char*));
    }

    for (i = 0; i < count; ++i) {
        method_write(ci, i);
    }

    if ( ci->mnum_callback != NULL ) {
        (*(ci->mnum_callback))(ci->number, ci->method_name, ci->method_descr,
                         count);
    }
}

/* ------------------------------------------------------------------- */
/* Cleanup function. */

static void
cleanup(CrwClassImage *ci)
{
    CRW_ASSERT_CI(ci);
    if ( ci->name != NULL ) {
        deallocate(ci, (void*)ci->name);
        ci->name = NULL;
    }
    if ( ci->method_name != NULL ) {
        deallocate(ci, (void*)ci->method_name);
        ci->method_name = NULL;
    }
    if ( ci->method_descr != NULL ) {
        deallocate(ci, (void*)ci->method_descr);
        ci->method_descr = NULL;
    }
    if ( ci->cpool != NULL ) {
        CrwCpoolIndex i;
        for(i=0; i<ci->cpool_count_plus_one; i++) {
            if ( ci->cpool[i].ptr != NULL ) {
                deallocate(ci, (void*)(ci->cpool[i].ptr));
                ci->cpool[i].ptr = NULL;
            }
        }
        deallocate(ci, (void*)ci->cpool);
        ci->cpool = NULL;
    }
}

static jboolean
skip_class(unsigned access_flags)
{
    if ( access_flags & JVM_ACC_INTERFACE ) {
        return JNI_TRUE;
    }
    return JNI_FALSE;
}

static long
inject_class(struct CrwClassImage *ci,
                 int system_class,
                 char* tclass_name,
                 char* tclass_sig,
                 char* call_name,
                 char* call_sig,
                 char* return_name,
                 char* return_sig,
                 char* obj_init_name,
                 char* obj_init_sig,
                 char* newarray_name,
                 char* newarray_sig,
                 unsigned char *buf,
                 long buf_len)
{
    CrwConstantPoolEntry        cs;
    CrwCpoolIndex               this_class;
    CrwCpoolIndex               super_class;
    unsigned                    magic;
    unsigned                    classfileMajorVersion;
    unsigned                    classfileMinorVersion;
    unsigned                    interface_count;

    CRW_ASSERT_CI(ci);
    CRW_ASSERT(ci, buf!=NULL);
    CRW_ASSERT(ci, buf_len!=0);

    CRW_ASSERT(ci, strchr(tclass_name,'.')==NULL); /* internal qualified name */

    ci->injection_count         = 0;
    ci->system_class            = system_class;
    ci->tclass_name             = tclass_name;
    ci->tclass_sig              = tclass_sig;
    ci->call_name               = call_name;
    ci->call_sig                = call_sig;
    ci->return_name             = return_name;
    ci->return_sig              = return_sig;
    ci->obj_init_name           = obj_init_name;
    ci->obj_init_sig            = obj_init_sig;
    ci->newarray_name           = newarray_name;
    ci->newarray_sig            = newarray_sig;
    ci->output                  = buf;
    ci->output_len              = buf_len;

    magic = copyU4(ci);
    CRW_ASSERT(ci, magic==0xCAFEBABE);
    if ( magic != 0xCAFEBABE ) {
        return (long)0;
    }

    /* minor version number not used */
    classfileMinorVersion = copyU2(ci);
    /* major version number not used */
    classfileMajorVersion = copyU2(ci);
    CRW_ASSERT(ci,  (classfileMajorVersion <= JVM_CLASSFILE_MAJOR_VERSION) ||
                   ((classfileMajorVersion == JVM_CLASSFILE_MAJOR_VERSION) &&
                    (classfileMinorVersion <= JVM_CLASSFILE_MINOR_VERSION)));

    cpool_setup(ci);

    ci->access_flags        = copyU2(ci);
    if ( skip_class(ci->access_flags) ) {
        return (long)0;
    }

    this_class          = copyU2(ci);

    cs = cpool_entry(ci, (CrwCpoolIndex)(cpool_entry(ci, this_class).index1));
    if ( ci->name == NULL ) {
        ci->name = duplicate(ci, cs.ptr, cs.len);
        CRW_ASSERT(ci, strchr(ci->name,'.')==NULL); /* internal qualified name */
    }
    CRW_ASSERT(ci, (int)strlen(ci->name)==cs.len && strncmp(ci->name, cs.ptr, cs.len)==0);

    super_class         = copyU2(ci);
    if ( super_class == 0 ) {
        ci->is_object_class = JNI_TRUE;
        CRW_ASSERT(ci, strcmp(ci->name,"java/lang/Object")==0);
    }

    interface_count     = copyU2(ci);
    copy(ci, interface_count * 2);

    copy_all_fields(ci);

    method_write_all(ci);

    if ( ci->injection_count == 0 ) {
        return (long)0;
    }

    copy_attributes(ci);

    return (long)ci->output_position;
}

/* ------------------------------------------------------------------- */
/* Exported interfaces */

JNIEXPORT void JNICALL
java_crw_demo(unsigned class_number,
         const char *name,
         const unsigned char *file_image,
         long file_len,
         int system_class,
         char* tclass_name,     /* Name of class that has tracker methods. */
         char* tclass_sig,      /* Signature of tclass */
         char* call_name,       /* Method name to call at offset 0 */
         char* call_sig,        /* Signature of this method */
         char* return_name,     /* Method name to call before any return */
         char* return_sig,      /* Signature of this method */
         char* obj_init_name,   /* Method name to call in Object <init> */
         char* obj_init_sig,    /* Signature of this method */
         char* newarray_name,   /* Method name to call after newarray opcodes */
         char* newarray_sig,    /* Signature of this method */
         unsigned char **pnew_file_image,
         long *pnew_file_len,
         FatalErrorHandler fatal_error_handler,
         MethodNumberRegister mnum_callback)
{
    CrwClassImage ci;
    long          max_length;
    long          new_length;
    void         *new_image;
    int           len;

    /* Initial setup of the CrwClassImage structure */
    (void)memset(&ci, 0, (int)sizeof(CrwClassImage));
    ci.fatal_error_handler = fatal_error_handler;
    ci.mnum_callback       = mnum_callback;

    /* Do some interface error checks */
    if ( pnew_file_image==NULL ) {
        CRW_FATAL(&ci, "pnew_file_image==NULL");
    }
    if ( pnew_file_len==NULL ) {
        CRW_FATAL(&ci, "pnew_file_len==NULL");
    }

    /* No file length means do nothing */
    *pnew_file_image = NULL;
    *pnew_file_len = 0;
    if ( file_len==0 ) {
        return;
    }

    /* Do some more interface error checks */
    if ( file_image == NULL ) {
        CRW_FATAL(&ci, "file_image == NULL");
    }
    if ( file_len < 0 ) {
        CRW_FATAL(&ci, "file_len < 0");
    }
    if ( system_class != 0 && system_class != 1 ) {
        CRW_FATAL(&ci, "system_class is not 0 or 1");
    }
    if ( tclass_name == NULL ) {
        CRW_FATAL(&ci, "tclass_name == NULL");
    }
    if ( tclass_sig == NULL || tclass_sig[0]!='L' ) {
        CRW_FATAL(&ci, "tclass_sig is not a valid class signature");
    }
    len = (int)strlen(tclass_sig);
    if ( tclass_sig[len-1]!=';' ) {
        CRW_FATAL(&ci, "tclass_sig is not a valid class signature");
    }
    if ( call_name != NULL ) {
        if ( call_sig == NULL || strcmp(call_sig, "(II)V") != 0 ) {
            CRW_FATAL(&ci, "call_sig is not (II)V");
        }
    }
    if ( return_name != NULL ) {
        if ( return_sig == NULL || strcmp(return_sig, "(II)V") != 0 ) {
            CRW_FATAL(&ci, "return_sig is not (II)V");
        }
    }
    if ( obj_init_name != NULL ) {
        if ( obj_init_sig == NULL || strcmp(obj_init_sig, "(Ljava/lang/Object;)V") != 0 ) {
            CRW_FATAL(&ci, "obj_init_sig is not (Ljava/lang/Object;)V");
        }
    }
    if ( newarray_name != NULL ) {
        if ( newarray_sig == NULL || strcmp(newarray_sig, "(Ljava/lang/Object;)V") != 0 ) {
            CRW_FATAL(&ci, "newarray_sig is not (Ljava/lang/Object;)V");
        }
    }

    /* Finish setup the CrwClassImage structure */
    ci.is_thread_class = JNI_FALSE;
    if ( name != NULL ) {
        CRW_ASSERT(&ci, strchr(name,'.')==NULL); /* internal qualified name */

        ci.name = duplicate(&ci, name, (int)strlen(name));
        if ( strcmp(name, "java/lang/Thread")==0 ) {
            ci.is_thread_class = JNI_TRUE;
        }
    }
    ci.number = class_number;
    ci.input = file_image;
    ci.input_len = file_len;

    /* Do the injection */
    max_length = file_len*2 + 512; /* Twice as big + 512 */
    new_image = allocate(&ci, (int)max_length);
    new_length = inject_class(&ci,
                                 system_class,
                                 tclass_name,
                                 tclass_sig,
                                 call_name,
                                 call_sig,
                                 return_name,
                                 return_sig,
                                 obj_init_name,
                                 obj_init_sig,
                                 newarray_name,
                                 newarray_sig,
                                 new_image,
                                 max_length);

    /* Dispose or shrink the space to be returned. */
    if ( new_length == 0 ) {
        deallocate(&ci, (void*)new_image);
        new_image = NULL;
    } else {
        new_image = (void*)reallocate(&ci, (void*)new_image, (int)new_length);
    }

    /* Return the new class image */
    *pnew_file_image = (unsigned char *)new_image;
    *pnew_file_len = (long)new_length;

    /* Cleanup before we leave. */
    cleanup(&ci);
}

/* Return the classname for this class which is inside the classfile image. */
JNIEXPORT char * JNICALL
java_crw_demo_classname(const unsigned char *file_image, long file_len,
        FatalErrorHandler fatal_error_handler)
{
    CrwClassImage               ci;
    CrwConstantPoolEntry        cs;
    CrwCpoolIndex               this_class;
    unsigned                    magic;
    char *                      name;

    name = NULL;

    if ( file_len==0 || file_image==NULL ) {
        return name;
    }

    /* The only fields we need filled in are the image pointer and the error
     *    handler.
     *    By not adding an output buffer pointer, no output is created.
     */
    (void)memset(&ci, 0, (int)sizeof(CrwClassImage));
    ci.input     = file_image;
    ci.input_len = file_len;
    ci.fatal_error_handler = fatal_error_handler;

    /* Read out the bytes from the classfile image */

    magic = readU4(&ci); /* magic number */
    CRW_ASSERT(&ci, magic==0xCAFEBABE);
    if ( magic != 0xCAFEBABE ) {
        return name;
    }
    (void)readU2(&ci); /* minor version number */
    (void)readU2(&ci); /* major version number */

    /* Read in constant pool. Since no output setup, writes are NOP's */
    cpool_setup(&ci);

    (void)readU2(&ci); /* access flags */
    this_class = readU2(&ci); /* 'this' class */

    /* Get 'this' constant pool entry */
    cs = cpool_entry(&ci, (CrwCpoolIndex)(cpool_entry(&ci, this_class).index1));

    /* Duplicate the name */
    name = (char *)duplicate(&ci, cs.ptr, cs.len);

    /* Cleanup before we leave. */
    cleanup(&ci);

    /* Return malloc space */
    return name;
}

Other Java examples (source code examples)

Here is a short list of links related to this Java java_crw_demo.c source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.