-
Notifications
You must be signed in to change notification settings - Fork 246
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(runtime): make kernel 'load' operation synchronous (#951)
The `jsii-runtime` process cannot perform async operations while waiting for a (synchronous) callback to return (the response flow is otherwise ambiguous). In order to allow loading of dependencies within a calllback flow, the `load` kernel API needed to be made syncrhonous. The reason why this is needed is because the host languages runtime libraries may be required to load dependencies at the last minute, for it may not be possible to determine what dependencies are required before a callback is executed. Introduced a (unit) test to confirm the `jsii-runtime` is indeed able to perform `load` operations within a callback context.
- Loading branch information
1 parent
526509c
commit 896d688
Showing
12 changed files
with
318 additions
and
88 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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,3 +8,5 @@ webpack | |
node_modules/ | ||
.nyc_output/ | ||
coverage/ | ||
|
||
test/_tarballs/ |
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 |
---|---|---|
|
@@ -9,3 +9,5 @@ coverage | |
.eslintrc.* | ||
tsconfig.json | ||
*.tsbuildinfo | ||
|
||
test/_tarballs/ |
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,2 @@ | ||
// this module doesn't export any symbols | ||
|
||
export * from './host'; | ||
export * from './in-out'; |
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
66 changes: 66 additions & 0 deletions
66
packages/jsii-runtime/test/__snapshots__/kernel-host.test.js.snap
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,66 @@ | ||
// Jest Snapshot v1, https://goo.gl/fbAQLP | ||
|
||
exports[`can load libraries from within a callback 1`] = ` | ||
Object { | ||
"ok": Object { | ||
"assembly": "@scope/jsii-calc-base", | ||
"types": "*redacted*", | ||
}, | ||
} | ||
`; | ||
|
||
exports[`can load libraries from within a callback 2`] = ` | ||
Object { | ||
"ok": Object { | ||
"assembly": "@scope/jsii-calc-lib", | ||
"types": "*redacted*", | ||
}, | ||
} | ||
`; | ||
|
||
exports[`can load libraries from within a callback 3`] = ` | ||
Object { | ||
"ok": Object { | ||
"$jsii.byref": "Object@10000", | ||
"$jsii.interfaces": Array [ | ||
"@scope/jsii-calc-lib.IFriendly", | ||
], | ||
}, | ||
} | ||
`; | ||
|
||
exports[`can load libraries from within a callback 4`] = ` | ||
Object { | ||
"callback": Object { | ||
"cbid": "jsii::callback::20000", | ||
"cookie": undefined, | ||
"invoke": Object { | ||
"args": Array [], | ||
"method": "hello", | ||
"objref": Object { | ||
"$jsii.byref": "Object@10000", | ||
"$jsii.interfaces": Array [ | ||
"@scope/jsii-calc-lib.IFriendly", | ||
], | ||
}, | ||
}, | ||
}, | ||
} | ||
`; | ||
|
||
exports[`can load libraries from within a callback 5`] = ` | ||
Object { | ||
"ok": Object { | ||
"assembly": "jsii-calc", | ||
"types": "*redacted*", | ||
}, | ||
} | ||
`; | ||
|
||
exports[`can load libraries from within a callback 6`] = ` | ||
Object { | ||
"ok": Object { | ||
"result": "SUCCESS!", | ||
}, | ||
} | ||
`; |
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,87 @@ | ||
import child = require('child_process'); | ||
import fs = require('fs'); | ||
import { api } from 'jsii-kernel'; | ||
import spec = require('jsii-spec'); | ||
import path = require('path'); | ||
import { KernelHost, InputOutput, Input, Output } from '../lib'; | ||
|
||
test('can load libraries from within a callback', () => { | ||
const inout = new TestInputOutput( | ||
[ | ||
{ api: 'load', ...loadRequest('@scope/jsii-calc-base') }, | ||
{ api: 'load', ...loadRequest('@scope/jsii-calc-lib') }, | ||
{ api: 'create', fqn: 'Object', interfaces: ['@scope/jsii-calc-lib.IFriendly'], overrides: [{ method: 'hello' }] }, | ||
{ api: 'invoke', objref: { [api.TOKEN_REF]: 'Object@10000' }, method: 'hello' }, | ||
{ api: 'load', ...loadRequest('jsii-calc') }, | ||
{ complete: { cbid: 'jsii::callback::20000', result: 'SUCCESS!' } }, | ||
] | ||
); | ||
const host = new KernelHost(inout, { noStack: true, debug: false }); | ||
return new Promise<void>(ok => { | ||
host.on('exit', () => ok(inout.expectCompleted())); | ||
host.run(); | ||
}); | ||
}); | ||
|
||
class TestInputOutput extends InputOutput { | ||
private readonly inputCommands: Input[]; | ||
|
||
public constructor(inputCommands: Input[], private readonly allowErrors = false) { | ||
super(); | ||
this.inputCommands = inputCommands.reverse(); | ||
} | ||
|
||
public read(): Input | undefined { | ||
return this.inputCommands.pop(); | ||
} | ||
|
||
public write(obj: Output): void { | ||
if (!this.allowErrors) { | ||
expect(obj).not.toHaveProperty('error'); | ||
} | ||
if ('ok' in obj && 'assembly' in obj.ok) { | ||
// Removing the type count as this is subject to change! | ||
(obj.ok as any).types = '*redacted*'; | ||
} | ||
expect(obj).toMatchSnapshot(); | ||
} | ||
|
||
/** | ||
* Validates that all inputs have been consumed, and all expected outputs have been checked. | ||
*/ | ||
public expectCompleted(): void { | ||
expect(this.inputCommands).toEqual([]); | ||
} | ||
} | ||
|
||
function loadRequest(library: string): api.LoadRequest { | ||
const assembly = loadAssembly(); | ||
const tarball = path.join(__dirname, '_tarballs', library, `${assembly.fingerprint.replace('/', '_')}.tgz`); | ||
if (!fs.existsSync(tarball)) { | ||
packageLibrary(tarball); | ||
} | ||
return { | ||
name: assembly.name, | ||
version: assembly.version, | ||
tarball, | ||
}; | ||
|
||
function loadAssembly(): spec.Assembly { | ||
const assemblyFile = path.resolve(require.resolve(`${library}/package.json`), '..', '.jsii'); | ||
return JSON.parse(fs.readFileSync(assemblyFile, { encoding: 'utf-8' })); | ||
} | ||
|
||
function packageLibrary(target: string): void { | ||
const targetDir = path.dirname(target); | ||
fs.mkdirSync(targetDir, { recursive: true }); | ||
const result = child.spawnSync('npm', ['pack', path.dirname(require.resolve(`${library}/package.json`))], { cwd: targetDir, stdio: ['inherit', 'pipe', 'pipe'] }); | ||
if (result.error) { | ||
throw result.error; | ||
} | ||
if (result.status !== 0) { | ||
console.error(result.stderr.toString('utf-8')); | ||
throw new Error(`Unable to 'npm pack' ${library}: process ${result.signal != null ? `killed by ${result.signal}` : `exited with code ${result.status}`}`); | ||
} | ||
fs.renameSync(path.join(targetDir, result.stdout.toString('utf-8').trim()), target); | ||
} | ||
} |
Oops, something went wrong.