zombiezen.com/go/lua@v0.0.0-20231013005828-290725fb9140/internal/lua54/exports.go (about)

     1  // Copyright 2023 Ross Light
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy of
     4  // this software and associated documentation files (the “Software”), to deal in
     5  // the Software without restriction, including without limitation the rights to
     6  // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
     7  // the Software, and to permit persons to whom the Software is furnished to do so,
     8  // subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in all
    11  // copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
    15  // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
    16  // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
    17  // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
    18  // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    19  //
    20  // SPDX-License-Identifier: MIT
    21  
    22  package lua54
    23  
    24  import (
    25  	"io"
    26  	"runtime/cgo"
    27  	"unsafe"
    28  )
    29  
    30  // This file is used to contain Go code exported to C.
    31  // It's kept in a separate file with a minimal C preamble
    32  // to avoid unintentional redefinitions.
    33  // See the caveat in https://pkg.go.dev/cmd/cgo for more details.
    34  
    35  // #include <stdlib.h>
    36  // #include <stddef.h>
    37  // #include "lua.h"
    38  //
    39  // void zombiezen_lua_pushstring(lua_State *L, _GoString_ s);
    40  import "C"
    41  
    42  //export zombiezen_lua_readercb
    43  func zombiezen_lua_readercb(l *C.lua_State, data unsafe.Pointer, size *C.size_t) *C.char {
    44  	r := (*cgo.Handle)(data).Value().(*reader)
    45  	buf := unsafe.Slice((*byte)(unsafe.Pointer(r.buf)), readerBufferSize)
    46  	n, err := r.r.Read(buf)
    47  	*size = C.size_t(n)
    48  	if n == 0 && err != nil && err != io.EOF {
    49  		// We have a trampoline that intercepts a NULL return.
    50  		// Push the error onto the stack.
    51  		C.zombiezen_lua_pushstring(l, err.Error())
    52  		return nil
    53  	}
    54  	return r.buf
    55  }
    56  
    57  type writerState struct {
    58  	w   cgo.Handle
    59  	n   int64
    60  	err cgo.Handle
    61  }
    62  
    63  //export zombiezen_lua_writercb
    64  func zombiezen_lua_writercb(l *C.lua_State, p unsafe.Pointer, size C.size_t, ud unsafe.Pointer) C.int {
    65  	state := (*writerState)(ud)
    66  	b := unsafe.Slice((*byte)(p), size)
    67  	n, err := state.w.Value().(io.Writer).Write(b)
    68  	state.n += int64(n)
    69  	if err != nil {
    70  		state.err = cgo.NewHandle(err)
    71  		return 1
    72  	}
    73  	return 0
    74  }
    75  
    76  //export zombiezen_lua_gocb
    77  func zombiezen_lua_gocb(l *C.lua_State) C.int {
    78  	state := stateForCallback(l)
    79  	defer func() {
    80  		// Once the callback has finished, clear the State.
    81  		// This prevents incorrect usage, especially with ActivationRecords.
    82  		*state = State{}
    83  	}()
    84  	funcID := copyUint64(state, goClosureUpvalueIndex)
    85  	f := state.data().closures[funcID]
    86  	if f == nil {
    87  		C.zombiezen_lua_pushstring(l, "Go closure upvalue corrupted")
    88  		return -1
    89  	}
    90  
    91  	results, err := pcall(f, state)
    92  	if err != nil {
    93  		C.zombiezen_lua_pushstring(l, err.Error())
    94  		return -1
    95  	}
    96  	if results < 0 {
    97  		C.zombiezen_lua_pushstring(l, "Go callback returned negative results")
    98  		return -1
    99  	}
   100  	return C.int(results)
   101  }
   102  
   103  //export zombiezen_lua_gcfunc
   104  func zombiezen_lua_gcfunc(l *C.lua_State) C.int {
   105  	state := stateForCallback(l)
   106  	funcID := copyUint64(state, 1)
   107  	if funcID != 0 {
   108  		delete(state.data().closures, funcID)
   109  		setUint64(state, 1, 0)
   110  	}
   111  	return 0
   112  }