1
0
mirror of https://github.com/quinton-ashley/java2js synced 2024-12-29 10:11:54 +01:00
This commit is contained in:
Quinton Ashley 2022-02-18 18:35:09 -05:00
parent ba69a3c8b0
commit 8de0c299bb
5 changed files with 51 additions and 47 deletions

View File

@ -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 ## 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 ## 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. 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 - 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. 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 ## 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 - 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 - 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 ## Contribute

7
ide.js
View File

@ -3,8 +3,11 @@
await jdk.init(); await jdk.init();
let file0 = document.getElementById('javaFile'); let file0 = document.getElementById('javaFile');
file0.onchange = () => { file0.onchange = async () => {
jdk.log.value = ''; jdk.log.value = '';
jdk.run(file0.value); let translation = await jdk.translate(file0.value);
console.log(translation);
jdk.load(translation);
jdk.run();
}; };
})(); })();

52
jdk.js
View File

@ -48,7 +48,7 @@
constructor() {} constructor() {}
async init(root) { async init(root) {
this.root = root || 'https://unpkg.com/java2js'; this.root = root || './';
this.java = {}; this.java = {};
let pkgs = ['com', 'io', 'lang', 'org', 'security', 'time', 'util']; let pkgs = ['com', 'io', 'lang', 'org', 'security', 'time', 'util'];
for (let pkg of pkgs) { for (let pkg of pkgs) {
@ -98,17 +98,6 @@
this.main = () => {}; 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) { run(jvmArgs) {
this.main(jvmArgs); this.main(jvmArgs);
} }
@ -148,9 +137,9 @@
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const script = document.createElement('script'); const script = document.createElement('script');
document.body.appendChild(script); document.body.appendChild(script);
script.async = true;
script.onload = resolve; script.onload = resolve;
script.onerror = reject; script.onerror = reject;
script.async = true;
script.src = src; script.src = src;
}); });
}; };
@ -172,11 +161,14 @@
// bodge fix to avoid getting the index of a commented out class // bodge fix to avoid getting the index of a commented out class
// (only works with normal comments) // (only works with normal comments)
let classLine = file.indexOf('\npublic class'); 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); let imports = file.slice(0, classLine);
imports = imports.match(/(?<=^import )[^;]*/gm) || []; imports = imports.match(/(?<=^import )[^;]*/gm) || [];
let userName = window?.QuintOS?.userName || 'quinton-ashley';
let className = file.slice(classLine + 13, file.indexOf(' {', classLine + 13)); let className = file.slice(classLine + 13, file.indexOf(' {', classLine + 13));
// workaround hack for converting triple quotes to a normal string // workaround hack for converting triple quotes to a normal string
@ -229,7 +221,6 @@
// cast to int, truncates the number (just removes decimal value) // 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.floor');
file = file.replace(/\(int\)\s*\-/gm, 'Math.ceil');
let trans = await java_to_javascript(file); let trans = await java_to_javascript(file);
@ -269,21 +260,9 @@
} }
load(trans) { load(trans) {
return new Promise(async (resolve, reject) => { const script = document.createElement('script');
const script = document.createElement('script'); script.innerHTML = trans;
script.async = false; document.body.appendChild(script);
script.onload = function () {
// log('loaded: ' + src);
resolve();
};
script.onerror = (e) => {
reject(e);
};
script.innerHTML = trans;
document.body.appendChild(script);
});
} }
} }
window.jdk = new JDK(); window.jdk = new JDK();
@ -19131,9 +19110,13 @@
let asyncMethods = []; let asyncMethods = [];
if (typeof QuintOS != 'undefined') { if (typeof QuintOS != 'undefined') {
// asyncMethods = {
// Sprite: ['move']
// };
asyncMethods = [ asyncMethods = [
'alert', 'alert',
'delay', 'delay',
'erase',
'eraseRect', 'eraseRect',
'frame', 'frame',
'move', 'move',
@ -19212,7 +19195,7 @@
return `${parseExpr(expr.leftOperand)} ${op} ${parseExpr(expr.rightOperand)}`; return `${parseExpr(expr.leftOperand)} ${op} ${parseExpr(expr.rightOperand)}`;
case 'MethodInvocation': case 'MethodInvocation':
let str = ''; let str = '';
if (asyncMethods.includes(expr.name.identifier)) { if (asyncMethods.includes(expr.name.identifier) && !expr.isInConstructor) {
str += 'await '; str += 'await ';
} }
if (expr.expression) { if (expr.expression) {
@ -19354,10 +19337,13 @@
const statements = []; const statements = [];
for (const stat of block.statements) { for (const stat of block.statements) {
if (method && stat.node == 'ExpressionStatement') {
stat.expression.isInConstructor = method.constructor;
}
const str = parseStatement(stat); const str = parseStatement(stat);
const arr = Array.isArray(str) ? str : [str]; const arr = Array.isArray(str) ? str : [str];
statements.push(...arr.map(semicolon)); 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); asyncMethods.push(method.name.identifier);
method.isAsync = true; method.isAsync = true;
} }

View File

@ -43,10 +43,10 @@ jdk.imports['java.io.PrintStream'].load = async () => {
} }
if (ln) str += '\n'; if (ln) str += '\n';
this.log += str; this.log += str;
if (window?.ide) { if (jdk.log) {
ide.log.value += str; jdk.log.value += str;
this._onPrint(str.length);
} }
this._onPrint(str.length);
return str; return str;
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "java2js", "name": "java2js",
"version": "1.1.8", "version": "1.1.9",
"description": "Converts Java to JavaScript with support for p5.js and QuintOS.", "description": "Converts Java to JavaScript with support for p5.js and QuintOS.",
"main": "jdk.js", "main": "jdk.js",
"scripts": { "scripts": {