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