JS property accessors inlining (KT-13456)
This commit is contained in:
+3
-1
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.js.translate.context.Namer
|
||||
import org.jetbrains.kotlin.js.translate.context.Namer.getCapturedVarAccessor
|
||||
import org.jetbrains.kotlin.js.translate.reference.ReferenceTranslator
|
||||
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
|
||||
import org.jetbrains.kotlin.js.translate.reference.buildReifiedTypeArgs
|
||||
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.pureFqn
|
||||
import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils
|
||||
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils
|
||||
@@ -120,7 +121,8 @@ object DefaultVariableAccessCase : VariableAccessCase() {
|
||||
|
||||
override fun VariableAccessInfo.extensionReceiver(): JsExpression {
|
||||
val functionRef = ReferenceTranslator.translateAsValueReference(getAccessDescriptorIfNeeded(), context)
|
||||
return JsInvocation(functionRef, extensionReceiver!!, *additionalArguments.toTypedArray())
|
||||
val reifiedTypeArguments = resolvedCall.typeArguments.buildReifiedTypeArgs(context)
|
||||
return JsInvocation(functionRef, reifiedTypeArguments + listOf(extensionReceiver!!) + additionalArguments)
|
||||
}
|
||||
|
||||
override fun VariableAccessInfo.bothReceivers(): JsExpression {
|
||||
|
||||
+8
-1
@@ -135,7 +135,7 @@ public final class FunctionTranslator extends AbstractTranslator {
|
||||
List<JsParameter> jsParameters = new SmartList<JsParameter>();
|
||||
Map<DeclarationDescriptor, JsExpression> aliases = new HashMap<DeclarationDescriptor, JsExpression>();
|
||||
|
||||
for (TypeParameterDescriptor type : descriptor.getTypeParameters()) {
|
||||
for (TypeParameterDescriptor type : getTypeParameters(descriptor)) {
|
||||
if (type.isReified()) {
|
||||
JsName paramNameForType = context().getNameForDescriptor(type);
|
||||
jsParameters.add(new JsParameter(paramNameForType));
|
||||
@@ -158,6 +158,13 @@ public final class FunctionTranslator extends AbstractTranslator {
|
||||
return jsParameters;
|
||||
}
|
||||
|
||||
private static List<TypeParameterDescriptor> getTypeParameters(FunctionDescriptor functionDescriptor) {
|
||||
if (functionDescriptor instanceof PropertyAccessorDescriptor) {
|
||||
return ((PropertyAccessorDescriptor) functionDescriptor).getCorrespondingProperty().getTypeParameters();
|
||||
}
|
||||
return functionDescriptor.getTypeParameters();
|
||||
}
|
||||
|
||||
public static void addParameters(List<JsParameter> list, FunctionDescriptor descriptor, TranslationContext context) {
|
||||
for (ValueParameterDescriptor valueParameter : descriptor.getValueParameters()) {
|
||||
JsParameter jsParameter = new JsParameter(context.getNameForDescriptor(valueParameter));
|
||||
|
||||
@@ -19,8 +19,6 @@ package org.jetbrains.kotlin.js.translate.expression
|
||||
import com.google.dart.compiler.backend.js.ast.*
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.js.translate.context.Namer
|
||||
import org.jetbrains.kotlin.js.translate.context.TranslationContext
|
||||
import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils.*
|
||||
|
||||
private val METADATA_PROPERTIES_COUNT = 2
|
||||
|
||||
|
||||
+4
-5
@@ -185,7 +185,7 @@ class CallArgumentTranslator private constructor(
|
||||
|
||||
if (CallExpressionTranslator.shouldBeInlined(callDescriptor)) {
|
||||
val typeArgs = resolvedCall.typeArguments
|
||||
return typeArgs.addReifiedTypeArgsTo(result, context)
|
||||
return result.copy(reifiedArguments = typeArgs.buildReifiedTypeArgs(context))
|
||||
}
|
||||
|
||||
return result
|
||||
@@ -330,10 +330,9 @@ class CallArgumentTranslator private constructor(
|
||||
|
||||
}
|
||||
|
||||
private fun Map<TypeParameterDescriptor, KotlinType>.addReifiedTypeArgsTo(
|
||||
info: CallArgumentTranslator.ArgumentsInfo,
|
||||
public fun Map<TypeParameterDescriptor, KotlinType>.buildReifiedTypeArgs(
|
||||
context: TranslationContext
|
||||
): CallArgumentTranslator.ArgumentsInfo {
|
||||
): List<JsExpression> {
|
||||
|
||||
val reifiedTypeArguments = SmartList<JsExpression>()
|
||||
val patternTranslator = PatternTranslator.newInstance(context)
|
||||
@@ -349,5 +348,5 @@ private fun Map<TypeParameterDescriptor, KotlinType>.addReifiedTypeArgsTo(
|
||||
reifiedTypeArguments.add(isCheckCallable)
|
||||
}
|
||||
|
||||
return info.copy(reifiedArguments = reifiedTypeArguments)
|
||||
return reifiedTypeArguments
|
||||
}
|
||||
|
||||
+1
-1
@@ -72,7 +72,7 @@ public final class CallExpressionTranslator extends AbstractCallExpressionTransl
|
||||
}
|
||||
|
||||
public static boolean shouldBeInlined(@NotNull CallableDescriptor descriptor) {
|
||||
if (descriptor instanceof SimpleFunctionDescriptor) {
|
||||
if (descriptor instanceof SimpleFunctionDescriptor || descriptor instanceof PropertyAccessorDescriptor) {
|
||||
return InlineUtil.isInline(descriptor);
|
||||
}
|
||||
|
||||
|
||||
+42
-6
@@ -16,10 +16,12 @@
|
||||
|
||||
package org.jetbrains.kotlin.js.translate.reference;
|
||||
|
||||
import com.google.dart.compiler.backend.js.ast.JsBinaryOperation;
|
||||
import com.google.dart.compiler.backend.js.ast.JsExpression;
|
||||
import com.google.dart.compiler.backend.js.ast.JsNameRef;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator;
|
||||
import org.jetbrains.kotlin.js.translate.context.TranslationContext;
|
||||
import org.jetbrains.kotlin.js.translate.general.AbstractTranslator;
|
||||
@@ -27,6 +29,9 @@ import org.jetbrains.kotlin.psi.KtReferenceExpression;
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt;
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
||||
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall;
|
||||
import org.jetbrains.kotlin.resolve.inline.InlineUtil;
|
||||
|
||||
import static org.jetbrains.kotlin.js.translate.utils.InlineUtils.setInlineCallMetadata;
|
||||
|
||||
public class VariableAccessTranslator extends AbstractTranslator implements AccessTranslator {
|
||||
public static VariableAccessTranslator newInstance(
|
||||
@@ -39,19 +44,23 @@ public class VariableAccessTranslator extends AbstractTranslator implements Acce
|
||||
resolvedCall = ((VariableAsFunctionResolvedCall) resolvedCall).getVariableCall();
|
||||
}
|
||||
assert resolvedCall.getResultingDescriptor() instanceof VariableDescriptor;
|
||||
return new VariableAccessTranslator(context, (ResolvedCall<? extends VariableDescriptor>) resolvedCall, receiver);
|
||||
return new VariableAccessTranslator(context, referenceExpression, (ResolvedCall<? extends VariableDescriptor>) resolvedCall,
|
||||
receiver);
|
||||
}
|
||||
|
||||
|
||||
private final ResolvedCall<? extends VariableDescriptor> resolvedCall;
|
||||
private final KtReferenceExpression referenceExpression;
|
||||
private final JsExpression receiver;
|
||||
|
||||
private VariableAccessTranslator(
|
||||
@NotNull TranslationContext context,
|
||||
@NotNull KtReferenceExpression referenceExpression,
|
||||
@NotNull ResolvedCall<? extends VariableDescriptor> resolvedCall,
|
||||
@Nullable JsExpression receiver
|
||||
) {
|
||||
super(context);
|
||||
this.referenceExpression = referenceExpression;
|
||||
this.receiver = receiver;
|
||||
this.resolvedCall = resolvedCall;
|
||||
}
|
||||
@@ -59,29 +68,56 @@ public class VariableAccessTranslator extends AbstractTranslator implements Acce
|
||||
@NotNull
|
||||
@Override
|
||||
public JsExpression translateAsGet() {
|
||||
return CallTranslator.INSTANCE.translateGet(context(), resolvedCall, receiver);
|
||||
JsExpression e = CallTranslator.INSTANCE.translateGet(context(), resolvedCall, receiver);
|
||||
CallableDescriptor original = resolvedCall.getResultingDescriptor().getOriginal();
|
||||
if (original instanceof PropertyDescriptor) {
|
||||
PropertyGetterDescriptor getter = ((PropertyDescriptor) original).getGetter();
|
||||
if (InlineUtil.isInline(getter)) {
|
||||
if (e instanceof JsNameRef) {
|
||||
// Get was translated as a name reference
|
||||
setInlineCallMetadata((JsNameRef) e, referenceExpression, getter);
|
||||
} else {
|
||||
setInlineCallMetadata(e, referenceExpression, getter, context());
|
||||
}
|
||||
}
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public JsExpression translateAsSet(@NotNull JsExpression setTo) {
|
||||
return CallTranslator.INSTANCE.translateSet(context(), resolvedCall, setTo, receiver);
|
||||
JsExpression e = CallTranslator.INSTANCE.translateSet(context(), resolvedCall, setTo, receiver);
|
||||
CallableDescriptor original = resolvedCall.getResultingDescriptor().getOriginal();
|
||||
if (original instanceof PropertyDescriptor) {
|
||||
PropertySetterDescriptor setter = ((PropertyDescriptor)original).getSetter();
|
||||
if (InlineUtil.isInline(setter)) {
|
||||
if (e instanceof JsBinaryOperation && ((JsBinaryOperation) e).getOperator().isAssignment()) {
|
||||
// Set was translated as an assignment
|
||||
setInlineCallMetadata((JsNameRef) (((JsBinaryOperation) e).getArg1()), referenceExpression, setter);
|
||||
} else {
|
||||
setInlineCallMetadata(e, referenceExpression, setter, context());
|
||||
}
|
||||
}
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public AccessTranslator getCached() {
|
||||
JsExpression cachedReceiver = receiver != null ? context().cacheExpressionIfNeeded(receiver) : null;
|
||||
return new CachedVariableAccessTranslator(context(), resolvedCall, cachedReceiver);
|
||||
return new CachedVariableAccessTranslator(context(), referenceExpression, resolvedCall, cachedReceiver);
|
||||
}
|
||||
|
||||
private static class CachedVariableAccessTranslator extends VariableAccessTranslator implements AccessTranslator {
|
||||
public CachedVariableAccessTranslator(
|
||||
@NotNull TranslationContext context,
|
||||
@NotNull KtReferenceExpression referenceExpression,
|
||||
@NotNull ResolvedCall<? extends VariableDescriptor> resolvedCall,
|
||||
@Nullable JsExpression cachedReceiver
|
||||
) {
|
||||
super(context, resolvedCall, cachedReceiver);
|
||||
super(context, referenceExpression, resolvedCall, cachedReceiver);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator;
|
||||
import org.jetbrains.kotlin.js.translate.context.Namer;
|
||||
import org.jetbrains.kotlin.js.translate.context.TemporaryConstVariable;
|
||||
import org.jetbrains.kotlin.js.translate.context.TranslationContext;
|
||||
import org.jetbrains.kotlin.js.translate.expression.InlineMetadata;
|
||||
import org.jetbrains.kotlin.js.translate.general.Translation;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
|
||||
@@ -37,6 +38,7 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument;
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver;
|
||||
import org.jetbrains.kotlin.resolve.inline.InlineUtil;
|
||||
import org.jetbrains.kotlin.types.KotlinType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -58,15 +60,21 @@ public final class TranslationUtils {
|
||||
public static JsPropertyInitializer translateFunctionAsEcma5PropertyDescriptor(@NotNull JsFunction function,
|
||||
@NotNull FunctionDescriptor descriptor,
|
||||
@NotNull TranslationContext context) {
|
||||
JsExpression functionExpression = function;
|
||||
if (InlineUtil.isInline(descriptor)) {
|
||||
InlineMetadata metadata = InlineMetadata.compose(function, descriptor);
|
||||
functionExpression = metadata.getFunctionWithMetadata();
|
||||
}
|
||||
|
||||
if (DescriptorUtils.isExtension(descriptor) ||
|
||||
descriptor instanceof PropertyAccessorDescriptor &&
|
||||
shouldAccessViaFunctions(((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty())
|
||||
) {
|
||||
return translateExtensionFunctionAsEcma5DataDescriptor(function, descriptor, context);
|
||||
return translateExtensionFunctionAsEcma5DataDescriptor(functionExpression, descriptor, context);
|
||||
}
|
||||
else {
|
||||
JsStringLiteral getOrSet = context.program().getStringLiteral(getAccessorFunctionName(descriptor));
|
||||
return new JsPropertyInitializer(getOrSet, function);
|
||||
return new JsPropertyInitializer(getOrSet, functionExpression);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,9 +90,9 @@ public final class TranslationUtils {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static JsPropertyInitializer translateExtensionFunctionAsEcma5DataDescriptor(@NotNull JsFunction function,
|
||||
private static JsPropertyInitializer translateExtensionFunctionAsEcma5DataDescriptor(@NotNull JsExpression functionExpression,
|
||||
@NotNull FunctionDescriptor descriptor, @NotNull TranslationContext context) {
|
||||
JsObjectLiteral meta = createDataDescriptor(function, ModalityKt.isOverridable(descriptor), false);
|
||||
JsObjectLiteral meta = createDataDescriptor(functionExpression, ModalityKt.isOverridable(descriptor), false);
|
||||
return new JsPropertyInitializer(context.getNameForDescriptor(descriptor).makeRef(), meta);
|
||||
}
|
||||
|
||||
|
||||
@@ -44,10 +44,9 @@ import org.jetbrains.kotlin.resolve.inline.InlineStrategy
|
||||
fun setInlineCallMetadata(
|
||||
expression: JsExpression,
|
||||
psiElement: KtExpression,
|
||||
resolvedCall: ResolvedCall<*>,
|
||||
descriptor: CallableDescriptor,
|
||||
context: TranslationContext
|
||||
) {
|
||||
val descriptor = PsiUtils.getFunctionDescriptor(resolvedCall)
|
||||
assert(CallExpressionTranslator.shouldBeInlined(descriptor)) {
|
||||
"Expected descriptor of callable, that should be inlined, but got: $descriptor"
|
||||
}
|
||||
@@ -69,6 +68,23 @@ fun setInlineCallMetadata(
|
||||
visitor.accept(expression)
|
||||
}
|
||||
|
||||
fun setInlineCallMetadata(
|
||||
expression: JsExpression,
|
||||
psiElement: KtExpression,
|
||||
resolvedCall: ResolvedCall<*>,
|
||||
context: TranslationContext
|
||||
) = setInlineCallMetadata(expression, psiElement, PsiUtils.getFunctionDescriptor(resolvedCall), context)
|
||||
|
||||
fun setInlineCallMetadata(
|
||||
nameRef: JsNameRef,
|
||||
psiElement: KtExpression,
|
||||
descriptor: CallableDescriptor
|
||||
) {
|
||||
nameRef.descriptor = descriptor
|
||||
nameRef.inlineStrategy = InlineStrategy.IN_PLACE
|
||||
nameRef.psiElement = psiElement
|
||||
}
|
||||
|
||||
fun TranslationContext.aliasedName(descriptor: CallableDescriptor): JsName {
|
||||
val alias = getAliasForDescriptor(descriptor)
|
||||
val aliasName = (alias as? JsNameRef)?.name
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
// MODULE: lib
|
||||
// FILE: lib.kt
|
||||
|
||||
package test
|
||||
|
||||
var a = 0
|
||||
|
||||
inline var p1: Int
|
||||
get() = a + 10000
|
||||
set(v) {
|
||||
a = v + 100
|
||||
}
|
||||
|
||||
var p2: Int
|
||||
inline get() = a + 20000
|
||||
set(v) {
|
||||
a = v + 200
|
||||
}
|
||||
|
||||
var p3: Int
|
||||
get() = a + 30000
|
||||
inline set(v) {
|
||||
a = v + 300
|
||||
}
|
||||
|
||||
inline var Int.p4: Int
|
||||
get() = this * 100 + a + 40000
|
||||
set(v) {
|
||||
a = this + v + 400
|
||||
}
|
||||
|
||||
var Int.p5: Int
|
||||
inline get() = this * 100 + a + 50000
|
||||
set(v) {
|
||||
a = this + v + 500
|
||||
}
|
||||
|
||||
var Int.p6: Int
|
||||
get() = this * 100 + a + 60000
|
||||
inline set(v) {
|
||||
a = this + v + 600
|
||||
}
|
||||
|
||||
class A {
|
||||
inline var p7: Int
|
||||
get() = a + 70000
|
||||
set(v) {
|
||||
a = v + 700
|
||||
}
|
||||
|
||||
var p8: Int
|
||||
inline get() = a + 80000
|
||||
set(v) {
|
||||
a = v + 800
|
||||
}
|
||||
|
||||
var p9: Int
|
||||
get() = a + 90000
|
||||
inline set(v) {
|
||||
a = v + 900
|
||||
}
|
||||
|
||||
inline var Int.p10: Int
|
||||
get() = this * 100 + a + 100000
|
||||
set(v) {
|
||||
a = this + v + 1000
|
||||
}
|
||||
|
||||
var Int.p11: Int
|
||||
inline get() = this * 100 + a + 110000
|
||||
set(v) {
|
||||
a = this + v + 1100
|
||||
}
|
||||
|
||||
var Int.p12: Int
|
||||
get() = this * 100 + a + 120000
|
||||
inline set(v) {
|
||||
a = this + v + 1200
|
||||
}
|
||||
}
|
||||
|
||||
inline var A.p13: Int
|
||||
get() = a + 130000
|
||||
set(v) {
|
||||
a = v + 1300
|
||||
}
|
||||
|
||||
var A.p14: Int
|
||||
inline get() = a + 140000
|
||||
set(v) {
|
||||
a = v + 1400
|
||||
}
|
||||
|
||||
var A.p15: Int
|
||||
get() = a + 150000
|
||||
inline set(v) {
|
||||
a = v + 1500
|
||||
}
|
||||
|
||||
// MODULE: main(lib)
|
||||
// FILE: main.kt
|
||||
// PROPERTY_NOT_USED: p1
|
||||
// PROPERTY_NOT_READ_FROM: p2
|
||||
// PROPERTY_NOT_WRITTEN_TO: p3
|
||||
// CHECK_NOT_CALLED: imported$get_p4
|
||||
// CHECK_NOT_CALLED: imported$set_p4
|
||||
// CHECK_NOT_CALLED: imported$get_p5
|
||||
// CHECK_NOT_CALLED: imported$set_p6
|
||||
// PROPERTY_NOT_USED: p7
|
||||
// PROPERTY_NOT_READ_FROM: p8
|
||||
// PROPERTY_NOT_WRITTEN_TO: p9
|
||||
// CHECK_NOT_CALLED: imported$A$get_A$p10
|
||||
// CHECK_NOT_CALLED: imported$A$set_A$p10
|
||||
// CHECK_NOT_CALLED: imported$A$get_A$p11
|
||||
// CHECK_NOT_CALLED: imported$A$set_A$p12
|
||||
// CHECK_NOT_CALLED: imported$get_p13
|
||||
// CHECK_NOT_CALLED: imported$set_p13
|
||||
// CHECK_NOT_CALLED: imported$get_p14
|
||||
// CHECK_NOT_CALLED: imported$set_p15
|
||||
|
||||
import test.*
|
||||
|
||||
fun box(): String {
|
||||
p1 = 1
|
||||
if (p1 != 10101) return "test1: $p1"
|
||||
p2 = 2
|
||||
if (p2 != 20202) return "test2: $p2"
|
||||
p3 = 3
|
||||
if (p3 != 30303) return "test3: $p3"
|
||||
|
||||
4000000.p4 = 4
|
||||
if (4000000.p4 != 404040404) return "test4: ${4000000.p4}"
|
||||
5000000.p5 = 5
|
||||
if (5000000.p5 != 505050505) return "test5: ${5000000.p5}"
|
||||
6000000.p6 = 6
|
||||
if (6000000.p6 != 606060606) return "test6: ${6000000.p6}"
|
||||
|
||||
|
||||
val a = A()
|
||||
|
||||
a.p7 = 7
|
||||
if (a.p7 != 70707) return "test7: ${a.p7}"
|
||||
a.p8 = 8
|
||||
if (a.p8 != 80808) return "test8: ${a.p8}"
|
||||
a.p9 = 9
|
||||
if (a.p9 != 90909) return "test9: ${a.p9}"
|
||||
|
||||
with (a) {
|
||||
10000000.p10 = 10
|
||||
if (10000000.p10 != 1010101010) return "test10: ${10000000.p10}"
|
||||
11000000.p11 = 11
|
||||
if (11000000.p11 != 1111111111) return "test11: ${11000000.p11}"
|
||||
12000000.p12 = 12
|
||||
if (12000000.p12 != 1212121212) return "test12: ${12000000.p12}"
|
||||
}
|
||||
|
||||
a.p13 = 13
|
||||
if (a.p13 != 131313) return "test13: ${a.p13}"
|
||||
a.p14 = 14
|
||||
if (a.p14 != 141414) return "test14: ${a.p14}"
|
||||
a.p15 = 15
|
||||
if (a.p15 != 151515) return "test15: ${a.p15}"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
Reference in New Issue
Block a user