Add support for source mappings #10
230
src/assets/js/bootstrap.js
vendored
230
src/assets/js/bootstrap.js
vendored
@ -1,116 +1,114 @@
|
|||||||
(function (ts, env, libs) {
|
(function (ts, env, libs) {
|
||||||
var src = '', version = 0;
|
var src = '', version = 0;
|
||||||
var lib = libs.concat([
|
var lib = libs.concat([
|
||||||
'declare const exit: never;',
|
'declare function exit(): never;',
|
||||||
'declare const go: any;',
|
'declare function go(): any;',
|
||||||
'declare function getTsDeclarations(): string[];'
|
'declare function getTsDeclarations(): string[];'
|
||||||
]).join('');
|
]).join('');
|
||||||
var libSnapshot = ts.ScriptSnapshot.fromString(lib);
|
var libSnapshot = ts.ScriptSnapshot.fromString(lib);
|
||||||
var environments = {};
|
var environments = {};
|
||||||
var declSnapshots = [];
|
var declSnapshots = [];
|
||||||
|
|
||||||
var settings = {
|
var settings = {
|
||||||
outDir: "/out",
|
outDir: "/out",
|
||||||
declarationDir: "/out",
|
declarationDir: "/out",
|
||||||
target: ts.ScriptTarget.ES5,
|
target: ts.ScriptTarget.ES5,
|
||||||
lib: [ ],
|
lib: [ ],
|
||||||
module: ts.ModuleKind.None,
|
module: ts.ModuleKind.None,
|
||||||
declaration: true,
|
declaration: true,
|
||||||
stripInternal: true,
|
stripInternal: true,
|
||||||
downlevelIteration: true,
|
downlevelIteration: true,
|
||||||
forceConsistentCasingInFileNames: true,
|
forceConsistentCasingInFileNames: true,
|
||||||
experimentalDecorators: true,
|
experimentalDecorators: true,
|
||||||
strict: true,
|
strict: true,
|
||||||
sourceMap: true,
|
sourceMap: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
var reg = ts.createDocumentRegistry();
|
var reg = ts.createDocumentRegistry();
|
||||||
var service = ts.createLanguageService({
|
var service = ts.createLanguageService({
|
||||||
getCurrentDirectory: function() { return "/"; },
|
getCurrentDirectory: function() { return "/"; },
|
||||||
getDefaultLibFileName: function() { return "/lib.d.ts"; },
|
getDefaultLibFileName: function() { return "/lib.d.ts"; },
|
||||||
getScriptFileNames: function() {
|
getScriptFileNames: function() {
|
||||||
var res = [ "/src.ts", "/lib.d.ts" ];
|
var res = [ "/src.ts", "/lib.d.ts" ];
|
||||||
for (var i = 0; i < declSnapshots.length; i++) res.push("/glob." + (i + 1) + ".d.ts");
|
for (var i = 0; i < declSnapshots.length; i++) res.push("/glob." + (i + 1) + ".d.ts");
|
||||||
return res;
|
return res;
|
||||||
},
|
},
|
||||||
getCompilationSettings: function () { return settings; },
|
getCompilationSettings: function () { return settings; },
|
||||||
fileExists: function(filename) { return filename === "/lib.d.ts" || filename === "/src.ts" || filename === "/glob.d.ts"; },
|
fileExists: function(filename) { return filename === "/lib.d.ts" || filename === "/src.ts" || filename === "/glob.d.ts"; },
|
||||||
|
|
||||||
getScriptSnapshot: function(filename) {
|
getScriptSnapshot: function(filename) {
|
||||||
if (filename === "/lib.d.ts") return libSnapshot;
|
if (filename === "/lib.d.ts") return libSnapshot;
|
||||||
if (filename === "/src.ts") return ts.ScriptSnapshot.fromString(src);
|
if (filename === "/src.ts") return ts.ScriptSnapshot.fromString(src);
|
||||||
|
|
||||||
var index = /\/glob\.(\d+)\.d\.ts/g.exec(filename);
|
var index = /\/glob\.(\d+)\.d\.ts/g.exec(filename);
|
||||||
if (index && index[1] && (index = Number(index[1])) && index > 0 && index <= declSnapshots.length) {
|
if (index && index[1] && (index = Number(index[1])) && index > 0 && index <= declSnapshots.length) {
|
||||||
return declSnapshots[index - 1];
|
return declSnapshots[index - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error("File '" + filename + "' doesn't exist.");
|
throw new Error("File '" + filename + "' doesn't exist.");
|
||||||
},
|
},
|
||||||
getScriptVersion: function (filename) {
|
getScriptVersion: function (filename) {
|
||||||
if (filename === "/lib.d.ts" || filename.startsWith("/glob.")) return 0;
|
if (filename === "/lib.d.ts" || filename.startsWith("/glob.")) return 0;
|
||||||
else return version;
|
else return version;
|
||||||
},
|
},
|
||||||
}, reg);
|
}, reg);
|
||||||
|
|
||||||
service.getEmitOutput("/lib.d.ts");
|
service.getEmitOutput("/lib.d.ts");
|
||||||
log("Loaded libraries!");
|
log("Loaded libraries!");
|
||||||
|
|
||||||
var oldCompile = env.compile;
|
var oldCompile = env.compile;
|
||||||
|
|
||||||
function compile(code, filename, env) {
|
function compile(code, filename, env) {
|
||||||
src = code;
|
src = code;
|
||||||
version++;
|
version++;
|
||||||
|
|
||||||
if (!environments[env.id]) environments[env.id] = []
|
if (!environments[env.id]) environments[env.id] = []
|
||||||
declSnapshots = environments[env.id];
|
declSnapshots = environments[env.id];
|
||||||
var emit = service.getEmitOutput("/src.ts");
|
debugger;
|
||||||
|
var emit = service.getEmitOutput("/src.ts");
|
||||||
var diagnostics = []
|
|
||||||
.concat(service.getCompilerOptionsDiagnostics())
|
var diagnostics = []
|
||||||
.concat(service.getSyntacticDiagnostics("/src.ts"))
|
.concat(service.getCompilerOptionsDiagnostics())
|
||||||
.concat(service.getSemanticDiagnostics("/src.ts"))
|
.concat(service.getSyntacticDiagnostics("/src.ts"))
|
||||||
.map(function (diagnostic) {
|
.concat(service.getSemanticDiagnostics("/src.ts"))
|
||||||
var message = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
|
.map(function (diagnostic) {
|
||||||
if (diagnostic.file) {
|
var message = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
|
||||||
var pos = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
|
if (diagnostic.file) {
|
||||||
var file = diagnostic.file.fileName.substring(1);
|
var pos = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
|
||||||
if (file === "src.ts") file = filename;
|
var file = diagnostic.file.fileName.substring(1);
|
||||||
return file + ":" + (pos.line + 1) + ":" + (pos.character + 1) + ": " + message;
|
if (file === "src.ts") file = filename;
|
||||||
}
|
return file + ":" + (pos.line + 1) + ":" + (pos.character + 1) + ": " + message;
|
||||||
else return message;
|
}
|
||||||
});
|
else return message;
|
||||||
|
});
|
||||||
if (diagnostics.length > 0) {
|
|
||||||
throw new SyntaxError(diagnostics.join("\n"));
|
if (diagnostics.length > 0) {
|
||||||
}
|
throw new SyntaxError(diagnostics.join("\n"));
|
||||||
|
}
|
||||||
var map = JSON.parse(emit.outputFiles[0].text);
|
|
||||||
var result = emit.outputFiles[1].text;
|
var map = JSON.parse(emit.outputFiles[0].text);
|
||||||
var declaration = emit.outputFiles[2].text;
|
var result = emit.outputFiles[1].text;
|
||||||
|
var declaration = emit.outputFiles[2].text;
|
||||||
var compiled = oldCompile(result, filename, env);
|
|
||||||
|
var compiled = oldCompile(result, filename, env);
|
||||||
return {
|
|
||||||
function: function () {
|
return {
|
||||||
var val = compiled.function.apply(this, arguments);
|
function: function () {
|
||||||
if (declaration !== '') declSnapshots.push(ts.ScriptSnapshot.fromString(declaration));
|
var val = compiled.function.apply(this, arguments);
|
||||||
return val;
|
if (declaration !== '') declSnapshots.push(ts.ScriptSnapshot.fromString(declaration));
|
||||||
},
|
return val;
|
||||||
mapChain: compiled.mapChain.concat(JSON.stringify({
|
},
|
||||||
file: filename,
|
breakpoints: compiled.breakpoints,
|
||||||
sources: [filename],
|
mapChain: compiled.mapChain.concat(map.mappings),
|
||||||
mappings: map.mappings,
|
};
|
||||||
}))
|
}
|
||||||
};
|
|
||||||
}
|
function apply(env) {
|
||||||
|
env.compile = compile;
|
||||||
function apply(env) {
|
env.global.getTsDeclarations = function() {
|
||||||
env.compile = compile;
|
return environments[env.id];
|
||||||
env.global.getTsDeclarations = function() {
|
}
|
||||||
return environments[env.id];
|
}
|
||||||
}
|
|
||||||
}
|
apply(env);
|
||||||
|
})(arguments[0], arguments[1], arguments[2]);
|
||||||
apply(env);
|
|
||||||
})(arguments[0], arguments[1], arguments[2]);
|
|
||||||
|
1231
src/assets/js/lib.d.ts
vendored
1231
src/assets/js/lib.d.ts
vendored
File diff suppressed because it is too large
Load Diff
170093
src/assets/js/ts.js
170093
src/assets/js/ts.js
File diff suppressed because one or more lines are too long
@ -1,53 +1,53 @@
|
|||||||
package me.topchetoeu.jscript.filesystem;
|
package me.topchetoeu.jscript;
|
||||||
|
|
||||||
public class Buffer {
|
public class Buffer {
|
||||||
private byte[] data;
|
private byte[] data;
|
||||||
private int length;
|
private int length;
|
||||||
|
|
||||||
public void write(int i, byte[] val) {
|
public void write(int i, byte[] val) {
|
||||||
if (i + val.length > data.length) {
|
if (i + val.length > data.length) {
|
||||||
var newCap = i + val.length + 1;
|
var newCap = i + val.length + 1;
|
||||||
if (newCap < data.length * 2) newCap = data.length * 2;
|
if (newCap < data.length * 2) newCap = data.length * 2;
|
||||||
|
|
||||||
var tmp = new byte[newCap];
|
var tmp = new byte[newCap];
|
||||||
System.arraycopy(this.data, 0, tmp, 0, length);
|
System.arraycopy(this.data, 0, tmp, 0, length);
|
||||||
this.data = tmp;
|
this.data = tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
System.arraycopy(val, 0, data, i, val.length);
|
System.arraycopy(val, 0, data, i, val.length);
|
||||||
if (i + val.length > length) length = i + val.length;
|
if (i + val.length > length) length = i + val.length;
|
||||||
}
|
}
|
||||||
public int read(int i, byte[] buff) {
|
public int read(int i, byte[] buff) {
|
||||||
int n = buff.length;
|
int n = buff.length;
|
||||||
if (i + n > length) n = length - i;
|
if (i + n > length) n = length - i;
|
||||||
System.arraycopy(data, i, buff, 0, n);
|
System.arraycopy(data, i, buff, 0, n);
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void append(byte b) {
|
public void append(byte b) {
|
||||||
write(length, new byte[] { b });
|
write(length, new byte[] { b });
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] data() {
|
public byte[] data() {
|
||||||
var res = new byte[length];
|
var res = new byte[length];
|
||||||
System.arraycopy(this.data, 0, res, 0, length);
|
System.arraycopy(this.data, 0, res, 0, length);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
public int length() {
|
public int length() {
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Buffer(byte[] data) {
|
public Buffer(byte[] data) {
|
||||||
this.data = new byte[data.length];
|
this.data = new byte[data.length];
|
||||||
this.length = data.length;
|
this.length = data.length;
|
||||||
System.arraycopy(data, 0, this.data, 0, data.length);
|
System.arraycopy(data, 0, this.data, 0, data.length);
|
||||||
}
|
}
|
||||||
public Buffer(int capacity) {
|
public Buffer(int capacity) {
|
||||||
this.data = new byte[capacity];
|
this.data = new byte[capacity];
|
||||||
this.length = 0;
|
this.length = 0;
|
||||||
}
|
}
|
||||||
public Buffer() {
|
public Buffer() {
|
||||||
this.data = new byte[128];
|
this.data = new byte[128];
|
||||||
this.length = 0;
|
this.length = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -71,4 +71,23 @@ public class Location implements Comparable<Location> {
|
|||||||
this.start = start;
|
this.start = start;
|
||||||
this.filename = filename;
|
this.filename = filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Location parse(String raw) {
|
||||||
|
int i0 = -1, i1 = -1;
|
||||||
|
for (var i = raw.length() - 1; i >= 0; i--) {
|
||||||
|
if (raw.charAt(i) == ':') {
|
||||||
|
if (i1 == -1) i1 = i;
|
||||||
|
else if (i0 == -1) {
|
||||||
|
i0 = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Location(
|
||||||
|
Integer.parseInt(raw.substring(i0 + 1, i1)),
|
||||||
|
Integer.parseInt(raw.substring(i1 + 1)),
|
||||||
|
Filename.parse(raw.substring(0, i0))
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,165 +1,168 @@
|
|||||||
package me.topchetoeu.jscript;
|
package me.topchetoeu.jscript;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
import me.topchetoeu.jscript.engine.Engine;
|
import me.topchetoeu.jscript.engine.Engine;
|
||||||
import me.topchetoeu.jscript.engine.Environment;
|
import me.topchetoeu.jscript.engine.Environment;
|
||||||
import me.topchetoeu.jscript.engine.debug.DebugServer;
|
import me.topchetoeu.jscript.engine.debug.DebugServer;
|
||||||
import me.topchetoeu.jscript.engine.debug.SimpleDebugger;
|
import me.topchetoeu.jscript.engine.debug.SimpleDebugger;
|
||||||
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
import me.topchetoeu.jscript.engine.values.NativeFunction;
|
||||||
import me.topchetoeu.jscript.events.Observer;
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
import me.topchetoeu.jscript.exceptions.InterruptException;
|
import me.topchetoeu.jscript.events.Observer;
|
||||||
import me.topchetoeu.jscript.exceptions.SyntaxException;
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
import me.topchetoeu.jscript.filesystem.MemoryFilesystem;
|
import me.topchetoeu.jscript.exceptions.InterruptException;
|
||||||
import me.topchetoeu.jscript.filesystem.Mode;
|
import me.topchetoeu.jscript.exceptions.SyntaxException;
|
||||||
import me.topchetoeu.jscript.filesystem.PhysicalFilesystem;
|
import me.topchetoeu.jscript.filesystem.MemoryFilesystem;
|
||||||
import me.topchetoeu.jscript.lib.Internals;
|
import me.topchetoeu.jscript.filesystem.Mode;
|
||||||
|
import me.topchetoeu.jscript.filesystem.PhysicalFilesystem;
|
||||||
public class Main {
|
import me.topchetoeu.jscript.lib.Internals;
|
||||||
public static class Printer implements Observer<Object> {
|
|
||||||
public void next(Object data) {
|
public class Main {
|
||||||
Values.printValue(null, data);
|
public static class Printer implements Observer<Object> {
|
||||||
System.out.println();
|
public void next(Object data) {
|
||||||
}
|
Values.printValue(null, data);
|
||||||
|
System.out.println();
|
||||||
public void error(RuntimeException err) {
|
}
|
||||||
Values.printError(err, null);
|
|
||||||
}
|
public void error(RuntimeException err) {
|
||||||
|
Values.printError(err, null);
|
||||||
public void finish() {
|
}
|
||||||
engineTask.interrupt();
|
|
||||||
}
|
public void finish() {
|
||||||
}
|
engineTask.interrupt();
|
||||||
|
}
|
||||||
static Thread engineTask, debugTask;
|
}
|
||||||
static Engine engine = new Engine(true);
|
|
||||||
static DebugServer debugServer = new DebugServer();
|
static Thread engineTask, debugTask;
|
||||||
static Environment environment = new Environment(null, null, null);
|
static Engine engine = new Engine(true);
|
||||||
|
static DebugServer debugServer = new DebugServer();
|
||||||
static int j = 0;
|
static Environment environment = new Environment(null, null, null);
|
||||||
static boolean exited = false;
|
|
||||||
static String[] args;
|
static int j = 0;
|
||||||
|
static boolean exited = false;
|
||||||
private static void reader() {
|
static String[] args;
|
||||||
try {
|
|
||||||
for (var arg : args) {
|
private static void reader() {
|
||||||
try {
|
try {
|
||||||
if (arg.equals("--ts")) initTypescript();
|
for (var arg : args) {
|
||||||
else {
|
try {
|
||||||
var file = Path.of(arg);
|
if (arg.equals("--ts")) initTypescript();
|
||||||
var raw = Files.readString(file);
|
else {
|
||||||
var res = engine.pushMsg(
|
var file = Path.of(arg);
|
||||||
false, new Context(engine, environment),
|
var raw = Files.readString(file);
|
||||||
Filename.fromFile(file.toFile()),
|
var res = engine.pushMsg(
|
||||||
raw, null
|
false, new Context(engine, environment),
|
||||||
).await();
|
Filename.fromFile(file.toFile()),
|
||||||
Values.printValue(null, res);
|
raw, null
|
||||||
System.out.println();
|
).await();
|
||||||
}
|
Values.printValue(null, res);
|
||||||
}
|
System.out.println();
|
||||||
catch (EngineException e) { Values.printError(e, null); }
|
}
|
||||||
}
|
}
|
||||||
for (var i = 0; ; i++) {
|
catch (EngineException e) { Values.printError(e, null); }
|
||||||
try {
|
}
|
||||||
var raw = Reading.read();
|
for (var i = 0; ; i++) {
|
||||||
|
try {
|
||||||
if (raw == null) break;
|
var raw = Reading.read();
|
||||||
var res = engine.pushMsg(
|
|
||||||
false, new Context(engine, environment),
|
if (raw == null) break;
|
||||||
new Filename("jscript", "repl/" + i + ".js"),
|
var res = engine.pushMsg(
|
||||||
raw, null
|
false, new Context(engine, environment),
|
||||||
).await();
|
new Filename("jscript", "repl/" + i + ".js"),
|
||||||
Values.printValue(null, res);
|
raw, null
|
||||||
System.out.println();
|
).await();
|
||||||
}
|
Values.printValue(null, res);
|
||||||
catch (EngineException e) { Values.printError(e, null); }
|
System.out.println();
|
||||||
catch (SyntaxException e) { Values.printError(e, null); }
|
}
|
||||||
}
|
catch (EngineException e) { Values.printError(e, null); }
|
||||||
}
|
catch (SyntaxException e) { Values.printError(e, null); }
|
||||||
catch (IOException e) {
|
}
|
||||||
System.out.println(e.toString());
|
}
|
||||||
exited = true;
|
catch (IOException e) {
|
||||||
}
|
System.out.println(e.toString());
|
||||||
catch (RuntimeException ex) {
|
exited = true;
|
||||||
if (!exited) {
|
}
|
||||||
System.out.println("Internal error ocurred:");
|
catch (RuntimeException ex) {
|
||||||
ex.printStackTrace();
|
if (!exited) {
|
||||||
}
|
System.out.println("Internal error ocurred:");
|
||||||
}
|
ex.printStackTrace();
|
||||||
if (exited) {
|
}
|
||||||
debugTask.interrupt();
|
}
|
||||||
engineTask.interrupt();
|
if (exited) {
|
||||||
}
|
debugTask.interrupt();
|
||||||
}
|
engineTask.interrupt();
|
||||||
|
}
|
||||||
private static void initEnv() {
|
}
|
||||||
environment = Internals.apply(environment);
|
|
||||||
|
private static void initEnv() {
|
||||||
environment.global.define("exit", _ctx -> {
|
environment = Internals.apply(environment);
|
||||||
exited = true;
|
|
||||||
throw new InterruptException();
|
environment.global.define(false, new NativeFunction("exit", (_ctx, th, args) -> {
|
||||||
});
|
exited = true;
|
||||||
environment.global.define("go", _ctx -> {
|
throw new InterruptException();
|
||||||
try {
|
}));
|
||||||
var f = Path.of("do.js");
|
environment.global.define(false, new NativeFunction("go", (_ctx, th, args) -> {
|
||||||
var func = _ctx.compile(new Filename("do", "do/" + j++ + ".js"), new String(Files.readAllBytes(f)));
|
try {
|
||||||
return func.call(_ctx);
|
var f = Path.of("do.js");
|
||||||
}
|
var func = _ctx.compile(new Filename("do", "do/" + j++ + ".js"), new String(Files.readAllBytes(f)));
|
||||||
catch (IOException e) {
|
return func.call(_ctx);
|
||||||
throw new EngineException("Couldn't open do.js");
|
}
|
||||||
}
|
catch (IOException e) {
|
||||||
});
|
throw new EngineException("Couldn't open do.js");
|
||||||
|
}
|
||||||
environment.filesystem.protocols.put("temp", new MemoryFilesystem(Mode.READ_WRITE));
|
}));
|
||||||
environment.filesystem.protocols.put("file", new PhysicalFilesystem(Path.of(".").toAbsolutePath()));
|
|
||||||
}
|
environment.filesystem.protocols.put("temp", new MemoryFilesystem(Mode.READ_WRITE));
|
||||||
private static void initEngine() {
|
environment.filesystem.protocols.put("file", new PhysicalFilesystem(Path.of(".").toAbsolutePath()));
|
||||||
debugServer.targets.put("target", (ws, req) -> new SimpleDebugger(ws, engine));
|
}
|
||||||
engineTask = engine.start();
|
private static void initEngine() {
|
||||||
debugTask = debugServer.start(new InetSocketAddress("127.0.0.1", 9229), true);
|
debugServer.targets.put("target", (ws, req) -> new SimpleDebugger(ws, engine));
|
||||||
}
|
engineTask = engine.start();
|
||||||
private static void initTypescript() {
|
debugTask = debugServer.start(new InetSocketAddress("127.0.0.1", 9229), true);
|
||||||
try {
|
}
|
||||||
var tsEnv = Internals.apply(new Environment(null, null, null));
|
private static void initTypescript() {
|
||||||
var bsEnv = Internals.apply(new Environment(null, null, null));
|
try {
|
||||||
|
var tsEnv = Internals.apply(new Environment(null, null, null));
|
||||||
engine.pushMsg(
|
tsEnv.global.define(null, "module", false, new ObjectValue());
|
||||||
false, new Context(engine, tsEnv),
|
var bsEnv = Internals.apply(new Environment(null, null, null));
|
||||||
new Filename("jscript", "ts.js"),
|
|
||||||
Reading.resourceToString("js/ts.js"), null
|
engine.pushMsg(
|
||||||
).await();
|
false, new Context(engine, tsEnv),
|
||||||
System.out.println("Loaded typescript!");
|
new Filename("jscript", "ts.js"),
|
||||||
|
Reading.resourceToString("js/ts.js"), null
|
||||||
var ctx = new Context(engine, bsEnv);
|
).await();
|
||||||
|
System.out.println("Loaded typescript!");
|
||||||
engine.pushMsg(
|
|
||||||
false, ctx,
|
var ctx = new Context(engine, bsEnv);
|
||||||
new Filename("jscript", "internals/bootstrap.js"), Reading.resourceToString("js/bootstrap.js"), null,
|
|
||||||
tsEnv.global.get(ctx, "ts"), environment, new ArrayValue(null, Reading.resourceToString("js/lib.d.ts"))
|
engine.pushMsg(
|
||||||
).await();
|
false, ctx,
|
||||||
}
|
new Filename("jscript", "bootstrap.js"), Reading.resourceToString("js/bootstrap.js"), null,
|
||||||
catch (EngineException e) {
|
tsEnv.global.get(ctx, "ts"), environment, new ArrayValue(null, Reading.resourceToString("js/lib.d.ts"))
|
||||||
Values.printError(e, "(while initializing TS)");
|
).await();
|
||||||
}
|
}
|
||||||
}
|
catch (EngineException e) {
|
||||||
|
Values.printError(e, "(while initializing TS)");
|
||||||
public static void main(String args[]) {
|
}
|
||||||
System.out.println(String.format("Running %s v%s by %s", Metadata.name(), Metadata.version(), Metadata.author()));
|
}
|
||||||
|
|
||||||
Main.args = args;
|
public static void main(String args[]) {
|
||||||
var reader = new Thread(Main::reader);
|
System.out.println(String.format("Running %s v%s by %s", Metadata.name(), Metadata.version(), Metadata.author()));
|
||||||
|
|
||||||
initEnv();
|
Main.args = args;
|
||||||
initEngine();
|
var reader = new Thread(Main::reader);
|
||||||
|
|
||||||
reader.setDaemon(true);
|
initEnv();
|
||||||
reader.setName("STD Reader");
|
initEngine();
|
||||||
reader.start();
|
|
||||||
}
|
reader.setDaemon(true);
|
||||||
}
|
reader.setName("STD Reader");
|
||||||
|
reader.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
21
src/me/topchetoeu/jscript/compilation/CalculateResult.java
Normal file
21
src/me/topchetoeu/jscript/compilation/CalculateResult.java
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package me.topchetoeu.jscript.compilation;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
|
|
||||||
|
public final class CalculateResult {
|
||||||
|
public final boolean exists;
|
||||||
|
public final Object value;
|
||||||
|
|
||||||
|
public final boolean isTruthy() {
|
||||||
|
return exists && Values.toBoolean(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CalculateResult(Object value) {
|
||||||
|
this.exists = true;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
public CalculateResult() {
|
||||||
|
this.exists = false;
|
||||||
|
this.value = null;
|
||||||
|
}
|
||||||
|
}
|
@ -1,42 +1,72 @@
|
|||||||
package me.topchetoeu.jscript.compilation;
|
package me.topchetoeu.jscript.compilation;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.HashMap;
|
||||||
import java.util.TreeSet;
|
import java.util.Map;
|
||||||
import java.util.Vector;
|
import java.util.TreeSet;
|
||||||
|
import java.util.Vector;
|
||||||
import me.topchetoeu.jscript.Location;
|
|
||||||
|
import me.topchetoeu.jscript.Location;
|
||||||
public class CompileTarget {
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
public final Vector<Instruction> target = new Vector<>();
|
import me.topchetoeu.jscript.engine.Environment;
|
||||||
public final Map<Long, FunctionBody> functions;
|
import me.topchetoeu.jscript.engine.values.CodeFunction;
|
||||||
public final TreeSet<Location> breakpoints;
|
|
||||||
|
public class CompileTarget {
|
||||||
public Instruction add(Instruction instr) {
|
public final Vector<Instruction> target = new Vector<>();
|
||||||
target.add(instr);
|
public final Map<Long, FunctionBody> functions;
|
||||||
return instr;
|
public final TreeSet<Location> breakpoints;
|
||||||
}
|
private final HashMap<Location, Instruction> bpToInstr = new HashMap<>();
|
||||||
public Instruction set(int i, Instruction instr) {
|
private BreakpointType queueType = BreakpointType.NONE;
|
||||||
return target.set(i, instr);
|
private Location queueLoc = null;
|
||||||
}
|
|
||||||
public void setDebug(int i) {
|
public Instruction add(Instruction instr) {
|
||||||
breakpoints.add(target.get(i).location);
|
target.add(instr);
|
||||||
}
|
if (queueType != BreakpointType.NONE) setDebug(queueType);
|
||||||
public void setDebug() {
|
if (queueLoc != null) instr.locate(queueLoc);
|
||||||
setDebug(target.size() - 1);
|
queueType = BreakpointType.NONE;
|
||||||
}
|
queueLoc = null;
|
||||||
public Instruction get(int i) {
|
return instr;
|
||||||
return target.get(i);
|
}
|
||||||
}
|
public Instruction set(int i, Instruction instr) {
|
||||||
public int size() { return target.size(); }
|
return target.set(i, instr);
|
||||||
public Location lastLoc(Location fallback) {
|
}
|
||||||
if (target.size() == 0) return fallback;
|
public void setDebug(int i, BreakpointType type) {
|
||||||
else return target.get(target.size() - 1).location;
|
var instr = target.get(i);
|
||||||
}
|
instr.breakpoint = type;
|
||||||
|
breakpoints.add(target.get(i).location);
|
||||||
public Instruction[] array() { return target.toArray(Instruction[]::new); }
|
|
||||||
|
var old = bpToInstr.put(instr.location, instr);
|
||||||
public CompileTarget(Map<Long, FunctionBody> functions, TreeSet<Location> breakpoints) {
|
if (old != null) old.breakpoint = BreakpointType.NONE;
|
||||||
this.functions = functions;
|
}
|
||||||
this.breakpoints = breakpoints;
|
public void setDebug(BreakpointType type) {
|
||||||
}
|
setDebug(target.size() - 1, type);
|
||||||
}
|
}
|
||||||
|
public Instruction get(int i) {
|
||||||
|
return target.get(i);
|
||||||
|
}
|
||||||
|
public int size() { return target.size(); }
|
||||||
|
public Location lastLoc(Location fallback) {
|
||||||
|
if (target.size() == 0) return fallback;
|
||||||
|
else return target.get(target.size() - 1).location;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void queueDebug(BreakpointType type) {
|
||||||
|
queueType = type;
|
||||||
|
}
|
||||||
|
public void queueDebug(BreakpointType type, Location loc) {
|
||||||
|
queueType = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Instruction[] array() { return target.toArray(Instruction[]::new); }
|
||||||
|
|
||||||
|
public FunctionBody body() {
|
||||||
|
return functions.get(0l);
|
||||||
|
}
|
||||||
|
public CodeFunction func(Environment env) {
|
||||||
|
return new CodeFunction(env, "", body());
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompileTarget(Map<Long, FunctionBody> functions, TreeSet<Location> breakpoints) {
|
||||||
|
this.functions = functions;
|
||||||
|
this.breakpoints = breakpoints;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,63 +1,53 @@
|
|||||||
package me.topchetoeu.jscript.compilation;
|
package me.topchetoeu.jscript.compilation;
|
||||||
|
|
||||||
import java.util.Vector;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.control.ContinueStatement;
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.compilation.control.ReturnStatement;
|
|
||||||
import me.topchetoeu.jscript.compilation.control.ThrowStatement;
|
|
||||||
import me.topchetoeu.jscript.compilation.values.FunctionStatement;
|
import me.topchetoeu.jscript.compilation.values.FunctionStatement;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class CompoundStatement extends Statement {
|
public class CompoundStatement extends Statement {
|
||||||
public final Statement[] statements;
|
public final Statement[] statements;
|
||||||
|
public final boolean separateFuncs;
|
||||||
public Location end;
|
public Location end;
|
||||||
|
|
||||||
@Override
|
@Override public boolean pure() {
|
||||||
public void declare(ScopeRecord varsScope) {
|
|
||||||
for (var stm : statements) {
|
for (var stm : statements) {
|
||||||
stm.declare(varsScope);
|
if (!stm.pure()) return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void declare(ScopeRecord varsScope) {
|
||||||
for (var stm : statements) {
|
for (var stm : statements) stm.declare(varsScope);
|
||||||
if (stm instanceof FunctionStatement) stm.compile(target, scope, false);
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compileWithDebug(CompileTarget target, ScopeRecord scope, boolean pollute, BreakpointType type) {
|
||||||
|
if (separateFuncs) for (var stm : statements) {
|
||||||
|
if (stm instanceof FunctionStatement && ((FunctionStatement)stm).statement) {
|
||||||
|
stm.compile(target, scope, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var polluted = false;
|
||||||
|
|
||||||
for (var i = 0; i < statements.length; i++) {
|
for (var i = 0; i < statements.length; i++) {
|
||||||
var stm = statements[i];
|
var stm = statements[i];
|
||||||
|
|
||||||
if (stm instanceof FunctionStatement) continue;
|
if (separateFuncs && stm instanceof FunctionStatement) continue;
|
||||||
if (i != statements.length - 1) stm.compileWithDebug(target, scope, false);
|
if (i != statements.length - 1) stm.compileWithDebug(target, scope, false, BreakpointType.STEP_OVER);
|
||||||
else stm.compileWithDebug(target, scope, pollute);
|
else stm.compileWithDebug(target, scope, polluted = pollute, BreakpointType.STEP_OVER);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (end != null) {
|
if (!polluted && pollute) {
|
||||||
target.add(Instruction.nop(end));
|
target.add(Instruction.loadValue(loc(), null));
|
||||||
target.setDebug();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Statement optimize() {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
var res = new Vector<Statement>(statements.length);
|
compileWithDebug(target, scope, pollute, BreakpointType.STEP_IN);
|
||||||
|
|
||||||
for (var i = 0; i < statements.length; i++) {
|
|
||||||
var stm = statements[i].optimize();
|
|
||||||
if (i < statements.length - 1 && stm.pure()) continue;
|
|
||||||
res.add(stm);
|
|
||||||
if (
|
|
||||||
stm instanceof ContinueStatement ||
|
|
||||||
stm instanceof ReturnStatement ||
|
|
||||||
stm instanceof ThrowStatement ||
|
|
||||||
stm instanceof ContinueStatement
|
|
||||||
) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res.size() == 1) return res.get(0);
|
|
||||||
else return new CompoundStatement(loc(), res.toArray(Statement[]::new));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompoundStatement setEnd(Location loc) {
|
public CompoundStatement setEnd(Location loc) {
|
||||||
@ -65,8 +55,9 @@ public class CompoundStatement extends Statement {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompoundStatement(Location loc, Statement ...statements) {
|
public CompoundStatement(Location loc, boolean separateFuncs, Statement ...statements) {
|
||||||
super(loc);
|
super(loc);
|
||||||
|
this.separateFuncs = separateFuncs;
|
||||||
this.statements = statements;
|
this.statements = statements;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
|
||||||
import me.topchetoeu.jscript.compilation.values.ConstantStatement;
|
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
|
||||||
|
|
||||||
public class DiscardStatement extends Statement {
|
|
||||||
public final Statement value;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
value.compile(target, scope, false);
|
|
||||||
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public Statement optimize() {
|
|
||||||
if (value == null) return this;
|
|
||||||
var val = value.optimize();
|
|
||||||
if (val.pure()) return new ConstantStatement(loc(), null);
|
|
||||||
else return new DiscardStatement(loc(), val);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DiscardStatement(Location loc, Statement val) {
|
|
||||||
super(loc);
|
|
||||||
this.value = val;
|
|
||||||
}
|
|
||||||
}
|
|
@ -10,7 +10,8 @@ public class Instruction {
|
|||||||
THROW,
|
THROW,
|
||||||
THROW_SYNTAX,
|
THROW_SYNTAX,
|
||||||
DELETE,
|
DELETE,
|
||||||
TRY,
|
TRY_START,
|
||||||
|
TRY_END,
|
||||||
NOP,
|
NOP,
|
||||||
|
|
||||||
CALL,
|
CALL,
|
||||||
@ -86,10 +87,29 @@ public class Instruction {
|
|||||||
// this(false);
|
// this(false);
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
public static enum BreakpointType {
|
||||||
|
NONE,
|
||||||
|
STEP_OVER,
|
||||||
|
STEP_IN;
|
||||||
|
|
||||||
|
public boolean shouldStepIn() {
|
||||||
|
return this != NONE;
|
||||||
|
}
|
||||||
|
public boolean shouldStepOver() {
|
||||||
|
return this == STEP_OVER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public final Type type;
|
public final Type type;
|
||||||
public final Object[] params;
|
public final Object[] params;
|
||||||
public Location location;
|
public Location location;
|
||||||
|
public BreakpointType breakpoint = BreakpointType.NONE;
|
||||||
|
|
||||||
|
public Instruction setDbgData(Instruction other) {
|
||||||
|
this.location = other.location;
|
||||||
|
this.breakpoint = other.breakpoint;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public Instruction locate(Location loc) {
|
public Instruction locate(Location loc) {
|
||||||
this.location = loc;
|
this.location = loc;
|
||||||
@ -129,8 +149,11 @@ public class Instruction {
|
|||||||
this.params = params;
|
this.params = params;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Instruction tryInstr(Location loc, int n, int catchN, int finallyN) {
|
public static Instruction tryStart(Location loc, int catchStart, int finallyStart, int end) {
|
||||||
return new Instruction(loc, Type.TRY, n, catchN, finallyN);
|
return new Instruction(loc, Type.TRY_START, catchStart, finallyStart, end);
|
||||||
|
}
|
||||||
|
public static Instruction tryEnd(Location loc) {
|
||||||
|
return new Instruction(loc, Type.TRY_END);
|
||||||
}
|
}
|
||||||
public static Instruction throwInstr(Location loc) {
|
public static Instruction throwInstr(Location loc) {
|
||||||
return new Instruction(loc, Type.THROW);
|
return new Instruction(loc, Type.THROW);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package me.topchetoeu.jscript.compilation;
|
package me.topchetoeu.jscript.compilation;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public abstract class Statement {
|
public abstract class Statement {
|
||||||
@ -9,12 +10,15 @@ public abstract class Statement {
|
|||||||
public boolean pure() { return false; }
|
public boolean pure() { return false; }
|
||||||
public abstract void compile(CompileTarget target, ScopeRecord scope, boolean pollute);
|
public abstract void compile(CompileTarget target, ScopeRecord scope, boolean pollute);
|
||||||
public void declare(ScopeRecord varsScope) { }
|
public void declare(ScopeRecord varsScope) { }
|
||||||
public Statement optimize() { return this; }
|
|
||||||
|
|
||||||
public void compileWithDebug(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compileWithDebug(CompileTarget target, ScopeRecord scope, boolean pollute, BreakpointType type) {
|
||||||
int start = target.size();
|
int start = target.size();
|
||||||
compile(target, scope, pollute);
|
compile(target, scope, pollute);
|
||||||
if (target.size() != start) target.setDebug(start);
|
|
||||||
|
if (target.size() != start) {
|
||||||
|
target.get(start).locate(loc());
|
||||||
|
target.setDebug(start, type);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Location loc() { return _loc; }
|
public Location loc() { return _loc; }
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
package me.topchetoeu.jscript.compilation;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
import me.topchetoeu.jscript.exceptions.SyntaxException;
|
||||||
|
|
||||||
|
public class ThrowSyntaxStatement extends Statement {
|
||||||
|
public final String name;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
|
target.add(Instruction.throwSyntax(loc(), name));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ThrowSyntaxStatement(SyntaxException e) {
|
||||||
|
super(e.loc);
|
||||||
|
this.name = e.msg;
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,7 @@ package me.topchetoeu.jscript.compilation;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.compilation.values.FunctionStatement;
|
import me.topchetoeu.jscript.compilation.values.FunctionStatement;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
@ -32,16 +33,13 @@ public class VariableDeclareStatement extends Statement {
|
|||||||
for (var entry : values) {
|
for (var entry : values) {
|
||||||
if (entry.name == null) continue;
|
if (entry.name == null) continue;
|
||||||
var key = scope.getKey(entry.name);
|
var key = scope.getKey(entry.name);
|
||||||
int start = target.size();
|
|
||||||
|
|
||||||
if (key instanceof String) target.add(Instruction.makeVar(entry.location, (String)key));
|
if (key instanceof String) target.add(Instruction.makeVar(entry.location, (String)key));
|
||||||
|
|
||||||
if (entry.value != null) {
|
target.queueDebug(BreakpointType.STEP_OVER, entry.location);
|
||||||
FunctionStatement.compileWithName(entry.value, target, scope, true, entry.name);
|
if (entry.value != null) FunctionStatement.compileWithName(entry.value, target, scope, true, entry.name);
|
||||||
target.add(Instruction.storeVar(entry.location, key));
|
else target.add(Instruction.loadValue(entry.location, null));
|
||||||
}
|
target.add(Instruction.storeVar(entry.location, key));
|
||||||
|
|
||||||
if (target.size() != start) target.setDebug(start);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
||||||
|
@ -2,12 +2,10 @@ package me.topchetoeu.jscript.compilation.control;
|
|||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.CompoundStatement;
|
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.compilation.values.ConstantStatement;
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
|
||||||
|
|
||||||
public class DoWhileStatement extends Statement {
|
public class DoWhileStatement extends Statement {
|
||||||
public final Statement condition, body;
|
public final Statement condition, body;
|
||||||
@ -20,56 +18,16 @@ public class DoWhileStatement extends Statement {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
if (condition instanceof ConstantStatement) {
|
|
||||||
int start = target.size();
|
|
||||||
body.compile(target, scope, false);
|
|
||||||
int end = target.size();
|
|
||||||
if (Values.toBoolean(((ConstantStatement)condition).value)) {
|
|
||||||
WhileStatement.replaceBreaks(target, label, start, end, end + 1, end + 1);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
target.add(Instruction.jmp(loc(), start - end));
|
|
||||||
WhileStatement.replaceBreaks(target, label, start, end, start, end + 1);
|
|
||||||
}
|
|
||||||
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int start = target.size();
|
int start = target.size();
|
||||||
body.compileWithDebug(target, scope, false);
|
body.compileWithDebug(target, scope, false, BreakpointType.STEP_OVER);
|
||||||
int mid = target.size();
|
int mid = target.size();
|
||||||
condition.compile(target, scope, true);
|
condition.compileWithDebug(target, scope, true, BreakpointType.STEP_OVER);
|
||||||
int end = target.size();
|
int end = target.size();
|
||||||
|
|
||||||
WhileStatement.replaceBreaks(target, label, start, mid - 1, mid, end + 1);
|
WhileStatement.replaceBreaks(target, label, start, mid - 1, mid, end + 1);
|
||||||
target.add(Instruction.jmpIf(loc(), start - end));
|
target.add(Instruction.jmpIf(loc(), start - end));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Statement optimize() {
|
|
||||||
var cond = condition.optimize();
|
|
||||||
var b = body.optimize();
|
|
||||||
|
|
||||||
if (b instanceof CompoundStatement) {
|
|
||||||
var comp = (CompoundStatement)b;
|
|
||||||
if (comp.statements.length > 0) {
|
|
||||||
var last = comp.statements[comp.statements.length - 1];
|
|
||||||
if (last instanceof ContinueStatement) comp.statements[comp.statements.length - 1] = new CompoundStatement(loc());
|
|
||||||
if (last instanceof BreakStatement) {
|
|
||||||
comp.statements[comp.statements.length - 1] = new CompoundStatement(loc());
|
|
||||||
return new CompoundStatement(loc());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (b instanceof ContinueStatement) {
|
|
||||||
b = new CompoundStatement(loc());
|
|
||||||
}
|
|
||||||
else if (b instanceof BreakStatement) return new CompoundStatement(loc());
|
|
||||||
|
|
||||||
if (b.pure()) return new DoWhileStatement(loc(), label, cond, new CompoundStatement(loc()));
|
|
||||||
else return new DoWhileStatement(loc(), label, cond, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DoWhileStatement(Location loc, String label, Statement condition, Statement body) {
|
public DoWhileStatement(Location loc, String label, Statement condition, Statement body) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.label = label;
|
this.label = label;
|
||||||
|
@ -4,6 +4,7 @@ import me.topchetoeu.jscript.Location;
|
|||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.engine.Operation;
|
import me.topchetoeu.jscript.engine.Operation;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
@ -32,7 +33,7 @@ public class ForInStatement extends Statement {
|
|||||||
target.add(Instruction.storeVar(loc(), scope.getKey(varName)));
|
target.add(Instruction.storeVar(loc(), scope.getKey(varName)));
|
||||||
}
|
}
|
||||||
|
|
||||||
object.compileWithDebug(target, scope, true);
|
object.compileWithDebug(target, scope, true, BreakpointType.STEP_OVER);
|
||||||
target.add(Instruction.keys(loc(), true));
|
target.add(Instruction.keys(loc(), true));
|
||||||
|
|
||||||
int start = target.size();
|
int start = target.size();
|
||||||
@ -43,10 +44,11 @@ public class ForInStatement extends Statement {
|
|||||||
target.add(Instruction.nop(loc()));
|
target.add(Instruction.nop(loc()));
|
||||||
|
|
||||||
target.add(Instruction.loadMember(varLocation, "value"));
|
target.add(Instruction.loadMember(varLocation, "value"));
|
||||||
|
target.queueDebug(BreakpointType.STEP_OVER, object.loc());
|
||||||
target.add(Instruction.storeVar(varLocation, key));
|
target.add(Instruction.storeVar(varLocation, key));
|
||||||
target.setDebug();
|
|
||||||
|
|
||||||
body.compileWithDebug(target, scope, false);
|
target.queueDebug(BreakpointType.STEP_OVER, body.loc());
|
||||||
|
body.compile(target, scope, false);
|
||||||
|
|
||||||
int end = target.size();
|
int end = target.size();
|
||||||
|
|
||||||
@ -57,7 +59,6 @@ public class ForInStatement extends Statement {
|
|||||||
target.set(mid, Instruction.jmpIf(loc(), end - mid + 1));
|
target.set(mid, Instruction.jmpIf(loc(), end - mid + 1));
|
||||||
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
||||||
target.get(first).locate(loc());
|
target.get(first).locate(loc());
|
||||||
target.setDebug(first);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ForInStatement(Location loc, Location varLocation, String label, boolean isDecl, String varName, Statement varValue, Statement object, Statement body) {
|
public ForInStatement(Location loc, Location varLocation, String label, boolean isDecl, String varName, Statement varValue, Statement object, Statement body) {
|
||||||
|
@ -2,12 +2,10 @@ package me.topchetoeu.jscript.compilation.control;
|
|||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.CompoundStatement;
|
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.values.ConstantStatement;
|
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
|
||||||
|
|
||||||
public class ForStatement extends Statement {
|
public class ForStatement extends Statement {
|
||||||
public final Statement declaration, assignment, condition, body;
|
public final Statement declaration, assignment, condition, body;
|
||||||
@ -20,29 +18,15 @@ public class ForStatement extends Statement {
|
|||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
declaration.compile(target, scope, false);
|
declaration.compileWithDebug(target, scope, false, BreakpointType.STEP_OVER);
|
||||||
|
|
||||||
if (condition instanceof ConstantStatement) {
|
|
||||||
if (Values.toBoolean(((ConstantStatement)condition).value)) {
|
|
||||||
int start = target.size();
|
|
||||||
body.compile(target, scope, false);
|
|
||||||
int mid = target.size();
|
|
||||||
assignment.compileWithDebug(target, scope, false);
|
|
||||||
int end = target.size();
|
|
||||||
WhileStatement.replaceBreaks(target, label, start, mid, mid, end + 1);
|
|
||||||
target.add(Instruction.jmp(loc(), start - target.size()));
|
|
||||||
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int start = target.size();
|
int start = target.size();
|
||||||
condition.compile(target, scope, true);
|
condition.compileWithDebug(target, scope, true, BreakpointType.STEP_OVER);
|
||||||
int mid = target.size();
|
int mid = target.size();
|
||||||
target.add(Instruction.nop(null));
|
target.add(Instruction.nop(null));
|
||||||
body.compile(target, scope, false);
|
body.compileWithDebug(target, scope, false, BreakpointType.STEP_OVER);
|
||||||
int beforeAssign = target.size();
|
int beforeAssign = target.size();
|
||||||
assignment.compileWithDebug(target, scope, false);
|
assignment.compileWithDebug(target, scope, false, BreakpointType.STEP_OVER);
|
||||||
int end = target.size();
|
int end = target.size();
|
||||||
|
|
||||||
WhileStatement.replaceBreaks(target, label, mid + 1, end, beforeAssign, end + 1);
|
WhileStatement.replaceBreaks(target, label, mid + 1, end, beforeAssign, end + 1);
|
||||||
@ -51,28 +35,6 @@ public class ForStatement extends Statement {
|
|||||||
target.set(mid, Instruction.jmpIfNot(loc(), end - mid + 1));
|
target.set(mid, Instruction.jmpIfNot(loc(), end - mid + 1));
|
||||||
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
||||||
}
|
}
|
||||||
@Override
|
|
||||||
public Statement optimize() {
|
|
||||||
var decl = declaration.optimize();
|
|
||||||
var asgn = assignment.optimize();
|
|
||||||
var cond = condition.optimize();
|
|
||||||
var b = body.optimize();
|
|
||||||
|
|
||||||
if (asgn.pure()) {
|
|
||||||
if (decl.pure()) return new WhileStatement(loc(), label, cond, b).optimize();
|
|
||||||
else return new CompoundStatement(loc(),
|
|
||||||
decl, new WhileStatement(loc(), label, cond, b)
|
|
||||||
).optimize();
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (b instanceof ContinueStatement) return new CompoundStatement(loc(),
|
|
||||||
decl, new WhileStatement(loc(), label, cond, new CompoundStatement(loc(), b, asgn))
|
|
||||||
);
|
|
||||||
else if (b instanceof BreakStatement) return decl;
|
|
||||||
|
|
||||||
if (b.pure()) return new ForStatement(loc(), label, decl, cond, asgn, new CompoundStatement(null));
|
|
||||||
else return new ForStatement(loc(), label, decl, cond, asgn, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ForStatement(Location loc, String label, Statement declaration, Statement condition, Statement assignment, Statement body) {
|
public ForStatement(Location loc, String label, Statement declaration, Statement condition, Statement assignment, Statement body) {
|
||||||
super(loc);
|
super(loc);
|
||||||
@ -82,14 +44,4 @@ public class ForStatement extends Statement {
|
|||||||
this.assignment = assignment;
|
this.assignment = assignment;
|
||||||
this.body = body;
|
this.body = body;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CompoundStatement ofFor(Location loc, String label, Statement declaration, Statement condition, Statement increment, Statement body) {
|
|
||||||
return new CompoundStatement(loc,
|
|
||||||
declaration,
|
|
||||||
new WhileStatement(loc, label, condition, new CompoundStatement(loc,
|
|
||||||
body,
|
|
||||||
increment
|
|
||||||
))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,10 @@ package me.topchetoeu.jscript.compilation.control;
|
|||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.CompoundStatement;
|
|
||||||
import me.topchetoeu.jscript.compilation.DiscardStatement;
|
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.compilation.values.ConstantStatement;
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
|
||||||
|
|
||||||
public class IfStatement extends Statement {
|
public class IfStatement extends Statement {
|
||||||
public final Statement condition, body, elseBody;
|
public final Statement condition, body, elseBody;
|
||||||
@ -20,52 +17,32 @@ public class IfStatement extends Statement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compileWithDebug(CompileTarget target, ScopeRecord scope, boolean pollute, BreakpointType breakpoint) {
|
||||||
if (condition instanceof ConstantStatement) {
|
condition.compileWithDebug(target, scope, true, breakpoint);
|
||||||
if (Values.not(((ConstantStatement)condition).value)) {
|
|
||||||
if (elseBody != null) elseBody.compileWithDebug(target, scope, pollute);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
body.compileWithDebug(target, scope, pollute);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
condition.compile(target, scope, true);
|
|
||||||
|
|
||||||
if (elseBody == null) {
|
if (elseBody == null) {
|
||||||
int i = target.size();
|
int i = target.size();
|
||||||
target.add(Instruction.nop(null));
|
target.add(Instruction.nop(null));
|
||||||
body.compileWithDebug(target, scope, pollute);
|
body.compileWithDebug(target, scope, pollute, breakpoint);
|
||||||
int endI = target.size();
|
int endI = target.size();
|
||||||
target.set(i, Instruction.jmpIfNot(loc(), endI - i));
|
target.set(i, Instruction.jmpIfNot(loc(), endI - i));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
int start = target.size();
|
int start = target.size();
|
||||||
target.add(Instruction.nop(null));
|
target.add(Instruction.nop(null));
|
||||||
body.compileWithDebug(target, scope, pollute);
|
body.compileWithDebug(target, scope, pollute, breakpoint);
|
||||||
target.add(Instruction.nop(null));
|
target.add(Instruction.nop(null));
|
||||||
int mid = target.size();
|
int mid = target.size();
|
||||||
elseBody.compileWithDebug(target, scope, pollute);
|
elseBody.compileWithDebug(target, scope, pollute, breakpoint);
|
||||||
int end = target.size();
|
int end = target.size();
|
||||||
|
|
||||||
target.set(start, Instruction.jmpIfNot(loc(), mid - start));
|
target.set(start, Instruction.jmpIfNot(loc(), mid - start));
|
||||||
target.set(mid - 1, Instruction.jmp(loc(), end - mid + 1));
|
target.set(mid - 1, Instruction.jmp(loc(), end - mid + 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Statement optimize() {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
var cond = condition.optimize();
|
compileWithDebug(target, scope, pollute, BreakpointType.STEP_IN);
|
||||||
var b = body.optimize();
|
|
||||||
var e = elseBody == null ? null : elseBody.optimize();
|
|
||||||
|
|
||||||
if (b.pure()) b = new CompoundStatement(null);
|
|
||||||
if (e != null && e.pure()) e = null;
|
|
||||||
|
|
||||||
if (b.pure() && e == null) return new DiscardStatement(loc(), cond).optimize();
|
|
||||||
else return new IfStatement(loc(), cond, b, e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IfStatement(Location loc, Statement condition, Statement body, Statement elseBody) {
|
public IfStatement(Location loc, Statement condition, Statement body, Statement elseBody) {
|
||||||
|
@ -6,6 +6,7 @@ import me.topchetoeu.jscript.Location;
|
|||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction.Type;
|
import me.topchetoeu.jscript.compilation.Instruction.Type;
|
||||||
import me.topchetoeu.jscript.engine.Operation;
|
import me.topchetoeu.jscript.engine.Operation;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
@ -36,7 +37,7 @@ public class SwitchStatement extends Statement {
|
|||||||
var caseToStatement = new HashMap<Integer, Integer>();
|
var caseToStatement = new HashMap<Integer, Integer>();
|
||||||
var statementToIndex = new HashMap<Integer, Integer>();
|
var statementToIndex = new HashMap<Integer, Integer>();
|
||||||
|
|
||||||
value.compile(target, scope, true);
|
value.compileWithDebug(target, scope, true, BreakpointType.STEP_OVER);
|
||||||
|
|
||||||
for (var ccase : cases) {
|
for (var ccase : cases) {
|
||||||
target.add(Instruction.dup(loc()));
|
target.add(Instruction.dup(loc()));
|
||||||
@ -52,7 +53,7 @@ public class SwitchStatement extends Statement {
|
|||||||
|
|
||||||
for (var stm : body) {
|
for (var stm : body) {
|
||||||
statementToIndex.put(statementToIndex.size(), target.size());
|
statementToIndex.put(statementToIndex.size(), target.size());
|
||||||
stm.compileWithDebug(target, scope, false);
|
stm.compileWithDebug(target, scope, false, BreakpointType.STEP_OVER);
|
||||||
}
|
}
|
||||||
|
|
||||||
int end = target.size();
|
int end = target.size();
|
||||||
|
@ -25,27 +25,28 @@ public class TryStatement extends Statement {
|
|||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
target.add(Instruction.nop(null));
|
target.add(Instruction.nop(null));
|
||||||
|
|
||||||
int start = target.size(), tryN, catchN = -1, finN = -1;
|
int start = target.size(), catchStart = -1, finallyStart = -1;
|
||||||
|
|
||||||
tryBody.compile(target, scope, false);
|
tryBody.compile(target, scope, false);
|
||||||
tryN = target.size() - start;
|
target.add(Instruction.tryEnd(loc()));
|
||||||
|
|
||||||
if (catchBody != null) {
|
if (catchBody != null) {
|
||||||
int tmp = target.size();
|
catchStart = target.size() - start;
|
||||||
var local = scope instanceof GlobalScope ? scope.child() : (LocalScopeRecord)scope;
|
var local = scope instanceof GlobalScope ? scope.child() : (LocalScopeRecord)scope;
|
||||||
local.define(name, true);
|
local.define(name, true);
|
||||||
catchBody.compile(target, scope, false);
|
catchBody.compile(target, scope, false);
|
||||||
local.undefine();
|
local.undefine();
|
||||||
catchN = target.size() - tmp;
|
target.add(Instruction.tryEnd(loc()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (finallyBody != null) {
|
if (finallyBody != null) {
|
||||||
int tmp = target.size();
|
finallyStart = target.size() - start;
|
||||||
finallyBody.compile(target, scope, false);
|
finallyBody.compile(target, scope, false);
|
||||||
finN = target.size() - tmp;
|
target.add(Instruction.tryEnd(loc()));
|
||||||
}
|
}
|
||||||
|
|
||||||
target.set(start - 1, Instruction.tryInstr(loc(), tryN, catchN, finN));
|
|
||||||
|
target.set(start - 1, Instruction.tryStart(loc(), catchStart, finallyStart, target.size() - start));
|
||||||
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,13 +3,10 @@ package me.topchetoeu.jscript.compilation.control;
|
|||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.CompoundStatement;
|
|
||||||
import me.topchetoeu.jscript.compilation.DiscardStatement;
|
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction.Type;
|
import me.topchetoeu.jscript.compilation.Instruction.Type;
|
||||||
import me.topchetoeu.jscript.compilation.values.ConstantStatement;
|
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
|
||||||
|
|
||||||
public class WhileStatement extends Statement {
|
public class WhileStatement extends Statement {
|
||||||
public final Statement condition, body;
|
public final Statement condition, body;
|
||||||
@ -21,22 +18,11 @@ public class WhileStatement extends Statement {
|
|||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
if (condition instanceof ConstantStatement) {
|
|
||||||
if (Values.toBoolean(((ConstantStatement)condition).value)) {
|
|
||||||
int start = target.size();
|
|
||||||
body.compile(target, scope, false);
|
|
||||||
int end = target.size();
|
|
||||||
replaceBreaks(target, label, start, end, start, end + 1);
|
|
||||||
target.add(Instruction.jmp(loc(), start - target.size()));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int start = target.size();
|
int start = target.size();
|
||||||
condition.compile(target, scope, true);
|
condition.compile(target, scope, true);
|
||||||
int mid = target.size();
|
int mid = target.size();
|
||||||
target.add(Instruction.nop(null));
|
target.add(Instruction.nop(null));
|
||||||
body.compile(target, scope, false);
|
body.compileWithDebug(target, scope, false, BreakpointType.STEP_OVER);
|
||||||
|
|
||||||
int end = target.size();
|
int end = target.size();
|
||||||
|
|
||||||
@ -46,19 +32,6 @@ public class WhileStatement extends Statement {
|
|||||||
target.set(mid, Instruction.jmpIfNot(loc(), end - mid + 1));
|
target.set(mid, Instruction.jmpIfNot(loc(), end - mid + 1));
|
||||||
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
||||||
}
|
}
|
||||||
@Override
|
|
||||||
public Statement optimize() {
|
|
||||||
var cond = condition.optimize();
|
|
||||||
var b = body.optimize();
|
|
||||||
|
|
||||||
if (b instanceof ContinueStatement) {
|
|
||||||
b = new CompoundStatement(loc());
|
|
||||||
}
|
|
||||||
else if (b instanceof BreakStatement) return new DiscardStatement(loc(), cond).optimize();
|
|
||||||
|
|
||||||
if (b.pure()) return new WhileStatement(loc(), label, cond, new CompoundStatement(null));
|
|
||||||
else return new WhileStatement(loc(), label, cond, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
public WhileStatement(Location loc, String label, Statement condition, Statement body) {
|
public WhileStatement(Location loc, String label, Statement condition, Statement body) {
|
||||||
super(loc);
|
super(loc);
|
||||||
@ -71,21 +44,11 @@ public class WhileStatement extends Statement {
|
|||||||
for (int i = start; i < end; i++) {
|
for (int i = start; i < end; i++) {
|
||||||
var instr = target.get(i);
|
var instr = target.get(i);
|
||||||
if (instr.type == Type.NOP && instr.is(0, "cont") && (instr.get(1) == null || instr.is(1, label))) {
|
if (instr.type == Type.NOP && instr.is(0, "cont") && (instr.get(1) == null || instr.is(1, label))) {
|
||||||
target.set(i, Instruction.jmp(instr.location, continuePoint - i));
|
target.set(i, Instruction.jmp(instr.location, continuePoint - i).setDbgData(target.get(i)));
|
||||||
}
|
}
|
||||||
if (instr.type == Type.NOP && instr.is(0, "break") && (instr.get(1) == null || instr.is(1, label))) {
|
if (instr.type == Type.NOP && instr.is(0, "break") && (instr.get(1) == null || instr.is(1, label))) {
|
||||||
target.set(i, Instruction.jmp(instr.location, breakPoint - i));
|
target.set(i, Instruction.jmp(instr.location, breakPoint - i).setDbgData(target.get(i)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// public static CompoundStatement ofFor(Location loc, String label, Statement declaration, Statement condition, Statement increment, Statement body) {
|
|
||||||
// return new CompoundStatement(loc,
|
|
||||||
// declaration,
|
|
||||||
// new WhileStatement(loc, label, condition, new CompoundStatement(loc,
|
|
||||||
// body,
|
|
||||||
// increment
|
|
||||||
// ))
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
@ -1,36 +1,41 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class ArrayStatement extends Statement {
|
public class ArrayStatement extends Statement {
|
||||||
public final Statement[] statements;
|
public final Statement[] statements;
|
||||||
|
|
||||||
@Override
|
@Override public boolean pure() {
|
||||||
public boolean pure() { return true; }
|
for (var stm : statements) {
|
||||||
|
if (!stm.pure()) return false;
|
||||||
@Override
|
}
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
target.add(Instruction.loadArr(loc(), statements.length));
|
return true;
|
||||||
|
}
|
||||||
for (var i = 0; i < statements.length; i++) {
|
|
||||||
var el = statements[i];
|
@Override
|
||||||
if (el != null) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
target.add(Instruction.dup(loc()));
|
target.add(Instruction.loadArr(loc(), statements.length));
|
||||||
target.add(Instruction.loadValue(loc(), i));
|
|
||||||
el.compile(target, scope, true);
|
for (var i = 0; i < statements.length; i++) {
|
||||||
target.add(Instruction.storeMember(loc()));
|
var el = statements[i];
|
||||||
}
|
if (el != null) {
|
||||||
}
|
target.add(Instruction.dup(loc()));
|
||||||
|
target.add(Instruction.loadValue(loc(), i));
|
||||||
if (!pollute) target.add(Instruction.discard(loc()));
|
el.compile(target, scope, true);
|
||||||
}
|
target.add(Instruction.storeMember(loc()));
|
||||||
|
}
|
||||||
public ArrayStatement(Location loc, Statement[] statements) {
|
}
|
||||||
super(loc);
|
|
||||||
this.statements = statements;
|
if (!pollute) target.add(Instruction.discard(loc()));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public ArrayStatement(Location loc, Statement[] statements) {
|
||||||
|
super(loc);
|
||||||
|
this.statements = statements;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -4,15 +4,18 @@ import me.topchetoeu.jscript.Location;
|
|||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class CallStatement extends Statement {
|
public class CallStatement extends Statement {
|
||||||
public final Statement func;
|
public final Statement func;
|
||||||
public final Statement[] args;
|
public final Statement[] args;
|
||||||
|
public final boolean isNew;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compileWithDebug(CompileTarget target, ScopeRecord scope, boolean pollute, BreakpointType type) {
|
||||||
if (func instanceof IndexStatement) {
|
if (isNew) func.compile(target, scope, true);
|
||||||
|
else if (func instanceof IndexStatement) {
|
||||||
((IndexStatement)func).compile(target, scope, true, true);
|
((IndexStatement)func).compile(target, scope, true, true);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -22,18 +25,26 @@ public class CallStatement extends Statement {
|
|||||||
|
|
||||||
for (var arg : args) arg.compile(target, scope, true);
|
for (var arg : args) arg.compile(target, scope, true);
|
||||||
|
|
||||||
target.add(Instruction.call(loc(), args.length));
|
target.queueDebug(type);
|
||||||
target.setDebug();
|
if (isNew) target.add(Instruction.callNew(loc(), args.length));
|
||||||
|
else target.add(Instruction.call(loc(), args.length));
|
||||||
|
|
||||||
if (!pollute) target.add(Instruction.discard(loc()));
|
if (!pollute) target.add(Instruction.discard(loc()));
|
||||||
}
|
}
|
||||||
|
@Override
|
||||||
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
|
compileWithDebug(target, scope, pollute, BreakpointType.STEP_IN);
|
||||||
|
}
|
||||||
|
|
||||||
public CallStatement(Location loc, Statement func, Statement ...args) {
|
public CallStatement(Location loc, boolean isNew, Statement func, Statement ...args) {
|
||||||
super(loc);
|
super(loc);
|
||||||
|
this.isNew = isNew;
|
||||||
this.func = func;
|
this.func = func;
|
||||||
this.args = args;
|
this.args = args;
|
||||||
}
|
}
|
||||||
public CallStatement(Location loc, Statement obj, Object key, Statement ...args) {
|
public CallStatement(Location loc, boolean isNew, Statement obj, Object key, Statement ...args) {
|
||||||
super(loc);
|
super(loc);
|
||||||
|
this.isNew = isNew;
|
||||||
this.func = new IndexStatement(loc, obj, new ConstantStatement(loc, key));
|
this.func = new IndexStatement(loc, obj, new ConstantStatement(loc, key));
|
||||||
this.args = args;
|
this.args = args;
|
||||||
}
|
}
|
||||||
|
@ -1,38 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
|
||||||
|
|
||||||
import java.util.Vector;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
|
||||||
|
|
||||||
public class CommaStatement extends Statement {
|
|
||||||
public final Statement[] values;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
for (var i = 0; i < values.length; i++) {
|
|
||||||
values[i].compileWithDebug(target, scope, i == values.length - 1 && pollute);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Statement optimize() {
|
|
||||||
var res = new Vector<Statement>(values.length);
|
|
||||||
|
|
||||||
for (var i = 0; i < values.length; i++) {
|
|
||||||
var stm = values[i].optimize();
|
|
||||||
if (i < values.length - 1 && stm.pure()) continue;
|
|
||||||
res.add(stm);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res.size() == 1) return res.get(0);
|
|
||||||
else return new CommaStatement(loc(), res.toArray(Statement[]::new));
|
|
||||||
}
|
|
||||||
|
|
||||||
public CommaStatement(Location loc, Statement ...args) {
|
|
||||||
super(loc);
|
|
||||||
this.values = args;
|
|
||||||
}
|
|
||||||
}
|
|
@ -9,8 +9,7 @@ import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
|||||||
public class ConstantStatement extends Statement {
|
public class ConstantStatement extends Statement {
|
||||||
public final Object value;
|
public final Object value;
|
||||||
|
|
||||||
@Override
|
@Override public boolean pure() { return true; }
|
||||||
public boolean pure() { return true; }
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
|
@ -1,30 +1,24 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class VoidStatement extends Statement {
|
public class DiscardStatement extends Statement {
|
||||||
public final Statement value;
|
public final Statement value;
|
||||||
|
|
||||||
|
@Override public boolean pure() { return value.pure(); }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
if (value != null) value.compile(target, scope, false);
|
value.compile(target, scope, false);
|
||||||
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public DiscardStatement(Location loc, Statement val) {
|
||||||
public Statement optimize() {
|
|
||||||
if (value == null) return this;
|
|
||||||
var val = value.optimize();
|
|
||||||
if (val.pure()) return new ConstantStatement(loc(), null);
|
|
||||||
else return new VoidStatement(loc(), val);
|
|
||||||
}
|
|
||||||
|
|
||||||
public VoidStatement(Location loc, Statement value) {
|
|
||||||
super(loc);
|
super(loc);
|
||||||
this.value = value;
|
this.value = val;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -17,15 +17,15 @@ public class FunctionStatement extends Statement {
|
|||||||
public final String varName;
|
public final String varName;
|
||||||
public final String[] args;
|
public final String[] args;
|
||||||
public final boolean statement;
|
public final boolean statement;
|
||||||
|
public final Location end;
|
||||||
|
|
||||||
private static Random rand = new Random();
|
private static Random rand = new Random();
|
||||||
|
|
||||||
@Override
|
@Override public boolean pure() { return varName == null && statement; }
|
||||||
public boolean pure() { return varName == null; }
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void declare(ScopeRecord scope) {
|
public void declare(ScopeRecord scope) {
|
||||||
if (varName != null) scope.define(varName);
|
if (varName != null && statement) scope.define(varName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void checkBreakAndCont(CompileTarget target, int start) {
|
public static void checkBreakAndCont(CompileTarget target, int start) {
|
||||||
@ -41,7 +41,7 @@ public class FunctionStatement extends Statement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected long compileBody(CompileTarget target, ScopeRecord scope, boolean polute) {
|
private long compileBody(CompileTarget target, ScopeRecord scope, boolean polute) {
|
||||||
for (var i = 0; i < args.length; i++) {
|
for (var i = 0; i < args.length; i++) {
|
||||||
for (var j = 0; j < i; j++) {
|
for (var j = 0; j < i; j++) {
|
||||||
if (args[i].equals(args[j])) {
|
if (args[i].equals(args[j])) {
|
||||||
@ -71,11 +71,14 @@ public class FunctionStatement extends Statement {
|
|||||||
|
|
||||||
body.declare(subscope);
|
body.declare(subscope);
|
||||||
body.compile(subtarget, subscope, false);
|
body.compile(subtarget, subscope, false);
|
||||||
subtarget.add(Instruction.ret(subtarget.lastLoc(loc())));
|
subtarget.add(Instruction.ret(end));
|
||||||
checkBreakAndCont(subtarget, 0);
|
checkBreakAndCont(subtarget, 0);
|
||||||
|
|
||||||
if (polute) target.add(Instruction.loadFunc(loc(), id, subscope.getCaptures()));
|
if (polute) target.add(Instruction.loadFunc(loc(), id, subscope.getCaptures()));
|
||||||
target.functions.put(id, new FunctionBody(subscope.localsCount(), args.length, subtarget.array(), subscope.captures(), subscope.locals()));
|
target.functions.put(id, new FunctionBody(
|
||||||
|
subscope.localsCount(), args.length,
|
||||||
|
subtarget.array(), subscope.captures(), subscope.locals()
|
||||||
|
));
|
||||||
|
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
@ -106,9 +109,10 @@ public class FunctionStatement extends Statement {
|
|||||||
compile(target, scope, pollute, null);
|
compile(target, scope, pollute, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public FunctionStatement(Location loc, String varName, String[] args, boolean statement, CompoundStatement body) {
|
public FunctionStatement(Location loc, Location end, String varName, String[] args, boolean statement, CompoundStatement body) {
|
||||||
super(loc);
|
super(loc);
|
||||||
|
|
||||||
|
this.end = end;
|
||||||
this.varName = varName;
|
this.varName = varName;
|
||||||
this.statement = statement;
|
this.statement = statement;
|
||||||
|
|
||||||
|
@ -7,8 +7,7 @@ import me.topchetoeu.jscript.compilation.Instruction;
|
|||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class GlobalThisStatement extends Statement {
|
public class GlobalThisStatement extends Statement {
|
||||||
@Override
|
@Override public boolean pure() { return true; }
|
||||||
public boolean pure() { return true; }
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
|
@ -4,6 +4,7 @@ import me.topchetoeu.jscript.Location;
|
|||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.engine.Operation;
|
import me.topchetoeu.jscript.engine.Operation;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
@ -25,7 +26,7 @@ public class IndexAssignStatement extends Statement {
|
|||||||
target.add(Instruction.operation(loc(), operation));
|
target.add(Instruction.operation(loc(), operation));
|
||||||
|
|
||||||
target.add(Instruction.storeMember(loc(), pollute));
|
target.add(Instruction.storeMember(loc(), pollute));
|
||||||
target.setDebug();
|
target.setDebug(BreakpointType.STEP_IN);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
object.compile(target, scope, true);
|
object.compile(target, scope, true);
|
||||||
@ -33,7 +34,7 @@ public class IndexAssignStatement extends Statement {
|
|||||||
value.compile(target, scope, true);
|
value.compile(target, scope, true);
|
||||||
|
|
||||||
target.add(Instruction.storeMember(loc(), pollute));
|
target.add(Instruction.storeMember(loc(), pollute));
|
||||||
target.setDebug();
|
target.setDebug(BreakpointType.STEP_IN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import me.topchetoeu.jscript.compilation.AssignableStatement;
|
|||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.engine.Operation;
|
import me.topchetoeu.jscript.engine.Operation;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
@ -12,9 +13,6 @@ public class IndexStatement extends AssignableStatement {
|
|||||||
public final Statement object;
|
public final Statement object;
|
||||||
public final Statement index;
|
public final Statement index;
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean pure() { return true; }
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Statement toAssign(Statement val, Operation operation) {
|
public Statement toAssign(Statement val, Operation operation) {
|
||||||
return new IndexAssignStatement(loc(), object, index, val, operation);
|
return new IndexAssignStatement(loc(), object, index, val, operation);
|
||||||
@ -24,13 +22,13 @@ public class IndexStatement extends AssignableStatement {
|
|||||||
if (dupObj) target.add(Instruction.dup(loc()));
|
if (dupObj) target.add(Instruction.dup(loc()));
|
||||||
if (index instanceof ConstantStatement) {
|
if (index instanceof ConstantStatement) {
|
||||||
target.add(Instruction.loadMember(loc(), ((ConstantStatement)index).value));
|
target.add(Instruction.loadMember(loc(), ((ConstantStatement)index).value));
|
||||||
target.setDebug();
|
target.setDebug(BreakpointType.STEP_IN);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
index.compile(target, scope, true);
|
index.compile(target, scope, true);
|
||||||
target.add(Instruction.loadMember(loc()));
|
target.add(Instruction.loadMember(loc()));
|
||||||
target.setDebug();
|
target.setDebug(BreakpointType.STEP_IN);
|
||||||
if (!pollute) target.add(Instruction.discard(loc()));
|
if (!pollute) target.add(Instruction.discard(loc()));
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
|
@ -10,10 +10,7 @@ import me.topchetoeu.jscript.engine.values.Values;
|
|||||||
public class LazyAndStatement extends Statement {
|
public class LazyAndStatement extends Statement {
|
||||||
public final Statement first, second;
|
public final Statement first, second;
|
||||||
|
|
||||||
@Override
|
@Override public boolean pure() { return first.pure() && second.pure(); }
|
||||||
public boolean pure() {
|
|
||||||
return first.pure() && second.pure();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
|
@ -10,10 +10,7 @@ import me.topchetoeu.jscript.engine.values.Values;
|
|||||||
public class LazyOrStatement extends Statement {
|
public class LazyOrStatement extends Statement {
|
||||||
public final Statement first, second;
|
public final Statement first, second;
|
||||||
|
|
||||||
@Override
|
@Override public boolean pure() { return first.pure() && second.pure(); }
|
||||||
public boolean pure() {
|
|
||||||
return first.pure() && second.pure();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
|
@ -1,28 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
|
||||||
|
|
||||||
public class NewStatement extends Statement {
|
|
||||||
public final Statement func;
|
|
||||||
public final Statement[] args;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
func.compile(target, scope, true);
|
|
||||||
|
|
||||||
for (var arg : args) arg.compile(target, scope, true);
|
|
||||||
|
|
||||||
target.add(Instruction.callNew(loc(), args.length));
|
|
||||||
target.setDebug();
|
|
||||||
}
|
|
||||||
|
|
||||||
public NewStatement(Location loc, Statement func, Statement ...args) {
|
|
||||||
super(loc);
|
|
||||||
this.func = func;
|
|
||||||
this.args = args;
|
|
||||||
}
|
|
||||||
}
|
|
@ -14,6 +14,14 @@ public class ObjectStatement extends Statement {
|
|||||||
public final Map<Object, FunctionStatement> getters;
|
public final Map<Object, FunctionStatement> getters;
|
||||||
public final Map<Object, FunctionStatement> setters;
|
public final Map<Object, FunctionStatement> setters;
|
||||||
|
|
||||||
|
@Override public boolean pure() {
|
||||||
|
for (var el : map.values()) {
|
||||||
|
if (!el.pure()) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
target.add(Instruction.loadObj(loc()));
|
target.add(Instruction.loadObj(loc()));
|
||||||
|
@ -4,16 +4,21 @@ import me.topchetoeu.jscript.Location;
|
|||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.compilation.control.ThrowStatement;
|
|
||||||
import me.topchetoeu.jscript.engine.Operation;
|
import me.topchetoeu.jscript.engine.Operation;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
|
||||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
|
||||||
|
|
||||||
public class OperationStatement extends Statement {
|
public class OperationStatement extends Statement {
|
||||||
public final Statement[] args;
|
public final Statement[] args;
|
||||||
public final Operation operation;
|
public final Operation operation;
|
||||||
|
|
||||||
|
@Override public boolean pure() {
|
||||||
|
for (var el : args) {
|
||||||
|
if (!el.pure()) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
for (var arg : args) {
|
for (var arg : args) {
|
||||||
@ -24,39 +29,6 @@ public class OperationStatement extends Statement {
|
|||||||
else target.add(Instruction.discard(loc()));
|
else target.add(Instruction.discard(loc()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean pure() {
|
|
||||||
for (var arg : args) {
|
|
||||||
if (!arg.pure()) return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Statement optimize() {
|
|
||||||
var args = new Statement[this.args.length];
|
|
||||||
var allConst = true;
|
|
||||||
|
|
||||||
for (var i = 0; i < this.args.length; i++) {
|
|
||||||
args[i] = this.args[i].optimize();
|
|
||||||
if (!(args[i] instanceof ConstantStatement)) allConst = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (allConst) {
|
|
||||||
var vals = new Object[this.args.length];
|
|
||||||
|
|
||||||
for (var i = 0; i < args.length; i++) {
|
|
||||||
vals[i] = ((ConstantStatement)args[i]).value;
|
|
||||||
}
|
|
||||||
|
|
||||||
try { return new ConstantStatement(loc(), Values.operation(null, operation, vals)); }
|
|
||||||
catch (EngineException e) { return new ThrowStatement(loc(), new ConstantStatement(loc(), e.value)); }
|
|
||||||
}
|
|
||||||
|
|
||||||
return new OperationStatement(loc(), operation, args);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public OperationStatement(Location loc, Operation operation, Statement ...args) {
|
public OperationStatement(Location loc, Operation operation, Statement ...args) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.operation = operation;
|
this.operation = operation;
|
||||||
|
@ -9,8 +9,8 @@ import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
|||||||
public class RegexStatement extends Statement {
|
public class RegexStatement extends Statement {
|
||||||
public final String pattern, flags;
|
public final String pattern, flags;
|
||||||
|
|
||||||
@Override
|
// Not really pure, since a function is called, but can be ignored.
|
||||||
public boolean pure() { return true; }
|
@Override public boolean pure() { return true; }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
|
@ -5,13 +5,13 @@ import me.topchetoeu.jscript.compilation.CompileTarget;
|
|||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
|
||||||
|
|
||||||
public class TypeofStatement extends Statement {
|
public class TypeofStatement extends Statement {
|
||||||
public final Statement value;
|
public final Statement value;
|
||||||
|
|
||||||
@Override
|
// Not really pure, since a variable from the global scope could be accessed,
|
||||||
public boolean pure() { return true; }
|
// which could lead to code execution, that would get omitted
|
||||||
|
@Override public boolean pure() { return true; }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
@ -26,23 +26,6 @@ public class TypeofStatement extends Statement {
|
|||||||
target.add(Instruction.typeof(loc()));
|
target.add(Instruction.typeof(loc()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Statement optimize() {
|
|
||||||
var val = value.optimize();
|
|
||||||
|
|
||||||
if (val instanceof ConstantStatement) {
|
|
||||||
return new ConstantStatement(loc(), Values.type(((ConstantStatement)val).value));
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
val instanceof ObjectStatement ||
|
|
||||||
val instanceof ArrayStatement ||
|
|
||||||
val instanceof GlobalThisStatement
|
|
||||||
) return new ConstantStatement(loc(), "object");
|
|
||||||
else if(val instanceof FunctionStatement) return new ConstantStatement(loc(), "function");
|
|
||||||
|
|
||||||
return new TypeofStatement(loc(), val);
|
|
||||||
}
|
|
||||||
|
|
||||||
public TypeofStatement(Location loc, Statement value) {
|
public TypeofStatement(Location loc, Statement value) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.value = value;
|
this.value = value;
|
||||||
|
@ -12,6 +12,8 @@ public class VariableAssignStatement extends Statement {
|
|||||||
public final Statement value;
|
public final Statement value;
|
||||||
public final Operation operation;
|
public final Operation operation;
|
||||||
|
|
||||||
|
@Override public boolean pure() { return false; }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
var i = scope.getKey(name);
|
var i = scope.getKey(name);
|
||||||
|
@ -9,8 +9,7 @@ import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
|||||||
public class VariableIndexStatement extends Statement {
|
public class VariableIndexStatement extends Statement {
|
||||||
public final int index;
|
public final int index;
|
||||||
|
|
||||||
@Override
|
@Override public boolean pure() { return true; }
|
||||||
public boolean pure() { return true; }
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
|
@ -11,8 +11,7 @@ import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
|||||||
public class VariableStatement extends AssignableStatement {
|
public class VariableStatement extends AssignableStatement {
|
||||||
public final String name;
|
public final String name;
|
||||||
|
|
||||||
@Override
|
@Override public boolean pure() { return false; }
|
||||||
public boolean pure() { return true; }
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Statement toAssign(Statement val, Operation operation) {
|
public Statement toAssign(Statement val, Operation operation) {
|
||||||
|
@ -1,104 +1,125 @@
|
|||||||
package me.topchetoeu.jscript.engine;
|
package me.topchetoeu.jscript.engine;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.Collections;
|
||||||
import java.util.Stack;
|
import java.util.List;
|
||||||
import java.util.TreeSet;
|
import java.util.Stack;
|
||||||
|
import java.util.TreeSet;
|
||||||
import me.topchetoeu.jscript.Filename;
|
import java.util.stream.Collectors;
|
||||||
import me.topchetoeu.jscript.Location;
|
|
||||||
import me.topchetoeu.jscript.engine.frame.CodeFrame;
|
import me.topchetoeu.jscript.Filename;
|
||||||
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
import me.topchetoeu.jscript.engine.frame.CodeFrame;
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
||||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
||||||
import me.topchetoeu.jscript.mapping.SourceMap;
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
public class Context {
|
import me.topchetoeu.jscript.mapping.SourceMap;
|
||||||
private final Stack<Environment> env = new Stack<>();
|
|
||||||
private final ArrayList<CodeFrame> frames = new ArrayList<>();
|
public class Context {
|
||||||
public final Engine engine;
|
private final Stack<Environment> env = new Stack<>();
|
||||||
|
private final ArrayList<CodeFrame> frames = new ArrayList<>();
|
||||||
public Environment environment() {
|
public final Engine engine;
|
||||||
return env.empty() ? null : env.peek();
|
|
||||||
}
|
public Environment environment() {
|
||||||
|
return env.empty() ? null : env.peek();
|
||||||
public Context pushEnv(Environment env) {
|
}
|
||||||
this.env.push(env);
|
|
||||||
return this;
|
public Context pushEnv(Environment env) {
|
||||||
}
|
this.env.push(env);
|
||||||
public void popEnv() {
|
return this;
|
||||||
if (!env.empty()) this.env.pop();
|
}
|
||||||
}
|
public void popEnv() {
|
||||||
|
if (!env.empty()) this.env.pop();
|
||||||
public FunctionValue compile(Filename filename, String raw) {
|
}
|
||||||
var env = environment();
|
|
||||||
var result = env.compile.call(this, null, raw, filename.toString(), env);
|
public FunctionValue compile(Filename filename, String raw) {
|
||||||
|
var env = environment();
|
||||||
var function = (FunctionValue)Values.getMember(this, result, "function");
|
var result = env.compile.call(this, null, raw, filename.toString(), env);
|
||||||
var rawMapChain = ((ArrayValue)Values.getMember(this, result, "mapChain")).toArray();
|
|
||||||
var maps = new SourceMap[rawMapChain.length];
|
var function = (FunctionValue)Values.getMember(this, result, "function");
|
||||||
for (var i = 0; i < maps.length; i++) maps[i] = SourceMap.parse((String)rawMapChain[i]);
|
if (!engine.debugging) return function;
|
||||||
var map = SourceMap.chain(maps);
|
|
||||||
|
var rawMapChain = ((ArrayValue)Values.getMember(this, result, "mapChain")).toArray();
|
||||||
engine.onSource(filename, raw, new TreeSet<>(), map);
|
var breakpoints = new TreeSet<>(
|
||||||
|
Arrays.stream(((ArrayValue)Values.getMember(this, result, "breakpoints")).toArray())
|
||||||
return function;
|
.map(v -> Location.parse(Values.toString(this, v)))
|
||||||
}
|
.collect(Collectors.toList())
|
||||||
|
);
|
||||||
|
var maps = new SourceMap[rawMapChain.length];
|
||||||
public void pushFrame(CodeFrame frame) {
|
|
||||||
frames.add(frame);
|
for (var i = 0; i < maps.length; i++) maps[i] = SourceMap.parse(Values.toString(this, (String)rawMapChain[i]));
|
||||||
if (frames.size() > engine.maxStackFrames) throw EngineException.ofRange("Stack overflow!");
|
|
||||||
pushEnv(frame.function.environment);
|
var map = SourceMap.chain(maps);
|
||||||
}
|
|
||||||
public boolean popFrame(CodeFrame frame) {
|
if (map != null) {
|
||||||
if (frames.size() == 0) return false;
|
var newBreakpoints = new TreeSet<Location>();
|
||||||
if (frames.get(frames.size() - 1) != frame) return false;
|
for (var bp : breakpoints) {
|
||||||
frames.remove(frames.size() - 1);
|
bp = map.toCompiled(bp);
|
||||||
popEnv();
|
if (bp != null) newBreakpoints.add(bp);
|
||||||
engine.onFramePop(this, frame);
|
}
|
||||||
return true;
|
breakpoints = newBreakpoints;
|
||||||
}
|
}
|
||||||
public CodeFrame peekFrame() {
|
|
||||||
if (frames.size() == 0) return null;
|
engine.onSource(filename, raw, breakpoints, map);
|
||||||
return frames.get(frames.size() - 1);
|
|
||||||
}
|
return function;
|
||||||
|
}
|
||||||
public List<CodeFrame> frames() {
|
|
||||||
return Collections.unmodifiableList(frames);
|
|
||||||
}
|
public void pushFrame(CodeFrame frame) {
|
||||||
public List<String> stackTrace() {
|
frames.add(frame);
|
||||||
var res = new ArrayList<String>();
|
if (frames.size() > engine.maxStackFrames) throw EngineException.ofRange("Stack overflow!");
|
||||||
|
pushEnv(frame.function.environment);
|
||||||
for (var i = frames.size() - 1; i >= 0; i--) {
|
engine.onFramePush(this, frame);
|
||||||
var el = frames.get(i);
|
}
|
||||||
var name = el.function.name;
|
public boolean popFrame(CodeFrame frame) {
|
||||||
Location loc = null;
|
if (frames.size() == 0) return false;
|
||||||
|
if (frames.get(frames.size() - 1) != frame) return false;
|
||||||
for (var j = el.codePtr; j >= 0 && loc == null; j--) loc = el.function.body[j].location;
|
frames.remove(frames.size() - 1);
|
||||||
if (loc == null) loc = el.function.loc();
|
popEnv();
|
||||||
|
engine.onFramePop(this, frame);
|
||||||
var trace = "";
|
return true;
|
||||||
|
}
|
||||||
if (loc != null) trace += "at " + loc.toString() + " ";
|
public CodeFrame peekFrame() {
|
||||||
if (name != null && !name.equals("")) trace += "in " + name + " ";
|
if (frames.size() == 0) return null;
|
||||||
|
return frames.get(frames.size() - 1);
|
||||||
trace = trace.trim();
|
}
|
||||||
|
|
||||||
if (!trace.equals("")) res.add(trace);
|
public List<CodeFrame> frames() {
|
||||||
}
|
return Collections.unmodifiableList(frames);
|
||||||
|
}
|
||||||
return res;
|
public List<String> stackTrace() {
|
||||||
}
|
var res = new ArrayList<String>();
|
||||||
|
|
||||||
public Context(Engine engine) {
|
for (var i = frames.size() - 1; i >= 0; i--) {
|
||||||
this.engine = engine;
|
var el = frames.get(i);
|
||||||
}
|
var name = el.function.name;
|
||||||
public Context(Engine engine, Environment env) {
|
Location loc = null;
|
||||||
this(engine);
|
|
||||||
this.pushEnv(env);
|
for (var j = el.codePtr; j >= 0 && loc == null; j--) loc = el.function.body[j].location;
|
||||||
|
if (loc == null) loc = el.function.loc();
|
||||||
}
|
|
||||||
}
|
var trace = "";
|
||||||
|
|
||||||
|
if (loc != null) trace += "at " + loc.toString() + " ";
|
||||||
|
if (name != null && !name.equals("")) trace += "in " + name + " ";
|
||||||
|
|
||||||
|
trace = trace.trim();
|
||||||
|
|
||||||
|
if (!trace.equals("")) res.add(trace);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Context(Engine engine) {
|
||||||
|
this.engine = engine;
|
||||||
|
}
|
||||||
|
public Context(Engine engine, Environment env) {
|
||||||
|
this(engine);
|
||||||
|
this.pushEnv(env);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -15,6 +15,7 @@ import me.topchetoeu.jscript.events.Awaitable;
|
|||||||
import me.topchetoeu.jscript.events.DataNotifier;
|
import me.topchetoeu.jscript.events.DataNotifier;
|
||||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
import me.topchetoeu.jscript.exceptions.InterruptException;
|
import me.topchetoeu.jscript.exceptions.InterruptException;
|
||||||
|
import me.topchetoeu.jscript.mapping.SourceMap;
|
||||||
|
|
||||||
public class Engine implements DebugController {
|
public class Engine implements DebugController {
|
||||||
private class UncompiledFunction extends FunctionValue {
|
private class UncompiledFunction extends FunctionValue {
|
||||||
@ -66,22 +67,25 @@ public class Engine implements DebugController {
|
|||||||
|
|
||||||
private final HashMap<Filename, String> sources = new HashMap<>();
|
private final HashMap<Filename, String> sources = new HashMap<>();
|
||||||
private final HashMap<Filename, TreeSet<Location>> bpts = new HashMap<>();
|
private final HashMap<Filename, TreeSet<Location>> bpts = new HashMap<>();
|
||||||
|
private final HashMap<Filename, SourceMap> maps = new HashMap<>();
|
||||||
|
|
||||||
private DebugController debugger;
|
private DebugController debugger;
|
||||||
private Thread thread;
|
private Thread thread;
|
||||||
private PriorityBlockingQueue<Task> tasks = new PriorityBlockingQueue<>();
|
private PriorityBlockingQueue<Task> tasks = new PriorityBlockingQueue<>();
|
||||||
|
|
||||||
public boolean attachDebugger(DebugController debugger) {
|
public synchronized boolean attachDebugger(DebugController debugger) {
|
||||||
if (!debugging || this.debugger != null) return false;
|
if (!debugging || this.debugger != null) return false;
|
||||||
|
|
||||||
for (var source : sources.entrySet()) {
|
for (var source : sources.entrySet()) debugger.onSource(
|
||||||
debugger.onSource(source.getKey(), source.getValue(), bpts.get(source.getKey()));
|
source.getKey(), source.getValue(),
|
||||||
}
|
bpts.get(source.getKey()),
|
||||||
|
maps.get(source.getKey())
|
||||||
|
);
|
||||||
|
|
||||||
this.debugger = debugger;
|
this.debugger = debugger;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
public boolean detachDebugger() {
|
public synchronized boolean detachDebugger() {
|
||||||
if (!debugging || this.debugger == null) return false;
|
if (!debugging || this.debugger == null) return false;
|
||||||
this.debugger = null;
|
this.debugger = null;
|
||||||
return true;
|
return true;
|
||||||
@ -122,7 +126,7 @@ public class Engine implements DebugController {
|
|||||||
public boolean inExecThread() {
|
public boolean inExecThread() {
|
||||||
return Thread.currentThread() == thread;
|
return Thread.currentThread() == thread;
|
||||||
}
|
}
|
||||||
public boolean isRunning() {
|
public synchronized boolean isRunning() {
|
||||||
return this.thread != null;
|
return this.thread != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,6 +139,10 @@ public class Engine implements DebugController {
|
|||||||
return pushMsg(micro, ctx, new UncompiledFunction(filename, raw), thisArg, args);
|
return pushMsg(micro, ctx, new UncompiledFunction(filename, raw), thisArg, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFramePush(Context ctx, CodeFrame frame) {
|
||||||
|
if (debugging && debugger != null) debugger.onFramePush(ctx, frame);
|
||||||
|
}
|
||||||
@Override public void onFramePop(Context ctx, CodeFrame frame) {
|
@Override public void onFramePop(Context ctx, CodeFrame frame) {
|
||||||
if (debugging && debugger != null) debugger.onFramePop(ctx, frame);
|
if (debugging && debugger != null) debugger.onFramePop(ctx, frame);
|
||||||
}
|
}
|
||||||
@ -142,11 +150,12 @@ public class Engine implements DebugController {
|
|||||||
if (debugging && debugger != null) return debugger.onInstruction(ctx, frame, instruction, returnVal, error, caught);
|
if (debugging && debugger != null) return debugger.onInstruction(ctx, frame, instruction, returnVal, error, caught);
|
||||||
else return false;
|
else return false;
|
||||||
}
|
}
|
||||||
@Override public void onSource(Filename filename, String source, TreeSet<Location> breakpoints) {
|
@Override public void onSource(Filename filename, String source, TreeSet<Location> breakpoints, SourceMap map) {
|
||||||
if (!debugging) return;
|
if (!debugging) return;
|
||||||
if (debugger != null) debugger.onSource(filename, source, breakpoints);
|
if (debugger != null) debugger.onSource(filename, source, breakpoints, map);
|
||||||
sources.put(filename, source);
|
sources.put(filename, source);
|
||||||
bpts.put(filename, breakpoints);
|
bpts.put(filename, breakpoints);
|
||||||
|
maps.put(filename, map);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Engine(boolean debugging) {
|
public Engine(boolean debugging) {
|
||||||
|
@ -1,114 +1,132 @@
|
|||||||
package me.topchetoeu.jscript.engine;
|
package me.topchetoeu.jscript.engine;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.TreeSet;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Filename;
|
import me.topchetoeu.jscript.Filename;
|
||||||
import me.topchetoeu.jscript.engine.scope.GlobalScope;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
import me.topchetoeu.jscript.engine.scope.GlobalScope;
|
||||||
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
||||||
import me.topchetoeu.jscript.engine.values.NativeFunction;
|
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
||||||
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
import me.topchetoeu.jscript.engine.values.NativeFunction;
|
||||||
import me.topchetoeu.jscript.engine.values.Symbol;
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
import me.topchetoeu.jscript.engine.values.Symbol;
|
||||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
import me.topchetoeu.jscript.filesystem.RootFilesystem;
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
import me.topchetoeu.jscript.interop.Native;
|
import me.topchetoeu.jscript.filesystem.RootFilesystem;
|
||||||
import me.topchetoeu.jscript.interop.NativeGetter;
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
import me.topchetoeu.jscript.interop.NativeSetter;
|
import me.topchetoeu.jscript.interop.NativeGetter;
|
||||||
import me.topchetoeu.jscript.interop.NativeWrapperProvider;
|
import me.topchetoeu.jscript.interop.NativeSetter;
|
||||||
import me.topchetoeu.jscript.parsing.Parsing;
|
import me.topchetoeu.jscript.interop.NativeWrapperProvider;
|
||||||
import me.topchetoeu.jscript.permissions.Permission;
|
import me.topchetoeu.jscript.parsing.Parsing;
|
||||||
import me.topchetoeu.jscript.permissions.PermissionsProvider;
|
import me.topchetoeu.jscript.permissions.Permission;
|
||||||
|
import me.topchetoeu.jscript.permissions.PermissionsProvider;
|
||||||
public class Environment implements PermissionsProvider {
|
|
||||||
private HashMap<String, ObjectValue> prototypes = new HashMap<>();
|
public class Environment implements PermissionsProvider {
|
||||||
|
private HashMap<String, ObjectValue> prototypes = new HashMap<>();
|
||||||
public final Data data = new Data();
|
|
||||||
public static final HashMap<String, Symbol> symbols = new HashMap<>();
|
public final Data data = new Data();
|
||||||
|
public static final HashMap<String, Symbol> symbols = new HashMap<>();
|
||||||
public GlobalScope global;
|
|
||||||
public WrappersProvider wrappers;
|
public GlobalScope global;
|
||||||
public PermissionsProvider permissions = null;
|
public WrappersProvider wrappers;
|
||||||
public final RootFilesystem filesystem = new RootFilesystem(this);
|
public PermissionsProvider permissions = null;
|
||||||
|
public final RootFilesystem filesystem = new RootFilesystem(this);
|
||||||
private static int nextId = 0;
|
|
||||||
|
private static int nextId = 0;
|
||||||
@Native public int id = ++nextId;
|
|
||||||
|
@Native public int id = ++nextId;
|
||||||
@Native public FunctionValue compile = new NativeFunction("compile", (ctx, thisArg, args) -> {
|
|
||||||
var source = Values.toString(ctx, args[0]);
|
@Native public FunctionValue compile = new NativeFunction("compile", (ctx, thisArg, args) -> {
|
||||||
var filename = Values.toString(ctx, args[1]);
|
var source = Values.toString(ctx, args[0]);
|
||||||
var env = Values.wrapper(args[2], Environment.class);
|
var filename = Values.toString(ctx, args[1]);
|
||||||
var res = new ObjectValue();
|
var isDebug = Values.toBoolean(args[2]);
|
||||||
|
|
||||||
res.defineProperty(ctx, "function", Parsing.compile(Engine.functions, new TreeSet<>(), env, Filename.parse(filename), source));
|
var env = Values.wrapper(args[2], Environment.class);
|
||||||
res.defineProperty(ctx, "mapChain", new ArrayValue());
|
var res = new ObjectValue();
|
||||||
|
|
||||||
return res;
|
System.out.println(source);
|
||||||
});
|
|
||||||
@Native public FunctionValue regexConstructor = new NativeFunction("RegExp", (ctx, thisArg, args) -> {
|
var target = Parsing.compile(env, Filename.parse(filename), source);
|
||||||
throw EngineException.ofError("Regular expressions not supported.").setCtx(ctx.environment(), ctx.engine);
|
Engine.functions.putAll(target.functions);
|
||||||
});
|
Engine.functions.remove(0l);
|
||||||
|
|
||||||
public Environment addData(Data data) {
|
res.defineProperty(ctx, "function", target.func(env));
|
||||||
this.data.addAll(data);
|
res.defineProperty(ctx, "mapChain", new ArrayValue());
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
if (isDebug) {
|
||||||
@Native public ObjectValue proto(String name) {
|
res.defineProperty(ctx, "breakpoints", ArrayValue.of(ctx, target.breakpoints.stream().map(Location::toString).collect(Collectors.toList())));
|
||||||
return prototypes.get(name);
|
}
|
||||||
}
|
|
||||||
@Native public void setProto(String name, ObjectValue val) {
|
return res;
|
||||||
prototypes.put(name, val);
|
});
|
||||||
}
|
@Native public FunctionValue regexConstructor = new NativeFunction("RegExp", (ctx, thisArg, args) -> {
|
||||||
|
throw EngineException.ofError("Regular expressions not supported.").setCtx(ctx.environment(), ctx.engine);
|
||||||
@Native public Symbol symbol(String name) {
|
});
|
||||||
if (symbols.containsKey(name)) return symbols.get(name);
|
|
||||||
else {
|
public Environment addData(Data data) {
|
||||||
var res = new Symbol(name);
|
this.data.addAll(data);
|
||||||
symbols.put(name, res);
|
return this;
|
||||||
return res;
|
}
|
||||||
}
|
|
||||||
}
|
@Native public ObjectValue proto(String name) {
|
||||||
|
return prototypes.get(name);
|
||||||
@NativeGetter("global") public ObjectValue getGlobal() {
|
}
|
||||||
return global.obj;
|
@Native public void setProto(String name, ObjectValue val) {
|
||||||
}
|
prototypes.put(name, val);
|
||||||
@NativeSetter("global") public void setGlobal(ObjectValue val) {
|
}
|
||||||
global = new GlobalScope(val);
|
|
||||||
}
|
@Native public Symbol symbol(String name) {
|
||||||
|
return getSymbol(name);
|
||||||
@Native public Environment fork() {
|
}
|
||||||
var res = new Environment(compile, null, global);
|
|
||||||
res.wrappers = wrappers.fork(res);
|
@NativeGetter("global") public ObjectValue getGlobal() {
|
||||||
res.regexConstructor = regexConstructor;
|
return global.obj;
|
||||||
res.prototypes = new HashMap<>(prototypes);
|
}
|
||||||
return res;
|
@NativeSetter("global") public void setGlobal(ObjectValue val) {
|
||||||
}
|
global = new GlobalScope(val);
|
||||||
@Native public Environment child() {
|
}
|
||||||
var res = fork();
|
|
||||||
res.global = res.global.globalChild();
|
@Native public Environment fork() {
|
||||||
return res;
|
var res = new Environment(compile, null, global);
|
||||||
}
|
res.wrappers = wrappers.fork(res);
|
||||||
|
res.regexConstructor = regexConstructor;
|
||||||
@Override public boolean hasPermission(Permission perm, char delim) {
|
res.prototypes = new HashMap<>(prototypes);
|
||||||
return permissions == null || permissions.hasPermission(perm, delim);
|
return res;
|
||||||
}
|
}
|
||||||
@Override public boolean hasPermission(Permission perm) {
|
@Native public Environment child() {
|
||||||
return permissions == null || permissions.hasPermission(perm);
|
var res = fork();
|
||||||
}
|
res.global = res.global.globalChild();
|
||||||
|
return res;
|
||||||
public Context context(Engine engine) {
|
}
|
||||||
return new Context(engine).pushEnv(this);
|
|
||||||
}
|
@Override public boolean hasPermission(Permission perm, char delim) {
|
||||||
|
return permissions == null || permissions.hasPermission(perm, delim);
|
||||||
public Environment(FunctionValue compile, WrappersProvider nativeConverter, GlobalScope global) {
|
}
|
||||||
if (compile != null) this.compile = compile;
|
@Override public boolean hasPermission(Permission perm) {
|
||||||
if (nativeConverter == null) nativeConverter = new NativeWrapperProvider(this);
|
return permissions == null || permissions.hasPermission(perm);
|
||||||
if (global == null) global = new GlobalScope();
|
}
|
||||||
|
|
||||||
this.wrappers = nativeConverter;
|
public Context context(Engine engine) {
|
||||||
this.global = global;
|
return new Context(engine).pushEnv(this);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public static Symbol getSymbol(String name) {
|
||||||
|
if (symbols.containsKey(name)) return symbols.get(name);
|
||||||
|
else {
|
||||||
|
var res = new Symbol(name);
|
||||||
|
symbols.put(name, res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Environment(FunctionValue compile, WrappersProvider nativeConverter, GlobalScope global) {
|
||||||
|
if (compile != null) this.compile = compile;
|
||||||
|
if (nativeConverter == null) nativeConverter = new NativeWrapperProvider(this);
|
||||||
|
if (global == null) global = new GlobalScope();
|
||||||
|
|
||||||
|
this.wrappers = nativeConverter;
|
||||||
|
this.global = global;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,40 +1,51 @@
|
|||||||
package me.topchetoeu.jscript.engine.debug;
|
package me.topchetoeu.jscript.engine.debug;
|
||||||
|
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Filename;
|
import me.topchetoeu.jscript.Filename;
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
import me.topchetoeu.jscript.engine.frame.CodeFrame;
|
import me.topchetoeu.jscript.engine.frame.CodeFrame;
|
||||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
|
import me.topchetoeu.jscript.mapping.SourceMap;
|
||||||
public interface DebugController {
|
|
||||||
/**
|
public interface DebugController {
|
||||||
* Called when a script has been loaded
|
/**
|
||||||
* @param breakpoints
|
* Called when a script has been loaded
|
||||||
*/
|
* @param filename The name of the source
|
||||||
void onSource(Filename filename, String source, TreeSet<Location> breakpoints);
|
* @param source The name of the source
|
||||||
|
* @param breakpoints A set of all the breakpointable locations in this source
|
||||||
/**
|
* @param map The source map associated with this file. null if this source map isn't mapped
|
||||||
* Called immediatly before an instruction is executed, as well as after an instruction, if it has threw or returned.
|
*/
|
||||||
* This function might pause in order to await debugging commands.
|
void onSource(Filename filename, String source, TreeSet<Location> breakpoints, SourceMap map);
|
||||||
* @param ctx The context of execution
|
|
||||||
* @param frame The frame in which execution is occuring
|
/**
|
||||||
* @param instruction The instruction which was or will be executed
|
* Called immediatly before an instruction is executed, as well as after an instruction, if it has threw or returned.
|
||||||
* @param loc The most recent location the code frame has been at
|
* This function might pause in order to await debugging commands.
|
||||||
* @param returnVal The return value of the instruction, Runners.NO_RETURN if none
|
* @param ctx The context of execution
|
||||||
* @param error The error that the instruction threw, null if none
|
* @param frame The frame in which execution is occuring
|
||||||
* @param caught Whether or not the error has been caught
|
* @param instruction The instruction which was or will be executed
|
||||||
* @return Whether or not the frame should restart
|
* @param loc The most recent location the code frame has been at
|
||||||
*/
|
* @param returnVal The return value of the instruction, Runners.NO_RETURN if none
|
||||||
boolean onInstruction(Context ctx, CodeFrame frame, Instruction instruction, Object returnVal, EngineException error, boolean caught);
|
* @param error The error that the instruction threw, null if none
|
||||||
|
* @param caught Whether or not the error has been caught
|
||||||
/**
|
* @return Whether or not the frame should restart
|
||||||
* Called immediatly after a frame has been popped out of the frame stack.
|
*/
|
||||||
* This function might pause in order to await debugging commands.
|
boolean onInstruction(Context ctx, CodeFrame frame, Instruction instruction, Object returnVal, EngineException error, boolean caught);
|
||||||
* @param ctx The context of execution
|
|
||||||
* @param frame The code frame which was popped out
|
/**
|
||||||
*/
|
* Called immediatly before a frame has been pushed on the frame stack.
|
||||||
void onFramePop(Context ctx, CodeFrame frame);
|
* This function might pause in order to await debugging commands.
|
||||||
}
|
* @param ctx The context of execution
|
||||||
|
* @param frame The code frame which was pushed
|
||||||
|
*/
|
||||||
|
void onFramePush(Context ctx, CodeFrame frame);
|
||||||
|
/**
|
||||||
|
* Called immediatly after a frame has been popped out of the frame stack.
|
||||||
|
* This function might pause in order to await debugging commands.
|
||||||
|
* @param ctx The context of execution
|
||||||
|
* @param frame The code frame which was popped out
|
||||||
|
*/
|
||||||
|
void onFramePop(Context ctx, CodeFrame frame);
|
||||||
|
}
|
||||||
|
@ -1,35 +1,34 @@
|
|||||||
package me.topchetoeu.jscript.engine.debug;
|
package me.topchetoeu.jscript.engine.debug;
|
||||||
|
|
||||||
public interface DebugHandler {
|
public interface DebugHandler {
|
||||||
void enable(V8Message msg);
|
void enable(V8Message msg);
|
||||||
void disable(V8Message msg);
|
void disable(V8Message msg);
|
||||||
|
|
||||||
void setBreakpoint(V8Message msg);
|
void setBreakpointByUrl(V8Message msg);
|
||||||
void setBreakpointByUrl(V8Message msg);
|
void removeBreakpoint(V8Message msg);
|
||||||
void removeBreakpoint(V8Message msg);
|
void continueToLocation(V8Message msg);
|
||||||
void continueToLocation(V8Message msg);
|
|
||||||
|
void getScriptSource(V8Message msg);
|
||||||
void getScriptSource(V8Message msg);
|
void getPossibleBreakpoints(V8Message msg);
|
||||||
void getPossibleBreakpoints(V8Message msg);
|
|
||||||
|
void resume(V8Message msg);
|
||||||
void resume(V8Message msg);
|
void pause(V8Message msg);
|
||||||
void pause(V8Message msg);
|
|
||||||
|
void stepInto(V8Message msg);
|
||||||
void stepInto(V8Message msg);
|
void stepOut(V8Message msg);
|
||||||
void stepOut(V8Message msg);
|
void stepOver(V8Message msg);
|
||||||
void stepOver(V8Message msg);
|
|
||||||
|
void setPauseOnExceptions(V8Message msg);
|
||||||
void setPauseOnExceptions(V8Message msg);
|
|
||||||
|
void evaluateOnCallFrame(V8Message msg);
|
||||||
void evaluateOnCallFrame(V8Message msg);
|
|
||||||
|
void getProperties(V8Message msg);
|
||||||
void getProperties(V8Message msg);
|
void releaseObjectGroup(V8Message msg);
|
||||||
void releaseObjectGroup(V8Message msg);
|
void releaseObject(V8Message msg);
|
||||||
void releaseObject(V8Message msg);
|
/**
|
||||||
/**
|
* This method might not execute the actual code for well-known requests
|
||||||
* This method might not execute the actual code for well-known requests
|
*/
|
||||||
*/
|
void callFunctionOn(V8Message msg);
|
||||||
void callFunctionOn(V8Message msg);
|
|
||||||
|
void runtimeEnable(V8Message msg);
|
||||||
void runtimeEnable(V8Message msg);
|
}
|
||||||
}
|
|
||||||
|
@ -1,246 +1,245 @@
|
|||||||
package me.topchetoeu.jscript.engine.debug;
|
package me.topchetoeu.jscript.engine.debug;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.ServerSocket;
|
import java.net.ServerSocket;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Metadata;
|
import me.topchetoeu.jscript.Metadata;
|
||||||
import me.topchetoeu.jscript.Reading;
|
import me.topchetoeu.jscript.Reading;
|
||||||
import me.topchetoeu.jscript.engine.debug.WebSocketMessage.Type;
|
import me.topchetoeu.jscript.engine.debug.WebSocketMessage.Type;
|
||||||
import me.topchetoeu.jscript.events.Notifier;
|
import me.topchetoeu.jscript.events.Notifier;
|
||||||
import me.topchetoeu.jscript.exceptions.SyntaxException;
|
import me.topchetoeu.jscript.exceptions.SyntaxException;
|
||||||
import me.topchetoeu.jscript.exceptions.UncheckedException;
|
import me.topchetoeu.jscript.exceptions.UncheckedException;
|
||||||
import me.topchetoeu.jscript.exceptions.UncheckedIOException;
|
import me.topchetoeu.jscript.exceptions.UncheckedIOException;
|
||||||
import me.topchetoeu.jscript.json.JSON;
|
import me.topchetoeu.jscript.json.JSON;
|
||||||
import me.topchetoeu.jscript.json.JSONList;
|
import me.topchetoeu.jscript.json.JSONList;
|
||||||
import me.topchetoeu.jscript.json.JSONMap;
|
import me.topchetoeu.jscript.json.JSONMap;
|
||||||
|
|
||||||
public class DebugServer {
|
public class DebugServer {
|
||||||
public static String browserDisplayName = Metadata.name() + "/" + Metadata.version();
|
public static String browserDisplayName = Metadata.name() + "/" + Metadata.version();
|
||||||
|
|
||||||
public final HashMap<String, DebuggerProvider> targets = new HashMap<>();
|
public final HashMap<String, DebuggerProvider> targets = new HashMap<>();
|
||||||
|
|
||||||
private final byte[] favicon, index, protocol;
|
private final byte[] favicon, index, protocol;
|
||||||
private final Notifier connNotifier = new Notifier();
|
private final Notifier connNotifier = new Notifier();
|
||||||
|
|
||||||
private static void send(HttpRequest req, String val) throws IOException {
|
private static void send(HttpRequest req, String val) throws IOException {
|
||||||
req.writeResponse(200, "OK", "application/json", val.getBytes());
|
req.writeResponse(200, "OK", "application/json", val.getBytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
// SILENCE JAVA
|
// SILENCE JAVA
|
||||||
private MessageDigest getDigestInstance() {
|
private MessageDigest getDigestInstance() {
|
||||||
try {
|
try {
|
||||||
return MessageDigest.getInstance("sha1");
|
return MessageDigest.getInstance("sha1");
|
||||||
}
|
}
|
||||||
catch (Throwable e) { throw new UncheckedException(e); }
|
catch (Throwable e) { throw new UncheckedException(e); }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Thread runAsync(Runnable func, String name) {
|
private static Thread runAsync(Runnable func, String name) {
|
||||||
var res = new Thread(func);
|
var res = new Thread(func);
|
||||||
res.setName(name);
|
res.setName(name);
|
||||||
res.start();
|
res.start();
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handle(WebSocket ws, Debugger debugger) {
|
private void handle(WebSocket ws, Debugger debugger) {
|
||||||
WebSocketMessage raw;
|
WebSocketMessage raw;
|
||||||
|
|
||||||
debugger.connect();
|
debugger.connect();
|
||||||
|
|
||||||
while ((raw = ws.receive()) != null) {
|
while ((raw = ws.receive()) != null) {
|
||||||
if (raw.type != Type.Text) {
|
if (raw.type != Type.Text) {
|
||||||
ws.send(new V8Error("Expected a text message."));
|
ws.send(new V8Error("Expected a text message."));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
V8Message msg;
|
V8Message msg;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
msg = new V8Message(raw.textData());
|
msg = new V8Message(raw.textData());
|
||||||
}
|
}
|
||||||
catch (SyntaxException e) {
|
catch (SyntaxException e) {
|
||||||
ws.send(new V8Error(e.getMessage()));
|
ws.send(new V8Error(e.getMessage()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
switch (msg.name) {
|
switch (msg.name) {
|
||||||
case "Debugger.enable":
|
case "Debugger.enable":
|
||||||
connNotifier.next();
|
connNotifier.next();
|
||||||
debugger.enable(msg); continue;
|
debugger.enable(msg); continue;
|
||||||
case "Debugger.disable": debugger.disable(msg); continue;
|
case "Debugger.disable": debugger.disable(msg); continue;
|
||||||
|
|
||||||
case "Debugger.setBreakpoint": debugger.setBreakpoint(msg); continue;
|
case "Debugger.setBreakpointByUrl": debugger.setBreakpointByUrl(msg); continue;
|
||||||
case "Debugger.setBreakpointByUrl": debugger.setBreakpointByUrl(msg); continue;
|
case "Debugger.removeBreakpoint": debugger.removeBreakpoint(msg); continue;
|
||||||
case "Debugger.removeBreakpoint": debugger.removeBreakpoint(msg); continue;
|
case "Debugger.continueToLocation": debugger.continueToLocation(msg); continue;
|
||||||
case "Debugger.continueToLocation": debugger.continueToLocation(msg); continue;
|
|
||||||
|
case "Debugger.getScriptSource": debugger.getScriptSource(msg); continue;
|
||||||
case "Debugger.getScriptSource": debugger.getScriptSource(msg); continue;
|
case "Debugger.getPossibleBreakpoints": debugger.getPossibleBreakpoints(msg); continue;
|
||||||
case "Debugger.getPossibleBreakpoints": debugger.getPossibleBreakpoints(msg); continue;
|
|
||||||
|
case "Debugger.resume": debugger.resume(msg); continue;
|
||||||
case "Debugger.resume": debugger.resume(msg); continue;
|
case "Debugger.pause": debugger.pause(msg); continue;
|
||||||
case "Debugger.pause": debugger.pause(msg); continue;
|
|
||||||
|
case "Debugger.stepInto": debugger.stepInto(msg); continue;
|
||||||
case "Debugger.stepInto": debugger.stepInto(msg); continue;
|
case "Debugger.stepOut": debugger.stepOut(msg); continue;
|
||||||
case "Debugger.stepOut": debugger.stepOut(msg); continue;
|
case "Debugger.stepOver": debugger.stepOver(msg); continue;
|
||||||
case "Debugger.stepOver": debugger.stepOver(msg); continue;
|
|
||||||
|
case "Debugger.setPauseOnExceptions": debugger.setPauseOnExceptions(msg); continue;
|
||||||
case "Debugger.setPauseOnExceptions": debugger.setPauseOnExceptions(msg); continue;
|
case "Debugger.evaluateOnCallFrame": debugger.evaluateOnCallFrame(msg); continue;
|
||||||
case "Debugger.evaluateOnCallFrame": debugger.evaluateOnCallFrame(msg); continue;
|
|
||||||
|
case "Runtime.releaseObjectGroup": debugger.releaseObjectGroup(msg); continue;
|
||||||
case "Runtime.releaseObjectGroup": debugger.releaseObjectGroup(msg); continue;
|
case "Runtime.releaseObject": debugger.releaseObject(msg); continue;
|
||||||
case "Runtime.releaseObject": debugger.releaseObject(msg); continue;
|
case "Runtime.getProperties": debugger.getProperties(msg); continue;
|
||||||
case "Runtime.getProperties": debugger.getProperties(msg); continue;
|
case "Runtime.callFunctionOn": debugger.callFunctionOn(msg); continue;
|
||||||
case "Runtime.callFunctionOn": debugger.callFunctionOn(msg); continue;
|
// case "NodeWorker.enable": debugger.nodeWorkerEnable(msg); continue;
|
||||||
// case "NodeWorker.enable": debugger.nodeWorkerEnable(msg); continue;
|
case "Runtime.enable": debugger.runtimeEnable(msg); continue;
|
||||||
case "Runtime.enable": debugger.runtimeEnable(msg); continue;
|
}
|
||||||
}
|
|
||||||
|
if (
|
||||||
if (
|
msg.name.startsWith("DOM.") ||
|
||||||
msg.name.startsWith("DOM.") ||
|
msg.name.startsWith("DOMDebugger.") ||
|
||||||
msg.name.startsWith("DOMDebugger.") ||
|
msg.name.startsWith("Emulation.") ||
|
||||||
msg.name.startsWith("Emulation.") ||
|
msg.name.startsWith("Input.") ||
|
||||||
msg.name.startsWith("Input.") ||
|
msg.name.startsWith("Network.") ||
|
||||||
msg.name.startsWith("Network.") ||
|
msg.name.startsWith("Page.")
|
||||||
msg.name.startsWith("Page.")
|
) ws.send(new V8Error("This isn't a browser..."));
|
||||||
) ws.send(new V8Error("This isn't a browser..."));
|
else ws.send(new V8Error("This API is not supported yet."));
|
||||||
else ws.send(new V8Error("This API is not supported yet."));
|
}
|
||||||
}
|
catch (Throwable e) {
|
||||||
catch (Throwable e) {
|
e.printStackTrace();
|
||||||
e.printStackTrace();
|
throw new UncheckedException(e);
|
||||||
throw new UncheckedException(e);
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
debugger.disconnect();
|
||||||
debugger.disconnect();
|
}
|
||||||
}
|
private void onWsConnect(HttpRequest req, Socket socket, DebuggerProvider debuggerProvider) {
|
||||||
private void onWsConnect(HttpRequest req, Socket socket, DebuggerProvider debuggerProvider) {
|
var key = req.headers.get("sec-websocket-key");
|
||||||
var key = req.headers.get("sec-websocket-key");
|
|
||||||
|
if (key == null) {
|
||||||
if (key == null) {
|
req.writeResponse(
|
||||||
req.writeResponse(
|
426, "Upgrade Required", "text/txt",
|
||||||
426, "Upgrade Required", "text/txt",
|
"Expected a WS upgrade".getBytes()
|
||||||
"Expected a WS upgrade".getBytes()
|
);
|
||||||
);
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
var resKey = Base64.getEncoder().encodeToString(getDigestInstance().digest(
|
||||||
var resKey = Base64.getEncoder().encodeToString(getDigestInstance().digest(
|
(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").getBytes()
|
||||||
(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").getBytes()
|
));
|
||||||
));
|
|
||||||
|
req.writeCode(101, "Switching Protocols");
|
||||||
req.writeCode(101, "Switching Protocols");
|
req.writeHeader("Connection", "Upgrade");
|
||||||
req.writeHeader("Connection", "Upgrade");
|
req.writeHeader("Sec-WebSocket-Accept", resKey);
|
||||||
req.writeHeader("Sec-WebSocket-Accept", resKey);
|
req.writeLastHeader("Upgrade", "WebSocket");
|
||||||
req.writeLastHeader("Upgrade", "WebSocket");
|
|
||||||
|
var ws = new WebSocket(socket);
|
||||||
var ws = new WebSocket(socket);
|
var debugger = debuggerProvider.getDebugger(ws, req);
|
||||||
var debugger = debuggerProvider.getDebugger(ws, req);
|
|
||||||
|
if (debugger == null) {
|
||||||
if (debugger == null) {
|
ws.close();
|
||||||
ws.close();
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
runAsync(() -> {
|
||||||
runAsync(() -> {
|
try { handle(ws, debugger); }
|
||||||
try { handle(ws, debugger); }
|
catch (RuntimeException e) {
|
||||||
catch (RuntimeException e) {
|
ws.send(new V8Error(e.getMessage()));
|
||||||
ws.send(new V8Error(e.getMessage()));
|
}
|
||||||
}
|
finally { ws.close(); debugger.disconnect(); }
|
||||||
finally { ws.close(); debugger.disconnect(); }
|
}, "Debug Handler");
|
||||||
}, "Debug Handler");
|
}
|
||||||
}
|
|
||||||
|
public void awaitConnection() {
|
||||||
public void awaitConnection() {
|
connNotifier.await();
|
||||||
connNotifier.await();
|
}
|
||||||
}
|
|
||||||
|
public void run(InetSocketAddress address) {
|
||||||
public void run(InetSocketAddress address) {
|
try {
|
||||||
try {
|
ServerSocket server = new ServerSocket();
|
||||||
ServerSocket server = new ServerSocket();
|
server.bind(address);
|
||||||
server.bind(address);
|
|
||||||
|
try {
|
||||||
try {
|
while (true) {
|
||||||
while (true) {
|
var socket = server.accept();
|
||||||
var socket = server.accept();
|
var req = HttpRequest.read(socket);
|
||||||
var req = HttpRequest.read(socket);
|
|
||||||
|
if (req == null) continue;
|
||||||
if (req == null) continue;
|
|
||||||
|
switch (req.path) {
|
||||||
switch (req.path) {
|
case "/json/version":
|
||||||
case "/json/version":
|
send(req, "{\"Browser\":\"" + browserDisplayName + "\",\"Protocol-Version\":\"1.1\"}");
|
||||||
send(req, "{\"Browser\":\"" + browserDisplayName + "\",\"Protocol-Version\":\"1.1\"}");
|
break;
|
||||||
break;
|
case "/json/list":
|
||||||
case "/json/list":
|
case "/json": {
|
||||||
case "/json": {
|
var res = new JSONList();
|
||||||
var res = new JSONList();
|
|
||||||
|
for (var el : targets.entrySet()) {
|
||||||
for (var el : targets.entrySet()) {
|
res.add(new JSONMap()
|
||||||
res.add(new JSONMap()
|
.set("description", "JScript debugger")
|
||||||
.set("description", "JScript debugger")
|
.set("favicon", "/favicon.ico")
|
||||||
.set("favicon", "/favicon.ico")
|
.set("id", el.getKey())
|
||||||
.set("id", el.getKey())
|
.set("type", "node")
|
||||||
.set("type", "node")
|
.set("webSocketDebuggerUrl", "ws://" + address.getHostString() + ":" + address.getPort() + "/" + el.getKey())
|
||||||
.set("webSocketDebuggerUrl", "ws://" + address.getHostString() + ":" + address.getPort() + "/" + el.getKey())
|
);
|
||||||
);
|
}
|
||||||
}
|
send(req, JSON.stringify(res));
|
||||||
send(req, JSON.stringify(res));
|
break;
|
||||||
break;
|
}
|
||||||
}
|
case "/json/protocol":
|
||||||
case "/json/protocol":
|
req.writeResponse(200, "OK", "application/json", protocol);
|
||||||
req.writeResponse(200, "OK", "application/json", protocol);
|
break;
|
||||||
break;
|
case "/json/new":
|
||||||
case "/json/new":
|
case "/json/activate":
|
||||||
case "/json/activate":
|
case "/json/close":
|
||||||
case "/json/close":
|
case "/devtools/inspector.html":
|
||||||
case "/devtools/inspector.html":
|
req.writeResponse(
|
||||||
req.writeResponse(
|
501, "Not Implemented", "text/txt",
|
||||||
501, "Not Implemented", "text/txt",
|
"This feature isn't (and probably won't be) implemented.".getBytes()
|
||||||
"This feature isn't (and probably won't be) implemented.".getBytes()
|
);
|
||||||
);
|
break;
|
||||||
break;
|
case "/":
|
||||||
case "/":
|
case "/index.html":
|
||||||
case "/index.html":
|
req.writeResponse(200, "OK", "text/html", index);
|
||||||
req.writeResponse(200, "OK", "text/html", index);
|
break;
|
||||||
break;
|
case "/favicon.ico":
|
||||||
case "/favicon.ico":
|
req.writeResponse(200, "OK", "image/png", favicon);
|
||||||
req.writeResponse(200, "OK", "image/png", favicon);
|
break;
|
||||||
break;
|
default:
|
||||||
default:
|
if (req.path.length() > 1 && targets.containsKey(req.path.substring(1))) {
|
||||||
if (req.path.length() > 1 && targets.containsKey(req.path.substring(1))) {
|
onWsConnect(req, socket, targets.get(req.path.substring(1)));
|
||||||
onWsConnect(req, socket, targets.get(req.path.substring(1)));
|
}
|
||||||
}
|
break;
|
||||||
break;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
finally { server.close(); }
|
||||||
finally { server.close(); }
|
}
|
||||||
}
|
catch (IOException e) { throw new UncheckedIOException(e); }
|
||||||
catch (IOException e) { throw new UncheckedIOException(e); }
|
}
|
||||||
}
|
|
||||||
|
public Thread start(InetSocketAddress address, boolean daemon) {
|
||||||
public Thread start(InetSocketAddress address, boolean daemon) {
|
var res = new Thread(() -> run(address), "Debug Server");
|
||||||
var res = new Thread(() -> run(address), "Debug Server");
|
res.setDaemon(daemon);
|
||||||
res.setDaemon(daemon);
|
res.start();
|
||||||
res.start();
|
return res;
|
||||||
return res;
|
}
|
||||||
}
|
|
||||||
|
public DebugServer() {
|
||||||
public DebugServer() {
|
try {
|
||||||
try {
|
this.favicon = Reading.resourceToStream("debugger/favicon.png").readAllBytes();
|
||||||
this.favicon = Reading.resourceToStream("debugger/favicon.png").readAllBytes();
|
this.protocol = Reading.resourceToStream("debugger/protocol.json").readAllBytes();
|
||||||
this.protocol = Reading.resourceToStream("debugger/protocol.json").readAllBytes();
|
this.index = Reading.resourceToString("debugger/index.html")
|
||||||
this.index = Reading.resourceToString("debugger/index.html")
|
.replace("${NAME}", Metadata.name())
|
||||||
.replace("${NAME}", Metadata.name())
|
.replace("${VERSION}", Metadata.version())
|
||||||
.replace("${VERSION}", Metadata.version())
|
.replace("${AUTHOR}", Metadata.author())
|
||||||
.replace("${AUTHOR}", Metadata.author())
|
.getBytes();
|
||||||
.getBytes();
|
}
|
||||||
}
|
catch (IOException e) { throw new UncheckedIOException(e); }
|
||||||
catch (IOException e) { throw new UncheckedIOException(e); }
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -17,32 +17,72 @@ import me.topchetoeu.jscript.exceptions.EngineException;
|
|||||||
import me.topchetoeu.jscript.exceptions.InterruptException;
|
import me.topchetoeu.jscript.exceptions.InterruptException;
|
||||||
|
|
||||||
public class CodeFrame {
|
public class CodeFrame {
|
||||||
|
public static enum TryState {
|
||||||
|
TRY,
|
||||||
|
CATCH,
|
||||||
|
FINALLY,
|
||||||
|
}
|
||||||
|
|
||||||
public static class TryCtx {
|
public static class TryCtx {
|
||||||
public static final int STATE_TRY = 0;
|
public final int start, end, catchStart, finallyStart;
|
||||||
public static final int STATE_CATCH = 1;
|
public final int restoreStackPtr;
|
||||||
public static final int STATE_FINALLY_THREW = 2;
|
public final TryState state;
|
||||||
public static final int STATE_FINALLY_RETURNED = 3;
|
public final EngineException error;
|
||||||
public static final int STATE_FINALLY_JUMPED = 4;
|
public final PendingResult result;
|
||||||
|
|
||||||
public final boolean hasCatch, hasFinally;
|
public boolean hasCatch() { return catchStart >= 0; }
|
||||||
public final int tryStart, catchStart, finallyStart, end;
|
public boolean hasFinally() { return finallyStart >= 0; }
|
||||||
public int state;
|
|
||||||
public Object retVal;
|
|
||||||
public EngineException err;
|
|
||||||
public int jumpPtr;
|
|
||||||
|
|
||||||
public TryCtx(int tryStart, int tryN, int catchN, int finallyN) {
|
public boolean inBounds(int ptr) {
|
||||||
hasCatch = catchN >= 0;
|
return ptr >= start && ptr < end;
|
||||||
hasFinally = finallyN >= 0;
|
}
|
||||||
|
|
||||||
if (catchN < 0) catchN = 0;
|
public TryCtx _catch(EngineException e) {
|
||||||
if (finallyN < 0) finallyN = 0;
|
e.setCause(error);
|
||||||
|
return new TryCtx(TryState.CATCH, e, result, restoreStackPtr, start, end, -1, finallyStart);
|
||||||
|
}
|
||||||
|
public TryCtx _finally(PendingResult res) {
|
||||||
|
return new TryCtx(TryState.FINALLY, error, res, restoreStackPtr, start, end, -1, -1);
|
||||||
|
}
|
||||||
|
|
||||||
this.tryStart = tryStart;
|
public TryCtx(TryState state, EngineException err, PendingResult res, int stackPtr, int start, int end, int catchStart, int finallyStart) {
|
||||||
this.catchStart = tryStart + tryN;
|
this.catchStart = catchStart;
|
||||||
this.finallyStart = catchStart + catchN;
|
this.finallyStart = finallyStart;
|
||||||
this.end = finallyStart + finallyN;
|
this.restoreStackPtr = stackPtr;
|
||||||
this.jumpPtr = end;
|
this.result = res == null ? PendingResult.ofNone() : res;
|
||||||
|
this.state = state;
|
||||||
|
this.start = start;
|
||||||
|
this.end = end;
|
||||||
|
this.error = err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class PendingResult {
|
||||||
|
public final boolean isReturn, isJump, isThrow;
|
||||||
|
public final Object value;
|
||||||
|
public final EngineException error;
|
||||||
|
public final int ptr;
|
||||||
|
|
||||||
|
private PendingResult(boolean isReturn, boolean isJump, boolean isThrow, Object value, EngineException error, int ptr) {
|
||||||
|
this.isReturn = isReturn;
|
||||||
|
this.isJump = isJump;
|
||||||
|
this.isThrow = isThrow;
|
||||||
|
this.value = value;
|
||||||
|
this.error = error;
|
||||||
|
this.ptr = ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PendingResult ofNone() {
|
||||||
|
return new PendingResult(false, false, false, null, null, 0);
|
||||||
|
}
|
||||||
|
public static PendingResult ofReturn(Object value) {
|
||||||
|
return new PendingResult(true, false, false, value, null, 0);
|
||||||
|
}
|
||||||
|
public static PendingResult ofThrow(EngineException error) {
|
||||||
|
return new PendingResult(false, false, true, null, error, 0);
|
||||||
|
}
|
||||||
|
public static PendingResult ofJump(int codePtr) {
|
||||||
|
return new PendingResult(false, true, false, null, null, codePtr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,11 +91,10 @@ public class CodeFrame {
|
|||||||
public final Object[] args;
|
public final Object[] args;
|
||||||
public final Stack<TryCtx> tryStack = new Stack<>();
|
public final Stack<TryCtx> tryStack = new Stack<>();
|
||||||
public final CodeFunction function;
|
public final CodeFunction function;
|
||||||
|
|
||||||
public Object[] stack = new Object[32];
|
public Object[] stack = new Object[32];
|
||||||
public int stackPtr = 0;
|
public int stackPtr = 0;
|
||||||
public int codePtr = 0;
|
public int codePtr = 0;
|
||||||
public boolean jumpFlag = false;
|
public boolean jumpFlag = false, popTryFlag = false;
|
||||||
private Location prevLoc = null;
|
private Location prevLoc = null;
|
||||||
|
|
||||||
public ObjectValue getLocalScope(Context ctx, boolean props) {
|
public ObjectValue getLocalScope(Context ctx, boolean props) {
|
||||||
@ -105,9 +144,9 @@ public class CodeFrame {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addTry(int n, int catchN, int finallyN) {
|
public void addTry(int start, int end, int catchStart, int finallyStart) {
|
||||||
var res = new TryCtx(codePtr + 1, n, catchN, finallyN);
|
var err = tryStack.empty() ? null : tryStack.peek().error;
|
||||||
if (!tryStack.empty()) res.err = tryStack.peek().err;
|
var res = new TryCtx(TryState.TRY, err, null, stackPtr, start, end, catchStart, finallyStart);
|
||||||
|
|
||||||
tryStack.add(res);
|
tryStack.add(res);
|
||||||
}
|
}
|
||||||
@ -145,10 +184,6 @@ public class CodeFrame {
|
|||||||
stack[stackPtr++] = Values.normalize(ctx, val);
|
stack[stackPtr++] = Values.normalize(ctx, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setCause(Context ctx, EngineException err, EngineException cause) {
|
|
||||||
err.setCause(cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object next(Context ctx, Object value, Object returnValue, EngineException error) {
|
public Object next(Context ctx, Object value, Object returnValue, EngineException error) {
|
||||||
if (value != Runners.NO_RETURN) push(ctx, value);
|
if (value != Runners.NO_RETURN) push(ctx, value);
|
||||||
|
|
||||||
@ -166,7 +201,7 @@ public class CodeFrame {
|
|||||||
if (instr.location != null) prevLoc = instr.location;
|
if (instr.location != null) prevLoc = instr.location;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.jumpFlag = false;
|
this.jumpFlag = this.popTryFlag = false;
|
||||||
returnValue = Runners.exec(ctx, instr, this);
|
returnValue = Runners.exec(ctx, instr, this);
|
||||||
}
|
}
|
||||||
catch (EngineException e) {
|
catch (EngineException e) {
|
||||||
@ -177,111 +212,75 @@ public class CodeFrame {
|
|||||||
catch (EngineException e) { error = e; }
|
catch (EngineException e) { error = e; }
|
||||||
}
|
}
|
||||||
|
|
||||||
while (!tryStack.empty()) {
|
if (!tryStack.empty()) {
|
||||||
var tryCtx = tryStack.peek();
|
var tryCtx = tryStack.peek();
|
||||||
var newState = -1;
|
var pendingResult = PendingResult.ofNone();
|
||||||
|
boolean shouldCatch = false, shouldFinally = false, shouldExit = this.popTryFlag;
|
||||||
|
|
||||||
switch (tryCtx.state) {
|
if (tryCtx.state != TryState.FINALLY) {
|
||||||
case TryCtx.STATE_TRY:
|
if (error != null && tryCtx.hasCatch()) shouldCatch = true;
|
||||||
if (error != null) {
|
else if (error != null && tryCtx.hasFinally()) {
|
||||||
if (tryCtx.hasCatch) {
|
pendingResult = PendingResult.ofThrow(error);
|
||||||
tryCtx.err = error;
|
shouldFinally = true;
|
||||||
newState = TryCtx.STATE_CATCH;
|
}
|
||||||
}
|
else if (returnValue != Runners.NO_RETURN && tryCtx.hasFinally()) {
|
||||||
else if (tryCtx.hasFinally) {
|
pendingResult = PendingResult.ofReturn(returnValue);
|
||||||
tryCtx.err = error;
|
shouldFinally = true;
|
||||||
newState = TryCtx.STATE_FINALLY_THREW;
|
}
|
||||||
}
|
else if (jumpFlag && !tryCtx.inBounds(codePtr) && tryCtx.hasFinally()) {
|
||||||
break;
|
pendingResult = PendingResult.ofJump(codePtr);
|
||||||
}
|
shouldFinally = true;
|
||||||
else if (returnValue != Runners.NO_RETURN) {
|
}
|
||||||
if (tryCtx.hasFinally) {
|
|
||||||
tryCtx.retVal = returnValue;
|
|
||||||
newState = TryCtx.STATE_FINALLY_RETURNED;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (codePtr >= tryCtx.tryStart && codePtr < tryCtx.catchStart) return Runners.NO_RETURN;
|
|
||||||
|
|
||||||
if (tryCtx.hasFinally) {
|
|
||||||
if (jumpFlag) tryCtx.jumpPtr = codePtr;
|
|
||||||
else tryCtx.jumpPtr = tryCtx.end;
|
|
||||||
newState = TryCtx.STATE_FINALLY_JUMPED;
|
|
||||||
}
|
|
||||||
else codePtr = tryCtx.end;
|
|
||||||
break;
|
|
||||||
case TryCtx.STATE_CATCH:
|
|
||||||
if (error != null) {
|
|
||||||
setCause(ctx, error, tryCtx.err);
|
|
||||||
if (tryCtx.hasFinally) {
|
|
||||||
tryCtx.err = error;
|
|
||||||
newState = TryCtx.STATE_FINALLY_THREW;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (returnValue != Runners.NO_RETURN) {
|
|
||||||
if (tryCtx.hasFinally) {
|
|
||||||
tryCtx.retVal = returnValue;
|
|
||||||
newState = TryCtx.STATE_FINALLY_RETURNED;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (codePtr >= tryCtx.catchStart && codePtr < tryCtx.finallyStart) return Runners.NO_RETURN;
|
|
||||||
|
|
||||||
if (tryCtx.hasFinally) {
|
|
||||||
if (jumpFlag) tryCtx.jumpPtr = codePtr;
|
|
||||||
else tryCtx.jumpPtr = tryCtx.end;
|
|
||||||
newState = TryCtx.STATE_FINALLY_JUMPED;
|
|
||||||
}
|
|
||||||
else codePtr = tryCtx.end;
|
|
||||||
break;
|
|
||||||
case TryCtx.STATE_FINALLY_THREW:
|
|
||||||
if (error != null) setCause(ctx, error, tryCtx.err);
|
|
||||||
else if (codePtr < tryCtx.finallyStart || codePtr >= tryCtx.end) error = tryCtx.err;
|
|
||||||
else if (returnValue == Runners.NO_RETURN) return Runners.NO_RETURN;
|
|
||||||
break;
|
|
||||||
case TryCtx.STATE_FINALLY_RETURNED:
|
|
||||||
if (error != null) setCause(ctx, error, tryCtx.err);
|
|
||||||
if (returnValue == Runners.NO_RETURN) {
|
|
||||||
if (codePtr < tryCtx.finallyStart || codePtr >= tryCtx.end) returnValue = tryCtx.retVal;
|
|
||||||
else return Runners.NO_RETURN;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TryCtx.STATE_FINALLY_JUMPED:
|
|
||||||
if (error != null) setCause(ctx, error, tryCtx.err);
|
|
||||||
else if (codePtr < tryCtx.finallyStart || codePtr >= tryCtx.end) {
|
|
||||||
if (!jumpFlag) codePtr = tryCtx.jumpPtr;
|
|
||||||
else codePtr = tryCtx.end;
|
|
||||||
}
|
|
||||||
else if (returnValue == Runners.NO_RETURN) return Runners.NO_RETURN;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tryCtx.state == TryCtx.STATE_CATCH) scope.catchVars.remove(scope.catchVars.size() - 1);
|
if (tryCtx.hasCatch() && shouldCatch) {
|
||||||
|
if (tryCtx.state != TryState.CATCH) scope.catchVars.add(new ValueVariable(false, error.value));
|
||||||
|
|
||||||
if (newState == -1) {
|
codePtr = tryCtx.catchStart;
|
||||||
var err = tryCtx.err;
|
stackPtr = tryCtx.restoreStackPtr;
|
||||||
tryStack.pop();
|
tryStack.pop();
|
||||||
if (!tryStack.isEmpty()) tryStack.peek().err = err;
|
tryStack.push(tryCtx._catch(error));
|
||||||
continue;
|
error = null;
|
||||||
|
returnValue = Runners.NO_RETURN;
|
||||||
}
|
}
|
||||||
|
else if (tryCtx.hasFinally() && shouldFinally) {
|
||||||
|
if (tryCtx.state == TryState.CATCH) scope.catchVars.remove(scope.catchVars.size() - 1);
|
||||||
|
|
||||||
tryCtx.state = newState;
|
codePtr = tryCtx.finallyStart;
|
||||||
switch (newState) {
|
stackPtr = tryCtx.restoreStackPtr;
|
||||||
case TryCtx.STATE_CATCH:
|
tryStack.pop();
|
||||||
scope.catchVars.add(new ValueVariable(false, tryCtx.err.value));
|
tryStack.push(tryCtx._finally(pendingResult));
|
||||||
codePtr = tryCtx.catchStart;
|
error = null;
|
||||||
ctx.engine.onInstruction(ctx, this, function.body[codePtr], null, error, true);
|
returnValue = Runners.NO_RETURN;
|
||||||
break;
|
}
|
||||||
default:
|
else if (shouldExit) {
|
||||||
|
if (tryCtx.state == TryState.CATCH) scope.catchVars.remove(scope.catchVars.size() - 1);
|
||||||
|
|
||||||
|
if (tryCtx.state != TryState.FINALLY && tryCtx.hasFinally()) {
|
||||||
codePtr = tryCtx.finallyStart;
|
codePtr = tryCtx.finallyStart;
|
||||||
|
stackPtr = tryCtx.restoreStackPtr;
|
||||||
|
tryStack.pop();
|
||||||
|
tryStack.push(tryCtx._finally(null));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
codePtr = tryCtx.end;
|
||||||
|
if (tryCtx.result.isJump) codePtr = tryCtx.result.ptr;
|
||||||
|
if (tryCtx.result.isReturn) returnValue = tryCtx.result.value;
|
||||||
|
if (tryCtx.result.isThrow) error = tryCtx.result.error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Runners.NO_RETURN;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error != null) {
|
if (error != null) {
|
||||||
ctx.engine.onInstruction(ctx, this, instr, null, error, false);
|
var caught = false;
|
||||||
|
|
||||||
|
for (var frame : ctx.frames()) {
|
||||||
|
for (var tryCtx : frame.tryStack) {
|
||||||
|
if (tryCtx.state == TryState.TRY) caught = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.engine.onInstruction(ctx, this, instr, null, error, caught);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
if (returnValue != Runners.NO_RETURN) {
|
if (returnValue != Runners.NO_RETURN) {
|
||||||
|
@ -97,27 +97,26 @@ public class Runners {
|
|||||||
obj.defineProperty(ctx, "value", el);
|
obj.defineProperty(ctx, "value", el);
|
||||||
frame.push(ctx, obj);
|
frame.push(ctx, obj);
|
||||||
}
|
}
|
||||||
// var arr = new ObjectValue();
|
|
||||||
|
|
||||||
// var members = Values.getMembers(ctx, val, false, false);
|
|
||||||
// Collections.reverse(members);
|
|
||||||
// for (var el : members) {
|
|
||||||
// if (el instanceof Symbol) continue;
|
|
||||||
// arr.defineProperty(ctx, i++, el);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// arr.defineProperty(ctx, "length", i);
|
|
||||||
|
|
||||||
// frame.push(ctx, arr);
|
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Object execTry(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execTryStart(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
frame.addTry(instr.get(0), instr.get(1), instr.get(2));
|
int start = frame.codePtr + 1;
|
||||||
|
int catchStart = (int)instr.get(0);
|
||||||
|
int finallyStart = (int)instr.get(1);
|
||||||
|
if (finallyStart >= 0) finallyStart += start;
|
||||||
|
if (catchStart >= 0) catchStart += start;
|
||||||
|
int end = (int)instr.get(2) + start;
|
||||||
|
frame.addTry(start, end, catchStart, finallyStart);
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
|
public static Object execTryEnd(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
|
frame.popTryFlag = true;
|
||||||
|
return NO_RETURN;
|
||||||
|
}
|
||||||
|
|
||||||
public static Object execDup(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execDup(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
int offset = instr.get(0), count = instr.get(1);
|
int offset = instr.get(0), count = instr.get(1);
|
||||||
@ -326,7 +325,8 @@ public class Runners {
|
|||||||
case THROW_SYNTAX: return execThrowSyntax(ctx, instr, frame);
|
case THROW_SYNTAX: return execThrowSyntax(ctx, instr, frame);
|
||||||
case CALL: return execCall(ctx, instr, frame);
|
case CALL: return execCall(ctx, instr, frame);
|
||||||
case CALL_NEW: return execCallNew(ctx, instr, frame);
|
case CALL_NEW: return execCallNew(ctx, instr, frame);
|
||||||
case TRY: return execTry(ctx, instr, frame);
|
case TRY_START: return execTryStart(ctx, instr, frame);
|
||||||
|
case TRY_END: return execTryEnd(ctx, instr, frame);
|
||||||
|
|
||||||
case DUP: return execDup(ctx, instr, frame);
|
case DUP: return execDup(ctx, instr, frame);
|
||||||
case MOVE: return execMove(ctx, instr, frame);
|
case MOVE: return execMove(ctx, instr, frame);
|
||||||
|
@ -12,9 +12,6 @@ import me.topchetoeu.jscript.exceptions.EngineException;
|
|||||||
public class GlobalScope implements ScopeRecord {
|
public class GlobalScope implements ScopeRecord {
|
||||||
public final ObjectValue obj;
|
public final ObjectValue obj;
|
||||||
|
|
||||||
@Override
|
|
||||||
public GlobalScope parent() { return null; }
|
|
||||||
|
|
||||||
public boolean has(Context ctx, String name) {
|
public boolean has(Context ctx, String name) {
|
||||||
return obj.hasMember(ctx, name, false);
|
return obj.hasMember(ctx, name, false);
|
||||||
}
|
}
|
||||||
@ -28,7 +25,7 @@ public class GlobalScope implements ScopeRecord {
|
|||||||
return new GlobalScope(obj);
|
return new GlobalScope(obj);
|
||||||
}
|
}
|
||||||
public LocalScopeRecord child() {
|
public LocalScopeRecord child() {
|
||||||
return new LocalScopeRecord(this);
|
return new LocalScopeRecord();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object define(String name) {
|
public Object define(String name) {
|
||||||
|
@ -2,11 +2,8 @@ package me.topchetoeu.jscript.engine.scope;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
|
||||||
|
|
||||||
public class LocalScopeRecord implements ScopeRecord {
|
public class LocalScopeRecord implements ScopeRecord {
|
||||||
public final LocalScopeRecord parent;
|
public final LocalScopeRecord parent;
|
||||||
public final GlobalScope global;
|
|
||||||
|
|
||||||
private final ArrayList<String> captures = new ArrayList<>();
|
private final ArrayList<String> captures = new ArrayList<>();
|
||||||
private final ArrayList<String> locals = new ArrayList<>();
|
private final ArrayList<String> locals = new ArrayList<>();
|
||||||
@ -18,11 +15,8 @@ public class LocalScopeRecord implements ScopeRecord {
|
|||||||
return locals.toArray(String[]::new);
|
return locals.toArray(String[]::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public LocalScopeRecord parent() { return parent; }
|
|
||||||
|
|
||||||
public LocalScopeRecord child() {
|
public LocalScopeRecord child() {
|
||||||
return new LocalScopeRecord(this, global);
|
return new LocalScopeRecord(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int localsCount() {
|
public int localsCount() {
|
||||||
@ -62,12 +56,6 @@ public class LocalScopeRecord implements ScopeRecord {
|
|||||||
|
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
public boolean has(Context ctx, String name) {
|
|
||||||
return
|
|
||||||
global.has(ctx, name) ||
|
|
||||||
locals.contains(name) ||
|
|
||||||
parent != null && parent.has(ctx, name);
|
|
||||||
}
|
|
||||||
public Object define(String name, boolean force) {
|
public Object define(String name, boolean force) {
|
||||||
if (!force && locals.contains(name)) return locals.indexOf(name);
|
if (!force && locals.contains(name)) return locals.indexOf(name);
|
||||||
locals.add(name);
|
locals.add(name);
|
||||||
@ -80,12 +68,10 @@ public class LocalScopeRecord implements ScopeRecord {
|
|||||||
locals.remove(locals.size() - 1);
|
locals.remove(locals.size() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public LocalScopeRecord(GlobalScope global) {
|
public LocalScopeRecord() {
|
||||||
this.parent = null;
|
this.parent = null;
|
||||||
this.global = global;
|
|
||||||
}
|
}
|
||||||
public LocalScopeRecord(LocalScopeRecord parent, GlobalScope global) {
|
public LocalScopeRecord(LocalScopeRecord parent) {
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.global = global;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,5 @@ package me.topchetoeu.jscript.engine.scope;
|
|||||||
public interface ScopeRecord {
|
public interface ScopeRecord {
|
||||||
public Object getKey(String name);
|
public Object getKey(String name);
|
||||||
public Object define(String name);
|
public Object define(String name);
|
||||||
public ScopeRecord parent();
|
|
||||||
public LocalScopeRecord child();
|
public LocalScopeRecord child();
|
||||||
}
|
}
|
||||||
|
@ -1,51 +1,54 @@
|
|||||||
package me.topchetoeu.jscript.engine.values;
|
package me.topchetoeu.jscript.engine.values;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
import me.topchetoeu.jscript.engine.scope.ValueVariable;
|
import me.topchetoeu.jscript.engine.scope.ValueVariable;
|
||||||
|
|
||||||
public class ScopeValue extends ObjectValue {
|
public class ScopeValue extends ObjectValue {
|
||||||
public final ValueVariable[] variables;
|
public final ValueVariable[] variables;
|
||||||
public final HashMap<String, Integer> names = new HashMap<>();
|
public final HashMap<String, Integer> names = new HashMap<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Object getField(Context ctx, Object key) {
|
protected Object getField(Context ctx, Object key) {
|
||||||
if (names.containsKey(key)) return variables[names.get(key)].get(ctx);
|
if (names.containsKey(key)) return variables[names.get(key)].get(ctx);
|
||||||
return super.getField(ctx, key);
|
return super.getField(ctx, key);
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
protected boolean setField(Context ctx, Object key, Object val) {
|
protected boolean setField(Context ctx, Object key, Object val) {
|
||||||
if (names.containsKey(key)) {
|
if (names.containsKey(key)) {
|
||||||
variables[names.get(key)].set(ctx, val);
|
variables[names.get(key)].set(ctx, val);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.setField(ctx, key, val);
|
var proto = getPrototype(ctx);
|
||||||
}
|
if (proto != null && proto.hasField(ctx, key) && proto.setField(ctx, key, val)) return true;
|
||||||
@Override
|
|
||||||
protected void deleteField(Context ctx, Object key) {
|
return super.setField(ctx, key, val);
|
||||||
if (names.containsKey(key)) return;
|
}
|
||||||
super.deleteField(ctx, key);
|
@Override
|
||||||
}
|
protected void deleteField(Context ctx, Object key) {
|
||||||
@Override
|
if (names.containsKey(key)) return;
|
||||||
protected boolean hasField(Context ctx, Object key) {
|
super.deleteField(ctx, key);
|
||||||
if (names.containsKey(key)) return true;
|
}
|
||||||
return super.hasField(ctx, key);
|
@Override
|
||||||
}
|
protected boolean hasField(Context ctx, Object key) {
|
||||||
@Override
|
if (names.containsKey(key)) return true;
|
||||||
public List<Object> keys(boolean includeNonEnumerable) {
|
return super.hasField(ctx, key);
|
||||||
var res = super.keys(includeNonEnumerable);
|
}
|
||||||
res.addAll(names.keySet());
|
@Override
|
||||||
return res;
|
public List<Object> keys(boolean includeNonEnumerable) {
|
||||||
}
|
var res = super.keys(includeNonEnumerable);
|
||||||
|
res.addAll(names.keySet());
|
||||||
public ScopeValue(ValueVariable[] variables, String[] names) {
|
return res;
|
||||||
this.variables = variables;
|
}
|
||||||
for (var i = 0; i < names.length && i < variables.length; i++) {
|
|
||||||
this.names.put(names[i], i);
|
public ScopeValue(ValueVariable[] variables, String[] names) {
|
||||||
this.nonConfigurableSet.add(names[i]);
|
this.variables = variables;
|
||||||
}
|
for (var i = 0; i < names.length && i < variables.length; i++) {
|
||||||
}
|
this.names.put(names[i], i);
|
||||||
}
|
this.nonConfigurableSet.add(names[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,39 +1,41 @@
|
|||||||
package me.topchetoeu.jscript.filesystem;
|
package me.topchetoeu.jscript.filesystem;
|
||||||
|
|
||||||
public interface File {
|
import me.topchetoeu.jscript.Buffer;
|
||||||
int read(byte[] buff);
|
|
||||||
void write(byte[] buff);
|
public interface File {
|
||||||
long getPtr();
|
int read(byte[] buff);
|
||||||
void setPtr(long offset, int pos);
|
void write(byte[] buff);
|
||||||
void close();
|
long getPtr();
|
||||||
Mode mode();
|
void setPtr(long offset, int pos);
|
||||||
|
void close();
|
||||||
default String readToString() {
|
Mode mode();
|
||||||
setPtr(0, 2);
|
|
||||||
long len = getPtr();
|
default String readToString() {
|
||||||
if (len < 0) return null;
|
setPtr(0, 2);
|
||||||
|
long len = getPtr();
|
||||||
setPtr(0, 0);
|
if (len < 0) return null;
|
||||||
|
|
||||||
byte[] res = new byte[(int)len];
|
setPtr(0, 0);
|
||||||
read(res);
|
|
||||||
|
byte[] res = new byte[(int)len];
|
||||||
return new String(res);
|
read(res);
|
||||||
}
|
|
||||||
default String readLine() {
|
return new String(res);
|
||||||
var res = new Buffer();
|
}
|
||||||
var buff = new byte[1];
|
default String readLine() {
|
||||||
|
var res = new Buffer();
|
||||||
while (true) {
|
var buff = new byte[1];
|
||||||
if (read(buff) == 0) {
|
|
||||||
if (res.length() == 0) return null;
|
while (true) {
|
||||||
else break;
|
if (read(buff) == 0) {
|
||||||
}
|
if (res.length() == 0) return null;
|
||||||
|
else break;
|
||||||
if (buff[0] == '\n') break;
|
}
|
||||||
|
|
||||||
res.write(res.length(), buff);
|
if (buff[0] == '\n') break;
|
||||||
}
|
|
||||||
return new String(res.data());
|
res.write(res.length(), buff);
|
||||||
}
|
}
|
||||||
|
return new String(res.data());
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,66 +1,67 @@
|
|||||||
package me.topchetoeu.jscript.filesystem;
|
package me.topchetoeu.jscript.filesystem;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.filesystem.FilesystemException.FSCode;
|
import me.topchetoeu.jscript.Buffer;
|
||||||
|
import me.topchetoeu.jscript.filesystem.FilesystemException.FSCode;
|
||||||
public class MemoryFile implements File {
|
|
||||||
private int ptr;
|
public class MemoryFile implements File {
|
||||||
private Mode mode;
|
private int ptr;
|
||||||
private Buffer data;
|
private Mode mode;
|
||||||
private String filename;
|
private Buffer data;
|
||||||
|
private String filename;
|
||||||
public Buffer data() { return data; }
|
|
||||||
|
public Buffer data() { return data; }
|
||||||
@Override
|
|
||||||
public int read(byte[] buff) {
|
@Override
|
||||||
if (data == null || !mode.readable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R);
|
public int read(byte[] buff) {
|
||||||
var res = data.read(ptr, buff);
|
if (data == null || !mode.readable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R);
|
||||||
ptr += res;
|
var res = data.read(ptr, buff);
|
||||||
return res;
|
ptr += res;
|
||||||
}
|
return res;
|
||||||
@Override
|
}
|
||||||
public void write(byte[] buff) {
|
@Override
|
||||||
if (data == null || !mode.writable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_RW);
|
public void write(byte[] buff) {
|
||||||
|
if (data == null || !mode.writable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_RW);
|
||||||
data.write(ptr, buff);
|
|
||||||
ptr += buff.length;
|
data.write(ptr, buff);
|
||||||
}
|
ptr += buff.length;
|
||||||
|
}
|
||||||
@Override
|
|
||||||
public long getPtr() {
|
@Override
|
||||||
if (data == null || !mode.readable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R);
|
public long getPtr() {
|
||||||
return ptr;
|
if (data == null || !mode.readable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R);
|
||||||
}
|
return ptr;
|
||||||
@Override
|
}
|
||||||
public void setPtr(long offset, int pos) {
|
@Override
|
||||||
if (data == null || !mode.readable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R);
|
public void setPtr(long offset, int pos) {
|
||||||
|
if (data == null || !mode.readable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R);
|
||||||
if (pos == 0) ptr = (int)offset;
|
|
||||||
else if (pos == 1) ptr += (int)offset;
|
if (pos == 0) ptr = (int)offset;
|
||||||
else if (pos == 2) ptr = data.length() - (int)offset;
|
else if (pos == 1) ptr += (int)offset;
|
||||||
}
|
else if (pos == 2) ptr = data.length() - (int)offset;
|
||||||
|
}
|
||||||
@Override
|
|
||||||
public void close() {
|
@Override
|
||||||
mode = Mode.NONE;
|
public void close() {
|
||||||
ptr = 0;
|
mode = Mode.NONE;
|
||||||
}
|
ptr = 0;
|
||||||
@Override
|
}
|
||||||
public Mode mode() {
|
@Override
|
||||||
if (data == null) return Mode.NONE;
|
public Mode mode() {
|
||||||
return mode;
|
if (data == null) return Mode.NONE;
|
||||||
}
|
return mode;
|
||||||
|
}
|
||||||
public MemoryFile(String filename, Buffer buff, Mode mode) {
|
|
||||||
this.filename = filename;
|
public MemoryFile(String filename, Buffer buff, Mode mode) {
|
||||||
this.data = buff;
|
this.filename = filename;
|
||||||
this.mode = mode;
|
this.data = buff;
|
||||||
}
|
this.mode = mode;
|
||||||
|
}
|
||||||
public static MemoryFile fromFileList(String filename, java.io.File[] list) {
|
|
||||||
var res = new StringBuilder();
|
public static MemoryFile fromFileList(String filename, java.io.File[] list) {
|
||||||
|
var res = new StringBuilder();
|
||||||
for (var el : list) res.append(el.getName()).append('\n');
|
|
||||||
|
for (var el : list) res.append(el.getName()).append('\n');
|
||||||
return new MemoryFile(filename, new Buffer(res.toString().getBytes()), Mode.READ);
|
|
||||||
}
|
return new MemoryFile(filename, new Buffer(res.toString().getBytes()), Mode.READ);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@ -1,89 +1,90 @@
|
|||||||
package me.topchetoeu.jscript.filesystem;
|
package me.topchetoeu.jscript.filesystem;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.filesystem.FilesystemException.FSCode;
|
import me.topchetoeu.jscript.Buffer;
|
||||||
|
import me.topchetoeu.jscript.filesystem.FilesystemException.FSCode;
|
||||||
public class MemoryFilesystem implements Filesystem {
|
|
||||||
public final Mode mode;
|
public class MemoryFilesystem implements Filesystem {
|
||||||
private HashMap<Path, Buffer> files = new HashMap<>();
|
public final Mode mode;
|
||||||
private HashSet<Path> folders = new HashSet<>();
|
private HashMap<Path, Buffer> files = new HashMap<>();
|
||||||
|
private HashSet<Path> folders = new HashSet<>();
|
||||||
private Path getPath(String name) {
|
|
||||||
return Path.of("/" + name.replace("\\", "/")).normalize();
|
private Path getPath(String name) {
|
||||||
}
|
return Path.of("/" + name.replace("\\", "/")).normalize();
|
||||||
|
}
|
||||||
@Override
|
|
||||||
public void create(String path, EntryType type) {
|
@Override
|
||||||
var _path = getPath(path);
|
public void create(String path, EntryType type) {
|
||||||
|
var _path = getPath(path);
|
||||||
switch (type) {
|
|
||||||
case FILE:
|
switch (type) {
|
||||||
if (!folders.contains(_path.getParent())) throw new FilesystemException(path, FSCode.DOESNT_EXIST);
|
case FILE:
|
||||||
if (folders.contains(_path) || files.containsKey(_path)) throw new FilesystemException(path, FSCode.ALREADY_EXISTS);
|
if (!folders.contains(_path.getParent())) throw new FilesystemException(path, FSCode.DOESNT_EXIST);
|
||||||
if (folders.contains(_path)) throw new FilesystemException(path, FSCode.ALREADY_EXISTS);
|
if (folders.contains(_path) || files.containsKey(_path)) throw new FilesystemException(path, FSCode.ALREADY_EXISTS);
|
||||||
files.put(_path, new Buffer());
|
if (folders.contains(_path)) throw new FilesystemException(path, FSCode.ALREADY_EXISTS);
|
||||||
break;
|
files.put(_path, new Buffer());
|
||||||
case FOLDER:
|
break;
|
||||||
if (!folders.contains(_path.getParent())) throw new FilesystemException(path, FSCode.DOESNT_EXIST);
|
case FOLDER:
|
||||||
if (folders.contains(_path) || files.containsKey(_path)) throw new FilesystemException(path, FSCode.ALREADY_EXISTS);
|
if (!folders.contains(_path.getParent())) throw new FilesystemException(path, FSCode.DOESNT_EXIST);
|
||||||
folders.add(_path);
|
if (folders.contains(_path) || files.containsKey(_path)) throw new FilesystemException(path, FSCode.ALREADY_EXISTS);
|
||||||
break;
|
folders.add(_path);
|
||||||
default:
|
break;
|
||||||
case NONE:
|
default:
|
||||||
if (!folders.remove(_path) && files.remove(_path) == null) throw new FilesystemException(path, FSCode.DOESNT_EXIST);
|
case NONE:
|
||||||
}
|
if (!folders.remove(_path) && files.remove(_path) == null) throw new FilesystemException(path, FSCode.DOESNT_EXIST);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
@Override
|
|
||||||
public File open(String path, Mode perms) {
|
@Override
|
||||||
var _path = getPath(path);
|
public File open(String path, Mode perms) {
|
||||||
var pcount = _path.getNameCount();
|
var _path = getPath(path);
|
||||||
|
var pcount = _path.getNameCount();
|
||||||
if (files.containsKey(_path)) return new MemoryFile(path, files.get(_path), perms);
|
|
||||||
else if (folders.contains(_path)) {
|
if (files.containsKey(_path)) return new MemoryFile(path, files.get(_path), perms);
|
||||||
var res = new StringBuilder();
|
else if (folders.contains(_path)) {
|
||||||
for (var folder : folders) {
|
var res = new StringBuilder();
|
||||||
if (pcount + 1 != folder.getNameCount()) continue;
|
for (var folder : folders) {
|
||||||
if (!folder.startsWith(_path)) continue;
|
if (pcount + 1 != folder.getNameCount()) continue;
|
||||||
res.append(folder.toFile().getName()).append('\n');
|
if (!folder.startsWith(_path)) continue;
|
||||||
}
|
res.append(folder.toFile().getName()).append('\n');
|
||||||
for (var file : files.keySet()) {
|
}
|
||||||
if (pcount + 1 != file.getNameCount()) continue;
|
for (var file : files.keySet()) {
|
||||||
if (!file.startsWith(_path)) continue;
|
if (pcount + 1 != file.getNameCount()) continue;
|
||||||
res.append(file.toFile().getName()).append('\n');
|
if (!file.startsWith(_path)) continue;
|
||||||
}
|
res.append(file.toFile().getName()).append('\n');
|
||||||
return new MemoryFile(path, new Buffer(res.toString().getBytes()), perms.intersect(Mode.READ));
|
}
|
||||||
}
|
return new MemoryFile(path, new Buffer(res.toString().getBytes()), perms.intersect(Mode.READ));
|
||||||
else throw new FilesystemException(path, FSCode.DOESNT_EXIST);
|
}
|
||||||
}
|
else throw new FilesystemException(path, FSCode.DOESNT_EXIST);
|
||||||
|
}
|
||||||
@Override
|
|
||||||
public FileStat stat(String path) {
|
@Override
|
||||||
var _path = getPath(path);
|
public FileStat stat(String path) {
|
||||||
|
var _path = getPath(path);
|
||||||
if (files.containsKey(_path)) return new FileStat(mode, EntryType.FILE);
|
|
||||||
else if (folders.contains(_path)) return new FileStat(mode, EntryType.FOLDER);
|
if (files.containsKey(_path)) return new FileStat(mode, EntryType.FILE);
|
||||||
else throw new FilesystemException(path, FSCode.DOESNT_EXIST);
|
else if (folders.contains(_path)) return new FileStat(mode, EntryType.FOLDER);
|
||||||
}
|
else throw new FilesystemException(path, FSCode.DOESNT_EXIST);
|
||||||
|
}
|
||||||
public MemoryFilesystem put(String path, byte[] data) {
|
|
||||||
var _path = getPath(path);
|
public MemoryFilesystem put(String path, byte[] data) {
|
||||||
var _curr = "/";
|
var _path = getPath(path);
|
||||||
|
var _curr = "/";
|
||||||
for (var seg : _path) {
|
|
||||||
create(_curr, EntryType.FOLDER);
|
for (var seg : _path) {
|
||||||
_curr += seg + "/";
|
create(_curr, EntryType.FOLDER);
|
||||||
}
|
_curr += seg + "/";
|
||||||
|
}
|
||||||
files.put(_path, new Buffer(data));
|
|
||||||
return this;
|
files.put(_path, new Buffer(data));
|
||||||
}
|
return this;
|
||||||
|
}
|
||||||
public MemoryFilesystem(Mode mode) {
|
|
||||||
this.mode = mode;
|
public MemoryFilesystem(Mode mode) {
|
||||||
folders.add(Path.of("/"));
|
this.mode = mode;
|
||||||
}
|
folders.add(Path.of("/"));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@ -1,221 +1,221 @@
|
|||||||
package me.topchetoeu.jscript.lib;
|
package me.topchetoeu.jscript.lib;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Reading;
|
import me.topchetoeu.jscript.Buffer;
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.Reading;
|
||||||
import me.topchetoeu.jscript.engine.DataKey;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
import me.topchetoeu.jscript.engine.Environment;
|
import me.topchetoeu.jscript.engine.DataKey;
|
||||||
import me.topchetoeu.jscript.engine.scope.GlobalScope;
|
import me.topchetoeu.jscript.engine.Environment;
|
||||||
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
import me.topchetoeu.jscript.engine.scope.GlobalScope;
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
||||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
import me.topchetoeu.jscript.filesystem.Buffer;
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
import me.topchetoeu.jscript.interop.Native;
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
import me.topchetoeu.jscript.interop.NativeGetter;
|
import me.topchetoeu.jscript.interop.NativeGetter;
|
||||||
import me.topchetoeu.jscript.parsing.Parsing;
|
import me.topchetoeu.jscript.parsing.Parsing;
|
||||||
|
|
||||||
public class Internals {
|
public class Internals {
|
||||||
private static final DataKey<HashMap<Integer, Thread>> THREADS = new DataKey<>();
|
private static final DataKey<HashMap<Integer, Thread>> THREADS = new DataKey<>();
|
||||||
private static final DataKey<Integer> I = new DataKey<>();
|
private static final DataKey<Integer> I = new DataKey<>();
|
||||||
|
|
||||||
|
|
||||||
@Native public static Object log(Context ctx, Object ...args) {
|
@Native public static Object log(Context ctx, Object ...args) {
|
||||||
for (var arg : args) {
|
for (var arg : args) {
|
||||||
Values.printValue(ctx, arg);
|
Values.printValue(ctx, arg);
|
||||||
System.out.print(" ");
|
System.out.print(" ");
|
||||||
}
|
}
|
||||||
System.out.println();
|
System.out.println();
|
||||||
|
|
||||||
if (args.length == 0) return null;
|
if (args.length == 0) return null;
|
||||||
else return args[0];
|
else return args[0];
|
||||||
}
|
}
|
||||||
@Native public static String readline(Context ctx) {
|
@Native public static String readline(Context ctx) {
|
||||||
try {
|
try {
|
||||||
return Reading.read();
|
return Reading.read();
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Native public static int setTimeout(Context ctx, FunctionValue func, int delay, Object ...args) {
|
@Native public static int setTimeout(Context ctx, FunctionValue func, int delay, Object ...args) {
|
||||||
var thread = new Thread(() -> {
|
var thread = new Thread(() -> {
|
||||||
var ms = (long)delay;
|
var ms = (long)delay;
|
||||||
var ns = (int)((delay - ms) * 10000000);
|
var ns = (int)((delay - ms) * 10000000);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Thread.sleep(ms, ns);
|
Thread.sleep(ms, ns);
|
||||||
}
|
}
|
||||||
catch (InterruptedException e) { return; }
|
catch (InterruptedException e) { return; }
|
||||||
|
|
||||||
ctx.engine.pushMsg(false, ctx, func, null, args);
|
ctx.engine.pushMsg(false, ctx, func, null, args);
|
||||||
});
|
});
|
||||||
thread.start();
|
thread.start();
|
||||||
|
|
||||||
int i = ctx.environment().data.increase(I, 1, 0);
|
int i = ctx.environment().data.increase(I, 1, 0);
|
||||||
var threads = ctx.environment().data.get(THREADS, new HashMap<>());
|
var threads = ctx.environment().data.get(THREADS, new HashMap<>());
|
||||||
threads.put(++i, thread);
|
threads.put(++i, thread);
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
@Native public static int setInterval(Context ctx, FunctionValue func, int delay, Object ...args) {
|
@Native public static int setInterval(Context ctx, FunctionValue func, int delay, Object ...args) {
|
||||||
var thread = new Thread(() -> {
|
var thread = new Thread(() -> {
|
||||||
var ms = (long)delay;
|
var ms = (long)delay;
|
||||||
var ns = (int)((delay - ms) * 10000000);
|
var ns = (int)((delay - ms) * 10000000);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
Thread.sleep(ms, ns);
|
Thread.sleep(ms, ns);
|
||||||
}
|
}
|
||||||
catch (InterruptedException e) { return; }
|
catch (InterruptedException e) { return; }
|
||||||
|
|
||||||
ctx.engine.pushMsg(false, ctx, func, null, args);
|
ctx.engine.pushMsg(false, ctx, func, null, args);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
thread.start();
|
thread.start();
|
||||||
|
|
||||||
int i = ctx.environment().data.increase(I, 1, 0);
|
int i = ctx.environment().data.increase(I, 1, 0);
|
||||||
var threads = ctx.environment().data.get(THREADS, new HashMap<>());
|
var threads = ctx.environment().data.get(THREADS, new HashMap<>());
|
||||||
threads.put(++i, thread);
|
threads.put(++i, thread);
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Native public static void clearTimeout(Context ctx, int i) {
|
@Native public static void clearTimeout(Context ctx, int i) {
|
||||||
var threads = ctx.environment().data.get(THREADS, new HashMap<>());
|
var threads = ctx.environment().data.get(THREADS, new HashMap<>());
|
||||||
|
|
||||||
var thread = threads.remove(i);
|
var thread = threads.remove(i);
|
||||||
if (thread != null) thread.interrupt();
|
if (thread != null) thread.interrupt();
|
||||||
}
|
}
|
||||||
@Native public static void clearInterval(Context ctx, int i) {
|
@Native public static void clearInterval(Context ctx, int i) {
|
||||||
clearTimeout(ctx, i);
|
clearTimeout(ctx, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Native public static double parseInt(Context ctx, String val) {
|
@Native public static double parseInt(Context ctx, String val) {
|
||||||
return NumberLib.parseInt(ctx, val);
|
return NumberLib.parseInt(ctx, val);
|
||||||
}
|
}
|
||||||
@Native public static double parseFloat(Context ctx, String val) {
|
@Native public static double parseFloat(Context ctx, String val) {
|
||||||
return NumberLib.parseFloat(ctx, val);
|
return NumberLib.parseFloat(ctx, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Native public static boolean isNaN(Context ctx, double val) {
|
@Native public static boolean isNaN(Context ctx, double val) {
|
||||||
return NumberLib.isNaN(ctx, val);
|
return NumberLib.isNaN(ctx, val);
|
||||||
}
|
}
|
||||||
@Native public static boolean isFinite(Context ctx, double val) {
|
@Native public static boolean isFinite(Context ctx, double val) {
|
||||||
return NumberLib.isFinite(ctx, val);
|
return NumberLib.isFinite(ctx, val);
|
||||||
}
|
}
|
||||||
@Native public static boolean isInfinite(Context ctx, double val) {
|
@Native public static boolean isInfinite(Context ctx, double val) {
|
||||||
return NumberLib.isInfinite(ctx, val);
|
return NumberLib.isInfinite(ctx, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NativeGetter public static double NaN(Context ctx) {
|
@NativeGetter public static double NaN(Context ctx) {
|
||||||
return Double.NaN;
|
return Double.NaN;
|
||||||
}
|
}
|
||||||
@NativeGetter public static double Infinity(Context ctx) {
|
@NativeGetter public static double Infinity(Context ctx) {
|
||||||
return Double.POSITIVE_INFINITY;
|
return Double.POSITIVE_INFINITY;
|
||||||
}
|
}
|
||||||
private static final String HEX = "0123456789ABCDEF";
|
private static final String HEX = "0123456789ABCDEF";
|
||||||
|
|
||||||
private static String encodeUriAny(String str, String keepAlphabet) {
|
private static String encodeUriAny(String str, String keepAlphabet) {
|
||||||
if (str == null) str = "undefined";
|
if (str == null) str = "undefined";
|
||||||
|
|
||||||
var bytes = str.getBytes();
|
var bytes = str.getBytes();
|
||||||
var sb = new StringBuilder(bytes.length);
|
var sb = new StringBuilder(bytes.length);
|
||||||
|
|
||||||
for (byte c : bytes) {
|
for (byte c : bytes) {
|
||||||
if (Parsing.isAlphanumeric((char)c) || Parsing.isAny((char)c, keepAlphabet)) sb.append((char)c);
|
if (Parsing.isAlphanumeric((char)c) || Parsing.isAny((char)c, keepAlphabet)) sb.append((char)c);
|
||||||
else {
|
else {
|
||||||
sb.append('%');
|
sb.append('%');
|
||||||
sb.append(HEX.charAt(c / 16));
|
sb.append(HEX.charAt(c / 16));
|
||||||
sb.append(HEX.charAt(c % 16));
|
sb.append(HEX.charAt(c % 16));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
private static String decodeUriAny(String str, String keepAlphabet) {
|
private static String decodeUriAny(String str, String keepAlphabet) {
|
||||||
if (str == null) str = "undefined";
|
if (str == null) str = "undefined";
|
||||||
|
|
||||||
var res = new Buffer();
|
var res = new Buffer();
|
||||||
var bytes = str.getBytes();
|
var bytes = str.getBytes();
|
||||||
|
|
||||||
for (var i = 0; i < bytes.length; i++) {
|
for (var i = 0; i < bytes.length; i++) {
|
||||||
var c = bytes[i];
|
var c = bytes[i];
|
||||||
if (c == '%') {
|
if (c == '%') {
|
||||||
if (i >= bytes.length - 2) throw EngineException.ofError("URIError", "URI malformed.");
|
if (i >= bytes.length - 2) throw EngineException.ofError("URIError", "URI malformed.");
|
||||||
var b = Parsing.fromHex((char)bytes[i + 1]) * 16 | Parsing.fromHex((char)bytes[i + 2]);
|
var b = Parsing.fromHex((char)bytes[i + 1]) * 16 | Parsing.fromHex((char)bytes[i + 2]);
|
||||||
if (!Parsing.isAny((char)b, keepAlphabet)) {
|
if (!Parsing.isAny((char)b, keepAlphabet)) {
|
||||||
i += 2;
|
i += 2;
|
||||||
res.append((byte)b);
|
res.append((byte)b);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
res.append(c);
|
res.append(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new String(res.data());
|
return new String(res.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Native public static String encodeURIComponent(String str) {
|
@Native public static String encodeURIComponent(String str) {
|
||||||
return encodeUriAny(str, ".-_!~*'()");
|
return encodeUriAny(str, ".-_!~*'()");
|
||||||
}
|
}
|
||||||
@Native public static String decodeURIComponent(String str) {
|
@Native public static String decodeURIComponent(String str) {
|
||||||
return decodeUriAny(str, "");
|
return decodeUriAny(str, "");
|
||||||
}
|
}
|
||||||
@Native public static String encodeURI(String str) {
|
@Native public static String encodeURI(String str) {
|
||||||
return encodeUriAny(str, ";,/?:@&=+$#.-_!~*'()");
|
return encodeUriAny(str, ";,/?:@&=+$#.-_!~*'()");
|
||||||
}
|
}
|
||||||
@Native public static String decodeURI(String str) {
|
@Native public static String decodeURI(String str) {
|
||||||
return decodeUriAny(str, ",/?:@&=+$#.");
|
return decodeUriAny(str, ",/?:@&=+$#.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Environment apply(Environment env) {
|
public static Environment apply(Environment env) {
|
||||||
var wp = env.wrappers;
|
var wp = env.wrappers;
|
||||||
var glob = env.global = new GlobalScope(wp.getNamespace(Internals.class));
|
var glob = env.global = new GlobalScope(wp.getNamespace(Internals.class));
|
||||||
|
|
||||||
glob.define(null, "Math", false, wp.getNamespace(MathLib.class));
|
glob.define(null, "Math", false, wp.getNamespace(MathLib.class));
|
||||||
glob.define(null, "JSON", false, wp.getNamespace(JSONLib.class));
|
glob.define(null, "JSON", false, wp.getNamespace(JSONLib.class));
|
||||||
glob.define(null, "Encoding", false, wp.getNamespace(EncodingLib.class));
|
glob.define(null, "Encoding", false, wp.getNamespace(EncodingLib.class));
|
||||||
glob.define(null, "Filesystem", false, wp.getNamespace(FilesystemLib.class));
|
glob.define(null, "Filesystem", false, wp.getNamespace(FilesystemLib.class));
|
||||||
|
|
||||||
glob.define(null, "Date", false, wp.getConstr(DateLib.class));
|
glob.define(false, wp.getConstr(DateLib.class));
|
||||||
glob.define(null, "Object", false, wp.getConstr(ObjectLib.class));
|
glob.define(false, wp.getConstr(ObjectLib.class));
|
||||||
glob.define(null, "Function", false, wp.getConstr(FunctionLib.class));
|
glob.define(false, wp.getConstr(FunctionLib.class));
|
||||||
glob.define(null, "Array", false, wp.getConstr(ArrayLib.class));
|
glob.define(false, wp.getConstr(ArrayLib.class));
|
||||||
|
|
||||||
glob.define(null, "Boolean", false, wp.getConstr(BooleanLib.class));
|
glob.define(false, wp.getConstr(BooleanLib.class));
|
||||||
glob.define(null, "Number", false, wp.getConstr(NumberLib.class));
|
glob.define(false, wp.getConstr(NumberLib.class));
|
||||||
glob.define(null, "String", false, wp.getConstr(StringLib.class));
|
glob.define(false, wp.getConstr(StringLib.class));
|
||||||
glob.define(null, "Symbol", false, wp.getConstr(SymbolLib.class));
|
glob.define(false, wp.getConstr(SymbolLib.class));
|
||||||
|
|
||||||
glob.define(null, "Promise", false, wp.getConstr(PromiseLib.class));
|
glob.define(false, wp.getConstr(PromiseLib.class));
|
||||||
glob.define(null, "RegExp", false, wp.getConstr(RegExpLib.class));
|
glob.define(false, wp.getConstr(RegExpLib.class));
|
||||||
glob.define(null, "Map", false, wp.getConstr(MapLib.class));
|
glob.define(false, wp.getConstr(MapLib.class));
|
||||||
glob.define(null, "Set", false, wp.getConstr(SetLib.class));
|
glob.define(false, wp.getConstr(SetLib.class));
|
||||||
|
|
||||||
glob.define(null, "Error", false, wp.getConstr(ErrorLib.class));
|
glob.define(false, wp.getConstr(ErrorLib.class));
|
||||||
glob.define(null, "SyntaxError", false, wp.getConstr(SyntaxErrorLib.class));
|
glob.define(false, wp.getConstr(SyntaxErrorLib.class));
|
||||||
glob.define(null, "TypeError", false, wp.getConstr(TypeErrorLib.class));
|
glob.define(false, wp.getConstr(TypeErrorLib.class));
|
||||||
glob.define(null, "RangeError", false, wp.getConstr(RangeErrorLib.class));
|
glob.define(false, wp.getConstr(RangeErrorLib.class));
|
||||||
|
|
||||||
env.setProto("object", wp.getProto(ObjectLib.class));
|
env.setProto("object", wp.getProto(ObjectLib.class));
|
||||||
env.setProto("function", wp.getProto(FunctionLib.class));
|
env.setProto("function", wp.getProto(FunctionLib.class));
|
||||||
env.setProto("array", wp.getProto(ArrayLib.class));
|
env.setProto("array", wp.getProto(ArrayLib.class));
|
||||||
|
|
||||||
env.setProto("bool", wp.getProto(BooleanLib.class));
|
env.setProto("bool", wp.getProto(BooleanLib.class));
|
||||||
env.setProto("number", wp.getProto(NumberLib.class));
|
env.setProto("number", wp.getProto(NumberLib.class));
|
||||||
env.setProto("string", wp.getProto(StringLib.class));
|
env.setProto("string", wp.getProto(StringLib.class));
|
||||||
env.setProto("symbol", wp.getProto(SymbolLib.class));
|
env.setProto("symbol", wp.getProto(SymbolLib.class));
|
||||||
|
|
||||||
env.setProto("error", wp.getProto(ErrorLib.class));
|
env.setProto("error", wp.getProto(ErrorLib.class));
|
||||||
env.setProto("syntaxErr", wp.getProto(SyntaxErrorLib.class));
|
env.setProto("syntaxErr", wp.getProto(SyntaxErrorLib.class));
|
||||||
env.setProto("typeErr", wp.getProto(TypeErrorLib.class));
|
env.setProto("typeErr", wp.getProto(TypeErrorLib.class));
|
||||||
env.setProto("rangeErr", wp.getProto(RangeErrorLib.class));
|
env.setProto("rangeErr", wp.getProto(RangeErrorLib.class));
|
||||||
|
|
||||||
wp.getProto(ObjectLib.class).setPrototype(null, null);
|
wp.getProto(ObjectLib.class).setPrototype(null, null);
|
||||||
env.regexConstructor = wp.getConstr(RegExpLib.class);
|
env.regexConstructor = wp.getConstr(RegExpLib.class);
|
||||||
|
|
||||||
return env;
|
return env;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,78 +1,78 @@
|
|||||||
package me.topchetoeu.jscript.lib;
|
package me.topchetoeu.jscript.lib;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
||||||
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
||||||
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
import me.topchetoeu.jscript.interop.Native;
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
import me.topchetoeu.jscript.interop.NativeGetter;
|
import me.topchetoeu.jscript.interop.NativeGetter;
|
||||||
|
|
||||||
@Native("Map") public class MapLib {
|
@Native("Map") public class MapLib {
|
||||||
private LinkedHashMap<Object, Object> map = new LinkedHashMap<>();
|
private LinkedHashMap<Object, Object> map = new LinkedHashMap<>();
|
||||||
|
|
||||||
@Native("@@Symbol.typeName") public final String name = "Map";
|
@Native("@@Symbol.typeName") public final String name = "Map";
|
||||||
@Native("@@Symbol.iterator") public ObjectValue iterator(Context ctx) {
|
@Native("@@Symbol.iterator") public ObjectValue iterator(Context ctx) {
|
||||||
return this.entries(ctx);
|
return this.entries(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Native public void clear() {
|
@Native public void clear() {
|
||||||
map.clear();
|
map.clear();
|
||||||
}
|
}
|
||||||
@Native public boolean delete(Object key) {
|
@Native public boolean delete(Object key) {
|
||||||
if (map.containsKey(key)) {
|
if (map.containsKey(key)) {
|
||||||
map.remove(key);
|
map.remove(key);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Native public ObjectValue entries(Context ctx) {
|
@Native public ObjectValue entries(Context ctx) {
|
||||||
var res = map.entrySet().stream().map(v -> {
|
return ArrayValue.of(ctx, map
|
||||||
return new ArrayValue(ctx, v.getKey(), v.getValue());
|
.entrySet()
|
||||||
}).collect(Collectors.toList());
|
.stream()
|
||||||
return Values.toJSIterator(ctx, res.iterator());
|
.map(v -> new ArrayValue(ctx, v.getKey(), v.getValue()))
|
||||||
}
|
.collect(Collectors.toList())
|
||||||
@Native public ObjectValue keys(Context ctx) {
|
);
|
||||||
var res = new ArrayList<>(map.keySet());
|
}
|
||||||
return Values.toJSIterator(ctx, res.iterator());
|
@Native public ObjectValue keys(Context ctx) {
|
||||||
}
|
return ArrayValue.of(ctx, map.keySet());
|
||||||
@Native public ObjectValue values(Context ctx) {
|
}
|
||||||
var res = new ArrayList<>(map.values());
|
@Native public ObjectValue values(Context ctx) {
|
||||||
return Values.toJSIterator(ctx, res.iterator());
|
return ArrayValue.of(ctx, map.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Native public Object get(Object key) {
|
@Native public Object get(Object key) {
|
||||||
return map.get(key);
|
return map.get(key);
|
||||||
}
|
}
|
||||||
@Native public MapLib set(Object key, Object val) {
|
@Native public MapLib set(Object key, Object val) {
|
||||||
map.put(key, val);
|
map.put(key, val);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@Native public boolean has(Object key) {
|
@Native public boolean has(Object key) {
|
||||||
return map.containsKey(key);
|
return map.containsKey(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NativeGetter public int size() {
|
@NativeGetter public int size() {
|
||||||
return map.size();
|
return map.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Native public void forEach(Context ctx, FunctionValue func, Object thisArg) {
|
@Native public void forEach(Context ctx, FunctionValue func, Object thisArg) {
|
||||||
var keys = new ArrayList<>(map.keySet());
|
var keys = new ArrayList<>(map.keySet());
|
||||||
|
|
||||||
for (var el : keys) func.call(ctx, thisArg, map.get(el), el,this);
|
for (var el : keys) func.call(ctx, thisArg, map.get(el), el,this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Native public MapLib(Context ctx, Object iterable) {
|
@Native public MapLib(Context ctx, Object iterable) {
|
||||||
for (var el : Values.fromJSIterator(ctx, iterable)) {
|
for (var el : Values.fromJSIterator(ctx, iterable)) {
|
||||||
try {
|
try {
|
||||||
set(Values.getMember(ctx, el, 0), Values.getMember(ctx, el, 1));
|
set(Values.getMember(ctx, el, 0), Values.getMember(ctx, el, 1));
|
||||||
}
|
}
|
||||||
catch (IllegalArgumentException e) { }
|
catch (IllegalArgumentException e) { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,366 +1,367 @@
|
|||||||
package me.topchetoeu.jscript.lib;
|
package me.topchetoeu.jscript.lib;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
||||||
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
||||||
import me.topchetoeu.jscript.engine.values.NativeFunction;
|
import me.topchetoeu.jscript.engine.values.NativeFunction;
|
||||||
import me.topchetoeu.jscript.engine.values.NativeWrapper;
|
import me.topchetoeu.jscript.engine.values.NativeWrapper;
|
||||||
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
import me.topchetoeu.jscript.interop.Native;
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
|
|
||||||
@Native("Promise") public class PromiseLib {
|
@Native("Promise") public class PromiseLib {
|
||||||
public static interface PromiseRunner {
|
public static interface PromiseRunner {
|
||||||
Object run();
|
Object run();
|
||||||
}
|
}
|
||||||
private static class Handle {
|
private static class Handle {
|
||||||
public final Context ctx;
|
public final Context ctx;
|
||||||
public final FunctionValue fulfilled;
|
public final FunctionValue fulfilled;
|
||||||
public final FunctionValue rejected;
|
public final FunctionValue rejected;
|
||||||
|
|
||||||
public Handle(Context ctx, FunctionValue fulfilled, FunctionValue rejected) {
|
public Handle(Context ctx, FunctionValue fulfilled, FunctionValue rejected) {
|
||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
this.fulfilled = fulfilled;
|
this.fulfilled = fulfilled;
|
||||||
this.rejected = rejected;
|
this.rejected = rejected;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Native("resolve")
|
@Native("resolve")
|
||||||
public static PromiseLib ofResolved(Context ctx, Object val) {
|
public static PromiseLib ofResolved(Context ctx, Object val) {
|
||||||
var res = new PromiseLib();
|
var res = new PromiseLib();
|
||||||
res.fulfill(ctx, val);
|
res.fulfill(ctx, val);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@Native("reject")
|
@Native("reject")
|
||||||
public static PromiseLib ofRejected(Context ctx, Object val) {
|
public static PromiseLib ofRejected(Context ctx, Object val) {
|
||||||
var res = new PromiseLib();
|
var res = new PromiseLib();
|
||||||
res.reject(ctx, val);
|
res.reject(ctx, val);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Native public static PromiseLib any(Context ctx, Object _promises) {
|
@Native public static PromiseLib any(Context ctx, Object _promises) {
|
||||||
if (!Values.isArray(_promises)) throw EngineException.ofType("Expected argument for any to be an array.");
|
if (!Values.isArray(_promises)) throw EngineException.ofType("Expected argument for any to be an array.");
|
||||||
var promises = Values.array(_promises);
|
var promises = Values.array(_promises);
|
||||||
if (promises.size() == 0) return ofResolved(ctx, new ArrayValue());
|
if (promises.size() == 0) return ofResolved(ctx, new ArrayValue());
|
||||||
var n = new int[] { promises.size() };
|
var n = new int[] { promises.size() };
|
||||||
var res = new PromiseLib();
|
var res = new PromiseLib();
|
||||||
|
|
||||||
var errors = new ArrayValue();
|
var errors = new ArrayValue();
|
||||||
|
|
||||||
for (var i = 0; i < promises.size(); i++) {
|
for (var i = 0; i < promises.size(); i++) {
|
||||||
var index = i;
|
var index = i;
|
||||||
var val = promises.get(i);
|
var val = promises.get(i);
|
||||||
then(ctx, val,
|
then(ctx, val,
|
||||||
new NativeFunction(null, (e, th, args) -> { res.fulfill(e, args[0]); return null; }),
|
new NativeFunction(null, (e, th, args) -> { res.fulfill(e, args[0]); return null; }),
|
||||||
new NativeFunction(null, (e, th, args) -> {
|
new NativeFunction(null, (e, th, args) -> {
|
||||||
errors.set(ctx, index, args[0]);
|
errors.set(ctx, index, args[0]);
|
||||||
n[0]--;
|
n[0]--;
|
||||||
if (n[0] <= 0) res.reject(e, errors);
|
if (n[0] <= 0) res.reject(e, errors);
|
||||||
return null;
|
return null;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@Native public static PromiseLib race(Context ctx, Object _promises) {
|
@Native public static PromiseLib race(Context ctx, Object _promises) {
|
||||||
if (!Values.isArray(_promises)) throw EngineException.ofType("Expected argument for any to be an array.");
|
if (!Values.isArray(_promises)) throw EngineException.ofType("Expected argument for any to be an array.");
|
||||||
var promises = Values.array(_promises);
|
var promises = Values.array(_promises);
|
||||||
if (promises.size() == 0) return ofResolved(ctx, new ArrayValue());
|
if (promises.size() == 0) return ofResolved(ctx, new ArrayValue());
|
||||||
var res = new PromiseLib();
|
var res = new PromiseLib();
|
||||||
|
|
||||||
for (var i = 0; i < promises.size(); i++) {
|
for (var i = 0; i < promises.size(); i++) {
|
||||||
var val = promises.get(i);
|
var val = promises.get(i);
|
||||||
then(ctx, val,
|
then(ctx, val,
|
||||||
new NativeFunction(null, (e, th, args) -> { res.fulfill(e, args[0]); return null; }),
|
new NativeFunction(null, (e, th, args) -> { res.fulfill(e, args[0]); return null; }),
|
||||||
new NativeFunction(null, (e, th, args) -> { res.reject(e, args[0]); return null; })
|
new NativeFunction(null, (e, th, args) -> { res.reject(e, args[0]); return null; })
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@Native public static PromiseLib all(Context ctx, Object _promises) {
|
@Native public static PromiseLib all(Context ctx, Object _promises) {
|
||||||
if (!Values.isArray(_promises)) throw EngineException.ofType("Expected argument for any to be an array.");
|
if (!Values.isArray(_promises)) throw EngineException.ofType("Expected argument for any to be an array.");
|
||||||
var promises = Values.array(_promises);
|
var promises = Values.array(_promises);
|
||||||
if (promises.size() == 0) return ofResolved(ctx, new ArrayValue());
|
if (promises.size() == 0) return ofResolved(ctx, new ArrayValue());
|
||||||
var n = new int[] { promises.size() };
|
var n = new int[] { promises.size() };
|
||||||
var res = new PromiseLib();
|
var res = new PromiseLib();
|
||||||
|
|
||||||
var result = new ArrayValue();
|
var result = new ArrayValue();
|
||||||
|
|
||||||
for (var i = 0; i < promises.size(); i++) {
|
for (var i = 0; i < promises.size(); i++) {
|
||||||
var index = i;
|
var index = i;
|
||||||
var val = promises.get(i);
|
var val = promises.get(i);
|
||||||
then(ctx, val,
|
then(ctx, val,
|
||||||
new NativeFunction(null, (e, th, args) -> {
|
new NativeFunction(null, (e, th, args) -> {
|
||||||
result.set(ctx, index, args[0]);
|
result.set(ctx, index, args[0]);
|
||||||
n[0]--;
|
n[0]--;
|
||||||
if (n[0] <= 0) res.fulfill(e, result);
|
if (n[0] <= 0) res.fulfill(e, result);
|
||||||
return null;
|
return null;
|
||||||
}),
|
}),
|
||||||
new NativeFunction(null, (e, th, args) -> { res.reject(e, args[0]); return null; })
|
new NativeFunction(null, (e, th, args) -> { res.reject(e, args[0]); return null; })
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (n[0] <= 0) res.fulfill(ctx, result);
|
if (n[0] <= 0) res.fulfill(ctx, result);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@Native public static PromiseLib allSettled(Context ctx, Object _promises) {
|
@Native public static PromiseLib allSettled(Context ctx, Object _promises) {
|
||||||
if (!Values.isArray(_promises)) throw EngineException.ofType("Expected argument for any to be an array.");
|
if (!Values.isArray(_promises)) throw EngineException.ofType("Expected argument for any to be an array.");
|
||||||
var promises = Values.array(_promises);
|
var promises = Values.array(_promises);
|
||||||
if (promises.size() == 0) return ofResolved(ctx, new ArrayValue());
|
if (promises.size() == 0) return ofResolved(ctx, new ArrayValue());
|
||||||
var n = new int[] { promises.size() };
|
var n = new int[] { promises.size() };
|
||||||
var res = new PromiseLib();
|
var res = new PromiseLib();
|
||||||
|
|
||||||
var result = new ArrayValue();
|
var result = new ArrayValue();
|
||||||
|
|
||||||
for (var i = 0; i < promises.size(); i++) {
|
for (var i = 0; i < promises.size(); i++) {
|
||||||
var index = i;
|
var index = i;
|
||||||
var val = promises.get(i);
|
var val = promises.get(i);
|
||||||
then(ctx, val,
|
then(ctx, val,
|
||||||
new NativeFunction(null, (e, th, args) -> {
|
new NativeFunction(null, (e, th, args) -> {
|
||||||
result.set(ctx, index, new ObjectValue(ctx, Map.of(
|
result.set(ctx, index, new ObjectValue(ctx, Map.of(
|
||||||
"status", "fulfilled",
|
"status", "fulfilled",
|
||||||
"value", args[0]
|
"value", args[0]
|
||||||
)));
|
)));
|
||||||
n[0]--;
|
n[0]--;
|
||||||
if (n[0] <= 0) res.fulfill(e, result);
|
if (n[0] <= 0) res.fulfill(e, result);
|
||||||
return null;
|
return null;
|
||||||
}),
|
}),
|
||||||
new NativeFunction(null, (e, th, args) -> {
|
new NativeFunction(null, (e, th, args) -> {
|
||||||
result.set(ctx, index, new ObjectValue(ctx, Map.of(
|
result.set(ctx, index, new ObjectValue(ctx, Map.of(
|
||||||
"status", "rejected",
|
"status", "rejected",
|
||||||
"reason", args[0]
|
"reason", args[0]
|
||||||
)));
|
)));
|
||||||
n[0]--;
|
n[0]--;
|
||||||
if (n[0] <= 0) res.fulfill(e, result);
|
if (n[0] <= 0) res.fulfill(e, result);
|
||||||
return null;
|
return null;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (n[0] <= 0) res.fulfill(ctx, result);
|
if (n[0] <= 0) res.fulfill(ctx, result);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Thread safe - you can call this from anywhere
|
* Thread safe - you can call this from anywhere
|
||||||
* HOWEVER, it's strongly recommended to use this only in javascript
|
* HOWEVER, it's strongly recommended to use this only in javascript
|
||||||
*/
|
*/
|
||||||
@Native(thisArg=true) public static Object then(Context ctx, Object thisArg, Object _onFulfill, Object _onReject) {
|
@Native(thisArg=true) public static Object then(Context ctx, Object _thisArg, Object _onFulfill, Object _onReject) {
|
||||||
var onFulfill = _onFulfill instanceof FunctionValue ? ((FunctionValue)_onFulfill) : null;
|
var onFulfill = _onFulfill instanceof FunctionValue ? ((FunctionValue)_onFulfill) : null;
|
||||||
var onReject = _onReject instanceof FunctionValue ? ((FunctionValue)_onReject) : null;
|
var onReject = _onReject instanceof FunctionValue ? ((FunctionValue)_onReject) : null;
|
||||||
|
|
||||||
var res = new PromiseLib();
|
var res = new PromiseLib();
|
||||||
|
|
||||||
var fulfill = onFulfill == null ? new NativeFunction((_ctx, _thisArg, _args) -> _args.length > 0 ? _args[0] : null) : (FunctionValue)onFulfill;
|
var fulfill = onFulfill == null ? new NativeFunction((_ctx, _0, _args) -> _args.length > 0 ? _args[0] : null) : (FunctionValue)onFulfill;
|
||||||
var reject = onReject == null ? new NativeFunction((_ctx, _thisArg, _args) -> {
|
var reject = onReject == null ? new NativeFunction((_ctx, _0, _args) -> {
|
||||||
throw new EngineException(_args.length > 0 ? _args[0] : null);
|
throw new EngineException(_args.length > 0 ? _args[0] : null);
|
||||||
}) : (FunctionValue)onReject;
|
}) : (FunctionValue)onReject;
|
||||||
|
|
||||||
if (thisArg instanceof NativeWrapper && ((NativeWrapper)thisArg).wrapped instanceof PromiseLib) {
|
var thisArg = _thisArg instanceof NativeWrapper && ((NativeWrapper)_thisArg).wrapped instanceof PromiseLib ?
|
||||||
thisArg = ((NativeWrapper)thisArg).wrapped;
|
((NativeWrapper)_thisArg).wrapped :
|
||||||
}
|
_thisArg;
|
||||||
|
|
||||||
var fulfillHandle = new NativeFunction(null, (_ctx, th, a) -> {
|
var fulfillHandle = new NativeFunction(null, (_ctx, th, a) -> {
|
||||||
try { res.fulfill(ctx, Values.convert(ctx, fulfill.call(ctx, null, a[0]), Object.class)); }
|
try { res.fulfill(ctx, Values.convert(ctx, fulfill.call(ctx, null, a[0]), Object.class)); }
|
||||||
catch (EngineException err) { res.reject(ctx, err.value); }
|
catch (EngineException err) { res.reject(ctx, err.value); }
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
var rejectHandle = new NativeFunction(null, (_ctx, th, a) -> {
|
var rejectHandle = new NativeFunction(null, (_ctx, th, a) -> {
|
||||||
try { res.fulfill(ctx, reject.call(ctx, null, a[0])); }
|
try { res.fulfill(ctx, reject.call(ctx, null, a[0])); }
|
||||||
catch (EngineException err) { res.reject(ctx, err.value); }
|
catch (EngineException err) { res.reject(ctx, err.value); }
|
||||||
return null;
|
if (thisArg instanceof PromiseLib) ((PromiseLib)thisArg).handled = true;
|
||||||
});
|
return null;
|
||||||
|
});
|
||||||
if (thisArg instanceof PromiseLib) ((PromiseLib)thisArg).handle(ctx, fulfillHandle, rejectHandle);
|
|
||||||
else {
|
if (thisArg instanceof PromiseLib) ((PromiseLib)thisArg).handle(ctx, fulfillHandle, rejectHandle);
|
||||||
Object next;
|
else {
|
||||||
try { next = Values.getMember(ctx, thisArg, "then"); }
|
Object next;
|
||||||
catch (IllegalArgumentException e) { next = null; }
|
try { next = Values.getMember(ctx, thisArg, "then"); }
|
||||||
|
catch (IllegalArgumentException e) { next = null; }
|
||||||
try {
|
|
||||||
if (next instanceof FunctionValue) ((FunctionValue)next).call(ctx, thisArg, fulfillHandle, rejectHandle);
|
try {
|
||||||
else res.fulfill(ctx, fulfill.call(ctx, null, thisArg));
|
if (next instanceof FunctionValue) ((FunctionValue)next).call(ctx, thisArg, fulfillHandle, rejectHandle);
|
||||||
}
|
else res.fulfill(ctx, fulfill.call(ctx, null, thisArg));
|
||||||
catch (EngineException err) {
|
}
|
||||||
res.reject(ctx, fulfill.call(ctx, null, err.value));
|
catch (EngineException err) {
|
||||||
}
|
res.reject(ctx, fulfill.call(ctx, null, err.value));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return res;
|
|
||||||
}
|
return res;
|
||||||
/**
|
}
|
||||||
* Thread safe - you can call this from anywhere
|
/**
|
||||||
* HOWEVER, it's strongly recommended to use this only in javascript
|
* Thread safe - you can call this from anywhere
|
||||||
*/
|
* HOWEVER, it's strongly recommended to use this only in javascript
|
||||||
@Native(value="catch", thisArg=true) public static Object _catch(Context ctx, Object thisArg, Object _onReject) {
|
*/
|
||||||
return then(ctx, thisArg, null, _onReject);
|
@Native(value="catch", thisArg=true) public static Object _catch(Context ctx, Object thisArg, Object _onReject) {
|
||||||
}
|
return then(ctx, thisArg, null, _onReject);
|
||||||
/**
|
}
|
||||||
* Thread safe - you can call this from anywhere
|
/**
|
||||||
* HOWEVER, it's strongly recommended to use this only in javascript
|
* Thread safe - you can call this from anywhere
|
||||||
*/
|
* HOWEVER, it's strongly recommended to use this only in javascript
|
||||||
@Native(value="finally", thisArg=true) public static Object _finally(Context ctx, Object thisArg, Object _handle) {
|
*/
|
||||||
return then(ctx, thisArg,
|
@Native(value="finally", thisArg=true) public static Object _finally(Context ctx, Object thisArg, Object _handle) {
|
||||||
new NativeFunction(null, (e, th, _args) -> {
|
return then(ctx, thisArg,
|
||||||
if (_handle instanceof FunctionValue) ((FunctionValue)_handle).call(ctx);
|
new NativeFunction(null, (e, th, _args) -> {
|
||||||
return _args.length > 0 ? _args[0] : null;
|
if (_handle instanceof FunctionValue) ((FunctionValue)_handle).call(ctx);
|
||||||
}),
|
return _args.length > 0 ? _args[0] : null;
|
||||||
new NativeFunction(null, (e, th, _args) -> {
|
}),
|
||||||
if (_handle instanceof FunctionValue) ((FunctionValue)_handle).call(ctx);
|
new NativeFunction(null, (e, th, _args) -> {
|
||||||
throw new EngineException(_args.length > 0 ? _args[0] : null);
|
if (_handle instanceof FunctionValue) ((FunctionValue)_handle).call(ctx);
|
||||||
})
|
throw new EngineException(_args.length > 0 ? _args[0] : null);
|
||||||
);
|
})
|
||||||
}
|
);
|
||||||
|
}
|
||||||
private List<Handle> handles = new ArrayList<>();
|
|
||||||
|
private List<Handle> handles = new ArrayList<>();
|
||||||
private static final int STATE_PENDING = 0;
|
|
||||||
private static final int STATE_FULFILLED = 1;
|
private static final int STATE_PENDING = 0;
|
||||||
private static final int STATE_REJECTED = 2;
|
private static final int STATE_FULFILLED = 1;
|
||||||
|
private static final int STATE_REJECTED = 2;
|
||||||
private int state = STATE_PENDING;
|
|
||||||
private boolean handled = false;
|
private int state = STATE_PENDING;
|
||||||
private Object val;
|
private boolean handled = false;
|
||||||
|
private Object val;
|
||||||
public synchronized void fulfill(Context ctx, Object val) {
|
|
||||||
if (this.state != STATE_PENDING) return;
|
public synchronized void fulfill(Context ctx, Object val) {
|
||||||
|
if (this.state != STATE_PENDING) return;
|
||||||
if (val instanceof PromiseLib) ((PromiseLib)val).handle(ctx,
|
|
||||||
new NativeFunction(null, (e, th, a) -> { this.fulfill(ctx, a[0]); return null; }),
|
if (val instanceof PromiseLib) ((PromiseLib)val).handle(ctx,
|
||||||
new NativeFunction(null, (e, th, a) -> { this.reject(ctx, a[0]); return null; })
|
new NativeFunction(null, (e, th, a) -> { this.fulfill(ctx, a[0]); return null; }),
|
||||||
);
|
new NativeFunction(null, (e, th, a) -> { this.reject(ctx, a[0]); return null; })
|
||||||
else {
|
);
|
||||||
Object then;
|
else {
|
||||||
try { then = Values.getMember(ctx, val, "then"); }
|
Object then;
|
||||||
catch (IllegalArgumentException e) { then = null; }
|
try { then = Values.getMember(ctx, val, "then"); }
|
||||||
|
catch (IllegalArgumentException e) { then = null; }
|
||||||
try {
|
|
||||||
if (then instanceof FunctionValue) ((FunctionValue)then).call(ctx, val,
|
try {
|
||||||
new NativeFunction((e, _thisArg, a) -> { this.fulfill(ctx, a.length > 0 ? a[0] : null); return null; }),
|
if (then instanceof FunctionValue) ((FunctionValue)then).call(ctx, val,
|
||||||
new NativeFunction((e, _thisArg, a) -> { this.reject(ctx, a.length > 0 ? a[0] : null); return null; })
|
new NativeFunction((e, _thisArg, a) -> { this.fulfill(ctx, a.length > 0 ? a[0] : null); return null; }),
|
||||||
);
|
new NativeFunction((e, _thisArg, a) -> { this.reject(ctx, a.length > 0 ? a[0] : null); return null; })
|
||||||
else {
|
);
|
||||||
this.val = val;
|
else {
|
||||||
this.state = STATE_FULFILLED;
|
this.val = val;
|
||||||
|
this.state = STATE_FULFILLED;
|
||||||
ctx.engine.pushMsg(true, ctx, new NativeFunction((_ctx, _thisArg, _args) -> {
|
|
||||||
for (var handle : handles) {
|
ctx.engine.pushMsg(true, ctx, new NativeFunction((_ctx, _thisArg, _args) -> {
|
||||||
handle.fulfilled.call(handle.ctx, null, val);
|
for (var handle : handles) {
|
||||||
}
|
handle.fulfilled.call(handle.ctx, null, val);
|
||||||
handles = null;
|
}
|
||||||
return null;
|
handles = null;
|
||||||
}), null);
|
return null;
|
||||||
}
|
}), null);
|
||||||
}
|
}
|
||||||
catch (EngineException err) {
|
}
|
||||||
this.reject(ctx, err.value);
|
catch (EngineException err) {
|
||||||
}
|
this.reject(ctx, err.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public synchronized void reject(Context ctx, Object val) {
|
}
|
||||||
if (this.state != STATE_PENDING) return;
|
public synchronized void reject(Context ctx, Object val) {
|
||||||
|
if (this.state != STATE_PENDING) return;
|
||||||
if (val instanceof PromiseLib) ((PromiseLib)val).handle(ctx,
|
|
||||||
new NativeFunction(null, (e, th, a) -> { this.reject(ctx, a[0]); return null; }),
|
if (val instanceof PromiseLib) ((PromiseLib)val).handle(ctx,
|
||||||
new NativeFunction(null, (e, th, a) -> { this.reject(ctx, a[0]); return null; })
|
new NativeFunction(null, (e, th, a) -> { this.reject(ctx, a[0]); return null; }),
|
||||||
);
|
new NativeFunction(null, (e, th, a) -> { this.reject(ctx, a[0]); return null; })
|
||||||
else {
|
);
|
||||||
Object then;
|
else {
|
||||||
try { then = Values.getMember(ctx, val, "then"); }
|
Object then;
|
||||||
catch (IllegalArgumentException e) { then = null; }
|
try { then = Values.getMember(ctx, val, "then"); }
|
||||||
|
catch (IllegalArgumentException e) { then = null; }
|
||||||
try {
|
|
||||||
if (then instanceof FunctionValue) ((FunctionValue)then).call(ctx, val,
|
try {
|
||||||
new NativeFunction((e, _thisArg, a) -> { this.reject(ctx, a.length > 0 ? a[0] : null); return null; }),
|
if (then instanceof FunctionValue) ((FunctionValue)then).call(ctx, val,
|
||||||
new NativeFunction((e, _thisArg, a) -> { this.reject(ctx, a.length > 0 ? a[0] : null); return null; })
|
new NativeFunction((e, _thisArg, a) -> { this.reject(ctx, a.length > 0 ? a[0] : null); return null; }),
|
||||||
);
|
new NativeFunction((e, _thisArg, a) -> { this.reject(ctx, a.length > 0 ? a[0] : null); return null; })
|
||||||
else {
|
);
|
||||||
this.val = val;
|
else {
|
||||||
this.state = STATE_REJECTED;
|
this.val = val;
|
||||||
|
this.state = STATE_REJECTED;
|
||||||
ctx.engine.pushMsg(true, ctx, new NativeFunction((_ctx, _thisArg, _args) -> {
|
|
||||||
for (var handle : handles) handle.rejected.call(handle.ctx, null, val);
|
ctx.engine.pushMsg(true, ctx, new NativeFunction((_ctx, _thisArg, _args) -> {
|
||||||
if (!handled) {
|
for (var handle : handles) handle.rejected.call(handle.ctx, null, val);
|
||||||
Values.printError(new EngineException(val).setCtx(ctx.environment(), ctx.engine), "(in promise)");
|
if (!handled) {
|
||||||
}
|
Values.printError(new EngineException(val).setCtx(ctx.environment(), ctx.engine), "(in promise)");
|
||||||
handles = null;
|
}
|
||||||
return null;
|
handles = null;
|
||||||
}), null);
|
return null;
|
||||||
}
|
}), null);
|
||||||
}
|
}
|
||||||
catch (EngineException err) {
|
}
|
||||||
this.reject(ctx, err.value);
|
catch (EngineException err) {
|
||||||
}
|
this.reject(ctx, err.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
private void handle(Context ctx, FunctionValue fulfill, FunctionValue reject) {
|
|
||||||
if (state == STATE_FULFILLED) ctx.engine.pushMsg(true, ctx, fulfill, null, val);
|
private void handle(Context ctx, FunctionValue fulfill, FunctionValue reject) {
|
||||||
else if (state == STATE_REJECTED) {
|
if (state == STATE_FULFILLED) ctx.engine.pushMsg(true, ctx, fulfill, null, val);
|
||||||
ctx.engine.pushMsg(true, ctx, reject, null, val);
|
else if (state == STATE_REJECTED) {
|
||||||
handled = true;
|
ctx.engine.pushMsg(true, ctx, reject, null, val);
|
||||||
}
|
handled = true;
|
||||||
else handles.add(new Handle(ctx, fulfill, reject));
|
}
|
||||||
}
|
else handles.add(new Handle(ctx, fulfill, reject));
|
||||||
|
}
|
||||||
@Override @Native public String toString() {
|
|
||||||
if (state == STATE_PENDING) return "Promise (pending)";
|
@Override @Native public String toString() {
|
||||||
else if (state == STATE_FULFILLED) return "Promise (fulfilled)";
|
if (state == STATE_PENDING) return "Promise (pending)";
|
||||||
else return "Promise (rejected)";
|
else if (state == STATE_FULFILLED) return "Promise (fulfilled)";
|
||||||
}
|
else return "Promise (rejected)";
|
||||||
|
}
|
||||||
/**
|
|
||||||
* NOT THREAD SAFE - must be called from the engine executor thread
|
/**
|
||||||
*/
|
* NOT THREAD SAFE - must be called from the engine executor thread
|
||||||
@Native public PromiseLib(Context ctx, FunctionValue func) {
|
*/
|
||||||
if (!(func instanceof FunctionValue)) throw EngineException.ofType("A function must be passed to the promise constructor.");
|
@Native public PromiseLib(Context ctx, FunctionValue func) {
|
||||||
try {
|
if (!(func instanceof FunctionValue)) throw EngineException.ofType("A function must be passed to the promise constructor.");
|
||||||
func.call(
|
try {
|
||||||
ctx, null,
|
func.call(
|
||||||
new NativeFunction(null, (e, th, args) -> {
|
ctx, null,
|
||||||
fulfill(e, args.length > 0 ? args[0] : null);
|
new NativeFunction(null, (e, th, args) -> {
|
||||||
return null;
|
fulfill(e, args.length > 0 ? args[0] : null);
|
||||||
}),
|
return null;
|
||||||
new NativeFunction(null, (e, th, args) -> {
|
}),
|
||||||
reject(e, args.length > 0 ? args[0] : null);
|
new NativeFunction(null, (e, th, args) -> {
|
||||||
return null;
|
reject(e, args.length > 0 ? args[0] : null);
|
||||||
})
|
return null;
|
||||||
);
|
})
|
||||||
}
|
);
|
||||||
catch (EngineException e) {
|
}
|
||||||
reject(ctx, e.value);
|
catch (EngineException e) {
|
||||||
}
|
reject(ctx, e.value);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
private PromiseLib(int state, Object val) {
|
|
||||||
this.state = state;
|
private PromiseLib(int state, Object val) {
|
||||||
this.val = val;
|
this.state = state;
|
||||||
}
|
this.val = val;
|
||||||
public PromiseLib() {
|
}
|
||||||
this(STATE_PENDING, null);
|
public PromiseLib() {
|
||||||
}
|
this(STATE_PENDING, null);
|
||||||
|
}
|
||||||
public static PromiseLib await(Context ctx, PromiseRunner runner) {
|
|
||||||
var res = new PromiseLib();
|
public static PromiseLib await(Context ctx, PromiseRunner runner) {
|
||||||
|
var res = new PromiseLib();
|
||||||
new Thread(() -> {
|
|
||||||
try {
|
new Thread(() -> {
|
||||||
res.fulfill(ctx, runner.run());
|
try {
|
||||||
}
|
res.fulfill(ctx, runner.run());
|
||||||
catch (EngineException e) {
|
}
|
||||||
res.reject(ctx, e.value);
|
catch (EngineException e) {
|
||||||
}
|
res.reject(ctx, e.value);
|
||||||
}, "Promisifier").start();
|
}
|
||||||
|
}, "Promisifier").start();
|
||||||
return res;
|
|
||||||
}
|
return res;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@ -1,63 +1,60 @@
|
|||||||
package me.topchetoeu.jscript.lib;
|
package me.topchetoeu.jscript.lib;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
||||||
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
||||||
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
import me.topchetoeu.jscript.interop.Native;
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
import me.topchetoeu.jscript.interop.NativeGetter;
|
import me.topchetoeu.jscript.interop.NativeGetter;
|
||||||
|
|
||||||
@Native("Set") public class SetLib {
|
@Native("Set") public class SetLib {
|
||||||
private LinkedHashSet<Object> set = new LinkedHashSet<>();
|
private LinkedHashSet<Object> set = new LinkedHashSet<>();
|
||||||
|
|
||||||
@Native("@@Symbol.typeName") public final String name = "Set";
|
@Native("@@Symbol.typeName") public final String name = "Set";
|
||||||
@Native("@@Symbol.iterator") public ObjectValue iterator(Context ctx) {
|
@Native("@@Symbol.iterator") public ObjectValue iterator(Context ctx) {
|
||||||
return this.values(ctx);
|
return this.values(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Native public ObjectValue entries(Context ctx) {
|
@Native public ObjectValue entries(Context ctx) {
|
||||||
var res = set.stream().map(v -> new ArrayValue(ctx, v, v)).collect(Collectors.toList());
|
return ArrayValue.of(ctx, set.stream().map(v -> new ArrayValue(ctx, v, v)).collect(Collectors.toList()));
|
||||||
return Values.toJSIterator(ctx, res.iterator());
|
}
|
||||||
}
|
@Native public ObjectValue keys(Context ctx) {
|
||||||
@Native public ObjectValue keys(Context ctx) {
|
return ArrayValue.of(ctx, set);
|
||||||
var res = new ArrayList<>(set);
|
}
|
||||||
return Values.toJSIterator(ctx, res.iterator());
|
@Native public ObjectValue values(Context ctx) {
|
||||||
}
|
return ArrayValue.of(ctx, set);
|
||||||
@Native public ObjectValue values(Context ctx) {
|
}
|
||||||
var res = new ArrayList<>(set);
|
|
||||||
return Values.toJSIterator(ctx, res.iterator());
|
@Native public Object add(Object key) {
|
||||||
}
|
return set.add(key);
|
||||||
|
}
|
||||||
@Native public Object add(Object key) {
|
@Native public boolean delete(Object key) {
|
||||||
return set.add(key);
|
return set.remove(key);
|
||||||
}
|
}
|
||||||
@Native public boolean delete(Object key) {
|
@Native public boolean has(Object key) {
|
||||||
return set.remove(key);
|
return set.contains(key);
|
||||||
}
|
}
|
||||||
@Native public boolean has(Object key) {
|
|
||||||
return set.contains(key);
|
@Native public void clear() {
|
||||||
}
|
set.clear();
|
||||||
|
}
|
||||||
@Native public void clear() {
|
|
||||||
set.clear();
|
@NativeGetter public int size() {
|
||||||
}
|
return set.size();
|
||||||
|
}
|
||||||
@NativeGetter public int size() {
|
|
||||||
return set.size();
|
@Native public void forEach(Context ctx, FunctionValue func, Object thisArg) {
|
||||||
}
|
var keys = new ArrayList<>(set);
|
||||||
|
|
||||||
@Native public void forEach(Context ctx, FunctionValue func, Object thisArg) {
|
for (var el : keys) func.call(ctx, thisArg, el, el, this);
|
||||||
var keys = new ArrayList<>(set);
|
}
|
||||||
|
|
||||||
for (var el : keys) func.call(ctx, thisArg, el, el, this);
|
@Native public SetLib(Context ctx, Object iterable) {
|
||||||
}
|
for (var el : Values.fromJSIterator(ctx, iterable)) add(el);
|
||||||
|
}
|
||||||
@Native public SetLib(Context ctx, Object iterable) {
|
}
|
||||||
for (var el : Values.fromJSIterator(ctx, iterable)) add(el);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,97 +1,108 @@
|
|||||||
package me.topchetoeu.jscript.mapping;
|
package me.topchetoeu.jscript.mapping;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.ArrayList;
|
||||||
import java.util.Map;
|
import java.util.List;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Filename;
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.json.JSON;
|
import me.topchetoeu.jscript.json.JSON;
|
||||||
|
|
||||||
public class SourceMap implements LocationMap {
|
public class SourceMap {
|
||||||
private final TreeMap<Location, Location> orgToSrc = new TreeMap<>();
|
private final TreeMap<Long, Long> origToComp = new TreeMap<>();
|
||||||
private final TreeMap<Location, Location> srcToOrg = new TreeMap<>();
|
private final TreeMap<Long, Long> compToOrig = new TreeMap<>();
|
||||||
|
|
||||||
public Location toSource(Location loc) {
|
public Location toCompiled(Location loc) { return convert(loc, origToComp); }
|
||||||
return convert(loc, orgToSrc);
|
public Location toOriginal(Location loc) { return convert(loc, compToOrig); }
|
||||||
|
|
||||||
|
private void add(long orig, long comp) {
|
||||||
|
var a = origToComp.remove(orig);
|
||||||
|
var b = compToOrig.remove(comp);
|
||||||
|
|
||||||
|
if (b != null) origToComp.remove(b);
|
||||||
|
if (a != null) compToOrig.remove(a);
|
||||||
|
|
||||||
|
origToComp.put(orig, comp);
|
||||||
|
compToOrig.put(comp, orig);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Location toOriginal(Location loc) {
|
public SourceMap apply(SourceMap map) {
|
||||||
return convert(loc, srcToOrg);
|
var res = new SourceMap();
|
||||||
}
|
|
||||||
|
|
||||||
public static Location convert(Location loc, TreeMap<Location, Location> map) {
|
for (var el : new ArrayList<>(origToComp.entrySet())) {
|
||||||
if (map.containsKey(loc)) return loc;
|
var mapped = convert(el.getValue(), map.origToComp);
|
||||||
|
add(el.getKey(), mapped);
|
||||||
var srcA = map.floorKey(loc);
|
|
||||||
return srcA == null ? loc : srcA;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void chain(LocationMap map) {
|
|
||||||
for (var key : orgToSrc.keySet()) {
|
|
||||||
orgToSrc.put(key, map.toSource(key));
|
|
||||||
}
|
}
|
||||||
for (var key : srcToOrg.keySet()) {
|
for (var el : new ArrayList<>(compToOrig.entrySet())) {
|
||||||
srcToOrg.put(map.toOriginal(key), key);
|
var mapped = convert(el.getKey(), map.compToOrig);
|
||||||
|
add(el.getValue(), mapped);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SourceMap clone() {
|
public SourceMap clone() {
|
||||||
var res = new SourceMap();
|
var res = new SourceMap();
|
||||||
res.orgToSrc.putAll(this.orgToSrc);
|
res.origToComp.putAll(this.origToComp);
|
||||||
res.srcToOrg.putAll(this.srcToOrg);
|
res.compToOrig.putAll(this.compToOrig);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void split(Map<Filename, TreeMap<Location, Location>> maps) {
|
|
||||||
for (var el : orgToSrc.entrySet()) {
|
|
||||||
var map = maps.get(el.getKey().filename());
|
|
||||||
if (map == null) maps.put(el.getKey().filename(), map = new TreeMap<>());
|
|
||||||
map.put(el.getKey(), el.getValue());
|
|
||||||
|
|
||||||
map = maps.get(el.getValue().filename());
|
|
||||||
if (map == null) maps.put(el.getValue().filename(), map = new TreeMap<>());
|
|
||||||
map.put(el.getValue(), el.getKey());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SourceMap parse(String raw) {
|
public static SourceMap parse(String raw) {
|
||||||
|
var mapping = VLQ.decodeMapping(raw);
|
||||||
var res = new SourceMap();
|
var res = new SourceMap();
|
||||||
var json = JSON.parse(null, raw).map();
|
|
||||||
var sources = json.list("sources").stream().map(v -> v.string()).toArray(String[]::new);
|
|
||||||
var dstFilename = Filename.parse(json.string("file"));
|
|
||||||
var mapping = VLQ.decodeMapping(json.string("mappings"));
|
|
||||||
|
|
||||||
var srcI = 0;
|
var compRow = 0l;
|
||||||
var srcRow = 0;
|
var compCol = 0l;
|
||||||
var srcCol = 0;
|
|
||||||
|
|
||||||
for (var dstRow = 0; dstRow < mapping.length; dstRow++) {
|
for (var origRow = 0; origRow < mapping.length; origRow++) {
|
||||||
var dstCol = 0;
|
var origCol = 0;
|
||||||
|
|
||||||
for (var rawSeg : mapping[dstRow]) {
|
for (var rawSeg : mapping[origRow]) {
|
||||||
dstCol += rawSeg.length > 0 ? rawSeg[0] : 0;
|
if (rawSeg.length > 1 && rawSeg[1] != 0) throw new IllegalArgumentException("Source mapping is to more than one files.");
|
||||||
srcI += rawSeg.length > 1 ? rawSeg[1] : 0;
|
origCol += rawSeg.length > 0 ? rawSeg[0] : 0;
|
||||||
srcRow += rawSeg.length > 2 ? rawSeg[2] : 0;
|
compRow += rawSeg.length > 2 ? rawSeg[2] : 0;
|
||||||
srcCol += rawSeg.length > 3 ? rawSeg[3] : 0;
|
compCol += rawSeg.length > 3 ? rawSeg[3] : 0;
|
||||||
|
|
||||||
var src = new Location(srcRow + 1, srcCol + 1, Filename.parse(sources[srcI]));
|
var compPacked = ((long)compRow << 32) | compCol;
|
||||||
var dst = new Location(dstRow + 1, dstCol + 1, dstFilename);
|
var origPacked = ((long)origRow << 32) | origCol;
|
||||||
|
|
||||||
res.orgToSrc.put(src, dst);
|
res.add(origPacked, compPacked);
|
||||||
res.srcToOrg.put(dst, src);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
public static List<String> getSources(String raw) {
|
||||||
|
var json = JSON.parse(null, raw).map();
|
||||||
|
return json
|
||||||
|
.list("sourcesContent")
|
||||||
|
.stream()
|
||||||
|
.map(v -> v.string())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
public static SourceMap chain(SourceMap ...maps) {
|
public static SourceMap chain(SourceMap ...maps) {
|
||||||
if (maps.length == 0) return null;
|
if (maps.length == 0) return null;
|
||||||
var res = maps[0];
|
var res = maps[0];
|
||||||
|
|
||||||
for (var i = 1; i < maps.length; i++) res.chain(maps[i]);
|
for (var i = 1; i < maps.length; i++) res = res.apply(maps[i]);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Long convert(long packed, TreeMap<Long, Long> map) {
|
||||||
|
if (map.containsKey(packed)) return map.get(packed);
|
||||||
|
var key = map.floorKey(packed);
|
||||||
|
if (key == null) return null;
|
||||||
|
else return map.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Location convert(Location loc, TreeMap<Long, Long> map) {
|
||||||
|
var packed = ((loc.line() - 1l) << 32) | (loc.start() - 1);
|
||||||
|
var resPacked = convert(packed, map);
|
||||||
|
|
||||||
|
if (resPacked == null) return null;
|
||||||
|
else return new Location((int)(resPacked >> 32) + 1, (int)(resPacked & 0xFFFF) + 1, loc.filename());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,8 +17,7 @@ import me.topchetoeu.jscript.compilation.control.SwitchStatement.SwitchCase;
|
|||||||
import me.topchetoeu.jscript.compilation.values.*;
|
import me.topchetoeu.jscript.compilation.values.*;
|
||||||
import me.topchetoeu.jscript.engine.Environment;
|
import me.topchetoeu.jscript.engine.Environment;
|
||||||
import me.topchetoeu.jscript.engine.Operation;
|
import me.topchetoeu.jscript.engine.Operation;
|
||||||
import me.topchetoeu.jscript.engine.scope.ValueVariable;
|
import me.topchetoeu.jscript.engine.scope.LocalScopeRecord;
|
||||||
import me.topchetoeu.jscript.engine.values.CodeFunction;
|
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
import me.topchetoeu.jscript.exceptions.SyntaxException;
|
import me.topchetoeu.jscript.exceptions.SyntaxException;
|
||||||
import me.topchetoeu.jscript.parsing.ParseRes.State;
|
import me.topchetoeu.jscript.parsing.ParseRes.State;
|
||||||
@ -806,9 +805,11 @@ public class Parsing {
|
|||||||
if (!res.isSuccess()) return ParseRes.error(loc, "Expected a compound statement for property accessor.", res);
|
if (!res.isSuccess()) return ParseRes.error(loc, "Expected a compound statement for property accessor.", res);
|
||||||
n += res.n;
|
n += res.n;
|
||||||
|
|
||||||
|
var end = getLoc(filename, tokens, i + n - 1);
|
||||||
|
|
||||||
return ParseRes.res(new ObjProp(
|
return ParseRes.res(new ObjProp(
|
||||||
name, access,
|
name, access,
|
||||||
new FunctionStatement(loc, access + " " + name.toString(), argsRes.result.toArray(String[]::new), false, res.result)
|
new FunctionStatement(loc, end, access + " " + name.toString(), argsRes.result.toArray(String[]::new), false, res.result)
|
||||||
), n);
|
), n);
|
||||||
}
|
}
|
||||||
public static ParseRes<ObjectStatement> parseObject(Filename filename, List<Token> tokens, int i) {
|
public static ParseRes<ObjectStatement> parseObject(Filename filename, List<Token> tokens, int i) {
|
||||||
@ -868,7 +869,7 @@ public class Parsing {
|
|||||||
|
|
||||||
return ParseRes.res(new ObjectStatement(loc, values, getters, setters), n);
|
return ParseRes.res(new ObjectStatement(loc, values, getters, setters), n);
|
||||||
}
|
}
|
||||||
public static ParseRes<NewStatement> parseNew(Filename filename, List<Token> tokens, int i) {
|
public static ParseRes<CallStatement> parseNew(Filename filename, List<Token> tokens, int i) {
|
||||||
var loc = getLoc(filename, tokens, i);
|
var loc = getLoc(filename, tokens, i);
|
||||||
var n = 0;
|
var n = 0;
|
||||||
if (!isIdentifier(tokens, i + n++, "new")) return ParseRes.failed();
|
if (!isIdentifier(tokens, i + n++, "new")) return ParseRes.failed();
|
||||||
@ -879,10 +880,10 @@ public class Parsing {
|
|||||||
var callRes = parseCall(filename, tokens, i + n, valRes.result, 0);
|
var callRes = parseCall(filename, tokens, i + n, valRes.result, 0);
|
||||||
n += callRes.n;
|
n += callRes.n;
|
||||||
if (callRes.isError()) return callRes.transform();
|
if (callRes.isError()) return callRes.transform();
|
||||||
else if (callRes.isFailed()) return ParseRes.res(new NewStatement(loc, valRes.result), n);
|
else if (callRes.isFailed()) return ParseRes.res(new CallStatement(loc, false, valRes.result), n);
|
||||||
var call = (CallStatement)callRes.result;
|
var call = (CallStatement)callRes.result;
|
||||||
|
|
||||||
return ParseRes.res(new NewStatement(loc, call.func, call.args), n);
|
return ParseRes.res(new CallStatement(loc, true, call.func, call.args), n);
|
||||||
}
|
}
|
||||||
public static ParseRes<TypeofStatement> parseTypeof(Filename filename, List<Token> tokens, int i) {
|
public static ParseRes<TypeofStatement> parseTypeof(Filename filename, List<Token> tokens, int i) {
|
||||||
var loc = getLoc(filename, tokens, i);
|
var loc = getLoc(filename, tokens, i);
|
||||||
@ -895,7 +896,7 @@ public class Parsing {
|
|||||||
|
|
||||||
return ParseRes.res(new TypeofStatement(loc, valRes.result), n);
|
return ParseRes.res(new TypeofStatement(loc, valRes.result), n);
|
||||||
}
|
}
|
||||||
public static ParseRes<VoidStatement> parseVoid(Filename filename, List<Token> tokens, int i) {
|
public static ParseRes<DiscardStatement> parseVoid(Filename filename, List<Token> tokens, int i) {
|
||||||
var loc = getLoc(filename, tokens, i);
|
var loc = getLoc(filename, tokens, i);
|
||||||
var n = 0;
|
var n = 0;
|
||||||
if (!isIdentifier(tokens, i + n++, "void")) return ParseRes.failed();
|
if (!isIdentifier(tokens, i + n++, "void")) return ParseRes.failed();
|
||||||
@ -904,7 +905,7 @@ public class Parsing {
|
|||||||
if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value after 'void' keyword.", valRes);
|
if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value after 'void' keyword.", valRes);
|
||||||
n += valRes.n;
|
n += valRes.n;
|
||||||
|
|
||||||
return ParseRes.res(new VoidStatement(loc, valRes.result), n);
|
return ParseRes.res(new DiscardStatement(loc, valRes.result), n);
|
||||||
}
|
}
|
||||||
public static ParseRes<? extends Statement> parseDelete(Filename filename, List<Token> tokens, int i) {
|
public static ParseRes<? extends Statement> parseDelete(Filename filename, List<Token> tokens, int i) {
|
||||||
var loc = getLoc(filename, tokens, i);
|
var loc = getLoc(filename, tokens, i);
|
||||||
@ -965,8 +966,9 @@ public class Parsing {
|
|||||||
|
|
||||||
var res = parseCompound(filename, tokens, i + n);
|
var res = parseCompound(filename, tokens, i + n);
|
||||||
n += res.n;
|
n += res.n;
|
||||||
|
var end = getLoc(filename, tokens, i + n - 1);
|
||||||
|
|
||||||
if (res.isSuccess()) return ParseRes.res(new FunctionStatement(loc, name, args.toArray(String[]::new), statement, res.result), n);
|
if (res.isSuccess()) return ParseRes.res(new FunctionStatement(loc, end, name, args.toArray(String[]::new), statement, res.result), n);
|
||||||
else return ParseRes.error(loc, "Expected a compound statement for function.", res);
|
else return ParseRes.error(loc, "Expected a compound statement for function.", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1186,7 +1188,7 @@ public class Parsing {
|
|||||||
else return ParseRes.failed();
|
else return ParseRes.failed();
|
||||||
}
|
}
|
||||||
|
|
||||||
return ParseRes.res(new CallStatement(loc, prev, args.toArray(Statement[]::new)), n);
|
return ParseRes.res(new CallStatement(loc, false, prev, args.toArray(Statement[]::new)), n);
|
||||||
}
|
}
|
||||||
public static ParseRes<ChangeStatement> parsePostfixChange(Filename filename, List<Token> tokens, int i, Statement prev, int precedence) {
|
public static ParseRes<ChangeStatement> parsePostfixChange(Filename filename, List<Token> tokens, int i, Statement prev, int precedence) {
|
||||||
var loc = getLoc(filename, tokens, i);
|
var loc = getLoc(filename, tokens, i);
|
||||||
@ -1232,7 +1234,7 @@ public class Parsing {
|
|||||||
|
|
||||||
return ParseRes.res(new OperationStatement(loc, Operation.IN, prev, valRes.result), n);
|
return ParseRes.res(new OperationStatement(loc, Operation.IN, prev, valRes.result), n);
|
||||||
}
|
}
|
||||||
public static ParseRes<CommaStatement> parseComma(Filename filename, List<Token> tokens, int i, Statement prev, int precedence) {
|
public static ParseRes<CompoundStatement> parseComma(Filename filename, List<Token> tokens, int i, Statement prev, int precedence) {
|
||||||
var loc = getLoc(filename, tokens, i);
|
var loc = getLoc(filename, tokens, i);
|
||||||
var n = 0;
|
var n = 0;
|
||||||
|
|
||||||
@ -1243,7 +1245,7 @@ public class Parsing {
|
|||||||
if (!res.isSuccess()) return ParseRes.error(loc, "Expected a value after the comma.", res);
|
if (!res.isSuccess()) return ParseRes.error(loc, "Expected a value after the comma.", res);
|
||||||
n += res.n;
|
n += res.n;
|
||||||
|
|
||||||
return ParseRes.res(new CommaStatement(loc, prev, res.result), n);
|
return ParseRes.res(new CompoundStatement(loc, false, prev, res.result), n);
|
||||||
}
|
}
|
||||||
public static ParseRes<IfStatement> parseTernary(Filename filename, List<Token> tokens, int i, Statement prev, int precedence) {
|
public static ParseRes<IfStatement> parseTernary(Filename filename, List<Token> tokens, int i, Statement prev, int precedence) {
|
||||||
var loc = getLoc(filename, tokens, i);
|
var loc = getLoc(filename, tokens, i);
|
||||||
@ -1509,7 +1511,7 @@ public class Parsing {
|
|||||||
statements.add(res.result);
|
statements.add(res.result);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ParseRes.res(new CompoundStatement(loc, statements.toArray(Statement[]::new)).setEnd(getLoc(filename, tokens, i + n - 1)), n);
|
return ParseRes.res(new CompoundStatement(loc, true, statements.toArray(Statement[]::new)).setEnd(getLoc(filename, tokens, i + n - 1)), n);
|
||||||
}
|
}
|
||||||
public static ParseRes<String> parseLabel(List<Token> tokens, int i) {
|
public static ParseRes<String> parseLabel(List<Token> tokens, int i) {
|
||||||
int n = 0;
|
int n = 0;
|
||||||
@ -1690,7 +1692,7 @@ public class Parsing {
|
|||||||
|
|
||||||
if (isOperator(tokens, i + n, Operator.SEMICOLON)) {
|
if (isOperator(tokens, i + n, Operator.SEMICOLON)) {
|
||||||
n++;
|
n++;
|
||||||
decl = new CompoundStatement(loc);
|
decl = new CompoundStatement(loc, false);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var declRes = ParseRes.any(
|
var declRes = ParseRes.any(
|
||||||
@ -1716,7 +1718,7 @@ public class Parsing {
|
|||||||
|
|
||||||
if (isOperator(tokens, i + n, Operator.PAREN_CLOSE)) {
|
if (isOperator(tokens, i + n, Operator.PAREN_CLOSE)) {
|
||||||
n++;
|
n++;
|
||||||
inc = new CompoundStatement(loc);
|
inc = new CompoundStatement(loc, false);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var incRes = parseValue(filename, tokens, i + n, 0);
|
var incRes = parseValue(filename, tokens, i + n, 0);
|
||||||
@ -1829,7 +1831,7 @@ public class Parsing {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static ParseRes<? extends Statement> parseStatement(Filename filename, List<Token> tokens, int i) {
|
public static ParseRes<? extends Statement> parseStatement(Filename filename, List<Token> tokens, int i) {
|
||||||
if (isOperator(tokens, i, Operator.SEMICOLON)) return ParseRes.res(new CompoundStatement(getLoc(filename, tokens, i)), 1);
|
if (isOperator(tokens, i, Operator.SEMICOLON)) return ParseRes.res(new CompoundStatement(getLoc(filename, tokens, i), false), 1);
|
||||||
if (isIdentifier(tokens, i, "with")) return ParseRes.error(getLoc(filename, tokens, i), "'with' statements are not allowed.");
|
if (isIdentifier(tokens, i, "with")) return ParseRes.error(getLoc(filename, tokens, i), "'with' statements are not allowed.");
|
||||||
return ParseRes.any(
|
return ParseRes.any(
|
||||||
parseVariableDeclare(filename, tokens, i),
|
parseVariableDeclare(filename, tokens, i),
|
||||||
@ -1872,38 +1874,30 @@ public class Parsing {
|
|||||||
return list.toArray(Statement[]::new);
|
return list.toArray(Statement[]::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CodeFunction compile(HashMap<Long, FunctionBody> funcs, TreeSet<Location> breakpoints, Environment environment, Statement ...statements) {
|
public static CompileTarget compile(Environment environment, Statement ...statements) {
|
||||||
var target = environment.global.globalChild();
|
var subscope = new LocalScopeRecord();
|
||||||
var subscope = target.child();
|
var target = new CompileTarget(new HashMap<>(), new TreeSet<>());
|
||||||
var res = new CompileTarget(funcs, breakpoints);
|
var stm = new CompoundStatement(null, true, statements);
|
||||||
var body = new CompoundStatement(null, statements);
|
|
||||||
if (body instanceof CompoundStatement) body = (CompoundStatement)body;
|
|
||||||
else body = new CompoundStatement(null, new Statement[] { body });
|
|
||||||
|
|
||||||
subscope.define("this");
|
subscope.define("this");
|
||||||
subscope.define("arguments");
|
subscope.define("arguments");
|
||||||
|
|
||||||
body.declare(target);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
body.compile(res, subscope, true);
|
stm.compile(target, subscope, true);
|
||||||
FunctionStatement.checkBreakAndCont(res, 0);
|
FunctionStatement.checkBreakAndCont(target, 0);
|
||||||
}
|
}
|
||||||
catch (SyntaxException e) {
|
catch (SyntaxException e) {
|
||||||
res.target.clear();
|
target.target.clear();
|
||||||
res.add(Instruction.throwSyntax(e.loc, e));
|
target.add(Instruction.throwSyntax(e.loc, e));
|
||||||
}
|
}
|
||||||
|
|
||||||
res.add(Instruction.ret(body.loc()));
|
target.add(Instruction.ret(stm.loc()));
|
||||||
|
target.functions.put(0l, new FunctionBody(subscope.localsCount(), 0, target.array(), subscope.captures(), subscope.locals()));
|
||||||
|
|
||||||
return new CodeFunction(environment, "", new FunctionBody(subscope.localsCount(), 0, res.array(), subscope.captures(), subscope.locals()), new ValueVariable[0]);
|
return target;
|
||||||
}
|
}
|
||||||
public static CodeFunction compile(HashMap<Long, FunctionBody> funcs, TreeSet<Location> breakpoints, Environment environment, Filename filename, String raw) {
|
public static CompileTarget compile(Environment environment, Filename filename, String raw) {
|
||||||
try {
|
try { return compile(environment, parse(filename, raw)); }
|
||||||
return compile(funcs, breakpoints, environment, parse(filename, raw));
|
catch (SyntaxException e) { return compile(environment, new ThrowSyntaxStatement(e)); }
|
||||||
}
|
|
||||||
catch (SyntaxException e) {
|
|
||||||
return new CodeFunction(environment, null, new FunctionBody(Instruction.throwSyntax(e.loc, e)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
80
test.txt
Normal file
80
test.txt
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
const { spawn } = require('child_process');
|
||||||
|
const fs = require('fs/promises');
|
||||||
|
const pt = require('path');
|
||||||
|
const { argv, exit } = require('process');
|
||||||
|
|
||||||
|
const conf = {
|
||||||
|
name: "java-jscript",
|
||||||
|
author: "TopchetoEU",
|
||||||
|
javahome: "",
|
||||||
|
version: argv[3]
|
||||||
|
};
|
||||||
|
|
||||||
|
if (conf.version.startsWith('refs/tags/')) conf.version = conf.version.substring(10);
|
||||||
|
if (conf.version.startsWith('v')) conf.version = conf.version.substring(1);
|
||||||
|
|
||||||
|
async function* find(src, dst, wildcard) {
|
||||||
|
const stat = await fs.stat(src);
|
||||||
|
|
||||||
|
if (stat.isDirectory()) {
|
||||||
|
for (const el of await fs.readdir(src)) {
|
||||||
|
for await (const res of find(pt.join(src, el), dst ? pt.join(dst, el) : undefined, wildcard)) yield res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (stat.isFile() && wildcard(src)) yield dst ? { src, dst } : src;
|
||||||
|
}
|
||||||
|
async function copy(src, dst, wildcard) {
|
||||||
|
const promises = [];
|
||||||
|
|
||||||
|
for await (const el of find(src, dst, wildcard)) {
|
||||||
|
promises.push((async () => {
|
||||||
|
await fs.mkdir(pt.dirname(el.dst), { recursive: true });
|
||||||
|
await fs.copyFile(el.src, el.dst);
|
||||||
|
})());
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(promises);
|
||||||
|
}
|
||||||
|
|
||||||
|
function run(cmd, ...args) {
|
||||||
|
return new Promise((res, rej) => {
|
||||||
|
const proc = spawn(cmd, args, { stdio: 'inherit' });
|
||||||
|
proc.once('exit', code => {
|
||||||
|
if (code === 0) res(code);
|
||||||
|
else rej(new Error(`Process ${cmd} exited with code ${code}.`));
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function compileJava() {
|
||||||
|
try {
|
||||||
|
await fs.writeFile('Metadata.java', (await fs.readFile('src/me/topchetoeu/jscript/Metadata.java')).toString()
|
||||||
|
.replace('${VERSION}', conf.version)
|
||||||
|
.replace('${NAME}', conf.name)
|
||||||
|
.replace('${AUTHOR}', conf.author)
|
||||||
|
);
|
||||||
|
const args = ['--release', '11', ];
|
||||||
|
if (argv[2] === 'debug') args.push('-g');
|
||||||
|
args.push('-d', 'dst/classes', 'Metadata.java');
|
||||||
|
|
||||||
|
for await (const path of find('src', undefined, v => v.endsWith('.java') && !v.endsWith('Metadata.java'))) args.push(path);
|
||||||
|
await run(conf.javahome + 'javac', ...args);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
await fs.rm('Metadata.java');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
try { await fs.rm('dst', { recursive: true }); } catch {}
|
||||||
|
await copy('src', 'dst/classes', v => !v.endsWith('.java'));
|
||||||
|
await compileJava();
|
||||||
|
await run('jar', '-c', '-f', 'dst/jscript.jar', '-e', 'me.topchetoeu.jscript.Main', '-C', 'dst/classes', '.');
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
if (argv[2] === 'debug') throw e;
|
||||||
|
console.log(e.toString());
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
})();
|
Loading…
Reference in New Issue
Block a user