Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(go): add UnsafeCast function #3316

Merged
merged 8 commits into from Jan 12, 2022
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
26 changes: 13 additions & 13 deletions .github/workflows/main.yml
Expand Up @@ -31,10 +31,10 @@ jobs:
uses: actions/setup-dotnet@v1
with:
dotnet-version: '5.0.x'
- name: Set up Go 1.16
- name: Set up Go 1.17
uses: actions/setup-go@v2
with:
go-version: '1.16'
go-version: '1.17'
- name: Set up Java 8
uses: actions/setup-java@v2
with:
Expand Down Expand Up @@ -125,10 +125,10 @@ jobs:
uses: actions/setup-dotnet@v1
with:
dotnet-version: '5.0.x'
- name: Set up Go 1.16
- name: Set up Go 1.17
uses: actions/setup-go@v2
with:
go-version: '1.16'
go-version: '1.17'
- name: Set up Java 8
uses: actions/setup-java@v2
with:
Expand Down Expand Up @@ -211,7 +211,7 @@ jobs:
matrix:
# All currently supported node versions (Maintenance LTS, Active LTS, Current)
dotnet: ['3.1.x']
go: ['1.16']
go: ['1.17']
java: ['8']
node: ['12', '14', '16']
os: [ubuntu-latest]
Expand All @@ -221,53 +221,53 @@ jobs:
# Test using Windows
- os: windows-latest
dotnet: '3.1.x'
go: '1.16'
go: '1.17'
java: '8'
node: '12'
python: '3.6'
# Test using macOS
- os: macos-latest
dotnet: '3.1.x'
go: '1.16'
go: '1.17'
java: '8'
node: '12'
python: '3.6'
# Test alternate .NETs
- java: '8'
dotnet: '5.0.x'
go: '1.16'
go: '1.17'
node: '12'
os: ubuntu-latest
python: '3.6'
- java: '8'
dotnet: '6.0.x'
go: '1.16'
go: '1.17'
node: '12'
os: ubuntu-latest
python: '3.6'
# Test alternate Javas
- java: '11'
dotnet: '3.1.x'
go: '1.16'
go: '1.17'
node: '12'
os: ubuntu-latest
python: '3.6'
# Test alternate Pythons
- python: '3.7'
dotnet: '3.1.x'
go: '1.16'
go: '1.17'
java: '8'
node: '12'
os: ubuntu-latest
- python: '3.8'
dotnet: '3.1.x'
go: '1.16'
go: '1.17'
java: '8'
node: '12'
os: ubuntu-latest
- python: '3.9'
dotnet: '3.1.x'
go: '1.16'
go: '1.17'
java: '8'
node: '12'
os: ubuntu-latest
Expand Down
3 changes: 2 additions & 1 deletion gh-pages/content/specification/6-compliance-report.md
Expand Up @@ -5,7 +5,7 @@
This section details the current state of each language binding with respect to our standard compliance suite.


| number | test | java (99.16%) | golang (78.15%) | Dotnet | Python |
| number | test | java (98.33%) | golang (78.33%) | Dotnet | Python |
| ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | -------------------------------------------- | ------ | ------ |
| 1 | asyncOverrides_overrideCallsSuper | 🟢 | [🔴](https://github.com/aws/jsii/issues/2670) | ⭕ | ⭕ |
| 2 | [arrayReturnedByMethodCanBeRead]("Array created in the kernel can be queried for its elements") | 🟢 | 🟢 | ⭕ | ⭕ |
Expand Down Expand Up @@ -126,3 +126,4 @@ This section details the current state of each language binding with respect to
| 117 | testInterfaces | 🟢 | 🟢 | ⭕ | ⭕ |
| 118 | [callbackParameterIsInterface]("Validates pure interfaces can be passed to callbacks") | ⭕ | 🟢 | ⭕ | ⭕ |
| 119 | [classCanBeUsedWhenNotExpressedlyLoaded]("Validates that types not explicitly loaded by the user can safely be returned by JS code") | 🟢 | 🟢 | ⭕ | ⭕ |
| 120 | [downcasting]("Ensures unsafe-cast features work as expected") | ⭕ | 🟢 | ⭕ | ⭕ |
10 changes: 10 additions & 0 deletions packages/@jsii/go-runtime-test/project/compliance_test.go
Expand Up @@ -1645,6 +1645,16 @@ func (suite *ComplianceSuite) TestClassCanBeUsedWhenNotExpressedlyLoaded() {
cdk16625.New().Test()
}

func (suite *ComplianceSuite) TestDownCasting() {
require := suite.Require()

anyValue := calc.SomeTypeJsii976_ReturnAnonymous()
var realValue calc.IReturnJsii976

jsii.UnsafeCast(anyValue, &realValue)

require.Equal(realValue.Foo(), jsii.Number(1337))
}

// required to make `go test` recognize the suite.
func TestComplianceSuite(t *testing.T) {
Expand Down
13 changes: 12 additions & 1 deletion packages/@jsii/go-runtime-test/project/go.mod
@@ -1,6 +1,6 @@
module github.com/aws/jsii/go-runtime-test

go 1.15
go 1.17

require (
github.com/aws/jsii-runtime-go v0.0.0
Expand All @@ -12,6 +12,17 @@ require (
golang.org/x/tools v0.1.0
)

require (
github.com/Masterminds/semver/v3 v3.1.1 // indirect
github.com/aws/jsii/jsii-calc/go/scopejsiicalcbaseofbase/v2 v2.1.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/mod v0.3.0 // indirect
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
)

replace (
github.com/aws/jsii-runtime-go => ../../go-runtime/jsii-runtime-go
github.com/aws/jsii/jsii-calc/go/jcb => ../jsii-calc/go/jcb
Expand Down
6 changes: 4 additions & 2 deletions packages/@jsii/go-runtime-test/project/go.sum
@@ -1,7 +1,8 @@
github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
Expand Down Expand Up @@ -39,5 +40,6 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1N
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Expand Up @@ -11,7 +11,7 @@ func New() abc.Cdk16625 {
return c
}

type cdk16625 struct{
type cdk16625 struct {
abc.Cdk16625
}

Expand Down
1 change: 1 addition & 0 deletions packages/@jsii/go-runtime-test/project/tools.go
@@ -1,3 +1,4 @@
//go:build tools
// +build tools

// Package tools contains the necessary statements to ensure tool dependencies
Expand Down
59 changes: 59 additions & 0 deletions packages/@jsii/go-runtime/jsii-runtime-go/cast.go
@@ -0,0 +1,59 @@
package jsii

import (
"fmt"
"reflect"

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

// UnsafeCast converts the given interface value to the desired target interface
// pointer. Panics if the from value is not a jsii proxy object, or if the to
// value is not a pointer to an interface type.
func UnsafeCast(from interface{}, into interface{}) {
rinto := reflect.ValueOf(into)
if rinto.Kind() != reflect.Ptr {
panic(fmt.Errorf("Second argument to UnsafeCast must be a pointer to an interface. Received %s", rinto.Type().String()))
}
rinto = rinto.Elem()
if rinto.Kind() != reflect.Interface {
panic(fmt.Errorf("Second argument to UnsafeCast must be a pointer to an interface. Received pointer to %s", rinto.Type().String()))
}

rfrom := reflect.ValueOf(from)

// If rfrom is essentially nil, set into to nil and return.
if !rfrom.IsValid() || rfrom.IsZero() {
null := reflect.Zero(rinto.Type())
rinto.Set(null)
return
}
// Interfaces may present as a pointer to an implementing struct, and that's fine...
if rfrom.Kind() != reflect.Interface && rfrom.Kind() != reflect.Ptr {
panic(fmt.Errorf("First argument to UnsafeCast must be an interface value. Received %s", rfrom.Type().String()))
}

// If rfrom can be directly converted to rinto, just do it.
if rfrom.CanConvert(rinto.Type()) {
RomainMuller marked this conversation as resolved.
Show resolved Hide resolved
rfrom = rfrom.Convert(rinto.Type())
rinto.Set(rfrom)
return
}

client := kernel.GetClient()
if objID, found := client.FindObjectRef(rfrom); found {
// Ensures the value is initialized properly. Panics if the target value is not a jsii interface type.
client.Types().InitJsiiProxy(rinto)

// If the target type is a behavioral interface, add it to the ObjectRef.Interfaces list.
if fqn, found := client.Types().InterfaceFQN(rinto.Type()); found {
objID.Interfaces = append(objID.Interfaces, fqn)
}

// Make the new value an alias to the old value.
client.RegisterInstance(rinto, objID)
return
}

panic(fmt.Errorf("First argument to UnsafeCast must be a jsii proxy value. Received %s", rfrom.String()))
}
101 changes: 101 additions & 0 deletions packages/@jsii/go-runtime/jsii-runtime-go/cast_test.go
@@ -0,0 +1,101 @@
package jsii

import (
"reflect"
"testing"

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

type MockInterfaceABase interface {
MockMethodABase(_ float64)
}

type mockABase struct {
_ int // padding
}

func (m *mockABase) MockMethodABase(_ float64) {}

type MockInterfaceA interface {
MockInterfaceABase
MockMethodA(_ string)
}

func NewMockInterfaceA() MockInterfaceA {
return &mockA{mockABase{}}
}

type mockA struct {
mockABase
}

func (m *mockA) MockMethodA(_ string) {}

type MockInterfaceB interface {
MockMethodB(_ int)
}

func NewMockInterfaceB() MockInterfaceB {
return &mockB{}
}

type mockB struct {
_ int // Padding
}

func (m *mockB) MockMethodB(_ int) {}

func TestNilSource(t *testing.T) {
// Make "into" not nil to ensure the cast function overwrites it.
into := NewMockInterfaceB()
UnsafeCast(nil, &into)

if into != nil {
t.Fail()
}
}

func TestSourceAndTargetAreTheSame(t *testing.T) {
into := NewMockInterfaceB()
original := into
UnsafeCast(into, &into)

if into != original {
t.Fail()
}
}

func TestTargetIsSubclassOfSource(t *testing.T) {
from := NewMockInterfaceA()
var into MockInterfaceABase
UnsafeCast(from, &into)

if into != from {
t.Fail()
}
}

func TestRegistersAlias(t *testing.T) {
client := kernel.GetClient()

objid := api.ObjectRef{InstanceID: "Object@1337#42"}
from := NewMockInterfaceA()
client.RegisterInstance(reflect.ValueOf(from), objid)

var into MockInterfaceB
client.Types().RegisterInterface(api.FQN("mock.InterfaceB"), reflect.TypeOf(&into).Elem(), []api.Override{}, func() interface{} { return NewMockInterfaceB() })

UnsafeCast(from, &into)

if into == nil {
t.Fail()
}

if refid, found := client.FindObjectRef(reflect.ValueOf(into)); !found {
t.Fail()
} else if refid.InstanceID != objid.InstanceID {
t.Fail()
}
}
8 changes: 7 additions & 1 deletion packages/@jsii/go-runtime/jsii-runtime-go/go.mod
@@ -1,10 +1,16 @@
module github.com/aws/jsii-runtime-go

go 1.16
go 1.17

require (
github.com/Masterminds/semver/v3 v3.1.1
github.com/stretchr/testify v1.7.0
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
)

retract v1.27.0
7 changes: 4 additions & 3 deletions packages/@jsii/go-runtime/jsii-runtime-go/go.sum
@@ -1,14 +1,15 @@
github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=