Skip to content

Commit 253cd26

Browse files
authored
fixup jmespath multiselect codegen (#551)
1 parent a4c9efc commit 253cd26

File tree

5 files changed

+70
-49
lines changed

5 files changed

+70
-49
lines changed

codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoJmespathExpressionGenerator.java

+36-19
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@
2121
import static software.amazon.smithy.go.codegen.util.ShapeUtil.BOOL_SHAPE;
2222
import static software.amazon.smithy.go.codegen.util.ShapeUtil.INT_SHAPE;
2323
import static software.amazon.smithy.go.codegen.util.ShapeUtil.STRING_SHAPE;
24-
import static software.amazon.smithy.go.codegen.util.ShapeUtil.listOf;
2524
import static software.amazon.smithy.utils.StringUtils.capitalize;
2625

26+
import java.util.HashMap;
2727
import java.util.List;
2828
import java.util.Map;
2929
import software.amazon.smithy.codegen.core.CodegenException;
@@ -64,6 +64,12 @@ public class GoJmespathExpressionGenerator {
6464

6565
private int idIndex = 0;
6666

67+
// as we traverse an expression, we may produce intermediate "synthetic" lists - e.g. list of string, list of list
68+
// of string, etc.
69+
// we may need to pull the member shapes back out later, but they're not guaranteed to be in the model - so keep a
70+
// shadow map of synthetic -> member to short-circuit the model lookup
71+
private final Map<Shape, Shape> synthetics = new HashMap<>();
72+
6773
public GoJmespathExpressionGenerator(GoCodegenContext ctx, GoWriter writer) {
6874
this.ctx = ctx;
6975
this.writer = writer;
@@ -121,8 +127,17 @@ private Variable visitMultiSelectList(MultiSelectListExpression expr, Variable c
121127
var first = items.get(0);
122128

123129
var ident = nextIdent();
124-
writer.write("$L := []$P{$L}", ident, first.type,
125-
String.join(",", items.stream().map(it -> it.ident).toList()));
130+
writer.write("$L := []$T{}", ident, first.type);
131+
for (var item : items) {
132+
if (isPointable(item.type)) {
133+
writer.write("""
134+
if $2L != nil {
135+
$1L = append($1L, *$2L)
136+
}""", ident, item.ident);
137+
} else {
138+
writer.write("$1L = append($1L, $2L)", ident, item.ident);
139+
}
140+
}
126141

127142
return new Variable(listOf(first.shape), ident, sliceOf(first.type));
128143
}
@@ -247,11 +262,13 @@ private Variable visitProjection(ProjectionExpression expr, Variable current) {
247262
writer.indent();
248263
// projected.shape is the _member_ of the resulting list
249264
var projected = visit(expr.getRight(), new Variable(leftMember, "v", leftSymbol));
250-
if (isPointable(lookahead.type)) { // projections implicitly filter out nil evaluations of RHS
265+
if (isPointable(lookahead.type)) { // projections implicitly filter out nil evaluations of RHS...
266+
var deref = lookahead.shape instanceof CollectionShape || lookahead.shape instanceof MapShape
267+
? "" : "*"; // ...but slices/maps do not get dereferenced
251268
writer.write("""
252-
if $2L != nil {
253-
$1L = append($1L, *$2L)
254-
}""", ident, projected.ident);
269+
if $1L != nil {
270+
$2L = append($2L, $3L$1L)
271+
}""", projected.ident, ident, deref);
255272
} else {
256273
writer.write("$1L = append($1L, $2L)", ident, projected.ident);
257274
}
@@ -348,22 +365,22 @@ private String nextIdent() {
348365
return "v" + idIndex;
349366
}
350367

368+
private Shape listOf(Shape shape) {
369+
var list = ShapeUtil.listOf(shape);
370+
synthetics.putIfAbsent(list, shape);
371+
return list;
372+
}
373+
351374
private Shape expectMember(CollectionShape shape) {
352-
return switch (shape.getMember().getTarget().toString()) {
353-
case "smithy.go.synthetic#StringList" -> listOf(STRING_SHAPE);
354-
case "smithy.go.synthetic#IntegerList" -> listOf(INT_SHAPE);
355-
case "smithy.go.synthetic#BooleanList" -> listOf(BOOL_SHAPE);
356-
default -> ShapeUtil.expectMember(ctx.model(), shape);
357-
};
375+
return synthetics.containsKey(shape)
376+
? synthetics.get(shape)
377+
: ShapeUtil.expectMember(ctx.model(), shape);
358378
}
359379

360380
private Shape expectMember(MapShape shape) {
361-
return switch (shape.getValue().getTarget().toString()) {
362-
case "smithy.go.synthetic#StringList" -> listOf(STRING_SHAPE);
363-
case "smithy.go.synthetic#IntegerList" -> listOf(INT_SHAPE);
364-
case "smithy.go.synthetic#BooleanList" -> listOf(BOOL_SHAPE);
365-
default -> ShapeUtil.expectMember(ctx.model(), shape);
366-
};
381+
return synthetics.containsKey(shape)
382+
? synthetics.get(shape)
383+
: ShapeUtil.expectMember(ctx.model(), shape);
367384
}
368385

369386
// helper to generate comparisons from two results, automatically handling any dereferencing in the process

codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/endpoints/EndpointParameterOperationBindingsGenerator.java

+1-23
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,11 @@
2323
import software.amazon.smithy.go.codegen.GoJmespathExpressionGenerator;
2424
import software.amazon.smithy.go.codegen.GoWriter;
2525
import software.amazon.smithy.go.codegen.SmithyGoTypes;
26-
import software.amazon.smithy.go.codegen.knowledge.GoPointableIndex;
2726
import software.amazon.smithy.jmespath.JmespathExpression;
2827
import software.amazon.smithy.model.node.Node;
2928
import software.amazon.smithy.model.shapes.OperationShape;
3029
import software.amazon.smithy.model.shapes.StructureShape;
3130
import software.amazon.smithy.rulesengine.language.EndpointRuleSet;
32-
import software.amazon.smithy.rulesengine.language.syntax.Identifier;
33-
import software.amazon.smithy.rulesengine.language.syntax.parameters.ParameterType;
3431
import software.amazon.smithy.rulesengine.traits.ContextParamTrait;
3532
import software.amazon.smithy.rulesengine.traits.EndpointRuleSetTrait;
3633
import software.amazon.smithy.rulesengine.traits.OperationContextParamDefinition;
@@ -103,33 +100,14 @@ private GoWriter.Writable generateOperationContextParamBindings() {
103100
}
104101

105102
private GoWriter.Writable generateOpContextParamBinding(String paramName, OperationContextParamDefinition def) {
106-
var param = rules.getParameters().get(Identifier.of(paramName)).get();
107103
var expr = JmespathExpression.parse(def.getPath());
108104

109105
return writer -> {
110106
var generator = new GoJmespathExpressionGenerator(ctx, writer);
111107

112108
writer.write("func() {"); // contain the scope for each binding
113109
var result = generator.generate(expr, new GoJmespathExpressionGenerator.Variable(input, "in"));
114-
115-
if (param.getType().equals(ParameterType.STRING_ARRAY)) {
116-
// projections can result in either []string OR []*string -- if the latter, we have to unwrap
117-
var target = result.shape().asListShape().get().getMember().getTarget();
118-
if (GoPointableIndex.of(ctx.model()).isPointable(target)) {
119-
writer.write("""
120-
deref := []string{}
121-
for _, v := range $L {
122-
if v != nil {
123-
deref = append(deref, *v)
124-
}
125-
}
126-
p.$L = deref""", result.ident(), capitalize(paramName));
127-
} else {
128-
writer.write("p.$L = $L", capitalize(paramName), result.ident());
129-
}
130-
} else {
131-
writer.write("p.$L = $L", capitalize(paramName), result.ident());
132-
}
110+
writer.write("p.$L = $L", capitalize(paramName), result.ident());
133111
writer.write("}()");
134112
};
135113
}

codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/endpoints/EndpointResolverGenerator.java

+18-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,10 @@
4343
import software.amazon.smithy.rulesengine.language.Endpoint;
4444
import software.amazon.smithy.rulesengine.language.EndpointRuleSet;
4545
import software.amazon.smithy.rulesengine.language.error.RuleError;
46+
import software.amazon.smithy.rulesengine.language.evaluation.type.ArrayType;
4647
import software.amazon.smithy.rulesengine.language.evaluation.type.OptionalType;
48+
import software.amazon.smithy.rulesengine.language.evaluation.type.StringType;
49+
import software.amazon.smithy.rulesengine.language.evaluation.type.Type;
4750
import software.amazon.smithy.rulesengine.language.syntax.Identifier;
4851
import software.amazon.smithy.rulesengine.language.syntax.expressions.Expression;
4952
import software.amazon.smithy.rulesengine.language.syntax.expressions.ExpressionVisitor;
@@ -304,14 +307,21 @@ private GoWriter.Writable generateRule(Rule rule, List<Condition> conditions, Sc
304307
}
305308

306309
if (fn.type() instanceof OptionalType || isConditionalFnResultOptional(condition, fn)) {
310+
// []string (e.g. in endpoint params) needs to be casted as stringSlice instead of dereferenced for index
311+
// operations
312+
var isStringSlice = false;
313+
if (fn.type() instanceof OptionalType opt) {
314+
isStringSlice = isStringSlice(opt.inner());
315+
}
307316
return goTemplate("""
308317
if exprVal := $target:W; exprVal != nil {
309-
$conditionIdent:L := *exprVal
318+
$conditionIdent:L := $exprVal:L
310319
_ = $conditionIdent:L
311320
$next:W
312321
}
313322
""",
314323
MapUtils.of(
324+
"exprVal", isStringSlice ? "stringSlice(exprVal)" : "*exprVal",
315325
"conditionIdent", conditionIdentifier,
316326
"target", generator.generate(fn),
317327
"next", generateRule(
@@ -601,4 +611,11 @@ private GoWriter.Writable generateStringSliceHelper() {
601611
return &v
602612
}""");
603613
}
614+
615+
private boolean isStringSlice(Type type) {
616+
if (!(type instanceof ArrayType array)) {
617+
return false;
618+
}
619+
return array.getMember() instanceof StringType;
620+
}
604621
}

codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/util/ShapeUtil.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,15 @@
2727

2828
public final class ShapeUtil {
2929
public static final StringShape STRING_SHAPE = StringShape.builder()
30-
.id("smithy.api#String")
30+
.id("smithy.api#PrimitiveString")
3131
.build();
3232

3333
public static final IntegerShape INT_SHAPE = IntegerShape.builder()
34-
.id("smithy.api#Integer")
34+
.id("smithy.api#PrimitiveInteger")
3535
.build();
3636

3737
public static final BooleanShape BOOL_SHAPE = BooleanShape.builder()
38-
.id("smithy.api#Boolean")
38+
.id("smithy.api#PrimitiveBoolean")
3939
.build();
4040

4141
private ShapeUtil() {}

codegen/smithy-go-codegen/src/test/java/software/amazon/smithy/go/codegen/GoJmespathExpressionGeneratorTest.java

+12-3
Original file line numberDiff line numberDiff line change
@@ -489,7 +489,13 @@ public void testMultiSelect() {
489489
assertThat(writer.toString(), Matchers.containsString("""
490490
v1 := input.SimpleShape
491491
v2 := input.SimpleShape2
492-
v3 := []*string{v1,v2}
492+
v3 := []string{}
493+
if v1 != nil {
494+
v3 = append(v3, *v1)
495+
}
496+
if v2 != nil {
497+
v3 = append(v3, *v2)
498+
}
493499
"""));
494500
}
495501

@@ -510,9 +516,12 @@ public void testMultiSelectFlatten() {
510516
var v2 [][]string
511517
for _, v := range v1 {
512518
v3 := v.Key
513-
v4 := []*string{v3}
519+
v4 := []string{}
520+
if v3 != nil {
521+
v4 = append(v4, *v3)
522+
}
514523
if v4 != nil {
515-
v2 = append(v2, *v4)
524+
v2 = append(v2, v4)
516525
}
517526
}
518527
var v5 []string

0 commit comments

Comments
 (0)