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 2021-09-23 23:26:42 -05:00
parent e9c4dd302e
commit e2ed130148
38 changed files with 150 additions and 148 deletions

View File

@ -1,11 +1,17 @@
# Java to Javascript for QuintOS # Java to Javascript for QuintOS
I've built on top of the "Java to Javascript" transpiler by @wyattades and got inspiration from the JRE implementation in "java2javascript" by @BobHanson and others. I've built on top of the "Java to Javascript" transpiler by @wyattades and got inspiration from the JavaDevelopmentKit implementation in "java2javascript" by @BobHanson and others.
The purpose of this project was to allow intro level CS students to write Java code but still use my QuintOS library which is web based instead of just having them run their programs in a Java console which is boring. I made a barebones JRE implementation in modern Javascript to acheive this. The purpose of this project was to allow intro level CS students to write Java code but still use my QuintOS library which is web based instead of just having them run their programs in a Java console which is boring. I made a barebones JavaDevelopmentKit implementation in modern Javascript to acheive this.
## Known limitations ## Known limitations
- casting to int requires putting the element being cast in parenthesis - casting to int truncation workaround requires parenthesis around the number being cast
- no support for method overloading - no support for method overloading, though a workaround could be made by making a function with the og name route to each of the variations of the overloaded function
- no support for private/public methods, though this could be done since they are included in modern JavaScript classes
## Contribute
I've only done a barebones implementation of Java 17 JDK, a lot is missing, so if you are interested in adding more please go for it and submit a pull request!

View File

@ -31,7 +31,7 @@
<body> <body>
<textarea id="javaFile" spellcheck="false" autocomplete="off" autocorrect="off" autocapitalize="off"></textarea> <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="javaConsole" spellcheck="false" autocomplete="off" autocorrect="off" autocapitalize="off"></textarea>
<script type="text/javascript" src="jre.js"></script> <script type="text/javascript" src="jdk.js"></script>
<script type="text/javascript" src="ide.js"></script> <script type="text/javascript" src="ide.js"></script>
</body> </body>
</html> </html>

2
ide.js
View File

@ -5,5 +5,5 @@ ide.file0 = document.getElementById('javaFile');
ide.file0.onchange = () => { ide.file0.onchange = () => {
ide.log.value = ''; ide.log.value = '';
let file = ide.file0.value; let file = ide.file0.value;
jre.run(file); jdk.run(file);
}; };

View File

@ -1,3 +1,5 @@
// This is a bundle the JDK class code is the main part that
// I (quinton-ashley) made. That section starts on line 40.
(function () { (function () {
function r(e, n, t) { function r(e, n, t) {
function o(i, f) { function o(i, f) {
@ -38,9 +40,11 @@
function (require, module, exports) { function (require, module, exports) {
const log = console.log; const log = console.log;
/* Java to JavaScript transpiler */
const java_to_javascript = require('java-to-javascript'); const java_to_javascript = require('java-to-javascript');
class JRE { /* JDK implementation in modern JavaScript */
class JDK {
constructor() { constructor() {
this.java = {}; this.java = {};
let pkgs = ['com', 'lang', 'org', 'io', 'util']; let pkgs = ['com', 'lang', 'org', 'io', 'util'];
@ -49,6 +53,12 @@
} }
this.imports = {}; this.imports = {};
/**
* The 'java.lang' package is imported automatically because this package
* provides classes that are fundamental to the Java programming language.
* Since classes like String already exist in JS, so I just extended them,
* no need to import.
*/
try { try {
let names = [ let names = [
'Character', 'Character',
@ -69,12 +79,15 @@
this.import(lang); this.import(lang);
} catch (e) {} } catch (e) {}
// stub main for now
this.main = () => {}; this.main = () => {};
this.load(); this.load();
} }
load() { load() {
let ready = 0; let ready = 0;
// load all imported classes
for (let className in this.imports) { for (let className in this.imports) {
let imp = this.imports[className]; let imp = this.imports[className];
if (imp.classPath) { if (imp.classPath) {
@ -96,6 +109,8 @@
ready++; ready++;
} }
} }
// some classes may load slower than others,
// wait for them all before launching the Java program
if (ready != Object.keys(this.imports).length) { if (ready != Object.keys(this.imports).length) {
let _this = this; let _this = this;
setTimeout(() => { setTimeout(() => {
@ -116,7 +131,7 @@
System.in.reset(); System.in.reset();
System.out.reset(); System.out.reset();
this.main(this.vmArgs); this.main(this.jvmArgs);
} }
getClass(classPath) { getClass(classPath) {
@ -149,7 +164,7 @@
imp.load = null; imp.load = null;
imp.classPath = className.split('.'); imp.classPath = className.split('.');
let src = './jre'; let src = './jdk';
for (let part of imp.classPath) { for (let part of imp.classPath) {
src += '/' + part; src += '/' + part;
} }
@ -190,14 +205,14 @@
let userName = window?.QuintOS?.userName || 'quinton-ashley'; 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));
let prefix = `(jre.imports['com.${userName}.${className}'] = {}).load = () => {\n\n`; let prefix = `(jdk.imports['com.${userName}.${className}'] = {}).load = () => {\n\n`;
// handle Java class imports // handle Java class imports
for (let i = 0; i < imports.length; i++) { for (let i = 0; i < imports.length; i++) {
let imp = imports[i]; let imp = imports[i];
let impPath = imp.split('.'); let impPath = imp.split('.');
let impName = impPath[impPath.length - 1]; let impName = impPath[impPath.length - 1];
prefix += `let ${impName} = jre.import('${imp}');\n`; prefix += `let ${impName} = jdk.import('${imp}');\n`;
} }
prefix += '\n'; prefix += '\n';
@ -219,7 +234,7 @@
// log(file); // log(file);
let suffix = `\njre.main = ${className}.main;\n}`; let suffix = `\njdk.main = ${className}.main;\n}`;
window.file0 = file; window.file0 = file;
@ -242,7 +257,7 @@
} }
} }
} }
window.jre = new JRE(); window.jdk = new JDK();
}, },
{ {
'java-to-javascript': 2 'java-to-javascript': 2

View File

@ -1,5 +1,4 @@
jre.imports['java.io.File'].load = () => { jdk.imports['java.io.File'].load = () => {
class File { class File {
constructor(file) { constructor(file) {
this.absPath = file; this.absPath = file;
@ -10,5 +9,5 @@ jre.imports['java.io.File'].load = () => {
} }
File.seperator = File.seperatorChar = '/'; File.seperator = File.seperatorChar = '/';
File.pathSeparator = File.pathSeparatorChar = '/'; File.pathSeparator = File.pathSeparatorChar = '/';
jre.java.io.File = File; jdk.java.io.File = File;
} };

View File

@ -1,4 +1,4 @@
jre.imports['java.io.InputStream'].load = () => { jdk.imports['java.io.InputStream'].load = () => {
class InputStream { class InputStream {
constructor() { constructor() {
this.reset(); this.reset();
@ -17,5 +17,5 @@ jre.imports['java.io.InputStream'].load = () => {
this.mark += length; this.mark += length;
} }
} }
jre.java.io.InputStream = InputStream; jdk.java.io.InputStream = InputStream;
}; };

View File

@ -1,4 +1,4 @@
jre.imports['java.io.PrintStream'].load = () => { jdk.imports['java.io.PrintStream'].load = () => {
class PrintStream { class PrintStream {
constructor() { constructor() {
this.log = ''; this.log = '';
@ -40,5 +40,5 @@ jre.imports['java.io.PrintStream'].load = () => {
} }
} }
} }
jre.java.io.PrintStream = PrintStream; jdk.java.io.PrintStream = PrintStream;
}; };

View File

@ -1,4 +1,4 @@
jre.imports['java.lang.Character'].load = () => { jdk.imports['java.lang.Character'].load = () => {
class Character {} class Character {}
Character.isDigit = (c) => { Character.isDigit = (c) => {
c = c.charCodeAt(0); c = c.charCodeAt(0);
@ -27,5 +27,5 @@ jre.imports['java.lang.Character'].load = () => {
Character.toString = (c) => { Character.toString = (c) => {
return c; return c;
}; };
jre.java.lang.Character = Character; jdk.java.lang.Character = Character;
}; };

View File

@ -1,19 +1,18 @@
jre.imports['java.lang.Double'].load = () => { jdk.imports['java.lang.Double'].load = () => {
class Double { class Double {
constructor() { constructor() {
throw "new Double() not supported"; throw 'new Double() not supported';
} }
} }
Double.parseDouble = (d) => { Double.parseDouble = (d) => {
return parseFloat(d); return parseFloat(d);
} };
Double.compare = (a, b) => { Double.compare = (a, b) => {
return a - b; return a - b;
} };
Double.MAX_VALUE = Number.MAX_VALUE; Double.MAX_VALUE = Number.MAX_VALUE;
Double.POSITIVE_INFINITY = Number.POSITIVE_INFINITY; Double.POSITIVE_INFINITY = Number.POSITIVE_INFINITY;
Double.NEGATIVE_INFINITY = Number.NEGATIVE_INFINITY; Double.NEGATIVE_INFINITY = Number.NEGATIVE_INFINITY;
Double.NaN = NaN; Double.NaN = NaN;
jre.java.lang.Double = Double; jdk.java.lang.Double = Double;
} };

View File

@ -1,15 +1,14 @@
jre.imports['java.lang.Float'].load = () => { jdk.imports['java.lang.Float'].load = () => {
class Float {} class Float {}
Float.parseFloat = (d) => { Float.parseFloat = (d) => {
return parseFloat(d); return parseFloat(d);
} };
Float.compare = (a, b) => { Float.compare = (a, b) => {
return a - b; return a - b;
} };
Float.MAX_VALUE = Number.MAX_VALUE; Float.MAX_VALUE = Number.MAX_VALUE;
Float.POSITIVE_INFINITY = Number.POSITIVE_INFINITY; Float.POSITIVE_INFINITY = Number.POSITIVE_INFINITY;
Float.NEGATIVE_INFINITY = Number.NEGATIVE_INFINITY; Float.NEGATIVE_INFINITY = Number.NEGATIVE_INFINITY;
Float.NaN = NaN; Float.NaN = NaN;
jre.java.lang.Float = Float; jdk.java.lang.Float = Float;
} };

View File

@ -1,15 +1,14 @@
jre.imports['java.lang.Integer'].load = () => { jdk.imports['java.lang.Integer'].load = () => {
class Integer {} class Integer {}
Integer.parseInt = (d) => { Integer.parseInt = (d) => {
return parseInt(d); return parseInt(d);
} };
Integer.compare = (a, b) => { Integer.compare = (a, b) => {
return a - b; return a - b;
} };
Integer.MAX_VALUE = Number.MAX_VALUE; Integer.MAX_VALUE = Number.MAX_VALUE;
Integer.POSITIVE_INFINITY = Number.POSITIVE_INFINITY; Integer.POSITIVE_INFINITY = Number.POSITIVE_INFINITY;
Integer.NEGATIVE_INFINITY = Number.NEGATIVE_INFINITY; Integer.NEGATIVE_INFINITY = Number.NEGATIVE_INFINITY;
Integer.NaN = NaN; Integer.NaN = NaN;
jre.java.lang.Integer = Integer; jdk.java.lang.Integer = Integer;
} };

View File

@ -1,15 +1,14 @@
jre.imports['java.lang.Long'].load = () => { jdk.imports['java.lang.Long'].load = () => {
class Long {} class Long {}
Long.parseLong = (d) => { Long.parseLong = (d) => {
return parseInt(d); return parseInt(d);
} };
Long.compare = (a, b) => { Long.compare = (a, b) => {
return a - b; return a - b;
} };
Long.MAX_VALUE = Number.MAX_VALUE; Long.MAX_VALUE = Number.MAX_VALUE;
Long.POSITIVE_INFINITY = Number.POSITIVE_INFINITY; Long.POSITIVE_INFINITY = Number.POSITIVE_INFINITY;
Long.NEGATIVE_INFINITY = Number.NEGATIVE_INFINITY; Long.NEGATIVE_INFINITY = Number.NEGATIVE_INFINITY;
Long.NaN = NaN; Long.NaN = NaN;
jre.java.lang.Long = Long; jdk.java.lang.Long = Long;
} };

View File

@ -1,15 +1,14 @@
jre.imports['java.lang.Short'].load = () => { jdk.imports['java.lang.Short'].load = () => {
class Short {} class Short {}
Short.parseShort = (d) => { Short.parseShort = (d) => {
return parseInt(d); return parseInt(d);
} };
Short.compare = (a, b) => { Short.compare = (a, b) => {
return a - b; return a - b;
} };
Short.MAX_VALUE = Number.MAX_VALUE; Short.MAX_VALUE = Number.MAX_VALUE;
Short.POSITIVE_INFINITY = Number.POSITIVE_INFINITY; Short.POSITIVE_INFINITY = Number.POSITIVE_INFINITY;
Short.NEGATIVE_INFINITY = Number.NEGATIVE_INFINITY; Short.NEGATIVE_INFINITY = Number.NEGATIVE_INFINITY;
Short.NaN = NaN; Short.NaN = NaN;
jre.java.lang.Short = Short; jdk.java.lang.Short = Short;
} };

View File

@ -1,4 +1,4 @@
jre.imports['java.lang.String'].load = () => {}; jdk.imports['java.lang.String'].load = () => {};
// String is special, I just extended js String prototype // String is special, I just extended js String prototype
String.prototype.hashCode = () => { String.prototype.hashCode = () => {
let h = this._hashCode; let h = this._hashCode;
@ -10,15 +10,15 @@ String.prototype.hashCode = () => {
this._hashCode = h; this._hashCode = h;
} }
return h; return h;
} };
String.prototype.isEmpty = () => { String.prototype.isEmpty = () => {
return this.length == 0 return this.length == 0;
} };
String.prototype.contains = (substr) => { String.prototype.contains = (substr) => {
return this.includes(substr); return this.includes(substr);
} };
// static methods // static methods
String.valueOf = (c) => { String.valueOf = (c) => {
return c.toString(); return c.toString();
} };
jre.java.lang.String = String; jdk.java.lang.String = String;

View File

@ -1,8 +1,7 @@
jre.imports['java.lang.StringBuilder'].load = () => { jdk.imports['java.lang.StringBuilder'].load = () => {
class StringBuilder { class StringBuilder {
constructor() { constructor() {
this._str = ""; this._str = '';
} }
append(val) { append(val) {
this._str = this._str + val; this._str = this._str + val;
@ -16,5 +15,5 @@ jre.imports['java.lang.StringBuilder'].load = () => {
return this._str; return this._str;
} }
} }
jre.java.lang.StringBuilder = StringBuilder; jdk.java.lang.StringBuilder = StringBuilder;
} };

View File

@ -1,7 +1,7 @@
jre.imports['java.lang.System'].load = () => { jdk.imports['java.lang.System'].load = () => {
const Formatter = jre.import('java.util.Formatter'); const Formatter = jdk.import('java.util.Formatter');
const InputStream = jre.import('java.io.InputStream'); const InputStream = jdk.import('java.io.InputStream');
const PrintStream = jre.import('java.io.PrintStream'); const PrintStream = jdk.import('java.io.PrintStream');
class System {} class System {}
@ -31,5 +31,5 @@ jre.imports['java.lang.System'].load = () => {
console.log('Exited with code: ' + code); console.log('Exited with code: ' + code);
if (window?.exit) exit(); if (window?.exit) exit();
}; };
jre.java.lang.System = System; jdk.java.lang.System = System;
}; };

8
jdk/java/lang/Thread.js Executable file
View File

@ -0,0 +1,8 @@
jdk.imports['java.lang.Thread'].load = () => {
class Thread {
async sleep(millis) {
await setTimeout(millis);
}
}
jdk.java.lang.Thread = Thread;
};

View File

@ -1,6 +1,5 @@
jre.imports['java.util.AbstractList'].load = () => { jdk.imports['java.util.AbstractList'].load = () => {
const Itr = jdk.import('java.util.Itr');
const Itr = jre.import('java.util.Itr');
class AbstractList { class AbstractList {
constructor() { constructor() {
@ -83,5 +82,5 @@ jre.imports['java.util.AbstractList'].load = () => {
return new Itr(this); return new Itr(this);
} }
} }
jre.java.util.AbstractList = AbstractList; jdk.java.util.AbstractList = AbstractList;
} };

10
jdk/java/util/ArrayList.js Executable file
View File

@ -0,0 +1,10 @@
jdk.imports['java.util.ArrayList'].load = () => {
let AbstractList = jdk.import('java.util.AbstractList');
class ArrayList extends AbstractList {
constructor(...args) {
super(args);
}
}
jdk.java.util.ArrayList = ArrayList;
};

View File

@ -1,5 +1,4 @@
jre.imports['java.util.Arrays'].load = () => { jdk.imports['java.util.Arrays'].load = () => {
class Arrays { class Arrays {
fill(data, begin, nbElem, param) { fill(data, begin, nbElem, param) {
const max = begin + nbElem; const max = begin + nbElem;
@ -9,9 +8,9 @@ jre.imports['java.util.Arrays'].load = () => {
} }
copyOf(original, newLength, ignore) { copyOf(original, newLength, ignore) {
const copy = new Array(newLength); const copy = new Array(newLength);
jre.java.lang.System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); jdk.java.lang.System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));
return copy; return copy;
} }
} }
jre.java.util.Arrays = Arrays; jdk.java.util.Arrays = Arrays;
} };

View File

@ -1,5 +1,4 @@
jre.imports['java.util.Collections'].load = () => { jdk.imports['java.util.Collections'].load = () => {
class Collections { class Collections {
sort(list) { sort(list) {
if (!list.size()) return; if (!list.size()) return;
@ -14,5 +13,5 @@ jre.imports['java.util.Collections'].load = () => {
l.set(i, l.set(j, l.get(i))); l.set(i, l.set(j, l.get(i)));
} }
} }
jre.java.util.Collections = Collections; jdk.java.util.Collections = Collections;
} };

View File

@ -1,6 +1,5 @@
jre.imports['java.util.Formatter'].load = () => { jdk.imports['java.util.Formatter'].load = () => {
const IllegalFormatException = jdk.import('java.util.IllegalFormatException');
const IllegalFormatException = jre.import('java.util.IllegalFormatException');
class Formatter { class Formatter {
format(format, ...args) { format(format, ...args) {
@ -14,5 +13,5 @@ jre.imports['java.util.Formatter'].load = () => {
return format; return format;
} }
} }
jre.java.util.Formatter = Formatter; jdk.java.util.Formatter = Formatter;
}; };

View File

@ -1,5 +1,4 @@
jre.imports['java.util.HashMap'].load = () => { jdk.imports['java.util.HashMap'].load = () => {
class HashMap { class HashMap {
constructor() { constructor() {
this.content = {}; this.content = {};
@ -57,5 +56,5 @@ jre.imports['java.util.HashMap'].load = () => {
return Object.keys(this.content).length; return Object.keys(this.content).length;
} }
} }
jre.java.util.HashMap = HashMap; jdk.java.util.HashMap = HashMap;
} };

View File

@ -1,5 +1,4 @@
jre.imports['java.util.HashSet'].load = () => { jdk.imports['java.util.HashSet'].load = () => {
class HashSet { class HashSet {
constructor() { constructor() {
this.content = {}; this.content = {};
@ -52,7 +51,7 @@ jre.imports['java.util.HashSet'].load = () => {
toArray(a) { toArray(a) {
const _this = this; const _this = this;
return Object.keys(this.content).map(key => _this.content[key]); return Object.keys(this.content).map((key) => _this.content[key]);
} }
iterator() { iterator() {
@ -69,5 +68,5 @@ jre.imports['java.util.HashSet'].load = () => {
return this.content[index]; return this.content[index];
} }
} }
jre.java.util.HashSet = HashSet; jdk.java.util.HashSet = HashSet;
} };

View File

@ -1,5 +1,4 @@
jre.imports['java.util.IllegalFormatException'].load = () => { jdk.imports['java.util.IllegalFormatException'].load = () => {
// TODO import and extend exception // TODO import and extend exception
class IllegalFormatException { class IllegalFormatException {
@ -7,5 +6,5 @@ jre.imports['java.util.IllegalFormatException'].load = () => {
this.message = message; this.message = message;
} }
} }
jre.java.util.IllegalFormatException = IllegalFormatException; jdk.java.util.IllegalFormatException = IllegalFormatException;
}; };

View File

@ -1,5 +1,4 @@
jre.imports['java.util.Itr'].load = () => { jdk.imports['java.util.Itr'].load = () => {
class Itr { class Itr {
constructor(list) { constructor(list) {
this.cursor = 0; this.cursor = 0;
@ -21,12 +20,12 @@ jre.imports['java.util.Itr'].load = () => {
} catch ($ex$) { } catch ($ex$) {
if ($ex$ instanceof Error) { if ($ex$ instanceof Error) {
const e = $ex$; const e = $ex$;
throw new Error("no such element exception"); throw new Error('no such element exception');
} else { } else {
throw $ex$; throw $ex$;
} }
} }
} }
} }
jre.java.util.Itr = Itr; jdk.java.util.Itr = Itr;
} };

10
jdk/java/util/LinkedList.js Executable file
View File

@ -0,0 +1,10 @@
jdk.imports['java.util.LinkedList'].load = () => {
let AbstractList = jdk.import('java.util.AbstractList');
class LinkedList extends AbstractList {
constructor(...args) {
super(args);
}
}
jdk.java.util.LinkedList = LinkedList;
};

View File

@ -1,5 +1,4 @@
jre.imports['java.util.Random'].load = () => { jdk.imports['java.util.Random'].load = () => {
class Random { class Random {
constructor() { constructor() {
this.seed = undefined; this.seed = undefined;
@ -44,5 +43,5 @@ jre.imports['java.util.Random'].load = () => {
return min + rnd * (max - min); return min + rnd * (max - min);
} }
} }
jre.java.util.Random = Random; jdk.java.util.Random = Random;
} };

View File

@ -1,5 +1,5 @@
jre.imports['java.util.Scanner'].load = () => { jdk.imports['java.util.Scanner'].load = () => {
const File = jre.import('java.io.File'); const File = jdk.import('java.io.File');
class Scanner { class Scanner {
constructor(input) { constructor(input) {
@ -50,5 +50,5 @@ jre.imports['java.util.Scanner'].load = () => {
} }
close() {} close() {}
} }
jre.java.util.Scanner = Scanner; jdk.java.util.Scanner = Scanner;
}; };

View File

@ -1,4 +1,4 @@
jre.imports['java.util.Stack'].load = () => { jdk.imports['java.util.Stack'].load = () => {
class Stack { class Stack {
constructor() { constructor() {
this.content = new Array(); this.content = new Array();
@ -20,5 +20,5 @@ jre.imports['java.util.Stack'].load = () => {
return this.content.slice(-1)[0]; return this.content.slice(-1)[0];
} }
} }
jre.java.util.Stack = Stack; jdk.java.util.Stack = Stack;
} };

View File

@ -1,9 +0,0 @@
jre.imports['java.lang.Thread'].load = () => {
class Thread {
async sleep(millis) {
await setTimeout(millis);
}
}
jre.java.lang.Thread = Thread;
}

View File

@ -1,11 +0,0 @@
jre.imports['java.util.ArrayList'].load = () => {
let AbstractList = jre.import('java.util.AbstractList');
class ArrayList extends AbstractList {
constructor(...args) {
super(args);
}
}
jre.java.util.ArrayList = ArrayList;
}

View File

@ -1,11 +0,0 @@
jre.imports['java.util.LinkedList'].load = () => {
let AbstractList = jre.import('java.util.AbstractList');
class LinkedList extends AbstractList {
constructor(...args) {
super(args);
}
}
jre.java.util.LinkedList = LinkedList;
}

View File

@ -1,6 +1,6 @@
{ {
"name": "java2js", "name": "java2js",
"version": "1.0.2", "version": "1.0.3",
"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": "java2js.js", "main": "java2js.js",
"scripts": { "scripts": {