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