Mangle bytecode method names according to declaration scope

This commit is contained in:
Astrash
2023-09-12 11:24:30 +10:00
parent 0a46e9050d
commit defb31e309
6 changed files with 49 additions and 17 deletions

View File

@@ -176,7 +176,7 @@ tasks.register("genTerrascriptAstClasses") {
listOf(
ASTNode("Expression", listOf("expression" to "Expr")),
ASTNode("Block", listOf("statements" to "List<Stmt>")),
ASTNode("FunctionDeclaration", listOf("identifier" to "String", "parameters" to "List<Pair<String, Type>>", "returnType" to "Type", "body" to "Block")),
ASTNode("FunctionDeclaration", listOf("identifier" to "String", "parameters" to "List<Pair<String, Type>>", "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<Pair<Expr, Block>>", "elseBody" to "Optional<Block>")),
@@ -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<TypedExpr>")),
ASTNode("Call", listOf("identifier" to "String", "arguments" to "List<TypedExpr>", "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<TypedStmt>")),
ASTNode("FunctionDeclaration", listOf("identifier" to "String", "parameters" to "List<Pair<String, Type>>", "returnType" to "Type", "body" to "Block")),
ASTNode("FunctionDeclaration", listOf("identifier" to "String", "parameters" to "List<Pair<String, Type>>", "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<Pair<TypedExpr, Block>>", "elseBody" to "Optional<Block>")),

View File

@@ -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<Integer> getNestedIndexes() {
List<Integer> 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<Pair<String, Type>> parameters;
public Function(Type type, List<Pair<String, Type>> parameters) {
public final Environment scope;
public Function(Type type, List<Pair<String, Type>> parameters, Environment scope) {
this.type = type;
this.parameters = parameters;
this.scope = scope;
}
}

View File

@@ -228,7 +228,7 @@ public class TerraScriptClassGenerator {
}
expr.arguments.forEach(a -> a.accept(this));
List<Type> 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<Type> 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

View File

@@ -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<Void>, Stmt.Visitor<Void> {
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));

View File

@@ -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<TypedExpr>, Stmt.Visitor<TypedStmt>
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<TypedExpr>, Stmt.Visitor<TypedStmt>
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<TypedExpr>, Stmt.Visitor<TypedStmt>
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

View File

@@ -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 {