/* * Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code 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 * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, * CA 95054 USA or visit www.sun.com if you need additional information or * have any questions. */ package org.visage.tools.script; import org.visage.tools.api.VisagecTool; import org.visage.tools.api.VisagecTaskImpl; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Writer; import java.io.PrintWriter; import java.io.Reader; import java.net.URL; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.tools.*; import org.visage.tools.code.*; import org.visage.tools.comp.*; import com.sun.tools.mjavac.util.Context; import com.sun.tools.mjavac.code.*; import com.sun.tools.mjavac.util.Name; /** * Simple interface to the Visage compiler using JSR 199 Compiler API. * This is "cumulative": Script-level declarations make by one script are * visible to future scripts, so this represents a "compiler context". * Based on https://scripting.dev.java.net's JavaCompiler by A. Sundararajan. */ public class VisageScriptCompiler { public VisagecTool tool; private ClassLoader parentClassLoader; // a map in which the key is package name and the value is list of // classes in that package. public Map<String, List<String>> packageMap; Scope namedImportScope; Map<String,MemoryFileManager.ClassOutputBuffer> clbuffers = new HashMap<String,MemoryFileManager.ClassOutputBuffer>(); MemoryFileManager manager; Name.Table names; VisageDefs defs; VisageTypes types; VisageSymtab syms; VisageClassReader reader; Name pseudoSourceFile; Name pseudoFile; Name pseudoDir; Name pseudoProfile; public VisageScriptCompiler(ClassLoader parent) { parentClassLoader = parent; tool = VisagecTool.create(); packageMap = new HashMap<String, List<String>>(); try { // fill package-class-list map from parent class loader fillPackageMap(parentClassLoader, packageMap); } catch (IOException exp) { exp.printStackTrace(); } StandardJavaFileManager stdManager = tool.getStandardFileManager(null, null, null); manager = new MemoryFileManager(stdManager, parentClassLoader, packageMap, clbuffers); } void initCompilerContext (Context context, VisagecTaskImpl task) { if (names == null) { // This is the first time initCompilerContext is called. names = Name.Table.instance(context); org.visage.tools.code.VisageSymtab.preRegister(context); org.visage.tools.code.VisageTypes.preRegister(context); org.visage.tools.comp.VisageClassReader.preRegister(context); pseudoFile = names.fromString("__FILE__"); pseudoSourceFile = names.fromString("__SOURCE_FILE__"); pseudoDir = names.fromString("__DIR__"); pseudoProfile = names.fromString("__PROFILE__"); defs = VisageDefs.instance(context); syms = (VisageSymtab) VisageSymtab.instance(context); types = VisageTypes.instance(context); } else { // Re-use names etc from previous calls to initCompilerContext. context.put(Name.Table.namesKey, names); context.put(VisageDefs.visageDefsKey, defs); VisageSymtab.preRegister(context, syms); VisageTypes.preRegister(context, types); } reader = VisageClassReader.instance(context); VisageScriptClassBuilder classBuilder = VisageScriptClassBuilder.instance(context); classBuilder.scriptingMode = true; task.compilerMain.registerServices(context, new String[] {}); if (namedImportScope == null) { namedImportScope = new Scope.ImportScope(syms.unnamedPackage); VisageMemberEnter.importPredefs(syms, namedImportScope); } } /** * compile given String source and return bytecodes as a Map. * * @param fileName source fileName to be used for error messages etc. * @param source Java source as String * @param err error writer where diagnostic messages are written * @param sourcePath location of additional .java source files * @param classPath location of additional .class files * @param diagnostics error and warning collector * @param printDiagnostics true if diagnostics should be displayed */ public VisageCompiledScript compile(String fileName, String source, Writer err, String sourcePath, String classPath, DiagnosticListener<JavaFileObject> diagnostics) { Context context = new Context(); context.put(DiagnosticListener.class, diagnostics); context.put(JavaFileManager.class, manager); // prepare the compilation unit List<JavaFileObject> compUnits = new ArrayList<JavaFileObject>(1); compUnits.add(manager.makeStringSource(fileName, source)); // javac options List<String> options = new ArrayList<String>(); options.add("-Xlint:all-unchecked"); options.add("-g"); options.add("-deprecation"); if (sourcePath != null) { options.add("-sourcepath"); options.add(sourcePath); } if (classPath != null) { options.add("-classpath"); options.add(classPath); } options.add("-target"); options.add("1.5"); options.add("-XDdumpvisage=" + System.getProperty("java.io.tmpdir")); // create a compilation task VisagecTaskImpl task = tool.getTask(context, err, manager, null, options, compUnits); initCompilerContext(context, task); task.setPreserveSymbols(namedImportScope, null, true); if (! task.call()) return null; VisageEnter env = VisageEnter.instance(context); Scope scriptScope = env.scriptScopes.first(); for (Scope.Entry e = scriptScope.elems; e != null; e = e.sibling) { if ((e.sym.flags() & Flags.SYNTHETIC) != 0) continue; Name name = e.sym.name; if (name == pseudoSourceFile || name == pseudoFile || name == pseudoDir || name == pseudoProfile) continue; Symbol old = namedImportScope.lookup(name).sym; if (old != null) namedImportScope.remove(old); e.sym.flags_field |= Flags.PUBLIC; namedImportScope.enter(e.sym, scriptScope); } env.scriptScopes.clear(); // ??? VisageCompiledScript result = new VisageCompiledScript(); result.compiler = this; result.scriptScope = scriptScope; result.clazzName = ((VisageClassSymbol) scriptScope.owner).flatname.toString(); return result; } static String readFully(Reader reader) throws java.io.IOException { char[] arr = new char[8*1024]; // 8K at a time StringBuilder buf = new StringBuilder(); int numChars; while ((numChars = reader.read(arr, 0, arr.length)) > 0) { buf.append(arr, 0, numChars); } return buf.toString(); } /* * javac needs list of classes of a given package to resolve imports. * Refer to JavacFileManager.list() method. We want to get list of classes * for each package that is visible to our parent class loader. We do that by * looking for resource files that lists all the .class files for each package. * The format of the file is same as the output of "jar tf <jar-file>". Each * line lists a package directory or a .class file entry with full package * directory. */ private static final String CLASS_LIST_RESOURCE = "META-INF/CLASS.LIST"; // Look for class list resources from the given class loader and fill // in class list for each package seen. private static void fillPackageMap(ClassLoader loader, Map<String, List<String>> packageMap) throws IOException { Enumeration<URL> urls = loader.getResources(CLASS_LIST_RESOURCE); while (urls.hasMoreElements()) { URL url = urls.nextElement(); InputStream stream = url.openStream(); fillPackageMap(stream, packageMap); } } // Given an input stream of a resource that lists .class entries, fill // in the package to classes list map. private static void fillPackageMap(InputStream stream, Map<String, List<String>> packageMap) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(stream)); String line = null; while ((line = br.readLine()) != null) { if (line.endsWith(".class")) { int lastSlash = line.lastIndexOf('/'); String dir = (lastSlash == -1)? "" : line.substring(0, lastSlash); String file = (lastSlash == -1)? line : line.substring(lastSlash + 1); List<String> classList; String pkgName = dir.replace('/', '.'); if (! packageMap.containsKey(pkgName)) { classList = new ArrayList<String>(); packageMap.put(pkgName, classList); } else { classList = packageMap.get(pkgName); } classList.add(file.substring(0, file.indexOf(".class"))); } } } public Symbol lookup (Name name) { Scope.Entry entry = namedImportScope.lookup(name); return entry == null ? null : entry.sym; } public Symbol lookup (String name) { return lookup(names.fromString(name)); } }