-
Notifications
You must be signed in to change notification settings - Fork 7
Support cross-backend adjustment #471
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
fa1900a
2f14fee
05b4883
4f8a2ff
d72a62a
076b85f
2fce200
6f3375c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,6 +2,8 @@ package lang.temper.be.java | |
|
|
||
| import lang.temper.ast.deepSlice | ||
| import lang.temper.ast.toLispy | ||
| import lang.temper.be.BackendAdjuster | ||
| import lang.temper.be.BackendAdjusterFactory | ||
| import lang.temper.be.Dependencies | ||
| import lang.temper.be.java.JavaOperator.Assign | ||
| import lang.temper.be.tmpl.FnAutodoc | ||
|
|
@@ -66,6 +68,7 @@ class JavaTranslator( | |
| /** a shared instance for consistent name mapping */ | ||
| private val topNames: JavaNames, | ||
| private val dependenciesBuilder: Dependencies.Builder<JavaBackend>? = null, | ||
| private val adjusterFactory: BackendAdjusterFactory? = null, | ||
| ) { | ||
| /** Spin off an instance for a given module */ | ||
| private fun forModule(module: ModuleName, programMeta: J.ProgramMeta = J.ProgramMeta(unknownPos)): ModuleScope = | ||
|
|
@@ -132,6 +135,7 @@ class JavaTranslator( | |
|
|
||
| /** Might even stay null for snippets. */ | ||
| private var module: TmpL.Module? = null | ||
| private var adjuster: BackendAdjuster? = null | ||
|
|
||
| private fun activeDecls(decls: MutableList<J.ClassBodyDeclaration>) = when { | ||
| processingTestCode -> moduleTestDecls | ||
|
|
@@ -157,6 +161,7 @@ class JavaTranslator( | |
|
|
||
| fun module(module: TmpL.Module): List<J.Program> { | ||
| this.module = module | ||
| adjuster = adjusterFactory?.makeAdjuster(module, this) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Each adjuster is per module, so I make an adjuster for each ModuleScope in JavaTranslator. And the |
||
| val result = module.result | ||
| topLevels@ for (tl in module.topLevels) { | ||
| try { | ||
|
|
@@ -236,6 +241,7 @@ class JavaTranslator( | |
| addAll(moduleTestDecls) | ||
| } | ||
| } | ||
| adjuster?.adjustFilesAfterTranslation(programs) | ||
| return programs | ||
| } | ||
|
|
||
|
|
@@ -481,7 +487,9 @@ class JavaTranslator( | |
| }.also { add(it.asNameExpr().asArgument()) } | ||
| } | ||
| }, | ||
| ).exprOrReturnStatement(shouldReturn = result !is J.VoidType).also { add(it) } | ||
| ).let { call -> | ||
| adjuster?.adjustConnectedCall(fn, call as J.ExpressionStatementExpr) ?: call | ||
| }.exprOrReturnStatement(shouldReturn = result !is J.VoidType).also { add(it) } | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Adjuster only operate in limited contexts so far, and connected call adjustment is one of those places. And note that a class cast exception would happen here if adjusters don't dynamically full the generic function signature. |
||
| }.let { J.BlockStatement(pos, it) } | ||
| } | ||
|
|
||
|
|
@@ -517,7 +525,7 @@ class JavaTranslator( | |
| private fun moduleFunction(t: TmpL.FunctionDeclaration) { | ||
| val autodoc = autodocFor(t) | ||
| val name = names.moduleFunction(t.name).second.toIdentifier(t.name.pos) | ||
| val result = resultType(names, t.returnType, pos = t.returnType.pos) | ||
| val result = resultType(t) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For my adjuster in a separate repo, I wanted my capabilities and conveniences, so I factored out more helpers and made them public. Then modified code here to use the new helpers. Note the |
||
| val body = when { | ||
| t.metadata.any { it.key.symbol == connectedSymbol } && module?.isStdLib != true -> | ||
| connectedBody(t, result) | ||
|
|
@@ -898,7 +906,7 @@ class JavaTranslator( | |
| } | ||
| } | ||
| is TmpL.Getter -> { | ||
| val result = resultType(names, m.returnType, m.pos) | ||
| val result = resultType(m) | ||
| val name = names.getterName(m.dotName, JavaType.toFrontend(m.returnType.ot)) | ||
| val parameters = parameters(m.parameters) | ||
| val prop = t.members.firstNotNullOfOrNull { | ||
|
|
@@ -924,7 +932,7 @@ class JavaTranslator( | |
| ) | ||
| } | ||
| is TmpL.Setter -> { | ||
| val result = resultType(names, m.returnType, m.pos) | ||
| val result = resultType(m) | ||
| val name = names.setterName(m.dotName) | ||
| val parameters = parameters(m.parameters) | ||
| add( | ||
|
|
@@ -958,7 +966,7 @@ class JavaTranslator( | |
| J.ModStatic.Dynamic | ||
| }, | ||
| ) | ||
| val tentativeResult = resultType(names, m.returnType, m.pos) | ||
| val tentativeResult = resultType(m) | ||
|
|
||
| val boxedTypeAdjustments = (m as? TmpL.NormalMethod)?.let { | ||
| findJavaParametersThatNeedAdjustmentToBoxedType( | ||
|
|
@@ -1066,7 +1074,7 @@ class JavaTranslator( | |
| modAccess = access(m), | ||
| modFinal = final(m.assignOnce), | ||
| ), | ||
| type = JavaType.fromTmpL(m.type, names).toTypeAst(m.pos), | ||
| type = varType(m), | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So |
||
| variable = names.field(m.name), | ||
| initializer = null, | ||
| ), | ||
|
|
@@ -1082,7 +1090,7 @@ class JavaTranslator( | |
| modFinal = final(m.assignOnce), | ||
| modStatic = J.ModStatic.Static, | ||
| ), | ||
| type = JavaType.fromTmpL(m.type, names).toTypeAst(m.pos), | ||
| type = varType(m), | ||
| variable = names.staticField(m.dotName), | ||
| initializer = expr(m.expression), | ||
| ), | ||
|
|
@@ -1164,7 +1172,7 @@ class JavaTranslator( | |
| pos = m.pos, | ||
| autodoc = autodocFor(m), | ||
| body = m.body, | ||
| result = resultType(names, m.returnType, m.pos), | ||
| result = resultType(m), | ||
| name = names.getterName(m.dotName, JavaType.toFrontend(m.returnType.ot)), | ||
| params = parameters(m.parameters), | ||
| ), | ||
|
|
@@ -1174,7 +1182,7 @@ class JavaTranslator( | |
| pos = m.pos, | ||
| autodoc = autodocFor(m), | ||
| body = m.body, | ||
| result = resultType(names, m.returnType, m.pos), | ||
| result = resultType(m), | ||
| name = names.setterName(m.dotName), | ||
| params = parameters(m.parameters), | ||
| ), | ||
|
|
@@ -1184,7 +1192,7 @@ class JavaTranslator( | |
| pos = m.pos, | ||
| autodoc = autodocFor(m), | ||
| body = m.body, | ||
| result = resultType(names, m.returnType, m.pos), | ||
| result = resultType(m), | ||
| name = names.method(m.dotName).toIdentifier(m.dotName.pos), | ||
| params = parameters(m.parameters), | ||
| typeParams = typeFormals(m.typeParameters), | ||
|
|
@@ -1196,7 +1204,7 @@ class JavaTranslator( | |
| autodoc = autodocFor(m), | ||
| body = m.body, | ||
| name = names.method(m.dotName).toIdentifier(m.dotName.pos), | ||
| result = resultType(names, m.returnType, m.pos), | ||
| result = resultType(m), | ||
| params = parameters(m.parameters), | ||
| typeParams = typeFormals(m.typeParameters), | ||
| isStatic = true, | ||
|
|
@@ -1211,7 +1219,7 @@ class JavaTranslator( | |
| J.InterfaceFieldDeclaration( | ||
| pos = m.pos, | ||
| javadoc = javadoc(autodocFor(m.pos, m.metadata)), | ||
| type = JavaType.fromTmpL(m.type, names).toTypeAst(m.type.pos), | ||
| type = varType(m), | ||
| variables = listOf( | ||
| J.VariableDeclarator( | ||
| m.pos, | ||
|
|
@@ -1243,6 +1251,28 @@ class JavaTranslator( | |
| ) | ||
| } | ||
|
|
||
| fun resultType(fn: TmpL.FunctionDeclarationOrMethod): J.ResultType { | ||
| // TODO Some manual calls chose the return type pos but most chose the function pos. | ||
| // TODO Maybe makes sense to standardize here, but which is best? | ||
| return resultType(names, fn.returnType, fn.pos) | ||
| } | ||
|
|
||
| fun resultType(type: TmpL.AType, pos: Position? = null): J.ResultType { | ||
| return resultType(names, type, pos ?: type.pos) | ||
| } | ||
|
|
||
| fun varType(property: TmpL.Property): J.Type { | ||
| return varType(property.type, property.pos) | ||
| } | ||
|
|
||
| fun varType(v: TmpL.VarLike): J.Type { | ||
| return JavaType.fromFrontend(v.descriptor, names).toTypeAst(v.pos) | ||
| } | ||
|
|
||
| fun varType(type: TmpL.AType, pos: Position? = null): J.Type { | ||
| return JavaType.fromTmpL(type, names).toTypeAst(pos ?: type.pos) | ||
| } | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Part of the line count increase is just these helpers. |
||
|
|
||
| /** Create an object containing the method parameters and necessary preamble statements. */ | ||
| private fun parameters(px: TmpL.Parameters): ParamsPreamble { | ||
| val rest = px.restParameter | ||
|
|
@@ -1405,8 +1435,7 @@ class JavaTranslator( | |
| scope.addDecl( | ||
| J.FieldDeclaration( | ||
| pos, | ||
| type = JavaType.fromTmpL(param.type, names) | ||
| .toTypeAst(pos), | ||
| type = varType(param), | ||
| variable = localName.outName.toIdentifier(varId.pos), | ||
| initializer = newNames[oldName.outName]!!.toNameExpr(varId.pos), | ||
| ), | ||
|
|
@@ -1452,7 +1481,7 @@ class JavaTranslator( | |
| scope.addDecl( | ||
| J.FieldDeclaration( | ||
| stmt.pos, | ||
| type = JavaType.fromTmpL(stmt.type, names).toTypeAst(stmt.pos), | ||
| type = varType(stmt), | ||
| variable = localName.outName.toIdentifier(varId.pos), | ||
| initializer = stmt.init?.let(::expr), | ||
| ), | ||
|
|
@@ -1493,7 +1522,7 @@ class JavaTranslator( | |
| stmts.add( | ||
| J.LocalVariableDeclaration( | ||
| pos, | ||
| type = JavaType.fromTmpL(param.type, names).toTypeAst(pos), | ||
| type = varType(param), | ||
| name = newName.toIdentifier(varId.pos), | ||
| expr = oldName.toIdentifier(varId.pos).asNameExpr(), | ||
| ), | ||
|
|
@@ -1569,7 +1598,7 @@ class JavaTranslator( | |
| val javaType: J.Type get() = JavaType.fromSig(funcType, names).toTypeAst(pos) | ||
|
|
||
| /** the result type of the function */ | ||
| val javaResultType: J.ResultType get() = resultType(names, tmplFunc.returnType, tmplFunc.returnType.pos) | ||
| val javaResultType: J.ResultType get() = resultType(tmplFunc) | ||
|
|
||
| /** a lambda expression can be used in a local variable declaration, or also in a forward declared form. */ | ||
| fun toLambdaExpr() = | ||
|
|
@@ -1659,7 +1688,7 @@ class JavaTranslator( | |
| private fun localVar(t: TmpL.LocalDeclaration) = | ||
| J.LocalVariableDeclaration( | ||
| t.pos, | ||
| type = JavaType.fromTmpL(t.type, names).toTypeAst(t.pos), | ||
| type = varType(t), | ||
| name = names.lookupRegularLocalNameObj(t.name).outName.toIdentifier(t.name.pos), | ||
| expr = t.init?.let(::expr), | ||
| ) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -46,4 +46,4 @@ internal fun ResolvedName.simpleText(): String = when (this) { | |
| internal fun ResolvedName.distinctSafeText() = distinctText().safeIdentifier() | ||
|
|
||
| /** Apply a simple set of rules to extract a name's text and ensure the identifier is safe for Java. */ | ||
| internal fun ResolvedName.simpleSafeText() = simpleText().safeIdentifier() | ||
| fun ResolvedName.simpleSafeText() = simpleText().safeIdentifier() | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wanted access to this. Now I don't remember if I used it in the end or not. If so, that's in a separate repo. |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1266,7 +1266,7 @@ AssignmentExpr requires `operator.operator.isAssignment()` ; | |
| * The target of an assignment may be a name, or an accessor. | ||
| * JLS 15.26 TODO add array access | ||
| */ | ||
| LeftHandSide = NameExpr | FieldAccessExpr ; | ||
| LeftHandSide = NameExpr | FieldAccessExpr | StaticFieldAccessExpr ; | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is unrelated to backend adjustment itself, but it's handy for my needs in some adjuster code in a different repo. |
||
|
|
||
| /** | ||
| * A lambda expression. JLS 15.27 | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -131,7 +131,7 @@ abstract class FunctionalTestRunner<BACKEND : Backend<BACKEND>>( | |
| val backendOrganization = organizeBackends( | ||
| listOf(backendId), | ||
| lookupFactory = ::lookupFactory, | ||
| onMissingFactory = { error(it) }, | ||
| onError = { error(it) }, | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I generalized this for more error kinds now. |
||
| ) | ||
| // TODO Actually build by buckets? | ||
| val outputRoot = OutputRoot(MemoryFileSystem()) | ||
|
|
@@ -160,6 +160,7 @@ abstract class FunctionalTestRunner<BACKEND : Backend<BACKEND>>( | |
| outputRoot = outputRoot, | ||
| preparedModules = preparedModules, | ||
| test = test, | ||
| adjusterFactory = backendOrganization.adjusterFactories[neededBackendId], | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These come originally from the BackendFactory. |
||
| ) | ||
| } | ||
|
|
||
|
|
@@ -204,6 +205,7 @@ abstract class FunctionalTestRunner<BACKEND : Backend<BACKEND>>( | |
| outputRoot: OutputRoot, | ||
| preparedModules: PreparedFunctionalTest, | ||
| test: FunctionalTestBase, | ||
| adjusterFactory: BackendAdjusterFactory?, | ||
| ) = run { | ||
| val supportedBackendList = listOf(backendId) | ||
| val functionalTestLibraryConfiguration = LibraryConfiguration( | ||
|
|
@@ -313,6 +315,7 @@ abstract class FunctionalTestRunner<BACKEND : Backend<BACKEND>>( | |
| config = config, | ||
| dependenciesBuilder = dependenciesBuilder, | ||
| rawBackendFiles = rawBackendFiles, | ||
| adjusterFactory = adjusterFactory, | ||
| ), | ||
| ) | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've implemented adjustment only in be-java for now. And in separate test backends also.