gt:         int outerlen = outername.length();
context: /*
   * Copyright (c) 1998, 2018, Oracle and/or its affiliates. All rights reserved.
   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   *
   * This code is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License version 2 only, as
   * published by the Free Software Foundation.  Oracle designates this
   * particular file as subject to the "Classpath" exception as provided
   * by Oracle in the LICENSE file that accompanied this code.
   *
   * This code is distributed in the hope that it will be useful, but WITHOUT
   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   * version 2 for more details (a copy is included in the LICENSE file that
   * accompanied this code).
   *
   * You should have received a copy of the GNU General Public License version
   * 2 along with this work; if not, write to the Free Software Foundation,
   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   *
   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   * or visit www.oracle.com if you need additional information or have any
   * questions.
   */
  
  package com.sun.tools.jdi;
  
  import com.sun.jdi.*;
  
  import java.util.*;
  import java.lang.ref.SoftReference;
  
  public abstract class ReferenceTypeImpl extends TypeImpl
  implements ReferenceType {
      protected long ref;
      private String signature = null;
      private String genericSignature = null;
      private boolean genericSignatureGotten = false;
      private String baseSourceName = null;
      private String baseSourceDir = null;
      private String baseSourcePath = null;
      protected int modifiers = -1;
      private SoftReference<List<Field>> fieldsRef = null;
      private SoftReference<List<Method>> methodsRef = null;
      private SoftReference<SDE> sdeRef = null;
  
      private boolean isClassLoaderCached = false;
      private ClassLoaderReference classLoader = null;
      private ClassObjectReference classObject = null;
  
      private int status = 0;
      private boolean isPrepared = false;
  
  
      private boolean versionNumberGotten = false;
      private int majorVersion;
      private int minorVersion;
  
      private boolean constantPoolInfoGotten = false;
      private int constanPoolCount;
      private byte[] constantPoolBytes;
      private SoftReference<byte[]> constantPoolBytesRef = null;
  
      /* to mark a SourceFile request that returned a genuine JDWP.Error.ABSENT_INFORMATION */
      private static final String ABSENT_BASE_SOURCE_NAME = "**ABSENT_BASE_SOURCE_NAME**";
  
      /* to mark when no info available */
      static final SDE NO_SDE_INFO_MARK = new SDE();
  
      // bits set when initialization was attempted (succeeded or failed)
      private static final int INITIALIZED_OR_FAILED =
          JDWP.ClassStatus.INITIALIZED | JDWP.ClassStatus.ERROR;
  
  
      protected ReferenceTypeImpl(VirtualMachine aVm, long aRef) {
          super(aVm);
          ref = aRef;
          genericSignatureGotten = false;
      }
  
      void noticeRedefineClass() {
          //Invalidate information previously fetched and cached.
          //These will be refreshed later on demand.
          baseSourceName = null;
          baseSourcePath = null;
          modifiers = -1;
          fieldsRef = null;
          methodsRef = null;
          sdeRef = null;
          versionNumberGotten = false;
          constantPoolInfoGotten = false;
      }
  
      Method getMethodMirror(long ref) {
          if (ref == 0) {
              // obsolete method
              return new ObsoleteMethodImpl(vm, this);
          }
          // Fetch all methods for the class, check performance impact
          // Needs no synchronization now, since methods() returns
          // unmodifiable local data
          Iterator<Method> it = methods().iterator();
          while (it.hasNext()) {
              MethodImpl method = (MethodImpl)it.next();
              if (method.ref() == ref) {
                  return method;
              }
          }
          throw new IllegalArgumentException("Invalid method id: " + ref);
      }
  
      Field getFieldMirror(long ref) {
          // Fetch all fields for the class, check performance impact
          // Needs no synchronization now, since fields() returns
          // unmodifiable local data
          Iterator<Field>it = fields().iterator();
          while (it.hasNext()) {
              FieldImpl field = (FieldImpl)it.next();
              if (field.ref() == ref) {
                  return field;
              }
          }
          throw new IllegalArgumentException("Invalid field id: " + ref);
      }
  
      public boolean equals(Object obj) {
          if ((obj != null) && (obj instanceof ReferenceTypeImpl)) {
              ReferenceTypeImpl other = (ReferenceTypeImpl)obj;
              return (ref() == other.ref()) &&
                  (vm.equals(other.virtualMachine()));
          } else {
              return false;
          }
      }
  
      public int hashCode() {
          return(int)ref();
      }
  
      public int compareTo(ReferenceType object) {
          /*
           * Note that it is critical that compareTo() == 0
           * implies that equals() == true. Otherwise, TreeSet
           * will collapse classes.
           *
           * (Classes of the same name loaded by different class loaders
           * or in different VMs must not return 0).
           */
          ReferenceTypeImpl other = (ReferenceTypeImpl)object;
          int comp = name().compareTo(other.name());
          if (comp == 0) {
              long rf1 = ref();
              long rf2 = other.ref();
              // optimize for typical case: refs equal and VMs equal
              if (rf1 == rf2) {
                  // sequenceNumbers are always positive
                  comp = vm.sequenceNumber -
                   ((VirtualMachineImpl)(other.virtualMachine())).sequenceNumber;
              } else {
                  comp = (rf1 < rf2)? -1 : 1;
              }
          }
          return comp;
      }
  
      public String signature() {
          if (signature == null) {
              // Does not need synchronization, since worst-case
              // static info is fetched twice
              if (vm.canGet1_5LanguageFeatures()) {
                  /*
                   * we might as well get both the signature and the
                   * generic signature.
                   */
                  genericSignature();
              } else {
                  try {
                      signature = JDWP.ReferenceType.Signature.
                          process(vm, this).signature;
                  } catch (JDWPException exc) {
                      throw exc.toJDIException();
                  }
              }
          }
          return signature;
      }
  
      public String genericSignature() {
          // This gets both the signature and the generic signature
          if (vm.canGet1_5LanguageFeatures() && !genericSignatureGotten) {
              // Does not need synchronization, since worst-case
              // static info is fetched twice
              JDWP.ReferenceType.SignatureWithGeneric result;
              try {
                  result = JDWP.ReferenceType.SignatureWithGeneric.
                      process(vm, this);
              } catch (JDWPException exc) {
                  throw exc.toJDIException();
              }
              signature = result.signature;
              setGenericSignature(result.genericSignature);
          }
          return genericSignature;
      }
  
      public ClassLoaderReference classLoader() {
          if (!isClassLoaderCached) {
              // Does not need synchronization, since worst-case
              // static info is fetched twice
              try {
                  classLoader = (ClassLoaderReference)
                      JDWP.ReferenceType.ClassLoader.
                      process(vm, this).classLoader;
                  isClassLoaderCached = true;
              } catch (JDWPException exc) {
                  throw exc.toJDIException();
              }
          }
          return classLoader;
      }
  
      public boolean isPublic() {
          if (modifiers == -1)
              getModifiers();
  
          return((modifiers & VMModifiers.PUBLIC) > 0);
      }
  
      public boolean isProtected() {
          if (modifiers == -1)
              getModifiers();
  
          return((modifiers & VMModifiers.PROTECTED) > 0);
      }
  
      public boolean isPrivate() {
          if (modifiers == -1)
              getModifiers();
  
          return((modifiers & VMModifiers.PRIVATE) > 0);
      }
  
      public boolean isPackagePrivate() {
          return !isPublic() && !isPrivate() && !isProtected();
      }
  
      public boolean isAbstract() {
          if (modifiers == -1)
              getModifiers();
  
          return((modifiers & VMModifiers.ABSTRACT) > 0);
      }
  
      public boolean isFinal() {
          if (modifiers == -1)
              getModifiers();
  
          return((modifiers & VMModifiers.FINAL) > 0);
      }
  
      public boolean isStatic() {
          if (modifiers == -1)
              getModifiers();
  
          return((modifiers & VMModifiers.STATIC) > 0);
      }
  
      public boolean isPrepared() {
          // This ref type may have been prepared before we were getting
          // events, so get it once.  After that,
          // this status flag is updated through the ClassPrepareEvent,
          // there is no need for the expense of a JDWP query.
          if (status == 0) {
              updateStatus();
          }
          return isPrepared;
      }
  
      public boolean isVerified() {
          // Once true, it never resets, so we don't need to update
          if ((status & JDWP.ClassStatus.VERIFIED) == 0) {
              updateStatus();
          }
          return (status & JDWP.ClassStatus.VERIFIED) != 0;
      }
  
      public boolean isInitialized() {
          // Once initialization succeeds or fails, it never resets,
          // so we don't need to update
          if ((status & INITIALIZED_OR_FAILED) == 0) {
              updateStatus();
          }
          return (status & JDWP.ClassStatus.INITIALIZED) != 0;
      }
  
      public boolean failedToInitialize() {
          // Once initialization succeeds or fails, it never resets,
          // so we don't need to update
          if ((status & INITIALIZED_OR_FAILED) == 0) {
              updateStatus();
          }
          return (status & JDWP.ClassStatus.ERROR) != 0;
      }
  
      public List<Field> fields() {
          List<Field> fields = (fieldsRef == null) ? null : fieldsRef.get();
          if (fields == null) {
              if (vm.canGet1_5LanguageFeatures()) {
                  JDWP.ReferenceType.FieldsWithGeneric.FieldInfo[] jdwpFields;
                  try {
                      jdwpFields = JDWP.ReferenceType.FieldsWithGeneric.process(vm, this).declared;
                  } catch (JDWPException exc) {
                      throw exc.toJDIException();
                  }
                  fields = new ArrayList<Field>(jdwpFields.length);
                  for (int i=0; i<jdwpFields.length; i++) {
                      JDWP.ReferenceType.FieldsWithGeneric.FieldInfo fi
                          = jdwpFields[i];
  
                      Field field = new FieldImpl(vm, this, fi.fieldID,
                                                  fi.name, fi.signature,
                                                  fi.genericSignature,
                                                  fi.modBits);
                      fields.add(field);
                  }
              } else {
                  JDWP.ReferenceType.Fields.FieldInfo[] jdwpFields;
                  try {
                      jdwpFields = JDWP.ReferenceType.Fields.
                          process(vm, this).declared;
                  } catch (JDWPException exc) {
                      throw exc.toJDIException();
                  }
                  fields = new ArrayList<Field>(jdwpFields.length);
                  for (int i=0; i<jdwpFields.length; i++) {
                      JDWP.ReferenceType.Fields.FieldInfo fi = jdwpFields[i];
  
                      Field field = new FieldImpl(vm, this, fi.fieldID,
                                              fi.name, fi.signature,
                                              null,
                                              fi.modBits);
                      fields.add(field);
                  }
              }
  
              fields = Collections.unmodifiableList(fields);
              fieldsRef = new SoftReference<List<Field>>(fields);
          }
          return fields;
      }
  
      abstract List<? extends ReferenceType> inheritedTypes();
  
      void addVisibleFields(List<Field> visibleList, Map<String, Field> visibleTable, List<String> ambiguousNames) {
          for (Field field : visibleFields()) {
              String name = field.name();
              if (!ambiguousNames.contains(name)) {
                  Field duplicate = visibleTable.get(name);
                  if (duplicate == null) {
                      visibleList.add(field);
                      visibleTable.put(name, field);
                  } else if (!field.equals(duplicate)) {
                      ambiguousNames.add(name);
                      visibleTable.remove(name);
                      visibleList.remove(duplicate);
                  } else {
                      // identical field from two branches; do nothing
                  }
              }
          }
      }
  
      public List<Field> visibleFields() {
          /*
           * Maintain two different collections of visible fields. The
           * list maintains a reasonable order for return. The
           * hash map provides an efficient way to lookup visible fields
           * by name, important for finding hidden or ambiguous fields.
           */
          List<Field> visibleList = new ArrayList<Field>();
          Map<String, Field>  visibleTable = new HashMap<String, Field>();
  
          /* Track fields removed from above collection due to ambiguity */
          List<String> ambiguousNames = new ArrayList<String>();
  
          /* Add inherited, visible fields */
          List<? extends ReferenceType> types = inheritedTypes();
          Iterator<? extends ReferenceType> iter = types.iterator();
          while (iter.hasNext()) {
              /*
               * TO DO: Be defensive and check for cyclic interface inheritance
               */
              ReferenceTypeImpl type = (ReferenceTypeImpl)iter.next();
              type.addVisibleFields(visibleList, visibleTable, ambiguousNames);
          }
  
          /*
           * Insert fields from this type, removing any inherited fields they
           * hide.
           */
          List<Field> retList = new ArrayList<Field>(fields());
          for (Field field : retList) {
              Field hidden = visibleTable.get(field.name());
              if (hidden != null) {
                  visibleList.remove(hidden);
              }
          }
          retList.addAll(visibleList);
          return retList;
      }
  
      void addAllFields(List<Field> fieldList, Set<ReferenceType> typeSet) {
          /* Continue the recursion only if this type is new */
          if (!typeSet.contains(this)) {
              typeSet.add((ReferenceType)this);
  
              /* Add local fields */
              fieldList.addAll(fields());
  
              /* Add inherited fields */
              List<? extends ReferenceType> types = inheritedTypes();
              Iterator<? extends ReferenceType> iter = types.iterator();
              while (iter.hasNext()) {
                  ReferenceTypeImpl type = (ReferenceTypeImpl)iter.next();
                  type.addAllFields(fieldList, typeSet);
              }
          }
      }
      public List<Field> allFields() {
          List<Field> fieldList = new ArrayList<Field>();
          Set<ReferenceType> typeSet = new HashSet<ReferenceType>();
          addAllFields(fieldList, typeSet);
          return fieldList;
      }
  
      public Field fieldByName(String fieldName) {
          List<Field> searchList = visibleFields();
  
          for (int i=0; i<searchList.size(); i++) {
              Field f = searchList.get(i);
  
              if (f.name().equals(fieldName)) {
                  return f;
              }
          }
          //throw new NoSuchFieldException("Field '" + fieldName + "' not found in " + name());
          return null;
      }
  
      public List<Method> methods() {
          List<Method> methods = (methodsRef == null) ? null : methodsRef.get();
          if (methods == null) {
              if (!vm.canGet1_5LanguageFeatures()) {
                  methods = methods1_4();
              } else {
                  JDWP.ReferenceType.MethodsWithGeneric.MethodInfo[] declared;
                  try {
                      declared = JDWP.ReferenceType.MethodsWithGeneric.
                          process(vm, this).declared;
                  } catch (JDWPException exc) {
                      throw exc.toJDIException();
                  }
                  methods = new ArrayList<Method>(declared.length);
                  for (int i=0; i<declared.length; i++) {
                      JDWP.ReferenceType.MethodsWithGeneric.MethodInfo
                          mi = declared[i];
  
                      Method method = MethodImpl.createMethodImpl(vm, this,
                                                           mi.methodID,
                                                           mi.name, mi.signature,
                                                           mi.genericSignature,
                                                           mi.modBits);
                      methods.add(method);
                  }
              }
              methods = Collections.unmodifiableList(methods);
              methodsRef = new SoftReference<List<Method>>(methods);
          }
          return methods;
      }
  
      private List<Method> methods1_4() {
          List<Method> methods;
          JDWP.ReferenceType.Methods.MethodInfo[] declared;
          try {
              declared = JDWP.ReferenceType.Methods.
                  process(vm, this).declared;
          } catch (JDWPException exc) {
              throw exc.toJDIException();
          }
          methods = new ArrayList<Method>(declared.length);
          for (int i=0; i<declared.length; i++) {
              JDWP.ReferenceType.Methods.MethodInfo mi = declared[i];
  
              Method method = MethodImpl.createMethodImpl(vm, this,
                                                          mi.methodID,
                                                          mi.name, mi.signature,
                                                          null,
                                                          mi.modBits);
              methods.add(method);
          }
          return methods;
      }
  
      /*
       * Utility method used by subclasses to build lists of visible
       * methods.
       */
      void addToMethodMap(Map<String, Method> methodMap, List<Method> methodList) {
          for (Method method : methodList)
              methodMap.put(method.name().concat(method.signature()), method);
          }
  
      abstract void addVisibleMethods(Map<String, Method> methodMap, Set<InterfaceType> seenInterfaces);
  
      public List<Method> visibleMethods() {
          /*
           * Build a collection of all visible methods. The hash
           * map allows us to do this efficiently by keying on the
           * concatenation of name and signature.
           */
          Map<String, Method> map = new HashMap<String, Method>();
          addVisibleMethods(map, new HashSet<InterfaceType>());
  
          /*
           * ... but the hash map destroys order. Methods should be
           * returned in a sensible order, as they are in allMethods().
           * So, start over with allMethods() and use the hash map
           * to filter that ordered collection.
           */
          List<Method> list = allMethods();
          list.retainAll(map.values());
          return list;
      }
  
      abstract public List<Method> allMethods();
  
      public List<Method> methodsByName(String name) {
          List<Method> methods = visibleMethods();
          ArrayList<Method> retList = new ArrayList<Method>(methods.size());
          for (Method candidate : methods) {
              if (candidate.name().equals(name)) {
                  retList.add(candidate);
              }
          }
          retList.trimToSize();
          return retList;
      }
  
      public List<Method> methodsByName(String name, String signature) {
          List<Method> methods = visibleMethods();
          ArrayList<Method> retList = new ArrayList<Method>(methods.size());
          for (Method candidate : methods) {
              if (candidate.name().equals(name) &&
                  candidate.signature().equals(signature)) {
                  retList.add(candidate);
              }
          }
          retList.trimToSize();
          return retList;
      }
  
      List<InterfaceType> getInterfaces() {
          InterfaceTypeImpl[] intfs;
          try {
              intfs = JDWP.ReferenceType.Interfaces.
                                           process(vm, this).interfaces;
          } catch (JDWPException exc) {
              throw exc.toJDIException();
          }
          return Arrays.asList((InterfaceType[])intfs);
      }
  
      public List<ReferenceType> nestedTypes() {
          List<ReferenceType> all = vm.allClasses();
          List<ReferenceType> nested = new ArrayList<ReferenceType>();
          String outername = name();
  