From 035ac67d8241a700aa196a18c42eca3bc5733668 Mon Sep 17 00:00:00 2001 From: Louis Grignon Date: Tue, 1 Sep 2020 17:30:09 +0200 Subject: [PATCH] try to avoid cyclic dependencies when generating file-by-file modules (JSweet 2 catch-up https://github.com/cincheo/jsweet/commit/2e7ae54ac281737e6f96b38eecb8530eaa579b8a) --- .../org/jsweet/transpiler/JSweetContext.java | 2 + .../jsweet/transpiler/JSweetTranspiler.java | 2 + .../transpiler/Java2TypeScriptTranslator.java | 47 ++++++++++++------- .../StaticInitilializerAnalyzer.java | 21 ++++----- .../java/org/jsweet/transpiler/util/Util.java | 4 ++ 5 files changed, 47 insertions(+), 29 deletions(-) diff --git a/transpiler/src/main/java/org/jsweet/transpiler/JSweetContext.java b/transpiler/src/main/java/org/jsweet/transpiler/JSweetContext.java index a169718e..38e5fd22 100644 --- a/transpiler/src/main/java/org/jsweet/transpiler/JSweetContext.java +++ b/transpiler/src/main/java/org/jsweet/transpiler/JSweetContext.java @@ -112,6 +112,8 @@ public class JSweetContext { } private Map jdkSubclasses = new HashMap<>(); + + public StaticInitilializerAnalyzer referenceAnalyzer; /** * Maps the name of a class to the JDK type it extends. diff --git a/transpiler/src/main/java/org/jsweet/transpiler/JSweetTranspiler.java b/transpiler/src/main/java/org/jsweet/transpiler/JSweetTranspiler.java index de6ca1c4..f6809aea 100644 --- a/transpiler/src/main/java/org/jsweet/transpiler/JSweetTranspiler.java +++ b/transpiler/src/main/java/org/jsweet/transpiler/JSweetTranspiler.java @@ -837,6 +837,8 @@ public class JSweetTranspiler implements JSweetOptions, AutoCloseable { factory.createBeforeTranslationScanner(transpilationHandler, context).process(context.compilationUnits); if (context.useModules) { + StaticInitilializerAnalyzer analizer = new StaticInitilializerAnalyzer(context); + analizer.process(context.compilationUnits); generateTsFiles(transpilationHandler, files, context.compilationUnits); } else { if (bundle) { diff --git a/transpiler/src/main/java/org/jsweet/transpiler/Java2TypeScriptTranslator.java b/transpiler/src/main/java/org/jsweet/transpiler/Java2TypeScriptTranslator.java index 9954c2b3..5863208f 100644 --- a/transpiler/src/main/java/org/jsweet/transpiler/Java2TypeScriptTranslator.java +++ b/transpiler/src/main/java/org/jsweet/transpiler/Java2TypeScriptTranslator.java @@ -609,22 +609,33 @@ public class Java2TypeScriptTranslator extends AbstractTreePrinter { // qualName.replace(".", "_")... this would work in the general // case... - if (direct) { - context.addHeader("import." + targetName, "import " + targetName + " = " + moduleName + ";\n"); - } else { + if (!context.moduleBundleMode && !(sourceElement instanceof TypeElement + && context.referenceAnalyzer.isDependent(compilationUnit, (TypeElement) sourceElement))) { - boolean fullImport = require || GLOBALS_CLASS_NAME.equals(targetName); - if (fullImport) { - if (context.useRequireForModules) { - context.addHeader("import." + targetName, - "import " + targetName + " = require(\"" + moduleName + "\");\n"); + // import as footer statements to avoid cyclic dependencies + // as much as possible + // note that the better way to avoid cyclic dependency + // issues is to create bundles + context.addTopFooterStatement("import { " + targetName + " } from '" + moduleName + "';\n"); + + } else { + if (direct) { + context.addHeader("import." + targetName, "import " + targetName + " = " + moduleName + ";\n"); + } else { + + boolean fullImport = require || GLOBALS_CLASS_NAME.equals(targetName); + if (fullImport) { + if (context.useRequireForModules) { + context.addHeader("import." + targetName, + "import " + targetName + " = require(\"" + moduleName + "\");\n"); + } else { + context.addHeader("import." + targetName, + "import * as " + targetName + " from '" + moduleName + "';\n"); + } } else { context.addHeader("import." + targetName, - "import * as " + targetName + " from '" + moduleName + "';\n"); + "import { " + targetName + " } from '" + moduleName + "';\n"); } - } else { - context.addHeader("import." + targetName, - "import { " + targetName + " } from '" + moduleName + "';\n"); } } } @@ -1119,7 +1130,7 @@ public class Java2TypeScriptTranslator extends AbstractTreePrinter { removeLastChars(3); return this; } - + Element typeElement = toTypeElement(typeTree); TypeMirror typeType = toType(typeTree); if (typeElement instanceof TypeParameterElement) { @@ -2798,12 +2809,12 @@ public class Java2TypeScriptTranslator extends AbstractTreePrinter { if (doesMemberNameRequireQuotes(name)) { printIndent(); print("if(").print("this['").print(name).print("']").print("===undefined) "); - print("this['").print(name).print("'] = ") - .print(getAdapter().getVariableInitialValue(varElement)).print(";").println(); + print("this['").print(name).print("'] = ").print(getAdapter().getVariableInitialValue(varElement)) + .print(";").println(); } else { printIndent(); - print("if(").print("this.").print(name).print("===undefined) this.").print(name) - .print(" = ").print(getAdapter().getVariableInitialValue(varElement)).print(";").println(); + print("if(").print("this.").print(name).print("===undefined) this.").print(name).print(" = ") + .print(getAdapter().getVariableInitialValue(varElement)).print(";").println(); } } } @@ -6312,7 +6323,7 @@ public class Java2TypeScriptTranslator extends AbstractTreePrinter { ModuleImportDescriptor moduleImport = getModuleImportDescriptor(name, (TypeElement) type); if (moduleImport != null) { useModule(false, moduleImport.isDirect(), moduleImport.getTargetPackage(), null, - moduleImport.getImportedName(), moduleImport.getPathToImportedClass(), null); + moduleImport.getImportedName(), moduleImport.getPathToImportedClass(), type); } } } diff --git a/transpiler/src/main/java/org/jsweet/transpiler/StaticInitilializerAnalyzer.java b/transpiler/src/main/java/org/jsweet/transpiler/StaticInitilializerAnalyzer.java index 182943cc..23bf678f 100644 --- a/transpiler/src/main/java/org/jsweet/transpiler/StaticInitilializerAnalyzer.java +++ b/transpiler/src/main/java/org/jsweet/transpiler/StaticInitilializerAnalyzer.java @@ -81,21 +81,20 @@ public class StaticInitilializerAnalyzer extends TreePathScanner { */ public StaticInitilializerAnalyzer(JSweetContext context) { this.context = context; + this.context.referenceAnalyzer = this; } private DirectedGraph getGraph() { - if (context.useModules) { - DirectedGraph graph = staticInitializersDependencies - .get(currentCompilationUnit.getPackage()); - if (graph == null) { - graph = new DirectedGraph<>(); - staticInitializersDependencies.put(currentCompilationUnit.getPackage(), graph); - } - return graph; - } else { - return globalStaticInitializersDependencies; - } + return globalStaticInitializersDependencies; } + + public boolean isDependent(CompilationUnitTree cuSource, TypeElement target) { + CompilationUnitTree cuTarget = typesToCompilationUnits.get(target); + if (cuSource != null && cuTarget != null) { + return globalStaticInitializersDependencies.hasEdge(cuTarget, cuSource); + } + return false; + } Set currentTopLevelImportedTypes = new HashSet<>(); diff --git a/transpiler/src/main/java/org/jsweet/transpiler/util/Util.java b/transpiler/src/main/java/org/jsweet/transpiler/util/Util.java index fbc8cd33..52c9edc8 100644 --- a/transpiler/src/main/java/org/jsweet/transpiler/util/Util.java +++ b/transpiler/src/main/java/org/jsweet/transpiler/util/Util.java @@ -2220,6 +2220,10 @@ public class Util { return list.get(list.size() - 1); } + /** + * Returns true if given element is an interface TypeElement.
+ * WARNING: does not include JSweet @Interface + */ public boolean isInterface(Element typeElement) { return typeElement != null && typeElement.getKind() == ElementKind.INTERFACE; }