001: /*
002: * @(#)PnutsCompiler.java 1.4 05/06/02
003: *
004: * Copyright (c) 1997-2005 Sun Microsystems, Inc. All Rights Reserved.
005: *
006: * See the file "LICENSE.txt" for information on usage and redistribution
007: * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
008: */
009: package pnuts.tools;
010:
011: import java.io.ByteArrayOutputStream;
012: import java.io.File;
013: import java.io.FileInputStream;
014: import java.io.FileOutputStream;
015: import java.io.FileReader;
016: import java.io.IOException;
017: import java.io.InputStream;
018: import java.io.InputStreamReader;
019: import java.io.Reader;
020: import java.util.Enumeration;
021: import java.util.Properties;
022: import java.util.Vector;
023: import java.util.zip.ZipEntry;
024: import java.util.zip.ZipFile;
025: import java.util.zip.ZipOutputStream;
026: import java.net.URL;
027: import java.net.MalformedURLException;
028:
029: import pnuts.compiler.ClassFile;
030: import pnuts.compiler.Compiler;
031: import pnuts.compiler.Constants;
032: import pnuts.compiler.FileWriterHandler;
033: import pnuts.compiler.Opcode;
034: import pnuts.compiler.ZipWriterHandler;
035: import pnuts.lang.ParseException;
036: import pnuts.lang.Pnuts;
037:
038: /**
039: * A batch compiler for Pnuts. It reads Pnuts scripts and generates class files
040: * or a JAR file.
041: *
042: * <pre>
043: *
044: * Usage:
045: * pnutsc [ -d destination_directory ] [ -o jar_file ] [ -v ] [ -no_proxy ] [ -prefix name ] [ -main className ] [ -m module ] [ -impl pnutsImplClassName ] [ -encoding encoding_name ] [ -C dir ] { script_file | jar_file } ...
046: *
047: * </pre>
048: */
049: public class PnutsCompiler {
050:
051: private boolean verbose = false;
052: private boolean useDynamicProxy = true;
053: private boolean includeMain = false;
054: private boolean includeLineNumber = true;
055:
056: static String file_sep = System.getProperty("file.separator");
057: static String prefix;
058:
059: public PnutsCompiler() {
060: }
061:
062: /**
063: * Set verbose mode
064: *
065: * @param flag
066: * If true, verbose message is printed
067: */
068: public void setVerbose(boolean flag) {
069: this .verbose = flag;
070: }
071:
072: /**
073: * @param flag
074: * If true, main() method is generated.
075: */
076: public void includeMainMethod(boolean flag) {
077: this .includeMain = flag;
078: }
079:
080: public void includeLineNumber(boolean flag) {
081: this .includeLineNumber = flag;
082: }
083:
084: Compiler getCompiler(String name) {
085: Compiler compiler = new Compiler(name, false, useDynamicProxy);
086: compiler.includeMainMethod(includeMain);
087: compiler.includeLineNo(includeLineNumber);
088: return compiler;
089: }
090:
091: /**
092: * Switch dynamic proxy generation
093: *
094: * @param flag
095: * If true dynamic proxy is generated for every
096: * method/constructor call.
097: */
098: public void useDynamicProxy(boolean flag) {
099: useDynamicProxy = flag;
100: }
101:
102: /**
103: * Set the prefix of the class name.
104: *
105: * @param prefix
106: * the prefix. Default is null.
107: */
108: public static void setClassPrefix(String p) {
109: prefix = p;
110: }
111:
112: /**
113: * Compile a parsed expression and save the compiled code to a Zip file
114: *
115: * @param p
116: * a parsed expression
117: * @param name
118: * the class name of the compiled code
119: * @param zout
120: * a ZipOutputStream to which the compiled code is written
121: */
122: public void compileToZip(Pnuts p, String name, ZipOutputStream zout) {
123: ZipWriterHandler handler = new ZipWriterHandler(zout);
124: if (verbose) {
125: handler.setVerbose(true);
126: }
127: getCompiler(name).compile(p, handler);
128: }
129:
130: /**
131: * Compile a parsed expression and save the compiled code to class files.
132: *
133: * @param p
134: * a parsed expression
135: * @param name
136: * the class name of the compiled code
137: * @param dir
138: * the directory in which the class files are saved
139: */
140: public void compileToFile(Pnuts p, String name, File dir) {
141: FileWriterHandler handler = new FileWriterHandler(dir);
142: if (verbose) {
143: handler.setVerbose(true);
144: }
145: getCompiler(name).compile(p, handler);
146: }
147:
148: void compileToZip(File file, ZipOutputStream zout,
149: Vector classNames, String encoding) throws IOException,
150: ParseException {
151: Reader reader;
152: String fileName = file.getName();
153: int idx = fileName.lastIndexOf('.');
154: if (idx > 0) {
155: String ext = fileName.substring(idx + 1).toLowerCase();
156: if ("jar".equals(ext) || "zip".equals(ext)) {
157: ZipFile zfile = new ZipFile(file);
158: for (Enumeration e = zfile.entries(); e
159: .hasMoreElements();) {
160: ZipEntry entry = (ZipEntry) e.nextElement();
161: String entryName = entry.getName();
162: if (entryName.endsWith(".pnut")) {
163: if (verbose) {
164: System.out.println(entryName);
165: }
166: InputStream in = zfile.getInputStream(entry);
167: if (encoding == null) {
168: reader = new InputStreamReader(in);
169: } else {
170: reader = new InputStreamReader(in, encoding);
171: }
172: Pnuts p = Pnuts.parse(reader);
173: try {
174: p.setScriptSource(new URL("jar:"
175: + file.toURL() + "!/" + entryName));
176: } catch (MalformedURLException mue) {
177: // skip
178: }
179: String className = className(entryName);
180: compileToZip(p, className, zout);
181: classNames.addElement(className);
182: } else if (entryName.endsWith(".pnc")) {
183: if (verbose) {
184: System.out.println(entryName);
185: }
186: InputStream in = zfile.getInputStream(entry);
187: if (encoding == null) {
188: reader = new InputStreamReader(in);
189: } else {
190: reader = new InputStreamReader(in, encoding);
191: }
192: ZipWriterHandler handler = new ZipWriterHandler(
193: zout);
194: if (verbose) {
195: handler.setVerbose(true);
196: }
197: URL url = new URL("jar:" + file.toURL() + "!/"
198: + entryName);
199: String className = className(entryName);
200: getCompiler(className).compileClassScript(
201: reader, url, handler);
202: }
203: }
204: return;
205: } else if ("pnc".equals(ext)) {
206: InputStream in = new FileInputStream(file);
207: if (encoding == null) {
208: reader = new InputStreamReader(in);
209: } else {
210: reader = new InputStreamReader(in, encoding);
211: }
212: ZipWriterHandler handler = new ZipWriterHandler(zout);
213: if (verbose) {
214: handler.setVerbose(true);
215: }
216: URL url = file.toURL();
217: String className = className(fileName);
218: getCompiler(className).compileClassScript(reader, url,
219: handler);
220: return;
221: }
222: }
223: if (encoding == null) {
224: reader = new FileReader(file);
225: } else {
226: reader = new InputStreamReader(new FileInputStream(file),
227: encoding);
228: }
229: Pnuts pn = Pnuts.parse(reader);
230: pn.setScriptSource(file.toURL());
231: String className = className(fileName);
232: compileToZip(pn, className, zout);
233: classNames.addElement(className);
234: }
235:
236: void compileToFile(File file, File dir, Vector classNames,
237: String encoding) throws IOException, ParseException {
238: Reader reader;
239: String fileName = file.getName();
240: int idx = fileName.lastIndexOf('.');
241: if (idx > 0) {
242: String ext = fileName.substring(idx + 1).toLowerCase();
243: if ("jar".equals(ext) || "zip".equals(ext)) {
244: ZipFile zfile = new ZipFile(file);
245: for (Enumeration e = zfile.entries(); e
246: .hasMoreElements();) {
247: ZipEntry entry = (ZipEntry) e.nextElement();
248: String entryName = entry.getName();
249: if (entryName.endsWith(".pnut")) {
250: if (verbose) {
251: System.out.println(entryName);
252: }
253: InputStream in = zfile.getInputStream(entry);
254: if (encoding == null) {
255: reader = new InputStreamReader(in);
256: } else {
257: reader = new InputStreamReader(in, encoding);
258: }
259: Pnuts p = Pnuts.parse(reader);
260: try {
261: p.setScriptSource(new URL("jar:"
262: + file.toURL() + "!/" + entryName));
263: } catch (MalformedURLException mue) {
264: // skip
265: }
266: String className = className(entryName);
267: compileToFile(p, className, dir);
268: classNames.addElement(className);
269: } else if (entryName.endsWith(".pnc")) {
270: if (verbose) {
271: System.out.println(entryName);
272: }
273: InputStream in = zfile.getInputStream(entry);
274: if (encoding == null) {
275: reader = new InputStreamReader(in);
276: } else {
277: reader = new InputStreamReader(in, encoding);
278: }
279: FileWriterHandler handler = new FileWriterHandler(
280: dir);
281: if (verbose) {
282: handler.setVerbose(true);
283: }
284: URL url = new URL("jar:" + file.toURL() + "!/"
285: + entryName);
286: String className = className(entryName);
287: getCompiler(className).compileClassScript(
288: reader, url, handler);
289: }
290: }
291: return;
292: } else if ("pnc".equals(ext)) {
293: InputStream in = new FileInputStream(file);
294: if (encoding == null) {
295: reader = new InputStreamReader(in);
296: } else {
297: reader = new InputStreamReader(in, encoding);
298: }
299: FileWriterHandler handler = new FileWriterHandler(dir);
300: if (verbose) {
301: handler.setVerbose(true);
302: }
303: URL url = file.toURL();
304: String className = className(fileName);
305: getCompiler(className).compileClassScript(reader, url,
306: handler);
307: return;
308: }
309: }
310: if (encoding == null) {
311: reader = new FileReader(file);
312: } else {
313: reader = new InputStreamReader(new FileInputStream(file),
314: encoding);
315: }
316: Pnuts pn = Pnuts.parse(reader);
317: pn.setScriptSource(file.toURL());
318: String className = className(fileName);
319: compileToFile(pn, className, dir);
320: classNames.addElement(className);
321: }
322:
323: static String className(String name) {
324: int pos = name.lastIndexOf('.');
325: String cname = name.replace('-', '_');
326: if (pos > 0) {
327: cname = cname.substring(0, pos).replace(file_sep.charAt(0),
328: '.');
329: if (!"/".equals(file_sep)) {
330: cname = cname.replace('/', '.');
331: }
332: }
333: if (prefix != null) {
334: if (!"".equals(prefix) && !prefix.endsWith(".")) {
335: prefix = prefix + ".";
336: }
337: cname = prefix + cname;
338: }
339: return cname;
340: }
341:
342: /**
343: * <pre>
344: * public class mainClassName extends Runtime {
345: * public Object run(Context ctx){
346: * ctx.usePackage(module1);
347: * ...
348: * new className1().run((Context)ctx.clone());
349: * ...
350: * }
351: * public static void main(String args[]) {
352: * Context context = new Context();
353: * context.getCurrentPackage().set("$args".intern(), args);
354: * new mainClassName().run(context);
355: * }
356: * }
357: * </pre>
358: */
359: public static ClassFile generateMainClass(String mainClassName,
360: String arg0, String[] modules, String[] classNames,
361: String pnutsImplClassName) {
362: ClassFile cf = new ClassFile(mainClassName,
363: "pnuts.lang.Runtime", null, Constants.ACC_PUBLIC);
364: cf.openMethod("<init>", "()V", Constants.ACC_PUBLIC);
365: cf.add(Opcode.ALOAD_0);
366: cf.add(Opcode.INVOKESPECIAL, "pnuts.lang.Runtime", "<init>",
367: "()", "V");
368: cf.add(Opcode.RETURN);
369: cf.closeMethod();
370:
371: cf.openMethod("run",
372: "(Lpnuts/lang/Context;)Ljava/lang/Object;",
373: Constants.ACC_PUBLIC);
374: cf.add(Opcode.ALOAD_1);
375: int ctx = cf.getLocal();
376: cf.storeLocal(ctx);
377:
378: for (int i = 0; i < modules.length; i++) {
379: cf.loadLocal(ctx);
380: cf.add(Opcode.LDC, cf.addConstant(modules[i]));
381: cf.add(Opcode.INVOKEVIRTUAL, "pnuts.lang.Context",
382: "usePackage", "(Ljava/lang/String;)", "Z");
383: cf.add(Opcode.POP);
384: }
385:
386: for (int i = 0; i < classNames.length; i++) {
387: if (i > 0) {
388: cf.add(Opcode.POP);
389: }
390: String name = classNames[i];
391: cf.add(Opcode.NEW, name);
392: cf.add(Opcode.DUP);
393: cf.add(Opcode.INVOKESPECIAL, name, "<init>", "()", "V");
394: cf.loadLocal(ctx);
395: cf.add(Opcode.INVOKEVIRTUAL, "pnuts.lang.Context", "clone",
396: "()", "Ljava/lang/Object;");
397: cf.add(Opcode.CHECKCAST, "pnuts.lang.Context");
398: cf.add(Opcode.INVOKEVIRTUAL, name, "run",
399: "(Lpnuts/lang/Context;)", "Ljava/lang/Object;");
400: }
401: cf.add(Opcode.ARETURN);
402: cf.closeMethod();
403:
404: cf.openMethod("main", "([Ljava/lang/String;)V",
405: (short) (Constants.ACC_PUBLIC | Constants.ACC_STATIC));
406:
407: cf.add(Opcode.ALOAD_0);
408: cf.add(Opcode.ARRAYLENGTH);
409: int len = cf.getLocal();
410: cf.istoreLocal(len);
411: cf.iloadLocal(len);
412: cf.add(Opcode.ICONST_1);
413: cf.add(Opcode.IADD);
414: cf.add(Opcode.ANEWARRAY, "java.lang.String");
415: int array = cf.getLocal();
416: cf.storeLocal(array);
417: cf.add(Opcode.ALOAD_0);
418: cf.add(Opcode.ICONST_0);
419: cf.loadLocal(array);
420: cf.add(Opcode.ICONST_1);
421: cf.iloadLocal(len);
422: cf.add(Opcode.INVOKESTATIC, "java.lang.System", "arraycopy",
423: "(Ljava/lang/Object;ILjava/lang/Object;II)", "V");
424:
425: cf.loadLocal(array);
426: cf.pushInteger(0);
427: cf.add(Opcode.LDC, cf.addConstant(arg0));
428: cf.add(Opcode.AASTORE);
429:
430: cf.add(Opcode.NEW, "pnuts.lang.Context");
431: cf.add(Opcode.DUP);
432: cf.add(Opcode.INVOKESPECIAL, "pnuts.lang.Context", "<init>",
433: "()", "V");
434: ctx = cf.getLocal();
435: cf.storeLocal(ctx);
436: cf.loadLocal(ctx);
437: cf.add(Opcode.INVOKEVIRTUAL, "pnuts.lang.Context",
438: "getCurrentPackage", "()", "Lpnuts/lang/Package;");
439:
440: cf.add(Opcode.LDC, cf.addConstant("$args"));
441: cf.add(Opcode.INVOKEVIRTUAL, "java.lang.String", "intern",
442: "()", "Ljava/lang/String;");
443: cf.loadLocal(array);
444: cf.add(Opcode.INVOKEVIRTUAL, "pnuts.lang.Package", "set",
445: "(Ljava/lang/String;Ljava/lang/Object;)", "V");
446:
447: cf.add(Opcode.NEW, mainClassName);
448: cf.add(Opcode.DUP);
449: cf
450: .add(Opcode.INVOKESPECIAL, mainClassName, "<init>",
451: "()", "V");
452:
453: if (pnutsImplClassName != null) {
454: cf.loadLocal(ctx);
455: cf.add(Opcode.NEW, pnutsImplClassName);
456: cf.add(Opcode.DUP);
457: cf.add(Opcode.INVOKESPECIAL, pnutsImplClassName, "<init>",
458: "()", "V");
459: cf.add(Opcode.INVOKEVIRTUAL, "pnuts.lang.Context",
460: "setImplementation",
461: "(Lpnuts/lang/Implementation;)", "V");
462: }
463:
464: cf.loadLocal(ctx);
465: cf.add(Opcode.INVOKEINTERFACE, "pnuts.lang.Executable", "run",
466: "(Lpnuts/lang/Context;)", "Ljava/lang/Object;");
467:
468: cf.add(Opcode.POP);
469: cf.add(Opcode.RETURN);
470: cf.closeMethod();
471: return cf;
472: }
473:
474: static void setProperty(String name, String value) {
475: Properties prop = System.getProperties();
476: prop.put(name, value);
477: System.setProperties(prop);
478: }
479:
480: /**
481: * Compile Pnuts scripts and save the generated code into a ZIP file.
482: *
483: * @param zout
484: * the ZipOutputStream to which the generated byte code is
485: * written
486: * @param files
487: * the script files
488: * @param dirs
489: * the directories in which each script file resides
490: * @param modules
491: * used modules
492: * @param mainClassName
493: * the main class name
494: */
495: public void compileToZip(ZipOutputStream zout, String[] files,
496: String[] dirs, String[] modules, String mainClassName,
497: String pnutsImplClassName, String encoding)
498: throws IOException, ParseException {
499: if (mainClassName != null) {
500: ZipEntry meta_inf = new ZipEntry("meta-inf/manifest.mf");
501: String manifest = "Manifest-Version: 1.0\nMain-Class: "
502: + mainClassName + "\nCreated-By: pnutsc\n";
503: zout.putNextEntry(meta_inf);
504: zout.write(manifest.getBytes());
505: }
506: String currentDir = System.getProperty("user.dir");
507: try {
508: Vector classNames = new Vector();
509: for (int i = 0; i < files.length; i++) {
510: String fileName = files[i];
511: setProperty("user.dir", new File(dirs[i])
512: .getCanonicalPath());
513: compileToZip(new File(fileName), zout, classNames,
514: encoding);
515: }
516: if (mainClassName != null) {
517: String[] classNameArray = new String[classNames.size()];
518: for (int i = 0; i < classNameArray.length; i++) {
519: classNameArray[i] = (String) classNames
520: .elementAt(i);
521: }
522: ZipEntry entry = new ZipEntry(mainClassName.replace(
523: '.', '/')
524: + ".class");
525: zout.putNextEntry(entry);
526: ClassFile cf = generateMainClass(mainClassName,
527: files[0], modules, classNameArray,
528: pnutsImplClassName);
529: cf.write(zout);
530: }
531: } finally {
532: setProperty("user.dir", currentDir);
533: }
534: }
535:
536: /**
537: * Compile Pnuts scripts and save the generated code into class files.
538: *
539: * @param dir
540: * the directory in which the generated byte code is saved
541: * @param files
542: * the script files
543: * @param dirs
544: * the directories in which each script file resides
545: * @param modules
546: * used modules
547: * @param mainClassName
548: * the main class name
549: */
550: public void compileToFile(File dir, String[] files, String[] dirs,
551: String[] modules, String mainClassName,
552: String pnutsImplClassName, String encoding)
553: throws IOException, ParseException {
554: if (mainClassName != null) {
555: File meta_dir = new File(dir, "meta-inf");
556: if (!meta_dir.exists()) {
557: meta_dir.mkdirs();
558: }
559: String manifest = "Manifest-Version: 1.0\nMain-Class: "
560: + mainClassName + "\nCreated-By: pnutsc\n";
561: FileOutputStream fout = new FileOutputStream(new File(
562: meta_dir, "manifest.mf"));
563: fout.write(manifest.getBytes());
564: fout.close();
565: }
566: Vector classNames = new Vector();
567: String currentDir = System.getProperty("user.dir");
568: try {
569: for (int i = 0; i < files.length; i++) {
570: String fileName = files[i];
571: setProperty("user.dir", new File(dirs[i])
572: .getCanonicalPath());
573: compileToFile(new File(fileName), dir, classNames,
574: encoding);
575: }
576: if (mainClassName != null) {
577: String[] classNameArray = new String[classNames.size()];
578: for (int i = 0; i < classNameArray.length; i++) {
579: classNameArray[i] = (String) classNames
580: .elementAt(i);
581: }
582: FileOutputStream main = new FileOutputStream(
583: new File(dir, mainClassName.replace('.', '/')
584: + ".class"));
585: ClassFile cf = generateMainClass(mainClassName,
586: files[0], modules, classNameArray,
587: pnutsImplClassName);
588: cf.write(main);
589: main.close();
590: }
591: } finally {
592: setProperty("user.dir", currentDir);
593: }
594: }
595:
596: public static void main(String args[]) throws Throwable {
597: int nargs = args.length;
598:
599: if (nargs == 0) {
600: Main.printHelp("pnutsc");
601: return;
602: }
603:
604: PnutsCompiler compiler = new PnutsCompiler();
605:
606: String dest = System.getProperty("user.dir");
607: String base = dest;
608: String jar = null;
609: Vector files = new Vector();
610: Vector dirs = new Vector();
611: Vector modules = new Vector();
612: boolean module = false;
613: String mainClassName = null;
614: String pnutsImpl = null;
615: String encoding = null;
616:
617: for (int i = 0; i < nargs; i++) {
618: if ("-d".equals(args[i])) {
619: dest = args[++i];
620: } else if ("-no_proxy".equals(args[i])) {
621: compiler.useDynamicProxy(false);
622: } else if ("-o".equals(args[i])) {
623: jar = args[++i];
624: } else if ("-v".equals(args[i])) {
625: compiler.setVerbose(true);
626: } else if ("-O".equals(args[i])) {
627: compiler.includeLineNumber(false);
628: } else if ("-C".equals(args[i])) {
629: base = args[++i];
630: } else if ("-prefix".equals(args[i])) {
631: PnutsCompiler.prefix = args[++i];
632: } else if ("-main".equals(args[i])) {
633: mainClassName = args[++i];
634: } else if ("-m".equals(args[i])) {
635: modules.addElement(args[++i]);
636: } else if ("-impl".equals(args[i])) {
637: pnutsImpl = args[++i];
638: } else if ("-encoding".equals(args[i])) {
639: encoding = args[++i];
640: } else if ("-help".equals(args[i])) {
641: Main.printHelp("pnutsc");
642: System.exit(0);
643: } else {
644: files.addElement(args[i]);
645: dirs.addElement(base);
646: }
647: }
648:
649: File dir = new File(dest);
650: if (!dir.exists()) {
651: System.err.println("Directory " + dest + " does not exist");
652: }
653:
654: if (files.size() == 0) {
655: Main.printHelp("pnutsc");
656: return;
657: }
658:
659: String[] fileArray = new String[files.size()];
660: for (int i = 0; i < fileArray.length; i++) {
661: fileArray[i] = (String) files.elementAt(i);
662: }
663: String[] dirArray = new String[dirs.size()];
664: for (int i = 0; i < dirArray.length; i++) {
665: dirArray[i] = (String) dirs.elementAt(i);
666: }
667: String[] moduleArray = new String[modules.size()];
668: for (int i = 0; i < moduleArray.length; i++) {
669: moduleArray[i] = (String) modules.elementAt(i);
670: }
671:
672: if (jar != null) {
673: if (new File(jar).exists()) {
674: ByteArrayOutputStream bout = new ByteArrayOutputStream();
675: ZipOutputStream zout = new ZipOutputStream(bout);
676:
677: ZipFile zfile = new ZipFile(jar);
678: byte[] buf = new byte[512];
679:
680: for (Enumeration e = zfile.entries(); e
681: .hasMoreElements();) {
682: ZipEntry entry = (ZipEntry) e.nextElement();
683: InputStream in = zfile.getInputStream(entry);
684: zout.putNextEntry((ZipEntry) entry.clone());
685: int n;
686: while ((n = in.read(buf)) != -1) {
687: zout.write(buf, 0, n);
688: }
689: }
690: compiler
691: .compileToZip(zout, fileArray, dirArray,
692: moduleArray, mainClassName, pnutsImpl,
693: encoding);
694: zout.finish();
695: FileOutputStream fout = new FileOutputStream(jar);
696: bout.writeTo(fout);
697: fout.close();
698: } else {
699: ZipOutputStream zout = new ZipOutputStream(
700: new FileOutputStream(jar));
701: compiler
702: .compileToZip(zout, fileArray, dirArray,
703: moduleArray, mainClassName, pnutsImpl,
704: encoding);
705: zout.close();
706: }
707: } else {
708: compiler.compileToFile(dir, fileArray, dirArray,
709: moduleArray, mainClassName, pnutsImpl, encoding);
710: }
711: }
712: }
|