WIP: Implements class in napi-go #10

Draft
Sirherobrine23 wants to merge 3 commits from class into main
2 changed files with 105 additions and 59 deletions
Showing only changes of commit b1d40d3f4f - Show all commits

150
class.go
View File

@@ -3,77 +3,123 @@ package napi
import ( import (
"fmt" "fmt"
"reflect" "reflect"
"unsafe"
"sirherobrine23.com.br/Sirherobrine23/napi-go/internal/napi" "sirherobrine23.com.br/Sirherobrine23/napi-go/internal/napi"
) )
type ( type Class[T ClassType] struct {
PropertyDescriptor = napi.PropertyDescriptor value
Class T
}
ClassType interface { type PropertyAttributes int
Contructor(env EnvType, this ValueType, args []ValueType) (*Object, error)
}
Class[T ClassType] struct { type PropertyDescriptor struct {
value Name string
Class T Method Callback
classReflect reflect.Type 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 ( const (
PropertyAttributesDefault = napi.Default PropertyAttributesWritable = PropertyAttributes(napi.Writable)
PropertyAttributesWritable = napi.Writable PropertyAttributesEnumerable = PropertyAttributes(napi.Enumerable)
PropertyAttributesEnumerable = napi.Enumerable PropertyAttributesConfigurable = PropertyAttributes(napi.Configurable)
PropertyAttributesConfigurable = napi.Configurable PropertyAttributesStatic = PropertyAttributes(napi.Static)
PropertyAttributesStatic = napi.Static PropertyAttributesDefault = PropertyAttributes(napi.Default)
PropertyAttributesDefaultMethod = napi.DefaultMethod PropertyAttributesDefaultMethod = PropertyAttributes(napi.DefaultMethod)
PropertyAttributesDefaultJSProperty = napi.DefaultJSProperty PropertyAttributesDefaultJSProperty = PropertyAttributes(napi.DefaultJSProperty)
) )
func CreateClass[T ClassType](env EnvType, propertys []PropertyDescriptor) (*Class[T], error) { func CreateClass[T ClassType](env EnvType) (*Class[T], error) {
ptrType := reflect.TypeFor[T]() ptrType := reflect.TypeFor[T]()
switch ptrType.Kind() { switch ptrType.Kind() {
case reflect.Struct:
case reflect.Pointer:
if ptrType = ptrType.Elem(); ptrType.Kind() != reflect.Struct {
return nil, fmt.Errorf("type %s is not a struct", ptrType.Name())
}
default: default:
return nil, fmt.Errorf("type %s is not a struct", ptrType.Name()) 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,
})
} }
startStruct := &Class[T]{classReflect: ptrType} var napiAtr []napi.PropertyDescriptor
value, status := napi.DefineClass(env.NapiValue(), ptrType.Name(), for _, value := range propertys {
func(env napi.Env, info napi.CallbackInfo) napi.Value { name, err := CreateString(env, value.Name)
startStruct.Class = reflect.New(ptrType).Interface().(T) if err != nil {
cbInfo, status := napi.GetCbInfo(env, info) return nil, err
if err := status.ToError(); err != nil { }
ThrowError(N_APIEnv(env), "", err.Error())
return nil
}
gonapiEnv := N_APIEnv(env) method, err := CreateFunction(env, value.Name, value.Method)
this := N_APIValue(gonapiEnv, cbInfo.This) if err != nil {
args := make([]ValueType, len(cbInfo.Args)) return nil, err
for i, cbArg := range cbInfo.Args { }
args[i] = N_APIValue(gonapiEnv, cbArg)
} 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,
)
res, err := startStruct.Class.Contructor(gonapiEnv, this, args)
if err != nil {
ThrowError(gonapiEnv, "", err.Error())
} else if res == nil {
und, _ := gonapiEnv.Undefined()
return und.NapiValue()
}
napi.Wrap(env, res.NapiValue(), unsafe.Pointer(startStruct), nil, nil)
return res.NapiValue()
}, propertys)
if err := status.ToError(); err != nil { if err := status.ToError(); err != nil {
return nil, err return nil, err
} }
startStruct.value = N_APIValue(env, value)
return startStruct, nil return &Class[T]{
value: N_APIValue(env, jsValue),
Class: valueOf.Interface().(T),
}, nil
} }

View File

@@ -3,16 +3,16 @@ package main
import ( import (
_ "unsafe" _ "unsafe"
_ "sirherobrine23.com.br/Sirherobrine23/napi-go/entry" _ "sirherobrine23.com.br/Sirherobrine23/napi-go/module"
"sirherobrine23.com.br/Sirherobrine23/napi-go" "sirherobrine23.com.br/Sirherobrine23/napi-go"
) )
func main() {} func main() {}
//go:linkname Register sirherobrine23.com.br/Sirherobrine23/napi-go/entry.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) {
class, err := napi.CreateClass[*ClassTest](env, []napi.PropertyDescriptor{}) class, err := napi.CreateClass[*ClassTest](env)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@@ -22,8 +22,8 @@ func Register(env napi.EnvType, export *napi.Object) {
type ClassTest struct{} type ClassTest struct{}
func (class *ClassTest) Contructor(env napi.EnvType, this napi.ValueType, args []napi.ValueType) (*napi.Object, error) { func (class *ClassTest) Contructor(ci *napi.CallbackInfo) (*napi.Object, error) {
obj, _ := napi.CreateObject(env) obj, _ := napi.CreateObject(ci.Env)
return obj, nil return obj, nil
} }