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