Skip to content

Commit

Permalink
exec and cache
Browse files Browse the repository at this point in the history
  • Loading branch information
hyorigo committed Feb 26, 2024
1 parent ef5e69a commit 87dd3fe
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 18 deletions.
102 changes: 85 additions & 17 deletions exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,102 @@ package starlet

import (
"bytes"
"errors"
"fmt"
"sync"

"go.starlark.net/starlark"
"go.starlark.net/syntax"
)

func starlarkExecFile(opts *syntax.FileOptions, thread *starlark.Thread, filename string, src interface{}, predeclared starlark.StringDict) (starlark.StringDict, error) {
// Parse, resolve, and compile a Starlark source file.
_, prog, err := starlark.SourceProgramOptions(opts, filename, src, predeclared.Has)
if err != nil {
return nil, err
func (m *Machine) execStarlarkFile(filename string, src interface{}) (starlark.StringDict, error) {
// restore the arguments for starlark.ExecFileOptions
opts := m.getFileOptions()
thread := m.thread
predeclared := m.predeclared
hasCache := m.progCache != nil

// if cache is not enabled, execute the original source
if !hasCache {
return starlark.ExecFileOptions(opts, thread, filename, src, predeclared)
}

// Try to save it to the cache
buf := new(bytes.Buffer)
if err := prog.Write(buf); err != nil {
return nil, err
// for compiled program and cache key
var (
prog *starlark.Program
err error
key = fmt.Sprintf("%d:%s", starlark.CompilerVersion, filename)
)
if hasCache {
// if cache is enabled, try to load compiled bytes from cache first
if cb, ok := m.progCache.Get(key); ok {
// load program from compiled bytes
if prog, err = starlark.CompiledProgram(bytes.NewReader(cb)); err != nil {
// if failed, remove the result and continue
prog = nil
}

Check warning on line 37 in exec.go

View check run for this annotation

Codecov / codecov/patch

exec.go#L25-L37

Added lines #L25 - L37 were not covered by tests
}
}
bs := buf.Bytes()
//cv := starlark.CompilerVersion

// Reload as a new program
np, err := starlark.CompiledProgram(bytes.NewReader(bs))
if err != nil {
return nil, err
// if program is not loaded from cache, compile and cache it
if prog == nil {
// parse, resolve, and compile a Starlark source file.
if _, prog, err = starlark.SourceProgramOptions(opts, filename, src, predeclared.Has); err != nil {
return nil, err
}

Check warning on line 46 in exec.go

View check run for this annotation

Codecov / codecov/patch

exec.go#L42-L46

Added lines #L42 - L46 were not covered by tests
// dump the compiled program to bytes
buf := new(bytes.Buffer)
if err = prog.Write(buf); err != nil {
return nil, err
}

Check warning on line 51 in exec.go

View check run for this annotation

Codecov / codecov/patch

exec.go#L48-L51

Added lines #L48 - L51 were not covered by tests
// save the compiled bytes to cache
_ = m.progCache.Set(key, buf.Bytes())

Check warning on line 53 in exec.go

View check run for this annotation

Codecov / codecov/patch

exec.go#L53

Added line #L53 was not covered by tests
}
prog = np

// execute the compiled program
g, err := prog.Init(thread, predeclared)
g.Freeze()
return g, err

Check warning on line 59 in exec.go

View check run for this annotation

Codecov / codecov/patch

exec.go#L57-L59

Added lines #L57 - L59 were not covered by tests
}

// ByteCache is an interface for caching byte data, used for caching compiled Starlark programs.
type ByteCache interface {
Get(key string) ([]byte, bool)
Set(key string, value []byte) error
}

// MapByteCache is a simple in-memory map-based ByteCache.
type MapByteCache struct {
sync.RWMutex
data map[string][]byte
}

// NewMapByteCache creates a new MapByteCache instance.
func NewMapByteCache() *MapByteCache {
return &MapByteCache{
data: make(map[string][]byte),
}

Check warning on line 78 in exec.go

View check run for this annotation

Codecov / codecov/patch

exec.go#L75-L78

Added lines #L75 - L78 were not covered by tests
}

// Get returns the value for the given key, and whether the key exists.
func (c *MapByteCache) Get(key string) ([]byte, bool) {
c.RLock()
defer c.RUnlock()

if c == nil || c.data == nil {
return nil, false
}
v, ok := c.data[key]
return v, ok

Check warning on line 90 in exec.go

View check run for this annotation

Codecov / codecov/patch

exec.go#L82-L90

Added lines #L82 - L90 were not covered by tests
}

// Set sets the value for the given key.
func (c *MapByteCache) Set(key string, value []byte) error {
c.Lock()
defer c.Unlock()

if c == nil || c.data == nil {
return errors.New("no data map found in the cache")
}
c.data[key] = value
return nil

Check warning on line 102 in exec.go

View check run for this annotation

Codecov / codecov/patch

exec.go#L94-L102

Added lines #L94 - L102 were not covered by tests
}
1 change: 1 addition & 0 deletions machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ type Machine struct {
scriptContent []byte
scriptFS fs.FS
// runtime core
progCache ByteCache
runTimes uint
loadCache *cache
thread *starlark.Thread
Expand Down
3 changes: 2 additions & 1 deletion run.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,8 @@ func (m *Machine) runInternal(ctx context.Context, extras StringAnyMap) (out Str
// run with everything prepared
m.runTimes++
//res, err := starlark.ExecFileOptions(m.getFileOptions(), m.thread, scriptName, source, m.predeclared)
res, err := starlarkExecFile(m.getFileOptions(), m.thread, scriptName, source, m.predeclared)
//res, err := starlarkExecFile(m.getFileOptions(), m.thread, scriptName, source, m.predeclared)
res, err := m.execStarlarkFile(scriptName, source)
done <- struct{}{}

// merge result as predeclared for next run
Expand Down

0 comments on commit 87dd3fe

Please sign in to comment.