Skip to content

Commit

Permalink
feat(go): Runtime method and static invoke support (#2145)
Browse files Browse the repository at this point in the history
* feat(go): Runtime method and static invoke support

Adds support to the go runtime for class method and static method
invocation. Adds codegen to jsii-pacmak to call the new runtime
functions where appropriate.

Co-authored-by: Hsing-Hui Hsu <[email protected]>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored Oct 20, 2020
1 parent b037efd commit ff882c1
Show file tree
Hide file tree
Showing 7 changed files with 2,298 additions and 1,589 deletions.
35 changes: 22 additions & 13 deletions packages/@jsii/go-runtime/jsii-experimental/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ func (r kernelResponder) isResponse() {
return
}

type objref struct {
JsiiInstanceId string `json:"$jsii.byref"`
}

type LoadRequest struct {
kernelRequester

Expand Down Expand Up @@ -168,28 +172,28 @@ type SetResponse struct {
kernelResponder
}

type StaticInvokeRequest struct {
type staticInvokeRequest struct {
kernelRequester

Api string `json:"api"`
Fqn *FQN `json:"fqn"`
Method *string `json:"method"`
Args []Any `json:"args"`
Api string `json:"api"`
Fqn FQN `json:"fqn"`
Method string `json:"method"`
Args []interface{} `json:"args"`
}

type InvokeRequest struct {
type invokeRequest struct {
kernelRequester

Api string `json:"api"`
Method *string `json:"method"`
Args []Any `json:"args"`
// Objref ObjRef
Api string `json:"api"`
Method string `json:"method"`
Args []interface{} `json:"args"`
Objref objref `json:"objref"`
}

type InvokeResponse struct {
type invokeResponse struct {
kernelResponder

Result Any `json:"result"`
Result interface{} `json:"result"`
}

type BeginRequest struct {
Expand Down Expand Up @@ -283,7 +287,7 @@ type InitOkResponse struct {
type Callback struct {
Cbid *string `json:"cbid"`
Cookie *string `json:"cookie"`
Invoke InvokeRequest `json:"invoke"`
Invoke invokeRequest `json:"invoke"`
Get GetRequest `json:"get"`
Set SetRequest `json:"set"`
}
Expand All @@ -309,6 +313,11 @@ func (r *createResponse) UnmarshalJSON(data []byte) error {
return unmarshalKernelResponse(data, (*response)(r))
}

func (r *invokeResponse) UnmarshalJSON(data []byte) error {
type response invokeResponse
return unmarshalKernelResponse(data, (*response)(r))
}

// Custom unmarshaling for kernel responses, checks for presence of `error` key on json and returns if present
func unmarshalKernelResponse(data []byte, resstruct interface{}) error {
datacopy := make([]byte, len(data))
Expand Down
10 changes: 10 additions & 0 deletions packages/@jsii/go-runtime/jsii-experimental/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,16 @@ func (c *client) create(request createRequest) (createResponse, error) {
return response, c.request(request, &response)
}

func (c *client) invoke(request invokeRequest) (invokeResponse, error) {
response := invokeResponse{}
return response, c.request(request, &response)
}

func (c *client) sinvoke(request staticInvokeRequest) (invokeResponse, error) {
response := invokeResponse{}
return response, c.request(request, &response)
}

func (c *client) close() {
if c.process != nil {
c.stdin.Close()
Expand Down
44 changes: 42 additions & 2 deletions packages/@jsii/go-runtime/jsii-experimental/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,48 @@ func Create(fqn FQN, args []interface{}, interfaces []FQN, overrides []Override,
panic(err)
}

// client.objects[res.JsiiInstanceId] = &returns
client.objects[&returns] = res.JsiiInstanceId
client.objects[returns] = res.JsiiInstanceId
}

// Invoke will call a method on a jsii class instance. The response should be
// decoded into the expected return type for the method being called.
func Invoke(obj interface{}, method string, args []interface{}, returns interface{}) {
client := getClient()

// Find reference to class instance in client
refid, found := client.findObjectRef(obj)

if !found {
panic("No Object Found")
}

_, err := client.invoke(invokeRequest{
Api: "invoke",
Method: method,
Args: args,
Objref: objref{
JsiiInstanceId: refid,
},
})

if err != nil {
panic(err)
}
}

func InvokeStatic(fqn FQN, method string, args []interface{}, returns interface{}) {
client := getClient()

_, err := client.sinvoke(staticInvokeRequest{
Api: "sinvoke",
Fqn: fqn,
Method: method,
Args: args,
})

if err != nil {
panic(err)
}
}

// Close finalizes the runtime process, signalling the end of the execution to
Expand Down
6 changes: 5 additions & 1 deletion packages/jsii-pacmak/lib/targets/go/runtime/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,12 @@ export const JSII_INIT_ALIAS = '_init_';

// Function to make create request
export const JSII_CREATE_FUNC = `${JSII_RT_ALIAS}.Create`;
// JSII invoke request
export const JSII_INVOKE_FUNC = `${JSII_RT_ALIAS}.Invoke`;
// JSII static invoke
export const JSII_SINVOKE_FUNC = `${JSII_RT_ALIAS}.InvokeStatic`;

// MISC Types
// MISC types & functions
// Jsii override struct type
export const JSII_OVERRIDE = `${JSII_RT_ALIAS}.Override`;
// Jsii Any mock
Expand Down
73 changes: 49 additions & 24 deletions packages/jsii-pacmak/lib/targets/go/runtime/method-call.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,61 @@
import { CodeMaker } from 'codemaker';

import { ClassMethod, Struct } from '../types';
import { JSII_INVOKE_FUNC, JSII_SINVOKE_FUNC } from './constants';
import { emitInitialization } from './util';

// NOOP type returns
const NOOP_RETURN_MAP: { [type: string]: string } = {
float64: '0.0',
string: '"NOOP_RETURN_STRING"',
bool: 'true',
};

function paramsString(params: string[]): string {
return `[]string{${params.reduce((accum: string, p: string, i: number) => {
const prefix = i === 0 ? '' : ' ';
return `${accum}${prefix}"${p}",`;
}, '')}}`;
}

export class MethodCall {
public constructor(
public readonly parent: ClassMethod,
private readonly inStatic: boolean,
) {}
public constructor(public readonly parent: ClassMethod) {}

public emit(code: CodeMaker) {
if (this.inStatic) {
emitInitialization(code);
this.emitStatic(code);
} else {
this.emitDynamic(code);
}
}

private emitDynamic(code: CodeMaker) {
code.line(`returns := ""`);
code.open(`${JSII_INVOKE_FUNC}(`);

code.line(`${this.parent.instanceArg},`);
code.line(`"${this.parent.method.name}",`);
code.line(`${this.argsString},`);
code.line(`&returns,`);

code.close(`)`);

this.emitReturnStatement(code);
}

private emitStatic(code: CodeMaker) {
emitInitialization(code);
code.line(`returns := ""`);
code.open(`${JSII_SINVOKE_FUNC}(`);

code.line(`"${this.parent.parent.fqn}",`);
code.line(`"${this.parent.method.name}",`);
code.line(`${this.argsString},`);
code.line(`&returns,`);

const name = code.toPascalCase(this.parent.name);
code.open(`_jsii_.NoOpRequest(_jsii_.NoOpApiRequest {`);
code.line(`Class: "${this.parent.parent.name}",`);
code.line(`Method: "${name}",`);
code.line(
`Args: ${paramsString(
this.parent.method.parameters.map((p) => p.type.toString()),
)},`,
);
code.close(`})`);
code.close(`)`);

this.emitReturnStatement(code);
}

private getDummyReturn(type: string): string {
return NOOP_RETURN_MAP[type] || 'nil';
}

protected emitReturnStatement(code: CodeMaker) {
const ret = this.parent.reference;
if (ret?.void) {
// don't emit a return statement if function doesn't return a value
Expand All @@ -51,7 +69,14 @@ export class MethodCall {
}
}

private getDummyReturn(type: string): string {
return NOOP_RETURN_MAP[type] || 'nil';
private get inStatic(): boolean {
return this.parent.method.static;
}

private get argsString(): string {
const argsList = this.parent.parameters
.map((param) => param.name)
.join(', ');
return `[]interface{}{${argsList}}`;
}
}
9 changes: 6 additions & 3 deletions packages/jsii-pacmak/lib/targets/go/types/class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,17 +142,16 @@ export class ClassMethod extends GoMethod {
public readonly method: Method,
) {
super(parent, method);
this.runtimeCall = new MethodCall(this, this.method.static);
this.runtimeCall = new MethodCall(this);
}

/* emit generates method implementation on the class */
public emit({ code }: EmitContext) {
const name = this.name;
const instanceArg = this.parent.name.substring(0, 1).toLowerCase();
const returnTypeString = this.reference?.void ? '' : ` ${this.returnType}`;

code.openBlock(
`func (${instanceArg} *${
`func (${this.instanceArg} *${
this.parent.name
}) ${name}(${this.paramString()})${returnTypeString}`,
);
Expand All @@ -175,6 +174,10 @@ export class ClassMethod extends GoMethod {
this.reference?.scopedName(this.parent.pkg) ?? this.method.toString()
);
}

public get instanceArg(): string {
return this.parent.name.substring(0, 1).toLowerCase();
}
}

export class StaticMethod extends ClassMethod {
Expand Down
Loading

0 comments on commit ff882c1

Please sign in to comment.