1 /*
2  * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package jdk.jfr.internal;
27 
28 import java.util.ArrayList;
29 import java.util.Collection;
30 import java.util.Collections;
31 import java.util.HashMap;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.Objects;
35 
36 import jdk.jfr.AnnotationElement;
37 import jdk.jfr.Event;
38 import jdk.jfr.SettingControl;
39 import jdk.jfr.ValueDescriptor;
40 
41 /**
42  * Internal data structure that describes a type,
43  *
44  * Used to create event types, value descriptor and annotations.
45  *
46  */
47 public class Type implements Comparable<Type> {
48     public static final String SUPER_TYPE_ANNOTATION = java.lang.annotation.Annotation.class.getName();
49     public static final String SUPER_TYPE_SETTING = SettingControl.class.getName();
50     public static final String SUPER_TYPE_EVENT = Event.class.getName();
51     public static final String EVENT_NAME_PREFIX = "jdk.";
52     public static final String TYPES_PREFIX = "jdk.types.";
53     public static final String SETTINGS_PREFIX = "jdk.settings.";
54 
55 
56     // Initialization of known types
57     private final static Map<Type, Class<?>> knownTypes = new HashMap<>();
58     static final Type BOOLEAN = register(boolean.class, new Type("boolean", null, 4));
59     static final Type CHAR = register(char.class, new Type("char", null, 5));
60     static final Type FLOAT = register(float.class, new Type("float", null, 6));
61     static final Type DOUBLE = register(double.class, new Type("double", null, 7));
62     static final Type BYTE = register(byte.class, new Type("byte", null, 8));
63     static final Type SHORT = register(short.class, new Type("short", null, 9));
64     static final Type INT = register(int.class, new Type("int", null, 10));
65     static final Type LONG = register(long.class, new Type("long", null, 11));
66     static final Type CLASS = register(Class.class, new Type("java.lang.Class", null, 20));
67     static final Type STRING = register(String.class, new Type("java.lang.String", null, 21));
68     static final Type THREAD = register(Thread.class, new Type("java.lang.Thread", null, 22));
69     static final Type STACK_TRACE = register(null, new Type(TYPES_PREFIX + "StackTrace", null, 23));
70 
71     private final AnnotationConstruct annos = new AnnotationConstruct();
72     private final String name;
73     private final String superType;
74     private final boolean constantPool;
75     private final long id;
76     private List<ValueDescriptor> fields = new ArrayList<>();
77     private Boolean simpleType; // calculated lazy
78     private boolean remove = true;
79     /**
80      * Creates a type
81      *
82      * @param javaTypeName i.e "java.lang.String"
83      * @param superType i.e "java.lang.Annotation"
84      * @param id the class id that represents the class in the JVM
85      *
86      */
Type(String javaTypeName, String superType, long typeId)87     public Type(String javaTypeName, String superType, long typeId) {
88         this(javaTypeName, superType, typeId, false);
89     }
90 
Type(String javaTypeName, String superType, long typeId, boolean contantPool)91     Type(String javaTypeName, String superType, long typeId, boolean contantPool) {
92         this(javaTypeName, superType, typeId, contantPool, null);
93     }
94 
Type(String javaTypeName, String superType, long typeId, boolean contantPool, Boolean simpleType)95     Type(String javaTypeName, String superType, long typeId, boolean contantPool, Boolean simpleType) {
96         Objects.requireNonNull(javaTypeName);
97 
98         if (!isValidJavaIdentifier(javaTypeName)) {
99             throw new IllegalArgumentException(javaTypeName + " is not a valid Java identifier");
100         }
101         this.constantPool = contantPool;
102         this.superType = superType;
103         this.name = javaTypeName;
104         this.id = typeId;
105         this.simpleType = simpleType;
106     }
107 
isDefinedByJVM(long id)108     static boolean isDefinedByJVM(long id) {
109         return id < JVM.RESERVED_CLASS_ID_LIMIT;
110     }
111 
getTypeId(Class<?> clazz)112     public static long getTypeId(Class<?> clazz) {
113         Type type = Type.getKnownType(clazz);
114         return type == null ? JVM.getJVM().getTypeId(clazz) : type.getId();
115     }
116 
getKnownTypes()117     static Collection<Type> getKnownTypes() {
118         return knownTypes.keySet();
119     }
120 
isValidJavaIdentifier(String identifier)121     public static boolean isValidJavaIdentifier(String identifier) {
122         if (identifier.isEmpty()) {
123             return false;
124         }
125         if (!Character.isJavaIdentifierStart(identifier.charAt(0))) {
126             return false;
127         }
128         for (int i = 1; i < identifier.length(); i++) {
129             char c = identifier.charAt(i);
130             if (c != '.') {
131                 if (!Character.isJavaIdentifierPart(c)) {
132                     return false;
133                 }
134             }
135         }
136         return true;
137     }
138 
isValidJavaFieldType(String name)139     public static boolean isValidJavaFieldType(String name) {
140         for (Map.Entry<Type, Class<?>> entry : knownTypes.entrySet()) {
141             Class<?> clazz = entry.getValue();
142             if (clazz != null && name.equals(clazz.getName())) {
143                 return true;
144             }
145         }
146         return false;
147     }
148 
getKnownType(String typeName)149     public static Type getKnownType(String typeName) {
150         for (Type type : knownTypes.keySet()) {
151             if (type.getName().equals(typeName)) {
152                 return type;
153             }
154         }
155         return null;
156     }
157 
isKnownType(Class<?> type)158     static boolean isKnownType(Class<?> type) {
159         if (type.isPrimitive()) {
160             return true;
161         }
162         if (type.equals(Class.class) || type.equals(Thread.class) || type.equals(String.class)) {
163             return true;
164         }
165         return false;
166     }
167 
getKnownType(Class<?> clazz)168     public static Type getKnownType(Class<?> clazz) {
169         for (Map.Entry<Type, Class<?>> entry : knownTypes.entrySet()) {
170             if (clazz != null && clazz.equals(entry.getValue())) {
171                 return entry.getKey();
172             }
173         }
174         return null;
175     }
176 
getName()177     public String getName() {
178         return name;
179     }
180 
getLogName()181     public String getLogName() {
182        return getName() + "(" + getId() + ")";
183     }
184 
getFields()185     public List<ValueDescriptor> getFields() {
186         if (fields instanceof ArrayList) {
187             ((ArrayList<ValueDescriptor>) fields).trimToSize();
188             fields = Collections.unmodifiableList(fields);
189         }
190         return fields;
191     }
192 
isSimpleType()193     public boolean isSimpleType() {
194         if (simpleType == null) {
195             simpleType = calculateSimpleType();
196         }
197         return simpleType.booleanValue();
198     }
199 
calculateSimpleType()200     private boolean calculateSimpleType() {
201         if (fields.size() != 1) {
202             return false;
203         }
204         // annotation, settings and event can never be simple types
205         return superType == null;
206     }
207 
isDefinedByJVM()208     public boolean isDefinedByJVM() {
209         return id < JVM.RESERVED_CLASS_ID_LIMIT;
210     }
211 
register(Class<?> clazz, Type type)212     private static Type register(Class<?> clazz, Type type) {
213         knownTypes.put(type, clazz);
214         return type;
215     }
216 
add(ValueDescriptor valueDescriptor)217     public void add(ValueDescriptor valueDescriptor) {
218         Objects.requireNonNull(valueDescriptor);
219         fields.add(valueDescriptor);
220     }
221 
trimFields()222     void trimFields() {
223         getFields();
224     }
225 
setAnnotations(List<AnnotationElement> annotations)226     void setAnnotations(List<AnnotationElement> annotations) {
227         annos.setAnnotationElements(annotations);
228     }
229 
getSuperType()230     public String getSuperType() {
231         return superType;
232     }
233 
getId()234     public long getId() {
235         return id;
236     }
237 
isConstantPool()238     public boolean isConstantPool() {
239         return constantPool;
240     }
241 
getLabel()242     public String getLabel() {
243         return annos.getLabel();
244     }
245 
getAnnotationElements()246     public List<AnnotationElement> getAnnotationElements() {
247         return annos.getUnmodifiableAnnotationElements();
248     }
249 
getAnnotation(Class<? extends java.lang.annotation.Annotation> clazz)250     public <T> T getAnnotation(Class<? extends java.lang.annotation.Annotation> clazz) {
251         return annos.getAnnotation(clazz);
252     }
253 
getDescription()254     public String getDescription() {
255         return annos.getDescription();
256     }
257 
258     @Override
hashCode()259     public int hashCode() {
260         return Long.hashCode(id);
261     }
262 
263     @Override
equals(Object object)264     public boolean equals(Object object) {
265         if (object instanceof Type) {
266             Type that = (Type) object;
267             return that.id == this.id;
268         }
269         return false;
270     }
271 
272     @Override
compareTo(Type that)273     public int compareTo(Type that) {
274         return Long.compare(this.id, that.id);
275     }
276 
log(String action, LogTag logTag, LogLevel level)277     void log(String action, LogTag logTag, LogLevel level) {
278         if (Logger.shouldLog(logTag, level) && !isSimpleType()) {
279             Logger.log(logTag, LogLevel.TRACE, action + " " + typeText() + " " + getLogName() + " {");
280             for (ValueDescriptor v : getFields()) {
281                 String array = v.isArray() ? "[]" : "";
282                 Logger.log(logTag, LogLevel.TRACE, "  " + v.getTypeName() + array + " " + v.getName() + ";");
283             }
284             Logger.log(logTag, LogLevel.TRACE, "}");
285         } else {
286             if (Logger.shouldLog(logTag, LogLevel.INFO) && !isSimpleType()) {
287                 Logger.log(logTag, LogLevel.INFO, action + " " + typeText() + " " + getLogName());
288             }
289         }
290     }
291 
typeText()292     private String typeText() {
293         if (this instanceof PlatformEventType) {
294             return "event type";
295         }
296         if (Type.SUPER_TYPE_SETTING.equals(superType)) {
297             return "setting type";
298         }
299         if (Type.SUPER_TYPE_ANNOTATION.equals(superType)) {
300             return "annotation type";
301         }
302         return "type";
303     }
304 
305     @Override
toString()306     public String toString() {
307         StringBuilder sb = new StringBuilder();
308         sb.append(getLogName());
309         if (!getFields().isEmpty()) {
310             sb.append(" {\n");
311             for (ValueDescriptor td : getFields()) {
312                 sb.append("  type=" + td.getTypeName() + "(" + td.getTypeId() + ") name=" + td.getName() + "\n");
313             }
314             sb.append("}\n");
315         }
316         return sb.toString();
317     }
318 
setRemove(boolean remove)319     public void setRemove(boolean remove) {
320        this.remove = remove;
321     }
322 
getRemove()323     public boolean getRemove() {
324         return remove;
325     }
326 }
327