001: /* -*- mode: Java; c-basic-offset: 2; -*- */
002:
003: /***
004: * Compiler.java
005: * Author: Oliver Steele, P T Withington
006: * Description: JavaScript -> SWF bytecode compiler
007: */package org.openlaszlo.sc;
008:
009: import java.io.*;
010: import java.util.*;
011: import java.util.regex.Pattern;
012: import java.text.SimpleDateFormat;
013: import java.text.DecimalFormat;
014:
015: import org.openlaszlo.server.LPS;
016: import org.openlaszlo.sc.parser.*;
017: import org.openlaszlo.sc.Translator;
018:
019: // Values
020: import org.openlaszlo.sc.Values;
021:
022: // Instructions
023: import org.openlaszlo.sc.Instructions;
024: import org.openlaszlo.sc.Instructions.Instruction;
025: import org.openlaszlo.sc.InstructionPrinter;
026:
027: public class Compiler {
028: // The parse tree is stored with the key (fname) and the
029: // value (ASTProgram, hasIncludes).
030: // It doesn't save any time to persist this cache to disk.
031: public static ScriptCompilerCache CachedParses;
032: // The instructions are stored with the keys (fname, cpass) where
033: // cpass is one of the compiler passes (1 or 2). The checksum in
034: // both cases is the file content string.
035: // It costs 10s to persist this to disk, but speeds up subsequent
036: // compiles.
037: // Instantiate this lazily, so that we don't construct it in server
038: // mode (since the call to os.getenv in the cache constructor won't
039: // work there).
040: public static ScriptCompilerCache CachedInstructions;
041:
042: public OptionMap options;
043:
044: //
045: // Compiler Facade
046: //
047: public Compiler(Map initialOptions) {
048: this .options = new OptionMap(initialOptions);
049: if (!options.containsKey(ACTIVATION_OBJECT)) {
050: options.putBoolean(ACTIVATION_OBJECT, !options
051: .getBoolean(FLASH_COMPILER_COMPATABILITY));
052: }
053: if (!options.containsKey(COMPILE_TIME_CONSTANTS)) {
054: options.put(COMPILE_TIME_CONSTANTS, new HashMap());
055: }
056: // TODO: [2002-1-05 ows] enable this instead of the line that
057: // follows it, once the sources comply
058: //- options.put(ALLOW_ROOT,
059: //- options.get(FLASH_COMPILER_COMPATABILITY))
060: if (!options.containsKey(ALLOW_ROOT)) {
061: options.putBoolean(ALLOW_ROOT, true);
062: }
063: if (!options.containsKey(OBFUSCATE)) {
064: options.putBoolean(OBFUSCATE, false);
065: }
066: if (!options.containsKey(RUNTIME)) {
067: options.put(RUNTIME, LPS
068: .getProperty("compiler.runtime.default", LPS
069: .getRuntimeDefault()));
070: }
071: defaultOptions();
072: if (options.getBoolean(PRINT_COMPILER_OPTIONS)) {
073: System.err.println("init compiler options"
074: + options.toString());
075: }
076: }
077:
078: public Compiler() {
079: this (new HashMap());
080: }
081:
082: // Map for options
083: public static class OptionMap extends HashMap {
084: OptionMap() {
085: super ();
086: }
087:
088: OptionMap(Map m) {
089: super (m);
090: }
091:
092: OptionMap(List pairs) {
093: for (Iterator i = pairs.iterator(); i.hasNext();) {
094: List pair = (List) i.next();
095: put(pair.get(0), pair.get(1));
096: }
097: }
098:
099: // Python
100: public OptionMap copy() {
101: return (OptionMap) clone();
102: }
103:
104: public Object get(Object key) {
105: if (containsKey(key)) {
106: return (super .get(key));
107: }
108: return null;
109: }
110:
111: // For Jython
112: public Object get(Object key, Object deflt) {
113: if (containsKey(key)) {
114: return (get(key));
115: }
116: return deflt;
117: }
118:
119: public boolean getBoolean(Object key) {
120: boolean result = false;
121: Object value = null;
122: if (containsKey(key)) {
123: value = get(key);
124: }
125: if (value != null) {
126: if (value instanceof String) {
127: result = "true".equalsIgnoreCase((String) value);
128: } else if (value instanceof Integer) {
129: result = (!Integer.valueOf("0").equals(value));
130: } else {
131: result = ((Boolean) value).booleanValue();
132: }
133: }
134: return result;
135: }
136:
137: public void putBoolean(Object key, boolean value) {
138: put(key, Boolean.valueOf(value));
139: }
140:
141: public void putBoolean(Object key, String value) {
142: put(key, Boolean.valueOf(value));
143: }
144: }
145:
146: // Error support
147: public static String getLocationString(SimpleNode node) {
148: StringBuffer location = new StringBuffer();
149: if (node != null) {
150: if (node.filename != null) {
151: location.append(node.filename);
152: if (node.beginLine != 0) {
153: location.append("#");
154: location.append(Integer.toString(node.beginLine));
155: location.append(".");
156: location.append(Integer.toString(node.beginColumn));
157: }
158: }
159: }
160: return location.toString();
161: }
162:
163: public static Instruction NONE = Instructions.NONE;
164: public static Instruction NextFrame = Instructions.NextFrame;
165: public static Instruction PreviousFrame = Instructions.PreviousFrame;
166: public static Instruction PLAY = Instructions.PLAY;
167: public static Instruction STOP = Instructions.STOP;
168: public static Instruction ToggleQuality = Instructions.ToggleQuality;
169: public static Instruction StopSounds = Instructions.StopSounds;
170: public static Instruction NumericAdd = Instructions.NumericAdd;
171: public static Instruction SUBTRACT = Instructions.SUBTRACT;
172: public static Instruction MULTIPLY = Instructions.MULTIPLY;
173: public static Instruction DIVIDE = Instructions.DIVIDE;
174: public static Instruction OldEquals = Instructions.OldEquals;
175: public static Instruction OldLessThan = Instructions.OldLessThan;
176: public static Instruction LogicalAnd = Instructions.LogicalAnd;
177: public static Instruction LogicalOr = Instructions.LogicalOr;
178: public static Instruction NOT = Instructions.NOT;
179: public static Instruction StringEqual = Instructions.StringEqual;
180: public static Instruction StringLength = Instructions.StringLength;
181: public static Instruction SUBSTRING = Instructions.SUBSTRING;
182: public static Instruction POP = Instructions.POP;
183: public static Instruction INT = Instructions.INT;
184: public static Instruction GetVariable = Instructions.GetVariable;
185: public static Instruction SetVariable = Instructions.SetVariable;
186: public static Instruction SetTargetExpression = Instructions.SetTargetExpression;
187: public static Instruction StringConcat = Instructions.StringConcat;
188: public static Instruction GetProperty = Instructions.GetProperty;
189: public static Instruction SetProperty = Instructions.SetProperty;
190: public static Instruction DuplicateMovieClip = Instructions.DuplicateMovieClip;
191: public static Instruction RemoveClip = Instructions.RemoveClip;
192: public static Instruction TRACE = Instructions.TRACE;
193: public static Instruction StartDragMovie = Instructions.StartDragMovie;
194: public static Instruction StopDragMovie = Instructions.StopDragMovie;
195: public static Instruction StringLessThan = Instructions.StringLessThan;
196: public static Instruction RANDOM = Instructions.RANDOM;
197: public static Instruction MBLENGTH = Instructions.MBLENGTH;
198: public static Instruction ORD = Instructions.ORD;
199: public static Instruction CHR = Instructions.CHR;
200: public static Instruction GetTimer = Instructions.GetTimer;
201: public static Instruction MBSUBSTRING = Instructions.MBSUBSTRING;
202: public static Instruction MBORD = Instructions.MBORD;
203: public static Instruction MBCHR = Instructions.MBCHR;
204: public static Instruction GotoFrame = Instructions.GotoFrame;
205: public static Instruction GetUrl = Instructions.GetUrl;
206: public static Instruction WaitForFrame = Instructions.WaitForFrame;
207: public static Instruction SetTarget = Instructions.SetTarget;
208: public static Instruction GotoLabel = Instructions.GotoLabel;
209: public static Instruction WaitForFrameExpression = Instructions.WaitForFrameExpression;
210: public static Instruction PUSH = Instructions.PUSH;
211: public static Instruction BRANCH = Instructions.BRANCH;
212: public static Instruction GetURL2 = Instructions.GetURL2;
213: public static Instruction BranchIfTrue = Instructions.BranchIfTrue;
214: public static Instruction CallFrame = Instructions.CallFrame;
215: public static Instruction GotoExpression = Instructions.GotoExpression;
216: public static Instruction DELETE = Instructions.DELETE;
217: public static Instruction DELETE2 = Instructions.DELETE2;
218: public static Instruction VarEquals = Instructions.VarEquals;
219: public static Instruction CallFunction = Instructions.CallFunction;
220: public static Instruction RETURN = Instructions.RETURN;
221: public static Instruction MODULO = Instructions.MODULO;
222: public static Instruction NEW = Instructions.NEW;
223: public static Instruction VAR = Instructions.VAR;
224: public static Instruction InitArray = Instructions.InitArray;
225: public static Instruction InitObject = Instructions.InitObject;
226: public static Instruction TypeOf = Instructions.TypeOf;
227: public static Instruction TargetPath = Instructions.TargetPath;
228: public static Instruction ENUMERATE = Instructions.ENUMERATE;
229: public static Instruction ADD = Instructions.ADD;
230: public static Instruction LessThan = Instructions.LessThan;
231: public static Instruction EQUALS = Instructions.EQUALS;
232: public static Instruction ObjectToNumber = Instructions.ObjectToNumber;
233: public static Instruction ObjectToString = Instructions.ObjectToString;
234: public static Instruction DUP = Instructions.DUP;
235: public static Instruction SWAP = Instructions.SWAP;
236: public static Instruction GetMember = Instructions.GetMember;
237: public static Instruction SetMember = Instructions.SetMember;
238: public static Instruction Increment = Instructions.Increment;
239: public static Instruction Decrement = Instructions.Decrement;
240: public static Instruction CallMethod = Instructions.CallMethod;
241: public static Instruction NewMethod = Instructions.NewMethod;
242: public static Instruction BitwiseAnd = Instructions.BitwiseAnd;
243: public static Instruction BitwiseOr = Instructions.BitwiseOr;
244: public static Instruction BitwiseXor = Instructions.BitwiseXor;
245: public static Instruction ShiftLeft = Instructions.ShiftLeft;
246: public static Instruction ShiftRight = Instructions.ShiftRight;
247: public static Instruction UShiftRight = Instructions.UShiftRight;
248: public static Instruction SetRegister = Instructions.SetRegister;
249: public static Instruction CONSTANTS = Instructions.CONSTANTS;
250: public static Instruction WITH = Instructions.WITH;
251: public static Instruction DefineFunction = Instructions.DefineFunction;
252: public static Instruction DefineFunction2 = Instructions.DefineFunction2;
253: public static Instruction InstanceOf = Instructions.InstanceOf;
254: public static Instruction EnumerateValue = Instructions.EnumerateValue;
255: public static Instruction StrictEquals = Instructions.StrictEquals;
256: public static Instruction GreaterThan = Instructions.GreaterThan;
257: public static Instruction StringGreaterThan = Instructions.StringGreaterThan;
258: public static Instruction BranchIfFalse = Instructions.BranchIfFalse;
259: public static Instruction LABEL = Instructions.LABEL;
260: public static Instruction COMMENT = Instructions.COMMENT;
261: public static Instruction CHECKPOINT = Instructions.CHECKPOINT;
262: public static Instruction BLOB = Instructions.BLOB;
263:
264: // Set internal flags that depend on external flags
265: public void defaultOptions() {
266: if (options.getBoolean(DEBUG)) {
267: options.put(WARN_UNDEFINED_REFERENCES, Boolean.TRUE);
268: if (!options.containsKey(WARN_GLOBAL_ASSIGNMENTS)) {
269: options.put(WARN_GLOBAL_ASSIGNMENTS, Boolean
270: .valueOf(LPS.getProperty(
271: "compiler.warn.globalassignments",
272: "false")));
273: }
274: if (!options.containsKey(WARN_UNUSED_LOCALS)) {
275: options.put(WARN_UNUSED_LOCALS, Boolean.valueOf(LPS
276: .getProperty("compiler.warn.unusedlocals",
277: "false")));
278: }
279: if (!options.containsKey(WARN_UNUSED_PARAMETERS)) {
280: options.put(WARN_UNUSED_PARAMETERS, Boolean.valueOf(LPS
281: .getProperty("compiler.warn.unusedparameters",
282: "false")));
283: }
284: options.putBoolean(NAME_FUNCTIONS, true);
285: }
286:
287: // TODO: [2005-04-15 ptw] This pretty much sucks, but the debug
288: // lfc only sets nameFunctions, not debug. This can go away
289: // when we can turn on debug for the lfc.
290: if (options.getBoolean(DEBUG)
291: || options.getBoolean(NAME_FUNCTIONS)) {
292: if (!options.containsKey(DEBUG_BACKTRACE)) {
293: options.putBoolean(DEBUG_BACKTRACE, false);
294: }
295: if (!options.containsKey(DEBUG_SIMPLE)) {
296: options.putBoolean(DEBUG_SIMPLE, false);
297: }
298: }
299: if (!options.containsKey(PROFILE)) {
300: options.putBoolean(PROFILE, false);
301: }
302: if (options.getBoolean(PROFILE)) {
303: options.putBoolean(NAME_FUNCTIONS, true);
304: }
305: options.putBoolean(GENERATE_FUNCTION_2, true);
306: options.putBoolean(GENERATE_FUNCTION_2_FOR_LZX, true);
307: }
308:
309: public void setProperties(Map properties) {
310: // Canonicalize String-valued properties. This is pretty bogus
311: // (dispatching on the value to decide if the property is a
312: // boolean-valued property, but it is the way the compiler always
313: // worked.
314: for (Iterator i = properties.keySet().iterator(); i.hasNext();) {
315: Object key = i.next();
316: Object value = properties.get(key);
317: if (value instanceof String) {
318: String v = (String) value;
319: if ("true".equalsIgnoreCase(v)
320: || "false".equalsIgnoreCase(v)) {
321: options.putBoolean(key, v);
322: continue;
323: }
324: }
325: options.put(key, value);
326: }
327: defaultOptions();
328: if (options.getBoolean(PRINT_COMPILER_OPTIONS)) {
329: System.err.println("set compiler options"
330: + options.toString());
331: }
332: }
333:
334: public byte[] compile(String source) {
335: try {
336: Profiler profiler = new Profiler();
337: profiler.enter("parse");
338: SimpleNode program = new Parser().parse(source);
339: profiler.phase("generate");
340: Translator cg;
341: byte[] bytes;
342: String runtime = (String) options.get(RUNTIME);
343: boolean compress = (!options.getBoolean(NAME_FUNCTIONS));
344: boolean obfuscate = options.getBoolean(OBFUSCATE);
345: if (org.openlaszlo.compiler.Compiler.SCRIPT_RUNTIMES
346: .contains(runtime)) {
347: cg = new JavascriptGenerator();
348: cg.setOptions(options);
349: SimpleNode translated = cg.translate(program);
350: ByteArrayOutputStream stream = new ByteArrayOutputStream();
351: (new ParseTreePrinter(compress, obfuscate)).print(
352: translated, stream);
353: bytes = stream.toByteArray();
354: } else {
355: cg = new CodeGenerator();
356: cg.setOptions(options);
357: cg.translate(program);
358: if (options.getBoolean(PROGRESS)) {
359: System.err.println("Assembling...");
360: }
361: profiler.phase("collect");
362: List instrs = ((InstructionCollector) cg.getCollector())
363: .getInstructions(true);
364: if (options.getBoolean(PRINT_INSTRUCTIONS)) {
365: new Optimizer(new InstructionPrinter())
366: .assemble(instrs);
367: }
368: profiler.phase("assemble");
369: Emitter asm = new Optimizer(new Assembler());
370: // end marker
371: instrs.add(NONE);
372: bytes = asm.assemble(instrs);
373: }
374: profiler.exit();
375: if (options.getBoolean(PROFILE_COMPILER)) {
376: profiler.pprint();
377: System.err.println();
378: }
379: if (options.getBoolean(PROGRESS)) {
380: System.err.println("done.");
381: }
382: return bytes;
383: } catch (CompilerImplementationError e) {
384: String ellipses = source.trim().length() > 80 ? "..." : "";
385: System.err.println("while compiling "
386: + source.trim().substring(0, 80) + ellipses);
387: throw (e);
388: } catch (CompilerError e) {
389: throw (new CompilerException(e.toString()));
390: }
391: }
392:
393: //
394: // Compiler Options
395: //
396:
397: // TODO [2004-03-11 ptw] share with CompilationEnvironment.java
398: public static String ACTIVATION_OBJECT = "createActivationObject";
399: public static String COMPUTE_METAREFERENCES = "computeMetaReferences";
400: public static String CONDITIONAL_COMPILATION = "conditionalCompilation";
401: public static String ALLOW_ROOT = "allowRoot";
402: public static String CACHE_COMPILES = "cacheCompiles";
403: public static String COMPILE_TRACE = "compileTrace";
404: public static String COMPILE_TIME_CONSTANTS = "compileTimeConstants";
405: public static String CONSTRAINT_FUNCTION = "constraintFunction";
406: public static String DEBUG = "debug";
407: public static String DEBUG_BACKTRACE = "debugBacktrace";
408: public static String DEBUG_SIMPLE = "debugSimple";
409: public static String DISABLE_CONSTANT_POOL = "disableConstantPool";
410: public static String ELIMINATE_DEAD_EXPRESSIONS = "eliminateDeadExpressions";
411: public static String FLASH_COMPILER_COMPATABILITY = "flashCompilerCompatability";
412: public static String GENERATE_FUNCTION_2 = "generateFunction2";
413: public static String GENERATE_FUNCTION_2_FOR_LZX = "generateFunction2ForLZX";
414: public static String GENERATE_PREDICTABLE_TEMPS = "generatePredictableTemps";
415: public static String INCLUDES = "processIncludes";
416: public static String INSTR_STATS = "instrStats";
417: public static String LINK = "link";
418: public static String RUNTIME = "runtime";
419: public static String METHOD_NAME = "methodName";
420: public static String NAME_FUNCTIONS = "nameFunctions";
421: public static String OBFUSCATE = "obfuscate";
422: public static String PROFILE = "profile";
423: public static String PROFILE_COMPILER = "profileCompiler";
424: public static String PROGRESS = "progress";
425: public static String PRINT_COMPILER_OPTIONS = "printCompilerOptions";
426: public static String PRINT_CONSTRAINTS = "printConstraints";
427: public static String PRINT_INSTRUCTIONS = "printInstructions";
428: public static String RESOLVER = "resolver";
429: public static String SCRIPT_ELEMENT = "scriptElement";
430: public static String VALIDATE_CACHES = "validateCaches";
431: public static String WARN_UNDEFINED_REFERENCES = "warnUndefinedReferences";
432: public static String WARN_GLOBAL_ASSIGNMENTS = "warnGlobalAssignments";
433: public static String WARN_UNUSED_LOCALS = "warnUnusedLocals";
434: public static String WARN_UNUSED_PARAMETERS = "warnUnusedParameters";
435: public static String WITH_THIS = "withThis";
436:
437: //
438: // Parser
439: //
440:
441: // A scanner and parser generated by JavaCC and jjtree are used to
442: // create a Java AST of the input, with literals annotated by Java
443: // objects (instances of String and the numeric types).
444: public static class Ops implements ParserConstants {
445: };
446:
447: // Wrapper for values that Parser.substitute should splice into
448: // place, instead of substituting at the level of the template
449: // variable.
450: public static class Splice {
451: SimpleNode value[];
452:
453: public Splice(SimpleNode[] value) {
454: this .value = value;
455: }
456:
457: public String toString() {
458: return "Splice(" + value.toString() + ")";
459: }
460: }
461:
462: // Wrapper for the Java parser. Returns a tuple-tree.
463: public static class Parser {
464: public SimpleNode parse0(String str, String type) {
465: org.openlaszlo.sc.parser.Parser p = new org.openlaszlo.sc.parser.Parser(
466: new StringReader(str));
467: assert "Program".equals(type);
468: try {
469: return p.Program();
470: } catch (ParseException pe) {
471: // NOTE: [2007-03-27 ptw]
472: // The parser tracks #file declarations, but does not pass the
473: // file to the exception constructor, so we fix that up here.
474: // (This is really a limitation of javacc.)
475: pe.initPathname(p.token_source.pathname);
476: throw pe;
477: }
478: }
479:
480: public SimpleNode parse0(String str) {
481: return parse0(str, "Program");
482: }
483:
484: public SimpleNode parse(String str) {
485: SimpleNode node = parse0(str, "Program");
486: SimpleNode refactored = refactorAST(node);
487: if (refactored != null) {
488: return refactored;
489: } else {
490: return node;
491: }
492: }
493:
494: private ParseTreePrinter ptp = new ParseTreePrinter();
495:
496: // The transforms in this branch insure that each binary
497: // expression sequence has exactly two children (not
498: // counting the operator).
499: private void fold(SimpleNode node, int arity) {
500: // Transform K(a0,a1,a2) -> K(K(a0,a1),a2), such that no K node
501: // has an arity greater than arity.
502: int size = node.size();
503: if (size > arity) {
504: try {
505: // TODO: [2005-11-21 ptw] clone would be simpler, if you
506: // could make it work
507: java.lang.reflect.Constructor constructor = node
508: .getClass().getConstructor(
509: new Class[] { int.class });
510: SimpleNode child = (SimpleNode) constructor
511: .newInstance(new Object[] { Integer
512: .valueOf("0") });
513: child.setBeginLocation(node.filename,
514: node.beginLine, node.beginColumn);
515: int split = size - (arity - 1);
516: SimpleNode[] children = new SimpleNode[split];
517: for (int i = 0; i < split; i++) {
518: children[i] = node.get(i);
519: }
520: child.setChildren(children);
521: if (child.size() > arity) {
522: fold(child, arity);
523: }
524: children = new SimpleNode[arity];
525: children[0] = child;
526: for (int i = split, j = 1; i < size; i++, j++) {
527: children[j] = node.get(i);
528: }
529: node.setChildren(children);
530: } catch (InstantiationException e) {
531: assert false : e.toString();
532: } catch (IllegalAccessException e) {
533: assert false : e.toString();
534: } catch (NoSuchMethodException e) {
535: assert false : e.toString();
536: } catch (java.lang.reflect.InvocationTargetException e) {
537: assert false : e.toString();
538: }
539: }
540: }
541:
542: // Modify the AST tree rooted at n so that its branching
543: // structure matches evaluation order. This is necessary because
544: // the parser is right-recursive, and generates flat trees for
545: // a+b+c and a.b.c.
546: public SimpleNode refactorAST(SimpleNode node) {
547: if (node == null || node.size() == 0) {
548: return null;
549: }
550: for (int i = 0; i < node.size(); i++) {
551: SimpleNode x = refactorAST(node.get(i));
552: if (x != null) {
553: node.set(i, x);
554: }
555: }
556: if (node instanceof ASTBinaryExpressionSequence) {
557: // Transform a flat sequence of subexpressions with
558: // alternating operators into a right-branching binary
559: // tree. This corrects the fact that the parser, being
560: // recursive-descent, is right-factored, but the operators
561: // are left-associative.
562: //
563: // For example:
564: // K(a, o1, b, o2, c) -> K(K(a, o1, b), o2, c)
565: // K(a, o1, b, o2, c, o3, d) -> K(K(K(a, o1, b), o2, c), o3, d)
566: fold(node, 3);
567: } else if (node instanceof ASTAndExpressionSequence
568: || node instanceof ASTOrExpressionSequence) {
569: // Transforms K(a, b, c) -> K(K(a, b), c),
570: // where node is in (AndExpressionSequence, OrExpressionSequence)
571: fold(node, 2);
572: }
573: if (node instanceof ASTCallExpression) {
574: // cf., CallExpression in Parser.jjt
575: // C(a, P(b)) -> P(a, b)
576: // C(a, P(b), P(c)) -> P(P(a, b), c)
577: // C(a, P(b), A) -> C(P(a, b), A)
578: // C(a, P(b), P(c), A) -> C(P(P(a, b), c), A)
579: // C(a, A) -> C(a, A)
580: // C(a, A, P(b)) -> P(C(a, A), b)
581: // where
582: // C = CallExpression
583: // P = PropertyIdentifierReference
584: // A = FunctionCallParameters
585: while (node.size() > 1) {
586: if (node.get(1) instanceof ASTFunctionCallParameters) {
587: if (node.size() > 2) {
588: try {
589: int size = node.size();
590: // TODO: [2005-11-21 ptw] clone would be simpler, if
591: // you could make it work
592: java.lang.reflect.Constructor constructor = node
593: .getClass()
594: .getConstructor(
595: new Class[] { int.class });
596: SimpleNode child = (SimpleNode) constructor
597: .newInstance(new Object[] { Integer
598: .valueOf("0") });
599: child.setBeginLocation(node.filename,
600: node.beginLine,
601: node.beginColumn);
602: SimpleNode children[] = new SimpleNode[2];
603: children[0] = node.get(0);
604: children[1] = node.get(1);
605: child.setChildren(children);
606: children = new SimpleNode[size - 2 + 1];
607: children[0] = child;
608: for (int i = 2, j = 1; i < size; i++, j++) {
609: children[j] = node.get(i);
610: }
611: node.setChildren(children);
612: } catch (InstantiationException e) {
613: assert false : e.toString();
614: } catch (IllegalAccessException e) {
615: assert false : e.toString();
616: } catch (NoSuchMethodException e) {
617: assert false : e.toString();
618: } catch (java.lang.reflect.InvocationTargetException e) {
619: assert false : e.toString();
620: }
621: continue;
622: } else {
623: break;
624: }
625: }
626: SimpleNode prop = node.get(1);
627: assert ((prop instanceof ASTPropertyIdentifierReference || prop instanceof ASTPropertyValueReference) && prop
628: .size() > 0) : (new ParseTreePrinter())
629: .visit(prop);
630: int size = node.size();
631: SimpleNode children[] = new SimpleNode[2];
632: children[0] = node.get(0);
633: children[1] = prop.get(0);
634: prop.setChildren(children);
635: children = new SimpleNode[size - 1];
636: for (int i = 1, j = 0; i < size; i++, j++) {
637: children[j] = node.get(i);
638: }
639: node.setChildren(children);
640: }
641: if (node.size() == 1) {
642: return node.get(0);
643: }
644: }
645: // After refactoring, assure each function has a name
646: // for debugging and profiling
647: if (node instanceof ASTAssignmentExpression) {
648: SimpleNode rhs = node.get(2);
649: if (rhs instanceof ASTFunctionExpression) {
650: // fn children are [(name), arglist, body]
651: if (rhs.size() == 2) {
652: String name = ptp.visit(node.get(0));
653: SimpleNode child = rhs;
654: int size = child.size();
655: SimpleNode children[] = new SimpleNode[size + 1];
656: children[0] = new ASTIdentifier(name);
657: for (int i = 0, j = 1; i < size; i++, j++) {
658: children[j] = child.get(i);
659: }
660: child.setChildren(children);
661: }
662: }
663: }
664: return node;
665: }
666:
667: // UNUSED
668: // // Build a node out of an AST tuple-tree
669: // public void build(*tuple) {
670: // node = tuple[0]
671: // assert ! node.children
672: // for (child in tuple[1)]:
673: // if (isinstance(child, TupleType))
674: // child = build(*child)
675: // node.jjtAddChild(child, len(node.children))
676: // return node
677:
678: private SimpleNode visit(SimpleNode node, Map keys) {
679: List result = new ArrayList();
680: int size = node.size();
681: for (int i = 0; i < size; i++) {
682: SimpleNode child = node.get(i);
683: if (child instanceof ASTIdentifier) {
684: String name = ((ASTIdentifier) child).getName();
685: if (keys.containsKey(name)) {
686: Object value = keys.get(name);
687: if (value instanceof Splice) {
688: result.addAll(Arrays
689: .asList(((Splice) value).value));
690: } else {
691: result.add(value);
692: }
693: continue;
694: }
695: }
696: result.add(visit(child, keys));
697: }
698: SimpleNode[] children = new SimpleNode[result.size()];
699: node.setChildren((SimpleNode[]) result.toArray(children));
700: return node;
701: }
702:
703: // Parse an expression and replace any identifier with the same
704: // name as a keyword argument to this function, with the value of
705: // that key. If the value has type Splice, it's spliced into
706: // place instead of substituting at the same level.
707: //
708: // >>> s = Parser().substitute
709: // >>> s("[0,1,2]")
710: // (ASTArrayLiteral, Literal(0.0), Literal(1), Literal(2))
711: // >>> s("[_0,1,2]", _0=Literal("sub"))
712: // (ASTArrayLiteral, Literal(sub), Literal(1), Literal(2))
713: // >>> s("[_0,1,2]", _0=s("[a,b,c]"))
714: // (ASTArrayLiteral, (ASTArrayLiteral, ASTIdentifier(a), ASTIdentifier(b), ASTIdentifier(c)), Literal(1), Literal(2))
715: // >>> s("[_0,1,2]", _0=Splice(s("[a,b,c]")))
716: // (ASTArrayLiteral, ASTArrayLiteral, ASTIdentifier(a), ASTIdentifier(b), ASTIdentifier(c), Literal(1), Literal(2))
717: //
718: // N.B., there is no attempt to enforce macro hygiene
719: public SimpleNode substitute(String str, Map keys) {
720: // Since the parser can't parse an Expression, turn the source
721: // into a Program, and extract the Expression from the parse tree.
722: SimpleNode node = parse(
723: "x = \n#file Compiler.substitute\n#line 0\n" + str)
724: .get(0).get(0).get(2);
725: return visit(node, keys);
726: }
727: }
728:
729: // Visitor -- only works for ParseTreePrinter so far
730: // public abstract static class Visitor {
731: // public java.lang.reflect.Method getVisitor(SimpleNode node) {
732: // // trim the module name, and the initial "AST"
733: // String name;
734: // if (node instanceof ASTIdentifier) {
735: // name = "Identifier";
736: // } else {
737: // name = node.getClass().getName();
738: // name = name.substring(name.lastIndexOf(".")+4, name.length());
739: // }
740: // try {
741: // return getClass().getMethod(
742: // "visit" + name,
743: // new Class[] { SimpleNode.class, String[].class }
744: // );
745: // } catch (NoSuchMethodException e) {
746: // System.err.println("Missing visitor: " + e.toString());
747: // try {
748: // return getClass().getMethod(
749: // "defaultVisitor",
750: // new Class[] { Object.class, Object[].class }
751: // );
752: // } catch (NoSuchMethodException ee) {
753: // assert false : ee.toString();
754: // }
755: // }
756: // assert false : "can't happen";
757: // return null;
758: // }
759:
760: // public abstract Object defaultVisitor(Object o, Object[] children);
761: // }
762:
763: // ASTNode -> fname, lineno
764: public static class SourceLocation {
765: private String file;
766: private int line;
767:
768: public SourceLocation(String file, int line) {
769: this .file = file;
770: this .line = line;
771: }
772:
773: public static SourceLocation get(SimpleNode node) {
774: return new SourceLocation(
775: node.filename != null ? node.filename
776: : "unknown file", node.beginLine);
777: }
778: }
779:
780: public static class PassThroughNode extends SimpleNode {
781: public SimpleNode realNode;
782:
783: public PassThroughNode(SimpleNode realNode) {
784: this .realNode = realNode;
785: }
786: }
787:
788: //
789: // Profiler for hand-instrumentation of Compiler
790: //
791: public static class Profiler {
792: static SimpleDateFormat timeFormatter = new SimpleDateFormat(
793: "HH:mm:ss.SS");
794: static {
795: timeFormatter.setTimeZone(TimeZone.getTimeZone("GMT"));
796: }
797: static DecimalFormat percentFormatter = new DecimalFormat(
798: "###.00");
799:
800: public static class Block {
801: Date startTime;
802: long elapsed;
803: String name;
804: Block parent;
805: List children;
806:
807: Block(String name) {
808: this .startTime = new Date();
809: this .name = name;
810: this .children = new ArrayList();
811: }
812:
813: Block make(String name) {
814: Block block = new Block(name);
815: block.parent = this ;
816: children.add(block);
817: return block;
818: }
819: }
820:
821: List names;
822: Block main;
823: Block current;
824:
825: Profiler() {
826: this .names = new ArrayList();
827: Block block = new Block("__main__");
828: this .main = block;
829: this .current = block;
830: }
831:
832: public void enter(String name) {
833: Block block = current.make(name);
834: current = block;
835: }
836:
837: public void exit() {
838: current.elapsed = (new Date()).getTime()
839: - current.startTime.getTime();
840: current = current.parent;
841: }
842:
843: public void phase(String name) {
844: exit();
845: enter(name);
846: }
847:
848: public void pprint() {
849: long total = 0;
850: for (Iterator i = current.children.iterator(); i.hasNext();) {
851: Block next = (Block) i.next();
852: total += next.elapsed;
853: }
854: for (Iterator i = current.children.iterator(); i.hasNext();) {
855: Block next = (Block) i.next();
856: long time = next.elapsed;
857: Object interval = new Date(next.elapsed);
858: System.out.println(next.name + "\t"
859: + timeFormatter.format(interval) + "\t"
860: + percentFormatter.format(time * 100 / total));
861: }
862: }
863: }
864:
865: private static ParseTreePrinter ptp = new ParseTreePrinter();
866:
867: public static String nodeString(SimpleNode node) {
868: return ptp.visit(node);
869: }
870:
871: }
872:
873: /**
874: * @copyright Copyright 2001-2007 Laszlo Systems, Inc. All Rights
875: * Reserved. Use is subject to license terms.
876: */
|