extension functions support and tests.

This commit is contained in:
Pavel Talanov
2011-12-08 21:54:49 +04:00
parent 6a07101bd7
commit 50c44db278
11 changed files with 157 additions and 8 deletions
@@ -4,6 +4,7 @@ import com.google.dart.compiler.backend.js.ast.*;
import com.google.dart.compiler.util.AstUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.k2js.translate.context.TemporaryVariable;
import org.jetbrains.k2js.translate.context.TranslationContext;
@@ -14,6 +15,8 @@ import java.util.ArrayList;
import java.util.List;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getDescriptorForElement;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionDescriptor;
import static org.jetbrains.k2js.translate.utils.DescriptorUtils.isExtensionFunction;
import static org.jetbrains.k2js.translate.utils.TranslationUtils.functionWithScope;
@@ -136,6 +139,7 @@ public final class FunctionTranslator extends AbstractTranslator {
private List<JsParameter> translateParameters(@NotNull List<JetParameter> jetParameters,
@NotNull TranslationContext functionBodyContext) {
List<JsParameter> jsParameters = new ArrayList<JsParameter>();
mayBeAddThisParameterForExtensionFunction(jsParameters);
for (JetParameter jetParameter : jetParameters) {
JsName parameterName = declareParameter(jetParameter, functionBodyContext);
jsParameters.add(new JsParameter(parameterName));
@@ -150,4 +154,15 @@ public final class FunctionTranslator extends AbstractTranslator {
getDescriptorForElement(functionBodyContext.bindingContext(), jetParameter);
return context().declareLocalVariable(parameterDescriptor);
}
private void mayBeAddThisParameterForExtensionFunction(@NotNull List<JsParameter> jsParameters) {
if (!(functionDeclaration instanceof JetNamedFunction)) return;
FunctionDescriptor functionDescriptor = getFunctionDescriptor(context().bindingContext(), functionDeclaration);
if (isExtensionFunction(functionDescriptor)) {
JsName receiver = context().jsScope().declareName("receiver");
context().aliaser().setAliasForThis(receiver);
jsParameters.add(new JsParameter(receiver));
}
}
}
@@ -14,6 +14,7 @@ import org.jetbrains.k2js.translate.intrinsic.FunctionIntrinsic;
import org.jetbrains.k2js.translate.utils.DescriptorUtils;
import org.jetbrains.k2js.translate.utils.TranslationUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -100,9 +101,24 @@ public final class CallTranslator extends AbstractTranslator {
if (isConstructor()) {
return constructorCall();
}
if (isExtensionFunction()) {
return extensionFunctionCall();
}
return methodCall();
}
@NotNull
private JsExpression extensionFunctionCall() {
List<JsExpression> argumentList = new ArrayList<JsExpression>();
argumentList.add(receiver);
argumentList.addAll(arguments);
return AstUtil.newInvocation(calleeReference(), argumentList);
}
private boolean isExtensionFunction() {
return DescriptorUtils.isExtensionFunction(functionDescriptor);
}
@NotNull
private JsExpression intrinsicInvocation() {
FunctionIntrinsic functionIntrinsic = context().intrinsics().getFunctionIntrinsic(functionDescriptor);
@@ -144,7 +160,9 @@ public final class CallTranslator extends AbstractTranslator {
@NotNull
private JsExpression qualifiedMethodReference(@NotNull DeclarationDescriptor descriptor) {
JsExpression methodReference = ReferenceTranslator.translateReference(descriptor, context());
if (receiver != null) {
if (isExtensionFunction()) {
AstUtil.setQualifier(methodReference, context().getQualifierForDescriptor(functionDescriptor));
} else if (receiver != null) {
AstUtil.setQualifier(methodReference, receiver);
}
return methodReference;
@@ -52,7 +52,7 @@ public final class BindingUtils {
@NotNull
public static FunctionDescriptor getFunctionDescriptor(@NotNull BindingContext context,
@NotNull JetNamedFunction declaration) {
@NotNull JetDeclarationWithBody declaration) {
return getDescriptorForExpression(context, declaration, FunctionDescriptor.class);
}
@@ -117,4 +117,8 @@ public final class DescriptorUtils {
assert containing != null : "Should be called on objects that have containing declaration.";
return containing;
}
public static boolean isExtensionFunction(@NotNull FunctionDescriptor functionDescriptor) {
return (functionDescriptor.getReceiverParameter().exists());
}
}
@@ -15,8 +15,8 @@ import org.jetbrains.k2js.translate.general.Translation;
import java.util.ArrayList;
import java.util.List;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionDescriptorForOperationExpression;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getPropertyDescriptor;
import static org.jetbrains.k2js.translate.utils.BindingUtils.*;
import static org.jetbrains.k2js.translate.utils.DescriptorUtils.isExtensionFunction;
/**
* @author Talanov Pavel
@@ -67,10 +67,10 @@ public final class TranslationUtils {
public static JsNameRef backingFieldReference(@NotNull TranslationContext context,
@NotNull PropertyDescriptor descriptor) {
JsName backingFieldName = context.getNameForDescriptor(descriptor);
if (BindingUtils.isOwnedByClass(descriptor)) {
if (isOwnedByClass(descriptor)) {
return getThisQualifiedNameReference(context, backingFieldName);
}
assert BindingUtils.isOwnedByNamespace(descriptor)
assert isOwnedByNamespace(descriptor)
: "Only classes and namespaces may own backing fields.";
JsNameRef qualifier = context.getQualifierForDescriptor(descriptor);
return AstUtil.qualified(backingFieldName, qualifier);
@@ -200,10 +200,15 @@ public final class TranslationUtils {
@Nullable
public static JsExpression getImplicitReceiver(@NotNull TranslationContext context,
@NotNull DeclarationDescriptor referencedDescriptor) {
if (BindingUtils.isOwnedByClass(referencedDescriptor)) {
if (referencedDescriptor instanceof FunctionDescriptor) {
if (isExtensionFunction((FunctionDescriptor) referencedDescriptor)) {
return TranslationUtils.getThisQualifier(context);
}
}
if (isOwnedByClass(referencedDescriptor)) {
return TranslationUtils.getThisQualifier(context);
}
if (BindingUtils.isOwnedByNamespace(referencedDescriptor)) {
if (isOwnedByNamespace(referencedDescriptor)) {
if (context.hasQualifierForDescriptor(referencedDescriptor)) {
return context.getQualifierForDescriptor(referencedDescriptor);
}
@@ -0,0 +1,35 @@
package org.jetbrains.k2js.test;
import org.junit.Test;
/**
* @author Talanov Pavel
*/
public final class ExtensionFunctionTest extends TranslationTest {
final private static String MAIN = "extensionFunction/";
@Override
protected String mainDirectory() {
return MAIN;
}
@Test
public void intExtension() throws Exception {
testFooBoxIsTrue("intExtension.kt");
}
@Test
public void extensionWithImplicitReceiver() throws Exception {
testFooBoxIsTrue("extensionWithImplicitReceiver.kt");
}
@Test
public void extensionFunctionOnExpression() throws Exception {
testFooBoxIsTrue("extensionFunctionOnExpression.kt");
}
@Test
public void extensionUsedInsideClass() throws Exception {
testFooBoxIsTrue("extensionUsedInsideClass.kt");
}
}
@@ -0,0 +1,22 @@
import java.util.*
fun <T> ArrayList<T>.findAll(predicate: fun (T) : Boolean): ArrayList<T> {
val result = ArrayList<T>()
for(val t in this) {
if (predicate(t)) result.add(t)
}
return result
}
fun box(): String {
val list: ArrayList<Int> = ArrayList<Int>()
list.add(2)
list.add(3)
list.add(5)
val m: ArrayList<Int> = list.findAll<Int>({(name: Int) => name < 4})
return if (m.size() == 2) "OK" else "fail"
}
@@ -0,0 +1,13 @@
namespace foo
fun Int.same() : Int {
return this
}
fun Int.quadruple() : Int {
return same() * 4;
}
fun box(): Boolean {
return ((3 + 4).quadruple() == 28)
}
@@ -0,0 +1,15 @@
namespace foo
class A(var a : Int) {
fun eval() = f();
}
fun A.f() : Int {
a = 3
return 10
}
fun box(): Boolean {
val a = A(4)
return (a.eval() == 10) && (a.a == 3)
}
@@ -0,0 +1,13 @@
namespace foo
fun Int.same() : Int {
return this
}
fun Int.quadruple() : Int {
return same() * 4;
}
fun box(): Boolean {
return (3.quadruple() == 12)
}
@@ -0,0 +1,9 @@
namespace foo
fun Int.quadruple() : Int {
return this * 4;
}
fun box(): Boolean {
return (3.quadruple() == 12)
}