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

io.github.dmlloyd.classfile.impl.TransformImpl Maven / Gradle / Ivy

/*
 * Copyright (c) 2022, 2024, 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 io.github.dmlloyd.classfile.impl;

import io.github.dmlloyd.classfile.ClassFileBuilder;
import io.github.dmlloyd.classfile.ClassFileTransform;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.Supplier;

import io.github.dmlloyd.classfile.ClassBuilder;
import io.github.dmlloyd.classfile.ClassElement;
import io.github.dmlloyd.classfile.ClassTransform;
import io.github.dmlloyd.classfile.ClassFileElement;
import io.github.dmlloyd.classfile.CodeBuilder;
import io.github.dmlloyd.classfile.CodeElement;
import io.github.dmlloyd.classfile.CodeModel;
import io.github.dmlloyd.classfile.CodeTransform;
import io.github.dmlloyd.classfile.FieldBuilder;
import io.github.dmlloyd.classfile.FieldElement;
import io.github.dmlloyd.classfile.FieldModel;
import io.github.dmlloyd.classfile.FieldTransform;
import io.github.dmlloyd.classfile.MethodBuilder;
import io.github.dmlloyd.classfile.MethodElement;
import io.github.dmlloyd.classfile.MethodModel;
import io.github.dmlloyd.classfile.MethodTransform;

public final class TransformImpl {
    // ClassTransform

    private TransformImpl() {
    }

    private static Runnable chainRunnable(Runnable a, Runnable b) {
        return () -> { a.run(); b.run(); };
    }

    private static final Runnable NOTHING = () -> { };

    public static >
            ResolvedTransform resolve(ClassFileTransform transform, B builder) {
        if (transform instanceof ResolvableTransform) {
            @SuppressWarnings("unchecked")
            var ut = (ResolvableTransform) transform;
            return ut.resolve(builder);
        }
        return new ResolvedTransform<>(e -> transform.accept(builder, e),
            () -> transform.atEnd(builder),
            () -> transform.atStart(builder));
    }

    interface ResolvableTransform> {
        ResolvedTransform resolve(B builder);
    }

    interface UnresolvedClassTransform extends ClassTransform, ResolvableTransform {
        @Override
        default void accept(ClassBuilder builder, ClassElement element) {
            throw new UnsupportedOperationException("transforms must be resolved before running");
        }

        @Override
        default void atEnd(ClassBuilder builder) {
            throw new UnsupportedOperationException("transforms must be resolved before running");
        }

        @Override
        default void atStart(ClassBuilder builder) {
            throw new UnsupportedOperationException("transforms must be resolved before running");
        }
    }

    public record ResolvedTransform(Consumer consumer,
                                     Runnable endHandler,
                                     Runnable startHandler) {

        public ResolvedTransform(Consumer consumer) {
            this(consumer, NOTHING, NOTHING);
        }
    }

    public record ChainedClassTransform(ClassTransform t,
                                        ClassTransform next)
            implements UnresolvedClassTransform {
        @Override
        public ResolvedTransform resolve(ClassBuilder builder) {
            ResolvedTransform downstream = TransformImpl.resolve(next, builder);
            ClassBuilder chainedBuilder = new ChainedClassBuilder(builder, downstream.consumer());
            ResolvedTransform upstream = TransformImpl.resolve(t, chainedBuilder);
            return new ResolvedTransform<>(upstream.consumer(),
                                          chainRunnable(upstream.endHandler(), downstream.endHandler()),
                                          chainRunnable(downstream.startHandler(), upstream.startHandler()));
        }
    }

    public record SupplierClassTransform(Supplier supplier)
            implements UnresolvedClassTransform {
        @Override
        public ResolvedTransform resolve(ClassBuilder builder) {
            return TransformImpl.resolve(supplier.get(), builder);
        }
    }

    public record ClassMethodTransform(MethodTransform transform,
                                       Predicate filter)
            implements UnresolvedClassTransform {
        @Override
        public ResolvedTransform resolve(ClassBuilder builder) {
            return new ResolvedTransform<>(ce -> {
                if (ce instanceof MethodModel mm && filter.test(mm))
                    builder.transformMethod(mm, transform);
                else
                    builder.with(ce);
            });
        }

        @Override
        public ClassTransform andThen(ClassTransform next) {
            if (next instanceof ClassMethodTransform cmt)
                return new ClassMethodTransform(transform.andThen(cmt.transform),
                                                mm -> filter.test(mm) && cmt.filter.test(mm));
            else
                return UnresolvedClassTransform.super.andThen(next);
        }
    }

    public record ClassFieldTransform(FieldTransform transform,
                                      Predicate filter)
            implements UnresolvedClassTransform {
        @Override
        public ResolvedTransform resolve(ClassBuilder builder) {
            return new ResolvedTransform<>(ce -> {
                if (ce instanceof FieldModel fm && filter.test(fm))
                    builder.transformField(fm, transform);
                else
                    builder.with(ce);
            });
        }

        @Override
        public ClassTransform andThen(ClassTransform next) {
            if (next instanceof ClassFieldTransform cft)
                return new ClassFieldTransform(transform.andThen(cft.transform),
                                               mm -> filter.test(mm) && cft.filter.test(mm));
            else
                return UnresolvedClassTransform.super.andThen(next);
        }
    }

    // MethodTransform

    interface UnresolvedMethodTransform extends MethodTransform, ResolvableTransform {
        @Override
        default void accept(MethodBuilder builder, MethodElement element) {
            throw new UnsupportedOperationException("transforms must be resolved before running");
        }

        @Override
        default void atEnd(MethodBuilder builder) {
            throw new UnsupportedOperationException("transforms must be resolved before running");
        }

        @Override
        default void atStart(MethodBuilder builder) {
            throw new UnsupportedOperationException("transforms must be resolved before running");
        }
    }

    public record ChainedMethodTransform(MethodTransform t,
                                         MethodTransform next)
            implements TransformImpl.UnresolvedMethodTransform {
        @Override
        public ResolvedTransform resolve(MethodBuilder builder) {
            ResolvedTransform downstream = TransformImpl.resolve(next, builder);
            MethodBuilder chainedBuilder = new ChainedMethodBuilder(builder, downstream.consumer());
            ResolvedTransform upstream = TransformImpl.resolve(t, chainedBuilder);
            return new ResolvedTransform<>(upstream.consumer(),
                                           chainRunnable(upstream.endHandler(), downstream.endHandler()),
                                           chainRunnable(downstream.startHandler(), upstream.startHandler()));
        }
    }

    public record SupplierMethodTransform(Supplier supplier)
            implements TransformImpl.UnresolvedMethodTransform {
        @Override
        public ResolvedTransform resolve(MethodBuilder builder) {
            return TransformImpl.resolve(supplier.get(), builder);
        }
    }

    public record MethodCodeTransform(CodeTransform xform)
            implements TransformImpl.UnresolvedMethodTransform {
        @Override
        public ResolvedTransform resolve(MethodBuilder builder) {
            return new ResolvedTransform<>(me -> {
                if (me instanceof CodeModel cm) {
                    builder.transformCode(cm, xform);
                }
                else {
                    builder.with(me);
                }
            }, NOTHING, NOTHING);
        }

        @Override
        public MethodTransform andThen(MethodTransform next) {
            return (next instanceof MethodCodeTransform mct)
                   ? new MethodCodeTransform(xform.andThen(mct.xform))
                   : UnresolvedMethodTransform.super.andThen(next);

        }
    }

    // FieldTransform

    interface UnresolvedFieldTransform extends FieldTransform, ResolvableTransform {
        @Override
        default void accept(FieldBuilder builder, FieldElement element) {
            throw new UnsupportedOperationException("transforms must be resolved before running");
        }

        @Override
        default void atEnd(FieldBuilder builder) {
            throw new UnsupportedOperationException("transforms must be resolved before running");
        }

        @Override
        default void atStart(FieldBuilder builder) {
            throw new UnsupportedOperationException("transforms must be resolved before running");
        }
    }

    public record ChainedFieldTransform(FieldTransform t, FieldTransform next)
            implements UnresolvedFieldTransform {
        @Override
        public ResolvedTransform resolve(FieldBuilder builder) {
            ResolvedTransform downstream = TransformImpl.resolve(next, builder);
            FieldBuilder chainedBuilder = new ChainedFieldBuilder(builder, downstream.consumer());
            ResolvedTransform upstream = TransformImpl.resolve(t, chainedBuilder);
            return new ResolvedTransform<>(upstream.consumer(),
                                           chainRunnable(upstream.endHandler(), downstream.endHandler()),
                                           chainRunnable(downstream.startHandler(), upstream.startHandler()));
        }
    }

    public record SupplierFieldTransform(Supplier supplier)
            implements UnresolvedFieldTransform {
        @Override
        public ResolvedTransform resolve(FieldBuilder builder) {
            return TransformImpl.resolve(supplier.get(), builder);
        }
    }

    // CodeTransform

    interface UnresolvedCodeTransform extends CodeTransform, ResolvableTransform {
        @Override
        default void accept(CodeBuilder builder, CodeElement element) {
            throw new UnsupportedOperationException("transforms must be resolved before running");
        }

        @Override
        default void atEnd(CodeBuilder builder) {
            throw new UnsupportedOperationException("transforms must be resolved before running");
        }

        @Override
        default void atStart(CodeBuilder builder) {
            throw new UnsupportedOperationException("transforms must be resolved before running");
        }
    }

    public record ChainedCodeTransform(CodeTransform t, CodeTransform next)
            implements UnresolvedCodeTransform {
        @Override
        public ResolvedTransform resolve(CodeBuilder builder) {
            ResolvedTransform downstream = TransformImpl.resolve(next, builder);
            CodeBuilder chainedBuilder = new ChainedCodeBuilder(builder, downstream.consumer());
            ResolvedTransform upstream = TransformImpl.resolve(t, chainedBuilder);
            return new ResolvedTransform<>(upstream.consumer(),
                                         chainRunnable(upstream.endHandler(), downstream.endHandler()),
                                         chainRunnable(downstream.startHandler(), upstream.startHandler()));
        }
    }

    public record SupplierCodeTransform(Supplier supplier)
            implements UnresolvedCodeTransform {
        @Override
        public ResolvedTransform resolve(CodeBuilder builder) {
            return TransformImpl.resolve(supplier.get(), builder);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy