/* * xtc - The eXTensible Compiler * Copyright (C) 2006-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.util.ArrayList; import java.util.Iterator; import java.util.List; import xtc.Constants; import xtc.tree.Attribute; import xtc.tree.GNode; import xtc.tree.Node; import xtc.tree.Visitor; import xtc.type.AliasT; import xtc.type.ClassOrInterfaceT; import xtc.type.ClassT; import xtc.type.InterfaceT; import xtc.type.Language; import xtc.type.MethodT; import xtc.type.PackageT; import xtc.type.Type; import xtc.type.VariableT; import xtc.util.Runtime; import xtc.util.SymbolTable; import xtc.util.Utilities; /** * A visitor that constructs externally visible types and fills the symbol table * for a Java file. Does not descend into method bodies, just finds the class, * interface, method, and field signatures. Used in conjunction with * JavaAnalyzer. Let F be the file currently analyzed by the JavaAnalyzer; then * the JavaExternalAnalyzer is used on (i) classes defined in F, and (ii) * compilation units that define classes whose name gets used in F. Packages, * classes, and interfaces go into namespace "tag(..)", methods go into * namespace "method(..)", and fields go into the default namespace. The scopes * associated with packages and types do not have the namespace in their name, * only the symbols do. For example, scope "java" contains scope "lang", but * scope "java" maps symbol "tag(lang)" to an instance of PackageT. Declared * types are represented by instances of ClassT or InterfaceT, whereas names * used, for example, as method return types are represented by instances of * AliasT. * * @author Martin Hirzel * @version $Revision: 1.71 $ */ public class JavaExternalAnalyzer extends Visitor { protected static List<Attribute> MODIFIERS_CLASS = new ArrayList<Attribute>(); protected static List<Attribute> MODIFIERS_CONSTRUCTOR = new ArrayList<Attribute>(); protected static List<Attribute> MODIFIERS_FIELD = new ArrayList<Attribute>(); protected static List<Attribute> MODIFIERS_INTERFACE = new ArrayList<Attribute>(); protected static List<Attribute> MODIFIERS_INTERFACE_FIELD = new ArrayList<Attribute>(); protected static List<Attribute> MODIFIERS_INTERFACE_MEMBERTYPE = new ArrayList<Attribute>(); protected static List<Attribute> MODIFIERS_INTERFACE_METHOD = new ArrayList<Attribute>(); protected static List<Attribute> MODIFIERS_METHOD = new ArrayList<Attribute>(); static { // gosling_et_al_2000 8.1.1 MODIFIERS_CLASS.add(JavaEntities.nameToModifier("public")); MODIFIERS_CLASS.add(JavaEntities.nameToModifier("protected")); MODIFIERS_CLASS.add(JavaEntities.nameToModifier("private")); MODIFIERS_CLASS.add(JavaEntities.nameToModifier("abstract")); MODIFIERS_CLASS.add(JavaEntities.nameToModifier("static")); MODIFIERS_CLASS.add(JavaEntities.nameToModifier("final")); MODIFIERS_CLASS.add(JavaEntities.nameToModifier("strictfp")); // gosling_et_al_2000 8.3.1 MODIFIERS_FIELD.add(JavaEntities.nameToModifier("public")); MODIFIERS_FIELD.add(JavaEntities.nameToModifier("protected")); MODIFIERS_FIELD.add(JavaEntities.nameToModifier("private")); MODIFIERS_FIELD.add(JavaEntities.nameToModifier("static")); MODIFIERS_FIELD.add(JavaEntities.nameToModifier("final")); MODIFIERS_FIELD.add(JavaEntities.nameToModifier("transient")); MODIFIERS_FIELD.add(JavaEntities.nameToModifier("volatile")); // gosling_et_al_2000 8.8 MODIFIERS_CONSTRUCTOR.add(JavaEntities.nameToModifier("public")); MODIFIERS_CONSTRUCTOR.add(JavaEntities.nameToModifier("protected")); MODIFIERS_CONSTRUCTOR.add(JavaEntities.nameToModifier("private")); // gosling_et_al_2000 9.1.1 MODIFIERS_INTERFACE.add(JavaEntities.nameToModifier("public")); MODIFIERS_INTERFACE.add(JavaEntities.nameToModifier("protected")); MODIFIERS_INTERFACE.add(JavaEntities.nameToModifier("private")); MODIFIERS_INTERFACE.add(JavaEntities.nameToModifier("abstract")); MODIFIERS_INTERFACE.add(JavaEntities.nameToModifier("static")); MODIFIERS_INTERFACE.add(JavaEntities.nameToModifier("strictfp")); // gosling_et_al_2000 9.5 MODIFIERS_INTERFACE_FIELD.add(JavaEntities.nameToModifier("public")); MODIFIERS_INTERFACE_FIELD.add(JavaEntities.nameToModifier("static")); MODIFIERS_INTERFACE_FIELD.add(JavaEntities.nameToModifier("final")); // gosling_et_al_2000 9.5 MODIFIERS_INTERFACE_MEMBERTYPE.add(JavaEntities.nameToModifier("static")); MODIFIERS_INTERFACE_MEMBERTYPE.add(JavaEntities.nameToModifier("public")); // gosling_et_al_2000 9.4 MODIFIERS_INTERFACE_METHOD.add(JavaEntities.nameToModifier("public")); MODIFIERS_INTERFACE_METHOD.add(JavaEntities.nameToModifier("abstract")); // gosling_et_al_2000 8.4.3 MODIFIERS_METHOD.add(JavaEntities.nameToModifier("public")); MODIFIERS_METHOD.add(JavaEntities.nameToModifier("protected")); MODIFIERS_METHOD.add(JavaEntities.nameToModifier("private")); MODIFIERS_METHOD.add(JavaEntities.nameToModifier("abstract")); MODIFIERS_METHOD.add(JavaEntities.nameToModifier("static")); MODIFIERS_METHOD.add(JavaEntities.nameToModifier("final")); MODIFIERS_METHOD.add(JavaEntities.nameToModifier("synchronized")); MODIFIERS_METHOD.add(JavaEntities.nameToModifier("native")); MODIFIERS_METHOD.add(JavaEntities.nameToModifier("strictfp")); } protected static void addModifier(final List<Attribute> modifiers, final String name) { if (!hasModifier(modifiers, name)) modifiers.add(JavaEntities.nameToModifier(name)); } protected static void addModifiers(final List<Attribute> modifiers, final List<Attribute> toAdd) { for (final Attribute attribute : toAdd) if (!modifiers.contains(attribute)) modifiers.add(attribute); } public static int countDimensions(final GNode dimNode) { return null == dimNode ? 0 : dimNode.size(); } protected static boolean hasModifier(final List<Attribute> modifiers, final String modifier) { return modifiers.contains(JavaEntities.nameToModifier(modifier)); } protected final Runtime _runtime; public final SymbolTable _table; public JavaExternalAnalyzer(final Runtime runtime, final SymbolTable table) { _runtime = runtime; _table = table; } /** Use this for asserting that the input is typed correctly. */ protected boolean assrt(final GNode n, final boolean cond, final String msgFormat, final Object... msgArgs) { return JavaEntities.runtimeAssrt(_runtime, n, cond, msgFormat, msgArgs); } protected void assrtDiffersFromEnclosing(final GNode n, final String canonicalName) { final String simpleName = Utilities.getName(canonicalName); final String packageName = JavaEntities.currentPackage(_table).getName(); final String nameWithoutPackage = 0 == packageName.length() ? canonicalName : canonicalName.substring(packageName.length() + 1); final String middleName = Utilities.getQualifier(nameWithoutPackage); if (null != middleName) { final String[] c = Utilities.toComponents(middleName); for (int i = 0; i < c.length; i++) assrt(n, !c[i].equals(simpleName), "name must not match enclosing type"); } } protected void assrtLegalModifiers(final GNode n, final List<Attribute> expected, final List<Attribute> actual, final String context) { final boolean pri = hasModifier(actual, "private"); final boolean pro = hasModifier(actual, "protected"); final boolean pub = hasModifier(actual, "public"); assrt(n, !(pri && pro), "conflicting modifiers private and protected"); assrt(n, !(pri && pub), "conflicting modifiers private and public"); assrt(n, !(pro && pub), "conflicting modifiers protected and public"); for (final Attribute m : actual) assrt(n, expected.contains(m), "illegal %s modifier %s", context, m); final boolean fin = hasModifier(actual, "final"); final boolean vol = hasModifier(actual, "volatile"); assrt(n, !(fin && vol), "conflicting modifiers final and volatile"); } protected final String currentScopeName() { return _table.current().getQualifiedName(); } protected final Type declareDefaultConstructorIfNecessary() { // gosling_et_al_2000 8.8.7 default constructor final Type wrappedBase = JavaEntities.currentType(_table); final ClassOrInterfaceT base = JavaEntities.resolveToRawClassOrInterfaceT(wrappedBase); for (final Type i : base.getMethods()) if (JavaEntities.isConstructor(base, i.toMethod())) return null; final MethodT result = JavaEntities.newRawConstructor(base, new ArrayList<Type>(), new ArrayList<Type>()); final List<Attribute> modifiers = new ArrayList<Attribute>(); if (JavaEntities.hasModifier(wrappedBase, "private")) modifiers.add(JavaEntities.nameToModifier("private")); else if (JavaEntities.hasModifier(wrappedBase, "protected")) modifiers.add(JavaEntities.nameToModifier("protected")); else if (JavaEntities.hasModifier(wrappedBase, "public")) modifiers.add(JavaEntities.nameToModifier("public")); for (final Attribute mod : modifiers) result.addAttribute(mod); final String symbol = "method(<init>)()"; _table.current().define(symbol, result); _table.enter(symbol); result.scope(_table.current().getQualifiedName()); _table.exit(); assert JavaEntities.isConstructor(base, result); JavaEntities.currentType(_table).getMethods().add(result); return result; } protected final List<Type> makeList(final GNode n) { final List<Type> result = new ArrayList<Type>(n.size()); for (final Object o : n) result.add((Type)dispatch((Node)o)); return result; } protected final boolean memberOfInterface() { if (!JavaEntities.isScopeForMember(currentScopeName())) return false; final Type t = JavaEntities.currentType(_table); return null != t && JavaEntities.isWrappedInterfaceT(t); } public final List<Type> processDeclarators(final List<Attribute> modifiers, final Type type, final GNode declarators) { assert JavaEntities.isRValueT(type); final List<Type> result = new ArrayList<Type>(); final boolean isLocal = JavaEntities.isScopeLocal(currentScopeName()); for (final Object i : declarators) { final GNode declNode = (GNode) i; final String name = declNode.getString(0); final Type dimType = JavaEntities.typeWithDimensions(type, countDimensions(declNode.getGeneric(1))); final Type entity = isLocal ? VariableT.newLocal(dimType, name) : VariableT.newField(dimType, name); for (final Attribute mod : modifiers) entity.addAttribute(mod); entity.language(Language.JAVA); assert isLocal ? JavaEntities.isLocalT(entity) : JavaEntities.isFieldT(entity); if (null == _table.current().lookupLocally(name)) { result.add(entity); _table.current().define(name, entity); entity.scope(_table.current().getQualifiedName()); if (!isLocal) JavaEntities.currentType(_table).getFields().add(entity); } else { if (isLocal) _runtime.error("duplicate variable declaration " + name, declNode); else _runtime.error("duplicate field declaration " + name, declNode); } declNode.setProperty(Constants.TYPE, entity); } return result; } /** Visit a BlockDeclaration = ["static"] Block (gosling_et_al_2000 <a * href="http://java.sun.com/docs/books/jls/second_edition/html/classes.doc.html#246032">§8.6</a>, * <a * href="http://java.sun.com/docs/books/jls/second_edition/html/classes.doc.html#39245">§8.7</a>). */ public final void visitBlockDeclaration(final GNode n) { assert 2 == n.size(); } /** * Visit a ClassBody = Declaration* (gosling_et_al_2000 <a * href="http://java.sun.com/docs/books/jls/second_edition/html/classes.doc.html#18988">§8.1.5</a>, * <a * href="http://java.sun.com/docs/books/jls/second_edition/html/interfaces.doc.html#236431">§9.1.3</a>, * <a * href="http://java.sun.com/docs/books/jls/second_edition/html/expressions.doc.html#41147">§15.9</a>). */ public final void visitClassBody(final GNode n) { for (final Object o : n) dispatch((Node)o); } /** * Visit a ClassDeclaration = Modifiers Identifier null [Extension] * [Implementation] ClassBody (gosling_et_al_2000 <a * href="http://java.sun.com/docs/books/jls/second_edition/html/classes.doc.html#15372">§8.1</a>, * <a * href="http://java.sun.com/docs/books/jls/second_edition/html/statements.doc.html#247766">§14.3</a>). */ public final ClassT visitClassDeclaration(final GNode n) { @SuppressWarnings("unchecked") final List<Attribute> modifiers = (List<Attribute>) dispatch(n.getNode(0)); final boolean isMember = JavaEntities.isScopeForMember(currentScopeName()); if (isMember) { final ClassOrInterfaceT declaringType = JavaEntities.currentType(_table); if (JavaEntities.isWrappedInterfaceT(declaringType)) addModifiers(modifiers, MODIFIERS_INTERFACE_MEMBERTYPE); if (JavaEntities.hasModifier(declaringType, "strictfp")) addModifier(modifiers, "strictfp"); if (hasModifier(modifiers, "static")) assrt(n, JavaEntities.hasModifier(declaringType, "static") || JavaEntities.isTypeTopLevel(declaringType), "illegal context for static member"); } assrtLegalModifiers(n.getGeneric(0), MODIFIERS_CLASS, modifiers, "class"); if (hasModifier(modifiers, "public")) assrt(n, isMember || JavaEntities.isScopeTopLevel(currentScopeName()), "public class must be member or top-level"); if (hasModifier(modifiers, "protected") || hasModifier(modifiers, "private")) assrt(n, isMember && !memberOfInterface(), "private or protected class must be member of class"); if (hasModifier(modifiers, "static")) assrt(n, isMember, "static class must be member"); assrt(n, !hasModifier(modifiers, "final") || !hasModifier(modifiers, "abstract"), "can not be both abstract and final"); final String simpleName = n.getString(1); final String canonicalName = JavaEntities.canonicalName(_table, simpleName); assrtDiffersFromEnclosing(n, canonicalName); final List<Type> extension = JavaEntities.typeList((List) dispatch(n.getNode(3))); final Type parent; if (null == extension) { parent = JavaEntities.tObjectAlias(_table); } else { assrt(n, 1 == extension.size(), "can only extend one class"); assrt(n, !"java.lang.Object".equals(canonicalName), "Object can not have an extends clause"); parent = extension.get(0); } assrt(n.getGeneric(0), JavaEntities.isWrappedClassT(parent), "class can only extend class"); final List<Type> interfaces = n.get(4) == null ? new ArrayList<Type>() : JavaEntities.typeList((List) dispatch(n.getNode(4))); if ("java.lang.Object".equals(canonicalName)) assrt(n, 0 == interfaces.size(), "Object can not have an implements clause"); for (final Type i : interfaces) assrt(n.getGeneric(4), JavaEntities.isWrappedInterfaceT(i), "class can only implement interface"); final ClassT result = new ClassT(canonicalName, parent, interfaces, new ArrayList<Type>(), new ArrayList<Type>()); for (final Attribute mod : modifiers) result.addAttribute(mod); final String tagName = SymbolTable.toTagName(simpleName); assert null == _table.current().lookupLocally(tagName); _table.current().define(tagName, result); _table.enter(simpleName); result.scope(_table.current().getQualifiedName()); dispatch(n.getNode(5)); declareDefaultConstructorIfNecessary(); _table.exit(); assert result.isClass(); return result; } /** * Visit a CompilationUnit = [PackageDeclaration] ImportDeclaration* * Declaration* (gosling_et_al_2000 <a * href="http://java.sun.com/docs/books/jls/second_edition/html/packages.doc.html#40031">§7.3</a>). */ public final void visitCompilationUnit(final GNode n) { if (null == n.get(0)) visitPackageDeclaration(null); else dispatch(n.getNode(0)); _table.enter(JavaEntities.fileNameToScopeName(n.getLocation().file)); for (int i = 1; i < n.size(); i++) { final GNode declNode = n.getGeneric(i); assrt(n, declNode.hasName("ImportDeclaration") || declNode.hasName("ClassDeclaration") || declNode.hasName("InterfaceDeclaration") || declNode.hasName("EmptyDeclaration"), "unexpected top-level %s", declNode.getName()); dispatch(declNode); } _table.setScope(_table.root()); } /** * Visit a EmptyDeclaration = (no children) (gosling_et_al_2000 <a * href="http://java.sun.com/docs/books/jls/second_edition/html/statements.doc.html#5970">§14.6</a>). */ public final void visitEmptyDeclaration(final GNode n) { assert 0 == n.size(); } /** Visit an Extension = Type+. */ public final List<Type> visitExtension(final GNode n) { // gosling_et_al_2000 8.1.3, 9.1.2 final List<Type> result = makeList(n); for (final Type t : result) assrt(n, JavaEntities.isWrappedClassT(t) || JavaEntities.isWrappedInterfaceT(t), "supertype must be class or interface"); return result; } /** * Visit a FieldDeclaration = Modifiers Type Declarators. * Also descends into Declarators = Declarator+ and into * Declarator = Identifier [Dimensions] [VariableInitializer] * to get all the fields declared by this node. */ public final List<Type> visitFieldDeclaration(final GNode n) { // gosling_et_al_2000 9.3 @SuppressWarnings("unchecked") final List<Attribute> modifiers = (List<Attribute>) dispatch(n.getNode(0)); if (memberOfInterface()) { addModifiers(modifiers, MODIFIERS_INTERFACE_FIELD); assrtLegalModifiers(n, MODIFIERS_INTERFACE_FIELD, modifiers, "interface field"); } else { assrtLegalModifiers(n, MODIFIERS_FIELD, modifiers, "field"); } final Type type = (Type) dispatch(n.getNode(1)); assrt(n.getGeneric(1), JavaEntities.isRValueT(type), "illegal type for field"); return processDeclarators(modifiers, type, n.getGeneric(2)); } /** Visit a FormalParameter = [Modifier] Type null Identifier [Dimensions]. */ public final Type visitFormalParameter(final GNode n) { // gosling_et_al_2000 8.4.1 assert null == n.get(4) : "must run JavaAstSimplifier first"; final String id = n.getString(3); final Type result = VariableT.newParam((Type) dispatch(n.getNode(1)), id); result.language(Language.JAVA); if (n.getGeneric(0).size() != 0) result.addAttribute(JavaEntities.nameToModifier("final")); if (null == _table.current().lookupLocally(id)) { _table.current().define(id, result); result.scope(_table.current().getQualifiedName()); } else _runtime.error("duplicate parameter declaration " + id, n); assert JavaEntities.isParameterT(result); return result; } /** Visit a FormalParameters = FormalParameter*. */ public final List<Type> visitFormalParameters(final GNode n) { // gosling_et_al_2000 8.4.1 return makeList(n); } /** Visit an Implementation = Type+. */ public final List<Type> visitImplementation(final GNode n) { // gosling_et_al_2000 8.1.4 final List<Type> result = makeList(n); for (final Iterator iN = n.iterator(), iT = result.iterator(); iT.hasNext();) assrt((GNode) iN.next(), JavaEntities.isWrappedInterfaceT((Type) iT .next()), "supertype must be class or interface"); return result; } /** Visit an ImportDeclaration = QualifiedIdentifier ["*"]. */ public final void visitImportDeclaration(final GNode n) { // gosling_et_al_2000 7.5 final String canonicalName = (String) dispatch(n.getNode(1)); if (n.get(2) == null) { // defer resolution to JavaAnalyzer.visitPrimaryIdentifier() final AliasT t = new AliasT(canonicalName); final String simpleName = Utilities.unqualify(canonicalName); // TD 03 (7.5) ImportDeclaration = QualifiedIdentifier ["*"] (check whether the name clashes with some existing package) assrt(n, JavaEntities.isWrappedClassT(t) || JavaEntities.isWrappedInterfaceT(t), "import must be class or interface"); _table.current().define(SymbolTable.toTagName(simpleName), t); } else { final PackageT t = JavaEntities.canonicalNameToPackage(_table, canonicalName); _table.current().addDefinition("imports(*)", t); } } /** Visit a InterfaceDeclaration = Modifiers Identifier null [Extension] ClassBody. */ public final InterfaceT visitInterfaceDeclaration(final GNode n) { // gosling_et_al_2000 9.1 @SuppressWarnings("unchecked") final List<Attribute> modifiers = (List<Attribute>) dispatch(n.getNode(0)); addModifier(modifiers, "abstract"); final boolean isMember = JavaEntities.isScopeForMember(currentScopeName()); if (isMember) { final ClassOrInterfaceT declaringType = JavaEntities.currentType(_table); if (declaringType.isInterface()) addModifiers(modifiers, MODIFIERS_INTERFACE_MEMBERTYPE); if (JavaEntities.hasModifier(declaringType, "strictfp")) addModifier(modifiers, "strictfp"); addModifier(modifiers, "static"); assrt(n, JavaEntities.hasModifier(declaringType, "static") || JavaEntities.isTypeTopLevel(declaringType), "illegal context for static member"); } if (hasModifier(modifiers, "protected") || hasModifier(modifiers, "private")) assrt(n, isMember, "private or protected interface must be member"); if (hasModifier(modifiers, "static")) assrt(n, isMember, "static interface must be member"); assrtLegalModifiers(n, MODIFIERS_INTERFACE, modifiers, "interface"); final String simpleName = n.getString(1); final String canonicalName = JavaEntities.canonicalName(_table, simpleName); assrtDiffersFromEnclosing(n, canonicalName); final List<Type> interfaces = null == n.get(3) ? new ArrayList<Type>() : JavaEntities.typeList((List) dispatch(n.getNode(3))); final InterfaceT result = new InterfaceT(canonicalName, interfaces, new ArrayList<Type>(), new ArrayList<Type>()); for (final Attribute mod : modifiers) result.addAttribute(mod); final String tagName = SymbolTable.toTagName(simpleName); if (null == _table.current().lookupLocally(tagName)) { _table.current().define(tagName, result); _table.enter(simpleName); result.scope(_table.current().getQualifiedName()); final GNode bodyNode = n.getGeneric(4); for (int i = 0; i < bodyNode.size(); i++) { final GNode memberNode = bodyNode.getGeneric(i); assrt(n, memberNode.hasName("EmptyDeclaration") || memberNode.hasName("FieldDeclaration") || memberNode.hasName("MethodDeclaration") || memberNode.hasName("ClassDeclaration") || memberNode.hasName("InterfaceDeclaration"), "illegal interface member"); dispatch(memberNode); } _table.exit(); if (JavaEntities.isTypeNested(result)) assrt(n, !JavaEntities.isTypeInner(JavaEntities.declaringType(_table, result)), "inner classes may not declare member interfaces"); } else { _runtime.error("duplicate declaration " + canonicalName, n); } assert result.isInterface(); return result; } /** * Visit a MethodDeclaration = Modifiers null Type Identifier FormalParameters [Dimensions] * [ThrowsClause] [Block]. */ public final Type visitMethodDeclaration(final GNode n) { // gosling_et_al_2000 8.4, 8.8, 9.4 assert null == n.get(5) : "must run JavaAstSimplifier first"; @SuppressWarnings("unchecked") final List<Attribute> modifiers = (List<Attribute>) dispatch(n.getNode(0)); final String name = n.getString(3); final ClassOrInterfaceT base = JavaEntities.currentType(_table); final boolean isConstructor = JavaEntities.typeToSimpleName(base).equals(name); final Type returnType = isConstructor ? (JavaEntities.constructorsReturnVoid() ? JavaEntities.nameToBaseType("void") : base) : (Type) dispatch(n.getNode(2)); if (!isConstructor) { assrt(n, null != n.get(2), "missing return type"); if (JavaEntities.hasModifier(base, "strictfp")) addModifier(modifiers, "strictfp"); if (JavaEntities.hasModifier(base, "final")) addModifier(modifiers, "final"); if (hasModifier(modifiers, "private")) addModifier(modifiers, "final"); } if (memberOfInterface()) { assrt(n, !isConstructor, "interface can not have constructor"); addModifiers(modifiers, MODIFIERS_INTERFACE_METHOD); assrtLegalModifiers(n, MODIFIERS_INTERFACE_METHOD, modifiers, "interface method"); } else { if (isConstructor) assrtLegalModifiers(n, MODIFIERS_CONSTRUCTOR, modifiers, "constructor"); else assrtLegalModifiers(n, MODIFIERS_METHOD, modifiers, "method"); } if (hasModifier(modifiers, "static")) assrt(n, JavaEntities.hasModifier(base, "static") || JavaEntities.isTypeTopLevel(base), "illegal context for static member"); final List<Type> exceptions = null == n.get(6) ? new ArrayList<Type>() : JavaEntities.typeList((List) dispatch(n.getNode(6))); final String symbol = JavaEntities.methodSymbolFromAst(n); _table.enter(symbol); final List<Type> parameters = JavaEntities.typeList((List) dispatch(n.getNode(4))); final Type result = new MethodT(returnType, name, parameters, false, exceptions); for (final Attribute mod : modifiers) result.addAttribute(mod); if (JavaEntities.hasModifier(result, "abstract")) assrt(n, !JavaEntities.hasModifier(result, "private") && !JavaEntities.hasModifier(result, "static") && !JavaEntities.hasModifier(result, "final") && !JavaEntities.hasModifier(result, "native") && !JavaEntities.hasModifier(result, "strictfp") && !JavaEntities.hasModifier(result, "synchronized"), "conflicting modifiers"); assrt(n, !JavaEntities.hasModifier(result, "strictfp") || !JavaEntities.hasModifier(result, "native"), "conflicting modifiers"); result.scope(_table.current().getQualifiedName()); _table.exit(); _table.current().define(symbol, result); JavaEntities.currentType(_table).getMethods().add(result); assert result.isMethod(); return result; } /** Visit a Modifiers = Modifier*. */ public final List<Attribute> visitModifiers(final GNode n) { final List<Attribute> result = new ArrayList<Attribute>(); for (int i = 0; i < n.size(); i++) { final String name = n.getGeneric(i).getString(0); final Attribute modifier = JavaEntities.nameToModifier(name); if (null == modifier) _runtime.error("unexpected modifier " + name, n); else if (result.contains(modifier)) _runtime.error("duplicate modifier " + name, n); else result.add(modifier); } return result; } /** Visit a PackageDeclaration = QualifiedIdentifier. */ public final PackageT visitPackageDeclaration(final GNode n) { // gosling_et_al_2000 9.4 final String canonicalName = null == n ? "" : (String) dispatch(n.getNode(1)); final PackageT result = JavaEntities.canonicalNameToPackage(_table, canonicalName); _table.enter(JavaEntities.packageNameToScopeName(result.getName())); return result; } /** Visit a PrimitiveType = ("byte" / "short" / "char" / "int" / "long" / "float" / "double" / "boolean") * (gosling_et_al_2000 <a href="http://java.sun.com/docs/books/jls/second_edition/html/typesValues.doc.html#85587">§4.2</a>). */ public final Type visitPrimitiveType(final GNode n) { final Type result = JavaEntities.nameToBaseType(n.getString(0)); assrt(n, null != result && (JavaEntities.isPrimitiveT(result) || result.isVoid()), "unknown base type %s", n.getString(0)); return result; } /** Visit a QualifiedIdentifier = Identifier+. */ public final String visitQualifiedIdentifier(final GNode n) { // using StringBuffer instead of Utilities.qualify() to avoid O(n^2) // behavior final StringBuffer b = new StringBuffer(); for (int i = 0; i < n.size(); i++) { if (b.length() > 0) b.append(Constants.QUALIFIER); b.append(n.getString(i)); } return b.toString(); } /** Visit a ThrowsClause = QualifiedIdentifier+. */ public final List<Type> visitThrowsClause(final GNode n) { //gosling_et_al_2000 8.4.4, 8.8.4, 9.4 final List<Type> result = new ArrayList<Type>(n.size()); for (int i = 0; i < n.size(); i++) { final String name = (String) dispatch(n.getNode(i)); result.add(new AliasT(name)); } return result; } /** * Visit a Type = TypeName Dimensions * (gosling_et_al_2000 <a href="http://java.sun.com/docs/books/jls/second_edition/html/typesValues.doc.html#48440">§4</a>, * <a href="http://java.sun.com/docs/books/jls/second_edition/html/arrays.doc.html#25518">§10.1</a>). * Note that TypeName is either PrimitiveType or ClassType, i.e., QualifiedIdentifier. * Make no resolution attempt in the case of a qualified identifier, just * return an alias every time. The reason is that in general, there is * too little information to resolve the type at this point. For example, the * name may refer to a type declared in another file, which may recursively * mention an entity in this file. */ public final Type visitType(final GNode n) { final boolean composite = n.getGeneric(0).hasName("QualifiedIdentifier"); final Object dispatched0 = dispatch(n.getNode(0)); final Type componentT = composite ? new AliasT((String) dispatched0) : (Type) dispatched0; final int dimensions = countDimensions(n.getGeneric(1)); final Type result = JavaEntities.typeWithDimensions(componentT, dimensions); assrt(n, JavaEntities.isReturnT(result), "unexpected type reference"); return result; } /** Visit a VoidType = (no children). */ public final Type visitVoidType(final GNode n) { return JavaEntities.nameToBaseType("void"); } }