Skip to content

Commit

Permalink
wasm: Add support for composite terms
Browse files Browse the repository at this point in the history
These changes update the planner and wasm backend to support array and
object literals. For now the literals are constructed dynamically, but
in the future we could intern them. Being able to construct them
dynamically will be required anyway when comprehensions are added.

Fixes open-policy-agent#1113

Signed-off-by: Torin Sandall <torinsandall@gmail.com>
  • Loading branch information
tsandall committed Jan 20, 2019
1 parent afef234 commit cea4ffa
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 1 deletion.
22 changes: 21 additions & 1 deletion internal/compiler/wasm/wasm.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,13 @@ const (
opaJSONParse = "opa_json_parse"
opaNull = "opa_null"
opaBoolean = "opa_boolean"
opaStringTerminated = "opa_string_terminated"
opaNumberInt = "opa_number_int"
opaNumberSize = "opa_number_size"
opaArrayWithCap = "opa_array_with_cap"
opaArrayAppend = "opa_array_append"
opaObject = "opa_object"
opaObjectInsert = "opa_object_insert"
opaStringTerminated = "opa_string_terminated"
opaValueBooleanSet = "opa_value_boolean_set"
opaValueNumberSetInt = "opa_value_number_set_int"
opaValueCompare = "opa_value_compare"
Expand Down Expand Up @@ -273,6 +277,13 @@ func (c *Compiler) compileBlock(block ir.Block) ([]instruction.Instruction, erro
instrs = append(instrs, instruction.I32Const{Value: c.stringAddr(stmt.Index)})
instrs = append(instrs, instruction.Call{Index: c.function(opaStringTerminated)})
instrs = append(instrs, instruction.SetLocal{Index: c.local(stmt.Target)})
case ir.MakeArrayStmt:
instrs = append(instrs, instruction.I32Const{Value: stmt.Capacity})
instrs = append(instrs, instruction.Call{Index: c.function(opaArrayWithCap)})
instrs = append(instrs, instruction.SetLocal{Index: c.local(stmt.Target)})
case ir.MakeObjectStmt:
instrs = append(instrs, instruction.Call{Index: c.function(opaObject)})
instrs = append(instrs, instruction.SetLocal{Index: c.local(stmt.Target)})
case ir.IsArrayStmt:
instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.Source)})
instrs = append(instrs, instruction.Call{Index: c.function(opaValueType)})
Expand All @@ -285,6 +296,15 @@ func (c *Compiler) compileBlock(block ir.Block) ([]instruction.Instruction, erro
instrs = append(instrs, instruction.I32Const{Value: opaTypeObject})
instrs = append(instrs, instruction.I32Ne{})
instrs = append(instrs, instruction.BrIf{Index: 0})
case ir.ArrayAppendStmt:
instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.Array)})
instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.Value)})
instrs = append(instrs, instruction.Call{Index: c.function(opaArrayAppend)})
case ir.ObjectInsertStmt:
instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.Object)})
instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.Key)})
instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.Value)})
instrs = append(instrs, instruction.Call{Index: c.function(opaObjectInsert)})
default:
var buf bytes.Buffer
ir.Pretty(&buf, stmt)
Expand Down
26 changes: 26 additions & 0 deletions internal/ir/ir.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,17 @@ type MakeNumberIntStmt struct {
Target Local
}

// MakeArrayStmt constructs a local variable that refers to an array value.
type MakeArrayStmt struct {
Capacity int32
Target Local
}

// MakeObjectStmt constructs a local variable that refers to an object value.
type MakeObjectStmt struct {
Target Local
}

// EqualStmt represents an value-equality check of two local variables.
type EqualStmt struct {
A Local
Expand Down Expand Up @@ -247,3 +258,18 @@ type IsArrayStmt struct {
type IsObjectStmt struct {
Source Local
}

// ArrayAppendStmt represents a dynamic append operation of a value
// onto an array.
type ArrayAppendStmt struct {
Value Local
Array Local
}

// ObjectInsertStmt represents a dynamic insert operation of a
// key/value pair into an object.
type ObjectInsertStmt struct {
Key Local
Value Local
Object Local
}
64 changes: 64 additions & 0 deletions internal/planner/planner.go
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,10 @@ func (p *Planner) planTerm(t *ast.Term, iter planiter) error {
return p.planVar(v, iter)
case ast.Ref:
return p.planRef(v, iter)
case ast.Array:
return p.planArray(v, iter)
case ast.Object:
return p.planObject(v, iter)
default:
return fmt.Errorf("%v term not implemented", ast.TypeName(v))
}
Expand Down Expand Up @@ -555,6 +559,66 @@ func (p *Planner) planVar(v ast.Var, iter planiter) error {
return iter()
}

func (p *Planner) planArray(arr ast.Array, iter planiter) error {

larr := p.newLocal()

p.appendStmt(ir.MakeArrayStmt{
Capacity: int32(len(arr)),
Target: larr,
})

return p.planArrayRec(arr, 0, larr, iter)
}

func (p *Planner) planArrayRec(arr ast.Array, index int, larr ir.Local, iter planiter) error {
if index == len(arr) {
return iter()
}

return p.planTerm(arr[index], func() error {

p.appendStmt(ir.ArrayAppendStmt{
Value: p.ltarget,
Array: larr,
})

return p.planArrayRec(arr, index+1, larr, iter)
})
}

func (p *Planner) planObject(obj ast.Object, iter planiter) error {

lobj := p.newLocal()

p.appendStmt(ir.MakeObjectStmt{
Target: lobj,
})

return p.planObjectRec(obj, 0, obj.Keys(), lobj, iter)
}

func (p *Planner) planObjectRec(obj ast.Object, index int, keys []*ast.Term, lobj ir.Local, iter planiter) error {
if index == len(keys) {
return iter()
}

return p.planTerm(keys[index], func() error {
lkey := p.ltarget

return p.planTerm(obj.Get(keys[index]), func() error {
lval := p.ltarget
p.appendStmt(ir.ObjectInsertStmt{
Key: lkey,
Value: lval,
Object: lobj,
})

return p.planObjectRec(obj, index+1, keys, lobj, iter)
})
})
}

func (p *Planner) planRef(ref ast.Ref, iter planiter) error {

if !ref[0].Equal(ast.InputRootDocument) {
Expand Down
24 changes: 24 additions & 0 deletions test/wasm/assets/003_comparison.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,27 @@ cases:
query: "input.x == 1"
input: {"x": 0}
return_code: 0
- note: deq/array
query: "[input.x] == [1]"
input: {"x": 1}
return_code: 1
- note: deq/array (negative)
query: "[input.x] == [1]"
input: {"x": 2}
return_code: 0
- note: deq/array (undefined)
query: "[input.x] = [1]"
input: {}
return_code: 0
- note: deq/object
query: '{"a": input.x} == {"a": 1}'
input: {"x": 1}
return_code: 1
- note: deq/object (negative)
query: '{"a": input.x} == {"a": 1}'
input: {"x": 2}
return_code: 0
- note: deq/object (undefined)
query: '{"a": input.x} == {"a": 1}'
input: {}
return_code: 0

0 comments on commit cea4ffa

Please sign in to comment.