/*
* Copyright 2010 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.drools.eclipse.debug.core;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.drools.eclipse.DroolsEclipsePlugin;
import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.model.IBreakpoint;
import org.eclipse.jdt.debug.core.IJavaStackFrame;
import org.eclipse.jdt.internal.debug.core.model.JDIDebugModelMessages;
import org.eclipse.jdt.internal.debug.core.model.JDIDebugTarget;
import org.eclipse.jdt.internal.debug.core.model.JDIStackFrame;
import org.eclipse.jdt.internal.debug.core.model.JDIThread;
import org.mvel2.debug.Debugger;
import com.sun.jdi.ClassType;
import com.sun.jdi.IntegerValue;
import com.sun.jdi.Location;
import com.sun.jdi.Method;
import com.sun.jdi.ObjectCollectedException;
import com.sun.jdi.ObjectReference;
import com.sun.jdi.ReferenceType;
import com.sun.jdi.StackFrame;
import com.sun.jdi.ThreadReference;
/**
* Drools Thread supporting MVEL and Java dialect stackframes
*/
public class DroolsThread extends JDIThread {
public DroolsThread(JDIDebugTarget target,
ThreadReference thread) throws ObjectCollectedException {
super( target,
thread );
}
protected synchronized List<IJavaStackFrame> computeStackFrames(boolean refreshChildren) throws DebugException {
List<IJavaStackFrame> fStackFrames = getInternalfStackFrames();
if ( isSuspended() ) {
if ( isTerminated() ) {
fStackFrames.clear();
} else if ( refreshChildren ) {
List<StackFrame> frames = getInternalUnderlyingFrames();
int oldSize = fStackFrames.size();
int newSize = frames.size();
int discard = oldSize - newSize; // number of old frames to discard, if any
for ( int i = 0; i < discard; i++ ) {
DroolsStackFrame invalid = (DroolsStackFrame) fStackFrames.remove( 0 );
invalid.bind( null,
-1 );
}
int newFrames = newSize - oldSize; // number of frames to create, if any
int depth = oldSize;
for ( int i = newFrames - 1; i >= 0; i-- ) {
StackFrame currentFrame = (StackFrame) frames.get( i );
JDIStackFrame customFrame = createCustomFrame( this,
depth,
currentFrame );
fStackFrames.add( 0,
customFrame );
depth++;
}
int numToRebind = Math.min( newSize,
oldSize ); // number of frames to attempt to rebind
int offset = newSize - 1;
for ( depth = 0; depth < numToRebind; depth++ ) {
DroolsStackFrame oldFrame = (DroolsStackFrame) fStackFrames.get( offset );
StackFrame frame = (StackFrame) frames.get( offset );
DroolsStackFrame newFrame = (DroolsStackFrame) oldFrame.bind( frame,
depth );
if ( newFrame != oldFrame ) {
fStackFrames.set( offset,
newFrame );
}
offset--;
}
}
setInternalfRefreshChildren( false );
} else {
return Collections.emptyList();
}
return fStackFrames;
}
public final static synchronized DroolsStackFrame createCustomFrame(DroolsThread thread,
int depth,
StackFrame currentFrame) {
DroolsStackFrame customFrame;
Location loc = currentFrame.location();
if ( loc.declaringType().name().equals( "org.drools.core.base.mvel.MVELDebugHandler" ) && loc.method().name().equals( "onBreak" ) ) {
customFrame = new MVELStackFrame( thread,
currentFrame,
depth );
} else {
customFrame = new DroolsStackFrame( thread,
currentFrame,
depth );
}
return customFrame;
}
// I don't see the need for any of this custom stepOver stuff, why is it here?
public synchronized void stepOver() throws DebugException {
// Detection for active stackframe
if ( !(getTopStackFrame() instanceof MVELStackFrame) ) {
super.stepOver();
return;
}
//MVEL step over
MVELStackFrame mvelStack = (MVELStackFrame) getTopStackFrame();
if ( !canStepOver() || !mvelStack.canStepOver() ) {
return;
}
if ( !setRemoteOnBreakReturn( Debugger.STEP ) ) {
return;
}
setRunning( true );
preserveStackFrames();
fireEvent( new DebugEvent( this,
DebugEvent.RESUME,
DebugEvent.STEP_OVER ) );
try {
getUnderlyingThread().resume();
} catch ( RuntimeException e ) {
//stepEnd();
targetRequestFailed(MessageFormat.format(JDIDebugModelMessages.JDIThread_exception_stepping, e.toString()), e);
}
}
private boolean setRemoteOnBreakReturn(int step_over) throws DebugException {
JDIStackFrame top = (JDIStackFrame) getTopStackFrame();
if ( top == null || (!(top instanceof MVELStackFrame)) ) {
return false;
}
Iterator<ReferenceType> handleriter = getVM().classesByName( "org.drools.core.base.mvel.MVELDebugHandler" ).iterator();
Object debugHandlerClass = handleriter.next();
int line = step_over;
ReferenceType refType = (ReferenceType) debugHandlerClass;
Method m = (Method) refType.methodsByName( "setOnBreakReturn" ).iterator().next();
List<IntegerValue> args = new ArrayList<IntegerValue>();
IntegerValue lineVal = getVM().mirrorOf( line );
//ObjectReference realVal = val.getUnderlyingObject();
args.add( lineVal );
try {
ClassType tt = (ClassType) debugHandlerClass;
tt.invokeMethod( getUnderlyingThread(),
m,
args,
ObjectReference.INVOKE_SINGLE_THREADED );
} catch ( Exception e ) {
DroolsEclipsePlugin.log( e );
return false;
}
return true;
}
public synchronized void resume() throws DebugException {
// clear up the step over flag. step over button never calls this method.
setRemoteOnBreakReturn( Debugger.CONTINUE );
super.resume();
}
protected synchronized void disposeStackFrames() {
super.disposeStackFrames();
}
protected void terminated() {
super.terminated();
}
protected void removeCurrentBreakpoint(IBreakpoint bp) {
super.removeCurrentBreakpoint( bp );
}
protected synchronized void suspendedByVM() {
super.suspendedByVM();
}
protected synchronized void resumedByVM() throws DebugException {
super.resumedByVM();
}
protected void setRunning(boolean running) {
super.setRunning( running );
}
public void setInternalfRefreshChildren(boolean bool) {
try {
java.lang.reflect.Field field = JDIThread.class.getDeclaredField( "fRefreshChildren" );
field.setAccessible( true );
field.set( this,
bool );
} catch ( Exception e ) {
}
}
@SuppressWarnings("unchecked")
public List<IJavaStackFrame> getInternalfStackFrames() {
try {
java.lang.reflect.Field field = JDIThread.class.getDeclaredField( "fStackFrames" );
field.setAccessible( true );
return (List<IJavaStackFrame>) field.get(this);
} catch ( Exception e ) {
return null;
}
}
@SuppressWarnings("unchecked")
private List<StackFrame> getInternalUnderlyingFrames() throws DebugException {
try {
java.lang.reflect.Method method = JDIThread.class.getDeclaredMethod("getUnderlyingFrames");
method.setAccessible( true );
return (List<StackFrame>) method.invoke(this);
} catch ( Exception e ) {
return null;
}
}
}