X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;ds=sidebyside;f=src%2Forg%2Feclipse%2Fjdt%2Finternal%2Fcompiler%2Fast%2FConstructorDeclaration.java;fp=src%2Forg%2Feclipse%2Fjdt%2Finternal%2Fcompiler%2Fast%2FConstructorDeclaration.java;h=7170bbf40d60755c9a86e35efa5f1a4dfd9fe537;hb=040fa5af2cd00017cf3575950cdaade34a6d7f6c;hp=0000000000000000000000000000000000000000;hpb=a580fb8376d315d05e4d6bfdff9ff1101a151cd6;p=org.ibex.tool.git diff --git a/src/org/eclipse/jdt/internal/compiler/ast/ConstructorDeclaration.java b/src/org/eclipse/jdt/internal/compiler/ast/ConstructorDeclaration.java new file mode 100644 index 0000000..7170bbf --- /dev/null +++ b/src/org/eclipse/jdt/internal/compiler/ast/ConstructorDeclaration.java @@ -0,0 +1,454 @@ +/******************************************************************************* + * 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.jdt.internal.compiler.ast; + +import java.util.ArrayList; + +import org.eclipse.jdt.core.compiler.*; +import org.eclipse.jdt.internal.compiler.*; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; +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 ConstructorDeclaration extends AbstractMethodDeclaration { + + public ExplicitConstructorCall constructorCall; + public final static char[] ConstantPoolName = "".toCharArray(); //$NON-NLS-1$ + public boolean isDefaultConstructor = false; + + public ConstructorDeclaration(CompilationResult compilationResult){ + super(compilationResult); + } + + public void analyseCode( + ClassScope classScope, + InitializationFlowContext initializerFlowContext, + FlowInfo flowInfo) { + + if (ignoreFurtherInvestigation) + return; + + if (this.binding != null && this.binding.isPrivate() && !this.binding.isPrivateUsed()) { + if (!classScope.referenceCompilationUnit().compilationResult.hasSyntaxError()) { + scope.problemReporter().unusedPrivateConstructor(this); + } + } + + // check constructor recursion, once all constructor got resolved + if (isRecursive(null /*lazy initialized visited list*/)) { + this.scope.problemReporter().recursiveConstructorInvocation(this.constructorCall); + } + + try { + ExceptionHandlingFlowContext constructorContext = + new ExceptionHandlingFlowContext( + initializerFlowContext.parent, + this, + binding.thrownExceptions, + scope, + FlowInfo.DEAD_END); + initializerFlowContext.checkInitializerExceptions( + scope, + constructorContext, + flowInfo); + + // anonymous constructor can gain extra thrown exceptions from unhandled ones + if (binding.declaringClass.isAnonymousType()) { + ArrayList computedExceptions = constructorContext.extendedExceptions; + if (computedExceptions != null){ + int size; + if ((size = computedExceptions.size()) > 0){ + ReferenceBinding[] actuallyThrownExceptions; + computedExceptions.toArray(actuallyThrownExceptions = new ReferenceBinding[size]); + binding.thrownExceptions = actuallyThrownExceptions; + } + } + } + + // propagate to constructor call + if (constructorCall != null) { + // if calling 'this(...)', then flag all non-static fields as definitely + // set since they are supposed to be set inside other local constructor + if (constructorCall.accessMode == ExplicitConstructorCall.This) { + FieldBinding[] fields = binding.declaringClass.fields(); + for (int i = 0, count = fields.length; i < count; i++) { + FieldBinding field; + if (!(field = fields[i]).isStatic()) { + flowInfo.markAsDefinitelyAssigned(field); + } + } + } + flowInfo = constructorCall.analyseCode(scope, constructorContext, flowInfo); + } + // propagate to statements + if (statements != null) { + boolean didAlreadyComplain = false; + for (int i = 0, count = statements.length; i < count; i++) { + Statement stat = statements[i]; + if (!stat.complainIfUnreachable(flowInfo, scope, didAlreadyComplain)) { + flowInfo = stat.analyseCode(scope, constructorContext, flowInfo); + } else { + didAlreadyComplain = true; + } + } + } + // check for missing returning path + this.needFreeReturn = flowInfo.isReachable(); + + // check missing blank final field initializations + if ((constructorCall != null) + && (constructorCall.accessMode != ExplicitConstructorCall.This)) { + flowInfo = flowInfo.mergedWith(constructorContext.initsOnReturn); + FieldBinding[] fields = binding.declaringClass.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, + isDefaultConstructor ? (ASTNode) scope.referenceType() : this); + } + } + } + // check unreachable catch blocks + constructorContext.complainIfUnusedExceptionHandlers(this); + } catch (AbortMethod e) { + this.ignoreFurtherInvestigation = true; + } + } + + /** + * Bytecode generation for a constructor + * + * @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 problemResetPC = 0; + if (ignoreFurtherInvestigation) { + 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.addProblemConstructor(this, binding, problemsCopy); + return; + } + try { + problemResetPC = classFile.contentsOffset; + this.internalGenerateCode(classScope, classFile); + } catch (AbortMethod e) { + if (e.compilationResult == CodeStream.RESTART_IN_WIDE_MODE) { + // a branch target required a goto_w, restart code gen in wide mode. + try { + classFile.contentsOffset = problemResetPC; + classFile.methodCount--; + classFile.codeStream.wideMode = true; // request wide mode + this.internalGenerateCode(classScope, classFile); // restart method generation + } catch (AbortMethod e2) { + int problemsLength; + IProblem[] problems = + scope.referenceCompilationUnit().compilationResult.getAllProblems(); + IProblem[] problemsCopy = new IProblem[problemsLength = problems.length]; + System.arraycopy(problems, 0, problemsCopy, 0, problemsLength); + classFile.addProblemConstructor(this, binding, problemsCopy, problemResetPC); + } + } else { + int problemsLength; + IProblem[] problems = + scope.referenceCompilationUnit().compilationResult.getAllProblems(); + IProblem[] problemsCopy = new IProblem[problemsLength = problems.length]; + System.arraycopy(problems, 0, problemsCopy, 0, problemsLength); + classFile.addProblemConstructor(this, binding, problemsCopy, problemResetPC); + } + } + } + + public void generateSyntheticFieldInitializationsIfNecessary( + MethodScope methodScope, + CodeStream codeStream, + ReferenceBinding declaringClass) { + + if (!declaringClass.isNestedType()) return; + + NestedTypeBinding nestedType = (NestedTypeBinding) declaringClass; + + SyntheticArgumentBinding[] syntheticArgs = nestedType.syntheticEnclosingInstances(); + for (int i = 0, max = syntheticArgs == null ? 0 : syntheticArgs.length; i < max; i++) { + SyntheticArgumentBinding syntheticArg; + if ((syntheticArg = syntheticArgs[i]).matchingField != null) { + codeStream.aload_0(); + codeStream.load(syntheticArg); + codeStream.putfield(syntheticArg.matchingField); + } + } + syntheticArgs = nestedType.syntheticOuterLocalVariables(); + for (int i = 0, max = syntheticArgs == null ? 0 : syntheticArgs.length; i < max; i++) { + SyntheticArgumentBinding syntheticArg; + if ((syntheticArg = syntheticArgs[i]).matchingField != null) { + codeStream.aload_0(); + codeStream.load(syntheticArg); + codeStream.putfield(syntheticArg.matchingField); + } + } + } + + private void internalGenerateCode(ClassScope classScope, ClassFile classFile) { + + classFile.generateMethodInfoHeader(binding); + int methodAttributeOffset = classFile.contentsOffset; + int attributeNumber = classFile.generateMethodInfoAttribute(binding); + if ((!binding.isNative()) && (!binding.isAbstract())) { + + TypeDeclaration declaringType = classScope.referenceContext; + int codeAttributeOffset = classFile.contentsOffset; + classFile.generateCodeAttributeHeader(); + CodeStream codeStream = classFile.codeStream; + codeStream.reset(this, classFile); + + // initialize local positions - including initializer scope. + ReferenceBinding declaringClass = binding.declaringClass; + + int argSlotSize = 1; // this==aload0 + + if (declaringClass.isNestedType()){ + NestedTypeBinding nestedType = (NestedTypeBinding) declaringClass; + this.scope.extraSyntheticArguments = nestedType.syntheticOuterLocalVariables(); + scope.computeLocalVariablePositions(// consider synthetic arguments if any + nestedType.enclosingInstancesSlotSize + 1, + codeStream); + argSlotSize += nestedType.enclosingInstancesSlotSize; + argSlotSize += nestedType.outerLocalVariablesSlotSize; + } else { + scope.computeLocalVariablePositions(1, codeStream); + } + + if (arguments != null) { + for (int i = 0, max = arguments.length; i < max; i++) { + // arguments initialization for local variable debug attributes + LocalVariableBinding argBinding; + codeStream.addVisibleLocalVariable(argBinding = arguments[i].binding); + argBinding.recordInitializationStartPC(0); + TypeBinding argType; + if ((argType = argBinding.type) == LongBinding || (argType == DoubleBinding)) { + argSlotSize += 2; + } else { + argSlotSize++; + } + } + } + + MethodScope initializerScope = declaringType.initializerScope; + initializerScope.computeLocalVariablePositions(argSlotSize, codeStream); // offset by the argument size (since not linked to method scope) + + boolean needFieldInitializations = constructorCall == null || constructorCall.accessMode != ExplicitConstructorCall.This; + + // post 1.4 source level, synthetic initializations occur prior to explicit constructor call + boolean preInitSyntheticFields = scope.environment().options.targetJDK >= ClassFileConstants.JDK1_4; + + if (needFieldInitializations && preInitSyntheticFields){ + generateSyntheticFieldInitializationsIfNecessary(scope, codeStream, declaringClass); + } + // generate constructor call + if (constructorCall != null) { + constructorCall.generateCode(scope, codeStream); + } + // generate field initialization - only if not invoking another constructor call of the same class + if (needFieldInitializations) { + if (!preInitSyntheticFields){ + generateSyntheticFieldInitializationsIfNecessary(scope, codeStream, declaringClass); + } + // generate user field initialization + 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(initializerScope, codeStream); + } + } + } + } + // generate statements + if (statements != null) { + for (int i = 0, max = statements.length; i < max; i++) { + statements[i].generateCode(scope, codeStream); + } + } + if (this.needFreeReturn) { + codeStream.return_(); + } + // local variable attributes + codeStream.exitUserScope(scope); + codeStream.recordPositionsFrom(0, this.bodyEnd); + 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, null); + } + } + + public boolean isConstructor() { + + return true; + } + + public boolean isDefaultConstructor() { + + return this.isDefaultConstructor; + } + + public boolean isInitializationMethod() { + + return true; + } + + /** + * Returns true if the constructor is directly involved in a cycle. + * Given most constructors aren't, we only allocate the visited list + * lazily. + * + * @param visited + * @return + */ + public boolean isRecursive(ArrayList visited) { + + if (this.binding == null + || this.constructorCall == null + || this.constructorCall.binding == null + || this.constructorCall.isSuperAccess() + || !this.constructorCall.binding.isValidBinding()) { + return false; + } + + ConstructorDeclaration targetConstructor = + ((ConstructorDeclaration)this.scope.referenceType().declarationOf(constructorCall.binding)); + if (this == targetConstructor) return true; // direct case + + if (visited == null) { // lazy allocation + visited = new ArrayList(1); + } else { + int index = visited.indexOf(this); + if (index >= 0) return index == 0; // only blame if directly part of the cycle + } + visited.add(this); + + return targetConstructor.isRecursive(visited); + } + + public void parseStatements(Parser parser, CompilationUnitDeclaration unit) { + + //fill up the constructor body with its statements + if (ignoreFurtherInvestigation) + return; + if (isDefaultConstructor){ + constructorCall = SuperReference.implicitSuperConstructorCall(); + constructorCall.sourceStart = sourceStart; + constructorCall.sourceEnd = sourceEnd; + return; + } + parser.parse(this, unit); + + } + + public StringBuffer printBody(int indent, StringBuffer output) { + + output.append(" {"); //$NON-NLS-1$ + if (constructorCall != null) { + output.append('\n'); + constructorCall.printStatement(indent, output); //$NON-NLS-1$ //$NON-NLS-2$ + } + if (statements != null) { + for (int i = 0; i < statements.length; i++) { + output.append('\n'); + statements[i].printStatement(indent, output); //$NON-NLS-1$ + } + } + output.append('\n'); + printIndent(indent == 0 ? 0 : indent - 1, output).append('}'); + return output; + } + + public void resolveJavadoc() { + + if (this.binding == null || this.javadoc != null) { + super.resolveJavadoc(); + } else if (!isDefaultConstructor) { + this.scope.problemReporter().javadocMissing(this.sourceStart, this.sourceEnd, this.binding.modifiers); + } + } + + /* + * Type checking for constructor, just another method, except for special check + * for recursive constructor invocations. + */ + public void resolveStatements() { + + if (!CharOperation.equals(scope.enclosingSourceType().sourceName, selector)){ + scope.problemReporter().missingReturnType(this); + } + + // if null ==> an error has occurs at parsing time .... + if (this.constructorCall != null) { + // e.g. using super() in java.lang.Object + if (this.binding != null + && this.binding.declaringClass.id == T_Object + && this.constructorCall.accessMode != ExplicitConstructorCall.This) { + if (this.constructorCall.accessMode == ExplicitConstructorCall.Super) { + scope.problemReporter().cannotUseSuperInJavaLangObject(this.constructorCall); + } + this.constructorCall = null; + } else { + this.constructorCall.resolve(this.scope); + } + } + if ((modifiers & AccSemicolonBody) != 0) { + scope.problemReporter().methodNeedBody(this); + } + super.resolveStatements(); + } + + public void traverse( + ASTVisitor visitor, + ClassScope classScope) { + + if (visitor.visit(this, classScope)) { + if (arguments != null) { + int argumentLength = arguments.length; + for (int i = 0; i < argumentLength; i++) + arguments[i].traverse(visitor, scope); + } + if (thrownExceptions != null) { + int thrownExceptionsLength = thrownExceptions.length; + for (int i = 0; i < thrownExceptionsLength; i++) + thrownExceptions[i].traverse(visitor, scope); + } + if (constructorCall != null) + constructorCall.traverse(visitor, scope); + if (statements != null) { + int statementsLength = statements.length; + for (int i = 0; i < statementsLength; i++) + statements[i].traverse(visitor, scope); + } + } + visitor.endVisit(this, classScope); + } +}