| /* |
| * 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 <init> ()"); |
| 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<ValueDescriptor> fields; |
| private final List<AnnotationElement> annotationElements; |
| |
| public EventClassBuilder(List<AnnotationElement> annotationElements, List<ValueDescriptor> fields) { |
| this.fullClassName = "jdk.jfr.DynamicEvent" + idCounter.incrementAndGet(); |
| this.type = Type.getType(fullClassName.replace(".", "/")); |
| this.fields = fields; |
| this.annotationElements = annotationElements; |
| } |
| |
| public Class<? extends Event> 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. |
| } |
| } |
| } |