JS: reworking translator pipeline to generate multiple fragment each for source file

This commit is contained in:
Alexey Andreev
2017-02-10 18:15:35 +03:00
parent e909ef984c
commit 6711db86ee
11 changed files with 315 additions and 370 deletions
@@ -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,
@@ -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<String> 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());
}
@@ -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<JsStatement>()
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())
@@ -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<JsStatement> declarationStatements = new ArrayList<>();
@NotNull
private final List<JsStatement> topLevelStatements = new ArrayList<>();
@NotNull
private final List<JsStatement> importStatements = new ArrayList<>();
@NotNull
private final DeclarationExporter exporter = new DeclarationExporter(this);
@NotNull
private final Set<ClassDescriptor> classes = new LinkedHashSet<>();
@NotNull
private final Map<FqName, JsScope> 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, "<root package>");
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<JsScope> generateNewScopesForPackageDescriptors = descriptor -> rootFunction.getScope();
Rule<JsScope> generateNewScopesForPackageDescriptors = descriptor -> fragment.getScope();
//TODO: never get there
Rule<JsScope> generateInnerScopesForMembers =
descriptor -> rootFunction.getScope().innerObjectScope("Scope for member " + descriptor.getName());
descriptor -> fragment.getScope().innerObjectScope("Scope for member " + descriptor.getName());
Rule<JsScope> 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<JsStatement> getTopLevelStatements() {
return topLevelStatements;
return fragment.getInitializerBlock().getStatements();
}
@NotNull
public List<JsStatement> 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<ClassDescriptor> visited = new HashSet<>();
for (ClassDescriptor cls : classes) {
addClassPrototypes(cls, visited);
}
}
}*/
private void addClassPrototypes(@NotNull ClassDescriptor cls, @NotNull Set<ClassDescriptor> visited) {
/*private void addClassPrototypes(@NotNull ClassDescriptor cls, @NotNull Set<ClassDescriptor> 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;
}
}*/
}
@@ -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) {
@@ -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
}
}
@@ -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<ClassDescriptor, ClassModel>()
fun copyInterfaceFunctions(classes: Collection<ClassDescriptor>) {
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<FunctionDescriptor>()) {
moveDefaultFunction(member, context)
}
for (member in members.filterIsInstance<FunctionDescriptor>()
.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<JsName> {
return (generateBridges(member) + member).map { context.getNameForDescriptor(it) }.distinctBy { it.ident }
}
private fun generateBridges(member: CallableMemberDescriptor): Sequence<CallableMemberDescriptor> = 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<String>()
val copiedProperties = mutableSetOf<String>()
}
}
@@ -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<KtFile> files;
public static void translateFiles(@NotNull Collection<KtFile> files, @NotNull TranslationContext context) {
new PackageDeclarationTranslator(files, context).translate();
}
private PackageDeclarationTranslator(@NotNull Iterable<KtFile> 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);
}
}
}
}
@@ -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<String, JsName>()
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<JsName, JsName>.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<JsName, JsName> {
val nameMap = mutableMapOf<JsName, JsName>()
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<JsName, JsName>.rename(fragment: JsProgramFragment) {
rename(fragment.declarationBlock)
rename(fragment.exportBlock)
rename(fragment.initializerBlock)
}
private fun <T: JsNode> Map<JsName, JsName>.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()
}
}
@@ -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<KtFile> 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<KtFile> 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<JsProgramFragment> fragments = new ArrayList<JsProgramFragment>();
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<JsStatement> 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<JsImportedModule> 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<JsStatement> statements, @NotNull String moduleId) {
JsName rootPackageName = context.scope().findName(Namer.getRootPackageName());
private static boolean isBuiltinModule(@NotNull List<JsProgramFragment> 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<JsStatement> 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());
}
}
@@ -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()
}