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.List;
29 import java.util.concurrent.atomic.AtomicLong;
30 
31 import jdk.internal.org.objectweb.asm.AnnotationVisitor;
32 import jdk.internal.org.objectweb.asm.ClassWriter;
33 import jdk.internal.org.objectweb.asm.Label;
34 import jdk.internal.org.objectweb.asm.MethodVisitor;
35 import jdk.internal.org.objectweb.asm.Opcodes;
36 import jdk.internal.org.objectweb.asm.Type;
37 import jdk.internal.org.objectweb.asm.commons.GeneratorAdapter;
38 import jdk.internal.org.objectweb.asm.commons.Method;
39 import jdk.jfr.AnnotationElement;
40 import jdk.jfr.Event;
41 import jdk.jfr.ValueDescriptor;
42 
43 
44 // Helper class for building dynamic events
45 public final class EventClassBuilder {
46 
47     private static final Type TYPE_EVENT = Type.getType(Event.class);
48     private static final Type TYPE_IOBE = Type.getType(IndexOutOfBoundsException.class);
49     private static final Method DEFAULT_CONSTRUCTOR = Method.getMethod("void <init> ()");
50     private static final Method SET_METHOD = Method.getMethod("void set (int, java.lang.Object)");
51     private static final AtomicLong idCounter = new AtomicLong();
52     private final ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
53     private final String fullClassName;
54     private final Type type;
55     private final List<ValueDescriptor> fields;
56     private final List<AnnotationElement> annotationElements;
57 
EventClassBuilder(List<AnnotationElement> annotationElements, List<ValueDescriptor> fields)58     public EventClassBuilder(List<AnnotationElement> annotationElements, List<ValueDescriptor> fields) {
59         this.fullClassName = "jdk.jfr.DynamicEvent" + idCounter.incrementAndGet();
60         this.type = Type.getType(fullClassName.replace(".", "/"));
61         this.fields = fields;
62         this.annotationElements = annotationElements;
63     }
64 
build()65     public Class<? extends Event> build() {
66         buildClassInfo();
67         buildConstructor();
68         buildFields();
69         buildSetMethod();
70         endClass();
71         byte[] bytes = classWriter.toByteArray();
72         ASMToolkit.logASM(fullClassName, bytes);
73         return SecuritySupport.defineClass(type.getInternalName(), bytes, Event.class.getClassLoader()).asSubclass(Event.class);
74     }
75 
endClass()76     private void endClass() {
77         classWriter.visitEnd();
78     }
79 
buildSetMethod()80     private void buildSetMethod() {
81         GeneratorAdapter ga = new GeneratorAdapter(Opcodes.ACC_PUBLIC, SET_METHOD, null, null, classWriter);
82         int index = 0;
83         for (ValueDescriptor v : fields) {
84             ga.loadArg(0);
85             ga.visitLdcInsn(index);
86             Label notEqual = new Label();
87             ga.ifICmp(GeneratorAdapter.NE, notEqual);
88             ga.loadThis();
89             ga.loadArg(1);
90             Type fieldType = ASMToolkit.toType(v);
91             ga.unbox(ASMToolkit.toType(v));
92             ga.putField(type, v.getName(), fieldType);
93             ga.visitInsn(Opcodes.RETURN);
94             ga.visitLabel(notEqual);
95             index++;
96         }
97         ga.throwException(TYPE_IOBE, "Index must between 0 and " + fields.size());
98         ga.endMethod();
99     }
100 
buildConstructor()101     private void buildConstructor() {
102         MethodVisitor mv = classWriter.visitMethod(Opcodes.ACC_PUBLIC, DEFAULT_CONSTRUCTOR.getName(), DEFAULT_CONSTRUCTOR.getDescriptor(), null, null);
103         mv.visitIntInsn(Opcodes.ALOAD, 0);
104         ASMToolkit.invokeSpecial(mv, TYPE_EVENT.getInternalName(), DEFAULT_CONSTRUCTOR);
105         mv.visitInsn(Opcodes.RETURN);
106         mv.visitMaxs(0, 0);
107     }
108 
buildClassInfo()109     private void buildClassInfo() {
110         String internalSuperName = ASMToolkit.getInternalName(Event.class.getName());
111         String internalClassName = type.getInternalName();
112         classWriter.visit(52, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER, internalClassName, null, internalSuperName, null);
113 
114         for (AnnotationElement a : annotationElements) {
115             String descriptor = ASMToolkit.getDescriptor(a.getTypeName());
116             AnnotationVisitor av = classWriter.visitAnnotation(descriptor, true);
117             for (ValueDescriptor v : a.getValueDescriptors()) {
118                 Object value = a.getValue(v.getName());
119                 String name = v.getName();
120                 if (v.isArray()) {
121                     AnnotationVisitor arrayVisitor = av.visitArray(name);
122                     Object[] array = (Object[]) value;
123                     for (int i = 0; i < array.length; i++) {
124                         arrayVisitor.visit(null, array[i]);
125                     }
126                     arrayVisitor.visitEnd();
127                 } else {
128                     av.visit(name, value);
129                 }
130             }
131             av.visitEnd();
132         }
133     }
134 
buildFields()135     private void buildFields() {
136         for (ValueDescriptor v : fields) {
137             String internal = ASMToolkit.getDescriptor(v.getTypeName());
138             classWriter.visitField(Opcodes.ACC_PRIVATE, v.getName(), internal, null, null);
139             // No need to store annotations on field since they will be replaced anyway.
140         }
141     }
142 }
143