Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an internal MemoryInspection class #715

Closed
wants to merge 3 commits into from
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 14 additions & 147 deletions src/java.base/share/classes/jdk/internal/foreign/MemoryInspection.java
Original file line number Diff line number Diff line change
@@ -50,7 +50,7 @@ private MemoryInspection() {

/**
* Returns a human-readable view of the provided {@linkplain MemorySegment memory} viewed
* through the provided {@linkplain MemoryLayout layout} using the provided {@linkplain ValueLayoutRenderer renderer}.
* through the provided {@linkplain MemoryLayout layout} using the provided {@code renderer}.
* <p>
* The exact format of the returned view is unspecified and should not
* be acted upon programmatically.
@@ -83,161 +83,28 @@ private MemoryInspection() {
*/
public static Stream<String> inspect(MemorySegment segment,
MemoryLayout layout,
ValueLayoutRenderer renderer) {
BiFunction<ValueLayout, Object, String> renderer) {
requireNonNull(segment);
requireNonNull(layout);
requireNonNull(renderer);
return MemoryInspectionUtil.inspect(segment, layout, renderer);
}

/**
* An interface that can be used to specify custom rendering of value
* layouts via the {@link MemoryInspection#inspect(MemorySegment, MemoryLayout, ValueLayoutRenderer)} method.
* {@return a standard value layout renderer that will render numeric values into decimal form and where
* other value types are rendered to a reasonable "natural" form}
* <p>
* The render methods take two parameters:
* More specifically, values types are rendered as follows:
* <ul>
* <li>layout: This can be used to select different formatting for different paths</li>
* <li>value: The actual value</li>
* <li>Numeric values are rendered in decimal form (e.g 1 or 1.2).</li>
* <li>Boolean values are rendered as {@code true} or {@code false}.</li>
* <li>Character values are rendered as {@code char}.</li>
* <li>Address values are rendered in hexadecimal form e.g. {@code 0x0000000000000000} (on 64-bit platforms) or
* {@code 0x00000000} (on 32-bit platforms)</li>
* </ul>
* <p>
* The {@linkplain ValueLayoutRenderer#standard() standard() } value layout renderer is path
* agnostic and will thus render all layouts of the same type the same way.
*
* @see MemoryInspection#inspect(MemorySegment, MemoryLayout, ValueLayoutRenderer)
*/
public interface ValueLayoutRenderer {
/**
* Renders the provided {@code booleanLayout} and {@code value} to a String.
*
* @param booleanLayout the layout to render
* @param value the value to render
* @return rendered String
*/
default String render(ValueLayout.OfBoolean booleanLayout, boolean value) {
requireNonNull(booleanLayout);
return Boolean.toString(value);
}

/**
* Renders the provided {@code byteLayout} and {@code value} to a String.
*
* @param byteLayout the layout to render
* @param value the value to render
* @return rendered String
*/
default String render(ValueLayout.OfByte byteLayout, byte value) {
requireNonNull(byteLayout);
return Byte.toString(value);
}

/**
* Renders the provided {@code charLayout} and {@code value} to a String.
*
* @param charLayout the layout to render
* @param value the value to render
* @return rendered String
*/
default String render(ValueLayout.OfChar charLayout, char value) {
requireNonNull(charLayout);
return Character.toString(value);
}

/**
* Renders the provided {@code shortLayout} and {@code value} to a String.
*
* @param shortLayout the layout to render
* @param value the value to render
* @return rendered String
*/
default String render(ValueLayout.OfShort shortLayout, short value) {
requireNonNull(shortLayout);
return Short.toString(value);
}

/**
* Renders the provided {@code intLayout} and {@code value} to a String.
*
* @param intLayout the layout to render
* @param value the value to render
* @return rendered String
*/
default String render(ValueLayout.OfInt intLayout, int value) {
requireNonNull(intLayout);
return Integer.toString(value);
}

/**
* Renders the provided {@code longLayout} and {@code value} to a String.
*
* @param longLayout the layout to render
* @param value the value to render
* @return rendered String
*/
default String render(ValueLayout.OfLong longLayout, long value) {
requireNonNull(longLayout);
return Long.toString(value);
}

/**
* Renders the provided {@code floatLayout} and {@code value} to a String.
*
* @param floatLayout the layout to render
* @param value the value to render
* @return rendered String
*/
default String render(ValueLayout.OfFloat floatLayout, float value) {
requireNonNull(floatLayout);
return Float.toString(value);
}

/**
* Renders the provided {@code doubleLayout} and {@code value} to a String.
*
* @param doubleLayout the layout to render
* @param value the value to render
* @return rendered String
*/
default String render(ValueLayout.OfDouble doubleLayout, double value) {
requireNonNull(doubleLayout);
return Double.toString(value);
}

/**
* Renders the provided {@code addressLayout} and {@code value} to a String.
*
* @param addressLayout the layout to render
* @param value the value to render
* @return rendered String
*/
default String render(ValueLayout.OfAddress addressLayout, MemorySegment value) {
requireNonNull(addressLayout);
return String.format("0x%0" + (ValueLayout.ADDRESS.byteSize() * 2) + "X", value.address());
}

/**
* {@return a standard value layout renderer that will render numeric values into decimal form and where
* other value types are rendered to a reasonable "natural" form}
* <p>
* More specifically, values types are rendered as follows:
* <ul>
* <li>Numeric values are rendered in decimal form (e.g 1 or 1.2).</li>
* <li>Boolean values are rendered as {@code true} or {@code false}.</li>
* <li>Character values are rendered as {@code char}.</li>
* <li>Address values are rendered in hexadecimal form e.g. {@code 0x0000000000000000} (on 64-bit platforms) or
* {@code 0x00000000} (on 32-bit platforms)</li>
* </ul>
*/
static ValueLayoutRenderer standard() {
return STANDARD_VALUE_LAYOUT_RENDERER;
}

/**
* {@return a value layout renderer that will render all value layout types via the provided {@code renderer}}
*/
static ValueLayoutRenderer of(BiFunction<ValueLayout, Object, String> renderer) {
requireNonNull(renderer);
return new SingleFunctionValueLayoutRenderer(renderer);
}

public static BiFunction<ValueLayout, Object, String> standardRenderer() {
return STANDARD_VALUE_LAYOUT_RENDERER;
}
}

}
Original file line number Diff line number Diff line change
@@ -43,15 +43,15 @@
*/
final class MemoryInspectionUtil {

static final MemoryInspection.ValueLayoutRenderer STANDARD_VALUE_LAYOUT_RENDERER = new StandardValueLayoutRenderer();
static final BiFunction<ValueLayout, Object, String> STANDARD_VALUE_LAYOUT_RENDERER = new StandardValueLayoutRenderer();

// Suppresses default constructor, ensuring non-instantiability.
private MemoryInspectionUtil() {
}

static Stream<String> inspect(MemorySegment segment,
MemoryLayout layout,
MemoryInspection.ValueLayoutRenderer renderer) {
BiFunction<ValueLayout, Object, String> renderer) {
requireNonNull(segment);
requireNonNull(layout);
requireNonNull(renderer);
@@ -63,47 +63,47 @@ static Stream<String> inspect(MemorySegment segment,

private static void toString0(MemorySegment segment,
MemoryLayout layout,
MemoryInspection.ValueLayoutRenderer renderer,
BiFunction<ValueLayout, Object, String> renderer,
Consumer<String> action,
ViewState state,
String suffix) {

// TODO: Replace with "patterns in switch statement" once this becomes available.

if (layout instanceof ValueLayout.OfBoolean ofBoolean) {
action.accept(renderValueLayout(state, ofBoolean, renderer.render(ofBoolean, segment.get(ofBoolean, state.indexAndAdd(ofBoolean))), suffix));
action.accept(renderValueLayout(state, ofBoolean, renderer.apply(ofBoolean, segment.get(ofBoolean, state.indexAndAdd(ofBoolean))), suffix));
return;
}
if (layout instanceof ValueLayout.OfByte ofByte) {
action.accept(renderValueLayout(state, ofByte, renderer.render(ofByte, segment.get(ofByte, state.indexAndAdd(ofByte))), suffix));
action.accept(renderValueLayout(state, ofByte, renderer.apply(ofByte, segment.get(ofByte, state.indexAndAdd(ofByte))), suffix));
return;
}
if (layout instanceof ValueLayout.OfShort ofShort) {
action.accept(renderValueLayout(state, ofShort, renderer.render(ofShort, segment.get(ofShort, state.indexAndAdd(ofShort))), suffix));
action.accept(renderValueLayout(state, ofShort, renderer.apply(ofShort, segment.get(ofShort, state.indexAndAdd(ofShort))), suffix));
return;
}
if (layout instanceof ValueLayout.OfInt ofInt) {
action.accept(renderValueLayout(state, ofInt, renderer.render(ofInt, segment.get(ofInt, state.indexAndAdd(ofInt))), suffix));
action.accept(renderValueLayout(state, ofInt, renderer.apply(ofInt, segment.get(ofInt, state.indexAndAdd(ofInt))), suffix));
return;
}
if (layout instanceof ValueLayout.OfLong ofLong) {
action.accept(renderValueLayout(state, ofLong, renderer.render(ofLong, segment.get(ofLong, state.indexAndAdd(ofLong))), suffix));
action.accept(renderValueLayout(state, ofLong, renderer.apply(ofLong, segment.get(ofLong, state.indexAndAdd(ofLong))), suffix));
return;
}
if (layout instanceof ValueLayout.OfFloat ofFloat) {
action.accept(renderValueLayout(state, ofFloat, renderer.render(ofFloat, segment.get(ofFloat, state.indexAndAdd(ofFloat))), suffix));
action.accept(renderValueLayout(state, ofFloat, renderer.apply(ofFloat, segment.get(ofFloat, state.indexAndAdd(ofFloat))), suffix));
return;
}
if (layout instanceof ValueLayout.OfDouble ofDouble) {
action.accept(renderValueLayout(state, ofDouble, renderer.render(ofDouble, segment.get(ofDouble, state.indexAndAdd(ofDouble))), suffix));
action.accept(renderValueLayout(state, ofDouble, renderer.apply(ofDouble, segment.get(ofDouble, state.indexAndAdd(ofDouble))), suffix));
return;
}
if (layout instanceof ValueLayout.OfChar ofChar) {
action.accept(renderValueLayout(state, ofChar, renderer.render(ofChar, segment.get(ofChar, state.indexAndAdd(ofChar))), suffix));
action.accept(renderValueLayout(state, ofChar, renderer.apply(ofChar, segment.get(ofChar, state.indexAndAdd(ofChar))), suffix));
return;
}
if (layout instanceof ValueLayout.OfAddress ofAddress) {
action.accept(renderValueLayout(state, ofAddress, renderer.render(ofAddress, segment.get(ofAddress, state.indexAndAdd(ofAddress))), suffix));
action.accept(renderValueLayout(state, ofAddress, renderer.apply(ofAddress, segment.get(ofAddress, state.indexAndAdd(ofAddress))), suffix));
return;
}
if (layout instanceof PaddingLayout paddingLayout) {
@@ -218,76 +218,53 @@ long indexAndAdd(MemoryLayout layout) {
}
}

private static final class StandardValueLayoutRenderer implements MemoryInspection.ValueLayoutRenderer {
@Override
public String toString() {
return singletonToString(StandardValueLayoutRenderer.class);
}
}

private static String singletonToString(Class<?> implementingClass) {
return "The " + implementingClass.getName() + " singleton";
}

static final class SingleFunctionValueLayoutRenderer implements MemoryInspection.ValueLayoutRenderer {

private final BiFunction<ValueLayout, Object, String> renderer;

public SingleFunctionValueLayoutRenderer(BiFunction<ValueLayout, Object, String> renderer) {
this.renderer = renderer;
}
private static final class StandardValueLayoutRenderer implements BiFunction<ValueLayout, Object, String> {

@Override
public String render(ValueLayout.OfBoolean booleanLayout, boolean value) {
return renderer.apply(booleanLayout, value);
}

@Override
public String render(ValueLayout.OfByte byteLayout, byte value) {
return renderer.apply(byteLayout, value);
}

@Override
public String render(ValueLayout.OfChar charLayout, char value) {
return renderer.apply(charLayout, value);
}

@Override
public String render(ValueLayout.OfShort shortLayout, short value) {
return renderer.apply(shortLayout, value);
}
public String apply(ValueLayout layout, Object o) {
requireNonNull(layout);
requireNonNull(o);

@Override
public String render(ValueLayout.OfInt intLayout, int value) {
return renderer.apply(intLayout, value);
}
// TODO: Replace with "patterns in switch statement" once this becomes available.

@Override
public String render(ValueLayout.OfLong longLayout, long value) {
return renderer.apply(longLayout, value);
}

@Override
public String render(ValueLayout.OfFloat floatLayout, float value) {
return renderer.apply(floatLayout, value);
}

@Override
public String render(ValueLayout.OfDouble doubleLayout, double value) {
return renderer.apply(doubleLayout, value);
}

@Override
public String render(ValueLayout.OfAddress addressLayout, MemorySegment value) {
return renderer.apply(addressLayout, value);
if (layout instanceof ValueLayout.OfBoolean ofBoolean && o instanceof Boolean b) {
return Boolean.toString(b);
}
if (layout instanceof ValueLayout.OfByte ofByte && o instanceof Byte b) {
return Byte.toString(b);
}
if (layout instanceof ValueLayout.OfShort ofShort && o instanceof Short s) {
return Short.toString(s);
}
if (layout instanceof ValueLayout.OfInt ofInt && o instanceof Integer i) {
return Integer.toString(i);
}
if (layout instanceof ValueLayout.OfLong ofLong && o instanceof Long l) {
return Long.toString(l);
}
if (layout instanceof ValueLayout.OfFloat ofFloat && o instanceof Float f) {
return Float.toString(f);
}
if (layout instanceof ValueLayout.OfDouble ofDouble && o instanceof Double d) {
return Double.toString(d);
}
if (layout instanceof ValueLayout.OfChar ofChar && o instanceof Character c) {
return Character.toString(c);
}
if (layout instanceof ValueLayout.OfAddress ofAddress && o instanceof MemorySegment m) {
return String.format("0x%0" + (ValueLayout.ADDRESS.byteSize() * 2) + "X", m.address());
}
throw new UnsupportedOperationException("layout " + layout + " for " + o.getClass().getName() + " not supported");
}

@Override
public String toString() {
return "SingleFunctionValueLayoutRenderer{" +
"renderer=" + renderer +
'}';
return singletonToString(StandardValueLayoutRenderer.class);
}
}

}
private static String singletonToString(Class<?> implementingClass) {
return "The " + implementingClass.getName() + " singleton";
}

}
Loading