*** empty log message ***
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / compiler / ast / Clinit.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2001, 2002 International Business Machines Corp. and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v0.5 
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v05.html
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  ******************************************************************************/
11 package net.sourceforge.phpdt.internal.compiler.ast;
12
13 import net.sourceforge.phpdt.internal.compiler.ClassFile;
14 import net.sourceforge.phpdt.internal.compiler.CompilationResult;
15 import net.sourceforge.phpdt.internal.compiler.IAbstractSyntaxTreeVisitor;
16 import net.sourceforge.phpdt.internal.compiler.codegen.CodeStream;
17 import net.sourceforge.phpdt.internal.compiler.codegen.ConstantPool;
18 import net.sourceforge.phpdt.internal.compiler.codegen.Label;
19 import net.sourceforge.phpdt.internal.compiler.flow.ExceptionHandlingFlowContext;
20 import net.sourceforge.phpdt.internal.compiler.flow.FlowInfo;
21 import net.sourceforge.phpdt.internal.compiler.flow.InitializationFlowContext;
22 import net.sourceforge.phpdt.internal.compiler.lookup.ClassScope;
23 import net.sourceforge.phpdt.internal.compiler.lookup.FieldBinding;
24 import net.sourceforge.phpdt.internal.compiler.lookup.MethodScope;
25 import net.sourceforge.phpdt.internal.compiler.lookup.SourceTypeBinding;
26 import net.sourceforge.phpdt.internal.compiler.parser.Parser;
27 import net.sourceforge.phpdt.internal.compiler.problem.AbortMethod;
28
29 public class Clinit extends AbstractMethodDeclaration {
30         
31         public final static char[] ConstantPoolName = "<clinit>".toCharArray(); //$NON-NLS-1$
32
33         private FieldBinding assertionSyntheticFieldBinding = null;
34         private FieldBinding classLiteralSyntheticField = null;
35
36         public Clinit(CompilationResult compilationResult) {
37                 super(compilationResult);
38                 modifiers = 0;
39                 selector = ConstantPoolName;
40         }
41
42         public void analyseCode(
43                 ClassScope classScope,
44                 InitializationFlowContext staticInitializerFlowContext,
45                 FlowInfo flowInfo) {
46
47                 if (ignoreFurtherInvestigation)
48                         return;
49                 try {
50                         ExceptionHandlingFlowContext clinitContext =
51                                 new ExceptionHandlingFlowContext(
52                                         staticInitializerFlowContext.parent,
53                                         this,
54                                         NoExceptions,
55                                         scope,
56                                         FlowInfo.DeadEnd);
57
58                         // check for missing returning path
59                         needFreeReturn =
60                                 !((flowInfo == FlowInfo.DeadEnd) || flowInfo.isFakeReachable());
61
62                         // check missing blank final field initializations
63                         flowInfo = flowInfo.mergedWith(staticInitializerFlowContext.initsOnReturn);
64                         FieldBinding[] fields = scope.enclosingSourceType().fields();
65                         for (int i = 0, count = fields.length; i < count; i++) {
66                                 FieldBinding field;
67                                 if ((field = fields[i]).isStatic()
68                                         && field.isFinal()
69                                         && (!flowInfo.isDefinitelyAssigned(fields[i]))) {
70                                         scope.problemReporter().uninitializedBlankFinalField(
71                                                 field,
72                                                 scope.referenceType().declarationOf(field));
73                                         // can complain against the field decl, since only one <clinit>
74                                 }
75                         }
76                         // check static initializers thrown exceptions
77                         staticInitializerFlowContext.checkInitializerExceptions(
78                                 scope,
79                                 clinitContext,
80                                 flowInfo);
81                 } catch (AbortMethod e) {
82                         this.ignoreFurtherInvestigation = true;
83                 }
84         }
85
86         /**
87          * Bytecode generation for a <clinit> method
88          *
89          * @param classScope org.eclipse.jdt.internal.compiler.lookup.ClassScope
90          * @param classFile org.eclipse.jdt.internal.compiler.codegen.ClassFile
91          */
92         public void generateCode(ClassScope classScope, ClassFile classFile) {
93
94                 int clinitOffset = 0;
95                 if (ignoreFurtherInvestigation) {
96                         // should never have to add any <clinit> problem method
97                         return;
98                 }
99                 try {
100                         clinitOffset = classFile.contentsOffset;
101                         this.generateCode(classScope, classFile, clinitOffset);
102                 } catch (AbortMethod e) {
103                         // should never occur
104                         // the clinit referenceContext is the type declaration
105                         // All clinit problems will be reported against the type: AbortType instead of AbortMethod
106                         // reset the contentsOffset to the value before generating the clinit code
107                         // decrement the number of method info as well.
108                         // This is done in the addProblemMethod and addProblemConstructor for other
109                         // cases.
110                         if (e.compilationResult == CodeStream.RESTART_IN_WIDE_MODE) {
111                                 // a branch target required a goto_w, restart code gen in wide mode.
112                                 try {
113                                         if (statements != null) {
114                                                 for (int i = 0, max = statements.length; i < max; i++)
115                                                         statements[i].resetStateForCodeGeneration();
116                                         }
117                                         classFile.contentsOffset = clinitOffset;
118                                         classFile.methodCount--;
119                                         classFile.codeStream.wideMode = true; // request wide mode 
120                                         this.generateCode(classScope, classFile, clinitOffset);
121                                         // restart method generation
122                                 } catch (AbortMethod e2) {
123                                         classFile.contentsOffset = clinitOffset;
124                                         classFile.methodCount--;
125                                 }
126                         } else {
127                                 // produce a problem method accounting for this fatal error
128                                 classFile.contentsOffset = clinitOffset;
129                                 classFile.methodCount--;
130                         }
131                 }
132         }
133
134         /**
135          * Bytecode generation for a <clinit> method
136          *
137          * @param classScope org.eclipse.jdt.internal.compiler.lookup.ClassScope
138          * @param classFile org.eclipse.jdt.internal.compiler.codegen.ClassFile
139          */
140         private void generateCode(
141                 ClassScope classScope,
142                 ClassFile classFile,
143                 int clinitOffset) {
144
145                 ConstantPool constantPool = classFile.constantPool;
146                 int constantPoolOffset = constantPool.currentOffset;
147                 int constantPoolIndex = constantPool.currentIndex;
148                 classFile.generateMethodInfoHeaderForClinit();
149                 int codeAttributeOffset = classFile.contentsOffset;
150                 classFile.generateCodeAttributeHeader();
151                 CodeStream codeStream = classFile.codeStream;
152                 this.resolve(classScope);
153
154                 codeStream.reset(this, classFile);
155                 TypeDeclaration declaringType = classScope.referenceContext;
156
157                 // initialize local positions - including initializer scope.
158                 scope.computeLocalVariablePositions(0, codeStream); // should not be necessary
159                 MethodScope staticInitializerScope = declaringType.staticInitializerScope;
160                 staticInitializerScope.computeLocalVariablePositions(0, codeStream);
161                 // offset by the argument size
162
163                 // 1.4 feature
164                 // This has to be done before any other initialization
165                 if (this.assertionSyntheticFieldBinding != null) {
166                         // generate code related to the activation of assertion for this class
167                         codeStream.generateClassLiteralAccessForType(
168                                 classScope.enclosingSourceType(),
169                                 classLiteralSyntheticField);
170                         codeStream.invokeJavaLangClassDesiredAssertionStatus();
171                         Label falseLabel = new Label(codeStream);
172                         codeStream.ifne(falseLabel);
173                         codeStream.iconst_1();
174                         Label jumpLabel = new Label(codeStream);
175                         codeStream.goto_(jumpLabel);
176                         falseLabel.place();
177                         codeStream.iconst_0();
178                         jumpLabel.place();
179                         codeStream.putstatic(this.assertionSyntheticFieldBinding);
180                 }
181                 // generate initializers
182                 if (declaringType.fields != null) {
183                         for (int i = 0, max = declaringType.fields.length; i < max; i++) {
184                                 FieldDeclaration fieldDecl;
185                                 if ((fieldDecl = declaringType.fields[i]).isStatic()) {
186                                         fieldDecl.generateCode(staticInitializerScope, codeStream);
187                                 }
188                         }
189                 }
190                 if (codeStream.position == 0) {
191                         // do not need to output a Clinit if no bytecodes
192                         // so we reset the offset inside the byte array contents.
193                         classFile.contentsOffset = clinitOffset;
194                         // like we don't addd a method we need to undo the increment on the method count
195                         classFile.methodCount--;
196                         // reset the constant pool to its state before the clinit
197                         constantPool.resetForClinit(constantPoolIndex, constantPoolOffset);
198                 } else {
199                         if (needFreeReturn) {
200                                 int oldPosition = codeStream.position;
201                                 codeStream.return_();
202                                 codeStream.updateLocalVariablesAttribute(oldPosition);
203                         }
204                         // Record the end of the clinit: point to the declaration of the class
205                         codeStream.recordPositionsFrom(0, declaringType.sourceStart);
206                         classFile.completeCodeAttributeForClinit(codeAttributeOffset);
207                 }
208         }
209
210         public boolean isClinit() {
211
212                 return true;
213         }
214
215         public boolean isInitializationMethod() {
216
217                 return true;
218         }
219
220         public boolean isStatic() {
221
222                 return true;
223         }
224
225         public void parseStatements(Parser parser, CompilationUnitDeclaration unit) {
226                 //the clinit is filled by hand .... 
227         }
228
229         public void resolve(ClassScope scope) {
230
231                 this.scope = new MethodScope(scope, scope.referenceContext, true);
232         }
233
234         public String toString(int tab) {
235
236                 String s = ""; //$NON-NLS-1$
237                 s = s + tabString(tab);
238                 s = s + "<clinit>()"; //$NON-NLS-1$
239                 s = s + toStringStatements(tab + 1);
240                 return s;
241         }
242
243         public void traverse(
244                 IAbstractSyntaxTreeVisitor visitor,
245                 ClassScope classScope) {
246
247                 visitor.visit(this, classScope);
248                 visitor.endVisit(this, classScope);
249         }
250
251         // 1.4 feature
252         public void addSupportForAssertion(FieldBinding assertionSyntheticFieldBinding) {
253
254                 this.assertionSyntheticFieldBinding = assertionSyntheticFieldBinding;
255
256                 // we need to add the field right now, because the field infos are generated before the methods
257                 SourceTypeBinding sourceType =
258                         this.scope.outerMostMethodScope().enclosingSourceType();
259                 this.classLiteralSyntheticField =
260                         sourceType.addSyntheticField(sourceType, scope);
261         }
262
263 }