diff --git a/craco.config.js b/craco.config.js index 6c8c6a5d34..9ced7dac49 100644 --- a/craco.config.js +++ b/craco.config.js @@ -18,7 +18,7 @@ const cracoConfig = { plugin => plugin.constructor.name === 'InjectManifest' ); if (injectManifestPlugin) { - injectManifestPlugin.config.maximumFileSizeToCacheInBytes = 17 * 1024 * 1024; + injectManifestPlugin.config.maximumFileSizeToCacheInBytes = 20 * 1024 * 1024; } // add rules to pack WASM (for Sourceror) @@ -47,9 +47,10 @@ const cracoConfig = { 'https': require.resolve('https-browserify'), 'os': require.resolve('os-browserify/browser'), 'path/posix': require.resolve('path-browserify'), + 'process/browser': require.resolve('process/browser'), 'stream': require.resolve('stream-browserify'), 'timers': require.resolve('timers-browserify'), - 'url': require.resolve('url/') + 'url': require.resolve('url/'), }; // workaround .mjs files by Acorn @@ -138,6 +139,7 @@ const cracoConfig = { 'split-on-first', 'filter-obj', '@sourceacademy/c-slang', + 'java-parser' ), '^.+\\.module\\.(css|sass|scss)$' ]; diff --git a/package.json b/package.json index 5d28df86bc..ef9f067938 100644 --- a/package.json +++ b/package.json @@ -51,8 +51,8 @@ "flexboxgrid": "^6.3.1", "flexboxgrid-helpers": "^1.1.3", "hastscript": "^9.0.0", - "java-slang": "^1.0.6", "js-slang": "^1.0.66", + "java-slang": "^1.0.13", "js-yaml": "^4.1.0", "konva": "^9.2.0", "lodash": "^4.17.21", diff --git a/src/commons/utils/JavaHelper.ts b/src/commons/utils/JavaHelper.ts index 21ddc66f51..42eea82175 100644 --- a/src/commons/utils/JavaHelper.ts +++ b/src/commons/utils/JavaHelper.ts @@ -1,3 +1,5 @@ +import { compileFromSource, typeCheck } from 'java-slang'; +import { BinaryWriter } from 'java-slang/dist/compiler/binary-writer'; import setupJVM, { parseBin } from 'java-slang/dist/jvm'; import { createModuleProxy, loadCachedFiles } from 'java-slang/dist/jvm/utils/integration'; import { Context } from 'js-slang'; @@ -9,6 +11,33 @@ import DisplayBufferService from './DisplayBufferService'; export async function javaRun(javaCode: string, context: Context) { let compiled = {}; + const stderr = (type: 'TypeCheck' | 'Compile' | 'Runtime', msg: string) => { + context.errors.push({ + type: type as any, + severity: 'Error' as any, + location: { start: { line: -1, column: -1 }, end: { line: -1, column: -1 } }, + explain: () => msg, + elaborate: () => msg + }); + }; + + const typeCheckResult = typeCheck(javaCode); + if (typeCheckResult.hasTypeErrors) { + const typeErrMsg = typeCheckResult.errorMsgs.join('\n'); + stderr('TypeCheck', typeErrMsg); + return Promise.resolve({ status: 'error' }); + } + + try { + const classFile = compileFromSource(javaCode); + compiled = { + 'Main.class': Buffer.from(new BinaryWriter().generateBinary(classFile)).toString('base64') + }; + } catch (e) { + stderr('Compile', e); + return Promise.resolve({ status: 'error' }); + } + let files = {}; let buffer: string[] = []; @@ -46,6 +75,7 @@ export async function javaRun(javaCode: string, context: Context) { } return parseBin(new DataView(bytes.buffer)); }; + const loadNatives = async (path: string) => { // dynamic load modules if (path.startsWith('modules')) { @@ -56,6 +86,7 @@ export async function javaRun(javaCode: string, context: Context) { } return await import(`java-slang/dist/jvm/stdlib/${path}.js`); }; + const stdout = (str: string) => { if (str.endsWith('\n')) { buffer.push(str); @@ -67,33 +98,6 @@ export async function javaRun(javaCode: string, context: Context) { buffer.push(str); } }; - const stderr = (msg: string) => { - context.errors.push({ - type: 'Runtime' as any, - severity: 'Error' as any, - location: { - start: { - line: -1, - column: -1 - }, - end: { - line: -1, - column: -1 - } - }, - explain: () => msg, - elaborate: () => msg - }); - }; - - // FIXME: Remove when the compiler is working - try { - const json = JSON.parse(javaCode); - compiled = json; - } catch (e) { - stderr(e); - return Promise.resolve({ status: 'error' }); - } // load cached classfiles from IndexedDB return loadCachedFiles(() => @@ -119,12 +123,20 @@ export async function javaRun(javaCode: string, context: Context) { readFileSync: readClassFiles, readFile: loadNatives, stdout, - stderr, + stderr: (msg: string) => stderr('Runtime', msg), onFinish: () => { resolve( context.errors.length ? { status: 'error' } - : { status: 'finished', context, value: '' } + : { + status: 'finished', + context, + value: new (class { + toString() { + return ' '; + } + })() + } ); } }, diff --git a/src/setupTests.ts b/src/setupTests.ts index 325a68de3a..342d11a07b 100644 --- a/src/setupTests.ts +++ b/src/setupTests.ts @@ -8,3 +8,10 @@ jest.mock('./commons/utils/notifications/createNotification', () => ({ show: jest.fn() } })); + +jest.mock('java-slang', () => { + return { + compileFromSource: () => '', + typeCheck: () => ({ hasTypeErrors: false, errorMsgs: [] }) + }; +}); diff --git a/yarn.lock b/yarn.lock index caff454791..021ca1a104 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8024,10 +8024,10 @@ java-parser@^2.0.5: chevrotain-allstar "0.3.1" lodash "4.17.21" -java-slang@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/java-slang/-/java-slang-1.0.6.tgz#34a68954a8dfb5bde8572d49a6bea170d7026749" - integrity sha512-CHq2s9xuzuuPfygRp9pQncfPfIuzpHivXn3wfy4QBiQ801IvdVFL3w/nV6qQn6GUG+kvOU+qCuxUFsFH8X5Dhg== +java-slang@^1.0.13: + version "1.0.13" + resolved "https://registry.yarnpkg.com/java-slang/-/java-slang-1.0.13.tgz#601454c9dd28a41ea6918dab51a7e65401d2c2d9" + integrity sha512-xBh84Gcp7iyc3o9bWDbaIa7GXf75tpUVmDrd/gXIAU/gxNARitJdl5xCjQW5y4iesqwucV7cY+Ewx6MIt4xKeQ== dependencies: "@types/lodash" "^4.14.198" java-parser "^2.0.5"