| /******************************************************************************* |
| * Copyright (c) 2000, 2013 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 |
| *******************************************************************************/ |
| package org.eclipse.wst.jsdt.internal.compiler.lookup; |
| |
| import org.eclipse.wst.jsdt.core.ast.IAbstractVariableDeclaration; |
| import org.eclipse.wst.jsdt.core.compiler.CharOperation; |
| import org.eclipse.wst.jsdt.core.infer.InferredMethod; |
| import org.eclipse.wst.jsdt.internal.compiler.ast.ASTNode; |
| import org.eclipse.wst.jsdt.internal.compiler.ast.AbstractMethodDeclaration; |
| import org.eclipse.wst.jsdt.internal.compiler.ast.Argument; |
| import org.eclipse.wst.jsdt.internal.compiler.ast.ConstructorDeclaration; |
| import org.eclipse.wst.jsdt.internal.compiler.ast.ProgramElement; |
| import org.eclipse.wst.jsdt.internal.compiler.ast.QualifiedNameReference; |
| import org.eclipse.wst.jsdt.internal.compiler.ast.SingleNameReference; |
| import org.eclipse.wst.jsdt.internal.compiler.ast.Statement; |
| import org.eclipse.wst.jsdt.internal.compiler.ast.TypeDeclaration; |
| import org.eclipse.wst.jsdt.internal.compiler.classfmt.ClassFileConstants; |
| import org.eclipse.wst.jsdt.internal.compiler.flow.FlowInfo; |
| import org.eclipse.wst.jsdt.internal.compiler.flow.UnconditionalFlowInfo; |
| import org.eclipse.wst.jsdt.internal.compiler.impl.ReferenceContext; |
| import org.eclipse.wst.jsdt.internal.compiler.problem.ProblemReporter; |
| import org.eclipse.wst.jsdt.internal.compiler.util.HashtableOfObject; |
| |
| /** |
| * Particular block scope used for methods, constructors or clinits, representing |
| * its outermost blockscope. Note also that such a scope will be provided to enclose |
| * field initializers subscopes as well. |
| */ |
| public class MethodScope extends BlockScope { |
| |
| public ReferenceContext referenceContext; |
| public boolean isStatic; // method modifier or initializer one |
| |
| //fields used during name resolution |
| public boolean isConstructorCall = false; |
| public FieldBinding initializedField; // the field being initialized |
| public int lastVisibleFieldID = -1; // the ID of the last field which got declared |
| // note that #initializedField can be null AND lastVisibleFieldID >= 0, when processing instance field initializers. |
| |
| // flow analysis |
| public int analysisIndex; // for setting flow-analysis id |
| |
| // for local variables table attributes |
| public int lastIndex = 0; |
| public long[] definiteInits = new long[4]; |
| public long[][] extraDefiniteInits = new long[4][]; |
| |
| |
| public static final char [] ARGUMENTS_NAME={'a','r','g','u','m','e','n','t','s'}; |
| |
| public LocalVariableBinding argumentsBinding; |
| |
| /** |
| * <p> |
| * Map of variable names to statements that have not been resolved that |
| * define those variables. |
| * </p> |
| */ |
| private HashtableOfObject fUnresolvedLocalVars; |
| |
| /** |
| * <p> |
| * Map of function names to statements that have not been resolved that |
| * define those variables. |
| * </p> |
| */ |
| private HashtableOfObject fUnresolvedLocalFuncs; |
| |
| public MethodScope(Scope parent, ReferenceContext context, boolean isStatic) { |
| |
| super(METHOD_SCOPE, parent); |
| locals = new LocalVariableBinding[5]; |
| this.referenceContext = context; |
| this.isStatic = isStatic; |
| this.startIndex = 0; |
| argumentsBinding = new LocalVariableBinding(ARGUMENTS_NAME,TypeBinding.UNKNOWN,0,true); |
| argumentsBinding.declaringScope=this; |
| } |
| |
| /* Spec : 8.4.3 & 9.4 |
| */ |
| private void checkAndSetModifiersForConstructor(MethodBinding methodBinding) { |
| |
| int modifiers = methodBinding.modifiers; |
| final ReferenceBinding declaringClass = methodBinding.declaringClass; |
| |
| // if (((ConstructorDeclaration) referenceContext).isDefaultConstructor) { |
| if ((methodBinding.modifiers&ExtraCompilerModifiers.AccIsDefaultConstructor)>0) { |
| // certain flags are propagated from declaring class onto constructor |
| final int DECLARING_FLAGS = ClassFileConstants.AccPublic|ClassFileConstants.AccProtected; |
| final int VISIBILITY_FLAGS = ClassFileConstants.AccPrivate|ClassFileConstants.AccPublic|ClassFileConstants.AccProtected; |
| int flags; |
| if ((flags = declaringClass.modifiers & DECLARING_FLAGS) != 0) { |
| modifiers &= ~VISIBILITY_FLAGS; |
| modifiers |= flags; // propagate public/protected |
| } |
| } |
| |
| // after this point, tests on the 16 bits reserved. |
| int realModifiers = modifiers & ExtraCompilerModifiers.AccJustFlag; |
| |
| // check for incompatible modifiers in the visibility bits, isolate the visibility bits |
| int accessorBits = realModifiers & (ClassFileConstants.AccPublic | ClassFileConstants.AccProtected | ClassFileConstants.AccPrivate); |
| if ((accessorBits & (accessorBits - 1)) != 0) { |
| |
| // need to keep the less restrictive so disable Protected/Private as necessary |
| if ((accessorBits & ClassFileConstants.AccPublic) != 0) { |
| if ((accessorBits & ClassFileConstants.AccProtected) != 0) |
| modifiers &= ~ClassFileConstants.AccProtected; |
| if ((accessorBits & ClassFileConstants.AccPrivate) != 0) |
| modifiers &= ~ClassFileConstants.AccPrivate; |
| } else if ((accessorBits & ClassFileConstants.AccProtected) != 0 && (accessorBits & ClassFileConstants.AccPrivate) != 0) { |
| modifiers &= ~ClassFileConstants.AccPrivate; |
| } |
| } |
| |
| // // if the receiver's declaring class is a private nested type, then make sure the receiver is not private (causes problems for inner type emulation) |
| // if (declaringClass.isPrivate() && (modifiers & ClassFileConstants.AccPrivate) != 0) |
| // modifiers &= ~ClassFileConstants.AccPrivate; |
| |
| methodBinding.modifiers = modifiers; |
| } |
| |
| /* Spec : 8.4.3 & 9.4 |
| */ |
| private void checkAndSetModifiersForMethod(MethodBinding methodBinding) { |
| |
| int modifiers = methodBinding.modifiers; |
| final ReferenceBinding declaringClass = methodBinding.declaringClass; |
| |
| // after this point, tests on the 16 bits reserved. |
| int realModifiers = modifiers & ExtraCompilerModifiers.AccJustFlag; |
| |
| // set the requested modifiers for a method in an interface/annotation |
| // if (declaringClass.isInterface()) { |
| // if ((realModifiers & ~(ClassFileConstants.AccPublic | ClassFileConstants.AccAbstract)) != 0) { |
| // if ((declaringClass.modifiers & ClassFileConstants.AccAnnotation) != 0) |
| // problemReporter().illegalModifierForAnnotationMember((AbstractMethodDeclaration) referenceContext); |
| // else |
| // problemReporter().illegalModifierForInterfaceMethod((AbstractMethodDeclaration) referenceContext); |
| // } |
| // return; |
| // } |
| |
| // check for incompatible modifiers in the visibility bits, isolate the visibility bits |
| int accessorBits = realModifiers & (ClassFileConstants.AccPublic | ClassFileConstants.AccProtected | ClassFileConstants.AccPrivate); |
| if ((accessorBits & (accessorBits - 1)) != 0) { |
| |
| // need to keep the less restrictive so disable Protected/Private as necessary |
| if ((accessorBits & ClassFileConstants.AccPublic) != 0) { |
| if ((accessorBits & ClassFileConstants.AccProtected) != 0) |
| modifiers &= ~ClassFileConstants.AccProtected; |
| if ((accessorBits & ClassFileConstants.AccPrivate) != 0) |
| modifiers &= ~ClassFileConstants.AccPrivate; |
| } else if ((accessorBits & ClassFileConstants.AccProtected) != 0 && (accessorBits & ClassFileConstants.AccPrivate) != 0) { |
| modifiers &= ~ClassFileConstants.AccPrivate; |
| } |
| } |
| |
| /* DISABLED for backward compatibility with javac (if enabled should also mark private methods as final) |
| // methods from a final class are final : 8.4.3.3 |
| if (methodBinding.declaringClass.isFinal()) |
| modifiers |= AccFinal; |
| */ |
| // // static members are only authorized in a static member or top level type |
| // if (((realModifiers & ClassFileConstants.AccStatic) != 0) && declaringClass.isNestedType() && !declaringClass.isStatic()) |
| // problemReporter().unexpectedStaticModifierForMethod(declaringClass, (AbstractMethodDeclaration) referenceContext); |
| |
| methodBinding.modifiers = modifiers; |
| } |
| |
| MethodBinding createMethod(InferredMethod inferredMethod,SourceTypeBinding declaringClass) { |
| boolean isConstructor=inferredMethod.isConstructor; |
| if (isConstructor && declaringClass!=inferredMethod.inType.binding) |
| isConstructor=false; |
| MethodBinding binding = createMethod((AbstractMethodDeclaration) inferredMethod.getFunctionDeclaration(),inferredMethod.name,declaringClass, isConstructor,false); |
| if (inferredMethod.isConstructor || declaringClass!=inferredMethod.inType.binding) |
| binding.allocationType=inferredMethod.inType.binding; |
| return binding; |
| } |
| |
| public MethodBinding createMethod(AbstractMethodDeclaration method,char[] name,SourceTypeBinding declaringClass, boolean isConstructor, boolean isLocal) { |
| |
| MethodBinding methodBinding=null; |
| // is necessary to ensure error reporting |
| this.referenceContext = method; |
| method.setScope(this); |
| int modifiers = method.modifiers | ExtraCompilerModifiers.AccUnresolved; |
| if ((method.modifiers &(ClassFileConstants.AccPrivate | ClassFileConstants.AccProtected))==0) |
| modifiers|=ClassFileConstants.AccPublic; |
| if (method.inferredMethod!=null && method.inferredMethod.isStatic) |
| modifiers|= ClassFileConstants.AccStatic; |
| if (isConstructor) { |
| if (method.isDefaultConstructor() || isConstructor) { |
| modifiers |= ExtraCompilerModifiers.AccIsDefaultConstructor; |
| } |
| TypeBinding constructorType = null; |
| if (method.inferredMethod!=null && method.inferredMethod.inType != null) { |
| constructorType=method.inferredMethod.inType.resolveType(this,method); |
| } |
| //return type still null, return type is unknown |
| if (constructorType==null) { |
| constructorType=TypeBinding.UNKNOWN; |
| } |
| methodBinding = new MethodBinding(modifiers, name, constructorType, null, constructorType instanceof ReferenceBinding ? (ReferenceBinding) constructorType : declaringClass); |
| methodBinding.tagBits|=TagBits.IsConstructor; |
| checkAndSetModifiersForConstructor(methodBinding); |
| } else { |
| TypeBinding returnType = |
| (method.inferredType!=null)?method.inferredType.resolveType(this,method):TypeBinding.UNKNOWN; |
| if (method.inferredType==null && method.inferredMethod!=null && method.inferredMethod.isConstructor |
| && method.inferredMethod.inType!=null) { |
| returnType=method.inferredMethod.inType.resolveType(this,method); |
| } |
| |
| //return type still null, return type is unknown |
| if (returnType==null) { |
| returnType=TypeBinding.UNKNOWN; |
| } |
| |
| if (isLocal && method.getName()!=null) { |
| methodBinding = |
| new LocalFunctionBinding(modifiers, name,returnType, null, declaringClass); |
| } else{// not local method |
| methodBinding = |
| new MethodBinding(modifiers, name,returnType, null, declaringClass); |
| } |
| |
| if (method.inferredMethod!=null) { |
| methodBinding.tagBits |= TagBits.IsInferredType; |
| if ((method.bits&ASTNode.IsInferredJsDocType)!=0) { |
| methodBinding.tagBits |= TagBits.IsInferredJsDocType; |
| |
| } |
| } |
| //methodBinding.createFunctionTypeBinding(this); |
| // if (method.inferredMethod!=null && method.inferredMethod.isConstructor) { |
| // methodBinding.tagBits|=TagBits.IsConstructor; |
| // } |
| checkAndSetModifiersForMethod(methodBinding); |
| } |
| methodBinding.createFunctionTypeBinding(this); |
| this.isStatic =methodBinding.isStatic(); |
| |
| //set arguments |
| Argument[] argTypes = method.arguments; |
| int argLength = argTypes == null ? 0 : argTypes.length; |
| if (argLength > 0 && compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5) { |
| if (argTypes[--argLength].isVarArgs()) |
| methodBinding.modifiers |= ClassFileConstants.AccVarargs; |
| } |
| |
| return methodBinding; |
| } |
| |
| public FieldBinding findField( |
| TypeBinding receiverType, |
| char[] fieldName, |
| InvocationSite invocationSite, |
| boolean needResolve) { |
| |
| FieldBinding field = super.findField(receiverType, fieldName, invocationSite, needResolve); |
| if (field == null) |
| return null; |
| if (!field.isValidBinding()) |
| return field; // answer the error field |
| if (field.isStatic()) |
| return field; // static fields are always accessible |
| |
| if (!isConstructorCall || receiverType != enclosingSourceType()) |
| return field; |
| |
| if (invocationSite instanceof SingleNameReference) |
| return new ProblemFieldBinding( |
| field, // closest match |
| field.declaringClass, |
| fieldName, |
| ProblemReasons.NonStaticReferenceInConstructorInvocation); |
| if (invocationSite instanceof QualifiedNameReference) { |
| // look to see if the field is the first binding |
| QualifiedNameReference name = (QualifiedNameReference) invocationSite; |
| if (name.binding == null) |
| // only true when the field is the fieldbinding at the beginning of name's tokens |
| return new ProblemFieldBinding( |
| field, // closest match |
| field.declaringClass, |
| fieldName, |
| ProblemReasons.NonStaticReferenceInConstructorInvocation); |
| } |
| return field; |
| } |
| |
| public boolean isInsideConstructor() { |
| |
| return (referenceContext instanceof ConstructorDeclaration); |
| } |
| |
| public boolean isInsideInitializer() { |
| |
| return (referenceContext instanceof TypeDeclaration); |
| } |
| |
| public boolean isInsideInitializerOrConstructor() { |
| |
| return (referenceContext instanceof TypeDeclaration) |
| || (referenceContext instanceof ConstructorDeclaration); |
| } |
| |
| /* 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() { |
| |
| MethodScope outerMethodScope; |
| if ((outerMethodScope = outerMostMethodScope()) == this) { |
| ProblemReporter problemReporter = referenceCompilationUnit().problemReporter; |
| problemReporter.referenceContext = referenceContext; |
| return problemReporter; |
| } |
| return outerMethodScope.problemReporter(); |
| } |
| |
| public final int recordInitializationStates(FlowInfo flowInfo) { |
| |
| if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) != 0) return -1; |
| |
| UnconditionalFlowInfo unconditionalFlowInfo = flowInfo.unconditionalInitsWithoutSideEffect(); |
| long[] extraInits = unconditionalFlowInfo.extra == null ? |
| null : unconditionalFlowInfo.extra[0]; |
| long inits = unconditionalFlowInfo.definiteInits; |
| checkNextEntry : for (int i = lastIndex; --i >= 0;) { |
| if (definiteInits[i] == inits) { |
| long[] otherInits = extraDefiniteInits[i]; |
| if ((extraInits != null) && (otherInits != null)) { |
| if (extraInits.length == otherInits.length) { |
| int j, max; |
| for (j = 0, max = extraInits.length; j < max; j++) { |
| if (extraInits[j] != otherInits[j]) { |
| continue checkNextEntry; |
| } |
| } |
| return i; |
| } |
| } else { |
| if ((extraInits == null) && (otherInits == null)) { |
| return i; |
| } |
| } |
| } |
| } |
| |
| // add a new entry |
| if (definiteInits.length == lastIndex) { |
| // need a resize |
| System.arraycopy( |
| definiteInits, |
| 0, |
| (definiteInits = new long[lastIndex + 20]), |
| 0, |
| lastIndex); |
| System.arraycopy( |
| extraDefiniteInits, |
| 0, |
| (extraDefiniteInits = new long[lastIndex + 20][]), |
| 0, |
| lastIndex); |
| } |
| definiteInits[lastIndex] = inits; |
| if (extraInits != null) { |
| extraDefiniteInits[lastIndex] = new long[extraInits.length]; |
| System.arraycopy( |
| extraInits, |
| 0, |
| extraDefiniteInits[lastIndex], |
| 0, |
| extraInits.length); |
| } |
| return lastIndex++; |
| } |
| |
| /* Answer the reference method of this scope, or null if initialization scoope. |
| */ |
| public AbstractMethodDeclaration referenceMethod() { |
| |
| if (referenceContext instanceof AbstractMethodDeclaration) return (AbstractMethodDeclaration) referenceContext; |
| return null; |
| } |
| |
| /* Answer the reference type of this scope. |
| * |
| * It is the nearest enclosing type of this scope. |
| */ |
| public TypeDeclaration referenceType() { |
| if (parent instanceof ClassScope) |
| return ((ClassScope) parent).referenceContext; |
| return null; |
| } |
| |
| String basicToString(int tab) { |
| |
| String newLine = "\n"; //$NON-NLS-1$ |
| for (int i = tab; --i >= 0;) |
| newLine += "\t"; //$NON-NLS-1$ |
| |
| String s = newLine + "--- Method 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$ |
| s += newLine + "isConstructorCall = " + isConstructorCall; //$NON-NLS-1$ |
| s += newLine + "initializedField = " + initializedField; //$NON-NLS-1$ |
| s += newLine + "lastVisibleFieldID = " + lastVisibleFieldID; //$NON-NLS-1$ |
| s += newLine + "referenceContext = " + referenceContext; //$NON-NLS-1$ |
| return s; |
| } |
| |
| /** |
| * <p> |
| * This implementation first calls super, if that returns no binding or a |
| * problem binding then the list of unresolved local variables is checked, |
| * and if there is a statement that defines a variable with the correct |
| * name it is resolved, and then the super implementation is called again. |
| * </p> |
| * |
| * <p> |
| * This allows {@link #addUnresolvedLocalVar(char[], Statement)} to be used in |
| * conjunction with this method to prevent having to resolve the entire |
| * {@link AbstractMethodDeclaration} associated with this scope. |
| * </p> |
| * |
| * @see org.eclipse.wst.jsdt.internal.compiler.lookup.BlockScope#findVariable(char[]) |
| */ |
| public LocalVariableBinding findVariable(char[] variableName) { |
| LocalVariableBinding binding = super.findVariable(variableName); |
| if (binding == null && CharOperation.equals(variableName,ARGUMENTS_NAME)) { |
| binding = this.argumentsBinding; |
| } |
| |
| // if super could not find a good binding then check list of unresolved local variables |
| if(binding == null && this.fUnresolvedLocalVars != null) { |
| IAbstractVariableDeclaration statement = (IAbstractVariableDeclaration)this.fUnresolvedLocalVars.removeKey(variableName); |
| if(statement != null && statement instanceof ProgramElement) { |
| //resolve and then call super again |
| ((ProgramElement)statement).resolve(this); |
| binding = super.findVariable(variableName); |
| } |
| } |
| |
| return binding; |
| } |
| |
| /** |
| * <p> |
| * This implementation first calls super, if that returns no binding or a |
| * problem binding then the list of unresolved local functions is checked, |
| * and if there is a statement that defines a function with the correct |
| * name it is resolved, and then its binding is returned. |
| * </p> |
| * |
| * <p> |
| * This allows {@link #addUnresolvedLocalFunc(char[], AbstractMethodDeclaration)} to be used in |
| * conjunction with this method to prevent having to resolve the entire |
| * {@link AbstractMethodDeclaration} associated with this scope. |
| * </p> |
| * |
| * @see org.eclipse.wst.jsdt.internal.compiler.lookup.Scope#findMethod(org.eclipse.wst.jsdt.internal.compiler.lookup.ReferenceBinding, char[], org.eclipse.wst.jsdt.internal.compiler.lookup.TypeBinding[], org.eclipse.wst.jsdt.internal.compiler.lookup.InvocationSite) |
| */ |
| public MethodBinding findMethod(ReferenceBinding receiverType, char[] selector, TypeBinding[] argumentTypes, InvocationSite invocationSite) { |
| MethodBinding binding = super.findMethod(receiverType, selector, argumentTypes, invocationSite); |
| |
| // if super could not find a good binding then check list of unresolved local functions |
| if((binding == null || !binding.isValidBinding()) && this.fUnresolvedLocalFuncs != null) { |
| AbstractMethodDeclaration statement = (AbstractMethodDeclaration)this.fUnresolvedLocalFuncs.removeKey(selector); |
| if(statement != null) { |
| //resolve and then return the statements binding |
| statement.resolve(this); |
| binding = statement.getBinding(); |
| } |
| } |
| |
| return binding; |
| } |
| |
| /** |
| * <p> |
| * This implementation first calls super, if that returns no binding or a |
| * problem binding then the list of unresolved local functions is checked, |
| * and if there is a statement that defines a function with the correct |
| * name it is resolved, and then its binding is returned. |
| * </p> |
| * |
| * <p> |
| * This allows {@link #addUnresolvedLocalFunc(char[], AbstractMethodDeclaration)} to be used in |
| * conjunction with this method to prevent having to resolve the entire |
| * {@link AbstractMethodDeclaration} associated with this scope. |
| * </p> |
| * |
| * @see org.eclipse.wst.jsdt.internal.compiler.lookup.Scope#findMethod(org.eclipse.wst.jsdt.internal.compiler.lookup.ReferenceBinding, char[], org.eclipse.wst.jsdt.internal.compiler.lookup.TypeBinding[], org.eclipse.wst.jsdt.internal.compiler.lookup.InvocationSite) |
| */ |
| public MethodBinding findMethod(char[] methodName, TypeBinding[]argumentTypes, boolean checkVars) { |
| MethodBinding binding = super.findMethod(methodName, argumentTypes, checkVars); |
| |
| // if super could not find a good binding then check list of unresolved local functions |
| if((binding == null || !binding.isValidBinding()) && this.fUnresolvedLocalFuncs != null) { |
| AbstractMethodDeclaration methDecl = (AbstractMethodDeclaration)this.fUnresolvedLocalFuncs.removeKey(methodName); |
| if(methDecl != null) { |
| //resolve and then return the statements binding |
| binding = methDecl.getBinding(); |
| if(binding == null || !binding.isValidBinding()) { |
| |
| /* if it is anonymous, but has a name (has to to be here) then this is a |
| * function assignment to a variable not declared in this scope so |
| * build with compilation unit scope. |
| * |
| * else build with this scope */ |
| Scope scope = null; |
| if(methDecl.isAnonymous()) { |
| scope = this.compilationUnitScope(); |
| } else { |
| scope = this; |
| } |
| methDecl.resolve(scope); |
| } |
| binding = methDecl.getBinding(); |
| } |
| } |
| |
| return binding; |
| } |
| |
| /** |
| * <p> |
| * Adds an unresolved local variable defined in this scope to be resolved |
| * on an as needed basses. |
| * </p> |
| * |
| * @param name |
| * the name of the unresolved local variable |
| * @param expr |
| * {@link IAbstractVariableDeclaration} that defines the unresolved local variable |
| * that will be used to resolve it if needed |
| */ |
| public void addUnresolvedLocalVar(char[] name, IAbstractVariableDeclaration expr) { |
| if(name != null && name.length > 0 && expr != null) { |
| if(this.fUnresolvedLocalVars == null) { |
| this.fUnresolvedLocalVars = new HashtableOfObject(); |
| } |
| |
| this.fUnresolvedLocalVars.put(name, expr); |
| } |
| } |
| |
| /** |
| * <p> |
| * Adds an unresolved local function defined in this scope to be resolved |
| * on an as needed basses. |
| * </p> |
| * |
| * @param name |
| * the name of the unresolved local function |
| * @param expr |
| * {@link AbstractMethodDeclaration} that defines the unresolved local function |
| * that will be used to resolve it if needed |
| */ |
| public void addUnresolvedLocalFunc(char[] name, AbstractMethodDeclaration func) { |
| if(name != null && name.length > 0 && func != null) { |
| if(this.fUnresolvedLocalFuncs == null) { |
| this.fUnresolvedLocalFuncs = new HashtableOfObject(); |
| } |
| |
| this.fUnresolvedLocalFuncs.put(name, func); |
| } |
| } |
| } |