1 /*
   2  * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
   3  *
   4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   5  *
   6  * The contents of this file are subject to the terms of either the Universal Permissive License
   7  * v 1.0 as shown at http://oss.oracle.com/licenses/upl
   8  *
   9  * or the following license:
  10  *
  11  * Redistribution and use in source and binary forms, with or without modification, are permitted
  12  * provided that the following conditions are met:
  13  *
  14  * 1. Redistributions of source code must retain the above copyright notice, this list of conditions
  15  * and the following disclaimer.
  16  *
  17  * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
  18  * conditions and the following disclaimer in the documentation and/or other materials provided with
  19  * the distribution.
  20  *
  21  * 3. Neither the name of the copyright holder nor the names of its contributors may be used to
  22  * endorse or promote products derived from this software without specific prior written permission.
  23  *
  24  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
  25  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  26  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
  27  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  29  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  30  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
  31  * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  32  */
  33 package org.openjdk.jmc.flightrecorder.internal.parser.v1;
  34 
  35 import java.io.IOException;
  36 import java.text.MessageFormat;
  37 import java.util.ArrayList;
  38 import java.util.HashMap;
  39 import java.util.Iterator;
  40 import java.util.List;
  41 import java.util.Map;
  42 
  43 import org.openjdk.jmc.common.collection.FastAccessNumberMap;
  44 import org.openjdk.jmc.common.unit.ContentType;
  45 import org.openjdk.jmc.common.unit.IUnit;
  46 import org.openjdk.jmc.common.unit.StructContentType;
  47 import org.openjdk.jmc.common.unit.UnitLookup;
  48 import org.openjdk.jmc.common.util.LabeledIdentifier;
  49 import org.openjdk.jmc.flightrecorder.internal.InvalidJfrFileException;
  50 import org.openjdk.jmc.flightrecorder.internal.parser.LoaderContext;
  51 import org.openjdk.jmc.flightrecorder.internal.parser.v1.ChunkMetadata.AnnotatedElement;
  52 import org.openjdk.jmc.flightrecorder.internal.parser.v1.ChunkMetadata.AnnotationElement;
  53 import org.openjdk.jmc.flightrecorder.internal.parser.v1.ChunkMetadata.ClassElement;
  54 import org.openjdk.jmc.flightrecorder.internal.parser.v1.ChunkMetadata.FieldElement;
  55 import org.openjdk.jmc.flightrecorder.internal.parser.v1.StructTypes.JfrFrame;
  56 import org.openjdk.jmc.flightrecorder.internal.parser.v1.StructTypes.JfrJavaClass;
  57 import org.openjdk.jmc.flightrecorder.internal.parser.v1.StructTypes.JfrJavaClassLoader;
  58 import org.openjdk.jmc.flightrecorder.internal.parser.v1.StructTypes.JfrJavaModule;
  59 import org.openjdk.jmc.flightrecorder.internal.parser.v1.StructTypes.JfrJavaPackage;
  60 import org.openjdk.jmc.flightrecorder.internal.parser.v1.StructTypes.JfrMethod;
  61 import org.openjdk.jmc.flightrecorder.internal.parser.v1.StructTypes.JfrOldObject;
  62 import org.openjdk.jmc.flightrecorder.internal.parser.v1.StructTypes.JfrOldObjectArray;
  63 import org.openjdk.jmc.flightrecorder.internal.parser.v1.StructTypes.JfrOldObjectField;
  64 import org.openjdk.jmc.flightrecorder.internal.parser.v1.StructTypes.JfrOldObjectGcRoot;
  65 import org.openjdk.jmc.flightrecorder.internal.parser.v1.StructTypes.JfrStackTrace;
  66 import org.openjdk.jmc.flightrecorder.internal.parser.v1.StructTypes.JfrThread;
  67 import org.openjdk.jmc.flightrecorder.internal.parser.v1.StructTypes.JfrThreadGroup;
  68 import org.openjdk.jmc.flightrecorder.internal.parser.v1.ValueReaders.AbstractStructReader;
  69 import org.openjdk.jmc.flightrecorder.internal.parser.v1.ValueReaders.ArrayReader;
  70 import org.openjdk.jmc.flightrecorder.internal.parser.v1.ValueReaders.IValueReader;
  71 import org.openjdk.jmc.flightrecorder.internal.parser.v1.ValueReaders.PoolReader;
  72 import org.openjdk.jmc.flightrecorder.internal.parser.v1.ValueReaders.PrimitiveReader;
  73 import org.openjdk.jmc.flightrecorder.internal.parser.v1.ValueReaders.QuantityReader;
  74 import org.openjdk.jmc.flightrecorder.internal.parser.v1.ValueReaders.ReflectiveReader;
  75 import org.openjdk.jmc.flightrecorder.internal.parser.v1.ValueReaders.StringReader;
  76 import org.openjdk.jmc.flightrecorder.internal.parser.v1.ValueReaders.StructReader;
  77 import org.openjdk.jmc.flightrecorder.internal.parser.v1.ValueReaders.TicksTimestampReader;
  78 import org.openjdk.jmc.flightrecorder.internal.util.JfrInternalConstants;
  79 import org.openjdk.jmc.flightrecorder.messages.internal.Messages;
  80 import org.openjdk.jmc.flightrecorder.parser.IEventSink;
  81 import org.openjdk.jmc.flightrecorder.parser.ValueField;
  82 
  83 class TypeManager {
  84 
  85         private static class NopEventSink implements IEventSink {
  86                 @Override
  87                 public void addEvent(Object[] values) {
  88                 }
  89         }
  90 
  91         private static class SkipFieldsEventSink implements IEventSink {
  92                 private final IEventSink subSink;
  93                 private final List<Integer> skipFields;
  94                 private final Object[] reusableStruct;
  95 
  96                 SkipFieldsEventSink(IEventSink subSink, List<Integer> skipFields, int fieldCount) {
  97                         this.subSink = subSink;
  98                         this.skipFields = skipFields;
  99                         reusableStruct = new Object[fieldCount - skipFields.size()];
 100                 }
 101 
 102                 @Override
 103                 public void addEvent(Object[] fieldValues) {
 104                         Iterator<Integer> skipIter = skipFields.iterator();
 105                         int skipNext = skipIter.next();
 106                         int j = 0;
 107                         for (int i = 0; i < fieldValues.length; i++) {
 108                                 if (i != skipNext) {
 109                                         reusableStruct[j++] = fieldValues[i];
 110                                 } else if (skipIter.hasNext()) {
 111                                         skipNext = skipIter.next();
 112                                 }
 113                         }
 114                         subSink.addEvent(reusableStruct);
 115                 }
 116         }
 117 
 118         // NOTE: Using constant pool id as identifier.
 119         private static final Map<Long, StructContentType<Object[]>> STRUCT_TYPES = new HashMap<>();
 120 
 121         private class TypeEntry {
 122                 private static final String STRUCT_TYPE_CLASS = "java.lang.Class"; //$NON-NLS-1$
 123                 private static final String STRUCT_TYPE_THREAD = "java.lang.Thread"; //$NON-NLS-1$
 124                 private static final String STRUCT_TYPE_STACK_TRACE = "com.oracle.jfr.types.StackTrace"; //$NON-NLS-1$
 125                 private static final String STRUCT_TYPE_STACK_TRACE_2 = "jdk.types.StackTrace"; //$NON-NLS-1$
 126                 private static final String STRUCT_TYPE_STACK_FRAME = "com.oracle.jfr.types.StackFrame"; //$NON-NLS-1$
 127                 private static final String STRUCT_TYPE_STACK_FRAME_2 = "jdk.types.StackFrame"; //$NON-NLS-1$
 128                 private static final String STRUCT_TYPE_METHOD = "com.oracle.jfr.types.Method"; //$NON-NLS-1$
 129                 private static final String STRUCT_TYPE_METHOD_2 = "jdk.types.Method"; //$NON-NLS-1$
 130                 private static final String STRUCT_TYPE_CLASS_LOADER = "com.oracle.jfr.types.ClassLoader"; //$NON-NLS-1$
 131                 private static final String STRUCT_TYPE_CLASS_LOADER_2 = "jdk.types.ClassLoader"; //$NON-NLS-1$
 132                 private static final String STRUCT_TYPE_MODULE = "com.oracle.jfr.types.Module"; //$NON-NLS-1$
 133                 private static final String STRUCT_TYPE_MODULE_2 = "jdk.types.Module"; //$NON-NLS-1$
 134                 private static final String STRUCT_TYPE_PACKAGE = "com.oracle.jfr.types.Package"; //$NON-NLS-1$
 135                 private static final String STRUCT_TYPE_PACKAGE_2 = "jdk.types.Package"; //$NON-NLS-1$
 136                 private static final String STRUCT_TYPE_OLD_OBJECT = "com.oracle.jfr.types.OldObject"; //$NON-NLS-1$
 137                 private static final String STRUCT_TYPE_OLD_OBJECT_2 = "jdk.types.OldObject"; //$NON-NLS-1$
 138                 private static final String STRUCT_TYPE_OLD_OBJECT_ARRAY = "com.oracle.jfr.types.OldObjectArray"; //$NON-NLS-1$
 139                 private static final String STRUCT_TYPE_OLD_OBJECT_ARRAY_2 = "jdk.types.OldObjectArray"; //$NON-NLS-1$
 140                 private static final String STRUCT_TYPE_OLD_OBJECT_FIELD = "com.oracle.jfr.types.OldObjectField"; //$NON-NLS-1$
 141                 private static final String STRUCT_TYPE_OLD_OBJECT_FIELD_2 = "jdk.types.OldObjectField"; //$NON-NLS-1$
 142                 private static final String STRUCT_TYPE_OLD_OBJECT_GC_ROOT = "com.oracle.jfr.types.OldObjectGcRoot"; //$NON-NLS-1$
 143                 private static final String STRUCT_TYPE_OLD_OBJECT_GC_ROOT_2 = "jdk.types.OldObjectGcRoot"; //$NON-NLS-1$
 144                 private static final String STRUCT_TYPE_THREAD_GROUP = "com.oracle.jfr.types.ThreadGroup"; //$NON-NLS-1$
 145                 private static final String STRUCT_TYPE_THREAD_GROUP_2 = "jdk.types.ThreadGroup"; //$NON-NLS-1$
 146 
 147                 final ClassElement element;
 148                 final FastAccessNumberMap<Object> constants;
 149                 private IValueReader reader;
 150 
 151                 TypeEntry(ClassElement element) {
 152                         this(element, new FastAccessNumberMap<>());
 153                 }
 154 
 155                 /**
 156                  * Temporary constructor for sharing constants. Only used for Strings.
 157                  */
 158                 TypeEntry(ClassElement element, FastAccessNumberMap<Object> constants) {
 159                         this.element = element;
 160                         this.constants = constants;
 161                 }
 162 
 163                 public IValueReader getReader() throws InvalidJfrFileException {
 164                         if (reader == null) {
 165                                 int fieldCount = element.getFieldCount();
 166                                 if (element.isSimpleType() && fieldCount == 1) {
 167                                         FieldElement singleField = element.fields.get(0);
 168                                         if (singleField.classId == element.classId) {
 169                                                 throw new InvalidJfrFileException(
 170                                                                 element.typeIdentifier + " is a simple type referring to itself"); //$NON-NLS-1$
 171                                         } else {
 172                                                 reader = createFieldReader(element.fields.get(0), null);
 173                                         }
 174                                 } else if (fieldCount == 0 && element.superType == null) {
 175                                         if (StringReader.STRING.equals(element.typeIdentifier)) {
 176                                                 reader = new StringReader(constants);
 177                                         } else {
 178                                                 reader = new PrimitiveReader(element.typeIdentifier);
 179                                         }
 180                                 } else {
 181                                         AbstractStructReader typeReader = element.typeIdentifier.startsWith("jdk.") //$NON-NLS-1$
 182                                                         ? createStructReaderV2(element.typeIdentifier, element.label, element.description,
 183                                                                         fieldCount)
 184                                                         : createStructReaderV1(element.typeIdentifier, element.label, element.description,
 185                                                                         fieldCount);
 186                                         // assign before resolving field since it may be recursive
 187                                         reader = typeReader;
 188                                         for (int i = 0; i < fieldCount; i++) {
 189                                                 FieldElement fe = element.fields.get(i);
 190                                                 IValueReader reader = createFieldReader(fe, null);
 191                                                 String labelOrId = (fe.label == null) ? fe.fieldIdentifier : fe.label;
 192                                                 typeReader.addField(fe.fieldIdentifier, labelOrId, fe.description, reader);
 193                                         }
 194                                 }
 195                         }
 196                         return reader;
 197                 }
 198 
 199                 private AbstractStructReader createStructReaderV2(
 200                         String identifier, String name, String description, int fieldCount) {
 201                         switch (identifier) {
 202                         case STRUCT_TYPE_THREAD_GROUP_2:
 203                                 return new ReflectiveReader(JfrThreadGroup.class, fieldCount, UnitLookup.THREAD_GROUP);
 204                         case STRUCT_TYPE_CLASS_LOADER_2:
 205                                 return new ReflectiveReader(JfrJavaClassLoader.class, fieldCount, UnitLookup.CLASS_LOADER);
 206                         case STRUCT_TYPE_OLD_OBJECT_GC_ROOT_2:
 207                                 return new ReflectiveReader(JfrOldObjectGcRoot.class, fieldCount, UnitLookup.OLD_OBJECT_GC_ROOT);
 208                         case STRUCT_TYPE_OLD_OBJECT_2:
 209                                 return new ReflectiveReader(JfrOldObject.class, fieldCount, UnitLookup.OLD_OBJECT);
 210                         case STRUCT_TYPE_OLD_OBJECT_ARRAY_2:
 211                                 return new ReflectiveReader(JfrOldObjectArray.class, fieldCount, UnitLookup.OLD_OBJECT_ARRAY);
 212                         case STRUCT_TYPE_OLD_OBJECT_FIELD_2:
 213                                 return new ReflectiveReader(JfrOldObjectField.class, fieldCount, UnitLookup.OLD_OBJECT_FIELD);
 214                         case STRUCT_TYPE_METHOD_2:
 215                                 return new ReflectiveReader(JfrMethod.class, fieldCount, UnitLookup.METHOD);
 216                         case STRUCT_TYPE_STACK_FRAME_2:
 217                                 return new ReflectiveReader(JfrFrame.class, fieldCount, UnitLookup.STACKTRACE_FRAME);
 218                         case STRUCT_TYPE_STACK_TRACE_2:
 219                                 return new ReflectiveReader(JfrStackTrace.class, fieldCount, UnitLookup.STACKTRACE);
 220                         case STRUCT_TYPE_MODULE_2:
 221                                 return new ReflectiveReader(JfrJavaModule.class, fieldCount, UnitLookup.MODULE);
 222                         case STRUCT_TYPE_PACKAGE_2:
 223                                 return new ReflectiveReader(JfrJavaPackage.class, fieldCount, UnitLookup.PACKAGE);
 224                         default:
 225                                 synchronized (STRUCT_TYPES) {
 226                                         StructContentType<Object[]> structType = STRUCT_TYPES.get(element.classId);
 227                                         if (structType == null) {
 228                                                 structType = new StructContentType<>(element.typeIdentifier, element.label,
 229                                                                 element.description);
 230                                                 STRUCT_TYPES.put(element.classId, structType);
 231                                         }
 232                                         return new StructReader(structType, fieldCount);
 233                                 }
 234                         }
 235                 }
 236 
 237                 private AbstractStructReader createStructReaderV1(
 238                         String identifier, String name, String description, int fieldCount) {
 239                         switch (identifier) {
 240                         case STRUCT_TYPE_THREAD:
 241                                 return new ReflectiveReader(JfrThread.class, fieldCount, UnitLookup.THREAD);
 242                         case STRUCT_TYPE_THREAD_GROUP:
 243                                 return new ReflectiveReader(JfrThreadGroup.class, fieldCount, UnitLookup.THREAD_GROUP);
 244                         case STRUCT_TYPE_CLASS:
 245                                 return new ReflectiveReader(JfrJavaClass.class, fieldCount, UnitLookup.CLASS);
 246                         case STRUCT_TYPE_CLASS_LOADER:
 247                                 return new ReflectiveReader(JfrJavaClassLoader.class, fieldCount, UnitLookup.CLASS_LOADER);
 248                         case STRUCT_TYPE_OLD_OBJECT_GC_ROOT:
 249                                 return new ReflectiveReader(JfrOldObjectGcRoot.class, fieldCount, UnitLookup.OLD_OBJECT_GC_ROOT);
 250                         case STRUCT_TYPE_OLD_OBJECT:
 251                                 return new ReflectiveReader(JfrOldObject.class, fieldCount, UnitLookup.OLD_OBJECT);
 252                         case STRUCT_TYPE_OLD_OBJECT_ARRAY:
 253                                 return new ReflectiveReader(JfrOldObjectArray.class, fieldCount, UnitLookup.OLD_OBJECT_ARRAY);
 254                         case STRUCT_TYPE_OLD_OBJECT_FIELD:
 255                                 return new ReflectiveReader(JfrOldObjectField.class, fieldCount, UnitLookup.OLD_OBJECT_FIELD);
 256                         case STRUCT_TYPE_METHOD:
 257                                 return new ReflectiveReader(JfrMethod.class, fieldCount, UnitLookup.METHOD);
 258                         case STRUCT_TYPE_STACK_FRAME:
 259                                 return new ReflectiveReader(JfrFrame.class, fieldCount, UnitLookup.STACKTRACE_FRAME);
 260                         case STRUCT_TYPE_STACK_TRACE:
 261                                 return new ReflectiveReader(JfrStackTrace.class, fieldCount, UnitLookup.STACKTRACE);
 262                         case STRUCT_TYPE_MODULE:
 263                                 return new ReflectiveReader(JfrJavaModule.class, fieldCount, UnitLookup.MODULE);
 264                         case STRUCT_TYPE_PACKAGE:
 265                                 return new ReflectiveReader(JfrJavaPackage.class, fieldCount, UnitLookup.PACKAGE);
 266                         default:
 267                                 synchronized (STRUCT_TYPES) {
 268                                         StructContentType<Object[]> structType = STRUCT_TYPES.get(element.classId);
 269                                         if (structType == null) {
 270                                                 structType = new StructContentType<>(element.typeIdentifier, element.label,
 271                                                                 element.description);
 272                                                 STRUCT_TYPES.put(element.classId, structType);
 273                                         }
 274                                         return new StructReader(structType, fieldCount);
 275                                 }
 276                         }
 277                 }
 278 
 279                 void resolveConstants() throws InvalidJfrFileException {
 280                         IValueReader r = reader;
 281                         if (r != null) {
 282                                 for (Object c : constants) {
 283                                         r.resolve(c);
 284                                         // FIXME: During resolve, some constants may become equal. Should we ensure canonical constants?
 285                                 }
 286                         }
 287                 }
 288 
 289                 void readConstant(IDataInput input) throws InvalidJfrFileException, IOException {
 290                         // FIXME: Constant lookup can perhaps be optimized (across chunks)
 291                         long constantIndex = input.readLong();
 292                         Object value = constants.get(constantIndex);
 293                         if (value == null) {
 294                                 value = getReader().read(input, true);
 295                                 constants.put(constantIndex, value);
 296                         } else {
 297                                 getReader().skip(input);
 298                         }
 299                 }
 300         }
 301 
 302         private class EventTypeEntry {
 303                 private final ClassElement element;
 304                 private final List<IValueReader> valueReaders;
 305                 private Object[] reusableStruct;
 306                 private IEventSink eventSink;
 307                 private LabeledIdentifier eventType;
 308 
 309                 EventTypeEntry(ClassElement element) {
 310                         this.element = element;
 311                         valueReaders = new ArrayList<>(element.getFieldCount());
 312                 }
 313 
 314                 void readEvent(IDataInput input) throws InvalidJfrFileException, IOException {
 315                         for (int i = 0; i < valueReaders.size(); i++) {
 316                                 reusableStruct[i] = valueReaders.get(i).read(input, false);
 317                         }
 318                         eventSink.addEvent(reusableStruct);
 319                 }
 320 
 321                 LabeledIdentifier getValueType() {
 322                         if (eventType == null) {
 323                                 eventType = new LabeledIdentifier(element.typeIdentifier, element.classId, element.label,
 324                                                 element.description);
 325                         }
 326                         return eventType;
 327                 }
 328 
 329                 void init(LoaderContext context) throws InvalidJfrFileException, IOException {
 330                         if (context.hideExperimentals() && element.experimental) {
 331                                 eventSink = new NopEventSink();
 332                         } else {
 333                                 List<ValueField> fieldsList = new ArrayList<>();
 334                                 List<Integer> skipFields = new ArrayList<>();
 335                                 for (int i = 0; i < element.getFieldCount(); i++) {
 336                                         FieldElement fe = element.fields.get(i);
 337                                         String valueType = context.getValueInterpretation(element.typeIdentifier, fe.fieldIdentifier);
 338                                         IValueReader reader = createFieldReader(fe, valueType);
 339                                         String fieldLabel = buildLabel(fe.fieldIdentifier, fe);
 340                                         if (context.hideExperimentals() && fe.experimental) {
 341                                                 valueReaders.add(reader);
 342                                                 skipFields.add(i);
 343                                         } else if (reader instanceof StructReader) {
 344                                                 // Flattening of nested structs
 345                                                 ClassElement fieldType = getTypeEntry(fe.classId).element;
 346                                                 for (int j = 0; j < fieldType.getFieldCount(); j++) {
 347                                                         FieldElement nestedField = fieldType.fields.get(j);
 348                                                         String nestedId = fe.fieldIdentifier + ":" + nestedField.fieldIdentifier; //$NON-NLS-1$
 349                                                         String nestedValueType = context.getValueInterpretation(element.typeIdentifier, nestedId);
 350                                                         IValueReader nestedReader = createFieldReader(nestedField, nestedValueType);
 351                                                         valueReaders.add(nestedReader);
 352                                                         String nestedLabel = fieldLabel + " : " //$NON-NLS-1$
 353                                                                         + (nestedField.label == null ? nestedField.fieldIdentifier : nestedField.label);
 354                                                         fieldsList.add(new ValueField(nestedId, nestedLabel, nestedField.description,
 355                                                                         nestedReader.getContentType()));
 356                                                 }
 357                                         } else {
 358                                                 valueReaders.add(reader);
 359                                                 fieldsList.add(new ValueField(fe.fieldIdentifier, fieldLabel, fe.description,
 360                                                                 reader.getContentType()));
 361                                         }
 362                                 }
 363                                 String typeLabel = buildLabel(element.typeIdentifier, element);
 364                                 // FIXME: Consider making the category array into something else, like an event type metadata array?
 365                                 eventSink = context.getSinkFactory().create(element.typeIdentifier, typeLabel, element.category,
 366                                                 element.description, fieldsList);
 367                                 reusableStruct = new Object[valueReaders.size()];
 368                                 if (skipFields.size() > 0) {
 369                                         eventSink = new SkipFieldsEventSink(eventSink, skipFields, reusableStruct.length);
 370                                 }
 371                         }
 372                 }
 373         }
 374 
 375         private final FastAccessNumberMap<TypeEntry> otherTypes = new FastAccessNumberMap<>();
 376         private final FastAccessNumberMap<EventTypeEntry> eventTypes = new FastAccessNumberMap<>();
 377         private final ChunkStructure header;
 378 
 379         TypeManager(List<ClassElement> classList, LoaderContext context, ChunkStructure header)
 380                         throws InvalidJfrFileException, IOException {
 381                 this.header = header;
 382                 for (ClassElement ce : classList) {
 383                         if (ce.isEventType()) {
 384                                 eventTypes.put(ce.classId, new EventTypeEntry(ce));
 385                         } else {
 386                                 otherTypes.put(ce.classId, new TypeEntry(ce));
 387                         }
 388                 }
 389                 for (ClassElement ce : classList) {
 390                         resolveAnnotations(ce);
 391                         for (int i = 0; i < ce.getFieldCount(); i++) {
 392                                 resolveAnnotations(ce.fields.get(i));
 393                         }
 394                 }
 395 
 396                 for (EventTypeEntry ce : eventTypes) {
 397                         ce.init(context);
 398                 }
 399         }
 400 
 401         void readEvent(long typeId, IDataInput input) throws InvalidJfrFileException, IOException {
 402                 EventTypeEntry entry = eventTypes.get(typeId);
 403                 if (entry == null) {
 404                         throw new InvalidJfrFileException("Event type with id " + typeId + " was not declared"); //$NON-NLS-1$ //$NON-NLS-2$
 405                 }
 406                 entry.readEvent(input);
 407         }
 408 
 409         void readConstants(long typeId, IDataInput input, int constantCount) throws InvalidJfrFileException, IOException {
 410                 TypeEntry entry = getTypeEntry(typeId);
 411                 for (int j = 0; j < constantCount; j++) {
 412                         entry.readConstant(input);
 413                 }
 414         }
 415 
 416         void resolveConstants() throws InvalidJfrFileException {
 417                 for (TypeEntry classEntry : otherTypes) {
 418                         classEntry.resolveConstants();
 419                 }
 420         }
 421 
 422         private TypeEntry getTypeEntry(long typeId) throws InvalidJfrFileException {
 423                 TypeEntry entry = otherTypes.get(typeId);
 424                 if (entry == null) {
 425                         throw new InvalidJfrFileException("Class with id " + typeId + " was not declared"); //$NON-NLS-1$ //$NON-NLS-2$
 426                 }
 427                 return entry;
 428         }
 429 
 430         private void resolveAnnotations(AnnotatedElement ae) throws InvalidJfrFileException {
 431                 if (ae.annotations != null) {
 432                         for (AnnotationElement a : ae.annotations) {
 433                                 ClassElement annotationType = getTypeEntry(a.classId).element;
 434                                 ae.resolveAnnotation(annotationType.typeIdentifier, a.values);
 435                         }
 436                 }
 437         }
 438 
 439         private IValueReader createFieldReader(FieldElement f, String valueType) throws InvalidJfrFileException {
 440                 TypeEntry fieldType = getTypeEntry(f.classId);
 441                 String typeIdentifier = fieldType.element.typeIdentifier;
 442                 boolean isNumeric = PrimitiveReader.isNumeric(typeIdentifier);
 443                 IValueReader reader = fieldType.getReader();
 444                 if (f.ticksUnitKind == UnitLookup.TIMESPAN) {
 445                         reader = new QuantityReader(typeIdentifier, header.getTicksTimespanUnit(), f.unsigned);
 446                 } else if (f.ticksUnitKind == UnitLookup.TIMESTAMP) {
 447                         reader = new TicksTimestampReader(typeIdentifier, header, f.unsigned);
 448                 } else if (f.unit != null) {
 449                         reader = new QuantityReader(typeIdentifier, f.unit, f.unsigned);
 450                 } else if (isNumeric) {
 451                         if (JfrInternalConstants.TYPE_IDENTIFIER_VALUE_INTERPRETATION.equals(valueType)) {
 452                                 reader = new TypeIdentifierReader(typeIdentifier, f.unsigned);
 453                         } else {
 454                                 IUnit unit = UnitLookup.getUnitOrNull(valueType);
 455                                 /*
 456                                  * FIXME: Currently we convert all numbers to quantities. This might not be ideal,
 457                                  * for example for thread IDs. See multiple notes referring to this method in
 458                                  * StructTypes.
 459                                  */
 460                                 reader = new QuantityReader(typeIdentifier, unit == null ? UnitLookup.NUMBER_UNITY : unit, f.unsigned);
 461                         }
 462                 }
 463                 if (f.isStoredInPool()) {
 464                         if (isNumeric) {
 465                                 throw new InvalidJfrFileException("Numerics should not be put in constant pools"); //$NON-NLS-1$
 466                         }
 467                         reader = new PoolReader(fieldType.constants, reader.getContentType());
 468                 }
 469                 return f.isArray() ? new ArrayReader(reader) : reader;
 470         }
 471 
 472         private static String buildLabel(String id, AnnotatedElement element) {
 473                 String labelOrId = element.label == null ? id : element.label;
 474                 return element.experimental
 475                                 ? MessageFormat.format(Messages.getString(Messages.TypeManager_EXPERIMENTAL_TYPE), labelOrId)
 476                                 : labelOrId;
 477         }
 478 
 479         private class TypeIdentifierReader implements IValueReader {
 480                 private final String typeIdentifier;
 481                 private final boolean unsigned;
 482 
 483                 TypeIdentifierReader(String typeIdentifier, boolean unsigned) throws InvalidJfrFileException {
 484                         this.typeIdentifier = typeIdentifier;
 485                         this.unsigned = unsigned;
 486                 }
 487 
 488                 @Override
 489                 public Object read(IDataInput in, boolean allowUnresolvedReference)
 490                                 throws IOException, InvalidJfrFileException {
 491                         long typeId = PrimitiveReader.readLong(in, typeIdentifier, unsigned);
 492                         return eventTypes.get(typeId).getValueType();
 493                 }
 494 
 495                 @Override
 496                 public Object resolve(Object value) throws InvalidJfrFileException {
 497                         return value;
 498                 }
 499 
 500                 @Override
 501                 public void skip(IDataInput in) throws IOException, InvalidJfrFileException {
 502                         PrimitiveReader.readLong(in, typeIdentifier, unsigned);
 503                 }
 504 
 505                 @Override
 506                 public ContentType<?> getContentType() {
 507                         return UnitLookup.LABELED_IDENTIFIER;
 508                 }
 509         }
 510 }