Skip to content
This repository has been archived by the owner on Nov 18, 2021. It is now read-only.

Commit

Permalink
cue: implement marshaling and restoring instances
Browse files Browse the repository at this point in the history
Also fixed bug in export that prevented proper
raw exporting.

Change-Id: I128760ed89efa1907e15b999722806267ec6144a
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/2707
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
  • Loading branch information
mpvl committed Aug 3, 2019
1 parent f03161d commit 9934915
Show file tree
Hide file tree
Showing 4 changed files with 180 additions and 24 deletions.
84 changes: 73 additions & 11 deletions cue/build.go
Expand Up @@ -15,12 +15,16 @@
package cue

import (
"bytes"
"compress/gzip"
"encoding/gob"
"path"
"strconv"

"cuelang.org/go/cue/ast"
"cuelang.org/go/cue/build"
"cuelang.org/go/cue/errors"
"cuelang.org/go/cue/format"
"cuelang.org/go/cue/token"
)

Expand All @@ -29,9 +33,8 @@ import (
// Any operation that involves two Values or Instances should originate from
// the same Runtime.
type Runtime struct {
Context *build.Context
Context *build.Context // TODO: remove
idx *index
ctxt *build.Context
}

func dummyLoad(token.Pos, string) *build.Instance { return nil }
Expand All @@ -56,6 +59,73 @@ func (r *Runtime) complete(p *build.Instance) (*Instance, error) {
return inst, nil
}

type instanceData struct {
Filename string
PkgPath string
Data []byte
}

// Unmarshal creates an Instance from bytes generated by the MarshalBinary
// method of an instance.
func (r *Runtime) Unmarshal(b []byte) (*Instance, error) {
reader, err := gzip.NewReader(bytes.NewReader(b))
if err != nil {
return nil, errors.Newf(token.NoPos, "restore failed: %v", err)
}
version := [1]byte{}
_, _ = reader.Read(version[:])

data := &instanceData{}
if err = gob.NewDecoder(reader).Decode(data); err != nil {
return nil, errors.Newf(token.NoPos, "restore failed: %v", err)
}

inst, err := r.Compile(data.Filename, data.Data)
if inst != nil {
inst.ImportPath = data.PkgPath
}
return inst, err
}

// MarshalBinary marshals c into binary data.
func (inst *Instance) MarshalBinary() (b []byte, err error) {
ctx := inst.index.newContext()
n := export(ctx, inst.rootValue, options{raw: true})

file, ok := n.(*ast.File)
if !ok {
file = &ast.File{}
if obj, ok := n.(*ast.StructLit); ok {
file.Decls = append(file.Decls, obj.Elts...)
} else {
file.Decls = append(file.Decls, &ast.EmbedDecl{Expr: n.(ast.Expr)})
}
}
if inst.Name != "" {
file.Name = ast.NewIdent(inst.Name)
}

b, err = format.Node(file)
if err != nil {
return nil, err
}

buf := &bytes.Buffer{}
zw := gzip.NewWriter(buf)
_, _ = zw.Write([]byte{0}) // version marker
enc := gob.NewEncoder(zw)
err = enc.Encode(&instanceData{
Filename: inst.Value().Pos().Filename(),
PkgPath: inst.ImportPath,
Data: b,
})
if err != nil {
return nil, err
}
err = zw.Close()
return buf.Bytes(), err
}

// Compile compiles the given source into an Instance. The source code may be
// provided as a string, byte slice, io.Reader. The name is used as the file
// name in position information. The source may import builtin packages. Use
Expand Down Expand Up @@ -119,15 +189,7 @@ func (r *Runtime) CompileExpr(expr ast.Expr) (*Instance, error) {
//
// Deprecated: use Compile
func (r *Runtime) Parse(name string, source interface{}) (*Instance, error) {
ctx := r.Context
if ctx == nil {
ctx = build.NewContext()
}
p := ctx.NewInstance(name, dummyLoad)
if err := p.AddFile(name, source); err != nil {
return nil, err
}
return r.complete(p)
return r.Compile(name, source)
}

// Build creates an Instance from the given build.Instance. A returned Instance
Expand Down
67 changes: 67 additions & 0 deletions cue/build_test.go
Expand Up @@ -24,6 +24,73 @@ import (
"cuelang.org/go/cue/token"
)

func TestMarshalling(t *testing.T) {
testCases := []struct {
filename string
input string
pkg string
}{{
filename: "foo.cue",
pkg: "foo",
input: `package foo
A: int
B: string
`,
}, {
filename: "bar.cue",
pkg: "bar",
input: `package bar
"Hello world!"
`,
}, {
filename: "qux.cue",
input: `
"Hello world!"
`,
}, {
filename: "baz.cue",
pkg: "baz",
input: `package baz
import "strings"
a: strings.TrimSpace(" Hello world! ")
`}}
for _, tc := range testCases {
t.Run(tc.filename, func(t *testing.T) {
r := &Runtime{}
inst, err := r.Compile(tc.filename, tc.input)
if err != nil {
t.Fatal(err)
}
inst.ImportPath = "test/pkg"
got := fmt.Sprint(inst.Value())

b, err := inst.MarshalBinary()
if err != nil {
t.Fatal(err)
}

r2 := &Runtime{}
inst, err = r2.Unmarshal(b)
if err != nil {
t.Fatal(err)
}

if inst.ImportPath != "test/pkg" {
t.Error("import path was not restored")
}
want := fmt.Sprint(inst.Value())

if got != want {
t.Errorf("\ngot: %q;\nwant: %q", got, want)
}
})
}
}

func TestFromExpr(t *testing.T) {
testCases := []struct {
expr ast.Expr
Expand Down
26 changes: 16 additions & 10 deletions cue/export.go
Expand Up @@ -44,6 +44,7 @@ func export(ctx *context, v value, m options) ast.Node {

value := e.expr(v)
if len(e.imports) == 0 {
// TODO: unwrap structs?
return value
}
imports := make([]string, 0, len(e.imports))
Expand Down Expand Up @@ -73,7 +74,6 @@ func export(ctx *context, v value, m options) ast.Node {
})
}

// TODO: should we unwrap structs?
if obj, ok := value.(*ast.StructLit); ok {
file.Decls = append(file.Decls, obj.Elts...)
} else {
Expand Down Expand Up @@ -275,7 +275,13 @@ func (p *exporter) expr(v value) ast.Expr {
}

case *callExpr:
call := &ast.CallExpr{Fun: p.expr(x.x)}
call := &ast.CallExpr{}
b := x.x.evalPartial(p.ctx)
if b, ok := b.(*builtin); ok {
call.Fun = p.expr(b)
} else {
call.Fun = p.expr(x.x)
}
for _, a := range x.args {
call.Args = append(call.Args, p.expr(a))
}
Expand Down Expand Up @@ -335,14 +341,14 @@ func (p *exporter) expr(v value) ast.Expr {
obj := &ast.StructLit{}
if doEval(p.mode) {
x = x.expandFields(p.ctx)
for _, a := range x.arcs {
p.stack = append(p.stack, remap{
key: x,
from: a.feature,
to: nil,
syn: obj,
})
}
}
for _, a := range x.arcs {
p.stack = append(p.stack, remap{
key: x,
from: a.feature,
to: nil,
syn: obj,
})
}
if x.emit != nil {
obj.Elts = append(obj.Elts, &ast.EmbedDecl{Expr: p.expr(x.emit)})
Expand Down
27 changes: 24 additions & 3 deletions cue/export_test.go
Expand Up @@ -305,7 +305,8 @@ func TestExport(t *testing.T) {
}][a]
a: int
c: 1
}`)}}
}`),
}}
for _, tc := range testCases {
t.Run("", func(t *testing.T) {
body := fmt.Sprintf("Test: %s", tc.in)
Expand Down Expand Up @@ -350,6 +351,16 @@ func TestExportFile(t *testing.T) {
in: `
import "strings"
a: strings.TrimSpace(" c ")
`,
out: unindent(`
import "strings"
a: strings.TrimSpace(" c ")`),
}, {
in: `
import "strings"
stringsx = strings
a: {
Expand All @@ -361,6 +372,16 @@ func TestExportFile(t *testing.T) {
STRINGS = strings
a strings: STRINGS.ContainsAny("c")`),
}, {
in: `
a: b - 100
b: a + 100
`,
out: unindent(`
{
a: b - 100
b: a + 100
}`),
}}
for _, tc := range testCases {
t.Run("", func(t *testing.T) {
Expand All @@ -372,8 +393,8 @@ func TestExportFile(t *testing.T) {
v := inst.Value()
ctx := r.index().newContext()

opts := options{raw: false}
b, err := format.Node(export(ctx, v.eval(ctx), opts), format.Simplify())
opts := options{raw: true}
b, err := format.Node(export(ctx, v.path.v, opts), format.Simplify())
if err != nil {
log.Fatal(err)
}
Expand Down

0 comments on commit 9934915

Please sign in to comment.