github.com/hsfzxjy/dgo/go@v0.2.0/go_callback.go (about)

     1  package dgo
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"reflect"
     7  )
     8  
     9  func (p *Port) Pend(callback any) GoCallback {
    10  	callbackV := reflect.ValueOf(callback)
    11  	if callbackV.Kind() != reflect.Func {
    12  		panic("dgo:go: callback must be a function")
    13  	}
    14  
    15  	var callbackId uint64
    16  	for n := 0; n < 10; n++ {
    17  		callbackId = uint64(p.nextCallbackId.Add(1))
    18  		_, loaded := p.goCallbacks.LoadOrStore(callbackId, callbackV)
    19  		if !loaded {
    20  			goto STORE_SUCCESS
    21  		}
    22  	}
    23  	panic(fmt.Sprintf("dgo:go: too many callbacks pending on %s", p))
    24  
    25  STORE_SUCCESS:
    26  	return GoCallback{callbackId, p}
    27  }
    28  
    29  func Pend(callback any, port *Port) GoCallback {
    30  	return portMap.ResolvePort(port).Pend(callback)
    31  }
    32  
    33  type GoCallback struct {
    34  	id   uint64 // Id (32 bits)
    35  	port *Port
    36  }
    37  
    38  var _ _Serializable = GoCallback{}
    39  
    40  func (cb GoCallback) specialInt()              {}
    41  func (cb GoCallback) getKind() _SpecialIntKind { return sikGoCallback }
    42  func (cb GoCallback) getPayload() uint64       { return cb.id }
    43  
    44  func (gcb *GoCallback) Remove() {
    45  	gcb.port.goCallbacks.Delete(gcb.id)
    46  }
    47  
    48  func (gcb *GoCallback) Exists() bool {
    49  	_, loaded := gcb.port.goCallbacks.Load(gcb.id)
    50  	return loaded
    51  }
    52  
    53  type invokingGoCallback struct {
    54  	payload uint64 // Flag (16 bits) | Id (32 bits)
    55  	port    *Port
    56  }
    57  
    58  func (cb invokingGoCallback) String() string {
    59  	return fmt.Sprintf("invokingGoCallback[payload=%016X, port=%s]", cb.payload, cb.port)
    60  }
    61  
    62  func (cb invokingGoCallback) specialInt() {}
    63  
    64  func (cb invokingGoCallback) handleCObjects(objs []*Dart_CObject) {
    65  	id := cb.payload & callbackIdMask
    66  	cf := CallbackFlag(cb.payload)
    67  
    68  	var (
    69  		callbackV reflect.Value
    70  		loaded    bool
    71  	)
    72  
    73  	if cf.HasFallible() {
    74  		defer func() {
    75  			if p := recover(); p != nil {
    76  				log.Printf("%+v\n", p)
    77  			}
    78  		}()
    79  	}
    80  
    81  	if cf.HasPop() {
    82  		callbackV, loaded = cb.port.goCallbacks.LoadAndDelete(id)
    83  	} else {
    84  		callbackV, loaded = cb.port.goCallbacks.Load(id)
    85  	}
    86  	if !loaded {
    87  		panic(fmt.Sprintf("dgo:go: go callback not exist, %s", cb))
    88  	}
    89  
    90  	hasPackArray := cf.HasPackArray()
    91  
    92  	var values []reflect.Value
    93  	var args []any
    94  
    95  	if hasPackArray {
    96  		args = make([]any, 0, len(objs)+2)
    97  	} else {
    98  		values = make([]reflect.Value, 0, len(objs)+2)
    99  	}
   100  
   101  	if cf.HasWithContext() {
   102  		context := &InvokeContext{cf, cb.port}
   103  		if hasPackArray {
   104  			args = append(args, context)
   105  		} else {
   106  			values = append(values, reflect.ValueOf(context))
   107  		}
   108  	}
   109  	if cf.HasFast() {
   110  		if len(objs) != 0 {
   111  			panic(fmt.Sprintf("dgo:go: expect zero argument when called with FAST flag, %s", cb))
   112  		}
   113  		var arg any
   114  		switch cf.FastKind() {
   115  		case CFFK_VOID:
   116  			goto SKIP
   117  		case CFFK_NIL:
   118  			if hasPackArray {
   119  				args = append(args, nil)
   120  			} else {
   121  				values = append(values, reflect.ValueOf(&arg).Elem())
   122  			}
   123  			goto SKIP
   124  		case CFFK_NO:
   125  			arg = false
   126  		case CFFK_YES:
   127  			arg = true
   128  		}
   129  		if hasPackArray {
   130  			args = append(args, arg)
   131  		} else {
   132  			values = append(values, reflect.ValueOf(arg))
   133  		}
   134  	SKIP:
   135  	} else {
   136  		for _, obj := range objs {
   137  			arg := cobjectParse(cb.port, obj)
   138  			if hasPackArray {
   139  				args = append(args, arg)
   140  			} else if arg == nil {
   141  				values = append(values, reflect.ValueOf(&arg).Elem())
   142  			} else {
   143  				values = append(values, reflect.ValueOf(arg))
   144  			}
   145  		}
   146  	}
   147  	if hasPackArray {
   148  		values = []reflect.Value{reflect.ValueOf(args)}
   149  	}
   150  	callbackV.Call(values)
   151  }