| package org.eclipse.jdt.internal.compiler.ast; |
| |
| /* |
| * (c) Copyright IBM Corp. 2000, 2001. |
| * All Rights Reserved. |
| */ |
| import org.eclipse.jdt.internal.compiler.IAbstractSyntaxTreeVisitor; |
| import org.eclipse.jdt.internal.compiler.*; |
| import org.eclipse.jdt.internal.compiler.codegen.*; |
| import org.eclipse.jdt.internal.compiler.flow.*; |
| import org.eclipse.jdt.internal.compiler.lookup.*; |
| import org.eclipse.jdt.internal.compiler.parser.*; |
| import org.eclipse.jdt.internal.compiler.problem.*; |
| |
| public class Clinit extends AbstractMethodDeclaration { |
| public final static char[] ConstantPoolName = "<clinit>"/*nonNLS*/.toCharArray(); |
| public Clinit() { |
| modifiers = 0; |
| selector = ConstantPoolName; |
| } |
| public void analyseCode(ClassScope classScope, InitializationFlowContext staticInitializerFlowContext, FlowInfo flowInfo){ |
| |
| if (ignoreFurtherInvestigation) |
| return; |
| try { |
| ExceptionHandlingFlowContext clinitContext = new ExceptionHandlingFlowContext( |
| staticInitializerFlowContext.parent, |
| this, |
| NoExceptions, |
| scope, |
| FlowInfo.DeadEnd); |
| |
| // check for missing returning path |
| needFreeReturn = !((flowInfo == FlowInfo.DeadEnd) || flowInfo.isFakeReachable()); |
| |
| // check missing blank final field initializations |
| flowInfo = flowInfo.mergedWith(staticInitializerFlowContext.initsOnReturn); |
| FieldBinding[] fields = scope.enclosingSourceType().fields(); |
| for (int i = 0, count = fields.length; i < count; i++) { |
| FieldBinding field; |
| if ((field = fields[i]).isStatic() |
| && field.isFinal() |
| && (!flowInfo.isDefinitelyAssigned(fields[i]))) { |
| scope.problemReporter().uninitializedBlankFinalField(field, scope.referenceType().declarationOf(field)); // can complain against the field decl, since only one <clinit> |
| } |
| } |
| // check static initializers thrown exceptions |
| staticInitializerFlowContext.checkInitializerExceptions(scope, clinitContext, flowInfo); |
| } catch (AbortMethod e) { |
| this.ignoreFurtherInvestigation = true; |
| } |
| } |
| /** |
| * Bytecode generation for a <clinit> method |
| * |
| * @param classScope org.eclipse.jdt.internal.compiler.lookup.ClassScope |
| * @param classFile org.eclipse.jdt.internal.compiler.codegen.ClassFile |
| */ |
| public void generateCode(ClassScope classScope, ClassFile classFile) { |
| int clinitOffset = 0; |
| if (ignoreFurtherInvestigation) { |
| // should never have to add any <clinit> problem method |
| return; |
| } |
| try { |
| clinitOffset = classFile.contentsOffset; |
| ConstantPool constantPool = classFile.constantPool; |
| int constantPoolOffset = constantPool.currentOffset; |
| int constantPoolIndex = constantPool.currentIndex; |
| classFile.generateMethodInfoHeaderForClinit(); |
| int codeAttributeOffset = classFile.contentsOffset; |
| classFile.generateCodeAttributeHeader(); |
| CodeStream codeStream = classFile.codeStream; |
| codeStream.reset(this, classFile); |
| TypeDeclaration declaringType = classScope.referenceContext; |
| |
| // initialize local positions - including initializer scope. |
| scope.computeLocalVariablePositions(0, codeStream); // should not be necessary |
| MethodScope staticInitializerScope = declaringType.staticInitializerScope; |
| staticInitializerScope.computeLocalVariablePositions(0, codeStream); // offset by the argument size |
| |
| // generate initializers |
| if (declaringType.fields != null) { |
| for (int i = 0, max = declaringType.fields.length; i < max; i++) { |
| FieldDeclaration fieldDecl; |
| if ((fieldDecl = declaringType.fields[i]).isStatic()) { |
| fieldDecl.generateCode(staticInitializerScope, codeStream); |
| } |
| } |
| } |
| if (codeStream.position == 0) { |
| // do not need to output a Clinit if no bytecodes |
| // so we reset the offset inside the byte array contents. |
| classFile.contentsOffset = clinitOffset; |
| // like we don't addd a method we need to undo the increment on the method count |
| classFile.methodCount--; |
| // reset the constant pool to its state before the clinit |
| constantPool.resetForClinit(constantPoolIndex, constantPoolOffset); |
| } else { |
| if (needFreeReturn) { |
| int oldPosition = codeStream.position; |
| codeStream.return_(); |
| codeStream.updateLocalVariablesAttribute(oldPosition); |
| } |
| // Record the end of the clinit: point to the declaration of the class |
| codeStream.recordPositionsFrom(0, declaringType); |
| classFile.completeCodeAttributeForClinit(codeAttributeOffset); |
| } |
| } catch (AbortMethod e) { |
| // should never occur |
| // the clinit referenceContext is the type declaration |
| // All clinit problems will be reported against the type: AbortType instead of AbortMethod |
| // reset the contentsOffset to the value before generating the clinit code |
| // decrement the number of method info as well. |
| // This is done in the addProblemMethod and addProblemConstructor for other |
| // cases. |
| classFile.contentsOffset = clinitOffset; |
| classFile.methodCount--; |
| } |
| } |
| public boolean isClinit() { |
| return true; |
| } |
| public boolean isInitializationMethod(){ |
| return true; |
| } |
| public boolean isStatic() { |
| return true; |
| } |
| public void parseStatements(Parser parser, CompilationUnitDeclaration unit){ |
| //the clinit is filled by hand .... |
| } |
| public void resolve(ClassScope scope) { |
| this.scope = new MethodScope(scope, scope.referenceContext, true); |
| } |
| public String toString(int tab){ |
| /* slow code */ |
| |
| String s = ""/*nonNLS*/ ; |
| s = s + tabString(tab); |
| s = s + "<clinit>()"/*nonNLS*/ ; |
| s = s + toStringStatements(tab + 1); |
| return s ;} |
| public void traverse(IAbstractSyntaxTreeVisitor visitor, ClassScope classScope) { |
| visitor.visit(this, classScope); |
| visitor.endVisit(this, classScope); |
| } |
| } |