1 /* 2 * Copyright (c) 2016, 2023, 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 static jdk.jfr.internal.util.Bytecode.invokespecial; 29 30 import java.lang.constant.ClassDesc; 31 import java.lang.constant.ConstantDescs; 32 import java.lang.reflect.AccessFlag; 33 import java.util.ArrayList; 34 import java.util.List; 35 import java.util.concurrent.atomic.AtomicLong; 36 37 import java.lang.classfile.AnnotationValue; 38 import java.lang.classfile.ClassBuilder; 39 import java.lang.classfile.ClassFile; 40 import java.lang.classfile.Label; 41 import java.lang.classfile.attribute.RuntimeVisibleAnnotationsAttribute; 42 import jdk.jfr.AnnotationElement; 43 import jdk.jfr.Event; 44 import jdk.jfr.ValueDescriptor; 45 import jdk.jfr.internal.util.Bytecode; 46 import jdk.jfr.internal.util.Bytecode.MethodDesc; 47 48 // Helper class for building dynamic events 49 public final class EventClassBuilder { 50 private static final ClassDesc TYPE_EVENT = Bytecode.classDesc(Event.class); 51 private static final ClassDesc TYPE_IOBE = Bytecode.classDesc(IndexOutOfBoundsException.class); 52 private static final MethodDesc DEFAULT_CONSTRUCTOR = MethodDesc.of("<init>", "()V"); 53 private static final MethodDesc SET_METHOD = MethodDesc.of("set", "(ILjava/lang/Object;)V"); 54 private static final AtomicLong idCounter = new AtomicLong(); 55 56 private final String fullClassName; 57 private final ClassDesc type; 58 private final List<ValueDescriptor> fields; 59 private final List<AnnotationElement> annotationElements; 60 61 public EventClassBuilder(List<AnnotationElement> annotationElements, List<ValueDescriptor> fields) { 62 this.fullClassName = "jdk.jfr.DynamicEvent" + idCounter.incrementAndGet(); 63 this.type = ClassDesc.of(fullClassName); 64 this.fields = fields; 65 this.annotationElements = annotationElements; 66 } 67 68 public Class<? extends Event> build() { 69 byte[] bytes = ClassFile.of().build(ClassDesc.of(fullClassName), cb -> build(cb)); 70 Bytecode.log(fullClassName, bytes); 71 return SecuritySupport.defineClass(Event.class, bytes).asSubclass(Event.class); 72 } 73 74 void build(ClassBuilder builder) { 75 buildClassInfo(builder); 76 buildConstructor(builder); 77 buildFields(builder); 78 buildSetMethod(builder); 79 } 80 81 private void buildSetMethod(ClassBuilder builder) { 82 // void Event::set(int index, Object value); 83 builder.withMethod(SET_METHOD.name(), SET_METHOD.descriptor(), ClassFile.ACC_PUBLIC, methodBuilder -> methodBuilder.withCode(codeBuilder -> { 84 int index = 0; 85 for (ValueDescriptor v : fields) { 86 codeBuilder.iload(1); 87 codeBuilder.loadConstant(index); 88 Label notEqual = codeBuilder.newLabel(); 89 codeBuilder.if_icmpne(notEqual); 90 codeBuilder.aload(0); // this 91 codeBuilder.aload(2); // value 92 ClassDesc cd = Bytecode.classDesc(v); 93 Bytecode.unbox(codeBuilder, cd); 94 codeBuilder.putfield(type, v.getName(), cd); 95 codeBuilder.return_(); 96 codeBuilder.labelBinding(notEqual); 97 index++; 98 } 99 Bytecode.throwException(codeBuilder, TYPE_IOBE, "Index must between 0 and " + fields.size()); 100 })); 101 } 102 103 private void buildConstructor(ClassBuilder builder) { 104 builder.withMethod(ConstantDescs.INIT_NAME, ConstantDescs.MTD_void, ClassFile.ACC_PUBLIC, methodBuilder -> methodBuilder.withCode(codeBuilder -> { 105 codeBuilder.aload(0); 106 invokespecial(codeBuilder, TYPE_EVENT, DEFAULT_CONSTRUCTOR); 107 codeBuilder.return_(); 108 })); 109 } 110 111 private void buildClassInfo(ClassBuilder builder) { 112 builder.withSuperclass(Bytecode.classDesc(Event.class)); 113 builder.withFlags(AccessFlag.FINAL, AccessFlag.PUBLIC, AccessFlag.SUPER); 114 List<java.lang.classfile.Annotation> annotations = new ArrayList<>(); 115 for (jdk.jfr.AnnotationElement a : annotationElements) { 116 List<java.lang.classfile.AnnotationElement> list = new ArrayList<>(); 117 for (ValueDescriptor v : a.getValueDescriptors()) { 118 // ValueDescriptor can only hold primitive 119 // No need to care about classes/enums 120 var value = a.getValue(v.getName()); 121 var av = AnnotationValue.of(value); 122 var ae = java.lang.classfile.AnnotationElement.of(v.getName(), av); 123 list.add(ae); 124 } 125 ClassDesc cd = ClassDesc.of(a.getTypeName()); 126 annotations.add(java.lang.classfile.Annotation.of(cd, list)); 127 } 128 builder.with(RuntimeVisibleAnnotationsAttribute.of(annotations)); 129 } 130 131 private void buildFields(ClassBuilder builder) { 132 for (ValueDescriptor v : fields) { 133 builder.withField(v.getName(), Bytecode.classDesc(v), ClassFile.ACC_PRIVATE); 134 // No need to store annotations on field since they will be replaced anyway. 135 } 136 } 137 }