Package org.eclipse.jdt.internal.compiler.ast

Source Code of org.eclipse.jdt.internal.compiler.ast.ExplicitConstructorCall

/*******************************************************************************
* Copyright (c) 2000, 2014 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
*     Stephan Herrmann - Contributions for
*                bug 319201 - [null] no warning when unboxing SingleNameReference causes NPE
*                bug 186342 - [compiler][null] Using annotations for null checking
*                bug 361407 - Resource leak warning when resource is assigned to a field outside of constructor
*                bug 370639 - [compiler][resource] restore the default for resource leak warnings
*                bug 388996 - [compiler][resource] Incorrect 'potential resource leak'
*                bug 403147 - [compiler][null] FUP of bug 400761: consolidate interaction between unboxing, NPE, and deferred checking
*                Bug 400874 - [1.8][compiler] Inference infrastructure should evolve to meet JLS8 18.x (Part G of JSR335 spec)
*                Bug 424710 - [1.8][compiler] CCE in SingleNameReference.localVariableBinding
*                Bug 425152 - [1.8] [compiler] Lambda Expression not resolved but flow analyzed leading to NPE.
*                Bug 424205 - [1.8] Cannot infer type for diamond type with lambda on method invocation
*                Bug 424415 - [1.8][compiler] Eventual resolution of ReferenceExpression is not seen to be happening.
*                Bug 426366 - [1.8][compiler] Type inference doesn't handle multiple candidate target types in outer overload context
*                Bug 426290 - [1.8][compiler] Inference + overloading => wrong method resolution ?
*                Bug 427483 - [Java 8] Variables in lambdas sometimes can't be resolved
*                Bug 427438 - [1.8][compiler] NPE at org.eclipse.jdt.internal.compiler.ast.ConditionalExpression.generateCode(ConditionalExpression.java:280)
*                Bug 428352 - [1.8][compiler] Resolution errors don't always surface
*        Andy Clement (GoPivotal, Inc) aclement@gopivotal.com - Contributions for
*                          Bug 409245 - [1.8][compiler] Type annotations dropped when call is routed through a synthetic bridge method
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;

import static org.eclipse.jdt.internal.compiler.ast.ExpressionContext.INVOCATION_CONTEXT;

import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.codegen.CodeStream;
import org.eclipse.jdt.internal.compiler.codegen.Opcodes;
import org.eclipse.jdt.internal.compiler.flow.FlowContext;
import org.eclipse.jdt.internal.compiler.flow.FlowInfo;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers;
import org.eclipse.jdt.internal.compiler.lookup.InferenceContext18;
import org.eclipse.jdt.internal.compiler.lookup.LocalTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
import org.eclipse.jdt.internal.compiler.lookup.ParameterizedGenericMethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.ParameterizedMethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.ProblemMethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.RawTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.Scope;
import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TagBits;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
import org.eclipse.jdt.internal.compiler.lookup.VariableBinding;
import org.eclipse.jdt.internal.compiler.util.SimpleLookupTable;

public class ExplicitConstructorCall extends Statement implements Invocation {

  public Expression[] arguments;
  public Expression qualification;
  public MethodBinding binding;              // exact binding resulting from lookup
  MethodBinding syntheticAccessor;            // synthetic accessor for inner-emulation
  public int accessMode;
  public TypeReference[] typeArguments;
  public TypeBinding[] genericTypeArguments;

  public final static int ImplicitSuper = 1;
  public final static int Super = 2;
  public final static int This = 3;

  public VariableBinding[][] implicitArguments;

  // TODO Remove once DOMParser is activated
  public int typeArgumentsSourceStart;

   // hold on to this context from invocation applicability inference until invocation type inference (per method candidate):
  private SimpleLookupTable/*<PGMB,InferenceContext18>*/ inferenceContexts;
  private InnerInferenceHelper innerInferenceHelper;

  public ExplicitConstructorCall(int accessMode) {
    this.accessMode = accessMode;
  }

  public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
    // must verify that exceptions potentially thrown by this expression are caught in the method.

    try {
      ((MethodScope) currentScope).isConstructorCall = true;

      // process enclosing instance
      if (this.qualification != null) {
        flowInfo =
          this.qualification
            .analyseCode(currentScope, flowContext, flowInfo)
            .unconditionalInits();
      }
      // process arguments
      if (this.arguments != null) {
        boolean analyseResources = currentScope.compilerOptions().analyseResourceLeaks;
        for (int i = 0, max = this.arguments.length; i < max; i++) {
          flowInfo =
            this.arguments[i]
              .analyseCode(currentScope, flowContext, flowInfo)
              .unconditionalInits();
          if (analyseResources) {
            // if argument is an AutoCloseable insert info that it *may* be closed (by the target constructor, i.e.)
            flowInfo = FakedTrackingVariable.markPassedToOutside(currentScope, this.arguments[i], flowInfo, flowContext, false);
          }
          this.arguments[i].checkNPEbyUnboxing(currentScope, flowContext, flowInfo);
        }
        analyseArguments(currentScope, flowContext, flowInfo, this.binding, this.arguments);
      }

      ReferenceBinding[] thrownExceptions;
      if ((thrownExceptions = this.binding.thrownExceptions) != Binding.NO_EXCEPTIONS) {
        if ((this.bits & ASTNode.Unchecked) != 0 && this.genericTypeArguments == null) {
          // https://bugs.eclipse.org/bugs/show_bug.cgi?id=277643, align with javac on JLS 15.12.2.6
          thrownExceptions = currentScope.environment().convertToRawTypes(this.binding.thrownExceptions, true, true);
        }       
        // check exceptions
        flowContext.checkExceptionHandlers(
          thrownExceptions,
          (this.accessMode == ExplicitConstructorCall.ImplicitSuper)
            ? (ASTNode) currentScope.methodScope().referenceContext
            : (ASTNode) this,
          flowInfo,
          currentScope);
      }
      manageEnclosingInstanceAccessIfNecessary(currentScope, flowInfo);
      manageSyntheticAccessIfNecessary(currentScope, flowInfo);
      return flowInfo;
    } finally {
      ((MethodScope) currentScope).isConstructorCall = false;
    }
  }

  /**
   * Constructor call code generation
   *
   * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
   * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
   */
  public void generateCode(BlockScope currentScope, CodeStream codeStream) {
    if ((this.bits & ASTNode.IsReachable) == 0) {
      return;
    }
    try {
      ((MethodScope) currentScope).isConstructorCall = true;

      int pc = codeStream.position;
      codeStream.aload_0();

      MethodBinding codegenBinding = this.binding.original();
      ReferenceBinding targetType = codegenBinding.declaringClass;

      // special name&ordinal argument generation for enum constructors
      if (targetType.erasure().id == TypeIds.T_JavaLangEnum || targetType.isEnum()) {
        codeStream.aload_1(); // pass along name param as name arg
        codeStream.iload_2(); // pass along ordinal param as ordinal arg
      }
      // handling innerclass constructor invocation
      // handling innerclass instance allocation - enclosing instance arguments
      if (targetType.isNestedType()) {
        codeStream.generateSyntheticEnclosingInstanceValues(
          currentScope,
          targetType,
          (this.bits & ASTNode.DiscardEnclosingInstance) != 0 ? null : this.qualification,
          this);
      }
      // generate arguments
      generateArguments(this.binding, this.arguments, currentScope, codeStream);

      // handling innerclass instance allocation - outer local arguments
      if (targetType.isNestedType()) {
        codeStream.generateSyntheticOuterArgumentValues(
          currentScope,
          targetType,
          this);
      }
      if (this.syntheticAccessor != null) {
        // synthetic accessor got some extra arguments appended to its signature, which need values
        for (int i = 0,
          max = this.syntheticAccessor.parameters.length - codegenBinding.parameters.length;
          i < max;
          i++) {
          codeStream.aconst_null();
        }
        codeStream.invoke(Opcodes.OPC_invokespecial, this.syntheticAccessor, null /* default declaringClass */, this.typeArguments);
      } else {
        codeStream.invoke(Opcodes.OPC_invokespecial, codegenBinding, null /* default declaringClass */, this.typeArguments);
      }
      codeStream.recordPositionsFrom(pc, this.sourceStart);
    } finally {
      ((MethodScope) currentScope).isConstructorCall = false;
    }
  }

  /**
   * @see org.eclipse.jdt.internal.compiler.lookup.InvocationSite#genericTypeArguments()
   */
  public TypeBinding[] genericTypeArguments() {
    return this.genericTypeArguments;
  }

  public boolean isImplicitSuper() {
    return (this.accessMode == ExplicitConstructorCall.ImplicitSuper);
  }

  public boolean isSuperAccess() {
    return this.accessMode != ExplicitConstructorCall.This;
  }

  public boolean isTypeAccess() {
    return true;
  }

  /* Inner emulation consists in either recording a dependency
   * link only, or performing one level of propagation.
   *
   * Dependency mechanism is used whenever dealing with source target
   * types, since by the time we reach them, we might not yet know their
   * exact need.
   */
  void manageEnclosingInstanceAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) {
    ReferenceBinding superTypeErasure = (ReferenceBinding) this.binding.declaringClass.erasure();

    if ((flowInfo.tagBits & FlowInfo.UNREACHABLE_OR_DEAD) == 0)  {
    // perform some emulation work in case there is some and we are inside a local type only
    if (superTypeErasure.isNestedType()
      && currentScope.enclosingSourceType().isLocalType()) {

      if (superTypeErasure.isLocalType()) {
        ((LocalTypeBinding) superTypeErasure).addInnerEmulationDependent(currentScope, this.qualification != null);
      } else {
        // locally propagate, since we already now the desired shape for sure
        currentScope.propagateInnerEmulation(superTypeErasure, this.qualification != null);
      }
    }
    }
  }

  public void manageSyntheticAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) {
    if ((flowInfo.tagBits & FlowInfo.UNREACHABLE_OR_DEAD) == 0)  {
      // if constructor from parameterized type got found, use the original constructor at codegen time
      MethodBinding codegenBinding = this.binding.original();

      // perform some emulation work in case there is some and we are inside a local type only
      if (this.binding.isPrivate() && this.accessMode != ExplicitConstructorCall.This) {
        ReferenceBinding declaringClass = codegenBinding.declaringClass;
        // from 1.4 on, local type constructor can lose their private flag to ease emulation
        if ((declaringClass.tagBits & TagBits.IsLocalType) != 0 && currentScope.compilerOptions().complianceLevel >= ClassFileConstants.JDK1_4) {
          // constructor will not be dumped as private, no emulation required thus
          codegenBinding.tagBits |= TagBits.ClearPrivateModifier;
        } else {
          this.syntheticAccessor = ((SourceTypeBinding) declaringClass).addSyntheticMethod(codegenBinding, isSuperAccess());
          currentScope.problemReporter().needToEmulateMethodAccess(codegenBinding, this);
        }
      }
    }
  }

  public StringBuffer printStatement(int indent, StringBuffer output) {
    printIndent(indent, output);
    if (this.qualification != null) this.qualification.printExpression(0, output).append('.');
    if (this.typeArguments != null) {
      output.append('<');
      int max = this.typeArguments.length - 1;
      for (int j = 0; j < max; j++) {
        this.typeArguments[j].print(0, output);
        output.append(", ");//$NON-NLS-1$
      }
      this.typeArguments[max].print(0, output);
      output.append('>');
    }
    if (this.accessMode == ExplicitConstructorCall.This) {
      output.append("this("); //$NON-NLS-1$
    } else {
      output.append("super("); //$NON-NLS-1$
    }
    if (this.arguments != null) {
      for (int i = 0; i < this.arguments.length; i++) {
        if (i > 0) output.append(", "); //$NON-NLS-1$
        this.arguments[i].printExpression(0, output);
      }
    }
    return output.append(");"); //$NON-NLS-1$
  }

  public void resolve(BlockScope scope) {
    // the return type should be void for a constructor.
    // the test is made into getConstructor

    // mark the fact that we are in a constructor call.....
    // unmark at all returns
    MethodScope methodScope = scope.methodScope();
    try {
      AbstractMethodDeclaration methodDeclaration = methodScope.referenceMethod();
      if (methodDeclaration == null
          || !methodDeclaration.isConstructor()
          || ((ConstructorDeclaration) methodDeclaration).constructorCall != this) {
        scope.problemReporter().invalidExplicitConstructorCall(this);
        // fault-tolerance
        if (this.qualification != null) {
          this.qualification.resolveType(scope);
        }
        if (this.typeArguments != null) {
          for (int i = 0, max = this.typeArguments.length; i < max; i++) {
            this.typeArguments[i].resolveType(scope, true /* check bounds*/);
          }
        }
        if (this.arguments != null) {
          for (int i = 0, max = this.arguments.length; i < max; i++) {
            this.arguments[i].resolveType(scope);
          }
        }
        return;
      }
      methodScope.isConstructorCall = true;
      ReferenceBinding receiverType = scope.enclosingReceiverType();
      boolean rcvHasError = false;
      if (this.accessMode != ExplicitConstructorCall.This) {
        receiverType = receiverType.superclass();
        TypeReference superclassRef = scope.referenceType().superclass;
        if (superclassRef != null && superclassRef.resolvedType != null && !superclassRef.resolvedType.isValidBinding()) {
          rcvHasError = true;
        }
      }
      if (receiverType != null) {
        // prevent (explicit) super constructor invocation from within enum
        if (this.accessMode == ExplicitConstructorCall.Super && receiverType.erasure().id == TypeIds.T_JavaLangEnum) {
          scope.problemReporter().cannotInvokeSuperConstructorInEnum(this, methodScope.referenceMethod().binding);
        }
        // qualification should be from the type of the enclosingType
        if (this.qualification != null) {
          if (this.accessMode != ExplicitConstructorCall.Super) {
            scope.problemReporter().unnecessaryEnclosingInstanceSpecification(
              this.qualification,
              receiverType);
          }
          if (!rcvHasError) {
            ReferenceBinding enclosingType = receiverType.enclosingType();
            if (enclosingType == null) {
              scope.problemReporter().unnecessaryEnclosingInstanceSpecification(this.qualification, receiverType);
              this.bits |= ASTNode.DiscardEnclosingInstance;
            } else {
              TypeBinding qTb = this.qualification.resolveTypeExpecting(scope, enclosingType);
              this.qualification.computeConversion(scope, qTb, qTb);
            }
          }
        }
      }
      // resolve type arguments (for generic constructor call)
      long sourceLevel = scope.compilerOptions().sourceLevel;
      if (this.typeArguments != null) {
        boolean argHasError = sourceLevel < ClassFileConstants.JDK1_5;
        int length = this.typeArguments.length;
        this.genericTypeArguments = new TypeBinding[length];
        for (int i = 0; i < length; i++) {
          TypeReference typeReference = this.typeArguments[i];
          if ((this.genericTypeArguments[i] = typeReference.resolveType(scope, true /* check bounds*/)) == null) {
            argHasError = true;
          }
          if (argHasError && typeReference instanceof Wildcard) {
            scope.problemReporter().illegalUsageOfWildcard(typeReference);
          }
        }
        if (argHasError) {
          if (this.arguments != null) { // still attempt to resolve arguments
            for (int i = 0, max = this.arguments.length; i < max; i++) {
              this.arguments[i].resolveType(scope);
            }
          }
          return;
        }
      }
      // arguments buffering for the method lookup
      TypeBinding[] argumentTypes = Binding.NO_PARAMETERS;
      boolean argsContainCast = false;
      if (this.arguments != null) {
        boolean argHasError = false; // typeChecks all arguments
        int length = this.arguments.length;
        argumentTypes = new TypeBinding[length];
        for (int i = 0; i < length; i++) {
          Expression argument = this.arguments[i];
          if (argument instanceof CastExpression) {
            argument.bits |= ASTNode.DisableUnnecessaryCastCheck; // will check later on
            argsContainCast = true;
          }
          argument.setExpressionContext(INVOCATION_CONTEXT);
          if ((argumentTypes[i] = argument.resolveType(scope)) == null) {
            argHasError = true;
          }
          if (sourceLevel >= ClassFileConstants.JDK1_8 && argument.isPolyExpression()) {
            if (this.innerInferenceHelper == null)
              this.innerInferenceHelper = new InnerInferenceHelper();
          }
        }
        if (argHasError) {
          if (receiverType == null) {
            return;
          }
          // record a best guess, for clients who need hint about possible contructor match
          TypeBinding[] pseudoArgs = new TypeBinding[length];
          for (int i = length; --i >= 0;) {
            pseudoArgs[i] = argumentTypes[i] == null ? TypeBinding.NULL : argumentTypes[i]; // replace args with errors with null type
          }
          this.binding = scope.findMethod(receiverType, TypeConstants.INIT, pseudoArgs, this, false);
          if (this.binding != null && !this.binding.isValidBinding()) {
            MethodBinding closestMatch = ((ProblemMethodBinding)this.binding).closestMatch;
            // record the closest match, for clients who may still need hint about possible method match
            if (closestMatch != null) {
              if (closestMatch.original().typeVariables != Binding.NO_TYPE_VARIABLES) { // generic method
                // shouldn't return generic method outside its context, rather convert it to raw method (175409)
                closestMatch = scope.environment().createParameterizedGenericMethod(closestMatch.original(), (RawTypeBinding)null);
              }
              this.binding = closestMatch;
              MethodBinding closestMatchOriginal = closestMatch.original();
              if (closestMatchOriginal.isOrEnclosedByPrivateType() && !scope.isDefinedInMethod(closestMatchOriginal)) {
                // ignore cases where method is used from within inside itself (e.g. direct recursions)
                closestMatchOriginal.modifiers |= ExtraCompilerModifiers.AccLocallyUsed;
              }
            }
          }
          return;
        }
      } else if (receiverType.erasure().id == TypeIds.T_JavaLangEnum) {
        // TODO (philippe) get rid of once well-known binding is available
        argumentTypes = new TypeBinding[] { scope.getJavaLangString(), TypeBinding.INT };
      }
      if (receiverType == null) {
        return;
      }
      this.binding = findConstructorBinding(scope, this, receiverType, argumentTypes);

      if (this.binding.isValidBinding()) {
        if ((this.binding.tagBits & TagBits.HasMissingType) != 0) {
          if (!methodScope.enclosingSourceType().isAnonymousType()) {
            scope.problemReporter().missingTypeInConstructor(this, this.binding);
          }
        }
        if (isMethodUseDeprecated(this.binding, scope, this.accessMode != ExplicitConstructorCall.ImplicitSuper)) {
          scope.problemReporter().deprecatedMethod(this.binding, this);
        }
        if (checkInvocationArguments(scope, null, receiverType, this.binding, this.arguments, argumentTypes, argsContainCast, this)) {
          this.bits |= ASTNode.Unchecked;
        }
        if (this.binding.isOrEnclosedByPrivateType()) {
          this.binding.original().modifiers |= ExtraCompilerModifiers.AccLocallyUsed;
        }
        if (this.typeArguments != null
            && this.binding.original().typeVariables == Binding.NO_TYPE_VARIABLES) {
          scope.problemReporter().unnecessaryTypeArgumentsForMethodInvocation(this.binding, this.genericTypeArguments, this.typeArguments);
        }
      } else {
        if (this.binding.declaringClass == null) {
          this.binding.declaringClass = receiverType;
        }
        if (rcvHasError)
          return;
        scope.problemReporter().invalidConstructor(this, this.binding);
      }
    } finally {
      methodScope.isConstructorCall = false;
    }
  }

  public void setActualReceiverType(ReferenceBinding receiverType) {
    // ignored
  }

  public void setDepth(int depth) {
    // ignore for here
  }

  public void setFieldIndex(int depth) {
    // ignore for here
  }
 
  public void traverse(ASTVisitor visitor, BlockScope scope) {
    if (visitor.visit(this, scope)) {
      if (this.qualification != null) {
        this.qualification.traverse(visitor, scope);
      }
      if (this.typeArguments != null) {
        for (int i = 0, typeArgumentsLength = this.typeArguments.length; i < typeArgumentsLength; i++) {
          this.typeArguments[i].traverse(visitor, scope);
        }
      }
      if (this.arguments != null) {
        for (int i = 0, argumentLength = this.arguments.length; i < argumentLength; i++)
          this.arguments[i].traverse(visitor, scope);
      }
    }
    visitor.endVisit(this, scope);
  }

  // -- interface Invocation: --
  public MethodBinding binding(TypeBinding targetType, boolean reportErrors, Scope scope) {
    if (reportErrors) {
      if (this.binding == null)
        scope.problemReporter().genericInferenceError("constructor is unexpectedly unresolved", this); //$NON-NLS-1$
      else if (!this.binding.isValidBinding())
        scope.problemReporter().invalidConstructor(this, this.binding);
    }
    return this.binding;
  }
  public Expression[] arguments() {
    return this.arguments;
  }
  public boolean updateBindings(MethodBinding updatedBinding, TypeBinding targetType) {
    boolean hasUpdate = this.binding != updatedBinding;
    if (this.inferenceContexts != null) {
      InferenceContext18 ctx = (InferenceContext18)this.inferenceContexts.removeKey(this.binding);
      if (ctx != null && updatedBinding instanceof ParameterizedGenericMethodBinding) {
        this.inferenceContexts.put(updatedBinding, ctx);
        // solution may have come from an outer inference, mark now that this (inner) is done (but not deep inners):
        hasUpdate |= ctx.registerSolution(targetType, updatedBinding);
      }
    }
    this.binding = updatedBinding;
    return hasUpdate;
  }
  public void registerInferenceContext(ParameterizedGenericMethodBinding method, InferenceContext18 infCtx18) {
    if (this.inferenceContexts == null)
      this.inferenceContexts = new SimpleLookupTable();
    this.inferenceContexts.put(method, infCtx18);
  }
  public InferenceContext18 getInferenceContext(ParameterizedMethodBinding method) {
    if (this.inferenceContexts == null)
      return null;
    return (InferenceContext18) this.inferenceContexts.get(method);
  }
  public boolean usesInference() {
    return (this.binding instanceof ParameterizedGenericMethodBinding)
        && getInferenceContext((ParameterizedGenericMethodBinding) this.binding) != null;
  }
  public boolean innersNeedUpdate() {
    return this.innerInferenceHelper != null;
  }
  public void innerUpdateDone() {
    this.innerInferenceHelper = null;
  }
  public InnerInferenceHelper innerInferenceHelper() {
    return this.innerInferenceHelper;
  }

  // -- interface InvocationSite: --
  public InferenceContext18 freshInferenceContext(Scope scope) {
    return new InferenceContext18(scope, this.arguments, this);
  }
}
TOP

Related Classes of org.eclipse.jdt.internal.compiler.ast.ExplicitConstructorCall

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.