Skip to content

Ilgonmic/2 module compilation #228

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

Open
wants to merge 9 commits into
base: kconf-platform-release
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@
"copy-examples": "node utils/copy-examples",
"release:ci": "rm -rf dist && npm run build:all && $NPM_TOKEN=%env.NPM_TOKEN% npm publish",
"start": "webpack-dev-server --port 9002",
"start-with-local-compiler": "webpack-dev-server --port 9002 --env webDemoUrl='//localhost:8080'",
"start-with-local-compiler": "webpack-dev-server --port 9002 --env webDemoUrl='//localhost:8080' webDemoResourcesUrl='//localhost:8081'",
"lint": "eslint . --ext .ts",
"fix": "eslint --fix --ext .ts .",
"test": "npm run build:all && npm run test:run",
Expand Down
22 changes: 17 additions & 5 deletions src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ export const RUNTIME_CONFIG = { ...getConfigFromElement(currentScript) };
export const API_URLS = {
server: (RUNTIME_CONFIG.server || __WEBDEMO_URL__).replace(/\/$/, ''),
composeServer: (
'https://compose.sandbox.intellij.net' || __WEBDEMO_URL__
__WEBDEMO_URL__ || 'https://compose.sandbox.intellij.net'
).replace(/\/$/, ''),
composeResources: (
__WEBDEMO_RESOURCES_URL__ || 'https://compose.sandbox.intellij.net'
).replace(/\/$/, ''),

COMPILE(platform, version) {
Expand Down Expand Up @@ -62,11 +65,20 @@ export const API_URLS = {
get VERSIONS() {
return `${this.server}/versions`;
},
SKIKO_MJS() {
return `${this.composeServer}/api/resource/skiko.mjs`;
RESOURCE_VERSIONS() {
return `${this.composeServer}/api/resource/compose-wasm-versions`;
},
SKIKO_MJS(version) {
return `${this.composeResources}/api/resource/skiko-${version}.mjs`;
},
SKIKO_WASM(version) {
return `${this.composeResources}/api/resource/skiko-${version}.wasm`;
},
STDLIB_MJS(hash) {
return `${this.composeResources}/api/resource/stdlib-${hash}.mjs`;
},
SKIKO_WASM() {
return `${this.composeServer}/api/resource/skiko.wasm`;
STDLIB_WASM(hash) {
return `${this.composeResources}/api/resource/stdlib-${hash}.wasm`;
},
get JQUERY() {
return `https://cdn.jsdelivr.net/npm/jquery@1/dist/jquery.min.js`;
Expand Down
7 changes: 4 additions & 3 deletions src/executable-code/executable-fragment.js
Original file line number Diff line number Diff line change
Expand Up @@ -413,8 +413,8 @@ export default class ExecutableFragment extends ExecutableCodeTemplate {
);
const additionalRequests = [];
if (targetPlatform === TargetPlatforms.COMPOSE_WASM) {
if (!this.jsExecutor.skikoImport) {
additionalRequests.push(this.jsExecutor.skikoImport);
if (this.jsExecutor.stdlibExports) {
additionalRequests.push(this.jsExecutor.stdlibExports);
}
}

Expand All @@ -428,7 +428,7 @@ export default class ExecutableFragment extends ExecutableCodeTemplate {
),
...additionalRequests,
]).then(
([state]) => {
([state, ...additionalRequestsResults]) => {
state.waitingForOutput = false;
const jsCode = state.jsCode;
const wasm = state.wasm;
Expand All @@ -453,6 +453,7 @@ export default class ExecutableFragment extends ExecutableCodeTemplate {
outputHeight,
theme,
onError,
additionalRequestsResults,
)
.then((output) => {
const originState = state.openConsole;
Expand Down
13 changes: 6 additions & 7 deletions src/js-executor/execute-es-module.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@ export async function executeWasmCode(container, jsCode, wasmCode) {
return execute(container, newCode, wasmCode);
}

export async function executeWasmCodeWithSkiko(container, jsCode, wasmCode) {
const newCode = prepareJsCode(jsCode)
.replaceAll(
"imports['./skiko.mjs']",
"window.skikoImports"
);
return execute(container, newCode, wasmCode);
export async function executeWasmCodeWithSkiko(container, jsCode) {
return executeJs(container, prepareJsCode(jsCode));
}

export async function executeWasmCodeWithStdlib(container, jsCode, wasmCode) {
return execute(container, prepareJsCode(jsCode), wasmCode);
}

function execute(container, jsCode, wasmCode) {
Expand Down
152 changes: 116 additions & 36 deletions src/js-executor/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import './index.scss';
import { API_URLS } from '../config';
import { showJsException } from '../view/output-view';
import { processingHtmlBrackets } from '../utils';
import { isWasmRelated, TargetPlatforms } from '../utils/platforms';
import { executeJs, executeWasmCode, executeWasmCodeWithSkiko } from './execute-es-module';
import { fetch } from "whatwg-fetch";
import {API_URLS} from '../config';
import {showJsException} from '../view/output-view';
import {processingHtmlBrackets} from '../utils';
import {isWasmRelated, TargetPlatforms} from '../utils/platforms';
import {executeJs, executeWasmCode, executeWasmCodeWithSkiko, executeWasmCodeWithStdlib} from './execute-es-module';
import {fetch} from "whatwg-fetch";

const INIT_SCRIPT =
'if(kotlin.BufferedOutput!==undefined){kotlin.out = new kotlin.BufferedOutput()}' +
Expand All @@ -26,7 +26,7 @@ const normalizeJsVersion = (version) => {
export default class JsExecutor {
constructor(kotlinVersion) {
this.kotlinVersion = kotlinVersion;
this.skikoImport = undefined;
this.stdlibExports = undefined;
}

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

const additionalRequestsResult = additionalRequestsResults[0];
const result = await this.executeWasm(
jsCode,
wasm,
executeWasmCodeWithSkiko,
executeWasmCodeWithStdlib,
theme,
processError,
additionalRequestsResult.stdlib,
additionalRequestsResult.output,
);

if (exception) {
Expand Down Expand Up @@ -106,8 +110,8 @@ export default class JsExecutor {
const output = this.iframe.contentWindow.eval(jsCode);
return output
? `<span class="standard-output ${theme}">${processingHtmlBrackets(
output,
)}</span>`
output,
)}</span>`
: '';
} catch (e) {
if (onError) onError();
Expand All @@ -119,20 +123,21 @@ export default class JsExecutor {
return await this.execute(jsCode, jsLibs, theme, onError, platform);
}

async executeWasm(jsCode, wasmCode, executor, theme, onError) {
async executeWasm(jsCode, wasmCode, executor, theme, onError, imports, output) {
try {
const exports = await executor(
this.iframe.contentWindow,
jsCode,
wasmCode,
);
await exports.instantiate();
const output = exports.bufferedOutput.buffer;
exports.bufferedOutput.buffer = '';
return output
await exports.instantiate({"playground.master": imports});
const bufferedOutput = output ?? exports.bufferedOutput;
const outputString = bufferedOutput.buffer;
bufferedOutput.buffer = '';
return outputString
? `<span class="standard-output ${theme}">${processingHtmlBrackets(
output,
)}</span>`
outputString,
)}</span>`
: '';
} catch (e) {
if (onError) onError();
Expand Down Expand Up @@ -177,27 +182,75 @@ export default class JsExecutor {
}
}
if (targetPlatform === TargetPlatforms.COMPOSE_WASM) {
this.skikoImport = fetch(API_URLS.SKIKO_MJS(compilerVersion), {
method: 'GET',
headers: {
'Content-Type': 'text/javascript',
}
})
.then(script => script.text())
.then(script => script.replace(
"new URL(\"skiko.wasm\",import.meta.url).href",
`'${API_URLS.SKIKO_WASM(compilerVersion)}'`
))
.then(async skikoCode => {
return await executeJs(
this.iframe.contentWindow,
skikoCode,
);

const skikoStdlib = fetch(API_URLS.RESOURCE_VERSIONS(),{
method: 'GET'
}).then(response => response.json())
.then(versions => {
const skikoVersion = versions["skiko"];

const skikoExports = fetch(API_URLS.SKIKO_MJS(skikoVersion), {
method: 'GET',
headers: {
'Content-Type': 'text/javascript',
}
}).then(script => script.text())
.then(script => script.replace(
"new URL(\"skiko.wasm\",import.meta.url).href",
`'${API_URLS.SKIKO_WASM(skikoVersion)}'`
))
.then(skikoCode =>
executeJs(
this.iframe.contentWindow,
skikoCode,
))
.then(skikoExports => fixedSkikoExports(skikoExports))

const stdlibVersion = versions["stdlib"];

const stdlibExports = fetch(API_URLS.STDLIB_MJS(stdlibVersion), {
method: 'GET',
headers: {
'Content-Type': 'text/javascript',
}
}).then(script => script.text())
.then(script =>
// necessary to load stdlib.wasm before its initialization to parallelize
// language=JavaScript
(`const stdlibWasm = fetch('${API_URLS.STDLIB_WASM(stdlibVersion)}');\n` + script).replace(
"fetch(new URL('./stdlib_master.wasm',import.meta.url).href)",
"stdlibWasm"
).replace(
"(extends) => { return { extends }; }",
"(extends_) => { return { extends_ }; }"
))
.then(stdlibCode =>
executeWasmCodeWithSkiko(
this.iframe.contentWindow,
stdlibCode,
)
)

return Promise.all([skikoExports, stdlibExports])
})

this.stdlibExports = skikoStdlib
.then(async ([skikoExportsResult, stdlibExportsResult]) => {
return [
await stdlibExportsResult.instantiate({
"./skiko.mjs": skikoExportsResult
}),
stdlibExportsResult
]
}
)
.then(([stdlibResult, outputResult]) => {
return {
"stdlib": stdlibResult.exports,
"output": outputResult.bufferedOutput
}
}
)
.then(skikoImports => {
this.iframe.contentWindow.skikoImports = skikoImports;
});

this.iframe.height = "1000"
iframeDoc.write(`<canvas height="1000" id="ComposeTarget"></canvas>`);
Expand All @@ -206,3 +259,30 @@ export default class JsExecutor {
iframeDoc.close();
}
}

function fixedSkikoExports(skikoExports) {
return {
...skikoExports,
org_jetbrains_skia_Bitmap__1nGetPixmap: function () {
console.log("org_jetbrains_skia_TextBlobBuilderRunHandler__1nGetFinalizer")
},
org_jetbrains_skia_Bitmap__1nIsVolatile: function () {
console.log("org_jetbrains_skia_TextBlobBuilderRunHandler__1nGetFinalizer")
},
org_jetbrains_skia_Bitmap__1nSetVolatile: function () {
console.log("org_jetbrains_skia_TextBlobBuilderRunHandler__1nGetFinalizer")
},
org_jetbrains_skia_TextBlobBuilderRunHandler__1nGetFinalizer: function () {
console.log("org_jetbrains_skia_TextBlobBuilderRunHandler__1nGetFinalizer")
},
org_jetbrains_skia_TextBlobBuilderRunHandler__1nMake: function () {
console.log("org_jetbrains_skia_TextBlobBuilderRunHandler__1nGetFinalizer")
},
org_jetbrains_skia_TextBlobBuilderRunHandler__1nMakeBlob: function () {
console.log("org_jetbrains_skia_TextBlobBuilderRunHandler__1nGetFinalizer")
},
org_jetbrains_skia_svg_SVGCanvasKt__1nMake: function () {
console.log("org_jetbrains_skia_TextBlobBuilderRunHandler__1nGetFinalizer")
}
}
}
17 changes: 0 additions & 17 deletions src/webdemo-api.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ export default class WebDemoApi {
const MINIMAL_VERSION_IR = '1.5.0';
const MINIMAL_VERSION_WASM = '1.9.0';
const MINIMAL_VERSION_SWIFT_EXPORT = '2.0.0';
const MAX_VERSION_COMPOSE_EXPORT = '2.0.0';

if (
platform === TargetPlatforms.JS_IR &&
Expand Down Expand Up @@ -90,22 +89,6 @@ export default class WebDemoApi {
});
}

if (
platform === TargetPlatforms.COMPOSE_WASM &&
compilerVersion >= MAX_VERSION_COMPOSE_EXPORT
) {
return Promise.resolve({
output: '',
errors: [
{
severity: 'ERROR',
message: `${TargetPlatforms.COMPOSE_WASM.printableName} compiler backend accessible only less ${MAX_VERSION_COMPOSE_EXPORT} version`,
},
],
jsCode: '',
});
}

if (
platform === TargetPlatforms.SWIFT_EXPORT &&
compilerVersion < MINIMAL_VERSION_SWIFT_EXPORT
Expand Down
13 changes: 11 additions & 2 deletions tests/utils/screenshots.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,14 @@ export async function hideCursor(node: Locator, callback: () => Promise<void>) {
await cursor.evaluate((element) => (element.style.display = null));
}

const MAX_DIFF_PIXEL_RATIO = 0.01;

export function checkScreenshot(node: Locator, message: string) {
return hideCursor(node, () => expect(node, message).toHaveScreenshot());
return hideCursor(node, () =>
expect(node, message).toHaveScreenshot({
maxDiffPixelRatio: MAX_DIFF_PIXEL_RATIO,
}),
);
}

export function checkEditorView(editor: Locator, message: string) {
Expand All @@ -34,6 +40,9 @@ export function checkEditorView(editor: Locator, message: string) {
height: boundingBox.height + margins.bottom,
};

await expect(editor.page(), message).toHaveScreenshot({ clip });
await expect(editor.page(), message).toHaveScreenshot({
clip,
maxDiffPixelRatio: MAX_DIFF_PIXEL_RATIO,
});
});
}
2 changes: 2 additions & 0 deletions webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ module.exports = (params = {}) => {
const isServer = process.argv[1].includes('webpack-dev-server');
const libraryName = 'KotlinPlayground';
const webDemoUrl = params.webDemoUrl || 'https://api.kotlinlang.org/';
const webDemoResourcesUrl = params.webDemoResourcesUrl || 'https://api.kotlinlang.org/';
const examplesPath = isServer ? '' : 'examples/';
const pathDist = path.resolve(__dirname, 'dist');

Expand Down Expand Up @@ -98,6 +99,7 @@ module.exports = (params = {}) => {

new webpack.DefinePlugin({
__WEBDEMO_URL__: JSON.stringify(webDemoUrl),
__WEBDEMO_RESOURCES_URL__: JSON.stringify(webDemoResourcesUrl),
__IS_PRODUCTION__: isProduction,
__LIBRARY_NAME__: JSON.stringify(libraryName),
}),
Expand Down