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

com.redhat.ceylon.compiler.java.tools.Java9Util Maven / Gradle / Ivy

There is a newer version: 1.3.3
Show newest version
/*
 * Copyright Red Hat Inc. and/or its affiliates and other contributors
 * as indicated by the authors tag. All rights reserved.
 *
 * This copyrighted material is made available to anyone wishing to use,
 * modify, copy, or redistribute it subject to the terms and conditions
 * of the GNU General Public License version 2.
 * 
 * This particular file is subject to the "Classpath" exception as provided in the 
 * LICENSE file that accompanied this code.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT A
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE.  See the GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License,
 * along with this distribution; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA  02110-1301, USA.
 */
package com.redhat.ceylon.compiler.java.tools;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

import com.redhat.ceylon.cmr.api.ModuleDependencyInfo;
import com.redhat.ceylon.cmr.api.ModuleInfo;
import com.redhat.ceylon.cmr.impl.IOUtils;
import com.redhat.ceylon.cmr.impl.XmlDependencyResolver;
import com.redhat.ceylon.langtools.classfile.Attribute;
import com.redhat.ceylon.langtools.classfile.Attributes;
import com.redhat.ceylon.langtools.classfile.ClassFile;
import com.redhat.ceylon.langtools.classfile.ClassWriter;
import com.redhat.ceylon.langtools.classfile.ConcealedPackages_attribute;
import com.redhat.ceylon.langtools.classfile.ConstantPool;
import com.redhat.ceylon.langtools.classfile.ConstantPool.CPInfo;
import com.redhat.ceylon.langtools.classfile.MainClass_attribute;
import com.redhat.ceylon.langtools.classfile.Module_attribute;
import com.redhat.ceylon.langtools.classfile.Version_attribute;
import com.redhat.ceylon.model.cmr.JDKUtils;
import com.redhat.ceylon.model.typechecker.model.Module;
import com.redhat.ceylon.model.typechecker.model.ModuleImport;
import com.redhat.ceylon.model.typechecker.model.Package;

public class Java9Util {

	private static String sanitiseName(String name) {
		return name.replace('-', '_');
	}

	static class Java9ModuleDescriptor {
		final String name, version, main;
		final Set exportedPackages = new HashSet(), concealedPackages = new HashSet();
		final List imports = new LinkedList();
		
		Java9ModuleDescriptor(Module module){
			name = sanitiseName(module.getNameAsString());
			version = module.getVersion();
			for(Package pkg : module.getPackages()){
				if(isShared(pkg))
					exportedPackages.add(pkg.getNameAsString());
				else
					concealedPackages.add(pkg.getNameAsString());
			}
			for(ModuleImport imp : module.getImports()){
				String dependency = JDKUtils.getJava9ModuleName(imp.getModule().getNameAsString(), imp.getModule().getVersion());
				if(skipModuleImport(dependency))
					continue;
				imports.add(new Java9ModuleImport(dependency, imp.isExport()));
			}
			addImplicitImports();
			main = getMain(module.getNameAsString());
		}
		
		public Java9ModuleDescriptor(String name, String version, ModuleInfo info, Set packages) {
			this.name = sanitiseName(name);
			this.version = version;
			exportedPackages.addAll(packages);
			for(ModuleDependencyInfo imp : info.getDependencies()){
				String dependency = JDKUtils.getJava9ModuleName(imp.getName(), imp.getVersion());
				if(skipModuleImport(dependency))
					continue;
				imports.add(new Java9ModuleImport(dependency, imp.isExport()));
			}
			addImplicitImports();
			main = getMain(name);
		}

		private boolean skipModuleImport(String dependency) {
			// model depends on language but only at runtime, Java 9 adds this dependency at runtime and
			// will barf if it sees it at compile-time, so special-case it
			if(name.equals("com.redhat.ceylon.model") && dependency.equals(Module.LANGUAGE_MODULE_NAME))
				return true;
			return false;
		}

		private String getMain(String module) {
			if(module.equals(Module.LANGUAGE_MODULE_NAME))
				return "com.redhat.ceylon.compiler.java.runtime.Main2";
			else 
				return null;
		}

		private boolean isShared(Package pkg) {
			// Special case for the language module where we don't want Ceylon users to access this package, but
			// we need our produced Java code to be allowed to
			if(pkg.getModule().getNameAsString().equals(Module.LANGUAGE_MODULE_NAME)){
				String name = pkg.getNameAsString();
				if(name.equals("com.redhat.ceylon.compiler.java.runtime.metamodel")
						|| name.equals("com.redhat.ceylon.compiler.java.runtime")
						|| name.equals("com.redhat.ceylon.compiler.java.metadata")
						|| name.equals("com.redhat.ceylon.compiler.java"))
					return true;
			}
			return pkg.isShared();
		}

		private void addImplicitImports() {
			for(Java9ModuleImport imp : imports){
				if(imp.name.equals("java.base"))
					return;
			}
			imports.add(new Java9ModuleImport("java.base", true));
		}

		int getPackagesSize(){
			return exportedPackages.size() + concealedPackages.size();
		}
	}
	
	static class Java9ModuleImport {
		final String name;
		final boolean exported;
		Java9ModuleImport(String name, boolean exported){
			this.name = sanitiseName(name);
			this.exported = exported;
		}
	}

	public static void writeModuleDescriptor(ZipOutputStream jarOutputStream, Java9ModuleDescriptor module) {
    	ClassWriter classWriter = new ClassWriter();
    	CPInfo[] pool = new ConstantPool.CPInfo[1+6
    	                                        + module.imports.size()
    	                                        + module.getPackagesSize()
    	                                        + (module.main != null ? 3 : 0)];
    	ConstantPool constantPool = new ConstantPool(pool);
    	
    	int cp = 1;
    	// 1: this_class name
    	pool[cp++] = new ConstantPool.CONSTANT_Utf8_info(module.name.replace('.', '/')+"/module-info");
    	// 2: this_class
    	pool[cp++] = new ConstantPool.CONSTANT_Class_info(constantPool, 1);
    	// 3: module attr
    	pool[cp++] = new ConstantPool.CONSTANT_Utf8_info(Attribute.Module);
    	// 4: concealed pkgs attr
    	pool[cp++] = new ConstantPool.CONSTANT_Utf8_info(Attribute.ConcealedPackages);
    	// 5: version attr
    	pool[cp++] = new ConstantPool.CONSTANT_Utf8_info(Attribute.Version);
    	// 6: version
    	pool[cp++] = new ConstantPool.CONSTANT_Utf8_info(module.version);
    	if(module.main != null){
    		// 7: main attr
    		pool[cp++] = new ConstantPool.CONSTANT_Utf8_info(Attribute.MainClass);
    		// 8: main class name
    		pool[cp++] = new ConstantPool.CONSTANT_Utf8_info(module.main.replace('.', '/'));
        	// 9: main class
        	pool[cp++] = new ConstantPool.CONSTANT_Class_info(constantPool, 8);
    	}
    	int i=0;
    	// now imports
    	Module_attribute.RequiresEntry[] requires = new Module_attribute.RequiresEntry[module.imports.size()];
    	for(Java9ModuleImport imp : module.imports){
    		pool[cp] = new ConstantPool.CONSTANT_Utf8_info(imp.name);
    		int flag = 0;
    		if(imp.exported)
    			flag &= Module_attribute.ACC_PUBLIC;
    		requires[i] = new Module_attribute.RequiresEntry(cp, flag);
    		i++;
    		cp++;
    	}
    	Module_attribute.ExportsEntry[] exports = new Module_attribute.ExportsEntry[module.exportedPackages.size()];
    	i = 0;
    	for(String pkg : module.exportedPackages){
    		pool[cp] = new ConstantPool.CONSTANT_Utf8_info(pkg.replace('.', '/'));
    		exports[i++] = new Module_attribute.ExportsEntry(cp, new int[0]);
    		cp++;
    	}
    	int[] concealedPackages = new int[module.concealedPackages.size()];
    	i = 0;
    	for(String pkg : module.concealedPackages){
    		pool[cp] = new ConstantPool.CONSTANT_Utf8_info(pkg.replace('.', '/'));
    		concealedPackages[i++] = cp;
    		cp++;
    	}
    	Attribute[] attributesArray = new Attribute[3 + (module.main != null ? 1 : 0)];
    	attributesArray[0] = new Module_attribute(3, 
    					requires,
    					exports, 
    					new int[0], 
    					new Module_attribute.ProvidesEntry[0]);
    	attributesArray[1] = new ConcealedPackages_attribute(4, concealedPackages);
    	attributesArray[2] = new Version_attribute(5, 6);
    	if(module.main != null)
    		attributesArray[3] = new MainClass_attribute(7, 9);
    			
		Attributes attributes = new Attributes(constantPool, attributesArray );
		ClassFile classFile = new ClassFile(com.redhat.ceylon.langtools.tools.javac.jvm.ClassFile.JAVA_MAGIC,
    			com.redhat.ceylon.langtools.tools.javac.jvm.ClassFile.Version.V53.major,
    			com.redhat.ceylon.langtools.tools.javac.jvm.ClassFile.Version.V53.minor,
    			constantPool,
    			new com.redhat.ceylon.langtools.classfile.AccessFlags(com.redhat.ceylon.langtools.classfile.AccessFlags.ACC_MODULE),
    			2,
    			0,
    			new int[0],
    			new com.redhat.ceylon.langtools.classfile.Field[0],
    			new com.redhat.ceylon.langtools.classfile.Method[0],
    			attributes);
        try {
            jarOutputStream.putNextEntry(new ZipEntry("module-info.class"));
            
            classWriter.write(classFile, jarOutputStream);
            jarOutputStream.flush();
            jarOutputStream.closeEntry();
        } catch (IOException e) {
        	e.printStackTrace();
            throw new RuntimeException(e);
        }
	}

    private static final String METAINF_JBOSSMODULES = "META-INF/jbossmodules/";
    private static final String MODULE_XML = "module.xml";

	public static void main(String[] args) throws IOException {
		System.err.println("Add Java9 Module info for "+args[0]);
		// FIXME: check arg
		String jarName = args[0];
		File jarFile = new File(jarName);
		ZipFile zipFile = new ZipFile(jarFile);
		File outFile = File.createTempFile(jarName, ".jar");
		ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(outFile));
		Enumeration entries = zipFile.entries();
		Set packages = new HashSet();
		ModuleInfo info = null;
		String moduleName = null, version = null;
		while(entries.hasMoreElements()){
			ZipEntry entry = entries.nextElement();
			String name = entry.getName();
			if(name.equals("module-info.class")){
				System.err.println(" Already has a module info!");
				zipFile.close();
				zos.close();
				outFile.delete();
				return;
			}else if(name.endsWith(".class")){
				int lastSlash = name.lastIndexOf('/');
				// ignore the default package (not sure if we can import or conceal it in Java 9
				if(lastSlash != -1){
					String pkg = name.substring(0, lastSlash).replace('/', '.');
					packages.add(pkg);
				}
			}else if(name.startsWith(METAINF_JBOSSMODULES)
					&& name.endsWith("module.xml")){
				String path = name.substring(METAINF_JBOSSMODULES.length());
				path = path.substring(0, path.length() - MODULE_XML.length() - 1);
	            int p = path.lastIndexOf('/');
	            if (p > 0) {
	                moduleName = path.substring(0, p).replace('/', '.');
	                version = path.substring(p + 1);
	            }
				try(InputStream is = zipFile.getInputStream(entry)){
					// name/version only used when we have an override
					info = XmlDependencyResolver.INSTANCE.resolveFromInputStream(is, null, null, null);
					System.err.println(" Found module descriptor at "+name);
				}
			}
			zos.putNextEntry(entry);
			if(!entry.isDirectory())
				IOUtils.copyStream(zipFile.getInputStream(entry), zos, true, false);
			zos.closeEntry();
		}
		zipFile.close();
		
		if(info != null && moduleName != null && version != null){
			writeModuleDescriptor(zos, new Java9ModuleDescriptor(moduleName, version, info, packages));
		}
		zos.flush();
		zos.close();
		// rename
		jarFile.delete();
        Files.copy(outFile.toPath(), jarFile.toPath(), StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES);
        outFile.delete();
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy