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 }