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