JS: prototyping coroutines

This commit is contained in:
Alexey Andreev
2016-10-28 20:15:13 +03:00
parent be8b1b7dfd
commit c5999e8375
81 changed files with 1037 additions and 606 deletions
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.descriptors.ModuleDescriptor;
import org.jetbrains.kotlin.js.analyze.TopDownAnalyzerFacadeForJS;
import org.jetbrains.kotlin.js.analyzer.JsAnalysisResult;
import org.jetbrains.kotlin.js.config.JsConfig;
import org.jetbrains.kotlin.js.coroutine.CoroutineTransformer;
import org.jetbrains.kotlin.js.facade.exceptions.TranslationException;
import org.jetbrains.kotlin.js.inline.JsInliner;
import org.jetbrains.kotlin.js.translate.context.TranslationContext;
@@ -85,6 +86,11 @@ public final class K2JSTranslator {
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled();
if (hasError(diagnostics)) return new TranslationResult.Fail(diagnostics);
CoroutineTransformer coroutineTransformer = new CoroutineTransformer(program);
coroutineTransformer.accept(program);
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled();
if (hasError(diagnostics)) return new TranslationResult.Fail(diagnostics);
expandIsCalls(program, context);
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled();
@@ -17,6 +17,9 @@
package org.jetbrains.kotlin.js.translate.callTranslator
import com.google.dart.compiler.backend.js.ast.JsExpression
import com.google.dart.compiler.backend.js.ast.JsFunction
import com.google.dart.compiler.backend.js.ast.JsInvocation
import com.google.dart.compiler.backend.js.ast.metadata.isSuspend
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.VariableDescriptor
@@ -115,7 +118,11 @@ private fun translateFunctionCall(context: TranslationContext,
resolvedCall: ResolvedCall<out FunctionDescriptor>,
explicitReceivers: ExplicitReceivers
): JsExpression {
return context.getCallInfo(resolvedCall, explicitReceivers).translateFunctionCall()
val callExpression = context.getCallInfo(resolvedCall, explicitReceivers).translateFunctionCall()
if (resolvedCall.resultingDescriptor.isSuspend) {
(callExpression as JsInvocation).isSuspend = true
}
return callExpression
}
fun computeExplicitReceiversForInvoke(
@@ -26,6 +26,7 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
import org.jetbrains.kotlin.descriptors.annotations.KotlinRetention;
import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator;
import org.jetbrains.kotlin.js.translate.context.Namer;
import org.jetbrains.kotlin.js.translate.context.TranslationContext;
import org.jetbrains.kotlin.js.translate.declaration.ClassTranslator;
@@ -37,6 +38,7 @@ import org.jetbrains.kotlin.js.translate.operation.UnaryOperationTranslator;
import org.jetbrains.kotlin.js.translate.reference.*;
import org.jetbrains.kotlin.js.translate.utils.BindingUtils;
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils;
import org.jetbrains.kotlin.js.translate.utils.UtilsKt;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
@@ -44,6 +46,7 @@ import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.BindingContextUtils;
import org.jetbrains.kotlin.resolve.bindingContextUtil.BindingContextUtilsKt;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument;
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator;
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
@@ -51,7 +54,9 @@ import org.jetbrains.kotlin.resolve.inline.InlineUtil;
import org.jetbrains.kotlin.types.expressions.DoubleColonLHS;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.jetbrains.kotlin.js.translate.context.Namer.GET_KCLASS;
import static org.jetbrains.kotlin.js.translate.context.Namer.GET_KCLASS_FROM_EXPRESSION;
@@ -129,7 +134,11 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
}
JsReturn jsReturn;
if (returned == null) {
JsExpression handleResultExpression = TranslationUtils.tryTranslateHandleResult(context, jetReturnExpression, returned);
if (handleResultExpression != null) {
jsReturn = new JsReturn(handleResultExpression);
}
else if (returned == null) {
jsReturn = new JsReturn(null);
}
else {
@@ -445,13 +454,13 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
@Override
@NotNull
public JsNode visitLambdaExpression(@NotNull KtLambdaExpression expression, @NotNull TranslationContext context) {
return new LiteralFunctionTranslator(context).translate(expression.getFunctionLiteral());
return new LiteralFunctionTranslator(context).translate(expression.getFunctionLiteral(), null, null);
}
@Override
@NotNull
public JsNode visitNamedFunction(@NotNull KtNamedFunction expression, @NotNull TranslationContext context) {
JsExpression alias = new LiteralFunctionTranslator(context).translate(expression);
JsExpression alias = new LiteralFunctionTranslator(context).translate(expression, null, null);
FunctionDescriptor descriptor = getFunctionDescriptor(context.bindingContext(), expression);
JsName name = context.getNameForDescriptor(descriptor);
@@ -36,7 +36,11 @@ import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.inline.InlineUtil
class LiteralFunctionTranslator(context: TranslationContext) : AbstractTranslator(context) {
fun translate(declaration: KtDeclarationWithBody): JsExpression {
fun translate(
declaration: KtDeclarationWithBody,
continuationType: ClassDescriptor? = null,
controllerType: ClassDescriptor? = null
): JsExpression {
val invokingContext = context()
val descriptor = getFunctionDescriptor(invokingContext.bindingContext(), declaration)
@@ -69,6 +73,8 @@ class LiteralFunctionTranslator(context: TranslationContext) : AbstractTranslato
val lambdaCreator = simpleReturnFunction(invokingContext.scope(), lambda)
lambdaCreator.name = invokingContext.getInnerNameForDescriptor(descriptor)
lambdaCreator.isLocal = true
lambdaCreator.coroutineType = continuationType
lambdaCreator.controllerType = controllerType
if (!isRecursive) {
lambda.name = null
}
@@ -77,6 +83,8 @@ class LiteralFunctionTranslator(context: TranslationContext) : AbstractTranslato
}
lambda.isLocal = true
lambda.coroutineType = continuationType
lambda.controllerType = controllerType
invokingContext.addDeclarationStatement(lambda.makeStmt())
lambda.name.staticRef = lambda
@@ -56,7 +56,7 @@ import static org.jetbrains.kotlin.js.descriptorUtils.DescriptorUtilsKt.getNameI
import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getTypeByReference;
import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getTypeForExpression;
import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.equality;
import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.negated;
import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.not;
import static org.jetbrains.kotlin.psi.KtPsiUtil.findChildByType;
import static org.jetbrains.kotlin.types.TypeUtils.*;
@@ -112,7 +112,7 @@ public final class PatternTranslator extends AbstractTranslator {
if (result == null) return JsLiteral.getBoolean(!expression.isNegated());
if (expression.isNegated()) {
return negated(result);
return not(result);
}
return result;
}
@@ -33,7 +33,7 @@ import org.jetbrains.kotlin.types.KotlinType;
import java.util.HashMap;
import java.util.Map;
import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.negated;
import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.not;
public final class WhenTranslator extends AbstractTranslator {
@Nullable
@@ -141,7 +141,7 @@ public final class WhenTranslator extends AbstractTranslator {
private JsExpression translateCondition(@NotNull KtWhenCondition condition, @NotNull TranslationContext context) {
JsExpression patternMatchExpression = translateWhenConditionToBooleanExpression(condition, context);
if (isNegated(condition)) {
return negated(patternMatchExpression);
return not(patternMatchExpression);
}
return patternMatchExpression;
}
@@ -62,7 +62,7 @@ object EqualsBOIF : BinaryOperationIntrinsicFactory {
}
val result = TopLevelFIF.KOTLIN_EQUALS.apply(left, Arrays.asList<JsExpression>(right), context)
return if (isNegated) JsAstUtils.negated(result) else result
return if (isNegated) JsAstUtils.not(result) else result
}
private fun canUseSimpleEquals(expression: KtBinaryExpression, context: TranslationContext): Boolean {
@@ -71,7 +71,7 @@ public class InOperationTranslator extends AbstractTranslator {
private JsExpression translateGeneral(@NotNull ResolvedCall<? extends FunctionDescriptor> call, @NotNull JsExpression rightTranslated) {
JsExpression result = CallTranslator.translate(context(), call, rightTranslated);
if (negated) {
result = JsAstUtils.negated(result);
result = JsAstUtils.not(result);
}
return result;
}
@@ -20,10 +20,13 @@ import com.google.dart.compiler.backend.js.ast.*
import com.google.dart.compiler.backend.js.ast.metadata.SideEffectKind
import com.google.dart.compiler.backend.js.ast.metadata.sideEffects
import com.intellij.util.SmartList
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.js.translate.context.Namer
import org.jetbrains.kotlin.js.translate.context.TemporaryConstVariable
import org.jetbrains.kotlin.js.translate.context.TranslationContext
import org.jetbrains.kotlin.js.translate.expression.LiteralFunctionTranslator
import org.jetbrains.kotlin.js.translate.expression.PatternTranslator
import org.jetbrains.kotlin.js.translate.general.AbstractTranslator
import org.jetbrains.kotlin.js.translate.general.Translation
@@ -31,6 +34,8 @@ import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils
import org.jetbrains.kotlin.js.translate.utils.getReferenceToJsClass
import org.jetbrains.kotlin.psi.KtLambdaExpression
import org.jetbrains.kotlin.psi.KtPsiUtil
import org.jetbrains.kotlin.psi.ValueArgument
import org.jetbrains.kotlin.resolve.calls.model.*
import org.jetbrains.kotlin.types.KotlinType
@@ -119,7 +124,7 @@ class CallArgumentTranslator private constructor(
}
}
else {
translateSingleArgument(actualArgument, result, argContext)
translateSingleArgument(parameterDescriptor, actualArgument, result, argContext)
}
context().moveVarsFrom(argContext)
@@ -178,8 +183,12 @@ class CallArgumentTranslator private constructor(
return result
}
private fun translateSingleArgument(actualArgument: ResolvedValueArgument, result: MutableList<JsExpression>,
context: TranslationContext) {
private fun translateSingleArgument(
parameterDescriptor: ValueParameterDescriptor,
actualArgument: ResolvedValueArgument,
result: MutableList<JsExpression>,
context: TranslationContext
) {
val valueArguments = actualArgument.arguments
if (actualArgument is DefaultValueArgument) {
@@ -190,10 +199,19 @@ class CallArgumentTranslator private constructor(
assert(actualArgument is ExpressionValueArgument)
assert(valueArguments.size == 1)
val argumentExpression = valueArguments[0].getArgumentExpression()!!
val argumentExpression = KtPsiUtil.deparenthesize(valueArguments[0].getArgumentExpression())!!
val jsExpression = Translation.translateAsExpression(argumentExpression, context)
result.add(jsExpression)
result += if (parameterDescriptor.isCoroutine && argumentExpression is KtLambdaExpression) {
val continuationType = parameterDescriptor.type.arguments.last().type
val continuationDescriptor = continuationType.constructor.declarationDescriptor as ClassDescriptor
val controllerType = parameterDescriptor.type.arguments[0].type
val controllerDescriptor = controllerType.constructor.declarationDescriptor as ClassDescriptor
LiteralFunctionTranslator(context).translate(
argumentExpression.functionLiteral, continuationDescriptor, controllerDescriptor)
}
else {
Translation.translateAsExpression(argumentExpression, context)
}
}
private fun translateVarargArgument(arguments: List<ValueArgument>, result: MutableList<JsExpression>,
@@ -243,7 +261,7 @@ class CallArgumentTranslator private constructor(
}
private fun concatArgumentsIfNeeded(concatArguments: List<JsExpression>): JsExpression {
assert(concatArguments.size > 0) { "concatArguments.size should not be 0" }
assert(concatArguments.isNotEmpty()) { "concatArguments.size should not be 0" }
if (concatArguments.size > 1) {
return JsInvocation(JsNameRef("concat", concatArguments[0]), concatArguments.subList(1, concatArguments.size))
@@ -255,7 +273,7 @@ class CallArgumentTranslator private constructor(
}
private fun prepareConcatArguments(arguments: List<ValueArgument>, list: List<JsExpression>): MutableList<JsExpression> {
assert(arguments.size != 0) { "arguments.size should not be 0" }
assert(arguments.isNotEmpty()) { "arguments.size should not be 0" }
assert(arguments.size == list.size) { "arguments.size: " + arguments.size + " != list.size: " + list.size }
val concatArguments = SmartList<JsExpression>()
@@ -94,7 +94,19 @@ public final class FunctionBodyTranslator extends AbstractTranslator {
jsBlock.getStatements().addAll(setDefaultValueForArguments(descriptor, context()));
}
jsBlock.getStatements().addAll(mayBeWrapWithReturn(Translation.translateExpression(jetBodyExpression, context(), jsBlock)).getStatements());
KotlinType returnType = descriptor.getReturnType();
assert returnType != null;
TranslationContext handleResultContext = context().innerBlock(jsBlock);
JsExpression handleResultExpr = TranslationUtils.tryTranslateHandleResult(handleResultContext, declaration, jetBodyExpression);
if (handleResultExpr != null) {
jsBlock.getStatements().add(new JsReturn(handleResultExpr));
}
else {
JsNode jsBody = Translation.translateExpression(jetBodyExpression, context(), jsBlock);
jsBlock.getStatements().addAll(mayBeWrapWithReturn(jsBody).getStatements());
}
return jsBlock;
}
@@ -208,12 +208,7 @@ public final class JsAstUtils {
}
@NotNull
public static JsPrefixOperation negated(@NotNull JsExpression expression) {
return new JsPrefixOperation(JsUnaryOperator.NOT, expression);
}
@NotNull
public static JsExpression negatedOptimized(@NotNull JsExpression expression) {
public static JsExpression notOptimized(@NotNull JsExpression expression) {
if (expression instanceof JsUnaryOperation) {
JsUnaryOperation unary = (JsUnaryOperation) expression;
if (unary.getOperator() == JsUnaryOperator.NOT) return unary.getArg();
@@ -222,9 +217,9 @@ public final class JsAstUtils {
JsBinaryOperation binary = (JsBinaryOperation) expression;
switch (binary.getOperator()) {
case AND:
return or(negatedOptimized(binary.getArg1()), negatedOptimized(binary.getArg2()));
return or(notOptimized(binary.getArg1()), notOptimized(binary.getArg2()));
case OR:
return and(negatedOptimized(binary.getArg1()), negatedOptimized(binary.getArg2()));
return and(notOptimized(binary.getArg1()), notOptimized(binary.getArg2()));
case EQ:
return new JsBinaryOperation(JsBinaryOperator.NEQ, binary.getArg1(), binary.getArg2());
case NEQ:
@@ -246,7 +241,7 @@ public final class JsAstUtils {
}
}
return negated(expression);
return not(expression);
}
@NotNull
@@ -18,23 +18,30 @@ package org.jetbrains.kotlin.js.translate.utils;
import com.google.dart.compiler.backend.js.ast.*;
import com.google.dart.compiler.backend.js.ast.JsBinaryOperator;
import com.google.dart.compiler.backend.js.ast.metadata.MetadataProperties;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.impl.LocalVariableAccessorDescriptor;
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator;
import org.jetbrains.kotlin.js.translate.context.Namer;
import org.jetbrains.kotlin.js.translate.context.TemporaryConstVariable;
import org.jetbrains.kotlin.js.translate.context.TranslationContext;
import org.jetbrains.kotlin.js.translate.general.Translation;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.DescriptorUtils;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument;
import org.jetbrains.kotlin.types.KotlinType;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static com.google.dart.compiler.backend.js.ast.JsBinaryOperator.*;
import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getCallableDescriptorForOperationExpression;
@@ -289,4 +296,38 @@ public final class TranslationUtils {
}
return false;
}
@Nullable
public static JsExpression tryTranslateHandleResult(
@NotNull TranslationContext context,
@NotNull KtExpression expression,
@Nullable KtExpression returnExpression
) {
ResolvedCall<? extends FunctionDescriptor> returnCall = context.bindingContext().get(
BindingContext.RETURN_HANDLE_RESULT_RESOLVED_CALL, expression);
if (returnCall == null) return null;
Map<KtExpression, JsExpression> aliases = new HashMap<KtExpression, JsExpression>();
List<ResolvedValueArgument> arguments = returnCall.getValueArgumentsByIndex();
assert arguments != null : "Arguments should be defined here: " + PsiUtilsKt.getTextWithLocation(expression);
KotlinType returnType = returnCall.getResultingDescriptor().getValueParameters().get(0).getType();
ValueArgument returnValueArgument = arguments.get(0).getArguments().get(0);
if (returnExpression != null) {
aliases.put(returnValueArgument.getArgumentExpression(), Translation.translateAsExpression(returnExpression, context));
}
else if (KotlinBuiltIns.isUnit(returnType)) {
aliases.put(returnValueArgument.getArgumentExpression(), JsLiteral.NULL);
}
ValueArgument continuationArgument = arguments.get(1).getArguments().get(0);
aliases.put(continuationArgument.getArgumentExpression(), JsLiteral.THIS);
TranslationContext returnContext = context.innerContextWithAliasesForExpressions(aliases);
JsInvocation handleResultInvocation = (JsInvocation) CallTranslator.translate(returnContext, returnCall, JsLiteral.THIS);
MetadataProperties.setHandleResult(handleResultInvocation, true);
return handleResultInvocation;
}
}