From 9ae5e8110972a0d577448387f79b5fdc02836d28 Mon Sep 17 00:00:00 2001 From: WarmthDawn Date: Sun, 26 Jun 2022 12:33:29 +0800 Subject: [PATCH] Add LocalVariableTable for output to support debug --- .../java/stanhebben/zenscript/ZenModule.java | 22 +++-- .../zenscript/compiler/EnvironmentMethod.java | 74 ++++++++++------- .../zenscript/compiler/EnvironmentScope.java | 15 ++++ .../compiler/IEnvironmentMethod.java | 8 +- .../zenclasses/ParsedClassConstructor.java | 57 ++++++++----- .../zenclasses/ParsedZenClass.java | 10 +-- .../zenclasses/ParsedZenClassMethod.java | 38 ++++++--- .../expression/ExpressionFunction.java | 24 +++--- .../expression/ExpressionJavaLambda.java | 11 ++- .../ExpressionJavaLambdaSimpleGeneric.java | 59 +++++++------- .../zenscript/statements/StatementBlock.java | 10 +++ .../statements/StatementForeach.java | 8 +- .../zenscript/statements/StatementVar.java | 5 +- .../zenscript/symbols/SymbolArgument.java | 10 ++- .../zenscript/util/MethodOutput.java | 36 ++++++++- .../localvariabletable/LocalVariable.java | 51 ++++++++++++ .../LocalVariableTable.java | 81 +++++++++++++++++++ 17 files changed, 403 insertions(+), 116 deletions(-) create mode 100644 src/main/java/stanhebben/zenscript/util/localvariabletable/LocalVariable.java create mode 100644 src/main/java/stanhebben/zenscript/util/localvariabletable/LocalVariableTable.java diff --git a/src/main/java/stanhebben/zenscript/ZenModule.java b/src/main/java/stanhebben/zenscript/ZenModule.java index db2d371..6e43d65 100644 --- a/src/main/java/stanhebben/zenscript/ZenModule.java +++ b/src/main/java/stanhebben/zenscript/ZenModule.java @@ -9,6 +9,8 @@ import stanhebben.zenscript.symbols.*; import stanhebben.zenscript.type.ZenType; import stanhebben.zenscript.util.*; +import stanhebben.zenscript.util.localvariabletable.LocalVariable; +import stanhebben.zenscript.util.localvariabletable.LocalVariableTable; import java.io.*; import java.util.*; @@ -114,10 +116,14 @@ public static void compileScripts(String mainFileName, List scrip MethodOutput methodOutput = new MethodOutput(clsScript, Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, function.getKey(), signature, null, null); EnvironmentMethod methodEnvironment = new EnvironmentMethod(methodOutput, environmentScript); + LocalVariableTable localVariableTable = methodEnvironment.getLocalVariableTable(); + localVariableTable.beginScope(); List arguments = function.getValue().getArguments(); for(int i = 0, j = 0; i < arguments.size(); i++) { ParsedFunctionArgument argument = arguments.get(i); - methodEnvironment.putValue(argument.getName(), new SymbolArgument(i + j, argument.getType()), fn.getPosition()); + SymbolArgument symbolArgument = new SymbolArgument(i + j, argument.getType()); + methodEnvironment.putValue(argument.getName(), symbolArgument, fn.getPosition()); + localVariableTable.put(LocalVariable.parameter(argument.getName(), symbolArgument)); if(argument.getType().isLarge()) ++j; } @@ -127,6 +133,8 @@ public static void compileScripts(String mainFileName, List scrip for(Statement statement : statements) { statement.compile(methodEnvironment); } + + localVariableTable.ensureFirstLabel(methodOutput, fn.getPosition()); if(function.getValue().getReturnType() != ZenType.VOID) { if(statements.length > 0 && statements[statements.length - 1] instanceof StatementReturn) { if(((StatementReturn) statements[statements.length - 1]).getExpression() != null) { @@ -140,18 +148,26 @@ public static void compileScripts(String mainFileName, List scrip } else if(statements.length == 0 || !(statements[statements.length - 1] instanceof StatementReturn)) { methodOutput.ret(); } + localVariableTable.endMethod(methodOutput); + localVariableTable.writeLocalVariables(methodOutput); methodOutput.end(); } if(script.getStatements().size() > 0) { MethodOutput scriptOutput = new MethodOutput(clsScript, Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "__script__", "()V", null, null); IEnvironmentMethod functionMethod = new EnvironmentMethod(scriptOutput, environmentScript); + + LocalVariableTable localVariableTable = functionMethod.getLocalVariableTable(); + localVariableTable.beginScope(); // scriptOutput.enableDebug(); scriptOutput.start(); for(Statement statement : script.getStatements()) { statement.compile(functionMethod); } + localVariableTable.ensureFirstLabel(scriptOutput, null); scriptOutput.ret(); + localVariableTable.endMethod(scriptOutput); + localVariableTable.writeLocalVariables(scriptOutput); scriptOutput.end(); mainRun.invokeStatic(script.getClassName().replace('.', '/'), "__script__", "()V"); @@ -204,7 +220,6 @@ public static void compileScripts(String mainFileName, List scrip * @param single file to be compiled * @param environment compile environment * @param baseClassLoader class loader - * * @return compiled module * @throws IOException if the file could not be read */ @@ -239,7 +254,6 @@ public static ZenModule compileScriptFile(File single, IZenCompileEnvironment en * @param name name of the script to be compiled * @param environment compile environment * @param baseClassLoader class loader - * * @return compiled module * @throws IOException if the file could not be read */ @@ -273,7 +287,6 @@ public static ZenModule compileScriptString(String script, String name, IZenComp * @param file zip file * @param subdir subdirectory (use empty string to compile all) * @param environment compile environment - * * @return compiled module * @throws IOException if the file could not be read properly */ @@ -327,7 +340,6 @@ private static void generateDebug(Map classes) throws IOExceptio * Converts a filename into a class name. * * @param filename filename to convert - * * @return class name */ public static String extractClassName(String filename) { diff --git a/src/main/java/stanhebben/zenscript/compiler/EnvironmentMethod.java b/src/main/java/stanhebben/zenscript/compiler/EnvironmentMethod.java index f0ecf85..e6da268 100644 --- a/src/main/java/stanhebben/zenscript/compiler/EnvironmentMethod.java +++ b/src/main/java/stanhebben/zenscript/compiler/EnvironmentMethod.java @@ -7,6 +7,7 @@ import stanhebben.zenscript.symbols.*; import stanhebben.zenscript.type.ZenType; import stanhebben.zenscript.util.*; +import stanhebben.zenscript.util.localvariabletable.LocalVariableTable; import java.lang.reflect.Type; import java.util.*; @@ -15,135 +16,154 @@ * @author Stanneke */ public class EnvironmentMethod implements IEnvironmentMethod { - + private final MethodOutput output; private final HashMap locals; final IEnvironmentClass environment; final Map local; - + + protected final LocalVariableTable localVariableTable; + public EnvironmentMethod(MethodOutput output, IEnvironmentClass environment) { this.output = output; this.locals = new HashMap<>(); this.environment = environment; this.local = new HashMap<>(); + this.localVariableTable = new LocalVariableTable(environment); } - + @Override public MethodOutput getOutput() { return output; } - + @Override public ClassVisitor getClassOutput() { return environment.getClassOutput(); } - + + @Override + public LocalVariableTable getLocalVariableTable() { + return localVariableTable; + } + @Override public int getLocal(SymbolLocal variable) { - if(!locals.containsKey(variable)) { + if (!locals.containsKey(variable)) { locals.put(variable, output.local(variable.getType().toASMType())); } return locals.get(variable); } - + + @Override + public int getLocal(SymbolLocal variable, boolean create) { + if (!locals.containsKey(variable)) { + if (!create) { + return -1; + } + locals.put(variable, output.local(variable.getType().toASMType())); + } + return locals.get(variable); + } + @Override public ZenType getType(Type type) { return environment.getType(type); } - + @Override public void error(ZenPosition position, String message) { environment.error(position, message); } - + @Override public void warning(ZenPosition position, String message) { environment.warning(position, message); } - + @Override public void info(ZenPosition position, String message) { environment.info(position, message); } - + @Override public IZenCompileEnvironment getEnvironment() { return environment.getEnvironment(); } - + @Override public TypeExpansion getExpansion(String name) { return environment.getExpansion(name); } - + @Override public ClassNameGenerator getClassNameGenerator() { return environment.getClassNameGenerator(); } - + @Override public String makeClassName() { return environment.makeClassName(); } - + @Override public String makeClassNameWithMiddleName(String middleName) { return environment.makeClassNameWithMiddleName(middleName); } - + @Override public void putClass(String name, byte[] data) { environment.putClass(name, data); } - + @Override public boolean containsClass(String name) { return environment.containsClass(name); } - + @Override public IPartialExpression getValue(String name, ZenPosition position) { - if(local.containsKey(name)) { + if (local.containsKey(name)) { return local.get(name).instance(position); } else { return environment.getValue(name, position); } } - + @Override public void putValue(String name, IZenSymbol value, ZenPosition position) { - if(local.containsKey(name)) { + if (local.containsKey(name)) { error(position, "Value already defined in this scope: " + name); } else { local.put(name, value); } } - + @Override public Set getClassNames() { return environment.getClassNames(); } - + @Override public byte[] getClass(String name) { return environment.getClass(name); } - + @Override public void error(String message) { environment.error(message); } - + @Override public void error(String message, Throwable e) { environment.error(message, e); } - + @Override public void warning(String message) { environment.warning(message); } - + @Override public void info(String message) { environment.info(message); diff --git a/src/main/java/stanhebben/zenscript/compiler/EnvironmentScope.java b/src/main/java/stanhebben/zenscript/compiler/EnvironmentScope.java index 3eac34b..3209193 100644 --- a/src/main/java/stanhebben/zenscript/compiler/EnvironmentScope.java +++ b/src/main/java/stanhebben/zenscript/compiler/EnvironmentScope.java @@ -6,6 +6,7 @@ import stanhebben.zenscript.symbols.*; import stanhebben.zenscript.type.ZenType; import stanhebben.zenscript.util.*; +import stanhebben.zenscript.util.localvariabletable.LocalVariableTable; import java.lang.reflect.Type; import java.util.*; @@ -30,6 +31,11 @@ public MethodOutput getOutput() { return outer.getOutput(); } + @Override + public LocalVariableTable getLocalVariableTable() { + return outer.getLocalVariableTable(); + } + @Override public int getLocal(SymbolLocal variable) { if(locals.containsKey(variable)) { @@ -39,6 +45,15 @@ public int getLocal(SymbolLocal variable) { } } + + @Override + public int getLocal(SymbolLocal variable, boolean create) { + if(locals.containsKey(variable)) { + return locals.get(variable); + } + return outer.getLocal(variable, create); + } + @Override public ClassVisitor getClassOutput() { return outer.getClassOutput(); diff --git a/src/main/java/stanhebben/zenscript/compiler/IEnvironmentMethod.java b/src/main/java/stanhebben/zenscript/compiler/IEnvironmentMethod.java index bfb34d5..83158b8 100644 --- a/src/main/java/stanhebben/zenscript/compiler/IEnvironmentMethod.java +++ b/src/main/java/stanhebben/zenscript/compiler/IEnvironmentMethod.java @@ -2,6 +2,7 @@ import stanhebben.zenscript.symbols.SymbolLocal; import stanhebben.zenscript.util.MethodOutput; +import stanhebben.zenscript.util.localvariabletable.LocalVariableTable; /** * @author Stan @@ -9,6 +10,11 @@ public interface IEnvironmentMethod extends IEnvironmentClass { MethodOutput getOutput(); - + int getLocal(SymbolLocal variable); + int getLocal(SymbolLocal variable, boolean create); + + + LocalVariableTable getLocalVariableTable(); + } diff --git a/src/main/java/stanhebben/zenscript/definitions/zenclasses/ParsedClassConstructor.java b/src/main/java/stanhebben/zenscript/definitions/zenclasses/ParsedClassConstructor.java index e823ac7..11fbe51 100644 --- a/src/main/java/stanhebben/zenscript/definitions/zenclasses/ParsedClassConstructor.java +++ b/src/main/java/stanhebben/zenscript/definitions/zenclasses/ParsedClassConstructor.java @@ -8,6 +8,8 @@ import stanhebben.zenscript.symbols.SymbolArgument; import stanhebben.zenscript.type.*; import stanhebben.zenscript.util.*; +import stanhebben.zenscript.util.localvariabletable.LocalVariable; +import stanhebben.zenscript.util.localvariabletable.LocalVariableTable; import java.util.*; import java.util.stream.IntStream; @@ -16,23 +18,24 @@ import static stanhebben.zenscript.util.ZenTypeUtil.internal; public class ParsedClassConstructor { - + final ZenType[] types; private final String[] names; private final List statements; - + private ZenTypeZenClass owner; + private ParsedClassConstructor(List types, List names, List statements) { this.types = types.toArray(new ZenType[types.size()]); this.names = names.toArray(new String[names.size()]); this.statements = statements; } - - + + static ParsedClassConstructor parse(ZenTokener parser, IEnvironmentGlobal environment) { parser.required(T_BROPEN, "( Needed"); List types = new LinkedList<>(); List names = new LinkedList<>(); - + while(parser.optional(T_BRCLOSE) == null) { String name = parser.required(T_ID, "Parameter identifier required").getValue(); ZenType type = ZenType.ANY; @@ -51,7 +54,7 @@ static ParsedClassConstructor parse(ZenTokener parser, IEnvironmentGlobal enviro } return new ParsedClassConstructor(types, names, statements); } - + public String getDescription() { StringBuilder builder = new StringBuilder("("); for(ZenType type : types) { @@ -59,14 +62,18 @@ public String getDescription() { } return builder.append(")V").toString(); } - + + public void setOwnerClassType(ZenTypeZenClass type) { + owner = type; + } + public void writeAll(IEnvironmentClass environmentNewClass, ClassVisitor newClass, List nonStatics, String className, ZenPosition position) { final MethodOutput init = new MethodOutput(newClass, Opcodes.ACC_PUBLIC, "", this.getDescription(), null, null); EnvironmentMethod initEnvironment = new EnvironmentMethod(init, environmentNewClass); init.start(); init.loadObject(0); init.invokeSpecial(internal(Object.class), "", "()V"); - + for(ParsedZenClassField nonStatic : nonStatics) { if(!nonStatic.hasInitializer()) continue; @@ -74,54 +81,62 @@ public void writeAll(IEnvironmentClass environmentNewClass, ClassVisitor newClas final Expression expression = nonStatic.initializer.compile(initEnvironment, nonStatic.type).eval(environmentNewClass); if(nonStatic.type == ZenType.ANY) nonStatic.type = expression.getType(); - + expression.compile(true, initEnvironment); init.putField(className, nonStatic.name, nonStatic.type.toASMType().getDescriptor()); } - + final EnvironmentMethod environmentMethod = new EnvironmentMethod(init, environmentNewClass); + LocalVariableTable localVariableTable = environmentMethod.getLocalVariableTable(); + localVariableTable.beginScope(); + localVariableTable.put(LocalVariable.thisRef(owner.toASMType())); this.injectParameters(environmentMethod, position); this.writeConstructor(environmentMethod); + localVariableTable.ensureFirstLabel(init, null); init.ret(); + localVariableTable.endMethod(init); + localVariableTable.writeLocalVariables(init); init.end(); } - - + + public void writeConstructor(IEnvironmentMethod environmentMethod) { for(Statement statement : statements) { statement.compile(environmentMethod); } } - + public boolean canAccept(Expression[] arguments, IEnvironmentGlobal environment) { return arguments.length == types.length && IntStream.range(0, arguments.length).allMatch(i -> arguments[i].getType().canCastImplicit(types[i], environment)); } - + public Expression call(ZenPosition position, Expression[] arguments, ZenTypeZenClass type) { if(arguments.length != this.types.length) throw new IllegalArgumentException(String.format("Expected %d arguments, received %d", types.length, arguments.length)); return new ExpressionCallConstructor(position, type, arguments); } - + public void injectParameters(IEnvironmentMethod environmentMethod, ZenPosition position) { for(int i = 0, j = 0; i < types.length; i++) { - environmentMethod.putValue(names[i], new SymbolArgument(i + 1 + j, types[i]), position); + SymbolArgument symbolArgument = new SymbolArgument(i + 1 + j, types[i]); + environmentMethod.putValue(names[i], symbolArgument, position); + environmentMethod.getLocalVariableTable().put(LocalVariable.parameter(names[i], symbolArgument)); if(types[i].isLarge()) j++; } } - + private final class ExpressionCallConstructor extends Expression { - + private final ZenTypeZenClass type; private final Expression[] arguments; - + ExpressionCallConstructor(ZenPosition position, ZenTypeZenClass type, Expression[] arguments) { super(position); this.type = type; this.arguments = arguments; } - + @Override public void compile(boolean result, IEnvironmentMethod environment) { MethodOutput output = environment.getOutput(); @@ -133,7 +148,7 @@ public void compile(boolean result, IEnvironmentMethod environment) { } output.invokeSpecial(type.getName(), "", ParsedClassConstructor.this.getDescription()); } - + @Override public ZenType getType() { return type; diff --git a/src/main/java/stanhebben/zenscript/definitions/zenclasses/ParsedZenClass.java b/src/main/java/stanhebben/zenscript/definitions/zenclasses/ParsedZenClass.java index c830e2a..1064f9d 100644 --- a/src/main/java/stanhebben/zenscript/definitions/zenclasses/ParsedZenClass.java +++ b/src/main/java/stanhebben/zenscript/definitions/zenclasses/ParsedZenClass.java @@ -87,15 +87,17 @@ private void addMethod(ParsedZenClassMethod parsedMethod) { classEnvironment.putValue(method.getName(), position1 -> new ExpressionThis(position1, type).getMember(position1, classEnvironment, method.getName()), position); } parsedMethod.addToMember(members.get(method.getName())); - for (int i = 0; i < method.getArguments().size(); i++) { + parsedMethod.setOwnerClassType(this.type); + for(int i = 0; i < method.getArguments().size(); i++) { ParsedFunctionArgument argument = method.getArguments().get(i); - if (argument.getDefaultExpression() != null) { + if(argument.getDefaultExpression() != null) { addField(new ParsedZenClassField(true, argument.getType(), argument.getDefaultExpression(), method.getDefaultParameterFieldName(i), className)); } } } private void addConstructor(ParsedClassConstructor parsedClassConstructor) { + parsedClassConstructor.setOwnerClassType(this.type); constructors.add(parsedClassConstructor); } @@ -188,9 +190,7 @@ public Expression call(ZenPosition position, IEnvironmentGlobal environment, Exp public IPartialExpression getMember(ZenPosition position, IEnvironmentGlobal environment, IPartialExpression value, String name, boolean isStatic) { if(members.containsKey(name)) - return isStatic - ? members.get(name).instance(position, environment) - : members.get(name).instance(position, environment, value); + return isStatic ? members.get(name).instance(position, environment) : members.get(name).instance(position, environment, value); environment.error("Could not find " + (isStatic ? "static " : "") + "member " + name); return new ExpressionInvalid(position); } diff --git a/src/main/java/stanhebben/zenscript/definitions/zenclasses/ParsedZenClassMethod.java b/src/main/java/stanhebben/zenscript/definitions/zenclasses/ParsedZenClassMethod.java index 58f47a2..0fcaacf 100644 --- a/src/main/java/stanhebben/zenscript/definitions/zenclasses/ParsedZenClassMethod.java +++ b/src/main/java/stanhebben/zenscript/definitions/zenclasses/ParsedZenClassMethod.java @@ -16,10 +16,13 @@ import stanhebben.zenscript.symbols.SymbolArgument; import stanhebben.zenscript.type.ZenType; import stanhebben.zenscript.type.ZenTypeAny; +import stanhebben.zenscript.type.ZenTypeZenClass; import stanhebben.zenscript.type.natives.IJavaMethod; import stanhebben.zenscript.type.natives.JavaMethod; import stanhebben.zenscript.type.natives.ZenNativeMember; import stanhebben.zenscript.util.MethodOutput; +import stanhebben.zenscript.util.localvariabletable.LocalVariable; +import stanhebben.zenscript.util.localvariabletable.LocalVariableTable; import java.util.ArrayList; import java.util.List; @@ -32,6 +35,7 @@ public class ParsedZenClassMethod { final ParsedFunction method; final String className; + private ZenTypeZenClass owner; public ParsedZenClassMethod(ParsedFunction parse, String className) { @@ -55,10 +59,10 @@ public static ParsedZenClassMethod parse(ZenTokener parser, EnvironmentScript cl if(parser.optional(T_AS) != null) { type = ZenType.read(parser, classEnvironment); } - - if (parser.optional(T_ASSIGN) != null) { + + if(parser.optional(T_ASSIGN) != null) { expression = ParsedExpression.read(parser, classEnvironment); - if (expression instanceof ParsedExpressionVariable) { + if(expression instanceof ParsedExpressionVariable) { throw new ParseException(parser.getFile(), parser.getLine(), parser.getLineOffset(), "Variables are not allowed in default arguments"); } hasDefaultArgument = true; @@ -73,14 +77,14 @@ public static ParsedZenClassMethod parse(ZenTokener parser, EnvironmentScript cl if(parser.optional(T_AS) != null) { type2 = ZenType.read(parser, classEnvironment); } - - if (parser.optional(T_ASSIGN) != null) { + + if(parser.optional(T_ASSIGN) != null) { expression2 = ParsedExpression.read(parser, classEnvironment); - if (expression2 instanceof ParsedExpressionVariable) { + if(expression2 instanceof ParsedExpressionVariable) { throw new ParseException(parser.getFile(), parser.getLine(), parser.getLineOffset(), "Variables are not allowed in default arguments"); } hasDefaultArgument = true; - } else if (hasDefaultArgument) { + } else if(hasDefaultArgument) { throw new ParseException(parser.getFile(), parser.getLine(), parser.getLineOffset(), "Parameter " + argName2.getValue() + " requires a default argument"); } @@ -122,10 +126,15 @@ public void writeAll(ClassVisitor newClass, IEnvironmentClass environmentNewClas MethodOutput methodOutput = new MethodOutput(newClass, Opcodes.ACC_PUBLIC, method.getName(), description, null, null); IEnvironmentMethod methodEnvironment = new EnvironmentMethod(methodOutput, environmentNewClass); + LocalVariableTable localVariableTable = methodEnvironment.getLocalVariableTable(); + localVariableTable.beginScope(); + localVariableTable.put(LocalVariable.thisRef(owner.toASMType())); List arguments = method.getArguments(); for(int i = 0, j = 0; i < arguments.size(); ) { ParsedFunctionArgument argument = arguments.get(i); - methodEnvironment.putValue(argument.getName(), new SymbolArgument(++i + j, argument.getType()), method.getPosition()); + SymbolArgument symbolArgument = new SymbolArgument(++i + j, argument.getType()); + methodEnvironment.putValue(argument.getName(), symbolArgument, method.getPosition()); + localVariableTable.put(LocalVariable.parameter(argument.getName(), symbolArgument)); if(argument.getType().isLarge()) ++j; } @@ -135,6 +144,7 @@ public void writeAll(ClassVisitor newClass, IEnvironmentClass environmentNewClas statement.compile(methodEnvironment); } + localVariableTable.ensureFirstLabel(methodOutput, method.getPosition()); if(method.getReturnType() != ZenType.VOID) { if(statements.length != 0 && statements[statements.length - 1] instanceof StatementReturn) { if(((StatementReturn) statements[statements.length - 1]).getExpression() != null) { @@ -148,9 +158,15 @@ public void writeAll(ClassVisitor newClass, IEnvironmentClass environmentNewClas } else if(statements.length == 0 || !(statements[statements.length - 1] instanceof StatementReturn)) { methodOutput.ret(); } + localVariableTable.endMethod(methodOutput); + localVariableTable.writeLocalVariables(methodOutput); methodOutput.end(); } + public void setOwnerClassType(ZenTypeZenClass type) { + this.owner = type; + } + public class ZenClassMethod implements IJavaMethod { @Override @@ -161,7 +177,7 @@ public boolean isStatic() { @Override public boolean accepts(int numArguments) { int defaultArguments = method.countDefaultArguments(); - if (defaultArguments == 0) { + if(defaultArguments == 0) { return method.getArgumentTypes().length == numArguments; } else { return numArguments + defaultArguments >= method.getArgumentTypes().length; @@ -226,11 +242,11 @@ public String getErrorDescription() { return builder.append(")").toString(); } - + public String getOwner() { return className; } - + public ParsedFunction getFunction() { return method; } diff --git a/src/main/java/stanhebben/zenscript/expression/ExpressionFunction.java b/src/main/java/stanhebben/zenscript/expression/ExpressionFunction.java index 41fb3e5..69fb14d 100644 --- a/src/main/java/stanhebben/zenscript/expression/ExpressionFunction.java +++ b/src/main/java/stanhebben/zenscript/expression/ExpressionFunction.java @@ -7,6 +7,8 @@ import stanhebben.zenscript.symbols.*; import stanhebben.zenscript.type.*; import stanhebben.zenscript.util.*; +import stanhebben.zenscript.util.localvariabletable.LocalVariable; +import stanhebben.zenscript.util.localvariabletable.LocalVariableTable; import java.lang.reflect.Method; import java.util.List; @@ -36,7 +38,7 @@ public ExpressionFunction(ZenPosition position, List arg for(int i = 0; i < arguments.size(); i++) { argumentTypes[i] = arguments.get(i).getType(); } - + this.className = className; functionType = new ZenTypeFunctionCallable(returnType, argumentTypes, className, makeDescriptor()); } @@ -109,9 +111,12 @@ public void compile(boolean result, IEnvironmentMethod environment) { IEnvironmentClass environmentClass = new EnvironmentClass(cw, environment); EnvironmentMethodLambda environmentMethod = new EnvironmentMethodLambda(output, environmentClass, className); - + LocalVariableTable localVariableTable = environmentMethod.getLocalVariableTable(); + localVariableTable.beginScope(); for(int i = 0, j = 0; i < arguments.size(); i++) { - environmentMethod.putValue(arguments.get(i).getName(), new SymbolArgument(i + 1 + j, arguments.get(i).getType()), getPosition()); + SymbolArgument symbolArgument = new SymbolArgument(i + 1 + j, arguments.get(i).getType()); + environmentMethod.putValue(arguments.get(i).getName(), symbolArgument, getPosition()); + localVariableTable.put(LocalVariable.parameter(arguments.get(i).getName(), symbolArgument)); if(arguments.get(i).getType().isLarge()) j++; } @@ -120,22 +125,19 @@ public void compile(boolean result, IEnvironmentMethod environment) { for(Statement statement : statements) { statement.compile(environmentMethod); } + localVariableTable.ensureFirstLabel(output, getPosition()); output.ret(); + localVariableTable.endMethod(output); + localVariableTable.writeLocalVariables(output); output.end(); environmentMethod.createConstructor(cw); environment.putClass(className, cw.toByteArray()); - + // make class instance environment.getOutput().newObject(className); environment.getOutput().dup(); - final String[] arguments = environmentMethod.getCapturedVariables().stream() - .map(SymbolCaptured::getEvaluated) - .peek(expression -> expression.compile(true, environment)) - .map(Expression::getType) - .map(ZenType::toASMType) - .map(Type::getDescriptor) - .toArray(String[]::new); + final String[] arguments = environmentMethod.getCapturedVariables().stream().map(SymbolCaptured::getEvaluated).peek(expression -> expression.compile(true, environment)).map(Expression::getType).map(ZenType::toASMType).map(Type::getDescriptor).toArray(String[]::new); environment.getOutput().construct(className, arguments); } diff --git a/src/main/java/stanhebben/zenscript/expression/ExpressionJavaLambda.java b/src/main/java/stanhebben/zenscript/expression/ExpressionJavaLambda.java index 166357c..3c0e434 100644 --- a/src/main/java/stanhebben/zenscript/expression/ExpressionJavaLambda.java +++ b/src/main/java/stanhebben/zenscript/expression/ExpressionJavaLambda.java @@ -8,6 +8,8 @@ import stanhebben.zenscript.symbols.*; import stanhebben.zenscript.type.ZenType; import stanhebben.zenscript.util.*; +import stanhebben.zenscript.util.localvariabletable.LocalVariable; +import stanhebben.zenscript.util.localvariabletable.LocalVariableTable; import java.lang.reflect.*; import java.util.*; @@ -62,9 +64,13 @@ public void compile(boolean result, IEnvironmentMethod environment) { output.position(getPosition()); IEnvironmentClass environmentClass = new EnvironmentClass(cw, environment); EnvironmentMethodLambda environmentMethod = new EnvironmentMethodLambda(output, environmentClass, clsName); + LocalVariableTable localVariableTable = environmentMethod.getLocalVariableTable(); + localVariableTable.beginScope(); for(int i = 0, j = 0; i < arguments.size(); i++) { - environmentMethod.putValue(arguments.get(i).getName(), new SymbolArgument(i + j + 1, environment.getType(method.getGenericParameterTypes()[i])), getPosition()); + SymbolArgument symbolArgument = new SymbolArgument(i + j + 1, environment.getType(method.getGenericParameterTypes()[i])); + environmentMethod.putValue(arguments.get(i).getName(), symbolArgument, getPosition()); + localVariableTable.put(LocalVariable.parameter(arguments.get(i).getName(), symbolArgument)); if(environment.getType(method.getGenericParameterTypes()[i]).isLarge()) j++; } @@ -73,7 +79,10 @@ public void compile(boolean result, IEnvironmentMethod environment) { for(Statement statement : statements) { statement.compile(environmentMethod); } + localVariableTable.ensureFirstLabel(output, getPosition()); output.ret(); + localVariableTable.endMethod(output); + localVariableTable.writeLocalVariables(output); output.end(); diff --git a/src/main/java/stanhebben/zenscript/expression/ExpressionJavaLambdaSimpleGeneric.java b/src/main/java/stanhebben/zenscript/expression/ExpressionJavaLambdaSimpleGeneric.java index a821f5e..5fdd160 100644 --- a/src/main/java/stanhebben/zenscript/expression/ExpressionJavaLambdaSimpleGeneric.java +++ b/src/main/java/stanhebben/zenscript/expression/ExpressionJavaLambdaSimpleGeneric.java @@ -7,6 +7,8 @@ import stanhebben.zenscript.symbols.*; import stanhebben.zenscript.type.ZenType; import stanhebben.zenscript.util.*; +import stanhebben.zenscript.util.localvariabletable.LocalVariable; +import stanhebben.zenscript.util.localvariabletable.LocalVariableTable; import java.lang.reflect.Method; import java.util.*; @@ -17,27 +19,27 @@ * @author Stanneke */ public class ExpressionJavaLambdaSimpleGeneric extends Expression { - + private final Class interfaceClass; public Class genericClass; private final List arguments; private final List statements; private final String descriptor; - + private final ZenType type; - + public ExpressionJavaLambdaSimpleGeneric(ZenPosition position, Class interfaceClass, List arguments, List statements, ZenType type) { super(position); - + this.interfaceClass = interfaceClass; this.arguments = arguments; this.statements = statements; - + this.type = type; - + ZenType genericType = arguments.get(0).getType(); this.genericClass = genericType.equals(ZenType.ANY) ? Object.class : genericType.toJavaClass(); - + final Method method = ZenTypeUtil.findFunctionalInterfaceMethod(interfaceClass); if(method == null) { throw new IllegalArgumentException("Internal error: Cannot create function for " + interfaceClass + " because it is not a functional interface!"); @@ -56,17 +58,17 @@ public ExpressionJavaLambdaSimpleGeneric(ZenPosition position, Class interfac sb.append(")").append(signature(interfaceClass.getDeclaredMethods()[0].getReturnType())); this.descriptor = sb.toString(); } - + @Override public ZenType getType() { return type; } - + @Override public void compile(boolean result, IEnvironmentMethod environment) { if(!result) return; - + final Method method = ZenTypeUtil.findFunctionalInterfaceMethod(interfaceClass); if(method == null) { //How did we even come this far? @@ -75,35 +77,42 @@ public void compile(boolean result, IEnvironmentMethod environment) { } // generate class String clsName = environment.makeClassNameWithMiddleName(getPosition().getFile().getClassName()); - + ClassWriter cw = new ZenClassWriter(ClassWriter.COMPUTE_FRAMES); cw.visitSource(getPosition().getFileName(), null); cw.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC, clsName, createMethodSignature(), "java/lang/Object", new String[]{internal(interfaceClass)}); - + MethodOutput output = new MethodOutput(cw, Opcodes.ACC_PUBLIC, method.getName(), descriptor, null, null); output.position(getPosition()); IEnvironmentClass environmentClass = new EnvironmentClass(cw, environment); EnvironmentMethodLambda environmentMethod = new EnvironmentMethodLambda(output, environmentClass, clsName); - + + LocalVariableTable localVariableTable = environmentMethod.getLocalVariableTable(); + localVariableTable.beginScope(); for(int i = 0, j = 0; i < arguments.size(); i++) { ZenType typeToPut = arguments.get(i).getType(); if(typeToPut.equals(ZenType.ANY)) typeToPut = environment.getType(method.getGenericParameterTypes()[i]); if(typeToPut == null) typeToPut = environment.getType(method.getParameterTypes()[i]); - - environmentMethod.putValue(arguments.get(i).getName(), new SymbolArgument(i + 1 + j, typeToPut), getPosition()); + + SymbolArgument symbolArgument = new SymbolArgument(i + 1 + j, typeToPut); + environmentMethod.putValue(arguments.get(i).getName(), symbolArgument, getPosition()); + localVariableTable.put(LocalVariable.parameter(arguments.get(i).getName(), symbolArgument)); if(typeToPut.isLarge()) j++; } - + output.start(); for(Statement statement : statements) { statement.compile(environmentMethod); } + localVariableTable.ensureFirstLabel(output, getPosition()); output.ret(); + localVariableTable.endMethod(output); + localVariableTable.writeLocalVariables(output); output.end(); - + if(!Objects.equals(genericClass, Object.class)) { MethodOutput bridge = new MethodOutput(cw, Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC | Opcodes.ACC_BRIDGE, method.getName(), ZenTypeUtil.descriptor(method), null, null); bridge.loadObject(0); @@ -114,28 +123,22 @@ public void compile(boolean result, IEnvironmentMethod environment) { bridge.load(org.objectweb.asm.Type.getType(method.getParameterTypes()[i]), ++i); } } - + bridge.invokeVirtual(clsName, method.getName(), descriptor); bridge.returnType(org.objectweb.asm.Type.getReturnType(method)); bridge.end(); } - + environmentMethod.createConstructor(cw); environment.putClass(clsName, cw.toByteArray()); - + // make class instance environment.getOutput().newObject(clsName); environment.getOutput().dup(); - final String[] arguments = environmentMethod.getCapturedVariables().stream() - .map(SymbolCaptured::getEvaluated) - .peek(expression -> expression.compile(true, environment)) - .map(Expression::getType) - .map(ZenType::toASMType) - .map(Type::getDescriptor) - .toArray(String[]::new); + final String[] arguments = environmentMethod.getCapturedVariables().stream().map(SymbolCaptured::getEvaluated).peek(expression -> expression.compile(true, environment)).map(Expression::getType).map(ZenType::toASMType).map(Type::getDescriptor).toArray(String[]::new); environment.getOutput().construct(clsName, arguments); } - + private String createMethodSignature() { StringBuilder sb = new StringBuilder(); sb.append("Ljava/lang/Object;"); diff --git a/src/main/java/stanhebben/zenscript/statements/StatementBlock.java b/src/main/java/stanhebben/zenscript/statements/StatementBlock.java index ad1dbe1..4b63b1d 100644 --- a/src/main/java/stanhebben/zenscript/statements/StatementBlock.java +++ b/src/main/java/stanhebben/zenscript/statements/StatementBlock.java @@ -1,7 +1,9 @@ package stanhebben.zenscript.statements; +import org.objectweb.asm.Label; import stanhebben.zenscript.compiler.*; import stanhebben.zenscript.util.ZenPosition; +import stanhebben.zenscript.util.localvariabletable.LocalVariableTable; import java.util.*; @@ -19,24 +21,32 @@ public StatementBlock(ZenPosition position, List statements) { public void compile(IEnvironmentMethod environment) { environment.getOutput().position(getPosition()); IEnvironmentMethod local = new EnvironmentScope(environment); + LocalVariableTable localVariableTable = local.getLocalVariableTable(); + localVariableTable.beginScope(); for(Statement statement : statements) { statement.compile(local); if(statement.isReturn()) { + localVariableTable.endScope(environment.getOutput().lastLabel()); return; } } + localVariableTable.endScope(environment.getOutput().lastLabel()); } @Override public void compile(IEnvironmentMethod environment, boolean forced) { environment.getOutput().position(getPosition()); IEnvironmentMethod local = new EnvironmentScope(environment); + LocalVariableTable localVariableTable = local.getLocalVariableTable(); + localVariableTable.beginScope(); for(Statement statement : statements) { statement.compile(local, forced); if(statement.isReturn()) { + localVariableTable.endScope(environment.getOutput().lastLabel()); return; } } + localVariableTable.endScope(environment.getOutput().lastLabel()); } @Override diff --git a/src/main/java/stanhebben/zenscript/statements/StatementForeach.java b/src/main/java/stanhebben/zenscript/statements/StatementForeach.java index f36c283..fbc8984 100644 --- a/src/main/java/stanhebben/zenscript/statements/StatementForeach.java +++ b/src/main/java/stanhebben/zenscript/statements/StatementForeach.java @@ -7,6 +7,8 @@ import stanhebben.zenscript.symbols.SymbolLocal; import stanhebben.zenscript.type.*; import stanhebben.zenscript.util.*; +import stanhebben.zenscript.util.localvariabletable.LocalVariable; +import stanhebben.zenscript.util.localvariabletable.LocalVariableTable; import java.util.*; @@ -36,13 +38,16 @@ public void compile(IEnvironmentMethod environment) { } MethodOutput methodOutput = environment.getOutput(); - environment.getOutput().position(getPosition()); + Label label = environment.getOutput().position(getPosition()); IEnvironmentMethod local = new EnvironmentScope(environment); + LocalVariableTable localVariableTable = local.getLocalVariableTable(); + localVariableTable.beginScope(); int[] localVariables = new int[varnames.length]; for(int i = 0; i < localVariables.length; i++) { SymbolLocal localVar = new SymbolLocal(iterator.getType(i), true); local.putValue(varnames[i], localVar, getPosition()); + localVariableTable.put(LocalVariable.localVariable(varnames[i], localVar, environment, label)); localVariables[i] = local.getLocal(localVar); } @@ -68,6 +73,7 @@ else if (statement instanceof StatementContinue) iterator.compilePostIterate(localVariables, exit, repeat); methodOutput.label(exit); iterator.compileEnd(); + localVariableTable.endScope(environment.getOutput().lastLabel()); } @Override diff --git a/src/main/java/stanhebben/zenscript/statements/StatementVar.java b/src/main/java/stanhebben/zenscript/statements/StatementVar.java index de3a63f..2f4d1f6 100644 --- a/src/main/java/stanhebben/zenscript/statements/StatementVar.java +++ b/src/main/java/stanhebben/zenscript/statements/StatementVar.java @@ -1,11 +1,13 @@ package stanhebben.zenscript.statements; +import org.objectweb.asm.Label; import stanhebben.zenscript.compiler.IEnvironmentMethod; import stanhebben.zenscript.expression.Expression; import stanhebben.zenscript.parser.expression.ParsedExpression; import stanhebben.zenscript.symbols.SymbolLocal; import stanhebben.zenscript.type.*; import stanhebben.zenscript.util.ZenPosition; +import stanhebben.zenscript.util.localvariabletable.LocalVariable; /** * @author Stanneke @@ -28,13 +30,14 @@ public StatementVar(ZenPosition position, String name, ZenType type, ParsedExpre @Override public void compile(IEnvironmentMethod environment) { - environment.getOutput().position(getPosition()); + Label label = environment.getOutput().position(getPosition()); Expression cInitializer = initializer == null ? null : initializer.compile(environment, type).eval(environment); ZenType cType = type == null ? (cInitializer == null ? ZenTypeAny.INSTANCE : cInitializer.getType()) : type; SymbolLocal symbol = new SymbolLocal(cType, isFinal); environment.putValue(name, symbol, getPosition()); + environment.getLocalVariableTable().put(LocalVariable.localVariable(name, symbol, environment, label)); if(cInitializer != null) { cInitializer.compile(true, environment); diff --git a/src/main/java/stanhebben/zenscript/symbols/SymbolArgument.java b/src/main/java/stanhebben/zenscript/symbols/SymbolArgument.java index aa799d1..8ec71ed 100644 --- a/src/main/java/stanhebben/zenscript/symbols/SymbolArgument.java +++ b/src/main/java/stanhebben/zenscript/symbols/SymbolArgument.java @@ -17,7 +17,15 @@ public SymbolArgument(int id, ZenType type) { this.id = id; this.type = type; } - + + public int getId() { + return id; + } + + public ZenType getType() { + return type; + } + @Override public IPartialExpression instance(ZenPosition position) { return new ExpressionArgument(position, id, type); diff --git a/src/main/java/stanhebben/zenscript/util/MethodOutput.java b/src/main/java/stanhebben/zenscript/util/MethodOutput.java index 734c7ba..178fb95 100644 --- a/src/main/java/stanhebben/zenscript/util/MethodOutput.java +++ b/src/main/java/stanhebben/zenscript/util/MethodOutput.java @@ -17,10 +17,13 @@ public class MethodOutput { private final LocalVariablesSorter visitor; - private boolean debug = false; + private boolean debug = true; private int labelIndex = 1; private Map labelNames; + private Label firstLabel; + private Label lastLabel; + public MethodOutput(ClassVisitor cls, int access, String name, String descriptor, String signature, String[] exceptions) { MethodVisitor methodVisitor = cls.visitMethod(access, name, descriptor, signature, exceptions); visitor = new LocalVariablesSorter(access, descriptor, methodVisitor); @@ -61,10 +64,23 @@ public void end() { visitor.visitEnd(); } + public Label firstLabel() { + return firstLabel; + } + + + public Label lastLabel() { + return lastLabel; + } + public void label(Label label) { if(debug) System.out.println("Label " + getLabelName(label)); + if(firstLabel == null) { + firstLabel = label; + } + lastLabel = label; visitor.visitLabel(label); } @@ -76,6 +92,14 @@ public int local(Class cls) { return visitor.newLocal(Type.getType(cls)); } + + public void localVariable(String name, Type type, int local, Label start, Label end) { + if(debug) + System.out.println("Local variable " + name + ": " + type + " (" + local + ")"); + + visitor.visitLocalVariable(name, type.getDescriptor(), null, start, end, local); + } + public void iConst0() { if(debug) System.out.println("iconst0"); @@ -1046,10 +1070,15 @@ public void aThrow() { visitor.visitInsn(ATHROW); } - public void position(ZenPosition position) { + public Label position(ZenPosition position) { Label label = new Label(); + if(firstLabel == null) { + firstLabel = label; + } + lastLabel = label; visitor.visitLabel(label); visitor.visitLineNumber(position.getLine(), label); + return label; } public void swap() { @@ -1060,7 +1089,7 @@ public void swap() { /** * Swaps a large type with a non-large type, where the non-large type is at the top of the stack - * + *

* Example: D1, D2, I. Swap would result in D1, I, D2. This method would return in I, D1, D2 */ public void swapLargeLower() { @@ -1080,4 +1109,5 @@ private String getLabelName(Label lbl) { return labelNames.get(lbl); } + } diff --git a/src/main/java/stanhebben/zenscript/util/localvariabletable/LocalVariable.java b/src/main/java/stanhebben/zenscript/util/localvariabletable/LocalVariable.java new file mode 100644 index 0000000..881aac7 --- /dev/null +++ b/src/main/java/stanhebben/zenscript/util/localvariabletable/LocalVariable.java @@ -0,0 +1,51 @@ +package stanhebben.zenscript.util.localvariabletable; + +import org.objectweb.asm.Label; +import org.objectweb.asm.Type; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.symbols.SymbolArgument; +import stanhebben.zenscript.symbols.SymbolLocal; +import stanhebben.zenscript.util.MethodOutput; + +import java.util.function.IntSupplier; + +public class LocalVariable { + private final String name; + private final Type type; + + private final IntSupplier idxSupplier; + + private Label start; + private Label end; + + private LocalVariable(String name, Type type, IntSupplier idxSupplier, Label start) { + this.name = name; + this.type = type; + this.idxSupplier = idxSupplier; + this.start = start; + } + + public void setEndLabel(Label end) { + this.end = end; + } + + public void writeTo(MethodOutput output) { + if (start == null) { + start = output.firstLabel(); + } + output.localVariable(name, type, idxSupplier.getAsInt(), start, end); + } + + + public static LocalVariable localVariable(String name, SymbolLocal symbol, IEnvironmentMethod env, Label pos) { + return new LocalVariable(name, symbol.getType().toASMType(), () -> env.getLocal(symbol, false), pos); + } + + public static LocalVariable thisRef(Type type) { + return new LocalVariable("this", type, () -> 0, null); + } + + public static LocalVariable parameter(String name, SymbolArgument symbol) { + return new LocalVariable(name, symbol.getType().toASMType(), symbol::getId, null); + } +} diff --git a/src/main/java/stanhebben/zenscript/util/localvariabletable/LocalVariableTable.java b/src/main/java/stanhebben/zenscript/util/localvariabletable/LocalVariableTable.java new file mode 100644 index 0000000..e222fb4 --- /dev/null +++ b/src/main/java/stanhebben/zenscript/util/localvariabletable/LocalVariableTable.java @@ -0,0 +1,81 @@ +package stanhebben.zenscript.util.localvariabletable; + +import org.objectweb.asm.Label; +import stanhebben.zenscript.compiler.IEnvironmentClass; +import stanhebben.zenscript.util.MethodOutput; +import stanhebben.zenscript.util.ZenPosition; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +public class LocalVariableTable { + + private final IEnvironmentClass envClass; + private final LinkedList> scopeStack = new LinkedList<>(); + + private final List variables = new ArrayList<>(); + + public LocalVariableTable(IEnvironmentClass environment) { + this.envClass = environment; + } + + + public void beginScope() { + scopeStack.push(new ArrayList<>()); + } + + + public void endScope(Label end) { + if (scopeStack.isEmpty()) { + envClass.error("End of variable scope without begin"); + return; + } + + List stack = scopeStack.pop(); + + for (LocalVariable variable : stack) { + variable.setEndLabel(end); + } + + variables.addAll(stack); + + } + + public void ensureFirstLabel(MethodOutput output, ZenPosition position) { + if (output.lastLabel() == null) { + if (position != null) { + output.position(position); + } else { + output.label(new Label()); + } + } + } + + public void endMethod(MethodOutput output) { + Label lastLabel = new Label(); + output.label(lastLabel); + + endScope(lastLabel); + } + + public void put(LocalVariable variable) { + if (scopeStack.isEmpty()) { + envClass.error("Adding local variable without begin scope"); + return; + } + scopeStack.peek().add(variable); + } + + + public void writeLocalVariables(MethodOutput output) { + if (!scopeStack.isEmpty()) { + envClass.error("There are unclosed scopes before write local variables, some variables may be missing"); + } + for (LocalVariable variable : variables) { + variable.writeTo(output); + } + } + + +}