/**
 * 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;
}
/**
 * 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;
}
/**
 * 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;
}
/**
 * Return true if a breakpoint is set at the specified location.
 */
jboolean
isBreakpointSet(jclass clazz, jmethodID method, jlocation location)
{
    LocationFilter lf;

    lf.clazz    = clazz;
    lf.method   = method;
    lf.location = location;

    return eventHandlerRestricted_iterator(JVMDI_EVENT_BREAKPOINT,
                                           matchBreakpoint, &lf);
}
/**
 * Do any disabling of events (including clearing breakpoints etc)
 * needed to no longer get the events requested by this handler node.
 */
static jvmtiError
disableEvents(HandlerNode *node)
{
    jvmtiError error = JVMTI_ERROR_NONE;
    jvmtiError error2 = JVMTI_ERROR_NONE;
    jthread thread;


    switch (NODE_EI(node)) {
        /* The stepping code directly enables/disables stepping as
         * necessary
         */
        case EI_SINGLE_STEP:
        /* Internal thread event handlers are always present
         * (hardwired in the event hook), so we don't change the
         * notification mode here.
         */
        case EI_THREAD_START:
        case EI_THREAD_END:
        case EI_VM_INIT:
        case EI_VM_DEATH:
        case EI_CLASS_PREPARE:
        case EI_GC_FINISH:
            return error;

        case EI_FIELD_ACCESS:
        case EI_FIELD_MODIFICATION:
            error = clearWatchpoint(node);
            break;

        case EI_BREAKPOINT:
            error = clearBreakpoint(node);
            break;

        default:
            break;
    }

    thread = requestThread(node);

    /* If this is the last request of it's kind on this thread
     * (or all threads (thread == NULL)) then disable these
     * events on this thread.
     *
     * Disable even if the above caused an error
     */
    if (!eventHandlerRestricted_iterator(NODE_EI(node), matchThread, thread)) {
        error2 = threadControl_setEventMode(JVMTI_DISABLE,
                                            NODE_EI(node), thread);
    }
    return error != JVMTI_ERROR_NONE? error : error2;
}
/**
 * Do any enabling of events (including setting breakpoints etc)
 * needed to get the events requested by this handler node.
 */
static jint
enableEvents(HandlerNode *node)
{
    jint error = JVMDI_ERROR_NONE;

    switch (KIND(node)) {
        /* The stepping code directly enables/disables stepping as
         * necessary 
         */
        case JVMDI_EVENT_SINGLE_STEP:
        /* Internal thread event handlers are always present
         * (hardwired in the event hook), so we don't change the
         * notification mode here.  
         */
        case JVMDI_EVENT_THREAD_START:
        case JVMDI_EVENT_THREAD_END:
            return error;

        case JVMDI_EVENT_FIELD_ACCESS:
        case JVMDI_EVENT_FIELD_MODIFICATION:
            error = setWatchpoint(node);
            break;

        case JVMDI_EVENT_BREAKPOINT:
            error = setBreakpoint(node);
            break;
    }

    /* Don't globally enable if the above failed */
    if (error == JVMDI_ERROR_NONE) {
        jthread thread = requestThread(node);

        /* If this is the first request of it's kind on this
         * thread (or all threads (thread == NULL)) then enable
         * these events on this thread.
         */
        if (!eventHandlerRestricted_iterator(
                KIND(node), matchThread, thread)) {
            error = threadControl_setEventMode(JVMDI_ENABLE, 
                                               KIND(node), thread);
        }
    }
    return error;
}
/**
 * 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;
}