JS: support inlining in new pipeline

This commit is contained in:
Alexey Andreev
2017-02-10 19:31:52 +03:00
parent a414cd64c5
commit 83140bc5f7
15 changed files with 157 additions and 62 deletions
@@ -17,10 +17,7 @@
package org.jetbrains.kotlin.resolve.calls.callUtil package org.jetbrains.kotlin.resolve.calls.callUtil
import com.intellij.psi.util.PsiTreeUtil import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.incremental.KotlinLookupLocation import org.jetbrains.kotlin.incremental.KotlinLookupLocation
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
@@ -210,6 +207,15 @@ fun KtExpression.getPropertyResolvedCallWithAssert(context: BindingContext): Res
return resolvedCall as ResolvedCall<out PropertyDescriptor> return resolvedCall as ResolvedCall<out PropertyDescriptor>
} }
fun KtExpression.getVariableResolvedCallWithAssert(context: BindingContext): ResolvedCall<out VariableDescriptor> {
val resolvedCall = getResolvedCallWithAssert(context)
assert(resolvedCall.resultingDescriptor is VariableDescriptor) {
"ResolvedCall for this expression must be ResolvedCall<? extends PropertyDescriptor>: ${this.getTextWithLocation()}"
}
@Suppress("UNCHECKED_CAST")
return resolvedCall as ResolvedCall<out VariableDescriptor>
}
fun KtExpression.getType(context: BindingContext): KotlinType? { fun KtExpression.getType(context: BindingContext): KotlinType? {
val type = context.getType(this) val type = context.getType(this)
if (type != null) return type if (type != null) return type
@@ -17,6 +17,7 @@ public class JsProgramFragment {
private final JsGlobalBlock initializerBlock = new JsGlobalBlock(); private final JsGlobalBlock initializerBlock = new JsGlobalBlock();
private final List<JsNameBinding> nameBindings = new ArrayList<JsNameBinding>(); private final List<JsNameBinding> nameBindings = new ArrayList<JsNameBinding>();
private final Map<JsName, JsClassModel> classes = new LinkedHashMap<JsName, JsClassModel>(); private final Map<JsName, JsClassModel> classes = new LinkedHashMap<JsName, JsClassModel>();
private final Map<String, JsExpression> inlineModuleMap = new LinkedHashMap<String, JsExpression>();
public JsProgramFragment(@NotNull JsScope scope) { public JsProgramFragment(@NotNull JsScope scope) {
this.scope = scope; this.scope = scope;
@@ -61,4 +62,9 @@ public class JsProgramFragment {
public Map<JsName, JsClassModel> getClasses() { public Map<JsName, JsClassModel> getClasses() {
return classes; return classes;
} }
@NotNull
public Map<String, JsExpression> getInlineModuleMap() {
return inlineModuleMap;
}
} }
@@ -27,9 +27,7 @@ import org.jetbrains.kotlin.js.inline.util.IdentitySet
import org.jetbrains.kotlin.js.inline.util.isCallInvocation import org.jetbrains.kotlin.js.inline.util.isCallInvocation
import org.jetbrains.kotlin.js.parser.parseFunction import org.jetbrains.kotlin.js.parser.parseFunction
import org.jetbrains.kotlin.js.translate.context.Namer import org.jetbrains.kotlin.js.translate.context.Namer
import org.jetbrains.kotlin.js.translate.context.TranslationContext
import org.jetbrains.kotlin.js.translate.reference.CallExpressionTranslator import org.jetbrains.kotlin.js.translate.reference.CallExpressionTranslator
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils.getModuleName import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils.getModuleName
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
import org.jetbrains.kotlin.resolve.inline.InlineStrategy import org.jetbrains.kotlin.resolve.inline.InlineStrategy
@@ -48,7 +46,7 @@ private val JS_IDENTIFIER="[$JS_IDENTIFIER_START][$JS_IDENTIFIER_PART]*"
private val DEFINE_MODULE_PATTERN = ("($JS_IDENTIFIER)\\.defineModule\\(\\s*(['\"])([^'\"]+)\\2\\s*,\\s*(\\w+)\\s*\\)").toRegex().toPattern() private val DEFINE_MODULE_PATTERN = ("($JS_IDENTIFIER)\\.defineModule\\(\\s*(['\"])([^'\"]+)\\2\\s*,\\s*(\\w+)\\s*\\)").toRegex().toPattern()
private val DEFINE_MODULE_FIND_PATTERN = ".defineModule(" private val DEFINE_MODULE_FIND_PATTERN = ".defineModule("
class FunctionReader(private val context: TranslationContext) { class FunctionReader(private val config: LibrarySourcesConfig, private val currentModuleName: JsName, fragments: List<JsProgramFragment>) {
/** /**
* fileContent: .js file content, that contains this module definition. * fileContent: .js file content, that contains this module definition.
* One file can contain more than one module definition. * One file can contain more than one module definition.
@@ -63,8 +61,12 @@ class FunctionReader(private val context: TranslationContext) {
private val moduleNameToInfo = HashMultimap.create<String, ModuleInfo>() private val moduleNameToInfo = HashMultimap.create<String, ModuleInfo>()
private val moduleNameMap: Map<String, JsExpression>
init { init {
val libs = context.config.libraries.map(::File) val libs = config.libraries.map(::File)
moduleNameMap = buildModuleNameMap(fragments)
JsLibraryUtils.traverseJsLibraries(libs) { fileContent, _ -> JsLibraryUtils.traverseJsLibraries(libs) { fileContent, _ ->
var current = 0 var current = 0
@@ -86,6 +88,13 @@ class FunctionReader(private val context: TranslationContext) {
} }
} }
// Since we compile each source file in its own context (and we may loose these context when performing incremental compilation)
// we don't use contexts to generate proper names for modules. Instead, we generate all necessary information during
// translation and rely on it here.
private fun buildModuleNameMap(fragments: List<JsProgramFragment>): Map<String, JsExpression> {
return fragments.flatMap { it.inlineModuleMap.entries }.associate { (k, v) -> k to v }
}
private fun rewindToIdentifierStart(text: String, index: Int): Int { private fun rewindToIdentifierStart(text: String, index: Int): Int {
var result = index var result = index
while (result > 0 && Character.isJavaIdentifierPart(text[result - 1])) { while (result > 0 && Character.isJavaIdentifierPart(text[result - 1])) {
@@ -112,7 +121,7 @@ class FunctionReader(private val context: TranslationContext) {
operator fun contains(descriptor: CallableDescriptor): Boolean { operator fun contains(descriptor: CallableDescriptor): Boolean {
val moduleName = getModuleName(descriptor) val moduleName = getModuleName(descriptor)
val currentModuleName = context.config.moduleId val currentModuleName = config.moduleId
return currentModuleName != moduleName && moduleName in moduleNameToInfo.keys() return currentModuleName != moduleName && moduleName in moduleNameToInfo.keys()
} }
@@ -144,7 +153,7 @@ class FunctionReader(private val context: TranslationContext) {
} }
val function = parseFunction(source, offset, ThrowExceptionOnErrorReporter, JsRootScope(JsProgram())) val function = parseFunction(source, offset, ThrowExceptionOnErrorReporter, JsRootScope(JsProgram()))
val moduleReference = context.getModuleExpressionFor(descriptor) ?: getRootPackage() val moduleReference = moduleNameMap[tag] ?: currentModuleName.makeRef()
val replacements = hashMapOf(info.moduleVariable to moduleReference, val replacements = hashMapOf(info.moduleVariable to moduleReference,
info.kotlinVariable to Namer.kotlinObject()) info.kotlinVariable to Namer.kotlinObject())
@@ -152,11 +161,6 @@ class FunctionReader(private val context: TranslationContext) {
function.markInlineArguments(descriptor) function.markInlineArguments(descriptor)
return function return function
} }
private fun getRootPackage(): JsExpression {
val rootName = context.program().rootScope.declareName(Namer.getRootPackageName())
return JsAstUtils.pureFqn(rootName, null)
}
} }
private val Char.isWhitespaceOrComma: Boolean private val Char.isWhitespaceOrComma: Boolean
@@ -28,6 +28,8 @@ import org.jetbrains.kotlin.diagnostics.DiagnosticSink;
import org.jetbrains.kotlin.diagnostics.Errors; import org.jetbrains.kotlin.diagnostics.Errors;
import org.jetbrains.kotlin.js.backend.ast.*; import org.jetbrains.kotlin.js.backend.ast.*;
import org.jetbrains.kotlin.js.backend.ast.metadata.MetadataProperties; import org.jetbrains.kotlin.js.backend.ast.metadata.MetadataProperties;
import org.jetbrains.kotlin.js.config.JsConfig;
import org.jetbrains.kotlin.js.config.LibrarySourcesConfig;
import org.jetbrains.kotlin.js.inline.clean.FunctionPostProcessor; import org.jetbrains.kotlin.js.inline.clean.FunctionPostProcessor;
import org.jetbrains.kotlin.js.inline.clean.RemoveUnusedFunctionDefinitionsKt; import org.jetbrains.kotlin.js.inline.clean.RemoveUnusedFunctionDefinitionsKt;
import org.jetbrains.kotlin.js.inline.clean.RemoveUnusedLocalFunctionDeclarationsKt; import org.jetbrains.kotlin.js.inline.clean.RemoveUnusedLocalFunctionDeclarationsKt;
@@ -37,7 +39,6 @@ import org.jetbrains.kotlin.js.inline.context.NamingContext;
import org.jetbrains.kotlin.js.inline.util.CollectUtilsKt; import org.jetbrains.kotlin.js.inline.util.CollectUtilsKt;
import org.jetbrains.kotlin.js.inline.util.CollectionUtilsKt; import org.jetbrains.kotlin.js.inline.util.CollectionUtilsKt;
import org.jetbrains.kotlin.js.inline.util.NamingUtilsKt; import org.jetbrains.kotlin.js.inline.util.NamingUtilsKt;
import org.jetbrains.kotlin.js.translate.context.TranslationContext;
import org.jetbrains.kotlin.resolve.inline.InlineStrategy; import org.jetbrains.kotlin.resolve.inline.InlineStrategy;
import java.util.*; import java.util.*;
@@ -61,15 +62,34 @@ public class JsInliner extends JsVisitorWithContextImpl {
private final Function1<JsNode, Boolean> canBeExtractedByInliner = private final Function1<JsNode, Boolean> canBeExtractedByInliner =
node -> node instanceof JsInvocation && hasToBeInlined((JsInvocation) node); node -> node instanceof JsInvocation && hasToBeInlined((JsInvocation) node);
public static JsProgram process(@NotNull TranslationContext context) { public static void process(
JsProgram program = context.program(); @NotNull JsConfig config,
Map<JsName, JsFunction> functions = CollectUtilsKt.collectNamedFunctions(program); @NotNull DiagnosticSink trace,
Map<String, JsFunction> accessors = CollectUtilsKt.collectAccessors(program); @NotNull JsName currentModuleName,
new DummyAccessorInvocationTransformer().accept(program); @NotNull List<JsProgramFragment> fragments,
JsInliner inliner = new JsInliner(functions, accessors, new FunctionReader(context), context.bindingTrace()); @NotNull List<JsProgramFragment> fragmentsToProcess
inliner.accept(program); ) {
RemoveUnusedFunctionDefinitionsKt.removeUnusedFunctionDefinitions(program, functions); Map<JsName, JsFunction> functions = CollectUtilsKt.collectNamedFunctions(fragments);
return program; Map<String, JsFunction> accessors = CollectUtilsKt.collectAccessors(fragments);
DummyAccessorInvocationTransformer accessorInvocationTransformer = new DummyAccessorInvocationTransformer();
for (JsProgramFragment fragment : fragmentsToProcess) {
accessorInvocationTransformer.accept(fragment.getDeclarationBlock());
accessorInvocationTransformer.accept(fragment.getInitializerBlock());
}
FunctionReader functionReader = new FunctionReader((LibrarySourcesConfig) config, currentModuleName, fragments);
JsInliner inliner = new JsInliner(functions, accessors, functionReader, trace);
for (JsProgramFragment fragment : fragmentsToProcess) {
inliner.inliningContexts.push(inliner.new JsInliningContext(null, fragment.getScope()));
inliner.accept(fragment.getDeclarationBlock());
// There can be inlined function in top-level initializers, we need to optimize them as well
JsFunction fakeInitFunction = new JsFunction(JsDynamicScope.INSTANCE, fragment.getInitializerBlock(), "");
inliner.accept(fakeInitFunction);
inliner.inliningContexts.pop();
JsBlock block = new JsBlock(fragment.getDeclarationBlock(), fragment.getInitializerBlock(), fragment.getExportBlock());
RemoveUnusedFunctionDefinitionsKt.removeUnusedFunctionDefinitions(block, functions);
}
} }
private JsInliner( private JsInliner(
@@ -86,7 +106,7 @@ public class JsInliner extends JsVisitorWithContextImpl {
@Override @Override
public boolean visit(@NotNull JsFunction function, @NotNull JsContext context) { public boolean visit(@NotNull JsFunction function, @NotNull JsContext context) {
inliningContexts.push(new JsInliningContext(function)); inliningContexts.push(new JsInliningContext(function, function.getScope()));
assert !inProcessFunctions.contains(function): "Inliner has revisited function"; assert !inProcessFunctions.contains(function): "Inliner has revisited function";
inProcessFunctions.add(function); inProcessFunctions.add(function);
@@ -266,8 +286,8 @@ public class JsInliner extends JsVisitorWithContextImpl {
private class JsInliningContext implements InliningContext { private class JsInliningContext implements InliningContext {
private final FunctionContext functionContext; private final FunctionContext functionContext;
JsInliningContext(@NotNull JsFunction function) { JsInliningContext(@NotNull JsScope scope) {
functionContext = new FunctionContext(function, functionReader) { functionContext = new FunctionContext(scope) {
@Nullable @Nullable
@Override @Override
protected JsFunction lookUpStaticFunction(@Nullable JsName functionName) { protected JsFunction lookUpStaticFunction(@Nullable JsName functionName) {
@@ -24,7 +24,7 @@ import org.jetbrains.kotlin.js.inline.util.*
import org.jetbrains.kotlin.js.translate.context.Namer import org.jetbrains.kotlin.js.translate.context.Namer
abstract class FunctionContext( abstract class FunctionContext(
private val function: JsFunction, private val scope: JsScope,
private val functionReader: FunctionReader private val functionReader: FunctionReader
) { ) {
protected abstract fun lookUpStaticFunction(functionName: JsName?): JsFunction? protected abstract fun lookUpStaticFunction(functionName: JsName?): JsFunction?
@@ -40,7 +40,7 @@ abstract class FunctionContext(
} }
fun getScope(): JsScope { fun getScope(): JsScope {
return function.scope return scope
} }
/** /**
@@ -137,6 +137,15 @@ fun collectNamedFunctions(scope: JsNode) = collectNamedFunctionsAndMetadata(scop
fun collectNamedFunctionsOrMetadata(scope: JsNode) = collectNamedFunctionsAndMetadata(scope).mapValues { it.value.second } fun collectNamedFunctionsOrMetadata(scope: JsNode) = collectNamedFunctionsAndMetadata(scope).mapValues { it.value.second }
fun collectNamedFunctions(fragments: List<JsProgramFragment>): Map<JsName, JsFunction> {
val result = mutableMapOf<JsName, JsFunction>()
for (fragment in fragments) {
result += collectNamedFunctions(fragment.declarationBlock)
result += collectNamedFunctions(fragment.initializerBlock)
}
return result
}
fun collectNamedFunctionsAndMetadata(scope: JsNode): Map<JsName, Pair<JsFunction, JsExpression>> { fun collectNamedFunctionsAndMetadata(scope: JsNode): Map<JsName, Pair<JsFunction, JsExpression>> {
val namedFunctions = mutableMapOf<JsName, Pair<JsFunction, JsExpression>>() val namedFunctions = mutableMapOf<JsName, Pair<JsFunction, JsExpression>>()
@@ -200,6 +209,14 @@ fun collectAccessors(scope: JsNode): Map<String, JsFunction> {
return accessors return accessors
} }
fun collectAccessors(fragments: List<JsProgramFragment>): Map<String, JsFunction> {
val result = mutableMapOf<String, JsFunction>()
for (fragment in fragments) {
result += collectAccessors(fragment.declarationBlock)
}
return result
}
fun <T : JsNode> collectInstances(klass: Class<T>, scope: JsNode): List<T> { fun <T : JsNode> collectInstances(klass: Class<T>, scope: JsNode): List<T> {
return with(InstanceCollector(klass, visitNestedDeclarations = false)) { return with(InstanceCollector(klass, visitNestedDeclarations = false)) {
accept(scope) accept(scope)
@@ -21,12 +21,14 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.ModuleDescriptor; import org.jetbrains.kotlin.descriptors.ModuleDescriptor;
import org.jetbrains.kotlin.js.analyze.TopDownAnalyzerFacadeForJS; import org.jetbrains.kotlin.js.analyze.TopDownAnalyzerFacadeForJS;
import org.jetbrains.kotlin.js.analyzer.JsAnalysisResult; import org.jetbrains.kotlin.js.analyzer.JsAnalysisResult;
import org.jetbrains.kotlin.js.backend.ast.JsProgram; import org.jetbrains.kotlin.js.backend.ast.JsImportedModule;
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.general.AstGenerationResult;
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;
@@ -74,18 +76,20 @@ public final class K2JSTranslator {
ModuleDescriptor moduleDescriptor = analysisResult.getModuleDescriptor(); ModuleDescriptor moduleDescriptor = analysisResult.getModuleDescriptor();
Diagnostics diagnostics = bindingTrace.getBindingContext().getDiagnostics(); Diagnostics diagnostics = bindingTrace.getBindingContext().getDiagnostics();
JsProgram program = Translation.generateAst(bindingTrace, files, mainCallParameters, moduleDescriptor, config); AstGenerationResult translationResult = 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);
//JsInliner.process(program); JsInliner.process(config, analysisResult.getBindingTrace(), translationResult.getInnerModuleName(),
ResolveTemporaryNamesKt.resolveTemporaryNames(program); translationResult.getFragments(), translationResult.getFragments());
ResolveTemporaryNamesKt.resolveTemporaryNames(translationResult.getProgram());
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled(); ProgressIndicatorAndCompilationCanceledStatus.checkCanceled();
if (hasError(diagnostics)) return new TranslationResult.Fail(diagnostics); if (hasError(diagnostics)) return new TranslationResult.Fail(diagnostics);
CoroutineTransformer coroutineTransformer = new CoroutineTransformer(program); CoroutineTransformer coroutineTransformer = new CoroutineTransformer(translationResult.getProgram());
coroutineTransformer.accept(program); coroutineTransformer.accept(translationResult.getProgram());
RemoveUnusedImportsKt.removeUnusedImports(program); RemoveUnusedImportsKt.removeUnusedImports(translationResult.getProgram());
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled(); ProgressIndicatorAndCompilationCanceledStatus.checkCanceled();
if (hasError(diagnostics)) return new TranslationResult.Fail(diagnostics); if (hasError(diagnostics)) return new TranslationResult.Fail(diagnostics);
@@ -93,10 +97,10 @@ public final class K2JSTranslator {
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled(); ProgressIndicatorAndCompilationCanceledStatus.checkCanceled();
List<String> importedModules = new ArrayList<>(); List<String> importedModules = new ArrayList<>();
/*for (JsImportedModule module : context.getImportedModules()) { for (JsImportedModule module : translationResult.getImportedModuleList()) {
importedModules.add(module.getExternalName()); importedModules.add(module.getExternalName());
}*/ }
return new TranslationResult.Success(config, files, program, diagnostics, importedModules, moduleDescriptor, return new TranslationResult.Success(config, files, translationResult.getProgram(), diagnostics, importedModules,
bindingTrace.getBindingContext()); moduleDescriptor, bindingTrace.getBindingContext());
} }
} }
@@ -34,7 +34,8 @@ internal class DeclarationExporter(val context: StaticContext) {
private val objectLikeKinds = setOf(ClassKind.OBJECT, ClassKind.ENUM_ENTRY) private val objectLikeKinds = setOf(ClassKind.OBJECT, ClassKind.ENUM_ENTRY)
private val exportedDeclarations = mutableSetOf<MemberDescriptor>() private val exportedDeclarations = mutableSetOf<MemberDescriptor>()
private val localPackageNames = mutableMapOf<FqName, JsName>() private val localPackageNames = mutableMapOf<FqName, JsName>()
val statements = mutableListOf<JsStatement>() private val statements: MutableList<JsStatement>
get() = context.fragment.exportBlock.statements
fun export(descriptor: MemberDescriptor, force: Boolean) { fun export(descriptor: MemberDescriptor, force: Boolean) {
if (exportedDeclarations.contains(descriptor)) return if (exportedDeclarations.contains(descriptor)) return
@@ -642,7 +642,7 @@ public final class StaticContext {
} }
@Nullable @Nullable
public JsExpression getModuleExpressionFor(@NotNull DeclarationDescriptor descriptor) { private JsExpression getModuleExpressionFor(@NotNull DeclarationDescriptor descriptor) {
JsName name = getModuleInnerName(descriptor); JsName name = getModuleInnerName(descriptor);
return name != null ? JsAstUtils.pureFqn(name, null) : null; return name != null ? JsAstUtils.pureFqn(name, null) : null;
} }
@@ -748,6 +748,11 @@ public final class StaticContext {
return currentModule; return currentModule;
} }
public void addInlineCall(@NotNull CallableDescriptor descriptor) {
String tag = Namer.getFunctionTag(descriptor);
fragment.getInlineModuleMap().put(tag, getModuleExpressionFor(descriptor));
}
/*public void postProcess() { /*public void postProcess() {
addInterfaceDefaultMethods(); addInterfaceDefaultMethods();
rootFunction.getBody().getStatements().addAll(importStatements); rootFunction.getBody().getStatements().addAll(importStatements);
@@ -625,9 +625,8 @@ public class TranslationContext {
callSites.add(new DeferredCallSite(constructor, invocationArgs, this)); callSites.add(new DeferredCallSite(constructor, invocationArgs, this));
} }
@Nullable public void addInlineCall(@NotNull CallableDescriptor descriptor) {
public JsExpression getModuleExpressionFor(@NotNull DeclarationDescriptor descriptor) { staticContext.addInlineCall(descriptor);
return staticContext.getModuleExpressionFor(descriptor);
} }
public void addDeclarationStatement(@NotNull JsStatement statement) { public void addDeclarationStatement(@NotNull JsStatement statement) {
@@ -0,0 +1,29 @@
/*
* 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.JsImportedModule
import org.jetbrains.kotlin.js.backend.ast.JsName
import org.jetbrains.kotlin.js.backend.ast.JsProgram
import org.jetbrains.kotlin.js.backend.ast.JsProgramFragment
class AstGenerationResult(
val program: JsProgram,
val innerModuleName: JsName,
val fragments: List<JsProgramFragment>,
val importedModuleList: List<JsImportedModule>
)
@@ -16,12 +16,13 @@
package org.jetbrains.kotlin.js.translate.general package org.jetbrains.kotlin.js.translate.general
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.js.backend.ast.* import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.js.backend.ast.metadata.coroutineMetadata import org.jetbrains.kotlin.js.backend.ast.metadata.coroutineMetadata
import org.jetbrains.kotlin.js.translate.context.Namer import org.jetbrains.kotlin.js.translate.context.Namer
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
class Merger(private val rootFunction: JsFunction, val internalModuleName: JsName) { class Merger(private val rootFunction: JsFunction, val internalModuleName: JsName, val module: ModuleDescriptor) {
// Maps unique signature (see generateSignature) to names // Maps unique signature (see generateSignature) to names
private val nameTable = mutableMapOf<String, JsName>() private val nameTable = mutableMapOf<String, JsName>()
private val importedModuleTable = mutableMapOf<JsImportedModuleKey, JsName>() private val importedModuleTable = mutableMapOf<JsImportedModuleKey, JsName>()
@@ -114,6 +115,7 @@ class Merger(private val rootFunction: JsFunction, val internalModuleName: JsNam
this += importBlock.statements this += importBlock.statements
addClassPrototypes(this) addClassPrototypes(this)
this += declarationBlock.statements this += declarationBlock.statements
this += exportBlock.statements
this += initializerBlock.statements this += initializerBlock.statements
} }
} }
@@ -234,7 +234,7 @@ public final class Translation {
} }
@NotNull @NotNull
public static JsProgram generateAst( public static AstGenerationResult generateAst(
@NotNull BindingTrace bindingTrace, @NotNull BindingTrace bindingTrace,
@NotNull Collection<KtFile> files, @NotNull Collection<KtFile> files,
@NotNull MainCallParameters mainCallParameters, @NotNull MainCallParameters mainCallParameters,
@@ -253,7 +253,7 @@ public final class Translation {
} }
@NotNull @NotNull
private static JsProgram doGenerateAst( private static AstGenerationResult doGenerateAst(
@NotNull BindingTrace bindingTrace, @NotNull BindingTrace bindingTrace,
@NotNull Collection<KtFile> files, @NotNull Collection<KtFile> files,
@NotNull MainCallParameters mainCallParameters, @NotNull MainCallParameters mainCallParameters,
@@ -263,7 +263,7 @@ public final class Translation {
JsProgram program = new JsProgram(); JsProgram program = new JsProgram();
JsFunction rootFunction = new JsFunction(program.getRootScope(), new JsBlock(), "root function"); JsFunction rootFunction = new JsFunction(program.getRootScope(), new JsBlock(), "root function");
JsName internalModuleName = program.getScope().declareName("_"); JsName internalModuleName = program.getScope().declareName("_");
Merger merger = new Merger(rootFunction, internalModuleName); Merger merger = new Merger(rootFunction, internalModuleName, moduleDescriptor);
List<JsProgramFragment> fragments = new ArrayList<JsProgramFragment>(); List<JsProgramFragment> fragments = new ArrayList<JsProgramFragment>();
for (KtFile file : files) { for (KtFile file : files) {
@@ -280,15 +280,13 @@ public final class Translation {
List<JsStatement> statements = rootBlock.getStatements(); List<JsStatement> statements = rootBlock.getStatements();
//staticContext.postProcess();
statements.add(0, program.getStringLiteral("use strict").makeStmt()); statements.add(0, program.getStringLiteral("use strict").makeStmt());
if (!isBuiltinModule(fragments)) { if (!isBuiltinModule(fragments)) {
defineModule(program, 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(internalModuleName));
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.
@@ -307,13 +305,13 @@ public final class Translation {
} }
*/ */
statements.add(new JsReturn(rootPackageName.makeRef())); statements.add(new JsReturn(internalModuleName.makeRef()));
JsBlock block = program.getGlobalBlock(); JsBlock block = program.getGlobalBlock();
block.getStatements().addAll(wrapIfNecessary(config.getModuleId(), rootFunction, importedModuleList, program, block.getStatements().addAll(wrapIfNecessary(config.getModuleId(), rootFunction, importedModuleList, program,
config.getModuleKind())); config.getModuleKind()));
return program; return new AstGenerationResult(program, internalModuleName, fragments, importedModuleList);
} }
private static boolean isBuiltinModule(@NotNull List<JsProgramFragment> fragments) { private static boolean isBuiltinModule(@NotNull List<JsProgramFragment> fragments) {
@@ -39,13 +39,12 @@ public class VariableAccessTranslator extends AbstractTranslator implements Acce
@NotNull KtReferenceExpression referenceExpression, @NotNull KtReferenceExpression referenceExpression,
@Nullable JsExpression receiver @Nullable JsExpression receiver
) { ) {
ResolvedCall<?> resolvedCall = CallUtilKt.getResolvedCallWithAssert(referenceExpression, context.bindingContext()); ResolvedCall<? extends VariableDescriptor> resolvedCall =
CallUtilKt.getVariableResolvedCallWithAssert(referenceExpression, context.bindingContext());
if (resolvedCall instanceof VariableAsFunctionResolvedCall) { if (resolvedCall instanceof VariableAsFunctionResolvedCall) {
resolvedCall = ((VariableAsFunctionResolvedCall) resolvedCall).getVariableCall(); resolvedCall = ((VariableAsFunctionResolvedCall) resolvedCall).getVariableCall();
} }
assert resolvedCall.getResultingDescriptor() instanceof VariableDescriptor; return new VariableAccessTranslator(context, referenceExpression, resolvedCall, receiver);
return new VariableAccessTranslator(context, referenceExpression, (ResolvedCall<? extends VariableDescriptor>) resolvedCall,
receiver);
} }
@@ -75,7 +74,7 @@ public class VariableAccessTranslator extends AbstractTranslator implements Acce
if (InlineUtil.isInline(getter)) { if (InlineUtil.isInline(getter)) {
if (e instanceof JsNameRef) { if (e instanceof JsNameRef) {
// Get was translated as a name reference // Get was translated as a name reference
setInlineCallMetadata((JsNameRef) e, referenceExpression, getter); setInlineCallMetadata((JsNameRef) e, referenceExpression, getter, context());
} else { } else {
setInlineCallMetadata(e, referenceExpression, getter, context()); setInlineCallMetadata(e, referenceExpression, getter, context());
} }
@@ -94,7 +93,7 @@ public class VariableAccessTranslator extends AbstractTranslator implements Acce
if (InlineUtil.isInline(setter)) { if (InlineUtil.isInline(setter)) {
if (e instanceof JsBinaryOperation && ((JsBinaryOperation) e).getOperator().isAssignment()) { if (e instanceof JsBinaryOperation && ((JsBinaryOperation) e).getOperator().isAssignment()) {
// Set was translated as an assignment // Set was translated as an assignment
setInlineCallMetadata((JsNameRef) (((JsBinaryOperation) e).getArg1()), referenceExpression, setter); setInlineCallMetadata((JsNameRef) (((JsBinaryOperation) e).getArg1()), referenceExpression, setter, context());
} else { } else {
setInlineCallMetadata(e, referenceExpression, setter, context()); setInlineCallMetadata(e, referenceExpression, setter, context());
} }
@@ -64,6 +64,8 @@ fun setInlineCallMetadata(
} }
visitor.accept(expression) visitor.accept(expression)
context.addInlineCall(descriptor)
} }
fun setInlineCallMetadata( fun setInlineCallMetadata(
@@ -76,11 +78,14 @@ fun setInlineCallMetadata(
fun setInlineCallMetadata( fun setInlineCallMetadata(
nameRef: JsNameRef, nameRef: JsNameRef,
psiElement: KtExpression, psiElement: KtExpression,
descriptor: CallableDescriptor descriptor: CallableDescriptor,
context: TranslationContext
) { ) {
nameRef.descriptor = descriptor nameRef.descriptor = descriptor
nameRef.inlineStrategy = InlineStrategy.IN_PLACE nameRef.inlineStrategy = InlineStrategy.IN_PLACE
nameRef.psiElement = psiElement nameRef.psiElement = psiElement
context.addInlineCall(descriptor)
} }
fun TranslationContext.aliasedName(descriptor: CallableDescriptor): JsName { fun TranslationContext.aliasedName(descriptor: CallableDescriptor): JsName {