From 6711db86eec44bb61155ce9fb9ff4f0f068a82b4 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Fri, 10 Feb 2017 18:15:35 +0300 Subject: [PATCH] JS: reworking translator pipeline to generate multiple fragment each for source file --- .../ast/metadata/metadataProperties.kt | 2 +- .../kotlin/js/facade/K2JSTranslator.java | 16 +- .../translate/context/DeclarationExporter.kt | 8 +- .../js/translate/context/StaticContext.java | 106 ++++------ .../translate/context/TranslationContext.java | 12 +- .../declaration/ClassModelGenerator.kt | 30 +++ .../declaration/InterfaceFunctionCopier.kt | 195 ------------------ .../PackageDeclarationTranslator.java | 62 ------ .../kotlin/js/translate/general/Merger.kt | 93 +++++++++ .../js/translate/general/Translation.java | 87 ++++++-- .../js/translate/utils/signatureUtils.kt | 74 +++++++ 11 files changed, 315 insertions(+), 370 deletions(-) create mode 100644 js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassModelGenerator.kt delete mode 100644 js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/InterfaceFunctionCopier.kt delete mode 100644 js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/PackageDeclarationTranslator.java create mode 100644 js/js.translator/src/org/jetbrains/kotlin/js/translate/general/Merger.kt create mode 100644 js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/signatureUtils.kt diff --git a/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/metadata/metadataProperties.kt b/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/metadata/metadataProperties.kt index 8a964e6e5bc..7fb2c10920e 100644 --- a/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/metadata/metadataProperties.kt +++ b/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/metadata/metadataProperties.kt @@ -100,7 +100,7 @@ var JsName.imported by MetadataProperty(default = false) var JsFunction.coroutineMetadata: CoroutineMetadata? by MetadataProperty(default = null) -class CoroutineMetadata( +data class CoroutineMetadata( val doResumeName: JsName, val stateName: JsName, val exceptionStateName: JsName, 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 07a4b3bd75b..e93c4bc5d69 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 @@ -16,8 +16,6 @@ package org.jetbrains.kotlin.js.facade; -import org.jetbrains.kotlin.js.backend.ast.JsImportedModule; -import org.jetbrains.kotlin.js.backend.ast.JsProgram; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.descriptors.ModuleDescriptor; @@ -27,11 +25,8 @@ import org.jetbrains.kotlin.js.backend.ast.JsProgram; import org.jetbrains.kotlin.js.config.JsConfig; import org.jetbrains.kotlin.js.coroutine.CoroutineTransformer; import org.jetbrains.kotlin.js.facade.exceptions.TranslationException; -import org.jetbrains.kotlin.js.inline.JsInliner; import org.jetbrains.kotlin.js.inline.clean.RemoveUnusedImportsKt; import org.jetbrains.kotlin.js.inline.clean.ResolveTemporaryNamesKt; -import org.jetbrains.kotlin.js.translate.context.StaticContext; -import org.jetbrains.kotlin.js.translate.context.TranslationContext; import org.jetbrains.kotlin.js.translate.general.Translation; import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus; import org.jetbrains.kotlin.psi.KtFile; @@ -42,7 +37,6 @@ import java.util.ArrayList; import java.util.List; import static org.jetbrains.kotlin.diagnostics.DiagnosticUtils.hasError; -import static org.jetbrains.kotlin.js.translate.utils.ExpandIsCallsKt.expandIsCalls; /** * An entry point of translator. @@ -80,11 +74,11 @@ public final class K2JSTranslator { ModuleDescriptor moduleDescriptor = analysisResult.getModuleDescriptor(); Diagnostics diagnostics = bindingTrace.getBindingContext().getDiagnostics(); - TranslationContext context = Translation.generateAst(bindingTrace, files, mainCallParameters, moduleDescriptor, config); + JsProgram program = Translation.generateAst(bindingTrace, files, mainCallParameters, moduleDescriptor, config); ProgressIndicatorAndCompilationCanceledStatus.checkCanceled(); if (hasError(diagnostics)) return new TranslationResult.Fail(diagnostics); - JsProgram program = JsInliner.process(context); + //JsInliner.process(program); ResolveTemporaryNamesKt.resolveTemporaryNames(program); ProgressIndicatorAndCompilationCanceledStatus.checkCanceled(); if (hasError(diagnostics)) return new TranslationResult.Fail(diagnostics); @@ -95,13 +89,13 @@ public final class K2JSTranslator { ProgressIndicatorAndCompilationCanceledStatus.checkCanceled(); if (hasError(diagnostics)) return new TranslationResult.Fail(diagnostics); - expandIsCalls(program, context); + //expandIsCalls(program, context); ProgressIndicatorAndCompilationCanceledStatus.checkCanceled(); List importedModules = new ArrayList<>(); - for (JsImportedModule module : context.getImportedModules()) { + /*for (JsImportedModule module : context.getImportedModules()) { importedModules.add(module.getExternalName()); - } + }*/ return new TranslationResult.Success(config, files, program, diagnostics, importedModules, moduleDescriptor, bindingTrace.getBindingContext()); } 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 f99989d2d2b..65a98779af9 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 @@ -98,7 +98,7 @@ internal class DeclarationExporter(val context: StaticContext) { val getterBody: JsExpression = if (simpleProperty) { val accessToField = JsReturn(context.getInnerNameForDescriptor(declaration).makeRef()) - JsFunction(context.rootFunction.scope, JsBlock(accessToField), "$declaration getter") + JsFunction(context.fragment.scope, JsBlock(accessToField), "$declaration getter") } else { context.getInnerNameForDescriptor(declaration.getter!!).makeRef() @@ -108,7 +108,7 @@ internal class DeclarationExporter(val context: StaticContext) { if (declaration.isVar) { val setterBody: JsExpression = if (simpleProperty) { val statements = mutableListOf() - val function = JsFunction(context.rootFunction.scope, JsBlock(statements), "$declaration setter") + val function = JsFunction(context.fragment.scope, JsBlock(statements), "$declaration setter") val valueName = function.scope.declareTemporaryName("value") function.parameters += JsParameter(valueName) statements += assignment(context.getInnerNameForDescriptor(declaration).makeRef(), valueName.makeRef()).makeStmt() @@ -125,11 +125,11 @@ internal class DeclarationExporter(val context: StaticContext) { private fun getLocalPackageReference(packageName: FqName): JsExpression { if (packageName.isRoot) { - return context.rootFunction.scope.declareName(Namer.getRootPackageName()).makeRef() + return context.fragment.scope.declareName(Namer.getRootPackageName()).makeRef() } var name = localPackageNames[packageName] if (name == null) { - name = context.rootFunction.scope.declareTemporaryName("package$" + packageName.shortName().asString()) + name = context.fragment.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/StaticContext.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java index 8dd8d106dea..475318cea4d 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 @@ -36,12 +36,11 @@ import org.jetbrains.kotlin.js.naming.NameSuggestion; import org.jetbrains.kotlin.js.naming.SuggestedName; import org.jetbrains.kotlin.js.translate.context.generator.Generator; import org.jetbrains.kotlin.js.translate.context.generator.Rule; -import org.jetbrains.kotlin.js.translate.declaration.InterfaceFunctionCopier; +import org.jetbrains.kotlin.js.translate.declaration.ClassModelGenerator; import org.jetbrains.kotlin.js.translate.intrinsic.Intrinsics; import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils; import org.jetbrains.kotlin.js.translate.utils.JsAstUtils; import org.jetbrains.kotlin.name.FqName; -import org.jetbrains.kotlin.name.FqNameUnsafe; import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.resolve.BindingTrace; import org.jetbrains.kotlin.resolve.DescriptorUtils; @@ -63,20 +62,12 @@ import static org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils.getSuper * Aggregates all the static parts of the context. */ public final class StaticContext { - - public static StaticContext generateStaticContext( - @NotNull BindingTrace bindingTrace, - @NotNull JsConfig config, - @NotNull ModuleDescriptor moduleDescriptor) { - 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); - } - @NotNull private final JsProgram program; + @NotNull + private final JsProgramFragment fragment; + @NotNull private final BindingTrace bindingTrace; @NotNull @@ -132,51 +123,40 @@ public final class StaticContext { @NotNull private final JsScope rootPackageScope; - @NotNull - private JsFunction rootFunction; - - @NotNull - private final List declarationStatements = new ArrayList<>(); - - @NotNull - private final List topLevelStatements = new ArrayList<>(); - @NotNull private final List importStatements = new ArrayList<>(); @NotNull private final DeclarationExporter exporter = new DeclarationExporter(this); - @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, - @NotNull JsFunction rootFunction, + @NotNull + private final ClassModelGenerator classModelGenerator; + + public StaticContext( @NotNull BindingTrace bindingTrace, - @NotNull Namer namer, - @NotNull JsScope rootScope, @NotNull JsConfig config, @NotNull ModuleDescriptor moduleDescriptor ) { - this.program = program; - this.rootFunction = rootFunction; + program = new JsProgram(); + JsFunction rootFunction = JsAstUtils.createFunctionWithEmptyBody(program.getScope()); + fragment = new JsProgramFragment(rootFunction.getScope()); + this.bindingTrace = bindingTrace; - this.namer = namer; + this.namer = Namer.newInstance(program.getRootScope()); this.intrinsics = new Intrinsics(this); - this.rootScope = rootScope; + this.rootScope = fragment.getScope(); this.config = config; this.currentModule = moduleDescriptor; - this.rootFunction = rootFunction; rootPackageScope = new JsObjectScope(rootScope, ""); JsName kotlinName = rootScope.declareName(Namer.KOTLIN_NAME); importedModules.put(new JsImportedModuleKey(Namer.KOTLIN_LOWER_NAME, null), new JsImportedModule(Namer.KOTLIN_LOWER_NAME, kotlinName, null)); + + classModelGenerator = new ClassModelGenerator(this); } @NotNull @@ -184,6 +164,11 @@ public final class StaticContext { return program; } + @NotNull + public JsProgramFragment getFragment() { + return fragment; + } + @NotNull public BindingTrace getBindingTrace() { return bindingTrace; @@ -408,7 +393,7 @@ public final class StaticContext { String baseName = NameSuggestion.sanitizeName(suggested.getNames().get(0)); if (suggested.getDescriptor() instanceof LocalVariableDescriptor || suggested.getDescriptor() instanceof ValueParameterDescriptor - ) { + ) { name = scope.declareTemporaryName(baseName); } else { @@ -474,7 +459,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().declareTemporaryName(suggestedName); + JsName result = fragment.getScope().declareTemporaryName(suggestedName); MetadataProperties.setImported(result, true); importStatements.add(JsAstUtils.newVar(result, declaration)); return result; @@ -485,7 +470,7 @@ public final class StaticContext { ModuleDescriptor module = DescriptorUtilsKt.getModule(descriptor); JsName name = module != currentModule ? importDeclaration(suggestedName, getQualifiedReference(descriptor)) : - rootFunction.getScope().declareTemporaryName(suggestedName); + fragment.getScope().declareTemporaryName(suggestedName); MetadataProperties.setDescriptor(name, descriptor); return name; } @@ -611,16 +596,16 @@ public final class StaticContext { } return getScopeForDescriptor(superclass).innerObjectScope("Scope for class " + descriptor.getName()); }; - Rule generateNewScopesForPackageDescriptors = descriptor -> rootFunction.getScope(); + Rule generateNewScopesForPackageDescriptors = descriptor -> fragment.getScope(); //TODO: never get there Rule generateInnerScopesForMembers = - descriptor -> rootFunction.getScope().innerObjectScope("Scope for member " + descriptor.getName()); + descriptor -> fragment.getScope().innerObjectScope("Scope for member " + descriptor.getName()); Rule createFunctionObjectsForCallableDescriptors = descriptor -> { if (!(descriptor instanceof CallableDescriptor)) { return null; } - JsFunction correspondingFunction = JsAstUtils.createFunctionWithEmptyBody(rootFunction.getScope()); + JsFunction correspondingFunction = JsAstUtils.createFunctionWithEmptyBody(fragment.getScope()); assert (!scopeToFunction.containsKey(correspondingFunction.getScope())) : "Scope to function value overridden for " + descriptor; scopeToFunction.put(correspondingFunction.getScope(), correspondingFunction); return correspondingFunction.getScope(); @@ -712,23 +697,18 @@ public final class StaticContext { return deferredCallSites; } - @NotNull - public JsFunction getRootFunction() { - return rootFunction; - } - @NotNull public List getTopLevelStatements() { - return topLevelStatements; + return fragment.getInitializerBlock().getStatements(); } @NotNull public List getDeclarationStatements() { - return declarationStatements; + return fragment.getDeclarationBlock().getStatements(); } public void addClass(@NotNull ClassDescriptor classDescriptor) { - classes.add(classDescriptor); + fragment.getClasses().put(getInnerNameForDescriptor(classDescriptor), classModelGenerator.generateClassModel(classDescriptor)); } public void export(@NotNull MemberDescriptor descriptor, boolean force) { @@ -745,23 +725,23 @@ public final class StaticContext { return currentModule; } - public void postProcess() { + /*public void postProcess() { addInterfaceDefaultMethods(); rootFunction.getBody().getStatements().addAll(importStatements); addClassPrototypes(); rootFunction.getBody().getStatements().addAll(declarationStatements); rootFunction.getBody().getStatements().addAll(exporter.getStatements()); rootFunction.getBody().getStatements().addAll(topLevelStatements); - } + }*/ - private void addClassPrototypes() { + /*private void addClassPrototypes() { Set visited = new HashSet<>(); for (ClassDescriptor cls : classes) { addClassPrototypes(cls, visited); } - } + }*/ - private void addClassPrototypes(@NotNull ClassDescriptor cls, @NotNull Set visited) { + /*private void addClassPrototypes(@NotNull ClassDescriptor cls, @NotNull Set visited) { if (!visited.add(cls)) return; if (DescriptorUtilsKt.getModule(cls) != currentModule) return; if (isNativeObject(cls) || isLibraryObject(cls)) return; @@ -789,19 +769,5 @@ public final class StaticContext { JsExpression constructorRef = new JsNameRef("constructor", prototype.deepCopy()); statements.add(JsAstUtils.assignment(constructorRef, classRef.deepCopy()).makeStmt()); } - } - - private void addInterfaceDefaultMethods() { - new InterfaceFunctionCopier(this).copyInterfaceFunctions(classes); - } - - public boolean isBuiltinModule() { - for (ClassDescriptor cls : classes) { - FqNameUnsafe fqn = DescriptorUtils.getFqName(cls); - if ("kotlin.Enum".equals(fqn.asString())) { - return true; - } - } - return false; - } + }*/ } 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 10ccd4c76ee..437507eadaf 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 @@ -69,9 +69,9 @@ public class TranslationContext { private final VariableDescriptor continuationParameterDescriptor; @NotNull - public static TranslationContext rootContext(@NotNull StaticContext staticContext, @NotNull JsFunction rootFunction) { - JsBlock block = new JsBlock(staticContext.getTopLevelStatements()); - DynamicContext rootDynamicContext = DynamicContext.rootContext(rootFunction.getScope(), block); + public static TranslationContext rootContext(@NotNull StaticContext staticContext) { + DynamicContext rootDynamicContext = DynamicContext.rootContext( + staticContext.getFragment().getScope(), staticContext.getFragment().getInitializerBlock()); AliasingContext rootAliasingContext = AliasingContext.getCleanContext(); return new TranslationContext(null, staticContext, rootDynamicContext, rootAliasingContext, null, null); } @@ -544,7 +544,7 @@ public class TranslationContext { if (result == null && classOrConstructor instanceof ConstructorDescriptor && ((ConstructorDescriptor) classOrConstructor).isPrimary() - ) { + ) { result = staticContext.getClassOrConstructorClosure((ClassDescriptor) classOrConstructor.getContainingDeclaration()); } return result; @@ -645,7 +645,7 @@ public class TranslationContext { @NotNull public JsName createGlobalName(@NotNull String suggestedName) { - return staticContext.getRootFunction().getScope().declareTemporaryName(suggestedName); + return staticContext.getFragment().getScope().declareTemporaryName(suggestedName); } @NotNull @@ -655,7 +655,7 @@ public class TranslationContext { @NotNull public JsFunction createRootScopedFunction(@NotNull String description) { - return new JsFunction(staticContext.getRootFunction().getScope(), new JsBlock(), description); + return new JsFunction(staticContext.getFragment().getScope(), new JsBlock(), description); } public void addClass(@NotNull ClassDescriptor classDescriptor) { diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassModelGenerator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassModelGenerator.kt new file mode 100644 index 00000000000..7748c85625c --- /dev/null +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassModelGenerator.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.js.translate.declaration + +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.js.backend.ast.JsClassModel +import org.jetbrains.kotlin.js.translate.context.StaticContext +import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny + +class ClassModelGenerator(val context: StaticContext) { + fun generateClassModel(descriptor: ClassDescriptor): JsClassModel { + val superName = descriptor.getSuperClassNotAny()?.let { context.getInnerNameForDescriptor(it) } + val model = JsClassModel(context.getInnerNameForDescriptor(descriptor), superName) + return model + } +} diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/InterfaceFunctionCopier.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/InterfaceFunctionCopier.kt deleted file mode 100644 index 75ba46791c3..00000000000 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/InterfaceFunctionCopier.kt +++ /dev/null @@ -1,195 +0,0 @@ -/* - * 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.declaration - -import org.jetbrains.kotlin.js.backend.ast.JsInvocation -import org.jetbrains.kotlin.js.backend.ast.JsName -import org.jetbrains.kotlin.js.backend.ast.JsNameRef -import org.jetbrains.kotlin.backend.common.bridges.generateBridgesForFunctionDescriptor -import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.js.translate.context.Namer -import org.jetbrains.kotlin.js.translate.context.StaticContext -import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils.isNativeObject -import org.jetbrains.kotlin.js.translate.utils.JsAstUtils -import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.prototypeOf -import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.pureFqn -import org.jetbrains.kotlin.resolve.DescriptorUtils -import org.jetbrains.kotlin.resolve.descriptorUtil.* -import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter -import org.jetbrains.kotlin.utils.DFS -import org.jetbrains.kotlin.utils.identity - -class InterfaceFunctionCopier(val context: StaticContext) { - private val classModels = mutableMapOf() - - fun copyInterfaceFunctions(classes: Collection) { - val orderedClasses = DFS.topologicalOrder(classes) { current -> DescriptorUtils.getSuperclassDescriptors(current) }.asReversed() - - for (classDescriptor in orderedClasses) { - addInterfaceDefaultMembers(classDescriptor, context) - } - } - - private fun addInterfaceDefaultMembers(descriptor: ClassDescriptor, context: StaticContext) { - // optimization: don't do anything for native declarations - if (isNativeObject(descriptor)) return - - val classModel = ClassModel(descriptor) - classModels[descriptor] = classModel - - val members = descriptor.unsubstitutedMemberScope - .getContributedDescriptors(DescriptorKindFilter.FUNCTIONS) - .mapNotNull { it as? CallableMemberDescriptor } - - for (member in members.filter { it.kind.isReal }) { - val names = generateAllNames(member, context) - val identifiers = names.map { it.ident } - when (member) { - is FunctionDescriptor -> { - if (member.hasOrInheritsParametersWithDefaultValue()) { - if (member.hasOwnParametersWithDefaultValue()) { - classModel.copiedFunctions += identifiers.map { it } - } - if (member.modality != Modality.ABSTRACT) { - classModel.copiedFunctions += identifiers.map { it + Namer.DEFAULT_PARAMETER_IMPLEMENTOR_SUFFIX } - } - } - else if (member.modality != Modality.ABSTRACT) { - classModel.copiedFunctions += identifiers - } - } - is PropertyDescriptor -> { - if (member.modality != Modality.ABSTRACT) { - classModel.copiedProperties += identifiers - } - } - } - } - - for (member in members.filterIsInstance()) { - moveDefaultFunction(member, context) - } - - for (member in members.filterIsInstance() - .filter { it.kind.isReal && it.hasOwnParametersWithDefaultValue() }) { - val names = generateAllNames(member, context) - val identifiers = names.map { it.ident } - classModel.copiedFunctions += identifiers - } - - val superModels = DescriptorUtils.getSuperclassDescriptors(descriptor) - .filter { it.kind == ClassKind.INTERFACE && !isNativeObject(it) } - .map { classModels[it]!! } - - for (superModel in superModels) { - for (name in superModel.copiedFunctions) { - if (classModel.copiedFunctions.add(name)) { - addDefaultMethodFromInterface(name, name, superModel.descriptor, descriptor, context) - } - } - for (name in superModel.copiedProperties) { - if (classModel.copiedProperties.add(name)) { - addDefaultPropertyFromInterface(name, superModel.descriptor, descriptor, context) - } - } - } - } - - private fun moveDefaultFunction(function: FunctionDescriptor, context: StaticContext) { - if (function.kind.isReal || function.modality == Modality.ABSTRACT) return - - val overriddenWithDefaultArg = function.overriddenDescriptors - .firstOrNull { it.hasOwnParametersWithDefaultValue() } ?: return - - if (!DescriptorUtils.isInterface(overriddenWithDefaultArg.containingDeclaration)) return - val fakeImplementation = function.overriddenDescriptors.first { it.modality != Modality.ABSTRACT } - - val interfaceFunctionName = context.getNameForDescriptor(overriddenWithDefaultArg).ident - val targetName = interfaceFunctionName + Namer.DEFAULT_PARAMETER_IMPLEMENTOR_SUFFIX - val sourceName = context.getNameForDescriptor(fakeImplementation).ident - - addDefaultMethodFromInterface(sourceName, targetName, fakeImplementation.containingDeclaration as ClassDescriptor, - function.containingDeclaration as ClassDescriptor, context) - - val overrideNames = generateAllNames(function, context).map { it.ident } - val namesFromBaseClass = function.overriddenDescriptors - .filter { !DescriptorUtils.isInterface(it.containingDeclaration) } - .flatMap { generateAllNames(it, context).map { it.ident }.asIterable() } - .distinct() - - for (name in overrideNames.filter { it in namesFromBaseClass }) { - addDefaultMethodFromInterface(interfaceFunctionName, name, overriddenWithDefaultArg.containingDeclaration as ClassDescriptor, - function.containingDeclaration as ClassDescriptor, context) - } - } - - private fun addDefaultMethodFromInterface( - sourceName: String, - targetName: String, - sourceDescriptor: ClassDescriptor, - targetDescriptor: ClassDescriptor, - context: StaticContext - ) { - if (targetDescriptor.module != context.currentModule) return - - val targetPrototype = prototypeOf(pureFqn(context.getInnerNameForDescriptor(targetDescriptor), null)) - val sourcePrototype = prototypeOf(pureFqn(context.getInnerNameForDescriptor(sourceDescriptor), null)) - val targetFunction = JsNameRef(targetName, targetPrototype) - val sourceFunction = JsNameRef(sourceName, sourcePrototype) - context.declarationStatements += JsAstUtils.assignment(targetFunction, sourceFunction).makeStmt() - } - - private fun addDefaultPropertyFromInterface( - name: String, - sourceDescriptor: ClassDescriptor, - targetDescriptor: ClassDescriptor, - context: StaticContext - ) { - if (targetDescriptor.module != context.currentModule) return - - val targetPrototype = prototypeOf(pureFqn(context.getInnerNameForDescriptor(targetDescriptor), null)) - val sourcePrototype = prototypeOf(pureFqn(context.getInnerNameForDescriptor(sourceDescriptor), null)) - val nameLiteral = context.program.getStringLiteral(name) - - val getPropertyDescriptor = JsInvocation(JsNameRef("getOwnPropertyDescriptor", "Object"), sourcePrototype, nameLiteral) - val defineProperty = JsAstUtils.defineProperty(targetPrototype, name, getPropertyDescriptor, context.program) - - context.declarationStatements += defineProperty.makeStmt() - } - - private fun generateAllNames(member: CallableMemberDescriptor, context: StaticContext): Sequence { - return (generateBridges(member) + member).map { context.getNameForDescriptor(it) }.distinctBy { it.ident } - } - - private fun generateBridges(member: CallableMemberDescriptor): Sequence = when (member) { - is FunctionDescriptor -> { - generateBridgesForFunctionDescriptor(member, identity()) { false } - .map { it.from } - .asSequence() - } - is PropertyDescriptor -> generateBridgesForFunctionDescriptor(member.getter!!, identity()) { false } - .map { it.from } - .map { (it as PropertyAccessorDescriptor).correspondingProperty } - .asSequence() - else -> error("Expected either be function or property: $member") - } - - private class ClassModel(val descriptor: ClassDescriptor) { - val copiedFunctions = mutableSetOf() - val copiedProperties = mutableSetOf() - } -} \ No newline at end of file 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 deleted file mode 100644 index 3d040846cb6..00000000000 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/PackageDeclarationTranslator.java +++ /dev/null @@ -1,62 +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.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 java.util.Collection; - -public final class PackageDeclarationTranslator extends AbstractTranslator { - private final Iterable files; - - public static void translateFiles(@NotNull Collection files, @NotNull TranslationContext context) { - new PackageDeclarationTranslator(files, context).translate(); - } - - private PackageDeclarationTranslator(@NotNull Iterable files, @NotNull TranslationContext context) { - super(context); - - this.files = files; - } - - private void translate() { - for (KtFile file : files) { - FileDeclarationVisitor fileVisitor = new FileDeclarationVisitor(context()); - - try { - for (KtDeclaration declaration : file.getDeclarations()) { - if (!AnnotationsUtils.isPredefinedObject(BindingUtils.getDescriptorForElement(bindingContext(), declaration))) { - declaration.accept(fileVisitor, context()); - } - } - } - catch (TranslationRuntimeException e) { - throw e; - } - catch (RuntimeException | AssertionError e) { - throw new TranslationRuntimeException(file, e); - } - } - } -} diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/Merger.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/Merger.kt new file mode 100644 index 00000000000..dd6488520c2 --- /dev/null +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/Merger.kt @@ -0,0 +1,93 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.js.translate.general + +import org.jetbrains.kotlin.js.backend.ast.* +import org.jetbrains.kotlin.js.backend.ast.metadata.coroutineMetadata +import org.jetbrains.kotlin.js.inline.clean.resolveTemporaryNames +import org.jetbrains.kotlin.js.translate.context.Namer + +class Merger(private val rootFunction: JsFunction, val internalModuleName: JsName) { + // Maps unique signature (see generateSignature) to names + private val nameTable = mutableMapOf() + private val importBlock = JsGlobalBlock() + private val declarationBlock = JsGlobalBlock() + private val initializerBlock = JsGlobalBlock() + private val exportBlock = JsGlobalBlock() + + // Add declaration and initialization statements from program fragment to resulting single program + fun addFragment(fragment: JsProgramFragment) { + val nameMap = buildNameMap(fragment) + nameMap.rename(fragment) + + declarationBlock.statements += fragment.declarationBlock + initializerBlock.statements += fragment.initializerBlock + exportBlock.statements += fragment.exportBlock + } + + private fun Map.rename(name: JsName): JsName = getOrElse(name) { name } + + // Builds mapping to map names from different fragments into single name when they denote single declaration + private fun buildNameMap(fragment: JsProgramFragment): Map { + val nameMap = mutableMapOf() + for (nameBinding in fragment.nameBindings) { + nameMap[nameBinding.name] = nameTable.getOrPut(nameBinding.key) { + fragment.scope.declareTemporaryName(nameBinding.name.ident).also { it.copyMetadataFrom(nameBinding.name) } + } + } + fragment.scope.findName(Namer.getRootPackageName())?.let { nameMap[it] = internalModuleName } + + return nameMap + } + + private fun Map.rename(fragment: JsProgramFragment) { + rename(fragment.declarationBlock) + rename(fragment.exportBlock) + rename(fragment.initializerBlock) + } + + private fun Map.rename(node: T): T { + node.accept(object : RecursiveJsVisitor() { + override fun visitElement(node: JsNode) { + super.visitElement(node) + if (node is HasName) { + node.name = node.name?.let { name -> rename(name) } + } + if (node is JsFunction) { + val coroutineMetadata = node.coroutineMetadata + if (coroutineMetadata != null) { + node.coroutineMetadata = coroutineMetadata.copy( + baseClassRef = rename(coroutineMetadata.baseClassRef), + suspendObjectRef = rename(coroutineMetadata.suspendObjectRef) + ) + } + } + } + }) + return node + } + + // Adds different boilerplate code (like imports, class prototypes, etc) to resulting program. + fun merge() { + rootFunction.body.statements.apply { + this += importBlock.statements + this += declarationBlock.statements + this += initializerBlock.statements + } + rootFunction.body.resolveTemporaryNames() + } +} \ No newline at end of file diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/Translation.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/Translation.java index 6567f660222..cba9d542e4a 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/Translation.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/Translation.java @@ -33,18 +33,17 @@ import org.jetbrains.kotlin.js.translate.context.Namer; import org.jetbrains.kotlin.js.translate.context.StaticContext; import org.jetbrains.kotlin.js.translate.context.TemporaryVariable; import org.jetbrains.kotlin.js.translate.context.TranslationContext; -import org.jetbrains.kotlin.js.translate.declaration.PackageDeclarationTranslator; +import org.jetbrains.kotlin.js.translate.declaration.FileDeclarationVisitor; import org.jetbrains.kotlin.js.translate.expression.ExpressionVisitor; import org.jetbrains.kotlin.js.translate.expression.PatternTranslator; import org.jetbrains.kotlin.js.translate.test.JSTestGenerator; import org.jetbrains.kotlin.js.translate.test.JSTester; import org.jetbrains.kotlin.js.translate.test.QUnitTester; +import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils; +import org.jetbrains.kotlin.js.translate.utils.BindingUtils; import org.jetbrains.kotlin.js.translate.utils.JsAstUtils; import org.jetbrains.kotlin.js.translate.utils.mutator.AssignToExpressionMutator; -import org.jetbrains.kotlin.psi.KtExpression; -import org.jetbrains.kotlin.psi.KtFile; -import org.jetbrains.kotlin.psi.KtNamedFunction; -import org.jetbrains.kotlin.psi.KtUnaryExpression; +import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.resolve.BindingTrace; import org.jetbrains.kotlin.resolve.bindingContextUtil.BindingContextUtilsKt; import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant; @@ -71,6 +70,7 @@ import static org.jetbrains.kotlin.js.translate.utils.mutator.LastExpressionMuta * Goal is to simplify interaction between translators. */ public final class Translation { + private static final String ENUM_SIGNATURE = "kotlin$Enum"; private Translation() { } @@ -234,7 +234,7 @@ public final class Translation { } @NotNull - public static TranslationContext generateAst( + public static JsProgram generateAst( @NotNull BindingTrace bindingTrace, @NotNull Collection files, @NotNull MainCallParameters mainCallParameters, @@ -253,38 +253,49 @@ public final class Translation { } @NotNull - private static TranslationContext doGenerateAst( + private static JsProgram doGenerateAst( @NotNull BindingTrace bindingTrace, @NotNull Collection files, @NotNull MainCallParameters mainCallParameters, @NotNull ModuleDescriptor moduleDescriptor, @NotNull JsConfig config ) { - StaticContext staticContext = StaticContext.generateStaticContext(bindingTrace, config, moduleDescriptor); - JsProgram program = staticContext.getProgram(); - JsName rootPackageName = program.getRootScope().declareName(Namer.getRootPackageName()); + JsProgram program = new JsProgram(); + JsFunction rootFunction = new JsFunction(program.getRootScope(), new JsBlock(), "root function"); + Merger merger = new Merger(rootFunction); + + List fragments = new ArrayList(); + for (KtFile file : files) { + StaticContext staticContext = new StaticContext(bindingTrace, config, moduleDescriptor); + TranslationContext context = TranslationContext.rootContext(staticContext); + translateFile(context, file); + fragments.add(staticContext.getFragment()); + merger.addFragment(staticContext.getFragment()); + } + + merger.merge(); - JsFunction rootFunction = staticContext.getRootFunction(); JsBlock rootBlock = rootFunction.getBody(); + List statements = rootBlock.getStatements(); program.getScope().declareName("_"); - TranslationContext context = TranslationContext.rootContext(staticContext, rootFunction); - PackageDeclarationTranslator.translateFiles(files, context); - staticContext.postProcess(); + //staticContext.postProcess(); statements.add(0, program.getStringLiteral("use strict").makeStmt()); - if (!staticContext.isBuiltinModule()) { - defineModule(context, statements, config.getModuleId()); + if (!isBuiltinModule(fragments)) { + defineModule(program, statements, config.getModuleId()); } - mayBeGenerateTests(files, rootBlock, context); + //mayBeGenerateTests(files, rootBlock, context); + JsName rootPackageName = program.getScope().declareName(Namer.getRootPackageName()); rootFunction.getParameters().add(new JsParameter((rootPackageName))); // Invoke function passing modules as arguments // This should help minifier tool to recognize references to these modules as local variables and make them shorter. List importedModuleList = new ArrayList<>(); + /* for (JsImportedModule importedModule : staticContext.getImportedModules()) { rootFunction.getParameters().add(new JsParameter(importedModule.getInternalName())); importedModuleList.add(importedModule); @@ -296,6 +307,7 @@ public final class Translation { statements.add(statement); } } + */ statements.add(new JsReturn(rootPackageName.makeRef())); @@ -303,13 +315,46 @@ public final class Translation { block.getStatements().addAll(wrapIfNecessary(config.getModuleId(), rootFunction, importedModuleList, program, config.getModuleKind())); - return context; + return program; } - private static void defineModule(@NotNull TranslationContext context, @NotNull List statements, @NotNull String moduleId) { - JsName rootPackageName = context.scope().findName(Namer.getRootPackageName()); + private static boolean isBuiltinModule(@NotNull List fragments) { + for (JsProgramFragment fragment : fragments) { + for (JsNameBinding nameBinding : fragment.getNameBindings()) { + if (nameBinding.getKey().equals(ENUM_SIGNATURE) && !fragment.getImports().containsKey(ENUM_SIGNATURE)) { + return true; + } + } + } + return false; + } + + private static void translateFile(@NotNull TranslationContext context, @NotNull KtFile file) { + FileDeclarationVisitor fileVisitor = new FileDeclarationVisitor(context); + + try { + for (KtDeclaration declaration : file.getDeclarations()) { + if (!AnnotationsUtils.isPredefinedObject(BindingUtils.getDescriptorForElement(context.bindingContext(), declaration))) { + declaration.accept(fileVisitor, context); + } + } + } + catch (TranslationRuntimeException e) { + throw e; + } + catch (RuntimeException e) { + throw new TranslationRuntimeException(file, e); + } + catch (AssertionError e) { + throw new TranslationRuntimeException(file, e); + } + } + + private static void defineModule(@NotNull JsProgram program, @NotNull List statements, @NotNull String moduleId) { + JsName rootPackageName = program.getScope().findName(Namer.getRootPackageName()); if (rootPackageName != null) { - statements.add(new JsInvocation(context.namer().kotlin("defineModule"), context.program().getStringLiteral(moduleId), + Namer namer = Namer.newInstance(program.getScope()); + statements.add(new JsInvocation(namer.kotlin("defineModule"), program.getStringLiteral(moduleId), rootPackageName.makeRef()).makeStmt()); } } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/signatureUtils.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/signatureUtils.kt new file mode 100644 index 00000000000..f987b536532 --- /dev/null +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/signatureUtils.kt @@ -0,0 +1,74 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.js.translate.utils + +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.js.naming.encodeSignature +import org.jetbrains.kotlin.resolve.DescriptorUtils + +// Unfortunately, our descriptor serializer can't serialize references to descriptors. +// We have to serialize entire class or package to serialize function or property. +// In case of declarations from external modules we end up with duplication of significant metadata. + +// What we really need these descriptors is to determine which names from different fragments point to one declarations, +// so that merger could turn these names into one name. Actually, we need a way to get unique identifier for every +// declaration. + +// This code does not generate unique identifiers, it's possible to pick two declarations with same strings. However, +// it's hard to do it unintentionally. +fun generateSignature(descriptor: DeclarationDescriptor): String? { + if (DescriptorUtils.isDescriptorWithLocalVisibility(descriptor)) return null + if (descriptor is DeclarationDescriptorWithVisibility && descriptor.visibility == Visibilities.PRIVATE && + !AnnotationsUtils.isNativeObject(descriptor) && !AnnotationsUtils.isLibraryObject(descriptor) + ) { + return null + } + return when (descriptor) { + is CallableDescriptor -> { + val parent = generateSignature(descriptor.containingDeclaration) ?: return null + if (descriptor !is VariableAccessorDescriptor && descriptor !is ConstructorDescriptor && descriptor.name.isSpecial) { + return null + } + + // Make distinction between functions with zero parameters and properties + val separator = if (descriptor is FunctionDescriptor) "#" else "!" + + parent + separator + escape(descriptor.name.asString()) + "|" + encodeSignature(descriptor) + } + is PackageFragmentDescriptor -> { + if (descriptor.fqName.isRoot) "" else escape(descriptor.fqName.pathSegments().map { escape(it.identifier) }.joinToString(".")) + } + is ClassDescriptor -> { + val parent = generateSignature(descriptor.containingDeclaration) ?: return null + if (descriptor.name.isSpecial) return null + parent + "$" + escape(descriptor.name.asString()) + } + else -> return null + } +} + +private fun escape(s: String): String { + val sb = StringBuilder() + for (c in s) { + val escapedChar = when (c) { + '\\', '"', '.', '$', '#', '!', '<', '>', '|', '+', '-', ':', '*', '?' -> "\\$c" + else -> c.toString() + } + sb.append(escapedChar) + } + return sb.toString() +}