/**
 * Free up global refs held by the filter.
 * free things up at the JNI level if needed.
 */
static jvmtiError
clearFilters(HandlerNode *node)
{
    JNIEnv *env = getEnv();
    jint i;
    jvmtiError error = JVMTI_ERROR_NONE;
    Filter *filter = FILTERS_ARRAY(node);

    for (i = 0; i < FILTER_COUNT(node); ++i, ++filter) {
        switch (filter->modifier) {
            case JDWP_REQUEST_MODIFIER(ThreadOnly):
                if ( filter->u.ThreadOnly.thread != NULL ) {
                    tossGlobalRef(env, &(filter->u.ThreadOnly.thread));
                }
                break;
            case JDWP_REQUEST_MODIFIER(LocationOnly):
                tossGlobalRef(env, &(filter->u.LocationOnly.clazz));
                break;
            case JDWP_REQUEST_MODIFIER(FieldOnly):
                tossGlobalRef(env, &(filter->u.FieldOnly.clazz));
                break;
            case JDWP_REQUEST_MODIFIER(ExceptionOnly):
                if ( filter->u.ExceptionOnly.exception != NULL ) {
                    tossGlobalRef(env, &(filter->u.ExceptionOnly.exception));
                }
                break;
            case JDWP_REQUEST_MODIFIER(InstanceOnly):
                if ( filter->u.InstanceOnly.instance != NULL ) {
                    tossGlobalRef(env, &(filter->u.InstanceOnly.instance));
                }
                break;
            case JDWP_REQUEST_MODIFIER(ClassOnly):
                tossGlobalRef(env, &(filter->u.ClassOnly.clazz));
                break;
            case JDWP_REQUEST_MODIFIER(ClassMatch):
                jvmtiDeallocate(filter->u.ClassMatch.classPattern);
                break;
            case JDWP_REQUEST_MODIFIER(ClassExclude):
                jvmtiDeallocate(filter->u.ClassExclude.classPattern);
                break;
            case JDWP_REQUEST_MODIFIER(Step): {
                jthread thread = filter->u.Step.thread;
                error = stepControl_endStep(thread);
                if (error == JVMTI_ERROR_NONE) {
                    tossGlobalRef(env, &(filter->u.Step.thread));
                }
                break;
            }
        }
    }
    if (error == JVMTI_ERROR_NONE) {
        FILTER_COUNT(node) = 0; /* blast so we don't clear again */
    }

    return error;
}
/**
 * Set a breakpoint if this is the first one at this location.
 */
static jvmtiError
setBreakpoint(HandlerNode *node)
{
    jvmtiError error = JVMTI_ERROR_NONE;
    Filter *filter;

    filter = findFilter(node, JDWP_REQUEST_MODIFIER(LocationOnly));
    if (filter == NULL) {
        /* bp event with no location filter */
        error = AGENT_ERROR_INTERNAL;
    } else {
        LocationFilter *lf = &(filter->u.LocationOnly);

        /* if this is the first handler for this
         * location, set bp at JVMTI level
         */
        if (!eventHandlerRestricted_iterator(
                EI_BREAKPOINT, matchBreakpoint, lf)) {
            LOG_LOC(("SetBreakpoint at location: method=%p,location=%d",
                        lf->method, (int)lf->location));
            error = JVMTI_FUNC_PTR(gdata->jvmti,SetBreakpoint)
                        (gdata->jvmti, lf->method, lf->location);
        }
    }
    return error;
}
jvmtiError
eventFilter_setStepFilter(HandlerNode *node, jint index,
                          jthread thread, jint size, jint depth)
{
    jvmtiError error;
    JNIEnv *env = getEnv();
    StepFilter *filter = &FILTER(node, index).u.Step;
    if (index >= FILTER_COUNT(node)) {
        return AGENT_ERROR_ILLEGAL_ARGUMENT;
    }
    if (NODE_EI(node) != EI_SINGLE_STEP) {
        return AGENT_ERROR_ILLEGAL_ARGUMENT;
    }

    /* Create a thread ref that will live beyond */
    /* the end of this call */
    saveGlobalRef(env, thread, &(filter->thread));
    error = stepControl_beginStep(env, filter->thread, size, depth, node);
    if (error != JVMTI_ERROR_NONE) {
        tossGlobalRef(env, &(filter->thread));
        return error;
    }
    FILTER(node, index).modifier = JDWP_REQUEST_MODIFIER(Step);
    filter->depth = depth;
    filter->size = size;
    return JVMTI_ERROR_NONE;
}
/**
 * Clear a watchpoint if this is the last one on this field.
 */
static jvmtiError
clearWatchpoint(HandlerNode *node)
{
    jvmtiError error = JVMTI_ERROR_NONE;
    Filter *filter;

    filter = findFilter(node, JDWP_REQUEST_MODIFIER(FieldOnly));
    if (filter == NULL) {
        /* event with no field filter */
        error = AGENT_ERROR_INTERNAL;
    } else {
        FieldFilter *ff = &(filter->u.FieldOnly);

        /* if this is the last handler for this
         * field, clear wp at JVMTI level
         */
        if (!eventHandlerRestricted_iterator(
                NODE_EI(node), matchWatchpoint, ff)) {
            error = (NODE_EI(node) == EI_FIELD_ACCESS) ?
                JVMTI_FUNC_PTR(gdata->jvmti,ClearFieldAccessWatch)
                        (gdata->jvmti, ff->clazz, ff->field) :
                JVMTI_FUNC_PTR(gdata->jvmti,ClearFieldModificationWatch)
                                (gdata->jvmti, ff->clazz, ff->field);
        }
    }
    return error;
}
jint 
eventFilter_setClassOnlyFilter(HandlerNode *node, jint index, 
                               jclass clazz)
{
    JNIEnv *env = getEnv();
    ClassFilter *filter = &FILTER(node, index).u.ClassOnly;
    if (index >= FILTER_COUNT(node)) {
        return JVMDI_ERROR_ILLEGAL_ARGUMENT;
    }
    if ((KIND(node) == JVMDI_EVENT_USER_DEFINED) || 
        (KIND(node) == JVMDI_EVENT_CLASS_UNLOAD) ||
        (KIND(node) == JVMDI_EVENT_THREAD_START) ||
        (KIND(node) == JVMDI_EVENT_THREAD_END)) {

        return JVMDI_ERROR_ILLEGAL_ARGUMENT;
    }

    /* Create a class ref that will live beyond */
    /* the end of this call */
    clazz = (*env)->NewGlobalRef(env, clazz);
    if (clazz == NULL) {
        return JVMDI_ERROR_OUT_OF_MEMORY;
    }
    FILTER(node, index).modifier = JDWP_REQUEST_MODIFIER(ClassOnly);
    filter->clazz = clazz;
    return JVMDI_ERROR_NONE;
}
jvmtiError
eventFilter_setExceptionOnlyFilter(HandlerNode *node, jint index,
                                   jclass exceptionClass,
                                   jboolean caught,
                                   jboolean uncaught)
{
    JNIEnv *env = getEnv();
    ExceptionFilter *filter = &FILTER(node, index).u.ExceptionOnly;
    if (index >= FILTER_COUNT(node)) {
        return AGENT_ERROR_ILLEGAL_ARGUMENT;
    }
    if (NODE_EI(node) != EI_EXCEPTION) {
        return AGENT_ERROR_ILLEGAL_ARGUMENT;
    }

    filter->exception = NULL;
    if (exceptionClass != NULL) {
        /* Create a class ref that will live beyond */
        /* the end of this call */
        saveGlobalRef(env, exceptionClass, &(filter->exception));
    }
    FILTER(node, index).modifier =
                       JDWP_REQUEST_MODIFIER(ExceptionOnly);
    filter->caught = caught;
    filter->uncaught = uncaught;
    return JVMTI_ERROR_NONE;
}
jint 
eventFilter_setExceptionOnlyFilter(HandlerNode *node, jint index, 
                                   jclass exceptionClass, 
                                   jboolean caught,
                                   jboolean uncaught)
{
    JNIEnv *env = getEnv();
    ExceptionFilter *filter = &FILTER(node, index).u.ExceptionOnly;
    if (index >= FILTER_COUNT(node)) {
        return JVMDI_ERROR_ILLEGAL_ARGUMENT;
    }
    if (KIND(node) != JVMDI_EVENT_EXCEPTION) { 
        return JVMDI_ERROR_ILLEGAL_ARGUMENT;
    }

    if (exceptionClass != NULL) {
        /* Create a class ref that will live beyond */
        /* the end of this call */
        exceptionClass = (*env)->NewGlobalRef(env, exceptionClass);
        if (exceptionClass == NULL) {
            return JVMDI_ERROR_OUT_OF_MEMORY;
        }
    }
    FILTER(node, index).modifier = 
                       JDWP_REQUEST_MODIFIER(ExceptionOnly);
    filter->exception = exceptionClass;
    filter->caught = caught;
    filter->uncaught = uncaught;
    return JVMDI_ERROR_NONE;
}
jint 
eventFilter_setStepFilter(HandlerNode *node, jint index, 
                          jthread thread, jint size, jint depth)
{
    jint error;
    JNIEnv *env = getEnv();
    StepFilter *filter = &FILTER(node, index).u.Step;
    if (index >= FILTER_COUNT(node)) {
        return JVMDI_ERROR_ILLEGAL_ARGUMENT;
    }
    if (KIND(node) != JVMDI_EVENT_SINGLE_STEP) { 
        return JVMDI_ERROR_ILLEGAL_ARGUMENT;
    }

    /* Create a thread ref that will live beyond */
    /* the end of this call */
    thread = (*env)->NewGlobalRef(env, thread);
    if (thread == NULL) {
        return JVMDI_ERROR_OUT_OF_MEMORY;
    }
    error = stepControl_beginStep(thread, size,depth, node);
    if (error != JVMDI_ERROR_NONE) {
        (*env)->DeleteGlobalRef(env, thread);
        return error;
    }
    FILTER(node, index).modifier = JDWP_REQUEST_MODIFIER(Step);
    filter->thread = thread;
    filter->depth = depth;
    filter->size = size;
    return JVMDI_ERROR_NONE;
}
jvmtiError
eventFilter_setLocationOnlyFilter(HandlerNode *node, jint index,
                                  jclass clazz, jmethodID method,
                                  jlocation location)
{
    JNIEnv *env = getEnv();
    LocationFilter *filter = &FILTER(node, index).u.LocationOnly;
    if (index >= FILTER_COUNT(node)) {
        return AGENT_ERROR_ILLEGAL_ARGUMENT;
    }
    if ((NODE_EI(node) != EI_BREAKPOINT) &&
        (NODE_EI(node) != EI_FIELD_ACCESS) &&
        (NODE_EI(node) != EI_FIELD_MODIFICATION) &&
        (NODE_EI(node) != EI_SINGLE_STEP) &&
        (NODE_EI(node) != EI_EXCEPTION)) {

        return AGENT_ERROR_ILLEGAL_ARGUMENT;
    }

    /* Create a class ref that will live beyond */
    /* the end of this call */
    saveGlobalRef(env, clazz, &(filter->clazz));
    FILTER(node, index).modifier = JDWP_REQUEST_MODIFIER(LocationOnly);
    filter->method = method;
    filter->location = location;
    return JVMTI_ERROR_NONE;
}
jint 
eventFilter_setLocationOnlyFilter(HandlerNode *node, jint index, 
                                  jclass clazz, jmethodID method,
                                  jlocation location)
{
    JNIEnv *env = getEnv();
    LocationFilter *filter = &FILTER(node, index).u.LocationOnly;
    if (index >= FILTER_COUNT(node)) {
        return JVMDI_ERROR_ILLEGAL_ARGUMENT;
    }
    if ((KIND(node) != JVMDI_EVENT_BREAKPOINT) && 
        (KIND(node) != JVMDI_EVENT_FIELD_ACCESS) && 
        (KIND(node) != JVMDI_EVENT_FIELD_MODIFICATION) && 
        (KIND(node) != JVMDI_EVENT_SINGLE_STEP) && 
        (KIND(node) != JVMDI_EVENT_EXCEPTION)) {

        return JVMDI_ERROR_ILLEGAL_ARGUMENT;
    }

    /* Create a class ref that will live beyond */
    /* the end of this call */
    clazz = (*env)->NewGlobalRef(env, clazz);
    if (clazz == NULL) {
        return JVMDI_ERROR_OUT_OF_MEMORY;
    }
    FILTER(node, index).modifier = JDWP_REQUEST_MODIFIER(LocationOnly);
    filter->clazz = clazz;
    filter->method = method;
    filter->location = location;
    return JVMDI_ERROR_NONE;
}
jint 
eventFilter_setFieldOnlyFilter(HandlerNode *node, jint index, 
                               jclass clazz, jfieldID field)
{
    JNIEnv *env = getEnv();
    FieldFilter *filter = &FILTER(node, index).u.FieldOnly;
    if (index >= FILTER_COUNT(node)) {
        return JVMDI_ERROR_ILLEGAL_ARGUMENT;
    }
    if ((KIND(node) != JVMDI_EVENT_FIELD_ACCESS) && 
        (KIND(node) != JVMDI_EVENT_FIELD_MODIFICATION)) {

        return JVMDI_ERROR_ILLEGAL_ARGUMENT;
    }

    /* Create a class ref that will live beyond */
    /* the end of this call */
    clazz = (*env)->NewGlobalRef(env, clazz);
    if (clazz == NULL) {
        return JVMDI_ERROR_OUT_OF_MEMORY;
    }
    FILTER(node, index).modifier = JDWP_REQUEST_MODIFIER(FieldOnly);
    filter->clazz = clazz;
    filter->field = field;
    return JVMDI_ERROR_NONE;
}
/**
 * Clear a watchpoint if this is the last one on this field.
 */
static jint
clearWatchpoint(HandlerNode *node)
{
    jint error = JVMDI_ERROR_NONE;
    Filter *filter;

    filter = findFilter(node, JDWP_REQUEST_MODIFIER(FieldOnly));
    if (filter == NULL) {
        /* event with no field filter */
        error = JVMDI_ERROR_INTERNAL; 
    } else {
        FieldFilter *ff = &(filter->u.FieldOnly);

        /* if this is the last handler for this 
         * field, clear wp at jvmdi level 
         */
        if (!eventHandlerRestricted_iterator(
                KIND(node), matchWatchpoint, ff)) {
            error = (KIND(node) == JVMDI_EVENT_FIELD_ACCESS) ?
                jvmdi->ClearFieldAccessWatch(ff->clazz, 
                                             ff->field) :
                jvmdi->ClearFieldModificationWatch(ff->clazz,
                                                   ff->field);
        }
    }
    return error;
}
/* Determine if this event is interesting to this handler.  Do so
 * by checking each of the handler's filters.  Return false if any
 * of the filters fail, true if the handler wants this event.
 * Special version of filter for unloads since they don't have an
 * event structure or a jclass.  
 *
 * If shouldDelete is returned true, a count filter has expired
 * and the corresponding node should be deleted.
 */
jboolean 
eventFilterRestricted_passesUnloadFilter(JNIEnv *env,
                                         char *classname, 
                                         HandlerNode *node, 
                                         jboolean *shouldDelete)
{
    Filter *filter = FILTERS_ARRAY(node);
    int i;

    *shouldDelete = JNI_FALSE;
    for (i = 0; i < FILTER_COUNT(node); ++i, ++filter) {
        switch (filter->modifier) {
           
            case JDWP_REQUEST_MODIFIER(Count): {
                JDI_ASSERT(filter->u.Count.count > 0);
                if (--filter->u.Count.count > 0) {
                    return JNI_FALSE;
                }
                *shouldDelete = JNI_TRUE;
                break;
            }
                
            case JDWP_REQUEST_MODIFIER(ClassMatch): {       
                if (!patternStringMatch(classname, 
                        filter->u.ClassMatch.classPattern)) {
                    return JNI_FALSE;
                }
                break;
            }
            
            case JDWP_REQUEST_MODIFIER(ClassExclude): {
                if (patternStringMatch(classname, 
                       filter->u.ClassExclude.classPattern)) {
                    return JNI_FALSE;
                }
                break;
            }
            
            default: 
                ERROR_MESSAGE_EXIT("Invalid filter modifier");
                return JNI_FALSE;
        }
    }
    return JNI_TRUE;
}
jint
eventFilter_setConditionalFilter(HandlerNode *node, jint index, 
                                 jint exprID)
{
    ConditionalFilter *filter = &FILTER(node, index).u.Conditional;
    if (index >= FILTER_COUNT(node)) {
        return JVMDI_ERROR_ILLEGAL_ARGUMENT;
    }
    FILTER(node, index).modifier = JDWP_REQUEST_MODIFIER(Conditional);
    filter->exprID = exprID;
    return JVMDI_ERROR_NONE;
}
/**
 * Determine if the given breakpoint node is in the specified class.
 */
jboolean
eventFilterRestricted_isBreakpointInClass(JNIEnv *env, jclass clazz,
                                          HandlerNode *node)
{
    Filter *filter = FILTERS_ARRAY(node);
    int i;

    for (i = 0; i < FILTER_COUNT(node); ++i, ++filter) {
        switch (filter->modifier) {
            case JDWP_REQUEST_MODIFIER(LocationOnly):
                return isSameObject(env, clazz, filter->u.LocationOnly.clazz);
        }
    }
    return JNI_TRUE; /* should never come here */
}
/**
 * Determine the thread this node is filtered on.
 * NULL if not thread filtered.
 */
static jthread 
requestThread(HandlerNode *node) 
{
    int i;
    Filter *filter = FILTERS_ARRAY(node);

    for (i = 0; i < FILTER_COUNT(node); ++i, ++filter) {
        switch (filter->modifier) {
            case JDWP_REQUEST_MODIFIER(ThreadOnly):
                return filter->u.ThreadOnly.thread;
        }
    }

    return NULL;
}
jint
eventFilter_setCountFilter(HandlerNode *node, jint index, 
                           jint count)
{
    CountFilter *filter = &FILTER(node, index).u.Count;
    if (index >= FILTER_COUNT(node)) {
        return JVMDI_ERROR_ILLEGAL_ARGUMENT;
    }
    if (count <= 0) {
        return JDWP_ERROR(INVALID_COUNT);
    } else {
        FILTER(node, index).modifier = JDWP_REQUEST_MODIFIER(Count);
        filter->count = count;
        return JVMDI_ERROR_NONE;
    }
}
jvmtiError
eventFilter_setSourceNameMatchFilter(HandlerNode *node,
                                    jint index,
                                    char *sourceNamePattern) {
    SourceNameFilter *filter = &FILTER(node, index).u.SourceNameOnly;
    if (index >= FILTER_COUNT(node)) {
        return AGENT_ERROR_ILLEGAL_ARGUMENT;
    }
    if (NODE_EI(node) != EI_CLASS_PREPARE) {
        return AGENT_ERROR_ILLEGAL_ARGUMENT;
    }

    FILTER(node, index).modifier =
                       JDWP_REQUEST_MODIFIER(SourceNameMatch);
    filter->sourceNamePattern = sourceNamePattern;
    return JVMTI_ERROR_NONE;

}
jvmtiError
eventFilter_setThreadOnlyFilter(HandlerNode *node, jint index,
                                jthread thread)
{
    JNIEnv *env = getEnv();
    ThreadFilter *filter = &FILTER(node, index).u.ThreadOnly;
    if (index >= FILTER_COUNT(node)) {
        return AGENT_ERROR_ILLEGAL_ARGUMENT;
    }
    if (NODE_EI(node) == EI_GC_FINISH) {
        return AGENT_ERROR_ILLEGAL_ARGUMENT;
    }

    /* Create a thread ref that will live beyond */
    /* the end of this call */
    saveGlobalRef(env, thread, &(filter->thread));
    FILTER(node, index).modifier = JDWP_REQUEST_MODIFIER(ThreadOnly);
    return JVMTI_ERROR_NONE;
}
jvmtiError
eventFilter_setClassExcludeFilter(HandlerNode *node, jint index,
                                  char *classPattern)
{
    MatchFilter *filter = &FILTER(node, index).u.ClassExclude;
    if (index >= FILTER_COUNT(node)) {
        return AGENT_ERROR_ILLEGAL_ARGUMENT;
    }
    if (
        (NODE_EI(node) == EI_THREAD_START) ||
        (NODE_EI(node) == EI_THREAD_END)) {

        return AGENT_ERROR_ILLEGAL_ARGUMENT;
    }

    FILTER(node, index).modifier =
                       JDWP_REQUEST_MODIFIER(ClassExclude);
    filter->classPattern = classPattern;
    return JVMTI_ERROR_NONE;
}
/**
 * Determine if the specified watchpoint node has the
 * same field as the FieldFilter passed in arg.
 *
 * This is a match function called by a
 * eventHandlerRestricted_iterator invokation.
 */
static jboolean
matchWatchpoint(JNIEnv *env, HandlerNode *node, void *arg)
{
    FieldFilter *goal = (FieldFilter *)arg;
    Filter *filter = FILTERS_ARRAY(node);
    int i;

    for (i = 0; i < FILTER_COUNT(node); ++i, ++filter) {
        switch (filter->modifier) {
        case JDWP_REQUEST_MODIFIER(FieldOnly): {
            FieldFilter *trial = &(filter->u.FieldOnly);
            if (trial->field == goal->field &&
                isSameObject(env, trial->clazz, goal->clazz)) {
                return JNI_TRUE;
            }
        }
        }
    }
    return JNI_FALSE;
}
jint 
eventFilter_setClassExcludeFilter(HandlerNode *node, jint index, 
                                  char *classPattern)
{
    MatchFilter *filter = &FILTER(node, index).u.ClassExclude;
    if (index >= FILTER_COUNT(node)) {
        return JVMDI_ERROR_ILLEGAL_ARGUMENT;
    }
    if ((KIND(node) == JVMDI_EVENT_USER_DEFINED) || 
        (KIND(node) == JVMDI_EVENT_THREAD_START) ||
        (KIND(node) == JVMDI_EVENT_THREAD_END)) {

        return JVMDI_ERROR_ILLEGAL_ARGUMENT;
    }

    FILTER(node, index).modifier = 
                       JDWP_REQUEST_MODIFIER(ClassExclude);
    filter->classPattern = classPattern;
    return JVMDI_ERROR_NONE;
}
jvmtiError
eventFilter_setInstanceOnlyFilter(HandlerNode *node, jint index,
                                  jobject instance)
{
    JNIEnv *env = getEnv();
    InstanceFilter *filter = &FILTER(node, index).u.InstanceOnly;
    if (index >= FILTER_COUNT(node)) {
        return AGENT_ERROR_ILLEGAL_ARGUMENT;
    }

    filter->instance = NULL;
    if (instance != NULL) {
        /* Create an object ref that will live beyond
         * the end of this call
         */
        saveGlobalRef(env, instance, &(filter->instance));
    }
    FILTER(node, index).modifier =
                       JDWP_REQUEST_MODIFIER(InstanceOnly);
    return JVMTI_ERROR_NONE;
}
/**
 * Determine if the specified breakpoint node is in the
 * same location as the LocationFilter passed in arg.
 *
 * This is a match function called by a
 * eventHandlerRestricted_iterator invokation.
 */
static jboolean
matchBreakpoint(JNIEnv *env, HandlerNode *node, void *arg)
{
    LocationFilter *goal = (LocationFilter *)arg;
    Filter *filter = FILTERS_ARRAY(node);
    int i;

    for (i = 0; i < FILTER_COUNT(node); ++i, ++filter) {
        switch (filter->modifier) {
        case JDWP_REQUEST_MODIFIER(LocationOnly): {
            LocationFilter *trial = &(filter->u.LocationOnly);
            if (trial->method == goal->method &&
                trial->location == goal->location &&
                isSameObject(env, trial->clazz, goal->clazz)) {
                return JNI_TRUE;
            }
        }
        }
    }
    return JNI_FALSE;
}
jvmtiError
eventFilter_setFieldOnlyFilter(HandlerNode *node, jint index,
                               jclass clazz, jfieldID field)
{
    JNIEnv *env = getEnv();
    FieldFilter *filter = &FILTER(node, index).u.FieldOnly;
    if (index >= FILTER_COUNT(node)) {
        return AGENT_ERROR_ILLEGAL_ARGUMENT;
    }
    if ((NODE_EI(node) != EI_FIELD_ACCESS) &&
        (NODE_EI(node) != EI_FIELD_MODIFICATION)) {

        return AGENT_ERROR_ILLEGAL_ARGUMENT;
    }

    /* Create a class ref that will live beyond */
    /* the end of this call */
    saveGlobalRef(env, clazz, &(filter->clazz));
    FILTER(node, index).modifier = JDWP_REQUEST_MODIFIER(FieldOnly);
    filter->field = field;
    return JVMTI_ERROR_NONE;
}
jvmtiError
eventFilter_setClassOnlyFilter(HandlerNode *node, jint index,
                               jclass clazz)
{
    JNIEnv *env = getEnv();
    ClassFilter *filter = &FILTER(node, index).u.ClassOnly;
    if (index >= FILTER_COUNT(node)) {
        return AGENT_ERROR_ILLEGAL_ARGUMENT;
    }
    if (
        (NODE_EI(node) == EI_GC_FINISH) ||
        (NODE_EI(node) == EI_THREAD_START) ||
        (NODE_EI(node) == EI_THREAD_END)) {

        return AGENT_ERROR_ILLEGAL_ARGUMENT;
    }

    /* Create a class ref that will live beyond */
    /* the end of this call */
    saveGlobalRef(env, clazz, &(filter->clazz));
    FILTER(node, index).modifier = JDWP_REQUEST_MODIFIER(ClassOnly);
    return JVMTI_ERROR_NONE;
}
jint 
eventFilter_setThreadOnlyFilter(HandlerNode *node, jint index,
                                jthread thread)
{
    JNIEnv *env = getEnv();
    ThreadFilter *filter = &FILTER(node, index).u.ThreadOnly;
    if (index >= FILTER_COUNT(node)) {
        return JVMDI_ERROR_ILLEGAL_ARGUMENT;
    }
    if (KIND(node) == JVMDI_EVENT_CLASS_UNLOAD) {
        return JVMDI_ERROR_ILLEGAL_ARGUMENT;
    } 
    
    /* Create a thread ref that will live beyond */
    /* the end of this call */
    thread = (*env)->NewGlobalRef(env, thread);
    if (thread == NULL) {
        return JVMDI_ERROR_OUT_OF_MEMORY;
    }
    FILTER(node, index).modifier = JDWP_REQUEST_MODIFIER(ThreadOnly);
    filter->thread = thread;
    return JVMDI_ERROR_NONE;
}
/**
 * Clear a breakpoint if this is the last one at this location.
 */
static jint
clearBreakpoint(HandlerNode *node)
{
    jint error = JVMDI_ERROR_NONE;
    Filter *filter;

    filter = findFilter(node, JDWP_REQUEST_MODIFIER(LocationOnly));
    if (filter == NULL) {
        /* bp event with no location filter */
        error = JVMDI_ERROR_INTERNAL; 
    } else {
        LocationFilter *lf = &(filter->u.LocationOnly);

        /* if this is the last handler for this 
         * location, clear bp at jvmdi level 
         */
        if (!eventHandlerRestricted_iterator(
                JVMDI_EVENT_BREAKPOINT, matchBreakpoint, lf)) {
            error = jvmdi->ClearBreakpoint(lf->clazz, lf->method, 
                                           lf->location);
        }
    }
    return error;
}
jint 
eventFilter_setInstanceOnlyFilter(HandlerNode *node, jint index, 
                                  jobject instance)
{
    JNIEnv *env = getEnv();
    InstanceFilter *filter = &FILTER(node, index).u.InstanceOnly;
    if (index >= FILTER_COUNT(node)) {
        return JVMDI_ERROR_ILLEGAL_ARGUMENT;
    }

    if (instance != NULL) {
        /* Create an object ref that will live beyond 
         * the end of this call
         */
        instance = (*env)->NewGlobalRef(env, instance);
        if (instance == NULL) {
            return JVMDI_ERROR_OUT_OF_MEMORY;
        }
    }
    FILTER(node, index).modifier =
                       JDWP_REQUEST_MODIFIER(InstanceOnly);
    filter->instance = instance;
    return JVMDI_ERROR_NONE;
}
/**
 * This function returns true only if it is certain that
 * all events for the given node in the given frame will
 * be filtered. It is used to optimize stepping. (If this
 * function returns true the stepping algorithm does not
 * have to step through every instruction in this frame;
 * instead, it can use more efficient method entry/exit
 * events.
 */
jboolean
eventFilter_predictFiltering(HandlerNode *node, jframeID frame)
{
    Filter *filter = FILTERS_ARRAY(node);
    int i;
    jclass clazz;
    jmethodID method;
    jlocation location;
    jboolean willBeFiltered = JNI_FALSE;
    jboolean done = JNI_FALSE;
    JNIEnv *env = getEnv();
    jint error;

    error = jvmdi->GetFrameLocation(frame, &clazz,
                                    &method, &location);
    if (error != JVMDI_ERROR_NONE) {
        return JNI_FALSE;
    }

    for (i = 0; (i < FILTER_COUNT(node)) && (!done); ++i, ++filter) {
        switch (filter->modifier) {
            case JDWP_REQUEST_MODIFIER(ClassOnly):
                if (!(*env)->IsAssignableFrom(env, clazz,
                                 filter->u.ClassOnly.clazz)) {
                    willBeFiltered = JNI_TRUE;
                    done = JNI_TRUE;
                }
                break;
        
            case JDWP_REQUEST_MODIFIER(Count): {   
                /*
                 * If preceeding filters have determined that events will
                 * be filtered out, that is fine and we won't get here.
                 * However, the count must be decremented - even if
                 * subsequent filters will filter these events.  We
                 * thus must end now unable to predict
                 */
                done = JNI_TRUE;
                break;
            }
                
            case JDWP_REQUEST_MODIFIER(ClassMatch): {              
                if (!patternMatch(clazz,
                        filter->u.ClassMatch.classPattern)) {
                    willBeFiltered = JNI_TRUE;
                    done = JNI_TRUE;
                }
                break;
            }
    
            case JDWP_REQUEST_MODIFIER(ClassExclude): {
                if (patternMatch(clazz,
                       filter->u.ClassExclude.classPattern)) {
                    willBeFiltered = JNI_TRUE;
                    done = JNI_TRUE;
                }
                break;
            }
        }
    }
    (*env)->DeleteGlobalRef(env, clazz);
    return willBeFiltered;
}