Skip to content
This repository was archived by the owner on Dec 31, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 22 additions & 11 deletions src/main/java/me/topchetoeu/jscript/common/Instruction.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,10 @@ public static enum Type {
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;
Expand Down Expand Up @@ -409,11 +411,11 @@ public static Instruction dup(int count, int offset) {
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() {
Expand Down Expand Up @@ -463,12 +465,21 @@ public static Instruction operation(Operation op) {
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 varFree(int i) {
return new Instruction(Type.VAR_FREE, i);
}
public static Instruction stackRealloc(int start, int n) {
return new Instruction(Type.STACK_REALLOC, start, start + n);
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();
Expand Down
158 changes: 158 additions & 0 deletions src/main/java/me/topchetoeu/jscript/compilation/ClassNode.java
Original file line number Diff line number Diff line change
@@ -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;
// }
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
25 changes: 25 additions & 0 deletions src/main/java/me/topchetoeu/jscript/compilation/CompileResult.java
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,31 @@ public void setLocationAndDebug(Location loc, BreakpointType type) {
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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,7 @@ public void compile(CompileResult target, boolean pollute, boolean singleEntry,
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) {
Expand All @@ -46,7 +43,7 @@ public void compile(CompileResult target, boolean pollute, boolean singleEntry,
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());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ protected final int[] captures(int id, CompileResult target) {
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;

Expand All @@ -30,6 +32,8 @@ public final CompileResult compileBody(Environment env, FunctionScope scope, boo
.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));
Expand All @@ -50,7 +54,7 @@ public final CompileResult compileBody(Environment env, FunctionScope scope, boo
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
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;
Expand Down Expand Up @@ -63,7 +64,7 @@ private DeclarationType(boolean strict, boolean readonly) {
"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) {
Expand All @@ -88,6 +89,7 @@ public static ParseRes<? extends Node> parseSimple(Source src, int i, boolean st
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,
Expand All @@ -96,7 +98,7 @@ public static ParseRes<? extends Node> parseSimple(Source src, int i, boolean st
ChangeNode::parsePrefixIncrease,
OperationNode::parsePrefix,
ArrayNode::parse,
FunctionArrowNode::parse,
(s, j) -> statement ? ParseRes.failed() : FunctionArrowNode.parse(s, j),
JavaScript::parseParens,
CallNode::parseNew,
TypeofNode::parse,
Expand Down Expand Up @@ -188,6 +190,7 @@ public static ParseRes<? extends Node> parseStatement(Source src, int i) {
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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ public Pair(Pattern destr, Node value, Location location) {
@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);
Expand Down
Loading