Skip to content

Commit 43d4b0d

Browse files
The type map references when keyed by attribute may be incorrect. (#361)
* The type map references when keyed by attribute may be incorrect. Fixed to refer to the original expression id. * Added test for verifying optimization paths being performed correctly
1 parent d486530 commit 43d4b0d

2 files changed

Lines changed: 113 additions & 9 deletions

File tree

interpreter/interpreter_test.go

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,47 @@ var (
540540
"input": "kathmandu",
541541
},
542542
},
543+
{
544+
name: "nested_proto_field",
545+
expr: `pb3.single_nested_message.bb`,
546+
types: []proto.Message{&proto3pb.TestAllTypes{}},
547+
env: []*exprpb.Decl{
548+
decls.NewVar("pb3",
549+
decls.NewObjectType("google.expr.proto3.test.TestAllTypes")),
550+
},
551+
in: map[string]interface{}{
552+
"pb3": &proto3pb.TestAllTypes{
553+
NestedType: &proto3pb.TestAllTypes_SingleNestedMessage{
554+
SingleNestedMessage: &proto3pb.TestAllTypes_NestedMessage{
555+
Bb: 1234,
556+
},
557+
},
558+
},
559+
},
560+
out: types.Int(1234),
561+
},
562+
{
563+
name: "nested_proto_field_with_index",
564+
expr: `pb3.map_int64_nested_type[0].child.payload.single_int32 == 1`,
565+
types: []proto.Message{&proto3pb.TestAllTypes{}},
566+
env: []*exprpb.Decl{
567+
decls.NewVar("pb3",
568+
decls.NewObjectType("google.expr.proto3.test.TestAllTypes")),
569+
},
570+
in: map[string]interface{}{
571+
"pb3": &proto3pb.TestAllTypes{
572+
MapInt64NestedType: map[int64]*proto3pb.NestedTestAllTypes{
573+
0: &proto3pb.NestedTestAllTypes{
574+
Child: &proto3pb.NestedTestAllTypes{
575+
Payload: &proto3pb.TestAllTypes{
576+
SingleInt32: 1,
577+
},
578+
},
579+
},
580+
},
581+
},
582+
},
583+
},
543584
{
544585
name: "or_true_1st",
545586
expr: `ai == 20 || ar["foo"] == "bar"`,
@@ -1007,6 +1048,53 @@ func TestInterpreter(t *testing.T) {
10071048
}
10081049
}
10091050

1051+
func TestInterpreter_ProtoAttributeOpt(t *testing.T) {
1052+
inst, _, err := program(&testCase{
1053+
name: "nested_proto_field_with_index",
1054+
expr: `pb3.map_int64_nested_type[0].child.payload.single_int32`,
1055+
types: []proto.Message{&proto3pb.TestAllTypes{}},
1056+
env: []*exprpb.Decl{
1057+
decls.NewVar("pb3",
1058+
decls.NewObjectType("google.expr.proto3.test.TestAllTypes")),
1059+
},
1060+
in: map[string]interface{}{
1061+
"pb3": &proto3pb.TestAllTypes{
1062+
MapInt64NestedType: map[int64]*proto3pb.NestedTestAllTypes{
1063+
0: &proto3pb.NestedTestAllTypes{
1064+
Child: &proto3pb.NestedTestAllTypes{
1065+
Payload: &proto3pb.TestAllTypes{
1066+
SingleInt32: 1,
1067+
},
1068+
},
1069+
},
1070+
},
1071+
},
1072+
},
1073+
}, Optimize())
1074+
if err != nil {
1075+
t.Fatal(err)
1076+
}
1077+
attr, ok := inst.(InterpretableAttribute)
1078+
if !ok {
1079+
t.Fatalf("got %v, expected attribute value", inst)
1080+
}
1081+
absAttr, ok := attr.Attr().(NamespacedAttribute)
1082+
if !ok {
1083+
t.Fatalf("got %v, expected global attribute", attr.Attr())
1084+
}
1085+
quals := absAttr.Qualifiers()
1086+
if len(quals) != 5 {
1087+
t.Errorf("got %d qualifiers, wanted 5", len(quals))
1088+
}
1089+
if !isFieldQual(quals[0], "map_int64_nested_type") ||
1090+
!isConstQual(quals[1], types.IntZero) ||
1091+
!isFieldQual(quals[2], "child") ||
1092+
!isFieldQual(quals[3], "payload") ||
1093+
!isFieldQual(quals[4], "single_int32") {
1094+
t.Error("unoptimized qualifier types present in optimized attribute")
1095+
}
1096+
}
1097+
10101098
func TestInterpreter_LogicalAndMissingType(t *testing.T) {
10111099
src := common.NewTextSource(`a && TestProto{c: true}.c`)
10121100
parsed, errors := parser.Parse(src)
@@ -1333,3 +1421,19 @@ func base64_encode(val ref.Val) ref.Val {
13331421
}
13341422
return types.String(base64.StdEncoding.EncodeToString([]byte(str)))
13351423
}
1424+
1425+
func isConstQual(q Qualifier, val ref.Val) bool {
1426+
c, ok := q.(ConstantQualifier)
1427+
if !ok {
1428+
return false
1429+
}
1430+
return c.Value().Equal(val) == types.True
1431+
}
1432+
1433+
func isFieldQual(q Qualifier, fieldName string) bool {
1434+
f, ok := q.(*fieldQualifier)
1435+
if !ok {
1436+
return false
1437+
}
1438+
return f.Name == fieldName
1439+
}

interpreter/planner.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -133,14 +133,14 @@ func (p *planner) decorate(i Interpretable, err error) (Interpretable, error) {
133133
// planIdent creates an Interpretable that resolves an identifier from an Activation.
134134
func (p *planner) planIdent(expr *exprpb.Expr) (Interpretable, error) {
135135
// Establish whether the identifier is in the reference map.
136-
if identRef, found := p.refMap[expr.Id]; found {
137-
return p.planCheckedIdent(expr.Id, identRef)
136+
if identRef, found := p.refMap[expr.GetId()]; found {
137+
return p.planCheckedIdent(expr.GetId(), identRef)
138138
}
139139
// Create the possible attribute list for the unresolved reference.
140140
ident := expr.GetIdentExpr()
141141
return &evalAttr{
142142
adapter: p.adapter,
143-
attr: p.attrFactory.MaybeAttribute(expr.Id, ident.Name),
143+
attr: p.attrFactory.MaybeAttribute(expr.GetId(), ident.Name),
144144
}, nil
145145
}
146146

@@ -178,8 +178,8 @@ func (p *planner) planCheckedIdent(id int64, identRef *exprpb.Reference) (Interp
178178
func (p *planner) planSelect(expr *exprpb.Expr) (Interpretable, error) {
179179
// If the Select id appears in the reference map from the CheckedExpr proto then it is either
180180
// a namespaced identifier or enum value.
181-
if identRef, found := p.refMap[expr.Id]; found {
182-
return p.planCheckedIdent(expr.Id, identRef)
181+
if identRef, found := p.refMap[expr.GetId()]; found {
182+
return p.planCheckedIdent(expr.GetId(), identRef)
183183
}
184184

185185
sel := expr.GetSelectExpr()
@@ -191,7 +191,7 @@ func (p *planner) planSelect(expr *exprpb.Expr) (Interpretable, error) {
191191

192192
// Determine the field type if this is a proto message type.
193193
var fieldType *ref.FieldType
194-
opType := p.typeMap[op.ID()]
194+
opType := p.typeMap[sel.GetOperand().GetId()]
195195
if opType.GetMessageType() != "" {
196196
ft, found := p.provider.FindFieldType(opType.GetMessageType(), sel.Field)
197197
if found && ft.IsSet != nil && ft.GetFrom != nil {
@@ -485,7 +485,7 @@ func (p *planner) planCallIndex(expr *exprpb.Expr,
485485
}
486486
indConst, isIndConst := ind.(InterpretableConst)
487487
if isIndConst {
488-
opType := p.typeMap[op.ID()]
488+
opType := p.typeMap[expr.GetCallExpr().GetTarget().GetId()]
489489
qual, err := p.attrFactory.NewQualifier(
490490
opType, indConst.ID(), indConst.Value())
491491
if err != nil {
@@ -673,7 +673,7 @@ func (p *planner) resolveFunction(expr *exprpb.Expr) (*exprpb.Expr, string, stri
673673

674674
// Checked expressions always have a reference map entry, and _should_ have the fully qualified
675675
// function name as the fnName value.
676-
oRef, hasOverload := p.refMap[expr.Id]
676+
oRef, hasOverload := p.refMap[expr.GetId()]
677677
if hasOverload {
678678
if len(oRef.GetOverloadId()) == 1 {
679679
return target, fnName, oRef.GetOverloadId()[0]
@@ -727,7 +727,7 @@ func (p *planner) resolveFunction(expr *exprpb.Expr) (*exprpb.Expr, string, stri
727727
func (p *planner) toQualifiedName(operand *exprpb.Expr) (string, bool) {
728728
// If the checker identified the expression as an attribute by the type-checker, then it can't
729729
// possibly be part of qualified name in a namespace.
730-
_, isAttr := p.refMap[operand.Id]
730+
_, isAttr := p.refMap[operand.GetId()]
731731
if isAttr {
732732
return "", false
733733
}

0 commit comments

Comments
 (0)