diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/functions/basic/FunctionIntrinsic.java b/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/functions/basic/FunctionIntrinsic.java index 9fb97b0d8bd..f8ed17660e2 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/functions/basic/FunctionIntrinsic.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/functions/basic/FunctionIntrinsic.java @@ -20,6 +20,7 @@ import com.google.dart.compiler.backend.js.ast.JsExpression; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.reference.CallTranslator; import java.util.List; @@ -45,10 +46,30 @@ public abstract class FunctionIntrinsic { }; @NotNull - public abstract JsExpression apply(@Nullable JsExpression receiver, @NotNull List arguments, - @NotNull TranslationContext context); + public JsExpression apply(@NotNull CallTranslator callTranslator, + @NotNull List arguments, + @NotNull TranslationContext context) { + return apply(callTranslator.getCallParameters().getThisOrReceiverOrNull(), arguments, context); + } + + @NotNull + public abstract JsExpression apply(@Nullable JsExpression receiver, + @NotNull List arguments, + @NotNull TranslationContext context + ); public boolean exists() { return true; } + + public abstract static class CallParametersAwareFunctionIntrinsic extends FunctionIntrinsic { + @NotNull + @Override + public JsExpression apply(@Nullable JsExpression receiver, + @NotNull List arguments, + @NotNull TranslationContext context + ) { + throw new IllegalStateException(); + } + } } diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/functions/factories/TopLevelFIF.java b/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/functions/factories/TopLevelFIF.java index 7ba23b5a005..7ad6599c655 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/functions/factories/TopLevelFIF.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/functions/factories/TopLevelFIF.java @@ -21,16 +21,22 @@ import com.google.dart.compiler.backend.js.ast.JsInvocation; import com.google.dart.compiler.backend.js.ast.JsNameRef; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; +import org.jetbrains.jet.lang.descriptors.FunctionDescriptor; +import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; import org.jetbrains.k2js.translate.context.TranslationContext; import org.jetbrains.k2js.translate.intrinsic.functions.basic.BuiltInFunctionIntrinsic; import org.jetbrains.k2js.translate.intrinsic.functions.basic.CallStandardMethodIntrinsic; import org.jetbrains.k2js.translate.intrinsic.functions.basic.FunctionIntrinsic; +import org.jetbrains.k2js.translate.intrinsic.functions.patterns.DescriptorPredicate; import org.jetbrains.k2js.translate.intrinsic.functions.patterns.NamePredicate; -import org.jetbrains.k2js.translate.intrinsic.functions.patterns.PatternBuilder; +import org.jetbrains.k2js.translate.reference.CallTranslator; import java.util.List; +import static org.jetbrains.k2js.translate.intrinsic.functions.basic.FunctionIntrinsic.CallParametersAwareFunctionIntrinsic; import static org.jetbrains.k2js.translate.intrinsic.functions.patterns.PatternBuilder.pattern; +import static org.jetbrains.k2js.translate.utils.TranslationUtils.generateCallArgumentList; public final class TopLevelFIF extends CompositeFIF { @NotNull @@ -52,14 +58,44 @@ public final class TopLevelFIF extends CompositeFIF { public static final FunctionIntrinsicFactory INSTANCE = new TopLevelFIF(); private TopLevelFIF() { - add(pattern("toString"), new BuiltInFunctionIntrinsic("toString")); - add(pattern("equals"), EQUALS); + add(pattern("jet", "toString").receiverExists(true), new BuiltInFunctionIntrinsic("toString")); + add(pattern("jet", "equals").receiverExists(true), EQUALS); add(pattern(NamePredicate.PRIMITIVE_NUMBERS, "equals"), EQUALS); add(pattern("String|Boolean|Char|Number.equals"), EQUALS); - add(pattern("arrayOfNulls"), new CallStandardMethodIntrinsic(new JsNameRef("nullArray", "Kotlin"), false, 1)); - add(pattern("iterator"), RETURN_RECEIVER_INTRINSIC); + add(pattern("jet", "arrayOfNulls"), new CallStandardMethodIntrinsic(new JsNameRef("nullArray", "Kotlin"), false, 1)); + add(pattern("jet", "iterator").receiverExists(true), RETURN_RECEIVER_INTRINSIC); + add(new DescriptorPredicate() { + @Override + public boolean apply(@NotNull FunctionDescriptor descriptor) { + if (!descriptor.getName().asString().equals("invoke")) { + return false; + } + int parameterCount = descriptor.getValueParameters().size(); + DeclarationDescriptor fun = descriptor.getContainingDeclaration(); + return fun == (descriptor.getReceiverParameter() == null + ? KotlinBuiltIns.getInstance().getFunction(parameterCount) + : KotlinBuiltIns.getInstance().getExtensionFunction(parameterCount)); + } + }, new CallParametersAwareFunctionIntrinsic() { + @NotNull + @Override + public JsExpression apply(@NotNull CallTranslator callTranslator, @NotNull List arguments, @NotNull TranslationContext context) { + JsExpression thisExpression = callTranslator.getCallParameters().getThisObject(); + if (thisExpression == null) { + return new JsInvocation(callTranslator.getCallParameters().getFunctionReference(), arguments); + } + else if (callTranslator.getResolvedCall().getReceiverArgument().exists()) { + return callTranslator.extensionFunctionCall(false); + } + else { + return new JsInvocation(new JsNameRef("call", callTranslator.getCallParameters().getFunctionReference()), + generateCallArgumentList(thisExpression, arguments)); + } + } + } + ); - add(PatternBuilder.create("java", "util", "set").receiverParameterExists(true), new FunctionIntrinsic() { + add(pattern("java", "util", "set").receiverExists(true), new FunctionIntrinsic() { @NotNull @Override public JsExpression apply( diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/functions/patterns/PatternBuilder.java b/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/functions/patterns/PatternBuilder.java index 7f24808aea4..f336a2aba35 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/functions/patterns/PatternBuilder.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/functions/patterns/PatternBuilder.java @@ -121,7 +121,7 @@ public final class PatternBuilder { } @NotNull - public static DescriptorPredicateImpl create(@NotNull String... names) { + public static DescriptorPredicateImpl pattern(@NotNull String... names) { return new DescriptorPredicateImpl(names); } @@ -138,7 +138,7 @@ public final class PatternBuilder { this.names = names; } - public DescriptorPredicateImpl receiverParameterExists(boolean receiverParameterExists) { + public DescriptorPredicateImpl receiverExists(boolean receiverParameterExists) { this.receiverParameterExists = receiverParameterExists; return this; } diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallTranslator.java index bc98ea66ac0..ac3f8cad403 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallTranslator.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallTranslator.java @@ -18,7 +18,6 @@ package org.jetbrains.k2js.translate.reference; import com.google.dart.compiler.backend.js.ast.JsExpression; import com.google.dart.compiler.backend.js.ast.JsInvocation; -import com.google.dart.compiler.backend.js.ast.JsNameRef; import com.google.dart.compiler.backend.js.ast.JsNew; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -35,8 +34,8 @@ import org.jetbrains.k2js.translate.intrinsic.functions.patterns.NamePredicate; import org.jetbrains.k2js.translate.utils.AnnotationsUtils; import org.jetbrains.k2js.translate.utils.ErrorReportingUtils; import org.jetbrains.k2js.translate.utils.JsDescriptorUtils; +import org.jetbrains.k2js.translate.utils.TranslationUtils; -import java.util.ArrayList; import java.util.List; import static org.jetbrains.k2js.translate.reference.CallParametersResolver.resolveCallParameters; @@ -72,46 +71,37 @@ public final class CallTranslator extends AbstractTranslator { this.callParameters = resolveCallParameters(receiver, callee, descriptor, resolvedCall, context); } + @NotNull + public ResolvedCall getResolvedCall() { + return resolvedCall; + } + + @NotNull + public CallParameters getCallParameters() { + return callParameters; + } + @NotNull /*package*/ JsExpression translate() { - if (isIntrinsic()) { - return intrinsicInvocation(); + JsExpression result = intrinsicInvocation(); + if (result != null) { + return result; } if (isConstructor()) { return constructorCall(); } if (resolvedCall.getReceiverArgument().exists()) { if (AnnotationsUtils.isNativeObject(descriptor)) { - return methodCall(callParameters.getReceiver()); + return nativeExtensionCall(); } - return extensionFunctionCall(!(descriptor instanceof ExpressionAsFunctionDescriptor) && !descriptor.getName().asString().equals("invoke")); + return extensionFunctionCall(!(descriptor instanceof ExpressionAsFunctionDescriptor)); } if (isExpressionAsFunction()) { return expressionAsFunctionCall(); } - if (isInvoke()) { - return invokeCall(); - } return methodCall(getThisObjectOrQualifier()); } - //TODO: - private boolean isInvoke() { - return descriptor.getName().asString().equals("invoke"); - } - - @NotNull - private JsExpression invokeCall() { - JsExpression thisExpression = callParameters.getThisObject(); - if (thisExpression == null) { - return new JsInvocation(callParameters.getFunctionReference(), arguments); - } - JsInvocation call = new JsInvocation(new JsNameRef("call", callParameters.getFunctionReference())); - call.getArguments().add(thisExpression); - call.getArguments().addAll(arguments); - return call; - } - private boolean isExpressionAsFunction() { return descriptor instanceof ExpressionAsFunctionDescriptor || resolvedCall instanceof VariableAsFunctionResolvedCall; @@ -122,25 +112,20 @@ public final class CallTranslator extends AbstractTranslator { return methodCall(null); } - private boolean isIntrinsic() { - if (descriptor instanceof FunctionDescriptor) { - FunctionIntrinsic intrinsic = context().intrinsics().getFunctionIntrinsics().getIntrinsic((FunctionDescriptor) descriptor); - return intrinsic.exists(); - } - return false; - } - - @NotNull + @Nullable private JsExpression intrinsicInvocation() { - assert descriptor instanceof FunctionDescriptor; - try { - FunctionIntrinsic intrinsic = context().intrinsics().getFunctionIntrinsics().getIntrinsic((FunctionDescriptor) descriptor); - assert intrinsic.exists(); - return intrinsic.apply(callParameters.getThisOrReceiverOrNull(), arguments, context()); - } - catch (RuntimeException e) { - throw ErrorReportingUtils.reportErrorWithLocation(e, descriptor, bindingContext()); + if (descriptor instanceof FunctionDescriptor) { + try { + FunctionIntrinsic intrinsic = context().intrinsics().getFunctionIntrinsics().getIntrinsic((FunctionDescriptor) descriptor); + if (intrinsic.exists()) { + return intrinsic.apply(this, arguments, context()); + } + } + catch (RuntimeException e) { + throw ErrorReportingUtils.reportErrorWithLocation(e, descriptor, bindingContext()); + } } + return null; } private boolean isConstructor() { @@ -189,27 +174,18 @@ public final class CallTranslator extends AbstractTranslator { } @NotNull - private JsExpression extensionFunctionCall(final boolean useThis) { - return callType.constructCall(callParameters.getReceiver(), new CallType.CallConstructor() { - @NotNull - @Override - public JsExpression construct(@Nullable JsExpression receiver) { - assert receiver != null : "Could not be null for extensions"; - JsExpression functionReference = callParameters.getFunctionReference(); - if (useThis) { - setQualifier(functionReference, getThisObjectOrQualifier()); - } - return new JsInvocation(functionReference, generateExtensionCallArgumentList(receiver)); - } - }, context()); + private JsExpression nativeExtensionCall() { + return methodCall(callParameters.getReceiver()); } @NotNull - private List generateExtensionCallArgumentList(@NotNull JsExpression receiver) { - List argumentList = new ArrayList(); - argumentList.add(receiver); - argumentList.addAll(arguments); - return argumentList; + public JsExpression extensionFunctionCall(boolean useThis) { + return callType.constructCall(callParameters.getReceiver(), new ExtensionCallConstructor(useThis), context()); + } + + @NotNull + private List generateCallArgumentList(@NotNull JsExpression receiver) { + return TranslationUtils.generateCallArgumentList(receiver, arguments); } @NotNull @@ -268,4 +244,23 @@ public final class CallTranslator extends AbstractTranslator { } return context().getQualifierForDescriptor(descriptor); } + + private class ExtensionCallConstructor implements CallType.CallConstructor { + private final boolean useThis; + + private ExtensionCallConstructor(boolean useThis) { + this.useThis = useThis; + } + + @NotNull + @Override + public JsExpression construct(@Nullable JsExpression receiver) { + assert receiver != null : "Could not be null for extensions"; + JsExpression functionReference = callParameters.getFunctionReference(); + if (useThis) { + setQualifier(functionReference, getThisObjectOrQualifier()); + } + return new JsInvocation(callParameters.getFunctionReference(), generateCallArgumentList(receiver)); + } + } } diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/utils/TranslationUtils.java b/js/js.translator/src/org/jetbrains/k2js/translate/utils/TranslationUtils.java index ce8e9f754b2..add8f9ee7d3 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/utils/TranslationUtils.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/utils/TranslationUtils.java @@ -36,6 +36,7 @@ import java.util.List; import static org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionDescriptorForOperationExpression; import static org.jetbrains.k2js.translate.utils.JsAstUtils.assignment; +import static org.jetbrains.k2js.translate.utils.JsAstUtils.createDataDescriptor; public final class TranslationUtils { private TranslationUtils() { @@ -57,7 +58,7 @@ public final class TranslationUtils { @NotNull private static JsPropertyInitializer translateExtensionFunctionAsEcma5DataDescriptor(@NotNull JsFunction function, @NotNull FunctionDescriptor descriptor, @NotNull TranslationContext context) { - JsObjectLiteral meta = JsAstUtils.createDataDescriptor(function, descriptor.getModality().isOverridable()); + JsObjectLiteral meta = createDataDescriptor(function, descriptor.getModality().isOverridable()); return new JsPropertyInitializer(context.getNameForDescriptor(descriptor).makeRef(), meta); } @@ -193,6 +194,18 @@ public final class TranslationUtils { return false; } + @NotNull + public static List generateCallArgumentList(@NotNull JsExpression receiver, @NotNull List arguments) { + if (arguments.isEmpty()) { + return Collections.singletonList(receiver); + } + + List argumentList = new ArrayList(1 + arguments.size()); + argumentList.add(receiver); + argumentList.addAll(arguments); + return argumentList; + } + public static boolean isCacheNeeded(@NotNull JsExpression expression) { return !(expression instanceof JsLiteral) && (!(expression instanceof JsNameRef) || ((JsNameRef) expression).getQualifier() != null);