Skip to content

Commit 02f6e5d

Browse files
committed
Add support of 2-module Wasm compilation for Compose.
It works like separate static stdlib bundle, and one small dynamic So we could instantiate stdlib with skiko, and then instantiate and run main bundle with ready stdlib.
1 parent d69d3b2 commit 02f6e5d

File tree

4 files changed

+111
-42
lines changed

4 files changed

+111
-42
lines changed

src/config.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export const RUNTIME_CONFIG = { ...getConfigFromElement(currentScript) };
1313
export const API_URLS = {
1414
server: (RUNTIME_CONFIG.server || __WEBDEMO_URL__).replace(/\/$/, ''),
1515
composeServer: (
16-
'https://compose.sandbox.intellij.net' || __WEBDEMO_URL__
16+
__WEBDEMO_URL__
1717
).replace(/\/$/, ''),
1818

1919
COMPILE(platform, version) {
@@ -63,10 +63,16 @@ export const API_URLS = {
6363
return `${this.server}/versions`;
6464
},
6565
SKIKO_MJS(version) {
66-
return `${this.composeServer}/api/resource/skiko-${version}.mjs`;
66+
return `${this.composeServer}/api/resource/skiko.mjs`;
6767
},
6868
SKIKO_WASM(version) {
69-
return `${this.composeServer}/api/resource/skiko-${version}.wasm`;
69+
return `${this.composeServer}/api/resource/skiko.wasm`;
70+
},
71+
STDLIB_MJS(version) {
72+
return `${this.composeServer}/api/resource/stdlib.mjs`;
73+
},
74+
STDLIB_WASM(version) {
75+
return `${this.composeServer}/api/resource/stdlib.wasm`;
7076
},
7177
get JQUERY() {
7278
return `https://cdn.jsdelivr.net/npm/jquery@1/dist/jquery.min.js`;

src/executable-code/executable-fragment.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -413,8 +413,8 @@ export default class ExecutableFragment extends ExecutableCodeTemplate {
413413
);
414414
const additionalRequests = [];
415415
if (targetPlatform === TargetPlatforms.COMPOSE_WASM) {
416-
if (!this.jsExecutor.skikoImport) {
417-
additionalRequests.push(this.jsExecutor.skikoImport);
416+
if (this.jsExecutor.stdlibExports) {
417+
additionalRequests.push(this.jsExecutor.stdlibExports);
418418
}
419419
}
420420

@@ -428,7 +428,7 @@ export default class ExecutableFragment extends ExecutableCodeTemplate {
428428
),
429429
...additionalRequests,
430430
]).then(
431-
([state]) => {
431+
([state, ...additionalRequestsResults]) => {
432432
state.waitingForOutput = false;
433433
const jsCode = state.jsCode;
434434
const wasm = state.wasm;
@@ -453,6 +453,7 @@ export default class ExecutableFragment extends ExecutableCodeTemplate {
453453
outputHeight,
454454
theme,
455455
onError,
456+
additionalRequestsResults,
456457
)
457458
.then((output) => {
458459
const originState = state.openConsole;

src/js-executor/execute-es-module.js

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,16 @@ export async function executeWasmCode(container, jsCode, wasmCode) {
33
return execute(container, newCode, wasmCode);
44
}
55

6-
export async function executeWasmCodeWithSkiko(container, jsCode, wasmCode) {
7-
const newCode = prepareJsCode(jsCode)
8-
.replaceAll(
9-
"imports['./skiko.mjs']",
10-
"window.skikoImports"
11-
);
12-
return execute(container, newCode, wasmCode);
6+
export async function executeWasmCodeWithSkiko(container, jsCode) {
7+
return executeJs(container, prepareJsCode(jsCode));
8+
}
9+
10+
export async function executeWasmCodeWithStdlib(container, jsCode, wasmCode) {
11+
return execute(container, prepareJsCode(jsCode), wasmCode);
1312
}
1413

1514
function execute(container, jsCode, wasmCode) {
16-
container.wasmCode = Uint8Array.from(atob(wasmCode), c => c.charCodeAt(0));
15+
container.wasmCode = Uint8Array.fromBase64(wasmCode);
1716
return executeJs(container, jsCode);
1817
}
1918

src/js-executor/index.js

Lines changed: 91 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import './index.scss';
2-
import { API_URLS } from '../config';
3-
import { showJsException } from '../view/output-view';
4-
import { processingHtmlBrackets } from '../utils';
5-
import { isWasmRelated, TargetPlatforms } from '../utils/platforms';
6-
import { executeJs, executeWasmCode, executeWasmCodeWithSkiko } from './execute-es-module';
7-
import { fetch } from "whatwg-fetch";
2+
import {API_URLS} from '../config';
3+
import {showJsException} from '../view/output-view';
4+
import {processingHtmlBrackets} from '../utils';
5+
import {isWasmRelated, TargetPlatforms} from '../utils/platforms';
6+
import {executeJs, executeWasmCode, executeWasmCodeWithSkiko, executeWasmCodeWithStdlib} from './execute-es-module';
7+
import {fetch} from "whatwg-fetch";
88

99
const INIT_SCRIPT =
1010
'if(kotlin.BufferedOutput!==undefined){kotlin.out = new kotlin.BufferedOutput()}' +
@@ -26,7 +26,7 @@ const normalizeJsVersion = (version) => {
2626
export default class JsExecutor {
2727
constructor(kotlinVersion) {
2828
this.kotlinVersion = kotlinVersion;
29-
this.skikoImport = undefined;
29+
this.stdlibExports = undefined;
3030
}
3131

3232
async executeJsCode(
@@ -37,6 +37,7 @@ export default class JsExecutor {
3737
outputHeight,
3838
theme,
3939
onError,
40+
additionalRequestsResults,
4041
) {
4142
if (platform === TargetPlatforms.SWIFT_EXPORT) {
4243
return `<span class="standard-output ${theme}"><div class="result-code">${jsCode}</span>`;
@@ -68,12 +69,15 @@ export default class JsExecutor {
6869
// for some reason resize function in Compose does not work in Firefox in invisible block
6970
this.iframe.style.display = 'block';
7071

72+
const additionalRequestsResult = additionalRequestsResults[0];
7173
const result = await this.executeWasm(
7274
jsCode,
7375
wasm,
74-
executeWasmCodeWithSkiko,
76+
executeWasmCodeWithStdlib,
7577
theme,
7678
processError,
79+
additionalRequestsResult.stdlib,
80+
additionalRequestsResult.output,
7781
);
7882

7983
if (exception) {
@@ -106,8 +110,8 @@ export default class JsExecutor {
106110
const output = this.iframe.contentWindow.eval(jsCode);
107111
return output
108112
? `<span class="standard-output ${theme}">${processingHtmlBrackets(
109-
output,
110-
)}</span>`
113+
output,
114+
)}</span>`
111115
: '';
112116
} catch (e) {
113117
if (onError) onError();
@@ -119,20 +123,21 @@ export default class JsExecutor {
119123
return await this.execute(jsCode, jsLibs, theme, onError, platform);
120124
}
121125

122-
async executeWasm(jsCode, wasmCode, executor, theme, onError) {
126+
async executeWasm(jsCode, wasmCode, executor, theme, onError, imports, output) {
123127
try {
124128
const exports = await executor(
125129
this.iframe.contentWindow,
126130
jsCode,
127131
wasmCode,
128132
);
129-
await exports.instantiate();
130-
const output = exports.bufferedOutput.buffer;
131-
exports.bufferedOutput.buffer = '';
132-
return output
133+
await exports.instantiate(imports);
134+
const bufferedOutput = output ?? exports.bufferedOutput;
135+
const outputString = bufferedOutput.buffer;
136+
bufferedOutput.buffer = '';
137+
return outputString
133138
? `<span class="standard-output ${theme}">${processingHtmlBrackets(
134-
output,
135-
)}</span>`
139+
outputString,
140+
)}</span>`
136141
: '';
137142
} catch (e) {
138143
if (onError) onError();
@@ -177,27 +182,58 @@ export default class JsExecutor {
177182
}
178183
}
179184
if (targetPlatform === TargetPlatforms.COMPOSE_WASM) {
180-
this.skikoImport = fetch(API_URLS.SKIKO_MJS(compilerVersion), {
185+
186+
const skikoExports = fetch(API_URLS.SKIKO_MJS(compilerVersion), {
181187
method: 'GET',
182188
headers: {
183189
'Content-Type': 'text/javascript',
184190
}
185-
})
186-
.then(script => script.text())
191+
}).then(script => script.text())
187192
.then(script => script.replace(
188193
"new URL(\"skiko.wasm\",import.meta.url).href",
189194
`'${API_URLS.SKIKO_WASM(compilerVersion)}'`
190195
))
191-
.then(async skikoCode => {
192-
return await executeJs(
193-
this.iframe.contentWindow,
194-
skikoCode,
195-
);
196+
.then(async skikoCode =>
197+
executeJs(
198+
this.iframe.contentWindow,
199+
skikoCode,
200+
))
201+
.then(skikoExports => fixedSkikoExports(skikoExports))
202+
203+
const stdlibExports = fetch(API_URLS.STDLIB_MJS(compilerVersion), {
204+
method: 'GET',
205+
headers: {
206+
'Content-Type': 'text/javascript',
207+
}
208+
}).then(script => script.text())
209+
.then(script => script.replace(
210+
"new URL('./stdlib.wasm',import.meta.url).href",
211+
`'${API_URLS.STDLIB_WASM(compilerVersion)}'`
212+
))
213+
.then(stdlibCode =>
214+
executeWasmCodeWithSkiko(
215+
this.iframe.contentWindow,
216+
stdlibCode,
217+
)
218+
)
219+
220+
this.stdlibExports = Promise.all([skikoExports, stdlibExports])
221+
.then(async ([skikoExportsResult, stdlibExportsResult]) => {
222+
return [
223+
await stdlibExportsResult.stdlib({
224+
"./skiko.mjs": skikoExportsResult
225+
}),
226+
stdlibExportsResult
227+
]
228+
}
229+
)
230+
.then(([stdlibResult, outputResult]) => {
231+
return {
232+
"stdlib": stdlibResult.exports,
233+
"output": outputResult.bufferedOutput
234+
}
196235
}
197236
)
198-
.then(skikoImports => {
199-
this.iframe.contentWindow.skikoImports = skikoImports;
200-
});
201237

202238
this.iframe.height = "1000"
203239
iframeDoc.write(`<canvas height="1000" id="ComposeTarget"></canvas>`);
@@ -206,3 +242,30 @@ export default class JsExecutor {
206242
iframeDoc.close();
207243
}
208244
}
245+
246+
function fixedSkikoExports(skikoExports) {
247+
return {
248+
...skikoExports,
249+
org_jetbrains_skia_Bitmap__1nGetPixmap: function () {
250+
console.log("org_jetbrains_skia_TextBlobBuilderRunHandler__1nGetFinalizer")
251+
},
252+
org_jetbrains_skia_Bitmap__1nIsVolatile: function () {
253+
console.log("org_jetbrains_skia_TextBlobBuilderRunHandler__1nGetFinalizer")
254+
},
255+
org_jetbrains_skia_Bitmap__1nSetVolatile: function () {
256+
console.log("org_jetbrains_skia_TextBlobBuilderRunHandler__1nGetFinalizer")
257+
},
258+
org_jetbrains_skia_TextBlobBuilderRunHandler__1nGetFinalizer: function () {
259+
console.log("org_jetbrains_skia_TextBlobBuilderRunHandler__1nGetFinalizer")
260+
},
261+
org_jetbrains_skia_TextBlobBuilderRunHandler__1nMake: function () {
262+
console.log("org_jetbrains_skia_TextBlobBuilderRunHandler__1nGetFinalizer")
263+
},
264+
org_jetbrains_skia_TextBlobBuilderRunHandler__1nMakeBlob: function () {
265+
console.log("org_jetbrains_skia_TextBlobBuilderRunHandler__1nGetFinalizer")
266+
},
267+
org_jetbrains_skia_svg_SVGCanvasKt__1nMake: function () {
268+
console.log("org_jetbrains_skia_TextBlobBuilderRunHandler__1nGetFinalizer")
269+
}
270+
}
271+
}

0 commit comments

Comments
 (0)