All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.ebean.enhance.entity.ConstructorDeferredCode Maven / Gradle / Ivy

There is a newer version: 15.8.0
Show newest version
package io.ebean.enhance.entity;

import io.ebean.enhance.asm.Label;
import io.ebean.enhance.asm.MethodVisitor;
import io.ebean.enhance.asm.Opcodes;
import io.ebean.enhance.common.ClassMeta;

import java.util.ArrayList;
import java.util.List;

import static io.ebean.enhance.common.EnhanceConstants.INIT;
import static io.ebean.enhance.common.EnhanceConstants.NOARG_VOID;

/**
 * This is a class that 'defers' bytecode instructions in the default constructor initialisation
 * such that code that initialises persistent many properties (Lists, Sets and Maps) is removed.
 * 

* The purpose is to consume unwanted initialisation of Lists, Sets and Maps for OneToMany * and ManyToMany properties. *

*
 *
 *  mv.visitVarInsn(ALOAD, 0);
 *  mv.visitInsn(ICONST_0);         // (A) Optionally generated by Kotlin
 *  mv.visitVarInsn(ISTORE, 1);     // (A)
 *  mv.visitTypeInsn(NEW, "java/util/ArrayList");
 *  mv.visitInsn(DUP);
 *  mv.visitMethodInsn(INVOKESPECIAL, "java/util/ArrayList", "", "()V", false);
 *  mv.visitTypeInsn(CHECKCAST, "java/util/List"); // (B) Optionally generated by Kotlin
 *  Label label = new Label();      // (B)
 *  mv.visitLabel(label);           // (B)
 *  mv.visitLineNumber(__, label4); // (B)
 *  mv.visitFieldInsn(PUTFIELD, "test/model/WithInitialisedCollections", "contacts", "Ljava/util/List;");
 *
 * 
*/ final class ConstructorDeferredCode implements Opcodes { enum State { UNSET, ALOAD, KT_ICONST, // optional kotlin state NEW_COLLECTION, DUP, INVOKE_SPECIAL, KT_CHECKCAST, // optional kotlin state KT_LABEL, // optional kotlin state KT_EMPTYLIST } private static final ALoad ALOAD_INSTRUCTION = new ALoad(); private static final Dup DUP_INSTRUCTION = new Dup(); private static final Iconst0 ICONST0_INSTRUCTION = new Iconst0(); private final ClassMeta meta; private final MethodVisitor mv; private final List codes = new ArrayList<>(); private State state = State.UNSET; private String stateInitialiseType; ConstructorDeferredCode(ClassMeta meta, MethodVisitor mv) { this.meta = meta; this.mv = mv; } /** * Return true if this is an ALOAD 0 which we defer. */ boolean deferVisitVarInsn(int opcode, int var) { if (state == State.KT_ICONST && opcode == ISTORE) { codes.add(new Istore(var)); state = State.ALOAD; return true; } flush(); if (opcode == ALOAD && var == 0) { codes.add(ALOAD_INSTRUCTION); state = State.ALOAD; return true; } return false; } /** * Return true if we defer this based on it being a NEW or CHECKCAST on persistent many * and was proceeded by a deferred ALOAD (for NEW) or Collection init (for CHECKCAST). */ boolean deferVisitTypeInsn(int opcode, String type) { if (opcode == NEW && stateAload()) { // && isCollection(type)) { codes.add(new NewCollection(type)); state = State.NEW_COLLECTION; return true; } if (opcode == CHECKCAST && stateInvokeSpecial()) { codes.add(new CheckCastCollection(type)); state = State.KT_CHECKCAST; return true; } flush(); return false; } /** * Return true if we defer this based on it being a DUP and was proceeded * by a deferred ALOAD and NEW. */ boolean deferVisitInsn(int opcode) { if (opcode == ICONST_0 && stateAload()) { codes.add(ICONST0_INSTRUCTION); state = State.KT_ICONST; return true; } if (opcode == DUP && stateNewCollection()) { codes.add(DUP_INSTRUCTION); state = State.DUP; return true; } flush(); return false; } /** * Return true if we defer this based on it being an init of a collection * and was proceeded by a deferred DUP. */ boolean deferVisitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { if (opcode == INVOKESPECIAL && stateDup() && isNoArgInit(name, desc)) { codes.add(new NoArgInit(opcode, owner, name, desc, itf)); state = State.INVOKE_SPECIAL; stateInitialiseType = owner; return true; } if (opcode == INVOKESTATIC && stateAload() && kotlinEmptyList(owner, name, desc)) { codes.add(new NoArgInit(opcode, owner, name, desc, itf)); state = State.KT_EMPTYLIST; stateInitialiseType = "java/util/ArrayList"; return true; } flush(); return false; } private boolean isNoArgInit(String name, String desc) { return name.equals(INIT) && desc.equals(NOARG_VOID); } private boolean kotlinEmptyList(String owner, String name, String desc) { return owner.equals("kotlin/collections/CollectionsKt") && name.equals("emptyList") && desc.equals("()Ljava/util/List;"); } /** * Return true if we have consumed all the deferred code that initialises a persistent collection. */ boolean consumeVisitFieldInsn(int opcode, String owner, String name, String desc) { if (opcode == PUTFIELD) { if (stateConsumeDeferred()) { if (meta.isConsumeInitMany(name) && isConsumeManyType()) { if (meta.isLog(3)) { meta.log("... consumed init of many: " + name); } state = State.UNSET; codes.clear(); return true; } else if (meta.isInitTransient(name)) { // keep the initialisation code for transient to 'replay' // it when adding a default constructor if needed if (meta.isLog(3)) { meta.log("... init transient: " + name + " type: " + stateInitialiseType); } meta.addTransientInit(new CapturedInitCode(codes, opcode, owner, name, desc, stateInitialiseType)); } else if (meta.isTransient(name)) { meta.addUnsupportedTransientInit(name); } } else if (meta.isTransient(name)) { meta.addUnsupportedTransientInit(name); } } flush(); return false; } boolean consumeVisitLabel(Label label) { if (state == State.KT_CHECKCAST) { codes.add(new DeferredLabel(label)); state = State.KT_LABEL; return true; } return false; } public boolean consumeVisitLineNumber(int line, Label start) { if (state == State.KT_LABEL) { codes.add(new DeferredLineNumber(line, start)); state = State.INVOKE_SPECIAL; return true; } return false; } /** * Flush all deferred instructions. */ void flush() { state = State.UNSET; if (!codes.isEmpty()) { for (DeferredCode code : codes) { if (meta.isLog(4)) { meta.log("... flush deferred: " + code); } code.write(mv); } codes.clear(); } } private boolean stateAload() { return state == State.ALOAD; } private boolean stateNewCollection() { return state == State.NEW_COLLECTION; } private boolean stateDup() { return state == State.DUP; } private boolean stateInvokeSpecial() { return state == State.INVOKE_SPECIAL; } private boolean stateConsumeDeferred() { return state == State.INVOKE_SPECIAL || state == State.KT_CHECKCAST || state == State.KT_EMPTYLIST; } /** * Return true if the type being initialised is valid for auto initialisation of ToMany or DbArray. */ private boolean isConsumeManyType() { return ("java/util/ArrayList".equals(stateInitialiseType) || "java/util/LinkedHashSet".equals(stateInitialiseType) || "java/util/HashSet".equals(stateInitialiseType)); //|| "java/util/LinkedHashMap".equals(stateInitialiseType) //|| "java/util/HashMap".equals(stateInitialiseType)); } private static class ALoad implements DeferredCode { @Override public void write(MethodVisitor mv) { mv.visitVarInsn(ALOAD, 0); } } private static class DeferredLabel implements DeferredCode { private final Label label; DeferredLabel(Label label) { this.label = label; } @Override public void write(MethodVisitor mv) { mv.visitLabel(label); } } private static class DeferredLineNumber implements DeferredCode { private final int line; private final Label label; DeferredLineNumber(int line, Label label) { this.line = line; this.label = label; } @Override public void write(MethodVisitor mv) { mv.visitLineNumber(line, label); } } private static class Iconst0 implements DeferredCode { @Override public void write(MethodVisitor mv) { mv.visitInsn(ICONST_0); } } private static class Istore implements DeferredCode { private final int value; Istore(int value) { this.value = value; } @Override public void write(MethodVisitor mv) { mv.visitVarInsn(ISTORE, value); } } /** * DUP */ private static class Dup implements DeferredCode { @Override public void write(MethodVisitor mv) { mv.visitInsn(DUP); } } /** * Typically NEW java/util/ArrayList */ private static class NewCollection implements DeferredCode { final String type; NewCollection(String type) { this.type = type; } @Override public void write(MethodVisitor mv) { mv.visitTypeInsn(NEW, type); } } /** * Typically CHECKCAST java/util/List */ private static class CheckCastCollection implements DeferredCode { final String type; CheckCastCollection(String type) { this.type = type; } @Override public void write(MethodVisitor mv) { mv.visitTypeInsn(CHECKCAST, type); } } /** * Typically INVOKESPECIAL java/util/ArrayList. ()V */ private static class NoArgInit implements DeferredCode { final int opcode; final String owner; final String name; final String desc; final boolean itf; NoArgInit(int opcode, String owner, String name, String desc, boolean itf) { this.opcode = opcode; this.owner = owner; this.name = name; this.desc = desc; this.itf = itf; } @Override public void write(MethodVisitor mv) { mv.visitMethodInsn(opcode, owner, name, desc, itf); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy