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 com.google.dart.compiler.util.AstUtil;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
|
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
|
||||||
|
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
|
||||||
import org.jetbrains.jet.lang.psi.*;
|
import org.jetbrains.jet.lang.psi.*;
|
||||||
import org.jetbrains.k2js.translate.context.TemporaryVariable;
|
import org.jetbrains.k2js.translate.context.TemporaryVariable;
|
||||||
import org.jetbrains.k2js.translate.context.TranslationContext;
|
import org.jetbrains.k2js.translate.context.TranslationContext;
|
||||||
@@ -14,6 +15,8 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.jetbrains.k2js.translate.utils.BindingUtils.getDescriptorForElement;
|
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;
|
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,
|
private List<JsParameter> translateParameters(@NotNull List<JetParameter> jetParameters,
|
||||||
@NotNull TranslationContext functionBodyContext) {
|
@NotNull TranslationContext functionBodyContext) {
|
||||||
List<JsParameter> jsParameters = new ArrayList<JsParameter>();
|
List<JsParameter> jsParameters = new ArrayList<JsParameter>();
|
||||||
|
mayBeAddThisParameterForExtensionFunction(jsParameters);
|
||||||
for (JetParameter jetParameter : jetParameters) {
|
for (JetParameter jetParameter : jetParameters) {
|
||||||
JsName parameterName = declareParameter(jetParameter, functionBodyContext);
|
JsName parameterName = declareParameter(jetParameter, functionBodyContext);
|
||||||
jsParameters.add(new JsParameter(parameterName));
|
jsParameters.add(new JsParameter(parameterName));
|
||||||
@@ -150,4 +154,15 @@ public final class FunctionTranslator extends AbstractTranslator {
|
|||||||
getDescriptorForElement(functionBodyContext.bindingContext(), jetParameter);
|
getDescriptorForElement(functionBodyContext.bindingContext(), jetParameter);
|
||||||
return context().declareLocalVariable(parameterDescriptor);
|
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.DescriptorUtils;
|
||||||
import org.jetbrains.k2js.translate.utils.TranslationUtils;
|
import org.jetbrains.k2js.translate.utils.TranslationUtils;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -100,9 +101,24 @@ public final class CallTranslator extends AbstractTranslator {
|
|||||||
if (isConstructor()) {
|
if (isConstructor()) {
|
||||||
return constructorCall();
|
return constructorCall();
|
||||||
}
|
}
|
||||||
|
if (isExtensionFunction()) {
|
||||||
|
return extensionFunctionCall();
|
||||||
|
}
|
||||||
return methodCall();
|
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
|
@NotNull
|
||||||
private JsExpression intrinsicInvocation() {
|
private JsExpression intrinsicInvocation() {
|
||||||
FunctionIntrinsic functionIntrinsic = context().intrinsics().getFunctionIntrinsic(functionDescriptor);
|
FunctionIntrinsic functionIntrinsic = context().intrinsics().getFunctionIntrinsic(functionDescriptor);
|
||||||
@@ -144,7 +160,9 @@ public final class CallTranslator extends AbstractTranslator {
|
|||||||
@NotNull
|
@NotNull
|
||||||
private JsExpression qualifiedMethodReference(@NotNull DeclarationDescriptor descriptor) {
|
private JsExpression qualifiedMethodReference(@NotNull DeclarationDescriptor descriptor) {
|
||||||
JsExpression methodReference = ReferenceTranslator.translateReference(descriptor, context());
|
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);
|
AstUtil.setQualifier(methodReference, receiver);
|
||||||
}
|
}
|
||||||
return methodReference;
|
return methodReference;
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ public final class BindingUtils {
|
|||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static FunctionDescriptor getFunctionDescriptor(@NotNull BindingContext context,
|
public static FunctionDescriptor getFunctionDescriptor(@NotNull BindingContext context,
|
||||||
@NotNull JetNamedFunction declaration) {
|
@NotNull JetDeclarationWithBody declaration) {
|
||||||
return getDescriptorForExpression(context, declaration, FunctionDescriptor.class);
|
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.";
|
assert containing != null : "Should be called on objects that have containing declaration.";
|
||||||
return containing;
|
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.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionDescriptorForOperationExpression;
|
import static org.jetbrains.k2js.translate.utils.BindingUtils.*;
|
||||||
import static org.jetbrains.k2js.translate.utils.BindingUtils.getPropertyDescriptor;
|
import static org.jetbrains.k2js.translate.utils.DescriptorUtils.isExtensionFunction;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Talanov Pavel
|
* @author Talanov Pavel
|
||||||
@@ -67,10 +67,10 @@ public final class TranslationUtils {
|
|||||||
public static JsNameRef backingFieldReference(@NotNull TranslationContext context,
|
public static JsNameRef backingFieldReference(@NotNull TranslationContext context,
|
||||||
@NotNull PropertyDescriptor descriptor) {
|
@NotNull PropertyDescriptor descriptor) {
|
||||||
JsName backingFieldName = context.getNameForDescriptor(descriptor);
|
JsName backingFieldName = context.getNameForDescriptor(descriptor);
|
||||||
if (BindingUtils.isOwnedByClass(descriptor)) {
|
if (isOwnedByClass(descriptor)) {
|
||||||
return getThisQualifiedNameReference(context, backingFieldName);
|
return getThisQualifiedNameReference(context, backingFieldName);
|
||||||
}
|
}
|
||||||
assert BindingUtils.isOwnedByNamespace(descriptor)
|
assert isOwnedByNamespace(descriptor)
|
||||||
: "Only classes and namespaces may own backing fields.";
|
: "Only classes and namespaces may own backing fields.";
|
||||||
JsNameRef qualifier = context.getQualifierForDescriptor(descriptor);
|
JsNameRef qualifier = context.getQualifierForDescriptor(descriptor);
|
||||||
return AstUtil.qualified(backingFieldName, qualifier);
|
return AstUtil.qualified(backingFieldName, qualifier);
|
||||||
@@ -200,10 +200,15 @@ public final class TranslationUtils {
|
|||||||
@Nullable
|
@Nullable
|
||||||
public static JsExpression getImplicitReceiver(@NotNull TranslationContext context,
|
public static JsExpression getImplicitReceiver(@NotNull TranslationContext context,
|
||||||
@NotNull DeclarationDescriptor referencedDescriptor) {
|
@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);
|
return TranslationUtils.getThisQualifier(context);
|
||||||
}
|
}
|
||||||
if (BindingUtils.isOwnedByNamespace(referencedDescriptor)) {
|
if (isOwnedByNamespace(referencedDescriptor)) {
|
||||||
if (context.hasQualifierForDescriptor(referencedDescriptor)) {
|
if (context.hasQualifierForDescriptor(referencedDescriptor)) {
|
||||||
return context.getQualifierForDescriptor(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