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
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
- 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>
<textarea id="javaFile" 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>
</body>
</html>

2
ide.js
View File

@ -5,5 +5,5 @@ ide.file0 = document.getElementById('javaFile');
ide.file0.onchange = () => {
ide.log.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 r(e, n, t) {
function o(i, f) {
@ -38,9 +40,11 @@
function (require, module, exports) {
const log = console.log;
/* Java to JavaScript transpiler */
const java_to_javascript = require('java-to-javascript');
class JRE {
/* JDK implementation in modern JavaScript */
class JDK {
constructor() {
this.java = {};
let pkgs = ['com', 'lang', 'org', 'io', 'util'];
@ -49,6 +53,12 @@
}
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 {
let names = [
'Character',
@ -69,12 +79,15 @@
this.import(lang);
} catch (e) {}
// stub main for now
this.main = () => {};
this.load();
}
load() {
let ready = 0;
// load all imported classes
for (let className in this.imports) {
let imp = this.imports[className];
if (imp.classPath) {
@ -96,6 +109,8 @@
ready++;
}
}
// some classes may load slower than others,
// wait for them all before launching the Java program
if (ready != Object.keys(this.imports).length) {
let _this = this;
setTimeout(() => {
@ -116,7 +131,7 @@
System.in.reset();
System.out.reset();
this.main(this.vmArgs);
this.main(this.jvmArgs);
}
getClass(classPath) {
@ -149,7 +164,7 @@
imp.load = null;
imp.classPath = className.split('.');
let src = './jre';
let src = './jdk';
for (let part of imp.classPath) {
src += '/' + part;
}
@ -190,14 +205,14 @@
let userName = window?.QuintOS?.userName || 'quinton-ashley';
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
for (let i = 0; i < imports.length; i++) {
let imp = imports[i];
let impPath = imp.split('.');
let impName = impPath[impPath.length - 1];
prefix += `let ${impName} = jre.import('${imp}');\n`;
prefix += `let ${impName} = jdk.import('${imp}');\n`;
}
prefix += '\n';
@ -219,7 +234,7 @@
// log(file);
let suffix = `\njre.main = ${className}.main;\n}`;
let suffix = `\njdk.main = ${className}.main;\n}`;
window.file0 = file;
@ -242,7 +257,7 @@
}
}
}
window.jre = new JRE();
window.jdk = new JDK();
},
{
'java-to-javascript': 2

View File

@ -1,5 +1,4 @@
jre.imports['java.io.File'].load = () => {
jdk.imports['java.io.File'].load = () => {
class File {
constructor(file) {
this.absPath = file;
@ -10,5 +9,5 @@ jre.imports['java.io.File'].load = () => {
}
File.seperator = File.seperatorChar = '/';
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 {
constructor() {
this.reset();
@ -17,5 +17,5 @@ jre.imports['java.io.InputStream'].load = () => {
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 {
constructor() {
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 {}
Character.isDigit = (c) => {
c = c.charCodeAt(0);
@ -27,5 +27,5 @@ jre.imports['java.lang.Character'].load = () => {
Character.toString = (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 {
constructor() {
throw "new Double() not supported";
throw 'new Double() not supported';
}
}
Double.parseDouble = (d) => {
return parseFloat(d);
}
};
Double.compare = (a, b) => {
return a - b;
}
};
Double.MAX_VALUE = Number.MAX_VALUE;
Double.POSITIVE_INFINITY = Number.POSITIVE_INFINITY;
Double.NEGATIVE_INFINITY = Number.NEGATIVE_INFINITY;
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 {}
Float.parseFloat = (d) => {
return parseFloat(d);
}
};
Float.compare = (a, b) => {
return a - b;
}
};
Float.MAX_VALUE = Number.MAX_VALUE;
Float.POSITIVE_INFINITY = Number.POSITIVE_INFINITY;
Float.NEGATIVE_INFINITY = Number.NEGATIVE_INFINITY;
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 {}
Integer.parseInt = (d) => {
return parseInt(d);
}
};
Integer.compare = (a, b) => {
return a - b;
}
};
Integer.MAX_VALUE = Number.MAX_VALUE;
Integer.POSITIVE_INFINITY = Number.POSITIVE_INFINITY;
Integer.NEGATIVE_INFINITY = Number.NEGATIVE_INFINITY;
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 {}
Long.parseLong = (d) => {
return parseInt(d);
}
};
Long.compare = (a, b) => {
return a - b;
}
};
Long.MAX_VALUE = Number.MAX_VALUE;
Long.POSITIVE_INFINITY = Number.POSITIVE_INFINITY;
Long.NEGATIVE_INFINITY = Number.NEGATIVE_INFINITY;
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 {}
Short.parseShort = (d) => {
return parseInt(d);
}
};
Short.compare = (a, b) => {
return a - b;
}
};
Short.MAX_VALUE = Number.MAX_VALUE;
Short.POSITIVE_INFINITY = Number.POSITIVE_INFINITY;
Short.NEGATIVE_INFINITY = Number.NEGATIVE_INFINITY;
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.prototype.hashCode = () => {
let h = this._hashCode;
@ -10,15 +10,15 @@ String.prototype.hashCode = () => {
this._hashCode = h;
}
return h;
}
};
String.prototype.isEmpty = () => {
return this.length == 0
}
return this.length == 0;
};
String.prototype.contains = (substr) => {
return this.includes(substr);
}
};
// static methods
String.valueOf = (c) => {
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 {
constructor() {
this._str = "";
this._str = '';
}
append(val) {
this._str = this._str + val;
@ -16,5 +15,5 @@ jre.imports['java.lang.StringBuilder'].load = () => {
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 = () => {
const Formatter = jre.import('java.util.Formatter');
const InputStream = jre.import('java.io.InputStream');
const PrintStream = jre.import('java.io.PrintStream');
jdk.imports['java.lang.System'].load = () => {
const Formatter = jdk.import('java.util.Formatter');
const InputStream = jdk.import('java.io.InputStream');
const PrintStream = jdk.import('java.io.PrintStream');
class System {}
@ -31,5 +31,5 @@ jre.imports['java.lang.System'].load = () => {
console.log('Exited with code: ' + code);
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 = () => {
const Itr = jre.import('java.util.Itr');
jdk.imports['java.util.AbstractList'].load = () => {
const Itr = jdk.import('java.util.Itr');
class AbstractList {
constructor() {
@ -83,5 +82,5 @@ jre.imports['java.util.AbstractList'].load = () => {
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 {
fill(data, begin, nbElem, param) {
const max = begin + nbElem;
@ -9,9 +8,9 @@ jre.imports['java.util.Arrays'].load = () => {
}
copyOf(original, newLength, ignore) {
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;
}
}
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 {
sort(list) {
if (!list.size()) return;
@ -14,5 +13,5 @@ jre.imports['java.util.Collections'].load = () => {
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 = () => {
const IllegalFormatException = jre.import('java.util.IllegalFormatException');
jdk.imports['java.util.Formatter'].load = () => {
const IllegalFormatException = jdk.import('java.util.IllegalFormatException');
class Formatter {
format(format, ...args) {
@ -14,5 +13,5 @@ jre.imports['java.util.Formatter'].load = () => {
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 {
constructor() {
this.content = {};
@ -57,5 +56,5 @@ jre.imports['java.util.HashMap'].load = () => {
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 {
constructor() {
this.content = {};
@ -52,7 +51,7 @@ jre.imports['java.util.HashSet'].load = () => {
toArray(a) {
const _this = this;
return Object.keys(this.content).map(key => _this.content[key]);
return Object.keys(this.content).map((key) => _this.content[key]);
}
iterator() {
@ -69,5 +68,5 @@ jre.imports['java.util.HashSet'].load = () => {
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
class IllegalFormatException {
@ -7,5 +6,5 @@ jre.imports['java.util.IllegalFormatException'].load = () => {
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 {
constructor(list) {
this.cursor = 0;
@ -21,12 +20,12 @@ jre.imports['java.util.Itr'].load = () => {
} catch ($ex$) {
if ($ex$ instanceof Error) {
const e = $ex$;
throw new Error("no such element exception");
throw new Error('no such element exception');
} else {
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 {
constructor() {
this.seed = undefined;
@ -44,5 +43,5 @@ jre.imports['java.util.Random'].load = () => {
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 = () => {
const File = jre.import('java.io.File');
jdk.imports['java.util.Scanner'].load = () => {
const File = jdk.import('java.io.File');
class Scanner {
constructor(input) {
@ -50,5 +50,5 @@ jre.imports['java.util.Scanner'].load = () => {
}
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 {
constructor() {
this.content = new Array();
@ -20,5 +20,5 @@ jre.imports['java.util.Stack'].load = () => {
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",
"version": "1.0.2",
"version": "1.0.3",
"description": "Converts Java to JavaScript with support for p5.js and QuintOS.",
"main": "java2js.js",
"scripts": {