From e54138b74fc091ef26c8a147cac64ba8c5ee0f33 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Wed, 23 Nov 2016 16:02:42 +0300 Subject: [PATCH] JS: use temporary names while translation, replace with fixed name during post-processing pass --- .../dart/compiler/backend/js/ast/HasName.java | 2 + .../compiler/backend/js/ast/JsCatchScope.java | 4 +- .../compiler/backend/js/ast/JsFunction.java | 1 + .../dart/compiler/backend/js/ast/JsLabel.java | 7 +- .../dart/compiler/backend/js/ast/JsName.java | 37 +++--- .../compiler/backend/js/ast/JsNameRef.java | 5 + .../compiler/backend/js/ast/JsParameter.java | 7 +- .../compiler/backend/js/ast/JsProgram.java | 4 +- .../dart/compiler/backend/js/ast/JsScope.java | 41 ++++--- .../dart/compiler/backend/js/ast/JsVars.java | 7 +- .../dart/compiler/backend/js/ast/jsScopes.kt | 14 +-- .../js/resolve/diagnostics/JsCallChecker.kt | 4 +- .../kotlin/js/inline/FunctionReader.kt | 2 +- .../inline/clean/RedundantBindElimination.kt | 8 +- .../js/inline/clean/resolveTemporaryNames.kt | 116 ++++++++++++++++++ .../kotlin/js/inline/context/NamingContext.kt | 6 +- .../kotlin/js/inline/util/collectUtils.kt | 18 +-- .../kotlin/js/inline/util/namingUtils.kt | 4 +- .../js/test/optimizer/BasicOptimizerTest.kt | 2 +- .../kotlin/js/facade/K2JSTranslator.java | 2 + .../js/translate/context/AliasingContext.java | 13 +- .../translate/context/DeclarationExporter.kt | 4 +- .../kotlin/js/translate/context/Namer.java | 7 +- .../js/translate/context/StaticContext.java | 52 ++++++-- .../translate/context/TranslationContext.java | 2 +- .../js/translate/context/UsageTracker.kt | 3 - .../translate/declaration/ClassTranslator.kt | 8 +- .../translate/declaration/EnumTranslator.kt | 2 +- .../PackageDeclarationTranslator.java | 17 +-- .../declaration/PackageTranslator.java | 55 --------- .../expression/ExpressionVisitor.java | 12 +- .../expression/FunctionTranslator.java | 19 ++- .../expression/LiteralFunctionTranslator.kt | 6 +- .../expression/LocalFunctionCollector.kt | 39 ++++++ .../reference/CallExpressionTranslator.java | 2 +- .../reference/CallableReferenceTranslator.kt | 2 +- .../kotlin/js/translate/utils/astUtils.kt | 2 +- .../kotlin/js/translate/utils/utils.kt | 4 +- .../testData/_commonFiles/asserts.kt | 16 +-- 39 files changed, 362 insertions(+), 194 deletions(-) create mode 100644 js/js.inliner/src/org/jetbrains/kotlin/js/inline/clean/resolveTemporaryNames.kt delete mode 100644 js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/PackageTranslator.java create mode 100644 js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/LocalFunctionCollector.kt diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/HasName.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/HasName.java index bdd87970929..4aeb9aff8a1 100644 --- a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/HasName.java +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/HasName.java @@ -11,4 +11,6 @@ import com.google.dart.compiler.common.HasSymbol; */ public interface HasName extends HasSymbol { JsName getName(); + + void setName(JsName name); } diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsCatchScope.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsCatchScope.java index afa4115f641..337550f7b06 100644 --- a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsCatchScope.java +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsCatchScope.java @@ -14,8 +14,8 @@ public class JsCatchScope extends JsScope { private final JsName name; public JsCatchScope(JsScope parent, @NotNull String ident) { - super(parent, "Catch scope", null); - name = new JsName(this, ident); + super(parent, "Catch scope"); + name = new JsName(this, ident, false); } @Override diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsFunction.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsFunction.java index f49725a14a6..0106994968c 100644 --- a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsFunction.java +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsFunction.java @@ -66,6 +66,7 @@ public final class JsFunction extends JsLiteral implements HasName { this.body = body; } + @Override public void setName(@Nullable JsName name) { this.name = name; } diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsLabel.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsLabel.java index 7241fbe24ac..4aca993b88e 100644 --- a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsLabel.java +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsLabel.java @@ -12,7 +12,7 @@ import org.jetbrains.annotations.NotNull; * Represents a JavaScript label statement. */ public class JsLabel extends SourceInfoAwareJsNode implements JsStatement, HasName { - private final JsName label; + private JsName label; private JsStatement statement; @@ -30,6 +30,11 @@ public class JsLabel extends SourceInfoAwareJsNode implements JsStatement, HasNa return label; } + @Override + public void setName(JsName name) { + label = name; + } + @Override public Symbol getSymbol() { return label; diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsName.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsName.java index ab19d383c1b..dfaa7cbc1f1 100644 --- a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsName.java +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsName.java @@ -12,17 +12,35 @@ import org.jetbrains.annotations.NotNull; * An abstract base class for named JavaScript objects. */ public class JsName extends HasMetadata implements Symbol { + private static int ordinalGenerator; private final JsScope enclosing; + private final int ordinal; @NotNull private final String ident; + private final boolean temporary; + /** * @param ident the unmangled ident to use for this name */ - JsName(JsScope enclosing, @NotNull String ident) { + JsName(JsScope enclosing, @NotNull String ident, boolean temporary) { this.enclosing = enclosing; this.ident = ident; + this.temporary = temporary; + ordinal = temporary ? ordinalGenerator++ : 0; + } + + public int getOrdinal() { + return ordinal; + } + + public JsScope getEnclosing() { + return enclosing; + } + + public boolean isTemporary() { + return temporary; } @NotNull @@ -39,21 +57,4 @@ public class JsName extends HasMetadata implements Symbol { public String toString() { return ident; } - - @Override - public int hashCode() { - return ident.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof JsName)) { - return false; - } - JsName other = (JsName) obj; - return ident.equals(other.ident) && enclosing == other.enclosing; - } } diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsNameRef.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsNameRef.java index 9bdb7bf498a..00bab0c1de6 100644 --- a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsNameRef.java +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsNameRef.java @@ -50,6 +50,11 @@ public final class JsNameRef extends JsExpression implements HasName { return name; } + @Override + public void setName(JsName name) { + this.name = name; + } + @Nullable @Override public Symbol getSymbol() { diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsParameter.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsParameter.java index 9c829a455f7..b6cdaa475a9 100644 --- a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsParameter.java +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsParameter.java @@ -12,7 +12,7 @@ import org.jetbrains.annotations.NotNull; */ public final class JsParameter extends SourceInfoAwareJsNode implements HasName { @NotNull - private final JsName name; + private JsName name; public JsParameter(@NotNull JsName name) { this.name = name; @@ -24,6 +24,11 @@ public final class JsParameter extends SourceInfoAwareJsNode implements HasName return name; } + @Override + public void setName(@NotNull JsName name) { + this.name = name; + } + @Override @NotNull public Symbol getSymbol() { diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsProgram.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsProgram.java index 648f5aaa6ac..a818b423f4f 100644 --- a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsProgram.java +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsProgram.java @@ -28,9 +28,9 @@ public final class JsProgram extends SourceInfoAwareJsNode { private final Map stringLiteralMap = new THashMap(); private final JsObjectScope topScope; - public JsProgram(String unitId) { + public JsProgram() { rootScope = new JsRootScope(this); - topScope = new JsObjectScope(rootScope, "Global", unitId); + topScope = new JsObjectScope(rootScope, "Global"); setFragmentCount(1); } diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsScope.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsScope.java index 8c26780826d..cbe5e1379d3 100644 --- a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsScope.java +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsScope.java @@ -8,13 +8,10 @@ import com.google.dart.compiler.util.Maps; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; +import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; -import static com.google.dart.compiler.backend.js.ast.JsScopesKt.JsObjectScope; /** * A scope is a factory for creating and allocating @@ -46,14 +43,13 @@ public abstract class JsScope { @NotNull private final String description; private Map names = Collections.emptyMap(); + private Map temporaryNames; + private Set readonlyTemporaryNames = null; private final JsScope parent; - private int tempIndex = 0; - private final String scopeId; private static final Pattern FRESH_NAME_SUFFIX = Pattern.compile("[\\$_]\\d+$"); - public JsScope(JsScope parent, @NotNull String description, @Nullable String scopeId) { - this.scopeId = scopeId; + public JsScope(JsScope parent, @NotNull String description) { this.description = description; this.parent = parent; } @@ -61,12 +57,21 @@ public abstract class JsScope { protected JsScope(@NotNull String description) { this.description = description; parent = null; - scopeId = null; + } + + public Set getTemporaryNames() { + if (temporaryNames == null) { + return Collections.emptySet(); + } + if (readonlyTemporaryNames == null) { + readonlyTemporaryNames = Collections.unmodifiableSet(temporaryNames.keySet()); + } + return readonlyTemporaryNames; } @NotNull public JsScope innerObjectScope(@NotNull String scopeName) { - return JsObjectScope(this, scopeName); + return new JsObjectScope(this, scopeName); } /** @@ -97,9 +102,15 @@ public abstract class JsScope { return doCreateName(ident); } - private String getNextTempName() { - // introduced by the compiler - return "tmp$" + (scopeId != null ? scopeId + "$" : "") + tempIndex++; + @NotNull + public JsName declareTemporaryName(@NotNull String suggestedName) { + assert !suggestedName.isEmpty(); + JsName name = new JsName(this, suggestedName, true); + if (temporaryNames == null) { + temporaryNames = new WeakHashMap(); + } + temporaryNames.put(name, this); + return name; } /** @@ -110,7 +121,7 @@ public abstract class JsScope { */ @NotNull public JsName declareTemporary() { - return declareFreshName(getNextTempName()); + return declareTemporaryName("tmp$"); } /** @@ -171,7 +182,7 @@ public abstract class JsScope { @NotNull protected JsName doCreateName(@NotNull String ident) { - JsName name = new JsName(this, ident); + JsName name = new JsName(this, ident, false); names = Maps.put(names, ident, name); return name; } diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsVars.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsVars.java index c8a4333db5d..ff69c0092e5 100644 --- a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsVars.java +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsVars.java @@ -49,7 +49,7 @@ public class JsVars extends SourceInfoAwareJsNode implements JsStatement, Iterab * A var declared using the JavaScript var statement. */ public static class JsVar extends SourceInfoAwareJsNode implements HasName { - private final JsName name; + private JsName name; private JsExpression initExpression; public JsVar(JsName name) { @@ -70,6 +70,11 @@ public class JsVars extends SourceInfoAwareJsNode implements JsStatement, Iterab return name; } + @Override + public void setName(JsName name) { + this.name = name; + } + @Override public Symbol getSymbol() { return name; diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/jsScopes.kt b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/jsScopes.kt index a7833920e81..9b278f5ac9c 100644 --- a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/jsScopes.kt +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/jsScopes.kt @@ -18,15 +18,13 @@ package com.google.dart.compiler.backend.js.ast import java.util.Stack -fun JsObjectScope(parent: JsScope, description: String): JsObjectScope = JsObjectScope(parent, description, null) +class JsObjectScope(parent: JsScope, description: String) : JsScope(parent, description) -class JsObjectScope(parent: JsScope, description: String, scopeId: String?) : JsScope(parent, description, scopeId) - -object JsDynamicScope : JsScope(null, "Scope for dynamic declarations", null) { - override fun doCreateName(name: String) = JsName(this, name) +object JsDynamicScope : JsScope(null, "Scope for dynamic declarations") { + override fun doCreateName(name: String) = JsName(this, name, false) } -open class JsFunctionScope(parent: JsScope, description: String) : JsScope(parent, description, null) { +open class JsFunctionScope(parent: JsScope, description: String) : JsScope(parent, description) { private val labelScopes = Stack() private val topLabelScope: LabelScope? @@ -50,7 +48,7 @@ open class JsFunctionScope(parent: JsScope, description: String) : JsScope(paren open fun findLabel(label: String): JsName? = topLabelScope?.findName(label) - private inner class LabelScope(parent: LabelScope?, val ident: String) : JsScope(parent, "Label scope for $ident", null) { + private inner class LabelScope(parent: LabelScope?, val ident: String) : JsScope(parent, "Label scope for $ident") { val labelName: JsName init { @@ -60,7 +58,7 @@ open class JsFunctionScope(parent: JsScope, description: String) : JsScope(paren else -> ident } - labelName = JsName(this@JsFunctionScope, freshIdent) + labelName = JsName(this@JsFunctionScope, freshIdent, false) } override fun findOwnName(name: String): JsName? = diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsCallChecker.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsCallChecker.kt index 27fe7f788ae..3df8635ea1b 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsCallChecker.kt +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsCallChecker.kt @@ -87,10 +87,10 @@ class JsCallChecker( val errorReporter = JsCodeErrorReporter(argument, code, context.trace) try { - val parserScope = JsFunctionScope(JsRootScope(JsProgram("")), "") + val parserScope = JsFunctionScope(JsRootScope(JsProgram()), "") val statements = parse(code, errorReporter, parserScope) - if (statements.size == 0) { + if (statements.isEmpty()) { context.trace.report(ErrorsJs.JSCODE_NO_JAVASCRIPT_PRODUCED.on(argument)) } } catch (e: AbortParsingException) { diff --git a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/FunctionReader.kt b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/FunctionReader.kt index 9e74a8b5781..8f975b6b2f7 100644 --- a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/FunctionReader.kt +++ b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/FunctionReader.kt @@ -148,7 +148,7 @@ class FunctionReader(private val context: TranslationContext) { offset++ } - val function = parseFunction(source, offset, ThrowExceptionOnErrorReporter, JsRootScope(JsProgram(""))) + val function = parseFunction(source, offset, ThrowExceptionOnErrorReporter, JsRootScope(JsProgram())) val moduleName = getExternalModuleName(descriptor)!! val moduleReference = context.getModuleExpressionFor(descriptor) ?: getRootPackage() diff --git a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/clean/RedundantBindElimination.kt b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/clean/RedundantBindElimination.kt index 0a2755c6ad0..e5140b6aaf2 100644 --- a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/clean/RedundantBindElimination.kt +++ b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/clean/RedundantBindElimination.kt @@ -35,12 +35,10 @@ class RedundantBindElimination(private val root: JsBlock) { } private fun tryEliminate(invocation: JsInvocation) { - val qualifier = invocation.qualifier - if (qualifier !is JsInvocation) return + val qualifier = invocation.qualifier as? JsInvocation ?: return - val outerQualifier = qualifier.qualifier - if (outerQualifier !is JsNameRef) return - val name = outerQualifier.name?.ident ?: outerQualifier.ident + val outerQualifier = qualifier.qualifier as? JsNameRef ?: return + val name = outerQualifier.ident if (name != "bind") return val qualifierReplacement = outerQualifier.qualifier ?: return diff --git a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/clean/resolveTemporaryNames.kt b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/clean/resolveTemporaryNames.kt new file mode 100644 index 00000000000..830c40e30b1 --- /dev/null +++ b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/clean/resolveTemporaryNames.kt @@ -0,0 +1,116 @@ +/* + * Copyright 2010-2016 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. + */ + +/* + * Copyright 2010-2016 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.inline.clean + +import com.google.dart.compiler.backend.js.ast.* +import com.google.dart.compiler.backend.js.ast.metadata.continuationInterfaceRef +import com.google.dart.compiler.backend.js.ast.metadata.interceptResumeRef +import com.google.dart.compiler.backend.js.ast.metadata.suspendObjectRef +import org.jetbrains.kotlin.js.inline.util.collectReferencedTemporaryNames + +fun JsNode.resolveTemporaryNames() { + val scopeTree = ScopeCollector().let { + it.accept(this) + it.scopeTree + } + + val renamings = scopeTree.resolveNames() + accept(object : RecursiveJsVisitor() { + override fun visitElement(node: JsNode) { + super.visitElement(node) + if (node is HasName) { + val name = node.name + if (name != null) { + renamings[name]?.let { node.name = it } + } + } + } + + override fun visitFunction(x: JsFunction) { + x.continuationInterfaceRef?.let { accept(it) } + x.interceptResumeRef?.let { accept(it) } + x.suspendObjectRef?.let { accept(it) } + super.visitFunction(x) + } + }) +} + +private fun Map>.resolveNames(): Map { + val replacements = mutableMapOf() + fun traverse(scope: JsScope) { + for (temporaryName in scope.temporaryNames.sortedBy { it.ordinal }) { + replacements[temporaryName] = scope.declareFreshName(temporaryName.ident).apply { copyMetadataFrom(temporaryName) } + } + this[scope]!!.forEach(::traverse) + } + + val roots = keys.toMutableSet() + values.forEach { roots -= it } + + for (root in roots) { + traverse(root) + } + + return replacements +} + +private class ScopeCollector() : RecursiveJsVisitor() { + val scopeTree = mutableMapOf>() + + override fun visitCatch(x: JsCatch) { + recordScope(x.scope) + super.visitCatch(x) + } + + override fun visitFunction(x: JsFunction) { + recordScope(x.scope) + super.visitFunction(x) + } + override fun visitElement(node: JsNode) { + if (node is HasName) { + node.name?.let { recordScope(it.enclosing) } + } + super.visitElement(node) + } + + private fun recordScope(scope: JsScope) { + if (scope !in scopeTree) { + scopeTree[scope] = mutableSetOf() + val parent = scope.parent + if (parent != null) { + recordScope(parent) + scopeTree[parent]!!.add(scope) + } + } + } +} \ No newline at end of file diff --git a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/context/NamingContext.kt b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/context/NamingContext.kt index 008eed0a950..b448769fa0b 100644 --- a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/context/NamingContext.kt +++ b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/context/NamingContext.kt @@ -50,7 +50,11 @@ class NamingContext( fun getFreshName(candidate: String): JsName = scope.declareFreshName(candidate) - fun getFreshName(candidate: JsName): JsName = getFreshName(candidate.ident) + + fun getTemporaryName(candidate: String): JsName = scope.declareTemporaryName(candidate) + + fun getFreshName(candidate: JsName) = if (candidate.isTemporary) getTemporaryName(candidate.ident) else getFreshName(candidate.ident) + fun newVar(name: JsName, value: JsExpression? = null) { val vars = JsAstUtils.newVar(name, value) diff --git a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/collectUtils.kt b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/collectUtils.kt index fec5bdfa096..d58078ba237 100644 --- a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/collectUtils.kt +++ b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/collectUtils.kt @@ -18,12 +18,9 @@ package org.jetbrains.kotlin.js.inline.util import com.google.dart.compiler.backend.js.ast.* import com.google.dart.compiler.backend.js.ast.metadata.staticRef - import org.jetbrains.kotlin.js.inline.util.collectors.InstanceCollector import org.jetbrains.kotlin.js.translate.expression.InlineMetadata import org.jetbrains.kotlin.js.translate.utils.JsAstUtils -import org.jetbrains.kotlin.js.translate.utils.name -import java.util.* fun collectFunctionReferencesInside(scope: JsNode): List = collectReferencedNames(scope).filter { it.staticRef is JsFunction } @@ -87,7 +84,7 @@ fun collectUsedNames(scope: JsNode): Set { } fun collectDefinedNames(scope: JsNode): Set { - val names: MutableMap = HashMap() + val names = mutableSetOf() object : RecursiveJsVisitor() { override fun visit(x: JsVars.JsVar) { @@ -95,7 +92,7 @@ fun collectDefinedNames(scope: JsNode): Set { if (initializer != null) { accept(initializer) } - addNameIfNeeded(x.name) + names += x.name } override fun visitExpressionStatement(x: JsExpressionStatement) { @@ -103,7 +100,7 @@ fun collectDefinedNames(scope: JsNode): Set { if (expression is JsFunction) { val name = expression.name if (name != null) { - addNameIfNeeded(name) + names += name } } super.visitExpressionStatement(x) @@ -112,16 +109,9 @@ fun collectDefinedNames(scope: JsNode): Set { // Skip function expression, since it does not introduce name in scope of containing function. // The only exception is function statement, that is handled with the code above. override fun visitFunction(x: JsFunction) { } - - private fun addNameIfNeeded(name: JsName) { - val ident = name.ident - val nameCollected = names[ident] - assert(nameCollected == null || nameCollected === name) { "ambiguous identifier $name" } - names[ident] = name - } }.accept(scope) - return names.values.toSet() + return names } fun JsFunction.collectFreeVariables() = collectUsedNames(body) - collectDefinedNames(body) - parameters.map { it.name } diff --git a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/namingUtils.kt b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/namingUtils.kt index 6b2f004aba1..c2ff617030e 100644 --- a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/namingUtils.kt +++ b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/namingUtils.kt @@ -58,8 +58,8 @@ fun renameLocalNames( function: JsFunction ) { for (name in collectDefinedNames(function.body)) { - val freshName = context.getFreshName(name).apply { staticRef = name.staticRef } - context.replaceName(name, freshName.makeRef()) + val temporaryName = context.getTemporaryName(name.ident).apply { staticRef = name.staticRef } + context.replaceName(name, temporaryName.makeRef()) } } diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/optimizer/BasicOptimizerTest.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/optimizer/BasicOptimizerTest.kt index 10ee773a611..41b162479fa 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/optimizer/BasicOptimizerTest.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/optimizer/BasicOptimizerTest.kt @@ -64,7 +64,7 @@ abstract class BasicOptimizerTest(private var basePath: String) { } private fun checkOptimizer(unoptimizedCode: String, optimizedCode: String) { - val parserScope = JsFunctionScope(JsRootScope(JsProgram("")), "") + val parserScope = JsFunctionScope(JsRootScope(JsProgram()), "") val unoptimizedAst = parse(unoptimizedCode, errorReporter, parserScope) updateMetadata(unoptimizedCode, unoptimizedAst) diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/facade/K2JSTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/facade/K2JSTranslator.java index cae7ae88b45..3318f5b8bcd 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/facade/K2JSTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/facade/K2JSTranslator.java @@ -28,6 +28,7 @@ import org.jetbrains.kotlin.js.facade.exceptions.TranslationException; import org.jetbrains.kotlin.js.inline.JsInliner; import org.jetbrains.kotlin.js.translate.context.StaticContext; import org.jetbrains.kotlin.js.inline.clean.RemoveUnusedImportsKt; +import org.jetbrains.kotlin.js.inline.clean.ResolveTemporaryNamesKt; import org.jetbrains.kotlin.js.translate.context.TranslationContext; import org.jetbrains.kotlin.js.translate.general.Translation; import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus; @@ -85,6 +86,7 @@ public final class K2JSTranslator { if (hasError(diagnostics)) return new TranslationResult.Fail(diagnostics); JsProgram program = JsInliner.process(context); + ResolveTemporaryNamesKt.resolveTemporaryNames(program); ProgressIndicatorAndCompilationCanceledStatus.checkCanceled(); if (hasError(diagnostics)) return new TranslationResult.Fail(diagnostics); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/AliasingContext.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/AliasingContext.java index c4a77b2560d..c83fbaaaf34 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/AliasingContext.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/AliasingContext.java @@ -17,7 +17,6 @@ package org.jetbrains.kotlin.js.translate.context; import com.google.dart.compiler.backend.js.ast.JsExpression; -import gnu.trove.THashMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.descriptors.DeclarationDescriptor; @@ -32,10 +31,10 @@ public class AliasingContext { return new AliasingContext(null, null, null); } - @NotNull + @Nullable private final Map aliasesForDescriptors; - @NotNull + @Nullable private final Map aliasesForExpressions; @Nullable @@ -47,8 +46,8 @@ public class AliasingContext { @Nullable Map aliasesForExpressions ) { this.parent = parent; - this.aliasesForDescriptors = aliasesForDescriptors == null ? Collections.emptyMap() : aliasesForDescriptors; - this.aliasesForExpressions = aliasesForExpressions == null ? Collections.emptyMap() : aliasesForExpressions; + this.aliasesForDescriptors = aliasesForDescriptors; + this.aliasesForExpressions = aliasesForExpressions; } @NotNull @@ -74,13 +73,13 @@ public class AliasingContext { @Nullable public JsExpression getAliasForDescriptor(@NotNull DeclarationDescriptor descriptor) { // these aliases cannot be shared and applicable only in current context - JsExpression alias = aliasesForDescriptors.get(descriptor.getOriginal()); + JsExpression alias = aliasesForDescriptors != null ? aliasesForDescriptors.get(descriptor.getOriginal()) : null; return alias != null || parent == null ? alias : parent.getAliasForDescriptor(descriptor); } @Nullable public JsExpression getAliasForExpression(@NotNull KtExpression element) { - JsExpression alias = aliasesForExpressions.get(element); + JsExpression alias = aliasesForExpressions != null ? aliasesForExpressions.get(element) : null; return alias != null || parent == null ? alias : parent.getAliasForExpression(element); } } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/DeclarationExporter.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/DeclarationExporter.kt index 978c2017b06..c401225fa04 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/DeclarationExporter.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/DeclarationExporter.kt @@ -105,7 +105,7 @@ internal class DeclarationExporter(val context: StaticContext) { val setterBody: JsExpression = if (simpleProperty) { val statements = mutableListOf() val function = JsFunction(context.rootFunction.scope, JsBlock(statements), "$declaration setter") - val valueName = function.scope.declareFreshName("value") + val valueName = function.scope.declareTemporaryName("value") function.parameters += JsParameter(valueName) statements += assignment(context.getInnerNameForDescriptor(declaration).makeRef(), valueName.makeRef()).makeStmt() function @@ -125,7 +125,7 @@ internal class DeclarationExporter(val context: StaticContext) { } var name = localPackageNames[packageName] if (name == null) { - name = context.rootFunction.scope.declareFreshName("package$" + packageName.shortName().asString()) + name = context.rootFunction.scope.declareTemporaryName("package$" + packageName.shortName().asString()) localPackageNames.put(packageName, name) val parentRef = getLocalPackageReference(packageName.parent()) diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/Namer.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/Namer.java index d9b84e1c829..bed7a68a59b 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/Namer.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/Namer.java @@ -43,7 +43,6 @@ import java.util.Collection; import java.util.Collections; import java.util.List; -import static com.google.dart.compiler.backend.js.ast.JsScopesKt.JsObjectScope; import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.pureFqn; import static org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils.getModuleName; @@ -127,7 +126,9 @@ public final class Namer { qualifier = fqNameParent.asString(); } - String mangledName = new NameSuggestion().suggest(functionDescriptor).getNames().get(0); + SuggestedName suggestedName = new NameSuggestion().suggest(functionDescriptor); + assert suggestedName != null : "Suggested name can be null only for module descriptors: " + functionDescriptor; + String mangledName = suggestedName.getNames().get(0); return StringUtil.join(Arrays.asList(moduleName, qualifier, mangledName), "."); } @@ -226,7 +227,7 @@ public final class Namer { private final JsName isTypeName; private Namer(@NotNull JsScope rootScope) { - kotlinScope = JsObjectScope(rootScope, "Kotlin standard object"); + kotlinScope = new JsObjectScope(rootScope, "Kotlin standard object"); callGetProperty = kotlin("callGetter"); callSetProperty = kotlin("callSetter"); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java index f54d49df654..3eef0c91e5c 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java @@ -64,7 +64,7 @@ public final class StaticContext { @NotNull BindingTrace bindingTrace, @NotNull JsConfig config, @NotNull ModuleDescriptor moduleDescriptor) { - JsProgram program = new JsProgram("main"); + JsProgram program = new JsProgram(); Namer namer = Namer.newInstance(program.getRootScope()); JsFunction rootFunction = JsAstUtils.createFunctionWithEmptyBody(program.getScope()); return new StaticContext(program, rootFunction, bindingTrace, namer, program.getRootScope(), config, moduleDescriptor); @@ -146,6 +146,9 @@ public final class StaticContext { @NotNull private final Set classes = new LinkedHashSet(); + @NotNull + private final Map packageScopes = new HashMap(); + //TODO: too many parameters in constructor private StaticContext( @NotNull JsProgram program, @@ -165,7 +168,7 @@ public final class StaticContext { this.config = config; this.currentModule = moduleDescriptor; this.rootFunction = rootFunction; - rootPackageScope = new JsObjectScope(rootScope, "", "root-package"); + rootPackageScope = new JsObjectScope(rootScope, ""); JsName kotlinName = rootScope.declareName(Namer.KOTLIN_NAME); importedModules.put(new ImportedModuleKey(Namer.KOTLIN_LOWER_NAME, null), @@ -362,10 +365,15 @@ public final class StaticContext { JsName name = nameCache.get(suggested.getDescriptor()); if (name == null) { String baseName = suggested.getNames().get(0); - if (!DescriptorUtils.isDescriptorWithLocalVisibility(suggested.getDescriptor())) { - baseName += "_0"; + if (suggested.getDescriptor() instanceof LocalVariableDescriptor) { + name = scope.declareTemporaryName(baseName); + } + else { + if (!DescriptorUtils.isDescriptorWithLocalVisibility(suggested.getDescriptor())) { + baseName += "_0"; + } + name = scope.declareFreshName(baseName); } - name = scope.declareFreshName(baseName); } nameCache.put(suggested.getDescriptor(), name); names.add(name); @@ -425,7 +433,7 @@ public final class StaticContext { // since local scope inherited from global scope. // TODO: remove prefix when problem with scopes is solved - JsName result = rootFunction.getScope().declareFreshName("imported$" + suggestedName); + JsName result = rootFunction.getScope().declareTemporaryName(suggestedName); MetadataProperties.setImported(result, true); importStatements.add(JsAstUtils.newVar(result, declaration)); return result; @@ -436,7 +444,7 @@ public final class StaticContext { ModuleDescriptor module = DescriptorUtilsKt.getModule(descriptor); return module != currentModule ? importDeclaration(suggestedName, getQualifiedReference(descriptor)) : - rootFunction.getScope().declareFreshName(suggestedName); + rootFunction.getScope().declareTemporaryName(suggestedName); } private final class InnerNameGenerator extends Generator { @@ -529,6 +537,21 @@ public final class StaticContext { return suggestedName; } + private JsScope getScopeForPackage(FqName fqName) { + JsScope scope = packageScopes.get(fqName); + if (scope == null) { + if (fqName.isRoot()) { + scope = new JsRootScope(program); + } + else { + JsScope parentScope = getScopeForPackage(fqName.parent()); + scope = parentScope.innerObjectScope(fqName.shortName().asString()); + } + packageScopes.put(fqName, scope); + } + return scope; + } + private final class ScopeGenerator extends Generator { public ScopeGenerator() { @@ -539,7 +562,7 @@ public final class StaticContext { return null; } if (getSuperclass((ClassDescriptor) descriptor) == null) { - JsFunction function = new JsFunction(rootFunction.getScope(), new JsBlock(), descriptor.toString()); + JsFunction function = new JsFunction(new JsRootScope(program), new JsBlock(), descriptor.toString()); scopeToFunction.put(function.getScope(), function); return function.getScope(); } @@ -585,6 +608,17 @@ public final class StaticContext { return correspondingFunction.getScope(); } }; + Rule scopeForPackage = new Rule() { + @Nullable + @Override + public JsScope apply(@NotNull DeclarationDescriptor descriptor) { + if (!(descriptor instanceof PackageFragmentDescriptor)) return null; + + PackageFragmentDescriptor packageDescriptor = (PackageFragmentDescriptor) descriptor; + return getScopeForPackage(packageDescriptor.getFqName()); + } + }; + addRule(scopeForPackage); addRule(createFunctionObjectsForCallableDescriptors); addRule(generateNewScopesForClassesWithNoAncestors); addRule(generateInnerScopesForDerivedClasses); @@ -625,7 +659,7 @@ public final class StaticContext { ImportedModule module = importedModules.get(key); if (module == null) { - JsName internalName = rootScope.declareFreshName(Namer.LOCAL_MODULE_PREFIX + Namer.suggestedModuleName(baseName)); + JsName internalName = rootScope.declareTemporaryName(Namer.LOCAL_MODULE_PREFIX + Namer.suggestedModuleName(baseName)); JsName plainName = descriptor != null ? rootScope.declareName(getPlainId(descriptor)) : null; module = new ImportedModule(baseName, internalName, plainName != null ? pureFqn(plainName, null) : null); importedModules.put(key, module); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TranslationContext.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TranslationContext.java index 47686d8544d..6a5fa0003ef 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TranslationContext.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TranslationContext.java @@ -607,7 +607,7 @@ public class TranslationContext { @NotNull public JsName createGlobalName(@NotNull String suggestedName) { - return staticContext.getRootFunction().getScope().declareFreshName(suggestedName); + return staticContext.getRootFunction().getScope().declareTemporaryName(suggestedName); } @NotNull diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/UsageTracker.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/UsageTracker.kt index 6e0cfa2b28a..949bc37caef 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/UsageTracker.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/UsageTracker.kt @@ -49,9 +49,6 @@ class UsageTracker( // local named function if (descriptor is FunctionDescriptor && descriptor.visibility == Visibilities.LOCAL) { - assert(descriptor.isCoroutineLambda || !descriptor.getName().isSpecial) { - "Function with special name can not be captured, descriptor: $descriptor" - } captureIfNeed(descriptor) } // local variable diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassTranslator.kt index 21b01ab0f89..9173af1f363 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassTranslator.kt @@ -150,8 +150,8 @@ class ClassTranslator private constructor( } private fun addEnumClassParameters(constructorFunction: JsFunction) { - val nameParamName = constructorFunction.scope.declareFreshName("name") - val ordinalParamName = constructorFunction.scope.declareFreshName("ordinal") + val nameParamName = constructorFunction.scope.declareTemporaryName("name") + val ordinalParamName = constructorFunction.scope.declareTemporaryName("ordinal") constructorFunction.parameters.addAll(0, listOf(JsParameter(nameParamName), JsParameter(ordinalParamName))) constructorFunction.body.statements += JsAstUtils.assignmentToThisField(Namer.ENUM_NAME_FIELD, nameParamName.makeRef()) @@ -232,8 +232,8 @@ class ClassTranslator private constructor( val leadingArgs = mutableListOf() if (descriptor.kind == ClassKind.ENUM_CLASS) { - val nameParamName = constructorInitializer.scope.declareFreshName("name") - val ordinalParamName = constructorInitializer.scope.declareFreshName("ordinal") + val nameParamName = constructorInitializer.scope.declareTemporaryName("name") + val ordinalParamName = constructorInitializer.scope.declareTemporaryName("ordinal") constructorInitializer.parameters.addAll(0, listOf(JsParameter(nameParamName), JsParameter(ordinalParamName))) leadingArgs += listOf(nameParamName.makeRef(), ordinalParamName.makeRef()) } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/EnumTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/EnumTranslator.kt index cc2fb5d9aa2..ffb49d22a20 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/EnumTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/EnumTranslator.kt @@ -46,7 +46,7 @@ class EnumTranslator( private fun generateValueOfFunction() { val function = createFunction(getEnumFunction(DescriptorUtils.ENUM_VALUE_OF)) - val nameParam = function.scope.declareFreshName("name") + val nameParam = function.scope.declareTemporaryName("name") function.parameters += JsParameter(nameParam) val clauses = entries.map { entry -> diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/PackageDeclarationTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/PackageDeclarationTranslator.java index 64fc843a453..92c2a70b24c 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/PackageDeclarationTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/PackageDeclarationTranslator.java @@ -17,13 +17,13 @@ package org.jetbrains.kotlin.js.translate.declaration; import org.jetbrains.annotations.NotNull; -import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor; import org.jetbrains.kotlin.js.facade.exceptions.TranslationRuntimeException; import org.jetbrains.kotlin.js.translate.context.TranslationContext; import org.jetbrains.kotlin.js.translate.general.AbstractTranslator; +import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils; +import org.jetbrains.kotlin.js.translate.utils.BindingUtils; +import org.jetbrains.kotlin.psi.KtDeclaration; import org.jetbrains.kotlin.psi.KtFile; -import org.jetbrains.kotlin.resolve.BindingContext; -import org.jetbrains.kotlin.resolve.BindingContextUtils; import java.util.Collection; @@ -42,13 +42,14 @@ public final class PackageDeclarationTranslator extends AbstractTranslator { private void translate() { for (KtFile file : files) { - PackageFragmentDescriptor packageFragment = - BindingContextUtils.getNotNull(context().bindingContext(), BindingContext.FILE_TO_PACKAGE_FRAGMENT, file); - - PackageTranslator translator = PackageTranslator.create(packageFragment, context()); + FileDeclarationVisitor fileVisitor = new FileDeclarationVisitor(context()); try { - translator.translate(file); + for (KtDeclaration declaration : file.getDeclarations()) { + if (!AnnotationsUtils.isPredefinedObject(BindingUtils.getDescriptorForElement(bindingContext(), declaration))) { + declaration.accept(fileVisitor, context()); + } + } } catch (TranslationRuntimeException e) { throw e; diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/PackageTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/PackageTranslator.java deleted file mode 100644 index 5d15c43519e..00000000000 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/PackageTranslator.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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.declaration; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor; -import org.jetbrains.kotlin.js.translate.context.TranslationContext; -import org.jetbrains.kotlin.js.translate.general.AbstractTranslator; -import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils; -import org.jetbrains.kotlin.js.translate.utils.BindingUtils; -import org.jetbrains.kotlin.psi.KtDeclaration; -import org.jetbrains.kotlin.psi.KtFile; - -final class PackageTranslator extends AbstractTranslator { - static PackageTranslator create( - @NotNull PackageFragmentDescriptor descriptor, - @NotNull TranslationContext context - ) { - TranslationContext newContext = context.newDeclaration(descriptor); - FileDeclarationVisitor visitor = new FileDeclarationVisitor(newContext); - return new PackageTranslator(newContext, visitor); - } - - private final FileDeclarationVisitor visitor; - - private PackageTranslator( - @NotNull TranslationContext context, - @NotNull FileDeclarationVisitor visitor - ) { - super(context); - this.visitor = visitor; - } - - public void translate(KtFile file) { - for (KtDeclaration declaration : file.getDeclarations()) { - if (!AnnotationsUtils.isPredefinedObject(BindingUtils.getDescriptorForElement(bindingContext(), declaration))) { - declaration.accept(visitor, context()); - } - } - } -} diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/ExpressionVisitor.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/ExpressionVisitor.java index 560e75a0d4f..35248732047 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/ExpressionVisitor.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/ExpressionVisitor.java @@ -460,13 +460,14 @@ public final class ExpressionVisitor extends TranslatorVisitor { JsExpression alias = new LiteralFunctionTranslator(context).translate(expression, null, null); FunctionDescriptor descriptor = getFunctionDescriptor(context.bindingContext(), expression); - JsName name = context.getNameForDescriptor(descriptor); + JsNameRef nameRef = (JsNameRef) ReferenceTranslator.translateAsValueReference(descriptor, context); + assert nameRef.getName() != null; if (InlineUtil.isInline(descriptor)) { - MetadataProperties.setStaticRef(name, alias); + MetadataProperties.setStaticRef(nameRef.getName(), alias); } boolean isExpression = BindingContextUtilsKt.isUsedAsExpression(expression, context.bindingContext()); - JsNode result = isExpression ? alias : JsAstUtils.newVar(name, alias); + JsNode result = isExpression ? alias : JsAstUtils.newVar(nameRef.getName(), alias); return result.source(expression); } @@ -552,10 +553,7 @@ public final class ExpressionVisitor extends TranslatorVisitor { ResolvedCall superCall = BindingUtils.getSuperCall(context.bindingContext(), expression.getObjectDeclaration()); if (superCall != null) { - assert context.getDeclarationDescriptor() != null : "This expression should be inside declaration: " + - PsiUtilsKt.getTextWithLocation(expression); - TranslationContext superCallContext = context.newDeclaration(context.getDeclarationDescriptor()); - closureArgs.addAll(CallArgumentTranslator.translate(superCall, null, superCallContext).getTranslateArguments()); + closureArgs.addAll(CallArgumentTranslator.translate(superCall, null, context).getTranslateArguments()); } return new JsNew(constructor, closureArgs); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/FunctionTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/FunctionTranslator.java index a3e14e0b1db..b49e4a323e9 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/FunctionTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/FunctionTranslator.java @@ -26,6 +26,7 @@ import org.jetbrains.kotlin.js.translate.context.AliasingContext; import org.jetbrains.kotlin.js.translate.context.Namer; import org.jetbrains.kotlin.js.translate.context.TranslationContext; import org.jetbrains.kotlin.js.translate.general.AbstractTranslator; +import org.jetbrains.kotlin.js.translate.utils.JsAstUtils; import org.jetbrains.kotlin.js.translate.utils.TranslationUtils; import org.jetbrains.kotlin.psi.KtDeclarationWithBody; import org.jetbrains.kotlin.psi.KtLambdaExpression; @@ -78,17 +79,27 @@ public final class FunctionTranslator extends AbstractTranslator { @NotNull private TranslationContext getFunctionBodyContext() { - AliasingContext aliasingContext; + Map aliases = new HashMap(); if (isExtensionFunction()) { DeclarationDescriptor expectedReceiverDescriptor = descriptor.getExtensionReceiverParameter(); assert expectedReceiverDescriptor != null; extensionFunctionReceiverName = functionObject.getScope().declareName(Namer.getReceiverParameterName()); //noinspection ConstantConditions - aliasingContext = context().aliasingContext().inner(expectedReceiverDescriptor, extensionFunctionReceiverName.makeRef()); + aliases.put(expectedReceiverDescriptor, extensionFunctionReceiverName.makeRef()); } - else { - aliasingContext = null; + + LocalFunctionCollector functionCollector = new LocalFunctionCollector(context().bindingContext()); + functionDeclaration.acceptChildren(functionCollector, null); + for (FunctionDescriptor localFunction : functionCollector.getFunctions()) { + String localIdent = localFunction.getName().isSpecial() ? "lambda" : localFunction.getName().asString(); + JsName localName = functionObject.getScope().getParent().declareTemporaryName(localIdent); + JsExpression alias = JsAstUtils.pureFqn(localName, null); + aliases.put(localFunction, alias); } + + AliasingContext aliasingContext = !aliases.isEmpty() ? context().aliasingContext().withDescriptorsAliased(aliases) : null; + + return context().newFunctionBody(functionObject, aliasingContext, descriptor); } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/LiteralFunctionTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/LiteralFunctionTranslator.kt index ef585503fb1..f7f87fe673b 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/LiteralFunctionTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/LiteralFunctionTranslator.kt @@ -264,7 +264,7 @@ private fun moveCapturedLocalInside(capturingFunction: JsFunction, capturedName: val capturedArgs = localFunAlias.arguments val scope = capturingFunction.getInnerFunction()?.scope!! - val freshNames = getFreshNamesInScope(scope, capturedArgs) + val freshNames = getTemporaryNamesInScope(scope, capturedArgs) val aliasCallArguments = freshNames.map { it.makeRef() } val alias = JsInvocation(localFunAlias.qualifier, aliasCallArguments) @@ -279,7 +279,7 @@ private fun declareAliasInsideFunction(function: JsFunction, name: JsName, alias function.getInnerFunction()?.addDeclaration(name, alias) } -private fun getFreshNamesInScope(scope: JsScope, suggested: List): List { +private fun getTemporaryNamesInScope(scope: JsScope, suggested: List): List { val freshNames = arrayListOf() for (suggestion in suggested) { @@ -288,7 +288,7 @@ private fun getFreshNamesInScope(scope: JsScope, suggested: List): } val ident = suggestion.ident - val name = scope.declareFreshName(ident) + val name = scope.declareTemporaryName(ident) freshNames.add(name) } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/LocalFunctionCollector.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/LocalFunctionCollector.kt new file mode 100644 index 00000000000..1ad43ed5b03 --- /dev/null +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/LocalFunctionCollector.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2010-2016 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.expression + +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.js.translate.utils.BindingUtils +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.resolve.BindingContext + +class LocalFunctionCollector(val bindingContext: BindingContext) : KtVisitorVoid() { + val functions = mutableSetOf() + + override fun visitExpression(expression: KtExpression) { + if (expression is KtDeclarationWithBody) { + functions += BindingUtils.getFunctionDescriptor(bindingContext, expression) + } + else { + expression.acceptChildren(this, null) + } + } + + override fun visitClassOrObject(classOrObject: KtClassOrObject) { + // skip + } +} \ No newline at end of file diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallExpressionTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallExpressionTranslator.java index 301e004c11a..3ea8a664645 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallExpressionTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallExpressionTranslator.java @@ -122,7 +122,7 @@ public final class CallExpressionTranslator extends AbstractCallExpressionTransl // but no new global ones. JsScope currentScope = context().scope(); assert currentScope instanceof JsFunctionScope : "Usage of js outside of function is unexpected"; - JsScope temporaryRootScope = new JsRootScope(new JsProgram("")); + JsScope temporaryRootScope = new JsRootScope(new JsProgram()); JsScope scope = new DelegatingJsFunctionScopeWithTemporaryParent((JsFunctionScope) currentScope, temporaryRootScope); return ParserUtilsKt.parse(jsCode, ThrowExceptionOnErrorReporter.INSTANCE, scope); } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallableReferenceTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallableReferenceTranslator.kt index a1dde7568a4..f2be1fc580c 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallableReferenceTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallableReferenceTranslator.kt @@ -153,7 +153,7 @@ object CallableReferenceTranslator { } val function = context.createRootScopedFunction(setter) - val valueParam = function.scope.declareFreshName("value") + val valueParam = function.scope.declareTemporaryName("value") function.parameters += JsParameter(valueParam) val expression = if (TranslationUtils.shouldAccessViaFunctions(descriptor)) { diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/astUtils.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/astUtils.kt index acf53ead861..9e5d7865b50 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/astUtils.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/astUtils.kt @@ -24,7 +24,7 @@ fun JsFunction.addStatement(stmt: JsStatement) { } fun JsFunction.addParameter(identifier: String, index: Int? = null): JsParameter { - val name = scope.declareFreshName(identifier) + val name = scope.declareTemporaryName(identifier) val parameter = JsParameter(name) if (index == null) { diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/utils.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/utils.kt index b841a02b84e..82157ea4c9c 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/utils.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/utils.kt @@ -41,14 +41,14 @@ fun generateDelegateCall( val functionScope = context.getScopeForDescriptor(fromDescriptor) if (DescriptorUtils.isExtension(fromDescriptor)) { - val extensionFunctionReceiverName = functionScope.declareFreshName(Namer.getReceiverParameterName()) + val extensionFunctionReceiverName = functionScope.declareTemporaryName(Namer.getReceiverParameterName()) parameters.add(JsParameter(extensionFunctionReceiverName)) args.add(JsNameRef(extensionFunctionReceiverName)) } for (param in fromDescriptor.valueParameters) { val paramName = param.name.asString() - val jsParamName = functionScope.declareFreshName(paramName) + val jsParamName = functionScope.declareTemporaryName(paramName) parameters.add(JsParameter(jsParamName)) args.add(JsNameRef(jsParamName)) } diff --git a/js/js.translator/testData/_commonFiles/asserts.kt b/js/js.translator/testData/_commonFiles/asserts.kt index a5668657f19..786234eb818 100644 --- a/js/js.translator/testData/_commonFiles/asserts.kt +++ b/js/js.translator/testData/_commonFiles/asserts.kt @@ -6,29 +6,29 @@ fun fail(message: String? = null): Nothing = js("throw new Error(message)") fun assertEquals(expected: T, actual: T, message: String? = null) { if (expected != actual) { - val msg = if (message == null) "" else (" message = '" + message + "',") - fail("Unexpected value:$msg expected = '$expected', actual = '$actual'") + val msg = if (message == null) "" else ", message = '$message'" + fail("Unexpected value: expected = '$expected', actual = '$actual'$msg") } } fun assertNotEquals(illegal: T, actual: T, message: String? = null) { if (illegal == actual) { - val msg = if (message == null) "" else (" message = '" + message + "',") - fail("Illegal value:$msg illegal = '$illegal', actual = '$actual'") + val msg = if (message == null) "" else ", message = '$message'" + fail("Illegal value: illegal = '$illegal', actual = '$actual'$msg") } } fun assertSame(expected: T, actual: T, message: String? = null) { if (expected !== actual) { - val msg = if (message == null) "" else (" message = '" + message + "',") - fail("Expected same instances, got expected = '$expected', actual = '$actual'") + val msg = if (message == null) "" else ", message = '$message'" + fail("Expected same instances: expected = '$expected', actual = '$actual'$msg") } } fun assertArrayEquals(expected: Array, actual: Array, message: String? = null) { if (!arraysEqual(expected, actual)) { - val msg = if (message == null) "" else (" message = '" + message + "',") - fail("Unexpected array:$msg expected = '$expected', actual = '$actual'") + val msg = if (message == null) "" else ", message = '$message'" + fail("Unexpected array: expected = '$expected', actual = '$actual'$msg") } }