Java tutorial
/* * 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 static org.apache.commons.lang3.StringUtils.isBlank; import static org.jsweet.JSweetConfig.ANNOTATION_ERASED; import static org.jsweet.JSweetConfig.ANNOTATION_FUNCTIONAL_INTERFACE; import static org.jsweet.JSweetConfig.ANNOTATION_OBJECT_TYPE; import static org.jsweet.JSweetConfig.ANNOTATION_STRING_TYPE; import static org.jsweet.JSweetConfig.GLOBALS_CLASS_NAME; import static org.jsweet.JSweetConfig.GLOBALS_PACKAGE_NAME; import static org.jsweet.JSweetConfig.TS_IDENTIFIER_FORBIDDEN_CHARS; import static org.jsweet.JSweetConfig.TUPLE_CLASSES_PACKAGE; import static org.jsweet.JSweetConfig.UNION_CLASS_NAME; import static org.jsweet.JSweetConfig.UTIL_PACKAGE; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.Stack; import java.util.function.BiFunction; import java.util.stream.Collectors; import javax.lang.model.element.Modifier; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import org.apache.commons.lang3.StringEscapeUtils; import org.apache.commons.lang3.StringUtils; import org.apache.log4j.Logger; import org.jsweet.JSweetConfig; import org.jsweet.transpiler.OverloadScanner.Overload; import org.jsweet.transpiler.extension.PrinterAdapter; import org.jsweet.transpiler.model.ExtendedElement; import org.jsweet.transpiler.model.ExtendedElementFactory; import org.jsweet.transpiler.model.support.ArrayAccessElementSupport; import org.jsweet.transpiler.model.support.AssignmentElementSupport; import org.jsweet.transpiler.model.support.CaseElementSupport; import org.jsweet.transpiler.model.support.ExtendedElementSupport; import org.jsweet.transpiler.model.support.ForeachLoopElementSupport; import org.jsweet.transpiler.model.support.ImportElementSupport; import org.jsweet.transpiler.model.support.MethodInvocationElementSupport; import org.jsweet.transpiler.model.support.NewClassElementSupport; import org.jsweet.transpiler.util.AbstractTreePrinter; import org.jsweet.transpiler.util.Util; import com.sun.source.tree.Tree.Kind; import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Symbol.ClassSymbol; import com.sun.tools.javac.code.Symbol.MethodSymbol; import com.sun.tools.javac.code.Symbol.PackageSymbol; import com.sun.tools.javac.code.Symbol.TypeSymbol; import com.sun.tools.javac.code.Symbol.TypeVariableSymbol; import com.sun.tools.javac.code.Symbol.VarSymbol; import com.sun.tools.javac.code.Type; import com.sun.tools.javac.code.Type.ArrayType; import com.sun.tools.javac.code.Type.MethodType; import com.sun.tools.javac.code.TypeTag; import com.sun.tools.javac.parser.Tokens.Comment; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCAnnotation; import com.sun.tools.javac.tree.JCTree.JCArrayAccess; import com.sun.tools.javac.tree.JCTree.JCArrayTypeTree; import com.sun.tools.javac.tree.JCTree.JCAssert; import com.sun.tools.javac.tree.JCTree.JCAssign; import com.sun.tools.javac.tree.JCTree.JCAssignOp; import com.sun.tools.javac.tree.JCTree.JCBinary; import com.sun.tools.javac.tree.JCTree.JCBlock; import com.sun.tools.javac.tree.JCTree.JCBreak; import com.sun.tools.javac.tree.JCTree.JCCase; import com.sun.tools.javac.tree.JCTree.JCCatch; import com.sun.tools.javac.tree.JCTree.JCClassDecl; import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; import com.sun.tools.javac.tree.JCTree.JCConditional; import com.sun.tools.javac.tree.JCTree.JCContinue; import com.sun.tools.javac.tree.JCTree.JCDoWhileLoop; import com.sun.tools.javac.tree.JCTree.JCEnhancedForLoop; import com.sun.tools.javac.tree.JCTree.JCExpression; import com.sun.tools.javac.tree.JCTree.JCExpressionStatement; import com.sun.tools.javac.tree.JCTree.JCFieldAccess; import com.sun.tools.javac.tree.JCTree.JCForLoop; import com.sun.tools.javac.tree.JCTree.JCIdent; import com.sun.tools.javac.tree.JCTree.JCIf; import com.sun.tools.javac.tree.JCTree.JCImport; import com.sun.tools.javac.tree.JCTree.JCInstanceOf; import com.sun.tools.javac.tree.JCTree.JCLabeledStatement; import com.sun.tools.javac.tree.JCTree.JCLambda; import com.sun.tools.javac.tree.JCTree.JCLiteral; import com.sun.tools.javac.tree.JCTree.JCMemberReference; import com.sun.tools.javac.tree.JCTree.JCMethodDecl; import com.sun.tools.javac.tree.JCTree.JCMethodInvocation; import com.sun.tools.javac.tree.JCTree.JCNewArray; import com.sun.tools.javac.tree.JCTree.JCNewClass; import com.sun.tools.javac.tree.JCTree.JCParens; import com.sun.tools.javac.tree.JCTree.JCPrimitiveTypeTree; import com.sun.tools.javac.tree.JCTree.JCReturn; import com.sun.tools.javac.tree.JCTree.JCStatement; import com.sun.tools.javac.tree.JCTree.JCSwitch; import com.sun.tools.javac.tree.JCTree.JCSynchronized; import com.sun.tools.javac.tree.JCTree.JCThrow; import com.sun.tools.javac.tree.JCTree.JCTry; import com.sun.tools.javac.tree.JCTree.JCTypeApply; import com.sun.tools.javac.tree.JCTree.JCTypeCast; import com.sun.tools.javac.tree.JCTree.JCTypeParameter; import com.sun.tools.javac.tree.JCTree.JCUnary; import com.sun.tools.javac.tree.JCTree.JCVariableDecl; import com.sun.tools.javac.tree.JCTree.JCWhileLoop; import com.sun.tools.javac.tree.JCTree.JCWildcard; import com.sun.tools.javac.tree.TreeScanner; import com.sun.tools.javac.util.Name; /** * This is a TypeScript printer for translating the Java AST to a TypeScript * program. * * @author Renaud Pawlak */ public class Java2TypeScriptTranslator extends AbstractTreePrinter { public static final String PARENT_CLASS_FIELD_NAME = "__parent"; public static final String INTERFACES_FIELD_NAME = "__interfaces"; public static final String STATIC_INITIALIZATION_SUFFIX = "_$LI$"; public static final String CLASS_NAME_IN_CONSTRUCTOR = "__class"; public static final String ANONYMOUS_PREFIX = "$"; public static final String ENUM_WRAPPER_CLASS_SUFFIX = "_$WRAPPER"; public static final String ENUM_WRAPPER_CLASS_WRAPPERS = "_$wrappers"; public static final String ENUM_WRAPPER_CLASS_NAME = "_$name"; public static final String ENUM_WRAPPER_CLASS_ORDINAL = "_$ordinal"; public static final String VAR_DECL_KEYWORD = "let"; public static final String BODY_MARKER = "#BODY#"; public static final String BASE_INDENT_MARKER = "#BASEINDENT#"; public static final String INDENT_MARKER = "#INDENT#"; public static final String METHOD_NAME_MARKER = "#METHODNAME#"; public static final String CLASS_NAME_MARKER = "#CLASSNAME#"; public static final String GENERATOR_PREFIX = "__generator_"; protected static Logger logger = Logger.getLogger(Java2TypeScriptTranslator.class); private static class ClassScope { private String name; private boolean interfaceScope = false; private boolean enumScope = false; private boolean isComplexEnum = false; private boolean enumWrapperClassScope = false; private boolean removedSuperclass = false; private boolean declareClassScope; private boolean skipTypeAnnotations = false; private boolean defaultMethodScope = false; private boolean eraseVariableTypes = false; private boolean hasDeclaredConstructor = false; private boolean innerClass = false; private boolean innerClassNotStatic = false; private boolean hasInnerClass = false; private List<JCClassDecl> anonymousClasses = new ArrayList<>(); private List<JCNewClass> anonymousClassesConstructors = new ArrayList<>(); private List<LinkedHashSet<VarSymbol>> finalVariables = new ArrayList<>(); private boolean hasConstructorOverloadWithSuperClass; private List<JCVariableDecl> fieldsWithInitializers = new ArrayList<>(); private List<String> inlinedConstructorArgs = null; private List<JCClassDecl> localClasses = new ArrayList<>(); // to be accessed in the parent scope private boolean isAnonymousClass = false; // to be accessed in the parent scope private boolean isInnerClass = false; // to be accessed in the parent scope private boolean isLocalClass = false; } private Stack<ClassScope> scope = new Stack<>(); private boolean isAnnotationScope = false; private boolean isDefinitionScope = false; private boolean isTopLevelScope() { return getIndent() == 0; } private ClassScope getScope() { return scope.peek(); } private ClassScope getScope(int i) { return scope.get(scope.size() - 1 - i); } public void enterScope() { scope.push(new ClassScope()); } public void exitScope() { scope.pop(); } /** * Creates a new TypeScript translator. * * @param adapter * an object that can tune various aspects of the TypeScript code * generation * @param logHandler * the handler for logging and error reporting * @param context * the AST scanning context * @param compilationUnit * the compilation unit to be translated * @param fillSourceMap * if true, the printer generates the source maps, for debugging * purpose */ public Java2TypeScriptTranslator(PrinterAdapter adapter, TranspilationHandler logHandler, JSweetContext context, JCCompilationUnit compilationUnit, boolean fillSourceMap) { super(logHandler, context, compilationUnit, adapter, fillSourceMap); } private static java.util.List<Class<?>> statementsWithNoSemis = Arrays .asList(new Class<?>[] { JCIf.class, JCForLoop.class, JCEnhancedForLoop.class, JCSwitch.class }); private static String mapConstructorType(String typeName) { if (CONSTRUCTOR_TYPE_MAPPING.containsKey(typeName)) { return CONSTRUCTOR_TYPE_MAPPING.get(typeName); } else { return typeName; } } public static final Map<String, String> TYPE_MAPPING; static { Map<String, String> mapping = new HashMap<>(); mapping.put("java.lang.String", "String"); mapping.put("java.lang.Number", "Number"); mapping.put("java.lang.Integer", "Number"); mapping.put("java.lang.Float", "Number"); mapping.put("java.lang.Double", "Number"); mapping.put("java.lang.Short", "Number"); mapping.put("java.lang.Character", "String"); mapping.put("java.lang.Byte", "Number"); mapping.put("java.lang.Boolean", "Boolean"); mapping.put("java.lang.Long", "Number"); mapping.put("int", "Number"); mapping.put("float", "Number"); mapping.put("double", "Number"); mapping.put("short", "Number"); mapping.put("char", "String"); mapping.put("boolean", "Boolean"); mapping.put("byte", "Number"); mapping.put("long", "Number"); TYPE_MAPPING = Collections.unmodifiableMap(mapping); } private static final Map<String, String> CONSTRUCTOR_TYPE_MAPPING; static { Map<String, String> mapping = new HashMap<>(); mapping.put("string", "String"); mapping.put("number", "Number"); mapping.put("boolean", "Boolean"); mapping.put("any", "Object"); CONSTRUCTOR_TYPE_MAPPING = Collections.unmodifiableMap(mapping); } private JCMethodDecl mainMethod; private PackageSymbol topLevelPackage; private void useModule(boolean require, PackageSymbol targetPackage, JCTree sourceTree, String targetName, String moduleName, Symbol sourceElement) { if (context.useModules) { context.packageDependencies.add(targetPackage); context.packageDependencies.add(compilationUnit.packge); context.packageDependencies.addEdge(compilationUnit.packge, targetPackage); } context.registerUsedModule(moduleName); Set<String> importedNames = context.getImportedNames(compilationUnit.getSourceFile().getName()); if (!importedNames.contains(targetName)) { if (context.useModules) { // TODO: when using several qualified Globals classes, we need // to disambiguate (Globals__1, Globals__2, ....) // TODO: IDEA FOR MODULES AND FULLY QUALIFIED NAMES // when using a fully qualified name in the code, we need an // import, which can be named after something like // qualName.replace(".", "_")... this would work in the general // case... boolean fullImport = require || GLOBALS_CLASS_NAME.equals(targetName); if (fullImport) { if (context.useRequireForModules) { print("import " + targetName + " = require(\"" + moduleName + "\"); ").println(); } else { print("import * as " + targetName + " from '" + moduleName + "'; ").println(); } } else { print("import { " + targetName + " } from '" + moduleName + "'; ").println(); } } context.registerImportedName(compilationUnit.getSourceFile().getName(), sourceElement, targetName); } } private boolean checkRootPackageParent(JCCompilationUnit topLevel, PackageSymbol rootPackage, PackageSymbol parentPackage) { if (parentPackage == null) { return true; } if (!context.options.isNoRootDirectories() || context.options.isBundle()) { return true; } if (context.isRootPackage(parentPackage)) { report(topLevel.getPackageName(), JSweetProblem.ENCLOSED_ROOT_PACKAGES, rootPackage.getQualifiedName().toString(), parentPackage.getQualifiedName().toString()); return false; } for (Symbol s : parentPackage.getEnclosedElements()) { if (s instanceof ClassSymbol) { if (Util.isSourceElement(s)) { report(topLevel.getPackageName(), JSweetProblem.CLASS_OUT_OF_ROOT_PACKAGE_SCOPE, s.getQualifiedName().toString(), rootPackage.getQualifiedName().toString()); return false; } } } return checkRootPackageParent(topLevel, rootPackage, (PackageSymbol) parentPackage.owner); } @Override public void visitTopLevel(JCCompilationUnit topLevel) { if (context.isPackageErased(topLevel.packge)) { return; } boolean noDefs = true; for (JCTree def : topLevel.defs) { if (def instanceof JCClassDecl) { if (!context.isIgnored(((JCClassDecl) def))) { noDefs = false; } } } // do not print the compilation unit at all if no defs are to be printed if (!context.bundleMode && noDefs) { return; } isDefinitionScope = topLevel.packge.getQualifiedName().toString() .startsWith(JSweetConfig.LIBS_PACKAGE + "."); if (context.hasAnnotationType(topLevel.packge, JSweetConfig.ANNOTATION_MODULE)) { context.addExportedElement( context.getAnnotationValue(topLevel.packge, JSweetConfig.ANNOTATION_MODULE, null), topLevel.packge, getCompilationUnit()); } PackageSymbol rootPackage = context.getFirstEnclosingRootPackage(topLevel.packge); if (rootPackage != null) { if (!checkRootPackageParent(topLevel, rootPackage, (PackageSymbol) rootPackage.owner)) { return; } } context.importedTopPackages.clear(); context.rootPackages.add(rootPackage); // TODO: check relaxing @Root // if (context.useModules && context.rootPackages.size() > 1) { // if (!context.reportedMultipleRootPackages) { // report(topLevel.getPackageName(), // JSweetProblem.MULTIPLE_ROOT_PACKAGES_NOT_ALLOWED_WITH_MODULES, // context.rootPackages.toString()); // context.reportedMultipleRootPackages = true; // } // return; // } topLevelPackage = context.getTopLevelPackage(topLevel.packge); if (topLevelPackage != null) { context.topLevelPackageNames.add(topLevelPackage.getQualifiedName().toString()); } footer.delete(0, footer.length()); setCompilationUnit(topLevel); String packge = topLevel.packge.toString(); boolean globalModule = JSweetConfig.GLOBALS_PACKAGE_NAME.equals(packge) || packge.endsWith("." + JSweetConfig.GLOBALS_PACKAGE_NAME); String rootRelativePackageName = ""; if (!globalModule) { rootRelativePackageName = getRootRelativeName(topLevel.packge); if (rootRelativePackageName.length() == 0) { globalModule = true; } } List<String> packageSegments = new ArrayList<String>(Arrays.asList(rootRelativePackageName.split("\\."))); packageSegments.retainAll(JSweetConfig.TS_TOP_LEVEL_KEYWORDS); if (!packageSegments.isEmpty()) { report(topLevel.getPackageName(), JSweetProblem.PACKAGE_NAME_CONTAINS_KEYWORD, packageSegments); } // generate requires by looking up imported external modules for (JCImport importDecl : topLevel.getImports()) { TreeScanner importedModulesScanner = new TreeScanner() { @Override public void scan(JCTree tree) { if (tree instanceof JCFieldAccess) { JCFieldAccess qualified = (JCFieldAccess) tree; if (qualified.sym != null) { // regular import case (qualified.sym is a package) if (context.hasAnnotationType(qualified.sym, JSweetConfig.ANNOTATION_MODULE)) { String actualName = context.getAnnotationValue(qualified.sym, JSweetConfig.ANNOTATION_MODULE, null); useModule(true, null, importDecl, qualified.name.toString(), actualName, ((PackageSymbol) qualified.sym)); } } else { // static import case (imported fields and methods) if (qualified.selected instanceof JCFieldAccess) { JCFieldAccess qualifier = (JCFieldAccess) qualified.selected; if (qualifier.sym != null) { try { for (Symbol importedMember : qualifier.sym.getEnclosedElements()) { if (qualified.name.equals(importedMember.getSimpleName())) { if (context.hasAnnotationType(importedMember, JSweetConfig.ANNOTATION_MODULE)) { String actualName = context.getAnnotationValue(importedMember, JSweetConfig.ANNOTATION_MODULE, null); useModule(true, null, importDecl, importedMember.getSimpleName().toString(), actualName, importedMember); break; } } } } catch (Exception e) { // TODO: sometimes, // getEnclosedElement // fails because of string types // (find // out why if possible) e.printStackTrace(); } } } } } super.scan(tree); } }; importedModulesScanner.scan(importDecl.qualid); } for (JCImport importDecl : topLevel.getImports()) { if (importDecl.qualid instanceof JCFieldAccess) { JCFieldAccess qualified = (JCFieldAccess) importDecl.qualid; String importedName = qualified.name.toString(); if (importDecl.isStatic() && (qualified.selected instanceof JCFieldAccess)) { qualified = (JCFieldAccess) qualified.selected; } if (qualified.sym instanceof ClassSymbol) { ClassSymbol importedClass = (ClassSymbol) qualified.sym; if (Util.isSourceElement(importedClass) && !importedClass.getQualifiedName().toString() .startsWith(JSweetConfig.LIBS_PACKAGE + ".")) { String importedModule = importedClass.sourcefile.getName(); if (importedModule.equals(compilationUnit.sourcefile.getName())) { continue; } String pathToImportedClass = Util.getRelativePath( new File(compilationUnit.sourcefile.getName()).getParent(), importedModule); pathToImportedClass = pathToImportedClass.substring(0, pathToImportedClass.length() - 5); if (!pathToImportedClass.startsWith(".")) { pathToImportedClass = "./" + pathToImportedClass; } Symbol symbol = qualified.sym.getEnclosingElement(); while (!(symbol instanceof PackageSymbol)) { importedName = symbol.getSimpleName().toString(); symbol = symbol.getEnclosingElement(); } if (symbol != null) { useModule(false, (PackageSymbol) symbol, importDecl, importedName, pathToImportedClass.replace('\\', '/'), null); } } } } } if (context.useModules) { TreeScanner usedTypesScanner = new TreeScanner() { private void checkType(TypeSymbol type) { if (type instanceof ClassSymbol) { if (type.getEnclosingElement().equals(compilationUnit.packge)) { String importedModule = ((ClassSymbol) type).sourcefile.getName(); if (!importedModule.equals(compilationUnit.sourcefile.getName())) { importedModule = "./" + new File(importedModule).getName(); importedModule = importedModule.substring(0, importedModule.length() - 5); useModule(false, (PackageSymbol) type.getEnclosingElement(), null, type.getSimpleName().toString(), importedModule, null); } } } } @Override public void scan(JCTree t) { if (t != null && t.type != null && t.type.tsym instanceof ClassSymbol) { if (!(t instanceof JCTypeApply)) { checkType(t.type.tsym); } } super.scan(t); } }; usedTypesScanner.scan(compilationUnit); } // require root modules when using fully qualified names or reserved // keywords TreeScanner inlinedModuleScanner = new TreeScanner() { Stack<JCTree> stack = new Stack<>(); public void scan(JCTree t) { if (t != null) { stack.push(t); try { super.scan(t); } finally { stack.pop(); } } } @SuppressWarnings("unchecked") public <T extends JCTree> T getParent(Class<T> type) { for (int i = this.stack.size() - 2; i >= 0; i--) { if (type.isAssignableFrom(this.stack.get(i).getClass())) { return (T) this.stack.get(i); } } return null; } public void visitIdent(JCIdent identifier) { if (identifier.sym instanceof PackageSymbol) { // ignore packages in imports if (getParent(JCImport.class) != null) { return; } boolean isSourceType = false; for (int i = stack.size() - 2; i >= 0; i--) { JCTree tree = stack.get(i); if (!(tree instanceof JCFieldAccess)) { break; } else { JCFieldAccess fa = (JCFieldAccess) tree; if ((fa.sym instanceof ClassSymbol) && Util.isSourceElement(fa.sym)) { isSourceType = true; break; } } } if (!isSourceType) { return; } PackageSymbol identifierPackage = (PackageSymbol) identifier.sym; String pathToModulePackage = Util.getRelativePath(compilationUnit.packge, identifierPackage); if (pathToModulePackage == null) { return; } File moduleFile = new File(new File(pathToModulePackage), JSweetConfig.MODULE_FILE_NAME); if (!identifierPackage.getSimpleName().toString() .equals(compilationUnit.packge.getSimpleName().toString())) { useModule(false, identifierPackage, identifier, identifierPackage.getSimpleName().toString(), moduleFile.getPath().replace('\\', '/'), null); } } else if (identifier.sym instanceof ClassSymbol) { if (JSweetConfig.GLOBALS_PACKAGE_NAME .equals(identifier.sym.getEnclosingElement().getSimpleName().toString())) { String pathToModulePackage = Util.getRelativePath(compilationUnit.packge, identifier.sym.getEnclosingElement()); if (pathToModulePackage == null) { return; } File moduleFile = new File(new File(pathToModulePackage), JSweetConfig.MODULE_FILE_NAME); if (!identifier.sym.getEnclosingElement() .equals(compilationUnit.packge.getSimpleName().toString())) { useModule(false, (PackageSymbol) identifier.sym.getEnclosingElement(), identifier, JSweetConfig.GLOBALS_PACKAGE_NAME, moduleFile.getPath().replace('\\', '/'), null); } } } } @Override public void visitApply(JCMethodInvocation invocation) { // 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 .getEnclosingElement().getEnclosingElement(); String rootRelativeInvocationPackageName = getRootRelativeName(invocationPackage); if (rootRelativeInvocationPackageName.indexOf('.') == -1) { super.visitApply(invocation); return; } String targetRootPackageName = rootRelativeInvocationPackageName.substring(0, rootRelativeInvocationPackageName.indexOf('.')); String pathToReachRootPackage = Util.getRelativePath( "/" + compilationUnit.packge.getQualifiedName().toString().replace('.', '/'), "/" + targetRootPackageName); if (pathToReachRootPackage == null) { super.visitApply(invocation); return; } File moduleFile = new File(new File(pathToReachRootPackage), JSweetConfig.MODULE_FILE_NAME); if (!invocationPackage.toString().equals(compilationUnit.packge.getSimpleName().toString())) { useModule(false, invocationPackage, invocation, targetRootPackageName, moduleFile.getPath().replace('\\', '/'), null); } } super.visitApply(invocation); } }; // TODO: change the way qualified names are handled (because of new // module organization) // inlinedModuleScanner.scan(compilationUnit); if (!globalModule && !context.useModules) { printIndent(); if (isDefinitionScope) { print("declare "); } print("namespace ").print(rootRelativePackageName).print(" {").startIndent().println(); } for (JCTree def : topLevel.defs) { mainMethod = null; printIndent(); int pos = getCurrentPosition(); print(def); if (getCurrentPosition() == pos) { removeLastIndent(); continue; } println().println(); } if (!globalModule && !context.useModules) { removeLastChar().endIndent().printIndent().print("}").println(); } if (footer.length() > 0) { println().print(footer.toString()); } globalModule = false; } private void printDocComment(JCTree element, boolean indent) { if (compilationUnit != null && compilationUnit.docComments != null) { Comment comment = compilationUnit.docComments.getComment(element); String commentText = getAdapter().adaptDocComment(element, comment == null ? null : comment.getText()); List<String> lines = new ArrayList<>(); if (commentText != null) { lines.addAll(Arrays.asList(commentText.split("\n"))); } if (!lines.isEmpty()) { if (indent) { printIndent(); } print("/**").println(); for (String line : lines) { printIndent().print(" * ").print(line.trim()).println(); } removeLastChar(); println().printIndent().print(" ").print("*/").println(); if (!indent) { printIndent(); } } } } private void printAnonymousClassTypeArgs(JCNewClass newClass) { if ((newClass.clazz instanceof JCTypeApply)) { JCTypeApply tapply = (JCTypeApply) newClass.clazz; if (tapply.getTypeArguments() != null && !tapply.getTypeArguments().isEmpty()) { boolean printed = false; print("<"); for (JCExpression targ : tapply.getTypeArguments()) { if (targ.type.tsym instanceof TypeVariableSymbol) { printed = true; print(targ).print(", "); } } if (printed) { removeLastChars(2); print(">"); } else { removeLastChar(); } } } } private boolean isAnonymousClass() { return scope.size() > 1 && getScope(1).isAnonymousClass; } private boolean isInnerClass() { return scope.size() > 1 && getScope(1).isInnerClass; } private boolean isLocalClass() { return scope.size() > 1 && getScope(1).isLocalClass; } /** * A flags that indicates that this adapter is printing type parameters. */ private boolean inTypeParameters = false; /** * A flags that indicates that this adapter is not substituting types. */ private boolean disableTypeSubstitution = false; protected final AbstractTreePrinter substituteAndPrintType(JCTree typeTree) { return substituteAndPrintType(typeTree, false, inTypeParameters, true, disableTypeSubstitution); } private AbstractTreePrinter printArguments(List<JCExpression> arguments) { int i = 1; for (JCExpression argument : arguments) { printArgument(argument, i++).print(", "); } if (arguments.size() > 0) { removeLastChars(2); } return this; } private AbstractTreePrinter printArgument(JCExpression argument, int i) { print("p" + i + ": "); substituteAndPrintType(argument, false, false, true, false); return this; } private AbstractTreePrinter substituteAndPrintType(JCTree typeTree, boolean arrayComponent, boolean inTypeParameters, boolean completeRawTypes, boolean disableSubstitution) { if (typeTree.type.tsym instanceof TypeVariableSymbol) { if (getAdapter().typeVariablesToErase.contains(typeTree.type.tsym)) { return print("any"); } } if (!disableSubstitution) { if (context.hasAnnotationType(typeTree.type.tsym, ANNOTATION_ERASED)) { return print("any"); } if (context.hasAnnotationType(typeTree.type.tsym, ANNOTATION_OBJECT_TYPE)) { // TODO: in case of object types, we should replace with the org // object type... return print("any"); } String typeFullName = typeTree.type.getModelType().toString(); // typeTree.type.tsym.getQualifiedName().toString(); if (Runnable.class.getName().equals(typeFullName)) { if (arrayComponent) { print("("); } print("() => void"); if (arrayComponent) { print(")"); } return this; } if (typeTree instanceof JCTypeApply) { JCTypeApply typeApply = ((JCTypeApply) typeTree); String typeName = typeApply.clazz.toString(); String mappedTypeName = context.getTypeMappingTarget(typeName); if (mappedTypeName != null && mappedTypeName.endsWith("<>")) { print(typeName.substring(0, mappedTypeName.length() - 2)); return this; } if (typeFullName.startsWith(TUPLE_CLASSES_PACKAGE + ".")) { print("["); for (JCExpression argument : typeApply.arguments) { substituteAndPrintType(argument, arrayComponent, inTypeParameters, completeRawTypes, false) .print(","); } if (typeApply.arguments.length() > 0) { removeLastChar(); } print("]"); return this; } if (typeFullName.startsWith(UNION_CLASS_NAME)) { print("("); for (JCExpression argument : typeApply.arguments) { print("("); substituteAndPrintType(argument, arrayComponent, inTypeParameters, completeRawTypes, false); print(")"); print("|"); } if (typeApply.arguments.length() > 0) { removeLastChar(); } print(")"); return this; } if (typeFullName.startsWith(UTIL_PACKAGE + ".") || typeFullName.startsWith("java.util.function.")) { if (typeName.endsWith("Consumer") || typeName.startsWith("Consumer")) { if (arrayComponent) { print("("); } print("("); if (typeName.startsWith("Int") || typeName.startsWith("Long") || typeName.startsWith("Double")) { print("p0 : number"); } else { printArguments(typeApply.arguments); } print(") => void"); if (arrayComponent) { print(")"); } return this; } else if (typeName.endsWith("Function") || typeName.startsWith("Function")) { if (arrayComponent) { print("("); } print("("); if (typeName.startsWith("Int") || typeName.startsWith("Long") || typeName.startsWith("Double")) { print("p0 : number"); } else { printArguments(typeApply.arguments.subList(0, typeApply.arguments.length() - 1)); } print(") => "); substituteAndPrintType(typeApply.arguments.get(typeApply.arguments.length() - 1), arrayComponent, inTypeParameters, completeRawTypes, false); if (arrayComponent) { print(")"); } return this; } else if (typeName.endsWith("Supplier") || typeName.startsWith("Supplier")) { if (arrayComponent) { print("("); } print("("); print(") => "); if (typeName.startsWith("Int") || typeName.startsWith("Long") || typeName.startsWith("Double")) { print("number"); } else { substituteAndPrintType(typeApply.arguments.get(0), arrayComponent, inTypeParameters, completeRawTypes, false); } if (arrayComponent) { print(")"); } return this; } else if (typeName.endsWith("Predicate")) { if (arrayComponent) { print("("); } print("("); if (typeName.startsWith("Int") || typeName.startsWith("Long") || typeName.startsWith("Double")) { print("p0 : number"); } else { printArguments(typeApply.arguments); } print(") => boolean"); if (arrayComponent) { print(")"); } return this; } else if (typeName.endsWith("Operator")) { if (arrayComponent) { print("("); } print("("); printArgument(typeApply.arguments.head, 1); if (typeName.startsWith("Binary")) { print(", "); printArgument(typeApply.arguments.head, 2); } print(") => "); substituteAndPrintType(typeApply.arguments.head, arrayComponent, inTypeParameters, completeRawTypes, false); if (arrayComponent) { print(")"); } return this; } } if (typeFullName.startsWith(Class.class.getName() + "<")) { return print("any"); } } else { if (!(typeTree instanceof JCArrayTypeTree) && typeFullName.startsWith("java.util.function.")) { // case of a raw functional type (programmer's mistake) return print("any"); } String mappedType = context.getTypeMappingTarget(typeFullName); if (mappedType != null) { if (mappedType.endsWith("<>")) { print(mappedType.substring(0, mappedType.length() - 2)); } else { print(mappedType); if (completeRawTypes && !typeTree.type.tsym.getTypeParameters().isEmpty() && !context.getTypeMappingTarget(typeFullName).equals("any")) { printAnyTypeArguments(typeTree.type.tsym.getTypeParameters().size()); } } return this; } } for (BiFunction<ExtendedElement, String, Object> mapping : context.getFunctionalTypeMappings()) { Object mapped = mapping.apply(new ExtendedElementSupport(typeTree), typeFullName); if (mapped instanceof String) { print((String) mapped); return this; } else if (mapped instanceof JCTree) { substituteAndPrintType((JCTree) mapped); return this; } else if (mapped instanceof TypeMirror) { print(getAdapter().getMappedType((TypeMirror) mapped)); return this; } } } if (typeTree instanceof JCTypeApply) { JCTypeApply typeApply = ((JCTypeApply) typeTree); substituteAndPrintType(typeApply.clazz, arrayComponent, inTypeParameters, false, disableSubstitution); if (!typeApply.arguments.isEmpty() && !"any".equals(getLastPrintedString(3)) && !"Object".equals(getLastPrintedString(6))) { print("<"); for (JCExpression argument : typeApply.arguments) { substituteAndPrintType(argument, arrayComponent, false, completeRawTypes, false).print(", "); } if (typeApply.arguments.length() > 0) { removeLastChars(2); } print(">"); } return this; } else if (typeTree instanceof JCWildcard) { JCWildcard wildcard = ((JCWildcard) typeTree); String name = context.getWildcardName(wildcard); if (name == null) { return print("any"); } else { print(name); if (inTypeParameters) { print(" extends "); return substituteAndPrintType(wildcard.getBound(), arrayComponent, false, completeRawTypes, disableSubstitution); } else { return this; } } } else { if (typeTree instanceof JCArrayTypeTree) { return substituteAndPrintType(((JCArrayTypeTree) typeTree).elemtype, true, inTypeParameters, completeRawTypes, disableSubstitution).print("[]"); } if (completeRawTypes && typeTree.type.tsym.getTypeParameters() != null && !typeTree.type.tsym.getTypeParameters().isEmpty()) { // raw type case (Java warning) print(typeTree); print("<"); for (int i = 0; i < typeTree.type.tsym.getTypeParameters().length(); i++) { print("any, "); } removeLastChars(2); print(">"); return this; } else { return print(typeTree); } } } @Override public void visitClassDef(JCClassDecl classdecl) { if (context.isIgnored(classdecl)) { getAdapter().afterType(classdecl.sym); return; } String name = classdecl.getSimpleName().toString(); if (!scope.isEmpty() && getScope().anonymousClasses.contains(classdecl)) { name = getScope().name + ANONYMOUS_PREFIX + getScope().anonymousClasses.indexOf(classdecl); } JCTree testParent = getFirstParent(JCClassDecl.class, JCMethodDecl.class); if (testParent != null && testParent instanceof JCMethodDecl) { if (!isLocalClass()) { getScope().localClasses.add(classdecl); return; } } enterScope(); getScope().name = name; JCClassDecl parent = getParent(JCClassDecl.class); List<TypeVariableSymbol> parentTypeVars = new ArrayList<>(); if (parent != null) { getScope().innerClass = true; if (!classdecl.getModifiers().getFlags().contains(Modifier.STATIC)) { getScope().innerClassNotStatic = true; if (parent.getTypeParameters() != null) { parentTypeVars.addAll(parent.getTypeParameters().stream() .map(t -> (TypeVariableSymbol) t.type.tsym).collect(Collectors.toList())); getAdapter().typeVariablesToErase.addAll(parentTypeVars); } } } getScope().declareClassScope = context.hasAnnotationType(classdecl.sym, JSweetConfig.ANNOTATION_AMBIENT) || isDefinitionScope; getScope().interfaceScope = false; getScope().removedSuperclass = false; getScope().enumScope = false; getScope().enumWrapperClassScope = false; HashSet<Entry<JCClassDecl, JCMethodDecl>> defaultMethods = null; boolean globals = JSweetConfig.GLOBALS_CLASS_NAME.equals(classdecl.name.toString()); if (globals && classdecl.extending != null) { report(classdecl, JSweetProblem.GLOBALS_CLASS_CANNOT_HAVE_SUPERCLASS); } List<Type> implementedInterfaces = new ArrayList<>(); if (!globals) { if (classdecl.extending != null && JSweetConfig.GLOBALS_CLASS_NAME .equals(classdecl.extending.type.tsym.getSimpleName().toString())) { report(classdecl, JSweetProblem.GLOBALS_CLASS_CANNOT_BE_SUBCLASSED); return; } printDocComment(classdecl, false); print(classdecl.mods); if (!isTopLevelScope() || context.useModules || isAnonymousClass() || isInnerClass() || isLocalClass()) { print("export "); } if (context.isInterface(classdecl.sym)) { print("interface "); getScope().interfaceScope = true; } else { if (classdecl.getKind() == Kind.ENUM) { if (getScope().declareClassScope && !(getIndent() != 0 && isDefinitionScope)) { print("declare "); } if (scope.size() > 1 && getScope(1).isComplexEnum) { if (Util.hasAbstractMethod(classdecl.sym)) { print("abstract "); } print("class "); getScope().enumWrapperClassScope = true; } else { print("enum "); getScope().enumScope = true; } } else { if (getScope().declareClassScope && !(getIndent() != 0 && isDefinitionScope)) { print("declare "); } defaultMethods = new HashSet<>(); Util.findDefaultMethodsInType(defaultMethods, context, classdecl.sym); if (classdecl.getModifiers().getFlags().contains(Modifier.ABSTRACT)) { print("abstract "); } print("class "); } } print(name + (getScope().enumWrapperClassScope ? ENUM_WRAPPER_CLASS_SUFFIX : "")); if (classdecl.typarams != null && classdecl.typarams.size() > 0) { print("<").printArgList(null, classdecl.typarams).print(">"); } else if (isAnonymousClass() && classdecl.getModifiers().getFlags().contains(Modifier.STATIC)) { JCNewClass newClass = getScope(1).anonymousClassesConstructors .get(getScope(1).anonymousClasses.indexOf(classdecl)); printAnonymousClassTypeArgs(newClass); } String mixin = null; if (context.hasAnnotationType(classdecl.sym, JSweetConfig.ANNOTATION_MIXIN)) { mixin = context.getAnnotationValue(classdecl.sym, JSweetConfig.ANNOTATION_MIXIN, null); } boolean extendsInterface = false; if (classdecl.extending != null) { boolean removeIterable = false; if (context.hasAnnotationType(classdecl.sym, JSweetConfig.ANNOTATION_SYNTACTIC_ITERABLE) && classdecl.extending.type.tsym.getQualifiedName().toString() .equals(Iterable.class.getName())) { removeIterable = true; } if (!removeIterable && !JSweetConfig.isJDKReplacementMode() && !(JSweetConfig.OBJECT_CLASSNAME.equals(classdecl.extending.type.toString()) || Object.class.getName().equals(classdecl.extending.type.toString())) && !(mixin != null && mixin.equals(classdecl.extending.type.toString())) && !(getAdapter() .eraseSuperClass(classdecl.sym, (ClassSymbol) classdecl.extending.type.tsym))) { if (!getScope().interfaceScope && context.isInterface(classdecl.extending.type.tsym)) { extendsInterface = true; print(" implements "); implementedInterfaces.add(classdecl.extending.type); } else { print(" extends "); } if (getScope().enumWrapperClassScope && getScope(1).anonymousClasses.contains(classdecl)) { print(classdecl.extending.toString() + ENUM_WRAPPER_CLASS_SUFFIX); } else { disableTypeSubstitution = !getAdapter().isSubstituteSuperTypes(); substituteAndPrintType(classdecl.extending); disableTypeSubstitution = false; } if (context.classesWithWrongConstructorOverload.contains(classdecl.sym)) { getScope().hasConstructorOverloadWithSuperClass = true; } } else { getScope().removedSuperclass = true; } } if (classdecl.implementing != null && !classdecl.implementing.isEmpty()) { List<JCExpression> implementing = new ArrayList<>(classdecl.implementing); if (context.hasAnnotationType(classdecl.sym, JSweetConfig.ANNOTATION_SYNTACTIC_ITERABLE)) { for (JCExpression itf : classdecl.implementing) { if (itf.type.tsym.getQualifiedName().toString().equals(Iterable.class.getName())) { implementing.remove(itf); } } } // erase Java interfaces for (JCExpression itf : classdecl.implementing) { if (context.isFunctionalType(itf.type.tsym) || getAdapter().eraseSuperInterface(classdecl.sym, (ClassSymbol) itf.type.tsym)) { implementing.remove(itf); } } if (!implementing.isEmpty()) { if (!extendsInterface) { if (getScope().interfaceScope) { print(" extends "); } else { print(" implements "); } } else { print(", "); } for (JCExpression itf : implementing) { disableTypeSubstitution = !getAdapter().isSubstituteSuperTypes(); substituteAndPrintType(itf); disableTypeSubstitution = false; implementedInterfaces.add(itf.type); print(", "); } removeLastChars(2); } } print(" {").println().startIndent(); } if (getScope().innerClassNotStatic && !getScope().interfaceScope && !getScope().enumScope) { printIndent().print("public " + PARENT_CLASS_FIELD_NAME + ": any;").println(); } if (defaultMethods != null && !defaultMethods.isEmpty()) { getScope().defaultMethodScope = true; for (Entry<JCClassDecl, JCMethodDecl> entry : defaultMethods) { if (!(entry.getValue().type instanceof MethodType)) { continue; } MethodSymbol s = Util.findMethodDeclarationInType(context.types, classdecl.sym, entry.getValue().getName().toString(), (MethodType) entry.getValue().type); if (s == null || s == entry.getValue().sym) { getAdapter().typeVariablesToErase .addAll(((ClassSymbol) s.getEnclosingElement()).getTypeParameters()); printIndent().print(entry.getValue()).println(); getAdapter().typeVariablesToErase .removeAll(((ClassSymbol) s.getEnclosingElement()).getTypeParameters()); } } getScope().defaultMethodScope = false; } if (getScope().enumScope) { printIndent(); } if (globals) { removeLastIndent(); } if (!getScope().interfaceScope && classdecl.getModifiers().getFlags().contains(Modifier.ABSTRACT)) { List<MethodSymbol> methods = new ArrayList<>(); for (Type t : implementedInterfaces) { context.grabMethodsToBeImplemented(methods, t.tsym); } Map<Name, String> signatures = new HashMap<>(); for (MethodSymbol meth : methods) { if (meth.type instanceof MethodType) { MethodSymbol s = Util.findMethodDeclarationInType(getContext().types, classdecl.sym, meth.getSimpleName().toString(), (MethodType) meth.type, true); boolean printDefaultImplementation = false; if (s != null) { if (!s.getEnclosingElement().equals(classdecl.sym)) { if (!(s.isDefault() || (!context.isInterface((TypeSymbol) s.getEnclosingElement()) && !s.getModifiers().contains(Modifier.ABSTRACT)))) { printDefaultImplementation = true; } } } if (printDefaultImplementation) { Overload o = context.getOverload(classdecl.sym, meth); if (o != null && o.methods.size() > 1 && !o.isValid) { if (!meth.type.equals(o.coreMethod.type)) { printDefaultImplementation = false; } } } if (s == null || printDefaultImplementation) { String signature = getContext().types.erasure(meth.type).toString(); if (!(signatures.containsKey(meth.name) && signatures.get(meth.name).equals(signature))) { printDefaultImplementation(meth); signatures.put(meth.name, signature); } } } } } for (JCTree def : classdecl.defs) { if (def instanceof JCClassDecl) { getScope().hasInnerClass = true; } if (def instanceof JCVariableDecl) { JCVariableDecl var = (JCVariableDecl) def; if (!var.sym.isStatic() && var.init != null) { getScope().fieldsWithInitializers.add((JCVariableDecl) def); } } } if (!globals && !context.isInterface(classdecl.sym) && context.getStaticInitializerCount(classdecl.sym) > 0) { printIndent().print("static __static_initialized : boolean = false;").println(); int liCount = context.getStaticInitializerCount(classdecl.sym); String prefix = classdecl.getSimpleName().toString() + "."; printIndent().print("static __static_initialize() { "); print("if(!" + prefix + "__static_initialized) { "); print(prefix + "__static_initialized = true; "); for (int i = 0; i < liCount; i++) { print(prefix + "__static_initializer_" + i + "(); "); } print("} }").println().println(); String qualifiedClassName = getQualifiedTypeName(classdecl.sym, globals); context.addTopFooterStatement( (isBlank(qualifiedClassName) ? "" : qualifiedClassName + ".__static_initialize();")); } boolean hasUninitializedFields = false; for (JCTree def : classdecl.defs) { if (getScope().interfaceScope && ((def instanceof JCMethodDecl && ((JCMethodDecl) def).sym.isStatic()) || (def instanceof JCVariableDecl && ((JCVariableDecl) def).sym.isStatic()))) { // static interface members are printed in a namespace continue; } if (def instanceof JCClassDecl) { // inner types are be printed in a namespace continue; } if (def instanceof JCVariableDecl) { if (getScope().enumScope && ((JCVariableDecl) def).type.tsym != classdecl.type.tsym) { getScope().isComplexEnum = true; continue; } if (!((JCVariableDecl) def).getModifiers().getFlags().contains(Modifier.STATIC) && ((JCVariableDecl) def).init == null) { hasUninitializedFields = true; } } if (def instanceof JCBlock && !((JCBlock) def).isStatic()) { hasUninitializedFields = true; } if (!getScope().enumScope) { printIndent(); } int pos = getCurrentPosition(); print(def); if (getCurrentPosition() == pos) { if (!getScope().enumScope) { removeLastIndent(); } continue; } if (def instanceof JCVariableDecl) { if (getScope().enumScope) { print(", "); } else { print(";").println().println(); } } else { println().println(); } } if (!getScope().hasDeclaredConstructor && !(getScope().interfaceScope || getScope().enumScope || getScope().declareClassScope)) { Set<String> interfaces = new HashSet<>(); context.grabSupportedInterfaceNames(interfaces, classdecl.sym); if (!interfaces.isEmpty() || getScope().innerClassNotStatic || hasUninitializedFields) { printIndent().print("constructor("); boolean hasArgs = false; if (getScope().innerClassNotStatic) { print(PARENT_CLASS_FIELD_NAME + ": any"); hasArgs = true; } int anonymousClassIndex = scope.size() > 1 ? getScope(1).anonymousClasses.indexOf(classdecl) : -1; if (anonymousClassIndex != -1) { for (int i = 0; i < getScope(1).anonymousClassesConstructors.get(anonymousClassIndex).args .length(); i++) { if (!hasArgs) { hasArgs = true; } else { print(", "); } print("__arg" + i + ": any"); } for (VarSymbol v : getScope(1).finalVariables.get(anonymousClassIndex)) { if (!hasArgs) { hasArgs = true; } else { print(", "); } print("private " + v.getSimpleName() + ": any"); } } print(") {").startIndent().println(); if (classdecl.extending != null && !getScope().removedSuperclass && !context.isInterface(classdecl.extending.type.tsym)) { printIndent().print("super("); if (getScope().innerClassNotStatic) { TypeSymbol s = classdecl.extending.type.tsym; boolean hasArg = false; if (s.getEnclosingElement() instanceof ClassSymbol && !s.isStatic()) { print(PARENT_CLASS_FIELD_NAME); hasArg = true; } if (anonymousClassIndex != -1) { for (int i = 0; i < getScope(1).anonymousClassesConstructors .get(anonymousClassIndex).args.length(); i++) { if (hasArg) { print(", "); } else { hasArg = true; } print("__arg" + i); } } } print(");").println(); } printInstanceInitialization(classdecl, null); endIndent().printIndent().print("}").println().println(); } } removeLastChar(); if (getScope().enumWrapperClassScope && !getScope(1).anonymousClasses.contains(classdecl)) { printIndent().print("public name() : string { return this." + ENUM_WRAPPER_CLASS_NAME + "; }") .println(); printIndent().print("public ordinal() : number { return this." + ENUM_WRAPPER_CLASS_ORDINAL + "; }") .println(); } if (getScope().enumScope) { removeLastChar().println(); } if (!globals) { endIndent().printIndent().print("}"); if (getContext().options.isSupportGetClass() && !getScope().interfaceScope && !getScope().declareClassScope && !getScope().enumScope && !classdecl.sym.isAnonymous()) { println().printIndent().print(classdecl.sym.getSimpleName().toString()) .print("[\"" + CLASS_NAME_IN_CONSTRUCTOR + "\"] = ") .print("\"" + context.getRootRelativeName(null, classdecl.sym) + "\";"); Set<String> interfaces = new HashSet<>(); context.grabSupportedInterfaceNames(interfaces, classdecl.sym); if (!interfaces.isEmpty()) { println().printIndent().print(classdecl.sym.getSimpleName().toString()) .print("[\"" + INTERFACES_FIELD_NAME + "\"] = "); print("["); for (String itf : interfaces) { print("\"").print(itf).print("\","); } removeLastChar(); print("];").println(); } if (!getScope().enumWrapperClassScope) { println(); } } } // enum class for complex enum if (getScope().isComplexEnum) { println().println().printIndent(); visitClassDef(classdecl); } // inner, anonymous and local classes in a namespace // ====================== // print valid inner classes boolean nameSpace = false; for (JCTree def : classdecl.defs) { if (def instanceof JCClassDecl) { JCClassDecl cdef = (JCClassDecl) def; if (context.isIgnored(cdef)) { continue; } if (!nameSpace) { nameSpace = true; println().println().printIndent(); if (!isTopLevelScope() || context.useModules) { print("export "); } else { if (isDefinitionScope) { print("declare "); } } print("namespace ").print(name).print(" {").startIndent(); } getScope().isInnerClass = true; println().println().printIndent().print(cdef); getScope().isInnerClass = false; } } // print anonymous classes for (JCClassDecl cdef : getScope().anonymousClasses) { if (!nameSpace) { nameSpace = true; println().println().printIndent(); if (!isTopLevelScope() || context.useModules) { print("export "); } print("namespace ").print(name).print(" {").startIndent(); } getScope().isAnonymousClass = true; println().println().printIndent().print(cdef); getScope().isAnonymousClass = false; } // print local classes for (JCClassDecl cdef : getScope().localClasses) { if (!nameSpace) { nameSpace = true; println().println().printIndent(); if (!isTopLevelScope() || context.useModules) { print("export "); } print("namespace ").print(name).print(" {").startIndent(); } getScope().isLocalClass = true; println().println().printIndent().print(cdef); getScope().isLocalClass = false; } if (nameSpace) { println().endIndent().printIndent().print("}").println(); } // end of namespace ================================================= if (getScope().enumScope && getScope().isComplexEnum && !getScope().anonymousClasses.contains(classdecl)) { println().printIndent().print(classdecl.sym.getSimpleName().toString()) .print("[\"" + ENUM_WRAPPER_CLASS_WRAPPERS + "\"] = ["); int index = 0; for (JCTree tree : classdecl.defs) { if (tree instanceof JCVariableDecl && ((JCVariableDecl) tree).type.equals(classdecl.type)) { JCVariableDecl varDecl = (JCVariableDecl) tree; // enum fields are not part of the enum auxiliary class but // will initialize the enum values JCNewClass newClass = (JCNewClass) varDecl.init; JCClassDecl clazz = classdecl; try { int anonymousClassIndex = getScope().anonymousClasses.indexOf(newClass.def); if (anonymousClassIndex >= 0) { print("new ") .print(clazz.getSimpleName().toString() + "." + clazz.getSimpleName().toString() + ANONYMOUS_PREFIX + anonymousClassIndex + ENUM_WRAPPER_CLASS_SUFFIX) .print("("); } else { print("new ").print(clazz.getSimpleName().toString() + ENUM_WRAPPER_CLASS_SUFFIX) .print("("); } print("" + (index++) + ", "); print("\"" + varDecl.sym.name.toString() + "\""); if (!newClass.args.isEmpty()) { print(", "); } printArgList(null, newClass.args).print(")"); print(", "); } catch (Exception e) { logger.error(e.getMessage(), e); } } } removeLastChars(2); print("];").println(); } if (getScope().interfaceScope) { // print static members of interfaces nameSpace = false; for (JCTree def : classdecl.defs) { if ((def instanceof JCMethodDecl && ((JCMethodDecl) def).sym.isStatic()) || (def instanceof JCVariableDecl && ((JCVariableDecl) def).sym.isStatic())) { if (def instanceof JCVariableDecl && context.hasAnnotationType(((JCVariableDecl) def).sym, ANNOTATION_STRING_TYPE, JSweetConfig.ANNOTATION_ERASED)) { continue; } if (!nameSpace) { nameSpace = true; println().println().printIndent(); if (getIndent() != 0 || context.useModules) { print("export "); } else { if (isDefinitionScope) { print("declare "); } } print("namespace ").print(classdecl.getSimpleName().toString()).print(" {").startIndent(); } println().println().printIndent().print(def); if (def instanceof JCVariableDecl) { print(";"); } } } if (nameSpace) { println().endIndent().printIndent().print("}").println(); } } if (mainMethod != null && mainMethod.getParameters().size() < 2 && mainMethod.sym.getEnclosingElement().equals(classdecl.sym)) { String mainClassName = getQualifiedTypeName(classdecl.sym, globals); String mainMethodQualifier = mainClassName; if (!isBlank(mainClassName)) { mainMethodQualifier = mainClassName + "."; } context.entryFiles.add(new File(compilationUnit.sourcefile.getName())); context.addFooterStatement(mainMethodQualifier + JSweetConfig.MAIN_FUNCTION_NAME + "(" + (mainMethod.getParameters().isEmpty() ? "" : "null") + ");"); } getAdapter().typeVariablesToErase.removeAll(parentTypeVars); exitScope(); getAdapter().afterType(classdecl.sym); } private void printDefaultImplementation(MethodSymbol method) { printIndent().print("public abstract ").print(method.getSimpleName().toString()); print("("); if (method.getParameters() != null && !method.getParameters().isEmpty()) { for (VarSymbol var : method.getParameters()) { print(var.name.toString()).print(": any"); print(", "); } removeLastChars(2); } print(")"); print(": any;").println(); } private String getQualifiedTypeName(TypeSymbol type, boolean globals) { return getAdapter().getQualifiedTypeName(type, globals); } private String getTSMethodName(JCMethodDecl methodDecl) { String name = context.getActualName(methodDecl.sym); switch (name) { case "<init>": return "constructor"; case JSweetConfig.ANONYMOUS_FUNCTION_NAME: case JSweetConfig.ANONYMOUS_STATIC_FUNCTION_NAME: return ""; case JSweetConfig.NEW_FUNCTION_NAME: return "new"; default: return name; } } private boolean printCoreMethodDelegate = false; protected boolean isDebugMode(JCMethodDecl methodDecl) { return methodDecl != null && !constructor && context.options.isDebugMode() && !(context.hasAnnotationType(methodDecl.sym, JSweetConfig.ANNOTATION_NO_DEBUG) || context .hasAnnotationType(methodDecl.sym.getEnclosingElement(), JSweetConfig.ANNOTATION_NO_DEBUG)); } boolean constructor = false; @Override public void visitMethodDef(JCMethodDecl methodDecl) { if (context.hasAnnotationType(methodDecl.sym, JSweetConfig.ANNOTATION_ERASED)) { // erased elements are ignored return; } JCClassDecl parent = (JCClassDecl) getParent(); if (parent != null && methodDecl.pos == parent.pos && !getScope().enumWrapperClassScope) { return; } if (JSweetConfig.INDEXED_GET_FUCTION_NAME.equals(methodDecl.getName().toString()) && methodDecl.getParameters().size() == 1) { print("[").print(methodDecl.getParameters().head).print("]: "); substituteAndPrintType(methodDecl.restype).print(";"); return; } constructor = methodDecl.sym.isConstructor(); if (getScope().enumScope) { if (constructor) { if (parent != null && parent.pos != methodDecl.pos) { getScope().isComplexEnum = true; } } else { getScope().isComplexEnum = true; } return; } Overload overload = null; boolean inOverload = false; boolean inCoreWrongOverload = false; if (parent != null) { overload = context.getOverload(parent.sym, methodDecl.sym); inOverload = overload != null && overload.methods.size() > 1; if (inOverload) { if (!overload.isValid) { if (!printCoreMethodDelegate) { if (overload.coreMethod.equals(methodDecl)) { inCoreWrongOverload = true; if (!context.isInterface(parent.sym) && !methodDecl.sym.isConstructor() && parent.sym.equals(overload.coreMethod.sym.getEnclosingElement())) { printCoreMethodDelegate = true; visitMethodDef(overload.coreMethod); println().println().printIndent(); printCoreMethodDelegate = false; } } else { if (methodDecl.sym.isConstructor()) { return; } if (!overload.printed && overload.coreMethod.sym.getEnclosingElement() != parent.sym && !overload.coreMethod.sym.getModifiers().contains(Modifier.ABSTRACT)) { visitMethodDef(overload.coreMethod); overload.printed = true; if (!context.isInterface(parent.sym)) { println().println().printIndent(); } } if (context.isInterface(parent.sym)) { return; } } } } else { if (!overload.coreMethod.equals(methodDecl)) { return; } } } } boolean ambient = context.hasAnnotationType(methodDecl.sym, JSweetConfig.ANNOTATION_AMBIENT); if (inOverload && !inCoreWrongOverload && (ambient || isDefinitionScope)) { // do not generate method stubs for definitions return; } if (isDebugMode(methodDecl)) { printMethodModifiers(methodDecl, parent, constructor, inOverload, overload); print(getTSMethodName(methodDecl)).print("("); printArgList(null, methodDecl.params); print(") : "); substituteAndPrintType(methodDecl.getReturnType()); print(" {").println(); startIndent().printIndent(); if (!context.types.isSameType(context.symtab.voidType, methodDecl.sym.getReturnType())) { print("return "); } print("__debug_exec('" + parent.sym.getQualifiedName() + "', '" + methodDecl.getName() + "', "); if (!methodDecl.params.isEmpty()) { print("["); for (JCVariableDecl param : methodDecl.params) { print("'" + param.getName() + "', "); } removeLastChars(2); print("]"); } else { print("undefined"); } print(", this, arguments, "); if (methodDecl.sym.isStatic()) { print(methodDecl.sym.getEnclosingElement().getSimpleName().toString()); } else { print("this"); } print("." + GENERATOR_PREFIX + getTSMethodName(methodDecl) + "("); for (JCVariableDecl param : methodDecl.params) { print(context.getActualName(param.sym) + ", "); } if (!methodDecl.params.isEmpty()) { removeLastChars(2); } print("));"); println().endIndent().printIndent(); print("}").println().println().printIndent(); } int jsniLine = -1; String[] content = null; if (methodDecl.mods.getFlags().contains(Modifier.NATIVE)) { if (!getScope().declareClassScope && !ambient && !getScope().interfaceScope) { content = getGetSource(getCompilationUnit()); if (content != null) { int line = 0; if (methodDecl.getParameters() != null && !methodDecl.getParameters().isEmpty()) { line = diagnosticSource.getLineNumber(methodDecl.getParameters().last().getStartPosition()) - 1; } else { line = diagnosticSource.getLineNumber(methodDecl.getStartPosition()) - 1; } if (content[line].contains("/*-{")) { jsniLine = line; } else { if (content[line + 1].contains("/*-{")) { jsniLine = line + 1; } } } if (jsniLine == -1) { report(methodDecl, methodDecl.name, JSweetProblem.NATIVE_MODIFIER_IS_NOT_ALLOWED, methodDecl.name); } } } else { if (getScope().declareClassScope && !constructor && !getScope().interfaceScope && !methodDecl.mods.getFlags().contains(Modifier.DEFAULT)) { report(methodDecl, methodDecl.name, JSweetProblem.INVALID_METHOD_BODY_IN_INTERFACE, methodDecl.name, parent == null ? "<no class>" : parent.name); } } if (methodDecl.name.toString().equals("constructor")) { report(methodDecl, methodDecl.name, JSweetProblem.CONSTRUCTOR_MEMBER); } if (parent != null) { VarSymbol v = Util.findFieldDeclaration(parent.sym, methodDecl.name); if (v != null && context.getFieldNameMapping(v) == null) { if (isDefinitionScope) { return; } else { report(methodDecl, methodDecl.name, JSweetProblem.METHOD_CONFLICTS_FIELD, methodDecl.name, v.owner); } } } if (JSweetConfig.MAIN_FUNCTION_NAME.equals(methodDecl.name.toString()) && methodDecl.mods.getFlags().contains(Modifier.STATIC) && !context.hasAnnotationType(methodDecl.sym, JSweetConfig.ANNOTATION_DISABLED)) { // ignore main methods in inner classes if (scope.size() == 1) { mainMethod = methodDecl; } } boolean globals = parent == null ? false : JSweetConfig.GLOBALS_CLASS_NAME.equals(parent.name.toString()); globals = globals || (getScope().interfaceScope && methodDecl.mods.getFlags().contains(Modifier.STATIC)); printDocComment(methodDecl, false); if (parent == null) { print("function "); } else if (globals) { if (constructor && methodDecl.sym.isPrivate() && methodDecl.getParameters().isEmpty()) { return; } if (constructor) { report(methodDecl, methodDecl.name, JSweetProblem.GLOBAL_CONSTRUCTOR_DEF); return; } if (!methodDecl.mods.getFlags().contains(Modifier.STATIC)) { report(methodDecl, methodDecl.name, JSweetProblem.GLOBALS_CAN_ONLY_HAVE_STATIC_MEMBERS); return; } if (context.hasAnnotationType(methodDecl.sym, JSweetConfig.ANNOTATION_MODULE)) { getContext().addExportedElement( context.getAnnotationValue(methodDecl.sym, JSweetConfig.ANNOTATION_MODULE, null), methodDecl.sym, getCompilationUnit()); } if (context.useModules) { if (!methodDecl.mods.getFlags().contains(Modifier.PRIVATE)) { print("export "); } } else { if (!isTopLevelScope()) { print("export "); } } if (ambient || (getIndent() == 0 && isDefinitionScope)) { print("declare "); } print("function "); } else { printMethodModifiers(methodDecl, parent, constructor, inOverload, overload); if (ambient) { report(methodDecl, methodDecl.name, JSweetProblem.WRONG_USE_OF_AMBIENT, methodDecl.name); } } if (parent == null || !context.isFunctionalType(parent.sym)) { if (isDebugMode(methodDecl)) { print("*").print(GENERATOR_PREFIX); } if (inOverload && !overload.isValid && !inCoreWrongOverload) { print(getOverloadMethodName(methodDecl.sym)); } else { String tsMethodName = getTSMethodName(methodDecl); if (doesMemberNameRequireQuotes(tsMethodName)) { print("'" + tsMethodName + "'"); } else { print(tsMethodName); } } } if ((methodDecl.typarams != null && !methodDecl.typarams.isEmpty()) || (getContext().getWildcards(methodDecl.sym) != null)) { inTypeParameters = true; print("<"); if (methodDecl.typarams != null && !methodDecl.typarams.isEmpty()) { printArgList(null, methodDecl.typarams); if (getContext().getWildcards(methodDecl.sym) != null) { print(", "); } } if (getContext().getWildcards(methodDecl.sym) != null) { printArgList(null, getContext().getWildcards(methodDecl.sym), this::substituteAndPrintType); } print(">"); inTypeParameters = false; } print("("); if (inCoreWrongOverload) { getScope().eraseVariableTypes = true; } boolean paramPrinted = false; if (getScope().innerClassNotStatic && methodDecl.sym.isConstructor()) { print(PARENT_CLASS_FIELD_NAME + ": any, "); paramPrinted = true; } if (constructor && getScope().enumWrapperClassScope) { print((isAnonymousClass() ? "" : "protected ") + ENUM_WRAPPER_CLASS_ORDINAL + " : number, "); print((isAnonymousClass() ? "" : "protected ") + ENUM_WRAPPER_CLASS_NAME + " : string"); if (!methodDecl.getParameters().isEmpty()) { print(", "); } } int i = 0; for (JCVariableDecl param : methodDecl.getParameters()) { print(param); if (inOverload && overload.isValid && overload.defaultValues.get(i) != null) { print(" = ").print(overload.defaultValues.get(i)); } print(", "); i++; paramPrinted = true; } if (inCoreWrongOverload) { getScope().eraseVariableTypes = false; } if (paramPrinted) { removeLastChars(2); } print(")"); if (inCoreWrongOverload && !methodDecl.sym.isConstructor()) { print(" : any"); } else { if (methodDecl.restype != null && methodDecl.restype.type.getTag() != TypeTag.VOID) { print(" : "); substituteAndPrintType(methodDecl.restype); } } if (inCoreWrongOverload && context.isInterface(parent.sym)) { print(";"); return; } if (methodDecl.getBody() == null && !(inCoreWrongOverload && !getScope().declareClassScope) || (methodDecl.mods.getFlags().contains(Modifier.DEFAULT) && !getScope().defaultMethodScope)) { if (!getScope().interfaceScope && methodDecl.getModifiers().getFlags().contains(Modifier.ABSTRACT) && inOverload && !overload.isValid) { print(" {"); // runtime error if we go there... print(" throw new Error('cannot invoke abstract overloaded method... check your argument(s) type(s)'); "); print("}"); } else if (jsniLine != -1) { int line = jsniLine; print(" {").println().startIndent(); String jsniCode = content[line].substring(content[line].indexOf("/*-{") + 4).trim(); StringBuilder jsni = new StringBuilder(); if (!StringUtils.isEmpty(jsniCode)) { jsni.append(jsniCode); jsni.append("\n"); } line++; while (!content[line].contains("}-*/")) { jsniCode = content[line++].trim(); jsni.append(jsniCode); jsni.append("\n"); } jsniCode = content[line].substring(0, content[line].indexOf("}-*/")).trim(); if (!StringUtils.isEmpty(jsniCode)) { jsni.append(jsniCode); jsni.append("\n"); } if (!StringUtils.isEmpty(jsni)) { jsni.deleteCharAt(jsni.length() - 1); } String mergedCode = parseJSNI(jsni.toString()); for (String s : mergedCode.split("\\n")) { printIndent().print(s).println(); } endIndent().printIndent().print("}"); } else { print(";"); } } else { if (getScope().interfaceScope) { if (!methodDecl.mods.getFlags().contains(Modifier.STATIC)) { report(methodDecl, methodDecl.name, JSweetProblem.INVALID_METHOD_BODY_IN_INTERFACE, methodDecl.name, parent == null ? "<no class>" : parent.name); } } if (getScope().declareClassScope) { if (!constructor || (methodDecl.getBody() != null && methodDecl.getBody().getStatements().isEmpty())) { report(methodDecl, methodDecl.name, JSweetProblem.INVALID_METHOD_BODY_IN_INTERFACE, methodDecl.name, parent == null ? "<no class>" : parent.name); } print(";"); } else { if (inCoreWrongOverload) { print(" {").println().startIndent().printIndent(); boolean wasPrinted = false; for (i = 0; i < overload.methods.size(); i++) { JCMethodDecl method = overload.methods.get(i); if (context.isInterface((ClassSymbol) method.sym.getEnclosingElement()) && !method.getModifiers().getFlags().contains(Modifier.DEFAULT)) { continue; } if (!Util.isParent(parent.sym, (ClassSymbol) method.sym.getEnclosingElement())) { continue; } if (wasPrinted) { print(" else "); } wasPrinted = true; print("if("); printMethodParamsTest(overload, method); print(") "); if (method.sym.isConstructor()) { printInlinedMethod(overload, method, methodDecl.getParameters()); } else { if (parent.sym != method.sym.getEnclosingElement() && context.getOverload((ClassSymbol) method.sym.getEnclosingElement(), method.sym).coreMethod == method) { print("{").println().startIndent().printIndent(); String tsMethodAccess = getTSMemberAccess(getTSMethodName(methodDecl), true); print("super" + tsMethodAccess); print("("); for (int j = 0; j < method.getParameters().size(); j++) { print(avoidJSKeyword( overload.coreMethod.getParameters().get(j).name.toString())) .print(", "); } if (!method.getParameters().isEmpty()) { removeLastChars(2); } print(");"); println().endIndent().printIndent().print("}"); } else { print("{").println().startIndent().printIndent(); // temporary cast to any because of Java // generics // bug print("return <any>"); if (method.sym.isStatic()) { print(getQualifiedTypeName(parent.sym, false).toString()); } else { print("this"); } print(".").print(getOverloadMethodName(method.sym)).print("("); for (int j = 0; j < method.getParameters().size(); j++) { print(avoidJSKeyword( overload.coreMethod.getParameters().get(j).name.toString())) .print(", "); } if (!method.getParameters().isEmpty()) { removeLastChars(2); } print(");"); println().endIndent().printIndent().print("}"); } } } print(" else throw new Error('invalid overload');"); endIndent().println().printIndent().print("}"); } else { print(" ").print("{").println().startIndent(); String replacedBody = null; if (context.hasAnnotationType(methodDecl.sym, JSweetConfig.ANNOTATION_REPLACE)) { replacedBody = (String) context.getAnnotationValue(methodDecl.sym, JSweetConfig.ANNOTATION_REPLACE, null); } int position = getCurrentPosition(); if (replacedBody == null || replacedBody.contains(BODY_MARKER)) { 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); } exit(); if (replacedBody != null) { String orgBody = getOutput().substring(position); removeLastChars(getCurrentPosition() - position); replacedBody = replacedBody.replace(BODY_MARKER, orgBody) .replace(BASE_INDENT_MARKER, getIndentString()).replace(INDENT_MARKER, INDENT) .replace(METHOD_NAME_MARKER, methodDecl.getName().toString()) .replace(CLASS_NAME_MARKER, parent.sym.getQualifiedName().toString()); } } if (replacedBody != null) { printIndent().print(replacedBody).println(); } endIndent().printIndent().print("}"); } } } } protected void printMethodModifiers(JCMethodDecl methodDecl, JCClassDecl parent, boolean constructor, boolean inOverload, Overload overload) { if (methodDecl.mods.getFlags().contains(Modifier.PUBLIC) || (inOverload && overload.coreMethod.equals(methodDecl))) { if (!getScope().interfaceScope) { print("public "); } } if (methodDecl.mods.getFlags().contains(Modifier.PRIVATE)) { if (!constructor) { if (!getScope().innerClass) { if (!getScope().interfaceScope) { if (!(inOverload && overload.coreMethod.equals(methodDecl) || getScope().hasInnerClass)) { print("private "); } } else { if (!(inOverload && overload.coreMethod.equals(methodDecl))) { report(methodDecl, methodDecl.name, JSweetProblem.INVALID_PRIVATE_IN_INTERFACE, methodDecl.name, parent == null ? "<no class>" : parent.name); } } } } } if (methodDecl.mods.getFlags().contains(Modifier.STATIC)) { if (!getScope().interfaceScope) { print("static "); } } if (methodDecl.mods.getFlags().contains(Modifier.ABSTRACT)) { if (!getScope().interfaceScope && !inOverload) { print("abstract "); } } } protected void printVariableInitialization(JCClassDecl clazz, JCVariableDecl var) { String name = var.getName().toString(); if (context.getFieldNameMapping(var.sym) != null) { name = context.getFieldNameMapping(var.sym); } else { name = getAdapter().getIdentifier(var.sym); } if (getScope().innerClassNotStatic && !Util.isConstantOrNullField(var)) { if (doesMemberNameRequireQuotes(name)) { printIndent().print("this['").print(name).print("'] = "); } else { printIndent().print("this.").print(name).print(" = "); } if (!substituteAssignedExpression(var.type, var.init)) { print(var.init); } print(";").println(); } else if (var.init == null) { if (doesMemberNameRequireQuotes(name)) { printIndent().print("this['").print(name).print("'] = ").print(Util.getTypeInitialValue(var.type)) .print(";").println(); } else { 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) { printIndent().print("this." + PARENT_CLASS_FIELD_NAME + " = " + PARENT_CLASS_FIELD_NAME + ";") .println(); } for (JCTree member : clazz.defs) { if (member instanceof JCVariableDecl) { JCVariableDecl var = (JCVariableDecl) member; if (!var.sym.isStatic() && !context.hasAnnotationType(var.sym, JSweetConfig.ANNOTATION_ERASED)) { printVariableInitialization(clazz, var); } } else if (member instanceof JCBlock) { JCBlock block = (JCBlock) member; if (!block.isStatic()) { printIndent().print("(() => {").startIndent().println(); printBlockStatements(block.stats); endIndent().printIndent().print("})();").println(); } } } } } private String parseJSNI(String jsniCode) { return jsniCode.replaceAll("@[^:]*::[\\n]?([a-zA-Z_$][a-zA-Z\\d_$]*)[\\n]?\\([^)]*\\)", "$1") .replaceAll("@[^:]*::\\n?([a-zA-Z_$][a-zA-Z\\d_$]*)", "$1"); } private void printInlinedMethod(Overload overload, JCMethodDecl method, List<? extends JCTree> args) { print("{").println().startIndent(); printIndent().print(VAR_DECL_KEYWORD + " __args = Array.prototype.slice.call(arguments);").println(); for (int j = 0; j < method.getParameters().size(); j++) { if (args.get(j) instanceof JCVariableDecl) { if (method.getParameters().get(j).name.equals(((JCVariableDecl) args.get(j)).name)) { continue; } else { printIndent().print(VAR_DECL_KEYWORD + " ") .print(avoidJSKeyword(method.getParameters().get(j).name.toString())).print(" : ") .print("any").print(Util.isVarargs(method.getParameters().get(j)) ? "[]" : "") .print(" = ").print("__args[" + j + "]").print(";").println(); } } else { if (method.getParameters().get(j).name.toString().equals(args.get(j).toString())) { continue; } else { getScope().inlinedConstructorArgs = method.getParameters().stream() .map(p -> p.sym.name.toString()).collect(Collectors.toList()); printIndent().print(VAR_DECL_KEYWORD + " ") .print(avoidJSKeyword(method.getParameters().get(j).name.toString())).print(" : ") .print("any").print(Util.isVarargs(method.getParameters().get(j)) ? "[]" : "") .print(" = ").print(args.get(j)).print(";").println(); getScope().inlinedConstructorArgs = null; } } } if (method.getBody() != null) { boolean skipFirst = false; boolean initialized = false; if (!method.getBody().stats.isEmpty() && method.getBody().stats.get(0).toString().startsWith("this(")) { skipFirst = true; JCMethodInvocation inv = (JCMethodInvocation) ((JCExpressionStatement) method.getBody().stats .get(0)).expr; MethodSymbol ms = Util.findMethodDeclarationInType(context.types, (TypeSymbol) overload.coreMethod.sym.getEnclosingElement(), inv); for (JCMethodDecl md : overload.methods) { if (md.sym.equals(ms)) { printIndent(); initialized = true; printInlinedMethod(overload, md, inv.args); println(); } } } String replacedBody = null; if (context.hasAnnotationType(method.sym, JSweetConfig.ANNOTATION_REPLACE)) { replacedBody = (String) context.getAnnotationValue(method.sym, JSweetConfig.ANNOTATION_REPLACE, null); } int position = getCurrentPosition(); if (replacedBody == null || replacedBody.contains(BODY_MARKER)) { 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(); if (replacedBody != null) { getIndent(); printIndent(); String orgBody = getOutput().substring(position); removeLastChars(getCurrentPosition() - position); replacedBody = replacedBody.replace(BODY_MARKER, orgBody) .replace(BASE_INDENT_MARKER, getIndentString()).replace(INDENT_MARKER, INDENT) .replace(METHOD_NAME_MARKER, method.getName().toString()).replace(CLASS_NAME_MARKER, method.sym.getEnclosingElement().getQualifiedName().toString()); } } if (replacedBody != null) { printIndent().print(replacedBody).println(); } } else { String returnValue = Util.getTypeInitialValue(method.sym.getReturnType()); if (returnValue != null) { print(" return ").print(returnValue).print("; "); } } endIndent().printIndent().print("}"); } private void printFieldInitializations() { JCClassDecl clazz = getParent(JCClassDecl.class); for (JCTree t : clazz.getMembers()) { if (t instanceof JCVariableDecl && !getScope().fieldsWithInitializers.contains(t)) { JCVariableDecl field = (JCVariableDecl) t; if (!field.sym.isStatic() && !context.hasAnnotationType(field.sym, JSweetConfig.ANNOTATION_ERASED)) { String name = getAdapter().getIdentifier(field.sym); if (context.getFieldNameMapping(field.sym) != null) { name = context.getFieldNameMapping(field.sym); } printIndent().print("this.").print(name).print(" = ") .print(Util.getTypeInitialValue(field.type)).print(";").println(); } } } for (JCVariableDecl field : getScope().fieldsWithInitializers) { if (context.hasAnnotationType(field.sym, JSweetConfig.ANNOTATION_ERASED)) { continue; } String name = getAdapter().getIdentifier(field.sym); if (context.getFieldNameMapping(field.sym) != null) { name = context.getFieldNameMapping(field.sym); } printIndent().print("this.").print(name).print(" = "); if (!substituteAssignedExpression(field.type, field.init)) { print(field.init); } print(";").println(); } } private void printBlockStatements(List<JCStatement> statements) { for (JCStatement statement : statements) { if (context.options.isDebugMode()) { JCMethodDecl methodDecl = getParent(JCMethodDecl.class); if (isDebugMode(methodDecl)) { int s = statement.getStartPosition(); int e = statement.getEndPosition(diagnosticSource.getEndPosTable()); if (e == -1) { e = s; } printIndent().print("yield { row: ").print("" + diagnosticSource.getLineNumber(s)) .print(", column: " + diagnosticSource.getColumnNumber(s, false)) .print(", statement: \""); print(StringEscapeUtils.escapeJson(statement.toString())).print("\""); final Stack<List<String>> locals = new Stack<>(); try { new TreeScanner() { public void scan(JCTree tree) { if (tree == statement) { throw new RuntimeException(); } boolean contextChange = false; if (tree instanceof JCBlock || tree instanceof JCEnhancedForLoop || tree instanceof JCLambda || tree instanceof JCForLoop || tree instanceof JCDoWhileLoop) { locals.push(new ArrayList<>()); contextChange = true; } if (tree instanceof JCVariableDecl) { locals.peek().add(((JCVariableDecl) tree).name.toString()); } super.scan(tree); if (contextChange) { locals.pop(); } } }.scan(methodDecl.body); } catch (Exception end) { // swallow } List<String> accessibleLocals = new ArrayList<>(); for (List<String> l : locals) { accessibleLocals.addAll(l); } if (!accessibleLocals.isEmpty()) { print(", locals: "); print("{"); for (String local : accessibleLocals) { print("" + local + ": " + local + ", "); } removeLastChars(2); print("}"); } print(" };").println(); } } printBlockStatement(statement); } } private void printBlockStatement(JCStatement statement) { printIndent(); int pos = getCurrentPosition(); print(statement); if (getCurrentPosition() == pos) { removeLastIndent(); return; } if (!statementsWithNoSemis.contains(statement.getClass())) { if (statement instanceof JCLabeledStatement) { if (!statementsWithNoSemis.contains(((JCLabeledStatement) statement).body.getClass())) { print(";"); } } else { print(";"); } } println(); } private String getOverloadMethodName(MethodSymbol method) { if (method.isConstructor()) { return "constructor"; } StringBuilder sb = new StringBuilder(method.getSimpleName().toString()); sb.append("$"); for (VarSymbol p : method.getParameters()) { sb.append(context.types.erasure(p.type).toString().replace('.', '_').replace("[]", "_A")); sb.append("$"); } if (!method.getParameters().isEmpty()) { sb.deleteCharAt(sb.length() - 1); } return sb.toString(); } private void printMethodParamsTest(Overload overload, JCMethodDecl m) { int i = 0; for (; i < m.getParameters().size(); i++) { print("("); printInstanceOf(avoidJSKeyword(overload.coreMethod.getParameters().get(i).name.toString()), null, m.getParameters().get(i).type); print(" || ") .print(avoidJSKeyword(overload.coreMethod.getParameters().get(i).name.toString()) + " === null") .print(")"); print(" && "); } for (; i < overload.coreMethod.getParameters().size(); i++) { print(avoidJSKeyword(overload.coreMethod.getParameters().get(i).name.toString())) .print(" === undefined"); print(" && "); } removeLastChars(4); } @Override public void visitBlock(JCBlock block) { JCTree parent = getParent(); boolean globals = (parent instanceof JCClassDecl) && JSweetConfig.GLOBALS_CLASS_NAME.equals(((JCClassDecl) parent).name.toString()); boolean initializer = (parent instanceof JCClassDecl) && !globals; int static_i = 0; if (initializer) { if (getScope().interfaceScope) { report(block, JSweetProblem.INVALID_INITIALIZER_IN_INTERFACE, ((JCClassDecl) parent).name); } if (!block.isStatic()) { // non-static blocks are initialized in the constructor return; } for (JCTree m : ((JCClassDecl) parent).getMembers()) { if (m instanceof JCBlock) { if (((JCBlock) m).isStatic()) { if (block == m) { print("static __static_initializer_" + static_i + "() "); break; } static_i++; } } } } if (!globals) { print("{").println().startIndent(); } printBlockStatements(block.stats); if (!globals) { endIndent().printIndent().print("}"); } } private String avoidJSKeyword(String name) { if (JSweetConfig.JS_KEYWORDS.contains(name)) { name = JSweetConfig.JS_KEYWORD_PREFIX + name; } return name; } @Override public void visitVarDef(JCVariableDecl varDecl) { if (context.hasAnnotationType(varDecl.sym, JSweetConfig.ANNOTATION_ERASED)) { // erased elements are ignored return; } if (context.hasAnnotationType(varDecl.sym, JSweetConfig.ANNOTATION_STRING_TYPE)) { // string type fields are ignored return; } if (getScope().enumScope) { print(varDecl.name.toString()); if (varDecl.init instanceof JCNewClass) { JCNewClass newClass = (JCNewClass) varDecl.init; if (newClass.def != null) { initAnonymousClass(newClass); } } } else { JCTree parent = getParent(); if (getScope().enumWrapperClassScope && varDecl.type.equals(parent.type)) { return; } String name = getAdapter().getIdentifier(varDecl.sym); if (context.getFieldNameMapping(varDecl.sym) != null) { name = context.getFieldNameMapping(varDecl.sym); } boolean confictInDefinitionScope = false; if (parent instanceof JCClassDecl) { MethodSymbol m = Util.findMethodDeclarationInType(context.types, ((JCClassDecl) parent).sym, name, null); if (m != null) { if (!isDefinitionScope) { report(varDecl, varDecl.name, JSweetProblem.FIELD_CONFLICTS_METHOD, name, m.owner); } else { confictInDefinitionScope = true; } } if (!getScope().interfaceScope && name.equals("constructor")) { report(varDecl, varDecl.name, JSweetProblem.CONSTRUCTOR_MEMBER); } } else { if (context.bundleMode) { if (context.importedTopPackages.contains(name)) { name = "__var_" + name; } } if (JSweetConfig.JS_KEYWORDS.contains(name)) { report(varDecl, varDecl.name, JSweetProblem.JS_KEYWORD_CONFLICT, name, name); name = JSweetConfig.JS_KEYWORD_PREFIX + name; } } boolean globals = (parent instanceof JCClassDecl) && JSweetConfig.GLOBALS_CLASS_NAME.equals(((JCClassDecl) parent).name.toString()); if (globals && !varDecl.mods.getFlags().contains(Modifier.STATIC)) { report(varDecl, varDecl.name, JSweetProblem.GLOBALS_CAN_ONLY_HAVE_STATIC_MEMBERS); return; } globals = globals || (parent instanceof JCClassDecl && (((JCClassDecl) parent).sym.isInterface() || getScope().interfaceScope && varDecl.sym.isStatic())); if (parent instanceof JCClassDecl) { printDocComment(varDecl, false); } if (!globals && parent instanceof JCClassDecl) { if (varDecl.mods.getFlags().contains(Modifier.PUBLIC)) { if (!getScope().interfaceScope) { print("public "); } } if (varDecl.mods.getFlags().contains(Modifier.PRIVATE)) { if (!getScope().interfaceScope) { if (!getScope().innerClass && !varDecl.mods.getFlags().contains(Modifier.STATIC)) { // cannot keep private fields because they may be // accessed in an inner class print("/*private*/ "); } } else { report(varDecl, varDecl.name, JSweetProblem.INVALID_PRIVATE_IN_INTERFACE, varDecl.name, ((JCClassDecl) parent).name); } } if (varDecl.mods.getFlags().contains(Modifier.STATIC)) { if (!getScope().interfaceScope) { print("static "); } } } if (!getScope().interfaceScope && parent instanceof JCClassDecl) { if (context.hasAnnotationType(varDecl.sym, JSweetConfig.ANNOTATION_OPTIONAL)) { report(varDecl, varDecl.name, JSweetProblem.USELESS_OPTIONAL_ANNOTATION, varDecl.name, ((JCClassDecl) parent).name); } } boolean ambient = context.hasAnnotationType(varDecl.sym, JSweetConfig.ANNOTATION_AMBIENT); if (globals || !(parent instanceof JCClassDecl || parent instanceof JCMethodDecl || parent instanceof JCLambda)) { if (globals) { if (context.hasAnnotationType(varDecl.sym, JSweetConfig.ANNOTATION_MODULE)) { getContext().addExportedElement( context.getAnnotationValue(varDecl.sym, JSweetConfig.ANNOTATION_MODULE, null), varDecl.sym, getCompilationUnit()); } if (context.useModules) { if (!varDecl.mods.getFlags().contains(Modifier.PRIVATE)) { print("export "); } } else { if (!isTopLevelScope()) { print("export "); } } if (ambient || (isTopLevelScope() && isDefinitionScope)) { print("declare "); } } if (!(inArgListTail && (parent instanceof JCForLoop))) { if (isDefinitionScope) { print("var "); } else { print(VAR_DECL_KEYWORD + " "); } } } else { if (ambient) { report(varDecl, varDecl.name, JSweetProblem.WRONG_USE_OF_AMBIENT, varDecl.name); } } if (Util.isVarargs(varDecl)) { print("..."); } if (doesMemberNameRequireQuotes(name)) { print("'" + name + "'"); } else { print(name); } if (!Util.isVarargs(varDecl) && (getScope().eraseVariableTypes || (getScope().interfaceScope && context.hasAnnotationType(varDecl.sym, JSweetConfig.ANNOTATION_OPTIONAL)))) { print("?"); } if (!getScope().skipTypeAnnotations && !getScope().enumWrapperClassScope) { if (typeChecker.checkType(varDecl, varDecl.name, varDecl.vartype)) { print(" : "); if (confictInDefinitionScope) { print("any"); } else { if (getScope().eraseVariableTypes) { print("any"); if (Util.isVarargs(varDecl)) { print("[]"); } } else { if (context.hasAnnotationType(varDecl.vartype.type.tsym, ANNOTATION_STRING_TYPE)) { print("\""); print(context.getAnnotationValue(varDecl.vartype.type.tsym, ANNOTATION_STRING_TYPE, varDecl.vartype.type.tsym.name.toString()).toString()); print("\""); } else { substituteAndPrintType(varDecl.vartype); } } } } } if (context.lazyInitializedStatics.contains(varDecl.sym) && !getScope().enumWrapperClassScope) { JCClassDecl clazz = (JCClassDecl) parent; String prefix = clazz.getSimpleName().toString(); if (GLOBALS_CLASS_NAME.equals(prefix)) { prefix = ""; } else { prefix += "."; } print("; "); if (globals) { if (!isTopLevelScope()) { print("export "); } print("function "); } else { print("public static "); } print(name).print(STATIC_INITIALIZATION_SUFFIX + "() : "); substituteAndPrintType(varDecl.vartype); print(" { "); int liCount = context.getStaticInitializerCount(clazz.sym); if (liCount > 0) { if (!globals) { print(prefix + "__static_initialize(); "); } } if (varDecl.init != null && !isDefinitionScope) { print("if(" + prefix).print(name).print(" == null) ").print(prefix).print(name).print(" = "); if (getScope().enumWrapperClassScope) { JCNewClass newClass = (JCNewClass) varDecl.init; print("new ").print(clazz.getSimpleName().toString()).print("(") .printArgList(null, newClass.args).print(")"); } else { if (!substituteAssignedExpression(varDecl.type, varDecl.init)) { print(varDecl.init); } } print("; "); } print("return ").print(prefix).print(name).print("; }"); if (!globals) { String qualifiedClassName = getQualifiedTypeName(clazz.sym, globals); context.addTopFooterStatement((isBlank(qualifiedClassName) ? "" : qualifiedClassName + ".") + name + STATIC_INITIALIZATION_SUFFIX + "();"); } } else { if (varDecl.init != null && !isDefinitionScope) { if (!(parent instanceof JCClassDecl && getScope().innerClassNotStatic && !varDecl.sym.isStatic() && !Util.isConstantOrNullField(varDecl))) { if (!globals && parent instanceof JCClassDecl && getScope().interfaceScope) { report(varDecl, varDecl.name, JSweetProblem.INVALID_FIELD_INITIALIZER_IN_INTERFACE, varDecl.name, ((JCClassDecl) parent).name); } else { if (!(getScope().hasConstructorOverloadWithSuperClass && getScope().fieldsWithInitializers.contains(varDecl))) { print(" = "); if (!substituteAssignedExpression(varDecl.type, varDecl.init)) { print(varDecl.init); } } } } } if (!(ambient || (isTopLevelScope() && isDefinitionScope)) && varDecl.sym.isStatic() && varDecl.init == null) { print(" = ").print(Util.getTypeInitialValue(varDecl.sym.type)); } } } } private String getTSMemberAccess(String memberName, boolean hasSelector) { if (doesMemberNameRequireQuotes(memberName)) { // TODO : hasSelector should not be false by now for member with // special chars for now but we should handle node case (window // isn't something) => replace with global context return (hasSelector ? "" : "window") + "['" + memberName + "']"; } else { return (hasSelector ? "." : "") + memberName; } } private boolean doesMemberNameRequireQuotes(String name) { for (char c : name.toCharArray()) { if (TS_IDENTIFIER_FORBIDDEN_CHARS.contains(c)) { return true; } } return false; } @Override public void visitParens(JCParens parens) { print("("); super.visitParens(parens); print(")"); } @Override public void visitImport(JCImport importDecl) { String qualId = importDecl.getQualifiedIdentifier().toString(); if (qualId.endsWith("*") && !qualId.endsWith("." + JSweetConfig.GLOBALS_CLASS_NAME + ".*")) { report(importDecl, JSweetProblem.WILDCARD_IMPORT); return; } String adaptedQualId = getAdapter().needsImport(new ImportElementSupport(importDecl), qualId); if (adaptedQualId != null && adaptedQualId.contains(".")) { if (importDecl.isStatic() && !qualId.contains("." + JSweetConfig.GLOBALS_CLASS_NAME + ".") && !qualId.contains("." + JSweetConfig.STRING_TYPES_INTERFACE_NAME + ".")) { if (!context.bundleMode) { print(VAR_DECL_KEYWORD + " ").print(qualId.substring(qualId.lastIndexOf('.') + 1)) .print(": any = ").print(qualId).print(";"); } } else { String[] namePath; if (context.useModules && importDecl.isStatic()) { namePath = qualId.split("\\."); } else { namePath = adaptedQualId.split("\\."); } String name = namePath[namePath.length - 1]; if (context.useModules) { if (!adaptedQualId.startsWith(GLOBALS_PACKAGE_NAME)) { if (!context.getImportedNames(compilationUnit.getSourceFile().getName()).contains(name)) { print("import ").print(name).print(" = ").print(adaptedQualId).print(";"); context.registerImportedName(compilationUnit.getSourceFile().getName(), null, name); } } } else { if (topLevelPackage == null) { if (context.globalImports.contains(name)) { // Tsc global package does allow multiple import // with // the same name in the global namespace (bug?) return; } context.globalImports.add(name); } if (context.bundleMode) { // in bundle mode, we do not use imports to minimize // dependencies // (imports create unavoidable dependencies!) context.importedTopPackages.add(namePath[0]); } else { print("import ").print(name).print(" = ").print(adaptedQualId).print(";"); } } } } } @Override public void visitSelect(JCFieldAccess fieldAccess) { if (!getAdapter().substitute(ExtendedElementFactory.INSTANCE.create(fieldAccess))) { if (fieldAccess.selected.type.tsym instanceof PackageSymbol) { if (context.isRootPackage(fieldAccess.selected.type.tsym)) { if (fieldAccess.type != null && fieldAccess.type.tsym != null) { printIdentifier(fieldAccess.type.tsym); } else { // TODO: see if it breaks something print(fieldAccess.name.toString()); } return; } } if ("class".equals(fieldAccess.name.toString())) { if (fieldAccess.type instanceof Type.ClassType && context.isInterface(((Type.ClassType) fieldAccess.type).typarams_field.head.tsym)) { print("\"").print(context .getRootRelativeJavaName(((Type.ClassType) fieldAccess.type).typarams_field.head.tsym)) .print("\""); } else { print(fieldAccess.selected); } } else if ("this".equals(fieldAccess.name.toString()) && getScope().innerClassNotStatic) { print("this." + PARENT_CLASS_FIELD_NAME); } else if ("this".equals(fieldAccess.name.toString())) { print("this"); } else { String selected = fieldAccess.selected.toString(); if (!selected.equals(GLOBALS_CLASS_NAME)) { if (selected.equals("super") && (fieldAccess.sym instanceof VarSymbol)) { print("this."); } else { boolean accessSubstituted = false; if (fieldAccess.sym instanceof VarSymbol) { VarSymbol varSym = (VarSymbol) fieldAccess.sym; if (varSym.isStatic() && varSym.owner.isInterface() && varSym.owner != Util.getSymbol(fieldAccess.selected)) { accessSubstituted = true; if (context.useModules) { // TODO: we assume it has been imported, but // it is clearly not always the case (to be // tested) print(varSym.owner.getSimpleName().toString()).print("."); } else { print(context.getRootRelativeName(null, varSym.owner)).print("."); } } } if (!accessSubstituted) { print(fieldAccess.selected).print("."); } } } String fieldName = null; if (fieldAccess.sym instanceof VarSymbol && context.getFieldNameMapping(fieldAccess.sym) != null) { fieldName = context.getFieldNameMapping(fieldAccess.sym); } else { fieldName = getAdapter().getIdentifier(fieldAccess.sym); } if (doesMemberNameRequireQuotes(fieldName)) { if (getLastPrintedChar() == '.') { removeLastChar(); print("['").print(fieldName).print("']"); } else { print("this['").print(fieldName).print("']"); } } else { print(fieldName); } if (fieldAccess.sym instanceof VarSymbol && !fieldAccess.sym.owner.isEnum() && context.lazyInitializedStatics.contains(fieldAccess.sym)) { if (!staticInitializedAssignment) { print(STATIC_INITIALIZATION_SUFFIX + "()"); } } } } } private JCImport getStaticGlobalImport(String methName) { if (getCompilationUnit() == null) { return null; } for (JCImport i : getCompilationUnit().getImports()) { if (i.staticImport) { if (i.qualid.toString().endsWith(JSweetConfig.GLOBALS_CLASS_NAME + "." + methName)) { return i; } } } return null; } private String getStaticContainerFullName(JCImport importDecl) { if (importDecl.getQualifiedIdentifier() instanceof JCFieldAccess) { JCFieldAccess fa = (JCFieldAccess) importDecl.getQualifiedIdentifier(); String name = context.getRootRelativeJavaName(fa.selected.type.tsym); // function is a top-level global function (no need to import) if (JSweetConfig.GLOBALS_CLASS_NAME.equals(name)) { return null; } boolean globals = name.endsWith("." + JSweetConfig.GLOBALS_CLASS_NAME); if (globals) { name = name.substring(0, name.length() - JSweetConfig.GLOBALS_CLASS_NAME.length() - 1); } // function belong to the current package (no need to import) if (compilationUnit.packge.getQualifiedName().toString().startsWith(name)) { return null; } return name; } return null; } private long applyTargetRefCounter = 0; @Override public void visitApply(JCMethodInvocation inv) { boolean debugMode = false; if (context.options.isDebugMode()) { if (Util.getSymbol(inv.meth) instanceof MethodSymbol) { MethodSymbol methodSymbol = (MethodSymbol) Util.getSymbol(inv.meth); if (!methodSymbol.isConstructor() && Util.isSourceElement(methodSymbol)) { debugMode = true; } } } if (debugMode) { print("__debug_result(yield "); } if (!getAdapter().substituteMethodInvocation(new MethodInvocationElementSupport(inv))) { String meth = inv.meth.toString(); String methName = meth.substring(meth.lastIndexOf('.') + 1); if (methName.equals("super") && getScope().removedSuperclass) { return; } boolean applyVarargs = true; if (JSweetConfig.NEW_FUNCTION_NAME.equals(methName)) { print("new "); applyVarargs = false; } boolean anonymous = JSweetConfig.ANONYMOUS_FUNCTION_NAME.equals(methName) || JSweetConfig.ANONYMOUS_STATIC_FUNCTION_NAME.equals(methName) || JSweetConfig.NEW_FUNCTION_NAME.equals(methName); boolean targetIsThisOrStaticImported = meth.equals(methName) || meth.equals("this." + methName); MethodType type = inv.meth.type instanceof MethodType ? (MethodType) inv.meth.type : null; MethodSymbol methSym = null; String methodName = null; boolean keywordHandled = false; if (targetIsThisOrStaticImported) { JCImport staticImport = getStaticGlobalImport(methName); if (staticImport == null) { JCClassDecl p = getParent(JCClassDecl.class); methSym = p == null ? null : Util.findMethodDeclarationInType(context.types, p.sym, methName, type); if (methSym != null) { typeChecker.checkApply(inv, methSym); if (!methSym.isStatic()) { if (!meth.startsWith("this.")) { print("this"); if (!anonymous) { print("."); } } } else { if (meth.startsWith("this.") && methSym.isStatic()) { report(inv, JSweetProblem.CANNOT_ACCESS_STATIC_MEMBER_ON_THIS, methSym.getSimpleName()); } if (!JSweetConfig.GLOBALS_CLASS_NAME.equals(methSym.owner.getSimpleName().toString())) { print("" + methSym.owner.getSimpleName()); if (methSym.owner.isEnum()) { print(ENUM_WRAPPER_CLASS_SUFFIX); } if (!anonymous) { print("."); } } } } else { if (getScope().defaultMethodScope) { TypeSymbol target = Util.getStaticImportTarget( getContext().getDefaultMethodCompilationUnit(getParent(JCMethodDecl.class)), methName); if (target != null) { print(getRootRelativeName(target) + "."); } } else { TypeSymbol target = Util.getStaticImportTarget(compilationUnit, methName); if (target != null) { print(getRootRelativeName(target) + "."); } } if (getScope().innerClass) { JCClassDecl parent = getParent(JCClassDecl.class); int level = 0; MethodSymbol method = null; if (parent != null) { while (getScope(level++).innerClass) { parent = getParent(JCClassDecl.class, parent); if ((method = Util.findMethodDeclarationInType(context.types, parent.sym, methName, type)) != null) { break; } } } if (method != null) { if (method.isStatic()) { print(method.getEnclosingElement().getSimpleName().toString() + "."); } else { print("this."); for (int i = 0; i < level; i++) { print(PARENT_CLASS_FIELD_NAME + "."); } if (anonymous) { removeLastChar(); } } } } } } else { JCFieldAccess staticFieldAccess = (JCFieldAccess) staticImport.qualid; methSym = Util.findMethodDeclarationInType(context.types, staticFieldAccess.selected.type.tsym, methName, type); if (methSym != null) { Map<String, VarSymbol> vars = new HashMap<>(); Util.fillAllVariablesInScope(vars, getStack(), inv, getParent(JCMethodDecl.class)); if (vars.containsKey(methSym.getSimpleName().toString())) { report(inv, JSweetProblem.HIDDEN_INVOCATION, methSym.getSimpleName()); } if (context.bundleMode && methSym.owner.getSimpleName().toString().equals(GLOBALS_CLASS_NAME) && methSym.owner.owner != null && !methSym.owner.owner.getSimpleName().toString().equals(GLOBALS_PACKAGE_NAME)) { String prefix = getRootRelativeName(methSym.owner.owner); if (!StringUtils.isEmpty(prefix)) { print(getRootRelativeName(methSym.owner.owner) + "."); } } } if (JSweetConfig.TS_STRICT_MODE_KEYWORDS.contains(context.getActualName(methSym))) { String targetClass = getStaticContainerFullName(staticImport); if (!isBlank(targetClass)) { print(targetClass); print("."); keywordHandled = true; } if (JSweetConfig.isLibPath(methSym.getEnclosingElement().getQualifiedName().toString())) { methodName = methName.toLowerCase(); } } } } else { if (inv.meth instanceof JCFieldAccess) { JCExpression selected = ((JCFieldAccess) inv.meth).selected; if (context.isFunctionalType(selected.type.tsym)) { anonymous = true; } methSym = Util.findMethodDeclarationInType(context.types, selected.type.tsym, methName, type); if (methSym != null) { typeChecker.checkApply(inv, methSym); } } } boolean isStatic = methSym == null || methSym.isStatic(); if (!Util.hasVarargs(methSym) // || !inv.args.isEmpty() && (inv.args.last().type.getKind() != TypeKind.ARRAY // we dont use apply if var args type differ || !context.types.erasure(((ArrayType) inv.args.last().type).elemtype) .equals(context.types.erasure( ((ArrayType) methSym.getParameters().last().type).elemtype)))) { applyVarargs = false; } String targetVarName = null; if (anonymous) { if (inv.meth instanceof JCFieldAccess) { JCExpression selected = ((JCFieldAccess) inv.meth).selected; print(selected); } } else { // method with name if (inv.meth instanceof JCFieldAccess && applyVarargs && !targetIsThisOrStaticImported && !isStatic) { targetVarName = "this['__jswref_" + (applyTargetRefCounter++) + "']"; print("("); print(targetVarName + " = "); print(((JCFieldAccess) inv.meth).selected); print(")"); String accessedMemberName; if (keywordHandled) { accessedMemberName = ((JCFieldAccess) inv.meth).name.toString(); } else { if (methSym == null) { methSym = (MethodSymbol) ((JCFieldAccess) inv.meth).sym; } if (methSym != null) { accessedMemberName = context.getActualName(methSym); } else { accessedMemberName = ((JCFieldAccess) inv.meth).name.toString(); } } print(getTSMemberAccess(accessedMemberName, true)); } else if (methodName != null) { print(getTSMemberAccess(methodName, removeLastChar('.'))); } else { if (keywordHandled) { print(inv.meth); } else { if (methSym == null && inv.meth instanceof JCFieldAccess && ((JCFieldAccess) inv.meth).sym instanceof MethodSymbol) { methSym = (MethodSymbol) ((JCFieldAccess) inv.meth).sym; } if (methSym != null && inv.meth instanceof JCFieldAccess) { JCExpression selected = ((JCFieldAccess) inv.meth).selected; if (!GLOBALS_CLASS_NAME.equals(selected.type.tsym.getSimpleName().toString())) { print(selected).print("."); } else { if (context.useModules) { if (!((ClassSymbol) selected.type.tsym).sourcefile.getName() .equals(getCompilationUnit().sourcefile.getName())) { // TODO: when using several qualified // Globals classes, we // need to disambiguate (use qualified // name with // underscores) print(GLOBALS_CLASS_NAME).print("."); } } Map<String, VarSymbol> vars = new HashMap<>(); Util.fillAllVariablesInScope(vars, getStack(), inv, getParent(JCMethodDecl.class)); if (vars.containsKey(methName)) { report(inv, JSweetProblem.HIDDEN_INVOCATION, methName); } } } if (methSym != null) { if (context.isInvalidOverload(methSym) && !methSym.getParameters().isEmpty() && !Util.hasTypeParameters(methSym) && !Util.hasVarargs(methSym) && getParent(JCMethodDecl.class) != null && !getParent(JCMethodDecl.class).sym.isDefault()) { if (methSym.getEnclosingElement().isInterface()) { removeLastChar('.'); print("['" + getOverloadMethodName(methSym) + "']"); } else { print(getOverloadMethodName(methSym)); } } else { print(getTSMemberAccess(context.getActualName(methSym), removeLastChar('.'))); } } else { print(inv.meth); } } } } if (applyVarargs) { print(".apply"); } else { if (inv.typeargs != null && !inv.typeargs.isEmpty()) { print("<"); for (JCExpression argument : inv.typeargs) { substituteAndPrintType(argument).print(","); } removeLastChar(); print(">"); } else { // force type arguments to any because they are inferred to // {}by default if (methSym != null && !methSym.getTypeParameters().isEmpty()) { ClassSymbol target = (ClassSymbol) methSym.getEnclosingElement(); if (!target.getQualifiedName().toString().startsWith(JSweetConfig.LIBS_PACKAGE + ".")) { // invalid overload type parameters are erased Overload overload = context.getOverload(target, methSym); boolean inOverload = overload != null && overload.methods.size() > 1; if (!(inOverload && !overload.isValid)) { printAnyTypeArguments(methSym.getTypeParameters().size()); } } } } } print("("); if (applyVarargs) { String contextVar = "null"; if (targetIsThisOrStaticImported) { contextVar = "this"; } else if (targetVarName != null) { contextVar = targetVarName; } print(contextVar + ", "); if (inv.args.size() > 1) { print("["); } } int argsLength = applyVarargs ? inv.args.size() - 1 : inv.args.size(); if (getScope().innerClassNotStatic && "super".equals(methName)) { TypeSymbol s = getParent(JCClassDecl.class).extending.type.tsym; if (s.getEnclosingElement() instanceof ClassSymbol && !s.isStatic()) { print(PARENT_CLASS_FIELD_NAME); if (argsLength > 0) { print(", "); } } } if (getScope().enumWrapperClassScope && isAnonymousClass() && "super".equals(methName)) { print(ENUM_WRAPPER_CLASS_ORDINAL + ", " + ENUM_WRAPPER_CLASS_NAME); if (argsLength > 0) { print(", "); } } for (int i = 0; i < argsLength; i++) { JCExpression arg = inv.args.get(i); if (inv.meth.type != null) { List<Type> argTypes = ((MethodType) inv.meth.type).argtypes; Type paramType = i < argTypes.size() ? argTypes.get(i) : argTypes.get(argTypes.size() - 1); if (!substituteAssignedExpression(paramType, arg)) { print(arg); } } else { // this should never happen but we fall back just in case print(arg); } if (i < argsLength - 1) { print(", "); } } if (applyVarargs) { if (inv.args.size() > 1) { // we cast array to any[] to avoid concat error on // different // types print("].concat(<any[]>"); } print(inv.args.last()); if (inv.args.size() > 1) { print(")"); } } print(")"); } if (debugMode) { print(")"); } } @Override public void visitIdent(JCIdent ident) { String name = ident.toString(); if (getScope().inlinedConstructorArgs != null) { if (ident.sym instanceof VarSymbol && getScope().inlinedConstructorArgs.contains(name)) { print("__args[" + getScope().inlinedConstructorArgs.indexOf(name) + "]"); return; } } if (!getAdapter().substitute(ExtendedElementFactory.INSTANCE.create(ident))) { boolean lazyInitializedStatic = false; // add this of class name if ident is a field if (ident.sym instanceof VarSymbol && !ident.sym.name.equals(context.names._this) && !ident.sym.name.equals(context.names._super)) { VarSymbol varSym = (VarSymbol) ident.sym; // findFieldDeclaration(currentClass, // ident.name); if (varSym != null) { if (varSym.owner instanceof ClassSymbol) { if (context.getFieldNameMapping(varSym) != null) { name = context.getFieldNameMapping(varSym); } else { name = getAdapter().getIdentifier(varSym); } if (!varSym.getModifiers().contains(Modifier.STATIC)) { print("this."); JCClassDecl parent = getParent(JCClassDecl.class); int level = 0; boolean foundInParent = false; while (getScope(level++).innerClassNotStatic) { parent = getParent(JCClassDecl.class, parent); if (parent != null && varSym.owner == parent.sym) { foundInParent = true; break; } } if (foundInParent) { for (int i = 0; i < level; i++) { print(PARENT_CLASS_FIELD_NAME + "."); } } } else { if (context.lazyInitializedStatics.contains(varSym)) { lazyInitializedStatic = true; } if (!varSym.owner.getQualifiedName().toString().endsWith("." + GLOBALS_CLASS_NAME)) { if (context.bundleMode && !varSym.owner.equals(getParent(JCClassDecl.class).sym)) { String prefix = context.getRootRelativeName(null, varSym.owner); if (!StringUtils.isEmpty(prefix)) { print(context.getRootRelativeName(null, varSym.owner) + "."); } } else { if (!varSym.owner.getSimpleName().toString().equals(GLOBALS_PACKAGE_NAME)) { print(varSym.owner.getSimpleName() + "."); } } } else { if (context.bundleMode) { String prefix = context.getRootRelativeName(null, varSym.owner); prefix = prefix.substring(0, prefix.length() - GLOBALS_CLASS_NAME.length()); if (!prefix.equals(GLOBALS_PACKAGE_NAME + ".") && !prefix.endsWith("." + GLOBALS_PACKAGE_NAME + ".")) { print(prefix); } } } } } else { if (varSym.owner instanceof MethodSymbol && isAnonymousClass() && getScope(1).finalVariables .get(getScope(1).anonymousClasses.indexOf(getParent(JCClassDecl.class))) .contains(varSym)) { print("this."); } else { if (context.bundleMode && varSym.owner instanceof MethodSymbol) { if (context.importedTopPackages.contains(name)) { name = "__var_" + name; } } if (JSweetConfig.JS_KEYWORDS.contains(name)) { name = JSweetConfig.JS_KEYWORD_PREFIX + name; } } } } } if (ident.sym instanceof ClassSymbol) { ClassSymbol clazz = (ClassSymbol) ident.sym; boolean prefixAdded = false; if (getScope().defaultMethodScope) { if (Util.isImported(getContext().getDefaultMethodCompilationUnit(getParent(JCMethodDecl.class)), clazz)) { print(getRootRelativeName(clazz.getEnclosingElement()) + "."); prefixAdded = true; } } // add parent class name if ident is an inner class of the // current class if (!prefixAdded && clazz.getEnclosingElement() instanceof ClassSymbol) { if (context.useModules) { print(clazz.getEnclosingElement().getSimpleName() + "."); prefixAdded = true; } else { // if the class has not been imported, we need to add // the containing class prefix if (!getCompilationUnit().getImports().stream() .map(i -> i.qualid.type == null ? null : i.qualid.type.tsym) .anyMatch(t -> t == clazz)) { print(clazz.getEnclosingElement().getSimpleName() + "."); prefixAdded = true; } } } if (!prefixAdded && context.bundleMode && !clazz.equals(getParent(JCClassDecl.class).sym)) { print(getRootRelativeName(clazz)); } else { print(name); } } else { if (doesMemberNameRequireQuotes(name)) { if (getLastPrintedChar() == '.') { removeLastChar(); print("['").print(name).print("']"); } else { print("this['").print(name).print("']"); } } else { print(name); } if (lazyInitializedStatic) { if (!staticInitializedAssignment) { print(STATIC_INITIALIZATION_SUFFIX + "()"); } } } } } @Override public void visitTypeApply(JCTypeApply typeApply) { substituteAndPrintType(typeApply); } private int initAnonymousClass(JCNewClass newClass) { int anonymousClassIndex = getScope().anonymousClasses.indexOf(newClass.def); if (anonymousClassIndex == -1) { anonymousClassIndex = getScope().anonymousClasses.size(); getScope().anonymousClasses.add(newClass.def); getScope().anonymousClassesConstructors.add(newClass); LinkedHashSet<VarSymbol> finalVars = new LinkedHashSet<>(); getScope().finalVariables.add(finalVars); new TreeScanner() { public void visitIdent(JCIdent var) { if (var.sym != null && (var.sym instanceof VarSymbol)) { VarSymbol varSymbol = (VarSymbol) var.sym; if (varSymbol.getEnclosingElement() instanceof MethodSymbol && varSymbol .getEnclosingElement().getEnclosingElement() == getParent(JCClassDecl.class).sym) { finalVars.add((VarSymbol) var.sym); } } } }.visitClassDef(newClass.def); } return anonymousClassIndex; } @Override public void visitNewClass(JCNewClass newClass) { ClassSymbol clazz = ((ClassSymbol) newClass.clazz.type.tsym); if (clazz.getSimpleName().toString().equals(JSweetConfig.GLOBALS_CLASS_NAME)) { report(newClass, JSweetProblem.GLOBAL_CANNOT_BE_INSTANTIATED); return; } if (getScope().localClasses.stream().map(c -> c.type).anyMatch(t -> t.equals(newClass.type))) { print("new ").print(getScope().name + ".").print(newClass.clazz.toString()); print("(").printConstructorArgList(newClass, true).print(")"); return; } boolean isInterface = context.isInterface(clazz); if (newClass.def != null || isInterface) { if (context.isAnonymousClass(newClass)) { int anonymousClassIndex = initAnonymousClass(newClass); print("new ") .print(getScope().name + "." + getScope().name + ANONYMOUS_PREFIX + anonymousClassIndex); if (newClass.def.getModifiers().getFlags().contains(Modifier.STATIC)) { printAnonymousClassTypeArgs(newClass); } print("(").printConstructorArgList(newClass, false).print(")"); return; } if (isInterface || context.hasAnnotationType(newClass.clazz.type.tsym, JSweetConfig.ANNOTATION_OBJECT_TYPE)) { if (isInterface) { print("<any>"); } Set<String> interfaces = new HashSet<>(); if (getContext().options.isInterfaceTracking()) { context.grabSupportedInterfaceNames(interfaces, clazz); if (!interfaces.isEmpty()) { print("Object.defineProperty("); } } print("{").println().startIndent(); boolean statementPrinted = false; boolean initializationBlockFound = false; if (newClass.def != null) { for (JCTree m : newClass.def.getMembers()) { if (m instanceof JCBlock) { initializationBlockFound = true; List<VarSymbol> initializedVars = new ArrayList<>(); for (JCTree s : ((JCBlock) m).stats) { boolean currentStatementPrinted = false; if (s instanceof JCExpressionStatement && ((JCExpressionStatement) s).expr instanceof JCAssign) { JCAssign assignment = (JCAssign) ((JCExpressionStatement) s).expr; VarSymbol var = null; if (assignment.lhs instanceof JCFieldAccess) { var = Util.findFieldDeclaration(clazz, ((JCFieldAccess) assignment.lhs).name); printIndent().print(var.getSimpleName().toString()); } else if (assignment.lhs instanceof JCIdent) { var = Util.findFieldDeclaration(clazz, ((JCIdent) assignment.lhs).name); printIndent().print(assignment.lhs.toString()); } else { continue; } initializedVars.add(var); print(": ").print(assignment.rhs).print(",").println(); currentStatementPrinted = true; statementPrinted = true; } else if (s instanceof JCExpressionStatement && ((JCExpressionStatement) s).expr instanceof JCMethodInvocation) { JCMethodInvocation invocation = (JCMethodInvocation) ((JCExpressionStatement) s).expr; String meth = invocation.meth.toString(); if (meth.equals(JSweetConfig.INDEXED_SET_FUCTION_NAME) || (meth .equals(JSweetConfig.UTIL_CLASSNAME + "." + JSweetConfig.INDEXED_SET_FUCTION_NAME) || meth.equals(JSweetConfig.DEPRECATED_UTIL_CLASSNAME + "." + JSweetConfig.INDEXED_SET_FUCTION_NAME))) { if (invocation.getArguments().size() == 3) { if ("this".equals(invocation.getArguments().get(0).toString())) { printIndent().print(invocation.args.tail.head).print(": ") .print(invocation.args.tail.tail.head).print(",").println(); } currentStatementPrinted = true; statementPrinted = true; } else { printIndent().print(invocation.args.head).print(": ") .print(invocation.args.tail.head).print(",").println(); currentStatementPrinted = true; statementPrinted = true; } } } if (!currentStatementPrinted) { report(s, JSweetProblem.INVALID_INITIALIZER_STATEMENT); } } for (Symbol s : clazz.getEnclosedElements()) { if (s instanceof VarSymbol) { if (!initializedVars.contains(s)) { if (!context.hasAnnotationType(s, JSweetConfig.ANNOTATION_OPTIONAL)) { report(m, JSweetProblem.UNINITIALIZED_FIELD, s); } } } } if (statementPrinted) { removeLastChars(2); } } } } if (!statementPrinted && !initializationBlockFound) { for (Symbol s : clazz.getEnclosedElements()) { if (s instanceof VarSymbol) { if (!context.hasAnnotationType(s, JSweetConfig.ANNOTATION_OPTIONAL)) { report(newClass, JSweetProblem.UNINITIALIZED_FIELD, s); } } } } println().endIndent().printIndent().print("}"); if (getContext().options.isInterfaceTracking()) { if (!interfaces.isEmpty()) { print(", '" + INTERFACES_FIELD_NAME + "', { configurable: true, value: "); print("["); for (String i : interfaces) { print("\"").print(i).print("\","); } removeLastChar(); print("]"); print(" })"); } } } else { // ((target : DataStruct3) => { // target['i'] = 1; // target['s2'] = ""; // return target // })(new DataStruct3()); print("((target:").print(newClass.clazz).print(") => {").println().startIndent(); for (JCTree m : newClass.def.getMembers()) { if (m instanceof JCBlock) { for (JCTree s : ((JCBlock) m).stats) { boolean currentStatementPrinted = false; if (s instanceof JCExpressionStatement && ((JCExpressionStatement) s).expr instanceof JCAssign) { JCAssign assignment = (JCAssign) ((JCExpressionStatement) s).expr; VarSymbol var = null; if (assignment.lhs instanceof JCFieldAccess) { var = Util.findFieldDeclaration(clazz, ((JCFieldAccess) assignment.lhs).name); printIndent().print("target['").print(var.getSimpleName().toString()) .print("']"); } else if (assignment.lhs instanceof JCIdent) { printIndent().print("target['").print(assignment.lhs.toString()).print("']"); } else { continue; } print(" = ").print(assignment.rhs).print(";").println(); currentStatementPrinted = true; } else if (s instanceof JCExpressionStatement && ((JCExpressionStatement) s).expr instanceof JCMethodInvocation) { JCMethodInvocation invocation = (JCMethodInvocation) ((JCExpressionStatement) s).expr; String meth = invocation.meth.toString(); if (meth.equals(JSweetConfig.INDEXED_SET_FUCTION_NAME) || (meth.equals( JSweetConfig.UTIL_CLASSNAME + "." + JSweetConfig.INDEXED_SET_FUCTION_NAME) || meth.equals(JSweetConfig.DEPRECATED_UTIL_CLASSNAME + "." + JSweetConfig.INDEXED_SET_FUCTION_NAME))) { if (invocation.getArguments().size() == 3) { if ("this".equals(invocation.getArguments().get(0).toString())) { printIndent().print("target[").print(invocation.args.tail.head) .print("]").print(" = ").print(invocation.args.tail.tail.head) .print(";").println(); } currentStatementPrinted = true; } else { printIndent().print("target[").print(invocation.args.head).print("]") .print(" = ").print(invocation.args.tail.head).print(";").println(); currentStatementPrinted = true; } } } if (!currentStatementPrinted) { report(s, JSweetProblem.INVALID_INITIALIZER_STATEMENT); } } } } printIndent().print("return target;").println(); println().endIndent().printIndent().print("})("); print("new ").print(newClass.clazz).print("(").printArgList(null, newClass.args).print("))"); } } else { if (context.hasAnnotationType(newClass.clazz.type.tsym, JSweetConfig.ANNOTATION_OBJECT_TYPE)) { print("{}"); } else { if (!getAdapter().substituteNewClass(new NewClassElementSupport(newClass))) { String mappedType = context.getTypeMappingTarget(newClass.clazz.type.toString()); if (typeChecker.checkType(newClass, null, newClass.clazz)) { boolean applyVarargs = true; MethodSymbol methSym = (MethodSymbol) newClass.constructor; if (newClass.args.size() == 0 || !Util.hasVarargs(methSym) // || newClass.args.last().type.getKind() != TypeKind.ARRAY // we dont use apply if var args type differ || !context.types.erasure(((ArrayType) newClass.args.last().type).elemtype) .equals(context.types.erasure( ((ArrayType) methSym.getParameters().last().type).elemtype))) { applyVarargs = false; } if (applyVarargs) { // this is necessary in case the user defines a // Function class that hides the global Function // class context.addGlobalsMapping("Function", "__Function"); print("<any>new (__Function.prototype.bind.apply("); if (mappedType != null) { print(mapConstructorType(mappedType)); } else { print(newClass.clazz); } print(", [null"); for (int i = 0; i < newClass.args.length() - 1; i++) { print(", ").print(newClass.args.get(i)); } print("].concat(<any[]>").print(newClass.args.last()).print(")))"); } else { if (newClass.clazz instanceof JCTypeApply) { JCTypeApply typeApply = (JCTypeApply) newClass.clazz; mappedType = context.getTypeMappingTarget(typeApply.clazz.type.toString()); print("new "); if (mappedType != null) { print(mapConstructorType(mappedType)); } else { print(typeApply.clazz); } if (!typeApply.arguments.isEmpty()) { print("<").printTypeArgList(typeApply.arguments).print(">"); } else { // erase types since the diamond (<>) // operator // does not exists in TypeScript printAnyTypeArguments( ((ClassSymbol) newClass.clazz.type.tsym).getTypeParameters().length()); } print("(").printConstructorArgList(newClass, false).print(")"); } else { print("new "); if (mappedType != null) { print(mapConstructorType(mappedType)); } else { print(newClass.clazz); } print("(").printConstructorArgList(newClass, false).print(")"); } } } } } } } public void printAnyTypeArguments(int count) { print("<"); for (int i = 0; i < count; i++) { print("any, "); } if (count > 0) { removeLastChars(2); } print(">"); } @Override public AbstractTreePrinter printConstructorArgList(JCNewClass newClass, boolean localClass) { boolean printed = false; if (localClass || (getScope().anonymousClasses.contains(newClass.def) && !newClass.def.getModifiers().getFlags().contains(Modifier.STATIC))) { print("this"); if (!newClass.args.isEmpty()) { print(", "); } printed = true; } else if ((newClass.clazz.type.tsym.getEnclosingElement() instanceof ClassSymbol && !newClass.clazz.type.tsym.getModifiers().contains(Modifier.STATIC))) { print("this"); JCClassDecl parent = getParent(JCClassDecl.class); ClassSymbol parentSymbol = parent == null ? null : parent.sym; if (newClass.clazz.type.tsym.getEnclosingElement() != parentSymbol) { print("." + PARENT_CLASS_FIELD_NAME); } if (!newClass.args.isEmpty()) { print(", "); } printed = true; } MethodType t = (MethodType) newClass.constructorType; printArgList(t == null ? null : t.argtypes, newClass.args); int index = getScope().anonymousClasses.indexOf(newClass.def); if (index >= 0 && !getScope().finalVariables.get(index).isEmpty()) { if (printed || !newClass.args.isEmpty()) { print(", "); } for (VarSymbol v : getScope().finalVariables.get(index)) { print(v.getSimpleName().toString()); print(", "); } removeLastChars(2); } return this; } @Override public void visitLiteral(JCLiteral literal) { String s = literal.toString(); switch (literal.typetag) { case FLOAT: if (s.endsWith("F")) { s = s.substring(0, s.length() - 1); } break; case LONG: if (s.endsWith("L")) { s = s.substring(0, s.length() - 1); } break; default: } print(s); } @Override public void visitIndexed(JCArrayAccess arrayAccess) { if (!getAdapter().substituteArrayAccess(new ArrayAccessElementSupport(arrayAccess))) { print(arrayAccess.indexed).print("[").print(arrayAccess.index).print("]"); } } @Override public void visitForeachLoop(JCEnhancedForLoop foreachLoop) { String indexVarName = "index" + Util.getId(); boolean[] hasLength = { false }; TypeSymbol targetType = foreachLoop.expr.type.tsym; Util.scanMemberDeclarationsInType(targetType, getAdapter().getErasedTypes(), element -> { if (element instanceof VarSymbol) { if ("length".equals(element.getSimpleName().toString()) && Util.isNumber(((VarSymbol) element).type)) { hasLength[0] = true; return false; } } return true; }); if (!getAdapter().substituteForEachLoop(new ForeachLoopElementSupport(foreachLoop), hasLength[0], indexVarName)) { boolean noVariable = foreachLoop.expr instanceof JCIdent || foreachLoop.expr instanceof JCFieldAccess; if (noVariable) { print("for(" + VAR_DECL_KEYWORD + " " + indexVarName + "=0; " + indexVarName + " < ") .print(foreachLoop.expr).print("." + "length" + "; " + indexVarName + "++) {").println() .startIndent().printIndent(); print(VAR_DECL_KEYWORD + " " + foreachLoop.var.name.toString() + " = ").print(foreachLoop.expr) .print("[" + indexVarName + "];").println(); } else { String arrayVarName = "array" + Util.getId(); print("{").println().startIndent().printIndent(); print(VAR_DECL_KEYWORD + " " + arrayVarName + " = ").print(foreachLoop.expr).print(";").println() .printIndent(); print("for(" + VAR_DECL_KEYWORD + " " + indexVarName + "=0; " + indexVarName + " < " + arrayVarName + ".length; " + indexVarName + "++) {").println().startIndent().printIndent(); print(VAR_DECL_KEYWORD + " " + foreachLoop.var.name.toString() + " = " + arrayVarName + "[" + indexVarName + "];").println(); } printIndent().print(foreachLoop.body); endIndent().println().printIndent().print("}"); if (!noVariable) { endIndent().println().printIndent().print("}"); } } } @Override public void visitTypeIdent(JCPrimitiveTypeTree type) { switch (type.typetag) { case BYTE: case DOUBLE: case FLOAT: case INT: case LONG: case SHORT: print("number"); break; default: print(type.toString()); } } @Override public void visitBinary(JCBinary binary) { if (Util.isIntegral(binary.type) && binary.getKind() == Kind.DIVIDE) { if (binary.type.getKind() == TypeKind.LONG) { print("Math.floor("); } else { print("("); } } boolean charWrapping = Util.isArithmeticOperator(binary.getKind()) || Util.isComparisonOperator(binary.getKind()); boolean actualCharWrapping = false; if (charWrapping && binary.lhs.type.isPrimitive() && context.symtab.charType.tsym == binary.lhs.type.tsym && !(binary.rhs.type.tsym == context.symtab.stringType.tsym)) { actualCharWrapping = true; if (binary.lhs instanceof JCLiteral) { print(binary.lhs).print(".charCodeAt(0)"); } else { print("(c => c.charCodeAt==null?<any>c:c.charCodeAt(0))(").print(binary.lhs).print(")"); } } else { print(binary.lhs); } String op = binary.operator.name.toString(); if (binary.lhs.type.getKind() == TypeKind.BOOLEAN) { if ("|".equals(op)) { op = "||"; } else if ("&".equals(op)) { op = "&&"; } else if ("^".equals(op)) { op = "!=="; } } if ("==".equals(op) || "!=".equals(op)) { if (charWrapping && binary.rhs.type.isPrimitive() && context.symtab.charType.tsym == binary.rhs.type.tsym && !(binary.lhs.type.tsym == context.symtab.stringType.tsym)) { actualCharWrapping = true; } } if ("==".equals(op) && !(Util.isNullLiteral(binary.lhs) || Util.isNullLiteral(binary.rhs))) { op = actualCharWrapping ? "==" : "==="; } else if ("!=".equals(op) && !(Util.isNullLiteral(binary.lhs) || Util.isNullLiteral(binary.rhs))) { op = actualCharWrapping ? "!=" : "!=="; } space().print(op).space(); if (charWrapping && binary.rhs.type.isPrimitive() && context.symtab.charType.tsym == binary.rhs.type.tsym && !(binary.lhs.type.tsym == context.symtab.stringType.tsym)) { if (binary.rhs instanceof JCLiteral) { print(binary.rhs).print(".charCodeAt(0)"); } else { print("(c => c.charCodeAt==null?<any>c:c.charCodeAt(0))(").print(binary.rhs).print(")"); } } else { print(binary.rhs); } if (Util.isIntegral(binary.type) && binary.getKind() == Kind.DIVIDE) { if (binary.type.getKind() == TypeKind.LONG) { print(")"); } else { print("|0)"); } } } @Override public void visitIf(JCIf ifStatement) { print("if").print(ifStatement.cond).print(" "); print(ifStatement.thenpart); if (!(ifStatement.thenpart instanceof JCBlock)) { if (!statementsWithNoSemis.contains(ifStatement.thenpart.getClass())) { print(";"); } } if (ifStatement.elsepart != null) { print(" else "); print(ifStatement.elsepart); if (!(ifStatement.elsepart instanceof JCBlock)) { if (!statementsWithNoSemis.contains(ifStatement.elsepart.getClass())) { print(";"); } } } } @Override public void visitReturn(JCReturn returnStatement) { print("return"); if (returnStatement.expr != null) { JCTree parentFunction = getFirstParent(JCMethodDecl.class, JCLambda.class); if (returnStatement.expr.type == null) { report(returnStatement, JSweetProblem.CANNOT_ACCESS_THIS, parentFunction == null ? returnStatement.toString() : parentFunction.toString()); return; } print(" "); Type returnType = null; if (parentFunction != null) { if (parentFunction instanceof JCMethodDecl) { returnType = ((JCMethodDecl) parentFunction).restype.type; } else { // TODO: this cannot work! Calculate the return type of the // lambda // either from the functional type type arguments, of from // the method defining the lambda's signature // returnType = ((JCLambda) parentFunction).type; } } if (!substituteAssignedExpression(returnType, returnStatement.expr)) { print(returnStatement.expr); } } } private boolean staticInitializedAssignment = false; private VarSymbol getStaticInitializedField(JCTree expr) { if (expr instanceof JCIdent) { return context.lazyInitializedStatics.contains(((JCIdent) expr).sym) ? (VarSymbol) ((JCIdent) expr).sym : null; } else if (expr instanceof JCFieldAccess) { return context.lazyInitializedStatics.contains(((JCFieldAccess) expr).sym) ? (VarSymbol) ((JCFieldAccess) expr).sym : null; } else { return null; } } @Override public void visitAssignop(JCAssignOp assignOp) { boolean expand = staticInitializedAssignment = (getStaticInitializedField(assignOp.lhs) != null); boolean expandChar = assignOp.lhs.type.getKind() == TypeKind.CHAR; print(assignOp.lhs); staticInitializedAssignment = false; String op = assignOp.operator.name.toString(); if (assignOp.lhs.type.getKind() == TypeKind.BOOLEAN) { if ("|".equals(op)) { print(" = ").print(assignOp.lhs).print(" || ").print(assignOp.rhs); return; } else if ("&".equals(op)) { print(" = ").print(assignOp.lhs).print(" && ").print(assignOp.rhs); return; } } if (expandChar) { print(" = String.fromCharCode(") .substituteAndPrintAssignedExpression(context.symtab.intType, assignOp.lhs) .print(" " + op + " ") .substituteAndPrintAssignedExpression(context.symtab.intType, assignOp.rhs).print(")"); return; } if (expand) { print(" = ").print(assignOp.lhs).print(" " + op + " ").print(assignOp.rhs); return; } print(" " + op + "= "); print(assignOp.rhs); } @Override public void visitConditional(JCConditional conditional) { print(conditional.cond); print("?"); if (!substituteAssignedExpression( rootConditionalAssignedTypes.isEmpty() ? null : rootConditionalAssignedTypes.peek(), conditional.truepart)) { print(conditional.truepart); } print(":"); if (!substituteAssignedExpression( rootConditionalAssignedTypes.isEmpty() ? null : rootConditionalAssignedTypes.peek(), conditional.falsepart)) { print(conditional.falsepart); } if (!rootConditionalAssignedTypes.isEmpty()) { rootConditionalAssignedTypes.pop(); } } @Override public void visitForLoop(JCForLoop forLoop) { print("for(").printArgList(null, forLoop.init).print("; ").print(forLoop.cond).print("; ") .printArgList(null, forLoop.step).print(") "); print(forLoop.body); } @Override public void visitContinue(JCContinue continueStatement) { print("continue"); if (continueStatement.label != null) { print(" ").print(continueStatement.label.toString()); } } @Override public void visitBreak(JCBreak breakStatement) { print("break"); if (breakStatement.label != null) { print(" ").print(breakStatement.label.toString()); } } @Override public void visitLabelled(JCLabeledStatement labelledStatement) { print(labelledStatement.label.toString()).print(": "); print(labelledStatement.body); } @Override public void visitTypeArray(JCArrayTypeTree arrayType) { print(arrayType.elemtype).print("[]"); } @Override public void visitNewArray(JCNewArray newArray) { if (newArray.elemtype != null) { typeChecker.checkType(newArray, null, newArray.elemtype); } if (newArray.dims != null && !newArray.dims.isEmpty()) { if (newArray.dims.size() == 1) { if (Util.isNumber(newArray.elemtype.type)) { if (newArray.dims.head instanceof JCLiteral && ((int) ((JCLiteral) newArray.dims.head).value) <= 10) { boolean hasElements = false; print("["); for (int i = 0; i < (int) ((JCLiteral) newArray.dims.head).value; i++) { print("0, "); hasElements = true; } if (hasElements) { removeLastChars(2); } print("]"); } else { print("(s => { let a=[]; while(s-->0) a.push(0); return a; })(").print(newArray.dims.head) .print(")"); } } else { print("new Array(").print(newArray.dims.head).print(")"); } } else { print("<any> (function(dims) { " + VAR_DECL_KEYWORD + " allocate = function(dims) { if(dims.length==0) { return " + (Util.isNumber(newArray.elemtype.type) ? "0" : "undefined") + "; } else { " + VAR_DECL_KEYWORD + " array = []; for(" + VAR_DECL_KEYWORD + " i = 0; i < dims[0]; i++) { array.push(allocate(dims.slice(1))); } return array; }}; return allocate(dims);})"); print("(["); printArgList(null, newArray.dims); print("])"); } } else { print("["); if (newArray.elems != null && !newArray.elems.isEmpty()) { for (JCExpression e : newArray.elems) { if (!rootArrayAssignedTypes.isEmpty()) { if (!substituteAssignedExpression(rootArrayAssignedTypes.peek(), e)) { print(e); } } else { print(e); } print(", "); } removeLastChars(2); if (!rootArrayAssignedTypes.isEmpty()) { rootArrayAssignedTypes.pop(); } } print("]"); } } boolean inRollback = false; @Override public void visitUnary(JCUnary unary) { if (getContext().options.isSupportSaticLazyInitialization()) { if (!inRollback) { JCStatement statement = null; VarSymbol[] staticInitializedField = { null }; switch (unary.getTag()) { case POSTDEC: case POSTINC: case PREDEC: case PREINC: staticInitializedAssignment = (staticInitializedField[0] = getStaticInitializedField( unary.arg)) != null; if (staticInitializedAssignment) { statement = getParent(JCStatement.class); } default: } if (statement != null) { rollback(statement, tree -> { print(context.getRootRelativeName(null, staticInitializedField[0].getEnclosingElement())) .print(".").print(staticInitializedField[0].getSimpleName().toString() + STATIC_INITIALIZATION_SUFFIX + "();") .println().printIndent(); inRollback = true; scan(tree); }); } } else { inRollback = false; } } switch (unary.getTag()) { case POS: print("+").print(unary.arg); break; case NEG: print("-").print(unary.arg); break; case POSTDEC: case POSTINC: print(unary.arg); print(unary.operator.name.toString()); break; default: print(unary.operator.name.toString()); print(unary.arg); break; } } @Override public void visitSwitch(JCSwitch switchStatement) { print("switch("); print(switchStatement.selector); if (context.types.isSameType(context.symtab.charType, switchStatement.selector.type)) { print(".charCodeAt(0)"); } print(") {").println(); for (JCCase caseStatement : switchStatement.cases) { printIndent(); print(caseStatement); } printIndent().print("}"); } protected void printCaseStatementPattern(JCExpression pattern) { } @Override public void visitCase(JCCase caseStatement) { if (caseStatement.pat != null) { print("case "); if (!getAdapter().substituteCaseStatementPattern(new CaseElementSupport(caseStatement), ExtendedElementFactory.INSTANCE.create(caseStatement.pat))) { if (caseStatement.pat.type.isPrimitive() || context.types.isSameType(context.symtab.stringType, caseStatement.pat.type)) { if (caseStatement.pat instanceof JCIdent) { Object value = ((VarSymbol) ((JCIdent) caseStatement.pat).sym).getConstValue(); if (context.types.isSameType(context.symtab.stringType, caseStatement.pat.type)) { print("\"" + value + "\" /* " + caseStatement.pat + " */"); } else { print("" + value + " /* " + caseStatement.pat + " */"); } } else { if (context.types.isSameType(context.symtab.charType, caseStatement.pat.type)) { print("" + ((JCLiteral) caseStatement.pat).value + " /* " + caseStatement.pat + " */"); } else { print(caseStatement.pat); } } } else { if (context.useModules) { print(caseStatement.pat.type.tsym.getSimpleName() + "." + caseStatement.pat); } else { print(getRootRelativeName(caseStatement.pat.type.tsym) + "." + caseStatement.pat); } } } } else { print("default"); } print(":"); println().startIndent(); for (JCStatement statement : caseStatement.stats) { printIndent(); print(statement); if (!statementsWithNoSemis.contains(statement.getClass())) { print(";"); } println(); } endIndent(); } @Override public void visitTypeCast(JCTypeCast cast) { if (substituteAssignedExpression(cast.type, cast.expr)) { return; } if (Util.isIntegral(cast.type)) { if (cast.type.getKind() == TypeKind.LONG) { print("Math.floor("); } else { print("("); } } if (!context.hasAnnotationType(cast.clazz.type.tsym, ANNOTATION_ERASED, ANNOTATION_OBJECT_TYPE, ANNOTATION_FUNCTIONAL_INTERFACE)) { // Java is more permissive than TypeScript when casting type // variables if (cast.expr.type.getKind() == TypeKind.TYPEVAR) { print("<any>"); } else { print("<"); substituteAndPrintType(cast.clazz).print(">"); } } print(cast.expr); if (Util.isIntegral(cast.type)) { if (cast.type.getKind() == TypeKind.LONG) { print(")"); } else { print("|0)"); } } } @Override public void visitDoLoop(JCDoWhileLoop doWhileLoop) { print("do "); if (doWhileLoop.body instanceof JCBlock) { print(doWhileLoop.body); } else { print(doWhileLoop.body).print(";"); } print(" while(").print(doWhileLoop.cond).print(")"); } @Override public void visitWhileLoop(JCWhileLoop whileLoop) { print("while(").print(whileLoop.cond).print(") "); print(whileLoop.body); } @Override public void visitAssign(JCAssign assign) { if (!getAdapter().substituteAssignment(new AssignmentElementSupport(assign))) { staticInitializedAssignment = getStaticInitializedField(assign.lhs) != null; print(assign.lhs).print(isAnnotationScope ? ": " : " = "); if (!substituteAssignedExpression(assign.lhs.type, assign.rhs)) { print(assign.rhs); } staticInitializedAssignment = false; } } @Override public void visitTry(JCTry tryStatement) { if (tryStatement.resources != null && !tryStatement.resources.isEmpty()) { report(tryStatement, JSweetProblem.UNSUPPORTED_TRY_WITH_RESOURCE); } if (tryStatement.catchers.isEmpty() && tryStatement.finalizer == null) { report(tryStatement, JSweetProblem.TRY_WITHOUT_CATCH_OR_FINALLY); } print("try ").print(tryStatement.body); if (tryStatement.catchers.size() > 1) { print(" catch(__e) {").startIndent(); for (JCCatch catcher : tryStatement.catchers) { println().printIndent().print("if"); printInstanceOf("__e", null, catcher.param.type); print(" {").startIndent().println().printIndent(); // if (!context.options.isUseJavaApis() && // catcher.param.type.toString().startsWith("java.")) { // print(catcher.param).print(" = ").print("__e;").println(); // } else { print(catcher.param).print(" = <"); substituteAndPrintType(catcher.param.getType()); print(">__e;").println(); // } printBlockStatements(catcher.body.getStatements()); endIndent().println().printIndent().print("}"); } endIndent().println().printIndent().print("}"); } else if (tryStatement.catchers.size() == 1) { print(tryStatement.catchers.head); } if (tryStatement.finalizer != null) { print(" finally ").print(tryStatement.finalizer); } } @Override public void visitCatch(JCCatch catcher) { print(" catch(").print(catcher.param.name.toString()).print(") "); print(catcher.body); } @Override public void visitLambda(JCLambda lamba) { Map<String, VarSymbol> varAccesses = new HashMap<>(); Util.fillAllVariableAccesses(varAccesses, lamba); Collection<VarSymbol> finalVars = new ArrayList<>(varAccesses.values()); if (!varAccesses.isEmpty()) { Map<String, VarSymbol> varDefs = new HashMap<>(); int parentIndex = getStack().size() - 2; int i = parentIndex; JCStatement statement = null; while (i > 0 && getStack().get(i).getKind() != Kind.LAMBDA_EXPRESSION && getStack().get(i).getKind() != Kind.METHOD) { if (statement == null && getStack().get(i) instanceof JCStatement) { statement = (JCStatement) getStack().get(i); } i--; } if (i >= 0 && getStack().get(i).getKind() != Kind.LAMBDA_EXPRESSION && statement != null) { Util.fillAllVariablesInScope(varDefs, getStack(), lamba, getStack().get(i)); } finalVars.retainAll(varDefs.values()); } if (!finalVars.isEmpty()) { print("(("); for (VarSymbol var : finalVars) { print(var.name.toString()).print(","); } removeLastChar(); print(") => {").println().startIndent().printIndent().print("return "); } getScope().skipTypeAnnotations = true; print("(").printArgList(null, lamba.params).print(") => "); getScope().skipTypeAnnotations = false; print(lamba.body); if (!finalVars.isEmpty()) { endIndent().println().printIndent().print("})("); for (VarSymbol var : finalVars) { print(var.name.toString()).print(","); } removeLastChar(); print(")"); } } @Override public void visitReference(JCMemberReference memberReference) { if (memberReference.sym instanceof MethodSymbol) { MethodSymbol method = (MethodSymbol) memberReference.sym; if (getParent() instanceof JCTypeCast) { print("("); } print("("); if (method.getParameters() != null) { for (VarSymbol var : method.getParameters()) { print(var.name.toString()); print(","); } if (!method.getParameters().isEmpty()) { removeLastChar(); } } print(")"); print(" => { return "); } if (JSweetConfig.GLOBALS_CLASS_NAME.equals(memberReference.expr.type.tsym.getSimpleName().toString())) { print(memberReference.name.toString()); } else { if ("<init>".equals(memberReference.name.toString())) { if (context.types.isArray(memberReference.expr.type)) { print("new Array<"); substituteAndPrintType(((JCArrayTypeTree) memberReference.expr).elemtype); print(">"); } else { print("new ").print(memberReference.expr); } } else { print(memberReference.expr).print(".").print(memberReference.name.toString()); } } if (memberReference.sym instanceof MethodSymbol) { MethodSymbol method = (MethodSymbol) memberReference.sym; print("("); if (method.getParameters() != null) { for (VarSymbol var : method.getParameters()) { print(var.name.toString()); print(","); } if (!method.getParameters().isEmpty()) { removeLastChar(); } } print(")"); print(" }"); if (getParent() instanceof JCTypeCast) { print(")"); } } } @Override public void visitTypeParameter(JCTypeParameter typeParameter) { print(typeParameter.name.toString()); if (typeParameter.bounds != null && !typeParameter.bounds.isEmpty()) { print(" extends "); for (JCExpression e : typeParameter.bounds) { substituteAndPrintType(e).print(" & "); } removeLastChars(3); } } @Override public void visitSynchronized(JCSynchronized sync) { report(sync, JSweetProblem.SYNCHRONIZATION); if (sync.body != null) { print(sync.body); } } public void print(String exprStr, JCTree expr) { if (exprStr == null) { print(expr); } else { print(exprStr); } } private void printInstanceOf(String exprStr, JCTree expr, Type type) { printInstanceOf(exprStr, expr, type, false); } private void printInstanceOf(String exprStr, JCTree expr, Type type, boolean checkFirstArrayElement) { if (!(getParent() instanceof JCParens)) { print("("); } if (checkFirstArrayElement || !getAdapter().substituteInstanceof(exprStr, ExtendedElementFactory.INSTANCE.create(expr), type)) { if (TYPE_MAPPING.containsKey(type.toString())) { print("typeof "); print(exprStr, expr); if (checkFirstArrayElement) print("[0]"); print(" === ").print("'" + TYPE_MAPPING.get(type.toString()).toLowerCase() + "'"); } else if (type.tsym.isEnum()) { print("typeof "); print(exprStr, expr); if (checkFirstArrayElement) print("[0]"); print(" === 'number'"); } else if (type.toString().startsWith(JSweetConfig.FUNCTION_CLASSES_PACKAGE + ".") || type.toString().startsWith("java.util.function.") || Runnable.class.getName().equals(type.toString()) || context.hasAnnotationType(type.tsym, JSweetConfig.ANNOTATION_FUNCTIONAL_INTERFACE)) { print("typeof "); print(exprStr, expr); if (checkFirstArrayElement) print("[0]"); print(" === 'function'"); int parameterCount = context.getFunctionalTypeParameterCount(type); if (parameterCount != -1) { print(" && (<any>"); print(exprStr, expr); if (checkFirstArrayElement) print("[0]"); print(").length == " + context.getFunctionalTypeParameterCount(type)); } } else { print(exprStr, expr); if (checkFirstArrayElement) print("[0]"); if (context.isInterface(type.tsym)) { print(" != null && "); print("("); print(exprStr, expr); if (checkFirstArrayElement) print("[0]"); print("[\"" + INTERFACES_FIELD_NAME + "\"]").print(" != null && "); print(exprStr, expr); if (checkFirstArrayElement) print("[0]"); print("[\"" + INTERFACES_FIELD_NAME + "\"].indexOf(\"") .print(type.tsym.getQualifiedName().toString()).print("\") >= 0"); print(" || "); print(exprStr, expr); if (checkFirstArrayElement) print("[0]"); print(".constructor != null && "); print(exprStr, expr); if (checkFirstArrayElement) print("[0]"); print(".constructor[\"" + INTERFACES_FIELD_NAME + "\"]").print(" != null && "); print(exprStr, expr); if (checkFirstArrayElement) print("[0]"); print(".constructor[\"" + INTERFACES_FIELD_NAME + "\"].indexOf(\"") .print(type.tsym.getQualifiedName().toString()).print("\") >= 0"); if (CharSequence.class.getName().equals(type.tsym.getQualifiedName().toString())) { print(" || typeof "); print(exprStr, expr); if (checkFirstArrayElement) print("[0]"); print(" === \"string\""); } print(")"); } else { if (type.tsym instanceof TypeVariableSymbol || Object.class.getName().equals(type.tsym.getQualifiedName().toString())) { print(" != null"); } else { String qualifiedName = getQualifiedTypeName(type.tsym, false); if (qualifiedName.startsWith("{")) { qualifiedName = "Object"; } print(" != null"); if (!"any".equals(qualifiedName)) { print(" && "); print(exprStr, expr); if (checkFirstArrayElement) print("[0]"); if (qualifiedName.startsWith(JSweetConfig.LIBS_PACKAGE + ".")) { print(" instanceof ").print(qualifiedName); } else { print(" instanceof <any>").print(qualifiedName); } if (type instanceof ArrayType) { ArrayType t = (ArrayType) type; print(" && ("); print(exprStr, expr); if (checkFirstArrayElement) print("[0]"); print(".length==0 || "); print(exprStr, expr); print("[0] == null ||"); if (t.elemtype instanceof ArrayType) { print(exprStr, expr); print("[0] instanceof Array"); } else { printInstanceOf(exprStr, expr, t.elemtype, true); } print(")"); } } } } } } if (!(getParent() instanceof JCParens)) { print(")"); } } @Override public void visitTypeTest(JCInstanceOf instanceOf) { printInstanceOf(null, instanceOf.expr, instanceOf.clazz.type); } @Override public void visitThrow(JCThrow throwStatement) { print("throw ").print(throwStatement.expr); } @Override public void visitAssert(JCAssert assertion) { if (!context.options.isIgnoreAssertions()) { String assertCode = assertion.toString().replace("\"", "'"); print("if(!(").print(assertion.cond).print( ")) throw new Error(\"Assertion error line " + getCurrentLine() + ": " + assertCode + "\");"); } } @Override public void visitAnnotation(JCAnnotation annotation) { if (!context.hasAnnotationType(annotation.type.tsym, JSweetConfig.ANNOTATION_DECORATOR)) { return; } print("@").print(annotation.getAnnotationType()).print("("); if (annotation.getArguments() != null && !annotation.getArguments().isEmpty()) { isAnnotationScope = true; print(" { "); for (JCExpression e : annotation.getArguments()) { print(e); print(", "); } removeLastChars(2); print(" } "); isAnnotationScope = false; } print(")").println().printIndent(); } Stack<Type> rootConditionalAssignedTypes = new Stack<>(); Stack<Type> rootArrayAssignedTypes = new Stack<>(); @Override protected boolean substituteAssignedExpression(Type assignedType, JCExpression expression) { if (assignedType == null) { return false; } if (expression instanceof JCConditional) { rootConditionalAssignedTypes.push(assignedType); return false; } if (expression instanceof JCNewArray && assignedType instanceof ArrayType) { rootArrayAssignedTypes.push(((ArrayType) assignedType).elemtype); return false; } if (assignedType.getTag() == TypeTag.CHAR && expression.type.getTag() != TypeTag.CHAR) { print("String.fromCharCode(").print(expression).print(")"); return true; } else if (Util.isNumber(assignedType) && expression.type.getTag() == TypeTag.CHAR) { print("(").print(expression).print(").charCodeAt(0)"); return true; } else { if (expression instanceof JCLambda) { if (assignedType.tsym.isInterface() && !context.isFunctionalType(assignedType.tsym)) { JCLambda lambda = (JCLambda) expression; MethodSymbol method = (MethodSymbol) assignedType.tsym.getEnclosedElements().get(0); print("{ " + method.getSimpleName() + " : ").print(lambda).print(" }"); return true; } } else if (expression instanceof JCNewClass) { JCNewClass newClass = (JCNewClass) expression; if (newClass.def != null && context.isFunctionalType(assignedType.tsym)) { List<JCTree> defs = newClass.def.defs; boolean printed = false; for (JCTree def : defs) { if (def instanceof JCMethodDecl) { if (printed) { // should never happen... report error? } JCMethodDecl method = (JCMethodDecl) def; if (method.sym.isConstructor()) { continue; } getStack().push(method); print("(").printArgList(null, method.getParameters()).print(") => ").print(method.body); getStack().pop(); printed = true; } } if (printed) { return true; } } else { // object assignment to functional type if ((newClass.def == null && context.isFunctionalType(assignedType.tsym))) { MethodSymbol method; for (Symbol s : assignedType.tsym.getEnclosedElements()) { if (s instanceof MethodSymbol) { // TODO also check that the method is compatible // (here we just apply to the first found // method) method = (MethodSymbol) s; print("("); for (VarSymbol p : method.getParameters()) { print(p.getSimpleName().toString()).print(", "); } if (!method.getParameters().isEmpty()) { removeLastChars(2); } print(") => { return new ").print(newClass.clazz).print("(").printArgList(null, newClass.args); print(").").print(method.getSimpleName().toString()).print("("); for (VarSymbol p : method.getParameters()) { print(p.getSimpleName().toString()).print(", "); } if (!method.getParameters().isEmpty()) { removeLastChars(2); } print("); }"); return true; } } } // raw generic type if (!newClass.type.tsym.getTypeParameters().isEmpty() && newClass.typeargs.isEmpty()) { print("<any>(").print(expression).print(")"); return true; } } } else if (!(expression instanceof JCLambda || expression instanceof JCMemberReference) && context.isFunctionalType(assignedType.tsym)) { // disallow typing to force objects to be passed as function // (may require runtime checks later on) print("<any>(").print(expression).print(")"); return true; } return false; } } }