JS: incremental translation of tests and main fun invocation
This commit is contained in:
@@ -158,11 +158,12 @@ public final class StaticContext {
|
||||
@NotNull BindingTrace bindingTrace,
|
||||
@NotNull JsConfig config,
|
||||
@NotNull ModuleDescriptor moduleDescriptor,
|
||||
@NotNull SourceFilePathResolver sourceFilePathResolver
|
||||
@NotNull SourceFilePathResolver sourceFilePathResolver,
|
||||
@NotNull String packageFqn
|
||||
) {
|
||||
program = new JsProgram();
|
||||
JsFunction rootFunction = JsAstUtils.createFunctionWithEmptyBody(program.getScope());
|
||||
fragment = new JsProgramFragment(rootFunction.getScope());
|
||||
fragment = new JsProgramFragment(rootFunction.getScope(), packageFqn);
|
||||
|
||||
this.bindingTrace = bindingTrace;
|
||||
this.namer = Namer.newInstance(program.getRootScope());
|
||||
|
||||
@@ -34,6 +34,8 @@ class Merger(private val rootFunction: JsFunction, val internalModuleName: JsNam
|
||||
val importBlock = JsGlobalBlock()
|
||||
private val declarationBlock = JsGlobalBlock()
|
||||
private val initializerBlock = JsGlobalBlock()
|
||||
private val testsMap = mutableMapOf<String, JsStatement>()
|
||||
private var mainFn: Pair<String, JsStatement>? = null
|
||||
private val exportBlock = JsGlobalBlock()
|
||||
private val declaredImports = mutableSetOf<String>()
|
||||
private val classes = mutableMapOf<JsName, JsClassModel>()
|
||||
@@ -55,11 +57,42 @@ class Merger(private val rootFunction: JsFunction, val internalModuleName: JsNam
|
||||
|
||||
declarationBlock.statements += fragment.declarationBlock
|
||||
initializerBlock.statements += fragment.initializerBlock
|
||||
fragment.tryUpdateTests()
|
||||
fragment.tryUpdateMain()
|
||||
addExportStatements(fragment)
|
||||
|
||||
classes += fragment.classes
|
||||
}
|
||||
|
||||
private fun JsProgramFragment.tryUpdateTests() {
|
||||
tests?.let { newTests ->
|
||||
testsMap.putIfAbsent(packageFqn, newTests)?.let { oldTests ->
|
||||
oldTests.decomposeTestInvocation()?.let {oldTestBody ->
|
||||
newTests.decomposeTestInvocation()?.let { newTestBody ->
|
||||
oldTestBody.statements += newTestBody.statements
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun JsStatement.decomposeTestInvocation(): JsBlock? {
|
||||
return (this as? JsExpressionStatement)?.let {
|
||||
(it.expression as? JsInvocation)?.let {
|
||||
(it.arguments[2] as? JsFunction)?.body
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun JsProgramFragment.tryUpdateMain() {
|
||||
mainFunction?.let { m ->
|
||||
val currentMainFqn = mainFn?.first
|
||||
if (currentMainFqn == null || currentMainFqn > packageFqn) {
|
||||
mainFn = packageFqn to m
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val importedModules: List<JsImportedModule>
|
||||
get() = importedModulesImpl
|
||||
|
||||
@@ -136,6 +169,9 @@ class Merger(private val rootFunction: JsFunction, val internalModuleName: JsNam
|
||||
fragment.classes += classes.map { it.name to it }
|
||||
|
||||
fragment.inlineModuleMap.forEach { (_, value) -> rename(value) }
|
||||
|
||||
fragment.tests?.let { rename(it) }
|
||||
fragment.mainFunction?.let { rename(it) }
|
||||
}
|
||||
|
||||
private fun <T: JsNode> Map<JsName, JsName>.rename(rootNode: T): T {
|
||||
@@ -174,6 +210,8 @@ class Merger(private val rootFunction: JsFunction, val internalModuleName: JsNam
|
||||
this += exportBlock.statements
|
||||
addClassPostDeclarations(this)
|
||||
this += initializerBlock.statements
|
||||
this += testsMap.values
|
||||
mainFn?.second?.let { this += it }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -340,15 +340,21 @@ public final class Translation {
|
||||
for (TranslationUnit unit : units) {
|
||||
if (unit instanceof TranslationUnit.SourceFile) {
|
||||
KtFile file = ((TranslationUnit.SourceFile) unit).getFile();
|
||||
StaticContext staticContext = new StaticContext(bindingTrace, config, moduleDescriptor, sourceFilePathResolver);
|
||||
StaticContext staticContext = new StaticContext(bindingTrace, config, moduleDescriptor, sourceFilePathResolver, file.getPackageFqName().asString());
|
||||
TranslationContext context = TranslationContext.rootContext(staticContext);
|
||||
List<DeclarationDescriptor> fileMemberScope = new ArrayList<>();
|
||||
translateFile(context, file, fileMemberScope);
|
||||
fragments.add(staticContext.getFragment());
|
||||
newFragments.add(staticContext.getFragment());
|
||||
fragmentMap.put(file, staticContext.getFragment());
|
||||
|
||||
JsProgramFragment fragment = staticContext.getFragment();
|
||||
|
||||
fragment.setTests(mayBeGenerateTests(context, file, fileMemberScope));
|
||||
fragment.setMainFunction(maybeGenerateCallToMain(context, config, moduleDescriptor, fileMemberScope, mainCallParameters));
|
||||
|
||||
fragments.add(fragment);
|
||||
newFragments.add(fragment);
|
||||
fragmentMap.put(file, fragment);
|
||||
fileMemberScopes.put(file, fileMemberScope);
|
||||
merger.addFragment(staticContext.getFragment());
|
||||
merger.addFragment(fragment);
|
||||
}
|
||||
else if (unit instanceof TranslationUnit.BinaryAst) {
|
||||
byte[] astData = ((TranslationUnit.BinaryAst) unit).getData();
|
||||
@@ -358,22 +364,8 @@ public final class Translation {
|
||||
}
|
||||
}
|
||||
|
||||
JsProgramFragment testFragment = mayBeGenerateTests(config, bindingTrace, moduleDescriptor, sourceFilePathResolver);
|
||||
fragments.add(testFragment);
|
||||
newFragments.add(testFragment);
|
||||
merger.addFragment(testFragment);
|
||||
rootFunction.getParameters().add(new JsParameter(internalModuleName));
|
||||
|
||||
if (mainCallParameters.shouldBeGenerated()) {
|
||||
JsProgramFragment mainCallFragment = generateCallToMain(
|
||||
bindingTrace, config, moduleDescriptor, sourceFilePathResolver, mainCallParameters.arguments());
|
||||
if (mainCallFragment != null) {
|
||||
fragments.add(mainCallFragment);
|
||||
newFragments.add(mainCallFragment);
|
||||
merger.addFragment(mainCallFragment);
|
||||
}
|
||||
}
|
||||
|
||||
merger.merge();
|
||||
|
||||
JsBlock rootBlock = rootFunction.getBody();
|
||||
@@ -447,30 +439,36 @@ public final class Translation {
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static JsProgramFragment mayBeGenerateTests(
|
||||
@NotNull JsConfig config, @NotNull BindingTrace trace,
|
||||
@NotNull ModuleDescriptor moduleDescriptor, @NotNull SourceFilePathResolver sourceFilePathResolver
|
||||
@Nullable
|
||||
private static JsStatement mayBeGenerateTests(
|
||||
@NotNull TranslationContext context,
|
||||
@NotNull KtFile file,
|
||||
@NotNull List<DeclarationDescriptor> fileMemberScope
|
||||
) {
|
||||
StaticContext staticContext = new StaticContext(trace, config, moduleDescriptor, sourceFilePathResolver);
|
||||
TranslationContext context = TranslationContext.rootContext(staticContext);
|
||||
|
||||
new JSTestGenerator(context).generateTestCalls(moduleDescriptor);
|
||||
|
||||
return staticContext.getFragment();
|
||||
return new JSTestGenerator(context).generateTestCalls(file, fileMemberScope);
|
||||
}
|
||||
|
||||
//TODO: determine whether should throw exception
|
||||
@Nullable
|
||||
private static JsProgramFragment generateCallToMain(
|
||||
@NotNull BindingTrace trace, @NotNull JsConfig config, @NotNull ModuleDescriptor moduleDescriptor,
|
||||
@NotNull SourceFilePathResolver sourceFilePathResolver,
|
||||
@NotNull List<String> arguments
|
||||
private static JsStatement maybeGenerateCallToMain(
|
||||
@NotNull TranslationContext context,
|
||||
@NotNull JsConfig config,
|
||||
@NotNull ModuleDescriptor moduleDescriptor,
|
||||
@NotNull List<DeclarationDescriptor> fileMemberScope,
|
||||
@NotNull MainCallParameters mainCallParameters
|
||||
) {
|
||||
StaticContext staticContext = new StaticContext(trace, config, moduleDescriptor, sourceFilePathResolver);
|
||||
TranslationContext context = TranslationContext.rootContext(staticContext);
|
||||
if (!mainCallParameters.shouldBeGenerated()) return null;
|
||||
|
||||
MainFunctionDetector mainFunctionDetector = new MainFunctionDetector(context.bindingContext(), config.getLanguageVersionSettings());
|
||||
FunctionDescriptor functionDescriptor = mainFunctionDetector.getMainFunction(moduleDescriptor);
|
||||
|
||||
FunctionDescriptor functionDescriptor = null;
|
||||
|
||||
for (DeclarationDescriptor d : fileMemberScope) {
|
||||
if (mainFunctionDetector.isMain(d)) {
|
||||
functionDescriptor = (FunctionDescriptor)d;
|
||||
}
|
||||
}
|
||||
|
||||
if (functionDescriptor == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -480,7 +478,7 @@ public final class Translation {
|
||||
|
||||
List<JsExpression> args = new ArrayList<>();
|
||||
if (parameterCount != 0) {
|
||||
args.add(new JsArrayLiteral(toStringLiteralList(arguments)));
|
||||
args.add(new JsArrayLiteral(toStringLiteralList(mainCallParameters.arguments())));
|
||||
}
|
||||
|
||||
if (functionDescriptor.isSuspend()) {
|
||||
@@ -492,7 +490,6 @@ public final class Translation {
|
||||
}
|
||||
|
||||
JsExpression call = CallTranslator.INSTANCE.buildCall(context, functionDescriptor, args, null);
|
||||
context.addTopLevelStatement(call.makeStmt());
|
||||
return staticContext.getFragment();
|
||||
return call.makeStmt();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,14 +24,25 @@ import org.jetbrains.kotlin.js.translate.reference.ReferenceTranslator
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.FqNameUnsafe
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
|
||||
class JSTestGenerator(val context: TranslationContext) {
|
||||
|
||||
fun generateTestCalls(moduleDescriptor: ModuleDescriptor) {
|
||||
generateTestCalls(moduleDescriptor, FqName.ROOT)
|
||||
fun generateTestCalls(file: KtFile, fileMemberScope: List<DeclarationDescriptor>): JsStatement? {
|
||||
val testsFunction = JsFunction(context.scope(), JsBlock(), "${file.virtualFilePath} file suite function")
|
||||
fileMemberScope.forEach {
|
||||
if (it is ClassDescriptor) {
|
||||
generateTestFunctions(it, testsFunction)
|
||||
}
|
||||
}
|
||||
if (!testsFunction.body.isEmpty) {
|
||||
val suiteName = JsStringLiteral(file.packageFqName.asString())
|
||||
return JsInvocation(suiteRef, suiteName, JsBooleanLiteral(false), testsFunction).makeStmt()
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun generateTestCalls(moduleDescriptor: ModuleDescriptor, packageName: FqName) {
|
||||
|
||||
Reference in New Issue
Block a user