Copyright (c) 2000, 2018 IBM Corporation and others. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which accompanies this distribution, and is available at https://www.eclipse.org/legal/epl-2.0/ SPDX-License-Identifier: EPL-2.0 Contributors: IBM Corporation - initial API and implementation Stephan Herrmann - Contributions for bug 365662 - [compiler][null] warn on contradictory and redundant null annotations bug 401030 - [1.8][null] Null analysis support for lambda methods. Bug 429958 - [1.8][null] evaluate new DefaultLocation attribute of @NonNullByDefault Bug 435805 - [1.8][compiler][null] Java 8 compiler does not recognize declaration style null annotations
/******************************************************************************* * Copyright (c) 2000, 2018 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 * * Contributors: * IBM Corporation - initial API and implementation * Stephan Herrmann - Contributions for * bug 365662 - [compiler][null] warn on contradictory and redundant null annotations * bug 401030 - [1.8][null] Null analysis support for lambda methods. * Bug 429958 - [1.8][null] evaluate new DefaultLocation attribute of @NonNullByDefault * Bug 435805 - [1.8][compiler][null] Java 8 compiler does not recognize declaration style null annotations *******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.CaseStatement; import org.eclipse.jdt.internal.compiler.ast.LambdaExpression; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.ast.TypeReference; public final class LocalTypeBinding extends NestedTypeBinding { final static char[] LocalTypePrefix = { '$', 'L', 'o', 'c', 'a', 'l', '$' }; private InnerEmulationDependency[] dependents; public CaseStatement enclosingCase; // from 1.4 on, local types should not be accessed across switch case blocks (52221) public int sourceStart; // used by computeUniqueKey to uniquely identify this binding public MethodBinding enclosingMethod; public LocalTypeBinding(ClassScope scope, SourceTypeBinding enclosingType, CaseStatement switchCase) { super( new char[][] {CharOperation.concat(LocalTypeBinding.LocalTypePrefix, scope.referenceContext.name)}, scope, enclosingType); TypeDeclaration typeDeclaration = scope.referenceContext; if ((typeDeclaration.bits & ASTNode.IsAnonymousType) != 0) { this.tagBits |= TagBits.AnonymousTypeMask; } else { this.tagBits |= TagBits.LocalTypeMask; } this.enclosingCase = switchCase; this.sourceStart = typeDeclaration.sourceStart; MethodScope methodScope = scope.enclosingMethodScope(); MethodBinding methodBinding = methodScope.referenceMethodBinding(); if (methodBinding != null) { this.enclosingMethod = methodBinding; } MethodScope lambdaScope = scope.enclosingLambdaScope(); if (lambdaScope != null) { ((LambdaExpression) lambdaScope.referenceContext).addLocalType(this); } } public LocalTypeBinding(LocalTypeBinding prototype) { super(prototype); this.dependents = prototype.dependents; this.enclosingCase = prototype.enclosingCase; this.sourceStart = prototype.sourceStart; this.enclosingMethod = prototype.enclosingMethod; } /* Record a dependency onto a source target type which may be altered * by the end of the innerclass emulation. Later on, we will revisit * all its dependents so as to update them (see updateInnerEmulationDependents()). */ public void addInnerEmulationDependent(BlockScope dependentScope, boolean wasEnclosingInstanceSupplied) { if (!isPrototype()) throw new IllegalStateException(); int index; if (this.dependents == null) { index = 0; this.dependents = new InnerEmulationDependency[1]; } else { index = this.dependents.length; for (int i = 0; i < index; i++) if (this.dependents[i].scope == dependentScope) return; // already stored System.arraycopy(this.dependents, 0, (this.dependents = new InnerEmulationDependency[index + 1]), 0, index); } this.dependents[index] = new InnerEmulationDependency(dependentScope, wasEnclosingInstanceSupplied); // System.out.println("Adding dependency: "+ new String(scope.enclosingType().readableName()) + " --> " + new String(this.readableName())); } @Override public MethodBinding enclosingMethod() { return this.enclosingMethod; } /* * Returns the anonymous original super type (in some error cases, superclass may get substituted with Object) */ public ReferenceBinding anonymousOriginalSuperType() { if (!isPrototype()) return ((LocalTypeBinding) this.prototype).anonymousOriginalSuperType(); if (this.superclass == null && this.scope != null) return this.scope.getJavaLangObject(); if (this.superInterfaces != Binding.NO_SUPERINTERFACES) { return this.superInterfaces[0]; } if ((this.tagBits & TagBits.HierarchyHasProblems) == 0) { return this.superclass; } if (this.scope != null) { TypeReference typeReference = this.scope.referenceContext.allocation.type; if (typeReference != null) { return (ReferenceBinding) typeReference.resolvedType; } } return this.superclass; // default answer } @Override public char[] computeUniqueKey(boolean isLeaf) { if (!isPrototype()) return this.prototype.computeUniqueKey(isLeaf); char[] outerKey = outermostEnclosingType().computeUniqueKey(isLeaf); int semicolon = CharOperation.lastIndexOf(';', outerKey); StringBuffer sig = new StringBuffer(); sig.append(outerKey, 0, semicolon); // insert $sourceStart sig.append('$'); sig.append(String.valueOf(this.sourceStart)); // insert $LocalName if local if (!isAnonymousType()) { sig.append('$'); sig.append(this.sourceName); } // insert remaining from outer key sig.append(outerKey, semicolon, outerKey.length-semicolon); int sigLength = sig.length(); char[] uniqueKey = new char[sigLength]; sig.getChars(0, sigLength, uniqueKey, 0); return uniqueKey; } @Override public char[] constantPoolName() /* java/lang/Object */ { if (this.constantPoolName != null) return this.constantPoolName; if (!isPrototype()) return this.constantPoolName = this.prototype.constantPoolName(); if (this.constantPoolName == null && this.scope != null) { // https://bugs.eclipse.org/bugs/show_bug.cgi?id=322154, we do have some // cases where the left hand does not know what the right is doing. this.constantPoolName = this.scope.compilationUnitScope().computeConstantPoolName(this); } return this.constantPoolName; } @Override public TypeBinding clone(TypeBinding outerType) { LocalTypeBinding copy = new LocalTypeBinding(this); copy.enclosingType = (SourceTypeBinding) outerType; return copy; } @Override public int hashCode() { return this.enclosingType.hashCode(); } /* * Overriden for code assist. In this case, the constantPoolName() has not been computed yet. * Slam the source name so that the signature is syntactically correct. * (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=99686) */ @Override public char[] genericTypeSignature() { if (!isPrototype()) return this.prototype.genericTypeSignature(); if (this.genericReferenceTypeSignature == null && this.constantPoolName == null) { if (isAnonymousType()) setConstantPoolName(superclass().sourceName()); else setConstantPoolName(sourceName()); } return super.genericTypeSignature(); } @Override public char[] readableName() /*java.lang.Object, p.X<T> */ { char[] readableName; if (isAnonymousType()) { readableName = CharOperation.concat(TypeConstants.ANONYM_PREFIX, anonymousOriginalSuperType().readableName(), TypeConstants.ANONYM_SUFFIX); } else if (isMemberType()) { readableName = CharOperation.concat(enclosingType().readableName(), this.sourceName, '.'); } else { readableName = this.sourceName; } TypeVariableBinding[] typeVars; if ((typeVars = typeVariables()) != Binding.NO_TYPE_VARIABLES) { StringBuffer nameBuffer = new StringBuffer(10); nameBuffer.append(readableName).append('<'); for (int i = 0, length = typeVars.length; i < length; i++) { if (i > 0) nameBuffer.append(','); nameBuffer.append(typeVars[i].readableName()); } nameBuffer.append('>'); int nameLength = nameBuffer.length(); readableName = new char[nameLength]; nameBuffer.getChars(0, nameLength, readableName, 0); } return readableName; } @Override public char[] shortReadableName() /*Object*/ { char[] shortReadableName; if (isAnonymousType()) { shortReadableName = CharOperation.concat(TypeConstants.ANONYM_PREFIX, anonymousOriginalSuperType().shortReadableName(), TypeConstants.ANONYM_SUFFIX); } else if (isMemberType()) { shortReadableName = CharOperation.concat(enclosingType().shortReadableName(), this.sourceName, '.'); } else { shortReadableName = this.sourceName; } TypeVariableBinding[] typeVars; if ((typeVars = typeVariables()) != Binding.NO_TYPE_VARIABLES) { StringBuffer nameBuffer = new StringBuffer(10); nameBuffer.append(shortReadableName).append('<'); for (int i = 0, length = typeVars.length; i < length; i++) { if (i > 0) nameBuffer.append(','); nameBuffer.append(typeVars[i].shortReadableName()); } nameBuffer.append('>'); int nameLength = nameBuffer.length(); shortReadableName = new char[nameLength]; nameBuffer.getChars(0, nameLength, shortReadableName, 0); } return shortReadableName; } // Record that the type is a local member type public void setAsMemberType() { if (!isPrototype()) { this.tagBits |= TagBits.MemberTypeMask; ((LocalTypeBinding) this.prototype).setAsMemberType(); return; } this.tagBits |= TagBits.MemberTypeMask; } public void setConstantPoolName(char[] computedConstantPoolName) /* java/lang/Object */ { if (!isPrototype()) { this.constantPoolName = computedConstantPoolName; ((LocalTypeBinding) this.prototype).setConstantPoolName(computedConstantPoolName); return; } this.constantPoolName = computedConstantPoolName; } public void transferConstantPoolNameTo(TypeBinding substType) { if (this.constantPoolName != null && substType instanceof LocalTypeBinding) { LocalTypeBinding substLocalType = (LocalTypeBinding) substType; if (substLocalType.constantPoolName == null) { substLocalType.setConstantPoolName(this.constantPoolName); this.scope.compilationUnitScope().constantPoolNameUsage.put(substLocalType.constantPoolName, substLocalType); } } } /* * Overriden for code assist. In this case, the constantPoolName() has not been computed yet. * Slam the source name so that the signature is syntactically correct. * (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=102284) */ @Override public char[] signature() { if (!isPrototype()) return this.prototype.signature(); if (this.signature == null && this.constantPoolName == null) { if (isAnonymousType()) setConstantPoolName(superclass().sourceName()); else setConstantPoolName(sourceName()); } return super.signature(); } @Override public char[] sourceName() { if (isAnonymousType()) { return CharOperation.concat(TypeConstants.ANONYM_PREFIX, anonymousOriginalSuperType().sourceName(), TypeConstants.ANONYM_SUFFIX); } else return this.sourceName; } @Override public String toString() { if (this.hasTypeAnnotations()) return annotatedDebugName() + " (local)"; //$NON-NLS-1$ if (isAnonymousType()) return "Anonymous type : " + super.toString(); //$NON-NLS-1$ if (isMemberType()) return "Local member type : " + new String(sourceName()) + " " + super.toString(); //$NON-NLS-2$ //$NON-NLS-1$ return "Local type : " + new String(sourceName()) + " " + super.toString(); //$NON-NLS-2$ //$NON-NLS-1$ } /* Trigger the dependency mechanism forcing the innerclass emulation * to be propagated to all dependent source types. */ @Override public void updateInnerEmulationDependents() { if (!isPrototype()) throw new IllegalStateException(); if (this.dependents != null) { for (int i = 0; i < this.dependents.length; i++) { InnerEmulationDependency dependency = this.dependents[i]; // System.out.println("Updating " + new String(this.readableName()) + " --> " + new String(dependency.scope.enclosingType().readableName())); dependency.scope.propagateInnerEmulation(this, dependency.wasEnclosingInstanceSupplied); } } } }