1 /*
   2  * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 package com.sun.tools.jextract;
  24 
  25 import javax.tools.FileObject;
  26 import javax.tools.ForwardingJavaFileManager;
  27 import javax.tools.JavaCompiler;
  28 import javax.tools.JavaFileManager;
  29 import javax.tools.JavaFileObject;
  30 import javax.tools.SimpleJavaFileObject;
  31 import javax.tools.ToolProvider;
  32 import java.io.ByteArrayOutputStream;
  33 import java.io.StringWriter;
  34 import java.io.Writer;
  35 import java.net.URI;
  36 import java.util.Arrays;
  37 import java.util.Collection;
  38 import java.util.HashMap;
  39 import java.util.LinkedList;
  40 import java.util.Map;
  41 import java.util.Map.Entry;
  42 
  43 final class InMemoryJavaCompiler {
  44     private InMemoryJavaCompiler() {}
  45 
  46     static Map<String, byte[]> compile(Map<String, ? extends CharSequence> inputMap,
  47             String... options) {
  48         Collection<JavaFileObject> sourceFiles = new LinkedList<>();
  49         for (Entry<String, ? extends CharSequence> entry : inputMap.entrySet()) {
  50             sourceFiles.add(new SourceFile(entry.getKey(), entry.getValue()));
  51         }
  52 
  53         JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
  54         FileManager fileManager = new FileManager(compiler.getStandardFileManager(null, null, null));
  55 
  56         Writer writer = new StringWriter();
  57         Boolean exitCode = compiler.getTask(writer, fileManager, null, Arrays.asList(options), null, sourceFiles).call();
  58         if (!exitCode) {
  59             throw new RuntimeException("Test bug: in memory compilation failed: " + writer.toString());
  60         }
  61         return fileManager.getByteCode();
  62     }
  63 
  64     // Wraper for class byte array
  65     private static class ClassFile extends SimpleJavaFileObject {
  66         private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
  67 
  68         protected ClassFile(String name) {
  69             super(URI.create("memo:///" + name.replace('.', '/') + Kind.CLASS.extension), Kind.CLASS);
  70         }
  71 
  72         @Override
  73         public ByteArrayOutputStream openOutputStream() { return this.baos; }
  74 
  75         byte[] toByteArray() { return baos.toByteArray(); }
  76     }
  77 
  78     // File manager which spawns ClassFile instances on demand
  79     private static class FileManager extends ForwardingJavaFileManager<JavaFileManager> {
  80         private final Map<String, ClassFile> classesMap = new HashMap<>();
  81 
  82         protected FileManager(JavaFileManager fileManager) {
  83             super(fileManager);
  84         }
  85 
  86         @Override
  87         public ClassFile getJavaFileForOutput(Location location, String name, JavaFileObject.Kind kind, FileObject source) {
  88             ClassFile classFile = new ClassFile(name);
  89             classesMap.put(name, classFile);
  90             return classFile;
  91         }
  92 
  93         public Map<String, byte[]> getByteCode() {
  94             Map<String, byte[]> result = new HashMap<>();
  95             for (Entry<String, ClassFile> entry : classesMap.entrySet()) {
  96                 result.put(entry.getKey(), entry.getValue().toByteArray());
  97             }
  98             return result;
  99         }
 100     }
 101 
 102     // Wrapper for source String
 103     private static class SourceFile extends SimpleJavaFileObject {
 104         private final CharSequence sourceCode;
 105 
 106         public SourceFile(String name, CharSequence sourceCode) {
 107             super(URI.create("memo:///" + name.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
 108             this.sourceCode = sourceCode;
 109         }
 110 
 111         @Override
 112         public CharSequence getCharContent(boolean ignore) {
 113             return this.sourceCode;
 114         }
 115     }
 116 }