JS/Inlining: always create temporary variables to alias expressions in following cases:
* when decomposing expression that contains inline calls, alias all subexpressions * when substituting arguments to inline call * for value returned from inline function. Instead, rely on dedicated optimization pass. Improve lookup of function to inline, since the old one relied on immediate optimizations. Give more hints to optimizer. This should make inlining more stable in different hard-to-reproduce corner cases with evaluation order.
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2015 JetBrains s.r.o.
|
* Copyright 2010-2016 JetBrains s.r.o.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -17,10 +17,10 @@
|
|||||||
package org.jetbrains.kotlin.js.inline
|
package org.jetbrains.kotlin.js.inline
|
||||||
|
|
||||||
import com.google.dart.compiler.backend.js.ast.*
|
import com.google.dart.compiler.backend.js.ast.*
|
||||||
|
import com.google.dart.compiler.backend.js.ast.metadata.staticRef
|
||||||
import com.google.dart.compiler.backend.js.ast.metadata.synthetic
|
import com.google.dart.compiler.backend.js.ast.metadata.synthetic
|
||||||
import com.intellij.util.SmartList
|
import com.intellij.util.SmartList
|
||||||
import org.jetbrains.kotlin.js.inline.util.IdentitySet
|
import org.jetbrains.kotlin.js.inline.util.IdentitySet
|
||||||
import org.jetbrains.kotlin.js.inline.util.canHaveOwnSideEffect
|
|
||||||
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.*
|
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.*
|
||||||
import org.jetbrains.kotlin.js.translate.utils.jsAstUtils.*
|
import org.jetbrains.kotlin.js.translate.utils.jsAstUtils.*
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@ internal class ExpressionDecomposer private constructor(
|
|||||||
val decomposer = with (statement) {
|
val decomposer = with (statement) {
|
||||||
val extractable = match(canBeExtractedByInliner)
|
val extractable = match(canBeExtractedByInliner)
|
||||||
val containsExtractable = withParentsOfNodes(extractable)
|
val containsExtractable = withParentsOfNodes(extractable)
|
||||||
val nodesWithSideEffect = match { it is JsExpression && it.canHaveOwnSideEffect() }
|
val nodesWithSideEffect = match { it !is JsLiteral.JsValueLiteral }
|
||||||
val containsNodeWithSideEffect = withParentsOfNodes(nodesWithSideEffect)
|
val containsNodeWithSideEffect = withParentsOfNodes(nodesWithSideEffect)
|
||||||
|
|
||||||
ExpressionDecomposer(scope, containsExtractable, containsNodeWithSideEffect)
|
ExpressionDecomposer(scope, containsExtractable, containsNodeWithSideEffect)
|
||||||
@@ -169,7 +169,7 @@ internal class ExpressionDecomposer private constructor(
|
|||||||
// Must be (someThingWithSideEffect).x = arg2, because arg1 can have side effect
|
// Must be (someThingWithSideEffect).x = arg2, because arg1 can have side effect
|
||||||
assert(arg1 is JsNameRef) { "Valid JavaScript left-hand side must be JsNameRef, got: $this" }
|
assert(arg1 is JsNameRef) { "Valid JavaScript left-hand side must be JsNameRef, got: $this" }
|
||||||
val arg1AsRef = arg1 as JsNameRef
|
val arg1AsRef = arg1 as JsNameRef
|
||||||
arg1AsRef.qualifier = arg1AsRef.qualifier!!.extractToTemporary()
|
arg1AsRef.qualifier = arg1AsRef.qualifier?.extractToTemporary()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
arg1 = arg1.extractToTemporary()
|
arg1 = arg1.extractToTemporary()
|
||||||
@@ -317,22 +317,21 @@ internal class ExpressionDecomposer private constructor(
|
|||||||
addStatement(tmp.variable)
|
addStatement(tmp.variable)
|
||||||
return tmp.nameRef
|
return tmp.nameRef
|
||||||
}
|
}
|
||||||
|
|
||||||
private inner class Temporary(val value: JsExpression? = null) {
|
private inner class Temporary(val value: JsExpression? = null) {
|
||||||
val name: JsName = scope.declareTemporary()
|
val name: JsName = scope.declareTemporary()
|
||||||
|
|
||||||
val variable: JsVars = newVar(name, value)
|
val variable: JsVars = newVar(name, value).apply {
|
||||||
|
synthetic = true
|
||||||
|
name.staticRef = value
|
||||||
|
}
|
||||||
|
|
||||||
val nameRef: JsExpression
|
val nameRef: JsExpression
|
||||||
get() = name.makeRef()
|
get() = name.makeRef()
|
||||||
|
|
||||||
init {
|
|
||||||
variable.synthetic = true
|
|
||||||
}
|
|
||||||
|
|
||||||
fun assign(value: JsExpression): JsStatement {
|
fun assign(value: JsExpression): JsStatement {
|
||||||
val statement = JsExpressionStatement(assignment(nameRef, value))
|
val statement = JsExpressionStatement(assignment(nameRef, value)).apply { synthetic = true }
|
||||||
statement.synthetic = true
|
name.staticRef = value
|
||||||
return statement
|
return statement
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import org.jetbrains.kotlin.js.inline.util.rewriters.ReturnReplacingVisitor
|
|||||||
class FunctionInlineMutator
|
class FunctionInlineMutator
|
||||||
private constructor(
|
private constructor(
|
||||||
private val call: JsInvocation,
|
private val call: JsInvocation,
|
||||||
private val inliningContext: InliningContext
|
inliningContext: InliningContext
|
||||||
) {
|
) {
|
||||||
private val invokedFunction: JsFunction
|
private val invokedFunction: JsFunction
|
||||||
private val namingContext: NamingContext
|
private val namingContext: NamingContext
|
||||||
@@ -85,7 +85,9 @@ private constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun applyCapturedArgs(call: JsInvocation, inner: JsFunction, outer: JsFunction) {
|
private fun applyCapturedArgs(call: JsInvocation, inner: JsFunction, outer: JsFunction) {
|
||||||
val namingContext = inliningContext.newNamingContext()
|
// We want statements that introduce temporary variables to be added immediately after applying renamings,
|
||||||
|
// so that further processing that involves detection of inlineable function had a chance to recognize them.
|
||||||
|
val namingContext = NamingContext(inner.scope) { inner.body.statements.addAll(0, it) }
|
||||||
val arguments = call.arguments
|
val arguments = call.arguments
|
||||||
val parameters = outer.parameters
|
val parameters = outer.parameters
|
||||||
aliasArgumentsIfNeeded(namingContext, arguments, parameters)
|
aliasArgumentsIfNeeded(namingContext, arguments, parameters)
|
||||||
@@ -98,11 +100,9 @@ private constructor(
|
|||||||
var thisReplacement = getThisReplacement(call)
|
var thisReplacement = getThisReplacement(call)
|
||||||
if (thisReplacement == null || thisReplacement is JsLiteral.JsThisRef) return
|
if (thisReplacement == null || thisReplacement is JsLiteral.JsThisRef) return
|
||||||
|
|
||||||
if (thisReplacement.needToAlias()) {
|
val thisName = namingContext.getFreshName(getThisAlias())
|
||||||
val thisName = namingContext.getFreshName(getThisAlias())
|
namingContext.newVar(thisName, thisReplacement)
|
||||||
namingContext.newVar(thisName, thisReplacement)
|
thisReplacement = thisName.makeRef()
|
||||||
thisReplacement = thisName.makeRef()
|
|
||||||
}
|
|
||||||
|
|
||||||
replaceThisReference(block, thisReplacement)
|
replaceThisReference(block, thisReplacement)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2015 JetBrains s.r.o.
|
* Copyright 2010-2016 JetBrains s.r.o.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.js.inline;
|
|||||||
import com.google.dart.compiler.backend.js.ast.*;
|
import com.google.dart.compiler.backend.js.ast.*;
|
||||||
import com.google.dart.compiler.backend.js.ast.metadata.MetadataProperties;
|
import com.google.dart.compiler.backend.js.ast.metadata.MetadataProperties;
|
||||||
import com.intellij.psi.PsiElement;
|
import com.intellij.psi.PsiElement;
|
||||||
|
import kotlin.Unit;
|
||||||
import kotlin.jvm.functions.Function1;
|
import kotlin.jvm.functions.Function1;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
@@ -58,7 +59,6 @@ public class JsInliner extends JsVisitorWithContextImpl {
|
|||||||
if (!(node instanceof JsInvocation)) return false;
|
if (!(node instanceof JsInvocation)) return false;
|
||||||
|
|
||||||
JsInvocation call = (JsInvocation) node;
|
JsInvocation call = (JsInvocation) node;
|
||||||
|
|
||||||
return hasToBeInlined(call);
|
return hasToBeInlined(call);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -199,7 +199,7 @@ public class JsInliner extends JsVisitorWithContextImpl {
|
|||||||
|
|
||||||
if (currentStatement instanceof JsExpressionStatement &&
|
if (currentStatement instanceof JsExpressionStatement &&
|
||||||
((JsExpressionStatement) currentStatement).getExpression() == call &&
|
((JsExpressionStatement) currentStatement).getExpression() == call &&
|
||||||
(resultExpression == null || !SideEffectUtilsKt.canHaveSideEffect(resultExpression))
|
resultExpression == null
|
||||||
) {
|
) {
|
||||||
statementContext.removeMe();
|
statementContext.removeMe();
|
||||||
}
|
}
|
||||||
@@ -267,7 +267,13 @@ public class JsInliner extends JsVisitorWithContextImpl {
|
|||||||
@Override
|
@Override
|
||||||
public NamingContext newNamingContext() {
|
public NamingContext newNamingContext() {
|
||||||
JsScope scope = getFunctionContext().getScope();
|
JsScope scope = getFunctionContext().getScope();
|
||||||
return new NamingContext(scope, getStatementContext());
|
return new NamingContext(scope, new Function1<List<? extends JsStatement>, Unit>() {
|
||||||
|
@Override
|
||||||
|
public Unit invoke(List<? extends JsStatement> statements) {
|
||||||
|
getStatementContext().addPrevious(statements);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2015 JetBrains s.r.o.
|
* Copyright 2010-2016 JetBrains s.r.o.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -67,7 +67,7 @@ internal class ReferenceTracker<in Reference, RemoveCandidate : JsNode> {
|
|||||||
return referenceFromTo.getOrPut(referrer, { IdentitySet<Reference>() })
|
return referenceFromTo.getOrPut(referrer, { IdentitySet<Reference>() })
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isKnown(ref: Reference): Boolean {
|
fun isKnown(ref: Reference): Boolean {
|
||||||
return removableCandidates.containsKey(ref)
|
return removableCandidates.containsKey(ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -69,7 +69,7 @@ internal class TemporaryAssignmentElimination(private val root: JsBlock) {
|
|||||||
val propertyMutation = JsAstUtils.decomposeAssignment(x.expression)
|
val propertyMutation = JsAstUtils.decomposeAssignment(x.expression)
|
||||||
if (propertyMutation != null) {
|
if (propertyMutation != null) {
|
||||||
val (target, value) = propertyMutation
|
val (target, value) = propertyMutation
|
||||||
if (!target.canHaveSideEffect()) {
|
if (!target.canHaveSideEffect(namesToProcess)) {
|
||||||
val usage = Usage.PropertyMutation(x, target)
|
val usage = Usage.PropertyMutation(x, target)
|
||||||
tryRecord(value, usage)
|
tryRecord(value, usage)
|
||||||
accept(value)
|
accept(value)
|
||||||
|
|||||||
+25
-4
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2015 JetBrains s.r.o.
|
* Copyright 2010-2016 JetBrains s.r.o.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -20,6 +20,7 @@ import com.google.dart.compiler.backend.js.ast.*
|
|||||||
import com.google.dart.compiler.backend.js.ast.metadata.staticRef
|
import com.google.dart.compiler.backend.js.ast.metadata.staticRef
|
||||||
|
|
||||||
import org.jetbrains.kotlin.js.inline.util.collectUsedNames
|
import org.jetbrains.kotlin.js.inline.util.collectUsedNames
|
||||||
|
import org.jetbrains.kotlin.js.inline.util.transitiveStaticRef
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes unused local function declarations like:
|
* Removes unused local function declarations like:
|
||||||
@@ -55,7 +56,7 @@ private class UnusedInstanceCollector : JsVisitorWithContextImpl() {
|
|||||||
|
|
||||||
val references = collectUsedNames(x)
|
val references = collectUsedNames(x)
|
||||||
references.filterNotNull()
|
references.filterNotNull()
|
||||||
.forEach { tracker.addRemovableReference(name, it) }
|
.forEach { tracker.addRemovableReference(name, it) }
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -73,8 +74,28 @@ private class UnusedInstanceCollector : JsVisitorWithContextImpl() {
|
|||||||
private fun isLocalFunctionDeclaration(jsVar: JsVars.JsVar): Boolean {
|
private fun isLocalFunctionDeclaration(jsVar: JsVars.JsVar): Boolean {
|
||||||
val name = jsVar.name
|
val name = jsVar.name
|
||||||
val expr = jsVar.initExpression
|
val expr = jsVar.initExpression
|
||||||
val staticRef = name?.staticRef
|
|
||||||
|
|
||||||
return staticRef != null && staticRef == expr
|
// For the case like this: `b = a; c = b;`, where `a` is a function. In this case we should remove both declaration,
|
||||||
|
// although second one contains 'usage' of `b`.
|
||||||
|
// see `inlineEvaluationOrder/cases/lambdaWithClosure.kt`.
|
||||||
|
if (expr is JsNameRef && (expr.name?.let { tracker.isKnown(it) } ?: false)) return true
|
||||||
|
|
||||||
|
val staticRef = name?.staticRef
|
||||||
|
return staticRef != null && staticRef == expr && isFunctionReference(expr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For RHS of `var a = b;` checks whether *b* is a reference to a function or a closure instantiation, direct or indirect.
|
||||||
|
private fun isFunctionReference(expr: JsExpression): Boolean {
|
||||||
|
val qualifier = when (expr) {
|
||||||
|
// `var tmp = foo(closure)`, where `foo` is a closure constructor.
|
||||||
|
is JsInvocation -> expr.qualifier
|
||||||
|
|
||||||
|
// Either alias to another variable that holds function or a lambda without closure.
|
||||||
|
is JsNameRef -> expr
|
||||||
|
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
return qualifier?.transitiveStaticRef is JsFunction
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2015 JetBrains s.r.o.
|
* Copyright 2010-2016 JetBrains s.r.o.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -17,11 +17,11 @@
|
|||||||
package org.jetbrains.kotlin.js.inline.context
|
package org.jetbrains.kotlin.js.inline.context
|
||||||
|
|
||||||
import com.google.dart.compiler.backend.js.ast.*
|
import com.google.dart.compiler.backend.js.ast.*
|
||||||
import com.google.dart.compiler.backend.js.ast.metadata.staticRef
|
|
||||||
|
|
||||||
import org.jetbrains.kotlin.js.inline.FunctionReader
|
|
||||||
import com.google.dart.compiler.backend.js.ast.metadata.descriptor
|
import com.google.dart.compiler.backend.js.ast.metadata.descriptor
|
||||||
import org.jetbrains.kotlin.js.inline.util.*
|
import org.jetbrains.kotlin.js.inline.FunctionReader
|
||||||
|
import org.jetbrains.kotlin.js.inline.util.getSimpleName
|
||||||
|
import org.jetbrains.kotlin.js.inline.util.isCallInvocation
|
||||||
|
import org.jetbrains.kotlin.js.inline.util.transitiveStaticRef
|
||||||
|
|
||||||
abstract class FunctionContext(
|
abstract class FunctionContext(
|
||||||
private val function: JsFunction,
|
private val function: JsFunction,
|
||||||
@@ -76,31 +76,19 @@ abstract class FunctionContext(
|
|||||||
if (descriptor != null && descriptor in functionReader) return functionReader[descriptor]
|
if (descriptor != null && descriptor in functionReader) return functionReader[descriptor]
|
||||||
|
|
||||||
/** remove ending `()` */
|
/** remove ending `()` */
|
||||||
var callQualifier: JsNode = call.qualifier
|
val callQualifier: JsExpression = if (isCallInvocation(call)) {
|
||||||
|
(call.qualifier as JsNameRef).qualifier!!
|
||||||
/** remove ending `.call()` */
|
|
||||||
if (isCallInvocation(call)) {
|
|
||||||
callQualifier = (callQualifier as JsNameRef).qualifier!!
|
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
/** in case 4, 5 get ref (reduce 4, 5 to 2, 3 accordingly) */
|
call.qualifier
|
||||||
@Suppress("USELESS_CAST") // NB do not remove 'as JsNode' below until KT-10752 is fixed
|
|
||||||
if (callQualifier is JsNameRef) {
|
|
||||||
val staticRef = callQualifier.name?.staticRef
|
|
||||||
|
|
||||||
callQualifier = when (staticRef) {
|
|
||||||
is JsNameRef -> staticRef as JsNode
|
|
||||||
is JsInvocation -> staticRef as JsNode
|
|
||||||
is JsFunction, null -> callQualifier
|
|
||||||
else -> throw AssertionError("Unexpected static reference type ${staticRef.javaClass}")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** process cases 2, 3 */
|
/** process cases 2, 3 */
|
||||||
val qualifier = callQualifier
|
val qualifier = callQualifier.transitiveStaticRef
|
||||||
return when (qualifier) {
|
return when (qualifier) {
|
||||||
is JsInvocation -> lookUpStaticFunction(getSimpleName(qualifier)!!)
|
is JsInvocation -> lookUpStaticFunction(getSimpleName(qualifier)!!)
|
||||||
is JsNameRef -> lookUpStaticFunction(qualifier.name)
|
is JsNameRef -> lookUpStaticFunction(qualifier.name)
|
||||||
|
is JsFunction -> qualifier
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2015 JetBrains s.r.o.
|
* Copyright 2010-2016 JetBrains s.r.o.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -27,7 +27,7 @@ import org.jetbrains.kotlin.js.inline.util.replaceNames
|
|||||||
|
|
||||||
class NamingContext(
|
class NamingContext(
|
||||||
private val scope: JsScope,
|
private val scope: JsScope,
|
||||||
private val statementContext: JsContext<JsStatement>
|
private val statementConsumer: (List<JsStatement>) -> Unit
|
||||||
) {
|
) {
|
||||||
private val renamings = IdentityHashMap<JsName, JsExpression>()
|
private val renamings = IdentityHashMap<JsName, JsExpression>()
|
||||||
private val declarations = ArrayList<JsVars>()
|
private val declarations = ArrayList<JsVars>()
|
||||||
@@ -35,7 +35,7 @@ class NamingContext(
|
|||||||
|
|
||||||
fun applyRenameTo(target: JsNode): JsNode {
|
fun applyRenameTo(target: JsNode): JsNode {
|
||||||
if (!addedDeclarations) {
|
if (!addedDeclarations) {
|
||||||
statementContext.addPrevious(declarations)
|
statementConsumer(declarations)
|
||||||
addedDeclarations = true
|
addedDeclarations = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2015 JetBrains s.r.o.
|
* Copyright 2010-2016 JetBrains s.r.o.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -121,3 +121,19 @@ private fun getStaticRef(invocation: JsInvocation): JsNode? {
|
|||||||
val qualifierName = (qualifier as? HasName)?.name
|
val qualifierName = (qualifier as? HasName)?.name
|
||||||
return qualifierName?.staticRef
|
return qualifierName?.staticRef
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If an expression A references to another expression B, which in turn references to C, or a corresponding expression
|
||||||
|
* at the end of a chain of references. They are usually JsNameRef expressions with JsFunction at the very end.
|
||||||
|
* This chain is produced when there are lots of aliases created from aliases, i.e. `var $tmp1 = foo; var $tmp2 = $tmp1;`.
|
||||||
|
* So for `$tmp2` we should get reference to `foo`.
|
||||||
|
*/
|
||||||
|
val JsExpression.transitiveStaticRef: JsExpression
|
||||||
|
get() {
|
||||||
|
var qualifier = this
|
||||||
|
while (qualifier is JsNameRef) {
|
||||||
|
val staticRef = qualifier.name?.staticRef as? JsExpression ?: break
|
||||||
|
qualifier = staticRef
|
||||||
|
}
|
||||||
|
return qualifier
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2015 JetBrains s.r.o.
|
* Copyright 2010-2016 JetBrains s.r.o.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
package org.jetbrains.kotlin.js.inline.util
|
package org.jetbrains.kotlin.js.inline.util
|
||||||
|
|
||||||
import com.google.dart.compiler.backend.js.ast.*
|
import com.google.dart.compiler.backend.js.ast.*
|
||||||
|
import com.google.dart.compiler.backend.js.ast.metadata.staticRef
|
||||||
|
|
||||||
import org.jetbrains.kotlin.js.inline.context.NamingContext
|
import org.jetbrains.kotlin.js.inline.context.NamingContext
|
||||||
import org.jetbrains.kotlin.js.inline.util.rewriters.LabelNameRefreshingVisitor
|
import org.jetbrains.kotlin.js.inline.util.rewriters.LabelNameRefreshingVisitor
|
||||||
@@ -30,14 +31,16 @@ fun aliasArgumentsIfNeeded(
|
|||||||
|
|
||||||
for ((arg, param) in arguments.zip(parameters)) {
|
for ((arg, param) in arguments.zip(parameters)) {
|
||||||
val paramName = param.name
|
val paramName = param.name
|
||||||
val replacement =
|
|
||||||
if (arg.needToAlias()) {
|
val replacement: JsExpression = if (arg is JsFunction && isFunctionCreator(arg)) {
|
||||||
val freshName = context.getFreshName(paramName)
|
arg
|
||||||
context.newVar(freshName, arg)
|
}
|
||||||
freshName.makeRef()
|
else {
|
||||||
} else {
|
val freshName = context.getFreshName(paramName).apply { staticRef = arg }
|
||||||
arg
|
context.newVar(freshName, arg)
|
||||||
}
|
val freshNameRef = freshName.makeRef()
|
||||||
|
freshNameRef
|
||||||
|
}
|
||||||
|
|
||||||
context.replaceName(paramName, replacement)
|
context.replaceName(paramName, replacement)
|
||||||
}
|
}
|
||||||
@@ -60,7 +63,7 @@ fun renameLocalNames(
|
|||||||
function: JsFunction
|
function: JsFunction
|
||||||
) {
|
) {
|
||||||
for (name in collectDefinedNames(function.body)) {
|
for (name in collectDefinedNames(function.body)) {
|
||||||
val freshName = context.getFreshName(name)
|
val freshName = context.getFreshName(name).apply { staticRef = name.staticRef }
|
||||||
context.replaceName(name, freshName.makeRef())
|
context.replaceName(name, freshName.makeRef())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-8
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2015 JetBrains s.r.o.
|
* Copyright 2010-2016 JetBrains s.r.o.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -20,7 +20,6 @@ import com.google.dart.compiler.backend.js.ast.*
|
|||||||
import com.google.dart.compiler.backend.js.ast.metadata.functionDescriptor
|
import com.google.dart.compiler.backend.js.ast.metadata.functionDescriptor
|
||||||
import com.google.dart.compiler.backend.js.ast.metadata.returnTarget
|
import com.google.dart.compiler.backend.js.ast.metadata.returnTarget
|
||||||
import com.google.dart.compiler.backend.js.ast.metadata.synthetic
|
import com.google.dart.compiler.backend.js.ast.metadata.synthetic
|
||||||
import org.jetbrains.kotlin.js.inline.util.canHaveSideEffect
|
|
||||||
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
|
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
|
||||||
|
|
||||||
class ReturnReplacingVisitor(
|
class ReturnReplacingVisitor(
|
||||||
@@ -55,14 +54,16 @@ class ReturnReplacingVisitor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun getReturnReplacement(returnExpression: JsExpression?): JsExpression? {
|
private fun getReturnReplacement(returnExpression: JsExpression?): JsExpression? {
|
||||||
if (returnExpression != null) {
|
return if (returnExpression != null) {
|
||||||
if (resultRef != null) {
|
if (resultRef != null) {
|
||||||
return JsAstUtils.assignment(resultRef, returnExpression).apply { synthetic = true }
|
JsAstUtils.assignment(resultRef, returnExpression).apply { synthetic = true }
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
returnExpression
|
||||||
}
|
}
|
||||||
|
|
||||||
if (returnExpression.canHaveSideEffect()) return returnExpression
|
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
return null
|
null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2015 JetBrains s.r.o.
|
* Copyright 2010-2016 JetBrains s.r.o.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -16,34 +16,20 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.js.inline.util
|
package org.jetbrains.kotlin.js.inline.util
|
||||||
|
|
||||||
import com.google.dart.compiler.backend.js.ast.JsLiteral.*
|
|
||||||
import com.google.dart.compiler.backend.js.ast.*
|
import com.google.dart.compiler.backend.js.ast.*
|
||||||
import com.google.dart.compiler.backend.js.ast.metadata.typeCheck
|
import com.google.dart.compiler.backend.js.ast.metadata.HasMetadata
|
||||||
|
import com.google.dart.compiler.backend.js.ast.metadata.sideEffects
|
||||||
import org.jetbrains.kotlin.js.translate.utils.jsAstUtils.any
|
import org.jetbrains.kotlin.js.translate.utils.jsAstUtils.any
|
||||||
|
|
||||||
fun JsExpression.canHaveSideEffect(): Boolean =
|
fun JsExpression.canHaveSideEffect(localVars: Set<JsName>) =
|
||||||
any { it is JsExpression && it.canHaveOwnSideEffect() }
|
any { it is JsExpression && it.canHaveOwnSideEffect(localVars) }
|
||||||
|
|
||||||
fun JsExpression.canHaveOwnSideEffect(): Boolean =
|
fun JsExpression.canHaveOwnSideEffect(vars: Set<JsName>) = when (this) {
|
||||||
when (this) {
|
is JsConditional,
|
||||||
is JsValueLiteral,
|
is JsLiteral -> false
|
||||||
is JsConditional,
|
is JsBinaryOperation -> operator.isAssignment
|
||||||
is JsArrayAccess,
|
is JsNameRef -> !(qualifier == null && name in vars) && sideEffects
|
||||||
is JsArrayLiteral,
|
is JsInvocation -> !isFunctionCreatorInvocation(this) && sideEffects
|
||||||
is JsNameRef -> false
|
is HasMetadata -> sideEffects
|
||||||
is JsBinaryOperation -> operator.isAssignment
|
else -> true
|
||||||
is JsInvocation -> !isFunctionCreatorInvocation(this)
|
}
|
||||||
else -> true
|
|
||||||
}
|
|
||||||
|
|
||||||
fun JsExpression.needToAlias(): Boolean =
|
|
||||||
any { it is JsExpression && it.shouldHaveOwnAlias() }
|
|
||||||
|
|
||||||
fun JsExpression.shouldHaveOwnAlias(): Boolean =
|
|
||||||
when (this) {
|
|
||||||
is JsConditional,
|
|
||||||
is JsBinaryOperation,
|
|
||||||
is JsArrayLiteral -> true
|
|
||||||
is JsInvocation -> if (typeCheck == null) canHaveSideEffect() else false
|
|
||||||
else -> canHaveOwnSideEffect()
|
|
||||||
}
|
|
||||||
|
|||||||
+2
-1
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.js.translate.expression
|
|||||||
import com.google.dart.compiler.backend.js.ast.*
|
import com.google.dart.compiler.backend.js.ast.*
|
||||||
import com.google.dart.compiler.backend.js.ast.metadata.functionDescriptor
|
import com.google.dart.compiler.backend.js.ast.metadata.functionDescriptor
|
||||||
import com.google.dart.compiler.backend.js.ast.metadata.isLocal
|
import com.google.dart.compiler.backend.js.ast.metadata.isLocal
|
||||||
|
import com.google.dart.compiler.backend.js.ast.metadata.sideEffects
|
||||||
import com.google.dart.compiler.backend.js.ast.metadata.staticRef
|
import com.google.dart.compiler.backend.js.ast.metadata.staticRef
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.js.inline.util.getInnerFunction
|
import org.jetbrains.kotlin.js.inline.util.getInnerFunction
|
||||||
@@ -62,7 +63,7 @@ class LiteralFunctionTranslator(context: TranslationContext) : AbstractTranslato
|
|||||||
}
|
}
|
||||||
|
|
||||||
lambda.isLocal = true
|
lambda.isLocal = true
|
||||||
return invokingContext.define(descriptor, lambda)
|
return invokingContext.define(descriptor, lambda).apply { sideEffects = false }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-4
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2015 JetBrains s.r.o.
|
* Copyright 2010-2016 JetBrains s.r.o.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -18,7 +18,6 @@ package org.jetbrains.kotlin.js.translate.intrinsic.functions.factories;
|
|||||||
|
|
||||||
import com.google.dart.compiler.backend.js.ast.JsExpression;
|
import com.google.dart.compiler.backend.js.ast.JsExpression;
|
||||||
import com.google.dart.compiler.backend.js.ast.JsInvocation;
|
import com.google.dart.compiler.backend.js.ast.JsInvocation;
|
||||||
import com.google.dart.compiler.backend.js.ast.JsNameRef;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.kotlin.js.translate.context.Namer;
|
import org.jetbrains.kotlin.js.translate.context.Namer;
|
||||||
@@ -31,10 +30,10 @@ import java.util.List;
|
|||||||
|
|
||||||
public class KotlinFunctionIntrinsic extends FunctionIntrinsic {
|
public class KotlinFunctionIntrinsic extends FunctionIntrinsic {
|
||||||
@NotNull
|
@NotNull
|
||||||
private final JsNameRef function;
|
private final String functionName;
|
||||||
|
|
||||||
public KotlinFunctionIntrinsic(@NotNull String functionName) {
|
public KotlinFunctionIntrinsic(@NotNull String functionName) {
|
||||||
function = JsAstUtils.fqnWithoutSideEffects(functionName, Namer.kotlinObject());
|
this.functionName = functionName;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@@ -44,6 +43,7 @@ public class KotlinFunctionIntrinsic extends FunctionIntrinsic {
|
|||||||
@NotNull List<JsExpression> arguments,
|
@NotNull List<JsExpression> arguments,
|
||||||
@NotNull TranslationContext context
|
@NotNull TranslationContext context
|
||||||
) {
|
) {
|
||||||
|
JsExpression function = JsAstUtils.fqnWithoutSideEffects(functionName, Namer.kotlinObject());
|
||||||
return new JsInvocation(function, receiver == null ? arguments : TranslationUtils.generateInvocationArguments(receiver, arguments));
|
return new JsInvocation(function, receiver == null ? arguments : TranslationUtils.generateInvocationArguments(receiver, arguments));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user