/* * xtc - The eXTensible Compiler * Copyright (C) 2005-2007 IBM Corp. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ package xtc.lang; import java.io.CharArrayWriter; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.Reader; import java.io.StringReader; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.Formatter; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; import xtc.Constants; import xtc.parser.ParseException; import xtc.parser.Result; import xtc.tree.Attribute; import xtc.tree.GNode; import xtc.tree.LineMarker; import xtc.tree.Location; import xtc.tree.Node; import xtc.tree.Printer; import xtc.tree.Visitor; import xtc.type.AliasT; import xtc.type.AnnotatedT; import xtc.type.ArrayT; import xtc.type.BooleanT; import xtc.type.ClassOrInterfaceT; import xtc.type.ClassT; import xtc.type.DynamicReference; import xtc.type.FunctionOrMethodT; import xtc.type.IntegerT; import xtc.type.InterfaceT; import xtc.type.MethodT; import xtc.type.NullReference; import xtc.type.NumberT; import xtc.type.PackageT; import xtc.type.Type; import xtc.type.TypePrinter; import xtc.type.VariableT; import xtc.type.VoidT; import xtc.type.WrappedT; import xtc.util.Runtime; import xtc.util.SymbolTable; import xtc.util.Utilities; import xtc.util.SymbolTable.Scope; /** * Common functionality for handling Java entities outside the AST. * * <p><u>Composite types</u> * <table border=0> * <tr><td>ExpressionT <td>> <td>GeneralLValueT, GeneralRValueT * <tr><td>GeneralLValueT <td>> <td><b>LValueT</b>, FieldT, LocalT, ParameterT * <tr><td><b>LValueT</b> <td>= <td>RValueT * <tr><td>FieldT <td>> <td><b>VariableT</b>:(FIELD Name WrappedRValueT) * <tr><td>LocalT <td>> <td><b>VariableT</b>:(LOCAL Name WrappedRValueT) * <tr><td>ParameterT <td>> <td><b>VariableT</b>:(PARAMETER Name RValueT) * <tr><td>GeneralRValueT <td>> <td><b>NullT</b>, WrappedRValueT * <tr><td>RValueT <td>> <td>PrimitiveT, <b>ArrayT</b>, WrappedClassT, WrappedInterfaceT * <tr><td><b>ArrayT</b> <td>= <td><b>LValueT</b> * <tr><td><b>ClassT</b> <td>= <td>Name WrappedClassT WrappedInterfaceT* Members * <tr><td><b>InterfaceT</b> <td>= <td>Name WrappedInterfaceT* Members * <tr><td>Members</b> <td>= <td>FieldT* MethodT* WrappedClassT* WrappedInterfaceT* * <tr><td><b>MethodT</b> <td>= <td>ReturnT Name ParameterT* WrappedClassT* * <tr><td>NotAValueT <td>> <td><b>PackageT</b>, <b>AnnotatedT</b>:ReturnT --- annotated with Constants.ATT_NOT_A_VALUE * <tr><td>ReturnT <td>> <td><b>VoidT</b>, RValueT * </table> * * <p><u>Wrapped types</u> * <table border=0> * <tr><td>WrappedRValueT</td> <td>> <td><b>ConstantT</b>:RValueT / RValueT * <tr><td>WrappedClassT</td> <td>> <td><b>AliasT</b>:Name / <b>AliasT</b>:<b>ClassT</b> / <b>ClassT</b> * <tr><td>WrappedInterfaceT</td> <td>> <td><b>AliasT</b>:Name / <b>AliasT</b>:<b>InterfaceT</b> / <b>InterfaceT</b> * </table> * * <p><u>Basetypes</u> * <table border=0> * <tr><td>PrimitiveT <td>> <td><b>BooleanT</b>, <b>NumberT</b> * <tr><td><b>NumberT</b> <td>> <td><b>IntegerT</b>, <b>FloatT</b> * <tr><td>NullT <td>= <td><b>ConstantT</b>:<b>VoidT</b> * </table> * </table> * * <p><u>Explanation</u> * <table border=0> * <tr><td><b>Bold font entities</b> <td>are represented explicitly in xtc.type * <tr><td><i>Super > Sub1, ..., SubN</i> <td>any of the <i>SubI</i> can appear where a <i>Super</i> is expected * <tr><td><i>Whole = Part1, ..., PartN</i> <td>the <i>Whole</i> consists of all the parts <i>PartI</i> * <tr><td><i>Whole = Part1 / ... / PartN</i> <td>the <i>Whole</i> consists of one of the parts <i>PartI</i> * <tr><td><i>Wrapper:Contents</i> <td>the <i>Wrapper</i> must wrap an instance of <i>Contents</i> * <tr><td><i>Repetee</i>* <td>zero or more occurences * </table> * * @author Martin Hirzel */ public final class JavaEntities { public static final class JavaTypePrinter extends TypePrinter { private boolean _showDetails; private final SymbolTable _table; public JavaTypePrinter(final SymbolTable tab, final boolean showDetails, final Printer initPrinter) { super(initPrinter); _showDetails = showDetails; _table = tab; } private boolean isJavaLangObject(final Type t) { if (null == t) return false; if (t.isClass()) return t.toClass().getQName().equals("java.lang.Object"); assert t.isAlias(); final String n = t.toAlias().getName(); return n.equals("Object") || n.equals("java.lang.Object"); } public final boolean printAnnotations(final Type t) { printAttributes(t); return t.hasAttributes(); } public final void printAttributes(final Type t) { if (t.hasAttributes()) for (final Attribute att : t.attributes()) printer.p(modifierToName(att)).p(' '); } private final void printMethodNoDetails(final MethodT t) { assert !_showDetails; dispatch(JavaEntities.declaringType(_table, t)); printer.p('.').p(t.getName()); } public final void printSignature(final FunctionOrMethodT t) { if (!_showDetails) throw new Error(); _showDetails = false; printer.p('('); for (final Iterator<Type> iter = t.getParameters().iterator(); iter.hasNext(); ) { printer.p(iter.next()); if (iter.hasNext() || t.isVarArgs()) printer.p(", "); } if (t.isVarArgs()) printer.p("..."); printer.p(") -> "); if (t.getResult().resolve().isFunction()) printer.p('(').p(t.getResult()).p(')'); else printer.p(t.getResult()); if ((null != t.getExceptions()) && (! t.getExceptions().isEmpty())) { printer.p(" throws "); for (Iterator<Type> iter = t.getExceptions().iterator(); iter.hasNext(); ) { printer.p(iter.next()); if (iter.hasNext()) printer.p(", "); } } _showDetails = true; } public final void visit(final AliasT t) { printer.p(t.getName()); } public final void visit(final AnnotatedT t) { if (JavaEntities.isNullT(t)) { printer.p("null"); return; } if (_showDetails) printAttributes(t); printer.p(t.getType()); } public final void visit(final ArrayT t) { printer.p(arrayElementType(t)).p("[]"); } public final void visit(final BooleanT t) { printer.p("boolean"); } public final void visit(final ClassT t) { if (!_showDetails) { printer.p(t.getQName()); return; } printAttributes(t); printer.p("class ").p(t.getQName()); if (!isJavaLangObject(t.getParent())) printer.p(" extends ").p(t.getParent()); if (!t.getInterfaces().isEmpty()) { printer.p(" implements "); for (final Iterator<Type> i = t.getInterfaces().iterator(); i.hasNext();) { printer.p(i.next()); if (i.hasNext()) printer.p(", "); } } printer.p("{"); final Scope scope = _table.getScope(typeToScopeName(t)); final TreeSet<String> sortedMembers = new TreeSet<String>(); for (final Iterator<String> i = scope.symbols(); i.hasNext();) sortedMembers.add(i.next()); for (final String s : sortedMembers) { final Type m = (Type) scope.lookupLocally(s); assert null != m; dispatch(m); printer.p("; "); } printer.p("}"); } public final void visit(final InterfaceT t) { if (!_showDetails) { printer.p(t.getQName()); return; } printAttributes(t); printer.p("interface ").p(t.getQName()); if (!t.getInterfaces().isEmpty()) { printer.p(" extends "); for (final Iterator<Type> i = t.getInterfaces().iterator(); i.hasNext();) { printer.p(i.next()); if (i.hasNext()) printer.p(", "); } } printer.p("{"); final Scope scope = _table.getScope(typeToScopeName(t)); final TreeSet<String> sortedMembers = new TreeSet<String>(); for (final Iterator<String> i = scope.symbols(); i.hasNext();) sortedMembers.add(i.next()); for (final String s : sortedMembers) { final Type m = (Type) scope.lookupLocally(s); assert null != m; dispatch(m); printer.p("; "); } printer.p("}"); } public final void visit(final MethodT t) { if (_showDetails) super.visit(t); else printMethodNoDetails(t); } } static class MiniVisitor_allUsedIdentifiers extends Visitor { public final Set<String> visit(final LineMarker m) { final Node n = m.getNode(); return null == n ? EMPTY_SET : stringSet(dispatch(n)); } public final Set<String> visit(final Node n) { final Set<String> result = new HashSet<String>(); for (int i = 0; i < n.size(); i++) { if (n.get(i) instanceof String) result.add(n.getString(i)); else if (n.get(i) instanceof Node) result.addAll(stringSet(dispatch(n.getNode(i)))); } return result; } public final Set<String> visitBinaryExpression(final GNode n) { final Set<String> result = new HashSet<String>(); result.addAll(stringSet(dispatch(n.getNode(0)))); result.addAll(stringSet(dispatch(n.getNode(2)))); return result; } public final Set<String> visitBlockDeclaration(final GNode n) { return stringSet(dispatch(n.getNode(1))); } public final Set<String> visitBooleanLiteral(final GNode n) { return EMPTY_SET; } public final Set<String> visitCharacterLiteral(final GNode n) { return EMPTY_SET; } public final Set<String> visitFieldDeclaration(final GNode n) { final Set<String> result = new HashSet<String>(); result.addAll(stringSet(dispatch(n.getNode(1)))); result.addAll(stringSet(dispatch(n.getNode(2)))); return result; } public final Set<String> visitFloatingPointLiteral(final GNode n) { return EMPTY_SET; } public final Set<String> visitForInit(final GNode n) { if (1 == n.size()) return stringSet(dispatch(n.getNode(0))); return stringSet(dispatch(n.getNode(1))); } public final Set<String> visitFormalParameter(final GNode n) { final Set<String> result = new HashSet<String>(); result.addAll(stringSet(dispatch(n.getNode(1)))); result.add(n.getString(3)); return result; } public final Set<String> visitImportDeclaration(final GNode n) { return stringSet(dispatch(n.getNode(1))); } public final Set<String> visitIntegerLiteral(final GNode n) { return EMPTY_SET; } public final Set<String> visitNullLiteral(final GNode n) { return EMPTY_SET; } public final Set<String> visitPostfixUnaryExpression(final GNode n) { return stringSet(dispatch(n.getNode(0))); } public final Set<String> visitPrefixUnaryExpression(final GNode n) { return stringSet(dispatch(n.getNode(1))); } public final Set<String> visitStringLiteral(final GNode n) { return EMPTY_SET; } public final Set<String> visitType(final GNode n) { return stringSet(dispatch(n.getNode(0))); } } static class MiniVisitor_scrubLocations extends Visitor { public final void visit(final Node n) { n.setLocation((Location)null); for (final Object o : n) if (o instanceof Node) dispatch((Node)o); } } /** * Enumerates all the types from which this type may inherit methods or * fields. For classes and interfaces, this means all reflexive or transitive * super-classes/insterfaces. For arrays, this means Object, Cloneable, or * Serializable. This iterator will resolve any aliases it encounters on the * way. This iterator only applies to reference types. */ static final class SuperTypesIter implements Iterator<Type> { private static String key(final Type type) { final Type t = type.isAlias() ? ((AliasT) type).getType() : type; if (t.isArray()) return key(JavaEntities.arrayElementType(t.toArray())) + "[]"; if (JavaEntities.isPrimitiveT(type)) return type.toString(); return JavaEntities.resolveToRawClassOrInterfaceT(t).getQName(); } private final List<File> _paths; /** Set of String keys: current and former contents of todo. */ private final Set<String> _seen = new HashSet<String>(); private final SymbolTable _tab; /** List of Types; add to end, remove from front. */ private final LinkedList<Type> _todo = new LinkedList<Type>(); public SuperTypesIter(final SymbolTable tab, final List<File> paths, final Type start) { this(tab, paths, start, true); } public SuperTypesIter(final SymbolTable tab, final List<File> paths, final Type start, final boolean reflexive) { _tab = tab; _paths = paths; final Type rStart = JavaEntities.resolveToRValue(start); if (reflexive) add(rStart); else addDirectSupertypes(rStart); } private final void add(final Type type) { assert null != type; assert JavaEntities.isReferenceT(type); if (_seen.add(key(type))) _todo.addLast(type); } private void addDirectSupertypes(final Type sub) { for (final ClassOrInterfaceT sup : JavaEntities.directSuperTypes(_tab, _paths, sub)) add(sup); } public final boolean hasNext() { return !_todo.isEmpty(); } public final Type next() { if (_todo.isEmpty()) return null; final Type result = _todo.removeFirst(); addDirectSupertypes(result); return result; } public final void remove() { throw new UnsupportedOperationException(); } } public static final class UnicodeUnescaper extends Reader { private boolean _havePeek; private int _peek; private final Reader _reader; public UnicodeUnescaper(final Reader reader) { _havePeek = false; _peek = -1; _reader = reader; } public final void close() throws IOException { _reader.close(); } public final int read() throws IOException { if (_havePeek) { _havePeek = false; return _peek; } final int a = _reader.read(); if ('\\' != a) return a; _peek = _reader.read(); if ('u' != _peek) { _havePeek = true; return a; } int curr = _peek; while ('u' == curr) curr = _reader.read(); final byte[] digits = new byte[4]; for (int i=0; i<4; i++) { if (0 != i) curr = _reader.read(); switch (curr) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': digits[i] = (byte)(curr - '0'); break; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': digits[i] = (byte)(10 + curr - 'a'); break; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': digits[i] = (byte)(10 + curr - 'A'); break; default: throw new Error("illegal unicode sequence"); } } int result = 0; for (int i=0; i<4; i++) result = (result << 4) | digits[i]; return result; } public final int read(final char[] cbuf, final int off, final int len) throws IOException { int result = 0; while (result < len) { final int c = read(); if (-1 == c) return -1; cbuf[off + result] = (char)c; result++; } return result; } } public static final File TEMP_DIR; public static final Set<String> EMPTY_SET = Collections.emptySet(); private static final Map<Attribute,String> MODIFIER_TO_NAME = new HashMap<Attribute,String>(); private static final Map<String,Type> NAME_TO_BASETYPE = new HashMap<String,Type>(); private static final Map<String,Attribute> NAME_TO_MODIFIER = new HashMap<String,Attribute>(); private static boolean recursiveTypeDotType = false; static { final String[] names = new String[] { System.getProperty("TMP"), System.getProperty("TEMP"), "/tmp", "/cygwin/tmp", "." }; File tempDir = null; for (final String name : names) { if (null != name) { final File f = new File(name); if (f.exists()) { tempDir = f; break; } } } assert null != tempDir && tempDir.exists(); TEMP_DIR = tempDir; NAME_TO_MODIFIER.put("public", Constants.ATT_PUBLIC); NAME_TO_MODIFIER.put("protected", Constants.ATT_PROTECTED); NAME_TO_MODIFIER.put("private", Constants.ATT_PRIVATE); NAME_TO_MODIFIER.put("static", Constants.ATT_STORAGE_STATIC); NAME_TO_MODIFIER.put("abstract", Constants.ATT_ABSTRACT); NAME_TO_MODIFIER.put("final", Constants.ATT_CONSTANT); NAME_TO_MODIFIER.put("native", Constants.ATT_NATIVE); NAME_TO_MODIFIER.put("synchronized", Constants.ATT_SYNCHRONIZED); NAME_TO_MODIFIER.put("transient", Constants.ATT_TRANSIENT); NAME_TO_MODIFIER.put("volatile", Constants.ATT_VOLATILE); NAME_TO_MODIFIER.put("strictfp", Constants.ATT_STRICT_FP); for (final Map.Entry<String,Attribute> e : NAME_TO_MODIFIER.entrySet()) MODIFIER_TO_NAME.put(e.getValue(), e.getKey()); NAME_TO_BASETYPE.put("boolean", BooleanT.TYPE); NAME_TO_BASETYPE.put("byte", NumberT.BYTE); NAME_TO_BASETYPE.put("char", NumberT.CHAR); NAME_TO_BASETYPE.put("double", NumberT.DOUBLE); NAME_TO_BASETYPE.put("float", NumberT.FLOAT); NAME_TO_BASETYPE.put("int", NumberT.INT); NAME_TO_BASETYPE.put("long", NumberT.LONG); NAME_TO_BASETYPE.put("null", VoidT.TYPE.annotate().constant(NullReference.NULL)); NAME_TO_BASETYPE.put("short", NumberT.SHORT); NAME_TO_BASETYPE.put("void", VoidT.TYPE); } public static void addBaseTypes(final SymbolTable tab) { if (null == tab.lookup("int")) { final String oldScope = JavaEntities.enterScopeByQualifiedName(tab, ""); for (final String name : NAME_TO_BASETYPE.keySet()) tab.current().define(name, nameToBaseType(name)); JavaEntities.enterScopeByQualifiedName(tab, oldScope); } } public static List<MethodT> allAbstractMethods(final SymbolTable tab, final List<File> paths, final ClassT clazz) { final List<MethodT> all = allMethods(tab, paths, clazz); final List<MethodT> specific = mostSpecificMethods(tab, paths, all); final List<MethodT> result = new ArrayList<MethodT>(); for (final MethodT method : specific) if (hasModifier(method, "abstract")) result.add(method); return result; } public static List<MethodT> allMethods(final SymbolTable tab, final List<File> paths, final ClassT base) { final List<MethodT> result = new ArrayList<MethodT>(); for (final Iterator<Type> i = new SuperTypesIter(tab, paths, base); i.hasNext();) result.addAll(methodsOwn(i.next())); return result; } public static Set<String> allUsedIdentifiers(final GNode ast) { final MiniVisitor_allUsedIdentifiers v = new MiniVisitor_allUsedIdentifiers(); final Set<String> result = stringSet(v.dispatch(ast)); return result; } public static Type arrayElementType(final ArrayT arrayT) { final AnnotatedT l = (AnnotatedT)arrayT.getType(); final Type result = l.getType(); assert isRValueT(result); return result; } public static VariableT arrayLengthField() { final VariableT result = VariableT.newField(nameToBaseType("int"), "length"); result.addAttribute(nameToModifier("final")); result.addAttribute(nameToModifier("public")); result.scope(".package(java.lang).file().Object"); assert isFieldT(result); return result; } public static String baseTypeToName(final Type baseType) { return typeToString(null, false, baseType); } public static ClassOrInterfaceT canonicalAliasToType(final SymbolTable tab, final List<File> paths, final AliasT alias, final boolean mayBeInUnnamedPackage) { if (alias.getType() == null) alias.setType(canonicalNameToType(tab, paths, alias.getName(), mayBeInUnnamedPackage)); return (ClassOrInterfaceT) alias.getType(); } public static String canonicalName(final SymbolTable tab, final String simpleName) { final ClassOrInterfaceT enclosingClass = currentType(tab); if (enclosingClass != null) return Utilities.qualify(enclosingClass.getQName(), simpleName); final PackageT enclosingPackage = currentPackage(tab); if (enclosingPackage == null) { final Error e = new Error(); e.printStackTrace(); throw e; } if ("".equals(enclosingPackage.getName())) return simpleName; return Utilities.qualify(enclosingPackage.getName(), simpleName); } public static PackageT canonicalNameToPackage(final SymbolTable tab, final String name) { if (name.indexOf('.') == -1) return simpleNameToPackage(tab, name); final PackageT base = canonicalNameToPackage( // tab, Utilities.getQualifier(name)); return packageDotPackage(tab, base, Utilities.unqualify(name)); } public static Type canonicalNameToPackageOrType(final SymbolTable tab, final List<File> paths, final String name, final boolean mayBeInUnnamedPackage) { final Type t = canonicalNameToType(tab, paths, name, mayBeInUnnamedPackage); return t != null ? t : canonicalNameToPackage(tab, name); } public static ClassOrInterfaceT canonicalNameToType(final SymbolTable tab, final List<File> paths, final String name, final boolean mayBeInUnnamedPackage) { if (name.indexOf('.') == -1) { if (mayBeInUnnamedPackage) return packageDotType(tab, paths, simpleNameToPackage(tab, ""), name); return null; } final String qualifier = Utilities.getQualifier(name); final String selector = Utilities.unqualify(name); if (qualifier.indexOf('.') == -1) return packageDotType(tab, paths, simpleNameToPackage(tab, qualifier), selector); final Type base = canonicalNameToPackageOrType(tab, paths, qualifier, mayBeInUnnamedPackage); if (base.isPackage()) return packageDotType(tab, paths, base.toPackage(), selector); return typeDotType(tab, paths, (ClassOrInterfaceT) base, false, selector); } public static List<File> classpath(final Runtime runtime) { return runtime.getFileList(Runtime.INPUT_DIRECTORY); } public static boolean constructorsReturnVoid() { return true; } /** Can only create concrete subclass that implements all abstract methods if there * is no pair of abstract methods with the same signature, but different return types. */ public static boolean couldCreateConcreteSubclass(final SymbolTable tab, final List<File> paths, final ClassT clazz) { final List<MethodT> abstractMethods = allAbstractMethods(tab, paths, clazz); for (final MethodT m : abstractMethods) resolveIfAliasMethod(tab, paths, m); for (final MethodT m1 : abstractMethods) for (final MethodT m2 : abstractMethods) if (m1.getName().equals(m2.getName()) && sameMethodSignature(m1, m2)) if (!JavaTypeConverter.isIdentical(m1.getResult(), m2.getResult())) return false; return true; } public static List<Type> currentImports(final SymbolTable tab) { for (SymbolTable.Scope s = tab.current(); !s.isRoot(); s = s.getParent()) { final String n = s.getName(); if (SymbolTable.isInNameSpace(n, "file")) return imports(tab, currentPackage(tab), SymbolTable.fromNameSpace(n)); } return imports(tab, null, null); } public static MethodT currentMethod(final SymbolTable tab) { for (SymbolTable.Scope s = tab.current(); !s.isRoot(); s = s.getParent()) { final String name = s.getName(); if (SymbolTable.isInNameSpace(name, "method")) { final ClassOrInterfaceT type = currentType(tab); for (final Type method : type.getMethods()) { assert method.isMethod(); final String s2; assert method.hasScope(false); s2 = method.getScope(); if (s.getQualifiedName().equals(s2)) return method.toMethod(); } } } return null; } public static PackageT currentPackage(final SymbolTable tab) { for (SymbolTable.Scope s = tab.current(); !s.isRoot(); s = s.getParent()) { final String n = s.getName(); if (n.startsWith("package(")) return canonicalNameToPackage(tab, SymbolTable.fromNameSpace(n)); } return canonicalNameToPackage(tab, ""); } public static ClassOrInterfaceT currentType(final SymbolTable tab) { for (SymbolTable.Scope s = tab.current(); !s.isRoot(); s = s.getParent()) { final String tagName = SymbolTable.toTagName(s.getName()); final ClassOrInterfaceT result = (ClassOrInterfaceT)s.getParent().lookup(tagName); if (result != null) return result; } return null; } public static PackageT declaringPackage(final SymbolTable tab, final Type type) { if (type.isPackage()) return (PackageT)type; if (type instanceof ClassOrInterfaceT && isTypeTopLevel((ClassOrInterfaceT)type)) { final String oldScope = enterScopeByQualifiedName(tab, typeToScopeName(type)); final PackageT result = currentPackage(tab); enterScopeByQualifiedName(tab, oldScope); return result; } if (isTypeMember(type)) return declaringPackage(tab, declaringType(tab, type)); final String oldScope = enterScopeByQualifiedName(tab, declaringScopeName(type)); final PackageT result = currentPackage(tab); enterScopeByQualifiedName(tab, oldScope); return result; } public static String declaringScopeName(final Type type) { assert !type.isAlias(); final Type u = isConstantT(type) ? ((AnnotatedT)type).getType() : type; if (u.hasScope(false)) { final String ownScope = u.getScope(); if (isLocalT(type) || isParameterT(type) || isFieldT(type)) return ownScope; return Utilities.getQualifier(ownScope); } return null; } public static ClassOrInterfaceT declaringType(final SymbolTable tab, final Type member) { assert member.isMethod() || isFieldT(member) || isWrappedClassT(member) || isWrappedInterfaceT(member); final String declaringScope = declaringScopeName(member); assert null != declaringScope; assert isScopeForMember(declaringScope); final String oldScope = enterScopeByQualifiedName(tab, declaringScope); final ClassOrInterfaceT result = currentType(tab); assert null != result; enterScopeByQualifiedName(tab, oldScope); return result; } public static Type dereference(final Type generalLValue) { assert isGeneralLValueT(generalLValue); final WrappedT wrapped = isLValueT(generalLValue) ? (AnnotatedT) generalLValue : resolveToRawLValue(generalLValue); final Type result = wrapped.getType(); assert isGeneralRValueT(result); return result; } public static List<ClassOrInterfaceT> directSuperTypes(final SymbolTable tab, final List<File> paths, final Type sub) { if (sub.isArray()) { final List<ClassOrInterfaceT> result = new ArrayList<ClassOrInterfaceT>(); result.add(JavaEntities.tObject(tab)); result.add(JavaEntities.tCloneable(tab)); result.add(JavaEntities.tSerializable(tab)); return result; } final ClassOrInterfaceT rawSub = JavaEntities.resolveToRawClassOrInterfaceT(sub); final List<AliasT> aliases = new ArrayList<AliasT>(); if (JavaEntities.isWrappedClassT(sub)) { final Type sup = rawSub.toClass().getParent(); if (null != sup) aliases.add(sup.toAlias()); } for (final Type i : rawSub.getInterfaces()) aliases.add(i.toAlias()); final String scope = JavaEntities.typeToScopeName(sub); return JavaEntities.qualifiedAliasesToTypes(tab, paths, scope, aliases); } public static String enterScopeByQualifiedName(final SymbolTable tab, final String name) { final String oldScopeName = tab.current().getQualifiedName(); assert null != name; tab.setScope(tab.getScope(name)); return oldScopeName; } public static List<VariableT> fieldsApplicableAndAccessible(final SymbolTable tab, final List<File> paths, final Type base, final boolean parents, final String name) { final List<VariableT> fields = parents ? fieldsOwnAndInherited(tab, paths, base) : fieldsOwn(base); final List<VariableT> result = new ArrayList<VariableT>(); for (final VariableT field : fields) if (isAccessibleIn(tab, paths, field, base)) if (isApplicableField(tab, paths, field, name)) result.add(field); return result; } public static List<VariableT> fieldsInherited(final SymbolTable tab, final List<File> paths, final Type base) { //gosling_et_al_2000 8.3 (before subsections) final List<VariableT> result = new ArrayList<VariableT>(); final List<VariableT> fieldsOwn = fieldsOwn(base); final String baseScope = declaringScopeName(base); assert null != baseScope || base.isArray(); final List<ClassOrInterfaceT> superTypes = directSuperTypes(tab, paths, base); for (final ClassOrInterfaceT superType : superTypes) { final List<VariableT> superFields = fieldsOwnAndInherited(tab, paths, superType); superFields: for (final VariableT f : superFields) { if (hasModifier(f, "private")) continue superFields; if (null != baseScope && !isAccessibleFromIn(tab, paths, baseScope, f, superType)) continue superFields; for (final VariableT o : fieldsOwn) if (f.getName().equals(o.getName())) continue superFields; for (final VariableT r : result) if (f == r) continue superFields; result.add(f); } } return result; } public static List<VariableT> fieldsOwn(final Type base) { final List<VariableT> result = new ArrayList<VariableT>(1); if (base.isArray()) result.add(arrayLengthField()); else for (final Type f : resolveToRawClassOrInterfaceT(base).getFields()) result.add(f.toVariable()); return result; } public static List<VariableT> fieldsOwnAndInherited(final SymbolTable tab, final List<File> paths, final Type base) { final List<VariableT> result = new ArrayList<VariableT>(); result.addAll(fieldsInherited(tab, paths, base)); result.addAll(fieldsOwn(base)); return result; } public static String fileNameToScopeName(final String absolutePath) { return SymbolTable.toNameSpace(absolutePath, "file"); } /** Does the class or one of its superclasses declare any abstract methods * that have not been implemented here or in a superclass? (gosling_et_al_2000 <a * href="http://java.sun.com/docs/books/jls/second_edition/html/classes.doc.html#34944">§8.1.1</a>). */ public static boolean hasAbstractMethods(final SymbolTable tab, final List<File> paths, final ClassT clazz) { return !allAbstractMethods(tab, paths, clazz).isEmpty(); } /** Does the class depend on itself by inheritance or by nesting? (gosling_et_al_2000 <a * href="http://java.sun.com/docs/books/jls/second_edition/html/classes.doc.html#262560">§8.1.3</a>). */ public static boolean hasCircularDependency(final SymbolTable tab, final List<File> paths, final ClassOrInterfaceT type) { final Iterator<Type> iSup = new SuperTypesIter(tab, paths, type, false); while (iSup.hasNext()) { Type t = iSup.next(); inner: while (true) { if (JavaTypeConverter.isIdentical(type, t)) return true; if (!isTypeMember(t)) break inner; t = declaringType(tab, t); } } return false; } public static boolean hasModifier(final Type t, final String m) { final Type u = t.isAlias() ? ((AliasT) t).getType() : t; assert isGeneralLValueT(u) || u.isMethod() || u.isClass() || u.isInterface(); return u.hasAttribute(nameToModifier(m), false); } static List<Type> imports(final SymbolTable tab, final PackageT packageT, final String fileName) { final String baseScope = null == packageT ? "" : scopeName(packageT, fileName); final Object o = tab.lookup(Utilities.qualify(baseScope, "imports(*)")); if (null == o) return new ArrayList<Type>(); if (o instanceof List) return typeList((List)o); final ArrayList<Type> list = new ArrayList<Type>(); list.add((Type)o); return list; } /** Is it allowed to use the entity from the current scope of the symbol table (gosling_et_al_2000 <a * href="http://java.sun.com/docs/books/jls/second_edition/html/names.doc.html#104285">§6.6</a>)? */ public static boolean isAccessible(final SymbolTable tab, final List<File> paths, final Type entity) { return isAccessibleIn(tab, paths, entity, null); } public static boolean isAccessibleFromIn(final SymbolTable tab, final List<File> paths, final String scope, final Type entity, final Type inType) { final String oldScope = JavaEntities.enterScopeByQualifiedName(tab, scope); final boolean result = isAccessibleIn(tab, paths, entity, inType); JavaEntities.enterScopeByQualifiedName(tab, oldScope); return result; } public static boolean isAccessibleIn(final SymbolTable tab, final List<File> paths, final Type entity, final Type inType) { if (entity.isArray()) return true; if (entity.isAlias()) return isAccessible(tab, paths, ((AliasT)entity).getType()); if (entity.isPackage()) return true; final boolean samePackage = declaringPackage(tab, entity).getName().equals(currentPackage(tab).getName()); if (isWrappedClassT(entity) || isWrappedInterfaceT(entity)) { if (hasModifier(entity, "public")) return true; if (isTypeTopLevel((ClassOrInterfaceT)entity)) return samePackage; } if (!isTypeMember(entity)) return true; final Type base = null == inType ? declaringType(tab, entity) : inType; assert null != base; if (!isAccessible(tab, paths, base)) return false; if (hasModifier(entity, "public")) return true; if (hasModifier(entity, "protected")) { if (samePackage) return true; if (entity.isMethod() && isConstructor(base, entity.toMethod())) { return false; //TD 36 (6.6.2) isAccessibleIn() for protected constructors in different package } else { final Type decl = declaringType(tab, entity), curr = currentType(tab); return (null == curr || isSuperClass(tab, paths, decl, curr)) && isSuperClass(tab, paths, decl, base); } } if (hasModifier(entity, "private")) { final Type top; final String oldScope = enterScopeByQualifiedName(tab, typeToScopeName(base)); while (true) { final ClassOrInterfaceT t = JavaEntities.currentType(tab); enterScopeByQualifiedName(tab, typeToScopeName(t)); if (isTypeTopLevel(t)) { top = t; break; } tab.exit(); } enterScopeByQualifiedName(tab, oldScope); return tab.current().getQualifiedName().startsWith(typeToScopeName(top)); } return samePackage; } public static boolean isApplicableField(final SymbolTable tab, final List<File> paths, final VariableT field, final String name) { if (name.equals(field.getName())) { final ClassOrInterfaceT declaringType = declaringType(tab, field); resolveIfAlias(tab, paths, typeToScopeName(declaringType), field.getType()); return true; } return false; } public static boolean isApplicableMemberType(final Type memberType, final String name) { final ClassOrInterfaceT raw = resolveToRawClassOrInterfaceT(memberType); return name.equals(raw.getName()); } public static boolean isApplicableMethod(final SymbolTable tab, final List<File> paths, final MethodT callee, final String name, final List<Type> actuals) { if (!callee.getName().equals(name)) return false; final List<Type> formals = callee.getParameters(); if (formals.size() != actuals.size()) return false; final ClassOrInterfaceT declaringType = declaringType(tab, callee); final String aScope = tab.current().getQualifiedName(); final Iterator<Type> iF = formals.iterator(), iA = actuals.iterator(); while (iF.hasNext()) { final Type formal = iF.next(); resolveIfAlias(tab, paths, typeToScopeName(declaringType), dereference(formal)); final Type aAlias = iA.next(); final Type actual = resolveIfAlias(tab, paths, aScope, aAlias); final boolean conv = JavaTypeConverter.isParameterPassable(tab, paths, dereference(formal), actual); if (!conv) return false; } return true; } public static boolean isCheckedException(final SymbolTable tab, final List<File> paths, final Type exc) { // gosling_et_al_2000 11.2 return isSuperClass(tab, paths, tThrowable(tab), exc) && !isSuperClass(tab, paths, tRuntimeException(tab), exc) && !isSuperClass(tab, paths, tError(tab), exc); } public static boolean isConstantT(final Type t) { return (null != t) && t.isAnnotated() && t.hasConstant(false); } public static boolean isConstructor(final Type declaringType, final MethodT maybeConstructor) { if (!declaringType.isClass()) return false; return declaringType.toClass().getName().equals(maybeConstructor.getName()); } /** ExpressionT > GeneralLValueT, GeneralRValueT. */ public static boolean isExpressionT(final Type t) { return isGeneralLValueT(t) || isGeneralRValueT(t); } /** FieldT = Name WrappedRValueT. */ public static boolean isFieldT(final Type t) { return t.isVariable() && VariableT.Kind.FIELD == t.toVariable().getKind(); } /** GeneralLValueT > LValueT, VariableT, FieldT, ParameterT. */ public static boolean isGeneralLValueT(final Type t) { return isLValueT(t) || isLocalT(t) || isFieldT(t) || isParameterT(t); } /** GeneralRValueT > NullT, WrappedRValueT. */ public static boolean isGeneralRValueT(final Type t) { return isNullT(t) || isWrappedRValueT(t); } public static boolean isInLocalNameSpace(final String name) { final String lastPart = Utilities.unqualify(name); return SymbolTable.isInNameSpace(lastPart, "method") || SymbolTable.isInNameSpace(lastPart, "block") || SymbolTable.isInNameSpace(lastPart, "forStatement") || SymbolTable.isInNameSpace(lastPart, "labeledStatement") || SymbolTable.isInNameSpace(lastPart, "function") //in Jeannie, not in Java || SymbolTable.isInNameSpace(lastPart, "withStatement") //in Jeannie, not in Java || SymbolTable.isInNameSpace(lastPart, "matchClause") //in Matchete, not in Java || SymbolTable.isInNameSpace(lastPart, "catchClause"); } public static boolean isInt(final Type t) { final Type n = JavaTypeConverter.promoteUnaryNumeric(t); if (null == n) return false; final Type r = resolveToRawRValue(n); return r.isInteger() && NumberT.Kind.INT == ((IntegerT)r).getKind(); } /** LocalT = Name WrappedRValueT. */ public static boolean isLocalT(final Type t) { return t.isVariable() && VariableT.Kind.LOCAL == t.toVariable().getKind(); } /** LValueT = RValueT. */ public static boolean isLValueT(final Type t) { return (null != t) && t.isAnnotated() && t.hasShape(false); } /** NotAValueT > PackageT, AnnotatedT:ReturnT --- annotated with Constants.ATT_NOT_A_VALUE */ public static boolean isNotAValueT(final Type t) { return t.isPackage() || t instanceof AnnotatedT && t.hasAttribute(Constants.ATT_NOT_A_VALUE) && isReturnT(((AnnotatedT)t).getType()); } /** NullT = ConstantT:VoidT. */ public static boolean isNullT(final Type t) { return isConstantT(t) && ((AnnotatedT)t).getType().isVoid(); } /** ParameterT = Name RValueT. */ public static boolean isParameterT(final Type t) { return t.isVariable() && VariableT.Kind.PARAMETER == t.toVariable().getKind(); } /** PrimitiveT > BooleanT, NumberT. */ public static boolean isPrimitiveT(final Type t) { return t.isBoolean() || t.isNumber(); } /** Is t a class, interface, or array type (gosling_et_al_2000 <a * href="http://java.sun.com/docs/books/jls/second_edition/html/typesValues.doc.html#9317">§4.3</a>)? */ public static boolean isReferenceT(final Type t) { if (isNullT(t)) return true; final Type u = isConstantT(t) ? ((AnnotatedT)t).getType() : t; return u.isArray() || isWrappedClassT(u) || isWrappedInterfaceT(u); } /** ReturnT > VoidT, RValueT. */ public static boolean isReturnT(final Type t) { return t.isVoid() || isRValueT(t); } /** RValueT > PrimitiveT, ArrayT, WrappedClassT, WrappedInterfaceT. */ public static boolean isRValueT(final Type t) { return isPrimitiveT(t) || t.isArray() || isWrappedClassT(t) || isWrappedInterfaceT(t); } public static boolean isScopeForMember(final String scopeName) { // gosling_et_al_2000 8.5, 9.5 return isScopeNested(scopeName) && !isInLocalNameSpace(scopeName); } public static boolean isScopeLocal(final String scopeName) { //gosling_et_al_2000 14.3 return isInLocalNameSpace(scopeName); } public static boolean isScopeNested(final String scopeName) { //gosling_et_al_2000 intro paragraph to chapters 8 and 9 if (isScopeTopLevel(scopeName)) return false; return 3 < Utilities.toComponents(scopeName).length; } public static boolean isScopeTopLevel(final String scopeName) { //gosling_et_al_2000 7.6 if (null == scopeName) System.out.println("hohoho"); assert null != scopeName; if ("".equals(scopeName) || ".".equals(scopeName)) return true; //top-level in C file final String[] scopeParts = Utilities.toComponents(scopeName); if (0 < scopeParts.length) assert "".equals(scopeParts[0]); if (2 < scopeParts.length && SymbolTable.isInNameSpace(scopeParts[1], "package")) { assert SymbolTable.isInNameSpace(scopeParts[2], "file"); return 3 == scopeParts.length; //top-level in Java file } return false; } public static boolean isSuperClass(final SymbolTable tab, final List<File> paths, final Type sup, final Type sub) { if (!isWrappedClassT(sub) || !isWrappedClassT(sup)) return false; return JavaTypeConverter.isIdentical(sup, sub) || JavaTypeConverter.isWiderReference(tab, paths, sup, sub); } /** Note: not symmetric! */ public static boolean isSuperMethod(final SymbolTable tab, final List<File> paths, final MethodT sup, final MethodT sub) { if (!sub.getName().equals(sup.getName())) return false; if (sub.getParameters().size() != sup.getParameters().size()) return false; if (hasModifier(sup, "private")) return false; final ClassOrInterfaceT dSup = declaringType(tab, sup), dSub = declaringType(tab, sub); if (!isAccessibleFromIn(tab, paths, typeToScopeName(dSub), sup, dSup)) return false; if (!JavaTypeConverter.isParameterPassable(tab, paths, dSup, dSub)) return false; final Iterator<Type> iSub = sub.getParameters().iterator(); final Iterator<Type> iSup = sup.getParameters().iterator(); while (iSub.hasNext()) { final VariableT tSup = iSup.next().toVariable(), tSub = iSub.next().toVariable(); final Type rSup = tSup.getType(), rSub = tSub.getType(); resolveIfAlias(tab, paths, declaringScopeName(sup), rSup); resolveIfAlias(tab, paths, declaringScopeName(sub), rSub); if (!JavaTypeConverter.isParameterPassable(tab, paths, rSup, rSub)) return false; } if (JavaTypeConverter.isIdentical(dSup, dSub) && sameMethodSignature(sup, sub)) { //Java 1.5 introduces contravariant return types, and we must tackle them in libraries, such as StringBuffer final Type tSup = sup.getResult(), tSub = sub.getResult(); resolveIfAlias(tab, paths, declaringScopeName(sup), tSup); resolveIfAlias(tab, paths, declaringScopeName(sub), tSub); if (!JavaTypeConverter.isReturnTypeSubstitutable(tab, paths, tSup, tSub)) return false; } return true; } public static boolean isTypeAnonymous(final ClassOrInterfaceT t) { return t.isClass() && null == t.toClass().getName(); } public static boolean isTypeInner(final ClassOrInterfaceT t) { // gosling_et_al_2000 8.1.2 return !hasModifier(t, "static") && isTypeNested(t); } public static boolean isTypeLocal(final ClassOrInterfaceT t) { // gosling_et_al_2000 14.3 return isScopeLocal(declaringScopeName(t)) && !isTypeAnonymous(t); } public static boolean isTypeMember(final Type t) { return isScopeForMember(declaringScopeName(t)); } public static boolean isTypeNamed(final ClassOrInterfaceT t) { return !isTypeAnonymous(t); } public static boolean isTypeNested(final ClassOrInterfaceT t) { final String scope = declaringScopeName(t); assert null != scope; return isScopeNested(scope); } public static boolean isTypeTopLevel(final ClassOrInterfaceT t) { return isScopeTopLevel(declaringScopeName(t)); } /** WrappedClassT = AliasT:Name / AliasT:ResolvedClassT / ResolvedClassT. */ public static boolean isWrappedClassT(final Type t) { if (t.isAlias()) { final Type r = ((AliasT) t).getType(); return null == r || r.isClass(); } return t.isClass(); } /** WrappedInterfaceT = AliasT:Name / AliasT:ResolvedInterfaceT / ResolvedInterfaceT. */ public static boolean isWrappedInterfaceT(final Type t) { if (t.isAlias()) { final Type r = ((AliasT) t).getType(); return null == r || r.isInterface(); } return t.isInterface(); } /** WrappedRValueT = ConstantT:RValueT / RValueT. */ public static boolean isWrappedRValueT(final Type t) { return isConstantT(t) && isRValueT(((AnnotatedT)t).getType()) || isRValueT(t); } public static String javaAstToString(final Node ast) { final CharArrayWriter writer = new CharArrayWriter(); final JavaPrinter printer = new JavaPrinter(new Printer(writer)); printer.dispatch(ast); return writer.toString(); } /** * Don't use this method gratuitously. Originally, I used this to serialize * ASTs into strings, then concatenated the strings into more code, then * reparsed the result to a larger AST. But that is slow and less elegant * than using FactoryFactory. */ private static GNode javaStringToAst(final String production, final String code) { try { return JavaEntities.javaStringToAst(production, code, true); } catch (final Exception e) { throw new Error(e); } } public static GNode javaStringToAst(final String production, final String escaped, final boolean simple) throws Exception { final Method method = JavaParser.class.getDeclaredMethod("p" + production, Integer.TYPE); method.setAccessible(true); final String string = unicodeUnescape(escaped); final JavaParser parser = new JavaParser( // new StringReader(string), "<input>", string.length()); final Object[] paramValues = { new Integer(0) }; final Result parseResult = (Result) method.invoke(parser, paramValues); if (! parseResult.hasValue()) parser.signal(parseResult.parseError()); if (parseResult.index != string.length()) throw new ParseException("input not fully consumed"); final GNode ast = (GNode) parseResult.semanticValue(); if (simple) return (GNode) new JavaAstSimplifier().dispatch(ast); return ast; } public static Type javaStringToType(final String code) { final SymbolTable tab = new SymbolTable(); JavaUnitTests.enterPackageFile(tab, "", "<input>"); return javaStringToType(code, tab); } private static Type javaStringToType(final String code, final SymbolTable tab) { final GNode ast = JavaEntities.javaStringToAst("Type", code); final JavaAnalyzer ana = new JavaAnalyzer(JavaUnitTests.newRuntime(), tab); final Type result = (Type) ana.dispatch(ast); return result; } public static GNode javaTypeToAst(final SymbolTable tab, final Type type) { final String s = JavaEntities.javaTypeToString(tab, type); final GNode result = JavaEntities.javaStringToAst("ResultType", s); return scrubLocations(result); } public static String javaTypeToString(final SymbolTable tab, final Type type) { if (JavaEntities.isGeneralRValueT(type)) { final Type r = JavaEntities.resolveToRawRValue(type); return JavaEntities.typeToString(tab, false, r); } return JavaEntities.typeToString(tab, false, type); } /** * Resolve single-type import. The name is canonical (gosling_et_al_2000 * Section 7.5.1) and denotes a class or interface. The only thing that is * uncertain is where the package ends and where the nested types start. For * example, in a.b.c, it could be that the package is a.b and the class is c, * or that the package is a, the outer class is b, and the inner class is c. * There can be no ambiguity (gosling_et_al_2000 Section 6.4), because a * package must not have a top-level type and a subpackage of the same name. */ static ClassOrInterfaceT lookupImport(final SymbolTable tab, final List<File> paths, final AliasT alias) { return canonicalAliasToType(tab, paths, alias, false); } public static List<Type> memberTypesApplicableAndAccessible(final SymbolTable tab, final List<File> paths, final ClassOrInterfaceT base, final boolean parents, final String name) { final List<Type> memberTypes = parents ? memberTypesOwnAndInherited(tab, paths, base) : memberTypesOwn(tab, paths, base); final List<Type> result = new ArrayList<Type>(); for (final Type memberType : memberTypes) if (isAccessibleIn(tab, paths, memberType, base)) if (isApplicableMemberType(memberType, name)) result.add(memberType); return result; } public static List<Type> memberTypesInherited(final SymbolTable tab, final List<File> paths, final ClassOrInterfaceT base) { //gosling_et_al_2000 8.5 (before subsections) final List<Type> result = new ArrayList<Type>(); final List<Type> memberTypesOwn = memberTypesOwn(tab, paths, base); final String baseScope = declaringScopeName(base); assert null != baseScope || base.isArray(); final List<ClassOrInterfaceT> superTypes = directSuperTypes(tab, paths, base); for (final ClassOrInterfaceT superType : superTypes) { final List<Type> superMemberTypes = memberTypesOwnAndInherited(tab, paths, superType); superMemberTypes: for (final Type t : superMemberTypes) { //the private and accessible checks are here for consistency with fieldsInherited, the specification doesn't explicitly mention them if (hasModifier(t, "private")) continue superMemberTypes; if (null != baseScope && !isAccessibleFromIn(tab, paths, baseScope, t, superType)) continue superMemberTypes; final String tName = resolveToRawClassOrInterfaceT(t).getName(); for (final Type o : memberTypesOwn) if (tName.equals(resolveToRawClassOrInterfaceT(o).getName())) continue superMemberTypes; for (final Type r : result) if (t == r) continue superMemberTypes; result.add(t); } } return result; } public static List<Type> memberTypesOwn(final SymbolTable tab, final List<File> paths, final ClassOrInterfaceT base) { if (base.isArray()) return new ArrayList<Type>(0); final Scope scope = tab.getScope(typeToScopeName(base)); final List<Type> result = new ArrayList<Type>(); for (final Iterator<String> i = scope.symbols(); i.hasNext();) { final String symbol = i.next(); final Type member = (Type)scope.lookupLocally(symbol); if (isWrappedClassT(member) || isWrappedInterfaceT(member)) result.add(member); } return result; } public static List<Type> memberTypesOwnAndInherited(final SymbolTable tab, final List<File> paths, final ClassOrInterfaceT base) { final List<Type> result = new ArrayList<Type>(); result.addAll(memberTypesInherited(tab, paths, base)); result.addAll(memberTypesOwn(tab, paths, base)); return result; } /** All methods in the base type that are accessible, and whose formals accept the actuals * after method invocation conversion (gosling_et_al_2000 <a * href="http://java.sun.com/docs/books/jls/second_edition/html/expressions.doc.html#18427">§15.12.2.1</a>). */ public static List<MethodT> methodsApplicableAndAccessible(final SymbolTable tab, final List<File> paths, final Type base, final boolean parents, final String name, final List<Type> actuals) { final List<MethodT> methods = parents ? methodsOwnAndInherited(tab, paths, base) : methodsOwn(base); final List<MethodT> result = new ArrayList<MethodT>(); for (final MethodT method : methods) if (isAccessibleIn(tab, paths, method, base)) if (isApplicableMethod(tab, paths, method, name, actuals)) result.add(method); return result; } public static List<MethodT> methodsInherited(final SymbolTable tab, final List<File> paths, final Type base, final boolean includeOverridden) { //gosling_et_al_2000 8.4.6 List<MethodT> result = new ArrayList<MethodT>(); final List<MethodT> methodsOwn = methodsOwn(base); final String baseScope = typeToScopeName(base); //declaringScopeName(base); assert null != baseScope || base.isArray(); final List<ClassOrInterfaceT> superTypes = directSuperTypes(tab, paths, base); for (final ClassOrInterfaceT superType : superTypes) { final List<MethodT> superMethods = methodsOwnAndInherited(tab, paths, superType); superMethods: for (final MethodT m : superMethods) { resolveIfAliasMethod(tab, paths, m); if (hasModifier(m, "private")) continue superMethods; if (null != baseScope && !isAccessibleFromIn(tab, paths, baseScope, m, superType)) continue superMethods; if (!includeOverridden) for (final MethodT o : methodsOwn) { resolveIfAliasMethod(tab, paths, o); if (sameMethodSignature(o, m)) continue superMethods; } boolean mustRemoveDuplicates = false; for (final MethodT r : result) { if (r == m) continue superMethods; //inherited from same interface by two different paths if (sameMethodSignature(m, r)) { assert hasModifier(m, "abstract") || hasModifier(r, "abstract"); if (hasModifier(m, "abstract") && hasModifier(r, "abstract")) { //TD 39 (8.4.6) report error if incompatible return types } else if (!hasModifier(m, "abstract")) { mustRemoveDuplicates = true; } else if (!hasModifier(r, "abstract")) { //TD 39 (8.4.6) report error if r is static continue superMethods; } } } result.add(m); if (mustRemoveDuplicates) { //TD 39 (8.4.6) report error if m is static final List<MethodT> oldResult = result; result = new ArrayList<MethodT>(); for (final MethodT r : oldResult) if (r == m || !sameMethodSignature(r, m)) result.add(r); } } } return result; } public static List<MethodT> methodsOwn(final Type base) { final List<MethodT> result = new ArrayList<MethodT>(); if (!base.isArray()) //TD should methodsOwn() list of array type be non-empty? for (final Type m : resolveToRawClassOrInterfaceT(base).getMethods()) result.add(m.toMethod()); return result; } public static List<MethodT> methodsOwnAndInherited(final SymbolTable tab, final List<File> paths, final Type base) { final List<MethodT> result = new ArrayList<MethodT>(); result.addAll(methodsInherited(tab, paths, base, false)); result.addAll(methodsOwn(base)); return result; } public static String methodSymbolFromAst(final GNode ast) { final String name = ast.getString(3); final String args = javaAstToString(ast.getGeneric(4)); /* In general, the arguments are unresolved, hence the scope name * is not canonical, hence it is useless for future lookup. That * is fine as long as we never look up methods or their scopes by * name. Instead, we should rely on the SCOPE attribute for that. */ return "method(" + name + ")" + args; } public static String methodSymbolFromConstructor(final Constructor method) { final StringBuilder result = new StringBuilder(); result.append("method(<init>)"); methodSymbolHelper(result, method.getParameterTypes()); return result.toString(); } public static String methodSymbolFromMethod(final Method method) { final StringBuilder result = new StringBuilder(); result.append("method(").append(method.getName()).append(")"); methodSymbolHelper(result, method.getParameterTypes()); return result.toString(); } private static void methodSymbolHelper(final StringBuilder b, final Class[] t) { b.append('('); for (int i = 0; i < t.length; i++) { if (0 < i) b.append(','); b.append(t[i].getName()); } b.append(')'); } public static String modifiersToString(final Type type) { final StringBuilder result = new StringBuilder(); for (final Attribute att : type.attributes()) { if (0 < result.length()) result.append(' '); result.append(modifierToName(att)); } return result.toString(); } public static String modifierToName(final Attribute modifier) { return MODIFIER_TO_NAME.get(modifier); } private static List<MethodT> mostSpecificMethods(final SymbolTable tab, final List<File> paths, final List<MethodT> allMethods) { final List<MethodT> result = new ArrayList<MethodT>(); outer: for (final MethodT mOuter : allMethods) { for (final MethodT mInner : allMethods) if (mOuter != mInner && isSuperMethod(tab, paths, mOuter, mInner)) continue outer; result.add(mOuter); } return result; } public static Type nameToBaseType(final String name) { return NAME_TO_BASETYPE.get(name); } public static Attribute nameToModifier(final String name) { final Attribute result = NAME_TO_MODIFIER.get(name); assert null != result; return result; } public static MethodT newRawConstructor(final Type base, final List<Type> formals, final List<Type> exceptions) { return new MethodT(constructorsReturnVoid() ? nameToBaseType("void") : base, typeToSimpleName(base), formals, false, exceptions); } public static Type notAValueIfClassOrInterface(final Type t) { if (null == t) return t; if (isWrappedClassT(t) || isWrappedInterfaceT(t)) { final Type result = new AnnotatedT(t); /* don't call t.attribute(), because that would pollute aliases of t */ return result.attribute(Constants.ATT_NOT_A_VALUE); } return t; } public static PackageT packageDotPackage(final SymbolTable tab, final PackageT base, final String selector) { final String tagName = SymbolTable.toTagName(selector); final String scopeName = packageNameToScopeName(base.getName()); final String symbol = "." + Utilities.qualify(scopeName, tagName); PackageT result = (PackageT) tab.lookup(symbol); if (result == null) { result = new PackageT(Utilities.qualify(base.getName(), selector)); SymbolTable.Scope scope = tab.lookupScope(symbol); if (scope == null) { final SymbolTable.Scope oldScope = tab.current(); tab.setScope(tab.root()); tab.enter(packageNameToScopeName(base.getName())); scope = tab.current(); tab.setScope(oldScope); } scope.define(tagName, result); } return result; } public static Type packageDotPackageOrType(final SymbolTable tab, final List<File> paths, final PackageT base, final String selector) { final Type t = packageDotType(tab, paths, base, selector); return t != null ? t : packageDotPackage(tab, base, selector); } static ClassOrInterfaceT packageDotType(final SymbolTable tab, final List<File> paths, final PackageT base, final String selector) { assert null != base; final ClassOrInterfaceT t1 = packageDotType_existing(tab, base, selector); if (null != t1) return t1; final ClassOrInterfaceT t2 = packageDotType_external(tab, paths, base, selector); if (null != t2) return t2; return packageDotType_noSource(tab, base, selector); } private static ClassOrInterfaceT packageDotType_existing(final SymbolTable tab, final PackageT base, final String selector) { final String tagName = SymbolTable.toTagName(selector); final String baseScopeName = "." + packageNameToScopeName(base.getName()); final SymbolTable.Scope scope = tab.getScope(baseScopeName); if (scope != null) for (final Iterator<String> i = scope.nested(); i.hasNext();) { final String symbol = baseScopeName + "." + i.next() + "." + tagName; final Type result = (Type) tab.lookup(symbol); if (result != null && !result.isAlias()) return (ClassOrInterfaceT) result; } return null; } private static ClassOrInterfaceT packageDotType_external(final SymbolTable tab, final List<File> paths, final PackageT base, final String selector) { final String tagName = SymbolTable.toTagName(selector); final String suffix = File.separatorChar + Utilities.toPath(base.getName()) + File.separatorChar + selector + ".java"; for (final File prefix : paths) { try { final File file = new File(prefix, suffix); final String path = file.getAbsolutePath(); if (file.exists()) { final String scopedName = scopeName(base, path) + "." + tagName; if (null == tab.lookupScope(scopedName)) { final String oldScope = enterScopeByQualifiedName(tab, ""); FileReader fileReader = null; try { fileReader = new FileReader(file); final UnicodeUnescaper unescaper = new UnicodeUnescaper(fileReader); final JavaParser parser = new JavaParser(unescaper, path); final Result parseResult = parser.pCompilationUnit(0); if (parseResult.hasValue()) { final GNode ast = (GNode) parseResult.semanticValue(); final GNode simple = (GNode) new JavaAstSimplifier().dispatch(ast); new JavaExternalAnalyzer(new Runtime(), tab).dispatch(simple); } } finally { if (null != fileReader) fileReader.close(); } enterScopeByQualifiedName(tab, oldScope); } final ClassOrInterfaceT result = (ClassOrInterfaceT) tab.lookup(scopedName); if (result != null) return result; } } catch (final IOException e) { e.printStackTrace(); } } return null; } static ClassOrInterfaceT packageDotType_noSource(final SymbolTable tab, final PackageT base, final String selector) { final String scopedName = "." + packageNameToScopeName(base.getName()) + ".file()." + SymbolTable.toTagName(selector); if (null == tab.lookup(scopedName)) { final Class clazz; try { clazz = Class.forName(base.getName() + "." + selector); } catch (final ClassNotFoundException e) { return null; } if (base.getName().equals(clazz.getPackage().getName())) { final String oldScope = enterScopeByQualifiedName(tab, ""); tab.enter(packageNameToScopeName(base.getName())); tab.enter("file()"); new JavaNoSourceAnalyzer(tab).visitClassOrInterface(clazz); enterScopeByQualifiedName(tab, oldScope); } } return (ClassOrInterfaceT) tab.lookup(scopedName); } public static String packageNameToScopeName(final String canonicalName) { return SymbolTable.toNameSpace(canonicalName, "package"); } public static String qNameWithDollars(final SymbolTable tab, final ClassOrInterfaceT t) { final String allDots = t.getQName(); if (null == tab) return allDots; final String packageName = declaringPackage(tab, t).getName(); if (0 == packageName.length()) return allDots; assert allDots.startsWith(packageName); final String afterPackage = allDots.substring(packageName.length() + 1).replace('.', '$'); final String result = packageName + "." + afterPackage; return result; } public static List<ClassOrInterfaceT> qualifiedAliasesToTypes(final SymbolTable tab, final List<File> paths, final String scope, final List<AliasT> aliases) { final List<ClassOrInterfaceT> result = new ArrayList<ClassOrInterfaceT>(aliases.size()); for (final AliasT a : aliases) { final ClassOrInterfaceT resolved = JavaEntities.qualifiedAliasToType(tab, paths, scope, a); if (null != resolved) result.add(resolved); } return result; } public static ClassOrInterfaceT qualifiedAliasToType(final SymbolTable tab, final List<File> paths, final String scope, final AliasT alias) { if (alias.getType() == null) alias.setType(qualifiedNameToType(tab, paths, scope, alias.getName())); return (ClassOrInterfaceT) alias.getType(); } public static Type qualifiedNameToPackageOrType(final SymbolTable tab, final List<File> paths, final String scope, final String name) { final Type t = qualifiedNameToType(tab, paths, scope, name); return t != null ? t : canonicalNameToPackage(tab, name); } public static ClassOrInterfaceT qualifiedNameToType(final SymbolTable tab, final List<File> paths, final String scope, final String name) { if (name.indexOf('.') == -1) return simpleNameToType(tab, paths, scope, name); final String qualifier = Utilities.getQualifier(name); final String selector = Utilities.unqualify(name); final Type base = qualifiedNameToPackageOrType(tab, paths, scope, qualifier); if (base.isPackage()) return packageDotType(tab, paths, base.toPackage(), selector); return typeDotType(tab, paths, (ClassOrInterfaceT)base, true, selector); } public static Type resolveIfAlias(final SymbolTable tab, final List<File> paths, final String scope, final Type typeThatMayBeAlias) { assert null != scope; if (null == typeThatMayBeAlias) return null; if (isGeneralLValueT(typeThatMayBeAlias)) { final Type t = dereference(typeThatMayBeAlias); resolveIfAlias(tab, paths, scope, t); return typeThatMayBeAlias; } if (typeThatMayBeAlias.isAnnotated()) { final Type t = ((AnnotatedT) typeThatMayBeAlias).getType(); resolveIfAlias(tab, paths, scope, t); return typeThatMayBeAlias; } if (typeThatMayBeAlias.isArray()) { final Type t = arrayElementType(typeThatMayBeAlias.toArray()); resolveIfAlias(tab, paths, scope, t); return typeThatMayBeAlias; } if (typeThatMayBeAlias.isAlias()) return qualifiedAliasToType(tab, paths, scope, typeThatMayBeAlias.toAlias()); return typeThatMayBeAlias; } public static MethodT resolveIfAliasMethod(final SymbolTable tab, final List<File> paths, final MethodT method) { final String scope = declaringScopeName(method); assert null != scope; resolveIfAlias(tab, paths, scope, declaringType(tab, method)); resolveIfAlias(tab, paths, scope, method.getResult()); for (final Type p : method.getParameters()) resolveIfAlias(tab, paths, scope, p); for (final Type e : method.getExceptions()) resolveIfAlias(tab, paths, scope, e); return method; } static ClassOrInterfaceT resolveToRawClassOrInterfaceT(final Type t) { return (ClassOrInterfaceT) resolveToRawRValue(t); } public static WrappedT resolveToRawLValue(final Type t) { assert isGeneralLValueT(t); final boolean a = t.isAnnotated(); final WrappedT r = (WrappedT) (a ? ((AnnotatedT) t).getType() : t); assert isLValueT(r) || isLocalT(r) || isFieldT(r) || isParameterT(r); return r; } public static Type resolveToRawRValue(final Type t) { assert isGeneralRValueT(t); Type result = t; if (!isNullT(result)) while (true) { if (result.isAlias()) result = ((AliasT) result).getType(); else if (result.isAnnotated()) result = ((AnnotatedT) result).getType(); else if (isConstantT(result)) result = ((AnnotatedT) result).getType(); else break; } assert isNullT(result) || isPrimitiveT(result) || result.isArray() || result.isClass() || result.isInterface(); return result; } public static Type resolveToRValue(final Type t0) { assert isGeneralRValueT(t0); final Type t1 = isConstantT(t0) ? ((AnnotatedT)t0).getType() : t0; final Type t2 = t1.isAlias() ? ((AliasT)t1).getType() : t1; assert isPrimitiveT(t2) || t2.isArray() || t2.isClass() || t2.isInterface(); return t2; } public static Type resolveToValue(final AnnotatedT notAValue) { assert isNotAValueT(notAValue); Type result = notAValue.getType(); for (final Attribute a : notAValue.attributes()) { if (!a.equals(Constants.ATT_NOT_A_VALUE)) result = result.attribute(a); } if (notAValue.hasScope(false)) result.scope(notAValue.getScope()); return result; } public static boolean runtimeAssrt(final Runtime runtime, final Node n, final boolean cond, final String msgFormat, final Object... msgArgs) { if (!cond) { final Formatter message = new Formatter(); message.format(msgFormat, msgArgs); runtime.error(message.toString(), n); } return cond; } public static boolean sameMethodReturnType(final MethodT m1, final MethodT m2) { final Type r1 = m1.getResult(), r2 = m2.getResult(); if (r1.isVoid() || r2.isVoid()) return r1.isVoid() && r2.isVoid(); return JavaTypeConverter.isIdentical(r1, r2); } public static boolean sameMethodSignature(final MethodT m1, final MethodT m2) { if (!m1.getName().equals(m2.getName())) return false; final List<Type> params1 = m1.getParameters(), params2 = m2.getParameters(); if (params1.size() != params2.size()) return false; final Iterator<Type> i1 = params1.iterator(), i2 = params2.iterator(); while (i1.hasNext()) { final Type t1 = i1.next(), t2 = i2.next(); assert isParameterT(t1) && isParameterT(t2); final Type r1 = dereference(t1), r2 = dereference(t2); if (!JavaTypeConverter.isIdentical(r1, r2)) return false; } return true; } static String scopeName(final PackageT packageT, final String fileName) { return "." + packageNameToScopeName(packageT.getName()) + "." + fileNameToScopeName(fileName); } public static GNode scrubLocations(final GNode result) { new MiniVisitor_scrubLocations().dispatch(result); return result; } public static Type simpleNameToExpression(final SymbolTable tab, final List<File> paths, final String scope, final String name) { final String oldScope = enterScopeByQualifiedName(tab, scope); final Type result = (Type) tab.lookup(name); resolveIfAlias(tab, paths, scope, result); final boolean found = null != result && isGeneralLValueT(result); if (found) { final Type rValue = dereference(result); resolveIfAlias(tab, paths, typeToScopeName(currentType(tab)), rValue); } enterScopeByQualifiedName(tab, oldScope); if (!found && isScopeNested(tab.current().getQualifiedName())) { final Type c = currentType(tab); if (null != c) return typeDotField(tab, paths, c, true, name); } return result; } public static PackageT simpleNameToPackage(final SymbolTable tab, final String name) { final String tagName = SymbolTable.toTagName(name); final String toplevelScopeDotSymbol = "." + tagName; PackageT result = (PackageT) tab.lookup(toplevelScopeDotSymbol); if (result == null) { result = new PackageT(name); tab.lookupScope(toplevelScopeDotSymbol).define(tagName, result); } return result; } public static Type simpleNameToPackageOrType(final SymbolTable tab, final List<File> paths, final String scope, final String name) { final Type t = simpleNameToType(tab, paths, scope, name); return t != null ? t : simpleNameToPackage(tab, name); } public static Type simpleNameToPackageOrTypeOrExpression( final SymbolTable tab, final List<File> paths, final String scope, final String name) { final Type e = simpleNameToExpression(tab, paths, scope, name); return e != null ? e : simpleNameToPackageOrType(tab, paths, scope, name); } public static ClassOrInterfaceT simpleNameToType(final SymbolTable tab, final List<File> paths, final String scope, final String name) { final String oldScope = enterScopeByQualifiedName(tab, scope); ClassOrInterfaceT result = null; { // find as local class, member class, single-import, or top-level class final Type t = (Type) tab.lookup(SymbolTable.toTagName(name)); if (null != t && !t.isPackage()) result = t.isAlias() ? lookupImport(tab, paths, t.toAlias()) : (ClassOrInterfaceT)t; } if (null == result && isScopeNested(scope)) { final ClassOrInterfaceT c = currentType(tab); if (null != c) result = typeDotType(tab, paths, c, true, name); } if (null == result) // find in other compilation unit of current package result = packageDotType(tab, paths, currentPackage(tab), name); if (null == result) { final List<Type> imports = currentImports(tab); if (null != imports) // find in import-on-demand for (final Type i : imports) { result = packageDotType(tab, paths, (PackageT) i, name); if (null != result) break; } } if (null == result) result = packageDotType(tab, paths, canonicalNameToPackage(tab, "java.lang"), name); enterScopeByQualifiedName(tab, oldScope); if (null == result) return null; return result; } @SuppressWarnings("unchecked") public static Set<String> stringSet(final Object s) { return (Set<String>)s; } public static ClassT tClass(final SymbolTable tab) { return tForName(tab, "java.lang.Class").toClass(); } public static InterfaceT tCloneable(final SymbolTable tab) { return tForName(tab, "java.lang.Cloneable").toInterface(); } public static ClassT tError(final SymbolTable tab) { return tForName(tab, "java.lang.Error").toClass(); } private static ClassOrInterfaceT tForName(final SymbolTable tab, final String name) { final List<File> paths = new ArrayList<File>(); return canonicalNameToType(tab, paths, name, false); } public static ClassT tObject(final SymbolTable tab) { return tForName(tab, "java.lang.Object").toClass(); } public static AliasT tObjectAlias(final SymbolTable tab) { return new AliasT("java.lang.Object", tObject(tab)); } public static ClassT tRuntimeException(final SymbolTable tab) { return tForName(tab, "java.lang.RuntimeException").toClass(); } public static InterfaceT tSerializable(final SymbolTable tab) { return tForName(tab, "java.io.Serializable").toInterface(); } public static ClassT tString(final SymbolTable tab) { return tForName(tab, "java.lang.String").toClass(); } public static ClassT tThrowable(final SymbolTable tab) { return tForName(tab, "java.lang.Throwable").toClass(); } public static String typeDeclString(final SymbolTable tab, final Object obj) { assert obj instanceof Type || obj instanceof List; if (obj instanceof Type) return typeToString(tab, true, (Type)obj); final StringBuilder result = new StringBuilder(); for (final Type t : typeList((List)obj)) result.append(typeToString(tab, true, t)).append(';'); return result.toString(); } public static VariableT typeDotField(final SymbolTable tab, final List<File> paths, final Type base, final boolean parents, final String name) { final List<VariableT> all = fieldsApplicableAndAccessible(tab, paths, base, parents, name); if (1 == all.size()) return all.get(0); return null; } public static MethodT typeDotMethod(final SymbolTable tab, final List<File> paths, final Type baseT, final boolean parents, final String selector, final List<Type> actuals) { final List<MethodT> all = methodsApplicableAndAccessible(tab, paths, baseT, parents, selector, actuals); // chose the most specific method: see gosling_et_al_2000 Section 15.12.2.2. final List<MethodT> specific = mostSpecificMethods(tab, paths, all); final MethodT result; if (specific.size() == 1) { result = specific.get(0); } else { int nonAbstract = 0; MethodT any = null; for (final MethodT m : specific) { if (any == null) any = m; assert sameMethodSignature(m, any); if (!hasModifier(m, "abstract")) { any = m; nonAbstract++; } } assert nonAbstract <= 1; result = any; } if (null == result) return null; resolveIfAlias(tab, paths, typeToScopeName(baseT), result.getResult()); return result; } public static ClassOrInterfaceT typeDotType(final SymbolTable tab, final List<File> paths, final ClassOrInterfaceT base, final boolean parents, final String selector) { if (recursiveTypeDotType) return null; recursiveTypeDotType = true; final List<Type> all = memberTypesApplicableAndAccessible(tab, paths, base, parents, selector); recursiveTypeDotType = false; if (1 == all.size()) return (ClassOrInterfaceT) all.get(0); return null; } public static Type typeDotTypeOrField(final SymbolTable tab, final List<File> paths, final Type base, final boolean parents, final String selector) { assert isWrappedClassT(base) || isWrappedInterfaceT(base) || base.isArray(); if (parents) { for (final Iterator<Type> i = new SuperTypesIter(tab, paths, base); i.hasNext();) { final Type member = typeDotTypeOrField(tab, paths, i.next(), false, selector); if (null != member) return member; } return null; } final VariableT field = typeDotField(tab, paths, base, false, selector); if (null != field) return field; return typeDotType(tab, paths, (ClassOrInterfaceT)base, false, selector); } @SuppressWarnings("unchecked") public static List<Type> typeList(final List list) { return list; } /** A descriptor is a JVM-internal string representation of a type of a field or method, see JVM * specification <a href="http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html#1169">§4.3</a>. */ public static String typeToDescriptor(final SymbolTable tab, final Type type) { if (isConstantT(type)) return typeToDescriptor(tab, type.toAnnotated().getType()); if (type.isBoolean()) return "Z"; if (type.isNumber()) { switch (((NumberT)type).getKind()) { case BYTE: return "B"; case CHAR: return "C"; case DOUBLE: return "D"; case FLOAT: return "F"; case INT: return "I"; case LONG: return "J"; case SHORT: return "S"; } } if (type.isVoid()) return "V"; if (type.isArray()) return "[" + typeToDescriptor(tab, arrayElementType(type.toArray())); if (isWrappedClassT(type) || isWrappedInterfaceT(type)) return "L" + qNameWithDollars(tab, resolveToRawClassOrInterfaceT(type)).replace('.', '/') + ";"; if (type.isMethod()) { final StringBuilder b = new StringBuilder(); b.append('('); for (final Type param : type.toMethod().getParameters()) b.append(typeToDescriptor(tab, dereference(param))); b.append(')').append(typeToDescriptor(tab, type.toMethod().getResult())); return b.toString(); } throw new Error(); } static String typeToScopeName(final Type type) { if (null == type || type.isArray()) return ""; final String result = type.getScope(); assert null != result; return result; } public static String typeToSimpleName(final Type type) { if (type.isAlias()) return Utilities.unqualify(type.toAlias().getName()); if (isWrappedClassT(type) || isWrappedInterfaceT(type)) return resolveToRawClassOrInterfaceT(type).getName(); assert type.isMethod(); return type.toMethod().getName(); } public static String typeToString(final SymbolTable tab, final boolean showDetails, final Type type) { final CharArrayWriter writer = new CharArrayWriter(); final Printer printer = new Printer(writer); final TypePrinter typePrinter = new JavaTypePrinter(tab, showDetails, printer); typePrinter.dispatch(type); printer.flush(); return writer.toString(); } public static Type typeWithDimensions(final Type componentT, final int dim) { if (dim == 0) return componentT; final Type rec = typeWithDimensions(componentT, dim - 1); // FIXME: plugged in faux reference with faux type. final Type rec2 = rec.annotate().shape(new DynamicReference(NumberT.INT)); return new ArrayT(rec2, true); } public static String unicodeUnescape(final String in) { if (!in.contains("\\u")) return in; final UnicodeUnescaper uu = new UnicodeUnescaper(new StringReader(in)); final StringBuilder result = new StringBuilder(in.length()); try { while (true) { final int c = uu.read(); if (-1 == c) break; result.append((char) c); } } catch (final IOException e) { throw new Error("internal error", e); } return result.toString(); } public static boolean zeroLiteral(final String s) { for (int i=0; i<s.length(); i++) { final char c = s.charAt(i); if ('1' <= c && c <= '9') return false; if ('d' == c || 'D' == c || 'e' == c || 'E' == c || 'f' == c || 'F' == c) return true; } return true; } }