From b8b000ddf0f14b6e936f7cfa1053b6ec2caee3f1 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Fri, 25 Aug 2023 11:08:57 +0300 Subject: [PATCH] feat: merge all operation instructions in one --- src/me/topchetoeu/jscript/Main.java | 1 + .../jscript/compilation/AssignStatement.java | 19 ++ .../compilation/AssignableStatement.java | 4 +- .../jscript/compilation/Instruction.java | 91 ++++---- .../compilation/control/ForInStatement.java | 11 +- .../compilation/control/SwitchStatement.java | 5 +- .../compilation/values/ArrayStatement.java | 2 +- .../compilation/values/ChangeStatement.java | 8 +- .../compilation/values/FunctionStatement.java | 8 +- .../values/IndexAssignStatement.java | 27 ++- .../compilation/values/IndexStatement.java | 7 +- .../compilation/values/LazyAndStatement.java | 2 +- .../compilation/values/LazyOrStatement.java | 2 +- .../compilation/values/ObjectStatement.java | 2 +- .../values/OperationStatement.java | 5 +- .../compilation/values/TypeofStatement.java | 20 +- .../values/VariableAssignStatement.java | 17 +- .../compilation/values/VariableStatement.java | 5 +- .../topchetoeu/jscript/engine/Operation.java | 42 ++++ .../jscript/engine/frame/CodeFrame.java | 162 +++++-------- .../jscript/engine/frame/Runners.java | 219 +++--------------- .../jscript/engine/values/Values.java | 42 ++++ .../topchetoeu/jscript/parsing/Operator.java | 47 ++-- .../topchetoeu/jscript/parsing/Parsing.java | 39 ++-- .../jscript/polyfills/AsyncFunction.java | 13 +- 25 files changed, 365 insertions(+), 435 deletions(-) create mode 100644 src/me/topchetoeu/jscript/compilation/AssignStatement.java create mode 100644 src/me/topchetoeu/jscript/engine/Operation.java diff --git a/src/me/topchetoeu/jscript/Main.java b/src/me/topchetoeu/jscript/Main.java index 1edc5a7..aa92604 100644 --- a/src/me/topchetoeu/jscript/Main.java +++ b/src/me/topchetoeu/jscript/Main.java @@ -14,6 +14,7 @@ import me.topchetoeu.jscript.events.Observer; import me.topchetoeu.jscript.exceptions.EngineException; import me.topchetoeu.jscript.exceptions.SyntaxException; import me.topchetoeu.jscript.polyfills.PolyfillEngine; +import me.topchetoeu.jscript.polyfills.TypescriptEngine; public class Main { static Thread task; diff --git a/src/me/topchetoeu/jscript/compilation/AssignStatement.java b/src/me/topchetoeu/jscript/compilation/AssignStatement.java new file mode 100644 index 0000000..6bb714f --- /dev/null +++ b/src/me/topchetoeu/jscript/compilation/AssignStatement.java @@ -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 target, ScopeRecord scope, boolean retPrevValue); + + @Override + public void compile(List target, ScopeRecord scope) { + compile(target, scope, false); + } + + protected AssignStatement(Location loc) { + super(loc); + } +} diff --git a/src/me/topchetoeu/jscript/compilation/AssignableStatement.java b/src/me/topchetoeu/jscript/compilation/AssignableStatement.java index eb88387..810c2f8 100644 --- a/src/me/topchetoeu/jscript/compilation/AssignableStatement.java +++ b/src/me/topchetoeu/jscript/compilation/AssignableStatement.java @@ -1,10 +1,10 @@ package me.topchetoeu.jscript.compilation; 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 Statement toAssign(Statement val, Type operation); + public abstract AssignStatement toAssign(Statement val, Operation operation); protected AssignableStatement(Location loc) { super(loc); diff --git a/src/me/topchetoeu/jscript/compilation/Instruction.java b/src/me/topchetoeu/jscript/compilation/Instruction.java index 07a82c1..54d5234 100644 --- a/src/me/topchetoeu/jscript/compilation/Instruction.java +++ b/src/me/topchetoeu/jscript/compilation/Instruction.java @@ -1,6 +1,7 @@ package me.topchetoeu.jscript.compilation; import me.topchetoeu.jscript.Location; +import me.topchetoeu.jscript.engine.Operation; import me.topchetoeu.jscript.exceptions.SyntaxException; public class Instruction { @@ -33,55 +34,58 @@ public class Instruction { LOAD_REGEX, DUP, + MOVE, STORE_VAR, STORE_MEMBER, DISCARD, - + MAKE_VAR, DEF_PROP, KEYS, TYPEOF, - INSTANCEOF(true), - IN(true), + OPERATION; + // TYPEOF, + // INSTANCEOF(true), + // IN(true), - MULTIPLY(true), - DIVIDE(true), - MODULO(true), - ADD(true), - SUBTRACT(true), + // MULTIPLY(true), + // DIVIDE(true), + // MODULO(true), + // ADD(true), + // SUBTRACT(true), - USHIFT_RIGHT(true), - SHIFT_RIGHT(true), - SHIFT_LEFT(true), + // USHIFT_RIGHT(true), + // SHIFT_RIGHT(true), + // SHIFT_LEFT(true), - GREATER(true), - LESS(true), - GREATER_EQUALS(true), - LESS_EQUALS(true), - LOOSE_EQUALS(true), - LOOSE_NOT_EQUALS(true), - EQUALS(true), - NOT_EQUALS(true), + // GREATER(true), + // LESS(true), + // GREATER_EQUALS(true), + // LESS_EQUALS(true), + // LOOSE_EQUALS(true), + // LOOSE_NOT_EQUALS(true), + // EQUALS(true), + // NOT_EQUALS(true), - AND(true), - OR(true), - XOR(true), + // AND(true), + // OR(true), + // XOR(true), - NEG(true), - POS(true), - NOT(true), - INVERSE(true); + // NEG(true), + // POS(true), + // NOT(true), + // INVERSE(true); - final boolean isOperation; + // final boolean isOperation; - private Type(boolean isOperation) { - this.isOperation = isOperation; - } - private Type() { - this(false); - } + // private Type(boolean isOperation) { + // this.isOperation = isOperation; + // } + // private Type() { + // this(false); + // } } public final Type type; @@ -103,6 +107,11 @@ public class Instruction { if (i >= params.length || i < 0) return null; return (T)params[i]; } + @SuppressWarnings("unchecked") + public T get(int i, T defaultVal) { + if (i >= params.length || i < 0) return defaultVal; + return (T)params[i]; + } public boolean match(Object ...args) { if (args.length != params.length) return false; for (int i = 0; i < args.length; i++) { @@ -226,11 +235,14 @@ public class Instruction { public static Instruction loadArr(int count) { return new Instruction(null, Type.LOAD_ARR, count); } - public static Instruction dup(int count) { - return new Instruction(null, Type.DUP, count, 0); + public static Instruction dup() { + return new Instruction(null, Type.DUP, 0, 1); } 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) { @@ -255,7 +267,7 @@ public class Instruction { public static Instruction 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); } @@ -267,9 +279,8 @@ public class Instruction { return new Instruction(null, Type.DEF_PROP); } - public static Instruction operation(Type op) { - if (!op.isOperation) throw new IllegalArgumentException("The instruction type %s is not an operation.".formatted(op)); - return new Instruction(null, op); + public static Instruction operation(Operation op) { + return new Instruction(null, Type.OPERATION, op); } @Override diff --git a/src/me/topchetoeu/jscript/compilation/control/ForInStatement.java b/src/me/topchetoeu/jscript/compilation/control/ForInStatement.java index 3851f9d..5f7538d 100644 --- a/src/me/topchetoeu/jscript/compilation/control/ForInStatement.java +++ b/src/me/topchetoeu/jscript/compilation/control/ForInStatement.java @@ -5,7 +5,7 @@ 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.Instruction.Type; +import me.topchetoeu.jscript.engine.Operation; import me.topchetoeu.jscript.engine.scope.ScopeRecord; public class ForInStatement extends Statement { @@ -37,17 +37,18 @@ public class ForInStatement extends Statement { target.add(Instruction.keys()); int start = target.size(); - target.add(Instruction.dup(1)); + target.add(Instruction.dup()); target.add(Instruction.loadMember("length")); target.add(Instruction.loadValue(0)); - target.add(Instruction.operation(Type.LESS_EQUALS)); + target.add(Instruction.operation(Operation.LESS_EQUALS)); int mid = target.size(); 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.loadValue(1)); - target.add(Instruction.operation(Type.SUBTRACT)); + target.add(Instruction.operation(Operation.SUBTRACT)); target.add(Instruction.dup(1, 2)); target.add(Instruction.loadValue("length")); target.add(Instruction.dup(1, 2)); diff --git a/src/me/topchetoeu/jscript/compilation/control/SwitchStatement.java b/src/me/topchetoeu/jscript/compilation/control/SwitchStatement.java index 25d193c..fd344c7 100644 --- a/src/me/topchetoeu/jscript/compilation/control/SwitchStatement.java +++ b/src/me/topchetoeu/jscript/compilation/control/SwitchStatement.java @@ -7,6 +7,7 @@ import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.compilation.Instruction; 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; public class SwitchStatement extends Statement { @@ -33,9 +34,9 @@ public class SwitchStatement extends Statement { value.compile(target, scope); for (var ccase : cases) { - target.add(Instruction.dup(1).locate(loc())); + target.add(Instruction.dup().locate(loc())); 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); target.add(Instruction.nop()); } diff --git a/src/me/topchetoeu/jscript/compilation/values/ArrayStatement.java b/src/me/topchetoeu/jscript/compilation/values/ArrayStatement.java index 1c6fdad..640ac5c 100644 --- a/src/me/topchetoeu/jscript/compilation/values/ArrayStatement.java +++ b/src/me/topchetoeu/jscript/compilation/values/ArrayStatement.java @@ -21,7 +21,7 @@ public class ArrayStatement extends Statement { var i = 0; for (var el : statements) { if (el != null) { - target.add(Instruction.dup(1).locate(loc())); + target.add(Instruction.dup().locate(loc())); target.add(Instruction.loadValue(i).locate(loc())); el.compileWithPollution(target, scope); target.add(Instruction.storeMember().locate(loc())); diff --git a/src/me/topchetoeu/jscript/compilation/values/ChangeStatement.java b/src/me/topchetoeu/jscript/compilation/values/ChangeStatement.java index bdf4083..3574f95 100644 --- a/src/me/topchetoeu/jscript/compilation/values/ChangeStatement.java +++ b/src/me/topchetoeu/jscript/compilation/values/ChangeStatement.java @@ -6,7 +6,7 @@ import me.topchetoeu.jscript.Location; import me.topchetoeu.jscript.compilation.AssignableStatement; import me.topchetoeu.jscript.compilation.Instruction; 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; public class ChangeStatement extends Statement { @@ -19,11 +19,7 @@ public class ChangeStatement extends Statement { @Override public void compile(List target, ScopeRecord scope) { - value.toAssign(new ConstantStatement(loc(), -addAmount), Type.SUBTRACT).compileWithPollution(target, scope); - if (postfix) { - target.add(Instruction.loadValue(addAmount).locate(loc())); - target.add(Instruction.operation(Type.SUBTRACT).locate(loc())); - } + value.toAssign(new ConstantStatement(loc(), -addAmount), Operation.SUBTRACT).compile(target, scope, postfix); } public ChangeStatement(Location loc, AssignableStatement value, double addAmount, boolean postfix) { diff --git a/src/me/topchetoeu/jscript/compilation/values/FunctionStatement.java b/src/me/topchetoeu/jscript/compilation/values/FunctionStatement.java index 2afe17c..eb52f84 100644 --- a/src/me/topchetoeu/jscript/compilation/values/FunctionStatement.java +++ b/src/me/topchetoeu/jscript/compilation/values/FunctionStatement.java @@ -53,13 +53,11 @@ public class FunctionStatement extends Statement { target.add(Instruction.nop()); subscope.define("this"); - 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++) { + target.add(Instruction.loadVar(argsVar).locate(loc())); target.add(Instruction.loadMember(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) { - 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.storeMember().locate(loc())); diff --git a/src/me/topchetoeu/jscript/compilation/values/IndexAssignStatement.java b/src/me/topchetoeu/jscript/compilation/values/IndexAssignStatement.java index 1a55646..872e804 100644 --- a/src/me/topchetoeu/jscript/compilation/values/IndexAssignStatement.java +++ b/src/me/topchetoeu/jscript/compilation/values/IndexAssignStatement.java @@ -3,45 +3,52 @@ 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.compilation.Instruction.Type; +import me.topchetoeu.jscript.engine.Operation; import me.topchetoeu.jscript.engine.scope.ScopeRecord; -public class IndexAssignStatement extends Statement { +public class IndexAssignStatement extends AssignStatement { public final Statement object; public final Statement index; public final Statement value; - public final Type operation; + public final Operation operation; @Override public boolean pollutesStack() { return true; } @Override - public void compile(List target, ScopeRecord scope) { + public void compile(List target, ScopeRecord scope, boolean retPrevValue) { int start = 0; + if (operation != null) { object.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())); + if (retPrevValue) { + target.add(Instruction.dup().locate(loc())); + target.add(Instruction.move(3, 1).locate(loc())); + } value.compileWithPollution(target, scope); target.add(Instruction.operation(operation).locate(loc())); - target.add(Instruction.storeMember(true).locate(loc())); + target.add(Instruction.storeMember(!retPrevValue).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); - 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); this.object = object; this.index = index; diff --git a/src/me/topchetoeu/jscript/compilation/values/IndexStatement.java b/src/me/topchetoeu/jscript/compilation/values/IndexStatement.java index c1fd5fe..39fe76d 100644 --- a/src/me/topchetoeu/jscript/compilation/values/IndexStatement.java +++ b/src/me/topchetoeu/jscript/compilation/values/IndexStatement.java @@ -3,10 +3,11 @@ 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; -import me.topchetoeu.jscript.compilation.Instruction.Type; +import me.topchetoeu.jscript.engine.Operation; import me.topchetoeu.jscript.engine.scope.ScopeRecord; public class IndexStatement extends AssignableStatement { @@ -19,13 +20,13 @@ public class IndexStatement extends AssignableStatement { public boolean pure() { return true; } @Override - public Statement toAssign(Statement val, Type operation) { + public AssignStatement toAssign(Statement val, Operation operation) { return new IndexAssignStatement(loc(), object, index, val, operation); } public void compile(List target, ScopeRecord scope, boolean dupObj) { int start = 0; 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) { target.add(Instruction.loadMember(((ConstantStatement)index).value).locate(loc())); return; diff --git a/src/me/topchetoeu/jscript/compilation/values/LazyAndStatement.java b/src/me/topchetoeu/jscript/compilation/values/LazyAndStatement.java index 00f55e7..59fb3e1 100644 --- a/src/me/topchetoeu/jscript/compilation/values/LazyAndStatement.java +++ b/src/me/topchetoeu/jscript/compilation/values/LazyAndStatement.java @@ -29,7 +29,7 @@ public class LazyAndStatement extends Statement { } first.compileWithPollution(target, scope); - target.add(Instruction.dup(1).locate(loc())); + target.add(Instruction.dup().locate(loc())); int start = target.size(); target.add(Instruction.nop()); target.add(Instruction.discard().locate(loc())); diff --git a/src/me/topchetoeu/jscript/compilation/values/LazyOrStatement.java b/src/me/topchetoeu/jscript/compilation/values/LazyOrStatement.java index 4cc5dd0..78b6104 100644 --- a/src/me/topchetoeu/jscript/compilation/values/LazyOrStatement.java +++ b/src/me/topchetoeu/jscript/compilation/values/LazyOrStatement.java @@ -29,7 +29,7 @@ public class LazyOrStatement extends Statement { } first.compileWithPollution(target, scope); - target.add(Instruction.dup(1).locate(loc())); + target.add(Instruction.dup().locate(loc())); int start = target.size(); target.add(Instruction.nop()); target.add(Instruction.discard().locate(loc())); diff --git a/src/me/topchetoeu/jscript/compilation/values/ObjectStatement.java b/src/me/topchetoeu/jscript/compilation/values/ObjectStatement.java index 99a0082..2852aca 100644 --- a/src/me/topchetoeu/jscript/compilation/values/ObjectStatement.java +++ b/src/me/topchetoeu/jscript/compilation/values/ObjectStatement.java @@ -20,9 +20,9 @@ public class ObjectStatement extends Statement { @Override public void compile(List target, ScopeRecord scope) { target.add(Instruction.loadObj().locate(loc())); - if (!map.isEmpty()) target.add(Instruction.dup(map.size()).locate(loc())); for (var el : map.entrySet()) { + target.add(Instruction.dup().locate(loc())); 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); diff --git a/src/me/topchetoeu/jscript/compilation/values/OperationStatement.java b/src/me/topchetoeu/jscript/compilation/values/OperationStatement.java index 642e865..114f7b6 100644 --- a/src/me/topchetoeu/jscript/compilation/values/OperationStatement.java +++ b/src/me/topchetoeu/jscript/compilation/values/OperationStatement.java @@ -7,13 +7,14 @@ import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.compilation.Statement; import me.topchetoeu.jscript.compilation.control.ThrowStatement; import me.topchetoeu.jscript.engine.CallContext; +import me.topchetoeu.jscript.engine.Operation; import me.topchetoeu.jscript.engine.scope.ScopeRecord; import me.topchetoeu.jscript.engine.values.Values; import me.topchetoeu.jscript.exceptions.EngineException; public class OperationStatement extends Statement { public final Statement[] args; - public final Instruction.Type operation; + public final Operation operation; @Override public void compile(List 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); this.operation = operation; this.args = args; diff --git a/src/me/topchetoeu/jscript/compilation/values/TypeofStatement.java b/src/me/topchetoeu/jscript/compilation/values/TypeofStatement.java index 6825066..0c069fd 100644 --- a/src/me/topchetoeu/jscript/compilation/values/TypeofStatement.java +++ b/src/me/topchetoeu/jscript/compilation/values/TypeofStatement.java @@ -6,8 +6,7 @@ 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.FunctionValue; -import me.topchetoeu.jscript.engine.values.Symbol; +import me.topchetoeu.jscript.engine.values.Values; public class TypeofStatement extends Statement { public final Statement value; @@ -22,7 +21,7 @@ public class TypeofStatement extends Statement { if (value instanceof VariableStatement) { var i = scope.getKey(((VariableStatement)value).name); if (i instanceof String) { - target.add(Instruction.typeof((String)i)); + target.add(Instruction.typeof((String)i).locate(loc())); return; } } @@ -35,15 +34,14 @@ public class TypeofStatement extends Statement { var val = value.optimize(); if (val instanceof ConstantStatement) { - var cnst = (ConstantStatement)val; - 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"); + return new ConstantStatement(loc(), Values.type(((ConstantStatement)val).value)); } + else if ( + val instanceof ObjectStatement || + val instanceof ArrayStatement || + val instanceof GlobalThisStatement + ) return new ConstantStatement(loc(), "object"); + else if(val instanceof FunctionStatement) return new ConstantStatement(loc(), "function"); return new TypeofStatement(loc(), val); } diff --git a/src/me/topchetoeu/jscript/compilation/values/VariableAssignStatement.java b/src/me/topchetoeu/jscript/compilation/values/VariableAssignStatement.java index 13be56b..5740c11 100644 --- a/src/me/topchetoeu/jscript/compilation/values/VariableAssignStatement.java +++ b/src/me/topchetoeu/jscript/compilation/values/VariableAssignStatement.java @@ -4,36 +4,39 @@ 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.compilation.Instruction.Type; +import me.topchetoeu.jscript.engine.Operation; import me.topchetoeu.jscript.engine.scope.ScopeRecord; -public class VariableAssignStatement extends Statement { +public class VariableAssignStatement extends AssignStatement { public final String name; public final Statement value; - public final Type operation; + public final Operation operation; @Override public boolean pollutesStack() { return true; } @Override - public void compile(List target, ScopeRecord scope) { + public void compile(List target, ScopeRecord scope, boolean retPrevValue) { 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); target.add(Instruction.operation(operation).locate(loc())); + target.add(Instruction.storeVar(i, !retPrevValue).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())); } - - 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); this.name = name; this.value = val; diff --git a/src/me/topchetoeu/jscript/compilation/values/VariableStatement.java b/src/me/topchetoeu/jscript/compilation/values/VariableStatement.java index 1ccff35..b2db5ae 100644 --- a/src/me/topchetoeu/jscript/compilation/values/VariableStatement.java +++ b/src/me/topchetoeu/jscript/compilation/values/VariableStatement.java @@ -3,10 +3,11 @@ 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; -import me.topchetoeu.jscript.compilation.Instruction.Type; +import me.topchetoeu.jscript.engine.Operation; import me.topchetoeu.jscript.engine.scope.ScopeRecord; public class VariableStatement extends AssignableStatement { @@ -18,7 +19,7 @@ public class VariableStatement extends AssignableStatement { public boolean pure() { return true; } @Override - public Statement toAssign(Statement val, Type operation) { + public AssignStatement toAssign(Statement val, Operation operation) { return new VariableAssignStatement(loc(), name, val, operation); } diff --git a/src/me/topchetoeu/jscript/engine/Operation.java b/src/me/topchetoeu/jscript/engine/Operation.java new file mode 100644 index 0000000..9a1e37a --- /dev/null +++ b/src/me/topchetoeu/jscript/engine/Operation.java @@ -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; + } +} diff --git a/src/me/topchetoeu/jscript/engine/frame/CodeFrame.java b/src/me/topchetoeu/jscript/engine/frame/CodeFrame.java index 8548535..4dc0580 100644 --- a/src/me/topchetoeu/jscript/engine/frame/CodeFrame.java +++ b/src/me/topchetoeu/jscript/engine/frame/CodeFrame.java @@ -4,8 +4,6 @@ import java.util.ArrayList; import java.util.List; 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.DebugCommand; import me.topchetoeu.jscript.engine.Engine; @@ -17,6 +15,8 @@ import me.topchetoeu.jscript.engine.values.Values; import me.topchetoeu.jscript.exceptions.EngineException; public class CodeFrame { + private record TryCtx(int tryStart, int tryEnd, int catchStart, int catchEnd, int finallyStart, int finallyEnd) { } + public static final DataKey STACK_N_KEY = new DataKey<>(); public static final DataKey MAX_STACK_KEY = new DataKey<>(); public static final DataKey STOP_AT_START_KEY = new DataKey<>(); @@ -25,10 +25,11 @@ public class CodeFrame { public final LocalScope scope; public final Object thisArg; public final Object[] args; - public final List stack = new ArrayList<>(); - public final List tryCtxs = new ArrayList<>(); + public final List tryStack = new ArrayList<>(); public final CodeFunction function; + public Object[] stack = new Object[32]; + public int stackPtr = 0; public int codePtr = 0; private DebugCommand debugCmd = null; private Location prevLoc = null; @@ -37,41 +38,50 @@ public class CodeFrame { return peek(0); } public Object peek(int offset) { - if (stack.size() <= offset) return null; - else return stack.get(stack.size() - 1 - offset); + if (stackPtr <= offset) return null; + else return stack[stackPtr - 1 - offset]; } public Object pop() { - if (stack.size() == 0) return null; - else return stack.remove(stack.size() - 1); + if (stackPtr == 0) return null; + 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) { - 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) { - stack.clear(); - codePtr = 0; - debugCmd = null; + public void start(CallContext ctx) { + if (ctx.getData(STACK_N_KEY, 0) >= ctx.addData(MAX_STACK_KEY, 10000)) throw EngineException.ofRange("Stack overflow!"); + ctx.changeData(STACK_N_KEY); + + 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); if (debugState != null) debugState.popFrame(); ctx.changeData(STACK_N_KEY, -1); } - public Object next(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); - } - + private Object nextNoTry(CallContext ctx) throws InterruptedException { if (Thread.currentThread().isInterrupted()) throw new InterruptedException(); if (codePtr < 0 || codePtr >= function.body.length) return null; @@ -80,20 +90,28 @@ public class CodeFrame { var loc = instr.location; if (loc != null) prevLoc = loc; - 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); + // var debugState = ctx.getData(Engine.DEBUG_STATE_KEY); + // if (debugCmd == null) { + // if (ctx.getData(STOP_AT_START_KEY, false)) debugCmd = DebugCommand.STEP_OVER; + // else debugCmd = DebugCommand.NORMAL; - debugState.breakpointNotifier.next(new BreakpointData(loc, ctx)); - debugCmd = debugState.commandNotifier.toAwaitable().await(); - if (debugCmd == DebugCommand.NORMAL) ctx.setData(STEPPING_TROUGH_KEY, false); - } - } + // if (debugState != null) debugState.pushFrame(this); + // } + + // 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 { 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 { try { + start(ctx); while (true) { var res = next(ctx); if (res != Runners.NO_RETURN) return res; } } 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) { diff --git a/src/me/topchetoeu/jscript/engine/frame/Runners.java b/src/me/topchetoeu/jscript/engine/frame/Runners.java index 97f7818..e4da6f8 100644 --- a/src/me/topchetoeu/jscript/engine/frame/Runners.java +++ b/src/me/topchetoeu/jscript/engine/frame/Runners.java @@ -5,6 +5,7 @@ import java.util.Collections; import me.topchetoeu.jscript.compilation.Instruction; import me.topchetoeu.jscript.engine.CallContext; import me.topchetoeu.jscript.engine.DebugCommand; +import me.topchetoeu.jscript.engine.Operation; import me.topchetoeu.jscript.engine.scope.ValueVariable; import me.topchetoeu.jscript.engine.values.ArrayValue; import me.topchetoeu.jscript.engine.values.CodeFunction; @@ -38,10 +39,7 @@ public class Runners { } public static Object execCall(DebugCommand state, Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException { - int n = instr.get(0); - - var callArgs = new Object[n]; - for (var i = n - 1; i >= 0; i--) callArgs[i] = frame.pop(); + var callArgs = frame.take(instr.get(0)); var func = frame.pop(); var thisArg = frame.pop(); @@ -51,10 +49,7 @@ public class Runners { return NO_RETURN; } public static Object execCallNew(DebugCommand state, Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException { - int n = instr.get(0); - - var callArgs = new Object[n]; - for (var i = n - 1; i >= 0; i--) callArgs[i] = frame.pop(); + var callArgs = frame.take(instr.get(0)); var funcObj = frame.pop(); if (Values.isFunction(funcObj) && Values.function(funcObj).special) { @@ -159,7 +154,7 @@ public class Runners { exception = null; try { 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; } catch (EngineException e) { @@ -194,10 +189,24 @@ public class Runners { } public static Object execDup(Instruction instr, CodeFrame frame, CallContext ctx) { - var val = frame.peek(instr.get(1)); - for (int i = 0; i < (int)instr.get(0); i++) { - frame.push(val); + int offset = instr.get(0), count = instr.get(1); + + 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++; return NO_RETURN; } @@ -376,157 +385,13 @@ public class Runners { return NO_RETURN; } - public static Object execAdd(Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException { - Object b = frame.pop(), a = frame.pop(); - frame.push(Values.add(ctx, a, b)); - 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 execOperation(Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException { + Operation op = instr.get(0); + var args = new Object[op.operands]; - public static Object execAnd(Instruction instr, CodeFrame frame, CallContext ctx) throws InterruptedException { - Object b = frame.pop(), a = frame.pop(); + for (var i = op.operands - 1; i >= 0; i--) args[i] = frame.pop(); - frame.push(Values.and(ctx, a, b)); - 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.push(Values.operation(ctx, op, args)); frame.codePtr++; return NO_RETURN; } @@ -544,6 +409,7 @@ public class Runners { case TRY: return execTry(state, 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_VAR: return execLoadVar(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 MAKE_VAR: return execMakeVar(instr, frame, ctx); - case IN: return execIn(instr, frame, ctx); case KEYS: return execKeys(instr, frame, ctx); case DEF_PROP: return execDefProp(instr, frame, ctx); case TYPEOF: return execTypeof(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_IF: return execJmpIf(instr, frame, ctx); case JMP_IFN: return execJmpIfNot(instr, frame, ctx); - case ADD: return execAdd(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); + case OPERATION: return execOperation(instr, frame, ctx); default: throw EngineException.ofSyntax("Invalid instruction " + instr.type.name() + "."); } diff --git a/src/me/topchetoeu/jscript/engine/values/Values.java b/src/me/topchetoeu/jscript/engine/values/Values.java index a5b6851..efd2488 100644 --- a/src/me/topchetoeu/jscript/engine/values/Values.java +++ b/src/me/topchetoeu/jscript/engine/values/Values.java @@ -11,6 +11,7 @@ import java.util.List; import java.util.Map; import me.topchetoeu.jscript.engine.CallContext; +import me.topchetoeu.jscript.engine.Operation; import me.topchetoeu.jscript.engine.frame.ConvertHint; import me.topchetoeu.jscript.exceptions.EngineException; @@ -218,6 +219,47 @@ public class Values { 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 { obj = normalize(obj); key = normalize(key); if (obj == null) throw new IllegalArgumentException("Tried to access member of undefined."); diff --git a/src/me/topchetoeu/jscript/parsing/Operator.java b/src/me/topchetoeu/jscript/parsing/Operator.java index 5c2b939..7037516 100644 --- a/src/me/topchetoeu/jscript/parsing/Operator.java +++ b/src/me/topchetoeu/jscript/parsing/Operator.java @@ -3,29 +3,28 @@ package me.topchetoeu.jscript.parsing; import java.util.HashMap; import java.util.Map; -import me.topchetoeu.jscript.compilation.Instruction; -import me.topchetoeu.jscript.compilation.Instruction.Type; +import me.topchetoeu.jscript.engine.Operation; public enum Operator { - MULTIPLY("*", Type.MULTIPLY, 13), - DIVIDE("/", Type.DIVIDE, 12), - MODULO("%", Type.MODULO, 12), - SUBTRACT("-", Type.SUBTRACT, 11), - ADD("+", Type.ADD, 11), - SHIFT_RIGHT(">>", Type.SHIFT_RIGHT, 10), - SHIFT_LEFT("<<", Type.SHIFT_LEFT, 10), - USHIFT_RIGHT(">>>", Type.USHIFT_RIGHT, 10), - GREATER(">", Type.GREATER, 9), - LESS("<", Type.LESS, 9), - GREATER_EQUALS(">=", Type.GREATER_EQUALS, 9), - LESS_EQUALS("<=", Type.LESS_EQUALS, 9), - NOT_EQUALS("!=", Type.LOOSE_NOT_EQUALS, 8), - LOOSE_NOT_EQUALS("!==", Type.NOT_EQUALS, 8), - EQUALS("==", Type.LOOSE_EQUALS, 8), - LOOSE_EQUALS("===", Type.EQUALS, 8), - AND("&", Type.AND, 7), - XOR("^", Type.XOR, 6), - OR("|", Type.OR, 5), + MULTIPLY("*", Operation.MULTIPLY, 13), + DIVIDE("/", Operation.DIVIDE, 12), + MODULO("%", Operation.MODULO, 12), + SUBTRACT("-", Operation.SUBTRACT, 11), + ADD("+", Operation.ADD, 11), + SHIFT_RIGHT(">>", Operation.SHIFT_RIGHT, 10), + SHIFT_LEFT("<<", Operation.SHIFT_LEFT, 10), + USHIFT_RIGHT(">>>", Operation.USHIFT_RIGHT, 10), + GREATER(">", Operation.GREATER, 9), + LESS("<", Operation.LESS, 9), + GREATER_EQUALS(">=", Operation.GREATER_EQUALS, 9), + LESS_EQUALS("<=", Operation.LESS_EQUALS, 9), + NOT_EQUALS("!=", Operation.LOOSE_NOT_EQUALS, 8), + LOOSE_NOT_EQUALS("!==", Operation.NOT_EQUALS, 8), + EQUALS("==", Operation.LOOSE_EQUALS, 8), + LOOSE_EQUALS("===", Operation.EQUALS, 8), + AND("&", Operation.AND, 7), + XOR("^", Operation.XOR, 6), + OR("|", Operation.OR, 5), LAZY_AND("&&", 4), LAZY_OR("||", 3), ASSIGN_SHIFT_LEFT("<<=", 2, true), @@ -57,7 +56,7 @@ public enum Operator { DECREASE("--"); public final String value; - public final Instruction.Type operation; + public final Operation operation; public final int precedence; public final boolean reverse; private static final Map ops = new HashMap<>(); @@ -99,13 +98,13 @@ public enum Operator { this.reverse = reverse; } - private Operator(String value, Instruction.Type funcName, int precedence) { + private Operator(String value, Operation funcName, int precedence) { this. value = value; this.operation = funcName; this.precedence = precedence; 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.operation = funcName; this.precedence = precedence; diff --git a/src/me/topchetoeu/jscript/parsing/Parsing.java b/src/me/topchetoeu/jscript/parsing/Parsing.java index c817931..27804db 100644 --- a/src/me/topchetoeu/jscript/parsing/Parsing.java +++ b/src/me/topchetoeu/jscript/parsing/Parsing.java @@ -13,6 +13,7 @@ import me.topchetoeu.jscript.compilation.VariableDeclareStatement.Pair; import me.topchetoeu.jscript.compilation.control.*; import me.topchetoeu.jscript.compilation.control.SwitchStatement.SwitchCase; 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.ValueVariable; import me.topchetoeu.jscript.engine.values.CodeFunction; @@ -938,12 +939,12 @@ public class Parsing { if (!opState.isSuccess()) return ParseRes.failed(); var op = opState.result; - Type operation = null; + Operation operation = null; - if (op == Operator.ADD) operation = Type.POS; - else if (op == Operator.SUBTRACT) operation = Type.NEG; - else if (op == Operator.INVERSE) operation = Type.INVERSE; - else if (op == Operator.NOT) operation = Type.NOT; + if (op == Operator.ADD) operation = Operation.POS; + else if (op == Operator.SUBTRACT) operation = Operation.NEG; + else if (op == Operator.INVERSE) operation = Operation.INVERSE; + else if (op == Operator.NOT) operation = Operation.NOT; else return ParseRes.failed(); 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); n += res.n; - Type operation = null; + Operation operation = null; - if (op == Operator.ASSIGN_ADD) operation = Type.ADD; - if (op == Operator.ASSIGN_SUBTRACT) operation = Type.SUBTRACT; - if (op == Operator.ASSIGN_MULTIPLY) operation = Type.MULTIPLY; - if (op == Operator.ASSIGN_DIVIDE) operation = Type.DIVIDE; - if (op == Operator.ASSIGN_MODULO) operation = Type.MODULO; - if (op == Operator.ASSIGN_OR) operation = Type.OR; - if (op == Operator.ASSIGN_XOR) operation = Type.XOR; - if (op == Operator.ASSIGN_AND) operation = Type.AND; - if (op == Operator.ASSIGN_SHIFT_LEFT) operation = Type.SHIFT_LEFT; - if (op == Operator.ASSIGN_SHIFT_RIGHT) operation = Type.SHIFT_RIGHT; - if (op == Operator.ASSIGN_USHIFT_RIGHT) operation = Type.USHIFT_RIGHT; + if (op == Operator.ASSIGN_ADD) operation = Operation.ADD; + if (op == Operator.ASSIGN_SUBTRACT) operation = Operation.SUBTRACT; + if (op == Operator.ASSIGN_MULTIPLY) operation = Operation.MULTIPLY; + if (op == Operator.ASSIGN_DIVIDE) operation = Operation.DIVIDE; + if (op == Operator.ASSIGN_MODULO) operation = Operation.MODULO; + if (op == Operator.ASSIGN_OR) operation = Operation.OR; + if (op == Operator.ASSIGN_XOR) operation = Operation.XOR; + if (op == Operator.ASSIGN_AND) operation = Operation.AND; + if (op == Operator.ASSIGN_SHIFT_LEFT) operation = Operation.SHIFT_LEFT; + if (op == Operator.ASSIGN_SHIFT_RIGHT) operation = Operation.SHIFT_RIGHT; + if (op == Operator.ASSIGN_USHIFT_RIGHT) operation = Operation.USHIFT_RIGHT; 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); 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 parseIn(String filename, List tokens, int i, Statement prev, int precedence) { 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); 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 parseComma(String filename, List tokens, int i, Statement prev, int precedence) { var loc = getLoc(filename, tokens, i); diff --git a/src/me/topchetoeu/jscript/polyfills/AsyncFunction.java b/src/me/topchetoeu/jscript/polyfills/AsyncFunction.java index 32c4ab3..b3bcf1f 100644 --- a/src/me/topchetoeu/jscript/polyfills/AsyncFunction.java +++ b/src/me/topchetoeu/jscript/polyfills/AsyncFunction.java @@ -28,6 +28,8 @@ public class AsyncFunction extends FunctionValue { public Object fulfill(CallContext ctx, Object thisArg, Object[] args) throws InterruptedException { if (args.length == 1) frame.push(args[0]); + frame.start(ctx); + while (true) { awaiting = false; awaited = null; @@ -36,12 +38,12 @@ public class AsyncFunction extends FunctionValue { var res = frame.next(ctx); if (res != Runners.NO_RETURN) { promise.fulfill(ctx, res); - return null; + break; } } catch (EngineException e) { promise.reject(e); - return null; + break; } if (!awaiting) continue; @@ -54,15 +56,18 @@ public class AsyncFunction extends FunctionValue { var res = Values.getMember(ctx, awaited, "then"); if (res instanceof FunctionValue) { Values.function(res).call(ctx, awaited, fulfillFunc, rejectFunc); - return null; + break; } else frame.push(awaited); } catch (EngineException e) { promise.reject(e); - return null; + break; } } + + frame.end(ctx); + return null; } public Object await(CallContext ctx, Object thisArg, Object[] args) {