| /******************************************************************************* |
| * 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 Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.compiler.ast; |
| |
| import org.eclipse.jdt.internal.compiler.ASTVisitor; |
| import org.eclipse.jdt.internal.compiler.*; |
| import org.eclipse.jdt.internal.compiler.codegen.*; |
| import org.eclipse.jdt.internal.compiler.env.IGenericType; |
| 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 { |
| |
| private FieldBinding assertionSyntheticFieldBinding = null; |
| private FieldBinding classLiteralSyntheticField = null; |
| |
| public Clinit(CompilationResult compilationResult) { |
| super(compilationResult); |
| modifiers = 0; |
| selector = TypeConstants.CLINIT; |
| } |
| |
| 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.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; |
| 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.jdt.internal.compiler.lookup.ClassScope |
| * @param classFile org.eclipse.jdt.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 static fields/initializers/enum constants |
| final FieldDeclaration[] fieldDeclarations = declaringType.fields; |
| BlockScope lastInitializerScope = null; |
| if (declaringType.kind() == IGenericType.ENUM_DECL) { |
| int enumCount = 0; |
| int remainingFieldCount = 0; |
| if (fieldDeclarations != null) { |
| for (int i = 0, max = fieldDeclarations.length; i < max; i++) { |
| FieldDeclaration fieldDecl = fieldDeclarations[i]; |
| if (fieldDecl.isStatic()) { |
| if (fieldDecl.getKind() == AbstractVariableDeclaration.ENUM_CONSTANT) { |
| fieldDecl.generateCode(staticInitializerScope, codeStream); |
| enumCount++; |
| } else { |
| remainingFieldCount++; |
| } |
| } |
| } |
| } |
| // enum need to initialize $VALUES synthetic cache of enum constants |
| if (enumCount > 0) { |
| if (fieldDeclarations != null) { |
| // $VALUES := new <EnumType>[<enumCount>] |
| codeStream.generateInlinedValue(enumCount); |
| codeStream.anewarray(declaringType.binding); |
| for (int i = 0, max = fieldDeclarations.length; i < max; i++) { |
| FieldDeclaration fieldDecl = fieldDeclarations[i]; |
| // $VALUES[i] = <enum-constant-i> |
| if (fieldDecl.getKind() == AbstractVariableDeclaration.ENUM_CONSTANT) { |
| codeStream.dup(); |
| codeStream.generateInlinedValue(fieldDecl.binding.id); |
| codeStream.getstatic(fieldDecl.binding); |
| codeStream.aastore(); |
| } |
| } |
| codeStream.putstatic(declaringType.enumValuesSyntheticfield); |
| } |
| } |
| if (remainingFieldCount != 0) { |
| // if fields that are not enum constants need to be generated (static initializer/static field) |
| for (int i = 0, max = fieldDeclarations.length; i < max; i++) { |
| FieldDeclaration fieldDecl = fieldDeclarations[i]; |
| switch (fieldDecl.getKind()) { |
| case AbstractVariableDeclaration.ENUM_CONSTANT : |
| break; |
| case AbstractVariableDeclaration.INITIALIZER : |
| if (!fieldDecl.isStatic()) |
| break; |
| lastInitializerScope = ((Initializer) fieldDecl).block.scope; |
| fieldDecl.generateCode(staticInitializerScope, codeStream); |
| break; |
| case AbstractVariableDeclaration.FIELD : |
| if (!fieldDecl.binding.isStatic()) |
| break; |
| lastInitializerScope = null; |
| fieldDecl.generateCode(staticInitializerScope, codeStream); |
| break; |
| } |
| } |
| } |
| } else { |
| if (fieldDeclarations != null) { |
| for (int i = 0, max = fieldDeclarations.length; i < max; i++) { |
| FieldDeclaration fieldDecl = fieldDeclarations[i]; |
| switch (fieldDecl.getKind()) { |
| case AbstractVariableDeclaration.INITIALIZER : |
| if (!fieldDecl.isStatic()) |
| break; |
| lastInitializerScope = ((Initializer) fieldDecl).block.scope; |
| fieldDecl.generateCode(staticInitializerScope, codeStream); |
| break; |
| case AbstractVariableDeclaration.FIELD : |
| if (!fieldDecl.binding.isStatic()) |
| break; |
| lastInitializerScope = null; |
| fieldDecl.generateCode(staticInitializerScope, codeStream); |
| break; |
| } |
| } |
| } |
| } |
| |
| 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 before = codeStream.position; |
| codeStream.return_(); |
| if (lastInitializerScope != null) { |
| // expand the last initializer variables to include the trailing return |
| codeStream.updateLastRecordedEndPC(lastInitializerScope, before); |
| } |
| } |
| // 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); |
| } |
| |
| 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.addSyntheticFieldForClassLiteral(sourceType, scope); |
| } |
| } |
| |
| } |