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

.kotlin.kotlin-compiler.1.2.71.source-code.Context.kt Maven / Gradle / Ivy

There is a newer version: 2.0.0
Show newest version
/*
 * Copyright 2010-2017 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.dce

import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.js.backend.ast.metadata.SpecialFunction
import org.jetbrains.kotlin.js.backend.ast.metadata.specialFunction
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
import org.jetbrains.kotlin.js.translate.utils.jsAstUtils.array
import org.jetbrains.kotlin.js.translate.utils.jsAstUtils.index

class Context {
    val globalScope = Node()
    val moduleExportsNode = globalScope.member("module").member("exports")
    var currentModule = globalScope
    val nodes = mutableMapOf()
    var thisNode: Node? = globalScope
    val namesOfLocalVars = mutableSetOf()

    fun addNodesForLocalVars(names: Collection) {
        nodes += names.filter { it !in nodes }.associate { it to Node(it) }
    }

    fun markSpecialFunctions(root: JsNode) {
        val candidates = mutableMapOf()
        val unsuitableNames = mutableSetOf()
        val assignedNames = mutableSetOf()
        root.accept(object : RecursiveJsVisitor() {
            override fun visit(x: JsVars.JsVar) {
                val name = x.name
                if (!assignedNames.add(name)) {
                    unsuitableNames += name
                }

                val initializer = x.initExpression
                if (initializer != null) {
                    val specialName = when {
                        isDefineInlineFunction(initializer) -> SpecialFunction.DEFINE_INLINE_FUNCTION
                        isWrapFunction(initializer) -> SpecialFunction.WRAP_FUNCTION
                        else -> null
                    }
                    specialName?.let { candidates[name] = specialName }
                }
                super.visit(x)
            }

            override fun visitBinaryExpression(x: JsBinaryOperation) {
                JsAstUtils.decomposeAssignmentToVariable(x)?.let { (left, _) -> unsuitableNames += left }
            }

            override fun visitFunction(x: JsFunction) {
                x.name?.let { unsuitableNames += it }
            }
        })

        for ((name, function) in candidates) {
            if (name !in unsuitableNames) {
                name.specialFunction = function
            }
        }
    }

    fun extractNode(expression: JsExpression): Node? {
        val node = extractNodeImpl(expression)?.original
        return if (node != null && moduleExportsNode in generateSequence(node) { it.qualifier?.parent }) {
            val path = node.pathFromRoot().drop(2)
            path.fold(currentModule.original) { n, memberName -> n.member(memberName) }
        }
        else {
            node
        }
    }

    private fun extractNodeImpl(expression: JsExpression): Node? {
        return when (expression) {
            is JsNameRef -> {
                val qualifier = expression.qualifier
                if (qualifier == null) {
                    val name = expression.name
                    if (name != null) {
                        if (name in namesOfLocalVars) return null
                        nodes[name]?.original?.let { return it }
                    }
                    globalScope.member(expression.ident)
                }
                else {
                    extractNodeImpl(qualifier)?.member(expression.ident)
                }
            }
            is JsArrayAccess -> {
                val index = expression.index
                if (index is JsStringLiteral) extractNodeImpl(expression.array)?.member(index.value) else null
            }
            is JsThisRef -> {
                thisNode
            }
            is JsInvocation -> {
                val qualifier = expression.qualifier
                if (qualifier is JsNameRef && qualifier.qualifier == null && qualifier.ident == "require" &&
                    qualifier.name !in nodes && expression.arguments.size == 1
                ) {
                    val argument = expression.arguments[0]
                    if (argument is JsStringLiteral) {
                        return globalScope.member(argument.value)
                    }
                }
                null
            }
            else -> {
                null
            }
        }
    }

    class Node private constructor(val localName: JsName?, qualifier: Qualifier?) {
        private val dependenciesImpl = mutableSetOf()
        private val expressionsImpl = mutableSetOf()
        private val functionsImpl = mutableSetOf()
        private var hasSideEffectsImpl = false
        private var reachableImpl = false
        private var declarationReachableImpl = false
        private val membersImpl = mutableMapOf()
        private val usedByAstNodesImpl = mutableSetOf()
        private var rank = 0

        val dependencies: MutableSet get() = original.dependenciesImpl

        val expressions: MutableSet get() = original.expressionsImpl

        val functions: MutableSet get() = original.functionsImpl

        var hasSideEffects: Boolean
            get() = original.hasSideEffectsImpl
            set(value) {
                original.hasSideEffectsImpl = value
            }

        var reachable: Boolean
            get() = original.reachableImpl
            set(value) {
                original.reachableImpl = value
            }

        var declarationReachable: Boolean
            get() = original.declarationReachableImpl
            set(value) {
                original.declarationReachableImpl = value
            }

        var qualifier: Qualifier? = qualifier
            get
            private set

        val usedByAstNodes: MutableSet get() = original.usedByAstNodesImpl

        val memberNames: MutableSet get() = original.membersImpl.keys

        constructor(localName: JsName? = null) : this(localName, null)

        var original: Node = this
            get() {
                if (field != this) {
                    field = field.original
                }
                return field
            }
            private set

        val members: Map get() = original.membersImpl

        fun member(name: String): Node = original.membersImpl.getOrPut(name) { Node(null, Qualifier(this, name)) }.original

        fun alias(other: Node) {
            val a = original
            val b = other.original
            if (a == b) return

            if (a.qualifier == null && b.qualifier == null) {
                a.merge(b)
            }
            else if (a.qualifier == null) {
                if (b.root() == a) a.makeDependencies(b) else b.evacuateFrom(a)
            }
            else if (b.qualifier == null) {
                if (a.root() == b) a.makeDependencies(b) else a.evacuateFrom(b)
            }
            else {
                a.makeDependencies(b)
            }
        }

        private fun makeDependencies(other: Node) {
            dependenciesImpl += other
            other.dependenciesImpl += this
        }

        private fun evacuateFrom(other: Node) {
            val (existingMembers, newMembers) = other.members.toList().partition { (name, _) -> name in membersImpl }
            other.original = this

            for ((name, member) in newMembers) {
                membersImpl[name] = member
                member.original.qualifier = Qualifier(this, member.original.qualifier!!.memberName)
            }
            for ((name, member) in existingMembers) {
                membersImpl[name]!!.original.merge(member.original)
                membersImpl[name] = member.original
                member.original.qualifier = Qualifier(this, member.original.qualifier!!.memberName)
            }
            other.membersImpl.clear()

            hasSideEffectsImpl = hasSideEffectsImpl || other.hasSideEffectsImpl
            expressionsImpl += other.expressionsImpl
            functionsImpl += other.functionsImpl
            dependenciesImpl += other.dependenciesImpl
            usedByAstNodesImpl += other.usedByAstNodesImpl

            other.expressionsImpl.clear()
            other.functionsImpl.clear()
            other.dependenciesImpl.clear()
            other.usedByAstNodesImpl.clear()
        }

        private fun merge(other: Node) {
            if (this == other) return

            if (rank < other.rank) {
                other.evacuateFrom(this)
            }
            else {
                evacuateFrom(other)
            }

            if (rank == other.rank) {
                rank++
            }
        }

        fun root(): Node = generateSequence(original) { it.qualifier?.parent?.original }.last()

        fun pathFromRoot(): List =
                generateSequence(original) { it.qualifier?.parent?.original }.mapNotNull { it.qualifier?.memberName }
                        .toList().asReversed()

        override fun toString(): String = (root().localName?.ident ?: "") + pathFromRoot().joinToString("") { ".$it" }
    }

    class Qualifier(val parent: Node, val memberName: String)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy