src/jdk.jfr/share/classes/jdk/jfr/internal/EventClassBuilder.java
changeset 50113 caf115bb98ad
child 52015 821bfc24d750
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/EventClassBuilder.java	Tue May 15 20:24:34 2018 +0200
@@ -0,0 +1,142 @@
+/*
+ * 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.
+        }
+    }
+}