mirror of
https://github.com/quinton-ashley/java2js
synced 2024-12-29 10:11:54 +01:00
1.1.10
This commit is contained in:
parent
8de0c299bb
commit
7e855616c8
60
README.md
60
README.md
@ -2,11 +2,9 @@
|
||||
|
||||
`java2js` can translate simple Java programs to JavaScript and runs them using a JavaScript based JDK.
|
||||
|
||||
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.
|
||||
Try out the `demo.html` and `ide.js`, a barebones implementation.
|
||||
|
||||
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 java2js JDK
|
||||
|
||||
| java.io | | |
|
||||
| ------- | ----------- | ----------- |
|
||||
@ -37,32 +35,10 @@ I created this project so intro level computer science students could write Java
|
||||
| LinkedList | Random | Scanner |
|
||||
| Set | Stack | |
|
||||
|
||||
## API
|
||||
|
||||
### await jdk.init(root)
|
||||
|
||||
- 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.
|
||||
|
||||
### await jdk.translate(javaFile)
|
||||
|
||||
- javaFile is a string with contents of a Java file
|
||||
|
||||
returns the Java file translated into JavaScript
|
||||
|
||||
### jdk.load(translatedJSFile)
|
||||
|
||||
- translatedJSFile is the translated JS class to run
|
||||
|
||||
Loads the JS class file but doesn't run the main method.
|
||||
|
||||
### jdk.run(jvmArgs)
|
||||
|
||||
Runs the main method with optional JVM arguments.
|
||||
|
||||
## Features
|
||||
|
||||
The java2js transpiler supports:
|
||||
|
||||
- imports
|
||||
- static classes
|
||||
- static methods
|
||||
@ -72,6 +48,26 @@ Runs the main method with optional JVM arguments.
|
||||
- lambda arrow functions
|
||||
- triple quotes
|
||||
|
||||
## API
|
||||
|
||||
### await jdk.init(root)
|
||||
|
||||
This function imports the standard java.lang classes into the global scope. You must use it before translating or running files.
|
||||
|
||||
- root (optional) path to the parent folder of the JS `jdk` folder, by default it is `.` (the current directory) you could also link to this package online 'https://unpkg.com/java2js'
|
||||
|
||||
### await jdk.transpile(javaFile)
|
||||
|
||||
Translates a Java class and loads it.
|
||||
|
||||
- javaFile is a String with a Java class in it
|
||||
|
||||
returns a String with the JavaScript transpilation of the Java class
|
||||
|
||||
### jdk.run(jvmArgs)
|
||||
|
||||
Runs the main method with optional JVM arguments.
|
||||
|
||||
## Known limitations
|
||||
|
||||
- not very good error reporting
|
||||
@ -88,6 +84,14 @@ Runs the main method with optional JVM arguments.
|
||||
|
||||
- no method can be named `.length()` because it will be replaced with `.length` for getting the length of Strings in JS
|
||||
|
||||
## Why did you make this?
|
||||
|
||||
I created this package 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.
|
||||
|
||||
## Credits
|
||||
|
||||
This project builds upon 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), which I got the inspiration to do from [java2javascript](https://github.com/java2script/java2script) by @BobHanson, @zhourenjian, @abego, and others.
|
||||
|
||||
## Contribute
|
||||
|
||||
I've only done a partial implementation of the Java 17 JDK in JavaScript, so if you're interested in adding more please go for it and submit a pull request!
|
||||
|
27
demo.html
27
demo.html
@ -17,20 +17,35 @@
|
||||
}
|
||||
|
||||
#javaFile {
|
||||
width: 47vw;
|
||||
height: 90vh;
|
||||
width: 48vw;
|
||||
height: 97vh;
|
||||
}
|
||||
|
||||
#javaConsole {
|
||||
width: 47vw;
|
||||
height: 90vh;
|
||||
width: 48vw;
|
||||
height: 97vh;
|
||||
float: right;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<textarea id="javaFile" spellcheck="false" autocomplete="off" autocorrect="off" autocapitalize="off"></textarea>
|
||||
<textarea id="javaConsole" spellcheck="false" autocomplete="off" autocorrect="off" autocapitalize="off"></textarea>
|
||||
<textarea
|
||||
id="javaFile"
|
||||
spellcheck="false"
|
||||
autocomplete="off"
|
||||
autocorrect="off"
|
||||
autocapitalize="off"
|
||||
placeholder="Create a Java class with a main method here..."
|
||||
></textarea>
|
||||
<textarea
|
||||
id="javaConsole"
|
||||
spellcheck="false"
|
||||
autocomplete="off"
|
||||
autocorrect="off"
|
||||
autocapitalize="off"
|
||||
placeholder="Java console, click here to run!"
|
||||
></textarea>
|
||||
|
||||
<script type="text/javascript" src="jdk.js"></script>
|
||||
<script type="text/javascript" src="ide.js"></script>
|
||||
|
3
ide.js
3
ide.js
@ -5,9 +5,8 @@
|
||||
let file0 = document.getElementById('javaFile');
|
||||
file0.onchange = async () => {
|
||||
jdk.log.value = '';
|
||||
let translation = await jdk.translate(file0.value);
|
||||
let translation = await jdk.transpile(file0.value);
|
||||
console.log(translation);
|
||||
jdk.load(translation);
|
||||
jdk.run();
|
||||
};
|
||||
})();
|
||||
|
79
jdk.js
79
jdk.js
@ -48,7 +48,7 @@
|
||||
constructor() {}
|
||||
|
||||
async init(root) {
|
||||
this.root = root || './';
|
||||
this.root = root || '.';
|
||||
this.java = {};
|
||||
let pkgs = ['com', 'io', 'lang', 'org', 'security', 'time', 'util'];
|
||||
for (let pkg of pkgs) {
|
||||
@ -94,8 +94,10 @@
|
||||
System.in.reset();
|
||||
System.out.reset();
|
||||
|
||||
// stub main for now
|
||||
this.main = () => {};
|
||||
// stub main
|
||||
this.main = () => {
|
||||
console.error('No main method found in loaded classes!');
|
||||
};
|
||||
}
|
||||
|
||||
run(jvmArgs) {
|
||||
@ -152,7 +154,7 @@
|
||||
return classes;
|
||||
}
|
||||
|
||||
async translate(file) {
|
||||
async transpile(file) {
|
||||
if (!this.java) {
|
||||
console.error(
|
||||
'java2js error: JDK not initialized! You must call `await jdk.init()` before using `jdk.load()`'
|
||||
@ -216,12 +218,11 @@
|
||||
return 'new Runnable("' + in0 + '")';
|
||||
});
|
||||
|
||||
// convert string .length() method
|
||||
file = file.replace(/\.length\(\)/gm, '.length');
|
||||
|
||||
// cast to int, truncates the number (just removes decimal value)
|
||||
file = file.replace(/\(int\)\s*/gm, 'Math.floor');
|
||||
|
||||
let packageName = (file.match(/package\s+([^;]+)/gm) || [])[1] || 'default';
|
||||
|
||||
let trans = await java_to_javascript(file);
|
||||
|
||||
// log(trans);
|
||||
@ -233,7 +234,7 @@
|
||||
|
||||
trans = trans.replace(/(\([^\(\)]*\) =>)/gm, 'async $1');
|
||||
|
||||
let prefix = `((jdk.imports['games_java.${className}'] = {}).load = async () => {\n\n`;
|
||||
let prefix = `(jdk.imports['${packageName}.${className}'] = {}).load = async () => {\n\n`;
|
||||
|
||||
// handle Java class imports
|
||||
for (let i = 0; i < imports.length; i++) {
|
||||
@ -252,17 +253,22 @@
|
||||
|
||||
let suffix = '\n';
|
||||
suffix += `window.${className} = ${className};\n`;
|
||||
suffix += '})();';
|
||||
suffix += '};';
|
||||
|
||||
trans = prefix + trans + suffix;
|
||||
|
||||
return trans;
|
||||
}
|
||||
|
||||
load(trans) {
|
||||
const script = document.createElement('script');
|
||||
script.innerHTML = trans;
|
||||
document.body.appendChild(script);
|
||||
|
||||
try {
|
||||
await jdk.imports[`${packageName}.${className}`].load();
|
||||
} catch (ror) {
|
||||
console.error('Failed to load class!\n' + trans);
|
||||
return;
|
||||
}
|
||||
|
||||
return trans;
|
||||
}
|
||||
}
|
||||
window.jdk = new JDK();
|
||||
@ -19073,8 +19079,11 @@
|
||||
|
||||
const joinStatements = (stats) => `${stats.join(';')}${stats.length ? ';' : ''}`;
|
||||
|
||||
let variableTypes = {};
|
||||
|
||||
const varToString = ({ name, value, type, final }, noLet) => {
|
||||
if (value === undefined) value = literalInitializers[type] || 'null';
|
||||
variableTypes[name] = type;
|
||||
return `${noLet !== true ? (final ? 'const ' : 'let ') : ''}${name} = ${value}`;
|
||||
};
|
||||
|
||||
@ -19108,27 +19117,16 @@
|
||||
};
|
||||
const classVarsMap = {};
|
||||
|
||||
let asyncMethods = [];
|
||||
let asyncMethods = {
|
||||
Scanner: ['next', 'nextLine', 'nextInt', 'nextShort', 'nextLong', 'nextFloat', 'nextDouble']
|
||||
};
|
||||
if (typeof QuintOS != 'undefined') {
|
||||
// asyncMethods = {
|
||||
// Sprite: ['move']
|
||||
// };
|
||||
asyncMethods = [
|
||||
'alert',
|
||||
'delay',
|
||||
'erase',
|
||||
'eraseRect',
|
||||
'frame',
|
||||
'move',
|
||||
'play',
|
||||
'print',
|
||||
'println',
|
||||
'printf',
|
||||
'prompt',
|
||||
'text',
|
||||
'textRect'
|
||||
];
|
||||
Object.assign(asyncMethods, {
|
||||
Sprite: ['move'],
|
||||
window: ['alert', 'delay', 'erase', 'eraseRect', 'frame', 'play', 'prompt', 'text', 'textRect']
|
||||
});
|
||||
}
|
||||
if (!asyncMethods.window) asyncMethods.window = [];
|
||||
|
||||
const assignParent = (name, isInConstructor) => {
|
||||
if (name in classVarsMap) {
|
||||
@ -19195,12 +19193,19 @@
|
||||
return `${parseExpr(expr.leftOperand)} ${op} ${parseExpr(expr.rightOperand)}`;
|
||||
case 'MethodInvocation':
|
||||
let str = '';
|
||||
if (asyncMethods.includes(expr.name.identifier) && !expr.isInConstructor) {
|
||||
str += 'await ';
|
||||
}
|
||||
if (expr.expression) {
|
||||
let exp = expr.expression;
|
||||
if (exp.expression) exp = exp.expression;
|
||||
let arr = asyncMethods[variableTypes[exp.identifier]];
|
||||
if (arr && arr.length && arr.includes(expr.name.identifier) && !expr.isInConstructor) {
|
||||
str += 'await ';
|
||||
}
|
||||
str += `${parseExpr(expr.expression)}.${expr.name.identifier}`;
|
||||
if (expr.name.identifier == 'length') return str;
|
||||
} else {
|
||||
if (asyncMethods.window.includes(expr.name.identifier) && !expr.isInConstructor) {
|
||||
str += 'await ';
|
||||
}
|
||||
str += `${assignParent(expr.name.identifier)}`;
|
||||
}
|
||||
return str + `(${expr.arguments.map(parseExpr)})`;
|
||||
@ -19259,9 +19264,9 @@
|
||||
data
|
||||
)
|
||||
);
|
||||
variableTypes[frag.name.identifier] = data.type;
|
||||
} else unhandledNode(frag);
|
||||
}
|
||||
|
||||
return vars;
|
||||
};
|
||||
|
||||
@ -19344,7 +19349,7 @@
|
||||
const arr = Array.isArray(str) ? str : [str];
|
||||
statements.push(...arr.map(semicolon));
|
||||
if (method && !method.isAsync && !method.constructor && statements.join('').includes('await')) {
|
||||
asyncMethods.push(method.name.identifier);
|
||||
asyncMethods.window.push(method.name.identifier);
|
||||
method.isAsync = true;
|
||||
}
|
||||
}
|
||||
|
@ -3,9 +3,9 @@ jdk.imports['java.io.InputStream'].load = async () => {
|
||||
constructor() {
|
||||
this.reset();
|
||||
let _this = this;
|
||||
if (window?.ide) {
|
||||
ide.log.onkeyup = () => {
|
||||
_this.stream = ide.log.value;
|
||||
if (jdk.log) {
|
||||
jdk.log.onkeyup = () => {
|
||||
_this.stream = jdk.log.value;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "java2js",
|
||||
"version": "1.1.9",
|
||||
"version": "1.1.10",
|
||||
"description": "Converts Java to JavaScript with support for p5.js and QuintOS.",
|
||||
"main": "jdk.js",
|
||||
"scripts": {
|
||||
|
Loading…
x
Reference in New Issue
Block a user