feat: separate compilation and runtime
This commit is contained in:
parent
c4d44547c8
commit
52489ad3a8
@ -1,4 +1,6 @@
|
|||||||
package me.topchetoeu.jscript.core.engine.scope;
|
package me.topchetoeu.jscript.common;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.core.scope.LocalScopeRecord;
|
||||||
|
|
||||||
public interface ScopeRecord {
|
public interface ScopeRecord {
|
||||||
public Object getKey(String name);
|
public Object getKey(String name);
|
@ -1,27 +0,0 @@
|
|||||||
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 -> {});
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,6 @@
|
|||||||
package me.topchetoeu.jscript.common.events;
|
package me.topchetoeu.jscript.common.events;
|
||||||
|
|
||||||
public class DataNotifier<T> implements Awaitable<T> {
|
public class DataNotifier<T> {
|
||||||
private Notifier notifier = new Notifier();
|
private Notifier notifier = new Notifier();
|
||||||
private boolean isErr;
|
private boolean isErr;
|
||||||
private T val;
|
private T val;
|
||||||
|
@ -5,16 +5,16 @@ import java.util.List;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Filename;
|
import me.topchetoeu.jscript.common.Filename;
|
||||||
import me.topchetoeu.jscript.core.engine.Context;
|
import me.topchetoeu.jscript.compilation.parsing.Operator;
|
||||||
import me.topchetoeu.jscript.core.engine.values.ArrayValue;
|
import me.topchetoeu.jscript.compilation.parsing.ParseRes;
|
||||||
import me.topchetoeu.jscript.core.engine.values.ObjectValue;
|
import me.topchetoeu.jscript.compilation.parsing.Parsing;
|
||||||
import me.topchetoeu.jscript.core.engine.values.Values;
|
import me.topchetoeu.jscript.compilation.parsing.Token;
|
||||||
|
import me.topchetoeu.jscript.core.Context;
|
||||||
|
import me.topchetoeu.jscript.core.values.ArrayValue;
|
||||||
|
import me.topchetoeu.jscript.core.values.ObjectValue;
|
||||||
|
import me.topchetoeu.jscript.core.values.Values;
|
||||||
import me.topchetoeu.jscript.core.exceptions.EngineException;
|
import me.topchetoeu.jscript.core.exceptions.EngineException;
|
||||||
import me.topchetoeu.jscript.core.exceptions.SyntaxException;
|
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 class JSON {
|
||||||
public static Object toJs(JSONElement val) {
|
public static Object toJs(JSONElement val) {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package me.topchetoeu.jscript.core.compilation;
|
package me.topchetoeu.jscript.compilation;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Location;
|
import me.topchetoeu.jscript.common.Location;
|
||||||
import me.topchetoeu.jscript.core.engine.Operation;
|
import me.topchetoeu.jscript.core.Operation;
|
||||||
|
|
||||||
public abstract class AssignableStatement extends Statement {
|
public abstract class AssignableStatement extends Statement {
|
||||||
public abstract Statement toAssign(Statement val, Operation operation);
|
public abstract Statement toAssign(Statement val, Operation operation);
|
@ -0,0 +1,77 @@
|
|||||||
|
package me.topchetoeu.jscript.compilation;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
|
import me.topchetoeu.jscript.compilation.mapping.FunctionMap;
|
||||||
|
import me.topchetoeu.jscript.compilation.mapping.FunctionMap.FunctionMapBuilder;
|
||||||
|
import me.topchetoeu.jscript.core.scope.LocalScopeRecord;
|
||||||
|
|
||||||
|
public class CompileResult {
|
||||||
|
public final Vector<Instruction> instructions = new Vector<>();
|
||||||
|
public final List<CompileResult> children = new LinkedList<>();
|
||||||
|
public final FunctionMapBuilder map = FunctionMap.builder();
|
||||||
|
public final LocalScopeRecord scope;
|
||||||
|
public int length = 0;
|
||||||
|
|
||||||
|
public int temp() {
|
||||||
|
instructions.add(null);
|
||||||
|
return instructions.size() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompileResult add(Instruction instr) {
|
||||||
|
instructions.add(instr);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public CompileResult set(int i, Instruction instr) {
|
||||||
|
instructions.set(i, instr);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public Instruction get(int i) {
|
||||||
|
return instructions.get(i);
|
||||||
|
}
|
||||||
|
public int size() { return instructions.size(); }
|
||||||
|
|
||||||
|
public void setDebug(Location loc, BreakpointType type) {
|
||||||
|
map.setDebug(loc, type);
|
||||||
|
}
|
||||||
|
public void setLocation(int i, Location loc) {
|
||||||
|
map.setLocation(i, loc);
|
||||||
|
}
|
||||||
|
public void setLocationAndDebug(int i, Location loc, BreakpointType type) {
|
||||||
|
map.setLocationAndDebug(i, loc, type);
|
||||||
|
}
|
||||||
|
public void setDebug(BreakpointType type) {
|
||||||
|
setDebug(map.last(), type);
|
||||||
|
}
|
||||||
|
public void setLocation(Location type) {
|
||||||
|
setLocation(instructions.size() - 1, type);
|
||||||
|
}
|
||||||
|
public void setLocationAndDebug(Location loc, BreakpointType type) {
|
||||||
|
setLocationAndDebug(instructions.size() - 1, loc, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompileResult addChild(CompileResult child) {
|
||||||
|
this.children.add(child);
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FunctionBody body() {
|
||||||
|
var builtChildren = new FunctionBody[children.size()];
|
||||||
|
|
||||||
|
for (var i = 0; i < children.size(); i++) builtChildren[i] = children.get(i).body();
|
||||||
|
|
||||||
|
return new FunctionBody(scope.localsCount(), length, instructions.toArray(Instruction[]::new), builtChildren);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FunctionMap map() {
|
||||||
|
return map.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompileResult(LocalScopeRecord scope) {
|
||||||
|
this.scope = scope;
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,11 @@
|
|||||||
package me.topchetoeu.jscript.core.compilation;
|
package me.topchetoeu.jscript.compilation;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Location;
|
import me.topchetoeu.jscript.common.Location;
|
||||||
import me.topchetoeu.jscript.core.compilation.Instruction.BreakpointType;
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.core.compilation.values.FunctionStatement;
|
import me.topchetoeu.jscript.compilation.values.FunctionStatement;
|
||||||
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
|
|
||||||
|
|
||||||
public class CompoundStatement extends Statement {
|
public class CompoundStatement extends Statement {
|
||||||
public final Statement[] statements;
|
public final Statement[] statements;
|
||||||
@ -22,16 +21,16 @@ public class CompoundStatement extends Statement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void declare(ScopeRecord varsScope) {
|
public void declare(CompileResult target) {
|
||||||
for (var stm : statements) stm.declare(varsScope);
|
for (var stm : statements) stm.declare(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, BreakpointType type) {
|
public void compile(CompileResult target, boolean pollute, BreakpointType type) {
|
||||||
List<Statement> statements = new Vector<Statement>();
|
List<Statement> statements = new Vector<Statement>();
|
||||||
if (separateFuncs) for (var stm : this.statements) {
|
if (separateFuncs) for (var stm : this.statements) {
|
||||||
if (stm instanceof FunctionStatement && ((FunctionStatement)stm).statement) {
|
if (stm instanceof FunctionStatement && ((FunctionStatement)stm).statement) {
|
||||||
stm.compile(target, scope, false);
|
stm.compile(target, false);
|
||||||
}
|
}
|
||||||
else statements.add(stm);
|
else statements.add(stm);
|
||||||
}
|
}
|
||||||
@ -42,12 +41,12 @@ public class CompoundStatement extends Statement {
|
|||||||
for (var i = 0; i < statements.size(); i++) {
|
for (var i = 0; i < statements.size(); i++) {
|
||||||
var stm = statements.get(i);
|
var stm = statements.get(i);
|
||||||
|
|
||||||
if (i != statements.size() - 1) stm.compile(target, scope, false, BreakpointType.STEP_OVER);
|
if (i != statements.size() - 1) stm.compile(target, false, BreakpointType.STEP_OVER);
|
||||||
else stm.compile(target, scope, polluted = pollute, BreakpointType.STEP_OVER);
|
else stm.compile(target, polluted = pollute, BreakpointType.STEP_OVER);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!polluted && pollute) {
|
if (!polluted && pollute) {
|
||||||
target.add(Instruction.loadValue(loc(), null));
|
target.add(Instruction.pushUndefined());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
14
src/java/me/topchetoeu/jscript/compilation/FunctionBody.java
Normal file
14
src/java/me/topchetoeu/jscript/compilation/FunctionBody.java
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package me.topchetoeu.jscript.compilation;
|
||||||
|
|
||||||
|
public class FunctionBody {
|
||||||
|
public final FunctionBody[] children;
|
||||||
|
public final Instruction[] instructions;
|
||||||
|
public final int localsN, argsN;
|
||||||
|
|
||||||
|
public FunctionBody(int localsN, int argsN, Instruction[] instructions, FunctionBody[] children) {
|
||||||
|
this.children = children;
|
||||||
|
this.argsN = argsN;
|
||||||
|
this.localsN = localsN;
|
||||||
|
this.instructions = instructions;
|
||||||
|
}
|
||||||
|
}
|
370
src/java/me/topchetoeu/jscript/compilation/Instruction.java
Normal file
370
src/java/me/topchetoeu/jscript/compilation/Instruction.java
Normal file
@ -0,0 +1,370 @@
|
|||||||
|
package me.topchetoeu.jscript.compilation;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.core.Operation;
|
||||||
|
import me.topchetoeu.jscript.core.exceptions.SyntaxException;
|
||||||
|
|
||||||
|
public class Instruction {
|
||||||
|
public static enum Type {
|
||||||
|
NOP(0),
|
||||||
|
RETURN(1),
|
||||||
|
THROW(2),
|
||||||
|
THROW_SYNTAX(3),
|
||||||
|
DELETE(4),
|
||||||
|
TRY_START(5),
|
||||||
|
TRY_END(6),
|
||||||
|
|
||||||
|
CALL(7),
|
||||||
|
CALL_NEW(8),
|
||||||
|
JMP_IF(9),
|
||||||
|
JMP_IFN(10),
|
||||||
|
JMP(11),
|
||||||
|
|
||||||
|
PUSH_UNDEFINED(12),
|
||||||
|
PUSH_NULL(13),
|
||||||
|
PUSH_BOOL(14),
|
||||||
|
PUSH_NUMBER(15),
|
||||||
|
PUSH_STRING(16),
|
||||||
|
|
||||||
|
LOAD_VAR(17),
|
||||||
|
LOAD_MEMBER(18),
|
||||||
|
LOAD_GLOB(20),
|
||||||
|
|
||||||
|
LOAD_FUNC(21),
|
||||||
|
LOAD_ARR(22),
|
||||||
|
LOAD_OBJ(23),
|
||||||
|
STORE_SELF_FUNC(24),
|
||||||
|
LOAD_REGEX(25),
|
||||||
|
|
||||||
|
DUP(26),
|
||||||
|
|
||||||
|
STORE_VAR(27),
|
||||||
|
STORE_MEMBER(28),
|
||||||
|
DISCARD(29),
|
||||||
|
|
||||||
|
MAKE_VAR(30),
|
||||||
|
DEF_PROP(31),
|
||||||
|
KEYS(32),
|
||||||
|
|
||||||
|
TYPEOF(33),
|
||||||
|
OPERATION(34);
|
||||||
|
|
||||||
|
private static final HashMap<Integer, Type> types = new HashMap<>();
|
||||||
|
public final int numeric;
|
||||||
|
|
||||||
|
static {
|
||||||
|
for (var val : Type.values()) types.put(val.numeric, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Type(int numeric) {
|
||||||
|
this.numeric = numeric;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Type fromNumeric(int i) {
|
||||||
|
return types.get(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static enum BreakpointType {
|
||||||
|
NONE,
|
||||||
|
STEP_OVER,
|
||||||
|
STEP_IN;
|
||||||
|
|
||||||
|
public boolean shouldStepIn() {
|
||||||
|
return this != NONE;
|
||||||
|
}
|
||||||
|
public boolean shouldStepOver() {
|
||||||
|
return this == STEP_OVER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final Type type;
|
||||||
|
public final Object[] params;
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> T get(int i) {
|
||||||
|
if (i >= params.length || i < 0) return null;
|
||||||
|
return (T)params[i];
|
||||||
|
}
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> T get(int i, T defaultVal) {
|
||||||
|
if (i >= params.length || i < 0) return defaultVal;
|
||||||
|
return (T)params[i];
|
||||||
|
}
|
||||||
|
public boolean match(Object ...args) {
|
||||||
|
if (args.length != params.length) return false;
|
||||||
|
for (int i = 0; i < args.length; i++) {
|
||||||
|
var a = params[i];
|
||||||
|
var b = args[i];
|
||||||
|
if (a == null || b == null) {
|
||||||
|
if (!(a == null && b == null)) return false;
|
||||||
|
}
|
||||||
|
if (!a.equals(b)) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
public boolean is(int i, Object arg) {
|
||||||
|
if (params.length <= i) return false;
|
||||||
|
return params[i].equals(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void write(DataOutputStream writer) throws IOException {
|
||||||
|
var rawType = type.numeric;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case KEYS:
|
||||||
|
case PUSH_BOOL:
|
||||||
|
case STORE_MEMBER: rawType |= (boolean)get(0) ? 128 : 0; break;
|
||||||
|
case STORE_VAR: rawType |= (boolean)get(1) ? 128 : 0; break;
|
||||||
|
case TYPEOF: rawType |= params.length > 0 ? 128 : 0; break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.writeByte(rawType);
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case CALL: writer.writeInt(get(0)); break;
|
||||||
|
case CALL_NEW: writer.writeInt(get(0)); break;
|
||||||
|
case DUP: writer.writeInt(get(0)); break;
|
||||||
|
case JMP: writer.writeInt(get(0)); break;
|
||||||
|
case JMP_IF: writer.writeInt(get(0)); break;
|
||||||
|
case JMP_IFN: writer.writeInt(get(0)); break;
|
||||||
|
case LOAD_ARR: writer.writeInt(get(0)); break;
|
||||||
|
case LOAD_FUNC: {
|
||||||
|
writer.writeInt(params.length - 1);
|
||||||
|
|
||||||
|
for (var i = 0; i < params.length; i++) {
|
||||||
|
writer.writeInt(get(i + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.writeInt(get(0));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LOAD_REGEX: writer.writeUTF(get(0)); break;
|
||||||
|
case LOAD_VAR: writer.writeInt(get(0)); break;
|
||||||
|
case MAKE_VAR: writer.writeUTF(get(0)); break;
|
||||||
|
case OPERATION: writer.writeByte(((Operation)get(0)).numeric); break;
|
||||||
|
case PUSH_NUMBER: writer.writeDouble(get(0)); break;
|
||||||
|
case PUSH_STRING: writer.writeUTF(get(0)); break;
|
||||||
|
case STORE_SELF_FUNC: writer.writeInt(get(0)); break;
|
||||||
|
case STORE_VAR: writer.writeInt(get(0)); break;
|
||||||
|
case THROW_SYNTAX: writer.writeUTF(get(0));
|
||||||
|
case TRY_START:
|
||||||
|
writer.writeInt(get(0));
|
||||||
|
writer.writeInt(get(1));
|
||||||
|
writer.writeInt(get(2));
|
||||||
|
break;
|
||||||
|
case TYPEOF:
|
||||||
|
if (params.length > 0) writer.writeUTF(get(0));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Instruction(Type type, Object ...params) {
|
||||||
|
this.type = type;
|
||||||
|
this.params = params;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Instruction read(DataInputStream stream) throws IOException {
|
||||||
|
var rawType = stream.readUnsignedByte();
|
||||||
|
var type = Type.fromNumeric(rawType & 127);
|
||||||
|
var flag = (rawType & 128) != 0;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case CALL: return call(stream.readInt());
|
||||||
|
case CALL_NEW: return callNew(stream.readInt());
|
||||||
|
case DEF_PROP: return defProp();
|
||||||
|
case DELETE: return delete();
|
||||||
|
case DISCARD: return discard();
|
||||||
|
case DUP: return dup(stream.readInt());
|
||||||
|
case JMP: return jmp(stream.readInt());
|
||||||
|
case JMP_IF: return jmpIf(stream.readInt());
|
||||||
|
case JMP_IFN: return jmpIfNot(stream.readInt());
|
||||||
|
case KEYS: return keys(flag);
|
||||||
|
case LOAD_ARR: return loadArr(stream.readInt());
|
||||||
|
case LOAD_FUNC: {
|
||||||
|
var captures = new int[stream.readInt()];
|
||||||
|
|
||||||
|
for (var i = 0; i < captures.length; i++) {
|
||||||
|
captures[i] = stream.readInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
return loadFunc(stream.readInt(), captures);
|
||||||
|
}
|
||||||
|
case LOAD_GLOB: return loadGlob();
|
||||||
|
case LOAD_MEMBER: return loadMember();
|
||||||
|
case LOAD_OBJ: return loadObj();
|
||||||
|
case LOAD_REGEX: return loadRegex(stream.readUTF(), null);
|
||||||
|
case LOAD_VAR: return loadVar(stream.readInt());
|
||||||
|
case MAKE_VAR: return makeVar(stream.readUTF());
|
||||||
|
case OPERATION: return operation(Operation.fromNumeric(stream.readUnsignedByte()));
|
||||||
|
case PUSH_NULL: return pushNull();
|
||||||
|
case PUSH_UNDEFINED: return pushUndefined();
|
||||||
|
case PUSH_BOOL: return pushValue(flag);
|
||||||
|
case PUSH_NUMBER: return pushValue(stream.readDouble());
|
||||||
|
case PUSH_STRING: return pushValue(stream.readUTF());
|
||||||
|
case RETURN: return ret();
|
||||||
|
case STORE_MEMBER: return storeMember(flag);
|
||||||
|
case STORE_SELF_FUNC: return storeSelfFunc(stream.readInt());
|
||||||
|
case STORE_VAR: return storeVar(stream.readInt(), flag);
|
||||||
|
case THROW: return throwInstr();
|
||||||
|
case THROW_SYNTAX: return throwSyntax(stream.readUTF());
|
||||||
|
case TRY_END: return tryEnd();
|
||||||
|
case TRY_START: return tryStart(stream.readInt(), stream.readInt(), stream.readInt());
|
||||||
|
case TYPEOF: return flag ? typeof(stream.readUTF()) : typeof();
|
||||||
|
case NOP:
|
||||||
|
if (flag) return null;
|
||||||
|
else return nop();
|
||||||
|
default: return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Instruction tryStart(int catchStart, int finallyStart, int end) {
|
||||||
|
return new Instruction(Type.TRY_START, catchStart, finallyStart, end);
|
||||||
|
}
|
||||||
|
public static Instruction tryEnd() {
|
||||||
|
return new Instruction(Type.TRY_END);
|
||||||
|
}
|
||||||
|
public static Instruction throwInstr() {
|
||||||
|
return new Instruction(Type.THROW);
|
||||||
|
}
|
||||||
|
public static Instruction throwSyntax(SyntaxException err) {
|
||||||
|
return new Instruction(Type.THROW_SYNTAX, err.getMessage());
|
||||||
|
}
|
||||||
|
public static Instruction throwSyntax(String err) {
|
||||||
|
return new Instruction(Type.THROW_SYNTAX, err);
|
||||||
|
}
|
||||||
|
public static Instruction delete() {
|
||||||
|
return new Instruction(Type.DELETE);
|
||||||
|
}
|
||||||
|
public static Instruction ret() {
|
||||||
|
return new Instruction(Type.RETURN);
|
||||||
|
}
|
||||||
|
public static Instruction debug() {
|
||||||
|
return new Instruction(Type.NOP, "debug");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Instruction nop(Object ...params) {
|
||||||
|
return new Instruction(Type.NOP, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Instruction call(int argn) {
|
||||||
|
return new Instruction(Type.CALL, argn);
|
||||||
|
}
|
||||||
|
public static Instruction callNew(int argn) {
|
||||||
|
return new Instruction(Type.CALL_NEW, argn);
|
||||||
|
}
|
||||||
|
public static Instruction jmp(int offset) {
|
||||||
|
return new Instruction(Type.JMP, offset);
|
||||||
|
}
|
||||||
|
public static Instruction jmpIf(int offset) {
|
||||||
|
return new Instruction(Type.JMP_IF, offset);
|
||||||
|
}
|
||||||
|
public static Instruction jmpIfNot(int offset) {
|
||||||
|
return new Instruction(Type.JMP_IFN, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Instruction pushUndefined() {
|
||||||
|
return new Instruction(Type.PUSH_UNDEFINED);
|
||||||
|
}
|
||||||
|
public static Instruction pushNull() {
|
||||||
|
return new Instruction(Type.PUSH_NULL);
|
||||||
|
}
|
||||||
|
public static Instruction pushValue(boolean val) {
|
||||||
|
return new Instruction(Type.PUSH_BOOL, val);
|
||||||
|
}
|
||||||
|
public static Instruction pushValue(double val) {
|
||||||
|
return new Instruction(Type.PUSH_NUMBER, val);
|
||||||
|
}
|
||||||
|
public static Instruction pushValue(String val) {
|
||||||
|
return new Instruction(Type.PUSH_STRING, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Instruction makeVar(String name) {
|
||||||
|
return new Instruction(Type.MAKE_VAR, name);
|
||||||
|
}
|
||||||
|
public static Instruction loadVar(Object i) {
|
||||||
|
return new Instruction(Type.LOAD_VAR, i);
|
||||||
|
}
|
||||||
|
public static Instruction loadGlob() {
|
||||||
|
return new Instruction(Type.LOAD_GLOB);
|
||||||
|
}
|
||||||
|
public static Instruction loadMember() {
|
||||||
|
return new Instruction(Type.LOAD_MEMBER);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Instruction loadRegex(String pattern, String flags) {
|
||||||
|
return new Instruction(Type.LOAD_REGEX, pattern, flags);
|
||||||
|
}
|
||||||
|
public static Instruction loadFunc(int id, int[] captures) {
|
||||||
|
var args = new Object[1 + captures.length];
|
||||||
|
args[0] = id;
|
||||||
|
for (var i = 0; i < captures.length; i++) args[i + 1] = captures[i];
|
||||||
|
return new Instruction(Type.LOAD_FUNC, args);
|
||||||
|
}
|
||||||
|
public static Instruction loadObj() {
|
||||||
|
return new Instruction(Type.LOAD_OBJ);
|
||||||
|
}
|
||||||
|
public static Instruction loadArr(int count) {
|
||||||
|
return new Instruction(Type.LOAD_ARR, count);
|
||||||
|
}
|
||||||
|
public static Instruction dup() {
|
||||||
|
return new Instruction(Type.DUP, 1);
|
||||||
|
}
|
||||||
|
public static Instruction dup(int count) {
|
||||||
|
return new Instruction(Type.DUP, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Instruction storeSelfFunc(int i) {
|
||||||
|
return new Instruction(Type.STORE_SELF_FUNC, i);
|
||||||
|
}
|
||||||
|
public static Instruction storeVar(Object i) {
|
||||||
|
return new Instruction(Type.STORE_VAR, i, false);
|
||||||
|
}
|
||||||
|
public static Instruction storeVar(Object i, boolean keep) {
|
||||||
|
return new Instruction(Type.STORE_VAR, i, keep);
|
||||||
|
}
|
||||||
|
public static Instruction storeMember() {
|
||||||
|
return new Instruction(Type.STORE_MEMBER, false);
|
||||||
|
}
|
||||||
|
public static Instruction storeMember(boolean keep) {
|
||||||
|
return new Instruction(Type.STORE_MEMBER, keep);
|
||||||
|
}
|
||||||
|
public static Instruction discard() {
|
||||||
|
return new Instruction(Type.DISCARD);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Instruction typeof() {
|
||||||
|
return new Instruction(Type.TYPEOF);
|
||||||
|
}
|
||||||
|
public static Instruction typeof(String varName) {
|
||||||
|
return new Instruction(Type.TYPEOF, varName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Instruction keys(boolean forInFormat) {
|
||||||
|
return new Instruction(Type.KEYS, forInFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Instruction defProp() {
|
||||||
|
return new Instruction(Type.DEF_PROP);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Instruction operation(Operation op) {
|
||||||
|
return new Instruction(Type.OPERATION, op);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
var res = type.toString();
|
||||||
|
|
||||||
|
for (int i = 0; i < params.length; i++) {
|
||||||
|
res += " " + params[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
27
src/java/me/topchetoeu/jscript/compilation/Statement.java
Normal file
27
src/java/me/topchetoeu/jscript/compilation/Statement.java
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package me.topchetoeu.jscript.compilation;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
|
|
||||||
|
public abstract class Statement {
|
||||||
|
private Location _loc;
|
||||||
|
|
||||||
|
public boolean pure() { return false; }
|
||||||
|
public void declare(CompileResult target) { }
|
||||||
|
|
||||||
|
public void compile(CompileResult target, boolean pollute, BreakpointType type) {
|
||||||
|
int start = target.size();
|
||||||
|
compile(target, pollute);
|
||||||
|
if (target.size() != start) target.setLocationAndDebug(start, loc(), type);
|
||||||
|
}
|
||||||
|
public void compile(CompileResult target, boolean pollute) {
|
||||||
|
compile(target, pollute, BreakpointType.NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Location loc() { return _loc; }
|
||||||
|
public void setLoc(Location loc) { _loc = loc; }
|
||||||
|
|
||||||
|
protected Statement(Location loc) {
|
||||||
|
this._loc = loc;
|
||||||
|
}
|
||||||
|
}
|
@ -1,14 +1,13 @@
|
|||||||
package me.topchetoeu.jscript.core.compilation;
|
package me.topchetoeu.jscript.compilation;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
|
|
||||||
import me.topchetoeu.jscript.core.exceptions.SyntaxException;
|
import me.topchetoeu.jscript.core.exceptions.SyntaxException;
|
||||||
|
|
||||||
public class ThrowSyntaxStatement extends Statement {
|
public class ThrowSyntaxStatement extends Statement {
|
||||||
public final String name;
|
public final String name;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileResult target, boolean pollute) {
|
||||||
target.add(Instruction.throwSyntax(loc(), name));
|
target.add(Instruction.throwSyntax(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ThrowSyntaxStatement(SyntaxException e) {
|
public ThrowSyntaxStatement(SyntaxException e) {
|
@ -1,11 +1,10 @@
|
|||||||
package me.topchetoeu.jscript.core.compilation;
|
package me.topchetoeu.jscript.compilation;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Location;
|
import me.topchetoeu.jscript.common.Location;
|
||||||
import me.topchetoeu.jscript.core.compilation.Instruction.BreakpointType;
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.core.compilation.values.FunctionStatement;
|
import me.topchetoeu.jscript.compilation.values.FunctionStatement;
|
||||||
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
|
|
||||||
|
|
||||||
public class VariableDeclareStatement extends Statement {
|
public class VariableDeclareStatement extends Statement {
|
||||||
public static class Pair {
|
public static class Pair {
|
||||||
@ -23,26 +22,26 @@ public class VariableDeclareStatement extends Statement {
|
|||||||
public final List<Pair> values;
|
public final List<Pair> values;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void declare(ScopeRecord varsScope) {
|
public void declare(CompileResult target) {
|
||||||
for (var key : values) {
|
for (var key : values) {
|
||||||
varsScope.define(key.name);
|
target.scope.define(key.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileResult target, boolean pollute) {
|
||||||
for (var entry : values) {
|
for (var entry : values) {
|
||||||
if (entry.name == null) continue;
|
if (entry.name == null) continue;
|
||||||
var key = scope.getKey(entry.name);
|
var key = target.scope.getKey(entry.name);
|
||||||
|
|
||||||
if (key instanceof String) target.add(Instruction.makeVar(entry.location, (String)key));
|
if (key instanceof String) target.add(Instruction.makeVar((String)key));
|
||||||
|
|
||||||
if (entry.value != null) {
|
if (entry.value != null) {
|
||||||
FunctionStatement.compileWithName(entry.value, target, scope, true, entry.name, BreakpointType.STEP_OVER);
|
FunctionStatement.compileWithName(entry.value, target, true, entry.name, BreakpointType.STEP_OVER);
|
||||||
target.add(Instruction.storeVar(entry.location, key));
|
target.add(Instruction.storeVar(key));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
if (pollute) target.add(Instruction.pushUndefined());
|
||||||
}
|
}
|
||||||
|
|
||||||
public VariableDeclareStatement(Location loc, List<Pair> values) {
|
public VariableDeclareStatement(Location loc, List<Pair> values) {
|
@ -0,0 +1,21 @@
|
|||||||
|
package me.topchetoeu.jscript.compilation.control;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
|
|
||||||
|
public class BreakStatement extends Statement {
|
||||||
|
public final String label;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compile(CompileResult target, boolean pollute) {
|
||||||
|
target.add(Instruction.nop("break", label));
|
||||||
|
if (pollute) target.add(Instruction.pushUndefined());
|
||||||
|
}
|
||||||
|
|
||||||
|
public BreakStatement(Location loc, String label) {
|
||||||
|
super(loc);
|
||||||
|
this.label = label;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package me.topchetoeu.jscript.compilation.control;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
|
|
||||||
|
public class ContinueStatement extends Statement {
|
||||||
|
public final String label;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compile(CompileResult target, boolean pollute) {
|
||||||
|
target.add(Instruction.nop(loc(), "cont", label));
|
||||||
|
if (pollute) target.add(Instruction.pushUndefined());
|
||||||
|
}
|
||||||
|
|
||||||
|
public ContinueStatement(Location loc, String label) {
|
||||||
|
super(loc);
|
||||||
|
this.label = label;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package me.topchetoeu.jscript.compilation.control;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
|
|
||||||
|
public class DebugStatement extends Statement {
|
||||||
|
@Override
|
||||||
|
public void compile(CompileResult target, boolean pollute) {
|
||||||
|
target.add(Instruction.debug());
|
||||||
|
if (pollute) target.add(Instruction.pushUndefined());
|
||||||
|
}
|
||||||
|
|
||||||
|
public DebugStatement(Location loc) {
|
||||||
|
super(loc);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package me.topchetoeu.jscript.compilation.control;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
|
|
||||||
|
public class DeleteStatement extends Statement {
|
||||||
|
public final Statement key;
|
||||||
|
public final Statement value;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compile(CompileResult target, boolean pollute) {
|
||||||
|
value.compile(target, true);
|
||||||
|
key.compile(target, true);
|
||||||
|
|
||||||
|
target.add(Instruction.delete());
|
||||||
|
if (pollute) target.add(Instruction.pushValue(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public DeleteStatement(Location loc, Statement key, Statement value) {
|
||||||
|
super(loc);
|
||||||
|
this.key = key;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
package me.topchetoeu.jscript.compilation.control;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
|
|
||||||
|
public class DoWhileStatement extends Statement {
|
||||||
|
public final Statement condition, body;
|
||||||
|
public final String label;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void declare(CompileResult target) {
|
||||||
|
body.declare(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compile(CompileResult target, boolean pollute) {
|
||||||
|
int start = target.size();
|
||||||
|
body.compile(target, false, BreakpointType.STEP_OVER);
|
||||||
|
int mid = target.size();
|
||||||
|
condition.compile(target, true, BreakpointType.STEP_OVER);
|
||||||
|
int end = target.size();
|
||||||
|
|
||||||
|
WhileStatement.replaceBreaks(target, label, start, mid - 1, mid, end + 1);
|
||||||
|
target.add(Instruction.jmpIf(start - end));
|
||||||
|
}
|
||||||
|
|
||||||
|
public DoWhileStatement(Location loc, String label, Statement condition, Statement body) {
|
||||||
|
super(loc);
|
||||||
|
this.label = label;
|
||||||
|
this.condition = condition;
|
||||||
|
this.body = body;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,69 @@
|
|||||||
|
package me.topchetoeu.jscript.compilation.control;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
|
import me.topchetoeu.jscript.core.Operation;
|
||||||
|
|
||||||
|
public class ForInStatement extends Statement {
|
||||||
|
public final String varName;
|
||||||
|
public final boolean isDeclaration;
|
||||||
|
public final Statement varValue, object, body;
|
||||||
|
public final String label;
|
||||||
|
public final Location varLocation;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void declare(CompileResult target) {
|
||||||
|
body.declare(target);
|
||||||
|
if (isDeclaration) target.scope.define(varName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compile(CompileResult target, boolean pollute) {
|
||||||
|
var key = target.scope.getKey(varName);
|
||||||
|
|
||||||
|
if (key instanceof String) target.add(Instruction.makeVar((String)key));
|
||||||
|
|
||||||
|
if (varValue != null) {
|
||||||
|
varValue.compile(target, true);
|
||||||
|
target.add(Instruction.storeVar(target.scope.getKey(varName)));
|
||||||
|
}
|
||||||
|
|
||||||
|
object.compile(target, true, BreakpointType.STEP_OVER);
|
||||||
|
target.add(Instruction.keys(true));
|
||||||
|
|
||||||
|
int start = target.size();
|
||||||
|
target.add(Instruction.dup());
|
||||||
|
target.add(Instruction.pushUndefined());
|
||||||
|
target.add(Instruction.operation(Operation.EQUALS));
|
||||||
|
int mid = target.temp();
|
||||||
|
|
||||||
|
target.add(Instruction.pushValue("value")).setLocation(varLocation);
|
||||||
|
target.add(Instruction.loadMember()).setLocation(varLocation);
|
||||||
|
target.add(Instruction.storeVar(key)).setLocationAndDebug(object.loc(), BreakpointType.STEP_OVER);
|
||||||
|
|
||||||
|
body.compile(target, false, BreakpointType.STEP_OVER);
|
||||||
|
|
||||||
|
int end = target.size();
|
||||||
|
|
||||||
|
WhileStatement.replaceBreaks(target, label, mid + 1, end, start, end + 1);
|
||||||
|
|
||||||
|
target.add(Instruction.jmp(start - end));
|
||||||
|
target.add(Instruction.discard());
|
||||||
|
target.set(mid, Instruction.jmpIf(end - mid + 1));
|
||||||
|
if (pollute) target.add(Instruction.pushUndefined());
|
||||||
|
}
|
||||||
|
|
||||||
|
public ForInStatement(Location loc, Location varLocation, String label, boolean isDecl, String varName, Statement varValue, Statement object, Statement body) {
|
||||||
|
super(loc);
|
||||||
|
this.varLocation = varLocation;
|
||||||
|
this.label = label;
|
||||||
|
this.isDeclaration = isDecl;
|
||||||
|
this.varName = varName;
|
||||||
|
this.varValue = varValue;
|
||||||
|
this.object = object;
|
||||||
|
this.body = body;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
package me.topchetoeu.jscript.compilation.control;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
|
|
||||||
|
public class ForStatement extends Statement {
|
||||||
|
public final Statement declaration, assignment, condition, body;
|
||||||
|
public final String label;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void declare(CompileResult target) {
|
||||||
|
declaration.declare(target);
|
||||||
|
body.declare(target);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void compile(CompileResult target, boolean pollute) {
|
||||||
|
declaration.compile(target, false, BreakpointType.STEP_OVER);
|
||||||
|
|
||||||
|
int start = target.size();
|
||||||
|
condition.compile(target, true, BreakpointType.STEP_OVER);
|
||||||
|
int mid = target.temp();
|
||||||
|
body.compile(target, false, BreakpointType.STEP_OVER);
|
||||||
|
int beforeAssign = target.size();
|
||||||
|
assignment.compile(target, false, BreakpointType.STEP_OVER);
|
||||||
|
int end = target.size();
|
||||||
|
|
||||||
|
WhileStatement.replaceBreaks(target, label, mid + 1, end, beforeAssign, end + 1);
|
||||||
|
|
||||||
|
target.add(Instruction.jmp(start - end));
|
||||||
|
target.set(mid, Instruction.jmpIfNot(end - mid + 1));
|
||||||
|
if (pollute) target.add(Instruction.pushUndefined());
|
||||||
|
}
|
||||||
|
|
||||||
|
public ForStatement(Location loc, String label, Statement declaration, Statement condition, Statement assignment, Statement body) {
|
||||||
|
super(loc);
|
||||||
|
this.label = label;
|
||||||
|
this.declaration = declaration;
|
||||||
|
this.condition = condition;
|
||||||
|
this.assignment = assignment;
|
||||||
|
this.body = body;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
package me.topchetoeu.jscript.compilation.control;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
|
|
||||||
|
public class IfStatement extends Statement {
|
||||||
|
public final Statement condition, body, elseBody;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void declare(CompileResult target) {
|
||||||
|
body.declare(target);
|
||||||
|
if (elseBody != null) elseBody.declare(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void compile(CompileResult target, boolean pollute, BreakpointType breakpoint) {
|
||||||
|
condition.compile(target, true, breakpoint);
|
||||||
|
|
||||||
|
if (elseBody == null) {
|
||||||
|
int i = target.temp();
|
||||||
|
body.compile(target, pollute, breakpoint);
|
||||||
|
int endI = target.size();
|
||||||
|
target.set(i, Instruction.jmpIfNot(endI - i));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
int start = target.temp();
|
||||||
|
body.compile(target, pollute, breakpoint);
|
||||||
|
int mid = target.temp();
|
||||||
|
elseBody.compile(target, pollute, breakpoint);
|
||||||
|
int end = target.size();
|
||||||
|
|
||||||
|
target.set(start, Instruction.jmpIfNot(mid - start - 1));
|
||||||
|
target.set(mid, Instruction.jmp(end - mid));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@Override public void compile(CompileResult target, boolean pollute) {
|
||||||
|
compile(target, pollute, BreakpointType.STEP_IN);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IfStatement(Location loc, Statement condition, Statement body, Statement elseBody) {
|
||||||
|
super(loc);
|
||||||
|
this.condition = condition;
|
||||||
|
this.body = body;
|
||||||
|
this.elseBody = elseBody;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package me.topchetoeu.jscript.compilation.control;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
|
|
||||||
|
public class ReturnStatement extends Statement {
|
||||||
|
public final Statement value;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compile(CompileResult target, boolean pollute) {
|
||||||
|
if (value == null) target.add(Instruction.pushUndefined());
|
||||||
|
else value.compile(target, true);
|
||||||
|
target.add(Instruction.ret()).setLocation(loc());
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReturnStatement(Location loc, Statement value) {
|
||||||
|
super(loc);
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,83 @@
|
|||||||
|
package me.topchetoeu.jscript.compilation.control;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||||
|
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.core.Operation;
|
||||||
|
|
||||||
|
public class SwitchStatement extends Statement {
|
||||||
|
public static class SwitchCase {
|
||||||
|
public final Statement value;
|
||||||
|
public final int statementI;
|
||||||
|
|
||||||
|
public SwitchCase(Statement value, int statementI) {
|
||||||
|
this.value = value;
|
||||||
|
this.statementI = statementI;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final Statement value;
|
||||||
|
public final SwitchCase[] cases;
|
||||||
|
public final Statement[] body;
|
||||||
|
public final int defaultI;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void declare(CompileResult target) {
|
||||||
|
for (var stm : body) stm.declare(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compile(CompileResult target, boolean pollute) {
|
||||||
|
var caseToStatement = new HashMap<Integer, Integer>();
|
||||||
|
var statementToIndex = new HashMap<Integer, Integer>();
|
||||||
|
|
||||||
|
value.compile(target, true, BreakpointType.STEP_OVER);
|
||||||
|
|
||||||
|
for (var ccase : cases) {
|
||||||
|
target.add(Instruction.dup());
|
||||||
|
ccase.value.compile(target, true);
|
||||||
|
target.add(Instruction.operation(Operation.EQUALS));
|
||||||
|
caseToStatement.put(target.temp(), ccase.statementI);
|
||||||
|
}
|
||||||
|
|
||||||
|
int start = target.temp();
|
||||||
|
|
||||||
|
for (var stm : body) {
|
||||||
|
statementToIndex.put(statementToIndex.size(), target.size());
|
||||||
|
stm.compile(target, false, BreakpointType.STEP_OVER);
|
||||||
|
}
|
||||||
|
|
||||||
|
int end = target.size();
|
||||||
|
target.add(Instruction.discard());
|
||||||
|
if (pollute) target.add(Instruction.pushUndefined());
|
||||||
|
|
||||||
|
if (defaultI < 0 || defaultI >= body.length) target.set(start, Instruction.jmp(end - start));
|
||||||
|
else target.set(start, Instruction.jmp(statementToIndex.get(defaultI) - start));
|
||||||
|
|
||||||
|
for (int i = start; i < end; i++) {
|
||||||
|
var instr = target.get(i);
|
||||||
|
if (instr.type == Type.NOP && instr.is(0, "break") && instr.get(1) == null) {
|
||||||
|
target.set(i, Instruction.jmp(end - i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (var el : caseToStatement.entrySet()) {
|
||||||
|
var i = statementToIndex.get(el.getValue());
|
||||||
|
if (i == null) i = end;
|
||||||
|
target.set(el.getKey(), Instruction.jmpIf(i - el.getKey()));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public SwitchStatement(Location loc, Statement value, int defaultI, SwitchCase[] cases, Statement[] body) {
|
||||||
|
super(loc);
|
||||||
|
this.value = value;
|
||||||
|
this.defaultI = defaultI;
|
||||||
|
this.cases = cases;
|
||||||
|
this.body = body;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package me.topchetoeu.jscript.compilation.control;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
|
|
||||||
|
public class ThrowStatement extends Statement {
|
||||||
|
public final Statement value;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compile(CompileResult target, boolean pollute) {
|
||||||
|
value.compile(target, true);
|
||||||
|
target.add(Instruction.throwInstr()).setLocation(loc());
|
||||||
|
}
|
||||||
|
|
||||||
|
public ThrowStatement(Location loc, Statement value) {
|
||||||
|
super(loc);
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,58 @@
|
|||||||
|
package me.topchetoeu.jscript.compilation.control;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
|
|
||||||
|
public class TryStatement extends Statement {
|
||||||
|
public final Statement tryBody;
|
||||||
|
public final Statement catchBody;
|
||||||
|
public final Statement finallyBody;
|
||||||
|
public final String name;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void declare(CompileResult target) {
|
||||||
|
tryBody.declare(target);
|
||||||
|
if (catchBody != null) catchBody.declare(target);
|
||||||
|
if (finallyBody != null) finallyBody.declare(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compile(CompileResult target, boolean pollute, BreakpointType bpt) {
|
||||||
|
int replace = target.temp();
|
||||||
|
|
||||||
|
int start = replace + 1, catchStart = -1, finallyStart = -1;
|
||||||
|
|
||||||
|
tryBody.compile(target, false);
|
||||||
|
target.add(Instruction.tryEnd());
|
||||||
|
|
||||||
|
if (catchBody != null) {
|
||||||
|
catchStart = target.size() - start;
|
||||||
|
target.scope.define(name, true);
|
||||||
|
catchBody.compile(target, false);
|
||||||
|
target.scope.undefine();
|
||||||
|
target.add(Instruction.tryEnd());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (finallyBody != null) {
|
||||||
|
finallyStart = target.size() - start;
|
||||||
|
finallyBody.compile(target, false);
|
||||||
|
target.add(Instruction.tryEnd());
|
||||||
|
}
|
||||||
|
|
||||||
|
target.set(replace, Instruction.tryStart(catchStart, finallyStart, target.size() - start));
|
||||||
|
target.setLocationAndDebug(replace, loc(), BreakpointType.STEP_OVER);
|
||||||
|
|
||||||
|
if (pollute) target.add(Instruction.pushUndefined());
|
||||||
|
}
|
||||||
|
|
||||||
|
public TryStatement(Location loc, Statement tryBody, Statement catchBody, Statement finallyBody, String name) {
|
||||||
|
super(loc);
|
||||||
|
this.tryBody = tryBody;
|
||||||
|
this.catchBody = catchBody;
|
||||||
|
this.finallyBody = finallyBody;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
package me.topchetoeu.jscript.compilation.control;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||||
|
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;
|
||||||
|
|
||||||
|
public class WhileStatement extends Statement {
|
||||||
|
public final Statement condition, body;
|
||||||
|
public final String label;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void declare(CompileResult target) {
|
||||||
|
body.declare(target);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void compile(CompileResult target, boolean pollute) {
|
||||||
|
int start = target.size();
|
||||||
|
condition.compile(target, true);
|
||||||
|
int mid = target.temp();
|
||||||
|
body.compile(target, false, BreakpointType.STEP_OVER);
|
||||||
|
|
||||||
|
int end = target.size();
|
||||||
|
|
||||||
|
replaceBreaks(target, label, mid + 1, end, start, end + 1);
|
||||||
|
|
||||||
|
target.add(Instruction.jmp(start - end));
|
||||||
|
target.set(mid, Instruction.jmpIfNot(end - mid + 1));
|
||||||
|
if (pollute) target.add(Instruction.pushUndefined());
|
||||||
|
}
|
||||||
|
|
||||||
|
public WhileStatement(Location loc, String label, Statement condition, Statement body) {
|
||||||
|
super(loc);
|
||||||
|
this.label = label;
|
||||||
|
this.condition = condition;
|
||||||
|
this.body = body;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void replaceBreaks(CompileResult target, String label, int start, int end, int continuePoint, int breakPoint) {
|
||||||
|
for (int i = start; i < end; i++) {
|
||||||
|
var instr = target.get(i);
|
||||||
|
if (instr.type == Type.NOP && instr.is(0, "cont") && (instr.get(1) == null || instr.is(1, label))) {
|
||||||
|
target.set(i, Instruction.jmp(continuePoint - i));
|
||||||
|
}
|
||||||
|
if (instr.type == Type.NOP && instr.is(0, "break") && (instr.get(1) == null || instr.is(1, label))) {
|
||||||
|
target.set(i, Instruction.jmp(breakPoint - i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
package me.topchetoeu.jscript.compilation.mapping;
|
||||||
|
|
||||||
|
public enum ConvertType {
|
||||||
|
Exact,
|
||||||
|
Lower,
|
||||||
|
Upper,
|
||||||
|
Both,
|
||||||
|
}
|
@ -0,0 +1,147 @@
|
|||||||
|
package me.topchetoeu.jscript.compilation.mapping;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.SortedSet;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
|
import me.topchetoeu.jscript.core.scope.LocalScopeRecord;
|
||||||
|
|
||||||
|
public class FunctionMap {
|
||||||
|
public static class FunctionMapBuilder {
|
||||||
|
private final TreeMap<Integer, Location> sourceMap = new TreeMap<>();
|
||||||
|
private final HashMap<Location, BreakpointType> breakpoints = new HashMap<>();
|
||||||
|
|
||||||
|
public Location toLocation(int pc) {
|
||||||
|
return sourceMap.headMap(pc, true).firstEntry().getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public FunctionMapBuilder setDebug(Location loc, BreakpointType type) {
|
||||||
|
breakpoints.put(loc, type);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public FunctionMapBuilder setLocation(int i, Location loc) {
|
||||||
|
sourceMap.put(i, loc);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public FunctionMapBuilder setLocationAndDebug(int i, Location loc, BreakpointType type) {
|
||||||
|
setDebug(loc, type);
|
||||||
|
setLocation(i, loc);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Location first() {
|
||||||
|
return sourceMap.firstEntry().getValue();
|
||||||
|
}
|
||||||
|
public Location last() {
|
||||||
|
return sourceMap.lastEntry().getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public FunctionMap build(String[] localNames, String[] captureNames) {
|
||||||
|
return new FunctionMap(sourceMap, breakpoints, localNames, captureNames);
|
||||||
|
}
|
||||||
|
public FunctionMap build(LocalScopeRecord scope) {
|
||||||
|
return new FunctionMap(sourceMap, breakpoints, scope.locals(), scope.captures());
|
||||||
|
}
|
||||||
|
public FunctionMap build() {
|
||||||
|
return new FunctionMap(sourceMap, breakpoints, new String[0], new String[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private FunctionMapBuilder() { }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final FunctionMap EMPTY = new FunctionMap();
|
||||||
|
|
||||||
|
private final HashMap<Location, BreakpointType> bps = new HashMap<>();
|
||||||
|
private final TreeSet<Location> bpLocs = new TreeSet<>();
|
||||||
|
|
||||||
|
private final TreeMap<Integer, Location> pcToLoc = new TreeMap<>();
|
||||||
|
private final HashMap<Location, Integer> locToPc = new HashMap<>();
|
||||||
|
|
||||||
|
public final String[] localNames, captureNames;
|
||||||
|
|
||||||
|
public Location toLocation(int pc, boolean approxiamte) {
|
||||||
|
var res = pcToLoc.get(pc);
|
||||||
|
if (!approxiamte || res != null) return res;
|
||||||
|
return pcToLoc.headMap(pc, true).lastEntry().getValue();
|
||||||
|
}
|
||||||
|
public Location toLocation(int pc) {
|
||||||
|
return toLocation(pc, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BreakpointType getBreakpoint(Location loc) {
|
||||||
|
return bps.getOrDefault(loc, BreakpointType.NONE);
|
||||||
|
}
|
||||||
|
public Location correctBreakpoint(Location loc) {
|
||||||
|
return bpLocs.ceiling(loc);
|
||||||
|
}
|
||||||
|
public SortedSet<Location> breakpoints() {
|
||||||
|
return Collections.unmodifiableSortedSet(bpLocs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Location start() {
|
||||||
|
return pcToLoc.firstEntry().getValue();
|
||||||
|
}
|
||||||
|
public Location end() {
|
||||||
|
return pcToLoc.lastEntry().getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer toProgramCounter(Location loc) {
|
||||||
|
return locToPc.get(loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FunctionMap apply(SourceMap map) {
|
||||||
|
var res = new FunctionMap();
|
||||||
|
|
||||||
|
for (var el : new ArrayList<>(pcToLoc.entrySet())) {
|
||||||
|
res.pcToLoc.put(el.getKey(), map.toCompiled(el.getValue()));
|
||||||
|
}
|
||||||
|
for (var el : new ArrayList<>(locToPc.entrySet())) {
|
||||||
|
var mapped = map.toOriginal(el.getKey());
|
||||||
|
res.locToPc.put(mapped, el.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FunctionMap clone() {
|
||||||
|
var res = new FunctionMap();
|
||||||
|
res.pcToLoc.putAll(this.pcToLoc);
|
||||||
|
res.locToPc.putAll(this.locToPc);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FunctionMap(Map<Integer, Location> map, Map<Location, BreakpointType> breakpoints, String[] localNames, String[] captureNames) {
|
||||||
|
for (var el : map.entrySet()) {
|
||||||
|
var pc = el.getKey();
|
||||||
|
var loc = el.getValue();
|
||||||
|
|
||||||
|
var a = pcToLoc.remove(pc);
|
||||||
|
var b = locToPc.remove(loc);
|
||||||
|
|
||||||
|
if (b != null) pcToLoc.remove(b);
|
||||||
|
if (a != null) locToPc.remove(a);
|
||||||
|
}
|
||||||
|
for (var el : breakpoints.entrySet()) {
|
||||||
|
if (el.getValue() == null || el.getValue() == BreakpointType.NONE) continue;
|
||||||
|
bps.put(el.getKey(), el.getValue());
|
||||||
|
bpLocs.add(el.getKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
this.localNames = localNames;
|
||||||
|
this.captureNames = captureNames;
|
||||||
|
}
|
||||||
|
private FunctionMap() {
|
||||||
|
localNames = new String[0];
|
||||||
|
captureNames = new String[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FunctionMapBuilder builder() {
|
||||||
|
return new FunctionMapBuilder();
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package me.topchetoeu.jscript.utils.mapping;
|
package me.topchetoeu.jscript.compilation.mapping;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -32,11 +32,12 @@ public class SourceMap {
|
|||||||
|
|
||||||
for (var el : new ArrayList<>(origToComp.entrySet())) {
|
for (var el : new ArrayList<>(origToComp.entrySet())) {
|
||||||
var mapped = convert(el.getValue(), map.origToComp);
|
var mapped = convert(el.getValue(), map.origToComp);
|
||||||
add(el.getKey(), mapped);
|
res.origToComp.put(el.getKey(), mapped);
|
||||||
}
|
}
|
||||||
for (var el : new ArrayList<>(compToOrig.entrySet())) {
|
for (var el : new ArrayList<>(compToOrig.entrySet())) {
|
||||||
var mapped = convert(el.getKey(), map.compToOrig);
|
var mapped = convert(el.getKey(), map.compToOrig);
|
||||||
add(el.getValue(), mapped);
|
res.compToOrig.put(mapped, el.getValue());
|
||||||
|
res.add(el.getValue(), mapped);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
@ -1,9 +1,9 @@
|
|||||||
package me.topchetoeu.jscript.core.parsing;
|
package me.topchetoeu.jscript.compilation.parsing;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.core.engine.Operation;
|
import me.topchetoeu.jscript.core.Operation;
|
||||||
|
|
||||||
public enum Operator {
|
public enum Operator {
|
||||||
MULTIPLY("*", Operation.MULTIPLY, 13),
|
MULTIPLY("*", Operation.MULTIPLY, 13),
|
||||||
@ -55,7 +55,7 @@ public enum Operator {
|
|||||||
INCREASE("++"),
|
INCREASE("++"),
|
||||||
DECREASE("--");
|
DECREASE("--");
|
||||||
|
|
||||||
public final String value;
|
public final String readable;
|
||||||
public final Operation operation;
|
public final Operation operation;
|
||||||
public final int precedence;
|
public final int precedence;
|
||||||
public final boolean reverse;
|
public final boolean reverse;
|
||||||
@ -63,7 +63,7 @@ public enum Operator {
|
|||||||
|
|
||||||
static {
|
static {
|
||||||
for (var el : Operator.values()) {
|
for (var el : Operator.values()) {
|
||||||
ops.put(el.value, el);
|
ops.put(el.readable, el);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,38 +74,38 @@ public enum Operator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Operator() {
|
private Operator() {
|
||||||
this.value = null;
|
this.readable = null;
|
||||||
this.operation = null;
|
this.operation = null;
|
||||||
this.precedence = -1;
|
this.precedence = -1;
|
||||||
this.reverse = false;
|
this.reverse = false;
|
||||||
}
|
}
|
||||||
private Operator(String value) {
|
private Operator(String value) {
|
||||||
this. value = value;
|
this.readable = value;
|
||||||
this.operation = null;
|
this.operation = null;
|
||||||
this.precedence = -1;
|
this.precedence = -1;
|
||||||
this.reverse = false;
|
this.reverse = false;
|
||||||
}
|
}
|
||||||
private Operator(String value, int precedence) {
|
private Operator(String value, int precedence) {
|
||||||
this. value = value;
|
this.readable = value;
|
||||||
this.operation = null;
|
this.operation = null;
|
||||||
this.precedence = precedence;
|
this.precedence = precedence;
|
||||||
this.reverse = false;
|
this.reverse = false;
|
||||||
}
|
}
|
||||||
private Operator(String value, int precedence, boolean reverse) {
|
private Operator(String value, int precedence, boolean reverse) {
|
||||||
this. value = value;
|
this.readable = value;
|
||||||
this.operation = null;
|
this.operation = null;
|
||||||
this.precedence = precedence;
|
this.precedence = precedence;
|
||||||
this.reverse = reverse;
|
this.reverse = reverse;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Operator(String value, Operation funcName, int precedence) {
|
private Operator(String value, Operation funcName, int precedence) {
|
||||||
this. value = value;
|
this.readable = value;
|
||||||
this.operation = funcName;
|
this.operation = funcName;
|
||||||
this.precedence = precedence;
|
this.precedence = precedence;
|
||||||
this.reverse = false;
|
this.reverse = false;
|
||||||
}
|
}
|
||||||
private Operator(String value, Operation funcName, int precedence, boolean reverse) {
|
private Operator(String value, Operation funcName, int precedence, boolean reverse) {
|
||||||
this.value = value;
|
this.readable = value;
|
||||||
this.operation = funcName;
|
this.operation = funcName;
|
||||||
this.precedence = precedence;
|
this.precedence = precedence;
|
||||||
this.reverse = reverse;
|
this.reverse = reverse;
|
@ -1,10 +1,10 @@
|
|||||||
package me.topchetoeu.jscript.core.parsing;
|
package me.topchetoeu.jscript.compilation.parsing;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Location;
|
import me.topchetoeu.jscript.common.Location;
|
||||||
import me.topchetoeu.jscript.core.parsing.Parsing.Parser;
|
import me.topchetoeu.jscript.compilation.parsing.Parsing.Parser;
|
||||||
|
|
||||||
public class ParseRes<T> {
|
public class ParseRes<T> {
|
||||||
public static enum State {
|
public static enum State {
|
@ -1,4 +1,4 @@
|
|||||||
package me.topchetoeu.jscript.core.parsing;
|
package me.topchetoeu.jscript.compilation.parsing;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@ -6,21 +6,18 @@ import java.util.HashMap;
|
|||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.TreeSet;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Filename;
|
import me.topchetoeu.jscript.common.Filename;
|
||||||
import me.topchetoeu.jscript.common.Location;
|
import me.topchetoeu.jscript.common.Location;
|
||||||
import me.topchetoeu.jscript.core.compilation.*;
|
import me.topchetoeu.jscript.compilation.*;
|
||||||
import me.topchetoeu.jscript.core.compilation.VariableDeclareStatement.Pair;
|
import me.topchetoeu.jscript.compilation.VariableDeclareStatement.Pair;
|
||||||
import me.topchetoeu.jscript.core.compilation.control.*;
|
import me.topchetoeu.jscript.compilation.control.*;
|
||||||
import me.topchetoeu.jscript.core.compilation.control.SwitchStatement.SwitchCase;
|
import me.topchetoeu.jscript.compilation.control.SwitchStatement.SwitchCase;
|
||||||
import me.topchetoeu.jscript.core.compilation.values.*;
|
import me.topchetoeu.jscript.compilation.parsing.ParseRes.State;
|
||||||
import me.topchetoeu.jscript.core.engine.Environment;
|
import me.topchetoeu.jscript.compilation.values.*;
|
||||||
import me.topchetoeu.jscript.core.engine.Operation;
|
import me.topchetoeu.jscript.core.Operation;
|
||||||
import me.topchetoeu.jscript.core.engine.scope.LocalScopeRecord;
|
import me.topchetoeu.jscript.core.scope.LocalScopeRecord;
|
||||||
import me.topchetoeu.jscript.core.engine.values.Values;
|
|
||||||
import me.topchetoeu.jscript.core.exceptions.SyntaxException;
|
import me.topchetoeu.jscript.core.exceptions.SyntaxException;
|
||||||
import me.topchetoeu.jscript.core.parsing.ParseRes.State;
|
|
||||||
|
|
||||||
// TODO: this has to be rewritten
|
// TODO: this has to be rewritten
|
||||||
public class Parsing {
|
public class Parsing {
|
||||||
@ -29,11 +26,11 @@ public class Parsing {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static class ObjProp {
|
private static class ObjProp {
|
||||||
public final Object name;
|
public final String name;
|
||||||
public final String access;
|
public final String access;
|
||||||
public final FunctionStatement func;
|
public final FunctionStatement func;
|
||||||
|
|
||||||
public ObjProp(Object name, String access, FunctionStatement func) {
|
public ObjProp(String name, String access, FunctionStatement func) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.access = access;
|
this.access = access;
|
||||||
this.func = func;
|
this.func = func;
|
||||||
@ -579,9 +576,9 @@ public class Parsing {
|
|||||||
var loc = new Location(el.line, el.start, filename);
|
var loc = new Location(el.line, el.start, filename);
|
||||||
switch (el.type) {
|
switch (el.type) {
|
||||||
case LITERAL: res.add(Token.identifier(el.line, el.start, el.value)); break;
|
case LITERAL: res.add(Token.identifier(el.line, el.start, el.value)); break;
|
||||||
case NUMBER: res.add(Token.number(el.line, el.start, parseNumber(loc, el.value))); break;
|
case NUMBER: res.add(Token.number(el.line, el.start, parseNumber(loc, el.value), el.value)); break;
|
||||||
case STRING: res.add(Token.string(el.line, el.start, parseString(loc, el.value))); break;
|
case STRING: res.add(Token.string(el.line, el.start, parseString(loc, el.value), el.value)); break;
|
||||||
case REGEX: res.add(Token.regex(el.line, el.start, parseRegex(loc, el.value))); break;
|
case REGEX: res.add(Token.regex(el.line, el.start, parseRegex(loc, el.value), el.value)); break;
|
||||||
case OPERATOR:
|
case OPERATOR:
|
||||||
Operator op = Operator.parse(el.value);
|
Operator op = Operator.parse(el.value);
|
||||||
if (op == null) throw new SyntaxException(loc, String.format("Unrecognized operator '%s'.", el.value));
|
if (op == null) throw new SyntaxException(loc, String.format("Unrecognized operator '%s'.", el.value));
|
||||||
@ -776,15 +773,18 @@ public class Parsing {
|
|||||||
return ParseRes.res(args, n);
|
return ParseRes.res(args, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ParseRes<? extends Object> parsePropName(Filename filename, List<Token> tokens, int i) {
|
public static ParseRes<String> parsePropName(Filename filename, List<Token> tokens, int i) {
|
||||||
var idRes = parseIdentifier(tokens, i);
|
var loc = getLoc(filename, tokens, i);
|
||||||
if (idRes.isSuccess()) return ParseRes.res(idRes.result, 1);
|
|
||||||
var strRes = parseString(null, tokens, i);
|
|
||||||
if (strRes.isSuccess()) return ParseRes.res(strRes.result.value, 1);
|
|
||||||
var numRes = parseNumber(null, tokens, i);
|
|
||||||
if (numRes.isSuccess()) return ParseRes.res(numRes.result.value, 1);
|
|
||||||
|
|
||||||
return ParseRes.failed();
|
try {
|
||||||
|
var token = tokens.get(i);
|
||||||
|
|
||||||
|
if (token.isNumber() || token.isIdentifier() || token.isString()) return ParseRes.res(token.rawValue, 1);
|
||||||
|
else return ParseRes.error(loc, "Expected identifier, string or number literal.");
|
||||||
|
}
|
||||||
|
catch (IndexOutOfBoundsException e) {
|
||||||
|
return ParseRes.failed();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
public static ParseRes<ObjProp> parseObjectProp(Filename filename, List<Token> tokens, int i) {
|
public static ParseRes<ObjProp> parseObjectProp(Filename filename, List<Token> tokens, int i) {
|
||||||
var loc = getLoc(filename, tokens, i);
|
var loc = getLoc(filename, tokens, i);
|
||||||
@ -820,9 +820,9 @@ public class Parsing {
|
|||||||
int n = 0;
|
int n = 0;
|
||||||
if (!isOperator(tokens, i + n++, Operator.BRACE_OPEN)) return ParseRes.failed();
|
if (!isOperator(tokens, i + n++, Operator.BRACE_OPEN)) return ParseRes.failed();
|
||||||
|
|
||||||
var values = new LinkedHashMap<Object, Statement>();
|
var values = new LinkedHashMap<String, Statement>();
|
||||||
var getters = new LinkedHashMap<Object, FunctionStatement>();
|
var getters = new LinkedHashMap<String, FunctionStatement>();
|
||||||
var setters = new LinkedHashMap<Object, FunctionStatement>();
|
var setters = new LinkedHashMap<String, FunctionStatement>();
|
||||||
|
|
||||||
if (isOperator(tokens, i + n, Operator.BRACE_CLOSE)) {
|
if (isOperator(tokens, i + n, Operator.BRACE_CLOSE)) {
|
||||||
n++;
|
n++;
|
||||||
@ -994,7 +994,7 @@ public class Parsing {
|
|||||||
var res = parseValue(filename, tokens, n + i, 14);
|
var res = parseValue(filename, tokens, n + i, 14);
|
||||||
|
|
||||||
if (res.isSuccess()) return ParseRes.res(new OperationStatement(loc, operation, res.result), n + res.n);
|
if (res.isSuccess()) return ParseRes.res(new OperationStatement(loc, operation, res.result), n + res.n);
|
||||||
else return ParseRes.error(loc, String.format("Expected a value after the unary operator '%s'.", op.value), res);
|
else return ParseRes.error(loc, String.format("Expected a value after the unary operator '%s'.", op.readable), res);
|
||||||
}
|
}
|
||||||
public static ParseRes<ChangeStatement> parsePrefixChange(Filename filename, List<Token> tokens, int i) {
|
public static ParseRes<ChangeStatement> parsePrefixChange(Filename filename, List<Token> tokens, int i) {
|
||||||
var loc = getLoc(filename, tokens, i);
|
var loc = getLoc(filename, tokens, i);
|
||||||
@ -1079,10 +1079,10 @@ public class Parsing {
|
|||||||
return ParseRes.res(new ConstantStatement(loc, false), 1);
|
return ParseRes.res(new ConstantStatement(loc, false), 1);
|
||||||
}
|
}
|
||||||
if (id.result.equals("undefined")) {
|
if (id.result.equals("undefined")) {
|
||||||
return ParseRes.res(new ConstantStatement(loc, null), 1);
|
return ParseRes.res(ConstantStatement.ofUndefined(loc), 1);
|
||||||
}
|
}
|
||||||
if (id.result.equals("null")) {
|
if (id.result.equals("null")) {
|
||||||
return ParseRes.res(new ConstantStatement(loc, Values.NULL), 1);
|
return ParseRes.res(ConstantStatement.ofNull(loc), 1);
|
||||||
}
|
}
|
||||||
if (id.result.equals("this")) {
|
if (id.result.equals("this")) {
|
||||||
return ParseRes.res(new VariableIndexStatement(loc, 0), 1);
|
return ParseRes.res(new VariableIndexStatement(loc, 0), 1);
|
||||||
@ -1141,7 +1141,7 @@ public class Parsing {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var res = parseValue(filename, tokens, i + n, 2);
|
var res = parseValue(filename, tokens, i + n, 2);
|
||||||
if (!res.isSuccess()) return ParseRes.error(loc, String.format("Expected value after assignment operator '%s'.", op.value), res);
|
if (!res.isSuccess()) return ParseRes.error(loc, String.format("Expected value after assignment operator '%s'.", op.readable), res);
|
||||||
n += res.n;
|
n += res.n;
|
||||||
|
|
||||||
Operation operation = null;
|
Operation operation = null;
|
||||||
@ -1279,7 +1279,7 @@ public class Parsing {
|
|||||||
if (op.isAssign()) return parseAssign(filename, tokens, i + n - 1, prev, precedence);
|
if (op.isAssign()) return parseAssign(filename, tokens, i + n - 1, prev, precedence);
|
||||||
|
|
||||||
var res = parseValue(filename, tokens, i + n, op.precedence + (op.reverse ? 0 : 1));
|
var res = parseValue(filename, tokens, i + n, op.precedence + (op.reverse ? 0 : 1));
|
||||||
if (!res.isSuccess()) return ParseRes.error(loc, String.format("Expected a value after the '%s' operator.", op.value), res);
|
if (!res.isSuccess()) return ParseRes.error(loc, String.format("Expected a value after the '%s' operator.", op.readable), res);
|
||||||
n += res.n;
|
n += res.n;
|
||||||
|
|
||||||
if (op == Operator.LAZY_AND) {
|
if (op == Operator.LAZY_AND) {
|
||||||
@ -1875,30 +1875,32 @@ public class Parsing {
|
|||||||
return list.toArray(Statement[]::new);
|
return list.toArray(Statement[]::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CompileTarget compile(Environment environment, Statement ...statements) {
|
public static CompileResult compile(Statement ...statements) {
|
||||||
var subscope = new LocalScopeRecord();
|
var target = new CompileResult(new LocalScopeRecord());
|
||||||
var target = new CompileTarget(new HashMap<>(), new TreeSet<>());
|
|
||||||
var stm = new CompoundStatement(null, true, statements);
|
var stm = new CompoundStatement(null, true, statements);
|
||||||
|
|
||||||
subscope.define("this");
|
target.scope.define("this");
|
||||||
subscope.define("arguments");
|
target.scope.define("arguments");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
stm.compile(target, subscope, true);
|
stm.compile(target, true);
|
||||||
FunctionStatement.checkBreakAndCont(target, 0);
|
FunctionStatement.checkBreakAndCont(target, 0);
|
||||||
}
|
}
|
||||||
catch (SyntaxException e) {
|
catch (SyntaxException e) {
|
||||||
target.target.clear();
|
target = new CompileResult(new LocalScopeRecord());
|
||||||
target.add(Instruction.throwSyntax(e.loc, e));
|
|
||||||
|
target.scope.define("this");
|
||||||
|
target.scope.define("arguments");
|
||||||
|
|
||||||
|
target.add(Instruction.throwSyntax(e)).setLocation(stm.loc());
|
||||||
}
|
}
|
||||||
|
|
||||||
target.add(Instruction.ret(stm.loc()));
|
target.add(Instruction.ret()).setLocation(stm.loc());
|
||||||
target.functions.put(0l, new FunctionBody(subscope.localsCount(), 0, target.array(), subscope.captures(), subscope.locals()));
|
|
||||||
|
|
||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
public static CompileTarget compile(Environment environment, Filename filename, String raw) {
|
public static CompileResult compile(Filename filename, String raw) {
|
||||||
try { return compile(environment, parse(filename, raw)); }
|
try { return compile(parse(filename, raw)); }
|
||||||
catch (SyntaxException e) { return compile(environment, new ThrowSyntaxStatement(e)); }
|
catch (SyntaxException e) { return compile(new ThrowSyntaxStatement(e)); }
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package me.topchetoeu.jscript.core.parsing;
|
package me.topchetoeu.jscript.compilation.parsing;
|
||||||
|
|
||||||
public class RawToken {
|
public class RawToken {
|
||||||
public final String value;
|
public final String value;
|
@ -1,7 +1,7 @@
|
|||||||
package me.topchetoeu.jscript.core.parsing;
|
package me.topchetoeu.jscript.compilation.parsing;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Location;
|
import me.topchetoeu.jscript.common.Location;
|
||||||
import me.topchetoeu.jscript.core.parsing.ParseRes.State;
|
import me.topchetoeu.jscript.compilation.parsing.ParseRes.State;
|
||||||
|
|
||||||
public class TestRes {
|
public class TestRes {
|
||||||
public final State state;
|
public final State state;
|
@ -1,21 +1,24 @@
|
|||||||
package me.topchetoeu.jscript.core.parsing;
|
package me.topchetoeu.jscript.compilation.parsing;
|
||||||
|
|
||||||
public class Token {
|
public class Token {
|
||||||
public final Object value;
|
public final Object value;
|
||||||
|
public final String rawValue;
|
||||||
public final boolean isString;
|
public final boolean isString;
|
||||||
public final boolean isRegex;
|
public final boolean isRegex;
|
||||||
public final int line;
|
public final int line;
|
||||||
public final int start;
|
public final int start;
|
||||||
|
|
||||||
private Token(int line, int start, Object value, boolean isString, boolean isRegex) {
|
private Token(int line, int start, Object value, String rawValue, boolean isString, boolean isRegex) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
|
this.rawValue = rawValue;
|
||||||
this.line = line;
|
this.line = line;
|
||||||
this.start = start;
|
this.start = start;
|
||||||
this.isString = isString;
|
this.isString = isString;
|
||||||
this.isRegex = isRegex;
|
this.isRegex = isRegex;
|
||||||
}
|
}
|
||||||
private Token(int line, int start, Object value) {
|
private Token(int line, int start, Object value, String rawValue) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
|
this.rawValue = rawValue;
|
||||||
this.line = line;
|
this.line = line;
|
||||||
this.start = start;
|
this.start = start;
|
||||||
this.isString = false;
|
this.isString = false;
|
||||||
@ -37,19 +40,19 @@ public class Token {
|
|||||||
public String identifier() { return (String)value; }
|
public String identifier() { return (String)value; }
|
||||||
public Operator operator() { return (Operator)value; }
|
public Operator operator() { return (Operator)value; }
|
||||||
|
|
||||||
public static Token regex(int line, int start, String val) {
|
public static Token regex(int line, int start, String val, String rawValue) {
|
||||||
return new Token(line, start, val, false, true);
|
return new Token(line, start, val, rawValue, false, true);
|
||||||
}
|
}
|
||||||
public static Token string(int line, int start, String val) {
|
public static Token string(int line, int start, String val, String rawValue) {
|
||||||
return new Token(line, start, val, true, false);
|
return new Token(line, start, val, rawValue, true, false);
|
||||||
}
|
}
|
||||||
public static Token number(int line, int start, double val) {
|
public static Token number(int line, int start, double val, String rawValue) {
|
||||||
return new Token(line, start, val);
|
return new Token(line, start, val, rawValue);
|
||||||
}
|
}
|
||||||
public static Token identifier(int line, int start, String val) {
|
public static Token identifier(int line, int start, String val) {
|
||||||
return new Token(line, start, val);
|
return new Token(line, start, val, val);
|
||||||
}
|
}
|
||||||
public static Token operator(int line, int start, Operator val) {
|
public static Token operator(int line, int start, Operator val) {
|
||||||
return new Token(line, start, val);
|
return new Token(line, start, val, val.readable);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package me.topchetoeu.jscript.core.parsing;
|
package me.topchetoeu.jscript.compilation.parsing;
|
||||||
|
|
||||||
enum TokenType {
|
enum TokenType {
|
||||||
REGEX,
|
REGEX,
|
@ -0,0 +1,40 @@
|
|||||||
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
|
|
||||||
|
public class ArrayStatement extends Statement {
|
||||||
|
public final Statement[] statements;
|
||||||
|
|
||||||
|
@Override public boolean pure() {
|
||||||
|
for (var stm : statements) {
|
||||||
|
if (!stm.pure()) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compile(CompileResult target, boolean pollute) {
|
||||||
|
target.add(Instruction.loadArr(statements.length));
|
||||||
|
|
||||||
|
for (var i = 0; i < statements.length; i++) {
|
||||||
|
var el = statements[i];
|
||||||
|
if (el != null) {
|
||||||
|
target.add(Instruction.dup());
|
||||||
|
target.add(Instruction.pushValue(i));
|
||||||
|
el.compile(target, true);
|
||||||
|
target.add(Instruction.storeMember());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pollute) target.add(Instruction.discard());
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArrayStatement(Location loc, Statement[] statements) {
|
||||||
|
super(loc);
|
||||||
|
this.statements = statements;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
|
|
||||||
|
public class CallStatement extends Statement {
|
||||||
|
public final Statement func;
|
||||||
|
public final Statement[] args;
|
||||||
|
public final boolean isNew;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compile(CompileResult target, boolean pollute, BreakpointType type) {
|
||||||
|
if (isNew) func.compile(target, true);
|
||||||
|
else if (func instanceof IndexStatement) {
|
||||||
|
((IndexStatement)func).compile(target, true, true);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
target.add(Instruction.pushUndefined());
|
||||||
|
func.compile(target, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var arg : args) arg.compile(target, true);
|
||||||
|
|
||||||
|
if (isNew) target.add(Instruction.callNew(args.length));
|
||||||
|
else target.add(Instruction.call(args.length)).setDebug(loc(), type);
|
||||||
|
|
||||||
|
if (!pollute) target.add(Instruction.discard());
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void compile(CompileResult target, boolean pollute) {
|
||||||
|
compile(target, pollute, BreakpointType.STEP_IN);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CallStatement(Location loc, boolean isNew, Statement func, Statement ...args) {
|
||||||
|
super(loc);
|
||||||
|
this.isNew = isNew;
|
||||||
|
this.func = func;
|
||||||
|
this.args = args;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.compilation.AssignableStatement;
|
||||||
|
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
|
import me.topchetoeu.jscript.core.Operation;
|
||||||
|
|
||||||
|
public class ChangeStatement extends Statement {
|
||||||
|
public final AssignableStatement value;
|
||||||
|
public final double addAmount;
|
||||||
|
public final boolean postfix;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compile(CompileResult target, boolean pollute) {
|
||||||
|
value.toAssign(new ConstantStatement(loc(), -addAmount), Operation.SUBTRACT).compile(target, true);
|
||||||
|
if (!pollute) target.add(Instruction.discard());
|
||||||
|
else if (postfix) {
|
||||||
|
target.add(Instruction.pushValue(addAmount));
|
||||||
|
target.add(Instruction.operation(Operation.SUBTRACT));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChangeStatement(Location loc, AssignableStatement value, double addAmount, boolean postfix) {
|
||||||
|
super(loc);
|
||||||
|
this.value = value;
|
||||||
|
this.addAmount = addAmount;
|
||||||
|
this.postfix = postfix;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
|
|
||||||
|
public class ConstantStatement extends Statement {
|
||||||
|
public final Object value;
|
||||||
|
public final boolean isNull;
|
||||||
|
|
||||||
|
@Override public boolean pure() { return true; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compile(CompileResult target, boolean pollute) {
|
||||||
|
if (pollute) {
|
||||||
|
if (isNull) target.add(Instruction.pushNull());
|
||||||
|
else if (value instanceof Double) target.add(Instruction.pushValue((Double)value));
|
||||||
|
else if (value instanceof String) target.add(Instruction.pushValue((String)value));
|
||||||
|
else if (value instanceof Boolean) target.add(Instruction.pushValue((Boolean)value));
|
||||||
|
else target.add(Instruction.pushUndefined());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ConstantStatement(Location loc, Object val, boolean isNull) {
|
||||||
|
super(loc);
|
||||||
|
this.value = val;
|
||||||
|
this.isNull = isNull;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConstantStatement(Location loc, boolean val) {
|
||||||
|
this(loc, val, false);
|
||||||
|
}
|
||||||
|
public ConstantStatement(Location loc, String val) {
|
||||||
|
this(loc, val, false);
|
||||||
|
}
|
||||||
|
public ConstantStatement(Location loc, double val) {
|
||||||
|
this(loc, val, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ConstantStatement ofUndefined(Location loc) {
|
||||||
|
return new ConstantStatement(loc, null, false);
|
||||||
|
}
|
||||||
|
public static ConstantStatement ofNull(Location loc) {
|
||||||
|
return new ConstantStatement(loc, null, true);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
|
|
||||||
|
public class DiscardStatement extends Statement {
|
||||||
|
public final Statement value;
|
||||||
|
|
||||||
|
@Override public boolean pure() { return value.pure(); }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compile(CompileResult target, boolean pollute) {
|
||||||
|
value.compile(target, false);
|
||||||
|
if (pollute) target.add(Instruction.pushUndefined());
|
||||||
|
}
|
||||||
|
|
||||||
|
public DiscardStatement(Location loc, Statement val) {
|
||||||
|
super(loc);
|
||||||
|
this.value = val;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,127 @@
|
|||||||
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||||
|
import me.topchetoeu.jscript.compilation.CompoundStatement;
|
||||||
|
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.core.exceptions.SyntaxException;
|
||||||
|
|
||||||
|
public class FunctionStatement extends Statement {
|
||||||
|
public final CompoundStatement body;
|
||||||
|
public final String varName;
|
||||||
|
public final String[] args;
|
||||||
|
public final boolean statement;
|
||||||
|
public final Location end;
|
||||||
|
|
||||||
|
@Override public boolean pure() { return varName == null && statement; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void declare(CompileResult target) {
|
||||||
|
if (varName != null && statement) target.scope.define(varName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void checkBreakAndCont(CompileResult target, int start) {
|
||||||
|
for (int i = start; i < target.size(); i++) {
|
||||||
|
if (target.get(i).type == Type.NOP) {
|
||||||
|
if (target.get(i).is(0, "break") ) {
|
||||||
|
throw new SyntaxException(target.map.toLocation(i), "Break was placed outside a loop.");
|
||||||
|
}
|
||||||
|
if (target.get(i).is(0, "cont")) {
|
||||||
|
throw new SyntaxException(target.map.toLocation(i), "Continue was placed outside a loop.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private CompileResult compileBody(CompileResult target, boolean pollute, BreakpointType bp) {
|
||||||
|
for (var i = 0; i < args.length; i++) {
|
||||||
|
for (var j = 0; j < i; j++) {
|
||||||
|
if (args[i].equals(args[j])) {
|
||||||
|
throw new SyntaxException(loc(), "Duplicate parameter '" + args[i] + "'.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var subtarget = new CompileResult(target.scope.child());
|
||||||
|
|
||||||
|
subtarget.scope.define("this");
|
||||||
|
var argsVar = subtarget.scope.define("arguments");
|
||||||
|
|
||||||
|
if (args.length > 0) {
|
||||||
|
for (var i = 0; i < args.length; i++) {
|
||||||
|
subtarget.add(Instruction.loadVar(argsVar));
|
||||||
|
subtarget.add(Instruction.pushValue(i));
|
||||||
|
subtarget.add(Instruction.loadMember());
|
||||||
|
subtarget.add(Instruction.storeVar(subtarget.scope.define(args[i])));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!statement && this.varName != null) {
|
||||||
|
subtarget.add(Instruction.storeSelfFunc((int)subtarget.scope.define(this.varName))).setLocationAndDebug(loc(), bp);
|
||||||
|
}
|
||||||
|
|
||||||
|
body.declare(subtarget);
|
||||||
|
body.compile(subtarget, false);
|
||||||
|
subtarget.length = args.length;
|
||||||
|
subtarget.add(Instruction.ret()).setLocation(end);
|
||||||
|
checkBreakAndCont(subtarget, 0);
|
||||||
|
|
||||||
|
if (pollute) target.add(Instruction.loadFunc(target.children.size(), subtarget.scope.getCaptures()));
|
||||||
|
return target.addChild(subtarget);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) {
|
||||||
|
if (this.varName != null) name = this.varName;
|
||||||
|
|
||||||
|
var hasVar = this.varName != null && statement;
|
||||||
|
var hasName = name != null;
|
||||||
|
|
||||||
|
compileBody(target, pollute || hasVar || hasName, bp);
|
||||||
|
|
||||||
|
if (hasName) {
|
||||||
|
if (pollute || hasVar) target.add(Instruction.dup());
|
||||||
|
target.add(Instruction.pushValue("name"));
|
||||||
|
target.add(Instruction.pushValue(name));
|
||||||
|
target.add(Instruction.storeMember());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasVar) {
|
||||||
|
var key = target.scope.getKey(this.varName);
|
||||||
|
|
||||||
|
if (key instanceof String) target.add(Instruction.makeVar((String)key));
|
||||||
|
target.add(Instruction.storeVar(target.scope.getKey(this.varName), false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void compile(CompileResult target, boolean pollute, String name) {
|
||||||
|
compile(target, pollute, name, BreakpointType.NONE);
|
||||||
|
}
|
||||||
|
@Override public void compile(CompileResult target, boolean pollute, BreakpointType bp) {
|
||||||
|
compile(target, pollute, (String)null, bp);
|
||||||
|
}
|
||||||
|
@Override public void compile(CompileResult target, boolean pollute) {
|
||||||
|
compile(target, pollute, (String)null, BreakpointType.NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FunctionStatement(Location loc, Location end, String varName, String[] args, boolean statement, CompoundStatement body) {
|
||||||
|
super(loc);
|
||||||
|
|
||||||
|
this.end = end;
|
||||||
|
this.varName = varName;
|
||||||
|
this.statement = statement;
|
||||||
|
|
||||||
|
this.args = args;
|
||||||
|
this.body = body;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void compileWithName(Statement stm, CompileResult target, boolean pollute, String name) {
|
||||||
|
if (stm instanceof FunctionStatement) ((FunctionStatement)stm).compile(target, pollute, name);
|
||||||
|
else stm.compile(target, pollute);
|
||||||
|
}
|
||||||
|
public static void compileWithName(Statement stm, CompileResult target, boolean pollute, String name, BreakpointType bp) {
|
||||||
|
if (stm instanceof FunctionStatement) ((FunctionStatement)stm).compile(target, pollute, name, bp);
|
||||||
|
else stm.compile(target, pollute, bp);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
|
|
||||||
|
public class GlobalThisStatement extends Statement {
|
||||||
|
@Override public boolean pure() { return true; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compile(CompileResult target, boolean pollute) {
|
||||||
|
if (pollute) target.add(Instruction.loadGlob());
|
||||||
|
}
|
||||||
|
|
||||||
|
public GlobalThisStatement(Location loc) {
|
||||||
|
super(loc);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
|
import me.topchetoeu.jscript.core.Operation;
|
||||||
|
|
||||||
|
public class IndexAssignStatement extends Statement {
|
||||||
|
public final Statement object;
|
||||||
|
public final Statement index;
|
||||||
|
public final Statement value;
|
||||||
|
public final Operation operation;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compile(CompileResult target, boolean pollute) {
|
||||||
|
if (operation != null) {
|
||||||
|
object.compile(target, true);
|
||||||
|
index.compile(target, true);
|
||||||
|
target.add(Instruction.dup(2));
|
||||||
|
|
||||||
|
target.add(Instruction.loadMember());
|
||||||
|
value.compile(target, true);
|
||||||
|
target.add(Instruction.operation(operation));
|
||||||
|
|
||||||
|
target.add(Instruction.storeMember(pollute)).setLocationAndDebug(loc(), BreakpointType.STEP_IN);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
object.compile(target, true);
|
||||||
|
index.compile(target, true);
|
||||||
|
value.compile(target, true);
|
||||||
|
|
||||||
|
target.add(Instruction.storeMember(pollute)).setLocationAndDebug(loc(), BreakpointType.STEP_IN);;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IndexAssignStatement(Location loc, Statement object, Statement index, Statement value, Operation operation) {
|
||||||
|
super(loc);
|
||||||
|
this.object = object;
|
||||||
|
this.index = index;
|
||||||
|
this.value = value;
|
||||||
|
this.operation = operation;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.compilation.AssignableStatement;
|
||||||
|
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
|
import me.topchetoeu.jscript.core.Operation;
|
||||||
|
|
||||||
|
public class IndexStatement extends AssignableStatement {
|
||||||
|
public final Statement object;
|
||||||
|
public final Statement index;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Statement toAssign(Statement val, Operation operation) {
|
||||||
|
return new IndexAssignStatement(loc(), object, index, val, operation);
|
||||||
|
}
|
||||||
|
public void compile(CompileResult target, boolean dupObj, boolean pollute) {
|
||||||
|
object.compile(target, true);
|
||||||
|
if (dupObj) target.add(Instruction.dup());
|
||||||
|
|
||||||
|
index.compile(target, true);
|
||||||
|
target.add(Instruction.loadMember()).setLocationAndDebug(loc(), BreakpointType.STEP_IN);
|
||||||
|
if (!pollute) target.add(Instruction.discard());
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void compile(CompileResult target, boolean pollute) {
|
||||||
|
compile(target, false, pollute);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IndexStatement(Location loc, Statement object, Statement index) {
|
||||||
|
super(loc);
|
||||||
|
this.object = object;
|
||||||
|
this.index = index;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
|
|
||||||
|
public class LazyAndStatement extends Statement {
|
||||||
|
public final Statement first, second;
|
||||||
|
|
||||||
|
@Override public boolean pure() { return first.pure() && second.pure(); }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compile(CompileResult target, boolean pollute) {
|
||||||
|
first.compile(target, true);
|
||||||
|
if (pollute) target.add(Instruction.dup());
|
||||||
|
int start = target.temp();
|
||||||
|
if (pollute) target.add(Instruction.discard());
|
||||||
|
second.compile(target, pollute);
|
||||||
|
target.set(start, Instruction.jmpIfNot(target.size() - start));
|
||||||
|
}
|
||||||
|
|
||||||
|
public LazyAndStatement(Location loc, Statement first, Statement second) {
|
||||||
|
super(loc);
|
||||||
|
this.first = first;
|
||||||
|
this.second = second;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
|
|
||||||
|
public class LazyOrStatement extends Statement {
|
||||||
|
public final Statement first, second;
|
||||||
|
|
||||||
|
@Override public boolean pure() { return first.pure() && second.pure(); }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compile(CompileResult target, boolean pollute) {
|
||||||
|
first.compile(target, true);
|
||||||
|
if (pollute) target.add(Instruction.dup());
|
||||||
|
int start = target.temp();
|
||||||
|
if (pollute) target.add(Instruction.discard());
|
||||||
|
second.compile(target, pollute);
|
||||||
|
target.set(start, Instruction.jmpIf(target.size() - start));
|
||||||
|
}
|
||||||
|
|
||||||
|
public LazyOrStatement(Location loc, Statement first, Statement second) {
|
||||||
|
super(loc);
|
||||||
|
this.first = first;
|
||||||
|
this.second = second;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,61 @@
|
|||||||
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
|
|
||||||
|
public class ObjectStatement extends Statement {
|
||||||
|
public final Map<String, Statement> map;
|
||||||
|
public final Map<String, FunctionStatement> getters;
|
||||||
|
public final Map<String, FunctionStatement> setters;
|
||||||
|
|
||||||
|
@Override public boolean pure() {
|
||||||
|
for (var el : map.values()) {
|
||||||
|
if (!el.pure()) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compile(CompileResult target, boolean pollute) {
|
||||||
|
target.add(Instruction.loadObj());
|
||||||
|
|
||||||
|
for (var el : map.entrySet()) {
|
||||||
|
target.add(Instruction.dup());
|
||||||
|
target.add(Instruction.pushValue(el.getKey()));
|
||||||
|
var val = el.getValue();
|
||||||
|
FunctionStatement.compileWithName(val, target, true, el.getKey().toString());
|
||||||
|
target.add(Instruction.storeMember());
|
||||||
|
}
|
||||||
|
|
||||||
|
var keys = new ArrayList<Object>();
|
||||||
|
keys.addAll(getters.keySet());
|
||||||
|
keys.addAll(setters.keySet());
|
||||||
|
|
||||||
|
for (var key : keys) {
|
||||||
|
target.add(Instruction.pushValue((String)key));
|
||||||
|
|
||||||
|
if (getters.containsKey(key)) getters.get(key).compile(target, true);
|
||||||
|
else target.add(Instruction.pushUndefined());
|
||||||
|
|
||||||
|
if (setters.containsKey(key)) setters.get(key).compile(target, true);
|
||||||
|
else target.add(Instruction.pushUndefined());
|
||||||
|
|
||||||
|
target.add(Instruction.defProp());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pollute) target.add(Instruction.discard());
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObjectStatement(Location loc, Map<String, Statement> map, Map<String, FunctionStatement> getters, Map<String, FunctionStatement> setters) {
|
||||||
|
super(loc);
|
||||||
|
this.map = map;
|
||||||
|
this.getters = getters;
|
||||||
|
this.setters = setters;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
|
import me.topchetoeu.jscript.core.Operation;
|
||||||
|
|
||||||
|
public class OperationStatement extends Statement {
|
||||||
|
public final Statement[] args;
|
||||||
|
public final Operation operation;
|
||||||
|
|
||||||
|
@Override public boolean pure() {
|
||||||
|
for (var el : args) {
|
||||||
|
if (!el.pure()) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compile(CompileResult target, boolean pollute) {
|
||||||
|
for (var arg : args) {
|
||||||
|
arg.compile(target, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pollute) target.add(Instruction.operation(operation));
|
||||||
|
else target.add(Instruction.discard());
|
||||||
|
}
|
||||||
|
|
||||||
|
public OperationStatement(Location loc, Operation operation, Statement ...args) {
|
||||||
|
super(loc);
|
||||||
|
this.operation = operation;
|
||||||
|
this.args = args;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
|
|
||||||
|
public class RegexStatement extends Statement {
|
||||||
|
public final String pattern, flags;
|
||||||
|
|
||||||
|
// Not really pure, since a function is called, but can be ignored.
|
||||||
|
@Override public boolean pure() { return true; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compile(CompileResult target, boolean pollute) {
|
||||||
|
target.add(Instruction.loadRegex(pattern, flags));
|
||||||
|
if (!pollute) target.add(Instruction.discard());
|
||||||
|
}
|
||||||
|
|
||||||
|
public RegexStatement(Location loc, String pattern, String flags) {
|
||||||
|
super(loc);
|
||||||
|
this.pattern = pattern;
|
||||||
|
this.flags = flags;
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +1,9 @@
|
|||||||
package me.topchetoeu.jscript.core.compilation.values;
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Location;
|
import me.topchetoeu.jscript.common.Location;
|
||||||
import me.topchetoeu.jscript.core.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||||
import me.topchetoeu.jscript.core.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.core.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
|
|
||||||
|
|
||||||
public class TypeofStatement extends Statement {
|
public class TypeofStatement extends Statement {
|
||||||
public final Statement value;
|
public final Statement value;
|
||||||
@ -14,16 +13,16 @@ public class TypeofStatement extends Statement {
|
|||||||
@Override public boolean pure() { return true; }
|
@Override public boolean pure() { return true; }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileResult target, boolean pollute) {
|
||||||
if (value instanceof VariableStatement) {
|
if (value instanceof VariableStatement) {
|
||||||
var i = scope.getKey(((VariableStatement)value).name);
|
var i = target.scope.getKey(((VariableStatement)value).name);
|
||||||
if (i instanceof String) {
|
if (i instanceof String) {
|
||||||
target.add(Instruction.typeof(loc(), (String)i));
|
target.add(Instruction.typeof((String)i));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
value.compile(target, scope, pollute);
|
value.compile(target, pollute);
|
||||||
target.add(Instruction.typeof(loc()));
|
target.add(Instruction.typeof());
|
||||||
}
|
}
|
||||||
|
|
||||||
public TypeofStatement(Location loc, Statement value) {
|
public TypeofStatement(Location loc, Statement value) {
|
@ -0,0 +1,37 @@
|
|||||||
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
|
import me.topchetoeu.jscript.core.Operation;
|
||||||
|
|
||||||
|
public class VariableAssignStatement extends Statement {
|
||||||
|
public final String name;
|
||||||
|
public final Statement value;
|
||||||
|
public final Operation operation;
|
||||||
|
|
||||||
|
@Override public boolean pure() { return false; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compile(CompileResult target, boolean pollute) {
|
||||||
|
var i = target.scope.getKey(name);
|
||||||
|
if (operation != null) {
|
||||||
|
target.add(Instruction.loadVar(i));
|
||||||
|
FunctionStatement.compileWithName(value, target, true, name);
|
||||||
|
target.add(Instruction.operation(operation));
|
||||||
|
target.add(Instruction.storeVar(i, pollute));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
FunctionStatement.compileWithName(value, target, true, name);
|
||||||
|
target.add(Instruction.storeVar(i, pollute));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public VariableAssignStatement(Location loc, String name, Statement val, Operation operation) {
|
||||||
|
super(loc);
|
||||||
|
this.name = name;
|
||||||
|
this.value = val;
|
||||||
|
this.operation = operation;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
|
|
||||||
|
public class VariableIndexStatement extends Statement {
|
||||||
|
public final int index;
|
||||||
|
|
||||||
|
@Override public boolean pure() { return true; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compile(CompileResult target, boolean pollute) {
|
||||||
|
if (pollute) target.add(Instruction.loadVar(index));
|
||||||
|
}
|
||||||
|
|
||||||
|
public VariableIndexStatement(Location loc, int i) {
|
||||||
|
super(loc);
|
||||||
|
this.index = i;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.compilation.AssignableStatement;
|
||||||
|
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
|
import me.topchetoeu.jscript.core.Operation;
|
||||||
|
|
||||||
|
public class VariableStatement extends AssignableStatement {
|
||||||
|
public final String name;
|
||||||
|
|
||||||
|
@Override public boolean pure() { return false; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Statement toAssign(Statement val, Operation operation) {
|
||||||
|
return new VariableAssignStatement(loc(), name, val, operation);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void compile(CompileResult target, boolean pollute) {
|
||||||
|
var i = target.scope.getKey(name);
|
||||||
|
target.add(Instruction.loadVar(i));
|
||||||
|
if (!pollute) target.add(Instruction.discard());
|
||||||
|
}
|
||||||
|
|
||||||
|
public VariableStatement(Location loc, String name) {
|
||||||
|
super(loc);
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
}
|
8
src/java/me/topchetoeu/jscript/core/Compiler.java
Normal file
8
src/java/me/topchetoeu/jscript/core/Compiler.java
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package me.topchetoeu.jscript.core;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Filename;
|
||||||
|
import me.topchetoeu.jscript.compilation.FunctionBody;
|
||||||
|
|
||||||
|
public interface Compiler {
|
||||||
|
public FunctionBody compile(Filename filename, String source);
|
||||||
|
}
|
@ -1,32 +1,23 @@
|
|||||||
package me.topchetoeu.jscript.core.engine;
|
package me.topchetoeu.jscript.core;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.TreeSet;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import java.util.stream.StreamSupport;
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Filename;
|
import me.topchetoeu.jscript.common.Filename;
|
||||||
import me.topchetoeu.jscript.common.Location;
|
import me.topchetoeu.jscript.core.debug.DebugContext;
|
||||||
import me.topchetoeu.jscript.core.engine.debug.DebugContext;
|
import me.topchetoeu.jscript.core.values.FunctionValue;
|
||||||
import me.topchetoeu.jscript.core.engine.frame.CodeFrame;
|
import me.topchetoeu.jscript.core.values.Symbol;
|
||||||
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.core.exceptions.EngineException;
|
||||||
import me.topchetoeu.jscript.lib.EnvironmentLib;
|
import me.topchetoeu.jscript.lib.EnvironmentLib;
|
||||||
import me.topchetoeu.jscript.utils.mapping.SourceMap;
|
|
||||||
|
|
||||||
public class Context implements Extensions {
|
public class Context implements Extensions {
|
||||||
public static final Context NULL = new Context(null);
|
public static final Context NULL = new Context(null);
|
||||||
|
|
||||||
public final Context parent;
|
public final Context parent;
|
||||||
public final Environment environment;
|
public final Environment environment;
|
||||||
public final CodeFrame frame;
|
public final Frame frame;
|
||||||
public final Engine engine;
|
public final Engine engine;
|
||||||
public final int stackSize;
|
public final int stackSize;
|
||||||
|
|
||||||
@ -67,43 +58,39 @@ public class Context implements Extensions {
|
|||||||
var env = environment;
|
var env = environment;
|
||||||
var result = Environment.compileFunc(this).call(this, null, raw, filename.toString(), new EnvironmentLib(env));
|
var result = Environment.compileFunc(this).call(this, null, raw, filename.toString(), new EnvironmentLib(env));
|
||||||
|
|
||||||
var function = (FunctionValue)Values.getMember(this, result, "function");
|
DebugContext.get(this).onSource(filename, raw);
|
||||||
if (!DebugContext.enabled(this)) return function;
|
return (FunctionValue)result;
|
||||||
|
|
||||||
var rawMapChain = ((ArrayValue)Values.getMember(this, result, "mapChain")).toArray();
|
// var rawMapChain = ((ArrayValue)Values.getMember(this, result, "mapChain")).toArray();
|
||||||
var breakpoints = new TreeSet<>(
|
// var breakpoints = new TreeSet<>(
|
||||||
Arrays.stream(((ArrayValue)Values.getMember(this, result, "breakpoints")).toArray())
|
// Arrays.stream(((ArrayValue)Values.getMember(this, result, "breakpoints")).toArray())
|
||||||
.map(v -> Location.parse(Values.toString(this, v)))
|
// .map(v -> Location.parse(Values.toString(this, v)))
|
||||||
.collect(Collectors.toList())
|
// .collect(Collectors.toList())
|
||||||
);
|
// );
|
||||||
var maps = new SourceMap[rawMapChain.length];
|
// var maps = new SourceMap[rawMapChain.length];
|
||||||
|
|
||||||
for (var i = 0; i < maps.length; i++) maps[i] = SourceMap.parse(Values.toString(this, (String)rawMapChain[i]));
|
// for (var i = 0; i < maps.length; i++) maps[i] = SourceMap.parse(Values.toString(this, (String)rawMapChain[i]));
|
||||||
|
|
||||||
var map = SourceMap.chain(maps);
|
// var map = SourceMap.chain(maps);
|
||||||
|
|
||||||
if (map != null) {
|
// if (map != null) {
|
||||||
var newBreakpoints = new TreeSet<Location>();
|
// var newBreakpoints = new TreeSet<Location>();
|
||||||
for (var bp : breakpoints) {
|
// for (var bp : breakpoints) {
|
||||||
bp = map.toCompiled(bp);
|
// bp = map.toCompiled(bp);
|
||||||
if (bp != null) newBreakpoints.add(bp);
|
// if (bp != null) newBreakpoints.add(bp);
|
||||||
}
|
// }
|
||||||
breakpoints = newBreakpoints;
|
// breakpoints = newBreakpoints;
|
||||||
}
|
// }
|
||||||
|
|
||||||
DebugContext.get(this).onSource(filename, raw, breakpoints, map);
|
|
||||||
|
|
||||||
return function;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Context pushFrame(CodeFrame frame) {
|
public Context pushFrame(Frame frame) {
|
||||||
var res = new Context(this, frame.function.environment, frame, engine, stackSize + 1);
|
var res = new Context(this, frame.function.environment, frame, engine, stackSize + 1);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Iterable<CodeFrame> frames() {
|
public Iterable<Frame> frames() {
|
||||||
var self = this;
|
var self = this;
|
||||||
return () -> new Iterator<CodeFrame>() {
|
return () -> new Iterator<Frame>() {
|
||||||
private Context curr = self;
|
private Context curr = self;
|
||||||
|
|
||||||
private void update() {
|
private void update() {
|
||||||
@ -114,7 +101,7 @@ public class Context implements Extensions {
|
|||||||
update();
|
update();
|
||||||
return curr != null;
|
return curr != null;
|
||||||
}
|
}
|
||||||
@Override public CodeFrame next() {
|
@Override public Frame next() {
|
||||||
update();
|
update();
|
||||||
var res = curr.frame;
|
var res = curr.frame;
|
||||||
curr = curr.parent;
|
curr = curr.parent;
|
||||||
@ -122,30 +109,9 @@ public class Context implements Extensions {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
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;
|
private Context(Context parent, Environment environment, Frame frame, Engine engine, int stackSize) {
|
||||||
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.parent = parent;
|
||||||
this.environment = environment;
|
this.environment = environment;
|
||||||
this.frame = frame;
|
this.frame = frame;
|
@ -1,15 +1,15 @@
|
|||||||
package me.topchetoeu.jscript.core.engine;
|
package me.topchetoeu.jscript.core;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Filename;
|
import me.topchetoeu.jscript.common.Filename;
|
||||||
import me.topchetoeu.jscript.common.events.Awaitable;
|
import me.topchetoeu.jscript.common.events.DataNotifier;
|
||||||
import me.topchetoeu.jscript.core.compilation.FunctionBody;
|
import me.topchetoeu.jscript.compilation.FunctionBody;
|
||||||
import me.topchetoeu.jscript.core.engine.values.FunctionValue;
|
import me.topchetoeu.jscript.core.values.FunctionValue;
|
||||||
import me.topchetoeu.jscript.core.engine.values.Symbol;
|
import me.topchetoeu.jscript.core.values.Symbol;
|
||||||
|
|
||||||
public class Engine extends EventLoop implements Extensions {
|
public class Engine extends EventLoop implements Extensions {
|
||||||
public static final HashMap<Long, FunctionBody> functions = new HashMap<>();
|
public static final HashMap<Integer, FunctionBody> functions = new HashMap<>();
|
||||||
|
|
||||||
private final Environment env = new Environment();
|
private final Environment env = new Environment();
|
||||||
|
|
||||||
@ -40,12 +40,12 @@ public class Engine extends EventLoop implements Extensions {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Awaitable<Object> pushMsg(boolean micro, Environment env, FunctionValue func, Object thisArg, Object ...args) {
|
public DataNotifier<Object> pushMsg(boolean micro, Environment env, FunctionValue func, Object thisArg, Object ...args) {
|
||||||
return pushMsg(() -> {
|
return pushMsg(() -> {
|
||||||
return func.call(new Context(this, env), thisArg, args);
|
return func.call(new Context(this, env), thisArg, args);
|
||||||
}, micro);
|
}, micro);
|
||||||
}
|
}
|
||||||
public Awaitable<Object> pushMsg(boolean micro, Environment env, Filename filename, String raw, Object thisArg, Object ...args) {
|
public DataNotifier<Object> pushMsg(boolean micro, Environment env, Filename filename, String raw, Object thisArg, Object ...args) {
|
||||||
return pushMsg(() -> {
|
return pushMsg(() -> {
|
||||||
var ctx = new Context(this, env);
|
var ctx = new Context(this, env);
|
||||||
return ctx.compile(filename, raw).call(new Context(this, env), thisArg, args);
|
return ctx.compile(filename, raw).call(new Context(this, env), thisArg, args);
|
@ -1,20 +1,12 @@
|
|||||||
package me.topchetoeu.jscript.core.engine;
|
package me.topchetoeu.jscript.core;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Filename;
|
import me.topchetoeu.jscript.core.scope.GlobalScope;
|
||||||
import me.topchetoeu.jscript.common.Location;
|
import me.topchetoeu.jscript.core.values.FunctionValue;
|
||||||
import me.topchetoeu.jscript.core.engine.debug.DebugContext;
|
import me.topchetoeu.jscript.core.values.NativeFunction;
|
||||||
import me.topchetoeu.jscript.core.engine.scope.GlobalScope;
|
import me.topchetoeu.jscript.core.values.Symbol;
|
||||||
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.exceptions.EngineException;
|
||||||
import me.topchetoeu.jscript.core.parsing.Parsing;
|
|
||||||
import me.topchetoeu.jscript.utils.interop.NativeWrapperProvider;
|
import me.topchetoeu.jscript.utils.interop.NativeWrapperProvider;
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@ -22,7 +14,6 @@ public class Environment implements Extensions {
|
|||||||
|
|
||||||
public static final HashMap<String, Symbol> symbols = new HashMap<>();
|
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 COMPILE_FUNC = Symbol.get("Environment.compile");
|
||||||
|
|
||||||
public static final Symbol REGEX_CONSTR = Symbol.get("Environment.regexConstructor");
|
public static final Symbol REGEX_CONSTR = Symbol.get("Environment.regexConstructor");
|
||||||
@ -69,30 +60,23 @@ public class Environment implements Extensions {
|
|||||||
|
|
||||||
public static FunctionValue compileFunc(Extensions ext) {
|
public static FunctionValue compileFunc(Extensions ext) {
|
||||||
return ext.init(COMPILE_FUNC, new NativeFunction("compile", args -> {
|
return ext.init(COMPILE_FUNC, new NativeFunction("compile", args -> {
|
||||||
var source = args.getString(0);
|
// var source = args.getString(0);
|
||||||
var filename = args.getString(1);
|
// 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);
|
// var target = Parsing.compile(Filename.parse(filename), source);
|
||||||
Engine.functions.putAll(target.functions);
|
// var res = new ObjectValue();
|
||||||
Engine.functions.remove(0l);
|
|
||||||
|
|
||||||
res.defineProperty(args.ctx, "function", target.func(env));
|
// res.defineProperty(args.ctx, "function", target.body());
|
||||||
res.defineProperty(args.ctx, "mapChain", new ArrayValue());
|
// res.defineProperty(args.ctx, "map", target.map());
|
||||||
|
|
||||||
if (isDebug) res.defineProperty(
|
// return res;
|
||||||
args.ctx, "breakpoints",
|
|
||||||
ArrayValue.of(args.ctx, target.breakpoints.stream().map(Location::toString).collect(Collectors.toList()))
|
|
||||||
);
|
|
||||||
|
|
||||||
return res;
|
throw EngineException.ofError("No compiler attached to engine.");
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
public static FunctionValue regexConstructor(Extensions ext) {
|
public static FunctionValue regexConstructor(Extensions ext) {
|
||||||
return ext.init(COMPILE_FUNC, new NativeFunction("RegExp", args -> {
|
return ext.init(COMPILE_FUNC, new NativeFunction("RegExp", args -> {
|
||||||
throw EngineException.ofError("Regular expressions not supported.").setCtx(args.ctx.environment, args.ctx.engine);
|
throw EngineException.ofError("Regular expressions not supported.").setCtx(args.ctx);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
@ -1,9 +1,8 @@
|
|||||||
package me.topchetoeu.jscript.core.engine;
|
package me.topchetoeu.jscript.core;
|
||||||
|
|
||||||
import java.util.concurrent.PriorityBlockingQueue;
|
import java.util.concurrent.PriorityBlockingQueue;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.ResultRunnable;
|
import me.topchetoeu.jscript.common.ResultRunnable;
|
||||||
import me.topchetoeu.jscript.common.events.Awaitable;
|
|
||||||
import me.topchetoeu.jscript.common.events.DataNotifier;
|
import me.topchetoeu.jscript.common.events.DataNotifier;
|
||||||
import me.topchetoeu.jscript.core.exceptions.InterruptException;
|
import me.topchetoeu.jscript.core.exceptions.InterruptException;
|
||||||
|
|
||||||
@ -28,12 +27,12 @@ public class EventLoop {
|
|||||||
private Thread thread;
|
private Thread thread;
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public <T> Awaitable<T> pushMsg(ResultRunnable<T> runnable, boolean micro) {
|
public <T> DataNotifier<T> pushMsg(ResultRunnable<T> runnable, boolean micro) {
|
||||||
var msg = new Task(runnable, micro);
|
var msg = new Task(runnable, micro);
|
||||||
tasks.add(msg);
|
tasks.add(msg);
|
||||||
return (Awaitable<T>)msg.notifier;
|
return (DataNotifier<T>)msg.notifier;
|
||||||
}
|
}
|
||||||
public Awaitable<Object> pushMsg(Runnable runnable, boolean micro) {
|
public DataNotifier<Void> pushMsg(Runnable runnable, boolean micro) {
|
||||||
return pushMsg(() -> { runnable.run(); return null; }, micro);
|
return pushMsg(() -> { runnable.run(); return null; }, micro);
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
|||||||
package me.topchetoeu.jscript.core.engine;
|
package me.topchetoeu.jscript.core;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.core.engine.values.Symbol;
|
import me.topchetoeu.jscript.core.values.Symbol;
|
||||||
|
|
||||||
public interface Extensions {
|
public interface Extensions {
|
||||||
<T> T get(Symbol key);
|
<T> T get(Symbol key);
|
@ -1,23 +1,21 @@
|
|||||||
package me.topchetoeu.jscript.core.engine.frame;
|
package me.topchetoeu.jscript.core;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Stack;
|
import java.util.Stack;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Location;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.core.compilation.Instruction;
|
import me.topchetoeu.jscript.core.debug.DebugContext;
|
||||||
import me.topchetoeu.jscript.core.engine.Context;
|
import me.topchetoeu.jscript.core.scope.LocalScope;
|
||||||
import me.topchetoeu.jscript.core.engine.debug.DebugContext;
|
import me.topchetoeu.jscript.core.scope.ValueVariable;
|
||||||
import me.topchetoeu.jscript.core.engine.scope.LocalScope;
|
import me.topchetoeu.jscript.core.values.ArrayValue;
|
||||||
import me.topchetoeu.jscript.core.engine.scope.ValueVariable;
|
import me.topchetoeu.jscript.core.values.CodeFunction;
|
||||||
import me.topchetoeu.jscript.core.engine.values.ArrayValue;
|
import me.topchetoeu.jscript.core.values.ObjectValue;
|
||||||
import me.topchetoeu.jscript.core.engine.values.CodeFunction;
|
import me.topchetoeu.jscript.core.values.ScopeValue;
|
||||||
import me.topchetoeu.jscript.core.engine.values.ObjectValue;
|
import me.topchetoeu.jscript.core.values.Values;
|
||||||
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.EngineException;
|
||||||
import me.topchetoeu.jscript.core.exceptions.InterruptException;
|
import me.topchetoeu.jscript.core.exceptions.InterruptException;
|
||||||
|
|
||||||
public class CodeFrame {
|
public class Frame {
|
||||||
public static enum TryState {
|
public static enum TryState {
|
||||||
TRY,
|
TRY,
|
||||||
CATCH,
|
CATCH,
|
||||||
@ -101,54 +99,6 @@ public class CodeFrame {
|
|||||||
public int stackPtr = 0;
|
public int stackPtr = 0;
|
||||||
public int codePtr = 0;
|
public int codePtr = 0;
|
||||||
public boolean jumpFlag = false, popTryFlag = false;
|
public boolean jumpFlag = false, popTryFlag = false;
|
||||||
private Location prevLoc = null;
|
|
||||||
|
|
||||||
public ObjectValue getLocalScope(boolean props) {
|
|
||||||
var names = new String[scope.locals.length];
|
|
||||||
|
|
||||||
for (int i = 0; i < scope.locals.length; i++) {
|
|
||||||
var name = "local_" + (i - 2);
|
|
||||||
|
|
||||||
if (i == 0) name = "this";
|
|
||||||
else if (i == 1) name = "arguments";
|
|
||||||
else if (i < function.localNames.length) name = function.localNames[i];
|
|
||||||
|
|
||||||
names[i] = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ScopeValue(scope.locals, names);
|
|
||||||
}
|
|
||||||
public ObjectValue getCaptureScope(boolean props) {
|
|
||||||
var names = new String[scope.captures.length];
|
|
||||||
|
|
||||||
for (int i = 0; i < scope.captures.length; i++) {
|
|
||||||
var name = "capture_" + (i - 2);
|
|
||||||
if (i < function.captureNames.length) name = function.captureNames[i];
|
|
||||||
names[i] = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ScopeValue(scope.captures, names);
|
|
||||||
}
|
|
||||||
public ObjectValue getValStackScope() {
|
|
||||||
return new ObjectValue() {
|
|
||||||
@Override
|
|
||||||
protected Object getField(Context ctx, Object key) {
|
|
||||||
var i = (int)Values.toNumber(ctx, key);
|
|
||||||
if (i < 0 || i >= stackPtr) return null;
|
|
||||||
else return stack[i];
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
protected boolean hasField(Context ctx, Object key) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public List<Object> keys(boolean includeNonEnumerable) {
|
|
||||||
var res = super.keys(includeNonEnumerable);
|
|
||||||
for (var i = 0; i < stackPtr; i++) res.add(i);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addTry(int start, int end, int catchStart, int finallyStart) {
|
public void addTry(int start, int end, int catchStart, int finallyStart) {
|
||||||
var err = tryStack.empty() ? null : tryStack.peek().error;
|
var err = tryStack.empty() ? null : tryStack.peek().error;
|
||||||
@ -194,7 +144,7 @@ public class CodeFrame {
|
|||||||
if (value != Values.NO_RETURN) push(value);
|
if (value != Values.NO_RETURN) push(value);
|
||||||
|
|
||||||
Instruction instr = null;
|
Instruction instr = null;
|
||||||
if (codePtr >= 0 && codePtr < function.body.length) instr = function.body[codePtr];
|
if (codePtr >= 0 && codePtr < function.body.instructions.length) instr = function.body.instructions[codePtr];
|
||||||
|
|
||||||
if (returnValue == Values.NO_RETURN && error == null) {
|
if (returnValue == Values.NO_RETURN && error == null) {
|
||||||
try {
|
try {
|
||||||
@ -204,14 +154,12 @@ public class CodeFrame {
|
|||||||
else {
|
else {
|
||||||
DebugContext.get(ctx).onInstruction(ctx, this, instr, Values.NO_RETURN, null, false);
|
DebugContext.get(ctx).onInstruction(ctx, this, instr, Values.NO_RETURN, null, false);
|
||||||
|
|
||||||
if (instr.location != null) prevLoc = instr.location;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.jumpFlag = this.popTryFlag = false;
|
this.jumpFlag = this.popTryFlag = false;
|
||||||
returnValue = Runners.exec(ctx, instr, this);
|
returnValue = InstructionRunner.exec(ctx, instr, this);
|
||||||
}
|
}
|
||||||
catch (EngineException e) {
|
catch (EngineException e) {
|
||||||
error = e.add(ctx, function.name, prevLoc);
|
error = e.add(ctx, function.name, DebugContext.get(ctx).getMap(function).toLocation(codePtr, true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -315,9 +263,58 @@ public class CodeFrame {
|
|||||||
DebugContext.get(ctx.parent).onFramePop(ctx.parent, this);
|
DebugContext.get(ctx.parent).onFramePop(ctx.parent, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CodeFrame(Context ctx, Object thisArg, Object[] args, CodeFunction func) {
|
public ObjectValue getLocalScope() {
|
||||||
|
var names = new String[scope.locals.length];
|
||||||
|
var map = DebugContext.get(ctx).getMapOrEmpty(function);
|
||||||
|
|
||||||
|
for (int i = 0; i < scope.locals.length; i++) {
|
||||||
|
var name = "local_" + (i - 2);
|
||||||
|
|
||||||
|
if (i == 0) name = "this";
|
||||||
|
else if (i == 1) name = "arguments";
|
||||||
|
else if (i < map.localNames.length) name = map.localNames[i];
|
||||||
|
|
||||||
|
names[i] = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ScopeValue(scope.locals, names);
|
||||||
|
}
|
||||||
|
public ObjectValue getCaptureScope() {
|
||||||
|
var names = new String[scope.captures.length];
|
||||||
|
var map = DebugContext.get(ctx).getMapOrEmpty(function);
|
||||||
|
|
||||||
|
for (int i = 0; i < scope.captures.length; i++) {
|
||||||
|
var name = "capture_" + (i - 2);
|
||||||
|
if (i < map.captureNames.length) name = map.captureNames[i];
|
||||||
|
names[i] = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ScopeValue(scope.captures, names);
|
||||||
|
}
|
||||||
|
public ObjectValue getValStackScope() {
|
||||||
|
return new ObjectValue() {
|
||||||
|
@Override
|
||||||
|
protected Object getField(Context ctx, Object key) {
|
||||||
|
var i = (int)Values.toNumber(ctx, key);
|
||||||
|
if (i < 0 || i >= stackPtr) return null;
|
||||||
|
else return stack[i];
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
protected boolean hasField(Context ctx, Object key) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public List<Object> keys(boolean includeNonEnumerable) {
|
||||||
|
var res = super.keys(includeNonEnumerable);
|
||||||
|
for (var i = 0; i < stackPtr; i++) res.add(i);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public Frame(Context ctx, Object thisArg, Object[] args, CodeFunction func) {
|
||||||
this.args = args.clone();
|
this.args = args.clone();
|
||||||
this.scope = new LocalScope(func.localsN, func.captures);
|
this.scope = new LocalScope(func.body.localsN, func.captures);
|
||||||
this.scope.get(0).set(null, thisArg);
|
this.scope.get(0).set(null, thisArg);
|
||||||
var argsObj = new ArrayValue();
|
var argsObj = new ArrayValue();
|
||||||
for (var i = 0; i < args.length; i++) {
|
for (var i = 0; i < args.length; i++) {
|
@ -1,33 +1,29 @@
|
|||||||
package me.topchetoeu.jscript.core.engine.frame;
|
package me.topchetoeu.jscript.core;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.core.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.core.engine.Context;
|
import me.topchetoeu.jscript.core.scope.ValueVariable;
|
||||||
import me.topchetoeu.jscript.core.engine.Engine;
|
import me.topchetoeu.jscript.core.values.ArrayValue;
|
||||||
import me.topchetoeu.jscript.core.engine.Environment;
|
import me.topchetoeu.jscript.core.values.CodeFunction;
|
||||||
import me.topchetoeu.jscript.core.engine.Operation;
|
import me.topchetoeu.jscript.core.values.FunctionValue;
|
||||||
import me.topchetoeu.jscript.core.engine.scope.ValueVariable;
|
import me.topchetoeu.jscript.core.values.ObjectValue;
|
||||||
import me.topchetoeu.jscript.core.engine.values.ArrayValue;
|
import me.topchetoeu.jscript.core.values.Symbol;
|
||||||
import me.topchetoeu.jscript.core.engine.values.CodeFunction;
|
import me.topchetoeu.jscript.core.values.Values;
|
||||||
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.exceptions.EngineException;
|
||||||
|
|
||||||
public class Runners {
|
public class InstructionRunner {
|
||||||
public static Object execReturn(Context ctx, Instruction instr, CodeFrame frame) {
|
private static Object execReturn(Context ctx, Instruction instr, Frame frame) {
|
||||||
return frame.pop();
|
return frame.pop();
|
||||||
}
|
}
|
||||||
public static Object execThrow(Context ctx, Instruction instr, CodeFrame frame) {
|
private static Object execThrow(Context ctx, Instruction instr, Frame frame) {
|
||||||
throw new EngineException(frame.pop());
|
throw new EngineException(frame.pop());
|
||||||
}
|
}
|
||||||
public static Object execThrowSyntax(Context ctx, Instruction instr, CodeFrame frame) {
|
private static Object execThrowSyntax(Context ctx, Instruction instr, Frame frame) {
|
||||||
throw EngineException.ofSyntax((String)instr.get(0));
|
throw EngineException.ofSyntax((String)instr.get(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Object execCall(Context ctx, Instruction instr, CodeFrame frame) {
|
private static Object execCall(Context ctx, Instruction instr, Frame frame) {
|
||||||
var callArgs = frame.take(instr.get(0));
|
var callArgs = frame.take(instr.get(0));
|
||||||
var func = frame.pop();
|
var func = frame.pop();
|
||||||
var thisArg = frame.pop();
|
var thisArg = frame.pop();
|
||||||
@ -37,7 +33,7 @@ public class Runners {
|
|||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return Values.NO_RETURN;
|
return Values.NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execCallNew(Context ctx, Instruction instr, CodeFrame frame) {
|
private static Object execCallNew(Context ctx, Instruction instr, Frame frame) {
|
||||||
var callArgs = frame.take(instr.get(0));
|
var callArgs = frame.take(instr.get(0));
|
||||||
var funcObj = frame.pop();
|
var funcObj = frame.pop();
|
||||||
|
|
||||||
@ -47,13 +43,13 @@ public class Runners {
|
|||||||
return Values.NO_RETURN;
|
return Values.NO_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Object execMakeVar(Context ctx, Instruction instr, CodeFrame frame) {
|
private static Object execMakeVar(Context ctx, Instruction instr, Frame frame) {
|
||||||
var name = (String)instr.get(0);
|
var name = (String)instr.get(0);
|
||||||
ctx.environment.global.define(name);
|
ctx.environment.global.define(name);
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return Values.NO_RETURN;
|
return Values.NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execDefProp(Context ctx, Instruction instr, CodeFrame frame) {
|
private static Object execDefProp(Context ctx, Instruction instr, Frame frame) {
|
||||||
var setter = frame.pop();
|
var setter = frame.pop();
|
||||||
var getter = frame.pop();
|
var getter = frame.pop();
|
||||||
var name = frame.pop();
|
var name = frame.pop();
|
||||||
@ -62,28 +58,13 @@ public class Runners {
|
|||||||
if (getter != null && !(getter instanceof FunctionValue)) throw EngineException.ofType("Getter must be a function or undefined.");
|
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 (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.");
|
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);
|
((ObjectValue)obj).defineProperty(ctx, name, (FunctionValue)getter, (FunctionValue)setter, false, false);
|
||||||
|
|
||||||
frame.push(obj);
|
frame.push(obj);
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return Values.NO_RETURN;
|
return Values.NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execInstanceof(Context ctx, Instruction instr, CodeFrame frame) {
|
private static Object execKeys(Context ctx, Instruction instr, Frame frame) {
|
||||||
var type = frame.pop();
|
|
||||||
var obj = frame.pop();
|
|
||||||
|
|
||||||
if (!Values.isPrimitive(type)) {
|
|
||||||
var proto = Values.getMember(ctx, type, "prototype");
|
|
||||||
frame.push(Values.isInstanceOf(ctx, obj, proto));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
frame.push(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
frame.codePtr++;
|
|
||||||
return Values.NO_RETURN;
|
|
||||||
}
|
|
||||||
public static Object execKeys(Context ctx, Instruction instr, CodeFrame frame) {
|
|
||||||
var val = frame.pop();
|
var val = frame.pop();
|
||||||
|
|
||||||
var members = Values.getMembers(ctx, val, false, false);
|
var members = Values.getMembers(ctx, val, false, false);
|
||||||
@ -102,7 +83,7 @@ public class Runners {
|
|||||||
return Values.NO_RETURN;
|
return Values.NO_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Object execTryStart(Context ctx, Instruction instr, CodeFrame frame) {
|
private static Object execTryStart(Context ctx, Instruction instr, Frame frame) {
|
||||||
int start = frame.codePtr + 1;
|
int start = frame.codePtr + 1;
|
||||||
int catchStart = (int)instr.get(0);
|
int catchStart = (int)instr.get(0);
|
||||||
int finallyStart = (int)instr.get(1);
|
int finallyStart = (int)instr.get(1);
|
||||||
@ -113,12 +94,12 @@ public class Runners {
|
|||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return Values.NO_RETURN;
|
return Values.NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execTryEnd(Context ctx, Instruction instr, CodeFrame frame) {
|
private static Object execTryEnd(Context ctx, Instruction instr, Frame frame) {
|
||||||
frame.popTryFlag = true;
|
frame.popTryFlag = true;
|
||||||
return Values.NO_RETURN;
|
return Values.NO_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Object execDup(Context ctx, Instruction instr, CodeFrame frame) {
|
private static Object execDup(Context ctx, Instruction instr, Frame frame) {
|
||||||
int count = instr.get(0);
|
int count = instr.get(0);
|
||||||
|
|
||||||
for (var i = 0; i < count; i++) {
|
for (var i = 0; i < count; i++) {
|
||||||
@ -128,17 +109,17 @@ public class Runners {
|
|||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return Values.NO_RETURN;
|
return Values.NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execLoadUndefined(Context ctx, Instruction instr, CodeFrame frame) {
|
private static Object execLoadValue(Context ctx, Instruction instr, Frame frame) {
|
||||||
frame.push(null);
|
switch (instr.type) {
|
||||||
|
case PUSH_UNDEFINED: frame.push(null); break;
|
||||||
|
case PUSH_NULL: frame.push(Values.NULL); break;
|
||||||
|
default: frame.push(instr.get(0)); break;
|
||||||
|
}
|
||||||
|
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return Values.NO_RETURN;
|
return Values.NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execLoadValue(Context ctx, Instruction instr, CodeFrame frame) {
|
private static Object execLoadVar(Context ctx, Instruction instr, Frame frame) {
|
||||||
frame.push(instr.get(0));
|
|
||||||
frame.codePtr++;
|
|
||||||
return Values.NO_RETURN;
|
|
||||||
}
|
|
||||||
public static Object execLoadVar(Context ctx, Instruction instr, CodeFrame frame) {
|
|
||||||
var i = instr.get(0);
|
var i = instr.get(0);
|
||||||
|
|
||||||
if (i instanceof String) frame.push(ctx.environment.global.get(ctx, (String)i));
|
if (i instanceof String) frame.push(ctx.environment.global.get(ctx, (String)i));
|
||||||
@ -147,39 +128,39 @@ public class Runners {
|
|||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return Values.NO_RETURN;
|
return Values.NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execLoadObj(Context ctx, Instruction instr, CodeFrame frame) {
|
private static Object execLoadObj(Context ctx, Instruction instr, Frame frame) {
|
||||||
frame.push(new ObjectValue());
|
frame.push(new ObjectValue());
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return Values.NO_RETURN;
|
return Values.NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execLoadGlob(Context ctx, Instruction instr, CodeFrame frame) {
|
private static Object execLoadGlob(Context ctx, Instruction instr, Frame frame) {
|
||||||
frame.push(ctx.environment.global.obj);
|
frame.push(ctx.environment.global.obj);
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return Values.NO_RETURN;
|
return Values.NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execLoadArr(Context ctx, Instruction instr, CodeFrame frame) {
|
private static Object execLoadArr(Context ctx, Instruction instr, Frame frame) {
|
||||||
var res = new ArrayValue();
|
var res = new ArrayValue();
|
||||||
res.setSize(instr.get(0));
|
res.setSize(instr.get(0));
|
||||||
frame.push(res);
|
frame.push(res);
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return Values.NO_RETURN;
|
return Values.NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execLoadFunc(Context ctx, Instruction instr, CodeFrame frame) {
|
private static Object execLoadFunc(Context ctx, Instruction instr, Frame frame) {
|
||||||
long id = (Long)instr.get(0);
|
int id = instr.get(0);
|
||||||
var captures = new ValueVariable[instr.params.length - 1];
|
var captures = new ValueVariable[instr.params.length - 1];
|
||||||
|
|
||||||
for (var i = 1; i < instr.params.length; i++) {
|
for (var i = 1; i < instr.params.length; i++) {
|
||||||
captures[i - 1] = frame.scope.get(instr.get(i));
|
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, "", frame.function.body.children[id], captures);
|
||||||
|
|
||||||
frame.push(func);
|
frame.push(func);
|
||||||
|
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return Values.NO_RETURN;
|
return Values.NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execLoadMember(Context ctx, Instruction instr, CodeFrame frame) {
|
private static Object execLoadMember(Context ctx, Instruction instr, Frame frame) {
|
||||||
var key = frame.pop();
|
var key = frame.pop();
|
||||||
var obj = frame.pop();
|
var obj = frame.pop();
|
||||||
|
|
||||||
@ -192,11 +173,7 @@ public class Runners {
|
|||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return Values.NO_RETURN;
|
return Values.NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execLoadKeyMember(Context ctx, Instruction instr, CodeFrame frame) {
|
private static Object execLoadRegEx(Context ctx, Instruction instr, Frame frame) {
|
||||||
frame.push(instr.get(0));
|
|
||||||
return execLoadMember(ctx, instr, frame);
|
|
||||||
}
|
|
||||||
public static Object execLoadRegEx(Context ctx, Instruction instr, CodeFrame frame) {
|
|
||||||
if (ctx.hasNotNull(Environment.REGEX_CONSTR)) {
|
if (ctx.hasNotNull(Environment.REGEX_CONSTR)) {
|
||||||
frame.push(Values.callNew(ctx, ctx.get(Environment.REGEX_CONSTR), instr.get(0), instr.get(1)));
|
frame.push(Values.callNew(ctx, ctx.get(Environment.REGEX_CONSTR), instr.get(0), instr.get(1)));
|
||||||
}
|
}
|
||||||
@ -207,12 +184,12 @@ public class Runners {
|
|||||||
return Values.NO_RETURN;
|
return Values.NO_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Object execDiscard(Context ctx, Instruction instr, CodeFrame frame) {
|
private static Object execDiscard(Context ctx, Instruction instr, Frame frame) {
|
||||||
frame.pop();
|
frame.pop();
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return Values.NO_RETURN;
|
return Values.NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execStoreMember(Context ctx, Instruction instr, CodeFrame frame) {
|
private static Object execStoreMember(Context ctx, Instruction instr, Frame frame) {
|
||||||
var val = frame.pop();
|
var val = frame.pop();
|
||||||
var key = frame.pop();
|
var key = frame.pop();
|
||||||
var obj = frame.pop();
|
var obj = frame.pop();
|
||||||
@ -222,7 +199,7 @@ public class Runners {
|
|||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return Values.NO_RETURN;
|
return Values.NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execStoreVar(Context ctx, Instruction instr, CodeFrame frame) {
|
private static Object execStoreVar(Context ctx, Instruction instr, Frame frame) {
|
||||||
var val = (boolean)instr.get(1) ? frame.peek() : frame.pop();
|
var val = (boolean)instr.get(1) ? frame.peek() : frame.pop();
|
||||||
var i = instr.get(0);
|
var i = instr.get(0);
|
||||||
|
|
||||||
@ -232,18 +209,18 @@ public class Runners {
|
|||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return Values.NO_RETURN;
|
return Values.NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execStoreSelfFunc(Context ctx, Instruction instr, CodeFrame frame) {
|
private static Object execStoreSelfFunc(Context ctx, Instruction instr, Frame frame) {
|
||||||
frame.scope.locals[(int)instr.get(0)].set(ctx, frame.function);
|
frame.scope.locals[(int)instr.get(0)].set(ctx, frame.function);
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return Values.NO_RETURN;
|
return Values.NO_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Object execJmp(Context ctx, Instruction instr, CodeFrame frame) {
|
private static Object execJmp(Context ctx, Instruction instr, Frame frame) {
|
||||||
frame.codePtr += (int)instr.get(0);
|
frame.codePtr += (int)instr.get(0);
|
||||||
frame.jumpFlag = true;
|
frame.jumpFlag = true;
|
||||||
return Values.NO_RETURN;
|
return Values.NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execJmpIf(Context ctx, Instruction instr, CodeFrame frame) {
|
private static Object execJmpIf(Context ctx, Instruction instr, Frame frame) {
|
||||||
if (Values.toBoolean(frame.pop())) {
|
if (Values.toBoolean(frame.pop())) {
|
||||||
frame.codePtr += (int)instr.get(0);
|
frame.codePtr += (int)instr.get(0);
|
||||||
frame.jumpFlag = true;
|
frame.jumpFlag = true;
|
||||||
@ -251,7 +228,7 @@ public class Runners {
|
|||||||
else frame.codePtr ++;
|
else frame.codePtr ++;
|
||||||
return Values.NO_RETURN;
|
return Values.NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execJmpIfNot(Context ctx, Instruction instr, CodeFrame frame) {
|
private static Object execJmpIfNot(Context ctx, Instruction instr, Frame frame) {
|
||||||
if (Values.not(frame.pop())) {
|
if (Values.not(frame.pop())) {
|
||||||
frame.codePtr += (int)instr.get(0);
|
frame.codePtr += (int)instr.get(0);
|
||||||
frame.jumpFlag = true;
|
frame.jumpFlag = true;
|
||||||
@ -260,15 +237,7 @@ public class Runners {
|
|||||||
return Values.NO_RETURN;
|
return Values.NO_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Object execIn(Context ctx, Instruction instr, CodeFrame frame) {
|
private static Object execTypeof(Context ctx, Instruction instr, Frame frame) {
|
||||||
var obj = frame.pop();
|
|
||||||
var index = frame.pop();
|
|
||||||
|
|
||||||
frame.push(Values.hasMember(ctx, obj, index, false));
|
|
||||||
frame.codePtr++;
|
|
||||||
return Values.NO_RETURN;
|
|
||||||
}
|
|
||||||
public static Object execTypeof(Context ctx, Instruction instr, CodeFrame frame) {
|
|
||||||
String name = instr.get(0);
|
String name = instr.get(0);
|
||||||
Object obj;
|
Object obj;
|
||||||
|
|
||||||
@ -285,12 +254,12 @@ public class Runners {
|
|||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return Values.NO_RETURN;
|
return Values.NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execNop(Context ctx, Instruction instr, CodeFrame frame) {
|
private static Object execNop(Context ctx, Instruction instr, Frame frame) {
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return Values.NO_RETURN;
|
return Values.NO_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Object execDelete(Context ctx, Instruction instr, CodeFrame frame) {
|
private static Object execDelete(Context ctx, Instruction instr, Frame frame) {
|
||||||
var key = frame.pop();
|
var key = frame.pop();
|
||||||
var val = frame.pop();
|
var val = frame.pop();
|
||||||
|
|
||||||
@ -299,7 +268,7 @@ public class Runners {
|
|||||||
return Values.NO_RETURN;
|
return Values.NO_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Object execOperation(Context ctx, Instruction instr, CodeFrame frame) {
|
private static Object execOperation(Context ctx, Instruction instr, Frame frame) {
|
||||||
Operation op = instr.get(0);
|
Operation op = instr.get(0);
|
||||||
var args = new Object[op.operands];
|
var args = new Object[op.operands];
|
||||||
|
|
||||||
@ -310,7 +279,7 @@ public class Runners {
|
|||||||
return Values.NO_RETURN;
|
return Values.NO_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Object exec(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object exec(Context ctx, Instruction instr, Frame frame) {
|
||||||
switch (instr.type) {
|
switch (instr.type) {
|
||||||
case NOP: return execNop(ctx, instr, frame);
|
case NOP: return execNop(ctx, instr, frame);
|
||||||
case RETURN: return execReturn(ctx, instr, frame);
|
case RETURN: return execReturn(ctx, instr, frame);
|
||||||
@ -322,13 +291,17 @@ public class Runners {
|
|||||||
case TRY_END: return execTryEnd(ctx, instr, frame);
|
case TRY_END: return execTryEnd(ctx, instr, frame);
|
||||||
|
|
||||||
case DUP: return execDup(ctx, instr, frame);
|
case DUP: return execDup(ctx, instr, frame);
|
||||||
case LOAD_VALUE: return execLoadValue(ctx, instr, frame);
|
case PUSH_UNDEFINED:
|
||||||
|
case PUSH_NULL:
|
||||||
|
case PUSH_STRING:
|
||||||
|
case PUSH_NUMBER:
|
||||||
|
case PUSH_BOOL:
|
||||||
|
return execLoadValue(ctx, instr, frame);
|
||||||
case LOAD_VAR: return execLoadVar(ctx, instr, frame);
|
case LOAD_VAR: return execLoadVar(ctx, instr, frame);
|
||||||
case LOAD_OBJ: return execLoadObj(ctx, instr, frame);
|
case LOAD_OBJ: return execLoadObj(ctx, instr, frame);
|
||||||
case LOAD_ARR: return execLoadArr(ctx, instr, frame);
|
case LOAD_ARR: return execLoadArr(ctx, instr, frame);
|
||||||
case LOAD_FUNC: return execLoadFunc(ctx, instr, frame);
|
case LOAD_FUNC: return execLoadFunc(ctx, instr, frame);
|
||||||
case LOAD_MEMBER: return execLoadMember(ctx, instr, frame);
|
case LOAD_MEMBER: return execLoadMember(ctx, instr, frame);
|
||||||
case LOAD_VAL_MEMBER: return execLoadKeyMember(ctx, instr, frame);
|
|
||||||
case LOAD_REGEX: return execLoadRegEx(ctx, instr, frame);
|
case LOAD_REGEX: return execLoadRegEx(ctx, instr, frame);
|
||||||
case LOAD_GLOB: return execLoadGlob(ctx, instr, frame);
|
case LOAD_GLOB: return execLoadGlob(ctx, instr, frame);
|
||||||
|
|
5
src/java/me/topchetoeu/jscript/core/Key.java
Normal file
5
src/java/me/topchetoeu/jscript/core/Key.java
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
package me.topchetoeu.jscript.core;
|
||||||
|
|
||||||
|
public class Key<T> {
|
||||||
|
|
||||||
|
}
|
54
src/java/me/topchetoeu/jscript/core/Operation.java
Normal file
54
src/java/me/topchetoeu/jscript/core/Operation.java
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
package me.topchetoeu.jscript.core;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
public enum Operation {
|
||||||
|
INSTANCEOF(1, 2),
|
||||||
|
IN(2, 2),
|
||||||
|
|
||||||
|
MULTIPLY(3, 2),
|
||||||
|
DIVIDE(4, 2),
|
||||||
|
MODULO(5, 2),
|
||||||
|
ADD(6, 2),
|
||||||
|
SUBTRACT(7, 2),
|
||||||
|
|
||||||
|
USHIFT_RIGHT(8, 2),
|
||||||
|
SHIFT_RIGHT(9, 2),
|
||||||
|
SHIFT_LEFT(10, 2),
|
||||||
|
|
||||||
|
GREATER(11, 2),
|
||||||
|
LESS(12, 2),
|
||||||
|
GREATER_EQUALS(13, 2),
|
||||||
|
LESS_EQUALS(14, 2),
|
||||||
|
LOOSE_EQUALS(15, 2),
|
||||||
|
LOOSE_NOT_EQUALS(16, 2),
|
||||||
|
EQUALS(17, 2),
|
||||||
|
NOT_EQUALS(18, 2),
|
||||||
|
|
||||||
|
AND(19, 2),
|
||||||
|
OR(20, 2),
|
||||||
|
XOR(21, 2),
|
||||||
|
|
||||||
|
NEG(23, 1),
|
||||||
|
POS(24, 1),
|
||||||
|
NOT(25, 1),
|
||||||
|
INVERSE(26, 1);
|
||||||
|
|
||||||
|
private static final HashMap<Integer, Operation> operations = new HashMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
for (var val : Operation.values()) operations.put(val.numeric, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
public final int numeric;
|
||||||
|
public final int operands;
|
||||||
|
|
||||||
|
private Operation(int numeric, int n) {
|
||||||
|
this.numeric = numeric;
|
||||||
|
this.operands = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Operation fromNumeric(int i) {
|
||||||
|
return operations.get(i);
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
package me.topchetoeu.jscript.core.engine;
|
package me.topchetoeu.jscript.core;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.core.engine.values.FunctionValue;
|
import me.topchetoeu.jscript.core.values.FunctionValue;
|
||||||
import me.topchetoeu.jscript.core.engine.values.ObjectValue;
|
import me.topchetoeu.jscript.core.values.ObjectValue;
|
||||||
|
|
||||||
public interface WrapperProvider {
|
public interface WrapperProvider {
|
||||||
public ObjectValue getProto(Class<?> obj);
|
public ObjectValue getProto(Class<?> obj);
|
@ -1,21 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.core.compilation;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.core.engine.values.Values;
|
|
||||||
|
|
||||||
public final class CalculateResult {
|
|
||||||
public final boolean exists;
|
|
||||||
public final Object value;
|
|
||||||
|
|
||||||
public final boolean isTruthy() {
|
|
||||||
return exists && Values.toBoolean(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public CalculateResult(Object value) {
|
|
||||||
this.exists = true;
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
public CalculateResult() {
|
|
||||||
this.exists = false;
|
|
||||||
this.value = null;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,66 +0,0 @@
|
|||||||
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.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<>();
|
|
||||||
public final Map<Long, FunctionBody> functions;
|
|
||||||
public final TreeSet<Location> breakpoints;
|
|
||||||
private final HashMap<Location, Instruction> bpToInstr = new HashMap<>();
|
|
||||||
|
|
||||||
public Instruction add(Instruction instr) {
|
|
||||||
target.add(instr);
|
|
||||||
return instr;
|
|
||||||
}
|
|
||||||
public Instruction set(int i, Instruction instr) {
|
|
||||||
return target.set(i, instr);
|
|
||||||
}
|
|
||||||
public void setDebug(int i, BreakpointType type) {
|
|
||||||
var instr = target.get(i);
|
|
||||||
instr.breakpoint = type;
|
|
||||||
|
|
||||||
if (type == BreakpointType.NONE) {
|
|
||||||
breakpoints.remove(target.get(i).location);
|
|
||||||
bpToInstr.remove(instr.location, instr);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
breakpoints.add(target.get(i).location);
|
|
||||||
|
|
||||||
var old = bpToInstr.put(instr.location, instr);
|
|
||||||
if (old != null) old.breakpoint = BreakpointType.NONE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public void setDebug(BreakpointType type) {
|
|
||||||
setDebug(target.size() - 1, type);
|
|
||||||
}
|
|
||||||
public Instruction get(int i) {
|
|
||||||
return target.get(i);
|
|
||||||
}
|
|
||||||
public int size() { return target.size(); }
|
|
||||||
public Location lastLoc(Location fallback) {
|
|
||||||
if (target.size() == 0) return fallback;
|
|
||||||
else return target.get(target.size() - 1).location;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Instruction[] array() { return target.toArray(Instruction[]::new); }
|
|
||||||
|
|
||||||
public FunctionBody body() {
|
|
||||||
return functions.get(0l);
|
|
||||||
}
|
|
||||||
public CodeFunction func(Environment env) {
|
|
||||||
return new CodeFunction(env, "", body());
|
|
||||||
}
|
|
||||||
|
|
||||||
public CompileTarget(Map<Long, FunctionBody> functions, TreeSet<Location> breakpoints) {
|
|
||||||
this.functions = functions;
|
|
||||||
this.breakpoints = breakpoints;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.core.compilation;
|
|
||||||
|
|
||||||
public class FunctionBody {
|
|
||||||
public final Instruction[] instructions;
|
|
||||||
public final String[] captureNames, localNames;
|
|
||||||
public final int localsN, argsN;
|
|
||||||
|
|
||||||
public FunctionBody(int localsN, int argsN, Instruction[] instructions, String[] captureNames, String[] localNames) {
|
|
||||||
this.argsN = argsN;
|
|
||||||
this.localsN = localsN;
|
|
||||||
this.instructions = instructions;
|
|
||||||
this.captureNames = captureNames;
|
|
||||||
this.localNames = localNames;
|
|
||||||
}
|
|
||||||
public FunctionBody(int localsN, int argsN, Instruction[] instructions) {
|
|
||||||
this.argsN = argsN;
|
|
||||||
this.localsN = localsN;
|
|
||||||
this.instructions = instructions;
|
|
||||||
this.captureNames = new String[0];
|
|
||||||
this.localNames = new String[0];
|
|
||||||
}
|
|
||||||
public FunctionBody(Instruction... instructions) {
|
|
||||||
this.argsN = 0;
|
|
||||||
this.localsN = 2;
|
|
||||||
this.instructions = instructions;
|
|
||||||
this.captureNames = new String[0];
|
|
||||||
this.localNames = new String[0];
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,256 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.core.compilation;
|
|
||||||
|
|
||||||
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 {
|
|
||||||
RETURN,
|
|
||||||
THROW,
|
|
||||||
THROW_SYNTAX,
|
|
||||||
DELETE,
|
|
||||||
TRY_START,
|
|
||||||
TRY_END,
|
|
||||||
NOP,
|
|
||||||
|
|
||||||
CALL,
|
|
||||||
CALL_NEW,
|
|
||||||
JMP_IF,
|
|
||||||
JMP_IFN,
|
|
||||||
JMP,
|
|
||||||
|
|
||||||
LOAD_VALUE,
|
|
||||||
|
|
||||||
LOAD_VAR,
|
|
||||||
LOAD_MEMBER,
|
|
||||||
LOAD_VAL_MEMBER,
|
|
||||||
LOAD_GLOB,
|
|
||||||
|
|
||||||
LOAD_FUNC,
|
|
||||||
LOAD_ARR,
|
|
||||||
LOAD_OBJ,
|
|
||||||
STORE_SELF_FUNC,
|
|
||||||
LOAD_REGEX,
|
|
||||||
|
|
||||||
DUP,
|
|
||||||
|
|
||||||
STORE_VAR,
|
|
||||||
STORE_MEMBER,
|
|
||||||
DISCARD,
|
|
||||||
|
|
||||||
MAKE_VAR,
|
|
||||||
DEF_PROP,
|
|
||||||
KEYS,
|
|
||||||
|
|
||||||
TYPEOF,
|
|
||||||
OPERATION;
|
|
||||||
}
|
|
||||||
public static enum BreakpointType {
|
|
||||||
NONE,
|
|
||||||
STEP_OVER,
|
|
||||||
STEP_IN;
|
|
||||||
|
|
||||||
public boolean shouldStepIn() {
|
|
||||||
return this != NONE;
|
|
||||||
}
|
|
||||||
public boolean shouldStepOver() {
|
|
||||||
return this == STEP_OVER;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public final Type type;
|
|
||||||
public final Object[] params;
|
|
||||||
public Location location;
|
|
||||||
public BreakpointType breakpoint = BreakpointType.NONE;
|
|
||||||
|
|
||||||
public Instruction setDbgData(Instruction other) {
|
|
||||||
this.location = other.location;
|
|
||||||
this.breakpoint = other.breakpoint;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Instruction locate(Location loc) {
|
|
||||||
this.location = loc;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public <T> T get(int i) {
|
|
||||||
if (i >= params.length || i < 0) return null;
|
|
||||||
return (T)params[i];
|
|
||||||
}
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public <T> T get(int i, T defaultVal) {
|
|
||||||
if (i >= params.length || i < 0) return defaultVal;
|
|
||||||
return (T)params[i];
|
|
||||||
}
|
|
||||||
public boolean match(Object ...args) {
|
|
||||||
if (args.length != params.length) return false;
|
|
||||||
for (int i = 0; i < args.length; i++) {
|
|
||||||
var a = params[i];
|
|
||||||
var b = args[i];
|
|
||||||
if (a == null || b == null) {
|
|
||||||
if (!(a == null && b == null)) return false;
|
|
||||||
}
|
|
||||||
if (!a.equals(b)) return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
public boolean is(int i, Object arg) {
|
|
||||||
if (params.length <= i) return false;
|
|
||||||
return params[i].equals(arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Instruction(Location location, Type type, Object ...params) {
|
|
||||||
this.location = location;
|
|
||||||
this.type = type;
|
|
||||||
this.params = params;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Instruction tryStart(Location loc, int catchStart, int finallyStart, int end) {
|
|
||||||
return new Instruction(loc, Type.TRY_START, catchStart, finallyStart, end);
|
|
||||||
}
|
|
||||||
public static Instruction tryEnd(Location loc) {
|
|
||||||
return new Instruction(loc, Type.TRY_END);
|
|
||||||
}
|
|
||||||
public static Instruction throwInstr(Location loc) {
|
|
||||||
return new Instruction(loc, Type.THROW);
|
|
||||||
}
|
|
||||||
public static Instruction throwSyntax(Location loc, SyntaxException err) {
|
|
||||||
return new Instruction(loc, Type.THROW_SYNTAX, err.getMessage());
|
|
||||||
}
|
|
||||||
public static Instruction throwSyntax(Location loc, String err) {
|
|
||||||
return new Instruction(loc, Type.THROW_SYNTAX, err);
|
|
||||||
}
|
|
||||||
public static Instruction delete(Location loc) {
|
|
||||||
return new Instruction(loc, Type.DELETE);
|
|
||||||
}
|
|
||||||
public static Instruction ret(Location loc) {
|
|
||||||
return new Instruction(loc, Type.RETURN);
|
|
||||||
}
|
|
||||||
public static Instruction debug(Location loc) {
|
|
||||||
return new Instruction(loc, Type.NOP, "debug");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Instruction nop(Location loc, Object ...params) {
|
|
||||||
for (var param : params) {
|
|
||||||
if (param instanceof String) continue;
|
|
||||||
if (param instanceof Boolean) continue;
|
|
||||||
if (param instanceof Double) continue;
|
|
||||||
if (param instanceof Integer) continue;
|
|
||||||
if (param == null) continue;
|
|
||||||
|
|
||||||
throw new RuntimeException("NOP params may contain only strings, booleans, doubles, integers and nulls.");
|
|
||||||
}
|
|
||||||
return new Instruction(loc, Type.NOP, params);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Instruction call(Location loc, int argn) {
|
|
||||||
return new Instruction(loc, Type.CALL, argn);
|
|
||||||
}
|
|
||||||
public static Instruction callNew(Location loc, int argn) {
|
|
||||||
return new Instruction(loc, Type.CALL_NEW, argn);
|
|
||||||
}
|
|
||||||
public static Instruction jmp(Location loc, int offset) {
|
|
||||||
return new Instruction(loc, Type.JMP, offset);
|
|
||||||
}
|
|
||||||
public static Instruction jmpIf(Location loc, int offset) {
|
|
||||||
return new Instruction(loc, Type.JMP_IF, offset);
|
|
||||||
}
|
|
||||||
public static Instruction jmpIfNot(Location loc, int offset) {
|
|
||||||
return new Instruction(loc, Type.JMP_IFN, offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Instruction loadValue(Location loc, Object val) {
|
|
||||||
return new Instruction(loc, Type.LOAD_VALUE, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Instruction makeVar(Location loc, String name) {
|
|
||||||
return new Instruction(loc, Type.MAKE_VAR, name);
|
|
||||||
}
|
|
||||||
public static Instruction loadVar(Location loc, Object i) {
|
|
||||||
return new Instruction(loc, Type.LOAD_VAR, i);
|
|
||||||
}
|
|
||||||
public static Instruction loadGlob(Location loc) {
|
|
||||||
return new Instruction(loc, Type.LOAD_GLOB);
|
|
||||||
}
|
|
||||||
public static Instruction loadMember(Location loc) {
|
|
||||||
return new Instruction(loc, Type.LOAD_MEMBER);
|
|
||||||
}
|
|
||||||
public static Instruction loadMember(Location loc, Object key) {
|
|
||||||
if (key instanceof Number) key = ((Number)key).doubleValue();
|
|
||||||
return new Instruction(loc, Type.LOAD_VAL_MEMBER, key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Instruction loadRegex(Location loc, String pattern, String flags) {
|
|
||||||
return new Instruction(loc, Type.LOAD_REGEX, pattern, flags);
|
|
||||||
}
|
|
||||||
public static Instruction loadFunc(Location loc, long id, int[] captures) {
|
|
||||||
var args = new Object[1 + captures.length];
|
|
||||||
args[0] = id;
|
|
||||||
for (var i = 0; i < captures.length; i++) args[i + 1] = captures[i];
|
|
||||||
return new Instruction(loc, Type.LOAD_FUNC, args);
|
|
||||||
}
|
|
||||||
public static Instruction loadObj(Location loc) {
|
|
||||||
return new Instruction(loc, Type.LOAD_OBJ);
|
|
||||||
}
|
|
||||||
public static Instruction loadArr(Location loc, int count) {
|
|
||||||
return new Instruction(loc, Type.LOAD_ARR, count);
|
|
||||||
}
|
|
||||||
public static Instruction dup(Location loc) {
|
|
||||||
return new Instruction(loc, Type.DUP, 1);
|
|
||||||
}
|
|
||||||
public static Instruction dup(Location loc, int count) {
|
|
||||||
return new Instruction(loc, Type.DUP, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Instruction storeSelfFunc(Location loc, int i) {
|
|
||||||
return new Instruction(loc, Type.STORE_SELF_FUNC, i);
|
|
||||||
}
|
|
||||||
public static Instruction storeVar(Location loc, Object i) {
|
|
||||||
return new Instruction(loc, Type.STORE_VAR, i, false);
|
|
||||||
}
|
|
||||||
public static Instruction storeVar(Location loc, Object i, boolean keep) {
|
|
||||||
return new Instruction(loc, Type.STORE_VAR, i, keep);
|
|
||||||
}
|
|
||||||
public static Instruction storeMember(Location loc) {
|
|
||||||
return new Instruction(loc, Type.STORE_MEMBER, false);
|
|
||||||
}
|
|
||||||
public static Instruction storeMember(Location loc, boolean keep) {
|
|
||||||
return new Instruction(loc, Type.STORE_MEMBER, keep);
|
|
||||||
}
|
|
||||||
public static Instruction discard(Location loc) {
|
|
||||||
return new Instruction(loc, Type.DISCARD);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Instruction typeof(Location loc) {
|
|
||||||
return new Instruction(loc, Type.TYPEOF);
|
|
||||||
}
|
|
||||||
public static Instruction typeof(Location loc, Object varName) {
|
|
||||||
return new Instruction(loc, Type.TYPEOF, varName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Instruction keys(Location loc, boolean forInFormat) {
|
|
||||||
return new Instruction(loc, Type.KEYS, forInFormat);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Instruction defProp(Location loc) {
|
|
||||||
return new Instruction(loc, Type.DEF_PROP);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Instruction operation(Location loc, Operation op) {
|
|
||||||
return new Instruction(loc, Type.OPERATION, op);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
var res = type.toString();
|
|
||||||
|
|
||||||
for (int i = 0; i < params.length; i++) {
|
|
||||||
res += " " + params[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.core.compilation;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
public boolean pure() { return false; }
|
|
||||||
public void declare(ScopeRecord varsScope) { }
|
|
||||||
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, BreakpointType type) {
|
|
||||||
int start = target.size();
|
|
||||||
compile(target, scope, pollute);
|
|
||||||
|
|
||||||
if (target.size() != start) {
|
|
||||||
target.get(start).locate(loc());
|
|
||||||
target.setDebug(start, type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
compile(target, scope, pollute, BreakpointType.NONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Location loc() { return _loc; }
|
|
||||||
public void setLoc(Location loc) { _loc = loc; }
|
|
||||||
|
|
||||||
protected Statement(Location loc) {
|
|
||||||
this._loc = loc;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.core.compilation.control;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
target.add(Instruction.nop(loc(), "break", label));
|
|
||||||
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
|
||||||
}
|
|
||||||
|
|
||||||
public BreakStatement(Location loc, String label) {
|
|
||||||
super(loc);
|
|
||||||
this.label = label;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.core.compilation.control;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
target.add(Instruction.nop(loc(), "cont", label));
|
|
||||||
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
|
||||||
}
|
|
||||||
|
|
||||||
public ContinueStatement(Location loc, String label) {
|
|
||||||
super(loc);
|
|
||||||
this.label = label;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.core.compilation.control;
|
|
||||||
|
|
||||||
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
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
target.add(Instruction.debug(loc()));
|
|
||||||
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
|
||||||
}
|
|
||||||
|
|
||||||
public DebugStatement(Location loc) {
|
|
||||||
super(loc);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.core.compilation.control;
|
|
||||||
|
|
||||||
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;
|
|
||||||
public final Statement value;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
value.compile(target, scope, true);
|
|
||||||
key.compile(target, scope, true);
|
|
||||||
|
|
||||||
target.add(Instruction.delete(loc()));
|
|
||||||
if (pollute) target.add(Instruction.loadValue(loc(), true));
|
|
||||||
}
|
|
||||||
|
|
||||||
public DeleteStatement(Location loc, Statement key, Statement value) {
|
|
||||||
super(loc);
|
|
||||||
this.key = key;
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.core.compilation.control;
|
|
||||||
|
|
||||||
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;
|
|
||||||
public final String label;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void declare(ScopeRecord globScope) {
|
|
||||||
body.declare(globScope);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
int start = target.size();
|
|
||||||
body.compile(target, scope, false, BreakpointType.STEP_OVER);
|
|
||||||
int mid = target.size();
|
|
||||||
condition.compile(target, scope, true, BreakpointType.STEP_OVER);
|
|
||||||
int end = target.size();
|
|
||||||
|
|
||||||
WhileStatement.replaceBreaks(target, label, start, mid - 1, mid, end + 1);
|
|
||||||
target.add(Instruction.jmpIf(loc(), start - end));
|
|
||||||
}
|
|
||||||
|
|
||||||
public DoWhileStatement(Location loc, String label, Statement condition, Statement body) {
|
|
||||||
super(loc);
|
|
||||||
this.label = label;
|
|
||||||
this.condition = condition;
|
|
||||||
this.body = body;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,73 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.core.compilation.control;
|
|
||||||
|
|
||||||
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;
|
|
||||||
public final boolean isDeclaration;
|
|
||||||
public final Statement varValue, object, body;
|
|
||||||
public final String label;
|
|
||||||
public final Location varLocation;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void declare(ScopeRecord globScope) {
|
|
||||||
body.declare(globScope);
|
|
||||||
if (isDeclaration) globScope.define(varName);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
var key = scope.getKey(varName);
|
|
||||||
|
|
||||||
int first = target.size();
|
|
||||||
if (key instanceof String) target.add(Instruction.makeVar(loc(), (String)key));
|
|
||||||
|
|
||||||
if (varValue != null) {
|
|
||||||
varValue.compile(target, scope, true);
|
|
||||||
target.add(Instruction.storeVar(loc(), scope.getKey(varName)));
|
|
||||||
}
|
|
||||||
|
|
||||||
object.compile(target, scope, true, BreakpointType.STEP_OVER);
|
|
||||||
target.add(Instruction.keys(loc(), true));
|
|
||||||
|
|
||||||
int start = target.size();
|
|
||||||
target.add(Instruction.dup(loc()));
|
|
||||||
target.add(Instruction.loadValue(loc(), null));
|
|
||||||
target.add(Instruction.operation(loc(), Operation.EQUALS));
|
|
||||||
int mid = target.size();
|
|
||||||
target.add(Instruction.nop(loc()));
|
|
||||||
|
|
||||||
target.add(Instruction.loadMember(varLocation, "value"));
|
|
||||||
target.add(Instruction.storeVar(object.loc(), key));
|
|
||||||
target.setDebug(BreakpointType.STEP_OVER);
|
|
||||||
|
|
||||||
body.compile(target, scope, false, BreakpointType.STEP_OVER);
|
|
||||||
|
|
||||||
int end = target.size();
|
|
||||||
|
|
||||||
WhileStatement.replaceBreaks(target, label, mid + 1, end, start, end + 1);
|
|
||||||
|
|
||||||
target.add(Instruction.jmp(loc(), start - end));
|
|
||||||
target.add(Instruction.discard(loc()));
|
|
||||||
target.set(mid, Instruction.jmpIf(loc(), end - mid + 1));
|
|
||||||
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
|
||||||
target.get(first).locate(loc());
|
|
||||||
}
|
|
||||||
|
|
||||||
public ForInStatement(Location loc, Location varLocation, String label, boolean isDecl, String varName, Statement varValue, Statement object, Statement body) {
|
|
||||||
super(loc);
|
|
||||||
this.varLocation = varLocation;
|
|
||||||
this.label = label;
|
|
||||||
this.isDeclaration = isDecl;
|
|
||||||
this.varName = varName;
|
|
||||||
this.varValue = varValue;
|
|
||||||
this.object = object;
|
|
||||||
this.body = body;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,47 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.core.compilation.control;
|
|
||||||
|
|
||||||
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;
|
|
||||||
public final String label;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void declare(ScopeRecord globScope) {
|
|
||||||
declaration.declare(globScope);
|
|
||||||
body.declare(globScope);
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
declaration.compile(target, scope, false, BreakpointType.STEP_OVER);
|
|
||||||
|
|
||||||
int start = target.size();
|
|
||||||
condition.compile(target, scope, true, BreakpointType.STEP_OVER);
|
|
||||||
int mid = target.size();
|
|
||||||
target.add(Instruction.nop(null));
|
|
||||||
body.compile(target, scope, false, BreakpointType.STEP_OVER);
|
|
||||||
int beforeAssign = target.size();
|
|
||||||
assignment.compile(target, scope, false, BreakpointType.STEP_OVER);
|
|
||||||
int end = target.size();
|
|
||||||
|
|
||||||
WhileStatement.replaceBreaks(target, label, mid + 1, end, beforeAssign, end + 1);
|
|
||||||
|
|
||||||
target.add(Instruction.jmp(loc(), start - end));
|
|
||||||
target.set(mid, Instruction.jmpIfNot(loc(), end - mid + 1));
|
|
||||||
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
|
||||||
}
|
|
||||||
|
|
||||||
public ForStatement(Location loc, String label, Statement declaration, Statement condition, Statement assignment, Statement body) {
|
|
||||||
super(loc);
|
|
||||||
this.label = label;
|
|
||||||
this.declaration = declaration;
|
|
||||||
this.condition = condition;
|
|
||||||
this.assignment = assignment;
|
|
||||||
this.body = body;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,52 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.core.compilation.control;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void declare(ScopeRecord globScope) {
|
|
||||||
body.declare(globScope);
|
|
||||||
if (elseBody != null) elseBody.declare(globScope);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, BreakpointType breakpoint) {
|
|
||||||
condition.compile(target, scope, true, breakpoint);
|
|
||||||
|
|
||||||
if (elseBody == null) {
|
|
||||||
int i = target.size();
|
|
||||||
target.add(Instruction.nop(null));
|
|
||||||
body.compile(target, scope, pollute, breakpoint);
|
|
||||||
int endI = target.size();
|
|
||||||
target.set(i, Instruction.jmpIfNot(loc(), endI - i));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
int start = target.size();
|
|
||||||
target.add(Instruction.nop(null));
|
|
||||||
body.compile(target, scope, pollute, breakpoint);
|
|
||||||
target.add(Instruction.nop(null));
|
|
||||||
int mid = target.size();
|
|
||||||
elseBody.compile(target, scope, pollute, breakpoint);
|
|
||||||
int end = target.size();
|
|
||||||
|
|
||||||
target.set(start, Instruction.jmpIfNot(loc(), mid - start));
|
|
||||||
target.set(mid - 1, Instruction.jmp(loc(), end - mid + 1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@Override public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
compile(target, scope, pollute, BreakpointType.STEP_IN);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IfStatement(Location loc, Statement condition, Statement body, Statement elseBody) {
|
|
||||||
super(loc);
|
|
||||||
this.condition = condition;
|
|
||||||
this.body = body;
|
|
||||||
this.elseBody = elseBody;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.core.compilation.control;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
if (value == null) target.add(Instruction.loadValue(loc(), null));
|
|
||||||
else value.compile(target, scope, true);
|
|
||||||
target.add(Instruction.ret(loc()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReturnStatement(Location loc, Statement value) {
|
|
||||||
super(loc);
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,87 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.core.compilation.control;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
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 {
|
|
||||||
public final Statement value;
|
|
||||||
public final int statementI;
|
|
||||||
|
|
||||||
public SwitchCase(Statement value, int statementI) {
|
|
||||||
this.value = value;
|
|
||||||
this.statementI = statementI;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public final Statement value;
|
|
||||||
public final SwitchCase[] cases;
|
|
||||||
public final Statement[] body;
|
|
||||||
public final int defaultI;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void declare(ScopeRecord varsScope) {
|
|
||||||
for (var stm : body) stm.declare(varsScope);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
var caseToStatement = new HashMap<Integer, Integer>();
|
|
||||||
var statementToIndex = new HashMap<Integer, Integer>();
|
|
||||||
|
|
||||||
value.compile(target, scope, true, BreakpointType.STEP_OVER);
|
|
||||||
|
|
||||||
for (var ccase : cases) {
|
|
||||||
target.add(Instruction.dup(loc()));
|
|
||||||
ccase.value.compile(target, scope, true);
|
|
||||||
target.add(Instruction.operation(loc(), Operation.EQUALS));
|
|
||||||
caseToStatement.put(target.size(), ccase.statementI);
|
|
||||||
target.add(Instruction.nop(null));
|
|
||||||
}
|
|
||||||
|
|
||||||
int start = target.size();
|
|
||||||
|
|
||||||
target.add(Instruction.nop(null));
|
|
||||||
|
|
||||||
for (var stm : body) {
|
|
||||||
statementToIndex.put(statementToIndex.size(), target.size());
|
|
||||||
stm.compile(target, scope, false, BreakpointType.STEP_OVER);
|
|
||||||
}
|
|
||||||
|
|
||||||
int end = target.size();
|
|
||||||
target.add(Instruction.discard(loc()));
|
|
||||||
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
|
||||||
|
|
||||||
if (defaultI < 0 || defaultI >= body.length) target.set(start, Instruction.jmp(loc(), end - start));
|
|
||||||
else target.set(start, Instruction.jmp(loc(), statementToIndex.get(defaultI) - start));
|
|
||||||
|
|
||||||
for (int i = start; i < end; i++) {
|
|
||||||
var instr = target.get(i);
|
|
||||||
if (instr.type == Type.NOP && instr.is(0, "break") && instr.get(1) == null) {
|
|
||||||
target.set(i, Instruction.jmp(loc(), end - i).locate(instr.location));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (var el : caseToStatement.entrySet()) {
|
|
||||||
var i = statementToIndex.get(el.getValue());
|
|
||||||
if (i == null) i = end;
|
|
||||||
target.set(el.getKey(), Instruction.jmpIf(loc(), i - el.getKey()));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public SwitchStatement(Location loc, Statement value, int defaultI, SwitchCase[] cases, Statement[] body) {
|
|
||||||
super(loc);
|
|
||||||
this.value = value;
|
|
||||||
this.defaultI = defaultI;
|
|
||||||
this.cases = cases;
|
|
||||||
this.body = body;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.core.compilation.control;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
value.compile(target, scope, true);
|
|
||||||
target.add(Instruction.throwInstr(loc()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public ThrowStatement(Location loc, Statement value) {
|
|
||||||
super(loc);
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,61 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.core.compilation.control;
|
|
||||||
|
|
||||||
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;
|
|
||||||
public final Statement catchBody;
|
|
||||||
public final Statement finallyBody;
|
|
||||||
public final String name;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void declare(ScopeRecord globScope) {
|
|
||||||
tryBody.declare(globScope);
|
|
||||||
if (catchBody != null) catchBody.declare(globScope);
|
|
||||||
if (finallyBody != null) finallyBody.declare(globScope);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, BreakpointType bpt) {
|
|
||||||
target.add(Instruction.nop(null));
|
|
||||||
|
|
||||||
int start = target.size(), catchStart = -1, finallyStart = -1;
|
|
||||||
|
|
||||||
tryBody.compile(target, scope, false);
|
|
||||||
target.add(Instruction.tryEnd(loc()));
|
|
||||||
|
|
||||||
if (catchBody != null) {
|
|
||||||
catchStart = target.size() - start;
|
|
||||||
var local = scope instanceof GlobalScope ? scope.child() : (LocalScopeRecord)scope;
|
|
||||||
local.define(name, true);
|
|
||||||
catchBody.compile(target, scope, false);
|
|
||||||
local.undefine();
|
|
||||||
target.add(Instruction.tryEnd(loc()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (finallyBody != null) {
|
|
||||||
finallyStart = target.size() - start;
|
|
||||||
finallyBody.compile(target, scope, false);
|
|
||||||
target.add(Instruction.tryEnd(loc()));
|
|
||||||
}
|
|
||||||
|
|
||||||
target.set(start - 1, Instruction.tryStart(loc(), catchStart, finallyStart, target.size() - start));
|
|
||||||
target.setDebug(start - 1, BreakpointType.STEP_OVER);
|
|
||||||
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
|
||||||
}
|
|
||||||
|
|
||||||
public TryStatement(Location loc, Statement tryBody, Statement catchBody, Statement finallyBody, String name) {
|
|
||||||
super(loc);
|
|
||||||
this.tryBody = tryBody;
|
|
||||||
this.catchBody = catchBody;
|
|
||||||
this.finallyBody = finallyBody;
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,54 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.core.compilation.control;
|
|
||||||
|
|
||||||
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;
|
|
||||||
public final String label;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void declare(ScopeRecord globScope) {
|
|
||||||
body.declare(globScope);
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
int start = target.size();
|
|
||||||
condition.compile(target, scope, true);
|
|
||||||
int mid = target.size();
|
|
||||||
target.add(Instruction.nop(null));
|
|
||||||
body.compile(target, scope, false, BreakpointType.STEP_OVER);
|
|
||||||
|
|
||||||
int end = target.size();
|
|
||||||
|
|
||||||
replaceBreaks(target, label, mid + 1, end, start, end + 1);
|
|
||||||
|
|
||||||
target.add(Instruction.jmp(loc(), start - end));
|
|
||||||
target.set(mid, Instruction.jmpIfNot(loc(), end - mid + 1));
|
|
||||||
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
|
||||||
}
|
|
||||||
|
|
||||||
public WhileStatement(Location loc, String label, Statement condition, Statement body) {
|
|
||||||
super(loc);
|
|
||||||
this.label = label;
|
|
||||||
this.condition = condition;
|
|
||||||
this.body = body;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void replaceBreaks(CompileTarget target, String label, int start, int end, int continuePoint, int breakPoint) {
|
|
||||||
for (int i = start; i < end; i++) {
|
|
||||||
var instr = target.get(i);
|
|
||||||
if (instr.type == Type.NOP && instr.is(0, "cont") && (instr.get(1) == null || instr.is(1, label))) {
|
|
||||||
target.set(i, Instruction.jmp(instr.location, continuePoint - i).setDbgData(target.get(i)));
|
|
||||||
}
|
|
||||||
if (instr.type == Type.NOP && instr.is(0, "break") && (instr.get(1) == null || instr.is(1, label))) {
|
|
||||||
target.set(i, Instruction.jmp(instr.location, breakPoint - i).setDbgData(target.get(i)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.core.compilation.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;
|
|
||||||
|
|
||||||
public class ArrayStatement extends Statement {
|
|
||||||
public final Statement[] statements;
|
|
||||||
|
|
||||||
@Override public boolean pure() {
|
|
||||||
for (var stm : statements) {
|
|
||||||
if (!stm.pure()) return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
target.add(Instruction.loadArr(loc(), statements.length));
|
|
||||||
|
|
||||||
for (var i = 0; i < statements.length; i++) {
|
|
||||||
var el = statements[i];
|
|
||||||
if (el != null) {
|
|
||||||
target.add(Instruction.dup(loc()));
|
|
||||||
target.add(Instruction.loadValue(loc(), i));
|
|
||||||
el.compile(target, scope, true);
|
|
||||||
target.add(Instruction.storeMember(loc()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pollute) target.add(Instruction.discard(loc()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public ArrayStatement(Location loc, Statement[] statements) {
|
|
||||||
super(loc);
|
|
||||||
this.statements = statements;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,51 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.core.compilation.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.compilation.Instruction.BreakpointType;
|
|
||||||
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
|
|
||||||
|
|
||||||
public class CallStatement extends Statement {
|
|
||||||
public final Statement func;
|
|
||||||
public final Statement[] args;
|
|
||||||
public final boolean isNew;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, BreakpointType type) {
|
|
||||||
if (isNew) func.compile(target, scope, true);
|
|
||||||
else if (func instanceof IndexStatement) {
|
|
||||||
((IndexStatement)func).compile(target, scope, true, true);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
target.add(Instruction.loadValue(loc(), null));
|
|
||||||
func.compile(target, scope, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var arg : args) arg.compile(target, scope, true);
|
|
||||||
|
|
||||||
if (isNew) target.add(Instruction.callNew(loc(), args.length));
|
|
||||||
else target.add(Instruction.call(loc(), args.length));
|
|
||||||
target.setDebug(type);
|
|
||||||
|
|
||||||
if (!pollute) target.add(Instruction.discard(loc()));
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
compile(target, scope, pollute, BreakpointType.STEP_IN);
|
|
||||||
}
|
|
||||||
|
|
||||||
public CallStatement(Location loc, boolean isNew, Statement func, Statement ...args) {
|
|
||||||
super(loc);
|
|
||||||
this.isNew = isNew;
|
|
||||||
this.func = func;
|
|
||||||
this.args = args;
|
|
||||||
}
|
|
||||||
public CallStatement(Location loc, boolean isNew, Statement obj, Object key, Statement ...args) {
|
|
||||||
super(loc);
|
|
||||||
this.isNew = isNew;
|
|
||||||
this.func = new IndexStatement(loc, obj, new ConstantStatement(loc, key));
|
|
||||||
this.args = args;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.core.compilation.values;
|
|
||||||
|
|
||||||
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;
|
|
||||||
public final double addAmount;
|
|
||||||
public final boolean postfix;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
value.toAssign(new ConstantStatement(loc(), -addAmount), Operation.SUBTRACT).compile(target, scope, true);
|
|
||||||
if (!pollute) target.add(Instruction.discard(loc()));
|
|
||||||
else if (postfix) {
|
|
||||||
target.add(Instruction.loadValue(loc(), addAmount));
|
|
||||||
target.add(Instruction.operation(loc(), Operation.SUBTRACT));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ChangeStatement(Location loc, AssignableStatement value, double addAmount, boolean postfix) {
|
|
||||||
super(loc);
|
|
||||||
this.value = value;
|
|
||||||
this.addAmount = addAmount;
|
|
||||||
this.postfix = postfix;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.core.compilation.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;
|
|
||||||
|
|
||||||
public class ConstantStatement extends Statement {
|
|
||||||
public final Object value;
|
|
||||||
|
|
||||||
@Override public boolean pure() { return true; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
if (pollute) target.add(Instruction.loadValue(loc(), value));
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConstantStatement(Location loc, Object val) {
|
|
||||||
super(loc);
|
|
||||||
this.value = val;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.core.compilation.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;
|
|
||||||
|
|
||||||
public class DiscardStatement extends Statement {
|
|
||||||
public final Statement value;
|
|
||||||
|
|
||||||
@Override public boolean pure() { return value.pure(); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
value.compile(target, scope, false);
|
|
||||||
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
|
||||||
}
|
|
||||||
|
|
||||||
public DiscardStatement(Location loc, Statement val) {
|
|
||||||
super(loc);
|
|
||||||
this.value = val;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,139 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.core.compilation.values;
|
|
||||||
|
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
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;
|
|
||||||
public final String varName;
|
|
||||||
public final String[] args;
|
|
||||||
public final boolean statement;
|
|
||||||
public final Location end;
|
|
||||||
|
|
||||||
private static Random rand = new Random();
|
|
||||||
|
|
||||||
@Override public boolean pure() { return varName == null && statement; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void declare(ScopeRecord scope) {
|
|
||||||
if (varName != null && statement) scope.define(varName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void checkBreakAndCont(CompileTarget target, int start) {
|
|
||||||
for (int i = start; i < target.size(); i++) {
|
|
||||||
if (target.get(i).type == Type.NOP) {
|
|
||||||
if (target.get(i).is(0, "break") ) {
|
|
||||||
throw new SyntaxException(target.get(i).location, "Break was placed outside a loop.");
|
|
||||||
}
|
|
||||||
if (target.get(i).is(0, "cont")) {
|
|
||||||
throw new SyntaxException(target.get(i).location, "Continue was placed outside a loop.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private long compileBody(CompileTarget target, ScopeRecord scope, boolean polute, BreakpointType bp) {
|
|
||||||
for (var i = 0; i < args.length; i++) {
|
|
||||||
for (var j = 0; j < i; j++) {
|
|
||||||
if (args[i].equals(args[j])) {
|
|
||||||
throw new SyntaxException(loc(), "Duplicate parameter '" + args[i] + "'.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var id = rand.nextLong();
|
|
||||||
var subscope = scope.child();
|
|
||||||
var subtarget = new CompileTarget(target.functions, target.breakpoints);
|
|
||||||
|
|
||||||
subscope.define("this");
|
|
||||||
var argsVar = subscope.define("arguments");
|
|
||||||
|
|
||||||
if (args.length > 0) {
|
|
||||||
for (var i = 0; i < args.length; i++) {
|
|
||||||
subtarget.add(Instruction.loadVar(loc(), argsVar));
|
|
||||||
subtarget.add(Instruction.loadMember(loc(), i));
|
|
||||||
subtarget.add(Instruction.storeVar(loc(), subscope.define(args[i])));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!statement && this.varName != null) {
|
|
||||||
subtarget.add(Instruction.storeSelfFunc(loc(), (int)subscope.define(this.varName)));
|
|
||||||
subtarget.setDebug(bp);
|
|
||||||
}
|
|
||||||
|
|
||||||
body.declare(subscope);
|
|
||||||
body.compile(subtarget, subscope, false);
|
|
||||||
subtarget.add(Instruction.ret(end));
|
|
||||||
checkBreakAndCont(subtarget, 0);
|
|
||||||
|
|
||||||
if (polute) target.add(Instruction.loadFunc(loc(), id, subscope.getCaptures()));
|
|
||||||
target.functions.put(id, new FunctionBody(
|
|
||||||
subscope.localsCount(), args.length,
|
|
||||||
subtarget.array(), subscope.captures(), subscope.locals()
|
|
||||||
));
|
|
||||||
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, String name, BreakpointType bp) {
|
|
||||||
if (this.varName != null) name = this.varName;
|
|
||||||
|
|
||||||
var hasVar = this.varName != null && statement;
|
|
||||||
var hasName = name != null;
|
|
||||||
|
|
||||||
compileBody(target, scope, pollute || hasVar || hasName, bp);
|
|
||||||
|
|
||||||
if (hasName) {
|
|
||||||
if (pollute || hasVar) target.add(Instruction.dup(loc()));
|
|
||||||
target.add(Instruction.loadValue(loc(), "name"));
|
|
||||||
target.add(Instruction.loadValue(loc(), name));
|
|
||||||
target.add(Instruction.storeMember(loc()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasVar) {
|
|
||||||
var key = scope.getKey(this.varName);
|
|
||||||
|
|
||||||
if (key instanceof String) target.add(Instruction.makeVar(loc(), (String)key));
|
|
||||||
target.add(Instruction.storeVar(loc(), scope.getKey(this.varName), false));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, String name) {
|
|
||||||
compile(target, scope, pollute, name, BreakpointType.NONE);
|
|
||||||
}
|
|
||||||
@Override public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, BreakpointType bp) {
|
|
||||||
compile(target, scope, pollute, (String)null, bp);
|
|
||||||
}
|
|
||||||
@Override public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
compile(target, scope, pollute, (String)null, BreakpointType.NONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
public FunctionStatement(Location loc, Location end, String varName, String[] args, boolean statement, CompoundStatement body) {
|
|
||||||
super(loc);
|
|
||||||
|
|
||||||
this.end = end;
|
|
||||||
this.varName = varName;
|
|
||||||
this.statement = statement;
|
|
||||||
|
|
||||||
this.args = args;
|
|
||||||
this.body = body;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void compileWithName(Statement stm, CompileTarget target, ScopeRecord scope, boolean pollute, String name) {
|
|
||||||
if (stm instanceof FunctionStatement) ((FunctionStatement)stm).compile(target, scope, pollute, name);
|
|
||||||
else stm.compile(target, scope, pollute);
|
|
||||||
}
|
|
||||||
public static void compileWithName(Statement stm, CompileTarget target, ScopeRecord scope, boolean pollute, String name, BreakpointType bp) {
|
|
||||||
if (stm instanceof FunctionStatement) ((FunctionStatement)stm).compile(target, scope, pollute, name, bp);
|
|
||||||
else stm.compile(target, scope, pollute, bp);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.core.compilation.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;
|
|
||||||
|
|
||||||
public class GlobalThisStatement extends Statement {
|
|
||||||
@Override public boolean pure() { return true; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
if (pollute) target.add(Instruction.loadGlob(loc()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public GlobalThisStatement(Location loc) {
|
|
||||||
super(loc);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,48 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.core.compilation.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.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;
|
|
||||||
public final Statement index;
|
|
||||||
public final Statement value;
|
|
||||||
public final Operation operation;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
if (operation != null) {
|
|
||||||
object.compile(target, scope, true);
|
|
||||||
index.compile(target, scope, true);
|
|
||||||
target.add(Instruction.dup(loc(), 2));
|
|
||||||
|
|
||||||
target.add(Instruction.loadMember(loc()));
|
|
||||||
value.compile(target, scope, true);
|
|
||||||
target.add(Instruction.operation(loc(), operation));
|
|
||||||
|
|
||||||
target.add(Instruction.storeMember(loc(), pollute));
|
|
||||||
target.setDebug(BreakpointType.STEP_IN);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
object.compile(target, scope, true);
|
|
||||||
index.compile(target, scope, true);
|
|
||||||
value.compile(target, scope, true);
|
|
||||||
|
|
||||||
target.add(Instruction.storeMember(loc(), pollute));
|
|
||||||
target.setDebug(BreakpointType.STEP_IN);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public IndexAssignStatement(Location loc, Statement object, Statement index, Statement value, Operation operation) {
|
|
||||||
super(loc);
|
|
||||||
this.object = object;
|
|
||||||
this.index = index;
|
|
||||||
this.value = value;
|
|
||||||
this.operation = operation;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,49 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.core.compilation.values;
|
|
||||||
|
|
||||||
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;
|
|
||||||
public final Statement index;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Statement toAssign(Statement val, Operation operation) {
|
|
||||||
return new IndexAssignStatement(loc(), object, index, val, operation);
|
|
||||||
}
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean dupObj, boolean pollute) {
|
|
||||||
object.compile(target, scope, true);
|
|
||||||
if (dupObj) target.add(Instruction.dup(loc()));
|
|
||||||
if (index instanceof ConstantStatement) {
|
|
||||||
target.add(Instruction.loadMember(loc(), ((ConstantStatement)index).value));
|
|
||||||
target.setDebug(BreakpointType.STEP_IN);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
index.compile(target, scope, true);
|
|
||||||
target.add(Instruction.loadMember(loc()));
|
|
||||||
target.setDebug(BreakpointType.STEP_IN);
|
|
||||||
if (!pollute) target.add(Instruction.discard(loc()));
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
compile(target, scope, false, pollute);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IndexStatement(Location loc, Statement object, Statement index) {
|
|
||||||
super(loc);
|
|
||||||
this.object = object;
|
|
||||||
this.index = index;
|
|
||||||
}
|
|
||||||
public IndexStatement(Location loc, Statement object, Object index) {
|
|
||||||
super(loc);
|
|
||||||
this.object = object;
|
|
||||||
this.index = new ConstantStatement(loc, index);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.core.compilation.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;
|
|
||||||
|
|
||||||
@Override public boolean pure() { return first.pure() && second.pure(); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
if (first instanceof ConstantStatement) {
|
|
||||||
if (Values.not(((ConstantStatement)first).value)) {
|
|
||||||
first.compile(target, scope, pollute);
|
|
||||||
}
|
|
||||||
else second.compile(target, scope, pollute);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
first.compile(target, scope, true);
|
|
||||||
if (pollute) target.add(Instruction.dup(loc()));
|
|
||||||
int start = target.size();
|
|
||||||
target.add(Instruction.nop(null));
|
|
||||||
if (pollute) target.add(Instruction.discard(loc()));
|
|
||||||
second.compile(target, scope, pollute);
|
|
||||||
target.set(start, Instruction.jmpIfNot(loc(), target.size() - start));
|
|
||||||
}
|
|
||||||
|
|
||||||
public LazyAndStatement(Location loc, Statement first, Statement second) {
|
|
||||||
super(loc);
|
|
||||||
this.first = first;
|
|
||||||
this.second = second;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.core.compilation.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;
|
|
||||||
|
|
||||||
@Override public boolean pure() { return first.pure() && second.pure(); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
if (first instanceof ConstantStatement) {
|
|
||||||
if (Values.not(((ConstantStatement)first).value)) {
|
|
||||||
second.compile(target, scope, pollute);
|
|
||||||
}
|
|
||||||
else first.compile(target, scope, pollute);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
first.compile(target, scope, true);
|
|
||||||
if (pollute) target.add(Instruction.dup(loc()));
|
|
||||||
int start = target.size();
|
|
||||||
target.add(Instruction.nop(null));
|
|
||||||
if (pollute) target.add(Instruction.discard(loc()));
|
|
||||||
second.compile(target, scope, pollute);
|
|
||||||
target.set(start, Instruction.jmpIf(loc(), target.size() - start));
|
|
||||||
}
|
|
||||||
|
|
||||||
public LazyOrStatement(Location loc, Statement first, Statement second) {
|
|
||||||
super(loc);
|
|
||||||
this.first = first;
|
|
||||||
this.second = second;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,63 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.core.compilation.values;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
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;
|
|
||||||
public final Map<Object, FunctionStatement> getters;
|
|
||||||
public final Map<Object, FunctionStatement> setters;
|
|
||||||
|
|
||||||
@Override public boolean pure() {
|
|
||||||
for (var el : map.values()) {
|
|
||||||
if (!el.pure()) return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
target.add(Instruction.loadObj(loc()));
|
|
||||||
|
|
||||||
for (var el : map.entrySet()) {
|
|
||||||
target.add(Instruction.dup(loc()));
|
|
||||||
target.add(Instruction.loadValue(loc(), el.getKey()));
|
|
||||||
var val = el.getValue();
|
|
||||||
FunctionStatement.compileWithName(val, target, scope, true, el.getKey().toString());
|
|
||||||
target.add(Instruction.storeMember(loc()));
|
|
||||||
}
|
|
||||||
|
|
||||||
var keys = new ArrayList<Object>();
|
|
||||||
keys.addAll(getters.keySet());
|
|
||||||
keys.addAll(setters.keySet());
|
|
||||||
|
|
||||||
for (var key : keys) {
|
|
||||||
if (key instanceof String) target.add(Instruction.loadValue(loc(), (String)key));
|
|
||||||
else target.add(Instruction.loadValue(loc(), (Double)key));
|
|
||||||
|
|
||||||
if (getters.containsKey(key)) getters.get(key).compile(target, scope, true);
|
|
||||||
else target.add(Instruction.loadValue(loc(), null));
|
|
||||||
|
|
||||||
if (setters.containsKey(key)) setters.get(key).compile(target, scope, true);
|
|
||||||
else target.add(Instruction.loadValue(loc(), null));
|
|
||||||
|
|
||||||
target.add(Instruction.defProp(loc()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pollute) target.add(Instruction.discard(loc()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public ObjectStatement(Location loc, Map<Object, Statement> map, Map<Object, FunctionStatement> getters, Map<Object, FunctionStatement> setters) {
|
|
||||||
super(loc);
|
|
||||||
this.map = map;
|
|
||||||
this.getters = getters;
|
|
||||||
this.setters = setters;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.core.compilation.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.Operation;
|
|
||||||
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
|
|
||||||
|
|
||||||
public class OperationStatement extends Statement {
|
|
||||||
public final Statement[] args;
|
|
||||||
public final Operation operation;
|
|
||||||
|
|
||||||
@Override public boolean pure() {
|
|
||||||
for (var el : args) {
|
|
||||||
if (!el.pure()) return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
for (var arg : args) {
|
|
||||||
arg.compile(target, scope, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pollute) target.add(Instruction.operation(loc(), operation));
|
|
||||||
else target.add(Instruction.discard(loc()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public OperationStatement(Location loc, Operation operation, Statement ...args) {
|
|
||||||
super(loc);
|
|
||||||
this.operation = operation;
|
|
||||||
this.args = args;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.core.compilation.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;
|
|
||||||
|
|
||||||
public class RegexStatement extends Statement {
|
|
||||||
public final String pattern, flags;
|
|
||||||
|
|
||||||
// Not really pure, since a function is called, but can be ignored.
|
|
||||||
@Override public boolean pure() { return true; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
target.add(Instruction.loadRegex(loc(), pattern, flags));
|
|
||||||
if (!pollute) target.add(Instruction.discard(loc()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public RegexStatement(Location loc, String pattern, String flags) {
|
|
||||||
super(loc);
|
|
||||||
this.pattern = pattern;
|
|
||||||
this.flags = flags;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,38 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.core.compilation.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.Operation;
|
|
||||||
import me.topchetoeu.jscript.core.engine.scope.ScopeRecord;
|
|
||||||
|
|
||||||
public class VariableAssignStatement extends Statement {
|
|
||||||
public final String name;
|
|
||||||
public final Statement value;
|
|
||||||
public final Operation operation;
|
|
||||||
|
|
||||||
@Override public boolean pure() { return false; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
var i = scope.getKey(name);
|
|
||||||
if (operation != null) {
|
|
||||||
target.add(Instruction.loadVar(loc(), i));
|
|
||||||
FunctionStatement.compileWithName(value, target, scope, true, name);
|
|
||||||
target.add(Instruction.operation(loc(), operation));
|
|
||||||
target.add(Instruction.storeVar(loc(), i, pollute));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
FunctionStatement.compileWithName(value, target, scope, true, name);
|
|
||||||
target.add(Instruction.storeVar(loc(), i, pollute));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public VariableAssignStatement(Location loc, String name, Statement val, Operation operation) {
|
|
||||||
super(loc);
|
|
||||||
this.name = name;
|
|
||||||
this.value = val;
|
|
||||||
this.operation = operation;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.core.compilation.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;
|
|
||||||
|
|
||||||
public class VariableIndexStatement extends Statement {
|
|
||||||
public final int index;
|
|
||||||
|
|
||||||
@Override public boolean pure() { return true; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
if (pollute) target.add(Instruction.loadVar(loc(), index));
|
|
||||||
}
|
|
||||||
|
|
||||||
public VariableIndexStatement(Location loc, int i) {
|
|
||||||
super(loc);
|
|
||||||
this.index = i;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.core.compilation.values;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
@Override public boolean pure() { return false; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Statement toAssign(Statement val, Operation operation) {
|
|
||||||
return new VariableAssignStatement(loc(), name, val, operation);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
|
||||||
var i = scope.getKey(name);
|
|
||||||
target.add(Instruction.loadVar(loc(), i));
|
|
||||||
if (!pollute) target.add(Instruction.discard(loc()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public VariableStatement(Location loc, String name) {
|
|
||||||
super(loc);
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
}
|
|
129
src/java/me/topchetoeu/jscript/core/debug/DebugContext.java
Normal file
129
src/java/me/topchetoeu/jscript/core/debug/DebugContext.java
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
package me.topchetoeu.jscript.core.debug;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.WeakHashMap;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Filename;
|
||||||
|
import me.topchetoeu.jscript.common.Location;
|
||||||
|
import me.topchetoeu.jscript.compilation.FunctionBody;
|
||||||
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
|
import me.topchetoeu.jscript.compilation.mapping.FunctionMap;
|
||||||
|
import me.topchetoeu.jscript.core.Context;
|
||||||
|
import me.topchetoeu.jscript.core.Extensions;
|
||||||
|
import me.topchetoeu.jscript.core.Frame;
|
||||||
|
import me.topchetoeu.jscript.core.values.CodeFunction;
|
||||||
|
import me.topchetoeu.jscript.core.values.FunctionValue;
|
||||||
|
import me.topchetoeu.jscript.core.values.Symbol;
|
||||||
|
import me.topchetoeu.jscript.core.exceptions.EngineException;
|
||||||
|
|
||||||
|
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 WeakHashMap<FunctionBody, FunctionMap> 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());
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FunctionMap getMap(FunctionBody func) {
|
||||||
|
if (maps == null) return null;
|
||||||
|
return maps.get(func);
|
||||||
|
}
|
||||||
|
public FunctionMap getMap(FunctionValue func) {
|
||||||
|
if (maps == null || !(func instanceof CodeFunction)) return null;
|
||||||
|
return getMap(((CodeFunction)func).body);
|
||||||
|
}
|
||||||
|
public FunctionMap getMapOrEmpty(FunctionBody func) {
|
||||||
|
if (maps == null) return FunctionMap.EMPTY;
|
||||||
|
var res = maps.get(func);
|
||||||
|
if (res == null) return FunctionMap.EMPTY;
|
||||||
|
else return res;
|
||||||
|
}
|
||||||
|
public FunctionMap getMapOrEmpty(FunctionValue func) {
|
||||||
|
if (maps == null || !(func instanceof CodeFunction)) return null;
|
||||||
|
return getMapOrEmpty(((CodeFunction)func).body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void onFramePop(Context ctx, Frame frame) {
|
||||||
|
if (debugger != null) debugger.onFramePop(ctx, frame);
|
||||||
|
}
|
||||||
|
@Override public void onFramePush(Context ctx, Frame frame) {
|
||||||
|
if (debugger != null) debugger.onFramePush(ctx, frame);
|
||||||
|
}
|
||||||
|
@Override public boolean onInstruction(Context ctx, Frame 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) {
|
||||||
|
if (debugger != null) debugger.onSource(filename, source);
|
||||||
|
if (sources != null) sources.put(filename, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DebugContext(boolean enabled) {
|
||||||
|
if (enabled) {
|
||||||
|
sources = new HashMap<>();
|
||||||
|
maps = new WeakHashMap<>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<String> stackTrace(Context ctx) {
|
||||||
|
var res = new ArrayList<String>();
|
||||||
|
var dbgCtx = get(ctx);
|
||||||
|
|
||||||
|
for (var el : ctx.frames()) {
|
||||||
|
var name = el.function.name;
|
||||||
|
|
||||||
|
var map = dbgCtx.getMap(el.function);
|
||||||
|
Location loc = null;
|
||||||
|
|
||||||
|
if (map != null) {
|
||||||
|
loc = map.toLocation(el.codePtr, true);
|
||||||
|
if (loc == null) loc = map.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user