001: package gnu.expr;
002:
003: import gnu.mapping.*;
004: import gnu.bytecode.*;
005: import gnu.mapping.Location; // As opposed to gnu.bytecode.Location
006: import gnu.text.*;
007: import java.io.*;
008: import gnu.kawa.reflect.StaticFieldLocation;
009: import java.net.URL;
010:
011: /**
012: * Class used to implement Scheme top-level environments.
013: * @author Per Bothner
014: */
015:
016: public class ModuleExp extends LambdaExp implements Externalizable {
017: public static final int EXPORT_SPECIFIED = LambdaExp.NEXT_AVAIL_FLAG;
018: public static final int STATIC_SPECIFIED = EXPORT_SPECIFIED << 1;
019: public static final int NONSTATIC_SPECIFIED = STATIC_SPECIFIED << 1;
020: public static final int SUPERTYPE_SPECIFIED = NONSTATIC_SPECIFIED << 1;
021: public static final int STATIC_RUN_SPECIFIED = SUPERTYPE_SPECIFIED << 1;
022: public static final int LAZY_DECLARATIONS = STATIC_RUN_SPECIFIED << 1;
023: public static final int IMMEDIATE = LAZY_DECLARATIONS << 1;
024:
025: public String getJavaName() {
026: String name = getName();
027: return name == null ? "lambda" : Compilation
028: .mangleName(name, 0);
029: }
030:
031: public ModuleExp() {
032: }
033:
034: /** Used to control which .zip file dumps are generated. */
035: public static String dumpZipPrefix;
036:
037: static int lastZipCounter;
038:
039: /** Numeric identifier for this interactive "command".
040: * Incremented by Shell.run, and used to set the module name,
041: * and maybe the name of the --debug-dump-zip output file.
042: * We increment and use this counter purely to ease debugging.
043: * (Since each module gets its own ClassLoader, they don't
044: * need to be named differently, and it doesn't matter
045: * if there is a race condition on the counter.) */
046: public static int interactiveCounter;
047:
048: public static Class evalToClass(Compilation comp, URL url) {
049: ModuleExp mexp = comp.getModule();
050: SourceMessages messages = comp.getMessages();
051: try {
052: ClassLoader parentLoader;
053: try {
054: parentLoader = Thread.currentThread()
055: .getContextClassLoader();
056: } catch (SecurityException ex) {
057: parentLoader = comp.getClass().getClassLoader();
058: }
059:
060: ArrayClassLoader loader = new ArrayClassLoader(parentLoader);
061: if (url == null)
062: url = Path.currentPath().toURL();
063: loader.setResourceContext(url);
064: comp.loader = loader;
065:
066: comp.minfo.loadByStages(Compilation.COMPILED);
067:
068: if (messages.seenErrors())
069: return null;
070:
071: java.util.zip.ZipOutputStream zout = null;
072: if (dumpZipPrefix != null) {
073: StringBuffer zipname = new StringBuffer(dumpZipPrefix);
074:
075: lastZipCounter++;
076: if (interactiveCounter > lastZipCounter)
077: lastZipCounter = interactiveCounter;
078: zipname.append(lastZipCounter);
079: zipname.append(".zip");
080: java.io.FileOutputStream zfout = new java.io.FileOutputStream(
081: zipname.toString());
082: zout = new java.util.zip.ZipOutputStream(zfout);
083: }
084:
085: for (int iClass = 0; iClass < comp.numClasses; iClass++) {
086: ClassType clas = comp.classes[iClass];
087: String className = clas.getName();
088: byte[] classBytes = clas.writeToArray();
089: loader.addClass(className, classBytes);
090: // This reduces memory leaks if we do lots of evalToClass.
091: clas.cleanupAfterCompilation();
092:
093: if (zout != null) {
094: String clname = className.replace('.', '/')
095: + ".class";
096: java.util.zip.ZipEntry zent = new java.util.zip.ZipEntry(
097: clname);
098: zent.setSize(classBytes.length);
099: java.util.zip.CRC32 crc = new java.util.zip.CRC32();
100: crc.update(classBytes);
101: zent.setCrc(crc.getValue());
102: zent.setMethod(java.util.zip.ZipEntry.STORED);
103: zout.putNextEntry(zent);
104: zout.write(classBytes);
105: }
106: }
107: if (zout != null) {
108: zout.close();
109: }
110:
111: /* DEBUGGING:
112: for (int iClass = 0; iClass < comp.numClasses; iClass++)
113: ClassTypeWriter.print(comp.classes[iClass], System.out, 0);
114: */
115:
116: Class clas = null;
117: for (int iClass = 0; iClass < comp.numClasses; iClass++) {
118: ClassType ctype = comp.classes[iClass];
119: Class cclass = loader.loadClass(ctype.getName());
120: ctype.setReflectClass(cclass);
121: ctype.setExisting(true);
122: if (iClass == 0)
123: clas = cclass;
124: }
125:
126: ModuleInfo minfo = comp.minfo;
127: minfo.moduleClass = clas;
128: int ndeps = minfo.numDependencies;
129:
130: for (int idep = 0; idep < ndeps; idep++) {
131: ModuleInfo dep = minfo.dependencies[idep];
132: if (dep.moduleClass == null)
133: dep.moduleClass = evalToClass(dep.comp, null);
134: comp.loader.addClass(dep.moduleClass);
135: }
136:
137: return clas;
138: } catch (java.io.IOException ex) {
139: throw new WrappedException("I/O error in lambda eval", ex);
140: } catch (ClassNotFoundException ex) {
141: throw new WrappedException(
142: "class not found in lambda eval", ex);
143: } catch (Throwable ex) {
144: comp.error('f', "internal compile error - caught " + ex);
145: throw WrappedException.wrapIfNeeded(ex);
146: }
147: }
148:
149: /** Flag to force compilation, even when not required. */
150: public static boolean alwaysCompile = false;
151:
152: public final static boolean evalModule(Environment env,
153: CallContext ctx, Compilation comp, URL url, OutPort msg)
154: throws Throwable {
155: comp.getLanguage().resolve(comp);
156: ModuleExp mexp = comp.getModule();
157: Environment orig_env = Environment.getCurrent();
158: Compilation orig_comp = Compilation.getCurrent();
159: SourceMessages messages = comp.getMessages();
160: ClassLoader savedLoader = null;
161: Thread thread = null; // Non-null if we need to restore context ClassLoader.
162: try {
163: if (env != orig_env)
164: Environment.setCurrent(env);
165: if (comp != orig_comp)
166: Compilation.setCurrent(comp);
167:
168: if (alwaysCompile || comp.mustCompile)
169: comp.addMainClass(mexp);
170:
171: comp.walkModule(mexp);
172: comp.setState(Compilation.WALKED);
173:
174: if (msg != null ? messages.checkErrors(msg, 20) : messages
175: .seenErrors())
176: return false;
177:
178: if (!alwaysCompile && !comp.mustCompile) { // optimization - don't generate unneeded Class.
179: if (Compilation.debugPrintFinalExpr) {
180: msg.println("[Evaluating final module \""
181: + mexp.getName() + "\":");
182: mexp.print(msg);
183: msg.println(']');
184: msg.flush();
185: }
186: mexp.body.apply(ctx);
187: } else {
188: if (comp.mainClass == null)
189: comp.addMainClass(mexp);
190:
191: try {
192: Class clas = evalToClass(comp, url);
193: if (clas == null)
194: return false;
195: try {
196: thread = Thread.currentThread();
197: savedLoader = thread.getContextClassLoader();
198: thread.setContextClassLoader(clas
199: .getClassLoader());
200: } catch (Throwable ex) {
201: thread = null;
202: }
203:
204: Object inst;
205: try {
206: inst = clas.getDeclaredField("$instance").get(
207: null);
208: } catch (NoSuchFieldException ex) {
209: inst = clas.newInstance();
210: }
211: /* #ifdef use:java.lang.Throwable.getCause */
212: catch (ExceptionInInitializerError ex) {
213: throw ex.getCause();
214: }
215: /* #endif */
216:
217: // Import declarations defined in module into the Environment.
218: for (Declaration decl = mexp.firstDecl(); decl != null; decl = decl
219: .nextDecl()) {
220: Object dname = decl.getSymbol();
221: if (decl.isPrivate() || dname == null)
222: continue;
223: Field fld = decl.field;
224: Symbol sym = dname instanceof Symbol ? (Symbol) dname
225: : Symbol.make("", dname.toString()
226: .intern());
227: Object property = comp.getLanguage()
228: .getEnvPropertyFor(decl);
229: // Would it be better to check if fld is FINAL?
230: // If it is, gets its value; otherwise create
231: // a FieldLocation to access it? FIXME.
232: Expression dvalue = decl.getValue();
233: if (decl.getFlag(Declaration.PROCEDURE
234: | Declaration.IS_CONSTANT
235: | Declaration.INDIRECT_BINDING)
236: && !(dvalue instanceof ReferenceExp && ((ReferenceExp) dvalue)
237: .getBinding().needsContext())) {
238: Object value;
239: if (dvalue instanceof QuoteExp
240: && dvalue != QuoteExp.undefined_exp)
241: value = ((QuoteExp) dvalue).getValue();
242: else
243: value = decl.field.getReflectField()
244: .get(null);
245: if (decl.isIndirectBinding())
246: env.addLocation(sym, property,
247: (Location) value);
248: else
249: env.define(sym, property, value);
250: // if IS_CONSTANT ... make Location constant. FIXME.
251: } else {
252: StaticFieldLocation loc = new StaticFieldLocation(
253: fld.getDeclaringClass(), fld
254: .getName());
255: loc.setDeclaration(decl);
256: env.addLocation(sym, property, loc);
257: }
258: }
259: if (msg != null ? messages.checkErrors(msg, 20)
260: : messages.seenErrors())
261: return false;
262: if (inst instanceof ModuleBody)
263: ((ModuleBody) inst).run(ctx);
264: } catch (IllegalAccessException ex) {
265: throw new RuntimeException(
266: "class illegal access: in lambda eval");
267: }
268: }
269: ctx.runUntilDone();
270: } finally {
271: if (env != orig_env)
272: Environment.setCurrent(orig_env);
273: if (comp != orig_comp)
274: Compilation.setCurrent(orig_comp);
275: if (thread != null)
276: thread.setContextClassLoader(savedLoader);
277: }
278: return true;
279: }
280:
281: ClassType super Type;
282: ClassType[] interfaces;
283:
284: ModuleInfo info;
285:
286: public final ClassType getSuperType() {
287: return super Type;
288: }
289:
290: public final void setSuperType(ClassType s) {
291: super Type = s;
292: }
293:
294: public final ClassType[] getInterfaces() {
295: return interfaces;
296: }
297:
298: public final void setInterfaces(ClassType[] s) {
299: interfaces = s;
300: }
301:
302: public final boolean isStatic() {
303: // In immediate mode there is no point in a non-static module:
304: // a static module is simpler and more efficient.
305: return (getFlag(STATIC_SPECIFIED) || ((gnu.expr.Compilation.moduleStatic > 0 || getFlag(IMMEDIATE))
306: && !getFlag(SUPERTYPE_SPECIFIED) && !getFlag(NONSTATIC_SPECIFIED)));
307: }
308:
309: /** True if module body (i.e. run) is called by class initializer. */
310: public boolean staticInitRun() {
311: return (isStatic() && (getFlag(STATIC_RUN_SPECIFIED) || Compilation.moduleStatic == 2));
312: }
313:
314: public void allocChildClasses(Compilation comp) {
315: declareClosureEnv();
316: if (!comp.usingCPStyle())
317: return;
318: allocFrame(comp);
319: }
320:
321: void allocFields(Compilation comp) {
322: // We want the create the loc$XXX Symbol fields for unknowns first,
323: // because it is possible some later Declaration's initializer may depend
324: // on it. Normally this is not an issue, as initializer are usually
325: // run as part of the "body" of the module, which is executed later.
326: // However, constant initializers are an exception - they are
327: // executed at init time.
328: for (Declaration decl = firstDecl(); decl != null; decl = decl
329: .nextDecl()) {
330: if ((decl.isSimple() && !decl.isPublic())
331: || decl.field != null)
332: continue;
333: if (decl.getFlag(Declaration.IS_UNKNOWN)
334: // We might have an unrefered unknown if the reference gets
335: // optimized away. For example references to <CLASSNAME>.
336: && decl.getFlag(Declaration.CAN_READ
337: | Declaration.CAN_CALL))
338: decl.makeField(comp, null);
339: }
340: for (Declaration decl = firstDecl(); decl != null; decl = decl
341: .nextDecl()) {
342: if (decl.field != null)
343: continue;
344: Expression value = decl.getValue();
345: if (((decl.isSimple() && !decl.isPublic()))
346: // Kludge - needed for macros - see Savannah bug #13601.
347: && !decl.isNamespaceDecl()
348: && !(decl.getFlag(Declaration.IS_CONSTANT) && decl
349: .getFlag(Declaration.CAN_READ
350: | Declaration.CAN_CALL))
351: && !(value instanceof ClassExp))
352: continue;
353: if (decl.getFlag(Declaration.IS_UNKNOWN))
354: continue;
355: if (value instanceof ModuleExp) // if decl set by a module-name command.
356: continue;
357: if (value instanceof LambdaExp
358: && !(value instanceof ClassExp)) {
359: ((LambdaExp) value).allocFieldFor(comp);
360: } else {
361: if (!(decl.getFlag(Declaration.IS_CONSTANT) || decl
362: .isAlias())
363: || value == QuoteExp.undefined_exp)
364: value = null;
365: decl.makeField(comp, value);
366: }
367: }
368: }
369:
370: protected Expression walk(ExpWalker walker) {
371: return walker.walkModuleExp(this );
372: }
373:
374: public void print(OutPort out) {
375: out.startLogicalBlock("(Module/", ")", 2);
376: Object sym = getSymbol();
377: if (sym != null) {
378: out.print(sym);
379: out.print('/');
380: }
381: out.print(id);
382: out.print('/');
383: out.writeSpaceFill();
384: out.startLogicalBlock("(", false, ")");
385: Declaration decl = firstDecl();
386: if (decl != null) {
387: out.print("Declarations:");
388: for (; decl != null; decl = decl.nextDecl()) {
389: out.writeSpaceFill();
390: decl.printInfo(out);
391: }
392: }
393: out.endLogicalBlock(")");
394: out.writeSpaceLinear();
395: if (body == null)
396: out.print("<null body>");
397: else
398: body.print(out);
399: out.endLogicalBlock(")");
400: }
401:
402: public Declaration firstDecl() {
403: synchronized (this ) {
404: if (getFlag(LAZY_DECLARATIONS))
405: info.setupModuleExp();
406: }
407: return decls;
408: }
409:
410: /** Return the class this module.
411: * If not set yet, sets it now, based on the source file name.
412: */
413: public ClassType classFor(Compilation comp) {
414: if (type != null && type != Compilation.typeProcedure)
415: return (ClassType) type;
416: String fileName = getFileName();
417: String mname = getName();
418: String className = null;
419: Path path = null;
420: if (mname != null)
421: fileName = mname;
422: else if (fileName == null) {
423: fileName = getName();
424: if (fileName == null)
425: fileName = "$unnamed_input_file$";
426: } else if (filename.equals("-")
427: || filename.equals("/dev/stdin")) {
428: fileName = getName();
429: if (fileName == null)
430: fileName = "$stdin$";
431: } else {
432: path = Path.valueOf(fileName);
433: fileName = path.getLast();
434: int dotIndex = fileName.lastIndexOf('.');
435: if (dotIndex > 0)
436: fileName = fileName.substring(0, dotIndex);
437: }
438: Path parentPath;
439: String parent;
440: if (getName() == null)
441: setName(fileName);
442: fileName = Compilation.mangleNameIfNeeded(fileName);
443: if (comp.classPrefix.length() == 0 && path != null
444: && !path.isAbsolute()
445: && (parentPath = path.getParent()) != null
446: && (parent = parentPath.toString()).length() > 0 // Probably redundant.
447: && parent.indexOf("..") < 0) {
448: parent = parent.replaceAll(System
449: .getProperty("file.separator"), "/");
450: if (parent.startsWith("./"))
451: parent = parent.substring(2);
452: className = parent.equals(".") ? fileName : Compilation
453: .mangleURI(parent)
454: + "." + fileName;
455: } else
456: className = comp.classPrefix + fileName;
457: ClassType clas = new ClassType(className);
458: setType(clas);
459: if (comp.mainLambda == this ) {
460: if (comp.mainClass == null)
461: comp.mainClass = clas;
462: else if (!className.equals(comp.mainClass.getName()))
463: comp.error('e', "inconsistent main class name: "
464: + className + " - old name: "
465: + comp.mainClass.getName());
466: }
467: return clas;
468: }
469:
470: public void writeExternal(ObjectOutput out) throws IOException {
471: String name = null;
472: if (type != null && type != Compilation.typeProcedure
473: && !type.isExisting())
474: // The class is (presumably) one we're currently generating.
475: // At run-time it may be loaded by a non-system ClassLoader.
476: // Thus compiling the class literal needs to use loadClassRef.
477: out.writeObject(type);
478: else {
479: if (name == null)
480: name = getName();
481: if (name == null)
482: name = getFileName();
483: out.writeObject(name);
484: }
485: }
486:
487: public void readExternal(ObjectInput in) throws IOException,
488: ClassNotFoundException {
489: Object name = in.readObject();
490: if (name instanceof ClassType) {
491: type = (ClassType) name;
492: setName(type.getName());
493: } else
494: setName((String) name);
495: flags |= LAZY_DECLARATIONS;
496: }
497: }
|