JDWP: test synthetic fields and methods

Tests the synthetic modifier is properly returned for field and
method commands of ReferenceType command set.

Bug: 22195410
Test: art/tools/run-jdwp-tests.sh --mode=host --variant=X64
Change-Id: If11229f0439cad0c308ef8b90c206ecfd41498d3
diff --git a/jdwp/src/test/java/org/apache/harmony/jpda/tests/jdwp/ReferenceType/SyntheticFieldsTest.java b/jdwp/src/test/java/org/apache/harmony/jpda/tests/jdwp/ReferenceType/SyntheticFieldsTest.java
new file mode 100644
index 0000000..b8c076b
--- /dev/null
+++ b/jdwp/src/test/java/org/apache/harmony/jpda/tests/jdwp/ReferenceType/SyntheticFieldsTest.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.harmony.jpda.tests.jdwp.ReferenceType;
+
+import org.apache.harmony.jpda.tests.framework.jdwp.CommandPacket;
+import org.apache.harmony.jpda.tests.framework.jdwp.JDWPCommands;
+import org.apache.harmony.jpda.tests.framework.jdwp.ReplyPacket;
+import org.apache.harmony.jpda.tests.jdwp.share.JDWPSyncTestCase;
+import org.apache.harmony.jpda.tests.jdwp.share.debuggee.SyntheticMembersDebuggee;
+import org.apache.harmony.jpda.tests.share.JPDADebuggeeSynchronizer;
+
+/**
+ * JDWP Unit test for ReferenceType.Fields and ReferenceType.FieldsWithGeneric commands.
+ */
+public class SyntheticFieldsTest extends JDWPSyncTestCase {
+
+    private static final int FIELD_SYNTHETIC_FLAG = 0xf0000000;
+
+    @Override
+    protected String getDebuggeeClassName() {
+        return SyntheticMembersDebuggee.class.getName();
+    }
+
+    /**
+     * This testcase exercises ReferenceType.Fields command.
+     *
+     * The test starts SyntheticMembersDebuggee class and verify that its non-static inner
+     * class has at least one synthetic field (the outer this).
+     */
+    public void testSyntheticFields() {
+        runTestSyntheticFields(false);
+    }
+
+    /**
+     * This testcase exercises ReferenceType.FieldsWithGeneric command.
+     *
+     * The test starts SyntheticMembersDebuggee class and verify that its non-static inner
+     * class has at least one synthetic field (the outer this).
+     */
+    public void testSyntheticFieldsWithGeneric() {
+        runTestSyntheticFields(true);
+    }
+
+    private void runTestSyntheticFields(boolean withGeneric) {
+        final String thisTestName = getName();
+        final byte commandCode;
+        final String commandName;
+        if (withGeneric) {
+            commandCode = JDWPCommands.ReferenceTypeCommandSet.FieldsWithGenericCommand;
+            commandName = "ReferenceType.FieldsWithGeneric command";
+        } else {
+            commandCode = JDWPCommands.ReferenceTypeCommandSet.FieldsCommand;
+            commandName = "ReferenceType.Fields command";
+        }
+        logWriter.println("==> " + thisTestName + " for " + commandName + ": START...");
+        synchronizer.receiveMessage(JPDADebuggeeSynchronizer.SGNL_READY);
+
+        String signature = getClassSignature(SyntheticMembersDebuggee.InnerClass.class);
+        long refTypeID = getClassIDBySignature(signature);
+
+        logWriter.println("=> Debuggee class = " + getDebuggeeClassName());
+        logWriter.println("=> referenceTypeID for inner class = " + refTypeID);
+        logWriter.println("=> CHECK: send " + commandName + " and check reply...");
+        CommandPacket fieldsCommand = new CommandPacket(
+                JDWPCommands.ReferenceTypeCommandSet.CommandSetID, commandCode);
+        fieldsCommand.setNextValueAsReferenceTypeID(refTypeID);
+
+        ReplyPacket fieldsReply = debuggeeWrapper.vmMirror.performCommand(fieldsCommand);
+        checkReplyPacket(fieldsReply, commandName);
+
+        int returnedFieldsNumber = fieldsReply.getNextValueAsInt();
+        logWriter.println("=> Returned fields number = " + returnedFieldsNumber);
+
+        boolean foundSyntheticField = false;
+        for (int i = 0; i < returnedFieldsNumber; ++i) {
+            long fieldID = fieldsReply.getNextValueAsFieldID();
+            String fieldName = fieldsReply.getNextValueAsString();
+            String fieldSignature = fieldsReply.getNextValueAsString();
+            logWriter.println("\n=> Field ID = " + fieldID);
+            logWriter.println("=> Field name = " + fieldName);
+            logWriter.println("=> Field signature = " + fieldSignature);
+            if (withGeneric) {
+                String fieldGenericSignature = fieldsReply.getNextValueAsString();
+                logWriter.println("=> Field generic signature = " + fieldGenericSignature);
+            }
+            int fieldModifiers = fieldsReply.getNextValueAsInt();
+            logWriter.println("=> Field modifiers = 0x" + Integer.toHexString(fieldModifiers));
+            if ((fieldModifiers & FIELD_SYNTHETIC_FLAG) == FIELD_SYNTHETIC_FLAG) {
+                // We found a synthetic field.
+                foundSyntheticField = true;
+            }
+        }
+        assertAllDataRead(fieldsReply);
+
+        assertTrue("Did not find any synthetic field", foundSyntheticField);
+
+        synchronizer.sendMessage(JPDADebuggeeSynchronizer.SGNL_CONTINUE);
+        logWriter.println("==> " + thisTestName + " for " + commandName + ": FINISH");
+    }
+}
diff --git a/jdwp/src/test/java/org/apache/harmony/jpda/tests/jdwp/ReferenceType/SyntheticMethodsTest.java b/jdwp/src/test/java/org/apache/harmony/jpda/tests/jdwp/ReferenceType/SyntheticMethodsTest.java
new file mode 100644
index 0000000..158b072
--- /dev/null
+++ b/jdwp/src/test/java/org/apache/harmony/jpda/tests/jdwp/ReferenceType/SyntheticMethodsTest.java
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.harmony.jpda.tests.jdwp.ReferenceType;
+
+import org.apache.harmony.jpda.tests.framework.jdwp.CommandPacket;
+import org.apache.harmony.jpda.tests.framework.jdwp.JDWPCommands;
+import org.apache.harmony.jpda.tests.framework.jdwp.ReplyPacket;
+import org.apache.harmony.jpda.tests.jdwp.share.JDWPSyncTestCase;
+import org.apache.harmony.jpda.tests.jdwp.share.debuggee.SyntheticMembersDebuggee;
+import org.apache.harmony.jpda.tests.share.JPDADebuggeeSynchronizer;
+
+/**
+ * JDWP Unit test for ReferenceType.Methods and ReferenceType.MethodsWithGeneric commands.
+ */
+public class SyntheticMethodsTest extends JDWPSyncTestCase {
+
+    private static final int METHOD_SYNTHETIC_FLAG = 0xf0000000;
+
+    @Override
+    protected String getDebuggeeClassName() {
+        return SyntheticMembersDebuggee.class.getName();
+    }
+
+    /**
+     * This testcase exercises ReferenceType.Methods command.
+     *
+     * The test starts SyntheticMembersDebuggee class and verify that its non-static inner
+     * class has at least one synthetic method (an access method).
+     */
+    public void testSyntheticMethods() {
+        runTestSyntheticMethods(false);
+    }
+
+    /**
+     * This testcase exercises ReferenceType.MethodsWithGeneric command.
+     *
+     * The test starts SyntheticMembersDebuggee class and verify that its non-static inner
+     * class has at least one synthetic method (an access method).
+     */
+    public void testSyntheticMethodsWithGeneric() {
+        runTestSyntheticMethods(true);
+    }
+
+    private void runTestSyntheticMethods(boolean withGeneric) {
+        String thisTestName = getName();
+        final byte commandCode;
+        final String commandName;
+        if (withGeneric) {
+            commandCode = JDWPCommands.ReferenceTypeCommandSet.MethodsWithGenericCommand;
+            commandName = "ReferenceType.MethodsWithGeneric command";
+        } else {
+            commandCode = JDWPCommands.ReferenceTypeCommandSet.MethodsCommand;
+            commandName = "ReferenceType.Methods command";
+        }
+        logWriter.println("==> " + thisTestName + " for " + commandName + ": START...");
+        synchronizer.receiveMessage(JPDADebuggeeSynchronizer.SGNL_READY);
+
+        String signature = getDebuggeeClassSignature();
+        long refTypeID = getClassIDBySignature(signature);
+
+        logWriter.println("=> Debuggee class = " + getDebuggeeClassName());
+        logWriter.println("=> referenceTypeID for debuggee class = " + refTypeID);
+        logWriter.println("=> CHECK: send " + commandName + " and check reply...");
+
+        CommandPacket methodsCommand = new CommandPacket(
+                JDWPCommands.ReferenceTypeCommandSet.CommandSetID, commandCode);
+        methodsCommand.setNextValueAsReferenceTypeID(refTypeID);
+        ReplyPacket methodsReply = debuggeeWrapper.vmMirror.performCommand(methodsCommand);
+        checkReplyPacket(methodsReply, commandName);
+
+        int returnedMethodsNumber = methodsReply.getNextValueAsInt();
+        logWriter.println("=> Returned methods number = " + returnedMethodsNumber);
+
+        logWriter.println("=> CHECK for all expected methods...");
+        boolean foundSyntheticMethod = false;
+        for (int i = 0; i < returnedMethodsNumber; i++) {
+            long methodID = methodsReply.getNextValueAsMethodID();
+            String methodName = methodsReply.getNextValueAsString();
+            String methodSignature = methodsReply.getNextValueAsString();
+            logWriter.println("\n=> Method ID = " + methodID);
+            logWriter.println("=> Method name = " + methodName);
+            logWriter.println("=> Method signature = " + methodSignature);
+            if (withGeneric) {
+                String methodGenericSignature = methodsReply.getNextValueAsString();
+                logWriter.println("=> Method generic signature = " + methodGenericSignature);
+            }
+            int methodModifiers = methodsReply.getNextValueAsInt();
+            logWriter.println("=> Method modifiers = 0x" + Integer.toHexString(methodModifiers));
+            if ((methodModifiers & METHOD_SYNTHETIC_FLAG) == METHOD_SYNTHETIC_FLAG) {
+                // We found a synthetic method.
+                foundSyntheticMethod = true;
+            }
+        }
+        assertAllDataRead(methodsReply);
+
+        assertTrue("Did not find any synthetic method", foundSyntheticMethod);
+
+        synchronizer.sendMessage(JPDADebuggeeSynchronizer.SGNL_CONTINUE);
+        logWriter.println("==> " + thisTestName + " for " + commandName + ": FINISH");
+    }
+}
diff --git a/jdwp/src/test/java/org/apache/harmony/jpda/tests/jdwp/share/debuggee/SyntheticMembersDebuggee.java b/jdwp/src/test/java/org/apache/harmony/jpda/tests/jdwp/share/debuggee/SyntheticMembersDebuggee.java
new file mode 100644
index 0000000..27249c6
--- /dev/null
+++ b/jdwp/src/test/java/org/apache/harmony/jpda/tests/jdwp/share/debuggee/SyntheticMembersDebuggee.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.harmony.jpda.tests.jdwp.share.debuggee;
+
+import org.apache.harmony.jpda.tests.share.JPDADebuggeeSynchronizer;
+import org.apache.harmony.jpda.tests.share.SyncDebuggee;
+
+public class SyntheticMembersDebuggee extends SyncDebuggee {
+
+    // Accessing this method will force the compiler to create a synthetic bridge method.
+    private void outerMethod() {
+    }
+
+    // A non-static inner class should have a reference to its "outer this" in a synthetic field.
+    public class InnerClass {
+        @SuppressWarnings("unused")
+        private void callOuterMethod() {
+            outerMethod();
+        }
+    }
+
+    @Override
+    public void run() {
+        logWriter.println("--> SyntheticMembersDebuggee START");
+
+        // Force class loading to ensure the class is visible through JDWP.
+        new InnerClass();
+
+        synchronizer.sendMessage(JPDADebuggeeSynchronizer.SGNL_READY);
+        logWriter.println("--> Debuggee: SyntheticMembersDebuggee...");
+        synchronizer.receiveMessage(JPDADebuggeeSynchronizer.SGNL_CONTINUE);
+
+        logWriter.println("--> SyntheticMembersDebuggee END");
+    }
+
+    public static void main(String [] args) {
+        runDebuggee(SyntheticMembersDebuggee.class);
+    }
+
+}
\ No newline at end of file
diff --git a/jdwp/src/test/java/org/apache/harmony/jpda/tests/share/AllTests.java b/jdwp/src/test/java/org/apache/harmony/jpda/tests/share/AllTests.java
index 44e4211..b119855 100644
--- a/jdwp/src/test/java/org/apache/harmony/jpda/tests/share/AllTests.java
+++ b/jdwp/src/test/java/org/apache/harmony/jpda/tests/share/AllTests.java
@@ -177,6 +177,8 @@
     suite.addTestSuite(org.apache.harmony.jpda.tests.jdwp.ReferenceType.SourceDebugExtensionTest.class);
     suite.addTestSuite(org.apache.harmony.jpda.tests.jdwp.ReferenceType.SourceFileTest.class);
     suite.addTestSuite(org.apache.harmony.jpda.tests.jdwp.ReferenceType.StatusTest.class);
+    suite.addTestSuite(org.apache.harmony.jpda.tests.jdwp.ReferenceType.SyntheticFieldsTest.class);
+    suite.addTestSuite(org.apache.harmony.jpda.tests.jdwp.ReferenceType.SyntheticMethodsTest.class);
     suite.addTestSuite(org.apache.harmony.jpda.tests.jdwp.StackFrame.GetValues002Test.class);
     suite.addTestSuite(org.apache.harmony.jpda.tests.jdwp.StackFrame.GetValuesTest.class);
     suite.addTestSuite(org.apache.harmony.jpda.tests.jdwp.StackFrame.PopFrames002Test.class);