JS backend: support for explicit delegation
#KT-4479 Fixed
This commit is contained in:
@@ -18,18 +18,19 @@ package org.jetbrains.jet.backend.common;
|
|||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
|
import org.jetbrains.jet.lang.descriptors.*;
|
||||||
import org.jetbrains.jet.lang.descriptors.ClassifierDescriptor;
|
import org.jetbrains.jet.lang.psi.JetDelegationSpecifier;
|
||||||
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
|
import org.jetbrains.jet.lang.psi.JetExpression;
|
||||||
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
|
import org.jetbrains.jet.lang.psi.JetSimpleNameExpression;
|
||||||
|
import org.jetbrains.jet.lang.resolve.BindingContext;
|
||||||
import org.jetbrains.jet.lang.resolve.calls.CallResolverUtil;
|
import org.jetbrains.jet.lang.resolve.calls.CallResolverUtil;
|
||||||
|
import org.jetbrains.jet.lang.resolve.calls.callUtil.CallUtilPackage;
|
||||||
|
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
|
||||||
import org.jetbrains.jet.lang.resolve.name.Name;
|
import org.jetbrains.jet.lang.resolve.name.Name;
|
||||||
import org.jetbrains.jet.lang.types.JetType;
|
import org.jetbrains.jet.lang.types.JetType;
|
||||||
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
|
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.*;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Backend-independent utility class.
|
* Backend-independent utility class.
|
||||||
@@ -91,6 +92,66 @@ public class CodegenUtil {
|
|||||||
return function;
|
return function;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static PropertyDescriptor getDelegatePropertyIfAny(JetExpression expression, ClassDescriptor classDescriptor, BindingContext bindingContext) {
|
||||||
|
PropertyDescriptor propertyDescriptor = null;
|
||||||
|
if (expression instanceof JetSimpleNameExpression) {
|
||||||
|
ResolvedCall<?> call = CallUtilPackage.getResolvedCall(expression, bindingContext);
|
||||||
|
if (call != null) {
|
||||||
|
CallableDescriptor callResultingDescriptor = call.getResultingDescriptor();
|
||||||
|
if (callResultingDescriptor instanceof ValueParameterDescriptor) {
|
||||||
|
ValueParameterDescriptor valueParameterDescriptor = (ValueParameterDescriptor) callResultingDescriptor;
|
||||||
|
// constructor parameter
|
||||||
|
if (valueParameterDescriptor.getContainingDeclaration() instanceof ConstructorDescriptor) {
|
||||||
|
// constructor of my class
|
||||||
|
if (valueParameterDescriptor.getContainingDeclaration().getContainingDeclaration() == classDescriptor) {
|
||||||
|
propertyDescriptor = bindingContext.get(BindingContext.VALUE_PARAMETER_AS_PROPERTY, valueParameterDescriptor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: when and if frontend will allow properties defined not as constructor parameters to be used in delegation specifier
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return propertyDescriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isFinalPropertyWithBackingField(PropertyDescriptor propertyDescriptor, BindingContext bindingContext) {
|
||||||
|
return propertyDescriptor != null &&
|
||||||
|
!propertyDescriptor.isVar() &&
|
||||||
|
Boolean.TRUE.equals(bindingContext.get(BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<CallableMemberDescriptor, CallableMemberDescriptor> getDelegates(ClassDescriptor descriptor, ClassDescriptor toClass) {
|
||||||
|
Map<CallableMemberDescriptor, CallableMemberDescriptor> result = new LinkedHashMap<CallableMemberDescriptor, CallableMemberDescriptor>();
|
||||||
|
for (DeclarationDescriptor declaration : descriptor.getDefaultType().getMemberScope().getAllDescriptors()) {
|
||||||
|
if (declaration instanceof CallableMemberDescriptor) {
|
||||||
|
CallableMemberDescriptor callableMemberDescriptor = (CallableMemberDescriptor) declaration;
|
||||||
|
if (callableMemberDescriptor.getKind() == CallableMemberDescriptor.Kind.DELEGATION) {
|
||||||
|
Set<? extends CallableMemberDescriptor> overriddenDescriptors = callableMemberDescriptor.getOverriddenDescriptors();
|
||||||
|
for (CallableMemberDescriptor overriddenDescriptor : overriddenDescriptors) {
|
||||||
|
if (overriddenDescriptor.getContainingDeclaration() == toClass) {
|
||||||
|
assert !result.containsKey(callableMemberDescriptor) :
|
||||||
|
"overridden is already set for " + callableMemberDescriptor;
|
||||||
|
result.put(callableMemberDescriptor, overriddenDescriptor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static ClassDescriptor getSuperClassByDelegationSpecifier(@NotNull JetDelegationSpecifier specifier, @NotNull BindingContext bindingContext) {
|
||||||
|
JetType superType = bindingContext.get(BindingContext.TYPE, specifier.getTypeReference());
|
||||||
|
assert superType != null : "superType should not be null: " + specifier.getText();
|
||||||
|
|
||||||
|
ClassDescriptor superClassDescriptor = (ClassDescriptor) superType.getConstructor().getDeclarationDescriptor();
|
||||||
|
assert superClassDescriptor != null : "superClassDescriptor should not be null: " + specifier.getText();
|
||||||
|
return superClassDescriptor;
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean valueParameterClassesMatch(
|
private static boolean valueParameterClassesMatch(
|
||||||
@NotNull List<ValueParameterDescriptor> parameters,
|
@NotNull List<ValueParameterDescriptor> parameters,
|
||||||
@NotNull List<ClassifierDescriptor> classifiers
|
@NotNull List<ClassifierDescriptor> classifiers
|
||||||
|
|||||||
@@ -1313,14 +1313,11 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
|||||||
for (JetDelegationSpecifier specifier : delegationSpecifiers) {
|
for (JetDelegationSpecifier specifier : delegationSpecifiers) {
|
||||||
if (specifier instanceof JetDelegatorByExpressionSpecifier) {
|
if (specifier instanceof JetDelegatorByExpressionSpecifier) {
|
||||||
JetExpression expression = ((JetDelegatorByExpressionSpecifier) specifier).getDelegateExpression();
|
JetExpression expression = ((JetDelegatorByExpressionSpecifier) specifier).getDelegateExpression();
|
||||||
PropertyDescriptor propertyDescriptor = getDelegatePropertyIfAny(expression);
|
PropertyDescriptor propertyDescriptor = CodegenUtil.getDelegatePropertyIfAny(expression, descriptor, bindingContext);
|
||||||
|
|
||||||
ClassDescriptor superClassDescriptor = getSuperClass(specifier);
|
ClassDescriptor superClassDescriptor = getSuperClass(specifier);
|
||||||
|
|
||||||
if (propertyDescriptor != null &&
|
if (CodegenUtil.isFinalPropertyWithBackingField(propertyDescriptor, bindingContext)) {
|
||||||
!propertyDescriptor.isVar() &&
|
|
||||||
Boolean.TRUE.equals(bindingContext.get(BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor))) {
|
|
||||||
// final property with backing field
|
|
||||||
result.addField((JetDelegatorByExpressionSpecifier) specifier, propertyDescriptor);
|
result.addField((JetDelegatorByExpressionSpecifier) specifier, propertyDescriptor);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -1334,12 +1331,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
|||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private ClassDescriptor getSuperClass(@NotNull JetDelegationSpecifier specifier) {
|
private ClassDescriptor getSuperClass(@NotNull JetDelegationSpecifier specifier) {
|
||||||
JetType superType = bindingContext.get(BindingContext.TYPE, specifier.getTypeReference());
|
return CodegenUtil.getSuperClassByDelegationSpecifier(specifier, bindingContext);
|
||||||
assert superType != null;
|
|
||||||
|
|
||||||
ClassDescriptor superClassDescriptor = (ClassDescriptor) superType.getConstructor().getDeclarationDescriptor();
|
|
||||||
assert superClassDescriptor != null;
|
|
||||||
return superClassDescriptor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void genCallToDelegatorByExpressionSpecifier(
|
private void genCallToDelegatorByExpressionSpecifier(
|
||||||
@@ -1361,26 +1353,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private PropertyDescriptor getDelegatePropertyIfAny(JetExpression expression) {
|
private PropertyDescriptor getDelegatePropertyIfAny(JetExpression expression) {
|
||||||
PropertyDescriptor propertyDescriptor = null;
|
return CodegenUtil.getDelegatePropertyIfAny(expression, descriptor, bindingContext);
|
||||||
if (expression instanceof JetSimpleNameExpression) {
|
|
||||||
ResolvedCall<?> call = CallUtilPackage.getResolvedCall(expression, bindingContext);
|
|
||||||
if (call != null) {
|
|
||||||
CallableDescriptor callResultingDescriptor = call.getResultingDescriptor();
|
|
||||||
if (callResultingDescriptor instanceof ValueParameterDescriptor) {
|
|
||||||
ValueParameterDescriptor valueParameterDescriptor = (ValueParameterDescriptor) callResultingDescriptor;
|
|
||||||
// constructor parameter
|
|
||||||
if (valueParameterDescriptor.getContainingDeclaration() instanceof ConstructorDescriptor) {
|
|
||||||
// constructor of my class
|
|
||||||
if (valueParameterDescriptor.getContainingDeclaration().getContainingDeclaration() == descriptor) {
|
|
||||||
propertyDescriptor = bindingContext.get(BindingContext.VALUE_PARAMETER_AS_PROPERTY, valueParameterDescriptor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo: when and if frontend will allow properties defined not as constructor parameters to be used in delegation specifier
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return propertyDescriptor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void lookupConstructorExpressionsInClosureIfPresent(final ConstructorContext constructorContext) {
|
private void lookupConstructorExpressionsInClosureIfPresent(final ConstructorContext constructorContext) {
|
||||||
@@ -1737,24 +1710,16 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void generateDelegates(ClassDescriptor toClass, DelegationFieldsInfo.Field field) {
|
protected void generateDelegates(ClassDescriptor toClass, DelegationFieldsInfo.Field field) {
|
||||||
for (DeclarationDescriptor declaration : descriptor.getDefaultType().getMemberScope().getAllDescriptors()) {
|
for (Map.Entry<CallableMemberDescriptor, CallableMemberDescriptor> entry : CodegenUtil.getDelegates(descriptor, toClass).entrySet()) {
|
||||||
if (declaration instanceof CallableMemberDescriptor) {
|
CallableMemberDescriptor callableMemberDescriptor = entry.getKey();
|
||||||
CallableMemberDescriptor callableMemberDescriptor = (CallableMemberDescriptor) declaration;
|
CallableMemberDescriptor overriddenDescriptor = entry.getValue();
|
||||||
if (callableMemberDescriptor.getKind() == CallableMemberDescriptor.Kind.DELEGATION) {
|
if (callableMemberDescriptor instanceof PropertyDescriptor) {
|
||||||
Set<? extends CallableMemberDescriptor> overriddenDescriptors = callableMemberDescriptor.getOverriddenDescriptors();
|
propertyCodegen
|
||||||
for (CallableMemberDescriptor overriddenDescriptor : overriddenDescriptors) {
|
.genDelegate((PropertyDescriptor) callableMemberDescriptor, (PropertyDescriptor) overriddenDescriptor, field.getStackValue());
|
||||||
if (overriddenDescriptor.getContainingDeclaration() == toClass) {
|
}
|
||||||
if (declaration instanceof PropertyDescriptor) {
|
else if (callableMemberDescriptor instanceof FunctionDescriptor) {
|
||||||
propertyCodegen
|
functionCodegen
|
||||||
.genDelegate((PropertyDescriptor) declaration, (PropertyDescriptor) overriddenDescriptor, field.getStackValue());
|
.genDelegate((FunctionDescriptor) callableMemberDescriptor, (FunctionDescriptor) overriddenDescriptor, field.getStackValue());
|
||||||
}
|
|
||||||
else if (declaration instanceof FunctionDescriptor) {
|
|
||||||
functionCodegen
|
|
||||||
.genDelegate((FunctionDescriptor) declaration, (FunctionDescriptor) overriddenDescriptor, field.getStackValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2014 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.k2js.test.semantics;
|
||||||
|
|
||||||
|
import org.jetbrains.k2js.test.SingleFileTranslationTest;
|
||||||
|
|
||||||
|
public class DelegationTest extends SingleFileTranslationTest {
|
||||||
|
|
||||||
|
public DelegationTest() {
|
||||||
|
super("delegation/");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDelegation2() throws Exception {
|
||||||
|
checkFooBoxIsOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDelegation3() throws Exception {
|
||||||
|
checkFooBoxIsOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDelegation4() throws Exception {
|
||||||
|
checkFooBoxIsOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDelegationGenericArg() throws Exception {
|
||||||
|
checkFooBoxIsOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDelegationMethodsWithArgs() throws Exception {
|
||||||
|
checkFooBoxIsOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDelegationByInh() throws Exception {
|
||||||
|
checkFooBoxIsOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDelegationChain() throws Exception {
|
||||||
|
checkFooBoxIsOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDelegationByExprWithArgs() throws Exception {
|
||||||
|
checkFooBoxIsOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDelegationByArg() throws Exception {
|
||||||
|
checkFooBoxIsOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDelegationByNewInstance() throws Exception {
|
||||||
|
checkFooBoxIsOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDelegationByFunExpr() throws Exception {
|
||||||
|
checkFooBoxIsOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDelegationByIfExpr() throws Exception {
|
||||||
|
checkFooBoxIsOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDelegationEvaluationOrder1() throws Exception {
|
||||||
|
checkFooBoxIsOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDelegationEvaluationOrder2() throws Exception {
|
||||||
|
checkFooBoxIsOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDelegationExtFun1() throws Exception {
|
||||||
|
checkFooBoxIsOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDelegationExtFun2() throws Exception {
|
||||||
|
checkFooBoxIsOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDelegationExtProp() throws Exception {
|
||||||
|
checkFooBoxIsOk();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -53,7 +53,7 @@ public final class Namer {
|
|||||||
private static final String SETTER_PREFIX = "set_";
|
private static final String SETTER_PREFIX = "set_";
|
||||||
private static final String GETTER_PREFIX = "get_";
|
private static final String GETTER_PREFIX = "get_";
|
||||||
private static final String BACKING_FIELD_PREFIX = "$";
|
private static final String BACKING_FIELD_PREFIX = "$";
|
||||||
private static final String DELEGATE_POSTFIX = "$delegate";
|
private static final String DELEGATE = "$delegate";
|
||||||
|
|
||||||
private static final String SUPER_METHOD_NAME = "baseInitializer";
|
private static final String SUPER_METHOD_NAME = "baseInitializer";
|
||||||
|
|
||||||
@@ -131,9 +131,14 @@ public final class Namer {
|
|||||||
return new JsNameRef(getPrototypeName(), classOrTraitExpression);
|
return new JsNameRef(getPrototypeName(), classOrTraitExpression);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static String getDelegatePrefix() {
|
||||||
|
return DELEGATE;
|
||||||
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static String getDelegateName(@NotNull String propertyName) {
|
public static String getDelegateName(@NotNull String propertyName) {
|
||||||
return propertyName + DELEGATE_POSTFIX;
|
return propertyName + DELEGATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
|
|||||||
@@ -125,14 +125,16 @@ public final class ClassTranslator extends AbstractTranslator {
|
|||||||
declarationContext = fixContextForClassObjectAccessing(declarationContext);
|
declarationContext = fixContextForClassObjectAccessing(declarationContext);
|
||||||
|
|
||||||
invocationArguments.add(getSuperclassReferences(declarationContext));
|
invocationArguments.add(getSuperclassReferences(declarationContext));
|
||||||
|
DelegationTranslator delegationTranslator = new DelegationTranslator(classDeclaration, context());
|
||||||
if (!isTrait()) {
|
if (!isTrait()) {
|
||||||
JsFunction initializer = new ClassInitializerTranslator(classDeclaration, declarationContext).generateInitializeMethod();
|
JsFunction initializer = new ClassInitializerTranslator(classDeclaration, declarationContext).generateInitializeMethod(delegationTranslator);
|
||||||
invocationArguments.add(initializer.getBody().getStatements().isEmpty() ? JsLiteral.NULL : initializer);
|
invocationArguments.add(initializer.getBody().getStatements().isEmpty() ? JsLiteral.NULL : initializer);
|
||||||
}
|
}
|
||||||
|
|
||||||
translatePropertiesAsConstructorParameters(declarationContext, properties);
|
translatePropertiesAsConstructorParameters(declarationContext, properties);
|
||||||
DeclarationBodyVisitor bodyVisitor = new DeclarationBodyVisitor(properties, staticProperties);
|
DeclarationBodyVisitor bodyVisitor = new DeclarationBodyVisitor(properties, staticProperties);
|
||||||
bodyVisitor.traverseContainer(classDeclaration, declarationContext);
|
bodyVisitor.traverseContainer(classDeclaration, declarationContext);
|
||||||
|
delegationTranslator.generateDelegated(properties);
|
||||||
mayBeAddEnumEntry(bodyVisitor.getEnumEntryList(), staticProperties, declarationContext);
|
mayBeAddEnumEntry(bodyVisitor.getEnumEntryList(), staticProperties, declarationContext);
|
||||||
|
|
||||||
if (KotlinBuiltIns.getInstance().isData(descriptor)) {
|
if (KotlinBuiltIns.getInstance().isData(descriptor)) {
|
||||||
|
|||||||
+210
@@ -0,0 +1,210 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2014 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.k2js.translate.declaration
|
||||||
|
|
||||||
|
|
||||||
|
import com.google.dart.compiler.backend.js.ast.*
|
||||||
|
import com.intellij.util.SmartList
|
||||||
|
import org.jetbrains.jet.lang.descriptors.*
|
||||||
|
import org.jetbrains.jet.lang.psi.*
|
||||||
|
import org.jetbrains.k2js.translate.context.Namer
|
||||||
|
import org.jetbrains.k2js.translate.context.TranslationContext
|
||||||
|
import org.jetbrains.k2js.translate.general.AbstractTranslator
|
||||||
|
import org.jetbrains.k2js.translate.general.Translation
|
||||||
|
import org.jetbrains.k2js.translate.utils.BindingUtils
|
||||||
|
import org.jetbrains.k2js.translate.utils.JsAstUtils
|
||||||
|
import org.jetbrains.k2js.translate.utils.JsDescriptorUtils
|
||||||
|
import org.jetbrains.k2js.translate.utils.TranslationUtils.*
|
||||||
|
|
||||||
|
import org.jetbrains.jet.lang.resolve.DescriptorUtils
|
||||||
|
import org.jetbrains.jet.backend.common.CodegenUtil
|
||||||
|
import java.util.HashMap
|
||||||
|
import com.google.dart.compiler.backend.js.ast.JsLiteral
|
||||||
|
import org.jetbrains.k2js.translate.declaration.propertyTranslator.addGetterAndSetter
|
||||||
|
|
||||||
|
public class DelegationTranslator(
|
||||||
|
private val classDeclaration: JetClassOrObject,
|
||||||
|
context: TranslationContext
|
||||||
|
) : AbstractTranslator(context) {
|
||||||
|
|
||||||
|
private val classDescriptor: ClassDescriptor =
|
||||||
|
BindingUtils.getClassDescriptor(context.bindingContext(), classDeclaration);
|
||||||
|
|
||||||
|
private val delegationBySpecifiers =
|
||||||
|
classDeclaration.getDelegationSpecifiers().filterIsInstance(javaClass<JetDelegatorByExpressionSpecifier>());
|
||||||
|
|
||||||
|
private class Field (val name: String, val generateField: Boolean)
|
||||||
|
private val fields = HashMap<JetDelegatorByExpressionSpecifier, Field>();
|
||||||
|
|
||||||
|
{
|
||||||
|
for (specifier in delegationBySpecifiers) {
|
||||||
|
val expression = specifier.getDelegateExpression() ?:
|
||||||
|
throw IllegalArgumentException("delegate expression should not be null: ${specifier.getText()}")
|
||||||
|
val descriptor = getSuperClass(specifier)
|
||||||
|
val propertyDescriptor = CodegenUtil.getDelegatePropertyIfAny(expression, classDescriptor, bindingContext())
|
||||||
|
|
||||||
|
if (CodegenUtil.isFinalPropertyWithBackingField(propertyDescriptor, bindingContext())) {
|
||||||
|
fields.put(specifier, Field(propertyDescriptor!!.getName().asString(), false))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val typeFqName = DescriptorUtils.getFqNameSafe(descriptor)
|
||||||
|
val delegateName = getMangledMemberNameForExplicitDelegation(Namer.getDelegatePrefix(), classDeclaration.getFqName(), typeFqName)
|
||||||
|
fields.put(specifier, Field(delegateName, true))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun addInitCode(statements: MutableList<JsStatement>) {
|
||||||
|
for (specifier in delegationBySpecifiers) {
|
||||||
|
val field = fields.get(specifier)!!
|
||||||
|
if (field.generateField) {
|
||||||
|
val expression = specifier.getDelegateExpression()!!
|
||||||
|
val delegateInitExpr = Translation.translateAsExpression(expression, context())
|
||||||
|
statements.add(JsAstUtils.defineSimpleProperty(field.name, delegateInitExpr))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun generateDelegated(properties: MutableList<JsPropertyInitializer>) {
|
||||||
|
for (specifier in delegationBySpecifiers) {
|
||||||
|
generateDelegates(getSuperClass(specifier), fields.get(specifier)!!, properties)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getSuperClass(specifier: JetDelegationSpecifier): ClassDescriptor =
|
||||||
|
CodegenUtil.getSuperClassByDelegationSpecifier(specifier, bindingContext())
|
||||||
|
|
||||||
|
private fun generateDelegates(toClass: ClassDescriptor, field: Field, properties: MutableList<JsPropertyInitializer>) {
|
||||||
|
for ((descriptor, overriddenDescriptor) in CodegenUtil.getDelegates(classDescriptor, toClass)) {
|
||||||
|
when (descriptor) {
|
||||||
|
is PropertyDescriptor ->
|
||||||
|
generateDelegateCallForPropertyMember(descriptor, field.name, properties)
|
||||||
|
is FunctionDescriptor ->
|
||||||
|
generateDelegateCallForFunctionMember(descriptor, overriddenDescriptor as FunctionDescriptor, field.name, properties)
|
||||||
|
else ->
|
||||||
|
throw IllegalArgumentException("Expected property or function ${descriptor}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateDelegateCallForPropertyMember(
|
||||||
|
descriptor: PropertyDescriptor,
|
||||||
|
delegateName: String,
|
||||||
|
properties: MutableList<JsPropertyInitializer>
|
||||||
|
) {
|
||||||
|
val propertyName: String = descriptor.getName().asString()
|
||||||
|
|
||||||
|
fun generateDelegateGetterFunction(getterDescriptor: PropertyGetterDescriptor): JsFunction {
|
||||||
|
val delegateRefName = context().getScopeForDescriptor(getterDescriptor).declareName(delegateName)
|
||||||
|
val delegateRef = JsNameRef(delegateRefName, JsLiteral.THIS)
|
||||||
|
|
||||||
|
val returnExpression = if (JsDescriptorUtils.isExtension(descriptor)) {
|
||||||
|
val getterName = context().getNameForDescriptor(getterDescriptor)
|
||||||
|
val receiver = Namer.getReceiverParameterName()
|
||||||
|
JsInvocation(JsNameRef(getterName, delegateRef), JsNameRef(receiver))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
JsNameRef(propertyName, delegateRef): JsExpression // TODO remove explicit type specification after resolving KT-5569
|
||||||
|
}
|
||||||
|
|
||||||
|
val jsFunction = simpleReturnFunction(context().getScopeForDescriptor(getterDescriptor.getContainingDeclaration()), returnExpression)
|
||||||
|
if (JsDescriptorUtils.isExtension(descriptor)) {
|
||||||
|
val receiverName = jsFunction.getScope().declareName(Namer.getReceiverParameterName())
|
||||||
|
jsFunction.getParameters().add(JsParameter(receiverName))
|
||||||
|
}
|
||||||
|
return jsFunction
|
||||||
|
}
|
||||||
|
|
||||||
|
fun generateDelegateSetterFunction(setterDescriptor: PropertySetterDescriptor): JsFunction {
|
||||||
|
val jsFunction = JsFunction(context().getScopeForDescriptor(setterDescriptor.getContainingDeclaration()))
|
||||||
|
|
||||||
|
assert(setterDescriptor.getValueParameters().size() == 1, "Setter must have 1 parameter")
|
||||||
|
val defaultParameter = JsParameter(jsFunction.getScope().declareTemporary())
|
||||||
|
val defaultParameterRef = defaultParameter.getName().makeRef()
|
||||||
|
|
||||||
|
val delegateRefName = context().getScopeForDescriptor(setterDescriptor).declareName(delegateName)
|
||||||
|
val delegateRef = JsNameRef(delegateRefName, JsLiteral.THIS)
|
||||||
|
|
||||||
|
val setExpression = if (JsDescriptorUtils.isExtension(descriptor)) {
|
||||||
|
val setterName = context().getNameForDescriptor(setterDescriptor)
|
||||||
|
val setterNameRef = JsNameRef(setterName, delegateRef)
|
||||||
|
val extensionFunctionReceiverName = jsFunction.getScope().declareName(Namer.getReceiverParameterName())
|
||||||
|
jsFunction.getParameters().add(JsParameter(extensionFunctionReceiverName))
|
||||||
|
JsInvocation(setterNameRef, JsNameRef(extensionFunctionReceiverName), defaultParameterRef)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val propertyNameRef = JsNameRef(propertyName, delegateRef)
|
||||||
|
JsAstUtils.assignment(propertyNameRef, defaultParameterRef)
|
||||||
|
}
|
||||||
|
|
||||||
|
jsFunction.getParameters().add(defaultParameter)
|
||||||
|
jsFunction.setBody(JsBlock(setExpression.makeStmt()))
|
||||||
|
return jsFunction
|
||||||
|
}
|
||||||
|
|
||||||
|
fun generateDelegateAccessor(accessorDescriptor: PropertyAccessorDescriptor, function: JsFunction): JsPropertyInitializer =
|
||||||
|
translateFunctionAsEcma5PropertyDescriptor(function, accessorDescriptor, context())
|
||||||
|
|
||||||
|
fun generateDelegateGetter(): JsPropertyInitializer {
|
||||||
|
val getterDescriptor = descriptor.getGetter() ?: throw IllegalStateException("Getter descriptor should not be null")
|
||||||
|
return generateDelegateAccessor(getterDescriptor, generateDelegateGetterFunction(getterDescriptor))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun generateDelegateSetter(): JsPropertyInitializer {
|
||||||
|
val setterDescriptor = descriptor.getSetter() ?: throw IllegalStateException("Setter descriptor should not be null")
|
||||||
|
return generateDelegateAccessor(setterDescriptor, generateDelegateSetterFunction(setterDescriptor))
|
||||||
|
}
|
||||||
|
|
||||||
|
properties.addGetterAndSetter(descriptor, context(), ::generateDelegateGetter, ::generateDelegateSetter
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun generateDelegateCallForFunctionMember(
|
||||||
|
descriptor: FunctionDescriptor,
|
||||||
|
overriddenDescriptor: FunctionDescriptor,
|
||||||
|
delegateName: String,
|
||||||
|
properties: MutableList<JsPropertyInitializer>
|
||||||
|
) {
|
||||||
|
val delegateMemberFunctionName = context().getNameForDescriptor(descriptor)
|
||||||
|
val overriddenMemberFunctionName = context().getNameForDescriptor(overriddenDescriptor)
|
||||||
|
val delegateRefName = context().getScopeForDescriptor(descriptor).declareName(delegateName)
|
||||||
|
val delegateRef = JsNameRef(delegateRefName, JsLiteral.THIS)
|
||||||
|
val overriddenMemberFunctionRef = JsNameRef(overriddenMemberFunctionName, delegateRef)
|
||||||
|
|
||||||
|
val parameters = SmartList<JsParameter>()
|
||||||
|
val args = SmartList<JsExpression>()
|
||||||
|
val functionScope = context().getScopeForDescriptor(descriptor);
|
||||||
|
|
||||||
|
if (JsDescriptorUtils.isExtension(descriptor)) {
|
||||||
|
val extensionFunctionReceiverName = functionScope.declareName(Namer.getReceiverParameterName())
|
||||||
|
parameters.add(JsParameter(extensionFunctionReceiverName))
|
||||||
|
args.add(JsNameRef(extensionFunctionReceiverName))
|
||||||
|
}
|
||||||
|
|
||||||
|
for (param in descriptor.getValueParameters()) {
|
||||||
|
val paramName = param.getName().asString()
|
||||||
|
val jsParamName = functionScope.declareName(paramName)
|
||||||
|
parameters.add(JsParameter(jsParamName))
|
||||||
|
args.add(JsNameRef(jsParamName))
|
||||||
|
}
|
||||||
|
|
||||||
|
val functionObject = simpleReturnFunction(context().getScopeForDescriptor(descriptor), JsInvocation(overriddenMemberFunctionRef, args))
|
||||||
|
functionObject.getParameters().addAll(parameters)
|
||||||
|
properties.add(JsPropertyInitializer(delegateMemberFunctionName.makeRef(), functionObject))
|
||||||
|
}
|
||||||
|
}
|
||||||
+24
-15
@@ -54,6 +54,27 @@ public fun translateAccessors(
|
|||||||
translateAccessors(descriptor, null, result, context)
|
translateAccessors(descriptor, null, result, context)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public fun MutableList<JsPropertyInitializer>.addGetterAndSetter(
|
||||||
|
descriptor: PropertyDescriptor,
|
||||||
|
context: TranslationContext,
|
||||||
|
generateGetter: () -> JsPropertyInitializer,
|
||||||
|
generateSetter: () -> JsPropertyInitializer
|
||||||
|
) {
|
||||||
|
val to: MutableList<JsPropertyInitializer>
|
||||||
|
if (!JsDescriptorUtils.isExtension(descriptor)) {
|
||||||
|
to = SmartList<JsPropertyInitializer>()
|
||||||
|
this.add(JsPropertyInitializer(context.getNameForDescriptor(descriptor).makeRef(), JsObjectLiteral(to, true)))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
to = this
|
||||||
|
}
|
||||||
|
|
||||||
|
to.add(generateGetter())
|
||||||
|
if (descriptor.isVar()) {
|
||||||
|
to.add(generateSetter())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class PropertyTranslator(
|
private class PropertyTranslator(
|
||||||
val descriptor: PropertyDescriptor,
|
val descriptor: PropertyDescriptor,
|
||||||
val declaration: JetProperty?,
|
val declaration: JetProperty?,
|
||||||
@@ -63,19 +84,7 @@ private class PropertyTranslator(
|
|||||||
private val propertyName: String = descriptor.getName().asString()
|
private val propertyName: String = descriptor.getName().asString()
|
||||||
|
|
||||||
fun translate(result: MutableList<JsPropertyInitializer>) {
|
fun translate(result: MutableList<JsPropertyInitializer>) {
|
||||||
val to: MutableList<JsPropertyInitializer>
|
result.addGetterAndSetter(descriptor, context(), { generateGetter() }, { generateSetter() })
|
||||||
if (!JsDescriptorUtils.isExtension(descriptor)) {
|
|
||||||
to = SmartList<JsPropertyInitializer>()
|
|
||||||
result.add(JsPropertyInitializer(context().getNameForDescriptor(descriptor).makeRef(), JsObjectLiteral(to, true)))
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
to = result
|
|
||||||
}
|
|
||||||
|
|
||||||
to.add(generateGetter())
|
|
||||||
if (descriptor.isVar()) {
|
|
||||||
to.add(generateSetter())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun generateGetter(): JsPropertyInitializer =
|
private fun generateGetter(): JsPropertyInitializer =
|
||||||
@@ -84,9 +93,9 @@ private class PropertyTranslator(
|
|||||||
private fun generateSetter(): JsPropertyInitializer =
|
private fun generateSetter(): JsPropertyInitializer =
|
||||||
if (hasCustomSetter()) translateCustomAccessor(getCustomSetterDeclaration()) else generateDefaultSetter()
|
if (hasCustomSetter()) translateCustomAccessor(getCustomSetterDeclaration()) else generateDefaultSetter()
|
||||||
|
|
||||||
private fun hasCustomGetter() = declaration != null && declaration.getGetter() != null && getCustomGetterDeclaration().hasBody()
|
private fun hasCustomGetter() = declaration?.getGetter() != null && getCustomGetterDeclaration().hasBody()
|
||||||
|
|
||||||
private fun hasCustomSetter() = declaration != null && declaration.getSetter() != null && getCustomSetterDeclaration().hasBody()
|
private fun hasCustomSetter() = declaration?.getSetter() != null && getCustomSetterDeclaration().hasBody()
|
||||||
|
|
||||||
private fun getCustomGetterDeclaration(): JetPropertyAccessor =
|
private fun getCustomGetterDeclaration(): JetPropertyAccessor =
|
||||||
declaration?.getGetter() ?:
|
declaration?.getGetter() ?:
|
||||||
|
|||||||
+3
-1
@@ -33,6 +33,7 @@ import org.jetbrains.jet.lang.types.JetType;
|
|||||||
import org.jetbrains.jet.lexer.JetTokens;
|
import org.jetbrains.jet.lexer.JetTokens;
|
||||||
import org.jetbrains.k2js.translate.context.Namer;
|
import org.jetbrains.k2js.translate.context.Namer;
|
||||||
import org.jetbrains.k2js.translate.context.TranslationContext;
|
import org.jetbrains.k2js.translate.context.TranslationContext;
|
||||||
|
import org.jetbrains.k2js.translate.declaration.DelegationTranslator;
|
||||||
import org.jetbrains.k2js.translate.general.AbstractTranslator;
|
import org.jetbrains.k2js.translate.general.AbstractTranslator;
|
||||||
import org.jetbrains.k2js.translate.reference.CallArgumentTranslator;
|
import org.jetbrains.k2js.translate.reference.CallArgumentTranslator;
|
||||||
|
|
||||||
@@ -62,7 +63,7 @@ public final class ClassInitializerTranslator extends AbstractTranslator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public JsFunction generateInitializeMethod() {
|
public JsFunction generateInitializeMethod(DelegationTranslator delegationTranslator) {
|
||||||
//TODO: it's inconsistent that we have scope for class and function for constructor, currently have problems implementing better way
|
//TODO: it's inconsistent that we have scope for class and function for constructor, currently have problems implementing better way
|
||||||
ConstructorDescriptor primaryConstructor = getConstructor(bindingContext(), classDeclaration);
|
ConstructorDescriptor primaryConstructor = getConstructor(bindingContext(), classDeclaration);
|
||||||
JsFunction result = context().getFunctionObject(primaryConstructor);
|
JsFunction result = context().getFunctionObject(primaryConstructor);
|
||||||
@@ -70,6 +71,7 @@ public final class ClassInitializerTranslator extends AbstractTranslator {
|
|||||||
// for properties declared as constructor parameters
|
// for properties declared as constructor parameters
|
||||||
result.getParameters().addAll(translatePrimaryConstructorParameters());
|
result.getParameters().addAll(translatePrimaryConstructorParameters());
|
||||||
mayBeAddCallToSuperMethod(result);
|
mayBeAddCallToSuperMethod(result);
|
||||||
|
delegationTranslator.addInitCode(initializerStatements);
|
||||||
new InitializerVisitor(initializerStatements).traverseContainer(classDeclaration, context());
|
new InitializerVisitor(initializerStatements).traverseContainer(classDeclaration, context());
|
||||||
|
|
||||||
List<JsStatement> statements = result.getBody().getStatements();
|
List<JsStatement> statements = result.getBody().getStatements();
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import org.jetbrains.jet.lang.descriptors.*;
|
|||||||
import org.jetbrains.jet.lang.descriptors.impl.AnonymousFunctionDescriptor;
|
import org.jetbrains.jet.lang.descriptors.impl.AnonymousFunctionDescriptor;
|
||||||
import org.jetbrains.jet.lang.psi.*;
|
import org.jetbrains.jet.lang.psi.*;
|
||||||
import org.jetbrains.jet.lang.resolve.OverrideResolver;
|
import org.jetbrains.jet.lang.resolve.OverrideResolver;
|
||||||
|
import org.jetbrains.jet.lang.resolve.name.FqName;
|
||||||
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
|
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
|
||||||
import org.jetbrains.jet.lang.types.JetType;
|
import org.jetbrains.jet.lang.types.JetType;
|
||||||
import org.jetbrains.k2js.translate.context.TemporaryConstVariable;
|
import org.jetbrains.k2js.translate.context.TemporaryConstVariable;
|
||||||
@@ -202,6 +203,12 @@ public final class TranslationUtils {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static String getMangledMemberNameForExplicitDelegation(@NotNull String suggestedName, FqName classFqName, FqName typeFqName) {
|
||||||
|
String forCalculateId = classFqName.asString() + ":" + typeFqName.asString();
|
||||||
|
return getStableMangledName(suggestedName, forCalculateId);
|
||||||
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private static String getStableMangledName(@NotNull String suggestedName, String forCalculateId) {
|
private static String getStableMangledName(@NotNull String suggestedName, String forCalculateId) {
|
||||||
int absHashCode = Math.abs(forCalculateId.hashCode());
|
int absHashCode = Math.abs(forCalculateId.hashCode());
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
// This test was adapted from compiler/testData/codegen/box/classes
|
||||||
|
package foo
|
||||||
|
|
||||||
|
trait Trait1 {
|
||||||
|
fun foo(): String
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Trait2 {
|
||||||
|
fun bar(): String
|
||||||
|
}
|
||||||
|
|
||||||
|
class T1 : Trait1 {
|
||||||
|
override fun foo() = "aaa"
|
||||||
|
}
|
||||||
|
|
||||||
|
class T2 : Trait2 {
|
||||||
|
override fun bar() = "bbb"
|
||||||
|
}
|
||||||
|
|
||||||
|
class C(a: Trait1, b: Trait2) : Trait1 by a, Trait2 by b
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val c = C(T1(), T2())
|
||||||
|
if (c.foo() != "aaa") return "fail"
|
||||||
|
if (c.bar() != "bbb") return "fail"
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
// This test was adapted from compiler/testData/codegen/box/classes
|
||||||
|
package foo
|
||||||
|
|
||||||
|
trait One {
|
||||||
|
public open fun foo(): Int
|
||||||
|
public open fun faz(): Int = 10
|
||||||
|
}
|
||||||
|
trait Two {
|
||||||
|
public open fun foo(): Int
|
||||||
|
public open fun quux(): Int = 100
|
||||||
|
}
|
||||||
|
|
||||||
|
class OneImpl : One {
|
||||||
|
public override fun foo() = 1
|
||||||
|
}
|
||||||
|
class TwoImpl : Two {
|
||||||
|
public override fun foo() = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
class Test2(a: One, b: Two) : Two by b, One by a {
|
||||||
|
public override fun foo() = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
var t2 = Test2(OneImpl(), TwoImpl())
|
||||||
|
if (t2.foo() != 0)
|
||||||
|
return "Fail #1"
|
||||||
|
if (t2.faz() != 10)
|
||||||
|
return "Fail #2"
|
||||||
|
if (t2.quux() != 100)
|
||||||
|
return "Fail #3"
|
||||||
|
if (t2 !is One)
|
||||||
|
return "Fail #4"
|
||||||
|
if (t2 !is Two)
|
||||||
|
return "Fail #5"
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
// This test was adapted from compiler/testData/codegen/box/classes
|
||||||
|
package foo
|
||||||
|
|
||||||
|
open trait First {
|
||||||
|
public open fun foo(): Int
|
||||||
|
}
|
||||||
|
|
||||||
|
open trait Second : First {
|
||||||
|
public open fun bar(): Int
|
||||||
|
}
|
||||||
|
|
||||||
|
class Impl : Second {
|
||||||
|
public override fun foo() = 1
|
||||||
|
public override fun bar() = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
class Test(s: Second) : Second by s {}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
var t = Test(Impl())
|
||||||
|
if (t.foo() != 1)
|
||||||
|
return "Fail #1"
|
||||||
|
if (t.bar() != 2)
|
||||||
|
return "Fail #2"
|
||||||
|
if (t !is First)
|
||||||
|
return "Fail #3"
|
||||||
|
if (t !is Second)
|
||||||
|
return "Fail #4"
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
trait Base {
|
||||||
|
abstract fun foo(x: String): String
|
||||||
|
var prop: String
|
||||||
|
}
|
||||||
|
|
||||||
|
class BaseImpl(val s: String) : Base {
|
||||||
|
override fun foo(x: String): String = "Base: ${s}:${x}"
|
||||||
|
override var prop: String = "prop"
|
||||||
|
}
|
||||||
|
|
||||||
|
class Derived(b: Base) : Base by b
|
||||||
|
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
var d = Derived(BaseImpl("test"))
|
||||||
|
assertEquals("Base: test:!!", d.foo("!!"), "delegation by argument, function member")
|
||||||
|
assertEquals("prop", d.prop, "delegation by argument, get property")
|
||||||
|
|
||||||
|
d.prop = "new value"
|
||||||
|
assertEquals("new value", d.prop, "delegation by argument, set property")
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
trait Base {
|
||||||
|
abstract fun foo(arg: String): String
|
||||||
|
}
|
||||||
|
|
||||||
|
class BaseImpl(val s1: String, val s2: String) : Base {
|
||||||
|
override fun foo(arg: String): String = "BaseImpl:foo ${s1}:${s2}:${arg}"
|
||||||
|
}
|
||||||
|
|
||||||
|
class Derived(s1: String, s2: String) : Base by BaseImpl(s1, s2)
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
assertEquals("BaseImpl:foo arg1:arg2:!!", Derived("arg1", "arg2").foo("!!"), "delegation with two arguments")
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
trait Base {
|
||||||
|
abstract fun foo(x: String): String
|
||||||
|
}
|
||||||
|
|
||||||
|
class BaseImpl(val s: String) : Base {
|
||||||
|
override fun foo(x: String): String = "Base: ${s}:${x}"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun newBase(s: String): Base = BaseImpl(s)
|
||||||
|
|
||||||
|
class Derived() : Base by newBase("test")
|
||||||
|
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
assertEquals("Base: test:!!", Derived().foo("!!"), "delegation by function expression")
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
trait Base {
|
||||||
|
abstract fun foo(x: String): String
|
||||||
|
}
|
||||||
|
|
||||||
|
class BaseImpl(val s: String) : Base {
|
||||||
|
override fun foo(x: String): String = "Base: ${s}:${x}"
|
||||||
|
}
|
||||||
|
|
||||||
|
var global = true
|
||||||
|
|
||||||
|
class Derived() : Base by if (global) BaseImpl("then") else BaseImpl("else")
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
assertEquals("Base: then:!!", Derived().foo("!!"), "delegation by if expression")
|
||||||
|
global = false
|
||||||
|
assertEquals("Base: else:!!", Derived().foo("!!"), "delegation by if expression")
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
trait Base {
|
||||||
|
abstract fun foo(s: String): String
|
||||||
|
var prop: String
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Base1 : Base {
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Base2 : Base1 {
|
||||||
|
override fun foo(s: String): String = "Base2:foo ${s}"
|
||||||
|
}
|
||||||
|
|
||||||
|
class Base2Impl() : Base2 {
|
||||||
|
override var prop: String = ""
|
||||||
|
set(value) {
|
||||||
|
$prop = "prop:${value}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Derived() : Base by Base2Impl()
|
||||||
|
|
||||||
|
class Derived1() : Base1 by Base2Impl()
|
||||||
|
|
||||||
|
class Derived2() : Base2 by Base2Impl()
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
assertEquals("Base2:foo !!", Derived().foo("!!"), "delegation (Base)")
|
||||||
|
assertEquals("Base2:foo !!", Derived1().foo("!!"), "delegation (Base1)")
|
||||||
|
assertEquals("Base2:foo !!", Derived2().foo("!!"), "delegation (Base2)")
|
||||||
|
|
||||||
|
var d = Derived()
|
||||||
|
d.prop = "A"
|
||||||
|
assertEquals("prop:A", d.prop, "delegation (Base) set property")
|
||||||
|
|
||||||
|
var d1 = Derived1()
|
||||||
|
d1.prop = "B"
|
||||||
|
assertEquals("prop:B", d1.prop, "delegation (Base1) set property")
|
||||||
|
|
||||||
|
var d2 = Derived1()
|
||||||
|
d2.prop = "C"
|
||||||
|
assertEquals("prop:C", d2.prop, "delegation (Base2) set property")
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
trait Base {
|
||||||
|
abstract fun foo(x: String): String
|
||||||
|
}
|
||||||
|
|
||||||
|
class BaseImpl(val s: String) : Base {
|
||||||
|
override fun foo(x: String): String = "Base: ${s}:${x}"
|
||||||
|
}
|
||||||
|
|
||||||
|
class Derived() : Base by BaseImpl("test")
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
assertEquals("Base: test:!!", Derived().foo("!!"), "delegation by new instance")
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
trait Base {
|
||||||
|
abstract fun foo(x: String): String
|
||||||
|
var prop: String
|
||||||
|
}
|
||||||
|
|
||||||
|
class BaseImpl(val s: String) : Base {
|
||||||
|
override fun foo(x: String) = "BaseImpl.foo: ${s}:${x}"
|
||||||
|
override var prop: String = "init"
|
||||||
|
set(value) {
|
||||||
|
$prop = "prop:${value}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Base2Impl(val s: String) : Base by BaseImpl("${s} by BaseImpl")
|
||||||
|
|
||||||
|
class Derived(val s: String) : Base by Base2Impl("${s} by Base2Impl")
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
assertEquals("BaseImpl.foo: Derived by Base2Impl by BaseImpl:!!", Derived("Derived").foo("!!"))
|
||||||
|
|
||||||
|
var d = Derived("Derived")
|
||||||
|
assertEquals("init", d.prop)
|
||||||
|
|
||||||
|
d.prop = "A"
|
||||||
|
assertEquals("prop:A", d.prop)
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
trait Base {
|
||||||
|
abstract fun foo(x: String): String
|
||||||
|
}
|
||||||
|
|
||||||
|
class BaseImpl(val s: String) : Base {
|
||||||
|
override fun foo(x: String): String = "Base: ${s}:${x}"
|
||||||
|
}
|
||||||
|
|
||||||
|
var global = ""
|
||||||
|
|
||||||
|
open class DerivedBase() {
|
||||||
|
{
|
||||||
|
global += ":DerivedBase"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun newBase(): Base {
|
||||||
|
global += ":newBase"
|
||||||
|
return BaseImpl("test")
|
||||||
|
}
|
||||||
|
|
||||||
|
class Derived() : DerivedBase(), Base by newBase() {
|
||||||
|
{
|
||||||
|
global += ":Derived"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Derived1() : Base by newBase(), DerivedBase() {
|
||||||
|
{
|
||||||
|
global += ":Derived"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
var d = Derived()
|
||||||
|
assertEquals(":DerivedBase:newBase:Derived", global, "evaluation order")
|
||||||
|
|
||||||
|
global = ""
|
||||||
|
var d1 = Derived1()
|
||||||
|
assertEquals(":DerivedBase:newBase:Derived", global, "evaluation order")
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
trait Base {
|
||||||
|
abstract fun foo(x: String): String
|
||||||
|
}
|
||||||
|
|
||||||
|
class BaseImpl(val s: String) : Base {
|
||||||
|
override fun foo(x: String): String = "Base: ${s}:${x}"
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Base2 {
|
||||||
|
abstract fun bar(x: String): String
|
||||||
|
}
|
||||||
|
|
||||||
|
class Base2Impl(val s: String) : Base2 {
|
||||||
|
override fun bar(x: String): String = "Base2: ${s}:${x}"
|
||||||
|
}
|
||||||
|
|
||||||
|
var global = ""
|
||||||
|
|
||||||
|
open class DerivedBase() {
|
||||||
|
{
|
||||||
|
global += ":DerivedBase"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun newBase(): Base {
|
||||||
|
global += ":newBase"
|
||||||
|
return BaseImpl("test")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun newBase2(): Base2 {
|
||||||
|
global += ":newBase2"
|
||||||
|
return Base2Impl("test")
|
||||||
|
}
|
||||||
|
|
||||||
|
class Derived() : DerivedBase(), Base by newBase(), Base2 by newBase2() {
|
||||||
|
{
|
||||||
|
global += ":Derived"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Derived1() : Base by newBase(), DerivedBase(), Base2 by newBase2() {
|
||||||
|
{
|
||||||
|
global += ":Derived"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Derived2() : Base by newBase(), Base2 by newBase2(), DerivedBase() {
|
||||||
|
{
|
||||||
|
global += ":Derived"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
var d = Derived()
|
||||||
|
assertEquals(":DerivedBase:newBase:newBase2:Derived", global, "evaluation order 1")
|
||||||
|
|
||||||
|
global = ""
|
||||||
|
var d1 = Derived1()
|
||||||
|
assertEquals(":DerivedBase:newBase:newBase2:Derived", global, "evaluation order 2")
|
||||||
|
|
||||||
|
global = ""
|
||||||
|
var d2 = Derived2()
|
||||||
|
assertEquals(":DerivedBase:newBase:newBase2:Derived", global, "evaluation order 3")
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
trait Base {
|
||||||
|
abstract fun Int.foo(): String
|
||||||
|
}
|
||||||
|
|
||||||
|
open class BaseImpl(val s: String) : Base {
|
||||||
|
override fun Int.foo(): String = "Int.foo ${s}:${this}"
|
||||||
|
}
|
||||||
|
|
||||||
|
class Derived() : Base by BaseImpl("test") {
|
||||||
|
fun bar(x: Int): String = x.foo()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
assertEquals("Int.foo test:5", Derived().bar(5))
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
trait Base {
|
||||||
|
abstract fun String.foo(arg: String): String
|
||||||
|
}
|
||||||
|
|
||||||
|
open class BaseImpl(val s: String) : Base {
|
||||||
|
override fun String.foo(arg: String): String = "Int.foo ${s}:${this}:${arg}"
|
||||||
|
}
|
||||||
|
|
||||||
|
class Derived() : Base by BaseImpl("test") {
|
||||||
|
fun bar(x: String, arg: String): String = x.foo(arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
assertEquals("Int.foo test:A:B", Derived().bar("A", "B"))
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
trait Base {
|
||||||
|
var prop: String
|
||||||
|
var Int.foo: String
|
||||||
|
}
|
||||||
|
|
||||||
|
open class BaseImpl(val s: String) : Base {
|
||||||
|
override var prop: String = "init"
|
||||||
|
override var Int.foo: String
|
||||||
|
get() = "get Int.foo:${s}:${this}"
|
||||||
|
set(value) {
|
||||||
|
prop = "set Int.foo:${s}:${this}:${value}"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class Derived() : Base by BaseImpl("test") {
|
||||||
|
fun getFooValue(x: Int): String = x.foo
|
||||||
|
fun setFooValue(x: Int, value: String) {
|
||||||
|
x.foo = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
var d = Derived()
|
||||||
|
assertEquals("get Int.foo:test:5", d.getFooValue(5))
|
||||||
|
|
||||||
|
d.setFooValue(10, "A")
|
||||||
|
assertEquals("set Int.foo:test:10:A", d.prop)
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
// This test was adapted from compiler/testData/codegen/box/classes
|
||||||
|
package foo
|
||||||
|
|
||||||
|
trait A<T> {
|
||||||
|
fun foo(t: T): String
|
||||||
|
}
|
||||||
|
|
||||||
|
class Derived(a: A<Int>) : A<Int> by a
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val o = object : A<Int> {
|
||||||
|
override fun foo(t: Int) = if (t == 42) "OK" else "Fail $t"
|
||||||
|
}
|
||||||
|
return Derived(o).foo(42)
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
// This test was adapted from compiler/testData/codegen/box/classes
|
||||||
|
package foo
|
||||||
|
|
||||||
|
trait TextField {
|
||||||
|
fun getText(): String
|
||||||
|
}
|
||||||
|
|
||||||
|
trait InputTextField : TextField {
|
||||||
|
fun setText(text: String)
|
||||||
|
}
|
||||||
|
|
||||||
|
trait MooableTextField : InputTextField {
|
||||||
|
fun moo(a: Int, b: Int, c: Int): Int
|
||||||
|
}
|
||||||
|
|
||||||
|
class SimpleTextField : MooableTextField {
|
||||||
|
private var text2 = ""
|
||||||
|
override fun getText() = text2
|
||||||
|
override fun setText(text: String) {
|
||||||
|
this.text2 = text
|
||||||
|
}
|
||||||
|
override fun moo(a: Int, b: Int, c: Int) = a + b + c
|
||||||
|
}
|
||||||
|
|
||||||
|
class TextFieldWrapper(textField: MooableTextField) : MooableTextField by textField
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val textField = TextFieldWrapper(SimpleTextField())
|
||||||
|
textField.setText("hello world!")
|
||||||
|
|
||||||
|
if (!textField.getText().equals("hello world!")) return "FAIL #!1"
|
||||||
|
if (textField.moo(1, 2, 3) != 6) return "FAIL #2"
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user