/* * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package jdk.jfr.internal; import java.util.List; import java.util.concurrent.atomic.AtomicLong; import jdk.internal.org.objectweb.asm.AnnotationVisitor; import jdk.internal.org.objectweb.asm.ClassWriter; import jdk.internal.org.objectweb.asm.Label; import jdk.internal.org.objectweb.asm.MethodVisitor; import jdk.internal.org.objectweb.asm.Opcodes; import jdk.internal.org.objectweb.asm.Type; import jdk.internal.org.objectweb.asm.commons.GeneratorAdapter; import jdk.internal.org.objectweb.asm.commons.Method; import jdk.jfr.AnnotationElement; import jdk.jfr.Event; import jdk.jfr.ValueDescriptor; // Helper class for building dynamic events public final class EventClassBuilder { private static final Type TYPE_EVENT = Type.getType(Event.class); private static final Type TYPE_IOBE = Type.getType(IndexOutOfBoundsException.class); private static final Method DEFAULT_CONSTRUCTOR = Method.getMethod("void ()"); private static final Method SET_METHOD = Method.getMethod("void set (int, java.lang.Object)"); private static final AtomicLong idCounter = new AtomicLong(); private final ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); private final String fullClassName; private final Type type; private final List fields; private final List annotationElements; public EventClassBuilder(List annotationElements, List fields) { this.fullClassName = "jdk.jfr.DynamicEvent" + idCounter.incrementAndGet(); this.type = Type.getType(fullClassName.replace(".", "/")); this.fields = fields; this.annotationElements = annotationElements; } public Class build() { buildClassInfo(); buildConstructor(); buildFields(); buildSetMethod(); endClass(); byte[] bytes = classWriter.toByteArray(); ASMToolkit.logASM(fullClassName, bytes); return SecuritySupport.defineClass(type.getInternalName(), bytes, Event.class.getClassLoader()).asSubclass(Event.class); } private void endClass() { classWriter.visitEnd(); } private void buildSetMethod() { GeneratorAdapter ga = new GeneratorAdapter(Opcodes.ACC_PUBLIC, SET_METHOD, null, null, classWriter); int index = 0; for (ValueDescriptor v : fields) { ga.loadArg(0); ga.visitLdcInsn(index); Label notEqual = new Label(); ga.ifICmp(GeneratorAdapter.NE, notEqual); ga.loadThis(); ga.loadArg(1); Type fieldType = ASMToolkit.toType(v); ga.unbox(ASMToolkit.toType(v)); ga.putField(type, v.getName(), fieldType); ga.visitInsn(Opcodes.RETURN); ga.visitLabel(notEqual); index++; } ga.throwException(TYPE_IOBE, "Index must between 0 and " + fields.size()); ga.endMethod(); } private void buildConstructor() { MethodVisitor mv = classWriter.visitMethod(Opcodes.ACC_PUBLIC, DEFAULT_CONSTRUCTOR.getName(), DEFAULT_CONSTRUCTOR.getDescriptor(), null, null); mv.visitIntInsn(Opcodes.ALOAD, 0); ASMToolkit.invokeSpecial(mv, TYPE_EVENT.getInternalName(), DEFAULT_CONSTRUCTOR); mv.visitInsn(Opcodes.RETURN); mv.visitMaxs(0, 0); } private void buildClassInfo() { String internalSuperName = ASMToolkit.getInternalName(Event.class.getName()); String internalClassName = type.getInternalName(); classWriter.visit(52, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER, internalClassName, null, internalSuperName, null); for (AnnotationElement a : annotationElements) { String descriptor = ASMToolkit.getDescriptor(a.getTypeName()); AnnotationVisitor av = classWriter.visitAnnotation(descriptor, true); for (ValueDescriptor v : a.getValueDescriptors()) { Object value = a.getValue(v.getName()); String name = v.getName(); if (v.isArray()) { AnnotationVisitor arrayVisitor = av.visitArray(name); Object[] array = (Object[]) value; for (int i = 0; i < array.length; i++) { arrayVisitor.visit(null, array[i]); } arrayVisitor.visitEnd(); } else { av.visit(name, value); } } av.visitEnd(); } } private void buildFields() { for (ValueDescriptor v : fields) { String internal = ASMToolkit.getDescriptor(v.getTypeName()); classWriter.visitField(Opcodes.ACC_PRIVATE, v.getName(), internal, null, null); // No need to store annotations on field since they will be replaced anyway. } } }