mirror of
https://github.com/cincheo/jsweet.git
synced 2025-12-15 07:19:22 +00:00
add more code generation tuning
- allow adapting case statements - add an annotation to substitute a Java method body with plain raw TypeScript
This commit is contained in:
parent
6be33221dc
commit
5b1cf57841
46
core-lib/es5/src/main/java/jsweet/lang/TypeScriptBody.java
Normal file
46
core-lib/es5/src/main/java/jsweet/lang/TypeScriptBody.java
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* JSweet - http://www.jsweet.org
|
||||
* Copyright (C) 2015 CINCHEO SAS <renaud.pawlak@cincheo.fr>
|
||||
*
|
||||
* 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 jsweet.lang;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* This annotation allows the programmer to substitute a method body
|
||||
* implementation by a TypeScript implementation.
|
||||
*
|
||||
* <p>
|
||||
* The annotation's value contains TypeScript which is generated as is by the
|
||||
* JSweet transpiler. The code will be checked by the TypeScript transpiler.
|
||||
*
|
||||
* @author Renaud Pawlak
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
@Documented
|
||||
public @interface TypeScriptBody {
|
||||
|
||||
/**
|
||||
* The code that will be generated by the transpiler in place of the
|
||||
* annotated method body.
|
||||
*/
|
||||
java.lang.String value();
|
||||
|
||||
}
|
||||
@ -237,6 +237,7 @@ public abstract class JSweetConfig {
|
||||
public static final String ANNOTATION_ROOT = JSweetConfig.LANG_PACKAGE + ".Root";
|
||||
public static final String ANNOTATION_NAME = JSweetConfig.LANG_PACKAGE + ".Name";
|
||||
public static final String ANNOTATION_DECORATOR = JSweetConfig.LANG_PACKAGE + ".Decorator";
|
||||
public static final String ANNOTATION_TYPE_SCRIPT_BODY = JSweetConfig.LANG_PACKAGE + ".TypeScriptBody";
|
||||
public static final String ANNOTATION_FUNCTIONAL_INTERFACE = FunctionalInterface.class.getName();
|
||||
|
||||
/**
|
||||
|
||||
@ -285,6 +285,10 @@ public class RemoveJavaDependenciesAdapter<C extends JSweetContext> extends Java
|
||||
printMacroName(targetMethodName);
|
||||
print(invocation.args.head).print(".indexOf(").printArgList(invocation.args.tail).print(")");
|
||||
return true;
|
||||
case "sort":
|
||||
printMacroName(targetMethodName);
|
||||
print(invocation.args.head).print(".sort(").printArgList(invocation.args.tail).print(")");
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case "java.util.Arrays":
|
||||
|
||||
@ -1690,28 +1690,52 @@ public class Java2TypeScriptTranslator<C extends JSweetContext> extends Abstract
|
||||
endIndent().println().printIndent().print("}");
|
||||
} else {
|
||||
print(" ").print("{").println().startIndent();
|
||||
enter(methodDecl.getBody());
|
||||
if (!methodDecl.getBody().stats.isEmpty()
|
||||
&& methodDecl.getBody().stats.head.toString().startsWith("super(")) {
|
||||
printBlockStatement(methodDecl.getBody().stats.head);
|
||||
if (parent != null) {
|
||||
printInstanceInitialization(parent, methodDecl.sym);
|
||||
}
|
||||
printBlockStatements(methodDecl.getBody().stats.tail);
|
||||
|
||||
if (context.hasAnnotationType(methodDecl.sym, JSweetConfig.ANNOTATION_TYPE_SCRIPT_BODY)) {
|
||||
String replacedBody = (String) context.getAnnotationValue(methodDecl.sym,
|
||||
JSweetConfig.ANNOTATION_TYPE_SCRIPT_BODY, null);
|
||||
printIndent().print(replacedBody).println();
|
||||
} else {
|
||||
if (parent != null) {
|
||||
printInstanceInitialization(parent, methodDecl.sym);
|
||||
enter(methodDecl.getBody());
|
||||
if (!methodDecl.getBody().stats.isEmpty()
|
||||
&& methodDecl.getBody().stats.head.toString().startsWith("super(")) {
|
||||
printBlockStatement(methodDecl.getBody().stats.head);
|
||||
if (parent != null) {
|
||||
printInstanceInitialization(parent, methodDecl.sym);
|
||||
}
|
||||
printBlockStatements(methodDecl.getBody().stats.tail);
|
||||
} else {
|
||||
if (parent != null) {
|
||||
printInstanceInitialization(parent, methodDecl.sym);
|
||||
}
|
||||
printBlockStatements(methodDecl.getBody().stats);
|
||||
}
|
||||
printBlockStatements(methodDecl.getBody().stats);
|
||||
exit();
|
||||
}
|
||||
endIndent().printIndent().print("}");
|
||||
exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void printInstanceInitialization(JCClassDecl clazz, MethodSymbol method) {
|
||||
protected void printVariableInitialization(JCClassDecl clazz, JCVariableDecl var) {
|
||||
if (getScope().innerClassNotStatic && !Util.isConstantOrNullField(var)) {
|
||||
String name = var.getName().toString();
|
||||
if (context.getFieldNameMapping(var.sym) != null) {
|
||||
name = context.getFieldNameMapping(var.sym);
|
||||
}
|
||||
printIndent().print("this.").print(name).print(" = ").print(var.init).print(";").println();
|
||||
} else if (var.init == null && Util.isCoreType(var.type)) {
|
||||
String name = var.getName().toString();
|
||||
if (context.getFieldNameMapping(var.sym) != null) {
|
||||
name = context.getFieldNameMapping(var.sym);
|
||||
}
|
||||
printIndent().print("this.").print(name).print(" = ").print(Util.getTypeInitialValue(var.type)).print(";")
|
||||
.println();
|
||||
}
|
||||
}
|
||||
|
||||
protected void printInstanceInitialization(JCClassDecl clazz, MethodSymbol method) {
|
||||
if (getContext().options.isInterfaceTracking() && method == null || method.isConstructor()) {
|
||||
getScope().hasDeclaredConstructor = true;
|
||||
if (getScope().innerClassNotStatic) {
|
||||
@ -1721,20 +1745,7 @@ public class Java2TypeScriptTranslator<C extends JSweetContext> extends Abstract
|
||||
for (JCTree member : clazz.defs) {
|
||||
if (member instanceof JCVariableDecl) {
|
||||
JCVariableDecl var = (JCVariableDecl) member;
|
||||
if (getScope().innerClassNotStatic && !Util.isConstantOrNullField(var)) {
|
||||
String name = var.getName().toString();
|
||||
if (context.getFieldNameMapping(var.sym) != null) {
|
||||
name = context.getFieldNameMapping(var.sym);
|
||||
}
|
||||
printIndent().print("this.").print(name).print(" = ").print(var.init).print(";").println();
|
||||
} else if (var.init == null && Util.isCoreType(var.type)) {
|
||||
String name = var.getName().toString();
|
||||
if (context.getFieldNameMapping(var.sym) != null) {
|
||||
name = context.getFieldNameMapping(var.sym);
|
||||
}
|
||||
printIndent().print("this.").print(name).print(" = ").print(Util.getTypeInitialValue(var.type))
|
||||
.print(";").println();
|
||||
}
|
||||
printVariableInitialization(clazz, var);
|
||||
} else if (member instanceof JCBlock) {
|
||||
JCBlock block = (JCBlock) member;
|
||||
if (!block.isStatic()) {
|
||||
@ -1798,37 +1809,43 @@ public class Java2TypeScriptTranslator<C extends JSweetContext> extends Abstract
|
||||
}
|
||||
|
||||
}
|
||||
enter(method.getBody());
|
||||
com.sun.tools.javac.util.List<JCStatement> stats = skipFirst ? method.getBody().stats.tail
|
||||
: method.getBody().stats;
|
||||
if (!stats.isEmpty() && stats.head.toString().startsWith("super(")) {
|
||||
printBlockStatement(stats.head);
|
||||
printFieldInitializations();
|
||||
if (!initialized) {
|
||||
printInstanceInitialization(getParent(JCClassDecl.class), method.sym);
|
||||
}
|
||||
if (!stats.tail.isEmpty()) {
|
||||
printIndent().print("((").print(") => {").startIndent().println();
|
||||
printBlockStatements(stats.tail);
|
||||
endIndent().printIndent().print("})(").print(");").println();
|
||||
}
|
||||
if (context.hasAnnotationType(method.sym, JSweetConfig.ANNOTATION_TYPE_SCRIPT_BODY)) {
|
||||
String replacedBody = (String) context.getAnnotationValue(method.sym,
|
||||
JSweetConfig.ANNOTATION_TYPE_SCRIPT_BODY, null);
|
||||
printIndent().print(replacedBody).println();
|
||||
} else {
|
||||
if (!initialized) {
|
||||
printInstanceInitialization(getParent(JCClassDecl.class), method.sym);
|
||||
}
|
||||
if (!stats.isEmpty() || !method.sym.isConstructor()) {
|
||||
printIndent();
|
||||
}
|
||||
if (!method.sym.isConstructor()) {
|
||||
print("return <any>");
|
||||
}
|
||||
if (!stats.isEmpty() || !method.sym.isConstructor()) {
|
||||
print("((").print(") => {").startIndent().println();
|
||||
printBlockStatements(stats);
|
||||
endIndent().printIndent().print("})(").print(");").println();
|
||||
enter(method.getBody());
|
||||
com.sun.tools.javac.util.List<JCStatement> stats = skipFirst ? method.getBody().stats.tail
|
||||
: method.getBody().stats;
|
||||
if (!stats.isEmpty() && stats.head.toString().startsWith("super(")) {
|
||||
printBlockStatement(stats.head);
|
||||
printFieldInitializations();
|
||||
if (!initialized) {
|
||||
printInstanceInitialization(getParent(JCClassDecl.class), method.sym);
|
||||
}
|
||||
if (!stats.tail.isEmpty()) {
|
||||
printIndent().print("((").print(") => {").startIndent().println();
|
||||
printBlockStatements(stats.tail);
|
||||
endIndent().printIndent().print("})(").print(");").println();
|
||||
}
|
||||
} else {
|
||||
if (!initialized) {
|
||||
printInstanceInitialization(getParent(JCClassDecl.class), method.sym);
|
||||
}
|
||||
if (!stats.isEmpty() || !method.sym.isConstructor()) {
|
||||
printIndent();
|
||||
}
|
||||
if (!method.sym.isConstructor()) {
|
||||
print("return <any>");
|
||||
}
|
||||
if (!stats.isEmpty() || !method.sym.isConstructor()) {
|
||||
print("((").print(") => {").startIndent().println();
|
||||
printBlockStatements(stats);
|
||||
endIndent().printIndent().print("})(").print(");").println();
|
||||
}
|
||||
}
|
||||
exit();
|
||||
}
|
||||
exit();
|
||||
} else {
|
||||
String returnValue = Util.getTypeInitialValue(method.sym.getReturnType());
|
||||
if (returnValue != null) {
|
||||
@ -3431,18 +3448,23 @@ public class Java2TypeScriptTranslator<C extends JSweetContext> extends Abstract
|
||||
printIndent().print("}");
|
||||
}
|
||||
|
||||
protected void printCaseStatementPattern(JCExpression pattern) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitCase(JCCase caseStatement) {
|
||||
if (caseStatement.pat != null) {
|
||||
print("case ");
|
||||
if (caseStatement.pat.type.isPrimitive()
|
||||
|| String.class.getName().equals(caseStatement.pat.type.toString())) {
|
||||
print(caseStatement.pat);
|
||||
} else {
|
||||
if (context.useModules) {
|
||||
print(caseStatement.pat.type.tsym.getSimpleName() + "." + caseStatement.pat);
|
||||
if (!getAdapter().substituteCaseStatementPattern(caseStatement, caseStatement.pat)) {
|
||||
if (caseStatement.pat.type.isPrimitive()
|
||||
|| String.class.getName().equals(caseStatement.pat.type.toString())) {
|
||||
print(caseStatement.pat);
|
||||
} else {
|
||||
print(getRootRelativeName(caseStatement.pat.type.tsym) + "." + caseStatement.pat);
|
||||
if (context.useModules) {
|
||||
print(caseStatement.pat.type.tsym.getSimpleName() + "." + caseStatement.pat);
|
||||
} else {
|
||||
print(getRootRelativeName(caseStatement.pat.type.tsym) + "." + caseStatement.pat);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@ -36,6 +36,7 @@ import com.sun.tools.javac.tree.JCTree;
|
||||
import com.sun.tools.javac.tree.JCTree.JCArrayAccess;
|
||||
import com.sun.tools.javac.tree.JCTree.JCArrayTypeTree;
|
||||
import com.sun.tools.javac.tree.JCTree.JCAssign;
|
||||
import com.sun.tools.javac.tree.JCTree.JCCase;
|
||||
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
|
||||
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
|
||||
import com.sun.tools.javac.tree.JCTree.JCEnhancedForLoop;
|
||||
@ -487,4 +488,12 @@ public abstract class AbstractPrinterAdapter<C extends JSweetContext> {
|
||||
public boolean substituteInstanceof(String exprStr, JCTree expr, Type type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Substitutes if necessary the pattern of a case statement.
|
||||
*/
|
||||
public boolean substituteCaseStatementPattern(JCCase caseStatement, JCExpression pattern) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -21,8 +21,11 @@ import static org.jsweet.transpiler.JSweetProblem.GLOBAL_INDEXER_GET;
|
||||
import static org.jsweet.transpiler.JSweetProblem.GLOBAL_INDEXER_SET;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.jsweet.transpiler.JSweetContext;
|
||||
import org.jsweet.transpiler.JSweetFactory;
|
||||
import org.jsweet.transpiler.JSweetProblem;
|
||||
import org.jsweet.transpiler.ModuleKind;
|
||||
import org.jsweet.transpiler.typescript.Java2TypeScriptAdapter;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
@ -53,6 +56,7 @@ import source.structural.NoWildcardsInImports;
|
||||
import source.structural.ObjectTypes;
|
||||
import source.structural.StaticMembersInInterfaces;
|
||||
import source.structural.TwoClassesInSameFile;
|
||||
import source.structural.TypeScriptBodyAnnotation;
|
||||
import source.structural.WrongConstructsInInterfaces;
|
||||
import source.structural.WrongThisAccessOnStatic;
|
||||
import source.structural.globalclasses.Globals;
|
||||
@ -81,7 +85,8 @@ public class StructuralTests extends AbstractTest {
|
||||
@Test
|
||||
public void testVariableMethodNameClashes() {
|
||||
transpile(logHandler -> {
|
||||
logHandler.assertReportedProblems(JSweetProblem.HIDDEN_INVOCATION, JSweetProblem.HIDDEN_INVOCATION, JSweetProblem.HIDDEN_INVOCATION);
|
||||
logHandler.assertReportedProblems(JSweetProblem.HIDDEN_INVOCATION, JSweetProblem.HIDDEN_INVOCATION,
|
||||
JSweetProblem.HIDDEN_INVOCATION);
|
||||
}, getSourceFile(NameClashesWithMethodInvocations.class));
|
||||
}
|
||||
|
||||
@ -188,7 +193,7 @@ public class StructuralTests extends AbstractTest {
|
||||
assertEquals("There should be no errors", 0, logHandler.reportedProblems.size());
|
||||
}, getSourceFile(InterfaceInheritance.class));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testInstanceofForInterfaces() {
|
||||
eval((logHandler, r) -> {
|
||||
@ -232,7 +237,8 @@ public class StructuralTests extends AbstractTest {
|
||||
// Assert.assertEquals("invoked1_2", r.get("Static"));
|
||||
Assert.assertEquals("invoked1_2", r.get("Ok"));
|
||||
Assert.assertEquals("invoked1_2", r.get("test2"));
|
||||
}, getSourceFile(Globals.class), getSourceFile(source.structural.globalclasses.e.Globals.class), getSourceFile(GlobalFunctionAccessFromMain.class));
|
||||
}, getSourceFile(Globals.class), getSourceFile(source.structural.globalclasses.e.Globals.class),
|
||||
getSourceFile(GlobalFunctionAccessFromMain.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -257,7 +263,8 @@ public class StructuralTests extends AbstractTest {
|
||||
Assert.assertEquals("A method was not executed as expected", true, r.get("m2"));
|
||||
Assert.assertEquals("A method was not executed as expected", true, r.get("sm1"));
|
||||
Assert.assertEquals("A method was not executed as expected", true, r.get("sm2"));
|
||||
}, getSourceFile(AutoImportClassesInSamePackageUsed.class), getSourceFile(AutoImportClassesInSamePackage.class));
|
||||
}, getSourceFile(AutoImportClassesInSamePackageUsed.class),
|
||||
getSourceFile(AutoImportClassesInSamePackage.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -271,8 +278,9 @@ public class StructuralTests extends AbstractTest {
|
||||
@Test
|
||||
public void testWrongGlobals() {
|
||||
transpile(logHandler -> {
|
||||
logHandler.assertReportedProblems(JSweetProblem.GLOBALS_CLASS_CANNOT_HAVE_SUPERCLASS, JSweetProblem.GLOBAL_CONSTRUCTOR_DEF,
|
||||
JSweetProblem.GLOBALS_CAN_ONLY_HAVE_STATIC_MEMBERS, JSweetProblem.GLOBALS_CAN_ONLY_HAVE_STATIC_MEMBERS);
|
||||
logHandler.assertReportedProblems(JSweetProblem.GLOBALS_CLASS_CANNOT_HAVE_SUPERCLASS,
|
||||
JSweetProblem.GLOBAL_CONSTRUCTOR_DEF, JSweetProblem.GLOBALS_CAN_ONLY_HAVE_STATIC_MEMBERS,
|
||||
JSweetProblem.GLOBALS_CAN_ONLY_HAVE_STATIC_MEMBERS);
|
||||
}, getSourceFile(source.structural.wrongglobals.Globals.class));
|
||||
}
|
||||
|
||||
@ -286,7 +294,8 @@ public class StructuralTests extends AbstractTest {
|
||||
@Test
|
||||
public void testWrongThisAccessOnStatic() {
|
||||
transpile(ModuleKind.none, logHandler -> {
|
||||
logHandler.assertReportedProblems(JSweetProblem.CANNOT_ACCESS_STATIC_MEMBER_ON_THIS, JSweetProblem.CANNOT_ACCESS_STATIC_MEMBER_ON_THIS);
|
||||
logHandler.assertReportedProblems(JSweetProblem.CANNOT_ACCESS_STATIC_MEMBER_ON_THIS,
|
||||
JSweetProblem.CANNOT_ACCESS_STATIC_MEMBER_ON_THIS);
|
||||
}, getSourceFile(WrongThisAccessOnStatic.class));
|
||||
}
|
||||
|
||||
@ -304,13 +313,36 @@ public class StructuralTests extends AbstractTest {
|
||||
}, getSourceFile(JSNI.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTypeScriptBody() {
|
||||
createTranspiler(new JSweetFactory<JSweetContext>() {
|
||||
@Override
|
||||
public Java2TypeScriptAdapter<JSweetContext> createAdapter(JSweetContext context) {
|
||||
return new Java2TypeScriptAdapter<JSweetContext>(context) {
|
||||
{
|
||||
context.addAnnotation("@TypeScriptBody('return (this.i + 2)')",
|
||||
"source.structural.TypeScriptBodyAnnotation.m2()");
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
eval(ModuleKind.none, (logHandler, r) -> {
|
||||
logHandler.assertReportedProblems();
|
||||
assertEquals(2, (int) r.get("test1"));
|
||||
assertEquals(3, (int) r.get("test2"));
|
||||
assertEquals(1, (int) r.get("test3"));
|
||||
}, getSourceFile(TypeScriptBodyAnnotation.class));
|
||||
createTranspiler(new JSweetFactory<JSweetContext>());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultMethods() {
|
||||
// TODO: make it work with modules
|
||||
eval(ModuleKind.none, (logHandler, r) -> {
|
||||
logHandler.assertReportedProblems();
|
||||
assertEquals("m,m1,m2-overriden", r.get("trace"));
|
||||
}, getSourceFile(ClassWithStaticMethod.class), getSourceFile(DefaultMethods.class), getSourceFile(DefaultMethodsConsumer.class));
|
||||
}, getSourceFile(ClassWithStaticMethod.class), getSourceFile(DefaultMethods.class),
|
||||
getSourceFile(DefaultMethodsConsumer.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@ -174,6 +174,12 @@ public class Collections implements Cloneable, Serializable {
|
||||
|
||||
trace.push("" + (java.util.Arrays.deepEquals(points1, points2)));
|
||||
|
||||
List<String> l3 = Arrays.asList(a3);
|
||||
|
||||
java.util.Collections.sort(l3);
|
||||
|
||||
java.util.Collections.sort(l3, c);
|
||||
|
||||
$export("trace", trace.join(","));
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,30 @@
|
||||
package source.structural;
|
||||
|
||||
import static jsweet.util.Globals.$export;
|
||||
|
||||
import jsweet.lang.TypeScriptBody;
|
||||
|
||||
public class TypeScriptBodyAnnotation {
|
||||
|
||||
public static void main(String[] args) {
|
||||
$export("test1", new TypeScriptBodyAnnotation().m1());
|
||||
$export("test2", new TypeScriptBodyAnnotation().m2());
|
||||
$export("test3", new TypeScriptBodyAnnotation().m3());
|
||||
}
|
||||
|
||||
int i = 1;
|
||||
|
||||
@TypeScriptBody("return this.i + 1;")
|
||||
public int m1() {
|
||||
return i;
|
||||
}
|
||||
|
||||
public int m2() {
|
||||
return i;
|
||||
}
|
||||
|
||||
public int m3() {
|
||||
return i;
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user