Skip to content

Commit

Permalink
8324789: Add line number information to code models
Browse files Browse the repository at this point in the history
Reviewed-by: mcimadamore
  • Loading branch information
Paul Sandoz committed Apr 25, 2024
1 parent 628a931 commit 9f35792
Show file tree
Hide file tree
Showing 14 changed files with 487 additions and 42 deletions.
47 changes: 47 additions & 0 deletions src/java.base/share/classes/java/lang/reflect/code/Location.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package java.lang.reflect.code;

/**
* Source location information.
*
* @param sourceRef the reference to the source
* @param line the line in the source
* @param column the column in the source
*/
public record Location(String sourceRef, int line, int column) {

/**
* The location value, {@code null}, indicating no location information.
*/
public static final Location NO_LOCATION = null;

public Location(int line, int column) {
this(null, line, column);
}

@Override
public String toString() {
StringBuilder s = new StringBuilder();
s.append(line).append(":").append(column);
if (sourceRef != null) {
s.append(":").append(sourceRef);
}
return s.toString();
}

public static Location fromString(String s) {
String[] split = s.split(":", 3);
if (split.length < 2) {
throw new IllegalArgumentException();
}

int line = Integer.parseInt(split[0]);
int column = Integer.parseInt(split[1]);
String sourceRef;
if (split.length == 3) {
sourceRef = split[2];
} else {
sourceRef = null;
}
return new Location(sourceRef, line, column);
}
}
26 changes: 26 additions & 0 deletions src/java.base/share/classes/java/lang/reflect/code/Op.java
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,9 @@ public Op op() {
// Set when op is bound to block, otherwise null when unbound
Result result;

// null if not specified
Location location;

final String name;

final List<Value> operands;
Expand All @@ -196,6 +199,7 @@ public Op op() {
*/
protected Op(Op that, CopyContext cc) {
this(that.name, cc.getValues(that.operands));
this.location = that.location;
}

/**
Expand Down Expand Up @@ -246,6 +250,28 @@ protected Op(String name, List<? extends Value> operands) {
this.operands = List.copyOf(operands);
}

/**
* Sets the originating source location of this operation, if unbound.
*
* @param l the location, the {@link Location#NO_LOCATION} value indicates the location is not specified.
* @throws IllegalStateException if this operation is bound
*/
public final void setLocation(Location l) {
// @@@ Fail if location != null?
if (result != null && result.block.isBound()) {
throw new IllegalStateException();
}

location = l;
}

/**
* {@return the originating source location of this operation, otherwise {@code null} if not specified}
*/
public final Location location() {
return location;
}

/**
* Returns this operation's parent block, otherwise {@code null} if the operation is not assigned to a block.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public interface OpTransformer extends BiFunction<Block.Builder, Op, Block.Build
* A copying transformer that applies the operation to the block builder, and returning the block builder.
*/
OpTransformer COPYING_TRANSFORMER = (block, op) -> {
block.apply(op);
block.op(op);
return block;
};

Expand All @@ -46,6 +46,15 @@ public interface OpTransformer extends BiFunction<Block.Builder, Op, Block.Build
*/
OpTransformer NOOP_TRANSFORMER = (block, op) -> block;

/**
* A transformer that drops location information from operations.
*/
OpTransformer DROP_LOCATION_TRANSFORMER = (block, op) -> {
Op.Result r = block.op(op);
r.op().setLocation(Location.NO_LOCATION);
return block;
};

/**
* Transforms a given operation to zero or more other operations appended to the
* given block builder. Returns a block builder to be used for appending further operations, such
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1989,6 +1989,10 @@ public abstract static sealed class VarAccessOp extends OpWithDefinition impleme
super(opdef);
}

VarAccessOp(VarAccessOp that, CopyContext cc) {
super(that, cc);
}

VarAccessOp(String name, List<Value> operands) {
super(name, operands);
}
Expand Down Expand Up @@ -2025,7 +2029,7 @@ public VarLoadOp(OpDefinition opdef) {
}

VarLoadOp(VarLoadOp that, CopyContext cc) {
this(cc.getValues(that.operands()));
super(that, cc);
}

VarLoadOp(List<Value> varValue) {
Expand Down Expand Up @@ -2066,7 +2070,7 @@ public VarStoreOp(OpDefinition opdef) {
}

VarStoreOp(VarStoreOp that, CopyContext cc) {
this(cc.getValues(that.operands()));
super(that, cc);
}

VarStoreOp(List<Value> values) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,19 @@

package java.lang.reflect.code.op;

import java.lang.reflect.code.CopyContext;
import java.lang.reflect.code.Op;
import java.lang.reflect.code.Value;
import java.lang.reflect.code.*;
import java.util.List;
import java.util.Map;

/**
* An operation that may be constructed with an operation {@link OpDefinition definition}.
*/
public abstract class OpWithDefinition extends Op {
final Map<String, Object> attributes;

/**
* The attribute name associated with the location attribute.
*/
public static final String ATTRIBUTE_LOCATION = "loc";

/**
* Constructs an operation by copying given operation.
Expand All @@ -47,8 +49,6 @@ public abstract class OpWithDefinition extends Op {
*/
protected OpWithDefinition(Op that, CopyContext cc) {
super(that, cc);

this.attributes = Map.of();
}

/**
Expand All @@ -59,8 +59,6 @@ protected OpWithDefinition(Op that, CopyContext cc) {
*/
protected OpWithDefinition(String name, List<? extends Value> operands) {
super(name, operands);

this.attributes = Map.of();
}

/**
Expand All @@ -77,12 +75,22 @@ protected OpWithDefinition(String name, List<? extends Value> operands) {
*/
protected OpWithDefinition(OpDefinition def) {
super(def.name(), def.operands());
setLocation(extractLocation(def));
}

this.attributes = Map.copyOf(def.attributes());
static Location extractLocation(OpDefinition def) {
Object v = def.attributes().get(ATTRIBUTE_LOCATION);
return switch(v) {
case String s -> Location.fromString(s);
case Location loc -> loc;
case null -> null;
default -> throw new UnsupportedOperationException("Unsupported location value:" + v);
};
}

@Override
public Map<String, Object> attributes() {
return attributes;
Location l = location();
return l == null ? Map.of() : Map.of(ATTRIBUTE_LOCATION, l);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,10 @@ Value buildAttributeValue(Object value) {
case TypeElement f -> {
yield buildType(f);
}
case Location l -> {
// @@@ Construct location explicitly
yield builder.op(constant(J_L_STRING, l.toString()));
}
case Object o when value == Op.NULL_ATTRIBUTE_VALUE -> {
yield builder.op(fieldLoad(FieldRef.field(Op.class, "NULL_ATTRIBUTE_VALUE", Object.class)));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import java.io.UncheckedIOException;
import java.io.Writer;
import java.lang.reflect.code.*;
import java.lang.reflect.code.op.OpWithDefinition;
import java.lang.reflect.code.type.JavaType;
import java.util.HashMap;
import java.util.Map;
Expand Down Expand Up @@ -161,9 +162,6 @@ void out(int i) {
}
}

final Function<CodeItem, String> namer;
final IndentWriter w;

/**
* Computes global names for blocks and values in a code model.
* <p>
Expand Down Expand Up @@ -214,7 +212,7 @@ public static Map<CodeItem, String> computeGlobalNames(Op root) {
* then character stream will be flushed.
*
* @param w the character stream
* @param op the code modelz
* @param op the code model
*/
public static void writeTo(Writer w, Op op) {
OpWriter ow = new OpWriter(w);
Expand All @@ -227,24 +225,105 @@ public static void writeTo(Writer w, Op op) {
}
}

/**
* Writes a code model (an operation) to the character stream.
* <p>
* A carriage return will be written after the model is writen, and
* then character stream will be flushed.
*
* @param w the character stream
* @param op the code model
* @param options the writer options
*/
public static void writeTo(Writer w, Op op, Option... options) {
OpWriter ow = new OpWriter(w, options);
ow.writeOp(op);
ow.write("\n");
try {
w.flush();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}

/**
* An option that affects the writing operations.
*/
public sealed interface Option {
}

/**
* An option describing the function to use for naming code items.
*/
public sealed interface CodeItemNamerOption extends Option
permits NamerOptionImpl {

static CodeItemNamerOption of(Function<CodeItem, String> named) {
return new NamerOptionImpl(named);
}

static CodeItemNamerOption defaultValue() {
return of(new GlobalValueBlockNaming());
}

Function<CodeItem, String> namer();
}
private record NamerOptionImpl(Function<CodeItem, String> namer) implements CodeItemNamerOption {
}

/**
* An option describing whether location information should be written or dropped.
*/
public enum LocationOption implements Option {
/** Writes location */
WRITE_LOCATION,
/** Drops location */
DROP_LOCATION;

public static LocationOption defaultValue() {
return WRITE_LOCATION;
}
}

final Function<CodeItem, String> namer;
final IndentWriter w;
final boolean dropLocation;

/**
* Creates a writer of code models (operations) to their textual form.
*
* @param w the character stream writer to write the textual form.
*/
public OpWriter(Writer w) {
this(w, new GlobalValueBlockNaming());
this.w = new IndentWriter(w);
this.namer = new GlobalValueBlockNaming();
this.dropLocation = false;
}

/**
* Creates a writer of code models (operations) to their textual form.
*
* @param w the character stream writer to write the textual form.
* @param namer the function that computes names for blocks and values.
* @param w the character stream writer to write the textual form.
* @param options the writer options
*/
public OpWriter(Writer w, Function<CodeItem, String> namer) {
this.namer = namer;
public OpWriter(Writer w, Option... options) {
Function<CodeItem, String> namer = null;
boolean dropLocation = false;
for (Option option : options) {
switch (option) {
case CodeItemNamerOption namerOption -> {
namer = namerOption.namer();
}
case LocationOption locationOption -> {
dropLocation = locationOption ==
LocationOption.DROP_LOCATION;
}
}
}

this.w = new IndentWriter(w);
this.namer = (namer == null) ? new GlobalValueBlockNaming() : namer;
this.dropLocation = dropLocation;
}

/**
Expand Down Expand Up @@ -272,9 +351,15 @@ public void writeOp(Op op) {
writeSpaceSeparatedList(op.successors(), this::writeSuccessor);
}

if (!op.attributes().isEmpty()) {
Map<String, Object> attributes = op.attributes();
if (dropLocation && !attributes.isEmpty() &&
attributes.containsKey(OpWithDefinition.ATTRIBUTE_LOCATION)) {
attributes = new HashMap<>(attributes);
attributes.remove(OpWithDefinition.ATTRIBUTE_LOCATION);
}
if (!attributes.isEmpty()) {
write(" ");
writeSpaceSeparatedList(op.attributes().entrySet(), e -> writeAttribute(e.getKey(), e.getValue()));
writeSpaceSeparatedList(attributes.entrySet(), e -> writeAttribute(e.getKey(), e.getValue()));
}

if (!op.bodies().isEmpty()) {
Expand Down
Loading

0 comments on commit 9f35792

Please sign in to comment.