-
Notifications
You must be signed in to change notification settings - Fork 45
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
Replace Kotlin getters/setters with property access in codegen #496 #1002
Replace Kotlin getters/setters with property access in codegen #496 #1002
Conversation
b9fb58a
to
31ddce3
Compare
+createCgExecutableCallFromUtExecutableCall(statementModel) | ||
val call = createCgExecutableCallFromUtExecutableCall(statementModel) | ||
val callOrAccess: CgStatement = replaceCgExecutableCallWithFieldAccessIfNeeded(call) | ||
if (callOrAccess is CgExecutableCall) |
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.
Improve this code fragment to make it more readable, please
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.
Should be clearer now. Also, changed return type of replaceCgExecutableCallWithFieldAccessIfNeeded
to be nullable and added docs to it
@@ -236,6 +246,7 @@ internal class CgVariableConstructor(val context: CgContext) : | |||
val initExpr = if (isPrimitiveWrapperOrString(type)) { | |||
cgLiteralForWrapper(params) | |||
} else { | |||
// TODO: if instantiation chain could be a setter call, we need to replace it in Kotlin |
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.
We have an instantiation call, now instantiation chain right now. Anyway, I can't imagine a situation when the top-level instantiation call is a setter.
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.
Thanks for advice, deleted the question
@@ -261,6 +272,23 @@ internal class CgVariableConstructor(val context: CgContext) : | |||
return cgCall | |||
} | |||
|
|||
private fun replaceCgExecutableCallWithFieldAccessIfNeeded(call: CgExecutableCall): CgStatement { | |||
if (call !is CgMethodCall || context.codegenLanguage != CodegenLanguage.KOTLIN) |
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.
For me it seems better to write code-specific logic in a way like
when (context.codegenlangauge) {
is Java -> return call
is Kotlin -> do some specific logic
}
But I do not insist on it.
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.
Fixed here, didn't check for other places
val caller = call.caller ?: return call | ||
|
||
caller.type.fieldThisIsSetterFor(call.executableId)?.let { | ||
return CgAssignment(caller[it], call.arguments.single()) |
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.
Why don't you require that arguments contains single item here as in line 285?
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 do -- at call.arguments.single()
....
return CgAssignment(caller[it], call.arguments.single()) | ||
} | ||
caller.type.fieldThisIsGetterFor(call.executableId)?.let { | ||
require(call.arguments.isEmpty()) |
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.
require is more useful with a message with failure description
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.
Added message
*/ | ||
internal fun ClassId.fieldThisIsGetterFor(methodId: MethodId): FieldId? = | ||
allDeclaredFieldIds.firstOrNull { !it.isStatic && it.getter == methodId } |
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.
May be singleOrNull
? Same for the second method.
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.
Fixed
* Returns field of [this], such that [methodId] is a setter for it (or null if methodId doesn't represent a setter) | ||
*/ | ||
internal fun ClassId.fieldThisIsSetterFor(methodId: MethodId): FieldId? = |
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 suggest the name fieldThatIsSetWith
.
Current name means that field is a setter isself, it seems strange.
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.
Renamed this and the method below
@@ -343,7 +343,7 @@ private fun createInstance(visibility: Visibility, language: CodegenLanguage): S | |||
} | |||
CodegenLanguage.KOTLIN -> { | |||
""" | |||
${visibility by language}fun createInstance(className: String): kotlin.Any? { | |||
${visibility by language}fun createInstance(className: String): kotlin.Any { |
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.
To be honest, I don't understand this change, but can believe that is is possible :)
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.
AFAIK, this method can't return null (at least, no compilation error is produced after this change), this may help to avoid unnecessary ?.
-calls
e01920b
to
38fb0d0
Compare
Description
Now codegen properly treats Kotlin's autogenerated getters/setters and uses direct property access instead of them.
Kotlin properties are treated as fields. However, instead of
FieldId.isAccessibleFrom
which is now private, methodsFieldId.canBeReadFrom
andFieldId.canBeSetFrom
should be used. First one behaves for Java code in the same way asisAccessibleFrom
did before, but for Kotlin it also returns true if there is an accessible getter. Behavior ofFieldId.canBeSetFrom
hasn't been changed for Java, but for Kotlin it now also returns true if there is accessible setter/getter pair. See docs for more details.These two methods guarantee that codegen produces code with field access where it is possible. However, some calls to getters/setters may be produced by
AssembleModelGenerator
, so this PR also addsreplaceCgExecutableCallWithFieldAccessIfNeeded
to replace such calls with direct accesses.Fixes #496
Type of Change
Please delete options that are not relevant.
How Has This Been Tested?
Manual Scenario
Works correctly on examples from #496 and #948
Checklist (remove irrelevant options):
This is the author self-check list