virtual extension functions support and tests.
This commit is contained in:
@@ -10,13 +10,13 @@ import org.jetbrains.k2js.translate.context.TemporaryVariable;
|
|||||||
import org.jetbrains.k2js.translate.context.TranslationContext;
|
import org.jetbrains.k2js.translate.context.TranslationContext;
|
||||||
import org.jetbrains.k2js.translate.general.AbstractTranslator;
|
import org.jetbrains.k2js.translate.general.AbstractTranslator;
|
||||||
import org.jetbrains.k2js.translate.general.Translation;
|
import org.jetbrains.k2js.translate.general.Translation;
|
||||||
|
import org.jetbrains.k2js.translate.utils.DescriptorUtils;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
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.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;
|
||||||
|
|
||||||
|
|
||||||
@@ -29,6 +29,8 @@ public final class FunctionTranslator extends AbstractTranslator {
|
|||||||
private final JetDeclarationWithBody functionDeclaration;
|
private final JetDeclarationWithBody functionDeclaration;
|
||||||
@NotNull
|
@NotNull
|
||||||
private final JsFunction functionObject;
|
private final JsFunction functionObject;
|
||||||
|
@NotNull
|
||||||
|
private final TranslationContext functionBodyContext;
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static FunctionTranslator newInstance(@NotNull JetDeclarationWithBody function,
|
public static FunctionTranslator newInstance(@NotNull JetDeclarationWithBody function,
|
||||||
@@ -41,6 +43,7 @@ public final class FunctionTranslator extends AbstractTranslator {
|
|||||||
super(context);
|
super(context);
|
||||||
this.functionDeclaration = functionDeclaration;
|
this.functionDeclaration = functionDeclaration;
|
||||||
this.functionObject = createFunctionObject();
|
this.functionObject = createFunctionObject();
|
||||||
|
this.functionBodyContext = functionBodyContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@@ -61,12 +64,18 @@ public final class FunctionTranslator extends AbstractTranslator {
|
|||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private JsFunction generateFunctionObject() {
|
private JsFunction generateFunctionObject() {
|
||||||
TranslationContext innerContext = functionBodyContext();
|
functionObject.setParameters(translateParameters(functionDeclaration.getValueParameters(), functionBodyContext));
|
||||||
functionObject.setParameters(translateParameters(functionDeclaration.getValueParameters(), innerContext));
|
functionObject.setBody(translateBody(functionBodyContext));
|
||||||
functionObject.setBody(translateBody(innerContext));
|
restoreContext();
|
||||||
return functionObject;
|
return functionObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void restoreContext() {
|
||||||
|
if (isExtensionFunction()) {
|
||||||
|
functionBodyContext.removeAliasForThis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private JsFunction createFunctionObject() {
|
private JsFunction createFunctionObject() {
|
||||||
if (isDeclaration()) {
|
if (isDeclaration()) {
|
||||||
return functionWithScope
|
return functionWithScope
|
||||||
@@ -156,13 +165,20 @@ public final class FunctionTranslator extends AbstractTranslator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void mayBeAddThisParameterForExtensionFunction(@NotNull List<JsParameter> jsParameters) {
|
private void mayBeAddThisParameterForExtensionFunction(@NotNull List<JsParameter> jsParameters) {
|
||||||
if (!(functionDeclaration instanceof JetNamedFunction)) return;
|
boolean isExtensionFunction = isExtensionFunction();
|
||||||
|
if (isExtensionFunction) {
|
||||||
FunctionDescriptor functionDescriptor = getFunctionDescriptor(context().bindingContext(), functionDeclaration);
|
JsName receiver = functionBodyContext.jsScope().declareName("receiver");
|
||||||
if (isExtensionFunction(functionDescriptor)) {
|
|
||||||
JsName receiver = context().jsScope().declareName("receiver");
|
|
||||||
context().aliaser().setAliasForThis(receiver);
|
context().aliaser().setAliasForThis(receiver);
|
||||||
jsParameters.add(new JsParameter(receiver));
|
jsParameters.add(new JsParameter(receiver));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isExtensionFunction() {
|
||||||
|
if (!(functionDeclaration instanceof JetNamedFunction)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
FunctionDescriptor functionDescriptor = getFunctionDescriptor(context().bindingContext(), functionDeclaration);
|
||||||
|
return DescriptorUtils.isExtensionFunction(functionDescriptor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -161,7 +161,8 @@ public final class CallTranslator extends AbstractTranslator {
|
|||||||
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 (isExtensionFunction()) {
|
if (isExtensionFunction()) {
|
||||||
AstUtil.setQualifier(methodReference, context().getQualifierForDescriptor(functionDescriptor));
|
AstUtil.setQualifier(methodReference,
|
||||||
|
TranslationUtils.getExtensionFunctionImplicitReceiver(context(), functionDescriptor));
|
||||||
} else if (receiver != null) {
|
} else if (receiver != null) {
|
||||||
AstUtil.setQualifier(methodReference, receiver);
|
AstUtil.setQualifier(methodReference, receiver);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -205,6 +205,20 @@ public final class TranslationUtils {
|
|||||||
return TranslationUtils.getThisQualifier(context);
|
return TranslationUtils.getThisQualifier(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return getImplicitReceiverBasedOnOwner(context, referencedDescriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static JsExpression getExtensionFunctionImplicitReceiver(@NotNull TranslationContext context,
|
||||||
|
@NotNull FunctionDescriptor descriptor) {
|
||||||
|
JsExpression receiverBasedOnOwner = getImplicitReceiverBasedOnOwner(context, descriptor);
|
||||||
|
assert receiverBasedOnOwner != null : "Extension function must have receiver based on owner.";
|
||||||
|
return receiverBasedOnOwner;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private static JsExpression getImplicitReceiverBasedOnOwner(@NotNull TranslationContext context,
|
||||||
|
@NotNull DeclarationDescriptor referencedDescriptor) {
|
||||||
if (isOwnedByClass(referencedDescriptor)) {
|
if (isOwnedByClass(referencedDescriptor)) {
|
||||||
return TranslationUtils.getThisQualifier(context);
|
return TranslationUtils.getThisQualifier(context);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,4 +32,14 @@ public final class ExtensionFunctionTest extends TranslationTest {
|
|||||||
public void extensionUsedInsideClass() throws Exception {
|
public void extensionUsedInsideClass() throws Exception {
|
||||||
testFooBoxIsTrue("extensionUsedInsideClass.kt");
|
testFooBoxIsTrue("extensionUsedInsideClass.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void virtualExtension() throws Exception {
|
||||||
|
testFooBoxIsTrue("virtualExtension.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void virtualExtensionOverride() throws Exception {
|
||||||
|
testFooBoxIsTrue("virtualExtensionOverride.kt");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
namespace foo
|
||||||
|
|
||||||
|
class A(var a : Int) {
|
||||||
|
|
||||||
|
fun Int.modify() : Int {
|
||||||
|
return this * 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
fun eval() = a.modify();
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): Boolean {
|
||||||
|
val a = A(4)
|
||||||
|
return (a.eval() == 12)
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
namespace foo
|
||||||
|
|
||||||
|
open class A(var a : Int) {
|
||||||
|
|
||||||
|
open fun Int.modify() : Int {
|
||||||
|
return this * 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
fun eval() = a.modify();
|
||||||
|
}
|
||||||
|
|
||||||
|
class B(a : Int) : A(a) {
|
||||||
|
override fun Int.modify() : Int {
|
||||||
|
return this - 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): Boolean {
|
||||||
|
return (A(4).eval() == 12) && (A(2).eval() == 6) && (B(3).eval() == 1)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user