mirror of
https://codeberg.org/scip/tablizer.git
synced 2025-12-18 21:11:03 +01:00
added
This commit is contained in:
209
vendor/github.com/glycerine/zygomys/zygo/callgo.go
generated
vendored
Normal file
209
vendor/github.com/glycerine/zygomys/zygo/callgo.go
generated
vendored
Normal file
@@ -0,0 +1,209 @@
|
||||
package zygo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// call Go methods
|
||||
|
||||
// Using reflection, invoke a Go method on a struct or interface.
|
||||
// args[0] is a hash with an an attached GoStruct
|
||||
// args[1] is a hash representing a method call on that struct.
|
||||
// The returned Sexp is a hash that represents the result of that call.
|
||||
func CallGoMethodFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) {
|
||||
Q("_method user func running!\n")
|
||||
|
||||
// protect against bad calls/bad reflection
|
||||
var wasPanic bool
|
||||
var recovered interface{}
|
||||
tr := make([]byte, 16384)
|
||||
trace := &tr
|
||||
sx, err := func() (Sexp, error) {
|
||||
defer func() {
|
||||
recovered = recover()
|
||||
if recovered != nil {
|
||||
wasPanic = true
|
||||
nbyte := runtime.Stack(*trace, false)
|
||||
*trace = (*trace)[:nbyte]
|
||||
}
|
||||
}()
|
||||
|
||||
narg := len(args)
|
||||
if narg < 2 {
|
||||
return SexpNull, WrongNargs
|
||||
}
|
||||
obj, isHash := args[0].(*SexpHash)
|
||||
if !isHash {
|
||||
return SexpNull, fmt.Errorf("_method error: first argument must be a hash or defmap (a record) with an attached GoObject")
|
||||
}
|
||||
|
||||
var methodname string
|
||||
switch m := args[1].(type) {
|
||||
case *SexpSymbol:
|
||||
methodname = m.name
|
||||
case *SexpStr:
|
||||
methodname = m.S
|
||||
default:
|
||||
return SexpNull, fmt.Errorf("_method error: second argument must be a method name in symbol or string form (got %T)", args[1])
|
||||
}
|
||||
|
||||
// get the method list, verify the method exists and get its type
|
||||
if obj.NumMethod == -1 {
|
||||
err := obj.SetMethodList(env)
|
||||
if err != nil {
|
||||
return SexpNull, fmt.Errorf("could not get method list for object: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
var method reflect.Method
|
||||
found := false
|
||||
for _, me := range obj.GoMethods {
|
||||
if me.Name == methodname {
|
||||
method = me
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return SexpNull, fmt.Errorf("no such method '%s' on %s. choices are: %s",
|
||||
methodname, obj.TypeName,
|
||||
(obj.GoMethSx).SexpString(nil))
|
||||
}
|
||||
// INVAR: var method holds our call target
|
||||
|
||||
// try always expecting this to be already done... test crashes
|
||||
//P("in CallGoMethod '%s' obj.GoShadowStructVa = '%#v'", methodname, obj.GoShadowStructVa)
|
||||
if obj.GoShadowStructVa.Kind() == reflect.Invalid {
|
||||
// ready the struct... but only because there isn't already a shadow struct there!!
|
||||
if !obj.ShadowSet {
|
||||
_, err := ToGoFunction(env, "togo", []Sexp{obj})
|
||||
if err != nil {
|
||||
return SexpNull, fmt.Errorf("error converting object to Go struct: '%s'", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
inputVa := []reflect.Value{(obj.GoShadowStructVa)}
|
||||
|
||||
// prep args.
|
||||
needed := method.Type.NumIn() - 1 // one for the receiver
|
||||
avail := narg - 2
|
||||
if needed != avail {
|
||||
// TODO: support varargs eventually
|
||||
return SexpNull, fmt.Errorf("method %s needs %d arguments, but we have %d", method.Name, needed, avail)
|
||||
}
|
||||
|
||||
var va reflect.Value
|
||||
for i := 2; i < narg; i++ {
|
||||
typ := method.Type.In(i - 1)
|
||||
pdepth := PointerDepth(typ)
|
||||
// we only handle 0 and 1 for now
|
||||
Q("pdepth = %v\n", pdepth)
|
||||
switch pdepth {
|
||||
case 0:
|
||||
va = reflect.New(typ)
|
||||
case 1:
|
||||
// handle the common single pointer to struct case
|
||||
va = reflect.New(typ.Elem())
|
||||
default:
|
||||
return SexpNull, fmt.Errorf("error converting %d-th argument to "+
|
||||
"Go: we don't handle double pointers", i-2)
|
||||
}
|
||||
Q("converting to go '%#v' into -> %#v\n", args[i], va.Interface())
|
||||
iface, err := SexpToGoStructs(args[i], va.Interface(), env, nil)
|
||||
if err != nil {
|
||||
return SexpNull, fmt.Errorf("error converting %d-th "+
|
||||
"argument to Go: '%s'", i-2, err)
|
||||
}
|
||||
switch pdepth {
|
||||
case 0:
|
||||
inputVa = append(inputVa, reflect.ValueOf(iface).Elem())
|
||||
case 1:
|
||||
inputVa = append(inputVa, reflect.ValueOf(iface))
|
||||
}
|
||||
Q("\n allocated new %T/val=%#v /i=%#v\n", va, va, va.Interface())
|
||||
}
|
||||
|
||||
//P("_method: about to .Call by reflection!\n")
|
||||
|
||||
out := method.Func.Call(inputVa)
|
||||
|
||||
var iout []interface{}
|
||||
for _, o := range out {
|
||||
iout = append(iout, o.Interface())
|
||||
}
|
||||
Q("done with _method call, iout = %#v\n", iout)
|
||||
Q("done with _method call, iout[0] = %#v\n", iout[0])
|
||||
|
||||
nout := len(out)
|
||||
r := make([]Sexp, 0)
|
||||
for i := 0; i < nout; i++ {
|
||||
f := out[i].Interface()
|
||||
switch e := f.(type) {
|
||||
case nil:
|
||||
r = append(r, SexpNull)
|
||||
case int64:
|
||||
r = append(r, &SexpInt{Val: e})
|
||||
case int:
|
||||
r = append(r, &SexpInt{Val: int64(e)})
|
||||
case error:
|
||||
r = append(r, &SexpError{e})
|
||||
case string:
|
||||
r = append(r, &SexpStr{S: e})
|
||||
case float64:
|
||||
r = append(r, &SexpFloat{Val: e})
|
||||
case []byte:
|
||||
r = append(r, &SexpRaw{Val: e})
|
||||
case rune:
|
||||
r = append(r, &SexpChar{Val: e})
|
||||
default:
|
||||
// go through the type registry
|
||||
found := false
|
||||
for hashName, factory := range GoStructRegistry.Registry {
|
||||
st, err := factory.Factory(env, nil)
|
||||
if err != nil {
|
||||
return SexpNull, fmt.Errorf("MakeHash '%s' problem on Factory call: %s",
|
||||
hashName, err)
|
||||
}
|
||||
Q("got st from Factory, checking if types match")
|
||||
if reflect.ValueOf(st).Type() == out[i].Type() {
|
||||
Q("types match")
|
||||
retHash, err := MakeHash([]Sexp{}, factory.RegisteredName, env)
|
||||
if err != nil {
|
||||
return SexpNull, fmt.Errorf("MakeHash '%s' problem: %s",
|
||||
hashName, err)
|
||||
}
|
||||
|
||||
Q("filling from shadow")
|
||||
err = retHash.FillHashFromShadow(env, f)
|
||||
if err != nil {
|
||||
return SexpNull, err
|
||||
}
|
||||
r = append(r, retHash)
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
r = append(r, &SexpReflect{Val: out[i]})
|
||||
}
|
||||
}
|
||||
}
|
||||
return env.NewSexpArray(r), nil
|
||||
}()
|
||||
if wasPanic {
|
||||
return SexpNull, fmt.Errorf("\n recovered from panic "+
|
||||
"during CallGo. panic on = '%v'\n"+
|
||||
"stack trace:\n%s\n", recovered, string(*trace))
|
||||
}
|
||||
return sx, err
|
||||
}
|
||||
|
||||
// detect if inteface is holding anything
|
||||
func NilOrHoldsNil(iface interface{}) bool {
|
||||
if iface == nil {
|
||||
return true
|
||||
}
|
||||
return reflect.ValueOf(iface).IsNil()
|
||||
}
|
||||
Reference in New Issue
Block a user