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 01:41:44 -05:00
commit fcf49e5a8d
37 changed files with 20306 additions and 0 deletions

0
LICENSE.md Normal file
View File

11
README.md Normal file
View File

@ -0,0 +1,11 @@
# 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.
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.
## Known limitations
- casting to int requires putting the element being cast in parenthesis
- no support for method overloading

38
demo.html Executable file
View File

@ -0,0 +1,38 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>java2js test</title>
<style>
body,
textarea {
background: #000;
color: #eee;
}
textarea {
-moz-tab-size: 2;
-o-tab-size: 2;
tab-size: 2;
}
#javaFile {
width: 47vw;
height: 90vh;
}
#javaConsole {
width: 47vw;
height: 90vh;
}
</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>
<script type="text/javascript" src="jre.js"></script>
<script type="text/javascript" src="ide.js"></script>
<script type="text/javascript" src="transpiler.js"></script>
</body>
</html>

76
ide.js Normal file
View File

@ -0,0 +1,76 @@
function ide(require, module, exports) {
const log = console.log;
window.java = {};
java.log = document.getElementById('javaConsole');
java.file0 = document.getElementById('javaFile');
window.java2js = (file) => {
const java_to_javascript = require('java-to-javascript');
let classLine = file.indexOf('public class');
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));
let prefix = `(jre.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 += '\n';
// hacky support for Java 15 triple quotes
file = file.replaceAll(/"""([^"]*)"""/g, (match, str) => {
str = str.replaceAll(/(.*)(\n|$)/g, '"$1\\n"+').slice(0, -1);
return str;
});
// hacky support for Array literals
file = file.replaceAll(/=\s*new \w*\[\]\s*\{/g, '= {');
// convert string .length() method
file = file.replaceAll(/\.length\(\)/g, '.length');
// 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');
// log(file);
let suffix = `\njre.main = ${className}.main;\n}`;
window.file0 = file;
let trans = java_to_javascript(file);
// log(trans);
trans = trans.replace(/(\([^\)]*\) =>)/gm, 'async $1');
trans = trans.replace(/([\w_\$]+\.next(Int|Float|Double|Line|Short|Long)*\(\))/gm, 'await $1');
trans = prefix + trans + suffix;
log(trans);
try {
eval(trans);
jre.run();
} catch (e) {
java.log.value += e;
}
};
java.file0.onchange = () => {
java.log.value = '';
let file = java.file0.value;
java2js(file);
};
}

141
jre.js Executable file
View File

@ -0,0 +1,141 @@
class JRE {
constructor() {
this.java = {};
let pkgs = ['com', 'lang', 'org', 'io', 'util'];
for (let pkg of pkgs) {
this.java[pkg] = {};
}
this.imports = {};
try {
let names = [
'Character', 'Double', 'Float', 'Integer', 'Long', 'Short', 'String',
'StringBuilder', 'System', 'Thread'
];
let lang = [];
for (let name of names) {
lang.push('java.lang.' + name);
}
this.import(lang);
} catch (e) {
}
this.main = () => {};
this.run();
}
run() {
this.load();
}
load() {
let ready = 0;
for (let className in this.imports) {
let imp = this.imports[className];
if (imp.classPath) {
imp.classPath = className.split('.');
}
if (imp.ready) {
ready++;
continue;
}
if (imp.load) {
try {
imp.load();
} catch (e) {
console.log(e);
continue;
}
imp.load = null;
imp.ready = true;
ready++;
}
}
if (ready != Object.keys(this.imports).length) {
let _this = this;
setTimeout(() => {
_this.load();
}, 100);
} else {
this.launch();
}
}
launch() {
// make java.lang classes global
for (let name in this.java.lang) {
if (name == 'String') continue;
window[name] = this.java.lang[name];
}
System.in.reset();
System.out.reset();
this.main(this.vmArgs);
}
getClass(classPath) {
let _class = this;
for (let part of classPath) {
_class = _class[part];
if (!_class) return;
}
return _class;
}
import(classNames) {
if (!classNames) return;
if (typeof classNames == 'string') {
classNames = [classNames];
}
let ready = 0;
for (let className of classNames) {
let imp = this.imports[className];
if (imp) {
// class is ready for use
if (imp.ready) ready++;
// class file loaded but not ready yet
continue;
}
imp = (this.imports[className] = {});
imp.load = null;
imp.classPath = className.split('.');
let src = './jre';
for (let part of imp.classPath) {
src += '/' + part;
}
src += '.js';
const getJS = new Promise((resolve, reject) => {
const script = document.createElement('script');
document.body.appendChild(script);
script.onload = resolve;
script.onerror = reject;
script.async = true;
script.src = src;
});
getJS.then(() => {});
}
if (ready != classNames.length) {
throw 'loading java classes';
}
let classes = [];
for (let className of classNames) {
let imp = this.imports[className];
classes.push(this.getClass(imp.classPath));
}
if (classNames.length == 1) {
classes = classes[0];
}
return classes;
}
}
window.jre = new JRE();

14
jre/java/io/File.js Executable file
View File

@ -0,0 +1,14 @@
jre.imports['java.io.File'].load = () => {
class File {
constructor(file) {
this.absPath = file;
}
getAbsolutePath() {
return this.absPath;
}
}
File.seperator = File.seperatorChar = '/';
File.pathSeparator = File.pathSeparatorChar = '/';
jre.java.io.File = File;
}

19
jre/java/io/InputStream.js Executable file
View File

@ -0,0 +1,19 @@
jre.imports['java.io.InputStream'].load = () => {
class InputStream {
constructor() {
this.reset();
let _this = this;
java.log.onkeyup = () => {
_this.stream = java.log.value;
};
}
reset() {
this.stream = '';
this.mark = 0;
}
read(length) {
this.mark += length;
}
}
jre.java.io.InputStream = InputStream;
};

44
jre/java/io/PrintStream.js Executable file
View File

@ -0,0 +1,44 @@
jre.imports['java.io.PrintStream'].load = () => {
class PrintStream {
constructor() {
this.log = '';
this.onPrint = () => {};
}
reset() {
this.log = '';
}
_onPrint(length) {
this.onPrint(length);
}
print(arg) {
let str = arg.toString();
this.log += str;
if (java.log) {
java.log.value += str;
this._onPrint(str.length);
}
}
println(arg) {
let str = arg.toString() + '\n';
this.log += str;
if (java.log) {
java.log.value += str;
this._onPrint(str.length);
}
}
printf(format, ...args) {
let str = new Formatter().format(format, args);
this.log += str;
if (java.log) {
java.log.value += str;
this._onPrint(str.length);
}
}
}
jre.java.io.PrintStream = PrintStream;
};

1
jre/java/lang/Boolean.js Executable file
View File

@ -0,0 +1 @@

0
jre/java/lang/Byte.js Executable file
View File

31
jre/java/lang/Character.js Executable file
View File

@ -0,0 +1,31 @@
jre.imports['java.lang.Character'].load = () => {
class Character {}
Character.isDigit = (c) => {
c = c.charCodeAt(0);
if (0x0030 <= c && c <= 0x0039) return true;
if (0x0660 <= c && c <= 0x0669) return true;
if (0x06f0 <= c && c <= 0x06f9) return true;
if (0x0966 <= c && c <= 0x096f) return true;
if (0xff10 <= c && c <= 0xff19) return true;
return false;
};
Character.isUpperCase = (c) => {
return 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.includes(c);
};
Character.isLowerCase = (c) => {
return 'abcdefghijklmnopqrstuvwxyz'.includes(c);
};
Character.isLetter = (c) => {
return Character.isLowerCase(c) || Character.isUpperCase(c);
};
Character.isAlphabetic = (c) => {
return Character.isLetter(c);
};
Character.compare = (a, b) => {
return a.charCodeAt(0) - b.charCodeAt(0);
};
Character.toString = (c) => {
return c;
};
jre.java.lang.Character = Character;
};

19
jre/java/lang/Double.js Executable file
View File

@ -0,0 +1,19 @@
jre.imports['java.lang.Double'].load = () => {
class Double {
constructor() {
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;
}

0
jre/java/lang/Exception.js Executable file
View File

15
jre/java/lang/Float.js Executable file
View File

@ -0,0 +1,15 @@
jre.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;
}

15
jre/java/lang/Integer.js Executable file
View File

@ -0,0 +1,15 @@
jre.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;
}

15
jre/java/lang/Long.js Executable file
View File

@ -0,0 +1,15 @@
jre.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;
}

15
jre/java/lang/Short.js Executable file
View File

@ -0,0 +1,15 @@
jre.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;
}

24
jre/java/lang/String.js Executable file
View File

@ -0,0 +1,24 @@
jre.imports['java.lang.String'].load = () => {};
// String is special, I just extended js String prototype
String.prototype.hashCode = () => {
let h = this._hashCode;
if (!h && this.length > 0) {
const val = this;
for (let i = 0; i < this.length; i++) {
h = 31 * h + this.charCodeAt(i);
}
this._hashCode = h;
}
return h;
}
String.prototype.isEmpty = () => {
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;

20
jre/java/lang/StringBuilder.js Executable file
View File

@ -0,0 +1,20 @@
jre.imports['java.lang.StringBuilder'].load = () => {
class StringBuilder {
constructor() {
this._str = "";
}
append(val) {
this._str = this._str + val;
return this;
}
insert(position, val) {
this._str = this._str.slice(0, position) + val + this._str.slice(position);
return this;
}
toString() {
return this._str;
}
}
jre.java.lang.StringBuilder = StringBuilder;
}

35
jre/java/lang/System.js Executable file
View File

@ -0,0 +1,35 @@
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');
class System {}
System.in = new InputStream();
System.out = new PrintStream();
System.out.onPrint = (length) => {
System.in.mark += length;
};
System.arraycopy = (src, srcPos, dest, destPos, numElements) => {
if (
(dest instanceof Float64Array || dest instanceof Int32Array) &&
(src instanceof Float64Array || src instanceof Int32Array)
) {
if (numElements == src.length) {
dest.set(src, destPos);
} else {
dest.set(src.subarray(srcPos, srcPos + numElements), destPos);
}
} else {
for (let i = 0; i < numElements; i++) {
dest[destPos + i] = src[srcPos + i];
}
}
};
System.exit = (code) => {
console.log('Exited with code: ' + code);
if (window.exit) exit();
};
jre.java.lang.System = System;
};

9
jre/java/lang/Thread.js Executable file
View File

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

0
jre/java/lang/Throwable.js Executable file
View File

87
jre/java/util/AbstractList.js Executable file
View File

@ -0,0 +1,87 @@
jre.imports['java.util.AbstractList'].load = () => {
const Itr = jre.import('java.util.Itr');
class AbstractList {
constructor() {
// TODO
this.content = [];
}
addAll(index, vals) {
const tempArray = vals.toArray(null);
for (let i = 0; i < tempArray.length; i++) {
this.content.push(tempArray[i]);
}
return false;
}
clear() {
this.content = [];
}
poll() {
return this.content.shift();
}
remove(indexOrElem) {
this.content.splice(indexOrElem, 1);
return true;
}
removeAll() {
this.content = [];
return true;
}
toArray(a) {
return this.content;
}
size() {
return this.content.length;
}
add(index, elem) {
if (typeof elem !== 'undefined') {
this.content.splice(index, 0, elem);
} else {
this.content.push(index);
}
}
get(index) {
return this.content[index];
}
contains(val) {
return this.content.indexOf(val) != -1;
}
containsAll(elems) {
return false;
}
isEmpty() {
return this.content.length == 0;
}
set(index, element) {
this.content[index] = element;
return element;
}
indexOf(element) {
return this.content.indexOf(element);
}
lastIndexOf(element) {
return this.content.lastIndexOf(element);
}
iterator() {
return new Itr(this);
}
}
jre.java.util.AbstractList = AbstractList;
}

11
jre/java/util/ArrayList.js Executable file
View File

@ -0,0 +1,11 @@
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;
}

17
jre/java/util/Arrays.js Executable file
View File

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

18
jre/java/util/Collections.js Executable file
View File

@ -0,0 +1,18 @@
jre.imports['java.util.Collections'].load = () => {
class Collections {
sort(list) {
if (!list.size()) return;
if (list.get(0).compareTo) {
list.sort(list.get(0).compareTo);
} else {
list.sort();
}
}
swap(list, i, j) {
const l = list;
l.set(i, l.set(j, l.get(i)));
}
}
jre.java.util.Collections = Collections;
}

18
jre/java/util/Formatter.js Executable file
View File

@ -0,0 +1,18 @@
jre.imports['java.util.Formatter'].load = () => {
const IllegalFormatException = jre.import('java.util.IllegalFormatException');
class Formatter {
format(format, ...args) {
let regex = /%[0-9\.]*[\w]/;
for (let arg of args) {
if (!regex.test(format)) {
throw new IllegalFormatException();
}
format = format.replace(regex, arg.toString());
}
return format;
}
}
jre.java.util.Formatter = Formatter;
};

61
jre/java/util/HashMap.js Executable file
View File

@ -0,0 +1,61 @@
jre.imports['java.util.HashMap'].load = () => {
class HashMap {
constructor() {
this.content = {};
}
get(key) {
return this.content[key];
}
put(key, value) {
const previous_val = this.content[key];
this.content[key] = value;
return previous_val;
}
containsKey(key) {
return this.content.hasOwnProperty(key);
}
remove(key) {
const tmp = this.content[key];
delete this.content[key];
return tmp;
}
keySet() {
const result = new HashSet();
for (const p in this.content) {
if (this.content.hasOwnProperty(p)) {
result.add(p);
}
}
return result;
}
isEmpty() {
return Object.keys(this.content).length == 0;
}
values() {
const result = new HashSet();
for (const p in this.content) {
if (this.content.hasOwnProperty(p)) {
result.add(this.content[p]);
}
}
return result;
}
clear() {
this.content = {};
}
size() {
return Object.keys(this.content).length;
}
}
jre.java.util.HashMap = HashMap;
}

73
jre/java/util/HashSet.js Executable file
View File

@ -0,0 +1,73 @@
jre.imports['java.util.HashSet'].load = () => {
class HashSet {
constructor() {
this.content = {};
}
add(val) {
this.content[val] = val;
}
clear() {
this.content = {};
}
contains(val) {
return this.content.hasOwnProperty(val);
}
containsAll(elems) {
return false;
}
addAll(vals) {
const tempArray = vals.toArray(null);
for (let i = 0; i < tempArray.length; i++) {
this.content[tempArray[i]] = tempArray[i];
}
return true;
}
remove(val) {
let b = false;
if (this.content[val]) {
b = true;
}
delete this.content[val];
return b;
}
removeAll() {
return false;
}
size() {
return Object.keys(this.content).length;
}
isEmpty() {
return this.size() == 0;
}
toArray(a) {
const _this = this;
return Object.keys(this.content).map(key => _this.content[key]);
}
iterator() {
return new java.util.Itr(this);
}
forEach(f) {
for (const p in this.content) {
f(this.content[p]);
}
}
get(index) {
return this.content[index];
}
}
jre.java.util.HashSet = HashSet;
}

View File

@ -0,0 +1,11 @@
jre.imports['java.util.IllegalFormatException'].load = () => {
// TODO import and extend exception
class IllegalFormatException {
constructor(message) {
this.message = message;
}
}
jre.java.util.IllegalFormatException = IllegalFormatException;
};

32
jre/java/util/Itr.js Executable file
View File

@ -0,0 +1,32 @@
jre.imports['java.util.Itr'].load = () => {
class Itr {
constructor(list) {
this.cursor = 0;
this.lastRet = -1;
this.list = list;
}
hasNext() {
return this.cursor != this.list.size();
}
next() {
try {
const i = this.cursor;
const next = this.list.get(i);
this.lastRet = i;
this.cursor = i + 1;
return next;
} catch ($ex$) {
if ($ex$ instanceof Error) {
const e = $ex$;
throw new Error("no such element exception");
} else {
throw $ex$;
}
}
}
}
jre.java.util.Itr = Itr;
}

11
jre/java/util/LinkedList.js Executable file
View File

@ -0,0 +1,11 @@
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;
}

48
jre/java/util/Random.js Executable file
View File

@ -0,0 +1,48 @@
jre.imports['java.util.Random'].load = () => {
class Random {
constructor() {
this.seed = undefined;
}
nextInt(max) {
if (typeof max === 'undefined') {
max = Math.pow(2, 32);
}
if (this.seed == undefined) {
return Math.floor(Math.random() * max);
} else {
return Math.floor(this.nextSeeded(0, max));
}
}
nextDouble() {
if (this.seed == undefined) {
return Math.random();
} else {
return this.nextSeeded();
}
}
nextBoolean() {
if (this.seed == undefined) {
return Math.random() >= 0.5;
} else {
return this.nextSeeded() >= 0.5;
}
}
setSeed(seed) {
this.seed = seed;
}
nextSeeded(min, max) {
var max = max || 1;
var min = min || 0;
this.seed = (this.seed * 9301 + 49297) % 233280;
const rnd = this.seed / 233280;
return min + rnd * (max - min);
}
}
jre.java.util.Random = Random;
}

54
jre/java/util/Scanner.js Executable file
View File

@ -0,0 +1,54 @@
jre.imports['java.util.Scanner'].load = () => {
const File = jre.import('java.io.File');
class Scanner {
constructor(input) {
if (input instanceof File) {
this.inputType = 'File';
throw 'unsupported Scanner input type: File';
}
this.in = input;
}
hasNext(pattern) {
if (pattern instanceof RegExp) {
return pattern.test(this.in.stream.slice(this.in.mark));
}
// if pattern is string
return this.in.stream.includes(pattern);
}
hasNextLine() {
return this.hasNext('\n');
}
async nextLine() {
return await this.next(/.*\n/);
}
async next(pattern) {
while (!this.hasNext(pattern)) {
await new Promise((done) => setTimeout(() => done(), 100));
}
let buf = this.in.stream.slice(this.in.mark);
let substr = buf.match(pattern)[0];
let start = buf.indexOf(substr);
let end = buf.indexOf('\n');
this.in.read(end - start + 1);
return buf.slice(start, end);
}
async nextShort() {
return await this.nextInt();
}
async nextInt() {
return Number(await this.next(/\d+\D/));
}
async nextLong() {
return await this.nextInt();
}
async nextFloat() {
return Number(await this.next(/[0-9\.]+[^0-9\.]/));
}
async nextDouble() {
return await this.nextFloat();
}
close() {}
}
jre.java.util.Scanner = Scanner;
};

24
jre/java/util/Stack.js Executable file
View File

@ -0,0 +1,24 @@
jre.imports['java.util.Stack'].load = () => {
class Stack {
constructor() {
this.content = new Array();
}
pop() {
return this.content.pop();
}
push(t) {
this.content.push(t);
}
isEmpty() {
return this.content.length == 0;
}
peek() {
return this.content.slice(-1)[0];
}
}
jre.java.util.Stack = Stack;
}

24
package.json Normal file
View File

@ -0,0 +1,24 @@
{
"name": "java2js",
"version": "1.0.1",
"description": "Converts Java to JavaScript with support for p5.js and QuintOS.",
"main": "java2js.js",
"scripts": {
"start": "open demo.html",
"test": "echo \"Error: no test specified\" && exit 1",
"v": "npm version patch --force",
"V": "npm version minor --force",
"version": "git add -A",
"postversion": "git push"
},
"repository": {
"type": "git",
"url": "git+https://github.com/quinton-ashley/java2js.git"
},
"author": "quinton-ashley",
"license": "GPL-3.0-only",
"bugs": {
"url": "https://github.com/quinton-ashley/java2js/issues"
},
"homepage": "https://github.com/quinton-ashley/java2js#readme"
}

19275
transpiler.js Executable file

File diff suppressed because it is too large Load Diff