feat: merge all operation instructions in one

This commit is contained in:
TopchetoEU 2023-08-25 11:08:57 +03:00
parent 79a93ef971
commit b8b000ddf0
No known key found for this signature in database
GPG Key ID: 24E57B2E9C61AD19
25 changed files with 365 additions and 435 deletions

View File

@ -14,6 +14,7 @@ import me.topchetoeu.jscript.events.Observer;
import me.topchetoeu.jscript.exceptions.EngineException; import me.topchetoeu.jscript.exceptions.EngineException;
import me.topchetoeu.jscript.exceptions.SyntaxException; import me.topchetoeu.jscript.exceptions.SyntaxException;
import me.topchetoeu.jscript.polyfills.PolyfillEngine; import me.topchetoeu.jscript.polyfills.PolyfillEngine;
import me.topchetoeu.jscript.polyfills.TypescriptEngine;
public class Main { public class Main {
static Thread task; static Thread task;

View File

@ -0,0 +1,19 @@
package me.topchetoeu.jscript.compilation;
import java.util.List;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
public abstract class AssignStatement extends Statement {
public abstract void compile(List<Instruction> target, ScopeRecord scope, boolean retPrevValue);
@Override
public void compile(List<Instruction> target, ScopeRecord scope) {
compile(target, scope, false);
}
protected AssignStatement(Location loc) {
super(loc);
}
}

View File

@ -1,10 +1,10 @@
package me.topchetoeu.jscript.compilation; package me.topchetoeu.jscript.compilation;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.Instruction.Type; import me.topchetoeu.jscript.engine.Operation;
public abstract class AssignableStatement extends Statement { public abstract class AssignableStatement extends Statement {
public abstract Statement toAssign(Statement val, Type operation); public abstract AssignStatement toAssign(Statement val, Operation operation);
protected AssignableStatement(Location loc) { protected AssignableStatement(Location loc) {
super(loc); super(loc);

View File

@ -1,6 +1,7 @@
package me.topchetoeu.jscript.compilation; package me.topchetoeu.jscript.compilation;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.engine.Operation;
import me.topchetoeu.jscript.exceptions.SyntaxException; import me.topchetoeu.jscript.exceptions.SyntaxException;
public class Instruction { public class Instruction {
@ -33,55 +34,58 @@ public class Instruction {
LOAD_REGEX, LOAD_REGEX,
DUP, DUP,
MOVE,
STORE_VAR, STORE_VAR,
STORE_MEMBER, STORE_MEMBER,
DISCARD, DISCARD,
MAKE_VAR, MAKE_VAR,
DEF_PROP, DEF_PROP,
KEYS, KEYS,
TYPEOF, TYPEOF,
INSTANCEOF(true), OPERATION;
IN(true), // TYPEOF,
// INSTANCEOF(true),
// IN(true),
MULTIPLY(true), // MULTIPLY(true),
DIVIDE(true), // DIVIDE(true),
MODULO(true), // MODULO(true),
ADD(true), // ADD(true),
SUBTRACT(true), // SUBTRACT(true),
USHIFT_RIGHT(true), // USHIFT_RIGHT(true),
SHIFT_RIGHT(true), // SHIFT_RIGHT(true),
SHIFT_LEFT(true), // SHIFT_LEFT(true),
GREATER(true), // GREATER(true),
LESS(true), // LESS(true),
GREATER_EQUALS(true), // GREATER_EQUALS(true),
LESS_EQUALS(true), // LESS_EQUALS(true),
LOOSE_EQUALS(true), // LOOSE_EQUALS(true),
LOOSE_NOT_EQUALS(true), // LOOSE_NOT_EQUALS(true),
EQUALS(true), // EQUALS(true),
NOT_EQUALS(true), // NOT_EQUALS(true),
AND(true), // AND(true),
OR(true), // OR(true),
XOR(true), // XOR(true),
NEG(true), // NEG(true),
POS(true), // POS(true),
NOT(true), // NOT(true),
INVERSE(true); // INVERSE(true);
final boolean isOperation; // final boolean isOperation;
private Type(boolean isOperation) { // private Type(boolean isOperation) {
this.isOperation = isOperation; // this.isOperation = isOperation;
} // }
private Type() { // private Type() {
this(false); // this(false);
} // }
} }
public final Type type; public final Type type;
@ -103,6 +107,11 @@ public class Instruction {
if (i >= params.length || i < 0) return null; if (i >= params.length || i < 0) return null;
return (T)params[i]; 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) { public boolean match(Object ...args) {
if (args.length != params.length) return false; if (args.length != params.length) return false;
for (int i = 0; i < args.length; i++) { for (int i = 0; i < args.length; i++) {
@ -226,11 +235,14 @@ public class Instruction {
public static Instruction loadArr(int count) { public static Instruction loadArr(int count) {
return new Instruction(null, Type.LOAD_ARR, count); return new Instruction(null, Type.LOAD_ARR, count);
} }
public static Instruction dup(int count) { public static Instruction dup() {
return new Instruction(null, Type.DUP, count, 0); return new Instruction(null, Type.DUP, 0, 1);
} }
public static Instruction dup(int count, int offset) { public static Instruction dup(int count, int offset) {
return new Instruction(null, Type.DUP, count, offset); return new Instruction(null, Type.DUP, offset, count);
}
public static Instruction move(int count, int offset) {
return new Instruction(null, Type.MOVE, offset, count);
} }
public static Instruction storeSelfFunc(int i) { public static Instruction storeSelfFunc(int i) {
@ -255,7 +267,7 @@ public class Instruction {
public static Instruction typeof() { public static Instruction typeof() {
return new Instruction(null, Type.TYPEOF); return new Instruction(null, Type.TYPEOF);
} }
public static Instruction typeof(String varName) { public static Instruction typeof(Object varName) {
return new Instruction(null, Type.TYPEOF, varName); return new Instruction(null, Type.TYPEOF, varName);
} }
@ -267,9 +279,8 @@ public class Instruction {
return new Instruction(null, Type.DEF_PROP); return new Instruction(null, Type.DEF_PROP);
} }
public static Instruction operation(Type op) { public static Instruction operation(Operation op) {
if (!op.isOperation) throw new IllegalArgumentException("The instruction type %s is not an operation.".formatted(op)); return new Instruction(null, Type.OPERATION, op);
return new Instruction(null, op);
} }
@Override @Override

View File

@ -5,7 +5,7 @@ import java.util.List;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.Instruction.Type; import me.topchetoeu.jscript.engine.Operation;
import me.topchetoeu.jscript.engine.scope.ScopeRecord; import me.topchetoeu.jscript.engine.scope.ScopeRecord;
public class ForInStatement extends Statement { public class ForInStatement extends Statement {
@ -37,17 +37,18 @@ public class ForInStatement extends Statement {
target.add(Instruction.keys()); target.add(Instruction.keys());
int start = target.size(); int start = target.size();
target.add(Instruction.dup(1)); target.add(Instruction.dup());
target.add(Instruction.loadMember("length")); target.add(Instruction.loadMember("length"));
target.add(Instruction.loadValue(0)); target.add(Instruction.loadValue(0));
target.add(Instruction.operation(Type.LESS_EQUALS)); target.add(Instruction.operation(Operation.LESS_EQUALS));
int mid = target.size(); int mid = target.size();
target.add(Instruction.nop()); target.add(Instruction.nop());
target.add(Instruction.dup(2)); target.add(Instruction.dup());
target.add(Instruction.dup());
target.add(Instruction.loadMember("length")); target.add(Instruction.loadMember("length"));
target.add(Instruction.loadValue(1)); target.add(Instruction.loadValue(1));
target.add(Instruction.operation(Type.SUBTRACT)); target.add(Instruction.operation(Operation.SUBTRACT));
target.add(Instruction.dup(1, 2)); target.add(Instruction.dup(1, 2));
target.add(Instruction.loadValue("length")); target.add(Instruction.loadValue("length"));
target.add(Instruction.dup(1, 2)); target.add(Instruction.dup(1, 2));

View File

@ -7,6 +7,7 @@ import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.Instruction.Type; import me.topchetoeu.jscript.compilation.Instruction.Type;
import me.topchetoeu.jscript.engine.Operation;
import me.topchetoeu.jscript.engine.scope.ScopeRecord; import me.topchetoeu.jscript.engine.scope.ScopeRecord;
public class SwitchStatement extends Statement { public class SwitchStatement extends Statement {
@ -33,9 +34,9 @@ public class SwitchStatement extends Statement {
value.compile(target, scope); value.compile(target, scope);
for (var ccase : cases) { for (var ccase : cases) {
target.add(Instruction.dup(1).locate(loc())); target.add(Instruction.dup().locate(loc()));
ccase.value.compileWithPollution(target, scope); ccase.value.compileWithPollution(target, scope);
target.add(Instruction.operation(Type.EQUALS).locate(loc())); target.add(Instruction.operation(Operation.EQUALS).locate(loc()));
caseMap.put(target.size(), ccase.statementI); caseMap.put(target.size(), ccase.statementI);
target.add(Instruction.nop()); target.add(Instruction.nop());
} }

View File

@ -21,7 +21,7 @@ public class ArrayStatement extends Statement {
var i = 0; var i = 0;
for (var el : statements) { for (var el : statements) {
if (el != null) { if (el != null) {
target.add(Instruction.dup(1).locate(loc())); target.add(Instruction.dup().locate(loc()));
target.add(Instruction.loadValue(i).locate(loc())); target.add(Instruction.loadValue(i).locate(loc()));
el.compileWithPollution(target, scope); el.compileWithPollution(target, scope);
target.add(Instruction.storeMember().locate(loc())); target.add(Instruction.storeMember().locate(loc()));

View File

@ -6,7 +6,7 @@ import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.AssignableStatement; import me.topchetoeu.jscript.compilation.AssignableStatement;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.Instruction.Type; import me.topchetoeu.jscript.engine.Operation;
import me.topchetoeu.jscript.engine.scope.ScopeRecord; import me.topchetoeu.jscript.engine.scope.ScopeRecord;
public class ChangeStatement extends Statement { public class ChangeStatement extends Statement {
@ -19,11 +19,7 @@ public class ChangeStatement extends Statement {
@Override @Override
public void compile(List<Instruction> target, ScopeRecord scope) { public void compile(List<Instruction> target, ScopeRecord scope) {
value.toAssign(new ConstantStatement(loc(), -addAmount), Type.SUBTRACT).compileWithPollution(target, scope); value.toAssign(new ConstantStatement(loc(), -addAmount), Operation.SUBTRACT).compile(target, scope, postfix);
if (postfix) {
target.add(Instruction.loadValue(addAmount).locate(loc()));
target.add(Instruction.operation(Type.SUBTRACT).locate(loc()));
}
} }
public ChangeStatement(Location loc, AssignableStatement value, double addAmount, boolean postfix) { public ChangeStatement(Location loc, AssignableStatement value, double addAmount, boolean postfix) {

View File

@ -53,13 +53,11 @@ public class FunctionStatement extends Statement {
target.add(Instruction.nop()); target.add(Instruction.nop());
subscope.define("this"); subscope.define("this");
var argsVar = subscope.define("arguments"); var argsVar = subscope.define("arguments");
if (args.length > 0) {
target.add(Instruction.loadVar(argsVar).locate(loc()));
if (args.length != 1) target.add(Instruction.dup(args.length - 1).locate(loc()));
if (args.length > 0) {
for (var i = 0; i < args.length; i++) { for (var i = 0; i < args.length; i++) {
target.add(Instruction.loadVar(argsVar).locate(loc()));
target.add(Instruction.loadMember(i).locate(loc())); target.add(Instruction.loadMember(i).locate(loc()));
target.add(Instruction.storeVar(subscope.define(args[i])).locate(loc())); target.add(Instruction.storeVar(subscope.define(args[i])).locate(loc()));
} }
@ -82,7 +80,7 @@ public class FunctionStatement extends Statement {
if (name == null) name = this.name; if (name == null) name = this.name;
if (name != null) { if (name != null) {
target.add(Instruction.dup(1).locate(loc())); target.add(Instruction.dup().locate(loc()));
target.add(Instruction.loadValue("name").locate(loc())); target.add(Instruction.loadValue("name").locate(loc()));
target.add(Instruction.loadValue(name).locate(loc())); target.add(Instruction.loadValue(name).locate(loc()));
target.add(Instruction.storeMember().locate(loc())); target.add(Instruction.storeMember().locate(loc()));

View File

@ -3,45 +3,52 @@ package me.topchetoeu.jscript.compilation.values;
import java.util.List; import java.util.List;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.AssignStatement;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.Instruction.Type; import me.topchetoeu.jscript.engine.Operation;
import me.topchetoeu.jscript.engine.scope.ScopeRecord; import me.topchetoeu.jscript.engine.scope.ScopeRecord;
public class IndexAssignStatement extends Statement { public class IndexAssignStatement extends AssignStatement {
public final Statement object; public final Statement object;
public final Statement index; public final Statement index;
public final Statement value; public final Statement value;
public final Type operation; public final Operation operation;
@Override @Override
public boolean pollutesStack() { return true; } public boolean pollutesStack() { return true; }
@Override @Override
public void compile(List<Instruction> target, ScopeRecord scope) { public void compile(List<Instruction> target, ScopeRecord scope, boolean retPrevValue) {
int start = 0; int start = 0;
if (operation != null) { if (operation != null) {
object.compileWithPollution(target, scope); object.compileWithPollution(target, scope);
index.compileWithPollution(target, scope); index.compileWithPollution(target, scope);
target.add(Instruction.dup(2, 0).locate(loc()));
target.add(Instruction.dup(1, 1).locate(loc()));
target.add(Instruction.dup(1, 1).locate(loc()));
target.add(Instruction.loadMember().locate(loc())); target.add(Instruction.loadMember().locate(loc()));
if (retPrevValue) {
target.add(Instruction.dup().locate(loc()));
target.add(Instruction.move(3, 1).locate(loc()));
}
value.compileWithPollution(target, scope); value.compileWithPollution(target, scope);
target.add(Instruction.operation(operation).locate(loc())); target.add(Instruction.operation(operation).locate(loc()));
target.add(Instruction.storeMember(true).locate(loc())); target.add(Instruction.storeMember(!retPrevValue).locate(loc()).setDebug(true));
} }
else { else {
object.compileWithPollution(target, scope); object.compileWithPollution(target, scope);
if (retPrevValue) target.add(Instruction.dup().locate(loc()));
index.compileWithPollution(target, scope); index.compileWithPollution(target, scope);
value.compileWithPollution(target, scope); value.compileWithPollution(target, scope);
target.add(Instruction.storeMember(true).locate(loc()));
target.add(Instruction.storeMember(!retPrevValue).locate(loc()).setDebug(true));
} }
target.get(start).setDebug(true); target.get(start);
} }
public IndexAssignStatement(Location loc, Statement object, Statement index, Statement value, Type operation) { public IndexAssignStatement(Location loc, Statement object, Statement index, Statement value, Operation operation) {
super(loc); super(loc);
this.object = object; this.object = object;
this.index = index; this.index = index;

View File

@ -3,10 +3,11 @@ package me.topchetoeu.jscript.compilation.values;
import java.util.List; import java.util.List;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.AssignStatement;
import me.topchetoeu.jscript.compilation.AssignableStatement; import me.topchetoeu.jscript.compilation.AssignableStatement;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.Instruction.Type; import me.topchetoeu.jscript.engine.Operation;
import me.topchetoeu.jscript.engine.scope.ScopeRecord; import me.topchetoeu.jscript.engine.scope.ScopeRecord;
public class IndexStatement extends AssignableStatement { public class IndexStatement extends AssignableStatement {
@ -19,13 +20,13 @@ public class IndexStatement extends AssignableStatement {
public boolean pure() { return true; } public boolean pure() { return true; }
@Override @Override
public Statement toAssign(Statement val, Type operation) { public AssignStatement toAssign(Statement val, Operation operation) {
return new IndexAssignStatement(loc(), object, index, val, operation); return new IndexAssignStatement(loc(), object, index, val, operation);
} }
public void compile(List<Instruction> target, ScopeRecord scope, boolean dupObj) { public void compile(List<Instruction> target, ScopeRecord scope, boolean dupObj) {
int start = 0; int start = 0;
object.compileWithPollution(target, scope); object.compileWithPollution(target, scope);
if (dupObj) target.add(Instruction.dup(1).locate(loc())); if (dupObj) target.add(Instruction.dup().locate(loc()));
if (index instanceof ConstantStatement) { if (index instanceof ConstantStatement) {
target.add(Instruction.loadMember(((ConstantStatement)index).value).locate(loc())); target.add(Instruction.loadMember(((ConstantStatement)index).value).locate(loc()));
return; return;

View File

@ -29,7 +29,7 @@ public class LazyAndStatement extends Statement {
} }
first.compileWithPollution(target, scope); first.compileWithPollution(target, scope);
target.add(Instruction.dup(1).locate(loc())); target.add(Instruction.dup().locate(loc()));
int start = target.size(); int start = target.size();
target.add(Instruction.nop()); target.add(Instruction.nop());
target.add(Instruction.discard().locate(loc())); target.add(Instruction.discard().locate(loc()));

View File

@ -29,7 +29,7 @@ public class LazyOrStatement extends Statement {
} }
first.compileWithPollution(target, scope); first.compileWithPollution(target, scope);
target.add(Instruction.dup(1).locate(loc())); target.add(Instruction.dup().locate(loc()));
int start = target.size(); int start = target.size();
target.add(Instruction.nop()); target.add(Instruction.nop());
target.add(Instruction.discard().locate(loc())); target.add(Instruction.discard().locate(loc()));

View File

@ -20,9 +20,9 @@ public class ObjectStatement extends Statement {
@Override @Override
public void compile(List<Instruction> target, ScopeRecord scope) { public void compile(List<Instruction> target, ScopeRecord scope) {
target.add(Instruction.loadObj().locate(loc())); target.add(Instruction.loadObj().locate(loc()));
if (!map.isEmpty()) target.add(Instruction.dup(map.size()).locate(loc()));
for (var el : map.entrySet()) { for (var el : map.entrySet()) {
target.add(Instruction.dup().locate(loc()));
target.add(Instruction.loadValue(el.getKey()).locate(loc())); target.add(Instruction.loadValue(el.getKey()).locate(loc()));
var val = el.getValue(); var val = el.getValue();
if (val instanceof FunctionStatement) ((FunctionStatement)val).compile(target, scope, el.getKey().toString(), false); if (val instanceof FunctionStatement) ((FunctionStatement)val).compile(target, scope, el.getKey().toString(), false);

View File

@ -7,13 +7,14 @@ import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.control.ThrowStatement; import me.topchetoeu.jscript.compilation.control.ThrowStatement;
import me.topchetoeu.jscript.engine.CallContext; import me.topchetoeu.jscript.engine.CallContext;
import me.topchetoeu.jscript.engine.Operation;
import me.topchetoeu.jscript.engine.scope.ScopeRecord; import me.topchetoeu.jscript.engine.scope.ScopeRecord;
import me.topchetoeu.jscript.engine.values.Values; import me.topchetoeu.jscript.engine.values.Values;
import me.topchetoeu.jscript.exceptions.EngineException; import me.topchetoeu.jscript.exceptions.EngineException;
public class OperationStatement extends Statement { public class OperationStatement extends Statement {
public final Statement[] args; public final Statement[] args;
public final Instruction.Type operation; public final Operation operation;
@Override @Override
public void compile(List<Instruction> target, ScopeRecord scope) { public void compile(List<Instruction> target, ScopeRecord scope) {
@ -96,7 +97,7 @@ public class OperationStatement extends Statement {
} }
public OperationStatement(Location loc, Instruction.Type operation, Statement... args) { public OperationStatement(Location loc, Operation operation, Statement... args) {
super(loc); super(loc);
this.operation = operation; this.operation = operation;
this.args = args; this.args = args;

View File

@ -6,8 +6,7 @@ import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.engine.scope.ScopeRecord; import me.topchetoeu.jscript.engine.scope.ScopeRecord;
import me.topchetoeu.jscript.engine.values.FunctionValue; import me.topchetoeu.jscript.engine.values.Values;
import me.topchetoeu.jscript.engine.values.Symbol;
public class TypeofStatement extends Statement { public class TypeofStatement extends Statement {
public final Statement value; public final Statement value;
@ -22,7 +21,7 @@ public class TypeofStatement extends Statement {
if (value instanceof VariableStatement) { if (value instanceof VariableStatement) {
var i = scope.getKey(((VariableStatement)value).name); var i = scope.getKey(((VariableStatement)value).name);
if (i instanceof String) { if (i instanceof String) {
target.add(Instruction.typeof((String)i)); target.add(Instruction.typeof((String)i).locate(loc()));
return; return;
} }
} }
@ -35,15 +34,14 @@ public class TypeofStatement extends Statement {
var val = value.optimize(); var val = value.optimize();
if (val instanceof ConstantStatement) { if (val instanceof ConstantStatement) {
var cnst = (ConstantStatement)val; return new ConstantStatement(loc(), Values.type(((ConstantStatement)val).value));
if (cnst.value == null) return new ConstantStatement(loc(), "undefined");
if (cnst.value instanceof Number) return new ConstantStatement(loc(), "number");
if (cnst.value instanceof Boolean) return new ConstantStatement(loc(), "boolean");
if (cnst.value instanceof String) return new ConstantStatement(loc(), "string");
if (cnst.value instanceof Symbol) return new ConstantStatement(loc(), "symbol");
if (cnst.value instanceof FunctionValue) return new ConstantStatement(loc(), "function");
return new ConstantStatement(loc(), "object");
} }
else if (
val instanceof ObjectStatement ||
val instanceof ArrayStatement ||
val instanceof GlobalThisStatement
) return new ConstantStatement(loc(), "object");
else if(val instanceof FunctionStatement) return new ConstantStatement(loc(), "function");
return new TypeofStatement(loc(), val); return new TypeofStatement(loc(), val);
} }

View File

@ -4,36 +4,39 @@ import java.util.List;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.AssignStatement;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Instruction.Type; import me.topchetoeu.jscript.engine.Operation;
import me.topchetoeu.jscript.engine.scope.ScopeRecord; import me.topchetoeu.jscript.engine.scope.ScopeRecord;
public class VariableAssignStatement extends Statement { public class VariableAssignStatement extends AssignStatement {
public final String name; public final String name;
public final Statement value; public final Statement value;
public final Type operation; public final Operation operation;
@Override @Override
public boolean pollutesStack() { return true; } public boolean pollutesStack() { return true; }
@Override @Override
public void compile(List<Instruction> target, ScopeRecord scope) { public void compile(List<Instruction> target, ScopeRecord scope, boolean retPrevValue) {
var i = scope.getKey(name); var i = scope.getKey(name);
if (operation != null) { if (operation != null) {
target.add(Instruction.loadVar(i).locate(loc())); target.add(Instruction.loadVar(i).locate(loc()));
if (retPrevValue) target.add(Instruction.dup().locate(loc()));
if (value instanceof FunctionStatement) ((FunctionStatement)value).compile(target, scope, name, false); if (value instanceof FunctionStatement) ((FunctionStatement)value).compile(target, scope, name, false);
else value.compileWithPollution(target, scope); else value.compileWithPollution(target, scope);
target.add(Instruction.operation(operation).locate(loc())); target.add(Instruction.operation(operation).locate(loc()));
target.add(Instruction.storeVar(i, !retPrevValue).locate(loc()));
} }
else { else {
if (retPrevValue) target.add(Instruction.loadVar(i).locate(loc()));
if (value instanceof FunctionStatement) ((FunctionStatement)value).compile(target, scope, name, false); if (value instanceof FunctionStatement) ((FunctionStatement)value).compile(target, scope, name, false);
else value.compileWithPollution(target, scope); else value.compileWithPollution(target, scope);
target.add(Instruction.storeVar(i, !retPrevValue).locate(loc()));
} }
target.add(Instruction.storeVar(i, true).locate(loc()));
} }
public VariableAssignStatement(Location loc, String name, Statement val, Type operation) { public VariableAssignStatement(Location loc, String name, Statement val, Operation operation) {
super(loc); super(loc);
this.name = name; this.name = name;
this.value = val; this.value = val;

View File

@ -3,10 +3,11 @@ package me.topchetoeu.jscript.compilation.values;
import java.util.List; import java.util.List;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.AssignStatement;
import me.topchetoeu.jscript.compilation.AssignableStatement; import me.topchetoeu.jscript.compilation.AssignableStatement;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.Instruction.Type; import me.topchetoeu.jscript.engine.Operation;
import me.topchetoeu.jscript.engine.scope.ScopeRecord; import me.topchetoeu.jscript.engine.scope.ScopeRecord;
public class VariableStatement extends AssignableStatement { public class VariableStatement extends AssignableStatement {
@ -18,7 +19,7 @@ public class VariableStatement extends AssignableStatement {
public boolean pure() { return true; } public boolean pure() { return true; }
@Override @Override
public Statement toAssign(Statement val, Type operation) { public AssignStatement toAssign(Statement val, Operation operation) {
return new VariableAssignStatement(loc(), name, val, operation); return new VariableAssignStatement(loc(), name, val, operation);
} }

View File

@ -0,0 +1,42 @@
package me.topchetoeu.jscript.engine;
public enum Operation {
INSTANCEOF(2, false),
IN(2, false),
MULTIPLY(2, true),
DIVIDE(2, true),
MODULO(2, true),
ADD(2, true),
SUBTRACT(2, true),
USHIFT_RIGHT(2, true),
SHIFT_RIGHT(2, true),
SHIFT_LEFT(2, true),
GREATER(2, true),
LESS(2, true),
GREATER_EQUALS(2, true),
LESS_EQUALS(2, true),
LOOSE_EQUALS(2, true),
LOOSE_NOT_EQUALS(2, true),
EQUALS(2, true),
NOT_EQUALS(2, true),
AND(2, true),
OR(2, true),
XOR(2, true),
NEG(1, true),
POS(1, true),
NOT(1, true),
INVERSE(1, true);
public final int operands;
public final boolean optimizable;
private Operation(int n, boolean opt) {
this.operands = n;
this.optimizable = opt;
}
}

View File

@ -4,8 +4,6 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.Instruction.Type;
import me.topchetoeu.jscript.engine.BreakpointData;
import me.topchetoeu.jscript.engine.CallContext; import me.topchetoeu.jscript.engine.CallContext;
import me.topchetoeu.jscript.engine.DebugCommand; import me.topchetoeu.jscript.engine.DebugCommand;
import me.topchetoeu.jscript.engine.Engine; import me.topchetoeu.jscript.engine.Engine;
@ -17,6 +15,8 @@ import me.topchetoeu.jscript.engine.values.Values;
import me.topchetoeu.jscript.exceptions.EngineException; import me.topchetoeu.jscript.exceptions.EngineException;
public class CodeFrame { public class CodeFrame {
private record TryCtx(int tryStart, int tryEnd, int catchStart, int catchEnd, int finallyStart, int finallyEnd) { }
public static final DataKey<Integer> STACK_N_KEY = new DataKey<>(); public static final DataKey<Integer> STACK_N_KEY = new DataKey<>();
public static final DataKey<Integer> MAX_STACK_KEY = new DataKey<>(); public static final DataKey<Integer> MAX_STACK_KEY = new DataKey<>();
public static final DataKey<Boolean> STOP_AT_START_KEY = new DataKey<>(); public static final DataKey<Boolean> STOP_AT_START_KEY = new DataKey<>();
@ -25,10 +25,11 @@ public class CodeFrame {
public final LocalScope scope; public final LocalScope scope;
public final Object thisArg; public final Object thisArg;
public final Object[] args; public final Object[] args;
public final List<Object> stack = new ArrayList<>(); public final List<TryCtx> tryStack = new ArrayList<>();
public final List<TryContext> tryCtxs = new ArrayList<>();
public final CodeFunction function; public final CodeFunction function;
public Object[] stack = new Object[32];
public int stackPtr = 0;
public int codePtr = 0; public int codePtr = 0;
private DebugCommand debugCmd = null; private DebugCommand debugCmd = null;
private Location prevLoc = null; private Location prevLoc = null;
@ -37,41 +38,50 @@ public class CodeFrame {
return peek(0); return peek(0);
} }
public Object peek(int offset) { public Object peek(int offset) {
if (stack.size() <= offset) return null; if (stackPtr <= offset) return null;
else return stack.get(stack.size() - 1 - offset); else return stack[stackPtr - 1 - offset];
} }
public Object pop() { public Object pop() {
if (stack.size() == 0) return null; if (stackPtr == 0) return null;
else return stack.remove(stack.size() - 1); return stack[--stackPtr];
}
public Object[] take(int n) {
int srcI = stackPtr - n;
if (srcI < 0) srcI = 0;
int dstI = n + srcI - stackPtr;
int copyN = stackPtr - srcI;
Object[] res = new Object[n];
System.arraycopy(stack, srcI, res, dstI, copyN);
stackPtr -= copyN;
return res;
} }
public void push(Object val) { public void push(Object val) {
stack.add(stack.size(), Values.normalize(val)); if (stack.length <= stackPtr) {
var newStack = new Object[stack.length * 2];
System.arraycopy(stack, 0, newStack, 0, stack.length);
stack = newStack;
}
stack[stackPtr++] = Values.normalize(val);
} }
public void cleanup(CallContext ctx) { public void start(CallContext ctx) {
stack.clear(); if (ctx.getData(STACK_N_KEY, 0) >= ctx.addData(MAX_STACK_KEY, 10000)) throw EngineException.ofRange("Stack overflow!");
codePtr = 0; ctx.changeData(STACK_N_KEY);
debugCmd = null;
var debugState = ctx.getData(Engine.DEBUG_STATE_KEY);
if (debugState != null) debugState.pushFrame(this);
}
public void end(CallContext ctx) {
var debugState = ctx.getData(Engine.DEBUG_STATE_KEY); var debugState = ctx.getData(Engine.DEBUG_STATE_KEY);
if (debugState != null) debugState.popFrame(); if (debugState != null) debugState.popFrame();
ctx.changeData(STACK_N_KEY, -1); ctx.changeData(STACK_N_KEY, -1);
} }
public Object next(CallContext ctx) throws InterruptedException { private Object nextNoTry(CallContext ctx) throws InterruptedException {
var debugState = ctx.getData(Engine.DEBUG_STATE_KEY);
if (debugCmd == null) {
if (ctx.getData(STACK_N_KEY, 0) >= ctx.addData(MAX_STACK_KEY, 100000))
throw EngineException.ofRange("Stack overflow!");
ctx.changeData(STACK_N_KEY);
if (ctx.getData(STOP_AT_START_KEY, false)) debugCmd = DebugCommand.STEP_OVER;
else debugCmd = DebugCommand.NORMAL;
if (debugState != null) debugState.pushFrame(this);
}
if (Thread.currentThread().isInterrupted()) throw new InterruptedException(); if (Thread.currentThread().isInterrupted()) throw new InterruptedException();
if (codePtr < 0 || codePtr >= function.body.length) return null; if (codePtr < 0 || codePtr >= function.body.length) return null;
@ -80,20 +90,28 @@ public class CodeFrame {
var loc = instr.location; var loc = instr.location;
if (loc != null) prevLoc = loc; if (loc != null) prevLoc = loc;
if (debugState != null && loc != null) { // var debugState = ctx.getData(Engine.DEBUG_STATE_KEY);
if ( // if (debugCmd == null) {
instr.type == Type.NOP && instr.match("debug") || debugState.breakpoints.contains(loc) || ( // if (ctx.getData(STOP_AT_START_KEY, false)) debugCmd = DebugCommand.STEP_OVER;
ctx.getData(STEPPING_TROUGH_KEY, false) && // else debugCmd = DebugCommand.NORMAL;
(debugCmd == DebugCommand.STEP_INTO || debugCmd == DebugCommand.STEP_OVER)
)
) {
ctx.setData(STEPPING_TROUGH_KEY, true);
debugState.breakpointNotifier.next(new BreakpointData(loc, ctx)); // if (debugState != null) debugState.pushFrame(this);
debugCmd = debugState.commandNotifier.toAwaitable().await(); // }
if (debugCmd == DebugCommand.NORMAL) ctx.setData(STEPPING_TROUGH_KEY, false);
} // if (debugState != null && loc != null) {
} // if (
// instr.type == Type.NOP && instr.match("debug") || debugState.breakpoints.contains(loc) || (
// ctx.getData(STEPPING_TROUGH_KEY, false) &&
// (debugCmd == DebugCommand.STEP_INTO || debugCmd == DebugCommand.STEP_OVER)
// )
// ) {
// ctx.setData(STEPPING_TROUGH_KEY, true);
// debugState.breakpointNotifier.next(new BreakpointData(loc, ctx));
// debugCmd = debugState.commandNotifier.toAwaitable().await();
// if (debugCmd == DebugCommand.NORMAL) ctx.setData(STEPPING_TROUGH_KEY, false);
// }
// }
try { try {
return Runners.exec(debugCmd, instr, this, ctx); return Runners.exec(debugCmd, instr, this, ctx);
@ -103,73 +121,21 @@ public class CodeFrame {
} }
} }
public Object next(CallContext ctx) throws InterruptedException {
return nextNoTry(ctx);
}
public Object run(CallContext ctx) throws InterruptedException { public Object run(CallContext ctx) throws InterruptedException {
try { try {
start(ctx);
while (true) { while (true) {
var res = next(ctx); var res = next(ctx);
if (res != Runners.NO_RETURN) return res; if (res != Runners.NO_RETURN) return res;
} }
} }
finally { finally {
cleanup(ctx); end(ctx);
} }
// var debugState = ctx.getData(Engine.DEBUG_STATE_KEY);
// DebugCommand command = ctx.getData(STOP_AT_START_KEY, false) ? DebugCommand.STEP_OVER : DebugCommand.NORMAL;
// if (ctx.getData(STACK_N_KEY, 0) >= ctx.addData(MAX_STACK_KEY, 200)) throw EngineException.ofRange("Stack overflow!");
// ctx.changeData(STACK_N_KEY);
// if (debugState != null) debugState.pushFrame(this);
// Location loc = null;
// Location loc = null;
// try {
// while (codePtr >= 0 && codePtr < function.body.length) {
// var _loc = function.body[codePtr].location;
// if (_loc != null) loc = _loc;
// if (Thread.currentThread().isInterrupted()) throw new InterruptedException();
// var instr = function.body[codePtr];
// if (debugState != null && loc != null) {
// if (
// instr.type == Type.NOP && instr.match("debug") ||
// (
// (command == DebugCommand.STEP_INTO || command == DebugCommand.STEP_OVER) &&
// ctx.getData(STEPPING_TROUGH_KEY, false)
// ) ||
// debugState.breakpoints.contains(loc)
// ) {
// ctx.setData(STEPPING_TROUGH_KEY, true);
// debugState.breakpointNotifier.next(new BreakpointData(loc, ctx));
// command = debugState.commandNotifier.toAwaitable().await();
// if (command == DebugCommand.NORMAL) ctx.setData(STEPPING_TROUGH_KEY, false);
// }
// }
// try {
// var res = Runners.exec(command, instr, this, ctx);
// if (res != Runners.NO_RETURN) return res;
// }
// catch (EngineException e) {
// throw e.add(function.name, instr.location);
// }
// }
// return null;
// }
// // catch (StackOverflowError e) {
// // e.printStackTrace();
// // throw EngineException.ofRange("Stack overflow!").add(function.name, loc);
// // }
// finally {
// ctx.changeData(STACK_N_KEY, -1);
// }
} }
public CodeFrame(Object thisArg, Object[] args, CodeFunction func) { public CodeFrame(Object thisArg, Object[] args, CodeFunction func) {

View File

@ -5,6 +5,7 @@ import java.util.Collections;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.engine.CallContext; import me.topchetoeu.jscript.engine.CallContext;
import me.topchetoeu.jscript.engine.DebugCommand; import me.topchetoeu.jscript.engine.DebugCommand;
import me.topchetoeu.jscript.engine.Operation;
import me.topchetoeu.jscript.engine.scope.ValueVariable; import me.topchetoeu.jscript.engine.scope.ValueVariable;
import me.topchetoeu.jscript.engine.values.ArrayValue; import me.topchetoeu.jscript.engine.values.ArrayValue;
import me.topchetoeu.jscript.engine.values.CodeFunction; import me.topchetoeu.jscript.engine.values.CodeFunction;
@ -38,10 +39,7 @@ public class Runners {
} }
public static Object execCall(DebugCommand state, Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException { public static Object execCall(DebugCommand state, Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException {
int n = instr.get(0); var callArgs = frame.take(instr.get(0));
var callArgs = new Object[n];
for (var i = n - 1; i >= 0; i--) callArgs[i] = frame.pop();
var func = frame.pop(); var func = frame.pop();
var thisArg = frame.pop(); var thisArg = frame.pop();
@ -51,10 +49,7 @@ public class Runners {
return NO_RETURN; return NO_RETURN;
} }
public static Object execCallNew(DebugCommand state, Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException { public static Object execCallNew(DebugCommand state, Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException {
int n = instr.get(0); var callArgs = frame.take(instr.get(0));
var callArgs = new Object[n];
for (var i = n - 1; i >= 0; i--) callArgs[i] = frame.pop();
var funcObj = frame.pop(); var funcObj = frame.pop();
if (Values.isFunction(funcObj) && Values.function(funcObj).special) { if (Values.isFunction(funcObj) && Values.function(funcObj).special) {
@ -159,7 +154,7 @@ public class Runners {
exception = null; exception = null;
try { try {
ctx.setData(CodeFrame.STOP_AT_START_KEY, state != DebugCommand.NORMAL); ctx.setData(CodeFrame.STOP_AT_START_KEY, state != DebugCommand.NORMAL);
var _res = Values.call(ctx, catchFunc, frame.thisArg, exc); var _res = Values.call(ctx, catchFunc, frame.thisArg, exc.value);
if (!SignalValue.isSignal(_res, "no_return")) res = _res; if (!SignalValue.isSignal(_res, "no_return")) res = _res;
} }
catch (EngineException e) { catch (EngineException e) {
@ -194,10 +189,24 @@ public class Runners {
} }
public static Object execDup(Instruction instr, CodeFrame frame, CallContext ctx) { public static Object execDup(Instruction instr, CodeFrame frame, CallContext ctx) {
var val = frame.peek(instr.get(1)); int offset = instr.get(0), count = instr.get(1);
for (int i = 0; i < (int)instr.get(0); i++) {
frame.push(val); for (var i = 0; i < count; i++) {
frame.push(frame.peek(offset + count - 1));
} }
frame.codePtr++;
return NO_RETURN;
}
public static Object execMove(Instruction instr, CodeFrame frame, CallContext ctx) {
int offset = instr.get(0), count = instr.get(1);
var tmp = frame.take(offset);
var res = frame.take(count);
for (var i = 0; i < offset; i++) frame.push(tmp[i]);
for (var i = 0; i < count; i++) frame.push(res[i]);
frame.codePtr++; frame.codePtr++;
return NO_RETURN; return NO_RETURN;
} }
@ -376,157 +385,13 @@ public class Runners {
return NO_RETURN; return NO_RETURN;
} }
public static Object execAdd(Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException { public static Object execOperation(Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException {
Object b = frame.pop(), a = frame.pop(); Operation op = instr.get(0);
frame.push(Values.add(ctx, a, b)); var args = new Object[op.operands];
frame.codePtr++;
return NO_RETURN;
}
public static Object execSubtract(Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException {
Object b = frame.pop(), a = frame.pop();
frame.push(Values.subtract(ctx, a, b));
frame.codePtr++;
return NO_RETURN;
}
public static Object execMultiply(Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException {
Object b = frame.pop(), a = frame.pop();
frame.push(Values.multiply(ctx, a, b));
frame.codePtr++;
return NO_RETURN;
}
public static Object execDivide(Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException {
Object b = frame.pop(), a = frame.pop();
frame.push(Values.divide(ctx, a, b));
frame.codePtr++;
return NO_RETURN;
}
public static Object execModulo(Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException {
Object b = frame.pop(), a = frame.pop();
frame.push(Values.modulo(ctx, a, b));
frame.codePtr++;
return NO_RETURN;
}
public static Object execAnd(Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException { for (var i = op.operands - 1; i >= 0; i--) args[i] = frame.pop();
Object b = frame.pop(), a = frame.pop();
frame.push(Values.and(ctx, a, b)); frame.push(Values.operation(ctx, op, args));
frame.codePtr++;
return NO_RETURN;
}
public static Object execOr(Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException {
Object b = frame.pop(), a = frame.pop();
frame.push(Values.or(ctx, a, b));
frame.codePtr++;
return NO_RETURN;
}
public static Object execXor(Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException {
Object b = frame.pop(), a = frame.pop();
frame.push(Values.xor(ctx, a, b));
frame.codePtr++;
return NO_RETURN;
}
public static Object execLeftShift(Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException {
Object b = frame.pop(), a = frame.pop();
frame.push(Values.shiftLeft(ctx, a, b));
frame.codePtr++;
return NO_RETURN;
}
public static Object execRightShift(Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException {
Object b = frame.pop(), a = frame.pop();
frame.push(Values.shiftRight(ctx, a, b));
frame.codePtr++;
return NO_RETURN;
}
public static Object execUnsignedRightShift(Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException {
Object b = frame.pop(), a = frame.pop();
frame.push(Values.unsignedShiftRight(ctx, a, b));
frame.codePtr++;
return NO_RETURN;
}
public static Object execNot(Instruction instr, CodeFrame frame, CallContext ctx) {
frame.push(Values.not(frame.pop()));
frame.codePtr++;
return NO_RETURN;
}
public static Object execNeg(Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException {
frame.push(Values.negative(ctx, frame.pop()));
frame.codePtr++;
return NO_RETURN;
}
public static Object execPos(Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException {
frame.push(Values.toNumber(ctx, frame.pop()));
frame.codePtr++;
return NO_RETURN;
}
public static Object execInverse(Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException {
frame.push(Values.bitwiseNot(ctx, frame.pop()));
frame.codePtr++;
return NO_RETURN;
}
public static Object execGreaterThan(Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException {
Object b = frame.pop(), a = frame.pop();
frame.push(Values.compare(ctx, a, b) > 0);
frame.codePtr++;
return NO_RETURN;
}
public static Object execLessThan(Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException {
Object b = frame.pop(), a = frame.pop();
frame.push(Values.compare(ctx, a, b) < 0);
frame.codePtr++;
return NO_RETURN;
}
public static Object execGreaterThanEquals(Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException {
Object b = frame.pop(), a = frame.pop();
frame.push(Values.compare(ctx, a, b) >= 0);
frame.codePtr++;
return NO_RETURN;
}
public static Object execLessThanEquals(Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException {
Object b = frame.pop(), a = frame.pop();
frame.push(Values.compare(ctx, a, b) <= 0);
frame.codePtr++;
return NO_RETURN;
}
public static Object execLooseEquals(Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException {
Object b = frame.pop(), a = frame.pop();
frame.push(Values.looseEqual(ctx, a, b));
frame.codePtr++;
return NO_RETURN;
}
public static Object execLooseNotEquals(Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException {
Object b = frame.pop(), a = frame.pop();
frame.push(!Values.looseEqual(ctx, a, b));
frame.codePtr++;
return NO_RETURN;
}
public static Object execEquals(Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException {
Object b = frame.pop(), a = frame.pop();
frame.push(Values.strictEquals(a, b));
frame.codePtr++;
return NO_RETURN;
}
public static Object execNotEquals(Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException {
Object b = frame.pop(), a = frame.pop();
frame.push(!Values.strictEquals(a, b));
frame.codePtr++; frame.codePtr++;
return NO_RETURN; return NO_RETURN;
} }
@ -544,6 +409,7 @@ public class Runners {
case TRY: return execTry(state, instr, frame, ctx); case TRY: return execTry(state, instr, frame, ctx);
case DUP: return execDup(instr, frame, ctx); case DUP: return execDup(instr, frame, ctx);
case MOVE: return execMove(instr, frame, ctx);
case LOAD_VALUE: return execLoadValue(instr, frame, ctx); case LOAD_VALUE: return execLoadValue(instr, frame, ctx);
case LOAD_VAR: return execLoadVar(instr, frame, ctx); case LOAD_VAR: return execLoadVar(instr, frame, ctx);
case LOAD_OBJ: return execLoadObj(instr, frame, ctx); case LOAD_OBJ: return execLoadObj(instr, frame, ctx);
@ -560,45 +426,16 @@ public class Runners {
case STORE_SELF_FUNC: return execStoreSelfFunc(instr, frame, ctx); case STORE_SELF_FUNC: return execStoreSelfFunc(instr, frame, ctx);
case MAKE_VAR: return execMakeVar(instr, frame, ctx); case MAKE_VAR: return execMakeVar(instr, frame, ctx);
case IN: return execIn(instr, frame, ctx);
case KEYS: return execKeys(instr, frame, ctx); case KEYS: return execKeys(instr, frame, ctx);
case DEF_PROP: return execDefProp(instr, frame, ctx); case DEF_PROP: return execDefProp(instr, frame, ctx);
case TYPEOF: return execTypeof(instr, frame, ctx); case TYPEOF: return execTypeof(instr, frame, ctx);
case DELETE: return execDelete(instr, frame, ctx); case DELETE: return execDelete(instr, frame, ctx);
case INSTANCEOF: return execInstanceof(instr, frame, ctx);
case JMP: return execJmp(instr, frame, ctx); case JMP: return execJmp(instr, frame, ctx);
case JMP_IF: return execJmpIf(instr, frame, ctx); case JMP_IF: return execJmpIf(instr, frame, ctx);
case JMP_IFN: return execJmpIfNot(instr, frame, ctx); case JMP_IFN: return execJmpIfNot(instr, frame, ctx);
case ADD: return execAdd(instr, frame, ctx); case OPERATION: return execOperation(instr, frame, ctx);
case SUBTRACT: return execSubtract(instr, frame, ctx);
case MULTIPLY: return execMultiply(instr, frame, ctx);
case DIVIDE: return execDivide(instr, frame, ctx);
case MODULO: return execModulo(instr, frame, ctx);
case AND: return execAnd(instr, frame, ctx);
case OR: return execOr(instr, frame, ctx);
case XOR: return execXor(instr, frame, ctx);
case SHIFT_LEFT: return execLeftShift(instr, frame, ctx);
case SHIFT_RIGHT: return execRightShift(instr, frame, ctx);
case USHIFT_RIGHT: return execUnsignedRightShift(instr, frame, ctx);
case NOT: return execNot(instr, frame, ctx);
case NEG: return execNeg(instr, frame, ctx);
case POS: return execPos(instr, frame, ctx);
case INVERSE: return execInverse(instr, frame, ctx);
case GREATER: return execGreaterThan(instr, frame, ctx);
case GREATER_EQUALS: return execGreaterThanEquals(instr, frame, ctx);
case LESS: return execLessThan(instr, frame, ctx);
case LESS_EQUALS: return execLessThanEquals(instr, frame, ctx);
case LOOSE_EQUALS: return execLooseEquals(instr, frame, ctx);
case LOOSE_NOT_EQUALS: return execLooseNotEquals(instr, frame, ctx);
case EQUALS: return execEquals(instr, frame, ctx);
case NOT_EQUALS: return execNotEquals(instr, frame, ctx);
default: throw EngineException.ofSyntax("Invalid instruction " + instr.type.name() + "."); default: throw EngineException.ofSyntax("Invalid instruction " + instr.type.name() + ".");
} }

View File

@ -11,6 +11,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import me.topchetoeu.jscript.engine.CallContext; import me.topchetoeu.jscript.engine.CallContext;
import me.topchetoeu.jscript.engine.Operation;
import me.topchetoeu.jscript.engine.frame.ConvertHint; import me.topchetoeu.jscript.engine.frame.ConvertHint;
import me.topchetoeu.jscript.exceptions.EngineException; import me.topchetoeu.jscript.exceptions.EngineException;
@ -218,6 +219,47 @@ public class Values {
return false; return false;
} }
public static Object operation(CallContext ctx, Operation op, Object... args) throws InterruptedException {
switch (op) {
case ADD: return add(ctx, args[0], args[1]);
case SUBTRACT: return subtract(ctx, args[0], args[1]);
case DIVIDE: return divide(ctx, args[0], args[1]);
case MULTIPLY: return multiply(ctx, args[0], args[1]);
case MODULO: return modulo(ctx, args[0], args[1]);
case AND: return and(ctx, args[0], args[1]);
case OR: return or(ctx, args[0], args[1]);
case XOR: return xor(ctx, args[0], args[1]);
case EQUALS: return strictEquals(args[0], args[1]);
case NOT_EQUALS: return !strictEquals(args[0], args[1]);
case LOOSE_EQUALS: return looseEqual(ctx, args[0], args[1]);
case LOOSE_NOT_EQUALS: return !looseEqual(ctx, args[0], args[1]);
case GREATER: return compare(ctx, args[0], args[1]) > 0;
case GREATER_EQUALS: return compare(ctx, args[0], args[1]) >= 0;
case LESS: return compare(ctx, args[0], args[1]) < 0;
case LESS_EQUALS: return compare(ctx, args[0], args[1]) <= 0;
case INVERSE: return bitwiseNot(ctx, args[0]);
case NOT: return not(args[0]);
case POS: return toNumber(ctx, args[0]);
case NEG: return negative(ctx, args[0]);
case SHIFT_LEFT: return shiftLeft(ctx, args[0], args[1]);
case SHIFT_RIGHT: return shiftRight(ctx, args[0], args[1]);
case USHIFT_RIGHT: return unsignedShiftRight(ctx, args[0], args[1]);
case IN: return hasMember(ctx, args[1], args[0], false);
case INSTANCEOF: {
var proto = getMember(ctx, args[1], "prototype");
return isInstanceOf(ctx, args[0], proto);
}
default: return null;
}
}
public static Object getMember(CallContext ctx, Object obj, Object key) throws InterruptedException { public static Object getMember(CallContext ctx, Object obj, Object key) throws InterruptedException {
obj = normalize(obj); key = normalize(key); obj = normalize(obj); key = normalize(key);
if (obj == null) throw new IllegalArgumentException("Tried to access member of undefined."); if (obj == null) throw new IllegalArgumentException("Tried to access member of undefined.");

View File

@ -3,29 +3,28 @@ package me.topchetoeu.jscript.parsing;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.engine.Operation;
import me.topchetoeu.jscript.compilation.Instruction.Type;
public enum Operator { public enum Operator {
MULTIPLY("*", Type.MULTIPLY, 13), MULTIPLY("*", Operation.MULTIPLY, 13),
DIVIDE("/", Type.DIVIDE, 12), DIVIDE("/", Operation.DIVIDE, 12),
MODULO("%", Type.MODULO, 12), MODULO("%", Operation.MODULO, 12),
SUBTRACT("-", Type.SUBTRACT, 11), SUBTRACT("-", Operation.SUBTRACT, 11),
ADD("+", Type.ADD, 11), ADD("+", Operation.ADD, 11),
SHIFT_RIGHT(">>", Type.SHIFT_RIGHT, 10), SHIFT_RIGHT(">>", Operation.SHIFT_RIGHT, 10),
SHIFT_LEFT("<<", Type.SHIFT_LEFT, 10), SHIFT_LEFT("<<", Operation.SHIFT_LEFT, 10),
USHIFT_RIGHT(">>>", Type.USHIFT_RIGHT, 10), USHIFT_RIGHT(">>>", Operation.USHIFT_RIGHT, 10),
GREATER(">", Type.GREATER, 9), GREATER(">", Operation.GREATER, 9),
LESS("<", Type.LESS, 9), LESS("<", Operation.LESS, 9),
GREATER_EQUALS(">=", Type.GREATER_EQUALS, 9), GREATER_EQUALS(">=", Operation.GREATER_EQUALS, 9),
LESS_EQUALS("<=", Type.LESS_EQUALS, 9), LESS_EQUALS("<=", Operation.LESS_EQUALS, 9),
NOT_EQUALS("!=", Type.LOOSE_NOT_EQUALS, 8), NOT_EQUALS("!=", Operation.LOOSE_NOT_EQUALS, 8),
LOOSE_NOT_EQUALS("!==", Type.NOT_EQUALS, 8), LOOSE_NOT_EQUALS("!==", Operation.NOT_EQUALS, 8),
EQUALS("==", Type.LOOSE_EQUALS, 8), EQUALS("==", Operation.LOOSE_EQUALS, 8),
LOOSE_EQUALS("===", Type.EQUALS, 8), LOOSE_EQUALS("===", Operation.EQUALS, 8),
AND("&", Type.AND, 7), AND("&", Operation.AND, 7),
XOR("^", Type.XOR, 6), XOR("^", Operation.XOR, 6),
OR("|", Type.OR, 5), OR("|", Operation.OR, 5),
LAZY_AND("&&", 4), LAZY_AND("&&", 4),
LAZY_OR("||", 3), LAZY_OR("||", 3),
ASSIGN_SHIFT_LEFT("<<=", 2, true), ASSIGN_SHIFT_LEFT("<<=", 2, true),
@ -57,7 +56,7 @@ public enum Operator {
DECREASE("--"); DECREASE("--");
public final String value; public final String value;
public final Instruction.Type operation; public final Operation operation;
public final int precedence; public final int precedence;
public final boolean reverse; public final boolean reverse;
private static final Map<String, Operator> ops = new HashMap<>(); private static final Map<String, Operator> ops = new HashMap<>();
@ -99,13 +98,13 @@ public enum Operator {
this.reverse = reverse; this.reverse = reverse;
} }
private Operator(String value, Instruction.Type funcName, int precedence) { private Operator(String value, Operation funcName, int precedence) {
this. value = value; this. value = value;
this.operation = funcName; this.operation = funcName;
this.precedence = precedence; this.precedence = precedence;
this.reverse = false; this.reverse = false;
} }
private Operator(String value, Instruction.Type funcName, int precedence, boolean reverse) { private Operator(String value, Operation funcName, int precedence, boolean reverse) {
this.value = value; this.value = value;
this.operation = funcName; this.operation = funcName;
this.precedence = precedence; this.precedence = precedence;

View File

@ -13,6 +13,7 @@ import me.topchetoeu.jscript.compilation.VariableDeclareStatement.Pair;
import me.topchetoeu.jscript.compilation.control.*; import me.topchetoeu.jscript.compilation.control.*;
import me.topchetoeu.jscript.compilation.control.SwitchStatement.SwitchCase; import me.topchetoeu.jscript.compilation.control.SwitchStatement.SwitchCase;
import me.topchetoeu.jscript.compilation.values.*; import me.topchetoeu.jscript.compilation.values.*;
import me.topchetoeu.jscript.engine.Operation;
import me.topchetoeu.jscript.engine.scope.GlobalScope; import me.topchetoeu.jscript.engine.scope.GlobalScope;
import me.topchetoeu.jscript.engine.scope.ValueVariable; import me.topchetoeu.jscript.engine.scope.ValueVariable;
import me.topchetoeu.jscript.engine.values.CodeFunction; import me.topchetoeu.jscript.engine.values.CodeFunction;
@ -938,12 +939,12 @@ public class Parsing {
if (!opState.isSuccess()) return ParseRes.failed(); if (!opState.isSuccess()) return ParseRes.failed();
var op = opState.result; var op = opState.result;
Type operation = null; Operation operation = null;
if (op == Operator.ADD) operation = Type.POS; if (op == Operator.ADD) operation = Operation.POS;
else if (op == Operator.SUBTRACT) operation = Type.NEG; else if (op == Operator.SUBTRACT) operation = Operation.NEG;
else if (op == Operator.INVERSE) operation = Type.INVERSE; else if (op == Operator.INVERSE) operation = Operation.INVERSE;
else if (op == Operator.NOT) operation = Type.NOT; else if (op == Operator.NOT) operation = Operation.NOT;
else return ParseRes.failed(); else return ParseRes.failed();
var res = parseValue(filename, tokens, n + i, 14); var res = parseValue(filename, tokens, n + i, 14);
@ -1103,19 +1104,19 @@ public class Parsing {
if (!res.isSuccess()) return ParseRes.error(loc, "Expected value after assignment operator '%s'.".formatted(op.value), res); if (!res.isSuccess()) return ParseRes.error(loc, "Expected value after assignment operator '%s'.".formatted(op.value), res);
n += res.n; n += res.n;
Type operation = null; Operation operation = null;
if (op == Operator.ASSIGN_ADD) operation = Type.ADD; if (op == Operator.ASSIGN_ADD) operation = Operation.ADD;
if (op == Operator.ASSIGN_SUBTRACT) operation = Type.SUBTRACT; if (op == Operator.ASSIGN_SUBTRACT) operation = Operation.SUBTRACT;
if (op == Operator.ASSIGN_MULTIPLY) operation = Type.MULTIPLY; if (op == Operator.ASSIGN_MULTIPLY) operation = Operation.MULTIPLY;
if (op == Operator.ASSIGN_DIVIDE) operation = Type.DIVIDE; if (op == Operator.ASSIGN_DIVIDE) operation = Operation.DIVIDE;
if (op == Operator.ASSIGN_MODULO) operation = Type.MODULO; if (op == Operator.ASSIGN_MODULO) operation = Operation.MODULO;
if (op == Operator.ASSIGN_OR) operation = Type.OR; if (op == Operator.ASSIGN_OR) operation = Operation.OR;
if (op == Operator.ASSIGN_XOR) operation = Type.XOR; if (op == Operator.ASSIGN_XOR) operation = Operation.XOR;
if (op == Operator.ASSIGN_AND) operation = Type.AND; if (op == Operator.ASSIGN_AND) operation = Operation.AND;
if (op == Operator.ASSIGN_SHIFT_LEFT) operation = Type.SHIFT_LEFT; if (op == Operator.ASSIGN_SHIFT_LEFT) operation = Operation.SHIFT_LEFT;
if (op == Operator.ASSIGN_SHIFT_RIGHT) operation = Type.SHIFT_RIGHT; if (op == Operator.ASSIGN_SHIFT_RIGHT) operation = Operation.SHIFT_RIGHT;
if (op == Operator.ASSIGN_USHIFT_RIGHT) operation = Type.USHIFT_RIGHT; if (op == Operator.ASSIGN_USHIFT_RIGHT) operation = Operation.USHIFT_RIGHT;
return ParseRes.res(((AssignableStatement)prev).toAssign(res.result, operation), n); return ParseRes.res(((AssignableStatement)prev).toAssign(res.result, operation), n);
} }
@ -1180,7 +1181,7 @@ public class Parsing {
if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value after 'instanceof'.", valRes); if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value after 'instanceof'.", valRes);
n += valRes.n; n += valRes.n;
return ParseRes.res(new OperationStatement(loc, Type.INSTANCEOF, prev, valRes.result), n); return ParseRes.res(new OperationStatement(loc, Operation.INSTANCEOF, prev, valRes.result), n);
} }
public static ParseRes<OperationStatement> parseIn(String filename, List<Token> tokens, int i, Statement prev, int precedence) { public static ParseRes<OperationStatement> parseIn(String filename, List<Token> tokens, int i, Statement prev, int precedence) {
var loc = getLoc(filename, tokens, i); var loc = getLoc(filename, tokens, i);
@ -1193,7 +1194,7 @@ public class Parsing {
if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value after 'in'.", valRes); if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value after 'in'.", valRes);
n += valRes.n; n += valRes.n;
return ParseRes.res(new OperationStatement(loc, Type.IN, prev, valRes.result), n); return ParseRes.res(new OperationStatement(loc, Operation.IN, prev, valRes.result), n);
} }
public static ParseRes<CommaStatement> parseComma(String filename, List<Token> tokens, int i, Statement prev, int precedence) { public static ParseRes<CommaStatement> parseComma(String filename, List<Token> tokens, int i, Statement prev, int precedence) {
var loc = getLoc(filename, tokens, i); var loc = getLoc(filename, tokens, i);

View File

@ -28,6 +28,8 @@ public class AsyncFunction extends FunctionValue {
public Object fulfill(CallContext ctx, Object thisArg, Object[] args) throws InterruptedException { public Object fulfill(CallContext ctx, Object thisArg, Object[] args) throws InterruptedException {
if (args.length == 1) frame.push(args[0]); if (args.length == 1) frame.push(args[0]);
frame.start(ctx);
while (true) { while (true) {
awaiting = false; awaiting = false;
awaited = null; awaited = null;
@ -36,12 +38,12 @@ public class AsyncFunction extends FunctionValue {
var res = frame.next(ctx); var res = frame.next(ctx);
if (res != Runners.NO_RETURN) { if (res != Runners.NO_RETURN) {
promise.fulfill(ctx, res); promise.fulfill(ctx, res);
return null; break;
} }
} }
catch (EngineException e) { catch (EngineException e) {
promise.reject(e); promise.reject(e);
return null; break;
} }
if (!awaiting) continue; if (!awaiting) continue;
@ -54,15 +56,18 @@ public class AsyncFunction extends FunctionValue {
var res = Values.getMember(ctx, awaited, "then"); var res = Values.getMember(ctx, awaited, "then");
if (res instanceof FunctionValue) { if (res instanceof FunctionValue) {
Values.function(res).call(ctx, awaited, fulfillFunc, rejectFunc); Values.function(res).call(ctx, awaited, fulfillFunc, rejectFunc);
return null; break;
} }
else frame.push(awaited); else frame.push(awaited);
} }
catch (EngineException e) { catch (EngineException e) {
promise.reject(e); promise.reject(e);
return null; break;
} }
} }
frame.end(ctx);
return null;
} }
public Object await(CallContext ctx, Object thisArg, Object[] args) { public Object await(CallContext ctx, Object thisArg, Object[] args) {