blob: 766cd2dda6e23602b5978194d2bd317a5b38e3d3 [file] [log] [blame]
package org.eclipse.jdt.internal.compiler.ast;
/*
* (c) Copyright IBM Corp. 2000, 2001.
* All Rights Reserved.
*/
import org.eclipse.jdt.core.compiler.*;
import org.eclipse.jdt.internal.compiler.*;
import org.eclipse.jdt.internal.compiler.impl.*;
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.problem.*;
import org.eclipse.jdt.internal.compiler.parser.*;
public abstract class AbstractMethodDeclaration
extends AstNode
implements ProblemSeverities, ReferenceContext {
public MethodScope scope;
//it is not relevent for constructor but it helps to have the name of the constructor here
//which is always the name of the class.....parsing do extra work to fill it up while it do not have to....
public char[] selector;
public int declarationSourceStart;
public int declarationSourceEnd;
public int modifiers;
public int modifiersSourceStart;
public Argument[] arguments;
public TypeReference[] thrownExceptions;
public Statement[] statements;
public int explicitDeclarations;
public MethodBinding binding;
public boolean ignoreFurtherInvestigation = false;
public boolean needFreeReturn = false;
public int bodyStart;
public int bodyEnd = -1;
public CompilationResult compilationResult;
AbstractMethodDeclaration(CompilationResult compilationResult){
this.compilationResult = compilationResult;
}
/*
* We cause the compilation task to abort to a given extent.
*/
public void abort(int abortLevel) {
if (scope == null) {
throw new AbortCompilation(); // cannot do better
}
CompilationResult compilationResult =
scope.referenceCompilationUnit().compilationResult;
switch (abortLevel) {
case AbortCompilation :
throw new AbortCompilation(compilationResult);
case AbortCompilationUnit :
throw new AbortCompilationUnit(compilationResult);
case AbortType :
throw new AbortType(compilationResult);
default :
throw new AbortMethod(compilationResult);
}
}
public void analyseCode(
ClassScope currentScope,
FlowContext flowContext,
FlowInfo flowInfo) {
// starting of the code analysis for methods
if (ignoreFurtherInvestigation)
return;
try {
if (binding == null)
return;
// may be in a non necessary <clinit> for innerclass with static final constant fields
if (binding.isAbstract() || binding.isNative())
return;
ExceptionHandlingFlowContext methodContext =
new ExceptionHandlingFlowContext(
flowContext,
this,
binding.thrownExceptions,
scope,
FlowInfo.DeadEnd);
// propagate to statements
if (statements != null) {
for (int i = 0, count = statements.length; i < count; i++) {
Statement stat;
if (!flowInfo.complainIfUnreachable((stat = statements[i]), scope)) {
flowInfo = stat.analyseCode(scope, methodContext, flowInfo);
}
}
}
// check for missing returning path
TypeBinding returnType = binding.returnType;
if ((returnType == VoidBinding) || isAbstract()) {
needFreeReturn =
!((flowInfo == FlowInfo.DeadEnd) || flowInfo.isFakeReachable());
} else {
if (flowInfo != FlowInfo.DeadEnd) {
// special test for empty methods that should return something
if ((statements == null) && (returnType != VoidBinding)) {
scope.problemReporter().shouldReturn(returnType, this);
} else {
scope.problemReporter().shouldReturn(
returnType,
statements[statements.length - 1]);
}
}
}
} catch (AbortMethod e) {
this.ignoreFurtherInvestigation = true;
}
}
/**
* Bind and add argument's binding into the scope of the method
*/
public void bindArguments() {
if (arguments != null) {
// by default arguments in abstract/native methods are considered to be used (no complaint is expected)
boolean used = binding == null || binding.isAbstract() || binding.isNative();
int length = arguments.length;
for (int i = 0; i < length; i++) {
TypeBinding argType = binding == null ? null : binding.parameters[i];
arguments[i].bind(scope, argType, used);
}
}
}
/**
* Record the thrown exception type bindings in the corresponding type references.
*/
public void bindThrownExceptions() {
if (this.thrownExceptions != null
&& this.binding != null
&& this.binding.thrownExceptions != null) {
int length = this.binding.thrownExceptions.length;
for (int i = 0; i < length; i++) {
this.thrownExceptions[i].binding = this.binding.thrownExceptions[i];
}
}
}
public CompilationResult compilationResult() {
return this.compilationResult;
}
/**
* Bytecode generation for a method
*/
public void generateCode(ClassScope classScope, ClassFile classFile) {
int problemResetPC = 0;
classFile.codeStream.wideMode = false; // reset wideMode to false
if (ignoreFurtherInvestigation) {
// method is known to have errors, dump a problem method
if (this.binding == null)
return; // handle methods with invalid signature or duplicates
int problemsLength;
IProblem[] problems =
scope.referenceCompilationUnit().compilationResult.getProblems();
IProblem[] problemsCopy = new IProblem[problemsLength = problems.length];
System.arraycopy(problems, 0, problemsCopy, 0, problemsLength);
classFile.addProblemMethod(this, binding, problemsCopy);
return;
}
// regular code generation
try {
problemResetPC = classFile.contentsOffset;
this.generateCode(classFile);
} catch (AbortMethod e) {
// a fatal error was detected during code generation, need to restart code gen if possible
if (e.compilationResult == CodeStream.RESTART_IN_WIDE_MODE) {
// a branch target required a goto_w, restart code gen in wide mode.
try {
this.traverse(new ResetStateForCodeGenerationVisitor(), classScope);
classFile.contentsOffset = problemResetPC;
classFile.methodCount--;
classFile.codeStream.wideMode = true; // request wide mode
this.generateCode(classFile); // restart method generation
} catch (AbortMethod e2) {
int problemsLength;
IProblem[] problems =
scope.referenceCompilationUnit().compilationResult.getProblems();
IProblem[] problemsCopy = new IProblem[problemsLength = problems.length];
System.arraycopy(problems, 0, problemsCopy, 0, problemsLength);
classFile.addProblemMethod(this, binding, problemsCopy, problemResetPC);
}
} else {
// produce a problem method accounting for this fatal error
int problemsLength;
IProblem[] problems =
scope.referenceCompilationUnit().compilationResult.getProblems();
IProblem[] problemsCopy = new IProblem[problemsLength = problems.length];
System.arraycopy(problems, 0, problemsCopy, 0, problemsLength);
classFile.addProblemMethod(this, binding, problemsCopy, problemResetPC);
}
}
}
private void generateCode(ClassFile classFile) {
classFile.generateMethodInfoHeader(binding);
int methodAttributeOffset = classFile.contentsOffset;
int attributeNumber = classFile.generateMethodInfoAttribute(binding);
if ((!binding.isNative()) && (!binding.isAbstract())) {
int codeAttributeOffset = classFile.contentsOffset;
classFile.generateCodeAttributeHeader();
CodeStream codeStream = classFile.codeStream;
codeStream.reset(this, classFile);
// initialize local positions
scope.computeLocalVariablePositions(binding.isStatic() ? 0 : 1, codeStream);
// arguments initialization for local variable debug attributes
if (arguments != null) {
for (int i = 0, max = arguments.length; i < max; i++) {
LocalVariableBinding argBinding;
codeStream.addVisibleLocalVariable(argBinding = arguments[i].binding);
argBinding.recordInitializationStartPC(0);
}
}
if (statements != null) {
for (int i = 0, max = statements.length; i < max; i++)
statements[i].generateCode(scope, codeStream);
}
if (needFreeReturn) {
codeStream.return_();
}
// local variable attributes
codeStream.exitUserScope(scope);
codeStream.recordPositionsFrom(0, this.sourceStart);
classFile.completeCodeAttribute(codeAttributeOffset);
attributeNumber++;
}
classFile.completeMethodInfo(methodAttributeOffset, attributeNumber);
// if a problem got reported during code gen, then trigger problem method creation
if (ignoreFurtherInvestigation) {
throw new AbortMethod(scope.referenceCompilationUnit().compilationResult);
}
}
public boolean hasErrors() {
return this.ignoreFurtherInvestigation;
}
public boolean isAbstract() {
if (binding != null)
return binding.isAbstract();
return (modifiers & AccAbstract) != 0;
}
public boolean isClinit() {
return false;
}
public boolean isConstructor() {
return false;
}
public boolean isDefaultConstructor() {
return false;
}
public boolean isInitializationMethod() {
return false;
}
public boolean isNative() {
if (binding != null)
return binding.isNative();
return (modifiers & AccNative) != 0;
}
public boolean isStatic() {
if (binding != null)
return binding.isStatic();
return (modifiers & AccStatic) != 0;
}
/**
* Fill up the method body with statement
*/
public abstract void parseStatements(
Parser parser,
CompilationUnitDeclaration unit);
public void resolve(ClassScope upperScope) {
if (binding == null) {
ignoreFurtherInvestigation = true;
}
try {
bindArguments();
bindThrownExceptions();
resolveStatements(upperScope);
} catch (AbortMethod e) { // ========= abort on fatal error =============
this.ignoreFurtherInvestigation = true;
}
}
public void resolveStatements(ClassScope upperScope) {
if (statements != null) {
int i = 0, length = statements.length;
while (i < length)
statements[i++].resolve(scope);
}
}
public String returnTypeToString(int tab) {
return ""; //$NON-NLS-1$
}
public void tagAsHavingErrors() {
ignoreFurtherInvestigation = true;
}
public String toString(int tab) {
String s = tabString(tab);
if (modifiers != AccDefault) {
s += modifiersString(modifiers);
}
s += returnTypeToString(0);
s += new String(selector) + "("; //$NON-NLS-1$
if (arguments != null) {
for (int i = 0; i < arguments.length; i++) {
s += arguments[i].toString(0);
if (i != (arguments.length - 1))
s = s + ", "; //$NON-NLS-1$
};
};
s += ")"; //$NON-NLS-1$
if (thrownExceptions != null) {
s += " throws "; //$NON-NLS-1$
for (int i = 0; i < thrownExceptions.length; i++) {
s += thrownExceptions[i].toString(0);
if (i != (thrownExceptions.length - 1))
s = s + ", "; //$NON-NLS-1$
};
};
s += toStringStatements(tab + 1);
return s;
}
public String toStringStatements(int tab) {
if (isAbstract() || (this.modifiers & AccSemicolonBody) != 0)
return ";"; //$NON-NLS-1$
String s = " {"; //$NON-NLS-1$
if (statements != null) {
for (int i = 0; i < statements.length; i++) {
s = s + "\n" + statements[i].toString(tab); //$NON-NLS-1$
if (!(statements[i] instanceof Block)) {
s += ";"; //$NON-NLS-1$
}
}
}
s += "\n" + tabString(tab == 0 ? 0 : tab - 1) + "}"; //$NON-NLS-2$ //$NON-NLS-1$
return s;
}
public void traverse(
IAbstractSyntaxTreeVisitor visitor,
ClassScope classScope) {
}
}