packages/@jsii/go-runtime/jsii-runtime-go/runtime/runtime.go (311 lines of code) (raw):
package runtime
import (
"fmt"
"reflect"
"strings"
"github.com/aws/jsii-runtime-go/internal/api"
"github.com/aws/jsii-runtime-go/internal/kernel"
)
// FQN represents a fully-qualified type name in the jsii type system.
type FQN api.FQN
// Member is a runtime descriptor for a class or interface member
type Member interface {
toOverride() api.Override
}
// MemberMethod is a runtime descriptor for a class method (implementation of Member)
type MemberMethod api.MethodOverride
func (m MemberMethod) toOverride() api.Override {
return api.MethodOverride(m)
}
// MemberProperty is a runtime descriptor for a class or interface property (implementation of Member)
type MemberProperty api.PropertyOverride
func (m MemberProperty) toOverride() api.Override {
return api.PropertyOverride(m)
}
// Load ensures a npm package is loaded in the jsii kernel.
func Load(name string, version string, tarball []byte) {
c := kernel.GetClient()
_, err := c.Load(kernel.LoadProps{
Name: name,
Version: version,
}, tarball)
if err != nil {
panic(err)
}
}
// RegisterClass associates a class fully qualified name to the specified class
// interface, member list, and proxy maker function. Panics if class is not a go
// interface, or if the provided fqn was already used to register a different type.
func RegisterClass(fqn FQN, class reflect.Type, members []Member, maker func() interface{}) {
client := kernel.GetClient()
overrides := make([]api.Override, len(members))
for i, m := range members {
overrides[i] = m.toOverride()
}
if err := client.Types().RegisterClass(api.FQN(fqn), class, overrides, maker); err != nil {
panic(err)
}
}
// RegisterEnum associates an enum's fully qualified name to the specified enum
// type, and members. Panics if enum is not a reflect.String type, any value in
// the provided members map is of a type other than enum, or if the provided
// fqn was already used to register a different type.
func RegisterEnum(fqn FQN, enum reflect.Type, members map[string]interface{}) {
client := kernel.GetClient()
if err := client.Types().RegisterEnum(api.FQN(fqn), enum, members); err != nil {
panic(err)
}
}
// RegisterInterface associates an interface's fully qualified name to the
// specified interface type, member list, and proxy maker function. Panics if iface is not
// an interface, or if the provided fqn was already used to register a different type.
func RegisterInterface(fqn FQN, iface reflect.Type, members []Member, maker func() interface{}) {
client := kernel.GetClient()
overrides := make([]api.Override, len(members))
for i, m := range members {
overrides[i] = m.toOverride()
}
if err := client.Types().RegisterInterface(api.FQN(fqn), iface, overrides, maker); err != nil {
panic(err)
}
}
// RegisterStruct associates a struct's fully qualified name to the specified
// struct type. Panics if strct is not a struct, or if the provided fqn was
// already used to register a different type.
func RegisterStruct(fqn FQN, strct reflect.Type) {
client := kernel.GetClient()
if err := client.Types().RegisterStruct(api.FQN(fqn), strct); err != nil {
panic(err)
}
}
// RegisterStructValidator adds a validator function to an already registered
// struct type. This is separate call largely to maintain backwards compatibility
// with existing code.
func RegisterStructValidator(strct reflect.Type, validator func(interface{}, func() string) error) {
client := kernel.GetClient()
if err := client.Types().RegisterStructValidator(strct, validator); err != nil {
panic(err)
}
}
// InitJsiiProxy initializes a jsii proxy instance at the provided pointer.
// Panics if the pointer cannot be initialized to a proxy instance (i.e: the
// element of it is not a registered jsii interface or class type).
func InitJsiiProxy(ptr interface{}) {
ptrVal := reflect.ValueOf(ptr).Elem()
if err := kernel.GetClient().Types().InitJsiiProxy(ptrVal, ptrVal.Type()); err != nil {
panic(err)
}
}
// IsAnonymousProxy tells whether the value v is an anonymous object proxy, or
// a pointer to one.
func IsAnonymousProxy(v interface{}) bool {
return kernel.GetClient().Types().IsAnonymousProxy(v)
}
// Create will construct a new JSII object within the kernel runtime. This is
// called by jsii object constructors.
func Create(fqn FQN, args []interface{}, inst interface{}) {
client := kernel.GetClient()
instVal := reflect.ValueOf(inst)
structVal := instVal.Elem()
instType := structVal.Type()
numField := instType.NumField()
for i := 0; i < numField; i++ {
field := instType.Field(i)
if !field.Anonymous {
continue
}
switch field.Type.Kind() {
case reflect.Interface:
fieldVal := structVal.Field(i)
if !fieldVal.IsNil() {
continue
}
if err := client.Types().InitJsiiProxy(fieldVal, fieldVal.Type()); err != nil {
panic(err)
}
case reflect.Struct:
fieldVal := structVal.Field(i)
if !fieldVal.IsZero() {
continue
}
if err := client.Types().InitJsiiProxy(fieldVal, fieldVal.Type()); err != nil {
panic(err)
}
}
}
// Find method overrides thru reflection
mOverrides := getMethodOverrides(inst, "jsiiProxy_")
// If overriding struct has no overriding methods, could happen if
// overriding methods are not defined with pointer receiver.
if len(mOverrides) == 0 && !strings.HasPrefix(instType.Name(), "jsiiProxy_") {
panic(fmt.Errorf("%v has no overriding methods. Overriding methods must be defined with a pointer receiver", instType.Name()))
}
var overrides []api.Override
registry := client.Types()
added := make(map[string]bool)
for _, name := range mOverrides {
// Use getter's name even if setter is overriden
if strings.HasPrefix(name, "Set") {
propName := name[3:]
if override, ok := registry.GetOverride(api.FQN(fqn), propName); ok {
if !added[propName] {
added[propName] = true
overrides = append(overrides, override)
}
continue
}
}
if override, ok := registry.GetOverride(api.FQN(fqn), name); ok {
if !added[name] {
added[name] = true
overrides = append(overrides, override)
}
}
}
interfaces, newOverrides := client.Types().DiscoverImplementation(instType)
overrides = append(overrides, newOverrides...)
res, err := client.Create(kernel.CreateProps{
FQN: api.FQN(fqn),
Arguments: convertArguments(args),
Interfaces: interfaces,
Overrides: overrides,
})
if err != nil {
panic(err)
}
if err = client.RegisterInstance(instVal, api.ObjectRef{InstanceID: res.InstanceID, Interfaces: interfaces}); err != nil {
panic(err)
}
}
// Invoke will call a method on a jsii class instance. The response will be
// decoded into the expected return type for the method being called.
func Invoke(obj interface{}, method string, args []interface{}, ret interface{}) {
client := kernel.GetClient()
// Find reference to class instance in client
ref, found := client.FindObjectRef(reflect.ValueOf(obj))
if !found {
panic("No Object Found")
}
res, err := client.Invoke(kernel.InvokeProps{
Method: method,
Arguments: convertArguments(args),
ObjRef: ref,
})
if err != nil {
panic(err)
}
client.CastAndSetToPtr(ret, res.Result)
}
// InvokeVoid will call a void method on a jsii class instance.
func InvokeVoid(obj interface{}, method string, args []interface{}) {
client := kernel.GetClient()
// Find reference to class instance in client
ref, found := client.FindObjectRef(reflect.ValueOf(obj))
if !found {
panic("No Object Found")
}
_, err := client.Invoke(kernel.InvokeProps{
Method: method,
Arguments: convertArguments(args),
ObjRef: ref,
})
if err != nil {
panic(err)
}
}
// StaticInvoke will call a static method on a given jsii class. The response
// will be decoded into the expected return type for the method being called.
func StaticInvoke(fqn FQN, method string, args []interface{}, ret interface{}) {
client := kernel.GetClient()
res, err := client.SInvoke(kernel.StaticInvokeProps{
FQN: api.FQN(fqn),
Method: method,
Arguments: convertArguments(args),
})
if err != nil {
panic(err)
}
client.CastAndSetToPtr(ret, res.Result)
}
// StaticInvokeVoid will call a static void method on a given jsii class.
func StaticInvokeVoid(fqn FQN, method string, args []interface{}) {
client := kernel.GetClient()
_, err := client.SInvoke(kernel.StaticInvokeProps{
FQN: api.FQN(fqn),
Method: method,
Arguments: convertArguments(args),
})
if err != nil {
panic(err)
}
}
// Get reads a property value on a given jsii class instance. The response
// should be decoded into the expected type of the property being read.
func Get(obj interface{}, property string, ret interface{}) {
client := kernel.GetClient()
// Find reference to class instance in client
ref, found := client.FindObjectRef(reflect.ValueOf(obj))
if !found {
panic(fmt.Errorf("no object reference found for %v", obj))
}
res, err := client.Get(kernel.GetProps{
Property: property,
ObjRef: ref,
})
if err != nil {
panic(err)
}
client.CastAndSetToPtr(ret, res.Value)
}
// StaticGet reads a static property value on a given jsii class. The response
// should be decoded into the expected type of the property being read.
func StaticGet(fqn FQN, property string, ret interface{}) {
client := kernel.GetClient()
res, err := client.SGet(kernel.StaticGetProps{
FQN: api.FQN(fqn),
Property: property,
})
if err != nil {
panic(err)
}
client.CastAndSetToPtr(ret, res.Value)
}
// Set writes a property on a given jsii class instance. The value should match
// the type of the property being written, or the jsii kernel will crash.
func Set(obj interface{}, property string, value interface{}) {
client := kernel.GetClient()
// Find reference to class instance in client
ref, found := client.FindObjectRef(reflect.ValueOf(obj))
if !found {
panic("No Object Found")
}
_, err := client.Set(kernel.SetProps{
Property: property,
Value: client.CastPtrToRef(reflect.ValueOf(value)),
ObjRef: ref,
})
if err != nil {
panic(err)
}
}
// StaticSet writes a static property on a given jsii class. The value should
// match the type of the property being written, or the jsii kernel will crash.
func StaticSet(fqn FQN, property string, value interface{}) {
client := kernel.GetClient()
_, err := client.SSet(kernel.StaticSetProps{
FQN: api.FQN(fqn),
Property: property,
Value: client.CastPtrToRef(reflect.ValueOf(value)),
})
if err != nil {
panic(err)
}
}
// convertArguments turns an argument struct and produces a list of values
// ready for inclusion in an invoke or create request.
func convertArguments(args []interface{}) []interface{} {
if len(args) == 0 {
return nil
}
result := make([]interface{}, len(args))
client := kernel.GetClient()
for i, arg := range args {
val := reflect.ValueOf(arg)
result[i] = client.CastPtrToRef(val)
}
return result
}
// Get ptr's methods names which override "base" struct methods.
// The "base" struct is identified by name prefix "basePrefix".
func getMethodOverrides(ptr interface{}, basePrefix string) (methods []string) {
// Methods override cache: [methodName]bool
mCache := make(map[string]bool)
getMethodOverridesRec(ptr, basePrefix, mCache)
// Return overriden methods names in embedding hierarchy
for m := range mCache {
methods = append(methods, m)
}
return
}
func getMethodOverridesRec(ptr interface{}, basePrefix string, cache map[string]bool) {
ptrType := reflect.TypeOf(ptr)
if ptrType.Kind() != reflect.Ptr {
return
}
structType := ptrType.Elem()
if structType.Kind() != reflect.Struct {
return
}
if strings.HasPrefix(structType.Name(), basePrefix) {
// Skip base class
return
}
ptrVal := reflect.ValueOf(ptr)
structVal := ptrVal.Elem()
// Add embedded/super overrides first
for i := 0; i < structType.NumField(); i++ {
field := structType.Field(i)
if !field.Anonymous {
continue
}
if field.Type.Kind() == reflect.Ptr ||
field.Type.Kind() == reflect.Interface {
p := structVal.Field(i)
if !p.IsNil() {
getMethodOverridesRec(p.Interface(), basePrefix, cache)
}
}
}
// Add overrides in current struct
// Current struct's value-type method-set
valMethods := make(map[string]bool)
for i := 0; i < structType.NumMethod(); i++ {
valMethods[structType.Method(i).Name] = true
}
// Compare current struct's pointer-type method-set to its value-type method-set
for i := 0; i < ptrType.NumMethod(); i++ {
mn := ptrType.Method(i).Name
if !valMethods[mn] {
cache[mn] = true
}
}
}