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 }