/******************************************************************************* * Copyright (c) 2000, 2014 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jdt.core.tests.eval; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Vector; import junit.framework.TestCase; import org.eclipse.jdt.core.tests.runtime.RuntimeConstants; import org.eclipse.jdt.internal.eval.EvaluationConstants; import org.eclipse.jdt.internal.eval.EvaluationResult; import org.eclipse.jdt.internal.eval.InstallException; import com.sun.jdi.AbsentInformationException; import com.sun.jdi.ClassNotLoadedException; import com.sun.jdi.ClassType; import com.sun.jdi.Field; import com.sun.jdi.IncompatibleThreadStateException; import com.sun.jdi.InvalidTypeException; import com.sun.jdi.InvocationException; import com.sun.jdi.LocalVariable; import com.sun.jdi.Location; import com.sun.jdi.Method; import com.sun.jdi.ObjectReference; import com.sun.jdi.ReferenceType; import com.sun.jdi.StackFrame; import com.sun.jdi.ThreadReference; import com.sun.jdi.Value; import com.sun.jdi.VirtualMachine; import com.sun.jdi.event.BreakpointEvent; import com.sun.jdi.event.Event; import com.sun.jdi.event.EventSet; import com.sun.jdi.request.BreakpointRequest; import com.sun.jdi.request.EventRequest; @SuppressWarnings({ "unchecked", "rawtypes" }) public class JDIStackFrame implements EvaluationConstants, RuntimeConstants { VirtualMachine jdiVM; ThreadReference jdiThread; String userCode; String breakpointClassName; String breakpointMethodName; int breakpointLine; long timeout; public JDIStackFrame(VirtualMachine jdiVM, DebugEvaluationTest test, String userCode) { this(jdiVM, test, userCode, "_JDIStackFrame_", "foo", Integer.MAX_VALUE); } public JDIStackFrame( VirtualMachine jdiVM, DebugEvaluationTest test, String userCode, String breakpointClassName, String breakpointMethodName, int breakpointLine) { this(jdiVM, test, userCode, breakpointClassName, breakpointMethodName, breakpointLine, 10000/*timeout*/); } public JDIStackFrame( VirtualMachine jdiVM, DebugEvaluationTest test, String userCode, String breakpointClassName, String breakpointMethodName, int breakpointLine, long timeout) { this.jdiVM = jdiVM; this.userCode = userCode; this.breakpointClassName = breakpointClassName; this.breakpointMethodName = breakpointMethodName; this.breakpointLine = breakpointLine; this.timeout = timeout; test.jdiStackFrame = null; this.jdiThread = getDebuggedThread(test); test.jdiStackFrame = this; } public char[] declaringTypeName() { if (this.breakpointLine != Integer.MAX_VALUE) { // if not in a code snippet StackFrame frame = getStackFrame(); return frame.location().declaringType().name().toCharArray(); } return null; } protected ThreadReference getDebuggedThread(DebugEvaluationTest test) { try { // desintall previous breakpoints this.jdiVM.eventRequestManager().deleteAllBreakpoints(); // install a breakpoint at the breakpointLine List classes = this.jdiVM.classesByName(this.breakpointClassName); if (classes.size() == 0) { if (this.breakpointClassName.equals("_JDIStackFrame_")) { // install special class String source = "public class _JDIStackFrame_ {\n" + " public int foo() {\n" + " return -1;\n" + " }\n" + "}"; test.compileAndDeploy(source, "_JDIStackFrame_"); } // force load of class test.evaluateWithExpectedDisplayString( ("return Class.forName(\"" + this.breakpointClassName + "\");").toCharArray(), ("class " + this.breakpointClassName).toCharArray() ); classes = this.jdiVM.classesByName(this.breakpointClassName); if (classes.size() == 0) { // workaround bug in Standard VM Iterator iterator = this.jdiVM.allClasses().iterator(); while (iterator.hasNext()) { ReferenceType type = (ReferenceType)iterator.next(); if (type.name().equals(this.breakpointClassName)) { classes = new ArrayList(1); classes.add(type); break; } } if (classes.size() == 0) { throw new Error("JDI could not retrieve class for " + this.breakpointClassName); } } } ClassType clazz = (ClassType)classes.get(0); Method method = clazz.methodsByName(this.breakpointMethodName).get(0); Location location; if (this.breakpointLine < 0 || this.breakpointLine == Integer.MAX_VALUE) { location = method.location(); } else { location = method.locationsOfLine(this.breakpointLine).get(0); } BreakpointRequest request = this.jdiVM.eventRequestManager().createBreakpointRequest(location); request.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD); request.enable(); // create a new thread that hit the breakpoint EvaluationTest.Requestor requestor = test.new Requestor(); try { test.resetEnv(); test.context.evaluate( ("(new Thread() {\n" + " public void run() {\n" + this.userCode + "\n" + (this.breakpointClassName.equals("_JDIStackFrame_") ? " new _JDIStackFrame_().foo();\n" : "") + " }\n" + " public String toString() {\n" + " return \"my thread\";\n" + " }\n" + "}).start();\n").toCharArray(), test.getEnv(), test.getCompilerOptions(), requestor, test.getProblemFactory()); } catch (InstallException e) { TestCase.assertTrue("Target exception " + e.getMessage(), false); } EvaluationResult[] results = requestor.results; for (int i = 0; i < requestor.resultIndex + 1; i++){ if (results[i].hasErrors()) { TestCase.assertTrue("Compilation error in user code", false); } } // Wait for the breakpoint event Event event = null; do { EventSet set = this.jdiVM.eventQueue().remove(); Iterator iterator = set.eventIterator(); while (iterator.hasNext()) { event = (Event)iterator.next(); if (event instanceof BreakpointEvent) break; } } while (!(event instanceof BreakpointEvent)); // Return the suspended thread return ((BreakpointEvent)event).thread(); } catch (AbsentInformationException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } return null; } protected StackFrame getStackFrame() { try { if (this.breakpointLine == Integer.MAX_VALUE) { return this.jdiThread.frame(1); } return this.jdiThread.frame(0); } catch (IncompatibleThreadStateException e) { e.printStackTrace(); return null; } } public boolean isConstructorCall() { if (this.breakpointLine != Integer.MAX_VALUE) { // if not in a code snippet StackFrame frame = getStackFrame(); return frame.location().method().isConstructor(); } return false; } public boolean isStatic() { if (this.breakpointLine != Integer.MAX_VALUE) { // if not in a code snippet StackFrame frame = getStackFrame(); return frame.location().method().isStatic(); } return false; } public int[] localVariableModifiers() { try { StackFrame frame = getStackFrame(); List variables = frame.visibleVariables(); int[] modifiers = new int[variables.size()]; /* Iterator iterator = variables.iterator(); int i = 0; while (iterator.hasNext()) { LocalVariable var = (LocalVariable)iterator.next(); // TBD modifiers[i++] = var. ??? ; } */ return modifiers; } catch (AbsentInformationException e) { return null; } } public char[][] localVariableNames() { try { StackFrame frame = getStackFrame(); Iterator variables = frame.visibleVariables().iterator(); Vector names = new Vector(); while (variables.hasNext()) { LocalVariable var = (LocalVariable)variables.next(); names.addElement(var.name().toCharArray()); } char[][] result = new char[names.size()][]; names.copyInto(result); return result; } catch (AbsentInformationException e) { return null; } } public char[][] localVariableTypeNames() { try { StackFrame frame = getStackFrame(); Iterator variables = frame.visibleVariables().iterator(); Vector names = new Vector(); while (variables.hasNext()) { LocalVariable var = (LocalVariable)variables.next(); names.addElement(var.typeName().toCharArray()); } char[][] result = new char[names.size()][]; names.copyInto(result); return result; } catch (AbsentInformationException e) { return null; } } public boolean run(String codeSnippetClassName) { ClassType codeSnippetClass; ObjectReference codeSnippet; Method method; List arguments; ObjectReference codeSnippetRunner; try { // Get the code snippet class long start = System.currentTimeMillis(); List classes = this.jdiVM.classesByName(codeSnippetClassName); while (classes.size() == 0) { try { Thread.sleep(100); } catch (InterruptedException e) { } classes = this.jdiVM.classesByName(codeSnippetClassName); if (classes.size() == 0) { // workaround bug in Standard VM Iterator iterator = this.jdiVM.allClasses().iterator(); while (iterator.hasNext()) { ReferenceType type = (ReferenceType)iterator.next(); if (type.name().equals(codeSnippetClassName)) { classes = new ArrayList(1); classes.add(type); break; } } if (classes.size() == 0 && (System.currentTimeMillis()-start) > this.timeout) { return false; } } } codeSnippetClass = (ClassType)classes.get(0); // Create a new code snippet Method constructor = codeSnippetClass.methodsByName("<init>").get(0); codeSnippet = codeSnippetClass.newInstance(this.jdiThread, constructor, new ArrayList(), ClassType.INVOKE_SINGLE_THREADED); // Install local variables and "this" into generated fields StackFrame stackFrame = getStackFrame(); try { Iterator variables = stackFrame.visibleVariables().iterator(); while (variables.hasNext()) { LocalVariable jdiVariable = (LocalVariable)variables.next(); Value value = stackFrame.getValue(jdiVariable); Field field = codeSnippetClass.fieldByName(new String(LOCAL_VAR_PREFIX) + jdiVariable.name()); codeSnippet.setValue(field, value); } } catch (AbsentInformationException e) { // No variables } Field delegateThis = codeSnippetClass.fieldByName(new String(DELEGATE_THIS)); ObjectReference thisObject; if (delegateThis != null && ((thisObject = stackFrame.thisObject()) != null)) { codeSnippet.setValue(delegateThis, thisObject); } // Get the code snippet runner ClassType codeSnippetRunnerClass = (ClassType)this.jdiVM.classesByName(CODE_SNIPPET_RUNNER_CLASS_NAME).get(0); Field theRunner = codeSnippetRunnerClass.fieldByName(THE_RUNNER_FIELD); codeSnippetRunner = (ObjectReference)codeSnippetRunnerClass.getValue(theRunner); // Get the method 'runCodeSnippet' and its arguments method = codeSnippetRunnerClass.methodsByName(RUN_CODE_SNIPPET_METHOD).get(0); arguments = new ArrayList(); arguments.add(codeSnippet); } catch (ClassNotLoadedException e) { e.printStackTrace(); return false; } catch (IncompatibleThreadStateException e) { e.printStackTrace(); return false; } catch (InvalidTypeException e) { e.printStackTrace(); return false; } catch (InvocationException e) { e.printStackTrace(); return false; } try { // Invoke runCodeSnippet(CodeSnippet) codeSnippetRunner.invokeMethod(this.jdiThread, method, arguments, ClassType.INVOKE_SINGLE_THREADED); // Retrieve values of local variables and put them back in the stack frame StackFrame stackFrame = getStackFrame(); try { Iterator variables = stackFrame.visibleVariables().iterator(); while (variables.hasNext()) { LocalVariable jdiVariable = (LocalVariable)variables.next(); Field field = codeSnippetClass.fieldByName(new String(LOCAL_VAR_PREFIX) + jdiVariable.name()); Value value = codeSnippet.getValue(field); stackFrame.setValue(jdiVariable, value); } } catch (AbsentInformationException e) { // No variables } } catch (ClassNotLoadedException e) { e.printStackTrace(); } catch (IncompatibleThreadStateException e) { e.printStackTrace(); } catch (InvalidTypeException e) { e.printStackTrace(); } catch (InvocationException e) { e.printStackTrace(); } return true; } }