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 * @test 26 * @summary Tests that all FieldAccess and FieldModification notifications 27 are generated for value classes. 28 * @requires vm.jvmti 29 * @enablePreview 30 * @run main/othervm/native -agentlib:FieldAccessModify -XX:+EnableValhalla FieldAccessModify 31 */ 32 33 import java.lang.reflect.Field; 34 import java.util.Arrays; 35 36 public class FieldAccessModify { 37 38 private static final String agentLib = "FieldAccessModify"; 39 40 private static value class ValueClass { 41 public int valueClass_fld1; 42 public int valueClass_fld2; 43 44 public ValueClass(int v1, int v2) { valueClass_fld1 = v1; valueClass_fld2 = v2; } 45 46 public String toString() { 47 return "ValueClass { fld1=" + valueClass_fld1 + ", fld2=" + valueClass_fld2 + "}"; 48 } 49 50 } 51 52 private static class InstanceHolder { 53 public final ValueClass instanceHolder_fld1; 54 55 public InstanceHolder(int v) { 56 instanceHolder_fld1 = new ValueClass(v, v + 100); 57 } 58 59 public String toString() { 60 return "InstanceHolder { fld1 is " + instanceHolder_fld1 + "}"; 61 } 62 } 63 64 private static value class ValueHolder { 65 public ValueClass valueHolder_fld1; 66 67 public ValueHolder(int v) { 68 valueHolder_fld1 = new ValueClass(v, v + 200); 69 } 70 71 public String toString() { 72 return "ValueHolder { fld1 is " + valueHolder_fld1 + "}"; 73 } 74 } 75 76 private static class TestHolder { 77 public ValueClass valueObj = new ValueClass(1, 1); 78 public InstanceHolder instanceHolderObj = new InstanceHolder(1); 79 public ValueHolder valueHolderObj = new ValueHolder(1); 80 } 81 82 public static void main(String[] args) throws Exception { 83 try { 84 System.loadLibrary(agentLib); 85 } catch (UnsatisfiedLinkError ex) { 86 System.err.println("Failed to load " + agentLib + " lib"); 87 System.err.println("java.library.path: " + System.getProperty("java.library.path")); 88 throw ex; 89 } 90 91 // create objects for access testing before setting watchers 92 TestHolder testHolder = new TestHolder(); 93 94 if (!initWatchers(ValueClass.class, ValueClass.class.getDeclaredField("valueClass_fld1"))) { 95 throw new RuntimeException("Watchers initializations error (valueClass_fld1)"); 96 } 97 if (!initWatchers(ValueClass.class, ValueClass.class.getDeclaredField("valueClass_fld2"))) { 98 throw new RuntimeException("Watchers initializations error (valueClass_fld2)"); 99 } 100 if (!initWatchers(InstanceHolder.class, InstanceHolder.class.getDeclaredField("instanceHolder_fld1"))) { 101 throw new RuntimeException("Watchers initializations error (instanceHolder_fld1)"); 102 } 103 if (!initWatchers(ValueHolder.class, ValueHolder.class.getDeclaredField("valueHolder_fld1"))) { 104 throw new RuntimeException("Watchers initializations error (valueHolder_fld1)"); 105 } 106 107 test("ValueClass (access)", () -> { 108 testHolder.valueObj.toString(); // should access both valueClass_fld1 and valueClass_fld2 109 }, new TestResult() { 110 public boolean valueClass_fld1_access; 111 public boolean valueClass_fld2_access; 112 }); 113 114 test("InstanceHolder (access)", () -> { 115 testHolder.instanceHolderObj.toString(); 116 }, new TestResult() { 117 public boolean instanceHolder_fld1_access; 118 // ValueClass fields should be accessed too 119 public boolean valueClass_fld1_access; 120 public boolean valueClass_fld2_access; 121 }); 122 123 test("ValueHolder (access)", () -> { 124 testHolder.valueHolderObj.toString(); 125 }, new TestResult() { 126 public boolean valueHolder_fld1_access; 127 // ValueClass fields should be accessed too 128 public boolean valueClass_fld1_access; 129 public boolean valueClass_fld2_access; 130 }); 131 132 test("ValueClass (modify)", () -> { 133 ValueClass obj = new ValueClass(1, 1); 134 }, new TestResult() { 135 public boolean valueClass_fld1_modify; 136 public boolean valueClass_fld2_modify; 137 }); 138 139 test("InstanceHolder (modify)", () -> { 140 InstanceHolder obj = new InstanceHolder(10); 141 }, new TestResult() { 142 public boolean instanceHolder_fld1_modify; 143 // ValueClass fields should be modified too 144 boolean valueClass_fld1_modify; 145 public boolean valueClass_fld2_modify; 146 }); 147 148 test("ValueHolder (modify)", () -> { 149 ValueHolder obj = new ValueHolder(11); 150 }, new TestResult() { 151 public boolean valueHolder_fld1_modify; 152 // ValueClass fields should be modified too 153 public boolean valueClass_fld1_modify; 154 public boolean valueClass_fld2_modify; 155 }); 156 157 } 158 159 private static void log(String msg) { 160 System.out.println(msg); 161 System.out.flush(); 162 } 163 164 private static void assertTrue(boolean value) { 165 if (!value) { 166 throw new RuntimeException("assert"); 167 } 168 } 169 170 // For every access/modify notification native part tries to locate 171 // boolean "<field_name>_access"/"<field_name>_modify" field and sets it to true 172 private static class TestResult { 173 174 // verify that all fields are set to true 175 public void verify() { 176 Arrays.stream(this.getClass().getDeclaredFields()).forEach(f -> verify(f)); 177 } 178 179 private void verify(Field f) { 180 try { 181 if (!f.getBoolean(this)) { 182 throw new RuntimeException(f.getName() + " notification is missed"); 183 } 184 log(" - " + f.getName() + ": OK"); 185 } catch (IllegalAccessException ex) { 186 throw new RuntimeException(ex); 187 } 188 } 189 } 190 191 @FunctionalInterface 192 private interface TestAction { 193 void apply(); 194 } 195 196 private static void test(String descr, TestAction action, TestResult result) throws Exception { 197 log(descr + ": starting"); 198 if (!startTest(result)) { 199 throw new RuntimeException("startTest failed"); 200 } 201 action.apply(); 202 // wait some time to ensure all posted events are handled 203 Thread.sleep(500); 204 205 stopTest(); 206 207 // check the results 208 result.verify(); 209 210 log(descr + ": OK"); 211 log(""); 212 } 213 214 private static native boolean initWatchers(Class cls, Field field); 215 private static native boolean startTest(TestResult results); 216 private static native void stopTest(); 217 218 }