1 /* 2 * Copyright (c) 2022, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.internal.foreign; 27 28 import java.lang.foreign.AddressLayout; 29 import java.lang.foreign.GroupLayout; 30 import java.lang.foreign.MemoryLayout; 31 import java.lang.foreign.MemorySegment; 32 import java.lang.foreign.PaddingLayout; 33 import java.lang.foreign.SequenceLayout; 34 import java.lang.foreign.StructLayout; 35 import java.lang.foreign.ValueLayout; 36 import java.lang.foreign.UnionLayout; 37 import java.util.function.BiFunction; 38 import java.util.function.Consumer; 39 import java.util.stream.Stream; 40 41 import static java.util.Objects.requireNonNull; 42 43 /** 44 * Internal class to support inspection MemorySegments into various formats. 45 */ 46 final class MemoryInspectionUtil { 47 48 static final BiFunction<ValueLayout, Object, String> STANDARD_VALUE_LAYOUT_RENDERER = new StandardValueLayoutRenderer(); 49 50 // Suppresses default constructor, ensuring non-instantiability. 51 private MemoryInspectionUtil() { 52 } 53 54 static Stream<String> inspect(MemorySegment segment, 55 MemoryLayout layout, 56 BiFunction<ValueLayout, Object, String> renderer) { 57 requireNonNull(segment); 58 requireNonNull(layout); 59 requireNonNull(renderer); 60 61 final var builder = Stream.<String>builder(); 62 toString0(segment, layout, renderer, builder::add, new ViewState(), ""); 63 return builder.build(); 64 } 65 66 private static void toString0(MemorySegment segment, 67 MemoryLayout layout, 68 BiFunction<ValueLayout, Object, String> renderer, 69 Consumer<String> action, 70 ViewState state, 71 String suffix) { 72 73 switch (layout) { 74 case ValueLayout.OfBoolean ofBoolean -> 75 action.accept(renderValueLayout(state, ofBoolean, renderer.apply(ofBoolean, segment.get(ofBoolean, state.indexAndAdd(ofBoolean))), suffix)); 76 case ValueLayout.OfByte ofByte -> 77 action.accept(renderValueLayout(state, ofByte, renderer.apply(ofByte, segment.get(ofByte, state.indexAndAdd(ofByte))), suffix)); 78 case ValueLayout.OfShort ofShort -> 79 action.accept(renderValueLayout(state, ofShort, renderer.apply(ofShort, segment.get(ofShort, state.indexAndAdd(ofShort))), suffix)); 80 case ValueLayout.OfChar ofChar -> 81 action.accept(renderValueLayout(state, ofChar, renderer.apply(ofChar, segment.get(ofChar, state.indexAndAdd(ofChar))), suffix)); 82 case ValueLayout.OfInt ofInt -> 83 action.accept(renderValueLayout(state, ofInt, renderer.apply(ofInt, segment.get(ofInt, state.indexAndAdd(ofInt))), suffix)); 84 case ValueLayout.OfLong ofLong -> 85 action.accept(renderValueLayout(state, ofLong, renderer.apply(ofLong, segment.get(ofLong, state.indexAndAdd(ofLong))), suffix)); 86 case ValueLayout.OfFloat ofFloat -> 87 action.accept(renderValueLayout(state, ofFloat, renderer.apply(ofFloat, segment.get(ofFloat, state.indexAndAdd(ofFloat))), suffix)); 88 case ValueLayout.OfDouble ofDouble -> 89 action.accept(renderValueLayout(state, ofDouble, renderer.apply(ofDouble, segment.get(ofDouble, state.indexAndAdd(ofDouble))), suffix)); 90 case AddressLayout addressLayout -> 91 action.accept(renderValueLayout(state, addressLayout, renderer.apply(addressLayout, segment.get(addressLayout, state.indexAndAdd(addressLayout))), suffix)); 92 case PaddingLayout paddingLayout -> { 93 action.accept(state.indentSpaces() + paddingLayout.byteSize() + " padding bytes"); 94 state.indexAndAdd(paddingLayout); 95 } 96 case GroupLayout groupLayout -> { 97 /* Strictly, we should provide all permutations of unions. 98 * So, if we have a union U = (A|B),(C|D) then we should present: 99 * (A,C), (A,D), (B,C) and (B,D) 100 */ 101 102 final var separator = groupLayout instanceof StructLayout 103 ? "," // Struct separator 104 : "|"; // Union separator 105 106 action.accept(indentedLabel(state, groupLayout) + " {"); 107 state.incrementIndent(); 108 final var members = groupLayout.memberLayouts(); 109 final long initialIndex = state.index(); 110 long maxIndex = initialIndex; 111 for (int i = 0; i < members.size(); i++) { 112 if (groupLayout instanceof UnionLayout) { 113 // If it is a union, we need to reset the index for each member 114 state.index(initialIndex); 115 // We record the max index used for any union member so that we can leave off from there 116 maxIndex = Math.max(maxIndex, state.index()); 117 } 118 toString0(segment, members.get(i), renderer, action, state, (i != (members.size() - 1)) ? separator : ""); 119 if (groupLayout instanceof UnionLayout) { 120 // This is the best we can do. 121 state.index(maxIndex); 122 } 123 } 124 state.decrementIndent(); 125 action.accept(state.indentSpaces() + "}" + suffix); 126 } 127 case SequenceLayout sequenceLayout -> { 128 action.accept(indentedLabel(state, sequenceLayout) + " ["); 129 state.incrementIndent(); 130 final long elementCount = sequenceLayout.elementCount(); 131 for (long i = 0; i < elementCount; i++) { 132 toString0(segment, sequenceLayout.elementLayout(), renderer, action, state, (i != (elementCount - 1L)) ? "," : ""); 133 } 134 state.decrementIndent(); 135 action.accept(state.indentSpaces() + "]" + suffix); 136 } 137 } 138 } 139 140 static String renderValueLayout(ViewState state, 141 ValueLayout layout, 142 String value, 143 String suffix) { 144 return indentedLabel(state, layout) + "=" + value + suffix; 145 } 146 147 static String indentedLabel(ViewState state, 148 MemoryLayout layout) { 149 return state.indentSpaces() + layout.name() 150 .orElseGet(layout::toString); 151 } 152 153 static final class ViewState { 154 155 private static final int SPACES_PER_INDENT = 4; 156 157 // Holding a non-static indents allows simple thread-safe use 158 private final StringBuilder indents = new StringBuilder(); 159 160 private int indent; 161 private long index; 162 163 void incrementIndent() { 164 indent++; 165 } 166 167 void decrementIndent() { 168 indent--; 169 } 170 171 String indentSpaces() { 172 final int spaces = indent * SPACES_PER_INDENT; 173 while (indents.length() < spaces) { 174 // Expand as needed 175 indents.append(" "); 176 } 177 return indents.substring(0, spaces); 178 } 179 180 long index() { 181 return index; 182 } 183 184 void index(long index) { 185 this.index = index; 186 } 187 188 long indexAndAdd(long delta) { 189 final long val = index; 190 index += delta; 191 return val; 192 } 193 194 long indexAndAdd(MemoryLayout layout) { 195 return indexAndAdd(layout.byteSize()); 196 } 197 } 198 199 private static final class StandardValueLayoutRenderer implements BiFunction<ValueLayout, Object, String> { 200 201 @Override 202 public String apply(ValueLayout layout, Object o) { 203 requireNonNull(layout); 204 requireNonNull(o); 205 206 return switch (layout) { 207 case ValueLayout.OfBoolean __ when o instanceof Boolean b -> Boolean.toString(b); 208 case ValueLayout.OfByte __ when o instanceof Byte b -> Byte.toString(b); 209 case ValueLayout.OfShort __ when o instanceof Short s -> Short.toString(s); 210 case ValueLayout.OfChar __ when o instanceof Character c -> Character.toString(c); 211 case ValueLayout.OfInt __ when o instanceof Integer i -> Integer.toString(i); 212 case ValueLayout.OfLong __ when o instanceof Long l -> Long.toString(l); 213 case ValueLayout.OfFloat __ when o instanceof Float f -> Float.toString(f); 214 case ValueLayout.OfDouble __ when o instanceof Double d -> Double.toString(d); 215 case AddressLayout __ when o instanceof MemorySegment m -> 216 String.format("0x%0" + (ValueLayout.ADDRESS.byteSize() * 2) + "X", m.address()); 217 default -> 218 throw new UnsupportedOperationException("layout " + layout + " for " + o.getClass().getName() + " not supported"); 219 }; 220 } 221 222 @Override 223 public String toString() { 224 return singletonToString(StandardValueLayoutRenderer.class); 225 } 226 } 227 228 private static String singletonToString(Class<?> implementingClass) { 229 return "The " + implementingClass.getName() + " singleton"; 230 } 231 232 }