diff --git a/api.debugger.jpda/apichanges.xml b/api.debugger.jpda/apichanges.xml --- a/api.debugger.jpda/apichanges.xml +++ b/api.debugger.jpda/apichanges.xml @@ -857,6 +857,24 @@ + + + Add a way to create mirror objects in the target VM and execute static methods. + + + + + + createMirrorVar() methods are introduced in JPDADebugger class. They + create a mirror object in the target virtual machine. +

+ Similar to invokeMethod() method on ObjectVariable, invokeMethod() + is introduced on JPDAClassType for invocation of static methods. + + + + + diff --git a/api.debugger.jpda/manifest.mf b/api.debugger.jpda/manifest.mf --- a/api.debugger.jpda/manifest.mf +++ b/api.debugger.jpda/manifest.mf @@ -1,6 +1,6 @@ Manifest-Version: 1.0 OpenIDE-Module: org.netbeans.api.debugger.jpda/2 OpenIDE-Module-Localizing-Bundle: org/netbeans/api/debugger/jpda/Bundle.properties -OpenIDE-Module-Specification-Version: 2.46 +OpenIDE-Module-Specification-Version: 2.47 OpenIDE-Module-Package-Dependencies: com.sun.jdi[VirtualMachineManager] diff --git a/api.debugger.jpda/src/org/netbeans/api/debugger/jpda/JPDAClassType.java b/api.debugger.jpda/src/org/netbeans/api/debugger/jpda/JPDAClassType.java --- a/api.debugger.jpda/src/org/netbeans/api/debugger/jpda/JPDAClassType.java +++ b/api.debugger.jpda/src/org/netbeans/api/debugger/jpda/JPDAClassType.java @@ -99,6 +99,25 @@ List staticFields(); /** + * Calls given static method in debugged JVM on this class and returns + * its value. + * + * @param methodName a name of method to be called + * @param signature a signature of method to be called + * @param arguments arguments to be used + * + * @return value of given method call on this instance + * @throws NoSuchMethodException when the method does not exist + * @throws InvalidExpressionException in case of execution problems + * @since 2.47 + */ + public abstract Variable invokeMethod ( + String methodName, + String signature, + Variable[] arguments + ) throws NoSuchMethodException, InvalidExpressionException; + + /** * Retrieves the number of instances this class. * Use {@link JPDADebugger#canGetInstanceInfo} to determine if this operation is supported. * @return the number of instances. diff --git a/api.debugger.jpda/src/org/netbeans/api/debugger/jpda/JPDADebugger.java b/api.debugger.jpda/src/org/netbeans/api/debugger/jpda/JPDADebugger.java --- a/api.debugger.jpda/src/org/netbeans/api/debugger/jpda/JPDADebugger.java +++ b/api.debugger.jpda/src/org/netbeans/api/debugger/jpda/JPDADebugger.java @@ -51,6 +51,7 @@ import java.beans.PropertyChangeListener; import java.io.File; +import java.io.InvalidObjectException; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -594,6 +595,32 @@ } /** + * Create a mirror object in the target virtual machine + * + * @param obj the object to create the mirror from + * @return variable containing the mirror value + * @throws InvalidObjectException when the mirror operation fails + * @since 2.47 + */ + public Variable createMirrorVar(Object obj) throws InvalidObjectException { + return createMirrorVar(obj, false); + } + + /** + * Create a mirror object in the target virtual machine + * + * @param obj the object to create the mirror from + * @param isPrimitive when true and the object is an encapsulation + * of a primitive value, then primitive mirror is created. + * @return variable containing the mirror value + * @throws InvalidObjectException when the mirror operation fails + * @since 2.47 + */ + public Variable createMirrorVar(Object obj, boolean isPrimitive) throws InvalidObjectException { + throw new InvalidObjectException("Object "+obj+" not supported"); + } + + /** * Retrieves the number of instances of each class in the list. * Use {@link #canGetInstanceInfo} to determine if this operation is supported. * @return an array of long containing one instance counts for diff --git a/debugger.jpda/test/unit/src/org/netbeans/api/debugger/jpda/MethodInvocationTest.java b/debugger.jpda/test/unit/src/org/netbeans/api/debugger/jpda/MethodInvocationTest.java new file mode 100644 --- /dev/null +++ b/debugger.jpda/test/unit/src/org/netbeans/api/debugger/jpda/MethodInvocationTest.java @@ -0,0 +1,105 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2013 Sun Microsystems, Inc. + */ + +package org.netbeans.api.debugger.jpda; + +import com.sun.jdi.ClassType; +import com.sun.jdi.ObjectReference; +import com.sun.jdi.StringReference; +import com.sun.jdi.Value; +import java.util.List; +import java.util.Properties; +import junit.framework.Test; +import org.netbeans.api.debugger.DebuggerManager; +import org.netbeans.junit.NbTestCase; +import org.netbeans.modules.debugger.jpda.expr.JDIVariable; + +/** + * + * @author Martin + */ +public class MethodInvocationTest extends NbTestCase { + + private JPDASupport support; + private DebuggerManager dm = DebuggerManager.getDebuggerManager (); + + public MethodInvocationTest(String s) { + super(s); + } + + public static Test suite() { + return JPDASupport.createTestSuite(MethodInvocationTest.class); + } + + public void testTargetMirrors() throws Exception { + try { + Utils.BreakPositions bp = Utils.getBreakPositions(System.getProperty ("test.dir.src") + + "org/netbeans/api/debugger/jpda/testapps/MirrorValuesApp.java"); + LineBreakpoint lb = bp.getLineBreakpoints().get(0); + dm.addBreakpoint (lb); + + support = JPDASupport.attach ("org.netbeans.api.debugger.jpda.testapps.MirrorValuesApp"); + + support.waitState (JPDADebugger.STATE_STOPPED); // breakpoint hit + + JPDADebugger debugger = support.getDebugger(); + + List systemClasses = debugger.getClassesByName("java.lang.System"); + assertEquals(systemClasses.size(), 1); + JPDAClassType systemClass = systemClasses.get(0); + Properties properties = System.getProperties(); + Variable propertiesVar = systemClass.invokeMethod("getProperties", "()Ljava/util/Properties;", new Variable[]{}); + Value pv = ((JDIVariable) propertiesVar).getJDIValue(); + assertTrue("Properties "+pv, (pv instanceof ObjectReference) && + Properties.class.getName().equals(((ClassType) pv.type()).name())); + String userHomeProperty = properties.getProperty("user.home"); + Variable propVar = ((ObjectVariable) propertiesVar).invokeMethod("getProperty", + "(Ljava/lang/String;)Ljava/lang/String;", + new Variable[] { debugger.createMirrorVar("user.home") }); + Value p = ((JDIVariable) propVar).getJDIValue(); + assertTrue(p instanceof StringReference); + assertEquals(userHomeProperty, ((StringReference) p).value()); + } finally { + support.doFinish (); + } + } +} diff --git a/debugger.jpda/test/unit/src/org/netbeans/api/debugger/jpda/MirrorValuesTest.java b/debugger.jpda/test/unit/src/org/netbeans/api/debugger/jpda/MirrorValuesTest.java --- a/debugger.jpda/test/unit/src/org/netbeans/api/debugger/jpda/MirrorValuesTest.java +++ b/debugger.jpda/test/unit/src/org/netbeans/api/debugger/jpda/MirrorValuesTest.java @@ -41,6 +41,10 @@ */ package org.netbeans.api.debugger.jpda; +import com.sun.jdi.ClassType; +import com.sun.jdi.IntegerValue; +import com.sun.jdi.StringReference; +import com.sun.jdi.Value; import java.awt.Color; import java.awt.Point; import java.beans.FeatureDescriptor; @@ -58,6 +62,7 @@ import junit.framework.Test; import org.netbeans.api.debugger.DebuggerManager; import org.netbeans.junit.NbTestCase; +import org.netbeans.modules.debugger.jpda.expr.JDIVariable; import org.openide.util.Exceptions; /** @@ -223,6 +228,43 @@ } + public void testTargetMirrors() throws Exception { + try { + Utils.BreakPositions bp = Utils.getBreakPositions(System.getProperty ("test.dir.src") + + "org/netbeans/api/debugger/jpda/testapps/MirrorValuesApp.java"); + LineBreakpoint lb = bp.getLineBreakpoints().get(0); + dm.addBreakpoint (lb); + + support = JPDASupport.attach (CLASS_NAME); + + support.waitState (JPDADebugger.STATE_STOPPED); // breakpoint hit + + JPDADebugger debugger = support.getDebugger(); + + Variable mirrorVar = debugger.createMirrorVar("Test"); + Value v = ((JDIVariable) mirrorVar).getJDIValue(); + assertTrue("Value "+v+" should be a String", v instanceof StringReference); + assertEquals("Test", ((StringReference) v).value()); + + Point p = new Point(-1, 1); + mirrorVar = debugger.createMirrorVar(p); + Object mp = mirrorVar.createMirrorObject(); + assertTrue("Correct point was created: "+mp, p.equals(mp)); + + mirrorVar = debugger.createMirrorVar(1); + v = ((JDIVariable) mirrorVar).getJDIValue(); + assertTrue("Value "+v+" should be an Integer object.", + (v.type() instanceof ClassType) && Integer.class.getName().equals(((ClassType) v.type()).name())); + + mirrorVar = debugger.createMirrorVar(1, true); + v = ((JDIVariable) mirrorVar).getJDIValue(); + assertTrue("Value "+v+" should be an int.", v instanceof IntegerValue); + assertEquals(((IntegerValue) v).value(), 1); + } finally { + support.doFinish (); + } + } + private static boolean compareArrays(Object arr1, Object arr2) { if (arr1 instanceof Object[]) { return Arrays.deepEquals((Object[]) arr1, (Object[]) arr2);