go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/llx/data_conversions.go (about)

     1  // Copyright (c) Mondoo, Inc.
     2  // SPDX-License-Identifier: BUSL-1.1
     3  
     4  package llx
     5  
     6  import (
     7  	"encoding/hex"
     8  	"errors"
     9  	"fmt"
    10  	"reflect"
    11  	"strconv"
    12  	"time"
    13  
    14  	"go.mondoo.com/cnquery/types"
    15  	"google.golang.org/protobuf/proto"
    16  )
    17  
    18  type (
    19  	dataConverter      func(interface{}, types.Type) (*Primitive, error)
    20  	primitiveConverter func(*Primitive) *RawData
    21  )
    22  
    23  var (
    24  	dataConverters      map[types.Type]dataConverter
    25  	primitiveConverters map[types.Type]primitiveConverter
    26  )
    27  
    28  func init() {
    29  	dataConverters = map[types.Type]dataConverter{
    30  		types.Unset:        unset2result,
    31  		types.Nil:          nil2result,
    32  		types.Bool:         bool2result,
    33  		types.Int:          int2result,
    34  		types.Float:        float2result,
    35  		types.String:       string2result,
    36  		types.Regex:        regex2result,
    37  		types.Time:         time2result,
    38  		types.Dict:         dict2result,
    39  		types.Score:        score2result,
    40  		types.Empty:        empty2result,
    41  		types.Block:        block2result,
    42  		types.ArrayLike:    array2result,
    43  		types.MapLike:      map2result,
    44  		types.ResourceLike: resource2result,
    45  		types.FunctionLike: function2result,
    46  	}
    47  
    48  	primitiveConverters = map[types.Type]primitiveConverter{
    49  		types.Unset:        punset2raw,
    50  		types.Nil:          pnil2raw,
    51  		types.Bool:         pbool2raw,
    52  		types.Int:          pint2raw,
    53  		types.Float:        pfloat2raw,
    54  		types.String:       pstring2raw,
    55  		types.Regex:        pregex2raw,
    56  		types.Time:         ptime2raw,
    57  		types.Dict:         pdict2raw,
    58  		types.Score:        pscore2raw,
    59  		types.Empty:        pempty2raw,
    60  		types.Block:        pblock2rawV2,
    61  		types.ArrayLike:    parray2raw,
    62  		types.MapLike:      pmap2raw,
    63  		types.ResourceLike: presource2raw,
    64  		types.FunctionLike: pfunction2raw,
    65  		types.Ref:          pref2raw,
    66  	}
    67  }
    68  
    69  func dict2primitive(value interface{}) (*Primitive, error) {
    70  	if value == nil {
    71  		return NilPrimitive, nil
    72  	}
    73  
    74  	switch x := value.(type) {
    75  	case bool:
    76  		return BoolPrimitive(x), nil
    77  	case int64:
    78  		return IntPrimitive(x), nil
    79  	case float64:
    80  		return FloatPrimitive(x), nil
    81  	case string:
    82  		return StringPrimitive(x), nil
    83  	case []interface{}:
    84  		res := make([]*Primitive, len(x))
    85  		var err error
    86  		for i := range x {
    87  			res[i], err = dict2primitive(x[i])
    88  			if err != nil {
    89  				return nil, err
    90  			}
    91  		}
    92  		return &Primitive{Type: string(types.Array(types.Dict)), Array: res}, nil
    93  
    94  	case map[string]interface{}:
    95  		res := make(map[string]*Primitive, len(x))
    96  		var err error
    97  		for k, v := range x {
    98  			res[k], err = dict2primitive(v)
    99  			if err != nil {
   100  				return nil, err
   101  			}
   102  		}
   103  		return &Primitive{Type: string(types.Map(types.String, types.Dict)), Map: res}, nil
   104  
   105  	default:
   106  		return nil, errors.New("failed to convert dict to primitive, unsupported child type: " + reflect.TypeOf(x).String())
   107  	}
   108  }
   109  
   110  func primitive2dictV2(p *Primitive) (interface{}, error) {
   111  	switch types.Type(p.Type).Underlying() {
   112  	case types.Nil:
   113  		return nil, nil
   114  	case types.Bool:
   115  		return bytes2bool(p.Value), nil
   116  	case types.Int:
   117  		return bytes2int(p.Value), nil
   118  	case types.Float:
   119  		return bytes2float(p.Value), nil
   120  	case types.String:
   121  		return string(p.Value), nil
   122  	case types.ArrayLike:
   123  		d, _, err := primitive2array(nil, 0, p.Array)
   124  		return d, err
   125  	case types.MapLike:
   126  		m, err := primitive2mapV2(p.Map)
   127  		return m, err
   128  	default:
   129  		hexType := make([]byte, hex.EncodedLen(len(p.Type)))
   130  		hex.Encode(hexType, []byte(p.Type))
   131  		return nil, errors.New("unknown type to convert dict primitive back to raw data (" + string(hexType) + ")")
   132  	}
   133  }
   134  
   135  func unset2result(value interface{}, typ types.Type) (*Primitive, error) {
   136  	return UnsetPrimitive, nil
   137  }
   138  
   139  func nil2result(value interface{}, typ types.Type) (*Primitive, error) {
   140  	return NilPrimitive, nil
   141  }
   142  
   143  func errInvalidConversion(value interface{}, expectedType types.Type) error {
   144  	return fmt.Errorf("could not convert %T to %s", value, expectedType.Label())
   145  }
   146  
   147  func bool2result(value interface{}, typ types.Type) (*Primitive, error) {
   148  	v, ok := value.(bool)
   149  	if !ok {
   150  		return nil, errInvalidConversion(value, typ)
   151  	}
   152  	return BoolPrimitive(v), nil
   153  }
   154  
   155  func ref2resultV2(value interface{}, typ types.Type) (*Primitive, error) {
   156  	v, ok := value.(uint64)
   157  	if !ok {
   158  		return nil, errInvalidConversion(value, typ)
   159  	}
   160  	return RefPrimitiveV2(v), nil
   161  }
   162  
   163  func int2result(value interface{}, typ types.Type) (*Primitive, error) {
   164  	if v, ok := value.(int64); ok {
   165  		return IntPrimitive(v), nil
   166  	}
   167  	// try to convert float64, which happens when we load this from JSON
   168  	if v, ok := value.(float64); ok {
   169  		return IntPrimitive(int64(v)), nil
   170  	}
   171  	return nil, errInvalidConversion(value, typ)
   172  }
   173  
   174  func float2result(value interface{}, typ types.Type) (*Primitive, error) {
   175  	v, ok := value.(float64)
   176  	if !ok {
   177  		return nil, errInvalidConversion(value, typ)
   178  	}
   179  	return FloatPrimitive(v), nil
   180  }
   181  
   182  func string2result(value interface{}, typ types.Type) (*Primitive, error) {
   183  	v, ok := value.(string)
   184  	if !ok {
   185  		return nil, errInvalidConversion(value, typ)
   186  	}
   187  	return StringPrimitive(v), nil
   188  }
   189  
   190  func regex2result(value interface{}, typ types.Type) (*Primitive, error) {
   191  	v, ok := value.(string)
   192  	if !ok {
   193  		return nil, errInvalidConversion(value, typ)
   194  	}
   195  	return RegexPrimitive(v), nil
   196  }
   197  
   198  func time2result(value interface{}, typ types.Type) (*Primitive, error) {
   199  	v, ok := value.(*time.Time)
   200  	if !ok {
   201  		return nil, errInvalidConversion(value, typ)
   202  	}
   203  	return TimePrimitive(v), nil
   204  }
   205  
   206  func dict2result(value interface{}, typ types.Type) (*Primitive, error) {
   207  	prim, err := dict2primitive(value)
   208  	if err != nil {
   209  		return nil, err
   210  	}
   211  
   212  	raw, err := proto.MarshalOptions{Deterministic: true}.Marshal(prim)
   213  	if err != nil {
   214  		return nil, err
   215  	}
   216  
   217  	return &Primitive{Type: string(types.Dict), Value: raw}, nil
   218  }
   219  
   220  func score2result(value interface{}, typ types.Type) (*Primitive, error) {
   221  	v, ok := value.([]byte)
   222  	if !ok {
   223  		return nil, errInvalidConversion(value, typ)
   224  	}
   225  	return &Primitive{
   226  		Type:  string(types.Score),
   227  		Value: v,
   228  	}, nil
   229  }
   230  
   231  func empty2result(value interface{}, typ types.Type) (*Primitive, error) {
   232  	return EmptyPrimitive, nil
   233  }
   234  
   235  func block2result(value interface{}, typ types.Type) (*Primitive, error) {
   236  	m, ok := value.(map[string]interface{})
   237  	if !ok {
   238  		return nil, errInvalidConversion(value, typ)
   239  	}
   240  	res := make(map[string]*Primitive)
   241  
   242  	for k, v := range m {
   243  		raw, ok := v.(*RawData)
   244  		if !ok {
   245  			return nil, errInvalidConversion(value, typ)
   246  		}
   247  		res[k] = raw.Result().Data
   248  	}
   249  	return &Primitive{Type: string(typ), Map: res}, nil
   250  }
   251  
   252  func array2result(value interface{}, typ types.Type) (*Primitive, error) {
   253  	arr, ok := value.([]interface{})
   254  	if !ok {
   255  		return nil, errInvalidConversion(value, typ)
   256  	}
   257  	res := make([]*Primitive, len(arr))
   258  	ct := typ.Child()
   259  	var err error
   260  	for i := range arr {
   261  		res[i], err = raw2primitive(arr[i], ct)
   262  		if err != nil {
   263  			return nil, err
   264  		}
   265  	}
   266  	return &Primitive{Type: string(typ), Array: res}, nil
   267  }
   268  
   269  func stringmap2result(value interface{}, typ types.Type) (*Primitive, error) {
   270  	m, ok := value.(map[string]interface{})
   271  	if !ok {
   272  		return nil, errInvalidConversion(value, typ)
   273  	}
   274  	res := make(map[string]*Primitive)
   275  	ct := typ.Child()
   276  	var err error
   277  	for k, v := range m {
   278  		res[k], err = raw2primitive(v, ct)
   279  		if err != nil {
   280  			return nil, err
   281  		}
   282  	}
   283  	return &Primitive{Type: string(typ), Map: res}, nil
   284  }
   285  
   286  func intmap2result(value interface{}, typ types.Type) (*Primitive, error) {
   287  	m, ok := value.(map[int32]interface{})
   288  	if !ok {
   289  		return nil, errInvalidConversion(value, typ)
   290  	}
   291  	res := make(map[string]*Primitive)
   292  	ct := typ.Child()
   293  	var err error
   294  	for k, v := range m {
   295  		res[strconv.FormatInt(int64(k), 10)], err = raw2primitive(v, ct)
   296  		if err != nil {
   297  			return nil, err
   298  		}
   299  	}
   300  	return &Primitive{Type: string(typ), Map: res}, nil
   301  }
   302  
   303  func map2result(value interface{}, typ types.Type) (*Primitive, error) {
   304  	switch typ.Key() {
   305  	case types.String:
   306  		return stringmap2result(value, typ)
   307  	case types.Int:
   308  		return intmap2result(value, typ)
   309  	default:
   310  		return nil, errors.New("only supports turning string or int maps into primitives, not " + typ.Label())
   311  	}
   312  }
   313  
   314  func resource2result(value interface{}, typ types.Type) (*Primitive, error) {
   315  	m, ok := value.(Resource)
   316  	if !ok {
   317  		return nil, errInvalidConversion(value, typ)
   318  	}
   319  	return &Primitive{Type: string(typ), Value: []byte(m.MqlID())}, nil
   320  }
   321  
   322  func function2result(value interface{}, typ types.Type) (*Primitive, error) {
   323  	v, ok := value.(uint64)
   324  	if ok {
   325  		return FunctionPrimitive(v), nil
   326  	}
   327  	return nil, errInvalidConversion(value, typ)
   328  }
   329  
   330  func raw2primitive(value interface{}, typ types.Type) (*Primitive, error) {
   331  	if value == nil {
   332  		// there are only few types whose value is allowed to be nil
   333  		switch typ {
   334  		case types.Unset:
   335  			return UnsetPrimitive, nil
   336  		default:
   337  			return NilPrimitive, nil
   338  		}
   339  	}
   340  
   341  	utyp := typ.Underlying()
   342  	c, ok := dataConverters[utyp]
   343  	if !ok {
   344  		rdata, ok := value.(*RawData)
   345  		if ok {
   346  			return raw2primitive(rdata.Value, rdata.Type)
   347  		}
   348  		return nil, errors.New("cannot serialize data type " + typ.Label())
   349  	}
   350  	return c(value, typ)
   351  }
   352  
   353  // Result converts the raw data into a proto-compliant data structure that
   354  // can be sent over the wire. It converts the interface{} value of RawData
   355  // into a []byte structure that is easily serializable
   356  func (r *RawData) Result() *Result {
   357  	errorMsg := ""
   358  
   359  	// In case we encounter an error we need to still construct the result object
   360  	// with the type information so it can be processed by the server
   361  	if r.Error != nil {
   362  		errorMsg = r.Error.Error()
   363  
   364  		// if the value is nil, we don't want to loose the type information,
   365  		// so we return it early before raw2primitive has a chance to change the
   366  		// type to nil
   367  		if r.Value == nil {
   368  			return &Result{
   369  				Data:  &Primitive{Type: string(r.Type)},
   370  				Error: errorMsg,
   371  			}
   372  		}
   373  	}
   374  
   375  	data, err := raw2primitive(r.Value, r.Type)
   376  	if err != nil {
   377  		// If we already have an error on record, we just return that instead.
   378  		// This typically only happens when the above check for Value==nil cannot
   379  		// be determined, because it is hidden behind an interface{}. See:
   380  		// https://stackoverflow.com/questions/43059653/golang-interfacenil-is-nil-or-not
   381  		if errorMsg == "" {
   382  			errorMsg = err.Error()
   383  		}
   384  		return &Result{
   385  			Data:  &Primitive{Type: string(r.Type)},
   386  			Error: errorMsg,
   387  		}
   388  	}
   389  	return &Result{
   390  		Data:  data,
   391  		Error: errorMsg,
   392  	}
   393  }
   394  
   395  func (r *RawData) CastResult(t types.Type) (*Result, error) {
   396  	errorMsg := ""
   397  
   398  	// In case we encounter an error we need to still construct the result object
   399  	// with the type information so it can be processed by the server
   400  	if r.Error != nil {
   401  		errorMsg = r.Error.Error()
   402  	}
   403  
   404  	// Allow any type to take on nil values
   405  	if r.Value == nil {
   406  		return &Result{
   407  			Data:  &Primitive{Type: string(t)},
   408  			Error: errorMsg,
   409  		}, nil
   410  	}
   411  
   412  	if t == types.Bool {
   413  		truthy, castable := r.IsTruthy()
   414  		if !castable {
   415  			return nil, fmt.Errorf("cannot cast from %s to %s", r.Type.Label(), t.Label())
   416  		}
   417  		return &Result{
   418  			Data:  BoolPrimitive(truthy),
   419  			Error: errorMsg,
   420  		}, nil
   421  	}
   422  
   423  	data, err := raw2primitive(r.Value, t)
   424  	if err != nil {
   425  		return nil, err
   426  	}
   427  	return &Result{
   428  		Data:  data,
   429  		Error: errorMsg,
   430  	}, nil
   431  }
   432  
   433  func (r *RawResult) CastResult(t types.Type) *Result {
   434  	res, err := r.Data.CastResult(t)
   435  	if err != nil {
   436  		return &Result{
   437  			CodeId: r.CodeID,
   438  			Data:   &Primitive{Type: string(t)},
   439  			Error:  err.Error(),
   440  		}
   441  	}
   442  	res.CodeId = r.CodeID
   443  	return res
   444  }
   445  
   446  // Result converts the raw result into a proto-compliant data structure that
   447  // can be sent over the wire. See RawData.Result()
   448  func (r *RawResult) Result() *Result {
   449  	res := r.Data.Result()
   450  	res.CodeId = r.CodeID
   451  	return res
   452  }
   453  
   454  func (r *Result) RawResultV2() *RawResult {
   455  	if r == nil {
   456  		return nil
   457  	}
   458  
   459  	res := &RawResult{
   460  		Data: r.RawData(),
   461  	}
   462  	res.CodeID = r.CodeId
   463  	return res
   464  }
   465  
   466  func (r *Result) RawData() *RawData {
   467  	if r == nil {
   468  		return nil
   469  	}
   470  
   471  	data := &RawData{}
   472  	if r.Data != nil {
   473  		// The type can be empty, when we do not have data
   474  		if r.Data.IsNil() || types.Type(r.Data.Type).NotSet() {
   475  			data.Type = types.Nil
   476  		} else {
   477  			data = r.Data.RawData()
   478  		}
   479  	}
   480  	if len(r.Error) > 0 {
   481  		data.Error = errors.New(r.Error)
   482  	}
   483  	return data
   484  }
   485  
   486  func punset2raw(p *Primitive) *RawData {
   487  	return UnsetData
   488  }
   489  
   490  func pnil2raw(p *Primitive) *RawData {
   491  	return NilData
   492  }
   493  
   494  func pbool2raw(p *Primitive) *RawData {
   495  	if len(p.Value) == 0 {
   496  		return &RawData{
   497  			Type:  types.Type(p.Type),
   498  			Value: false,
   499  		}
   500  	}
   501  	return BoolData(bytes2bool(p.Value))
   502  }
   503  
   504  func pint2raw(p *Primitive) *RawData {
   505  	if len(p.Value) == 0 {
   506  		return &RawData{
   507  			Type:  types.Type(p.Type),
   508  			Value: int64(0),
   509  		}
   510  	}
   511  	return IntData(bytes2int(p.Value))
   512  }
   513  
   514  func pfloat2raw(p *Primitive) *RawData {
   515  	if len(p.Value) == 0 {
   516  		return &RawData{
   517  			Type:  types.Type(p.Type),
   518  			Value: float64(0),
   519  		}
   520  	}
   521  	return FloatData(bytes2float(p.Value))
   522  }
   523  
   524  func pstring2raw(p *Primitive) *RawData {
   525  	return StringData(string(p.Value))
   526  }
   527  
   528  func pregex2raw(p *Primitive) *RawData {
   529  	return RegexData(string(p.Value))
   530  }
   531  
   532  func ptime2raw(p *Primitive) *RawData {
   533  	if len(p.Value) == 0 {
   534  		t := time.Unix(0, 0)
   535  		return &RawData{
   536  			Type:  types.Type(p.Type),
   537  			Value: &t,
   538  		}
   539  	}
   540  	return TimeData(bytes2time(p.Value))
   541  }
   542  
   543  func pdict2raw(p *Primitive) *RawData {
   544  	if p.Value == nil {
   545  		return &RawData{
   546  			Type:  types.Dict,
   547  			Value: nil,
   548  		}
   549  	}
   550  
   551  	res := Primitive{} // unmarshal placeholder
   552  	err := proto.Unmarshal(p.Value, &res)
   553  	if err != nil {
   554  		return &RawData{Error: err, Type: types.Dict}
   555  	}
   556  
   557  	raw, err := primitive2dictV2(&res)
   558  	return &RawData{Error: err, Type: types.Dict, Value: raw}
   559  }
   560  
   561  func pscore2raw(p *Primitive) *RawData {
   562  	if len(p.Value) == 0 {
   563  		return &RawData{
   564  			Value: int64(0),
   565  			Type:  types.Score,
   566  		}
   567  	}
   568  	return &RawData{Value: p.Value, Type: types.Score}
   569  }
   570  
   571  func pempty2raw(p *Primitive) *RawData {
   572  	return &RawData{Type: types.Type(p.Type)}
   573  }
   574  
   575  func pblock2rawV2(p *Primitive) *RawData {
   576  	d, err := primitive2rawdataMapV2(p.Map)
   577  	return &RawData{Value: d, Error: err, Type: types.Type(p.Type)}
   578  }
   579  
   580  func parray2raw(p *Primitive) *RawData {
   581  	// Note: We don't hand over the compiler here. Reason is that if you have
   582  	// primitives that have refs in them, you should properly resolve them
   583  	// during the execution of the code. This function is really only applicable
   584  	// much later when you try to just get to the values of the returned data.
   585  	d, _, err := primitive2array(nil, 0, p.Array)
   586  	if d == nil {
   587  		d = []interface{}{}
   588  	}
   589  	return &RawData{Value: d, Error: err, Type: types.Type(p.Type)}
   590  }
   591  
   592  func pmap2raw(p *Primitive) *RawData {
   593  	d, err := primitive2mapV2(p.Map)
   594  	return &RawData{Value: d, Error: err, Type: types.Type(p.Type)}
   595  }
   596  
   597  func presource2raw(p *Primitive) *RawData {
   598  	id := string(p.Value)
   599  	typ := types.Type(p.Type)
   600  	return &RawData{Value: &MockResource{
   601  		Name: typ.ResourceName(),
   602  		ID:   id,
   603  	}, Type: typ}
   604  }
   605  
   606  func pfunction2raw(p *Primitive) *RawData {
   607  	// note: function pointers can never have a value that is nil
   608  	rv := bytes2int(p.Value)
   609  	if rv>>32 != 0 {
   610  		return &RawData{Value: uint64(bytes2int(p.Value)), Type: types.Type(p.Type)}
   611  	} else {
   612  		return &RawData{Value: int32(bytes2int(p.Value)), Type: types.Type(p.Type)}
   613  	}
   614  }
   615  
   616  func pref2raw(p *Primitive) *RawData {
   617  	// note: refs can never have a value that is nil
   618  	rv := bytes2int(p.Value)
   619  	if rv>>32 != 0 {
   620  		return &RawData{Value: uint64(bytes2int(p.Value)), Type: types.Type(p.Type)}
   621  	} else {
   622  		return &RawData{Value: int32(bytes2int(p.Value)), Type: types.Type(p.Type)}
   623  	}
   624  }
   625  
   626  // Tries to resolve primitives; returns refs if they don't exist yet.
   627  // Returns nil and a ref != 0 if a value needs resolving.
   628  func primitive2array(b *blockExecutor, ref uint64, args []*Primitive) ([]interface{}, uint64, error) {
   629  	if args == nil {
   630  		return []interface{}{}, 0, nil
   631  	}
   632  
   633  	res := make([]interface{}, len(args))
   634  	for i := range args {
   635  		var cur *RawData
   636  
   637  		if b != nil && types.Type(args[i].Type) == types.Ref {
   638  			var rref uint64
   639  			var err error
   640  			cur, rref, err = b.resolveValue(args[i], ref)
   641  			if rref > 0 || err != nil {
   642  				return nil, rref, err
   643  			}
   644  		} else {
   645  			cur = args[i].RawData()
   646  		}
   647  
   648  		if cur != nil {
   649  			if cur.Error != nil {
   650  				return nil, 0, cur.Error
   651  			}
   652  			res[i] = cur.Value
   653  		}
   654  	}
   655  	return res, 0, nil
   656  }
   657  
   658  // Converts a map of primitives into a map of go data (no type info).
   659  // Return map is never nil.
   660  func primitive2mapV2(m map[string]*Primitive) (map[string]interface{}, error) {
   661  	if m == nil {
   662  		return map[string]interface{}{}, nil
   663  	}
   664  
   665  	res := make(map[string]interface{})
   666  	for k, v := range m {
   667  		if v == nil {
   668  			res[k] = nil
   669  			continue
   670  		}
   671  		cur := v.RawData()
   672  		if cur.Error != nil {
   673  			return nil, cur.Error
   674  		}
   675  		res[k] = cur.Value
   676  	}
   677  	return res, nil
   678  }
   679  
   680  // Converts a map of primitives into a map of RawData (to preserve type-info).
   681  // Return map is never nil.
   682  func primitive2rawdataMapV2(m map[string]*Primitive) (map[string]interface{}, error) {
   683  	if m == nil {
   684  		return map[string]interface{}{}, nil
   685  	}
   686  
   687  	res := make(map[string]interface{})
   688  	for k, v := range m {
   689  		if v == nil {
   690  			res[k] = nil
   691  			continue
   692  		}
   693  		cur := v.RawData()
   694  		if cur.Error != nil {
   695  			return nil, cur.Error
   696  		}
   697  		res[k] = cur
   698  	}
   699  	return res, nil
   700  }
   701  
   702  // RawData converts the primitive into the internal go-representation of the
   703  // data that can be used for computations
   704  func (p *Primitive) RawData() *RawData {
   705  	// FIXME: This is a stopgap. It points to an underlying problem that exists and needs fixing.
   706  	if p.GetType() == "" {
   707  		return &RawData{Error: errors.New("cannot convert primitive with NO type information")}
   708  	}
   709  
   710  	typ := types.Type(p.Type)
   711  	c, ok := primitiveConverters[typ.Underlying()]
   712  	if !ok {
   713  		return &RawData{Error: errors.New("cannot convert primitive to value for primitive type " + typ.Label())}
   714  	}
   715  	return c(p)
   716  }
   717  
   718  func (b *blockExecutor) lookupValue(ref uint64) (*RawData, uint64, error) {
   719  	if b == nil {
   720  		panic("value not computed")
   721  	}
   722  
   723  	res, ok := b.cache.Load(ref)
   724  	if !ok {
   725  		return b.parent.lookupValue(ref)
   726  	}
   727  	return res.Result, 0, res.Result.Error
   728  }
   729  
   730  func (b *blockExecutor) resolveRef(srcRef uint64, ref uint64) (*RawData, uint64, error) {
   731  	if !b.isInMyBlock(srcRef) {
   732  		// the value is provided by a parent
   733  		return b.parent.lookupValue(srcRef)
   734  	} else {
   735  		// check if the reference exists; if not connect it
   736  		res, ok := b.cache.Load(srcRef)
   737  		if !ok {
   738  			return b.connectRef(srcRef, ref)
   739  		}
   740  		return res.Result, 0, res.Result.Error
   741  	}
   742  }
   743  
   744  // returns the resolved argument if it's a ref; otherwise just the argument
   745  // returns the reference if something else needs executing before it can be computed
   746  // returns an error otherwise
   747  func (b *blockExecutor) resolveValue(arg *Primitive, ref uint64) (*RawData, uint64, error) {
   748  	typ := types.Type(arg.Type)
   749  	switch typ.Underlying() {
   750  	case types.Ref:
   751  		srcRef := uint64(bytes2int(arg.Value))
   752  		return b.resolveRef(srcRef, ref)
   753  	case types.ArrayLike:
   754  		res := make([]interface{}, len(arg.Array))
   755  		for i := range arg.Array {
   756  			c, ref, err := b.resolveValue(arg.Array[i], ref)
   757  			if ref != 0 || err != nil {
   758  				return nil, ref, err
   759  			}
   760  			res[i] = c.Value
   761  		}
   762  
   763  		// type is in arg.Value
   764  		return &RawData{
   765  			Type:  typ,
   766  			Value: res,
   767  		}, 0, nil
   768  	default:
   769  		v := arg.RawData()
   770  		return v, 0, v.Error
   771  	}
   772  }
   773  
   774  func TArr2Raw[T any](arr []T) []interface{} {
   775  	res := make([]interface{}, len(arr))
   776  	for i := range arr {
   777  		res[i] = arr[i]
   778  	}
   779  	return res
   780  }
   781  
   782  func TMap2Raw[T any](m map[string]T) map[string]interface{} {
   783  	res := make(map[string]interface{}, len(m))
   784  	for k, v := range m {
   785  		res[k] = v
   786  	}
   787  	return res
   788  }
   789  
   790  func TRaw2T[T any](v interface{}) T {
   791  	if res, ok := v.(T); ok {
   792  		return res
   793  	}
   794  	var res T
   795  	return res
   796  }
   797  
   798  func TRaw2TArr[T any](v interface{}) []T {
   799  	arr, ok := v.([]interface{})
   800  	if !ok {
   801  		return nil
   802  	}
   803  
   804  	res := make([]T, len(arr))
   805  	for i := range arr {
   806  		res[i], _ = arr[i].(T)
   807  	}
   808  	return res
   809  }
   810  
   811  func TRaw2TMap[T any](v interface{}) map[string]T {
   812  	m, ok := v.(map[string]interface{})
   813  	if !ok {
   814  		return nil
   815  	}
   816  
   817  	res := make(map[string]T, len(m))
   818  	for k, v := range m {
   819  		res[k], _ = v.(T)
   820  	}
   821  	return res
   822  }