From defb31e3094e416e8ffb24bf68a20ec49f249759 Mon Sep 17 00:00:00 2001 From: Astrash Date: Tue, 12 Sep 2023 11:24:30 +1000 Subject: [PATCH] Mangle bytecode method names according to declaration scope --- .../build.gradle.kts | 6 ++-- .../terra/addons/terrascript/Environment.java | 35 +++++++++++++++---- .../asm/TerraScriptClassGenerator.java | 6 ++-- .../semanticanalysis/ScopeAnalyzer.java | 5 ++- .../semanticanalysis/TypeChecker.java | 9 +++-- .../src/test/java/codegen/CodeGenTest.java | 5 ++- 6 files changed, 49 insertions(+), 17 deletions(-) diff --git a/common/addons/structure-terrascript-loader/build.gradle.kts b/common/addons/structure-terrascript-loader/build.gradle.kts index 92f154c50..415249f99 100644 --- a/common/addons/structure-terrascript-loader/build.gradle.kts +++ b/common/addons/structure-terrascript-loader/build.gradle.kts @@ -176,7 +176,7 @@ tasks.register("genTerrascriptAstClasses") { listOf( ASTNode("Expression", listOf("expression" to "Expr")), ASTNode("Block", listOf("statements" to "List")), - ASTNode("FunctionDeclaration", listOf("identifier" to "String", "parameters" to "List>", "returnType" to "Type", "body" to "Block")), + ASTNode("FunctionDeclaration", listOf("identifier" to "String", "parameters" to "List>", "returnType" to "Type", "body" to "Block"), listOf("symbol" to "Symbol.Function")), ASTNode("VariableDeclaration", listOf("type" to "Type", "identifier" to "String", "value" to "Expr")), ASTNode("Return", listOf("value" to "Expr"), listOf("type" to "Type")), ASTNode("If", listOf("condition" to "Expr", "trueBody" to "Block", "elseIfClauses" to "List>", "elseBody" to "Optional")), @@ -201,7 +201,7 @@ tasks.register("genTerrascriptAstClasses") { ASTNode("Grouping", listOf("expression" to "TypedExpr")), ASTNode("Literal", listOf("value" to "Object")), ASTNode("Unary", listOf("operator" to "UnaryOperator", "operand" to "TypedExpr")), - ASTNode("Call", listOf("identifier" to "String", "arguments" to "List")), + ASTNode("Call", listOf("identifier" to "String", "arguments" to "List", "scopedIdentifier" to "String")), ASTNode("Variable", listOf("identifier" to "String")), ASTNode("Assignment", listOf("lValue" to "Variable", "rValue" to "TypedExpr")), ASTNode("Void", listOf()), @@ -219,7 +219,7 @@ tasks.register("genTerrascriptAstClasses") { listOf( ASTNode("Expression", listOf("expression" to "TypedExpr")), ASTNode("Block", listOf("statements" to "List")), - ASTNode("FunctionDeclaration", listOf("identifier" to "String", "parameters" to "List>", "returnType" to "Type", "body" to "Block")), + ASTNode("FunctionDeclaration", listOf("identifier" to "String", "parameters" to "List>", "returnType" to "Type", "body" to "Block", "scopedIdentifier" to "String")), ASTNode("VariableDeclaration", listOf("type" to "Type", "identifier" to "String", "value" to "TypedExpr")), ASTNode("Return", listOf("value" to "TypedExpr")), ASTNode("If", listOf("condition" to "TypedExpr", "trueBody" to "Block", "elseIfClauses" to "List>", "elseBody" to "Optional")), diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/Environment.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/Environment.java index 378d3a27b..b0cc564fa 100644 --- a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/Environment.java +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/Environment.java @@ -3,6 +3,7 @@ package com.dfsek.terra.addons.terrascript; import javax.annotation.Nullable; import java.lang.reflect.Method; import java.lang.reflect.Parameter; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; @@ -31,33 +32,50 @@ public class Environment { private final boolean inLoop; - private Environment(@Nullable Environment outer, boolean canAccessOuterVariables, boolean inLoop) { + private final int index; + + private int innerCount = 0; + + public final String name; + + private Environment(@Nullable Environment outer, boolean canAccessOuterVariables, boolean inLoop, int index) { this.outer = outer; this.canAccessOuterVariables = canAccessOuterVariables; this.inLoop = inLoop; + this.index = index; + this.name = String.join("_", getNestedIndexes().stream().map(Object::toString).toList()); // Populate symbol tables with built-in Java implemented methods TerraScript.BUILTIN_FUNCTIONS.forEach((name, method) -> symbolTable .put(name, new Function( Type.from(method.getReturnType()).orElseThrow(() -> new RuntimeException("")), // Map Java classes to TerraScript types - IntStream.range(0, method.getParameterCount()).mapToObj(i -> Pair.of("param" + i, Type.from(method.getParameterTypes()[i]).orElseThrow(() -> new RuntimeException("")))).toList()))); + IntStream.range(0, method.getParameterCount()).mapToObj(i -> Pair.of("param" + i, Type.from(method.getParameterTypes()[i]).orElseThrow(() -> new RuntimeException("")))).toList(), + this))); } public static Environment global() { - return new Environment(null, false, false); + return new Environment(null, false, false, 0); } public Environment lexicalInner() { - return new Environment(this, true, inLoop); + return new Environment(this, true, inLoop, innerCount++); } public Environment loopInner() { - return new Environment(this, true, true); + return new Environment(this, true, true, innerCount++); } public Environment functionalInner() { - return new Environment(this, false, inLoop); + return new Environment(this, false, inLoop, innerCount++); + } + + private List getNestedIndexes() { + List idxs = new ArrayList<>(); + for (Environment env = this; env.outer != null; env = env.outer) { + idxs.add(0, env.index); + } + return idxs; } public Environment outer() { @@ -117,9 +135,12 @@ public class Environment { public final List> parameters; - public Function(Type type, List> parameters) { + public final Environment scope; + + public Function(Type type, List> parameters, Environment scope) { this.type = type; this.parameters = parameters; + this.scope = scope; } } diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/codegen/asm/TerraScriptClassGenerator.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/codegen/asm/TerraScriptClassGenerator.java index ce962ea74..5bc1e958e 100644 --- a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/codegen/asm/TerraScriptClassGenerator.java +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/codegen/asm/TerraScriptClassGenerator.java @@ -228,7 +228,7 @@ public class TerraScriptClassGenerator { } expr.arguments.forEach(a -> a.accept(this)); List parameters = expr.arguments.stream().map(e -> e.type).toList(); - method.visitMethodInsn(Opcodes.INVOKESTATIC, className, expr.identifier, getFunctionDescriptor(parameters, expr.type), false); + method.visitMethodInsn(Opcodes.INVOKESTATIC, className, expr.scopedIdentifier, getFunctionDescriptor(parameters, expr.type), false); return null; } @@ -284,8 +284,8 @@ public class TerraScriptClassGenerator { List parameterTypes = stmt.parameters.stream().map(Pair::getRight).toList(); int access = Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC; - // TODO - Mangle identifier based on scope to avoid issues with using the same identifier in different scopes - MethodVisitor method = classWriter.visitMethod(access, stmt.identifier, getFunctionDescriptor(parameterTypes, stmt.returnType), null, null); + + MethodVisitor method = classWriter.visitMethod(access, stmt.scopedIdentifier, getFunctionDescriptor(parameterTypes, stmt.returnType), null, null); method.visitCode(); // Start method body diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/semanticanalysis/ScopeAnalyzer.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/semanticanalysis/ScopeAnalyzer.java index 39e649671..5e45a94d6 100644 --- a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/semanticanalysis/ScopeAnalyzer.java +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/semanticanalysis/ScopeAnalyzer.java @@ -3,6 +3,7 @@ package com.dfsek.terra.addons.terrascript.semanticanalysis; import com.dfsek.terra.addons.terrascript.Environment; import com.dfsek.terra.addons.terrascript.Environment.ScopeException.NonexistentSymbolException; import com.dfsek.terra.addons.terrascript.Environment.ScopeException.SymbolTypeMismatchException; +import com.dfsek.terra.addons.terrascript.Environment.Symbol; import com.dfsek.terra.addons.terrascript.ErrorHandler; import com.dfsek.terra.addons.terrascript.Type; import com.dfsek.terra.addons.terrascript.ast.Expr; @@ -119,7 +120,9 @@ public class ScopeAnalyzer implements Visitor, Stmt.Visitor { stmt.body.accept(this); currentScope = currentScope.outer(); try { - currentScope.put(stmt.identifier, new Environment.Symbol.Function(stmt.returnType, stmt.parameters)); + Symbol.Function symbol = new Symbol.Function(stmt.returnType, stmt.parameters, currentScope); + stmt.setSymbol(symbol); + currentScope.put(stmt.identifier, symbol); } catch(Environment.ScopeException.SymbolAlreadyExistsException e) { errorHandler.add(new IdentifierAlreadyDeclaredException("Name '" + stmt.identifier + "' is already defined in this scope", stmt.position)); diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/semanticanalysis/TypeChecker.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/semanticanalysis/TypeChecker.java index 789c18618..06b668ba7 100644 --- a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/semanticanalysis/TypeChecker.java +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/semanticanalysis/TypeChecker.java @@ -5,6 +5,7 @@ import java.util.Optional; import java.util.concurrent.atomic.AtomicBoolean; import com.dfsek.terra.addons.terrascript.Environment; +import com.dfsek.terra.addons.terrascript.Environment.Symbol; import com.dfsek.terra.addons.terrascript.ErrorHandler; import com.dfsek.terra.addons.terrascript.Type; import com.dfsek.terra.addons.terrascript.ast.Expr.Assignment; @@ -33,6 +34,10 @@ public class TypeChecker implements Visitor, Stmt.Visitor TypeChecker(ErrorHandler errorHandler) { this.errorHandler = errorHandler; } + private static String scopedIdentifier(String identifier, Symbol.Function symbol) { + return identifier + "$" + symbol.scope.name; + } + @Override public TypedExpr visitBinaryExpr(Binary expr) { TypedExpr left = expr.left.accept(this); @@ -124,7 +129,7 @@ public class TypeChecker implements Visitor, Stmt.Visitor providedType + " instead", expr.position)); } - return new TypedExpr.Call(expr.identifier, arguments, signature.type); + return new TypedExpr.Call(expr.identifier, arguments, scopedIdentifier(expr.identifier, expr.getSymbol()), signature.type); } @Override @@ -179,7 +184,7 @@ public class TypeChecker implements Visitor, Stmt.Visitor new InvalidFunctionDeclarationException("Function body for '" + stmt.identifier + "' does not contain return statement", stmt.position)); } - return new TypedStmt.FunctionDeclaration(stmt.identifier, stmt.parameters, stmt.returnType, body); + return new TypedStmt.FunctionDeclaration(stmt.identifier, stmt.parameters, stmt.returnType, body, scopedIdentifier(stmt.identifier, stmt.getSymbol())); } @Override diff --git a/common/addons/structure-terrascript-loader/src/test/java/codegen/CodeGenTest.java b/common/addons/structure-terrascript-loader/src/test/java/codegen/CodeGenTest.java index 1bbb9311f..41819a608 100644 --- a/common/addons/structure-terrascript-loader/src/test/java/codegen/CodeGenTest.java +++ b/common/addons/structure-terrascript-loader/src/test/java/codegen/CodeGenTest.java @@ -73,7 +73,10 @@ public class CodeGenTest { } fun concatThree(a: str, b: str, c: str): str { - return a + b + c; + fun concatTwo(a: str, b: str): str { + return a + b; + } + return concatTwo(a, b) + c; } fun retStr(): str {