extension functions support and tests.
This commit is contained in:
@@ -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)
|
||||
}
|
||||
Reference in New Issue
Block a user