Delegated Properties: Code generation for local properties (JS)
This commit is contained in:
@@ -27,6 +27,30 @@ public class DelegatePropertyTest extends SingleFileTranslationTest {
|
||||
checkFooBoxIsOk();
|
||||
}
|
||||
|
||||
public void testLocalVal() throws Exception {
|
||||
checkFooBoxIsOk();
|
||||
}
|
||||
|
||||
public void testLocalVar() throws Exception {
|
||||
checkFooBoxIsOk();
|
||||
}
|
||||
|
||||
public void testCapturedLocalVal() throws Exception {
|
||||
checkFooBoxIsOk();
|
||||
}
|
||||
|
||||
public void testCapturedLocalVar() throws Exception {
|
||||
checkFooBoxIsOk();
|
||||
}
|
||||
|
||||
public void testCapturedLocalValNoInline() throws Exception {
|
||||
checkFooBoxIsOk();
|
||||
}
|
||||
|
||||
public void testCapturedLocalVarNoInline() throws Exception {
|
||||
checkFooBoxIsOk();
|
||||
}
|
||||
|
||||
public void testPropertyMetadata() throws Exception {
|
||||
checkFooBoxIsOk();
|
||||
}
|
||||
|
||||
+13
@@ -21,7 +21,9 @@ import com.google.dart.compiler.backend.js.ast.JsInvocation
|
||||
import com.google.dart.compiler.backend.js.ast.JsNameRef
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
|
||||
import org.jetbrains.kotlin.js.translate.context.Namer.getCapturedVarAccessor
|
||||
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils
|
||||
import org.jetbrains.kotlin.resolve.BindingContextUtils.isVarCapturedInClosure
|
||||
import java.util.*
|
||||
|
||||
@@ -53,6 +55,17 @@ object DefaultVariableAccessCase : VariableAccessCase() {
|
||||
functionRef
|
||||
}
|
||||
|
||||
val localVariableDescriptor = resolvedCall.resultingDescriptor as? LocalVariableDescriptor
|
||||
val accessorDescriptor = if (isGetAccess()) localVariableDescriptor?.getter else localVariableDescriptor?.setter
|
||||
if (accessorDescriptor != null) {
|
||||
val funRef = JsNameRef(TranslationUtils.getAccessorFunctionName(accessorDescriptor), ref)
|
||||
return if (isGetAccess()) {
|
||||
JsInvocation(funRef)
|
||||
} else {
|
||||
JsInvocation(funRef, value!!)
|
||||
}
|
||||
}
|
||||
|
||||
return constructAccessExpression(ref)
|
||||
}
|
||||
|
||||
|
||||
+23
-19
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.js.translate.declaration
|
||||
import com.google.dart.compiler.backend.js.ast.*
|
||||
import com.intellij.util.SmartList
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.impl.LocalVariableAccessorDescriptor
|
||||
import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator
|
||||
import org.jetbrains.kotlin.js.translate.context.Namer
|
||||
import org.jetbrains.kotlin.js.translate.context.Namer.getDelegateNameRef
|
||||
@@ -45,18 +46,19 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
|
||||
*/
|
||||
|
||||
fun translateAccessors(
|
||||
descriptor: PropertyDescriptor,
|
||||
descriptor: VariableDescriptorWithAccessors,
|
||||
declaration: KtProperty?,
|
||||
result: MutableList<JsPropertyInitializer>,
|
||||
context: TranslationContext
|
||||
) {
|
||||
if (descriptor.modality == Modality.ABSTRACT || JsDescriptorUtils.isSimpleFinalProperty(descriptor)) return
|
||||
if (descriptor is PropertyDescriptor
|
||||
&& (descriptor.modality == Modality.ABSTRACT || JsDescriptorUtils.isSimpleFinalProperty(descriptor))) return
|
||||
|
||||
PropertyTranslator(descriptor, declaration, context).translate(result)
|
||||
}
|
||||
|
||||
fun translateAccessors(
|
||||
descriptor: PropertyDescriptor,
|
||||
descriptor: VariableDescriptorWithAccessors,
|
||||
result: MutableList<JsPropertyInitializer>,
|
||||
context: TranslationContext
|
||||
) {
|
||||
@@ -64,7 +66,7 @@ fun translateAccessors(
|
||||
}
|
||||
|
||||
fun MutableList<JsPropertyInitializer>.addGetterAndSetter(
|
||||
descriptor: PropertyDescriptor,
|
||||
descriptor: VariableDescriptorWithAccessors,
|
||||
context: TranslationContext,
|
||||
generateGetter: () -> JsPropertyInitializer,
|
||||
generateSetter: () -> JsPropertyInitializer
|
||||
@@ -85,7 +87,7 @@ fun MutableList<JsPropertyInitializer>.addGetterAndSetter(
|
||||
}
|
||||
|
||||
private class PropertyTranslator(
|
||||
val descriptor: PropertyDescriptor,
|
||||
val descriptor: VariableDescriptorWithAccessors,
|
||||
val declaration: KtProperty?,
|
||||
context: TranslationContext
|
||||
) : AbstractTranslator(context) {
|
||||
@@ -119,7 +121,7 @@ private class PropertyTranslator(
|
||||
return generateDefaultAccessor(getterDescriptor, generateDefaultGetterFunction(getterDescriptor))
|
||||
}
|
||||
|
||||
private fun generateDefaultGetterFunction(getterDescriptor: PropertyGetterDescriptor): JsFunction {
|
||||
private fun generateDefaultGetterFunction(getterDescriptor: VariableAccessorDescriptor): JsFunction {
|
||||
val delegatedCall = bindingContext().get(BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, getterDescriptor)
|
||||
|
||||
if (delegatedCall != null) {
|
||||
@@ -127,16 +129,17 @@ private class PropertyTranslator(
|
||||
}
|
||||
|
||||
assert(!descriptor.isExtension) { "Unexpected extension property $descriptor}" }
|
||||
assert(descriptor is PropertyDescriptor) { "Property descriptor expected: $descriptor" }
|
||||
val scope = context().getScopeForDescriptor(getterDescriptor.containingDeclaration)
|
||||
val result = backingFieldReference(context(), descriptor)
|
||||
val result = backingFieldReference(context(), descriptor as PropertyDescriptor)
|
||||
val body = JsBlock(JsReturn(result))
|
||||
|
||||
return JsFunction(scope, body, accessorDescription(getterDescriptor))
|
||||
}
|
||||
|
||||
private fun generateDelegatedGetterFunction(
|
||||
getterDescriptor: PropertyGetterDescriptor,
|
||||
delegatedCall: ResolvedCall<FunctionDescriptor>
|
||||
getterDescriptor: VariableAccessorDescriptor,
|
||||
delegatedCall: ResolvedCall<FunctionDescriptor>
|
||||
): JsFunction {
|
||||
val scope = context().getScopeForDescriptor(getterDescriptor.containingDeclaration)
|
||||
val function = JsFunction(scope, JsBlock(), accessorDescription(getterDescriptor))
|
||||
@@ -158,9 +161,9 @@ private class PropertyTranslator(
|
||||
}
|
||||
|
||||
private fun contextWithPropertyMetadataCreationIntrinsified(
|
||||
context: TranslationContext, delegatedCall: ResolvedCall<FunctionDescriptor>, accessor: PropertyAccessorDescriptor
|
||||
context: TranslationContext, delegatedCall: ResolvedCall<FunctionDescriptor>, accessor: VariableAccessorDescriptor
|
||||
): TranslationContext {
|
||||
val propertyNameLiteral = context.program().getStringLiteral(accessor.correspondingProperty.name.asString())
|
||||
val propertyNameLiteral = context.program().getStringLiteral(accessor.correspondingVariable.name.asString())
|
||||
// 0th argument is instance, 1st is KProperty, 2nd (for setter) is value
|
||||
val fakeArgumentExpression =
|
||||
(delegatedCall.valueArgumentsByIndex!![1] as ExpressionValueArgument).valueArgument!!.getArgumentExpression()
|
||||
@@ -175,12 +178,12 @@ private class PropertyTranslator(
|
||||
return generateDefaultAccessor(setterDescriptor, generateDefaultSetterFunction(setterDescriptor))
|
||||
}
|
||||
|
||||
private fun generateDefaultSetterFunction(setterDescriptor: PropertySetterDescriptor): JsFunction {
|
||||
private fun generateDefaultSetterFunction(setterDescriptor: VariableAccessorDescriptor): JsFunction {
|
||||
val containingScope = context().getScopeForDescriptor(setterDescriptor.containingDeclaration)
|
||||
val function = JsFunction(containingScope, JsBlock(), accessorDescription(setterDescriptor))
|
||||
|
||||
assert(setterDescriptor.valueParameters.size == 1) { "Setter must have 1 parameter" }
|
||||
val correspondingPropertyName = setterDescriptor.correspondingProperty.name.asString()
|
||||
val correspondingPropertyName = setterDescriptor.correspondingVariable.name.asString()
|
||||
val valueParameter = function.addParameter(correspondingPropertyName).name
|
||||
val withAliased = context().innerContextWithAliased(setterDescriptor.valueParameters[0], valueParameter.makeRef())
|
||||
val delegatedCall = context().bindingContext().get(BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, setterDescriptor)
|
||||
@@ -199,25 +202,26 @@ private class PropertyTranslator(
|
||||
}
|
||||
else {
|
||||
assert(!descriptor.isExtension) { "Unexpected extension property $descriptor}" }
|
||||
val assignment = assignmentToBackingField(withAliased, descriptor, valueParameter.makeRef())
|
||||
assert(descriptor is PropertyDescriptor) { "Property descriptor expected: $descriptor" }
|
||||
val assignment = assignmentToBackingField(withAliased, descriptor as PropertyDescriptor, valueParameter.makeRef())
|
||||
function.addStatement(assignment.makeStmt())
|
||||
}
|
||||
|
||||
return function
|
||||
}
|
||||
|
||||
private fun generateDefaultAccessor(accessorDescriptor: PropertyAccessorDescriptor, function: JsFunction): JsPropertyInitializer =
|
||||
private fun generateDefaultAccessor(accessorDescriptor: VariableAccessorDescriptor, function: JsFunction): JsPropertyInitializer =
|
||||
translateFunctionAsEcma5PropertyDescriptor(function, accessorDescriptor, context())
|
||||
|
||||
private fun translateCustomAccessor(expression: KtPropertyAccessor): JsPropertyInitializer =
|
||||
Translation.functionTranslator(expression, context()).translateAsEcma5PropertyDescriptor()
|
||||
|
||||
private fun accessorDescription(accessorDescriptor: PropertyAccessorDescriptor): String {
|
||||
private fun accessorDescription(accessorDescriptor: VariableAccessorDescriptor): String {
|
||||
val accessorType =
|
||||
when(accessorDescriptor) {
|
||||
is PropertyGetterDescriptor ->
|
||||
when (accessorDescriptor) {
|
||||
is PropertyGetterDescriptor, is LocalVariableAccessorDescriptor.Getter ->
|
||||
"getter"
|
||||
is PropertySetterDescriptor ->
|
||||
is PropertySetterDescriptor, is LocalVariableAccessorDescriptor.Setter ->
|
||||
"setter"
|
||||
else ->
|
||||
throw IllegalArgumentException("Unknown accessor type ${accessorDescriptor.javaClass}")
|
||||
|
||||
+21
@@ -20,14 +20,21 @@ import com.google.dart.compiler.backend.js.ast.*;
|
||||
import com.google.dart.compiler.backend.js.ast.metadata.MetadataProperties;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.SmartList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.VariableDescriptorWithAccessors;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.annotations.KotlinRetention;
|
||||
import org.jetbrains.kotlin.js.translate.context.Namer;
|
||||
import org.jetbrains.kotlin.js.translate.context.TranslationContext;
|
||||
import org.jetbrains.kotlin.js.translate.declaration.ClassTranslator;
|
||||
import org.jetbrains.kotlin.js.translate.declaration.PropertyTranslatorKt;
|
||||
import org.jetbrains.kotlin.js.translate.expression.loopTranslator.LoopTranslator;
|
||||
import org.jetbrains.kotlin.js.translate.general.Translation;
|
||||
import org.jetbrains.kotlin.js.translate.general.TranslatorVisitor;
|
||||
@@ -216,11 +223,25 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
|
||||
// assume it is a local variable declaration
|
||||
public JsNode visitProperty(@NotNull KtProperty expression, @NotNull TranslationContext context) {
|
||||
VariableDescriptor descriptor = BindingContextUtils.getNotNull(context.bindingContext(), BindingContext.VARIABLE, expression);
|
||||
|
||||
JsExpression initializer = translateInitializerForProperty(expression, context);
|
||||
if (initializer != null && JsAstUtils.isEmptyExpression(initializer)) {
|
||||
return context.getEmptyExpression();
|
||||
}
|
||||
|
||||
KtExpression delegateExpression = expression.getDelegateExpression();
|
||||
if (delegateExpression != null) {
|
||||
SmartList<JsPropertyInitializer> propertyInitializers = new SmartList<JsPropertyInitializer>();
|
||||
PropertyTranslatorKt.translateAccessors((VariableDescriptorWithAccessors) descriptor, propertyInitializers, context);
|
||||
assert propertyInitializers.size() == 1 : descriptor;
|
||||
initializer = propertyInitializers.get(0).getValueExpr();
|
||||
JsPropertyInitializer delegateInitializer = new JsPropertyInitializer(
|
||||
context.program().getStringLiteral(Namer.getDelegateName(descriptor.getName().asString())),
|
||||
Translation.translateAsExpression(delegateExpression, context)
|
||||
);
|
||||
((JsObjectLiteral) initializer).getPropertyInitializers().add(delegateInitializer);
|
||||
}
|
||||
|
||||
JsName name = context.getNameForDescriptor(descriptor);
|
||||
if (isVarCapturedInClosure(context.bindingContext(), descriptor)) {
|
||||
JsNameRef alias = getCapturedVarAccessor(name.makeRef());
|
||||
|
||||
@@ -24,6 +24,8 @@ import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor;
|
||||
import org.jetbrains.kotlin.js.translate.context.Namer;
|
||||
import org.jetbrains.kotlin.js.translate.context.StaticContext;
|
||||
import org.jetbrains.kotlin.descriptors.impl.LocalVariableAccessorDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
|
||||
import org.jetbrains.kotlin.js.translate.context.TemporaryConstVariable;
|
||||
import org.jetbrains.kotlin.js.translate.context.TranslationContext;
|
||||
import org.jetbrains.kotlin.js.translate.general.Translation;
|
||||
@@ -56,11 +58,17 @@ public final class TranslationUtils {
|
||||
return translateExtensionFunctionAsEcma5DataDescriptor(function, descriptor, context);
|
||||
}
|
||||
else {
|
||||
JsStringLiteral getOrSet = context.program().getStringLiteral(descriptor instanceof PropertyGetterDescriptor ? "get" : "set");
|
||||
JsStringLiteral getOrSet = context.program().getStringLiteral(getAccessorFunctionName(descriptor));
|
||||
return new JsPropertyInitializer(getOrSet, function);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String getAccessorFunctionName(@NotNull FunctionDescriptor descriptor) {
|
||||
boolean isGetter = descriptor instanceof PropertyGetterDescriptor || descriptor instanceof LocalVariableAccessorDescriptor.Getter;
|
||||
return isGetter ? "get" : "set";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static JsFunction simpleReturnFunction(@NotNull JsScope functionScope, @NotNull JsExpression returnExpression) {
|
||||
return new JsFunction(functionScope, new JsBlock(new JsReturn(returnExpression)), "<simpleReturnFunction>");
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package foo
|
||||
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
class Delegate {
|
||||
operator fun getValue(t: Any?, p: KProperty<*>): Int = 1
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val prop: Int by Delegate()
|
||||
return run { if (prop == 1) "OK" else "fail" }
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package foo
|
||||
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
fun <T> myRun(f: () -> T) = f()
|
||||
|
||||
class Delegate {
|
||||
operator fun getValue(t: Any?, p: KProperty<*>): Int = 1
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val prop: Int by Delegate()
|
||||
return myRun { if (prop == 1) "OK" else "fail" }
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package foo
|
||||
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
class Delegate {
|
||||
var inner = 1
|
||||
operator fun getValue(t: Any?, p: KProperty<*>): Int = inner
|
||||
operator fun setValue(t: Any?, p: KProperty<*>, i: Int) {
|
||||
inner = i
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
var prop: Int by Delegate()
|
||||
run { prop = 2 }
|
||||
if (prop != 2) return "fail get"
|
||||
return run { if (prop != 2) "fail set" else "OK" }
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package foo
|
||||
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
fun <T> myRun(f: () -> T) = f()
|
||||
|
||||
class Delegate {
|
||||
var inner = 1
|
||||
operator fun getValue(t: Any?, p: KProperty<*>): Int = inner
|
||||
operator fun setValue(t: Any?, p: KProperty<*>, i: Int) {
|
||||
inner = i
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
var prop: Int by Delegate()
|
||||
myRun { prop = 2 }
|
||||
if (prop != 2) return "fail get"
|
||||
return myRun { if (prop != 2) "fail set" else "OK" }
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package foo
|
||||
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
class Delegate {
|
||||
operator fun getValue(t: Any?, p: KProperty<*>): Int = 1
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val prop: Int by Delegate()
|
||||
return if (prop == 1) "OK" else "fail"
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package foo
|
||||
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
class Delegate {
|
||||
var inner = 1
|
||||
operator fun getValue(t: Any?, p: KProperty<*>): Int = inner
|
||||
operator fun setValue(t: Any?, p: KProperty<*>, i: Int) {
|
||||
inner = i
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
var prop: Int by Delegate()
|
||||
if (prop != 1) return "fail get"
|
||||
prop = 2
|
||||
if (prop != 2) return "fail set"
|
||||
return "OK"
|
||||
}
|
||||
Reference in New Issue
Block a user