org.jsweet.transpiler.Java2TypeScriptTranslator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jsweet-transpiler Show documentation
Show all versions of jsweet-transpiler Show documentation
A Java to TypeScript/JavaScript Open Transpiler
The newest version!
/*
* JSweet transpiler - http://www.jsweet.org
* Copyright (C) 2015 CINCHEO SAS
*
* 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 java.util.stream.Collectors.toList;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.jsweet.JSweetConfig.ANNOTATION_ERASED;
import static org.jsweet.JSweetConfig.ANNOTATION_STRING_ENUM;
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_CLASSNAME;
import static org.jsweet.JSweetConfig.UTIL_PACKAGE;
import static org.jsweet.transpiler.util.Util.CONSTRUCTOR_METHOD_NAME;
import static org.jsweet.transpiler.util.Util.firstOrDefault;
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.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.NestingKind;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.TypeParameterElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.ExecutableType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.TypeVariable;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.StringEscapeUtils;
import org.apache.log4j.Logger;
import org.jsweet.JSweetConfig;
import org.jsweet.transpiler.JSweetContext.DefaultMethodEntry;
import org.jsweet.transpiler.JSweetContext.GlobalMethodInfos;
import org.jsweet.transpiler.OverloadScanner.Overload;
import org.jsweet.transpiler.OverloadScanner.OverloadMethodEntry;
import org.jsweet.transpiler.extension.PrinterAdapter;
import org.jsweet.transpiler.model.ExtendedElement;
import org.jsweet.transpiler.model.MethodInvocationElement;
import org.jsweet.transpiler.model.support.CompilationUnitElementSupport;
import org.jsweet.transpiler.model.support.NewArrayElementSupport;
import org.jsweet.transpiler.util.AbstractTreePrinter;
import org.jsweet.transpiler.util.JSDoc;
import org.jsweet.transpiler.util.Util;
import com.sun.source.tree.AnnotationTree;
import com.sun.source.tree.ArrayAccessTree;
import com.sun.source.tree.ArrayTypeTree;
import com.sun.source.tree.AssertTree;
import com.sun.source.tree.AssignmentTree;
import com.sun.source.tree.BinaryTree;
import com.sun.source.tree.BlockTree;
import com.sun.source.tree.BreakTree;
import com.sun.source.tree.CaseTree;
import com.sun.source.tree.CatchTree;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.CompoundAssignmentTree;
import com.sun.source.tree.ConditionalExpressionTree;
import com.sun.source.tree.ContinueTree;
import com.sun.source.tree.DoWhileLoopTree;
import com.sun.source.tree.EnhancedForLoopTree;
import com.sun.source.tree.ExpressionStatementTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.ForLoopTree;
import com.sun.source.tree.IdentifierTree;
import com.sun.source.tree.IfTree;
import com.sun.source.tree.ImportTree;
import com.sun.source.tree.InstanceOfTree;
import com.sun.source.tree.IntersectionTypeTree;
import com.sun.source.tree.LabeledStatementTree;
import com.sun.source.tree.LambdaExpressionTree;
import com.sun.source.tree.LiteralTree;
import com.sun.source.tree.MemberReferenceTree;
import com.sun.source.tree.MemberSelectTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.NewArrayTree;
import com.sun.source.tree.NewClassTree;
import com.sun.source.tree.ParameterizedTypeTree;
import com.sun.source.tree.ParenthesizedTree;
import com.sun.source.tree.PrimitiveTypeTree;
import com.sun.source.tree.ReturnTree;
import com.sun.source.tree.StatementTree;
import com.sun.source.tree.SwitchTree;
import com.sun.source.tree.SynchronizedTree;
import com.sun.source.tree.ThrowTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.Tree.Kind;
import com.sun.source.tree.TryTree;
import com.sun.source.tree.TypeCastTree;
import com.sun.source.tree.TypeParameterTree;
import com.sun.source.tree.UnaryTree;
import com.sun.source.tree.VariableTree;
import com.sun.source.tree.WhileLoopTree;
import com.sun.source.tree.WildcardTree;
import com.sun.source.util.TreePath;
import com.sun.source.util.TreeScanner;
import com.sun.source.util.Trees;
/**
* This is a TypeScript printer for translating the Java AST to a TypeScript
* program.
*
* @author Renaud Pawlak
* @author Louis Grignon
*/
public class Java2TypeScriptTranslator extends AbstractTreePrinter {
/**
* The name of the field where the parent class is stored in the generated
* TypeScript code.
*/
public static final String PARENT_CLASS_FIELD_NAME = "__parent";
/**
* The name of the field where the implemented interface names are stored in the
* generated TypeScript code (for instanceof
operator).
*/
public static final String INTERFACES_FIELD_NAME = "__interfaces";
/**
* The suffix added to static field initialization methods (for Java semantics).
*/
public static final String STATIC_INITIALIZATION_SUFFIX = "_$LI$";
/**
* The name of the field where the class name is stored in the class
* constructor.
*/
public static final String CLASS_NAME_IN_CONSTRUCTOR = "__class";
/**
* A prefix/separator for anonymous classes.
*/
public static final String ANONYMOUS_PREFIX = "$";
/**
* A suffix for name of classes that wrap regular TypeScript enums.
*/
public static final String ENUM_WRAPPER_CLASS_SUFFIX = "_$WRAPPER";
/**
* The name of the variable that contains the enum wrapper instances.
*/
public static final String ENUM_WRAPPER_CLASS_WRAPPERS = "_$wrappers";
/**
* The field name for storing the enum's name.
*/
public static final String ENUM_WRAPPER_CLASS_NAME = "_$name";
/**
* The field name for storing the enum's ordinal.
*/
public static final String ENUM_WRAPPER_CLASS_ORDINAL = "_$ordinal";
/**
* The default keyword for declaring variables.
*/
public static final String VAR_DECL_KEYWORD = "let";
/**
* A regular expression for matching body markers in @Replace
* expression.
*/
public static final Pattern BODY_MARKER = Pattern.compile("\\{\\{\\s*body\\s*\\}\\}");
/**
* A regular expression for matching base indent markers in
* @Replace
expression.
*/
public static final Pattern BASE_INDENT_MARKER = Pattern.compile("\\{\\{\\s*baseIndent\\s*\\}\\}");
/**
* A regular expression for matching indent markers in @Replace
* expression.
*/
public static final Pattern INDENT_MARKER = Pattern.compile("\\{\\{\\s*indent\\s*\\}\\}");
/**
* A regular expression for matching method name markers in
* @Replace
expression.
*/
public static final Pattern METHOD_NAME_MARKER = Pattern.compile("\\{\\{\\s*methodName\\s*\\}\\}");
/**
* A regular expression for matching class name markers in @Replace
* expression.
*/
public static final Pattern CLASS_NAME_MARKER = Pattern.compile("\\{\\{\\s*className\\s*\\}\\}");
/**
* A prefix for generators.
*/
public static final String GENERATOR_PREFIX = "__generator_";
/**
* A logger for internal messages.
*/
protected static Logger logger = Logger.getLogger(Java2TypeScriptTranslator.class);
/**
* A state flag indicating the comparison mode to be used by this printer for
* printing comparison operators.
*
* @author Renaud Pawlak
*/
public static enum ComparisonMode {
/**
* Forces the strict comparison operators (===, >==, <==), even for null
* literals.
*/
FORCE_STRICT,
/**
* Uses the strict comparison operators (===, >==, <==), except for null
* literals, where loose operators are used to match better the Java semantics.
* This is the default behavior.
*/
STRICT,
/**
* Uses the loose comparison operators (==, >=, <=).
*/
LOOSE;
}
private final Stack comparisonModeStack = new Stack<>();
/**
* Selects a comparison mode for subsequently printed comparison operators.
*
* @see #exitComparisonMode()
*/
public void enterComparisonMode(ComparisonMode comparisonMode) {
comparisonModeStack.push(comparisonMode);
}
/**
* Exits a comparison mode and go back to the previous one.
*
* @see #enterComparisonMode(ComparisonMode)
*/
public void exitComparisonMode() {
comparisonModeStack.pop();
}
private ComparisonMode getComparisonMode() {
if (comparisonModeStack.isEmpty()) {
return ComparisonMode.STRICT;
} else {
if (comparisonModeStack.peek() == ComparisonMode.STRICT) {
return ComparisonMode.FORCE_STRICT;
} else {
return ComparisonMode.LOOSE;
}
}
}
public static class ClassScope {
private String name;
private MethodTree mainMethod;
private boolean interfaceScope = false;
private boolean enumScope = false;
private boolean isComplexEnum = false;
private boolean enumWrapperClassScope = false;
private boolean stringEnumScope = 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 anonymousClasses = new ArrayList<>();
private List anonymousClassesConstructors = new ArrayList<>();
private List> finalVariables = new ArrayList<>();
private boolean hasConstructorOverloadWithSuperClass;
private List fieldsWithInitializers = new ArrayList<>();
private List inlinedConstructorArgs = null;
private List localClasses = new ArrayList<>();
private List generatedMethodNames = 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 boolean constructor = false;
private boolean decoratorScope = false;
public Stack foreachLoopContext;
private final JSweetContext context;
private final CompilationUnitTree compilationUnit;
private ClassScope(JSweetContext context, CompilationUnitTree compilationUnit) {
this.context = context;
this.compilationUnit = compilationUnit;
}
public String getName() {
return name;
}
public MethodTree getMainMethod() {
return mainMethod;
}
public ExecutableElement getMainMethodElement() {
return Util.getElement(mainMethod);
}
public List getLocalClassesTypes() {
return getLocalClasses().stream() //
.map((ClassTree localClassTree) -> (DeclaredType) context.util.getType(localClassTree))
.collect(toList());
}
public boolean isLocalClassType(TypeMirror type) {
return getLocalClassesTypes().stream().anyMatch(localClassType -> localClassType.equals(type));
}
public boolean isInterfaceScope() {
return interfaceScope;
}
public boolean isEnumScope() {
return enumScope;
}
public boolean isComplexEnum() {
return isComplexEnum;
}
public boolean isEnumWrapperClassScope() {
return enumWrapperClassScope;
}
public boolean isRemovedSuperclass() {
return removedSuperclass;
}
public boolean isDeclareClassScope() {
return declareClassScope;
}
public boolean isSkipTypeAnnotations() {
return skipTypeAnnotations;
}
public boolean isDefaultMethodScope() {
return defaultMethodScope;
}
public boolean isEraseVariableTypes() {
return eraseVariableTypes;
}
public void setEraseVariableTypes(boolean eraseVariableTypes) {
this.eraseVariableTypes = eraseVariableTypes;
}
public boolean isHasDeclaredConstructor() {
return hasDeclaredConstructor;
}
public boolean getInnerClass() {
return innerClass;
}
public boolean isInnerClassNotStatic() {
return innerClassNotStatic;
}
public boolean isHasInnerClass() {
return hasInnerClass;
}
public List getAnonymousClasses() {
return anonymousClasses;
}
public List getAnonymousClassesConstructors() {
return anonymousClassesConstructors;
}
public NewClassTree getAnonymousClassConstructorFromClassTree(ClassTree classTree) {
int anonymousClassIndex = anonymousClasses.indexOf(classTree);
if (anonymousClassIndex == -1) {
return null;
}
return anonymousClassesConstructors.get(anonymousClassIndex);
}
public List> getFinalVariables() {
return finalVariables;
}
public boolean isHasConstructorOverloadWithSuperClass() {
return hasConstructorOverloadWithSuperClass;
}
public List getFieldsWithInitializers() {
return fieldsWithInitializers;
}
public List getInlinedConstructorArgs() {
return inlinedConstructorArgs;
}
public List getLocalClasses() {
return localClasses;
}
public List getGeneratedMethodNames() {
return generatedMethodNames;
}
public boolean isAnonymousClass() {
return isAnonymousClass;
}
public boolean isInnerClass() {
return isInnerClass;
}
public boolean isLocalClass() {
return isLocalClass;
}
public boolean isConstructor() {
return constructor;
}
public boolean isDecoratorScope() {
return decoratorScope;
}
}
private static class ForeachLoopScope {
public EnhancedForLoopTree loop;
public String variableName;
public String arrayName;
public ForeachLoopScope(EnhancedForLoopTree loop) {
super();
this.loop = loop;
}
}
private Stack scope = new Stack<>();
private boolean isAnnotationScope = false;
private boolean isDefinitionScope = false;
protected final boolean isTopLevelScope() {
return getIndent() == 0;
}
protected final ClassScope getScope() {
return scope.peek();
}
protected final ClassScope getScope(int i) {
return scope.get(scope.size() - 1 - i);
}
/**
* Enters a new class scope.
*
* @see #exitScope()
*/
public void enterScope() {
scope.push(new ClassScope(context, getCompilationUnit()));
}
/**
* Exits a class scope.
*
* @see #enterScope()
*/
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,
CompilationUnitTree compilationUnit, boolean fillSourceMap) {
super(logHandler, context, compilationUnit, adapter, fillSourceMap);
}
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 TYPE_MAPPING;
static {
Map 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 CONSTRUCTOR_TYPE_MAPPING;
static {
Map 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 PackageElement topLevelPackage;
public void useModule(ModuleImportDescriptor moduleImport) {
if (moduleImport != null) {
useModule(false, moduleImport.isDirect(), moduleImport.getTargetPackage(), null,
moduleImport.getImportedName(), moduleImport.getPathToImportedClass(), null);
}
}
private void useModule(boolean require, boolean direct, PackageElement targetPackage, Tree sourceTree,
String targetName, String moduleName, Element sourceElement) {
// avoid imports for names of types that are declared in this file
for (Tree t : getCompilationUnit().getTypeDecls()) {
if (t instanceof ClassTree) {
if (targetName.equals( Util.getElement(((ClassTree)t)).getSimpleName().toString())) {
return;
}
}
}
if (context.useModules && targetPackage != null) {
context.packageDependencies.add((PackageElement) targetPackage);
PackageElement packageElement = Util.getElement(compilationUnit.getPackage());
context.packageDependencies.add(packageElement);
context.packageDependencies.addEdge(packageElement, (PackageElement) targetPackage);
}
context.registerUsedModule(moduleName);
Set 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...
if (context.isExcludedSourcePath(util().getSourceFilePath(sourceElement))) {
// ignore excluded source files
return;
}
if (sourceElement instanceof TypeElement
&& context.hasAnnotationType(sourceElement, JSweetConfig.ANNOTATION_DECORATOR)) {
context.forceTopImports();
}
if (!context.moduleBundleMode && sourceElement instanceof TypeElement
&& util().isSourceElement(sourceElement)
&& !(sourceElement instanceof TypeElement && context.referenceAnalyzer != null
&& context.referenceAnalyzer.isDependent(compilationUnit,
(TypeElement) sourceElement))) {
// import as footer statements to avoid cyclic dependencies
// as much as possible
// note that the better way to avoid cyclic dependency
// issues is to create bundles
context.addTopFooterStatement("import." + targetName,
"import { " + targetName + " } from '" + moduleName + "';\n");
} else {
if (direct) {
context.addHeader("import." + targetName, "import " + targetName + " = " + moduleName + ";\n");
} else {
boolean fullImport = require || GLOBALS_CLASS_NAME.equals(targetName);
if (fullImport) {
if (context.useRequireForModules) {
context.addHeader("import." + targetName, "import " + targetName + " = require("
+ getStringLiteralQuote() + moduleName + getStringLiteralQuote() + ");\n");
} else {
context.addHeader("import." + targetName,
"import * as " + targetName + " from '" + moduleName + "';\n");
}
} else {
context.addHeader("import." + targetName,
"import { " + targetName + " } from '" + moduleName + "';\n");
}
}
}
}
context.registerImportedName(compilationUnit.getSourceFile().getName(), sourceElement, targetName);
}
}
private boolean checkRootPackageParent(CompilationUnitTree topLevel, PackageElement rootPackage,
PackageElement 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 (Element s : parentPackage.getEnclosedElements()) {
if (s instanceof TypeElement) {
if (util().isSourceElement(s)) {
report(topLevel.getPackageName(), JSweetProblem.CLASS_OUT_OF_ROOT_PACKAGE_SCOPE,
util().getQualifiedName(s), rootPackage.getQualifiedName().toString());
return false;
}
}
}
return checkRootPackageParent(topLevel, rootPackage, util().getParentPackage(parentPackage));
}
private ModuleImportDescriptor getModuleImportDescriptor(String importedName, TypeElement importedClass) {
return getAdapter().getModuleImportDescriptor(createExtendedElement(compilationUnit), importedName,
importedClass);
}
private boolean isMappedOrErasedType(Element typeElement) {
return context.isMappedType(util().getQualifiedName(typeElement))
|| context.hasAnnotationType(typeElement, JSweetConfig.ANNOTATION_ERASED);
}
/**
* Ensures that the module that corresponds to the given element is used
* (imported).
*/
public void ensureModuleIsUsed(Element element) {
if (context.useModules) {
if (element instanceof TypeElement) {
ModuleImportDescriptor moduleImport = getAdapter().getModuleImportDescriptor(
getCompilationUnitElement(), element.getSimpleName().toString(), (TypeElement) element);
if (moduleImport != null) {
useModule(moduleImport);
}
}
}
}
/**
* Prints a compilation unit tree.
*/
@Override
public Void visitCompilationUnit(final CompilationUnitTree compilationUnit, final Trees trees) {
PackageElement packageElement = Util.getElement(compilationUnit.getPackage());
if (context.isPackageErased(packageElement)) {
return returnNothing();
}
String packageFullName = util().getPackageFullNameForCompilationUnit(compilationUnit);
isDefinitionScope = packageFullName.startsWith(JSweetConfig.LIBS_PACKAGE + ".");
if (packageElement != null && context.hasAnnotationType(packageElement, JSweetConfig.ANNOTATION_MODULE)) {
context.addExportedElement(
context.getAnnotationValue(packageElement, JSweetConfig.ANNOTATION_MODULE, String.class, null),
packageElement, getCompilationUnit());
}
PackageElement rootPackage = context.getFirstEnclosingRootPackage(packageElement);
if (rootPackage != null) {
if (!checkRootPackageParent(compilationUnit, rootPackage,
util().getParentPackage((PackageElement) rootPackage))) {
return returnNothing();
}
}
context.importedTopPackages.clear();
context.rootPackages.add(rootPackage);
topLevelPackage = context.getTopLevelPackage(packageElement);
if (topLevelPackage != null) {
context.topLevelPackageNames.add(topLevelPackage.getQualifiedName().toString());
}
footer.delete(0, footer.length());
setCompilationUnit(compilationUnit);
getAdapter().beforeCompilationUnit();
boolean globalModule = JSweetConfig.GLOBALS_PACKAGE_NAME.equals(packageFullName)
|| packageFullName.endsWith("." + JSweetConfig.GLOBALS_PACKAGE_NAME);
String rootRelativePackageName = "";
if (!globalModule) {
rootRelativePackageName = getRootRelativeName(packageElement);
if (rootRelativePackageName.length() == 0) {
globalModule = true;
}
}
List packageSegments = new ArrayList(Arrays.asList(rootRelativePackageName.split("\\.")));
packageSegments.retainAll(JSweetConfig.TS_TOP_LEVEL_KEYWORDS);
if (!packageSegments.isEmpty()) {
report(compilationUnit.getPackageName(), JSweetProblem.PACKAGE_NAME_CONTAINS_KEYWORD, packageSegments);
}
detectAndUseImportedModules(compilationUnit);
detectAndUseModulesFromReferencedTypes(compilationUnit);
// require root modules when using fully qualified names or reserved
// keywords
new TreeScanner() {
Stack stack = new Stack<>();
@Override
public Void scan(Tree tree, Trees trees) {
if (tree != null) {
stack.push(tree);
try {
super.scan(tree, trees);
} finally {
stack.pop();
}
}
return returnNothing();
}
@SuppressWarnings("unchecked")
public T getParent(Class 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;
}
@Override
public Void visitIdentifier(final IdentifierTree identifier, final Trees trees) {
PackageElement compilationUnitPackageElement = Util.getElement(compilationUnit.getPackage());
Element identifierElement = Util.getElement(identifier);
if (identifierElement instanceof PackageElement) {
// ignore packages in imports
if (getParent(ImportTree.class) != null) {
return returnNothing();
}
boolean isSourceType = false;
for (int i = stack.size() - 2; i >= 0; i--) {
Tree tree = stack.get(i);
if (!(tree instanceof MemberSelectTree)) {
break;
} else {
MemberSelectTree selectTree = (MemberSelectTree) tree;
Element selectElement = Util.getElement(selectTree);
if ((selectElement instanceof TypeElement) && util().isSourceElement(selectElement)) {
isSourceType = true;
break;
}
}
}
if (!isSourceType) {
return returnNothing();
}
PackageElement identifierPackage = (PackageElement) identifierElement;
String pathToModulePackage = util().getRelativePath(compilationUnitPackageElement,
identifierPackage);
if (pathToModulePackage == null) {
return returnNothing();
}
File moduleFile = new File(new File(pathToModulePackage), JSweetConfig.MODULE_FILE_NAME);
if (!identifierPackage.getSimpleName().toString()
.equals(compilationUnitPackageElement.getSimpleName().toString())) {
useModule(false, false, identifierPackage, identifier,
identifierPackage.getSimpleName().toString(), moduleFile.getPath().replace('\\', '/'),
null);
}
} else if (identifierElement instanceof TypeElement) {
if (JSweetConfig.GLOBALS_PACKAGE_NAME
.equals(identifierElement.getEnclosingElement().getSimpleName().toString())) {
String pathToModulePackage = util().getRelativePath(compilationUnitPackageElement,
identifierElement.getEnclosingElement());
if (pathToModulePackage == null) {
return returnNothing();
}
File moduleFile = new File(new File(pathToModulePackage), JSweetConfig.MODULE_FILE_NAME);
if (!identifierElement.getEnclosingElement().getSimpleName().toString()
.equals(compilationUnitPackageElement.getSimpleName().toString())) {
useModule(false, false, (PackageElement) identifierElement.getEnclosingElement(),
identifier, JSweetConfig.GLOBALS_PACKAGE_NAME,
moduleFile.getPath().replace('\\', '/'), null);
}
}
}
return returnNothing();
}
@Override
public Void visitMethodInvocation(final MethodInvocationTree invocation, final Trees trees) {
PackageElement compilationUnitPackageElement = Util.getElement(compilationUnit.getPackage());
// TODO: same for static variables
if (invocation.getMethodSelect() instanceof IdentifierTree && JSweetConfig.TS_STRICT_MODE_KEYWORDS
.contains(invocation.getMethodSelect().toString().toLowerCase())) {
Element invokedMethodElement = Util.getElement((IdentifierTree) invocation.getMethodSelect());
PackageElement invocationPackage = util().getParentElement(invokedMethodElement,
PackageElement.class);
String rootRelativeInvocationPackageName = getRootRelativeName(invocationPackage);
if (rootRelativeInvocationPackageName.indexOf('.') == -1) {
return super.visitMethodInvocation(invocation, trees);
}
String targetRootPackageName = rootRelativeInvocationPackageName.substring(0,
rootRelativeInvocationPackageName.indexOf('.'));
String pathToReachRootPackage = util().getRelativePath(
"/" + compilationUnitPackageElement.getQualifiedName().toString().replace('.', '/'),
"/" + targetRootPackageName);
if (pathToReachRootPackage == null) {
return super.visitMethodInvocation(invocation, trees);
}
File moduleFile = new File(new File(pathToReachRootPackage), JSweetConfig.MODULE_FILE_NAME);
if (!invocationPackage.toString()
.equals(compilationUnitPackageElement.getSimpleName().toString())) {
useModule(false, false, invocationPackage, invocation, targetRootPackageName,
moduleFile.getPath().replace('\\', '/'), null);
}
}
return super.visitMethodInvocation(invocation, trees);
}
};
// 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 ");
} else if (context.moduleBundleMode) {
print("export ");
}
print("namespace ").print(rootRelativePackageName).print(" {").startIndent().println();
}
for (ImportTree def : compilationUnit.getImports()) {
print(def);
}
for (ClassTree def : util().getSortedClassDeclarations(compilationUnit.getTypeDecls(), compilationUnit)) {
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;
return returnNothing();
}
private void detectAndUseModulesFromReferencedTypes(CompilationUnitTree compilationUnit) {
if (context.useModules) {
new UsedTypesScanner().scan(compilationUnit, context.trees);
}
}
private void detectAndUseImportedModules(CompilationUnitTree compilationUnit) {
// generate requires by looking up imported external modules
for (ImportTree importDecl : compilationUnit.getImports()) {
TreeScanner importedModulesScanner = new TreeScanner<>() {
@Override
public Void scan(Tree tree, Trees trees) {
if (tree instanceof MemberSelectTree) {
MemberSelectTree qualified = (MemberSelectTree) tree;
Element memberSelectElement = Util.getElement(tree);
if (memberSelectElement != null) {
// regular import case (qualified.sym is a package)
if (context.hasAnnotationType(memberSelectElement, JSweetConfig.ANNOTATION_MODULE)) {
String targetName = createImportAliasFromFieldAccess(qualified);
String actualName = context.getAnnotationValue(memberSelectElement,
JSweetConfig.ANNOTATION_MODULE, String.class, null);
useModule(true, false, null, importDecl, targetName, actualName, memberSelectElement);
}
} else {
// static import case (imported fields and methods)
if (qualified.getExpression() instanceof MemberSelectTree) {
MemberSelectTree qualifier = (MemberSelectTree) qualified.getExpression();
Element subMemberSelectElement = Util.getElement(qualifier);
if (subMemberSelectElement != null) {
try {
for (Element importedMember : subMemberSelectElement.getEnclosedElements()) {
if (qualified.getIdentifier().equals(importedMember.getSimpleName())) {
if (context.hasAnnotationType(importedMember,
JSweetConfig.ANNOTATION_MODULE)) {
String targetName = createImportAliasFromSymbol(importedMember);
String actualName = context.getAnnotationValue(importedMember,
JSweetConfig.ANNOTATION_MODULE, String.class, null);
useModule(true, false, null, importDecl, targetName, actualName,
importedMember);
break;
}
}
}
} catch (Exception e) {
logger.error("error occurred collecting modules from static imports", e);
}
}
}
}
}
return super.scan(tree, trees);
}
};
importedModulesScanner.scan(importDecl.getQualifiedIdentifier(), context.trees);
for (ImportTree importTree : compilationUnit.getImports()) {
if (importTree.getQualifiedIdentifier() instanceof MemberSelectTree) {
MemberSelectTree qualified = (MemberSelectTree) importTree.getQualifiedIdentifier();
String importedName = qualified.getIdentifier().toString();
if (importTree.isStatic() && (qualified.getExpression() instanceof MemberSelectTree)) {
qualified = (MemberSelectTree) qualified.getExpression();
}
Element qualifiedElement = Util.getElement(qualified);
if (qualifiedElement instanceof TypeElement) {
boolean globals = JSweetConfig.GLOBALS_CLASS_NAME
.equals(qualifiedElement.getSimpleName().toString());
if (!globals) {
importedName = qualified.getIdentifier().toString();
}
TypeElement importedClass = (TypeElement) qualifiedElement;
String qualId = importTree.getQualifiedIdentifier().toString();
String adaptedQualId = getAdapter().needsImport(createExtendedElement(importTree), qualId);
if (globals || adaptedQualId != null) {
ModuleImportDescriptor moduleImport = getModuleImportDescriptor(importedName,
importedClass);
if (moduleImport != null) {
useModule(false, moduleImport.isDirect(), moduleImport.getTargetPackage(), importTree,
moduleImport.getImportedName(), moduleImport.getPathToImportedClass(), null);
}
}
}
}
}
}
}
private String createImportAliasFromFieldAccess(MemberSelectTree access) {
String name = extractNameFromAnnotatedSymbol(Util.getElement(access));
if (name != null) {
return name;
} else {
return access.getIdentifier().toString();
}
}
private String createImportAliasFromSymbol(Element symbol) {
String name = extractNameFromAnnotatedSymbol(symbol);
if (name != null) {
return name;
} else {
return symbol.getSimpleName().toString();
}
}
private String extractNameFromAnnotatedSymbol(Element symbol) {
if (context.hasAnnotationType(symbol, JSweetConfig.ANNOTATION_NAME)) {
return context.getAnnotationValue(symbol, JSweetConfig.ANNOTATION_NAME, String.class, null);
} else {
return null;
}
}
private void printDocComment(Tree element) {
printDocComment(element, false);
}
private void printDocComment(Tree tree, boolean newline) {
if (compilationUnit != null) {
TreePath treePath = TreePath.getPath(compilationUnit, tree);
Element element = Util.getElement(tree);
if (treePath == null && element != null) {
treePath = trees().getPath(element);
}
String docComment = trees().getDocComment(treePath);
String commentText = JSDoc.adaptDocComment(context, treePath, tree, docComment);
if (element != null) {
commentText = getAdapter().adaptDocComment(element, commentText);
}
List lines = new ArrayList<>();
if (commentText != null) {
lines.addAll(Arrays.asList(commentText.split("\n")));
}
if (!lines.isEmpty()) {
if (newline) {
println().printIndent();
}
print("/**").println();
for (String line : lines) {
printIndent().print(" * ").print(line.trim()).println();
}
removeLastChar();
println().printIndent().print(" ").print("*/").println();
printIndent();
}
}
}
private void printAnonymousClassTypeArgs(NewClassTree newClass) {
if ((newClass.getIdentifier() instanceof ParameterizedTypeTree)) {
ParameterizedTypeTree tapply = (ParameterizedTypeTree) newClass.getIdentifier();
if (tapply.getTypeArguments() != null && !tapply.getTypeArguments().isEmpty()) {
boolean printed = false;
print("<");
for (Tree typeArg : tapply.getTypeArguments()) {
TypeMirror typeArgType = Util.getType(typeArg);
if (typeArgType instanceof TypeVariable) {
printed = true;
print(typeArg).print(", ");
}
}
if (printed) {
removeLastChars(2);
print(">");
} else {
removeLastChar();
}
}
}
}
protected 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;
public final AbstractTreePrinter substituteAndPrintType(Tree typeTree) {
return substituteAndPrintType(typeTree, false, inTypeParameters, true, disableTypeSubstitution);
}
private AbstractTreePrinter printArguments(List arguments) {
int i = 1;
for (Tree argument : arguments) {
printArgument(argument, i++).print(", ");
}
if (arguments.size() > 0) {
removeLastChars(2);
}
return this;
}
private AbstractTreePrinter printArgument(Tree argument, int i) {
print("p" + i + ": ");
substituteAndPrintType(argument, false, false, true, false);
return this;
}
private AbstractTreePrinter substituteAndPrintType(Tree typeTree, boolean arrayComponent, boolean inTypeParameters,
boolean completeRawTypes, boolean disableSubstitution) {
if (typeTree instanceof IntersectionTypeTree) {
for (Tree t : ((IntersectionTypeTree) typeTree).getBounds()) {
substituteAndPrintType(t, arrayComponent, inTypeParameters, completeRawTypes, disableSubstitution);
print(" & ");
}
removeLastChars(3);
return this;
}
Element typeElement = Util.getTypeElement(typeTree);
TypeMirror typeType = Util.getType(typeTree);
if (typeElement instanceof TypeParameterElement) {
if (getAdapter().typeVariablesToErase.contains(typeElement)) {
return print("any");
}
}
if (!disableSubstitution) {
if (context.hasAnnotationType(typeElement, ANNOTATION_ERASED)) {
return print("any");
}
if (context.hasAnnotationType(typeElement, ANNOTATION_OBJECT_TYPE)) {
// TODO: in case of object types, we should replace with the org
// object type...
return print("any");
}
String typeFullName = util().getQualifiedName(typeType);
if (Runnable.class.getName().equals(typeFullName)) {
if (arrayComponent) {
print("(");
}
print("() => void");
if (arrayComponent) {
print(")");
}
return this;
}
if (typeTree instanceof ParameterizedTypeTree) {
ParameterizedTypeTree typeApply = ((ParameterizedTypeTree) typeTree);
TypeElement parametrizedTypeElement = Util.getTypeElement(typeApply.getType());
String typeName = parametrizedTypeElement.getSimpleName().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 (Tree argument : typeApply.getTypeArguments()) {
substituteAndPrintType(argument, arrayComponent, inTypeParameters, completeRawTypes, false)
.print(",");
}
if (typeApply.getTypeArguments().size() > 0) {
removeLastChar();
}
print("]");
return this;
}
if (typeFullName.startsWith(UNION_CLASS_NAME)) {
print("(");
for (Tree argument : typeApply.getTypeArguments()) {
print("(");
substituteAndPrintType(argument, arrayComponent, inTypeParameters, completeRawTypes, false);
print(")");
print("|");
}
if (typeApply.getTypeArguments().size() > 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.getTypeArguments());
}
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.getTypeArguments().subList(0, typeApply.getTypeArguments().size() - 1));
}
print(") => ");
substituteAndPrintType(util().last(typeApply.getTypeArguments()), 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.getTypeArguments().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.getTypeArguments());
}
print(") => boolean");
if (arrayComponent) {
print(")");
}
return this;
} else if (typeName.endsWith("Operator")) {
if (arrayComponent) {
print("(");
}
print("(");
printArgument(typeApply.getTypeArguments().get(0), 1);
if (typeName.startsWith("Binary")) {
print(", ");
printArgument(typeApply.getTypeArguments().get(0), 2);
}
print(") => ");
substituteAndPrintType(typeApply.getTypeArguments().get(0), arrayComponent, inTypeParameters,
completeRawTypes, false);
if (arrayComponent) {
print(")");
}
return this;
}
}
if (typeType.toString().startsWith(Class.class.getName() + "<")) {
return print("any");
}
} else {
if (!(typeTree instanceof ArrayTypeTree) && 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 && typeElement != null
&& !((TypeElement) typeElement).getTypeParameters().isEmpty()
&& !context.getTypeMappingTarget(typeFullName).equals("any")) {
printAnyTypeArguments(((TypeElement) typeElement).getTypeParameters().size());
}
}
return this;
}
}
for (BiFunction mapping : context.getFunctionalTypeMappings()) {
Object mapped = mapping.apply(createExtendedElement(typeTree), typeFullName);
if (mapped instanceof String) {
print((String) mapped);
return this;
} else if (mapped instanceof Tree) {
substituteAndPrintType((Tree) mapped);
return this;
} else if (mapped instanceof TypeMirror) {
print(getAdapter().getMappedType((TypeMirror) mapped));
return this;
}
}
for (Function mapping : context.getFunctionalTypeMirrorMappings()) {
String mapped = mapping.apply(typeType);
if (mapped != null) {
print(mapped);
return this;
}
}
}
if (typeTree instanceof ParameterizedTypeTree) {
ParameterizedTypeTree typeApply = ((ParameterizedTypeTree) typeTree);
substituteAndPrintType(typeApply.getType(), arrayComponent, inTypeParameters, false, disableSubstitution);
if (!typeApply.getTypeArguments().isEmpty() && !"any".equals(getLastPrintedString(3))
&& !"Object".equals(getLastPrintedString(6))) {
print("<");
for (Tree argument : typeApply.getTypeArguments()) {
substituteAndPrintType(argument, arrayComponent, false, completeRawTypes, false).print(", ");
}
if (typeApply.getTypeArguments().size() > 0) {
removeLastChars(2);
}
print(">");
}
return this;
} else if (typeTree instanceof WildcardTree) {
WildcardTree wildcard = ((WildcardTree) 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 ArrayTypeTree) {
return substituteAndPrintType(((ArrayTypeTree) typeTree).getType(), true, inTypeParameters,
completeRawTypes, disableSubstitution).print("[]");
}
if (completeRawTypes && (typeElement instanceof TypeElement)
&& ((TypeElement) typeElement).getTypeParameters() != null
&& !((TypeElement) typeElement).getTypeParameters().isEmpty()) {
// raw type case (Java warning)
print(typeTree);
print("<");
for (int i = 0; i < ((TypeElement) typeElement).getTypeParameters().size(); i++) {
print("any, ");
}
removeLastChars(2);
print(">");
return this;
} else {
return print(typeTree);
}
}
}
private String getClassName(TypeElement clazz) {
String name;
if (context.hasClassNameMapping(clazz)) {
name = context.getClassNameMapping(clazz);
} else {
name = clazz.getSimpleName().toString();
}
if (clazz.getKind() == ElementKind.ENUM) {
name += ENUM_WRAPPER_CLASS_SUFFIX;
}
return name;
}
@Override
public Void visitClass(final ClassTree classTree, final Trees trees) {
TypeElement classTypeElement = Util.getElement(classTree);
if (getAdapter().substituteType(classTypeElement)) {
getAdapter().afterType(classTypeElement);
return null;
}
if (context.isIgnored(classTree, compilationUnit)) {
getAdapter().afterType(classTypeElement);
return returnNothing();
}
String name = classTree.getSimpleName().toString();
if (context.hasClassNameMapping(classTypeElement)) {
name = context.getClassNameMapping(classTypeElement);
}
if (!scope.isEmpty() && getScope().anonymousClasses.contains(classTree)) {
name = getScope().name + ANONYMOUS_PREFIX + getScope().anonymousClasses.indexOf(classTree);
}
Tree testParent = getFirstParent(ClassTree.class, MethodTree.class);
if (testParent != null && testParent instanceof MethodTree) {
if (!isLocalClass()) {
getScope().localClasses.add(classTree);
return returnNothing();
}
}
enterScope();
getScope().name = name;
ClassTree parent = getParent(ClassTree.class);
List parentTypeVars = new ArrayList<>();
if (parent != null) {
getScope().innerClass = true;
if (!classTree.getModifiers().getFlags().contains(Modifier.STATIC)
&& (getScope(1).getAnonymousClassConstructorFromClassTree(classTree) == null
|| !isStaticAnonymousClass(getScope(1).getAnonymousClassConstructorFromClassTree(classTree),
getCompilationUnit()))) {
getScope().innerClassNotStatic = true;
if (parent.getTypeParameters() != null) {
parentTypeVars.addAll(parent.getTypeParameters().stream()
.map(t -> (TypeParameterElement) Util.getTypeElement(t)).collect(Collectors.toList()));
getAdapter().typeVariablesToErase.addAll(parentTypeVars);
}
}
}
getScope().declareClassScope = context.hasAnnotationType(classTypeElement, JSweetConfig.ANNOTATION_AMBIENT)
|| isDefinitionScope;
getScope().interfaceScope = false;
getScope().removedSuperclass = false;
getScope().enumScope = false;
getScope().enumWrapperClassScope = false;
if (getScope().declareClassScope) {
if (context.hasAnnotationType(classTypeElement, JSweetConfig.ANNOTATION_DECORATOR)) {
print("declare function ").print(name).print("(...args: any[]);").println();
exitScope();
return returnNothing();
}
} else {
if (context.lookupDecoratorAnnotation(classTypeElement.getQualifiedName().toString()) != null) {
boolean requiresDecoratorFunction = context.getAnnotationValue(classTypeElement,
JSweetConfig.ANNOTATION_DECORATOR, Boolean.class, true);
if (requiresDecoratorFunction) {
GlobalMethodInfos globalDecoratorFunction = context
.lookupGlobalMethod(classTypeElement.getQualifiedName().toString());
if (globalDecoratorFunction == null) {
report(classTree, JSweetProblem.CANNOT_FIND_GLOBAL_DECORATOR_FUNCTION,
classTypeElement.getQualifiedName());
} else {
CompilationUnitTree previousCompilUnit = getCompilationUnit();
getScope().decoratorScope = true;
compilationUnit = globalDecoratorFunction.compilationUnitTree;
try {
enter(globalDecoratorFunction.classTree);
print(globalDecoratorFunction.methodTree);
exit();
} finally {
getScope().decoratorScope = false;
compilationUnit = previousCompilUnit;
}
}
exitScope();
return returnNothing();
}
}
}
HashSet defaultMethods = null;
boolean globals = JSweetConfig.GLOBALS_CLASS_NAME.equals(classTree.getSimpleName().toString());
if (globals && classTree.getExtendsClause() != null) {
report(classTree, JSweetProblem.GLOBALS_CLASS_CANNOT_HAVE_SUPERCLASS);
}
List implementedInterfaces = new ArrayList<>();
if (!globals) {
if (classTree.getExtendsClause() != null && JSweetConfig.GLOBALS_CLASS_NAME
.equals(Util.getTypeElement(classTree.getExtendsClause()).getSimpleName().toString())) {
report(classTree, JSweetProblem.GLOBALS_CLASS_CANNOT_BE_SUBCLASSED);
return returnNothing();
}
if (!(classTree.getKind() == Kind.ENUM && scope.size() > 1 && getScope(1).isComplexEnum)) {
printDocComment(classTree);
} else {
print("/** @ignore */").println().printIndent();
}
print(classTree.getModifiers());
if (!isTopLevelScope() || context.useModules || context.moduleBundleMode || isAnonymousClass()
|| isInnerClass() || isLocalClass()) {
print("export ");
}
if (context.isInterface(classTypeElement)) {
print("interface ");
getScope().interfaceScope = true;
} else {
if (classTree.getKind() == Kind.ENUM) {
if (getScope().declareClassScope && !(getIndent() != 0 && isDefinitionScope)) {
print("declare ");
}
if (scope.size() > 1 && getScope(1).isComplexEnum) {
if (util().hasAbstractMethod(classTypeElement)) {
print("abstract ");
}
print("class ");
getScope().enumWrapperClassScope = true;
} else {
print("enum ");
getScope().enumScope = true;
if (context.hasAnnotationType(classTypeElement, ANNOTATION_STRING_ENUM)) {
getScope().stringEnumScope = true;
getScope().enumWrapperClassScope = false;
getScope().isComplexEnum = false;
}
}
} else {
if (getScope().declareClassScope && !(getIndent() != 0 && isDefinitionScope)) {
print("declare ");
}
defaultMethods = new HashSet<>();
util().findDefaultMethodsInType(defaultMethods, context, classTypeElement);
TypeElement superClass = Util.getElement(classTypeElement.getSuperclass());
if (util().isSourceElement(superClass) && !isMappedOrErasedType(superClass)) {
Set superClassDefaultMethods = new HashSet<>();
util().findDefaultMethodsInType(superClassDefaultMethods, context, superClass);
defaultMethods.removeAll(superClassDefaultMethods);
}
if (classTree.getModifiers().getFlags().contains(Modifier.ABSTRACT)) {
print("abstract ");
}
print("class ");
}
}
print(name + (getScope().enumWrapperClassScope ? ENUM_WRAPPER_CLASS_SUFFIX : ""));
if (classTree.getTypeParameters() != null && classTree.getTypeParameters().size() > 0) {
print("<").printArgList(null, classTree.getTypeParameters()).print(">");
} else if (isAnonymousClass()) {
NewClassTree newClass = getScope(1).anonymousClassesConstructors
.get(getScope(1).anonymousClasses.indexOf(classTree));
if (isStaticAnonymousClass(newClass, getCompilationUnit())) {
printAnonymousClassTypeArgs(newClass);
}
}
TypeMirror mixin = null;
if (context.hasAnnotationType(classTypeElement, JSweetConfig.ANNOTATION_MIXIN)) {
mixin = context.getAnnotationValue(classTypeElement, JSweetConfig.ANNOTATION_MIXIN, TypeMirror.class,
null);
for (AnnotationMirror c : classTypeElement.getAnnotationMirrors()) {
if (JSweetConfig.ANNOTATION_MIXIN.equals(c.getAnnotationType().toString())) {
Entry annotationValuesEntry = firstOrDefault(
c.getElementValues().entrySet());
AnnotationValue mixinClassAnnotationValue = annotationValuesEntry.getValue();
TypeMirror mixinClassType = (TypeMirror) mixinClassAnnotationValue.getValue();
TypeElement valueTypeElement = (TypeElement) types().asElement(mixinClassType);
String targetName = getRootRelativeName(valueTypeElement);
String mixinName = getRootRelativeName(classTypeElement);
if (!mixinName.equals(targetName)) {
report(classTree, JSweetProblem.WRONG_MIXIN_NAME, mixinName, targetName);
} else {
if (valueTypeElement.equals(classTypeElement)) {
report(classTree, JSweetProblem.SELF_MIXIN_TARGET, mixinName);
}
}
}
}
}
// print EXTENDS
boolean extendsInterface = false;
if (classTree.getExtendsClause() != null) {
TypeElement superTypeElement = Util.getTypeElement(classTree.getExtendsClause());
TypeMirror superType = superTypeElement.asType();
boolean removeIterable = false;
if (context.hasAnnotationType(classTypeElement, JSweetConfig.ANNOTATION_SYNTACTIC_ITERABLE)
&& superTypeElement.getQualifiedName().toString().equals(Iterable.class.getName())) {
removeIterable = true;
}
if (!getAdapter().substituteExtends(classTypeElement)) {
if (!removeIterable && !JSweetConfig.isJDKReplacementMode()
&& !(JSweetConfig.OBJECT_CLASSNAME.equals(superType.toString())
|| Object.class.getName().equals(superType.toString()))
&& !(mixin != null && types().isSameType(mixin, superType))
&& !(getAdapter().eraseSuperClass(classTypeElement, superTypeElement))) {
if (!getScope().interfaceScope && context.isInterface(superTypeElement)) {
extendsInterface = true;
print(" implements ");
implementedInterfaces.add(superType);
} else {
print(" extends ");
}
if (getScope().enumWrapperClassScope && getScope(1).anonymousClasses.contains(classTree)) {
print(classTree.getExtendsClause().toString() + ENUM_WRAPPER_CLASS_SUFFIX);
} else {
disableTypeSubstitution = !getAdapter().isSubstituteSuperTypes();
substituteAndPrintType(classTree.getExtendsClause());
disableTypeSubstitution = false;
}
if (context.classesWithWrongConstructorOverload.contains(classTypeElement)) {
getScope().hasConstructorOverloadWithSuperClass = true;
}
} else {
getScope().removedSuperclass = true;
}
}
}
// print IMPLEMENTS
if (!getAdapter().substituteImplements(classTypeElement)) {
if (classTree.getImplementsClause() != null && !classTree.getImplementsClause().isEmpty()
&& !getScope().enumScope) {
List implementing = new ArrayList<>(classTree.getImplementsClause());
if (context.hasAnnotationType(classTypeElement, JSweetConfig.ANNOTATION_SYNTACTIC_ITERABLE)) {
for (Tree implementsTree : classTree.getImplementsClause()) {
TypeElement implementsElement = Util.getTypeElement(implementsTree);
if (implementsElement.getQualifiedName().toString().equals(Iterable.class.getName())) {
implementing.remove(implementsTree);
}
}
}
for (Tree implementsTree : classTree.getImplementsClause()) {
TypeElement implementsElement = Util.getTypeElement(implementsTree);
// erase Java interfaces
if (context.isFunctionalType(implementsElement) //
|| getAdapter().eraseSuperInterface(classTypeElement, implementsElement)) {
implementing.remove(implementsTree);
}
}
if (!implementing.isEmpty()) {
if (!extendsInterface) {
if (getScope().interfaceScope) {
print(" extends ");
} else {
print(" implements ");
}
} else {
print(", ");
}
for (Tree implementsTree : implementing) {
TypeMirror implementsType = Util.getType(implementsTree);
disableTypeSubstitution = !getAdapter().isSubstituteSuperTypes();
substituteAndPrintType(implementsTree);
disableTypeSubstitution = false;
implementedInterfaces.add(implementsType);
print(", ");
}
removeLastChars(2);
}
}
}
print(" {").println().startIndent();
}
getAdapter().beforeTypeBody(classTypeElement);
if (getScope().innerClassNotStatic && !getScope().interfaceScope && !getScope().enumScope
&& !getScope().enumWrapperClassScope) {
printIndent().print("public " + PARENT_CLASS_FIELD_NAME + ": any;").println();
}
Set injectedDefaultMethods = new HashSet<>();
Map injectedDefaultMethodMap = new HashMap<>();
if (defaultMethods != null && !defaultMethods.isEmpty()) {
for (DefaultMethodEntry entry : defaultMethods) {
ExecutableElement methodElementMatch = util().findMethodDeclarationInType2(classTypeElement,
entry.methodTree.getName().toString(), entry.methodType);
if (methodElementMatch == null || methodElementMatch == entry.methodElement) {
injectedDefaultMethods.add(entry.methodTree);
injectedDefaultMethodMap.put(entry.methodTree, entry);
}
}
}
if (getScope().enumScope) {
printIndent();
}
if (globals) {
removeLastIndent();
}
for (Tree def : classTree.getMembers()) {
if (def instanceof ClassTree) {
getScope().hasInnerClass = true;
}
if (def instanceof VariableTree) {
VariableTree var = (VariableTree) def;
VariableElement varElement = Util.getElement(var);
if (!varElement.getModifiers().contains(Modifier.STATIC) && var.getInitializer() != null) {
getScope().fieldsWithInitializers.add(var);
}
}
}
if (!globals && !getScope().enumScope && !context.isInterface(classTypeElement)
&& context.getStaticInitializerCount(classTypeElement) > 0) {
printIndent().print("static __static_initialized: boolean = false;").println();
int liCount = context.getStaticInitializerCount(classTypeElement);
String prefix = getClassName(classTypeElement) + ".";
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(classTypeElement, globals, true);
if (util().isPartOfAnEnum(classTypeElement)) {
qualifiedClassName += ENUM_WRAPPER_CLASS_SUFFIX;
}
context.addTopFooterStatement(
(isBlank(qualifiedClassName) ? "" : qualifiedClassName + ".__static_initialize();"));
}
boolean hasUninitializedFields = false;
List classMembers = new ArrayList<>();
classMembers.addAll(injectedDefaultMethods);
classMembers.addAll(classTree.getMembers());
if (context.options.isSortClassMembers()) {
List memberDefs = classMembers.stream()
.filter(t -> (t instanceof MethodTree || t instanceof VariableTree)).sorted((t1, t2) -> getAdapter()
.getClassMemberComparator().compare(createExtendedElement(t1), createExtendedElement(t2)))
.collect(Collectors.toList());
classMembers = new ArrayList<>();
classMembers.addAll(memberDefs);
for (Tree def : classTree.getMembers()) {
if (!classMembers.contains(def)) {
classMembers.add(def);
}
}
}
for (Tree def : classMembers) {
if (injectedDefaultMethods.contains(def)) {
MethodTree defaultMethod = (MethodTree) def;
DefaultMethodEntry entry = injectedDefaultMethodMap.get(defaultMethod);
getScope().defaultMethodScope = true;
getAdapter().typeVariablesToErase
.addAll(((TypeElement) entry.methodElement.getEnclosingElement()).getTypeParameters());
// scan for used types to generate imports
if (context.useModules) {
UsedTypesScanner scanner = new UsedTypesScanner();
if (!context.hasAnnotationType(entry.methodElement, JSweetConfig.ANNOTATION_ERASED)) {
if (context.hasAnnotationType(entry.methodElement, JSweetConfig.ANNOTATION_REPLACE)) {
// do not scan the method body
scanner.scan(entry.methodTree.getParameters(), trees);
scanner.scan(entry.methodTree.getReturnType(), trees);
scanner.scan(entry.methodTree.getThrows(), trees);
scanner.scan(entry.methodTree.getTypeParameters(), trees);
scanner.scan(entry.methodTree.getReceiverParameter(), trees);
} else {
scanner.scan(entry.methodTree, trees);
}
}
}
if (!context.hasAnnotationType(entry.methodElement, JSweetConfig.ANNOTATION_ERASED)) {
printIndent().print(
"/* Default method injected from " + entry.enclosingClassElement.getQualifiedName() + " */")
.println();
}
printIndent().print(defaultMethod).println();
getAdapter().typeVariablesToErase
.removeAll(((TypeElement) entry.methodElement.getEnclosingElement()).getTypeParameters());
getScope().defaultMethodScope = false;
continue;
}
if (getScope().interfaceScope) {
// static interface members are printed in a namespace
Element memberElement = Util.getElementNoErrors(def);
if (memberElement != null && (def instanceof MethodTree || def instanceof VariableTree)
&& memberElement.getModifiers().contains(Modifier.STATIC)) {
continue;
}
}
if (getScope().interfaceScope && def instanceof MethodTree) {
// object method should not be defined otherwise they will have
// to be implemented
if (util().isOverridingBuiltInJavaObjectMethod(Util.getElement(def))) {
continue;
}
}
if (def instanceof ClassTree) {
// inner types are be printed in a namespace
continue;
}
if (def instanceof VariableTree) {
if (getScope().enumScope && Util.getElement(def).getKind() != ElementKind.ENUM_CONSTANT) {
getScope().isComplexEnum = true;
continue;
}
if (shouldPrintFieldInitializationInConstructor((VariableTree) def)) {
hasUninitializedFields = true;
}
}
if (def instanceof BlockTree && !((BlockTree) def).isStatic()) {
hasUninitializedFields = true;
}
if (!getScope().enumScope) {
printIndent();
}
int pos = getCurrentPosition();
print(def);
if (getScope().enumScope && def.getKind() == Kind.VARIABLE && getScope().stringEnumScope) {
print(" = " + getStringLiteralQuote());
print(def);
print(getStringLiteralQuote());
}
if (getCurrentPosition() == pos) {
if (!getScope().enumScope) {
removeLastIndent();
}
continue;
}
if (def instanceof VariableTree) {
if (getScope().enumScope) {
print(", ");
} else {
if (getLastPrintedChar() != '}') {
print(";");
}
println().println();
}
} else {
println().println();
}
}
// generate missing abstract methods if abstract class
if (!getScope().interfaceScope && classTree.getModifiers().getFlags().contains(Modifier.ABSTRACT)) {
List methods = new ArrayList<>();
for (TypeMirror t : implementedInterfaces) {
context.grabMethodsToBeImplemented(methods, (TypeElement) types().asElement(t));
}
methods.sort( //
(ExecutableElement m1, ExecutableElement m2) //
-> m1.getSimpleName().toString().compareTo(m2.getSimpleName().toString()));
Map signatures = new HashMap<>();
for (ExecutableElement meth : methods) {
if (meth.getKind() == ElementKind.METHOD
&& !context.hasAnnotationType(meth, JSweetConfig.ANNOTATION_ERASED)
&& !isMappedOrErasedType(util().getParentElement(meth, TypeElement.class))) {
// do not generate default abstract method for already
// generated methods
if (getScope().generatedMethodNames.contains(meth.getSimpleName().toString())) {
continue;
}
ExecutableType methodType = (ExecutableType) meth.asType();
ExecutableElement s = util().findMethodDeclarationInType(classTypeElement,
meth.getSimpleName().toString(), methodType, true);
if (Object.class.getName().equals(s.getEnclosingElement().toString())) {
s = null;
}
boolean printAbstractDeclaration = false;
if (s != null) {
if (!s.getEnclosingElement().equals(classTypeElement)) {
if (!(s.isDefault() || (!context.isInterface((TypeElement) s.getEnclosingElement())
&& !s.getModifiers().contains(Modifier.ABSTRACT)))) {
printAbstractDeclaration = true;
}
}
}
if (printAbstractDeclaration) {
Overload o = context.getOverload(classTypeElement, meth);
if (o != null && o.getMethodsCount() > 1 && !o.isValid) {
if (!methodType.equals(o.getCoreEntry().methodType)) {
printAbstractDeclaration = false;
}
}
}
if (s == null || printAbstractDeclaration) {
String signature = getContext().types.erasure(methodType).toString();
String methodName = meth.getSimpleName().toString();
if (!(signatures.containsKey(methodName) && signatures.get(methodName).equals(signature))) {
printAbstractMethodDeclaration(meth);
signatures.put(methodName, signature);
}
}
}
}
}
// automated constructor generation (for inner class for instance)
if (!getScope().hasDeclaredConstructor
&& !(getScope().interfaceScope || getScope().enumScope || getScope().declareClassScope)) {
Set interfaces = new HashSet<>();
context.grabSupportedInterfaceNames(interfaces, classTypeElement, getAdapter());
if (!interfaces.isEmpty() || getScope().innerClass || 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(classTree) : -1;
if (anonymousClassIndex != -1) {
for (int i = 0; i < getScope(1).anonymousClassesConstructors.get(anonymousClassIndex).getArguments()
.size(); i++) {
if (!hasArgs) {
hasArgs = true;
} else {
print(", ");
}
print("__arg" + i + ": any");
}
for (VariableElement v : getScope(1).finalVariables.get(anonymousClassIndex)) {
if (!hasArgs) {
hasArgs = true;
} else {
print(", ");
}
print("private " + v.getSimpleName() + ": any");
}
}
print(") {").startIndent().println();
if (classTree.getExtendsClause() != null && !getScope().removedSuperclass) {
TypeElement superTypeElement = Util.getTypeElement(classTree.getExtendsClause());
if (!context.isInterface(superTypeElement)) {
printIndent().print("super(");
boolean hasArg = false;
if (getScope().innerClassNotStatic) {
if (superTypeElement.getEnclosingElement() instanceof TypeElement //
&& !superTypeElement.getModifiers().contains(Modifier.STATIC)) {
print(PARENT_CLASS_FIELD_NAME);
hasArg = true;
}
}
if (anonymousClassIndex != -1) {
for (int i = 0; i < getScope(1).anonymousClassesConstructors.get(anonymousClassIndex)
.getArguments().size(); i++) {
if (hasArg) {
print(", ");
} else {
hasArg = true;
}
print("__arg" + i);
}
}
print(");").println();
}
}
printInstanceInitialization(classTree, null);
endIndent().printIndent().print("}").println().println();
}
}
removeLastChar();
if (getScope().enumWrapperClassScope && !getScope(1).anonymousClasses.contains(classTree)) {
printIndent().print("public name(): string { return this." + ENUM_WRAPPER_CLASS_NAME + "; }").println();
printIndent().print("public ordinal(): number { return this." + ENUM_WRAPPER_CLASS_ORDINAL + "; }")
.println();
printIndent().print("public compareTo(other: any): number { return this." + ENUM_WRAPPER_CLASS_ORDINAL
+ " - (isNaN(other)?other." + ENUM_WRAPPER_CLASS_ORDINAL + ":other); }").println();
}
if (getScope().enumScope) {
removeLastChar().println();
}
getAdapter().afterTypeBody(classTypeElement);
if (!globals) {
endIndent().printIndent().print("}");
if (!getScope().interfaceScope && !getScope().declareClassScope && !getScope().enumScope
&& !(getScope().enumWrapperClassScope
&& classTypeElement.getNestingKind() == NestingKind.ANONYMOUS)) {
if (classTypeElement.getNestingKind() != NestingKind.ANONYMOUS) {
println().printIndent()
.print(getScope().enumWrapperClassScope ? classTypeElement.getSimpleName().toString()
: name)
.print("[" + getStringLiteralQuote() + CLASS_NAME_IN_CONSTRUCTOR + getStringLiteralQuote()
+ "] = ")
.print(getStringLiteralQuote() + classTypeElement.getQualifiedName().toString()
+ getStringLiteralQuote() + ";");
}
Set interfaces = new HashSet<>();
context.grabSupportedInterfaceNames(interfaces, classTypeElement, getAdapter());
if (!interfaces.isEmpty()) {
println().printIndent()
.print(getScope().enumWrapperClassScope ? classTypeElement.getSimpleName().toString()
: name)
.print("[" + getStringLiteralQuote() + INTERFACES_FIELD_NAME + getStringLiteralQuote()
+ "] = ");
print("[");
for (String itf : interfaces) {
print(getStringLiteralQuote()).print(itf).print(getStringLiteralQuote() + ",");
}
removeLastChar();
print("];").println();
}
if (!getScope().enumWrapperClassScope) {
println();
}
}
}
// enum class for complex enum
if (getScope().isComplexEnum) {
println().println().printIndent();
visitClass(classTree, trees);
}
boolean nameSpace = false;
if (getScope().interfaceScope) {
// print static members of interfaces
for (Tree def : classTree.getMembers()) {
Element memberElement = Util.getElementNoErrors(def);
if ((def instanceof MethodTree || def instanceof VariableTree)
&& memberElement.getModifiers().contains(Modifier.STATIC)) {
if (def instanceof VariableTree && context.hasAnnotationType(memberElement, ANNOTATION_STRING_TYPE,
JSweetConfig.ANNOTATION_ERASED)) {
continue;
}
if (!nameSpace) {
nameSpace = true;
println().println().printIndent();
if (getIndent() != 0 || context.useModules || context.moduleBundleMode) {
print("export ");
} else {
if (isDefinitionScope) {
print("declare ");
}
}
print("namespace ").print(classTree.getSimpleName().toString()).print(" {").startIndent();
}
println().println().printIndent().print(def);
if (def instanceof VariableTree) {
print(";");
}
}
}
if (nameSpace) {
println().endIndent().printIndent().print("}").println();
}
}
nameSpace = false;
// inner, anonymous and local classes in a namespace
// ======================
// print valid inner classes
for (Tree def : util().getSortedClassDeclarations(classTree.getMembers(), compilationUnit)) {
if (def instanceof ClassTree) {
ClassTree cdef = (ClassTree) def;
if (context.isIgnored(cdef, compilationUnit)) {
continue;
}
if (!nameSpace) {
nameSpace = true;
println().println().printIndent();
if (!isTopLevelScope() || context.useModules || context.moduleBundleMode) {
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 (ClassTree cdef : getScope().anonymousClasses) {
if (!nameSpace) {
nameSpace = true;
println().println().printIndent();
if (!isTopLevelScope() || context.useModules || context.moduleBundleMode) {
print("export ");
}
print("namespace ").print(name).print(" {").startIndent();
}
getScope().isAnonymousClass = true;
println().println().printIndent().print(cdef);
getScope().isAnonymousClass = false;
}
// print local classes
for (ClassTree 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(classTree)) {
println().printIndent().print(classTypeElement.getSimpleName().toString()).print(
"[" + getStringLiteralQuote() + ENUM_WRAPPER_CLASS_WRAPPERS + getStringLiteralQuote() + "] = {");
int index = 0;
for (Tree tree : classTree.getMembers()) {
Element memberElement = Util.getElementNoErrors(tree);
if (tree instanceof VariableTree && memberElement.getKind() == ElementKind.ENUM_CONSTANT) {
VariableTree varDecl = (VariableTree) tree;
// enum fields are not part of the enum auxiliary class but
// will initialize the enum values
NewClassTree newClass = (NewClassTree) varDecl.getInitializer();
ClassTree clazz = classTree;
try {
if (getScope().stringEnumScope) {
print(getStringLiteralQuote());
print(memberElement.getSimpleName().toString());
print(getStringLiteralQuote());
} else {
print("" + index);
}
print(": ");
int anonymousClassIndex = getScope().anonymousClasses.indexOf(newClass.getClassBody());
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(getStringLiteralQuote() + memberElement.getSimpleName().toString()
+ getStringLiteralQuote());
if (!newClass.getArguments().isEmpty()) {
print(", ");
}
printArgList(null, newClass.getArguments()).print(")");
print(", ");
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
}
removeLastChars(2);
print("};").println();
}
if (getScope().mainMethod != null && getScope().mainMethod.getParameters().size() < 2
&& getScope().getMainMethodElement().getEnclosingElement().equals(classTypeElement)) {
String mainClassName = getQualifiedTypeName(classTypeElement, globals, true);
if (util().isPartOfAnEnum(classTypeElement)) {
mainClassName += ENUM_WRAPPER_CLASS_SUFFIX;
}
String mainMethodQualifier = mainClassName;
if (!isBlank(mainClassName)) {
mainMethodQualifier = mainClassName + ".";
}
context.entryFiles.add(new File(compilationUnit.getSourceFile().getName()));
context.addFooterStatement(mainMethodQualifier + JSweetConfig.MAIN_FUNCTION_NAME + "("
+ (getScope().mainMethod.getParameters().isEmpty() ? "" : "null") + ");");
}
getAdapter().typeVariablesToErase.removeAll(parentTypeVars);
exitScope();
getAdapter().afterType(classTypeElement);
return returnNothing();
}
private void printAbstractMethodDeclaration(ExecutableElement method) {
printIndent().print("public abstract ").print(method.getSimpleName().toString());
print("(");
if (method.getParameters() != null && !method.getParameters().isEmpty()) {
for (VariableElement var : method.getParameters()) {
print(var.getSimpleName().toString()).print("?: any");
print(", ");
}
removeLastChars(2);
}
print(")");
print(": any;").println();
}
private String getTSMethodName(MethodTree methodDecl) {
ExecutableElement methodElement = Util.getElement(methodDecl);
String name = context.getActualName(methodElement);
switch (name) {
case CONSTRUCTOR_METHOD_NAME:
return "constructor";
case JSweetConfig.ANONYMOUS_FUNCTION_NAME:
case JSweetConfig.ANONYMOUS_STATIC_FUNCTION_NAME:
return "";
case JSweetConfig.ANONYMOUS_DEPRECATED_FUNCTION_NAME:
case JSweetConfig.ANONYMOUS_DEPRECATED_STATIC_FUNCTION_NAME:
if (context.deprecatedApply) {
return "";
} else {
return name;
}
case JSweetConfig.NEW_FUNCTION_NAME:
return "new";
default:
if (context.hasMethodNameMapping(methodElement)) {
return context.getMethodNameMapping(methodElement);
} else {
return name;
}
}
}
private boolean printCoreMethodDelegate = false;
protected boolean isDebugMode(MethodTree methodDecl) {
ExecutableElement methodElement = Util.getElement(methodDecl);
return methodDecl != null && !getScope().constructor && context.options.isDebugMode()
&& !(context.hasAnnotationType(methodElement, JSweetConfig.ANNOTATION_NO_DEBUG) || context
.hasAnnotationType(methodElement.getEnclosingElement(), JSweetConfig.ANNOTATION_NO_DEBUG));
}
private boolean isInterfaceMethod(ClassTree parent, MethodTree method) {
ExecutableElement methodElement = Util.getElement(method);
TypeElement parentElement = Util.getElement(parent);
return context.isInterface(parentElement) && !methodElement.getModifiers().contains(Modifier.STATIC);
}
@Override
public Void visitMethod(final MethodTree methodTree, final Trees trees) {
ExecutableElement methodElement = Util.getElement(methodTree);
if (getAdapter().substituteExecutable(methodElement)) {
return returnNothing();
}
if (context.hasAnnotationType(methodElement, JSweetConfig.ANNOTATION_ERASED)) {
// erased elements are ignored
return returnNothing();
}
final ClassTree parent = (ClassTree) getParent();
TypeElement parentElement = Util.getElement(parent);
if (!getScope().enumWrapperClassScope //
&& parent != null //
&& util().isGeneratedConstructor(methodTree, parent, methodElement) //
) {
return returnNothing();
}
if (JSweetConfig.INDEXED_GET_FUCTION_NAME.equals(methodTree.getName().toString())
&& methodTree.getParameters().size() == 1) {
print("[").print(methodTree.getParameters().get(0)).print("]: ");
substituteAndPrintType(methodTree.getReturnType()).print(";");
return returnNothing();
}
getScope().constructor = methodElement.getKind() == ElementKind.CONSTRUCTOR;
if (getScope().enumScope) {
if (getScope().constructor) {
if (parent != null && util().getStartPosition(methodTree, getCompilationUnit()) != util()
.getStartPosition(parent, getCompilationUnit())) {
getScope().isComplexEnum = true;
}
} else {
getScope().isComplexEnum = true;
}
return returnNothing();
}
// do not generate definition if parent class already declares method to avoid
// wrong override error with overloads ({ scale(number); scale(number, number);
// } cannot be overriden with { scale(number) } only)
if (getScope().isDeclareClassScope() && parent.getExtendsClause() != null && !getScope().constructor) {
TypeElement superTypeElement = Util.getTypeElement(parent.getExtendsClause());
ExecutableElement superMethod = util().findMethodDeclarationInType(superTypeElement,
methodTree.getName().toString(), Util.getType(methodTree));
if (superMethod != null && util().isSourceElement(superMethod)) {
return returnNothing();
}
}
Overload overload = null;
boolean inOverload = false;
boolean inCoreWrongOverload = false;
if (parent != null) {
overload = context.getOverload(parentElement, methodElement);
inOverload = overload != null && overload.getMethodsCount() > 1;
if (inOverload) {
if (!overload.isValid) {
if (!printCoreMethodDelegate) {
if (overload.getCoreMethod().equals(methodTree)) {
inCoreWrongOverload = true;
if (!isInterfaceMethod(parent, methodTree)
&& methodElement.getKind() != ElementKind.CONSTRUCTOR
&& parentElement.equals(overload.getCoreMethodElement().getEnclosingElement())) {
printCoreMethodDelegate = true;
visitMethod(overload.getCoreMethod(), trees);
println().println().printIndent();
printCoreMethodDelegate = false;
}
} else {
if (methodElement.getKind() == ElementKind.CONSTRUCTOR) {
return returnNothing();
}
boolean addCoreMethod = false;
addCoreMethod = !overload.printed
&& overload.getCoreMethodElement().getEnclosingElement() != parentElement
&& !overload.getCoreMethodElement().getModifiers().contains(Modifier.DEFAULT)
&& (!overload.getCoreMethodElement().getModifiers().contains(Modifier.ABSTRACT)
|| isInterfaceMethod(parent, methodTree)
|| !types().isSubtype(parentElement.asType(),
overload.getCoreMethodElement().getEnclosingElement().asType()));
if (!overload.printed && !addCoreMethod
&& overload.getCoreMethodElement().getKind() == ElementKind.METHOD) {
addCoreMethod = util().findMethodDeclarationInType(parentElement,
methodTree.getName().toString(),
(ExecutableType) overload.getCoreMethodElement().asType()) == null;
}
if (addCoreMethod) {
visitMethod(overload.getCoreMethod(), trees);
overload.printed = true;
if (!isInterfaceMethod(parent, methodTree)) {
println().println().printIndent();
}
}
if (isInterfaceMethod(parent, methodTree)) {
return returnNothing();
}
}
}
} else {
if (!overload.getCoreMethod().equals(methodTree)) {
return returnNothing();
}
}
}
}
boolean ambient = context.hasAnnotationType(methodElement, JSweetConfig.ANNOTATION_AMBIENT);
if (inOverload && !inCoreWrongOverload && (ambient || isDefinitionScope)) {
// do not generate method stubs for definitions
return returnNothing();
}
if (isDebugMode(methodTree)) {
printMethodModifiers(methodTree, parent, getScope().constructor, inOverload, overload);
print(getTSMethodName(methodTree)).print("(");
printArgList(null, methodTree.getParameters());
print("): ");
substituteAndPrintType(methodTree.getReturnType());
print(" {").println();
startIndent().printIndent();
if (!util().isVoidType(methodElement.getReturnType())) {
print("return ");
}
print("__debug_exec('" + (parentElement == null ? "null" : parentElement.getQualifiedName()) + "', '"
+ methodTree.getName() + "', ");
if (!methodTree.getParameters().isEmpty()) {
print("[");
for (VariableTree param : methodTree.getParameters()) {
print("'" + param.getName() + "', ");
}
removeLastChars(2);
print("]");
} else {
print("undefined");
}
print(", this, arguments, ");
if (methodElement.getModifiers().contains(Modifier.STATIC)) {
print(methodElement.getEnclosingElement().getSimpleName().toString());
} else {
print("this");
}
print("." + GENERATOR_PREFIX + getTSMethodName(methodTree) + "(");
for (VariableTree param : methodTree.getParameters()) {
VariableElement paramElement = Util.getElement(param);
print(context.getActualName(paramElement) + ", ");
}
if (!methodTree.getParameters().isEmpty()) {
removeLastChars(2);
}
print("));");
println().endIndent().printIndent();
print("}").println().println().printIndent();
}
if (inOverload && !overload.isValid) {
// spread all annotations of the overload to the methods
for (MethodTree m : overload.getMethods()) {
if (m != methodTree) {
for (AnnotationTree anno : m.getModifiers().getAnnotations()) {
if (!methodTree.getModifiers().getAnnotations().stream().anyMatch(
a -> a.getAnnotationType().toString().equals(anno.getAnnotationType().toString()))) {
util().addAnnotation(methodTree.getModifiers(), anno);
}
}
}
}
}
print(methodTree.getModifiers());
if (methodTree.getModifiers().getFlags().contains(Modifier.NATIVE)) {
if (!getScope().declareClassScope && !ambient && !getScope().interfaceScope) {
report(methodTree, methodTree.getName(), JSweetProblem.NATIVE_MODIFIER_IS_NOT_ALLOWED,
methodTree.getName());
}
} else {
if (getScope().declareClassScope && !getScope().constructor && !getScope().interfaceScope
&& !methodTree.getModifiers().getFlags().contains(Modifier.DEFAULT)) {
report(methodTree, methodTree.getName(), JSweetProblem.INVALID_METHOD_BODY_IN_INTERFACE,
methodTree.getName(), parent == null ? "" : parent.getSimpleName());
}
}
if (methodTree.getName().toString().equals("constructor")) {
report(methodTree, methodTree.getName(), JSweetProblem.CONSTRUCTOR_MEMBER);
}
if (parent != null) {
VariableElement v = util().findFieldDeclaration(parentElement, methodTree.getName());
if (v != null && context.getFieldNameMapping(v) == null) {
if (isDefinitionScope) {
return returnNothing();
} else {
if (methodTree.getName().toString().equals(context.getActualName(v))) {
report(methodTree, methodTree.getName(), JSweetProblem.METHOD_CONFLICTS_FIELD,
methodTree.getName(), parentElement);
}
}
}
}
if (JSweetConfig.MAIN_FUNCTION_NAME.equals(methodTree.getName().toString())
&& methodTree.getModifiers().getFlags().contains(Modifier.STATIC)
&& !context.hasAnnotationType(methodElement, JSweetConfig.ANNOTATION_DISABLED)) {
// ignore main methods in inner classes
if (scope.size() == 1 || (scope.size() == 2 && getScope().enumWrapperClassScope)) {
getScope().mainMethod = methodTree;
}
}
boolean globals = parent == null ? false
: JSweetConfig.GLOBALS_CLASS_NAME.equals(parent.getSimpleName().toString());
globals = globals
|| (getScope().interfaceScope && methodTree.getModifiers().getFlags().contains(Modifier.STATIC));
if (!(inOverload && !inCoreWrongOverload)) {
printDocComment(methodTree);
}
if (parent == null) {
printAsyncKeyword(methodTree);
print("function ");
} else if (globals) {
if (getScope().constructor && methodElement.getModifiers().contains(Modifier.PRIVATE)
&& methodTree.getParameters().isEmpty()) {
return returnNothing();
}
if (getScope().constructor) {
report(methodTree, methodTree.getName(), JSweetProblem.GLOBAL_CONSTRUCTOR_DEF);
return returnNothing();
}
if (context.lookupDecoratorAnnotation((parentElement.getQualifiedName() + "." + methodTree.getName())
.replace(JSweetConfig.GLOBALS_CLASS_NAME + ".", "")) != null) {
if (!getScope().decoratorScope) {
return returnNothing();
}
}
if (!methodTree.getModifiers().getFlags().contains(Modifier.STATIC)) {
report(methodTree, methodTree.getName(), JSweetProblem.GLOBALS_CAN_ONLY_HAVE_STATIC_MEMBERS);
return returnNothing();
}
if (context.hasAnnotationType(methodElement, JSweetConfig.ANNOTATION_MODULE)) {
getContext().addExportedElement(
context.getAnnotationValue(methodElement, JSweetConfig.ANNOTATION_MODULE, String.class, null),
methodElement, getCompilationUnit());
}
if (context.useModules || context.moduleBundleMode) {
if (!methodTree.getModifiers().getFlags().contains(Modifier.PRIVATE)) {
print("export ");
}
} else {
if (!isTopLevelScope()) {
print("export ");
}
}
if (ambient || (getIndent() == 0 && isDefinitionScope)) {
print("declare ");
}
printAsyncKeyword(methodTree);
print("function ");
} else {
printMethodModifiers(methodTree, parent, getScope().constructor, inOverload, overload);
printAsyncKeyword(methodTree);
if (ambient) {
report(methodTree, methodTree.getName(), JSweetProblem.WRONG_USE_OF_AMBIENT, methodTree.getName());
}
}
if (parent == null || !context.isFunctionalType(Util.getTypeElement(parent))) {
if (isDebugMode(methodTree)) {
print("*").print(GENERATOR_PREFIX);
}
if (inOverload && !overload.isValid && !inCoreWrongOverload) {
print(getOverloadMethodName(methodElement));
} else {
String tsMethodName = getTSMethodName(methodTree);
getScope().generatedMethodNames.add(tsMethodName);
if (doesMemberNameRequireQuotes(tsMethodName)) {
print("'" + tsMethodName + "'");
} else {
print(tsMethodName);
}
}
}
if (inOverload && inCoreWrongOverload) {
// for overload, we print the maximum number of type args, with default values
int maxTypeArgs = 0;
for (OverloadMethodEntry entry : overload.getEntries()) {
maxTypeArgs = Math.max(maxTypeArgs, entry.methodElement.getTypeParameters().size());
}
if (maxTypeArgs > 0) {
print("<");
for (int i = 0; i < maxTypeArgs; i++) {
print("T" + i + " = any, ");
}
removeLastChars(2);
print(">");
}
} else {
if ((methodTree.getTypeParameters() != null && !methodTree.getTypeParameters().isEmpty())
|| (getContext().getWildcards(methodElement) != null)) {
inTypeParameters = true;
print("<");
if (methodTree.getTypeParameters() != null && !methodTree.getTypeParameters().isEmpty()) {
printArgList(null, methodTree.getTypeParameters());
if (getContext().getWildcards(methodElement) != null) {
print(", ");
}
}
if (getContext().getWildcards(methodElement) != null) {
printArgList(null, getContext().getWildcards(methodElement), this::substituteAndPrintType);
}
print(">");
inTypeParameters = false;
}
}
print("(");
printMethodArgs(methodTree, overload, inOverload, inCoreWrongOverload, getScope());
print(")");
printMethodReturnDeclaration(methodTree, overload, inCoreWrongOverload);
if (inCoreWrongOverload && isInterfaceMethod(parent, methodTree)) {
print(";");
return returnNothing();
}
if (methodTree.getBody() == null && !(inCoreWrongOverload && !getScope().declareClassScope)
|| (methodTree.getModifiers().getFlags().contains(Modifier.DEFAULT)
&& !getScope().defaultMethodScope)) {
if (!getScope().interfaceScope && methodTree.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 {
print(";");
}
} else {
if (!getScope().declareClassScope && getScope().interfaceScope) {
if (!methodTree.getModifiers().getFlags().contains(Modifier.STATIC)) {
report(methodTree, methodTree.getName(), JSweetProblem.INVALID_METHOD_BODY_IN_INTERFACE,
methodTree.getName(), parent == null ? "" : parent.getSimpleName());
}
}
if (getScope().declareClassScope) {
if (!getScope().constructor
|| (methodTree.getBody() != null && methodTree.getBody().getStatements().isEmpty())) {
report(methodTree, methodTree.getName(), JSweetProblem.INVALID_METHOD_BODY_IN_INTERFACE,
methodTree.getName(), parent == null ? "" : parent.getSimpleName());
}
print(";");
} else {
if (inCoreWrongOverload) {
print(" {").println().startIndent().printIndent();
printCoreOverloadMethodBody(methodTree, parent, overload);
print(" else throw new Error('invalid overload');");
endIndent().println().printIndent().print("}");
} else {
print(" ").print("{").println().startIndent();
if (!getAdapter().substituteMethodBody(parentElement, methodElement)) {
String replacedBody = null;
if (context.hasAnnotationType(methodElement, JSweetConfig.ANNOTATION_REPLACE)) {
replacedBody = (String) context.getAnnotationValue(methodElement,
JSweetConfig.ANNOTATION_REPLACE, String.class, null);
}
boolean fieldsInitPrinted = false;
int position = getCurrentPosition();
if (replacedBody == null || BODY_MARKER.matcher(replacedBody).find()) {
enter(methodTree.getBody());
List statements = methodTree.getBody().getStatements();
if (!statements.isEmpty() && statements.get(0).toString().startsWith("super(")) {
printBlockStatement(statements.get(0));
if (parent != null) {
printInstanceInitialization(parent, methodElement);
fieldsInitPrinted = true;
}
printBlockStatements(statements.subList(1, statements.size()));
} else {
if (parent != null) {
printInstanceInitialization(parent, methodElement);
fieldsInitPrinted = true;
}
printBlockStatements(statements);
}
exit();
if (replacedBody != null) {
String orgBody = getOutput().substring(position);
removeLastChars(getCurrentPosition() - position);
replacedBody = BODY_MARKER.matcher(replacedBody).replaceAll(orgBody);
replacedBody = BASE_INDENT_MARKER.matcher(replacedBody).replaceAll(getIndentString());
replacedBody = INDENT_MARKER.matcher(replacedBody).replaceAll(INDENT);
replacedBody = METHOD_NAME_MARKER.matcher(replacedBody)
.replaceAll(methodTree.getName().toString());
TypeElement parentTypeElement = Util.getElement(parent);
replacedBody = CLASS_NAME_MARKER.matcher(replacedBody)
.replaceAll(parentTypeElement.getQualifiedName().toString());
}
}
if (replacedBody != null && replacedBody.trim().startsWith("super(")) {
String superCall = replacedBody.substring(0, replacedBody.indexOf(';') + 1);
replacedBody = replacedBody.substring(replacedBody.indexOf(';') + 1);
println(superCall);
}
if (!fieldsInitPrinted && parent != null) {
printInstanceInitialization(parent, methodElement);
fieldsInitPrinted = true;
}
if (replacedBody != null) {
if (methodElement.getKind() == ElementKind.CONSTRUCTOR) {
getScope().hasDeclaredConstructor = true;
}
printIndent().print(replacedBody).println();
}
}
endIndent().printIndent().print("}");
}
}
}
return returnNothing();
}
private void printCoreOverloadMethodBody(MethodTree methodDecl, ClassTree parent, Overload overload) {
if (getAdapter().substituteOverloadMethodBody(Util.getElement(parent), overload)) {
return;
}
boolean wasPrinted = false;
for (OverloadMethodEntry overloadMethodEntry : overload.getEntries()) {
MethodTree method = overloadMethodEntry.methodTree;
ExecutableElement methodElement = overloadMethodEntry.methodElement;
if (context.isInterface((TypeElement) methodElement.getEnclosingElement())
&& !method.getModifiers().getFlags().contains(Modifier.DEFAULT)
&& !method.getModifiers().getFlags().contains(Modifier.STATIC)) {
continue;
}
TypeElement parentElement = Util.getElement(parent);
if (!util().isParent(parentElement, (TypeElement) methodElement.getEnclosingElement())) {
continue;
}
if (wasPrinted) {
print(" else ");
}
wasPrinted = true;
print("if (");
printMethodParamsTest(overload, overloadMethodEntry);
print(") ");
if (methodElement.getKind() == ElementKind.CONSTRUCTOR
|| (methodElement.getModifiers().contains(Modifier.DEFAULT)
&& method.equals(overload.getCoreMethod()))) {
printInlinedMethod(overload, method, methodDecl.getParameters());
} else {
if (parentElement != methodElement.getEnclosingElement()
&& context.getOverload((TypeElement) methodElement.getEnclosingElement(), methodElement)
.getCoreMethod() == method) {
print("{").println().startIndent().printIndent();
if ((method.getBody() == null || (method.getModifiers().getFlags().contains(Modifier.DEFAULT)
&& !getScope().defaultMethodScope)) && !getScope().interfaceScope
&& method.getModifiers().getFlags().contains(Modifier.ABSTRACT)) {
print(" throw new Error('cannot invoke abstract overloaded method... check your argument(s) type(s)'); ");
} else {
String tsMethodAccess = getTSMemberAccess(getTSMethodName(methodDecl), true);
if (!"void".equals(Util.getType(method.getReturnType()).toString())) {
print("return ");
}
print("super" + tsMethodAccess);
print("(");
for (int j = 0; j < method.getParameters().size(); j++) {
print(avoidJSKeyword(overload.getCoreMethod().getParameters().get(j).getName().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 ");
if (isAsyncMethod(method)) {
print("await ");
}
if (!util().isGlobalsClassName(parent.getSimpleName().toString())) {
if (methodElement.getModifiers().contains(Modifier.STATIC)) {
print(getQualifiedTypeName(parentElement, false, false).toString());
if (util().isPartOfAnEnum(parentElement)) {
print(ENUM_WRAPPER_CLASS_SUFFIX);
}
} else {
print("this");
}
print(".");
}
print(getOverloadMethodName(methodElement)).print("(");
for (int j = 0; j < method.getParameters().size(); j++) {
if (j == method.getParameters().size() - 1 && util().hasVarargs(methodElement)) {
print("...");
} else if (j == method.getParameters().size() - 1
&& util().hasVarargs(overload.getCoreMethodElement())) {
print("");
}
print(avoidJSKeyword(overload.getCoreMethod().getParameters().get(j).getName().toString()))
.print(", ");
}
if (!method.getParameters().isEmpty()) {
removeLastChars(2);
}
print(");");
println().endIndent().printIndent().print("}");
}
}
}
}
protected void printMethodReturnDeclaration(MethodTree methodTree, Overload overload, boolean inCoreWrongOverload) {
ExecutableElement methodElement = Util.getElement(methodTree);
if (methodTree.getReturnType() != null) {
Element returnTypeElement = Util.getTypeElement(methodTree.getReturnType());
TypeMirror returnType = Util.getType(methodTree.getReturnType());
if (returnType.getKind() != TypeKind.VOID) {
print(": ");
boolean promisify = isAsyncMethod(methodTree) && (returnTypeElement == null
|| !util().getQualifiedName(returnTypeElement).endsWith(".Promise"));
if (promisify) {
print("Promise<");
}
boolean eraseOverloadsReturnTypeToAny = false;
if (inCoreWrongOverload && methodElement.getKind() != ElementKind.CONSTRUCTOR) {
for (OverloadMethodEntry overloadEntry : overload.getEntries()) {
if (!context.types.isSameType(returnType, overloadEntry.methodElement.getReturnType())) {
eraseOverloadsReturnTypeToAny = true;
break;
}
}
}
if (eraseOverloadsReturnTypeToAny) {
print("any");
} else {
substituteAndPrintType(methodTree.getReturnType());
}
if (promisify) {
print(">");
}
}
}
}
/**
* Print async keyword for given method if relevant. Prints nothing if method
* shouldn't be async
*
*/
protected void printAsyncKeyword(MethodTree methodDecl) {
if (getScope().declareClassScope || getScope().interfaceScope) {
return;
}
if (isAsyncMethod(methodDecl)) {
print("async ");
}
}
protected boolean isAsyncMethod(MethodTree methodTree) {
return context.hasAnnotationType(Util.getElement(methodTree), JSweetConfig.ANNOTATION_ASYNC);
}
protected void printMethodArgs(MethodTree methodTree, Overload overload, boolean inOverload,
boolean inCoreWrongOverload, ClassScope scope) {
ExecutableElement methodElement = Util.getElement(methodTree);
boolean isWrapped = false;
if (this.context.hasAnnotationType(methodElement, JSweetConfig.ANNOTATION_WRAP_PARAMETERS)) {
isWrapped = true;
}
if (isWrapped) {
print("{");
}
if (inCoreWrongOverload) {
scope.setEraseVariableTypes(true);
}
boolean paramPrinted = false;
if (scope.isInnerClassNotStatic() && methodElement.getKind() == ElementKind.CONSTRUCTOR
&& !scope.isEnumWrapperClassScope()) {
print(PARENT_CLASS_FIELD_NAME + ": any, ");
paramPrinted = true;
}
if (scope.isConstructor() && scope.isEnumWrapperClassScope()) {
print((isAnonymousClass() ? "" : "protected ") + ENUM_WRAPPER_CLASS_ORDINAL + ": number, ");
print((isAnonymousClass() ? "" : "protected ") + ENUM_WRAPPER_CLASS_NAME + ": string, ");
paramPrinted = true;
}
int i = 0;
for (VariableTree param : methodTree.getParameters()) {
if (isWrapped) {
print(param.getName().toString());
} else {
print(param);
}
if (inOverload && overload.isValid && overload.defaultValues.get(i) != null) {
print(" = ").print(overload.defaultValues.get(i));
}
print(", ");
i++;
paramPrinted = true;
}
if (inCoreWrongOverload) {
scope.setEraseVariableTypes(false);
}
if (paramPrinted) {
removeLastChars(2);
}
if (isWrapped) {
print("}: {");
for (VariableTree param : methodTree.getParameters()) {
print(param).println(";");
}
print("}");
}
}
protected void printMethodModifiers(MethodTree methodDecl, ClassTree parent, boolean constructor,
boolean inOverload, Overload overload) {
if (methodDecl.getModifiers().getFlags().contains(Modifier.PUBLIC)
|| (inOverload && overload.getCoreMethod().equals(methodDecl))) {
if (!getScope().interfaceScope) {
print("public ");
}
}
if (methodDecl.getModifiers().getFlags().contains(Modifier.PRIVATE)) {
if (!constructor) {
if (!getScope().innerClass) {
if (!getScope().interfaceScope) {
if (!(inOverload && overload.getCoreMethod().equals(methodDecl) || getScope().hasInnerClass)) {
print("/*private*/ ");
}
} else {
if (!(inOverload && overload.getCoreMethod().equals(methodDecl))) {
report(methodDecl, methodDecl.getName(), JSweetProblem.INVALID_PRIVATE_IN_INTERFACE,
methodDecl.getName(), parent == null ? "" : parent.getSimpleName());
}
}
}
}
}
if (methodDecl.getModifiers().getFlags().contains(Modifier.STATIC)) {
if (!getScope().interfaceScope) {
print("static ");
}
}
if (methodDecl.getModifiers().getFlags().contains(Modifier.ABSTRACT)) {
if (!getScope().interfaceScope && !inOverload) {
print("abstract ");
}
}
}
private boolean shouldPrintFieldInitializationInConstructor(VariableTree var) {
VariableElement varElement = Util.getElement(var);
if (varElement.getModifiers().contains(Modifier.STATIC)) {
return false;
}
return var.getInitializer() == null || (!getScope().hasConstructorOverloadWithSuperClass
&& getScope().getFieldsWithInitializers().contains(var));
}
protected void printVariableInitialization(ClassTree clazz, VariableTree var) {
VariableElement varElement = Util.getElement(var);
String name = var.getName().toString();
if (context.getFieldNameMapping(varElement) != null) {
name = context.getFieldNameMapping(varElement);
} else {
name = getIdentifier(varElement);
}
if (getScope().innerClassNotStatic && !util().isConstantOrNullField(var)
// constructor fields init is handled in another method
// (printFieldInitializations)
|| var.getInitializer() != null && shouldPrintFieldInitializationInConstructor(var)) {
if (doesMemberNameRequireQuotes(name)) {
printIndent().print("this['").print(name).print("'] = ");
} else {
printIndent().print("this.").print(name).print(" = ");
}
substituteAndPrintAssignedExpression(varElement.asType(), var.getInitializer());
print(";").println();
} else if (var.getInitializer() == null) {
if (doesMemberNameRequireQuotes(name)) {
printIndent();
print("if (").print("this['").print(name).print("']").print(" === undefined) { ");
if (context.options.isNonEnumerableTransients()
&& varElement.getModifiers().contains(Modifier.TRANSIENT)) {
print("Object.defineProperty(this, " + getStringLiteralQuote() + name + getStringLiteralQuote()
+ ", { value: ").print(getAdapter().getVariableInitialValue(varElement))
.print(", enumerable: false }); } ").println();
} else {
print("this['").print(name).print("'] = ").print(getAdapter().getVariableInitialValue(varElement))
.print("; } ").println();
}
} else {
printIndent().print("if (").print("this.").print(name).print(" === undefined) { ");
if (context.options.isNonEnumerableTransients()
&& varElement.getModifiers().contains(Modifier.TRANSIENT)) {
print("Object.defineProperty(this, " + getStringLiteralQuote() + name + getStringLiteralQuote()
+ ", { value: ").print(getAdapter().getVariableInitialValue(varElement))
.print(", enumerable: false }); } ").println();
} else {
print("this.").print(name).print(" = ").print(getAdapter().getVariableInitialValue(varElement))
.print("; }").println();
}
}
}
}
protected void printInstanceInitialization(ClassTree clazz, ExecutableElement method) {
if (method == null || method.getKind() == ElementKind.CONSTRUCTOR) {
getScope().hasDeclaredConstructor = true;
TypeElement typeElement = Util.getElement(clazz);
// this workaround will not work on all browsers (see
// https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work)
if (types().isAssignable(typeElement.asType(), util().getType(Throwable.class))) {
printIndent().print("(Object).setPrototypeOf(this, " + getClassName(typeElement) + ".prototype);")
.println();
}
if (getScope().innerClassNotStatic && !getScope().enumWrapperClassScope) {
printIndent().print("this." + PARENT_CLASS_FIELD_NAME + " = " + PARENT_CLASS_FIELD_NAME + ";")
.println();
}
for (Tree member : clazz.getMembers()) {
if (member instanceof VariableTree) {
VariableTree var = (VariableTree) member;
VariableElement varElement = Util.getElement(var);
if (!varElement.getModifiers().contains(Modifier.STATIC)
&& !context.hasAnnotationType(varElement, JSweetConfig.ANNOTATION_ERASED)) {
printVariableInitialization(clazz, var);
}
} else if (member instanceof BlockTree) {
BlockTree block = (BlockTree) member;
if (!block.isStatic()) {
printIndent().print("(() => {").startIndent().println();
stack.push(block);
printBlockStatements(block.getStatements());
stack.pop();
endIndent().printIndent().print("})();").println();
}
}
}
}
}
private void printInlinedMethod(Overload overload, MethodTree method, List args) {
print("{").println().startIndent();
if (getScope().innerClassNotStatic && getScope().constructor) {
// the __parent added parameter is not part of the actual arguments
printIndent().print(VAR_DECL_KEYWORD + " __args = Array.prototype.slice.call(arguments, [1]);").println();
} else {
printIndent().print(VAR_DECL_KEYWORD + " __args = arguments;").println();
}
for (int j = 0; j < method.getParameters().size(); j++) {
if (args.get(j) instanceof VariableTree) {
if (method.getParameters().get(j).getName().equals(((VariableTree) args.get(j)).getName())) {
continue;
} else {
printIndent().print(VAR_DECL_KEYWORD + " ")
.print(avoidJSKeyword(method.getParameters().get(j).getName().toString())).print(": ")
.print("any")
.print(util().isVarargs(method.getParameters().get(j)) ? "[]" : "")
.print(" = ").print("__args[" + j + "]").print(";").println();
}
} else {
if (method.getParameters().get(j).getName().toString().equals(args.get(j).toString())) {
continue;
} else {
getScope().inlinedConstructorArgs = method.getParameters().stream()
.map((VariableTree p) -> Util.getElement(p).getSimpleName().toString())
.collect(Collectors.toList());
printIndent().print(VAR_DECL_KEYWORD + " ")
.print(avoidJSKeyword(method.getParameters().get(j).getName().toString())).print(": ")
.print("any")
.print(util().isVarargs(method.getParameters().get(j)) ? "[]" : "")
.print(" = ").print(args.get(j)).print(";").println();
getScope().inlinedConstructorArgs = null;
}
}
}
ExecutableElement methodElement = Util.getElement(method);
if (method.getBody() != null) {
boolean skipFirst = false;
List statements = method.getBody().getStatements();
if (!statements.isEmpty() && statements.get(0).toString().startsWith("this(")) {
skipFirst = true;
MethodInvocationTree inv = (MethodInvocationTree) ((ExpressionStatementTree) statements.get(0))
.getExpression();
ExecutableElement ms = util().findMethodDeclarationInType(
(TypeElement) overload.getCoreMethodElement().getEnclosingElement(), inv);
for (OverloadMethodEntry overloadMethodEntry : overload.getEntries()) {
if (overloadMethodEntry.methodElement.equals(ms)) {
printIndent();
printInlinedMethod(overload, overloadMethodEntry.methodTree, inv.getArguments());
println();
}
}
}
boolean isConstructor = methodElement.getKind() == ElementKind.CONSTRUCTOR;
String replacedBody = null;
if (context.hasAnnotationType(methodElement, JSweetConfig.ANNOTATION_REPLACE)) {
replacedBody = context.getAnnotationValue(methodElement, JSweetConfig.ANNOTATION_REPLACE, String.class,
null);
}
int position = getCurrentPosition();
if (replacedBody == null || BODY_MARKER.matcher(replacedBody).find()) {
enter(method.getBody());
List actualStatements = skipFirst ? statements.subList(1, statements.size())
: statements;
if (isConstructor) {
getScope().hasDeclaredConstructor = true;
}
if (!actualStatements.isEmpty() && actualStatements.get(0).toString().startsWith("super(")) {
printBlockStatement(actualStatements.get(0));
printFieldInitializations(actualStatements);
List nextStatements = actualStatements.subList(1, actualStatements.size());
if (!nextStatements.isEmpty()) {
printBlockStatements(nextStatements);
}
} else {
printFieldInitializations(actualStatements);
if (!actualStatements.isEmpty() || !isConstructor) {
printIndent();
}
if (!isConstructor) {
print("return ");
}
if (!actualStatements.isEmpty() || !isConstructor) {
print("((").print(") => {").startIndent().println();
printBlockStatements(actualStatements);
endIndent().printIndent().print("})(").print(");").println();
}
}
exit();
if (replacedBody != null) {
getIndent();
printIndent();
String orgBody = getOutput().substring(position);
removeLastChars(getCurrentPosition() - position);
replacedBody = BODY_MARKER.matcher(replacedBody).replaceAll(orgBody);
replacedBody = BASE_INDENT_MARKER.matcher(replacedBody).replaceAll(getIndentString());
replacedBody = INDENT_MARKER.matcher(replacedBody).replaceAll(INDENT);
replacedBody = METHOD_NAME_MARKER.matcher(replacedBody).replaceAll(method.getName().toString());
replacedBody = CLASS_NAME_MARKER.matcher(replacedBody)
.replaceAll(util().getQualifiedName(methodElement.getEnclosingElement()));
}
}
if (replacedBody != null) {
if (isConstructor) {
getScope().hasDeclaredConstructor = true;
}
printIndent().print(replacedBody).println();
}
} else {
String returnValue = util().getTypeInitialValue(methodElement.getReturnType());
if (returnValue != null) {
print(" return ").print(returnValue).print("; ");
}
}
endIndent().printIndent().print("}");
}
private void printFieldInitializations(List potentialInitializationStatements) {
ClassTree clazz = getParent(ClassTree.class);
for (Tree t : clazz.getMembers()) {
if (t instanceof VariableTree && !getScope().fieldsWithInitializers.contains(t)) {
VariableTree field = (VariableTree) t;
VariableElement fieldElement = Util.getElement(field);
if (!fieldElement.getModifiers().contains(Modifier.STATIC)
&& !context.hasAnnotationType(fieldElement, JSweetConfig.ANNOTATION_ERASED)) {
String name = getIdentifier(fieldElement);
if (context.getFieldNameMapping(fieldElement) != null) {
name = context.getFieldNameMapping(fieldElement);
}
printIndent().print("if (").print("this.").print(name).print(" === undefined) { ");
if (context.options.isNonEnumerableTransients()
&& fieldElement.getModifiers().contains(Modifier.TRANSIENT)) {
print("Object.defineProperty(this, " + getStringLiteralQuote() + name + getStringLiteralQuote()
+ ", { value: ").print(getAdapter().getVariableInitialValue(fieldElement))
.print(", enumerable: false }); } ");
} else {
print("this.").print(name).print(" = ")
.print(getAdapter().getVariableInitialValue(fieldElement)).print("; } ").println();
}
}
}
}
for (VariableTree field : getScope().fieldsWithInitializers) {
VariableElement fieldElement = Util.getElement(field);
if (context.hasAnnotationType(fieldElement, JSweetConfig.ANNOTATION_ERASED)) {
continue;
}
String name = getIdentifier(fieldElement);
if (context.getFieldNameMapping(fieldElement) != null) {
name = context.getFieldNameMapping(fieldElement);
}
printIndent().print("this.").print(name).print(" = ");
if (!substituteAssignedExpression(fieldElement.asType(), field.getInitializer())) {
print(field.getInitializer());
}
print(";").println();
}
}
protected void printBlockStatements(List statements) {
for (StatementTree statement : statements) {
if (context.options.isDebugMode()) {
MethodTree methodDecl = getParent(MethodTree.class);
if (isDebugMode(methodDecl)) {
SourcePosition statementPosition = util().getSourcePosition(statement, getCompilationUnit());
printIndent();
print("yield { row: ");
print("" + statementPosition.getStartLine());
print(", column: " + statementPosition.getStartColumn());
print(", statement: \"");
print(StringEscapeUtils.escapeJson(statement.toString())).print("\"");
final Stack> locals = new Stack<>();
try {
new TreeScanner() {
@Override
public Void scan(Tree tree, Trees trees) {
if (tree == statement) {
throw new RuntimeException();
}
boolean contextChange = false;
if (tree instanceof BlockTree || tree instanceof EnhancedForLoopTree
|| tree instanceof LambdaExpressionTree || tree instanceof ForLoopTree
|| tree instanceof DoWhileLoopTree) {
locals.push(new ArrayList<>());
contextChange = true;
}
if (tree instanceof VariableTree) {
locals.peek().add(((VariableTree) tree).getName().toString());
}
super.scan(tree, trees);
if (contextChange) {
locals.pop();
}
return returnNothing();
}
}.scan(methodDecl.getBody(), trees());
} catch (Exception end) {
// swallow
}
List accessibleLocals = new ArrayList<>();
for (List 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(StatementTree statement) {
printIndent();
int pos = getCurrentPosition();
print(statement);
if (getCurrentPosition() == pos) {
removeLastIndent();
return;
}
if (!isStatementWithNoSemiColon(statement)) {
if (statement instanceof LabeledStatementTree) {
if (!isStatementWithNoSemiColon(((LabeledStatementTree) statement).getStatement())) {
print(";");
}
} else {
print(";");
}
}
println();
}
private String getOverloadMethodName(ExecutableElement method) {
if (method.getKind() == ElementKind.CONSTRUCTOR) {
return "constructor";
}
StringBuilder nameBuilder = new StringBuilder(method.getSimpleName().toString());
nameBuilder.append("$");
for (VariableElement paramElement : method.getParameters()) {
nameBuilder.append(types().erasure(paramElement.asType()).toString().replace('.', '_').replace("[]", "_A"));
nameBuilder.append("$");
}
if (!method.getParameters().isEmpty()) {
nameBuilder.deleteCharAt(nameBuilder.length() - 1);
}
return nameBuilder.toString();
}
private void checkType(Element type) {
if (type instanceof TypeElement && !isMappedOrErasedType(type)) {
String name = type.getSimpleName().toString();
ModuleImportDescriptor moduleImport = getModuleImportDescriptor(name, (TypeElement) type);
if (moduleImport != null) {
useModule(false, moduleImport.isDirect(), moduleImport.getTargetPackage(), null,
moduleImport.getImportedName(), moduleImport.getPathToImportedClass(), null);
}
}
}
private void printMethodParamsTest(Overload overload, OverloadMethodEntry methodEntry) {
MethodTree method = methodEntry.methodTree;
int i = 0;
for (; i < method.getParameters().size(); i++) {
print("(");
VariableTree parameter = method.getParameters().get(i);
TypeMirror paramType = Util.getType(parameter);
Element paramTypeElement = Util.getTypeElement(parameter);
if (Class.class.getName().equals(context.types.erasure(paramType).toString())) {
List typeArguments = ((DeclaredType) paramType).getTypeArguments();
if (typeArguments.size() > 0) {
Element firstArgTypeElement = types().asElement(typeArguments.get(0));
if (firstArgTypeElement != null && firstArgTypeElement.getKind() == ElementKind.INTERFACE) {
print(avoidJSKeyword(overload.getCoreMethod().getParameters().get(i).getName().toString()))
.print(" === ");
print(getStringLiteralQuote()).print(typeArguments.get(0).toString())
.print(getStringLiteralQuote());
print(" || ");
}
}
}
printInstanceOf(avoidJSKeyword(overload.getCoreMethod().getParameters().get(i).getName().toString()), null,
paramType);
checkType(paramTypeElement);
print(" || ").print(
avoidJSKeyword(overload.getCoreMethod().getParameters().get(i).getName().toString()) + " === null")
.print(")");
print(" && ");
}
for (; i < overload.getCoreMethod().getParameters().size(); i++) {
print(avoidJSKeyword(overload.getCoreMethod().getParameters().get(i).getName().toString()))
.print(" === undefined");
if (i == overload.getCoreMethod().getParameters().size() - 1
&& overload.getCoreMethodElement().isVarArgs()) {
print(" || ");
print(avoidJSKeyword(overload.getCoreMethod().getParameters().get(i).getName().toString()))
.print(".length === 0");
}
print(" && ");
}
removeLastChars(4);
}
@Override
public Void visitBlock(BlockTree block, Trees trees) {
Tree parent = getParent();
boolean globals = (parent instanceof ClassTree)
&& JSweetConfig.GLOBALS_CLASS_NAME.equals(((ClassTree) parent).getSimpleName().toString());
boolean initializer = (parent instanceof ClassTree) && !globals;
if (initializer) {
if (getScope().interfaceScope) {
report(block, JSweetProblem.INVALID_INITIALIZER_IN_INTERFACE, ((ClassTree) parent).getSimpleName());
}
if (!block.isStatic()) {
// non-static blocks are initialized in the constructor
return returnNothing();
}
printStaticInitializer(block);
return returnNothing();
}
if (!globals) {
print("{").println().startIndent();
}
printBlockStatements(block.getStatements());
if (!globals) {
endIndent().printIndent().print("}");
}
return returnNothing();
}
private void printStaticInitializer(BlockTree block) {
if (getScope().isEnumScope()) {
// static blocks are initialized in the enum wrapper class
return;
}
if (getScope().isDeclareClassScope()) {
// static init block are erased in declare class
return;
}
int static_i = 0;
for (Tree m : ((ClassTree) getParent()).getMembers()) {
if (m instanceof BlockTree) {
if (((BlockTree) m).isStatic()) {
if (block == m) {
String asyncKeyword = isAsyncStaticInitializer(block) ? "async" : "";
print("static " + asyncKeyword + " __static_initializer_" + static_i + "() ");
print("{");
println().startIndent();
printBlockStatements(block.getStatements());
endIndent().printIndent();
print("}");
break;
}
static_i++;
}
}
}
}
protected boolean isAsyncStaticInitializer(BlockTree initializerBlock) {
AsyncCallsFinder finder = new AsyncCallsFinder();
initializerBlock.accept(finder, trees());
return finder.found;
}
private class AsyncCallsFinder extends TreeScanner {
protected boolean found = false;
@Override
public Void scan(Tree tree, Trees p) {
if (found) {
return returnNothing();
}
return super.scan(tree, p);
}
@Override
public Void scan(Iterable nodes, Trees p) {
if (found) {
return returnNothing();
}
return super.scan(nodes, p);
}
@Override
public Void visitMethodInvocation(MethodInvocationTree methodInvocation, Trees trees) {
Element methodSymbol = Util.getElement(methodInvocation.getMethodSelect());
if (methodSymbol instanceof ExecutableElement) {
ExecutableElement method = (ExecutableElement) methodSymbol;
if (isAwait(method)) {
found = true;
return returnNothing();
}
}
return super.visitMethodInvocation(methodInvocation, trees);
}
private boolean isAwait(ExecutableElement method) {
return method.getEnclosingElement() instanceof TypeElement
&& ((TypeElement) method.getEnclosingElement()).getQualifiedName().toString().equals(UTIL_CLASSNAME)
&& method.getSimpleName().toString().equals("await");
}
}
private String avoidJSKeyword(String name) {
if (JSweetConfig.JS_KEYWORDS.contains(name)) {
name = JSweetConfig.JS_KEYWORD_PREFIX + name;
}
return name;
}
private boolean isLazyInitialized(VariableElement var) {
return context.options.isLazyInitializedStatics() && var.getModifiers().contains(Modifier.STATIC)
&& context.lazyInitializedStatics.contains(var)
&& /* enum fields are not lazy initialized */ !(util().isPartOfAnEnum(var)
&& var.getEnclosingElement().equals(Util.getElement(var.asType())));
}
@Override
public Void visitVariable(VariableTree varTree, Trees trees) {
VariableElement varElement = Util.getElement(varTree);
if (getAdapter().substituteVariable(varElement)) {
return returnNothing();
}
if (context.hasAnnotationType(varElement, JSweetConfig.ANNOTATION_ERASED)) {
// erased elements are ignored
return returnNothing();
}
if (context.hasAnnotationType(varElement, JSweetConfig.ANNOTATION_STRING_TYPE)) {
// string type fields are ignored
return returnNothing();
}
if (getScope().enumScope) {
// we print the doc comment for information, but
// TypeScript-generated cannot be recognized by JSDoc... so this
// comment will be ignored and shall be inserted in the enum
// document with a @property annotation
printDocComment(varTree, true);
print(varTree.getName().toString());
if (varTree.getInitializer() instanceof NewClassTree) {
NewClassTree newClass = (NewClassTree) varTree.getInitializer();
if (newClass.getClassBody() != null) {
initAnonymousClass(newClass);
}
}
} else {
Tree parent = getParent();
if (getScope().enumWrapperClassScope && varElement.getKind() == ElementKind.ENUM_CONSTANT) {
return returnNothing();
}
String name = getIdentifier(varElement);
if (context.getFieldNameMapping(varElement) != null) {
name = context.getFieldNameMapping(varElement);
}
boolean confictInDefinitionScope = false;
if (parent instanceof ClassTree) {
ExecutableElement parentMethod = util().findMethodDeclarationInType(Util.getElement((ClassTree) parent), name,
null);
if (parentMethod != null) {
if (!isDefinitionScope) {
report(varTree, varTree.getName(), JSweetProblem.FIELD_CONFLICTS_METHOD, name,
parentMethod.getEnclosingElement());
} else {
confictInDefinitionScope = true;
}
}
if (!getScope().interfaceScope && name.equals("constructor")) {
report(varTree, varTree.getName(), JSweetProblem.CONSTRUCTOR_MEMBER);
}
} else {
if (!context.useModules) {
if (context.importedTopPackages.contains(name)) {
name = "__var_" + name;
}
}
if (JSweetConfig.JS_KEYWORDS.contains(name)) {
report(varTree, varTree.getName(), JSweetProblem.JS_KEYWORD_CONFLICT, name, name);
name = JSweetConfig.JS_KEYWORD_PREFIX + name;
}
}
boolean globals = (parent instanceof ClassTree)
&& JSweetConfig.GLOBALS_CLASS_NAME.equals(((ClassTree) parent).getSimpleName().toString());
if (globals && !varTree.getModifiers().getFlags().contains(Modifier.STATIC)) {
report(varTree, varTree.getName(), JSweetProblem.GLOBALS_CAN_ONLY_HAVE_STATIC_MEMBERS);
return returnNothing();
}
globals = globals || (parent instanceof ClassTree && ((ClassTree) parent).getKind() == Kind.INTERFACE)
|| (getScope().interfaceScope && varElement.getModifiers().contains(Modifier.STATIC));
if (parent instanceof ClassTree) {
printDocComment(varTree);
}
print(varTree.getModifiers());
if (!globals && parent instanceof ClassTree) {
if (varTree.getModifiers().getFlags().contains(Modifier.PUBLIC)) {
if (!getScope().interfaceScope) {
print("public ");
}
}
if (varTree.getModifiers().getFlags().contains(Modifier.PRIVATE)) {
if (!getScope().interfaceScope) {
if (!getScope().innerClass && !varTree.getModifiers().getFlags().contains(Modifier.STATIC)) {
// cannot keep private fields because they may be
// accessed in an inner class
print("/*private*/ ");
}
} else {
report(varTree, varTree.getName(), JSweetProblem.INVALID_PRIVATE_IN_INTERFACE,
varTree.getName(), ((ClassTree) parent).getSimpleName());
}
}
if (varTree.getModifiers().getFlags().contains(Modifier.STATIC)) {
if (!getScope().interfaceScope) {
print("static ");
}
}
}
if (!getScope().interfaceScope && parent instanceof ClassTree) {
if (context.hasAnnotationType(varElement, JSweetConfig.ANNOTATION_OPTIONAL)) {
report(varTree, varTree.getName(), JSweetProblem.USELESS_OPTIONAL_ANNOTATION, varTree.getName(),
((ClassTree) parent).getSimpleName());
}
}
boolean ambient = context.hasAnnotationType(varElement, JSweetConfig.ANNOTATION_AMBIENT);
if (globals || !(parent instanceof ClassTree || parent instanceof MethodTree
|| parent instanceof LambdaExpressionTree)) {
if (globals) {
if (context.hasAnnotationType(varElement, JSweetConfig.ANNOTATION_MODULE)) {
getContext().addExportedElement(context.getAnnotationValue(varElement,
JSweetConfig.ANNOTATION_MODULE, String.class, null), varElement, getCompilationUnit());
}
if (context.useModules || context.moduleBundleMode) {
if (!varTree.getModifiers().getFlags().contains(Modifier.PRIVATE)) {
print("export ");
}
} else {
if (!isTopLevelScope()) {
print("export ");
}
}
if (ambient || (isTopLevelScope() && isDefinitionScope)) {
print("declare ");
}
}
if (!(inArgListTail && (parent instanceof ForLoopTree))) {
if (!getAdapter().substituteVariableDeclarationKeyword(varElement)) {
if (isDefinitionScope) {
print("var ");
} else {
if (!isLazyInitialized(varElement) && ((!globals && context.constAnalyzer != null
&& !context.constAnalyzer.getModifiedVariables().contains(varElement))
|| (globals && varElement.getModifiers().contains(Modifier.FINAL)
&& varTree.getInitializer() != null))) {
print("const ");
} else {
print(VAR_DECL_KEYWORD + " ");
}
}
}
}
} else {
if (ambient) {
report(varTree, varTree.getName(), JSweetProblem.WRONG_USE_OF_AMBIENT, varTree.getName());
}
}
if (util().isVarargs(varTree)) {
print("...");
}
if (doesMemberNameRequireQuotes(name)) {
print("'" + name + "'");
} else {
print(name);
}
if (!util().isVarargs(varTree)
&& (getScope().isEraseVariableTypes() || (getScope().interfaceScope
&& context.hasAnnotationType(varElement, JSweetConfig.ANNOTATION_OPTIONAL)))) {
print("?");
}
if (!getScope().skipTypeAnnotations && !getScope().enumWrapperClassScope) {
if (typeChecker.checkType(varTree, varTree.getName(), varTree.getType(),
compilationUnit)) {
print(": ");
if (confictInDefinitionScope) {
print("any");
} else {
if (getScope().isEraseVariableTypes()) {
print("any");
if (util().isVarargs(varTree)) {
print("[]");
}
} else {
Element varTypeElement = Util.getTypeElement(varTree.getType());
if (varTypeElement != null
&& context.hasAnnotationType(varTypeElement, ANNOTATION_STRING_TYPE)
&& !util().isPartOfAnEnum(varTypeElement)) {
print(getStringLiteralQuote());
print(context.getAnnotationValue(varTypeElement, ANNOTATION_STRING_TYPE, String.class,
varTypeElement.getSimpleName().toString()).toString());
print(getStringLiteralQuote());
} else {
substituteAndPrintType(varTree.getType());
}
}
}
}
}
if (isLazyInitialized(varElement)) {
ClassTree clazz = (ClassTree) parent;
TypeElement classTypeElement = Util.getElement(clazz);
String prefix = getClassName(classTypeElement);
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(varTree.getType());
print(" { ");
int liCount = context.getStaticInitializerCount(classTypeElement);
if (liCount > 0) {
if (!globals) {
print(prefix + "__static_initialize(); ");
}
}
if (varTree.getInitializer() != null && !isDefinitionScope) {
print("if (" + prefix).print(name).print(" == null) { ").print(prefix).print(name).print(" = ");
/*
* if (getScope().enumWrapperClassScope) { NewClassTree newClass =
* (NewClassTree) varDecl.init; print("new "
* ).print(clazz.getSimpleName().toString()).print("(") .printArgList(null,
* newClass.args).print(")"); } else {
*/
if (!substituteAssignedExpression(Util.getType(varTree), varTree.getInitializer())) {
print(varTree.getInitializer());
}
// }
print("; } ");
}
print(" return ").print(prefix).print(name).print("; }");
if (!globals && context.bundleMode) {
String qualifiedClassName = getQualifiedTypeName(classTypeElement, globals, true);
if (util().isPartOfAnEnum(classTypeElement)) {
qualifiedClassName += ENUM_WRAPPER_CLASS_SUFFIX;
}
context.addTopFooterStatement((isBlank(qualifiedClassName) ? "" : qualifiedClassName + ".") + name
+ STATIC_INITIALIZATION_SUFFIX + "();");
}
} else {
if (varTree.getInitializer() != null && !isDefinitionScope) {
if (!(parent instanceof ClassTree && getScope().innerClassNotStatic
&& !varElement.getModifiers().contains(Modifier.STATIC)
&& !util().isConstantOrNullField(varTree))) {
if (!globals && parent instanceof ClassTree && getScope().interfaceScope) {
report(varTree, varTree.getName(), JSweetProblem.INVALID_FIELD_INITIALIZER_IN_INTERFACE,
varTree.getName(), ((ClassTree) parent).getSimpleName());
} else {
if (!getScope().fieldsWithInitializers.contains(varTree)) {
print(" = ");
if (!substituteAssignedExpression(Util.getType(varTree), varTree.getInitializer())) {
print(varTree.getInitializer());
}
}
}
}
}
// var initialization is not allowed in definition
if (!isDefinitionScope && !(ambient || (isTopLevelScope() && isDefinitionScope))
&& varElement.getModifiers().contains(Modifier.STATIC) && varTree.getInitializer() == null) {
print(" = ").print(getAdapter().getVariableInitialValue(varElement));
}
}
}
return returnNothing();
}
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 visitParenthesized(ParenthesizedTree parens, Trees trees) {
print("(");
super.visitParenthesized(parens, trees);
print(")");
return returnNothing();
}
@Override
public Void visitImport(ImportTree importTree, Trees trees) {
String qualId = importTree.getQualifiedIdentifier().toString();
if (context.useModules && qualId.endsWith("*")
&& !(qualId.endsWith("." + JSweetConfig.GLOBALS_CLASS_NAME + ".*")
|| qualId.equals(JSweetConfig.UTIL_CLASSNAME + ".*"))) {
report(importTree, JSweetProblem.WILDCARD_IMPORT);
return returnNothing();
}
String adaptedQualId = getAdapter().needsImport(createExtendedElement(importTree), qualId);
if (adaptedQualId != null && adaptedQualId.contains(".")) {
if (!(importTree.isStatic() && !qualId.contains("." + JSweetConfig.GLOBALS_CLASS_NAME + ".")
&& !qualId.contains("." + JSweetConfig.STRING_TYPES_INTERFACE_NAME + "."))) {
String[] namePath;
if (context.useModules && importTree.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(";").println();
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 returnNothing();
}
context.globalImports.add(name);
}
if (!context.useModules) {
// in bundle mode, we do not use imports to minimize
// dependencies
// (imports create unavoidable dependencies!)
context.importedTopPackages.add(namePath[0]);
} else {
if (!context.getImportedNames(compilationUnit.getSourceFile().getName()).contains(name)) {
print("import ").print(name).print(" = ").print(adaptedQualId).print(";").println();
context.registerImportedName(compilationUnit.getSourceFile().getName(), null, name);
}
}
}
}
}
return returnNothing();
}
private void printInnerClassAccess(String accessedElementName, ElementKind kind) {
printInnerClassAccess(accessedElementName, kind, null);
}
private void printInnerClassAccess(String accessedElementName, ElementKind kind, Integer methodArgsCount) {
print("this.");
ClassTree parent = getParent(ClassTree.class);
TypeElement parentTypeElement = Util.getElement(parent);
int level = 0;
boolean foundInParent = util().findFirstDeclarationInClassAndSuperClasses(parentTypeElement,
accessedElementName, kind, methodArgsCount) != null;
if (!foundInParent) {
while (getScope(level++).innerClassNotStatic) {
parent = getParent(ClassTree.class, parent);
parentTypeElement = Util.getElement(parent);
if (parent != null && util().findFirstDeclarationInClassAndSuperClasses(parentTypeElement,
accessedElementName, kind, methodArgsCount) != null) {
foundInParent = true;
break;
}
}
}
if (foundInParent && level > 0) {
if (getScope().constructor) {
removeLastChars(5);
}
for (int i = 0; i < level; i++) {
print(PARENT_CLASS_FIELD_NAME + ".");
}
}
}
public boolean printClass(TypeMirror type) {
Element element = Util.getElement(type);
TypeElement typeElement = element instanceof TypeElement ? (TypeElement) element : null;
String className = typeElement == null ? type.toString() : typeElement.toString();
if (context.isMappedType(className)) {
String target = context.getTypeMappingTarget(className);
if (CONSTRUCTOR_TYPE_MAPPING.containsKey(target)) {
print(mapConstructorType(target));
return true;
} else if (typeElement != null) {
print(getStringLiteralQuote())
.print(context.getRootRelativeJavaName(typeElement))
.print(getStringLiteralQuote());
return true;
}
} else {
if (CONSTRUCTOR_TYPE_MAPPING.containsKey(className)) {
print(mapConstructorType(className));
return true;
}
}
return false;
}
@Override
public Void visitMemberSelect(MemberSelectTree memberSelectTree, Trees trees) {
if (!getAdapter().substitute(createExtendedElement(memberSelectTree))) {
Element selectedElement = null;
if (memberSelectTree.getExpression() instanceof MemberSelectTree
|| memberSelectTree.getExpression() instanceof IdentifierTree) {
selectedElement = Util.getElement(memberSelectTree.getExpression());
}
TypeMirror selectedType = Util.getType(memberSelectTree.getExpression());
Element selectedTypeElement = Util.getTypeElement(memberSelectTree.getExpression());
Element memberElement = Util.getElement(memberSelectTree);
TypeMirror memberType = Util.getType(memberSelectTree);
Element memberTypeElement = Util.getTypeElement(memberSelectTree);
if (selectedElement instanceof PackageElement) {
if (context.isRootPackage(selectedElement)) {
if (memberTypeElement != null) {
printIdentifier(memberTypeElement);
} else {
print(memberSelectTree.getIdentifier().toString());
}
return returnNothing();
}
}
if ("class".equals(memberSelectTree.getIdentifier().toString())) {
if (memberType.getKind() == TypeKind.DECLARED //
&& util().getFirstTypeArgumentAsElement((DeclaredType) memberType) != null //
&& context.isInterface(util().getFirstTypeArgumentAsElement((DeclaredType) memberType))) {
print(getStringLiteralQuote())
.print(context.getRootRelativeJavaName(
util().getFirstTypeArgumentAsElement((DeclaredType) memberType)))
.print(getStringLiteralQuote());
} else {
if (!printClass(selectedType)) {
String name = selectedTypeElement == null ? selectedType.toString()
: selectedTypeElement.toString();
if (context.isMappedType(name)) {
Element firstTypeArgumentAsElement = util()
.getFirstTypeArgumentAsElement((DeclaredType) memberType);
print(getStringLiteralQuote())
.print(context.getRootRelativeJavaName(firstTypeArgumentAsElement))
.print(getStringLiteralQuote());
} else {
print(memberSelectTree.getExpression());
}
}
}
} else if ("this".equals(memberSelectTree.getIdentifier().toString())) {
print("this");
if (getScope().innerClassNotStatic) {
ClassTree parent = getParent(ClassTree.class);
TypeElement parentTypeElement = Util.getElement(parent);
int level = 0;
boolean foundInParent = false;
while (getScope(level++).innerClassNotStatic) {
parent = getParent(ClassTree.class, parent);
parentTypeElement = Util.getElement(parent);
if (parent != null && parentTypeElement.equals(selectedTypeElement)) {
foundInParent = true;
break;
}
}
if (foundInParent && level > 0) {
print(".");
if (getScope().constructor) {
removeLastChars(5);
}
for (int i = 0; i < level; i++) {
print(PARENT_CLASS_FIELD_NAME + ".");
}
removeLastChar();
}
}
} else {
String selected = memberSelectTree.getExpression().toString();
if (!selected.equals(GLOBALS_CLASS_NAME)) {
if (selected.equals("super") && (memberElement instanceof VariableElement)) {
print("this.");
} else if (getScope().innerClassNotStatic
&& ("this".equals(selected) || selected.endsWith(".this"))) {
printInnerClassAccess(memberSelectTree.getIdentifier().toString(), ElementKind.FIELD);
} else {
boolean accessSubstituted = false;
if (memberElement instanceof VariableElement) {
VariableElement varElement = (VariableElement) memberElement;
if (varElement.getModifiers().contains(Modifier.STATIC)
&& varElement.getEnclosingElement() != selectedElement) {
accessSubstituted = true;
print(getRootRelativeName(varElement.getEnclosingElement())).print(".");
ensureModuleIsUsed(varElement.getEnclosingElement());
}
} else if (selectedElement instanceof PackageElement && context.useModules
&& !context.moduleBundleMode && memberElement instanceof TypeElement
&& util().isSourceElement(memberElement)) {
accessSubstituted = true;
ModuleImportDescriptor moduleImport = getAdapter().getModuleImportDescriptor(
getCompilationUnitElement(), memberElement.getSimpleName().toString(),
(TypeElement) memberElement);
if (moduleImport != null) {
useModule(moduleImport);
}
}
if (!accessSubstituted) {
print(memberSelectTree.getExpression()).print(".");
}
}
}
String fieldName = null;
if (memberElement instanceof VariableElement
&& context.getFieldNameMapping((VariableElement) memberElement) != null) {
fieldName = context.getFieldNameMapping((VariableElement) memberElement);
} else {
fieldName = getIdentifier(memberElement);
}
if (memberElement instanceof TypeElement) {
if (context.hasClassNameMapping((TypeElement) memberElement)) {
fieldName = context.getClassNameMapping((TypeElement) selectedElement);
}
}
if (doesMemberNameRequireQuotes(fieldName)) {
if (getLastPrintedChar() == '.') {
removeLastChar();
print("['").print(fieldName).print("']");
} else {
print("this['").print(fieldName).print("']");
}
} else {
print(fieldName);
}
if (memberElement instanceof VariableElement && isLazyInitialized((VariableElement) memberElement)) {
if (!staticInitializedAssignment) {
print(STATIC_INITIALIZATION_SUFFIX + "()");
}
}
}
}
return returnNothing();
}
@Override
public Void visitMethodInvocation(MethodInvocationTree methodInvocationTree, Trees trees) {
boolean debugMode = false;
if (context.options.isDebugMode()) {
Element methodElement = Util.getElement(methodInvocationTree.getMethodSelect());
if (methodElement instanceof ExecutableElement) {
if (methodElement.getKind() != ElementKind.CONSTRUCTOR && util().isSourceElement(methodElement)) {
debugMode = true;
}
}
}
if (debugMode) {
print("__debug_result(yield ");
}
getAdapter().substituteMethodInvocation(createExtendedElement(methodInvocationTree));
if (debugMode) {
print(")");
}
return returnNothing();
}
/**
* Prints a method invocation tree (default behavior).
*/
public void printDefaultMethodInvocation(MethodInvocationTree methodInvocationTree) {
String meth = methodInvocationTree.getMethodSelect().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;
}
if (context.isAwaitInvocation(methodInvocationTree)) {
if (getParent() instanceof MethodInvocationTree) {
print("(");
}
print("await ");
}
boolean anonymous = isAnonymousMethod(methName);
boolean targetIsThis = meth.equals("this." + methName);
boolean targetIsThisOrStaticImported = (meth.equals(methName) || targetIsThis);
ExecutableType type = Util.getType(methodInvocationTree.getMethodSelect()) instanceof ExecutableType
? Util.getType(methodInvocationTree.getMethodSelect())
: null;
ExecutableElement methSym = null;
String methodName = null;
boolean keywordHandled = false;
if (targetIsThisOrStaticImported) {
ImportTree staticImport = getStaticGlobalImport(methName);
if (staticImport == null || targetIsThis) {
ClassTree p = getParent(ClassTree.class);
methSym = p == null ? null : util().findMethodDeclarationInType(Util.getElement(p), methName, type);
if (methSym != null) {
typeChecker.checkApply(methodInvocationTree, methSym);
if (!methSym.getModifiers().contains(Modifier.STATIC)) {
if (!meth.startsWith("this.")) {
print("this");
if (!anonymous) {
print(".");
}
}
} else {
if (meth.startsWith("this.") && methSym.getModifiers().contains(Modifier.STATIC)) {
report(methodInvocationTree, JSweetProblem.CANNOT_ACCESS_STATIC_MEMBER_ON_THIS,
methSym.getSimpleName());
}
Element methodOwner = util().getParentElement(methSym, TypeElement.class);
if (!JSweetConfig.GLOBALS_CLASS_NAME.equals(methodOwner.getSimpleName().toString())) {
print("" + methodOwner.getSimpleName());
if (util().isPartOfAnEnum(methodOwner)) {
print(Java2TypeScriptTranslator.ENUM_WRAPPER_CLASS_SUFFIX);
}
if (!anonymous) {
print(".");
}
}
}
} else {
if (getScope().defaultMethodScope) {
TypeElement target = util().getStaticImportTarget(
getContext().getDefaultMethodCompilationUnit(getParent(MethodTree.class)), methName);
if (target != null) {
print(getRootRelativeName(target) + ".");
}
} else {
TypeElement target = util().getStaticImportTarget(getCompilationUnit(), methName);
if (target != null) {
print(getRootRelativeName(target) + ".");
}
}
if (getScope().innerClass) {
ClassTree parent = getParent(ClassTree.class);
int level = 0;
ExecutableElement method = null;
if (parent != null) {
while (getScope(level++).innerClass) {
parent = getParent(ClassTree.class, parent);
if ((method = util().findMethodDeclarationInType(Util.getElement(parent), methName,
type)) != null) {
break;
}
}
}
if (method != null) {
if (method.getModifiers().contains(Modifier.STATIC)) {
print(method.getEnclosingElement().getSimpleName().toString() + ".");
} else {
if (level == 0 || !getScope().constructor) {
print("this.");
}
for (int i = 0; i < level; i++) {
print(Java2TypeScriptTranslator.PARENT_CLASS_FIELD_NAME + ".");
}
if (anonymous) {
removeLastChar();
}
}
}
}
}
} else {
MemberSelectTree staticFieldAccess = (MemberSelectTree) staticImport.getQualifiedIdentifier();
methSym = util().findMethodDeclarationInType(Util.getTypeElement(staticFieldAccess.getExpression()), methName,
type);
if (methSym != null) {
Map vars = new HashMap<>();
util().fillAllVariablesInScope(vars, getStack(), methodInvocationTree, getParent(MethodTree.class),
getCompilationUnit());
if (vars.containsKey(methSym.getSimpleName().toString())) {
report(methodInvocationTree, JSweetProblem.HIDDEN_INVOCATION, methSym.getSimpleName());
}
Element methodOwner = util().getParentElement(methSym, TypeElement.class);
if (!context.useModules && methodOwner.getSimpleName().toString().equals(GLOBALS_CLASS_NAME)
&& methodOwner.getEnclosingElement() != null && !methodOwner.getEnclosingElement()
.getSimpleName().toString().equals(GLOBALS_PACKAGE_NAME)) {
String prefix = getRootRelativeName(methodOwner.getEnclosingElement());
if (!StringUtils.isEmpty(prefix)) {
print(getRootRelativeName(methodOwner.getEnclosingElement()) + ".");
}
}
}
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(util().getQualifiedName(methSym.getEnclosingElement()))) {
methodName = methName.toLowerCase();
}
}
}
} else {
if (methodInvocationTree.getMethodSelect() instanceof MemberSelectTree) {
ExpressionTree selected = ((MemberSelectTree) methodInvocationTree.getMethodSelect()).getExpression();
Element selectedTypeElement = Util.getTypeElement(selected);
if (context.isFunctionalType(selectedTypeElement)) {
anonymous = true;
}
if (selectedTypeElement instanceof TypeParameterElement) {
TypeParameterElement typeParameterElement = ((TypeParameterElement) selectedTypeElement);
for (TypeMirror genericBound : typeParameterElement.getBounds()) {
TypeElement genericBoundTypeElement = (TypeElement) types().asElement(genericBound);
methSym = util().findMethodDeclarationInType(genericBoundTypeElement, methName, type);
if (methSym != null) {
break;
}
}
} else {
methSym = util().findMethodDeclarationInType((TypeElement) selectedTypeElement, methName, type);
}
if (methSym != null) {
typeChecker.checkApply(methodInvocationTree, methSym);
}
}
}
boolean isStatic = methSym == null || methSym.getModifiers().contains(Modifier.STATIC);
List arguments = methodInvocationTree.getArguments();
if (!util().hasVarargs(methSym) //
|| !arguments.isEmpty() && (Util.getType(util().last(arguments)).getKind() != TypeKind.ARRAY
// we dont use apply if var args type differ
|| !types().erasure(((ArrayType) Util.getType(util().last(arguments))).getComponentType())
.equals(types().erasure(((ArrayType) util().last(methSym.getParameters()).asType())
.getComponentType())))) {
applyVarargs = false;
}
List substitutionArgs = new ArrayList<>(methodInvocationTree.getArguments());
if (methSym != null && applyVarargs && arguments.size() > 0 && arguments.size() == methSym.getParameters().size()) {
ExpressionTree expr = arguments.get(arguments.size() - 1);
NewArrayTree newArrayExpr = null;
if (expr instanceof NewArrayTree) {
newArrayExpr =(NewArrayTree) expr;
} else if (expr instanceof TypeCastTree && ((TypeCastTree)expr).getExpression() instanceof NewArrayTree) {
newArrayExpr =(NewArrayTree) ((TypeCastTree)expr).getExpression();
}
if (newArrayExpr != null) {
substitutionArgs = new ArrayList<>(arguments.subList(0, arguments.size() - 1));
applyVarargs = false;
if (newArrayExpr.getInitializers() != null) {
substitutionArgs.addAll(newArrayExpr.getInitializers());
}
}
if (expr instanceof LiteralTree && "null".equals(expr.toString()) ||
expr instanceof TypeCastTree && ((TypeCastTree)expr).getExpression() instanceof LiteralTree
&& "null".equals(((TypeCastTree)expr).getExpression().toString())) {
substitutionArgs = new ArrayList<>(arguments.subList(0, arguments.size() - 1));
applyVarargs = false;
}
}
if (anonymous) {
applyVarargs = false;
if (methodInvocationTree.getMethodSelect() instanceof MemberSelectTree) {
ExpressionTree selected = ((MemberSelectTree) methodInvocationTree.getMethodSelect()).getExpression();
print(selected);
}
} else {
// method with name
if (methodInvocationTree.getMethodSelect() instanceof MemberSelectTree && applyVarargs
&& !targetIsThisOrStaticImported && !isStatic) {
print("(o => o");
String accessedMemberName;
if (keywordHandled) {
accessedMemberName = ((MemberSelectTree) methodInvocationTree.getMethodSelect()).getIdentifier()
.toString();
} else {
if (methSym == null) {
methSym = Util.getElement((MemberSelectTree) methodInvocationTree.getMethodSelect());
}
if (methSym != null) {
accessedMemberName = context.getActualName(methSym);
} else {
accessedMemberName = ((MemberSelectTree) methodInvocationTree.getMethodSelect()).getIdentifier()
.toString();
}
}
print(getTSMemberAccess(accessedMemberName, true));
} else if (methodName != null) {
print(getTSMemberAccess(methodName, removeLastChar('.')));
} else {
if (keywordHandled) {
print(methodInvocationTree.getMethodSelect());
} else {
if (methSym == null && methodInvocationTree.getMethodSelect() instanceof MemberSelectTree
&& Util.getElement((MemberSelectTree) methodInvocationTree
.getMethodSelect()) instanceof ExecutableElement) {
methSym = Util.getElement((MemberSelectTree) methodInvocationTree.getMethodSelect());
}
if (methSym != null && methodInvocationTree.getMethodSelect() instanceof MemberSelectTree) {
ExpressionTree selected = ((MemberSelectTree) methodInvocationTree.getMethodSelect())
.getExpression();
Element selectedTypeElement = Util.getTypeElement(selected);
if (selectedTypeElement == null
|| !GLOBALS_CLASS_NAME.equals(selectedTypeElement.getSimpleName().toString())) {
if (getScope().innerClassNotStatic
&& ("this".equals(selected.toString()) || selected.toString().endsWith(".this"))) {
printInnerClassAccess(methSym.getSimpleName().toString(), methSym.getKind(),
methSym.getParameters().size());
} else {
if (methSym.getModifiers().contains(Modifier.STATIC)
&& selected instanceof IdentifierTree
&& Util.getElement(selected) instanceof VariableElement) {
// case of instance static access
if (context.useModules && !context.moduleBundleMode) {
print(getClassName((TypeElement) selectedTypeElement));
ModuleImportDescriptor moduleImport = getAdapter().getModuleImportDescriptor(
getCompilationUnitElement(),
selectedTypeElement.getSimpleName().toString(),
(TypeElement) selectedTypeElement);
if (moduleImport != null) {
useModule(moduleImport);
}
} else {
print(getRootRelativeName(selectedTypeElement));
}
} else {
print(selected);
}
print(".");
}
} else {
if (methSym.getModifiers().contains(Modifier.STATIC)) {
ModuleImportDescriptor moduleImport = getAdapter().getModuleImportDescriptor(
new CompilationUnitElementSupport(compilationUnit),
methSym.getSimpleName().toString(),
(TypeElement) selectedTypeElement);
if (moduleImport != null) {
useModule(moduleImport);
}
}
Map vars = new HashMap<>();
util().fillAllVariablesInScope(vars, getStack(), methodInvocationTree,
getParent(MethodTree.class), getCompilationUnit());
if (vars.containsKey(methName)) {
report(methodInvocationTree, JSweetProblem.HIDDEN_INVOCATION, methName);
}
}
}
if (methSym != null) {
if (context.isInvalidOverload(methSym) && !util().hasTypeParameters(methSym)
&& !methSym.isDefault() && getParent(MethodTree.class) != null
&& !getParent(MethodTree.class).getModifiers().getFlags().contains(Modifier.DEFAULT)) {
if (context.isInterface((TypeElement) methSym.getEnclosingElement())) {
removeLastChar('.');
print("['" + getOverloadMethodName(methSym) + "']");
} else {
print(getOverloadMethodName(methSym));
}
} else {
print(getTSMemberAccess(context.getActualName(methSym), removeLastChar('.')));
}
} else {
print(methodInvocationTree.getMethodSelect());
}
}
}
}
if (applyVarargs) {
print(".apply");
} else {
if (methodInvocationTree.getTypeArguments() != null && !methodInvocationTree.getTypeArguments().isEmpty()) {
print("<");
for (Tree argument : methodInvocationTree.getTypeArguments()) {
substituteAndPrintType(argument).print(",");
}
// with overloads, missing generic types need to be added because core method
// has all generics
if (methSym != null) {
TypeElement targetType = (TypeElement) methSym.getEnclosingElement();
Overload overload = context.getOverload(targetType, methSym);
if (overload != null && overload.getMethodsCount() > 1) {
int missingArgsCount = overload.getCoreMethodElement().getTypeParameters().size()
- methodInvocationTree.getTypeArguments().size();
print("any,".repeat(missingArgsCount));
}
}
removeLastChar(',');
print(">");
} else {
// force type arguments to any because they are inferred to
// {} by default
if (methSym != null && !methSym.getTypeParameters().isEmpty()) {
TypeElement targetType = (TypeElement) methSym.getEnclosingElement();
if (!targetType.getQualifiedName().toString().startsWith(JSweetConfig.LIBS_PACKAGE + ".")) {
// invalid overload type parameters are erased
Overload overload = context.getOverload(targetType, methSym);
boolean inOverload = overload != null && overload.getMethodsCount() > 1;
if (!(inOverload && !overload.isValid)) {
printAnyTypeArguments(methSym.getTypeParameters().size());
}
}
}
}
}
print("(");
if (applyVarargs) {
String contextVar = "null";
if (targetIsThisOrStaticImported) {
contextVar = "this";
} else if (methodInvocationTree.getMethodSelect() instanceof MemberSelectTree
&& !targetIsThisOrStaticImported && !isStatic) {
contextVar = "o";
}
print(contextVar + ", ");
if (methodInvocationTree.getArguments().size() > 1) {
print("[");
}
}
int argsLength = applyVarargs ? substitutionArgs.size() - 1 : substitutionArgs.size();
if (getScope().innerClassNotStatic && "super".equals(methName)) {
TypeElement s = Util.getTypeElement(getParent(ClassTree.class).getExtendsClause());
if (s.getEnclosingElement() instanceof TypeElement && !s.getModifiers().contains(Modifier.STATIC)) {
print(Java2TypeScriptTranslator.PARENT_CLASS_FIELD_NAME);
if (argsLength > 0) {
print(", ");
}
}
}
if (getScope().enumWrapperClassScope && isAnonymousClass() && "super".equals(methName)) {
print(Java2TypeScriptTranslator.ENUM_WRAPPER_CLASS_ORDINAL + ", "
+ Java2TypeScriptTranslator.ENUM_WRAPPER_CLASS_NAME);
if (argsLength > 0) {
print(", ");
}
}
if ("super".equals(methName)) {
ClassTree p = getParent(ClassTree.class);
methSym = p == null ? null : util().findMethodDeclarationInType(Util.getElement(p), "this", type);
}
TypeMirror methodType = Util.getType(methodInvocationTree.getMethodSelect());
for (int i = 0; i < argsLength; i++) {
ExpressionTree arg = substitutionArgs.get(i);
if (methodType != null && methodType.getKind() == TypeKind.EXECUTABLE) {
// varargs transmission with TS ... notation
List argTypes = ((ExecutableType) methodType).getParameterTypes();
TypeMirror paramType = i < argTypes.size() ? argTypes.get(i) : util().last(argTypes);
if (i == argsLength - 1 && !applyVarargs && methSym != null && methSym.isVarArgs()) {
if (arg instanceof IdentifierTree && Util.getElement((IdentifierTree) arg) instanceof VariableElement) {
VariableElement var = Util.getElement((IdentifierTree) arg);
if (var.getEnclosingElement() instanceof ExecutableElement) {
if (util().isVarargs(var)) {
print("...");
}
}
}
}
if (!substituteAssignedExpression(paramType, arg)) {
print(arg);
}
} else {
// fall back when method type is wrongly resolved
print(arg);
}
if (i < argsLength - 1) {
print(", ");
}
}
if (applyVarargs) {
if (substitutionArgs.size() > 1) {
// we cast array to any[] to avoid concat error on
// different
// types
print("].concat(");
}
if (!substitutionArgs.isEmpty()) {
print(substitutionArgs.get(substitutionArgs.size() - 1));
}
if (substitutionArgs.size() > 1) {
print(")");
}
if (methodInvocationTree.getMethodSelect() instanceof MemberSelectTree && !targetIsThisOrStaticImported
&& !isStatic) {
print("))(").print(((MemberSelectTree) methodInvocationTree.getMethodSelect()).getExpression());
}
}
print(")");
if (context.isAwaitInvocation(methodInvocationTree) && getParent() instanceof MethodInvocationTree) {
print(")");
}
}
private boolean isAnonymousMethod(String methName) {
boolean anonymous = JSweetConfig.ANONYMOUS_FUNCTION_NAME.equals(methName)
|| JSweetConfig.ANONYMOUS_STATIC_FUNCTION_NAME.equals(methName)
|| (context.deprecatedApply && JSweetConfig.ANONYMOUS_DEPRECATED_FUNCTION_NAME.equals(methName))
|| (context.deprecatedApply && JSweetConfig.ANONYMOUS_DEPRECATED_STATIC_FUNCTION_NAME.equals(methName))
|| JSweetConfig.NEW_FUNCTION_NAME.equals(methName);
return anonymous;
}
private ImportTree getStaticGlobalImport(String methName) {
if (compilationUnit == null) {
return null;
}
for (ImportTree importTree : compilationUnit.getImports()) {
if (importTree.isStatic()) {
if (importTree.getQualifiedIdentifier().toString()
.endsWith(JSweetConfig.GLOBALS_CLASS_NAME + "." + methName)) {
return importTree;
}
}
}
return null;
}
private String getStaticContainerFullName(ImportTree importDecl) {
if (importDecl.getQualifiedIdentifier() instanceof MemberSelectTree) {
MemberSelectTree fa = (MemberSelectTree) importDecl.getQualifiedIdentifier();
String name = context.getRootRelativeJavaName(Util.getTypeElement(fa.getExpression()));
// 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 (util().getPackageFullNameForCompilationUnit(compilationUnit).startsWith(name)) {
return null;
}
return name;
}
return null;
}
@Override
public Void visitIdentifier(IdentifierTree identifierTree, Trees trees) {
Element identifierElement =Util.getElement(identifierTree);
String name = identifierTree.toString();
if (getScope().inlinedConstructorArgs != null) {
if (identifierElement instanceof VariableElement && getScope().inlinedConstructorArgs.contains(name)) {
print("__args[" + getScope().inlinedConstructorArgs.indexOf(name) + "]");
return returnNothing();
}
}
if (!getAdapter().substitute(createExtendedElement(identifierTree))) {
boolean lazyInitializedStatic = false;
// add this of class name if ident is a field
if (identifierElement instanceof VariableElement
&& !identifierElement.getSimpleName().toString().equals("this")
&& !identifierElement.getSimpleName().toString().equals("super")) {
VariableElement varElement = (VariableElement) identifierElement;
if (varElement != null) {
Element varEnclosingElement = varElement.getEnclosingElement();
if (varEnclosingElement instanceof TypeElement) {
if (context.getFieldNameMapping(varElement) != null) {
name = context.getFieldNameMapping(varElement);
} else {
name = getIdentifier(varElement);
}
if (!varElement.getModifiers().contains(Modifier.STATIC)) {
printInnerClassAccess(varElement.getSimpleName().toString(), ElementKind.FIELD);
} else {
if (isLazyInitialized(varElement)) {
lazyInitializedStatic = true;
}
if (!util().getQualifiedName(varEnclosingElement).toString()
.endsWith("." + GLOBALS_CLASS_NAME)) {
if (!context.useModules
&& !varEnclosingElement.equals(Util.getElement(getParent(ClassTree.class)))) {
String prefix = context.getRootRelativeName(null, varEnclosingElement);
if (!StringUtils.isEmpty(prefix)) {
print(context.getRootRelativeName(null, varEnclosingElement));
if (lazyInitializedStatic && util().isPartOfAnEnum(varEnclosingElement)) {
print(ENUM_WRAPPER_CLASS_SUFFIX);
}
print(".");
}
} else {
if (!varEnclosingElement.getSimpleName().toString().equals(GLOBALS_PACKAGE_NAME)) {
print(varEnclosingElement.getSimpleName().toString());
ensureModuleIsUsed(varEnclosingElement);
if (lazyInitializedStatic && util().isPartOfAnEnum(varEnclosingElement)) {
print(ENUM_WRAPPER_CLASS_SUFFIX);
}
print(".");
}
}
} else {
if (!context.useModules) {
String prefix = context.getRootRelativeName(null, varEnclosingElement);
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 (varEnclosingElement instanceof ExecutableElement && isAnonymousClass()
&& getScope(1).finalVariables
.get(getScope(1).anonymousClasses.indexOf(getParent(ClassTree.class)))
.contains(varElement)) {
print("this.");
} else {
if (!context.useModules && varEnclosingElement instanceof ExecutableElement) {
if (context.importedTopPackages.contains(name)) {
name = "__var_" + name;
}
}
if (JSweetConfig.JS_KEYWORDS.contains(name)) {
name = JSweetConfig.JS_KEYWORD_PREFIX + name;
}
}
}
}
}
if (identifierElement instanceof TypeElement) {
TypeElement classIdentifierTypeElement = (TypeElement) identifierElement;
boolean prefixAdded = false;
if (getScope().defaultMethodScope) {
if (util().isImported(getContext().getDefaultMethodCompilationUnit(getParent(MethodTree.class)),
classIdentifierTypeElement)) {
String rootRelativeName = getRootRelativeName(classIdentifierTypeElement.getEnclosingElement());
if (!rootRelativeName.isEmpty()) {
print(rootRelativeName + ".");
PackageElement identifierPackage = util().getParentElement(classIdentifierTypeElement,
PackageElement.class);
String pathToModulePackage = util().getRelativePath(Util.getElement(compilationUnit.getPackage()),
identifierPackage);
if (pathToModulePackage == null) {
pathToModulePackage = ".";
}
File moduleFile = new File(new File(pathToModulePackage),
classIdentifierTypeElement.getEnclosingElement().getSimpleName().toString());
useModule(false, false, identifierPackage, identifierTree,
classIdentifierTypeElement.getEnclosingElement().getSimpleName().toString(),
moduleFile.getPath().replace('\\', '/'), null);
}
prefixAdded = true;
}
}
// add parent class name if ident is an inner class of the
// current class
if (!prefixAdded && classIdentifierTypeElement.getEnclosingElement() instanceof TypeElement) {
if (context.useModules) {
String enclosingName = classIdentifierTypeElement.getEnclosingElement().getSimpleName().toString();
if (context.hasClassNameMapping((TypeElement)classIdentifierTypeElement.getEnclosingElement())) {
enclosingName = context.getClassNameMapping((TypeElement)classIdentifierTypeElement.getEnclosingElement());
}
print(enclosingName + ".");
prefixAdded = true;
} else {
// if the class has not been imported, we need to add
// the containing class prefix
if (!getCompilationUnit().getImports().stream()
.map(i -> Util.getTypeElement(i.getQualifiedIdentifier()))
.anyMatch(t -> t == classIdentifierTypeElement)) {
if (classIdentifierTypeElement.getEnclosingElement() instanceof TypeElement) {
print(getClassName((TypeElement) classIdentifierTypeElement.getEnclosingElement())
+ ".");
} else {
print(classIdentifierTypeElement.getEnclosingElement().getSimpleName() + ".");
}
prefixAdded = true;
}
}
}
if (!prefixAdded && !context.useModules
&& !classIdentifierTypeElement.equals(Util.getElement(getParent(ClassTree.class)))) {
print(getRootRelativeName(classIdentifierTypeElement));
} else {
if (context.hasClassNameMapping(classIdentifierTypeElement)) {
print(context.getClassNameMapping(classIdentifierTypeElement));
} 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 + "()");
}
}
}
}
return returnNothing();
}
/**
* Prints a type apply (T
) tree.
*/
@Override
public Void visitParameterizedType(ParameterizedTypeTree typeApply, Trees trees) {
substituteAndPrintType(typeApply);
return returnNothing();
}
private int initAnonymousClass(NewClassTree newClass) {
int anonymousClassIndex = getScope().anonymousClasses.indexOf(newClass.getClassBody());
if (anonymousClassIndex == -1) {
anonymousClassIndex = getScope().anonymousClasses.size();
getScope().anonymousClasses.add(newClass.getClassBody());
getScope().anonymousClassesConstructors.add(newClass);
LinkedHashSet finalVars = new LinkedHashSet<>();
getScope().finalVariables.add(finalVars);
Element newClassTypeElement = Util.getElement(newClass.getClassBody());
new TreeScanner() {
@Override
public Void visitIdentifier(IdentifierTree var, Trees trees) {
if (Util.getElement(var) instanceof VariableElement) {
VariableElement varElement = Util.getElement(var);
if (varElement.getEnclosingElement() instanceof ExecutableElement) {
Element methodEnclosingTypeElement = varElement.getEnclosingElement().getEnclosingElement();
// search through method owners to match anonymous class to know if var (method
// param) is declared in anonymous class
// if so, we do not need it as an anonymous class param
boolean finalVariableFromThisClass = false;
Element wrappingTypeElement = methodEnclosingTypeElement;
do {
if (wrappingTypeElement == newClassTypeElement) {
finalVariableFromThisClass = true;
break;
}
wrappingTypeElement = wrappingTypeElement.getEnclosingElement();
} while (wrappingTypeElement != null);
if (!finalVariableFromThisClass) {
finalVars.add(varElement);
}
}
}
return returnNothing();
}
}.visitClass(newClass.getClassBody(), trees());
}
return anonymousClassIndex;
}
/**
* Prints a new-class expression tree.
*/
@Override
public Void visitNewClass(NewClassTree newClass, Trees trees) {
TypeElement classTypeElement = Util.getTypeElement(newClass.getIdentifier());
if (classTypeElement.getSimpleName().toString().equals(JSweetConfig.GLOBALS_CLASS_NAME)) {
report(newClass, JSweetProblem.GLOBAL_CANNOT_BE_INSTANTIATED);
return returnNothing();
}
if (getScope().isLocalClassType(classTypeElement.asType())) {
print("new ").print(getScope().getName() + ".").print(newClass.getIdentifier().toString());
print("(").printConstructorArgList(newClass, true).print(")");
return returnNothing();
}
boolean isInterface = context.isInterface(classTypeElement);
if (newClass.getClassBody() != null || isInterface) {
if (context.isAnonymousClass(newClass, getCompilationUnit())) {
int anonymousClassIndex = initAnonymousClass(newClass);
print("new ").print(
getScope().getName() + "." + getScope().getName() + ANONYMOUS_PREFIX + anonymousClassIndex);
if (isStaticAnonymousClass(newClass, getCompilationUnit())) {
printAnonymousClassTypeArgs(newClass);
}
print("(");
printConstructorArgList(newClass, false);
print(")");
return returnNothing();
}
if (isInterface || context.hasAnnotationType(classTypeElement, JSweetConfig.ANNOTATION_OBJECT_TYPE)) {
if (isInterface) {
print("");
}
Set interfaces = new HashSet<>();
context.grabSupportedInterfaceNames(interfaces, classTypeElement, getAdapter());
if (!interfaces.isEmpty()) {
print("Object.defineProperty(");
}
print("{").println().startIndent();
boolean statementPrinted = false;
boolean initializationBlockFound = false;
if (newClass.getClassBody() != null) {
for (Tree m : newClass.getClassBody().getMembers()) {
if (m instanceof BlockTree) {
initializationBlockFound = true;
List initializedVars = new ArrayList<>();
for (Tree s : ((BlockTree) m).getStatements()) {
boolean currentStatementPrinted = false;
if (s instanceof ExpressionStatementTree
&& ((ExpressionStatementTree) s).getExpression() instanceof AssignmentTree) {
AssignmentTree assignment = (AssignmentTree) ((ExpressionStatementTree) s)
.getExpression();
VariableElement var = null;
if (assignment.getVariable() instanceof MemberSelectTree) {
var = util().findFieldDeclaration(classTypeElement,
((MemberSelectTree) assignment.getVariable()).getIdentifier());
printIndent().print(var.getSimpleName().toString());
} else if (assignment.getVariable() instanceof IdentifierTree) {
var = util().findFieldDeclaration(classTypeElement,
((IdentifierTree) assignment.getVariable()).getName());
printIndent().print(assignment.getVariable().toString());
} else {
continue;
}
initializedVars.add(var);
print(": ").print(assignment.getExpression()).print(",").println();
currentStatementPrinted = true;
statementPrinted = true;
} else if (s instanceof ExpressionStatementTree && ((ExpressionStatementTree) s)
.getExpression() instanceof MethodInvocationTree) {
MethodInvocationTree invocation = (MethodInvocationTree) ((ExpressionStatementTree) s)
.getExpression();
MethodInvocationElement invocationElement = (MethodInvocationElement) createExtendedElement(
invocation);
if (invocationElement.getMethodName()
.equals(JSweetConfig.INDEXED_SET_FUCTION_NAME)) {
if (invocation.getArguments().size() == 3) {
if ("this".equals(invocation.getArguments().get(0).toString())) {
printIndent().print(invocation.getArguments().get(1)).print(": ")
.print(invocation.getArguments().get(2)).print(",").println();
}
currentStatementPrinted = true;
statementPrinted = true;
} else {
printIndent().print(invocation.getArguments().get(0)).print(": ")
.print(invocation.getArguments().get(1)).print(",").println();
currentStatementPrinted = true;
statementPrinted = true;
}
}
}
if (!currentStatementPrinted) {
report(s, JSweetProblem.INVALID_INITIALIZER_STATEMENT);
}
}
for (Element s : classTypeElement.getEnclosedElements()) {
if (s instanceof VariableElement) {
if (!initializedVars.contains(s)) {
if (!context.hasAnnotationType(s, JSweetConfig.ANNOTATION_OPTIONAL)) {
report(m, JSweetProblem.UNINITIALIZED_FIELD, s);
}
}
}
}
}
if (m instanceof MethodTree) {
MethodTree method = (MethodTree) m;
if (Util.getElement(method).getKind() != ElementKind.CONSTRUCTOR) {
printIndent().print(method.getName() + ": (");
for (VariableTree param : method.getParameters()) {
print(param.getName() + ", ");
}
if (!method.getParameters().isEmpty()) {
removeLastChars(2);
}
print(") => ");
print(method.getBody());
print(",").println();
statementPrinted = true;
}
}
}
if (statementPrinted) {
removeLastChars(2);
}
}
if (!statementPrinted && !initializationBlockFound) {
for (Element s : classTypeElement.getEnclosedElements()) {
if (s instanceof VariableElement) {
if (!context.hasAnnotationType(s, JSweetConfig.ANNOTATION_OPTIONAL)) {
report(newClass, JSweetProblem.UNINITIALIZED_FIELD, s);
}
}
}
}
println().endIndent().printIndent().print("}");
if (!interfaces.isEmpty()) {
print(", 'constructor', { configurable: true, value: { " + INTERFACES_FIELD_NAME + ": ");
print("[");
for (String i : interfaces) {
print(getStringLiteralQuote()).print(i).print(getStringLiteralQuote() + ",");
}
removeLastChar();
print("] }");
print(" })");
}
} else {
print("((target:").print(newClass.getIdentifier()).print(") => {").println().startIndent();
for (Tree m : newClass.getClassBody().getMembers()) {
if (m instanceof BlockTree) {
for (Tree s : ((BlockTree) m).getStatements()) {
boolean currentStatementPrinted = false;
if (s instanceof ExpressionStatementTree
&& ((ExpressionStatementTree) s).getExpression() instanceof AssignmentTree) {
AssignmentTree assignment = (AssignmentTree) ((ExpressionStatementTree) s)
.getExpression();
VariableElement var = null;
if (assignment.getVariable() instanceof MemberSelectTree) {
var = util().findFieldDeclaration(classTypeElement,
((MemberSelectTree) assignment.getVariable()).getIdentifier());
printIndent().print("target['").print(var.getSimpleName().toString()).print("']");
} else if (assignment.getVariable() instanceof IdentifierTree) {
printIndent().print("target['").print(assignment.getVariable().toString())
.print("']");
} else {
continue;
}
print(" = ").print(assignment.getExpression()).print(";").println();
currentStatementPrinted = true;
} else if (s instanceof ExpressionStatementTree
&& ((ExpressionStatementTree) s).getExpression() instanceof MethodInvocationTree) {
MethodInvocationTree invocation = (MethodInvocationTree) ((ExpressionStatementTree) s)
.getExpression();
MethodInvocationElement invocationElement = (MethodInvocationElement) createExtendedElement(
invocation);
if (invocationElement.getMethodName().equals(JSweetConfig.INDEXED_SET_FUCTION_NAME)) {
if (invocation.getArguments().size() == 3) {
if ("this".equals(invocation.getArguments().get(0).toString())) {
printIndent().print("target[").print(invocation.getArguments().get(1))
.print("]").print(" = ").print(invocation.getArguments().get(2))
.print(";").println();
}
currentStatementPrinted = true;
} else {
printIndent().print("target[").print(invocation.getArguments().get(0))
.print("]").print(" = ").print(invocation.getArguments().get(1))
.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.getIdentifier()).print("(").printArgList(null, newClass.getArguments())
.print("))");
}
} else {
if (context.hasAnnotationType(classTypeElement, JSweetConfig.ANNOTATION_OBJECT_TYPE)) {
print("{}");
} else {
getAdapter().substituteNewClass(createExtendedElement(newClass));
}
}
return returnNothing();
}
/**
* Prints a new-class expression tree (default behavior).
*/
public void printDefaultNewClass(NewClassTree newClass) {
TypeMirror classType = Util.getType(newClass.getIdentifier());
TypeElement classTypeElement = Util.getTypeElement(newClass.getIdentifier());
String mappedType = context.getTypeMappingTarget(classType.toString());
if (typeChecker.checkType(newClass, null, newClass.getIdentifier(), getCompilationUnit())) {
boolean applyVarargs = true;
Element constructorElement = Util.getElement(newClass);
if (!(constructorElement instanceof ExecutableElement)) {
// not in source path
print("null /*cannot resolve " + newClass.getIdentifier() + "*/");
return;
}
ExecutableElement constructorExecutableElement = (ExecutableElement) constructorElement;
if (newClass.getArguments().size() == 0 || !util().hasVarargs(constructorExecutableElement) //
|| Util.getType(util().last(newClass.getArguments())).getKind() != TypeKind.ARRAY
// we dont use apply if var args type differ
|| !types().erasure(((ArrayType) Util.getType(util().last(newClass.getArguments()))).getComponentType())
.equals(types().erasure(
((ArrayType) util().last(constructorExecutableElement.getParameters()).asType())
.getComponentType()))) {
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("new (__Function.prototype.bind.apply(");
if (mappedType != null) {
print(Java2TypeScriptTranslator.mapConstructorType(mappedType));
} else {
print(newClass.getIdentifier());
}
print(", [null");
for (int i = 0; i < newClass.getArguments().size() - 1; i++) {
print(", ").print(newClass.getArguments().get(i));
}
print("].concat(").print(util().last(newClass.getArguments())).print(")))");
} else {
if (newClass.getIdentifier() instanceof ParameterizedTypeTree) {
ParameterizedTypeTree typeApply = (ParameterizedTypeTree) newClass.getIdentifier();
mappedType = context.getTypeMappingTarget(Util.getType(typeApply.getType()).toString());
print("new ");
if (mappedType != null) {
print(Java2TypeScriptTranslator.mapConstructorType(mappedType));
} else {
print(typeApply.getType());
}
if (!typeApply.getTypeArguments().isEmpty()) {
print("<").printTypeArgList(typeApply.getTypeArguments()).print(">");
} else {
// erase types since the diamond (<>)
// operator
// does not exists in TypeScript
printAnyTypeArguments(classTypeElement.getTypeParameters().size());
}
print("(").printConstructorArgList(newClass, false).print(")");
} else {
if (constructorExecutableElement.asType().getKind() == TypeKind.ERROR) {
print("null /*cannot resolve " + newClass.getIdentifier() + "*/");
return;
}
print("new ");
if (mappedType != null) {
print(Java2TypeScriptTranslator.mapConstructorType(mappedType));
} else {
print(newClass.getIdentifier());
}
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(NewClassTree newClass, boolean localClass) {
TypeElement classTypeElement = Util.getTypeElement(newClass.getIdentifier());
boolean printed = false;
if (localClass || (getScope().anonymousClasses.contains(newClass.getClassBody())
&& !isStaticAnonymousClass(newClass, getCompilationUnit()))) {
print("this");
if (!newClass.getArguments().isEmpty()) {
print(", ");
}
printed = true;
} else if ((classTypeElement.getEnclosingElement() instanceof TypeElement
&& !classTypeElement.getModifiers().contains(Modifier.STATIC)
&& !isStaticAnonymousClass(newClass, getCompilationUnit()))) {
print("this");
ClassTree parent = getParent(ClassTree.class);
TypeElement parentSymbol = parent == null ? null : Util.getElement(parent);
if (classTypeElement.getEnclosingElement() != parentSymbol) {
print("." + PARENT_CLASS_FIELD_NAME);
}
if (!newClass.getArguments().isEmpty()) {
print(", ");
}
printed = true;
}
ExecutableType methodType = (ExecutableType)Util.getElement(newClass).asType();
printArgList(methodType == null ? null : methodType.getParameterTypes(), newClass.getArguments());
int index = getScope().anonymousClasses.indexOf(newClass.getClassBody());
if (index >= 0 && !getScope().finalVariables.get(index).isEmpty()) {
if (printed || !newClass.getArguments().isEmpty()) {
print(", ");
}
for (VariableElement paramToBeTransmittedToAnonymous : getScope().finalVariables.get(index)) {
Element paramEnclosingTypeElement = paramToBeTransmittedToAnonymous.getEnclosingElement()
.getEnclosingElement();
Element anonymousClassEnclosingTypeElement = Util.getElement(getParent(ClassTree.class));
if (anonymousClassEnclosingTypeElement != paramEnclosingTypeElement) {
print("this.");
}
print(paramToBeTransmittedToAnonymous.getSimpleName().toString());
print(", ");
}
removeLastChars(2);
}
return this;
}
@Override
public Void visitLiteral(LiteralTree literal, Trees trees) {
String s = literal.toString();
switch (Util.getType(literal).getKind()) {
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:
}
if (s.startsWith("\"") && context.options.isUseSingleQuotesForStringLiterals()) {
s = "'" + s.substring(1, s.length() - 1).replace("'", "\'") + "'";
}
print(s);
return returnNothing();
}
@Override
public Void visitArrayAccess(ArrayAccessTree arrayAccess, Trees trees) {
if (!getAdapter().substituteArrayAccess(createExtendedElement(arrayAccess))) {
print(arrayAccess.getExpression()).print("[")
.substituteAndPrintAssignedExpression(util().getType(int.class), arrayAccess.getIndex()).print("]");
}
return returnNothing();
}
private int getIndexVariableCount() {
return getScope().foreachLoopContext == null ? 0 : getScope().foreachLoopContext.size();
}
private Set getGeneratedVariableNames() {
Set names = new HashSet<>();
if (getScope().foreachLoopContext != null) {
getScope().foreachLoopContext.stream().forEach(s -> {
names.add(s.variableName);
if (s.arrayName != null) {
names.add(s.arrayName);
}
});
}
return names;
}
private int getArrayVariableCount() {
return getScope().foreachLoopContext == null ? 0
: (int) getScope().foreachLoopContext.stream().filter(s -> s.arrayName != null).count();
}
private String getFreeVariableName(String variablePrefix, int index) {
String name = variablePrefix + (index == 0 ? "" : "" + index);
Set generatedVariableNames = getGeneratedVariableNames();
while (generatedVariableNames.contains(name)) {
name = variablePrefix + (++index);
}
int position = stack.size() - 2;
while (position >= 0 && !(stack.get(position) instanceof MethodTree)) {
if (stack.get(position) instanceof BlockTree) {
BlockTree block = (BlockTree) stack.get(position);
// analyze all the previous declarations in the block
for (Tree t : block.getStatements()) {
if (t == stack.get(position + 1)) {
// do not analyze post declarations
break;
}
// name clash: we try again with another index
if (t instanceof VariableTree && name.equals(((VariableTree) t).getName().toString())) {
return getFreeVariableName(variablePrefix, index + 1);
}
}
}
position--;
}
if (position >= 0 && stack.get(position) instanceof MethodTree) {
for (VariableTree param : ((MethodTree) stack.get(position)).getParameters()) {
if (name.equals(param.getName().toString())) {
return getFreeVariableName(variablePrefix, index + 1);
}
}
}
if (getCurrent() instanceof EnhancedForLoopTree) {
var loop = (EnhancedForLoopTree) getCurrent();
if (name.equals(loop.getVariable().getName().toString())) {
return getFreeVariableName(variablePrefix, index + 1);
}
StatementTree body = loop.getStatement();
if (body instanceof BlockTree) {
var block = (BlockTree) body;
// analyze all the previous declarations in the loop body
for (Tree t : block.getStatements()) {
// name clash: we try again with another index
if (t instanceof VariableTree && name.equals(((VariableTree) t).getName().toString())) {
return getFreeVariableName(variablePrefix, index + 1);
}
}
}
}
return name;
}
/**
* Prints a foreach loop tree.
*/
@Override
public Void visitEnhancedForLoop(EnhancedForLoopTree foreachLoop, Trees trees) {
String indexVarName = getFreeVariableName(
"index".equals(foreachLoop.getVariable().getName().toString()) ? "loopIndex" : "index", getIndexVariableCount());
boolean[] hasLength = { false };
TypeElement targetType = Util.getTypeElement(foreachLoop.getExpression());
TypeMirror collectionType = Util.getType(foreachLoop.getExpression());
if (collectionType.getKind() == TypeKind.ARRAY) {
hasLength[0] = true;
} else {
util().scanMemberDeclarationsInType(targetType, getAdapter().getErasedTypes(), element -> {
if (element instanceof VariableElement) {
if ("length".equals(element.getSimpleName().toString())
&& util().isNumber(((VariableElement) element).asType())) {
hasLength[0] = true;
return false;
}
}
return true;
});
}
if (!getAdapter().substituteForEachLoop(createExtendedElement(foreachLoop), hasLength[0], indexVarName)) {
if (getScope().foreachLoopContext == null) {
getScope().foreachLoopContext = new Stack<>();
}
ForeachLoopScope foreachLoopScope = new ForeachLoopScope(foreachLoop);
getScope().foreachLoopContext.push(foreachLoopScope);
boolean noVariable = foreachLoop.getExpression() instanceof IdentifierTree
|| foreachLoop.getExpression() instanceof MemberSelectTree;
foreachLoopScope.variableName = indexVarName;
if (noVariable) {
print("for(" + VAR_DECL_KEYWORD + " " + indexVarName + " = 0; " + indexVarName + " < ")
.print(foreachLoop.getExpression()).print("." + "length" + "; " + indexVarName + "++) {")
.println().startIndent().printIndent();
print(VAR_DECL_KEYWORD + " " + avoidJSKeyword(foreachLoop.getVariable().getName().toString()) + " = ")
.print(foreachLoop.getExpression()).print("[" + indexVarName + "];").println();
} else {
String arrayVarName = getFreeVariableName("array", getArrayVariableCount());
foreachLoopScope.arrayName = arrayVarName;
print("{").println().startIndent().printIndent();
print(VAR_DECL_KEYWORD + " " + arrayVarName + " = ").print(foreachLoop.getExpression()).print(";")
.println().printIndent();
print("for(" + VAR_DECL_KEYWORD + " " + indexVarName + " = 0; " + indexVarName + " < " + arrayVarName
+ ".length; " + indexVarName + "++) {").println().startIndent().printIndent();
print(VAR_DECL_KEYWORD + " " + avoidJSKeyword(foreachLoop.getVariable().getName().toString()) + " = "
+ arrayVarName + "[" + indexVarName + "];").println();
}
visitBeforeForEachBody(foreachLoop);
printIndent().print(foreachLoop.getStatement());
endIndent().println().printIndent().print("}");
if (!noVariable) {
endIndent().println().printIndent().print("}");
}
getScope().foreachLoopContext.pop();
}
return returnNothing();
}
protected void visitBeforeForEachBody(EnhancedForLoopTree foreachLoop) {
}
/**
* Prints a type identifier tree.
*/
@Override
public Void visitPrimitiveType(PrimitiveTypeTree type, Trees trees) {
switch (Util.getType(type).getKind()) {
case BYTE:
case DOUBLE:
case FLOAT:
case INT:
case LONG:
case SHORT:
print("number");
break;
default:
print(type.toString());
}
return returnNothing();
}
private boolean singlePrecisionFloats() {
return !context.options.isDisableSinglePrecisionFloats()
&& context.options.getEcmaTargetVersion().higherThan(EcmaScriptComplianceLevel.ES3);
}
@Override
public Void visitBinary(BinaryTree binary, Trees trees) {
if (!getAdapter().substituteBinaryOperator(createExtendedElement(binary))) {
String op = util().toOperator(binary.getKind());
boolean forceParens = false;
boolean booleanOp = false;
if (types().isSameType(util().getType(boolean.class),
util().unboxedTypeOrType(Util.getType(binary.getLeftOperand())))) {
booleanOp = true;
if ("^".equals(op)) {
forceParens = true;
}
if ("|".equals(op) || "&".equals(op)) {
print("((lhs, rhs) => lhs " + op + op + " rhs)(").print(binary.getLeftOperand()).print(", ")
.print(binary.getRightOperand()).print(")");
return returnNothing();
}
}
TypeMirror binaryType = Util.getType(binary);
TypeMirror leftType = Util.getType(binary.getLeftOperand());
TypeMirror rightType = Util.getType(binary.getRightOperand());
TypeMirror stringType = util().getType(String.class);
boolean closeParen = false;
boolean truncate = false;
if (util().isIntegral(binaryType) && binary.getKind() == Kind.DIVIDE) {
if (binaryType.getKind() == TypeKind.LONG) {
print("(n => n<0?Math.ceil(n):Math.floor(n))(");
closeParen = true;
} else {
print("(");
truncate = true;
}
}
if (singlePrecisionFloats() && binaryType.getKind() == TypeKind.FLOAT) {
print("(Math).fround(");
closeParen = true;
}
boolean charWrapping = util().isArithmeticOrLogicalOperator(binary.getKind())
|| util().isComparisonOperator(binary.getKind());
boolean actualCharWrapping = false;
if (charWrapping
&& types().isSameType(util().getType(char.class),
util().unboxedTypeOrType(Util.getType(binary.getLeftOperand())))
&& !types().isSameType(rightType, stringType)) {
actualCharWrapping = true;
if (binary.getLeftOperand() instanceof LiteralTree) {
printBinaryLeftOperand(binary);
print(".charCodeAt(0)");
} else {
print("(c => c.charCodeAt==null?c:c.charCodeAt(0))(").print(binary.getLeftOperand())
.print(")");
}
} else {
if (forceParens) {
print("(");
}
printBinaryLeftOperand(binary);
if (forceParens) {
print(")");
}
}
if (booleanOp) {
if ("|".equals(op)) {
op = "||";
} else if ("&".equals(op)) {
op = "&&";
} else if ("^".equals(op)) {
op = "!==";
}
}
if ("==".equals(op) || "!=".equals(op)) {
if (charWrapping && types().isSameType(util().getType(char.class), util().unboxedTypeOrType(rightType))
&& !types().isSameType(leftType, stringType)) {
actualCharWrapping = true;
}
}
if (!actualCharWrapping && ("==".equals(op) || "!=".equals(op))) {
switch (getComparisonMode()) {
case FORCE_STRICT:
op += "=";
break;
case STRICT:
if (!(util().isNullLiteral(binary.getLeftOperand())
|| util().isNullLiteral(binary.getRightOperand()))) {
op += "=";
}
break;
default:
break;
}
}
space().print(op).space();
if (charWrapping && types().isSameType(util().getType(char.class), util().unboxedTypeOrType(rightType))
&& !types().isSameType(leftType, stringType)) {
if (binary.getRightOperand() instanceof LiteralTree) {
printBinaryRightOperand(binary);
print(".charCodeAt(0)");
} else {
print("(c => c.charCodeAt==null?c:c.charCodeAt(0))(");
printBinaryRightOperand(binary);
print(")");
}
} else {
if (forceParens) {
print("(");
}
printBinaryRightOperand(binary);
if (forceParens) {
print(")");
}
}
if (closeParen) {
print(")");
}
if (truncate) {
print("|0)");
}
}
return returnNothing();
}
protected void printBinaryRightOperand(BinaryTree binary) {
addInlinedExpression(binary.getRightOperand());
print(binary.getRightOperand());
}
protected void printBinaryLeftOperand(BinaryTree binary) {
addInlinedExpression(binary.getLeftOperand());
print(binary.getLeftOperand());
}
/**
* Prints an if
tree.
*/
@Override
public Void visitIf(IfTree ifStatement, Trees trees) {
print("if ");
if (ifStatement.getCondition() instanceof ParenthesizedTree) {
print(ifStatement.getCondition());
} else {
print("(");
print(ifStatement.getCondition());
print(") ");
}
print(ifStatement.getThenStatement());
if (!(ifStatement.getThenStatement() instanceof BlockTree)) {
if (!isStatementWithNoSemiColon(ifStatement.getThenStatement())) {
print(";");
}
}
if (ifStatement.getElseStatement() != null) {
print(" else ");
print(ifStatement.getElseStatement());
if (!(ifStatement.getElseStatement() instanceof BlockTree)) {
if (!isStatementWithNoSemiColon(ifStatement.getElseStatement())) {
print(";");
}
}
}
return returnNothing();
}
/**
* Prints a return
tree.
*/
@Override
public Void visitReturn(ReturnTree returnStatement, Trees trees) {
print("return");
if (returnStatement.getExpression() != null) {
Tree parentFunction = getFirstParent(MethodTree.class, LambdaExpressionTree.class);
if (Util.getType(returnStatement.getExpression()) == null) {
report(returnStatement, JSweetProblem.CANNOT_ACCESS_THIS,
parentFunction == null ? returnStatement.toString() : parentFunction.toString());
return returnNothing();
}
print(" ");
TypeMirror returnType = null;
if (parentFunction != null) {
if (parentFunction instanceof MethodTree) {
returnType = Util.getType(((MethodTree) parentFunction).getReturnType());
} 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 = ((LambdaExpressionTree) parentFunction).type;
}
}
if (!substituteAssignedExpression(returnType, returnStatement.getExpression())) {
print(returnStatement.getExpression());
}
}
return returnNothing();
}
private boolean staticInitializedAssignment = false;
private VariableElement getStaticInitializedField(Tree expr) {
if (expr instanceof ArrayAccessTree) {
return null;
}
Element element = Util.getElement(expr);
if (element instanceof VariableElement && context.lazyInitializedStatics.contains(element)) {
return (VariableElement) element;
}
return null;
}
/**
* Prints an assignment operator tree (+=, -=, *=, ...
).
*/
@Override
public Void visitCompoundAssignment(CompoundAssignmentTree assignOpTree, Trees trees) {
if (!getAdapter().substituteAssignmentWithOperator(createExtendedElement(assignOpTree))) {
boolean expand = staticInitializedAssignment = (getStaticInitializedField(
assignOpTree.getVariable()) != null);
TypeMirror variableType = Util.getType(assignOpTree.getVariable());
TypeMirror expressionType = Util.getType(assignOpTree.getExpression());
boolean expandChar = types().isSameType(util().getType(char.class), util().unboxedTypeOrType(variableType));
print(assignOpTree.getVariable());
staticInitializedAssignment = false;
String assignmentOperator = util().toOperator(assignOpTree.getKind());
String operator = assignmentOperator.replace("=", "");
if (types().isSameType(util().getType(boolean.class), util().unboxedTypeOrType(variableType))) {
if ("|".equals(operator)) {
print(" = ").print(assignOpTree.getExpression()).print(" || ").print(assignOpTree.getVariable());
return returnNothing();
} else if ("&".equals(operator)) {
print(" = ").print(assignOpTree.getExpression()).print(" && ").print(assignOpTree.getVariable());
return returnNothing();
}
}
boolean castToIntegral = "/".equals(operator) //
&& util().isIntegral(variableType) //
&& util().isIntegral(expressionType);
if (expandChar) {
print(" = String.fromCharCode(")
.substituteAndPrintAssignedExpression(util().getType(int.class), assignOpTree.getVariable())
.print(" " + operator + " ")
.substituteAndPrintAssignedExpression(util().getType(int.class), assignOpTree.getExpression())
.print(")");
return returnNothing();
}
if (expand || castToIntegral) {
print(" = ");
if (castToIntegral) {
print("(n => n<0?Math.ceil(n):Math.floor(n))(");
}
print(assignOpTree.getVariable());
print(" " + operator + " ");
if (types().isSameType(util().getType(char.class), util().unboxedTypeOrType(expressionType))) {
substituteAndPrintAssignedExpression(util().getType(int.class), assignOpTree.getExpression());
} else {
printAssignWithOperatorRightOperand(assignOpTree);
}
if (castToIntegral) {
print(")");
}
return returnNothing();
}
print(" " + operator + "= ");
if (types().isSameType(util().getType(char.class), util().unboxedTypeOrType(expressionType))) {
// TypeMirror lhsType = assignOp.getVariable().type;
boolean isLeftOperandString = (types().asElement(variableType) == types()
.asElement(util().getType(String.class)));
TypeMirror rightPromotedType = isLeftOperandString ? util().getType(char.class)
: util().getType(int.class);
substituteAndPrintAssignedExpression(rightPromotedType, assignOpTree.getExpression());
} else {
printAssignWithOperatorRightOperand(assignOpTree);
}
}
return returnNothing();
}
protected void printAssignWithOperatorRightOperand(CompoundAssignmentTree assignOp) {
print(assignOp.getExpression());
}
/**
* Prints a condition?trueExpr:falseExpr
tree.
*/
@Override
public Void visitConditionalExpression(ConditionalExpressionTree conditional, Trees trees) {
print(conditional.getCondition());
print(" ? ");
if (!substituteAssignedExpression(
rootConditionalAssignedTypes.isEmpty() ? null : rootConditionalAssignedTypes.peek(),
conditional.getTrueExpression())) {
print(conditional.getTrueExpression());
}
print(" : ");
if (!substituteAssignedExpression(
rootConditionalAssignedTypes.isEmpty() ? null : rootConditionalAssignedTypes.peek(),
conditional.getFalseExpression())) {
print(conditional.getFalseExpression());
}
if (!rootConditionalAssignedTypes.isEmpty()) {
rootConditionalAssignedTypes.pop();
}
return returnNothing();
}
/**
* Prints a for
loop tree.
*/
@Override
public Void visitForLoop(ForLoopTree forLoopTree, Trees trees) {
print("for(").printArgList(null, forLoopTree.getInitializer()).print("; ").print(forLoopTree.getCondition())
.print("; ").printArgList(null, forLoopTree.getUpdate()).print(") ");
print("{");
visitBeforeForBody(forLoopTree);
print(forLoopTree.getStatement()).print(";");
print("}");
return returnNothing();
}
protected void visitBeforeForBody(ForLoopTree forLoop) {
}
/**
* Prints a continue
tree.
*/
@Override
public Void visitContinue(ContinueTree continueStatement, Trees trees) {
print("continue");
if (continueStatement.getLabel() != null) {
print(" ").print(continueStatement.getLabel().toString());
}
return returnNothing();
}
/**
* Prints a break
tree.
*/
@Override
public Void visitBreak(BreakTree breakStatement, Trees trees) {
print("break");
if (breakStatement.getLabel() != null) {
print(" ").print(breakStatement.getLabel().toString());
}
return returnNothing();
}
/**
* Prints a labeled statement tree.
*/
@Override
public Void visitLabeledStatement(LabeledStatementTree labelledStatement, Trees trees) {
Tree parent = getParent(MethodTree.class);
if (parent == null) {
parent = getParent(BlockTree.class);
while (parent != null && getParent(BlockTree.class, parent) != null) {
parent = getParent(BlockTree.class, parent);
}
}
boolean[] used = { false };
new TreeScanner() {
@Override
public Void visitBreak(BreakTree b, Trees trees) {
if (b.getLabel() != null && labelledStatement.getLabel().equals(b.getLabel())) {
used[0] = true;
}
return returnNothing();
}
@Override
public Void visitContinue(ContinueTree c, Trees trees) {
if (c.getLabel() != null && labelledStatement.getLabel().equals(c.getLabel())) {
used[0] = true;
}
return returnNothing();
}
}.scan(parent, trees);
if (!used[0]) {
print("/*");
}
print(labelledStatement.getLabel().toString()).print(":");
if (!used[0]) {
print("*/");
}
print(" ");
print(labelledStatement.getStatement());
return returnNothing();
}
/**
* Prints an array type tree.
*/
@Override
public Void visitArrayType(ArrayTypeTree arrayType, Trees trees) {
print(arrayType.getType()).print("[]");
return returnNothing();
}
/**
* Prints a new array tree.
*/
@Override
public Void visitNewArray(NewArrayTree newArray, Trees trees) {
if (newArray.getType() != null) {
typeChecker.checkType(newArray, null, newArray.getType(), getCompilationUnit());
}
if (newArray.getDimensions() != null && !newArray.getDimensions().isEmpty()) {
TypeMirror newArrayElementType = Util.getType(newArray.getType());
if (newArray.getDimensions().size() == 1) {
if (newArray.getDimensions().get(0) instanceof LiteralTree
&& ((int) ((LiteralTree) newArray.getDimensions().get(0)).getValue()) <= 10) {
boolean hasElements = false;
print("[");
for (int i = 0; i < (int) ((LiteralTree) newArray.getDimensions().get(0)).getValue(); i++) {
print(util().getTypeInitialValue(newArrayElementType) + ", ");
hasElements = true;
}
if (hasElements) {
removeLastChars(2);
}
print("]");
} else {
if (!getAdapter().substituteNewArrayWithVariableLength(new NewArrayElementSupport(newArray))) {
print("(s => { let a=[]; while(s-->0) a.push(" + util().getTypeInitialValue(newArrayElementType)
+ "); return a; })(").print(newArray.getDimensions().get(0)).print(")");
}
}
} else {
print(" (function(dims) { " + VAR_DECL_KEYWORD
+ " allocate = function(dims) { if (dims.length === 0) { return "
+ util().getTypeInitialValue(newArrayElementType) + "; } 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.getDimensions());
print("])");
}
} else {
print("[");
if (newArray.getInitializers() != null && !newArray.getInitializers().isEmpty()) {
for (ExpressionTree e : newArray.getInitializers()) {
if (!rootArrayAssignedTypes.isEmpty()) {
if (!substituteAssignedExpression(rootArrayAssignedTypes.peek(), e)) {
print(e);
}
} else {
print(e);
}
print(", ");
}
removeLastChars(2);
if (!rootArrayAssignedTypes.isEmpty()) {
rootArrayAssignedTypes.pop();
}
}
print("]");
}
return returnNothing();
}
protected boolean inRollback = false;
/**
* Prints a unary operator tree.
*/
@Override
public Void visitUnary(UnaryTree unary, Trees trees) {
if (!getAdapter().substituteUnaryOperator(createExtendedElement(unary))) {
addInlinedExpression(unary.getExpression());
String operatorAsString = util().toOperator(unary.getKind());
if (!inRollback) {
StatementTree statement = null;
VariableElement[] staticInitializedField = { null };
switch (operatorAsString) {
case "--":
case "++":
staticInitializedAssignment = (staticInitializedField[0] = getStaticInitializedField(
unary.getExpression())) != null;
if (staticInitializedAssignment) {
statement = getParent(StatementTree.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, trees);
});
}
} else {
inRollback = false;
}
switch (unary.getKind()) {
case POSTFIX_DECREMENT:
case POSTFIX_INCREMENT:
print(unary.getExpression());
print(operatorAsString);
break;
default:
print(operatorAsString);
print(unary.getExpression());
break;
}
}
return returnNothing();
}
/**
* Prints a switch
tree.
*/
@Override
public Void visitSwitch(SwitchTree switchStatement, Trees trees) {
print("switch(");
if (!getAdapter().substituteSwitchStatementSelector(createExtendedElement(switchStatement.getExpression()))) {
print(switchStatement.getExpression());
if (types().isSameType(util().getType(char.class),
util().unboxedTypeOrType(Util.getType(switchStatement.getExpression())))) {
print(".charCodeAt(0)");
}
}
print(") {").println();
for (CaseTree caseStatement : switchStatement.getCases()) {
printIndent();
print(caseStatement);
}
printIndent().print("}");
return returnNothing();
}
protected void printCaseStatementPattern(ExpressionTree pattern) {
}
/**
* Prints a case
tree.
*/
@Override
public Void visitCase(CaseTree caseTree, Trees trees) {
if (caseTree.getExpression() != null) {
TypeMirror expressionType = Util.getType(caseTree.getExpression());
print("case ");
if (!getAdapter().substituteCaseStatementPattern(createExtendedElement(caseTree),
createExtendedElement(caseTree.getExpression()))) {
if (util().isPrimitiveOrVoid(expressionType)
|| types().isSameType(util().getType(String.class), expressionType)) {
if (caseTree.getExpression() instanceof IdentifierTree) {
VariableElement varElement = Util.getElement((IdentifierTree) caseTree.getExpression());
Object value = varElement.getConstantValue();
if (types().isSameType(util().getType(String.class), expressionType)) {
print(getStringLiteralQuote() + value + getStringLiteralQuote() + " /* "
+ caseTree.getExpression() + " */");
} else {
if (value != null && value.getClass() == Character.class) {
value = (int) ((Character) value);
}
print("" + value + " /* " + caseTree.getExpression() + " */");
}
} else {
if (types().isSameType(util().getType(char.class), expressionType)) {
ExpressionTree caseExpression = caseTree.getExpression();
if (caseExpression instanceof TypeCastTree) {
caseExpression = ((TypeCastTree) caseExpression).getExpression();
}
if (caseExpression instanceof LiteralTree) {
Object value = ((LiteralTree) caseExpression).getValue();
if (value instanceof Character) {
int charCodePoint = Character.codePointAt(value.toString(), 0);
value = charCodePoint;
}
print(value + " /* " + caseTree.getExpression() + " */");
} else {
print(caseExpression);
}
} else {
print(caseTree.getExpression());
}
}
} else {
Element expressionTypeElement = types().asElement(expressionType);
print(getRootRelativeName(expressionTypeElement) + "." + caseTree.getExpression());
ensureModuleIsUsed(expressionTypeElement);
}
}
} else {
print("default");
}
print(":");
println().startIndent();
for (StatementTree statement : caseTree.getStatements()) {
printIndent();
print(statement);
if (!isStatementWithNoSemiColon(statement)) {
print(";");
}
println();
}
endIndent();
return returnNothing();
}
/**
* Prints a type cast tree.
*/
@Override
public Void visitTypeCast(TypeCastTree cast, Trees trees) {
TypeMirror fromType = Util.getType(cast.getExpression());
Element fromTypeElement = types().asElement(fromType);
TypeMirror toType = Util.getType(cast.getType());
Element toTypeElement = types().asElement(toType);
if (substituteAssignedExpression(toType, cast.getExpression())) {
return returnNothing();
}
if (getAdapter().substituteTypeCast(createExtendedElement(cast))) {
return returnNothing();
}
if (util().isIntegral(toType)) {
if (toType.getKind() == TypeKind.LONG) {
print("(n => n<0?Math.ceil(n):Math.floor(n))(");
} else {
print("(");
}
}
if (!context.hasAnnotationType(toTypeElement, ANNOTATION_ERASED, ANNOTATION_OBJECT_TYPE,
ANNOTATION_FUNCTIONAL_INTERFACE)) {
// Java is more permissive than TypeScript when casting type
// variables
if (fromType.getKind() == TypeKind.TYPEVAR) {
print("");
} else {
print("<");
substituteAndPrintType(cast.getType()).print(">");
// Java always allows casting when an interface or a type param
// is involved
// (that's weak!!)
if (util().isInterface(fromTypeElement) || util().isInterface(toTypeElement)
|| toType.getKind() == TypeKind.TYPEVAR) {
print("");
}
}
}
print(cast.getExpression());
if (util().isIntegral(toType)) {
if (toType.getKind() == TypeKind.LONG) {
print(")");
} else {
print("|0)");
}
}
return returnNothing();
}
/**
* Prints a do - while
loop tree.
*/
@Override
public Void visitDoWhileLoop(DoWhileLoopTree doWhileLoop, Trees trees) {
print("do ");
print("{");
visitBeforeDoWhileBody(doWhileLoop);
if (doWhileLoop.getStatement() instanceof BlockTree) {
print(doWhileLoop.getStatement());
} else {
print(doWhileLoop.getStatement()).print(";");
}
print("}");
print(" while(").print(doWhileLoop.getCondition()).print(")");
return returnNothing();
}
protected void visitBeforeDoWhileBody(DoWhileLoopTree doWhileLoop) {
}
/**
* Prints a while
loop tree.
*/
@Override
public Void visitWhileLoop(WhileLoopTree whileLoop, Trees trees) {
print("while(").print(whileLoop.getCondition()).print(") ");
print("{");
visitBeforeWhileBody(whileLoop);
print(whileLoop.getStatement());
print("}");
return returnNothing();
}
protected void visitBeforeWhileBody(WhileLoopTree whileLoop) {
}
/**
* Prints a variable assignment tree.
*/
@Override
public Void visitAssignment(AssignmentTree assign, Trees trees) {
if (!getAdapter().substituteAssignment(createExtendedElement(assign))) {
staticInitializedAssignment = getStaticInitializedField(assign.getVariable()) != null;
print(assign.getVariable()).print(isAnnotationScope ? ": " : " = ");
if (!substituteAssignedExpression(Util.getType(assign.getVariable()), assign.getExpression())) {
print(assign.getExpression());
}
staticInitializedAssignment = false;
}
return returnNothing();
}
/**
* Prints a try
tree.
*/
@Override
public Void visitTry(TryTree tryStatement, Trees trees) {
boolean resourced = tryStatement.getResources() != null && !tryStatement.getResources().isEmpty();
if (resourced) {
for (Tree resource : tryStatement.getResources()) {
print(resource).println(";").printIndent();
}
} else if (tryStatement.getCatches().isEmpty() && tryStatement.getFinallyBlock() == null) {
report(tryStatement, JSweetProblem.TRY_WITHOUT_CATCH_OR_FINALLY);
}
print("try ").print(tryStatement.getBlock());
if (tryStatement.getCatches().size() > 1) {
print(" catch(__e) {").startIndent();
for (CatchTree catcher : tryStatement.getCatches()) {
println().printIndent().print("if");
printInstanceOf("__e", null, Util.getType(catcher.getParameter()));
print(" {").startIndent().println().printIndent();
// if (!context.options.isUseJavaApis() &&
// catcher.param.type.toString().startsWith("java.")) {
// print(catcher.param).print(" = ").print("__e;").println();
// } else {
print(catcher.getParameter()).print(" = <");
substituteAndPrintType(catcher.getParameter().getType());
print(">__e;").println();
// }
printBlockStatements(catcher.getBlock().getStatements());
endIndent().println().printIndent().print("}");
}
endIndent().println().printIndent().print("}");
} else if (tryStatement.getCatches().size() == 1) {
print(tryStatement.getCatches().get(0));
}
if (resourced || tryStatement.getFinallyBlock() != null) {
print(" finally {");
if (resourced) {
// resources are closed in reverse order, before finally block is executed
startIndent();
List reversedResources = new ArrayList<>(tryStatement.getResources());
Collections.reverse(reversedResources);
for (Tree resource : reversedResources) {
if (resource instanceof VariableTree) {
println().printIndent().print(((VariableTree) resource).getName() + ".close();");
}
}
endIndent();
}
if (tryStatement.getFinallyBlock() != null) {
startIndent();// .printIndent();
for (StatementTree statement : tryStatement.getFinallyBlock().getStatements()) {
println().printIndent().print(statement).print(";");
}
endIndent();
}
println().printIndent().print("}"); // closes finally block
}
return returnNothing();
}
/**
* Prints a catch
tree.
*/
@Override
public Void visitCatch(CatchTree catcher, Trees trees) {
print(" catch(").print(catcher.getParameter().getName().toString()).print(") ");
print(catcher.getBlock());
return returnNothing();
}
/**
* Prints a lambda expression tree.
*/
@Override
public Void visitLambdaExpression(LambdaExpressionTree lamba, Trees trees) {
boolean regularFunction = false;
if (getParent() instanceof MethodInvocationTree
&& ((MethodInvocationTree) getParent()).getMethodSelect().toString().endsWith("function")
&& getParentOfParent() instanceof MethodInvocationTree
&& ((MethodInvocationTree) getParentOfParent()).getMethodSelect().toString().endsWith("$noarrow")) {
MethodInvocationElement invocation = (MethodInvocationElement) createExtendedElement(getParent());
if (JSweetConfig.UTIL_CLASSNAME.equals(invocation.getMethod().getEnclosingElement().toString())) {
regularFunction = true;
}
}
Map varAccesses = new HashMap<>();
util().fillAllVariableAccesses(varAccesses, lamba, getCompilationUnit());
Collection finalVars = new ArrayList<>(varAccesses.values());
if (!varAccesses.isEmpty()) {
Map varDefs = new HashMap<>();
int parentIndex = getStack().size() - 2;
int i = parentIndex;
StatementTree 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 StatementTree) {
statement = (StatementTree) getStack().get(i);
}
i--;
}
if (i >= 0 && getStack().get(i).getKind() != Kind.LAMBDA_EXPRESSION && statement != null) {
util().fillAllVariablesInScope(varDefs, getStack(), lamba, getStack().get(i), getCompilationUnit());
}
finalVars.retainAll(varDefs.values());
}
if (!finalVars.isEmpty()) {
print("((");
for (VariableElement var : finalVars) {
print(var.getSimpleName().toString()).print(",");
}
removeLastChar();
print(") => {").println().startIndent().printIndent().print("return ");
}
getScope().skipTypeAnnotations = true;
if (regularFunction) {
print("function(").printArgList(null, lamba.getParameters()).print(") ");
} else {
print("(").printArgList(null, lamba.getParameters()).print(") => ");
}
getScope().skipTypeAnnotations = false;
print(lamba.getBody());
if (!finalVars.isEmpty()) {
endIndent().println().printIndent().print("})(");
for (VariableElement var : finalVars) {
print(var.getSimpleName().toString()).print(",");
}
removeLastChar();
print(")");
}
return returnNothing();
}
@Override
public Void visitMemberReference(MemberReferenceTree memberReference, Trees trees) {
Element element = Util.getElement(memberReference);
String memberReferenceSimpleName;
if (memberReference.getQualifierExpression() instanceof ArrayTypeTree) {
memberReferenceSimpleName = "Array";
} else {
memberReferenceSimpleName = Util.getTypeElement(memberReference.getQualifierExpression()).getSimpleName()
.toString();
}
boolean printAsInstanceMethod = !element.getModifiers().contains(Modifier.STATIC)
&& !CONSTRUCTOR_METHOD_NAME.equals(memberReference.getName().toString())
&& !JSweetConfig.GLOBALS_CLASS_NAME.equals(memberReferenceSimpleName);
boolean exprIsInstance = memberReference.getQualifierExpression().toString().equals("this")
|| memberReference.getQualifierExpression().toString().equals("super")
|| (memberReference.getQualifierExpression() instanceof IdentifierTree && Util.getElement(
(IdentifierTree) memberReference.getQualifierExpression()) instanceof VariableElement)
|| (memberReference.getQualifierExpression() instanceof MemberSelectTree && Util.getElement(
(MemberSelectTree) memberReference.getQualifierExpression()) instanceof VariableElement);
if (element instanceof ExecutableElement) {
ExecutableElement method = (ExecutableElement) element;
if (getParent() instanceof TypeCastTree) {
print("(");
}
print("(");
int argumentsPrinted = 0;
if (printAsInstanceMethod && !exprIsInstance) {
print("instance$").print(memberReferenceSimpleName);
print(",");
argumentsPrinted++;
}
if (method.getParameters() != null) {
for (VariableElement var : method.getParameters()) {
print(var.getSimpleName().toString());
print(",");
argumentsPrinted++;
}
}
if (argumentsPrinted > 0) {
removeLastChar();
}
print(")");
print(" => { return ");
}
if (JSweetConfig.GLOBALS_CLASS_NAME.equals(memberReferenceSimpleName)) {
print(memberReference.getName().toString());
} else {
if (CONSTRUCTOR_METHOD_NAME.equals(memberReference.getName().toString())) {
if (Util.getType(memberReference.getQualifierExpression()).getKind() == TypeKind.ARRAY) {
print("new Array<");
substituteAndPrintType(((ArrayTypeTree) memberReference.getQualifierExpression()).getType());
print(">");
} else {
print("new ").print(memberReference.getQualifierExpression());
}
} else {
if (printAsInstanceMethod && !exprIsInstance) {
print("instance$").print(memberReferenceSimpleName);
} else {
print(memberReference.getQualifierExpression());
}
print(".").print(memberReference.getName().toString());
}
}
if (element instanceof ExecutableElement) {
ExecutableElement method = (ExecutableElement) element;
print("(");
if (method.getParameters() != null) {
for (VariableElement var : method.getParameters()) {
print(var.getSimpleName().toString());
print(",");
}
if (!method.getParameters().isEmpty()) {
removeLastChar();
}
}
print(")");
print(" }");
if (getParent() instanceof TypeCastTree) {
print(")");
}
}
return returnNothing();
}
/**
* Prints a type parameter tree.
*/
@Override
public Void visitTypeParameter(TypeParameterTree typeParameter, Trees trees) {
print(typeParameter.getName().toString());
if (typeParameter.getBounds() != null && !typeParameter.getBounds().isEmpty()) {
print(" extends ");
for (Tree e : typeParameter.getBounds()) {
substituteAndPrintType(e).print(" & ");
}
removeLastChars(3);
}
return returnNothing();
}
/** Prints a synchronized
tree. */
@Override
public Void visitSynchronized(SynchronizedTree sync, Trees trees) {
report(sync, JSweetProblem.SYNCHRONIZATION);
if (sync.getBlock() != null) {
print(sync.getBlock());
}
return returnNothing();
}
/**
* Prints either a string, or the tree if the the string is null.
*
* @param exprStr a string to be printed as is if not null
* @param expr a tree to be printed if exprStr is null
*/
public void print(String exprStr, Tree expr) {
if (exprStr == null) {
print(expr);
} else {
print(exprStr);
}
}
private void printInstanceOf(String exprStr, Tree expr, TypeMirror type) {
printInstanceOf(exprStr, expr, type, false);
}
private void printInstanceOf(String exprStr, Tree expr, TypeMirror type, boolean checkFirstArrayElement) {
if (!(getParent() instanceof ParenthesizedTree)) {
print("(");
}
if (checkFirstArrayElement || !getAdapter().substituteInstanceof(exprStr, createExtendedElement(expr), type)) {
Element typeElement = types().asElement(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 (util().isPartOfAnEnum(typeElement)) {
print("typeof ");
print(exprStr, expr);
if (checkFirstArrayElement)
print("[0]");
boolean isStringEnum = context.hasAnnotationType(typeElement, ANNOTATION_STRING_ENUM);
if (isStringEnum) {
print(" === 'string'");
} else {
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(typeElement, JSweetConfig.ANNOTATION_FUNCTIONAL_INTERFACE)) {
print("typeof ");
print(exprStr, expr);
if (checkFirstArrayElement)
print("[0]");
print(" === 'function'");
int parameterCount = context.getFunctionalTypeParameterCount(type);
if (parameterCount != -1) {
print(" && (");
print(exprStr, expr);
if (checkFirstArrayElement)
print("[0]");
print(").length === " + context.getFunctionalTypeParameterCount(type));
}
} else {
print(exprStr, expr);
if (checkFirstArrayElement)
print("[0]");
if (context.isInterface(typeElement)) {
String interfaceQualifiedName = util().getQualifiedName(typeElement);
print(" != null && ");
print("(");
// comment
print(exprStr, expr);
if (checkFirstArrayElement)
print("[0]");
print(".constructor != null && ");
print(exprStr, expr);
if (checkFirstArrayElement)
print("[0]");
print(".constructor[" + getStringLiteralQuote() + INTERFACES_FIELD_NAME + getStringLiteralQuote()
+ "]").print(" != null && ");
print(exprStr, expr);
if (checkFirstArrayElement)
print("[0]");
print(".constructor[" + getStringLiteralQuote() + INTERFACES_FIELD_NAME + getStringLiteralQuote()
+ "].indexOf(" + getStringLiteralQuote()).print(interfaceQualifiedName)
.print(getStringLiteralQuote() + ") >= 0");
if (CharSequence.class.getName().equals(interfaceQualifiedName)) {
print(" || typeof ");
print(exprStr, expr);
if (checkFirstArrayElement)
print("[0]");
print(" === " + getStringLiteralQuote() + "string" + getStringLiteralQuote());
}
print(")");
} else if (typeElement instanceof TypeElement
&& Class.class.getName().equals(((TypeElement) typeElement).getQualifiedName().toString())) {
print(" != null && ");
print("(");
print(exprStr, expr);
if (checkFirstArrayElement)
print("[0]");
print("[" + getStringLiteralQuote() + CLASS_NAME_IN_CONSTRUCTOR + getStringLiteralQuote() + "]")
.print(" != null");
print(" || ((t) => { try { new t; return true; } catch { return false; } })(");
print(exprStr, expr);
if (checkFirstArrayElement)
print("[0]");
print(")");
print(")");
} else {
if (typeElement instanceof TypeParameterElement
|| Object.class.getName().equals(util().getQualifiedName(typeElement))) {
print(" != null");
} else {
String qualifiedName;
if (typeElement == null) {
if (type.getKind() == TypeKind.ARRAY) {
qualifiedName = "Array";
} else {
qualifiedName = getAdapter().getMappedType(type);
}
} else {
qualifiedName = getQualifiedTypeName(typeElement, false, false);
if (util().isPartOfAnEnum(typeElement)) {
qualifiedName += ENUM_WRAPPER_CLASS_SUFFIX;
}
}
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 ").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.getComponentType() instanceof ArrayType) {
print(exprStr, expr);
print("[0] instanceof Array");
} else {
printInstanceOf(exprStr, expr, t.getComponentType(), true);
}
print(")");
}
}
}
}
}
}
if (!(getParent() instanceof ParenthesizedTree)) {
print(")");
}
}
/**
* Prints an instanceof
tree.
*/
@Override
public Void visitInstanceOf(InstanceOfTree instanceOf, Trees trees) {
printInstanceOf(null, instanceOf.getExpression(), Util.getType(instanceOf.getType()));
return returnNothing();
}
@Override
public Void visitThrow(ThrowTree throwTree, Trees trees) {
print("throw ").print(throwTree.getExpression());
return returnNothing();
}
/**
* Prints an assert tree.
*/
@Override
public Void visitAssert(AssertTree assertion, Trees trees) {
if (!context.options.isIgnoreAssertions()) {
String assertCode = assertion.toString().replace("\"", "'");
print("if (!(").print(assertion.getCondition()).print(
")) { throw new Error(\"Assertion error line " + getCurrentLine() + ": " + assertCode + "\"); }");
}
return returnNothing();
}
@Override
public Void visitAnnotation(AnnotationTree annotation, Trees trees) {
if (!context.hasAnnotationType(Util.getTypeElement(annotation), JSweetConfig.ANNOTATION_DECORATOR)) {
return returnNothing();
}
if (getScope().isInterfaceScope()) {
return returnNothing();
}
print("@").print(annotation.getAnnotationType());
if (annotation.getArguments() != null && !annotation.getArguments().isEmpty()) {
print("(");
isAnnotationScope = true;
print(" { ");
for (ExpressionTree e : annotation.getArguments()) {
print(e);
print(", ");
}
removeLastChars(2);
print(" } ");
isAnnotationScope = false;
print(")");
} else {
boolean parens = true;
if (annotation.getAnnotationType() instanceof IdentifierTree) {
GlobalMethodInfos globalDecoratorFunction = context
.lookupGlobalMethod(Util.getElement(annotation.getAnnotationType()).toString());
if (globalDecoratorFunction != null) {
if (!globalDecoratorFunction.methodTree.getParameters().isEmpty()) {
parens = false;
}
}
}
if (parens) {
print("()");
}
}
println().printIndent();
return returnNothing();
}
Stack rootConditionalAssignedTypes = new Stack<>();
Stack rootArrayAssignedTypes = new Stack<>();
@Override
protected boolean substituteAssignedExpression(TypeMirror assignedType, ExpressionTree expression) {
if (assignedType == null) {
return false;
}
if (getAdapter().substituteAssignedExpression(assignedType, createExtendedElement(expression))) {
return true;
}
Element expressionTypeElement = Util.getTypeElement(expression);
TypeMirror expressionType = Util.getType(expression);
Element assignedTypeElement = types().asElement(assignedType);
if (util().isInterface(assignedTypeElement) && expressionTypeElement != null
&& util().isPartOfAnEnum(expressionTypeElement)) {
String relTarget = getRootRelativeName(expressionTypeElement);
TypeElement targetTypeElement = (TypeElement) expressionTypeElement;
useModule(getAdapter().getModuleImportDescriptor(getAdapter().getCompilationUnit(),
context.getActualName(targetTypeElement), (TypeElement) targetTypeElement));
print("((wrappers, value) => wrappers===undefined?value:wrappers[value])(")
.print(relTarget).print("[" + getStringLiteralQuote()
+ Java2TypeScriptTranslator.ENUM_WRAPPER_CLASS_WRAPPERS + getStringLiteralQuote() + "], ")
.print(expression).print(")");
return true;
}
if (expression instanceof ConditionalExpressionTree) {
rootConditionalAssignedTypes.push(assignedType);
return false;
}
if (expression instanceof NewArrayTree && assignedType instanceof ArrayType) {
rootArrayAssignedTypes.push(((ArrayType) assignedType).getComponentType());
return false;
}
if (assignedType.getKind() == TypeKind.CHAR && expressionType.getKind() != TypeKind.CHAR) {
print("String.fromCharCode(").print(expression).print(")");
return true;
} else if (util().isNumber(assignedType) && expressionType.getKind() == TypeKind.CHAR) {
print("(").print(expression).print(").charCodeAt(0)");
return true;
} else if (singlePrecisionFloats() && assignedType.getKind() == TypeKind.FLOAT
&& expressionType.getKind() == TypeKind.DOUBLE) {
print("(Math).fround(").print(expression).print(")");
return true;
} else {
if (expression instanceof LambdaExpressionTree) {
if (util().isInterface(assignedTypeElement)
&& !context.isFunctionalType((TypeElement) assignedTypeElement)) {
LambdaExpressionTree lambda = (LambdaExpressionTree) expression;
ExecutableElement method = (ExecutableElement) assignedTypeElement.getEnclosedElements().get(0);
print("{ " + method.getSimpleName() + ": ").print(lambda).print(" }");
return true;
}
} else if (expression instanceof NewClassTree) {
NewClassTree newClass = (NewClassTree) expression;
if (newClass.getClassBody() != null && context.isFunctionalType((TypeElement) assignedTypeElement)) {
List defs = newClass.getClassBody().getMembers();
boolean printed = false;
for (Tree def : defs) {
if (def instanceof MethodTree) {
if (printed) {
// should never happen... report error?
}
MethodTree method = (MethodTree) def;
if (Util.getElement(method).getKind() == ElementKind.CONSTRUCTOR) {
continue;
}
getStack().push(method);
print("(").printArgList(null, method.getParameters()).print(") => ")
.print(method.getBody());
getStack().pop();
printed = true;
}
}
if (printed) {
return true;
}
} else {
// object assignment to functional type
if ((newClass.getClassBody() == null
&& context.isFunctionalType((TypeElement) assignedTypeElement))) {
boolean lambdaPrinted = printFunctionalTypeAsLambda((TypeElement) assignedTypeElement, () -> {
print("new ").print(newClass.getIdentifier()).print("(");
printArgList(null, newClass.getArguments());
print(")");
});
if (lambdaPrinted) {
return true;
}
}
// raw generic type
TypeElement newClassElement = Util.getTypeElement(newClass);
if (!newClassElement.getTypeParameters().isEmpty() && newClass.getTypeArguments().isEmpty()) {
print("(").print(expression).print(")");
return true;
}
}
} else if (!(expression instanceof LambdaExpressionTree || expression instanceof MemberReferenceTree)
&& context.isFunctionalType(assignedTypeElement)) {
// disallow typing to force objects to be passed as function
// (may require runtime checks later on)
print("(");
printFunctionalTypeAsLambda((TypeElement) assignedTypeElement, () -> print(expression));
print(")");
return true;
} else if (expression instanceof MethodInvocationTree) {
// disable type checking when the method returns a type variable
// because it may to be correctly set in the invocation
ExpressionTree methodSelectTree = ((MethodInvocationTree) expression).getMethodSelect();
Element m = Util.getElement(methodSelectTree);
if (m instanceof ExecutableElement) {
ExecutableElement methodElement = (ExecutableElement) m;
if (methodElement.getReturnType().getKind() == TypeKind.TYPEVAR
&& types().asElement(methodElement.getReturnType()).getEnclosingElement() == m) {
print("(").print(expression).print(")");
return true;
}
}
}
return false;
}
}
private boolean printFunctionalTypeAsLambda(TypeElement assignedTypeElement, Runnable printInstance) {
if (context.isFunctionalType(assignedTypeElement)) {
ExecutableElement method;
for (Element s : assignedTypeElement.getEnclosedElements()) {
if (s instanceof ExecutableElement) {
// TODO also check that the method is compatible
// (here we just apply to the first found
// method)
method = (ExecutableElement) s;
String functionalMethodName = method.getSimpleName().toString();
print("((funcInst: any) => { if (typeof funcInst == 'function') { return funcInst } ");
print("return (");
for (VariableElement p : method.getParameters()) {
print(p.getSimpleName().toString()).print(", ");
}
if (!method.getParameters().isEmpty()) {
removeLastChars(2);
}
print(") => (funcInst['" + functionalMethodName + "'] ? funcInst['" + functionalMethodName
+ "'] : funcInst) ");
print(".call(funcInst, ");
for (VariableElement p : method.getParameters()) {
print(p.getSimpleName().toString()).print(", ");
}
removeLastChar(' ');
removeLastChar(',');
print(")");
print("})(");
printInstance.run();
print(")");
return true;
}
}
}
return false;
}
/**
* Gets the qualified name for a given type symbol.
*/
@Override
public String getQualifiedTypeName(Element type, boolean globals, boolean ignoreLangTypes) {
String qualifiedName = super.getQualifiedTypeName(type, globals, ignoreLangTypes);
String typeName = util().getQualifiedName(type);
if (!ignoreLangTypes && context.getLangTypeMappings().containsKey(typeName)) {
qualifiedName = context.getLangTypeMappings().get(typeName);
} else if (context.isMappedType(typeName)) {
qualifiedName = context.getTypeMappingTarget(typeName);
if (qualifiedName.endsWith("<>")) {
qualifiedName = qualifiedName.substring(0, qualifiedName.length() - 2);
}
} else {
if (context.useModules) {
String[] namePath = qualifiedName.split("\\.");
int i = namePath.length - 1;
Element s = type;
qualifiedName = "";
while (i >= 0 && !(s instanceof PackageElement)) {
qualifiedName = namePath[i--] + ("".equals(qualifiedName) ? "" : "." + qualifiedName);
s = s.getEnclosingElement();
}
}
if (globals) {
int dotIndex = qualifiedName.lastIndexOf(".");
if (dotIndex == -1) {
qualifiedName = "";
} else {
qualifiedName = qualifiedName.substring(0, dotIndex);
}
}
}
return qualifiedName;
}
private static final Void returnNothing() {
return null;
}
private boolean isStatementWithNoSemiColon(Tree tree) {
return (tree instanceof IfTree || tree instanceof ForLoopTree || tree instanceof EnhancedForLoopTree
|| tree instanceof SwitchTree || tree instanceof TryTree);
}
/**
* Returns true if this new class expression defines an anonymous class which is
* contained in a static method.
*/
private boolean isStaticAnonymousClass(NewClassTree newClass, CompilationUnitTree compilationUnit) {
if (!context.isAnonymousClass(newClass, compilationUnit)) {
return false;
}
BlockTree parentBlock = getParent(BlockTree.class);
if (parentBlock != null && parentBlock.isStatic()) {
return true;
}
ExecutableElement parentMethodElement = null;
Element newClassElement = Util.getElement(newClass);
do {
if (newClassElement.getModifiers().contains(Modifier.STATIC)) {
return false;
}
parentMethodElement = util().getParentElement(newClassElement, ExecutableElement.class);
if (parentMethodElement == null) {
return false;
}
if (parentMethodElement.getModifiers().contains(Modifier.STATIC)) {
return true;
}
} while ((newClassElement = util().getParentElement(parentMethodElement, TypeElement.class)) != null);
return false;
}
class UsedTypesScanner extends TreeScanner {
private HashSet names = new HashSet<>();
private void checkType(Element type) {
if (type instanceof TypeElement) {
String name = type.getSimpleName().toString();
if (!names.contains(name)) {
names.add(name);
ModuleImportDescriptor moduleImport = getModuleImportDescriptor(name, (TypeElement) type);
if (moduleImport != null) {
useModule(false, moduleImport.isDirect(), moduleImport.getTargetPackage(), null,
moduleImport.getImportedName(), moduleImport.getPathToImportedClass(),
moduleImport.getImportedClass());
}
}
} else if (type instanceof ExecutableElement) {
String name = type.getSimpleName().toString();
if (!names.contains(name)) {
names.add(name);
ModuleImportDescriptor moduleImport = getModuleImportDescriptor(name, (TypeElement) type.getEnclosingElement());
if (moduleImport != null) {
useModule(false, moduleImport.isDirect(), moduleImport.getTargetPackage(), null,
moduleImport.getImportedName(), moduleImport.getPathToImportedClass(),
moduleImport.getImportedClass());
}
}
}
}
@Override
public Void scan(Tree tree, Trees trees) {
if (tree instanceof ImportTree) {
return returnNothing();
}
if (tree != null) {
Element treeElement = Util.getElementNoErrors(tree);
if (!(treeElement instanceof TypeElement)) {
treeElement = Util.getTypeElement(tree);
}
if (treeElement instanceof TypeElement) {
if (!(tree instanceof ParameterizedTypeTree)
// hack to include only explicit type declarations in AST
&& (tree instanceof IdentifierTree
&& tree.toString().equals(treeElement.getSimpleName().toString()))) {
checkType(treeElement);
}
}
}
// Check the owner of invoked static methods since those may be statically imported
if (tree instanceof MethodInvocationTree) {
MethodInvocationTree inv = (MethodInvocationTree) tree;
if (inv.getMethodSelect() instanceof IdentifierTree && Util.getElement(((IdentifierTree)inv.getMethodSelect()))
instanceof ExecutableElement) {
ExecutableElement sym = Util.getElement(((IdentifierTree)inv.getMethodSelect()));
if (sym.getModifiers().contains(Modifier.STATIC)) {
if (GLOBALS_CLASS_NAME.equals(sym.getEnclosingElement().getSimpleName().toString())) {
checkType(sym);
} else {
checkType(sym.getEnclosingElement());
}
}
}
}
return super.scan(tree, trees);
}
}
}