github.com/0chain/gosdk@v1.17.11/wasmsdk/jsbridge/output.go (about)

     1  //go:build js && wasm
     2  // +build js,wasm
     3  
     4  package jsbridge
     5  
     6  import (
     7  	"reflect"
     8  	"syscall/js"
     9  )
    10  
    11  // OutputBinder convert Outputs from js.Value to reflect.Value
    12  type OutputBinder func([]reflect.Value) []js.Value
    13  
    14  // OutputBuilder binder builder
    15  type OutputBuilder struct {
    16  	fn      reflect.Type
    17  	numOut  int
    18  	binders []func(rv reflect.Value) js.Value
    19  }
    20  
    21  // NewOutputBuilder create OutputBuilder
    22  func NewOutputBuilder(fn reflect.Type) *OutputBuilder {
    23  	return &OutputBuilder{
    24  		fn:     fn,
    25  		numOut: fn.NumOut(),
    26  	}
    27  }
    28  
    29  // Build build OutputBinder
    30  // js.ValueOf returns x as a JavaScript value:
    31  //
    32  //  | Go                     | JavaScript             |
    33  //  | ---------------------- | ---------------------- |
    34  //  | js.Value               | [its value]            |
    35  //  | js.Func                | function               |
    36  //  | nil                    | null                   |
    37  //  | bool                   | boolean                |
    38  //  | integers and floats    | number                 |
    39  //  | string                 | string                 |
    40  //  | []interface{}          | new array              |
    41  //  | map[string]interface{} | new object             |
    42  //
    43  // Panics if x is not one of the expected types.
    44  func (b *OutputBuilder) Build() (OutputBinder, error) {
    45  
    46  	b.binders = make([]func(rv reflect.Value) js.Value, b.numOut)
    47  
    48  	for i := 0; i < b.numOut; i++ {
    49  		outputType := b.fn.Out(i)
    50  
    51  		// TODO: Fast path for basic types that do not require reflection.
    52  		switch outputType.String() {
    53  		case TypeError:
    54  			b.binders[i] = func(rv reflect.Value) js.Value {
    55  				if rv.IsNil() {
    56  					return js.Null()
    57  				}
    58  
    59  				err := rv.Interface().(error)
    60  				if err != nil {
    61  					jsErr := NewJsError(err.Error())
    62  					return js.ValueOf(jsErr)
    63  				}
    64  				return js.Null()
    65  
    66  			}
    67  		case TypeBytes:
    68  			b.binders[i] = func(rv reflect.Value) js.Value {
    69  				if rv.IsNil() {
    70  					return js.Null()
    71  				}
    72  
    73  				buf := rv.Interface().([]byte)
    74  
    75  				return NewBytes(buf)
    76  
    77  			}
    78  		default:
    79  			b.binders[i] = func(rv reflect.Value) js.Value {
    80  
    81  				if rv.CanInterface() && rv.Interface() == nil {
    82  					return js.Null()
    83  				}
    84  
    85  				return NewObject(rv.Interface())
    86  			}
    87  		}
    88  	}
    89  
    90  	return b.Bind, nil
    91  }
    92  
    93  // Bind bind js Outputs to reflect values
    94  func (b *OutputBuilder) Bind(args []reflect.Value) []js.Value {
    95  	values := make([]js.Value, b.numOut)
    96  	for i := 0; i < b.numOut; i++ {
    97  		values[i] = b.binders[i](args[i])
    98  	}
    99  	return values
   100  }