001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdt.internal.compiler.ast;
011:
012: import org.eclipse.jdt.internal.compiler.ASTVisitor;
013: import org.eclipse.jdt.internal.compiler.ClassFile;
014: import org.eclipse.jdt.internal.compiler.CompilationResult;
015: import org.eclipse.jdt.internal.compiler.codegen.BranchLabel;
016: import org.eclipse.jdt.internal.compiler.codegen.CodeStream;
017: import org.eclipse.jdt.internal.compiler.codegen.ConstantPool;
018: import org.eclipse.jdt.internal.compiler.flow.ExceptionHandlingFlowContext;
019: import org.eclipse.jdt.internal.compiler.flow.FlowInfo;
020: import org.eclipse.jdt.internal.compiler.flow.InitializationFlowContext;
021: import org.eclipse.jdt.internal.compiler.lookup.Binding;
022: import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
023: import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
024: import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
025: import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
026: import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
027: import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
028: import org.eclipse.jdt.internal.compiler.parser.Parser;
029: import org.eclipse.jdt.internal.compiler.problem.AbortMethod;
030:
031: public class Clinit extends AbstractMethodDeclaration {
032:
033: private FieldBinding assertionSyntheticFieldBinding = null;
034: private FieldBinding classLiteralSyntheticField = null;
035:
036: public Clinit(CompilationResult compilationResult) {
037: super (compilationResult);
038: modifiers = 0;
039: selector = TypeConstants.CLINIT;
040: }
041:
042: public void analyseCode(ClassScope classScope,
043: InitializationFlowContext staticInitializerFlowContext,
044: FlowInfo flowInfo) {
045:
046: if (ignoreFurtherInvestigation)
047: return;
048: try {
049: ExceptionHandlingFlowContext clinitContext = new ExceptionHandlingFlowContext(
050: staticInitializerFlowContext.parent, this ,
051: Binding.NO_EXCEPTIONS, scope, FlowInfo.DEAD_END);
052:
053: // check for missing returning path
054: if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) {
055: this .bits |= ASTNode.NeedFreeReturn;
056: }
057:
058: // check missing blank final field initializations
059: flowInfo = flowInfo
060: .mergedWith(staticInitializerFlowContext.initsOnReturn);
061: FieldBinding[] fields = scope.enclosingSourceType()
062: .fields();
063: for (int i = 0, count = fields.length; i < count; i++) {
064: FieldBinding field;
065: if ((field = fields[i]).isStatic() && field.isFinal()
066: && (!flowInfo.isDefinitelyAssigned(fields[i]))) {
067: scope.problemReporter()
068: .uninitializedBlankFinalField(
069: field,
070: scope.referenceType()
071: .declarationOf(
072: field.original()));
073: // can complain against the field decl, since only one <clinit>
074: }
075: }
076: // check static initializers thrown exceptions
077: staticInitializerFlowContext.checkInitializerExceptions(
078: scope, clinitContext, flowInfo);
079: } catch (AbortMethod e) {
080: this .ignoreFurtherInvestigation = true;
081: }
082: }
083:
084: /**
085: * Bytecode generation for a <clinit> method
086: *
087: * @param classScope org.eclipse.jdt.internal.compiler.lookup.ClassScope
088: * @param classFile org.eclipse.jdt.internal.compiler.codegen.ClassFile
089: */
090: public void generateCode(ClassScope classScope, ClassFile classFile) {
091:
092: int clinitOffset = 0;
093: if (ignoreFurtherInvestigation) {
094: // should never have to add any <clinit> problem method
095: return;
096: }
097: try {
098: clinitOffset = classFile.contentsOffset;
099: this .generateCode(classScope, classFile, clinitOffset);
100: } catch (AbortMethod e) {
101: // should never occur
102: // the clinit referenceContext is the type declaration
103: // All clinit problems will be reported against the type: AbortType instead of AbortMethod
104: // reset the contentsOffset to the value before generating the clinit code
105: // decrement the number of method info as well.
106: // This is done in the addProblemMethod and addProblemConstructor for other
107: // cases.
108: if (e.compilationResult == CodeStream.RESTART_IN_WIDE_MODE) {
109: // a branch target required a goto_w, restart code gen in wide mode.
110: try {
111: classFile.contentsOffset = clinitOffset;
112: classFile.methodCount--;
113: classFile.codeStream.wideMode = true; // request wide mode
114: this .generateCode(classScope, classFile,
115: clinitOffset);
116: // restart method generation
117: } catch (AbortMethod e2) {
118: classFile.contentsOffset = clinitOffset;
119: classFile.methodCount--;
120: }
121: } else {
122: // produce a problem method accounting for this fatal error
123: classFile.contentsOffset = clinitOffset;
124: classFile.methodCount--;
125: }
126: }
127: }
128:
129: /**
130: * Bytecode generation for a <clinit> method
131: *
132: * @param classScope org.eclipse.jdt.internal.compiler.lookup.ClassScope
133: * @param classFile org.eclipse.jdt.internal.compiler.codegen.ClassFile
134: */
135: private void generateCode(ClassScope classScope,
136: ClassFile classFile, int clinitOffset) {
137:
138: ConstantPool constantPool = classFile.constantPool;
139: int constantPoolOffset = constantPool.currentOffset;
140: int constantPoolIndex = constantPool.currentIndex;
141: classFile.generateMethodInfoHeaderForClinit();
142: int codeAttributeOffset = classFile.contentsOffset;
143: classFile.generateCodeAttributeHeader();
144: CodeStream codeStream = classFile.codeStream;
145: this .resolve(classScope);
146:
147: codeStream.reset(this , classFile);
148: TypeDeclaration declaringType = classScope.referenceContext;
149:
150: // initialize local positions - including initializer scope.
151: MethodScope staticInitializerScope = declaringType.staticInitializerScope;
152: staticInitializerScope.computeLocalVariablePositions(0,
153: codeStream);
154:
155: // 1.4 feature
156: // This has to be done before any other initialization
157: if (this .assertionSyntheticFieldBinding != null) {
158: // generate code related to the activation of assertion for this class
159: codeStream.generateClassLiteralAccessForType(classScope
160: .outerMostClassScope().enclosingSourceType(),
161: this .classLiteralSyntheticField);
162: codeStream.invokeJavaLangClassDesiredAssertionStatus();
163: BranchLabel falseLabel = new BranchLabel(codeStream);
164: codeStream.ifne(falseLabel);
165: codeStream.iconst_1();
166: BranchLabel jumpLabel = new BranchLabel(codeStream);
167: codeStream.decrStackSize(1);
168: codeStream.goto_(jumpLabel);
169: falseLabel.place();
170: codeStream.iconst_0();
171: jumpLabel.place();
172: codeStream.putstatic(this .assertionSyntheticFieldBinding);
173: }
174: // generate static fields/initializers/enum constants
175: final FieldDeclaration[] fieldDeclarations = declaringType.fields;
176: BlockScope lastInitializerScope = null;
177: if (TypeDeclaration.kind(declaringType.modifiers) == TypeDeclaration.ENUM_DECL) {
178: int enumCount = 0;
179: int remainingFieldCount = 0;
180: if (fieldDeclarations != null) {
181: for (int i = 0, max = fieldDeclarations.length; i < max; i++) {
182: FieldDeclaration fieldDecl = fieldDeclarations[i];
183: if (fieldDecl.isStatic()) {
184: if (fieldDecl.getKind() == AbstractVariableDeclaration.ENUM_CONSTANT) {
185: fieldDecl.generateCode(
186: staticInitializerScope, codeStream);
187: enumCount++;
188: } else {
189: remainingFieldCount++;
190: }
191: }
192: }
193: }
194: // enum need to initialize $VALUES synthetic cache of enum constants
195: // $VALUES := new <EnumType>[<enumCount>]
196: codeStream.generateInlinedValue(enumCount);
197: codeStream.anewarray(declaringType.binding);
198: if (enumCount > 0) {
199: if (fieldDeclarations != null) {
200: for (int i = 0, max = fieldDeclarations.length; i < max; i++) {
201: FieldDeclaration fieldDecl = fieldDeclarations[i];
202: // $VALUES[i] = <enum-constant-i>
203: if (fieldDecl.getKind() == AbstractVariableDeclaration.ENUM_CONSTANT) {
204: codeStream.dup();
205: codeStream
206: .generateInlinedValue(fieldDecl.binding.id);
207: codeStream.getstatic(fieldDecl.binding);
208: codeStream.aastore();
209: }
210: }
211: }
212: }
213: codeStream
214: .putstatic(declaringType.enumValuesSyntheticfield);
215: if (remainingFieldCount != 0) {
216: // if fields that are not enum constants need to be generated (static initializer/static field)
217: for (int i = 0, max = fieldDeclarations.length; i < max; i++) {
218: FieldDeclaration fieldDecl = fieldDeclarations[i];
219: switch (fieldDecl.getKind()) {
220: case AbstractVariableDeclaration.ENUM_CONSTANT:
221: break;
222: case AbstractVariableDeclaration.INITIALIZER:
223: if (!fieldDecl.isStatic())
224: break;
225: lastInitializerScope = ((Initializer) fieldDecl).block.scope;
226: fieldDecl.generateCode(staticInitializerScope,
227: codeStream);
228: break;
229: case AbstractVariableDeclaration.FIELD:
230: if (!fieldDecl.binding.isStatic())
231: break;
232: lastInitializerScope = null;
233: fieldDecl.generateCode(staticInitializerScope,
234: codeStream);
235: break;
236: }
237: }
238: }
239: } else {
240: if (fieldDeclarations != null) {
241: for (int i = 0, max = fieldDeclarations.length; i < max; i++) {
242: FieldDeclaration fieldDecl = fieldDeclarations[i];
243: switch (fieldDecl.getKind()) {
244: case AbstractVariableDeclaration.INITIALIZER:
245: if (!fieldDecl.isStatic())
246: break;
247: lastInitializerScope = ((Initializer) fieldDecl).block.scope;
248: fieldDecl.generateCode(staticInitializerScope,
249: codeStream);
250: break;
251: case AbstractVariableDeclaration.FIELD:
252: if (!fieldDecl.binding.isStatic())
253: break;
254: lastInitializerScope = null;
255: fieldDecl.generateCode(staticInitializerScope,
256: codeStream);
257: break;
258: }
259: }
260: }
261: }
262:
263: if (codeStream.position == 0) {
264: // do not need to output a Clinit if no bytecodes
265: // so we reset the offset inside the byte array contents.
266: classFile.contentsOffset = clinitOffset;
267: // like we don't addd a method we need to undo the increment on the method count
268: classFile.methodCount--;
269: // reset the constant pool to its state before the clinit
270: constantPool.resetForClinit(constantPoolIndex,
271: constantPoolOffset);
272: } else {
273: if ((this .bits & ASTNode.NeedFreeReturn) != 0) {
274: int before = codeStream.position;
275: codeStream.return_();
276: if (lastInitializerScope != null) {
277: // expand the last initializer variables to include the trailing return
278: codeStream.updateLastRecordedEndPC(
279: lastInitializerScope, before);
280: }
281: }
282: // Record the end of the clinit: point to the declaration of the class
283: codeStream
284: .recordPositionsFrom(0, declaringType.sourceStart);
285: classFile
286: .completeCodeAttributeForClinit(codeAttributeOffset);
287: }
288: }
289:
290: public boolean isClinit() {
291:
292: return true;
293: }
294:
295: public boolean isInitializationMethod() {
296:
297: return true;
298: }
299:
300: public boolean isStatic() {
301:
302: return true;
303: }
304:
305: public void parseStatements(Parser parser,
306: CompilationUnitDeclaration unit) {
307: //the clinit is filled by hand ....
308: }
309:
310: public StringBuffer print(int tab, StringBuffer output) {
311:
312: printIndent(tab, output).append("<clinit>()"); //$NON-NLS-1$
313: printBody(tab + 1, output);
314: return output;
315: }
316:
317: public void resolve(ClassScope classScope) {
318:
319: this .scope = new MethodScope(classScope,
320: classScope.referenceContext, true);
321: }
322:
323: public void traverse(ASTVisitor visitor, ClassScope classScope) {
324:
325: visitor.visit(this , classScope);
326: visitor.endVisit(this , classScope);
327: }
328:
329: public void setAssertionSupport(
330: FieldBinding assertionSyntheticFieldBinding,
331: boolean needClassLiteralField) {
332:
333: this .assertionSyntheticFieldBinding = assertionSyntheticFieldBinding;
334:
335: // we need to add the field right now, because the field infos are generated before the methods
336: if (needClassLiteralField) {
337: SourceTypeBinding sourceType = this .scope
338: .outerMostClassScope().enclosingSourceType();
339: // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=22334
340: if (!sourceType.isInterface() && !sourceType.isBaseType()) {
341: this.classLiteralSyntheticField = sourceType
342: .addSyntheticFieldForClassLiteral(sourceType,
343: scope);
344: }
345: }
346: }
347:
348: }
|