001: /**
002: * InstantJ
003: *
004: * Copyright (C) 2002 Nils Meier
005: * Additional changes (C) 2002 Andy Thomas
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: */package instantj.compile.sun;
018:
019: import instantj.compile.CompilationFailedException;
020: import instantj.compile.CompiledClass;
021: import instantj.compile.Compiler;
022: import instantj.compile.Source;
023:
024: import java.io.ByteArrayOutputStream;
025: import java.io.FileNotFoundException;
026: import java.io.IOException;
027: import java.util.ArrayList;
028: import java.util.Collection;
029: import java.util.Enumeration;
030: import java.util.Iterator;
031: import java.util.List;
032: import java.util.Map;
033:
034: import sun.tools.java.Constants;
035:
036: /**
037: * The concrete implementation for Sun's compile stuff from
038: * sun.tools.javac.* - this might have to change if Sun comes
039: * up with changes in that package
040: *
041: * @author <A href="mailto:nils@meiers.net">Nils Meier</A>
042: * @author Kent Vidrine - added debugging flags
043: */
044: public class SunSourceCompiler extends Compiler implements
045: sun.tools.java.Constants {
046:
047: private static char INNERCLASS_PREFIX = sun.tools.java.Identifier.INNERCLASS_PREFIX;
048:
049: /**
050: * This implementation's compile steps - going for Sun's stuff
051: */
052: public CompiledClass compileImpl(Source source, boolean verbose,
053: List options, Map deps) throws CompilationFailedException {
054:
055: // Create an ErrorConsumer for collection Sun's compiler-callbacks
056: SunErrorConsumer consumer = new SunErrorConsumer(verbose);
057:
058: // Check ClassLoader
059: ClassLoader cl = Thread.currentThread().getContextClassLoader();
060: if (cl == null)
061: cl = getClass().getClassLoader();
062:
063: // Create the compiling BatchEnvironment
064: ContextClassPath ccp = new ContextClassPath(cl, deps);
065: sun.tools.javac.BatchEnvironment env = new sun.tools.javac.BatchEnvironment(
066: System.out, ccp, consumer);
067:
068: // Check options
069: if (options != null) {
070:
071: // .. and enable debugging (this IS tricky)
072: Iterator it = options.iterator();
073: while (it.hasNext()) {
074: String option = it.next().toString();
075: if (!option.startsWith("-g"))
076: continue;
077: if (option.indexOf(':') < 0) {
078: env.flags |= F_DEBUG_LINES | F_DEBUG_VARS
079: | F_DEBUG_SOURCE;
080: break;
081: }
082: if (option.indexOf("none") > 0)
083: break;
084: if (option.indexOf("lines") > 0)
085: env.flags |= F_DEBUG_LINES;
086: if (option.indexOf("vars") > 0)
087: env.flags |= F_DEBUG_VARS;
088: if (option.indexOf("source") > 0)
089: env.flags |= F_DEBUG_SOURCE;
090: break;
091: }
092:
093: // .. optimization?
094: if (options.contains("-O"))
095: env.flags |= F_OPT;
096:
097: }
098:
099: // .. and parse the source
100: try {
101: env.parseFile(new InstantClassFile(source));
102: } catch (FileNotFoundException e) {
103: // can't happen
104: }
105:
106: // Check what happened up to here
107: env.flushErrors();
108: if (consumer.hasErrors()) {
109: throw new CompilationFailedException("Errors in source",
110: consumer.getErrors());
111: }
112:
113: // Get and check the class definitions
114: CompiledClass result = null;
115: List inners = new ArrayList();
116: Enumeration classDefs = env.getClasses();
117: while (classDefs.hasMoreElements()) {
118:
119: sun.tools.java.ClassDeclaration decl = (sun.tools.java.ClassDeclaration) classDefs
120: .nextElement();
121:
122: // sticking to binary classes, not loaded and not "undefined".
123: if (decl.getStatus() == Constants.CS_BINARY
124: || decl.getStatus() == Constants.CS_UNDEFINED)
125: continue;
126:
127: // wrap
128: CompiledClass compiled = new CompiledClass(getName(decl),
129: getBytecode(consumer, env, decl));
130:
131: // is it the main class?
132: if (compiled.getName().equals(source.getClassName())) {
133: result = compiled; // is result
134: } else {
135: inners.add(compiled); // is inner
136: }
137:
138: // next decl
139: }
140:
141: // check if everything went as expected
142: if (result == null)
143: throw new CompilationFailedException(
144: "Didn't receive byte code for " + source.getName());
145:
146: // keep used classe from library
147: result.addDependencies(ccp.getUsedDeps());
148:
149: // keep inner classes
150: result.addInnerClasses(inners);
151:
152: // done
153: return result;
154: }
155:
156: /**
157: * Our ErrorConsumer collection compiler errors
158: */
159: private static class SunErrorConsumer implements
160: sun.tools.javac.ErrorConsumer {
161:
162: /** the tracked errors during compile */
163: private Collection errors = new ArrayList();
164:
165: /** whether errors will be verbose */
166: private boolean verbose;
167:
168: /**
169: * Constructor
170: */
171: /*package*/SunErrorConsumer(boolean verbose) {
172: this .verbose = verbose;
173: }
174:
175: /**
176: * Our way of keeping track with popping up errors - Sun's compiler
177: * notifies this method of occuring errors
178: */
179: public void pushError(String file, int line, String msg,
180: String referenceTxt, String referenceTxtPointer) {
181: if (verbose)
182: errors.add(file + "(" + line + "): " + msg);
183: else
184: errors.add(msg);
185: }
186:
187: /**
188: * Returns the errors
189: */
190: /*package*/Collection getErrors() {
191: return errors;
192: }
193:
194: /**
195: * Whether we have errors
196: */
197: /*package*/boolean hasErrors() {
198: return errors.size() > 0;
199: }
200:
201: } // EOC
202:
203: /**
204: * Resolve bytecode for given class-declaration
205: * @param consumer
206: * @param env
207: * @param decl
208: * @return byte[]
209: * @throws CompilationFailedException
210: */
211: public byte[] getBytecode(SunErrorConsumer consumer,
212: sun.tools.javac.BatchEnvironment env,
213: sun.tools.java.ClassDeclaration decl)
214: throws CompilationFailedException {
215:
216: // we need a little bit of space to store bytes in
217: ByteArrayOutputStream buf = new ByteArrayOutputStream(4096);
218:
219: // grab the class definition
220: sun.tools.java.ClassDefinition classDef;
221: try {
222: classDef = decl.getClassDefinition(env);
223: } catch (sun.tools.java.ClassNotFound e) {
224: // shouldn't happen
225: throw new Error("Unexpected " + e.getMessage());
226: }
227:
228: // either binary or source
229: if (classDef instanceof sun.tools.java.BinaryClass) {
230: // get binary bytes
231: try {
232: ((sun.tools.java.BinaryClass) classDef).write(env, buf);
233: } catch (IOException e) {
234: // shouldn't happen
235: throw new Error("Unexpected " + e.getMessage());
236: }
237: } else {
238: // check source and compile
239: sun.tools.javac.SourceClass src = (sun.tools.javac.SourceClass) classDef;
240: try {
241: src.check(env);
242: } catch (sun.tools.java.ClassNotFound e) {
243: // shouldn't happen
244: throw new Error("Unexpected " + e.getMessage());
245: }
246:
247: // Check again what happened up to here
248: env.flushErrors();
249: if (consumer.hasErrors()) {
250: throw new CompilationFailedException(
251: "Errors in source", consumer.getErrors());
252: }
253:
254: // compile the source
255: try {
256: src.compile(buf);
257: } catch (Exception e) {
258: // can't happen
259: }
260: // any more errors?
261: if (src.getNestError()) {
262: env.flushErrors();
263: throw new CompilationFailedException(
264: "Errors in source", consumer.getErrors());
265: }
266:
267: }
268:
269: // done
270: return buf.toByteArray();
271: }
272:
273: /**
274: * Resolve name for given ClassDeclaration
275: */
276: private String getName(sun.tools.java.ClassDeclaration decl) {
277:
278: // name of class in decl
279: String name = decl.getName().toString();
280: int innerMarker = name.indexOf(INNERCLASS_PREFIX);
281: if (innerMarker > 0) {
282: // .. could be 'somepackage.Foo Inner.Innerst'
283: name = name.substring(0, innerMarker - 1) + '$'
284: + name.substring(innerMarker + 1).replace('.', '$');
285: }
286:
287: // done
288: return name;
289: }
290:
291: } //SunSourceCompiler
|