Skip to content

Commit 58abb38

Browse files
committed
Replace C Lua interpreter with new interpreter
`CGO_ENABLED=0 go build ./cmd/zb` now runs. 🎉 Fixes #28
1 parent 3c86b1d commit 58abb38

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

135 files changed

+2097
-53885
lines changed

internal/frontend/derivation_eval.go

+6-29
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ package frontend
66
import (
77
"context"
88
"fmt"
9-
"runtime/cgo"
109
"strings"
1110

1211
"zb.256lights.llc/pkg/internal/jsonrpc"
@@ -24,7 +23,6 @@ func registerDerivationMetatable(l *lua.State) {
2423
err := lua.SetFuncs(l, 0, map[string]lua.Function{
2524
"__index": indexDerivation,
2625
"__pairs": derivationPairs,
27-
"__gc": gcDerivation,
2826
"__tostring": derivationToString,
2927
"__concat": concatDerivation,
3028
"__metatable": nil, // prevent Lua access to metatable
@@ -203,17 +201,15 @@ func (eval *Eval) derivationFunction(ctx context.Context, l *lua.State) (int, er
203201
DrvPath: drvPath,
204202
OutputName: outputName,
205203
}
206-
l.PushStringContext(placeholder, []string{contextValue{outputReference: ref}.String()})
204+
l.PushStringContext(placeholder, sets.New(contextValue{outputReference: ref}.String()))
207205
if err := l.SetField(tableCopyIndex, outputName, 0); err != nil {
208206
return 0, fmt.Errorf("derivation: %v", err)
209207
}
210208
}
211209

212-
l.NewUserdataUV(8, 1)
210+
l.NewUserdata(drv, 1)
213211
l.Rotate(-2, -1) // Swap userdata and argument table copy.
214212
l.SetUserValue(-2, 1)
215-
handle := cgo.NewHandle(drv)
216-
setUserdataHandle(l, -1, handle)
217213

218214
lua.SetMetatable(l, derivationTypeName)
219215

@@ -277,7 +273,7 @@ func stringToEnvVar(l *lua.State, drv *zbstore.Derivation, idx int) (string, err
277273
l.PushValue(idx) // Clone so that we don't munge a number.
278274
defer l.Pop(1)
279275
s, _ := l.ToString(-1)
280-
for _, dep := range l.StringContext(-1) {
276+
for dep := range l.StringContext(-1).All() {
281277
c, err := parseContextString(dep)
282278
if err != nil {
283279
return "", fmt.Errorf("internal error: %v", err)
@@ -359,30 +355,11 @@ func toDerivation(l *lua.State) (*zbstore.Derivation, error) {
359355
}
360356

361357
func testDerivation(l *lua.State, idx int) *zbstore.Derivation {
362-
handle, _ := testUserdataHandle(l, idx, derivationTypeName)
363-
if handle == 0 {
364-
return nil
365-
}
366-
drv, _ := handle.Value().(*zbstore.Derivation)
358+
x, _ := lua.TestUserdata(l, idx, derivationTypeName)
359+
drv, _ := x.(*zbstore.Derivation)
367360
return drv
368361
}
369362

370-
// gcDerivation handles the __gc metamethod on derivations
371-
// by releasing the [*derivation].
372-
func gcDerivation(l *lua.State) (int, error) {
373-
const idx = 1
374-
handle, ok := testUserdataHandle(l, idx, derivationTypeName)
375-
if !ok {
376-
return 0, lua.NewTypeError(l, idx, derivationTypeName)
377-
}
378-
if handle == 0 {
379-
return 0, nil
380-
}
381-
handle.Delete()
382-
setUserdataHandle(l, idx, 0)
383-
return 0, nil
384-
}
385-
386363
// indexDerivation handles the __index metamethod on derivations.
387364
func indexDerivation(l *lua.State) (int, error) {
388365
if _, err := toDerivation(l); err != nil {
@@ -497,5 +474,5 @@ func (c contextValue) String() string {
497474
}
498475

499476
func pushStorePath(l *lua.State, path zbstore.Path) {
500-
l.PushStringContext(string(path), []string{contextValue{path: path}.String()})
477+
l.PushStringContext(string(path), sets.New(contextValue{path: path}.String()))
501478
}

internal/frontend/eval.go

+10-26
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,16 @@
44
package frontend
55

66
import (
7+
"bufio"
78
"context"
89
"embed"
9-
"encoding/binary"
1010
"errors"
1111
"fmt"
1212
"io"
1313
"io/fs"
1414
"os"
1515
slashpath "path"
1616
"path/filepath"
17-
"runtime/cgo"
1817
"strings"
1918

2019
"zb.256lights.llc/pkg/internal/jsonrpc"
@@ -72,7 +71,9 @@ func NewEval(storeDir zbstore.Directory, store *jsonrpc.Client, cacheDB string)
7271

7372
registerDerivationMetatable(&eval.l)
7473

75-
base := lua.NewOpenBase(io.Discard, nil)
74+
base := lua.NewOpenBase(&lua.BaseOptions{
75+
Output: io.Discard,
76+
})
7677
if err := lua.Require(&eval.l, lua.GName, true, base); err != nil {
7778
return nil, err
7879
}
@@ -123,7 +124,7 @@ func NewEval(storeDir zbstore.Directory, store *jsonrpc.Client, cacheDB string)
123124
}
124125

125126
// Run prelude.
126-
if err := eval.l.LoadString(preludeSource, "=(prelude)", "t"); err != nil {
127+
if err := eval.l.Load(strings.NewReader(preludeSource), lua.AbstractSource("(prelude)"), "t"); err != nil {
127128
return nil, err
128129
}
129130
if err := eval.l.Call(0, 0, 0); err != nil {
@@ -258,7 +259,6 @@ func (eval *Eval) File(ctx context.Context, exprFile string, attrPaths []string)
258259
return nil, err
259260
}
260261
if err := eval.l.Call(0, 1, 0); err != nil {
261-
eval.l.Pop(1)
262262
return nil, err
263263
}
264264
return eval.attrPaths(attrPaths)
@@ -281,7 +281,6 @@ func (eval *Eval) Expression(ctx context.Context, expr string, attrPaths []strin
281281
return nil, err
282282
}
283283
if err := eval.l.Call(0, 1, 0); err != nil {
284-
eval.l.Pop(1)
285284
return nil, err
286285
}
287286
return eval.attrPaths(attrPaths)
@@ -305,13 +304,12 @@ func (eval *Eval) attrPaths(paths []string) ([]any, error) {
305304
expr += "."
306305
}
307306
expr += p + ";"
308-
if err := eval.l.LoadString(expr, expr, "t"); err != nil {
307+
if err := eval.l.Load(strings.NewReader(expr), lua.LiteralSource(expr), "t"); err != nil {
309308
eval.l.Pop(1)
310309
return result, fmt.Errorf("%s: %v", p, err)
311310
}
312311
eval.l.PushValue(-2)
313312
if err := eval.l.Call(1, 1, 0); err != nil {
314-
eval.l.Pop(1)
315313
return result, fmt.Errorf("%s: %v", p, err)
316314
}
317315
x, err := luaToGo(&eval.l)
@@ -389,20 +387,6 @@ func luaToGo(l *lua.State) (any, error) {
389387
}
390388
}
391389

392-
func testUserdataHandle(l *lua.State, idx int, tname string) (cgo.Handle, bool) {
393-
data := lua.TestUserdata(l, idx, tname)
394-
if len(data) != 8 {
395-
return 0, false
396-
}
397-
return cgo.Handle(binary.LittleEndian.Uint64(data)), true
398-
}
399-
400-
func setUserdataHandle(l *lua.State, idx int, handle cgo.Handle) {
401-
var buf [8]byte
402-
binary.LittleEndian.PutUint64(buf[:], uint64(handle))
403-
l.SetUserdata(idx, 0, buf[:])
404-
}
405-
406390
func loadFile(l *lua.State, path string) error {
407391
path, err := filepath.Abs(path)
408392
if err != nil {
@@ -414,19 +398,19 @@ func loadFile(l *lua.State, path string) error {
414398
}
415399
defer f.Close()
416400

417-
if err := l.Load(f, "@"+path, "t"); err != nil {
401+
if err := l.Load(bufio.NewReader(f), lua.FilenameSource(path), "t"); err != nil {
418402
l.Pop(1)
419403
return fmt.Errorf("load file %s: %w", path, err)
420404
}
421405
return nil
422406
}
423407

424408
func loadExpression(l *lua.State, expr string) error {
425-
if err := l.LoadString("return "+expr+";", expr, "t"); err == nil {
409+
if err := l.Load(strings.NewReader("return "+expr+";"), lua.LiteralSource(expr), "t"); err == nil {
426410
return nil
427411
}
428412
l.Pop(1)
429-
if err := l.LoadString(expr, expr, "t"); err != nil {
413+
if err := l.Load(strings.NewReader(expr), lua.LiteralSource(expr), "t"); err != nil {
430414
l.Pop(1)
431415
return err
432416
}
@@ -495,7 +479,7 @@ func (eval *Eval) loadfileFunction(ctx context.Context, l *lua.State) (int, erro
495479
// TODO(someday): If we have dependencies and we're using a non-local store,
496480
// export the store object and read it.
497481
var rewrites []string
498-
for _, dep := range filenameContext {
482+
for dep := range filenameContext {
499483
c, err := parseContextString(dep)
500484
if err != nil {
501485
l.PushNil()

internal/frontend/path_eval.go

+9-7
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616

1717
"zb.256lights.llc/pkg/internal/jsonrpc"
1818
"zb.256lights.llc/pkg/internal/lua"
19+
"zb.256lights.llc/pkg/sets"
1920
"zb.256lights.llc/pkg/zbstore"
2021
"zombiezen.com/go/log"
2122
"zombiezen.com/go/nix"
@@ -26,7 +27,7 @@ import (
2627

2728
func (eval *Eval) pathFunction(ctx context.Context, cache *sqlite.Conn, l *lua.State) (nResults int, err error) {
2829
var p string
29-
var pcontext []string
30+
var pcontext sets.Set[string]
3031
var name string
3132
switch l.Type(1) {
3233
case lua.TypeString:
@@ -226,7 +227,7 @@ func (eval *Eval) toFileFunction(ctx context.Context, l *lua.State) (int, error)
226227
h := nix.NewHasher(nix.SHA256)
227228
h.WriteString(s)
228229
var refs zbstore.References
229-
for _, dep := range l.StringContext(2) {
230+
for dep := range l.StringContext(2) {
230231
c, err := parseContextString(dep)
231232
if err != nil {
232233
return 0, fmt.Errorf("internal error: %v", err)
@@ -297,7 +298,7 @@ func writeSingleFileNAR(w io.Writer, r io.Reader, sz int64) error {
297298

298299
// absSourcePath takes a source path passed as an argument from Lua to Go
299300
// and resolves it relative to the calling function.
300-
func absSourcePath(l *lua.State, path string, context []string) (string, error) {
301+
func absSourcePath(l *lua.State, path string, context sets.Set[string]) (string, error) {
301302
if filepath.IsAbs(path) {
302303
return path, nil
303304
}
@@ -306,7 +307,7 @@ func absSourcePath(l *lua.State, path string, context []string) (string, error)
306307
// Such placeholders have a leading slash, followed by a long hash digest.
307308
// On non-POSIX systems, such a path might not be recognized as an absolute path.
308309
// We treat this as an absolute path while preserving the placeholder.
309-
for _, dep := range context {
310+
for dep := range context {
310311
c, err := parseContextString(dep)
311312
if err != nil {
312313
return "", fmt.Errorf("resolve path: internal error: %v", err)
@@ -329,12 +330,13 @@ func absSourcePath(l *lua.State, path string, context []string) (string, error)
329330
}
330331
}
331332

332-
// TODO(maybe): This is probably wonky with tail calls.
333-
debugInfo := l.Stack(1).Info("S")
333+
// Lua guarantees that a call to a native function will never be a tail call,
334+
// so we can always get information about the immediate caller.
335+
debugInfo := l.Info(1)
334336
if debugInfo == nil {
335337
return "", fmt.Errorf("resolve path: no caller information available")
336338
}
337-
source, ok := strings.CutPrefix(debugInfo.Source, "@")
339+
source, ok := debugInfo.Source.Filename()
338340
if !ok {
339341
// Not loaded from a file. Use working directory.
340342
//

internal/lua/LICENSE

-19
This file was deleted.

internal/lua/README.md

-41
This file was deleted.

0 commit comments

Comments
 (0)