1 
  2 /*
  3  * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
  4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  5  *
  6  * This code is free software; you can redistribute it and/or modify it
  7  * under the terms of the GNU General Public License version 2 only, as
  8  * published by the Free Software Foundation.
  9  *
 10  * This code is distributed in the hope that it will be useful, but WITHOUT
 11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 13  * version 2 for more details (a copy is included in the LICENSE file that
 14  * accompanied this code).
 15  *
 16  * You should have received a copy of the GNU General Public License version
 17  * 2 along with this work; if not, write to the Free Software Foundation,
 18  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 19  *
 20  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 21  * or visit www.oracle.com if you need additional information or have any
 22  * questions.
 23  */
 24 
 25 /*
 26  * @test
 27  * @modules java.base/jdk.internal.foreign
 28  * @run testng/othervm --enable-native-access=ALL-UNNAMED TestMemoryInspection
 29  */
 30 
 31 import java.lang.foreign.*;
 32 import java.lang.invoke.VarHandle;
 33 import java.util.List;
 34 import java.util.function.BiFunction;
 35 import java.util.function.Function;
 36 import java.util.stream.Stream;
 37 
 38 import jdk.internal.foreign.MemoryInspection;
 39 import org.testng.annotations.*;
 40 
 41 import static java.lang.foreign.ValueLayout.*;
 42 import static java.util.stream.Collectors.joining;
 43 import static org.testng.Assert.*;
 44 import static java.util.Objects.requireNonNull;
 45 
 46 @Test
 47 public class TestMemoryInspection {
 48 
 49     private static final String EXPECT_ADDRESS = "0x" + "00".repeat((int) ValueLayout.ADDRESS.byteSize());
 50 
 51     @Test
 52     public void valueLayouts() {
 53 
 54         record TestInput(ValueLayout layout, String stringValue) {
 55         }
 56 
 57         List.of(
 58                 new TestInput(ValueLayout.JAVA_BYTE, "0"),
 59                 new TestInput(ValueLayout.JAVA_SHORT, "0"),
 60                 new TestInput(ValueLayout.JAVA_INT, "0"),
 61                 new TestInput(ValueLayout.JAVA_LONG, "0"),
 62                 new TestInput(ValueLayout.JAVA_FLOAT, "0.0"),
 63                 new TestInput(ValueLayout.JAVA_DOUBLE, "0.0"),
 64                 new TestInput(ValueLayout.JAVA_CHAR, "" + (char) 0),
 65                 new TestInput(JAVA_BOOLEAN, "false"),
 66                 new TestInput(ValueLayout.ADDRESS, EXPECT_ADDRESS)
 67         ).forEach(ti -> {
 68             var expect = ti.layout() + "=" + ti.stringValue();
 69             var actual = testWithFreshMemorySegment(ti.layout().byteSize(), s -> jdk.internal.foreign.MemoryInspection.inspect(s, ti.layout(), jdk.internal.foreign.MemoryInspection.standardRenderer()))
 70                     .collect(joining(System.lineSeparator()));
 71             assertEquals(actual, expect);
 72         });
 73     }
 74 
 75     @Test
 76     public void point() {
 77 
 78         var expect = platformLineSeparated("""
 79                 Point {
 80                     x=1,
 81                     y=2
 82                 }""");
 83 
 84         var actual = testWithFreshMemorySegment(Integer.BYTES * 2, segment -> {
 85             final Point point = new Point(segment);
 86             point.x(1);
 87             point.y(2);
 88             return jdk.internal.foreign.MemoryInspection.inspect(segment, Point.LAYOUT, jdk.internal.foreign.MemoryInspection.standardRenderer())
 89                     .collect(joining(System.lineSeparator()));
 90         });
 91 
 92         assertEquals(actual, expect);
 93     }
 94 
 95     @Test
 96     public void pointCustomRenderer() {
 97 
 98         var expect = platformLineSeparated("""
 99                 Point {
100                     x=0x0001,
101                     y=0x0002
102                 }""");
103 
104         var actual = testWithFreshMemorySegment(Integer.BYTES * 2, segment -> {
105             final Point point = new Point(segment);
106             point.x(1);
107             point.y(2);
108             return MemoryInspection.inspect(segment, Point.LAYOUT, new BiFunction<ValueLayout, Object, String>() {
109 
110                         @Override
111                         public String apply(ValueLayout layout, Object o) {
112                             return String.format("0x%04x", (int)o);
113                         }
114                     })
115                     .collect(joining(System.lineSeparator()));
116         });
117 
118         assertEquals(actual, expect);
119     }
120 
121     @Test
122     public void standardCustomRenderer() {
123 
124         MemoryLayout layout = MemoryLayout.structLayout(
125                 // These are in bit alignment order (descending) for all platforms
126                 // in order for each element to be aligned to its type's bit alignment.
127                 Stream.of(
128                                 JAVA_LONG,
129                                 JAVA_DOUBLE,
130                                 ADDRESS,
131                                 JAVA_INT,
132                                 JAVA_FLOAT,
133                                 JAVA_SHORT,
134                                 JAVA_CHAR,
135                                 JAVA_BOOLEAN,
136                                 JAVA_BYTE
137                         )
138                         .map(vl -> vl.withName(vl.carrier().getSimpleName()))
139                         .toArray(MemoryLayout[]::new)
140         ).withName("struct");
141 
142         System.out.println("layout = " + layout);
143         var expect = platformLineSeparated("""
144                 struct {
145                     long=0,
146                     double=0.0,
147                     MemorySegment=$1,
148                     int=0,
149                     float=0.0,
150                     short=0,
151                     char=\u0000,
152                     boolean=false,
153                     byte=0
154                 }""").replace("$1", EXPECT_ADDRESS);
155 
156 
157         var actual = testWithFreshMemorySegment(layout.byteSize(), segment ->
158                 jdk.internal.foreign.MemoryInspection.inspect(segment, layout, jdk.internal.foreign.MemoryInspection.standardRenderer()))
159                 .collect(joining(System.lineSeparator()));
160 
161         assertEquals(actual, expect);
162     }
163 
164 
165     @Test
166     public void sequence() {
167         final int arraySize = 4;
168         var sequenceLayout = MemoryLayout.sequenceLayout(arraySize,
169                 MemoryLayout.structLayout(
170                         ValueLayout.JAVA_INT.withName("x"),
171                         ValueLayout.JAVA_INT.withName("y")
172                 ).withName("Point")
173         ).withName("PointArrayOfElements");
174 
175         var xh = sequenceLayout.varHandle(PathElement.sequenceElement(), PathElement.groupElement("x"));
176         var yh = sequenceLayout.varHandle(PathElement.sequenceElement(), PathElement.groupElement("y"));
177 
178         var expect = platformLineSeparated("""
179                 PointArrayOfElements [
180                     Point {
181                         x=1,
182                         y=0
183                     },
184                     Point {
185                         x=1,
186                         y=1
187                     },
188                     Point {
189                         x=1,
190                         y=2
191                     },
192                     Point {
193                         x=1,
194                         y=3
195                     }
196                 ]""");
197         var actual = testWithFreshMemorySegment(Integer.BYTES * 2 * arraySize, segment -> {
198             for (long i = 0; i < sequenceLayout.elementCount(); i++) {
199                 xh.set(segment, 0L, i, 1);
200                 yh.set(segment, 0L, i, (int) i);
201             }
202 
203             return jdk.internal.foreign.MemoryInspection.inspect(segment, sequenceLayout, jdk.internal.foreign.MemoryInspection.standardRenderer())
204                 .collect(joining(System.lineSeparator()));}
205         );
206         assertEquals(actual, expect);
207     }
208 
209 
210     @Test
211     public void union() {
212         var u0 = MemoryLayout.structLayout(
213                 ValueLayout.JAVA_INT.withName("x"),
214                 ValueLayout.JAVA_INT.withName("y"),
215                 MemoryLayout.paddingLayout(Integer.BYTES)
216         ).withName("Point");
217 
218         var u1 = MemoryLayout.structLayout(
219                 ValueLayout.JAVA_INT.withName("x"),
220                 ValueLayout.JAVA_INT.withName("y"),
221                 ValueLayout.JAVA_INT.withName("z")
222         ).withName("3D-Point");
223 
224         var union = MemoryLayout.unionLayout(u0, u1).withName("Union");
225 
226         var expect = platformLineSeparated("""
227                 Union {
228                     Point {
229                         x=1,
230                         y=2,
231                         4 padding bytes
232                     }|
233                     3D-Point {
234                         x=1,
235                         y=2,
236                         z=3
237                     }
238                 }""");
239         var actual = testWithFreshMemorySegment(Integer.BYTES * 3, segment -> {
240             u0.varHandle(PathElement.groupElement("x")).set(segment, 0L, 1);
241             u1.varHandle(PathElement.groupElement("y")).set(segment, 0L, 2);
242             u1.varHandle(PathElement.groupElement("z")).set(segment, 0L, 3);
243             return jdk.internal.foreign.MemoryInspection.inspect(segment, union, jdk.internal.foreign.MemoryInspection.standardRenderer())
244                     .collect(joining(System.lineSeparator()));
245         });
246 
247         assertEquals(actual, expect);
248     }
249 
250     static final class Point {
251 
252         static final MemoryLayout LAYOUT = MemoryLayout.structLayout(
253                 ValueLayout.JAVA_INT.withName("x"),
254                 ValueLayout.JAVA_INT.withName("y")
255         ).withName("Point");
256 
257         static final VarHandle xVH = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("x"));
258         static final VarHandle yVH = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("y"));
259 
260         private final MemorySegment memorySegment;
261 
262         Point(MemorySegment memorySegment) {
263             this.memorySegment = requireNonNull(memorySegment);
264         }
265 
266         int x() {
267             return (int) xVH.get(memorySegment, 0L);
268         }
269 
270         int y() {
271             return (int) yVH.get(memorySegment, 0L);
272         }
273 
274         void x(int x) {
275             xVH.set(memorySegment, 0L, x);
276         }
277 
278         void y(int y) {
279             yVH.set(memorySegment, 0L, y);
280         }
281 
282         @Override
283         public String toString() {
284             return "Point {x=" + x() + ", y=" + y() + "}";
285         }
286     }
287 
288     private static String platformLineSeparated(String s) {
289         return s.lines()
290                 .collect(joining(System.lineSeparator()));
291     }
292 
293     private static <T> T testWithFreshMemorySegment(long size,
294                                                     Function<MemorySegment, T> mapper) {
295         try (final Arena arena = Arena.ofConfined()) {
296             var segment = arena.allocate(size);
297             return mapper.apply(segment);
298         }
299     }
300 
301 }