1 /* 2 * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /** 25 * @ignore Fix JDK-8328461 26 * @test 27 * @summary Tests that all FieldAccess and FieldModification notifications 28 are generated for value classes. 29 * @requires vm.jvmti 30 * @enablePreview 31 * @compile FieldAccessModify.java 32 * @run main/othervm/native -agentlib:FieldAccessModify FieldAccessModify 33 */ 34 35 import java.lang.reflect.Field; 36 import java.util.Arrays; 37 38 39 public class FieldAccessModify { 40 41 private static final String agentLib = "FieldAccessModify"; 42 43 private static value class MyPrimitive { 44 public int MyPrimitive_fld1; 45 public int MyPrimitive_fld2; 46 47 public MyPrimitive(int v1, int v2) { MyPrimitive_fld1 = v1; MyPrimitive_fld2 = v2; } 48 49 public static MyPrimitive create(int v1, int v2) { 50 return new MyPrimitive(v1, v2); 51 } 52 53 public String toString() { 54 return "MyPrimitive { fld1=" + MyPrimitive_fld1 + ", fld2=" + MyPrimitive_fld2 + "}"; 55 } 56 57 } 58 59 private static class InstanceHolder { 60 public final MyPrimitive InstanceHolder_fld1; 61 62 public InstanceHolder(int v) { 63 InstanceHolder_fld1 = MyPrimitive.create(v, v + 100); 64 } 65 66 public String toString() { 67 return "InstanceHolder { fld1 is " + InstanceHolder_fld1 + "}"; 68 } 69 } 70 71 private static value class PrimitiveHolder { 72 public MyPrimitive PrimitiveHolder_fld1; 73 74 public PrimitiveHolder(int v) { 75 PrimitiveHolder_fld1 = MyPrimitive.create(v, v + 200); 76 } 77 78 public String toString() { 79 return "PrimitiveHolder { fld1 is " + PrimitiveHolder_fld1 + "}"; 80 } 81 } 82 83 private static class TestHolder { 84 public MyPrimitive primitiveObj = MyPrimitive.create(1, 1); 85 public InstanceHolder instanceHolderObj = new InstanceHolder(1); 86 public PrimitiveHolder primitiveHolderObj = new PrimitiveHolder(1); 87 } 88 89 public static void main(String[] args) throws Exception { 90 try { 91 System.loadLibrary(agentLib); 92 } catch (UnsatisfiedLinkError ex) { 93 System.err.println("Failed to load " + agentLib + " lib"); 94 System.err.println("java.library.path: " + System.getProperty("java.library.path")); 95 throw ex; 96 } 97 98 // create objects for access testing before setting watchers 99 TestHolder testHolder = new TestHolder(); 100 101 if (!initWatchers(MyPrimitive.class, MyPrimitive.class.getDeclaredField("MyPrimitive_fld1"))) { 102 throw new RuntimeException("Watchers initializations error (MyPrimitive_fld1)"); 103 } 104 if (!initWatchers(MyPrimitive.class, MyPrimitive.class.getDeclaredField("MyPrimitive_fld2"))) { 105 throw new RuntimeException("Watchers initializations error (MyPrimitive_fld2)"); 106 } 107 if (!initWatchers(InstanceHolder.class, InstanceHolder.class.getDeclaredField("InstanceHolder_fld1"))) { 108 throw new RuntimeException("Watchers initializations error (InstanceHolder_fld1)"); 109 } 110 if (!initWatchers(PrimitiveHolder.class, PrimitiveHolder.class.getDeclaredField("PrimitiveHolder_fld1"))) { 111 throw new RuntimeException("Watchers initializations error (PrimitiveHolder_fld1)"); 112 } 113 114 test("MyPrimitive (access)", () -> { 115 testHolder.primitiveObj.toString(); // should access both MyPrimitive_fld1 and MyPrimitive_fld2 116 }, new TestResult() { 117 public boolean MyPrimitive_fld1_access; 118 public boolean MyPrimitive_fld2_access; 119 }); 120 121 test("InstanceHolder (access)", () -> { 122 testHolder.instanceHolderObj.toString(); 123 }, new TestResult() { 124 public boolean InstanceHolder_fld1_access; 125 // MyPrimitive fields should be accessed too 126 public boolean MyPrimitive_fld1_access; 127 public boolean MyPrimitive_fld2_access; 128 }); 129 130 test("PrimitiveHolder (access)", () -> { 131 testHolder.primitiveHolderObj.toString(); 132 }, new TestResult() { 133 public boolean PrimitiveHolder_fld1_access; 134 // MyPrimitive fields should be accessed too 135 public boolean MyPrimitive_fld1_access; 136 public boolean MyPrimitive_fld2_access; 137 }); 138 139 test("MyPrimitive (modify)", () -> { 140 MyPrimitive obj = MyPrimitive.create(1, 1); 141 }, new TestResult() { 142 // KNOWN_ISSUE public boolean MyPrimitive_fld1_modify; 143 // KNOWN_ISSUE public boolean MyPrimitive_fld2_modify; 144 }); 145 146 test("InstanceHolder (modify)", () -> { 147 InstanceHolder obj = new InstanceHolder(10); 148 }, new TestResult() { 149 public boolean InstanceHolder_fld1_modify; 150 // MyPrimitive fields should be modified too 151 // KNOWN_ISSUE public boolean MyPrimitive_fld1_modify; 152 // KNOWN_ISSUE public boolean MyPrimitive_fld2_modify; 153 }); 154 155 test("PrimitiveHolder (modify)", () -> { 156 PrimitiveHolder obj = new PrimitiveHolder(11); 157 }, new TestResult() { 158 // KNOWN_ISSUE public boolean PrimitiveHolder_fld1_modify; 159 // MyPrimitive fields should be modified too 160 // KNOWN_ISSUE public boolean MyPrimitive_fld1_modify; 161 // KNOWN_ISSUE public boolean MyPrimitive_fld2_modify; 162 }); 163 164 } 165 166 private static void log(String msg) { 167 System.out.println(msg); 168 System.out.flush(); 169 } 170 171 private static void assertTrue(boolean value) { 172 if (!value) { 173 throw new RuntimeException("assert"); 174 } 175 } 176 177 // For every access/modify notification native part tries to locate 178 // boolean "<field_name>_access"/"<field_name>_modify" field and sets it to true 179 private static class TestResult { 180 181 // verify that all fields are set to true 182 public void verify() { 183 Arrays.stream(this.getClass().getDeclaredFields()).forEach(f -> verify(f)); 184 } 185 186 private void verify(Field f) { 187 try { 188 if (!f.getBoolean(this)) { 189 throw new RuntimeException(f.getName() + " notification is missed"); 190 } 191 log(" - " + f.getName() + ": OK"); 192 } catch (IllegalAccessException ex) { 193 throw new RuntimeException(ex); 194 } 195 } 196 } 197 198 @FunctionalInterface 199 private interface TestAction { 200 void apply(); 201 } 202 203 private static void test(String descr, TestAction action, TestResult result) throws Exception { 204 log(descr + ": starting"); 205 if (!startTest(result)) { 206 throw new RuntimeException("startTest failed"); 207 } 208 action.apply(); 209 // wait some time to ensure all posted events are handled 210 Thread.sleep(500); 211 212 stopTest(); 213 214 // check the results 215 result.verify(); 216 217 log(descr + ": OK"); 218 log(""); 219 } 220 221 private static native boolean initWatchers(Class cls, Field field); 222 private static native boolean startTest(TestResult results); 223 private static native void stopTest(); 224 225 }