From 8b1c2a5e4e48a48a6743f9c6c16220c83e110359 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Fri, 24 Jan 2025 05:57:15 +0200 Subject: [PATCH] separate more stuff into lib project --- compilation/build.gradle.kts | 9 - lib/build.gradle.kts | 50 +- lib/package.json | 4 +- .../java/me/topchetoeu/j2s/lib/Compilers.java | 123 +++ .../me/topchetoeu/j2s/lib}/JSONConverter.java | 2 +- .../me/topchetoeu/j2s/lib}/NativeMapper.java | 2 +- .../me/topchetoeu/j2s/lib/Primordials.java | 694 +++++++++++++++ .../java/me/topchetoeu/j2s/lib/StdLib.java | 29 + .../j2s/lib}/buffers/Int32ArrayValue.java | 2 +- .../j2s/lib}/buffers/Int8ArrayValue.java | 2 +- .../j2s/lib}/buffers/TypedArrayValue.java | 2 +- .../j2s/lib}/buffers/Uint8ArrayValue.java | 2 +- lib/src/stdlib/_entry.ts | 17 +- lib/src/stdlib/namespaces/console.ts | 2 +- lib/src/stdlib/primordials.ts | 2 + .../transpiler/{_entry.ts => _entry-babel.ts} | 0 lib/src/transpiler/_entry-coffee.ts | 3 + lib/src/transpiler/_entry-typescript.ts | 3 + lib/src/transpiler/babel.ts | 1 + repl/build.gradle.kts | 9 - .../me/topchetoeu/j2s/repl/SimpleRepl.java | 814 +----------------- .../j2s/repl/debug/SimpleDebugger.java | 8 +- runtime/build.gradle.kts | 9 - 23 files changed, 921 insertions(+), 868 deletions(-) create mode 100644 lib/src/main/java/me/topchetoeu/j2s/lib/Compilers.java rename {repl/src/main/java/me/topchetoeu/j2s/repl => lib/src/main/java/me/topchetoeu/j2s/lib}/JSONConverter.java (98%) rename {repl/src/main/java/me/topchetoeu/j2s/repl/mapping => lib/src/main/java/me/topchetoeu/j2s/lib}/NativeMapper.java (98%) create mode 100644 lib/src/main/java/me/topchetoeu/j2s/lib/Primordials.java create mode 100644 lib/src/main/java/me/topchetoeu/j2s/lib/StdLib.java rename {repl/src/main/java/me/topchetoeu/j2s/repl => lib/src/main/java/me/topchetoeu/j2s/lib}/buffers/Int32ArrayValue.java (94%) rename {repl/src/main/java/me/topchetoeu/j2s/repl => lib/src/main/java/me/topchetoeu/j2s/lib}/buffers/Int8ArrayValue.java (89%) rename {repl/src/main/java/me/topchetoeu/j2s/repl => lib/src/main/java/me/topchetoeu/j2s/lib}/buffers/TypedArrayValue.java (97%) rename {repl/src/main/java/me/topchetoeu/j2s/repl => lib/src/main/java/me/topchetoeu/j2s/lib}/buffers/Uint8ArrayValue.java (91%) rename lib/src/transpiler/{_entry.ts => _entry-babel.ts} (100%) create mode 100644 lib/src/transpiler/_entry-coffee.ts create mode 100644 lib/src/transpiler/_entry-typescript.ts diff --git a/compilation/build.gradle.kts b/compilation/build.gradle.kts index e3a0e8f..8a6997b 100644 --- a/compilation/build.gradle.kts +++ b/compilation/build.gradle.kts @@ -4,15 +4,6 @@ plugins { description = "A compiler of EcmaScript 5 code to J2S bytecode"; -tasks.processResources { - filesMatching("metadata.json", { - expand( - "version" to properties["project_version"], - "name" to properties["project_name"], - ); - }); -} - tasks.test { useJUnitPlatform(); } diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 41f8806..fb7e197 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -1,15 +1,14 @@ import com.github.gradle.node.npm.task.NpmTask; plugins { - id("common"); + id("common-java"); id("com.github.node-gradle.node") version "5.0.0"; } -tasks.compileJava { - enabled = false; -} -tasks.classes { - enabled = false; +dependencies { + implementation(project(":common")); + implementation(project(":compilation")); + implementation(project(":runtime")); } node { @@ -27,16 +26,38 @@ tasks.register("compileStdlib") { args.set(listOf("run", "build-env")); } -tasks.register("compileTranspiler") { +tasks.register("compileBabel") { dependsOn("npmInstall"); inputs.files("rollup.config.js"); inputs.dir("src/transpiler"); - outputs.files("build/js/transpiler.js"); + outputs.files("build/js/babel.js"); // nom nom tasty ram environment.put("NODE_OPTIONS", "--max-old-space-size=4096"); - args.set(listOf("run", "build-ts")); + args.set(listOf("run", "build-babel")); +} +tasks.register("compileTypescript") { + dependsOn("npmInstall"); + + inputs.files("rollup.config.js"); + inputs.dir("src/transpiler"); + outputs.files("build/js/typescript.js"); + // nom nom tasty ram + environment.put("NODE_OPTIONS", "--max-old-space-size=4096"); + + args.set(listOf("run", "build-typescript")); +} +tasks.register("compileCoffee") { + dependsOn("npmInstall"); + + inputs.files("rollup.config.js"); + inputs.dir("src/transpiler"); + outputs.files("build/js/coffee.js"); + // nom nom tasty ram + environment.put("NODE_OPTIONS", "--max-old-space-size=4096"); + + args.set(listOf("run", "build-coffee")); } tasks.jar { @@ -50,7 +71,9 @@ tasks.jar { tasks.processResources { dependsOn("compileStdlib"); - dependsOn("compileTranspiler"); + dependsOn("compileTypescript"); + dependsOn("compileBabel"); + dependsOn("compileCoffee"); from("build/js") { into("lib"); @@ -58,11 +81,4 @@ tasks.processResources { from("src/lib") { into("lib"); } - - filesMatching("metadata.json", { - expand( - "version" to properties["project_version"].toString(), - "name" to properties["project_name"].toString(), - ); - }) } diff --git a/lib/package.json b/lib/package.json index 0a6fb17..8a8546f 100644 --- a/lib/package.json +++ b/lib/package.json @@ -1,7 +1,9 @@ { "scripts": { "build-env": "rollup -c --environment INPUT:src/stdlib/_entry.ts,OUTPUT:build/js/stdlib.js,POLYFILLS:src/polyfills", - "build-ts": "rollup -c --environment INPUT:src/transpiler/_entry.ts,OUTPUT:build/js/transpiler.js" + "build-babel": "rollup -c --environment INPUT:src/transpiler/_entry-babel.ts,OUTPUT:build/js/babel.js", + "build-coffee": "rollup -c --environment INPUT:src/transpiler/_entry-coffee.ts,OUTPUT:build/js/coffee.js", + "build-typescript": "rollup -c --environment INPUT:src/transpiler/_entry-typescript.ts,OUTPUT:build/js/typescript.js" }, "dependencies": { "@babel/core": "^7.26.0", diff --git a/lib/src/main/java/me/topchetoeu/j2s/lib/Compilers.java b/lib/src/main/java/me/topchetoeu/j2s/lib/Compilers.java new file mode 100644 index 0000000..73b653d --- /dev/null +++ b/lib/src/main/java/me/topchetoeu/j2s/lib/Compilers.java @@ -0,0 +1,123 @@ +package me.topchetoeu.j2s.lib; + +import me.topchetoeu.j2s.common.Environment; +import me.topchetoeu.j2s.common.Filename; +import me.topchetoeu.j2s.common.Metadata; +import me.topchetoeu.j2s.common.Reading; +import me.topchetoeu.j2s.common.SyntaxException; +import me.topchetoeu.j2s.compilation.JavaScript; +import me.topchetoeu.j2s.runtime.Compiler; +import me.topchetoeu.j2s.runtime.debug.DebugHandler; +import me.topchetoeu.j2s.runtime.exceptions.EngineException; +import me.topchetoeu.j2s.runtime.values.Value; +import me.topchetoeu.j2s.runtime.values.functions.CodeFunction; +import me.topchetoeu.j2s.runtime.values.functions.FunctionValue; +import me.topchetoeu.j2s.runtime.values.functions.NativeFunction; +import me.topchetoeu.j2s.runtime.values.primitives.StringValue; + +public class Compilers { + public static Compiler jsCompiler() { + return (env, filename, raw, mapper) -> { + try { + var res = JavaScript.compile(env, filename, raw, true); + var body = res.body(); + + DebugHandler.get(env).onSourceLoad(filename, raw); + for (var el : res.all()) { + DebugHandler.get(env).onFunctionLoad(el.body(), el.map(mapper)); + } + + return new CodeFunction(env, filename.toString(), body, new Value[0][]); + } + catch (SyntaxException e) { + var res = EngineException.ofSyntax(e.msg); + res.add(env, e.loc.filename() + "", e.loc); + throw res; + } + }; + } + + public static Compiler wrap(Compiler first, Environment compilerEnv, Environment targetEnv, FunctionValue factory) { + var curr = new NativeFunction(args -> { + var filename = Filename.parse(args.get(0).toString(args.env)); + var src = args.get(1).toString(args.env); + var mapper = (FunctionValue)args.get(2); + return first.compile(targetEnv, filename, src, NativeMapper.unwrap(args.env, mapper)); + }); + + var next = (FunctionValue)factory.apply(compilerEnv, Value.UNDEFINED, curr); + + return (env, filename, source, map) -> { + return (FunctionValue)next.apply( + compilerEnv, Value.UNDEFINED, + StringValue.of(filename.toString()), + StringValue.of(source), + new NativeMapper(map) + ); + }; + } + + public static Compiler transpilerFromSource(Compiler prev, Environment target, Filename compilerName, String compilerSrc) { + var env = StdLib.apply(null); + + var glob = Value.global(env); + var compilerFactory = new FunctionValue[1]; + + glob.defineOwnField(env, "getResource", new NativeFunction(args -> { + var name = args.get(0).toString(args.env); + var src = Reading.resourceToString("lib/" + name); + + if (src == null) return Value.UNDEFINED; + else return StringValue.of(src); + })); + glob.defineOwnField(env, "register", new NativeFunction(args -> { + var func = (FunctionValue)args.get(0); + compilerFactory[0] = func; + return Value.UNDEFINED; + })); + glob.defineOwnField(env, "registerSource", new NativeFunction(args -> { + var filename = Filename.parse(args.get(0).toString(args.env)); + var src = args.get(1).toString(args.env); + DebugHandler.get(target).onSourceLoad(filename, src); + return Value.UNDEFINED; + })); + + var compiled = JavaScript.compile(compilerName, compilerSrc, false); + new CodeFunction(env, "intializer", compiled.body(), new Value[0][]).apply(env, Value.UNDEFINED); + + return wrap(prev, env, target, compilerFactory[0]); + } + + public static Compiler babelCompiler(Compiler prev, Environment target) { + return transpilerFromSource(prev, target, + new Filename(Metadata.name(), "babel.js"), + Reading.resourceToString("lib/babel.js") + ); + } + public static Compiler typescriptCompiler(Compiler prev, Environment target) { + return transpilerFromSource(prev, target, + new Filename(Metadata.name(), "typescript.js"), + Reading.resourceToString("lib/typescript.js") + ); + } + public static Compiler coffeescriptCompiler(Compiler prev, Environment target) { + return transpilerFromSource(prev, target, + new Filename(Metadata.name(), "coffee.js"), + Reading.resourceToString("lib/coffee.js") + ); + } + + public static interface TranspilerFactory { + Compiler create(Compiler prev, Environment target); + } + + public static Compiler chainTranspilers(Environment target, Compiler base, TranspilerFactory ...factories) { + var res = base; + + for (var el : factories) { + res = el.create(res, target); + } + + return res; + } +} diff --git a/repl/src/main/java/me/topchetoeu/j2s/repl/JSONConverter.java b/lib/src/main/java/me/topchetoeu/j2s/lib/JSONConverter.java similarity index 98% rename from repl/src/main/java/me/topchetoeu/j2s/repl/JSONConverter.java rename to lib/src/main/java/me/topchetoeu/j2s/lib/JSONConverter.java index 221e5f4..2fa0a79 100644 --- a/repl/src/main/java/me/topchetoeu/j2s/repl/JSONConverter.java +++ b/lib/src/main/java/me/topchetoeu/j2s/lib/JSONConverter.java @@ -1,4 +1,4 @@ -package me.topchetoeu.j2s.repl; +package me.topchetoeu.j2s.lib; import java.util.HashSet; import java.util.stream.Collectors; diff --git a/repl/src/main/java/me/topchetoeu/j2s/repl/mapping/NativeMapper.java b/lib/src/main/java/me/topchetoeu/j2s/lib/NativeMapper.java similarity index 98% rename from repl/src/main/java/me/topchetoeu/j2s/repl/mapping/NativeMapper.java rename to lib/src/main/java/me/topchetoeu/j2s/lib/NativeMapper.java index eae9b50..19aaff8 100644 --- a/repl/src/main/java/me/topchetoeu/j2s/repl/mapping/NativeMapper.java +++ b/lib/src/main/java/me/topchetoeu/j2s/lib/NativeMapper.java @@ -1,4 +1,4 @@ -package me.topchetoeu.j2s.repl.mapping; +package me.topchetoeu.j2s.lib; import java.util.function.Function; diff --git a/lib/src/main/java/me/topchetoeu/j2s/lib/Primordials.java b/lib/src/main/java/me/topchetoeu/j2s/lib/Primordials.java new file mode 100644 index 0000000..b072fc8 --- /dev/null +++ b/lib/src/main/java/me/topchetoeu/j2s/lib/Primordials.java @@ -0,0 +1,694 @@ +package me.topchetoeu.j2s.lib; + +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Optional; +import java.util.WeakHashMap; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +import me.topchetoeu.j2s.runtime.Compiler; +import me.topchetoeu.j2s.runtime.EventLoop; +import me.topchetoeu.j2s.common.Environment; +import me.topchetoeu.j2s.common.Filename; +import me.topchetoeu.j2s.common.Key; +import me.topchetoeu.j2s.common.Metadata; +import me.topchetoeu.j2s.common.SyntaxException; +import me.topchetoeu.j2s.compilation.json.JSON; +import me.topchetoeu.j2s.compilation.parsing.Parsing; +import me.topchetoeu.j2s.compilation.parsing.Source; +import me.topchetoeu.j2s.lib.buffers.Int32ArrayValue; +import me.topchetoeu.j2s.lib.buffers.Int8ArrayValue; +import me.topchetoeu.j2s.lib.buffers.TypedArrayValue; +import me.topchetoeu.j2s.lib.buffers.Uint8ArrayValue; +import me.topchetoeu.j2s.runtime.ArgumentsValue; +import me.topchetoeu.j2s.runtime.Frame; +import me.topchetoeu.j2s.runtime.exceptions.EngineException; +import me.topchetoeu.j2s.runtime.values.Value; +import me.topchetoeu.j2s.runtime.values.functions.FunctionValue; +import me.topchetoeu.j2s.runtime.values.functions.NativeFunction; +import me.topchetoeu.j2s.runtime.values.objects.ArrayLikeValue; +import me.topchetoeu.j2s.runtime.values.objects.ArrayValue; +import me.topchetoeu.j2s.runtime.values.objects.ObjectValue; +import me.topchetoeu.j2s.runtime.values.primitives.BoolValue; +import me.topchetoeu.j2s.runtime.values.primitives.StringValue; +import me.topchetoeu.j2s.runtime.values.primitives.SymbolValue; +import me.topchetoeu.j2s.runtime.values.primitives.UserValue; +import me.topchetoeu.j2s.runtime.values.primitives.VoidValue; +import me.topchetoeu.j2s.runtime.values.primitives.numbers.NumberValue; + +public class Primordials { + @SuppressWarnings("unchecked") + public static ObjectValue mapPrimordials(Environment env) { + var res = new ObjectValue(); + res.setPrototype(null, null); + + var prototype = new ObjectValue[1]; + NativeFunction mapConstr = new NativeFunction(args -> { + var isWeak = args.get(0).toBoolean(); + return UserValue.of(isWeak ? new WeakHashMap<>() : new LinkedHashMap<>(), prototype[0]); + }); + mapConstr.prototype.defineOwnField(env, "get", new NativeFunction(getArgs -> { + var map = getArgs.self(Map.class); + var key = getArgs.get(0); + var val = map.get(key); + return val == null ? Value.UNDEFINED : (Value)val; + })); + mapConstr.prototype.defineOwnField(env, "set", new NativeFunction(getArgs -> { + var map = getArgs.self(Map.class); + var key = getArgs.get(0); + var val = getArgs.get(1); + map.put(key, val); + + return Value.UNDEFINED; + })); + mapConstr.prototype.defineOwnField(env, "has", new NativeFunction(getArgs -> { + var map = getArgs.self(Map.class); + var key = getArgs.get(0); + return BoolValue.of(map.containsKey(key)); + })); + mapConstr.prototype.defineOwnField(env, "delete", new NativeFunction(getArgs -> { + var map = getArgs.self(Map.class); + var key = getArgs.get(0); + map.remove(key); + return Value.UNDEFINED; + })); + mapConstr.prototype.defineOwnField(env, "keys", new NativeFunction(getArgs -> { + var map = getArgs.self(Map.class); + return ArrayValue.of(map.keySet()); + })); + mapConstr.prototype.defineOwnField(env, "clear", new NativeFunction(getArgs -> { + getArgs.self(Map.class).clear(); + return Value.UNDEFINED; + })); + mapConstr.prototype.defineOwnField(env, "size", new NativeFunction(getArgs -> { + return NumberValue.of(getArgs.self(Map.class).size()); + })); + prototype[0] = (ObjectValue)mapConstr.prototype; + + return mapConstr; + } + + public static String processRegex(String src) { + var n = 0; + + var source = new StringBuilder(); + + StringBuilder bracesSource = null; + StringBuilder bracketsSource = null; + + while (true) { + if (n >= src.length()) break; + var c = src.charAt(n++); + + if (c == '\\' && n + 1 < src.length() && src.charAt(n) == 'b') { + c = '\b'; + n++; + } + + if (bracesSource != null) { + var failed = true; + + if (Character.isDigit(c)) { + bracesSource.append(c); + failed = false; + } + else if (c == ',' && bracesSource.indexOf(",") < 0) { + bracesSource.append(c); + failed = false; + } + else if (c == '}' && bracesSource.length() > 0) { + bracesSource.append(c); + source.append(bracesSource); + bracesSource = null; + continue; + } + + if (failed) { + source.append("\\"); + source.append(bracesSource); + bracesSource = null; + n--; + } + } + else if (bracketsSource != null) { + if (c == '[') bracketsSource.append("\\["); + else if (c == ']') { + var res = bracketsSource.append(']').toString(); + bracketsSource = null; + if (res.equals("[^]")) res = "[\\s\\S]"; + else if (res.equals("[]")) res = "[^\\s\\S]"; + source.append(res); + } + else if (c == '\\') { + if (n >= src.length()) break; + bracketsSource.append(c).append(src.charAt(n++)); + } + else bracketsSource.append(c); + } + else if (c == '\\') { + if (n >= src.length()) throw new PatternSyntaxException("Unexpected end", src, n); + c = src.charAt(n++); + source.append('\\').append(c); + } + else if (c == '[') { + bracketsSource = new StringBuilder("["); + } + else if (c == '{' && bracketsSource == null) { + bracesSource = new StringBuilder("{"); + } + else source.append(c); + } + + if (bracesSource != null) { + source.append("\\"); + source.append(bracesSource); + } + if (bracketsSource != null) throw new PatternSyntaxException("Unmatched '['", src, n - bracketsSource.length()); + + return source.toString(); + } + + public static ObjectValue regexPrimordials(Environment env) { + var res = new ObjectValue(); + res.setPrototype(null, null); + + var prototype = new ObjectValue[1]; + NativeFunction mapConstr = new NativeFunction(args -> { + var flags = 0; + if (args.get(1).toBoolean()) flags |= Pattern.MULTILINE; + if (args.get(2).toBoolean()) flags |= Pattern.CASE_INSENSITIVE; + if (args.get(3).toBoolean()) flags |= Pattern.DOTALL; + if (args.get(4).toBoolean()) flags |= Pattern.UNICODE_CASE | Pattern.CANON_EQ; + if (args.get(5).toBoolean()) flags |= Pattern.UNICODE_CHARACTER_CLASS; + try { + var pattern = Pattern.compile(processRegex(args.get(0).toString(args.env)), flags); + return UserValue.of(pattern, prototype[0]); + } + catch (PatternSyntaxException e) { + throw EngineException.ofSyntax("(regex):" + e.getIndex() + ": " + e.getDescription()); + } + }); + mapConstr.prototype.defineOwnField(env, "exec", new NativeFunction(args -> { + var pattern = args.self(Pattern.class); + var target = args.get(0).toString(args.env); + var offset = args.get(1).toNumber(args.env).getInt(); + var index = args.get(2).toBoolean(); + + if (offset > target.length()) return Value.NULL; + + var matcher = pattern.matcher(target).region(offset, target.length()); + if (!matcher.find()) return Value.NULL; + + var matchesArr = new ArrayValue(matcher.groupCount() + 1); + for (var i = 0; i < matcher.groupCount() + 1; i++) { + var group = matcher.group(i); + if (group == null) continue; + matchesArr.set(args.env, i, StringValue.of(group)); + } + + matchesArr.defineOwnField(args.env, "index", NumberValue.of(matcher.start())); + matchesArr.defineOwnField(args.env, "input", StringValue.of(target)); + if (index) { + var indices = new ArrayValue(); + indices.setPrototype(args.env, null); + for (var i = 0; i < matcher.groupCount(); i++) { + matchesArr.set(args.env, i, ArrayValue.of(Arrays.asList( + NumberValue.of(matcher.start(i)), + NumberValue.of(matcher.end(i)) + ))); + } + } + + var obj = new ObjectValue(); + obj.defineOwnField(args.env, "matches", matchesArr); + obj.defineOwnField(args.env, "end", NumberValue.of(matcher.end())); + + return obj; + // return val == null ? Value.UNDEFINED : (Value)val; + })); + mapConstr.prototype.defineOwnField(env, "groupCount", new NativeFunction(args -> { + var pattern = args.self(Pattern.class); + return NumberValue.of(pattern.matcher("").groupCount()); + })); + prototype[0] = (ObjectValue)mapConstr.prototype; + + return mapConstr; + } + + public static ObjectValue symbolPrimordials(Environment env) { + var res = new ObjectValue(); + res.setPrototype(null, null); + + res.defineOwnField(env, "makeSymbol", new NativeFunction(args -> new SymbolValue(args.get(0).toString(args.env)))); + res.defineOwnField(env, "getSymbol", new NativeFunction(args -> SymbolValue.get(args.get(0).toString(args.env)))); + res.defineOwnField(env, "getSymbolKey", new NativeFunction(args -> ((SymbolValue)args.get(0)).key())); + res.defineOwnField(env, "getSymbolDescriptor", new NativeFunction(args -> StringValue.of(((SymbolValue)args.get(0)).value))); + + return res; + } + + public static ObjectValue numberPrimordials(Environment env) { + var res = new ObjectValue(); + res.setPrototype(null, null); + + res.defineOwnField(env, "parseInt", new NativeFunction(args -> { + var nradix = args.get(1).toNumber(env); + var radix = nradix.isInt() ? nradix.getInt() : 10; + + if (radix != 10 && args.get(0) instanceof NumberValue num) { + if (num.isInt()) return num; + else return NumberValue.of(num.getDouble() - num.getDouble() % 1); + } + else { + if (radix < 2 || radix > 36) return NumberValue.NAN; + + var str = args.get(0).toString().trim(); + var numRes = Parsing.parseInt(new Source(str), 0, "0123456789abcdefghijklmnopqrstuvwxyz".substring(0, radix), true); + if (numRes.isSuccess()) { + if (numRes.n == str.length()) return NumberValue.of(numRes.result); + } + return NumberValue.NAN; + } + })); + res.defineOwnField(env, "parseFloat", new NativeFunction(args -> { + if (args.get(0) instanceof NumberValue) { + return args.get(0); + } + else { + var str = args.get(0).toString().trim(); + var numRes = Parsing.parseFloat(new Source(str), 0, true); + if (numRes.isSuccess()) { + if (numRes.n == str.length()) return NumberValue.of(numRes.result); + } + return NumberValue.NAN; + } + })); + res.defineOwnField(env, "isNaN", new NativeFunction(args -> BoolValue.of(args.get(0).isNaN()))); + + res.defineOwnField(env, "pow", new NativeFunction(args -> { + return NumberValue.of(Math.pow(args.get(0).toNumber(args.env).getDouble(), args.get(1).toNumber(args.env).getDouble())); + })); + res.defineOwnField(env, "log", new NativeFunction(args -> { + return NumberValue.of(Math.log(args.get(0).toNumber(args.env).getDouble())); + })); + + res.defineOwnField(env, "NaN", NumberValue.NAN); + res.defineOwnField(env, "Infinity", NumberValue.of(Double.POSITIVE_INFINITY)); + res.defineOwnField(env, "PI", NumberValue.of(Math.PI)); + res.defineOwnField(env, "E", NumberValue.of(Math.E)); + + return res; + } + + public static ObjectValue stringPrimordials(Environment env) { + var res = new ObjectValue(); + res.setPrototype(null, null); + + res.defineOwnField(env, "stringBuild", new NativeFunction(args -> { + var parts = ((ArrayValue)args.get(0)).toArray(); + var sb = new StringBuilder(); + + for (var i = 0; i < parts.length; i++) { + sb.append(((StringValue)parts[i]).value); + } + + return StringValue.of(sb.toString()); + })); + + res.defineOwnField(env, "fromCharCode", new NativeFunction(args -> { + return StringValue.of(new String(new char[] { (char)args.get(0).toNumber(args.env).getInt() })); + })); + + res.defineOwnField(env, "toCharCode", new NativeFunction(args -> { + return NumberValue.of(args.get(0).toString(args.env).charAt(0)); + })); + res.defineOwnField(env, "toCodePoint", new NativeFunction(args -> { + return NumberValue.of(args.get(0).toString(args.env).codePointAt(args.get(1).toNumber(args.env).getInt())); + })); + + res.defineOwnField(env, "substring", new NativeFunction(args -> { + var str = args.get(0).toString(args.env); + var start = args.get(1).toNumber(args.env).getInt(); + var end = args.get(2).toNumber(args.env).getInt(); + + if (end <= start) return StringValue.of(""); + + start = Math.max(Math.min(start, str.length()), 0); + end = Math.max(Math.min(end, str.length()), 0); + + return StringValue.of(str.substring(start, end)); + })); + + res.defineOwnField(env, "indexOf", new NativeFunction(args -> { + var str = args.get(0).toString(args.env); + var search = args.get(1).toString(args.env); + var start = args.get(2).toNumber(args.env).getInt(); + if (start > str.length()) return NumberValue.of(-1); + var reverse = args.get(3).toBoolean(); + + if (reverse) return NumberValue.of(str.lastIndexOf(search, start)); + else return NumberValue.of(str.indexOf(search, start)); + })); + + res.defineOwnField(env, "lower", new NativeFunction(args -> { + return StringValue.of(args.get(0).toString(args.env).toLowerCase()); + })); + res.defineOwnField(env, "upper", new NativeFunction(args -> { + return StringValue.of(args.get(0).toString(args.env).toUpperCase()); + })); + + return res; + } + + public static ObjectValue objectPrimordials(Environment env) { + var res = new ObjectValue(); + res.setPrototype(null, null); + + res.defineOwnField(env, "defineField", new NativeFunction(args -> { + var obj = (ObjectValue)args.get(0); + var key = args.get(1); + var desc = (ObjectValue)args.get(2); + + var valField = desc.getOwnMember(env, "v"); + var writeField = desc.getOwnMember(env, "w"); + var configField = desc.getOwnMember(env, "c"); + var enumField = desc.getOwnMember(env, "e"); + + var enumerable = enumField == null ? null : enumField.get(env, desc).toBoolean(); + var configurable = configField == null ? null : configField.get(env, desc).toBoolean(); + var writable = writeField == null ? null : writeField.get(env, desc).toBoolean(); + var value = valField == null ? null : valField.get(env, desc); + + return BoolValue.of(obj.defineOwnField(args.env, key, value, configurable, enumerable, writable)); + })); + res.defineOwnField(env, "defineProperty", new NativeFunction(args -> { + var obj = (ObjectValue)args.get(0); + var key = args.get(1); + var desc = args.get(2); + + var configField = desc.getOwnMember(env, "c"); + var enumField = desc.getOwnMember(env, "e"); + var getField = desc.getOwnMember(env, "g"); + var setField = desc.getOwnMember(env, "s"); + + var enumerable = enumField == null ? null : enumField.get(env, desc).toBoolean(); + var configurable = configField == null ? null : configField.get(env, desc).toBoolean(); + Optional getter = null, setter = null; + + if (getField != null) { + var getVal = getField.get(env, desc); + if (getVal == Value.UNDEFINED) getter = Optional.empty(); + else getter = Optional.of((FunctionValue)getVal); + } + if (setField != null) { + var setVal = setField.get(env, desc); + if (setVal == Value.UNDEFINED) setter = Optional.empty(); + else setter = Optional.of((FunctionValue)setVal); + } + + return BoolValue.of(obj.defineOwnProperty(args.env, key, getter, setter, configurable, enumerable)); + })); + res.defineOwnField(env, "getPrototype", new NativeFunction(args -> { + var proto = args.get(0).getPrototype(env); + if (proto == null) return Value.NULL; + else return proto; + })); + res.defineOwnField(env, "setPrototype", new NativeFunction(args -> { + var proto = args.get(1) instanceof VoidValue ? null : (ObjectValue)args.get(1); + args.get(0).setPrototype(env, proto); + return args.get(0); + })); + res.defineOwnField(env, "getMembers", new NativeFunction(args -> { + var val = new ArrayValue(); + + for (var key : args.get(0).getMembers(env, args.get(1).toBoolean(), args.get(2).toBoolean())) { + val.set(args.env, val.size(), StringValue.of(key)); + } + + return val; + })); + res.defineOwnField(env, "getSymbolMembers", new NativeFunction(args -> { + return ArrayValue.of(args.get(0).getSymbolMembers(env, args.get(1).toBoolean(), args.get(2).toBoolean())); + })); + res.defineOwnField(env, "getOwnMember", new NativeFunction(args -> { + var obj = args.get(0); + var key = args.get(1); + + var member = obj.getOwnMember(args.env, key); + if (member == null) return Value.UNDEFINED; + else return member.descriptor(args.env, obj); + })); + res.defineOwnField(env, "preventExt", new NativeFunction(args -> { + args.get(0).preventExtensions(); + return VoidValue.UNDEFINED; + })); + res.defineOwnField(env, "seal", new NativeFunction(args -> { + args.get(0).seal(); + return VoidValue.UNDEFINED; + })); + res.defineOwnField(env, "freeze", new NativeFunction(args -> { + args.get(0).freeze(); + return VoidValue.UNDEFINED; + })); + res.defineOwnField(env, "memcpy", new NativeFunction(args -> { + var src = (ArrayValue)args.get(0); + var dst = (ArrayValue)args.get(1); + var srcI = args.get(2).toNumber(args.env).getInt(); + var dstI = args.get(3).toNumber(args.env).getInt(); + var n = args.get(4).toNumber(args.env).getInt(); + + src.copyTo(dst, srcI, dstI, n); + + return VoidValue.UNDEFINED; + })); + res.defineOwnField(env, "sort", new NativeFunction(args -> { + var arr = (ArrayValue)args.get(0); + var func = (FunctionValue)args.get(1); + + arr.sort((a, b) -> { + return func.apply(args.env, Value.UNDEFINED, a, b).toNumber(args.env).getInt(); + }); + + return arr; + })); + + res.defineOwnField(env, "isArray", new NativeFunction(args -> { + return BoolValue.of(args.get(0) instanceof ArrayLikeValue); + })); + + return res; + } + + public static ObjectValue bufferPrimordials(Environment env) { + var buffProto = new ObjectValue(); + buffProto.defineOwnProperty(env, "length", Optional.of(new NativeFunction(args -> { + return NumberValue.of(args.self(byte[].class).length); + })), Optional.empty(), false, true); + + var res = new ObjectValue(); + res.setPrototype(null, null); + + res.defineOwnField(env, "buff", new NativeFunction(args -> { + var size = args.get(0).toNumber(env).getInt(); + return TypedArrayValue.buffer(new byte[size], buffProto); + })); + + res.defineOwnField(env, "uint8", new NativeFunction(args -> { + var buff = args.get(byte[].class, 0); + var start = args.get(1).toNumber(env).getInt(); + var end = args.get(2).toNumber(env).getInt(); + return new Uint8ArrayValue(buff, start, end); + })); + res.defineOwnField(env, "int8", new NativeFunction(args -> { + var buff = args.get(byte[].class, 0); + var start = args.get(1).toNumber(env).getInt(); + var end = args.get(2).toNumber(env).getInt(); + return new Int8ArrayValue(buff, start, end); + })); + res.defineOwnField(env, "int32", new NativeFunction(args -> { + var buff = args.get(byte[].class, 0); + var start = args.get(1).toNumber(env).getInt(); + var end = args.get(2).toNumber(env).getInt(); + return new Int32ArrayValue(buff, start, end); + })); + + res.defineOwnField(env, "isUint8", new NativeFunction(args -> { + return BoolValue.of(args.get(0) instanceof Uint8ArrayValue); + })); + res.defineOwnField(env, "isInt8", new NativeFunction(args -> { + return BoolValue.of(args.get(0) instanceof Int8ArrayValue); + })); + res.defineOwnField(env, "isInt32", new NativeFunction(args -> { + return BoolValue.of(args.get(0) instanceof Int32ArrayValue); + })); + + res.defineOwnField(env, "is", new NativeFunction(args -> { + return BoolValue.of(args.get(0) instanceof TypedArrayValue); + })); + res.defineOwnField(env, "isBuff", new NativeFunction(args -> { + return BoolValue.of(args.get(byte[].class, 0) != null); + })); + + res.defineOwnField(env, "backer", new NativeFunction(args -> { + return TypedArrayValue.buffer(((TypedArrayValue)args.get(0)).buffer, buffProto); + })); + res.defineOwnField(env, "start", new NativeFunction(args -> { + return NumberValue.of(((TypedArrayValue)args.get(0)).start); + })); + res.defineOwnField(env, "end", new NativeFunction(args -> { + return NumberValue.of(((TypedArrayValue)args.get(0)).end); + })); + + return res; + } + + public static ObjectValue functionPrimordials(Environment env) { + var res = new ObjectValue(); + res.setPrototype(null, null); + + res.defineOwnField(env, "setCallable", new NativeFunction(args -> { + var func = (FunctionValue)args.get(0); + func.enableApply = args.get(1).toBoolean(); + return Value.UNDEFINED; + })); + res.defineOwnField(env, "setConstructable", new NativeFunction(args -> { + var func = (FunctionValue)args.get(0); + func.enableConstruct = args.get(1).toBoolean(); + return Value.UNDEFINED; + })); + res.defineOwnField(env, "invokeType", new NativeFunction(args -> { + if (((ArgumentsValue)args.get(0)).frame.isNew) return StringValue.of("new"); + else return StringValue.of("call"); + })); + res.defineOwnField(env, "invokeTypeInfer", new NativeFunction(args -> { + var frame = Frame.get(args.env, args.get(0).toNumber(args.env).getInt()); + if (frame.isNew) return StringValue.of("new"); + else return StringValue.of("call"); + })); + res.defineOwnField(env, "target", new NativeFunction(args -> { + var frame = Frame.get(args.env, args.get(0).toNumber(args.env).getInt()); + if (frame.target == null) return Value.UNDEFINED; + else return frame.target; + })); + + res.defineOwnField(env, "invoke", new NativeFunction(args -> { + var func = (FunctionValue)args.get(0); + var self = args.get(1); + var funcArgs = (ArrayLikeValue)args.get(2); + + return func.apply(env, self, funcArgs.toArray()); + })); + res.defineOwnField(env, "construct", new NativeFunction(args -> { + var func = (FunctionValue)args.get(0); + var target = args.get(1); + var funcArgs = (ArrayLikeValue)args.get(2); + + if (target == Value.UNDEFINED) return func.constructNoSelf(env, funcArgs.toArray()); + else return func.construct(env, target, funcArgs.toArray()); + })); + + return res; + } + + public static ObjectValue jsonPrimordials(Environment env) { + var res = new ObjectValue(); + res.setPrototype(null, null); + + res.defineOwnField(env, "stringify", new NativeFunction(args -> { + return StringValue.of(JSON.stringify(JSONConverter.fromJs(env, args.get(0)))); + })); + res.defineOwnField(env, "parse", new NativeFunction(args -> { + try { + return JSONConverter.toJs(JSON.parse(null, args.get(0).toString(env))); + } + catch (SyntaxException e) { + throw EngineException.ofSyntax(e.msg).add(env, e.loc.filename() + "", e.loc); + } + })); + + return res; + } + + public static void setProto(Environment env, Environment target, Key key, ObjectValue repo, String name) { + var val = repo.getMember(env, name); + if (val instanceof ObjectValue obj) { + target.add(key, obj); + } + } + + public static ObjectValue create(Environment env) { + var res = new ObjectValue(); + res.setPrototype(null, null); + + res.defineOwnField(env, "symbol", symbolPrimordials(env)); + res.defineOwnField(env, "number", numberPrimordials(env)); + res.defineOwnField(env, "string", stringPrimordials(env)); + res.defineOwnField(env, "object", objectPrimordials(env)); + res.defineOwnField(env, "buffer", bufferPrimordials(env)); + res.defineOwnField(env, "function", functionPrimordials(env)); + res.defineOwnField(env, "json", jsonPrimordials(env)); + res.defineOwnField(env, "map", mapPrimordials(env)); + res.defineOwnField(env, "regex", regexPrimordials(env)); + + int[] i = new int[1]; + + res.defineOwnField(env, "setGlobalPrototypes", new NativeFunction(args -> { + var obj = (ObjectValue)args.get(0); + + setProto(args.env, env, Value.OBJECT_PROTO, obj, "object"); + setProto(args.env, env, Value.FUNCTION_PROTO, obj, "function"); + setProto(args.env, env, Value.ARRAY_PROTO, obj, "array"); + setProto(args.env, env, Value.BOOL_PROTO, obj, "boolean"); + setProto(args.env, env, Value.NUMBER_PROTO, obj, "number"); + setProto(args.env, env, Value.STRING_PROTO, obj, "string"); + setProto(args.env, env, Value.SYMBOL_PROTO, obj, "symbol"); + setProto(args.env, env, Value.ERROR_PROTO, obj, "error"); + setProto(args.env, env, Value.SYNTAX_ERR_PROTO, obj, "syntax"); + setProto(args.env, env, Value.TYPE_ERR_PROTO, obj, "type"); + setProto(args.env, env, Value.RANGE_ERR_PROTO, obj, "range"); + setProto(args.env, env, Value.UINT8_ARR_PROTO, obj, "uint8"); + setProto(args.env, env, Value.INT32_ARR_PROTO, obj, "int32"); + return Value.UNDEFINED; + })); + res.defineOwnField(env, "setIntrinsic", new NativeFunction(args -> { + var name = args.get(0).toString(env); + var val = args.get(1); + + Value.intrinsics(env).put(name, val); + + return Value.UNDEFINED; + })); + res.defineOwnField(env, "compile", new NativeFunction(args -> { + var nameVal = args.get(1); + var name = nameVal instanceof VoidValue ? + new Filename(Metadata.name(), "func" + i[0]++ + ".js") : + Filename.parse(nameVal.toString(args.env)); + + return Compiler.compileFunc(env, name, args.get(0).toString(env)); + })); + res.defineOwnField(env, "now", new NativeFunction(args -> { + return NumberValue.of(System.currentTimeMillis()); + })); + res.defineOwnField(env, "next", new NativeFunction(args -> { + var func = (FunctionValue)args.get(0); + EventLoop.get(env).pushMsg(() -> { + func.apply(env, Value.UNDEFINED); + }, true); + return Value.UNDEFINED; + })); + res.defineOwnField(env, "print", new NativeFunction(args -> { + for (var el : args.args) { + if (el instanceof StringValue) System.out.print(((StringValue)el).value + " \t"); + else System.out.print(el.toReadable(args.env) + " \t"); + } + System.out.println(); + + return Value.UNDEFINED; + })); + + return res; + } + +} diff --git a/lib/src/main/java/me/topchetoeu/j2s/lib/StdLib.java b/lib/src/main/java/me/topchetoeu/j2s/lib/StdLib.java new file mode 100644 index 0000000..d981bd7 --- /dev/null +++ b/lib/src/main/java/me/topchetoeu/j2s/lib/StdLib.java @@ -0,0 +1,29 @@ +package me.topchetoeu.j2s.lib; + +import me.topchetoeu.j2s.common.Environment; +import me.topchetoeu.j2s.common.Filename; +import me.topchetoeu.j2s.common.Metadata; +import me.topchetoeu.j2s.common.Reading; +import me.topchetoeu.j2s.compilation.CompileResult; +import me.topchetoeu.j2s.compilation.JavaScript; +import me.topchetoeu.j2s.runtime.values.Value; +import me.topchetoeu.j2s.runtime.values.functions.CodeFunction; + +public class StdLib { + private static final CompileResult RUNNER = JavaScript.compile(new Filename(Metadata.name(), "init.js"), Reading.resourceToString("lib/stdlib.js"), false); + + public static Environment apply(Environment env) { + if (env == null) { + env = new Environment(); + } + + var stubEnv = new Environment(); + Value.global(stubEnv).defineOwnField(stubEnv, "target", Value.global(env)); + Value.global(stubEnv).defineOwnField(stubEnv, "primordials", Primordials.create(env)); + + var func = new CodeFunction(stubEnv, "intializer", RUNNER.body(), new Value[0][]); + func.apply(stubEnv, Value.UNDEFINED); + + return env; + } +} diff --git a/repl/src/main/java/me/topchetoeu/j2s/repl/buffers/Int32ArrayValue.java b/lib/src/main/java/me/topchetoeu/j2s/lib/buffers/Int32ArrayValue.java similarity index 94% rename from repl/src/main/java/me/topchetoeu/j2s/repl/buffers/Int32ArrayValue.java rename to lib/src/main/java/me/topchetoeu/j2s/lib/buffers/Int32ArrayValue.java index aea022f..9bd530c 100644 --- a/repl/src/main/java/me/topchetoeu/j2s/repl/buffers/Int32ArrayValue.java +++ b/lib/src/main/java/me/topchetoeu/j2s/lib/buffers/Int32ArrayValue.java @@ -1,4 +1,4 @@ -package me.topchetoeu.j2s.repl.buffers; +package me.topchetoeu.j2s.lib.buffers; public final class Int32ArrayValue extends TypedArrayValue { @Override protected int onGet(int i) { diff --git a/repl/src/main/java/me/topchetoeu/j2s/repl/buffers/Int8ArrayValue.java b/lib/src/main/java/me/topchetoeu/j2s/lib/buffers/Int8ArrayValue.java similarity index 89% rename from repl/src/main/java/me/topchetoeu/j2s/repl/buffers/Int8ArrayValue.java rename to lib/src/main/java/me/topchetoeu/j2s/lib/buffers/Int8ArrayValue.java index d3a8fa5..13da40b 100644 --- a/repl/src/main/java/me/topchetoeu/j2s/repl/buffers/Int8ArrayValue.java +++ b/lib/src/main/java/me/topchetoeu/j2s/lib/buffers/Int8ArrayValue.java @@ -1,4 +1,4 @@ -package me.topchetoeu.j2s.repl.buffers; +package me.topchetoeu.j2s.lib.buffers; public final class Int8ArrayValue extends TypedArrayValue { @Override protected int onGet(int i) { diff --git a/repl/src/main/java/me/topchetoeu/j2s/repl/buffers/TypedArrayValue.java b/lib/src/main/java/me/topchetoeu/j2s/lib/buffers/TypedArrayValue.java similarity index 97% rename from repl/src/main/java/me/topchetoeu/j2s/repl/buffers/TypedArrayValue.java rename to lib/src/main/java/me/topchetoeu/j2s/lib/buffers/TypedArrayValue.java index db1d6d5..9cf1669 100644 --- a/repl/src/main/java/me/topchetoeu/j2s/repl/buffers/TypedArrayValue.java +++ b/lib/src/main/java/me/topchetoeu/j2s/lib/buffers/TypedArrayValue.java @@ -1,4 +1,4 @@ -package me.topchetoeu.j2s.repl.buffers; +package me.topchetoeu.j2s.lib.buffers; import java.util.WeakHashMap; diff --git a/repl/src/main/java/me/topchetoeu/j2s/repl/buffers/Uint8ArrayValue.java b/lib/src/main/java/me/topchetoeu/j2s/lib/buffers/Uint8ArrayValue.java similarity index 91% rename from repl/src/main/java/me/topchetoeu/j2s/repl/buffers/Uint8ArrayValue.java rename to lib/src/main/java/me/topchetoeu/j2s/lib/buffers/Uint8ArrayValue.java index dd2590a..1a8396b 100644 --- a/repl/src/main/java/me/topchetoeu/j2s/repl/buffers/Uint8ArrayValue.java +++ b/lib/src/main/java/me/topchetoeu/j2s/lib/buffers/Uint8ArrayValue.java @@ -1,4 +1,4 @@ -package me.topchetoeu.j2s.repl.buffers; +package me.topchetoeu.j2s.lib.buffers; public final class Uint8ArrayValue extends TypedArrayValue { @Override protected int onGet(int i) { diff --git a/lib/src/stdlib/_entry.ts b/lib/src/stdlib/_entry.ts index e2dab74..695d7a2 100644 --- a/lib/src/stdlib/_entry.ts +++ b/lib/src/stdlib/_entry.ts @@ -1,4 +1,4 @@ -import { object, setGlobalPrototypes, setIntrinsic, target } from "./primordials.ts"; +import { now, object, print, setGlobalPrototypes, setIntrinsic, target } from "./primordials.ts"; import { Error, RangeError, SyntaxError, TypeError } from "./values/errors.ts"; import { Boolean } from "./values/boolean.ts"; import { Function } from "./values/function.ts"; @@ -21,11 +21,6 @@ import { Uint8Array } from "./arrays/Uint8Array.ts"; import { Int32Array } from "./arrays/Int32Array.ts"; import { TypedArray } from "./arrays/TypedArray.ts"; -declare global { - function print(...args: any[]): void; - function measure(func: Function): void; -} - function fixup(clazz: T) { object.setPrototype(clazz, Function.prototype); object.setPrototype(clazz.prototype as any, Object.prototype); @@ -72,6 +67,16 @@ target.NaN = Number.NaN; target.Infinity = Number.POSITIVE_INFINITY; target.encodeURI = encodeURI; target.encodeURIComponent = encodeURIComponent; +target.print = print; +target.measure = (func: () => void) => { + const start = now(); + try { + return func(); + } + finally { + print(`Took ${now() - start}ms`); + } +}; setGlobalPrototypes({ string: String.prototype, diff --git a/lib/src/stdlib/namespaces/console.ts b/lib/src/stdlib/namespaces/console.ts index ca18d17..0e1a638 100644 --- a/lib/src/stdlib/namespaces/console.ts +++ b/lib/src/stdlib/namespaces/console.ts @@ -1,4 +1,4 @@ -import { func, json, object } from "../primordials.ts"; +import { func, object, print } from "../primordials.ts"; export const console = {}; diff --git a/lib/src/stdlib/primordials.ts b/lib/src/stdlib/primordials.ts index 29f223c..0cd54a2 100644 --- a/lib/src/stdlib/primordials.ts +++ b/lib/src/stdlib/primordials.ts @@ -111,6 +111,7 @@ export interface Primordials { next(func: () => void): void; schedule(func: () => void, delay: number): () => void; setIntrinsic(key: string, val: any): void; + print(...args: any[]): void; } // prevent optimization to "undefined", which doesn't exist yet @@ -134,6 +135,7 @@ export const { next, schedule, setIntrinsic, + print, } = primordials; export type regex = InstanceType; \ No newline at end of file diff --git a/lib/src/transpiler/_entry.ts b/lib/src/transpiler/_entry-babel.ts similarity index 100% rename from lib/src/transpiler/_entry.ts rename to lib/src/transpiler/_entry-babel.ts diff --git a/lib/src/transpiler/_entry-coffee.ts b/lib/src/transpiler/_entry-coffee.ts new file mode 100644 index 0000000..a609c61 --- /dev/null +++ b/lib/src/transpiler/_entry-coffee.ts @@ -0,0 +1,3 @@ +import coffee from "./coffeescript.ts"; + +register(coffee); diff --git a/lib/src/transpiler/_entry-typescript.ts b/lib/src/transpiler/_entry-typescript.ts new file mode 100644 index 0000000..4167078 --- /dev/null +++ b/lib/src/transpiler/_entry-typescript.ts @@ -0,0 +1,3 @@ +import ts from "./typescript.ts"; + +register(ts); diff --git a/lib/src/transpiler/babel.ts b/lib/src/transpiler/babel.ts index adafa92..6b9931d 100644 --- a/lib/src/transpiler/babel.ts +++ b/lib/src/transpiler/babel.ts @@ -47,6 +47,7 @@ export default function babel(next: Compiler): Compiler { availablePlugins["transform-exponentiation-operator"], // ES2015 + availablePlugins["transform-regenerator"], availablePlugins["transform-arrow-functions"], availablePlugins["transform-block-scoping"], availablePlugins["transform-classes"], diff --git a/repl/build.gradle.kts b/repl/build.gradle.kts index 434842c..5251db3 100644 --- a/repl/build.gradle.kts +++ b/repl/build.gradle.kts @@ -5,15 +5,6 @@ plugins { description = "A simple REPL for the interpreter, can be used for simple prototyping"; -tasks.processResources { - filesMatching("metadata.json", { - expand( - "version" to properties["project_version"], - "name" to properties["project_name"], - ); - }); -} - tasks.test { useJUnitPlatform(); } diff --git a/repl/src/main/java/me/topchetoeu/j2s/repl/SimpleRepl.java b/repl/src/main/java/me/topchetoeu/j2s/repl/SimpleRepl.java index 0f76ad0..6054eaf 100644 --- a/repl/src/main/java/me/topchetoeu/j2s/repl/SimpleRepl.java +++ b/repl/src/main/java/me/topchetoeu/j2s/repl/SimpleRepl.java @@ -6,15 +6,8 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetSocketAddress; -import java.util.Arrays; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Optional; -import java.util.WeakHashMap; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; -import java.util.regex.Pattern; -import java.util.regex.PatternSyntaxException; import me.topchetoeu.j2s.common.Environment; import me.topchetoeu.j2s.common.Filename; @@ -22,63 +15,24 @@ import me.topchetoeu.j2s.common.Key; import me.topchetoeu.j2s.common.Metadata; import me.topchetoeu.j2s.common.Reading; import me.topchetoeu.j2s.common.SyntaxException; -import me.topchetoeu.j2s.compilation.JavaScript; -import me.topchetoeu.j2s.compilation.json.JSON; -import me.topchetoeu.j2s.compilation.parsing.Parsing; -import me.topchetoeu.j2s.compilation.parsing.Source; -import me.topchetoeu.j2s.repl.buffers.Int32ArrayValue; -import me.topchetoeu.j2s.repl.buffers.Int8ArrayValue; -import me.topchetoeu.j2s.repl.buffers.TypedArrayValue; -import me.topchetoeu.j2s.repl.buffers.Uint8ArrayValue; +import me.topchetoeu.j2s.lib.Compilers; +import me.topchetoeu.j2s.lib.StdLib; import me.topchetoeu.j2s.repl.debug.SimpleDebugHandler; import me.topchetoeu.j2s.repl.debug.DebugServer; import me.topchetoeu.j2s.repl.debug.Debugger; import me.topchetoeu.j2s.repl.debug.SimpleDebugger; -import me.topchetoeu.j2s.repl.mapping.NativeMapper; -import me.topchetoeu.j2s.runtime.ArgumentsValue; import me.topchetoeu.j2s.runtime.Compiler; import me.topchetoeu.j2s.runtime.Engine; import me.topchetoeu.j2s.runtime.EventLoop; -import me.topchetoeu.j2s.runtime.Frame; import me.topchetoeu.j2s.runtime.debug.DebugHandler; import me.topchetoeu.j2s.runtime.exceptions.EngineException; import me.topchetoeu.j2s.runtime.values.Value; -import me.topchetoeu.j2s.runtime.values.functions.CodeFunction; -import me.topchetoeu.j2s.runtime.values.functions.FunctionValue; import me.topchetoeu.j2s.runtime.values.functions.NativeFunction; -import me.topchetoeu.j2s.runtime.values.objects.ArrayLikeValue; -import me.topchetoeu.j2s.runtime.values.objects.ArrayValue; -import me.topchetoeu.j2s.runtime.values.objects.ObjectValue; -import me.topchetoeu.j2s.runtime.values.primitives.BoolValue; -import me.topchetoeu.j2s.runtime.values.primitives.StringValue; -import me.topchetoeu.j2s.runtime.values.primitives.SymbolValue; -import me.topchetoeu.j2s.runtime.values.primitives.UserValue; -import me.topchetoeu.j2s.runtime.values.primitives.VoidValue; -import me.topchetoeu.j2s.runtime.values.primitives.numbers.NumberValue; public class SimpleRepl { - public static final Compiler DEFAULT_COMPILER = (env, filename, raw, mapper) -> { - try { - var res = JavaScript.compile(env, filename, raw, true); - var body = res.body(); - - DebugHandler.get(env).onSourceLoad(filename, raw); - for (var el : res.all()) { - DebugHandler.get(env).onFunctionLoad(el.body(), el.map(mapper)); - } - - return new CodeFunction(env, filename.toString(), body, new Value[0][]); - } - catch (SyntaxException e) { - var res = EngineException.ofSyntax(e.msg); - res.add(env, e.loc.filename() + "", e.loc); - throw res; - } - }; - static Thread engineTask, debugTask; static Engine engine = new Engine(); - static Environment environment = Environment.empty(), tsEnvironment; + static Environment environment = Environment.empty(); static DebugServer server; static Debugger debugger; static Key STDOUT = new Key<>(); @@ -90,28 +44,12 @@ public class SimpleRepl { private static void reader() { try { - try { - environment = createESEnv(); - tsEnvironment = createESEnv(); - } - catch (ExecutionException e) { throw e.getCause(); } - server = new DebugServer(); debugTask = server.start(new InetSocketAddress("127.0.0.1", 9229), true); server.targets.put("default", (socket, req) -> new SimpleDebugger(socket) .attach((SimpleDebugHandler)DebugHandler.get(environment)) - .attach((SimpleDebugHandler)DebugHandler.get(tsEnvironment)) ); - try { - try { initGlobals(); } catch (ExecutionException e) { throw e.getCause(); } - } - catch (EngineException | SyntaxException e) { - System.err.println("Failed to load stdlib. Falling back to barebones environment..."); - System.err.println(Value.errorToReadable(environment, e, null)); - } - - System.out.println(String.format("Running %s v%s by %s", Metadata.name(), Metadata.version(), Metadata.author())); for (var arg : args) { @@ -165,691 +103,12 @@ public class SimpleRepl { } } - @SuppressWarnings("unchecked") - private static ObjectValue mapPrimordials(Environment env) { - var res = new ObjectValue(); - res.setPrototype(null, null); - - var prototype = new ObjectValue[1]; - NativeFunction mapConstr = new NativeFunction(args -> { - var isWeak = args.get(0).toBoolean(); - return UserValue.of(isWeak ? new WeakHashMap<>() : new LinkedHashMap<>(), prototype[0]); - }); - mapConstr.prototype.defineOwnField(env, "get", new NativeFunction(getArgs -> { - var map = getArgs.self(Map.class); - var key = getArgs.get(0); - var val = map.get(key); - return val == null ? Value.UNDEFINED : (Value)val; - })); - mapConstr.prototype.defineOwnField(env, "set", new NativeFunction(getArgs -> { - var map = getArgs.self(Map.class); - var key = getArgs.get(0); - var val = getArgs.get(1); - map.put(key, val); - - return Value.UNDEFINED; - })); - mapConstr.prototype.defineOwnField(env, "has", new NativeFunction(getArgs -> { - var map = getArgs.self(Map.class); - var key = getArgs.get(0); - return BoolValue.of(map.containsKey(key)); - })); - mapConstr.prototype.defineOwnField(env, "delete", new NativeFunction(getArgs -> { - var map = getArgs.self(Map.class); - var key = getArgs.get(0); - map.remove(key); - return Value.UNDEFINED; - })); - mapConstr.prototype.defineOwnField(env, "keys", new NativeFunction(getArgs -> { - var map = getArgs.self(Map.class); - return ArrayValue.of(map.keySet()); - })); - mapConstr.prototype.defineOwnField(env, "clear", new NativeFunction(getArgs -> { - getArgs.self(Map.class).clear(); - return Value.UNDEFINED; - })); - mapConstr.prototype.defineOwnField(env, "size", new NativeFunction(getArgs -> { - return NumberValue.of(getArgs.self(Map.class).size()); - })); - prototype[0] = (ObjectValue)mapConstr.prototype; - - return mapConstr; - } - - public static String processRegex(String src) { - var n = 0; - - var source = new StringBuilder(); - - StringBuilder bracesSource = null; - StringBuilder bracketsSource = null; - - while (true) { - if (n >= src.length()) break; - var c = src.charAt(n++); - - if (c == '\\' && n + 1 < src.length() && src.charAt(n) == 'b') { - c = '\b'; - n++; - } - - if (bracesSource != null) { - var failed = true; - - if (Character.isDigit(c)) { - bracesSource.append(c); - failed = false; - } - else if (c == ',' && bracesSource.indexOf(",") < 0) { - bracesSource.append(c); - failed = false; - } - else if (c == '}' && bracesSource.length() > 0) { - bracesSource.append(c); - source.append(bracesSource); - bracesSource = null; - continue; - } - - if (failed) { - source.append("\\"); - source.append(bracesSource); - bracesSource = null; - n--; - } - } - else if (bracketsSource != null) { - if (c == '[') bracketsSource.append("\\["); - else if (c == ']') { - var res = bracketsSource.append(']').toString(); - bracketsSource = null; - if (res.equals("[^]")) res = "[\\s\\S]"; - else if (res.equals("[]")) res = "[^\\s\\S]"; - source.append(res); - } - else if (c == '\\') { - if (n >= src.length()) break; - bracketsSource.append(c).append(src.charAt(n++)); - } - else bracketsSource.append(c); - } - else if (c == '\\') { - if (n >= src.length()) throw new PatternSyntaxException("Unexpected end", src, n); - c = src.charAt(n++); - source.append('\\').append(c); - } - else if (c == '[') { - bracketsSource = new StringBuilder("["); - } - else if (c == '{' && bracketsSource == null) { - bracesSource = new StringBuilder("{"); - } - else source.append(c); - } - - if (bracesSource != null) { - source.append("\\"); - source.append(bracesSource); - } - if (bracketsSource != null) throw new PatternSyntaxException("Unmatched '['", src, n - bracketsSource.length()); - - return source.toString(); - } - - private static ObjectValue regexPrimordials(Environment env) { - var res = new ObjectValue(); - res.setPrototype(null, null); - - var prototype = new ObjectValue[1]; - NativeFunction mapConstr = new NativeFunction(args -> { - var flags = 0; - if (args.get(1).toBoolean()) flags |= Pattern.MULTILINE; - if (args.get(2).toBoolean()) flags |= Pattern.CASE_INSENSITIVE; - if (args.get(3).toBoolean()) flags |= Pattern.DOTALL; - if (args.get(4).toBoolean()) flags |= Pattern.UNICODE_CASE | Pattern.CANON_EQ; - if (args.get(5).toBoolean()) flags |= Pattern.UNICODE_CHARACTER_CLASS; - try { - var pattern = Pattern.compile(processRegex(args.get(0).toString(args.env)), flags); - return UserValue.of(pattern, prototype[0]); - } - catch (PatternSyntaxException e) { - throw EngineException.ofSyntax("(regex):" + e.getIndex() + ": " + e.getDescription()); - } - }); - mapConstr.prototype.defineOwnField(env, "exec", new NativeFunction(args -> { - var pattern = args.self(Pattern.class); - var target = args.get(0).toString(args.env); - var offset = args.get(1).toNumber(args.env).getInt(); - var index = args.get(2).toBoolean(); - - if (offset > target.length()) return Value.NULL; - - var matcher = pattern.matcher(target).region(offset, target.length()); - if (!matcher.find()) return Value.NULL; - - var matchesArr = new ArrayValue(matcher.groupCount() + 1); - for (var i = 0; i < matcher.groupCount() + 1; i++) { - var group = matcher.group(i); - if (group == null) continue; - matchesArr.set(args.env, i, StringValue.of(group)); - } - - matchesArr.defineOwnField(args.env, "index", NumberValue.of(matcher.start())); - matchesArr.defineOwnField(args.env, "input", StringValue.of(target)); - if (index) { - var indices = new ArrayValue(); - indices.setPrototype(args.env, null); - for (var i = 0; i < matcher.groupCount(); i++) { - matchesArr.set(args.env, i, ArrayValue.of(Arrays.asList( - NumberValue.of(matcher.start(i)), - NumberValue.of(matcher.end(i)) - ))); - } - } - - var obj = new ObjectValue(); - obj.defineOwnField(args.env, "matches", matchesArr); - obj.defineOwnField(args.env, "end", NumberValue.of(matcher.end())); - - return obj; - // return val == null ? Value.UNDEFINED : (Value)val; - })); - mapConstr.prototype.defineOwnField(env, "groupCount", new NativeFunction(args -> { - var pattern = args.self(Pattern.class); - return NumberValue.of(pattern.matcher("").groupCount()); - })); - prototype[0] = (ObjectValue)mapConstr.prototype; - - return mapConstr; - } - - private static ObjectValue symbolPrimordials(Environment env) { - var res = new ObjectValue(); - res.setPrototype(null, null); - - res.defineOwnField(env, "makeSymbol", new NativeFunction(args -> new SymbolValue(args.get(0).toString(args.env)))); - res.defineOwnField(env, "getSymbol", new NativeFunction(args -> SymbolValue.get(args.get(0).toString(args.env)))); - res.defineOwnField(env, "getSymbolKey", new NativeFunction(args -> ((SymbolValue)args.get(0)).key())); - res.defineOwnField(env, "getSymbolDescriptor", new NativeFunction(args -> StringValue.of(((SymbolValue)args.get(0)).value))); - - return res; - } - - private static ObjectValue numberPrimordials(Environment env) { - var res = new ObjectValue(); - res.setPrototype(null, null); - - res.defineOwnField(env, "parseInt", new NativeFunction(args -> { - var nradix = args.get(1).toNumber(env); - var radix = nradix.isInt() ? nradix.getInt() : 10; - - if (radix != 10 && args.get(0) instanceof NumberValue num) { - if (num.isInt()) return num; - else return NumberValue.of(num.getDouble() - num.getDouble() % 1); - } - else { - if (radix < 2 || radix > 36) return NumberValue.NAN; - - var str = args.get(0).toString().trim(); - var numRes = Parsing.parseInt(new Source(str), 0, "0123456789abcdefghijklmnopqrstuvwxyz".substring(0, radix), true); - if (numRes.isSuccess()) { - if (numRes.n == str.length()) return NumberValue.of(numRes.result); - } - return NumberValue.NAN; - } - })); - res.defineOwnField(env, "parseFloat", new NativeFunction(args -> { - if (args.get(0) instanceof NumberValue) { - return args.get(0); - } - else { - var str = args.get(0).toString().trim(); - var numRes = Parsing.parseFloat(new Source(str), 0, true); - if (numRes.isSuccess()) { - if (numRes.n == str.length()) return NumberValue.of(numRes.result); - } - return NumberValue.NAN; - } - })); - res.defineOwnField(env, "isNaN", new NativeFunction(args -> BoolValue.of(args.get(0).isNaN()))); - - res.defineOwnField(env, "pow", new NativeFunction(args -> { - return NumberValue.of(Math.pow(args.get(0).toNumber(args.env).getDouble(), args.get(1).toNumber(args.env).getDouble())); - })); - res.defineOwnField(env, "log", new NativeFunction(args -> { - return NumberValue.of(Math.log(args.get(0).toNumber(args.env).getDouble())); - })); - - res.defineOwnField(env, "NaN", NumberValue.NAN); - res.defineOwnField(env, "Infinity", NumberValue.of(Double.POSITIVE_INFINITY)); - res.defineOwnField(env, "PI", NumberValue.of(Math.PI)); - res.defineOwnField(env, "E", NumberValue.of(Math.E)); - - return res; - } - - private static ObjectValue stringPrimordials(Environment env) { - var res = new ObjectValue(); - res.setPrototype(null, null); - - res.defineOwnField(env, "stringBuild", new NativeFunction(args -> { - var parts = ((ArrayValue)args.get(0)).toArray(); - var sb = new StringBuilder(); - - for (var i = 0; i < parts.length; i++) { - sb.append(((StringValue)parts[i]).value); - } - - return StringValue.of(sb.toString()); - })); - - res.defineOwnField(env, "fromCharCode", new NativeFunction(args -> { - return StringValue.of(new String(new char[] { (char)args.get(0).toNumber(args.env).getInt() })); - })); - - res.defineOwnField(env, "toCharCode", new NativeFunction(args -> { - return NumberValue.of(args.get(0).toString(args.env).charAt(0)); - })); - res.defineOwnField(env, "toCodePoint", new NativeFunction(args -> { - return NumberValue.of(args.get(0).toString(args.env).codePointAt(args.get(1).toNumber(args.env).getInt())); - })); - - res.defineOwnField(env, "substring", new NativeFunction(args -> { - var str = args.get(0).toString(args.env); - var start = args.get(1).toNumber(args.env).getInt(); - var end = args.get(2).toNumber(args.env).getInt(); - - if (end <= start) return StringValue.of(""); - - start = Math.max(Math.min(start, str.length()), 0); - end = Math.max(Math.min(end, str.length()), 0); - - return StringValue.of(str.substring(start, end)); - })); - - res.defineOwnField(env, "indexOf", new NativeFunction(args -> { - var str = args.get(0).toString(args.env); - var search = args.get(1).toString(args.env); - var start = args.get(2).toNumber(args.env).getInt(); - if (start > str.length()) return NumberValue.of(-1); - var reverse = args.get(3).toBoolean(); - - if (reverse) return NumberValue.of(str.lastIndexOf(search, start)); - else return NumberValue.of(str.indexOf(search, start)); - })); - - res.defineOwnField(env, "lower", new NativeFunction(args -> { - return StringValue.of(args.get(0).toString(args.env).toLowerCase()); - })); - res.defineOwnField(env, "upper", new NativeFunction(args -> { - return StringValue.of(args.get(0).toString(args.env).toUpperCase()); - })); - - return res; - } - - private static ObjectValue objectPrimordials(Environment env) { - var res = new ObjectValue(); - res.setPrototype(null, null); - - res.defineOwnField(env, "defineField", new NativeFunction(args -> { - var obj = (ObjectValue)args.get(0); - var key = args.get(1); - var desc = (ObjectValue)args.get(2); - - var valField = desc.getOwnMember(env, "v"); - var writeField = desc.getOwnMember(env, "w"); - var configField = desc.getOwnMember(env, "c"); - var enumField = desc.getOwnMember(env, "e"); - - var enumerable = enumField == null ? null : enumField.get(env, desc).toBoolean(); - var configurable = configField == null ? null : configField.get(env, desc).toBoolean(); - var writable = writeField == null ? null : writeField.get(env, desc).toBoolean(); - var value = valField == null ? null : valField.get(env, desc); - - return BoolValue.of(obj.defineOwnField(args.env, key, value, configurable, enumerable, writable)); - })); - res.defineOwnField(env, "defineProperty", new NativeFunction(args -> { - var obj = (ObjectValue)args.get(0); - var key = args.get(1); - var desc = args.get(2); - - var configField = desc.getOwnMember(env, "c"); - var enumField = desc.getOwnMember(env, "e"); - var getField = desc.getOwnMember(env, "g"); - var setField = desc.getOwnMember(env, "s"); - - var enumerable = enumField == null ? null : enumField.get(env, desc).toBoolean(); - var configurable = configField == null ? null : configField.get(env, desc).toBoolean(); - Optional getter = null, setter = null; - - if (getField != null) { - var getVal = getField.get(env, desc); - if (getVal == Value.UNDEFINED) getter = Optional.empty(); - else getter = Optional.of((FunctionValue)getVal); - } - if (setField != null) { - var setVal = setField.get(env, desc); - if (setVal == Value.UNDEFINED) setter = Optional.empty(); - else setter = Optional.of((FunctionValue)setVal); - } - - return BoolValue.of(obj.defineOwnProperty(args.env, key, getter, setter, configurable, enumerable)); - })); - res.defineOwnField(env, "getPrototype", new NativeFunction(args -> { - var proto = args.get(0).getPrototype(env); - if (proto == null) return Value.NULL; - else return proto; - })); - res.defineOwnField(env, "setPrototype", new NativeFunction(args -> { - var proto = args.get(1) instanceof VoidValue ? null : (ObjectValue)args.get(1); - args.get(0).setPrototype(env, proto); - return args.get(0); - })); - res.defineOwnField(env, "getMembers", new NativeFunction(args -> { - var val = new ArrayValue(); - - for (var key : args.get(0).getMembers(env, args.get(1).toBoolean(), args.get(2).toBoolean())) { - val.set(args.env, val.size(), StringValue.of(key)); - } - - return val; - })); - res.defineOwnField(env, "getSymbolMembers", new NativeFunction(args -> { - return ArrayValue.of(args.get(0).getSymbolMembers(env, args.get(1).toBoolean(), args.get(2).toBoolean())); - })); - res.defineOwnField(env, "getOwnMember", new NativeFunction(args -> { - var obj = args.get(0); - var key = args.get(1); - - var member = obj.getOwnMember(args.env, key); - if (member == null) return Value.UNDEFINED; - else return member.descriptor(args.env, obj); - })); - res.defineOwnField(env, "preventExt", new NativeFunction(args -> { - args.get(0).preventExtensions(); - return VoidValue.UNDEFINED; - })); - res.defineOwnField(env, "seal", new NativeFunction(args -> { - args.get(0).seal(); - return VoidValue.UNDEFINED; - })); - res.defineOwnField(env, "freeze", new NativeFunction(args -> { - args.get(0).freeze(); - return VoidValue.UNDEFINED; - })); - res.defineOwnField(env, "memcpy", new NativeFunction(args -> { - var src = (ArrayValue)args.get(0); - var dst = (ArrayValue)args.get(1); - var srcI = args.get(2).toNumber(args.env).getInt(); - var dstI = args.get(3).toNumber(args.env).getInt(); - var n = args.get(4).toNumber(args.env).getInt(); - - src.copyTo(dst, srcI, dstI, n); - - return VoidValue.UNDEFINED; - })); - res.defineOwnField(env, "sort", new NativeFunction(args -> { - var arr = (ArrayValue)args.get(0); - var func = (FunctionValue)args.get(1); - - arr.sort((a, b) -> { - return func.apply(args.env, Value.UNDEFINED, a, b).toNumber(args.env).getInt(); - }); - - return arr; - })); - - res.defineOwnField(env, "isArray", new NativeFunction(args -> { - return BoolValue.of(args.get(0) instanceof ArrayLikeValue); - })); - - return res; - } - - private static ObjectValue bufferPrimordials(Environment env) { - var buffProto = new ObjectValue(); - buffProto.defineOwnProperty(env, "length", Optional.of(new NativeFunction(args -> { - return NumberValue.of(args.self(byte[].class).length); - })), Optional.empty(), false, true); - - var res = new ObjectValue(); - res.setPrototype(null, null); - - res.defineOwnField(env, "buff", new NativeFunction(args -> { - var size = args.get(0).toNumber(env).getInt(); - return TypedArrayValue.buffer(new byte[size], buffProto); - })); - - res.defineOwnField(env, "uint8", new NativeFunction(args -> { - var buff = args.get(byte[].class, 0); - var start = args.get(1).toNumber(env).getInt(); - var end = args.get(2).toNumber(env).getInt(); - return new Uint8ArrayValue(buff, start, end); - })); - res.defineOwnField(env, "int8", new NativeFunction(args -> { - var buff = args.get(byte[].class, 0); - var start = args.get(1).toNumber(env).getInt(); - var end = args.get(2).toNumber(env).getInt(); - return new Int8ArrayValue(buff, start, end); - })); - res.defineOwnField(env, "int32", new NativeFunction(args -> { - var buff = args.get(byte[].class, 0); - var start = args.get(1).toNumber(env).getInt(); - var end = args.get(2).toNumber(env).getInt(); - return new Int32ArrayValue(buff, start, end); - })); - - res.defineOwnField(env, "isUint8", new NativeFunction(args -> { - return BoolValue.of(args.get(0) instanceof Uint8ArrayValue); - })); - res.defineOwnField(env, "isInt8", new NativeFunction(args -> { - return BoolValue.of(args.get(0) instanceof Int8ArrayValue); - })); - res.defineOwnField(env, "isInt32", new NativeFunction(args -> { - return BoolValue.of(args.get(0) instanceof Int32ArrayValue); - })); - - res.defineOwnField(env, "is", new NativeFunction(args -> { - return BoolValue.of(args.get(0) instanceof TypedArrayValue); - })); - res.defineOwnField(env, "isBuff", new NativeFunction(args -> { - return BoolValue.of(args.get(byte[].class, 0) != null); - })); - - res.defineOwnField(env, "backer", new NativeFunction(args -> { - return TypedArrayValue.buffer(((TypedArrayValue)args.get(0)).buffer, buffProto); - })); - res.defineOwnField(env, "start", new NativeFunction(args -> { - return NumberValue.of(((TypedArrayValue)args.get(0)).start); - })); - res.defineOwnField(env, "end", new NativeFunction(args -> { - return NumberValue.of(((TypedArrayValue)args.get(0)).end); - })); - - return res; - } - - private static ObjectValue functionPrimordials(Environment env) { - var res = new ObjectValue(); - res.setPrototype(null, null); - - res.defineOwnField(env, "setCallable", new NativeFunction(args -> { - var func = (FunctionValue)args.get(0); - func.enableApply = args.get(1).toBoolean(); - return Value.UNDEFINED; - })); - res.defineOwnField(env, "setConstructable", new NativeFunction(args -> { - var func = (FunctionValue)args.get(0); - func.enableConstruct = args.get(1).toBoolean(); - return Value.UNDEFINED; - })); - res.defineOwnField(env, "invokeType", new NativeFunction(args -> { - if (((ArgumentsValue)args.get(0)).frame.isNew) return StringValue.of("new"); - else return StringValue.of("call"); - })); - res.defineOwnField(env, "invokeTypeInfer", new NativeFunction(args -> { - var frame = Frame.get(args.env, args.get(0).toNumber(args.env).getInt()); - if (frame.isNew) return StringValue.of("new"); - else return StringValue.of("call"); - })); - res.defineOwnField(env, "target", new NativeFunction(args -> { - var frame = Frame.get(args.env, args.get(0).toNumber(args.env).getInt()); - if (frame.target == null) return Value.UNDEFINED; - else return frame.target; - })); - - res.defineOwnField(env, "invoke", new NativeFunction(args -> { - var func = (FunctionValue)args.get(0); - var self = args.get(1); - var funcArgs = (ArrayLikeValue)args.get(2); - - return func.apply(env, self, funcArgs.toArray()); - })); - res.defineOwnField(env, "construct", new NativeFunction(args -> { - var func = (FunctionValue)args.get(0); - var target = args.get(1); - var funcArgs = (ArrayLikeValue)args.get(2); - - if (target == Value.UNDEFINED) return func.constructNoSelf(env, funcArgs.toArray()); - else return func.construct(env, target, funcArgs.toArray()); - })); - - return res; - } - - private static ObjectValue jsonPrimordials(Environment env) { - var res = new ObjectValue(); - res.setPrototype(null, null); - - res.defineOwnField(env, "stringify", new NativeFunction(args -> { - return StringValue.of(JSON.stringify(JSONConverter.fromJs(env, args.get(0)))); - })); - res.defineOwnField(env, "parse", new NativeFunction(args -> { - try { - return JSONConverter.toJs(JSON.parse(null, args.get(0).toString(env))); - } - catch (SyntaxException e) { - throw EngineException.ofSyntax(e.msg).add(env, e.loc.filename() + "", e.loc); - } - })); - - return res; - } - - private static void setProto(Environment env, Environment target, Key key, ObjectValue repo, String name) { - var val = repo.getMember(env, name); - if (val instanceof ObjectValue obj) { - target.add(key, obj); - } - } - - private static ObjectValue primordials(Environment env) { - var res = new ObjectValue(); - res.setPrototype(null, null); - - res.defineOwnField(env, "symbol", symbolPrimordials(env)); - res.defineOwnField(env, "number", numberPrimordials(env)); - res.defineOwnField(env, "string", stringPrimordials(env)); - res.defineOwnField(env, "object", objectPrimordials(env)); - res.defineOwnField(env, "buffer", bufferPrimordials(env)); - res.defineOwnField(env, "function", functionPrimordials(env)); - res.defineOwnField(env, "json", jsonPrimordials(env)); - res.defineOwnField(env, "map", mapPrimordials(env)); - res.defineOwnField(env, "regex", regexPrimordials(env)); - - int[] i = new int[1]; - - res.defineOwnField(env, "setGlobalPrototypes", new NativeFunction(args -> { - var obj = (ObjectValue)args.get(0); - - setProto(args.env, env, Value.OBJECT_PROTO, obj, "object"); - setProto(args.env, env, Value.FUNCTION_PROTO, obj, "function"); - setProto(args.env, env, Value.ARRAY_PROTO, obj, "array"); - setProto(args.env, env, Value.BOOL_PROTO, obj, "boolean"); - setProto(args.env, env, Value.NUMBER_PROTO, obj, "number"); - setProto(args.env, env, Value.STRING_PROTO, obj, "string"); - setProto(args.env, env, Value.SYMBOL_PROTO, obj, "symbol"); - setProto(args.env, env, Value.ERROR_PROTO, obj, "error"); - setProto(args.env, env, Value.SYNTAX_ERR_PROTO, obj, "syntax"); - setProto(args.env, env, Value.TYPE_ERR_PROTO, obj, "type"); - setProto(args.env, env, Value.RANGE_ERR_PROTO, obj, "range"); - setProto(args.env, env, Value.UINT8_ARR_PROTO, obj, "uint8"); - setProto(args.env, env, Value.INT32_ARR_PROTO, obj, "int32"); - return Value.UNDEFINED; - })); - res.defineOwnField(env, "setIntrinsic", new NativeFunction(args -> { - var name = args.get(0).toString(env); - var val = args.get(1); - - Value.intrinsics(env).put(name, val); - - return Value.UNDEFINED; - })); - res.defineOwnField(env, "compile", new NativeFunction(args -> { - var nameVal = args.get(1); - var name = nameVal instanceof VoidValue ? - new Filename(Metadata.name(), "func" + i[0]++ + ".js") : - Filename.parse(nameVal.toString(args.env)); - - return Compiler.compileFunc(env, name, args.get(0).toString(env)); - })); - res.defineOwnField(env, "now", new NativeFunction(args -> { - return NumberValue.of(System.currentTimeMillis()); - })); - res.defineOwnField(env, "next", new NativeFunction(args -> { - var func = (FunctionValue)args.get(0); - EventLoop.get(env).pushMsg(() -> { - func.apply(env, Value.UNDEFINED); - }, true); - return Value.UNDEFINED; - })); - - return res; - } - - private static Environment createESEnv() throws InterruptedException, ExecutionException { - var env = initEnv(); - var stubEnv = initEnv(); - Value.global(stubEnv).defineOwnField(stubEnv, "target", Value.global(env)); - Value.global(stubEnv).defineOwnField(stubEnv, "primordials", primordials(env)); - - EventLoop.get(stubEnv).pushMsg( - false, stubEnv, - new Filename(Metadata.name(), "init.js"), Reading.resourceToString("lib/stdlib.js"), - Value.UNDEFINED - ).get(); - - return env; - } - - private static Compiler wrap(Compiler first, Environment compilerEnv, Environment targetEnv, FunctionValue factory) { - var curr = new NativeFunction(args -> { - var filename = Filename.parse(args.get(0).toString(args.env)); - var src = args.get(1).toString(args.env); - var mapper = (FunctionValue)args.get(2); - return first.compile(targetEnv, filename, src, NativeMapper.unwrap(args.env, mapper)); - }); - - var next = (FunctionValue)factory.apply(compilerEnv, Value.UNDEFINED, curr); - - return (env, filename, source, map) -> { - return (FunctionValue)next.apply( - compilerEnv, Value.UNDEFINED, - StringValue.of(filename.toString()), - StringValue.of(source), - new NativeMapper(map) - ); - }; - } - - - private static Environment initEnv() { + private static Environment createESEnv() { var env = new Environment(); env.add(EventLoop.KEY, engine); env.add(DebugHandler.KEY, new SimpleDebugHandler()); - env.add(Compiler.KEY, DEFAULT_COMPILER); - // env.add(CompileResult.DEBUG_LOG); + env.add(Compiler.KEY, Compilers.chainTranspilers(environment, Compilers.jsCompiler(), Compilers::babelCompiler, Compilers::coffeescriptCompiler)); + StdLib.apply(env); var glob = Value.global(env); @@ -857,76 +116,19 @@ public class SimpleRepl { Thread.currentThread().interrupt(); throw new CancellationException(); })); - glob.defineOwnField(null, "print", new NativeFunction("print", args -> { - for (var el : args.args) { - if (el instanceof StringValue) System.out.print(((StringValue)el).value + " \t"); - else System.out.print(el.toReadable(args.env) + " \t"); - } - System.out.println(); - - return Value.UNDEFINED; - })); - glob.defineOwnField(null, "measure", new NativeFunction("measure", args -> { - var start = System.nanoTime(); - - ((FunctionValue)args.get(0)).apply(args.env, Value.UNDEFINED); - - System.out.println(String.format("Finished in %sms", (System.nanoTime() - start) / 1000000.)); - - return Value.UNDEFINED; - })); return env; } + private static void initEngine() { engineTask = engine.start(); } - private static void initGlobals() throws InterruptedException, ExecutionException { - var res = new FunctionValue[1]; - var setter = new NativeFunction(args -> { - res[0] = (FunctionValue)args.get(0); - return Value.UNDEFINED; - }); - - var tsGlob = Value.global(tsEnvironment); - var tsCompilerFactory = new FunctionValue[1]; - - tsGlob.defineOwnField(tsEnvironment, "getResource", new NativeFunction(args -> { - var name = args.get(0).toString(args.env); - var src = Reading.resourceToString("lib/" + name); - - if (src == null) return Value.UNDEFINED; - else return StringValue.of(src); - })); - tsGlob.defineOwnField(tsEnvironment, "register", new NativeFunction(args -> { - var func = (FunctionValue)args.get(0); - tsCompilerFactory[0] = func; - return Value.UNDEFINED; - })); - tsGlob.defineOwnField(tsEnvironment, "registerSource", new NativeFunction(args -> { - var filename = Filename.parse(args.get(0).toString(args.env)); - var src = args.get(1).toString(args.env); - DebugHandler.get(environment).onSourceLoad(filename, src); - return Value.UNDEFINED; - })); - - var ts = Reading.resourceToString("lib/transpiler.js"); - if (ts != null) EventLoop.get(tsEnvironment).pushMsg( - false, tsEnvironment, - new Filename(Metadata.name(), "transpiler.js"), ts, - Value.UNDEFINED, setter - ).get(); - - var tsCompiler = wrap(Compiler.get(environment), tsEnvironment, environment, tsCompilerFactory[0]); - environment.add(Compiler.KEY, tsCompiler); - } - public static void main(String args[]) throws InterruptedException { SimpleRepl.args = args; var reader = new Thread(SimpleRepl::reader); - environment = initEnv(); + environment = createESEnv(); initEngine(); diff --git a/repl/src/main/java/me/topchetoeu/j2s/repl/debug/SimpleDebugger.java b/repl/src/main/java/me/topchetoeu/j2s/repl/debug/SimpleDebugger.java index 0ed2566..90326db 100644 --- a/repl/src/main/java/me/topchetoeu/j2s/repl/debug/SimpleDebugger.java +++ b/repl/src/main/java/me/topchetoeu/j2s/repl/debug/SimpleDebugger.java @@ -24,8 +24,8 @@ import me.topchetoeu.j2s.compilation.json.JSON; import me.topchetoeu.j2s.compilation.json.JSONElement; import me.topchetoeu.j2s.compilation.json.JSONList; import me.topchetoeu.j2s.compilation.json.JSONMap; -import me.topchetoeu.j2s.repl.JSONConverter; -import me.topchetoeu.j2s.repl.SimpleRepl; +import me.topchetoeu.j2s.lib.Compilers; +import me.topchetoeu.j2s.lib.JSONConverter; import me.topchetoeu.j2s.runtime.Compiler; import me.topchetoeu.j2s.runtime.Engine; import me.topchetoeu.j2s.runtime.EventLoop; @@ -625,7 +625,7 @@ public class SimpleDebugger implements Debugger { env.remove(DebugHandler.KEY); env.remove(EventLoop.KEY); env.remove(Value.GLOBAL); - env.add(Compiler.KEY, SimpleRepl.DEFAULT_COMPILER); + env.add(Compiler.KEY, Compilers.jsCompiler()); env.add(EventLoop.KEY, engine); env.add(Value.GLOBAL, codeFrame.variables); @@ -1080,7 +1080,7 @@ public class SimpleDebugger implements Debugger { frame = getFrame(cf); - var map = DebugHandler.get(env).getMap(env, frame.frame.function); + var map = DebugHandler.get(env).getMapOrEmpty(env, frame.frame.function); frame.updateLoc(map.toLocation(frame.frame.codePtr)); loc = frame.location; diff --git a/runtime/build.gradle.kts b/runtime/build.gradle.kts index 1bce0bf..e03c94f 100644 --- a/runtime/build.gradle.kts +++ b/runtime/build.gradle.kts @@ -4,15 +4,6 @@ plugins { description = "The runtime of J2S, used to execute J2S bytecode"; -tasks.processResources { - filesMatching("metadata.json", { - expand( - "version" to properties["project_version"], - "name" to properties["project_name"], - ); - }); -} - tasks.test { useJUnitPlatform(); }