WIP: Implements class in napi-go #10
125
class.go
Normal file
125
class.go
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
package napi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"sirherobrine23.com.br/Sirherobrine23/napi-go/internal/napi"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Class[T ClassType] struct {
|
||||||
|
value
|
||||||
|
Class T
|
||||||
|
}
|
||||||
|
|
||||||
|
type PropertyAttributes int
|
||||||
|
|
||||||
|
type PropertyDescriptor struct {
|
||||||
|
Name string
|
||||||
|
Method Callback
|
||||||
|
Getter Callback
|
||||||
|
Setter Callback
|
||||||
|
Value ValueType
|
||||||
|
Attributes PropertyAttributes
|
||||||
|
}
|
||||||
|
|
||||||
|
// Base to class declaration
|
||||||
|
type ClassType interface {
|
||||||
|
Contructor(*CallbackInfo) (*Object, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
var ClassFuncMathod = reflect.TypeFor[Callback]()
|
||||||
|
|
||||||
|
const (
|
||||||
|
PropertyAttributesWritable = PropertyAttributes(napi.Writable)
|
||||||
|
PropertyAttributesEnumerable = PropertyAttributes(napi.Enumerable)
|
||||||
|
PropertyAttributesConfigurable = PropertyAttributes(napi.Configurable)
|
||||||
|
PropertyAttributesStatic = PropertyAttributes(napi.Static)
|
||||||
|
PropertyAttributesDefault = PropertyAttributes(napi.Default)
|
||||||
|
PropertyAttributesDefaultMethod = PropertyAttributes(napi.DefaultMethod)
|
||||||
|
PropertyAttributesDefaultJSProperty = PropertyAttributes(napi.DefaultJSProperty)
|
||||||
|
)
|
||||||
|
|
||||||
|
func CreateClass[T ClassType](env EnvType) (*Class[T], error) {
|
||||||
|
ptrType := reflect.TypeFor[T]()
|
||||||
|
switch ptrType.Kind() {
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("type %s is not a struct", ptrType.Name())
|
||||||
|
case reflect.Pointer:
|
||||||
|
elem := ptrType.Elem()
|
||||||
|
if elem.Kind() == reflect.Struct {
|
||||||
|
return classMount[T](env, elem)
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
case reflect.Struct:
|
||||||
|
return classMount[T](env, ptrType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func classMount[T ClassType](env EnvType, ptr reflect.Type) (*Class[T], error) {
|
||||||
|
valueOf := reflect.New(ptr)
|
||||||
|
|
||||||
|
var propertys []*PropertyDescriptor
|
||||||
|
for methodIndex := range ptr.NumMethod() {
|
||||||
|
method := ptr.Method(methodIndex)
|
||||||
|
if method.Type.Kind() != reflect.Func || !method.IsExported() {
|
||||||
|
continue
|
||||||
|
} else if !method.Type.Implements(ClassFuncMathod) {
|
||||||
|
continue
|
||||||
|
} else if method.Name == "Contructor" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
propertys = append(propertys, &PropertyDescriptor{
|
||||||
|
Name: method.Name,
|
||||||
|
Attributes: PropertyAttributes(PropertyAttributesStatic),
|
||||||
|
Method: valueOf.Method(methodIndex).Interface().(Callback),
|
||||||
|
Getter: nil,
|
||||||
|
Setter: nil,
|
||||||
|
Value: nil,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var napiAtr []napi.PropertyDescriptor
|
||||||
|
for _, value := range propertys {
|
||||||
|
name, err := CreateString(env, value.Name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
method, err := CreateFunction(env, value.Name, value.Method)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
napiAtr = append(napiAtr, napi.PropertyDescriptor{
|
||||||
|
Utf8name: value.Name,
|
||||||
|
Name: name.NapiValue(),
|
||||||
|
Method: method.NapiCallback(),
|
||||||
|
Attributes: napi.PropertyAttributes(value.Attributes),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
jsConstruct, err := CreateFunction(env, "constructor", func(ci *CallbackInfo) (ValueType, error) {
|
||||||
|
res := valueOf.MethodByName("Contructor").Call([]reflect.Value{reflect.ValueOf(ci)})
|
||||||
|
obj, err := res[0].Interface().(*Object), res[1].Interface().(error)
|
||||||
|
return N_APIValue(obj.Env(), obj.NapiValue()), err
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
jsValue, status := napi.DefineClass(env.NapiValue(), ptr.Name(),
|
||||||
|
jsConstruct.fn,
|
||||||
|
napiAtr,
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := status.ToError(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Class[T]{
|
||||||
|
value: N_APIValue(env, jsValue),
|
||||||
|
Class: valueOf.Interface().(T),
|
||||||
|
}, nil
|
||||||
|
}
|
47
function.go
47
function.go
@ -6,19 +6,44 @@ import (
|
|||||||
"sirherobrine23.com.br/Sirherobrine23/napi-go/internal/napi"
|
"sirherobrine23.com.br/Sirherobrine23/napi-go/internal/napi"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Function struct{ value }
|
type Function struct {
|
||||||
|
value
|
||||||
|
fn napi.Callback
|
||||||
|
}
|
||||||
|
|
||||||
// Function to call on Javascript caller
|
// Function to call on Javascript caller
|
||||||
type Callback func(env EnvType, this ValueType, args []ValueType) (ValueType, error)
|
type Callback func(*CallbackInfo) (ValueType, error)
|
||||||
|
|
||||||
|
// Values from [napi.CallbackInfo]
|
||||||
|
type CallbackInfo struct {
|
||||||
|
Env EnvType
|
||||||
|
This ValueType
|
||||||
|
Args []ValueType
|
||||||
|
|
||||||
|
info napi.CallbackInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (call *CallbackInfo) NewTarget() (ValueType, error) {
|
||||||
|
v, status := napi.GetNewTarget(call.Env.NapiValue(), call.info)
|
||||||
|
if err := status.ToError(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return N_APIValue(call.Env, v), nil
|
||||||
|
}
|
||||||
|
|
||||||
// Convert [ValueType] to [*Function]
|
// Convert [ValueType] to [*Function]
|
||||||
func ToFunction(o ValueType) *Function { return &Function{o} }
|
func ToFunction(o ValueType) *Function { return &Function{o, nil} }
|
||||||
|
|
||||||
|
// CreateFunction creates a new JavaScript function in the given N-API environment with the specified name and callback.
|
||||||
|
// The callback is invoked when the JavaScript function is called, receiving a CallbackInfo containing the environment,
|
||||||
|
// 'this' value, arguments, and callback info. Any errors returned by the callback or panics are converted to JavaScript
|
||||||
|
// exceptions. If the callback returns nil, the JavaScript 'undefined' value is returned. If the callback returns a value
|
||||||
|
// of TypeError, it is thrown as a JavaScript exception.
|
||||||
func CreateFunction(env EnvType, name string, callback Callback) (*Function, error) {
|
func CreateFunction(env EnvType, name string, callback Callback) (*Function, error) {
|
||||||
return CreateFunctionNapi(env, name, func(napiEnv napi.Env, info napi.CallbackInfo) napi.Value {
|
return CreateFunctionNapi(env, name, func(napiEnv napi.Env, info napi.CallbackInfo) napi.Value {
|
||||||
env := N_APIEnv(napiEnv)
|
env := N_APIEnv(napiEnv)
|
||||||
cbInfo, err := napi.GetCbInfo(napiEnv, info)
|
cbInfo, status := napi.GetCbInfo(napiEnv, info)
|
||||||
if err := err.ToError(); err != nil {
|
if err := status.ToError(); err != nil {
|
||||||
ThrowError(env, "", err.Error())
|
ThrowError(env, "", err.Error())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -40,8 +65,7 @@ func CreateFunction(env EnvType, name string, callback Callback) (*Function, err
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
{
|
res, err := callback(&CallbackInfo{env, this, args, info})
|
||||||
res, err := callback(env, this, args)
|
|
||||||
switch {
|
switch {
|
||||||
case err != nil:
|
case err != nil:
|
||||||
ThrowError(env, "", err.Error())
|
ThrowError(env, "", err.Error())
|
||||||
@ -57,7 +81,6 @@ func CreateFunction(env EnvType, name string, callback Callback) (*Function, err
|
|||||||
}
|
}
|
||||||
return res.NapiValue()
|
return res.NapiValue()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,7 +90,13 @@ func CreateFunctionNapi(env EnvType, name string, callback napi.Callback) (*Func
|
|||||||
if err := err.ToError(); err != nil {
|
if err := err.ToError(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return ToFunction(N_APIValue(env, fnCall)), nil
|
fn := ToFunction(N_APIValue(env, fnCall))
|
||||||
|
fn.fn = callback
|
||||||
|
return fn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fn *Function) NapiCallback() napi.Callback {
|
||||||
|
return fn.fn
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fn *Function) internalCall(this napi.Value, argc int, argv []napi.Value) (ValueType, error) {
|
func (fn *Function) internalCall(this napi.Value, argc int, argv []napi.Value) (ValueType, error) {
|
||||||
|
@ -14,7 +14,7 @@ var waitTime = time.Second * 3
|
|||||||
|
|
||||||
//go:linkname Register sirherobrine23.com.br/Sirherobrine23/napi-go/module.Register
|
//go:linkname Register sirherobrine23.com.br/Sirherobrine23/napi-go/module.Register
|
||||||
func Register(env napi.EnvType, export *napi.Object) {
|
func Register(env napi.EnvType, export *napi.Object) {
|
||||||
fn, _ := napi.CreateFunction(env, "", func(env napi.EnvType, _ napi.ValueType, args []napi.ValueType) (napi.ValueType, error) {
|
fn, _ := napi.CreateFunction(env, "", func(ci *napi.CallbackInfo) (napi.ValueType, error) {
|
||||||
var Test *napi.String
|
var Test *napi.String
|
||||||
return napi.CreateAsyncWorker(env,
|
return napi.CreateAsyncWorker(env,
|
||||||
func(env napi.EnvType) {
|
func(env napi.EnvType) {
|
||||||
|
29
internal/examples/class/main.go
Normal file
29
internal/examples/class/main.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "unsafe"
|
||||||
|
|
||||||
|
_ "sirherobrine23.com.br/Sirherobrine23/napi-go/module"
|
||||||
|
|
||||||
|
"sirherobrine23.com.br/Sirherobrine23/napi-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {}
|
||||||
|
|
||||||
|
//go:linkname Register sirherobrine23.com.br/Sirherobrine23/napi-go/module.Register
|
||||||
|
func Register(env napi.EnvType, export *napi.Object) {
|
||||||
|
class, err := napi.CreateClass[*ClassTest](env)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
export.Set("class", class)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClassTest struct{}
|
||||||
|
|
||||||
|
func (class *ClassTest) Contructor(ci *napi.CallbackInfo) (*napi.Object, error) {
|
||||||
|
obj, _ := napi.CreateObject(ci.Env)
|
||||||
|
|
||||||
|
return obj, nil
|
||||||
|
}
|
@ -12,12 +12,12 @@ import (
|
|||||||
|
|
||||||
//go:linkname Register sirherobrine23.com.br/Sirherobrine23/napi-go/module.Register
|
//go:linkname Register sirherobrine23.com.br/Sirherobrine23/napi-go/module.Register
|
||||||
func Register(env napi.EnvType, export *napi.Object) {
|
func Register(env napi.EnvType, export *napi.Object) {
|
||||||
jsFunc := napi.Callback(func(env napi.EnvType, this napi.ValueType, args []napi.ValueType) (napi.ValueType, error) {
|
jsFunc := napi.Callback(func(ci *napi.CallbackInfo) (napi.ValueType, error) {
|
||||||
var waitTime = time.Second * 3
|
var waitTime = time.Second * 3
|
||||||
|
|
||||||
if len(args) == 1 {
|
if len(ci.Args) == 1 {
|
||||||
if typof, _ := args[0].Type(); typof == napi.TypeNumber {
|
if typof, _ := ci.Args[0].Type(); typof == napi.TypeNumber {
|
||||||
wait := napi.As[*napi.Number](args[0])
|
wait := napi.As[*napi.Number](ci.Args[0])
|
||||||
_waitTime, _ := wait.Int()
|
_waitTime, _ := wait.Int()
|
||||||
waitTime = time.Duration(_waitTime)
|
waitTime = time.Duration(_waitTime)
|
||||||
}
|
}
|
||||||
|
27
internal/napi/class.go
Normal file
27
internal/napi/class.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package napi
|
||||||
|
|
||||||
|
// #include <node/node_api.h>
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
func DefineClass(env Env, name string, constructor Callback, Property []PropertyDescriptor) (Value, Status) {
|
||||||
|
var pro *C.napi_property_descriptor
|
||||||
|
if len(Property) > 0 {
|
||||||
|
pro = (*C.napi_property_descriptor)(unsafe.Pointer(&Property[0]))
|
||||||
|
}
|
||||||
|
|
||||||
|
var result Value
|
||||||
|
call := &constructor
|
||||||
|
status := Status(C.napi_define_class(
|
||||||
|
C.napi_env(env),
|
||||||
|
C.CString(name),
|
||||||
|
C.size_t(len(name)),
|
||||||
|
C.napi_callback(unsafe.Pointer(call)),
|
||||||
|
nil,
|
||||||
|
C.size_t(len(Property)),
|
||||||
|
pro,
|
||||||
|
(*C.napi_value)(unsafe.Pointer(&result)),
|
||||||
|
))
|
||||||
|
return result, status
|
||||||
|
}
|
@ -37,7 +37,7 @@ func funcOf(env EnvType, ptr reflect.Value) (ValueType, error) {
|
|||||||
case internalNapi.Callback: // return internal/napi function value
|
case internalNapi.Callback: // return internal/napi function value
|
||||||
return CreateFunctionNapi(env, funcName, v)
|
return CreateFunctionNapi(env, funcName, v)
|
||||||
default: // Convert go function to javascript function
|
default: // Convert go function to javascript function
|
||||||
return CreateFunction(env, funcName, func(env EnvType, this ValueType, args []ValueType) (ValueType, error) {
|
return CreateFunction(env, funcName, func(ci *CallbackInfo) (ValueType, error) {
|
||||||
var goFnReturn []reflect.Value
|
var goFnReturn []reflect.Value
|
||||||
in, out, veridict := ptr.Type().NumIn(), ptr.Type().NumOut(), ptr.Type().IsVariadic()
|
in, out, veridict := ptr.Type().NumIn(), ptr.Type().NumOut(), ptr.Type().IsVariadic()
|
||||||
|
|
||||||
@ -46,9 +46,9 @@ func funcOf(env EnvType, ptr reflect.Value) (ValueType, error) {
|
|||||||
case in == 0 && out == 0: // only call
|
case in == 0 && out == 0: // only call
|
||||||
goFnReturn = ptr.Call([]reflect.Value{})
|
goFnReturn = ptr.Call([]reflect.Value{})
|
||||||
case !veridict: // call same args
|
case !veridict: // call same args
|
||||||
goFnReturn = ptr.Call(goValuesInFunc(ptr, args, false))
|
goFnReturn = ptr.Call(goValuesInFunc(ptr, ci.Args, false))
|
||||||
default: // call with slice on end
|
default: // call with slice on end
|
||||||
goFnReturn = ptr.CallSlice(goValuesInFunc(ptr, args, true))
|
goFnReturn = ptr.CallSlice(goValuesInFunc(ptr, ci.Args, true))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for last element is error
|
// Check for last element is error
|
||||||
|
Reference in New Issue
Block a user