mirror of
https://github.com/cincheo/jsweet.git
synced 2025-12-15 07:19:22 +00:00
add an option to propagate async/await contructs automatically
This commit is contained in:
parent
bbb1bd1ddf
commit
80721ed55d
@ -624,6 +624,14 @@ public class JSweetCommandLineLauncher {
|
||||
"PrinterAdapter#getClassMemberComparator(), to be overloaded by the user to "+
|
||||
"implement the desired order.");
|
||||
jsap.registerParameter(switchArg);
|
||||
|
||||
// Auto propagate async methods and await invocations
|
||||
switchArg = new Switch(JSweetOptions.autoPropagateAsyncAwaits);
|
||||
switchArg.setLongFlag(JSweetOptions.autoPropagateAsyncAwaits);
|
||||
switchArg.setHelp(
|
||||
"Propagate automatically the async modifier when a method invokes an async method "+
|
||||
"and automatically adds await keywords when invoking async methods.");
|
||||
jsap.registerParameter(switchArg);
|
||||
|
||||
return jsap;
|
||||
}
|
||||
@ -860,6 +868,9 @@ public class JSweetCommandLineLauncher {
|
||||
if (jsapArgs.userSpecified(JSweetOptions.sortClassMembers)) {
|
||||
transpiler.setSortClassMembers(jsapArgs.getBoolean(JSweetOptions.sortClassMembers));
|
||||
}
|
||||
if (jsapArgs.userSpecified(JSweetOptions.autoPropagateAsyncAwaits)) {
|
||||
transpiler.setAutoPropagateAsyncAwaits(jsapArgs.getBoolean(JSweetOptions.autoPropagateAsyncAwaits));
|
||||
}
|
||||
|
||||
if (tsOutputDir != null) {
|
||||
transpiler.setTsOutputDir(tsOutputDir);
|
||||
|
||||
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* JSweet transpiler - http://www.jsweet.org
|
||||
* Copyright (C) 2015 CINCHEO SAS <renaud.pawlak@cincheo.fr>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
package org.jsweet.transpiler;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.jsweet.JSweetConfig;
|
||||
import org.jsweet.transpiler.util.AbstractTreeScanner;
|
||||
|
||||
import com.sun.tools.javac.code.Symbol.MethodSymbol;
|
||||
import com.sun.tools.javac.tree.JCTree;
|
||||
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
|
||||
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
|
||||
import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
|
||||
|
||||
/**
|
||||
* This AST scanner performs propagates async methods automatically.
|
||||
*
|
||||
* @author Renaud Pawlak
|
||||
*/
|
||||
public class AsyncAwaitPropagationScanner extends AbstractTreeScanner {
|
||||
|
||||
boolean stillWorking = false;
|
||||
|
||||
/**
|
||||
* Creates a new global scanner.
|
||||
*/
|
||||
public AsyncAwaitPropagationScanner(JSweetContext context) {
|
||||
super(null, context, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTopLevel(JCCompilationUnit topLevel) {
|
||||
if (topLevel.packge.getQualifiedName().toString().startsWith(JSweetConfig.LIBS_PACKAGE + ".")) {
|
||||
return;
|
||||
}
|
||||
this.compilationUnit = topLevel;
|
||||
super.visitTopLevel(topLevel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitApply(JCMethodInvocation invocation) {
|
||||
try {
|
||||
MethodSymbol method = (MethodSymbol) invocation.meth.getClass().getField("sym").get(invocation.meth);
|
||||
if (context.hasAnnotationType(method, JSweetConfig.ANNOTATION_ASYNC)
|
||||
&& !"void".equals(method.getReturnType().toString())) {
|
||||
JCMethodDecl parent = getParent(JCMethodDecl.class);
|
||||
if (!context.hasAnnotationType(parent.sym, JSweetConfig.ANNOTATION_ASYNC)) {
|
||||
context.addExtraAnnotationType(parent.sym, JSweetConfig.ANNOTATION_ASYNC);
|
||||
stillWorking = true;
|
||||
}
|
||||
JCTree directParent = getParent();
|
||||
if (!(context.isAwaitInvocation(invocation) || (directParent instanceof JCMethodInvocation
|
||||
&& (((JCMethodInvocation) directParent).meth.toString().equals("await")
|
||||
|| ((JCMethodInvocation) directParent).meth.toString().endsWith(".await"))))) {
|
||||
context.addAwaitInvocation(invocation);
|
||||
stillWorking = true;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
super.visitApply(invocation);
|
||||
}
|
||||
|
||||
public void process(List<JCCompilationUnit> compilationUnits) {
|
||||
do {
|
||||
stillWorking = false;
|
||||
for (JCCompilationUnit compilationUnit : compilationUnits) {
|
||||
scan(compilationUnit);
|
||||
}
|
||||
} while (stillWorking);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1316,9 +1316,40 @@ public class JSweetContext extends Context {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (extraAnnotations != null) {
|
||||
for (String annotationType : annotationTypes) {
|
||||
Set<Symbol> symbols = extraAnnotations.get(annotationType);
|
||||
if (symbols != null && symbols.contains(symbol)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hasActualAnnotationType(symbol, annotationTypes);
|
||||
}
|
||||
|
||||
private Map<String, Set<Symbol>> extraAnnotations;
|
||||
|
||||
/**
|
||||
* Adds an extra annotation type to a symbol (no args).
|
||||
* See {@link #hasAnnotationType(Symbol, String...)}.
|
||||
*
|
||||
* @param symbol the symbol to add the annotation to
|
||||
* @param annotationTypeName the annotation type name
|
||||
*/
|
||||
public void addExtraAnnotationType(Symbol symbol, String annotationTypeName) {
|
||||
if (extraAnnotations == null) {
|
||||
extraAnnotations = new HashMap<String, Set<Symbol>>();
|
||||
}
|
||||
Set<Symbol> symbols = extraAnnotations.get(annotationTypeName);
|
||||
if (symbols == null) {
|
||||
symbols = new HashSet<>();
|
||||
extraAnnotations.put(annotationTypeName, symbols);
|
||||
}
|
||||
symbols.add(symbol);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the actual name of a symbol from a JSweet convention, so including
|
||||
* potential <code>jsweet.lang.Name</code> annotation.
|
||||
@ -1961,4 +1992,21 @@ public class JSweetContext extends Context {
|
||||
}
|
||||
}
|
||||
|
||||
private Set<JCMethodInvocation> awaitInvocations;
|
||||
|
||||
public void addAwaitInvocation(JCMethodInvocation invocation) {
|
||||
if (this.awaitInvocations == null) {
|
||||
this.awaitInvocations = new HashSet<>();
|
||||
}
|
||||
this.awaitInvocations.add(invocation);
|
||||
}
|
||||
|
||||
public boolean isAwaitInvocation(JCMethodInvocation invocation) {
|
||||
if (this.awaitInvocations != null) {
|
||||
return this.awaitInvocations.contains(invocation);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -143,6 +143,11 @@ public interface JSweetOptions {
|
||||
* Constant string for the 'sortClassMembers' option.
|
||||
*/
|
||||
String sortClassMembers = "sortClassMembers";
|
||||
|
||||
/**
|
||||
* Constant string for the 'autoPropagateAsyncs' option.
|
||||
*/
|
||||
String autoPropagateAsyncAwaits = "autoPropagateAsyncAwaits";
|
||||
|
||||
/**
|
||||
* All the supported options (used to report non-blocking errors when options do not exist).
|
||||
@ -150,7 +155,8 @@ public interface JSweetOptions {
|
||||
String[] options = { bundle, noRootDirectories, sourceMap, module, encoding, outEncoding, enableAssertions,
|
||||
declaration, tsOnly, ignoreDefinitions, ignoreJavaErrors, header, disableSinglePrecisionFloats,
|
||||
disableStaticsLazyInitialization, targetVersion, tsout, dtsout, jsout, candiesJsOut, moduleResolution,
|
||||
extraSystemPath, useSingleQuotesForStringLiterals, nonEnumerableTransients, classpath, sortClassMembers };
|
||||
extraSystemPath, useSingleQuotesForStringLiterals, nonEnumerableTransients, classpath, sortClassMembers,
|
||||
autoPropagateAsyncAwaits };
|
||||
|
||||
/**
|
||||
* Returns the configuration from the configuration file.
|
||||
@ -375,4 +381,10 @@ public interface JSweetOptions {
|
||||
* {@link PrinterAdapter#getClassMemberComparator()}.
|
||||
*/
|
||||
boolean isSortClassMembers();
|
||||
|
||||
/**
|
||||
* If true, auto propagates async methods and await invocations.
|
||||
*/
|
||||
boolean isAutoPropagateAsyncAwaits();
|
||||
|
||||
}
|
||||
@ -243,7 +243,8 @@ public class JSweetTranspiler implements JSweetOptions {
|
||||
private boolean useSingleQuotesForStringLiterals = false;
|
||||
private boolean nonEnumerableTransients = false;
|
||||
private boolean sortClassMembers = false;
|
||||
|
||||
private boolean autoPropagateAsyncAwaits = false;
|
||||
|
||||
private ArrayList<String> adapters = new ArrayList<>();
|
||||
private File configurationFile;
|
||||
|
||||
@ -998,6 +999,10 @@ public class JSweetTranspiler implements JSweetOptions {
|
||||
context.dumpOverloads(System.out);
|
||||
}
|
||||
|
||||
if (isAutoPropagateAsyncAwaits()) {
|
||||
new AsyncAwaitPropagationScanner(context).process(compilationUnits);
|
||||
}
|
||||
|
||||
adapter.onTranspilationStarted();
|
||||
|
||||
String[] headerLines = getHeaderLines();
|
||||
@ -1125,9 +1130,14 @@ public class JSweetTranspiler implements JSweetOptions {
|
||||
}
|
||||
|
||||
new OverloadScanner(transpilationHandler, context).process(orderedCompilationUnits);
|
||||
|
||||
context.constAnalyzer = new ConstAnalyzer();
|
||||
context.constAnalyzer.process(orderedCompilationUnits);
|
||||
|
||||
if (isAutoPropagateAsyncAwaits()) {
|
||||
new AsyncAwaitPropagationScanner(context).process(compilationUnits);
|
||||
}
|
||||
|
||||
adapter.onTranspilationStarted();
|
||||
|
||||
logger.debug("ordered compilation units: " + orderedCompilationUnits.stream().map(cu -> {
|
||||
@ -1959,4 +1969,13 @@ public class JSweetTranspiler implements JSweetOptions {
|
||||
this.sortClassMembers = sortClassMembers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAutoPropagateAsyncAwaits() {
|
||||
return this.autoPropagateAsyncAwaits;
|
||||
}
|
||||
|
||||
public void setAutoPropagateAsyncAwaits(boolean autoPropagateAsyncAwaits) {
|
||||
this.autoPropagateAsyncAwaits = autoPropagateAsyncAwaits;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -924,7 +924,7 @@ public class Java2TypeScriptTranslator extends AbstractTreePrinter {
|
||||
|
||||
@Override
|
||||
public void visitApply(JCMethodInvocation invocation) {
|
||||
// TODO: same for static variables
|
||||
// TODO: same for static variables
|
||||
if (invocation.meth instanceof JCIdent
|
||||
&& JSweetConfig.TS_STRICT_MODE_KEYWORDS.contains(invocation.meth.toString().toLowerCase())) {
|
||||
PackageSymbol invocationPackage = (PackageSymbol) ((JCIdent) invocation.meth).sym
|
||||
@ -2683,13 +2683,13 @@ public class Java2TypeScriptTranslator extends AbstractTreePrinter {
|
||||
boolean promisify = isAsyncMethod(methodDecl)
|
||||
&& !methodDecl.restype.type.tsym.getQualifiedName().toString().endsWith(".Promise");
|
||||
if (promisify) {
|
||||
print(" Promise< ");
|
||||
print("Promise<");
|
||||
}
|
||||
|
||||
substituteAndPrintType(methodDecl.restype);
|
||||
|
||||
if (promisify) {
|
||||
print(" > ");
|
||||
print(">");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3841,7 +3841,6 @@ public class Java2TypeScriptTranslator extends AbstractTreePrinter {
|
||||
*/
|
||||
@Override
|
||||
public void visitApply(JCMethodInvocation inv) {
|
||||
|
||||
boolean debugMode = false;
|
||||
if (context.options.isDebugMode()) {
|
||||
if (Util.getAccessedSymbol(inv.meth) instanceof MethodSymbol) {
|
||||
@ -3877,6 +3876,10 @@ public class Java2TypeScriptTranslator extends AbstractTreePrinter {
|
||||
applyVarargs = false;
|
||||
}
|
||||
|
||||
if (context.isAwaitInvocation(inv)) {
|
||||
print("await ");
|
||||
}
|
||||
|
||||
boolean anonymous = isAnonymousMethod(methName);
|
||||
boolean targetIsThisOrStaticImported = /*
|
||||
* !"super".equals(methName) &&
|
||||
|
||||
@ -39,6 +39,7 @@ import org.junit.Test;
|
||||
|
||||
import source.extension.ToBeSorted;
|
||||
import source.syntax.AnnotationQualifiedNames;
|
||||
import source.syntax.AsyncAwaitPropagation;
|
||||
import source.syntax.Casts;
|
||||
import source.syntax.DocComments;
|
||||
import source.syntax.DynamicInvoke;
|
||||
@ -312,4 +313,13 @@ public class SyntaxTests extends AbstractTest {
|
||||
transpile(TestTranspilationHandler::assertNoProblems, getSourceFile(MemberReferences.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsyncAwaitPropagation() {
|
||||
transpilerTest().getTranspiler().setAutoPropagateAsyncAwaits(true);
|
||||
transpile(ModuleKind.none, logHandler -> {
|
||||
logHandler.assertNoProblems();
|
||||
}, getSourceFile(AsyncAwaitPropagation.class));
|
||||
transpilerTest().getTranspiler().setAutoPropagateAsyncAwaits(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,27 @@
|
||||
package source.syntax;
|
||||
|
||||
import jsweet.lang.Async;
|
||||
|
||||
public class AsyncAwaitPropagation {
|
||||
|
||||
@Async int m1() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int m2() {
|
||||
return 1 + m1();
|
||||
}
|
||||
|
||||
void m3() {
|
||||
if (m1() == 1) {
|
||||
// do something
|
||||
} else {
|
||||
// do something else
|
||||
}
|
||||
}
|
||||
|
||||
int m4() {
|
||||
return m2();
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user