All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.kotlin.js.translate.reference.CallArgumentTranslator.kt Maven / Gradle / Ivy

There is a newer version: 2.0.0
Show newest version
/*
 * Copyright 2010-2015 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jetbrains.kotlin.js.translate.reference

import com.google.dart.compiler.backend.js.ast.*
import com.intellij.util.SmartList
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.js.descriptorUtils.nameIfStandardType
import org.jetbrains.kotlin.js.translate.context.TemporaryConstVariable
import org.jetbrains.kotlin.js.translate.context.TemporaryVariable
import org.jetbrains.kotlin.js.translate.context.TranslationContext
import org.jetbrains.kotlin.js.translate.expression.PatternTranslator
import org.jetbrains.kotlin.js.translate.general.AbstractTranslator
import org.jetbrains.kotlin.js.translate.general.Translation
import org.jetbrains.kotlin.js.translate.utils.*
import org.jetbrains.kotlin.psi.JetExpression
import org.jetbrains.kotlin.psi.ValueArgument
import org.jetbrains.kotlin.resolve.calls.model.*
import org.jetbrains.kotlin.types.JetType

import java.util.ArrayList
import java.util.Collections

public class CallArgumentTranslator private constructor(
        private val resolvedCall: ResolvedCall<*>,
        private val receiver: JsExpression?,
        context: TranslationContext
) : AbstractTranslator(context) {

    public data class ArgumentsInfo(
            public val valueArguments: List,
            public val hasSpreadOperator: Boolean,
            public val cachedReceiver: TemporaryConstVariable?,
            public val reifiedArguments: List = listOf()
    ) {
        public val translateArguments: List
            get() = reifiedArguments + valueArguments
    }

    private enum class ArgumentsKind {
        HAS_EMPTY_EXPRESSION_ARGUMENT,
        HAS_NOT_EMPTY_EXPRESSION_ARGUMENT
    }

    private val isNativeFunctionCall = AnnotationsUtils.isNativeObject(resolvedCall.getCandidateDescriptor())

    private fun removeLastUndefinedArguments(result: MutableList) {
        var i = result.size() - 1

        while (i >= 0) {
            if (result.get(i) != context().namer().getUndefinedExpression()) {
                break
            }
            i--
        }

        result.subList(i + 1, result.size()).clear()
    }

    private fun translate(): ArgumentsInfo {
        val valueParameters = resolvedCall.getResultingDescriptor().getValueParameters()
        if (valueParameters.isEmpty()) {
            return ArgumentsInfo(listOf(), false, null)
        }
        var hasSpreadOperator = false
        var cachedReceiver: TemporaryConstVariable? = null

        var result: MutableList = ArrayList(valueParameters.size())
        val valueArgumentsByIndex = resolvedCall.getValueArgumentsByIndex()
        if (valueArgumentsByIndex == null) {
            throw IllegalStateException("Failed to arrange value arguments by index: " + resolvedCall.getResultingDescriptor())
        }
        var argsBeforeVararg: List? = null
        var argumentsShouldBeExtractedToTmpVars = false
        val argContexts = SmartList()
        var kind = ArgumentsKind.HAS_NOT_EMPTY_EXPRESSION_ARGUMENT
        var concatArguments: MutableList? = null

        for (parameterDescriptor in valueParameters) {
            val actualArgument = valueArgumentsByIndex.get(parameterDescriptor.getIndex())

            val argContext = context().innerBlock()

            if (actualArgument is VarargValueArgument) {

                val arguments = actualArgument.getArguments()

                val size = arguments.size()
                var i = 0
                while (i != size) {
                    if (arguments.get(i).getSpreadElement() != null) {
                        hasSpreadOperator = true
                        break
                    }
                    ++i
                }

                if (hasSpreadOperator) {
                    if (isNativeFunctionCall) {
                        argsBeforeVararg = result
                        result = SmartList()
                        val list = SmartList()
                        kind = translateValueArguments(arguments, list, argContext)
                        concatArguments = prepareConcatArguments(arguments, list)
                    }
                    else {
                        kind = translateVarargArgument(arguments, result, argContext, size > 1)
                    }
                }
                else {
                    kind = translateVarargArgument(arguments, result, argContext, !isNativeFunctionCall)
                }
            }
            else {
                kind = translateSingleArgument(actualArgument, result, argContext)
            }

            context().moveVarsFrom(argContext)
            argContexts.add(argContext)
            argumentsShouldBeExtractedToTmpVars = argumentsShouldBeExtractedToTmpVars || !argContext.currentBlockIsEmpty()

            if (kind == ArgumentsKind.HAS_EMPTY_EXPRESSION_ARGUMENT) break
        }

        if (argumentsShouldBeExtractedToTmpVars) {
            extractArguments(result, argContexts, context(), kind == ArgumentsKind.HAS_NOT_EMPTY_EXPRESSION_ARGUMENT)
        }

        if (isNativeFunctionCall && hasSpreadOperator) {
            assert(argsBeforeVararg != null, "argsBeforeVararg should not be null")
            assert(concatArguments != null, "concatArguments should not be null")

            concatArguments!!.addAll(result)

            if (!argsBeforeVararg!!.isEmpty()) {
                concatArguments.add(0, JsArrayLiteral(argsBeforeVararg))
            }

            result = SmartList(concatArgumentsIfNeeded(concatArguments))

            if (receiver != null) {
                cachedReceiver = context().getOrDeclareTemporaryConstVariable(receiver)
                result.add(0, cachedReceiver.reference())
            }
            else {
                result.add(0, JsLiteral.NULL)
            }
        }

        removeLastUndefinedArguments(result)
        return ArgumentsInfo(result, hasSpreadOperator, cachedReceiver)
    }

    companion object {

        @JvmStatic
        public fun translate(resolvedCall: ResolvedCall<*>, receiver: JsExpression?, context: TranslationContext): ArgumentsInfo {
            return translate(resolvedCall, receiver, context, context.dynamicContext().jsBlock())
        }

        @JvmStatic
        public fun translate(resolvedCall: ResolvedCall<*>, receiver: JsExpression?, context: TranslationContext, block: JsBlock): ArgumentsInfo {
            val innerContext = context.innerBlock(block)
            val argumentTranslator = CallArgumentTranslator(resolvedCall, receiver, innerContext)
            val result = argumentTranslator.translate()
            context.moveVarsFrom(innerContext)
            val callDescriptor = resolvedCall.getCandidateDescriptor()

            if (CallExpressionTranslator.shouldBeInlined(callDescriptor)) {
                val typeArgs = resolvedCall.getTypeArguments()
                return typeArgs.addReifiedTypeArgsTo(result, context)
            }

            return result;
        }

        private fun translateSingleArgument(actualArgument: ResolvedValueArgument, result: MutableList, context: TranslationContext): ArgumentsKind {
            val valueArguments = actualArgument.getArguments()

            if (actualArgument is DefaultValueArgument) {
                result.add(context.namer().getUndefinedExpression())
                return ArgumentsKind.HAS_NOT_EMPTY_EXPRESSION_ARGUMENT
            }

            assert(actualArgument is ExpressionValueArgument)
            assert(valueArguments.size() == 1)

            val argumentExpression = valueArguments.get(0).getArgumentExpression()
            assert(argumentExpression != null)
            argumentExpression!!

            val jsExpression = Translation.translateAsExpression(argumentExpression, context)
            result.add(jsExpression)

            if (JsAstUtils.isEmptyExpression(jsExpression)) {
                return ArgumentsKind.HAS_EMPTY_EXPRESSION_ARGUMENT
            }
            else {
                return ArgumentsKind.HAS_NOT_EMPTY_EXPRESSION_ARGUMENT
            }
        }

        private fun translateVarargArgument(arguments: List, result: MutableList, context: TranslationContext, shouldWrapVarargInArray: Boolean): ArgumentsKind {
            if (arguments.isEmpty()) {
                if (shouldWrapVarargInArray) {
                    result.add(JsArrayLiteral(listOf()))
                }
                return ArgumentsKind.HAS_NOT_EMPTY_EXPRESSION_ARGUMENT
            }

            val list: MutableList
            if (shouldWrapVarargInArray) {
                list = if (arguments.size() == 1) SmartList() else ArrayList(arguments.size())
            }
            else {
                list = result
            }

            val resultKind = translateValueArguments(arguments, list, context)

            if (shouldWrapVarargInArray) {
                val concatArguments = prepareConcatArguments(arguments, list)
                val concatExpression = concatArgumentsIfNeeded(concatArguments)
                result.add(concatExpression)
            }

            return resultKind
        }

        private fun translateValueArguments(arguments: List, list: MutableList, context: TranslationContext): ArgumentsKind {
            var resultKind = ArgumentsKind.HAS_NOT_EMPTY_EXPRESSION_ARGUMENT
            val argContexts = SmartList()
            var argumentsShouldBeExtractedToTmpVars = false
            for (argument in arguments) {
                val argumentExpression = argument.getArgumentExpression()
                assert(argumentExpression != null)
                argumentExpression!!
                val argContext = context.innerBlock()
                val argExpression = Translation.translateAsExpression(argumentExpression, argContext)
                list.add(argExpression)
                context.moveVarsFrom(argContext)
                argContexts.add(argContext)
                argumentsShouldBeExtractedToTmpVars = argumentsShouldBeExtractedToTmpVars || !argContext.currentBlockIsEmpty()
                if (JsAstUtils.isEmptyExpression(argExpression)) {
                    resultKind = ArgumentsKind.HAS_EMPTY_EXPRESSION_ARGUMENT
                    break
                }
            }
            if (argumentsShouldBeExtractedToTmpVars) {
                extractArguments(list, argContexts, context, resultKind == ArgumentsKind.HAS_NOT_EMPTY_EXPRESSION_ARGUMENT)
            }
            return resultKind
        }

        private fun concatArgumentsIfNeeded(concatArguments: List): JsExpression {
            assert(concatArguments.size() > 0, "concatArguments.size should not be 0")

            if (concatArguments.size() > 1) {
                return JsInvocation(JsNameRef("concat", concatArguments.get(0)), concatArguments.subList(1, concatArguments.size()))

            }
            else {
                return concatArguments.get(0)
            }
        }

        private fun prepareConcatArguments(arguments: List, list: List): MutableList {
            assert(arguments.size() != 0, "arguments.size should not be 0")
            assert(arguments.size() == list.size()) { "arguments.size: " + arguments.size() + " != list.size: " + list.size() }

            val concatArguments = SmartList()
            var lastArrayContent: MutableList = SmartList()

            val size = arguments.size()
            for (index in 0..size - 1) {
                val valueArgument = arguments.get(index)
                val expressionArgument = list.get(index)

                if (valueArgument.getSpreadElement() != null) {
                    if (lastArrayContent.size() > 0) {
                        concatArguments.add(JsArrayLiteral(lastArrayContent))
                        concatArguments.add(expressionArgument)
                        lastArrayContent = SmartList()
                    }
                    else {
                        concatArguments.add(expressionArgument)
                    }
                }
                else {
                    lastArrayContent.add(expressionArgument)
                }
            }
            if (lastArrayContent.size() > 0) {
                concatArguments.add(JsArrayLiteral(lastArrayContent))
            }

            return concatArguments
        }

        private fun extractArguments(argExpressions: MutableList, argContexts: List, context: TranslationContext, toTmpVars: Boolean) {
            for (i in argExpressions.indices) {
                val argContext = argContexts.get(i)
                val jsArgExpression = argExpressions.get(i)
                if (argContext.currentBlockIsEmpty() && TranslationUtils.isCacheNeeded(jsArgExpression)) {
                    if (toTmpVars) {
                        val temporaryVariable = context.declareTemporary(jsArgExpression)
                        context.addStatementToCurrentBlock(temporaryVariable.assignmentExpression().makeStmt())
                        argExpressions.set(i, temporaryVariable.reference())
                    }
                    else {
                        context.addStatementToCurrentBlock(jsArgExpression.makeStmt())
                    }
                }
                else {
                    context.addStatementsToCurrentBlockFrom(argContext)
                }
            }
        }
    }

}

private fun Map.addReifiedTypeArgsTo(
        info: CallArgumentTranslator.ArgumentsInfo,
        context: TranslationContext
): CallArgumentTranslator.ArgumentsInfo {

    val reifiedTypeArguments = SmartList()
    val patternTranslator = PatternTranslator.newInstance(context)

    for (param in keySet().sortBy { it.getIndex() }) {
        if (!param.isReified()) continue

        val argumentType = get(param)
        if (argumentType == null) continue

        val isCheckCallable = patternTranslator.getIsTypeCheckCallable(argumentType)
        reifiedTypeArguments.add(isCheckCallable)
    }

    return info.copy(reifiedArguments = reifiedTypeArguments)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy