JS: use temporary names while translation, replace with fixed name during post-processing pass

This commit is contained in:
Alexey Andreev
2016-11-23 16:02:42 +03:00
parent 9c78301008
commit e54138b74f
39 changed files with 362 additions and 194 deletions
@@ -11,4 +11,6 @@ import com.google.dart.compiler.common.HasSymbol;
*/
public interface HasName extends HasSymbol {
JsName getName();
void setName(JsName name);
}
@@ -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
@@ -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;
}
@@ -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;
@@ -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;
}
}
@@ -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() {
@@ -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() {
@@ -28,9 +28,9 @@ public final class JsProgram extends SourceInfoAwareJsNode {
private final Map<String, JsStringLiteral> stringLiteralMap = new THashMap<String, JsStringLiteral>();
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);
}
@@ -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<String, JsName> names = Collections.emptyMap();
private Map<JsName, Object> temporaryNames;
private Set<JsName> 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<JsName> 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<JsName, Object>();
}
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;
}
@@ -49,7 +49,7 @@ public class JsVars extends SourceInfoAwareJsNode implements JsStatement, Iterab
* A var declared using the JavaScript <code>var</code> 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;
@@ -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<LabelScope>()
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? =
@@ -87,10 +87,10 @@ class JsCallChecker(
val errorReporter = JsCodeErrorReporter(argument, code, context.trace)
try {
val parserScope = JsFunctionScope(JsRootScope(JsProgram("<js checker>")), "<js fun>")
val parserScope = JsFunctionScope(JsRootScope(JsProgram()), "<js fun>")
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) {
@@ -148,7 +148,7 @@ class FunctionReader(private val context: TranslationContext) {
offset++
}
val function = parseFunction(source, offset, ThrowExceptionOnErrorReporter, JsRootScope(JsProgram("<inline>")))
val function = parseFunction(source, offset, ThrowExceptionOnErrorReporter, JsRootScope(JsProgram()))
val moduleName = getExternalModuleName(descriptor)!!
val moduleReference = context.getModuleExpressionFor(descriptor) ?: getRootPackage()
@@ -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
@@ -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<JsScope, Set<JsScope>>.resolveNames(): Map<JsName, JsName> {
val replacements = mutableMapOf<JsName, JsName>()
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<JsScope, MutableSet<JsScope>>()
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)
}
}
}
}
@@ -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)
@@ -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<JsName> =
collectReferencedNames(scope).filter { it.staticRef is JsFunction }
@@ -87,7 +84,7 @@ fun collectUsedNames(scope: JsNode): Set<JsName> {
}
fun collectDefinedNames(scope: JsNode): Set<JsName> {
val names: MutableMap<String, JsName> = HashMap()
val names = mutableSetOf<JsName>()
object : RecursiveJsVisitor() {
override fun visit(x: JsVars.JsVar) {
@@ -95,7 +92,7 @@ fun collectDefinedNames(scope: JsNode): Set<JsName> {
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<JsName> {
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<JsName> {
// 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 }
@@ -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())
}
}
@@ -64,7 +64,7 @@ abstract class BasicOptimizerTest(private var basePath: String) {
}
private fun checkOptimizer(unoptimizedCode: String, optimizedCode: String) {
val parserScope = JsFunctionScope(JsRootScope(JsProgram("<js checker>")), "<js fun>")
val parserScope = JsFunctionScope(JsRootScope(JsProgram()), "<js fun>")
val unoptimizedAst = parse(unoptimizedCode, errorReporter, parserScope)
updateMetadata(unoptimizedCode, unoptimizedAst)
@@ -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);
@@ -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<DeclarationDescriptor, JsExpression> aliasesForDescriptors;
@NotNull
@Nullable
private final Map<KtExpression, JsExpression> aliasesForExpressions;
@Nullable
@@ -47,8 +46,8 @@ public class AliasingContext {
@Nullable Map<KtExpression, JsExpression> aliasesForExpressions
) {
this.parent = parent;
this.aliasesForDescriptors = aliasesForDescriptors == null ? Collections.<DeclarationDescriptor, JsExpression>emptyMap() : aliasesForDescriptors;
this.aliasesForExpressions = aliasesForExpressions == null ? Collections.<KtExpression, JsExpression>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);
}
}
@@ -105,7 +105,7 @@ internal class DeclarationExporter(val context: StaticContext) {
val setterBody: JsExpression = if (simpleProperty) {
val statements = mutableListOf<JsStatement>()
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())
@@ -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");
@@ -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<ClassDescriptor> classes = new LinkedHashSet<ClassDescriptor>();
@NotNull
private final Map<FqName, JsScope> packageScopes = new HashMap<FqName, JsScope>();
//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>", "root-package");
rootPackageScope = new JsObjectScope(rootScope, "<root package>");
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<JsName> {
@@ -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<JsScope> {
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<JsScope> scopeForPackage = new Rule<JsScope>() {
@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);
@@ -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
@@ -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
@@ -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<JsExpression>()
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())
}
@@ -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 ->
@@ -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;
@@ -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());
}
}
}
}
@@ -460,13 +460,14 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
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<JsNode> {
ResolvedCall<FunctionDescriptor> 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);
@@ -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<DeclarationDescriptor, JsExpression> aliases = new HashMap<DeclarationDescriptor, JsExpression>();
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);
}
@@ -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<JsExpression>): List<JsName> {
private fun getTemporaryNamesInScope(scope: JsScope, suggested: List<JsExpression>): List<JsName> {
val freshNames = arrayListOf<JsName>()
for (suggestion in suggested) {
@@ -288,7 +288,7 @@ private fun getFreshNamesInScope(scope: JsScope, suggested: List<JsExpression>):
}
val ident = suggestion.ident
val name = scope.declareFreshName(ident)
val name = scope.declareTemporaryName(ident)
freshNames.add(name)
}
@@ -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<FunctionDescriptor>()
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
}
}
@@ -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("<js code>"));
JsScope temporaryRootScope = new JsRootScope(new JsProgram());
JsScope scope = new DelegatingJsFunctionScopeWithTemporaryParent((JsFunctionScope) currentScope, temporaryRootScope);
return ParserUtilsKt.parse(jsCode, ThrowExceptionOnErrorReporter.INSTANCE, scope);
}
@@ -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)) {
@@ -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) {
@@ -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))
}
+8 -8
View File
@@ -6,29 +6,29 @@ fun fail(message: String? = null): Nothing = js("throw new Error(message)")
fun <T> 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 <T> 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 <T> 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 <T> assertArrayEquals(expected: Array<out T>, actual: Array<out T>, 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")
}
}