diff --git a/gonative.go b/gonative.go index 92a07f0715b..24359fc80fe 100644 --- a/gonative.go +++ b/gonative.go @@ -169,6 +169,7 @@ func go2GnoValue(rv reflect.Value) (tv TypedValue) { tv.V = &nativeValue{Value: rv} return } +REFLECT_KIND_SWITCH: tv.T = go2GnoType(rv.Type()) switch rk := rv.Kind(); rk { case reflect.Bool: @@ -204,7 +205,12 @@ func go2GnoValue(rv reflect.Value) (tv TypedValue) { case reflect.Func: tv.V = &nativeValue{rv} case reflect.Interface: - tv.V = &nativeValue{rv} + if rv.IsNil() { + tv.V = nil // nil-interface, "undefined". + } else { + rv = rv.Elem() + goto REFLECT_KIND_SWITCH + } case reflect.Map: tv.V = &nativeValue{rv} case reflect.Ptr: @@ -594,7 +600,11 @@ func gno2GoType(t Type) reflect.Type { // gno.PointerValue. If rv is nil, an addressable one will be // constructed and returned, otherwise returns rv. func gno2GoValue(tv *TypedValue, rv reflect.Value) reflect.Value { - if tv.IsUndefined() { + if tv.IsNilInterface() { + rt := gno2GoType(tv.T) + rv = reflect.New(rt).Elem() + return rv + } else if tv.IsUndefined() { if debug { if !rv.IsValid() { panic("unexpected undefined gno value") diff --git a/op_assign.go b/op_assign.go index b623b9fd9c0..a4b650836b7 100644 --- a/op_assign.go +++ b/op_assign.go @@ -22,7 +22,7 @@ func (m *Machine) doOpAssign() { // NOTE: PopValues() returns a slice in // forward order, not the usual reverse. rvs := m.PopValues(len(s.Lhs)) - for i := len(s.Rhs) - 1; 0 <= i; i-- { + for i := len(s.Lhs) - 1; 0 <= i; i-- { // Pop lhs value and desired type. lv := m.PopAsPointer(s.Lhs[i]) lv.Assign2(m.Realm, rvs[i]) diff --git a/op_binary.go b/op_binary.go index 95dae10d5ed..0cf46d4d0c7 100644 --- a/op_binary.go +++ b/op_binary.go @@ -351,11 +351,14 @@ func assertTypes(lt, rt Type) { // TODO: can be much faster. func isEql(lv, rv *TypedValue) bool { + if lv.IsUndefined() { + return rv.IsUndefined() + } switch lv.T.Kind() { case BoolKind: return (lv.GetBool() == rv.GetBool()) case StringKind: - return (lv.V.(StringValue) == rv.V.(StringValue)) + return (lv.GetString() == rv.GetString()) case IntKind: return (lv.GetInt() == rv.GetInt()) case Int8Kind: @@ -557,7 +560,7 @@ func addAssign(lv, rv *TypedValue) { // NOTE this block is replicated in op_assign.go switch lv.T.Kind() { case StringKind: - lv.V = lv.GetString() + rv.GetString() + lv.V = StringValue(lv.GetString() + rv.GetString()) case IntKind: lv.SetInt(lv.GetInt() + rv.GetInt()) case Int8Kind: diff --git a/preprocess.go b/preprocess.go index d2becc6ddc3..93edd9812ae 100644 --- a/preprocess.go +++ b/preprocess.go @@ -526,7 +526,13 @@ func Preprocess(imp Importer, ctx BlockNode, n Node) Node { assertTypes(lt, rt) } } - if lnt, ok := lt.(*nativeType); ok { + if n.Op == EQL || n.Op == NEQ { + // If == or !=, no conversions. + } else if lnt, ok := lt.(*nativeType); ok { + // If left and right are native type, + // convert left and right to gno, then + // convert result back to native. + // // get concrete native base type. pt := go2GnoBaseType(lnt.Type).(PrimitiveType) // convert n.Left to (gno) pt type, @@ -545,7 +551,8 @@ func Preprocess(imp Importer, ctx BlockNode, n Node) Node { Source: n, Type: lnt, } - // reset/create n2 to preprocess children. + // reset/create n2 to preprocess + // children. n2 := &BinaryExpr{ Left: ln, Op: n.Op, @@ -554,8 +561,9 @@ func Preprocess(imp Importer, ctx BlockNode, n Node) Node { resn := Node(Call(tx, n2)) resn = Preprocess(imp, last, resn) return resn, TRANS_CONTINUE - // NOTE: binary operations are always computed in - // gno, never with reflect. + // NOTE: binary operations are always + // computed in gno, never with + // reflect. } else { // nothing to do. } diff --git a/tests/imports_test.go b/tests/imports_test.go index 8ae1d636c63..81892bf960e 100644 --- a/tests/imports_test.go +++ b/tests/imports_test.go @@ -4,6 +4,7 @@ import ( "bufio" "bytes" "compress/flate" + "context" "crypto/sha1" "encoding/binary" "encoding/xml" @@ -111,6 +112,12 @@ func testImporter(out io.Writer) gno.Importer { pkg := gno.NewPackageNode("flate", "flate", nil) pkg.DefineGoNativeValue("BestSpeed", flate.BestSpeed) return pkg.NewPackage(nil) + case "context": + pkg := gno.NewPackageNode("context", "context", nil) + pkg.DefineGoNativeType(reflect.TypeOf((*context.Context)(nil)).Elem()) + pkg.DefineGoNativeValue("WithValue", context.WithValue) + pkg.DefineGoNativeValue("Background", context.Background) + return pkg.NewPackage(nil) default: panic("unknown package path " + pkgPath) } diff --git a/values.go b/values.go index c8b5302a4a3..3589fe5e7f6 100644 --- a/values.go +++ b/values.go @@ -451,6 +451,24 @@ func (tv *TypedValue) IsUndefined() bool { } } return true + } else { + return tv.IsNilInterface() + } +} + +func (tv *TypedValue) IsNilInterface() bool { + if tv.T != nil && tv.T.Kind() == InterfaceKind { + if tv.V == nil { + return true + } else { + if debug { + if tv.N != [8]byte{} { + panic(fmt.Sprintf( + "corrupted TypeValue (nil interface)")) + } + } + return false + } } return false } @@ -622,7 +640,7 @@ func (tv *TypedValue) GetBool() bool { return *(*bool)(unsafe.Pointer(&tv.N)) } -func (tv *TypedValue) GetString() StringValue { +func (tv *TypedValue) GetString() string { if debug { if tv.T != nil && tv.T.Kind() != StringKind { panic(fmt.Sprintf( @@ -631,9 +649,9 @@ func (tv *TypedValue) GetString() StringValue { } } if tv.V == nil { - return StringValue("") + return "" } else { - return tv.V.(StringValue) + return string(tv.V.(StringValue)) } }