You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@uima.apache.org by sc...@apache.org on 2016/08/18 15:56:45 UTC

svn commit: r1756826 - /uima/uimaj/branches/experiment-v3-jcas/uimaj-core/src/main/java/org/apache/uima/cas/impl/FSClassRegistry.java

Author: schor
Date: Thu Aug 18 15:56:45 2016
New Revision: 1756826

URL: http://svn.apache.org/viewvc?rev=1756826&view=rev
Log:
[UIMA-4679] [UIMA-5030] drop generator use for built-in arrays.  Support Pears, fix method handle lookup context to work with Pears. 

Modified:
    uima/uimaj/branches/experiment-v3-jcas/uimaj-core/src/main/java/org/apache/uima/cas/impl/FSClassRegistry.java

Modified: uima/uimaj/branches/experiment-v3-jcas/uimaj-core/src/main/java/org/apache/uima/cas/impl/FSClassRegistry.java
URL: http://svn.apache.org/viewvc/uima/uimaj/branches/experiment-v3-jcas/uimaj-core/src/main/java/org/apache/uima/cas/impl/FSClassRegistry.java?rev=1756826&r1=1756825&r2=1756826&view=diff
==============================================================================
--- uima/uimaj/branches/experiment-v3-jcas/uimaj-core/src/main/java/org/apache/uima/cas/impl/FSClassRegistry.java (original)
+++ uima/uimaj/branches/experiment-v3-jcas/uimaj-core/src/main/java/org/apache/uima/cas/impl/FSClassRegistry.java Thu Aug 18 15:56:45 2016
@@ -25,16 +25,20 @@ import java.lang.invoke.CallSite;
 import java.lang.invoke.LambdaMetafactory;
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
 import java.lang.invoke.MethodType;
 import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.lang.reflect.Parameter;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
+import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 
 import org.apache.uima.UIMAFramework;
 import org.apache.uima.UIMARuntimeException;
@@ -43,13 +47,16 @@ import org.apache.uima.cas.CAS;
 import org.apache.uima.cas.CASException;
 import org.apache.uima.cas.CASRuntimeException;
 import org.apache.uima.internal.util.Misc;
+import org.apache.uima.internal.util.UIMAClassLoader;
 import org.apache.uima.jcas.cas.TOP;
+import org.apache.uima.jcas.tcas.Annotation;
 import org.apache.uima.util.Level;
 import org.apache.uima.util.Logger;
 
-/*
- * There is one **class** instance of this per class loader.
- *   - Builtin JCas Types are loaded and shared among all type systems, once, when this class is loaded.
+/**
+ * There is one **class** instance of this per UIMA core class loader.
+ *   The class loader is the loader for the UIMA core classes, not any UIMA extension class loader
+ *   - **Builtin** JCas Types are loaded and shared among all type systems, once, when this class is loaded.
  * 
  * There are no instances of this class.  
  *   - The type system impl instances at commit time initialize parts of their Impl from data in this class
@@ -60,20 +67,32 @@ import org.apache.uima.util.Logger;
  *   - by multiple CASes (in a CAS pool, for instance, when these CASes are sharing the same type system).
  *   - by all views of those CASes.
  *   - by multiple different pipelines, built using the same merged type system instance
+ *   - by non-built-in JCas classes, loaded under possibly different extension class loaders
  *   
  * PEAR support
- *   Multiple PEAR contexts can be associated with the main class loader.
- *   - Each has a unique class loader, whose parent is the main class loader.
+ *   Multiple PEAR contexts can be used.
+ *   - hierarchy (each is parent of kind below
+ *     -- UIMA core class loader (built-ins, not redefinable by user JCas classes) 
+ *         --- a new limitation of UIMA V3 to allow sharing of built-in JCas classes, which also
+ *             have custom impl, and don't fit the model used for PEAR Trampolines
+ *     -- outer (non Pear) class loader (optional, known as base extension class loader)
+ *         --- possible multiple, for different AE pipelines
+ *     -- Within PEAR class loader
  *   - when running within a PEAR, operations which return Feature Structures potentially return
  *     JCas instances of classes loaded from the Pear's class loader. 
- *       - These instances share the same int[] and Object[] and _typeImpl and _casView refs with the outer class loader's FS  
- */
-
- /*          
- *   At typeSystemCommit time, this data structure is created and initialized: 
+ *       - These instances share the same int[] and Object[] and _typeImpl and _casView refs with the outer class loader's FS
+ * 
+ * Timing / life cycle
+ *   Built-in classes loaded & initialized at first type system commit time.
+ *   non-pear classes loaded & initialized at type system commit time (if not already loaded)
+ *     - special checks for conformability if some types loaded later, due to requirements for computing feature offsets at load time
+ *   pear classes loaded & initialized at first entry to Pear, for a given type system and class loader.        
+ *
+ *          
+ *   At typeSystemCommit time, this class is created and initialized: 
  *     - The built-in JCas types are loaded
  *     
- *     - The user-defined JCas classes are loaded (not lazy, but eager), provided the type system is a new one.
+ *     - The user-defined non-PEAR JCas classes are loaded (not lazy, but eager), provided the type system is a new one.
  *       (If the type system is "equal" to an existing committed one, that one is used instead).
  *          
  *       -- User classes defined with the name of UIMA types, but which are not JCas definitions, are not used as 
@@ -81,8 +100,7 @@ import org.apache.uima.util.Logger;
  *          has the same name as a UIMA type, but is not a JCas class.
  *       -- These classes, once loaded, remain loaded because of Java's design, unless the ClassLoader
  *          used to load them is Garbage Collected.
- *          --- The ClassLoader used is the same ClassLoader used for the type system, because
- *              multiple sets of JCas classes per typesystem isn't supported.
+ *          --- The ClassLoader used is the CAS's JCasClassLoader, set from the UIMA Extension class loader if specified.
  *              
  *   Assigning slots for features:
  *     - each type being loaded runs static final initializers to set for (a subset of) all features the offset
@@ -92,22 +110,22 @@ import org.apache.uima.util.Logger;
  */
 
 public abstract class FSClassRegistry { // abstract to prevent instantiating; this class only has static methods
+      
+  // Used for the built-ins.
+  private static final MethodHandles.Lookup defaultLookup = MethodHandles.lookup();
   
-//  private static final boolean GETTER = true;
-//  private static final boolean SETTER = false;
-    
-  private static final MethodHandles.Lookup lookup = MethodHandles.lookup();
+  private static MethodHandles.Lookup lookup;
   
   private static final MethodType findConstructorJCasCoverType      = methodType(void.class, TypeImpl.class, CASImpl.class);
-  private static final MethodType findConstructorJCasCoverTypeArray = methodType(void.class, TypeImpl.class, CASImpl.class, int.class);
+//  private static final MethodType findConstructorJCasCoverTypeArray = methodType(void.class, TypeImpl.class, CASImpl.class, int.class);
   /**
    * The callsite has the return type, followed by capture arguments
    */
   private static final MethodType callsiteFsGenerator      = methodType(FsGenerator.class);
-  private static final MethodType callsiteFsGeneratorArray = methodType(FsGeneratorArray.class);
+//  private static final MethodType callsiteFsGeneratorArray = methodType(FsGeneratorArray.class);  // NO LONGER USED
   
   private static final MethodType fsGeneratorType      = methodType(TOP.class, TypeImpl.class, CASImpl.class);
-  private static final MethodType fsGeneratorArrayType = methodType(TOP.class, TypeImpl.class, CASImpl.class, int.class);
+//  private static final MethodType fsGeneratorArrayType = methodType(TOP.class, TypeImpl.class, CASImpl.class, int.class); // NO LONGER USED
 
   static private class ErrorReport {
     final Exception e;
@@ -120,193 +138,142 @@ public abstract class FSClassRegistry {
   // must precede first (static) use
   static private ThreadLocal<List<ErrorReport>> errorSet = new ThreadLocal<>();
  
-
-//  public static class GetterSetter {
-//    final Object getter;
-//    final Object setter;
-//    final Object setterNcNj;
-//    GetterSetter(Object getter, Object setter, Object setterNcNj) {
-//      this.getter = getter;
-//      this.setter = setter;
-//      this.setterNcNj = setterNcNj;
-//    }
-//  }
-  
   /**
-   * One instance per JCas Class, loaded or not
+   * One instance per JCas Class per class loader
    * 
    * Created when potentially loading JCas classes.
    * 
-   * Entries kept in a global static hashmap, key = typename (string)
+   * Entries kept in potentially multiple global static hashmap, typename (string)
+   *   - one hashmap per classloader
    *   Entries reused potentially by multiple type systems.
-   *   - Entries copied into global static array for built-ins, indexed by type code,
-   *   - Entries copied into instance array for particular type system, indexed by type code. 
    * 
    * Info used for identifying the target of a "new" operator - could be generator for superclass.
-   * One entry per defined JCas class; no instance if no JCas class is defined.
+   * One entry per defined JCas class per classloader; no instance if no JCas class is defined.
    */
   public static class JCasClassInfo {
     
-    /**
-     * For createFS(type) use
-     *   cast to FsGenerator or FsGeneratorArray
-     */
-    final Object generator;
+    final FsGenerator generator;
    
     /**
      * The corresponding loaded JCas Class
      */
     final Class<?> jcasClass;
     
+    /**
+     * NOT the TypeCode, but instead, the unique int assigned to the JCas class 
+     * when it gets loaded
+     * Might be -1 if the JCasClassInfo instance is for a non-JCas instantiable type
+     */
     final int jcasType;
-    
-//    /**
-//     * map from the feature short name to the getter/setter Lambda
-//     */
-//    final Map<String, GetterSetter> gettersAndSetters = new HashMap<>(1);
-    
-    JCasClassInfo(Class<?> jcasClass, Object generator, int jcasType) {
+        
+    JCasClassInfo(Class<?> jcasClass, FsGenerator generator, int jcasType) {
       this.generator = generator;
       this.jcasClass = jcasClass;
-      this.jcasType = jcasType;
+      this.jcasType = jcasType;    // typeId for jcas class, **NOT Typecode**
+    }
+    
+    boolean isCopydown(TypeImpl ti) {
+      return isCopydown(Misc.typeName2ClassName(ti.getName()));
+    }
+  
+    boolean isCopydown(String jcasClassName) {
+      return !jcasClass.getCanonicalName().equals(jcasClassName);
+    }
+    
+    boolean isPearOverride(ClassLoader cl) {
+      return jcasClass.getClassLoader().equals(cl);
     }
   }
 
   /**
-   * Map from all type names from all type systems loaded under this class loader to the JCasClassInfo for that type.
-   * 
-   * Set after the JCas Class is attempted to be loaded for the first time.
+   * Map from class loaders used to load JCas Classes, both PEAR and non-Pear cases, to JCasClassInfo for that loaded JCas class instance.
+   *   Key is JCas fully qualified name (not UIMA type name).
+   *   Is String, since different type systems may use the same JCas classes.
+   * Cache of FsGenerator[]s kept in TypeSystemImpl instance, since it depends on type codes.
+   * Current FsGenerator[] kept in CASImpl shared view data, switched as needed for PEARs. 
    */
-  public static final Map<String, JCasClassInfo> type2JCas = new HashMap<>(64);
-  
+  private static final Map<ClassLoader, Map<String, JCasClassInfo>> cl2type2JCas = new IdentityHashMap<>();
+    
   /**
    * precomputed generators for built-in types
    * These instances are shared for all type systems
+   * Key = index = typecode
    */
   private static final JCasClassInfo[] jcasClassesInfoForBuiltins;
 
   static {
     TypeSystemImpl tsi = TypeSystemImpl.staticTsi;
     jcasClassesInfoForBuiltins = new JCasClassInfo[tsi.getTypeArraySize()]; 
+    lookup = defaultLookup;
         
     // walk in subsumption order, supertype before subtype
-    loadBuiltins(tsi.topType);
+    // Class loader used for builtins is the UIMA framework's class loader
+    loadBuiltins(tsi.topType, tsi.getClass().getClassLoader());
     
     reportErrors();
   }
   
-  private static void loadBuiltins(TypeImpl ti) {
+  private static void loadBuiltins(TypeImpl ti, ClassLoader cl) {
     String typeName = ti.getName();
     
     if (BuiltinTypeKinds.creatableBuiltinJCas.contains(typeName) || typeName.equals(CAS.TYPE_NAME_SOFA)) {
-      Class<?> builtinClass = maybeLoadJCas(ti);
+      Class<?> builtinClass = maybeLoadJCas(ti, cl);
       assert (builtinClass != null);  // builtin types must be present
       // copy down to subtypes, if needed, done later
       int jcasType = Misc.getStaticIntFieldNoInherit(builtinClass, "typeIndexID");
       JCasClassInfo jcasClassInfo = createJCasClassInfo(builtinClass, ti, jcasType); 
       jcasClassesInfoForBuiltins[ti.getCode()] = jcasClassInfo; 
-//      setupGetterSetter(ti, jcasClassInfo);
     }
     
     for (TypeImpl subType : ti.getDirectSubtypes()) {
       TypeSystemImpl.typeBeingLoadedThreadLocal.set(subType);
-      loadBuiltins(subType);
+      loadBuiltins(subType, cl);
     }
   }
-  
-//  final FeatureImpl[] featuresFromJFRI;
-  
-//  /**
-//   * Called at Type System commit time
-//   * 
-//   * Install the default (non-JCas) generator for all types in the type system and the
-//   * JCas style generators for the built-in types
-//   * 
-//   * Also, for all loaded JCas classes, set the javaClass field (including
-//   *   in subtypes with no JCas class defined).
-//   *   
-//   * Called under TypeSystemImpl - instance - lock 
-//   * 
-//   * Could be running multiple threads
-//   *   - one per TypeSystemImpl - instance
-//   *   
-//   * @param ts - the type system
-//   * @param isDoUserJCasLoading a flag to skip loading the JCas classes
-//   */
-//  FSClassRegistry(TypeSystemImpl ts, boolean isDoUserJCasLoading) {
-//    
-//    ts.jcasClassesInfo = new JCasClassInfo[ts.getTypeArraySize()];
-//
-//    /**
-//     * copy in built-ins
-//     */
-//    for (int i = 0; i < jcasClassesInfoForBuiltins.length; i++) {
-//  
-//      JCasClassInfo jci = jcasClassesInfoForBuiltins[i];
-//      ts.jcasClassesInfo[i] = jci;
-//      if (jci != null) {
-//        int v = Misc.getStaticIntField(ts.getJCasClass(i), "typeIndexID");
-//        // v is negative if not found, which is the case for types like FloatList (these can't be instantiated)
-//        if (v >= 0) {
-//          ts.setJCasRegisteredType(v, ts.getTypeForCode(i));
-//        }
-//      }
-//    }
-//     
-//    /**
-//     * Add all user-defined JCas Types, in subsumption order
-//     *   We add these now, in case JCas is turned on later - unless specifically
-//     *   specified to run without user-defined JCas loading
-//     */
-//    
-//    if (isDoUserJCasLoading) {
-//      /**
-//       * Two passes are needed loading is needed.  
-//       *   - The first one loads the JCas Cover Classes initializes everything
-//       *      -- some of the classes might already be loaded (including the builtins which are loaded once per class loader)
-//       *   - The second pass performs the conformance checks between the loaded JCas cover classes, and the current type system.
-//       *     This depends on having the TypeImpl's javaClass field be accurate (reflect any loaded JCas types)
-//       */
-//      maybeLoadJCasAndSubtypes(ts, ts.topType, ts.jcasClassesInfo[TypeSystemConstants.topTypeCode]);
-//      checkConformance(ts, ts.topType);
-////      setupGettersSetters(ts, ts.topType, jcasClassesInfo);
-//    }
-//    
-//    // walk the type system and extract all the registry indexes
-//    // While walking, update the FeatureImpl with the registry index
-////    ArrayList<FeatureImpl> ffjfri = getFeatureFromJFRI(ts, ts.topType, new ArrayList<FeatureImpl>());
-//    
-////    featuresFromJFRI = new FeatureImpl[ffjfri.size()];
-////    ffjfri.toArray(featuresFromJFRI);
-//    
-//    reportErrors();
-//  }
-  
-  static void loadAtTypeSystemCommitTime(TypeSystemImpl ts, boolean isDoUserJCasLoading) { 
     
-//    ts.jcasClassesInfo = new JCasClassInfo[ts.getTypeArraySize()];
+  /**
+   * Load JCas types for some combination of class loader and type system
+   * These classes may have already been loaded for this type system
+   * These classes may have already been loaded (perhaps for another type system)
+   * @param ts 
+   * @param isDoUserJCasLoading
+   * @param cl
+   */
+  static void loadAtTypeSystemCommitTime(TypeSystemImpl ts, boolean isDoUserJCasLoading, ClassLoader cl) { 
 
+    boolean alreadyLoaded = false;
+    Map<String, JCasClassInfo> t2jcci;
+
+    synchronized (cl2type2JCas) {
+      t2jcci = cl2type2JCas.get(cl);
+    
+      if (null == t2jcci) {    
+        t2jcci = new HashMap<>();
+        cl2type2JCas.put(cl, t2jcci);
+      } else {
+        alreadyLoaded = true;
+      }
+    }
+    
     /**
      * copy in built-ins
+     *   update t2jcci (if not already loaded) with load info for type
+     *   update type system's map from unique JCasID to the type in this type system
      */
-    for (int i = 0; i < jcasClassesInfoForBuiltins.length; i++) {
+    lookup = defaultLookup;
+    for (int typecode = 1; typecode < jcasClassesInfoForBuiltins.length; typecode++) {
   
-      JCasClassInfo jci = jcasClassesInfoForBuiltins[i];
-      if (jci != null) {
-        TypeImpl ti = ts.getTypeForCode(i);
-        ti.setJcasClassInfo(jci);
-//        ts.jcasClassesInfo[i] = jci;
-//        TypeImpl ti = ts.getTypeForCode(i);
-//        int v = Misc.getStaticIntField(ti.getJcasClassInfo().jcasClass, "typeIndexID");
-
-        int v = Misc.getStaticIntField(jci.jcasClass, "typeIndexID");
-        // v is negative if not found, which is the case for types like FloatList (these can't be instantiated)
-        if (v >= 0) {
-          ts.setJCasRegisteredType(v, ts.getTypeForCode(i));
+      JCasClassInfo jcci = jcasClassesInfoForBuiltins[typecode];
+      if (jcci != null) {
+        Class<?> jcasClass = jcci.jcasClass;  
+
+        if (!alreadyLoaded) {
+          t2jcci.put(jcasClass.getCanonicalName(), jcci);
         }
+        setTypeFromJCasIDforBuiltIns(jcci, ts, typecode);
       }
-    }
-     
+    }  
+    
     /**
      * Add all user-defined JCas Types, in subsumption order
      *   We add these now, in case JCas is turned on later - unless specifically
@@ -321,134 +288,139 @@ public abstract class FSClassRegistry {
        *   - The second pass performs the conformance checks between the loaded JCas cover classes, and the current type system.
        *     This depends on having the TypeImpl's javaClass field be accurate (reflect any loaded JCas types)
        */
-      maybeLoadJCasAndSubtypes(ts, ts.topType, ts.topType.getJcasClassInfo()); 
-      checkConformance(ts, ts.topType);
+      
+      try {
+        Class<?> clazz = Class.forName(UIMAClassLoader.MHLC, true, cl);
+        Method m = clazz.getMethod("getMethodHandlesLookup");
+        lookup = (Lookup) m.invoke(null);
+      } catch (ClassNotFoundException | NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+        throw new UIMARuntimeException(e, UIMARuntimeException.INTERNAL_ERROR);
+      }
+
+      maybeLoadJCasAndSubtypes(ts, ts.topType, t2jcci.get(TOP.class.getCanonicalName()), cl, t2jcci);
+      checkConformance(ts, ts.topType, t2jcci);
     }
         
     reportErrors();
   }
 
-//  /**
-//   * Walk type system from TOP, depth first
-//   *   - for each type, for all the features introduced, 
-//   *     -- collect if exists the field registry # and also save in the FeatureImpl
-//   * @param ts
-//   * @param ti
-//   * @param collector
-//   * @return
-//   */
-//  private ArrayList<FeatureImpl> getFeatureFromJFRI(TypeSystemImpl ts, TypeImpl ti, ArrayList<FeatureImpl> collector) {
-//    Class<?> clazz = getJCasClass(ti.getCode());
-//    for (FeatureImpl fi : ti.getMergedStaticFeaturesIntroducedByThisType()) {
-//      int indexJFRI = Misc.getStaticIntFieldNoInherit(clazz, "_FI_" + fi.getShortName());
-//      if (indexJFRI != Integer.MIN_VALUE) {  // that value is code for not found
-//        fi.registryIndex = indexJFRI;
-//        Misc.setWithExpand(collector, indexJFRI, fi);
-////      } else {
-////        System.out.println("debug: not found " + clazz.getName() + ", feature = " + fi.getShortName());
-//      }
-//    }
-//    
-//    for (TypeImpl subtype : ti.getDirectSubtypes()) {
-//      getFeatureFromJFRI(ts, subtype, collector);
-//    }
-//    return collector;
-//  } 
+  private static void setTypeFromJCasIDforBuiltIns(JCasClassInfo jcci, TypeSystemImpl tsi, int typeCode) {
+    int v = jcci.jcasType;
+    // v is negative if not found, which is the case for types like FloatList (these can't be instantiated)
+    if (v >= 0) {
+      tsi.setJCasRegisteredType(v, tsi.getTypeForCode(typeCode));
+    }
+  }
 
   /**
-   * Called for all the types, including the built-ins.
-   * @param ts
-   * @param ti
+   * Called for all the types, including the built-ins, but the built-ins have already been set up by the caller.
+   * Saves the results in two places
+   *   type system independent spot: JCasClassInfo instance indexed by JCasClassName
+   *   type system spot: the JCasIndexID -> type table in the type system
+   * @param ts the type system
+   * @param ti the type to process
    * @param copyDownDefault_jcasClassInfo
+   * @param cl the loader used to load, and to save the results under the key of the class loader the results
+   * @param type2JCas map holding the results of loading JCas classes
    */
-  private static void maybeLoadJCasAndSubtypes(TypeSystemImpl ts, TypeImpl ti, JCasClassInfo copyDownDefault_jcasClassInfo) {
+  private static void maybeLoadJCasAndSubtypes(
+      TypeSystemImpl ts, 
+      TypeImpl ti, 
+      JCasClassInfo copyDownDefault_jcasClassInfo,
+      ClassLoader cl,
+      Map<String, JCasClassInfo> type2JCas) {
+        
+    String t2jcciKey = Misc.typeName2ClassName(ti.getName());
+    JCasClassInfo jcci = type2JCas.get(t2jcciKey);
+    boolean isCopyDown = true;
+
+    if (jcci == null) {
+
+      // not yet recorded as loaded under this class loader.
     
-    JCasClassInfo jcasClassInfo;
-//    boolean debug = "com.ibm.hutt.Predicate".equals(ti.getName());
-//    if (debug) {
-//      System.out.println("Loading com.ibm.hutt.Predicate");
-//    }
-    synchronized(type2JCas) {
-      jcasClassInfo = type2JCas.get(ti.getName());
-//      if (debug) {
-//        System.out.println("Loading com.ibm.hutt.Predicate: jcasClassInfo fetch was " + ((jcasClassInfo == null) ? "null" : "not null"));
-//      }
-      if (jcasClassInfo == null) {
-        // not yet loaded.  if Built-in, always skip this body
-        jcasClassInfo = copyDownDefault_jcasClassInfo;  // initialize in case no JCas for this type
-      
+      Class<?> clazz = maybeLoadJCas(ti, cl);
+      if (null != clazz && TOP.class.isAssignableFrom(clazz)) {
+        
+        int jcasType = -1;
+        if (!Modifier.isAbstract(clazz.getModifiers())) { // skip next for abstract classes
+          jcasType = Misc.getStaticIntFieldNoInherit(clazz, "typeIndexID");
+          // if jcasType is negative, this means there's no value for this field
+          assert(jcasType >= 0);
+        }         
+        jcci = createJCasClassInfo(clazz, ti, jcasType);
+        isCopyDown = false;
+        if (clazz != TOP.class) {  // TOP has no super class
+          validateSuperClass(jcci, ti);
+        }
+      } else {
+        jcci = copyDownDefault_jcasClassInfo;
+      }
       
-        Class<?> clazz = maybeLoadJCas(ti);
-        if (null != clazz && TOP.class.isAssignableFrom(clazz)) {
-          
-          int jcasType = -1;
-          if (!Modifier.isAbstract(clazz.getModifiers())) { // skip next for abstract classes
-            jcasType = Misc.getStaticIntFieldNoInherit(clazz, "typeIndexID");
-            // if jcasType is negative, this means there's no value for this field
-            assert(jcasType >= 0);
-            ts.setJCasRegisteredType(jcasType, ti);
-          } else {
-          }
-          
-          jcasClassInfo = createJCasClassInfo(clazz, ti, jcasType); 
-          type2JCas.put(ti.getName(), jcasClassInfo);
+      type2JCas.put(t2jcciKey, jcci);
 
-        } 
-      } else {  // jcasClassInfo already set
-        // already have info for this class, but need to set the jcas registry for this type system
-        if (jcasClassInfo.jcasType >= 0) {
-          ts.setJCasRegisteredType(jcasClassInfo.jcasType, ti);
-        }
+    } else {
+      // this UIMA type was set up (maybe loaded, maybe defaulted to a copy-down) previously
+      isCopyDown = jcci.isCopydown(t2jcciKey);
+
+      if (isCopyDown) {
+        // the "stored" version might have the wrong super class for this type system
+        type2JCas.put(t2jcciKey, jcci = copyDownDefault_jcasClassInfo);
+        
+      } else if (ti != ts.topType) {
+        // strong test for non-copy-down case: supertype must match, with 2 exceptions
+        validateSuperClass(jcci, ti);
       }
-    } // end of synch on type2JCas
-    
-    // this check is done even after the class is first loaded, in case the type system changed.
-    //   -- if the new type system is equal to a previous one, then no new FSClassRegistry is created.
-    ti.setJcasClassInfo(jcasClassInfo);
-//    ts.jcasClassesInfo[ti.getCode()] = jcasClassInfo;  // sets new one or default one
+    }
+       
+    // this is done even after the class is first loaded, in case the type system changed.
+    // don't set anything if copy down - otherwise was setting the copyed-down typeId ref to the 
+    //   new ti
+    if (jcci.jcasType >= 0 && ! isCopyDown) {
+      ts.setJCasRegisteredType(jcci.jcasType, ti); 
+    }
     
     if (!ti.isPrimitive()) {  // bypass this for primitives because the jcasClassInfo is the "inherited one" of TOP
-      ti.setJavaClass(jcasClassInfo.jcasClass);
+      /**
+       * Note: this value sets into the shared TypeImpl (maybe shared among many JCas impls) the "latest" jcasClass
+       * It is "read" by the conformance testing, while still under the type system lock.
+       * Other uses of this may get an arbitrary (the latest) version of the class
+       * Currently the only other use is in backwards compatibility with low level type system "switching" an existing type.
+       */
+      ti.setJavaClass(jcci.jcasClass);
     }
     
+    
     for (TypeImpl subtype : ti.getDirectSubtypes()) {
       TypeSystemImpl.typeBeingLoadedThreadLocal.set(subtype);
-      maybeLoadJCasAndSubtypes(ts, subtype, jcasClassInfo);
+      maybeLoadJCasAndSubtypes(ts, subtype, jcci, cl, type2JCas);
     }
   }
-    
-//  private static void setupGettersSetters(TypeSystemImpl ts, TypeImpl ti, JCasClassInfo[] jci) {
-//    boolean isBuiltin = BuiltinTypeKinds.creatableBuiltinJCas.contains(ti.getName());
-//
-//    if (!isBuiltin) {
-//      setupGetterSetter(ti, jci[ti.getCode()]);
-//    }
-//    
-//    for (TypeImpl subtype : ti.getDirectSubtypes()) {
-//      setupGettersSetters(ts, subtype, jci);
-//    }
-//  }
-  
-//  private static void setupGetterSetter(TypeImpl ti, JCasClassInfo jcasClassInfo) {
-//
-//      final Class<?> jcasClass = jcasClassInfo.jcasClass;
-//
-//      if (jcasClass.getName().equals(typeName2ClassName(ti.getName()))) {  // skip if this type is using a superclass JCas class
-//        for (FeatureImpl fi : ti.getMergedStaticFeaturesIntroducedByThisType()) {
-//          if (!isFieldInClass(fi, jcasClass)) {
-//            continue;
-//          }
-//          Object getter = createGetterOrSetter(jcasClass, fi, GETTER, false);
-//          Object setter = createGetterOrSetter(jcasClass, fi, SETTER, false);
-//          Object setterNcNj = null; // createGetterOrSetter(jcasClass, fi, SETTER, true);
-//          
-//          GetterSetter prev = jcasClassInfo.gettersAndSetters.put(fi.getShortName(), new GetterSetter(getter, setter, setterNcNj));
-//          if (prev != null) {
-//            throw new CASRuntimeException(CASRuntimeException.INTERNAL_ERROR);
-//          }
-//        }            
-//      }
-//  }
+
+  private static String superTypeJCasName(TypeImpl ti) {
+    return Misc.typeName2ClassName(ti.getSuperType().getName());
+  }
+  /**
+   * verify that the supertype class chain matches the type
+   * @param clazz -
+   * @param ti -
+   */
+  private static void validateSuperClass(JCasClassInfo jcci, TypeImpl ti) {
+    final Class<?> clazz = jcci.jcasClass; 
+    if (! clazz.getSuperclass().getCanonicalName().equals(superTypeJCasName(ti))) {
+      /** Special case exceptions */
+      TypeImpl superti = ti.getSuperType();
+      TypeSystemImpl tsi = ti.getTypeSystem();
+      if (superti == tsi.arrayBaseType ||
+          superti == tsi.listBaseType) return;
+      /** The JCas class: "{0}" has supertype: "{1}" which doesn''t  match the UIMA type "{2}"''s supertype "{3}". */
+      throw new CASRuntimeException(CASRuntimeException.JCAS_MISMATCH_SUPERTYPE,
+        clazz.getCanonicalName(), 
+        clazz.getSuperclass().getCanonicalName(),
+        ti.getName(),
+        ti.getSuperType().getName());
+    }
+
+  }
   
   /**
    * Called to load (if possible) a corresponding JCas class for a UIMA type.
@@ -462,16 +434,13 @@ public abstract class FSClassRegistry {
    * @param cl the class loader to use
    * @return the loaded / resolved class
    */
-  private static Class<?> maybeLoadJCas(TypeImpl ti) {
+  private static Class<?> maybeLoadJCas(TypeImpl ti, ClassLoader cl) {
     Class<?> clazz = null;
     String className = Misc.typeName2ClassName(ti.getName());
     
     try {
       TypeSystemImpl.typeBeingLoadedThreadLocal.set(ti);
-//      synchronized (className) { // to insure clazz receives a fully-resolved class, must use interned name
-        // parallel class loaders already sync on the name
-        clazz = Class.forName(className, true, FSClassRegistry.class.getClassLoader());
-//      }
+      clazz = Class.forName(className, true, cl);
     } catch (ClassNotFoundException e) {
       // This is normal, if there is no JCas for this class
     } finally {
@@ -488,30 +457,26 @@ public abstract class FSClassRegistry {
    * @return a Functional Interface whose createFS method takes a casImpl 
    *         and when subsequently invoked, returns a new instance of the class
    */
-  private static Object createGenerator(Class<?> jcasClass, boolean isArray) {
+  private static FsGenerator createGenerator(Class<?> jcasClass) {
     try {
-      MethodHandle mh = lookup.findConstructor(jcasClass, isArray ? findConstructorJCasCoverTypeArray 
-                                                                  : findConstructorJCasCoverType);
-      MethodType mtThisGenerator = isArray ? methodType(jcasClass, TypeImpl.class, CASImpl.class, int.class)
-                                           : methodType(jcasClass, TypeImpl.class, CASImpl.class);
+      
+      MethodHandle mh = lookup.findConstructor(jcasClass, findConstructorJCasCoverType);
+      MethodType mtThisGenerator = methodType(jcasClass, TypeImpl.class, CASImpl.class);
  
       CallSite callSite = LambdaMetafactory.metafactory(
           lookup, // lookup context for the constructor 
           "createFS", // name of the method in the Function Interface 
-          isArray ? callsiteFsGeneratorArray  // signature of callsite, return type is functional interface, args are captured args if any
-                  : callsiteFsGenerator,
-          isArray ? fsGeneratorArrayType 
-                  : fsGeneratorType,  // samMethodType signature and return type of method impl by function object 
+          callsiteFsGenerator, // signature of callsite, return type is functional interface, args are captured args if any
+          fsGeneratorType,  // samMethodType signature and return type of method impl by function object 
           mh,  // method handle to constructor 
           mtThisGenerator);
-      return isArray ? (FsGeneratorArray) callSite.getTarget().invokeExact()
-                     : (FsGenerator) callSite.getTarget().invokeExact();
+      return (FsGenerator) callSite.getTarget().invokeExact();
     } catch (Throwable e) {
       if (e instanceof NoSuchMethodException) {
         String classname = jcasClass.getName();
         add2errors(errorSet, new CASRuntimeException(e, CASRuntimeException.JCAS_CAS_NOT_V3, 
             classname,
-            FSClassRegistry.class.getClassLoader().getResource(classname.replace('.', '/') + ".class").toString()
+            jcasClass.getClassLoader().getResource(classname.replace('.', '/') + ".class").toString()
             ));
         return null;
       }
@@ -635,8 +600,9 @@ public abstract class FSClassRegistry {
    */
   private static JCasClassInfo createJCasClassInfo(Class<?> jcasClass, TypeImpl ti, int jcasType) {
     boolean noGenerator = ti.getCode() == TypeSystemConstants.sofaTypeCode ||
-                          Modifier.isAbstract(jcasClass.getModifiers()); 
-    Object generator = noGenerator ? null : createGenerator(jcasClass, ti.isArray());
+                          Modifier.isAbstract(jcasClass.getModifiers()) ||
+                          ti.isArray(); 
+    FsGenerator generator = noGenerator ? null : createGenerator(jcasClass);
     JCasClassInfo jcasClassInfo = new JCasClassInfo(jcasClass, generator, jcasType);
     return jcasClassInfo;
   }
@@ -650,15 +616,15 @@ public abstract class FSClassRegistry {
 //  }
   
   
-  private static void checkConformance(TypeSystemImpl ts, TypeImpl ti) {
+  private static void checkConformance(TypeSystemImpl ts, TypeImpl ti, Map<String, JCasClassInfo> type2jcci) {
     if (ti.isPrimitive()) return;
-    JCasClassInfo jcasClassInfo = type2JCas.get(ti.getName());
+    JCasClassInfo jcasClassInfo = type2jcci.get(ti.getName());
     if (null != jcasClassInfo) { // skip if the UIMA class has an abstract (non-creatable) JCas class)      
-      checkConformance(type2JCas.get(ti.getName()).jcasClass, ts, ti);
+      checkConformance(jcasClassInfo.jcasClass, ts, ti, type2jcci);
     }
     
     for (TypeImpl subtype : ti.getDirectSubtypes()) {
-      checkConformance(ts, subtype);
+      checkConformance(ts, subtype, type2jcci);
     }
   }
   
@@ -672,7 +638,7 @@ public abstract class FSClassRegistry {
    * @param tsi -
    * @param ti -
    */
-  private static void checkConformance(Class<?> clazz, TypeSystemImpl tsi, TypeImpl ti) {
+  private static void checkConformance(Class<?> clazz, TypeSystemImpl tsi, TypeImpl ti, Map<String, JCasClassInfo> type2jcci) {
 
     // skip the test if the jcasClassInfo is being inherited
     //   because that has already been checked
@@ -681,13 +647,13 @@ public abstract class FSClassRegistry {
     }
     
     // check supertype
-   
+         
     // one of the supertypes must match a superclass of the class
     boolean isOk = false;
     List<Class<?>> superClasses = new ArrayList<>();
    outer:
     for (TypeImpl superType : ti.getAllSuperTypes()) {
-      JCasClassInfo jci = type2JCas.get(superType.getName());
+      JCasClassInfo jci = type2jcci.get(superType.getName());
       if (jci == null) continue;
       Class<?> superClass = clazz.getSuperclass();
       superClasses.add(superClass);
@@ -767,18 +733,21 @@ public abstract class FSClassRegistry {
            * having a different non-compatible type system for this class, trying to use a common JCas cover class, which is not supported. */
           add2errors(errorSet, 
                      new CASRuntimeException(CASRuntimeException.JCAS_FIELD_ADJ_OFFSET_CHANGED,
-                        clazz.getName(), fi.getName(), staticOffsetInClass, fi.getAdjustedOffset()),
+                        clazz.getName(), 
+                        fi.getName(), 
+                        Integer.valueOf(staticOffsetInClass), 
+                        Integer.valueOf(fi.getAdjustedOffset())),
                      staticOffsetInClass != -1);  // throw unless static offset is -1, in that case, a runtime error will occur if it is usedd
         }
       }
     }
   }
   
-  static void add2errors(ThreadLocal<List<ErrorReport>> errors, Exception e) {
+  private static void add2errors(ThreadLocal<List<ErrorReport>> errors, Exception e) {
     add2errors(errors, e, true);
   }
   
-  static void add2errors(ThreadLocal<List<ErrorReport>> errors, Exception e, boolean doThrow) {
+  private static void add2errors(ThreadLocal<List<ErrorReport>> errors, Exception e, boolean doThrow) {
     List<ErrorReport> es = errors.get();
     if (es == null) {
       es = new ArrayList<ErrorReport>();
@@ -787,7 +756,7 @@ public abstract class FSClassRegistry {
     es.add(new ErrorReport(e, doThrow));
   }
   
-  static void reportErrors() {
+  private static void reportErrors() {
     boolean throwWhenDone = false;
     List<ErrorReport> es = errorSet.get();
     if (es != null) {
@@ -811,5 +780,53 @@ public abstract class FSClassRegistry {
       }
     }
   }  
+  
+  /**
+   * called infrequently to set up cache
+   * Only called when a type system has not had generators for a particular class loader.
+   * 
+   * For PEAR generators: 
+   *   Populates only for those classes the PEAR has overriding implementations
+   *     - other entries are null; this serves as a boolean indicator that no pear override exists for that type
+   *       and therefore no trampoline is needed
+   * 
+   * @param cl identifies which set of jcas cover classes
+   * @param isPear true for pear case
+   * @param tsi the type system being used
+   * @return the generators for that set, as an array indexed by type code
+   */
+  static FsGenerator[] getGeneratorsForClassLoader(ClassLoader cl, boolean isPear, TypeSystemImpl tsi) {
+    synchronized(cl2type2JCas) {
+      // This is the first time this class loader is being used - load the classes for this type system, or
+      // This is the first time this class loader is being used with this particular type system
+      loadAtTypeSystemCommitTime(tsi, true, cl);      
+
+      FsGenerator[] r = new FsGenerator[tsi.getTypeArraySize()];
+                          
+      Map<String, JCasClassInfo> t2jcci = cl2type2JCas.get(cl);
+      // can't use values alone because many types have the same value (due to copy-down)
+      for (Entry<String, JCasClassInfo> e : t2jcci.entrySet()) {
+        TypeImpl ti = tsi.getType(Misc.javaClassName2UimaTypeName(e.getKey()));
+        if (null == ti) {
+          continue;  // JCas loaded some type in the past, but it's not in this type system
+        }
+        JCasClassInfo jcci = e.getValue();
+        
+        if (!isPear || jcci.isPearOverride(cl)) {
+          r[ti.getCode()] = (FsGenerator) jcci.generator;
+        }      
+      }
+      return r;
+    }   
+  }
+  
+  private static boolean isAllNull(FsGenerator[] r) {
+    for (FsGenerator v : r) {
+      if (v != null)
+        return false;
+    }
+    return true;
+  }
+
 }
   
\ No newline at end of file