JS backend: support for explicit delegation

#KT-4479 Fixed
This commit is contained in:
Michael Nedzelsky
2014-08-06 11:02:52 +04:00
parent 82af542747
commit 12db8f1551
26 changed files with 942 additions and 75 deletions
@@ -18,18 +18,19 @@ package org.jetbrains.jet.backend.common;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
import org.jetbrains.jet.lang.descriptors.ClassifierDescriptor;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.psi.JetDelegationSpecifier;
import org.jetbrains.jet.lang.psi.JetExpression;
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.callUtil.CallUtilPackage;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
import org.jetbrains.jet.lang.resolve.name.Name;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.*;
/**
* Backend-independent utility class.
@@ -91,6 +92,66 @@ public class CodegenUtil {
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(
@NotNull List<ValueParameterDescriptor> parameters,
@NotNull List<ClassifierDescriptor> classifiers
@@ -1313,14 +1313,11 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
for (JetDelegationSpecifier specifier : delegationSpecifiers) {
if (specifier instanceof JetDelegatorByExpressionSpecifier) {
JetExpression expression = ((JetDelegatorByExpressionSpecifier) specifier).getDelegateExpression();
PropertyDescriptor propertyDescriptor = getDelegatePropertyIfAny(expression);
PropertyDescriptor propertyDescriptor = CodegenUtil.getDelegatePropertyIfAny(expression, descriptor, bindingContext);
ClassDescriptor superClassDescriptor = getSuperClass(specifier);
if (propertyDescriptor != null &&
!propertyDescriptor.isVar() &&
Boolean.TRUE.equals(bindingContext.get(BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor))) {
// final property with backing field
if (CodegenUtil.isFinalPropertyWithBackingField(propertyDescriptor, bindingContext)) {
result.addField((JetDelegatorByExpressionSpecifier) specifier, propertyDescriptor);
}
else {
@@ -1334,12 +1331,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
@NotNull
private ClassDescriptor getSuperClass(@NotNull JetDelegationSpecifier specifier) {
JetType superType = bindingContext.get(BindingContext.TYPE, specifier.getTypeReference());
assert superType != null;
ClassDescriptor superClassDescriptor = (ClassDescriptor) superType.getConstructor().getDeclarationDescriptor();
assert superClassDescriptor != null;
return superClassDescriptor;
return CodegenUtil.getSuperClassByDelegationSpecifier(specifier, bindingContext);
}
private void genCallToDelegatorByExpressionSpecifier(
@@ -1361,26 +1353,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
@Nullable
private PropertyDescriptor getDelegatePropertyIfAny(JetExpression expression) {
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() == 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;
return CodegenUtil.getDelegatePropertyIfAny(expression, descriptor, bindingContext);
}
private void lookupConstructorExpressionsInClosureIfPresent(final ConstructorContext constructorContext) {
@@ -1737,24 +1710,16 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
}
protected void generateDelegates(ClassDescriptor toClass, DelegationFieldsInfo.Field field) {
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) {
if (declaration instanceof PropertyDescriptor) {
propertyCodegen
.genDelegate((PropertyDescriptor) declaration, (PropertyDescriptor) overriddenDescriptor, field.getStackValue());
}
else if (declaration instanceof FunctionDescriptor) {
functionCodegen
.genDelegate((FunctionDescriptor) declaration, (FunctionDescriptor) overriddenDescriptor, field.getStackValue());
}
}
}
}
for (Map.Entry<CallableMemberDescriptor, CallableMemberDescriptor> entry : CodegenUtil.getDelegates(descriptor, toClass).entrySet()) {
CallableMemberDescriptor callableMemberDescriptor = entry.getKey();
CallableMemberDescriptor overriddenDescriptor = entry.getValue();
if (callableMemberDescriptor instanceof PropertyDescriptor) {
propertyCodegen
.genDelegate((PropertyDescriptor) callableMemberDescriptor, (PropertyDescriptor) overriddenDescriptor, field.getStackValue());
}
else if (callableMemberDescriptor instanceof FunctionDescriptor) {
functionCodegen
.genDelegate((FunctionDescriptor) callableMemberDescriptor, (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 GETTER_PREFIX = "get_";
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";
@@ -131,9 +131,14 @@ public final class Namer {
return new JsNameRef(getPrototypeName(), classOrTraitExpression);
}
@NotNull
public static String getDelegatePrefix() {
return DELEGATE;
}
@NotNull
public static String getDelegateName(@NotNull String propertyName) {
return propertyName + DELEGATE_POSTFIX;
return propertyName + DELEGATE;
}
@NotNull
@@ -125,14 +125,16 @@ public final class ClassTranslator extends AbstractTranslator {
declarationContext = fixContextForClassObjectAccessing(declarationContext);
invocationArguments.add(getSuperclassReferences(declarationContext));
DelegationTranslator delegationTranslator = new DelegationTranslator(classDeclaration, context());
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);
}
translatePropertiesAsConstructorParameters(declarationContext, properties);
DeclarationBodyVisitor bodyVisitor = new DeclarationBodyVisitor(properties, staticProperties);
bodyVisitor.traverseContainer(classDeclaration, declarationContext);
delegationTranslator.generateDelegated(properties);
mayBeAddEnumEntry(bodyVisitor.getEnumEntryList(), staticProperties, declarationContext);
if (KotlinBuiltIns.getInstance().isData(descriptor)) {
@@ -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))
}
}
@@ -54,6 +54,27 @@ public fun translateAccessors(
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(
val descriptor: PropertyDescriptor,
val declaration: JetProperty?,
@@ -63,19 +84,7 @@ private class PropertyTranslator(
private val propertyName: String = descriptor.getName().asString()
fun translate(result: MutableList<JsPropertyInitializer>) {
val to: MutableList<JsPropertyInitializer>
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())
}
result.addGetterAndSetter(descriptor, context(), { generateGetter() }, { generateSetter() })
}
private fun generateGetter(): JsPropertyInitializer =
@@ -84,9 +93,9 @@ private class PropertyTranslator(
private fun generateSetter(): JsPropertyInitializer =
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 =
declaration?.getGetter() ?:
@@ -33,6 +33,7 @@ import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lexer.JetTokens;
import org.jetbrains.k2js.translate.context.Namer;
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.reference.CallArgumentTranslator;
@@ -62,7 +63,7 @@ public final class ClassInitializerTranslator extends AbstractTranslator {
}
@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
ConstructorDescriptor primaryConstructor = getConstructor(bindingContext(), classDeclaration);
JsFunction result = context().getFunctionObject(primaryConstructor);
@@ -70,6 +71,7 @@ public final class ClassInitializerTranslator extends AbstractTranslator {
// for properties declared as constructor parameters
result.getParameters().addAll(translatePrimaryConstructorParameters());
mayBeAddCallToSuperMethod(result);
delegationTranslator.addInitCode(initializerStatements);
new InitializerVisitor(initializerStatements).traverseContainer(classDeclaration, context());
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.psi.*;
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.types.JetType;
import org.jetbrains.k2js.translate.context.TemporaryConstVariable;
@@ -202,6 +203,12 @@ public final class TranslationUtils {
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
private static String getStableMangledName(@NotNull String suggestedName, String forCalculateId) {
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"
}