1 /*
   2  * Copyright (c) 2016, 2019, 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 
  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 
  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 
  76     private void endClass() {
  77         classWriter.visitEnd();
  78     }
  79 
  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 
 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 
 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 
 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 }