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

dev.denwav.hypo.model.SystemClassProviderRootJdk9 Maven / Gradle / Ivy

/*
 * Hypo, an extensible and pluggable Java bytecode analytical model.
 *
 * Copyright (C) 2023  Kyle Wood (DenWav)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the Lesser GNU General Public License as published by
 * the Free Software Foundation, version 3 of the License only.
 *
 * This program 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see .
 */

package dev.denwav.hypo.model;

import java.io.IOException;
import java.lang.module.ModuleFinder;
import java.lang.module.ModuleReader;
import java.lang.module.ModuleReference;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/**
 * {@link ClassProviderRoot} implementation for system classes for Java 9+. Tested on JDKs up to Java 17.
 */
@SuppressWarnings("unused") // Loaded dynamically
class SystemClassProviderRootJdk9 implements ClassProviderRoot {

    private final @NotNull List readers;

    /**
     * Constructor for {@link SystemClassProviderRootJdk9}. Use {@link ClassProviderRoot#ofJdk()} instead.
     *
     * @throws IOException If an IO error occurs while opening JDK modules.
     */
    SystemClassProviderRootJdk9() throws IOException {
        final Set refs = ModuleFinder.ofSystem().findAll();
        final ModuleReader[] readers = new ModuleReader[refs.size()];
        int index = 0;
        for (final ModuleReference ref : refs) {
            readers[index++] = ref.open();
        }
        this.readers = Arrays.asList(readers);
    }

    @Override
    public byte @Nullable [] getClassData(@NotNull String fileName) throws IOException {
        for (final ModuleReader reader : this.readers) {
            final ByteBuffer resource = reader.read(fileName).orElse(null);
            if (resource == null) {
                continue;
            }
            try {
                final byte[] data = new byte[resource.remaining()];
                resource.get(data);
                return data;
            } finally {
                reader.release(resource);
            }
        }
        return null;
    }

    @Override
    public @NotNull List getAllClasses() throws IOException {
        List refs = null;

        for (final ModuleReader reader : this.readers) {
            final List list = reader.list()
                .filter(n -> n.endsWith(".class"))
                .map(n -> new SystemFileDataReference(n, reader))
                .collect(Collectors.toList());

            if (refs == null) {
                refs = list;
            } else {
                refs.addAll(list);
            }
        }
        return refs == null ? Collections.emptyList() : refs;
    }

    @Override
    public void close() throws IOException {
        IOException thrown = null;
        for (final ModuleReader reader : this.readers) {
            try {
                reader.close();
            } catch (final IOException e) {
                thrown = HypoModelUtil.addSuppressed(thrown, e);
            }
        }
        if (thrown != null) {
            throw thrown;
        }
    }

    /**
     * Implementation of {@link FileDataReference} for {@link SystemClassProviderRootJdk9}.
     */
    static final class SystemFileDataReference implements FileDataReference {

        private final @NotNull String name;
        private final @NotNull ModuleReader reader;

        /**
         * Construct a new instance of {@link SystemFileDataReference}.
         *
         * @param name The file name.
         * @param reader The {@link ModuleReader} to read the class from.
         */
        SystemFileDataReference(final @NotNull String name, final @NotNull ModuleReader reader) {
            this.name = name;
            this.reader = reader;
        }

        @Override
        public @NotNull String name() {
            return this.name;
        }

        @Override
        public byte @Nullable [] readData() throws IOException {
            final ByteBuffer resource = this.reader.read(this.name).orElse(null);
            if (resource == null) {
                return null;
            }
            try {
                final byte[] data = new byte[resource.remaining()];
                resource.get(data);
                return data;
            } finally {
                this.reader.release(resource);
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy