diff --git a/js/js.inliner/src/org/jetbrains/k2js/inline/FunctionInlineMutator.java b/js/js.inliner/src/org/jetbrains/k2js/inline/FunctionInlineMutator.java index beb451cb155..b1ed89cf828 100644 --- a/js/js.inliner/src/org/jetbrains/k2js/inline/FunctionInlineMutator.java +++ b/js/js.inliner/src/org/jetbrains/k2js/inline/FunctionInlineMutator.java @@ -19,6 +19,8 @@ package org.jetbrains.k2js.inline; import com.google.dart.compiler.backend.js.ast.*; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; +import static org.jetbrains.k2js.inline.util.UtilPackage.replaceReturns; +import static org.jetbrains.k2js.inline.util.UtilPackage.replaceThisReference; import static org.jetbrains.k2js.inline.InlinePackage.*; @@ -83,20 +85,16 @@ class FunctionInlineMutator { applyRenaming(); doDirectInline(); } else { - replaceReturns(); + processReturns(); applyRenaming(); } } private void replaceThis() { - if (!hasThisReference(body)) { - return; - } + if (!hasThisReference(body)) return; - JsExpression thisReplacement = inliningContext.getThisReplacement(call); - if (thisReplacement == JsLiteral.THIS) { - return; - } + JsExpression thisReplacement = getThisReplacement(call); + if (thisReplacement == null) return; if (needToAlias(thisReplacement)) { JsName thisName = renamingContext.getFreshName(getThisAlias()); @@ -149,7 +147,7 @@ class FunctionInlineMutator { } assert resultExpr == null || resultExpr instanceof JsNameRef; - ReplaceReturnVisitor.replaceReturn(body, (JsNameRef) resultExpr, breakLabelRef); + replaceReturns(body, (JsNameRef) resultExpr, breakLabelRef); } @NotNull @@ -190,6 +188,24 @@ class FunctionInlineMutator { return labelPrefix + "$"; } + @Nullable + private static JsExpression getThisReplacement(JsInvocation call) { + if (isCallInvocation(call)) { + return call.getArguments().get(0); + } + + if (hasCallerQualifier(call)) { + return getCallerQualifier(call); + } + + return null; + } + + private static boolean hasThisReference(JsBlock body) { + List thisRefs = collectInstances(JsLiteral.JsThisRef.class, body); + return !thisRefs.isEmpty(); + } + private static boolean canBeExpression(JsBlock body) { List statements = body.getStatements(); return statements.size() == 1 && statements.get(0) instanceof JsReturn; diff --git a/js/js.inliner/src/org/jetbrains/k2js/inline/RenamingVisitor.java b/js/js.inliner/src/org/jetbrains/k2js/inline/RenamingVisitor.java deleted file mode 100644 index a023b0fa653..00000000000 --- a/js/js.inliner/src/org/jetbrains/k2js/inline/RenamingVisitor.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2010-2014 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.k2js.inline; - -import com.google.dart.compiler.backend.js.ast.*; -import org.jetbrains.annotations.NotNull; - -import java.util.IdentityHashMap; - -public class RenamingVisitor extends JsVisitorWithContextImpl { - private final IdentityHashMap replaceMap; - - @NotNull - public static T rename(T node, IdentityHashMap replaceMap) { - RenamingVisitor visitor = new RenamingVisitor(replaceMap); - return visitor.accept(node); - } - - private RenamingVisitor(IdentityHashMap replaceMap) { - this.replaceMap = replaceMap; - } - - @Override - public void endVisit(JsNameRef x, JsContext ctx) { - JsExpression replacement = replaceMap.get(x.getName()); - - if (replacement == null) { - return; - } - - ctx.replaceMe(replacement); - } - - @Override - public void endVisit(JsVars.JsVar x, JsContext ctx) { - JsExpression replacement = replaceMap.get(x.getName()); - if (replacement instanceof HasName) { - JsName replacementName = ((HasName) replacement).getName(); - JsVars.JsVar replacementVar = new JsVars.JsVar(replacementName, x.getInitExpression()); - ctx.replaceMe(replacementVar); - } - } - - @Override - public void endVisit(JsLabel x, JsContext ctx) { - JsExpression replacement = replaceMap.get(x.getName()); - if (replacement instanceof HasName) { - JsName replacementName = ((HasName) replacement).getName(); - JsLabel replacementLabel = new JsLabel(replacementName, x.getStatement()); - ctx.replaceMe(replacementLabel); - } - } - - private static boolean hasStaticRef(@NotNull HasName hasName) { - JsName name = hasName.getName(); - return name != null && name.getStaticRef() != null; - } -} diff --git a/js/js.inliner/src/org/jetbrains/k2js/inline/ReplaceReturnVisitor.java b/js/js.inliner/src/org/jetbrains/k2js/inline/ReplaceReturnVisitor.java deleted file mode 100644 index 2b0b4d8aaf6..00000000000 --- a/js/js.inliner/src/org/jetbrains/k2js/inline/ReplaceReturnVisitor.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2010-2014 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.k2js.inline; - -import com.google.dart.compiler.backend.js.ast.*; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.k2js.translate.utils.JsAstUtils; - -import static org.jetbrains.k2js.inline.util.UtilPackage.canHaveSideEffect; - -class ReplaceReturnVisitor extends JsVisitorWithContextImpl { - private final JsNameRef resultRef; - - @Nullable - private final JsNameRef breakLabel; - - @NotNull - public static JsNode replaceReturn(JsStatement statement, @Nullable JsNameRef resultRef, @Nullable JsNameRef breakLabel) { - return new ReplaceReturnVisitor(resultRef, breakLabel).accept(statement); - } - - private ReplaceReturnVisitor(@Nullable JsNameRef resultRef, @Nullable JsNameRef breakLabel) { - this.resultRef = resultRef; - this.breakLabel = breakLabel; - } - - @Override - public void endVisit(JsReturn x, JsContext ctx) { - if (breakLabel != null) { - JsBreak breakFunction = new JsBreak(breakLabel); - ctx.insertAfter(breakFunction); - } - - JsExpression returnExpression = x.getExpression(); - if (returnExpression == null) { - ctx.removeMe(); - return; - } - - if (resultRef != null) { - JsExpression resultAssignment = JsAstUtils.assignment(resultRef, returnExpression); - ctx.replaceMe(new JsExpressionStatement(resultAssignment)); - return; - } - - if (canHaveSideEffect(returnExpression)) { - JsStatement replacement = new JsExpressionStatement(returnExpression); - ctx.replaceMe(replacement); - } else { - ctx.removeMe(); - } - } - - @Override - public boolean visit(JsObjectLiteral x, JsContext ctx) { - return false; - } - - @Override - public boolean visit(JsFunction x, JsContext ctx) { - return false; - } -} diff --git a/js/js.inliner/src/org/jetbrains/k2js/inline/ThisReplacing.kt b/js/js.inliner/src/org/jetbrains/k2js/inline/ThisReplacing.kt deleted file mode 100644 index e51db59654e..00000000000 --- a/js/js.inliner/src/org/jetbrains/k2js/inline/ThisReplacing.kt +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2010-2014 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.k2js.inline - -import com.google.dart.compiler.backend.js.ast.* - -fun replaceThisReference(node: T, replacement: JsExpression) { - val visitor = ThisReplacingVisitor(replacement) - visitor.accept(node) -} - -fun hasThisReference(node: JsNode): Boolean { - val visitor = ContainsThisVisitor() - visitor.accept(node) - return visitor.containsThis -} - -private class ThisReplacingVisitor(private val thisReplacement: JsExpression) : JsVisitorWithContextImpl() { - override fun endVisit(x: JsLiteral.JsThisRef?, ctx: JsContext?) { - ctx?.replaceMe(thisReplacement) - } - - override fun visit(x: JsFunction?, ctx: JsContext?) = false - - override fun visit(x: JsObjectLiteral?, ctx: JsContext?) = false -} - -private class ContainsThisVisitor(): RecursiveJsVisitor() { - public var containsThis: Boolean = false - private set - - override fun visitElement(node: JsNode?) { - if (!containsThis) { - super.visitElement(node) - } - } - - override fun visitThis(x: JsLiteral.JsThisRef?) { - containsThis = true - } - - - override fun visitFunction(x: JsFunction?) { } - - override fun visitObjectLiteral(x: JsObjectLiteral?) { } -} diff --git a/js/js.inliner/src/org/jetbrains/k2js/inline/util/rewriteUtils.kt b/js/js.inliner/src/org/jetbrains/k2js/inline/util/rewriteUtils.kt new file mode 100644 index 00000000000..740538c2b85 --- /dev/null +++ b/js/js.inliner/src/org/jetbrains/k2js/inline/util/rewriteUtils.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2010-2014 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.k2js.inline.util + +import com.google.dart.compiler.backend.js.ast.JsExpression +import com.google.dart.compiler.backend.js.ast.JsLiteral +import com.google.dart.compiler.backend.js.ast.JsName +import com.google.dart.compiler.backend.js.ast.JsNameRef +import com.google.dart.compiler.backend.js.ast.JsNode +import com.google.dart.compiler.backend.js.ast.JsStatement +import org.jetbrains.k2js.inline.util.rewriters.NameReplacingVisitor +import org.jetbrains.k2js.inline.util.rewriters.ReturnReplacingVisitor +import org.jetbrains.k2js.inline.util.rewriters.ThisReplacingVisitor + +import java.util.IdentityHashMap + +public fun replaceNames(node: T, replaceMap: IdentityHashMap): T { + return NameReplacingVisitor(replaceMap).accept(node)!! +} + +public fun replaceReturns(scope: JsNode, resultRef: JsNameRef?, breakLabel: JsNameRef?): JsNode { + return ReturnReplacingVisitor(resultRef, breakLabel).accept(scope)!! +} + +public fun replaceThisReference(node: T, replacement: JsExpression) { + ThisReplacingVisitor(replacement).accept(node) +} \ No newline at end of file diff --git a/js/js.inliner/src/org/jetbrains/k2js/inline/util/rewriters/NameReplacingVisitor.kt b/js/js.inliner/src/org/jetbrains/k2js/inline/util/rewriters/NameReplacingVisitor.kt new file mode 100644 index 00000000000..c7e1e89aaeb --- /dev/null +++ b/js/js.inliner/src/org/jetbrains/k2js/inline/util/rewriters/NameReplacingVisitor.kt @@ -0,0 +1,58 @@ +/* + * Copyright 2010-2014 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.k2js.inline.util.rewriters + +import com.google.dart.compiler.backend.js.ast.JsVisitorWithContextImpl +import com.google.dart.compiler.backend.js.ast.JsName +import com.google.dart.compiler.backend.js.ast.JsExpression +import com.google.dart.compiler.backend.js.ast.JsNameRef +import com.google.dart.compiler.backend.js.ast.JsContext +import com.google.dart.compiler.backend.js.ast.JsVars +import com.google.dart.compiler.backend.js.ast.HasName +import com.google.dart.compiler.backend.js.ast.JsLabel + +class NameReplacingVisitor(private val replaceMap: Map) : JsVisitorWithContextImpl() { + + override fun endVisit(x: JsNameRef?, ctx: JsContext?) { + if (x == null || ctx == null) return + + val replacement = replaceMap[x.getName()] + if (replacement == null) return + + ctx.replaceMe(replacement) + } + + override fun endVisit(x: JsVars.JsVar?, ctx: JsContext?) { + if (x == null || ctx == null) return + + val replacement = replaceMap[x.getName()] + if (replacement is HasName) { + val replacementVar = JsVars.JsVar(replacement.getName(), x.getInitExpression()) + ctx.replaceMe(replacementVar) + } + } + + override fun endVisit(x: JsLabel?, ctx: JsContext?) { + if (x == null || ctx == null) return + + val replacement = replaceMap[x.getName()] + if (replacement is HasName) { + val replacementLabel = JsLabel(replacement.getName(), x.getStatement()) + ctx.replaceMe(replacementLabel) + } + } +} \ No newline at end of file diff --git a/js/js.inliner/src/org/jetbrains/k2js/inline/util/rewriters/ReturnReplacingVisitor.kt b/js/js.inliner/src/org/jetbrains/k2js/inline/util/rewriters/ReturnReplacingVisitor.kt new file mode 100644 index 00000000000..fa1d12a28d4 --- /dev/null +++ b/js/js.inliner/src/org/jetbrains/k2js/inline/util/rewriters/ReturnReplacingVisitor.kt @@ -0,0 +1,70 @@ +/* + * Copyright 2010-2014 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.k2js.inline.util.rewriters + +import com.google.dart.compiler.backend.js.ast.JsBreak +import com.google.dart.compiler.backend.js.ast.JsContext +import com.google.dart.compiler.backend.js.ast.JsExpression +import com.google.dart.compiler.backend.js.ast.JsExpressionStatement +import com.google.dart.compiler.backend.js.ast.JsFunction +import com.google.dart.compiler.backend.js.ast.JsNameRef +import com.google.dart.compiler.backend.js.ast.JsObjectLiteral +import com.google.dart.compiler.backend.js.ast.JsReturn +import com.google.dart.compiler.backend.js.ast.JsStatement +import com.google.dart.compiler.backend.js.ast.JsVisitorWithContextImpl +import org.jetbrains.k2js.inline.util.canHaveSideEffect +import org.jetbrains.k2js.translate.utils.JsAstUtils + +class ReturnReplacingVisitor(private val resultRef: JsNameRef?, private val breakLabel: JsNameRef?) : JsVisitorWithContextImpl() { + + /** + * Prevents replacing returns in object literal + */ + override fun visit(x: JsObjectLiteral?, ctx: JsContext?): Boolean = false + + /** + * Prevents replacing returns in inner function + */ + override fun visit(x: JsFunction?, ctx: JsContext?): Boolean = false + + override fun endVisit(x: JsReturn?, ctx: JsContext?) { + if (x == null || ctx == null) return + + if (breakLabel != null) { + ctx.insertAfter(JsBreak(breakLabel)) + } + + val returnReplacement = getReturnReplacement(x.getExpression()) + if (returnReplacement != null) { + ctx.insertBefore(JsExpressionStatement(returnReplacement)) + } + + ctx.removeMe() + } + + private fun getReturnReplacement(returnExpression: JsExpression?): JsExpression? { + if (returnExpression != null) { + if (resultRef != null) + return JsAstUtils.assignment(resultRef, returnExpression) + + if (canHaveSideEffect(returnExpression)) + return returnExpression + } + + return null + } +} \ No newline at end of file diff --git a/js/js.inliner/src/org/jetbrains/k2js/inline/util/rewriters/ThisReplacingVisitor.kt b/js/js.inliner/src/org/jetbrains/k2js/inline/util/rewriters/ThisReplacingVisitor.kt new file mode 100644 index 00000000000..909a27bbb04 --- /dev/null +++ b/js/js.inliner/src/org/jetbrains/k2js/inline/util/rewriters/ThisReplacingVisitor.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2010-2014 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.k2js.inline.util.rewriters + +import com.google.dart.compiler.backend.js.ast.JsContext +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.JsLiteral +import com.google.dart.compiler.backend.js.ast.JsObjectLiteral +import com.google.dart.compiler.backend.js.ast.JsVisitorWithContextImpl + +class ThisReplacingVisitor(private val thisReplacement: JsExpression) : JsVisitorWithContextImpl() { + override fun endVisit(x: JsLiteral.JsThisRef?, ctx: JsContext?) { + ctx?.replaceMe(thisReplacement) + } + + override fun visit(x: JsFunction?, ctx: JsContext?) = false + + override fun visit(x: JsObjectLiteral?, ctx: JsContext?) = false +} \ No newline at end of file