/* * The MIT License (MIT) * * Copyright (c) 2016. Diorite (by Bartłomiej Mazur (aka GotoFinal)) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package org.diorite.inject.impl.controller; import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.FieldInsnNode; import org.objectweb.asm.tree.InsnList; import org.objectweb.asm.tree.MethodInsnNode; import org.objectweb.asm.tree.MethodNode; import org.diorite.inject.impl.utils.AsmUtils; final class TransformerFieldInjector { private final Transformer injectTransformer; private TransformerFieldInjector(Transformer injectTransformer) { this.injectTransformer = injectTransformer; } public static TransformerFieldInjector run(Transformer injectTransformer) { TransformerFieldInjector injectorTransformer = new TransformerFieldInjector(injectTransformer); injectorTransformer.injectFields(); return injectorTransformer; } public Transformer getInjectTransformer() { return this.injectTransformer; } private void injectFields() { // assert this.clinit != null; // this.findAllFieldInitMethods(this.clinit); for (MethodNode init : this.injectTransformer.inits.keySet()) { this.injectFieldsIn(init); } } private void injectFieldsIn(MethodNode rootNode) { InsnList instructions = rootNode.instructions; if (instructions.size() == 0) { return; } AbstractInsnNode node = instructions.getFirst(); while (node != null) { while (! (node instanceof FieldInsnNode) || ! AsmUtils.isPutField(node.getOpcode())) { node = node.getNext(); if (node == null) { return; } } this.trackFieldToInject((FieldInsnNode) node, rootNode.instructions); node = node.getNext(); } } private void trackFieldToInject(FieldInsnNode fieldInsnNode, InsnList insnList) { // check if field is in this same class if (! fieldInsnNode.owner.equals(this.injectTransformer.classNode.name)) { return; } // first check if field is injected TransformerFieldPair fieldPair = this.injectTransformer.getFieldPair(fieldInsnNode); if ((fieldPair == null) || (fieldPair.node == null) || (fieldPair.data == null)) { return; } TransformerInjectTracker injectTracker = TransformerInjectTracker.trackFromField(this.injectTransformer, fieldInsnNode, insnList); fieldPair.placeholderType = injectTracker.getPlaceholderType(); this.injectField(fieldPair, injectTracker); } private void injectField(TransformerFieldPair fieldPair, TransformerInjectTracker result) { ControllerFieldData<?> fieldData = fieldPair.data; assert fieldData != null; MethodInsnNode injectionInvokeNode = result.getResult(); FieldInsnNode putfieldNode = result.getFieldInsnNode(); // insert invoke methods: MethodNode codeBefore = new MethodNode(); MethodNode codeAfter = new MethodNode(); this.injectTransformer.fillMethodInvokes(codeBefore, codeAfter, fieldData); // node list for PUTFIELD InsnList initNodeList = result.getInitNodeList(); // node list for invoke execution InsnList resultNodeList = result.getResultNodeList(); if (codeBefore.instructions.size() != 0) { // invoke before should be added before PUTFIELD and before INVOKE resultNodeList.insertBefore(injectionInvokeNode, codeBefore.instructions); } if (codeAfter.instructions.size() != 0) { // invoke after should be added after PUTFIELD initNodeList.insert(putfieldNode, codeAfter.instructions); } // and replace placeholder node with real injections: { MethodNode tempNode = new MethodNode(); TransformerInvokerGenerator.generateFieldInjection(this.injectTransformer.classData, fieldData, tempNode, - 1, fieldPair.placeholderType); resultNodeList.insertBefore(injectionInvokeNode, tempNode.instructions); resultNodeList.remove(injectionInvokeNode); } } }