Update pkg to same Node addon Header #8

Merged
Sirherobrine23 merged 2 commits from rewrite_pkg into main 2025-04-27 06:59:22 +00:00
61 changed files with 13715 additions and 1149 deletions
Showing only changes of commit d5c8cdd68e - Show all commits

View File

@ -70,3 +70,16 @@ func (arr *Array) Seq() iter.Seq[ValueType] {
}
}
}
func (arr *Array) Append(values ...ValueType) error {
length, err := arr.Length()
if err != nil {
return err
}
for valueIndex := range values {
if err = arr.Set(length+valueIndex, values[valueIndex]); err != nil {
return err
}
}
return nil
}

20
boolean.go Normal file
View File

@ -0,0 +1,20 @@
package napi
import "sirherobrine23.com.br/Sirherobrine23/napi-go/internal/napi"
type Boolean struct{ value }
// Convert [ValueType] to [*Boolean]
func ToBoolean(o ValueType) *Boolean { return &Boolean{o} }
func CreateBoolean(env EnvType, value bool) (*Boolean, error) {
v, err := mustValueErr(napi.GetBoolean(env.NapiValue(), value))
if err != nil {
return nil, err
}
return ToBoolean(N_APIValue(env, v)), nil
}
func (bo *Boolean) Value() (bool, error) {
return mustValueErr(napi.GetValueBool(bo.NapiEnv(), bo.NapiValue()))
}

36
buffer.go Normal file
View File

@ -0,0 +1,36 @@
package napi
import "sirherobrine23.com.br/Sirherobrine23/napi-go/internal/napi"
type Buffer struct{ value }
// Convert [ValueType] to [*Buffer].
func ToBuffer(o ValueType) *Buffer { return &Buffer{o} }
// Create new Buffer with length
func CreateBuffer(env EnvType, length int) (*Buffer, error) {
napiValue, err := mustValueErr(napi.CreateBuffer(env.NapiValue(), length))
if err != nil {
return nil, err
}
return ToBuffer(N_APIValue(env, napiValue)), nil
}
// Copy []byte to Node::Buffer struct
func CopyBuffer(env EnvType, buff []byte) (*Buffer, error) {
napiValue, err := mustValueErr(napi.CreateBufferCopy(env.NapiValue(), buff))
if err != nil {
return nil, err
}
return ToBuffer(N_APIValue(env, napiValue)), nil
}
// Get size of buffer
func (buff *Buffer) Length() (int, error) {
return mustValueErr(napi.GetBufferInfoSize(buff.NapiEnv(), buff.NapiValue()))
}
// return []byte from Buffer value
func (buff *Buffer) Data() ([]byte, error) {
return mustValueErr(napi.GetBufferInfoData(buff.NapiEnv(), buff.NapiValue()))
}

29
date.go Normal file
View File

@ -0,0 +1,29 @@
package napi
import (
"time"
"sirherobrine23.com.br/Sirherobrine23/napi-go/internal/napi"
)
type Date struct{ value }
// Convert [ValueType] to [*Date]
func ToDate(o ValueType) *Date { return &Date{o} }
func CreateDate(env EnvType, date time.Time) (*Date, error) {
value, err := mustValueErr(napi.CreateDate(env.NapiValue(), float64(date.UnixMilli())))
if err != nil {
return nil, err
}
return &Date{value: &Value{env: env, valueOf: value}}, nil
}
// Get time from [*Date] object.
func (d Date) Time() (time.Time, error) {
timeFloat, err := mustValueErr(napi.GetDateValue(d.NapiEnv(), d.NapiValue()))
if err != nil {
return time.Time{}, err
}
return time.UnixMilli(int64(timeFloat)), nil
}

View File

@ -24,6 +24,8 @@ package entry
import "C"
import (
"fmt"
gonapi "sirherobrine23.com.br/Sirherobrine23/napi-go"
"sirherobrine23.com.br/Sirherobrine23/napi-go/internal/napi"
)
@ -37,6 +39,17 @@ func initializeModule(cEnv C.napi_env, cExports C.napi_value) C.napi_value {
env, exports := napi.Env(cEnv), napi.Value(cExports)
napi.InitializeInstanceData(env)
defer func() {
if err := recover(); err != nil {
switch v := err.(type) {
case error:
gonapi.ThrowError(gonapi.N_APIEnv(env), "", v.Error())
default:
gonapi.ThrowError(gonapi.N_APIEnv(env), "", fmt.Sprintf("%s", v))
}
}
}()
for _, registerCall := range modFuncInit {
registerCall(env, exports)
}
@ -44,7 +57,7 @@ func initializeModule(cEnv C.napi_env, cExports C.napi_value) C.napi_value {
return cExports
}
func Register(fn func(gonapi.EnvType, *gonapi.Object)) {
func Register(fn func(env gonapi.EnvType, export *gonapi.Object)) {
modFuncInit = append(modFuncInit, func(env napi.Env, object napi.Value) {
registerEnv := gonapi.N_APIEnv(env)
registerObj := gonapi.ToObject(gonapi.N_APIValue(registerEnv, object))

37
error.go Normal file
View File

@ -0,0 +1,37 @@
package napi
import (
"runtime"
"sirherobrine23.com.br/Sirherobrine23/napi-go/internal/napi"
)
type Error struct{ value }
func ToError(o ValueType) *Error { return &Error{o} }
func CreateError(env EnvType, msg string) (*Error, error) {
napiMsg, err := CreateString(env, msg)
if err != nil {
return nil, err
}
napiValue, err := mustValueErr(napi.CreateError(env.NapiValue(), nil, napiMsg.NapiValue()))
if err != nil {
return nil, err
}
return ToError(N_APIValue(env, napiValue)), nil
}
func (er *Error) ThrowAsJavaScriptException() error {
return singleMustValueErr(napi.Throw(er.NapiEnv(), er.NapiValue()))
}
// This throws a JavaScript Error with the text provided.
func ThrowError(env EnvType, code, err string) error {
if code == "" {
stackTraceBuf := make([]byte, 8192)
stackTraceSz := runtime.Stack(stackTraceBuf, false)
code = string(stackTraceBuf[:stackTraceSz])
}
return singleMustValueErr(napi.ThrowError(env.NapiValue(), code, err))
}

80
example/test.go Normal file
View File

@ -0,0 +1,80 @@
package main
import (
"encoding/json"
"net/netip"
"sirherobrine23.com.br/Sirherobrine23/napi-go"
"sirherobrine23.com.br/Sirherobrine23/napi-go/entry"
"sirherobrine23.com.br/Sirherobrine23/napi-go/js"
)
type Test struct {
Int int
String string
Sub []any
}
func init() {
entry.Register(func(env napi.EnvType, export *napi.Object) {
inNode, _ := napi.CreateString(env, "from golang napi string")
inNode2, _ := napi.CopyBuffer(env, []byte{1, 0, 244, 21})
toGoReflect := &Test{
Int: 14,
String: "From golang",
Sub: []any{
1,
[]string{"test", "gopher"},
[]bool{false, true},
[]int{23, 244, 10, 2024, 2025, 2000},
map[string]string{"exampleMap": "test"},
map[int]string{1: "one"},
map[bool]string{false: "false", true: "true"},
map[[2]string]string{[2]string{"go"}: "example"},
netip.IPv4Unspecified(),
netip.IPv6Unspecified(),
netip.AddrPortFrom(netip.IPv6Unspecified(), 19132),
nil,
true,
false,
inNode,
inNode2,
func() {
println("called in go")
},
},
}
napiStruct, err := js.ValueOf(env, toGoReflect)
if err != nil {
panic(err)
}
export.Set("goStruct", napiStruct)
fnCall, err := js.GoFuncOf(env, func(call ...any) (string, error) {
d, err := json.MarshalIndent(call, "", " ")
if err == nil {
println(string(d))
}
return string(d), err
})
if err != nil {
panic(err)
}
export.Set("printAny", fnCall)
fnCallStruct, err := js.GoFuncOf(env, func(call ...Test) (string, error) {
d, err := json.MarshalIndent(call, "", " ")
if err == nil {
println(string(d))
}
return string(d), err
})
if err != nil {
panic(err)
}
export.Set("printTestStruct", fnCallStruct)
})
}
func main() {}

96
function.go Normal file
View File

@ -0,0 +1,96 @@
package napi
import (
"fmt"
"sirherobrine23.com.br/Sirherobrine23/napi-go/internal/napi"
)
type Function struct{ value }
// Function to call on Javascript caller
type Callback func(env EnvType, this ValueType, args []ValueType) (ValueType, error)
// Convert [ValueType] to [*Function]
func ToFunction(o ValueType) *Function { return &Function{o} }
func CreateFunction(env EnvType, name string, callback Callback) (*Function, error) {
return CreateFunctionNapi(env, name, func(napiEnv napi.Env, info napi.CallbackInfo) napi.Value {
env := N_APIEnv(napiEnv)
cbInfo, err := mustValueErr(napi.GetCbInfo(napiEnv, info))
if err != nil {
ThrowError(env, "", err.Error())
return nil
}
this := N_APIValue(env, cbInfo.This)
args := make([]ValueType, len(cbInfo.Args))
for i, cbArg := range cbInfo.Args {
args[i] = N_APIValue(env, cbArg)
}
defer func() {
if err := recover(); err != nil {
switch v := err.(type) {
case error:
ThrowError(env, "", v.Error())
default:
ThrowError(env, "", fmt.Sprintf("panic recover: %s", err))
}
}
}()
res, err := callback(env, this, args)
switch {
case err != nil:
ThrowError(env, "", err.Error())
return nil
case res == nil:
und, _ := env.Undefined()
return und.NapiValue()
default:
typeOf, _ := res.Type()
if typeOf == TypeError {
ToError(res).ThrowAsJavaScriptException()
return nil
}
return res.NapiValue()
}
})
}
func CreateFunctionNapi(env EnvType, name string, callback napi.Callback) (*Function, error) {
fnCall, err := mustValueErr(napi.CreateFunction(env.NapiValue(), name, callback))
if err != nil {
return nil, err
}
return ToFunction(N_APIValue(env, fnCall)), nil
}
func (fn *Function) internalCall(this napi.Value, argc int, argv []napi.Value) (ValueType, error) {
// napi_call_function(env, global, add_two, argc, argv, &return_val);
res, err := mustValueErr(napi.CallFunction(fn.NapiEnv(), this, fn.NapiValue(), argc, argv))
if err != nil {
return nil, err
}
return N_APIValue(fn.Env(), res), nil
}
// Call function with custom global/this value
func (fn *Function) CallWithGlobal(this ValueType, args ...ValueType) (ValueType, error) {
argc := len(args)
argv := make([]napi.Value, argc)
for index := range argc {
argv[index] = args[index].NapiValue()
}
return fn.internalCall(this.NapiValue(), argc, argv)
}
// Call function with args
func (fn *Function) Call(args ...ValueType) (ValueType, error) {
global, err := fn.Env().Global()
if err != nil {
return nil, err
}
return fn.CallWithGlobal(global, args...)
}

View File

@ -201,6 +201,28 @@ func GetValueStringUtf8(env Env, value Value) (string, Status) {
), status
}
func GetValueStringUtf16(env Env, value Value) ([]uint16, Status) {
bufsize := C.size_t(0)
var strsize C.size_t
status := Status(C.napi_get_value_string_utf16(C.napi_env(env), C.napi_value(value), nil, bufsize, &strsize))
if status != StatusOK {
return nil, status
}
strsize++
cstr := (*C.char16_t)(C.malloc(C.sizeof_char * strsize))
defer C.free(unsafe.Pointer(cstr))
status = Status(C.napi_get_value_string_utf16(C.napi_env(env), C.napi_value(value), cstr, strsize, &strsize))
if status != StatusOK {
return nil, status
}
runes := make([]uint16, strsize)
copy(runes, (*[1 << 30]uint16)(unsafe.Pointer(cstr))[:strsize:strsize])
return runes, status
}
func SetProperty(env Env, object, key, value Value) Status {
return Status(C.napi_set_property(
C.napi_env(env),
@ -340,20 +362,6 @@ func GetInstanceData(env Env) (any, Status) {
return provider.GetUserData(), status
}
func CreateBuffer(env Env, length int) (Value, []byte, Status) {
var result Value
data := make([]byte, length)
dataPtr := unsafe.Pointer(&data[0])
status := Status(C.napi_create_buffer(
C.napi_env(env),
C.size_t(length),
&dataPtr,
(*C.napi_value)(unsafe.Pointer(&result)),
))
return result, data, status
}
func CreateExternal(env Env, data unsafe.Pointer, finalize Finalize, finalizeHint unsafe.Pointer) (Value, Status) {
var result Value
finalizer := FinalizeToFinalizer(finalize)
@ -469,18 +477,27 @@ func CoerceToString(env Env, value Value) (Value, Status) {
return result, status
}
func CreateBufferCopy(env Env, data []byte) (Value, *byte, Status) {
func CreateBuffer(env Env, length int) (Value, Status) {
var result Value
status := Status(C.napi_create_buffer(
C.napi_env(env),
C.size_t(length),
nil,
(*C.napi_value)(unsafe.Pointer(&result)),
))
return result, status
}
func CreateBufferCopy(env Env, data []byte) (Value, Status) {
var result Value
var copiedData *byte
copiedDataPtr := unsafe.Pointer(&copiedData)
status := Status(C.napi_create_buffer_copy(
C.napi_env(env),
C.size_t(len(data)),
unsafe.Pointer(&data[0]),
&copiedDataPtr,
nil,
(*C.napi_value)(unsafe.Pointer(&result)),
))
return result, copiedData, status
return result, status
}
func GetBufferInfo(env Env, value Value) (*byte, int, Status) {
@ -488,16 +505,29 @@ func GetBufferInfo(env Env, value Value) (*byte, int, Status) {
var length C.size_t
dataPtr := unsafe.Pointer(&data)
status := Status(C.napi_get_buffer_info(
C.napi_env(env),
C.napi_value(value),
&dataPtr,
&length,
))
status := Status(C.napi_get_buffer_info(C.napi_env(env), C.napi_value(value), &dataPtr, &length))
return data, int(length), status
}
func GetBufferInfoSize(env Env, value Value) (int, Status) {
var length C.size_t
status := Status(C.napi_get_buffer_info(C.napi_env(env), C.napi_value(value), nil, &length))
return int(length), status
}
func GetBufferInfoData(env Env, value Value) (buff []byte, status Status) {
var data *byte
var length C.size_t
dataPtr := unsafe.Pointer(&data)
status = Status(C.napi_get_buffer_info(C.napi_env(env), C.napi_value(value), &dataPtr, &length))
if status == StatusOK {
buff = make([]byte, length)
copy(buff, (*[1 << 30]byte)(unsafe.Pointer(data))[:length:length])
}
return
}
func GetArrayLength(env Env, value Value) (int, Status) {
var length C.uint32_t
status := Status(C.napi_get_array_length(

21
js/funcof.md Normal file
View File

@ -0,0 +1,21 @@
# Go functions conversion to Javascript functions
## Simples conversions
| Go | Node |
| --------------------------------- | ------------------------------------------- |
| `func()` | `function(): void` |
| `func() value` | `function(): value` |
| `func(value1, value2)` | `function(value1, value2): void` |
| `func() (value1, value2)` | `function(): [value1, value2]` |
| `func(value1, value2, values...)` | `function(value1, value2, ...values): void` |
| `func(values...)` | `function(...values): void` |
## Error throw
| Go | Node |
| ------------------------------------------ | --------------------------------------------------- |
| `func() error` | `function(): throw Error` |
| `func(value1, value2) error` | `function(value1, value2): throw Error` |
| `func() (value, error)` | `function(): value \|\| throw Error` |
| `func(value1, value2) (...nValues, error)` | `function(value1, value2): (Array \|\|throw Error)` |

575
js/js.go Normal file
View File

@ -0,0 +1,575 @@
package js
import (
"encoding"
"encoding/json"
"fmt"
"reflect"
"strconv"
"strings"
"time"
"sirherobrine23.com.br/Sirherobrine23/napi-go"
)
const propertiesTagName = "napi"
// Convert go types to valid NAPI, if not conpatible return Undefined.
func ValueOf(env napi.EnvType, value any) (napiValue napi.ValueType, err error) {
return valueOf(env, reflect.ValueOf(value))
}
// Convert NAPI value to Go values
func ValueFrom(napiValue napi.ValueType, v any) error {
ptr := reflect.ValueOf(v)
if ptr.Kind() != reflect.Pointer {
return fmt.Errorf("require point to convert napi value to go value")
}
return valueFrom(napiValue, ptr.Elem())
}
func valueOf(env napi.EnvType, ptr reflect.Value) (napiValue napi.ValueType, err error) {
defer func(err *error) {
if err2 := recover(); err2 != nil {
switch v := err2.(type) {
case error:
*err = v
default:
*err = fmt.Errorf("panic recover: %s", err2)
}
}
}(&err)
if !ptr.IsValid() || ptr.IsZero() {
return env.Undefined()
}
// Marshalers
if ptr.CanInterface() {
switch v := ptr.Interface().(type) {
case napi.ValueType:
return v, nil
case time.Time:
return napi.CreateDate(env, v)
case encoding.TextMarshaler:
data, err := v.MarshalText()
if err != nil {
return nil, err
}
return napi.CreateString(env, string(data))
case json.Marshaler:
var pointData any
data, err := v.MarshalJSON()
if err != nil {
return nil, err
} else if err = json.Unmarshal(data, &pointData); err != nil {
return nil, err
}
return ValueOf(env, pointData)
}
}
ptrType := ptr.Type()
switch ptrType.Kind() {
case reflect.Pointer:
return valueOf(env, ptr.Elem())
case reflect.String:
return napi.CreateString(env, ptr.String())
case reflect.Bool:
return napi.CreateBoolean(env, ptr.Bool())
case reflect.Int, reflect.Uint, reflect.Int32, reflect.Uint32, reflect.Float32, reflect.Int8, reflect.Uint8, reflect.Int16, reflect.Uint16:
return napi.CreateNumber(env, ptr.Int())
case reflect.Float64:
return napi.CreateNumber(env, ptr.Float())
case reflect.Int64, reflect.Uint64:
return napi.CreateBigint(env, ptr.Int())
case reflect.Func:
return funcOf(env, ptr)
case reflect.Slice, reflect.Array:
arr, err := napi.CreateArray(env, ptr.Len())
if err != nil {
return nil, err
}
for index := range ptr.Len() {
value, err := valueOf(env, ptr.Index(index))
if err != nil {
return arr, err
} else if err = arr.Set(index, value); err != nil {
return arr, err
}
}
return arr, nil
case reflect.Struct:
obj, err := napi.CreateObject(env)
if err != nil {
return nil, err
}
for keyIndex := range ptrType.NumField() {
field, fieldType := ptr.Field(keyIndex), ptrType.Field(keyIndex)
if !fieldType.IsExported() || fieldType.Tag.Get(propertiesTagName) == "-" {
continue
}
value, err := valueOf(env, field)
if err != nil {
return obj, err
}
typeof, err := value.Type()
if err != nil {
return nil, err
}
keyNamed := fieldType.Name
if strings.Count(fieldType.Tag.Get(propertiesTagName), ",") > 0 {
fields := strings.SplitN(fieldType.Tag.Get(propertiesTagName), ",", 2)
keyNamed = fields[0]
switch fields[1] {
case "omitempty":
switch typeof {
case napi.TypeUndefined, napi.TypeNull, napi.TypeUnkown:
continue
case napi.TypeString:
str, err := napi.ToString(value).Utf8Value()
if err != nil {
return nil, err
} else if str == "" {
continue
}
}
case "omitzero":
switch typeof {
case napi.TypeUndefined, napi.TypeNull, napi.TypeUnkown:
continue
case napi.TypeDate:
value, err := napi.ToDate(value).Time()
if err != nil {
return nil, err
} else if value.Unix() == 0 {
continue
}
case napi.TypeBigInt:
value, err := napi.ToBigint(value).Int64()
if err != nil {
return nil, err
} else if value == 0 {
continue
}
case napi.TypeNumber:
value, err := napi.ToNumber(value).Int()
if err != nil {
return nil, err
} else if value == 0 {
continue
}
case napi.TypeArray:
value, err := napi.ToArray(value).Length()
if err != nil {
return nil, err
} else if value == 0 {
continue
}
}
}
}
if err = obj.Set(keyNamed, value); err != nil {
return obj, err
}
}
return obj, nil
case reflect.Map:
obj, err := napi.CreateObject(env)
if err != nil {
return nil, err
}
for ptrKey, ptrValue := range ptr.Seq2() {
key, err := valueOf(env, ptrKey)
if err != nil {
return nil, err
}
value, err := valueOf(env, ptrValue)
if err != nil {
return nil, err
} else if err = obj.SetWithValue(key, value); err != nil {
return nil, err
}
}
return obj, nil
case reflect.Interface:
if ptr.IsValid() {
if ptr.IsNil() {
return env.Null()
} else if ptr.CanInterface() {
return valueOf(env, reflect.ValueOf(ptr.Interface()))
}
}
}
return env.Undefined()
}
func valueFrom(napiValue napi.ValueType, ptr reflect.Value) error {
typeOf, err := napiValue.Type()
if err != nil {
return err
}
ptrType := ptr.Type()
switch ptrType.Kind() {
case reflect.Pointer:
return valueFrom(napiValue, ptr.Elem())
case reflect.Interface:
// Check if is any and can set
if ptr.CanSet() && ptrType == reflect.TypeFor[any]() {
switch typeOf {
case napi.TypeNull, napi.TypeUndefined, napi.TypeUnkown:
ptr.Set(reflect.Zero(ptrType))
return nil
case napi.TypeBoolean:
valueOf, err := napi.ToBoolean(napiValue).Value()
if err != nil {
return err
}
ptr.Set(reflect.ValueOf(valueOf))
case napi.TypeNumber:
numberValue, err := napi.ToNumber(napiValue).Float()
if err != nil {
return err
}
ptr.Set(reflect.ValueOf(numberValue))
case napi.TypeBigInt:
numberValue, err := napi.ToBigint(napiValue).Int64()
if err != nil {
return err
}
ptr.Set(reflect.ValueOf(numberValue))
case napi.TypeString:
str, err := napi.ToString(napiValue).Utf8Value()
if err != nil {
return err
}
ptr.Set(reflect.ValueOf(str))
case napi.TypeDate:
timeDate, err := napi.ToDate(napiValue).Time()
if err != nil {
return err
}
ptr.Set(reflect.ValueOf(timeDate))
case napi.TypeArray:
napiArray := napi.ToArray(napiValue)
size, err := napiArray.Length()
if err != nil {
return err
}
value := reflect.MakeSlice(reflect.SliceOf(ptrType), size, size)
for index := range size {
napiValue, err := napiArray.Get(index)
if err != nil {
return err
} else if err = valueFrom(napiValue, value.Index(index)); err != nil {
return err
}
}
ptr.Set(value)
case napi.TypeBuffer:
buff, err := napi.ToBuffer(napiValue).Data()
if err != nil {
return err
}
ptr.Set(reflect.ValueOf(buff))
case napi.TypeObject:
obj := napi.ToObject(napiValue)
goMap := reflect.MakeMap(reflect.MapOf(reflect.TypeFor[string](), reflect.TypeFor[any]()))
for keyName, value := range obj.Seq() {
valueOf := reflect.New(reflect.TypeFor[any]())
if err := valueFrom(value, valueOf); err != nil {
return err
}
goMap.SetMapIndex(reflect.ValueOf(keyName), valueOf)
}
ptr.Set(goMap)
case napi.TypeFunction:
ptr.Set(reflect.ValueOf(napi.ToFunction(napiValue)))
}
return nil
}
return fmt.Errorf("cannot set value, returned %s", typeOf)
}
switch typeOf {
case napi.TypeNull, napi.TypeUndefined, napi.TypeUnkown:
switch ptrType.Kind() {
case reflect.Interface, reflect.Pointer:
ptr.Set(reflect.Zero(ptrType))
return nil
default:
return fmt.Errorf("cannot set value, returned %s", typeOf)
}
case napi.TypeBoolean:
switch ptr.Kind() {
case reflect.Bool:
valueOf, err := napi.ToBoolean(napiValue).Value()
if err != nil {
return err
}
ptr.SetBool(valueOf)
default:
return fmt.Errorf("cannot set boolean value to %s", ptr.Kind())
}
case napi.TypeNumber:
switch ptrType.Kind() {
case reflect.Float32, reflect.Float64:
floatValue, err := napi.ToNumber(napiValue).Float()
if err != nil {
return err
}
ptr.SetFloat(floatValue)
return nil
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
numberValue, err := napi.ToNumber(napiValue).Int()
if err != nil {
return err
}
ptr.SetInt(numberValue)
return nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
numberValue, err := napi.ToNumber(napiValue).Int()
if err != nil {
return err
}
ptr.SetUint(uint64(numberValue))
return nil
default:
return fmt.Errorf("cannot set number value to %s", ptr.Kind())
}
case napi.TypeBigInt:
switch ptrType.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
numberValue, err := napi.ToNumber(napiValue).Int()
if err != nil {
return err
}
ptr.SetInt(numberValue)
return nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
numberValue, err := napi.ToNumber(napiValue).Int()
if err != nil {
return err
}
ptr.SetUint(uint64(numberValue))
return nil
default:
return fmt.Errorf("cannot set number value to %s", ptr.Kind())
}
case napi.TypeString:
switch ptr.Kind() {
case reflect.String:
default:
return fmt.Errorf("cannot set string to %s", ptr.Kind())
}
str, err := napi.ToString(napiValue).Utf8Value()
if err != nil {
return err
}
ptr.Set(reflect.ValueOf(str))
return nil
case napi.TypeDate:
switch ptrType.Kind() {
case reflect.Struct:
if ptrType == reflect.TypeFor[time.Time]() {
break
}
fallthrough
default:
return fmt.Errorf("cannot set Date to %s", ptr.Kind())
}
timeDate, err := napi.ToDate(napiValue).Time()
if err != nil {
return err
}
ptr.Set(reflect.ValueOf(timeDate))
return nil
case napi.TypeArray:
napiArray := napi.ToArray(napiValue)
size, err := napiArray.Length()
if err != nil {
return err
}
switch ptr.Kind() {
case reflect.Slice:
value := reflect.MakeSlice(ptrType, size, size)
for index := range size {
napiValue, err := napiArray.Get(index)
if err != nil {
return err
} else if err = valueFrom(napiValue, value.Index(index)); err != nil {
return err
}
}
ptr.Set(value)
return nil
case reflect.Array:
value := reflect.New(ptrType)
for index := range min(size, value.Len()) {
napiValue, err := napiArray.Get(index)
if err != nil {
return err
} else if err = valueFrom(napiValue, value.Index(index)); err != nil {
return err
}
}
ptr.Set(value)
return nil
default:
return fmt.Errorf("cannot set Array to %s", ptr.Kind())
}
case napi.TypeBuffer:
switch ptr.Kind() {
case reflect.Slice:
if ptrType == reflect.TypeFor[[]byte]() {
break
}
fallthrough
default:
return fmt.Errorf("cannot set Buffer to %s", ptr.Kind())
}
buff, err := napi.ToBuffer(napiValue).Data()
if err != nil {
return err
}
ptr.SetBytes(buff)
return nil
case napi.TypeObject:
obj := napi.ToObject(napiValue)
switch ptr.Kind() {
case reflect.Struct:
ptr.Set(reflect.New(ptrType).Elem())
for keyIndex := range ptrType.NumField() {
field, fieldType := ptr.Field(keyIndex), ptrType.Field(keyIndex)
if !fieldType.IsExported() || fieldType.Tag.Get(propertiesTagName) == "-" {
continue
}
keyName, omitEmpty, omitZero := fieldType.Name, false, false
if strings.Count(fieldType.Tag.Get(propertiesTagName), ",") > 0 {
fields := strings.SplitN(fieldType.Tag.Get(propertiesTagName), ",", 2)
keyName = fields[0]
switch fields[1] {
case "omitempty":
omitEmpty = true
case "omitzero":
omitZero = true
}
} else {
omitEmpty, omitZero = true, true
}
if ok, err := obj.Has(keyName); err != nil {
return err
} else if !ok && !(omitEmpty || omitZero) {
return fmt.Errorf("cannot set %s to %s", keyName, ptr.Kind())
}
value, err := obj.Get(keyName)
if err != nil {
return err
}
valueTypeof, _ := value.Type()
if omitEmpty || omitZero {
switch valueTypeof {
case napi.TypeUndefined, napi.TypeNull, napi.TypeUnkown:
continue
case napi.TypeString:
if str, _ := napi.ToString(value).Utf8Value(); str == "" {
continue
}
case napi.TypeDate:
if timeDate, _ := napi.ToDate(value).Time(); timeDate.Unix() == 0 {
continue
}
case napi.TypeBigInt:
if numberValue, _ := napi.ToBigint(value).Int64(); numberValue == 0 {
continue
}
case napi.TypeNumber:
if numberValue, _ := napi.ToNumber(value).Int(); numberValue == 0 {
continue
}
case napi.TypeArray:
if size, _ := napi.ToArray(value).Length(); size == 0 {
continue
}
}
}
valueOf := reflect.New(fieldType.Type).Elem()
if err := valueFrom(value, valueOf); err != nil {
return err
}
field.Set(valueOf)
}
return nil
case reflect.Map:
// Check if key is string, bool, int*, uint*, float*, else return error
switch ptrType.Key().Kind() {
case reflect.String:
case reflect.Bool:
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
case reflect.Float32, reflect.Float64:
default:
return fmt.Errorf("cannot set Object to %s", ptr.Kind())
}
goMap := reflect.MakeMap(ptrType)
for keyName, value := range obj.Seq() {
keySetValue := reflect.New(ptrType.Key()).Elem()
switch ptrType.Key().Kind() {
case reflect.String:
keySetValue.SetString(keyName)
case reflect.Bool:
boolV, err := strconv.ParseBool(keyName)
if err != nil {
return err
}
keySetValue.SetBool(boolV)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
intV, err := strconv.ParseInt(keyName, 10, 64)
if err != nil {
return err
}
keySetValue.SetInt(intV)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
intV, err := strconv.ParseUint(keyName, 10, 64)
if err != nil {
return err
}
keySetValue.SetUint(intV)
case reflect.Float32, reflect.Float64:
floatV, err := strconv.ParseFloat(keyName, 64)
if err != nil {
return err
}
keySetValue.SetFloat(floatV)
}
valueOf := reflect.New(ptrType.Elem()).Elem()
if err := valueFrom(value, valueOf); err != nil {
return err
}
goMap.SetMapIndex(keySetValue, valueOf)
}
ptr.Set(goMap)
return nil
default:
return fmt.Errorf("cannot set Object to %s", ptr.Kind())
}
default:
println(typeOf.String())
}
return nil
}

123
js/js_func.go Normal file
View File

@ -0,0 +1,123 @@
package js
import (
"fmt"
"reflect"
"runtime"
"sirherobrine23.com.br/Sirherobrine23/napi-go"
internalNapi "sirherobrine23.com.br/Sirherobrine23/napi-go/internal/napi"
)
// Create napi bind to golang functions
func GoFuncOf(env napi.EnvType, function any) (napi.ValueType, error) {
return funcOf(env, reflect.ValueOf(function))
}
func funcOf(env napi.EnvType, ptr reflect.Value) (napi.ValueType, error) {
if ptr.Kind() != reflect.Func {
return nil, fmt.Errorf("return function to return napi value")
}
funcName := runtime.FuncForPC(ptr.Pointer()).Name()
switch v := ptr.Interface().(type) {
case nil:
return nil, nil
case napi.Callback:
return napi.CreateFunction(env, funcName, v)
case internalNapi.Callback:
return napi.CreateFunctionNapi(env, funcName, v)
default:
return napi.CreateFunction(env, funcName, func(env napi.EnvType, this napi.ValueType, args []napi.ValueType) (napi.ValueType, error) {
fnType := ptr.Type()
returnValues := []reflect.Value{}
switch {
case fnType.NumIn() == 0 && fnType.NumOut() == 0:
returnValues = ptr.Call([]reflect.Value{})
case !fnType.IsVariadic():
returnValues = ptr.Call(goValuesInFunc(ptr, args, false))
default:
returnValues = ptr.CallSlice(goValuesInFunc(ptr, args, true))
}
switch len(returnValues) {
case 0:
return nil, nil
case 1:
return valueOf(env, returnValues[0])
default:
lastValue := returnValues[len(returnValues)-1]
if lastValue.CanConvert(reflect.TypeFor[error]()) {
returnValues = returnValues[:len(returnValues)-1]
if !lastValue.IsNil() {
if err := lastValue.Interface().(error); err != nil {
return nil, err
}
}
}
switch len(returnValues) {
case 1:
// Return value from return
return valueOf(env, returnValues[0])
default:
// Create array
arr, err := napi.CreateArray(env, len(returnValues))
if err != nil {
return nil, err
}
// Append values to js array
for index, value := range returnValues {
napiValue, err := valueOf(env, value)
if err != nil {
return nil, err
} else if err = arr.Set(index, napiValue); err != nil {
return nil, err
}
}
return arr, nil
}
}
})
}
}
func goValuesInFunc(ptr reflect.Value, jsArgs []napi.ValueType, variadic bool) (values []reflect.Value) {
if variadic && (ptr.Type().NumIn()-1 > 0) && ptr.Type().NumIn()-1 < len(jsArgs) {
panic(fmt.Errorf("require minimun %d arguments, called with %d", ptr.Type().NumIn()-1, len(jsArgs)))
} else if !variadic &&ptr.Type().NumIn() != len(jsArgs) {
panic(fmt.Errorf("require %d arguments, called with %d", ptr.Type().NumIn(), len(jsArgs)))
}
size := ptr.Type().NumIn()
if variadic {
size--
}
values = make([]reflect.Value, size)
for index := range values {
valueOf := reflect.New(ptr.Type().In(index))
if err := valueFrom(jsArgs[index], valueOf); err != nil {
panic(err)
}
values[index] = valueOf
}
if variadic {
variadicType := ptr.Type().In(size).Elem()
valueAppend := jsArgs[size:]
valueOf := reflect.MakeSlice(reflect.SliceOf(variadicType), len(valueAppend), len(valueAppend))
for index := range valueAppend {
if err := valueFrom(valueAppend[index], valueOf.Index(index)); err != nil {
panic(err)
}
}
values = append(values, valueOf)
}
return
}

114
number.go Normal file
View File

@ -0,0 +1,114 @@
package napi
import (
"fmt"
"sirherobrine23.com.br/Sirherobrine23/napi-go/internal/napi"
)
type Number struct{ value }
type Bigint struct{ value }
// Convert [ValueType] to [*Number]
func ToNumber(o ValueType) *Number { return &Number{o} }
// Convert [ValueType] to [*Bigint]
func ToBigint(o ValueType) *Bigint { return &Bigint{o} }
func (num *Number) Float() (float64, error) {
return mustValueErr(napi.GetValueDouble(num.NapiEnv(), num.NapiValue()))
}
func (num *Number) Int() (int64, error) {
return mustValueErr(napi.GetValueInt64(num.NapiEnv(), num.NapiValue()))
}
func (num *Number) Uint32() (uint32, error) {
return mustValueErr(napi.GetValueUint32(num.NapiEnv(), num.NapiValue()))
}
func (num *Number) Int32() (int32, error) {
return mustValueErr(napi.GetValueInt32(num.NapiEnv(), num.NapiValue()))
}
func (big *Bigint) Int64() (int64, error) {
return mustValueErr2(napi.GetValueBigIntInt64(big.NapiEnv(), big.NapiValue()))
}
func (big *Bigint) Uint64() (uint64, error) {
return mustValueErr2(napi.GetValueBigIntUint64(big.NapiEnv(), big.NapiValue()))
}
func CreateBigint[T int64 | uint64](env EnvType, valueOf T) (*Bigint, error) {
var value napi.Value
var err error
switch v := any(valueOf).(type) {
case int64:
if value, err = mustValueErr(napi.CreateBigIntInt64(env.NapiValue(), v)); err != nil {
return nil, err
}
case uint64:
if value, err = mustValueErr(napi.CreateBigIntUint64(env.NapiValue(), v)); err != nil {
return nil, err
}
}
return &Bigint{value: &Value{env: env, valueOf: value}}, nil
}
func CreateNumber[T ~int | ~uint | ~int8 | ~uint8 | ~int16 | ~uint16 | ~int32 | ~uint32 | ~int64 | ~uint64 | ~float32 | ~float64](env EnvType, n T) (*Number, error) {
var value napi.Value
var err error
switch v := any(n).(type) {
case int:
if value, err = mustValueErr(napi.CreateInt64(env.NapiValue(), int64(v))); err != nil {
return nil, err
}
case uint:
if value, err = mustValueErr(napi.CreateInt64(env.NapiValue(), int64(v))); err != nil {
return nil, err
}
case int8:
if value, err = mustValueErr(napi.CreateInt64(env.NapiValue(), int64(v))); err != nil {
return nil, err
}
case uint8:
if value, err = mustValueErr(napi.CreateInt64(env.NapiValue(), int64(v))); err != nil {
return nil, err
}
case int16:
if value, err = mustValueErr(napi.CreateInt64(env.NapiValue(), int64(v))); err != nil {
return nil, err
}
case uint16:
if value, err = mustValueErr(napi.CreateInt64(env.NapiValue(), int64(v))); err != nil {
return nil, err
}
case int32:
if value, err = mustValueErr(napi.CreateInt32(env.NapiValue(), v)); err != nil {
return nil, err
}
case uint32:
if value, err = mustValueErr(napi.CreateUint32(env.NapiValue(), v)); err != nil {
return nil, err
}
case int64:
if value, err = mustValueErr(napi.CreateInt64(env.NapiValue(), v)); err != nil {
return nil, err
}
case uint64:
if value, err = mustValueErr(napi.CreateInt64(env.NapiValue(), int64(v))); err != nil {
return nil, err
}
case float32:
if value, err = mustValueErr(napi.CreateDouble(env.NapiValue(), float64(v))); err != nil {
return nil, err
}
case float64:
if value, err = mustValueErr(napi.CreateDouble(env.NapiValue(), v)); err != nil {
return nil, err
}
default:
return nil, fmt.Errorf("invalid number type")
}
return ToNumber(N_APIValue(env, value)), err
}

View File

@ -1,6 +1,10 @@
package napi
import "sirherobrine23.com.br/Sirherobrine23/napi-go/internal/napi"
import (
"iter"
"sirherobrine23.com.br/Sirherobrine23/napi-go/internal/napi"
)
type Object struct{ value }
@ -104,3 +108,27 @@ func (obj *Object) Freeze() error {
func (obj *Object) Seal() error {
return singleMustValueErr(napi.ObjectSeal(obj.NapiEnv(), obj.NapiValue()))
}
func (obj *Object) Seq() iter.Seq2[string, ValueType] {
keys, err := obj.GetPropertyNames()
if err != nil {
panic(err)
}
return func(yield func(string, ValueType) bool) {
for key := range keys.Seq() {
value, err := obj.GetWithValue(key)
if err != nil {
panic(err)
}
keyName, err := ToString(key).Utf8Value()
if err != nil {
panic(err)
}
if !yield(keyName, value) {
return
}
}
}
}

View File

@ -1,6 +1,10 @@
package napi
import "sirherobrine23.com.br/Sirherobrine23/napi-go/internal/napi"
import (
"unicode/utf16"
"sirherobrine23.com.br/Sirherobrine23/napi-go/internal/napi"
)
type String struct{ value }
@ -15,3 +19,26 @@ func CreateString(env EnvType, str string) (*String, error) {
}
return ToString(N_APIValue(env, napiString)), nil
}
// Create string to utf16
func CreateStringUtf16(env EnvType, str []rune) (*String, error) {
napiString, err := mustValueErr(napi.CreateStringUtf16(env.NapiValue(), utf16.Encode(str)))
if err != nil {
return nil, err
}
return ToString(N_APIValue(env, napiString)), nil
}
// Get String value.
func (str *String) Utf8Value() (string, error) {
return mustValueErr(napi.GetValueStringUtf8(str.NapiEnv(), str.NapiValue()))
}
// Converts a String value to a UTF-16 encoded in rune.
func (str *String) Utf16Value() ([]rune, error) {
valueOf, err := mustValueErr(napi.GetValueStringUtf16(str.NapiEnv(), str.NapiValue()))
if err != nil {
return nil, err
}
return utf16.Decode(valueOf), nil
}