JS: reworking translator pipeline to generate multiple fragment each for source file
This commit is contained in:
@@ -100,7 +100,7 @@ var JsName.imported by MetadataProperty(default = false)
|
|||||||
|
|
||||||
var JsFunction.coroutineMetadata: CoroutineMetadata? by MetadataProperty(default = null)
|
var JsFunction.coroutineMetadata: CoroutineMetadata? by MetadataProperty(default = null)
|
||||||
|
|
||||||
class CoroutineMetadata(
|
data class CoroutineMetadata(
|
||||||
val doResumeName: JsName,
|
val doResumeName: JsName,
|
||||||
val stateName: JsName,
|
val stateName: JsName,
|
||||||
val exceptionStateName: JsName,
|
val exceptionStateName: JsName,
|
||||||
|
|||||||
@@ -16,8 +16,6 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.js.facade;
|
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.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor;
|
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.config.JsConfig;
|
||||||
import org.jetbrains.kotlin.js.coroutine.CoroutineTransformer;
|
import org.jetbrains.kotlin.js.coroutine.CoroutineTransformer;
|
||||||
import org.jetbrains.kotlin.js.facade.exceptions.TranslationException;
|
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.RemoveUnusedImportsKt;
|
||||||
import org.jetbrains.kotlin.js.inline.clean.ResolveTemporaryNamesKt;
|
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.js.translate.general.Translation;
|
||||||
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus;
|
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus;
|
||||||
import org.jetbrains.kotlin.psi.KtFile;
|
import org.jetbrains.kotlin.psi.KtFile;
|
||||||
@@ -42,7 +37,6 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.jetbrains.kotlin.diagnostics.DiagnosticUtils.hasError;
|
import static org.jetbrains.kotlin.diagnostics.DiagnosticUtils.hasError;
|
||||||
import static org.jetbrains.kotlin.js.translate.utils.ExpandIsCallsKt.expandIsCalls;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An entry point of translator.
|
* An entry point of translator.
|
||||||
@@ -80,11 +74,11 @@ public final class K2JSTranslator {
|
|||||||
ModuleDescriptor moduleDescriptor = analysisResult.getModuleDescriptor();
|
ModuleDescriptor moduleDescriptor = analysisResult.getModuleDescriptor();
|
||||||
Diagnostics diagnostics = bindingTrace.getBindingContext().getDiagnostics();
|
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();
|
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled();
|
||||||
if (hasError(diagnostics)) return new TranslationResult.Fail(diagnostics);
|
if (hasError(diagnostics)) return new TranslationResult.Fail(diagnostics);
|
||||||
|
|
||||||
JsProgram program = JsInliner.process(context);
|
//JsInliner.process(program);
|
||||||
ResolveTemporaryNamesKt.resolveTemporaryNames(program);
|
ResolveTemporaryNamesKt.resolveTemporaryNames(program);
|
||||||
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled();
|
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled();
|
||||||
if (hasError(diagnostics)) return new TranslationResult.Fail(diagnostics);
|
if (hasError(diagnostics)) return new TranslationResult.Fail(diagnostics);
|
||||||
@@ -95,13 +89,13 @@ public final class K2JSTranslator {
|
|||||||
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled();
|
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled();
|
||||||
if (hasError(diagnostics)) return new TranslationResult.Fail(diagnostics);
|
if (hasError(diagnostics)) return new TranslationResult.Fail(diagnostics);
|
||||||
|
|
||||||
expandIsCalls(program, context);
|
//expandIsCalls(program, context);
|
||||||
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled();
|
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled();
|
||||||
|
|
||||||
List<String> importedModules = new ArrayList<>();
|
List<String> importedModules = new ArrayList<>();
|
||||||
for (JsImportedModule module : context.getImportedModules()) {
|
/*for (JsImportedModule module : context.getImportedModules()) {
|
||||||
importedModules.add(module.getExternalName());
|
importedModules.add(module.getExternalName());
|
||||||
}
|
}*/
|
||||||
return new TranslationResult.Success(config, files, program, diagnostics, importedModules, moduleDescriptor,
|
return new TranslationResult.Success(config, files, program, diagnostics, importedModules, moduleDescriptor,
|
||||||
bindingTrace.getBindingContext());
|
bindingTrace.getBindingContext());
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-4
@@ -98,7 +98,7 @@ internal class DeclarationExporter(val context: StaticContext) {
|
|||||||
|
|
||||||
val getterBody: JsExpression = if (simpleProperty) {
|
val getterBody: JsExpression = if (simpleProperty) {
|
||||||
val accessToField = JsReturn(context.getInnerNameForDescriptor(declaration).makeRef())
|
val accessToField = JsReturn(context.getInnerNameForDescriptor(declaration).makeRef())
|
||||||
JsFunction(context.rootFunction.scope, JsBlock(accessToField), "$declaration getter")
|
JsFunction(context.fragment.scope, JsBlock(accessToField), "$declaration getter")
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
context.getInnerNameForDescriptor(declaration.getter!!).makeRef()
|
context.getInnerNameForDescriptor(declaration.getter!!).makeRef()
|
||||||
@@ -108,7 +108,7 @@ internal class DeclarationExporter(val context: StaticContext) {
|
|||||||
if (declaration.isVar) {
|
if (declaration.isVar) {
|
||||||
val setterBody: JsExpression = if (simpleProperty) {
|
val setterBody: JsExpression = if (simpleProperty) {
|
||||||
val statements = mutableListOf<JsStatement>()
|
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")
|
val valueName = function.scope.declareTemporaryName("value")
|
||||||
function.parameters += JsParameter(valueName)
|
function.parameters += JsParameter(valueName)
|
||||||
statements += assignment(context.getInnerNameForDescriptor(declaration).makeRef(), valueName.makeRef()).makeStmt()
|
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 {
|
private fun getLocalPackageReference(packageName: FqName): JsExpression {
|
||||||
if (packageName.isRoot) {
|
if (packageName.isRoot) {
|
||||||
return context.rootFunction.scope.declareName(Namer.getRootPackageName()).makeRef()
|
return context.fragment.scope.declareName(Namer.getRootPackageName()).makeRef()
|
||||||
}
|
}
|
||||||
var name = localPackageNames[packageName]
|
var name = localPackageNames[packageName]
|
||||||
if (name == null) {
|
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)
|
localPackageNames.put(packageName, name)
|
||||||
|
|
||||||
val parentRef = getLocalPackageReference(packageName.parent())
|
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.naming.SuggestedName;
|
||||||
import org.jetbrains.kotlin.js.translate.context.generator.Generator;
|
import org.jetbrains.kotlin.js.translate.context.generator.Generator;
|
||||||
import org.jetbrains.kotlin.js.translate.context.generator.Rule;
|
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.intrinsic.Intrinsics;
|
||||||
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils;
|
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils;
|
||||||
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
|
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
|
||||||
import org.jetbrains.kotlin.name.FqName;
|
import org.jetbrains.kotlin.name.FqName;
|
||||||
import org.jetbrains.kotlin.name.FqNameUnsafe;
|
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||||
import org.jetbrains.kotlin.resolve.BindingTrace;
|
import org.jetbrains.kotlin.resolve.BindingTrace;
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
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.
|
* Aggregates all the static parts of the context.
|
||||||
*/
|
*/
|
||||||
public final class StaticContext {
|
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
|
@NotNull
|
||||||
private final JsProgram program;
|
private final JsProgram program;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private final JsProgramFragment fragment;
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private final BindingTrace bindingTrace;
|
private final BindingTrace bindingTrace;
|
||||||
@NotNull
|
@NotNull
|
||||||
@@ -132,51 +123,40 @@ public final class StaticContext {
|
|||||||
@NotNull
|
@NotNull
|
||||||
private final JsScope rootPackageScope;
|
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
|
@NotNull
|
||||||
private final List<JsStatement> importStatements = new ArrayList<>();
|
private final List<JsStatement> importStatements = new ArrayList<>();
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private final DeclarationExporter exporter = new DeclarationExporter(this);
|
private final DeclarationExporter exporter = new DeclarationExporter(this);
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private final Set<ClassDescriptor> classes = new LinkedHashSet<>();
|
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private final Map<FqName, JsScope> packageScopes = new HashMap<>();
|
private final Map<FqName, JsScope> packageScopes = new HashMap<>();
|
||||||
|
|
||||||
//TODO: too many parameters in constructor
|
@NotNull
|
||||||
private StaticContext(
|
private final ClassModelGenerator classModelGenerator;
|
||||||
@NotNull JsProgram program,
|
|
||||||
@NotNull JsFunction rootFunction,
|
public StaticContext(
|
||||||
@NotNull BindingTrace bindingTrace,
|
@NotNull BindingTrace bindingTrace,
|
||||||
@NotNull Namer namer,
|
|
||||||
@NotNull JsScope rootScope,
|
|
||||||
@NotNull JsConfig config,
|
@NotNull JsConfig config,
|
||||||
@NotNull ModuleDescriptor moduleDescriptor
|
@NotNull ModuleDescriptor moduleDescriptor
|
||||||
) {
|
) {
|
||||||
this.program = program;
|
program = new JsProgram();
|
||||||
this.rootFunction = rootFunction;
|
JsFunction rootFunction = JsAstUtils.createFunctionWithEmptyBody(program.getScope());
|
||||||
|
fragment = new JsProgramFragment(rootFunction.getScope());
|
||||||
|
|
||||||
this.bindingTrace = bindingTrace;
|
this.bindingTrace = bindingTrace;
|
||||||
this.namer = namer;
|
this.namer = Namer.newInstance(program.getRootScope());
|
||||||
this.intrinsics = new Intrinsics(this);
|
this.intrinsics = new Intrinsics(this);
|
||||||
this.rootScope = rootScope;
|
this.rootScope = fragment.getScope();
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.currentModule = moduleDescriptor;
|
this.currentModule = moduleDescriptor;
|
||||||
this.rootFunction = rootFunction;
|
|
||||||
rootPackageScope = new JsObjectScope(rootScope, "<root package>");
|
rootPackageScope = new JsObjectScope(rootScope, "<root package>");
|
||||||
|
|
||||||
JsName kotlinName = rootScope.declareName(Namer.KOTLIN_NAME);
|
JsName kotlinName = rootScope.declareName(Namer.KOTLIN_NAME);
|
||||||
importedModules.put(new JsImportedModuleKey(Namer.KOTLIN_LOWER_NAME, null),
|
importedModules.put(new JsImportedModuleKey(Namer.KOTLIN_LOWER_NAME, null),
|
||||||
new JsImportedModule(Namer.KOTLIN_LOWER_NAME, kotlinName, null));
|
new JsImportedModule(Namer.KOTLIN_LOWER_NAME, kotlinName, null));
|
||||||
|
|
||||||
|
classModelGenerator = new ClassModelGenerator(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@@ -184,6 +164,11 @@ public final class StaticContext {
|
|||||||
return program;
|
return program;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public JsProgramFragment getFragment() {
|
||||||
|
return fragment;
|
||||||
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public BindingTrace getBindingTrace() {
|
public BindingTrace getBindingTrace() {
|
||||||
return bindingTrace;
|
return bindingTrace;
|
||||||
@@ -408,7 +393,7 @@ public final class StaticContext {
|
|||||||
String baseName = NameSuggestion.sanitizeName(suggested.getNames().get(0));
|
String baseName = NameSuggestion.sanitizeName(suggested.getNames().get(0));
|
||||||
if (suggested.getDescriptor() instanceof LocalVariableDescriptor ||
|
if (suggested.getDescriptor() instanceof LocalVariableDescriptor ||
|
||||||
suggested.getDescriptor() instanceof ValueParameterDescriptor
|
suggested.getDescriptor() instanceof ValueParameterDescriptor
|
||||||
) {
|
) {
|
||||||
name = scope.declareTemporaryName(baseName);
|
name = scope.declareTemporaryName(baseName);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -474,7 +459,7 @@ public final class StaticContext {
|
|||||||
// since local scope inherited from global scope.
|
// since local scope inherited from global scope.
|
||||||
// TODO: remove prefix when problem with scopes is solved
|
// 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);
|
MetadataProperties.setImported(result, true);
|
||||||
importStatements.add(JsAstUtils.newVar(result, declaration));
|
importStatements.add(JsAstUtils.newVar(result, declaration));
|
||||||
return result;
|
return result;
|
||||||
@@ -485,7 +470,7 @@ public final class StaticContext {
|
|||||||
ModuleDescriptor module = DescriptorUtilsKt.getModule(descriptor);
|
ModuleDescriptor module = DescriptorUtilsKt.getModule(descriptor);
|
||||||
JsName name = module != currentModule ?
|
JsName name = module != currentModule ?
|
||||||
importDeclaration(suggestedName, getQualifiedReference(descriptor)) :
|
importDeclaration(suggestedName, getQualifiedReference(descriptor)) :
|
||||||
rootFunction.getScope().declareTemporaryName(suggestedName);
|
fragment.getScope().declareTemporaryName(suggestedName);
|
||||||
MetadataProperties.setDescriptor(name, descriptor);
|
MetadataProperties.setDescriptor(name, descriptor);
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
@@ -611,16 +596,16 @@ public final class StaticContext {
|
|||||||
}
|
}
|
||||||
return getScopeForDescriptor(superclass).innerObjectScope("Scope for class " + descriptor.getName());
|
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
|
//TODO: never get there
|
||||||
Rule<JsScope> generateInnerScopesForMembers =
|
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 -> {
|
Rule<JsScope> createFunctionObjectsForCallableDescriptors = descriptor -> {
|
||||||
if (!(descriptor instanceof CallableDescriptor)) {
|
if (!(descriptor instanceof CallableDescriptor)) {
|
||||||
return null;
|
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;
|
assert (!scopeToFunction.containsKey(correspondingFunction.getScope())) : "Scope to function value overridden for " + descriptor;
|
||||||
scopeToFunction.put(correspondingFunction.getScope(), correspondingFunction);
|
scopeToFunction.put(correspondingFunction.getScope(), correspondingFunction);
|
||||||
return correspondingFunction.getScope();
|
return correspondingFunction.getScope();
|
||||||
@@ -712,23 +697,18 @@ public final class StaticContext {
|
|||||||
return deferredCallSites;
|
return deferredCallSites;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public JsFunction getRootFunction() {
|
|
||||||
return rootFunction;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public List<JsStatement> getTopLevelStatements() {
|
public List<JsStatement> getTopLevelStatements() {
|
||||||
return topLevelStatements;
|
return fragment.getInitializerBlock().getStatements();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public List<JsStatement> getDeclarationStatements() {
|
public List<JsStatement> getDeclarationStatements() {
|
||||||
return declarationStatements;
|
return fragment.getDeclarationBlock().getStatements();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addClass(@NotNull ClassDescriptor classDescriptor) {
|
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) {
|
public void export(@NotNull MemberDescriptor descriptor, boolean force) {
|
||||||
@@ -745,23 +725,23 @@ public final class StaticContext {
|
|||||||
return currentModule;
|
return currentModule;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void postProcess() {
|
/*public void postProcess() {
|
||||||
addInterfaceDefaultMethods();
|
addInterfaceDefaultMethods();
|
||||||
rootFunction.getBody().getStatements().addAll(importStatements);
|
rootFunction.getBody().getStatements().addAll(importStatements);
|
||||||
addClassPrototypes();
|
addClassPrototypes();
|
||||||
rootFunction.getBody().getStatements().addAll(declarationStatements);
|
rootFunction.getBody().getStatements().addAll(declarationStatements);
|
||||||
rootFunction.getBody().getStatements().addAll(exporter.getStatements());
|
rootFunction.getBody().getStatements().addAll(exporter.getStatements());
|
||||||
rootFunction.getBody().getStatements().addAll(topLevelStatements);
|
rootFunction.getBody().getStatements().addAll(topLevelStatements);
|
||||||
}
|
}*/
|
||||||
|
|
||||||
private void addClassPrototypes() {
|
/*private void addClassPrototypes() {
|
||||||
Set<ClassDescriptor> visited = new HashSet<>();
|
Set<ClassDescriptor> visited = new HashSet<>();
|
||||||
for (ClassDescriptor cls : classes) {
|
for (ClassDescriptor cls : classes) {
|
||||||
addClassPrototypes(cls, visited);
|
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 (!visited.add(cls)) return;
|
||||||
if (DescriptorUtilsKt.getModule(cls) != currentModule) return;
|
if (DescriptorUtilsKt.getModule(cls) != currentModule) return;
|
||||||
if (isNativeObject(cls) || isLibraryObject(cls)) return;
|
if (isNativeObject(cls) || isLibraryObject(cls)) return;
|
||||||
@@ -789,19 +769,5 @@ public final class StaticContext {
|
|||||||
JsExpression constructorRef = new JsNameRef("constructor", prototype.deepCopy());
|
JsExpression constructorRef = new JsNameRef("constructor", prototype.deepCopy());
|
||||||
statements.add(JsAstUtils.assignment(constructorRef, classRef.deepCopy()).makeStmt());
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-6
@@ -69,9 +69,9 @@ public class TranslationContext {
|
|||||||
private final VariableDescriptor continuationParameterDescriptor;
|
private final VariableDescriptor continuationParameterDescriptor;
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static TranslationContext rootContext(@NotNull StaticContext staticContext, @NotNull JsFunction rootFunction) {
|
public static TranslationContext rootContext(@NotNull StaticContext staticContext) {
|
||||||
JsBlock block = new JsBlock(staticContext.getTopLevelStatements());
|
DynamicContext rootDynamicContext = DynamicContext.rootContext(
|
||||||
DynamicContext rootDynamicContext = DynamicContext.rootContext(rootFunction.getScope(), block);
|
staticContext.getFragment().getScope(), staticContext.getFragment().getInitializerBlock());
|
||||||
AliasingContext rootAliasingContext = AliasingContext.getCleanContext();
|
AliasingContext rootAliasingContext = AliasingContext.getCleanContext();
|
||||||
return new TranslationContext(null, staticContext, rootDynamicContext, rootAliasingContext, null, null);
|
return new TranslationContext(null, staticContext, rootDynamicContext, rootAliasingContext, null, null);
|
||||||
}
|
}
|
||||||
@@ -544,7 +544,7 @@ public class TranslationContext {
|
|||||||
if (result == null &&
|
if (result == null &&
|
||||||
classOrConstructor instanceof ConstructorDescriptor &&
|
classOrConstructor instanceof ConstructorDescriptor &&
|
||||||
((ConstructorDescriptor) classOrConstructor).isPrimary()
|
((ConstructorDescriptor) classOrConstructor).isPrimary()
|
||||||
) {
|
) {
|
||||||
result = staticContext.getClassOrConstructorClosure((ClassDescriptor) classOrConstructor.getContainingDeclaration());
|
result = staticContext.getClassOrConstructorClosure((ClassDescriptor) classOrConstructor.getContainingDeclaration());
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@@ -645,7 +645,7 @@ public class TranslationContext {
|
|||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public JsName createGlobalName(@NotNull String suggestedName) {
|
public JsName createGlobalName(@NotNull String suggestedName) {
|
||||||
return staticContext.getRootFunction().getScope().declareTemporaryName(suggestedName);
|
return staticContext.getFragment().getScope().declareTemporaryName(suggestedName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@@ -655,7 +655,7 @@ public class TranslationContext {
|
|||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public JsFunction createRootScopedFunction(@NotNull String description) {
|
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) {
|
public void addClass(@NotNull ClassDescriptor classDescriptor) {
|
||||||
|
|||||||
+30
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
-195
@@ -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>()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-62
@@ -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.StaticContext;
|
||||||
import org.jetbrains.kotlin.js.translate.context.TemporaryVariable;
|
import org.jetbrains.kotlin.js.translate.context.TemporaryVariable;
|
||||||
import org.jetbrains.kotlin.js.translate.context.TranslationContext;
|
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.ExpressionVisitor;
|
||||||
import org.jetbrains.kotlin.js.translate.expression.PatternTranslator;
|
import org.jetbrains.kotlin.js.translate.expression.PatternTranslator;
|
||||||
import org.jetbrains.kotlin.js.translate.test.JSTestGenerator;
|
import org.jetbrains.kotlin.js.translate.test.JSTestGenerator;
|
||||||
import org.jetbrains.kotlin.js.translate.test.JSTester;
|
import org.jetbrains.kotlin.js.translate.test.JSTester;
|
||||||
import org.jetbrains.kotlin.js.translate.test.QUnitTester;
|
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.JsAstUtils;
|
||||||
import org.jetbrains.kotlin.js.translate.utils.mutator.AssignToExpressionMutator;
|
import org.jetbrains.kotlin.js.translate.utils.mutator.AssignToExpressionMutator;
|
||||||
import org.jetbrains.kotlin.psi.KtExpression;
|
import org.jetbrains.kotlin.psi.*;
|
||||||
import org.jetbrains.kotlin.psi.KtFile;
|
|
||||||
import org.jetbrains.kotlin.psi.KtNamedFunction;
|
|
||||||
import org.jetbrains.kotlin.psi.KtUnaryExpression;
|
|
||||||
import org.jetbrains.kotlin.resolve.BindingTrace;
|
import org.jetbrains.kotlin.resolve.BindingTrace;
|
||||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.BindingContextUtilsKt;
|
import org.jetbrains.kotlin.resolve.bindingContextUtil.BindingContextUtilsKt;
|
||||||
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
|
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.
|
* Goal is to simplify interaction between translators.
|
||||||
*/
|
*/
|
||||||
public final class Translation {
|
public final class Translation {
|
||||||
|
private static final String ENUM_SIGNATURE = "kotlin$Enum";
|
||||||
|
|
||||||
private Translation() {
|
private Translation() {
|
||||||
}
|
}
|
||||||
@@ -234,7 +234,7 @@ public final class Translation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static TranslationContext generateAst(
|
public static JsProgram generateAst(
|
||||||
@NotNull BindingTrace bindingTrace,
|
@NotNull BindingTrace bindingTrace,
|
||||||
@NotNull Collection<KtFile> files,
|
@NotNull Collection<KtFile> files,
|
||||||
@NotNull MainCallParameters mainCallParameters,
|
@NotNull MainCallParameters mainCallParameters,
|
||||||
@@ -253,38 +253,49 @@ public final class Translation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private static TranslationContext doGenerateAst(
|
private static JsProgram doGenerateAst(
|
||||||
@NotNull BindingTrace bindingTrace,
|
@NotNull BindingTrace bindingTrace,
|
||||||
@NotNull Collection<KtFile> files,
|
@NotNull Collection<KtFile> files,
|
||||||
@NotNull MainCallParameters mainCallParameters,
|
@NotNull MainCallParameters mainCallParameters,
|
||||||
@NotNull ModuleDescriptor moduleDescriptor,
|
@NotNull ModuleDescriptor moduleDescriptor,
|
||||||
@NotNull JsConfig config
|
@NotNull JsConfig config
|
||||||
) {
|
) {
|
||||||
StaticContext staticContext = StaticContext.generateStaticContext(bindingTrace, config, moduleDescriptor);
|
JsProgram program = new JsProgram();
|
||||||
JsProgram program = staticContext.getProgram();
|
JsFunction rootFunction = new JsFunction(program.getRootScope(), new JsBlock(), "root function");
|
||||||
JsName rootPackageName = program.getRootScope().declareName(Namer.getRootPackageName());
|
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();
|
JsBlock rootBlock = rootFunction.getBody();
|
||||||
|
|
||||||
List<JsStatement> statements = rootBlock.getStatements();
|
List<JsStatement> statements = rootBlock.getStatements();
|
||||||
|
|
||||||
program.getScope().declareName("_");
|
program.getScope().declareName("_");
|
||||||
|
|
||||||
TranslationContext context = TranslationContext.rootContext(staticContext, rootFunction);
|
//staticContext.postProcess();
|
||||||
PackageDeclarationTranslator.translateFiles(files, context);
|
|
||||||
staticContext.postProcess();
|
|
||||||
statements.add(0, program.getStringLiteral("use strict").makeStmt());
|
statements.add(0, program.getStringLiteral("use strict").makeStmt());
|
||||||
if (!staticContext.isBuiltinModule()) {
|
if (!isBuiltinModule(fragments)) {
|
||||||
defineModule(context, statements, config.getModuleId());
|
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)));
|
rootFunction.getParameters().add(new JsParameter((rootPackageName)));
|
||||||
|
|
||||||
// Invoke function passing modules as arguments
|
// Invoke function passing modules as arguments
|
||||||
// This should help minifier tool to recognize references to these modules as local variables and make them shorter.
|
// This should help minifier tool to recognize references to these modules as local variables and make them shorter.
|
||||||
List<JsImportedModule> importedModuleList = new ArrayList<>();
|
List<JsImportedModule> importedModuleList = new ArrayList<>();
|
||||||
|
|
||||||
|
/*
|
||||||
for (JsImportedModule importedModule : staticContext.getImportedModules()) {
|
for (JsImportedModule importedModule : staticContext.getImportedModules()) {
|
||||||
rootFunction.getParameters().add(new JsParameter(importedModule.getInternalName()));
|
rootFunction.getParameters().add(new JsParameter(importedModule.getInternalName()));
|
||||||
importedModuleList.add(importedModule);
|
importedModuleList.add(importedModule);
|
||||||
@@ -296,6 +307,7 @@ public final class Translation {
|
|||||||
statements.add(statement);
|
statements.add(statement);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
statements.add(new JsReturn(rootPackageName.makeRef()));
|
statements.add(new JsReturn(rootPackageName.makeRef()));
|
||||||
|
|
||||||
@@ -303,13 +315,46 @@ public final class Translation {
|
|||||||
block.getStatements().addAll(wrapIfNecessary(config.getModuleId(), rootFunction, importedModuleList, program,
|
block.getStatements().addAll(wrapIfNecessary(config.getModuleId(), rootFunction, importedModuleList, program,
|
||||||
config.getModuleKind()));
|
config.getModuleKind()));
|
||||||
|
|
||||||
return context;
|
return program;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void defineModule(@NotNull TranslationContext context, @NotNull List<JsStatement> statements, @NotNull String moduleId) {
|
private static boolean isBuiltinModule(@NotNull List<JsProgramFragment> fragments) {
|
||||||
JsName rootPackageName = context.scope().findName(Namer.getRootPackageName());
|
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) {
|
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());
|
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()
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user