Mangle bytecode method names according to declaration scope

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

No files matched your search

@@ -176,7 +176,7 @@ tasks.register("genTerrascriptAstClasses") {
listOf( listOf(
ASTNode("Expression", listOf("expression" to "Expr")), ASTNode("Expression", listOf("expression" to "Expr")),
ASTNode("Block", listOf("statements" to "List<Stmt>")), 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("VariableDeclaration", listOf("type" to "Type", "identifier" to "String", "value" to "Expr")),
ASTNode("Return", listOf("value" to "Expr"), listOf("type" to "Type")), 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>")), 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("Grouping", listOf("expression" to "TypedExpr")),
ASTNode("Literal", listOf("value" to "Object")), ASTNode("Literal", listOf("value" to "Object")),
ASTNode("Unary", listOf("operator" to "UnaryOperator", "operand" to "TypedExpr")), 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("Variable", listOf("identifier" to "String")),
ASTNode("Assignment", listOf("lValue" to "Variable", "rValue" to "TypedExpr")), ASTNode("Assignment", listOf("lValue" to "Variable", "rValue" to "TypedExpr")),
ASTNode("Void", listOf()), ASTNode("Void", listOf()),
@@ -219,7 +219,7 @@ tasks.register("genTerrascriptAstClasses") {
listOf( listOf(
ASTNode("Expression", listOf("expression" to "TypedExpr")), ASTNode("Expression", listOf("expression" to "TypedExpr")),
ASTNode("Block", listOf("statements" to "List<TypedStmt>")), 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("VariableDeclaration", listOf("type" to "Type", "identifier" to "String", "value" to "TypedExpr")),
ASTNode("Return", listOf("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>")), ASTNode("If", listOf("condition" to "TypedExpr", "trueBody" to "Block", "elseIfClauses" to "List<Pair<TypedExpr, Block>>", "elseBody" to "Optional<Block>")),
@@ -3,6 +3,7 @@ package com.dfsek.terra.addons.terrascript;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Parameter; import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
@@ -31,33 +32,50 @@ public class Environment {
private final boolean inLoop; 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.outer = outer;
this.canAccessOuterVariables = canAccessOuterVariables; this.canAccessOuterVariables = canAccessOuterVariables;
this.inLoop = inLoop; 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 // Populate symbol tables with built-in Java implemented methods
TerraScript.BUILTIN_FUNCTIONS.forEach((name, method) -> symbolTable TerraScript.BUILTIN_FUNCTIONS.forEach((name, method) -> symbolTable
.put(name, .put(name,
new Function( new Function(
Type.from(method.getReturnType()).orElseThrow(() -> new RuntimeException("")), Type.from(method.getReturnType()).orElseThrow(() -> new RuntimeException("")),
// Map Java classes to TerraScript types // 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() { public static Environment global() {
return new Environment(null, false, false); return new Environment(null, false, false, 0);
} }
public Environment lexicalInner() { public Environment lexicalInner() {
return new Environment(this, true, inLoop); return new Environment(this, true, inLoop, innerCount++);
} }
public Environment loopInner() { public Environment loopInner() {
return new Environment(this, true, true); return new Environment(this, true, true, innerCount++);
} }
public Environment functionalInner() { 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() { public Environment outer() {
@@ -117,9 +135,12 @@ public class Environment {
public final List<Pair<String, Type>> parameters; 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.type = type;
this.parameters = parameters; this.parameters = parameters;
this.scope = scope;
} }
} }
@@ -228,7 +228,7 @@ public class TerraScriptClassGenerator {
} }
expr.arguments.forEach(a -> a.accept(this)); expr.arguments.forEach(a -> a.accept(this));
List<Type> parameters = expr.arguments.stream().map(e -> e.type).toList(); 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; return null;
} }
@@ -284,8 +284,8 @@ public class TerraScriptClassGenerator {
List<Type> parameterTypes = stmt.parameters.stream().map(Pair::getRight).toList(); List<Type> parameterTypes = stmt.parameters.stream().map(Pair::getRight).toList();
int access = Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC; 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 method.visitCode(); // Start method body
@@ -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;
import com.dfsek.terra.addons.terrascript.Environment.ScopeException.NonexistentSymbolException; 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.ScopeException.SymbolTypeMismatchException;
import com.dfsek.terra.addons.terrascript.Environment.Symbol;
import com.dfsek.terra.addons.terrascript.ErrorHandler; import com.dfsek.terra.addons.terrascript.ErrorHandler;
import com.dfsek.terra.addons.terrascript.Type; import com.dfsek.terra.addons.terrascript.Type;
import com.dfsek.terra.addons.terrascript.ast.Expr; 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); stmt.body.accept(this);
currentScope = currentScope.outer(); currentScope = currentScope.outer();
try { 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) { } catch(Environment.ScopeException.SymbolAlreadyExistsException e) {
errorHandler.add(new IdentifierAlreadyDeclaredException("Name '" + stmt.identifier + "' is already defined in this scope", errorHandler.add(new IdentifierAlreadyDeclaredException("Name '" + stmt.identifier + "' is already defined in this scope",
stmt.position)); stmt.position));
@@ -5,6 +5,7 @@ import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import com.dfsek.terra.addons.terrascript.Environment; 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.ErrorHandler;
import com.dfsek.terra.addons.terrascript.Type; import com.dfsek.terra.addons.terrascript.Type;
import com.dfsek.terra.addons.terrascript.ast.Expr.Assignment; 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; } TypeChecker(ErrorHandler errorHandler) { this.errorHandler = errorHandler; }
private static String scopedIdentifier(String identifier, Symbol.Function symbol) {
return identifier + "$" + symbol.scope.name;
}
@Override @Override
public TypedExpr visitBinaryExpr(Binary expr) { public TypedExpr visitBinaryExpr(Binary expr) {
TypedExpr left = expr.left.accept(this); TypedExpr left = expr.left.accept(this);
@@ -124,7 +129,7 @@ public class TypeChecker implements Visitor<TypedExpr>, Stmt.Visitor<TypedStmt>
providedType + " instead", expr.position)); 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 @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", new InvalidFunctionDeclarationException("Function body for '" + stmt.identifier + "' does not contain return statement",
stmt.position)); 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 @Override
@@ -73,7 +73,10 @@ public class CodeGenTest {
} }
fun concatThree(a: str, b: str, c: str): str { 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 { fun retStr(): str {