001: // This file is part of KeY - Integrated Deductive Software Design
002: // Copyright (C) 2001-2007 Universitaet Karlsruhe, Germany
003: // Universitaet Koblenz-Landau, Germany
004: // Chalmers University of Technology, Sweden
005: //
006: // The KeY system is protected by the GNU General Public License.
007: // See LICENSE.TXT for details.
008: //
009: // This file is part of KeY - Integrated Deductive Software Design
010: // Copyright (C) 2001-2004 Universitaet Karlsruhe, Germany
011: // Universitaet Koblenz-Landau, Germany
012: // Chalmers University of Technology, Sweden
013: //
014: // The KeY system is protected by the GNU General Public License.
015: // See LICENSE.TXT for details.
016: package de.uka.ilkd.key.java.recoderext;
017:
018: import java.util.HashMap;
019:
020: import recoder.CrossReferenceServiceConfiguration;
021: import recoder.abstraction.ClassType;
022: import recoder.java.*;
023: import recoder.java.declaration.*;
024: import recoder.java.declaration.modifier.Public;
025: import recoder.java.declaration.modifier.Static;
026: import recoder.java.expression.literal.BooleanLiteral;
027: import recoder.java.expression.operator.CopyAssignment;
028: import recoder.java.expression.operator.LogicalNot;
029: import recoder.java.expression.operator.New;
030: import recoder.java.reference.*;
031: import recoder.java.statement.*;
032: import recoder.kit.ProblemReport;
033: import recoder.list.*;
034: import de.uka.ilkd.key.util.Debug;
035:
036: /**
037: * Each class is prepared before it is initialised. The preparation of
038: * a class consists of pre-initialising the class fields with their
039: * default values. This class creates the implicit method
040: * <code><clprepare></code> responsible for the class
041: * preparation.
042: */
043: public class ClassInitializeMethodBuilder extends
044: RecoderModelTransformer {
045:
046: public static final String CLASS_INITIALIZE_IDENTIFIER = "<clinit>";
047:
048: /** maps a class to its static NON CONSTANT fields */
049: private HashMap class2initializers;
050:
051: /** maps a class to its superclass */
052: private HashMap class2super ;
053:
054: private ClassType javaLangObject;
055:
056: /**
057: * Creates an instance of the class preparation method model
058: * transformer. Information about the current recoder model can be
059: * accessed via the given service configuration. The implicit
060: * preparation method is created and added for all classes,
061: * which are declared in one of the given compilation units.
062: * @param services the CrossReferenceServiceConfiguration with the
063: * information about the recoder model
064: * @param units the CompilationUnitMutableList with the classes to
065: * be transformed
066: */
067: public ClassInitializeMethodBuilder(
068: CrossReferenceServiceConfiguration services,
069: CompilationUnitMutableList units) {
070: super (services, units);
071: class2initializers = new HashMap(10 * units.size());
072: class2super = new HashMap(2 * units.size());
073: }
074:
075: /**
076: * returns true if the given fieldspecification denotes a constant
077: * field. A constant field is declared as final and static and
078: * initialised with a time constant, which is not prepared or
079: * initialised here. ATTENTION: this is a derivation from the JLS
080: * but the obtained behaviour is equivalent as we only consider
081: * completely compiled programs and not partial compilations. The
082: * reason for preparation and initialisation of comnpile time
083: * constant fields is due to binary compatibility reasons.
084: */
085: private boolean isConstantField(FieldSpecification spec) {
086: return spec.isStatic()
087: && spec.isFinal()
088: && services.getConstantEvaluator()
089: .isCompileTimeConstant(spec.getInitializer());
090: }
091:
092: /** creates the package reference java.lang */
093: private PackageReference createJavaLangPackageReference() {
094: return new PackageReference(new PackageReference(
095: new Identifier("java")), new Identifier("lang"));
096: }
097:
098: /**
099: * iterates throuh the given field declaration and creates for each
100: * specification that contains an initializer a corresponding copy
101: * assignment. Thereby only non constant fields are considered.
102: */
103: private StatementList fieldInitializersToAssignments(
104: FieldDeclaration fd) {
105:
106: FieldSpecificationList specs = fd.getFieldSpecifications();
107: StatementMutableList result = new StatementArrayList(specs
108: .size());
109:
110: for (int i = 0; i < specs.size(); i++) {
111: FieldSpecification fs = specs.getFieldSpecification(i);
112: if (fs.isStatic() && fs.getInitializer() != null
113: && !isConstantField(fs)) {
114: result.add(assign(passiveFieldReference((Identifier) fs
115: .getIdentifier().deepClone()), (Expression) fs
116: .getInitializer().deepClone()));
117: }
118: }
119:
120: return result;
121:
122: }
123:
124: /**
125: * retrieves all static non constant fields and returns a list of
126: * copy assignment pre-initialising them with their default values
127: *
128: * some special settings for implicit fields are performed here as well
129: * @param typeDeclaration the ClassDeclaration whose fields have to be prepared
130: * @return the list of copy assignments
131: */
132: private StatementList getInitializers(
133: TypeDeclaration typeDeclaration) {
134:
135: StatementMutableList result = new StatementArrayList(
136: typeDeclaration.getChildCount());
137:
138: for (int i = 0; i < typeDeclaration.getChildCount(); i++) {
139: if (typeDeclaration.getChildAt(i) instanceof ClassInitializer) {
140: result
141: .add((Statement) ((ClassInitializer) typeDeclaration
142: .getChildAt(i)).getBody().deepClone());
143: } else if (typeDeclaration.getChildAt(i) instanceof FieldDeclaration) {
144: result
145: .add(fieldInitializersToAssignments((FieldDeclaration) typeDeclaration
146: .getChildAt(i)));
147: }
148: }
149: return result;
150: }
151:
152: public ProblemReport analyze() {
153: javaLangObject = services.getNameInfo().getJavaLangObject();
154: if (!(javaLangObject instanceof ClassDeclaration)) {
155: Debug
156: .fail("Could not find class java.lang.Object or only as bytecode");
157: }
158: for (int unit = 0; unit < units.size(); unit++) {
159: CompilationUnit cu = units.getCompilationUnit(unit);
160: int typeCount = cu.getTypeDeclarationCount();
161:
162: for (int i = 0; i < typeCount; i++) {
163: TypeDeclaration td = cu.getTypeDeclarationAt(i);
164: if (td.getTypeDeclarationCount() > 0) {
165: Debug
166: .out("clInitializeBuilder: Inner Class detected. "
167: + "Reject building class initialisation methods.");
168: }
169: class2initializers.put(td, getInitializers(td));
170: if (td instanceof ClassDeclaration
171: && td != javaLangObject) {
172: ClassDeclaration cd = (ClassDeclaration) td;
173: TypeReference super Type;
174: if (cd.getExtendedTypes() != null) {
175: super Type = (TypeReference) cd
176: .getExtendedTypes().getTypeReferenceAt(
177: 0).deepClone();
178: } else {
179: super Type = new TypeReference(
180: createJavaLangPackageReference(),
181: new Identifier("Object"));
182: }
183: class2super .put(cd, super Type);
184: }
185: }
186:
187: }
188: setProblemReport(NO_PROBLEM);
189: return NO_PROBLEM;
190: }
191:
192: /**
193: * creates passive field reference access
194: */
195: protected PassiveExpression passiveFieldReference(Identifier id) {
196: return new PassiveExpression(new FieldReference(id));
197: }
198:
199: /**
200: * creates a recoder copy assignment
201: */
202: protected CopyAssignment assign(Expression left, Expression right) {
203: return new CopyAssignment(left, right);
204: }
205:
206: /**
207: * creates the following catch clause
208: * <code>
209: * catch (<i>caughtType</i> <i>caughtParam</i>) {
210: * <classInitializationInProgress>=false;
211: * <classClassErroneous>=true;
212: * t;
213: * }
214: * </code>
215: */
216: private Catch createCatchClause(String caughtType,
217: String caughtParam, Throw t) {
218:
219: StatementMutableList catcher = new StatementArrayList(3);
220:
221: CopyAssignment resetInitInProgress = assign(
222: passiveFieldReference(new ImplicitIdentifier(
223: ImplicitFieldAdder.IMPLICIT_CLASS_INIT_IN_PROGRESS)),
224: new BooleanLiteral(false));
225:
226: CopyAssignment markErroneous = assign(
227: passiveFieldReference(new ImplicitIdentifier(
228: ImplicitFieldAdder.IMPLICIT_CLASS_ERRONEOUS)),
229: new BooleanLiteral(true));
230:
231: ParameterDeclaration param = new ParameterDeclaration(
232: new TypeReference(createJavaLangPackageReference(),
233: new Identifier(caughtType)), new Identifier(
234: caughtParam));
235:
236: catcher.add((Statement) resetInitInProgress.deepClone());
237: catcher.add((Statement) markErroneous.deepClone());
238:
239: catcher.add(t);
240:
241: return new Catch(param, new StatementBlock(catcher));
242: }
243:
244: /**
245: * around the initializers there is a try block that catches
246: * eventually thrown errors or exceptions and handles them in a
247: * special way
248: */
249: private Try createInitializerExecutionTryBlock(TypeDeclaration td) {
250:
251: // try block
252: StatementMutableList initializerExecutionBody;
253:
254: initializerExecutionBody = (StatementMutableList) class2initializers
255: .get(td);
256: if (initializerExecutionBody == null) {
257: initializerExecutionBody = new StatementArrayList(20);
258: }
259:
260: if (td instanceof ClassDeclaration && td != javaLangObject) {
261: ClassDeclaration cd = (ClassDeclaration) td;
262: initializerExecutionBody
263: .insert(
264: 0,
265: new PassiveExpression(
266: new MethodReference(
267: (TypeReference) ((TypeReference) class2super
268: .get(cd))
269: .deepClone(),
270: new ImplicitIdentifier(
271: ClassInitializeMethodBuilder.CLASS_INITIALIZE_IDENTIFIER))));
272: }
273:
274: // catch clauses
275:
276: BranchMutableList catchClauses = new BranchArrayList(2);
277:
278: catchClauses.add(createCatchClause("Error", "err", new Throw(
279: new VariableReference(new Identifier("err")))));
280:
281: ExpressionMutableList exceptionInInitializerArguments = new ExpressionArrayList(
282: 1);
283: exceptionInInitializerArguments.add(new VariableReference(
284: new Identifier("twa")));
285:
286: Throw t = new Throw(new New(null, new TypeReference(
287: createJavaLangPackageReference(), new Identifier(
288: "ExceptionInInitializerError")),
289: exceptionInInitializerArguments));
290:
291: catchClauses.add(createCatchClause("Throwable", "twa", t));
292:
293: return new Try(new StatementBlock(initializerExecutionBody),
294: catchClauses);
295: }
296:
297: /**
298: * creates the body of the initialize method
299: */
300: private StatementBlock createInitializeMethodBody(TypeDeclaration td) {
301:
302: StatementMutableList methodBody = new StatementArrayList(1);
303: StatementMutableList clInitializeBody = new StatementArrayList(
304: 2);
305: StatementMutableList clInitNotInProgressBody = new StatementArrayList(
306: 20);
307:
308: StatementMutableList clNotPreparedBody = new StatementArrayList(
309: 1);
310: clNotPreparedBody
311: .add(new PassiveExpression(
312: new MethodReference(
313: new ImplicitIdentifier(
314: ClassPreparationMethodBuilder.CLASS_PREPARE_IDENTIFIER))));
315:
316: If isClassPrepared = new If(new LogicalNot(
317: passiveFieldReference(new ImplicitIdentifier(
318: ImplicitFieldAdder.IMPLICIT_CLASS_PREPARED))),
319: new Then(new StatementBlock(clNotPreparedBody)));
320:
321: clInitNotInProgressBody.add(isClassPrepared);
322:
323: StatementMutableList clErroneousBody = new StatementArrayList(1);
324: clErroneousBody.add(new Throw(new New(null, new TypeReference(
325: createJavaLangPackageReference(), new Identifier(
326: "NoClassDefFoundError")), null)));
327: If isClassErroneous = new If(
328: passiveFieldReference(new ImplicitIdentifier(
329: ImplicitFieldAdder.IMPLICIT_CLASS_ERRONEOUS)),
330: new Then(new StatementBlock(clErroneousBody)));
331:
332: clInitNotInProgressBody.add(isClassErroneous);
333:
334: // @(CLASS_INIT_IN_PROGRESS) = true;
335: clInitNotInProgressBody
336: .add(assign(
337: passiveFieldReference(new ImplicitIdentifier(
338: ImplicitFieldAdder.IMPLICIT_CLASS_INIT_IN_PROGRESS)),
339: new BooleanLiteral(true)));
340:
341: // create try block in initialize method
342: clInitNotInProgressBody
343: .add(createInitializerExecutionTryBlock(td));
344: clInitNotInProgressBody
345: .add(assign(
346: passiveFieldReference((new ImplicitIdentifier(
347: ImplicitFieldAdder.IMPLICIT_CLASS_INIT_IN_PROGRESS))),
348: new BooleanLiteral(false)));
349: clInitNotInProgressBody.add(assign(
350: passiveFieldReference((new ImplicitIdentifier(
351: ImplicitFieldAdder.IMPLICIT_CLASS_ERRONEOUS))),
352: new BooleanLiteral(false)));
353: clInitNotInProgressBody
354: .add(assign(
355: passiveFieldReference((new ImplicitIdentifier(
356: ImplicitFieldAdder.IMPLICIT_CLASS_INITIALIZED))),
357: new BooleanLiteral(true)));
358:
359: If isClassInitializationInProgress = new If(
360: new LogicalNot(
361: passiveFieldReference(new ImplicitIdentifier(
362: ImplicitFieldAdder.IMPLICIT_CLASS_INIT_IN_PROGRESS))),
363: new Then(new StatementBlock(clInitNotInProgressBody)));
364:
365: clInitializeBody.add(isClassInitializationInProgress);
366: If isClassInitialized = new If(
367: new LogicalNot(
368: passiveFieldReference(new ImplicitIdentifier(
369: ImplicitFieldAdder.IMPLICIT_CLASS_INITIALIZED))),
370: new Then(new StatementBlock(clInitializeBody)));
371:
372: methodBody.add(isClassInitialized);
373:
374: return new StatementBlock(methodBody);
375: }
376:
377: /**
378: * creates the static method <code><clprepare></code> for the
379: * given type declaration
380: * @param td the TypeDeclaration to which the new created method
381: * will be attached
382: * @return the created class preparation method
383: */
384: private MethodDeclaration createInitializeMethod(TypeDeclaration td) {
385: ModifierMutableList modifiers = new ModifierArrayList(2);
386: modifiers.add(new Static());
387: modifiers.add(new Public());
388: return new MethodDeclaration(modifiers,
389: null, // return type is void
390: new ImplicitIdentifier(CLASS_INITIALIZE_IDENTIFIER),
391: new ParameterDeclarationArrayList(0), null, // no declared throws
392: createInitializeMethodBody(td));
393: }
394:
395: /**
396: * entry method for the constructor normalform builder
397: * @param td the TypeDeclaration
398: */
399: protected void makeExplicit(TypeDeclaration td) {
400: attach(createInitializeMethod(td), td, 0);
401: // for debug
402: // java.io.StringWriter sw = new java.io.StringWriter();
403: // if (td instanceof ClassDeclaration) {
404: // services.getProgramFactory().getPrettyPrinter(sw).visitClassDeclaration((ClassDeclaration)td);
405: // } else {
406: // services.getProgramFactory().getPrettyPrinter(sw).visitInterfaceDeclaration((InterfaceDeclaration)td);
407: // }
408: // System.out.println(sw.toString());
409: // try { sw.close(); } catch (Exception e) {}
410: }
411:
412: }
|