< prev index next >

src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java

Print this page

        

*** 26,36 **** --- 26,38 ---- package com.sun.tools.javac.comp; import java.util.HashSet; import java.util.Set; import java.util.function.BiConsumer; + import java.util.stream.Collectors; + import javax.lang.model.element.ElementKind; import javax.tools.JavaFileObject; import com.sun.tools.javac.code.*; import com.sun.tools.javac.code.Lint.LintCategory; import com.sun.tools.javac.code.Scope.ImportFilter;
*** 52,62 **** --- 54,67 ---- import static com.sun.tools.javac.code.Flags.ANNOTATION; import static com.sun.tools.javac.code.Scope.LookupKind.NON_RECURSIVE; import static com.sun.tools.javac.code.Kinds.Kind.*; import static com.sun.tools.javac.code.TypeTag.CLASS; import static com.sun.tools.javac.code.TypeTag.ERROR; + import static com.sun.tools.javac.code.TypeTag.NONE; + import com.sun.tools.javac.resources.CompilerProperties.Fragments; + import static com.sun.tools.javac.tree.JCTree.Tag.*; import com.sun.tools.javac.util.Dependencies.CompletionCause; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
*** 138,147 **** --- 143,154 ---- typeEnvs = TypeEnvs.instance(context); dependencies = Dependencies.instance(context); Source source = Source.instance(context); allowTypeAnnos = Feature.TYPE_ANNOTATIONS.allowedInSource(source); allowDeprecationOnImport = Feature.DEPRECATION_ON_IMPORT.allowedInSource(source); + Options options = Options.instance(context); + dontErrorIfSealedExtended = options.isSet("dontErrorIfSealedExtended"); } /** Switch: support type annotations. */ boolean allowTypeAnnos;
*** 155,164 **** --- 162,178 ---- * enter, as we only need to look up types. This avoids * unnecessarily deep recursion. */ boolean completionEnabled = true; + /** + * Temporary switch, false by default but if set, allows generating classes that can extend a sealed class + * even if not listed as a permitted subtype. This allows testing the VM runtime. Should be removed before sealed types + * gets integrated + */ + boolean dontErrorIfSealedExtended; + /* Verify Imports: */ protected void ensureImportsChecked(List<JCCompilationUnit> trees) { // if there remain any unimported toplevels (these must have // no classes at all), process their import statements as well.
*** 675,689 **** Type supertype; JCExpression extending; if (tree.extending != null) { extending = clearTypeParams(tree.extending); ! supertype = attr.attribBase(extending, baseEnv, true, false, true); } else { extending = null; supertype = ((tree.mods.flags & Flags.ENUM) != 0) ! ? attr.attribBase(enumBase(tree.pos, sym), baseEnv, true, false, false) : (sym.fullname == names.java_lang_Object) ? Type.noType : syms.objectType; } --- 689,703 ---- Type supertype; JCExpression extending; if (tree.extending != null) { extending = clearTypeParams(tree.extending); ! supertype = attr.attribBase(extending, sym, baseEnv, true, false, true); } else { extending = null; supertype = ((tree.mods.flags & Flags.ENUM) != 0) ! ? attr.attribBase(enumBase(tree.pos, sym), sym, baseEnv, true, false, false) : (sym.fullname == names.java_lang_Object) ? Type.noType : syms.objectType; }
*** 693,721 **** ListBuffer<Type> interfaces = new ListBuffer<>(); ListBuffer<Type> all_interfaces = null; // lazy init List<JCExpression> interfaceTrees = tree.implementing; for (JCExpression iface : interfaceTrees) { iface = clearTypeParams(iface); ! Type it = attr.attribBase(iface, baseEnv, false, true, true); if (it.hasTag(CLASS)) { interfaces.append(it); if (all_interfaces != null) all_interfaces.append(it); } else { if (all_interfaces == null) all_interfaces = new ListBuffer<Type>().appendList(interfaces); all_interfaces.append(modelMissingTypes(baseEnv, it, iface, true)); } } if ((sym.flags_field & ANNOTATION) != 0) { ct.interfaces_field = List.of(syms.annotationType); ct.all_interfaces_field = ct.interfaces_field; } else { ct.interfaces_field = interfaces.toList(); ct.all_interfaces_field = (all_interfaces == null) ? ct.interfaces_field : all_interfaces.toList(); } } //where: protected JCExpression clearTypeParams(JCExpression superType) { return superType; } --- 707,747 ---- ListBuffer<Type> interfaces = new ListBuffer<>(); ListBuffer<Type> all_interfaces = null; // lazy init List<JCExpression> interfaceTrees = tree.implementing; for (JCExpression iface : interfaceTrees) { iface = clearTypeParams(iface); ! Type it = attr.attribBase(iface, sym, baseEnv, false, true, true); if (it.hasTag(CLASS)) { interfaces.append(it); if (all_interfaces != null) all_interfaces.append(it); } else { if (all_interfaces == null) all_interfaces = new ListBuffer<Type>().appendList(interfaces); all_interfaces.append(modelMissingTypes(baseEnv, it, iface, true)); } } + // Determine permits. + ListBuffer<Type> permittedSubtypes = new ListBuffer<>(); + List<JCExpression> permittedTrees = tree.permitting; + for (JCExpression permitted : permittedTrees) { + permitted = clearTypeParams(permitted); + Type pt = attr.attribBase(permitted, sym, baseEnv, false, false, false); + permittedSubtypes.append(pt); + } + if ((sym.flags_field & ANNOTATION) != 0) { ct.interfaces_field = List.of(syms.annotationType); ct.all_interfaces_field = ct.interfaces_field; } else { ct.interfaces_field = interfaces.toList(); ct.all_interfaces_field = (all_interfaces == null) ? ct.interfaces_field : all_interfaces.toList(); } + + ct.permitted = permittedSubtypes.toList(); + ct.isPermittedExplicit = !permittedSubtypes.isEmpty(); } //where: protected JCExpression clearTypeParams(JCExpression superType) { return superType; }
*** 799,809 **** } private final class HeaderPhase extends AbstractHeaderPhase { public HeaderPhase() { ! super(CompletionCause.HEADER_PHASE, new MembersPhase()); } @Override protected void runPhase(Env<AttrContext> env) { JCClassDecl tree = env.enclClass; --- 825,835 ---- } private final class HeaderPhase extends AbstractHeaderPhase { public HeaderPhase() { ! super(CompletionCause.HEADER_PHASE, new RecordPhase()); } @Override protected void runPhase(Env<AttrContext> env) { JCClassDecl tree = env.enclClass;
*** 849,864 **** sym.flags_field |= AUXILIARY; } } } ! /** Enter member fields and methods of a class ! */ ! private final class MembersPhase extends Phase { ! public MembersPhase() { ! super(CompletionCause.MEMBERS_PHASE, null); } private boolean completing; private List<Env<AttrContext>> todo = List.nil(); --- 875,888 ---- sym.flags_field |= AUXILIARY; } } } ! private abstract class AbstractMembersPhase extends Phase { ! public AbstractMembersPhase(CompletionCause completionCause, Phase next) { ! super(completionCause, next); } private boolean completing; private List<Env<AttrContext>> todo = List.nil();
*** 878,931 **** } } finally { completing = prevCompleting; } } @Override protected void runPhase(Env<AttrContext> env) { JCClassDecl tree = env.enclClass; ClassSymbol sym = tree.sym; ClassType ct = (ClassType)sym.type; // Add default constructor if needed. if ((sym.flags() & INTERFACE) == 0 && !TreeInfo.hasConstructors(tree.defs)) { ! List<Type> argtypes = List.nil(); ! List<Type> typarams = List.nil(); ! List<Type> thrown = List.nil(); ! long ctorFlags = 0; ! boolean based = false; ! boolean addConstructor = true; ! JCNewClass nc = null; if (sym.name.isEmpty()) { ! nc = (JCNewClass)env.next.tree; if (nc.constructor != null) { ! addConstructor = nc.constructor.kind != ERR; ! Type superConstrType = types.memberType(sym.type, ! nc.constructor); ! argtypes = superConstrType.getParameterTypes(); ! typarams = superConstrType.getTypeArguments(); ! ctorFlags = nc.constructor.flags() & VARARGS; ! if (nc.encl != null) { ! argtypes = argtypes.prepend(nc.encl.type); ! based = true; } - thrown = superConstrType.getThrownTypes(); } } ! if (addConstructor) { ! MethodSymbol basedConstructor = nc != null ? ! (MethodSymbol)nc.constructor : null; ! JCTree constrDef = DefaultConstructor(make.at(tree.pos), sym, ! basedConstructor, ! typarams, argtypes, thrown, ! ctorFlags, based); tree.defs = tree.defs.prepend(constrDef); } } - // enter symbols for 'this' into current scope. VarSymbol thisSym = new VarSymbol(FINAL | HASINIT, names._this, sym.type, sym); thisSym.pos = Position.FIRSTPOS; env.info.scope.enter(thisSym); --- 902,999 ---- } } finally { completing = prevCompleting; } } + } + + private final class RecordPhase extends AbstractMembersPhase { + + public RecordPhase() { + super(CompletionCause.RECORD_PHASE, new MembersPhase()); + } + + @Override + protected void runPhase(Env<AttrContext> env) { + JCClassDecl tree = env.enclClass; + ClassSymbol sym = tree.sym; + if ((sym.flags_field & RECORD) != 0) { + memberEnter.memberEnter(TreeInfo.recordFields(tree), env); + } + } + } + + /** Enter member fields and methods of a class + */ + private final class MembersPhase extends AbstractMembersPhase { + + public MembersPhase() { + super(CompletionCause.MEMBERS_PHASE, null); + } @Override protected void runPhase(Env<AttrContext> env) { JCClassDecl tree = env.enclClass; ClassSymbol sym = tree.sym; ClassType ct = (ClassType)sym.type; + boolean defaultConstructorGenerated = false; // Add default constructor if needed. if ((sym.flags() & INTERFACE) == 0 && !TreeInfo.hasConstructors(tree.defs)) { ! DefaultConstructorHelper helper = new BasicConstructorHelper(sym); if (sym.name.isEmpty()) { ! JCNewClass nc = (JCNewClass)env.next.tree; if (nc.constructor != null) { ! if (nc.constructor.kind != ERR) { ! helper = new AnonClassConstructorHelper(sym, (MethodSymbol)nc.constructor, nc.encl); ! } else { ! helper = null; } } + } else if ((sym.flags() & RECORD) != 0) { + helper = new RecordConstructorHelper(sym, TreeInfo.recordFields(tree)); } ! if (helper != null) { ! JCTree constrDef = defaultConstructor(make.at(tree.pos), helper); tree.defs = tree.defs.prepend(constrDef); + defaultConstructorGenerated = true; + } + } else { + if ((sym.flags() & RECORD) != 0) { + // there are constructors but they could be incomplete + for (JCTree def : tree.defs) { + if (TreeInfo.isConstructor(def)) { + Name constructorInvocationName = + TreeInfo.getConstructorInvocationName(((JCMethodDecl)def).body.stats, names, true); + if (constructorInvocationName == names.empty || + constructorInvocationName == names._super) { + RecordConstructorHelper helper = new RecordConstructorHelper(sym, TreeInfo.recordFields(tree)); + JCMethodDecl methDecl = (JCMethodDecl)def; + if (constructorInvocationName == names.empty) { + JCStatement supCall = make.at(methDecl.body.pos).Exec(make.Apply(List.nil(), + make.Ident(names._super), List.nil())); + methDecl.body.stats = methDecl.body.stats.prepend(supCall); + } + ListBuffer<JCStatement> initializations = new ListBuffer<>(); + List<Name> inits = helper.inits(); + InitializationFinder initFinder = new InitializationFinder(inits); + initFinder.scan(methDecl.body.stats); + List<Name> found = initFinder.found.toList(); + inits = inits.diff(found); + if (!inits.isEmpty()) { + for (Name initName : inits) { + initializations.add(make.Exec(make.Assign(make.Select(make.Ident(names._this), + initName), make.Ident(initName)))); + } + methDecl.body.stats = methDecl.body.stats.appendList(initializations.toList()); + } + } + } + } } } // enter symbols for 'this' into current scope. VarSymbol thisSym = new VarSymbol(FINAL | HASINIT, names._this, sym.type, sym); thisSym.pos = Position.FIRSTPOS; env.info.scope.enter(thisSym);
*** 943,974 **** for (JCTypeParameter tvar : tree.typarams) { chk.checkNonCyclic(tvar, (TypeVar)tvar.type); } } ! finishClass(tree, env); if (allowTypeAnnos) { typeAnnotations.organizeTypeAnnotationsSignatures(env, (JCClassDecl)env.tree); typeAnnotations.validateTypeAnnotationsSignatures(env, (JCClassDecl)env.tree); } } /** Enter members for a class. */ ! void finishClass(JCClassDecl tree, Env<AttrContext> env) { if ((tree.mods.flags & Flags.ENUM) != 0 && !tree.sym.type.hasTag(ERROR) && (types.supertype(tree.sym.type).tsym.flags() & Flags.ENUM) == 0) { addEnumMembers(tree, env); } ! memberEnter.memberEnter(tree.defs, env); if (tree.sym.isAnnotationType()) { Assert.check(tree.sym.isCompleted()); tree.sym.setAnnotationTypeMetadata(new AnnotationTypeMetadata(tree.sym, annotate.annotationTypeSourceCompleter())); } } /** Add the implicit members for an enum type * to the symbol table. */ --- 1011,1160 ---- for (JCTypeParameter tvar : tree.typarams) { chk.checkNonCyclic(tvar, (TypeVar)tvar.type); } } ! finishClass(tree, env, defaultConstructorGenerated); if (allowTypeAnnos) { typeAnnotations.organizeTypeAnnotationsSignatures(env, (JCClassDecl)env.tree); typeAnnotations.validateTypeAnnotationsSignatures(env, (JCClassDecl)env.tree); } } + class InitializationFinder extends TreeScanner { + List<Name> fieldNames; + ListBuffer<Name> found = new ListBuffer<>(); + + public InitializationFinder(List<Name> fieldNames) { + this.fieldNames = fieldNames; + } + + @Override + public void visitAssign(JCAssign tree) { + super.visitAssign(tree); + if (tree.lhs.hasTag(SELECT)) { + JCFieldAccess select = (JCFieldAccess)tree.lhs; + if ((select.selected.hasTag(IDENT)) && + ((JCIdent)select.selected).name == names._this) { + if (fieldNames.contains(select.name)) { + found.add(select.name); + } + } + } + } + } + /** Enter members for a class. */ ! void finishClass(JCClassDecl tree, Env<AttrContext> env, boolean defaultConstructorGenerated) { if ((tree.mods.flags & Flags.ENUM) != 0 && !tree.sym.type.hasTag(ERROR) && (types.supertype(tree.sym.type).tsym.flags() & Flags.ENUM) == 0) { addEnumMembers(tree, env); } ! boolean isRecord = (tree.sym.flags_field & RECORD) != 0; ! List<JCTree> defsToEnter = isRecord ? ! tree.defs.diff(List.convert(JCTree.class, TreeInfo.recordFields(tree))) : tree.defs; ! memberEnter.memberEnter(defsToEnter, env); ! List<JCTree> defsBeforeAddingNewMembers = tree.defs; ! if (isRecord) { ! addRecordMembersIfNeeded(tree, env, defaultConstructorGenerated); ! addAccessorsIfNeeded(tree, env); ! } ! // now we need to enter any additional mandated member that could have been added in the previous step ! memberEnter.memberEnter(tree.defs.diff(List.convert(JCTree.class, defsBeforeAddingNewMembers)), env); if (tree.sym.isAnnotationType()) { Assert.check(tree.sym.isCompleted()); tree.sym.setAnnotationTypeMetadata(new AnnotationTypeMetadata(tree.sym, annotate.annotationTypeSourceCompleter())); } + + Type st = types.supertype(tree.sym.type); + boolean anyParentIsSealed = false; + ListBuffer<Pair<ClassType, JCExpression>> potentiallySealedParents = new ListBuffer<>(); + if (st != Type.noType && (st.tsym.isSealed())) { + potentiallySealedParents.add(new Pair<>((ClassType)st, tree.extending)); + anyParentIsSealed = true; + } + + if (tree.implementing != null) { + for (JCExpression expr : tree.implementing) { + if (expr.type.tsym.isSealed()) { + potentiallySealedParents.add(new Pair<>((ClassType)expr.type, expr)); + anyParentIsSealed = true; + } + } + } + + for (Pair<ClassType, JCExpression> sealedParentPair: potentiallySealedParents) { + if (!sealedParentPair.fst.permitted.map(t -> t.tsym).contains(tree.sym.type.tsym)) { + boolean areInSameCompilationUnit = TreeInfo.declarationFor(sealedParentPair.fst.tsym, env.toplevel) != null && + TreeInfo.declarationFor(tree.sym.outermostClass(), env.toplevel) != null; + boolean isSealed = sealedParentPair.fst.tsym.isSealed(); + if (areInSameCompilationUnit) { + if (sealedParentPair.fst.tsym.isSealed() && !((ClassType)sealedParentPair.fst.tsym.type).isPermittedExplicit) { + if (tree.sym.isAnonymous()) { + log.error(sealedParentPair.snd, Errors.CantInheritFromSealed(sealedParentPair.fst.tsym)); + } else { + sealedParentPair.fst.permitted = sealedParentPair.fst.permitted.prepend(tree.sym.type); + } + } else if (!dontErrorIfSealedExtended) { + log.error(sealedParentPair.snd, Errors.CantInheritFromSealed(sealedParentPair.fst.tsym)); + } + } else if (!dontErrorIfSealedExtended) { + log.error(sealedParentPair.snd, Errors.CantInheritFromSealed(sealedParentPair.fst.tsym)); + } + } + } + + if (anyParentIsSealed && ((tree.sym.flags_field & Flags.NON_SEALED) == 0) ) { + // once we have the non-final keyword this will change + tree.sym.flags_field |= (tree.sym.flags_field & ABSTRACT) != 0 ? SEALED : FINAL; + } + + if (!anyParentIsSealed && ((tree.sym.flags_field & Flags.NON_SEALED) != 0) ) { + log.error(tree, Errors.NonSealedWithNoSealedSupertype); + } + } + + /** Add the accessors for fields to the symbol table. + */ + private void addAccessorsIfNeeded(JCClassDecl tree, Env<AttrContext> env) { + tree.defs.stream() + .filter(t -> t.hasTag(VARDEF)) + .map(t -> (JCVariableDecl)t) + .filter(vd -> vd.accessors != null && vd.accessors.nonEmpty()) + .forEach(vd -> addAccessors(vd, env)); + } + + private void addAccessors(JCVariableDecl tree, Env<AttrContext> env) { + for (Pair<Accessors.Kind, Name> accessor : tree.accessors) { + Type accessorType = accessor.fst.accessorType(syms, tree.sym.type); + MethodSymbol implSym = lookupMethod(env.enclClass.sym, accessor.snd, accessorType.getParameterTypes()); + if (implSym == null || (implSym.flags_field & MANDATED) != 0) { + JCMethodDecl getter = make.at(tree.pos).MethodDef(make.Modifiers(Flags.PUBLIC | Flags.MANDATED, tree.mods.annotations), + accessor.snd, + make.Type(accessorType.getReturnType()), + List.nil(), + accessorType.getParameterTypes().stream() + .map(ptype -> make.Param(tree.name, tree.sym.type, env.enclClass.sym)) + .collect(List.collector()), + List.nil(), // thrown + null, + null); + memberEnter.memberEnter(getter, env); + tree.sym.accessors = tree.sym.accessors.prepend(new Pair<>(accessor.fst, getter.sym)); + } else if (implSym != null) { + if ((implSym.flags() & Flags.PUBLIC) == 0) { + log.error(TreeInfo.declarationFor(implSym, env.enclClass), Errors.MethodMustBePublic(implSym.name)); + } + if (!types.isSameType(implSym.type.getReturnType(), tree.sym.type)) { + log.error(TreeInfo.declarationFor(implSym, env.enclClass), Errors.AccessorReturnTypeDoesntMatch(tree.sym.type, implSym.type.getReturnType())); + } + } + } } /** Add the implicit members for an enum type * to the symbol table. */
*** 1001,1140 **** null, //make.Block(0, Tree.emptyList.prepend(make.Return(make.Ident(names._null)))), null); memberEnter.memberEnter(valueOf, env); } } /* *************************************************************************** * tree building ****************************************************************************/ ! /** Generate default constructor for given class. For classes different ! * from java.lang.Object, this is: ! * ! * c(argtype_0 x_0, ..., argtype_n x_n) throws thrown { ! * super(x_0, ..., x_n) ! * } ! * ! * or, if based == true: ! * ! * c(argtype_0 x_0, ..., argtype_n x_n) throws thrown { ! * x_0.super(x_1, ..., x_n) ! * } ! * ! * @param make The tree factory. ! * @param c The class owning the default constructor. ! * @param argtypes The parameter types of the constructor. ! * @param thrown The thrown exceptions of the constructor. ! * @param based Is first parameter a this$n? ! */ ! JCTree DefaultConstructor(TreeMaker make, ! ClassSymbol c, ! MethodSymbol baseInit, ! List<Type> typarams, ! List<Type> argtypes, ! List<Type> thrown, ! long flags, ! boolean based) { ! JCTree result; ! if ((c.flags() & ENUM) != 0 && ! (types.supertype(c.type).tsym == syms.enumSym)) { ! // constructors of true enums are private ! flags = (flags & ~AccessFlags) | PRIVATE | GENERATEDCONSTR; ! } else ! flags |= (c.flags() & AccessFlags) | GENERATEDCONSTR; ! if (c.name.isEmpty()) { ! flags |= ANONCONSTR; ! } ! if (based) { ! flags |= ANONCONSTR_BASED; ! } ! Type mType = new MethodType(argtypes, null, thrown, c); ! Type initType = typarams.nonEmpty() ? ! new ForAll(typarams, mType) : ! mType; ! MethodSymbol init = new MethodSymbol(flags, names.init, ! initType, c); ! init.params = createDefaultConstructorParams(make, baseInit, init, ! argtypes, based); ! List<JCVariableDecl> params = make.Params(argtypes, init); ! List<JCStatement> stats = List.nil(); ! if (c.type != syms.objectType) { ! stats = stats.prepend(SuperCall(make, typarams, params, based)); } - result = make.MethodDef(init, make.Block(0, stats)); - return result; } ! private List<VarSymbol> createDefaultConstructorParams( ! TreeMaker make, ! MethodSymbol baseInit, ! MethodSymbol init, ! List<Type> argtypes, ! boolean based) { ! List<VarSymbol> initParams = null; ! List<Type> argTypesList = argtypes; ! if (based) { ! /* In this case argtypes will have an extra type, compared to baseInit, ! * corresponding to the type of the enclosing instance i.e.: ! * ! * Inner i = outer.new Inner(1){} ! * ! * in the above example argtypes will be (Outer, int) and baseInit ! * will have parameter's types (int). So in this case we have to add ! * first the extra type in argtypes and then get the names of the ! * parameters from baseInit. ! */ ! initParams = List.nil(); ! VarSymbol param = new VarSymbol(PARAMETER, make.paramName(0), argtypes.head, init); ! initParams = initParams.append(param); ! argTypesList = argTypesList.tail; ! } ! if (baseInit != null && baseInit.params != null && ! baseInit.params.nonEmpty() && argTypesList.nonEmpty()) { ! initParams = (initParams == null) ? List.nil() : initParams; ! List<VarSymbol> baseInitParams = baseInit.params; ! while (baseInitParams.nonEmpty() && argTypesList.nonEmpty()) { ! VarSymbol param = new VarSymbol(baseInitParams.head.flags() | PARAMETER, ! baseInitParams.head.name, argTypesList.head, init); ! initParams = initParams.append(param); ! baseInitParams = baseInitParams.tail; ! argTypesList = argTypesList.tail; } } - return initParams; } ! /** Generate call to superclass constructor. This is: ! * ! * super(id_0, ..., id_n) ! * ! * or, if based == true ! * ! * id_0.super(id_1,...,id_n) ! * ! * where id_0, ..., id_n are the names of the given parameters. ! * ! * @param make The tree factory ! * @param params The parameters that need to be passed to super ! * @param typarams The type parameters that need to be passed to super ! * @param based Is first parameter a this$n? ! */ ! JCExpressionStatement SuperCall(TreeMaker make, ! List<Type> typarams, ! List<JCVariableDecl> params, ! boolean based) { ! JCExpression meth; ! if (based) { ! meth = make.Select(make.Ident(params.head), names._super); ! params = params.tail; ! } else { ! meth = make.Ident(names._super); } ! List<JCExpression> typeargs = typarams.nonEmpty() ? make.Types(typarams) : null; ! return make.Exec(make.Apply(typeargs, meth, make.Idents(params))); } /** * Mark sym deprecated if annotations contain @Deprecated annotation. */ --- 1187,1512 ---- null, //make.Block(0, Tree.emptyList.prepend(make.Return(make.Ident(names._null)))), null); memberEnter.memberEnter(valueOf, env); } + /** Add the implicit members for a record + * to the symbol table. + */ + private void addRecordMembersIfNeeded(JCClassDecl tree, Env<AttrContext> env, boolean defaultConstructorGenerated) { + if (!defaultConstructorGenerated) { + // let's check if there is a constructor with exactly the same arguments as the record components + List<Type> recordComponentTypes = TreeInfo.recordFields(tree).map(vd -> vd.sym.type); + List<Type> erasedTypes = types.erasure(recordComponentTypes); + JCMethodDecl canonicalDecl = null; + for (JCTree def : tree.defs) { + if (TreeInfo.isConstructor(def)) { + JCMethodDecl mdecl = (JCMethodDecl)def; + if (types.isSameTypes(mdecl.sym.type.getParameterTypes(), erasedTypes)) { + canonicalDecl = mdecl; + break; + } + } + } + if (canonicalDecl != null && !types.isSameTypes(erasedTypes, recordComponentTypes)) { + // error we found a constructor with the same erasure as the canonical constructor + log.error(canonicalDecl, Errors.ConstructorWithSameErasureAsCanonical); + } + MethodSymbol canonicalInit = canonicalDecl == null ? + null : + canonicalDecl.sym; + if (canonicalInit == null) { + RecordConstructorHelper helper = new RecordConstructorHelper(tree.sym, TreeInfo.recordFields(tree)); + JCTree constrDef = defaultConstructor(make.at(tree.pos), helper); + tree.defs = tree.defs.prepend(constrDef); + defaultConstructorGenerated = true; + } else { + /* there is an explicit constructor that match the canonical constructor by type + let's check that the match is also by name + */ + List<Name> recordComponentNames = TreeInfo.recordFields(tree).map(vd -> vd.sym.name); + List<Name> initParamNames = canonicalInit.params.map(p -> p.name); + if (!initParamNames.equals(recordComponentNames)) { + log.error(canonicalDecl, Errors.CanonicalWithNameMismatch); + } + // let's use the RECORD flag to mark it as the canonical constructor + canonicalInit.flags_field |= Flags.RECORD; + } + } + + if (lookupMethod(tree.sym, names.toString, List.nil()) == null) { + // public String toString() { return ???; } + JCMethodDecl toString = make. + MethodDef(make.Modifiers(Flags.PUBLIC | Flags.RECORD | Flags.MANDATED), + names.toString, + make.Type(syms.stringType), + List.nil(), + List.nil(), + List.nil(), // thrown + null, + null); + memberEnter.memberEnter(toString, env); + } + + if (lookupMethod(tree.sym, names.hashCode, List.nil()) == null) { + // public int hashCode() { return ???; } + JCMethodDecl hashCode = make. + MethodDef(make.Modifiers(Flags.PUBLIC | Flags.RECORD | Flags.FINAL | Flags.MANDATED), + names.hashCode, + make.Type(syms.intType), + List.nil(), + List.nil(), + List.nil(), // thrown + null, + null); + memberEnter.memberEnter(hashCode, env); + } + + if (lookupMethod(tree.sym, names.equals, List.of(syms.objectType)) == null) { + // public boolean equals(Object o) { return ???; } + JCMethodDecl equals = make. + MethodDef(make.Modifiers(Flags.PUBLIC | Flags.RECORD | Flags.FINAL | Flags.MANDATED), + names.equals, + make.Type(syms.booleanType), + List.nil(), + List.of(make.VarDef(make.Modifiers(Flags.PARAMETER), + names.fromString("o"), + make.Type(syms.objectType), null)), + List.nil(), // thrown + null, + null); + memberEnter.memberEnter(equals, env); + } + + if (attr.isSerializable(tree.sym.type)) { + if (lookupMethod(tree.sym, names.readResolve, List.nil()) == null && + lookupMethod(tree.sym, names.readObject, List.nil()) == null) { + // private Object readResolve() { return ???; } + JCMethodDecl readResolve = make. + MethodDef(make.Modifiers(Flags.PRIVATE | Flags.RECORD | Flags.FINAL | Flags.MANDATED), + names.readResolve, + make.Type(syms.objectType), + List.nil(), + List.nil(), + List.nil(), // thrown + null, + null); + memberEnter.memberEnter(readResolve, env); + } + } + } + } + + private MethodSymbol lookupMethod(TypeSymbol tsym, Name name, List<Type> argtypes) { + for (Symbol s : tsym.members().getSymbolsByName(name, s -> s.kind == MTH)) { + if (types.isSameTypes(s.type.getParameterTypes(), argtypes)) { + return (MethodSymbol) s; + } + } + return null; } /* *************************************************************************** * tree building ****************************************************************************/ ! interface DefaultConstructorHelper { ! Type constructorType(); ! MethodSymbol constructorSymbol(); ! Type enclosingType(); ! TypeSymbol owner(); ! List<Name> superArgs(); ! List<Name> inits(); ! default JCMethodDecl finalAdjustment(JCMethodDecl md) { return md; } ! } ! ! class BasicConstructorHelper implements DefaultConstructorHelper { ! ! TypeSymbol owner; ! Type constructorType; ! MethodSymbol constructorSymbol; ! ! BasicConstructorHelper(TypeSymbol owner) { ! this.owner = owner; ! } ! ! @Override ! public Type constructorType() { ! if (constructorType == null) { ! constructorType = new MethodType(List.nil(), syms.voidType, List.nil(), syms.methodClass); ! } ! return constructorType; ! } ! ! @Override ! public MethodSymbol constructorSymbol() { ! if (constructorSymbol == null) { ! long flags; ! if ((owner().flags() & ENUM) != 0 && ! (types.supertype(owner().type).tsym == syms.enumSym)) { ! // constructors of true enums are private ! flags = PRIVATE | GENERATEDCONSTR; ! } else { ! flags = (owner().flags() & AccessFlags) | GENERATEDCONSTR; ! } ! constructorSymbol = new MethodSymbol(flags, names.init, ! constructorType(), owner()); ! } ! return constructorSymbol; ! } ! ! @Override ! public Type enclosingType() { ! return Type.noType; ! } ! ! @Override ! public TypeSymbol owner() { ! return owner; ! } ! ! @Override ! public List<Name> superArgs() { ! return List.nil(); ! } ! ! @Override ! public List<Name> inits() { ! return List.nil(); } } ! class AnonClassConstructorHelper extends BasicConstructorHelper { ! ! MethodSymbol constr; ! Type encl; ! boolean based = false; ! ! AnonClassConstructorHelper(TypeSymbol owner, MethodSymbol constr, JCExpression encl) { ! super(owner); ! this.constr = constr; ! this.encl = encl != null ? encl.type : Type.noType; ! } ! ! @Override ! public Type constructorType() { ! if (constructorType == null) { ! Type ctype = types.memberType(owner.type, constr); ! if (!enclosingType().hasTag(NONE)) { ! ctype = types.createMethodTypeWithParameters(ctype, ctype.getParameterTypes().prepend(enclosingType())); ! based = true; ! } ! constructorType = ctype; ! } ! return constructorType; ! } ! ! @Override ! public MethodSymbol constructorSymbol() { ! MethodSymbol csym = super.constructorSymbol(); ! csym.flags_field |= ANONCONSTR | (constr.flags() & VARARGS); ! csym.flags_field |= based ? ANONCONSTR_BASED : 0; ! ListBuffer<VarSymbol> params = new ListBuffer<>(); ! List<Type> argtypes = constructorType().getParameterTypes(); ! if (!enclosingType().hasTag(NONE)) { ! argtypes = argtypes.tail; ! params = params.prepend(new VarSymbol(PARAMETER, make.paramName(0), enclosingType(), csym)); ! } ! if (constr.params != null) { ! for (VarSymbol p : constr.params) { ! params.add(new VarSymbol(PARAMETER | p.flags(), p.name, argtypes.head, csym)); ! argtypes = argtypes.tail; ! } } + csym.params = params.toList(); + return csym; + } + + @Override + public Type enclosingType() { + return encl; + } + + @Override + public List<Name> superArgs() { + List<JCVariableDecl> params = make.Params(constructorType().getParameterTypes(), constructorSymbol()); + if (!enclosingType().hasTag(NONE)) { + params = params.tail; + } + return params.map(vd -> vd.name); } } ! class RecordConstructorHelper extends BasicConstructorHelper { ! ! List<VarSymbol> recordFieldSymbols; ! List<JCVariableDecl> recordFieldDecls; ! ! RecordConstructorHelper(TypeSymbol owner, List<JCVariableDecl> recordFieldDecls) { ! super(owner); ! this.recordFieldDecls = recordFieldDecls; ! this.recordFieldSymbols = recordFieldDecls.map(vd -> vd.sym); ! } ! ! @Override ! public Type constructorType() { ! if (constructorType == null) { ! List<Type> argtypes = recordFieldSymbols.map(v -> v.type); ! constructorType = new MethodType(argtypes, syms.voidType, List.nil(), syms.methodClass); ! } ! return constructorType; } ! ! @Override ! public MethodSymbol constructorSymbol() { ! MethodSymbol csym = super.constructorSymbol(); ! ListBuffer<VarSymbol> params = new ListBuffer<>(); ! for (VarSymbol p : recordFieldSymbols) { ! params.add(new VarSymbol(MANDATED | PARAMETER | RECORD, p.name, p.type, csym)); ! } ! csym.params = params.toList(); ! csym.flags_field |= RECORD | PUBLIC; ! return csym; ! } ! ! @Override ! public List<Name> inits() { ! return recordFieldSymbols.map(v -> v.name); ! } ! ! @Override ! public JCMethodDecl finalAdjustment(JCMethodDecl md) { ! List<JCVariableDecl> tmpRecordFieldDecls = recordFieldDecls; ! for (JCVariableDecl arg : md.params) { ! arg.mods.annotations = tmpRecordFieldDecls.head.mods.annotations; ! tmpRecordFieldDecls = tmpRecordFieldDecls.tail; ! } ! return md; ! } ! } ! ! JCTree defaultConstructor(TreeMaker make, DefaultConstructorHelper helper) { ! Type initType = helper.constructorType(); ! MethodSymbol initSym = helper.constructorSymbol(); ! ListBuffer<JCStatement> stats = new ListBuffer<>(); ! if (helper.owner().type != syms.objectType) { ! JCExpression meth; ! if (!helper.enclosingType().hasTag(NONE)) { ! meth = make.Select(make.Ident(initSym.params.head), names._super); ! } else { ! meth = make.Ident(names._super); ! } ! List<JCExpression> typeargs = initType.getTypeArguments().nonEmpty() ? ! make.Types(initType.getTypeArguments()) : null; ! JCStatement superCall = make.Exec(make.Apply(typeargs, meth, helper.superArgs().map(make::Ident))); ! stats.add(superCall); ! } ! helper.inits().forEach((initName) -> { ! stats.add(make.Exec(make.Assign(make.Select(make.Ident(names._this), initName), make.Ident(initName)))); ! }); ! JCMethodDecl result = make.MethodDef(initSym, make.Block(0, stats.toList())); ! return helper.finalAdjustment(result); } /** * Mark sym deprecated if annotations contain @Deprecated annotation. */
< prev index next >