/******************************************************************************* * 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.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.isConstantValue()); // 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 | Binding.TYPE | Binding.PACKAGE, invocationSite, needResolve); invocationSite.setFieldIndex(1); if (binding instanceof VariableBinding) return binding; CompilationUnitScope unitScope = compilationUnitScope(); unitScope.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) { unitScope.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 & Binding.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 & Binding.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 & Binding.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 & Binding.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++], Binding.VARIABLE | Binding.TYPE | Binding.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 && sourceType.findSuperTypeErasingTo(targetEnclosingType) != null)) { 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 && sourceType.findSuperTypeErasingTo(targetEnclosingType) != null))) { 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 && currentType.findSuperTypeErasingTo(targetEnclosingType) != null)) 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 && currentType.findSuperTypeErasingTo(targetEnclosingType) != null)) { 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; } }