- Fix code generation for inlined String::plus

#KT-10131 Fixed.
- Make extra factory methods in IntrinsicMethod protected.
This ensures that any of them can be properly overridden.
This commit is contained in:
Dmitry Petrov
2015-11-23 15:16:08 +03:00
parent 2b4868c3ce
commit 931463af0d
5 changed files with 49 additions and 14 deletions
@@ -17,18 +17,17 @@
package org.jetbrains.kotlin.codegen.intrinsics package org.jetbrains.kotlin.codegen.intrinsics
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.codegen.AsmUtil.genInvokeAppendMethod import org.jetbrains.kotlin.codegen.AsmUtil.genInvokeAppendMethod
import org.jetbrains.kotlin.codegen.AsmUtil.genStringBuilderConstructor import org.jetbrains.kotlin.codegen.AsmUtil.genStringBuilderConstructor
import org.jetbrains.kotlin.codegen.Callable
import org.jetbrains.kotlin.codegen.CallableMethod
import org.jetbrains.kotlin.codegen.ExpressionCodegen
import org.jetbrains.kotlin.codegen.StackValue
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtBinaryExpression import org.jetbrains.kotlin.psi.KtBinaryExpression
import org.jetbrains.kotlin.psi.KtCallableReferenceExpression
import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.jvm.AsmTypes import org.jetbrains.kotlin.resolve.jvm.AsmTypes
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.JAVA_STRING_TYPE import org.jetbrains.kotlin.resolve.jvm.AsmTypes.JAVA_STRING_TYPE
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
@@ -41,11 +40,11 @@ public class Concat : IntrinsicMethod() {
arguments: List<KtExpression>, arguments: List<KtExpression>,
receiver: StackValue receiver: StackValue
): Type { ): Type {
if (element is KtBinaryExpression && element.getOperationReference().getReferencedNameElementType() == KtTokens.PLUS) { if (element is KtBinaryExpression && element.operationReference.getReferencedNameElementType() == KtTokens.PLUS) {
// LHS + RHS // LHS + RHS
genStringBuilderConstructor(v) genStringBuilderConstructor(v)
codegen.invokeAppend(element.getLeft()) codegen.invokeAppend(element.left)
codegen.invokeAppend(element.getRight()) codegen.invokeAppend(element.right)
} }
else { else {
// LHS?.plus(RHS) // LHS?.plus(RHS)
@@ -60,7 +59,6 @@ public class Concat : IntrinsicMethod() {
return JAVA_STRING_TYPE return JAVA_STRING_TYPE
} }
override fun toCallable(method: CallableMethod): Callable = override fun toCallable(method: CallableMethod): Callable =
object : IntrinsicCallable(method) { object : IntrinsicCallable(method) {
override fun invokeMethodWithArguments( override fun invokeMethodWithArguments(
@@ -68,16 +66,37 @@ public class Concat : IntrinsicMethod() {
receiver: StackValue, receiver: StackValue,
codegen: ExpressionCodegen codegen: ExpressionCodegen
): StackValue { ): StackValue {
if (resolvedCall.call.callElement.parent is KtCallableReferenceExpression) {
// NB we come here only in case of inlined callable reference to String::plus.
// This will map arguments properly, invoking callbacks defined in Callable.
return super.invokeMethodWithArguments(resolvedCall, receiver, codegen)
}
return StackValue.operation(returnType) { return StackValue.operation(returnType) {
val arguments = resolvedCall.getCall().getValueArguments().map { it.getArgumentExpression()!! } val arguments = resolvedCall.call.valueArguments.map { it.getArgumentExpression()!! }
val actualType = generateImpl( val actualType = generateImpl(
codegen, it, returnType, codegen, it, returnType,
resolvedCall.getCall().getCallElement(), resolvedCall.call.callElement,
arguments, arguments,
StackValue.receiver(resolvedCall, receiver, codegen, this) StackValue.receiver(resolvedCall, receiver, codegen, this)
) )
StackValue.coerce(actualType, returnType, it) StackValue.coerce(actualType, returnType, it)
} }
} }
override fun afterReceiverGeneration(v: InstructionAdapter) {
v.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder")
v.dupX1()
v.swap()
v.invokespecial("java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V", false)
}
override fun invokeIntrinsic(v: InstructionAdapter) {
// String::plus has type String.(Any?) -> String, thus we have no argument type information
// in case of callable reference passed to a generic function, e.g.:
// charArrayOf('O', 'K').fold("", String::plus)
// TODO Make String::plus generic, and invoke proper StringBuilder#append.
AsmUtil.genInvokeAppendMethod(v, AsmTypes.OBJECT_TYPE)
v.invokevirtual("java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false)
}
} }
} }
@@ -37,17 +37,17 @@ public abstract class IntrinsicMethod {
} }
@NotNull @NotNull
public Callable toCallable(@NotNull CallableMethod method, boolean isSuper, @NotNull ResolvedCall resolvedCall) { protected Callable toCallable(@NotNull CallableMethod method, boolean isSuper, @NotNull ResolvedCall resolvedCall) {
return toCallable(method, isSuper); return toCallable(method, isSuper);
} }
@NotNull @NotNull
public Callable toCallable(@NotNull CallableMethod method, boolean isSuperCall) { protected Callable toCallable(@NotNull CallableMethod method, boolean isSuperCall) {
return toCallable(method); return toCallable(method);
} }
@NotNull @NotNull
public Callable toCallable(@NotNull CallableMethod method) { protected Callable toCallable(@NotNull CallableMethod method) {
throw new UnsupportedOperationException("Not implemented"); throw new UnsupportedOperationException("Not implemented");
} }
@@ -0,0 +1,2 @@
fun box(): String =
listOf('O', 'K').fold("", String::plus)
@@ -0,0 +1,2 @@
fun box(): String =
charArrayOf('O', 'K').fold("", String::plus)
@@ -1996,6 +1996,18 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode
doTestWithStdlib(fileName); doTestWithStdlib(fileName);
} }
@TestMetadata("kt10131.kt")
public void testKt10131() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/intrinsics/kt10131.kt");
doTestWithStdlib(fileName);
}
@TestMetadata("kt10131a.kt")
public void testKt10131a() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/intrinsics/kt10131a.kt");
doTestWithStdlib(fileName);
}
@TestMetadata("kt5937.kt") @TestMetadata("kt5937.kt")
public void testKt5937() throws Exception { public void testKt5937() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/intrinsics/kt5937.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/intrinsics/kt5937.kt");