tinygo-org / tinygo

Go compiler for small places. Microcontrollers, WebAssembly (WASM/WASI), and command-line tools. Based on LLVM.
https://tinygo.org
Other
15.33k stars 905 forks source link

how to write this code `pointer := reflect.ValueOf(reflect.TypeOf(false)).Pointer()` in tinygo? #2858

Closed hunjixin closed 1 year ago

hunjixin commented 2 years ago
pointer := reflect.ValueOf(reflect.TypeOf(false)).Pointer()

this code is golang, and in tinygo got panic, how to write it in tinygo?

skabbes commented 2 years ago

Reflection isn't very well supported in tinygo at the moment - there are some parts that are being worked on, but I don't know if I'd ever count of complete support for reflection.

The code you wrote, doesn't look like it need reflection in particular however - as it appears to just be constructing a *bool

hunjixin commented 2 years ago

here is the cast : I converted the go code to wasm to run, but there is a dependency package that has this code

https://github.com/polydawn/refmt/blob/30ac6d18308e584ca6a2e74ba81475559db94c5f/obj/builtins.go#L8

hunjixin commented 2 years ago

the author uses this type address as the map key,this maybe slightly faster than the reflect type as the key

skabbes commented 2 years ago

Here is the relevant piece of code, just FYI:

https://github.com/tinygo-org/tinygo/blob/release/src/reflect/value.go#L135-L149

hunjixin commented 2 years ago

https://github.com/tinygo-org/tinygo/blob/release/src/reflect/value.go#L135-L149

yeap, i have dig here, its Uintptr type, maybe can convert this value to unsafe.Pointer and return. its okay?

dankegel commented 2 years ago

The dependency you mentioned uses reflect heavily, so might be hard to get running in tinygo... though I haven't checked. https://github.com/polydawn/refmt

hunjixin commented 2 years ago

@dankegel In fact, it may not be used, but it's in the init, and it fails in the initAll func when just start program....

dkegel-fastly commented 2 years ago

So, I tried running polydown/refmt's tests in tinygo, but they failed as follows:

refmt dkegel$ GO111MODULE=off GOPATH="$PWD/.gopath/" tinygo test ./... -v
# github.com/smartystreets/assertions/internal/go-render/render
.gopath/src/github.com/smartystreets/assertions/internal/go-render/render/render.go:318:19: MapOf not declared by package reflect
.gopath/src/github.com/smartystreets/assertions/internal/go-render/render/render.go:418:15: av.InterfaceData undefined (type reflect.Value has no field or method InterfaceData)
.gopath/src/github.com/smartystreets/assertions/internal/go-render/render/render.go:418:35: bv.InterfaceData undefined (type reflect.Value has no field or method InterfaceData)
FAIL
hunjixin commented 2 years ago

very said, this is the dependency of the project's dependency, and most of the functions are not used. I just want to make it as easy as possible to get past this init issue, there are too many to rewrites. I'm also not sure if other dependencies have the same issue.

as metion before could i add code in Pointer to resolve Uintptr type like this, i am sure its okay or not

// Pointer returns the underlying pointer of the given value for the following
// types: chan, map, pointer, unsafe.Pointer, slice, func.
func (v Value) Pointer() uintptr {
    switch v.Kind() {
    case Chan, Map, Ptr, UnsafePointer:
        return uintptr(v.pointer())
    case Slice:
        slice := (*sliceHeader)(v.value)
        return uintptr(slice.data)
    case Uintptr:
        return uintptr(v.value)
    case Func:
        panic("unimplemented: (reflect.Value).Pointer()")
    default: // not implemented: Func
        panic(&ValueError{Method: "Pointer"})
    }
}
codefromthecrypt commented 2 years ago

Ack there are many issues open about reflection, but I figured I would get specific on this one, as we didn't have the full source. @hunjixin is this what you were trying, and also on https://github.com/tinygo-org/tinygo/issues/2858#issuecomment-1133519485 can you be specific to which file you are patching?

package main

import "reflect"

var pointer = reflect.ValueOf(reflect.TypeOf(false)).Pointer()

func main() {
}

normal

panic: reflect: call of UnsafePointer on zero Value
Abort trap: 6

wasi

panic: reflect: call of UnsafePointer on zero Value
Error: failed to run main module `main.wasm`

Caused by:
    0: failed to invoke command default
    1: wasm trap: wasm `unreachable` instruction executed
       wasm backtrace:
           0:  0x93f - <unknown>!runtime._panic
           1:  0x45c - <unknown>!(reflect.Value).Pointer
           2: 0x25cd - <unknown>!runtime.initAll
           3: 0x255a - <unknown>!runtime.run$1
           4: 0x2498 - <unknown>!runtime.run$1$gowrapper
           5: 0x2616 - <unknown>!tinygo_launch
           6: 0x235c - <unknown>!_start
aykevl commented 2 years ago

@hunjixin the code change you have suggested is not correct, because it does not match the API of the reflect package:

Pointer returns v's value as a uintptr. It returns uintptr instead of unsafe.Pointer so that code using reflect cannot obtain unsafe.Pointers without importing the unsafe package explicitly. It panics if v's Kind is not Chan, Func, Map, Pointer, Slice, or UnsafePointer.

A uintptr is not a pointer. Therefore, Pointer() shouldn't return it as a pointer.

But it looks like there is a bug here because reflect.ValueOf(reflect.TypeOf(false)).Pointer() does not panic in Go.

aykevl commented 2 years ago

On second thought, it looks like the code is doing something non-portable:

var (
    rtid_bool    = ValueOf(TypeOf(false)).Pointer()
    rtid_string  = ValueOf(TypeOf("")).Pointer()
    rtid_bytes   = ValueOf(TypeOf([]byte{})).Pointer()
    rtid_int     = ValueOf(TypeOf(int(0))).Pointer()
    rtid_int8    = ValueOf(TypeOf(int8(0))).Pointer()
    rtid_int16   = ValueOf(TypeOf(int16(0))).Pointer()
    rtid_int32   = ValueOf(TypeOf(int32(0))).Pointer()
    rtid_int64   = ValueOf(TypeOf(int64(0))).Pointer()
    rtid_uint    = ValueOf(TypeOf(uint(0))).Pointer()
    rtid_uint8   = ValueOf(TypeOf(uint8(0))).Pointer()
    rtid_uint16  = ValueOf(TypeOf(uint16(0))).Pointer()
    rtid_uint32  = ValueOf(TypeOf(uint32(0))).Pointer()
    rtid_uint64  = ValueOf(TypeOf(uint64(0))).Pointer()
    rtid_uintptr = ValueOf(TypeOf(uintptr(0))).Pointer()
    rtid_float32 = ValueOf(TypeOf(float32(0))).Pointer()
    rtid_float64 = ValueOf(TypeOf(float64(0))).Pointer()
)

It looks like they are inspecting the underlying type of reflect.Type. Don't do that. It's an implementation detail. I don't think there is much that TinyGo can do (although #2640 might fix this as an unintended side effect).

dgryski commented 1 year ago

These are now unique, but please don't do this.

~/go/src/github.com/dgryski/bug/m $ cat main.go
package main

import (
    . "reflect"
)

func main() {
    var (
        rtid_bool    = ValueOf(TypeOf(false)).Pointer()
        rtid_string  = ValueOf(TypeOf("")).Pointer()
        rtid_bytes   = ValueOf(TypeOf([]byte{})).Pointer()
        rtid_int     = ValueOf(TypeOf(int(0))).Pointer()
        rtid_int8    = ValueOf(TypeOf(int8(0))).Pointer()
        rtid_int16   = ValueOf(TypeOf(int16(0))).Pointer()
        rtid_int32   = ValueOf(TypeOf(int32(0))).Pointer()
        rtid_int64   = ValueOf(TypeOf(int64(0))).Pointer()
        rtid_uint    = ValueOf(TypeOf(uint(0))).Pointer()
        rtid_uint8   = ValueOf(TypeOf(uint8(0))).Pointer()
        rtid_uint16  = ValueOf(TypeOf(uint16(0))).Pointer()
        rtid_uint32  = ValueOf(TypeOf(uint32(0))).Pointer()
        rtid_uint64  = ValueOf(TypeOf(uint64(0))).Pointer()
        rtid_uintptr = ValueOf(TypeOf(uintptr(0))).Pointer()
        rtid_float32 = ValueOf(TypeOf(float32(0))).Pointer()
        rtid_float64 = ValueOf(TypeOf(float64(0))).Pointer()
    )

    println(rtid_bool)
    println(rtid_string)
    println(rtid_bytes)
    println(rtid_int)
    println(rtid_int8)
    println(rtid_int16)
    println(rtid_int32)
    println(rtid_int64)
    println(rtid_uint)
    println(rtid_uint8)
    println(rtid_uint16)
    println(rtid_uint32)
    println(rtid_uint64)
    println(rtid_uintptr)
    println(rtid_float32)
    println(rtid_float64)
}
~/go/src/github.com/dgryski/bug/m $ tinygo run main.go |sort |uniq -c |sort -n
   1 0x00000001065f0030
   1 0x00000001065f0070
   1 0x00000001065f00b0
   1 0x00000001065f00f8
   1 0x00000001065f0138
   1 0x00000001065f0178
   1 0x00000001065f01b8
   1 0x00000001065f01f8
   1 0x00000001065f0238
   1 0x00000001065f0278
   1 0x00000001065f02b8
   1 0x00000001065f02f8
   1 0x00000001065f0338
   1 0x00000001065f0378
   1 0x00000001065f03b8
   1 0x00000001065f03f8
hunjixin commented 1 year ago

good work. I don't want to do this, but it's from someone else's library. i have to fork and replace it before.

deadprogram commented 1 year ago

This is part of the v0.28 release so now closing this issue. Thanks!