diff --git a/README.md b/README.md index b822d5e..821f980 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,10 @@ -# Java to Javascript +# Java to JavaScript (java2js) -I've built on top of the [java-to-javascript](https://github.com/wyattades/java-to-javascript) transpiler by @wyattades and got inspiration from the barebones JDK (Java Development Kit) implementation in [java2javascript](https://github.com/java2script/java2script) by @BobHanson, @zhourenjian, @abego, and others. +`java2js` can translate simple Java programs to JavaScript and runs them using a JavaScript based JDK. -I created this project so intro level computer science students could write Java code but still use my [QuintOS](https://github.com/quinton-ashley/quintos) retro game engine library, which is web based. Yes, I went to all this trouble just so my students don't have to run their programs in a boring Java console! To achieve this I've been working on a JDK implementation in modern Javascript. +I made significant improvements to the [java-to-javascript](https://github.com/wyattades/java-to-javascript) transpiler by @wyattades. I also did a modern JS implementation of the fundamental classes in the JDK (Java Development Kit), inspired by [java2javascript](https://github.com/java2script/java2script) by @BobHanson, @zhourenjian, @abego, and others. + +I created this project so intro level computer science students could write Java code but still use my [QuintOS](https://github.com/quinton-ashley/quintos) retro game engine library, which is web based. ## Java Classes Included in the JS JDK @@ -37,13 +39,13 @@ I created this project so intro level computer science students could write Java ## API -### jdk.init(root) +### await jdk.init(root) -- root (optional) can be a local path or url to the parent folder of the JS `jdk` folder, by default it links to 'https://unpkg.com/java2js' (this package) so you don't need to change it +- root (optional) path to the parent folder of the JS `jdk` folder, by default it is `./` you could also link to this package online 'https://unpkg.com/java2js' This function imports much of the standard Java lang classes into the global scope. You must use it before translating or running files. -### jdk.translate(javaFile) +### await jdk.translate(javaFile) - javaFile is a string with contents of a Java file @@ -59,9 +61,22 @@ Loads the JS class file but doesn't run the main method. Runs the main method with optional JVM arguments. +## Features + +- imports +- static classes +- static methods +- array literals +- arrays with initial size +- two dimensional arrays +- lambda arrow functions +- triple quotes + ## Known limitations -- casting to int truncation workaround requires parenthesis around the number being cast `int x = (int) (Math.random() * 100);` +- not very good error reporting + +- casting to int (truncation) requires parenthesis around the number being cast `int x = (int) (Math.random() * 100);` - no support for method overloading, though a workaround might be possible by making a function with the og name route to each of the variations of the overloaded function @@ -71,7 +86,7 @@ Runs the main method with optional JVM arguments. - no third level static classes -- not much error checking +- no method can be named `.length()` because it will be replaced with `.length` for getting the length of Strings in JS ## Contribute diff --git a/ide.js b/ide.js index deb2cb1..f987293 100644 --- a/ide.js +++ b/ide.js @@ -3,8 +3,11 @@ await jdk.init(); let file0 = document.getElementById('javaFile'); - file0.onchange = () => { + file0.onchange = async () => { jdk.log.value = ''; - jdk.run(file0.value); + let translation = await jdk.translate(file0.value); + console.log(translation); + jdk.load(translation); + jdk.run(); }; })(); diff --git a/jdk.js b/jdk.js index 155083d..56a2f2a 100755 --- a/jdk.js +++ b/jdk.js @@ -48,7 +48,7 @@ constructor() {} async init(root) { - this.root = root || 'https://unpkg.com/java2js'; + this.root = root || './'; this.java = {}; let pkgs = ['com', 'io', 'lang', 'org', 'security', 'time', 'util']; for (let pkg of pkgs) { @@ -98,17 +98,6 @@ this.main = () => {}; } - // async load() { - // // load all imported classes - // for (let className in this.imports) { - // let imp = this.imports[className]; - // imp.classPath ??= className.split('.'); - // await imp.load(); - // } - - // this.launch(); - // } - run(jvmArgs) { this.main(jvmArgs); } @@ -148,9 +137,9 @@ return new Promise((resolve, reject) => { const script = document.createElement('script'); document.body.appendChild(script); + script.async = true; script.onload = resolve; script.onerror = reject; - script.async = true; script.src = src; }); }; @@ -172,11 +161,14 @@ // bodge fix to avoid getting the index of a commented out class // (only works with normal comments) let classLine = file.indexOf('\npublic class'); - if (classLine < 0) classLine = file.indexOf('public class'); + if (classLine < 0) { + classLine = file.indexOf('public class'); + } else { + classLine += 1; + } let imports = file.slice(0, classLine); imports = imports.match(/(?<=^import )[^;]*/gm) || []; - let userName = window?.QuintOS?.userName || 'quinton-ashley'; let className = file.slice(classLine + 13, file.indexOf(' {', classLine + 13)); // workaround hack for converting triple quotes to a normal string @@ -229,7 +221,6 @@ // cast to int, truncates the number (just removes decimal value) file = file.replace(/\(int\)\s*/gm, 'Math.floor'); - file = file.replace(/\(int\)\s*\-/gm, 'Math.ceil'); let trans = await java_to_javascript(file); @@ -269,21 +260,9 @@ } load(trans) { - return new Promise(async (resolve, reject) => { - const script = document.createElement('script'); - script.async = false; - script.onload = function () { - // log('loaded: ' + src); - resolve(); - }; - script.onerror = (e) => { - reject(e); - }; - - script.innerHTML = trans; - - document.body.appendChild(script); - }); + const script = document.createElement('script'); + script.innerHTML = trans; + document.body.appendChild(script); } } window.jdk = new JDK(); @@ -19131,9 +19110,13 @@ let asyncMethods = []; if (typeof QuintOS != 'undefined') { + // asyncMethods = { + // Sprite: ['move'] + // }; asyncMethods = [ 'alert', 'delay', + 'erase', 'eraseRect', 'frame', 'move', @@ -19212,7 +19195,7 @@ return `${parseExpr(expr.leftOperand)} ${op} ${parseExpr(expr.rightOperand)}`; case 'MethodInvocation': let str = ''; - if (asyncMethods.includes(expr.name.identifier)) { + if (asyncMethods.includes(expr.name.identifier) && !expr.isInConstructor) { str += 'await '; } if (expr.expression) { @@ -19354,10 +19337,13 @@ const statements = []; for (const stat of block.statements) { + if (method && stat.node == 'ExpressionStatement') { + stat.expression.isInConstructor = method.constructor; + } const str = parseStatement(stat); const arr = Array.isArray(str) ? str : [str]; statements.push(...arr.map(semicolon)); - if (method && !method.isAsync && statements.join('').includes('await')) { + if (method && !method.isAsync && !method.constructor && statements.join('').includes('await')) { asyncMethods.push(method.name.identifier); method.isAsync = true; } diff --git a/jdk/java/io/PrintStream.js b/jdk/java/io/PrintStream.js index 9e5803e..bd4e176 100755 --- a/jdk/java/io/PrintStream.js +++ b/jdk/java/io/PrintStream.js @@ -43,10 +43,10 @@ jdk.imports['java.io.PrintStream'].load = async () => { } if (ln) str += '\n'; this.log += str; - if (window?.ide) { - ide.log.value += str; - this._onPrint(str.length); + if (jdk.log) { + jdk.log.value += str; } + this._onPrint(str.length); return str; } diff --git a/package.json b/package.json index 1678512..712c103 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "java2js", - "version": "1.1.8", + "version": "1.1.9", "description": "Converts Java to JavaScript with support for p5.js and QuintOS.", "main": "jdk.js", "scripts": {