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  }