BytecodeWriter.java

/*-
 * #%L
 * io.earcam.instrumental.module.jpms
 * %%
 * Copyright (C) 2018 earcam
 * %%
 * SPDX-License-Identifier: (BSD-3-Clause OR EPL-1.0 OR Apache-2.0 OR MIT)
 * 
 * You <b>must</b> choose to accept, in full - any individual or combination of 
 * the following licenses:
 * <ul>
 * 	<li><a href="https://opensource.org/licenses/BSD-3-Clause">BSD-3-Clause</a></li>
 * 	<li><a href="https://www.eclipse.org/legal/epl-v10.html">EPL-1.0</a></li>
 * 	<li><a href="https://www.apache.org/licenses/LICENSE-2.0">Apache-2.0</a></li>
 * 	<li><a href="https://opensource.org/licenses/MIT">MIT</a></li>
 * </ul>
 * #L%
 */
package io.earcam.instrumental.module.jpms;

import static org.objectweb.asm.Opcodes.ACC_MODULE;
import static org.objectweb.asm.Opcodes.ASM6;
import static org.objectweb.asm.Opcodes.V9;

import java.util.Arrays;
import java.util.Objects;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.ModuleVisitor;

import io.earcam.utilitarian.charstar.CharSequences;

/**
 * Handles ASM bits:
 * 1. Creates the class writer returning this modulevisitor
 * 2. Converts JLS names to internal names
 */
class BytecodeWriter extends ModuleVisitor {

	/**
	 * <p>
	 * Constructor for BytecodeWriter.
	 * </p>
	 *
	 * @param mv a {@link org.objectweb.asm.ModuleVisitor} object.
	 */
	public BytecodeWriter(ModuleVisitor mv)
	{
		super(ASM6, mv);
	}


	static byte[] toBytecode(ModuleInfo module)
	{
		ClassWriter writer = new ClassWriter(ASM6);
		writer.visit(V9, ACC_MODULE, "module-info", null, null, null);
		BytecodeWriter visitor = new BytecodeWriter(writer.visitModule(module.name(), module.access(), module.version()));
		if(module.mainClass() != null) {
			visitor.visitMainClass(module.mainClass());
		}

		module.packages().stream()
				.forEach(visitor::visitPackage);

		module.requires().forEach(r -> visitor.visitRequire(r.module(), r.access(), r.version()));
		module.exports().forEach(e -> visitor.visitExport(e.paquet(), e.access(), e.modules()));
		module.opens().forEach(o -> visitor.visitOpen(o.paquet(), o.access(), o.modules()));
		module.uses().forEach(visitor::visitUse);
		module.provides().forEach(visitor::visitProvide);
		visitor.visitEnd();
		writer.visitEnd();
		return writer.toByteArray();

	}


	@Override
	public void visitExport(String packaze, int access, String... modules)
	{
		super.visitExport(internalName(packaze), access, modules);
	}


	static String internalName(CharSequence qualifiedName)
	{
		return CharSequences.replace(qualifiedName, '.', '/').toString();
	}


	@Override
	public void visitMainClass(String mainClass)
	{
		super.visitMainClass(internalName(mainClass));
	}


	@Override
	public void visitOpen(String packaze, int access, String... modules)
	{
		super.visitOpen(internalName(packaze), access, modules);
	}


	/**
	 * <p>
	 * visitPackage.
	 * </p>
	 *
	 * @param packaze a {@link java.lang.CharSequence} object.
	 */
	public void visitPackage(CharSequence packaze)
	{
		visitPackage(Objects.toString(packaze));
	}


	@Override
	public void visitPackage(String packaze)
	{
		super.visitPackage(internalName(packaze));
	}


	@Override
	public void visitProvide(String service, String... providers)
	{
		super.visitProvide(internalName(service), internalNames(providers));
	}


	static String[] internalNames(String[] providers)
	{
		return Arrays.stream(providers)
				.map(BytecodeWriter::internalName)
				.toArray(s -> new String[s]);
	}


	@Override
	public void visitUse(String service)
	{
		super.visitUse(internalName(service));
	}
}