Skip to content

Commit

Permalink
Clean up dangling source information from macro expansion
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 629500228
  • Loading branch information
l46kok authored and copybara-github committed May 2, 2024
1 parent be5aa9b commit 01e2977
Show file tree
Hide file tree
Showing 7 changed files with 192 additions and 36 deletions.
22 changes: 17 additions & 5 deletions common/src/main/java/dev/cel/common/CelSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public final class CelSource {
private CelSource(Builder builder) {
this.codePoints = checkNotNull(builder.codePoints);
this.description = checkNotNull(builder.description);
this.positions = checkNotNull(builder.positions.buildOrThrow());
this.positions = checkNotNull(ImmutableMap.copyOf(builder.positions));
this.lineOffsets = checkNotNull(ImmutableList.copyOf(builder.lineOffsets));
this.macroCalls = checkNotNull(ImmutableMap.copyOf(builder.macroCalls));
this.extensions = checkNotNull(builder.extensions.build());
Expand Down Expand Up @@ -208,7 +208,7 @@ public static final class Builder {

private final CelCodePointArray codePoints;
private final List<Integer> lineOffsets;
private final ImmutableMap.Builder<Long, Integer> positions;
private final Map<Long, Integer> positions;
private final Map<Long, CelExpr> macroCalls;
private final ImmutableSet.Builder<Extension> extensions;

Expand All @@ -221,7 +221,7 @@ private Builder() {
private Builder(CelCodePointArray codePoints, List<Integer> lineOffsets) {
this.codePoints = checkNotNull(codePoints);
this.lineOffsets = checkNotNull(lineOffsets);
this.positions = ImmutableMap.builder();
this.positions = new HashMap<>();
this.macroCalls = new HashMap<>();
this.extensions = ImmutableSet.builder();
this.description = "";
Expand Down Expand Up @@ -261,6 +261,18 @@ public Builder addPositions(long exprId, int position) {
return this;
}

@CanIgnoreReturnValue
public Builder clearPositions() {
this.positions.clear();
return this;
}

@CanIgnoreReturnValue
public Builder removePositions(long exprId) {
this.positions.remove(exprId);
return this;
}

@CanIgnoreReturnValue
public Builder addMacroCalls(long exprId, CelExpr expr) {
this.macroCalls.put(exprId, expr);
Expand Down Expand Up @@ -329,8 +341,8 @@ public Optional<CelSourceLocation> getOffsetLocation(int offset) {
}

@CheckReturnValue
public ImmutableMap<Long, Integer> getPositionsMap() {
return this.positions.buildOrThrow();
public Map<Long, Integer> getPositionsMap() {
return this.positions;
}

@CheckReturnValue
Expand Down
26 changes: 8 additions & 18 deletions common/src/main/java/dev/cel/common/ast/CelExprFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,12 @@
/** Factory for generating expression nodes. */
@Internal
public class CelExprFactory {

private final CelExprIdGeneratorFactory.ExprIdGenerator idGenerator;
private long exprId = 0L;

public static CelExprFactory newInstance() {
return new CelExprFactory();
}

public static CelExprFactory newInstance(
CelExprIdGeneratorFactory.ExprIdGenerator exprIdGenerator) {
return new CelExprFactory(exprIdGenerator);
}

/** Create a new constant expression. */
public final CelExpr newConstant(CelConstant constant) {
return CelExpr.newBuilder().setId(nextExprId()).setConstant(constant).build();
Expand Down Expand Up @@ -545,19 +539,15 @@ public final CelExpr newSelect(CelExpr operand, String field, boolean testOnly)

/** Returns the next unique expression ID. */
protected long nextExprId() {
return idGenerator.generate(
/* exprId= */ -1); // Unconditionally generate next unique ID (i.e: no renumbering).
return ++exprId;
}

protected CelExprFactory() {
this(CelExprIdGeneratorFactory.newMonotonicIdGenerator(0));
/** Attempts to decrement the next expr ID if possible. */
protected void maybeDeleteId(long id) {
if (id == exprId - 1) {
exprId--;
}
}

private CelExprFactory(CelExprIdGeneratorFactory.MonotonicIdGenerator idGenerator) {
this((unused) -> idGenerator.nextExprId());
}

private CelExprFactory(CelExprIdGeneratorFactory.ExprIdGenerator exprIdGenerator) {
idGenerator = exprIdGenerator;
}
protected CelExprFactory() {}
}
12 changes: 0 additions & 12 deletions common/src/test/java/dev/cel/common/ast/CelExprFactoryTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

import static com.google.common.truth.Truth.assertThat;

import dev.cel.common.ast.CelExprIdGeneratorFactory.StableIdGenerator;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
Expand All @@ -38,15 +37,4 @@ public void nextExprId_startingDefaultIsOne() {
assertThat(exprFactory.nextExprId()).isEqualTo(1L);
assertThat(exprFactory.nextExprId()).isEqualTo(2L);
}

@Test
public void nextExprId_usingStableIdGenerator() {
StableIdGenerator stableIdGenerator = CelExprIdGeneratorFactory.newStableIdGenerator(0);
CelExprFactory exprFactory = CelExprFactory.newInstance(stableIdGenerator::nextExprId);

assertThat(exprFactory.nextExprId()).isEqualTo(1L);
assertThat(exprFactory.nextExprId()).isEqualTo(2L);
assertThat(stableIdGenerator.hasId(-1)).isFalse();
assertThat(stableIdGenerator.hasId(0)).isFalse();
}
}
8 changes: 8 additions & 0 deletions parser/src/main/java/dev/cel/parser/Parser.java
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,7 @@ private Optional<CelExpr> visitMacro(
CelExpr.newBuilder().setCall(callExpr.build()).build());
}

exprFactory.maybeDeleteId(expr.id());
return expandedMacro;
}

Expand Down Expand Up @@ -895,6 +896,7 @@ private CelExpr macroOrCall(
ImmutableList<CelExpr> arguments = visitExprListContext(args);
Optional<CelExpr> errorArg = arguments.stream().filter(ERROR::equals).findAny();
if (errorArg.isPresent()) {
sourceInfo.clearPositions();
// Any arguments passed in to the macro may fail parsing.
// Stop the macro expansion in this case as the result of the macro will be a parse failure.
return ERROR;
Expand Down Expand Up @@ -1109,6 +1111,12 @@ private long nextExprId(int position) {
return exprId;
}

@Override
protected void maybeDeleteId(long id) {
sourceInfo.removePositions(id);
super.maybeDeleteId(id);
}

@Override
public long copyExprId(long id) {
return nextExprId(getPosition(id));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import com.google.protobuf.Descriptors.FieldDescriptor;
import com.google.protobuf.Descriptors.OneofDescriptor;
import com.google.testing.junit.testparameterinjector.TestParameterInjector;
import dev.cel.common.CelAbstractSyntaxTree;
import dev.cel.common.CelOptions;
import dev.cel.common.CelProtoAbstractSyntaxTree;
import dev.cel.common.CelSource;
Expand Down Expand Up @@ -248,6 +249,11 @@ public void parser_errors() {
runTest(parserWithoutOptionalSupport, "[?a, ?b]");
}

@Test
public void source_info() throws Exception {
runSourceInfoTest("[{}, {'field': true}].exists(i, has(i.field))");
}

private void runTest(CelParser parser, String expression) {
runTest(parser, expression, true);
}
Expand Down Expand Up @@ -287,6 +293,15 @@ private void runTest(CelParser parser, String expression, boolean validateParseO
testOutput().println();
}

private void runSourceInfoTest(String expression) throws Exception {
CelAbstractSyntaxTree ast = PARSER.parse(expression).getAst();
SourceInfo sourceInfo =
CelProtoAbstractSyntaxTree.fromCelAst(ast).toParsedExpr().getSourceInfo();
testOutput().println("I: " + expression);
testOutput().println("=====>");
testOutput().println("S: " + sourceInfo);
}

private String convertMacroCallsToString(SourceInfo sourceInfo) {
KindAndIdAdorner macroCallsAdorner = new KindAndIdAdorner(sourceInfo);
// Sort in ascending order so that nested macro calls are always in the same order for tests
Expand Down
2 changes: 1 addition & 1 deletion parser/src/test/resources/parser.baseline
Original file line number Diff line number Diff line change
Expand Up @@ -1543,7 +1543,7 @@ L: noop_macro(
I: get_constant_macro()
=====>
P: 10^#1:int64#
L: 10^#1[1,18]#
L: 10^#1[NO_POS]#
M: get_constant_macro()^#0:Expr.Call#

I: a.?b[?0] && a[?c]
Expand Down
143 changes: 143 additions & 0 deletions parser/src/test/resources/source_info.baseline
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
I: [{}, {'field': true}].exists(i, has(i.field))
=====>
S: location: "<input>"
line_offsets: 46
positions {
key: 1
value: 0
}
positions {
key: 2
value: 1
}
positions {
key: 3
value: 5
}
positions {
key: 4
value: 13
}
positions {
key: 5
value: 6
}
positions {
key: 6
value: 15
}
positions {
key: 8
value: 29
}
positions {
key: 10
value: 36
}
positions {
key: 11
value: 37
}
positions {
key: 12
value: 35
}
positions {
key: 13
value: 28
}
positions {
key: 14
value: 28
}
positions {
key: 15
value: 28
}
positions {
key: 16
value: 28
}
positions {
key: 17
value: 28
}
positions {
key: 18
value: 28
}
positions {
key: 19
value: 28
}
positions {
key: 20
value: 28
}
macro_calls {
key: 12
value {
call_expr {
function: "has"
args {
id: 11
select_expr {
operand {
id: 10
ident_expr {
name: "i"
}
}
field: "field"
}
}
}
}
}
macro_calls {
key: 20
value {
call_expr {
target {
id: 1
list_expr {
elements {
id: 2
struct_expr {
}
}
elements {
id: 3
struct_expr {
entries {
id: 4
map_key {
id: 5
const_expr {
string_value: "field"
}
}
value {
id: 6
const_expr {
bool_value: true
}
}
}
}
}
}
}
function: "exists"
args {
id: 8
ident_expr {
name: "i"
}
}
args {
id: 12
}
}
}
}

0 comments on commit 01e2977

Please sign in to comment.