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 }