/* * Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of Business Objects nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * ClassInstance.java * Created: April 5, 2001 * By: Bo Ilic */ package org.openquark.cal.compiler; import java.io.IOException; import java.util.HashSet; import java.util.Set; import java.util.SortedSet; import java.util.List; import java.util.ArrayList; import org.openquark.cal.internal.serialization.ModuleSerializationTags; import org.openquark.cal.internal.serialization.RecordInputStream; import org.openquark.cal.internal.serialization.RecordOutputStream; import org.openquark.cal.internal.serialization.RecordInputStream.RecordHeaderInfo; /** * Models a class instance definition. * <p> * There are 2 kinds of class instances: * <p> * <ol> * <li> type constructor instances -the instance type has a type constructor root * e.g. * <ul> * <li> instance Num Int * <li> instance Eq a => Eq (List a) * <li> instance (Ord a, Ord b) => Ord (Tuple2 a b) * <li> instance Enum Ordering * </ul> * <p> * <li> universal record instances - the instance type is a record-polymorphic record. * It is "universal" because the instance definition applies to *all* records whose fields satisfy the specified * constraints i.e. it does not make any restrictions on the allowable field names. For example, * instance Outputable a => Outputable {a} * </ol> * <p> * @author Bo Ilic */ public final class ClassInstance { private static final int serializationSchema = 0; /** the module in which this class instance is defined. */ private final ModuleName moduleName; /** * The type of the instance e.g. if Int is an instance of Num, then this is Int. This is * a type (rather than just a string such as "Int") because of constrained instances. * For example, "instance Eq a => [a]" and "instance (Ord a, Ord b) => Ord (a, b)" to * define Eq on lists, and Ord for pairs. */ private final TypeExpr instanceType; /** the TypeClass object that this instance implements */ private final TypeClass typeClass; /** the style of the instance - indicates how it was created*/ private final InstanceStyle instanceStyle; private transient ClassInstanceIdentifier identifier; /** * the (QualifiedName) list of names of the concrete methods that implement the class methods. * Note, these must follow the same ordering as TypeClass.classMethodsList. * For example, in the case of Int as an instance of Num, this would be * ["addInt", "subtractInt", "multiplyInt", "divideInt"] * Note that an entry in this array will be null if no explicit instance method was supplied in the instance declaration * and we default to the default class method. */ private final QualifiedName[] instanceMethods; /** The CALDoc comment for this instance, or null if there is none. */ private CALDocComment calDocComment; /** The CALDoc comments for the instance methods. Each element can be null if the method has no corresponding CALDoc. */ private final CALDocComment[] methodCALDocComments; /** * The prefix used for defining dictionary functions such as $dictEq#Int. */ static final String DICTIONARY_FUNCTION_PREFIX = "$dict"; /** * This is an Enumeration that defines the different styles of instances * @author Magnus Byne */ final public static class InstanceStyle { private final String name; private final int key; private InstanceStyle(String name, int key) { this.name = name; this.key = key; } @Override public String toString() { return name; } private static final int explicitKey = 1; private static final int derivingKey = 2; private static final int internalKey = 3; /** instances that are defined explicitly in full in source. This does * not include instances that are created via a deriving clause*/ public static final InstanceStyle EXPLICIT = new InstanceStyle("Explicit", explicitKey); /** instances that are created via a deriving clause*/ public static final InstanceStyle DERIVING = new InstanceStyle("Deriving", derivingKey); /** instances that are created by the compiler. This includes most typeable instances, * which are typically created automatically be the compiler.*/ public static final InstanceStyle INTERNAL = new InstanceStyle("Internal", internalKey); /** * @return this is true if the instance has been defined implicitly, * e.g. through a deriving clause, or by the compiler. */ public final boolean isImplicit() { return this != EXPLICIT; } /** serialize the style*/ void write(RecordOutputStream s) throws IOException { s.writeIntCompressed(key); } /** un-serialize the style*/ static InstanceStyle load(RecordInputStream s, CompilerMessageLogger msgLogger) throws IOException { int key = s.readIntCompressed(); switch (key) { case explicitKey: return EXPLICIT; case derivingKey: return DERIVING; case internalKey: return INTERNAL; default: throw new IOException("Unknown " + InstanceStyle.class.getName() + " key encountered: " + key); } } } /** * ClassInstance constructor. * Creation date: (4/5/01 12:25:25 PM) * @param moduleName the module in which this class instance is defined * @param instanceType the type of the instance e.g. Int * @param typeClass the class that this is an instance of e.g. Ord * @param instanceMethods instance methods, in order of the type class methods. Can be null if they will be set later, * or if they should be automatically determined for instanceStyle instances. * @param instanceStyle whether the class instance is internally defined e.g. most Typeable instances and derived * instances (versus defined in source via an explicit instance declaration). */ ClassInstance(ModuleName moduleName, TypeExpr instanceType, TypeClass typeClass, QualifiedName[] instanceMethods, InstanceStyle instanceStyle) { if (moduleName == null || instanceType == null || typeClass == null) { throw new NullPointerException(); } final int nClassMethods = typeClass.getNClassMethods(); if (instanceType instanceof TypeConsApp) { //do a quick check to verify that all the arguments of the type constructor //are uninstantiated type variables. TypeConsApp instanceTypeConsApp = (TypeConsApp)instanceType; for (int i = 0, nVars = instanceTypeConsApp.getNArgs(); i < nVars; ++i) { TypeVar typeVar = (TypeVar)instanceTypeConsApp.getArg(i); if (typeVar.getInstance() != null) { throw new IllegalStateException(); } } if ( (instanceStyle.isImplicit()) && instanceMethods == null) { //the names of the instance methods of implicitly defined instance are determined from the //class method name and instance type constructor (provided they haven't been pre-supplied via //a non-null instanceMethods argument. instanceMethods = new QualifiedName[nClassMethods]; //if this is a built-in Typeable instance, then there is a single class method whose name is derived from the TypeConsApp name //For example, for Either, it will be $typeOfEither. QualifiedName typeConsName = instanceTypeConsApp.getName(); for (int i = 0; i < nClassMethods; ++i) { ClassMethod classMethod = typeClass.getNthClassMethod(i); instanceMethods[i] = ClassInstance.makeInternalInstanceMethodName(classMethod.getName().getUnqualifiedName(), typeConsName); } } } else if (instanceType instanceof RecordType) { if ((instanceStyle.isImplicit())) { throw new IllegalArgumentException("there are no implicitly defined record instances."); } //this is OK as well. } else { throw new IllegalArgumentException("instances must be based on type constructors or records."); } this.moduleName = moduleName; this.instanceType = instanceType; this.typeClass = typeClass; this.instanceStyle = instanceStyle; if (instanceMethods == null) { //initialize, but do not populate. instanceMethods will be populated by addInstanceMethod calls below. this.instanceMethods = new QualifiedName[nClassMethods]; } else { this.instanceMethods = instanceMethods; } this.identifier = ClassInstanceIdentifier.make(this); this.calDocComment = null; this.methodCALDocComments = new CALDocComment[this.instanceMethods.length]; } /** * The name of the instance method for an internally defined class instance. * * @param classMethodName for example "equals" or "typeOf" * @param typeConsName for example, "Prelude.Either" or "LegacyTuple.Tuple2" * @return for example, "Prelude.$equals$Either" or "LegacyTuple.$typeOf$Tuple2" */ static QualifiedName makeInternalInstanceMethodName(String classMethodName, QualifiedName typeConsName) { String instanceMethodName = new StringBuilder("$") .append(classMethodName) .append('$') .append(typeConsName.getUnqualifiedName()).toString(); return QualifiedName.make(typeConsName.getModuleName(), instanceMethodName); } /** * @return true if the instance type is a record-polymorphic record type, such as e.g. "instance Eq r => {r}". */ public final boolean isUniversalRecordInstance () { return instanceType instanceof RecordType; } /** * @return the style of this instance, e.g. internal, deriving, explicit */ public final InstanceStyle getInstanceStyle () { return instanceStyle; } /** * @return true if the instance type has a type constructor root, such as e.g. "instance Ord Int", "instance Eq a => Eq (List a)" */ public final boolean isTypeConstructorInstance () { return instanceType instanceof TypeConsApp; } /** * @return TypeClass the type class that this instance is an instance of. e.g. if Ord Int, then it is the Ord type class. */ public final TypeClass getTypeClass() { return typeClass; } /** * Add the name of an implementing method to the instance. * * @param classMethod the class method that this instance method will implement * @param instanceMethodName */ void addInstanceMethod(ClassMethod classMethod, QualifiedName instanceMethodName) { final int ordinal = classMethod.getOrdinal(); if (instanceMethods[ordinal] != null) { throw new IllegalStateException("attempt to re-initialize an instance method."); } instanceMethods[ordinal] = instanceMethodName; } /** * For example, if this is the Eq-Int instance, and the class method name is equals, * this returns equalsInt. * @param classMethodName * @return QualifiedName instance method name corresponding to classMethodName, or null if none such exists. */ QualifiedName getInstanceMethod(QualifiedName classMethodName) { if (!classMethodName.getModuleName().equals(typeClass.getName().getModuleName())) { return null; } int methodN = typeClass.getClassMethodIndex(classMethodName.getUnqualifiedName()); if (methodN == -1) { return null; } return getInstanceMethod(methodN); } /** * Get the name of the implementing instance method. * * @return QualifiedName * @param methodN the number of the method in the list of class methods for the type class. * Will be null the instance does not define the method. That is, the intent is to use * the default class method. */ public final QualifiedName getInstanceMethod(int methodN) { return instanceMethods[methodN]; } /** * Returns the type of the specified instance method. For example, the add method in the instance Num Int * would have a type of Int -> Int -> Int. * @param methodN the index of the method in the list of class methods for the type class * @return type type of the specified instance method. */ public final TypeExpr getInstanceMethodType(int methodN) { ClassMethod classMethod = typeClass.getNthClassMethod(methodN); TypeExpr classMethodType = CopyEnv.freshType(classMethod.getTypeExpr(), null); TypeVar classTypeVar = classMethodType.getTypeClassTypeVar(typeClass.getTypeClassTypeVarName()); classTypeVar.setInstance(this.getType()); TypeExpr instanceMethodType = classMethodType; return instanceMethodType; } /** * Returns the type of the specified instance method. For example, the add method in the instance Num Int * would have a type of Int -> Int -> Int. * @param methodName the name of the instance method. * @return type type of the specified instance method. */ public final TypeExpr getInstanceMethodType(String methodName) { int methodN = typeClass.getClassMethodIndex(methodName); if (methodN == -1) { throw new IllegalArgumentException("There is no method named " + methodName + " in this instance."); } return getInstanceMethodType(methodN); } /** * @return int the number of instance methods. This is the same as the number of class methods of the correspoding type class. */ public final int getNInstanceMethods() { return instanceMethods.length; } /** * The type of the instance e.g. Int, Double, Eq a => [a]. * Creation date: (4/5/01 1:00:23 PM) * @return TypeExpr */ public final TypeExpr getType() { //make a copy so that clients don't accidentally mess up ClassInstance's own internal value. return CopyEnv.freshType(instanceType, null); } /** * @return the instance identifier, where the type class and type constructor entity name are * fully qualified and separated by a space e.g. "Cal.Core.Prelude.Num Cal.Core.Prelude.Int" */ public final ClassInstanceIdentifier getIdentifier() { return identifier; } /** * The internal name of the hidden dictionary function corresponding to this class instance. * For example, for a C-T instance defined in module M this is M.$dictC#T. * * Note the behavior in the case of single method root classes is to return the instance method name * (or the default class method name if there is no instance method defined). This is because the single * method root class case is optimized and there are no dictionary functions generated for it. The instance * (or class) method returned serves an analogous role. * * @return the internal name of the dictionary function for this instance. */ QualifiedName getDictionaryFunctionName() { //the dictionary function for an instance whose type class is a single class //method type classes with no parent classes can be defined as in the example: //$dictClass#Type = instanceFunction; //so we might as well inline the dictionary function, and just use instanceFunction. if (typeClass.internal_isSingleMethodRootClass()) { QualifiedName instanceMethodName = getInstanceMethod(0); if (instanceMethodName == null) { //use the default class method ClassMethod classMethod = typeClass.getNthClassMethod(0); return classMethod.getDefaultClassMethodName(); } return instanceMethodName; } return QualifiedName.make(moduleName, DICTIONARY_FUNCTION_PREFIX + getIdentifier().getInstanceIdentifier()); } /** * A canonical form of the name of the class instance, including the context. * This is essentially what follows the "instance" keyword in the CAL source for the instance declaration if fully * qualified names are used. * * <p>Some examples: * <ol> * <li> Cal.Core.Prelude.Ord Cal.Core.Prelude.Int * <li> (Cal.Core.Prelude.Eq a, Cal.Core.Prelude.Eq b) => Cal.Core.Prelude.Eq (Cal.Core.Prelude.Either a b) * <li> Cal.Core.Debug.Show a => Cal.Core.Debug.Show [a] * </ol> * * @return a canonical form of the name of the class instance, along with its context. */ public final String getNameWithContext() { return getNameWithContext(ScopedEntityNamingPolicy.FULLY_QUALIFIED); } /** * The name of the class instance, including the context, using the specified naming policy. * This is essentially what follows the "instance" keyword in the CAL source for the instance declaration, modified * according to the naming policy. * * <p>Some examples with the unqualified naming policy: * <ol> * <li> Ord Int * <li> (Eq a, Eq b) => Eq (Either a b) * <li> Show a => Show [a] * </ol> * * @param namingPolicy used for formatting type class and type constructor names within the class instance name and context * @return the name of the class instance, along with its context, using the specified naming policy. */ public final String getNameWithContext(ScopedEntityNamingPolicy namingPolicy) { return getNameWithContext(typeClass, instanceType, namingPolicy); } /** * @return a canonical form of the name of the class instance. */ public final String getName() { return getName(ScopedEntityNamingPolicy.FULLY_QUALIFIED); } /** * @param namingPolicy * @return the name of the class instance (e.g. "Eq (Either a b)") not including the class context, using the specified naming policy. */ public final String getName(ScopedEntityNamingPolicy namingPolicy) { return getNameHelper(typeClass, instanceType, namingPolicy, false); } /** * Sometimes a class instance for typeClass-instanceType is required, but none is supplied. In that case * we want to give an error message describing what instance is required. This method provides the * declaration name for such an instance. * @param typeClass * @param instanceType * @param namingPolicy * @return String */ static String getNameWithContext(TypeClass typeClass, TypeExpr instanceType, ScopedEntityNamingPolicy namingPolicy) { return getNameHelper(typeClass, instanceType, namingPolicy, true); } static private String getNameHelper( final TypeClass typeClass, final TypeExpr instanceType, final ScopedEntityNamingPolicy namingPolicy, final boolean includeContext) { final String className = typeClass.getAdaptedName(namingPolicy); final String typeName = instanceType.toString(namingPolicy); final int nArgs; if (instanceType instanceof TypeConsApp) { nArgs = ((TypeConsApp)instanceType).getNArgs(); } else { nArgs = 0; } //find if the type has a context final int index = typeName.indexOf(" => "); final String classInstanceName; if (index != -1) { final String rawTypeString = typeName.substring(index + 4); if (includeContext) { final String contextString = typeName.substring(0, index); classInstanceName = contextString + " => " + className + " " + parenthesize(rawTypeString, nArgs); } else { classInstanceName = className + " " + parenthesize(rawTypeString, nArgs); } } else { classInstanceName = className + " " + parenthesize(typeName, nArgs); } return classInstanceName; } private static String parenthesize (String typeName, int nArgs) { //don't print Eq ([a]) or Eq ((a, b)) but rather Eq [a] or Eq (a, b) //also don't parenthesize 0-arity constructors i.e. Eq Int not Eq (Int). if (Character.isLetter(typeName.charAt(0)) && nArgs > 0) { return "(" + typeName + ")"; } return typeName; } /** * @return the module in which this class instance is defined. */ public final ModuleName getModuleName() { return moduleName; } /** * @return the CALDoc comment for this instance, or null if there is none. */ public final CALDocComment getCALDocComment() { return calDocComment; } /** * Set the CALDoc comment associated with this instance. * @param comment the CALDoc comment for this instance, or null if there is none. */ final void setCALDocComment(CALDocComment comment) { calDocComment = comment; } /** * @param methodName the name of the method. * @return the CALDoc comment for the named method, or null if there is none. */ public final CALDocComment getMethodCALDocComment(String methodName) { int methodN = typeClass.getClassMethodIndex(methodName); if (methodN == -1) { throw new IllegalArgumentException("There is no method named " + methodName + " in this instance."); } return methodCALDocComments[methodN]; } /** * Set the CALDoc comment associated with the named method. * @param methodName the name of the method. * @param comment the CALDoc comment for the named method, or null if there is none. */ void setMethodCALDocComment(String methodName, CALDocComment comment) { int methodN = typeClass.getClassMethodIndex(methodName); if (methodN == -1) { throw new IllegalArgumentException("There is no method named " + methodName + " in this instance."); } methodCALDocComments[methodN] = comment; } /** * Textual description of the instance. This is very close to what Haskell source * for the instance declaration would be. * Creation date: (4/5/01 1:14:01 PM) * @return String */ @Override public String toString() { StringBuilder result = new StringBuilder("instance "); result.append(getNameWithContext()); result.append(" where\n"); int nClassMethods = typeClass.getNClassMethods(); int nInstanceMethods = getNInstanceMethods(); for (int i = 0; i < nClassMethods; ++i) { result.append(" ").append(typeClass.getNthClassMethod(i).getName()).append(" = "); if (i < nInstanceMethods) { result.append(getInstanceMethod(i)); } else { result.append("???"); } result.append(";\n"); } return result.toString(); } /** * Gets the constraints on the polymorphic variables of a class instance in a way suitable for processing. * For example, for the instance (Eq a, Ord c, Ord d, Enum a, Outputable d) => Ord (Foo a b c d), * this would return [{Eq, Enum}, {}, {Eq, Ord}, {Eq, Ord, Outputable}]. * * For example, for the instance (Eq a) => Eq {a} this would return [{Eq}]. * * The constraints include the implicit superclass constraints as well. * @return List<Set<TypeClass>> the ith element of the list is the set of all (including implicit superclass) TypeClass constraints * on the ith polymorphic var of the instance type. (T in C-T). */ List<Set<TypeClass>> getSuperclassPolymorphicVarConstraints() { if (isUniversalRecordInstance()) { List<Set<TypeClass>> varConstraints = new ArrayList<Set<TypeClass>>(1); Set<TypeClass> varConstraint = new HashSet<TypeClass>(((RecordType)instanceType).getPrunedRecordVar().getTypeClassConstraintSet()); varConstraints.add(varConstraint); return varConstraints; } //call getType() in order to work with a copy of the type TypeConsApp typeConsApp = (TypeConsApp)getType(); final int nVars = typeConsApp.getNArgs(); List<Set<TypeClass>> varConstraints = new ArrayList<Set<TypeClass>>(nVars); for (int i = 0; i < nVars; ++i) { TypeVar typeVar = (TypeVar)(typeConsApp.getArg(i)); varConstraints.add(typeVar.flattenTypeClassConstraintSet()); } return varConstraints; } /** * Gets the constraints on the polymorphic variables of a class instance in a way suitable for processing. * For example, for the instance (Eq a, Ord c, Ord d, Enum a, Outputable d) => Ord (Foo a b c d), * this would return [{Enum, Eq}, {}, {Ord}, {Outputable, Ord}]. * The ordering of the sets is alphabetical (by fully qualified type class name). * The constraints do not include implicit superclass constraints. * * For example, for the universal record instance (Eq a) => Eq {a} this would return [{Eq}]. * * @return SortedSet[] the ith element of the array is the set of all TypeClass constraints on the ith constrained * polymorphic var of the instance type. (T in C-T). */ List<SortedSet<TypeClass>> getDeclaredPolymorphicVarConstraints() { if (isUniversalRecordInstance()) { List<SortedSet<TypeClass>> varConstraints = new ArrayList<SortedSet<TypeClass>>(1); varConstraints.add(((RecordType)instanceType).getPrunedRecordVar().getTypeClassConstraintSet()); return varConstraints; } //call getType() in order to work with a copy of the type TypeConsApp typeConsApp = (TypeConsApp)getType(); final int nVars = typeConsApp.getNArgs(); List<SortedSet<TypeClass>> varConstraints = new ArrayList<SortedSet<TypeClass>>(nVars); for (int i = 0; i < nVars; ++i) { TypeVar typeVar = (TypeVar)(typeConsApp.getArg(i)); varConstraints.add(typeVar.getTypeClassConstraintSet()); } return varConstraints; } /** * Write this ClassInstance to the RecordOutputStream. * @param s * @throws IOException */ final void write (RecordOutputStream s) throws IOException { s.startRecord(ModuleSerializationTags.CLASS_INSTANCE, serializationSchema); s.writeModuleName(moduleName); instanceStyle.write(s); instanceType.write(s); s.writeQualifiedName(typeClass.getName()); s.writeShortCompressed(instanceMethods.length); for (int i = 0; i < instanceMethods.length; ++i) { s.writeQualifiedName(instanceMethods[i]); } // CALDoc comment boolean hasCALDocComment = (calDocComment != null); s.writeBoolean(hasCALDocComment); if (hasCALDocComment) { calDocComment.write(s); } // CALDoc comments for instance methods // 1) write out a bit vector describing which of the instance methods have associated CALDoc comments boolean[] hasMethodCALDocComment = new boolean[methodCALDocComments.length]; for (int i = 0; i < methodCALDocComments.length; i++) { hasMethodCALDocComment[i] = (methodCALDocComments[i] != null); } byte[] caldocFlags = RecordOutputStream.booleanArrayToBitArray(hasMethodCALDocComment); s.write(caldocFlags); // 2) write out the actual comments for (final CALDocComment methodCALDocComment : methodCALDocComments) { if (methodCALDocComment != null) { methodCALDocComment.write(s); } } s.endRecord(); } /** * Load an instance of ClassInstance from the RecordInputStream. * The read position will be before the record header. * @param s * @param mti * @param msgLogger the logger to which to log deserialization messages. * @return an instance of ClassInstance. * @throws IOException */ static final ClassInstance load (RecordInputStream s, ModuleTypeInfo mti, CompilerMessageLogger msgLogger) throws IOException { // Look for Record header. RecordHeaderInfo rhi = s.findRecord(ModuleSerializationTags.CLASS_INSTANCE); if (rhi == null) { throw new IOException("Unable to find ClassInstance record header."); } DeserializationHelper.checkSerializationSchema(rhi.getSchema(), serializationSchema, mti.getModuleName(), "ClassInstance", msgLogger); ModuleName moduleName = s.readModuleName(); InstanceStyle instanceStyle = InstanceStyle.load(s, msgLogger); TypeExpr instanceType = TypeExpr.load(s, mti, msgLogger); QualifiedName typeClassName = s.readQualifiedName(); TypeClass typeClass = mti.getReachableTypeClass(typeClassName); if (typeClass == null) { throw new IOException("Unable to resolve TypeClass " + typeClassName + " while loading ClassInstance."); } int nInstanceMethods = s.readShortCompressed(); QualifiedName instanceMethods[] = new QualifiedName[nInstanceMethods]; for (int i = 0; i < nInstanceMethods; ++i) { QualifiedName qn = s.readQualifiedName(); instanceMethods[i] = qn; } ClassInstance ci = new ClassInstance(moduleName, instanceType, typeClass, instanceMethods, instanceStyle); // CALDoc comment boolean hasCALDocComment = s.readBoolean(); if (hasCALDocComment) { ci.calDocComment = CALDocComment.load(s, mti.getModuleName(), msgLogger); } // CALDoc comments for instance methods // 1) read the bit vector describing which of the instance methods have associated CALDoc comments int nBytesForCALDocFlags = (nInstanceMethods + 7) / 8; byte[] caldocFlags = new byte[nBytesForCALDocFlags]; for (int i = 0; i < nBytesForCALDocFlags; i++) { caldocFlags[i] = s.readByte(); } // 2) read the actual comments for (int i = 0; i < nInstanceMethods; i++) { boolean hasMethodCALDocComment = RecordInputStream.booleanFromBitArray(caldocFlags, i); if (hasMethodCALDocComment) { ci.methodCALDocComments[i] = CALDocComment.load(s, mti.getModuleName(), msgLogger); } } s.skipRestOfRecord(); return ci; } }