/******************************************************************************* | |
* Copyright (c) 2000, 2004 IBM Corporation and others. | |
* All rights reserved. This program and the accompanying materials | |
* are made available under the terms of the Common Public License v1.0 | |
* which accompanies this distribution, and is available at | |
* http://www.eclipse.org/legal/cpl-v10.html | |
* | |
* Contributors: | |
* IBM Corporation - initial API and implementation | |
*******************************************************************************/ | |
package org.eclipse.wst.jsdt.internal.compiler.ast; | |
import org.eclipse.wst.jsdt.internal.compiler.ASTVisitor; | |
import org.eclipse.wst.jsdt.internal.compiler.*; | |
import org.eclipse.wst.jsdt.internal.compiler.codegen.*; | |
import org.eclipse.wst.jsdt.internal.compiler.flow.*; | |
import org.eclipse.wst.jsdt.internal.compiler.lookup.*; | |
import org.eclipse.wst.jsdt.internal.compiler.parser.*; | |
import org.eclipse.wst.jsdt.internal.compiler.problem.*; | |
public class Clinit extends AbstractMethodDeclaration { | |
public final static char[] ConstantPoolName = "<clinit>".toCharArray(); //$NON-NLS-1$ | |
private FieldBinding assertionSyntheticFieldBinding = null; | |
private FieldBinding classLiteralSyntheticField = null; | |
public Clinit(CompilationResult compilationResult) { | |
super(compilationResult); | |
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.DEAD_END); | |
// check for missing returning path | |
this.needFreeReturn = flowInfo.isReachable(); | |
// 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.original())); | |
// 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.wst.jsdt.internal.compiler.lookup.ClassScope | |
* @param classFile org.eclipse.wst.jsdt.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; | |
this.generateCode(classScope, classFile, clinitOffset); | |
} 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. | |
if (e.compilationResult == CodeStream.RESTART_IN_WIDE_MODE) { | |
// a branch target required a goto_w, restart code gen in wide mode. | |
try { | |
classFile.contentsOffset = clinitOffset; | |
classFile.methodCount--; | |
classFile.codeStream.wideMode = true; // request wide mode | |
this.generateCode(classScope, classFile, clinitOffset); | |
// restart method generation | |
} catch (AbortMethod e2) { | |
classFile.contentsOffset = clinitOffset; | |
classFile.methodCount--; | |
} | |
} else { | |
// produce a problem method accounting for this fatal error | |
classFile.contentsOffset = clinitOffset; | |
classFile.methodCount--; | |
} | |
} | |
} | |
/** | |
* Bytecode generation for a <clinit> method | |
* | |
* @param classScope org.eclipse.wst.jsdt.internal.compiler.lookup.ClassScope | |
* @param classFile org.eclipse.wst.jsdt.internal.compiler.codegen.ClassFile | |
*/ | |
private void generateCode( | |
ClassScope classScope, | |
ClassFile classFile, | |
int clinitOffset) { | |
ConstantPool constantPool = classFile.constantPool; | |
int constantPoolOffset = constantPool.currentOffset; | |
int constantPoolIndex = constantPool.currentIndex; | |
classFile.generateMethodInfoHeaderForClinit(); | |
int codeAttributeOffset = classFile.contentsOffset; | |
classFile.generateCodeAttributeHeader(); | |
CodeStream codeStream = classFile.codeStream; | |
this.resolve(classScope); | |
codeStream.reset(this, classFile); | |
TypeDeclaration declaringType = classScope.referenceContext; | |
// initialize local positions - including initializer scope. | |
MethodScope staticInitializerScope = declaringType.staticInitializerScope; | |
staticInitializerScope.computeLocalVariablePositions(0, codeStream); | |
// 1.4 feature | |
// This has to be done before any other initialization | |
if (this.assertionSyntheticFieldBinding != null) { | |
// generate code related to the activation of assertion for this class | |
codeStream.generateClassLiteralAccessForType( | |
classScope.enclosingSourceType(), | |
classLiteralSyntheticField); | |
codeStream.invokeJavaLangClassDesiredAssertionStatus(); | |
Label falseLabel = new Label(codeStream); | |
codeStream.ifne(falseLabel); | |
codeStream.iconst_1(); | |
Label jumpLabel = new Label(codeStream); | |
codeStream.goto_(jumpLabel); | |
falseLabel.place(); | |
codeStream.iconst_0(); | |
jumpLabel.place(); | |
codeStream.putstatic(this.assertionSyntheticFieldBinding); | |
} | |
// 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 (this.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.sourceStart); | |
classFile.completeCodeAttributeForClinit(codeAttributeOffset); | |
} | |
} | |
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 StringBuffer print(int tab, StringBuffer output) { | |
printIndent(tab, output).append("<clinit>()"); //$NON-NLS-1$ | |
printBody(tab + 1, output); | |
return output; | |
} | |
public void resolve(ClassScope classScope) { | |
this.scope = new MethodScope(classScope, classScope.referenceContext, true); | |
} | |
public void traverse( | |
ASTVisitor visitor, | |
ClassScope classScope) { | |
visitor.visit(this, classScope); | |
visitor.endVisit(this, classScope); | |
} | |
// 1.4 feature | |
public void setAssertionSupport(FieldBinding assertionSyntheticFieldBinding, boolean needClassLiteralField) { | |
this.assertionSyntheticFieldBinding = assertionSyntheticFieldBinding; | |
// we need to add the field right now, because the field infos are generated before the methods | |
SourceTypeBinding sourceType = | |
this.scope.outerMostMethodScope().enclosingSourceType(); | |
if (needClassLiteralField) { | |
this.classLiteralSyntheticField = | |
sourceType.addSyntheticField(sourceType, scope); | |
} | |
} | |
} |