Classes #29
@ -64,8 +64,10 @@ public class Instruction {
|
||||
GLOB_SET(0x61),
|
||||
GLOB_DEF(0x62),
|
||||
|
||||
STACK_ALLOC(0x70),
|
||||
STACK_REALLOC(0x71);
|
||||
// CAP_INIT(0x70),
|
||||
VAR_INIT(0x71),
|
||||
CAP_FREE(0x72),
|
||||
VAR_FREE(0x73);
|
||||
|
||||
private static final HashMap<Integer, Type> types = new HashMap<>();
|
||||
public final int numeric;
|
||||
@ -409,11 +411,11 @@ public class Instruction {
|
||||
return new Instruction(Type.DUP, count, offset);
|
||||
}
|
||||
|
||||
public static Instruction storeVar(int i) {
|
||||
return new Instruction(Type.STORE_VAR, i, false);
|
||||
}
|
||||
public static Instruction storeVar(int i, boolean keep) {
|
||||
return new Instruction(Type.STORE_VAR, i, keep);
|
||||
// public static Instruction storeVar(int i) {
|
||||
// return new Instruction(Type.STORE_VAR, i, false);
|
||||
// }
|
||||
public static Instruction storeVar(int i, boolean keep, boolean initialize) {
|
||||
return new Instruction(Type.STORE_VAR, i, keep, initialize);
|
||||
}
|
||||
|
||||
public static Instruction storeMember() {
|
||||
@ -463,12 +465,21 @@ public class Instruction {
|
||||
return new Instruction(Type.OPERATION, op);
|
||||
}
|
||||
|
||||
public static Instruction stackAlloc(int start, int n) {
|
||||
return new Instruction(Type.STACK_ALLOC, start, start + n);
|
||||
public static Instruction capFree(int i) {
|
||||
return new Instruction(Type.CAP_FREE, i);
|
||||
}
|
||||
public static Instruction stackRealloc(int start, int n) {
|
||||
return new Instruction(Type.STACK_REALLOC, start, start + n);
|
||||
public static Instruction varFree(int i) {
|
||||
return new Instruction(Type.VAR_FREE, i);
|
||||
}
|
||||
public static Instruction varInit(int i, boolean force) {
|
||||
return new Instruction(Type.VAR_INIT, i, force);
|
||||
}
|
||||
// public static Instruction stackAlloc(int start, int n) {
|
||||
// return new Instruction(Type.STACK_ALLOC, start, start + n);
|
||||
// }
|
||||
// public static Instruction stackRealloc(int start, int n) {
|
||||
// return new Instruction(Type.STACK_REALLOC, start, start + n);
|
||||
// }
|
||||
|
||||
@Override public String toString() {
|
||||
var res = type.toString();
|
||||
|
158
src/main/java/me/topchetoeu/jscript/compilation/ClassNode.java
Normal file
158
src/main/java/me/topchetoeu/jscript/compilation/ClassNode.java
Normal file
@ -0,0 +1,158 @@
|
||||
package me.topchetoeu.jscript.compilation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
||||
import me.topchetoeu.jscript.common.parsing.Location;
|
||||
import me.topchetoeu.jscript.common.parsing.ParseRes;
|
||||
import me.topchetoeu.jscript.common.parsing.Parsing;
|
||||
import me.topchetoeu.jscript.common.parsing.Source;
|
||||
import me.topchetoeu.jscript.compilation.members.FieldMemberNode;
|
||||
import me.topchetoeu.jscript.compilation.members.MethodMemberNode;
|
||||
import me.topchetoeu.jscript.compilation.members.PropertyMemberNode;
|
||||
|
||||
public abstract class ClassNode extends FunctionNode {
|
||||
public static final class ClassBody {
|
||||
public final List<Node> staticMembers;
|
||||
public final List<FieldMemberNode> protoFields;
|
||||
public final List<Node> protoMembers;
|
||||
public final Parameters constructorParameters;
|
||||
public final CompoundNode constructorBody;
|
||||
|
||||
public ClassBody(
|
||||
List<Node> staticMembers, List<FieldMemberNode> protoFields, List<Node> protoMembers,
|
||||
Parameters constructorParameters, CompoundNode constructorBody
|
||||
) {
|
||||
this.staticMembers = staticMembers;
|
||||
this.protoFields = protoFields;
|
||||
this.protoMembers = protoMembers;
|
||||
this.constructorParameters = constructorParameters;
|
||||
this.constructorBody = constructorBody;
|
||||
}
|
||||
}
|
||||
|
||||
public final ClassBody body;
|
||||
public final String name;
|
||||
|
||||
@Override public String name() { return name; }
|
||||
|
||||
public void compileStatic(CompileResult target) {
|
||||
for (var member : body.staticMembers) member.compile(target, true);
|
||||
}
|
||||
public void compilePrototype(CompileResult target) {
|
||||
if (body.protoMembers.size() > 0) {
|
||||
target.add(Instruction.dup());
|
||||
target.add(Instruction.loadMember("prototype"));
|
||||
|
||||
for (var i = 0; i < body.protoMembers.size() - 1; i++) {
|
||||
body.protoMembers.get(i).compile(target, true);
|
||||
}
|
||||
|
||||
body.protoMembers.get(body.protoMembers.size() - 1).compile(target, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override protected void compilePreBody(CompileResult target) {
|
||||
for (var member : body.protoFields) {
|
||||
target.add(Instruction.loadThis());
|
||||
member.compile(target, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) {
|
||||
var id = target.addChild(compileBody(target, name, null));
|
||||
target.add(_i -> Instruction.loadFunc(id, false, true, false, name, captures(id, target)));
|
||||
compileStatic(target);
|
||||
compilePrototype(target);
|
||||
}
|
||||
|
||||
public ClassNode(Location loc, Location end, String name, ClassBody body) {
|
||||
super(loc, end, body.constructorParameters, body.constructorBody);
|
||||
|
||||
this.name = name;
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
public static ParseRes<Node> parseMember(Source src, int i) {
|
||||
return ParseRes.first(src, i,
|
||||
PropertyMemberNode::parse,
|
||||
FieldMemberNode::parseClass,
|
||||
MethodMemberNode::parse
|
||||
);
|
||||
}
|
||||
|
||||
public static ParseRes<ClassBody> parseBody(Source src, int i) {
|
||||
var n = Parsing.skipEmpty(src, i);
|
||||
var loc = src.loc(i + n);
|
||||
|
||||
if (!src.is(i + n, "{")) return ParseRes.failed();
|
||||
n++;
|
||||
n += Parsing.skipEmpty(src, i + n);
|
||||
|
||||
var fields = new LinkedList<FieldMemberNode>();
|
||||
var members = new LinkedList<Node>();
|
||||
var statics = new LinkedList<Node>();
|
||||
|
||||
var params = new Parameters(new ArrayList<>());
|
||||
var body = new CompoundNode(loc, false);
|
||||
var hasConstr = false;
|
||||
|
||||
if (src.is(i + n, "}")) {
|
||||
n++;
|
||||
return ParseRes.res(new ClassBody(statics, fields, members, params, body), n);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
ParseRes<Node> prop = parseMember(src, i + n);
|
||||
|
||||
if (prop.isSuccess()) {
|
||||
n += prop.n;
|
||||
|
||||
if (prop.result instanceof FieldMemberNode field) fields.add(field);
|
||||
else if (prop.result instanceof MethodMemberNode method && method.name().equals("constructor")) {
|
||||
if (hasConstr) return ParseRes.error(loc, "A class may only have one constructor");
|
||||
|
||||
params = method.params;
|
||||
body = method.body;
|
||||
hasConstr = true;
|
||||
}
|
||||
else members.add(prop.result);
|
||||
}
|
||||
else if (Parsing.isIdentifier(src, i + n, "static")) {
|
||||
n += 6;
|
||||
|
||||
var staticProp = parseMember(src, i + n);
|
||||
if (!staticProp.isSuccess()) {
|
||||
if (prop.isError()) return prop.chainError();
|
||||
else return staticProp.chainError(src.loc(i + n), "Expected a member after 'static' keyword");
|
||||
}
|
||||
n += staticProp.n;
|
||||
|
||||
statics.add(staticProp.result);
|
||||
}
|
||||
else {
|
||||
var end = JavaScript.parseStatementEnd(src, i + n);
|
||||
if (end.isSuccess()) n += end.n;
|
||||
else return ParseRes.error(src.loc(i + n), "Expected a member, end of statement or a closing colon");
|
||||
}
|
||||
|
||||
n += Parsing.skipEmpty(src, i + n);
|
||||
|
||||
if (src.is(i + n, "}")) {
|
||||
n++;
|
||||
break;
|
||||
}
|
||||
else ParseRes.error(src.loc(i + n), "Expected a comma or a closing brace.");
|
||||
}
|
||||
|
||||
return ParseRes.res(new ClassBody(statics, fields, members, params, body), n);
|
||||
}
|
||||
|
||||
// public FunctionStatementNode(Location loc, Location end, Parameters params, CompoundNode body, String name) {
|
||||
// super(loc, end, params, body);
|
||||
// this.name = name;
|
||||
// }
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package me.topchetoeu.jscript.compilation;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
||||
import me.topchetoeu.jscript.common.parsing.Location;
|
||||
import me.topchetoeu.jscript.common.parsing.ParseRes;
|
||||
import me.topchetoeu.jscript.common.parsing.Parsing;
|
||||
import me.topchetoeu.jscript.common.parsing.Source;
|
||||
import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType;
|
||||
|
||||
public class ClassStatementNode extends ClassNode {
|
||||
@Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) {
|
||||
super.compile(target, pollute, name, bp);
|
||||
var i = target.scope.define(DeclarationType.LET, name(), loc());
|
||||
target.add(_i -> i.index().toInit());
|
||||
if (pollute) target.add(Instruction.pushUndefined());
|
||||
}
|
||||
|
||||
public ClassStatementNode(Location loc, Location end, String name, ClassBody body) {
|
||||
super(loc, end, name, body);
|
||||
}
|
||||
|
||||
public static ParseRes<ClassStatementNode> parse(Source src, int i) {
|
||||
var n = Parsing.skipEmpty(src, i);
|
||||
var loc = src.loc(i + n);
|
||||
|
||||
if (!Parsing.isIdentifier(src, i + n, "class")) return ParseRes.failed();
|
||||
n += 5;
|
||||
|
||||
var name = Parsing.parseIdentifier(src, i + n);
|
||||
if (!name.isSuccess()) return name.chainError(src.loc(i + n), "Expected a class name");
|
||||
n += name.n;
|
||||
|
||||
var body = parseBody(src, i + n);
|
||||
if (!body.isSuccess()) return body.chainError(src.loc(i + n), "Expected a class body");
|
||||
n += body.n;
|
||||
|
||||
return ParseRes.res(new ClassStatementNode(loc, src.loc(i + n), name.result, body.result), n);
|
||||
}
|
||||
}
|
@ -78,6 +78,31 @@ public final class CompileResult {
|
||||
setLocationAndDebug(instructions.size() - 1, loc, type);
|
||||
}
|
||||
|
||||
public void beginScope() {
|
||||
// for (var cap : scope.capturables()) {
|
||||
// add(_i -> Instruction.capInit(cap.index().index));
|
||||
// }
|
||||
}
|
||||
public void reallocScope() {
|
||||
for (var cap : scope.capturables()) {
|
||||
add(_i -> cap.index().toGet());
|
||||
add(_i -> Instruction.capFree(cap.index().index));
|
||||
add(_i -> cap.index().toInit());
|
||||
}
|
||||
|
||||
scope.end();
|
||||
}
|
||||
public void endScope() {
|
||||
for (var cap : scope.capturables()) {
|
||||
add(_i -> Instruction.capFree(cap.index().index));
|
||||
}
|
||||
for (var var : scope.locals()) {
|
||||
add(_i -> Instruction.varFree(var.index().index));
|
||||
}
|
||||
|
||||
scope.end();
|
||||
}
|
||||
|
||||
public int addChild(CompileResult res) {
|
||||
this.children.add(res);
|
||||
return this.children.size() - 1;
|
||||
|
@ -25,10 +25,7 @@ public class CompoundNode extends Node {
|
||||
List<Node> statements = new ArrayList<Node>();
|
||||
|
||||
var subtarget = hasScope ? target.subtarget() : target;
|
||||
if (hasScope) {
|
||||
subtarget.add(i -> Instruction.stackAlloc(subtarget.scope.capturablesOffset(), subtarget.scope.allocCount()));
|
||||
subtarget.scope.singleEntry = singleEntry;
|
||||
}
|
||||
if (hasScope) subtarget.beginScope();
|
||||
|
||||
for (var stm : this.statements) {
|
||||
if (stm instanceof FunctionStatementNode func) {
|
||||
@ -46,7 +43,7 @@ public class CompoundNode extends Node {
|
||||
else stm.compile(subtarget, polluted = pollute, BreakpointType.STEP_OVER);
|
||||
}
|
||||
|
||||
if (hasScope) subtarget.scope.end();
|
||||
if (hasScope) subtarget.endScope();
|
||||
|
||||
if (!polluted && pollute) {
|
||||
target.add(Instruction.pushUndefined());
|
||||
|
@ -22,6 +22,8 @@ public abstract class FunctionNode extends Node {
|
||||
return ((FunctionScope)target.children.get(id).scope).getCaptureIndices();
|
||||
}
|
||||
|
||||
protected void compilePreBody(CompileResult target) { }
|
||||
|
||||
public final CompileResult compileBody(Environment env, FunctionScope scope, boolean lastReturn, String _name, String selfName) {
|
||||
var name = this.name() != null ? this.name() : _name;
|
||||
|
||||
@ -30,6 +32,8 @@ public abstract class FunctionNode extends Node {
|
||||
.remove(LabelContext.CONTINUE_CTX);
|
||||
|
||||
return new CompileResult(env, scope, params.params.size(), target -> {
|
||||
compilePreBody(target);
|
||||
|
||||
if (params.params.size() > 0) {
|
||||
target.add(Instruction.loadArgs(true));
|
||||
if (params.params.size() > 1) target.add(Instruction.dup(params.params.size() - 1, 0));
|
||||
@ -50,7 +54,7 @@ public abstract class FunctionNode extends Node {
|
||||
var i = scope.defineSpecial(new Variable(selfName, true), end);
|
||||
|
||||
target.add(Instruction.loadCallee());
|
||||
target.add(_i -> i.index().toSet(false));
|
||||
target.add(_i -> i.index().toInit());
|
||||
}
|
||||
|
||||
body.resolve(target);
|
||||
|
@ -18,7 +18,8 @@ public class FunctionStatementNode extends FunctionNode {
|
||||
@Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) {
|
||||
var id = target.addChild(compileBody(target, name, null));
|
||||
target.add(_i -> Instruction.loadFunc(id, true, true, false, name, captures(id, target)));
|
||||
target.add(VariableNode.toSet(target, end, this.name, pollute, true));
|
||||
target.add(VariableNode.toInit(target, end, this.name));
|
||||
if (pollute) target.add(Instruction.pushUndefined());
|
||||
}
|
||||
|
||||
public FunctionStatementNode(Location loc, Location end, Parameters params, CompoundNode body, String name) {
|
||||
|
@ -28,6 +28,7 @@ import me.topchetoeu.jscript.compilation.control.WhileNode;
|
||||
import me.topchetoeu.jscript.compilation.scope.FunctionScope;
|
||||
import me.topchetoeu.jscript.compilation.values.ArgumentsNode;
|
||||
import me.topchetoeu.jscript.compilation.values.ArrayNode;
|
||||
import me.topchetoeu.jscript.compilation.values.ClassValueNode;
|
||||
import me.topchetoeu.jscript.compilation.values.ObjectNode;
|
||||
import me.topchetoeu.jscript.compilation.values.RegexNode;
|
||||
import me.topchetoeu.jscript.compilation.values.ThisNode;
|
||||
@ -63,7 +64,7 @@ public final class JavaScript {
|
||||
"finally", "for", "do", "while", "switch", "case", "default", "new",
|
||||
"function", "var", "return", "throw", "typeof", "delete", "break",
|
||||
"continue", "debugger", "implements", "interface", "package", "private",
|
||||
"protected", "public", "static", "arguments"
|
||||
"protected", "public", "static", "arguments", "class"
|
||||
));
|
||||
|
||||
public static ParseRes<? extends Node> parseParens(Source src, int i) {
|
||||
@ -88,6 +89,7 @@ public final class JavaScript {
|
||||
return ParseRes.first(src, i,
|
||||
(s, j) -> statement ? ParseRes.failed() : ObjectNode.parse(s, j),
|
||||
(s, j) -> statement ? ParseRes.failed() : FunctionNode.parseFunction(s, j, false),
|
||||
(s, j) -> statement ? ParseRes.failed() : ClassValueNode.parse(s, j),
|
||||
JavaScript::parseLiteral,
|
||||
StringNode::parse,
|
||||
RegexNode::parse,
|
||||
@ -96,7 +98,7 @@ public final class JavaScript {
|
||||
ChangeNode::parsePrefixIncrease,
|
||||
OperationNode::parsePrefix,
|
||||
ArrayNode::parse,
|
||||
FunctionArrowNode::parse,
|
||||
(s, j) -> statement ? ParseRes.failed() : FunctionArrowNode.parse(s, j),
|
||||
JavaScript::parseParens,
|
||||
CallNode::parseNew,
|
||||
TypeofNode::parse,
|
||||
@ -188,6 +190,7 @@ public final class JavaScript {
|
||||
if (Parsing.isIdentifier(src, i + n, "with")) return ParseRes.error(src.loc(i + n), "'with' statements are not allowed.");
|
||||
|
||||
ParseRes<? extends Node> res = ParseRes.first(src, i + n,
|
||||
ClassStatementNode::parse,
|
||||
VariableDeclareNode::parse,
|
||||
ReturnNode::parse,
|
||||
ThrowNode::parse,
|
||||
|
@ -37,8 +37,8 @@ public class VariableDeclareNode extends Node {
|
||||
@Override public void compile(CompileResult target, boolean pollute) {
|
||||
for (var entry : values) {
|
||||
if (entry.value == null) {
|
||||
if (declType == DeclarationType.VAR) entry.destructor.declare(target, null);
|
||||
else entry.destructor.declare(target, declType);
|
||||
if (declType == DeclarationType.VAR) entry.destructor.declare(target, null, false);
|
||||
else entry.destructor.declare(target, declType, false);
|
||||
}
|
||||
else {
|
||||
entry.value.compile(target, true);
|
||||
|
@ -12,24 +12,20 @@ import me.topchetoeu.jscript.compilation.DeferredIntSupplier;
|
||||
import me.topchetoeu.jscript.compilation.JavaScript;
|
||||
import me.topchetoeu.jscript.compilation.LabelContext;
|
||||
import me.topchetoeu.jscript.compilation.Node;
|
||||
import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType;
|
||||
import me.topchetoeu.jscript.compilation.scope.Variable;
|
||||
import me.topchetoeu.jscript.compilation.values.VariableNode;
|
||||
import me.topchetoeu.jscript.compilation.patterns.Binding;
|
||||
|
||||
public class ForInNode extends Node {
|
||||
public final String varName;
|
||||
public final DeclarationType declType;
|
||||
public final Binding binding;
|
||||
public final Node object, body;
|
||||
public final String label;
|
||||
public final Location varLocation;
|
||||
|
||||
@Override public void resolve(CompileResult target) {
|
||||
body.resolve(target);
|
||||
if (declType != null && !declType.strict) target.scope.define(new Variable(varName, false), loc());
|
||||
binding.resolve(target);
|
||||
}
|
||||
|
||||
@Override public void compile(CompileResult target, boolean pollute) {
|
||||
if (declType != null && declType.strict) target.scope.defineStrict(new Variable(varName, declType.readonly), varLocation);
|
||||
binding.declareLateInit(target);
|
||||
|
||||
object.compile(target, true, BreakpointType.STEP_OVER);
|
||||
target.add(Instruction.keys(false, true));
|
||||
@ -38,8 +34,8 @@ public class ForInNode extends Node {
|
||||
target.add(Instruction.dup());
|
||||
int mid = target.temp();
|
||||
|
||||
target.add(Instruction.loadMember("value")).setLocation(varLocation);
|
||||
target.add(VariableNode.toSet(target, loc(), varName, pollute, declType != null && declType.strict));
|
||||
target.add(Instruction.loadMember("value")).setLocation(binding.loc());
|
||||
binding.assign(target, false);
|
||||
target.setLocationAndDebug(object.loc(), BreakpointType.STEP_OVER);
|
||||
|
||||
var end = new DeferredIntSupplier();
|
||||
@ -56,12 +52,10 @@ public class ForInNode extends Node {
|
||||
if (pollute) target.add(Instruction.pushUndefined());
|
||||
}
|
||||
|
||||
public ForInNode(Location loc, Location varLocation, String label, DeclarationType declType, String varName, Node object, Node body) {
|
||||
public ForInNode(Location loc, String label, Binding binding, Node object, Node body) {
|
||||
super(loc);
|
||||
this.varLocation = varLocation;
|
||||
this.label = label;
|
||||
this.declType = declType;
|
||||
this.varName = varName;
|
||||
this.binding = binding;
|
||||
this.object = object;
|
||||
this.body = body;
|
||||
}
|
||||
@ -82,13 +76,9 @@ public class ForInNode extends Node {
|
||||
n++;
|
||||
n += Parsing.skipEmpty(src, i + n);
|
||||
|
||||
var declType = JavaScript.parseDeclarationType(src, i + n);
|
||||
n += declType.n;
|
||||
|
||||
var name = Parsing.parseIdentifier(src, i + n);
|
||||
if (!name.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a variable name for for-in loop");
|
||||
var nameLoc = src.loc(i + n);
|
||||
n += name.n;
|
||||
var binding = Binding.parse(src, i + n);
|
||||
if (!binding.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a binding in for-in loop");
|
||||
n += binding.n;
|
||||
n += Parsing.skipEmpty(src, i + n);
|
||||
|
||||
if (!Parsing.isIdentifier(src, i + n, "in")) return ParseRes.error(src.loc(i + n), "Expected 'in' keyword after variable declaration");
|
||||
@ -106,6 +96,6 @@ public class ForInNode extends Node {
|
||||
if (!bodyRes.isSuccess()) return bodyRes.chainError(src.loc(i + n), "Expected a for-in body");
|
||||
n += bodyRes.n;
|
||||
|
||||
return ParseRes.res(new ForInNode(loc, nameLoc, label.result, declType.result, name.result, obj.result, bodyRes.result), n);
|
||||
return ParseRes.res(new ForInNode(loc, label.result, binding.result, obj.result, bodyRes.result), n);
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ public class ForNode extends Node {
|
||||
@Override public void compile(CompileResult target, boolean pollute) {
|
||||
var subtarget = target.subtarget();
|
||||
subtarget.scope.singleEntry = false;
|
||||
subtarget.add(i -> Instruction.stackAlloc(subtarget.scope.capturablesOffset(), subtarget.scope.allocCount()));
|
||||
subtarget.beginScope();
|
||||
|
||||
declaration.compile(subtarget, false, BreakpointType.STEP_OVER);
|
||||
|
||||
@ -40,7 +40,7 @@ public class ForNode extends Node {
|
||||
CompoundNode.compileMultiEntry(body, subtarget, false, BreakpointType.STEP_OVER);
|
||||
LabelContext.popLoop(subtarget.env, label);
|
||||
|
||||
subtarget.add(_i -> Instruction.stackRealloc(subtarget.scope.capturablesOffset(), subtarget.scope.allocCount()));
|
||||
subtarget.reallocScope();
|
||||
|
||||
CompoundNode.compileMultiEntry(assignment, subtarget, false, BreakpointType.STEP_OVER);
|
||||
int endI = subtarget.size();
|
||||
@ -51,7 +51,7 @@ public class ForNode extends Node {
|
||||
subtarget.set(mid, Instruction.jmpIfNot(endI - mid + 1));
|
||||
if (pollute) subtarget.add(Instruction.pushUndefined());
|
||||
|
||||
subtarget.scope.end();
|
||||
subtarget.endScope();
|
||||
}
|
||||
|
||||
public ForNode(Location loc, String label, Node declaration, Node condition, Node assignment, Node body) {
|
||||
|
@ -12,24 +12,20 @@ import me.topchetoeu.jscript.compilation.DeferredIntSupplier;
|
||||
import me.topchetoeu.jscript.compilation.JavaScript;
|
||||
import me.topchetoeu.jscript.compilation.LabelContext;
|
||||
import me.topchetoeu.jscript.compilation.Node;
|
||||
import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType;
|
||||
import me.topchetoeu.jscript.compilation.scope.Variable;
|
||||
import me.topchetoeu.jscript.compilation.values.VariableNode;
|
||||
import me.topchetoeu.jscript.compilation.patterns.Binding;
|
||||
|
||||
public class ForOfNode extends Node {
|
||||
public final String varName;
|
||||
public final DeclarationType declType;
|
||||
public final Binding binding;
|
||||
public final Node iterable, body;
|
||||
public final String label;
|
||||
public final Location varLocation;
|
||||
|
||||
@Override public void resolve(CompileResult target) {
|
||||
body.resolve(target);
|
||||
if (declType != null && !declType.strict) target.scope.define(new Variable(varName, false), varLocation);
|
||||
binding.resolve(target);
|
||||
}
|
||||
|
||||
@Override public void compile(CompileResult target, boolean pollute) {
|
||||
if (declType != null && declType.strict) target.scope.defineStrict(new Variable(varName, declType.readonly), varLocation);
|
||||
binding.declareLateInit(target);
|
||||
|
||||
iterable.compile(target, true, BreakpointType.STEP_OVER);
|
||||
target.add(Instruction.dup());
|
||||
@ -46,8 +42,8 @@ public class ForOfNode extends Node {
|
||||
target.add(Instruction.loadMember("done")).setLocation(iterable.loc());
|
||||
int mid = target.temp();
|
||||
|
||||
target.add(Instruction.loadMember("value")).setLocation(varLocation);
|
||||
target.add(VariableNode.toSet(target, varLocation, varName, false, declType != null && declType.strict));
|
||||
target.add(Instruction.loadMember("value")).setLocation(binding.loc);
|
||||
binding.assign(target, false);
|
||||
|
||||
var end = new DeferredIntSupplier();
|
||||
|
||||
@ -65,12 +61,10 @@ public class ForOfNode extends Node {
|
||||
if (pollute) target.add(Instruction.pushUndefined());
|
||||
}
|
||||
|
||||
public ForOfNode(Location loc, Location varLocation, String label, DeclarationType declType, String varName, Node object, Node body) {
|
||||
public ForOfNode(Location loc, String label, Binding binding, Node object, Node body) {
|
||||
super(loc);
|
||||
this.varLocation = varLocation;
|
||||
this.label = label;
|
||||
this.declType = declType;
|
||||
this.varName = varName;
|
||||
this.binding = binding;
|
||||
this.iterable = object;
|
||||
this.body = body;
|
||||
}
|
||||
@ -91,13 +85,9 @@ public class ForOfNode extends Node {
|
||||
n++;
|
||||
n += Parsing.skipEmpty(src, i + n);
|
||||
|
||||
var declType = JavaScript.parseDeclarationType(src, i + n);
|
||||
n += declType.n;
|
||||
|
||||
var name = Parsing.parseIdentifier(src, i + n);
|
||||
if (!name.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a variable name for for-of loop");
|
||||
var nameLoc = src.loc(i + n);
|
||||
n += name.n;
|
||||
var binding = Binding.parse(src, i + n);
|
||||
if (!binding.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a binding in for-of loop");
|
||||
n += binding.n;
|
||||
n += Parsing.skipEmpty(src, i + n);
|
||||
|
||||
if (!Parsing.isIdentifier(src, i + n, "of")) return ParseRes.error(src.loc(i + n), "Expected 'of' keyword after variable declaration");
|
||||
@ -115,6 +105,6 @@ public class ForOfNode extends Node {
|
||||
if (!bodyRes.isSuccess()) return bodyRes.chainError(src.loc(i + n), "Expected a for-of body");
|
||||
n += bodyRes.n;
|
||||
|
||||
return ParseRes.res(new ForOfNode(loc, nameLoc, label.result, declType.result, name.result, obj.result, bodyRes.result), n);
|
||||
return ParseRes.res(new ForOfNode(loc, label.result, binding.result, obj.result, bodyRes.result), n);
|
||||
}
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ public class SwitchNode extends Node {
|
||||
value.compile(target, true, BreakpointType.STEP_OVER);
|
||||
|
||||
var subtarget = target.subtarget();
|
||||
subtarget.add(_i -> Instruction.stackAlloc(subtarget.scope.capturablesOffset(), subtarget.scope.allocCount()));
|
||||
subtarget.beginScope();
|
||||
|
||||
// TODO: create a jump map
|
||||
for (var ccase : cases) {
|
||||
@ -64,7 +64,7 @@ public class SwitchNode extends Node {
|
||||
}
|
||||
LabelContext.getBreak(target.env).pop(label);
|
||||
|
||||
subtarget.scope.end();
|
||||
subtarget.endScope();
|
||||
|
||||
int endI = subtarget.size();
|
||||
end.set(endI);
|
||||
|
@ -43,13 +43,14 @@ public class TryNode extends Node {
|
||||
|
||||
if (captureName != null) {
|
||||
var subtarget = target.subtarget();
|
||||
subtarget.add(i -> Instruction.stackAlloc(subtarget.scope.capturablesOffset(), subtarget.scope.allocCount()));
|
||||
subtarget.beginScope();
|
||||
subtarget.scope.singleEntry = true;
|
||||
|
||||
var catchVar = subtarget.scope.defineStrict(new Variable(captureName, false), catchBody.loc());
|
||||
subtarget.add(Instruction.loadError());
|
||||
subtarget.add(_i -> catchVar.index().toSet(false));
|
||||
subtarget.add(catchVar.index().toInit());
|
||||
catchBody.compile(subtarget, false);
|
||||
subtarget.endScope();
|
||||
|
||||
subtarget.scope.end();
|
||||
}
|
||||
|
@ -0,0 +1,54 @@
|
||||
package me.topchetoeu.jscript.compilation.members;
|
||||
|
||||
import me.topchetoeu.jscript.common.SyntaxException;
|
||||
import me.topchetoeu.jscript.common.parsing.Location;
|
||||
import me.topchetoeu.jscript.common.parsing.ParseRes;
|
||||
import me.topchetoeu.jscript.common.parsing.Parsing;
|
||||
import me.topchetoeu.jscript.common.parsing.Source;
|
||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
import me.topchetoeu.jscript.compilation.JavaScript;
|
||||
import me.topchetoeu.jscript.compilation.Node;
|
||||
import me.topchetoeu.jscript.compilation.patterns.AssignTarget;
|
||||
import me.topchetoeu.jscript.compilation.values.VariableNode;
|
||||
import me.topchetoeu.jscript.compilation.values.constants.StringNode;
|
||||
import me.topchetoeu.jscript.compilation.values.operations.AssignNode;
|
||||
|
||||
public class AssignShorthandNode extends Node {
|
||||
public final Node key;
|
||||
public final AssignTarget target;
|
||||
public final Node value;
|
||||
|
||||
@Override public void compile(CompileResult target, boolean pollute) {
|
||||
throw new SyntaxException(loc(), "Unexpected assign shorthand in non-destructor context");
|
||||
}
|
||||
|
||||
public AssignShorthandNode(Location loc, Node key, AssignTarget target, Node value) {
|
||||
super(loc);
|
||||
this.key = key;
|
||||
this.target = target;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public AssignTarget target() {
|
||||
return new AssignNode(loc(), target, value);
|
||||
}
|
||||
|
||||
public static ParseRes<AssignShorthandNode> parse(Source src, int i) {
|
||||
var n = Parsing.skipEmpty(src, i);
|
||||
var loc = src.loc(i + n);
|
||||
|
||||
var var = VariableNode.parse(src, i + n);
|
||||
if (!var.isSuccess()) return var.chainError();
|
||||
n += var.n;
|
||||
n += Parsing.skipEmpty(src, i + n);
|
||||
|
||||
if (!src.is(i + n, "=")) return ParseRes.failed();
|
||||
n++;
|
||||
|
||||
var value = JavaScript.parseExpression(src, i + n, 2);
|
||||
if (!value.isSuccess()) return value.chainError(src.loc(i + n), "Expected a shorthand initializer");
|
||||
n += value.n;
|
||||
|
||||
return ParseRes.res(new AssignShorthandNode(loc, new StringNode(loc, var.result.name), var.result, value.result), n);
|
||||
}
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
package me.topchetoeu.jscript.compilation.members;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.parsing.Location;
|
||||
import me.topchetoeu.jscript.common.parsing.ParseRes;
|
||||
import me.topchetoeu.jscript.common.parsing.Parsing;
|
||||
import me.topchetoeu.jscript.common.parsing.Source;
|
||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
import me.topchetoeu.jscript.compilation.JavaScript;
|
||||
import me.topchetoeu.jscript.compilation.Node;
|
||||
import me.topchetoeu.jscript.compilation.values.ObjectNode;
|
||||
import me.topchetoeu.jscript.compilation.values.VariableNode;
|
||||
import me.topchetoeu.jscript.compilation.values.constants.StringNode;
|
||||
|
||||
public class FieldMemberNode extends Node {
|
||||
public final Node key;
|
||||
public final Node value;
|
||||
|
||||
@Override public void compile(CompileResult target, boolean pollute) {
|
||||
if (pollute) target.add(Instruction.dup());
|
||||
key.compile(target, true);
|
||||
|
||||
if (value == null) target.add(Instruction.pushUndefined());
|
||||
else value.compile(target, true);
|
||||
|
||||
target.add(Instruction.defField());
|
||||
}
|
||||
|
||||
public FieldMemberNode(Location loc, Node key, Node value) {
|
||||
super(loc);
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static ParseRes<FieldMemberNode> parseObject(Source src, int i) {
|
||||
var n = Parsing.skipEmpty(src, i);
|
||||
var loc = src.loc(i + n);
|
||||
|
||||
var name = ObjectNode.parsePropName(src, i + n);
|
||||
if (!name.isSuccess()) return name.chainError();
|
||||
n += name.n;
|
||||
n += Parsing.skipEmpty(src, i + n);
|
||||
|
||||
if (!src.is(i + n, ":")) return ParseRes.failed();
|
||||
n++;
|
||||
|
||||
var value = JavaScript.parseExpression(src, i + n, 2);
|
||||
if (!value.isSuccess()) return value.chainError(src.loc(i + n), "Expected a value");
|
||||
n += value.n;
|
||||
|
||||
return ParseRes.res(new FieldMemberNode(loc, name.result, value.result), n);
|
||||
}
|
||||
|
||||
public static ParseRes<FieldMemberNode> parseShorthand(Source src, int i) {
|
||||
var n = Parsing.skipEmpty(src, i);
|
||||
var loc = src.loc(i + n);
|
||||
|
||||
var var = VariableNode.parse(src, i + n);
|
||||
if (!var.isSuccess()) return var.chainError();
|
||||
n += var.n;
|
||||
|
||||
return ParseRes.res(new FieldMemberNode(loc, new StringNode(loc, var.result.name), var.result), n);
|
||||
}
|
||||
|
||||
public static ParseRes<FieldMemberNode> parseClass(Source src, int i) {
|
||||
var n = Parsing.skipEmpty(src, i);
|
||||
var loc = src.loc(i + n);
|
||||
|
||||
var name = ObjectNode.parsePropName(src, i + n);
|
||||
if (!name.isSuccess()) return name.chainError();
|
||||
n += name.n;
|
||||
n += Parsing.skipEmpty(src, i + n);
|
||||
|
||||
if (!src.is(i + n, "=")) {
|
||||
var end = JavaScript.parseStatement(src, i + n);
|
||||
if (!end.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected an end of statement or a field initializer");
|
||||
n += end.n;
|
||||
|
||||
return ParseRes.res(new FieldMemberNode(loc, name.result, null), n);
|
||||
}
|
||||
n++;
|
||||
|
||||
var value = JavaScript.parseExpression(src, i + n, 2);
|
||||
if (!value.isSuccess()) return value.chainError(src.loc(i + n), "Expected a value");
|
||||
n += value.n;
|
||||
|
||||
return ParseRes.res(new FieldMemberNode(loc, name.result, value.result), n);
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
package me.topchetoeu.jscript.compilation.members;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
||||
import me.topchetoeu.jscript.common.parsing.Location;
|
||||
import me.topchetoeu.jscript.common.parsing.ParseRes;
|
||||
import me.topchetoeu.jscript.common.parsing.Parsing;
|
||||
import me.topchetoeu.jscript.common.parsing.Source;
|
||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
import me.topchetoeu.jscript.compilation.CompoundNode;
|
||||
import me.topchetoeu.jscript.compilation.FunctionNode;
|
||||
import me.topchetoeu.jscript.compilation.Node;
|
||||
import me.topchetoeu.jscript.compilation.Parameters;
|
||||
import me.topchetoeu.jscript.compilation.values.ObjectNode;
|
||||
import me.topchetoeu.jscript.compilation.values.constants.StringNode;
|
||||
|
||||
public class MethodMemberNode extends FunctionNode {
|
||||
public final Node key;
|
||||
|
||||
@Override public String name() {
|
||||
if (key instanceof StringNode str) return str.value;
|
||||
else return null;
|
||||
}
|
||||
|
||||
@Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) {
|
||||
if (pollute) target.add(Instruction.dup());
|
||||
key.compile(target, true);
|
||||
|
||||
var id = target.addChild(compileBody(target, name, null));
|
||||
target.add(_i -> Instruction.loadFunc(id, true, false, false, name, captures(id, target)));
|
||||
|
||||
target.add(Instruction.defField());
|
||||
}
|
||||
|
||||
public MethodMemberNode(Location loc, Location end, Node key, Parameters params, CompoundNode body) {
|
||||
super(loc, end, params, body);
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public static ParseRes<MethodMemberNode> parse(Source src, int i) {
|
||||
var n = Parsing.skipEmpty(src, i);
|
||||
var loc = src.loc(i + n);
|
||||
|
||||
var name = ObjectNode.parsePropName(src, i + n);
|
||||
if (!name.isSuccess()) return name.chainError();
|
||||
n += name.n;
|
||||
|
||||
var params = Parameters.parseParameters(src, i + n);
|
||||
if (!params.isSuccess()) return params.chainError(src.loc(i + n), "Expected an argument list");
|
||||
n += params.n;
|
||||
|
||||
var body = CompoundNode.parse(src, i + n);
|
||||
if (!body.isSuccess()) return body.chainError(src.loc(i + n), "Expected a compound statement for property accessor.");
|
||||
n += body.n;
|
||||
|
||||
var end = src.loc(i + n - 1);
|
||||
|
||||
return ParseRes.res(new MethodMemberNode(
|
||||
loc, end, name.result, params.result, body.result
|
||||
), n);
|
||||
}
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
package me.topchetoeu.jscript.compilation.members;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
||||
import me.topchetoeu.jscript.common.parsing.Location;
|
||||
import me.topchetoeu.jscript.common.parsing.ParseRes;
|
||||
import me.topchetoeu.jscript.common.parsing.Parsing;
|
||||
import me.topchetoeu.jscript.common.parsing.Source;
|
||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
import me.topchetoeu.jscript.compilation.CompoundNode;
|
||||
import me.topchetoeu.jscript.compilation.FunctionNode;
|
||||
import me.topchetoeu.jscript.compilation.Node;
|
||||
import me.topchetoeu.jscript.compilation.Parameters;
|
||||
import me.topchetoeu.jscript.compilation.patterns.Pattern;
|
||||
import me.topchetoeu.jscript.compilation.values.ObjectNode;
|
||||
import me.topchetoeu.jscript.compilation.values.constants.StringNode;
|
||||
|
||||
public class PropertyMemberNode extends FunctionNode {
|
||||
public final Node key;
|
||||
public final Pattern argument;
|
||||
|
||||
@Override public String name() {
|
||||
if (key instanceof StringNode str) {
|
||||
if (isGetter()) return "get " + str.value;
|
||||
else return "set " + str.value;
|
||||
}
|
||||
else return null;
|
||||
}
|
||||
|
||||
public boolean isGetter() { return argument == null; }
|
||||
public boolean isSetter() { return argument != null; }
|
||||
|
||||
@Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) {
|
||||
if (pollute) target.add(Instruction.dup());
|
||||
key.compile(target, true);
|
||||
|
||||
var id = target.addChild(compileBody(target, name, null));
|
||||
target.add(_i -> Instruction.loadFunc(id, true, false, false, name, captures(id, target)));
|
||||
|
||||
target.add(Instruction.defProp(isSetter()));
|
||||
}
|
||||
|
||||
public PropertyMemberNode(Location loc, Location end, Node key, Pattern argument, CompoundNode body) {
|
||||
super(loc, end, argument == null ? new Parameters(Arrays.asList()) : new Parameters(Arrays.asList(argument)), body);
|
||||
this.key = key;
|
||||
this.argument = argument;
|
||||
}
|
||||
|
||||
public static ParseRes<PropertyMemberNode> parse(Source src, int i) {
|
||||
var n = Parsing.skipEmpty(src, i);
|
||||
var loc = src.loc(i + n);
|
||||
|
||||
var access = Parsing.parseIdentifier(src, i + n);
|
||||
if (!access.isSuccess()) return ParseRes.failed();
|
||||
if (!access.result.equals("get") && !access.result.equals("set")) return ParseRes.failed();
|
||||
n += access.n;
|
||||
|
||||
var name = ObjectNode.parsePropName(src, i + n);
|
||||
if (!name.isSuccess()) return name.chainError(src.loc(i + n), "Expected a property name after '" + access + "'");
|
||||
n += name.n;
|
||||
|
||||
var params = Parameters.parseParameters(src, i + n);
|
||||
if (!params.isSuccess()) return params.chainError(src.loc(i + n), "Expected an argument list");
|
||||
if (access.result.equals("get") && params.result.params.size() != 0) return ParseRes.error(src.loc(i + n), "Getter must not have any parameters");
|
||||
if (access.result.equals("set") && params.result.params.size() != 1) return ParseRes.error(src.loc(i + n), "Setter must have exactly one parameter");
|
||||
if (params.result.rest != null) return ParseRes.error(params.result.rest.loc(), "Property members may not have rest arguments");
|
||||
n += params.n;
|
||||
|
||||
var body = CompoundNode.parse(src, i + n);
|
||||
if (!body.isSuccess()) return body.chainError(src.loc(i + n), "Expected a compound statement for property accessor.");
|
||||
n += body.n;
|
||||
|
||||
var end = src.loc(i + n - 1);
|
||||
|
||||
return ParseRes.res(new PropertyMemberNode(
|
||||
loc, end, name.result, access.result.equals("get") ? null : params.result.params.get(0), body.result
|
||||
), n);
|
||||
}
|
||||
}
|
@ -14,19 +14,17 @@ import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType;
|
||||
|
||||
public class AssignPattern implements Pattern {
|
||||
public final Location loc;
|
||||
public final Pattern assignable;
|
||||
public final AssignTarget assignable;
|
||||
public final Node value;
|
||||
|
||||
@Override public Location loc() { return loc; }
|
||||
|
||||
@Override public void destructDeclResolve(CompileResult target) {
|
||||
assignable.destructDeclResolve(target);
|
||||
if (!(assignable instanceof Pattern p)) throw new SyntaxException(assignable.loc(), "Unexpected non-pattern in destruct context");
|
||||
p.destructDeclResolve(target);
|
||||
}
|
||||
|
||||
@Override public void declare(CompileResult target, DeclarationType decl) {
|
||||
throw new SyntaxException(loc(), "Expected an assignment value for destructor declaration");
|
||||
}
|
||||
@Override public void destruct(CompileResult target, DeclarationType decl, boolean shouldDeclare) {
|
||||
private void common(CompileResult target) {
|
||||
target.add(Instruction.dup());
|
||||
target.add(Instruction.pushUndefined());
|
||||
target.add(Instruction.operation(Operation.EQUALS));
|
||||
@ -36,8 +34,27 @@ public class AssignPattern implements Pattern {
|
||||
value.compile(target, true);
|
||||
|
||||
target.set(start, Instruction.jmpIfNot(target.size() - start));
|
||||
}
|
||||
|
||||
assignable.destruct(target, decl, shouldDeclare);
|
||||
@Override public void declare(CompileResult target, DeclarationType decl, boolean lateInitializer) {
|
||||
if (lateInitializer) {
|
||||
if (assignable instanceof Pattern p) p.declare(target, decl, lateInitializer);
|
||||
else throw new SyntaxException(assignable.loc(), "Unexpected non-pattern in destruct context");
|
||||
}
|
||||
else throw new SyntaxException(loc(), "Expected an assignment value for destructor declaration");
|
||||
}
|
||||
@Override public void destruct(CompileResult target, DeclarationType decl, boolean shouldDeclare) {
|
||||
if (!(assignable instanceof Pattern p)) throw new SyntaxException(assignable.loc(), "Unexpected non-pattern in destruct context");
|
||||
common(target);
|
||||
p.destruct(target, decl, shouldDeclare);
|
||||
}
|
||||
|
||||
@Override public void beforeAssign(CompileResult target) {
|
||||
assignable.beforeAssign(target);
|
||||
}
|
||||
@Override public void afterAssign(CompileResult target, boolean pollute) {
|
||||
common(target);
|
||||
assignable.afterAssign(target, false);
|
||||
}
|
||||
|
||||
public AssignPattern(Location loc, Pattern assignable, Node value) {
|
||||
|
@ -0,0 +1,80 @@
|
||||
package me.topchetoeu.jscript.compilation.patterns;
|
||||
|
||||
import me.topchetoeu.jscript.common.SyntaxException;
|
||||
import me.topchetoeu.jscript.common.parsing.Location;
|
||||
import me.topchetoeu.jscript.common.parsing.ParseRes;
|
||||
import me.topchetoeu.jscript.common.parsing.Parsing;
|
||||
import me.topchetoeu.jscript.common.parsing.Source;
|
||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
import me.topchetoeu.jscript.compilation.JavaScript;
|
||||
import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType;
|
||||
|
||||
public class Binding implements Pattern {
|
||||
public final Location loc;
|
||||
public final DeclarationType type;
|
||||
public final AssignTarget assignable;
|
||||
|
||||
@Override public Location loc() { return loc; }
|
||||
|
||||
@Override public void destructDeclResolve(CompileResult target) {
|
||||
if (type != null && !type.strict) {
|
||||
if (!(assignable instanceof Pattern p)) throw new SyntaxException(assignable.loc(), "Unexpected non-pattern in destruct context");
|
||||
p.destructDeclResolve(target);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void destruct(CompileResult target, DeclarationType decl, boolean shouldDeclare) {
|
||||
if (!(assignable instanceof Pattern p)) throw new SyntaxException(assignable.loc(), "Unexpected non-pattern in destruct context");
|
||||
p.destruct(target, decl, shouldDeclare);
|
||||
}
|
||||
@Override public void declare(CompileResult target, DeclarationType decl, boolean lateInitializer) {
|
||||
if (!(assignable instanceof Pattern p)) throw new SyntaxException(assignable.loc(), "Unexpected non-pattern in destruct context");
|
||||
p.declare(target, decl, lateInitializer);
|
||||
}
|
||||
|
||||
public void resolve(CompileResult target) {
|
||||
if (type != null) destructDeclResolve(target);
|
||||
}
|
||||
|
||||
public void declare(CompileResult target, boolean hasInit) {
|
||||
if (type != null) destructVar(target, type, hasInit);
|
||||
}
|
||||
public void declareLateInit(CompileResult target) {
|
||||
if (type != null) declare(target, type, true);
|
||||
}
|
||||
|
||||
@Override public void afterAssign(CompileResult target, boolean pollute) {
|
||||
assignable.assign(target, pollute);
|
||||
}
|
||||
|
||||
public Binding(Location loc, DeclarationType type, AssignTarget assignable) {
|
||||
this.loc = loc;
|
||||
this.type = type;
|
||||
this.assignable = assignable;
|
||||
}
|
||||
|
||||
public static ParseRes<Binding> parse(Source src, int i) {
|
||||
var n = Parsing.skipEmpty(src, i);
|
||||
var loc = src.loc(i + n);
|
||||
|
||||
var declType = JavaScript.parseDeclarationType(src, i + n);
|
||||
if (!declType.isSuccess()) {
|
||||
var res = JavaScript.parseExpression(src, i + n, 13);
|
||||
if (res.isSuccess() && res.result instanceof AssignTargetLike target) {
|
||||
n += res.n;
|
||||
return ParseRes.res(new Binding(loc, null, target.toAssignTarget()), n);
|
||||
}
|
||||
else return ParseRes.failed();
|
||||
}
|
||||
else {
|
||||
n += declType.n;
|
||||
n += Parsing.skipEmpty(src, i + n);
|
||||
|
||||
var res = Pattern.parse(src, i + n, false);
|
||||
if (!res.isSuccess()) return ParseRes.failed();
|
||||
n += res.n;
|
||||
|
||||
return ParseRes.res(new Binding(loc, declType.result, res.result), n);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
package me.topchetoeu.jscript.compilation.patterns;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import me.topchetoeu.jscript.common.parsing.Location;
|
||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
|
||||
public class ObjectAssignable extends ObjectDestructor<AssignTarget> implements AssignTarget {
|
||||
@Override public void afterAssign(CompileResult target, boolean pollute) {
|
||||
compile(target, t -> t.assign(target, false), pollute);
|
||||
}
|
||||
|
||||
public ObjectAssignable(Location loc, List<Member<AssignTarget>> members) {
|
||||
super(loc, members);
|
||||
}
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
package me.topchetoeu.jscript.compilation.patterns;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.parsing.Location;
|
||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
import me.topchetoeu.jscript.compilation.Node;
|
||||
import me.topchetoeu.jscript.compilation.values.operations.IndexNode;
|
||||
|
||||
public abstract class ObjectDestructor<T> extends Node {
|
||||
public static final class Member<T> {
|
||||
public final Node key;
|
||||
public final T consumable;
|
||||
|
||||
public Member(Node key, T consumer) {
|
||||
this.key = key;
|
||||
this.consumable = consumer;
|
||||
}
|
||||
}
|
||||
|
||||
public final List<Member<T>> members;
|
||||
|
||||
public void consume(Consumer<T> consumer) {
|
||||
for (var el : members) {
|
||||
consumer.accept(el.consumable);
|
||||
}
|
||||
}
|
||||
public void compile(CompileResult target, Consumer<T> consumer, boolean pollute) {
|
||||
for (var el : members) {
|
||||
target.add(Instruction.dup());
|
||||
IndexNode.indexLoad(target, el.key, true);
|
||||
consumer.accept(el.consumable);
|
||||
}
|
||||
|
||||
if (!pollute) target.add(Instruction.discard());
|
||||
}
|
||||
|
||||
public ObjectDestructor(Location loc, List<Member<T>> members) {
|
||||
super(loc);
|
||||
this.members = members;
|
||||
}
|
||||
}
|
@ -2,36 +2,79 @@ package me.topchetoeu.jscript.compilation.patterns;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.SyntaxException;
|
||||
import me.topchetoeu.jscript.common.parsing.Location;
|
||||
import me.topchetoeu.jscript.common.parsing.ParseRes;
|
||||
import me.topchetoeu.jscript.common.parsing.Parsing;
|
||||
import me.topchetoeu.jscript.common.parsing.Source;
|
||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
import me.topchetoeu.jscript.compilation.Node;
|
||||
import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType;
|
||||
import me.topchetoeu.jscript.compilation.values.ObjectNode;
|
||||
import me.topchetoeu.jscript.compilation.values.VariableNode;
|
||||
import me.topchetoeu.jscript.compilation.values.constants.StringNode;
|
||||
import me.topchetoeu.jscript.compilation.values.operations.IndexNode;
|
||||
|
||||
public class ObjectPattern extends Node implements Pattern {
|
||||
public static final class Member {
|
||||
public final Node key;
|
||||
public final AssignTarget consumable;
|
||||
|
||||
public Member(Node key, AssignTarget consumer) {
|
||||
this.key = key;
|
||||
this.consumable = consumer;
|
||||
}
|
||||
}
|
||||
|
||||
public final List<Member> members;
|
||||
|
||||
public void compile(CompileResult target, Consumer<AssignTarget> consumer, boolean pollute) {
|
||||
for (var el : members) {
|
||||
target.add(Instruction.dup());
|
||||
IndexNode.indexLoad(target, el.key, true);
|
||||
consumer.accept(el.consumable);
|
||||
}
|
||||
|
||||
if (!pollute) target.add(Instruction.discard());
|
||||
}
|
||||
|
||||
public class ObjectPattern extends ObjectDestructor<Pattern> implements Pattern {
|
||||
@Override public void destructDeclResolve(CompileResult target) {
|
||||
consume(t -> t.destructDeclResolve(target));
|
||||
for (var t : members) {
|
||||
if (t.consumable instanceof Pattern p) p.destructDeclResolve(target);
|
||||
else throw new SyntaxException(t.consumable.loc(), "Unexpected non-pattern in destruct context");
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void destruct(CompileResult target, DeclarationType decl, boolean shouldDeclare) {
|
||||
compile(target, t -> t.destruct(target, decl, shouldDeclare), false);
|
||||
compile(target, t -> {
|
||||
if (t instanceof Pattern p) p.destruct(target, decl, shouldDeclare);
|
||||
else throw new SyntaxException(t.loc(), "Unexpected non-pattern in destruct context");
|
||||
}, false);
|
||||
}
|
||||
|
||||
@Override public void declare(CompileResult target, DeclarationType decl) {
|
||||
throw new SyntaxException(loc(), "Object pattern must be initialized");
|
||||
@Override public void afterAssign(CompileResult target, boolean pollute) {
|
||||
compile(target, t -> t.assign(target, false), pollute);
|
||||
}
|
||||
|
||||
public ObjectPattern(Location loc, List<Member<Pattern>> members) {
|
||||
super(loc, members);
|
||||
@Override public void declare(CompileResult target, DeclarationType decl, boolean lateInitializer) {
|
||||
if (lateInitializer) {
|
||||
for (var t : members) {
|
||||
if (t.consumable instanceof Pattern p) p.declare(target, decl, lateInitializer);
|
||||
else throw new SyntaxException(t.consumable.loc(), "Unexpected non-pattern in destruct context");
|
||||
}
|
||||
}
|
||||
else throw new SyntaxException(loc(), "Object pattern must be initialized");
|
||||
}
|
||||
|
||||
private static ParseRes<Member<Pattern>> parseShorthand(Source src, int i) {
|
||||
public ObjectPattern(Location loc, List<Member> members) {
|
||||
super(loc);
|
||||
this.members = members;
|
||||
}
|
||||
|
||||
private static ParseRes<Member> parseShorthand(Source src, int i) {
|
||||
ParseRes<Pattern> res = ParseRes.first(src, i,
|
||||
AssignPattern::parse,
|
||||
VariableNode::parse
|
||||
@ -40,17 +83,17 @@ public class ObjectPattern extends ObjectDestructor<Pattern> implements Pattern
|
||||
if (res.isSuccess()) {
|
||||
if (res.result instanceof AssignPattern assign) {
|
||||
if (assign.assignable instanceof VariableNode var) {
|
||||
return ParseRes.res(new Member<>(new StringNode(var.loc(), var.name), res.result), res.n);
|
||||
return ParseRes.res(new Member(new StringNode(var.loc(), var.name), res.result), res.n);
|
||||
}
|
||||
}
|
||||
else if (res.result instanceof VariableNode var) {
|
||||
return ParseRes.res(new Member<>(new StringNode(var.loc(), var.name), res.result), res.n);
|
||||
return ParseRes.res(new Member(new StringNode(var.loc(), var.name), res.result), res.n);
|
||||
}
|
||||
}
|
||||
|
||||
return res.chainError();
|
||||
}
|
||||
private static ParseRes<Member<Pattern>> parseKeyed(Source src, int i) {
|
||||
private static ParseRes<Member> parseKeyed(Source src, int i) {
|
||||
var n = Parsing.skipEmpty(src, i);
|
||||
|
||||
var key = ObjectNode.parsePropName(src, i + n);
|
||||
@ -65,7 +108,7 @@ public class ObjectPattern extends ObjectDestructor<Pattern> implements Pattern
|
||||
if (!res.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a pattern after colon");
|
||||
n += res.n;
|
||||
|
||||
return ParseRes.res(new Member<>(key.result, res.result), n);
|
||||
return ParseRes.res(new Member(key.result, res.result), n);
|
||||
}
|
||||
|
||||
public static ParseRes<ObjectPattern> parse(Source src, int i) {
|
||||
@ -76,7 +119,7 @@ public class ObjectPattern extends ObjectDestructor<Pattern> implements Pattern
|
||||
n++;
|
||||
n += Parsing.skipEmpty(src, i + n);
|
||||
|
||||
var members = new LinkedList<Member<Pattern>>();
|
||||
var members = new LinkedList<Member>();
|
||||
|
||||
if (src.is(i + n, "}")) {
|
||||
n++;
|
||||
@ -84,7 +127,7 @@ public class ObjectPattern extends ObjectDestructor<Pattern> implements Pattern
|
||||
}
|
||||
|
||||
while (true) {
|
||||
ParseRes<Member<Pattern>> prop = ParseRes.first(src, i + n,
|
||||
ParseRes<Member> prop = ParseRes.first(src, i + n,
|
||||
ObjectPattern::parseKeyed,
|
||||
ObjectPattern::parseShorthand
|
||||
);
|
||||
|
@ -10,7 +10,7 @@ import me.topchetoeu.jscript.compilation.values.VariableNode;
|
||||
/**
|
||||
* Represents all nodes that can be a destructors (note that all destructors are assign targets, too)
|
||||
*/
|
||||
public interface Pattern {
|
||||
public interface Pattern extends AssignTarget {
|
||||
Location loc();
|
||||
|
||||
/**
|
||||
@ -28,7 +28,21 @@ public interface Pattern {
|
||||
/**
|
||||
* Run when destructing a declaration without an initializer
|
||||
*/
|
||||
void declare(CompileResult target, DeclarationType decl);
|
||||
void declare(CompileResult target, DeclarationType decl, boolean lateInitializer);
|
||||
|
||||
public default void destructArg(CompileResult target, DeclarationType decl) {
|
||||
destruct(target, decl, false);
|
||||
}
|
||||
public default void destructVar(CompileResult target, DeclarationType decl, boolean hasInitializer) {
|
||||
if (hasInitializer) {
|
||||
if (decl == null || !decl.strict) destruct(target, null, true);
|
||||
else destruct(target, decl, true);
|
||||
}
|
||||
else {
|
||||
if (decl == null || !decl.strict) declare(target, null, false);
|
||||
else declare(target, decl, false);
|
||||
}
|
||||
}
|
||||
|
||||
public static ParseRes<Pattern> parse(Source src, int i, boolean withDefault) {
|
||||
return withDefault ?
|
||||
|
@ -36,7 +36,7 @@ public class FunctionScope extends Scope {
|
||||
}
|
||||
else {
|
||||
functionVarMap.put(var.name, var);
|
||||
return variables.add(var);
|
||||
return locals.add(var);
|
||||
}
|
||||
}
|
||||
public Variable defineSpecial(Variable var, Location loc) {
|
||||
@ -44,7 +44,7 @@ public class FunctionScope extends Scope {
|
||||
if (strictVarMap.containsKey(var.name)) throw alreadyDefinedErr(loc, var.name);
|
||||
|
||||
specialVarMap.put(var.name, var);
|
||||
return variables.add(var);
|
||||
return locals.add(var);
|
||||
}
|
||||
|
||||
@Override public Variable get(String name, boolean capture) {
|
||||
|
@ -10,8 +10,8 @@ import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType;
|
||||
public class Scope {
|
||||
protected final HashMap<String, Variable> strictVarMap = new HashMap<>();
|
||||
|
||||
protected final VariableList variables = new VariableList(VariableIndex.IndexType.LOCALS, this::variableOffset);
|
||||
protected final VariableList captured = new VariableList(VariableIndex.IndexType.CAPTURABLES, this::capturablesOffset);
|
||||
protected final VariableList locals = new VariableList(VariableIndex.IndexType.LOCALS, this::variableOffset);
|
||||
protected final VariableList capturables = new VariableList(VariableIndex.IndexType.CAPTURABLES, this::capturablesOffset);
|
||||
|
||||
private boolean ended = false;
|
||||
private boolean finished = false;
|
||||
@ -29,7 +29,7 @@ public class Scope {
|
||||
|
||||
protected void transferCaptured(Variable var) {
|
||||
if (!singleEntry) {
|
||||
this.captured.add(var);
|
||||
this.capturables.add(var);
|
||||
}
|
||||
else if (parent != null) {
|
||||
parent.transferCaptured(var);
|
||||
@ -60,7 +60,7 @@ public class Scope {
|
||||
*/
|
||||
public Variable defineTemp() {
|
||||
checkNotEnded();
|
||||
return this.variables.add(new Variable("<temp>", false));
|
||||
return this.locals.add(new Variable("<temp>", false));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -96,7 +96,7 @@ public class Scope {
|
||||
if (hasNonStrict(var.name)) throw alreadyDefinedErr(loc, var.name);
|
||||
|
||||
strictVarMap.put(var.name, var);
|
||||
return variables.add(var);
|
||||
return locals.add(var);
|
||||
}
|
||||
/**
|
||||
* Gets the index supplier of the given variable name, or null if it is a global
|
||||
@ -129,7 +129,7 @@ public class Scope {
|
||||
var res = 0;
|
||||
|
||||
for (var curr = parent; curr != null; curr = curr.parent) {
|
||||
res += parent.variables.size();
|
||||
res += parent.locals.size();
|
||||
}
|
||||
|
||||
return res;
|
||||
@ -138,7 +138,7 @@ public class Scope {
|
||||
var res = 0;
|
||||
|
||||
for (var curr = this; curr != null; curr = curr.parent) {
|
||||
if (curr != this) res += parent.captured.size();
|
||||
if (curr != this) res += parent.capturables.size();
|
||||
if (curr.parent == null) res += curr.localsCount();
|
||||
}
|
||||
|
||||
@ -157,19 +157,26 @@ public class Scope {
|
||||
if (res < childN) res = childN;
|
||||
}
|
||||
|
||||
return res + variables.size();
|
||||
return res + locals.size();
|
||||
}
|
||||
public int capturesCount() { return 0; }
|
||||
public int allocCount() {
|
||||
var res = captured.size();
|
||||
var res = capturables.size();
|
||||
return res;
|
||||
}
|
||||
public int capturablesCount() {
|
||||
var res = captured.size();
|
||||
var res = capturables.size();
|
||||
for (var child : children) res += child.capturablesCount();
|
||||
return res;
|
||||
}
|
||||
|
||||
public Iterable<Variable> capturables() {
|
||||
return capturables.all();
|
||||
}
|
||||
public Iterable<Variable> locals() {
|
||||
return locals.all();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends this scope. This will make it possible for another child to take its place
|
||||
*/
|
||||
@ -187,8 +194,8 @@ public class Scope {
|
||||
}
|
||||
|
||||
protected void onFinish() {
|
||||
this.variables.freeze();
|
||||
this.captured.freeze();
|
||||
this.locals.freeze();
|
||||
this.capturables.freeze();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -30,9 +30,25 @@ public final class VariableIndex {
|
||||
}
|
||||
public final Instruction toSet(boolean keep) {
|
||||
switch (type) {
|
||||
case CAPTURES: return Instruction.storeVar(~index, keep);
|
||||
case CAPTURABLES: return Instruction.storeVar(index, keep);
|
||||
case LOCALS: return Instruction.storeVar(index, keep);
|
||||
case CAPTURES: return Instruction.storeVar(index, keep, false);
|
||||
case CAPTURABLES: return Instruction.storeVar(index, keep, false);
|
||||
case LOCALS: return Instruction.storeVar(index, keep, false);
|
||||
default: throw new UnsupportedOperationException("Unknown index type " + type);
|
||||
}
|
||||
}
|
||||
public final Instruction toInit() {
|
||||
switch (type) {
|
||||
case CAPTURES: throw new UnsupportedOperationException("Unknown index type " + type);
|
||||
case CAPTURABLES: return Instruction.storeVar(index, false, true);
|
||||
case LOCALS: return Instruction.storeVar(index, false, true);
|
||||
default: throw new UnsupportedOperationException("Unknown index type " + type);
|
||||
}
|
||||
}
|
||||
public final Instruction toUndefinedInit(boolean force) {
|
||||
switch (type) {
|
||||
case CAPTURES: throw new UnsupportedOperationException("Unknown index type " + type);
|
||||
case CAPTURABLES: return Instruction.varInit(index, force);
|
||||
case LOCALS: return Instruction.varInit(index, force);
|
||||
default: throw new UnsupportedOperationException("Unknown index type " + type);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,30 @@
|
||||
package me.topchetoeu.jscript.compilation.values;
|
||||
|
||||
import me.topchetoeu.jscript.common.parsing.Location;
|
||||
import me.topchetoeu.jscript.common.parsing.ParseRes;
|
||||
import me.topchetoeu.jscript.common.parsing.Parsing;
|
||||
import me.topchetoeu.jscript.common.parsing.Source;
|
||||
import me.topchetoeu.jscript.compilation.ClassNode;
|
||||
|
||||
public class ClassValueNode extends ClassNode {
|
||||
public ClassValueNode(Location loc, Location end, String name, ClassBody body) {
|
||||
super(loc, end, name, body);
|
||||
}
|
||||
|
||||
public static ParseRes<ClassValueNode> parse(Source src, int i) {
|
||||
var n = Parsing.skipEmpty(src, i);
|
||||
var loc = src.loc(i + n);
|
||||
|
||||
if (!Parsing.isIdentifier(src, i + n, "class")) return ParseRes.failed();
|
||||
n += 5;
|
||||
|
||||
var name = Parsing.parseIdentifier(src, i + n);
|
||||
n += name.n;
|
||||
|
||||
var body = parseBody(src, i + n);
|
||||
if (!body.isSuccess()) return body.chainError(src.loc(i + n), "Expected a class body");
|
||||
n += body.n;
|
||||
|
||||
return ParseRes.res(new ClassValueNode(loc, src.loc(i + n), name.result, body.result), n);
|
||||
}
|
||||
}
|
@ -1,252 +1,28 @@
|
||||
package me.topchetoeu.jscript.compilation.values;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.SyntaxException;
|
||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
||||
import me.topchetoeu.jscript.common.parsing.Location;
|
||||
import me.topchetoeu.jscript.common.parsing.ParseRes;
|
||||
import me.topchetoeu.jscript.common.parsing.Parsing;
|
||||
import me.topchetoeu.jscript.common.parsing.Source;
|
||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
import me.topchetoeu.jscript.compilation.CompoundNode;
|
||||
import me.topchetoeu.jscript.compilation.FunctionNode;
|
||||
import me.topchetoeu.jscript.compilation.JavaScript;
|
||||
import me.topchetoeu.jscript.compilation.Node;
|
||||
import me.topchetoeu.jscript.compilation.Parameters;
|
||||
import me.topchetoeu.jscript.compilation.members.AssignShorthandNode;
|
||||
import me.topchetoeu.jscript.compilation.members.FieldMemberNode;
|
||||
import me.topchetoeu.jscript.compilation.members.MethodMemberNode;
|
||||
import me.topchetoeu.jscript.compilation.members.PropertyMemberNode;
|
||||
import me.topchetoeu.jscript.compilation.patterns.AssignTarget;
|
||||
import me.topchetoeu.jscript.compilation.patterns.AssignTargetLike;
|
||||
import me.topchetoeu.jscript.compilation.patterns.ObjectAssignable;
|
||||
import me.topchetoeu.jscript.compilation.patterns.Pattern;
|
||||
import me.topchetoeu.jscript.compilation.patterns.ObjectDestructor.Member;
|
||||
import me.topchetoeu.jscript.compilation.patterns.ObjectPattern;
|
||||
import me.topchetoeu.jscript.compilation.values.constants.NumberNode;
|
||||
import me.topchetoeu.jscript.compilation.values.constants.StringNode;
|
||||
import me.topchetoeu.jscript.compilation.values.operations.AssignNode;
|
||||
|
||||
public class ObjectNode extends Node implements AssignTargetLike {
|
||||
public static class PropertyMemberNode extends FunctionNode {
|
||||
public final Node key;
|
||||
public final Pattern argument;
|
||||
|
||||
@Override public String name() {
|
||||
if (key instanceof StringNode str) {
|
||||
if (isGetter()) return "get " + str.value;
|
||||
else return "set " + str.value;
|
||||
}
|
||||
else return null;
|
||||
}
|
||||
|
||||
public boolean isGetter() { return argument == null; }
|
||||
public boolean isSetter() { return argument != null; }
|
||||
|
||||
@Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) {
|
||||
key.compile(target, true);
|
||||
|
||||
var id = target.addChild(compileBody(target, name, null));
|
||||
target.add(_i -> Instruction.loadFunc(id, true, false, false, name, captures(id, target)));
|
||||
|
||||
target.add(Instruction.defProp(isSetter()));
|
||||
}
|
||||
|
||||
public PropertyMemberNode(Location loc, Location end, Node key, Pattern argument, CompoundNode body) {
|
||||
super(loc, end, argument == null ? new Parameters(Arrays.asList()) : new Parameters(Arrays.asList(argument)), body);
|
||||
this.key = key;
|
||||
this.argument = argument;
|
||||
}
|
||||
|
||||
public static ParseRes<PropertyMemberNode> parse(Source src, int i) {
|
||||
var n = Parsing.skipEmpty(src, i);
|
||||
var loc = src.loc(i + n);
|
||||
|
||||
var access = Parsing.parseIdentifier(src, i + n);
|
||||
if (!access.isSuccess()) return ParseRes.failed();
|
||||
if (!access.result.equals("get") && !access.result.equals("set")) return ParseRes.failed();
|
||||
n += access.n;
|
||||
|
||||
var name = parsePropName(src, i + n);
|
||||
if (!name.isSuccess()) return name.chainError(src.loc(i + n), "Expected a property name after '" + access + "'");
|
||||
n += name.n;
|
||||
|
||||
var params = Parameters.parseParameters(src, i + n);
|
||||
if (!params.isSuccess()) return params.chainError(src.loc(i + n), "Expected an argument list");
|
||||
if (access.result.equals("get") && params.result.params.size() != 0) return ParseRes.error(src.loc(i + n), "Getter must not have any parameters");
|
||||
if (access.result.equals("set") && params.result.params.size() != 1) return ParseRes.error(src.loc(i + n), "Setter must have exactly one parameter");
|
||||
if (params.result.rest != null) return ParseRes.error(params.result.rest.loc(), "Property members may not have rest arguments");
|
||||
n += params.n;
|
||||
|
||||
var body = CompoundNode.parse(src, i + n);
|
||||
if (!body.isSuccess()) return body.chainError(src.loc(i + n), "Expected a compound statement for property accessor.");
|
||||
n += body.n;
|
||||
|
||||
var end = src.loc(i + n - 1);
|
||||
|
||||
return ParseRes.res(new PropertyMemberNode(
|
||||
loc, end, name.result, access.result.equals("get") ? null : params.result.params.get(0), body.result
|
||||
), n);
|
||||
}
|
||||
}
|
||||
public static class MethodMemberNode extends FunctionNode {
|
||||
public final Node key;
|
||||
|
||||
@Override public String name() {
|
||||
if (key instanceof StringNode str) return str.value;
|
||||
else return null;
|
||||
}
|
||||
|
||||
@Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) {
|
||||
key.compile(target, true);
|
||||
|
||||
var id = target.addChild(compileBody(target, name, null));
|
||||
target.add(_i -> Instruction.loadFunc(id, true, false, false, name, captures(id, target)));
|
||||
|
||||
target.add(Instruction.defField());
|
||||
}
|
||||
|
||||
public MethodMemberNode(Location loc, Location end, Node key, Parameters params, CompoundNode body) {
|
||||
super(loc, end, params, body);
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public static ParseRes<MethodMemberNode> parse(Source src, int i) {
|
||||
var n = Parsing.skipEmpty(src, i);
|
||||
var loc = src.loc(i + n);
|
||||
|
||||
var name = parsePropName(src, i + n);
|
||||
if (!name.isSuccess()) return name.chainError();
|
||||
n += name.n;
|
||||
|
||||
var params = Parameters.parseParameters(src, i + n);
|
||||
if (!params.isSuccess()) return params.chainError(src.loc(i + n), "Expected an argument list");
|
||||
n += params.n;
|
||||
|
||||
var body = CompoundNode.parse(src, i + n);
|
||||
if (!body.isSuccess()) return body.chainError(src.loc(i + n), "Expected a compound statement for property accessor.");
|
||||
n += body.n;
|
||||
|
||||
var end = src.loc(i + n - 1);
|
||||
|
||||
return ParseRes.res(new MethodMemberNode(
|
||||
loc, end, name.result, params.result, body.result
|
||||
), n);
|
||||
}
|
||||
}
|
||||
public static class FieldMemberNode extends Node {
|
||||
public final Node key;
|
||||
public final Node value;
|
||||
|
||||
@Override public void compile(CompileResult target, boolean pollute) {
|
||||
key.compile(target, true);
|
||||
|
||||
if (value == null) target.add(Instruction.pushUndefined());
|
||||
else value.compile(target, true);
|
||||
|
||||
target.add(Instruction.defField());
|
||||
}
|
||||
|
||||
public FieldMemberNode(Location loc, Node key, Node value) {
|
||||
super(loc);
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static ParseRes<FieldMemberNode> parseObject(Source src, int i) {
|
||||
var n = Parsing.skipEmpty(src, i);
|
||||
var loc = src.loc(i + n);
|
||||
|
||||
var name = parsePropName(src, i + n);
|
||||
if (!name.isSuccess()) return name.chainError();
|
||||
n += name.n;
|
||||
n += Parsing.skipEmpty(src, i + n);
|
||||
|
||||
if (!src.is(i + n, ":")) return ParseRes.failed();
|
||||
n++;
|
||||
|
||||
var value = JavaScript.parseExpression(src, i + n, 2);
|
||||
if (!value.isSuccess()) return value.chainError(src.loc(i + n), "Expected a value");
|
||||
n += value.n;
|
||||
|
||||
return ParseRes.res(new FieldMemberNode(loc, name.result, value.result), n);
|
||||
}
|
||||
|
||||
public static ParseRes<FieldMemberNode> parseShorthand(Source src, int i) {
|
||||
var n = Parsing.skipEmpty(src, i);
|
||||
var loc = src.loc(i + n);
|
||||
|
||||
var var = VariableNode.parse(src, i + n);
|
||||
if (!var.isSuccess()) return var.chainError();
|
||||
n += var.n;
|
||||
|
||||
return ParseRes.res(new FieldMemberNode(loc, new StringNode(loc, var.result.name), var.result), n);
|
||||
}
|
||||
|
||||
public static ParseRes<FieldMemberNode> parseClass(Source src, int i) {
|
||||
var n = Parsing.skipEmpty(src, i);
|
||||
var loc = src.loc(i + n);
|
||||
|
||||
var name = parsePropName(src, i + n);
|
||||
if (!name.isSuccess()) return name.chainError();
|
||||
n += name.n;
|
||||
n += Parsing.skipEmpty(src, i + n);
|
||||
|
||||
if (!src.is(i + n, "=")) {
|
||||
var end = JavaScript.parseStatement(src, i + n);
|
||||
if (!end.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected an end of statement or a field initializer");
|
||||
n += end.n;
|
||||
|
||||
return ParseRes.res(new FieldMemberNode(loc, name.result, null), n);
|
||||
}
|
||||
n++;
|
||||
|
||||
var value = JavaScript.parseExpression(src, i + n, 2);
|
||||
if (!value.isSuccess()) return value.chainError(src.loc(i + n), "Expected a value");
|
||||
n += value.n;
|
||||
|
||||
return ParseRes.res(new FieldMemberNode(loc, name.result, value.result), n);
|
||||
}
|
||||
}
|
||||
public static class AssignShorthandNode extends Node {
|
||||
public final Node key;
|
||||
public final AssignTarget target;
|
||||
public final Node value;
|
||||
|
||||
@Override public void compile(CompileResult target, boolean pollute) {
|
||||
throw new SyntaxException(loc(), "Unexpected assign shorthand in non-destructor context");
|
||||
}
|
||||
|
||||
public AssignShorthandNode(Location loc, Node key, AssignTarget target, Node value) {
|
||||
super(loc);
|
||||
this.key = key;
|
||||
this.target = target;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public AssignTarget target() {
|
||||
return new AssignNode(loc(), target, value);
|
||||
}
|
||||
|
||||
public static ParseRes<AssignShorthandNode> parse(Source src, int i) {
|
||||
var n = Parsing.skipEmpty(src, i);
|
||||
var loc = src.loc(i + n);
|
||||
|
||||
var var = VariableNode.parse(src, i + n);
|
||||
if (!var.isSuccess()) return var.chainError();
|
||||
n += var.n;
|
||||
n += Parsing.skipEmpty(src, i + n);
|
||||
|
||||
if (!src.is(i + n, "=")) return ParseRes.failed();
|
||||
n++;
|
||||
|
||||
var value = JavaScript.parseExpression(src, i + n, 2);
|
||||
if (!value.isSuccess()) return value.chainError(src.loc(i + n), "Expected a shorthand initializer");
|
||||
n += value.n;
|
||||
|
||||
return ParseRes.res(new AssignShorthandNode(loc, new StringNode(loc, var.result.name), var.result, value.result), n);
|
||||
}
|
||||
}
|
||||
|
||||
public final List<Node> members;
|
||||
|
||||
// TODO: Implement spreading into object
|
||||
@ -288,26 +64,22 @@ public class ObjectNode extends Node implements AssignTargetLike {
|
||||
|
||||
@Override public void compile(CompileResult target, boolean pollute) {
|
||||
target.add(Instruction.loadObj());
|
||||
|
||||
for (var el : members) {
|
||||
target.add(Instruction.dup());
|
||||
el.compile(target, false);
|
||||
}
|
||||
for (var el : members) el.compile(target, true);
|
||||
}
|
||||
|
||||
@Override public AssignTarget toAssignTarget() {
|
||||
var newMembers = new LinkedList<Member<AssignTarget>>();
|
||||
var newMembers = new LinkedList<ObjectPattern.Member>();
|
||||
|
||||
for (var el : members) {
|
||||
if (el instanceof FieldMemberNode field) {
|
||||
if (field.value instanceof AssignTargetLike target) newMembers.add(new Member<>(field.key, target.toAssignTarget()));
|
||||
if (field.value instanceof AssignTargetLike target) newMembers.add(new ObjectPattern.Member(field.key, target.toAssignTarget()));
|
||||
else throw new SyntaxException(field.value.loc(), "Expected an assignable in deconstructor");
|
||||
}
|
||||
else if (el instanceof AssignShorthandNode shorthand) newMembers.add(new Member<>(shorthand.key, shorthand.target()));
|
||||
else if (el instanceof AssignShorthandNode shorthand) newMembers.add(new ObjectPattern.Member(shorthand.key, shorthand.target()));
|
||||
else throw new SyntaxException(el.loc(), "Unexpected member in deconstructor");
|
||||
}
|
||||
|
||||
return new ObjectAssignable(loc(), newMembers);
|
||||
return new ObjectPattern(loc(), newMembers);
|
||||
}
|
||||
|
||||
public ObjectNode(Location loc, List<Node> map) {
|
||||
|
@ -26,17 +26,18 @@ public class VariableNode extends Node implements Pattern, ChangeTarget {
|
||||
}
|
||||
|
||||
@Override public void destructDeclResolve(CompileResult target) {
|
||||
target.scope.define(new Variable(name, false), loc());
|
||||
var i = target.scope.define(new Variable(name, false), loc());
|
||||
if (i != null) target.add(_i -> i.index().toUndefinedInit(false));
|
||||
}
|
||||
|
||||
@Override public void afterAssign(CompileResult target, boolean pollute) {
|
||||
target.add(VariableNode.toSet(target, loc(), name, pollute, false));
|
||||
target.add(VariableNode.toSet(target, loc(), name, pollute));
|
||||
}
|
||||
|
||||
@Override public void declare(CompileResult target, DeclarationType decl) {
|
||||
@Override public void declare(CompileResult target, DeclarationType decl, boolean lateInitializer) {
|
||||
if (decl != null) {
|
||||
if (decl.strict) target.scope.defineStrict(new Variable(name, decl.readonly), loc());
|
||||
else target.scope.define(new Variable(name, decl.readonly), loc());
|
||||
var i = target.scope.define(decl, name, loc());
|
||||
target.add(_i -> i.index().toUndefinedInit(decl.strict));
|
||||
}
|
||||
else target.add(_i -> {
|
||||
var i = target.scope.get(name, false);
|
||||
@ -48,52 +49,60 @@ public class VariableNode extends Node implements Pattern, ChangeTarget {
|
||||
|
||||
@Override public void destruct(CompileResult target, DeclarationType decl, boolean shouldDeclare) {
|
||||
if (!shouldDeclare || decl == null) {
|
||||
target.add(VariableNode.toSet(target, loc(), name, false, shouldDeclare));
|
||||
if (shouldDeclare) target.add(VariableNode.toInit(target, loc(), name));
|
||||
else target.add(VariableNode.toInit(target, loc(), name));
|
||||
}
|
||||
else {
|
||||
if (decl == DeclarationType.VAR && target.scope.has(name, false)) throw new SyntaxException(loc(), "Duplicate parameter name not allowed");
|
||||
var v = target.scope.define(decl, name, loc());
|
||||
target.add(_i -> v.index().toSet(false));
|
||||
target.add(_i -> v.index().toInit());
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void compile(CompileResult target, boolean pollute) {
|
||||
var i = target.scope.get(name, false);
|
||||
|
||||
if (i == null) {
|
||||
target.add(_i -> {
|
||||
if (target.scope.has(name, false)) return Instruction.throwSyntax(loc(), String.format("Cannot access '%s' before initialization", name));
|
||||
return Instruction.globGet(name, false);
|
||||
});
|
||||
|
||||
if (!pollute) target.add(Instruction.discard());
|
||||
}
|
||||
else if (pollute) target.add(_i -> i.index().toGet());
|
||||
target.add(toGet(target, loc(), name, true, false));
|
||||
}
|
||||
|
||||
public static IntFunction<Instruction> toGet(CompileResult target, Location loc, String name, boolean forceGet) {
|
||||
var i = target.scope.get(name, false);
|
||||
public static IntFunction<Instruction> toGet(CompileResult target, Location loc, String name, boolean keep, boolean forceGet) {
|
||||
var oldI = target.scope.get(name, false);
|
||||
|
||||
if (i == null) return _i -> {
|
||||
if (target.scope.has(name, false)) return Instruction.throwSyntax(loc, String.format("Cannot access '%s' before initialization", name));
|
||||
else return Instruction.globGet(name, forceGet);
|
||||
if (oldI != null) {
|
||||
if (keep) return _i -> oldI.index().toGet();
|
||||
else return _i -> Instruction.nop();
|
||||
}
|
||||
else return _i -> {
|
||||
var newI = target.scope.get(name, false);
|
||||
|
||||
if (newI == null) return Instruction.globGet(name, forceGet);
|
||||
else if (keep) return newI.index().toGet();
|
||||
else return Instruction.nop();
|
||||
};
|
||||
else return _i -> i.index().toGet();
|
||||
}
|
||||
public static IntFunction<Instruction> toGet(CompileResult target, Location loc, String name) {
|
||||
return toGet(target, loc, name, false);
|
||||
return toGet(target, loc, name, true, false);
|
||||
}
|
||||
|
||||
public static IntFunction<Instruction> toInit(CompileResult target, Location loc, String name) {
|
||||
var oldI = target.scope.get(name, false);
|
||||
|
||||
public static IntFunction<Instruction> toSet(CompileResult target, Location loc, String name, boolean keep, boolean define) {
|
||||
if (oldI != null) return _i -> oldI.index().toInit();
|
||||
else return _i -> {
|
||||
var i = target.scope.get(name, false);
|
||||
|
||||
if (i == null) return _i -> {
|
||||
if (target.scope.has(name, false)) return Instruction.throwSyntax(loc, String.format("Cannot access '%s' before initialization", name));
|
||||
else return Instruction.globSet(name, keep, define);
|
||||
if (i == null) return Instruction.globSet(name, false, true);
|
||||
else return i.index().toInit();
|
||||
};
|
||||
}
|
||||
public static IntFunction<Instruction> toSet(CompileResult target, Location loc, String name, boolean keep) {
|
||||
var oldI = target.scope.get(name, false);
|
||||
|
||||
if (oldI != null) return _i -> oldI.index().toSet(keep);
|
||||
else return _i -> {
|
||||
var i = target.scope.get(name, false);
|
||||
|
||||
if (i == null) return Instruction.globSet(name, keep, false);
|
||||
else return i.index().toSet(keep);
|
||||
};
|
||||
else if (!define && i.readonly) return _i -> Instruction.throwSyntax(new SyntaxException(loc, "Assignment to constant variable"));
|
||||
else return _i -> i.index().toSet(keep);
|
||||
}
|
||||
|
||||
public VariableNode(Location loc, String name) {
|
||||
|
@ -16,7 +16,7 @@ public class TypeofNode extends Node {
|
||||
|
||||
@Override public void compile(CompileResult target, boolean pollute) {
|
||||
if (value instanceof VariableNode varNode) {
|
||||
target.add(VariableNode.toGet(target, varNode.loc(), varNode.name, true));
|
||||
target.add(VariableNode.toGet(target, varNode.loc(), varNode.name, true, true));
|
||||
if (pollute) target.add(Instruction.typeof());
|
||||
else target.add(Instruction.discard());
|
||||
|
||||
|
@ -18,11 +18,11 @@ public class VariableAssignNode extends Node {
|
||||
target.add(VariableNode.toGet(target, loc(), name));
|
||||
FunctionNode.compileWithName(value, target, true, name);
|
||||
target.add(Instruction.operation(operation));
|
||||
target.add(VariableNode.toSet(target, loc(), name, pollute, false));
|
||||
target.add(VariableNode.toSet(target, loc(), name, pollute));
|
||||
}
|
||||
else {
|
||||
FunctionNode.compileWithName(value, target, true, name);
|
||||
target.add(VariableNode.toSet(target, loc(), name, pollute, false));
|
||||
target.add(VariableNode.toSet(target, loc(), name, pollute));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
package me.topchetoeu.jscript.runtime;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Stack;
|
||||
import java.util.concurrent.CancellationException;
|
||||
|
||||
@ -361,15 +360,7 @@ public final class Frame {
|
||||
this.argsVal = new ArgumentsValue(this, args);
|
||||
this.captures = func.captures;
|
||||
|
||||
var i = 0;
|
||||
|
||||
this.locals = new Value[func.body.localsN];
|
||||
Arrays.fill(locals, Value.UNDEFINED);
|
||||
|
||||
this.capturables = new Value[func.body.capturablesN][1];
|
||||
|
||||
for (i = 0; i < func.body.capturablesN; i++) {
|
||||
this.capturables[i][0] = Value.UNDEFINED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -90,7 +90,7 @@ public class InstructionRunner {
|
||||
var members = new ArrayList<>(val.getMembers(env, instr.get(0), instr.get(1)));
|
||||
Collections.reverse(members);
|
||||
|
||||
frame.push(null);
|
||||
frame.push(Value.UNDEFINED);
|
||||
|
||||
for (var el : members) {
|
||||
var obj = new ObjectValue();
|
||||
@ -147,7 +147,9 @@ public class InstructionRunner {
|
||||
private static Value execLoadVar(Environment env, Instruction instr, Frame frame) {
|
||||
int i = instr.get(0);
|
||||
|
||||
frame.push(frame.getVar(i));
|
||||
var res = frame.getVar(i);
|
||||
if (res == null) throw EngineException.ofSyntax("Uninitialized variable");
|
||||
frame.push(res);
|
||||
frame.codePtr++;
|
||||
|
||||
return null;
|
||||
@ -285,6 +287,7 @@ public class InstructionRunner {
|
||||
var val = (boolean)instr.get(1) ? frame.peek() : frame.pop();
|
||||
int i = instr.get(0);
|
||||
|
||||
if (!(boolean)instr.get(2) && frame.getVar(i) == null) throw EngineException.ofSyntax("Uninitialized variable");
|
||||
frame.setVar(i, val);
|
||||
frame.codePtr++;
|
||||
|
||||
@ -516,21 +519,21 @@ public class InstructionRunner {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Value execStackAlloc(Environment env, Instruction instr, Frame frame) {
|
||||
int offset = instr.get(0);
|
||||
int n = instr.get(1);
|
||||
|
||||
for (var i = offset; i < n; i++) frame.capturables[i] = new Value[] { Value.UNDEFINED };
|
||||
private static Value execVarInit(Environment env, Instruction instr, Frame frame) {
|
||||
if ((boolean)instr.get(1) || frame.getVar(instr.get(0)) == null) {
|
||||
frame.setVar(instr.get(0), Value.UNDEFINED);
|
||||
}
|
||||
|
||||
frame.codePtr++;
|
||||
return null;
|
||||
}
|
||||
private static Value execStackRealloc(Environment env, Instruction instr, Frame frame) {
|
||||
int offset = instr.get(0);
|
||||
int n = instr.get(1);
|
||||
|
||||
for (var i = offset; i < n; i++) frame.capturables[i] = new Value[] { frame.capturables[i][0] };
|
||||
|
||||
private static Value execVarFree(Environment env, Instruction instr, Frame frame) {
|
||||
frame.locals[(int)instr.get(0)] = null;
|
||||
frame.codePtr++;
|
||||
return null;
|
||||
}
|
||||
private static Value execCapFree(Environment env, Instruction instr, Frame frame) {
|
||||
frame.capturables[(int)instr.get(0) - frame.locals.length] = new Value[1];
|
||||
frame.codePtr++;
|
||||
return null;
|
||||
}
|
||||
@ -592,8 +595,9 @@ public class InstructionRunner {
|
||||
case GLOB_GET: return exexGlobGet(env, instr, frame);
|
||||
case GLOB_SET: return exexGlobSet(env, instr, frame);
|
||||
|
||||
case STACK_ALLOC: return execStackAlloc(env, instr, frame);
|
||||
case STACK_REALLOC: return execStackRealloc(env, instr, frame);
|
||||
case VAR_INIT: return execVarInit(env, instr, frame);
|
||||
case VAR_FREE: return execVarFree(env, instr, frame);
|
||||
case CAP_FREE: return execCapFree(env, instr, frame);
|
||||
|
||||
default: throw EngineException.ofSyntax("Invalid instruction " + instr.type.name() + ".");
|
||||
}
|
||||
|
@ -254,11 +254,6 @@ public class SimpleRepl {
|
||||
res.defineOwnMember(env, "parse", new NativeFunction(args -> {
|
||||
return JSONConverter.toJs(JSON.parse(null, args.get(0).toString(env)));
|
||||
}));
|
||||
res.defineOwnMember(env, "setConstructable", new NativeFunction(args -> {
|
||||
var func = (FunctionValue)args.get(0);
|
||||
func.enableNew = args.get(1).toBoolean();
|
||||
return Value.UNDEFINED;
|
||||
}));
|
||||
res.defineOwnMember(env, "invokeType", new NativeFunction(args -> {
|
||||
if (((ArgumentsValue)args.get(0)).frame.isNew) return StringValue.of("new");
|
||||
else return StringValue.of("call");
|
||||
|
@ -200,7 +200,7 @@ public abstract class Value {
|
||||
public final Value getMemberOrNull(Environment env, KeyCache key) {
|
||||
for (Value obj = this; obj != null; obj = obj.getPrototype(env)) {
|
||||
var member = obj.getOwnMember(env, key);
|
||||
if (member != null) return member.get(env, obj);
|
||||
if (member != null) return member.get(env, this);
|
||||
}
|
||||
|
||||
return null;
|
||||
@ -240,7 +240,7 @@ public abstract class Value {
|
||||
for (Value obj = this; obj != null; obj = obj.getPrototype(env)) {
|
||||
var member = obj.getOwnMember(env, key);
|
||||
if (member != null && (member instanceof PropertyMember || obj == this)) {
|
||||
if (member.set(env, val, obj)) {
|
||||
if (member.set(env, val, this)) {
|
||||
if (val instanceof FunctionValue) ((FunctionValue)val).setName(key.toString(env));
|
||||
return true;
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package me.topchetoeu.jscript.runtime.values.functions;
|
||||
|
||||
import me.topchetoeu.jscript.common.environment.Environment;
|
||||
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
|
||||
import me.topchetoeu.jscript.runtime.values.KeyCache;
|
||||
import me.topchetoeu.jscript.runtime.values.Member;
|
||||
import me.topchetoeu.jscript.runtime.values.Value;
|
||||
@ -50,7 +51,10 @@ public abstract class FunctionValue extends ObjectValue {
|
||||
@Override public String toString() { return String.format("function %s(...)", name); }
|
||||
@Override public Value call(Environment ext, boolean isNew, String name, Value thisArg, Value ...args) {
|
||||
if (isNew && !enableNew) super.call(ext, isNew, name, thisArg, args);
|
||||
if (!isNew && !enableCall) super.call(ext, isNew, name, thisArg, args);
|
||||
if (!isNew && !enableCall) {
|
||||
if (name == null || name.equals("")) name = "(intermediate value)";
|
||||
throw EngineException.ofType(name + " is not invokable");
|
||||
}
|
||||
|
||||
return onCall(ext, isNew, name, thisArg, args);
|
||||
}
|
||||
|
@ -55,6 +55,7 @@ public abstract class ArrayLikeValue extends ObjectValue {
|
||||
@Override public Member getOwnMember(Environment env, KeyCache key) {
|
||||
var res = super.getOwnMember(env, key);
|
||||
if (res != null) return res;
|
||||
if (key.isSymbol()) return null;
|
||||
|
||||
var num = key.toNumber(env);
|
||||
var i = key.toInt(env);
|
||||
@ -67,6 +68,7 @@ public abstract class ArrayLikeValue extends ObjectValue {
|
||||
if (!(member instanceof FieldMember) || super.getOwnMember(env, key) != null) return super.defineOwnMember(env, key, member);
|
||||
if (!getState().writable) return false;
|
||||
|
||||
if (!key.isSymbol()) {
|
||||
var num = key.toNumber(env);
|
||||
var i = key.toInt(env);
|
||||
|
||||
@ -74,11 +76,13 @@ public abstract class ArrayLikeValue extends ObjectValue {
|
||||
if (!getState().extendable && !has(i)) return false;
|
||||
if (set(env, i, ((FieldMember)member).get(env, this))) return true;
|
||||
}
|
||||
}
|
||||
|
||||
return super.defineOwnMember(env, key, member);
|
||||
}
|
||||
@Override public boolean deleteOwnMember(Environment env, KeyCache key) {
|
||||
if (!super.deleteOwnMember(env, key)) return false;
|
||||
if (key.isSymbol()) return true;
|
||||
|
||||
var num = key.toNumber(env);
|
||||
var i = key.toInt(env);
|
||||
|
@ -54,7 +54,7 @@ public class ArrayValue extends ArrayLikeValue implements Iterable<Value> {
|
||||
@Override public boolean remove(int i) {
|
||||
if (i < 0 || i >= values.length) return true;
|
||||
values[i] = null;
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void shrink(int n) {
|
||||
|
@ -72,7 +72,10 @@ public class ObjectValue extends Value {
|
||||
@Override public final void freeze() { state = State.FROZEN; }
|
||||
|
||||
@Override public Member getOwnMember(Environment env, KeyCache key) {
|
||||
if (symbolMembers.size() > 0 && key.isSymbol()) return symbolMembers.get(key.toSymbol());
|
||||
if (key.isSymbol()) {
|
||||
if (symbolMembers.size() > 0) return symbolMembers.get(key.toSymbol());
|
||||
else return null;
|
||||
}
|
||||
else if (members.size() > 0) return members.get(key.toString(env));
|
||||
else return null;
|
||||
}
|
||||
|
@ -40,6 +40,7 @@ public final class StringValue extends PrimitiveValue {
|
||||
@Override public ObjectValue getPrototype(Environment env) { return env.get(STRING_PROTO); }
|
||||
|
||||
@Override public Member getOwnMember(Environment env, KeyCache key) {
|
||||
if (!key.isSymbol()) {
|
||||
var num = key.toNumber(env);
|
||||
var i = key.toInt(env);
|
||||
|
||||
@ -49,7 +50,9 @@ public final class StringValue extends PrimitiveValue {
|
||||
else if (key.toString(env).equals("length")) {
|
||||
return FieldMember.of(this, NumberValue.of(value.length()), false, false, false);
|
||||
}
|
||||
else return super.getOwnMember(env, key);
|
||||
}
|
||||
|
||||
return super.getOwnMember(env, key);
|
||||
}
|
||||
|
||||
@Override public Set<String> getOwnMembers(Environment env, boolean onlyEnumerable) {
|
||||
|
@ -1,3 +1,5 @@
|
||||
// return;
|
||||
|
||||
const target = arguments[0];
|
||||
const primordials = arguments[1];
|
||||
|
||||
@ -58,8 +60,7 @@ const undefined = ({}).definitelyDefined;
|
||||
|
||||
target.undefined = undefined;
|
||||
|
||||
const unwrapThis = (self, type, constr, name, arg, defaultVal) => {
|
||||
if (arg == null) arg = "this";
|
||||
const unwrapThis = (self, type, constr, name, arg = "this", defaultVal) => {
|
||||
if (typeof self === type) return self;
|
||||
if (self instanceof constr && valueKey in self) self = self[valueKey];
|
||||
if (typeof self === type) return self;
|
||||
@ -70,14 +71,31 @@ const unwrapThis = (self, type, constr, name, arg, defaultVal) => {
|
||||
|
||||
const wrapIndex = (i, len) => {};
|
||||
|
||||
const Symbol = (name = "") => symbol.makeSymbol(name);
|
||||
class Symbol {
|
||||
get description() {
|
||||
return symbol.getSymbolDescriptor(unwrapThis(this, "symbol", Symbol, "Symbol.prototype.description"));
|
||||
}
|
||||
toString() {
|
||||
return "Symbol(" + unwrapThis(this, "symbol", Symbol, "Symbol.prototype.toString").description + ")";
|
||||
}
|
||||
valueOf() {
|
||||
return unwrapThis(this, "symbol", Symbol, "Symbol.prototype.valueOf");
|
||||
}
|
||||
|
||||
defineField(Symbol, "for", true, false, true, function(name) {
|
||||
constructor(name = "") {
|
||||
return symbol.makeSymbol(name);
|
||||
}
|
||||
|
||||
static for(name) {
|
||||
return symbol.getSymbol(name + "");
|
||||
});
|
||||
defineField(Symbol, "keyFor", true, false, true, function(value) {
|
||||
}
|
||||
static keyFor(value) {
|
||||
return symbol.getSymbolKey(unwrapThis(value, "symbol", Symbol, "Symbol.keyFor"));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
setCallable(Symbol, true);
|
||||
setConstructable(Symbol, false);
|
||||
|
||||
defineField(Symbol, "asyncIterator", false, false, false, Symbol("Symbol.asyncIterator"));
|
||||
defineField(Symbol, "iterator", false, false, false, Symbol("Symbol.iterator"));
|
||||
@ -87,61 +105,60 @@ defineField(Symbol, "replace", false, false, false, Symbol("Symbol.replace"));
|
||||
defineField(Symbol, "search", false, false, false, Symbol("Symbol.search"));
|
||||
defineField(Symbol, "split", false, false, false, Symbol("Symbol.split"));
|
||||
defineField(Symbol, "toStringTag", false, false, false, Symbol("Symbol.toStringTag"));
|
||||
defineField(Symbol, "prototype", false, false, false, {});
|
||||
|
||||
defineProperty(Symbol.prototype, "description", false, true, function () {
|
||||
return symbol.getSymbolDescription(unwrapThis(this, "symbol", Symbol, "Symbol.prototype.description"));
|
||||
}, undefined);
|
||||
defineField(Symbol.prototype, "toString", true, false, true, function() {
|
||||
return "Symbol(" + unwrapThis(this, "symbol", Symbol, "Symbol.prototype.toString").description + ")";
|
||||
});
|
||||
defineField(Symbol.prototype, "valueOf", true, false, true, function() {
|
||||
return unwrapThis(this, "symbol", Symbol, "Symbol.prototype.valueOf");
|
||||
});
|
||||
|
||||
Symbol();
|
||||
target.Symbol = Symbol;
|
||||
|
||||
const Number = function(value) {
|
||||
class Number {
|
||||
toString() {
|
||||
return "" + unwrapThis(this, "number", Number, "Number.prototype.toString");
|
||||
}
|
||||
valueOf() {
|
||||
return unwrapThis(this, "number", Number, "Number.prototype.toString");
|
||||
}
|
||||
|
||||
constructor(value) {
|
||||
if (invokeType(arguments) === "call") {
|
||||
if (arguments.length === 0) return 0;
|
||||
else return +value;
|
||||
}
|
||||
|
||||
this[valueKey] = target.Number(value);
|
||||
};
|
||||
}
|
||||
|
||||
defineField(Number, "isFinite", true, false, true, function(value) {
|
||||
static isFinite(value) {
|
||||
value = unwrapThis(value, "number", Number, "Number.isFinite", "value", undefined);
|
||||
|
||||
if (value === undefined || value !== value) return false;
|
||||
if (value === Infinity || value === -Infinity) return false;
|
||||
|
||||
return true;
|
||||
});
|
||||
defineField(Number, "isInteger", true, false, true, function(value) {
|
||||
}
|
||||
static isInteger(value) {
|
||||
value = unwrapThis(value, "number", Number, "Number.isInteger", "value", undefined);
|
||||
if (value === undefined) return false;
|
||||
return number.parseInt(value) === value;
|
||||
});
|
||||
defineField(Number, "isNaN", true, false, true, function(value) {
|
||||
}
|
||||
static isNaN(value) {
|
||||
return number.isNaN(value);
|
||||
});
|
||||
defineField(Number, "isSafeInteger", true, false, true, function(value) {
|
||||
}
|
||||
static isSafeInteger(value) {
|
||||
value = unwrapThis(value, "number", Number, "Number.isSafeInteger", "value", undefined);
|
||||
if (value === undefined || number.parseInt(value) !== value) return false;
|
||||
return value >= -9007199254740991 && value <= 9007199254740991;
|
||||
});
|
||||
defineField(Number, "parseFloat", true, false, true, function(value) {
|
||||
}
|
||||
static parseFloat(value) {
|
||||
value = 0 + value;
|
||||
return number.parseFloat(value);
|
||||
});
|
||||
defineField(Number, "parseInt", true, false, true, function(value, radix) {
|
||||
}
|
||||
static parseInt(value, radix) {
|
||||
value = 0 + value;
|
||||
radix = +radix;
|
||||
if (number.isNaN(radix)) radix = 10;
|
||||
|
||||
return number.parseInt(value, radix);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
defineField(Number, "EPSILON", false, false, false, 2.220446049250313e-16);
|
||||
defineField(Number, "MIN_SAFE_INTEGER", false, false, false, -9007199254740991);
|
||||
@ -151,32 +168,36 @@ defineField(Number, "NEGATIVE_INFINITY", false, false, false, -number.Infinity);
|
||||
defineField(Number, "NaN", false, false, false, number.NaN);
|
||||
defineField(Number, "MAX_VALUE", false, false, false, 1.7976931348623157e+308);
|
||||
defineField(Number, "MIN_VALUE", false, false, false, 5e-324);
|
||||
defineField(Number, "prototype", false, false, false, {});
|
||||
|
||||
defineField(Number.prototype, "toString", true, false, true);
|
||||
defineField(Number.prototype, "toString", true, false, true, function() {
|
||||
return "" + unwrapThis(this, "number", Number, "Number.prototype.toString");
|
||||
});
|
||||
defineField(Number.prototype, "valueOf", true, false, true, function() {
|
||||
return unwrapThis(this, "number", Number, "Number.prototype.toString");
|
||||
});
|
||||
|
||||
setCallable(Number, true);
|
||||
target.Number = Number;
|
||||
target.parseInt = Number.parseInt;
|
||||
target.parseFloat = Number.parseFloat;
|
||||
target.NaN = Number.NaN;
|
||||
target.Infinity = Number.POSITIVE_INFINITY;
|
||||
|
||||
const String = function(value) {
|
||||
class String {
|
||||
at(index) {
|
||||
throw "Not implemented :/";
|
||||
return unwrapThis(this, "string", String, "String.prototype.at")[index];
|
||||
}
|
||||
toString() {
|
||||
return unwrapThis(this, "string", String, "String.prototype.toString");
|
||||
}
|
||||
valueOf() {
|
||||
return unwrapThis(this, "string", String, "String.prototype.valueOf");
|
||||
}
|
||||
|
||||
constructor(value) {
|
||||
if (invokeType(arguments) === "call") {
|
||||
if (arguments.length === 0) return "";
|
||||
else return value + "";
|
||||
}
|
||||
|
||||
this[valueKey] = String(value);
|
||||
};
|
||||
}
|
||||
|
||||
defineField(String, "fromCharCode", true, false, true, function() {
|
||||
static fromCharCode() {
|
||||
const res = [];
|
||||
res[arguments.length] = 0;
|
||||
|
||||
@ -185,8 +206,8 @@ defineField(String, "fromCharCode", true, false, true, function() {
|
||||
}
|
||||
|
||||
return stringBuild(res);
|
||||
});
|
||||
defineField(String, "fromCodePoint", true, false, true, function() {
|
||||
}
|
||||
static fromCodePoint() {
|
||||
const res = [];
|
||||
res[arguments.length] = 0;
|
||||
|
||||
@ -195,44 +216,50 @@ defineField(String, "fromCodePoint", true, false, true, function() {
|
||||
}
|
||||
|
||||
return stringBuild(res);
|
||||
});
|
||||
|
||||
defineField(String, "prototype", false, false, false, {});
|
||||
|
||||
defineField(String.prototype, "at", true, false, true, function(index) {
|
||||
throw "Not implemented :/";
|
||||
return unwrapThis(this, "string", String, "String.prototype.at")[index];
|
||||
});
|
||||
defineField(String.prototype, "toString", true, false, true, function() {
|
||||
return unwrapThis(this, "string", String, "String.prototype.toString");
|
||||
});
|
||||
defineField(String.prototype, "valueOf", true, false, true, function() {
|
||||
return unwrapThis(this, "string", String, "String.prototype.valueOf");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
setCallable(String, true);
|
||||
target.String = String;
|
||||
|
||||
const Boolean = function(value) {
|
||||
class Boolean {
|
||||
toString() {
|
||||
return "" + unwrapThis(this, "boolean", Boolean, "Boolean.prototype.toString");
|
||||
}
|
||||
valueOf() {
|
||||
return unwrapThis(this, "boolean", Boolean, "Boolean.prototype.valueOf");
|
||||
}
|
||||
|
||||
constructor(value) {
|
||||
if (invokeType(arguments) === "call") {
|
||||
if (arguments.length === 0) return false;
|
||||
else return !!value;
|
||||
}
|
||||
|
||||
this[valueKey] = Boolean(value);
|
||||
};
|
||||
|
||||
defineField(Boolean, "prototype", false, false, false, {});
|
||||
|
||||
defineField(Boolean.prototype, "toString", true, false, true, function() {
|
||||
return "" + unwrapThis(this, "boolean", Boolean, "Boolean.prototype.toString");
|
||||
});
|
||||
defineField(Boolean.prototype, "valueOf", true, false, true, function() {
|
||||
return unwrapThis(this, "boolean", Boolean, "Boolean.prototype.valueOf");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
setCallable(Boolean, true);
|
||||
target.Boolean = Boolean;
|
||||
|
||||
const Object = function(value) {
|
||||
class Object {
|
||||
toString() {
|
||||
print("2");
|
||||
if (this !== null && this !== undefined && (Symbol.toStringTag in this)) return "[object " + this[Symbol.toStringTag] + "]";
|
||||
else if (typeof this === "number" || this instanceof Number) return "[object Number]";
|
||||
else if (typeof this === "symbol" || this instanceof Symbol) return "[object Symbol]";
|
||||
else if (typeof this === "string" || this instanceof String) return "[object String]";
|
||||
else if (typeof this === "boolean" || this instanceof Boolean) return "[object Boolean]";
|
||||
else if (typeof this === "function") return "[object Function]";
|
||||
else return "[object Object]";
|
||||
}
|
||||
valueOf() {
|
||||
print("1");
|
||||
return this;
|
||||
}
|
||||
|
||||
constructor(value) {
|
||||
if (typeof value === 'object' && value !== null) return value;
|
||||
|
||||
if (typeof value === 'string') return new String(value);
|
||||
@ -246,14 +273,13 @@ const Object = function(value) {
|
||||
}
|
||||
|
||||
const target = this;
|
||||
// TODO: use new.target.prototype as proto
|
||||
if (target == null || typeof target !== 'object') target = {};
|
||||
|
||||
this[valueKey] = Object(value);
|
||||
};
|
||||
}
|
||||
|
||||
defineField(Object, "prototype", false, false, false, setPrototype({}, null));
|
||||
|
||||
defineField(Object, "defineProperty", true, false, true, (obj, key, desc) => {
|
||||
static defineProperty(obj, key, desc) {
|
||||
if (typeof obj !== "object" || obj === null) {
|
||||
print(obj);
|
||||
print(typeof obj);
|
||||
@ -282,24 +308,20 @@ defineField(Object, "defineProperty", true, false, true, (obj, key, desc) => {
|
||||
}
|
||||
|
||||
return obj;
|
||||
});
|
||||
|
||||
defineField(Object.prototype, "toString", true, false, true, function() {
|
||||
if (this !== null && this !== undefined && (Symbol.toStringTag in this)) return "[object " + this[Symbol.toStringTag] + "]";
|
||||
else if (typeof this === "number" || this instanceof Number) return "[object Number]";
|
||||
else if (typeof this === "symbol" || this instanceof Symbol) return "[object Symbol]";
|
||||
else if (typeof this === "string" || this instanceof String) return "[object String]";
|
||||
else if (typeof this === "boolean" || this instanceof Boolean) return "[object Boolean]";
|
||||
else if (typeof this === "function") return "[object Function]";
|
||||
else return "[object Object]";
|
||||
});
|
||||
defineField(Object.prototype, "valueOf", true, false, true, function() {
|
||||
return this;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
setCallable(Object, true);
|
||||
setPrototype(Object.prototype, null);
|
||||
target.Object = Object;
|
||||
|
||||
const Function = function() {
|
||||
class Function {
|
||||
toString() {
|
||||
if (this.name !== "") return "function " + this.name + "(...) { ... }";
|
||||
else return "function (...) { ... }";
|
||||
}
|
||||
|
||||
constructor() {
|
||||
const parts = ["return function annonymous("];
|
||||
|
||||
for (let i = 0; i < arguments.length - 1; i++) {
|
||||
@ -312,27 +334,24 @@ const Function = function() {
|
||||
|
||||
const res = compile(stringBuild(parts))();
|
||||
return res;
|
||||
};
|
||||
|
||||
defineField(Function, "compile", true, false, true, (src = "", options = {}) => {
|
||||
if (options.globals == null) options.globals = [];
|
||||
if (options.wrap == null) options.wrap = true;
|
||||
}
|
||||
|
||||
static compile(src = "", { globals = [], wrap = false } = {}) {
|
||||
const parts = [];
|
||||
|
||||
if (options.wrap) parts[parts.length] = "return (function() {\n";
|
||||
if (options.globals.length > 0) {
|
||||
if (wrap) parts[parts.length] = "return (function() {\n";
|
||||
if (globals.length > 0) {
|
||||
parts[parts.length] = "var ";
|
||||
|
||||
for (let i = 0; i < options.globals.length; i++) {
|
||||
for (let i = 0; i < globals.length; i++) {
|
||||
if (i > 0) parts[parts.length] = ",";
|
||||
parts[parts.length] = options.globals[i];
|
||||
parts[parts.length] = globals[i];
|
||||
}
|
||||
|
||||
parts[parts.length] = ";((g=arguments[0])=>{";
|
||||
|
||||
for (let i = 0; i < options.globals.length; i++) {
|
||||
const name = options.globals[i];
|
||||
for (let i = 0; i < globals.length; i++) {
|
||||
const name = globals[i];
|
||||
parts[parts.length] = name + "=g[" + json.stringify(name) + "];";
|
||||
}
|
||||
|
||||
@ -340,78 +359,97 @@ defineField(Function, "compile", true, false, true, (src = "", options = {}) =>
|
||||
}
|
||||
|
||||
parts[parts.length] = src;
|
||||
if (options.wrap) parts[parts.length] = "\n})(arguments[0])";
|
||||
if (wrap) parts[parts.length] = "\n})(arguments[0])";
|
||||
|
||||
const res = compile(stringBuild(parts));
|
||||
return res;
|
||||
});
|
||||
defineField(Function, "prototype", false, false, false, setPrototype({}, null));
|
||||
|
||||
defineField(Function.prototype, "toString", true, false, true, function() {
|
||||
if (this.name !== "") return "function " + this.name + "(...) { ... }";
|
||||
else return "function (...) { ... }";
|
||||
});
|
||||
defineField(Function.prototype, "valueOf", true, false, true, function() {
|
||||
return this;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
setCallable(Function, true);
|
||||
target.Function = Function;
|
||||
|
||||
// setIntrinsic("spread_obj", target.spread_obj = (target, obj) => {
|
||||
// if (obj === null || obj === undefined) return;
|
||||
// const members = getOwnMembers(obj, true);
|
||||
// const symbols = getOwnSymbolMembers(obj, true);
|
||||
class Array {
|
||||
constructor(len) {
|
||||
if (arguments.length === 1 && typeof len === "number") {
|
||||
const res = [];
|
||||
res.length = len;
|
||||
return res;
|
||||
}
|
||||
// TODO: Implement spreading
|
||||
else throw new Error("Spreading not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
// for (let i = 0; i < members.length; i++) {
|
||||
// const member = members[i];
|
||||
// target[member] = obj[member];
|
||||
// }
|
||||
setCallable(Array, true);
|
||||
target.Array = Array;
|
||||
|
||||
// for (let i = 0; i < symbols.length; i++) {
|
||||
// const member = symbols[i];
|
||||
// target[member] = obj[member];
|
||||
// }
|
||||
// });
|
||||
// setIntrinsic("apply", target.spread_call = (func, self, args) => {
|
||||
// return invoke(func, self, args);
|
||||
// });
|
||||
// setIntrinsic("apply", target.spread_new = (func, args) => {
|
||||
// return invoke(func, null, args);
|
||||
// });
|
||||
|
||||
const Error = function(msg = "") {
|
||||
if (invokeType(arguments) === "call") return new Error(msg);
|
||||
this.message = msg + "";
|
||||
};
|
||||
defineField(Error.prototype, "name", true, false, true, "Error");
|
||||
defineField(Error.prototype, "message", true, false, true, "");
|
||||
defineField(Error.prototype, "toString", true, false, true, function toString() {
|
||||
class Error {
|
||||
toString() {
|
||||
let res = this.name || "Error";
|
||||
|
||||
const msg = this.message;
|
||||
if (msg) res += ": " + msg;
|
||||
|
||||
return res;
|
||||
});
|
||||
}
|
||||
|
||||
constructor (msg = "") {
|
||||
if (invokeType(arguments) === "call") return new Error(msg);
|
||||
this.message = msg + "";
|
||||
}
|
||||
}
|
||||
|
||||
defineField(Error.prototype, "name", true, false, true, "Error");
|
||||
defineField(Error.prototype, "message", true, false, true, "");
|
||||
setCallable(Error, true);
|
||||
target.Error = Error;
|
||||
|
||||
const SyntaxError = function(msg = "") {
|
||||
class SyntaxError {
|
||||
constructor (msg = "") {
|
||||
if (invokeType(arguments) === "call") return new SyntaxError(msg);
|
||||
this.message = msg + "";
|
||||
};
|
||||
defineField(SyntaxError.prototype, "name", true, false, true, "SyntaxError");
|
||||
}
|
||||
}
|
||||
|
||||
defineField(SyntaxError.prototype, "name", true, false, true, "SyntaxError");
|
||||
setPrototype(SyntaxError, Error);
|
||||
setPrototype(SyntaxError.prototype, Error.prototype);
|
||||
|
||||
setCallable(SyntaxError, true);
|
||||
target.SyntaxError = SyntaxError;
|
||||
|
||||
class TypeError {
|
||||
constructor (msg = "") {
|
||||
if (invokeType(arguments) === "call") return new TypeError(msg);
|
||||
this.message = msg + "";
|
||||
}
|
||||
}
|
||||
|
||||
defineField(TypeError.prototype, "name", true, false, true, "TypeError");
|
||||
setPrototype(TypeError, Error);
|
||||
setPrototype(TypeError.prototype, Error.prototype);
|
||||
setCallable(TypeError, true);
|
||||
target.TypeError = TypeError;
|
||||
|
||||
class RangeError {
|
||||
constructor (msg = "") {
|
||||
if (invokeType(arguments) === "call") return new RangeError(msg);
|
||||
this.message = msg + "";
|
||||
}
|
||||
}
|
||||
|
||||
defineField(RangeError.prototype, "name", true, false, true, "RangeError");
|
||||
setPrototype(RangeError, Error);
|
||||
setPrototype(RangeError.prototype, Error.prototype);
|
||||
setCallable(RangeError, true);
|
||||
target.RangeError = RangeError;
|
||||
|
||||
setGlobalPrototype("string", String.prototype);
|
||||
setGlobalPrototype("number", Number.prototype);
|
||||
setGlobalPrototype("boolean", Boolean.prototype);
|
||||
setGlobalPrototype("symbol", Symbol.prototype);
|
||||
setGlobalPrototype("object", Object.prototype);
|
||||
setGlobalPrototype("array", Array.prototype);
|
||||
setGlobalPrototype("function", Function.prototype);
|
||||
setGlobalPrototype("error", Error.prototype);
|
||||
setGlobalPrototype("syntax", SyntaxError.prototype);
|
||||
|
Loading…
Reference in New Issue
Block a user