Compare commits

...

40 Commits

Author SHA1 Message Date
af35d7f20b fix: oops 2024-01-10 11:25:29 +02:00
cfa0e001b9 refactor: split up code in 4 modules 2024-01-10 11:21:49 +02:00
c10d071346 feat: some API improvements 2024-01-10 11:21:35 +02:00
89eea7d62b refactor: remove old useless exceptions 2024-01-06 19:51:48 +02:00
18d22a1282 Merge pull request #12 from TopchetoEU/TopchetoEU/cleanup
Major codebase cleanup
2024-01-06 18:28:11 +02:00
72a0d39d0b fix: make java 11 compatible 2024-01-06 18:27:36 +02:00
d8585a20bf refactor: don't require ctx in frame.push 2024-01-06 18:23:34 +02:00
e4c9a8756e fix: debugger hanging sometimes 2024-01-06 18:23:20 +02:00
c6e6425c7e fix: bring back ts minification 2024-01-06 17:53:42 +02:00
292ca64cb9 fix: wrong behavior in Number.toString (which somehow broke typescript) 2024-01-06 17:50:06 +02:00
4572db5c46 feat: some array tests 2024-01-06 17:49:36 +02:00
0251c4689d fix: use Values to access members in ObjectLib, instead of direct access 2024-01-06 17:49:27 +02:00
3173919b49 fix: implement proper parseInt logic 2024-01-06 17:48:35 +02:00
45f133c6b0 fix: use Values.call instead of direct calling 2024-01-06 17:48:10 +02:00
34276d720c fix: remove sparse call arguments 2024-01-06 17:47:38 +02:00
2c634778c3 fix: report proper function name in String.length errors 2024-01-06 17:47:07 +02:00
4aa757e625 fix: Function.bind now passess this argument, instead of the function itself 2024-01-06 17:46:39 +02:00
918f2623cd fix: small issue with sparse arrays 2024-01-06 17:46:13 +02:00
a321fc14bc fix: wrong signature of Map.forEach 2024-01-06 17:45:56 +02:00
07a6f18b16 refactor: some spring cleaning in array lib, fix small issue with join 2024-01-06 17:45:52 +02:00
5f4011aa0c refactor: move NO_RETURN to Values, remove some casters from Values 2024-01-06 17:45:44 +02:00
71f735b812 fix: some more libs fixes 2024-01-04 13:58:04 +02:00
e575b3287e fix: try-catch-finally fix #457846982 2024-01-04 13:57:41 +02:00
4fa5f5a815 feat: use new wrapper API in libs 2024-01-04 10:02:14 +02:00
a61c6a494e fix: some Argument and Engine API improvements, 2024-01-04 10:02:01 +02:00
978ee8db79 feat: make better native wrapper API 2023-12-28 16:55:57 +02:00
e372941e99 refactor: generalize Reading class 2023-12-27 20:18:41 +02:00
c36a0db860 refactor: remove more dead code 2023-12-27 20:18:23 +02:00
d6ee59363f refactor: remove unneeded event system 2023-12-27 20:10:11 +02:00
d5fd6e650e fix: clean up debugger API 2023-12-27 20:02:45 +02:00
c0b895e00a feat: greatly improve Context API 2023-12-27 14:22:18 +02:00
9ea5cd9277 refactor: remove old Data API 2023-12-27 14:21:52 +02:00
aaf9a6fa45 fix: pass environment to compiler via simple environment wrapper 2023-12-27 13:59:19 +02:00
579f09c837 fix: pass arguments to regex constructor in LOAD_REGEX 2023-12-27 13:28:17 +02:00
3343262e72 fix: main now uses new env API 2023-12-27 13:16:21 +02:00
153a1a9a49 feat: readd permissions API via new env API 2023-12-27 13:16:12 +02:00
bf38587271 feat: readd module API via env API 2023-12-27 13:15:46 +02:00
21534efd60 feat: readd FS API via new env API 2023-12-27 13:14:46 +02:00
802f2f3f52 fix: access env via context 2023-12-27 13:14:09 +02:00
38acc20a6f refactor: greatly improve Environment API 2023-12-26 17:12:20 +02:00
216 changed files with 4931 additions and 4265 deletions

5
.gitignore vendored
View File

@@ -2,12 +2,15 @@
!/src
!/src/**/*
/src/assets/js/ts.js
!/doc
!/doc/**/*
!/tests
!/tests/**/*
!/.github
!/.github/**/*

View File

@@ -11,7 +11,7 @@ JScript is an engine, capable of running EcmaScript 5, written entirely in Java.
The following is going to execute a simple javascript statement:
```java
var engine = new Engine(false);
var engine = new Engine();
// Initialize a standard environment, with implementations of most basic standard libraries (Object, Array, Symbol, etc.)
var env = Internals.apply(new Environment());
@@ -26,4 +26,4 @@ System.out.println(awaitable.await());
## NOTE:
To setup the typescript bundle in your sources, run `node build.js init-ts`. This will download the latest version of typescript, minify it, and add it to your src/assets folder. If you are going to work with the `node build.js debug|release` command, this is not a necessary step.
To setup the typescript bundle in your sources, run `node build.js init-ts`. This will download the latest version of typescript, minify it, and add it to your src folder. If you are going to work with the `node build.js debug|release` command, this is not a necessary step.

View File

@@ -77,6 +77,7 @@ async function downloadTypescript(outFile) {
console.log('Minifying typescript...');
const minified = minify((await fs.readFile('tmp/typescript-es5.js')).toString());
// const minified = { code: (await fs.readFile('tmp/typescript-es5.js')).toString() };
if (minified.error) throw minified.error;
// Patch unsupported regex syntax
@@ -117,7 +118,7 @@ The following is a minified version of the unmodified Typescript 5.2
}
async function compileJava(conf) {
try {
await fs.writeFile('Metadata.java', (await fs.readFile('src/me/topchetoeu/jscript/Metadata.java')).toString()
await fs.writeFile('Metadata.java', (await fs.readFile('src/me/topchetoeu/jscript/common/Metadata.java')).toString()
.replace('${VERSION}', conf.version)
.replace('${NAME}', conf.name)
.replace('${AUTHOR}', conf.author)
@@ -135,11 +136,22 @@ async function compileJava(conf) {
await fs.rm('Metadata.java');
}
}
async function jar(conf, project, mainClass) {
const args = [
'jar', '-c',
'-f', `dst/${project}-v${conf.version}.jar`,
];
if (mainClass) args.push('-e', mainClass);
args.push('-C', 'dst/classes', project.replaceAll('.', '/'));
console.log(args.join(' '));
await run(true, ...args);
}
(async () => {
try {
if (argv[2] === 'init-ts') {
await downloadTypescript('src/assets/js/ts.js');
await downloadTypescript('src/me/topchetoeu/jscript/utils/assets/js/ts.js');
}
else {
const conf = {
@@ -155,12 +167,21 @@ async function compileJava(conf) {
try { await fs.rm('dst', { recursive: true }); } catch {}
await Promise.all([
downloadTypescript('dst/classes/assets/js/ts.js'),
copy('src', 'dst/classes', v => !v.endsWith('.java')),
(async () => {
await copy('src', 'dst/classes', v => !v.endsWith('.java'));
await downloadTypescript('dst/classes/me/topchetoeu/jscript/utils/assets/js/ts.js');
})(),
compileJava(conf),
]);
await run(true, 'jar', '-c', '-f', 'dst/jscript.jar', '-e', 'me.topchetoeu.jscript.Main', '-C', 'dst/classes', '.');
await Promise.all([
jar(conf, 'me.topchetoeu.jscript.common'),
jar(conf, 'me.topchetoeu.jscript.core'),
jar(conf, 'me.topchetoeu.jscript.lib'),
jar(conf, 'me.topchetoeu.jscript.utils'),
jar(conf, 'me.topchetoeu.jscript', 'me.topchetoeu.jscript.utils.JScriptRepl'),
]);
console.log('Done!');
}
}

View File

@@ -1,6 +0,0 @@
package me.topchetoeu.jscript;
public interface MessageReceiver {
void sendMessage(String msg);
void sendError(String msg);
}

View File

@@ -1,4 +1,4 @@
package me.topchetoeu.jscript;
package me.topchetoeu.jscript.common;
public class Buffer {
private byte[] data;

View File

@@ -1,4 +1,4 @@
package me.topchetoeu.jscript;
package me.topchetoeu.jscript.common;
import java.io.File;
import java.nio.file.Path;

View File

@@ -1,4 +1,4 @@
package me.topchetoeu.jscript;
package me.topchetoeu.jscript.common;
public class Location implements Comparable<Location> {
public static final Location INTERNAL = new Location(0, 0, new Filename("jscript", "native"));

View File

@@ -1,4 +1,4 @@
package me.topchetoeu.jscript;
package me.topchetoeu.jscript.common;
public class Metadata {
private static final String VERSION = "${VERSION}";

View File

@@ -1,25 +1,31 @@
package me.topchetoeu.jscript;
package me.topchetoeu.jscript.common;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import me.topchetoeu.jscript.exceptions.UncheckedException;
import java.io.UncheckedIOException;
public class Reading {
private static final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
public static synchronized String read() throws IOException {
public static synchronized String readline() throws IOException {
return reader.readLine();
}
/**
* Reads the given stream to a string
* @param in
* @return
*/
public static String streamToString(InputStream in) {
try { return new String(in.readAllBytes()); }
catch (Throwable e) { throw new UncheckedException(e); }
try {
return new String(in.readAllBytes());
}
catch (IOException e) { throw new UncheckedIOException(e); }
}
public static InputStream resourceToStream(String name) {
return Reading.class.getResourceAsStream("/assets/" + name);
return Reading.class.getResourceAsStream("/" + name);
}
public static String resourceToString(String name) {
return streamToString(resourceToStream(name));

View File

@@ -0,0 +1,5 @@
package me.topchetoeu.jscript.common;
public interface ResultRunnable<T> {
T run();
}

View File

@@ -0,0 +1,27 @@
package me.topchetoeu.jscript.common.events;
public interface Awaitable<T> {
public static interface ResultHandler<T> {
public void onResult(T data);
}
public static interface ErrorHandler {
public void onError(RuntimeException error);
}
T await();
default void handle(ResultHandler<T> onResult, ErrorHandler onError) {
var thread = new Thread(() -> {
try {
onResult.onResult(await());
}
catch (RuntimeException e) {
onError.onError(e);
}
}, "Awaiter");
thread.start();
}
default void handle(ResultHandler<T> onResult) {
handle(onResult, err -> {});
}
}

View File

@@ -1,4 +1,4 @@
package me.topchetoeu.jscript.events;
package me.topchetoeu.jscript.common.events;
public class DataNotifier<T> implements Awaitable<T> {
private Notifier notifier = new Notifier();
@@ -11,9 +11,6 @@ public class DataNotifier<T> implements Awaitable<T> {
isErr = true;
notifier.next();
}
public void error(Throwable t) {
error(new RuntimeException(t));
}
public void next(T val) {
this.val = val;
isErr = false;

View File

@@ -1,6 +1,6 @@
package me.topchetoeu.jscript.events;
package me.topchetoeu.jscript.common.events;
import me.topchetoeu.jscript.exceptions.InterruptException;
import me.topchetoeu.jscript.core.exceptions.InterruptException;
public class Notifier {
private boolean ok = false;

View File

@@ -1,20 +1,20 @@
package me.topchetoeu.jscript.json;
package me.topchetoeu.jscript.common.json;
import java.util.HashSet;
import java.util.List;
import java.util.stream.Collectors;
import me.topchetoeu.jscript.Filename;
import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.engine.values.ArrayValue;
import me.topchetoeu.jscript.engine.values.ObjectValue;
import me.topchetoeu.jscript.engine.values.Values;
import me.topchetoeu.jscript.exceptions.EngineException;
import me.topchetoeu.jscript.exceptions.SyntaxException;
import me.topchetoeu.jscript.parsing.Operator;
import me.topchetoeu.jscript.parsing.ParseRes;
import me.topchetoeu.jscript.parsing.Parsing;
import me.topchetoeu.jscript.parsing.Token;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.core.engine.Context;
import me.topchetoeu.jscript.core.engine.values.ArrayValue;
import me.topchetoeu.jscript.core.engine.values.ObjectValue;
import me.topchetoeu.jscript.core.engine.values.Values;
import me.topchetoeu.jscript.core.exceptions.EngineException;
import me.topchetoeu.jscript.core.exceptions.SyntaxException;
import me.topchetoeu.jscript.core.parsing.Operator;
import me.topchetoeu.jscript.core.parsing.ParseRes;
import me.topchetoeu.jscript.core.parsing.Parsing;
import me.topchetoeu.jscript.core.parsing.Token;
public class JSON {
public static Object toJs(JSONElement val) {
@@ -58,8 +58,8 @@ public class JSON {
var res = new JSONMap();
for (var el : ((ObjectValue)val).keys(false)) {
var jsonEl = fromJs(ctx, ((ObjectValue)val).getMember(ctx, el), prev);
for (var el : Values.getMembers(ctx, val, false, false)) {
var jsonEl = fromJs(ctx, Values.getMember(ctx, val, el), prev);
if (jsonEl == null) continue;
if (el instanceof String || el instanceof Number) res.put(el.toString(), jsonEl);
}

View File

@@ -1,4 +1,4 @@
package me.topchetoeu.jscript.json;
package me.topchetoeu.jscript.common.json;
public class JSONElement {
public static enum Type {

View File

@@ -1,4 +1,4 @@
package me.topchetoeu.jscript.json;
package me.topchetoeu.jscript.common.json;
import java.util.ArrayList;
import java.util.Collection;

View File

@@ -1,4 +1,4 @@
package me.topchetoeu.jscript.json;
package me.topchetoeu.jscript.common.json;
import java.util.Collection;
import java.util.HashMap;

View File

@@ -1,7 +1,7 @@
package me.topchetoeu.jscript.compilation;
package me.topchetoeu.jscript.core.compilation;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.engine.Operation;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.core.engine.Operation;
public abstract class AssignableStatement extends Statement {
public abstract Statement toAssign(Statement val, Operation operation);

View File

@@ -1,6 +1,6 @@
package me.topchetoeu.jscript.compilation;
package me.topchetoeu.jscript.core.compilation;
import me.topchetoeu.jscript.engine.values.Values;
import me.topchetoeu.jscript.core.engine.values.Values;
public final class CalculateResult {
public final boolean exists;

View File

@@ -1,14 +1,14 @@
package me.topchetoeu.jscript.compilation;
package me.topchetoeu.jscript.core.compilation;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeSet;
import java.util.Vector;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
import me.topchetoeu.jscript.engine.Environment;
import me.topchetoeu.jscript.engine.values.CodeFunction;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.core.compilation.Instruction.BreakpointType;
import me.topchetoeu.jscript.core.engine.Environment;
import me.topchetoeu.jscript.core.engine.values.CodeFunction;
public class CompileTarget {
public final Vector<Instruction> target = new Vector<>();

View File

@@ -1,12 +1,12 @@
package me.topchetoeu.jscript.compilation;
package me.topchetoeu.jscript.core.compilation;
import java.util.List;
import java.util.Vector;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
import me.topchetoeu.jscript.compilation.values.FunctionStatement;
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.core.compilation.Instruction.BreakpointType;
import me.topchetoeu.jscript.core.compilation.values.FunctionStatement;
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
public class CompoundStatement extends Statement {
public final Statement[] statements;

View File

@@ -1,4 +1,4 @@
package me.topchetoeu.jscript.compilation;
package me.topchetoeu.jscript.core.compilation;
public class FunctionBody {
public final Instruction[] instructions;

View File

@@ -1,8 +1,8 @@
package me.topchetoeu.jscript.compilation;
package me.topchetoeu.jscript.core.compilation;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.engine.Operation;
import me.topchetoeu.jscript.exceptions.SyntaxException;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.core.engine.Operation;
import me.topchetoeu.jscript.core.exceptions.SyntaxException;
public class Instruction {
public static enum Type {

View File

@@ -1,8 +1,8 @@
package me.topchetoeu.jscript.compilation;
package me.topchetoeu.jscript.core.compilation;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.core.compilation.Instruction.BreakpointType;
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
public abstract class Statement {
private Location _loc;

View File

@@ -1,7 +1,7 @@
package me.topchetoeu.jscript.compilation;
package me.topchetoeu.jscript.core.compilation;
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
import me.topchetoeu.jscript.exceptions.SyntaxException;
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
import me.topchetoeu.jscript.core.exceptions.SyntaxException;
public class ThrowSyntaxStatement extends Statement {
public final String name;

View File

@@ -1,11 +1,11 @@
package me.topchetoeu.jscript.compilation;
package me.topchetoeu.jscript.core.compilation;
import java.util.List;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
import me.topchetoeu.jscript.compilation.values.FunctionStatement;
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.core.compilation.Instruction.BreakpointType;
import me.topchetoeu.jscript.core.compilation.values.FunctionStatement;
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
public class VariableDeclareStatement extends Statement {
public static class Pair {

View File

@@ -1,10 +1,10 @@
package me.topchetoeu.jscript.compilation.control;
package me.topchetoeu.jscript.core.compilation.control;
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;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.core.compilation.CompileTarget;
import me.topchetoeu.jscript.core.compilation.Instruction;
import me.topchetoeu.jscript.core.compilation.Statement;
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
public class BreakStatement extends Statement {
public final String label;

View File

@@ -1,10 +1,10 @@
package me.topchetoeu.jscript.compilation.control;
package me.topchetoeu.jscript.core.compilation.control;
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;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.core.compilation.CompileTarget;
import me.topchetoeu.jscript.core.compilation.Instruction;
import me.topchetoeu.jscript.core.compilation.Statement;
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
public class ContinueStatement extends Statement {
public final String label;

View File

@@ -1,10 +1,10 @@
package me.topchetoeu.jscript.compilation.control;
package me.topchetoeu.jscript.core.compilation.control;
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;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.core.compilation.CompileTarget;
import me.topchetoeu.jscript.core.compilation.Instruction;
import me.topchetoeu.jscript.core.compilation.Statement;
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
public class DebugStatement extends Statement {
@Override

View File

@@ -1,10 +1,10 @@
package me.topchetoeu.jscript.compilation.control;
package me.topchetoeu.jscript.core.compilation.control;
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;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.core.compilation.CompileTarget;
import me.topchetoeu.jscript.core.compilation.Instruction;
import me.topchetoeu.jscript.core.compilation.Statement;
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
public class DeleteStatement extends Statement {
public final Statement key;

View File

@@ -1,11 +1,11 @@
package me.topchetoeu.jscript.compilation.control;
package me.topchetoeu.jscript.core.compilation.control;
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.compilation.Instruction.BreakpointType;
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.core.compilation.CompileTarget;
import me.topchetoeu.jscript.core.compilation.Instruction;
import me.topchetoeu.jscript.core.compilation.Statement;
import me.topchetoeu.jscript.core.compilation.Instruction.BreakpointType;
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
public class DoWhileStatement extends Statement {
public final Statement condition, body;

View File

@@ -1,12 +1,12 @@
package me.topchetoeu.jscript.compilation.control;
package me.topchetoeu.jscript.core.compilation.control;
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.compilation.Instruction.BreakpointType;
import me.topchetoeu.jscript.engine.Operation;
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.core.compilation.CompileTarget;
import me.topchetoeu.jscript.core.compilation.Instruction;
import me.topchetoeu.jscript.core.compilation.Statement;
import me.topchetoeu.jscript.core.compilation.Instruction.BreakpointType;
import me.topchetoeu.jscript.core.engine.Operation;
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
public class ForInStatement extends Statement {
public final String varName;

View File

@@ -1,11 +1,11 @@
package me.topchetoeu.jscript.compilation.control;
package me.topchetoeu.jscript.core.compilation.control;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
import me.topchetoeu.jscript.compilation.CompileTarget;
import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.core.compilation.CompileTarget;
import me.topchetoeu.jscript.core.compilation.Instruction;
import me.topchetoeu.jscript.core.compilation.Statement;
import me.topchetoeu.jscript.core.compilation.Instruction.BreakpointType;
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
public class ForStatement extends Statement {
public final Statement declaration, assignment, condition, body;

View File

@@ -1,11 +1,11 @@
package me.topchetoeu.jscript.compilation.control;
package me.topchetoeu.jscript.core.compilation.control;
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.compilation.Instruction.BreakpointType;
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.core.compilation.CompileTarget;
import me.topchetoeu.jscript.core.compilation.Instruction;
import me.topchetoeu.jscript.core.compilation.Statement;
import me.topchetoeu.jscript.core.compilation.Instruction.BreakpointType;
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
public class IfStatement extends Statement {
public final Statement condition, body, elseBody;

View File

@@ -1,10 +1,10 @@
package me.topchetoeu.jscript.compilation.control;
package me.topchetoeu.jscript.core.compilation.control;
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;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.core.compilation.CompileTarget;
import me.topchetoeu.jscript.core.compilation.Instruction;
import me.topchetoeu.jscript.core.compilation.Statement;
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
public class ReturnStatement extends Statement {
public final Statement value;

View File

@@ -1,15 +1,15 @@
package me.topchetoeu.jscript.compilation.control;
package me.topchetoeu.jscript.core.compilation.control;
import java.util.HashMap;
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.compilation.Instruction.BreakpointType;
import me.topchetoeu.jscript.compilation.Instruction.Type;
import me.topchetoeu.jscript.engine.Operation;
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.core.compilation.CompileTarget;
import me.topchetoeu.jscript.core.compilation.Instruction;
import me.topchetoeu.jscript.core.compilation.Statement;
import me.topchetoeu.jscript.core.compilation.Instruction.BreakpointType;
import me.topchetoeu.jscript.core.compilation.Instruction.Type;
import me.topchetoeu.jscript.core.engine.Operation;
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
public class SwitchStatement extends Statement {
public static class SwitchCase {

View File

@@ -1,10 +1,10 @@
package me.topchetoeu.jscript.compilation.control;
package me.topchetoeu.jscript.core.compilation.control;
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;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.core.compilation.CompileTarget;
import me.topchetoeu.jscript.core.compilation.Instruction;
import me.topchetoeu.jscript.core.compilation.Statement;
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
public class ThrowStatement extends Statement {
public final Statement value;

View File

@@ -1,13 +1,13 @@
package me.topchetoeu.jscript.compilation.control;
package me.topchetoeu.jscript.core.compilation.control;
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.compilation.Instruction.BreakpointType;
import me.topchetoeu.jscript.engine.scope.GlobalScope;
import me.topchetoeu.jscript.engine.scope.LocalScopeRecord;
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.core.compilation.CompileTarget;
import me.topchetoeu.jscript.core.compilation.Instruction;
import me.topchetoeu.jscript.core.compilation.Statement;
import me.topchetoeu.jscript.core.compilation.Instruction.BreakpointType;
import me.topchetoeu.jscript.core.engine.scope.GlobalScope;
import me.topchetoeu.jscript.core.engine.scope.LocalScopeRecord;
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
public class TryStatement extends Statement {
public final Statement tryBody;

View File

@@ -1,12 +1,12 @@
package me.topchetoeu.jscript.compilation.control;
package me.topchetoeu.jscript.core.compilation.control;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.CompileTarget;
import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
import me.topchetoeu.jscript.compilation.Instruction.Type;
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.core.compilation.CompileTarget;
import me.topchetoeu.jscript.core.compilation.Instruction;
import me.topchetoeu.jscript.core.compilation.Statement;
import me.topchetoeu.jscript.core.compilation.Instruction.BreakpointType;
import me.topchetoeu.jscript.core.compilation.Instruction.Type;
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
public class WhileStatement extends Statement {
public final Statement condition, body;

View File

@@ -1,10 +1,10 @@
package me.topchetoeu.jscript.compilation.values;
package me.topchetoeu.jscript.core.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;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.core.compilation.CompileTarget;
import me.topchetoeu.jscript.core.compilation.Instruction;
import me.topchetoeu.jscript.core.compilation.Statement;
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
public class ArrayStatement extends Statement {
public final Statement[] statements;

View File

@@ -1,11 +1,11 @@
package me.topchetoeu.jscript.compilation.values;
package me.topchetoeu.jscript.core.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.compilation.Instruction.BreakpointType;
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.core.compilation.CompileTarget;
import me.topchetoeu.jscript.core.compilation.Instruction;
import me.topchetoeu.jscript.core.compilation.Statement;
import me.topchetoeu.jscript.core.compilation.Instruction.BreakpointType;
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
public class CallStatement extends Statement {
public final Statement func;

View File

@@ -1,12 +1,12 @@
package me.topchetoeu.jscript.compilation.values;
package me.topchetoeu.jscript.core.compilation.values;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.AssignableStatement;
import me.topchetoeu.jscript.compilation.CompileTarget;
import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.engine.Operation;
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.core.compilation.AssignableStatement;
import me.topchetoeu.jscript.core.compilation.CompileTarget;
import me.topchetoeu.jscript.core.compilation.Instruction;
import me.topchetoeu.jscript.core.compilation.Statement;
import me.topchetoeu.jscript.core.engine.Operation;
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
public class ChangeStatement extends Statement {
public final AssignableStatement value;

View File

@@ -1,10 +1,10 @@
package me.topchetoeu.jscript.compilation.values;
package me.topchetoeu.jscript.core.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;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.core.compilation.CompileTarget;
import me.topchetoeu.jscript.core.compilation.Instruction;
import me.topchetoeu.jscript.core.compilation.Statement;
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
public class ConstantStatement extends Statement {
public final Object value;

View File

@@ -1,10 +1,10 @@
package me.topchetoeu.jscript.compilation.values;
package me.topchetoeu.jscript.core.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;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.core.compilation.CompileTarget;
import me.topchetoeu.jscript.core.compilation.Instruction;
import me.topchetoeu.jscript.core.compilation.Statement;
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
public class DiscardStatement extends Statement {
public final Statement value;

View File

@@ -1,17 +1,17 @@
package me.topchetoeu.jscript.compilation.values;
package me.topchetoeu.jscript.core.compilation.values;
import java.util.Random;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.CompileTarget;
import me.topchetoeu.jscript.compilation.CompoundStatement;
import me.topchetoeu.jscript.compilation.FunctionBody;
import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
import me.topchetoeu.jscript.compilation.Instruction.Type;
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
import me.topchetoeu.jscript.exceptions.SyntaxException;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.core.compilation.CompileTarget;
import me.topchetoeu.jscript.core.compilation.CompoundStatement;
import me.topchetoeu.jscript.core.compilation.FunctionBody;
import me.topchetoeu.jscript.core.compilation.Instruction;
import me.topchetoeu.jscript.core.compilation.Statement;
import me.topchetoeu.jscript.core.compilation.Instruction.BreakpointType;
import me.topchetoeu.jscript.core.compilation.Instruction.Type;
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
import me.topchetoeu.jscript.core.exceptions.SyntaxException;
public class FunctionStatement extends Statement {
public final CompoundStatement body;

View File

@@ -1,10 +1,10 @@
package me.topchetoeu.jscript.compilation.values;
package me.topchetoeu.jscript.core.compilation.values;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.CompileTarget;
import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.core.compilation.CompileTarget;
import me.topchetoeu.jscript.core.compilation.Instruction;
import me.topchetoeu.jscript.core.compilation.Statement;
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
public class GlobalThisStatement extends Statement {
@Override public boolean pure() { return true; }

View File

@@ -1,12 +1,12 @@
package me.topchetoeu.jscript.compilation.values;
package me.topchetoeu.jscript.core.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.compilation.Instruction.BreakpointType;
import me.topchetoeu.jscript.engine.Operation;
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.core.compilation.CompileTarget;
import me.topchetoeu.jscript.core.compilation.Instruction;
import me.topchetoeu.jscript.core.compilation.Statement;
import me.topchetoeu.jscript.core.compilation.Instruction.BreakpointType;
import me.topchetoeu.jscript.core.engine.Operation;
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
public class IndexAssignStatement extends Statement {
public final Statement object;

View File

@@ -1,13 +1,13 @@
package me.topchetoeu.jscript.compilation.values;
package me.topchetoeu.jscript.core.compilation.values;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.AssignableStatement;
import me.topchetoeu.jscript.compilation.CompileTarget;
import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
import me.topchetoeu.jscript.engine.Operation;
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.core.compilation.AssignableStatement;
import me.topchetoeu.jscript.core.compilation.CompileTarget;
import me.topchetoeu.jscript.core.compilation.Instruction;
import me.topchetoeu.jscript.core.compilation.Statement;
import me.topchetoeu.jscript.core.compilation.Instruction.BreakpointType;
import me.topchetoeu.jscript.core.engine.Operation;
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
public class IndexStatement extends AssignableStatement {
public final Statement object;

View File

@@ -1,11 +1,11 @@
package me.topchetoeu.jscript.compilation.values;
package me.topchetoeu.jscript.core.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;
import me.topchetoeu.jscript.engine.values.Values;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.core.compilation.CompileTarget;
import me.topchetoeu.jscript.core.compilation.Instruction;
import me.topchetoeu.jscript.core.compilation.Statement;
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
import me.topchetoeu.jscript.core.engine.values.Values;
public class LazyAndStatement extends Statement {
public final Statement first, second;

View File

@@ -1,11 +1,11 @@
package me.topchetoeu.jscript.compilation.values;
package me.topchetoeu.jscript.core.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;
import me.topchetoeu.jscript.engine.values.Values;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.core.compilation.CompileTarget;
import me.topchetoeu.jscript.core.compilation.Instruction;
import me.topchetoeu.jscript.core.compilation.Statement;
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
import me.topchetoeu.jscript.core.engine.values.Values;
public class LazyOrStatement extends Statement {
public final Statement first, second;

View File

@@ -1,13 +1,13 @@
package me.topchetoeu.jscript.compilation.values;
package me.topchetoeu.jscript.core.compilation.values;
import java.util.ArrayList;
import java.util.Map;
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;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.core.compilation.CompileTarget;
import me.topchetoeu.jscript.core.compilation.Instruction;
import me.topchetoeu.jscript.core.compilation.Statement;
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
public class ObjectStatement extends Statement {
public final Map<Object, Statement> map;

View File

@@ -1,11 +1,11 @@
package me.topchetoeu.jscript.compilation.values;
package me.topchetoeu.jscript.core.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.Operation;
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.core.compilation.CompileTarget;
import me.topchetoeu.jscript.core.compilation.Instruction;
import me.topchetoeu.jscript.core.compilation.Statement;
import me.topchetoeu.jscript.core.engine.Operation;
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
public class OperationStatement extends Statement {
public final Statement[] args;

View File

@@ -1,10 +1,10 @@
package me.topchetoeu.jscript.compilation.values;
package me.topchetoeu.jscript.core.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;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.core.compilation.CompileTarget;
import me.topchetoeu.jscript.core.compilation.Instruction;
import me.topchetoeu.jscript.core.compilation.Statement;
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
public class RegexStatement extends Statement {
public final String pattern, flags;

View File

@@ -1,10 +1,10 @@
package me.topchetoeu.jscript.compilation.values;
package me.topchetoeu.jscript.core.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;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.core.compilation.CompileTarget;
import me.topchetoeu.jscript.core.compilation.Instruction;
import me.topchetoeu.jscript.core.compilation.Statement;
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
public class TypeofStatement extends Statement {
public final Statement value;

View File

@@ -1,11 +1,11 @@
package me.topchetoeu.jscript.compilation.values;
package me.topchetoeu.jscript.core.compilation.values;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.CompileTarget;
import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.engine.Operation;
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.core.compilation.CompileTarget;
import me.topchetoeu.jscript.core.compilation.Instruction;
import me.topchetoeu.jscript.core.compilation.Statement;
import me.topchetoeu.jscript.core.engine.Operation;
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
public class VariableAssignStatement extends Statement {
public final String name;

View File

@@ -1,10 +1,10 @@
package me.topchetoeu.jscript.compilation.values;
package me.topchetoeu.jscript.core.compilation.values;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.CompileTarget;
import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.core.compilation.CompileTarget;
import me.topchetoeu.jscript.core.compilation.Instruction;
import me.topchetoeu.jscript.core.compilation.Statement;
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
public class VariableIndexStatement extends Statement {
public final int index;

View File

@@ -1,12 +1,12 @@
package me.topchetoeu.jscript.compilation.values;
package me.topchetoeu.jscript.core.compilation.values;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.AssignableStatement;
import me.topchetoeu.jscript.compilation.CompileTarget;
import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.engine.Operation;
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.core.compilation.AssignableStatement;
import me.topchetoeu.jscript.core.compilation.CompileTarget;
import me.topchetoeu.jscript.core.compilation.Instruction;
import me.topchetoeu.jscript.core.compilation.Statement;
import me.topchetoeu.jscript.core.engine.Operation;
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
public class VariableStatement extends AssignableStatement {
public final String name;

View File

@@ -0,0 +1,166 @@
package me.topchetoeu.jscript.core.engine;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.TreeSet;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.core.engine.debug.DebugContext;
import me.topchetoeu.jscript.core.engine.frame.CodeFrame;
import me.topchetoeu.jscript.core.engine.values.ArrayValue;
import me.topchetoeu.jscript.core.engine.values.FunctionValue;
import me.topchetoeu.jscript.core.engine.values.Symbol;
import me.topchetoeu.jscript.core.engine.values.Values;
import me.topchetoeu.jscript.core.exceptions.EngineException;
import me.topchetoeu.jscript.lib.EnvironmentLib;
import me.topchetoeu.jscript.utils.mapping.SourceMap;
public class Context implements Extensions {
public static final Context NULL = new Context(null);
public final Context parent;
public final Environment environment;
public final CodeFrame frame;
public final Engine engine;
public final int stackSize;
@Override public <T> void add(Symbol key, T obj) {
if (environment != null) environment.add(key, obj);
else if (engine != null) engine.add(key, obj);
}
@Override public <T> T get(Symbol key) {
if (environment != null && environment.has(key)) return environment.get(key);
else if (engine != null && engine.has(key)) return engine.get(key);
return null;
}
@Override public boolean has(Symbol key) {
return
environment != null && environment.has(key) ||
engine != null && engine.has(key);
}
@Override public boolean remove(Symbol key) {
var res = false;
if (environment != null) res |= environment.remove(key);
else if (engine != null) res |= engine.remove(key);
return res;
}
@Override public Iterable<Symbol> keys() {
if (engine == null && environment == null) return List.of();
if (engine == null) return environment.keys();
if (environment == null) return engine.keys();
return () -> Stream.concat(
StreamSupport.stream(engine.keys().spliterator(), false),
StreamSupport.stream(environment.keys().spliterator(), false)
).distinct().iterator();
}
public FunctionValue compile(Filename filename, String raw) {
var env = environment;
var result = Environment.compileFunc(this).call(this, null, raw, filename.toString(), new EnvironmentLib(env));
var function = (FunctionValue)Values.getMember(this, result, "function");
if (!DebugContext.enabled(this)) return function;
var rawMapChain = ((ArrayValue)Values.getMember(this, result, "mapChain")).toArray();
var breakpoints = new TreeSet<>(
Arrays.stream(((ArrayValue)Values.getMember(this, result, "breakpoints")).toArray())
.map(v -> Location.parse(Values.toString(this, v)))
.collect(Collectors.toList())
);
var maps = new SourceMap[rawMapChain.length];
for (var i = 0; i < maps.length; i++) maps[i] = SourceMap.parse(Values.toString(this, (String)rawMapChain[i]));
var map = SourceMap.chain(maps);
if (map != null) {
var newBreakpoints = new TreeSet<Location>();
for (var bp : breakpoints) {
bp = map.toCompiled(bp);
if (bp != null) newBreakpoints.add(bp);
}
breakpoints = newBreakpoints;
}
DebugContext.get(this).onSource(filename, raw, breakpoints, map);
return function;
}
public Context pushFrame(CodeFrame frame) {
var res = new Context(this, frame.function.environment, frame, engine, stackSize + 1);
return res;
}
public Iterable<CodeFrame> frames() {
var self = this;
return () -> new Iterator<CodeFrame>() {
private Context curr = self;
private void update() {
while (curr != null && curr.frame == null) curr = curr.parent;
}
@Override public boolean hasNext() {
update();
return curr != null;
}
@Override public CodeFrame next() {
update();
var res = curr.frame;
curr = curr.parent;
return res;
}
};
}
public List<String> stackTrace() {
var res = new ArrayList<String>();
for (var el : frames()) {
var name = el.function.name;
Location loc = null;
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;
}
private Context(Context parent, Environment environment, CodeFrame frame, Engine engine, int stackSize) {
this.parent = parent;
this.environment = environment;
this.frame = frame;
this.engine = engine;
this.stackSize = stackSize;
if (hasNotNull(Environment.MAX_STACK_COUNT) && stackSize > (int)get(Environment.MAX_STACK_COUNT)) {
throw EngineException.ofRange("Stack overflow!");
}
}
public Context(Engine engine) {
this(null, null, null, engine, 0);
}
public Context(Engine engine, Environment env) {
this(null, env, null, engine, 0);
}
}

View File

@@ -0,0 +1,57 @@
package me.topchetoeu.jscript.core.engine;
import java.util.HashMap;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.events.Awaitable;
import me.topchetoeu.jscript.core.compilation.FunctionBody;
import me.topchetoeu.jscript.core.engine.values.FunctionValue;
import me.topchetoeu.jscript.core.engine.values.Symbol;
public class Engine extends EventLoop implements Extensions {
public static final HashMap<Long, FunctionBody> functions = new HashMap<>();
private final Environment env = new Environment();
@Override
public <T> void add(Symbol key, T obj) {
this.env.add(key, obj);
}
@Override
public <T> T get(Symbol key) {
return this.env.get(key);
}
@Override
public boolean has(Symbol key) {
return this.env.has(key);
}
@Override
public boolean remove(Symbol key) {
return this.env.remove(key);
}
@Override
public Iterable<Symbol> keys() {
return env.keys();
}
public Engine copy() {
var res = new Engine();
res.env.addAll(env);
return res;
}
public Awaitable<Object> pushMsg(boolean micro, Environment env, FunctionValue func, Object thisArg, Object ...args) {
return pushMsg(() -> {
return func.call(new Context(this, env), thisArg, args);
}, micro);
}
public Awaitable<Object> pushMsg(boolean micro, Environment env, Filename filename, String raw, Object thisArg, Object ...args) {
return pushMsg(() -> {
var ctx = new Context(this, env);
return ctx.compile(filename, raw).call(new Context(this, env), thisArg, args);
}, micro);
}
public Engine() {
}
}

View File

@@ -0,0 +1,128 @@
package me.topchetoeu.jscript.core.engine;
import java.util.HashMap;
import java.util.stream.Collectors;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.core.engine.debug.DebugContext;
import me.topchetoeu.jscript.core.engine.scope.GlobalScope;
import me.topchetoeu.jscript.core.engine.values.ArrayValue;
import me.topchetoeu.jscript.core.engine.values.FunctionValue;
import me.topchetoeu.jscript.core.engine.values.NativeFunction;
import me.topchetoeu.jscript.core.engine.values.ObjectValue;
import me.topchetoeu.jscript.core.engine.values.Symbol;
import me.topchetoeu.jscript.core.engine.values.Values;
import me.topchetoeu.jscript.core.exceptions.EngineException;
import me.topchetoeu.jscript.core.parsing.Parsing;
import me.topchetoeu.jscript.utils.interop.NativeWrapperProvider;
@SuppressWarnings("unchecked")
public class Environment implements Extensions {
public static final HashMap<String, Symbol> symbols = new HashMap<>();
public static final Symbol WRAPPERS = Symbol.get("Environment.wrappers");
public static final Symbol COMPILE_FUNC = Symbol.get("Environment.compile");
public static final Symbol REGEX_CONSTR = Symbol.get("Environment.regexConstructor");
public static final Symbol STACK = Symbol.get("Environment.stack");
public static final Symbol MAX_STACK_COUNT = Symbol.get("Environment.maxStackCount");
public static final Symbol HIDE_STACK = Symbol.get("Environment.hideStack");
public static final Symbol OBJECT_PROTO = Symbol.get("Environment.objectPrototype");
public static final Symbol FUNCTION_PROTO = Symbol.get("Environment.functionPrototype");
public static final Symbol ARRAY_PROTO = Symbol.get("Environment.arrayPrototype");
public static final Symbol BOOL_PROTO = Symbol.get("Environment.boolPrototype");
public static final Symbol NUMBER_PROTO = Symbol.get("Environment.numberPrototype");
public static final Symbol STRING_PROTO = Symbol.get("Environment.stringPrototype");
public static final Symbol SYMBOL_PROTO = Symbol.get("Environment.symbolPrototype");
public static final Symbol ERROR_PROTO = Symbol.get("Environment.errorPrototype");
public static final Symbol SYNTAX_ERR_PROTO = Symbol.get("Environment.syntaxErrorPrototype");
public static final Symbol TYPE_ERR_PROTO = Symbol.get("Environment.typeErrorPrototype");
public static final Symbol RANGE_ERR_PROTO = Symbol.get("Environment.rangeErrorPrototype");
private HashMap<Symbol, Object> data = new HashMap<>();
public GlobalScope global;
public WrapperProvider wrappers;
@Override public <T> void add(Symbol key, T obj) {
data.put(key, obj);
}
@Override public <T> T get(Symbol key) {
return (T)data.get(key);
}
@Override public boolean remove(Symbol key) {
if (data.containsKey(key)) {
data.remove(key);
return true;
}
return false;
}
@Override public boolean has(Symbol key) {
return data.containsKey(key);
}
@Override public Iterable<Symbol> keys() {
return data.keySet();
}
public static FunctionValue compileFunc(Extensions ext) {
return ext.init(COMPILE_FUNC, new NativeFunction("compile", args -> {
var source = args.getString(0);
var filename = args.getString(1);
var env = Values.wrapper(Values.getMember(args.ctx, args.get(2), Symbol.get("env")), Environment.class);
var isDebug = DebugContext.enabled(args.ctx);
var res = new ObjectValue();
var target = Parsing.compile(env, Filename.parse(filename), source);
Engine.functions.putAll(target.functions);
Engine.functions.remove(0l);
res.defineProperty(args.ctx, "function", target.func(env));
res.defineProperty(args.ctx, "mapChain", new ArrayValue());
if (isDebug) res.defineProperty(
args.ctx, "breakpoints",
ArrayValue.of(args.ctx, target.breakpoints.stream().map(Location::toString).collect(Collectors.toList()))
);
return res;
}));
}
public static FunctionValue regexConstructor(Extensions ext) {
return ext.init(COMPILE_FUNC, new NativeFunction("RegExp", args -> {
throw EngineException.ofError("Regular expressions not supported.").setCtx(args.ctx.environment, args.ctx.engine);
}));
}
public Environment copy() {
var res = new Environment(null, global);
res.wrappers = wrappers.fork(res);
res.global = global;
res.data.putAll(data);
return res;
}
public Environment child() {
var res = copy();
res.global = res.global.globalChild();
return res;
}
public Context context(Engine engine) {
return new Context(engine, this);
}
public Environment(WrapperProvider nativeConverter, GlobalScope global) {
if (nativeConverter == null) nativeConverter = new NativeWrapperProvider(this);
if (global == null) global = new GlobalScope();
this.wrappers = nativeConverter;
this.global = global;
}
public Environment() {
this(null, null);
}
}

View File

@@ -0,0 +1,81 @@
package me.topchetoeu.jscript.core.engine;
import java.util.concurrent.PriorityBlockingQueue;
import me.topchetoeu.jscript.common.ResultRunnable;
import me.topchetoeu.jscript.common.events.Awaitable;
import me.topchetoeu.jscript.common.events.DataNotifier;
import me.topchetoeu.jscript.core.exceptions.InterruptException;
public class EventLoop {
private static class Task implements Comparable<Task> {
public final ResultRunnable<?> runnable;
public final DataNotifier<Object> notifier = new DataNotifier<>();
public final boolean micro;
public Task(ResultRunnable<?> runnable, boolean micro) {
this.runnable = runnable;
this.micro = micro;
}
@Override
public int compareTo(Task other) {
return Integer.compare(this.micro ? 0 : 1, other.micro ? 0 : 1);
}
}
private PriorityBlockingQueue<Task> tasks = new PriorityBlockingQueue<>();
private Thread thread;
@SuppressWarnings("unchecked")
public <T> Awaitable<T> pushMsg(ResultRunnable<T> runnable, boolean micro) {
var msg = new Task(runnable, micro);
tasks.add(msg);
return (Awaitable<T>)msg.notifier;
}
public Awaitable<Object> pushMsg(Runnable runnable, boolean micro) {
return pushMsg(() -> { runnable.run(); return null; }, micro);
}
public void run(boolean untilEmpty) {
while (!untilEmpty || !tasks.isEmpty()) {
try {
var task = tasks.take();
try {
task.notifier.next(task.runnable.run());
}
catch (RuntimeException e) {
if (e instanceof InterruptException) throw e;
task.notifier.error(e);
}
}
catch (InterruptedException | InterruptException e) {
for (var msg : tasks) msg.notifier.error(new InterruptException(e));
break;
}
}
}
public Thread thread() {
return thread;
}
public Thread start() {
if (thread == null) {
thread = new Thread(() -> run(false), "Event loop #" + hashCode());
thread.start();
}
return thread;
}
public void stop() {
if (thread != null) thread.interrupt();
thread = null;
}
public boolean inLoopThread() {
return Thread.currentThread() == thread;
}
public boolean isRunning() {
return this.thread != null;
}
}

View File

@@ -0,0 +1,34 @@
package me.topchetoeu.jscript.core.engine;
import me.topchetoeu.jscript.core.engine.values.Symbol;
public interface Extensions {
<T> T get(Symbol key);
<T> void add(Symbol key, T obj);
Iterable<Symbol> keys();
boolean has(Symbol key);
boolean remove(Symbol key);
default boolean hasNotNull(Symbol key) {
return has(key) && get(key) != null;
}
default <T> T get(Symbol key, T defaultVal) {
if (has(key)) return get(key);
else return defaultVal;
}
default <T> T init(Symbol key, T val) {
if (has(key)) return get(key);
else {
add(key, val);
return val;
}
}
default void addAll(Extensions source) {
for (var key : source.keys()) {
add(key, source.get(key));
}
}
}

View File

@@ -1,4 +1,4 @@
package me.topchetoeu.jscript.engine;
package me.topchetoeu.jscript.core.engine;
public enum Operation {
INSTANCEOF(2, false),

View File

@@ -0,0 +1,12 @@
package me.topchetoeu.jscript.core.engine;
import me.topchetoeu.jscript.core.engine.values.FunctionValue;
import me.topchetoeu.jscript.core.engine.values.ObjectValue;
public interface WrapperProvider {
public ObjectValue getProto(Class<?> obj);
public ObjectValue getNamespace(Class<?> obj);
public FunctionValue getConstr(Class<?> obj);
public WrapperProvider fork(Environment env);
}

View File

@@ -0,0 +1,100 @@
package me.topchetoeu.jscript.core.engine.debug;
import java.util.HashMap;
import java.util.TreeSet;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.core.compilation.Instruction;
import me.topchetoeu.jscript.core.engine.Context;
import me.topchetoeu.jscript.core.engine.Extensions;
import me.topchetoeu.jscript.core.engine.frame.CodeFrame;
import me.topchetoeu.jscript.core.engine.values.Symbol;
import me.topchetoeu.jscript.core.exceptions.EngineException;
import me.topchetoeu.jscript.utils.mapping.SourceMap;
public class DebugContext implements DebugController {
public static final Symbol ENV_KEY = Symbol.get("Engine.debug");
public static final Symbol IGNORE = Symbol.get("Engine.ignoreDebug");
private HashMap<Filename, String> sources;
private HashMap<Filename, TreeSet<Location>> bpts;
private HashMap<Filename, SourceMap> maps;
private DebugController debugger;
public boolean attachDebugger(DebugController debugger) {
if (this.debugger != null) return false;
if (sources != null) {
for (var source : sources.entrySet()) debugger.onSource(
source.getKey(), source.getValue(),
bpts.get(source.getKey()),
maps.get(source.getKey())
);
}
this.debugger = debugger;
return true;
}
public boolean detachDebugger() {
this.debugger = null;
return true;
}
public DebugController debugger() {
if (debugger == null) return DebugController.empty();
else return debugger;
}
@Override public void onFramePop(Context ctx, CodeFrame frame) {
if (debugger != null) debugger.onFramePop(ctx, frame);
}
@Override public void onFramePush(Context ctx, CodeFrame frame) {
if (debugger != null) debugger.onFramePush(ctx, frame);
}
@Override public boolean onInstruction(Context ctx, CodeFrame frame, Instruction instruction, Object returnVal, EngineException error, boolean caught) {
if (debugger != null) return debugger.onInstruction(ctx, frame, instruction, returnVal, error, caught);
else return false;
}
@Override public void onSource(Filename filename, String source, TreeSet<Location> breakpoints, SourceMap map) {
if (debugger != null) debugger.onSource(filename, source, breakpoints, map);
if (sources != null) sources.put(filename, source);
if (bpts != null) bpts.put(filename, breakpoints);
if (maps != null) maps.put(filename, map);
}
public Location mapToCompiled(Location location) {
if (maps == null) return location;
var map = maps.get(location.filename());
if (map == null) return location;
return map.toCompiled(location);
}
public Location mapToOriginal(Location location) {
if (maps == null) return location;
var map = maps.get(location.filename());
if (map == null) return location;
return map.toOriginal(location);
}
private DebugContext(boolean enabled) {
if (enabled) {
sources = new HashMap<>();
bpts = new HashMap<>();
maps = new HashMap<>();
}
}
public DebugContext() {
this(true);
}
public static boolean enabled(Extensions exts) {
return exts.hasNotNull(ENV_KEY) && !exts.has(IGNORE);
}
public static DebugContext get(Extensions exts) {
if (enabled(exts)) return exts.get(ENV_KEY);
else return new DebugContext(false);
}
}

View File

@@ -1,14 +1,14 @@
package me.topchetoeu.jscript.engine.debug;
package me.topchetoeu.jscript.core.engine.debug;
import java.util.TreeSet;
import me.topchetoeu.jscript.Filename;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.engine.frame.CodeFrame;
import me.topchetoeu.jscript.exceptions.EngineException;
import me.topchetoeu.jscript.mapping.SourceMap;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.core.compilation.Instruction;
import me.topchetoeu.jscript.core.engine.Context;
import me.topchetoeu.jscript.core.engine.frame.CodeFrame;
import me.topchetoeu.jscript.core.exceptions.EngineException;
import me.topchetoeu.jscript.utils.mapping.SourceMap;
public interface DebugController {
/**
@@ -27,7 +27,7 @@ public interface DebugController {
* @param frame The frame in which execution is occuring
* @param instruction The instruction which was or will be executed
* @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
* @param returnVal The return value of the instruction, Values.NO_RETURN if none
* @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
@@ -48,4 +48,15 @@ public interface DebugController {
* @param frame The code frame which was popped out
*/
void onFramePop(Context ctx, CodeFrame frame);
public static DebugController empty() {
return new DebugController () {
@Override public void onFramePop(Context ctx, CodeFrame frame) { }
@Override public void onFramePush(Context ctx, CodeFrame frame) { }
@Override public boolean onInstruction(Context ctx, CodeFrame frame, Instruction instruction, Object returnVal, EngineException error, boolean caught) {
return false;
}
@Override public void onSource(Filename filename, String source, TreeSet<Location> breakpoints, SourceMap map) { }
};
}
}

View File

@@ -0,0 +1,33 @@
package me.topchetoeu.jscript.core.engine.debug;
import java.io.IOException;
public interface DebugHandler {
void enable(V8Message msg) throws IOException;
void disable(V8Message msg) throws IOException;
void setBreakpointByUrl(V8Message msg) throws IOException;
void removeBreakpoint(V8Message msg) throws IOException;
void continueToLocation(V8Message msg) throws IOException;
void getScriptSource(V8Message msg) throws IOException;
void getPossibleBreakpoints(V8Message msg) throws IOException;
void resume(V8Message msg) throws IOException;
void pause(V8Message msg) throws IOException;
void stepInto(V8Message msg) throws IOException;
void stepOut(V8Message msg) throws IOException;
void stepOver(V8Message msg) throws IOException;
void setPauseOnExceptions(V8Message msg) throws IOException;
void evaluateOnCallFrame(V8Message msg) throws IOException;
void getProperties(V8Message msg) throws IOException;
void releaseObjectGroup(V8Message msg) throws IOException;
void releaseObject(V8Message msg) throws IOException;
void callFunctionOn(V8Message msg) throws IOException;
void runtimeEnable(V8Message msg) throws IOException;
}

View File

@@ -1,6 +1,7 @@
package me.topchetoeu.jscript.engine.debug;
package me.topchetoeu.jscript.core.engine.debug;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
@@ -8,16 +9,14 @@ import java.security.MessageDigest;
import java.util.Base64;
import java.util.HashMap;
import me.topchetoeu.jscript.Metadata;
import me.topchetoeu.jscript.Reading;
import me.topchetoeu.jscript.engine.debug.WebSocketMessage.Type;
import me.topchetoeu.jscript.events.Notifier;
import me.topchetoeu.jscript.exceptions.SyntaxException;
import me.topchetoeu.jscript.exceptions.UncheckedException;
import me.topchetoeu.jscript.exceptions.UncheckedIOException;
import me.topchetoeu.jscript.json.JSON;
import me.topchetoeu.jscript.json.JSONList;
import me.topchetoeu.jscript.json.JSONMap;
import me.topchetoeu.jscript.common.Metadata;
import me.topchetoeu.jscript.common.Reading;
import me.topchetoeu.jscript.common.events.Notifier;
import me.topchetoeu.jscript.common.json.JSON;
import me.topchetoeu.jscript.common.json.JSONList;
import me.topchetoeu.jscript.common.json.JSONMap;
import me.topchetoeu.jscript.core.engine.debug.WebSocketMessage.Type;
import me.topchetoeu.jscript.core.exceptions.SyntaxException;
public class DebugServer {
public static String browserDisplayName = Metadata.name() + "/" + Metadata.version();
@@ -36,8 +35,7 @@ public class DebugServer {
try {
return MessageDigest.getInstance("sha1");
}
catch (Throwable e) { throw new UncheckedException(e); }
catch (Throwable e) { throw new RuntimeException(e); }
}
private static Thread runAsync(Runnable func, String name) {
@@ -47,11 +45,9 @@ public class DebugServer {
return res;
}
private void handle(WebSocket ws, Debugger debugger) {
private void handle(WebSocket ws, Debugger debugger) throws IOException {
WebSocketMessage raw;
debugger.connect();
while ((raw = ws.receive()) != null) {
if (raw.type != Type.Text) {
ws.send(new V8Error("Expected a text message."));
@@ -68,55 +64,50 @@ public class DebugServer {
return;
}
try {
switch (msg.name) {
case "Debugger.enable":
connNotifier.next();
debugger.enable(msg); continue;
case "Debugger.disable": debugger.disable(msg); continue;
switch (msg.name) {
case "Debugger.enable":
connNotifier.next();
debugger.enable(msg);
continue;
case "Debugger.disable": debugger.close(); continue;
case "Debugger.setBreakpointByUrl": debugger.setBreakpointByUrl(msg); continue;
case "Debugger.removeBreakpoint": debugger.removeBreakpoint(msg); continue;
case "Debugger.continueToLocation": debugger.continueToLocation(msg); continue;
case "Debugger.setBreakpointByUrl": debugger.setBreakpointByUrl(msg); continue;
case "Debugger.removeBreakpoint": debugger.removeBreakpoint(msg); continue;
case "Debugger.continueToLocation": debugger.continueToLocation(msg); continue;
case "Debugger.getScriptSource": debugger.getScriptSource(msg); continue;
case "Debugger.getPossibleBreakpoints": debugger.getPossibleBreakpoints(msg); continue;
case "Debugger.getScriptSource": debugger.getScriptSource(msg); continue;
case "Debugger.getPossibleBreakpoints": debugger.getPossibleBreakpoints(msg); continue;
case "Debugger.resume": debugger.resume(msg); continue;
case "Debugger.pause": debugger.pause(msg); continue;
case "Debugger.resume": debugger.resume(msg); continue;
case "Debugger.pause": debugger.pause(msg); continue;
case "Debugger.stepInto": debugger.stepInto(msg); continue;
case "Debugger.stepOut": debugger.stepOut(msg); continue;
case "Debugger.stepOver": debugger.stepOver(msg); continue;
case "Debugger.stepInto": debugger.stepInto(msg); continue;
case "Debugger.stepOut": debugger.stepOut(msg); continue;
case "Debugger.stepOver": debugger.stepOver(msg); continue;
case "Debugger.setPauseOnExceptions": debugger.setPauseOnExceptions(msg); continue;
case "Debugger.evaluateOnCallFrame": debugger.evaluateOnCallFrame(msg); continue;
case "Debugger.setPauseOnExceptions": debugger.setPauseOnExceptions(msg); continue;
case "Debugger.evaluateOnCallFrame": debugger.evaluateOnCallFrame(msg); continue;
case "Runtime.releaseObjectGroup": debugger.releaseObjectGroup(msg); continue;
case "Runtime.releaseObject": debugger.releaseObject(msg); continue;
case "Runtime.getProperties": debugger.getProperties(msg); continue;
case "Runtime.callFunctionOn": debugger.callFunctionOn(msg); continue;
// case "NodeWorker.enable": debugger.nodeWorkerEnable(msg); continue;
case "Runtime.enable": debugger.runtimeEnable(msg); continue;
}
if (
msg.name.startsWith("DOM.") ||
msg.name.startsWith("DOMDebugger.") ||
msg.name.startsWith("Emulation.") ||
msg.name.startsWith("Input.") ||
msg.name.startsWith("Network.") ||
msg.name.startsWith("Page.")
) ws.send(new V8Error("This isn't a browser..."));
else ws.send(new V8Error("This API is not supported yet."));
}
catch (Throwable e) {
e.printStackTrace();
throw new UncheckedException(e);
case "Runtime.releaseObjectGroup": debugger.releaseObjectGroup(msg); continue;
case "Runtime.releaseObject": debugger.releaseObject(msg); continue;
case "Runtime.getProperties": debugger.getProperties(msg); continue;
case "Runtime.callFunctionOn": debugger.callFunctionOn(msg); continue;
// case "NodeWorker.enable": debugger.nodeWorkerEnable(msg); continue;
case "Runtime.enable": debugger.runtimeEnable(msg); continue;
}
if (
msg.name.startsWith("DOM.") ||
msg.name.startsWith("DOMDebugger.") ||
msg.name.startsWith("Emulation.") ||
msg.name.startsWith("Input.") ||
msg.name.startsWith("Network.") ||
msg.name.startsWith("Page.")
) ws.send(new V8Error("This isn't a browser..."));
else ws.send(new V8Error("This API is not supported yet."));
}
debugger.disconnect();
debugger.close();
}
private void onWsConnect(HttpRequest req, Socket socket, DebuggerProvider debuggerProvider) {
var key = req.headers.get("sec-websocket-key");
@@ -148,10 +139,13 @@ public class DebugServer {
runAsync(() -> {
try { handle(ws, debugger); }
catch (RuntimeException e) {
ws.send(new V8Error(e.getMessage()));
catch (RuntimeException | IOException e) {
try {
ws.send(new V8Error(e.getMessage()));
}
catch (IOException e2) { /* Shit outta luck */ }
}
finally { ws.close(); debugger.disconnect(); }
finally { ws.close(); debugger.close(); }
}, "Debug Handler");
}
@@ -232,14 +226,16 @@ public class DebugServer {
public DebugServer() {
try {
this.favicon = Reading.resourceToStream("debugger/favicon.png").readAllBytes();
this.protocol = Reading.resourceToStream("debugger/protocol.json").readAllBytes();
this.index = Reading.resourceToString("debugger/index.html")
this.favicon = Reading.resourceToStream("me/topchetoeu/jscript/utils/assets/debugger/favicon.png").readAllBytes();
this.protocol = Reading.resourceToStream("me/topchetoeu/jscript/utils/assets/debugger/protocol.json").readAllBytes();
this.index = Reading.resourceToString("me/topchetoeu/jscript/utils/assets/debugger/index.html")
.replace("${NAME}", Metadata.name())
.replace("${VERSION}", Metadata.version())
.replace("${AUTHOR}", Metadata.author())
.getBytes();
}
catch (IOException e) { throw new UncheckedIOException(e); }
catch (IOException e) {
throw new UncheckedIOException(e);
}
}
}

View File

@@ -0,0 +1,5 @@
package me.topchetoeu.jscript.core.engine.debug;
public interface Debugger extends DebugHandler, DebugController {
void close();
}

View File

@@ -1,4 +1,4 @@
package me.topchetoeu.jscript.engine.debug;
package me.topchetoeu.jscript.core.engine.debug;
public interface DebuggerProvider {
Debugger getDebugger(WebSocket socket, HttpRequest req);

View File

@@ -1,4 +1,4 @@
package me.topchetoeu.jscript.engine.debug;
package me.topchetoeu.jscript.core.engine.debug;
import java.io.BufferedReader;
import java.io.IOException;

View File

@@ -1,5 +1,6 @@
package me.topchetoeu.jscript.engine.debug;
package me.topchetoeu.jscript.core.engine.debug;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@@ -9,29 +10,29 @@ import java.util.TreeSet;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import me.topchetoeu.jscript.Filename;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Instruction.Type;
import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.engine.Engine;
import me.topchetoeu.jscript.engine.frame.CodeFrame;
import me.topchetoeu.jscript.engine.frame.Runners;
import me.topchetoeu.jscript.engine.scope.GlobalScope;
import me.topchetoeu.jscript.engine.values.ArrayValue;
import me.topchetoeu.jscript.engine.values.CodeFunction;
import me.topchetoeu.jscript.engine.values.FunctionValue;
import me.topchetoeu.jscript.engine.values.ObjectValue;
import me.topchetoeu.jscript.engine.values.Symbol;
import me.topchetoeu.jscript.engine.values.Values;
import me.topchetoeu.jscript.events.Notifier;
import me.topchetoeu.jscript.exceptions.EngineException;
import me.topchetoeu.jscript.json.JSON;
import me.topchetoeu.jscript.json.JSONElement;
import me.topchetoeu.jscript.json.JSONList;
import me.topchetoeu.jscript.json.JSONMap;
import me.topchetoeu.jscript.mapping.SourceMap;
import me.topchetoeu.jscript.parsing.Parsing;
import me.topchetoeu.jscript.common.Filename;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.common.events.Notifier;
import me.topchetoeu.jscript.common.json.JSON;
import me.topchetoeu.jscript.common.json.JSONElement;
import me.topchetoeu.jscript.common.json.JSONList;
import me.topchetoeu.jscript.common.json.JSONMap;
import me.topchetoeu.jscript.core.compilation.Instruction;
import me.topchetoeu.jscript.core.compilation.Instruction.Type;
import me.topchetoeu.jscript.core.engine.Context;
import me.topchetoeu.jscript.core.engine.Engine;
import me.topchetoeu.jscript.core.engine.Environment;
import me.topchetoeu.jscript.core.engine.frame.CodeFrame;
import me.topchetoeu.jscript.core.engine.scope.GlobalScope;
import me.topchetoeu.jscript.core.engine.values.ArrayValue;
import me.topchetoeu.jscript.core.engine.values.CodeFunction;
import me.topchetoeu.jscript.core.engine.values.FunctionValue;
import me.topchetoeu.jscript.core.engine.values.ObjectValue;
import me.topchetoeu.jscript.core.engine.values.Symbol;
import me.topchetoeu.jscript.core.engine.values.Values;
import me.topchetoeu.jscript.core.exceptions.EngineException;
import me.topchetoeu.jscript.core.parsing.Parsing;
import me.topchetoeu.jscript.utils.mapping.SourceMap;
// very simple indeed
public class SimpleDebugger implements Debugger {
@@ -109,34 +110,47 @@ public class SimpleDebugger implements Debugger {
public ObjectValue local, capture, global, valstack;
public JSONMap serialized;
public Location location;
public boolean debugData = false;
public void updateLoc(Location loc) {
if (loc == null) return;
this.location = loc;
}
public Frame(Context ctx, CodeFrame frame, int id) {
public Frame(CodeFrame frame, int id) {
this.frame = frame;
this.func = frame.function;
this.id = id;
this.global = frame.function.environment.global.obj;
this.local = frame.getLocalScope(ctx, true);
this.capture = frame.getCaptureScope(ctx, true);
this.local.setPrototype(ctx, capture);
this.capture.setPrototype(ctx, global);
this.valstack = frame.getValStackScope(ctx);
debugData = true;
this.local = frame.getLocalScope(true);
this.capture = frame.getCaptureScope(true);
Values.makePrototypeChain(frame.ctx, global, capture, local);
this.valstack = frame.getValStackScope();
this.serialized = new JSONMap()
.set("callFrameId", id + "")
.set("functionName", func.name)
.set("scopeChain", new JSONList()
.add(new JSONMap().set("type", "local").set("name", "Local Scope").set("object", serializeObj(ctx, local)))
.add(new JSONMap().set("type", "closure").set("name", "Closure").set("object", serializeObj(ctx, capture)))
.add(new JSONMap().set("type", "global").set("name", "Global Scope").set("object", serializeObj(ctx, global)))
.add(new JSONMap().set("type", "other").set("name", "Value Stack").set("object", serializeObj(ctx, valstack)))
.add(new JSONMap()
.set("type", "local")
.set("name", "Local Scope")
.set("object", serializeObj(frame.ctx, local))
)
.add(new JSONMap()
.set("type", "closure")
.set("name", "Closure")
.set("object", serializeObj(frame.ctx, capture))
)
.add(new JSONMap()
.set("type", "global")
.set("name", "Global Scope")
.set("object", serializeObj(frame.ctx, global))
)
.add(new JSONMap()
.set("type", "other")
.set("name", "Value Stack")
.set("object", serializeObj(frame.ctx, valstack))
)
);
}
}
@@ -173,7 +187,6 @@ public class SimpleDebugger implements Debugger {
public State state = State.RESUMED;
public final WebSocket ws;
public final Engine target;
private ObjectValue emptyObject = new ObjectValue();
@@ -232,26 +245,29 @@ public class SimpleDebugger implements Debugger {
return nextId++;
}
private synchronized void updateFrames(Context ctx) {
var frame = ctx.peekFrame();
if (frame == null) return;
private synchronized Frame getFrame(CodeFrame frame) {
if (!codeFrameToFrame.containsKey(frame)) {
var id = nextId();
var fr = new Frame(ctx, frame, id);
var fr = new Frame(frame, id);
idToFrame.put(id, fr);
codeFrameToFrame.put(frame, fr);
}
currFrame = codeFrameToFrame.get(frame);
return fr;
}
else return codeFrameToFrame.get(frame);
}
private synchronized void updateFrames(Context ctx) {
var frame = ctx.frame;
if (frame == null) return;
currFrame = getFrame(frame);
}
private JSONList serializeFrames(Context ctx) {
var res = new JSONList();
var frames = ctx.frames();
for (var i = frames.size() - 1; i >= 0; i--) {
var frame = codeFrameToFrame.get(frames.get(i));
for (var el : ctx.frames()) {
var frame = getFrame(el);
if (frame.location == null) continue;
frame.serialized.set("location", serializeLocation(frame.location));
if (frame.location != null) res.add(frame.serialized);
@@ -302,6 +318,8 @@ public class SimpleDebugger implements Debugger {
}
private JSONMap serializeObj(Context ctx, Object val, boolean byValue) {
val = Values.normalize(null, val);
ctx = new Context(ctx.engine.copy(), ctx.environment);
ctx.engine.add(DebugContext.IGNORE, true);
if (val == Values.NULL) {
return new JSONMap()
@@ -341,7 +359,7 @@ public class SimpleDebugger implements Debugger {
try {
defaultToString =
Values.getMember(ctx, obj, "toString") ==
Values.getMember(ctx, ctx.environment().proto("object"), "toString");
Values.getMember(ctx, ctx.get(Environment.OBJECT_PROTO), "toString");
}
catch (Exception e) { }
@@ -435,55 +453,85 @@ public class SimpleDebugger implements Debugger {
}
private void resume(State state) {
this.state = state;
ws.send(new V8Event("Debugger.resumed", new JSONMap()));
updateNotifier.next();
try {
this.state = state;
ws.send(new V8Event("Debugger.resumed", new JSONMap()));
updateNotifier.next();
}
catch (IOException e) {
ws.close();
close();
}
}
private void pauseDebug(Context ctx, Breakpoint bp) {
state = State.PAUSED_NORMAL;
var map = new JSONMap()
.set("callFrames", serializeFrames(ctx))
.set("reason", "debugCommand");
try {
state = State.PAUSED_NORMAL;
var map = new JSONMap()
.set("callFrames", serializeFrames(ctx))
.set("reason", "debugCommand");
if (bp != null) map.set("hitBreakpoints", new JSONList().add(bp.id + ""));
ws.send(new V8Event("Debugger.paused", map));
if (bp != null) map.set("hitBreakpoints", new JSONList().add(bp.id + ""));
ws.send(new V8Event("Debugger.paused", map));
}
catch (IOException e) {
ws.close();
close();
}
}
private void pauseException(Context ctx) {
state = State.PAUSED_EXCEPTION;
var map = new JSONMap()
.set("callFrames", serializeFrames(ctx))
.set("reason", "exception");
try {
state = State.PAUSED_EXCEPTION;
var map = new JSONMap()
.set("callFrames", serializeFrames(ctx))
.set("reason", "exception");
ws.send(new V8Event("Debugger.paused", map));
ws.send(new V8Event("Debugger.paused", map));
}
catch (IOException e) {
ws.close();
close();
}
}
private void sendSource(Source src) {
ws.send(new V8Event("Debugger.scriptParsed", new JSONMap()
.set("scriptId", src.id + "")
.set("hash", src.source.hashCode())
.set("url", src.filename + "")
));
private void sendSource(Source src){
try {
ws.send(new V8Event("Debugger.scriptParsed", new JSONMap()
.set("scriptId", src.id + "")
.set("hash", src.source.hashCode())
.set("url", src.filename + "")
));
}
catch (IOException e) {
ws.close();
close();
}
}
private void addBreakpoint(Breakpoint bpt) {
idToBreakpoint.put(bpt.id, bpt);
locToBreakpoint.put(bpt.location, bpt);
try {
idToBreakpoint.put(bpt.id, bpt);
locToBreakpoint.put(bpt.location, bpt);
ws.send(new V8Event("Debugger.breakpointResolved", new JSONMap()
.set("breakpointId", bpt.id)
.set("location", serializeLocation(bpt.location))
));
ws.send(new V8Event("Debugger.breakpointResolved", new JSONMap()
.set("breakpointId", bpt.id)
.set("location", serializeLocation(bpt.location))
));
}
catch (IOException e) {
ws.close();
close();
}
}
private RunResult run(Frame codeFrame, String code) {
if (codeFrame == null) return new RunResult(null, code, new EngineException("Invalid code frame!"));
var engine = new Engine(false);
var env = codeFrame.func.environment.fork();
var engine = new Engine();
var env = codeFrame.func.environment.copy();
env.global = new GlobalScope(codeFrame.local);
var ctx = new Context(engine, env);
var awaiter = engine.pushMsg(false, ctx.environment(), new Filename("jscript", "eval"), code, codeFrame.frame.thisArg, codeFrame.frame.args);
var awaiter = engine.pushMsg(false, ctx.environment, new Filename("jscript", "eval"), code, codeFrame.frame.thisArg, codeFrame.frame.args);
engine.run(true);
@@ -495,7 +543,7 @@ public class SimpleDebugger implements Debugger {
var res = new ArrayValue();
var passed = new HashSet<String>();
var tildas = "~";
if (target == null) target = ctx.environment().getGlobal();
if (target == null) target = ctx.environment.global;
for (var proto = target; proto != null && proto != Values.NULL; proto = Values.getPrototype(ctx, proto)) {
for (var el : Values.getMembers(ctx, proto, true, true)) {
@@ -564,7 +612,7 @@ public class SimpleDebugger implements Debugger {
return resObj;
}
@Override public synchronized void enable(V8Message msg) {
@Override public synchronized void enable(V8Message msg) throws IOException {
enabled = true;
ws.send(msg.respond());
@@ -573,17 +621,45 @@ public class SimpleDebugger implements Debugger {
updateNotifier.next();
}
@Override public synchronized void disable(V8Message msg) {
enabled = false;
@Override public synchronized void disable(V8Message msg) throws IOException {
close();
ws.send(msg.respond());
}
public synchronized void close() {
enabled = false;
execptionType = CatchType.NONE;
state = State.RESUMED;
idToBptCand.clear();
idToBreakpoint.clear();
locToBreakpoint.clear();
tmpBreakpts.clear();
filenameToId.clear();
idToSource.clear();
pendingSources.clear();
idToFrame.clear();
codeFrameToFrame.clear();
idToObject.clear();
objectToId.clear();
objectGroups.clear();
pendingPause = false;
stepOutFrame = currFrame = null;
stepOutPtr = 0;
updateNotifier.next();
}
@Override public synchronized void getScriptSource(V8Message msg) {
@Override public synchronized void getScriptSource(V8Message msg) throws IOException {
int id = Integer.parseInt(msg.params.string("scriptId"));
ws.send(msg.respond(new JSONMap().set("scriptSource", idToSource.get(id).source)));
}
@Override public synchronized void getPossibleBreakpoints(V8Message msg) {
@Override public synchronized void getPossibleBreakpoints(V8Message msg) throws IOException {
var src = idToSource.get(Integer.parseInt(msg.params.map("start").string("scriptId")));
var start = deserializeLocation(msg.params.get("start"), false);
var end = msg.params.isMap("end") ? deserializeLocation(msg.params.get("end"), false) : null;
@@ -598,16 +674,16 @@ public class SimpleDebugger implements Debugger {
ws.send(msg.respond(new JSONMap().set("locations", res)));
}
@Override public synchronized void pause(V8Message msg) {
@Override public synchronized void pause(V8Message msg) throws IOException {
pendingPause = true;
ws.send(msg.respond());
}
@Override public synchronized void resume(V8Message msg) {
@Override public synchronized void resume(V8Message msg) throws IOException {
resume(State.RESUMED);
ws.send(msg.respond(new JSONMap()));
}
@Override public synchronized void setBreakpointByUrl(V8Message msg) {
@Override public synchronized void setBreakpointByUrl(V8Message msg) throws IOException {
var line = (int)msg.params.number("lineNumber") + 1;
var col = (int)msg.params.number("columnNumber", 0) + 1;
var cond = msg.params.string("condition", "").trim();
@@ -642,7 +718,7 @@ public class SimpleDebugger implements Debugger {
.set("locations", locs)
));
}
@Override public synchronized void removeBreakpoint(V8Message msg) {
@Override public synchronized void removeBreakpoint(V8Message msg) throws IOException {
var id = Integer.parseInt(msg.params.string("breakpointId"));
if (idToBptCand.containsKey(id)) {
@@ -659,7 +735,7 @@ public class SimpleDebugger implements Debugger {
}
ws.send(msg.respond());
}
@Override public synchronized void continueToLocation(V8Message msg) {
@Override public synchronized void continueToLocation(V8Message msg) throws IOException {
var loc = deserializeLocation(msg.params.get("location"), true);
tmpBreakpts.add(loc);
@@ -668,7 +744,7 @@ public class SimpleDebugger implements Debugger {
ws.send(msg.respond());
}
@Override public synchronized void setPauseOnExceptions(V8Message msg) {
@Override public synchronized void setPauseOnExceptions(V8Message msg) throws IOException {
switch (msg.params.string("state")) {
case "none": execptionType = CatchType.NONE; break;
case "all": execptionType = CatchType.ALL; break;
@@ -681,7 +757,7 @@ public class SimpleDebugger implements Debugger {
ws.send(msg.respond());
}
@Override public synchronized void stepInto(V8Message msg) {
@Override public synchronized void stepInto(V8Message msg) throws IOException {
if (state == State.RESUMED) ws.send(new V8Error("Debugger is resumed."));
else {
stepOutFrame = currFrame;
@@ -690,7 +766,7 @@ public class SimpleDebugger implements Debugger {
ws.send(msg.respond());
}
}
@Override public synchronized void stepOut(V8Message msg) {
@Override public synchronized void stepOut(V8Message msg) throws IOException {
if (state == State.RESUMED) ws.send(new V8Error("Debugger is resumed."));
else {
stepOutFrame = currFrame;
@@ -699,7 +775,7 @@ public class SimpleDebugger implements Debugger {
ws.send(msg.respond());
}
}
@Override public synchronized void stepOver(V8Message msg) {
@Override public synchronized void stepOver(V8Message msg) throws IOException {
if (state == State.RESUMED) ws.send(new V8Error("Debugger is resumed."));
else {
stepOutFrame = currFrame;
@@ -709,7 +785,7 @@ public class SimpleDebugger implements Debugger {
}
}
@Override public synchronized void evaluateOnCallFrame(V8Message msg) {
@Override public synchronized void evaluateOnCallFrame(V8Message msg) throws IOException {
var cfId = Integer.parseInt(msg.params.string("callFrameId"));
var expr = msg.params.string("expression");
var group = msg.params.string("objectGroup", null);
@@ -723,12 +799,12 @@ public class SimpleDebugger implements Debugger {
else ws.send(msg.respond(new JSONMap().set("result", serializeObj(res.ctx, res.result))));
}
@Override public synchronized void releaseObjectGroup(V8Message msg) {
@Override public synchronized void releaseObjectGroup(V8Message msg) throws IOException {
var group = msg.params.string("objectGroup");
releaseGroup(group);
ws.send(msg.respond());
}
@Override public synchronized void releaseObject(V8Message msg) {
@Override public synchronized void releaseObject(V8Message msg) throws IOException {
var id = Integer.parseInt(msg.params.string("objectId"));
var ref = idToObject.get(id);
ref.held = false;
@@ -740,7 +816,7 @@ public class SimpleDebugger implements Debugger {
ws.send(msg.respond());
}
@Override public synchronized void getProperties(V8Message msg) {
@Override public synchronized void getProperties(V8Message msg) throws IOException {
var ref = idToObject.get(Integer.parseInt(msg.params.string("objectId")));
var obj = ref.obj;
@@ -764,7 +840,7 @@ public class SimpleDebugger implements Debugger {
}
else {
propDesc.set("name", Values.toString(ctx, key));
propDesc.set("value", serializeObj(ctx, obj.getMember(ctx, key)));
propDesc.set("value", serializeObj(ctx, Values.getMember(ctx, obj, key)));
propDesc.set("writable", obj.memberWritable(key));
propDesc.set("enumerable", obj.memberEnumerable(key));
propDesc.set("configurable", obj.memberConfigurable(key));
@@ -773,7 +849,7 @@ public class SimpleDebugger implements Debugger {
}
}
var proto = obj.getPrototype(ctx);
var proto = Values.getPrototype(ctx, obj);
var protoDesc = new JSONMap();
protoDesc.set("name", "__proto__");
@@ -787,7 +863,7 @@ public class SimpleDebugger implements Debugger {
ws.send(msg.respond(new JSONMap().set("result", res)));
}
@Override public synchronized void callFunctionOn(V8Message msg) {
@Override public synchronized void callFunctionOn(V8Message msg) throws IOException {
var src = msg.params.string("functionDeclaration");
var args = msg.params
.list("arguments", new JSONList())
@@ -835,7 +911,7 @@ public class SimpleDebugger implements Debugger {
catch (EngineException e) { ws.send(msg.respond(new JSONMap().set("exceptionDetails", serializeException(ctx, e)))); }
}
@Override public synchronized void runtimeEnable(V8Message msg) {
@Override public synchronized void runtimeEnable(V8Message msg) throws IOException {
ws.send(msg.respond());
}
@@ -866,18 +942,20 @@ public class SimpleDebugger implements Debugger {
Frame frame;
synchronized (this) {
frame = codeFrameToFrame.get(cf);
frame = getFrame(cf);
if (!frame.debugData) return false;
if (instruction.location != null) frame.updateLoc(ctx.engine.mapToCompiled(instruction.location));
if (instruction.location != null) frame.updateLoc(DebugContext.get(ctx).mapToCompiled(instruction.location));
loc = frame.location;
isBreakpointable = loc != null && (instruction.breakpoint.shouldStepIn());
if (error != null && (execptionType == CatchType.ALL || execptionType == CatchType.UNCAUGHT && !caught)) {
pauseException(ctx);
}
else if (loc != null && (state == State.STEPPING_IN || state == State.STEPPING_OVER) && returnVal != Runners.NO_RETURN && stepOutFrame == frame) {
else if (
loc != null &&
(state == State.STEPPING_IN || state == State.STEPPING_OVER) &&
returnVal != Values.NO_RETURN && stepOutFrame == frame
) {
pauseDebug(ctx, null);
}
else if (isBreakpointable && locToBreakpoint.containsKey(loc)) {
@@ -906,7 +984,7 @@ public class SimpleDebugger implements Debugger {
case STEPPING_IN:
case STEPPING_OVER:
if (stepOutFrame.frame == frame.frame) {
if (returnVal != Runners.NO_RETURN || error != null) {
if (returnVal != Values.NO_RETURN || error != null) {
state = State.STEPPING_OUT;
continue;
}
@@ -943,7 +1021,7 @@ public class SimpleDebugger implements Debugger {
try { idToFrame.remove(codeFrameToFrame.remove(frame).id); }
catch (NullPointerException e) { }
if (ctx.frames().size() == 0) {
if (ctx.stackSize == 0) {
if (state == State.PAUSED_EXCEPTION || state == State.PAUSED_NORMAL) resume(State.RESUMED);
}
else if (stepOutFrame != null && stepOutFrame.frame == frame && state == State.STEPPING_OUT) {
@@ -952,19 +1030,12 @@ public class SimpleDebugger implements Debugger {
}
}
@Override public synchronized void connect() {
if (!target.attachDebugger(this)) {
ws.send(new V8Error("A debugger is already attached to this engine."));
}
}
@Override public synchronized void disconnect() {
target.detachDebugger();
enabled = false;
updateNotifier.next();
public SimpleDebugger attach(DebugContext ctx) {
ctx.attachDebugger(this);
return this;
}
public SimpleDebugger(WebSocket ws, Engine target) {
public SimpleDebugger(WebSocket ws) {
this.ws = ws;
this.target = target;
}
}

View File

@@ -1,7 +1,7 @@
package me.topchetoeu.jscript.engine.debug;
package me.topchetoeu.jscript.core.engine.debug;
import me.topchetoeu.jscript.json.JSON;
import me.topchetoeu.jscript.json.JSONMap;
import me.topchetoeu.jscript.common.json.JSON;
import me.topchetoeu.jscript.common.json.JSONMap;
public class V8Error {
public final String message;

View File

@@ -1,7 +1,7 @@
package me.topchetoeu.jscript.engine.debug;
package me.topchetoeu.jscript.core.engine.debug;
import me.topchetoeu.jscript.json.JSON;
import me.topchetoeu.jscript.json.JSONMap;
import me.topchetoeu.jscript.common.json.JSON;
import me.topchetoeu.jscript.common.json.JSONMap;
public class V8Event {
public final String name;

View File

@@ -1,10 +1,10 @@
package me.topchetoeu.jscript.engine.debug;
package me.topchetoeu.jscript.core.engine.debug;
import java.util.Map;
import me.topchetoeu.jscript.json.JSON;
import me.topchetoeu.jscript.json.JSONElement;
import me.topchetoeu.jscript.json.JSONMap;
import me.topchetoeu.jscript.common.json.JSON;
import me.topchetoeu.jscript.common.json.JSONElement;
import me.topchetoeu.jscript.common.json.JSONMap;
public class V8Message {
public final String name;

View File

@@ -1,7 +1,7 @@
package me.topchetoeu.jscript.engine.debug;
package me.topchetoeu.jscript.core.engine.debug;
import me.topchetoeu.jscript.json.JSON;
import me.topchetoeu.jscript.json.JSONMap;
import me.topchetoeu.jscript.common.json.JSON;
import me.topchetoeu.jscript.common.json.JSONMap;
public class V8Result {
public final int id;

View File

@@ -0,0 +1,193 @@
package me.topchetoeu.jscript.core.engine.debug;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import me.topchetoeu.jscript.core.engine.debug.WebSocketMessage.Type;
public class WebSocket implements AutoCloseable {
public long maxLength = 1 << 20;
private Socket socket;
private boolean closed = false;
private OutputStream out() throws IOException {
return socket.getOutputStream();
}
private InputStream in() throws IOException {
return socket.getInputStream();
}
private long readLen(int byteLen) throws IOException {
long res = 0;
if (byteLen == 126) {
res |= in().read() << 8;
res |= in().read();
return res;
}
else if (byteLen == 127) {
res |= in().read() << 56;
res |= in().read() << 48;
res |= in().read() << 40;
res |= in().read() << 32;
res |= in().read() << 24;
res |= in().read() << 16;
res |= in().read() << 8;
res |= in().read();
return res;
}
else return byteLen;
}
private byte[] readMask(boolean has) throws IOException {
if (has) {
return new byte[] {
(byte)in().read(),
(byte)in().read(),
(byte)in().read(),
(byte)in().read()
};
}
else return new byte[4];
}
private void writeLength(int len) throws IOException {
if (len < 126) {
out().write((int)len);
}
else if (len <= 0xFFFF) {
out().write(126);
out().write((int)(len >> 8) & 0xFF);
out().write((int)len & 0xFF);
}
else {
out().write(127);
out().write((len >> 56) & 0xFF);
out().write((len >> 48) & 0xFF);
out().write((len >> 40) & 0xFF);
out().write((len >> 32) & 0xFF);
out().write((len >> 24) & 0xFF);
out().write((len >> 16) & 0xFF);
out().write((len >> 8) & 0xFF);
out().write(len & 0xFF);
}
}
private synchronized void write(int type, byte[] data) throws IOException {
int i;
for (i = 0; i < data.length / 0xFFFF; i++) {
out().write(type);
writeLength(0xFFFF);
out().write(data, i * 0xFFFF, 0xFFFF);
type = 0;
}
out().write(type | 0x80);
writeLength(data.length % 0xFFFF);
out().write(data, i * 0xFFFF, data.length % 0xFFFF);
}
public void send(String data) throws IOException {
if (closed) throw new IllegalStateException("Object is closed.");
write(1, data.getBytes());
}
public void send(byte[] data) throws IOException {
if (closed) throw new IllegalStateException("Object is closed.");
write(2, data);
}
public void send(WebSocketMessage msg) throws IOException {
if (msg.type == Type.Binary) send(msg.binaryData());
else send(msg.textData());
}
public void send(Object data) throws IOException {
if (closed) throw new IllegalStateException("Object is closed.");
write(1, data.toString().getBytes());
}
public void close(String reason) {
if (socket != null) {
try {
write(8, reason.getBytes());
socket.close();
}
catch (Throwable e) { }
}
socket = null;
closed = true;
}
public void close() {
close("");
}
private WebSocketMessage fail(String reason) {
System.out.println("WebSocket Error: " + reason);
close(reason);
return null;
}
private byte[] readData() throws IOException {
var maskLen = in().read();
var hasMask = (maskLen & 0x80) != 0;
var len = (int)readLen(maskLen & 0x7F);
var mask = readMask(hasMask);
if (len > maxLength) fail("WebSocket Error: client exceeded configured max message size");
else {
var buff = new byte[len];
if (in().read(buff) < len) fail("WebSocket Error: payload too short");
else {
for (int i = 0; i < len; i++) {
buff[i] ^= mask[(int)(i % 4)];
}
return buff;
}
}
return null;
}
public WebSocketMessage receive() throws IOException {
var data = new ByteArrayOutputStream();
var type = 0;
while (socket != null && !closed) {
var finId = in().read();
if (finId < 0) break;
var fin = (finId & 0x80) != 0;
int id = finId & 0x0F;
if (id == 0x8) { close(); return null; }
if (id >= 0x8) {
if (!fin) return fail("WebSocket Error: client-sent control frame was fragmented");
if (id == 0x9) write(0xA, data.toByteArray());
continue;
}
if (type == 0) type = id;
if (type == 0) return fail("WebSocket Error: client used opcode 0x00 for first fragment");
var buff = readData();
if (buff == null) break;
if (data.size() + buff.length > maxLength) return fail("WebSocket Error: client exceeded configured max message size");
data.write(buff);
if (!fin) continue;
var raw = data.toByteArray();
if (type == 1) return new WebSocketMessage(new String(raw));
else return new WebSocketMessage(raw);
}
return null;
}
public WebSocket(Socket socket) {
this.socket = socket;
}
}

View File

@@ -1,4 +1,4 @@
package me.topchetoeu.jscript.engine.debug;
package me.topchetoeu.jscript.core.engine.debug;
public class WebSocketMessage {
public static enum Type {

View File

@@ -1,20 +1,21 @@
package me.topchetoeu.jscript.engine.frame;
package me.topchetoeu.jscript.core.engine.frame;
import java.util.List;
import java.util.Stack;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.engine.scope.LocalScope;
import me.topchetoeu.jscript.engine.scope.ValueVariable;
import me.topchetoeu.jscript.engine.values.ArrayValue;
import me.topchetoeu.jscript.engine.values.CodeFunction;
import me.topchetoeu.jscript.engine.values.ObjectValue;
import me.topchetoeu.jscript.engine.values.ScopeValue;
import me.topchetoeu.jscript.engine.values.Values;
import me.topchetoeu.jscript.exceptions.EngineException;
import me.topchetoeu.jscript.exceptions.InterruptException;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.core.compilation.Instruction;
import me.topchetoeu.jscript.core.engine.Context;
import me.topchetoeu.jscript.core.engine.debug.DebugContext;
import me.topchetoeu.jscript.core.engine.scope.LocalScope;
import me.topchetoeu.jscript.core.engine.scope.ValueVariable;
import me.topchetoeu.jscript.core.engine.values.ArrayValue;
import me.topchetoeu.jscript.core.engine.values.CodeFunction;
import me.topchetoeu.jscript.core.engine.values.ObjectValue;
import me.topchetoeu.jscript.core.engine.values.ScopeValue;
import me.topchetoeu.jscript.core.engine.values.Values;
import me.topchetoeu.jscript.core.exceptions.EngineException;
import me.topchetoeu.jscript.core.exceptions.InterruptException;
public class CodeFrame {
public static enum TryState {
@@ -37,8 +38,10 @@ public class CodeFrame {
return ptr >= start && ptr < end;
}
public void setCause(EngineException target) {
if (error != null) target.setCause(error);
}
public TryCtx _catch(EngineException e) {
if (error != null) e.setCause(error);
return new TryCtx(TryState.CATCH, e, result, restoreStackPtr, start, end, -1, finallyStart);
}
public TryCtx _finally(PendingResult res) {
@@ -93,13 +96,14 @@ public class CodeFrame {
public final Object[] args;
public final Stack<TryCtx> tryStack = new Stack<>();
public final CodeFunction function;
public final Context ctx;
public Object[] stack = new Object[32];
public int stackPtr = 0;
public int codePtr = 0;
public boolean jumpFlag = false, popTryFlag = false;
private Location prevLoc = null;
public ObjectValue getLocalScope(Context ctx, boolean props) {
public ObjectValue getLocalScope(boolean props) {
var names = new String[scope.locals.length];
for (int i = 0; i < scope.locals.length; i++) {
@@ -114,7 +118,7 @@ public class CodeFrame {
return new ScopeValue(scope.locals, names);
}
public ObjectValue getCaptureScope(Context ctx, boolean props) {
public ObjectValue getCaptureScope(boolean props) {
var names = new String[scope.captures.length];
for (int i = 0; i < scope.captures.length; i++) {
@@ -125,7 +129,7 @@ public class CodeFrame {
return new ScopeValue(scope.captures, names);
}
public ObjectValue getValStackScope(Context ctx) {
public ObjectValue getValStackScope() {
return new ObjectValue() {
@Override
protected Object getField(Context ctx, Object key) {
@@ -177,7 +181,7 @@ public class CodeFrame {
return res;
}
public void push(Context ctx, Object val) {
public void push(Object val) {
if (stack.length <= stackPtr) {
var newStack = new Object[stack.length * 2];
System.arraycopy(stack, 0, newStack, 0, stack.length);
@@ -186,20 +190,19 @@ public class CodeFrame {
stack[stackPtr++] = Values.normalize(ctx, val);
}
public Object next(Context ctx, Object value, Object returnValue, EngineException error) {
if (value != Runners.NO_RETURN) push(ctx, value);
public Object next(Object value, Object returnValue, EngineException error) {
if (value != Values.NO_RETURN) push(value);
Instruction instr = null;
if (codePtr >= 0 && codePtr < function.body.length) instr = function.body[codePtr];
if (returnValue == Runners.NO_RETURN && error == null) {
if (returnValue == Values.NO_RETURN && error == null) {
try {
if (Thread.currentThread().isInterrupted()) throw new InterruptException();
if (instr == null) returnValue = null;
else {
// System.out.println(instr + "@" + instr.location);
ctx.engine.onInstruction(ctx, this, instr, Runners.NO_RETURN, null, false);
DebugContext.get(ctx).onInstruction(ctx, this, instr, Values.NO_RETURN, null, false);
if (instr.location != null) prevLoc = instr.location;
@@ -220,10 +223,11 @@ public class CodeFrame {
TryCtx newCtx = null;
if (error != null) {
tryCtx.setCause(error);
if (tryCtx.hasCatch()) newCtx = tryCtx._catch(error);
else if (tryCtx.hasFinally()) newCtx = tryCtx._finally(PendingResult.ofThrow(error, instr));
}
else if (returnValue != Runners.NO_RETURN) {
else if (returnValue != Values.NO_RETURN) {
if (tryCtx.hasFinally()) newCtx = tryCtx._finally(PendingResult.ofReturn(returnValue, instr));
}
else if (jumpFlag && !tryCtx.inBounds(codePtr)) {
@@ -250,7 +254,7 @@ public class CodeFrame {
tryStack.push(newCtx);
}
error = null;
returnValue = Runners.NO_RETURN;
returnValue = Values.NO_RETURN;
break;
}
else {
@@ -268,15 +272,17 @@ public class CodeFrame {
tryStack.pop();
codePtr = tryCtx.end;
if (tryCtx.result.instruction != null) instr = tryCtx.result.instruction;
if (tryCtx.result.isJump) {
codePtr = tryCtx.result.ptr;
jumpFlag = true;
if (!jumpFlag && returnValue == Values.NO_RETURN && error == null) {
if (tryCtx.result.isJump) {
codePtr = tryCtx.result.ptr;
jumpFlag = true;
}
if (tryCtx.result.isReturn) returnValue = tryCtx.result.value;
if (error == null && tryCtx.result.isThrow) {
error = tryCtx.result.error;
}
}
if (tryCtx.result.isReturn) returnValue = tryCtx.result.value;
if (tryCtx.result.isThrow) {
error = tryCtx.result.error;
}
if (error != null) error.setCause(tryCtx.error);
if (error != null) tryCtx.setCause(error);
continue;
}
}
@@ -291,15 +297,22 @@ public class CodeFrame {
}
}
ctx.engine.onInstruction(ctx, this, instr, null, error, caught);
DebugContext.get(ctx).onInstruction(ctx, this, instr, null, error, caught);
throw error;
}
if (returnValue != Runners.NO_RETURN) {
ctx.engine.onInstruction(ctx, this, instr, returnValue, null, false);
if (returnValue != Values.NO_RETURN) {
DebugContext.get(ctx).onInstruction(ctx, this, instr, returnValue, null, false);
return returnValue;
}
return Runners.NO_RETURN;
return Values.NO_RETURN;
}
public void onPush() {
DebugContext.get(ctx).onFramePush(ctx, this);
}
public void onPop() {
DebugContext.get(ctx.parent).onFramePop(ctx.parent, this);
}
public CodeFrame(Context ctx, Object thisArg, Object[] args, CodeFunction func) {
@@ -314,5 +327,6 @@ public class CodeFrame {
this.thisArg = thisArg;
this.function = func;
this.ctx = ctx.pushFrame(this);
}
}

View File

@@ -1,4 +1,4 @@
package me.topchetoeu.jscript.engine.frame;
package me.topchetoeu.jscript.core.engine.frame;
public enum ConvertHint {
TOSTRING,

View File

@@ -1,22 +1,22 @@
package me.topchetoeu.jscript.engine.frame;
package me.topchetoeu.jscript.core.engine.frame;
import java.util.Collections;
import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.engine.Engine;
import me.topchetoeu.jscript.engine.Operation;
import me.topchetoeu.jscript.engine.scope.ValueVariable;
import me.topchetoeu.jscript.engine.values.ArrayValue;
import me.topchetoeu.jscript.engine.values.CodeFunction;
import me.topchetoeu.jscript.engine.values.ObjectValue;
import me.topchetoeu.jscript.engine.values.Symbol;
import me.topchetoeu.jscript.engine.values.Values;
import me.topchetoeu.jscript.exceptions.EngineException;
import me.topchetoeu.jscript.core.compilation.Instruction;
import me.topchetoeu.jscript.core.engine.Context;
import me.topchetoeu.jscript.core.engine.Engine;
import me.topchetoeu.jscript.core.engine.Environment;
import me.topchetoeu.jscript.core.engine.Operation;
import me.topchetoeu.jscript.core.engine.scope.ValueVariable;
import me.topchetoeu.jscript.core.engine.values.ArrayValue;
import me.topchetoeu.jscript.core.engine.values.CodeFunction;
import me.topchetoeu.jscript.core.engine.values.FunctionValue;
import me.topchetoeu.jscript.core.engine.values.ObjectValue;
import me.topchetoeu.jscript.core.engine.values.Symbol;
import me.topchetoeu.jscript.core.engine.values.Values;
import me.topchetoeu.jscript.core.exceptions.EngineException;
public class Runners {
public static final Object NO_RETURN = new Object();
public static Object execReturn(Context ctx, Instruction instr, CodeFrame frame) {
return frame.pop();
}
@@ -32,26 +32,26 @@ public class Runners {
var func = frame.pop();
var thisArg = frame.pop();
frame.push(ctx, Values.call(ctx, func, thisArg, callArgs));
frame.push(Values.call(ctx, func, thisArg, callArgs));
frame.codePtr++;
return NO_RETURN;
return Values.NO_RETURN;
}
public static Object execCallNew(Context ctx, Instruction instr, CodeFrame frame) {
var callArgs = frame.take(instr.get(0));
var funcObj = frame.pop();
frame.push(ctx, Values.callNew(ctx, funcObj, callArgs));
frame.push(Values.callNew(ctx, funcObj, callArgs));
frame.codePtr++;
return NO_RETURN;
return Values.NO_RETURN;
}
public static Object execMakeVar(Context ctx, Instruction instr, CodeFrame frame) {
var name = (String)instr.get(0);
ctx.environment().global.define(name);
ctx.environment.global.define(name);
frame.codePtr++;
return NO_RETURN;
return Values.NO_RETURN;
}
public static Object execDefProp(Context ctx, Instruction instr, CodeFrame frame) {
var setter = frame.pop();
@@ -59,14 +59,14 @@ public class Runners {
var name = frame.pop();
var obj = frame.pop();
if (getter != null && !Values.isFunction(getter)) throw EngineException.ofType("Getter must be a function or undefined.");
if (setter != null && !Values.isFunction(setter)) throw EngineException.ofType("Setter must be a function or undefined.");
if (!Values.isObject(obj)) throw EngineException.ofType("Property apply target must be an object.");
if (getter != null && !(getter instanceof FunctionValue)) throw EngineException.ofType("Getter must be a function or undefined.");
if (setter != null && !(setter instanceof FunctionValue)) throw EngineException.ofType("Setter must be a function or undefined.");
if (!(obj instanceof ObjectValue)) throw EngineException.ofType("Property apply target must be an object.");
Values.object(obj).defineProperty(ctx, name, Values.function(getter), Values.function(setter), false, false);
frame.push(ctx, obj);
frame.push(obj);
frame.codePtr++;
return NO_RETURN;
return Values.NO_RETURN;
}
public static Object execInstanceof(Context ctx, Instruction instr, CodeFrame frame) {
var type = frame.pop();
@@ -74,14 +74,14 @@ public class Runners {
if (!Values.isPrimitive(type)) {
var proto = Values.getMember(ctx, type, "prototype");
frame.push(ctx, Values.isInstanceOf(ctx, obj, proto));
frame.push(Values.isInstanceOf(ctx, obj, proto));
}
else {
frame.push(ctx, false);
frame.push(false);
}
frame.codePtr++;
return NO_RETURN;
return Values.NO_RETURN;
}
public static Object execKeys(Context ctx, Instruction instr, CodeFrame frame) {
var val = frame.pop();
@@ -89,17 +89,17 @@ public class Runners {
var members = Values.getMembers(ctx, val, false, false);
Collections.reverse(members);
frame.push(ctx, null);
frame.push(null);
for (var el : members) {
if (el instanceof Symbol) continue;
var obj = new ObjectValue();
obj.defineProperty(ctx, "value", el);
frame.push(ctx, obj);
frame.push(obj);
}
frame.codePtr++;
return NO_RETURN;
return Values.NO_RETURN;
}
public static Object execTryStart(Context ctx, Instruction instr, CodeFrame frame) {
@@ -111,58 +111,58 @@ public class Runners {
int end = (int)instr.get(2) + start;
frame.addTry(start, end, catchStart, finallyStart);
frame.codePtr++;
return NO_RETURN;
return Values.NO_RETURN;
}
public static Object execTryEnd(Context ctx, Instruction instr, CodeFrame frame) {
frame.popTryFlag = true;
return NO_RETURN;
return Values.NO_RETURN;
}
public static Object execDup(Context ctx, Instruction instr, CodeFrame frame) {
int count = instr.get(0);
for (var i = 0; i < count; i++) {
frame.push(ctx, frame.peek(count - 1));
frame.push(frame.peek(count - 1));
}
frame.codePtr++;
return NO_RETURN;
return Values.NO_RETURN;
}
public static Object execLoadUndefined(Context ctx, Instruction instr, CodeFrame frame) {
frame.push(ctx, null);
frame.push(null);
frame.codePtr++;
return NO_RETURN;
return Values.NO_RETURN;
}
public static Object execLoadValue(Context ctx, Instruction instr, CodeFrame frame) {
frame.push(ctx, instr.get(0));
frame.push(instr.get(0));
frame.codePtr++;
return NO_RETURN;
return Values.NO_RETURN;
}
public static Object execLoadVar(Context ctx, Instruction instr, CodeFrame frame) {
var i = instr.get(0);
if (i instanceof String) frame.push(ctx, ctx.environment().global.get(ctx, (String)i));
else frame.push(ctx, frame.scope.get((int)i).get(ctx));
if (i instanceof String) frame.push(ctx.environment.global.get(ctx, (String)i));
else frame.push(frame.scope.get((int)i).get(ctx));
frame.codePtr++;
return NO_RETURN;
return Values.NO_RETURN;
}
public static Object execLoadObj(Context ctx, Instruction instr, CodeFrame frame) {
frame.push(ctx, new ObjectValue());
frame.push(new ObjectValue());
frame.codePtr++;
return NO_RETURN;
return Values.NO_RETURN;
}
public static Object execLoadGlob(Context ctx, Instruction instr, CodeFrame frame) {
frame.push(ctx, ctx.environment().global.obj);
frame.push(ctx.environment.global.obj);
frame.codePtr++;
return NO_RETURN;
return Values.NO_RETURN;
}
public static Object execLoadArr(Context ctx, Instruction instr, CodeFrame frame) {
var res = new ArrayValue();
res.setSize(instr.get(0));
frame.push(ctx, res);
frame.push(res);
frame.codePtr++;
return NO_RETURN;
return Values.NO_RETURN;
}
public static Object execLoadFunc(Context ctx, Instruction instr, CodeFrame frame) {
long id = (Long)instr.get(0);
@@ -172,40 +172,45 @@ public class Runners {
captures[i - 1] = frame.scope.get(instr.get(i));
}
var func = new CodeFunction(ctx.environment(), "", Engine.functions.get(id), captures);
var func = new CodeFunction(ctx.environment, "", Engine.functions.get(id), captures);
frame.push(ctx, func);
frame.push(func);
frame.codePtr++;
return NO_RETURN;
return Values.NO_RETURN;
}
public static Object execLoadMember(Context ctx, Instruction instr, CodeFrame frame) {
var key = frame.pop();
var obj = frame.pop();
try {
frame.push(ctx, Values.getMember(ctx, obj, key));
frame.push(Values.getMember(ctx, obj, key));
}
catch (IllegalArgumentException e) {
throw EngineException.ofType(e.getMessage());
}
frame.codePtr++;
return NO_RETURN;
return Values.NO_RETURN;
}
public static Object execLoadKeyMember(Context ctx, Instruction instr, CodeFrame frame) {
frame.push(ctx, instr.get(0));
frame.push(instr.get(0));
return execLoadMember(ctx, instr, frame);
}
public static Object execLoadRegEx(Context ctx, Instruction instr, CodeFrame frame) {
frame.push(ctx, ctx.environment().regexConstructor.call(ctx, null, instr.get(0), instr.get(1)));
if (ctx.hasNotNull(Environment.REGEX_CONSTR)) {
frame.push(Values.callNew(ctx, ctx.get(Environment.REGEX_CONSTR), instr.get(0), instr.get(1)));
}
else {
throw EngineException.ofSyntax("Regex is not supported.");
}
frame.codePtr++;
return NO_RETURN;
return Values.NO_RETURN;
}
public static Object execDiscard(Context ctx, Instruction instr, CodeFrame frame) {
frame.pop();
frame.codePtr++;
return NO_RETURN;
return Values.NO_RETURN;
}
public static Object execStoreMember(Context ctx, Instruction instr, CodeFrame frame) {
var val = frame.pop();
@@ -213,30 +218,30 @@ public class Runners {
var obj = frame.pop();
if (!Values.setMember(ctx, obj, key, val)) throw EngineException.ofSyntax("Can't set member '" + key + "'.");
if ((boolean)instr.get(0)) frame.push(ctx, val);
if ((boolean)instr.get(0)) frame.push(val);
frame.codePtr++;
return NO_RETURN;
return Values.NO_RETURN;
}
public static Object execStoreVar(Context ctx, Instruction instr, CodeFrame frame) {
var val = (boolean)instr.get(1) ? frame.peek() : frame.pop();
var i = instr.get(0);
if (i instanceof String) ctx.environment().global.set(ctx, (String)i, val);
if (i instanceof String) ctx.environment.global.set(ctx, (String)i, val);
else frame.scope.get((int)i).set(ctx, val);
frame.codePtr++;
return NO_RETURN;
return Values.NO_RETURN;
}
public static Object execStoreSelfFunc(Context ctx, Instruction instr, CodeFrame frame) {
frame.scope.locals[(int)instr.get(0)].set(ctx, frame.function);
frame.codePtr++;
return NO_RETURN;
return Values.NO_RETURN;
}
public static Object execJmp(Context ctx, Instruction instr, CodeFrame frame) {
frame.codePtr += (int)instr.get(0);
frame.jumpFlag = true;
return NO_RETURN;
return Values.NO_RETURN;
}
public static Object execJmpIf(Context ctx, Instruction instr, CodeFrame frame) {
if (Values.toBoolean(frame.pop())) {
@@ -244,7 +249,7 @@ public class Runners {
frame.jumpFlag = true;
}
else frame.codePtr ++;
return NO_RETURN;
return Values.NO_RETURN;
}
public static Object execJmpIfNot(Context ctx, Instruction instr, CodeFrame frame) {
if (Values.not(frame.pop())) {
@@ -252,37 +257,37 @@ public class Runners {
frame.jumpFlag = true;
}
else frame.codePtr ++;
return NO_RETURN;
return Values.NO_RETURN;
}
public static Object execIn(Context ctx, Instruction instr, CodeFrame frame) {
var obj = frame.pop();
var index = frame.pop();
frame.push(ctx, Values.hasMember(ctx, obj, index, false));
frame.push(Values.hasMember(ctx, obj, index, false));
frame.codePtr++;
return NO_RETURN;
return Values.NO_RETURN;
}
public static Object execTypeof(Context ctx, Instruction instr, CodeFrame frame) {
String name = instr.get(0);
Object obj;
if (name != null) {
if (ctx.environment().global.has(ctx, name)) {
obj = ctx.environment().global.get(ctx, name);
if (ctx.environment.global.has(ctx, name)) {
obj = ctx.environment.global.get(ctx, name);
}
else obj = null;
}
else obj = frame.pop();
frame.push(ctx, Values.type(obj));
frame.push(Values.type(obj));
frame.codePtr++;
return NO_RETURN;
return Values.NO_RETURN;
}
public static Object execNop(Context ctx, Instruction instr, CodeFrame frame) {
frame.codePtr++;
return NO_RETURN;
return Values.NO_RETURN;
}
public static Object execDelete(Context ctx, Instruction instr, CodeFrame frame) {
@@ -291,7 +296,7 @@ public class Runners {
if (!Values.deleteMember(ctx, val, key)) throw EngineException.ofSyntax("Can't delete member '" + key + "'.");
frame.codePtr++;
return NO_RETURN;
return Values.NO_RETURN;
}
public static Object execOperation(Context ctx, Instruction instr, CodeFrame frame) {
@@ -300,9 +305,9 @@ public class Runners {
for (var i = op.operands - 1; i >= 0; i--) args[i] = frame.pop();
frame.push(ctx, Values.operation(ctx, op, args));
frame.push(Values.operation(ctx, op, args));
frame.codePtr++;
return NO_RETURN;
return Values.NO_RETURN;
}
public static Object exec(Context ctx, Instruction instr, CodeFrame frame) {

View File

@@ -1,19 +1,20 @@
package me.topchetoeu.jscript.engine.scope;
package me.topchetoeu.jscript.core.engine.scope;
import java.util.HashSet;
import java.util.Set;
import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.engine.values.FunctionValue;
import me.topchetoeu.jscript.engine.values.NativeFunction;
import me.topchetoeu.jscript.engine.values.ObjectValue;
import me.topchetoeu.jscript.exceptions.EngineException;
import me.topchetoeu.jscript.core.engine.Context;
import me.topchetoeu.jscript.core.engine.values.FunctionValue;
import me.topchetoeu.jscript.core.engine.values.NativeFunction;
import me.topchetoeu.jscript.core.engine.values.ObjectValue;
import me.topchetoeu.jscript.core.engine.values.Values;
import me.topchetoeu.jscript.core.exceptions.EngineException;
public class GlobalScope implements ScopeRecord {
public final ObjectValue obj;
public boolean has(Context ctx, String name) {
return obj.hasMember(ctx, name, false);
return Values.hasMember(null, obj, name, false);
}
public Object getKey(String name) {
return name;
@@ -21,7 +22,7 @@ public class GlobalScope implements ScopeRecord {
public GlobalScope globalChild() {
var obj = new ObjectValue();
obj.setPrototype(null, this.obj);
Values.setPrototype(null, obj, this.obj);
return new GlobalScope(obj);
}
public LocalScopeRecord child() {
@@ -29,14 +30,14 @@ public class GlobalScope implements ScopeRecord {
}
public Object define(String name) {
if (obj.hasMember(null, name, true)) return name;
obj.defineProperty(null, name, null);
if (Values.hasMember(Context.NULL, obj, name, false)) return name;
obj.defineProperty(Context.NULL, name, null);
return name;
}
public void define(String name, Variable val) {
obj.defineProperty(null, name,
new NativeFunction("get " + name, (ctx, th, a) -> val.get(ctx)),
new NativeFunction("set " + name, (ctx, th, args) -> { val.set(ctx, args.length > 0 ? args[0] : null); return null; }),
obj.defineProperty(Context.NULL, name,
new NativeFunction("get " + name, args -> val.get(args.ctx)),
new NativeFunction("set " + name, args -> { val.set(args.ctx, args.get(0)); return null; }),
true, true
);
}
@@ -51,12 +52,12 @@ public class GlobalScope implements ScopeRecord {
}
public Object get(Context ctx, String name) {
if (!obj.hasMember(ctx, name, false)) throw EngineException.ofSyntax("The variable '" + name + "' doesn't exist.");
else return obj.getMember(ctx, name);
if (!Values.hasMember(ctx, obj, name, false)) throw EngineException.ofSyntax("The variable '" + name + "' doesn't exist.");
else return Values.getMember(ctx, obj, name);
}
public void set(Context ctx, String name, Object val) {
if (!obj.hasMember(ctx, name, false)) throw EngineException.ofSyntax("The variable '" + name + "' doesn't exist.");
if (!obj.setMember(ctx, name, val, false)) throw EngineException.ofSyntax("The global '" + name + "' is readonly.");
if (!Values.hasMember(ctx, obj, name, false)) throw EngineException.ofSyntax("The variable '" + name + "' doesn't exist.");
if (!Values.setMember(ctx, obj, name, val)) throw EngineException.ofSyntax("The global '" + name + "' is readonly.");
}
public Set<String> keys() {

View File

@@ -1,4 +1,4 @@
package me.topchetoeu.jscript.engine.scope;
package me.topchetoeu.jscript.core.engine.scope;
import java.util.ArrayList;

View File

@@ -1,4 +1,4 @@
package me.topchetoeu.jscript.engine.scope;
package me.topchetoeu.jscript.core.engine.scope;
import java.util.ArrayList;

View File

@@ -1,4 +1,4 @@
package me.topchetoeu.jscript.engine.scope;
package me.topchetoeu.jscript.core.engine.scope;
public interface ScopeRecord {
public Object getKey(String name);

View File

@@ -1,7 +1,7 @@
package me.topchetoeu.jscript.engine.scope;
package me.topchetoeu.jscript.core.engine.scope;
import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.engine.values.Values;
import me.topchetoeu.jscript.core.engine.Context;
import me.topchetoeu.jscript.core.engine.values.Values;
public class ValueVariable implements Variable {
public boolean readonly;

View File

@@ -1,6 +1,6 @@
package me.topchetoeu.jscript.engine.scope;
package me.topchetoeu.jscript.core.engine.scope;
import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.core.engine.Context;
public interface Variable {
Object get(Context ctx);

View File

@@ -1,4 +1,4 @@
package me.topchetoeu.jscript.engine.values;
package me.topchetoeu.jscript.core.engine.values;
import java.util.Arrays;
import java.util.Collection;
@@ -6,7 +6,7 @@ import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.core.engine.Context;
// TODO: Make methods generic
public class ArrayValue extends ObjectValue implements Iterable<Object> {
@@ -84,6 +84,8 @@ public class ArrayValue extends ObjectValue implements Iterable<Object> {
}
public void copyTo(Context ctx, ArrayValue arr, int sourceStart, int destStart, int count) {
// Iterate in reverse to reallocate at most once
if (destStart + count > arr.size) arr.size = destStart + count;
for (var i = count - 1; i >= 0; i--) {
if (i + sourceStart < 0 || i + sourceStart >= size) arr.remove(i + destStart);
if (values[i + sourceStart] == UNDEFINED) arr.set(ctx, i + destStart, null);

View File

@@ -1,13 +1,12 @@
package me.topchetoeu.jscript.engine.values;
package me.topchetoeu.jscript.core.engine.values;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.FunctionBody;
import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.engine.Environment;
import me.topchetoeu.jscript.engine.frame.CodeFrame;
import me.topchetoeu.jscript.engine.frame.Runners;
import me.topchetoeu.jscript.engine.scope.ValueVariable;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.core.compilation.FunctionBody;
import me.topchetoeu.jscript.core.compilation.Instruction;
import me.topchetoeu.jscript.core.engine.Context;
import me.topchetoeu.jscript.core.engine.Environment;
import me.topchetoeu.jscript.core.engine.frame.CodeFrame;
import me.topchetoeu.jscript.core.engine.scope.ValueVariable;
public class CodeFunction extends FunctionValue {
public final int localsN;
@@ -32,16 +31,17 @@ public class CodeFunction extends FunctionValue {
@Override
public Object call(Context ctx, Object thisArg, Object ...args) {
var frame = new CodeFrame(ctx, thisArg, args, this);
try {
ctx.pushFrame(frame);
frame.onPush();
try {
while (true) {
var res = frame.next(ctx, Runners.NO_RETURN, Runners.NO_RETURN, null);
if (res != Runners.NO_RETURN) return res;
var res = frame.next(Values.NO_RETURN, Values.NO_RETURN, null);
if (res != Values.NO_RETURN) return res;
}
}
finally {
ctx.popFrame(frame);
frame.onPop();
}
}

View File

@@ -1,8 +1,8 @@
package me.topchetoeu.jscript.engine.values;
package me.topchetoeu.jscript.core.engine.values;
import java.util.List;
import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.core.engine.Context;
public abstract class FunctionValue extends ObjectValue {
public String name = "";

View File

@@ -1,17 +1,18 @@
package me.topchetoeu.jscript.engine.values;
package me.topchetoeu.jscript.core.engine.values;
import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.core.engine.Context;
import me.topchetoeu.jscript.utils.interop.Arguments;
public class NativeFunction extends FunctionValue {
public static interface NativeFunctionRunner {
Object run(Context ctx, Object thisArg, Object[] args);
Object run(Arguments args);
}
public final NativeFunctionRunner action;
@Override
public Object call(Context ctx, Object thisArg, Object ...args) {
return action.run(ctx, thisArg, args);
return action.run(new Arguments(ctx, thisArg, args));
}
public NativeFunction(String name, NativeFunctionRunner action) {

View File

@@ -1,6 +1,6 @@
package me.topchetoeu.jscript.engine.values;
package me.topchetoeu.jscript.core.engine.values;
import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.core.engine.Context;
public class NativeWrapper extends ObjectValue {
private static final Object NATIVE_PROTO = new Object();
@@ -8,7 +8,7 @@ public class NativeWrapper extends ObjectValue {
@Override
public ObjectValue getPrototype(Context ctx) {
if (prototype == NATIVE_PROTO) return ctx.environment().wrappers.getProto(wrapped.getClass());
if (prototype == NATIVE_PROTO) return ctx.environment.wrappers.getProto(wrapped.getClass());
else return super.getPrototype(ctx);
}

View File

@@ -1,4 +1,4 @@
package me.topchetoeu.jscript.engine.values;
package me.topchetoeu.jscript.core.engine.values;
import java.util.ArrayList;
import java.util.LinkedHashMap;
@@ -6,7 +6,8 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.core.engine.Context;
import me.topchetoeu.jscript.core.engine.Environment;
public class ObjectValue {
public static enum PlaceholderProto {
@@ -53,6 +54,13 @@ public class ObjectValue {
public LinkedHashSet<Object> nonConfigurableSet = new LinkedHashSet<>();
public LinkedHashSet<Object> nonEnumerableSet = new LinkedHashSet<>();
private Property getProperty(Context ctx, Object key) {
if (properties.containsKey(key)) return properties.get(key);
var proto = getPrototype(ctx);
if (proto != null) return proto.getProperty(ctx, key);
else return null;
}
public final boolean memberWritable(Object key) {
if (state == State.FROZEN) return false;
return !values.containsKey(key) || !nonWritableSet.contains(key);
@@ -146,45 +154,18 @@ public class ObjectValue {
public ObjectValue getPrototype(Context ctx) {
try {
if (prototype == OBJ_PROTO) return ctx.environment().proto("object");
if (prototype == ARR_PROTO) return ctx.environment().proto("array");
if (prototype == FUNC_PROTO) return ctx.environment().proto("function");
if (prototype == ERR_PROTO) return ctx.environment().proto("error");
if (prototype == RANGE_ERR_PROTO) return ctx.environment().proto("rangeErr");
if (prototype == SYNTAX_ERR_PROTO) return ctx.environment().proto("syntaxErr");
if (prototype == TYPE_ERR_PROTO) return ctx.environment().proto("typeErr");
if (prototype == OBJ_PROTO) return ctx.get(Environment.OBJECT_PROTO);
if (prototype == ARR_PROTO) return ctx.get(Environment.ARRAY_PROTO);
if (prototype == FUNC_PROTO) return ctx.get(Environment.FUNCTION_PROTO);
if (prototype == ERR_PROTO) return ctx.get(Environment.ERROR_PROTO);
if (prototype == RANGE_ERR_PROTO) return ctx.get(Environment.RANGE_ERR_PROTO);
if (prototype == SYNTAX_ERR_PROTO) return ctx.get(Environment.SYNTAX_ERR_PROTO);
if (prototype == TYPE_ERR_PROTO) return ctx.get(Environment.TYPE_ERR_PROTO);
}
catch (NullPointerException e) { return null; }
return (ObjectValue)prototype;
}
public final boolean setPrototype(Context ctx, Object val) {
val = Values.normalize(ctx, val);
if (!extensible()) return false;
if (val == null || val == Values.NULL) {
prototype = null;
return true;
}
else if (Values.isObject(val)) {
var obj = Values.object(val);
if (ctx != null && ctx.environment() != null) {
if (obj == ctx.environment().proto("object")) prototype = OBJ_PROTO;
else if (obj == ctx.environment().proto("array")) prototype = ARR_PROTO;
else if (obj == ctx.environment().proto("function")) prototype = FUNC_PROTO;
else if (obj == ctx.environment().proto("error")) prototype = ERR_PROTO;
else if (obj == ctx.environment().proto("syntaxErr")) prototype = SYNTAX_ERR_PROTO;
else if (obj == ctx.environment().proto("typeErr")) prototype = TYPE_ERR_PROTO;
else if (obj == ctx.environment().proto("rangeErr")) prototype = RANGE_ERR_PROTO;
else prototype = obj;
}
else prototype = obj;
return true;
}
return false;
}
public final boolean setPrototype(PlaceholderProto val) {
if (!extensible()) return false;
switch (val) {
@@ -200,18 +181,21 @@ public class ObjectValue {
return true;
}
protected Property getProperty(Context ctx, Object key) {
if (properties.containsKey(key)) return properties.get(key);
var proto = getPrototype(ctx);
if (proto != null) return proto.getProperty(ctx, key);
else return null;
}
/**
* A method, used to get the value of a field. If a property is bound to
* this key, but not a field, this method should return null.
*/
protected Object getField(Context ctx, Object key) {
if (values.containsKey(key)) return values.get(key);
var proto = getPrototype(ctx);
if (proto != null) return proto.getField(ctx, key);
else return null;
}
/**
* Changes the value of a field, that is bound to the given key. If no field is
* bound to this key, a new field should be created with the given value
* @return Whether or not the operation was successful
*/
protected boolean setField(Context ctx, Object key, Object val) {
if (val instanceof FunctionValue && ((FunctionValue)val).name.equals("")) {
((FunctionValue)val).name = Values.toString(ctx, key);
@@ -220,9 +204,16 @@ public class ObjectValue {
values.put(key, val);
return true;
}
/**
* Deletes the field bound to the given key.
*/
protected void deleteField(Context ctx, Object key) {
values.remove(key);
}
/**
* Returns whether or not there is a field bound to the given key.
* This must ignore properties
*/
protected boolean hasField(Context ctx, Object key) {
return values.containsKey(key);
}
@@ -243,10 +234,6 @@ public class ObjectValue {
}
else return getField(ctx, key);
}
public final Object getMember(Context ctx, Object key) {
return getMember(ctx, key, this);
}
public final boolean setMember(Context ctx, Object key, Object val, Object thisArg, boolean onlyProps) {
key = Values.normalize(ctx, key); val = Values.normalize(ctx, val);
@@ -266,10 +253,6 @@ public class ObjectValue {
else if (nonWritableSet.contains(key)) return false;
else return setField(ctx, key, val);
}
public final boolean setMember(Context ctx, Object key, Object val, boolean onlyProps) {
return setMember(ctx, Values.normalize(ctx, key), Values.normalize(ctx, val), this, onlyProps);
}
public final boolean hasMember(Context ctx, Object key, boolean own) {
key = Values.normalize(ctx, key);
@@ -290,6 +273,33 @@ public class ObjectValue {
deleteField(ctx, key);
return true;
}
public final boolean setPrototype(Context ctx, Object val) {
val = Values.normalize(ctx, val);
if (!extensible()) return false;
if (val == null || val == Values.NULL) {
prototype = null;
return true;
}
else if (val instanceof ObjectValue) {
var obj = Values.object(val);
if (ctx != null) {
if (obj == ctx.get(Environment.OBJECT_PROTO)) prototype = OBJ_PROTO;
else if (obj == ctx.get(Environment.ARRAY_PROTO)) prototype = ARR_PROTO;
else if (obj == ctx.get(Environment.FUNCTION_PROTO)) prototype = FUNC_PROTO;
else if (obj == ctx.get(Environment.ERROR_PROTO)) prototype = ERR_PROTO;
else if (obj == ctx.get(Environment.SYNTAX_ERR_PROTO)) prototype = SYNTAX_ERR_PROTO;
else if (obj == ctx.get(Environment.TYPE_ERR_PROTO)) prototype = TYPE_ERR_PROTO;
else if (obj == ctx.get(Environment.RANGE_ERR_PROTO)) prototype = RANGE_ERR_PROTO;
else prototype = obj;
}
else prototype = obj;
return true;
}
return false;
}
public final ObjectValue getMemberDescriptor(Context ctx, Object key) {
key = Values.normalize(ctx, key);

View File

@@ -1,10 +1,10 @@
package me.topchetoeu.jscript.engine.values;
package me.topchetoeu.jscript.core.engine.values;
import java.util.HashMap;
import java.util.List;
import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.engine.scope.ValueVariable;
import me.topchetoeu.jscript.core.engine.Context;
import me.topchetoeu.jscript.core.engine.scope.ValueVariable;
public class ScopeValue extends ObjectValue {
public final ValueVariable[] variables;
@@ -23,7 +23,7 @@ public class ScopeValue extends ObjectValue {
}
var proto = getPrototype(ctx);
if (proto != null && proto.hasField(ctx, key) && proto.setField(ctx, key, val)) return true;
if (proto != null && proto.hasMember(ctx, key, false) && proto.setField(ctx, key, val)) return true;
return super.setField(ctx, key, val);
}

View File

@@ -0,0 +1,28 @@
package me.topchetoeu.jscript.core.engine.values;
import java.util.HashMap;
public final class Symbol {
private static final HashMap<String, Symbol> registry = new HashMap<>();
public final String value;
public Symbol(String value) {
this.value = value;
}
@Override
public String toString() {
if (value == null) return "Symbol";
else return "@@" + value;
}
public static Symbol get(String name) {
if (registry.containsKey(name)) return registry.get(name);
else {
var res = new Symbol(name);
registry.put(name, res);
return res;
}
}
}

View File

@@ -1,5 +1,7 @@
package me.topchetoeu.jscript.engine.values;
package me.topchetoeu.jscript.core.engine.values;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.util.ArrayList;
@@ -10,13 +12,13 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.engine.Operation;
import me.topchetoeu.jscript.engine.frame.ConvertHint;
import me.topchetoeu.jscript.exceptions.ConvertException;
import me.topchetoeu.jscript.exceptions.EngineException;
import me.topchetoeu.jscript.exceptions.SyntaxException;
import me.topchetoeu.jscript.exceptions.UncheckedException;
import me.topchetoeu.jscript.core.engine.Context;
import me.topchetoeu.jscript.core.engine.Environment;
import me.topchetoeu.jscript.core.engine.Operation;
import me.topchetoeu.jscript.core.engine.frame.ConvertHint;
import me.topchetoeu.jscript.core.exceptions.ConvertException;
import me.topchetoeu.jscript.core.exceptions.EngineException;
import me.topchetoeu.jscript.core.exceptions.SyntaxException;
import me.topchetoeu.jscript.lib.PromiseLib;
public class Values {
@@ -39,10 +41,8 @@ public class Values {
}
public static final Object NULL = new Object();
public static final Object NO_RETURN = new Object();
public static boolean isObject(Object val) { return val instanceof ObjectValue; }
public static boolean isFunction(Object val) { return val instanceof FunctionValue; }
public static boolean isArray(Object val) { return val instanceof ArrayValue; }
public static boolean isWrapper(Object val) { return val instanceof NativeWrapper; }
public static boolean isWrapper(Object val, Class<?> clazz) {
if (!isWrapper(val)) return false;
@@ -89,8 +89,8 @@ public class Values {
private static Object tryCallConvertFunc(Context ctx, Object obj, String name) {
var func = getMember(ctx, obj, name);
if (func != null) {
var res = ((FunctionValue)func).call(ctx, obj);
if (func instanceof FunctionValue) {
var res = Values.call(ctx, func, obj);
if (isPrimitive(res)) return res;
}
@@ -136,7 +136,6 @@ public class Values {
if (val instanceof String) {
try { return Double.parseDouble((String)val); }
catch (NumberFormatException e) { return Double.NaN; }
catch (Throwable e) { throw new UncheckedException(e); }
}
return Double.NaN;
}
@@ -284,7 +283,7 @@ public class Values {
obj = normalize(ctx, obj); key = normalize(ctx, key);
if (obj == null) throw new IllegalArgumentException("Tried to access member of undefined.");
if (obj == NULL) throw new IllegalArgumentException("Tried to access member of null.");
if (isObject(obj)) return object(obj).getMember(ctx, key);
if (obj instanceof ObjectValue) return ((ObjectValue)obj).getMember(ctx, key, obj);
if (obj instanceof String && key instanceof Number) {
var i = number(key);
@@ -310,7 +309,7 @@ public class Values {
if (obj == null) throw EngineException.ofType("Tried to access member of undefined.");
if (obj == NULL) throw EngineException.ofType("Tried to access member of null.");
if (key != null && "__proto__".equals(key)) return setPrototype(ctx, obj, val);
if (isObject(obj)) return object(obj).setMember(ctx, key, val, false);
if (obj instanceof ObjectValue) return ((ObjectValue)obj).setMember(ctx, key, val, obj, false);
var proto = getPrototype(ctx, obj);
return proto.setMember(ctx, key, val, obj, true);
@@ -320,7 +319,7 @@ public class Values {
obj = normalize(ctx, obj); key = normalize(ctx, key);
if ("__proto__".equals(key)) return true;
if (isObject(obj)) return object(obj).hasMember(ctx, key, own);
if (obj instanceof ObjectValue) return object(obj).hasMember(ctx, key, own);
if (obj instanceof String && key instanceof Number) {
var i = number(key);
@@ -337,30 +336,35 @@ public class Values {
if (obj == null || obj == NULL) return false;
obj = normalize(ctx, obj); key = normalize(ctx, key);
if (isObject(obj)) return object(obj).deleteMember(ctx, key);
if (obj instanceof ObjectValue) return object(obj).deleteMember(ctx, key);
else return false;
}
public static ObjectValue getPrototype(Context ctx, Object obj) {
if (obj == null || obj == NULL) return null;
obj = normalize(ctx, obj);
if (isObject(obj)) return object(obj).getPrototype(ctx);
if (obj instanceof ObjectValue) return ((ObjectValue)obj).getPrototype(ctx);
if (ctx == null) return null;
if (obj instanceof String) return ctx.environment().proto("string");
else if (obj instanceof Number) return ctx.environment().proto("number");
else if (obj instanceof Boolean) return ctx.environment().proto("bool");
else if (obj instanceof Symbol) return ctx.environment().proto("symbol");
if (obj instanceof String) return ctx.get(Environment.STRING_PROTO);
else if (obj instanceof Number) return ctx.get(Environment.NUMBER_PROTO);
else if (obj instanceof Boolean) return ctx.get(Environment.BOOL_PROTO);
else if (obj instanceof Symbol) return ctx.get(Environment.SYMBOL_PROTO);
return null;
}
public static boolean setPrototype(Context ctx, Object obj, Object proto) {
obj = normalize(ctx, obj);
return isObject(obj) && object(obj).setPrototype(ctx, proto);
return obj instanceof ObjectValue && ((ObjectValue)obj).setPrototype(ctx, proto);
}
public static void makePrototypeChain(Context ctx, Object... chain) {
for(var i = 1; i < chain.length; i++) {
setPrototype(ctx, chain[i], chain[i - 1]);
}
}
public static List<Object> getMembers(Context ctx, Object obj, boolean own, boolean includeNonEnumerable) {
List<Object> res = new ArrayList<>();
if (isObject(obj)) res = object(obj).keys(includeNonEnumerable);
if (obj instanceof ObjectValue) res = object(obj).keys(includeNonEnumerable);
if (obj instanceof String) {
for (var i = 0; i < ((String)obj).length(); i++) res.add((double)i);
}
@@ -370,7 +374,7 @@ public class Values {
while (proto != null) {
res.addAll(proto.keys(includeNonEnumerable));
proto = proto.getPrototype(ctx);
proto = getPrototype(ctx, proto);
}
}
@@ -396,17 +400,17 @@ public class Values {
}
public static Object call(Context ctx, Object func, Object thisArg, Object ...args) {
if (!isFunction(func)) throw EngineException.ofType("Tried to call a non-function value.");
if (!(func instanceof FunctionValue)) throw EngineException.ofType("Tried to call a non-function value.");
return function(func).call(ctx, thisArg, args);
}
public static Object callNew(Context ctx, Object func, Object ...args) {
var res = new ObjectValue();
try {
var proto = Values.getMember(ctx, func, "prototype");
res.setPrototype(ctx, proto);
setPrototype(ctx, res, proto);
var ret = call(ctx, func, res, args);
if (ret != null && func instanceof FunctionValue && ((FunctionValue)func).special) return ret;
return res;
}
@@ -476,7 +480,7 @@ public class Values {
if (val instanceof Class) {
if (ctx == null) return null;
else return ctx.environment().wrappers.getConstr((Class<?>)val);
else return ctx.environment.wrappers.getConstr((Class<?>)val);
}
return new NativeWrapper(val);
@@ -543,6 +547,9 @@ public class Values {
if (obj == null) return null;
if (clazz.isInstance(obj)) return (T)obj;
if (clazz.isAssignableFrom(NativeWrapper.class)) {
return (T)new NativeWrapper(obj);
}
throw new ConvertException(type(obj), clazz.getSimpleName());
}
@@ -550,16 +557,16 @@ public class Values {
public static Iterable<Object> fromJSIterator(Context ctx, Object obj) {
return () -> {
try {
var symbol = ctx.environment().symbol("Symbol.iterator");
var symbol = Symbol.get("Symbol.iterator");
var iteratorFunc = getMember(ctx, obj, symbol);
if (!isFunction(iteratorFunc)) return Collections.emptyIterator();
if (!(iteratorFunc instanceof FunctionValue)) return Collections.emptyIterator();
var iterator = iteratorFunc instanceof FunctionValue ?
((FunctionValue)iteratorFunc).call(ctx, obj, obj) :
iteratorFunc;
var nextFunc = getMember(ctx, call(ctx, iteratorFunc, obj), "next");
if (!isFunction(nextFunc)) return Collections.emptyIterator();
if (!(nextFunc instanceof FunctionValue)) return Collections.emptyIterator();
return new Iterator<Object>() {
private Object value = null;
@@ -569,11 +576,11 @@ public class Values {
private void loadNext() {
if (next == null) value = null;
else if (consumed) {
var curr = object(next.call(ctx, iterator));
var curr = next.call(ctx, iterator);
if (curr == null) { next = null; value = null; }
if (toBoolean(curr.getMember(ctx, "done"))) { next = null; value = null; }
if (toBoolean(Values.getMember(ctx, curr, "done"))) { next = null; value = null; }
else {
this.value = curr.getMember(ctx, "value");
this.value = Values.getMember(ctx, curr, "value");
consumed = false;
}
}
@@ -604,16 +611,16 @@ public class Values {
var res = new ObjectValue();
try {
var key = getMember(ctx, getMember(ctx, ctx.environment().proto("symbol"), "constructor"), "iterator");
res.defineProperty(ctx, key, new NativeFunction("", (_ctx, thisArg, args) -> thisArg));
var key = getMember(ctx, getMember(ctx, ctx.get(Environment.SYMBOL_PROTO), "constructor"), "iterator");
res.defineProperty(ctx, key, new NativeFunction("", args -> args.self));
}
catch (IllegalArgumentException | NullPointerException e) { }
res.defineProperty(ctx, "next", new NativeFunction("", (_ctx, _th, _args) -> {
res.defineProperty(ctx, "next", new NativeFunction("", args -> {
if (!it.hasNext()) return new ObjectValue(ctx, Map.of("done", true));
else {
var obj = new ObjectValue();
obj.defineProperty(_ctx, "value", it.next());
obj.defineProperty(args.ctx, "value", it.next());
return obj;
}
}));
@@ -629,17 +636,17 @@ public class Values {
var res = new ObjectValue();
try {
var key = getMemberPath(ctx, ctx.environment().proto("symbol"), "constructor", "asyncIterator");
res.defineProperty(ctx, key, new NativeFunction("", (_ctx, thisArg, args) -> thisArg));
var key = getMemberPath(ctx, ctx.get(Environment.SYMBOL_PROTO), "constructor", "asyncIterator");
res.defineProperty(ctx, key, new NativeFunction("", args -> args.self));
}
catch (IllegalArgumentException | NullPointerException e) { }
res.defineProperty(ctx, "next", new NativeFunction("", (_ctx, _th, _args) -> {
return PromiseLib.await(ctx, () -> {
res.defineProperty(ctx, "next", new NativeFunction("", args -> {
return PromiseLib.await(args.ctx, () -> {
if (!it.hasNext()) return new ObjectValue(ctx, Map.of("done", true));
else {
var obj = new ObjectValue();
obj.defineProperty(_ctx, "value", it.next());
obj.defineProperty(args.ctx, "value", it.next());
return obj;
}
});
@@ -658,104 +665,107 @@ public class Values {
if (protoObj.values.size() + protoObj.properties.size() != 1) return false;
return true;
}
private static void printValue(Context ctx, Object val, HashSet<Object> passed, int tab) {
if (tab == 0 && val instanceof String) {
System.out.print(val);
return;
}
private static String toReadable(Context ctx, Object val, HashSet<Object> passed, int tab) {
if (tab == 0 && val instanceof String) return (String)val;
if (passed.contains(val)) {
System.out.print("[circular]");
return;
}
if (passed.contains(val)) return "[circular]";
var printed = true;
var res = new StringBuilder();
if (val instanceof FunctionValue) {
System.out.print(val.toString());
res.append(val.toString());
var loc = val instanceof CodeFunction ? ((CodeFunction)val).loc() : null;
if (loc != null) System.out.print(" @ " + loc);
if (loc != null) res.append(" @ " + loc);
}
else if (val instanceof ArrayValue) {
System.out.print("[");
res.append("[");
var obj = ((ArrayValue)val);
for (int i = 0; i < obj.size(); i++) {
if (i != 0) System.out.print(", ");
else System.out.print(" ");
if (obj.has(i)) printValue(ctx, obj.get(i), passed, tab);
else System.out.print("<empty>");
if (i != 0) res.append(", ");
else res.append(" ");
if (obj.has(i)) res.append(toReadable(ctx, obj.get(i), passed, tab));
else res.append("<empty>");
}
System.out.print(" ] ");
res.append(" ] ");
}
else if (val instanceof NativeWrapper) {
var obj = ((NativeWrapper)val).wrapped;
System.out.print("Native " + obj.toString() + " ");
res.append("Native " + obj.toString() + " ");
}
else printed = false;
if (val instanceof ObjectValue) {
if (tab > 3) {
System.out.print("{...}");
return;
return "{...}";
}
passed.add(val);
var obj = (ObjectValue)val;
if (obj.values.size() + obj.properties.size() == 0 || isEmptyFunc(obj)) {
if (!printed) System.out.println("{}");
if (!printed) res.append("{}\n");
}
else {
System.out.println("{");
res.append("{\n");
for (var el : obj.values.entrySet()) {
for (int i = 0; i < tab + 1; i++) System.out.print(" ");
printValue(ctx, el.getKey(), passed, tab + 1);
System.out.print(": ");
printValue(ctx, el.getValue(), passed, tab + 1);
System.out.println(",");
res.append(toReadable(ctx, el.getKey(), passed, tab + 1));
res.append(": ");
res.append(toReadable(ctx, el.getValue(), passed, tab + 1));
res.append(",\n");
}
for (var el : obj.properties.entrySet()) {
for (int i = 0; i < tab + 1; i++) System.out.print(" ");
printValue(ctx, el.getKey(), passed, tab + 1);
System.out.println(": [prop],");
res.append(toReadable(ctx, el.getKey(), passed, tab + 1));
res.append(": [prop],\n");
}
for (int i = 0; i < tab; i++) System.out.print(" ");
System.out.print("}");
for (int i = 0; i < tab; i++) res.append(" ");
res.append("}");
}
passed.remove(val);
}
else if (val == null) System.out.print("undefined");
else if (val == Values.NULL) System.out.print("null");
else if (val instanceof String) System.out.print("'" + val + "'");
else System.out.print(Values.toString(ctx, val));
else if (val == null) return "undefined";
else if (val == Values.NULL) return "null";
else if (val instanceof String) return "'" + val + "'";
else return Values.toString(ctx, val);
return res.toString();
}
public static String toReadable(Context ctx, Object val) {
return toReadable(ctx, val, new HashSet<>(), 0);
}
public static String errorToReadable(RuntimeException err, String prefix) {
prefix = prefix == null ? "Uncaught" : "Uncaught " + prefix;
if (err instanceof EngineException) {
var ee = ((EngineException)err);
try {
return prefix + " " + ee.toString(new Context(ee.engine, ee.env));
}
catch (EngineException ex) {
return prefix + " " + toReadable(new Context(ee.engine, ee.env), ee.value);
}
}
else if (err instanceof SyntaxException) {
return prefix + " SyntaxError " + ((SyntaxException)err).msg;
}
else if (err.getCause() instanceof InterruptedException) return "";
else {
var str = new ByteArrayOutputStream();
err.printStackTrace(new PrintStream(str));
return prefix + " internal error " + str.toString();
}
}
public static void printValue(Context ctx, Object val) {
printValue(ctx, val, new HashSet<>(), 0);
System.out.print(toReadable(ctx, val));
}
public static void printError(RuntimeException err, String prefix) {
prefix = prefix == null ? "Uncaught" : "Uncaught " + prefix;
try {
if (err instanceof EngineException) {
var ee = ((EngineException)err);
System.out.println(prefix + " " + ee.toString(new Context(ee.engine, ee.env)));
}
else if (err instanceof SyntaxException) {
System.out.println("Syntax error:" + ((SyntaxException)err).msg);
}
else if (err.getCause() instanceof InterruptedException) return;
else {
System.out.println("Internal error ocurred:");
err.printStackTrace();
}
}
catch (EngineException ex) {
System.out.println("Uncaught ");
Values.printValue(null, ((EngineException)err).value);
System.out.println();
}
System.out.println(errorToReadable(err, prefix));
}
}

View File

@@ -1,4 +1,4 @@
package me.topchetoeu.jscript.exceptions;
package me.topchetoeu.jscript.core.exceptions;
public class ConvertException extends RuntimeException {
public final String source, target;

View File

@@ -1,15 +1,16 @@
package me.topchetoeu.jscript.exceptions;
package me.topchetoeu.jscript.core.exceptions;
import java.util.ArrayList;
import java.util.List;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.engine.Context;
import me.topchetoeu.jscript.engine.Engine;
import me.topchetoeu.jscript.engine.Environment;
import me.topchetoeu.jscript.engine.values.ObjectValue;
import me.topchetoeu.jscript.engine.values.Values;
import me.topchetoeu.jscript.engine.values.ObjectValue.PlaceholderProto;
import me.topchetoeu.jscript.common.Location;
import me.topchetoeu.jscript.core.engine.Context;
import me.topchetoeu.jscript.core.engine.Engine;
import me.topchetoeu.jscript.core.engine.Environment;
import me.topchetoeu.jscript.core.engine.debug.DebugContext;
import me.topchetoeu.jscript.core.engine.values.ObjectValue;
import me.topchetoeu.jscript.core.engine.values.Values;
import me.topchetoeu.jscript.core.engine.values.ObjectValue.PlaceholderProto;
public class EngineException extends RuntimeException {
public static class StackElement {
@@ -18,13 +19,13 @@ public class EngineException extends RuntimeException {
public final Context ctx;
public boolean visible() {
return ctx == null || ctx.environment() == null || ctx.environment().stackVisible;
return ctx == null || !ctx.get(Environment.HIDE_STACK, false);
}
public String toString() {
var res = "";
var loc = location;
if (loc != null && ctx != null && ctx.engine != null) loc = ctx.engine.mapToCompiled(loc);
if (loc != null && ctx != null && ctx.engine != null) loc = DebugContext.get(ctx).mapToCompiled(loc);
if (loc != null) res += "at " + loc.toString() + " ";
if (function != null && !function.equals("")) res += "in " + function + " ";
@@ -37,7 +38,7 @@ public class EngineException extends RuntimeException {
if (function.equals("")) function = null;
if (ctx == null) this.ctx = null;
else this.ctx = new Context(ctx.engine, ctx.environment());
else this.ctx = new Context(ctx.engine, ctx.environment);
this.location = location;
this.function = function;
}
@@ -52,7 +53,7 @@ public class EngineException extends RuntimeException {
public EngineException add(Context ctx, String name, Location location) {
var el = new StackElement(ctx, location, name);
if (el.function == null && el.location == null) return this;
setCtx(ctx.environment(), ctx.engine);
setCtx(ctx.environment, ctx.engine);
stackTrace.add(el);
return this;
}
@@ -65,6 +66,11 @@ public class EngineException extends RuntimeException {
if (this.engine == null) this.engine = engine;
return this;
}
public EngineException setCtx(Context ctx) {
if (this.env == null) this.env = ctx.environment;
if (this.engine == null) this.engine = ctx.engine;
return this;
}
public String toString(Context ctx) {
var ss = new StringBuilder();

View File

@@ -1,4 +1,4 @@
package me.topchetoeu.jscript.exceptions;
package me.topchetoeu.jscript.core.exceptions;
public class InterruptException extends RuntimeException {
public InterruptException() { }

View File

@@ -1,6 +1,6 @@
package me.topchetoeu.jscript.exceptions;
package me.topchetoeu.jscript.core.exceptions;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.common.Location;
public class SyntaxException extends RuntimeException {
public final Location loc;

Some files were not shown because too many files have changed in this diff Show More