Skip to content

Commit

Permalink
chore(release): v1.26.0
Browse files Browse the repository at this point in the history
  • Loading branch information
AWS CDK Team committed Mar 22, 2021
1 parent bd906fe commit ec37187
Show file tree
Hide file tree
Showing 17 changed files with 257 additions and 73 deletions.
28 changes: 25 additions & 3 deletions internal/api/api.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package api

import (
"fmt"
"regexp"
)

// FQN represents a fully-qualified type name in the jsii type system.
type FQN string

Expand All @@ -8,14 +13,13 @@ type FQN string
// MethodOverride and PropertyOverride to simulate the union type of Override =
// MethodOverride | PropertyOverride.
type Override interface {
GoName() string
isOverride()
}

type override struct{}

func (o override) isOverride() {
return
}
func (o override) isOverride() {}

// MethodOverride is used to register a "go-native" implementation to be
// substituted to the default javascript implementation on the created object.
Expand All @@ -26,6 +30,10 @@ type MethodOverride struct {
GoMethod string `json:"cookie"`
}

func (m MethodOverride) GoName() string {
return m.GoMethod
}

// PropertyOverride is used to register a "go-native" implementation to be
// substituted to the default javascript implementation on the created object.
type PropertyOverride struct {
Expand All @@ -35,6 +43,10 @@ type PropertyOverride struct {
GoGetter string `json:"cookie"`
}

func (m PropertyOverride) GoName() string {
return m.GoGetter
}

func IsMethodOverride(value Override) bool {
switch value.(type) {
case MethodOverride, *MethodOverride:
Expand All @@ -55,6 +67,16 @@ func IsPropertyOverride(value Override) bool {

type ObjectRef struct {
InstanceID string `json:"$jsii.byref"`
Interfaces []FQN `json:"$jsii.interfaces,omitempty"`
}

func (o *ObjectRef) TypeFQN() FQN {
re := regexp.MustCompile("^(.+)@(\\d+)$")
if parts := re.FindStringSubmatch(o.InstanceID); parts == nil {
panic(fmt.Errorf("invalid instance id: %s", o.InstanceID))
} else {
return FQN(parts[1])
}
}

type EnumRef struct {
Expand Down
2 changes: 1 addition & 1 deletion internal/embedded/embedded.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const embeddedRootDir string = "resources"
var embeddedFS embed.FS

// entrypointName is the path to the entry point relative to the embeddedRootDir.
var entrypointName string = path.Join("bin", "jsii-runtime.js")
var entrypointName = path.Join("bin", "jsii-runtime.js")

// ExtractRuntime extracts a copy of the embedded runtime library into
// the designated directory, and returns the fully qualified path to the entry
Expand Down
4 changes: 2 additions & 2 deletions internal/embedded/resources/lib/program.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion internal/embedded/resources/lib/program.js.map

Large diffs are not rendered by default.

19 changes: 16 additions & 3 deletions internal/kernel/callbacks.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ func (c *callback) handle(result kernelResponder) error {
return fmt.Errorf("invalid callback object: %v", c)
}

if err != nil {
return err
}

type callbackResult struct {
CallbackID string `json:"cbid"`
Result interface{} `json:"result,omitempty"`
Expand Down Expand Up @@ -114,8 +118,17 @@ func (c *Client) invoke(method reflect.Value, args []interface{}) (retval reflec
err = fmt.Errorf("too many arguments received %d for %d", len(args), numIn)
return
}
callArgs[i] = reflect.New(argType)
c.CastAndSetToPtr(arg, callArgs[i].Interface())
if argType.Kind() == reflect.Ptr {
callArgs[i] = reflect.New(argType.Elem())
} else {
callArgs[i] = reflect.New(argType)
}
c.castAndSetToPtr(callArgs[i].Elem(), reflect.ValueOf(arg))
if argType.Kind() != reflect.Ptr && argType.Kind() != reflect.Interface {
// The result of `reflect.New` is always a pointer, so if the
// argument is by-value, we have to de-reference it first.
callArgs[i] = callArgs[i].Elem()
}
}

// Ready to catch an error if the method panics...
Expand All @@ -136,7 +149,7 @@ func (c *Client) invoke(method reflect.Value, args []interface{}) (retval reflec
result := method.Call(callArgs)
switch len(result) {
case 0:
retval = reflect.ValueOf(nil)
// Nothing to do, retval is already a 0-value.
case 1:
retval = result[0]
default:
Expand Down
3 changes: 2 additions & 1 deletion internal/kernel/client_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package kernel

import (
"github.com/aws/jsii-runtime-go/internal/typeregistry"
"reflect"
"testing"

"github.com/aws/jsii-runtime-go/internal/typeregistry"
)

func TestClient(t *testing.T) {
Expand Down
108 changes: 82 additions & 26 deletions internal/kernel/conversions.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ func (c *Client) castAndSetToPtr(ptr reflect.Value, data reflect.Value) {
// reflect.Value. In such cases, we must craft the correctly-typed zero
// value ourselves.
data = reflect.Zero(ptr.Type())
} else if ptr.Kind() == reflect.Ptr && ptr.IsNil() {
// if ptr is a Pointer type and data is valid, initialize a non-nil pointer
// type. Otherwise inner value is not-settable upon recursion. See third
// law of reflection.
// https://blog.golang.org/laws-of-reflection
ptr.Set(reflect.New(ptr.Type().Elem()))
c.castAndSetToPtr(ptr.Elem(), data)
return
} else if data.Kind() == reflect.Interface && !data.IsNil() {
// If data is a non-nil interface, unwrap it to get it's dynamic value
// type sorted out, so that further calls in this method don't have to
Expand Down Expand Up @@ -77,6 +85,11 @@ func (c *Client) castAndSetToPtr(ptr reflect.Value, data reflect.Value) {
return
}

if date, isDate := castValToDate(data); isDate {
ptr.Set(reflect.ValueOf(date))
return
}

// maps
if m, isMap := c.castValToMap(data, ptr.Type()); isMap {
ptr.Set(m)
Expand Down Expand Up @@ -104,6 +117,11 @@ func (c *Client) castAndSetToPtr(ptr reflect.Value, data reflect.Value) {
// objref for the runtime. Recursively casts types that may contain nested
// object references.
func (c *Client) CastPtrToRef(dataVal reflect.Value) interface{} {
if !dataVal.IsValid() {
// dataVal is a 0-value, meaning we have no value available... We return
// this to JavaScript as a "null" value.
return nil
}
if (dataVal.Kind() == reflect.Interface || dataVal.Kind() == reflect.Ptr) && dataVal.IsNil() {
return nil
}
Expand Down Expand Up @@ -131,30 +149,32 @@ func (c *Client) CastPtrToRef(dataVal reflect.Value) interface{} {
return c.CastPtrToRef(elem)
}

if ref, err := c.ManageObject(dataVal); err != nil {
panic(err)
} else {
return ref
}
if dataVal.Elem().Kind() == reflect.Struct {
elemVal := dataVal.Elem()
if fields, fqn, isStruct := c.Types().StructFields(elemVal.Type()); isStruct {
data := make(map[string]interface{})
for _, field := range fields {
fieldVal := elemVal.FieldByIndex(field.Index)
if (fieldVal.Kind() == reflect.Ptr || fieldVal.Kind() == reflect.Interface) && fieldVal.IsNil() {
continue
}
key := field.Tag.Get("json")
data[key] = c.CastPtrToRef(fieldVal)
}

case reflect.Struct:
if fields, fqn, isStruct := c.Types().StructFields(dataVal.Type()); isStruct {
data := make(map[string]interface{})
for _, field := range fields {
fieldVal := dataVal.FieldByIndex(field.Index)
if (fieldVal.Kind() == reflect.Ptr || fieldVal.Kind() == reflect.Interface) && fieldVal.IsNil() {
continue
return api.WireStruct{
StructDescriptor: api.StructDescriptor{
FQN: fqn,
Fields: data,
},
}
key := field.Tag.Get("json")
data[key] = c.CastPtrToRef(fieldVal)
}
}

return api.WireStruct{
StructDescriptor: api.StructDescriptor{
FQN: fqn,
Fields: data,
},
}
if ref, err := c.ManageObject(dataVal); err != nil {
panic(err)
} else {
return ref
}

case reflect.Slice:
Expand All @@ -172,19 +192,39 @@ func (c *Client) CastPtrToRef(dataVal reflect.Value) interface{} {
return dataVal.Interface()
}

func castValToRef(data reflect.Value) (api.ObjectRef, bool) {
ref := api.ObjectRef{}
ok := false

func castValToRef(data reflect.Value) (ref api.ObjectRef, ok bool) {
if data.Kind() == reflect.Map {
for _, k := range data.MapKeys() {
// Finding values type requires extracting from reflect.Value
// otherwise .Kind() returns `interface{}`
v := reflect.ValueOf(data.MapIndex(k).Interface())

if k.Kind() == reflect.String && k.String() == "$jsii.byref" && v.Kind() == reflect.String {
if k.Kind() != reflect.String {
continue
}

switch k.String() {
case "$jsii.byref":
if v.Kind() != reflect.String {
ok = false
return
}
ref.InstanceID = v.String()
ok = true
case "$jsii.interfaces":
if v.Kind() != reflect.Slice {
continue
}
ifaces := make([]api.FQN, v.Len())
for i := 0; i < v.Len(); i++ {
e := reflect.ValueOf(v.Index(i).Interface())
if e.Kind() != reflect.String {
ok = false
return
}
ifaces[i] = api.FQN(e.String())
}
ref.Interfaces = ifaces
}

}
Expand All @@ -193,6 +233,22 @@ func castValToRef(data reflect.Value) (api.ObjectRef, bool) {
return ref, ok
}

// TODO: This should return a time.Time instead
func castValToDate(data reflect.Value) (date string, ok bool) {
if data.Kind() == reflect.Map {
for _, k := range data.MapKeys() {
v := reflect.ValueOf(data.MapIndex(k).Interface())
if k.Kind() == reflect.String && k.String() == "$jsii.date" && v.Kind() == reflect.String {
date = v.String()
ok = true
break
}
}
}

return
}

func castValToEnumRef(data reflect.Value) (enum api.EnumRef, ok bool) {
ok = false

Expand All @@ -205,7 +261,7 @@ func castValToEnumRef(data reflect.Value) (enum api.EnumRef, ok bool) {
if k.Kind() == reflect.String && k.String() == "$jsii.enum" && v.Kind() == reflect.String {
enum.MemberFQN = v.String()
ok = true
return
break
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions internal/kernel/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import "github.com/aws/jsii-runtime-go/internal/api"

type CreateProps struct {
FQN api.FQN `json:"fqn"`
Interfaces []api.FQN `json:"interfaces"`
Arguments []interface{} `json:"args"`
Overrides []api.Override `json:"overrides"`
Interfaces []api.FQN `json:"interfaces,omitempty"`
Arguments []interface{} `json:"args,omitempty"`
Overrides []api.Override `json:"overrides,omitempty"`
}

// TODO extends AnnotatedObjRef?
Expand Down
7 changes: 6 additions & 1 deletion internal/kernel/manage-object.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ const objectFQN = "Object"

func (c *Client) ManageObject(v reflect.Value) (ref api.ObjectRef, err error) {
// Ensuring we use a pointer, so we can see pointer-receiver methods, too.
vt := reflect.Indirect(v).Addr().Type()
var vt reflect.Type
if v.Kind() == reflect.Interface || (v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Interface) {
vt = reflect.Indirect(reflect.ValueOf(v.Interface())).Addr().Type()
} else {
vt = reflect.Indirect(v).Addr().Type()
}
interfaces, overrides := c.Types().DiscoverImplementation(vt)

var resp CreateResponse
Expand Down
2 changes: 1 addition & 1 deletion internal/kernel/version.generated.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
package kernel

const version = "1.25.0"
const version = "1.26.0"
15 changes: 10 additions & 5 deletions internal/objectstore/objectstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,20 +53,25 @@ func (o *ObjectStore) Register(value reflect.Value, instanceID string) error {
if existing == instanceID {
return nil
}
return fmt.Errorf("attempting to register %v as %s, but it was already registered as %s", value, instanceID, existing)
return fmt.Errorf("attempting to register %s as %s, but it was already registered as %s", value, instanceID, existing)
}

aliases := findAliases(value)

if existing, found := o.idToObject[instanceID]; found {
if existing == value {
return nil
}
return fmt.Errorf("attempted to register %v as %s, but %v has this ID", value, instanceID, existing)
// Value already exists (e.g: a constructor made a callback with "this"
// passed as an argument). We make the current value an alias of the new
// one.
aliases = append(aliases, existing)
}

aliases := findAliases(value)
for _, alias := range aliases {
ptr := alias.Pointer()
if existing, found := o.objectToID[ptr]; found && existing != instanceID {
return fmt.Errorf("value %s is embedded in %s which has ID %s, but was already assigned %s", alias, value, instanceID, existing)
return fmt.Errorf("value %s is embedded in %s which has ID %s, but was already assigned %s", alias.String(), value.String(), instanceID, existing)
}
}

Expand Down Expand Up @@ -131,7 +136,7 @@ func canonicalValue(value reflect.Value) (reflect.Value, error) {
// a nil value, or a value that is not ultimately a reflect.Struct may result
// in panic.
func findAliases(value reflect.Value) []reflect.Value {
result := []reflect.Value{}
var result []reflect.Value

// Indirect so we always work on the pointer referree
value = reflect.Indirect(value)
Expand Down
Loading

0 comments on commit ec37187

Please sign in to comment.