-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(go): support direct implementation of jsii interfaces (aws#2614)
Pure go implementations of jsii interfaces are detected upon being passed as a parameter to a JavaScript call. When this happens, a `create` call is sent to the `@jsii/kernel` process with the correct list of implemented interface FQNs, and all necessary overrides records. The object is then registered into the new `ObjectStore` for later reference. When a callback request interrupts the normal request/response flow, the `kernel.Client` will handle the callback by getting the appropriate receiver object, invoking the designated go implementation, and sending the result to the `@jsii/kernel` process before resuming normal response handling. Related to aws#2048 (partial support)
- Loading branch information
1 parent
19cbd25
commit 9da3282
Showing
43 changed files
with
3,088 additions
and
1,534 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import { spawnSync } from 'child_process'; | ||
import { constants } from 'os'; | ||
import { join } from 'path'; | ||
import { argv, exit } from 'process'; | ||
|
||
import { runCommand } from './_constants'; | ||
|
||
if (argv.length < 3) { | ||
console.error(`Not enough arguments: ${argv[0]} ${argv[1]} <cmd> [args]`); | ||
exit(1); | ||
} | ||
|
||
const [, , cmd, ...args] = argv; | ||
|
||
const result = spawnSync('go', ['env', 'GOPATH'], { | ||
stdio: ['inherit', 'pipe', 'inherit'], | ||
}); | ||
|
||
if (result.error) { | ||
console.error('"go env GOPATH" failed:', result.error); | ||
exit(-1); | ||
} | ||
|
||
if (result.status !== 0) { | ||
console.error('"go env GOPATH" failed:', result.status ?? result.signal); | ||
exit( | ||
result.status ?? 128 + constants.signals[result.signal as NodeJS.Signals], | ||
); | ||
} | ||
|
||
const gopath = result.stdout.toString('utf8').trim(); | ||
runCommand(join(gopath, 'bin', cmd), args); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,37 @@ | ||
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/yuin/goldmark v1.2.1 h1:ruQGxdhGHe7FWOJPT0mKs5+pD2Xs1Bm/kdGlHO04FmM= | ||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= | ||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | ||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | ||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= | ||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | ||
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 h1:2M3HP5CCK1Si9FQhwnzYhXdG6DXeebvUHFpre8QvbyI= | ||
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= | ||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= | ||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= | ||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= | ||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||
golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI= | ||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= | ||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck= | ||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 h1:myAQVi0cGEoqQVR5POX+8RR2mrocKqNN1hmeMqhX27k= | ||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= | ||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | ||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | ||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= | ||
golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY= | ||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= | ||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= | ||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= |
26 changes: 26 additions & 0 deletions
26
packages/@jsii/go-runtime/jsii-calc-test/callbacks_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package main | ||
|
||
import ( | ||
"testing" | ||
|
||
calc "github.com/aws/jsii/jsii-calc/go/jsiicalc/v3" | ||
) | ||
|
||
func TestPureInterfacesCanBeUsedTransparently(t *testing.T) { | ||
expected := calc.StructB{RequiredString: "It's Britney b**ch!"} | ||
delegate := &StructReturningDelegate{expected: expected} | ||
consumer := calc.NewConsumePureInterface(delegate) | ||
actual := consumer.WorkItBaby() | ||
|
||
if actual != expected { | ||
t.Errorf("Expected %v; actual: %v", expected, actual) | ||
} | ||
} | ||
|
||
type StructReturningDelegate struct { | ||
expected calc.StructB | ||
} | ||
|
||
func (o *StructReturningDelegate) ReturnStruct() calc.StructB { | ||
return o.expected | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
3 changes: 3 additions & 0 deletions
3
packages/@jsii/go-runtime/jsii-runtime-go/internal/embedded/doc.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
// Package embedded contains the embedded @jsii/kernel code, which is used | ||
// unless the JSII_RUNTIME environment variable is set to a different program. | ||
package embedded |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
143 changes: 137 additions & 6 deletions
143
packages/@jsii/go-runtime/jsii-runtime-go/internal/kernel/callbacks.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,146 @@ | ||
package kernel | ||
|
||
import ( | ||
"fmt" | ||
"reflect" | ||
|
||
"github.com/aws/jsii-runtime-go/internal/api" | ||
) | ||
|
||
type CallbacksResponse struct { | ||
kernelResponse | ||
Callbacks []api.Callback `json:"callbacks"` | ||
type callback struct { | ||
CallbackID string `json:"cbid"` | ||
Cookie string `json:"cookie"` | ||
Invoke *invokeCallback `json:"invoke"` | ||
Get *getCallback `json:"get"` | ||
Set *setCallback `json:"set"` | ||
} | ||
|
||
func (c *client) Callbacks() (response CallbacksResponse, err error) { | ||
err = c.request(kernelRequest{"callbacks"}, &response) | ||
return | ||
func (c *callback) handle(result kernelResponder) error { | ||
var ( | ||
retval reflect.Value | ||
err error | ||
) | ||
if c.Invoke != nil { | ||
retval, err = c.Invoke.handle(c.Cookie) | ||
} else if c.Get != nil { | ||
retval, err = c.Get.handle(c.Cookie) | ||
} else if c.Set != nil { | ||
retval, err = c.Set.handle(c.Cookie) | ||
} else { | ||
return fmt.Errorf("invalid callback object: %v", c) | ||
} | ||
|
||
type callbackResult struct { | ||
CallbackID string `json:"cbid"` | ||
Result interface{} `json:"result,omitempty"` | ||
Error string `json:"err,omitempty"` | ||
} | ||
type completeRequest struct { | ||
kernelRequester | ||
callbackResult `json:"complete"` | ||
} | ||
|
||
client := GetClient() | ||
request := completeRequest{} | ||
request.CallbackID = c.CallbackID | ||
request.Result = client.CastPtrToRef(retval) | ||
if err != nil { | ||
request.Error = err.Error() | ||
} | ||
return client.request(request, result) | ||
} | ||
|
||
type invokeCallback struct { | ||
Method string `json:"method"` | ||
Arguments []interface{} `json:"args"` | ||
ObjRef api.ObjectRef `json:"objref"` | ||
} | ||
|
||
func (i *invokeCallback) handle(cookie string) (retval reflect.Value, err error) { | ||
client := GetClient() | ||
|
||
receiver := reflect.ValueOf(client.GetObject(i.ObjRef)) | ||
method := receiver.MethodByName(cookie) | ||
|
||
return client.invoke(method, i.Arguments) | ||
} | ||
|
||
type getCallback struct { | ||
Property string `json:"property"` | ||
ObjRef api.ObjectRef `json:"objref"` | ||
} | ||
|
||
func (g *getCallback) handle(cookie string) (retval reflect.Value, err error) { | ||
client := GetClient() | ||
|
||
receiver := reflect.ValueOf(client.GetObject(g.ObjRef)) | ||
method := receiver.MethodByName(cookie) | ||
|
||
return client.invoke(method, nil) | ||
} | ||
|
||
type setCallback struct { | ||
Property string `json:"property"` | ||
Value interface{} `json:"value"` | ||
ObjRef api.ObjectRef `json:"objref"` | ||
} | ||
|
||
func (s *setCallback) handle(cookie string) (retval reflect.Value, err error) { | ||
client := GetClient() | ||
|
||
receiver := reflect.ValueOf(client.GetObject(s.ObjRef)) | ||
method := receiver.MethodByName(fmt.Sprintf("Set%v", cookie)) | ||
|
||
return client.invoke(method, []interface{}{s.Value}) | ||
} | ||
|
||
func (c *Client) invoke(method reflect.Value, args []interface{}) (retval reflect.Value, err error) { | ||
if !method.IsValid() { | ||
err = fmt.Errorf("invalid method") | ||
return | ||
} | ||
|
||
// Convert the arguments, if any... | ||
callArgs := make([]reflect.Value, len(args)) | ||
methodType := method.Type() | ||
numIn := methodType.NumIn() | ||
for i, arg := range args { | ||
var argType reflect.Type | ||
if i < numIn { | ||
argType = methodType.In(i) | ||
} else if methodType.IsVariadic() { | ||
argType = methodType.In(i - 1) | ||
} else { | ||
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()) | ||
} | ||
|
||
// Ready to catch an error if the method panics... | ||
defer func() { | ||
if r := recover(); r != nil { | ||
if err == nil { | ||
var ok bool | ||
if err, ok = r.(error); !ok { | ||
err = fmt.Errorf("%v", r) | ||
} | ||
} else { | ||
// This is not expected - so we panic! | ||
panic(r) | ||
} | ||
} | ||
}() | ||
|
||
result := method.Call(callArgs) | ||
switch len(result) { | ||
case 0: | ||
retval = reflect.ValueOf(nil) | ||
case 1: | ||
retval = result[0] | ||
default: | ||
err = fmt.Errorf("too many return values: %v", result) | ||
} | ||
return | ||
} |
Oops, something went wrong.