001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdt.internal.compiler.ast;
011:
012: import java.util.ArrayList;
013:
014: import org.eclipse.jdt.core.compiler.*;
015: import org.eclipse.jdt.internal.compiler.*;
016: import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
017: import org.eclipse.jdt.internal.compiler.codegen.*;
018: import org.eclipse.jdt.internal.compiler.flow.*;
019: import org.eclipse.jdt.internal.compiler.lookup.*;
020: import org.eclipse.jdt.internal.compiler.parser.*;
021: import org.eclipse.jdt.internal.compiler.problem.*;
022:
023: public class ConstructorDeclaration extends AbstractMethodDeclaration {
024:
025: public ExplicitConstructorCall constructorCall;
026:
027: public TypeParameter[] typeParameters;
028:
029: public ConstructorDeclaration(CompilationResult compilationResult) {
030: super (compilationResult);
031: }
032:
033: /**
034: * @see org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration#analyseCode(org.eclipse.jdt.internal.compiler.lookup.ClassScope, org.eclipse.jdt.internal.compiler.flow.InitializationFlowContext, org.eclipse.jdt.internal.compiler.flow.FlowInfo)
035: * @deprecated use instead {@link #analyseCode(ClassScope, InitializationFlowContext, FlowInfo, int)}
036: */
037: public void analyseCode(ClassScope classScope,
038: InitializationFlowContext initializerFlowContext,
039: FlowInfo flowInfo) {
040: analyseCode(classScope, initializerFlowContext, flowInfo,
041: FlowInfo.REACHABLE);
042: }
043:
044: /**
045: * The flowInfo corresponds to non-static field initialization infos. It may be unreachable (155423), but still the explicit constructor call must be
046: * analysed as reachable, since it will be generated in the end.
047: */
048: public void analyseCode(ClassScope classScope,
049: InitializationFlowContext initializerFlowContext,
050: FlowInfo flowInfo, int initialReachMode) {
051: if (this .ignoreFurtherInvestigation)
052: return;
053:
054: int nonStaticFieldInfoReachMode = flowInfo.reachMode();
055: flowInfo.setReachMode(initialReachMode);
056:
057: checkUnused: {
058: MethodBinding constructorBinding;
059: if ((constructorBinding = this .binding) == null)
060: break checkUnused;
061: if ((this .bits & ASTNode.IsDefaultConstructor) != 0)
062: break checkUnused;
063: if (constructorBinding.isUsed())
064: break checkUnused;
065: if (constructorBinding.isPrivate()) {
066: if ((this .binding.declaringClass.tagBits & TagBits.HasNonPrivateConstructor) == 0)
067: break checkUnused; // tolerate as known pattern to block instantiation
068: } else if ((this .binding.declaringClass.tagBits & (TagBits.IsAnonymousType | TagBits.IsLocalType)) != TagBits.IsLocalType) {
069: break checkUnused;
070: }
071: // complain unused
072: this .scope.problemReporter().unusedPrivateConstructor(this );
073: }
074:
075: // check constructor recursion, once all constructor got resolved
076: if (isRecursive(null /*lazy initialized visited list*/)) {
077: this .scope.problemReporter()
078: .recursiveConstructorInvocation(
079: this .constructorCall);
080: }
081:
082: try {
083: ExceptionHandlingFlowContext constructorContext = new ExceptionHandlingFlowContext(
084: initializerFlowContext.parent, this ,
085: this .binding.thrownExceptions, this .scope,
086: FlowInfo.DEAD_END);
087: initializerFlowContext.checkInitializerExceptions(
088: this .scope, constructorContext, flowInfo);
089:
090: // anonymous constructor can gain extra thrown exceptions from unhandled ones
091: if (this .binding.declaringClass.isAnonymousType()) {
092: ArrayList computedExceptions = constructorContext.extendedExceptions;
093: if (computedExceptions != null) {
094: int size;
095: if ((size = computedExceptions.size()) > 0) {
096: ReferenceBinding[] actuallyThrownExceptions;
097: computedExceptions
098: .toArray(actuallyThrownExceptions = new ReferenceBinding[size]);
099: this .binding.thrownExceptions = actuallyThrownExceptions;
100: }
101: }
102: }
103:
104: // tag parameters as being set
105: if (this .arguments != null) {
106: for (int i = 0, count = this .arguments.length; i < count; i++) {
107: flowInfo
108: .markAsDefinitelyAssigned(this .arguments[i].binding);
109: }
110: }
111:
112: // propagate to constructor call
113: if (this .constructorCall != null) {
114: // if calling 'this(...)', then flag all non-static fields as definitely
115: // set since they are supposed to be set inside other local constructor
116: if (this .constructorCall.accessMode == ExplicitConstructorCall.This) {
117: FieldBinding[] fields = this .binding.declaringClass
118: .fields();
119: for (int i = 0, count = fields.length; i < count; i++) {
120: FieldBinding field;
121: if (!(field = fields[i]).isStatic()) {
122: flowInfo.markAsDefinitelyAssigned(field);
123: }
124: }
125: }
126: flowInfo = this .constructorCall.analyseCode(this .scope,
127: constructorContext, flowInfo);
128: }
129:
130: // reuse the reachMode from non static field info
131: flowInfo.setReachMode(nonStaticFieldInfoReachMode);
132:
133: // propagate to statements
134: if (this .statements != null) {
135: boolean didAlreadyComplain = false;
136: for (int i = 0, count = this .statements.length; i < count; i++) {
137: Statement stat = this .statements[i];
138: if (!stat.complainIfUnreachable(flowInfo,
139: this .scope, didAlreadyComplain)) {
140: flowInfo = stat.analyseCode(this .scope,
141: constructorContext, flowInfo);
142: } else {
143: didAlreadyComplain = true;
144: }
145: }
146: }
147: // check for missing returning path
148: if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) {
149: this .bits |= ASTNode.NeedFreeReturn;
150: }
151:
152: // reuse the initial reach mode for diagnosing missing blank finals
153: flowInfo.setReachMode(initialReachMode);
154:
155: // check missing blank final field initializations
156: if ((this .constructorCall != null)
157: && (this .constructorCall.accessMode != ExplicitConstructorCall.This)) {
158: flowInfo = flowInfo
159: .mergedWith(constructorContext.initsOnReturn);
160: FieldBinding[] fields = this .binding.declaringClass
161: .fields();
162: for (int i = 0, count = fields.length; i < count; i++) {
163: FieldBinding field;
164: if ((!(field = fields[i]).isStatic())
165: && field.isFinal()
166: && (!flowInfo
167: .isDefinitelyAssigned(fields[i]))) {
168: this .scope
169: .problemReporter()
170: .uninitializedBlankFinalField(
171: field,
172: ((this .bits & ASTNode.IsDefaultConstructor) != 0) ? (ASTNode) this .scope
173: .referenceType()
174: : this );
175: }
176: }
177: }
178: // check unreachable catch blocks
179: constructorContext.complainIfUnusedExceptionHandlers(this );
180: } catch (AbortMethod e) {
181: this .ignoreFurtherInvestigation = true;
182: }
183: }
184:
185: /**
186: * Bytecode generation for a constructor
187: *
188: * @param classScope org.eclipse.jdt.internal.compiler.lookup.ClassScope
189: * @param classFile org.eclipse.jdt.internal.compiler.codegen.ClassFile
190: */
191: public void generateCode(ClassScope classScope, ClassFile classFile) {
192: int problemResetPC = 0;
193: if (this .ignoreFurtherInvestigation) {
194: if (this .binding == null)
195: return; // Handle methods with invalid signature or duplicates
196: int problemsLength;
197: CategorizedProblem[] problems = this .scope
198: .referenceCompilationUnit().compilationResult
199: .getProblems();
200: CategorizedProblem[] problemsCopy = new CategorizedProblem[problemsLength = problems.length];
201: System.arraycopy(problems, 0, problemsCopy, 0,
202: problemsLength);
203: classFile.addProblemConstructor(this , this .binding,
204: problemsCopy);
205: return;
206: }
207: try {
208: problemResetPC = classFile.contentsOffset;
209: this .internalGenerateCode(classScope, classFile);
210: } catch (AbortMethod e) {
211: if (e.compilationResult == CodeStream.RESTART_IN_WIDE_MODE) {
212: // a branch target required a goto_w, restart code gen in wide mode.
213: try {
214: classFile.contentsOffset = problemResetPC;
215: classFile.methodCount--;
216: classFile.codeStream.wideMode = true; // request wide mode
217: this .internalGenerateCode(classScope, classFile); // restart method generation
218: } catch (AbortMethod e2) {
219: int problemsLength;
220: CategorizedProblem[] problems = this .scope
221: .referenceCompilationUnit().compilationResult
222: .getAllProblems();
223: CategorizedProblem[] problemsCopy = new CategorizedProblem[problemsLength = problems.length];
224: System.arraycopy(problems, 0, problemsCopy, 0,
225: problemsLength);
226: classFile.addProblemConstructor(this , this .binding,
227: problemsCopy, problemResetPC);
228: }
229: } else {
230: int problemsLength;
231: CategorizedProblem[] problems = this .scope
232: .referenceCompilationUnit().compilationResult
233: .getAllProblems();
234: CategorizedProblem[] problemsCopy = new CategorizedProblem[problemsLength = problems.length];
235: System.arraycopy(problems, 0, problemsCopy, 0,
236: problemsLength);
237: classFile.addProblemConstructor(this , this .binding,
238: problemsCopy, problemResetPC);
239: }
240: }
241: }
242:
243: public void generateSyntheticFieldInitializationsIfNecessary(
244: MethodScope methodScope, CodeStream codeStream,
245: ReferenceBinding declaringClass) {
246: if (!declaringClass.isNestedType())
247: return;
248:
249: NestedTypeBinding nestedType = (NestedTypeBinding) declaringClass;
250:
251: SyntheticArgumentBinding[] syntheticArgs = nestedType
252: .syntheticEnclosingInstances();
253: for (int i = 0, max = syntheticArgs == null ? 0
254: : syntheticArgs.length; i < max; i++) {
255: SyntheticArgumentBinding syntheticArg;
256: if ((syntheticArg = syntheticArgs[i]).matchingField != null) {
257: codeStream.aload_0();
258: codeStream.load(syntheticArg);
259: codeStream.putfield(syntheticArg.matchingField);
260: }
261: }
262: syntheticArgs = nestedType.syntheticOuterLocalVariables();
263: for (int i = 0, max = syntheticArgs == null ? 0
264: : syntheticArgs.length; i < max; i++) {
265: SyntheticArgumentBinding syntheticArg;
266: if ((syntheticArg = syntheticArgs[i]).matchingField != null) {
267: codeStream.aload_0();
268: codeStream.load(syntheticArg);
269: codeStream.putfield(syntheticArg.matchingField);
270: }
271: }
272: }
273:
274: private void internalGenerateCode(ClassScope classScope,
275: ClassFile classFile) {
276: classFile.generateMethodInfoHeader(this .binding);
277: int methodAttributeOffset = classFile.contentsOffset;
278: int attributeNumber = classFile
279: .generateMethodInfoAttribute(this .binding);
280: if ((!this .binding.isNative()) && (!this .binding.isAbstract())) {
281:
282: TypeDeclaration declaringType = classScope.referenceContext;
283: int codeAttributeOffset = classFile.contentsOffset;
284: classFile.generateCodeAttributeHeader();
285: CodeStream codeStream = classFile.codeStream;
286: codeStream.reset(this , classFile);
287:
288: // initialize local positions - including initializer scope.
289: ReferenceBinding declaringClass = this .binding.declaringClass;
290:
291: int enumOffset = declaringClass.isEnum() ? 2 : 0; // String name, int ordinal
292: int argSlotSize = 1 + enumOffset; // this==aload0
293:
294: if (declaringClass.isNestedType()) {
295: NestedTypeBinding nestedType = (NestedTypeBinding) declaringClass;
296: this .scope.extraSyntheticArguments = nestedType
297: .syntheticOuterLocalVariables();
298: this .scope.computeLocalVariablePositions(// consider synthetic arguments if any
299: nestedType.enclosingInstancesSlotSize + 1
300: + enumOffset, codeStream);
301: argSlotSize += nestedType.enclosingInstancesSlotSize;
302: argSlotSize += nestedType.outerLocalVariablesSlotSize;
303: } else {
304: this .scope.computeLocalVariablePositions(
305: 1 + enumOffset, codeStream);
306: }
307:
308: if (this .arguments != null) {
309: for (int i = 0, max = this .arguments.length; i < max; i++) {
310: // arguments initialization for local variable debug attributes
311: LocalVariableBinding argBinding;
312: codeStream
313: .addVisibleLocalVariable(argBinding = this .arguments[i].binding);
314: argBinding.recordInitializationStartPC(0);
315: TypeBinding argType;
316: if ((argType = argBinding.type) == TypeBinding.LONG
317: || (argType == TypeBinding.DOUBLE)) {
318: argSlotSize += 2;
319: } else {
320: argSlotSize++;
321: }
322: }
323: }
324:
325: MethodScope initializerScope = declaringType.initializerScope;
326: initializerScope.computeLocalVariablePositions(argSlotSize,
327: codeStream); // offset by the argument size (since not linked to method scope)
328:
329: boolean needFieldInitializations = this .constructorCall == null
330: || this .constructorCall.accessMode != ExplicitConstructorCall.This;
331:
332: // post 1.4 target level, synthetic initializations occur prior to explicit constructor call
333: boolean preInitSyntheticFields = this .scope
334: .compilerOptions().targetJDK >= ClassFileConstants.JDK1_4;
335:
336: if (needFieldInitializations && preInitSyntheticFields) {
337: generateSyntheticFieldInitializationsIfNecessary(
338: this .scope, codeStream, declaringClass);
339: }
340: // generate constructor call
341: if (this .constructorCall != null) {
342: this .constructorCall.generateCode(this .scope,
343: codeStream);
344: }
345: // generate field initialization - only if not invoking another constructor call of the same class
346: if (needFieldInitializations) {
347: if (!preInitSyntheticFields) {
348: generateSyntheticFieldInitializationsIfNecessary(
349: this .scope, codeStream, declaringClass);
350: }
351: // generate user field initialization
352: if (declaringType.fields != null) {
353: for (int i = 0, max = declaringType.fields.length; i < max; i++) {
354: FieldDeclaration fieldDecl;
355: if (!(fieldDecl = declaringType.fields[i])
356: .isStatic()) {
357: fieldDecl.generateCode(initializerScope,
358: codeStream);
359: }
360: }
361: }
362: }
363: // generate statements
364: if (this .statements != null) {
365: for (int i = 0, max = this .statements.length; i < max; i++) {
366: this .statements[i].generateCode(this .scope,
367: codeStream);
368: }
369: }
370: // if a problem got reported during code gen, then trigger problem method creation
371: if (this .ignoreFurtherInvestigation) {
372: throw new AbortMethod(this .scope
373: .referenceCompilationUnit().compilationResult,
374: null);
375: }
376: if ((this .bits & ASTNode.NeedFreeReturn) != 0) {
377: codeStream.return_();
378: }
379: // local variable attributes
380: codeStream.exitUserScope(this .scope);
381: codeStream.recordPositionsFrom(0, this .bodyEnd);
382: classFile.completeCodeAttribute(codeAttributeOffset);
383: attributeNumber++;
384: }
385: classFile.completeMethodInfo(methodAttributeOffset,
386: attributeNumber);
387: }
388:
389: public boolean isConstructor() {
390: return true;
391: }
392:
393: public boolean isDefaultConstructor() {
394: return (this .bits & ASTNode.IsDefaultConstructor) != 0;
395: }
396:
397: public boolean isInitializationMethod() {
398: return true;
399: }
400:
401: /*
402: * Returns true if the constructor is directly involved in a cycle.
403: * Given most constructors aren't, we only allocate the visited list
404: * lazily.
405: */
406: public boolean isRecursive(ArrayList visited) {
407: if (this .binding == null || this .constructorCall == null
408: || this .constructorCall.binding == null
409: || this .constructorCall.isSuperAccess()
410: || !this .constructorCall.binding.isValidBinding()) {
411: return false;
412: }
413:
414: ConstructorDeclaration targetConstructor = ((ConstructorDeclaration) this .scope
415: .referenceType().declarationOf(
416: this .constructorCall.binding.original()));
417: if (this == targetConstructor)
418: return true; // direct case
419:
420: if (visited == null) { // lazy allocation
421: visited = new ArrayList(1);
422: } else {
423: int index = visited.indexOf(this );
424: if (index >= 0)
425: return index == 0; // only blame if directly part of the cycle
426: }
427: visited.add(this );
428:
429: return targetConstructor.isRecursive(visited);
430: }
431:
432: public void parseStatements(Parser parser,
433: CompilationUnitDeclaration unit) {
434: //fill up the constructor body with its statements
435: if (this .ignoreFurtherInvestigation)
436: return;
437: if (((this .bits & ASTNode.IsDefaultConstructor) != 0)
438: && this .constructorCall == null) {
439: this .constructorCall = SuperReference
440: .implicitSuperConstructorCall();
441: this .constructorCall.sourceStart = this .sourceStart;
442: this .constructorCall.sourceEnd = this .sourceEnd;
443: return;
444: }
445: parser.parse(this , unit);
446:
447: }
448:
449: public StringBuffer printBody(int indent, StringBuffer output) {
450: output.append(" {"); //$NON-NLS-1$
451: if (this .constructorCall != null) {
452: output.append('\n');
453: this .constructorCall.printStatement(indent, output);
454: }
455: if (this .statements != null) {
456: for (int i = 0; i < this .statements.length; i++) {
457: output.append('\n');
458: this .statements[i].printStatement(indent, output);
459: }
460: }
461: output.append('\n');
462: printIndent(indent == 0 ? 0 : indent - 1, output).append('}');
463: return output;
464: }
465:
466: public void resolveJavadoc() {
467: if (this .binding == null || this .javadoc != null) {
468: super .resolveJavadoc();
469: } else if ((this .bits & ASTNode.IsDefaultConstructor) == 0) {
470: this .scope.problemReporter().javadocMissing(
471: this .sourceStart, this .sourceEnd,
472: this .binding.modifiers);
473: }
474: }
475:
476: /*
477: * Type checking for constructor, just another method, except for special check
478: * for recursive constructor invocations.
479: */
480: public void resolveStatements() {
481: SourceTypeBinding sourceType = this .scope.enclosingSourceType();
482: if (!CharOperation.equals(sourceType.sourceName, this .selector)) {
483: this .scope.problemReporter().missingReturnType(this );
484: }
485: if (this .typeParameters != null) {
486: for (int i = 0, length = this .typeParameters.length; i < length; i++) {
487: this .typeParameters[i].resolve(this .scope);
488: }
489: }
490: if (this .binding != null && !this .binding.isPrivate()) {
491: sourceType.tagBits |= TagBits.HasNonPrivateConstructor;
492: }
493: // if null ==> an error has occurs at parsing time ....
494: if (this .constructorCall != null) {
495: if (sourceType.id == TypeIds.T_JavaLangObject
496: && this .constructorCall.accessMode != ExplicitConstructorCall.This) {
497: // cannot use super() in java.lang.Object
498: if (this .constructorCall.accessMode == ExplicitConstructorCall.Super) {
499: this .scope.problemReporter()
500: .cannotUseSuperInJavaLangObject(
501: this .constructorCall);
502: }
503: this .constructorCall = null;
504: } else {
505: this .constructorCall.resolve(this .scope);
506: }
507: }
508: if ((this .modifiers & ExtraCompilerModifiers.AccSemicolonBody) != 0) {
509: this .scope.problemReporter().methodNeedBody(this );
510: }
511: super .resolveStatements();
512: }
513:
514: public void traverse(ASTVisitor visitor, ClassScope classScope) {
515: if (visitor.visit(this , classScope)) {
516: if (this .javadoc != null) {
517: this .javadoc.traverse(visitor, this .scope);
518: }
519: if (this .annotations != null) {
520: int annotationsLength = this .annotations.length;
521: for (int i = 0; i < annotationsLength; i++)
522: this .annotations[i].traverse(visitor, this .scope);
523: }
524: if (this .typeParameters != null) {
525: int typeParametersLength = this .typeParameters.length;
526: for (int i = 0; i < typeParametersLength; i++) {
527: this .typeParameters[i]
528: .traverse(visitor, this .scope);
529: }
530: }
531: if (this .arguments != null) {
532: int argumentLength = this .arguments.length;
533: for (int i = 0; i < argumentLength; i++)
534: this .arguments[i].traverse(visitor, this .scope);
535: }
536: if (this .thrownExceptions != null) {
537: int thrownExceptionsLength = this .thrownExceptions.length;
538: for (int i = 0; i < thrownExceptionsLength; i++)
539: this .thrownExceptions[i].traverse(visitor,
540: this .scope);
541: }
542: if (this .constructorCall != null)
543: this .constructorCall.traverse(visitor, this .scope);
544: if (this .statements != null) {
545: int statementsLength = this .statements.length;
546: for (int i = 0; i < statementsLength; i++)
547: this .statements[i].traverse(visitor, this .scope);
548: }
549: }
550: visitor.endVisit(this , classScope);
551: }
552:
553: public TypeParameter[] typeParameters() {
554: return this.typeParameters;
555: }
556: }
|