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
@ -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

7
ide.js
View File

@ -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();
};
})();

52
jdk.js
View File

@ -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;
}

View File

@ -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;
}

View File

@ -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": {