/*******************************************************************************
* Copyright (c) 2000, 2011 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.che.ide.ext.java.jdt.internal.compiler.ast;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.ASTVisitor;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.CompilationResult;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.flow.ExceptionHandlingFlowContext;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.flow.FlowInfo;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.flow.InitializationFlowContext;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup.Binding;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup.ClassScope;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup.FieldBinding;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup.MethodScope;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup.SourceTypeBinding;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup.TypeConstants;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.parser.Parser;
import org.eclipse.che.ide.ext.java.jdt.internal.compiler.problem.AbortMethod;
public class Clinit extends AbstractMethodDeclaration {
private static int ENUM_CONSTANTS_THRESHOLD = 2000;
private FieldBinding assertionSyntheticFieldBinding = null;
private FieldBinding classLiteralSyntheticField = null;
public Clinit(CompilationResult compilationResult) {
super(compilationResult);
this.modifiers = 0;
this.selector = TypeConstants.CLINIT;
}
@Override
public void analyseCode(ClassScope classScope, InitializationFlowContext staticInitializerFlowContext,
FlowInfo flowInfo) {
if (this.ignoreFurtherInvestigation) {
return;
}
try {
ExceptionHandlingFlowContext clinitContext =
new ExceptionHandlingFlowContext(staticInitializerFlowContext.parent, this, Binding.NO_EXCEPTIONS,
staticInitializerFlowContext, this.scope, FlowInfo.DEAD_END);
// check for missing returning path
if ((flowInfo.tagBits & FlowInfo.UNREACHABLE_OR_DEAD) == 0) {
this.bits |= ASTNode.NeedFreeReturn;
}
// check missing blank final field initializations
flowInfo = flowInfo.mergedWith(staticInitializerFlowContext.initsOnReturn);
FieldBinding[] fields = this.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]))) {
this.scope.problemReporter().uninitializedBlankFinalField(field,
this.scope.referenceType().declarationOf(field.original()));
// can complain against the field decl, since only one <clinit>
}
}
// check static initializers thrown exceptions
staticInitializerFlowContext.checkInitializerExceptions(this.scope, clinitContext, flowInfo);
} catch (AbortMethod e) {
this.ignoreFurtherInvestigation = true;
}
}
/**
* Bytecode generation for a <clinit> method
*
* @param classScope
* org.eclipse.che.ide.java.client.internal.compiler.lookup.ClassScope
*/
@Override
public void generateCode(ClassScope classScope) {
// int clinitOffset = 0;
// if (this.ignoreFurtherInvestigation) {
// // should never have to add any <clinit> problem method
// return;
// }
// boolean restart = false;
// do {
// try {
// clinitOffset = classFile.contentsOffset;
// this.generateCode(classScope, classFile, clinitOffset);
// restart = false;
// } 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.
// if (!restart) {
// classFile.contentsOffset = clinitOffset;
// classFile.methodCount--;
// classFile.codeStream.resetInWideMode(); // request wide mode
// // restart method generation
// restart = true;
// } else {
// classFile.contentsOffset = clinitOffset;
// classFile.methodCount--;
// }
// } else if (e.compilationResult == CodeStream.RESTART_CODE_GEN_FOR_UNUSED_LOCALS_MODE) {
// classFile.contentsOffset = clinitOffset;
// classFile.methodCount--;
// classFile.codeStream.resetForCodeGenUnusedLocals();
// // restart method generation
// restart = true;
// } else {
// // produce a problem method accounting for this fatal error
// classFile.contentsOffset = clinitOffset;
// classFile.methodCount--;
// restart = false;
// }
// }
// } while (restart);
}
// /**
// * Bytecode generation for a <clinit> method
// *
// * @param classScope org.eclipse.che.ide.java.client.internal.compiler.lookup.ClassScope
// * @param classFile org.eclipse.che.ide.java.client.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;
// 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.outerMostClassScope().enclosingSourceType(),
// this.classLiteralSyntheticField);
// codeStream.invokeJavaLangClassDesiredAssertionStatus();
// BranchLabel falseLabel = new BranchLabel(codeStream);
// codeStream.ifne(falseLabel);
// codeStream.iconst_1();
// BranchLabel jumpLabel = new BranchLabel(codeStream);
// codeStream.decrStackSize(1);
// codeStream.goto_(jumpLabel);
// falseLabel.place();
// codeStream.iconst_0();
// jumpLabel.place();
// codeStream.fieldAccess(Opcodes.OPC_putstatic, this.assertionSyntheticFieldBinding, null /* default declaringClass */);
// }
// // generate static fields/initializers/enum constants
// final FieldDeclaration[] fieldDeclarations = declaringType.fields;
// BlockScope lastInitializerScope = null;
// int remainingFieldCount = 0;
// if (TypeDeclaration.kind(declaringType.modifiers) == TypeDeclaration.ENUM_DECL) {
// int enumCount = declaringType.enumConstantsCounter;
// if (enumCount > ENUM_CONSTANTS_THRESHOLD) {
// // generate synthetic methods to initialize all the enum constants
// int begin = -1;
// int count = 0;
// if (fieldDeclarations != null) {
// int max = fieldDeclarations.length;
// for (int i = 0; i < max; i++) {
// FieldDeclaration fieldDecl = fieldDeclarations[i];
// if (fieldDecl.isStatic()) {
// if (fieldDecl.getKind() == AbstractVariableDeclaration.ENUM_CONSTANT) {
// if (begin == -1) {
// begin = i;
// }
// count++;
// if (count > ENUM_CONSTANTS_THRESHOLD) {
// SyntheticMethodBinding syntheticMethod = declaringType.binding.addSyntheticMethodForEnumInitialization(begin, i);
// codeStream.invoke(Opcodes.OPC_invokestatic, syntheticMethod, null /* default declaringClass */);
// begin = i;
// count = 1;
// }
// }
// }
// }
// if (count != 0) {
// // add last synthetic method
// SyntheticMethodBinding syntheticMethod = declaringType.binding.addSyntheticMethodForEnumInitialization(begin, max);
// codeStream.invoke(Opcodes.OPC_invokestatic, syntheticMethod, null /* default declaringClass */);
// }
// }
// } else 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);
// } else {
// remainingFieldCount++;
// }
// }
// }
// }
// // enum need to initialize $VALUES synthetic cache of enum constants
// // $VALUES := new <EnumType>[<enumCount>]
// codeStream.generateInlinedValue(enumCount);
// codeStream.anewarray(declaringType.binding);
// if (enumCount > 0) {
// if (fieldDeclarations != null) {
// 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.fieldAccess(Opcodes.OPC_getstatic, fieldDecl.binding, null /* default declaringClass */);
// codeStream.aastore();
// }
// }
// }
// }
// codeStream.fieldAccess(Opcodes.OPC_putstatic, declaringType.enumValuesSyntheticfield, null /* default declaringClass */);
// 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 && remainingFieldCount >= 0; i++) {
// FieldDeclaration fieldDecl = fieldDeclarations[i];
// switch (fieldDecl.getKind()) {
// case AbstractVariableDeclaration.ENUM_CONSTANT :
// break;
// case AbstractVariableDeclaration.INITIALIZER :
// if (!fieldDecl.isStatic()) {
// break;
// }
// remainingFieldCount--;
// lastInitializerScope = ((Initializer) fieldDecl).block.scope;
// fieldDecl.generateCode(staticInitializerScope, codeStream);
// break;
// case AbstractVariableDeclaration.FIELD :
// if (!fieldDecl.binding.isStatic()) {
// break;
// }
// remainingFieldCount--;
// 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.bits & ASTNode.NeedFreeReturn) != 0) {
// 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);
// }
// }
@Override
public boolean isClinit() {
return true;
}
@Override
public boolean isInitializationMethod() {
return true;
}
@Override
public boolean isStatic() {
return true;
}
@Override
public void parseStatements(Parser parser, CompilationUnitDeclaration unit) {
//the clinit is filled by hand ....
}
@Override
public StringBuffer print(int tab, StringBuffer output) {
printIndent(tab, output).append("<clinit>()"); //$NON-NLS-1$
printBody(tab + 1, output);
return output;
}
@Override
public void resolve(ClassScope classScope) {
this.scope = new MethodScope(classScope, classScope.referenceContext, true);
}
@Override
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
if (needClassLiteralField) {
SourceTypeBinding sourceType = this.scope.outerMostClassScope().enclosingSourceType();
// see https://bugs.eclipse.org/bugs/show_bug.cgi?id=22334
if (!sourceType.isInterface() && !sourceType.isBaseType()) {
this.classLiteralSyntheticField = sourceType.addSyntheticFieldForClassLiteral(sourceType, this.scope);
}
}
}
}