Module support #11
@ -1,52 +1,52 @@
|
|||||||
package me.topchetoeu.jscript.compilation;
|
package me.topchetoeu.jscript.compilation;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.compilation.values.FunctionStatement;
|
import me.topchetoeu.jscript.compilation.values.FunctionStatement;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class VariableDeclareStatement extends Statement {
|
public class VariableDeclareStatement extends Statement {
|
||||||
public static class Pair {
|
public static class Pair {
|
||||||
public final String name;
|
public final String name;
|
||||||
public final Statement value;
|
public final Statement value;
|
||||||
public final Location location;
|
public final Location location;
|
||||||
|
|
||||||
public Pair(String name, Statement value, Location location) {
|
public Pair(String name, Statement value, Location location) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.value = value;
|
this.value = value;
|
||||||
this.location = location;
|
this.location = location;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final List<Pair> values;
|
public final List<Pair> values;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void declare(ScopeRecord varsScope) {
|
public void declare(ScopeRecord varsScope) {
|
||||||
for (var key : values) {
|
for (var key : values) {
|
||||||
varsScope.define(key.name);
|
varsScope.define(key.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
for (var entry : values) {
|
for (var entry : values) {
|
||||||
if (entry.name == null) continue;
|
if (entry.name == null) continue;
|
||||||
var key = scope.getKey(entry.name);
|
var key = scope.getKey(entry.name);
|
||||||
|
|
||||||
if (key instanceof String) target.add(Instruction.makeVar(entry.location, (String)key));
|
if (key instanceof String) target.add(Instruction.makeVar(entry.location, (String)key));
|
||||||
|
|
||||||
if (entry.value != null) {
|
if (entry.value != null) {
|
||||||
FunctionStatement.compileWithName(entry.value, target, scope, true, entry.name, BreakpointType.STEP_OVER);
|
FunctionStatement.compileWithName(entry.value, target, scope, true, entry.name, BreakpointType.STEP_OVER);
|
||||||
target.add(Instruction.storeVar(entry.location, key));
|
target.add(Instruction.storeVar(entry.location, key));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
||||||
}
|
}
|
||||||
|
|
||||||
public VariableDeclareStatement(Location loc, List<Pair> values) {
|
public VariableDeclareStatement(Location loc, List<Pair> values) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.values = values;
|
this.values = values;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,22 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
package me.topchetoeu.jscript.compilation.control;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class BreakStatement extends Statement {
|
public class BreakStatement extends Statement {
|
||||||
public final String label;
|
public final String label;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
target.add(Instruction.nop(loc(), "break", label));
|
target.add(Instruction.nop(loc(), "break", label));
|
||||||
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
||||||
}
|
}
|
||||||
|
|
||||||
public BreakStatement(Location loc, String label) {
|
public BreakStatement(Location loc, String label) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.label = label;
|
this.label = label;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,22 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
package me.topchetoeu.jscript.compilation.control;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class ContinueStatement extends Statement {
|
public class ContinueStatement extends Statement {
|
||||||
public final String label;
|
public final String label;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
target.add(Instruction.nop(loc(), "cont", label));
|
target.add(Instruction.nop(loc(), "cont", label));
|
||||||
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ContinueStatement(Location loc, String label) {
|
public ContinueStatement(Location loc, String label) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.label = label;
|
this.label = label;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
package me.topchetoeu.jscript.compilation.control;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class DebugStatement extends Statement {
|
public class DebugStatement extends Statement {
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
target.add(Instruction.debug(loc()));
|
target.add(Instruction.debug(loc()));
|
||||||
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
||||||
}
|
}
|
||||||
|
|
||||||
public DebugStatement(Location loc) {
|
public DebugStatement(Location loc) {
|
||||||
super(loc);
|
super(loc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,27 +1,27 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
package me.topchetoeu.jscript.compilation.control;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class DeleteStatement extends Statement {
|
public class DeleteStatement extends Statement {
|
||||||
public final Statement key;
|
public final Statement key;
|
||||||
public final Statement value;
|
public final Statement value;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
value.compile(target, scope, true);
|
value.compile(target, scope, true);
|
||||||
key.compile(target, scope, true);
|
key.compile(target, scope, true);
|
||||||
|
|
||||||
target.add(Instruction.delete(loc()));
|
target.add(Instruction.delete(loc()));
|
||||||
if (pollute) target.add(Instruction.loadValue(loc(), true));
|
if (pollute) target.add(Instruction.loadValue(loc(), true));
|
||||||
}
|
}
|
||||||
|
|
||||||
public DeleteStatement(Location loc, Statement key, Statement value) {
|
public DeleteStatement(Location loc, Statement key, Statement value) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.key = key;
|
this.key = key;
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,37 +1,37 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
package me.topchetoeu.jscript.compilation.control;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class DoWhileStatement extends Statement {
|
public class DoWhileStatement extends Statement {
|
||||||
public final Statement condition, body;
|
public final Statement condition, body;
|
||||||
public final String label;
|
public final String label;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void declare(ScopeRecord globScope) {
|
public void declare(ScopeRecord globScope) {
|
||||||
body.declare(globScope);
|
body.declare(globScope);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
int start = target.size();
|
int start = target.size();
|
||||||
body.compile(target, scope, false, BreakpointType.STEP_OVER);
|
body.compile(target, scope, false, BreakpointType.STEP_OVER);
|
||||||
int mid = target.size();
|
int mid = target.size();
|
||||||
condition.compile(target, scope, true, BreakpointType.STEP_OVER);
|
condition.compile(target, scope, true, BreakpointType.STEP_OVER);
|
||||||
int end = target.size();
|
int end = target.size();
|
||||||
|
|
||||||
WhileStatement.replaceBreaks(target, label, start, mid - 1, mid, end + 1);
|
WhileStatement.replaceBreaks(target, label, start, mid - 1, mid, end + 1);
|
||||||
target.add(Instruction.jmpIf(loc(), start - end));
|
target.add(Instruction.jmpIf(loc(), start - end));
|
||||||
}
|
}
|
||||||
|
|
||||||
public DoWhileStatement(Location loc, String label, Statement condition, Statement body) {
|
public DoWhileStatement(Location loc, String label, Statement condition, Statement body) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.label = label;
|
this.label = label;
|
||||||
this.condition = condition;
|
this.condition = condition;
|
||||||
this.body = body;
|
this.body = body;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,73 +1,73 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
package me.topchetoeu.jscript.compilation.control;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.engine.Operation;
|
import me.topchetoeu.jscript.engine.Operation;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class ForInStatement extends Statement {
|
public class ForInStatement extends Statement {
|
||||||
public final String varName;
|
public final String varName;
|
||||||
public final boolean isDeclaration;
|
public final boolean isDeclaration;
|
||||||
public final Statement varValue, object, body;
|
public final Statement varValue, object, body;
|
||||||
public final String label;
|
public final String label;
|
||||||
public final Location varLocation;
|
public final Location varLocation;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void declare(ScopeRecord globScope) {
|
public void declare(ScopeRecord globScope) {
|
||||||
body.declare(globScope);
|
body.declare(globScope);
|
||||||
if (isDeclaration) globScope.define(varName);
|
if (isDeclaration) globScope.define(varName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
var key = scope.getKey(varName);
|
var key = scope.getKey(varName);
|
||||||
|
|
||||||
int first = target.size();
|
int first = target.size();
|
||||||
if (key instanceof String) target.add(Instruction.makeVar(loc(), (String)key));
|
if (key instanceof String) target.add(Instruction.makeVar(loc(), (String)key));
|
||||||
|
|
||||||
if (varValue != null) {
|
if (varValue != null) {
|
||||||
varValue.compile(target, scope, true);
|
varValue.compile(target, scope, true);
|
||||||
target.add(Instruction.storeVar(loc(), scope.getKey(varName)));
|
target.add(Instruction.storeVar(loc(), scope.getKey(varName)));
|
||||||
}
|
}
|
||||||
|
|
||||||
object.compile(target, scope, true, BreakpointType.STEP_OVER);
|
object.compile(target, scope, true, BreakpointType.STEP_OVER);
|
||||||
target.add(Instruction.keys(loc(), true));
|
target.add(Instruction.keys(loc(), true));
|
||||||
|
|
||||||
int start = target.size();
|
int start = target.size();
|
||||||
target.add(Instruction.dup(loc()));
|
target.add(Instruction.dup(loc()));
|
||||||
target.add(Instruction.loadValue(loc(), null));
|
target.add(Instruction.loadValue(loc(), null));
|
||||||
target.add(Instruction.operation(loc(), Operation.EQUALS));
|
target.add(Instruction.operation(loc(), Operation.EQUALS));
|
||||||
int mid = target.size();
|
int mid = target.size();
|
||||||
target.add(Instruction.nop(loc()));
|
target.add(Instruction.nop(loc()));
|
||||||
|
|
||||||
target.add(Instruction.loadMember(varLocation, "value"));
|
target.add(Instruction.loadMember(varLocation, "value"));
|
||||||
target.add(Instruction.storeVar(object.loc(), key));
|
target.add(Instruction.storeVar(object.loc(), key));
|
||||||
target.setDebug(BreakpointType.STEP_OVER);
|
target.setDebug(BreakpointType.STEP_OVER);
|
||||||
|
|
||||||
body.compile(target, scope, false, BreakpointType.STEP_OVER);
|
body.compile(target, scope, false, BreakpointType.STEP_OVER);
|
||||||
|
|
||||||
int end = target.size();
|
int end = target.size();
|
||||||
|
|
||||||
WhileStatement.replaceBreaks(target, label, mid + 1, end, start, end + 1);
|
WhileStatement.replaceBreaks(target, label, mid + 1, end, start, end + 1);
|
||||||
|
|
||||||
target.add(Instruction.jmp(loc(), start - end));
|
target.add(Instruction.jmp(loc(), start - end));
|
||||||
target.add(Instruction.discard(loc()));
|
target.add(Instruction.discard(loc()));
|
||||||
target.set(mid, Instruction.jmpIf(loc(), end - mid + 1));
|
target.set(mid, Instruction.jmpIf(loc(), end - mid + 1));
|
||||||
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
||||||
target.get(first).locate(loc());
|
target.get(first).locate(loc());
|
||||||
}
|
}
|
||||||
|
|
||||||
public ForInStatement(Location loc, Location varLocation, String label, boolean isDecl, String varName, Statement varValue, Statement object, Statement body) {
|
public ForInStatement(Location loc, Location varLocation, String label, boolean isDecl, String varName, Statement varValue, Statement object, Statement body) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.varLocation = varLocation;
|
this.varLocation = varLocation;
|
||||||
this.label = label;
|
this.label = label;
|
||||||
this.isDeclaration = isDecl;
|
this.isDeclaration = isDecl;
|
||||||
this.varName = varName;
|
this.varName = varName;
|
||||||
this.varValue = varValue;
|
this.varValue = varValue;
|
||||||
this.object = object;
|
this.object = object;
|
||||||
this.body = body;
|
this.body = body;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,47 +1,47 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
package me.topchetoeu.jscript.compilation.control;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class ForStatement extends Statement {
|
public class ForStatement extends Statement {
|
||||||
public final Statement declaration, assignment, condition, body;
|
public final Statement declaration, assignment, condition, body;
|
||||||
public final String label;
|
public final String label;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void declare(ScopeRecord globScope) {
|
public void declare(ScopeRecord globScope) {
|
||||||
declaration.declare(globScope);
|
declaration.declare(globScope);
|
||||||
body.declare(globScope);
|
body.declare(globScope);
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
declaration.compile(target, scope, false, BreakpointType.STEP_OVER);
|
declaration.compile(target, scope, false, BreakpointType.STEP_OVER);
|
||||||
|
|
||||||
int start = target.size();
|
int start = target.size();
|
||||||
condition.compile(target, scope, true, BreakpointType.STEP_OVER);
|
condition.compile(target, scope, true, BreakpointType.STEP_OVER);
|
||||||
int mid = target.size();
|
int mid = target.size();
|
||||||
target.add(Instruction.nop(null));
|
target.add(Instruction.nop(null));
|
||||||
body.compile(target, scope, false, BreakpointType.STEP_OVER);
|
body.compile(target, scope, false, BreakpointType.STEP_OVER);
|
||||||
int beforeAssign = target.size();
|
int beforeAssign = target.size();
|
||||||
assignment.compile(target, scope, false, BreakpointType.STEP_OVER);
|
assignment.compile(target, scope, false, BreakpointType.STEP_OVER);
|
||||||
int end = target.size();
|
int end = target.size();
|
||||||
|
|
||||||
WhileStatement.replaceBreaks(target, label, mid + 1, end, beforeAssign, end + 1);
|
WhileStatement.replaceBreaks(target, label, mid + 1, end, beforeAssign, end + 1);
|
||||||
|
|
||||||
target.add(Instruction.jmp(loc(), start - end));
|
target.add(Instruction.jmp(loc(), start - end));
|
||||||
target.set(mid, Instruction.jmpIfNot(loc(), end - mid + 1));
|
target.set(mid, Instruction.jmpIfNot(loc(), end - mid + 1));
|
||||||
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ForStatement(Location loc, String label, Statement declaration, Statement condition, Statement assignment, Statement body) {
|
public ForStatement(Location loc, String label, Statement declaration, Statement condition, Statement assignment, Statement body) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.label = label;
|
this.label = label;
|
||||||
this.declaration = declaration;
|
this.declaration = declaration;
|
||||||
this.condition = condition;
|
this.condition = condition;
|
||||||
this.assignment = assignment;
|
this.assignment = assignment;
|
||||||
this.body = body;
|
this.body = body;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,52 +1,52 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
package me.topchetoeu.jscript.compilation.control;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class IfStatement extends Statement {
|
public class IfStatement extends Statement {
|
||||||
public final Statement condition, body, elseBody;
|
public final Statement condition, body, elseBody;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void declare(ScopeRecord globScope) {
|
public void declare(ScopeRecord globScope) {
|
||||||
body.declare(globScope);
|
body.declare(globScope);
|
||||||
if (elseBody != null) elseBody.declare(globScope);
|
if (elseBody != null) elseBody.declare(globScope);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, BreakpointType breakpoint) {
|
@Override public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, BreakpointType breakpoint) {
|
||||||
condition.compile(target, scope, true, breakpoint);
|
condition.compile(target, scope, true, breakpoint);
|
||||||
|
|
||||||
if (elseBody == null) {
|
if (elseBody == null) {
|
||||||
int i = target.size();
|
int i = target.size();
|
||||||
target.add(Instruction.nop(null));
|
target.add(Instruction.nop(null));
|
||||||
body.compile(target, scope, pollute, breakpoint);
|
body.compile(target, scope, pollute, breakpoint);
|
||||||
int endI = target.size();
|
int endI = target.size();
|
||||||
target.set(i, Instruction.jmpIfNot(loc(), endI - i));
|
target.set(i, Instruction.jmpIfNot(loc(), endI - i));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
int start = target.size();
|
int start = target.size();
|
||||||
target.add(Instruction.nop(null));
|
target.add(Instruction.nop(null));
|
||||||
body.compile(target, scope, pollute, breakpoint);
|
body.compile(target, scope, pollute, breakpoint);
|
||||||
target.add(Instruction.nop(null));
|
target.add(Instruction.nop(null));
|
||||||
int mid = target.size();
|
int mid = target.size();
|
||||||
elseBody.compile(target, scope, pollute, breakpoint);
|
elseBody.compile(target, scope, pollute, breakpoint);
|
||||||
int end = target.size();
|
int end = target.size();
|
||||||
|
|
||||||
target.set(start, Instruction.jmpIfNot(loc(), mid - start));
|
target.set(start, Instruction.jmpIfNot(loc(), mid - start));
|
||||||
target.set(mid - 1, Instruction.jmp(loc(), end - mid + 1));
|
target.set(mid - 1, Instruction.jmp(loc(), end - mid + 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@Override public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
@Override public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
compile(target, scope, pollute, BreakpointType.STEP_IN);
|
compile(target, scope, pollute, BreakpointType.STEP_IN);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IfStatement(Location loc, Statement condition, Statement body, Statement elseBody) {
|
public IfStatement(Location loc, Statement condition, Statement body, Statement elseBody) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.condition = condition;
|
this.condition = condition;
|
||||||
this.body = body;
|
this.body = body;
|
||||||
this.elseBody = elseBody;
|
this.elseBody = elseBody;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,23 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
package me.topchetoeu.jscript.compilation.control;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class ReturnStatement extends Statement {
|
public class ReturnStatement extends Statement {
|
||||||
public final Statement value;
|
public final Statement value;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
if (value == null) target.add(Instruction.loadValue(loc(), null));
|
if (value == null) target.add(Instruction.loadValue(loc(), null));
|
||||||
else value.compile(target, scope, true);
|
else value.compile(target, scope, true);
|
||||||
target.add(Instruction.ret(loc()));
|
target.add(Instruction.ret(loc()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReturnStatement(Location loc, Statement value) {
|
public ReturnStatement(Location loc, Statement value) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,87 +1,87 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
package me.topchetoeu.jscript.compilation.control;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction.Type;
|
import me.topchetoeu.jscript.compilation.Instruction.Type;
|
||||||
import me.topchetoeu.jscript.engine.Operation;
|
import me.topchetoeu.jscript.engine.Operation;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class SwitchStatement extends Statement {
|
public class SwitchStatement extends Statement {
|
||||||
public static class SwitchCase {
|
public static class SwitchCase {
|
||||||
public final Statement value;
|
public final Statement value;
|
||||||
public final int statementI;
|
public final int statementI;
|
||||||
|
|
||||||
public SwitchCase(Statement value, int statementI) {
|
public SwitchCase(Statement value, int statementI) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
this.statementI = statementI;
|
this.statementI = statementI;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final Statement value;
|
public final Statement value;
|
||||||
public final SwitchCase[] cases;
|
public final SwitchCase[] cases;
|
||||||
public final Statement[] body;
|
public final Statement[] body;
|
||||||
public final int defaultI;
|
public final int defaultI;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void declare(ScopeRecord varsScope) {
|
public void declare(ScopeRecord varsScope) {
|
||||||
for (var stm : body) stm.declare(varsScope);
|
for (var stm : body) stm.declare(varsScope);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
var caseToStatement = new HashMap<Integer, Integer>();
|
var caseToStatement = new HashMap<Integer, Integer>();
|
||||||
var statementToIndex = new HashMap<Integer, Integer>();
|
var statementToIndex = new HashMap<Integer, Integer>();
|
||||||
|
|
||||||
value.compile(target, scope, true, BreakpointType.STEP_OVER);
|
value.compile(target, scope, true, BreakpointType.STEP_OVER);
|
||||||
|
|
||||||
for (var ccase : cases) {
|
for (var ccase : cases) {
|
||||||
target.add(Instruction.dup(loc()));
|
target.add(Instruction.dup(loc()));
|
||||||
ccase.value.compile(target, scope, true);
|
ccase.value.compile(target, scope, true);
|
||||||
target.add(Instruction.operation(loc(), Operation.EQUALS));
|
target.add(Instruction.operation(loc(), Operation.EQUALS));
|
||||||
caseToStatement.put(target.size(), ccase.statementI);
|
caseToStatement.put(target.size(), ccase.statementI);
|
||||||
target.add(Instruction.nop(null));
|
target.add(Instruction.nop(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
int start = target.size();
|
int start = target.size();
|
||||||
|
|
||||||
target.add(Instruction.nop(null));
|
target.add(Instruction.nop(null));
|
||||||
|
|
||||||
for (var stm : body) {
|
for (var stm : body) {
|
||||||
statementToIndex.put(statementToIndex.size(), target.size());
|
statementToIndex.put(statementToIndex.size(), target.size());
|
||||||
stm.compile(target, scope, false, BreakpointType.STEP_OVER);
|
stm.compile(target, scope, false, BreakpointType.STEP_OVER);
|
||||||
}
|
}
|
||||||
|
|
||||||
int end = target.size();
|
int end = target.size();
|
||||||
target.add(Instruction.discard(loc()));
|
target.add(Instruction.discard(loc()));
|
||||||
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
||||||
|
|
||||||
if (defaultI < 0 || defaultI >= body.length) target.set(start, Instruction.jmp(loc(), end - start));
|
if (defaultI < 0 || defaultI >= body.length) target.set(start, Instruction.jmp(loc(), end - start));
|
||||||
else target.set(start, Instruction.jmp(loc(), statementToIndex.get(defaultI) - start));
|
else target.set(start, Instruction.jmp(loc(), statementToIndex.get(defaultI) - start));
|
||||||
|
|
||||||
for (int i = start; i < end; i++) {
|
for (int i = start; i < end; i++) {
|
||||||
var instr = target.get(i);
|
var instr = target.get(i);
|
||||||
if (instr.type == Type.NOP && instr.is(0, "break") && instr.get(1) == null) {
|
if (instr.type == Type.NOP && instr.is(0, "break") && instr.get(1) == null) {
|
||||||
target.set(i, Instruction.jmp(loc(), end - i).locate(instr.location));
|
target.set(i, Instruction.jmp(loc(), end - i).locate(instr.location));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (var el : caseToStatement.entrySet()) {
|
for (var el : caseToStatement.entrySet()) {
|
||||||
var i = statementToIndex.get(el.getValue());
|
var i = statementToIndex.get(el.getValue());
|
||||||
if (i == null) i = end;
|
if (i == null) i = end;
|
||||||
target.set(el.getKey(), Instruction.jmpIf(loc(), i - el.getKey()));
|
target.set(el.getKey(), Instruction.jmpIf(loc(), i - el.getKey()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public SwitchStatement(Location loc, Statement value, int defaultI, SwitchCase[] cases, Statement[] body) {
|
public SwitchStatement(Location loc, Statement value, int defaultI, SwitchCase[] cases, Statement[] body) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.value = value;
|
this.value = value;
|
||||||
this.defaultI = defaultI;
|
this.defaultI = defaultI;
|
||||||
this.cases = cases;
|
this.cases = cases;
|
||||||
this.body = body;
|
this.body = body;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,22 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
package me.topchetoeu.jscript.compilation.control;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class ThrowStatement extends Statement {
|
public class ThrowStatement extends Statement {
|
||||||
public final Statement value;
|
public final Statement value;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
value.compile(target, scope, true);
|
value.compile(target, scope, true);
|
||||||
target.add(Instruction.throwInstr(loc()));
|
target.add(Instruction.throwInstr(loc()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ThrowStatement(Location loc, Statement value) {
|
public ThrowStatement(Location loc, Statement value) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,61 +1,61 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
package me.topchetoeu.jscript.compilation.control;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.engine.scope.GlobalScope;
|
import me.topchetoeu.jscript.engine.scope.GlobalScope;
|
||||||
import me.topchetoeu.jscript.engine.scope.LocalScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.LocalScopeRecord;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class TryStatement extends Statement {
|
public class TryStatement extends Statement {
|
||||||
public final Statement tryBody;
|
public final Statement tryBody;
|
||||||
public final Statement catchBody;
|
public final Statement catchBody;
|
||||||
public final Statement finallyBody;
|
public final Statement finallyBody;
|
||||||
public final String name;
|
public final String name;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void declare(ScopeRecord globScope) {
|
public void declare(ScopeRecord globScope) {
|
||||||
tryBody.declare(globScope);
|
tryBody.declare(globScope);
|
||||||
if (catchBody != null) catchBody.declare(globScope);
|
if (catchBody != null) catchBody.declare(globScope);
|
||||||
if (finallyBody != null) finallyBody.declare(globScope);
|
if (finallyBody != null) finallyBody.declare(globScope);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, BreakpointType bpt) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, BreakpointType bpt) {
|
||||||
target.add(Instruction.nop(null));
|
target.add(Instruction.nop(null));
|
||||||
|
|
||||||
int start = target.size(), catchStart = -1, finallyStart = -1;
|
int start = target.size(), catchStart = -1, finallyStart = -1;
|
||||||
|
|
||||||
tryBody.compile(target, scope, false);
|
tryBody.compile(target, scope, false);
|
||||||
target.add(Instruction.tryEnd(loc()));
|
target.add(Instruction.tryEnd(loc()));
|
||||||
|
|
||||||
if (catchBody != null) {
|
if (catchBody != null) {
|
||||||
catchStart = target.size() - start;
|
catchStart = target.size() - start;
|
||||||
var local = scope instanceof GlobalScope ? scope.child() : (LocalScopeRecord)scope;
|
var local = scope instanceof GlobalScope ? scope.child() : (LocalScopeRecord)scope;
|
||||||
local.define(name, true);
|
local.define(name, true);
|
||||||
catchBody.compile(target, scope, false);
|
catchBody.compile(target, scope, false);
|
||||||
local.undefine();
|
local.undefine();
|
||||||
target.add(Instruction.tryEnd(loc()));
|
target.add(Instruction.tryEnd(loc()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (finallyBody != null) {
|
if (finallyBody != null) {
|
||||||
finallyStart = target.size() - start;
|
finallyStart = target.size() - start;
|
||||||
finallyBody.compile(target, scope, false);
|
finallyBody.compile(target, scope, false);
|
||||||
target.add(Instruction.tryEnd(loc()));
|
target.add(Instruction.tryEnd(loc()));
|
||||||
}
|
}
|
||||||
|
|
||||||
target.set(start - 1, Instruction.tryStart(loc(), catchStart, finallyStart, target.size() - start));
|
target.set(start - 1, Instruction.tryStart(loc(), catchStart, finallyStart, target.size() - start));
|
||||||
target.setDebug(start - 1, BreakpointType.STEP_OVER);
|
target.setDebug(start - 1, BreakpointType.STEP_OVER);
|
||||||
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
||||||
}
|
}
|
||||||
|
|
||||||
public TryStatement(Location loc, Statement tryBody, Statement catchBody, Statement finallyBody, String name) {
|
public TryStatement(Location loc, Statement tryBody, Statement catchBody, Statement finallyBody, String name) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.tryBody = tryBody;
|
this.tryBody = tryBody;
|
||||||
this.catchBody = catchBody;
|
this.catchBody = catchBody;
|
||||||
this.finallyBody = finallyBody;
|
this.finallyBody = finallyBody;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,54 +1,54 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
package me.topchetoeu.jscript.compilation.control;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction.Type;
|
import me.topchetoeu.jscript.compilation.Instruction.Type;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class WhileStatement extends Statement {
|
public class WhileStatement extends Statement {
|
||||||
public final Statement condition, body;
|
public final Statement condition, body;
|
||||||
public final String label;
|
public final String label;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void declare(ScopeRecord globScope) {
|
public void declare(ScopeRecord globScope) {
|
||||||
body.declare(globScope);
|
body.declare(globScope);
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
int start = target.size();
|
int start = target.size();
|
||||||
condition.compile(target, scope, true);
|
condition.compile(target, scope, true);
|
||||||
int mid = target.size();
|
int mid = target.size();
|
||||||
target.add(Instruction.nop(null));
|
target.add(Instruction.nop(null));
|
||||||
body.compile(target, scope, false, BreakpointType.STEP_OVER);
|
body.compile(target, scope, false, BreakpointType.STEP_OVER);
|
||||||
|
|
||||||
int end = target.size();
|
int end = target.size();
|
||||||
|
|
||||||
replaceBreaks(target, label, mid + 1, end, start, end + 1);
|
replaceBreaks(target, label, mid + 1, end, start, end + 1);
|
||||||
|
|
||||||
target.add(Instruction.jmp(loc(), start - end));
|
target.add(Instruction.jmp(loc(), start - end));
|
||||||
target.set(mid, Instruction.jmpIfNot(loc(), end - mid + 1));
|
target.set(mid, Instruction.jmpIfNot(loc(), end - mid + 1));
|
||||||
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
||||||
}
|
}
|
||||||
|
|
||||||
public WhileStatement(Location loc, String label, Statement condition, Statement body) {
|
public WhileStatement(Location loc, String label, Statement condition, Statement body) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.label = label;
|
this.label = label;
|
||||||
this.condition = condition;
|
this.condition = condition;
|
||||||
this.body = body;
|
this.body = body;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void replaceBreaks(CompileTarget target, String label, int start, int end, int continuePoint, int breakPoint) {
|
public static void replaceBreaks(CompileTarget target, String label, int start, int end, int continuePoint, int breakPoint) {
|
||||||
for (int i = start; i < end; i++) {
|
for (int i = start; i < end; i++) {
|
||||||
var instr = target.get(i);
|
var instr = target.get(i);
|
||||||
if (instr.type == Type.NOP && instr.is(0, "cont") && (instr.get(1) == null || instr.is(1, label))) {
|
if (instr.type == Type.NOP && instr.is(0, "cont") && (instr.get(1) == null || instr.is(1, label))) {
|
||||||
target.set(i, Instruction.jmp(instr.location, continuePoint - i).setDbgData(target.get(i)));
|
target.set(i, Instruction.jmp(instr.location, continuePoint - i).setDbgData(target.get(i)));
|
||||||
}
|
}
|
||||||
if (instr.type == Type.NOP && instr.is(0, "break") && (instr.get(1) == null || instr.is(1, label))) {
|
if (instr.type == Type.NOP && instr.is(0, "break") && (instr.get(1) == null || instr.is(1, label))) {
|
||||||
target.set(i, Instruction.jmp(instr.location, breakPoint - i).setDbgData(target.get(i)));
|
target.set(i, Instruction.jmp(instr.location, breakPoint - i).setDbgData(target.get(i)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,41 +1,41 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class ArrayStatement extends Statement {
|
public class ArrayStatement extends Statement {
|
||||||
public final Statement[] statements;
|
public final Statement[] statements;
|
||||||
|
|
||||||
@Override public boolean pure() {
|
@Override public boolean pure() {
|
||||||
for (var stm : statements) {
|
for (var stm : statements) {
|
||||||
if (!stm.pure()) return false;
|
if (!stm.pure()) return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
target.add(Instruction.loadArr(loc(), statements.length));
|
target.add(Instruction.loadArr(loc(), statements.length));
|
||||||
|
|
||||||
for (var i = 0; i < statements.length; i++) {
|
for (var i = 0; i < statements.length; i++) {
|
||||||
var el = statements[i];
|
var el = statements[i];
|
||||||
if (el != null) {
|
if (el != null) {
|
||||||
target.add(Instruction.dup(loc()));
|
target.add(Instruction.dup(loc()));
|
||||||
target.add(Instruction.loadValue(loc(), i));
|
target.add(Instruction.loadValue(loc(), i));
|
||||||
el.compile(target, scope, true);
|
el.compile(target, scope, true);
|
||||||
target.add(Instruction.storeMember(loc()));
|
target.add(Instruction.storeMember(loc()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pollute) target.add(Instruction.discard(loc()));
|
if (!pollute) target.add(Instruction.discard(loc()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArrayStatement(Location loc, Statement[] statements) {
|
public ArrayStatement(Location loc, Statement[] statements) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.statements = statements;
|
this.statements = statements;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,51 +1,51 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class CallStatement extends Statement {
|
public class CallStatement extends Statement {
|
||||||
public final Statement func;
|
public final Statement func;
|
||||||
public final Statement[] args;
|
public final Statement[] args;
|
||||||
public final boolean isNew;
|
public final boolean isNew;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, BreakpointType type) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, BreakpointType type) {
|
||||||
if (isNew) func.compile(target, scope, true);
|
if (isNew) func.compile(target, scope, true);
|
||||||
else if (func instanceof IndexStatement) {
|
else if (func instanceof IndexStatement) {
|
||||||
((IndexStatement)func).compile(target, scope, true, true);
|
((IndexStatement)func).compile(target, scope, true, true);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
target.add(Instruction.loadValue(loc(), null));
|
target.add(Instruction.loadValue(loc(), null));
|
||||||
func.compile(target, scope, true);
|
func.compile(target, scope, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var arg : args) arg.compile(target, scope, true);
|
for (var arg : args) arg.compile(target, scope, true);
|
||||||
|
|
||||||
if (isNew) target.add(Instruction.callNew(loc(), args.length));
|
if (isNew) target.add(Instruction.callNew(loc(), args.length));
|
||||||
else target.add(Instruction.call(loc(), args.length));
|
else target.add(Instruction.call(loc(), args.length));
|
||||||
target.setDebug(type);
|
target.setDebug(type);
|
||||||
|
|
||||||
if (!pollute) target.add(Instruction.discard(loc()));
|
if (!pollute) target.add(Instruction.discard(loc()));
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
compile(target, scope, pollute, BreakpointType.STEP_IN);
|
compile(target, scope, pollute, BreakpointType.STEP_IN);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CallStatement(Location loc, boolean isNew, Statement func, Statement ...args) {
|
public CallStatement(Location loc, boolean isNew, Statement func, Statement ...args) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.isNew = isNew;
|
this.isNew = isNew;
|
||||||
this.func = func;
|
this.func = func;
|
||||||
this.args = args;
|
this.args = args;
|
||||||
}
|
}
|
||||||
public CallStatement(Location loc, boolean isNew, Statement obj, Object key, Statement ...args) {
|
public CallStatement(Location loc, boolean isNew, Statement obj, Object key, Statement ...args) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.isNew = isNew;
|
this.isNew = isNew;
|
||||||
this.func = new IndexStatement(loc, obj, new ConstantStatement(loc, key));
|
this.func = new IndexStatement(loc, obj, new ConstantStatement(loc, key));
|
||||||
this.args = args;
|
this.args = args;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,32 +1,32 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.AssignableStatement;
|
import me.topchetoeu.jscript.compilation.AssignableStatement;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.engine.Operation;
|
import me.topchetoeu.jscript.engine.Operation;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class ChangeStatement extends Statement {
|
public class ChangeStatement extends Statement {
|
||||||
public final AssignableStatement value;
|
public final AssignableStatement value;
|
||||||
public final double addAmount;
|
public final double addAmount;
|
||||||
public final boolean postfix;
|
public final boolean postfix;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
value.toAssign(new ConstantStatement(loc(), -addAmount), Operation.SUBTRACT).compile(target, scope, true);
|
value.toAssign(new ConstantStatement(loc(), -addAmount), Operation.SUBTRACT).compile(target, scope, true);
|
||||||
if (!pollute) target.add(Instruction.discard(loc()));
|
if (!pollute) target.add(Instruction.discard(loc()));
|
||||||
else if (postfix) {
|
else if (postfix) {
|
||||||
target.add(Instruction.loadValue(loc(), addAmount));
|
target.add(Instruction.loadValue(loc(), addAmount));
|
||||||
target.add(Instruction.operation(loc(), Operation.SUBTRACT));
|
target.add(Instruction.operation(loc(), Operation.SUBTRACT));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChangeStatement(Location loc, AssignableStatement value, double addAmount, boolean postfix) {
|
public ChangeStatement(Location loc, AssignableStatement value, double addAmount, boolean postfix) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.value = value;
|
this.value = value;
|
||||||
this.addAmount = addAmount;
|
this.addAmount = addAmount;
|
||||||
this.postfix = postfix;
|
this.postfix = postfix;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,23 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class ConstantStatement extends Statement {
|
public class ConstantStatement extends Statement {
|
||||||
public final Object value;
|
public final Object value;
|
||||||
|
|
||||||
@Override public boolean pure() { return true; }
|
@Override public boolean pure() { return true; }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
if (pollute) target.add(Instruction.loadValue(loc(), value));
|
if (pollute) target.add(Instruction.loadValue(loc(), value));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConstantStatement(Location loc, Object val) {
|
public ConstantStatement(Location loc, Object val) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.value = val;
|
this.value = val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,24 +1,24 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class DiscardStatement extends Statement {
|
public class DiscardStatement extends Statement {
|
||||||
public final Statement value;
|
public final Statement value;
|
||||||
|
|
||||||
@Override public boolean pure() { return value.pure(); }
|
@Override public boolean pure() { return value.pure(); }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
value.compile(target, scope, false);
|
value.compile(target, scope, false);
|
||||||
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
if (pollute) target.add(Instruction.loadValue(loc(), null));
|
||||||
}
|
}
|
||||||
|
|
||||||
public DiscardStatement(Location loc, Statement val) {
|
public DiscardStatement(Location loc, Statement val) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.value = val;
|
this.value = val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,139 +1,139 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.CompoundStatement;
|
import me.topchetoeu.jscript.compilation.CompoundStatement;
|
||||||
import me.topchetoeu.jscript.compilation.FunctionBody;
|
import me.topchetoeu.jscript.compilation.FunctionBody;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction.Type;
|
import me.topchetoeu.jscript.compilation.Instruction.Type;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
import me.topchetoeu.jscript.exceptions.SyntaxException;
|
import me.topchetoeu.jscript.exceptions.SyntaxException;
|
||||||
|
|
||||||
public class FunctionStatement extends Statement {
|
public class FunctionStatement extends Statement {
|
||||||
public final CompoundStatement body;
|
public final CompoundStatement body;
|
||||||
public final String varName;
|
public final String varName;
|
||||||
public final String[] args;
|
public final String[] args;
|
||||||
public final boolean statement;
|
public final boolean statement;
|
||||||
public final Location end;
|
public final Location end;
|
||||||
|
|
||||||
private static Random rand = new Random();
|
private static Random rand = new Random();
|
||||||
|
|
||||||
@Override public boolean pure() { return varName == null && statement; }
|
@Override public boolean pure() { return varName == null && statement; }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void declare(ScopeRecord scope) {
|
public void declare(ScopeRecord scope) {
|
||||||
if (varName != null && statement) scope.define(varName);
|
if (varName != null && statement) scope.define(varName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void checkBreakAndCont(CompileTarget target, int start) {
|
public static void checkBreakAndCont(CompileTarget target, int start) {
|
||||||
for (int i = start; i < target.size(); i++) {
|
for (int i = start; i < target.size(); i++) {
|
||||||
if (target.get(i).type == Type.NOP) {
|
if (target.get(i).type == Type.NOP) {
|
||||||
if (target.get(i).is(0, "break") ) {
|
if (target.get(i).is(0, "break") ) {
|
||||||
throw new SyntaxException(target.get(i).location, "Break was placed outside a loop.");
|
throw new SyntaxException(target.get(i).location, "Break was placed outside a loop.");
|
||||||
}
|
}
|
||||||
if (target.get(i).is(0, "cont")) {
|
if (target.get(i).is(0, "cont")) {
|
||||||
throw new SyntaxException(target.get(i).location, "Continue was placed outside a loop.");
|
throw new SyntaxException(target.get(i).location, "Continue was placed outside a loop.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private long compileBody(CompileTarget target, ScopeRecord scope, boolean polute, BreakpointType bp) {
|
private long compileBody(CompileTarget target, ScopeRecord scope, boolean polute, BreakpointType bp) {
|
||||||
for (var i = 0; i < args.length; i++) {
|
for (var i = 0; i < args.length; i++) {
|
||||||
for (var j = 0; j < i; j++) {
|
for (var j = 0; j < i; j++) {
|
||||||
if (args[i].equals(args[j])) {
|
if (args[i].equals(args[j])) {
|
||||||
throw new SyntaxException(loc(), "Duplicate parameter '" + args[i] + "'.");
|
throw new SyntaxException(loc(), "Duplicate parameter '" + args[i] + "'.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var id = rand.nextLong();
|
var id = rand.nextLong();
|
||||||
var subscope = scope.child();
|
var subscope = scope.child();
|
||||||
var subtarget = new CompileTarget(target.functions, target.breakpoints);
|
var subtarget = new CompileTarget(target.functions, target.breakpoints);
|
||||||
|
|
||||||
subscope.define("this");
|
subscope.define("this");
|
||||||
var argsVar = subscope.define("arguments");
|
var argsVar = subscope.define("arguments");
|
||||||
|
|
||||||
if (args.length > 0) {
|
if (args.length > 0) {
|
||||||
for (var i = 0; i < args.length; i++) {
|
for (var i = 0; i < args.length; i++) {
|
||||||
subtarget.add(Instruction.loadVar(loc(), argsVar));
|
subtarget.add(Instruction.loadVar(loc(), argsVar));
|
||||||
subtarget.add(Instruction.loadMember(loc(), i));
|
subtarget.add(Instruction.loadMember(loc(), i));
|
||||||
subtarget.add(Instruction.storeVar(loc(), subscope.define(args[i])));
|
subtarget.add(Instruction.storeVar(loc(), subscope.define(args[i])));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!statement && this.varName != null) {
|
if (!statement && this.varName != null) {
|
||||||
subtarget.add(Instruction.storeSelfFunc(loc(), (int)subscope.define(this.varName)));
|
subtarget.add(Instruction.storeSelfFunc(loc(), (int)subscope.define(this.varName)));
|
||||||
subtarget.setDebug(bp);
|
subtarget.setDebug(bp);
|
||||||
}
|
}
|
||||||
|
|
||||||
body.declare(subscope);
|
body.declare(subscope);
|
||||||
body.compile(subtarget, subscope, false);
|
body.compile(subtarget, subscope, false);
|
||||||
subtarget.add(Instruction.ret(end));
|
subtarget.add(Instruction.ret(end));
|
||||||
checkBreakAndCont(subtarget, 0);
|
checkBreakAndCont(subtarget, 0);
|
||||||
|
|
||||||
if (polute) target.add(Instruction.loadFunc(loc(), id, subscope.getCaptures()));
|
if (polute) target.add(Instruction.loadFunc(loc(), id, subscope.getCaptures()));
|
||||||
target.functions.put(id, new FunctionBody(
|
target.functions.put(id, new FunctionBody(
|
||||||
subscope.localsCount(), args.length,
|
subscope.localsCount(), args.length,
|
||||||
subtarget.array(), subscope.captures(), subscope.locals()
|
subtarget.array(), subscope.captures(), subscope.locals()
|
||||||
));
|
));
|
||||||
|
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, String name, BreakpointType bp) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, String name, BreakpointType bp) {
|
||||||
if (this.varName != null) name = this.varName;
|
if (this.varName != null) name = this.varName;
|
||||||
|
|
||||||
var hasVar = this.varName != null && statement;
|
var hasVar = this.varName != null && statement;
|
||||||
var hasName = name != null;
|
var hasName = name != null;
|
||||||
|
|
||||||
compileBody(target, scope, pollute || hasVar || hasName, bp);
|
compileBody(target, scope, pollute || hasVar || hasName, bp);
|
||||||
|
|
||||||
if (hasName) {
|
if (hasName) {
|
||||||
if (pollute || hasVar) target.add(Instruction.dup(loc()));
|
if (pollute || hasVar) target.add(Instruction.dup(loc()));
|
||||||
target.add(Instruction.loadValue(loc(), "name"));
|
target.add(Instruction.loadValue(loc(), "name"));
|
||||||
target.add(Instruction.loadValue(loc(), name));
|
target.add(Instruction.loadValue(loc(), name));
|
||||||
target.add(Instruction.storeMember(loc()));
|
target.add(Instruction.storeMember(loc()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasVar) {
|
if (hasVar) {
|
||||||
var key = scope.getKey(this.varName);
|
var key = scope.getKey(this.varName);
|
||||||
|
|
||||||
if (key instanceof String) target.add(Instruction.makeVar(loc(), (String)key));
|
if (key instanceof String) target.add(Instruction.makeVar(loc(), (String)key));
|
||||||
target.add(Instruction.storeVar(loc(), scope.getKey(this.varName), false));
|
target.add(Instruction.storeVar(loc(), scope.getKey(this.varName), false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, String name) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, String name) {
|
||||||
compile(target, scope, pollute, name, BreakpointType.NONE);
|
compile(target, scope, pollute, name, BreakpointType.NONE);
|
||||||
}
|
}
|
||||||
@Override public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, BreakpointType bp) {
|
@Override public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, BreakpointType bp) {
|
||||||
compile(target, scope, pollute, (String)null, bp);
|
compile(target, scope, pollute, (String)null, bp);
|
||||||
}
|
}
|
||||||
@Override public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
@Override public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
compile(target, scope, pollute, (String)null, BreakpointType.NONE);
|
compile(target, scope, pollute, (String)null, BreakpointType.NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public FunctionStatement(Location loc, Location end, String varName, String[] args, boolean statement, CompoundStatement body) {
|
public FunctionStatement(Location loc, Location end, String varName, String[] args, boolean statement, CompoundStatement body) {
|
||||||
super(loc);
|
super(loc);
|
||||||
|
|
||||||
this.end = end;
|
this.end = end;
|
||||||
this.varName = varName;
|
this.varName = varName;
|
||||||
this.statement = statement;
|
this.statement = statement;
|
||||||
|
|
||||||
this.args = args;
|
this.args = args;
|
||||||
this.body = body;
|
this.body = body;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void compileWithName(Statement stm, CompileTarget target, ScopeRecord scope, boolean pollute, String name) {
|
public static void compileWithName(Statement stm, CompileTarget target, ScopeRecord scope, boolean pollute, String name) {
|
||||||
if (stm instanceof FunctionStatement) ((FunctionStatement)stm).compile(target, scope, pollute, name);
|
if (stm instanceof FunctionStatement) ((FunctionStatement)stm).compile(target, scope, pollute, name);
|
||||||
else stm.compile(target, scope, pollute);
|
else stm.compile(target, scope, pollute);
|
||||||
}
|
}
|
||||||
public static void compileWithName(Statement stm, CompileTarget target, ScopeRecord scope, boolean pollute, String name, BreakpointType bp) {
|
public static void compileWithName(Statement stm, CompileTarget target, ScopeRecord scope, boolean pollute, String name, BreakpointType bp) {
|
||||||
if (stm instanceof FunctionStatement) ((FunctionStatement)stm).compile(target, scope, pollute, name, bp);
|
if (stm instanceof FunctionStatement) ((FunctionStatement)stm).compile(target, scope, pollute, name, bp);
|
||||||
else stm.compile(target, scope, pollute, bp);
|
else stm.compile(target, scope, pollute, bp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class GlobalThisStatement extends Statement {
|
public class GlobalThisStatement extends Statement {
|
||||||
@Override public boolean pure() { return true; }
|
@Override public boolean pure() { return true; }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
if (pollute) target.add(Instruction.loadGlob(loc()));
|
if (pollute) target.add(Instruction.loadGlob(loc()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public GlobalThisStatement(Location loc) {
|
public GlobalThisStatement(Location loc) {
|
||||||
super(loc);
|
super(loc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,48 +1,48 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.engine.Operation;
|
import me.topchetoeu.jscript.engine.Operation;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class IndexAssignStatement extends Statement {
|
public class IndexAssignStatement extends Statement {
|
||||||
public final Statement object;
|
public final Statement object;
|
||||||
public final Statement index;
|
public final Statement index;
|
||||||
public final Statement value;
|
public final Statement value;
|
||||||
public final Operation operation;
|
public final Operation operation;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
if (operation != null) {
|
if (operation != null) {
|
||||||
object.compile(target, scope, true);
|
object.compile(target, scope, true);
|
||||||
index.compile(target, scope, true);
|
index.compile(target, scope, true);
|
||||||
target.add(Instruction.dup(loc(), 2));
|
target.add(Instruction.dup(loc(), 2));
|
||||||
|
|
||||||
target.add(Instruction.loadMember(loc()));
|
target.add(Instruction.loadMember(loc()));
|
||||||
value.compile(target, scope, true);
|
value.compile(target, scope, true);
|
||||||
target.add(Instruction.operation(loc(), operation));
|
target.add(Instruction.operation(loc(), operation));
|
||||||
|
|
||||||
target.add(Instruction.storeMember(loc(), pollute));
|
target.add(Instruction.storeMember(loc(), pollute));
|
||||||
target.setDebug(BreakpointType.STEP_IN);
|
target.setDebug(BreakpointType.STEP_IN);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
object.compile(target, scope, true);
|
object.compile(target, scope, true);
|
||||||
index.compile(target, scope, true);
|
index.compile(target, scope, true);
|
||||||
value.compile(target, scope, true);
|
value.compile(target, scope, true);
|
||||||
|
|
||||||
target.add(Instruction.storeMember(loc(), pollute));
|
target.add(Instruction.storeMember(loc(), pollute));
|
||||||
target.setDebug(BreakpointType.STEP_IN);
|
target.setDebug(BreakpointType.STEP_IN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IndexAssignStatement(Location loc, Statement object, Statement index, Statement value, Operation operation) {
|
public IndexAssignStatement(Location loc, Statement object, Statement index, Statement value, Operation operation) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.object = object;
|
this.object = object;
|
||||||
this.index = index;
|
this.index = index;
|
||||||
this.value = value;
|
this.value = value;
|
||||||
this.operation = operation;
|
this.operation = operation;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,49 +1,49 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.AssignableStatement;
|
import me.topchetoeu.jscript.compilation.AssignableStatement;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
import me.topchetoeu.jscript.compilation.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.engine.Operation;
|
import me.topchetoeu.jscript.engine.Operation;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class IndexStatement extends AssignableStatement {
|
public class IndexStatement extends AssignableStatement {
|
||||||
public final Statement object;
|
public final Statement object;
|
||||||
public final Statement index;
|
public final Statement index;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Statement toAssign(Statement val, Operation operation) {
|
public Statement toAssign(Statement val, Operation operation) {
|
||||||
return new IndexAssignStatement(loc(), object, index, val, operation);
|
return new IndexAssignStatement(loc(), object, index, val, operation);
|
||||||
}
|
}
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean dupObj, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean dupObj, boolean pollute) {
|
||||||
object.compile(target, scope, true);
|
object.compile(target, scope, true);
|
||||||
if (dupObj) target.add(Instruction.dup(loc()));
|
if (dupObj) target.add(Instruction.dup(loc()));
|
||||||
if (index instanceof ConstantStatement) {
|
if (index instanceof ConstantStatement) {
|
||||||
target.add(Instruction.loadMember(loc(), ((ConstantStatement)index).value));
|
target.add(Instruction.loadMember(loc(), ((ConstantStatement)index).value));
|
||||||
target.setDebug(BreakpointType.STEP_IN);
|
target.setDebug(BreakpointType.STEP_IN);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
index.compile(target, scope, true);
|
index.compile(target, scope, true);
|
||||||
target.add(Instruction.loadMember(loc()));
|
target.add(Instruction.loadMember(loc()));
|
||||||
target.setDebug(BreakpointType.STEP_IN);
|
target.setDebug(BreakpointType.STEP_IN);
|
||||||
if (!pollute) target.add(Instruction.discard(loc()));
|
if (!pollute) target.add(Instruction.discard(loc()));
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
compile(target, scope, false, pollute);
|
compile(target, scope, false, pollute);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IndexStatement(Location loc, Statement object, Statement index) {
|
public IndexStatement(Location loc, Statement object, Statement index) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.object = object;
|
this.object = object;
|
||||||
this.index = index;
|
this.index = index;
|
||||||
}
|
}
|
||||||
public IndexStatement(Location loc, Statement object, Object index) {
|
public IndexStatement(Location loc, Statement object, Object index) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.object = object;
|
this.object = object;
|
||||||
this.index = new ConstantStatement(loc, index);
|
this.index = new ConstantStatement(loc, index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,39 +1,39 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
|
|
||||||
public class LazyAndStatement extends Statement {
|
public class LazyAndStatement extends Statement {
|
||||||
public final Statement first, second;
|
public final Statement first, second;
|
||||||
|
|
||||||
@Override public boolean pure() { return first.pure() && second.pure(); }
|
@Override public boolean pure() { return first.pure() && second.pure(); }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
if (first instanceof ConstantStatement) {
|
if (first instanceof ConstantStatement) {
|
||||||
if (Values.not(((ConstantStatement)first).value)) {
|
if (Values.not(((ConstantStatement)first).value)) {
|
||||||
first.compile(target, scope, pollute);
|
first.compile(target, scope, pollute);
|
||||||
}
|
}
|
||||||
else second.compile(target, scope, pollute);
|
else second.compile(target, scope, pollute);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
first.compile(target, scope, true);
|
first.compile(target, scope, true);
|
||||||
if (pollute) target.add(Instruction.dup(loc()));
|
if (pollute) target.add(Instruction.dup(loc()));
|
||||||
int start = target.size();
|
int start = target.size();
|
||||||
target.add(Instruction.nop(null));
|
target.add(Instruction.nop(null));
|
||||||
if (pollute) target.add(Instruction.discard(loc()));
|
if (pollute) target.add(Instruction.discard(loc()));
|
||||||
second.compile(target, scope, pollute);
|
second.compile(target, scope, pollute);
|
||||||
target.set(start, Instruction.jmpIfNot(loc(), target.size() - start));
|
target.set(start, Instruction.jmpIfNot(loc(), target.size() - start));
|
||||||
}
|
}
|
||||||
|
|
||||||
public LazyAndStatement(Location loc, Statement first, Statement second) {
|
public LazyAndStatement(Location loc, Statement first, Statement second) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.first = first;
|
this.first = first;
|
||||||
this.second = second;
|
this.second = second;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,39 +1,39 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
|
|
||||||
public class LazyOrStatement extends Statement {
|
public class LazyOrStatement extends Statement {
|
||||||
public final Statement first, second;
|
public final Statement first, second;
|
||||||
|
|
||||||
@Override public boolean pure() { return first.pure() && second.pure(); }
|
@Override public boolean pure() { return first.pure() && second.pure(); }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
if (first instanceof ConstantStatement) {
|
if (first instanceof ConstantStatement) {
|
||||||
if (Values.not(((ConstantStatement)first).value)) {
|
if (Values.not(((ConstantStatement)first).value)) {
|
||||||
second.compile(target, scope, pollute);
|
second.compile(target, scope, pollute);
|
||||||
}
|
}
|
||||||
else first.compile(target, scope, pollute);
|
else first.compile(target, scope, pollute);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
first.compile(target, scope, true);
|
first.compile(target, scope, true);
|
||||||
if (pollute) target.add(Instruction.dup(loc()));
|
if (pollute) target.add(Instruction.dup(loc()));
|
||||||
int start = target.size();
|
int start = target.size();
|
||||||
target.add(Instruction.nop(null));
|
target.add(Instruction.nop(null));
|
||||||
if (pollute) target.add(Instruction.discard(loc()));
|
if (pollute) target.add(Instruction.discard(loc()));
|
||||||
second.compile(target, scope, pollute);
|
second.compile(target, scope, pollute);
|
||||||
target.set(start, Instruction.jmpIf(loc(), target.size() - start));
|
target.set(start, Instruction.jmpIf(loc(), target.size() - start));
|
||||||
}
|
}
|
||||||
|
|
||||||
public LazyOrStatement(Location loc, Statement first, Statement second) {
|
public LazyOrStatement(Location loc, Statement first, Statement second) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.first = first;
|
this.first = first;
|
||||||
this.second = second;
|
this.second = second;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,63 +1,63 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class ObjectStatement extends Statement {
|
public class ObjectStatement extends Statement {
|
||||||
public final Map<Object, Statement> map;
|
public final Map<Object, Statement> map;
|
||||||
public final Map<Object, FunctionStatement> getters;
|
public final Map<Object, FunctionStatement> getters;
|
||||||
public final Map<Object, FunctionStatement> setters;
|
public final Map<Object, FunctionStatement> setters;
|
||||||
|
|
||||||
@Override public boolean pure() {
|
@Override public boolean pure() {
|
||||||
for (var el : map.values()) {
|
for (var el : map.values()) {
|
||||||
if (!el.pure()) return false;
|
if (!el.pure()) return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
target.add(Instruction.loadObj(loc()));
|
target.add(Instruction.loadObj(loc()));
|
||||||
|
|
||||||
for (var el : map.entrySet()) {
|
for (var el : map.entrySet()) {
|
||||||
target.add(Instruction.dup(loc()));
|
target.add(Instruction.dup(loc()));
|
||||||
target.add(Instruction.loadValue(loc(), el.getKey()));
|
target.add(Instruction.loadValue(loc(), el.getKey()));
|
||||||
var val = el.getValue();
|
var val = el.getValue();
|
||||||
FunctionStatement.compileWithName(val, target, scope, true, el.getKey().toString());
|
FunctionStatement.compileWithName(val, target, scope, true, el.getKey().toString());
|
||||||
target.add(Instruction.storeMember(loc()));
|
target.add(Instruction.storeMember(loc()));
|
||||||
}
|
}
|
||||||
|
|
||||||
var keys = new ArrayList<Object>();
|
var keys = new ArrayList<Object>();
|
||||||
keys.addAll(getters.keySet());
|
keys.addAll(getters.keySet());
|
||||||
keys.addAll(setters.keySet());
|
keys.addAll(setters.keySet());
|
||||||
|
|
||||||
for (var key : keys) {
|
for (var key : keys) {
|
||||||
if (key instanceof String) target.add(Instruction.loadValue(loc(), (String)key));
|
if (key instanceof String) target.add(Instruction.loadValue(loc(), (String)key));
|
||||||
else target.add(Instruction.loadValue(loc(), (Double)key));
|
else target.add(Instruction.loadValue(loc(), (Double)key));
|
||||||
|
|
||||||
if (getters.containsKey(key)) getters.get(key).compile(target, scope, true);
|
if (getters.containsKey(key)) getters.get(key).compile(target, scope, true);
|
||||||
else target.add(Instruction.loadValue(loc(), null));
|
else target.add(Instruction.loadValue(loc(), null));
|
||||||
|
|
||||||
if (setters.containsKey(key)) setters.get(key).compile(target, scope, true);
|
if (setters.containsKey(key)) setters.get(key).compile(target, scope, true);
|
||||||
else target.add(Instruction.loadValue(loc(), null));
|
else target.add(Instruction.loadValue(loc(), null));
|
||||||
|
|
||||||
target.add(Instruction.defProp(loc()));
|
target.add(Instruction.defProp(loc()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pollute) target.add(Instruction.discard(loc()));
|
if (!pollute) target.add(Instruction.discard(loc()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ObjectStatement(Location loc, Map<Object, Statement> map, Map<Object, FunctionStatement> getters, Map<Object, FunctionStatement> setters) {
|
public ObjectStatement(Location loc, Map<Object, Statement> map, Map<Object, FunctionStatement> getters, Map<Object, FunctionStatement> setters) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.map = map;
|
this.map = map;
|
||||||
this.getters = getters;
|
this.getters = getters;
|
||||||
this.setters = setters;
|
this.setters = setters;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,37 +1,37 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.engine.Operation;
|
import me.topchetoeu.jscript.engine.Operation;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class OperationStatement extends Statement {
|
public class OperationStatement extends Statement {
|
||||||
public final Statement[] args;
|
public final Statement[] args;
|
||||||
public final Operation operation;
|
public final Operation operation;
|
||||||
|
|
||||||
@Override public boolean pure() {
|
@Override public boolean pure() {
|
||||||
for (var el : args) {
|
for (var el : args) {
|
||||||
if (!el.pure()) return false;
|
if (!el.pure()) return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
for (var arg : args) {
|
for (var arg : args) {
|
||||||
arg.compile(target, scope, true);
|
arg.compile(target, scope, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pollute) target.add(Instruction.operation(loc(), operation));
|
if (pollute) target.add(Instruction.operation(loc(), operation));
|
||||||
else target.add(Instruction.discard(loc()));
|
else target.add(Instruction.discard(loc()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public OperationStatement(Location loc, Operation operation, Statement ...args) {
|
public OperationStatement(Location loc, Operation operation, Statement ...args) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.operation = operation;
|
this.operation = operation;
|
||||||
this.args = args;
|
this.args = args;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,26 +1,26 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class RegexStatement extends Statement {
|
public class RegexStatement extends Statement {
|
||||||
public final String pattern, flags;
|
public final String pattern, flags;
|
||||||
|
|
||||||
// Not really pure, since a function is called, but can be ignored.
|
// Not really pure, since a function is called, but can be ignored.
|
||||||
@Override public boolean pure() { return true; }
|
@Override public boolean pure() { return true; }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
target.add(Instruction.loadRegex(loc(), pattern, flags));
|
target.add(Instruction.loadRegex(loc(), pattern, flags));
|
||||||
if (!pollute) target.add(Instruction.discard(loc()));
|
if (!pollute) target.add(Instruction.discard(loc()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public RegexStatement(Location loc, String pattern, String flags) {
|
public RegexStatement(Location loc, String pattern, String flags) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.pattern = pattern;
|
this.pattern = pattern;
|
||||||
this.flags = flags;
|
this.flags = flags;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,33 +1,33 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class TypeofStatement extends Statement {
|
public class TypeofStatement extends Statement {
|
||||||
public final Statement value;
|
public final Statement value;
|
||||||
|
|
||||||
// Not really pure, since a variable from the global scope could be accessed,
|
// Not really pure, since a variable from the global scope could be accessed,
|
||||||
// which could lead to code execution, that would get omitted
|
// which could lead to code execution, that would get omitted
|
||||||
@Override public boolean pure() { return true; }
|
@Override public boolean pure() { return true; }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
if (value instanceof VariableStatement) {
|
if (value instanceof VariableStatement) {
|
||||||
var i = scope.getKey(((VariableStatement)value).name);
|
var i = scope.getKey(((VariableStatement)value).name);
|
||||||
if (i instanceof String) {
|
if (i instanceof String) {
|
||||||
target.add(Instruction.typeof(loc(), (String)i));
|
target.add(Instruction.typeof(loc(), (String)i));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
value.compile(target, scope, pollute);
|
value.compile(target, scope, pollute);
|
||||||
target.add(Instruction.typeof(loc()));
|
target.add(Instruction.typeof(loc()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public TypeofStatement(Location loc, Statement value) {
|
public TypeofStatement(Location loc, Statement value) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,38 +1,38 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.engine.Operation;
|
import me.topchetoeu.jscript.engine.Operation;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class VariableAssignStatement extends Statement {
|
public class VariableAssignStatement extends Statement {
|
||||||
public final String name;
|
public final String name;
|
||||||
public final Statement value;
|
public final Statement value;
|
||||||
public final Operation operation;
|
public final Operation operation;
|
||||||
|
|
||||||
@Override public boolean pure() { return false; }
|
@Override public boolean pure() { return false; }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
var i = scope.getKey(name);
|
var i = scope.getKey(name);
|
||||||
if (operation != null) {
|
if (operation != null) {
|
||||||
target.add(Instruction.loadVar(loc(), i));
|
target.add(Instruction.loadVar(loc(), i));
|
||||||
FunctionStatement.compileWithName(value, target, scope, true, name);
|
FunctionStatement.compileWithName(value, target, scope, true, name);
|
||||||
target.add(Instruction.operation(loc(), operation));
|
target.add(Instruction.operation(loc(), operation));
|
||||||
target.add(Instruction.storeVar(loc(), i, pollute));
|
target.add(Instruction.storeVar(loc(), i, pollute));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
FunctionStatement.compileWithName(value, target, scope, true, name);
|
FunctionStatement.compileWithName(value, target, scope, true, name);
|
||||||
target.add(Instruction.storeVar(loc(), i, pollute));
|
target.add(Instruction.storeVar(loc(), i, pollute));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public VariableAssignStatement(Location loc, String name, Statement val, Operation operation) {
|
public VariableAssignStatement(Location loc, String name, Statement val, Operation operation) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.value = val;
|
this.value = val;
|
||||||
this.operation = operation;
|
this.operation = operation;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,23 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class VariableIndexStatement extends Statement {
|
public class VariableIndexStatement extends Statement {
|
||||||
public final int index;
|
public final int index;
|
||||||
|
|
||||||
@Override public boolean pure() { return true; }
|
@Override public boolean pure() { return true; }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
if (pollute) target.add(Instruction.loadVar(loc(), index));
|
if (pollute) target.add(Instruction.loadVar(loc(), index));
|
||||||
}
|
}
|
||||||
|
|
||||||
public VariableIndexStatement(Location loc, int i) {
|
public VariableIndexStatement(Location loc, int i) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.index = i;
|
this.index = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,32 +1,32 @@
|
|||||||
package me.topchetoeu.jscript.compilation.values;
|
package me.topchetoeu.jscript.compilation.values;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.AssignableStatement;
|
import me.topchetoeu.jscript.compilation.AssignableStatement;
|
||||||
import me.topchetoeu.jscript.compilation.CompileTarget;
|
import me.topchetoeu.jscript.compilation.CompileTarget;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.compilation.Statement;
|
import me.topchetoeu.jscript.compilation.Statement;
|
||||||
import me.topchetoeu.jscript.engine.Operation;
|
import me.topchetoeu.jscript.engine.Operation;
|
||||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||||
|
|
||||||
public class VariableStatement extends AssignableStatement {
|
public class VariableStatement extends AssignableStatement {
|
||||||
public final String name;
|
public final String name;
|
||||||
|
|
||||||
@Override public boolean pure() { return false; }
|
@Override public boolean pure() { return false; }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Statement toAssign(Statement val, Operation operation) {
|
public Statement toAssign(Statement val, Operation operation) {
|
||||||
return new VariableAssignStatement(loc(), name, val, operation);
|
return new VariableAssignStatement(loc(), name, val, operation);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) {
|
||||||
var i = scope.getKey(name);
|
var i = scope.getKey(name);
|
||||||
target.add(Instruction.loadVar(loc(), i));
|
target.add(Instruction.loadVar(loc(), i));
|
||||||
if (!pollute) target.add(Instruction.discard(loc()));
|
if (!pollute) target.add(Instruction.discard(loc()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public VariableStatement(Location loc, String name) {
|
public VariableStatement(Location loc, String name) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,125 +1,125 @@
|
|||||||
package me.topchetoeu.jscript.engine;
|
package me.topchetoeu.jscript.engine;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Stack;
|
import java.util.Stack;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Filename;
|
import me.topchetoeu.jscript.Filename;
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.engine.frame.CodeFrame;
|
import me.topchetoeu.jscript.engine.frame.CodeFrame;
|
||||||
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
||||||
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
import me.topchetoeu.jscript.mapping.SourceMap;
|
import me.topchetoeu.jscript.mapping.SourceMap;
|
||||||
|
|
||||||
public class Context {
|
public class Context {
|
||||||
private final Stack<Environment> env = new Stack<>();
|
private final Stack<Environment> env = new Stack<>();
|
||||||
private final ArrayList<CodeFrame> frames = new ArrayList<>();
|
private final ArrayList<CodeFrame> frames = new ArrayList<>();
|
||||||
public final Engine engine;
|
public final Engine engine;
|
||||||
|
|
||||||
public Environment environment() {
|
public Environment environment() {
|
||||||
return env.empty() ? null : env.peek();
|
return env.empty() ? null : env.peek();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Context pushEnv(Environment env) {
|
public Context pushEnv(Environment env) {
|
||||||
this.env.push(env);
|
this.env.push(env);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
public void popEnv() {
|
public void popEnv() {
|
||||||
if (!env.empty()) this.env.pop();
|
if (!env.empty()) this.env.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
public FunctionValue compile(Filename filename, String raw) {
|
public FunctionValue compile(Filename filename, String raw) {
|
||||||
var env = environment();
|
var env = environment();
|
||||||
var result = env.compile.call(this, null, raw, filename.toString(), env);
|
var result = env.compile.call(this, null, raw, filename.toString(), env);
|
||||||
|
|
||||||
var function = (FunctionValue)Values.getMember(this, result, "function");
|
var function = (FunctionValue)Values.getMember(this, result, "function");
|
||||||
if (!engine.debugging) return function;
|
if (!engine.debugging) return function;
|
||||||
|
|
||||||
var rawMapChain = ((ArrayValue)Values.getMember(this, result, "mapChain")).toArray();
|
var rawMapChain = ((ArrayValue)Values.getMember(this, result, "mapChain")).toArray();
|
||||||
var breakpoints = new TreeSet<>(
|
var breakpoints = new TreeSet<>(
|
||||||
Arrays.stream(((ArrayValue)Values.getMember(this, result, "breakpoints")).toArray())
|
Arrays.stream(((ArrayValue)Values.getMember(this, result, "breakpoints")).toArray())
|
||||||
.map(v -> Location.parse(Values.toString(this, v)))
|
.map(v -> Location.parse(Values.toString(this, v)))
|
||||||
.collect(Collectors.toList())
|
.collect(Collectors.toList())
|
||||||
);
|
);
|
||||||
var maps = new SourceMap[rawMapChain.length];
|
var maps = new SourceMap[rawMapChain.length];
|
||||||
|
|
||||||
for (var i = 0; i < maps.length; i++) maps[i] = SourceMap.parse(Values.toString(this, (String)rawMapChain[i]));
|
for (var i = 0; i < maps.length; i++) maps[i] = SourceMap.parse(Values.toString(this, (String)rawMapChain[i]));
|
||||||
|
|
||||||
var map = SourceMap.chain(maps);
|
var map = SourceMap.chain(maps);
|
||||||
|
|
||||||
if (map != null) {
|
if (map != null) {
|
||||||
var newBreakpoints = new TreeSet<Location>();
|
var newBreakpoints = new TreeSet<Location>();
|
||||||
for (var bp : breakpoints) {
|
for (var bp : breakpoints) {
|
||||||
bp = map.toCompiled(bp);
|
bp = map.toCompiled(bp);
|
||||||
if (bp != null) newBreakpoints.add(bp);
|
if (bp != null) newBreakpoints.add(bp);
|
||||||
}
|
}
|
||||||
breakpoints = newBreakpoints;
|
breakpoints = newBreakpoints;
|
||||||
}
|
}
|
||||||
|
|
||||||
engine.onSource(filename, raw, breakpoints, map);
|
engine.onSource(filename, raw, breakpoints, map);
|
||||||
|
|
||||||
return function;
|
return function;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void pushFrame(CodeFrame frame) {
|
public void pushFrame(CodeFrame frame) {
|
||||||
frames.add(frame);
|
frames.add(frame);
|
||||||
if (frames.size() > engine.maxStackFrames) throw EngineException.ofRange("Stack overflow!");
|
if (frames.size() > engine.maxStackFrames) throw EngineException.ofRange("Stack overflow!");
|
||||||
pushEnv(frame.function.environment);
|
pushEnv(frame.function.environment);
|
||||||
engine.onFramePush(this, frame);
|
engine.onFramePush(this, frame);
|
||||||
}
|
}
|
||||||
public boolean popFrame(CodeFrame frame) {
|
public boolean popFrame(CodeFrame frame) {
|
||||||
if (frames.size() == 0) return false;
|
if (frames.size() == 0) return false;
|
||||||
if (frames.get(frames.size() - 1) != frame) return false;
|
if (frames.get(frames.size() - 1) != frame) return false;
|
||||||
frames.remove(frames.size() - 1);
|
frames.remove(frames.size() - 1);
|
||||||
popEnv();
|
popEnv();
|
||||||
engine.onFramePop(this, frame);
|
engine.onFramePop(this, frame);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
public CodeFrame peekFrame() {
|
public CodeFrame peekFrame() {
|
||||||
if (frames.size() == 0) return null;
|
if (frames.size() == 0) return null;
|
||||||
return frames.get(frames.size() - 1);
|
return frames.get(frames.size() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<CodeFrame> frames() {
|
public List<CodeFrame> frames() {
|
||||||
return Collections.unmodifiableList(frames);
|
return Collections.unmodifiableList(frames);
|
||||||
}
|
}
|
||||||
public List<String> stackTrace() {
|
public List<String> stackTrace() {
|
||||||
var res = new ArrayList<String>();
|
var res = new ArrayList<String>();
|
||||||
|
|
||||||
for (var i = frames.size() - 1; i >= 0; i--) {
|
for (var i = frames.size() - 1; i >= 0; i--) {
|
||||||
var el = frames.get(i);
|
var el = frames.get(i);
|
||||||
var name = el.function.name;
|
var name = el.function.name;
|
||||||
Location loc = null;
|
Location loc = null;
|
||||||
|
|
||||||
for (var j = el.codePtr; j >= 0 && loc == null; j--) loc = el.function.body[j].location;
|
for (var j = el.codePtr; j >= 0 && loc == null; j--) loc = el.function.body[j].location;
|
||||||
if (loc == null) loc = el.function.loc();
|
if (loc == null) loc = el.function.loc();
|
||||||
|
|
||||||
var trace = "";
|
var trace = "";
|
||||||
|
|
||||||
if (loc != null) trace += "at " + loc.toString() + " ";
|
if (loc != null) trace += "at " + loc.toString() + " ";
|
||||||
if (name != null && !name.equals("")) trace += "in " + name + " ";
|
if (name != null && !name.equals("")) trace += "in " + name + " ";
|
||||||
|
|
||||||
trace = trace.trim();
|
trace = trace.trim();
|
||||||
|
|
||||||
if (!trace.equals("")) res.add(trace);
|
if (!trace.equals("")) res.add(trace);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Context(Engine engine) {
|
public Context(Engine engine) {
|
||||||
this.engine = engine;
|
this.engine = engine;
|
||||||
}
|
}
|
||||||
public Context(Engine engine, Environment env) {
|
public Context(Engine engine, Environment env) {
|
||||||
this(engine);
|
this(engine);
|
||||||
this.pushEnv(env);
|
this.pushEnv(env);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,175 +1,175 @@
|
|||||||
package me.topchetoeu.jscript.engine;
|
package me.topchetoeu.jscript.engine;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
import java.util.concurrent.PriorityBlockingQueue;
|
import java.util.concurrent.PriorityBlockingQueue;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Filename;
|
import me.topchetoeu.jscript.Filename;
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.FunctionBody;
|
import me.topchetoeu.jscript.compilation.FunctionBody;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.engine.debug.DebugController;
|
import me.topchetoeu.jscript.engine.debug.DebugController;
|
||||||
import me.topchetoeu.jscript.engine.frame.CodeFrame;
|
import me.topchetoeu.jscript.engine.frame.CodeFrame;
|
||||||
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
||||||
import me.topchetoeu.jscript.events.Awaitable;
|
import me.topchetoeu.jscript.events.Awaitable;
|
||||||
import me.topchetoeu.jscript.events.DataNotifier;
|
import me.topchetoeu.jscript.events.DataNotifier;
|
||||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
import me.topchetoeu.jscript.exceptions.InterruptException;
|
import me.topchetoeu.jscript.exceptions.InterruptException;
|
||||||
import me.topchetoeu.jscript.mapping.SourceMap;
|
import me.topchetoeu.jscript.mapping.SourceMap;
|
||||||
|
|
||||||
public class Engine implements DebugController {
|
public class Engine implements DebugController {
|
||||||
private class UncompiledFunction extends FunctionValue {
|
private class UncompiledFunction extends FunctionValue {
|
||||||
public final Filename filename;
|
public final Filename filename;
|
||||||
public final String raw;
|
public final String raw;
|
||||||
private FunctionValue compiled = null;
|
private FunctionValue compiled = null;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object call(Context ctx, Object thisArg, Object ...args) {
|
public Object call(Context ctx, Object thisArg, Object ...args) {
|
||||||
if (compiled == null) compiled = ctx.compile(filename, raw);
|
if (compiled == null) compiled = ctx.compile(filename, raw);
|
||||||
return compiled.call(ctx, thisArg, args);
|
return compiled.call(ctx, thisArg, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public UncompiledFunction(Filename filename, String raw) {
|
public UncompiledFunction(Filename filename, String raw) {
|
||||||
super(filename + "", 0);
|
super(filename + "", 0);
|
||||||
this.filename = filename;
|
this.filename = filename;
|
||||||
this.raw = raw;
|
this.raw = raw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class Task implements Comparable<Task> {
|
private static class Task implements Comparable<Task> {
|
||||||
public final FunctionValue func;
|
public final FunctionValue func;
|
||||||
public final Object thisArg;
|
public final Object thisArg;
|
||||||
public final Object[] args;
|
public final Object[] args;
|
||||||
public final DataNotifier<Object> notifier = new DataNotifier<>();
|
public final DataNotifier<Object> notifier = new DataNotifier<>();
|
||||||
public final Context ctx;
|
public final Context ctx;
|
||||||
public final boolean micro;
|
public final boolean micro;
|
||||||
|
|
||||||
public Task(Context ctx, FunctionValue func, Object thisArg, Object[] args, boolean micro) {
|
public Task(Context ctx, FunctionValue func, Object thisArg, Object[] args, boolean micro) {
|
||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
this.func = func;
|
this.func = func;
|
||||||
this.thisArg = thisArg;
|
this.thisArg = thisArg;
|
||||||
this.args = args;
|
this.args = args;
|
||||||
this.micro = micro;
|
this.micro = micro;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(Task other) {
|
public int compareTo(Task other) {
|
||||||
return Integer.compare(this.micro ? 0 : 1, other.micro ? 0 : 1);
|
return Integer.compare(this.micro ? 0 : 1, other.micro ? 0 : 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int nextId = 0;
|
private static int nextId = 0;
|
||||||
public static final HashMap<Long, FunctionBody> functions = new HashMap<>();
|
public static final HashMap<Long, FunctionBody> functions = new HashMap<>();
|
||||||
|
|
||||||
public final int id = ++nextId;
|
public final int id = ++nextId;
|
||||||
public final boolean debugging;
|
public final boolean debugging;
|
||||||
public int maxStackFrames = 10000;
|
public int maxStackFrames = 10000;
|
||||||
|
|
||||||
private final HashMap<Filename, String> sources = new HashMap<>();
|
private final HashMap<Filename, String> sources = new HashMap<>();
|
||||||
private final HashMap<Filename, TreeSet<Location>> bpts = new HashMap<>();
|
private final HashMap<Filename, TreeSet<Location>> bpts = new HashMap<>();
|
||||||
private final HashMap<Filename, SourceMap> maps = new HashMap<>();
|
private final HashMap<Filename, SourceMap> maps = new HashMap<>();
|
||||||
|
|
||||||
public Location mapToCompiled(Location location) {
|
public Location mapToCompiled(Location location) {
|
||||||
var map = maps.get(location.filename());
|
var map = maps.get(location.filename());
|
||||||
if (map == null) return location;
|
if (map == null) return location;
|
||||||
return map.toCompiled(location);
|
return map.toCompiled(location);
|
||||||
}
|
}
|
||||||
public Location mapToOriginal(Location location) {
|
public Location mapToOriginal(Location location) {
|
||||||
var map = maps.get(location.filename());
|
var map = maps.get(location.filename());
|
||||||
if (map == null) return location;
|
if (map == null) return location;
|
||||||
return map.toOriginal(location);
|
return map.toOriginal(location);
|
||||||
}
|
}
|
||||||
|
|
||||||
private DebugController debugger;
|
private DebugController debugger;
|
||||||
private Thread thread;
|
private Thread thread;
|
||||||
private PriorityBlockingQueue<Task> tasks = new PriorityBlockingQueue<>();
|
private PriorityBlockingQueue<Task> tasks = new PriorityBlockingQueue<>();
|
||||||
|
|
||||||
public synchronized boolean attachDebugger(DebugController debugger) {
|
public synchronized boolean attachDebugger(DebugController debugger) {
|
||||||
if (!debugging || this.debugger != null) return false;
|
if (!debugging || this.debugger != null) return false;
|
||||||
|
|
||||||
for (var source : sources.entrySet()) debugger.onSource(
|
for (var source : sources.entrySet()) debugger.onSource(
|
||||||
source.getKey(), source.getValue(),
|
source.getKey(), source.getValue(),
|
||||||
bpts.get(source.getKey()),
|
bpts.get(source.getKey()),
|
||||||
maps.get(source.getKey())
|
maps.get(source.getKey())
|
||||||
);
|
);
|
||||||
|
|
||||||
this.debugger = debugger;
|
this.debugger = debugger;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
public synchronized boolean detachDebugger() {
|
public synchronized boolean detachDebugger() {
|
||||||
if (!debugging || this.debugger == null) return false;
|
if (!debugging || this.debugger == null) return false;
|
||||||
this.debugger = null;
|
this.debugger = null;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void runTask(Task task) {
|
private void runTask(Task task) {
|
||||||
try {
|
try {
|
||||||
task.notifier.next(task.func.call(task.ctx, task.thisArg, task.args));
|
task.notifier.next(task.func.call(task.ctx, task.thisArg, task.args));
|
||||||
}
|
}
|
||||||
catch (RuntimeException e) {
|
catch (RuntimeException e) {
|
||||||
if (e instanceof InterruptException) throw e;
|
if (e instanceof InterruptException) throw e;
|
||||||
task.notifier.error(e);
|
task.notifier.error(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public void run(boolean untilEmpty) {
|
public void run(boolean untilEmpty) {
|
||||||
while (!untilEmpty || !tasks.isEmpty()) {
|
while (!untilEmpty || !tasks.isEmpty()) {
|
||||||
try {
|
try {
|
||||||
runTask(tasks.take());
|
runTask(tasks.take());
|
||||||
}
|
}
|
||||||
catch (InterruptedException | InterruptException e) {
|
catch (InterruptedException | InterruptException e) {
|
||||||
for (var msg : tasks) msg.notifier.error(new InterruptException(e));
|
for (var msg : tasks) msg.notifier.error(new InterruptException(e));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Thread start() {
|
public Thread start() {
|
||||||
if (this.thread == null) {
|
if (this.thread == null) {
|
||||||
this.thread = new Thread(() -> run(false), "JavaScript Runner #" + id);
|
this.thread = new Thread(() -> run(false), "JavaScript Runner #" + id);
|
||||||
this.thread.start();
|
this.thread.start();
|
||||||
}
|
}
|
||||||
return this.thread;
|
return this.thread;
|
||||||
}
|
}
|
||||||
public void stop() {
|
public void stop() {
|
||||||
thread.interrupt();
|
thread.interrupt();
|
||||||
thread = null;
|
thread = null;
|
||||||
}
|
}
|
||||||
public boolean inExecThread() {
|
public boolean inExecThread() {
|
||||||
return Thread.currentThread() == thread;
|
return Thread.currentThread() == thread;
|
||||||
}
|
}
|
||||||
public synchronized boolean isRunning() {
|
public synchronized boolean isRunning() {
|
||||||
return this.thread != null;
|
return this.thread != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Awaitable<Object> pushMsg(boolean micro, Context ctx, FunctionValue func, Object thisArg, Object ...args) {
|
public Awaitable<Object> pushMsg(boolean micro, Context ctx, FunctionValue func, Object thisArg, Object ...args) {
|
||||||
var msg = new Task(ctx == null ? new Context(this) : ctx, func, thisArg, args, micro);
|
var msg = new Task(ctx == null ? new Context(this) : ctx, func, thisArg, args, micro);
|
||||||
tasks.add(msg);
|
tasks.add(msg);
|
||||||
return msg.notifier;
|
return msg.notifier;
|
||||||
}
|
}
|
||||||
public Awaitable<Object> pushMsg(boolean micro, Context ctx, Filename filename, String raw, Object thisArg, Object ...args) {
|
public Awaitable<Object> pushMsg(boolean micro, Context ctx, Filename filename, String raw, Object thisArg, Object ...args) {
|
||||||
return pushMsg(micro, ctx, new UncompiledFunction(filename, raw), thisArg, args);
|
return pushMsg(micro, ctx, new UncompiledFunction(filename, raw), thisArg, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFramePush(Context ctx, CodeFrame frame) {
|
public void onFramePush(Context ctx, CodeFrame frame) {
|
||||||
if (debugging && debugger != null) debugger.onFramePush(ctx, frame);
|
if (debugging && debugger != null) debugger.onFramePush(ctx, frame);
|
||||||
}
|
}
|
||||||
@Override public void onFramePop(Context ctx, CodeFrame frame) {
|
@Override public void onFramePop(Context ctx, CodeFrame frame) {
|
||||||
if (debugging && debugger != null) debugger.onFramePop(ctx, frame);
|
if (debugging && debugger != null) debugger.onFramePop(ctx, frame);
|
||||||
}
|
}
|
||||||
@Override public boolean onInstruction(Context ctx, CodeFrame frame, Instruction instruction, Object returnVal, EngineException error, boolean caught) {
|
@Override public boolean onInstruction(Context ctx, CodeFrame frame, Instruction instruction, Object returnVal, EngineException error, boolean caught) {
|
||||||
if (debugging && debugger != null) return debugger.onInstruction(ctx, frame, instruction, returnVal, error, caught);
|
if (debugging && debugger != null) return debugger.onInstruction(ctx, frame, instruction, returnVal, error, caught);
|
||||||
else return false;
|
else return false;
|
||||||
}
|
}
|
||||||
@Override public void onSource(Filename filename, String source, TreeSet<Location> breakpoints, SourceMap map) {
|
@Override public void onSource(Filename filename, String source, TreeSet<Location> breakpoints, SourceMap map) {
|
||||||
if (!debugging) return;
|
if (!debugging) return;
|
||||||
if (debugger != null) debugger.onSource(filename, source, breakpoints, map);
|
if (debugger != null) debugger.onSource(filename, source, breakpoints, map);
|
||||||
sources.put(filename, source);
|
sources.put(filename, source);
|
||||||
bpts.put(filename, breakpoints);
|
bpts.put(filename, breakpoints);
|
||||||
maps.put(filename, map);
|
maps.put(filename, map);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Engine(boolean debugging) {
|
public Engine(boolean debugging) {
|
||||||
this.debugging = debugging;
|
this.debugging = debugging;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,131 +1,131 @@
|
|||||||
package me.topchetoeu.jscript.engine;
|
package me.topchetoeu.jscript.engine;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Filename;
|
import me.topchetoeu.jscript.Filename;
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.engine.scope.GlobalScope;
|
import me.topchetoeu.jscript.engine.scope.GlobalScope;
|
||||||
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
||||||
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
||||||
import me.topchetoeu.jscript.engine.values.NativeFunction;
|
import me.topchetoeu.jscript.engine.values.NativeFunction;
|
||||||
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
import me.topchetoeu.jscript.engine.values.Symbol;
|
import me.topchetoeu.jscript.engine.values.Symbol;
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
import me.topchetoeu.jscript.filesystem.RootFilesystem;
|
import me.topchetoeu.jscript.filesystem.RootFilesystem;
|
||||||
import me.topchetoeu.jscript.interop.Native;
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
import me.topchetoeu.jscript.interop.NativeGetter;
|
import me.topchetoeu.jscript.interop.NativeGetter;
|
||||||
import me.topchetoeu.jscript.interop.NativeSetter;
|
import me.topchetoeu.jscript.interop.NativeSetter;
|
||||||
import me.topchetoeu.jscript.interop.NativeWrapperProvider;
|
import me.topchetoeu.jscript.interop.NativeWrapperProvider;
|
||||||
import me.topchetoeu.jscript.parsing.Parsing;
|
import me.topchetoeu.jscript.parsing.Parsing;
|
||||||
import me.topchetoeu.jscript.permissions.Permission;
|
import me.topchetoeu.jscript.permissions.Permission;
|
||||||
import me.topchetoeu.jscript.permissions.PermissionsProvider;
|
import me.topchetoeu.jscript.permissions.PermissionsProvider;
|
||||||
|
|
||||||
public class Environment implements PermissionsProvider {
|
public class Environment implements PermissionsProvider {
|
||||||
private HashMap<String, ObjectValue> prototypes = new HashMap<>();
|
private HashMap<String, ObjectValue> prototypes = new HashMap<>();
|
||||||
|
|
||||||
public final Data data = new Data();
|
public final Data data = new Data();
|
||||||
public static final HashMap<String, Symbol> symbols = new HashMap<>();
|
public static final HashMap<String, Symbol> symbols = new HashMap<>();
|
||||||
|
|
||||||
public GlobalScope global;
|
public GlobalScope global;
|
||||||
public WrappersProvider wrappers;
|
public WrappersProvider wrappers;
|
||||||
public PermissionsProvider permissions = null;
|
public PermissionsProvider permissions = null;
|
||||||
public final RootFilesystem filesystem = new RootFilesystem(this);
|
public final RootFilesystem filesystem = new RootFilesystem(this);
|
||||||
|
|
||||||
private static int nextId = 0;
|
private static int nextId = 0;
|
||||||
|
|
||||||
@Native public boolean stackVisible = true;
|
@Native public boolean stackVisible = true;
|
||||||
@Native public int id = ++nextId;
|
@Native public int id = ++nextId;
|
||||||
|
|
||||||
@Native public FunctionValue compile = new NativeFunction("compile", (ctx, thisArg, args) -> {
|
@Native public FunctionValue compile = new NativeFunction("compile", (ctx, thisArg, args) -> {
|
||||||
var source = Values.toString(ctx, args[0]);
|
var source = Values.toString(ctx, args[0]);
|
||||||
var filename = Values.toString(ctx, args[1]);
|
var filename = Values.toString(ctx, args[1]);
|
||||||
var isDebug = Values.toBoolean(args[2]);
|
var isDebug = Values.toBoolean(args[2]);
|
||||||
|
|
||||||
var env = Values.wrapper(args[2], Environment.class);
|
var env = Values.wrapper(args[2], Environment.class);
|
||||||
var res = new ObjectValue();
|
var res = new ObjectValue();
|
||||||
|
|
||||||
var target = Parsing.compile(env, Filename.parse(filename), source);
|
var target = Parsing.compile(env, Filename.parse(filename), source);
|
||||||
Engine.functions.putAll(target.functions);
|
Engine.functions.putAll(target.functions);
|
||||||
Engine.functions.remove(0l);
|
Engine.functions.remove(0l);
|
||||||
|
|
||||||
res.defineProperty(ctx, "function", target.func(env));
|
res.defineProperty(ctx, "function", target.func(env));
|
||||||
res.defineProperty(ctx, "mapChain", new ArrayValue());
|
res.defineProperty(ctx, "mapChain", new ArrayValue());
|
||||||
|
|
||||||
|
|
||||||
if (isDebug) {
|
if (isDebug) {
|
||||||
res.defineProperty(ctx, "breakpoints", ArrayValue.of(ctx, target.breakpoints.stream().map(Location::toString).collect(Collectors.toList())));
|
res.defineProperty(ctx, "breakpoints", ArrayValue.of(ctx, target.breakpoints.stream().map(Location::toString).collect(Collectors.toList())));
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
});
|
});
|
||||||
@Native public FunctionValue regexConstructor = new NativeFunction("RegExp", (ctx, thisArg, args) -> {
|
@Native public FunctionValue regexConstructor = new NativeFunction("RegExp", (ctx, thisArg, args) -> {
|
||||||
throw EngineException.ofError("Regular expressions not supported.").setCtx(ctx.environment(), ctx.engine);
|
throw EngineException.ofError("Regular expressions not supported.").setCtx(ctx.environment(), ctx.engine);
|
||||||
});
|
});
|
||||||
|
|
||||||
public Environment addData(Data data) {
|
public Environment addData(Data data) {
|
||||||
this.data.addAll(data);
|
this.data.addAll(data);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Native public ObjectValue proto(String name) {
|
@Native public ObjectValue proto(String name) {
|
||||||
return prototypes.get(name);
|
return prototypes.get(name);
|
||||||
}
|
}
|
||||||
@Native public void setProto(String name, ObjectValue val) {
|
@Native public void setProto(String name, ObjectValue val) {
|
||||||
prototypes.put(name, val);
|
prototypes.put(name, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Native public Symbol symbol(String name) {
|
@Native public Symbol symbol(String name) {
|
||||||
return getSymbol(name);
|
return getSymbol(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NativeGetter("global") public ObjectValue getGlobal() {
|
@NativeGetter("global") public ObjectValue getGlobal() {
|
||||||
return global.obj;
|
return global.obj;
|
||||||
}
|
}
|
||||||
@NativeSetter("global") public void setGlobal(ObjectValue val) {
|
@NativeSetter("global") public void setGlobal(ObjectValue val) {
|
||||||
global = new GlobalScope(val);
|
global = new GlobalScope(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Native public Environment fork() {
|
@Native public Environment fork() {
|
||||||
var res = new Environment(compile, null, global);
|
var res = new Environment(compile, null, global);
|
||||||
res.wrappers = wrappers.fork(res);
|
res.wrappers = wrappers.fork(res);
|
||||||
res.regexConstructor = regexConstructor;
|
res.regexConstructor = regexConstructor;
|
||||||
res.prototypes = new HashMap<>(prototypes);
|
res.prototypes = new HashMap<>(prototypes);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@Native public Environment child() {
|
@Native public Environment child() {
|
||||||
var res = fork();
|
var res = fork();
|
||||||
res.global = res.global.globalChild();
|
res.global = res.global.globalChild();
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public boolean hasPermission(Permission perm, char delim) {
|
@Override public boolean hasPermission(Permission perm, char delim) {
|
||||||
return permissions == null || permissions.hasPermission(perm, delim);
|
return permissions == null || permissions.hasPermission(perm, delim);
|
||||||
}
|
}
|
||||||
@Override public boolean hasPermission(Permission perm) {
|
@Override public boolean hasPermission(Permission perm) {
|
||||||
return permissions == null || permissions.hasPermission(perm);
|
return permissions == null || permissions.hasPermission(perm);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Context context(Engine engine) {
|
public Context context(Engine engine) {
|
||||||
return new Context(engine).pushEnv(this);
|
return new Context(engine).pushEnv(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Symbol getSymbol(String name) {
|
public static Symbol getSymbol(String name) {
|
||||||
if (symbols.containsKey(name)) return symbols.get(name);
|
if (symbols.containsKey(name)) return symbols.get(name);
|
||||||
else {
|
else {
|
||||||
var res = new Symbol(name);
|
var res = new Symbol(name);
|
||||||
symbols.put(name, res);
|
symbols.put(name, res);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Environment(FunctionValue compile, WrappersProvider nativeConverter, GlobalScope global) {
|
public Environment(FunctionValue compile, WrappersProvider nativeConverter, GlobalScope global) {
|
||||||
if (compile != null) this.compile = compile;
|
if (compile != null) this.compile = compile;
|
||||||
if (nativeConverter == null) nativeConverter = new NativeWrapperProvider(this);
|
if (nativeConverter == null) nativeConverter = new NativeWrapperProvider(this);
|
||||||
if (global == null) global = new GlobalScope();
|
if (global == null) global = new GlobalScope();
|
||||||
|
|
||||||
this.wrappers = nativeConverter;
|
this.wrappers = nativeConverter;
|
||||||
this.global = global;
|
this.global = global;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,51 +1,51 @@
|
|||||||
package me.topchetoeu.jscript.engine.debug;
|
package me.topchetoeu.jscript.engine.debug;
|
||||||
|
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Filename;
|
import me.topchetoeu.jscript.Filename;
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
import me.topchetoeu.jscript.engine.frame.CodeFrame;
|
import me.topchetoeu.jscript.engine.frame.CodeFrame;
|
||||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
import me.topchetoeu.jscript.mapping.SourceMap;
|
import me.topchetoeu.jscript.mapping.SourceMap;
|
||||||
|
|
||||||
public interface DebugController {
|
public interface DebugController {
|
||||||
/**
|
/**
|
||||||
* Called when a script has been loaded
|
* Called when a script has been loaded
|
||||||
* @param filename The name of the source
|
* @param filename The name of the source
|
||||||
* @param source The name of the source
|
* @param source The name of the source
|
||||||
* @param breakpoints A set of all the breakpointable locations in this source
|
* @param breakpoints A set of all the breakpointable locations in this source
|
||||||
* @param map The source map associated with this file. null if this source map isn't mapped
|
* @param map The source map associated with this file. null if this source map isn't mapped
|
||||||
*/
|
*/
|
||||||
void onSource(Filename filename, String source, TreeSet<Location> breakpoints, SourceMap map);
|
void onSource(Filename filename, String source, TreeSet<Location> breakpoints, SourceMap map);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called immediatly before an instruction is executed, as well as after an instruction, if it has threw or returned.
|
* Called immediatly before an instruction is executed, as well as after an instruction, if it has threw or returned.
|
||||||
* This function might pause in order to await debugging commands.
|
* This function might pause in order to await debugging commands.
|
||||||
* @param ctx The context of execution
|
* @param ctx The context of execution
|
||||||
* @param frame The frame in which execution is occuring
|
* @param frame The frame in which execution is occuring
|
||||||
* @param instruction The instruction which was or will be executed
|
* @param instruction The instruction which was or will be executed
|
||||||
* @param loc The most recent location the code frame has been at
|
* @param loc The most recent location the code frame has been at
|
||||||
* @param returnVal The return value of the instruction, Runners.NO_RETURN if none
|
* @param returnVal The return value of the instruction, Runners.NO_RETURN if none
|
||||||
* @param error The error that the instruction threw, null if none
|
* @param error The error that the instruction threw, null if none
|
||||||
* @param caught Whether or not the error has been caught
|
* @param caught Whether or not the error has been caught
|
||||||
* @return Whether or not the frame should restart
|
* @return Whether or not the frame should restart
|
||||||
*/
|
*/
|
||||||
boolean onInstruction(Context ctx, CodeFrame frame, Instruction instruction, Object returnVal, EngineException error, boolean caught);
|
boolean onInstruction(Context ctx, CodeFrame frame, Instruction instruction, Object returnVal, EngineException error, boolean caught);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called immediatly before a frame has been pushed on the frame stack.
|
* Called immediatly before a frame has been pushed on the frame stack.
|
||||||
* This function might pause in order to await debugging commands.
|
* This function might pause in order to await debugging commands.
|
||||||
* @param ctx The context of execution
|
* @param ctx The context of execution
|
||||||
* @param frame The code frame which was pushed
|
* @param frame The code frame which was pushed
|
||||||
*/
|
*/
|
||||||
void onFramePush(Context ctx, CodeFrame frame);
|
void onFramePush(Context ctx, CodeFrame frame);
|
||||||
/**
|
/**
|
||||||
* Called immediatly after a frame has been popped out of the frame stack.
|
* Called immediatly after a frame has been popped out of the frame stack.
|
||||||
* This function might pause in order to await debugging commands.
|
* This function might pause in order to await debugging commands.
|
||||||
* @param ctx The context of execution
|
* @param ctx The context of execution
|
||||||
* @param frame The code frame which was popped out
|
* @param frame The code frame which was popped out
|
||||||
*/
|
*/
|
||||||
void onFramePop(Context ctx, CodeFrame frame);
|
void onFramePop(Context ctx, CodeFrame frame);
|
||||||
}
|
}
|
||||||
|
@ -1,34 +1,34 @@
|
|||||||
package me.topchetoeu.jscript.engine.debug;
|
package me.topchetoeu.jscript.engine.debug;
|
||||||
|
|
||||||
public interface DebugHandler {
|
public interface DebugHandler {
|
||||||
void enable(V8Message msg);
|
void enable(V8Message msg);
|
||||||
void disable(V8Message msg);
|
void disable(V8Message msg);
|
||||||
|
|
||||||
void setBreakpointByUrl(V8Message msg);
|
void setBreakpointByUrl(V8Message msg);
|
||||||
void removeBreakpoint(V8Message msg);
|
void removeBreakpoint(V8Message msg);
|
||||||
void continueToLocation(V8Message msg);
|
void continueToLocation(V8Message msg);
|
||||||
|
|
||||||
void getScriptSource(V8Message msg);
|
void getScriptSource(V8Message msg);
|
||||||
void getPossibleBreakpoints(V8Message msg);
|
void getPossibleBreakpoints(V8Message msg);
|
||||||
|
|
||||||
void resume(V8Message msg);
|
void resume(V8Message msg);
|
||||||
void pause(V8Message msg);
|
void pause(V8Message msg);
|
||||||
|
|
||||||
void stepInto(V8Message msg);
|
void stepInto(V8Message msg);
|
||||||
void stepOut(V8Message msg);
|
void stepOut(V8Message msg);
|
||||||
void stepOver(V8Message msg);
|
void stepOver(V8Message msg);
|
||||||
|
|
||||||
void setPauseOnExceptions(V8Message msg);
|
void setPauseOnExceptions(V8Message msg);
|
||||||
|
|
||||||
void evaluateOnCallFrame(V8Message msg);
|
void evaluateOnCallFrame(V8Message msg);
|
||||||
|
|
||||||
void getProperties(V8Message msg);
|
void getProperties(V8Message msg);
|
||||||
void releaseObjectGroup(V8Message msg);
|
void releaseObjectGroup(V8Message msg);
|
||||||
void releaseObject(V8Message msg);
|
void releaseObject(V8Message msg);
|
||||||
/**
|
/**
|
||||||
* This method might not execute the actual code for well-known requests
|
* This method might not execute the actual code for well-known requests
|
||||||
*/
|
*/
|
||||||
void callFunctionOn(V8Message msg);
|
void callFunctionOn(V8Message msg);
|
||||||
|
|
||||||
void runtimeEnable(V8Message msg);
|
void runtimeEnable(V8Message msg);
|
||||||
}
|
}
|
||||||
|
@ -1,245 +1,245 @@
|
|||||||
package me.topchetoeu.jscript.engine.debug;
|
package me.topchetoeu.jscript.engine.debug;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.ServerSocket;
|
import java.net.ServerSocket;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Metadata;
|
import me.topchetoeu.jscript.Metadata;
|
||||||
import me.topchetoeu.jscript.Reading;
|
import me.topchetoeu.jscript.Reading;
|
||||||
import me.topchetoeu.jscript.engine.debug.WebSocketMessage.Type;
|
import me.topchetoeu.jscript.engine.debug.WebSocketMessage.Type;
|
||||||
import me.topchetoeu.jscript.events.Notifier;
|
import me.topchetoeu.jscript.events.Notifier;
|
||||||
import me.topchetoeu.jscript.exceptions.SyntaxException;
|
import me.topchetoeu.jscript.exceptions.SyntaxException;
|
||||||
import me.topchetoeu.jscript.exceptions.UncheckedException;
|
import me.topchetoeu.jscript.exceptions.UncheckedException;
|
||||||
import me.topchetoeu.jscript.exceptions.UncheckedIOException;
|
import me.topchetoeu.jscript.exceptions.UncheckedIOException;
|
||||||
import me.topchetoeu.jscript.json.JSON;
|
import me.topchetoeu.jscript.json.JSON;
|
||||||
import me.topchetoeu.jscript.json.JSONList;
|
import me.topchetoeu.jscript.json.JSONList;
|
||||||
import me.topchetoeu.jscript.json.JSONMap;
|
import me.topchetoeu.jscript.json.JSONMap;
|
||||||
|
|
||||||
public class DebugServer {
|
public class DebugServer {
|
||||||
public static String browserDisplayName = Metadata.name() + "/" + Metadata.version();
|
public static String browserDisplayName = Metadata.name() + "/" + Metadata.version();
|
||||||
|
|
||||||
public final HashMap<String, DebuggerProvider> targets = new HashMap<>();
|
public final HashMap<String, DebuggerProvider> targets = new HashMap<>();
|
||||||
|
|
||||||
private final byte[] favicon, index, protocol;
|
private final byte[] favicon, index, protocol;
|
||||||
private final Notifier connNotifier = new Notifier();
|
private final Notifier connNotifier = new Notifier();
|
||||||
|
|
||||||
private static void send(HttpRequest req, String val) throws IOException {
|
private static void send(HttpRequest req, String val) throws IOException {
|
||||||
req.writeResponse(200, "OK", "application/json", val.getBytes());
|
req.writeResponse(200, "OK", "application/json", val.getBytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
// SILENCE JAVA
|
// SILENCE JAVA
|
||||||
private MessageDigest getDigestInstance() {
|
private MessageDigest getDigestInstance() {
|
||||||
try {
|
try {
|
||||||
return MessageDigest.getInstance("sha1");
|
return MessageDigest.getInstance("sha1");
|
||||||
}
|
}
|
||||||
catch (Throwable e) { throw new UncheckedException(e); }
|
catch (Throwable e) { throw new UncheckedException(e); }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Thread runAsync(Runnable func, String name) {
|
private static Thread runAsync(Runnable func, String name) {
|
||||||
var res = new Thread(func);
|
var res = new Thread(func);
|
||||||
res.setName(name);
|
res.setName(name);
|
||||||
res.start();
|
res.start();
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handle(WebSocket ws, Debugger debugger) {
|
private void handle(WebSocket ws, Debugger debugger) {
|
||||||
WebSocketMessage raw;
|
WebSocketMessage raw;
|
||||||
|
|
||||||
debugger.connect();
|
debugger.connect();
|
||||||
|
|
||||||
while ((raw = ws.receive()) != null) {
|
while ((raw = ws.receive()) != null) {
|
||||||
if (raw.type != Type.Text) {
|
if (raw.type != Type.Text) {
|
||||||
ws.send(new V8Error("Expected a text message."));
|
ws.send(new V8Error("Expected a text message."));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
V8Message msg;
|
V8Message msg;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
msg = new V8Message(raw.textData());
|
msg = new V8Message(raw.textData());
|
||||||
}
|
}
|
||||||
catch (SyntaxException e) {
|
catch (SyntaxException e) {
|
||||||
ws.send(new V8Error(e.getMessage()));
|
ws.send(new V8Error(e.getMessage()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
switch (msg.name) {
|
switch (msg.name) {
|
||||||
case "Debugger.enable":
|
case "Debugger.enable":
|
||||||
connNotifier.next();
|
connNotifier.next();
|
||||||
debugger.enable(msg); continue;
|
debugger.enable(msg); continue;
|
||||||
case "Debugger.disable": debugger.disable(msg); continue;
|
case "Debugger.disable": debugger.disable(msg); continue;
|
||||||
|
|
||||||
case "Debugger.setBreakpointByUrl": debugger.setBreakpointByUrl(msg); continue;
|
case "Debugger.setBreakpointByUrl": debugger.setBreakpointByUrl(msg); continue;
|
||||||
case "Debugger.removeBreakpoint": debugger.removeBreakpoint(msg); continue;
|
case "Debugger.removeBreakpoint": debugger.removeBreakpoint(msg); continue;
|
||||||
case "Debugger.continueToLocation": debugger.continueToLocation(msg); continue;
|
case "Debugger.continueToLocation": debugger.continueToLocation(msg); continue;
|
||||||
|
|
||||||
case "Debugger.getScriptSource": debugger.getScriptSource(msg); continue;
|
case "Debugger.getScriptSource": debugger.getScriptSource(msg); continue;
|
||||||
case "Debugger.getPossibleBreakpoints": debugger.getPossibleBreakpoints(msg); continue;
|
case "Debugger.getPossibleBreakpoints": debugger.getPossibleBreakpoints(msg); continue;
|
||||||
|
|
||||||
case "Debugger.resume": debugger.resume(msg); continue;
|
case "Debugger.resume": debugger.resume(msg); continue;
|
||||||
case "Debugger.pause": debugger.pause(msg); continue;
|
case "Debugger.pause": debugger.pause(msg); continue;
|
||||||
|
|
||||||
case "Debugger.stepInto": debugger.stepInto(msg); continue;
|
case "Debugger.stepInto": debugger.stepInto(msg); continue;
|
||||||
case "Debugger.stepOut": debugger.stepOut(msg); continue;
|
case "Debugger.stepOut": debugger.stepOut(msg); continue;
|
||||||
case "Debugger.stepOver": debugger.stepOver(msg); continue;
|
case "Debugger.stepOver": debugger.stepOver(msg); continue;
|
||||||
|
|
||||||
case "Debugger.setPauseOnExceptions": debugger.setPauseOnExceptions(msg); continue;
|
case "Debugger.setPauseOnExceptions": debugger.setPauseOnExceptions(msg); continue;
|
||||||
case "Debugger.evaluateOnCallFrame": debugger.evaluateOnCallFrame(msg); continue;
|
case "Debugger.evaluateOnCallFrame": debugger.evaluateOnCallFrame(msg); continue;
|
||||||
|
|
||||||
case "Runtime.releaseObjectGroup": debugger.releaseObjectGroup(msg); continue;
|
case "Runtime.releaseObjectGroup": debugger.releaseObjectGroup(msg); continue;
|
||||||
case "Runtime.releaseObject": debugger.releaseObject(msg); continue;
|
case "Runtime.releaseObject": debugger.releaseObject(msg); continue;
|
||||||
case "Runtime.getProperties": debugger.getProperties(msg); continue;
|
case "Runtime.getProperties": debugger.getProperties(msg); continue;
|
||||||
case "Runtime.callFunctionOn": debugger.callFunctionOn(msg); continue;
|
case "Runtime.callFunctionOn": debugger.callFunctionOn(msg); continue;
|
||||||
// case "NodeWorker.enable": debugger.nodeWorkerEnable(msg); continue;
|
// case "NodeWorker.enable": debugger.nodeWorkerEnable(msg); continue;
|
||||||
case "Runtime.enable": debugger.runtimeEnable(msg); continue;
|
case "Runtime.enable": debugger.runtimeEnable(msg); continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
msg.name.startsWith("DOM.") ||
|
msg.name.startsWith("DOM.") ||
|
||||||
msg.name.startsWith("DOMDebugger.") ||
|
msg.name.startsWith("DOMDebugger.") ||
|
||||||
msg.name.startsWith("Emulation.") ||
|
msg.name.startsWith("Emulation.") ||
|
||||||
msg.name.startsWith("Input.") ||
|
msg.name.startsWith("Input.") ||
|
||||||
msg.name.startsWith("Network.") ||
|
msg.name.startsWith("Network.") ||
|
||||||
msg.name.startsWith("Page.")
|
msg.name.startsWith("Page.")
|
||||||
) ws.send(new V8Error("This isn't a browser..."));
|
) ws.send(new V8Error("This isn't a browser..."));
|
||||||
else ws.send(new V8Error("This API is not supported yet."));
|
else ws.send(new V8Error("This API is not supported yet."));
|
||||||
}
|
}
|
||||||
catch (Throwable e) {
|
catch (Throwable e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
throw new UncheckedException(e);
|
throw new UncheckedException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
debugger.disconnect();
|
debugger.disconnect();
|
||||||
}
|
}
|
||||||
private void onWsConnect(HttpRequest req, Socket socket, DebuggerProvider debuggerProvider) {
|
private void onWsConnect(HttpRequest req, Socket socket, DebuggerProvider debuggerProvider) {
|
||||||
var key = req.headers.get("sec-websocket-key");
|
var key = req.headers.get("sec-websocket-key");
|
||||||
|
|
||||||
if (key == null) {
|
if (key == null) {
|
||||||
req.writeResponse(
|
req.writeResponse(
|
||||||
426, "Upgrade Required", "text/txt",
|
426, "Upgrade Required", "text/txt",
|
||||||
"Expected a WS upgrade".getBytes()
|
"Expected a WS upgrade".getBytes()
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var resKey = Base64.getEncoder().encodeToString(getDigestInstance().digest(
|
var resKey = Base64.getEncoder().encodeToString(getDigestInstance().digest(
|
||||||
(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").getBytes()
|
(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").getBytes()
|
||||||
));
|
));
|
||||||
|
|
||||||
req.writeCode(101, "Switching Protocols");
|
req.writeCode(101, "Switching Protocols");
|
||||||
req.writeHeader("Connection", "Upgrade");
|
req.writeHeader("Connection", "Upgrade");
|
||||||
req.writeHeader("Sec-WebSocket-Accept", resKey);
|
req.writeHeader("Sec-WebSocket-Accept", resKey);
|
||||||
req.writeLastHeader("Upgrade", "WebSocket");
|
req.writeLastHeader("Upgrade", "WebSocket");
|
||||||
|
|
||||||
var ws = new WebSocket(socket);
|
var ws = new WebSocket(socket);
|
||||||
var debugger = debuggerProvider.getDebugger(ws, req);
|
var debugger = debuggerProvider.getDebugger(ws, req);
|
||||||
|
|
||||||
if (debugger == null) {
|
if (debugger == null) {
|
||||||
ws.close();
|
ws.close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
runAsync(() -> {
|
runAsync(() -> {
|
||||||
try { handle(ws, debugger); }
|
try { handle(ws, debugger); }
|
||||||
catch (RuntimeException e) {
|
catch (RuntimeException e) {
|
||||||
ws.send(new V8Error(e.getMessage()));
|
ws.send(new V8Error(e.getMessage()));
|
||||||
}
|
}
|
||||||
finally { ws.close(); debugger.disconnect(); }
|
finally { ws.close(); debugger.disconnect(); }
|
||||||
}, "Debug Handler");
|
}, "Debug Handler");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void awaitConnection() {
|
public void awaitConnection() {
|
||||||
connNotifier.await();
|
connNotifier.await();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void run(InetSocketAddress address) {
|
public void run(InetSocketAddress address) {
|
||||||
try {
|
try {
|
||||||
ServerSocket server = new ServerSocket();
|
ServerSocket server = new ServerSocket();
|
||||||
server.bind(address);
|
server.bind(address);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
while (true) {
|
while (true) {
|
||||||
var socket = server.accept();
|
var socket = server.accept();
|
||||||
var req = HttpRequest.read(socket);
|
var req = HttpRequest.read(socket);
|
||||||
|
|
||||||
if (req == null) continue;
|
if (req == null) continue;
|
||||||
|
|
||||||
switch (req.path) {
|
switch (req.path) {
|
||||||
case "/json/version":
|
case "/json/version":
|
||||||
send(req, "{\"Browser\":\"" + browserDisplayName + "\",\"Protocol-Version\":\"1.1\"}");
|
send(req, "{\"Browser\":\"" + browserDisplayName + "\",\"Protocol-Version\":\"1.1\"}");
|
||||||
break;
|
break;
|
||||||
case "/json/list":
|
case "/json/list":
|
||||||
case "/json": {
|
case "/json": {
|
||||||
var res = new JSONList();
|
var res = new JSONList();
|
||||||
|
|
||||||
for (var el : targets.entrySet()) {
|
for (var el : targets.entrySet()) {
|
||||||
res.add(new JSONMap()
|
res.add(new JSONMap()
|
||||||
.set("description", "JScript debugger")
|
.set("description", "JScript debugger")
|
||||||
.set("favicon", "/favicon.ico")
|
.set("favicon", "/favicon.ico")
|
||||||
.set("id", el.getKey())
|
.set("id", el.getKey())
|
||||||
.set("type", "node")
|
.set("type", "node")
|
||||||
.set("webSocketDebuggerUrl", "ws://" + address.getHostString() + ":" + address.getPort() + "/" + el.getKey())
|
.set("webSocketDebuggerUrl", "ws://" + address.getHostString() + ":" + address.getPort() + "/" + el.getKey())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
send(req, JSON.stringify(res));
|
send(req, JSON.stringify(res));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "/json/protocol":
|
case "/json/protocol":
|
||||||
req.writeResponse(200, "OK", "application/json", protocol);
|
req.writeResponse(200, "OK", "application/json", protocol);
|
||||||
break;
|
break;
|
||||||
case "/json/new":
|
case "/json/new":
|
||||||
case "/json/activate":
|
case "/json/activate":
|
||||||
case "/json/close":
|
case "/json/close":
|
||||||
case "/devtools/inspector.html":
|
case "/devtools/inspector.html":
|
||||||
req.writeResponse(
|
req.writeResponse(
|
||||||
501, "Not Implemented", "text/txt",
|
501, "Not Implemented", "text/txt",
|
||||||
"This feature isn't (and probably won't be) implemented.".getBytes()
|
"This feature isn't (and probably won't be) implemented.".getBytes()
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case "/":
|
case "/":
|
||||||
case "/index.html":
|
case "/index.html":
|
||||||
req.writeResponse(200, "OK", "text/html", index);
|
req.writeResponse(200, "OK", "text/html", index);
|
||||||
break;
|
break;
|
||||||
case "/favicon.ico":
|
case "/favicon.ico":
|
||||||
req.writeResponse(200, "OK", "image/png", favicon);
|
req.writeResponse(200, "OK", "image/png", favicon);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (req.path.length() > 1 && targets.containsKey(req.path.substring(1))) {
|
if (req.path.length() > 1 && targets.containsKey(req.path.substring(1))) {
|
||||||
onWsConnect(req, socket, targets.get(req.path.substring(1)));
|
onWsConnect(req, socket, targets.get(req.path.substring(1)));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally { server.close(); }
|
finally { server.close(); }
|
||||||
}
|
}
|
||||||
catch (IOException e) { throw new UncheckedIOException(e); }
|
catch (IOException e) { throw new UncheckedIOException(e); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public Thread start(InetSocketAddress address, boolean daemon) {
|
public Thread start(InetSocketAddress address, boolean daemon) {
|
||||||
var res = new Thread(() -> run(address), "Debug Server");
|
var res = new Thread(() -> run(address), "Debug Server");
|
||||||
res.setDaemon(daemon);
|
res.setDaemon(daemon);
|
||||||
res.start();
|
res.start();
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DebugServer() {
|
public DebugServer() {
|
||||||
try {
|
try {
|
||||||
this.favicon = Reading.resourceToStream("debugger/favicon.png").readAllBytes();
|
this.favicon = Reading.resourceToStream("debugger/favicon.png").readAllBytes();
|
||||||
this.protocol = Reading.resourceToStream("debugger/protocol.json").readAllBytes();
|
this.protocol = Reading.resourceToStream("debugger/protocol.json").readAllBytes();
|
||||||
this.index = Reading.resourceToString("debugger/index.html")
|
this.index = Reading.resourceToString("debugger/index.html")
|
||||||
.replace("${NAME}", Metadata.name())
|
.replace("${NAME}", Metadata.name())
|
||||||
.replace("${VERSION}", Metadata.version())
|
.replace("${VERSION}", Metadata.version())
|
||||||
.replace("${AUTHOR}", Metadata.author())
|
.replace("${AUTHOR}", Metadata.author())
|
||||||
.getBytes();
|
.getBytes();
|
||||||
}
|
}
|
||||||
catch (IOException e) { throw new UncheckedIOException(e); }
|
catch (IOException e) { throw new UncheckedIOException(e); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,317 +1,317 @@
|
|||||||
package me.topchetoeu.jscript.engine.frame;
|
package me.topchetoeu.jscript.engine.frame;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Stack;
|
import java.util.Stack;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
import me.topchetoeu.jscript.engine.scope.LocalScope;
|
import me.topchetoeu.jscript.engine.scope.LocalScope;
|
||||||
import me.topchetoeu.jscript.engine.scope.ValueVariable;
|
import me.topchetoeu.jscript.engine.scope.ValueVariable;
|
||||||
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
||||||
import me.topchetoeu.jscript.engine.values.CodeFunction;
|
import me.topchetoeu.jscript.engine.values.CodeFunction;
|
||||||
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
import me.topchetoeu.jscript.engine.values.ScopeValue;
|
import me.topchetoeu.jscript.engine.values.ScopeValue;
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
import me.topchetoeu.jscript.exceptions.InterruptException;
|
import me.topchetoeu.jscript.exceptions.InterruptException;
|
||||||
|
|
||||||
public class CodeFrame {
|
public class CodeFrame {
|
||||||
public static enum TryState {
|
public static enum TryState {
|
||||||
TRY,
|
TRY,
|
||||||
CATCH,
|
CATCH,
|
||||||
FINALLY,
|
FINALLY,
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class TryCtx {
|
public static class TryCtx {
|
||||||
public final int start, end, catchStart, finallyStart;
|
public final int start, end, catchStart, finallyStart;
|
||||||
public final int restoreStackPtr;
|
public final int restoreStackPtr;
|
||||||
public final TryState state;
|
public final TryState state;
|
||||||
public final EngineException error;
|
public final EngineException error;
|
||||||
public final PendingResult result;
|
public final PendingResult result;
|
||||||
|
|
||||||
public boolean hasCatch() { return catchStart >= 0; }
|
public boolean hasCatch() { return catchStart >= 0; }
|
||||||
public boolean hasFinally() { return finallyStart >= 0; }
|
public boolean hasFinally() { return finallyStart >= 0; }
|
||||||
|
|
||||||
public boolean inBounds(int ptr) {
|
public boolean inBounds(int ptr) {
|
||||||
return ptr >= start && ptr < end;
|
return ptr >= start && ptr < end;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TryCtx _catch(EngineException e) {
|
public TryCtx _catch(EngineException e) {
|
||||||
if (error != null) e.setCause(error);
|
if (error != null) e.setCause(error);
|
||||||
return new TryCtx(TryState.CATCH, e, result, restoreStackPtr, start, end, -1, finallyStart);
|
return new TryCtx(TryState.CATCH, e, result, restoreStackPtr, start, end, -1, finallyStart);
|
||||||
}
|
}
|
||||||
public TryCtx _finally(PendingResult res) {
|
public TryCtx _finally(PendingResult res) {
|
||||||
return new TryCtx(TryState.FINALLY, error, res, restoreStackPtr, start, end, -1, -1);
|
return new TryCtx(TryState.FINALLY, error, res, restoreStackPtr, start, end, -1, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TryCtx(TryState state, EngineException err, PendingResult res, int stackPtr, int start, int end, int catchStart, int finallyStart) {
|
public TryCtx(TryState state, EngineException err, PendingResult res, int stackPtr, int start, int end, int catchStart, int finallyStart) {
|
||||||
this.catchStart = catchStart;
|
this.catchStart = catchStart;
|
||||||
this.finallyStart = finallyStart;
|
this.finallyStart = finallyStart;
|
||||||
this.restoreStackPtr = stackPtr;
|
this.restoreStackPtr = stackPtr;
|
||||||
this.result = res == null ? PendingResult.ofNone() : res;
|
this.result = res == null ? PendingResult.ofNone() : res;
|
||||||
this.state = state;
|
this.state = state;
|
||||||
this.start = start;
|
this.start = start;
|
||||||
this.end = end;
|
this.end = end;
|
||||||
this.error = err;
|
this.error = err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class PendingResult {
|
private static class PendingResult {
|
||||||
public final boolean isReturn, isJump, isThrow;
|
public final boolean isReturn, isJump, isThrow;
|
||||||
public final Object value;
|
public final Object value;
|
||||||
public final EngineException error;
|
public final EngineException error;
|
||||||
public final int ptr;
|
public final int ptr;
|
||||||
public final Instruction instruction;
|
public final Instruction instruction;
|
||||||
|
|
||||||
private PendingResult(Instruction instr, boolean isReturn, boolean isJump, boolean isThrow, Object value, EngineException error, int ptr) {
|
private PendingResult(Instruction instr, boolean isReturn, boolean isJump, boolean isThrow, Object value, EngineException error, int ptr) {
|
||||||
this.instruction = instr;
|
this.instruction = instr;
|
||||||
this.isReturn = isReturn;
|
this.isReturn = isReturn;
|
||||||
this.isJump = isJump;
|
this.isJump = isJump;
|
||||||
this.isThrow = isThrow;
|
this.isThrow = isThrow;
|
||||||
this.value = value;
|
this.value = value;
|
||||||
this.error = error;
|
this.error = error;
|
||||||
this.ptr = ptr;
|
this.ptr = ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PendingResult ofNone() {
|
public static PendingResult ofNone() {
|
||||||
return new PendingResult(null, false, false, false, null, null, 0);
|
return new PendingResult(null, false, false, false, null, null, 0);
|
||||||
}
|
}
|
||||||
public static PendingResult ofReturn(Object value, Instruction instr) {
|
public static PendingResult ofReturn(Object value, Instruction instr) {
|
||||||
return new PendingResult(instr, true, false, false, value, null, 0);
|
return new PendingResult(instr, true, false, false, value, null, 0);
|
||||||
}
|
}
|
||||||
public static PendingResult ofThrow(EngineException error, Instruction instr) {
|
public static PendingResult ofThrow(EngineException error, Instruction instr) {
|
||||||
return new PendingResult(instr, false, false, true, null, error, 0);
|
return new PendingResult(instr, false, false, true, null, error, 0);
|
||||||
}
|
}
|
||||||
public static PendingResult ofJump(int codePtr, Instruction instr) {
|
public static PendingResult ofJump(int codePtr, Instruction instr) {
|
||||||
return new PendingResult(instr, false, true, false, null, null, codePtr);
|
return new PendingResult(instr, false, true, false, null, null, codePtr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final LocalScope scope;
|
public final LocalScope scope;
|
||||||
public final Object thisArg;
|
public final Object thisArg;
|
||||||
public final Object[] args;
|
public final Object[] args;
|
||||||
public final Stack<TryCtx> tryStack = new Stack<>();
|
public final Stack<TryCtx> tryStack = new Stack<>();
|
||||||
public final CodeFunction function;
|
public final CodeFunction function;
|
||||||
public Object[] stack = new Object[32];
|
public Object[] stack = new Object[32];
|
||||||
public int stackPtr = 0;
|
public int stackPtr = 0;
|
||||||
public int codePtr = 0;
|
public int codePtr = 0;
|
||||||
public boolean jumpFlag = false, popTryFlag = false;
|
public boolean jumpFlag = false, popTryFlag = false;
|
||||||
private Location prevLoc = null;
|
private Location prevLoc = null;
|
||||||
|
|
||||||
public ObjectValue getLocalScope(Context ctx, boolean props) {
|
public ObjectValue getLocalScope(Context ctx, boolean props) {
|
||||||
var names = new String[scope.locals.length];
|
var names = new String[scope.locals.length];
|
||||||
|
|
||||||
for (int i = 0; i < scope.locals.length; i++) {
|
for (int i = 0; i < scope.locals.length; i++) {
|
||||||
var name = "local_" + (i - 2);
|
var name = "local_" + (i - 2);
|
||||||
|
|
||||||
if (i == 0) name = "this";
|
if (i == 0) name = "this";
|
||||||
else if (i == 1) name = "arguments";
|
else if (i == 1) name = "arguments";
|
||||||
else if (i < function.localNames.length) name = function.localNames[i];
|
else if (i < function.localNames.length) name = function.localNames[i];
|
||||||
|
|
||||||
names[i] = name;
|
names[i] = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ScopeValue(scope.locals, names);
|
return new ScopeValue(scope.locals, names);
|
||||||
}
|
}
|
||||||
public ObjectValue getCaptureScope(Context ctx, boolean props) {
|
public ObjectValue getCaptureScope(Context ctx, boolean props) {
|
||||||
var names = new String[scope.captures.length];
|
var names = new String[scope.captures.length];
|
||||||
|
|
||||||
for (int i = 0; i < scope.captures.length; i++) {
|
for (int i = 0; i < scope.captures.length; i++) {
|
||||||
var name = "capture_" + (i - 2);
|
var name = "capture_" + (i - 2);
|
||||||
if (i < function.captureNames.length) name = function.captureNames[i];
|
if (i < function.captureNames.length) name = function.captureNames[i];
|
||||||
names[i] = name;
|
names[i] = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ScopeValue(scope.captures, names);
|
return new ScopeValue(scope.captures, names);
|
||||||
}
|
}
|
||||||
public ObjectValue getValStackScope(Context ctx) {
|
public ObjectValue getValStackScope(Context ctx) {
|
||||||
return new ObjectValue() {
|
return new ObjectValue() {
|
||||||
@Override
|
@Override
|
||||||
protected Object getField(Context ctx, Object key) {
|
protected Object getField(Context ctx, Object key) {
|
||||||
var i = (int)Values.toNumber(ctx, key);
|
var i = (int)Values.toNumber(ctx, key);
|
||||||
if (i < 0 || i >= stackPtr) return null;
|
if (i < 0 || i >= stackPtr) return null;
|
||||||
else return stack[i];
|
else return stack[i];
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
protected boolean hasField(Context ctx, Object key) {
|
protected boolean hasField(Context ctx, Object key) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public List<Object> keys(boolean includeNonEnumerable) {
|
public List<Object> keys(boolean includeNonEnumerable) {
|
||||||
var res = super.keys(includeNonEnumerable);
|
var res = super.keys(includeNonEnumerable);
|
||||||
for (var i = 0; i < stackPtr; i++) res.add(i);
|
for (var i = 0; i < stackPtr; i++) res.add(i);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addTry(int start, int end, int catchStart, int finallyStart) {
|
public void addTry(int start, int end, int catchStart, int finallyStart) {
|
||||||
var err = tryStack.empty() ? null : tryStack.peek().error;
|
var err = tryStack.empty() ? null : tryStack.peek().error;
|
||||||
var res = new TryCtx(TryState.TRY, err, null, stackPtr, start, end, catchStart, finallyStart);
|
var res = new TryCtx(TryState.TRY, err, null, stackPtr, start, end, catchStart, finallyStart);
|
||||||
|
|
||||||
tryStack.add(res);
|
tryStack.add(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object peek() {
|
public Object peek() {
|
||||||
return peek(0);
|
return peek(0);
|
||||||
}
|
}
|
||||||
public Object peek(int offset) {
|
public Object peek(int offset) {
|
||||||
if (stackPtr <= offset) return null;
|
if (stackPtr <= offset) return null;
|
||||||
else return stack[stackPtr - 1 - offset];
|
else return stack[stackPtr - 1 - offset];
|
||||||
}
|
}
|
||||||
public Object pop() {
|
public Object pop() {
|
||||||
if (stackPtr == 0) return null;
|
if (stackPtr == 0) return null;
|
||||||
return stack[--stackPtr];
|
return stack[--stackPtr];
|
||||||
}
|
}
|
||||||
public Object[] take(int n) {
|
public Object[] take(int n) {
|
||||||
int srcI = stackPtr - n;
|
int srcI = stackPtr - n;
|
||||||
if (srcI < 0) srcI = 0;
|
if (srcI < 0) srcI = 0;
|
||||||
|
|
||||||
int dstI = n + srcI - stackPtr;
|
int dstI = n + srcI - stackPtr;
|
||||||
int copyN = stackPtr - srcI;
|
int copyN = stackPtr - srcI;
|
||||||
|
|
||||||
Object[] res = new Object[n];
|
Object[] res = new Object[n];
|
||||||
System.arraycopy(stack, srcI, res, dstI, copyN);
|
System.arraycopy(stack, srcI, res, dstI, copyN);
|
||||||
stackPtr -= copyN;
|
stackPtr -= copyN;
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
public void push(Context ctx, Object val) {
|
public void push(Context ctx, Object val) {
|
||||||
if (stack.length <= stackPtr) {
|
if (stack.length <= stackPtr) {
|
||||||
var newStack = new Object[stack.length * 2];
|
var newStack = new Object[stack.length * 2];
|
||||||
System.arraycopy(stack, 0, newStack, 0, stack.length);
|
System.arraycopy(stack, 0, newStack, 0, stack.length);
|
||||||
stack = newStack;
|
stack = newStack;
|
||||||
}
|
}
|
||||||
stack[stackPtr++] = Values.normalize(ctx, val);
|
stack[stackPtr++] = Values.normalize(ctx, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object next(Context ctx, Object value, Object returnValue, EngineException error) {
|
public Object next(Context ctx, Object value, Object returnValue, EngineException error) {
|
||||||
if (value != Runners.NO_RETURN) push(ctx, value);
|
if (value != Runners.NO_RETURN) push(ctx, value);
|
||||||
|
|
||||||
Instruction instr = null;
|
Instruction instr = null;
|
||||||
if (codePtr >= 0 && codePtr < function.body.length) instr = function.body[codePtr];
|
if (codePtr >= 0 && codePtr < function.body.length) instr = function.body[codePtr];
|
||||||
|
|
||||||
if (returnValue == Runners.NO_RETURN && error == null) {
|
if (returnValue == Runners.NO_RETURN && error == null) {
|
||||||
try {
|
try {
|
||||||
if (Thread.currentThread().isInterrupted()) throw new InterruptException();
|
if (Thread.currentThread().isInterrupted()) throw new InterruptException();
|
||||||
|
|
||||||
if (instr == null) returnValue = null;
|
if (instr == null) returnValue = null;
|
||||||
else {
|
else {
|
||||||
// System.out.println(instr + "@" + instr.location);
|
// System.out.println(instr + "@" + instr.location);
|
||||||
ctx.engine.onInstruction(ctx, this, instr, Runners.NO_RETURN, null, false);
|
ctx.engine.onInstruction(ctx, this, instr, Runners.NO_RETURN, null, false);
|
||||||
|
|
||||||
if (instr.location != null) prevLoc = instr.location;
|
if (instr.location != null) prevLoc = instr.location;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.jumpFlag = this.popTryFlag = false;
|
this.jumpFlag = this.popTryFlag = false;
|
||||||
returnValue = Runners.exec(ctx, instr, this);
|
returnValue = Runners.exec(ctx, instr, this);
|
||||||
}
|
}
|
||||||
catch (EngineException e) {
|
catch (EngineException e) {
|
||||||
error = e.add(ctx, function.name, prevLoc);
|
error = e.add(ctx, function.name, prevLoc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (EngineException e) { error = e; }
|
catch (EngineException e) { error = e; }
|
||||||
}
|
}
|
||||||
|
|
||||||
while (!tryStack.empty()) {
|
while (!tryStack.empty()) {
|
||||||
var tryCtx = tryStack.peek();
|
var tryCtx = tryStack.peek();
|
||||||
TryCtx newCtx = null;
|
TryCtx newCtx = null;
|
||||||
|
|
||||||
if (error != null) {
|
if (error != null) {
|
||||||
if (tryCtx.hasCatch()) newCtx = tryCtx._catch(error);
|
if (tryCtx.hasCatch()) newCtx = tryCtx._catch(error);
|
||||||
else if (tryCtx.hasFinally()) newCtx = tryCtx._finally(PendingResult.ofThrow(error, instr));
|
else if (tryCtx.hasFinally()) newCtx = tryCtx._finally(PendingResult.ofThrow(error, instr));
|
||||||
}
|
}
|
||||||
else if (returnValue != Runners.NO_RETURN) {
|
else if (returnValue != Runners.NO_RETURN) {
|
||||||
if (tryCtx.hasFinally()) newCtx = tryCtx._finally(PendingResult.ofReturn(returnValue, instr));
|
if (tryCtx.hasFinally()) newCtx = tryCtx._finally(PendingResult.ofReturn(returnValue, instr));
|
||||||
}
|
}
|
||||||
else if (jumpFlag && !tryCtx.inBounds(codePtr)) {
|
else if (jumpFlag && !tryCtx.inBounds(codePtr)) {
|
||||||
if (tryCtx.hasFinally()) newCtx = tryCtx._finally(PendingResult.ofJump(codePtr, instr));
|
if (tryCtx.hasFinally()) newCtx = tryCtx._finally(PendingResult.ofJump(codePtr, instr));
|
||||||
}
|
}
|
||||||
else if (!this.popTryFlag) newCtx = tryCtx;
|
else if (!this.popTryFlag) newCtx = tryCtx;
|
||||||
|
|
||||||
if (newCtx != null) {
|
if (newCtx != null) {
|
||||||
if (newCtx != tryCtx) {
|
if (newCtx != tryCtx) {
|
||||||
switch (newCtx.state) {
|
switch (newCtx.state) {
|
||||||
case CATCH:
|
case CATCH:
|
||||||
if (tryCtx.state != TryState.CATCH) scope.catchVars.add(new ValueVariable(false, error.value));
|
if (tryCtx.state != TryState.CATCH) scope.catchVars.add(new ValueVariable(false, error.value));
|
||||||
codePtr = tryCtx.catchStart;
|
codePtr = tryCtx.catchStart;
|
||||||
stackPtr = tryCtx.restoreStackPtr;
|
stackPtr = tryCtx.restoreStackPtr;
|
||||||
break;
|
break;
|
||||||
case FINALLY:
|
case FINALLY:
|
||||||
if (tryCtx.state == TryState.CATCH) scope.catchVars.remove(scope.catchVars.size() - 1);
|
if (tryCtx.state == TryState.CATCH) scope.catchVars.remove(scope.catchVars.size() - 1);
|
||||||
codePtr = tryCtx.finallyStart;
|
codePtr = tryCtx.finallyStart;
|
||||||
stackPtr = tryCtx.restoreStackPtr;
|
stackPtr = tryCtx.restoreStackPtr;
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
tryStack.pop();
|
tryStack.pop();
|
||||||
tryStack.push(newCtx);
|
tryStack.push(newCtx);
|
||||||
}
|
}
|
||||||
error = null;
|
error = null;
|
||||||
returnValue = Runners.NO_RETURN;
|
returnValue = Runners.NO_RETURN;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (tryCtx.state == TryState.CATCH) scope.catchVars.remove(scope.catchVars.size() - 1);
|
if (tryCtx.state == TryState.CATCH) scope.catchVars.remove(scope.catchVars.size() - 1);
|
||||||
|
|
||||||
if (tryCtx.state != TryState.FINALLY && tryCtx.hasFinally()) {
|
if (tryCtx.state != TryState.FINALLY && tryCtx.hasFinally()) {
|
||||||
codePtr = tryCtx.finallyStart;
|
codePtr = tryCtx.finallyStart;
|
||||||
stackPtr = tryCtx.restoreStackPtr;
|
stackPtr = tryCtx.restoreStackPtr;
|
||||||
tryStack.pop();
|
tryStack.pop();
|
||||||
tryStack.push(tryCtx._finally(null));
|
tryStack.push(tryCtx._finally(null));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
tryStack.pop();
|
tryStack.pop();
|
||||||
codePtr = tryCtx.end;
|
codePtr = tryCtx.end;
|
||||||
if (tryCtx.result.instruction != null) instr = tryCtx.result.instruction;
|
if (tryCtx.result.instruction != null) instr = tryCtx.result.instruction;
|
||||||
if (tryCtx.result.isJump) {
|
if (tryCtx.result.isJump) {
|
||||||
codePtr = tryCtx.result.ptr;
|
codePtr = tryCtx.result.ptr;
|
||||||
jumpFlag = true;
|
jumpFlag = true;
|
||||||
}
|
}
|
||||||
if (tryCtx.result.isReturn) returnValue = tryCtx.result.value;
|
if (tryCtx.result.isReturn) returnValue = tryCtx.result.value;
|
||||||
if (tryCtx.result.isThrow) {
|
if (tryCtx.result.isThrow) {
|
||||||
error = tryCtx.result.error;
|
error = tryCtx.result.error;
|
||||||
}
|
}
|
||||||
if (error != null) error.setCause(tryCtx.error);
|
if (error != null) error.setCause(tryCtx.error);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error != null) {
|
if (error != null) {
|
||||||
var caught = false;
|
var caught = false;
|
||||||
|
|
||||||
for (var frame : ctx.frames()) {
|
for (var frame : ctx.frames()) {
|
||||||
for (var tryCtx : frame.tryStack) {
|
for (var tryCtx : frame.tryStack) {
|
||||||
if (tryCtx.state == TryState.TRY) caught = true;
|
if (tryCtx.state == TryState.TRY) caught = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.engine.onInstruction(ctx, this, instr, null, error, caught);
|
ctx.engine.onInstruction(ctx, this, instr, null, error, caught);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
if (returnValue != Runners.NO_RETURN) {
|
if (returnValue != Runners.NO_RETURN) {
|
||||||
ctx.engine.onInstruction(ctx, this, instr, returnValue, null, false);
|
ctx.engine.onInstruction(ctx, this, instr, returnValue, null, false);
|
||||||
return returnValue;
|
return returnValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Runners.NO_RETURN;
|
return Runners.NO_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CodeFrame(Context ctx, Object thisArg, Object[] args, CodeFunction func) {
|
public CodeFrame(Context ctx, Object thisArg, Object[] args, CodeFunction func) {
|
||||||
this.args = args.clone();
|
this.args = args.clone();
|
||||||
this.scope = new LocalScope(func.localsN, func.captures);
|
this.scope = new LocalScope(func.localsN, func.captures);
|
||||||
this.scope.get(0).set(null, thisArg);
|
this.scope.get(0).set(null, thisArg);
|
||||||
var argsObj = new ArrayValue();
|
var argsObj = new ArrayValue();
|
||||||
for (var i = 0; i < args.length; i++) {
|
for (var i = 0; i < args.length; i++) {
|
||||||
argsObj.set(ctx, i, args[i]);
|
argsObj.set(ctx, i, args[i]);
|
||||||
}
|
}
|
||||||
this.scope.get(1).value = argsObj;
|
this.scope.get(1).value = argsObj;
|
||||||
|
|
||||||
this.thisArg = thisArg;
|
this.thisArg = thisArg;
|
||||||
this.function = func;
|
this.function = func;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package me.topchetoeu.jscript.engine.frame;
|
package me.topchetoeu.jscript.engine.frame;
|
||||||
|
|
||||||
public enum ConvertHint {
|
public enum ConvertHint {
|
||||||
TOSTRING,
|
TOSTRING,
|
||||||
VALUEOF,
|
VALUEOF,
|
||||||
}
|
}
|
@ -1,9 +1,9 @@
|
|||||||
package me.topchetoeu.jscript.engine.frame;
|
package me.topchetoeu.jscript.engine.frame;
|
||||||
|
|
||||||
public class InstructionResult {
|
public class InstructionResult {
|
||||||
public final Object value;
|
public final Object value;
|
||||||
|
|
||||||
public InstructionResult(Object value) {
|
public InstructionResult(Object value) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,350 +1,350 @@
|
|||||||
package me.topchetoeu.jscript.engine.frame;
|
package me.topchetoeu.jscript.engine.frame;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
import me.topchetoeu.jscript.engine.Engine;
|
import me.topchetoeu.jscript.engine.Engine;
|
||||||
import me.topchetoeu.jscript.engine.Operation;
|
import me.topchetoeu.jscript.engine.Operation;
|
||||||
import me.topchetoeu.jscript.engine.scope.ValueVariable;
|
import me.topchetoeu.jscript.engine.scope.ValueVariable;
|
||||||
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
||||||
import me.topchetoeu.jscript.engine.values.CodeFunction;
|
import me.topchetoeu.jscript.engine.values.CodeFunction;
|
||||||
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
import me.topchetoeu.jscript.engine.values.Symbol;
|
import me.topchetoeu.jscript.engine.values.Symbol;
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
|
|
||||||
public class Runners {
|
public class Runners {
|
||||||
public static final Object NO_RETURN = new Object();
|
public static final Object NO_RETURN = new Object();
|
||||||
|
|
||||||
public static Object execReturn(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execReturn(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
return frame.pop();
|
return frame.pop();
|
||||||
}
|
}
|
||||||
public static Object execThrow(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execThrow(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
throw new EngineException(frame.pop());
|
throw new EngineException(frame.pop());
|
||||||
}
|
}
|
||||||
public static Object execThrowSyntax(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execThrowSyntax(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
throw EngineException.ofSyntax((String)instr.get(0));
|
throw EngineException.ofSyntax((String)instr.get(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Object execCall(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execCall(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
var callArgs = frame.take(instr.get(0));
|
var callArgs = frame.take(instr.get(0));
|
||||||
var func = frame.pop();
|
var func = frame.pop();
|
||||||
var thisArg = frame.pop();
|
var thisArg = frame.pop();
|
||||||
|
|
||||||
frame.push(ctx, Values.call(ctx, func, thisArg, callArgs));
|
frame.push(ctx, Values.call(ctx, func, thisArg, callArgs));
|
||||||
|
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execCallNew(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execCallNew(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
var callArgs = frame.take(instr.get(0));
|
var callArgs = frame.take(instr.get(0));
|
||||||
var funcObj = frame.pop();
|
var funcObj = frame.pop();
|
||||||
|
|
||||||
frame.push(ctx, Values.callNew(ctx, funcObj, callArgs));
|
frame.push(ctx, Values.callNew(ctx, funcObj, callArgs));
|
||||||
|
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Object execMakeVar(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execMakeVar(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
var name = (String)instr.get(0);
|
var name = (String)instr.get(0);
|
||||||
ctx.environment().global.define(name);
|
ctx.environment().global.define(name);
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execDefProp(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execDefProp(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
var setter = frame.pop();
|
var setter = frame.pop();
|
||||||
var getter = frame.pop();
|
var getter = frame.pop();
|
||||||
var name = frame.pop();
|
var name = frame.pop();
|
||||||
var obj = frame.pop();
|
var obj = frame.pop();
|
||||||
|
|
||||||
if (getter != null && !Values.isFunction(getter)) throw EngineException.ofType("Getter must be a function or undefined.");
|
if (getter != null && !Values.isFunction(getter)) throw EngineException.ofType("Getter must be a function or undefined.");
|
||||||
if (setter != null && !Values.isFunction(setter)) throw EngineException.ofType("Setter must be a function or undefined.");
|
if (setter != null && !Values.isFunction(setter)) throw EngineException.ofType("Setter must be a function or undefined.");
|
||||||
if (!Values.isObject(obj)) throw EngineException.ofType("Property apply target must be an object.");
|
if (!Values.isObject(obj)) throw EngineException.ofType("Property apply target must be an object.");
|
||||||
Values.object(obj).defineProperty(ctx, name, Values.function(getter), Values.function(setter), false, false);
|
Values.object(obj).defineProperty(ctx, name, Values.function(getter), Values.function(setter), false, false);
|
||||||
|
|
||||||
frame.push(ctx, obj);
|
frame.push(ctx, obj);
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execInstanceof(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execInstanceof(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
var type = frame.pop();
|
var type = frame.pop();
|
||||||
var obj = frame.pop();
|
var obj = frame.pop();
|
||||||
|
|
||||||
if (!Values.isPrimitive(type)) {
|
if (!Values.isPrimitive(type)) {
|
||||||
var proto = Values.getMember(ctx, type, "prototype");
|
var proto = Values.getMember(ctx, type, "prototype");
|
||||||
frame.push(ctx, Values.isInstanceOf(ctx, obj, proto));
|
frame.push(ctx, Values.isInstanceOf(ctx, obj, proto));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
frame.push(ctx, false);
|
frame.push(ctx, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execKeys(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execKeys(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
var val = frame.pop();
|
var val = frame.pop();
|
||||||
|
|
||||||
var members = Values.getMembers(ctx, val, false, false);
|
var members = Values.getMembers(ctx, val, false, false);
|
||||||
Collections.reverse(members);
|
Collections.reverse(members);
|
||||||
|
|
||||||
frame.push(ctx, null);
|
frame.push(ctx, null);
|
||||||
|
|
||||||
for (var el : members) {
|
for (var el : members) {
|
||||||
if (el instanceof Symbol) continue;
|
if (el instanceof Symbol) continue;
|
||||||
var obj = new ObjectValue();
|
var obj = new ObjectValue();
|
||||||
obj.defineProperty(ctx, "value", el);
|
obj.defineProperty(ctx, "value", el);
|
||||||
frame.push(ctx, obj);
|
frame.push(ctx, obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Object execTryStart(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execTryStart(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
int start = frame.codePtr + 1;
|
int start = frame.codePtr + 1;
|
||||||
int catchStart = (int)instr.get(0);
|
int catchStart = (int)instr.get(0);
|
||||||
int finallyStart = (int)instr.get(1);
|
int finallyStart = (int)instr.get(1);
|
||||||
if (finallyStart >= 0) finallyStart += start;
|
if (finallyStart >= 0) finallyStart += start;
|
||||||
if (catchStart >= 0) catchStart += start;
|
if (catchStart >= 0) catchStart += start;
|
||||||
int end = (int)instr.get(2) + start;
|
int end = (int)instr.get(2) + start;
|
||||||
frame.addTry(start, end, catchStart, finallyStart);
|
frame.addTry(start, end, catchStart, finallyStart);
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execTryEnd(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execTryEnd(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
frame.popTryFlag = true;
|
frame.popTryFlag = true;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Object execDup(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execDup(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
int count = instr.get(0);
|
int count = instr.get(0);
|
||||||
|
|
||||||
for (var i = 0; i < count; i++) {
|
for (var i = 0; i < count; i++) {
|
||||||
frame.push(ctx, frame.peek(count - 1));
|
frame.push(ctx, frame.peek(count - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execLoadUndefined(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execLoadUndefined(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
frame.push(ctx, null);
|
frame.push(ctx, null);
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execLoadValue(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execLoadValue(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
frame.push(ctx, instr.get(0));
|
frame.push(ctx, instr.get(0));
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execLoadVar(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execLoadVar(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
var i = instr.get(0);
|
var i = instr.get(0);
|
||||||
|
|
||||||
if (i instanceof String) frame.push(ctx, ctx.environment().global.get(ctx, (String)i));
|
if (i instanceof String) frame.push(ctx, ctx.environment().global.get(ctx, (String)i));
|
||||||
else frame.push(ctx, frame.scope.get((int)i).get(ctx));
|
else frame.push(ctx, frame.scope.get((int)i).get(ctx));
|
||||||
|
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execLoadObj(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execLoadObj(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
frame.push(ctx, new ObjectValue());
|
frame.push(ctx, new ObjectValue());
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execLoadGlob(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execLoadGlob(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
frame.push(ctx, ctx.environment().global.obj);
|
frame.push(ctx, ctx.environment().global.obj);
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execLoadArr(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execLoadArr(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
var res = new ArrayValue();
|
var res = new ArrayValue();
|
||||||
res.setSize(instr.get(0));
|
res.setSize(instr.get(0));
|
||||||
frame.push(ctx, res);
|
frame.push(ctx, res);
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execLoadFunc(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execLoadFunc(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
long id = (Long)instr.get(0);
|
long id = (Long)instr.get(0);
|
||||||
var captures = new ValueVariable[instr.params.length - 1];
|
var captures = new ValueVariable[instr.params.length - 1];
|
||||||
|
|
||||||
for (var i = 1; i < instr.params.length; i++) {
|
for (var i = 1; i < instr.params.length; i++) {
|
||||||
captures[i - 1] = frame.scope.get(instr.get(i));
|
captures[i - 1] = frame.scope.get(instr.get(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
var func = new CodeFunction(ctx.environment(), "", Engine.functions.get(id), captures);
|
var func = new CodeFunction(ctx.environment(), "", Engine.functions.get(id), captures);
|
||||||
|
|
||||||
frame.push(ctx, func);
|
frame.push(ctx, func);
|
||||||
|
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execLoadMember(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execLoadMember(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
var key = frame.pop();
|
var key = frame.pop();
|
||||||
var obj = frame.pop();
|
var obj = frame.pop();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
frame.push(ctx, Values.getMember(ctx, obj, key));
|
frame.push(ctx, Values.getMember(ctx, obj, key));
|
||||||
}
|
}
|
||||||
catch (IllegalArgumentException e) {
|
catch (IllegalArgumentException e) {
|
||||||
throw EngineException.ofType(e.getMessage());
|
throw EngineException.ofType(e.getMessage());
|
||||||
}
|
}
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execLoadKeyMember(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execLoadKeyMember(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
frame.push(ctx, instr.get(0));
|
frame.push(ctx, instr.get(0));
|
||||||
return execLoadMember(ctx, instr, frame);
|
return execLoadMember(ctx, instr, frame);
|
||||||
}
|
}
|
||||||
public static Object execLoadRegEx(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execLoadRegEx(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
frame.push(ctx, ctx.environment().regexConstructor.call(ctx, null, instr.get(0), instr.get(1)));
|
frame.push(ctx, ctx.environment().regexConstructor.call(ctx, null, instr.get(0), instr.get(1)));
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Object execDiscard(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execDiscard(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
frame.pop();
|
frame.pop();
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execStoreMember(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execStoreMember(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
var val = frame.pop();
|
var val = frame.pop();
|
||||||
var key = frame.pop();
|
var key = frame.pop();
|
||||||
var obj = frame.pop();
|
var obj = frame.pop();
|
||||||
|
|
||||||
if (!Values.setMember(ctx, obj, key, val)) throw EngineException.ofSyntax("Can't set member '" + key + "'.");
|
if (!Values.setMember(ctx, obj, key, val)) throw EngineException.ofSyntax("Can't set member '" + key + "'.");
|
||||||
if ((boolean)instr.get(0)) frame.push(ctx, val);
|
if ((boolean)instr.get(0)) frame.push(ctx, val);
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execStoreVar(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execStoreVar(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
var val = (boolean)instr.get(1) ? frame.peek() : frame.pop();
|
var val = (boolean)instr.get(1) ? frame.peek() : frame.pop();
|
||||||
var i = instr.get(0);
|
var i = instr.get(0);
|
||||||
|
|
||||||
if (i instanceof String) ctx.environment().global.set(ctx, (String)i, val);
|
if (i instanceof String) ctx.environment().global.set(ctx, (String)i, val);
|
||||||
else frame.scope.get((int)i).set(ctx, val);
|
else frame.scope.get((int)i).set(ctx, val);
|
||||||
|
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execStoreSelfFunc(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execStoreSelfFunc(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
frame.scope.locals[(int)instr.get(0)].set(ctx, frame.function);
|
frame.scope.locals[(int)instr.get(0)].set(ctx, frame.function);
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Object execJmp(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execJmp(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
frame.codePtr += (int)instr.get(0);
|
frame.codePtr += (int)instr.get(0);
|
||||||
frame.jumpFlag = true;
|
frame.jumpFlag = true;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execJmpIf(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execJmpIf(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
if (Values.toBoolean(frame.pop())) {
|
if (Values.toBoolean(frame.pop())) {
|
||||||
frame.codePtr += (int)instr.get(0);
|
frame.codePtr += (int)instr.get(0);
|
||||||
frame.jumpFlag = true;
|
frame.jumpFlag = true;
|
||||||
}
|
}
|
||||||
else frame.codePtr ++;
|
else frame.codePtr ++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execJmpIfNot(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execJmpIfNot(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
if (Values.not(frame.pop())) {
|
if (Values.not(frame.pop())) {
|
||||||
frame.codePtr += (int)instr.get(0);
|
frame.codePtr += (int)instr.get(0);
|
||||||
frame.jumpFlag = true;
|
frame.jumpFlag = true;
|
||||||
}
|
}
|
||||||
else frame.codePtr ++;
|
else frame.codePtr ++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Object execIn(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execIn(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
var obj = frame.pop();
|
var obj = frame.pop();
|
||||||
var index = frame.pop();
|
var index = frame.pop();
|
||||||
|
|
||||||
frame.push(ctx, Values.hasMember(ctx, obj, index, false));
|
frame.push(ctx, Values.hasMember(ctx, obj, index, false));
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execTypeof(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execTypeof(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
String name = instr.get(0);
|
String name = instr.get(0);
|
||||||
Object obj;
|
Object obj;
|
||||||
|
|
||||||
if (name != null) {
|
if (name != null) {
|
||||||
if (ctx.environment().global.has(ctx, name)) {
|
if (ctx.environment().global.has(ctx, name)) {
|
||||||
obj = ctx.environment().global.get(ctx, name);
|
obj = ctx.environment().global.get(ctx, name);
|
||||||
}
|
}
|
||||||
else obj = null;
|
else obj = null;
|
||||||
}
|
}
|
||||||
else obj = frame.pop();
|
else obj = frame.pop();
|
||||||
|
|
||||||
frame.push(ctx, Values.type(obj));
|
frame.push(ctx, Values.type(obj));
|
||||||
|
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
public static Object execNop(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execNop(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Object execDelete(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execDelete(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
var key = frame.pop();
|
var key = frame.pop();
|
||||||
var val = frame.pop();
|
var val = frame.pop();
|
||||||
|
|
||||||
if (!Values.deleteMember(ctx, val, key)) throw EngineException.ofSyntax("Can't delete member '" + key + "'.");
|
if (!Values.deleteMember(ctx, val, key)) throw EngineException.ofSyntax("Can't delete member '" + key + "'.");
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Object execOperation(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object execOperation(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
Operation op = instr.get(0);
|
Operation op = instr.get(0);
|
||||||
var args = new Object[op.operands];
|
var args = new Object[op.operands];
|
||||||
|
|
||||||
for (var i = op.operands - 1; i >= 0; i--) args[i] = frame.pop();
|
for (var i = op.operands - 1; i >= 0; i--) args[i] = frame.pop();
|
||||||
|
|
||||||
frame.push(ctx, Values.operation(ctx, op, args));
|
frame.push(ctx, Values.operation(ctx, op, args));
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return NO_RETURN;
|
return NO_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Object exec(Context ctx, Instruction instr, CodeFrame frame) {
|
public static Object exec(Context ctx, Instruction instr, CodeFrame frame) {
|
||||||
switch (instr.type) {
|
switch (instr.type) {
|
||||||
case NOP: return execNop(ctx, instr, frame);
|
case NOP: return execNop(ctx, instr, frame);
|
||||||
case RETURN: return execReturn(ctx, instr, frame);
|
case RETURN: return execReturn(ctx, instr, frame);
|
||||||
case THROW: return execThrow(ctx, instr, frame);
|
case THROW: return execThrow(ctx, instr, frame);
|
||||||
case THROW_SYNTAX: return execThrowSyntax(ctx, instr, frame);
|
case THROW_SYNTAX: return execThrowSyntax(ctx, instr, frame);
|
||||||
case CALL: return execCall(ctx, instr, frame);
|
case CALL: return execCall(ctx, instr, frame);
|
||||||
case CALL_NEW: return execCallNew(ctx, instr, frame);
|
case CALL_NEW: return execCallNew(ctx, instr, frame);
|
||||||
case TRY_START: return execTryStart(ctx, instr, frame);
|
case TRY_START: return execTryStart(ctx, instr, frame);
|
||||||
case TRY_END: return execTryEnd(ctx, instr, frame);
|
case TRY_END: return execTryEnd(ctx, instr, frame);
|
||||||
|
|
||||||
case DUP: return execDup(ctx, instr, frame);
|
case DUP: return execDup(ctx, instr, frame);
|
||||||
case LOAD_VALUE: return execLoadValue(ctx, instr, frame);
|
case LOAD_VALUE: return execLoadValue(ctx, instr, frame);
|
||||||
case LOAD_VAR: return execLoadVar(ctx, instr, frame);
|
case LOAD_VAR: return execLoadVar(ctx, instr, frame);
|
||||||
case LOAD_OBJ: return execLoadObj(ctx, instr, frame);
|
case LOAD_OBJ: return execLoadObj(ctx, instr, frame);
|
||||||
case LOAD_ARR: return execLoadArr(ctx, instr, frame);
|
case LOAD_ARR: return execLoadArr(ctx, instr, frame);
|
||||||
case LOAD_FUNC: return execLoadFunc(ctx, instr, frame);
|
case LOAD_FUNC: return execLoadFunc(ctx, instr, frame);
|
||||||
case LOAD_MEMBER: return execLoadMember(ctx, instr, frame);
|
case LOAD_MEMBER: return execLoadMember(ctx, instr, frame);
|
||||||
case LOAD_VAL_MEMBER: return execLoadKeyMember(ctx, instr, frame);
|
case LOAD_VAL_MEMBER: return execLoadKeyMember(ctx, instr, frame);
|
||||||
case LOAD_REGEX: return execLoadRegEx(ctx, instr, frame);
|
case LOAD_REGEX: return execLoadRegEx(ctx, instr, frame);
|
||||||
case LOAD_GLOB: return execLoadGlob(ctx, instr, frame);
|
case LOAD_GLOB: return execLoadGlob(ctx, instr, frame);
|
||||||
|
|
||||||
case DISCARD: return execDiscard(ctx, instr, frame);
|
case DISCARD: return execDiscard(ctx, instr, frame);
|
||||||
case STORE_MEMBER: return execStoreMember(ctx, instr, frame);
|
case STORE_MEMBER: return execStoreMember(ctx, instr, frame);
|
||||||
case STORE_VAR: return execStoreVar(ctx, instr, frame);
|
case STORE_VAR: return execStoreVar(ctx, instr, frame);
|
||||||
case STORE_SELF_FUNC: return execStoreSelfFunc(ctx, instr, frame);
|
case STORE_SELF_FUNC: return execStoreSelfFunc(ctx, instr, frame);
|
||||||
case MAKE_VAR: return execMakeVar(ctx, instr, frame);
|
case MAKE_VAR: return execMakeVar(ctx, instr, frame);
|
||||||
|
|
||||||
case KEYS: return execKeys(ctx, instr, frame);
|
case KEYS: return execKeys(ctx, instr, frame);
|
||||||
case DEF_PROP: return execDefProp(ctx, instr, frame);
|
case DEF_PROP: return execDefProp(ctx, instr, frame);
|
||||||
case TYPEOF: return execTypeof(ctx, instr, frame);
|
case TYPEOF: return execTypeof(ctx, instr, frame);
|
||||||
case DELETE: return execDelete(ctx, instr, frame);
|
case DELETE: return execDelete(ctx, instr, frame);
|
||||||
|
|
||||||
case JMP: return execJmp(ctx, instr, frame);
|
case JMP: return execJmp(ctx, instr, frame);
|
||||||
case JMP_IF: return execJmpIf(ctx, instr, frame);
|
case JMP_IF: return execJmpIf(ctx, instr, frame);
|
||||||
case JMP_IFN: return execJmpIfNot(ctx, instr, frame);
|
case JMP_IFN: return execJmpIfNot(ctx, instr, frame);
|
||||||
|
|
||||||
case OPERATION: return execOperation(ctx, instr, frame);
|
case OPERATION: return execOperation(ctx, instr, frame);
|
||||||
|
|
||||||
default: throw EngineException.ofSyntax("Invalid instruction " + instr.type.name() + ".");
|
default: throw EngineException.ofSyntax("Invalid instruction " + instr.type.name() + ".");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,78 +1,78 @@
|
|||||||
package me.topchetoeu.jscript.engine.scope;
|
package me.topchetoeu.jscript.engine.scope;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
||||||
import me.topchetoeu.jscript.engine.values.NativeFunction;
|
import me.topchetoeu.jscript.engine.values.NativeFunction;
|
||||||
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
|
|
||||||
public class GlobalScope implements ScopeRecord {
|
public class GlobalScope implements ScopeRecord {
|
||||||
public final ObjectValue obj;
|
public final ObjectValue obj;
|
||||||
|
|
||||||
public boolean has(Context ctx, String name) {
|
public boolean has(Context ctx, String name) {
|
||||||
return obj.hasMember(ctx, name, false);
|
return obj.hasMember(ctx, name, false);
|
||||||
}
|
}
|
||||||
public Object getKey(String name) {
|
public Object getKey(String name) {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GlobalScope globalChild() {
|
public GlobalScope globalChild() {
|
||||||
var obj = new ObjectValue();
|
var obj = new ObjectValue();
|
||||||
obj.setPrototype(null, this.obj);
|
obj.setPrototype(null, this.obj);
|
||||||
return new GlobalScope(obj);
|
return new GlobalScope(obj);
|
||||||
}
|
}
|
||||||
public LocalScopeRecord child() {
|
public LocalScopeRecord child() {
|
||||||
return new LocalScopeRecord();
|
return new LocalScopeRecord();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object define(String name) {
|
public Object define(String name) {
|
||||||
if (obj.hasMember(null, name, true)) return name;
|
if (obj.hasMember(null, name, true)) return name;
|
||||||
obj.defineProperty(null, name, null);
|
obj.defineProperty(null, name, null);
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
public void define(String name, Variable val) {
|
public void define(String name, Variable val) {
|
||||||
obj.defineProperty(null, name,
|
obj.defineProperty(null, name,
|
||||||
new NativeFunction("get " + name, (ctx, th, a) -> val.get(ctx)),
|
new NativeFunction("get " + name, (ctx, th, a) -> val.get(ctx)),
|
||||||
new NativeFunction("set " + name, (ctx, th, args) -> { val.set(ctx, args.length > 0 ? args[0] : null); return null; }),
|
new NativeFunction("set " + name, (ctx, th, args) -> { val.set(ctx, args.length > 0 ? args[0] : null); return null; }),
|
||||||
true, true
|
true, true
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
public void define(Context ctx, String name, boolean readonly, Object val) {
|
public void define(Context ctx, String name, boolean readonly, Object val) {
|
||||||
obj.defineProperty(ctx, name, val, readonly, true, true);
|
obj.defineProperty(ctx, name, val, readonly, true, true);
|
||||||
}
|
}
|
||||||
public void define(String ...names) {
|
public void define(String ...names) {
|
||||||
for (var n : names) define(n);
|
for (var n : names) define(n);
|
||||||
}
|
}
|
||||||
public void define(boolean readonly, FunctionValue val) {
|
public void define(boolean readonly, FunctionValue val) {
|
||||||
define(null, val.name, readonly, val);
|
define(null, val.name, readonly, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object get(Context ctx, String name) {
|
public Object get(Context ctx, String name) {
|
||||||
if (!obj.hasMember(ctx, name, false)) throw EngineException.ofSyntax("The variable '" + name + "' doesn't exist.");
|
if (!obj.hasMember(ctx, name, false)) throw EngineException.ofSyntax("The variable '" + name + "' doesn't exist.");
|
||||||
else return obj.getMember(ctx, name);
|
else return obj.getMember(ctx, name);
|
||||||
}
|
}
|
||||||
public void set(Context ctx, String name, Object val) {
|
public void set(Context ctx, String name, Object val) {
|
||||||
if (!obj.hasMember(ctx, name, false)) throw EngineException.ofSyntax("The variable '" + name + "' doesn't exist.");
|
if (!obj.hasMember(ctx, name, false)) throw EngineException.ofSyntax("The variable '" + name + "' doesn't exist.");
|
||||||
if (!obj.setMember(ctx, name, val, false)) throw EngineException.ofSyntax("The global '" + name + "' is readonly.");
|
if (!obj.setMember(ctx, name, val, false)) throw EngineException.ofSyntax("The global '" + name + "' is readonly.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<String> keys() {
|
public Set<String> keys() {
|
||||||
var res = new HashSet<String>();
|
var res = new HashSet<String>();
|
||||||
|
|
||||||
for (var key : keys()) {
|
for (var key : keys()) {
|
||||||
if (key instanceof String) res.add((String)key);
|
if (key instanceof String) res.add((String)key);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GlobalScope() {
|
public GlobalScope() {
|
||||||
this.obj = new ObjectValue();
|
this.obj = new ObjectValue();
|
||||||
}
|
}
|
||||||
public GlobalScope(ObjectValue val) {
|
public GlobalScope(ObjectValue val) {
|
||||||
this.obj = val;
|
this.obj = val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,29 +1,29 @@
|
|||||||
package me.topchetoeu.jscript.engine.scope;
|
package me.topchetoeu.jscript.engine.scope;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
public class LocalScope {
|
public class LocalScope {
|
||||||
public final ValueVariable[] captures;
|
public final ValueVariable[] captures;
|
||||||
public final ValueVariable[] locals;
|
public final ValueVariable[] locals;
|
||||||
public final ArrayList<ValueVariable> catchVars = new ArrayList<>();
|
public final ArrayList<ValueVariable> catchVars = new ArrayList<>();
|
||||||
|
|
||||||
public ValueVariable get(int i) {
|
public ValueVariable get(int i) {
|
||||||
if (i >= locals.length) return catchVars.get(i - locals.length);
|
if (i >= locals.length) return catchVars.get(i - locals.length);
|
||||||
if (i >= 0) return locals[i];
|
if (i >= 0) return locals[i];
|
||||||
else return captures[~i];
|
else return captures[~i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public int size() {
|
public int size() {
|
||||||
return captures.length + locals.length;
|
return captures.length + locals.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LocalScope(int n, ValueVariable[] captures) {
|
public LocalScope(int n, ValueVariable[] captures) {
|
||||||
locals = new ValueVariable[n];
|
locals = new ValueVariable[n];
|
||||||
this.captures = captures;
|
this.captures = captures;
|
||||||
|
|
||||||
for (int i = 0; i < n; i++) {
|
for (int i = 0; i < n; i++) {
|
||||||
locals[i] = new ValueVariable(false, null);
|
locals[i] = new ValueVariable(false, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,77 +1,77 @@
|
|||||||
package me.topchetoeu.jscript.engine.scope;
|
package me.topchetoeu.jscript.engine.scope;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
public class LocalScopeRecord implements ScopeRecord {
|
public class LocalScopeRecord implements ScopeRecord {
|
||||||
public final LocalScopeRecord parent;
|
public final LocalScopeRecord parent;
|
||||||
|
|
||||||
private final ArrayList<String> captures = new ArrayList<>();
|
private final ArrayList<String> captures = new ArrayList<>();
|
||||||
private final ArrayList<String> locals = new ArrayList<>();
|
private final ArrayList<String> locals = new ArrayList<>();
|
||||||
|
|
||||||
public String[] captures() {
|
public String[] captures() {
|
||||||
return captures.toArray(String[]::new);
|
return captures.toArray(String[]::new);
|
||||||
}
|
}
|
||||||
public String[] locals() {
|
public String[] locals() {
|
||||||
return locals.toArray(String[]::new);
|
return locals.toArray(String[]::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
public LocalScopeRecord child() {
|
public LocalScopeRecord child() {
|
||||||
return new LocalScopeRecord(this);
|
return new LocalScopeRecord(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int localsCount() {
|
public int localsCount() {
|
||||||
return locals.size();
|
return locals.size();
|
||||||
}
|
}
|
||||||
public int capturesCount() {
|
public int capturesCount() {
|
||||||
return captures.size();
|
return captures.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int[] getCaptures() {
|
public int[] getCaptures() {
|
||||||
var buff = new int[captures.size()];
|
var buff = new int[captures.size()];
|
||||||
var i = 0;
|
var i = 0;
|
||||||
|
|
||||||
for (var name : captures) {
|
for (var name : captures) {
|
||||||
var index = parent.getKey(name);
|
var index = parent.getKey(name);
|
||||||
if (index instanceof Integer) buff[i++] = (int)index;
|
if (index instanceof Integer) buff[i++] = (int)index;
|
||||||
}
|
}
|
||||||
|
|
||||||
var res = new int[i];
|
var res = new int[i];
|
||||||
System.arraycopy(buff, 0, res, 0, i);
|
System.arraycopy(buff, 0, res, 0, i);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object getKey(String name) {
|
public Object getKey(String name) {
|
||||||
var capI = captures.indexOf(name);
|
var capI = captures.indexOf(name);
|
||||||
var locI = locals.lastIndexOf(name);
|
var locI = locals.lastIndexOf(name);
|
||||||
if (locI >= 0) return locI;
|
if (locI >= 0) return locI;
|
||||||
if (capI >= 0) return ~capI;
|
if (capI >= 0) return ~capI;
|
||||||
if (parent != null) {
|
if (parent != null) {
|
||||||
var res = parent.getKey(name);
|
var res = parent.getKey(name);
|
||||||
if (res != null && res instanceof Integer) {
|
if (res != null && res instanceof Integer) {
|
||||||
captures.add(name);
|
captures.add(name);
|
||||||
return -captures.size();
|
return -captures.size();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
public Object define(String name, boolean force) {
|
public Object define(String name, boolean force) {
|
||||||
if (!force && locals.contains(name)) return locals.indexOf(name);
|
if (!force && locals.contains(name)) return locals.indexOf(name);
|
||||||
locals.add(name);
|
locals.add(name);
|
||||||
return locals.size() - 1;
|
return locals.size() - 1;
|
||||||
}
|
}
|
||||||
public Object define(String name) {
|
public Object define(String name) {
|
||||||
return define(name, false);
|
return define(name, false);
|
||||||
}
|
}
|
||||||
public void undefine() {
|
public void undefine() {
|
||||||
locals.remove(locals.size() - 1);
|
locals.remove(locals.size() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public LocalScopeRecord() {
|
public LocalScopeRecord() {
|
||||||
this.parent = null;
|
this.parent = null;
|
||||||
}
|
}
|
||||||
public LocalScopeRecord(LocalScopeRecord parent) {
|
public LocalScopeRecord(LocalScopeRecord parent) {
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package me.topchetoeu.jscript.engine.scope;
|
package me.topchetoeu.jscript.engine.scope;
|
||||||
|
|
||||||
public interface ScopeRecord {
|
public interface ScopeRecord {
|
||||||
public Object getKey(String name);
|
public Object getKey(String name);
|
||||||
public Object define(String name);
|
public Object define(String name);
|
||||||
public LocalScopeRecord child();
|
public LocalScopeRecord child();
|
||||||
}
|
}
|
||||||
|
@ -1,28 +1,28 @@
|
|||||||
package me.topchetoeu.jscript.engine.scope;
|
package me.topchetoeu.jscript.engine.scope;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
|
|
||||||
public class ValueVariable implements Variable {
|
public class ValueVariable implements Variable {
|
||||||
public boolean readonly;
|
public boolean readonly;
|
||||||
public Object value;
|
public Object value;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean readonly() { return readonly; }
|
public boolean readonly() { return readonly; }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object get(Context ctx) {
|
public Object get(Context ctx) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void set(Context ctx, Object val) {
|
public void set(Context ctx, Object val) {
|
||||||
if (readonly) return;
|
if (readonly) return;
|
||||||
this.value = Values.normalize(ctx, val);
|
this.value = Values.normalize(ctx, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ValueVariable(boolean readonly, Object val) {
|
public ValueVariable(boolean readonly, Object val) {
|
||||||
this.readonly = readonly;
|
this.readonly = readonly;
|
||||||
this.value = val;
|
this.value = val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package me.topchetoeu.jscript.engine.scope;
|
package me.topchetoeu.jscript.engine.scope;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
|
|
||||||
public interface Variable {
|
public interface Variable {
|
||||||
Object get(Context ctx);
|
Object get(Context ctx);
|
||||||
default boolean readonly() { return true; }
|
default boolean readonly() { return true; }
|
||||||
default void set(Context ctx, Object val) { }
|
default void set(Context ctx, Object val) { }
|
||||||
}
|
}
|
||||||
|
@ -1,220 +1,220 @@
|
|||||||
package me.topchetoeu.jscript.engine.values;
|
package me.topchetoeu.jscript.engine.values;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
|
|
||||||
// TODO: Make methods generic
|
// TODO: Make methods generic
|
||||||
public class ArrayValue extends ObjectValue implements Iterable<Object> {
|
public class ArrayValue extends ObjectValue implements Iterable<Object> {
|
||||||
private static final Object UNDEFINED = new Object();
|
private static final Object UNDEFINED = new Object();
|
||||||
private Object[] values;
|
private Object[] values;
|
||||||
private int size;
|
private int size;
|
||||||
|
|
||||||
private Object[] alloc(int index) {
|
private Object[] alloc(int index) {
|
||||||
index++;
|
index++;
|
||||||
if (index < values.length) return values;
|
if (index < values.length) return values;
|
||||||
if (index < values.length * 2) index = values.length * 2;
|
if (index < values.length * 2) index = values.length * 2;
|
||||||
|
|
||||||
var arr = new Object[index];
|
var arr = new Object[index];
|
||||||
System.arraycopy(values, 0, arr, 0, values.length);
|
System.arraycopy(values, 0, arr, 0, values.length);
|
||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int size() { return size; }
|
public int size() { return size; }
|
||||||
public boolean setSize(int val) {
|
public boolean setSize(int val) {
|
||||||
if (val < 0) return false;
|
if (val < 0) return false;
|
||||||
if (size > val) shrink(size - val);
|
if (size > val) shrink(size - val);
|
||||||
else {
|
else {
|
||||||
values = alloc(val);
|
values = alloc(val);
|
||||||
size = val;
|
size = val;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object get(int i) {
|
public Object get(int i) {
|
||||||
if (i < 0 || i >= size) return null;
|
if (i < 0 || i >= size) return null;
|
||||||
var res = values[i];
|
var res = values[i];
|
||||||
if (res == UNDEFINED) return null;
|
if (res == UNDEFINED) return null;
|
||||||
else return res;
|
else return res;
|
||||||
}
|
}
|
||||||
public void set(Context ctx, int i, Object val) {
|
public void set(Context ctx, int i, Object val) {
|
||||||
if (i < 0) return;
|
if (i < 0) return;
|
||||||
|
|
||||||
values = alloc(i);
|
values = alloc(i);
|
||||||
|
|
||||||
val = Values.normalize(ctx, val);
|
val = Values.normalize(ctx, val);
|
||||||
if (val == null) val = UNDEFINED;
|
if (val == null) val = UNDEFINED;
|
||||||
values[i] = val;
|
values[i] = val;
|
||||||
if (i >= size) size = i + 1;
|
if (i >= size) size = i + 1;
|
||||||
}
|
}
|
||||||
public boolean has(int i) {
|
public boolean has(int i) {
|
||||||
return i >= 0 && i < size && values[i] != null;
|
return i >= 0 && i < size && values[i] != null;
|
||||||
}
|
}
|
||||||
public void remove(int i) {
|
public void remove(int i) {
|
||||||
if (i < 0 || i >= values.length) return;
|
if (i < 0 || i >= values.length) return;
|
||||||
values[i] = null;
|
values[i] = null;
|
||||||
}
|
}
|
||||||
public void shrink(int n) {
|
public void shrink(int n) {
|
||||||
if (n >= values.length) {
|
if (n >= values.length) {
|
||||||
values = new Object[16];
|
values = new Object[16];
|
||||||
size = 0;
|
size = 0;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
for (int i = 0; i < n; i++) {
|
for (int i = 0; i < n; i++) {
|
||||||
values[--size] = null;
|
values[--size] = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object[] toArray() {
|
public Object[] toArray() {
|
||||||
Object[] res = new Object[size];
|
Object[] res = new Object[size];
|
||||||
copyTo(res, 0, 0, size);
|
copyTo(res, 0, 0, size);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
public void copyTo(Object[] arr, int sourceStart, int destStart, int count) {
|
public void copyTo(Object[] arr, int sourceStart, int destStart, int count) {
|
||||||
for (var i = 0; i < count; i++) {
|
for (var i = 0; i < count; i++) {
|
||||||
if (i + sourceStart < 0 || i + sourceStart >= size) arr[i + destStart] = null;
|
if (i + sourceStart < 0 || i + sourceStart >= size) arr[i + destStart] = null;
|
||||||
if (values[i + sourceStart] == UNDEFINED) arr[i + destStart] = null;
|
if (values[i + sourceStart] == UNDEFINED) arr[i + destStart] = null;
|
||||||
else arr[i + sourceStart] = values[i + destStart];
|
else arr[i + sourceStart] = values[i + destStart];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public void copyTo(Context ctx, ArrayValue arr, int sourceStart, int destStart, int count) {
|
public void copyTo(Context ctx, ArrayValue arr, int sourceStart, int destStart, int count) {
|
||||||
// Iterate in reverse to reallocate at most once
|
// Iterate in reverse to reallocate at most once
|
||||||
for (var i = count - 1; i >= 0; i--) {
|
for (var i = count - 1; i >= 0; i--) {
|
||||||
if (i + sourceStart < 0 || i + sourceStart >= size) arr.remove(i + destStart);
|
if (i + sourceStart < 0 || i + sourceStart >= size) arr.remove(i + destStart);
|
||||||
if (values[i + sourceStart] == UNDEFINED) arr.set(ctx, i + destStart, null);
|
if (values[i + sourceStart] == UNDEFINED) arr.set(ctx, i + destStart, null);
|
||||||
else if (values[i + sourceStart] == null) arr.remove(i + destStart);
|
else if (values[i + sourceStart] == null) arr.remove(i + destStart);
|
||||||
else arr.set(ctx, i + destStart, values[i + sourceStart]);
|
else arr.set(ctx, i + destStart, values[i + sourceStart]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void copyFrom(Context ctx, Object[] arr, int sourceStart, int destStart, int count) {
|
public void copyFrom(Context ctx, Object[] arr, int sourceStart, int destStart, int count) {
|
||||||
for (var i = 0; i < count; i++) {
|
for (var i = 0; i < count; i++) {
|
||||||
set(ctx, i + destStart, arr[i + sourceStart]);
|
set(ctx, i + destStart, arr[i + sourceStart]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void move(int srcI, int dstI, int n) {
|
public void move(int srcI, int dstI, int n) {
|
||||||
values = alloc(dstI + n);
|
values = alloc(dstI + n);
|
||||||
|
|
||||||
System.arraycopy(values, srcI, values, dstI, n);
|
System.arraycopy(values, srcI, values, dstI, n);
|
||||||
|
|
||||||
if (dstI + n >= size) size = dstI + n;
|
if (dstI + n >= size) size = dstI + n;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sort(Comparator<Object> comparator) {
|
public void sort(Comparator<Object> comparator) {
|
||||||
Arrays.sort(values, 0, size, (a, b) -> {
|
Arrays.sort(values, 0, size, (a, b) -> {
|
||||||
var _a = 0;
|
var _a = 0;
|
||||||
var _b = 0;
|
var _b = 0;
|
||||||
|
|
||||||
if (a == UNDEFINED) _a = 1;
|
if (a == UNDEFINED) _a = 1;
|
||||||
if (a == null) _a = 2;
|
if (a == null) _a = 2;
|
||||||
|
|
||||||
if (b == UNDEFINED) _b = 1;
|
if (b == UNDEFINED) _b = 1;
|
||||||
if (b == null) _b = 2;
|
if (b == null) _b = 2;
|
||||||
|
|
||||||
if (_a != 0 || _b != 0) return Integer.compare(_a, _b);
|
if (_a != 0 || _b != 0) return Integer.compare(_a, _b);
|
||||||
|
|
||||||
return comparator.compare(a, b);
|
return comparator.compare(a, b);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Object getField(Context ctx, Object key) {
|
protected Object getField(Context ctx, Object key) {
|
||||||
if (key instanceof Number) {
|
if (key instanceof Number) {
|
||||||
var i = ((Number)key).doubleValue();
|
var i = ((Number)key).doubleValue();
|
||||||
if (i >= 0 && i - Math.floor(i) == 0) {
|
if (i >= 0 && i - Math.floor(i) == 0) {
|
||||||
return get((int)i);
|
return get((int)i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.getField(ctx, key);
|
return super.getField(ctx, key);
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
protected boolean setField(Context ctx, Object key, Object val) {
|
protected boolean setField(Context ctx, Object key, Object val) {
|
||||||
if (key instanceof Number) {
|
if (key instanceof Number) {
|
||||||
var i = Values.number(key);
|
var i = Values.number(key);
|
||||||
if (i >= 0 && i - Math.floor(i) == 0) {
|
if (i >= 0 && i - Math.floor(i) == 0) {
|
||||||
set(ctx, (int)i, val);
|
set(ctx, (int)i, val);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.setField(ctx, key, val);
|
return super.setField(ctx, key, val);
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
protected boolean hasField(Context ctx, Object key) {
|
protected boolean hasField(Context ctx, Object key) {
|
||||||
if (key instanceof Number) {
|
if (key instanceof Number) {
|
||||||
var i = Values.number(key);
|
var i = Values.number(key);
|
||||||
if (i >= 0 && i - Math.floor(i) == 0) {
|
if (i >= 0 && i - Math.floor(i) == 0) {
|
||||||
return has((int)i);
|
return has((int)i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.hasField(ctx, key);
|
return super.hasField(ctx, key);
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
protected void deleteField(Context ctx, Object key) {
|
protected void deleteField(Context ctx, Object key) {
|
||||||
if (key instanceof Number) {
|
if (key instanceof Number) {
|
||||||
var i = Values.number(key);
|
var i = Values.number(key);
|
||||||
if (i >= 0 && i - Math.floor(i) == 0) {
|
if (i >= 0 && i - Math.floor(i) == 0) {
|
||||||
remove((int)i);
|
remove((int)i);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
super.deleteField(ctx, key);
|
super.deleteField(ctx, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Object> keys(boolean includeNonEnumerable) {
|
public List<Object> keys(boolean includeNonEnumerable) {
|
||||||
var res = super.keys(includeNonEnumerable);
|
var res = super.keys(includeNonEnumerable);
|
||||||
for (var i = 0; i < size(); i++) {
|
for (var i = 0; i < size(); i++) {
|
||||||
if (has(i)) res.add(i);
|
if (has(i)) res.add(i);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Iterator<Object> iterator() {
|
public Iterator<Object> iterator() {
|
||||||
return new Iterator<Object>() {
|
return new Iterator<Object>() {
|
||||||
private int i = 0;
|
private int i = 0;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasNext() {
|
public boolean hasNext() {
|
||||||
return i < size();
|
return i < size();
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public Object next() {
|
public Object next() {
|
||||||
if (!hasNext()) return null;
|
if (!hasNext()) return null;
|
||||||
return get(i++);
|
return get(i++);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArrayValue() {
|
public ArrayValue() {
|
||||||
super(PlaceholderProto.ARRAY);
|
super(PlaceholderProto.ARRAY);
|
||||||
values = new Object[16];
|
values = new Object[16];
|
||||||
size = 0;
|
size = 0;
|
||||||
}
|
}
|
||||||
public ArrayValue(int cap) {
|
public ArrayValue(int cap) {
|
||||||
super(PlaceholderProto.ARRAY);
|
super(PlaceholderProto.ARRAY);
|
||||||
values = new Object[cap];
|
values = new Object[cap];
|
||||||
size = 0;
|
size = 0;
|
||||||
}
|
}
|
||||||
public ArrayValue(Context ctx, Object ...values) {
|
public ArrayValue(Context ctx, Object ...values) {
|
||||||
this();
|
this();
|
||||||
this.values = new Object[values.length];
|
this.values = new Object[values.length];
|
||||||
size = values.length;
|
size = values.length;
|
||||||
|
|
||||||
for (var i = 0; i < size; i++) this.values[i] = Values.normalize(ctx, values[i]);
|
for (var i = 0; i < size; i++) this.values[i] = Values.normalize(ctx, values[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ArrayValue of(Context ctx, Collection<?> values) {
|
public static ArrayValue of(Context ctx, Collection<?> values) {
|
||||||
return new ArrayValue(ctx, values.toArray(Object[]::new));
|
return new ArrayValue(ctx, values.toArray(Object[]::new));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,57 +1,57 @@
|
|||||||
package me.topchetoeu.jscript.engine.values;
|
package me.topchetoeu.jscript.engine.values;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.compilation.FunctionBody;
|
import me.topchetoeu.jscript.compilation.FunctionBody;
|
||||||
import me.topchetoeu.jscript.compilation.Instruction;
|
import me.topchetoeu.jscript.compilation.Instruction;
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
import me.topchetoeu.jscript.engine.Environment;
|
import me.topchetoeu.jscript.engine.Environment;
|
||||||
import me.topchetoeu.jscript.engine.frame.CodeFrame;
|
import me.topchetoeu.jscript.engine.frame.CodeFrame;
|
||||||
import me.topchetoeu.jscript.engine.frame.Runners;
|
import me.topchetoeu.jscript.engine.frame.Runners;
|
||||||
import me.topchetoeu.jscript.engine.scope.ValueVariable;
|
import me.topchetoeu.jscript.engine.scope.ValueVariable;
|
||||||
|
|
||||||
public class CodeFunction extends FunctionValue {
|
public class CodeFunction extends FunctionValue {
|
||||||
public final int localsN;
|
public final int localsN;
|
||||||
public final Instruction[] body;
|
public final Instruction[] body;
|
||||||
public final String[] captureNames, localNames;
|
public final String[] captureNames, localNames;
|
||||||
public final ValueVariable[] captures;
|
public final ValueVariable[] captures;
|
||||||
public Environment environment;
|
public Environment environment;
|
||||||
|
|
||||||
public Location loc() {
|
public Location loc() {
|
||||||
for (var instr : body) {
|
for (var instr : body) {
|
||||||
if (instr.location != null) return instr.location;
|
if (instr.location != null) return instr.location;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
public String readable() {
|
public String readable() {
|
||||||
var loc = loc();
|
var loc = loc();
|
||||||
if (loc == null) return name;
|
if (loc == null) return name;
|
||||||
else if (name.equals("")) return loc.toString();
|
else if (name.equals("")) return loc.toString();
|
||||||
else return name + "@" + loc;
|
else return name + "@" + loc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object call(Context ctx, Object thisArg, Object ...args) {
|
public Object call(Context ctx, Object thisArg, Object ...args) {
|
||||||
var frame = new CodeFrame(ctx, thisArg, args, this);
|
var frame = new CodeFrame(ctx, thisArg, args, this);
|
||||||
try {
|
try {
|
||||||
ctx.pushFrame(frame);
|
ctx.pushFrame(frame);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
var res = frame.next(ctx, Runners.NO_RETURN, Runners.NO_RETURN, null);
|
var res = frame.next(ctx, Runners.NO_RETURN, Runners.NO_RETURN, null);
|
||||||
if (res != Runners.NO_RETURN) return res;
|
if (res != Runners.NO_RETURN) return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
ctx.popFrame(frame);
|
ctx.popFrame(frame);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public CodeFunction(Environment environment, String name, FunctionBody body, ValueVariable... captures) {
|
public CodeFunction(Environment environment, String name, FunctionBody body, ValueVariable... captures) {
|
||||||
super(name, body.argsN);
|
super(name, body.argsN);
|
||||||
this.captures = captures;
|
this.captures = captures;
|
||||||
this.captureNames = body.captureNames;
|
this.captureNames = body.captureNames;
|
||||||
this.localNames = body.localNames;
|
this.localNames = body.localNames;
|
||||||
this.environment = environment;
|
this.environment = environment;
|
||||||
this.localsN = body.localsN;
|
this.localsN = body.localsN;
|
||||||
this.body = body.instructions;
|
this.body = body.instructions;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,70 +1,70 @@
|
|||||||
package me.topchetoeu.jscript.engine.values;
|
package me.topchetoeu.jscript.engine.values;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
|
|
||||||
public abstract class FunctionValue extends ObjectValue {
|
public abstract class FunctionValue extends ObjectValue {
|
||||||
public String name = "";
|
public String name = "";
|
||||||
public boolean special = false;
|
public boolean special = false;
|
||||||
public int length;
|
public int length;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return String.format("function %s(...)", name);
|
return String.format("function %s(...)", name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract Object call(Context ctx, Object thisArg, Object ...args);
|
public abstract Object call(Context ctx, Object thisArg, Object ...args);
|
||||||
public Object call(Context ctx) {
|
public Object call(Context ctx) {
|
||||||
return call(ctx, null);
|
return call(ctx, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Object getField(Context ctx, Object key) {
|
protected Object getField(Context ctx, Object key) {
|
||||||
if ("name".equals(key)) return name;
|
if ("name".equals(key)) return name;
|
||||||
if ("length".equals(key)) return length;
|
if ("length".equals(key)) return length;
|
||||||
return super.getField(ctx, key);
|
return super.getField(ctx, key);
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
protected boolean setField(Context ctx, Object key, Object val) {
|
protected boolean setField(Context ctx, Object key, Object val) {
|
||||||
if ("name".equals(key)) name = Values.toString(ctx, val);
|
if ("name".equals(key)) name = Values.toString(ctx, val);
|
||||||
else if ("length".equals(key)) length = (int)Values.toNumber(ctx, val);
|
else if ("length".equals(key)) length = (int)Values.toNumber(ctx, val);
|
||||||
else return super.setField(ctx, key, val);
|
else return super.setField(ctx, key, val);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
protected boolean hasField(Context ctx, Object key) {
|
protected boolean hasField(Context ctx, Object key) {
|
||||||
if ("name".equals(key)) return true;
|
if ("name".equals(key)) return true;
|
||||||
if ("length".equals(key)) return true;
|
if ("length".equals(key)) return true;
|
||||||
return super.hasField(ctx, key);
|
return super.hasField(ctx, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Object> keys(boolean includeNonEnumerable) {
|
public List<Object> keys(boolean includeNonEnumerable) {
|
||||||
var res = super.keys(includeNonEnumerable);
|
var res = super.keys(includeNonEnumerable);
|
||||||
if (includeNonEnumerable) {
|
if (includeNonEnumerable) {
|
||||||
res.add("name");
|
res.add("name");
|
||||||
res.add("length");
|
res.add("length");
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public FunctionValue(String name, int length) {
|
public FunctionValue(String name, int length) {
|
||||||
super(PlaceholderProto.FUNCTION);
|
super(PlaceholderProto.FUNCTION);
|
||||||
|
|
||||||
if (name == null) name = "";
|
if (name == null) name = "";
|
||||||
this.length = length;
|
this.length = length;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
|
||||||
nonConfigurableSet.add("name");
|
nonConfigurableSet.add("name");
|
||||||
nonEnumerableSet.add("name");
|
nonEnumerableSet.add("name");
|
||||||
nonWritableSet.add("length");
|
nonWritableSet.add("length");
|
||||||
nonConfigurableSet.add("length");
|
nonConfigurableSet.add("length");
|
||||||
nonEnumerableSet.add("length");
|
nonEnumerableSet.add("length");
|
||||||
|
|
||||||
var proto = new ObjectValue();
|
var proto = new ObjectValue();
|
||||||
proto.defineProperty(null, "constructor", this, true, false, false);
|
proto.defineProperty(null, "constructor", this, true, false, false);
|
||||||
this.defineProperty(null, "prototype", proto, true, false, false);
|
this.defineProperty(null, "prototype", proto, true, false, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
package me.topchetoeu.jscript.engine.values;
|
package me.topchetoeu.jscript.engine.values;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
|
|
||||||
public class NativeFunction extends FunctionValue {
|
public class NativeFunction extends FunctionValue {
|
||||||
public static interface NativeFunctionRunner {
|
public static interface NativeFunctionRunner {
|
||||||
Object run(Context ctx, Object thisArg, Object[] args);
|
Object run(Context ctx, Object thisArg, Object[] args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final NativeFunctionRunner action;
|
public final NativeFunctionRunner action;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object call(Context ctx, Object thisArg, Object ...args) {
|
public Object call(Context ctx, Object thisArg, Object ...args) {
|
||||||
return action.run(ctx, thisArg, args);
|
return action.run(ctx, thisArg, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public NativeFunction(String name, NativeFunctionRunner action) {
|
public NativeFunction(String name, NativeFunctionRunner action) {
|
||||||
super(name, 0);
|
super(name, 0);
|
||||||
this.action = action;
|
this.action = action;
|
||||||
}
|
}
|
||||||
public NativeFunction(NativeFunctionRunner action) {
|
public NativeFunction(NativeFunctionRunner action) {
|
||||||
super("", 0);
|
super("", 0);
|
||||||
this.action = action;
|
this.action = action;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,32 +1,32 @@
|
|||||||
package me.topchetoeu.jscript.engine.values;
|
package me.topchetoeu.jscript.engine.values;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
|
|
||||||
public class NativeWrapper extends ObjectValue {
|
public class NativeWrapper extends ObjectValue {
|
||||||
private static final Object NATIVE_PROTO = new Object();
|
private static final Object NATIVE_PROTO = new Object();
|
||||||
public final Object wrapped;
|
public final Object wrapped;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ObjectValue getPrototype(Context ctx) {
|
public ObjectValue getPrototype(Context ctx) {
|
||||||
if (prototype == NATIVE_PROTO) return ctx.environment().wrappers.getProto(wrapped.getClass());
|
if (prototype == NATIVE_PROTO) return ctx.environment().wrappers.getProto(wrapped.getClass());
|
||||||
else return super.getPrototype(ctx);
|
else return super.getPrototype(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return wrapped.toString();
|
return wrapped.toString();
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
return wrapped.equals(obj);
|
return wrapped.equals(obj);
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return wrapped.hashCode();
|
return wrapped.hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
public NativeWrapper(Object wrapped) {
|
public NativeWrapper(Object wrapped) {
|
||||||
this.wrapped = wrapped;
|
this.wrapped = wrapped;
|
||||||
prototype = NATIVE_PROTO;
|
prototype = NATIVE_PROTO;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,344 +1,344 @@
|
|||||||
package me.topchetoeu.jscript.engine.values;
|
package me.topchetoeu.jscript.engine.values;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
|
|
||||||
public class ObjectValue {
|
public class ObjectValue {
|
||||||
public static enum PlaceholderProto {
|
public static enum PlaceholderProto {
|
||||||
NONE,
|
NONE,
|
||||||
OBJECT,
|
OBJECT,
|
||||||
ARRAY,
|
ARRAY,
|
||||||
FUNCTION,
|
FUNCTION,
|
||||||
ERROR,
|
ERROR,
|
||||||
SYNTAX_ERROR,
|
SYNTAX_ERROR,
|
||||||
TYPE_ERROR,
|
TYPE_ERROR,
|
||||||
RANGE_ERROR,
|
RANGE_ERROR,
|
||||||
}
|
}
|
||||||
public static enum State {
|
public static enum State {
|
||||||
NORMAL,
|
NORMAL,
|
||||||
NO_EXTENSIONS,
|
NO_EXTENSIONS,
|
||||||
SEALED,
|
SEALED,
|
||||||
FROZEN,
|
FROZEN,
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Property {
|
public static class Property {
|
||||||
public final FunctionValue getter;
|
public final FunctionValue getter;
|
||||||
public final FunctionValue setter;
|
public final FunctionValue setter;
|
||||||
|
|
||||||
public Property(FunctionValue getter, FunctionValue setter) {
|
public Property(FunctionValue getter, FunctionValue setter) {
|
||||||
this.getter = getter;
|
this.getter = getter;
|
||||||
this.setter = setter;
|
this.setter = setter;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Object OBJ_PROTO = new Object();
|
private static final Object OBJ_PROTO = new Object();
|
||||||
private static final Object ARR_PROTO = new Object();
|
private static final Object ARR_PROTO = new Object();
|
||||||
private static final Object FUNC_PROTO = new Object();
|
private static final Object FUNC_PROTO = new Object();
|
||||||
private static final Object ERR_PROTO = new Object();
|
private static final Object ERR_PROTO = new Object();
|
||||||
private static final Object SYNTAX_ERR_PROTO = new Object();
|
private static final Object SYNTAX_ERR_PROTO = new Object();
|
||||||
private static final Object TYPE_ERR_PROTO = new Object();
|
private static final Object TYPE_ERR_PROTO = new Object();
|
||||||
private static final Object RANGE_ERR_PROTO = new Object();
|
private static final Object RANGE_ERR_PROTO = new Object();
|
||||||
|
|
||||||
protected Object prototype;
|
protected Object prototype;
|
||||||
|
|
||||||
public State state = State.NORMAL;
|
public State state = State.NORMAL;
|
||||||
public LinkedHashMap<Object, Object> values = new LinkedHashMap<>();
|
public LinkedHashMap<Object, Object> values = new LinkedHashMap<>();
|
||||||
public LinkedHashMap<Object, Property> properties = new LinkedHashMap<>();
|
public LinkedHashMap<Object, Property> properties = new LinkedHashMap<>();
|
||||||
public LinkedHashSet<Object> nonWritableSet = new LinkedHashSet<>();
|
public LinkedHashSet<Object> nonWritableSet = new LinkedHashSet<>();
|
||||||
public LinkedHashSet<Object> nonConfigurableSet = new LinkedHashSet<>();
|
public LinkedHashSet<Object> nonConfigurableSet = new LinkedHashSet<>();
|
||||||
public LinkedHashSet<Object> nonEnumerableSet = new LinkedHashSet<>();
|
public LinkedHashSet<Object> nonEnumerableSet = new LinkedHashSet<>();
|
||||||
|
|
||||||
public final boolean memberWritable(Object key) {
|
public final boolean memberWritable(Object key) {
|
||||||
if (state == State.FROZEN) return false;
|
if (state == State.FROZEN) return false;
|
||||||
return !values.containsKey(key) || !nonWritableSet.contains(key);
|
return !values.containsKey(key) || !nonWritableSet.contains(key);
|
||||||
}
|
}
|
||||||
public final boolean memberConfigurable(Object key) {
|
public final boolean memberConfigurable(Object key) {
|
||||||
if (state == State.SEALED || state == State.FROZEN) return false;
|
if (state == State.SEALED || state == State.FROZEN) return false;
|
||||||
return !nonConfigurableSet.contains(key);
|
return !nonConfigurableSet.contains(key);
|
||||||
}
|
}
|
||||||
public final boolean memberEnumerable(Object key) {
|
public final boolean memberEnumerable(Object key) {
|
||||||
return !nonEnumerableSet.contains(key);
|
return !nonEnumerableSet.contains(key);
|
||||||
}
|
}
|
||||||
public final boolean extensible() {
|
public final boolean extensible() {
|
||||||
return state == State.NORMAL;
|
return state == State.NORMAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void preventExtensions() {
|
public final void preventExtensions() {
|
||||||
if (state == State.NORMAL) state = State.NO_EXTENSIONS;
|
if (state == State.NORMAL) state = State.NO_EXTENSIONS;
|
||||||
}
|
}
|
||||||
public final void seal() {
|
public final void seal() {
|
||||||
if (state == State.NORMAL || state == State.NO_EXTENSIONS) state = State.SEALED;
|
if (state == State.NORMAL || state == State.NO_EXTENSIONS) state = State.SEALED;
|
||||||
}
|
}
|
||||||
public final void freeze() {
|
public final void freeze() {
|
||||||
state = State.FROZEN;
|
state = State.FROZEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean defineProperty(Context ctx, Object key, Object val, boolean writable, boolean configurable, boolean enumerable) {
|
public final boolean defineProperty(Context ctx, Object key, Object val, boolean writable, boolean configurable, boolean enumerable) {
|
||||||
key = Values.normalize(ctx, key); val = Values.normalize(ctx, val);
|
key = Values.normalize(ctx, key); val = Values.normalize(ctx, val);
|
||||||
boolean reconfigured =
|
boolean reconfigured =
|
||||||
writable != memberWritable(key) ||
|
writable != memberWritable(key) ||
|
||||||
configurable != memberConfigurable(key) ||
|
configurable != memberConfigurable(key) ||
|
||||||
enumerable != memberEnumerable(key);
|
enumerable != memberEnumerable(key);
|
||||||
|
|
||||||
if (!reconfigured) {
|
if (!reconfigured) {
|
||||||
if (!memberWritable(key)) {
|
if (!memberWritable(key)) {
|
||||||
var a = values.get(key);
|
var a = values.get(key);
|
||||||
var b = val;
|
var b = val;
|
||||||
if (a == null || b == null) return a == null && b == null;
|
if (a == null || b == null) return a == null && b == null;
|
||||||
return a == b || a.equals(b);
|
return a == b || a.equals(b);
|
||||||
}
|
}
|
||||||
values.put(key, val);
|
values.put(key, val);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
properties.containsKey(key) &&
|
properties.containsKey(key) &&
|
||||||
values.get(key) == val &&
|
values.get(key) == val &&
|
||||||
!reconfigured
|
!reconfigured
|
||||||
) return true;
|
) return true;
|
||||||
|
|
||||||
if (!extensible() && !values.containsKey(key) && !properties.containsKey(key)) return false;
|
if (!extensible() && !values.containsKey(key) && !properties.containsKey(key)) return false;
|
||||||
if (!memberConfigurable(key)) return false;
|
if (!memberConfigurable(key)) return false;
|
||||||
|
|
||||||
nonWritableSet.remove(key);
|
nonWritableSet.remove(key);
|
||||||
nonEnumerableSet.remove(key);
|
nonEnumerableSet.remove(key);
|
||||||
properties.remove(key);
|
properties.remove(key);
|
||||||
values.remove(key);
|
values.remove(key);
|
||||||
|
|
||||||
if (!writable) nonWritableSet.add(key);
|
if (!writable) nonWritableSet.add(key);
|
||||||
if (!configurable) nonConfigurableSet.add(key);
|
if (!configurable) nonConfigurableSet.add(key);
|
||||||
if (!enumerable) nonEnumerableSet.add(key);
|
if (!enumerable) nonEnumerableSet.add(key);
|
||||||
|
|
||||||
values.put(key, val);
|
values.put(key, val);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
public final boolean defineProperty(Context ctx, Object key, Object val) {
|
public final boolean defineProperty(Context ctx, Object key, Object val) {
|
||||||
return defineProperty(ctx, key, val, true, true, true);
|
return defineProperty(ctx, key, val, true, true, true);
|
||||||
}
|
}
|
||||||
public final boolean defineProperty(Context ctx, Object key, FunctionValue getter, FunctionValue setter, boolean configurable, boolean enumerable) {
|
public final boolean defineProperty(Context ctx, Object key, FunctionValue getter, FunctionValue setter, boolean configurable, boolean enumerable) {
|
||||||
key = Values.normalize(ctx, key);
|
key = Values.normalize(ctx, key);
|
||||||
if (
|
if (
|
||||||
properties.containsKey(key) &&
|
properties.containsKey(key) &&
|
||||||
properties.get(key).getter == getter &&
|
properties.get(key).getter == getter &&
|
||||||
properties.get(key).setter == setter &&
|
properties.get(key).setter == setter &&
|
||||||
!configurable == nonConfigurableSet.contains(key) &&
|
!configurable == nonConfigurableSet.contains(key) &&
|
||||||
!enumerable == nonEnumerableSet.contains(key)
|
!enumerable == nonEnumerableSet.contains(key)
|
||||||
) return true;
|
) return true;
|
||||||
if (!extensible() && !values.containsKey(key) && !properties.containsKey(key)) return false;
|
if (!extensible() && !values.containsKey(key) && !properties.containsKey(key)) return false;
|
||||||
if (!memberConfigurable(key)) return false;
|
if (!memberConfigurable(key)) return false;
|
||||||
|
|
||||||
nonWritableSet.remove(key);
|
nonWritableSet.remove(key);
|
||||||
nonEnumerableSet.remove(key);
|
nonEnumerableSet.remove(key);
|
||||||
properties.remove(key);
|
properties.remove(key);
|
||||||
values.remove(key);
|
values.remove(key);
|
||||||
|
|
||||||
if (!configurable) nonConfigurableSet.add(key);
|
if (!configurable) nonConfigurableSet.add(key);
|
||||||
if (!enumerable) nonEnumerableSet.add(key);
|
if (!enumerable) nonEnumerableSet.add(key);
|
||||||
|
|
||||||
properties.put(key, new Property(getter, setter));
|
properties.put(key, new Property(getter, setter));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ObjectValue getPrototype(Context ctx) {
|
public ObjectValue getPrototype(Context ctx) {
|
||||||
try {
|
try {
|
||||||
if (prototype == OBJ_PROTO) return ctx.environment().proto("object");
|
if (prototype == OBJ_PROTO) return ctx.environment().proto("object");
|
||||||
if (prototype == ARR_PROTO) return ctx.environment().proto("array");
|
if (prototype == ARR_PROTO) return ctx.environment().proto("array");
|
||||||
if (prototype == FUNC_PROTO) return ctx.environment().proto("function");
|
if (prototype == FUNC_PROTO) return ctx.environment().proto("function");
|
||||||
if (prototype == ERR_PROTO) return ctx.environment().proto("error");
|
if (prototype == ERR_PROTO) return ctx.environment().proto("error");
|
||||||
if (prototype == RANGE_ERR_PROTO) return ctx.environment().proto("rangeErr");
|
if (prototype == RANGE_ERR_PROTO) return ctx.environment().proto("rangeErr");
|
||||||
if (prototype == SYNTAX_ERR_PROTO) return ctx.environment().proto("syntaxErr");
|
if (prototype == SYNTAX_ERR_PROTO) return ctx.environment().proto("syntaxErr");
|
||||||
if (prototype == TYPE_ERR_PROTO) return ctx.environment().proto("typeErr");
|
if (prototype == TYPE_ERR_PROTO) return ctx.environment().proto("typeErr");
|
||||||
}
|
}
|
||||||
catch (NullPointerException e) { return null; }
|
catch (NullPointerException e) { return null; }
|
||||||
|
|
||||||
return (ObjectValue)prototype;
|
return (ObjectValue)prototype;
|
||||||
}
|
}
|
||||||
public final boolean setPrototype(Context ctx, Object val) {
|
public final boolean setPrototype(Context ctx, Object val) {
|
||||||
val = Values.normalize(ctx, val);
|
val = Values.normalize(ctx, val);
|
||||||
|
|
||||||
if (!extensible()) return false;
|
if (!extensible()) return false;
|
||||||
if (val == null || val == Values.NULL) {
|
if (val == null || val == Values.NULL) {
|
||||||
prototype = null;
|
prototype = null;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (Values.isObject(val)) {
|
else if (Values.isObject(val)) {
|
||||||
var obj = Values.object(val);
|
var obj = Values.object(val);
|
||||||
|
|
||||||
if (ctx != null && ctx.environment() != null) {
|
if (ctx != null && ctx.environment() != null) {
|
||||||
if (obj == ctx.environment().proto("object")) prototype = OBJ_PROTO;
|
if (obj == ctx.environment().proto("object")) prototype = OBJ_PROTO;
|
||||||
else if (obj == ctx.environment().proto("array")) prototype = ARR_PROTO;
|
else if (obj == ctx.environment().proto("array")) prototype = ARR_PROTO;
|
||||||
else if (obj == ctx.environment().proto("function")) prototype = FUNC_PROTO;
|
else if (obj == ctx.environment().proto("function")) prototype = FUNC_PROTO;
|
||||||
else if (obj == ctx.environment().proto("error")) prototype = ERR_PROTO;
|
else if (obj == ctx.environment().proto("error")) prototype = ERR_PROTO;
|
||||||
else if (obj == ctx.environment().proto("syntaxErr")) prototype = SYNTAX_ERR_PROTO;
|
else if (obj == ctx.environment().proto("syntaxErr")) prototype = SYNTAX_ERR_PROTO;
|
||||||
else if (obj == ctx.environment().proto("typeErr")) prototype = TYPE_ERR_PROTO;
|
else if (obj == ctx.environment().proto("typeErr")) prototype = TYPE_ERR_PROTO;
|
||||||
else if (obj == ctx.environment().proto("rangeErr")) prototype = RANGE_ERR_PROTO;
|
else if (obj == ctx.environment().proto("rangeErr")) prototype = RANGE_ERR_PROTO;
|
||||||
else prototype = obj;
|
else prototype = obj;
|
||||||
}
|
}
|
||||||
else prototype = obj;
|
else prototype = obj;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
public final boolean setPrototype(PlaceholderProto val) {
|
public final boolean setPrototype(PlaceholderProto val) {
|
||||||
if (!extensible()) return false;
|
if (!extensible()) return false;
|
||||||
switch (val) {
|
switch (val) {
|
||||||
case OBJECT: prototype = OBJ_PROTO; break;
|
case OBJECT: prototype = OBJ_PROTO; break;
|
||||||
case FUNCTION: prototype = FUNC_PROTO; break;
|
case FUNCTION: prototype = FUNC_PROTO; break;
|
||||||
case ARRAY: prototype = ARR_PROTO; break;
|
case ARRAY: prototype = ARR_PROTO; break;
|
||||||
case ERROR: prototype = ERR_PROTO; break;
|
case ERROR: prototype = ERR_PROTO; break;
|
||||||
case SYNTAX_ERROR: prototype = SYNTAX_ERR_PROTO; break;
|
case SYNTAX_ERROR: prototype = SYNTAX_ERR_PROTO; break;
|
||||||
case TYPE_ERROR: prototype = TYPE_ERR_PROTO; break;
|
case TYPE_ERROR: prototype = TYPE_ERR_PROTO; break;
|
||||||
case RANGE_ERROR: prototype = RANGE_ERR_PROTO; break;
|
case RANGE_ERROR: prototype = RANGE_ERR_PROTO; break;
|
||||||
case NONE: prototype = null; break;
|
case NONE: prototype = null; break;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Property getProperty(Context ctx, Object key) {
|
protected Property getProperty(Context ctx, Object key) {
|
||||||
if (properties.containsKey(key)) return properties.get(key);
|
if (properties.containsKey(key)) return properties.get(key);
|
||||||
var proto = getPrototype(ctx);
|
var proto = getPrototype(ctx);
|
||||||
if (proto != null) return proto.getProperty(ctx, key);
|
if (proto != null) return proto.getProperty(ctx, key);
|
||||||
else return null;
|
else return null;
|
||||||
}
|
}
|
||||||
protected Object getField(Context ctx, Object key) {
|
protected Object getField(Context ctx, Object key) {
|
||||||
if (values.containsKey(key)) return values.get(key);
|
if (values.containsKey(key)) return values.get(key);
|
||||||
var proto = getPrototype(ctx);
|
var proto = getPrototype(ctx);
|
||||||
if (proto != null) return proto.getField(ctx, key);
|
if (proto != null) return proto.getField(ctx, key);
|
||||||
else return null;
|
else return null;
|
||||||
}
|
}
|
||||||
protected boolean setField(Context ctx, Object key, Object val) {
|
protected boolean setField(Context ctx, Object key, Object val) {
|
||||||
if (val instanceof FunctionValue && ((FunctionValue)val).name.equals("")) {
|
if (val instanceof FunctionValue && ((FunctionValue)val).name.equals("")) {
|
||||||
((FunctionValue)val).name = Values.toString(ctx, key);
|
((FunctionValue)val).name = Values.toString(ctx, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
values.put(key, val);
|
values.put(key, val);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
protected void deleteField(Context ctx, Object key) {
|
protected void deleteField(Context ctx, Object key) {
|
||||||
values.remove(key);
|
values.remove(key);
|
||||||
}
|
}
|
||||||
protected boolean hasField(Context ctx, Object key) {
|
protected boolean hasField(Context ctx, Object key) {
|
||||||
return values.containsKey(key);
|
return values.containsKey(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final Object getMember(Context ctx, Object key, Object thisArg) {
|
public final Object getMember(Context ctx, Object key, Object thisArg) {
|
||||||
key = Values.normalize(ctx, key);
|
key = Values.normalize(ctx, key);
|
||||||
|
|
||||||
if ("__proto__".equals(key)) {
|
if ("__proto__".equals(key)) {
|
||||||
var res = getPrototype(ctx);
|
var res = getPrototype(ctx);
|
||||||
return res == null ? Values.NULL : res;
|
return res == null ? Values.NULL : res;
|
||||||
}
|
}
|
||||||
|
|
||||||
var prop = getProperty(ctx, key);
|
var prop = getProperty(ctx, key);
|
||||||
|
|
||||||
if (prop != null) {
|
if (prop != null) {
|
||||||
if (prop.getter == null) return null;
|
if (prop.getter == null) return null;
|
||||||
else return prop.getter.call(ctx, Values.normalize(ctx, thisArg));
|
else return prop.getter.call(ctx, Values.normalize(ctx, thisArg));
|
||||||
}
|
}
|
||||||
else return getField(ctx, key);
|
else return getField(ctx, key);
|
||||||
}
|
}
|
||||||
public final Object getMember(Context ctx, Object key) {
|
public final Object getMember(Context ctx, Object key) {
|
||||||
return getMember(ctx, key, this);
|
return getMember(ctx, key, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean setMember(Context ctx, Object key, Object val, Object thisArg, boolean onlyProps) {
|
public final boolean setMember(Context ctx, Object key, Object val, Object thisArg, boolean onlyProps) {
|
||||||
key = Values.normalize(ctx, key); val = Values.normalize(ctx, val);
|
key = Values.normalize(ctx, key); val = Values.normalize(ctx, val);
|
||||||
|
|
||||||
var prop = getProperty(ctx, key);
|
var prop = getProperty(ctx, key);
|
||||||
if (prop != null) {
|
if (prop != null) {
|
||||||
if (prop.setter == null) return false;
|
if (prop.setter == null) return false;
|
||||||
prop.setter.call(ctx, Values.normalize(ctx, thisArg), val);
|
prop.setter.call(ctx, Values.normalize(ctx, thisArg), val);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (onlyProps) return false;
|
else if (onlyProps) return false;
|
||||||
else if (!extensible() && !values.containsKey(key)) return false;
|
else if (!extensible() && !values.containsKey(key)) return false;
|
||||||
else if (key == null) {
|
else if (key == null) {
|
||||||
values.put(key, val);
|
values.put(key, val);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if ("__proto__".equals(key)) return setPrototype(ctx, val);
|
else if ("__proto__".equals(key)) return setPrototype(ctx, val);
|
||||||
else if (nonWritableSet.contains(key)) return false;
|
else if (nonWritableSet.contains(key)) return false;
|
||||||
else return setField(ctx, key, val);
|
else return setField(ctx, key, val);
|
||||||
}
|
}
|
||||||
public final boolean setMember(Context ctx, Object key, Object val, boolean onlyProps) {
|
public final boolean setMember(Context ctx, Object key, Object val, boolean onlyProps) {
|
||||||
return setMember(ctx, Values.normalize(ctx, key), Values.normalize(ctx, val), this, onlyProps);
|
return setMember(ctx, Values.normalize(ctx, key), Values.normalize(ctx, val), this, onlyProps);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean hasMember(Context ctx, Object key, boolean own) {
|
public final boolean hasMember(Context ctx, Object key, boolean own) {
|
||||||
key = Values.normalize(ctx, key);
|
key = Values.normalize(ctx, key);
|
||||||
|
|
||||||
if (key != null && "__proto__".equals(key)) return true;
|
if (key != null && "__proto__".equals(key)) return true;
|
||||||
if (hasField(ctx, key)) return true;
|
if (hasField(ctx, key)) return true;
|
||||||
if (properties.containsKey(key)) return true;
|
if (properties.containsKey(key)) return true;
|
||||||
if (own) return false;
|
if (own) return false;
|
||||||
var proto = getPrototype(ctx);
|
var proto = getPrototype(ctx);
|
||||||
return proto != null && proto.hasMember(ctx, key, own);
|
return proto != null && proto.hasMember(ctx, key, own);
|
||||||
}
|
}
|
||||||
public final boolean deleteMember(Context ctx, Object key) {
|
public final boolean deleteMember(Context ctx, Object key) {
|
||||||
key = Values.normalize(ctx, key);
|
key = Values.normalize(ctx, key);
|
||||||
|
|
||||||
if (!memberConfigurable(key)) return false;
|
if (!memberConfigurable(key)) return false;
|
||||||
properties.remove(key);
|
properties.remove(key);
|
||||||
nonWritableSet.remove(key);
|
nonWritableSet.remove(key);
|
||||||
nonEnumerableSet.remove(key);
|
nonEnumerableSet.remove(key);
|
||||||
deleteField(ctx, key);
|
deleteField(ctx, key);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final ObjectValue getMemberDescriptor(Context ctx, Object key) {
|
public final ObjectValue getMemberDescriptor(Context ctx, Object key) {
|
||||||
key = Values.normalize(ctx, key);
|
key = Values.normalize(ctx, key);
|
||||||
|
|
||||||
var prop = properties.get(key);
|
var prop = properties.get(key);
|
||||||
var res = new ObjectValue();
|
var res = new ObjectValue();
|
||||||
|
|
||||||
res.defineProperty(ctx, "configurable", memberConfigurable(key));
|
res.defineProperty(ctx, "configurable", memberConfigurable(key));
|
||||||
res.defineProperty(ctx, "enumerable", memberEnumerable(key));
|
res.defineProperty(ctx, "enumerable", memberEnumerable(key));
|
||||||
|
|
||||||
if (prop != null) {
|
if (prop != null) {
|
||||||
res.defineProperty(ctx, "get", prop.getter);
|
res.defineProperty(ctx, "get", prop.getter);
|
||||||
res.defineProperty(ctx, "set", prop.setter);
|
res.defineProperty(ctx, "set", prop.setter);
|
||||||
}
|
}
|
||||||
else if (hasField(ctx, key)) {
|
else if (hasField(ctx, key)) {
|
||||||
res.defineProperty(ctx, "value", values.get(key));
|
res.defineProperty(ctx, "value", values.get(key));
|
||||||
res.defineProperty(ctx, "writable", memberWritable(key));
|
res.defineProperty(ctx, "writable", memberWritable(key));
|
||||||
}
|
}
|
||||||
else return null;
|
else return null;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Object> keys(boolean includeNonEnumerable) {
|
public List<Object> keys(boolean includeNonEnumerable) {
|
||||||
var res = new ArrayList<Object>();
|
var res = new ArrayList<Object>();
|
||||||
|
|
||||||
for (var key : values.keySet()) {
|
for (var key : values.keySet()) {
|
||||||
if (nonEnumerableSet.contains(key) && !includeNonEnumerable) continue;
|
if (nonEnumerableSet.contains(key) && !includeNonEnumerable) continue;
|
||||||
res.add(key);
|
res.add(key);
|
||||||
}
|
}
|
||||||
for (var key : properties.keySet()) {
|
for (var key : properties.keySet()) {
|
||||||
if (nonEnumerableSet.contains(key) && !includeNonEnumerable) continue;
|
if (nonEnumerableSet.contains(key) && !includeNonEnumerable) continue;
|
||||||
res.add(key);
|
res.add(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ObjectValue(Context ctx, Map<?, ?> values) {
|
public ObjectValue(Context ctx, Map<?, ?> values) {
|
||||||
this(PlaceholderProto.OBJECT);
|
this(PlaceholderProto.OBJECT);
|
||||||
for (var el : values.entrySet()) {
|
for (var el : values.entrySet()) {
|
||||||
defineProperty(ctx, el.getKey(), el.getValue());
|
defineProperty(ctx, el.getKey(), el.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public ObjectValue(PlaceholderProto proto) {
|
public ObjectValue(PlaceholderProto proto) {
|
||||||
nonConfigurableSet.add("__proto__");
|
nonConfigurableSet.add("__proto__");
|
||||||
nonEnumerableSet.add("__proto__");
|
nonEnumerableSet.add("__proto__");
|
||||||
setPrototype(proto);
|
setPrototype(proto);
|
||||||
}
|
}
|
||||||
public ObjectValue() {
|
public ObjectValue() {
|
||||||
this(PlaceholderProto.OBJECT);
|
this(PlaceholderProto.OBJECT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,54 +1,54 @@
|
|||||||
package me.topchetoeu.jscript.engine.values;
|
package me.topchetoeu.jscript.engine.values;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
import me.topchetoeu.jscript.engine.scope.ValueVariable;
|
import me.topchetoeu.jscript.engine.scope.ValueVariable;
|
||||||
|
|
||||||
public class ScopeValue extends ObjectValue {
|
public class ScopeValue extends ObjectValue {
|
||||||
public final ValueVariable[] variables;
|
public final ValueVariable[] variables;
|
||||||
public final HashMap<String, Integer> names = new HashMap<>();
|
public final HashMap<String, Integer> names = new HashMap<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Object getField(Context ctx, Object key) {
|
protected Object getField(Context ctx, Object key) {
|
||||||
if (names.containsKey(key)) return variables[names.get(key)].get(ctx);
|
if (names.containsKey(key)) return variables[names.get(key)].get(ctx);
|
||||||
return super.getField(ctx, key);
|
return super.getField(ctx, key);
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
protected boolean setField(Context ctx, Object key, Object val) {
|
protected boolean setField(Context ctx, Object key, Object val) {
|
||||||
if (names.containsKey(key)) {
|
if (names.containsKey(key)) {
|
||||||
variables[names.get(key)].set(ctx, val);
|
variables[names.get(key)].set(ctx, val);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
var proto = getPrototype(ctx);
|
var proto = getPrototype(ctx);
|
||||||
if (proto != null && proto.hasField(ctx, key) && proto.setField(ctx, key, val)) return true;
|
if (proto != null && proto.hasField(ctx, key) && proto.setField(ctx, key, val)) return true;
|
||||||
|
|
||||||
return super.setField(ctx, key, val);
|
return super.setField(ctx, key, val);
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
protected void deleteField(Context ctx, Object key) {
|
protected void deleteField(Context ctx, Object key) {
|
||||||
if (names.containsKey(key)) return;
|
if (names.containsKey(key)) return;
|
||||||
super.deleteField(ctx, key);
|
super.deleteField(ctx, key);
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
protected boolean hasField(Context ctx, Object key) {
|
protected boolean hasField(Context ctx, Object key) {
|
||||||
if (names.containsKey(key)) return true;
|
if (names.containsKey(key)) return true;
|
||||||
return super.hasField(ctx, key);
|
return super.hasField(ctx, key);
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public List<Object> keys(boolean includeNonEnumerable) {
|
public List<Object> keys(boolean includeNonEnumerable) {
|
||||||
var res = super.keys(includeNonEnumerable);
|
var res = super.keys(includeNonEnumerable);
|
||||||
res.addAll(names.keySet());
|
res.addAll(names.keySet());
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ScopeValue(ValueVariable[] variables, String[] names) {
|
public ScopeValue(ValueVariable[] variables, String[] names) {
|
||||||
this.variables = variables;
|
this.variables = variables;
|
||||||
for (var i = 0; i < names.length && i < variables.length; i++) {
|
for (var i = 0; i < names.length && i < variables.length; i++) {
|
||||||
this.names.put(names[i], i);
|
this.names.put(names[i], i);
|
||||||
this.nonConfigurableSet.add(names[i]);
|
this.nonConfigurableSet.add(names[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
package me.topchetoeu.jscript.engine.values;
|
package me.topchetoeu.jscript.engine.values;
|
||||||
|
|
||||||
public final class Symbol {
|
public final class Symbol {
|
||||||
public final String value;
|
public final String value;
|
||||||
|
|
||||||
public Symbol(String value) {
|
public Symbol(String value) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
if (value == null) return "Symbol";
|
if (value == null) return "Symbol";
|
||||||
else return "@@" + value;
|
else return "@@" + value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
package me.topchetoeu.jscript.events;
|
package me.topchetoeu.jscript.events;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.exceptions.InterruptException;
|
import me.topchetoeu.jscript.exceptions.InterruptException;
|
||||||
|
|
||||||
public interface Awaitable<T> {
|
public interface Awaitable<T> {
|
||||||
T await() throws FinishedException;
|
T await() throws FinishedException;
|
||||||
|
|
||||||
default Observable<T> toObservable() {
|
default Observable<T> toObservable() {
|
||||||
return sub -> {
|
return sub -> {
|
||||||
var thread = new Thread(() -> {
|
var thread = new Thread(() -> {
|
||||||
try {
|
try {
|
||||||
sub.next(await());
|
sub.next(await());
|
||||||
sub.finish();
|
sub.finish();
|
||||||
}
|
}
|
||||||
catch (InterruptException | FinishedException e) { sub.finish(); }
|
catch (InterruptException | FinishedException e) { sub.finish(); }
|
||||||
catch (RuntimeException e) {
|
catch (RuntimeException e) {
|
||||||
sub.error(e);
|
sub.error(e);
|
||||||
}
|
}
|
||||||
}, "Awaiter");
|
}, "Awaiter");
|
||||||
thread.start();
|
thread.start();
|
||||||
|
|
||||||
return () -> thread.interrupt();
|
return () -> thread.interrupt();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,34 +1,34 @@
|
|||||||
package me.topchetoeu.jscript.events;
|
package me.topchetoeu.jscript.events;
|
||||||
|
|
||||||
public class DataNotifier<T> implements Awaitable<T> {
|
public class DataNotifier<T> implements Awaitable<T> {
|
||||||
private Notifier notifier = new Notifier();
|
private Notifier notifier = new Notifier();
|
||||||
private boolean isErr;
|
private boolean isErr;
|
||||||
private T val;
|
private T val;
|
||||||
private RuntimeException err;
|
private RuntimeException err;
|
||||||
|
|
||||||
public void error(RuntimeException t) {
|
public void error(RuntimeException t) {
|
||||||
err = t;
|
err = t;
|
||||||
isErr = true;
|
isErr = true;
|
||||||
notifier.next();
|
notifier.next();
|
||||||
}
|
}
|
||||||
public void error(Throwable t) {
|
public void error(Throwable t) {
|
||||||
error(new RuntimeException(t));
|
error(new RuntimeException(t));
|
||||||
}
|
}
|
||||||
public void next(T val) {
|
public void next(T val) {
|
||||||
this.val = val;
|
this.val = val;
|
||||||
isErr = false;
|
isErr = false;
|
||||||
notifier.next();
|
notifier.next();
|
||||||
}
|
}
|
||||||
public T await() {
|
public T await() {
|
||||||
notifier.await();
|
notifier.await();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (isErr) throw err;
|
if (isErr) throw err;
|
||||||
else return val;
|
else return val;
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
this.err = null;
|
this.err = null;
|
||||||
this.val = null;
|
this.val = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,49 +1,49 @@
|
|||||||
package me.topchetoeu.jscript.events;
|
package me.topchetoeu.jscript.events;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
|
||||||
public class Event<T> implements Observer<T>, Observable<T> {
|
public class Event<T> implements Observer<T>, Observable<T> {
|
||||||
private HashSet<Observer<T>> handlers = new HashSet<>();
|
private HashSet<Observer<T>> handlers = new HashSet<>();
|
||||||
|
|
||||||
public Handle on(Observer<T> handler) {
|
public Handle on(Observer<T> handler) {
|
||||||
if (handlers == null) {
|
if (handlers == null) {
|
||||||
handler.finish();
|
handler.finish();
|
||||||
return () -> {};
|
return () -> {};
|
||||||
}
|
}
|
||||||
|
|
||||||
handlers.add(handler);
|
handlers.add(handler);
|
||||||
return () -> {
|
return () -> {
|
||||||
if (handlers == null) return;
|
if (handlers == null) return;
|
||||||
handlers.remove(handler);
|
handlers.remove(handler);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isFinished() {
|
public boolean isFinished() {
|
||||||
return handlers == null;
|
return handlers == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void next(T value) {
|
public void next(T value) {
|
||||||
if (handlers == null) throw new IllegalStateException("Cannot use a finished event.");
|
if (handlers == null) throw new IllegalStateException("Cannot use a finished event.");
|
||||||
for (var handler : handlers) {
|
for (var handler : handlers) {
|
||||||
handler.next(value);
|
handler.next(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public void error(RuntimeException value) {
|
public void error(RuntimeException value) {
|
||||||
if (handlers == null) throw new IllegalStateException("Cannot use a finished event.");
|
if (handlers == null) throw new IllegalStateException("Cannot use a finished event.");
|
||||||
for (var handler : handlers) {
|
for (var handler : handlers) {
|
||||||
handler.error(value);
|
handler.error(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
handlers.clear();
|
handlers.clear();
|
||||||
handlers = null;
|
handlers = null;
|
||||||
}
|
}
|
||||||
public void finish() {
|
public void finish() {
|
||||||
if (handlers == null) throw new IllegalStateException("Cannot use a finished event.");
|
if (handlers == null) throw new IllegalStateException("Cannot use a finished event.");
|
||||||
for (var handler : handlers) {
|
for (var handler : handlers) {
|
||||||
handler.finish();
|
handler.finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
handlers.clear();
|
handlers.clear();
|
||||||
handlers = null;
|
handlers = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package me.topchetoeu.jscript.events;
|
package me.topchetoeu.jscript.events;
|
||||||
|
|
||||||
public class FinishedException extends RuntimeException {
|
public class FinishedException extends RuntimeException {
|
||||||
public FinishedException() {
|
public FinishedException() {
|
||||||
super("The observable has ended.");
|
super("The observable has ended.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
package me.topchetoeu.jscript.events;
|
package me.topchetoeu.jscript.events;
|
||||||
|
|
||||||
public interface Handle {
|
public interface Handle {
|
||||||
void free();
|
void free();
|
||||||
}
|
}
|
@ -1,19 +1,19 @@
|
|||||||
package me.topchetoeu.jscript.events;
|
package me.topchetoeu.jscript.events;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.exceptions.InterruptException;
|
import me.topchetoeu.jscript.exceptions.InterruptException;
|
||||||
|
|
||||||
public class Notifier {
|
public class Notifier {
|
||||||
private boolean ok = false;
|
private boolean ok = false;
|
||||||
|
|
||||||
public synchronized void next() {
|
public synchronized void next() {
|
||||||
ok = true;
|
ok = true;
|
||||||
notifyAll();
|
notifyAll();
|
||||||
}
|
}
|
||||||
public synchronized void await() {
|
public synchronized void await() {
|
||||||
try {
|
try {
|
||||||
while (!ok) wait();
|
while (!ok) wait();
|
||||||
ok = false;
|
ok = false;
|
||||||
}
|
}
|
||||||
catch (InterruptedException e) { throw new InterruptException(e); }
|
catch (InterruptedException e) { throw new InterruptException(e); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,75 +1,75 @@
|
|||||||
package me.topchetoeu.jscript.events;
|
package me.topchetoeu.jscript.events;
|
||||||
|
|
||||||
public interface Observable<T> {
|
public interface Observable<T> {
|
||||||
Handle on(Observer<T> val);
|
Handle on(Observer<T> val);
|
||||||
|
|
||||||
default Handle once(Observer<T> observer) {
|
default Handle once(Observer<T> observer) {
|
||||||
// Java is fucking retarded
|
// Java is fucking retarded
|
||||||
var unhandler = new Handle[1];
|
var unhandler = new Handle[1];
|
||||||
var shouldUnsub = new boolean[1];
|
var shouldUnsub = new boolean[1];
|
||||||
|
|
||||||
unhandler[0] = on(new Observer<>() {
|
unhandler[0] = on(new Observer<>() {
|
||||||
public void next(T data) {
|
public void next(T data) {
|
||||||
observer.next(data);
|
observer.next(data);
|
||||||
if (unhandler[0] == null) shouldUnsub[0] = true;
|
if (unhandler[0] == null) shouldUnsub[0] = true;
|
||||||
else unhandler[0].free();
|
else unhandler[0].free();
|
||||||
}
|
}
|
||||||
public void error(RuntimeException err) {
|
public void error(RuntimeException err) {
|
||||||
observer.error(err);
|
observer.error(err);
|
||||||
if (unhandler[0] == null) shouldUnsub[0] = true;
|
if (unhandler[0] == null) shouldUnsub[0] = true;
|
||||||
else unhandler[0].free();
|
else unhandler[0].free();
|
||||||
}
|
}
|
||||||
public void finish() {
|
public void finish() {
|
||||||
observer.finish();
|
observer.finish();
|
||||||
if (unhandler[0] == null) shouldUnsub[0] = true;
|
if (unhandler[0] == null) shouldUnsub[0] = true;
|
||||||
else unhandler[0].free();
|
else unhandler[0].free();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (shouldUnsub[0]) {
|
if (shouldUnsub[0]) {
|
||||||
unhandler[0].free();
|
unhandler[0].free();
|
||||||
return () -> {};
|
return () -> {};
|
||||||
}
|
}
|
||||||
else return unhandler[0];
|
else return unhandler[0];
|
||||||
}
|
}
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
default Awaitable<T> toAwaitable() {
|
default Awaitable<T> toAwaitable() {
|
||||||
return () -> {
|
return () -> {
|
||||||
var notifier = new Notifier();
|
var notifier = new Notifier();
|
||||||
var valRef = new Object[1];
|
var valRef = new Object[1];
|
||||||
var isErrRef = new boolean[1];
|
var isErrRef = new boolean[1];
|
||||||
|
|
||||||
once(new Observer<>() {
|
once(new Observer<>() {
|
||||||
public void next(T data) {
|
public void next(T data) {
|
||||||
valRef[0] = data;
|
valRef[0] = data;
|
||||||
notifier.next();
|
notifier.next();
|
||||||
}
|
}
|
||||||
public void error(RuntimeException err) {
|
public void error(RuntimeException err) {
|
||||||
isErrRef[0] = true;
|
isErrRef[0] = true;
|
||||||
valRef[0] = err;
|
valRef[0] = err;
|
||||||
notifier.next();
|
notifier.next();
|
||||||
}
|
}
|
||||||
public void finish() {
|
public void finish() {
|
||||||
isErrRef[0] = true;
|
isErrRef[0] = true;
|
||||||
valRef[0] = new FinishedException();
|
valRef[0] = new FinishedException();
|
||||||
notifier.next();
|
notifier.next();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
notifier.await();
|
notifier.await();
|
||||||
|
|
||||||
if (isErrRef[0]) throw (RuntimeException)valRef[0];
|
if (isErrRef[0]) throw (RuntimeException)valRef[0];
|
||||||
else return (T)valRef[0];
|
else return (T)valRef[0];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
default Observable<T> encapsulate() {
|
default Observable<T> encapsulate() {
|
||||||
return val -> on(val);
|
return val -> on(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
default <T2> Observable<T2> pipe(Pipe<T, T2> pipe) {
|
default <T2> Observable<T2> pipe(Pipe<T, T2> pipe) {
|
||||||
return sub -> on(pipe.apply(sub));
|
return sub -> on(pipe.apply(sub));
|
||||||
}
|
}
|
||||||
default WarmObservable<T> warmUp() {
|
default WarmObservable<T> warmUp() {
|
||||||
return new WarmObservable<>(this);
|
return new WarmObservable<>(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package me.topchetoeu.jscript.events;
|
package me.topchetoeu.jscript.events;
|
||||||
|
|
||||||
public interface Observer<T> {
|
public interface Observer<T> {
|
||||||
public void next(T data);
|
public void next(T data);
|
||||||
public default void error(RuntimeException err) {}
|
public default void error(RuntimeException err) {}
|
||||||
public default void finish() { }
|
public default void finish() { }
|
||||||
}
|
}
|
@ -1,59 +1,59 @@
|
|||||||
package me.topchetoeu.jscript.events;
|
package me.topchetoeu.jscript.events;
|
||||||
|
|
||||||
public interface Pipe<T, T2> {
|
public interface Pipe<T, T2> {
|
||||||
Observer<T> apply(Observer<T2> obs);
|
Observer<T> apply(Observer<T2> obs);
|
||||||
// void next(T val, Observer<T2> target);
|
// void next(T val, Observer<T2> target);
|
||||||
// default void error(RuntimeException err, Observer<T2> target) {
|
// default void error(RuntimeException err, Observer<T2> target) {
|
||||||
// target.error(err);
|
// target.error(err);
|
||||||
// }
|
// }
|
||||||
// default void finish(Observer<T2> target) {
|
// default void finish(Observer<T2> target) {
|
||||||
// target.finish();
|
// target.finish();
|
||||||
// }
|
// }
|
||||||
|
|
||||||
public static interface MapFunc<T1, T2> {
|
public static interface MapFunc<T1, T2> {
|
||||||
T2 map(T1 val);
|
T2 map(T1 val);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T1, T2> Pipe<T1, T2> map(MapFunc<T1, T2> func) {
|
public static <T1, T2> Pipe<T1, T2> map(MapFunc<T1, T2> func) {
|
||||||
return o -> val -> o.next(func.map(val));
|
return o -> val -> o.next(func.map(val));
|
||||||
}
|
}
|
||||||
public static <T> Pipe<T, T> filter(MapFunc<T, Boolean> func) {
|
public static <T> Pipe<T, T> filter(MapFunc<T, Boolean> func) {
|
||||||
return o -> val -> {
|
return o -> val -> {
|
||||||
if (func.map(val)) o.next(val);
|
if (func.map(val)) o.next(val);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
public static <T> Pipe<T, T> skip(int n) {
|
public static <T> Pipe<T, T> skip(int n) {
|
||||||
var i = new int[1];
|
var i = new int[1];
|
||||||
|
|
||||||
return target -> val -> {
|
return target -> val -> {
|
||||||
if (i[0] >= n) target.next(val);
|
if (i[0] >= n) target.next(val);
|
||||||
else i[0]++;
|
else i[0]++;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
public static <T> Pipe<T, T> limit(int n) {
|
public static <T> Pipe<T, T> limit(int n) {
|
||||||
return target -> new Observer<T>() {
|
return target -> new Observer<T>() {
|
||||||
private int i;
|
private int i;
|
||||||
|
|
||||||
public void next(T val) {
|
public void next(T val) {
|
||||||
if (i >= n) target.finish();
|
if (i >= n) target.finish();
|
||||||
else {
|
else {
|
||||||
target.next(val);
|
target.next(val);
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public void error(RuntimeException err) {
|
public void error(RuntimeException err) {
|
||||||
if (i < n) target.error(err);
|
if (i < n) target.error(err);
|
||||||
}
|
}
|
||||||
public void finish() {
|
public void finish() {
|
||||||
if (i < n) target.finish();
|
if (i < n) target.finish();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
public static <T> Pipe<T, T> first() {
|
public static <T> Pipe<T, T> first() {
|
||||||
return limit(1);
|
return limit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> Pipe<Observable<T>, T> merge() {
|
public static <T> Pipe<Observable<T>, T> merge() {
|
||||||
return target -> val -> val.on(target);
|
return target -> val -> val.on(target);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,46 +1,46 @@
|
|||||||
package me.topchetoeu.jscript.events;
|
package me.topchetoeu.jscript.events;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
|
||||||
public class WarmObservable<T> implements Observable<T>, Handle {
|
public class WarmObservable<T> implements Observable<T>, Handle {
|
||||||
private HashSet<Observer<T>> observers = new HashSet<>();
|
private HashSet<Observer<T>> observers = new HashSet<>();
|
||||||
private Handle handle;
|
private Handle handle;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Handle on(Observer<T> val) {
|
public Handle on(Observer<T> val) {
|
||||||
if (observers == null) return () -> {};
|
if (observers == null) return () -> {};
|
||||||
observers.add(val);
|
observers.add(val);
|
||||||
return () -> observers.remove(val);
|
return () -> observers.remove(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void free() {
|
public void free() {
|
||||||
if (observers == null) return;
|
if (observers == null) return;
|
||||||
handle.free();
|
handle.free();
|
||||||
handle = null;
|
handle = null;
|
||||||
observers = null;
|
observers = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public WarmObservable(Observable<T> observable) {
|
public WarmObservable(Observable<T> observable) {
|
||||||
observable.on(new Observer<>() {
|
observable.on(new Observer<>() {
|
||||||
public void next(T data) {
|
public void next(T data) {
|
||||||
for (var obs : observers) obs.next(data);
|
for (var obs : observers) obs.next(data);
|
||||||
}
|
}
|
||||||
public void error(RuntimeException err) {
|
public void error(RuntimeException err) {
|
||||||
for (var obs : observers) obs.error(err);
|
for (var obs : observers) obs.error(err);
|
||||||
handle = null;
|
handle = null;
|
||||||
observers = null;
|
observers = null;
|
||||||
}
|
}
|
||||||
public void finish() {
|
public void finish() {
|
||||||
for (var obs : observers) obs.finish();
|
for (var obs : observers) obs.finish();
|
||||||
handle = null;
|
handle = null;
|
||||||
observers = null;
|
observers = null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WarmObservable<T> warmUp() {
|
public WarmObservable<T> warmUp() {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,117 +1,117 @@
|
|||||||
package me.topchetoeu.jscript.exceptions;
|
package me.topchetoeu.jscript.exceptions;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
import me.topchetoeu.jscript.engine.Engine;
|
import me.topchetoeu.jscript.engine.Engine;
|
||||||
import me.topchetoeu.jscript.engine.Environment;
|
import me.topchetoeu.jscript.engine.Environment;
|
||||||
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
import me.topchetoeu.jscript.engine.values.ObjectValue.PlaceholderProto;
|
import me.topchetoeu.jscript.engine.values.ObjectValue.PlaceholderProto;
|
||||||
|
|
||||||
public class EngineException extends RuntimeException {
|
public class EngineException extends RuntimeException {
|
||||||
public static class StackElement {
|
public static class StackElement {
|
||||||
public final Location location;
|
public final Location location;
|
||||||
public final String function;
|
public final String function;
|
||||||
public final Context ctx;
|
public final Context ctx;
|
||||||
|
|
||||||
public boolean visible() {
|
public boolean visible() {
|
||||||
return ctx == null || ctx.environment() == null || ctx.environment().stackVisible;
|
return ctx == null || ctx.environment() == null || ctx.environment().stackVisible;
|
||||||
}
|
}
|
||||||
public String toString() {
|
public String toString() {
|
||||||
var res = "";
|
var res = "";
|
||||||
var loc = location;
|
var loc = location;
|
||||||
|
|
||||||
if (loc != null && ctx != null && ctx.engine != null) loc = ctx.engine.mapToCompiled(loc);
|
if (loc != null && ctx != null && ctx.engine != null) loc = ctx.engine.mapToCompiled(loc);
|
||||||
|
|
||||||
if (loc != null) res += "at " + loc.toString() + " ";
|
if (loc != null) res += "at " + loc.toString() + " ";
|
||||||
if (function != null && !function.equals("")) res += "in " + function + " ";
|
if (function != null && !function.equals("")) res += "in " + function + " ";
|
||||||
|
|
||||||
return res.trim();
|
return res.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
public StackElement(Context ctx, Location location, String function) {
|
public StackElement(Context ctx, Location location, String function) {
|
||||||
if (function != null) function = function.trim();
|
if (function != null) function = function.trim();
|
||||||
if (function.equals("")) function = null;
|
if (function.equals("")) function = null;
|
||||||
|
|
||||||
if (ctx == null) this.ctx = null;
|
if (ctx == null) this.ctx = null;
|
||||||
else this.ctx = new Context(ctx.engine).pushEnv(ctx.environment());
|
else this.ctx = new Context(ctx.engine).pushEnv(ctx.environment());
|
||||||
this.location = location;
|
this.location = location;
|
||||||
this.function = function;
|
this.function = function;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final Object value;
|
public final Object value;
|
||||||
public EngineException cause;
|
public EngineException cause;
|
||||||
public Environment env = null;
|
public Environment env = null;
|
||||||
public Engine engine = null;
|
public Engine engine = null;
|
||||||
public final List<StackElement> stackTrace = new ArrayList<>();
|
public final List<StackElement> stackTrace = new ArrayList<>();
|
||||||
|
|
||||||
public EngineException add(Context ctx, String name, Location location) {
|
public EngineException add(Context ctx, String name, Location location) {
|
||||||
var el = new StackElement(ctx, location, name);
|
var el = new StackElement(ctx, location, name);
|
||||||
if (el.function == null && el.location == null) return this;
|
if (el.function == null && el.location == null) return this;
|
||||||
setCtx(ctx.environment(), ctx.engine);
|
setCtx(ctx.environment(), ctx.engine);
|
||||||
stackTrace.add(el);
|
stackTrace.add(el);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
public EngineException setCause(EngineException cause) {
|
public EngineException setCause(EngineException cause) {
|
||||||
this.cause = cause;
|
this.cause = cause;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
public EngineException setCtx(Environment env, Engine engine) {
|
public EngineException setCtx(Environment env, Engine engine) {
|
||||||
if (this.env == null) this.env = env;
|
if (this.env == null) this.env = env;
|
||||||
if (this.engine == null) this.engine = engine;
|
if (this.engine == null) this.engine = engine;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString(Context ctx) {
|
public String toString(Context ctx) {
|
||||||
var ss = new StringBuilder();
|
var ss = new StringBuilder();
|
||||||
try {
|
try {
|
||||||
ss.append(Values.toString(ctx, value)).append('\n');
|
ss.append(Values.toString(ctx, value)).append('\n');
|
||||||
}
|
}
|
||||||
catch (EngineException e) {
|
catch (EngineException e) {
|
||||||
ss.append("[Error while stringifying]\n");
|
ss.append("[Error while stringifying]\n");
|
||||||
}
|
}
|
||||||
for (var line : stackTrace) {
|
for (var line : stackTrace) {
|
||||||
if (line.visible()) ss.append(" ").append(line.toString()).append("\n");
|
if (line.visible()) ss.append(" ").append(line.toString()).append("\n");
|
||||||
}
|
}
|
||||||
if (cause != null) ss.append("Caused by ").append(cause.toString(ctx)).append('\n');
|
if (cause != null) ss.append("Caused by ").append(cause.toString(ctx)).append('\n');
|
||||||
ss.deleteCharAt(ss.length() - 1);
|
ss.deleteCharAt(ss.length() - 1);
|
||||||
return ss.toString();
|
return ss.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Object err(String name, String msg, PlaceholderProto proto) {
|
private static Object err(String name, String msg, PlaceholderProto proto) {
|
||||||
var res = new ObjectValue(proto);
|
var res = new ObjectValue(proto);
|
||||||
if (name != null) res.defineProperty(null, "name", name);
|
if (name != null) res.defineProperty(null, "name", name);
|
||||||
res.defineProperty(null, "message", msg);
|
res.defineProperty(null, "message", msg);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public EngineException(Object error) {
|
public EngineException(Object error) {
|
||||||
super(error == null ? "null" : error.toString());
|
super(error == null ? "null" : error.toString());
|
||||||
|
|
||||||
this.value = error;
|
this.value = error;
|
||||||
this.cause = null;
|
this.cause = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static EngineException ofError(String name, String msg) {
|
public static EngineException ofError(String name, String msg) {
|
||||||
return new EngineException(err(name, msg, PlaceholderProto.ERROR));
|
return new EngineException(err(name, msg, PlaceholderProto.ERROR));
|
||||||
}
|
}
|
||||||
public static EngineException ofError(String msg) {
|
public static EngineException ofError(String msg) {
|
||||||
return new EngineException(err(null, msg, PlaceholderProto.ERROR));
|
return new EngineException(err(null, msg, PlaceholderProto.ERROR));
|
||||||
}
|
}
|
||||||
public static EngineException ofSyntax(SyntaxException e) {
|
public static EngineException ofSyntax(SyntaxException e) {
|
||||||
return new EngineException(err(null, e.msg, PlaceholderProto.SYNTAX_ERROR)).add(null, null, e.loc);
|
return new EngineException(err(null, e.msg, PlaceholderProto.SYNTAX_ERROR)).add(null, null, e.loc);
|
||||||
}
|
}
|
||||||
public static EngineException ofSyntax(String msg) {
|
public static EngineException ofSyntax(String msg) {
|
||||||
return new EngineException(err(null, msg, PlaceholderProto.SYNTAX_ERROR));
|
return new EngineException(err(null, msg, PlaceholderProto.SYNTAX_ERROR));
|
||||||
}
|
}
|
||||||
public static EngineException ofType(String msg) {
|
public static EngineException ofType(String msg) {
|
||||||
return new EngineException(err(null, msg, PlaceholderProto.TYPE_ERROR));
|
return new EngineException(err(null, msg, PlaceholderProto.TYPE_ERROR));
|
||||||
}
|
}
|
||||||
public static EngineException ofRange(String msg) {
|
public static EngineException ofRange(String msg) {
|
||||||
return new EngineException(err(null, msg, PlaceholderProto.RANGE_ERROR));
|
return new EngineException(err(null, msg, PlaceholderProto.RANGE_ERROR));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
package me.topchetoeu.jscript.exceptions;
|
package me.topchetoeu.jscript.exceptions;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
|
|
||||||
public class SyntaxException extends RuntimeException {
|
public class SyntaxException extends RuntimeException {
|
||||||
public final Location loc;
|
public final Location loc;
|
||||||
public final String msg;
|
public final String msg;
|
||||||
|
|
||||||
public SyntaxException(Location loc, String msg) {
|
public SyntaxException(Location loc, String msg) {
|
||||||
super(String.format("Syntax error (at %s): %s", loc, msg));
|
super(String.format("Syntax error (at %s): %s", loc, msg));
|
||||||
this.loc = loc;
|
this.loc = loc;
|
||||||
this.msg = msg;
|
this.msg = msg;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,67 +1,67 @@
|
|||||||
package me.topchetoeu.jscript.filesystem;
|
package me.topchetoeu.jscript.filesystem;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Buffer;
|
import me.topchetoeu.jscript.Buffer;
|
||||||
import me.topchetoeu.jscript.filesystem.FilesystemException.FSCode;
|
import me.topchetoeu.jscript.filesystem.FilesystemException.FSCode;
|
||||||
|
|
||||||
public class MemoryFile implements File {
|
public class MemoryFile implements File {
|
||||||
private int ptr;
|
private int ptr;
|
||||||
private Mode mode;
|
private Mode mode;
|
||||||
private Buffer data;
|
private Buffer data;
|
||||||
private String filename;
|
private String filename;
|
||||||
|
|
||||||
public Buffer data() { return data; }
|
public Buffer data() { return data; }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int read(byte[] buff) {
|
public int read(byte[] buff) {
|
||||||
if (data == null || !mode.readable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R);
|
if (data == null || !mode.readable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R);
|
||||||
var res = data.read(ptr, buff);
|
var res = data.read(ptr, buff);
|
||||||
ptr += res;
|
ptr += res;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void write(byte[] buff) {
|
public void write(byte[] buff) {
|
||||||
if (data == null || !mode.writable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_RW);
|
if (data == null || !mode.writable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_RW);
|
||||||
|
|
||||||
data.write(ptr, buff);
|
data.write(ptr, buff);
|
||||||
ptr += buff.length;
|
ptr += buff.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getPtr() {
|
public long getPtr() {
|
||||||
if (data == null || !mode.readable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R);
|
if (data == null || !mode.readable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R);
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void setPtr(long offset, int pos) {
|
public void setPtr(long offset, int pos) {
|
||||||
if (data == null || !mode.readable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R);
|
if (data == null || !mode.readable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R);
|
||||||
|
|
||||||
if (pos == 0) ptr = (int)offset;
|
if (pos == 0) ptr = (int)offset;
|
||||||
else if (pos == 1) ptr += (int)offset;
|
else if (pos == 1) ptr += (int)offset;
|
||||||
else if (pos == 2) ptr = data.length() - (int)offset;
|
else if (pos == 2) ptr = data.length() - (int)offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
mode = Mode.NONE;
|
mode = Mode.NONE;
|
||||||
ptr = 0;
|
ptr = 0;
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public Mode mode() {
|
public Mode mode() {
|
||||||
if (data == null) return Mode.NONE;
|
if (data == null) return Mode.NONE;
|
||||||
return mode;
|
return mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MemoryFile(String filename, Buffer buff, Mode mode) {
|
public MemoryFile(String filename, Buffer buff, Mode mode) {
|
||||||
this.filename = filename;
|
this.filename = filename;
|
||||||
this.data = buff;
|
this.data = buff;
|
||||||
this.mode = mode;
|
this.mode = mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MemoryFile fromFileList(String filename, java.io.File[] list) {
|
public static MemoryFile fromFileList(String filename, java.io.File[] list) {
|
||||||
var res = new StringBuilder();
|
var res = new StringBuilder();
|
||||||
|
|
||||||
for (var el : list) res.append(el.getName()).append('\n');
|
for (var el : list) res.append(el.getName()).append('\n');
|
||||||
|
|
||||||
return new MemoryFile(filename, new Buffer(res.toString().getBytes()), Mode.READ);
|
return new MemoryFile(filename, new Buffer(res.toString().getBytes()), Mode.READ);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,90 +1,90 @@
|
|||||||
package me.topchetoeu.jscript.filesystem;
|
package me.topchetoeu.jscript.filesystem;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Buffer;
|
import me.topchetoeu.jscript.Buffer;
|
||||||
import me.topchetoeu.jscript.filesystem.FilesystemException.FSCode;
|
import me.topchetoeu.jscript.filesystem.FilesystemException.FSCode;
|
||||||
|
|
||||||
public class MemoryFilesystem implements Filesystem {
|
public class MemoryFilesystem implements Filesystem {
|
||||||
public final Mode mode;
|
public final Mode mode;
|
||||||
private HashMap<Path, Buffer> files = new HashMap<>();
|
private HashMap<Path, Buffer> files = new HashMap<>();
|
||||||
private HashSet<Path> folders = new HashSet<>();
|
private HashSet<Path> folders = new HashSet<>();
|
||||||
|
|
||||||
private Path getPath(String name) {
|
private Path getPath(String name) {
|
||||||
return Path.of("/" + name.replace("\\", "/")).normalize();
|
return Path.of("/" + name.replace("\\", "/")).normalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void create(String path, EntryType type) {
|
public void create(String path, EntryType type) {
|
||||||
var _path = getPath(path);
|
var _path = getPath(path);
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case FILE:
|
case FILE:
|
||||||
if (!folders.contains(_path.getParent())) throw new FilesystemException(path, FSCode.DOESNT_EXIST);
|
if (!folders.contains(_path.getParent())) throw new FilesystemException(path, FSCode.DOESNT_EXIST);
|
||||||
if (folders.contains(_path) || files.containsKey(_path)) throw new FilesystemException(path, FSCode.ALREADY_EXISTS);
|
if (folders.contains(_path) || files.containsKey(_path)) throw new FilesystemException(path, FSCode.ALREADY_EXISTS);
|
||||||
if (folders.contains(_path)) throw new FilesystemException(path, FSCode.ALREADY_EXISTS);
|
if (folders.contains(_path)) throw new FilesystemException(path, FSCode.ALREADY_EXISTS);
|
||||||
files.put(_path, new Buffer());
|
files.put(_path, new Buffer());
|
||||||
break;
|
break;
|
||||||
case FOLDER:
|
case FOLDER:
|
||||||
if (!folders.contains(_path.getParent())) throw new FilesystemException(path, FSCode.DOESNT_EXIST);
|
if (!folders.contains(_path.getParent())) throw new FilesystemException(path, FSCode.DOESNT_EXIST);
|
||||||
if (folders.contains(_path) || files.containsKey(_path)) throw new FilesystemException(path, FSCode.ALREADY_EXISTS);
|
if (folders.contains(_path) || files.containsKey(_path)) throw new FilesystemException(path, FSCode.ALREADY_EXISTS);
|
||||||
folders.add(_path);
|
folders.add(_path);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
case NONE:
|
case NONE:
|
||||||
if (!folders.remove(_path) && files.remove(_path) == null) throw new FilesystemException(path, FSCode.DOESNT_EXIST);
|
if (!folders.remove(_path) && files.remove(_path) == null) throw new FilesystemException(path, FSCode.DOESNT_EXIST);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public File open(String path, Mode perms) {
|
public File open(String path, Mode perms) {
|
||||||
var _path = getPath(path);
|
var _path = getPath(path);
|
||||||
var pcount = _path.getNameCount();
|
var pcount = _path.getNameCount();
|
||||||
|
|
||||||
if (files.containsKey(_path)) return new MemoryFile(path, files.get(_path), perms);
|
if (files.containsKey(_path)) return new MemoryFile(path, files.get(_path), perms);
|
||||||
else if (folders.contains(_path)) {
|
else if (folders.contains(_path)) {
|
||||||
var res = new StringBuilder();
|
var res = new StringBuilder();
|
||||||
for (var folder : folders) {
|
for (var folder : folders) {
|
||||||
if (pcount + 1 != folder.getNameCount()) continue;
|
if (pcount + 1 != folder.getNameCount()) continue;
|
||||||
if (!folder.startsWith(_path)) continue;
|
if (!folder.startsWith(_path)) continue;
|
||||||
res.append(folder.toFile().getName()).append('\n');
|
res.append(folder.toFile().getName()).append('\n');
|
||||||
}
|
}
|
||||||
for (var file : files.keySet()) {
|
for (var file : files.keySet()) {
|
||||||
if (pcount + 1 != file.getNameCount()) continue;
|
if (pcount + 1 != file.getNameCount()) continue;
|
||||||
if (!file.startsWith(_path)) continue;
|
if (!file.startsWith(_path)) continue;
|
||||||
res.append(file.toFile().getName()).append('\n');
|
res.append(file.toFile().getName()).append('\n');
|
||||||
}
|
}
|
||||||
return new MemoryFile(path, new Buffer(res.toString().getBytes()), perms.intersect(Mode.READ));
|
return new MemoryFile(path, new Buffer(res.toString().getBytes()), perms.intersect(Mode.READ));
|
||||||
}
|
}
|
||||||
else throw new FilesystemException(path, FSCode.DOESNT_EXIST);
|
else throw new FilesystemException(path, FSCode.DOESNT_EXIST);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FileStat stat(String path) {
|
public FileStat stat(String path) {
|
||||||
var _path = getPath(path);
|
var _path = getPath(path);
|
||||||
|
|
||||||
if (files.containsKey(_path)) return new FileStat(mode, EntryType.FILE);
|
if (files.containsKey(_path)) return new FileStat(mode, EntryType.FILE);
|
||||||
else if (folders.contains(_path)) return new FileStat(mode, EntryType.FOLDER);
|
else if (folders.contains(_path)) return new FileStat(mode, EntryType.FOLDER);
|
||||||
else throw new FilesystemException(path, FSCode.DOESNT_EXIST);
|
else throw new FilesystemException(path, FSCode.DOESNT_EXIST);
|
||||||
}
|
}
|
||||||
|
|
||||||
public MemoryFilesystem put(String path, byte[] data) {
|
public MemoryFilesystem put(String path, byte[] data) {
|
||||||
var _path = getPath(path);
|
var _path = getPath(path);
|
||||||
var _curr = "/";
|
var _curr = "/";
|
||||||
|
|
||||||
for (var seg : _path) {
|
for (var seg : _path) {
|
||||||
create(_curr, EntryType.FOLDER);
|
create(_curr, EntryType.FOLDER);
|
||||||
_curr += seg + "/";
|
_curr += seg + "/";
|
||||||
}
|
}
|
||||||
|
|
||||||
files.put(_path, new Buffer(data));
|
files.put(_path, new Buffer(data));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MemoryFilesystem(Mode mode) {
|
public MemoryFilesystem(Mode mode) {
|
||||||
this.mode = mode;
|
this.mode = mode;
|
||||||
folders.add(Path.of("/"));
|
folders.add(Path.of("/"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
package me.topchetoeu.jscript.interop;
|
package me.topchetoeu.jscript.interop;
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
import java.lang.annotation.ElementType;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.TYPE })
|
@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.TYPE })
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
public @interface Native {
|
public @interface Native {
|
||||||
public String value() default "";
|
public String value() default "";
|
||||||
public boolean thisArg() default false;
|
public boolean thisArg() default false;
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
package me.topchetoeu.jscript.interop;
|
package me.topchetoeu.jscript.interop;
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
import java.lang.annotation.ElementType;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
@Target({ ElementType.METHOD })
|
@Target({ ElementType.METHOD })
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
public @interface NativeGetter {
|
public @interface NativeGetter {
|
||||||
public String value() default "";
|
public String value() default "";
|
||||||
public boolean thisArg() default false;
|
public boolean thisArg() default false;
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
package me.topchetoeu.jscript.interop;
|
package me.topchetoeu.jscript.interop;
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
import java.lang.annotation.ElementType;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
@Target({ ElementType.METHOD })
|
@Target({ ElementType.METHOD })
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
public @interface NativeSetter {
|
public @interface NativeSetter {
|
||||||
public String value() default "";
|
public String value() default "";
|
||||||
public boolean thisArg() default false;
|
public boolean thisArg() default false;
|
||||||
}
|
}
|
||||||
|
@ -1,127 +1,127 @@
|
|||||||
package me.topchetoeu.jscript.interop;
|
package me.topchetoeu.jscript.interop;
|
||||||
|
|
||||||
import java.lang.reflect.Array;
|
import java.lang.reflect.Array;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
||||||
import me.topchetoeu.jscript.engine.values.NativeWrapper;
|
import me.topchetoeu.jscript.engine.values.NativeWrapper;
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
import me.topchetoeu.jscript.exceptions.ConvertException;
|
import me.topchetoeu.jscript.exceptions.ConvertException;
|
||||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
import me.topchetoeu.jscript.exceptions.InterruptException;
|
import me.topchetoeu.jscript.exceptions.InterruptException;
|
||||||
|
|
||||||
public class OverloadFunction extends FunctionValue {
|
public class OverloadFunction extends FunctionValue {
|
||||||
public final List<Overload> overloads = new ArrayList<>();
|
public final List<Overload> overloads = new ArrayList<>();
|
||||||
|
|
||||||
public Object call(Context ctx, Object thisArg, Object ...args) {
|
public Object call(Context ctx, Object thisArg, Object ...args) {
|
||||||
loop: for (var overload : overloads) {
|
loop: for (var overload : overloads) {
|
||||||
Object[] newArgs = new Object[overload.params.length];
|
Object[] newArgs = new Object[overload.params.length];
|
||||||
|
|
||||||
boolean consumesEngine = overload.params.length > 0 && overload.params[0] == Context.class;
|
boolean consumesEngine = overload.params.length > 0 && overload.params[0] == Context.class;
|
||||||
int start = (consumesEngine ? 1 : 0) + (overload.passThis ? 1 : 0);
|
int start = (consumesEngine ? 1 : 0) + (overload.passThis ? 1 : 0);
|
||||||
int end = overload.params.length - (overload.variadic ? 1 : 0);
|
int end = overload.params.length - (overload.variadic ? 1 : 0);
|
||||||
|
|
||||||
for (var i = start; i < end; i++) {
|
for (var i = start; i < end; i++) {
|
||||||
Object val;
|
Object val;
|
||||||
|
|
||||||
if (i - start >= args.length) val = null;
|
if (i - start >= args.length) val = null;
|
||||||
else val = args[i - start];
|
else val = args[i - start];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
newArgs[i] = Values.convert(ctx, val, overload.params[i]);
|
newArgs[i] = Values.convert(ctx, val, overload.params[i]);
|
||||||
}
|
}
|
||||||
catch (ConvertException e) {
|
catch (ConvertException e) {
|
||||||
if (overloads.size() > 1) continue loop;
|
if (overloads.size() > 1) continue loop;
|
||||||
else throw EngineException.ofType(String.format("Argument %d can't be converted from %s to %s", i - start, e.source, e.target));
|
else throw EngineException.ofType(String.format("Argument %d can't be converted from %s to %s", i - start, e.source, e.target));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (overload.variadic) {
|
if (overload.variadic) {
|
||||||
var type = overload.params[overload.params.length - 1].getComponentType();
|
var type = overload.params[overload.params.length - 1].getComponentType();
|
||||||
var n = Math.max(args.length - end + start, 0);
|
var n = Math.max(args.length - end + start, 0);
|
||||||
Object varArg = Array.newInstance(type, n);
|
Object varArg = Array.newInstance(type, n);
|
||||||
|
|
||||||
for (var i = 0; i < n; i++) {
|
for (var i = 0; i < n; i++) {
|
||||||
try {
|
try {
|
||||||
Array.set(varArg, i, Values.convert(ctx, args[i + end - start], type));
|
Array.set(varArg, i, Values.convert(ctx, args[i + end - start], type));
|
||||||
}
|
}
|
||||||
catch (ConvertException e) {
|
catch (ConvertException e) {
|
||||||
if (overloads.size() > 1) continue loop;
|
if (overloads.size() > 1) continue loop;
|
||||||
else throw EngineException.ofType(String.format("Element in variadic argument can't be converted from %s to %s", e.source, e.target));
|
else throw EngineException.ofType(String.format("Element in variadic argument can't be converted from %s to %s", e.source, e.target));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
newArgs[newArgs.length - 1] = varArg;
|
newArgs[newArgs.length - 1] = varArg;
|
||||||
}
|
}
|
||||||
|
|
||||||
var thisArgType = overload.passThis ? overload.params[consumesEngine ? 1 : 0] : overload.thisArg;
|
var thisArgType = overload.passThis ? overload.params[consumesEngine ? 1 : 0] : overload.thisArg;
|
||||||
Object _this;
|
Object _this;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
_this = thisArgType == null ? null : Values.convert(ctx, thisArg, thisArgType);
|
_this = thisArgType == null ? null : Values.convert(ctx, thisArg, thisArgType);
|
||||||
}
|
}
|
||||||
catch (ConvertException e) {
|
catch (ConvertException e) {
|
||||||
if (overloads.size() > 1) continue loop;
|
if (overloads.size() > 1) continue loop;
|
||||||
else throw EngineException.ofType(String.format("This argument can't be converted from %s to %s", e.source, e.target));
|
else throw EngineException.ofType(String.format("This argument can't be converted from %s to %s", e.source, e.target));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (consumesEngine) newArgs[0] = ctx;
|
if (consumesEngine) newArgs[0] = ctx;
|
||||||
if (overload.passThis) {
|
if (overload.passThis) {
|
||||||
newArgs[consumesEngine ? 1 : 0] = _this;
|
newArgs[consumesEngine ? 1 : 0] = _this;
|
||||||
_this = null;
|
_this = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return Values.normalize(ctx, overload.runner.run(ctx, _this, newArgs));
|
return Values.normalize(ctx, overload.runner.run(ctx, _this, newArgs));
|
||||||
}
|
}
|
||||||
catch (InstantiationException e) { throw EngineException.ofError("The class may not be instantiated."); }
|
catch (InstantiationException e) { throw EngineException.ofError("The class may not be instantiated."); }
|
||||||
catch (IllegalAccessException | IllegalArgumentException e) { continue; }
|
catch (IllegalAccessException | IllegalArgumentException e) { continue; }
|
||||||
catch (InvocationTargetException e) {
|
catch (InvocationTargetException e) {
|
||||||
var loc = Location.INTERNAL;
|
var loc = Location.INTERNAL;
|
||||||
if (e.getTargetException() instanceof EngineException) {
|
if (e.getTargetException() instanceof EngineException) {
|
||||||
throw ((EngineException)e.getTargetException()).add(ctx, name, loc);
|
throw ((EngineException)e.getTargetException()).add(ctx, name, loc);
|
||||||
}
|
}
|
||||||
else if (e.getTargetException() instanceof NullPointerException) {
|
else if (e.getTargetException() instanceof NullPointerException) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
throw EngineException.ofType("Unexpected value of 'undefined'.").add(ctx, name, loc);
|
throw EngineException.ofType("Unexpected value of 'undefined'.").add(ctx, name, loc);
|
||||||
}
|
}
|
||||||
else if (e.getTargetException() instanceof InterruptException || e.getTargetException() instanceof InterruptedException) {
|
else if (e.getTargetException() instanceof InterruptException || e.getTargetException() instanceof InterruptedException) {
|
||||||
throw new InterruptException();
|
throw new InterruptException();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var target = e.getTargetException();
|
var target = e.getTargetException();
|
||||||
var targetClass = target.getClass();
|
var targetClass = target.getClass();
|
||||||
var err = new NativeWrapper(e.getTargetException());
|
var err = new NativeWrapper(e.getTargetException());
|
||||||
|
|
||||||
err.defineProperty(ctx, "message", target.getMessage());
|
err.defineProperty(ctx, "message", target.getMessage());
|
||||||
err.defineProperty(ctx, "name", NativeWrapperProvider.getName(targetClass));
|
err.defineProperty(ctx, "name", NativeWrapperProvider.getName(targetClass));
|
||||||
|
|
||||||
throw new EngineException(err).add(ctx, name, loc);
|
throw new EngineException(err).add(ctx, name, loc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (ReflectiveOperationException e) {
|
catch (ReflectiveOperationException e) {
|
||||||
throw EngineException.ofError(e.getMessage()).add(ctx, name, Location.INTERNAL);
|
throw EngineException.ofError(e.getMessage()).add(ctx, name, Location.INTERNAL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw EngineException.ofType("No overload found for native method.");
|
throw EngineException.ofType("No overload found for native method.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public OverloadFunction add(Overload overload) {
|
public OverloadFunction add(Overload overload) {
|
||||||
this.overloads.add(overload);
|
this.overloads.add(overload);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public OverloadFunction(String name) {
|
public OverloadFunction(String name) {
|
||||||
super(name, 0);
|
super(name, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static OverloadFunction of(String name, Overload overload) {
|
public static OverloadFunction of(String name, Overload overload) {
|
||||||
if (overload == null) return null;
|
if (overload == null) return null;
|
||||||
else return new OverloadFunction(name).add(overload);
|
else return new OverloadFunction(name).add(overload);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,243 +1,243 @@
|
|||||||
package me.topchetoeu.jscript.json;
|
package me.topchetoeu.jscript.json;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Filename;
|
import me.topchetoeu.jscript.Filename;
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
||||||
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
import me.topchetoeu.jscript.exceptions.SyntaxException;
|
import me.topchetoeu.jscript.exceptions.SyntaxException;
|
||||||
import me.topchetoeu.jscript.parsing.Operator;
|
import me.topchetoeu.jscript.parsing.Operator;
|
||||||
import me.topchetoeu.jscript.parsing.ParseRes;
|
import me.topchetoeu.jscript.parsing.ParseRes;
|
||||||
import me.topchetoeu.jscript.parsing.Parsing;
|
import me.topchetoeu.jscript.parsing.Parsing;
|
||||||
import me.topchetoeu.jscript.parsing.Token;
|
import me.topchetoeu.jscript.parsing.Token;
|
||||||
|
|
||||||
public class JSON {
|
public class JSON {
|
||||||
public static Object toJs(JSONElement val) {
|
public static Object toJs(JSONElement val) {
|
||||||
if (val.isBoolean()) return val.bool();
|
if (val.isBoolean()) return val.bool();
|
||||||
if (val.isString()) return val.string();
|
if (val.isString()) return val.string();
|
||||||
if (val.isNumber()) return val.number();
|
if (val.isNumber()) return val.number();
|
||||||
if (val.isList()) return ArrayValue.of(null, val.list().stream().map(JSON::toJs).collect(Collectors.toList()));
|
if (val.isList()) return ArrayValue.of(null, val.list().stream().map(JSON::toJs).collect(Collectors.toList()));
|
||||||
if (val.isMap()) {
|
if (val.isMap()) {
|
||||||
var res = new ObjectValue();
|
var res = new ObjectValue();
|
||||||
for (var el : val.map().entrySet()) {
|
for (var el : val.map().entrySet()) {
|
||||||
res.defineProperty(null, el.getKey(), toJs(el.getValue()));
|
res.defineProperty(null, el.getKey(), toJs(el.getValue()));
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
if (val.isNull()) return Values.NULL;
|
if (val.isNull()) return Values.NULL;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
private static JSONElement fromJs(Context ctx, Object val, HashSet<Object> prev) {
|
private static JSONElement fromJs(Context ctx, Object val, HashSet<Object> prev) {
|
||||||
if (val instanceof Boolean) return JSONElement.bool((boolean)val);
|
if (val instanceof Boolean) return JSONElement.bool((boolean)val);
|
||||||
if (val instanceof Number) return JSONElement.number(((Number)val).doubleValue());
|
if (val instanceof Number) return JSONElement.number(((Number)val).doubleValue());
|
||||||
if (val instanceof String) return JSONElement.string((String)val);
|
if (val instanceof String) return JSONElement.string((String)val);
|
||||||
if (val == Values.NULL) return JSONElement.NULL;
|
if (val == Values.NULL) return JSONElement.NULL;
|
||||||
if (val instanceof ArrayValue) {
|
if (val instanceof ArrayValue) {
|
||||||
if (prev.contains(val)) throw new EngineException("Circular dependency in JSON.");
|
if (prev.contains(val)) throw new EngineException("Circular dependency in JSON.");
|
||||||
prev.add(val);
|
prev.add(val);
|
||||||
|
|
||||||
var res = new JSONList();
|
var res = new JSONList();
|
||||||
|
|
||||||
for (var el : ((ArrayValue)val).toArray()) {
|
for (var el : ((ArrayValue)val).toArray()) {
|
||||||
var jsonEl = fromJs(ctx, el, prev);
|
var jsonEl = fromJs(ctx, el, prev);
|
||||||
if (jsonEl == null) jsonEl = JSONElement.NULL;
|
if (jsonEl == null) jsonEl = JSONElement.NULL;
|
||||||
res.add(jsonEl);
|
res.add(jsonEl);
|
||||||
}
|
}
|
||||||
|
|
||||||
prev.remove(val);
|
prev.remove(val);
|
||||||
return JSONElement.of(res);
|
return JSONElement.of(res);
|
||||||
}
|
}
|
||||||
if (val instanceof ObjectValue) {
|
if (val instanceof ObjectValue) {
|
||||||
if (prev.contains(val)) throw new EngineException("Circular dependency in JSON.");
|
if (prev.contains(val)) throw new EngineException("Circular dependency in JSON.");
|
||||||
prev.add(val);
|
prev.add(val);
|
||||||
|
|
||||||
var res = new JSONMap();
|
var res = new JSONMap();
|
||||||
|
|
||||||
for (var el : ((ObjectValue)val).keys(false)) {
|
for (var el : ((ObjectValue)val).keys(false)) {
|
||||||
var jsonEl = fromJs(ctx, ((ObjectValue)val).getMember(ctx, el), prev);
|
var jsonEl = fromJs(ctx, ((ObjectValue)val).getMember(ctx, el), prev);
|
||||||
if (jsonEl == null) continue;
|
if (jsonEl == null) continue;
|
||||||
if (el instanceof String || el instanceof Number) res.put(el.toString(), jsonEl);
|
if (el instanceof String || el instanceof Number) res.put(el.toString(), jsonEl);
|
||||||
}
|
}
|
||||||
|
|
||||||
prev.remove(val);
|
prev.remove(val);
|
||||||
return JSONElement.of(res);
|
return JSONElement.of(res);
|
||||||
}
|
}
|
||||||
if (val == null) return null;
|
if (val == null) return null;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
public static JSONElement fromJs(Context ctx, Object val) {
|
public static JSONElement fromJs(Context ctx, Object val) {
|
||||||
return fromJs(ctx, val, new HashSet<>());
|
return fromJs(ctx, val, new HashSet<>());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ParseRes<String> parseIdentifier(List<Token> tokens, int i) {
|
public static ParseRes<String> parseIdentifier(List<Token> tokens, int i) {
|
||||||
return Parsing.parseIdentifier(tokens, i);
|
return Parsing.parseIdentifier(tokens, i);
|
||||||
}
|
}
|
||||||
public static ParseRes<String> parseString(Filename filename, List<Token> tokens, int i) {
|
public static ParseRes<String> parseString(Filename filename, List<Token> tokens, int i) {
|
||||||
var res = Parsing.parseString(filename, tokens, i);
|
var res = Parsing.parseString(filename, tokens, i);
|
||||||
if (res.isSuccess()) return ParseRes.res((String)res.result.value, res.n);
|
if (res.isSuccess()) return ParseRes.res((String)res.result.value, res.n);
|
||||||
else return res.transform();
|
else return res.transform();
|
||||||
}
|
}
|
||||||
public static ParseRes<Double> parseNumber(Filename filename, List<Token> tokens, int i) {
|
public static ParseRes<Double> parseNumber(Filename filename, List<Token> tokens, int i) {
|
||||||
var minus = Parsing.isOperator(tokens, i, Operator.SUBTRACT);
|
var minus = Parsing.isOperator(tokens, i, Operator.SUBTRACT);
|
||||||
if (minus) i++;
|
if (minus) i++;
|
||||||
|
|
||||||
var res = Parsing.parseNumber(filename, tokens, i);
|
var res = Parsing.parseNumber(filename, tokens, i);
|
||||||
if (res.isSuccess()) return ParseRes.res((minus ? -1 : 1) * (Double)res.result.value, res.n + (minus ? 1 : 0));
|
if (res.isSuccess()) return ParseRes.res((minus ? -1 : 1) * (Double)res.result.value, res.n + (minus ? 1 : 0));
|
||||||
else return res.transform();
|
else return res.transform();
|
||||||
}
|
}
|
||||||
public static ParseRes<Boolean> parseBool(Filename filename, List<Token> tokens, int i) {
|
public static ParseRes<Boolean> parseBool(Filename filename, List<Token> tokens, int i) {
|
||||||
var id = parseIdentifier(tokens, i);
|
var id = parseIdentifier(tokens, i);
|
||||||
|
|
||||||
if (!id.isSuccess()) return ParseRes.failed();
|
if (!id.isSuccess()) return ParseRes.failed();
|
||||||
else if (id.result.equals("true")) return ParseRes.res(true, 1);
|
else if (id.result.equals("true")) return ParseRes.res(true, 1);
|
||||||
else if (id.result.equals("false")) return ParseRes.res(false, 1);
|
else if (id.result.equals("false")) return ParseRes.res(false, 1);
|
||||||
else return ParseRes.failed();
|
else return ParseRes.failed();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ParseRes<?> parseValue(Filename filename, List<Token> tokens, int i) {
|
public static ParseRes<?> parseValue(Filename filename, List<Token> tokens, int i) {
|
||||||
return ParseRes.any(
|
return ParseRes.any(
|
||||||
parseString(filename, tokens, i),
|
parseString(filename, tokens, i),
|
||||||
parseNumber(filename, tokens, i),
|
parseNumber(filename, tokens, i),
|
||||||
parseBool(filename, tokens, i),
|
parseBool(filename, tokens, i),
|
||||||
parseMap(filename, tokens, i),
|
parseMap(filename, tokens, i),
|
||||||
parseList(filename, tokens, i)
|
parseList(filename, tokens, i)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ParseRes<JSONMap> parseMap(Filename filename, List<Token> tokens, int i) {
|
public static ParseRes<JSONMap> parseMap(Filename filename, List<Token> tokens, int i) {
|
||||||
int n = 0;
|
int n = 0;
|
||||||
if (!Parsing.isOperator(tokens, i + n++, Operator.BRACE_OPEN)) return ParseRes.failed();
|
if (!Parsing.isOperator(tokens, i + n++, Operator.BRACE_OPEN)) return ParseRes.failed();
|
||||||
|
|
||||||
var values = new JSONMap();
|
var values = new JSONMap();
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (Parsing.isOperator(tokens, i + n, Operator.BRACE_CLOSE)) {
|
if (Parsing.isOperator(tokens, i + n, Operator.BRACE_CLOSE)) {
|
||||||
n++;
|
n++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
var name = ParseRes.any(
|
var name = ParseRes.any(
|
||||||
parseIdentifier(tokens, i + n),
|
parseIdentifier(tokens, i + n),
|
||||||
parseString(filename, tokens, i + n),
|
parseString(filename, tokens, i + n),
|
||||||
parseNumber(filename, tokens, i + n)
|
parseNumber(filename, tokens, i + n)
|
||||||
);
|
);
|
||||||
if (!name.isSuccess()) return ParseRes.error(Parsing.getLoc(filename, tokens, i + n), "Expected an index.", name);
|
if (!name.isSuccess()) return ParseRes.error(Parsing.getLoc(filename, tokens, i + n), "Expected an index.", name);
|
||||||
else n += name.n;
|
else n += name.n;
|
||||||
|
|
||||||
if (!Parsing.isOperator(tokens, i + n, Operator.COLON)) {
|
if (!Parsing.isOperator(tokens, i + n, Operator.COLON)) {
|
||||||
return ParseRes.error(Parsing.getLoc(filename, tokens, i + n), "Expected a colon.", name);
|
return ParseRes.error(Parsing.getLoc(filename, tokens, i + n), "Expected a colon.", name);
|
||||||
}
|
}
|
||||||
n++;
|
n++;
|
||||||
|
|
||||||
var res = parseValue(filename, tokens, i + n);
|
var res = parseValue(filename, tokens, i + n);
|
||||||
if (!res.isSuccess()) return ParseRes.error(Parsing.getLoc(filename, tokens, i + n), "Expected a list element.", res);
|
if (!res.isSuccess()) return ParseRes.error(Parsing.getLoc(filename, tokens, i + n), "Expected a list element.", res);
|
||||||
else n += res.n;
|
else n += res.n;
|
||||||
|
|
||||||
values.put(name.result.toString(), JSONElement.of(res.result));
|
values.put(name.result.toString(), JSONElement.of(res.result));
|
||||||
|
|
||||||
if (Parsing.isOperator(tokens, i + n, Operator.COMMA)) n++;
|
if (Parsing.isOperator(tokens, i + n, Operator.COMMA)) n++;
|
||||||
else if (Parsing.isOperator(tokens, i + n, Operator.BRACE_CLOSE)) {
|
else if (Parsing.isOperator(tokens, i + n, Operator.BRACE_CLOSE)) {
|
||||||
n++;
|
n++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ParseRes.res(values, n);
|
return ParseRes.res(values, n);
|
||||||
}
|
}
|
||||||
public static ParseRes<JSONList> parseList(Filename filename, List<Token> tokens, int i) {
|
public static ParseRes<JSONList> parseList(Filename filename, List<Token> tokens, int i) {
|
||||||
int n = 0;
|
int n = 0;
|
||||||
if (!Parsing.isOperator(tokens, i + n++, Operator.BRACKET_OPEN)) return ParseRes.failed();
|
if (!Parsing.isOperator(tokens, i + n++, Operator.BRACKET_OPEN)) return ParseRes.failed();
|
||||||
|
|
||||||
var values = new JSONList();
|
var values = new JSONList();
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (Parsing.isOperator(tokens, i + n, Operator.BRACKET_CLOSE)) {
|
if (Parsing.isOperator(tokens, i + n, Operator.BRACKET_CLOSE)) {
|
||||||
n++;
|
n++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
var res = parseValue(filename, tokens, i + n);
|
var res = parseValue(filename, tokens, i + n);
|
||||||
if (!res.isSuccess()) return ParseRes.error(Parsing.getLoc(filename, tokens, i + n), "Expected a list element.", res);
|
if (!res.isSuccess()) return ParseRes.error(Parsing.getLoc(filename, tokens, i + n), "Expected a list element.", res);
|
||||||
else n += res.n;
|
else n += res.n;
|
||||||
|
|
||||||
values.add(JSONElement.of(res.result));
|
values.add(JSONElement.of(res.result));
|
||||||
|
|
||||||
if (Parsing.isOperator(tokens, i + n, Operator.COMMA)) n++;
|
if (Parsing.isOperator(tokens, i + n, Operator.COMMA)) n++;
|
||||||
else if (Parsing.isOperator(tokens, i + n, Operator.BRACKET_CLOSE)) {
|
else if (Parsing.isOperator(tokens, i + n, Operator.BRACKET_CLOSE)) {
|
||||||
n++;
|
n++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ParseRes.res(values, n);
|
return ParseRes.res(values, n);
|
||||||
}
|
}
|
||||||
public static JSONElement parse(Filename filename, String raw) {
|
public static JSONElement parse(Filename filename, String raw) {
|
||||||
if (filename == null) filename = new Filename("jscript", "json");
|
if (filename == null) filename = new Filename("jscript", "json");
|
||||||
var res = parseValue(filename, Parsing.tokenize(filename, raw), 0);
|
var res = parseValue(filename, Parsing.tokenize(filename, raw), 0);
|
||||||
if (res.isFailed()) throw new SyntaxException(null, "Invalid JSON given.");
|
if (res.isFailed()) throw new SyntaxException(null, "Invalid JSON given.");
|
||||||
else if (res.isError()) throw new SyntaxException(null, res.error);
|
else if (res.isError()) throw new SyntaxException(null, res.error);
|
||||||
else return JSONElement.of(res.result);
|
else return JSONElement.of(res.result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String stringify(JSONElement el) {
|
public static String stringify(JSONElement el) {
|
||||||
if (el.isNumber()) return Double.toString(el.number());
|
if (el.isNumber()) return Double.toString(el.number());
|
||||||
if (el.isBoolean()) return el.bool() ? "true" : "false";
|
if (el.isBoolean()) return el.bool() ? "true" : "false";
|
||||||
if (el.isNull()) return "null";
|
if (el.isNull()) return "null";
|
||||||
if (el.isString()) {
|
if (el.isString()) {
|
||||||
var res = new StringBuilder("\"");
|
var res = new StringBuilder("\"");
|
||||||
var alphabet = "0123456789ABCDEF".toCharArray();
|
var alphabet = "0123456789ABCDEF".toCharArray();
|
||||||
|
|
||||||
for (var c : el.string().toCharArray()) {
|
for (var c : el.string().toCharArray()) {
|
||||||
if (c < 32 || c >= 127) {
|
if (c < 32 || c >= 127) {
|
||||||
res
|
res
|
||||||
.append("\\u")
|
.append("\\u")
|
||||||
.append(alphabet[(c >> 12) & 0xF])
|
.append(alphabet[(c >> 12) & 0xF])
|
||||||
.append(alphabet[(c >> 8) & 0xF])
|
.append(alphabet[(c >> 8) & 0xF])
|
||||||
.append(alphabet[(c >> 4) & 0xF])
|
.append(alphabet[(c >> 4) & 0xF])
|
||||||
.append(alphabet[(c >> 0) & 0xF]);
|
.append(alphabet[(c >> 0) & 0xF]);
|
||||||
}
|
}
|
||||||
else if (c == '\\')
|
else if (c == '\\')
|
||||||
res.append("\\\\");
|
res.append("\\\\");
|
||||||
else if (c == '"')
|
else if (c == '"')
|
||||||
res.append("\\\"");
|
res.append("\\\"");
|
||||||
else res.append(c);
|
else res.append(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.append('"').toString();
|
return res.append('"').toString();
|
||||||
}
|
}
|
||||||
if (el.isList()) {
|
if (el.isList()) {
|
||||||
var res = new StringBuilder().append("[");
|
var res = new StringBuilder().append("[");
|
||||||
for (int i = 0; i < el.list().size(); i++) {
|
for (int i = 0; i < el.list().size(); i++) {
|
||||||
if (i != 0) res.append(",");
|
if (i != 0) res.append(",");
|
||||||
res.append(stringify(el.list().get(i)));
|
res.append(stringify(el.list().get(i)));
|
||||||
}
|
}
|
||||||
res.append("]");
|
res.append("]");
|
||||||
return res.toString();
|
return res.toString();
|
||||||
}
|
}
|
||||||
if (el.isMap()) {
|
if (el.isMap()) {
|
||||||
var res = new StringBuilder().append("{");
|
var res = new StringBuilder().append("{");
|
||||||
var entries = el.map().entrySet().stream().collect(Collectors.toList());
|
var entries = el.map().entrySet().stream().collect(Collectors.toList());
|
||||||
|
|
||||||
for (int i = 0; i < entries.size(); i++) {
|
for (int i = 0; i < entries.size(); i++) {
|
||||||
if (i != 0) res.append(",");
|
if (i != 0) res.append(",");
|
||||||
res.append(stringify(JSONElement.string(entries.get(i).getKey())));
|
res.append(stringify(JSONElement.string(entries.get(i).getKey())));
|
||||||
res.append(":");
|
res.append(":");
|
||||||
res.append(stringify(entries.get(i).getValue()));
|
res.append(stringify(entries.get(i).getValue()));
|
||||||
}
|
}
|
||||||
res.append("}");
|
res.append("}");
|
||||||
return res.toString();
|
return res.toString();
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
public static String stringify(JSONMap map) {
|
public static String stringify(JSONMap map) {
|
||||||
return stringify(JSONElement.of(map));
|
return stringify(JSONElement.of(map));
|
||||||
}
|
}
|
||||||
public static String stringify(JSONList list) {
|
public static String stringify(JSONList list) {
|
||||||
return stringify(JSONElement.of(list));
|
return stringify(JSONElement.of(list));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,88 +1,88 @@
|
|||||||
package me.topchetoeu.jscript.json;
|
package me.topchetoeu.jscript.json;
|
||||||
|
|
||||||
public class JSONElement {
|
public class JSONElement {
|
||||||
public static enum Type {
|
public static enum Type {
|
||||||
STRING,
|
STRING,
|
||||||
NUMBER,
|
NUMBER,
|
||||||
BOOLEAN,
|
BOOLEAN,
|
||||||
NULL,
|
NULL,
|
||||||
LIST,
|
LIST,
|
||||||
MAP,
|
MAP,
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final JSONElement NULL = new JSONElement(Type.NULL, null);
|
public static final JSONElement NULL = new JSONElement(Type.NULL, null);
|
||||||
|
|
||||||
public static JSONElement map(JSONMap val) {
|
public static JSONElement map(JSONMap val) {
|
||||||
return new JSONElement(Type.MAP, val);
|
return new JSONElement(Type.MAP, val);
|
||||||
}
|
}
|
||||||
public static JSONElement list(JSONList val) {
|
public static JSONElement list(JSONList val) {
|
||||||
return new JSONElement(Type.LIST, val);
|
return new JSONElement(Type.LIST, val);
|
||||||
}
|
}
|
||||||
public static JSONElement string(String val) {
|
public static JSONElement string(String val) {
|
||||||
return new JSONElement(Type.STRING, val);
|
return new JSONElement(Type.STRING, val);
|
||||||
}
|
}
|
||||||
public static JSONElement number(double val) {
|
public static JSONElement number(double val) {
|
||||||
return new JSONElement(Type.NUMBER, val);
|
return new JSONElement(Type.NUMBER, val);
|
||||||
}
|
}
|
||||||
public static JSONElement bool(boolean val) {
|
public static JSONElement bool(boolean val) {
|
||||||
return new JSONElement(Type.BOOLEAN, val);
|
return new JSONElement(Type.BOOLEAN, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static JSONElement of(Object val) {
|
public static JSONElement of(Object val) {
|
||||||
if (val instanceof JSONMap) return map((JSONMap)val);
|
if (val instanceof JSONMap) return map((JSONMap)val);
|
||||||
else if (val instanceof JSONList) return list((JSONList)val);
|
else if (val instanceof JSONList) return list((JSONList)val);
|
||||||
else if (val instanceof String) return string((String)val);
|
else if (val instanceof String) return string((String)val);
|
||||||
else if (val instanceof Boolean) return bool((Boolean)val);
|
else if (val instanceof Boolean) return bool((Boolean)val);
|
||||||
else if (val instanceof Number) return number(((Number)val).doubleValue());
|
else if (val instanceof Number) return number(((Number)val).doubleValue());
|
||||||
else if (val == null) return NULL;
|
else if (val == null) return NULL;
|
||||||
else throw new IllegalArgumentException("val must be: String, Boolean, Number, JSONList or JSONMap.");
|
else throw new IllegalArgumentException("val must be: String, Boolean, Number, JSONList or JSONMap.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public final Type type;
|
public final Type type;
|
||||||
private final Object value;
|
private final Object value;
|
||||||
|
|
||||||
public boolean isMap() { return type == Type.MAP; }
|
public boolean isMap() { return type == Type.MAP; }
|
||||||
public boolean isList() { return type == Type.LIST; }
|
public boolean isList() { return type == Type.LIST; }
|
||||||
public boolean isString() { return type == Type.STRING; }
|
public boolean isString() { return type == Type.STRING; }
|
||||||
public boolean isNumber() { return type == Type.NUMBER; }
|
public boolean isNumber() { return type == Type.NUMBER; }
|
||||||
public boolean isBoolean() { return type == Type.BOOLEAN; }
|
public boolean isBoolean() { return type == Type.BOOLEAN; }
|
||||||
public boolean isNull() { return type == Type.NULL; }
|
public boolean isNull() { return type == Type.NULL; }
|
||||||
|
|
||||||
public JSONMap map() {
|
public JSONMap map() {
|
||||||
if (!isMap()) throw new IllegalStateException("Element is not a map.");
|
if (!isMap()) throw new IllegalStateException("Element is not a map.");
|
||||||
return (JSONMap)value;
|
return (JSONMap)value;
|
||||||
}
|
}
|
||||||
public JSONList list() {
|
public JSONList list() {
|
||||||
if (!isList()) throw new IllegalStateException("Element is not a map.");
|
if (!isList()) throw new IllegalStateException("Element is not a map.");
|
||||||
return (JSONList)value;
|
return (JSONList)value;
|
||||||
}
|
}
|
||||||
public String string() {
|
public String string() {
|
||||||
if (!isString()) throw new IllegalStateException("Element is not a string.");
|
if (!isString()) throw new IllegalStateException("Element is not a string.");
|
||||||
return (String)value;
|
return (String)value;
|
||||||
}
|
}
|
||||||
public double number() {
|
public double number() {
|
||||||
if (!isNumber()) throw new IllegalStateException("Element is not a number.");
|
if (!isNumber()) throw new IllegalStateException("Element is not a number.");
|
||||||
return (double)value;
|
return (double)value;
|
||||||
}
|
}
|
||||||
public boolean bool() {
|
public boolean bool() {
|
||||||
if (!isBoolean()) throw new IllegalStateException("Element is not a boolean.");
|
if (!isBoolean()) throw new IllegalStateException("Element is not a boolean.");
|
||||||
return (boolean)value;
|
return (boolean)value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
if (isMap()) return "{...}";
|
if (isMap()) return "{...}";
|
||||||
if (isList()) return "[...]";
|
if (isList()) return "[...]";
|
||||||
if (isString()) return (String)value;
|
if (isString()) return (String)value;
|
||||||
if (isString()) return (String)value;
|
if (isString()) return (String)value;
|
||||||
if (isNumber()) return (double)value + "";
|
if (isNumber()) return (double)value + "";
|
||||||
if (isBoolean()) return (boolean)value + "";
|
if (isBoolean()) return (boolean)value + "";
|
||||||
if (isNull()) return "null";
|
if (isNull()) return "null";
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
private JSONElement(Type type, Object val) {
|
private JSONElement(Type type, Object val) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.value = val;
|
this.value = val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,26 +1,26 @@
|
|||||||
package me.topchetoeu.jscript.json;
|
package me.topchetoeu.jscript.json;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class JSONList extends ArrayList<JSONElement> {
|
public class JSONList extends ArrayList<JSONElement> {
|
||||||
public JSONList() {}
|
public JSONList() {}
|
||||||
public JSONList(JSONElement ...els) {
|
public JSONList(JSONElement ...els) {
|
||||||
super(List.of(els));
|
super(List.of(els));
|
||||||
}
|
}
|
||||||
public JSONList(Collection<JSONElement> els) {
|
public JSONList(Collection<JSONElement> els) {
|
||||||
super(els);
|
super(els);
|
||||||
}
|
}
|
||||||
|
|
||||||
public JSONList addNull() { this.add(JSONElement.NULL); return this; }
|
public JSONList addNull() { this.add(JSONElement.NULL); return this; }
|
||||||
public JSONList add(String val) { this.add(JSONElement.of(val)); return this; }
|
public JSONList add(String val) { this.add(JSONElement.of(val)); return this; }
|
||||||
public JSONList add(double val) { this.add(JSONElement.of(val)); return this; }
|
public JSONList add(double val) { this.add(JSONElement.of(val)); return this; }
|
||||||
public JSONList add(boolean val) { this.add(JSONElement.of(val)); return this; }
|
public JSONList add(boolean val) { this.add(JSONElement.of(val)); return this; }
|
||||||
public JSONList add(Map<String, JSONElement> val) { this.add(JSONElement.of(val)); return this; }
|
public JSONList add(Map<String, JSONElement> val) { this.add(JSONElement.of(val)); return this; }
|
||||||
public JSONList add(Collection<JSONElement> val) { this.add(JSONElement.of(val)); return this; }
|
public JSONList add(Collection<JSONElement> val) { this.add(JSONElement.of(val)); return this; }
|
||||||
public JSONList add(JSONMap val) { this.add(JSONElement.of(val)); return this; }
|
public JSONList add(JSONMap val) { this.add(JSONElement.of(val)); return this; }
|
||||||
public JSONList add(JSONList val) { this.add(JSONElement.of(val)); return this; }
|
public JSONList add(JSONList val) { this.add(JSONElement.of(val)); return this; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,150 +1,150 @@
|
|||||||
package me.topchetoeu.jscript.json;
|
package me.topchetoeu.jscript.json;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
public class JSONMap implements Map<String, JSONElement> {
|
public class JSONMap implements Map<String, JSONElement> {
|
||||||
private Map<String, JSONElement> elements = new HashMap<>();
|
private Map<String, JSONElement> elements = new HashMap<>();
|
||||||
|
|
||||||
public JSONElement get(String path) {
|
public JSONElement get(String path) {
|
||||||
var curr = this;
|
var curr = this;
|
||||||
var segs = path.split("\\.");
|
var segs = path.split("\\.");
|
||||||
var i = 0;
|
var i = 0;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
var tmp = curr.elements.get(segs[i++]);
|
var tmp = curr.elements.get(segs[i++]);
|
||||||
if (i == segs.length) return tmp;
|
if (i == segs.length) return tmp;
|
||||||
if (!tmp.isMap()) return null;
|
if (!tmp.isMap()) return null;
|
||||||
curr = tmp.map();
|
curr = tmp.map();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isMap(String path) {
|
public boolean isMap(String path) {
|
||||||
var el = get(path);
|
var el = get(path);
|
||||||
return el != null && el.isMap();
|
return el != null && el.isMap();
|
||||||
}
|
}
|
||||||
public boolean isList(String path) {
|
public boolean isList(String path) {
|
||||||
var el = get(path);
|
var el = get(path);
|
||||||
return el != null && el.isList();
|
return el != null && el.isList();
|
||||||
}
|
}
|
||||||
public boolean isString(String path) {
|
public boolean isString(String path) {
|
||||||
var el = get(path);
|
var el = get(path);
|
||||||
return el != null && el.isString();
|
return el != null && el.isString();
|
||||||
}
|
}
|
||||||
public boolean isNumber(String path) {
|
public boolean isNumber(String path) {
|
||||||
var el = get(path);
|
var el = get(path);
|
||||||
return el != null && el.isNumber();
|
return el != null && el.isNumber();
|
||||||
}
|
}
|
||||||
public boolean isBoolean(String path) {
|
public boolean isBoolean(String path) {
|
||||||
var el = get(path);
|
var el = get(path);
|
||||||
return el != null && el.isBoolean();
|
return el != null && el.isBoolean();
|
||||||
}
|
}
|
||||||
public boolean isNull(String path) {
|
public boolean isNull(String path) {
|
||||||
var el = get(path);
|
var el = get(path);
|
||||||
return el != null && el.isNull();
|
return el != null && el.isNull();
|
||||||
}
|
}
|
||||||
public boolean contains(String path) {
|
public boolean contains(String path) {
|
||||||
return get(path) != null;
|
return get(path) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public JSONMap map(String path) {
|
public JSONMap map(String path) {
|
||||||
var el = get(path);
|
var el = get(path);
|
||||||
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist.", path));
|
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist.", path));
|
||||||
return el.map();
|
return el.map();
|
||||||
}
|
}
|
||||||
public JSONMap map(String path, JSONMap defaultVal) {
|
public JSONMap map(String path, JSONMap defaultVal) {
|
||||||
var el = get(path);
|
var el = get(path);
|
||||||
if (el == null) return defaultVal;
|
if (el == null) return defaultVal;
|
||||||
if (el.isMap()) return el.map();
|
if (el.isMap()) return el.map();
|
||||||
return defaultVal;
|
return defaultVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
public JSONList list(String path) {
|
public JSONList list(String path) {
|
||||||
var el = get(path);
|
var el = get(path);
|
||||||
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist.", path));
|
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist.", path));
|
||||||
return el.list();
|
return el.list();
|
||||||
}
|
}
|
||||||
public JSONList list(String path, JSONList defaultVal) {
|
public JSONList list(String path, JSONList defaultVal) {
|
||||||
var el = get(path);
|
var el = get(path);
|
||||||
if (el == null) return defaultVal;
|
if (el == null) return defaultVal;
|
||||||
if (el.isList()) return el.list();
|
if (el.isList()) return el.list();
|
||||||
return defaultVal;
|
return defaultVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String string(String path) {
|
public String string(String path) {
|
||||||
var el = get(path);
|
var el = get(path);
|
||||||
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist.", path));
|
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist.", path));
|
||||||
return el.string();
|
return el.string();
|
||||||
}
|
}
|
||||||
public String string(String path, String defaultVal) {
|
public String string(String path, String defaultVal) {
|
||||||
var el = get(path);
|
var el = get(path);
|
||||||
if (el == null) return defaultVal;
|
if (el == null) return defaultVal;
|
||||||
if (el.isString()) return el.string();
|
if (el.isString()) return el.string();
|
||||||
return defaultVal;
|
return defaultVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
public double number(String path) {
|
public double number(String path) {
|
||||||
var el = get(path);
|
var el = get(path);
|
||||||
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist.", path));
|
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist.", path));
|
||||||
return el.number();
|
return el.number();
|
||||||
}
|
}
|
||||||
public double number(String path, double defaultVal) {
|
public double number(String path, double defaultVal) {
|
||||||
var el = get(path);
|
var el = get(path);
|
||||||
if (el == null) return defaultVal;
|
if (el == null) return defaultVal;
|
||||||
if (el.isNumber()) return el.number();
|
if (el.isNumber()) return el.number();
|
||||||
return defaultVal;
|
return defaultVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean bool(String path) {
|
public boolean bool(String path) {
|
||||||
var el = get(path);
|
var el = get(path);
|
||||||
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist.", path));
|
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist.", path));
|
||||||
return el.bool();
|
return el.bool();
|
||||||
}
|
}
|
||||||
public boolean bool(String path, boolean defaultVal) {
|
public boolean bool(String path, boolean defaultVal) {
|
||||||
var el = get(path);
|
var el = get(path);
|
||||||
if (el == null) return defaultVal;
|
if (el == null) return defaultVal;
|
||||||
if (el.isBoolean()) return el.bool();
|
if (el.isBoolean()) return el.bool();
|
||||||
return defaultVal;
|
return defaultVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
public JSONMap setNull(String key) { elements.put(key, JSONElement.NULL); return this; }
|
public JSONMap setNull(String key) { elements.put(key, JSONElement.NULL); return this; }
|
||||||
public JSONMap set(String key, String val) { elements.put(key, JSONElement.of(val)); return this; }
|
public JSONMap set(String key, String val) { elements.put(key, JSONElement.of(val)); return this; }
|
||||||
public JSONMap set(String key, double val) { elements.put(key, JSONElement.of(val)); return this; }
|
public JSONMap set(String key, double val) { elements.put(key, JSONElement.of(val)); return this; }
|
||||||
public JSONMap set(String key, boolean val) { elements.put(key, JSONElement.of(val)); return this; }
|
public JSONMap set(String key, boolean val) { elements.put(key, JSONElement.of(val)); return this; }
|
||||||
public JSONMap set(String key, Map<String, JSONElement> val) { elements.put(key, JSONElement.of(val)); return this; }
|
public JSONMap set(String key, Map<String, JSONElement> val) { elements.put(key, JSONElement.of(val)); return this; }
|
||||||
public JSONMap set(String key, Collection<JSONElement> val) { elements.put(key, JSONElement.of(val)); return this; }
|
public JSONMap set(String key, Collection<JSONElement> val) { elements.put(key, JSONElement.of(val)); return this; }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int size() { return elements.size(); }
|
public int size() { return elements.size(); }
|
||||||
@Override
|
@Override
|
||||||
public boolean isEmpty() { return elements.isEmpty(); }
|
public boolean isEmpty() { return elements.isEmpty(); }
|
||||||
@Override
|
@Override
|
||||||
public boolean containsKey(Object key) { return elements.containsKey(key); }
|
public boolean containsKey(Object key) { return elements.containsKey(key); }
|
||||||
@Override
|
@Override
|
||||||
public boolean containsValue(Object value) { return elements.containsValue(value); }
|
public boolean containsValue(Object value) { return elements.containsValue(value); }
|
||||||
@Override
|
@Override
|
||||||
public JSONElement get(Object key) { return elements.get(key); }
|
public JSONElement get(Object key) { return elements.get(key); }
|
||||||
@Override
|
@Override
|
||||||
public JSONElement put(String key, JSONElement value) { return elements.put(key, value); }
|
public JSONElement put(String key, JSONElement value) { return elements.put(key, value); }
|
||||||
@Override
|
@Override
|
||||||
public JSONElement remove(Object key) { return elements.remove(key); }
|
public JSONElement remove(Object key) { return elements.remove(key); }
|
||||||
@Override
|
@Override
|
||||||
public void putAll(Map<? extends String, ? extends JSONElement> m) { elements.putAll(m); }
|
public void putAll(Map<? extends String, ? extends JSONElement> m) { elements.putAll(m); }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clear() { elements.clear(); }
|
public void clear() { elements.clear(); }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<String> keySet() { return elements.keySet(); }
|
public Set<String> keySet() { return elements.keySet(); }
|
||||||
@Override
|
@Override
|
||||||
public Collection<JSONElement> values() { return elements.values(); }
|
public Collection<JSONElement> values() { return elements.values(); }
|
||||||
@Override
|
@Override
|
||||||
public Set<Entry<String, JSONElement>> entrySet() { return elements.entrySet(); }
|
public Set<Entry<String, JSONElement>> entrySet() { return elements.entrySet(); }
|
||||||
|
|
||||||
public JSONMap() { }
|
public JSONMap() { }
|
||||||
public JSONMap(Map<String, JSONElement> els) {
|
public JSONMap(Map<String, JSONElement> els) {
|
||||||
this.elements = new HashMap<>(els);
|
this.elements = new HashMap<>(els);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,78 +1,78 @@
|
|||||||
package me.topchetoeu.jscript.lib;
|
package me.topchetoeu.jscript.lib;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
||||||
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
||||||
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
import me.topchetoeu.jscript.interop.Native;
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
import me.topchetoeu.jscript.interop.NativeGetter;
|
import me.topchetoeu.jscript.interop.NativeGetter;
|
||||||
|
|
||||||
@Native("Map") public class MapLib {
|
@Native("Map") public class MapLib {
|
||||||
private LinkedHashMap<Object, Object> map = new LinkedHashMap<>();
|
private LinkedHashMap<Object, Object> map = new LinkedHashMap<>();
|
||||||
|
|
||||||
@Native("@@Symbol.typeName") public final String name = "Map";
|
@Native("@@Symbol.typeName") public final String name = "Map";
|
||||||
@Native("@@Symbol.iterator") public ObjectValue iterator(Context ctx) {
|
@Native("@@Symbol.iterator") public ObjectValue iterator(Context ctx) {
|
||||||
return this.entries(ctx);
|
return this.entries(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Native public void clear() {
|
@Native public void clear() {
|
||||||
map.clear();
|
map.clear();
|
||||||
}
|
}
|
||||||
@Native public boolean delete(Object key) {
|
@Native public boolean delete(Object key) {
|
||||||
if (map.containsKey(key)) {
|
if (map.containsKey(key)) {
|
||||||
map.remove(key);
|
map.remove(key);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Native public ObjectValue entries(Context ctx) {
|
@Native public ObjectValue entries(Context ctx) {
|
||||||
return ArrayValue.of(ctx, map
|
return ArrayValue.of(ctx, map
|
||||||
.entrySet()
|
.entrySet()
|
||||||
.stream()
|
.stream()
|
||||||
.map(v -> new ArrayValue(ctx, v.getKey(), v.getValue()))
|
.map(v -> new ArrayValue(ctx, v.getKey(), v.getValue()))
|
||||||
.collect(Collectors.toList())
|
.collect(Collectors.toList())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@Native public ObjectValue keys(Context ctx) {
|
@Native public ObjectValue keys(Context ctx) {
|
||||||
return ArrayValue.of(ctx, map.keySet());
|
return ArrayValue.of(ctx, map.keySet());
|
||||||
}
|
}
|
||||||
@Native public ObjectValue values(Context ctx) {
|
@Native public ObjectValue values(Context ctx) {
|
||||||
return ArrayValue.of(ctx, map.values());
|
return ArrayValue.of(ctx, map.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Native public Object get(Object key) {
|
@Native public Object get(Object key) {
|
||||||
return map.get(key);
|
return map.get(key);
|
||||||
}
|
}
|
||||||
@Native public MapLib set(Object key, Object val) {
|
@Native public MapLib set(Object key, Object val) {
|
||||||
map.put(key, val);
|
map.put(key, val);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@Native public boolean has(Object key) {
|
@Native public boolean has(Object key) {
|
||||||
return map.containsKey(key);
|
return map.containsKey(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NativeGetter public int size() {
|
@NativeGetter public int size() {
|
||||||
return map.size();
|
return map.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Native public void forEach(Context ctx, FunctionValue func, Object thisArg) {
|
@Native public void forEach(Context ctx, FunctionValue func, Object thisArg) {
|
||||||
var keys = new ArrayList<>(map.keySet());
|
var keys = new ArrayList<>(map.keySet());
|
||||||
|
|
||||||
for (var el : keys) func.call(ctx, thisArg, map.get(el), el,this);
|
for (var el : keys) func.call(ctx, thisArg, map.get(el), el,this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Native public MapLib(Context ctx, Object iterable) {
|
@Native public MapLib(Context ctx, Object iterable) {
|
||||||
for (var el : Values.fromJSIterator(ctx, iterable)) {
|
for (var el : Values.fromJSIterator(ctx, iterable)) {
|
||||||
try {
|
try {
|
||||||
set(Values.getMember(ctx, el, 0), Values.getMember(ctx, el, 1));
|
set(Values.getMember(ctx, el, 0), Values.getMember(ctx, el, 1));
|
||||||
}
|
}
|
||||||
catch (IllegalArgumentException e) { }
|
catch (IllegalArgumentException e) { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,367 +1,367 @@
|
|||||||
package me.topchetoeu.jscript.lib;
|
package me.topchetoeu.jscript.lib;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
||||||
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
||||||
import me.topchetoeu.jscript.engine.values.NativeFunction;
|
import me.topchetoeu.jscript.engine.values.NativeFunction;
|
||||||
import me.topchetoeu.jscript.engine.values.NativeWrapper;
|
import me.topchetoeu.jscript.engine.values.NativeWrapper;
|
||||||
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||||
import me.topchetoeu.jscript.interop.Native;
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
|
|
||||||
@Native("Promise") public class PromiseLib {
|
@Native("Promise") public class PromiseLib {
|
||||||
public static interface PromiseRunner {
|
public static interface PromiseRunner {
|
||||||
Object run();
|
Object run();
|
||||||
}
|
}
|
||||||
private static class Handle {
|
private static class Handle {
|
||||||
public final Context ctx;
|
public final Context ctx;
|
||||||
public final FunctionValue fulfilled;
|
public final FunctionValue fulfilled;
|
||||||
public final FunctionValue rejected;
|
public final FunctionValue rejected;
|
||||||
|
|
||||||
public Handle(Context ctx, FunctionValue fulfilled, FunctionValue rejected) {
|
public Handle(Context ctx, FunctionValue fulfilled, FunctionValue rejected) {
|
||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
this.fulfilled = fulfilled;
|
this.fulfilled = fulfilled;
|
||||||
this.rejected = rejected;
|
this.rejected = rejected;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Native("resolve")
|
@Native("resolve")
|
||||||
public static PromiseLib ofResolved(Context ctx, Object val) {
|
public static PromiseLib ofResolved(Context ctx, Object val) {
|
||||||
var res = new PromiseLib();
|
var res = new PromiseLib();
|
||||||
res.fulfill(ctx, val);
|
res.fulfill(ctx, val);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@Native("reject")
|
@Native("reject")
|
||||||
public static PromiseLib ofRejected(Context ctx, Object val) {
|
public static PromiseLib ofRejected(Context ctx, Object val) {
|
||||||
var res = new PromiseLib();
|
var res = new PromiseLib();
|
||||||
res.reject(ctx, val);
|
res.reject(ctx, val);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Native public static PromiseLib any(Context ctx, Object _promises) {
|
@Native public static PromiseLib any(Context ctx, Object _promises) {
|
||||||
if (!Values.isArray(_promises)) throw EngineException.ofType("Expected argument for any to be an array.");
|
if (!Values.isArray(_promises)) throw EngineException.ofType("Expected argument for any to be an array.");
|
||||||
var promises = Values.array(_promises);
|
var promises = Values.array(_promises);
|
||||||
if (promises.size() == 0) return ofResolved(ctx, new ArrayValue());
|
if (promises.size() == 0) return ofResolved(ctx, new ArrayValue());
|
||||||
var n = new int[] { promises.size() };
|
var n = new int[] { promises.size() };
|
||||||
var res = new PromiseLib();
|
var res = new PromiseLib();
|
||||||
|
|
||||||
var errors = new ArrayValue();
|
var errors = new ArrayValue();
|
||||||
|
|
||||||
for (var i = 0; i < promises.size(); i++) {
|
for (var i = 0; i < promises.size(); i++) {
|
||||||
var index = i;
|
var index = i;
|
||||||
var val = promises.get(i);
|
var val = promises.get(i);
|
||||||
then(ctx, val,
|
then(ctx, val,
|
||||||
new NativeFunction(null, (e, th, args) -> { res.fulfill(e, args[0]); return null; }),
|
new NativeFunction(null, (e, th, args) -> { res.fulfill(e, args[0]); return null; }),
|
||||||
new NativeFunction(null, (e, th, args) -> {
|
new NativeFunction(null, (e, th, args) -> {
|
||||||
errors.set(ctx, index, args[0]);
|
errors.set(ctx, index, args[0]);
|
||||||
n[0]--;
|
n[0]--;
|
||||||
if (n[0] <= 0) res.reject(e, errors);
|
if (n[0] <= 0) res.reject(e, errors);
|
||||||
return null;
|
return null;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@Native public static PromiseLib race(Context ctx, Object _promises) {
|
@Native public static PromiseLib race(Context ctx, Object _promises) {
|
||||||
if (!Values.isArray(_promises)) throw EngineException.ofType("Expected argument for any to be an array.");
|
if (!Values.isArray(_promises)) throw EngineException.ofType("Expected argument for any to be an array.");
|
||||||
var promises = Values.array(_promises);
|
var promises = Values.array(_promises);
|
||||||
if (promises.size() == 0) return ofResolved(ctx, new ArrayValue());
|
if (promises.size() == 0) return ofResolved(ctx, new ArrayValue());
|
||||||
var res = new PromiseLib();
|
var res = new PromiseLib();
|
||||||
|
|
||||||
for (var i = 0; i < promises.size(); i++) {
|
for (var i = 0; i < promises.size(); i++) {
|
||||||
var val = promises.get(i);
|
var val = promises.get(i);
|
||||||
then(ctx, val,
|
then(ctx, val,
|
||||||
new NativeFunction(null, (e, th, args) -> { res.fulfill(e, args[0]); return null; }),
|
new NativeFunction(null, (e, th, args) -> { res.fulfill(e, args[0]); return null; }),
|
||||||
new NativeFunction(null, (e, th, args) -> { res.reject(e, args[0]); return null; })
|
new NativeFunction(null, (e, th, args) -> { res.reject(e, args[0]); return null; })
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@Native public static PromiseLib all(Context ctx, Object _promises) {
|
@Native public static PromiseLib all(Context ctx, Object _promises) {
|
||||||
if (!Values.isArray(_promises)) throw EngineException.ofType("Expected argument for any to be an array.");
|
if (!Values.isArray(_promises)) throw EngineException.ofType("Expected argument for any to be an array.");
|
||||||
var promises = Values.array(_promises);
|
var promises = Values.array(_promises);
|
||||||
if (promises.size() == 0) return ofResolved(ctx, new ArrayValue());
|
if (promises.size() == 0) return ofResolved(ctx, new ArrayValue());
|
||||||
var n = new int[] { promises.size() };
|
var n = new int[] { promises.size() };
|
||||||
var res = new PromiseLib();
|
var res = new PromiseLib();
|
||||||
|
|
||||||
var result = new ArrayValue();
|
var result = new ArrayValue();
|
||||||
|
|
||||||
for (var i = 0; i < promises.size(); i++) {
|
for (var i = 0; i < promises.size(); i++) {
|
||||||
var index = i;
|
var index = i;
|
||||||
var val = promises.get(i);
|
var val = promises.get(i);
|
||||||
then(ctx, val,
|
then(ctx, val,
|
||||||
new NativeFunction(null, (e, th, args) -> {
|
new NativeFunction(null, (e, th, args) -> {
|
||||||
result.set(ctx, index, args[0]);
|
result.set(ctx, index, args[0]);
|
||||||
n[0]--;
|
n[0]--;
|
||||||
if (n[0] <= 0) res.fulfill(e, result);
|
if (n[0] <= 0) res.fulfill(e, result);
|
||||||
return null;
|
return null;
|
||||||
}),
|
}),
|
||||||
new NativeFunction(null, (e, th, args) -> { res.reject(e, args[0]); return null; })
|
new NativeFunction(null, (e, th, args) -> { res.reject(e, args[0]); return null; })
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (n[0] <= 0) res.fulfill(ctx, result);
|
if (n[0] <= 0) res.fulfill(ctx, result);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@Native public static PromiseLib allSettled(Context ctx, Object _promises) {
|
@Native public static PromiseLib allSettled(Context ctx, Object _promises) {
|
||||||
if (!Values.isArray(_promises)) throw EngineException.ofType("Expected argument for any to be an array.");
|
if (!Values.isArray(_promises)) throw EngineException.ofType("Expected argument for any to be an array.");
|
||||||
var promises = Values.array(_promises);
|
var promises = Values.array(_promises);
|
||||||
if (promises.size() == 0) return ofResolved(ctx, new ArrayValue());
|
if (promises.size() == 0) return ofResolved(ctx, new ArrayValue());
|
||||||
var n = new int[] { promises.size() };
|
var n = new int[] { promises.size() };
|
||||||
var res = new PromiseLib();
|
var res = new PromiseLib();
|
||||||
|
|
||||||
var result = new ArrayValue();
|
var result = new ArrayValue();
|
||||||
|
|
||||||
for (var i = 0; i < promises.size(); i++) {
|
for (var i = 0; i < promises.size(); i++) {
|
||||||
var index = i;
|
var index = i;
|
||||||
var val = promises.get(i);
|
var val = promises.get(i);
|
||||||
then(ctx, val,
|
then(ctx, val,
|
||||||
new NativeFunction(null, (e, th, args) -> {
|
new NativeFunction(null, (e, th, args) -> {
|
||||||
result.set(ctx, index, new ObjectValue(ctx, Map.of(
|
result.set(ctx, index, new ObjectValue(ctx, Map.of(
|
||||||
"status", "fulfilled",
|
"status", "fulfilled",
|
||||||
"value", args[0]
|
"value", args[0]
|
||||||
)));
|
)));
|
||||||
n[0]--;
|
n[0]--;
|
||||||
if (n[0] <= 0) res.fulfill(e, result);
|
if (n[0] <= 0) res.fulfill(e, result);
|
||||||
return null;
|
return null;
|
||||||
}),
|
}),
|
||||||
new NativeFunction(null, (e, th, args) -> {
|
new NativeFunction(null, (e, th, args) -> {
|
||||||
result.set(ctx, index, new ObjectValue(ctx, Map.of(
|
result.set(ctx, index, new ObjectValue(ctx, Map.of(
|
||||||
"status", "rejected",
|
"status", "rejected",
|
||||||
"reason", args[0]
|
"reason", args[0]
|
||||||
)));
|
)));
|
||||||
n[0]--;
|
n[0]--;
|
||||||
if (n[0] <= 0) res.fulfill(e, result);
|
if (n[0] <= 0) res.fulfill(e, result);
|
||||||
return null;
|
return null;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (n[0] <= 0) res.fulfill(ctx, result);
|
if (n[0] <= 0) res.fulfill(ctx, result);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Thread safe - you can call this from anywhere
|
* Thread safe - you can call this from anywhere
|
||||||
* HOWEVER, it's strongly recommended to use this only in javascript
|
* HOWEVER, it's strongly recommended to use this only in javascript
|
||||||
*/
|
*/
|
||||||
@Native(thisArg=true) public static Object then(Context ctx, Object _thisArg, Object _onFulfill, Object _onReject) {
|
@Native(thisArg=true) public static Object then(Context ctx, Object _thisArg, Object _onFulfill, Object _onReject) {
|
||||||
var onFulfill = _onFulfill instanceof FunctionValue ? ((FunctionValue)_onFulfill) : null;
|
var onFulfill = _onFulfill instanceof FunctionValue ? ((FunctionValue)_onFulfill) : null;
|
||||||
var onReject = _onReject instanceof FunctionValue ? ((FunctionValue)_onReject) : null;
|
var onReject = _onReject instanceof FunctionValue ? ((FunctionValue)_onReject) : null;
|
||||||
|
|
||||||
var res = new PromiseLib();
|
var res = new PromiseLib();
|
||||||
|
|
||||||
var fulfill = onFulfill == null ? new NativeFunction((_ctx, _0, _args) -> _args.length > 0 ? _args[0] : null) : (FunctionValue)onFulfill;
|
var fulfill = onFulfill == null ? new NativeFunction((_ctx, _0, _args) -> _args.length > 0 ? _args[0] : null) : (FunctionValue)onFulfill;
|
||||||
var reject = onReject == null ? new NativeFunction((_ctx, _0, _args) -> {
|
var reject = onReject == null ? new NativeFunction((_ctx, _0, _args) -> {
|
||||||
throw new EngineException(_args.length > 0 ? _args[0] : null);
|
throw new EngineException(_args.length > 0 ? _args[0] : null);
|
||||||
}) : (FunctionValue)onReject;
|
}) : (FunctionValue)onReject;
|
||||||
|
|
||||||
var thisArg = _thisArg instanceof NativeWrapper && ((NativeWrapper)_thisArg).wrapped instanceof PromiseLib ?
|
var thisArg = _thisArg instanceof NativeWrapper && ((NativeWrapper)_thisArg).wrapped instanceof PromiseLib ?
|
||||||
((NativeWrapper)_thisArg).wrapped :
|
((NativeWrapper)_thisArg).wrapped :
|
||||||
_thisArg;
|
_thisArg;
|
||||||
|
|
||||||
var fulfillHandle = new NativeFunction(null, (_ctx, th, a) -> {
|
var fulfillHandle = new NativeFunction(null, (_ctx, th, a) -> {
|
||||||
try { res.fulfill(ctx, Values.convert(ctx, fulfill.call(ctx, null, a[0]), Object.class)); }
|
try { res.fulfill(ctx, Values.convert(ctx, fulfill.call(ctx, null, a[0]), Object.class)); }
|
||||||
catch (EngineException err) { res.reject(ctx, err.value); }
|
catch (EngineException err) { res.reject(ctx, err.value); }
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
var rejectHandle = new NativeFunction(null, (_ctx, th, a) -> {
|
var rejectHandle = new NativeFunction(null, (_ctx, th, a) -> {
|
||||||
try { res.fulfill(ctx, reject.call(ctx, null, a[0])); }
|
try { res.fulfill(ctx, reject.call(ctx, null, a[0])); }
|
||||||
catch (EngineException err) { res.reject(ctx, err.value); }
|
catch (EngineException err) { res.reject(ctx, err.value); }
|
||||||
if (thisArg instanceof PromiseLib) ((PromiseLib)thisArg).handled = true;
|
if (thisArg instanceof PromiseLib) ((PromiseLib)thisArg).handled = true;
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (thisArg instanceof PromiseLib) ((PromiseLib)thisArg).handle(ctx, fulfillHandle, rejectHandle);
|
if (thisArg instanceof PromiseLib) ((PromiseLib)thisArg).handle(ctx, fulfillHandle, rejectHandle);
|
||||||
else {
|
else {
|
||||||
Object next;
|
Object next;
|
||||||
try { next = Values.getMember(ctx, thisArg, "then"); }
|
try { next = Values.getMember(ctx, thisArg, "then"); }
|
||||||
catch (IllegalArgumentException e) { next = null; }
|
catch (IllegalArgumentException e) { next = null; }
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (next instanceof FunctionValue) ((FunctionValue)next).call(ctx, thisArg, fulfillHandle, rejectHandle);
|
if (next instanceof FunctionValue) ((FunctionValue)next).call(ctx, thisArg, fulfillHandle, rejectHandle);
|
||||||
else res.fulfill(ctx, fulfill.call(ctx, null, thisArg));
|
else res.fulfill(ctx, fulfill.call(ctx, null, thisArg));
|
||||||
}
|
}
|
||||||
catch (EngineException err) {
|
catch (EngineException err) {
|
||||||
res.reject(ctx, fulfill.call(ctx, null, err.value));
|
res.reject(ctx, fulfill.call(ctx, null, err.value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Thread safe - you can call this from anywhere
|
* Thread safe - you can call this from anywhere
|
||||||
* HOWEVER, it's strongly recommended to use this only in javascript
|
* HOWEVER, it's strongly recommended to use this only in javascript
|
||||||
*/
|
*/
|
||||||
@Native(value="catch", thisArg=true) public static Object _catch(Context ctx, Object thisArg, Object _onReject) {
|
@Native(value="catch", thisArg=true) public static Object _catch(Context ctx, Object thisArg, Object _onReject) {
|
||||||
return then(ctx, thisArg, null, _onReject);
|
return then(ctx, thisArg, null, _onReject);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Thread safe - you can call this from anywhere
|
* Thread safe - you can call this from anywhere
|
||||||
* HOWEVER, it's strongly recommended to use this only in javascript
|
* HOWEVER, it's strongly recommended to use this only in javascript
|
||||||
*/
|
*/
|
||||||
@Native(value="finally", thisArg=true) public static Object _finally(Context ctx, Object thisArg, Object _handle) {
|
@Native(value="finally", thisArg=true) public static Object _finally(Context ctx, Object thisArg, Object _handle) {
|
||||||
return then(ctx, thisArg,
|
return then(ctx, thisArg,
|
||||||
new NativeFunction(null, (e, th, _args) -> {
|
new NativeFunction(null, (e, th, _args) -> {
|
||||||
if (_handle instanceof FunctionValue) ((FunctionValue)_handle).call(ctx);
|
if (_handle instanceof FunctionValue) ((FunctionValue)_handle).call(ctx);
|
||||||
return _args.length > 0 ? _args[0] : null;
|
return _args.length > 0 ? _args[0] : null;
|
||||||
}),
|
}),
|
||||||
new NativeFunction(null, (e, th, _args) -> {
|
new NativeFunction(null, (e, th, _args) -> {
|
||||||
if (_handle instanceof FunctionValue) ((FunctionValue)_handle).call(ctx);
|
if (_handle instanceof FunctionValue) ((FunctionValue)_handle).call(ctx);
|
||||||
throw new EngineException(_args.length > 0 ? _args[0] : null);
|
throw new EngineException(_args.length > 0 ? _args[0] : null);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Handle> handles = new ArrayList<>();
|
private List<Handle> handles = new ArrayList<>();
|
||||||
|
|
||||||
private static final int STATE_PENDING = 0;
|
private static final int STATE_PENDING = 0;
|
||||||
private static final int STATE_FULFILLED = 1;
|
private static final int STATE_FULFILLED = 1;
|
||||||
private static final int STATE_REJECTED = 2;
|
private static final int STATE_REJECTED = 2;
|
||||||
|
|
||||||
private int state = STATE_PENDING;
|
private int state = STATE_PENDING;
|
||||||
private boolean handled = false;
|
private boolean handled = false;
|
||||||
private Object val;
|
private Object val;
|
||||||
|
|
||||||
public synchronized void fulfill(Context ctx, Object val) {
|
public synchronized void fulfill(Context ctx, Object val) {
|
||||||
if (this.state != STATE_PENDING) return;
|
if (this.state != STATE_PENDING) return;
|
||||||
|
|
||||||
if (val instanceof PromiseLib) ((PromiseLib)val).handle(ctx,
|
if (val instanceof PromiseLib) ((PromiseLib)val).handle(ctx,
|
||||||
new NativeFunction(null, (e, th, a) -> { this.fulfill(ctx, a[0]); return null; }),
|
new NativeFunction(null, (e, th, a) -> { this.fulfill(ctx, a[0]); return null; }),
|
||||||
new NativeFunction(null, (e, th, a) -> { this.reject(ctx, a[0]); return null; })
|
new NativeFunction(null, (e, th, a) -> { this.reject(ctx, a[0]); return null; })
|
||||||
);
|
);
|
||||||
else {
|
else {
|
||||||
Object then;
|
Object then;
|
||||||
try { then = Values.getMember(ctx, val, "then"); }
|
try { then = Values.getMember(ctx, val, "then"); }
|
||||||
catch (IllegalArgumentException e) { then = null; }
|
catch (IllegalArgumentException e) { then = null; }
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (then instanceof FunctionValue) ((FunctionValue)then).call(ctx, val,
|
if (then instanceof FunctionValue) ((FunctionValue)then).call(ctx, val,
|
||||||
new NativeFunction((e, _thisArg, a) -> { this.fulfill(ctx, a.length > 0 ? a[0] : null); return null; }),
|
new NativeFunction((e, _thisArg, a) -> { this.fulfill(ctx, a.length > 0 ? a[0] : null); return null; }),
|
||||||
new NativeFunction((e, _thisArg, a) -> { this.reject(ctx, a.length > 0 ? a[0] : null); return null; })
|
new NativeFunction((e, _thisArg, a) -> { this.reject(ctx, a.length > 0 ? a[0] : null); return null; })
|
||||||
);
|
);
|
||||||
else {
|
else {
|
||||||
this.val = val;
|
this.val = val;
|
||||||
this.state = STATE_FULFILLED;
|
this.state = STATE_FULFILLED;
|
||||||
|
|
||||||
ctx.engine.pushMsg(true, ctx, new NativeFunction((_ctx, _thisArg, _args) -> {
|
ctx.engine.pushMsg(true, ctx, new NativeFunction((_ctx, _thisArg, _args) -> {
|
||||||
for (var handle : handles) {
|
for (var handle : handles) {
|
||||||
handle.fulfilled.call(handle.ctx, null, val);
|
handle.fulfilled.call(handle.ctx, null, val);
|
||||||
}
|
}
|
||||||
handles = null;
|
handles = null;
|
||||||
return null;
|
return null;
|
||||||
}), null);
|
}), null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (EngineException err) {
|
catch (EngineException err) {
|
||||||
this.reject(ctx, err.value);
|
this.reject(ctx, err.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public synchronized void reject(Context ctx, Object val) {
|
public synchronized void reject(Context ctx, Object val) {
|
||||||
if (this.state != STATE_PENDING) return;
|
if (this.state != STATE_PENDING) return;
|
||||||
|
|
||||||
if (val instanceof PromiseLib) ((PromiseLib)val).handle(ctx,
|
if (val instanceof PromiseLib) ((PromiseLib)val).handle(ctx,
|
||||||
new NativeFunction(null, (e, th, a) -> { this.reject(ctx, a[0]); return null; }),
|
new NativeFunction(null, (e, th, a) -> { this.reject(ctx, a[0]); return null; }),
|
||||||
new NativeFunction(null, (e, th, a) -> { this.reject(ctx, a[0]); return null; })
|
new NativeFunction(null, (e, th, a) -> { this.reject(ctx, a[0]); return null; })
|
||||||
);
|
);
|
||||||
else {
|
else {
|
||||||
Object then;
|
Object then;
|
||||||
try { then = Values.getMember(ctx, val, "then"); }
|
try { then = Values.getMember(ctx, val, "then"); }
|
||||||
catch (IllegalArgumentException e) { then = null; }
|
catch (IllegalArgumentException e) { then = null; }
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (then instanceof FunctionValue) ((FunctionValue)then).call(ctx, val,
|
if (then instanceof FunctionValue) ((FunctionValue)then).call(ctx, val,
|
||||||
new NativeFunction((e, _thisArg, a) -> { this.reject(ctx, a.length > 0 ? a[0] : null); return null; }),
|
new NativeFunction((e, _thisArg, a) -> { this.reject(ctx, a.length > 0 ? a[0] : null); return null; }),
|
||||||
new NativeFunction((e, _thisArg, a) -> { this.reject(ctx, a.length > 0 ? a[0] : null); return null; })
|
new NativeFunction((e, _thisArg, a) -> { this.reject(ctx, a.length > 0 ? a[0] : null); return null; })
|
||||||
);
|
);
|
||||||
else {
|
else {
|
||||||
this.val = val;
|
this.val = val;
|
||||||
this.state = STATE_REJECTED;
|
this.state = STATE_REJECTED;
|
||||||
|
|
||||||
ctx.engine.pushMsg(true, ctx, new NativeFunction((_ctx, _thisArg, _args) -> {
|
ctx.engine.pushMsg(true, ctx, new NativeFunction((_ctx, _thisArg, _args) -> {
|
||||||
for (var handle : handles) handle.rejected.call(handle.ctx, null, val);
|
for (var handle : handles) handle.rejected.call(handle.ctx, null, val);
|
||||||
if (!handled) {
|
if (!handled) {
|
||||||
Values.printError(new EngineException(val).setCtx(ctx.environment(), ctx.engine), "(in promise)");
|
Values.printError(new EngineException(val).setCtx(ctx.environment(), ctx.engine), "(in promise)");
|
||||||
}
|
}
|
||||||
handles = null;
|
handles = null;
|
||||||
return null;
|
return null;
|
||||||
}), null);
|
}), null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (EngineException err) {
|
catch (EngineException err) {
|
||||||
this.reject(ctx, err.value);
|
this.reject(ctx, err.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handle(Context ctx, FunctionValue fulfill, FunctionValue reject) {
|
private void handle(Context ctx, FunctionValue fulfill, FunctionValue reject) {
|
||||||
if (state == STATE_FULFILLED) ctx.engine.pushMsg(true, ctx, fulfill, null, val);
|
if (state == STATE_FULFILLED) ctx.engine.pushMsg(true, ctx, fulfill, null, val);
|
||||||
else if (state == STATE_REJECTED) {
|
else if (state == STATE_REJECTED) {
|
||||||
ctx.engine.pushMsg(true, ctx, reject, null, val);
|
ctx.engine.pushMsg(true, ctx, reject, null, val);
|
||||||
handled = true;
|
handled = true;
|
||||||
}
|
}
|
||||||
else handles.add(new Handle(ctx, fulfill, reject));
|
else handles.add(new Handle(ctx, fulfill, reject));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override @Native public String toString() {
|
@Override @Native public String toString() {
|
||||||
if (state == STATE_PENDING) return "Promise (pending)";
|
if (state == STATE_PENDING) return "Promise (pending)";
|
||||||
else if (state == STATE_FULFILLED) return "Promise (fulfilled)";
|
else if (state == STATE_FULFILLED) return "Promise (fulfilled)";
|
||||||
else return "Promise (rejected)";
|
else return "Promise (rejected)";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* NOT THREAD SAFE - must be called from the engine executor thread
|
* NOT THREAD SAFE - must be called from the engine executor thread
|
||||||
*/
|
*/
|
||||||
@Native public PromiseLib(Context ctx, FunctionValue func) {
|
@Native public PromiseLib(Context ctx, FunctionValue func) {
|
||||||
if (!(func instanceof FunctionValue)) throw EngineException.ofType("A function must be passed to the promise constructor.");
|
if (!(func instanceof FunctionValue)) throw EngineException.ofType("A function must be passed to the promise constructor.");
|
||||||
try {
|
try {
|
||||||
func.call(
|
func.call(
|
||||||
ctx, null,
|
ctx, null,
|
||||||
new NativeFunction(null, (e, th, args) -> {
|
new NativeFunction(null, (e, th, args) -> {
|
||||||
fulfill(e, args.length > 0 ? args[0] : null);
|
fulfill(e, args.length > 0 ? args[0] : null);
|
||||||
return null;
|
return null;
|
||||||
}),
|
}),
|
||||||
new NativeFunction(null, (e, th, args) -> {
|
new NativeFunction(null, (e, th, args) -> {
|
||||||
reject(e, args.length > 0 ? args[0] : null);
|
reject(e, args.length > 0 ? args[0] : null);
|
||||||
return null;
|
return null;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
catch (EngineException e) {
|
catch (EngineException e) {
|
||||||
reject(ctx, e.value);
|
reject(ctx, e.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private PromiseLib(int state, Object val) {
|
private PromiseLib(int state, Object val) {
|
||||||
this.state = state;
|
this.state = state;
|
||||||
this.val = val;
|
this.val = val;
|
||||||
}
|
}
|
||||||
public PromiseLib() {
|
public PromiseLib() {
|
||||||
this(STATE_PENDING, null);
|
this(STATE_PENDING, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PromiseLib await(Context ctx, PromiseRunner runner) {
|
public static PromiseLib await(Context ctx, PromiseRunner runner) {
|
||||||
var res = new PromiseLib();
|
var res = new PromiseLib();
|
||||||
|
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
try {
|
try {
|
||||||
res.fulfill(ctx, runner.run());
|
res.fulfill(ctx, runner.run());
|
||||||
}
|
}
|
||||||
catch (EngineException e) {
|
catch (EngineException e) {
|
||||||
res.reject(ctx, e.value);
|
res.reject(ctx, e.value);
|
||||||
}
|
}
|
||||||
}, "Promisifier").start();
|
}, "Promisifier").start();
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,60 +1,60 @@
|
|||||||
package me.topchetoeu.jscript.lib;
|
package me.topchetoeu.jscript.lib;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.engine.Context;
|
import me.topchetoeu.jscript.engine.Context;
|
||||||
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
import me.topchetoeu.jscript.engine.values.ArrayValue;
|
||||||
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
import me.topchetoeu.jscript.engine.values.FunctionValue;
|
||||||
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||||
import me.topchetoeu.jscript.engine.values.Values;
|
import me.topchetoeu.jscript.engine.values.Values;
|
||||||
import me.topchetoeu.jscript.interop.Native;
|
import me.topchetoeu.jscript.interop.Native;
|
||||||
import me.topchetoeu.jscript.interop.NativeGetter;
|
import me.topchetoeu.jscript.interop.NativeGetter;
|
||||||
|
|
||||||
@Native("Set") public class SetLib {
|
@Native("Set") public class SetLib {
|
||||||
private LinkedHashSet<Object> set = new LinkedHashSet<>();
|
private LinkedHashSet<Object> set = new LinkedHashSet<>();
|
||||||
|
|
||||||
@Native("@@Symbol.typeName") public final String name = "Set";
|
@Native("@@Symbol.typeName") public final String name = "Set";
|
||||||
@Native("@@Symbol.iterator") public ObjectValue iterator(Context ctx) {
|
@Native("@@Symbol.iterator") public ObjectValue iterator(Context ctx) {
|
||||||
return this.values(ctx);
|
return this.values(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Native public ObjectValue entries(Context ctx) {
|
@Native public ObjectValue entries(Context ctx) {
|
||||||
return ArrayValue.of(ctx, set.stream().map(v -> new ArrayValue(ctx, v, v)).collect(Collectors.toList()));
|
return ArrayValue.of(ctx, set.stream().map(v -> new ArrayValue(ctx, v, v)).collect(Collectors.toList()));
|
||||||
}
|
}
|
||||||
@Native public ObjectValue keys(Context ctx) {
|
@Native public ObjectValue keys(Context ctx) {
|
||||||
return ArrayValue.of(ctx, set);
|
return ArrayValue.of(ctx, set);
|
||||||
}
|
}
|
||||||
@Native public ObjectValue values(Context ctx) {
|
@Native public ObjectValue values(Context ctx) {
|
||||||
return ArrayValue.of(ctx, set);
|
return ArrayValue.of(ctx, set);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Native public Object add(Object key) {
|
@Native public Object add(Object key) {
|
||||||
return set.add(key);
|
return set.add(key);
|
||||||
}
|
}
|
||||||
@Native public boolean delete(Object key) {
|
@Native public boolean delete(Object key) {
|
||||||
return set.remove(key);
|
return set.remove(key);
|
||||||
}
|
}
|
||||||
@Native public boolean has(Object key) {
|
@Native public boolean has(Object key) {
|
||||||
return set.contains(key);
|
return set.contains(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Native public void clear() {
|
@Native public void clear() {
|
||||||
set.clear();
|
set.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NativeGetter public int size() {
|
@NativeGetter public int size() {
|
||||||
return set.size();
|
return set.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Native public void forEach(Context ctx, FunctionValue func, Object thisArg) {
|
@Native public void forEach(Context ctx, FunctionValue func, Object thisArg) {
|
||||||
var keys = new ArrayList<>(set);
|
var keys = new ArrayList<>(set);
|
||||||
|
|
||||||
for (var el : keys) func.call(ctx, thisArg, el, el, this);
|
for (var el : keys) func.call(ctx, thisArg, el, el, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Native public SetLib(Context ctx, Object iterable) {
|
@Native public SetLib(Context ctx, Object iterable) {
|
||||||
for (var el : Values.fromJSIterator(ctx, iterable)) add(el);
|
for (var el : Values.fromJSIterator(ctx, iterable)) add(el);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,113 +1,113 @@
|
|||||||
package me.topchetoeu.jscript.parsing;
|
package me.topchetoeu.jscript.parsing;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.engine.Operation;
|
import me.topchetoeu.jscript.engine.Operation;
|
||||||
|
|
||||||
public enum Operator {
|
public enum Operator {
|
||||||
MULTIPLY("*", Operation.MULTIPLY, 13),
|
MULTIPLY("*", Operation.MULTIPLY, 13),
|
||||||
DIVIDE("/", Operation.DIVIDE, 12),
|
DIVIDE("/", Operation.DIVIDE, 12),
|
||||||
MODULO("%", Operation.MODULO, 12),
|
MODULO("%", Operation.MODULO, 12),
|
||||||
SUBTRACT("-", Operation.SUBTRACT, 11),
|
SUBTRACT("-", Operation.SUBTRACT, 11),
|
||||||
ADD("+", Operation.ADD, 11),
|
ADD("+", Operation.ADD, 11),
|
||||||
SHIFT_RIGHT(">>", Operation.SHIFT_RIGHT, 10),
|
SHIFT_RIGHT(">>", Operation.SHIFT_RIGHT, 10),
|
||||||
SHIFT_LEFT("<<", Operation.SHIFT_LEFT, 10),
|
SHIFT_LEFT("<<", Operation.SHIFT_LEFT, 10),
|
||||||
USHIFT_RIGHT(">>>", Operation.USHIFT_RIGHT, 10),
|
USHIFT_RIGHT(">>>", Operation.USHIFT_RIGHT, 10),
|
||||||
GREATER(">", Operation.GREATER, 9),
|
GREATER(">", Operation.GREATER, 9),
|
||||||
LESS("<", Operation.LESS, 9),
|
LESS("<", Operation.LESS, 9),
|
||||||
GREATER_EQUALS(">=", Operation.GREATER_EQUALS, 9),
|
GREATER_EQUALS(">=", Operation.GREATER_EQUALS, 9),
|
||||||
LESS_EQUALS("<=", Operation.LESS_EQUALS, 9),
|
LESS_EQUALS("<=", Operation.LESS_EQUALS, 9),
|
||||||
NOT_EQUALS("!=", Operation.LOOSE_NOT_EQUALS, 8),
|
NOT_EQUALS("!=", Operation.LOOSE_NOT_EQUALS, 8),
|
||||||
LOOSE_NOT_EQUALS("!==", Operation.NOT_EQUALS, 8),
|
LOOSE_NOT_EQUALS("!==", Operation.NOT_EQUALS, 8),
|
||||||
EQUALS("==", Operation.LOOSE_EQUALS, 8),
|
EQUALS("==", Operation.LOOSE_EQUALS, 8),
|
||||||
LOOSE_EQUALS("===", Operation.EQUALS, 8),
|
LOOSE_EQUALS("===", Operation.EQUALS, 8),
|
||||||
AND("&", Operation.AND, 7),
|
AND("&", Operation.AND, 7),
|
||||||
XOR("^", Operation.XOR, 6),
|
XOR("^", Operation.XOR, 6),
|
||||||
OR("|", Operation.OR, 5),
|
OR("|", Operation.OR, 5),
|
||||||
LAZY_AND("&&", 4),
|
LAZY_AND("&&", 4),
|
||||||
LAZY_OR("||", 3),
|
LAZY_OR("||", 3),
|
||||||
ASSIGN_SHIFT_LEFT("<<=", 2, true),
|
ASSIGN_SHIFT_LEFT("<<=", 2, true),
|
||||||
ASSIGN_SHIFT_RIGHT(">>=", 2, true),
|
ASSIGN_SHIFT_RIGHT(">>=", 2, true),
|
||||||
ASSIGN_USHIFT_RIGHT(">>>=", 2, true),
|
ASSIGN_USHIFT_RIGHT(">>>=", 2, true),
|
||||||
ASSIGN_AND("&=", 2, true),
|
ASSIGN_AND("&=", 2, true),
|
||||||
ASSIGN_OR("|=", 2, true),
|
ASSIGN_OR("|=", 2, true),
|
||||||
ASSIGN_XOR("^=", 2, true),
|
ASSIGN_XOR("^=", 2, true),
|
||||||
ASSIGN_MODULO("%=", 2, true),
|
ASSIGN_MODULO("%=", 2, true),
|
||||||
ASSIGN_DIVIDE("/=", 2, true),
|
ASSIGN_DIVIDE("/=", 2, true),
|
||||||
ASSIGN_MULTIPLY("*=", 2, true),
|
ASSIGN_MULTIPLY("*=", 2, true),
|
||||||
ASSIGN_SUBTRACT("-=", 2, true),
|
ASSIGN_SUBTRACT("-=", 2, true),
|
||||||
ASSIGN_ADD("+=", 2, true),
|
ASSIGN_ADD("+=", 2, true),
|
||||||
ASSIGN("=", 2, true),
|
ASSIGN("=", 2, true),
|
||||||
SEMICOLON(";"),
|
SEMICOLON(";"),
|
||||||
COLON(":"),
|
COLON(":"),
|
||||||
PAREN_OPEN("("),
|
PAREN_OPEN("("),
|
||||||
PAREN_CLOSE(")"),
|
PAREN_CLOSE(")"),
|
||||||
BRACKET_OPEN("["),
|
BRACKET_OPEN("["),
|
||||||
BRACKET_CLOSE("]"),
|
BRACKET_CLOSE("]"),
|
||||||
BRACE_OPEN("{"),
|
BRACE_OPEN("{"),
|
||||||
BRACE_CLOSE("}"),
|
BRACE_CLOSE("}"),
|
||||||
DOT("."),
|
DOT("."),
|
||||||
COMMA(","),
|
COMMA(","),
|
||||||
NOT("!"),
|
NOT("!"),
|
||||||
QUESTION("?"),
|
QUESTION("?"),
|
||||||
INVERSE("~"),
|
INVERSE("~"),
|
||||||
INCREASE("++"),
|
INCREASE("++"),
|
||||||
DECREASE("--");
|
DECREASE("--");
|
||||||
|
|
||||||
public final String value;
|
public final String value;
|
||||||
public final Operation operation;
|
public final Operation operation;
|
||||||
public final int precedence;
|
public final int precedence;
|
||||||
public final boolean reverse;
|
public final boolean reverse;
|
||||||
private static final Map<String, Operator> ops = new HashMap<>();
|
private static final Map<String, Operator> ops = new HashMap<>();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
for (var el : Operator.values()) {
|
for (var el : Operator.values()) {
|
||||||
ops.put(el.value, el);
|
ops.put(el.value, el);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isAssign() { return precedence == 2; }
|
public boolean isAssign() { return precedence == 2; }
|
||||||
|
|
||||||
public static Operator parse(String val) {
|
public static Operator parse(String val) {
|
||||||
return ops.get(val);
|
return ops.get(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Operator() {
|
private Operator() {
|
||||||
this.value = null;
|
this.value = null;
|
||||||
this.operation = null;
|
this.operation = null;
|
||||||
this.precedence = -1;
|
this.precedence = -1;
|
||||||
this.reverse = false;
|
this.reverse = false;
|
||||||
}
|
}
|
||||||
private Operator(String value) {
|
private Operator(String value) {
|
||||||
this. value = value;
|
this. value = value;
|
||||||
this.operation = null;
|
this.operation = null;
|
||||||
this.precedence = -1;
|
this.precedence = -1;
|
||||||
this.reverse = false;
|
this.reverse = false;
|
||||||
}
|
}
|
||||||
private Operator(String value, int precedence) {
|
private Operator(String value, int precedence) {
|
||||||
this. value = value;
|
this. value = value;
|
||||||
this.operation = null;
|
this.operation = null;
|
||||||
this.precedence = precedence;
|
this.precedence = precedence;
|
||||||
this.reverse = false;
|
this.reverse = false;
|
||||||
}
|
}
|
||||||
private Operator(String value, int precedence, boolean reverse) {
|
private Operator(String value, int precedence, boolean reverse) {
|
||||||
this. value = value;
|
this. value = value;
|
||||||
this.operation = null;
|
this.operation = null;
|
||||||
this.precedence = precedence;
|
this.precedence = precedence;
|
||||||
this.reverse = reverse;
|
this.reverse = reverse;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Operator(String value, Operation funcName, int precedence) {
|
private Operator(String value, Operation funcName, int precedence) {
|
||||||
this. value = value;
|
this. value = value;
|
||||||
this.operation = funcName;
|
this.operation = funcName;
|
||||||
this.precedence = precedence;
|
this.precedence = precedence;
|
||||||
this.reverse = false;
|
this.reverse = false;
|
||||||
}
|
}
|
||||||
private Operator(String value, Operation funcName, int precedence, boolean reverse) {
|
private Operator(String value, Operation funcName, int precedence, boolean reverse) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
this.operation = funcName;
|
this.operation = funcName;
|
||||||
this.precedence = precedence;
|
this.precedence = precedence;
|
||||||
this.reverse = reverse;
|
this.reverse = reverse;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,97 +1,97 @@
|
|||||||
package me.topchetoeu.jscript.parsing;
|
package me.topchetoeu.jscript.parsing;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.parsing.Parsing.Parser;
|
import me.topchetoeu.jscript.parsing.Parsing.Parser;
|
||||||
|
|
||||||
public class ParseRes<T> {
|
public class ParseRes<T> {
|
||||||
public static enum State {
|
public static enum State {
|
||||||
SUCCESS,
|
SUCCESS,
|
||||||
FAILED,
|
FAILED,
|
||||||
ERROR;
|
ERROR;
|
||||||
|
|
||||||
public boolean isSuccess() { return this == SUCCESS; }
|
public boolean isSuccess() { return this == SUCCESS; }
|
||||||
public boolean isFailed() { return this == FAILED; }
|
public boolean isFailed() { return this == FAILED; }
|
||||||
public boolean isError() { return this == ERROR; }
|
public boolean isError() { return this == ERROR; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public final ParseRes.State state;
|
public final ParseRes.State state;
|
||||||
public final String error;
|
public final String error;
|
||||||
public final T result;
|
public final T result;
|
||||||
public final int n;
|
public final int n;
|
||||||
|
|
||||||
private ParseRes(ParseRes.State state, String error, T result, int readN) {
|
private ParseRes(ParseRes.State state, String error, T result, int readN) {
|
||||||
this.result = result;
|
this.result = result;
|
||||||
this.n = readN;
|
this.n = readN;
|
||||||
this.state = state;
|
this.state = state;
|
||||||
this.error = error;
|
this.error = error;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ParseRes<T> setN(int i) {
|
public ParseRes<T> setN(int i) {
|
||||||
if (!state.isSuccess()) return this;
|
if (!state.isSuccess()) return this;
|
||||||
return new ParseRes<>(state, null, result, i);
|
return new ParseRes<>(state, null, result, i);
|
||||||
}
|
}
|
||||||
public ParseRes<T> addN(int i) {
|
public ParseRes<T> addN(int i) {
|
||||||
if (!state.isSuccess()) return this;
|
if (!state.isSuccess()) return this;
|
||||||
return new ParseRes<>(state, null, result, this.n + i);
|
return new ParseRes<>(state, null, result, this.n + i);
|
||||||
}
|
}
|
||||||
public <T2> ParseRes<T2> transform() {
|
public <T2> ParseRes<T2> transform() {
|
||||||
if (isSuccess()) throw new RuntimeException("Can't transform a ParseRes that hasn't failed.");
|
if (isSuccess()) throw new RuntimeException("Can't transform a ParseRes that hasn't failed.");
|
||||||
return new ParseRes<>(state, error, null, 0);
|
return new ParseRes<>(state, error, null, 0);
|
||||||
}
|
}
|
||||||
public TestRes toTest() {
|
public TestRes toTest() {
|
||||||
if (isSuccess()) return TestRes.res(n);
|
if (isSuccess()) return TestRes.res(n);
|
||||||
else if (isError()) return TestRes.error(null, error);
|
else if (isError()) return TestRes.error(null, error);
|
||||||
else return TestRes.failed();
|
else return TestRes.failed();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isSuccess() { return state.isSuccess(); }
|
public boolean isSuccess() { return state.isSuccess(); }
|
||||||
public boolean isFailed() { return state.isFailed(); }
|
public boolean isFailed() { return state.isFailed(); }
|
||||||
public boolean isError() { return state.isError(); }
|
public boolean isError() { return state.isError(); }
|
||||||
|
|
||||||
public static <T> ParseRes<T> failed() {
|
public static <T> ParseRes<T> failed() {
|
||||||
return new ParseRes<T>(State.FAILED, null, null, 0);
|
return new ParseRes<T>(State.FAILED, null, null, 0);
|
||||||
}
|
}
|
||||||
public static <T> ParseRes<T> error(Location loc, String error) {
|
public static <T> ParseRes<T> error(Location loc, String error) {
|
||||||
if (loc != null) error = loc + ": " + error;
|
if (loc != null) error = loc + ": " + error;
|
||||||
return new ParseRes<>(State.ERROR, error, null, 0);
|
return new ParseRes<>(State.ERROR, error, null, 0);
|
||||||
}
|
}
|
||||||
public static <T> ParseRes<T> error(Location loc, String error, ParseRes<?> other) {
|
public static <T> ParseRes<T> error(Location loc, String error, ParseRes<?> other) {
|
||||||
if (loc != null) error = loc + ": " + error;
|
if (loc != null) error = loc + ": " + error;
|
||||||
if (!other.isError()) return new ParseRes<>(State.ERROR, error, null, 0);
|
if (!other.isError()) return new ParseRes<>(State.ERROR, error, null, 0);
|
||||||
return new ParseRes<>(State.ERROR, other.error, null, 0);
|
return new ParseRes<>(State.ERROR, other.error, null, 0);
|
||||||
}
|
}
|
||||||
public static <T> ParseRes<T> res(T val, int i) {
|
public static <T> ParseRes<T> res(T val, int i) {
|
||||||
return new ParseRes<>(State.SUCCESS, null, val, i);
|
return new ParseRes<>(State.SUCCESS, null, val, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SafeVarargs
|
@SafeVarargs
|
||||||
public static <T> ParseRes<? extends T> any(ParseRes<? extends T> ...parsers) {
|
public static <T> ParseRes<? extends T> any(ParseRes<? extends T> ...parsers) {
|
||||||
ParseRes<? extends T> best = null;
|
ParseRes<? extends T> best = null;
|
||||||
ParseRes<? extends T> error = ParseRes.failed();
|
ParseRes<? extends T> error = ParseRes.failed();
|
||||||
|
|
||||||
for (var parser : parsers) {
|
for (var parser : parsers) {
|
||||||
if (parser.isSuccess()) {
|
if (parser.isSuccess()) {
|
||||||
if (best == null || best.n < parser.n) best = parser;
|
if (best == null || best.n < parser.n) best = parser;
|
||||||
}
|
}
|
||||||
else if (parser.isError() && error.isFailed()) error = parser.transform();
|
else if (parser.isError() && error.isFailed()) error = parser.transform();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (best != null) return best;
|
if (best != null) return best;
|
||||||
else return error;
|
else return error;
|
||||||
}
|
}
|
||||||
@SafeVarargs
|
@SafeVarargs
|
||||||
public static <T> ParseRes<? extends T> first(String filename, List<Token> tokens, Map<String, Parser<T>> named, Parser<? extends T> ...parsers) {
|
public static <T> ParseRes<? extends T> first(String filename, List<Token> tokens, Map<String, Parser<T>> named, Parser<? extends T> ...parsers) {
|
||||||
ParseRes<? extends T> error = ParseRes.failed();
|
ParseRes<? extends T> error = ParseRes.failed();
|
||||||
|
|
||||||
for (var parser : parsers) {
|
for (var parser : parsers) {
|
||||||
var res = parser.parse(null, tokens, 0);
|
var res = parser.parse(null, tokens, 0);
|
||||||
if (res.isSuccess()) return res;
|
if (res.isSuccess()) return res;
|
||||||
else if (res.isError() && error.isFailed()) error = res.transform();
|
else if (res.isError() && error.isFailed()) error = res.transform();
|
||||||
}
|
}
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
}
|
}
|
File diff suppressed because it is too large
Load Diff
@ -1,15 +1,15 @@
|
|||||||
package me.topchetoeu.jscript.parsing;
|
package me.topchetoeu.jscript.parsing;
|
||||||
|
|
||||||
public class RawToken {
|
public class RawToken {
|
||||||
public final String value;
|
public final String value;
|
||||||
public final TokenType type;
|
public final TokenType type;
|
||||||
public final int line;
|
public final int line;
|
||||||
public final int start;
|
public final int start;
|
||||||
|
|
||||||
public RawToken(String value, TokenType type, int line, int start) {
|
public RawToken(String value, TokenType type, int line, int start) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.line = line;
|
this.line = line;
|
||||||
this.start = start;
|
this.start = start;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,45 +1,45 @@
|
|||||||
package me.topchetoeu.jscript.parsing;
|
package me.topchetoeu.jscript.parsing;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.Location;
|
import me.topchetoeu.jscript.Location;
|
||||||
import me.topchetoeu.jscript.parsing.ParseRes.State;
|
import me.topchetoeu.jscript.parsing.ParseRes.State;
|
||||||
|
|
||||||
public class TestRes {
|
public class TestRes {
|
||||||
public final State state;
|
public final State state;
|
||||||
public final String error;
|
public final String error;
|
||||||
public final int i;
|
public final int i;
|
||||||
|
|
||||||
private TestRes(ParseRes.State state, String error, int i) {
|
private TestRes(ParseRes.State state, String error, int i) {
|
||||||
this.i = i;
|
this.i = i;
|
||||||
this.state = state;
|
this.state = state;
|
||||||
this.error = error;
|
this.error = error;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TestRes add(int n) {
|
public TestRes add(int n) {
|
||||||
return new TestRes(state, null, this.i + n);
|
return new TestRes(state, null, this.i + n);
|
||||||
}
|
}
|
||||||
public <T> ParseRes<T> transform() {
|
public <T> ParseRes<T> transform() {
|
||||||
if (isSuccess()) throw new RuntimeException("Can't transform a TestRes that hasn't failed.");
|
if (isSuccess()) throw new RuntimeException("Can't transform a TestRes that hasn't failed.");
|
||||||
else if (isError()) return ParseRes.error(null, error);
|
else if (isError()) return ParseRes.error(null, error);
|
||||||
else return ParseRes.failed();
|
else return ParseRes.failed();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isSuccess() { return state.isSuccess(); }
|
public boolean isSuccess() { return state.isSuccess(); }
|
||||||
public boolean isFailed() { return state.isFailed(); }
|
public boolean isFailed() { return state.isFailed(); }
|
||||||
public boolean isError() { return state.isError(); }
|
public boolean isError() { return state.isError(); }
|
||||||
|
|
||||||
public static TestRes failed() {
|
public static TestRes failed() {
|
||||||
return new TestRes(State.FAILED, null, 0);
|
return new TestRes(State.FAILED, null, 0);
|
||||||
}
|
}
|
||||||
public static TestRes error(Location loc, String error) {
|
public static TestRes error(Location loc, String error) {
|
||||||
if (loc != null) error = loc + ": " + error;
|
if (loc != null) error = loc + ": " + error;
|
||||||
return new TestRes(State.ERROR, error, 0);
|
return new TestRes(State.ERROR, error, 0);
|
||||||
}
|
}
|
||||||
public static TestRes error(Location loc, String error, TestRes other) {
|
public static TestRes error(Location loc, String error, TestRes other) {
|
||||||
if (loc != null) error = loc + ": " + error;
|
if (loc != null) error = loc + ": " + error;
|
||||||
if (!other.isError()) return new TestRes(State.ERROR, error, 0);
|
if (!other.isError()) return new TestRes(State.ERROR, error, 0);
|
||||||
return new TestRes(State.ERROR, other.error, 0);
|
return new TestRes(State.ERROR, other.error, 0);
|
||||||
}
|
}
|
||||||
public static TestRes res(int i) {
|
public static TestRes res(int i) {
|
||||||
return new TestRes(State.SUCCESS, null, i);
|
return new TestRes(State.SUCCESS, null, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,55 +1,55 @@
|
|||||||
package me.topchetoeu.jscript.parsing;
|
package me.topchetoeu.jscript.parsing;
|
||||||
|
|
||||||
public class Token {
|
public class Token {
|
||||||
public final Object value;
|
public final Object value;
|
||||||
public final boolean isString;
|
public final boolean isString;
|
||||||
public final boolean isRegex;
|
public final boolean isRegex;
|
||||||
public final int line;
|
public final int line;
|
||||||
public final int start;
|
public final int start;
|
||||||
|
|
||||||
private Token(int line, int start, Object value, boolean isString, boolean isRegex) {
|
private Token(int line, int start, Object value, boolean isString, boolean isRegex) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
this.line = line;
|
this.line = line;
|
||||||
this.start = start;
|
this.start = start;
|
||||||
this.isString = isString;
|
this.isString = isString;
|
||||||
this.isRegex = isRegex;
|
this.isRegex = isRegex;
|
||||||
}
|
}
|
||||||
private Token(int line, int start, Object value) {
|
private Token(int line, int start, Object value) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
this.line = line;
|
this.line = line;
|
||||||
this.start = start;
|
this.start = start;
|
||||||
this.isString = false;
|
this.isString = false;
|
||||||
this.isRegex = false;
|
this.isRegex = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isString() { return isString; }
|
public boolean isString() { return isString; }
|
||||||
public boolean isRegex() { return isRegex; }
|
public boolean isRegex() { return isRegex; }
|
||||||
public boolean isNumber() { return value instanceof Number; }
|
public boolean isNumber() { return value instanceof Number; }
|
||||||
public boolean isIdentifier() { return !isString && !isRegex && value instanceof String; }
|
public boolean isIdentifier() { return !isString && !isRegex && value instanceof String; }
|
||||||
public boolean isOperator() { return value instanceof Operator; }
|
public boolean isOperator() { return value instanceof Operator; }
|
||||||
|
|
||||||
public boolean isIdentifier(String lit) { return !isString && !isRegex && value.equals(lit); }
|
public boolean isIdentifier(String lit) { return !isString && !isRegex && value.equals(lit); }
|
||||||
public boolean isOperator(Operator op) { return value.equals(op); }
|
public boolean isOperator(Operator op) { return value.equals(op); }
|
||||||
|
|
||||||
public String string() { return (String)value; }
|
public String string() { return (String)value; }
|
||||||
public String regex() { return (String)value; }
|
public String regex() { return (String)value; }
|
||||||
public double number() { return (double)value; }
|
public double number() { return (double)value; }
|
||||||
public String identifier() { return (String)value; }
|
public String identifier() { return (String)value; }
|
||||||
public Operator operator() { return (Operator)value; }
|
public Operator operator() { return (Operator)value; }
|
||||||
|
|
||||||
public static Token regex(int line, int start, String val) {
|
public static Token regex(int line, int start, String val) {
|
||||||
return new Token(line, start, val, false, true);
|
return new Token(line, start, val, false, true);
|
||||||
}
|
}
|
||||||
public static Token string(int line, int start, String val) {
|
public static Token string(int line, int start, String val) {
|
||||||
return new Token(line, start, val, true, false);
|
return new Token(line, start, val, true, false);
|
||||||
}
|
}
|
||||||
public static Token number(int line, int start, double val) {
|
public static Token number(int line, int start, double val) {
|
||||||
return new Token(line, start, val);
|
return new Token(line, start, val);
|
||||||
}
|
}
|
||||||
public static Token identifier(int line, int start, String val) {
|
public static Token identifier(int line, int start, String val) {
|
||||||
return new Token(line, start, val);
|
return new Token(line, start, val);
|
||||||
}
|
}
|
||||||
public static Token operator(int line, int start, Operator val) {
|
public static Token operator(int line, int start, Operator val) {
|
||||||
return new Token(line, start, val);
|
return new Token(line, start, val);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,9 +1,9 @@
|
|||||||
package me.topchetoeu.jscript.parsing;
|
package me.topchetoeu.jscript.parsing;
|
||||||
|
|
||||||
enum TokenType {
|
enum TokenType {
|
||||||
REGEX,
|
REGEX,
|
||||||
STRING,
|
STRING,
|
||||||
NUMBER,
|
NUMBER,
|
||||||
LITERAL,
|
LITERAL,
|
||||||
OPERATOR,
|
OPERATOR,
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user