github.com/arnodel/golua@v0.0.0-20230215163904-e0b5347eaaa1/lib/base/base.go (about) 1 package base 2 3 import ( 4 "errors" 5 "io" 6 "io/ioutil" 7 "os" 8 9 "github.com/arnodel/golua/lib/packagelib" 10 rt "github.com/arnodel/golua/runtime" 11 "github.com/arnodel/golua/safeio" 12 ) 13 14 var LibLoader = packagelib.Loader{ 15 Load: Load, 16 } 17 18 func Load(r *rt.Runtime) (rt.Value, func()) { 19 env := r.GlobalEnv() 20 r.SetEnv(env, "_G", rt.TableValue(env)) 21 r.SetEnv(env, "_VERSION", rt.StringValue("Golua 5.4")) 22 r.SetEnv(env, "next", rt.FunctionValue(nextGoFunc)) 23 24 rt.SolemnlyDeclareCompliance( 25 rt.ComplyCpuSafe|rt.ComplyMemSafe|rt.ComplyTimeSafe|rt.ComplyIoSafe, 26 27 ipairsIterator, 28 nextGoFunc, 29 r.SetEnvGoFunc(env, "assert", assert, 1, true), 30 r.SetEnvGoFunc(env, "error", errorF, 2, false), 31 r.SetEnvGoFunc(env, "getmetatable", getmetatable, 1, false), 32 r.SetEnvGoFunc(env, "ipairs", ipairs, 1, false), 33 r.SetEnvGoFunc(env, "load", load, 4, false), 34 r.SetEnvGoFunc(env, "pairs", pairs, 1, false), 35 r.SetEnvGoFunc(env, "pcall", pcall, 1, true), 36 r.SetEnvGoFunc(env, "print", print, 0, true), // Not really iosafe/timesafe but used in all tests... 37 r.SetEnvGoFunc(env, "rawequal", rawequal, 2, false), 38 r.SetEnvGoFunc(env, "rawget", rawget, 2, false), 39 r.SetEnvGoFunc(env, "rawlen", rawlen, 1, false), 40 r.SetEnvGoFunc(env, "rawset", rawset, 3, false), 41 r.SetEnvGoFunc(env, "select", selectF, 1, true), 42 r.SetEnvGoFunc(env, "setmetatable", setmetatable, 2, false), 43 r.SetEnvGoFunc(env, "tonumber", tonumber, 2, false), 44 r.SetEnvGoFunc(env, "tostring", tostring, 1, false), 45 r.SetEnvGoFunc(env, "type", typeString, 1, false), 46 r.SetEnvGoFunc(env, "warn", warn, 0, true), // Added in Lua 5.4 47 r.SetEnvGoFunc(env, "xpcall", xpcall, 2, true), 48 ) 49 rt.SolemnlyDeclareCompliance( 50 rt.ComplyCpuSafe|rt.ComplyMemSafe, 51 r.SetEnvGoFunc(env, "dofile", dofile, 1, false), 52 r.SetEnvGoFunc(env, "loadfile", loadfile, 3, false), 53 ) 54 // That's not safe! 55 r.SetEnvGoFunc(env, "collectgarbage", collectgarbage, 2, false) 56 return rt.NilValue, nil 57 } 58 59 func ToString(t *rt.Thread, v rt.Value) (string, error) { 60 next := rt.NewTerminationWith(t.CurrentCont(), 1, false) 61 err, ok := rt.Metacall(t, v, "__tostring", []rt.Value{v}, next) 62 if err != nil { 63 return "", err 64 } 65 if ok { 66 s, ok := next.Get(0).ToString() 67 if !ok { 68 return "", errors.New("'__tostring' must return a string") 69 } 70 return s, nil 71 } 72 s, _ := v.ToString() 73 return s, nil 74 } 75 76 func tostring(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { 77 if err := c.Check1Arg(); err != nil { 78 return nil, err 79 } 80 s, err := ToString(t, c.Arg(0)) 81 if err != nil { 82 return nil, err 83 } 84 return c.PushingNext(t.Runtime, rt.StringValue(s)), nil 85 } 86 87 // Load a chunk from a file and require the memory / cpu for it. Callers might 88 // want to release the memory when they are done with the chunk. 89 func loadChunk(t *rt.Thread, args []rt.Value) (chunk []byte, chunkName string, err error) { 90 budget := t.LinearUnused(10) 91 var reader io.Reader 92 if len(args) == 0 { 93 chunkName = "stdin" 94 reader = os.Stdin 95 } else { 96 var ok bool 97 chunkName, ok = args[0].TryString() 98 if !ok { 99 return nil, chunkName, errors.New("#1 must be a string") 100 } 101 f, err := safeio.OpenFile(t.Runtime, chunkName, os.O_RDONLY, 0) 102 if err != nil { 103 return nil, chunkName, err 104 } 105 defer f.Close() 106 reader = f 107 } 108 if budget > 0 { 109 reader = io.LimitReader(reader, int64(budget)) 110 } 111 chunk, err = ioutil.ReadAll(reader) 112 if err != nil { 113 return nil, chunkName, err 114 } 115 t.LinearRequire(10, uint64(len(chunk))) 116 return chunk, chunkName, nil 117 }