refactor: some code cleanup, emit better bytecode

This commit is contained in:
TopchetoEU 2023-10-04 12:15:52 +03:00
parent 4d1846f082
commit 21a6d20ac5
Signed by: topchetoeu
GPG Key ID: 6531B8583E5F6ED4
46 changed files with 192 additions and 454 deletions

View File

@ -1,19 +0,0 @@
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

@ -4,7 +4,7 @@ import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.engine.Operation;
public abstract class AssignableStatement extends Statement {
public abstract AssignStatement toAssign(Statement val, Operation operation);
public abstract Statement toAssign(Statement val, Operation operation);
protected AssignableStatement(Location loc) {
super(loc);

View File

@ -1,11 +0,0 @@
package me.topchetoeu.jscript.compilation;
public class CompileOptions {
public final boolean emitBpMap;
public final boolean emitVarNames;
public CompileOptions(boolean emitBpMap, boolean emitVarNames) {
this.emitBpMap = emitBpMap;
this.emitVarNames = emitVarNames;
}
}

View File

@ -13,16 +13,6 @@ import me.topchetoeu.jscript.engine.scope.ScopeRecord;
public class CompoundStatement extends Statement {
public final Statement[] statements;
@Override
public boolean pollutesStack() {
for (var stm : statements) {
if (stm instanceof FunctionStatement) continue;
return true;
}
return false;
}
@Override
public void declare(ScopeRecord varsScope) {
for (var stm : statements) {
@ -31,7 +21,7 @@ public class CompoundStatement extends Statement {
}
@Override
public void compile(List<Instruction> target, ScopeRecord scope) {
public void compile(List<Instruction> target, ScopeRecord scope, boolean pollute) {
for (var stm : statements) {
if (stm instanceof FunctionStatement) {
int start = target.size();
@ -45,8 +35,8 @@ public class CompoundStatement extends Statement {
var stm = statements[i];
if (stm instanceof FunctionStatement) continue;
if (i != statements.length - 1) stm.compileNoPollution(target, scope, true);
else stm.compileWithPollution(target, scope);
if (i != statements.length - 1) stm.compileWithDebug(target, scope, false);
else stm.compileWithDebug(target, scope, pollute);
}
}

View File

@ -10,13 +10,9 @@ public class DiscardStatement extends Statement {
public final Statement value;
@Override
public boolean pollutesStack() { return false; }
@Override
public void compile(List<Instruction> target, ScopeRecord scope) {
if (value == null) return;
value.compile(target, scope);
if (value.pollutesStack()) target.add(Instruction.discard());
public void compile(List<Instruction> target, ScopeRecord scope, boolean pollute) {
value.compile(target, scope, false);
}
@Override
public Statement optimize() {

View File

@ -7,7 +7,6 @@ import me.topchetoeu.jscript.exceptions.SyntaxException;
public class Instruction {
public static enum Type {
RETURN,
SIGNAL,
THROW,
THROW_SYNTAX,
DELETE,
@ -162,12 +161,6 @@ public class Instruction {
return new Instruction(null, Type.NOP, args);
}
/**
* ATTENTION: Usage outside of try/catch is broken af
*/
public static Instruction signal(String name) {
return new Instruction(null, Type.SIGNAL, name);
}
public static Instruction nop(Object ...params) {
for (var param : params) {
if (param instanceof String) continue;

View File

@ -8,29 +8,15 @@ import me.topchetoeu.jscript.engine.scope.ScopeRecord;
public abstract class Statement {
private Location _loc;
public abstract boolean pollutesStack();
public boolean pure() { return false; }
public abstract void compile(List<Instruction> target, ScopeRecord scope);
public abstract void compile(List<Instruction> target, ScopeRecord scope, boolean pollute);
public void declare(ScopeRecord varsScope) { }
public Statement optimize() { return this; }
public void compileNoPollution(List<Instruction> target, ScopeRecord scope, boolean debug) {
public void compileWithDebug(List<Instruction> target, ScopeRecord scope, boolean pollute) {
int start = target.size();
compile(target, scope);
if (debug && target.size() != start) target.get(start).setDebug(true);
if (pollutesStack()) target.add(Instruction.discard().locate(loc()));
}
public void compileWithPollution(List<Instruction> target, ScopeRecord scope, boolean debug) {
int start = target.size();
compile(target, scope);
if (debug && target.size() != start) target.get(start).setDebug(true);
if (!pollutesStack()) target.add(Instruction.loadValue(null).locate(loc()));
}
public void compileNoPollution(List<Instruction> target, ScopeRecord scope) {
compileNoPollution(target, scope, false);
}
public void compileWithPollution(List<Instruction> target, ScopeRecord scope) {
compileWithPollution(target, scope, false);
compile(target, scope, pollute);
if (target.size() != start) target.get(start).setDebug(true);
}
public Location loc() { return _loc; }

View File

@ -19,8 +19,6 @@ public class VariableDeclareStatement extends Statement {
public final List<Pair> values;
@Override
public boolean pollutesStack() { return false; }
@Override
public void declare(ScopeRecord varsScope) {
for (var key : values) {
@ -28,7 +26,7 @@ public class VariableDeclareStatement extends Statement {
}
}
@Override
public void compile(List<Instruction> target, ScopeRecord scope) {
public void compile(List<Instruction> target, ScopeRecord scope, boolean pollute) {
for (var entry : values) {
if (entry.name == null) continue;
var key = scope.getKey(entry.name);
@ -39,10 +37,12 @@ public class VariableDeclareStatement extends Statement {
target.add(Instruction.storeVar(key).locate(loc()));
}
else if (entry.value != null) {
entry.value.compileWithPollution(target, scope);
entry.value.compile(target, scope, true);
target.add(Instruction.storeVar(key).locate(loc()));
}
}
if (pollute) target.add(Instruction.loadValue(null).locate(loc()));
}
public VariableDeclareStatement(Location loc, List<Pair> values) {

View File

@ -1,37 +1,36 @@
package me.topchetoeu.jscript.compilation.values;
import java.util.List;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
public class ArrayStatement extends Statement {
public final Statement[] statements;
@Override
public boolean pollutesStack() { return true; }
@Override
public boolean pure() { return true; }
@Override
public void compile(List<Instruction> target, ScopeRecord scope) {
target.add(Instruction.loadArr(statements.length).locate(loc()));
var i = 0;
for (var el : statements) {
if (el != null) {
target.add(Instruction.dup().locate(loc()));
target.add(Instruction.loadValue(i).locate(loc()));
el.compileWithPollution(target, scope);
target.add(Instruction.storeMember().locate(loc()));
}
i++;
}
}
public ArrayStatement(Location loc, Statement[] statements) {
super(loc);
this.statements = statements;
}
}
package me.topchetoeu.jscript.compilation.control;
import java.util.List;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
public class ArrayStatement extends Statement {
public final Statement[] statements;
@Override
public boolean pure() { return true; }
@Override
public void compile(List<Instruction> target, ScopeRecord scope, boolean pollute) {
target.add(Instruction.loadArr(statements.length).locate(loc()));
var i = 0;
for (var el : statements) {
if (el != null) {
target.add(Instruction.dup().locate(loc()));
target.add(Instruction.loadValue(i).locate(loc()));
el.compile(target, scope, true);
target.add(Instruction.storeMember().locate(loc()));
}
i++;
}
if (!pollute) target.add(Instruction.discard().locate(loc()));
}
public ArrayStatement(Location loc, Statement[] statements) {
super(loc);
this.statements = statements;
}
}

View File

@ -11,11 +11,9 @@ public class BreakStatement extends Statement {
public final String label;
@Override
public boolean pollutesStack() { return false; }
@Override
public void compile(List<Instruction> target, ScopeRecord scope) {
public void compile(List<Instruction> target, ScopeRecord scope, boolean pollute) {
target.add(Instruction.nop("break", label).locate(loc()));
if (pollute) target.add(Instruction.loadValue(null).locate(loc()));
}
public BreakStatement(Location loc, String label) {

View File

@ -11,11 +11,9 @@ public class ContinueStatement extends Statement {
public final String label;
@Override
public boolean pollutesStack() { return false; }
@Override
public void compile(List<Instruction> target, ScopeRecord scope) {
public void compile(List<Instruction> target, ScopeRecord scope, boolean pollute) {
target.add(Instruction.nop("cont", label).locate(loc()));
if (pollute) target.add(Instruction.loadValue(null).locate(loc()));
}
public ContinueStatement(Location loc, String label) {

View File

@ -9,11 +9,9 @@ import me.topchetoeu.jscript.engine.scope.ScopeRecord;
public class DebugStatement extends Statement {
@Override
public boolean pollutesStack() { return false; }
@Override
public void compile(List<Instruction> target, ScopeRecord scope) {
public void compile(List<Instruction> target, ScopeRecord scope, boolean pollute) {
target.add(Instruction.debug().locate(loc()));
if (pollute) target.add(Instruction.loadValue(null).locate(loc()));
}
public DebugStatement(Location loc) {

View File

@ -12,13 +12,12 @@ public class DeleteStatement extends Statement {
public final Statement value;
@Override
public boolean pollutesStack() { return true; }
public void compile(List<Instruction> target, ScopeRecord scope, boolean pollute) {
value.compile(target, scope, true);
key.compile(target, scope, true);
@Override
public void compile(List<Instruction> target, ScopeRecord scope) {
value.compile(target, scope);
key.compile(target, scope);
target.add(Instruction.delete().locate(loc()));
if (!pollute) target.add(Instruction.discard().locate(loc()));
}
public DeleteStatement(Location loc, Statement key, Statement value) {

View File

@ -14,19 +14,16 @@ public class DoWhileStatement extends Statement {
public final Statement condition, body;
public final String label;
@Override
public boolean pollutesStack() { return false; }
@Override
public void declare(ScopeRecord globScope) {
body.declare(globScope);
}
@Override
public void compile(List<Instruction> target, ScopeRecord scope) {
public void compile(List<Instruction> target, ScopeRecord scope, boolean pollute) {
if (condition instanceof ConstantStatement) {
int start = target.size();
body.compileNoPollution(target, scope);
body.compile(target, scope, false);
int end = target.size();
if (Values.toBoolean(((ConstantStatement)condition).value)) {
WhileStatement.replaceBreaks(target, label, start, end, end + 1, end + 1);
@ -35,13 +32,14 @@ public class DoWhileStatement extends Statement {
target.add(Instruction.jmp(start - end).locate(loc()));
WhileStatement.replaceBreaks(target, label, start, end, start, end + 1);
}
if (pollute) target.add(Instruction.loadValue(null).locate(loc()));
return;
}
int start = target.size();
body.compileNoPollution(target, scope, true);
body.compileWithDebug(target, scope, false);
int mid = target.size();
condition.compileWithPollution(target, scope);
condition.compile(target, scope, true);
int end = target.size();
WhileStatement.replaceBreaks(target, label, start, mid - 1, mid, end + 1);

View File

@ -14,9 +14,6 @@ public class ForInStatement extends Statement {
public final Statement varValue, object, body;
public final String label;
@Override
public boolean pollutesStack() { return false; }
@Override
public void declare(ScopeRecord globScope) {
body.declare(globScope);
@ -24,16 +21,16 @@ public class ForInStatement extends Statement {
}
@Override
public void compile(List<Instruction> target, ScopeRecord scope) {
public void compile(List<Instruction> target, ScopeRecord scope, boolean pollute) {
var key = scope.getKey(varName);
if (key instanceof String) target.add(Instruction.makeVar((String)key));
if (varValue != null) {
varValue.compileWithPollution(target, scope);
varValue.compile(target, scope, true);
target.add(Instruction.storeVar(scope.getKey(varName)));
}
object.compileWithPollution(target, scope);
object.compile(target, scope, true);
target.add(Instruction.keys());
int start = target.size();
@ -58,8 +55,7 @@ public class ForInStatement extends Statement {
for (var i = start; i < target.size(); i++) target.get(i).locate(loc());
body.compileNoPollution(target, scope, true);
body.compileWithDebug(target, scope, false);
int end = target.size();
@ -68,6 +64,7 @@ public class ForInStatement extends Statement {
target.add(Instruction.jmp(start - end).locate(loc()));
target.add(Instruction.discard().locate(loc()));
target.set(mid, Instruction.jmpIf(end - mid + 1).locate(loc()));
if (pollute) target.add(Instruction.loadValue(null).locate(loc()));
}
public ForInStatement(Location loc, String label, boolean isDecl, String varName, Statement varValue, Statement object, Statement body) {

View File

@ -14,44 +14,43 @@ public class ForStatement extends Statement {
public final Statement declaration, assignment, condition, body;
public final String label;
@Override
public boolean pollutesStack() { return false; }
@Override
public void declare(ScopeRecord globScope) {
declaration.declare(globScope);
body.declare(globScope);
}
@Override
public void compile(List<Instruction> target, ScopeRecord scope) {
declaration.compile(target, scope);
public void compile(List<Instruction> target, ScopeRecord scope, boolean pollute) {
declaration.compile(target, scope, false);
if (condition instanceof ConstantStatement) {
if (Values.toBoolean(((ConstantStatement)condition).value)) {
int start = target.size();
body.compileNoPollution(target, scope);
body.compile(target, scope, false);
int mid = target.size();
assignment.compileNoPollution(target, scope, true);
assignment.compileWithDebug(target, scope, false);
int end = target.size();
WhileStatement.replaceBreaks(target, label, start, mid, mid, end + 1);
target.add(Instruction.jmp(start - target.size()).locate(loc()));
return;
if (pollute) target.add(Instruction.loadValue(null).locate(loc()));
}
return;
}
int start = target.size();
condition.compileWithPollution(target, scope);
condition.compile(target, scope, true);
int mid = target.size();
target.add(Instruction.nop());
body.compileNoPollution(target, scope);
body.compile(target, scope, false);
int beforeAssign = target.size();
assignment.compile(target, scope);
assignment.compileWithDebug(target, scope, false);
int end = target.size();
WhileStatement.replaceBreaks(target, label, mid + 1, end, beforeAssign, end + 1);
target.add(Instruction.jmp(start - end).locate(loc()));
target.set(mid, Instruction.jmpIfNot(end - mid + 1).locate(loc()));
if (pollute) target.add(Instruction.loadValue(null).locate(loc()));
}
@Override
public Statement optimize() {

View File

@ -14,9 +14,6 @@ import me.topchetoeu.jscript.engine.values.Values;
public class IfStatement extends Statement {
public final Statement condition, body, elseBody;
@Override
public boolean pollutesStack() { return false; }
@Override
public void declare(ScopeRecord globScope) {
body.declare(globScope);
@ -24,33 +21,34 @@ public class IfStatement extends Statement {
}
@Override
public void compile(List<Instruction> target, ScopeRecord scope) {
public void compile(List<Instruction> target, ScopeRecord scope, boolean pollute) {
if (condition instanceof ConstantStatement) {
if (Values.not(((ConstantStatement)condition).value)) {
if (elseBody != null) elseBody.compileNoPollution(target, scope, true);
if (elseBody != null) elseBody.compileWithDebug(target, scope, pollute);
}
else {
body.compileNoPollution(target, scope, true);
body.compileWithDebug(target, scope, pollute);
}
return;
}
condition.compileWithPollution(target, scope);
condition.compile(target, scope, true);
if (elseBody == null) {
int i = target.size();
target.add(Instruction.nop());
body.compileNoPollution(target, scope, true);
body.compileWithDebug(target, scope, pollute);
int endI = target.size();
target.set(i, Instruction.jmpIfNot(endI - i).locate(loc()));
}
else {
int start = target.size();
target.add(Instruction.nop());
body.compileNoPollution(target, scope, true);
body.compileWithDebug(target, scope, pollute);
target.add(Instruction.nop());
int mid = target.size();
elseBody.compileNoPollution(target, scope, true);
elseBody.compileWithDebug(target, scope, pollute);
int end = target.size();
target.set(start, Instruction.jmpIfNot(mid - start).locate(loc()));

View File

@ -11,12 +11,9 @@ public class ReturnStatement extends Statement {
public final Statement value;
@Override
public boolean pollutesStack() { return false; }
@Override
public void compile(List<Instruction> target, ScopeRecord scope) {
public void compile(List<Instruction> target, ScopeRecord scope, boolean pollute) {
if (value == null) target.add(Instruction.loadValue(null).locate(loc()));
else value.compileWithPollution(target, scope);
else value.compile(target, scope, true);
target.add(Instruction.ret().locate(loc()));
}

View File

@ -21,9 +21,6 @@ public class SwitchStatement extends Statement {
}
}
@Override
public boolean pollutesStack() { return false; }
public final Statement value;
public final SwitchCase[] cases;
public final Statement[] body;
@ -35,15 +32,15 @@ public class SwitchStatement extends Statement {
}
@Override
public void compile(List<Instruction> target, ScopeRecord scope) {
public void compile(List<Instruction> target, ScopeRecord scope, boolean pollute) {
var caseMap = new HashMap<Integer, Integer>();
var stmIndexMap = new HashMap<Integer, Integer>();
value.compile(target, scope);
value.compile(target, scope, true);
for (var ccase : cases) {
target.add(Instruction.dup().locate(loc()));
ccase.value.compileWithPollution(target, scope);
ccase.value.compile(target, scope, true);
target.add(Instruction.operation(Operation.EQUALS).locate(loc()));
caseMap.put(target.size(), ccase.statementI);
target.add(Instruction.nop());
@ -55,7 +52,7 @@ public class SwitchStatement extends Statement {
for (var stm : body) {
stmIndexMap.put(stmIndexMap.size(), target.size());
stm.compileNoPollution(target, scope, true);
stm.compileWithDebug(target, scope, false);
}
if (defaultI < 0 || defaultI >= body.length) target.set(start, Instruction.jmp(target.size() - start).locate(loc()));

View File

@ -11,11 +11,8 @@ public class ThrowStatement extends Statement {
public final Statement value;
@Override
public boolean pollutesStack() { return false; }
@Override
public void compile(List<Instruction> target, ScopeRecord scope) {
value.compileWithPollution(target, scope);
public void compile(List<Instruction> target, ScopeRecord scope, boolean pollute) {
value.compile(target, scope, true);
target.add(Instruction.throwInstr().locate(loc()));
}

View File

@ -15,9 +15,6 @@ public class TryStatement extends Statement {
public final Statement finallyBody;
public final String name;
@Override
public boolean pollutesStack() { return false; }
@Override
public void declare(ScopeRecord globScope) {
tryBody.declare(globScope);
@ -26,30 +23,31 @@ public class TryStatement extends Statement {
}
@Override
public void compile(List<Instruction> target, ScopeRecord scope) {
public void compile(List<Instruction> target, ScopeRecord scope, boolean pollute) {
target.add(Instruction.nop());
int start = target.size(), tryN, catchN = -1, finN = -1;
tryBody.compileNoPollution(target, scope);
tryBody.compile(target, scope, false);
tryN = target.size() - start;
if (catchBody != null) {
int tmp = target.size();
var local = scope instanceof GlobalScope ? scope.child() : (LocalScopeRecord)scope;
local.define(name, true);
catchBody.compileNoPollution(target, scope);
catchBody.compile(target, scope, false);
local.undefine();
catchN = target.size() - tmp;
}
if (finallyBody != null) {
int tmp = target.size();
finallyBody.compileNoPollution(target, scope);
finallyBody.compile(target, scope, false);
finN = target.size() - tmp;
}
target.set(start - 1, Instruction.tryInstr(tryN, catchN, finN).locate(loc()));
if (pollute) target.add(Instruction.loadValue(null).locate(loc()));
}
public TryStatement(Location loc, Statement tryBody, Statement catchBody, Statement finallyBody, String name) {

View File

@ -16,19 +16,16 @@ public class WhileStatement extends Statement {
public final Statement condition, body;
public final String label;
@Override
public boolean pollutesStack() { return false; }
@Override
public void declare(ScopeRecord globScope) {
body.declare(globScope);
}
@Override
public void compile(List<Instruction> target, ScopeRecord scope) {
public void compile(List<Instruction> target, ScopeRecord scope, boolean pollute) {
if (condition instanceof ConstantStatement) {
if (Values.toBoolean(((ConstantStatement)condition).value)) {
int start = target.size();
body.compileNoPollution(target, scope);
body.compile(target, scope, false);
int end = target.size();
replaceBreaks(target, label, start, end, start, end + 1);
target.add(Instruction.jmp(start - target.size()).locate(loc()));
@ -37,10 +34,10 @@ public class WhileStatement extends Statement {
}
int start = target.size();
condition.compileWithPollution(target, scope);
condition.compile(target, scope, true);
int mid = target.size();
target.add(Instruction.nop());
body.compileNoPollution(target, scope);
body.compile(target, scope, false);
int end = target.size();
@ -48,6 +45,7 @@ public class WhileStatement extends Statement {
target.add(Instruction.jmp(start - end).locate(loc()));
target.set(mid, Instruction.jmpIfNot(end - mid + 1).locate(loc()));
if (pollute) target.add(Instruction.loadValue(null).locate(loc()));
}
@Override
public Statement optimize() {

View File

@ -12,23 +12,19 @@ public class CallStatement extends Statement {
public final Statement[] args;
@Override
public boolean pollutesStack() { return true; }
@Override
public void compile(List<Instruction> target, ScopeRecord scope) {
public void compile(List<Instruction> target, ScopeRecord scope, boolean pollute) {
if (func instanceof IndexStatement) {
((IndexStatement)func).compile(target, scope, true);
((IndexStatement)func).compile(target, scope, true, true);
}
else {
target.add(Instruction.loadValue(null).locate(loc()));
func.compileWithPollution(target, scope);
target.add(Instruction.loadValue(null).locate(loc()));
func.compile(target, scope, true);
}
for (var arg : args) {
arg.compileWithPollution(target, scope);
}
for (var arg : args) arg.compile(target, scope, true);
target.add(Instruction.call(args.length).locate(loc()).setDebug(true));
if (!pollute) target.add(Instruction.discard().locate(loc()));
}
public CallStatement(Location loc, Statement func, Statement ...args) {

View File

@ -15,11 +15,9 @@ public class ChangeStatement extends Statement {
public final boolean postfix;
@Override
public boolean pollutesStack() { return true; }
@Override
public void compile(List<Instruction> target, ScopeRecord scope) {
public void compile(List<Instruction> target, ScopeRecord scope, boolean pollute) {
value.toAssign(new ConstantStatement(loc(), -addAmount), Operation.SUBTRACT).compile(target, scope, postfix);
if (!pollute) target.add(Instruction.discard().locate(loc()));
}
public ChangeStatement(Location loc, AssignableStatement value, double addAmount, boolean postfix) {

View File

@ -1,38 +0,0 @@
package me.topchetoeu.jscript.compilation.values;
import java.util.List;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
public class CommaStatement extends Statement {
public final Statement first;
public final Statement second;
@Override
public boolean pollutesStack() { return true; }
@Override
public boolean pure() { return first.pure() && second.pure(); }
@Override
public void compile(List<Instruction> target, ScopeRecord scope) {
first.compileNoPollution(target, scope);
second.compileWithPollution(target, scope);
}
@Override
public Statement optimize() {
var f = first.optimize();
var s = second.optimize();
if (f.pure()) return s;
else return new CommaStatement(loc(), f, s);
}
public CommaStatement(Location loc, Statement first, Statement second) {
super(loc);
this.first = first;
this.second = second;
}
}

View File

@ -10,14 +10,12 @@ import me.topchetoeu.jscript.engine.scope.ScopeRecord;
public class ConstantStatement extends Statement {
public final Object value;
@Override
public boolean pollutesStack() { return true; }
@Override
public boolean pure() { return true; }
@Override
public void compile(List<Instruction> target, ScopeRecord scope) {
target.add(Instruction.loadValue(value).locate(loc()));
public void compile(List<Instruction> target, ScopeRecord scope, boolean pollute) {
if (pollute) target.add(Instruction.loadValue(value).locate(loc()));
}
public ConstantStatement(Location loc, Object val) {

View File

@ -17,8 +17,6 @@ public class FunctionStatement extends Statement {
@Override
public boolean pure() { return name == null; }
@Override
public boolean pollutesStack() { return true; }
@Override
public void declare(ScopeRecord scope) {
@ -69,7 +67,7 @@ public class FunctionStatement extends Statement {
body.declare(subscope);
target.add(Instruction.debugVarNames(subscope.locals()));
body.compile(target, subscope);
body.compile(target, subscope, false);
checkBreakAndCont(target, start);
@ -90,12 +88,13 @@ public class FunctionStatement extends Statement {
var key = scope.getKey(this.name);
if (key instanceof String) target.add(Instruction.makeVar((String)key).locate(loc()));
target.add(Instruction.storeVar(scope.getKey(this.name), true).locate(loc()));
target.add(Instruction.storeVar(scope.getKey(this.name), false).locate(loc()));
}
}
@Override
public void compile(List<Instruction> target, ScopeRecord scope) {
public void compile(List<Instruction> target, ScopeRecord scope, boolean pollute) {
compile(target, scope, null, false);
if (!pollute) target.add(Instruction.discard().locate(loc()));
}
public FunctionStatement(Location loc, String name, String[] args, CompoundStatement body) {

View File

@ -8,14 +8,12 @@ import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
public class GlobalThisStatement extends Statement {
@Override
public boolean pollutesStack() { return true; }
@Override
public boolean pure() { return true; }
@Override
public void compile(List<Instruction> target, ScopeRecord scope) {
target.add(Instruction.loadGlob().locate(loc()));
public void compile(List<Instruction> target, ScopeRecord scope, boolean pollute) {
if (pollute) target.add(Instruction.loadGlob().locate(loc()));
}
public GlobalThisStatement(Location loc) {

View File

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

View File

@ -3,7 +3,6 @@ package me.topchetoeu.jscript.compilation.values;
import java.util.List;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.AssignStatement;
import me.topchetoeu.jscript.compilation.AssignableStatement;
import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement;
@ -14,31 +13,30 @@ public class IndexStatement extends AssignableStatement {
public final Statement object;
public final Statement index;
@Override
public boolean pollutesStack() { return true; }
@Override
public boolean pure() { return true; }
@Override
public AssignStatement toAssign(Statement val, Operation operation) {
public Statement toAssign(Statement val, Operation 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, boolean pollute) {
int start = 0;
object.compileWithPollution(target, scope);
object.compile(target, scope, true);
if (dupObj) target.add(Instruction.dup().locate(loc()));
if (index instanceof ConstantStatement) {
target.add(Instruction.loadMember(((ConstantStatement)index).value).locate(loc()));
return;
}
index.compileWithPollution(target, scope);
index.compile(target, scope, true);
target.add(Instruction.loadMember().locate(loc()));
target.get(start).setDebug(true);
if (!pollute) target.add(Instruction.discard().locate(loc()));
}
@Override
public void compile(List<Instruction> target, ScopeRecord scope) {
compile(target, scope, false);
public void compile(List<Instruction> target, ScopeRecord scope, boolean pollute) {
compile(target, scope, false, pollute);
}
public IndexStatement(Location loc, Statement object, Statement index) {

View File

@ -11,29 +11,27 @@ import me.topchetoeu.jscript.engine.values.Values;
public class LazyAndStatement extends Statement {
public final Statement first, second;
@Override
public boolean pollutesStack() { return true; }
@Override
public boolean pure() {
return first.pure() && second.pure();
}
@Override
public void compile(List<Instruction> target, ScopeRecord scope) {
public void compile(List<Instruction> target, ScopeRecord scope, boolean pollute) {
if (first instanceof ConstantStatement) {
if (Values.not(((ConstantStatement)first).value)) {
first.compileWithPollution(target, scope);
first.compile(target, scope, pollute);
}
else second.compileWithPollution(target, scope);
else second.compile(target, scope, pollute);
return;
}
first.compileWithPollution(target, scope);
target.add(Instruction.dup().locate(loc()));
first.compile(target, scope, true);
if (pollute) target.add(Instruction.dup().locate(loc()));
int start = target.size();
target.add(Instruction.nop());
target.add(Instruction.discard().locate(loc()));
second.compileWithPollution(target, scope);
second.compile(target, scope, pollute);
target.set(start, Instruction.jmpIfNot(target.size() - start).locate(loc()));
}

View File

@ -11,29 +11,27 @@ import me.topchetoeu.jscript.engine.values.Values;
public class LazyOrStatement extends Statement {
public final Statement first, second;
@Override
public boolean pollutesStack() { return true; }
@Override
public boolean pure() {
return first.pure() && second.pure();
}
@Override
public void compile(List<Instruction> target, ScopeRecord scope) {
public void compile(List<Instruction> target, ScopeRecord scope, boolean pollute) {
if (first instanceof ConstantStatement) {
if (Values.not(((ConstantStatement)first).value)) {
second.compileWithPollution(target, scope);
second.compile(target, scope, pollute);
}
else first.compileWithPollution(target, scope);
else first.compile(target, scope, pollute);
return;
}
first.compileWithPollution(target, scope);
target.add(Instruction.dup().locate(loc()));
first.compile(target, scope, true);
if (pollute) target.add(Instruction.dup().locate(loc()));
int start = target.size();
target.add(Instruction.nop());
target.add(Instruction.discard().locate(loc()));
second.compileWithPollution(target, scope);
second.compile(target, scope, pollute);
target.set(start, Instruction.jmpIf(target.size() - start).locate(loc()));
}

View File

@ -12,14 +12,10 @@ public class NewStatement extends Statement {
public final Statement[] args;
@Override
public boolean pollutesStack() { return true; }
public void compile(List<Instruction> target, ScopeRecord scope, boolean pollute) {
func.compile(target, scope, true);
@Override
public void compile(List<Instruction> target, ScopeRecord scope) {
func.compileWithPollution(target, scope);
for (var arg : args) {
arg.compileWithPollution(target, scope);
}
for (var arg : args) arg.compile(target, scope, true);
target.add(Instruction.callNew(args.length).locate(loc()).setDebug(true));
}

View File

@ -15,10 +15,7 @@ public class ObjectStatement extends Statement {
public final Map<Object, FunctionStatement> setters;
@Override
public boolean pollutesStack() { return true; }
@Override
public void compile(List<Instruction> target, ScopeRecord scope) {
public void compile(List<Instruction> target, ScopeRecord scope, boolean pollute) {
target.add(Instruction.loadObj().locate(loc()));
for (var el : map.entrySet()) {
@ -26,7 +23,7 @@ public class ObjectStatement extends Statement {
target.add(Instruction.loadValue(el.getKey()).locate(loc()));
var val = el.getValue();
if (val instanceof FunctionStatement) ((FunctionStatement)val).compile(target, scope, el.getKey().toString(), false);
else val.compileWithPollution(target, scope);
else val.compile(target, scope, true);
target.add(Instruction.storeMember().locate(loc()));
}
@ -38,14 +35,16 @@ public class ObjectStatement extends Statement {
if (key instanceof String) target.add(Instruction.loadValue((String)key).locate(loc()));
else target.add(Instruction.loadValue((Double)key).locate(loc()));
if (getters.containsKey(key)) getters.get(key).compileWithPollution(target, scope);
if (getters.containsKey(key)) getters.get(key).compile(target, scope, true);
else target.add(Instruction.loadValue(null).locate(loc()));
if (setters.containsKey(key)) setters.get(key).compileWithPollution(target, scope);
if (setters.containsKey(key)) setters.get(key).compile(target, scope, true);
else target.add(Instruction.loadValue(null).locate(loc()));
target.add(Instruction.defProp().locate(loc()));
}
if (!pollute) target.add(Instruction.discard().locate(loc()));
}
public ObjectStatement(Location loc, Map<Object, Statement> map, Map<Object, FunctionStatement> getters, Map<Object, FunctionStatement> setters) {

View File

@ -16,15 +16,15 @@ public class OperationStatement extends Statement {
public final Operation operation;
@Override
public void compile(List<Instruction> target, ScopeRecord scope) {
public void compile(List<Instruction> target, ScopeRecord scope, boolean pollute) {
for (var arg : args) {
arg.compileWithPollution(target, scope);
arg.compile(target, scope, true);
}
target.add(Instruction.operation(operation).locate(loc()));
if (pollute) target.add(Instruction.operation(operation).locate(loc()));
else target.add(Instruction.discard().locate(loc()));
}
@Override
public boolean pollutesStack() { return true; }
@Override
public boolean pure() {
for (var arg : args) {

View File

@ -10,14 +10,13 @@ import me.topchetoeu.jscript.engine.scope.ScopeRecord;
public class RegexStatement extends Statement {
public final String pattern, flags;
@Override
public boolean pollutesStack() { return true; }
@Override
public boolean pure() { return true; }
@Override
public void compile(List<Instruction> target, ScopeRecord scope) {
public void compile(List<Instruction> target, ScopeRecord scope, boolean pollute) {
target.add(Instruction.loadRegex(pattern, flags).locate(loc()));
if (!pollute) target.add(Instruction.discard().locate(loc()));
}
public RegexStatement(Location loc, String pattern, String flags) {

View File

@ -1,58 +0,0 @@
package me.topchetoeu.jscript.compilation.values;
import java.util.List;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
import me.topchetoeu.jscript.engine.values.Values;
public class TernaryStatement extends Statement {
public final Statement condition;
public final Statement first;
public final Statement second;
@Override
public boolean pollutesStack() { return true; }
@Override
public boolean pure() { return true; }
@Override
public void compile(List<Instruction> target, ScopeRecord scope) {
if (condition instanceof ConstantStatement) {
if (!Values.toBoolean(((ConstantStatement)condition).value)) {
second.compileWithPollution(target, scope);
}
else first.compileWithPollution(target, scope);
return;
}
condition.compileWithPollution(target, scope);
int start = target.size();
target.add(Instruction.nop());
first.compileWithPollution(target, scope);
int mid = target.size();
target.add(Instruction.nop());
second.compileWithPollution(target, scope);
int end = target.size();
target.set(start, Instruction.jmpIfNot(mid - start + 1).locate(loc()));
target.set(mid, Instruction.jmp(end - mid).locate(loc()));
}
@Override
public Statement optimize() {
var cond = condition.optimize();
var f = first.optimize();
var s = second.optimize();
return new TernaryStatement(loc(), cond, f, s);
}
public TernaryStatement(Location loc, Statement condition, Statement first, Statement second) {
super(loc);
this.condition = condition;
this.first = first;
this.second = second;
}
}

View File

@ -5,19 +5,18 @@ import java.util.List;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement;
import me.topchetoeu.jscript.compilation.control.ArrayStatement;
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
import me.topchetoeu.jscript.engine.values.Values;
public class TypeofStatement extends Statement {
public final Statement value;
@Override
public boolean pollutesStack() { return true; }
@Override
public boolean pure() { return true; }
@Override
public void compile(List<Instruction> target, ScopeRecord scope) {
public void compile(List<Instruction> target, ScopeRecord scope, boolean pollute) {
if (value instanceof VariableStatement) {
var i = scope.getKey(((VariableStatement)value).name);
if (i instanceof String) {
@ -25,7 +24,7 @@ public class TypeofStatement extends Statement {
return;
}
}
value.compileWithPollution(target, scope);
value.compile(target, scope, pollute);
target.add(Instruction.typeof().locate(loc()));
}

View File

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

View File

@ -10,14 +10,12 @@ import me.topchetoeu.jscript.engine.scope.ScopeRecord;
public class VariableIndexStatement extends Statement {
public final int index;
@Override
public boolean pollutesStack() { return true; }
@Override
public boolean pure() { return true; }
@Override
public void compile(List<Instruction> target, ScopeRecord scope) {
target.add(Instruction.loadVar(index).locate(loc()));
public void compile(List<Instruction> target, ScopeRecord scope, boolean pollute) {
if (pollute) target.add(Instruction.loadVar(index).locate(loc()));
}
public VariableIndexStatement(Location loc, int i) {

View File

@ -3,7 +3,6 @@ package me.topchetoeu.jscript.compilation.values;
import java.util.List;
import me.topchetoeu.jscript.Location;
import me.topchetoeu.jscript.compilation.AssignStatement;
import me.topchetoeu.jscript.compilation.AssignableStatement;
import me.topchetoeu.jscript.compilation.Instruction;
import me.topchetoeu.jscript.compilation.Statement;
@ -13,20 +12,19 @@ import me.topchetoeu.jscript.engine.scope.ScopeRecord;
public class VariableStatement extends AssignableStatement {
public final String name;
@Override
public boolean pollutesStack() { return true; }
@Override
public boolean pure() { return true; }
@Override
public AssignStatement toAssign(Statement val, Operation operation) {
public Statement toAssign(Statement val, Operation operation) {
return new VariableAssignStatement(loc(), name, val, operation);
}
@Override
public void compile(List<Instruction> target, ScopeRecord scope) {
public void compile(List<Instruction> target, ScopeRecord scope, boolean pollute) {
var i = scope.getKey(name);
target.add(Instruction.loadVar(i).locate(loc()));
if (!pollute) target.add(Instruction.discard().locate(loc()));
}
public VariableStatement(Location loc, String name) {

View File

@ -11,12 +11,9 @@ public class VoidStatement extends Statement {
public final Statement value;
@Override
public boolean pollutesStack() { return true; }
@Override
public void compile(List<Instruction> target, ScopeRecord scope) {
if (value != null) value.compileNoPollution(target, scope);
target.add(Instruction.loadValue(null).locate(loc()));
public void compile(List<Instruction> target, ScopeRecord scope, boolean pollute) {
if (value != null) value.compile(target, scope, false);
if (pollute) target.add(Instruction.loadValue(null).locate(loc()));
}
@Override

View File

@ -9,7 +9,6 @@ import me.topchetoeu.jscript.engine.scope.ValueVariable;
import me.topchetoeu.jscript.engine.values.ArrayValue;
import me.topchetoeu.jscript.engine.values.CodeFunction;
import me.topchetoeu.jscript.engine.values.ObjectValue;
import me.topchetoeu.jscript.engine.values.SignalValue;
import me.topchetoeu.jscript.engine.values.Symbol;
import me.topchetoeu.jscript.engine.values.Values;
import me.topchetoeu.jscript.exceptions.EngineException;
@ -20,9 +19,6 @@ public class Runners {
public static Object execReturn(Context ctx, Instruction instr, CodeFrame frame) {
return frame.pop();
}
public static Object execSignal(Context ctx, Instruction instr, CodeFrame frame) {
return new SignalValue(instr.get(0));
}
public static Object execThrow(Context ctx, Instruction instr, CodeFrame frame) {
throw new EngineException(frame.pop());
}
@ -343,11 +339,9 @@ public class Runners {
}
public static Object exec(Context ctx, Instruction instr, CodeFrame frame) throws InterruptedException {
// System.out.println(instr + "@" + instr.location);
switch (instr.type) {
case NOP: return execNop(ctx, instr, frame);
case RETURN: return execReturn(ctx, instr, frame);
case SIGNAL: return execSignal(ctx, instr, frame);
case THROW: return execThrow(ctx, instr, frame);
case THROW_SYNTAX: return execThrowSyntax(ctx, instr, frame);
case CALL: return execCall(ctx, instr, frame);

View File

@ -1,17 +0,0 @@
package me.topchetoeu.jscript.engine.values;
public final class SignalValue {
public final String data;
public SignalValue(String data) {
this.data = data;
}
public static boolean isSignal(Object signal, String value) {
if (!(signal instanceof SignalValue)) return false;
var val = ((SignalValue)signal).data;
if (value.endsWith("*")) return val.startsWith(value.substring(0, value.length() - 1));
else return val.equals(value);
}
}

View File

@ -84,7 +84,6 @@ public class Values {
obj instanceof String ||
obj instanceof Boolean ||
obj instanceof Symbol ||
obj instanceof SignalValue ||
obj == null ||
obj == NULL;
}
@ -143,7 +142,6 @@ public class Values {
if (val instanceof Boolean) return (Boolean)val ? "true" : "false";
if (val instanceof String) return (String)val;
if (val instanceof Symbol) return ((Symbol)val).toString();
if (val instanceof SignalValue) return "[signal '" + ((SignalValue)val).data + "']";
return "Unknown value";
}

View File

@ -1230,7 +1230,7 @@ public class Parsing {
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<CompoundStatement> parseComma(String filename, List<Token> tokens, int i, Statement prev, int precedence) {
var loc = getLoc(filename, tokens, i);
var n = 0;
@ -1241,9 +1241,9 @@ public class Parsing {
if (!res.isSuccess()) return ParseRes.error(loc, "Expected a value after the comma.", res);
n += res.n;
return ParseRes.res(new CommaStatement(loc, prev, res.result), n);
return ParseRes.res(new CompoundStatement(loc, prev, res.result), n);
}
public static ParseRes<TernaryStatement> parseTernary(String filename, List<Token> tokens, int i, Statement prev, int precedence) {
public static ParseRes<IfStatement> parseTernary(String filename, List<Token> tokens, int i, Statement prev, int precedence) {
var loc = getLoc(filename, tokens, i);
var n = 0;
@ -1260,7 +1260,7 @@ public class Parsing {
if (!b.isSuccess()) return ParseRes.error(loc, "Expected a second value after the ternary operator.", b);
n += b.n;
return ParseRes.res(new TernaryStatement(loc, prev, a.result, b.result), n);
return ParseRes.res(new IfStatement(loc, prev, a.result, b.result), n);
}
public static ParseRes<? extends Statement> parseOperator(String filename, List<Token> tokens, int i, Statement prev, int precedence) {
var loc = getLoc(filename, tokens, i);
@ -1886,7 +1886,7 @@ public class Parsing {
body.declare(target);
try {
body.compile(res, subscope);
body.compile(res, subscope, true);
FunctionStatement.checkBreakAndCont(res, 0);
}
catch (SyntaxException e) {