    001    /* Copyright 2000, 2001, Compaq Computer Corporation */
    002
    003    package escjava.translate;
    004
    005    import java.util.Vector;
    006    import java.util.Hashtable;
    007    import javafe.util.Assert;
    008    import javafe.util.Location;
    009    import javafe.ast.*;
    010    import javafe.parser.ParseUtil;
    011    import escjava.ast.TagConstants;
    012    import escjava.translate.Substitute;
    013
    014    /* This class performs an experiment with inlining to remove spurious warnings
    015       caused by the lack of object invariant annotations.  In particular, the class
    016       takes a method declaration and replaces it by a method declaration consisting
    017       of an appropriate constructor call, followed by an inlined call to the
    018       original method. */
    019
    020    public class InlineConstructor {
    021
    022        //@ requires cus != null;
    023        public static void inlineConstructorsEverywhere(Vector cus) {
    024            int size = cus.size();
    025            for (int i = 0; i < size; i++) {
    026                CompilationUnit cu = (CompilationUnit) cus.elementAt(i);
    027                int numClasses = cu.elems.size();
    028                for (int j = 0; j < numClasses; j++) {
    029                    TypeDecl td = (TypeDecl) cu.elems.elementAt(j);
    030                    if (!Modifiers.isAbstract(td.modifiers))
    031                        inlineConstructorsInAllMethods(td);
    032                }
    033            }
    034        }
    035
    036        public static void inlineConstructorsInAllMethods(TypeDecl td) {
    037            // find the appropriate modifier for STATIC
    038
    039            // create a static field for the replacement for <code>this</code>
    040            Identifier newThis = uniquifyName("this");
    041            TypeName selfType0 = TypeName.make(SimpleName.make(td.id, td.locId));
    042            FieldDecl newField = FieldDecl.make(Modifiers.ACC_STATIC, null,
    043                                                newThis,
    044                                                selfType0, td.locOpenBrace, null,
    045                                                td.locOpenBrace);
    046            newField.setParent(td);
    047            td.elems.addElement(newField);
    048
    049            // find all constructors
    050            TypeDeclElemVec constructors = TypeDeclElemVec.make();
    051            int size = td.elems.size();
    052            for (int i = 0; i < size; i++) {
    053                TypeDeclElem elem = td.elems.elementAt(i);
    054                if (elem instanceof ConstructorDecl)
    055                    constructors.addElement(elem);
    056            }
    057
    058            int numConstructors = constructors.size();
    059
    060            // if we couldn't find any constructors, we're done
    061            if (numConstructors == 0)
    062                return;
    063
    064            // for each non-static method, create a constructor-inlined version of
    065            // it with each constructor
    066            for (int j = 0; j < size; j++) {
    067                TypeDeclElem tde = td.elems.elementAt(j);
    068                if (isConstructorInlinable(tde)) {
    069                    for (int i = 0; i < numConstructors; i++)
    070                        createMethodDecl((MethodDecl) tde, td,
    071                                         (ConstructorDecl) constructors.elementAt(i),
    072                                         newThis, i);
    073                }
    074            }
    075        }
    076
    077
    078        private static void createMethodDecl(MethodDecl md, TypeDecl td,
    079                                             ConstructorDecl cd,
    080                                             Identifier newThis,
    081                                             int count) {
    082
    083            // create the constructor invocation statement
    084            TypeName selfType1 = TypeName.make(SimpleName.make(td.id, cd.locId));
    085            ExprVec evec = ExprVec.make();
    086            int size = cd.args.size();
    087            for (int i = 0; i < size; i++) {
    088                Identifier argName = uniquifyName(cd.args.elementAt(i).id);
    089                Name name = SimpleName.make(argName, cd.locId);
    090                evec.addElement(AmbiguousVariableAccess.make(name));
    091            }
    092            NewInstanceExpr newexpr = NewInstanceExpr.make(null, cd.locId,
    093                                                           selfType1, evec,
    094                                                           null, cd.locId,
    095                                                           cd.locId);
    096            // inline this call, assuming the preconditions and all body checks
    097            Translate.inlineDecoration.set(newexpr,
    098                                           new InlineSettings(true, true, true));
    099
    100            AmbiguousVariableAccess newThisVar =
    101                AmbiguousVariableAccess.make(SimpleName.make(newThis, cd.locId));
    102            BinaryExpr assignExpr = BinaryExpr.make(TagConstants.ASSIGN, newThisVar,
    103                                                    newexpr, cd.locId);
    104            EvalStmt firstStmt = EvalStmt.make(assignExpr);
    105
    106
    107            // create the method invocation statement
    108            evec = ExprVec.make();
    109            size = md.args.size();
    110            for (int i = 0; i < size; i++) {
    111                Name name = SimpleName.make(md.args.elementAt(i).id, md.locId);
    112                evec.addElement(AmbiguousVariableAccess.make(name));
    113            }
    114            // inline this call, checking all of the body checks
    115            AmbiguousVariableAccess receiver =
    116                AmbiguousVariableAccess.make(SimpleName.make(newThis,
    117                                                             md.locId));
    118            ObjectDesignator od = ExprObjectDesignator.make(md.locId, receiver);
    119            MethodInvocation invocation =
    120                MethodInvocation.make(od, md.id, md.tmodifiers, md.locId,
    121                                      md.locId, evec);
    122            invocation.decl = md;
    123            //inline the call, assuming the preconditions and checking the body
    124            Translate.inlineDecoration.set(invocation,
    125                                           new InlineSettings(true, false, false));
    126            Stmt secondStmt;
    127            if (md.returnType instanceof PrimitiveType &&
    128                ((PrimitiveType) md.returnType).tag == TagConstants.VOIDTYPE)
    129                secondStmt = EvalStmt.make(invocation);
    130            else
    131                secondStmt = ReturnStmt.make(invocation, md.locId);
    132
    133            // create the new method
    134            StmtVec stmts = StmtVec.make(2);
    135            stmts.addElement(firstStmt);
    136            stmts.addElement(secondStmt);
    137            BlockStmt body = BlockStmt.make(stmts, md.loc, md.loc);
    138
    139            FormalParaDeclVec args = FormalParaDeclVec.make();
    140            size = md.args.size();
    141            for (int i = 0; i < size; i++) {
    142                FormalParaDecl arg = md.args.elementAt(i);
    143                args.addElement(FormalParaDecl.make(arg.modifiers, arg.pmodifiers,
    144                                                    arg.id, arg.type, arg.locId));
    145            }
    146            // we uniquify constructor arg names so they don't clash with
    147            // the names of the parameters above
    148            size = cd.args.size();
    149            for (int i = 0; i < size; i++) {
    150                FormalParaDecl arg = cd.args.elementAt(i);
    151                Identifier newName = uniquifyName(arg.id);
    152                args.addElement(FormalParaDecl.make(arg.modifiers, arg.pmodifiers,
    153                                                    newName, arg.type,
    154                                                    arg.locId));
    155            }
    156
    157
    158            /** ***TODO**
    159
    160            // copy <code>md</code>'s postconditions, with "this" replaced by our
    161            // newThis.
    162            ModifierPragmaVec postconds = null;
    163            boolean first = true;
    164            if (md.pmodifiers != null) {
    165                size = md.pmodifiers.size();
    166                for (int i = 0; i < size; i++) {
    167                    ModifierPragma mp = md.pmodifiers.elementAt(i);
    168                    int tag = mp.getTag();
    169                    if (tag == TagConstants.ENSURES ||
    170                        tag == TagConstants.ALSO_ENSURES) {
    171                        if (first) {
    172                            postconds = ModifierPragmaVec.make();
    173                            first = false;
    174                        }
    175                    }
    176                }
    177            }
    178            **/
    179
    180            /* create the throws clause of the new method
    181               Note:  We don't check for duplicates, so this may contain
    182               the same exception twice.  Is that a problem?
    183            */
    184            TypeNameVec raises = null;
    185            if (md.raises != null) {
    186                size = md.raises.size();
    187                raises = TypeNameVec.make();
    188                for (int i = 0; i < size; i++)
    189                    raises.addElement(copyTypeName(md.raises.elementAt(i), md));
    190            }
    191            if (cd.raises != null) {
    192                size = cd.raises.size();
    193                if (raises == null)
    194                    raises = TypeNameVec.make(size);
    195                for (int i = 0; i < size; i++)
    196                    raises.addElement(copyTypeName(cd.raises.elementAt(i), cd));
    197            }
    198
    199            MethodDecl newMethod = MethodDecl.make(Modifiers.ACC_STATIC, null,
    200                                                   null, args, raises, body,
    201                                                   md.loc, md.loc, md.loc, md.loc,
    202                                                   uniquifyName(md.id.toString()
    203                                                                + "_" + count),
    204                                                   copyType(md.returnType, md),
    205                                                   md.loc);
    206            newMethod.setParent(td);
    207            td.elems.addElement(newMethod);
    208
    209        }
    210
    211
    212        /**
    213         ** Returns true if the given method is a constructor-inlined version
    214         ** of some other method
    215         **/
    216        public static boolean isConstructorInlinedMethod(MethodDecl md) {
    217            return md.id.toString().startsWith("*");
    218        }
    219
    220
    221        /* Returns true iff the given TypeDeclElem is a non-static, non-helper
    222           method.
    223        */
    224        public static boolean isConstructorInlinable(TypeDeclElem tde) {
    225            if (tde instanceof MethodDecl) {
    226                MethodDecl md = (MethodDecl) tde;
    227                if (!(Modifiers.isStatic(md.modifiers) ||
    228                      Helper.isHelper(md)))
    229                    return true;
    230            }
    231            return false;
    232        }
    233
    234
    235        /* Return a new copy of a Type, using the location of the surrounding
    236           RoutineDecl.
    237        */
    238        private static Type copyType(Type t, RoutineDecl rd) {
    239            if (t instanceof PrimitiveType) {
    240                PrimitiveType pt = (PrimitiveType) t;
    241                return PrimitiveType.make(pt.tag, rd.loc);
    242            }
    243            else if (t instanceof TypeName) {
    244                return copyTypeName((TypeName) t, rd);
    245            }
    246            else if (t instanceof ArrayType) {
    247                ArrayType at = (ArrayType) t;
    248                return ArrayType.make(copyType(at.elemType, rd), rd.loc);
    249            }
    250            else {
    251                Assert.fail("Unknown kind of Type");
    252                return null;
    253            }
    254        }
    255
    256        private static TypeName copyTypeName(TypeName tn, RoutineDecl rd) {
    257            return TypeName.make(copyName(tn.name, rd));
    258        }
    259
    260        /* Return a new copy of a Name, with all locations nulled out. */
    261        private static Name copyName(Name n, RoutineDecl rd) {
    262            if (n instanceof SimpleName) {
    263                SimpleName sn = (SimpleName) n;
    264                return SimpleName.make(sn.id, rd.loc);
    265            }
    266            else if (n instanceof CompoundName) {
    267                CompoundName cn = (CompoundName) n;
    268                int size = cn.ids.size();
    269                IdentifierVec iv = IdentifierVec.make(size);
    270                int[] ids = new int[size];
    271                int[] dots = new int[size];
    272                for (int i = 0; i < size; i++) {
    273                    iv.addElement(cn.ids.elementAt(i));
    274                    ids[i] = rd.loc;
    275                    dots[i] = rd.loc;
    276                }
    277                return CompoundName.make(iv, ids, dots);
    278            }
    279            else {
    280                Assert.fail("Unknown kind of Name");
    281                return null;
    282            }
    283        }
    284
    285
    286        /* surround an identifier by '*'s. */
    287        private static Identifier uniquifyName(String s) {
    288            return Identifier.intern("*" + s + "*");
    289        }
    290
    291        private static Identifier uniquifyName(Identifier i) {
    292            return uniquifyName(i.toString());
    293        }
    294
    295    }
    296