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

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*.node

View File

@ -1,6 +1,7 @@
MIT License
Copyright (c) 2022 Akshay Ganeshen
Copyright (c) 2024 Matheus Sampaio Queiroga
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -1,82 +1,3 @@
# napi-go
A Go library for building Node.js Native Addons using Node-API.
## Usage
Use `go get` to install the library:
```sh
go get -u github.com/akshayganeshen/napi-go
```
Then use the library to define handlers:
```go
package handlers
import "github.com/akshayganeshen/napi-go"
func MyHandler(env napi.Env, info napi.CallbackInfo) napi.Value {
return nil
}
```
Next, create a `main.go` that registers all module exports:
```go
package main
import "github.com/akshayganeshen/napi-go/entry"
func init() {
entry.Export("myHandler", MyHandler)
}
func main() {}
```
Finally, build the Node.js addon using `go build`:
```sh
go build -buildmode=c-shared -o "example.node" .
```
The output `.node` file can now be imported via `require`:
```js
const example = require("./example.node");
example.myHandler();
```
### JS Helpers
In addition to the Node-API exposed via package `napi`, the `napi-go/js`
package provides functions similar to the `syscall/js` standard library.
```go
package main
import (
"github.com/akshayganeshen/napi-go/entry"
"github.com/akshayganeshen/napi-go/js"
)
func init() {
entry.Export("myCallback", js.AsCallback(MyCallback))
}
func MyCallback(env js.Env, this js.Value, args []js.Value) any {
return map[string]any{
"message": "hello world",
"args": args,
}
}
func main() {}
```
## Examples
Check out the example addons in [`docs/examples`](docs/examples).

85
array.go Normal file
View File

@ -0,0 +1,85 @@
package napi
import (
"iter"
"sirherobrine23.com.br/Sirherobrine23/napi-go/internal/napi"
)
type Array struct{ value }
// Convert [ValueType] to [*Array].
func ToArray(o ValueType) *Array { return &Array{o} }
// Create Array.
func CreateArray(env EnvType, size ...int) (*Array, error) {
sizeOf := 0
if len(size) > 0 {
sizeOf = size[0]
}
napiValue, err := napi.Value(nil), error(nil)
if sizeOf == 0 {
napiValue, err = mustValueErr(napi.CreateArray(env.NapiValue()))
} else {
napiValue, err = mustValueErr(napi.CreateArrayWithLength(env.NapiValue(), sizeOf))
}
// Check error exists
if err != nil {
return nil, err
}
return ToArray(N_APIValue(env, napiValue)), nil
}
// Get array length.
func (arr *Array) Length() (int, error) {
return mustValueErr(napi.GetArrayLength(arr.NapiEnv(), arr.NapiValue()))
}
// Delete index elemente from array.
func (arr *Array) Delete(index int) (bool, error) {
return mustValueErr(napi.DeleteElement(arr.NapiEnv(), arr.NapiValue(), index))
}
// Set value in index
func (arr *Array) Set(index int, value ValueType) error {
return singleMustValueErr(napi.SetElement(arr.NapiEnv(), arr.NapiValue(), index, value.NapiValue()))
}
// Get Value from index
func (arr *Array) Get(index int) (ValueType, error) {
napiValue, err := mustValueErr(napi.GetElement(arr.NapiEnv(), arr.NapiValue(), index))
if err != nil {
return nil, err
}
return N_APIValue(arr.Env(), napiValue), nil
}
// Get values with [iter.Seq]
func (arr *Array) Seq() iter.Seq[ValueType] {
length, err := arr.Length()
if err != nil {
return nil
}
return func(yield func(ValueType) bool) {
for index := range length {
if value, err := arr.Get(index); err == nil {
if !yield(value) {
return
}
}
}
}
}
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()))
}

186
c++/napi-inl.deprecated.h Normal file
View File

@ -0,0 +1,186 @@
#ifndef SRC_NAPI_INL_DEPRECATED_H_
#define SRC_NAPI_INL_DEPRECATED_H_
////////////////////////////////////////////////////////////////////////////////
// PropertyDescriptor class
////////////////////////////////////////////////////////////////////////////////
template <typename Getter>
inline PropertyDescriptor PropertyDescriptor::Accessor(
const char* utf8name,
Getter getter,
napi_property_attributes attributes,
void* /*data*/) {
using CbData = details::CallbackData<Getter, Napi::Value>;
// TODO: Delete when the function is destroyed
auto callbackData = new CbData({getter, nullptr});
return PropertyDescriptor({utf8name,
nullptr,
nullptr,
CbData::Wrapper,
nullptr,
nullptr,
attributes,
callbackData});
}
template <typename Getter>
inline PropertyDescriptor PropertyDescriptor::Accessor(
const std::string& utf8name,
Getter getter,
napi_property_attributes attributes,
void* data) {
return Accessor(utf8name.c_str(), getter, attributes, data);
}
template <typename Getter>
inline PropertyDescriptor PropertyDescriptor::Accessor(
napi_value name,
Getter getter,
napi_property_attributes attributes,
void* /*data*/) {
using CbData = details::CallbackData<Getter, Napi::Value>;
// TODO: Delete when the function is destroyed
auto callbackData = new CbData({getter, nullptr});
return PropertyDescriptor({nullptr,
name,
nullptr,
CbData::Wrapper,
nullptr,
nullptr,
attributes,
callbackData});
}
template <typename Getter>
inline PropertyDescriptor PropertyDescriptor::Accessor(
Name name, Getter getter, napi_property_attributes attributes, void* data) {
napi_value nameValue = name;
return PropertyDescriptor::Accessor(nameValue, getter, attributes, data);
}
template <typename Getter, typename Setter>
inline PropertyDescriptor PropertyDescriptor::Accessor(
const char* utf8name,
Getter getter,
Setter setter,
napi_property_attributes attributes,
void* /*data*/) {
using CbData = details::AccessorCallbackData<Getter, Setter>;
// TODO: Delete when the function is destroyed
auto callbackData = new CbData({getter, setter, nullptr});
return PropertyDescriptor({utf8name,
nullptr,
nullptr,
CbData::GetterWrapper,
CbData::SetterWrapper,
nullptr,
attributes,
callbackData});
}
template <typename Getter, typename Setter>
inline PropertyDescriptor PropertyDescriptor::Accessor(
const std::string& utf8name,
Getter getter,
Setter setter,
napi_property_attributes attributes,
void* data) {
return Accessor(utf8name.c_str(), getter, setter, attributes, data);
}
template <typename Getter, typename Setter>
inline PropertyDescriptor PropertyDescriptor::Accessor(
napi_value name,
Getter getter,
Setter setter,
napi_property_attributes attributes,
void* /*data*/) {
using CbData = details::AccessorCallbackData<Getter, Setter>;
// TODO: Delete when the function is destroyed
auto callbackData = new CbData({getter, setter, nullptr});
return PropertyDescriptor({nullptr,
name,
nullptr,
CbData::GetterWrapper,
CbData::SetterWrapper,
nullptr,
attributes,
callbackData});
}
template <typename Getter, typename Setter>
inline PropertyDescriptor PropertyDescriptor::Accessor(
Name name,
Getter getter,
Setter setter,
napi_property_attributes attributes,
void* data) {
napi_value nameValue = name;
return PropertyDescriptor::Accessor(
nameValue, getter, setter, attributes, data);
}
template <typename Callable>
inline PropertyDescriptor PropertyDescriptor::Function(
const char* utf8name,
Callable cb,
napi_property_attributes attributes,
void* /*data*/) {
using ReturnType = decltype(cb(CallbackInfo(nullptr, nullptr)));
using CbData = details::CallbackData<Callable, ReturnType>;
// TODO: Delete when the function is destroyed
auto callbackData = new CbData({cb, nullptr});
return PropertyDescriptor({utf8name,
nullptr,
CbData::Wrapper,
nullptr,
nullptr,
nullptr,
attributes,
callbackData});
}
template <typename Callable>
inline PropertyDescriptor PropertyDescriptor::Function(
const std::string& utf8name,
Callable cb,
napi_property_attributes attributes,
void* data) {
return Function(utf8name.c_str(), cb, attributes, data);
}
template <typename Callable>
inline PropertyDescriptor PropertyDescriptor::Function(
napi_value name,
Callable cb,
napi_property_attributes attributes,
void* /*data*/) {
using ReturnType = decltype(cb(CallbackInfo(nullptr, nullptr)));
using CbData = details::CallbackData<Callable, ReturnType>;
// TODO: Delete when the function is destroyed
auto callbackData = new CbData({cb, nullptr});
return PropertyDescriptor({nullptr,
name,
CbData::Wrapper,
nullptr,
nullptr,
nullptr,
attributes,
callbackData});
}
template <typename Callable>
inline PropertyDescriptor PropertyDescriptor::Function(
Name name, Callable cb, napi_property_attributes attributes, void* data) {
napi_value nameValue = name;
return PropertyDescriptor::Function(nameValue, cb, attributes, data);
}
#endif // !SRC_NAPI_INL_DEPRECATED_H_

6936
c++/napi-inl.h Normal file

File diff suppressed because it is too large Load Diff

3290
c++/napi.h Normal file

File diff suppressed because it is too large Load Diff

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

@ -1,49 +0,0 @@
package main
import (
"fmt"
"time"
"github.com/akshayganeshen/napi-go"
"github.com/akshayganeshen/napi-go/entry"
)
func init() {
entry.Export("getPromise", GetPromiseHandler)
}
func GetPromiseHandler(env napi.Env, info napi.CallbackInfo) napi.Value {
result, _ := napi.CreatePromise(env)
asyncResourceName, _ := napi.CreateStringUtf8(
env,
"napi-go/async-promise-example",
)
var asyncWork napi.AsyncWork
asyncWork, _ = napi.CreateAsyncWork(
env,
nil, asyncResourceName,
func(env napi.Env) {
fmt.Printf("AsyncExecuteCallback(start)\n")
defer fmt.Printf("AsyncExecuteCallback(stop)\n")
time.Sleep(time.Second)
},
func(env napi.Env, status napi.Status) {
defer napi.DeleteAsyncWork(env, asyncWork)
if status == napi.StatusCancelled {
fmt.Printf("AsyncCompleteCallback(cancelled)\n")
return
}
fmt.Printf("AsyncCompleteCallback\n")
resolution, _ := napi.CreateStringUtf8(env, "resolved")
napi.ResolveDeferred(env, result.Deferred, resolution)
},
)
napi.QueueAsyncWork(env, asyncWork)
return result.Value
}
func main() {}

View File

@ -1,25 +0,0 @@
package main
import (
"github.com/akshayganeshen/napi-go"
"github.com/akshayganeshen/napi-go/entry"
)
func init() {
entry.Export("getCallback", GetCallbackHandler)
}
func GetCallbackHandler(env napi.Env, info napi.CallbackInfo) napi.Value {
result, _ := napi.CreateFunction(
env,
"callback",
func(env napi.Env, info napi.CallbackInfo) napi.Value {
result, _ := napi.CreateStringUtf8(env, "hello world")
return result
},
)
return result
}
func main() {}

View File

@ -1,52 +0,0 @@
package main
import (
"github.com/akshayganeshen/napi-go"
"github.com/akshayganeshen/napi-go/entry"
)
func init() {
entry.Export("describeArgs", DescribeArgsHandler)
}
func DescribeArgsHandler(env napi.Env, info napi.CallbackInfo) napi.Value {
extractedInfo, _ := napi.GetCbInfo(env, info)
result, _ := napi.CreateArrayWithLength(env, len(extractedInfo.Args))
for i, arg := range extractedInfo.Args {
vt, _ := napi.Typeof(env, arg)
dv, _ := napi.CreateStringUtf8(env, DescribeValueType(vt))
napi.SetElement(env, result, i, dv)
}
return result
}
func DescribeValueType(vt napi.ValueType) string {
switch vt {
case napi.ValueTypeUndefined:
return "undefined"
case napi.ValueTypeNull:
return "null"
case napi.ValueTypeBoolean:
return "boolean"
case napi.ValueTypeNumber:
return "number"
case napi.ValueTypeString:
return "string"
case napi.ValueTypeSymbol:
return "symbol"
case napi.ValueTypeObject:
return "object"
case napi.ValueTypeFunction:
return "function"
case napi.ValueTypeExternal:
return "external"
case napi.ValueTypeBigint:
return "bigint"
default:
return "other"
}
}
func main() {}

View File

@ -1,19 +0,0 @@
package main
import (
"fmt"
"github.com/akshayganeshen/napi-go"
"github.com/akshayganeshen/napi-go/entry"
)
func init() {
entry.Export("hello", HelloHandler)
}
func HelloHandler(env napi.Env, info napi.CallbackInfo) napi.Value {
fmt.Println("hello world!")
return nil
}
func main() {}

View File

@ -1,81 +0,0 @@
package main
import (
"fmt"
"time"
"github.com/akshayganeshen/napi-go"
"github.com/akshayganeshen/napi-go/entry"
"github.com/akshayganeshen/napi-go/js"
)
func init() {
entry.Export("getMap", GetMapHandler)
entry.Export("getCallback", js.AsCallback(GetCallback))
entry.Export("getArray", js.AsCallback(GetArray))
entry.Export("getPromiseResolve", js.AsCallback(GetPromiseResolve))
entry.Export("getPromiseReject", js.AsCallback(GetPromiseReject))
}
func GetMapHandler(env napi.Env, info napi.CallbackInfo) napi.Value {
jsEnv := js.AsEnv(env)
return jsEnv.ValueOf(
map[string]any{
"string": "hello world",
"number": 123,
"bool": false,
"undefined": jsEnv.Undefined(),
"null": nil,
"function": jsEnv.FuncOf(
func(env js.Env, this js.Value, args []js.Value) any {
return "hello world"
},
),
},
).Value
}
func GetCallback(env js.Env, this js.Value, args []js.Value) any {
return func(env js.Env, this js.Value, args []js.Value) any {
return map[string]any{
"this": this,
"args": args,
}
}
}
func GetArray(env js.Env, this js.Value, args []js.Value) any {
return []any{
"hello world",
123,
true,
map[string]any{
"key": "value",
},
}
}
func GetPromiseResolve(env js.Env, this js.Value, args []js.Value) any {
promise := env.NewPromise()
go func() {
time.Sleep(time.Second)
promise.Resolve("resolved")
}()
return promise
}
func GetPromiseReject(env js.Env, this js.Value, args []js.Value) any {
promise := env.NewPromise()
go func() {
time.Sleep(time.Second)
promise.Reject(fmt.Errorf("rejected"))
}()
return promise
}
func main() {}

66
entry/entry.go Normal file
View File

@ -0,0 +1,66 @@
package entry
/*
#cgo CFLAGS: -DDEBUG
#cgo CFLAGS: -D_DEBUG
#cgo CFLAGS: -DV8_ENABLE_CHECKS
#cgo CFLAGS: -DNAPI_EXPERIMENTAL
#cgo CFLAGS: -I/usr/local/include/node
#cgo CXXFLAGS: -std=c++11
#cgo darwin LDFLAGS: -Wl,-undefined,dynamic_lookup
#cgo darwin LDFLAGS: -Wl,-no_pie
#cgo darwin LDFLAGS: -Wl,-search_paths_first
#cgo (darwin && amd64) LDFLAGS: -arch x86_64
#cgo (darwin && arm64) LDFLAGS: -arch arm64
#cgo linux LDFLAGS: -Wl,-unresolved-symbols=ignore-all
#cgo LDFLAGS: -L${SRCDIR}
#include <stdlib.h>
#include "./entry.h"
*/
import "C"
import (
"fmt"
gonapi "sirherobrine23.com.br/Sirherobrine23/napi-go"
"sirherobrine23.com.br/Sirherobrine23/napi-go/internal/napi"
)
type registerCallback func(env napi.Env, object napi.Value)
var modFuncInit = []registerCallback{}
//export initializeModule
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)
}
return cExports
}
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))
fn(registerEnv, registerObj)
})
}

View File

@ -7,9 +7,9 @@
extern "C" {
#endif /* __cplusplus */
// InitializeModule is a N-API module initialization function.
// InitializeModule is suitable for use as a napi_addon_register_func.
extern napi_value InitializeModule(
// initializeModule is a N-API module initialization function.
// initializeModule is suitable for use as a napi_addon_register_func.
extern napi_value initializeModule(
napi_env env,
napi_value exports
);

View File

@ -1,19 +0,0 @@
package entry
import (
"github.com/akshayganeshen/napi-go"
)
type napiGoExport struct {
Name string
Callback napi.Callback
}
var napiGoGlobalExports []napiGoExport
func Export(name string, callback napi.Callback) {
napiGoGlobalExports = append(napiGoGlobalExports, napiGoExport{
Name: name,
Callback: callback,
})
}

View File

@ -2,4 +2,4 @@
#include "./entry.h"
NAPI_MODULE(napiGo, InitializeModule)
NAPI_MODULE(napiGo, initializeModule)

View File

@ -1,26 +0,0 @@
package entry
/*
#include <stdlib.h>
#include "./entry.h"
*/
import "C"
import (
"github.com/akshayganeshen/napi-go"
)
//export InitializeModule
func InitializeModule(cEnv C.napi_env, cExports C.napi_value) C.napi_value {
env, exports := napi.Env(cEnv), napi.Value(cExports)
napi.InitializeInstanceData(env)
for _, export := range napiGoGlobalExports {
cb, _ := napi.CreateFunction(env, export.Name, export.Callback)
name, _ := napi.CreateStringUtf8(env, export.Name)
napi.SetProperty(env, exports, name, cb)
}
return cExports
}

View File

@ -1,21 +0,0 @@
package entry
/*
#cgo CFLAGS: -DDEBUG
#cgo CFLAGS: -D_DEBUG
#cgo CFLAGS: -DV8_ENABLE_CHECKS
#cgo CFLAGS: -DNAPI_EXPERIMENTAL
#cgo CFLAGS: -I/usr/local/include/node
#cgo CXXFLAGS: -std=c++11
#cgo darwin LDFLAGS: -Wl,-undefined,dynamic_lookup
#cgo darwin LDFLAGS: -Wl,-no_pie
#cgo darwin LDFLAGS: -Wl,-search_paths_first
#cgo darwin LDFLAGS: -arch x86_64
#cgo linux LDFLAGS: -Wl,-unresolved-symbols=ignore-all
#cgo LDFLAGS: -L${SRCDIR}
#cgo LDFLAGS: -stdlib=libc++
*/
import "C"

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...)
}

4
go.mod
View File

@ -1,3 +1,3 @@
module github.com/akshayganeshen/napi-go
module sirherobrine23.com.br/Sirherobrine23/napi-go
go 1.18
go 1.24

File diff suppressed because it is too large Load Diff

34
internal/napi/key_type.go Normal file
View File

@ -0,0 +1,34 @@
package napi
/*
#include <node/node_api.h>
*/
import "C"
// KeyCollectionMode type
type KeyCollectionMode C.napi_key_collection_mode
const (
KeyIncludePrototypes KeyCollectionMode = C.napi_key_include_prototypes
KeyOwnOnly KeyCollectionMode = C.napi_key_own_only
)
// KeyFilter type
type KeyFilter C.napi_key_filter
const (
KeyAllProperties KeyFilter = C.napi_key_all_properties
KeyWritable KeyFilter = C.napi_key_writable
KeyEnumerable KeyFilter = C.napi_key_enumerable
KeyConfigurable KeyFilter = C.napi_key_configurable
KeySkipStrings KeyFilter = C.napi_key_skip_strings
KeySkipSymbols KeyFilter = C.napi_key_skip_symbols
)
// KeyConversion type
type KeyConversion C.napi_key_conversion
const (
KeyKeepNumbers KeyConversion = C.napi_key_keep_numbers
KeyNumbersToStrings KeyConversion = C.napi_key_numbers_to_strings
)

View File

@ -11,11 +11,11 @@ package napi
#cgo darwin LDFLAGS: -Wl,-undefined,dynamic_lookup
#cgo darwin LDFLAGS: -Wl,-no_pie
#cgo darwin LDFLAGS: -Wl,-search_paths_first
#cgo darwin LDFLAGS: -arch x86_64
// #cgo darwin amd64 LDFLAGS: -arch x86_64
// #cgo darwin arm64 LDFLAGS: -arch arm64
#cgo linux LDFLAGS: -Wl,-unresolved-symbols=ignore-all
#cgo LDFLAGS: -L${SRCDIR}
#cgo LDFLAGS: -stdlib=libc++
*/
import "C"
import "C"

View File

@ -1,6 +1,7 @@
package napi
/*
#include <stdlib.h>
#include <node/node_api.h>
*/
import "C"
@ -113,11 +114,12 @@ func CreateThreadsafeFunction(
func CallThreadsafeFunction(
fn ThreadsafeFunction,
mode ThreadsafeFunctionCallMode,
) Status {
return Status(C.napi_call_threadsafe_function(
C.napi_threadsafe_function(fn),
nil,
C.napi_tsfn_blocking,
C.napi_threadsafe_function_call_mode(mode),
))
}
@ -129,9 +131,108 @@ func AcquireThreadsafeFunction(fn ThreadsafeFunction) Status {
func ReleaseThreadsafeFunction(
fn ThreadsafeFunction,
mode ThreadsafeFunctionReleaseMode,
) Status {
return Status(C.napi_release_threadsafe_function(
C.napi_threadsafe_function(fn),
C.napi_tsfn_release,
C.napi_threadsafe_function_release_mode(mode),
))
}
func GetThreadsafeFunctionContext(
fn ThreadsafeFunction,
) (unsafe.Pointer, Status) {
var context unsafe.Pointer
status := Status(C.napi_get_threadsafe_function_context(
C.napi_threadsafe_function(fn),
&context,
))
return context, status
}
func RefThreadsafeFunction(env Env, fn ThreadsafeFunction) Status {
return Status(C.napi_ref_threadsafe_function(
C.napi_env(env),
C.napi_threadsafe_function(fn),
))
}
func UnrefThreadsafeFunction(env Env, fn ThreadsafeFunction) Status {
return Status(C.napi_unref_threadsafe_function(
C.napi_env(env),
C.napi_threadsafe_function(fn),
))
}
func ThrowSyntaxError(env Env, code, msg string) Status {
codeCStr, msgCStr := C.CString(code), C.CString(msg)
defer C.free(unsafe.Pointer(codeCStr))
defer C.free(unsafe.Pointer(msgCStr))
return Status(C.node_api_throw_syntax_error(
C.napi_env(env),
codeCStr,
msgCStr,
))
}
func CreateSyntaxError(env Env, code, msg Value) (Value, Status) {
var result Value
status := Status(C.node_api_create_syntax_error(
C.napi_env(env),
C.napi_value(code),
C.napi_value(msg),
(*C.napi_value)(unsafe.Pointer(&result)),
))
return result, status
}
func SymbolFor(env Env, description string) (Value, Status) {
var result Value
status := Status(C.node_api_symbol_for(
C.napi_env(env),
C.CString(description),
C.size_t(len(description)),
(*C.napi_value)(unsafe.Pointer(&result)),
))
return result, status
}
func CreatePropertyKeyLatin1(env Env, str string) (Value, Status) {
cstr := C.CString(str)
defer C.free(unsafe.Pointer(cstr))
var result Value
status := Status(C.node_api_create_property_key_latin1(
C.napi_env(env),
cstr,
C.size_t(len([]byte(str))),
(*C.napi_value)(unsafe.Pointer(&result)),
))
return result, status
}
func CreatePropertyKeyUtf16(env Env, str []uint16) (Value, Status) {
var result Value
status := Status(C.node_api_create_property_key_utf16(
C.napi_env(env),
(*C.char16_t)(unsafe.Pointer(&str[0])),
C.size_t(len(str)),
(*C.napi_value)(unsafe.Pointer(&result)),
))
return result, status
}
func CreatePropertyKeyUtf8(env Env, str string) (Value, Status) {
cstr := C.CString(str)
defer C.free(unsafe.Pointer(cstr))
var result Value
status := Status(C.node_api_create_property_key_utf8(
C.napi_env(env),
cstr,
C.size_t(len([]byte(str))),
(*C.napi_value)(unsafe.Pointer(&result)),
))
return result, status
}

View File

@ -0,0 +1,18 @@
package napi
/*
#include <node/node_api.h>
*/
import "C"
type PropertyAttributes C.napi_property_attributes
const (
Default PropertyAttributes = C.napi_default
Writable PropertyAttributes = C.napi_writable
Enumerable PropertyAttributes = C.napi_enumerable
Configurable PropertyAttributes = C.napi_configurable
Static PropertyAttributes = C.napi_static
DefaultMethod PropertyAttributes = C.napi_default_method
DefaultJSProperty PropertyAttributes = C.napi_default_jsproperty
)

View File

@ -0,0 +1,20 @@
package napi
/*
#include <node/node_api.h>
*/
import "C"
type ThreadsafeFunctionReleaseMode C.napi_threadsafe_function_release_mode
const (
Release ThreadsafeFunctionReleaseMode = C.napi_tsfn_release
Abort ThreadsafeFunctionReleaseMode = C.napi_tsfn_abort
)
type ThreadsafeFunctionCallMode C.napi_threadsafe_function_call_mode
const (
NonBlocking ThreadsafeFunctionCallMode = C.napi_tsfn_nonblocking
Blocking ThreadsafeFunctionCallMode = C.napi_tsfn_blocking
)

View File

@ -0,0 +1,22 @@
package napi
/*
#include <node/node_api.h>
*/
import "C"
type TypedArrayType C.napi_typedarray_type
const (
TypedArrayInt8Array TypedArrayType = C.napi_int8_array
TypedArrayUint8Array TypedArrayType = C.napi_uint8_array
TypedArrayUint8ClampedArray TypedArrayType = C.napi_uint8_clamped_array
TypedArrayInt16Array TypedArrayType = C.napi_int16_array
TypedArrayUint16Array TypedArrayType = C.napi_uint16_array
TypedArrayInt32Array TypedArrayType = C.napi_int32_array
TypedArrayUint32Array TypedArrayType = C.napi_uint32_array
TypedArrayFloat32Array TypedArrayType = C.napi_float32_array
TypedArrayFloat64Array TypedArrayType = C.napi_float64_array
TypedArrayBigInt64Array TypedArrayType = C.napi_bigint64_array
TypedArrayBigUint64Array TypedArrayType = C.napi_biguint64_array
)

41
internal/napi/types.go Normal file
View File

@ -0,0 +1,41 @@
package napi
/*
#include <node/node_api.h>
*/
import "C"
import "unsafe"
type PropertyDescriptor struct {
Utf8name string
Name Value
Method Callback
Getter Callback
Setter Callback
Value Value
Attributes PropertyAttributes
Data unsafe.Pointer
}
type Finalize func(env Env, finalizeData, finalizeHint unsafe.Pointer)
func FinalizeToFinalizer(fn Finalize) Finalizer {
return func(env C.napi_env, finalizeData, finalizeHint unsafe.Pointer) {
fn(Env(env), finalizeData, finalizeHint)
}
}
// Finalizer as a C-compatible function pointer type
type Finalizer func(env C.napi_env, finalizeData, finalizeHint unsafe.Pointer)
type Reference struct {
Ref unsafe.Pointer
}
type EscapableHandleScope struct {
Scope unsafe.Pointer
}
type HandleScope struct {
Scope unsafe.Pointer
}

View File

@ -0,0 +1,48 @@
package napi
/*
#include <node/node_api.h>
*/
import "C"
type ValueType C.napi_valuetype
const (
ValueTypeUndefined ValueType = C.napi_undefined
ValueTypeNull ValueType = C.napi_null
ValueTypeBoolean ValueType = C.napi_boolean
ValueTypeNumber ValueType = C.napi_number
ValueTypeString ValueType = C.napi_string
ValueTypeSymbol ValueType = C.napi_symbol
ValueTypeObject ValueType = C.napi_object
ValueTypeFunction ValueType = C.napi_function
ValueTypeExternal ValueType = C.napi_external
ValueTypeBigint ValueType = C.napi_bigint
)
func (v ValueType) String() string {
switch v {
case ValueTypeUndefined:
return "undefined"
case ValueTypeNull:
return "null"
case ValueTypeBoolean:
return "boolean"
case ValueTypeNumber:
return "number"
case ValueTypeString:
return "string"
case ValueTypeSymbol:
return "symbol"
case ValueTypeObject:
return "object"
case ValueTypeFunction:
return "function"
case ValueTypeExternal:
return "external"
case ValueTypeBigint:
return "bigint"
default:
return "undefined"
}
}

View File

@ -1,32 +0,0 @@
package js
import (
"github.com/akshayganeshen/napi-go"
)
type Callback = func(env Env, this Value, args []Value) any
func AsCallback(fn Callback) napi.Callback {
return func(env napi.Env, info napi.CallbackInfo) napi.Value {
cbInfo, st := napi.GetCbInfo(env, info)
if st != napi.StatusOK {
panic(napi.StatusError(st))
}
jsEnv := AsEnv(env)
this := Value{
Env: jsEnv,
Value: cbInfo.This,
}
args := make([]Value, len(cbInfo.Args))
for i, cbArg := range cbInfo.Args {
args[i] = Value{
Env: jsEnv,
Value: cbArg,
}
}
result := fn(jsEnv, this, args)
return jsEnv.ValueOf(result).Value
}
}

196
js/env.go
View File

@ -1,196 +0,0 @@
package js
import (
"fmt"
"unsafe"
"github.com/akshayganeshen/napi-go"
)
type Env struct {
Env napi.Env
}
type InvalidValueTypeError struct {
Value any
}
var _ error = InvalidValueTypeError{}
func AsEnv(env napi.Env) Env {
return Env{
Env: env,
}
}
func (e Env) Global() Value {
v, st := napi.GetGlobal(e.Env)
if st != napi.StatusOK {
panic(napi.StatusError(st))
}
return Value{
Env: e,
Value: v,
}
}
func (e Env) Null() Value {
v, st := napi.GetNull(e.Env)
if st != napi.StatusOK {
panic(napi.StatusError(st))
}
return Value{
Env: e,
Value: v,
}
}
func (e Env) Undefined() Value {
v, st := napi.GetUndefined(e.Env)
if st != napi.StatusOK {
panic(napi.StatusError(st))
}
return Value{
Env: e,
Value: v,
}
}
func (e Env) ValueOf(x any) Value {
var (
v napi.Value
st napi.Status
)
switch xt := x.(type) {
case Value:
return xt
case []Value:
l := len(xt)
v, st = napi.CreateArrayWithLength(e.Env, l)
if st != napi.StatusOK {
break
}
for i, xti := range xt {
// TODO: Use Value.SetIndex helper
st = napi.SetElement(e.Env, v, i, xti.Value)
if st != napi.StatusOK {
break
}
}
case Func:
return xt.Value
case Callback:
return e.FuncOf(xt).Value
case *Promise:
v, st = xt.Promise.Value, napi.StatusOK
case napi.Value:
v, st = xt, napi.StatusOK
case nil:
v, st = napi.GetNull(e.Env)
case bool:
v, st = napi.GetBoolean(e.Env, xt)
case int:
v, st = napi.CreateDouble(e.Env, float64(xt))
case int8:
v, st = napi.CreateDouble(e.Env, float64(xt))
case int16:
v, st = napi.CreateDouble(e.Env, float64(xt))
case int64:
v, st = napi.CreateDouble(e.Env, float64(xt))
case uint:
v, st = napi.CreateDouble(e.Env, float64(xt))
case uint8:
v, st = napi.CreateDouble(e.Env, float64(xt))
case uint16:
v, st = napi.CreateDouble(e.Env, float64(xt))
case uint64:
v, st = napi.CreateDouble(e.Env, float64(xt))
case uintptr:
v, st = napi.CreateDouble(e.Env, float64(xt))
case unsafe.Pointer:
v, st = napi.CreateDouble(e.Env, float64(uintptr(xt)))
case float32:
v, st = napi.CreateDouble(e.Env, float64(xt))
case float64:
v, st = napi.CreateDouble(e.Env, xt)
case string:
v, st = napi.CreateStringUtf8(e.Env, xt)
case error:
msg := e.ValueOf(xt.Error())
v, st = napi.CreateError(e.Env, nil, msg.Value)
case []any:
l := len(xt)
v, st = napi.CreateArrayWithLength(e.Env, l)
if st != napi.StatusOK {
break
}
for i, xti := range xt {
// TODO: Use Value.SetIndex helper
vti := e.ValueOf(xti)
st = napi.SetElement(e.Env, v, i, vti.Value)
if st != napi.StatusOK {
break
}
}
case map[string]any:
v, st = napi.CreateObject(e.Env)
if st != napi.StatusOK {
break
}
for xtk, xtv := range xt {
// TODO: Use Value.Set helper
vtk, vtv := e.ValueOf(xtk), e.ValueOf(xtv)
st = napi.SetProperty(e.Env, v, vtk.Value, vtv.Value)
if st != napi.StatusOK {
break
}
}
default:
panic(InvalidValueTypeError{x})
}
if st != napi.StatusOK {
panic(napi.StatusError(st))
}
return Value{
Env: e,
Value: v,
}
}
func (e Env) FuncOf(fn Callback) Func {
// TODO: Add CreateReference to FuncOf to keep value alive
v, st := napi.CreateFunction(
e.Env,
"",
AsCallback(fn),
)
if st != napi.StatusOK {
panic(napi.StatusError(st))
}
return Func{
Value: Value{
Env: e,
Value: v,
},
}
}
func (e Env) NewPromise() *Promise {
var result Promise
result.reset(e)
return &result
}
func (err InvalidValueTypeError) Error() string {
return fmt.Sprintf("Value cannot be represented in JS: %T", err.Value)
}

View File

@ -1,5 +0,0 @@
package js
type Func struct {
Value
}

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
}

View File

@ -1,118 +0,0 @@
package js
import (
"errors"
"github.com/akshayganeshen/napi-go"
)
type Promise struct {
Promise napi.Promise
ThreadsafeFunction napi.ThreadsafeFunction
Result any
ResultType PromiseResultType
}
type PromiseResultType string
type PromiseProvider interface {
Resolve(resolution any)
Reject(rejection any)
}
var _ PromiseProvider = &Promise{}
const (
PromiseResultTypeResolved PromiseResultType = "resolved"
PromiseResultTypeRejected PromiseResultType = "rejected"
)
var ErrPromiseSettled = errors.New(
"Promise: Cannot resolve/reject a settled promise",
)
func (p *Promise) Resolve(resolution any) {
p.ensurePending()
p.Result = resolution
p.ResultType = PromiseResultTypeResolved
// function has already been acquired during reset
defer p.release()
p.settle()
}
func (p *Promise) Reject(rejection any) {
p.ensurePending()
p.Result = rejection
p.ResultType = PromiseResultTypeRejected
// function has already been acquired during reset
defer p.release()
p.settle()
}
func (p *Promise) reset(e Env) {
np, st := napi.CreatePromise(e.Env)
if st != napi.StatusOK {
panic(napi.StatusError(st))
}
asyncResourceName := e.ValueOf("napi-go/js-promise")
fn := e.FuncOf(func(env Env, this Value, args []Value) any {
value := env.ValueOf(p.Result)
st := napi.StatusOK
switch p.ResultType {
case PromiseResultTypeResolved:
st = napi.ResolveDeferred(env.Env, p.Promise.Deferred, value.Value)
case PromiseResultTypeRejected:
st = napi.RejectDeferred(env.Env, p.Promise.Deferred, value.Value)
}
if st != napi.StatusOK {
panic(napi.StatusError(st))
}
return nil
})
tsFn, st := napi.CreateThreadsafeFunction(
e.Env,
fn.Value.Value,
nil, asyncResourceName.Value,
0,
1, // initialize with 1 acquisition
)
if st != napi.StatusOK {
panic(napi.StatusError(st))
}
*p = Promise{
Promise: np,
ThreadsafeFunction: tsFn,
}
}
func (p *Promise) ensurePending() {
if p.ResultType != "" {
panic(ErrPromiseSettled)
}
}
func (p *Promise) settle() {
st := napi.CallThreadsafeFunction(p.ThreadsafeFunction)
if st != napi.StatusOK {
panic(napi.StatusError(st))
}
}
func (p *Promise) release() {
st := napi.ReleaseThreadsafeFunction(p.ThreadsafeFunction)
if st == napi.StatusClosing {
p.ThreadsafeFunction = nil
} else if st != napi.StatusOK {
panic(napi.StatusError(st))
}
}

View File

@ -1,14 +0,0 @@
package js
import (
"github.com/akshayganeshen/napi-go"
)
type Value struct {
Env Env
Value napi.Value
}
func (v Value) GetEnv() Env {
return v.Env
}

View File

@ -1,341 +0,0 @@
package napi
/*
#include <stdlib.h>
#include <node/node_api.h>
*/
import "C"
import (
"unsafe"
)
func GetUndefined(env Env) (Value, Status) {
var result Value
status := Status(C.napi_get_undefined(
C.napi_env(env),
(*C.napi_value)(unsafe.Pointer(&result)),
))
return result, status
}
func GetNull(env Env) (Value, Status) {
var result Value
status := Status(C.napi_get_null(
C.napi_env(env),
(*C.napi_value)(unsafe.Pointer(&result)),
))
return result, status
}
func GetGlobal(env Env) (Value, Status) {
var result Value
status := Status(C.napi_get_global(
C.napi_env(env),
(*C.napi_value)(unsafe.Pointer(&result)),
))
return result, status
}
func GetBoolean(env Env, value bool) (Value, Status) {
var result Value
status := Status(C.napi_get_boolean(
C.napi_env(env),
C.bool(value),
(*C.napi_value)(unsafe.Pointer(&result)),
))
return result, status
}
func CreateObject(env Env) (Value, Status) {
var result Value
status := Status(C.napi_create_object(
C.napi_env(env),
(*C.napi_value)(unsafe.Pointer(&result)),
))
return result, status
}
func CreateArray(env Env) (Value, Status) {
var result Value
status := Status(C.napi_create_array(
C.napi_env(env),
(*C.napi_value)(unsafe.Pointer(&result)),
))
return result, status
}
func CreateArrayWithLength(env Env, length int) (Value, Status) {
var result Value
status := Status(C.napi_create_array_with_length(
C.napi_env(env),
C.size_t(length),
(*C.napi_value)(unsafe.Pointer(&result)),
))
return result, status
}
func CreateDouble(env Env, value float64) (Value, Status) {
var result Value
status := Status(C.napi_create_double(
C.napi_env(env),
C.double(value),
(*C.napi_value)(unsafe.Pointer(&result)),
))
return result, status
}
func CreateStringUtf8(env Env, str string) (Value, Status) {
cstr := C.CString(str)
defer C.free(unsafe.Pointer(cstr))
var result Value
status := Status(C.napi_create_string_utf8(
C.napi_env(env),
cstr,
C.size_t(len([]byte(str))), // must pass number of bytes
(*C.napi_value)(unsafe.Pointer(&result)),
))
return result, status
}
func CreateSymbol(env Env, description Value) (Value, Status) {
var result Value
status := Status(C.napi_create_symbol(
C.napi_env(env),
C.napi_value(description),
(*C.napi_value)(unsafe.Pointer(&result)),
))
return result, status
}
func CreateFunction(env Env, name string, cb Callback) (Value, Status) {
provider, status := getInstanceData(env)
if status != StatusOK || provider == nil {
return nil, status
}
return provider.GetCallbackData().CreateCallback(env, name, cb)
}
func CreateError(env Env, code, msg Value) (Value, Status) {
var result Value
status := Status(C.napi_create_error(
C.napi_env(env),
C.napi_value(code),
C.napi_value(msg),
(*C.napi_value)(unsafe.Pointer(&result)),
))
return result, status
}
func Typeof(env Env, value Value) (ValueType, Status) {
var result ValueType
status := Status(C.napi_typeof(
C.napi_env(env),
C.napi_value(value),
(*C.napi_valuetype)(unsafe.Pointer(&result)),
))
return result, status
}
func GetValueDouble(env Env, value Value) (float64, Status) {
var result float64
status := Status(C.napi_get_value_double(
C.napi_env(env),
C.napi_value(value),
(*C.double)(unsafe.Pointer(&result)),
))
return result, status
}
func GetValueBool(env Env, value Value) (bool, Status) {
var result bool
status := Status(C.napi_get_value_bool(
C.napi_env(env),
C.napi_value(value),
(*C.bool)(unsafe.Pointer(&result)),
))
return result, status
}
func GetValueStringUtf8(env Env, value Value) (string, Status) {
// call napi_get_value_string_utf8 twice
// first is to get number of bytes
// second is to populate the actual string buffer
bufsize := C.size_t(0)
var strsize C.size_t
status := Status(C.napi_get_value_string_utf8(
C.napi_env(env),
C.napi_value(value),
nil,
bufsize,
&strsize,
))
if status != StatusOK {
return "", status
}
// ensure there is room for the null terminator as well
strsize++
cstr := (*C.char)(C.malloc(C.sizeof_char * strsize))
defer C.free(unsafe.Pointer(cstr))
status = Status(C.napi_get_value_string_utf8(
C.napi_env(env),
C.napi_value(value),
cstr,
strsize,
&strsize,
))
if status != StatusOK {
return "", status
}
return C.GoStringN(
(*C.char)(cstr),
(C.int)(strsize),
), status
}
func SetProperty(env Env, object, key, value Value) Status {
return Status(C.napi_set_property(
C.napi_env(env),
C.napi_value(object),
C.napi_value(key),
C.napi_value(value),
))
}
func SetElement(env Env, object Value, index int, value Value) Status {
return Status(C.napi_set_element(
C.napi_env(env),
C.napi_value(object),
C.uint32_t(index),
C.napi_value(value),
))
}
func StrictEquals(env Env, lhs, rhs Value) (bool, Status) {
var result bool
status := Status(C.napi_strict_equals(
C.napi_env(env),
C.napi_value(lhs),
C.napi_value(rhs),
(*C.bool)(&result),
))
return result, status
}
type GetCbInfoResult struct {
Args []Value
This Value
}
func GetCbInfo(env Env, info CallbackInfo) (GetCbInfoResult, Status) {
// call napi_get_cb_info twice
// first is to get total number of arguments
// second is to populate the actual arguments
argc := C.size_t(0)
status := Status(C.napi_get_cb_info(
C.napi_env(env),
C.napi_callback_info(info),
&argc,
nil,
nil,
nil,
))
if status != StatusOK {
return GetCbInfoResult{}, status
}
argv := make([]Value, int(argc))
var cArgv unsafe.Pointer
if argc > 0 {
cArgv = unsafe.Pointer(&argv[0]) // must pass element pointer
}
var thisArg Value
status = Status(C.napi_get_cb_info(
C.napi_env(env),
C.napi_callback_info(info),
&argc,
(*C.napi_value)(cArgv),
(*C.napi_value)(unsafe.Pointer(&thisArg)),
nil,
))
return GetCbInfoResult{
Args: argv,
This: thisArg,
}, status
}
func Throw(env Env, err Value) Status {
return Status(C.napi_throw(
C.napi_env(env),
C.napi_value(err),
))
}
func ThrowError(env Env, code, msg string) Status {
codeCStr, msgCCstr := C.CString(code), C.CString(msg)
defer C.free(unsafe.Pointer(codeCStr))
defer C.free(unsafe.Pointer(msgCCstr))
return Status(C.napi_throw_error(
C.napi_env(env),
codeCStr,
msgCCstr,
))
}
func CreatePromise(env Env) (Promise, Status) {
var result Promise
status := Status(C.napi_create_promise(
C.napi_env(env),
(*C.napi_deferred)(unsafe.Pointer(&result.Deferred)),
(*C.napi_value)(unsafe.Pointer(&result.Value)),
))
return result, status
}
func ResolveDeferred(env Env, deferred Deferred, resolution Value) Status {
return Status(C.napi_resolve_deferred(
C.napi_env(env),
C.napi_deferred(deferred),
C.napi_value(resolution),
))
}
func RejectDeferred(env Env, deferred Deferred, rejection Value) Status {
return Status(C.napi_reject_deferred(
C.napi_env(env),
C.napi_deferred(deferred),
C.napi_value(rejection),
))
}
func SetInstanceData(env Env, data any) Status {
provider, status := getInstanceData(env)
if status != StatusOK || provider == nil {
return status
}
provider.SetUserData(data)
return status
}
func GetInstanceData(env Env) (any, Status) {
provider, status := getInstanceData(env)
if status != StatusOK || provider == nil {
return nil, status
}
return provider.GetUserData(), status
}

View File

@ -1,40 +0,0 @@
all: doc
clean: clean-doc
EXAMPLE_DIR = docs/examples
EXAMPLE_PACKAGES = \
async-promise \
callback \
describe-args \
hello-world \
js
NAPI_LIB_SUFFIX = .node
TARGET_BUILDDIR = build
EXAMPLE_BINDINGS = $(addsuffix $(NAPI_LIB_SUFFIX),$(EXAMPLE_PACKAGES))
TARGET_EXAMPLES = \
$(addprefix $(TARGET_BUILDDIR)/, $(EXAMPLE_BINDINGS))
# TODO: Configure CGO_LDFLAGS_ALLOW for non-darwin systems.
CGO_LDFLAGS_ALLOW = (-Wl,(-undefined,dynamic_lookup|-no_pie|-search_paths_first))
doc: $(TARGET_EXAMPLES)
$(TARGET_EXAMPLES): | $(TARGET_BUILDDIR)
$(TARGET_EXAMPLES): $(TARGET_BUILDDIR)/%$(NAPI_LIB_SUFFIX): $(EXAMPLE_DIR)/%
CGO_LDFLAGS_ALLOW='$(CGO_LDFLAGS_ALLOW)' \
go build -buildmode=c-shared -o "$(@)" "./$(<)/"
$(TARGET_BUILDDIR):
mkdir -p "$(TARGET_BUILDDIR)"
clean:
rmdir "$(TARGET_BUILDDIR)"
clean-doc:
rm -f $(patsubst %,"%",$(TARGET_EXAMPLES))
.PHONY: all doc
.PHONY: clean clean-doc

50
napi_env.go Normal file
View File

@ -0,0 +1,50 @@
package napi
import "sirherobrine23.com.br/Sirherobrine23/napi-go/internal/napi"
type EnvType interface {
NapiValue() napi.Env // Primitive value to NAPI call
Global() (*Object, error)
Undefined() (ValueType, error)
Null() (ValueType, error)
}
// Return N-API env reference
func N_APIEnv(env napi.Env) EnvType { return &Env{env} }
// N-API Env
type Env struct{
NapiEnv napi.Env
}
// Return [napi.Env] to point from internal napi cgo
func (e *Env) NapiValue() napi.Env {
return e.NapiEnv
}
// Return representantion to 'This' [*Object]
func (e *Env) Global() (*Object, error) {
napiValue, err := mustValueErr(napi.GetGlobal(e.NapiEnv))
if err != nil {
return nil, err
}
return ToObject(N_APIValue(e, napiValue)), nil
}
// Return Undefined value
func (e *Env) Undefined() (ValueType, error) {
napiValue, err := mustValueErr(napi.GetUndefined(e.NapiEnv))
if err != nil {
return nil, err
}
return N_APIValue(e, napiValue), nil
}
// Return Null value
func (e *Env) Null() (ValueType, error) {
napiValue, err := mustValueErr(napi.GetNull(e.NapiEnv))
if err != nil {
return nil, err
}
return N_APIValue(e, napiValue), nil
}

35
napi_status.go Normal file
View File

@ -0,0 +1,35 @@
package napi
import "sirherobrine23.com.br/Sirherobrine23/napi-go/internal/napi"
// Process status to return error if StatusOK return nil on error
func mustValueErr[T any](input T, status napi.Status) (T, error) {
if status != napi.StatusOK {
return input, napi.StatusError(status)
}
return input, nil
}
// return error from status
func singleMustValueErr(status napi.Status) error {
if status != napi.StatusOK {
return napi.StatusError(status)
}
return nil
}
// Process status to return error if StatusOK return nil on error
func mustValueErr2[T any](input T, _ bool, status napi.Status) (T, error) {
if status != napi.StatusOK {
return input, napi.StatusError(status)
}
return input, nil
}
// Process status to return error if StatusOK return nil on error
func mustValueErr3[T, C any](input T, i2 C, status napi.Status) (T, C, error) {
if status != napi.StatusOK {
return input, i2, napi.StatusError(status)
}
return input, i2, nil
}

167
napi_value.go Normal file
View File

@ -0,0 +1,167 @@
package napi
import (
"fmt"
"sirherobrine23.com.br/Sirherobrine23/napi-go/internal/napi"
)
type ValueType interface {
NapiValue() napi.Value // Primitive value to NAPI call
NapiEnv() napi.Env // NAPI Env to NAPI call
Env() EnvType // NAPI Env to NAPI call
Type() (NapiType, error) // NAPI Type of value
}
type (
value = ValueType // to dont expose to external structs
// Generic type to NAPI value
Value struct {
env EnvType
valueOf napi.Value
}
NapiType int // Return typeof of Value
)
const (
TypeUnkown NapiType = iota
TypeUndefined
TypeNull
TypeBoolean
TypeNumber
TypeBigInt
TypeString
TypeSymbol
TypeObject
TypeFunction
TypeExternal
TypeTypedArray
TypePromise
TypeDataView
TypeBuffer
TypeDate
TypeArray
TypeArrayBuffer
TypeError
)
var napiTypeNames = map[NapiType]string{
TypeUnkown: "Unknown",
TypeUndefined: "Undefined",
TypeNull: "Null",
TypeBoolean: "Boolean",
TypeNumber: "Number",
TypeBigInt: "BigInt",
TypeString: "String",
TypeSymbol: "Symbol",
TypeObject: "Object",
TypeFunction: "Function",
TypeExternal: "External",
TypeTypedArray: "TypedArray",
TypePromise: "Promise",
TypeDataView: "DaraView",
TypeBuffer: "Buffer",
TypeDate: "Date",
TypeArray: "Array",
TypeArrayBuffer: "ArrayBuffer",
TypeError: "Error",
}
// Return [ValueType] from [napi.Value]
func N_APIValue(env EnvType, value napi.Value) ValueType {
return &Value{env: env, valueOf: value}
}
func (v *Value) NapiValue() napi.Value { return v.valueOf }
func (v *Value) NapiEnv() napi.Env { return v.env.NapiValue() }
func (v *Value) Env() EnvType { return v.env }
func (v *Value) Type() (NapiType, error) {
isTypedArray, err := mustValueErr(napi.IsTypedArray(v.NapiEnv(), v.NapiValue()))
if err != nil {
return TypeUnkown, err
}
isPromise, err := mustValueErr(napi.IsPromise(v.NapiEnv(), v.NapiValue()))
if err != nil {
return TypeUnkown, err
}
isDataView, err := mustValueErr(napi.IsDataView(v.NapiEnv(), v.NapiValue()))
if err != nil {
return TypeUnkown, err
}
isBuffer, err := mustValueErr(napi.IsBuffer(v.NapiEnv(), v.NapiValue()))
if err != nil {
return TypeUnkown, err
}
isDate, err := mustValueErr(napi.IsDate(v.NapiEnv(), v.NapiValue()))
if err != nil {
return TypeUnkown, err
}
isArray, err := mustValueErr(napi.IsArray(v.NapiEnv(), v.NapiValue()))
if err != nil {
return TypeUnkown, err
}
isArrayBuffer, err := mustValueErr(napi.IsArrayBuffer(v.NapiEnv(), v.NapiValue()))
if err != nil {
return TypeUnkown, err
}
isError, err := mustValueErr(napi.IsError(v.NapiEnv(), v.NapiValue()))
if err != nil {
return TypeUnkown, err
}
isTypeof, err := mustValueErr(napi.Typeof(v.NapiEnv(), v.NapiValue()))
if err != nil {
return TypeUnkown, err
}
switch {
case isTypedArray:
return TypeTypedArray, nil
case isPromise:
return TypePromise, nil
case isDataView:
return TypeDataView, nil
case isBuffer:
return TypeBuffer, nil
case isDate:
return TypeDate, nil
case isArray:
return TypeArray, nil
case isArrayBuffer:
return TypeArrayBuffer, nil
case isError:
return TypeError, nil
case isTypeof == napi.ValueTypeUndefined:
return TypeUndefined, nil
case isTypeof == napi.ValueTypeNull:
return TypeNull, nil
case isTypeof == napi.ValueTypeBoolean:
return TypeBoolean, nil
case isTypeof == napi.ValueTypeNumber:
return TypeNumber, nil
case isTypeof == napi.ValueTypeString:
return TypeString, nil
case isTypeof == napi.ValueTypeSymbol:
return TypeSymbol, nil
case isTypeof == napi.ValueTypeObject:
return TypeObject, nil
case isTypeof == napi.ValueTypeFunction:
return TypeFunction, nil
case isTypeof == napi.ValueTypeExternal:
return TypeExternal, nil
case isTypeof == napi.ValueTypeBigint:
return TypeBigInt, nil
}
return TypeUnkown, nil
}
func (t NapiType) String() string {
if name, ok := napiTypeNames[t]; ok {
return name
}
return fmt.Sprintf("Unknown NapiType %d", t)
}

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
}

134
object.go Normal file
View File

@ -0,0 +1,134 @@
package napi
import (
"iter"
"sirherobrine23.com.br/Sirherobrine23/napi-go/internal/napi"
)
type Object struct{ value }
// Convert ValueType to [*Object]
func ToObject(o ValueType) *Object { return &Object{o} }
// Create [*Object]
func CreateObject(env EnvType) (*Object, error) {
napiValue, err := mustValueErr(napi.CreateObject(env.NapiValue()))
if err != nil {
return nil, err
}
return ToObject(N_APIValue(env, napiValue)), nil
}
// Check if exists named property.
func (obj *Object) Has(name string) (bool, error) {
return mustValueErr(napi.HasNamedProperty(obj.NapiEnv(), obj.NapiValue(), name))
}
// Checks whether a own property is present.
func (obj *Object) HasOwnProperty(key ValueType) (bool, error) {
return mustValueErr(napi.HasOwnProperty(obj.NapiEnv(), obj.NapiValue(), key.NapiValue()))
}
// Checks whether a own property is present.
func (obj *Object) HasOwnPropertyString(keyString string) (bool, error) {
napiString, err := CreateString(obj.Env(), keyString)
if err != nil {
return false, err
}
return obj.HasOwnProperty(napiString)
}
// Gets a property.
func (obj *Object) Get(key string) (ValueType, error) {
keyValue, err := CreateString(obj.Env(), key)
if err != nil {
return nil, err
}
return obj.GetWithValue(keyValue)
}
// Gets a property.
func (obj *Object) GetWithValue(key ValueType) (ValueType, error) {
napiValue, err := mustValueErr(napi.GetProperty(obj.Env().NapiValue(), obj.NapiValue(), key.NapiValue()))
if err != nil {
return nil, err
}
return N_APIValue(obj.Env(), napiValue), nil
}
// Sets a property.
func (obj *Object) Set(key string, value ValueType) error {
keyValue, err := CreateString(obj.Env(), key)
if err != nil {
return err
}
return obj.SetWithValue(keyValue, value)
}
// Sets a property.
func (obj *Object) SetWithValue(key, value ValueType) error {
return singleMustValueErr(napi.SetProperty(obj.NapiEnv(), obj.NapiValue(), key.NapiValue(), value.NapiValue()))
}
// Delete property.
func (obj *Object) Delete(key string) (bool, error) {
keyValue, err := CreateString(obj.Env(), key)
if err != nil {
return false, err
}
return obj.DeleteWithValue(keyValue)
}
// Delete property.
func (obj *Object) DeleteWithValue(key ValueType) (bool, error) {
return mustValueErr(napi.DeleteProperty(obj.NapiEnv(), obj.NapiValue(), key.NapiValue()))
}
// Get all property names.
func (obj *Object) GetPropertyNames() (*Array, error) {
arrValue, err := mustValueErr(napi.GetPropertyNames(obj.NapiEnv(), obj.NapiValue()))
if err != nil {
return nil, err
}
return ToArray(N_APIValue(obj.Env(), arrValue)), nil
}
// Checks if an object is an instance created by a constructor function,
// this is equivalent to the JavaScript `instanceof` operator.
func (obj *Object) InstanceOf(value ValueType) (bool, error) {
return mustValueErr(napi.InstanceOf(obj.NapiEnv(), obj.NapiValue(), value.NapiValue()))
}
// Freeze object.
func (obj *Object) Freeze() error {
return singleMustValueErr(napi.ObjectFreeze(obj.NapiEnv(), obj.NapiValue()))
}
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
}
}
}
}

44
string.go Normal file
View File

@ -0,0 +1,44 @@
package napi
import (
"unicode/utf16"
"sirherobrine23.com.br/Sirherobrine23/napi-go/internal/napi"
)
type String struct{ value }
// Convert [ValueType] to [*String]
func ToString(o ValueType) *String { return &String{o} }
// Create [*String] from go string
func CreateString(env EnvType, str string) (*String, error) {
napiString, err := mustValueErr(napi.CreateStringUtf8(env.NapiValue(), str))
if err != nil {
return nil, err
}
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
}

View File

@ -1,21 +0,0 @@
package napi
/*
#include <node/node_api.h>
*/
import "C"
type ValueType int
const (
ValueTypeUndefined ValueType = C.napi_undefined
ValueTypeNull ValueType = C.napi_null
ValueTypeBoolean ValueType = C.napi_boolean
ValueTypeNumber ValueType = C.napi_number
ValueTypeString ValueType = C.napi_string
ValueTypeSymbol ValueType = C.napi_symbol
ValueTypeObject ValueType = C.napi_object
ValueTypeFunction ValueType = C.napi_function
ValueTypeExternal ValueType = C.napi_external
ValueTypeBigint ValueType = C.napi_bigint
)