X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;f=src%2Forg%2Feclipse%2Fjdt%2Finternal%2Fcompiler%2Flookup%2FBlockScope.java;fp=src%2Forg%2Feclipse%2Fjdt%2Finternal%2Fcompiler%2Flookup%2FBlockScope.java;h=f0f94517b76ff29095221da26f8056aca5f598b2;hb=040fa5af2cd00017cf3575950cdaade34a6d7f6c;hp=0000000000000000000000000000000000000000;hpb=a580fb8376d315d05e4d6bfdff9ff1101a151cd6;p=org.ibex.tool.git diff --git a/src/org/eclipse/jdt/internal/compiler/lookup/BlockScope.java b/src/org/eclipse/jdt/internal/compiler/lookup/BlockScope.java new file mode 100644 index 0000000..f0f9451 --- /dev/null +++ b/src/org/eclipse/jdt/internal/compiler/lookup/BlockScope.java @@ -0,0 +1,826 @@ +/******************************************************************************* + * 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.lookup; + +import org.eclipse.jdt.core.compiler.CharOperation; +import org.eclipse.jdt.internal.compiler.ast.*; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; +import org.eclipse.jdt.internal.compiler.codegen.CodeStream; +import org.eclipse.jdt.internal.compiler.impl.Constant; +import org.eclipse.jdt.internal.compiler.problem.ProblemReporter; + +public class BlockScope extends Scope { + + // Local variable management + public LocalVariableBinding[] locals; + public int localIndex; // position for next variable + public int startIndex; // start position in this scope - for ordering scopes vs. variables + public int offset; // for variable allocation throughout scopes + public int maxOffset; // for variable allocation throughout scopes + + // finally scopes must be shifted behind respective try&catch scope(s) so as to avoid + // collisions of secret variables (return address, save value). + public BlockScope[] shiftScopes; + + public final static VariableBinding[] EmulationPathToImplicitThis = {}; + public final static VariableBinding[] NoEnclosingInstanceInConstructorCall = {}; + public final static VariableBinding[] NoEnclosingInstanceInStaticContext = {}; + + public Scope[] subscopes = new Scope[1]; // need access from code assist + public int subscopeCount = 0; // need access from code assist + + // record the current case statement being processed (for entire switch case block). + public CaseStatement switchCase; // from 1.4 on, local types should not be accessed across switch case blocks (52221) + + protected BlockScope(int kind, Scope parent) { + + super(kind, parent); + } + + public BlockScope(BlockScope parent) { + + this(parent, true); + } + + public BlockScope(BlockScope parent, boolean addToParentScope) { + + this(BLOCK_SCOPE, parent); + locals = new LocalVariableBinding[5]; + if (addToParentScope) parent.addSubscope(this); + this.startIndex = parent.localIndex; + } + + public BlockScope(BlockScope parent, int variableCount) { + + this(BLOCK_SCOPE, parent); + locals = new LocalVariableBinding[variableCount]; + parent.addSubscope(this); + this.startIndex = parent.localIndex; + } + + /* Create the class scope & binding for the anonymous type. + */ + public final void addAnonymousType( + TypeDeclaration anonymousType, + ReferenceBinding superBinding) { + + ClassScope anonymousClassScope = new ClassScope(this, anonymousType); + anonymousClassScope.buildAnonymousTypeBinding( + enclosingSourceType(), + superBinding); + } + + /* Create the class scope & binding for the local type. + */ + public final void addLocalType(TypeDeclaration localType) { + + // check that the localType does not conflict with an enclosing type + ReferenceBinding type = enclosingSourceType(); + do { + if (CharOperation.equals(type.sourceName, localType.name)) { + problemReporter().hidingEnclosingType(localType); + return; + } + type = type.enclosingType(); + } while (type != null); + + // check that the localType does not conflict with another sibling local type + Scope scope = this; + do { + if (((BlockScope) scope).findLocalType(localType.name) != null) { + problemReporter().duplicateNestedType(localType); + return; + } + } while ((scope = scope.parent) instanceof BlockScope); + + ClassScope localTypeScope = new ClassScope(this, localType); + addSubscope(localTypeScope); + localTypeScope.buildLocalTypeBinding(enclosingSourceType()); + } + + /* Insert a local variable into a given scope, updating its position + * and checking there are not too many locals or arguments allocated. + */ + public final void addLocalVariable(LocalVariableBinding binding) { + + checkAndSetModifiersForVariable(binding); + + // insert local in scope + if (localIndex == locals.length) + System.arraycopy( + locals, + 0, + (locals = new LocalVariableBinding[localIndex * 2]), + 0, + localIndex); + locals[localIndex++] = binding; + + // update local variable binding + binding.declaringScope = this; + binding.id = this.outerMostMethodScope().analysisIndex++; + // share the outermost method scope analysisIndex + } + + public void addSubscope(Scope childScope) { + if (subscopeCount == subscopes.length) + System.arraycopy( + subscopes, + 0, + (subscopes = new Scope[subscopeCount * 2]), + 0, + subscopeCount); + subscopes[subscopeCount++] = childScope; + } + + /* Answer true if the receiver is suitable for assigning final blank fields. + * + * in other words, it is inside an initializer, a constructor or a clinit + */ + public final boolean allowBlankFinalFieldAssignment(FieldBinding binding) { + + if (enclosingSourceType() != binding.declaringClass) + return false; + + MethodScope methodScope = methodScope(); + if (methodScope.isStatic != binding.isStatic()) + return false; + return methodScope.isInsideInitializer() // inside initializer + || ((AbstractMethodDeclaration) methodScope.referenceContext) + .isInitializationMethod(); // inside constructor or clinit + } + String basicToString(int tab) { + String newLine = "\n"; //$NON-NLS-1$ + for (int i = tab; --i >= 0;) + newLine += "\t"; //$NON-NLS-1$ + + String s = newLine + "--- Block Scope ---"; //$NON-NLS-1$ + newLine += "\t"; //$NON-NLS-1$ + s += newLine + "locals:"; //$NON-NLS-1$ + for (int i = 0; i < localIndex; i++) + s += newLine + "\t" + locals[i].toString(); //$NON-NLS-1$ + s += newLine + "startIndex = " + startIndex; //$NON-NLS-1$ + return s; + } + + private void checkAndSetModifiersForVariable(LocalVariableBinding varBinding) { + + int modifiers = varBinding.modifiers; + if ((modifiers & AccAlternateModifierProblem) != 0 && varBinding.declaration != null){ + problemReporter().duplicateModifierForVariable(varBinding.declaration, this instanceof MethodScope); + } + int realModifiers = modifiers & AccJustFlag; + + int unexpectedModifiers = ~AccFinal; + if ((realModifiers & unexpectedModifiers) != 0 && varBinding.declaration != null){ + problemReporter().illegalModifierForVariable(varBinding.declaration, this instanceof MethodScope); + } + varBinding.modifiers = modifiers; + } + + /* Compute variable positions in scopes given an initial position offset + * ignoring unused local variables. + * + * No argument is expected here (ilocal is the first non-argument local of the outermost scope) + * Arguments are managed by the MethodScope method + */ + void computeLocalVariablePositions(int ilocal, int initOffset, CodeStream codeStream) { + + this.offset = initOffset; + this.maxOffset = initOffset; + + // local variable init + int maxLocals = this.localIndex; + boolean hasMoreVariables = ilocal < maxLocals; + + // scope init + int iscope = 0, maxScopes = this.subscopeCount; + boolean hasMoreScopes = maxScopes > 0; + + // iterate scopes and variables in parallel + while (hasMoreVariables || hasMoreScopes) { + if (hasMoreScopes + && (!hasMoreVariables || (subscopes[iscope].startIndex() <= ilocal))) { + // consider subscope first + if (subscopes[iscope] instanceof BlockScope) { + BlockScope subscope = (BlockScope) subscopes[iscope]; + int subOffset = subscope.shiftScopes == null ? this.offset : subscope.maxShiftedOffset(); + subscope.computeLocalVariablePositions(0, subOffset, codeStream); + if (subscope.maxOffset > this.maxOffset) + this.maxOffset = subscope.maxOffset; + } + hasMoreScopes = ++iscope < maxScopes; + } else { + + // consider variable first + LocalVariableBinding local = locals[ilocal]; // if no local at all, will be locals[ilocal]==null + + // check if variable is actually used, and may force it to be preserved + boolean generateCurrentLocalVar = (local.useFlag == LocalVariableBinding.USED && (local.constant == Constant.NotAConstant)); + + // do not report fake used variable + if (local.useFlag == LocalVariableBinding.UNUSED + && (local.declaration != null) // unused (and non secret) local + && ((local.declaration.bits & ASTNode.IsLocalDeclarationReachableMASK) != 0)) { // declaration is reachable + + if (!(local.declaration instanceof Argument)) // do not report unused catch arguments + this.problemReporter().unusedLocalVariable(local.declaration); + } + + // could be optimized out, but does need to preserve unread variables ? + if (!generateCurrentLocalVar) { + if (local.declaration != null && environment().options.preserveAllLocalVariables) { + generateCurrentLocalVar = true; // force it to be preserved in the generated code + local.useFlag = LocalVariableBinding.USED; + } + } + + // allocate variable + if (generateCurrentLocalVar) { + + if (local.declaration != null) { + codeStream.record(local); // record user-defined local variables for attribute generation + } + // assign variable position + local.resolvedPosition = this.offset; + + if ((local.type == LongBinding) || (local.type == DoubleBinding)) { + this.offset += 2; + } else { + this.offset++; + } + if (this.offset > 0xFFFF) { // no more than 65535 words of locals + this.problemReporter().noMoreAvailableSpaceForLocal( + local, + local.declaration == null ? (ASTNode)this.methodScope().referenceContext : local.declaration); + } + } else { + local.resolvedPosition = -1; // not generated + } + hasMoreVariables = ++ilocal < maxLocals; + } + } + if (this.offset > this.maxOffset) + this.maxOffset = this.offset; + } + + /* + * Record the suitable binding denoting a synthetic field or constructor argument, + * mapping to the actual outer local variable in the scope context. + * Note that this may not need any effect, in case the outer local variable does not + * need to be emulated and can directly be used as is (using its back pointer to its + * declaring scope). + */ + public void emulateOuterAccess(LocalVariableBinding outerLocalVariable) { + + MethodScope currentMethodScope; + if ((currentMethodScope = this.methodScope()) + != outerLocalVariable.declaringScope.methodScope()) { + NestedTypeBinding currentType = (NestedTypeBinding) this.enclosingSourceType(); + + //do nothing for member types, pre emulation was performed already + if (!currentType.isLocalType()) { + return; + } + // must also add a synthetic field if we're not inside a constructor + if (!currentMethodScope.isInsideInitializerOrConstructor()) { + currentType.addSyntheticArgumentAndField(outerLocalVariable); + } else { + currentType.addSyntheticArgument(outerLocalVariable); + } + } + } + + /* Note that it must never produce a direct access to the targetEnclosingType, + * but instead a field sequence (this$2.this$1.this$0) so as to handle such a test case: + * + * class XX { + * void foo() { + * class A { + * class B { + * class C { + * boolean foo() { + * return (Object) A.this == (Object) B.this; + * } + * } + * } + * } + * new A().new B().new C(); + * } + * } + * where we only want to deal with ONE enclosing instance for C (could not figure out an A for C) + */ + public final ReferenceBinding findLocalType(char[] name) { + + long compliance = environment().options.complianceLevel; + for (int i = 0, length = subscopeCount; i < length; i++) { + if (subscopes[i] instanceof ClassScope) { + LocalTypeBinding sourceType = (LocalTypeBinding)((ClassScope) subscopes[i]).referenceContext.binding; + // from 1.4 on, local types should not be accessed across switch case blocks (52221) + if (compliance >= ClassFileConstants.JDK1_4 && sourceType.switchCase != this.switchCase) continue; + if (CharOperation.equals(sourceType.sourceName(), name)) + return sourceType; + } + } + return null; + } + + public LocalVariableBinding findVariable(char[] variable) { + + int varLength = variable.length; + for (int i = 0, length = locals.length; i < length; i++) { + LocalVariableBinding local = locals[i]; + if (local == null) + return null; + if (local.name.length == varLength && CharOperation.equals(local.name, variable)) + return local; + } + return null; + } + /* API + * flag is a mask of the following values VARIABLE (= FIELD or LOCAL), TYPE. + * Only bindings corresponding to the mask will be answered. + * + * if the VARIABLE mask is set then + * If the first name provided is a field (or local) then the field (or local) is answered + * Otherwise, package names and type names are consumed until a field is found. + * In this case, the field is answered. + * + * if the TYPE mask is set, + * package names and type names are consumed until the end of the input. + * Only if all of the input is consumed is the type answered + * + * All other conditions are errors, and a problem binding is returned. + * + * NOTE: If a problem binding is returned, senders should extract the compound name + * from the binding & not assume the problem applies to the entire compoundName. + * + * The VARIABLE mask has precedence over the TYPE mask. + * + * InvocationSite implements + * isSuperAccess(); this is used to determine if the discovered field is visible. + * setFieldIndex(int); this is used to record the number of names that were consumed. + * + * For example, getBinding({"foo","y","q", VARIABLE, site) will answer + * the binding for the field or local named "foo" (or an error binding if none exists). + * In addition, setFieldIndex(1) will be sent to the invocation site. + * If a type named "foo" exists, it will not be detected (and an error binding will be answered) + * + * IMPORTANT NOTE: This method is written under the assumption that compoundName is longer than length 1. + */ + public Binding getBinding(char[][] compoundName, int mask, InvocationSite invocationSite, boolean needResolve) { + + Binding binding = getBinding(compoundName[0], mask | TYPE | PACKAGE, invocationSite, needResolve); + invocationSite.setFieldIndex(1); + if (binding instanceof VariableBinding) return binding; + compilationUnitScope().recordSimpleReference(compoundName[0]); + if (!binding.isValidBinding()) return binding; + + int length = compoundName.length; + int currentIndex = 1; + foundType : if (binding instanceof PackageBinding) { + PackageBinding packageBinding = (PackageBinding) binding; + while (currentIndex < length) { + compilationUnitScope().recordReference(packageBinding.compoundName, compoundName[currentIndex]); + binding = packageBinding.getTypeOrPackage(compoundName[currentIndex++]); + invocationSite.setFieldIndex(currentIndex); + if (binding == null) { + if (currentIndex == length) { + // must be a type if its the last name, otherwise we have no idea if its a package or type + return new ProblemReferenceBinding( + CharOperation.subarray(compoundName, 0, currentIndex), + NotFound); + } + return new ProblemBinding( + CharOperation.subarray(compoundName, 0, currentIndex), + NotFound); + } + if (binding instanceof ReferenceBinding) { + if (!binding.isValidBinding()) + return new ProblemReferenceBinding( + CharOperation.subarray(compoundName, 0, currentIndex), + binding.problemId()); + if (!((ReferenceBinding) binding).canBeSeenBy(this)) + return new ProblemReferenceBinding( + CharOperation.subarray(compoundName, 0, currentIndex), + (ReferenceBinding) binding, + NotVisible); + break foundType; + } + packageBinding = (PackageBinding) binding; + } + + // It is illegal to request a PACKAGE from this method. + return new ProblemReferenceBinding( + CharOperation.subarray(compoundName, 0, currentIndex), + NotFound); + } + + // know binding is now a ReferenceBinding + while (currentIndex < length) { + ReferenceBinding typeBinding = (ReferenceBinding) binding; + char[] nextName = compoundName[currentIndex++]; + invocationSite.setFieldIndex(currentIndex); + invocationSite.setActualReceiverType(typeBinding); + if ((mask & FIELD) != 0 && (binding = findField(typeBinding, nextName, invocationSite, true /*resolve*/)) != null) { + if (!binding.isValidBinding()) + return new ProblemFieldBinding( + ((FieldBinding) binding).declaringClass, + CharOperation.subarray(compoundName, 0, currentIndex), + binding.problemId()); + break; // binding is now a field + } + if ((binding = findMemberType(nextName, typeBinding)) == null) { + if ((mask & FIELD) != 0) { + return new ProblemBinding( + CharOperation.subarray(compoundName, 0, currentIndex), + typeBinding, + NotFound); + } + return new ProblemReferenceBinding( + CharOperation.subarray(compoundName, 0, currentIndex), + typeBinding, + NotFound); + } + if (!binding.isValidBinding()) + return new ProblemReferenceBinding( + CharOperation.subarray(compoundName, 0, currentIndex), + binding.problemId()); + } + if ((mask & FIELD) != 0 && (binding instanceof FieldBinding)) { + // was looking for a field and found a field + FieldBinding field = (FieldBinding) binding; + if (!field.isStatic()) + return new ProblemFieldBinding( + field.declaringClass, + CharOperation.subarray(compoundName, 0, currentIndex), + NonStaticReferenceInStaticContext); + return binding; + } + if ((mask & TYPE) != 0 && (binding instanceof ReferenceBinding)) { + // was looking for a type and found a type + return binding; + } + + // handle the case when a field or type was asked for but we resolved the compoundName to a type or field + return new ProblemBinding( + CharOperation.subarray(compoundName, 0, currentIndex), + NotFound); + } + + // Added for code assist... NOT Public API + public final Binding getBinding( + char[][] compoundName, + InvocationSite invocationSite) { + int currentIndex = 0; + int length = compoundName.length; + Binding binding = + getBinding( + compoundName[currentIndex++], + VARIABLE | TYPE | PACKAGE, + invocationSite, + true /*resolve*/); + if (!binding.isValidBinding()) + return binding; + + foundType : if (binding instanceof PackageBinding) { + while (currentIndex < length) { + PackageBinding packageBinding = (PackageBinding) binding; + binding = packageBinding.getTypeOrPackage(compoundName[currentIndex++]); + if (binding == null) { + if (currentIndex == length) { + // must be a type if its the last name, otherwise we have no idea if its a package or type + return new ProblemReferenceBinding( + CharOperation.subarray(compoundName, 0, currentIndex), + NotFound); + } + return new ProblemBinding( + CharOperation.subarray(compoundName, 0, currentIndex), + NotFound); + } + if (binding instanceof ReferenceBinding) { + if (!binding.isValidBinding()) + return new ProblemReferenceBinding( + CharOperation.subarray(compoundName, 0, currentIndex), + binding.problemId()); + if (!((ReferenceBinding) binding).canBeSeenBy(this)) + return new ProblemReferenceBinding( + CharOperation.subarray(compoundName, 0, currentIndex), + (ReferenceBinding) binding, + NotVisible); + break foundType; + } + } + return binding; + } + + foundField : if (binding instanceof ReferenceBinding) { + while (currentIndex < length) { + ReferenceBinding typeBinding = (ReferenceBinding) binding; + char[] nextName = compoundName[currentIndex++]; + if ((binding = findField(typeBinding, nextName, invocationSite, true /*resolve*/)) != null) { + if (!binding.isValidBinding()) + return new ProblemFieldBinding( + ((FieldBinding) binding).declaringClass, + CharOperation.subarray(compoundName, 0, currentIndex), + binding.problemId()); + if (!((FieldBinding) binding).isStatic()) + return new ProblemFieldBinding( + ((FieldBinding) binding).declaringClass, + CharOperation.subarray(compoundName, 0, currentIndex), + NonStaticReferenceInStaticContext); + break foundField; // binding is now a field + } + if ((binding = findMemberType(nextName, typeBinding)) == null) + return new ProblemBinding( + CharOperation.subarray(compoundName, 0, currentIndex), + typeBinding, + NotFound); + if (!binding.isValidBinding()) + return new ProblemReferenceBinding( + CharOperation.subarray(compoundName, 0, currentIndex), + binding.problemId()); + } + return binding; + } + + VariableBinding variableBinding = (VariableBinding) binding; + while (currentIndex < length) { + TypeBinding typeBinding = variableBinding.type; + if (typeBinding == null) + return new ProblemFieldBinding( + null, + CharOperation.subarray(compoundName, 0, currentIndex + 1), + NotFound); + variableBinding = + findField(typeBinding, compoundName[currentIndex++], invocationSite, true /*resolve*/); + if (variableBinding == null) + return new ProblemFieldBinding( + null, + CharOperation.subarray(compoundName, 0, currentIndex), + NotFound); + if (!variableBinding.isValidBinding()) + return variableBinding; + } + return variableBinding; + } + + /* + * This retrieves the argument that maps to an enclosing instance of the suitable type, + * if not found then answers nil -- do not create one + * + * #implicitThis : the implicit this will be ok + * #((arg) this$n) : available as a constructor arg + * #((arg) this$n ... this$p) : available as as a constructor arg + a sequence of fields + * #((fieldDescr) this$n ... this$p) : available as a sequence of fields + * nil : not found + * + * Note that this algorithm should answer the shortest possible sequence when + * shortcuts are available: + * this$0 . this$0 . this$0 + * instead of + * this$2 . this$1 . this$0 . this$1 . this$0 + * thus the code generation will be more compact and runtime faster + */ + public VariableBinding[] getEmulationPath(LocalVariableBinding outerLocalVariable) { + + MethodScope currentMethodScope = this.methodScope(); + SourceTypeBinding sourceType = currentMethodScope.enclosingSourceType(); + + // identity check + if (currentMethodScope == outerLocalVariable.declaringScope.methodScope()) { + return new VariableBinding[] { outerLocalVariable }; + // implicit this is good enough + } + // use synthetic constructor arguments if possible + if (currentMethodScope.isInsideInitializerOrConstructor() + && (sourceType.isNestedType())) { + SyntheticArgumentBinding syntheticArg; + if ((syntheticArg = ((NestedTypeBinding) sourceType).getSyntheticArgument(outerLocalVariable)) != null) { + return new VariableBinding[] { syntheticArg }; + } + } + // use a synthetic field then + if (!currentMethodScope.isStatic) { + FieldBinding syntheticField; + if ((syntheticField = sourceType.getSyntheticField(outerLocalVariable)) != null) { + return new VariableBinding[] { syntheticField }; + } + } + return null; + } + + /* + * This retrieves the argument that maps to an enclosing instance of the suitable type, + * if not found then answers nil -- do not create one + * + * #implicitThis : the implicit this will be ok + * #((arg) this$n) : available as a constructor arg + * #((arg) this$n access$m... access$p) : available as as a constructor arg + a sequence of synthetic accessors to synthetic fields + * #((fieldDescr) this$n access#m... access$p) : available as a first synthetic field + a sequence of synthetic accessors to synthetic fields + * null : not found + * jls 15.9.2 + http://www.ergnosis.com/java-spec-report/java-language/jls-8.8.5.1-d.html + */ + public Object[] getEmulationPath( + ReferenceBinding targetEnclosingType, + boolean onlyExactMatch, + boolean ignoreEnclosingArgInConstructorCall) { + + MethodScope currentMethodScope = this.methodScope(); + SourceTypeBinding sourceType = currentMethodScope.enclosingSourceType(); + + // use 'this' if possible + if (!currentMethodScope.isConstructorCall && !currentMethodScope.isStatic) { + if (sourceType == targetEnclosingType || (!onlyExactMatch && targetEnclosingType.isSuperclassOf(sourceType))) { + return EmulationPathToImplicitThis; // implicit this is good enough + } + } + if (!sourceType.isNestedType() || sourceType.isStatic()) { // no emulation from within non-inner types + if (currentMethodScope.isConstructorCall) { + return NoEnclosingInstanceInConstructorCall; + } else if (currentMethodScope.isStatic){ + return NoEnclosingInstanceInStaticContext; + } + return null; + } + boolean insideConstructor = currentMethodScope.isInsideInitializerOrConstructor(); + // use synthetic constructor arguments if possible + if (insideConstructor) { + SyntheticArgumentBinding syntheticArg; + if ((syntheticArg = ((NestedTypeBinding) sourceType).getSyntheticArgument(targetEnclosingType, onlyExactMatch)) != null) { + // reject allocation and super constructor call + if (ignoreEnclosingArgInConstructorCall + && currentMethodScope.isConstructorCall + && (sourceType == targetEnclosingType || (!onlyExactMatch && targetEnclosingType.isSuperclassOf(sourceType)))) { + return NoEnclosingInstanceInConstructorCall; + } + return new Object[] { syntheticArg }; + } + } + + // use a direct synthetic field then + if (currentMethodScope.isStatic) { + return NoEnclosingInstanceInStaticContext; + } + FieldBinding syntheticField = sourceType.getSyntheticField(targetEnclosingType, onlyExactMatch); + if (syntheticField != null) { + if (currentMethodScope.isConstructorCall){ + return NoEnclosingInstanceInConstructorCall; + } + return new Object[] { syntheticField }; + } + // could be reached through a sequence of enclosing instance link (nested members) + Object[] path = new Object[2]; // probably at least 2 of them + ReferenceBinding currentType = sourceType.enclosingType(); + if (insideConstructor) { + path[0] = ((NestedTypeBinding) sourceType).getSyntheticArgument(currentType, onlyExactMatch); + } else { + if (currentMethodScope.isConstructorCall){ + return NoEnclosingInstanceInConstructorCall; + } + path[0] = sourceType.getSyntheticField(currentType, onlyExactMatch); + } + if (path[0] != null) { // keep accumulating + + int count = 1; + ReferenceBinding currentEnclosingType; + while ((currentEnclosingType = currentType.enclosingType()) != null) { + + //done? + if (currentType == targetEnclosingType + || (!onlyExactMatch && targetEnclosingType.isSuperclassOf(currentType))) break; + + if (currentMethodScope != null) { + currentMethodScope = currentMethodScope.enclosingMethodScope(); + if (currentMethodScope != null && currentMethodScope.isConstructorCall){ + return NoEnclosingInstanceInConstructorCall; + } + if (currentMethodScope != null && currentMethodScope.isStatic){ + return NoEnclosingInstanceInStaticContext; + } + } + + syntheticField = ((NestedTypeBinding) currentType).getSyntheticField(currentEnclosingType, onlyExactMatch); + if (syntheticField == null) break; + + // append inside the path + if (count == path.length) { + System.arraycopy(path, 0, (path = new Object[count + 1]), 0, count); + } + // private access emulation is necessary since synthetic field is private + path[count++] = ((SourceTypeBinding) syntheticField.declaringClass).addSyntheticMethod(syntheticField, true); + currentType = currentEnclosingType; + } + if (currentType == targetEnclosingType + || (!onlyExactMatch && targetEnclosingType.isSuperclassOf(currentType))) { + return path; + } + } + return null; + } + + /* Answer true if the variable name already exists within the receiver's scope. + */ + public final boolean isDuplicateLocalVariable(char[] name) { + BlockScope current = this; + while (true) { + for (int i = 0; i < localIndex; i++) { + if (CharOperation.equals(name, current.locals[i].name)) + return true; + } + if (current.kind != BLOCK_SCOPE) return false; + current = (BlockScope)current.parent; + } + } + + public int maxShiftedOffset() { + int max = -1; + if (this.shiftScopes != null){ + for (int i = 0, length = this.shiftScopes.length; i < length; i++){ + int subMaxOffset = this.shiftScopes[i].maxOffset; + if (subMaxOffset > max) max = subMaxOffset; + } + } + return max; + } + + /* Answer the problem reporter to use for raising new problems. + * + * Note that as a side-effect, this updates the current reference context + * (unit, type or method) in case the problem handler decides it is necessary + * to abort. + */ + public ProblemReporter problemReporter() { + + return outerMostMethodScope().problemReporter(); + } + + /* + * Code responsible to request some more emulation work inside the invocation type, so as to supply + * correct synthetic arguments to any allocation of the target type. + */ + public void propagateInnerEmulation(ReferenceBinding targetType, boolean isEnclosingInstanceSupplied) { + + // no need to propagate enclosing instances, they got eagerly allocated already. + + SyntheticArgumentBinding[] syntheticArguments; + if ((syntheticArguments = targetType.syntheticOuterLocalVariables()) != null) { + for (int i = 0, max = syntheticArguments.length; i < max; i++) { + SyntheticArgumentBinding syntheticArg = syntheticArguments[i]; + // need to filter out the one that could match a supplied enclosing instance + if (!(isEnclosingInstanceSupplied + && (syntheticArg.type == targetType.enclosingType()))) { + this.emulateOuterAccess(syntheticArg.actualOuterLocalVariable); + } + } + } + } + + /* Answer the reference type of this scope. + * + * It is the nearest enclosing type of this scope. + */ + public TypeDeclaration referenceType() { + + return methodScope().referenceType(); + } + + /* + * Answer the index of this scope relatively to its parent. + * For method scope, answers -1 (not a classScope relative position) + */ + public int scopeIndex() { + if (this instanceof MethodScope) return -1; + BlockScope parentScope = (BlockScope)parent; + Scope[] parentSubscopes = parentScope.subscopes; + for (int i = 0, max = parentScope.subscopeCount; i < max; i++) { + if (parentSubscopes[i] == this) return i; + } + return -1; + } + + // start position in this scope - for ordering scopes vs. variables + int startIndex() { + return startIndex; + } + + public String toString() { + return toString(0); + } + + public String toString(int tab) { + + String s = basicToString(tab); + for (int i = 0; i < subscopeCount; i++) + if (subscopes[i] instanceof BlockScope) + s += ((BlockScope) subscopes[i]).toString(tab + 1) + "\n"; //$NON-NLS-1$ + return s; + } +}