< prev index next >

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

Print this page

        

@@ -26,11 +26,13 @@
 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,11 +54,14 @@
 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,10 +143,12 @@
         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,10 +162,17 @@
      *  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,15 +689,15 @@
             Type supertype;
             JCExpression extending;
 
             if (tree.extending != null) {
                 extending = clearTypeParams(tree.extending);
-                supertype = attr.attribBase(extending, baseEnv, true, false, true);
+                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), baseEnv,
+                ? attr.attribBase(enumBase(tree.pos, sym), sym, baseEnv,
                                   true, false, false)
                 : (sym.fullname == names.java_lang_Object)
                 ? Type.noType
                 : syms.objectType;
             }

@@ -693,29 +707,41 @@
             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);
+                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,11 +825,11 @@
     }
 
     private final class HeaderPhase extends AbstractHeaderPhase {
 
         public HeaderPhase() {
-            super(CompletionCause.HEADER_PHASE, new MembersPhase());
+            super(CompletionCause.HEADER_PHASE, new RecordPhase());
         }
 
         @Override
         protected void runPhase(Env<AttrContext> env) {
             JCClassDecl tree = env.enclClass;

@@ -849,16 +875,14 @@
                 sym.flags_field |= AUXILIARY;
             }
         }
     }
 
-    /** Enter member fields and methods of a class
-     */
-    private final class MembersPhase extends Phase {
+    private abstract class AbstractMembersPhase extends Phase {
 
-        public MembersPhase() {
-            super(CompletionCause.MEMBERS_PHASE, null);
+        public AbstractMembersPhase(CompletionCause completionCause, Phase next) {
+            super(completionCause, next);
         }
 
         private boolean completing;
         private List<Env<AttrContext>> todo = List.nil();
 

@@ -878,54 +902,98 @@
                 }
             } 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)) {
-                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;
+                DefaultConstructorHelper helper = new BasicConstructorHelper(sym);
                 if (sym.name.isEmpty()) {
-                    nc = (JCNewClass)env.next.tree;
+                    JCNewClass 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;
+                        if (nc.constructor.kind != ERR) {
+                            helper = new AnonClassConstructorHelper(sym, (MethodSymbol)nc.constructor, nc.encl);
+                        } else {
+                            helper = null;
                         }
-                        thrown = superConstrType.getThrownTypes();
                     }
+                } else if ((sym.flags() & RECORD) != 0) {
+                    helper = new RecordConstructorHelper(sym, TreeInfo.recordFields(tree));
                 }
-                if (addConstructor) {
-                    MethodSymbol basedConstructor = nc != null ?
-                            (MethodSymbol)nc.constructor : null;
-                    JCTree constrDef = DefaultConstructor(make.at(tree.pos), sym,
-                                                        basedConstructor,
-                                                        typarams, argtypes, thrown,
-                                                        ctorFlags, based);
+                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,32 +1011,150 @@
                 for (JCTypeParameter tvar : tree.typarams) {
                     chk.checkNonCyclic(tvar, (TypeVar)tvar.type);
                 }
             }
 
-            finishClass(tree, env);
+            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) {
+        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);
             }
-            memberEnter.memberEnter(tree.defs, 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,140 +1187,326 @@
                           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
  ****************************************************************************/
 
-    /** 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));
+    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();
         }
-        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;
+    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);
         }
-        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);
+    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;
         }
-        List<JCExpression> typeargs = typarams.nonEmpty() ? make.Types(typarams) : null;
-        return make.Exec(make.Apply(typeargs, meth, make.Idents(params)));
+
+        @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 >