Add intrinsics for compile-time computing trimMargin/trimIndent

These only apply when the receiver can be resolved to a constant and the margin prefix, if specified, is also a constant.
This commit is contained in:
Jake Wharton
2019-03-07 12:22:03 -05:00
committed by Alexander Udalov
parent 2ec6ab92b5
commit 6ee987fa2e
9 changed files with 251 additions and 3 deletions
@@ -64,10 +64,10 @@ public class IntrinsicMethods {
private final IntrinsicsMap intrinsicsMap = new IntrinsicsMap();
public IntrinsicMethods(JvmTarget jvmTarget) {
this(jvmTarget, true);
this(jvmTarget, false, true);
}
public IntrinsicMethods(JvmTarget jvmTarget, boolean shouldThrowNpeOnExplicitEqualsForBoxedNull) {
public IntrinsicMethods(JvmTarget jvmTarget, boolean canReplaceStdlibRuntimeApiBehavior, boolean shouldThrowNpeOnExplicitEqualsForBoxedNull) {
intrinsicsMap.registerIntrinsic(KOTLIN_JVM, RECEIVER_PARAMETER_FQ_NAME, "javaClass", -1, JavaClassProperty.INSTANCE);
intrinsicsMap.registerIntrinsic(KOTLIN_JVM, KotlinBuiltIns.FQ_NAMES.kClass, "java", -1, new KClassJavaProperty());
intrinsicsMap.registerIntrinsic(KOTLIN_JVM, KotlinBuiltIns.FQ_NAMES.kClass, "javaObjectType", -1, new KClassJavaObjectTypeProperty());
@@ -143,6 +143,11 @@ public class IntrinsicMethods {
declareIntrinsicFunction(FQ_NAMES.string, "plus", 1, new Concat());
declareIntrinsicFunction(FQ_NAMES.string, "get", 1, new StringGetChar());
if (canReplaceStdlibRuntimeApiBehavior) {
intrinsicsMap.registerIntrinsic(TEXT_PACKAGE_FQ_NAME, FQ_NAMES.string, "trimMargin", 1, new TrimMargin());
intrinsicsMap.registerIntrinsic(TEXT_PACKAGE_FQ_NAME, FQ_NAMES.string, "trimIndent", 0, new TrimIndent());
}
declareIntrinsicFunction(FQ_NAMES.cloneable, "clone", 0, CLONE);
intrinsicsMap.registerIntrinsic(BUILT_INS_PACKAGE_FQ_NAME, KotlinBuiltIns.FQ_NAMES.any, "toString", 0, TO_STRING);
@@ -0,0 +1,63 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.codegen.intrinsics
import org.jetbrains.kotlin.codegen.Callable
import org.jetbrains.kotlin.codegen.ExpressionCodegen
import org.jetbrains.kotlin.codegen.StackValue
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.resolve.calls.callUtil.getReceiverExpression
import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument
import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.constants.StringValue
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.JAVA_STRING_TYPE
class TrimMargin : IntrinsicMethod() {
override fun toCallable(fd: FunctionDescriptor, isSuper: Boolean, resolvedCall: ResolvedCall<*>, codegen: ExpressionCodegen): Callable {
return tryApply(resolvedCall, codegen)
?: codegen.state.typeMapper.mapToCallableMethod(fd, false)
}
private fun tryApply(resolvedCall: ResolvedCall<*>, codegen: ExpressionCodegen): Callable? {
val literalText = resolvedCall.getReceiverExpression()
?.let { codegen.getCompileTimeConstant(it) as? StringValue }
?.value ?: return null
val text = when (val argument = resolvedCall.valueArguments.values.single()) {
is DefaultValueArgument -> literalText.trimMargin()
is ExpressionValueArgument -> {
val marginPrefix = argument.valueArgument?.getArgumentExpression()
?.let { codegen.getCompileTimeConstant(it) as? StringValue }
?.value ?: return null
literalText.trimMargin(marginPrefix)
}
else -> error("Unknown value argument type ${argument::class}: $argument")
}
return StringConstant(text)
}
}
class TrimIndent : IntrinsicMethod() {
override fun toCallable(fd: FunctionDescriptor, isSuper: Boolean, resolvedCall: ResolvedCall<*>, codegen: ExpressionCodegen): Callable {
return tryApply(resolvedCall, codegen)
?: codegen.state.typeMapper.mapToCallableMethod(fd, false)
}
private fun tryApply(resolvedCall: ResolvedCall<*>, codegen: ExpressionCodegen): Callable? {
val literalText = resolvedCall.getReceiverExpression()
?.let { codegen.getCompileTimeConstant(it) as? StringValue }
?.value ?: return null
val text = literalText.trimIndent()
return StringConstant(text)
}
}
private class StringConstant(private val text: String) : IntrinsicCallable(JAVA_STRING_TYPE, emptyList(), null, null), IntrinsicWithSpecialReceiver {
override fun invokeMethodWithArguments(resolvedCall: ResolvedCall<*>, receiver: StackValue, codegen: ExpressionCodegen) =
StackValue.constant(text, JAVA_STRING_TYPE)
}
@@ -200,7 +200,8 @@ class GenerationState private constructor(
val intrinsics: IntrinsicMethods = run {
val shouldUseConsistentEquals = languageVersionSettings.supportsFeature(LanguageFeature.ThrowNpeOnExplicitEqualsForBoxedNull) &&
!configuration.getBoolean(JVMConfigurationKeys.NO_EXCEPTION_ON_EXPLICIT_EQUALS_FOR_BOXED_NULL)
IntrinsicMethods(target, shouldUseConsistentEquals)
val canReplaceStdlibRuntimeApiBehavior = languageVersionSettings.apiVersion <= ApiVersion.parse(KotlinVersion.CURRENT.toString())!!
IntrinsicMethods(target, canReplaceStdlibRuntimeApiBehavior, shouldUseConsistentEquals)
}
val samWrapperClasses: SamWrapperClasses = SamWrapperClasses(this)
val globalInlineContext: GlobalInlineContext = GlobalInlineContext(diagnostics)