-
Notifications
You must be signed in to change notification settings - Fork 83
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
Add JS and Wasm for 2.0.x version #197
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,6 @@ | ||
import { compressToBase64 } from 'lz-string'; | ||
|
||
import { isKeyOfObject } from '../utils/types'; | ||
import { TargetPlatforms, TargetPlatformsKeys } from '../utils/platforms'; | ||
import { getTargetById, TargetPlatformsKeys } from '../utils/platforms'; | ||
|
||
import { | ||
escapeRegExp, | ||
|
@@ -34,11 +33,8 @@ export function generateCrosslink(code: string, options?: LinkOptions) { | |
|
||
if (options && options.targetPlatform) { | ||
const target = | ||
options.targetPlatform && options.targetPlatform.toUpperCase(); | ||
|
||
if (!isKeyOfObject(target, TargetPlatforms)) | ||
throw new Error('Invalid target platform'); | ||
|
||
options.targetPlatform && getTargetById(options.targetPlatform); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's real bug - it's impossible to pass |
||
if (!target) throw new Error('Invalid target platform'); | ||
opts.targetPlatform = options.targetPlatform; | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -50,27 +50,26 @@ export default class WebDemoApi { | |
* @returns {*|PromiseLike<T>|Promise<T>} | ||
*/ | ||
static translateKotlinToJs(code, compilerVersion, platform, args, hiddenDependencies) { | ||
const MINIMAL_MINOR_VERSION_IR = 5 | ||
const MINIMAL_MINOR_VERSION_WASM = 9 | ||
const minor = parseInt(compilerVersion.split(".")[1]); | ||
const MINIMAL_VERSION_IR = '1.5.0'; | ||
const MINIMAL_VERSION_WASM = '1.9.0'; | ||
|
||
if (platform === TargetPlatforms.JS_IR && minor < MINIMAL_MINOR_VERSION_IR) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Main issue |
||
if (platform === TargetPlatforms.JS_IR && compilerVersion < MINIMAL_VERSION_IR) { | ||
return Promise.resolve({ | ||
output: "", | ||
errors: [{ | ||
severity: "ERROR", | ||
message: `JS IR compiler backend accessible only since 1.${MINIMAL_MINOR_VERSION_IR}.0 version` | ||
message: `JS IR compiler backend accessible only since ${MINIMAL_VERSION_IR} version` | ||
}], | ||
jsCode: "" | ||
}) | ||
} | ||
|
||
if (platform === TargetPlatforms.WASM && minor < MINIMAL_MINOR_VERSION_WASM) { | ||
if (platform === TargetPlatforms.WASM && compilerVersion < MINIMAL_VERSION_WASM) { | ||
return Promise.resolve({ | ||
output: "", | ||
errors: [{ | ||
severity: "ERROR", | ||
message: `Wasm compiler backend accessible only since 1.${MINIMAL_MINOR_VERSION_WASM}.0 version` | ||
message: `Wasm compiler backend accessible only since ${MINIMAL_VERSION_WASM} version` | ||
}], | ||
jsCode: "" | ||
}) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
import { expect, Page, test } from '@playwright/test'; | ||
|
||
import { readFileSync } from 'fs'; | ||
import { join } from 'path'; | ||
|
||
import { gotoHtmlWidget } from './utlis/server/playground'; | ||
|
||
import { RESULT_SELECTOR, WIDGET_SELECTOR } from './utlis/selectors'; | ||
|
||
import { prepareNetwork, printlnCode } from './utlis'; | ||
import { mockRunRequest, waitRunRequest } from './utlis/mocks/compiler'; | ||
import { runButton } from './utlis/interactions'; | ||
import { makeJSPrintCode } from './utlis/mocks/result'; | ||
|
||
const OUTPUTS = Object.freeze({ | ||
'js-ir': { | ||
jsCode: makeJSPrintCode('Hello, world!'), | ||
errors: { 'File.kt': [] }, | ||
exception: null, | ||
text: '<outStream>Hello, world!\n</outStream>', | ||
}, | ||
wasm: JSON.parse( | ||
readFileSync(join(__dirname, 'utlis/mocks/wasm.json'), 'utf-8'), | ||
), | ||
}); | ||
|
||
const VERSIONS = [ | ||
{ version: '1.3.10' }, | ||
{ version: '1.9.20', latestStable: true }, | ||
{ version: '2.0.1' }, | ||
] as const; | ||
|
||
test.describe('platform restrictions', () => { | ||
test.beforeEach(async ({ page, baseURL }) => { | ||
await prepareNetwork(page, baseURL, { | ||
versions: (route) => | ||
route.fulfill({ | ||
body: JSON.stringify(VERSIONS), | ||
}), | ||
}); // offline mode | ||
}); | ||
|
||
test('JS_IR for unsupported version', async ({ page }) => { | ||
await shouldFailedRun( | ||
page, | ||
'js-ir', | ||
'1.3.10', | ||
'JS IR compiler backend accessible only since 1.5.0 version', | ||
); | ||
}); | ||
|
||
test('JS_IR for supported by minor version', async ({ page }) => { | ||
await shouldSuccessRun(page, 'js-ir', '1.9.0'); | ||
}); | ||
|
||
test('JS_IR for supported by major version', async ({ page }) => { | ||
await shouldSuccessRun(page, 'js-ir', '2.0.1'); | ||
}); | ||
|
||
test('WASM for unsupported version', async ({ page }) => { | ||
await shouldFailedRun( | ||
page, | ||
'wasm', | ||
'1.3.10', | ||
'Wasm compiler backend accessible only since 1.9.0 version', | ||
); | ||
}); | ||
|
||
test('WASM for supported by minor version', async ({ page, browserName }) => { | ||
test.skip( | ||
browserName !== 'chromium', | ||
"WASM doesn't supported in this browser", | ||
); | ||
await shouldSuccessRun(page, 'wasm', '1.9.0'); | ||
}); | ||
|
||
test('WASM for supported by major version', async ({ page, browserName }) => { | ||
test.skip( | ||
browserName !== 'chromium', | ||
"WASM doesn't supported in this browser", | ||
); | ||
await shouldSuccessRun(page, 'wasm', '2.0.1'); | ||
}); | ||
}); | ||
|
||
async function shouldSuccessRun( | ||
page: Page, | ||
platform: keyof typeof OUTPUTS, | ||
version: string, | ||
) { | ||
await gotoHtmlWidget( | ||
page, | ||
{ selector: 'code', version: version }, | ||
/* language=html */ ` | ||
<code data-target-platform='${platform}'>${printlnCode( | ||
'Hello, world!', | ||
)}</code> | ||
`, | ||
); | ||
|
||
const resolveRun = await mockRunRequest(page); | ||
|
||
const editor = page.locator(WIDGET_SELECTOR); | ||
|
||
await Promise.all([waitRunRequest(page), runButton(editor)]); | ||
|
||
resolveRun({ | ||
json: Object.freeze(OUTPUTS[platform]), | ||
}); | ||
|
||
// playground loaded | ||
await expect(editor.locator(RESULT_SELECTOR)).toBeVisible(); | ||
await expect(editor.locator(RESULT_SELECTOR)).toContainText('Hello, world!'); | ||
} | ||
|
||
async function shouldFailedRun( | ||
page: Page, | ||
platform: string, | ||
version: string, | ||
text: string, | ||
) { | ||
await gotoHtmlWidget( | ||
page, | ||
{ selector: 'code', version: version }, | ||
/* language=html */ ` | ||
<code data-target-platform='${platform}'>${printlnCode( | ||
'Hello, world!', | ||
)}</code> | ||
`, | ||
); | ||
|
||
const editor = page.locator(WIDGET_SELECTOR); | ||
await runButton(editor); | ||
|
||
await expect(editor.locator(RESULT_SELECTOR)).toBeVisible(); | ||
await expect( | ||
editor.locator(RESULT_SELECTOR).locator('.test-fail'), | ||
).toContainText(text); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,7 +30,9 @@ function isRunRequest(url: URL | string) { | |
|
||
return ( | ||
uri.host === API_HOST && | ||
uri.pathname.match(/^\/?\/api\/\d+\.\d+\.\d+\/compiler\/run$/) !== null | ||
(uri.pathname.match(/^\/?\/api\/\d+\.\d+\.\d+\/compiler\/run$/) !== null || | ||
uri.pathname.match(/^\/?\/api\/\d+\.\d+\.\d+\/compiler\/translate$/) !== | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Catch for both types of targets. |
||
null) | ||
); | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export function makeJSPrintCode(text: string) { | ||
return `var moduleId = function (_) {\n 'use strict';\n //region block: pre-declaration\n setMetadataFor(Unit, 'Unit', objectMeta);\n setMetadataFor(BaseOutput, 'BaseOutput', classMeta);\n setMetadataFor(NodeJsOutput, 'NodeJsOutput', classMeta, BaseOutput);\n setMetadataFor(BufferedOutput, 'BufferedOutput', classMeta, BaseOutput, VOID, BufferedOutput);\n setMetadataFor(BufferedOutputToConsoleLog, 'BufferedOutputToConsoleLog', classMeta, BufferedOutput, VOID, BufferedOutputToConsoleLog);\n //endregion\n function Unit() {\n }\n protoOf(Unit).toString = function () {\n return 'kotlin.Unit';\n };\n var Unit_instance;\n function Unit_getInstance() {\n return Unit_instance;\n }\n function get_output() {\n _init_properties_console_kt__rfg7jv();\n return output;\n }\n var output;\n function BaseOutput() {\n }\n function NodeJsOutput(outputStream) {\n BaseOutput.call(this);\n this.outputStream_1 = outputStream;\n }\n protoOf(NodeJsOutput).print_o1pwgy_k$ = function (message) {\n // Inline function 'kotlin.io.String' call\n var messageString = String(message);\n this.outputStream_1.write(messageString);\n };\n function BufferedOutputToConsoleLog() {\n BufferedOutput.call(this);\n }\n protoOf(BufferedOutputToConsoleLog).print_o1pwgy_k$ = function (message) {\n // Inline function 'kotlin.io.String' call\n var s = String(message);\n // Inline function 'kotlin.text.nativeLastIndexOf' call\n // Inline function 'kotlin.js.asDynamic' call\n var i = s.lastIndexOf('\\n', 0);\n if (i >= 0) {\n var tmp = this;\n var tmp_0 = this.buffer_1;\n // Inline function 'kotlin.text.substring' call\n // Inline function 'kotlin.js.asDynamic' call\n tmp.buffer_1 = tmp_0 + s.substring(0, i);\n this.flush_shahbo_k$();\n // Inline function 'kotlin.text.substring' call\n var this_0 = s;\n var startIndex = i + 1 | 0;\n // Inline function 'kotlin.js.asDynamic' call\n s = this_0.substring(startIndex);\n }\n this.buffer_1 = this.buffer_1 + s;\n };\n protoOf(BufferedOutputToConsoleLog).flush_shahbo_k$ = function () {\n console.log(this.buffer_1);\n this.buffer_1 = '';\n };\n function BufferedOutput() {\n BaseOutput.call(this);\n this.buffer_1 = '';\n }\n protoOf(BufferedOutput).print_o1pwgy_k$ = function (message) {\n var tmp = this;\n var tmp_0 = this.buffer_1;\n // Inline function 'kotlin.io.String' call\n tmp.buffer_1 = tmp_0 + String(message);\n };\n function print(message) {\n _init_properties_console_kt__rfg7jv();\n get_output().print_o1pwgy_k$(message);\n }\n var properties_initialized_console_kt_gll9dl;\n function _init_properties_console_kt__rfg7jv() {\n if (!properties_initialized_console_kt_gll9dl) {\n properties_initialized_console_kt_gll9dl = true;\n // Inline function 'kotlin.run' call\n // Inline function 'kotlin.contracts.contract' call\n // Inline function 'kotlin.io.output.<anonymous>' call\n var isNode = typeof process !== 'undefined' && process.versions && !!process.versions.node;\n output = isNode ? new NodeJsOutput(process.stdout) : new BufferedOutputToConsoleLog();\n }\n }\n function implement(interfaces) {\n var maxSize = 1;\n var masks = [];\n var inductionVariable = 0;\n var last = interfaces.length;\n while (inductionVariable < last) {\n var i = interfaces[inductionVariable];\n inductionVariable = inductionVariable + 1 | 0;\n var currentSize = maxSize;\n var tmp1_elvis_lhs = i.prototype.$imask$;\n var imask = tmp1_elvis_lhs == null ? i.$imask$ : tmp1_elvis_lhs;\n if (!(imask == null)) {\n masks.push(imask);\n currentSize = imask.length;\n }\n var iid = i.$metadata$.iid;\n var tmp;\n if (iid == null) {\n tmp = null;\n } else {\n // Inline function 'kotlin.let' call\n // Inline function 'kotlin.contracts.contract' call\n // Inline function 'kotlin.js.implement.<anonymous>' call\n tmp = bitMaskWith(iid);\n }\n var iidImask = tmp;\n if (!(iidImask == null)) {\n masks.push(iidImask);\n currentSize = Math.max(currentSize, iidImask.length);\n }\n if (currentSize > maxSize) {\n maxSize = currentSize;\n }\n }\n return compositeBitMask(maxSize, masks);\n }\n function bitMaskWith(activeBit) {\n var numberIndex = activeBit >> 5;\n var intArray = new Int32Array(numberIndex + 1 | 0);\n var positionInNumber = activeBit & 31;\n var numberWithSettledBit = 1 << positionInNumber;\n intArray[numberIndex] = intArray[numberIndex] | numberWithSettledBit;\n return intArray;\n }\n function compositeBitMask(capacity, masks) {\n var tmp = 0;\n var tmp_0 = new Int32Array(capacity);\n while (tmp < capacity) {\n var tmp_1 = tmp;\n var result = 0;\n var inductionVariable = 0;\n var last = masks.length;\n while (inductionVariable < last) {\n var mask = masks[inductionVariable];\n inductionVariable = inductionVariable + 1 | 0;\n if (tmp_1 < mask.length) {\n result = result | mask[tmp_1];\n }\n }\n tmp_0[tmp_1] = result;\n tmp = tmp + 1 | 0;\n }\n return tmp_0;\n }\n function protoOf(constructor) {\n return constructor.prototype;\n }\n function defineProp(obj, name, getter, setter) {\n return Object.defineProperty(obj, name, {configurable: true, get: getter, set: setter});\n }\n function objectCreate(proto) {\n return Object.create(proto);\n }\n function classMeta(name, defaultConstructor, associatedObjectKey, associatedObjects, suspendArity) {\n return createMetadata('class', name, defaultConstructor, associatedObjectKey, associatedObjects, suspendArity, null);\n }\n function createMetadata(kind, name, defaultConstructor, associatedObjectKey, associatedObjects, suspendArity, iid) {\n var undef = VOID;\n return {kind: kind, simpleName: name, associatedObjectKey: associatedObjectKey, associatedObjects: associatedObjects, suspendArity: suspendArity, $kClass$: undef, defaultConstructor: defaultConstructor, iid: iid};\n }\n function setMetadataFor(ctor, name, metadataConstructor, parent, interfaces, defaultConstructor, associatedObjectKey, associatedObjects, suspendArity) {\n if (!(parent == null)) {\n ctor.prototype = Object.create(parent.prototype);\n ctor.prototype.constructor = ctor;\n }\n var metadata = metadataConstructor(name, defaultConstructor, associatedObjectKey, associatedObjects, suspendArity == null ? [] : suspendArity);\n ctor.$metadata$ = metadata;\n if (!(interfaces == null)) {\n var receiver = !(metadata.iid == null) ? ctor : ctor.prototype;\n receiver.$imask$ = implement(interfaces);\n }\n }\n function objectMeta(name, defaultConstructor, associatedObjectKey, associatedObjects, suspendArity) {\n return createMetadata('object', name, defaultConstructor, associatedObjectKey, associatedObjects, suspendArity, null);\n }\n function get_VOID() {\n _init_properties_void_kt__3zg9as();\n return VOID;\n }\n var VOID;\n var properties_initialized_void_kt_e4ret2;\n function _init_properties_void_kt__3zg9as() {\n if (!properties_initialized_void_kt_e4ret2) {\n properties_initialized_void_kt_e4ret2 = true;\n VOID = void 0;\n }\n }\n function main() {\n print('${text}');\n }\n //region block: init\n Unit_instance = new Unit();\n //endregion\nif (typeof get_output !== "undefined") {\n get_output();\n output = new BufferedOutput();\n _.output = get_output();\n}\n main();\n return _;\n}(typeof moduleId === 'undefined' ? {} : moduleId);\nmoduleId.output?.buffer_1;\n\n` as const; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's required for Wasm test in chrome with enabled WasmGC.