From bc3f53afffc560c7bd4d19552e780270a884b687 Mon Sep 17 00:00:00 2001 From: Zalim Bashorov Date: Tue, 9 Dec 2014 22:06:01 +0300 Subject: [PATCH] JS backend: added the support dynamic. --- .../lang/resolve/calls/tasks/dynamicCalls.kt | 2 +- .../callTranslator/FunctionCallCases.kt | 76 +++++++++++++++++++ .../k2js/translate/context/StaticContext.java | 18 +++++ .../intrinsic/operation/CompareToBOIF.kt | 3 + .../intrinsic/operation/EqualsBOIF.kt | 13 ++++ .../operation/BinaryOperationTranslator.java | 4 + .../operation/IncrementTranslator.java | 13 +++- .../translate/operation/OperatorTable.java | 6 ++ .../k2js/translate/utils/PsiUtils.java | 2 +- 9 files changed, 133 insertions(+), 4 deletions(-) diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/tasks/dynamicCalls.kt b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/tasks/dynamicCalls.kt index 510c1394011..58b11c7e2ec 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/tasks/dynamicCalls.kt +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/tasks/dynamicCalls.kt @@ -179,7 +179,7 @@ object DynamicCallableDescriptors { } } -fun DeclarationDescriptor.isDynamic(): Boolean { +public fun DeclarationDescriptor.isDynamic(): Boolean { if (this !is CallableDescriptor) return false val dispatchReceiverParameter = getDispatchReceiverParameter() return dispatchReceiverParameter != null && dispatchReceiverParameter.getType().isDynamic() diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/callTranslator/FunctionCallCases.kt b/js/js.translator/src/org/jetbrains/k2js/translate/callTranslator/FunctionCallCases.kt index 9cffc92806f..b767f162c9e 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/callTranslator/FunctionCallCases.kt +++ b/js/js.translator/src/org/jetbrains/k2js/translate/callTranslator/FunctionCallCases.kt @@ -35,6 +35,18 @@ import com.google.dart.compiler.backend.js.ast.JsArrayAccess import org.jetbrains.k2js.translate.utils.JsAstUtils import org.jetbrains.k2js.translate.utils.AnnotationsUtils import org.jetbrains.k2js.PredefinedAnnotation +import org.jetbrains.jet.lang.resolve.calls.tasks.isDynamic +import org.jetbrains.jet.lang.psi.Call +import org.jetbrains.k2js.translate.operation.OperatorTable +import com.google.dart.compiler.backend.js.ast.JsBinaryOperation +import org.jetbrains.jet.lang.psi.JetPrefixExpression +import com.google.dart.compiler.backend.js.ast.JsPrefixOperation +import org.jetbrains.jet.lang.psi.JetPostfixExpression +import com.google.dart.compiler.backend.js.ast.JsPostfixOperation +import org.jetbrains.jet.lang.psi.JetBinaryExpression +import org.jetbrains.jet.lexer.JetTokens +import org.jetbrains.jet.lang.psi.JetOperationExpression +import org.jetbrains.k2js.translate.utils.PsiUtils public fun addReceiverToArgs(receiver: JsExpression, arguments: List): List { if (arguments.isEmpty()) @@ -230,6 +242,64 @@ object SuperCallCase : FunctionCallCase { } } +object DynamicInvokeAndBracketAccessCallCase : FunctionCallCase { + fun canApply(callInfo: FunctionCallInfo): Boolean = + callInfo.resolvedCall.getCall().getCallType() != Call.CallType.DEFAULT && callInfo.callableDescriptor.isDynamic() + + override fun FunctionCallInfo.dispatchReceiver(): JsExpression { + val arguments = argumentsInfo.getTranslateArguments() + val callType = resolvedCall.getCall().getCallType() + return when (callType) { + Call.CallType.INVOKE -> + JsInvocation(dispatchReceiver, arguments) + Call.CallType.ARRAY_GET_METHOD -> + JsArrayAccess(dispatchReceiver, arguments[0]) + Call.CallType.ARRAY_SET_METHOD -> + JsAstUtils.assignment(JsArrayAccess(dispatchReceiver, arguments[0]), arguments[1]) + + else -> + unsupported("Unsupported call type: $callType, callInfo: $this") + } + } +} + +object DynamicOperatorCallCase : FunctionCallCase { + fun canApply(callInfo: FunctionCallInfo): Boolean = + callInfo.callableDescriptor.isDynamic() && + callInfo.resolvedCall.getCall().getCallElement() let { + it is JetOperationExpression && + PsiUtils.getOperationToken(it) let { (it == JetTokens.NOT_IN || OperatorTable.hasCorrespondingOperator(it)) } + } + + override fun FunctionCallInfo.dispatchReceiver(): JsExpression { + val callElement = resolvedCall.getCall().getCallElement() as JetOperationExpression + val operationToken = PsiUtils.getOperationToken(callElement) + + val arguments = argumentsInfo.getTranslateArguments() + + return when (callElement) { + is JetBinaryExpression -> { + // `!in` translated as `in` and will be wrapped by negation operation in BinaryOperationTranslator#translateAsOverloadedBinaryOperation by mayBeWrapWithNegation + val operationTokenToFind = if (operationToken == JetTokens.NOT_IN) JetTokens.IN_KEYWORD else operationToken + val binaryOperator = OperatorTable.getBinaryOperator(operationTokenToFind) + + if (operationTokenToFind == JetTokens.IN_KEYWORD) + JsBinaryOperation(binaryOperator, arguments[0], dispatchReceiver) + else + JsBinaryOperation(binaryOperator, dispatchReceiver, arguments[0]) + } + is JetPrefixExpression -> { + JsPrefixOperation(OperatorTable.getUnaryOperator(operationToken), dispatchReceiver) + } + is JetPostfixExpression -> { + // TODO drop hack with ":JsExpression" when KT-5569 will be fixed + JsPostfixOperation(OperatorTable.getUnaryOperator(operationToken), dispatchReceiver): JsExpression + } + else -> unsupported("Unsupported callElement type: ${callElement.javaClass}, callElement: $callElement, callInfo: $this") + } + } +} + fun FunctionCallInfo.translateFunctionCall(): JsExpression { val intrinsic = DelegateFunctionIntrinsic.intrinsic(this) @@ -250,6 +320,12 @@ fun FunctionCallInfo.translateFunctionCall(): JsExpression { ConstructorCallCase.translate(this) SuperCallCase.canApply(this) -> SuperCallCase.translate(this) + + DynamicInvokeAndBracketAccessCallCase.canApply(this) -> + DynamicInvokeAndBracketAccessCallCase.translate(this) + DynamicOperatorCallCase.canApply(this) -> + DynamicOperatorCallCase.translate(this) + else -> DefaultFunctionCallCase.translate(this) } diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/context/StaticContext.java b/js/js.translator/src/org/jetbrains/k2js/translate/context/StaticContext.java index d414dedb6bc..3f78cffe5c8 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/context/StaticContext.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/context/StaticContext.java @@ -27,6 +27,7 @@ import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.reflect.ReflectionTypes; import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.DescriptorUtils; +import org.jetbrains.jet.lang.resolve.calls.tasks.TasksPackage; import org.jetbrains.jet.lang.resolve.name.FqName; import org.jetbrains.k2js.config.Config; import org.jetbrains.k2js.config.EcmaVersion; @@ -229,6 +230,21 @@ public final class StaticContext { private final class NameGenerator extends Generator { public NameGenerator() { + Rule namesForDynamic = new Rule() { + @Override + @Nullable + public JsName apply(@NotNull DeclarationDescriptor descriptor) { + if (TasksPackage.isDynamic(descriptor)) { + String name = descriptor.getName().asString(); + JsScope scope = getEnclosingScope(descriptor); + assert scope instanceof JsFunctionScope; + return ((JsFunctionScope) scope).declareNameUnsafe(name); + } + + return null; + } + }; + Rule namesForStandardClasses = new Rule() { @Override @Nullable @@ -332,6 +348,8 @@ public final class StaticContext { return result; } }; + + addRule(namesForDynamic); addRule(namesForStandardClasses); addRule(constructorOrClassObjectHasTheSameNameAsTheClass); addRule(propertyOrPropertyAccessor); diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/operation/CompareToBOIF.kt b/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/operation/CompareToBOIF.kt index 9860dc40f9b..1651b5b81b9 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/operation/CompareToBOIF.kt +++ b/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/operation/CompareToBOIF.kt @@ -28,6 +28,7 @@ import org.jetbrains.k2js.translate.operation.OperatorTable import org.jetbrains.k2js.translate.utils.JsAstUtils import org.jetbrains.k2js.translate.utils.JsDescriptorUtils import org.jetbrains.k2js.translate.utils.PsiUtils.getOperationToken +import org.jetbrains.jet.lang.resolve.calls.tasks.isDynamic object CompareToBOIF : BinaryOperationIntrinsicFactory { @@ -67,6 +68,8 @@ object CompareToBOIF : BinaryOperationIntrinsicFactory { override public fun getSupportTokens(): ImmutableSet = OperatorConventions.COMPARISON_OPERATIONS override public fun getIntrinsic(descriptor: FunctionDescriptor): BinaryOperationIntrinsic? { + if (descriptor.isDynamic()) return CompareToIntrinsic + if (!JsDescriptorUtils.isBuiltin(descriptor)) return null return when { diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/operation/EqualsBOIF.kt b/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/operation/EqualsBOIF.kt index 261e6611111..8cb3f784401 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/operation/EqualsBOIF.kt +++ b/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/operation/EqualsBOIF.kt @@ -34,6 +34,8 @@ import org.jetbrains.jet.lexer.JetToken import org.jetbrains.jet.lexer.JetTokens import com.google.common.collect.ImmutableSet import org.jetbrains.k2js.translate.intrinsic.functions.patterns.PatternBuilder.pattern +import org.jetbrains.jet.lang.resolve.calls.callUtil.getResolvedCall +import org.jetbrains.jet.lang.types.isDynamic object EqualsBOIF : BinaryOperationIntrinsicFactory { @@ -57,6 +59,17 @@ object EqualsBOIF : BinaryOperationIntrinsicFactory { return JsBinaryOperation(if (isNegated) JsBinaryOperator.REF_NEQ else JsBinaryOperator.REF_EQ, left, right) } + val resolvedCall = expression.getResolvedCall(context.bindingContext()) + val appliedToDynamic = + resolvedCall != null && + with(resolvedCall.getDispatchReceiver()) { + if (exists()) getType().isDynamic() else false + } + + if (appliedToDynamic) { + return JsBinaryOperation(if (isNegated) JsBinaryOperator.NEQ else JsBinaryOperator.EQ, left, right) + } + val result = TopLevelFIF.KOTLIN_EQUALS.apply(left, Arrays.asList(right), context) return if (isNegated) JsAstUtils.negated(result) else result } diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/operation/BinaryOperationTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/operation/BinaryOperationTranslator.java index c64e7ae7c2a..824d3549ae5 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/operation/BinaryOperationTranslator.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/operation/BinaryOperationTranslator.java @@ -209,6 +209,10 @@ public final class BinaryOperationTranslator extends AbstractTranslator { if (rightBlock.isEmpty()) { return new JsBinaryOperation(operator, leftExpression, rightExpression); } + else if (OperatorConventions.IDENTITY_EQUALS_OPERATIONS.contains(operationToken)) { + context().addStatementsToCurrentBlockFrom(rightBlock); + return new JsBinaryOperation(operator, leftExpression, rightExpression); + } assert operationToken.equals(JetTokens.ANDAND) || operationToken.equals(JetTokens.OROR) : "Unsupported binary operation: " + expression.getText(); boolean isOror = operationToken.equals(JetTokens.OROR); diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/operation/IncrementTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/operation/IncrementTranslator.java index 68dd07727b0..5550ac5fcc1 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/operation/IncrementTranslator.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/operation/IncrementTranslator.java @@ -23,8 +23,10 @@ import com.google.dart.compiler.backend.js.ast.JsExpression; import com.google.dart.compiler.util.AstUtil; import com.intellij.psi.tree.IElementType; import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.descriptors.CallableDescriptor; import org.jetbrains.jet.lang.psi.JetExpression; import org.jetbrains.jet.lang.psi.JetUnaryExpression; +import org.jetbrains.jet.lang.resolve.calls.tasks.TasksPackage; import org.jetbrains.jet.lang.types.expressions.OperatorConventions; import org.jetbrains.k2js.translate.context.TemporaryVariable; import org.jetbrains.k2js.translate.context.TranslationContext; @@ -34,8 +36,10 @@ import org.jetbrains.k2js.translate.reference.CachedAccessTranslator; import java.util.List; import static org.jetbrains.k2js.translate.reference.AccessTranslationUtils.getCachedAccessTranslator; +import static org.jetbrains.k2js.translate.utils.BindingUtils.getCallableDescriptorForOperationExpression; import static org.jetbrains.k2js.translate.utils.JsAstUtils.newSequence; -import static org.jetbrains.k2js.translate.utils.PsiUtils.*; +import static org.jetbrains.k2js.translate.utils.PsiUtils.getBaseExpression; +import static org.jetbrains.k2js.translate.utils.PsiUtils.isPrefix; import static org.jetbrains.k2js.translate.utils.TemporariesUtils.temporariesInitialization; import static org.jetbrains.k2js.translate.utils.TranslationUtils.hasCorrespondingFunctionIntrinsic; @@ -50,7 +54,7 @@ public abstract class IncrementTranslator extends AbstractTranslator { @NotNull public static JsExpression translate(@NotNull JetUnaryExpression expression, @NotNull TranslationContext context) { - if (hasCorrespondingFunctionIntrinsic(context, expression)) { + if (hasCorrespondingFunctionIntrinsic(context, expression) || isDynamic(context, expression)) { return IntrinsicIncrementTranslator.doTranslate(expression, context); } return (new OverloadedIncrementTranslator(expression, context)).translateIncrementExpression(); @@ -121,4 +125,9 @@ public abstract class IncrementTranslator extends AbstractTranslator { @NotNull abstract JsExpression operationExpression(@NotNull JsExpression receiver); + + private static boolean isDynamic(TranslationContext context, JetUnaryExpression expression) { + CallableDescriptor operationDescriptor = getCallableDescriptorForOperationExpression(context.bindingContext(), expression); + return TasksPackage.isDynamic(operationDescriptor); + } } diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/operation/OperatorTable.java b/js/js.translator/src/org/jetbrains/k2js/translate/operation/OperatorTable.java index daf878a7c34..5c1d0864997 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/operation/OperatorTable.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/operation/OperatorTable.java @@ -46,6 +46,9 @@ public final class OperatorTable { .put(JetTokens.DIVEQ, JsBinaryOperator.ASG_DIV) .put(JetTokens.MULTEQ, JsBinaryOperator.ASG_MUL) .put(JetTokens.PERCEQ, JsBinaryOperator.ASG_MOD) + .put(JetTokens.IN_KEYWORD, JsBinaryOperator.INOP) + .put(JetTokens.EQEQEQ, JsBinaryOperator.REF_EQ) + .put(JetTokens.EXCLEQEQEQ, JsBinaryOperator.REF_NEQ) .build(); private static final ImmutableBiMap unaryOperatorsMap = ImmutableBiMap.builder() @@ -59,6 +62,9 @@ public final class OperatorTable { private OperatorTable() { } + public static boolean hasCorrespondingOperator(@NotNull JetToken token) { + return binaryOperatorsMap.containsKey(token) || unaryOperatorsMap.containsKey(token); + } public static boolean hasCorrespondingBinaryOperator(@NotNull JetToken token) { return binaryOperatorsMap.containsKey(token); diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/utils/PsiUtils.java b/js/js.translator/src/org/jetbrains/k2js/translate/utils/PsiUtils.java index 9cd77a57547..f224d8c0df2 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/utils/PsiUtils.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/utils/PsiUtils.java @@ -71,7 +71,7 @@ public final class PsiUtils { public static JetToken getOperationToken(@NotNull JetOperationExpression expression) { JetSimpleNameExpression operationExpression = expression.getOperationReference(); IElementType elementType = operationExpression.getReferencedNameElementType(); - assert elementType instanceof JetToken : "Unary expression should have operation token of type JetToken"; + assert elementType instanceof JetToken : "Expected JetToken type, but " + elementType.getClass() + ", expression: " + expression.getText(); return (JetToken) elementType; }