/*******************************************************************************
* PSHDL is a library and (trans-)compiler for PSHDL input. It generates
* output suitable for implementation or simulation of it.
*
* Copyright (C) 2013 Karsten Becker (feedback (at) pshdl (dot) org)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* This License does not grant permission to use the trade names, trademarks,
* service marks, or product names of the Licensor, except as required for
* reasonable and customary use in describing the origin of the Work.
*
* Contributors:
* Karsten Becker - initial API and implementation
******************************************************************************/
package org.pshdl.model.simulation.codegenerator;
import java.io.File;
import java.io.StringWriter;
import java.lang.reflect.Constructor;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.tools.Diagnostic;
import javax.tools.Diagnostic.Kind;
import javax.tools.DiagnosticListener;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;
import org.pshdl.interpreter.IChangeListener;
import org.pshdl.interpreter.IHDLInterpreter;
import org.pshdl.interpreter.IHDLInterpreterFactory;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import com.google.common.io.Files;
public class JavaClassRuntimeLoader implements AutoCloseable {
public static class DiagnosticsException extends RuntimeException {
private static final long serialVersionUID = -1283967107290479965L;
public final List<Diagnostic<? extends JavaFileObject>> diagnostics;
public DiagnosticsException(List<Diagnostic<? extends JavaFileObject>> diagnostics) {
this.diagnostics = diagnostics;
}
@Override
public String getMessage() {
for (final Diagnostic<? extends JavaFileObject> diagnostic : diagnostics) {
if (diagnostic.getKind() == Kind.ERROR)
return diagnostic.getMessage(null);
}
return super.getMessage();
}
}
private final static class ErrorCheckDiagnostic implements DiagnosticListener<JavaFileObject> {
public Kind kind;
public List<Diagnostic<? extends JavaFileObject>> diagnostics = Lists.newArrayList();
@Override
public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
final Kind newKind = diagnostic.getKind();
if (newKind == Kind.ERROR) {
kind = newKind;
}
diagnostics.add(diagnostic);
}
}
private final File tempDir;
private final URLClassLoader classLoader;
private final JavaCompiler compiler;
private final StandardJavaFileManager fileManager;
public JavaClassRuntimeLoader() throws Exception {
tempDir = Files.createTempDir();
tempDir.deleteOnExit();
classLoader = URLClassLoader.newInstance(new URL[] { tempDir.toURI().toURL() });
compiler = ToolProvider.getSystemJavaCompiler();
fileManager = compiler.getStandardFileManager(null, null, null);
final String property = System.getProperty("java.class.path");
final List<File> cp = new ArrayList<>();
final Iterable<String> split = Splitter.on(File.pathSeparatorChar).split(property);
for (final String entry : split) {
cp.add(new File(entry));
}
cp.add(tempDir);
fileManager.setLocation(StandardLocation.CLASS_PATH, cp);
fileManager.setLocation(StandardLocation.SOURCE_PATH, Collections.singleton(tempDir));
}
public IHDLInterpreterFactory<IHDLInterpreter> compileAndLoad(String mainClassFQN, String sourceCode, final boolean disableEdge, final boolean disableOutputLogic)
throws Exception {
final Class<?> cls = compileClass(mainClassFQN, sourceCode);
return new IHDLInterpreterFactory<IHDLInterpreter>() {
@Override
public IHDLInterpreter newInstance() {
Constructor<?> constructor;
try {
constructor = cls.getConstructor(Boolean.TYPE, Boolean.TYPE);
return (IHDLInterpreter) constructor.newInstance(disableEdge, disableOutputLogic);
} catch (final Exception e) {
throw new RuntimeException(e);
}
}
};
}
public Class<?> compileClass(String mainClassFQN, String sourceCode) throws Exception {
final String pathName = mainClassFQN.replace('.', File.separatorChar) + ".java";
final File sourceFile = new File(tempDir, pathName);
final File pkgDir = sourceFile.getParentFile();
if (pkgDir == null)
throw new IllegalArgumentException("Failed to get parent of:" + sourceFile);
if (!pkgDir.exists() && !pkgDir.mkdirs())
throw new IllegalArgumentException("Failed to create package directories:" + pkgDir);
Files.write(sourceCode, sourceFile, StandardCharsets.UTF_8);
final StringWriter error = new StringWriter();
final ErrorCheckDiagnostic diagnostic = new ErrorCheckDiagnostic();
compiler.getTask(error, fileManager, diagnostic, null, null, fileManager.getJavaFileObjectsFromFiles(Arrays.asList(sourceFile))).call();
if (diagnostic.kind == Kind.ERROR)
throw new DiagnosticsException(diagnostic.diagnostics);
// Load and instantiate compiled class.
return Class.forName(mainClassFQN, true, classLoader);
}
@Override
public void close() throws Exception {
fileManager.close();
}
public File getTempDir() {
return tempDir;
}
public IHDLInterpreter compileAndLoadChangeAdapter(String mainClassFQN, String sourceCode, IHDLInterpreter mainInterpreter, IChangeListener... listeners) throws Exception {
final Class<?> adapterClass = compileClass(mainClassFQN, sourceCode);
Constructor<?> constructor = null;
try {
constructor = adapterClass.getConstructor(mainInterpreter.getClass(), IChangeListener[].class);
} catch (final Exception e) {
constructor = adapterClass.getConstructor(IHDLInterpreter.class, IChangeListener[].class);
}
return (IHDLInterpreter) constructor.newInstance(mainInterpreter, listeners);
}
}