JS backend: added the support dynamic.

This commit is contained in:
Zalim Bashorov
2014-12-09 22:06:01 +03:00
parent 45b0fd9f4c
commit bc3f53afff
9 changed files with 133 additions and 4 deletions
@@ -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()
@@ -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<JsExpression>): List<JsExpression> {
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)
}
@@ -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<JsName> {
public NameGenerator() {
Rule<JsName> namesForDynamic = new Rule<JsName>() {
@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<JsName> namesForStandardClasses = new Rule<JsName>() {
@Override
@Nullable
@@ -332,6 +348,8 @@ public final class StaticContext {
return result;
}
};
addRule(namesForDynamic);
addRule(namesForStandardClasses);
addRule(constructorOrClassObjectHasTheSameNameAsTheClass);
addRule(propertyOrPropertyAccessor);
@@ -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<JetToken> = OperatorConventions.COMPARISON_OPERATIONS
override public fun getIntrinsic(descriptor: FunctionDescriptor): BinaryOperationIntrinsic? {
if (descriptor.isDynamic()) return CompareToIntrinsic
if (!JsDescriptorUtils.isBuiltin(descriptor)) return null
return when {
@@ -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<JsExpression>(right), context)
return if (isNegated) JsAstUtils.negated(result) else result
}
@@ -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);
@@ -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);
}
}
@@ -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<JetToken, JsUnaryOperator> unaryOperatorsMap = ImmutableBiMap.<JetToken, JsUnaryOperator>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);
@@ -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;
}