1 /*******************************************************************************
2 * Copyright (c) 2000, 2004 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v10.html
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package org.eclipse.jdt.internal.compiler.ast;
13 import java.util.ArrayList;
15 import org.eclipse.jdt.core.compiler.*;
16 import org.eclipse.jdt.internal.compiler.*;
17 import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
18 import org.eclipse.jdt.internal.compiler.codegen.*;
19 import org.eclipse.jdt.internal.compiler.flow.*;
20 import org.eclipse.jdt.internal.compiler.lookup.*;
21 import org.eclipse.jdt.internal.compiler.parser.*;
22 import org.eclipse.jdt.internal.compiler.problem.*;
24 public class ConstructorDeclaration extends AbstractMethodDeclaration {
26 public ExplicitConstructorCall constructorCall;
28 public boolean isDefaultConstructor = false;
29 public TypeParameter[] typeParameters;
31 public ConstructorDeclaration(CompilationResult compilationResult){
32 super(compilationResult);
35 public void analyseCode(
36 ClassScope classScope,
37 InitializationFlowContext initializerFlowContext,
40 if (ignoreFurtherInvestigation)
43 if (this.binding != null && this.binding.isPrivate() && !this.binding.isPrivateUsed()) {
44 if (!classScope.referenceCompilationUnit().compilationResult.hasSyntaxError()) {
45 scope.problemReporter().unusedPrivateConstructor(this);
49 // check constructor recursion, once all constructor got resolved
50 if (isRecursive(null /*lazy initialized visited list*/)) {
51 this.scope.problemReporter().recursiveConstructorInvocation(this.constructorCall);
55 ExceptionHandlingFlowContext constructorContext =
56 new ExceptionHandlingFlowContext(
57 initializerFlowContext.parent,
59 binding.thrownExceptions,
62 initializerFlowContext.checkInitializerExceptions(
67 // anonymous constructor can gain extra thrown exceptions from unhandled ones
68 if (binding.declaringClass.isAnonymousType()) {
69 ArrayList computedExceptions = constructorContext.extendedExceptions;
70 if (computedExceptions != null){
72 if ((size = computedExceptions.size()) > 0){
73 ReferenceBinding[] actuallyThrownExceptions;
74 computedExceptions.toArray(actuallyThrownExceptions = new ReferenceBinding[size]);
75 binding.thrownExceptions = actuallyThrownExceptions;
80 // tag parameters as being set
81 if (this.arguments != null) {
82 for (int i = 0, count = this.arguments.length; i < count; i++) {
83 flowInfo.markAsDefinitelyAssigned(this.arguments[i].binding);
87 // propagate to constructor call
88 if (constructorCall != null) {
89 // if calling 'this(...)', then flag all non-static fields as definitely
90 // set since they are supposed to be set inside other local constructor
91 if (constructorCall.accessMode == ExplicitConstructorCall.This) {
92 FieldBinding[] fields = binding.declaringClass.fields();
93 for (int i = 0, count = fields.length; i < count; i++) {
95 if (!(field = fields[i]).isStatic()) {
96 flowInfo.markAsDefinitelyAssigned(field);
100 flowInfo = constructorCall.analyseCode(scope, constructorContext, flowInfo);
102 // propagate to statements
103 if (statements != null) {
104 boolean didAlreadyComplain = false;
105 for (int i = 0, count = statements.length; i < count; i++) {
106 Statement stat = statements[i];
107 if (!stat.complainIfUnreachable(flowInfo, scope, didAlreadyComplain)) {
108 flowInfo = stat.analyseCode(scope, constructorContext, flowInfo);
110 didAlreadyComplain = true;
114 // check for missing returning path
115 this.needFreeReturn = flowInfo.isReachable();
117 // check missing blank final field initializations
118 if ((constructorCall != null)
119 && (constructorCall.accessMode != ExplicitConstructorCall.This)) {
120 flowInfo = flowInfo.mergedWith(constructorContext.initsOnReturn);
121 FieldBinding[] fields = binding.declaringClass.fields();
122 for (int i = 0, count = fields.length; i < count; i++) {
124 if ((!(field = fields[i]).isStatic())
126 && (!flowInfo.isDefinitelyAssigned(fields[i]))) {
127 scope.problemReporter().uninitializedBlankFinalField(
129 isDefaultConstructor ? (ASTNode) scope.referenceType() : this);
133 // check unreachable catch blocks
134 constructorContext.complainIfUnusedExceptionHandlers(this);
135 } catch (AbortMethod e) {
136 this.ignoreFurtherInvestigation = true;
141 * Bytecode generation for a constructor
143 * @param classScope org.eclipse.jdt.internal.compiler.lookup.ClassScope
144 * @param classFile org.eclipse.jdt.internal.compiler.codegen.ClassFile
146 public void generateCode(ClassScope classScope, ClassFile classFile) {
148 int problemResetPC = 0;
149 if (ignoreFurtherInvestigation) {
150 if (this.binding == null)
151 return; // Handle methods with invalid signature or duplicates
153 IProblem[] problems =
154 scope.referenceCompilationUnit().compilationResult.getProblems();
155 IProblem[] problemsCopy = new IProblem[problemsLength = problems.length];
156 System.arraycopy(problems, 0, problemsCopy, 0, problemsLength);
157 classFile.addProblemConstructor(this, binding, problemsCopy);
161 problemResetPC = classFile.contentsOffset;
162 this.internalGenerateCode(classScope, classFile);
163 } catch (AbortMethod e) {
164 if (e.compilationResult == CodeStream.RESTART_IN_WIDE_MODE) {
165 // a branch target required a goto_w, restart code gen in wide mode.
167 classFile.contentsOffset = problemResetPC;
168 classFile.methodCount--;
169 classFile.codeStream.wideMode = true; // request wide mode
170 this.internalGenerateCode(classScope, classFile); // restart method generation
171 } catch (AbortMethod e2) {
173 IProblem[] problems =
174 scope.referenceCompilationUnit().compilationResult.getAllProblems();
175 IProblem[] problemsCopy = new IProblem[problemsLength = problems.length];
176 System.arraycopy(problems, 0, problemsCopy, 0, problemsLength);
177 classFile.addProblemConstructor(this, binding, problemsCopy, problemResetPC);
181 IProblem[] problems =
182 scope.referenceCompilationUnit().compilationResult.getAllProblems();
183 IProblem[] problemsCopy = new IProblem[problemsLength = problems.length];
184 System.arraycopy(problems, 0, problemsCopy, 0, problemsLength);
185 classFile.addProblemConstructor(this, binding, problemsCopy, problemResetPC);
190 public void generateSyntheticFieldInitializationsIfNecessary(
191 MethodScope methodScope,
192 CodeStream codeStream,
193 ReferenceBinding declaringClass) {
195 if (!declaringClass.isNestedType()) return;
197 NestedTypeBinding nestedType = (NestedTypeBinding) declaringClass;
199 SyntheticArgumentBinding[] syntheticArgs = nestedType.syntheticEnclosingInstances();
200 for (int i = 0, max = syntheticArgs == null ? 0 : syntheticArgs.length; i < max; i++) {
201 SyntheticArgumentBinding syntheticArg;
202 if ((syntheticArg = syntheticArgs[i]).matchingField != null) {
203 codeStream.aload_0();
204 codeStream.load(syntheticArg);
205 codeStream.putfield(syntheticArg.matchingField);
208 syntheticArgs = nestedType.syntheticOuterLocalVariables();
209 for (int i = 0, max = syntheticArgs == null ? 0 : syntheticArgs.length; i < max; i++) {
210 SyntheticArgumentBinding syntheticArg;
211 if ((syntheticArg = syntheticArgs[i]).matchingField != null) {
212 codeStream.aload_0();
213 codeStream.load(syntheticArg);
214 codeStream.putfield(syntheticArg.matchingField);
219 private void internalGenerateCode(ClassScope classScope, ClassFile classFile) {
221 classFile.generateMethodInfoHeader(binding);
222 int methodAttributeOffset = classFile.contentsOffset;
223 int attributeNumber = classFile.generateMethodInfoAttribute(this.binding);
224 if ((!binding.isNative()) && (!binding.isAbstract())) {
226 TypeDeclaration declaringType = classScope.referenceContext;
227 int codeAttributeOffset = classFile.contentsOffset;
228 classFile.generateCodeAttributeHeader();
229 CodeStream codeStream = classFile.codeStream;
230 codeStream.reset(this, classFile);
232 // initialize local positions - including initializer scope.
233 ReferenceBinding declaringClass = binding.declaringClass;
235 int enumOffset = declaringClass.isEnum() ? 2 : 0; // String name, int ordinal
236 int argSlotSize = 1 + enumOffset; // this==aload0
238 if (declaringClass.isNestedType()){
239 NestedTypeBinding nestedType = (NestedTypeBinding) declaringClass;
240 this.scope.extraSyntheticArguments = nestedType.syntheticOuterLocalVariables();
241 scope.computeLocalVariablePositions(// consider synthetic arguments if any
242 nestedType.enclosingInstancesSlotSize + 1 + enumOffset,
244 argSlotSize += nestedType.enclosingInstancesSlotSize;
245 argSlotSize += nestedType.outerLocalVariablesSlotSize;
247 scope.computeLocalVariablePositions(1 + enumOffset, codeStream);
250 if (arguments != null) {
251 for (int i = 0, max = arguments.length; i < max; i++) {
252 // arguments initialization for local variable debug attributes
253 LocalVariableBinding argBinding;
254 codeStream.addVisibleLocalVariable(argBinding = arguments[i].binding);
255 argBinding.recordInitializationStartPC(0);
257 if ((argType = argBinding.type) == LongBinding || (argType == DoubleBinding)) {
265 MethodScope initializerScope = declaringType.initializerScope;
266 initializerScope.computeLocalVariablePositions(argSlotSize, codeStream); // offset by the argument size (since not linked to method scope)
268 boolean needFieldInitializations = constructorCall == null || constructorCall.accessMode != ExplicitConstructorCall.This;
270 // post 1.4 source level, synthetic initializations occur prior to explicit constructor call
271 boolean preInitSyntheticFields = scope.environment().options.targetJDK >= ClassFileConstants.JDK1_4;
273 if (needFieldInitializations && preInitSyntheticFields){
274 generateSyntheticFieldInitializationsIfNecessary(scope, codeStream, declaringClass);
276 // generate constructor call
277 if (constructorCall != null) {
278 constructorCall.generateCode(scope, codeStream);
280 // generate field initialization - only if not invoking another constructor call of the same class
281 if (needFieldInitializations) {
282 if (!preInitSyntheticFields){
283 generateSyntheticFieldInitializationsIfNecessary(scope, codeStream, declaringClass);
285 // generate user field initialization
286 if (declaringType.fields != null) {
287 for (int i = 0, max = declaringType.fields.length; i < max; i++) {
288 FieldDeclaration fieldDecl;
289 if (!(fieldDecl = declaringType.fields[i]).isStatic()) {
290 fieldDecl.generateCode(initializerScope, codeStream);
295 // generate statements
296 if (statements != null) {
297 for (int i = 0, max = statements.length; i < max; i++) {
298 statements[i].generateCode(scope, codeStream);
301 if (this.needFreeReturn) {
302 codeStream.return_();
304 // local variable attributes
305 codeStream.exitUserScope(scope);
306 codeStream.recordPositionsFrom(0, this.bodyEnd);
307 classFile.completeCodeAttribute(codeAttributeOffset);
310 classFile.completeMethodInfo(methodAttributeOffset, attributeNumber);
312 // if a problem got reported during code gen, then trigger problem method creation
313 if (ignoreFurtherInvestigation) {
314 throw new AbortMethod(scope.referenceCompilationUnit().compilationResult, null);
318 public boolean isConstructor() {
323 public boolean isDefaultConstructor() {
325 return this.isDefaultConstructor;
328 public boolean isInitializationMethod() {
334 * Returns true if the constructor is directly involved in a cycle.
335 * Given most constructors aren't, we only allocate the visited list
338 public boolean isRecursive(ArrayList visited) {
340 if (this.binding == null
341 || this.constructorCall == null
342 || this.constructorCall.binding == null
343 || this.constructorCall.isSuperAccess()
344 || !this.constructorCall.binding.isValidBinding()) {
348 ConstructorDeclaration targetConstructor =
349 ((ConstructorDeclaration)this.scope.referenceType().declarationOf(constructorCall.binding.original()));
350 if (this == targetConstructor) return true; // direct case
352 if (visited == null) { // lazy allocation
353 visited = new ArrayList(1);
355 int index = visited.indexOf(this);
356 if (index >= 0) return index == 0; // only blame if directly part of the cycle
360 return targetConstructor.isRecursive(visited);
363 public void parseStatements(Parser parser, CompilationUnitDeclaration unit) {
365 //fill up the constructor body with its statements
366 if (ignoreFurtherInvestigation)
368 if (isDefaultConstructor && this.constructorCall == null){
369 this.constructorCall = SuperReference.implicitSuperConstructorCall();
370 this.constructorCall.sourceStart = this.sourceStart;
371 this.constructorCall.sourceEnd = this.sourceEnd;
374 parser.parse(this, unit);
378 public StringBuffer printBody(int indent, StringBuffer output) {
380 output.append(" {"); //$NON-NLS-1$
381 if (constructorCall != null) {
383 constructorCall.printStatement(indent, output); //$NON-NLS-1$ //$NON-NLS-2$
385 if (statements != null) {
386 for (int i = 0; i < statements.length; i++) {
388 statements[i].printStatement(indent, output); //$NON-NLS-1$
392 printIndent(indent == 0 ? 0 : indent - 1, output).append('}');
396 public void resolveJavadoc() {
398 if (this.binding == null || this.javadoc != null) {
399 super.resolveJavadoc();
400 } else if (!isDefaultConstructor) {
401 this.scope.problemReporter().javadocMissing(this.sourceStart, this.sourceEnd, this.binding.modifiers);
406 * Type checking for constructor, just another method, except for special check
407 * for recursive constructor invocations.
409 public void resolveStatements() {
411 if (!CharOperation.equals(scope.enclosingSourceType().sourceName, selector)){
412 scope.problemReporter().missingReturnType(this);
415 if (this.binding != null && this.binding.declaringClass.isAnnotationType()) {
416 scope.problemReporter().annotationTypeDeclarationCannotHaveConstructor(this);
418 // if null ==> an error has occurs at parsing time ....
419 if (this.constructorCall != null) {
420 // e.g. using super() in java.lang.Object
421 if (this.binding != null
422 && this.binding.declaringClass.id == T_JavaLangObject
423 && this.constructorCall.accessMode != ExplicitConstructorCall.This) {
424 if (this.constructorCall.accessMode == ExplicitConstructorCall.Super) {
425 scope.problemReporter().cannotUseSuperInJavaLangObject(this.constructorCall);
427 this.constructorCall = null;
429 this.constructorCall.resolve(this.scope);
432 if ((modifiers & AccSemicolonBody) != 0) {
433 scope.problemReporter().methodNeedBody(this);
435 super.resolveStatements();
438 public void traverse(
440 ClassScope classScope) {
443 if (visitor.visit(this, classScope)) {
444 if (this.annotations != null) {
445 int annotationsLength = this.annotations.length;
446 for (int i = 0; i < annotationsLength; i++)
447 this.annotations[i].traverse(visitor, scope);
449 if (this.typeParameters != null) {
450 int typeParametersLength = this.typeParameters.length;
451 for (int i = 0; i < typeParametersLength; i++) {
452 this.typeParameters[i].traverse(visitor, scope);
455 if (arguments != null) {
456 int argumentLength = arguments.length;
457 for (int i = 0; i < argumentLength; i++)
458 arguments[i].traverse(visitor, scope);
460 if (thrownExceptions != null) {
461 int thrownExceptionsLength = thrownExceptions.length;
462 for (int i = 0; i < thrownExceptionsLength; i++)
463 thrownExceptions[i].traverse(visitor, scope);
465 if (constructorCall != null)
466 constructorCall.traverse(visitor, scope);
467 if (statements != null) {
468 int statementsLength = statements.length;
469 for (int i = 0; i < statementsLength; i++)
470 statements[i].traverse(visitor, scope);
473 visitor.endVisit(this, classScope);
475 public TypeParameter[] typeParameters() {
476 return this.typeParameters;