001: /* -*- mode: Java; c-basic-offset: 2; -*- */
002:
003: /**
004: * Common Code for Translation and Code Generation
005: *
006: * @author steele@osteele.com
007: * @author ptw@openlaszlo.org
008: * @author dda@ddanderson.com
009: * @description: Common baseclass for code generators
010: *
011: * This class is extended by CodeGenerator, JavascriptGenerator.
012: */package org.openlaszlo.sc;
013:
014: import java.io.*;
015: import java.util.*;
016: import java.nio.ByteBuffer;
017:
018: import org.openlaszlo.sc.parser.*;
019: import org.openlaszlo.sc.Instructions;
020: import org.openlaszlo.sc.Instructions.Instruction;
021:
022: import org.openlaszlo.cache.PersistentMap;
023:
024: // The code generator dispatches a node whose class is named ASTName to
025: // a method visitName, passing the node, a context, and the node's
026: // children as arguments. The context for a statement visitor is a
027: // TranslationContext, defined above. The context for an expression
028: // visitor is a boolean value, that is true iff the value of the
029: // expression is used. The return value of a statement visitor is
030: // ignored. The return value of an expression visitor is true iff it
031: // generated code that did NOT leave a value on the stack. (This is so
032: // that an expression visitor that ignores its context need do nothing
033: // special to indicate that it ignored it: the default return value of
034: // null signals this.)
035: //
036: // Methods of the form visitName are AST node visitors, and follow the
037: // protocol described above. Methods of the form translateName are
038: // helper functions for the visitors, and have arbitrary parameter
039: // lists and return values.
040:
041: // TODO: [2006-01-17 ptw] Remove some day
042: // Replace instruction subsequences by a BLOB instruction that
043: // represents the same bytes. By default, the BLOB instructions are
044: // separated by PUSH's (which depend on the constant pool), and
045: // branches and targets (since they can't be resolved until the size of
046: // the PUSH instructions is known). When noConstantPool=true, PUSH's
047: // are compiled against a null constant pool, and branches and targets
048: // are compiled, so the instructions combine to a single BLOB.
049: // public void combineInstructions(instrsIn, noConstantPool=false) {
050: // instrsOut = [];
051: // buffer = ByteBuffer.allocate(64000);
052: // public void flush(instrsOut=instrsOut,buffer=buffer) {
053: // if (buffer.position()) {
054: // import jarray;
055: // bytes = jarray.zeros(buffer.position(), "b");
056: // buffer.flip();
057: // buffer.get(bytes);
058: // buffer.clear();
059: // instrsOut.append(BLOB("bytes", bytes));
060: // for (instr in instrsIn) {
061: // if (noConstantPool || instr.isPush || instr.isLabel || instr.hasTarget) {
062: // flush();
063: // instrsOut.append(instr);
064: // } else {
065: // instr.writeBytes(buffer, null);
066: // flush();
067: // return instrsOut;
068: // }
069:
070: public abstract class CommonGenerator implements ASTVisitor {
071:
072: Compiler.OptionMap options = new Compiler.OptionMap();
073: String runtime;
074: TranslationContext context = null;
075: boolean debugVisit = false;
076: InstructionCollector collector = null;
077:
078: public Compiler.OptionMap getOptions() {
079: return options;
080: }
081:
082: public TranslationContext getContext() {
083: return context;
084: }
085:
086: protected abstract void setRuntime(String runtime);
087:
088: public void setOptions(Compiler.OptionMap options) {
089: this .options = options;
090: this .runtime = ((String) options.get(Compiler.RUNTIME))
091: .intern();
092: setRuntime(this .runtime);
093: }
094:
095: public InstructionCollector getCollector() {
096: return collector;
097: }
098:
099: // Options that don't affect code generation. This is used to decide
100: // what it's okay to cache across LFC build versions. It's okay if
101: // it's too small.
102: static Set NonCodeGenerationOptions = new HashSet();
103: static {
104: NonCodeGenerationOptions.add(Compiler.CACHE_COMPILES);
105: NonCodeGenerationOptions.add(Compiler.INSTR_STATS);
106: NonCodeGenerationOptions.add(Compiler.PRINT_COMPILER_OPTIONS);
107: NonCodeGenerationOptions.add(Compiler.PRINT_CONSTRAINTS);
108: NonCodeGenerationOptions.add(Compiler.PROFILE_COMPILER);
109: NonCodeGenerationOptions.add(Compiler.PROGRESS);
110: NonCodeGenerationOptions.add(Compiler.RESOLVER);
111: // These affect the default settings for the options above, but
112: // do not themselves make a difference.
113: NonCodeGenerationOptions.add(Compiler.DEBUG);
114: }
115:
116: static class LessHalfAssedHashMap extends HashMap {
117: LessHalfAssedHashMap() {
118: super ();
119: }
120:
121: Object get(int key) {
122: return get(new Integer(key));
123: }
124:
125: Object put(int key, Object value) {
126: return put(new Integer(key), value);
127: }
128:
129: Object put(int key, int value) {
130: return put(new Integer(key), new Integer(value));
131: }
132: }
133:
134: static SimpleNode parseFragment(String code) {
135: if (code.equals("\"\"") || code == null) {
136: code = "";
137: }
138: code = "{"
139: + "\n#pragma 'warnUndefinedReferences=false'\n"
140: + "\n#file JavascriptGenerator.parseFragment\n#line 0\n"
141: + code + "}";
142: // Extract the statement list from the program
143: try {
144: return (new Compiler.Parser()).parse(code).get(0);
145: } catch (ParseException e) {
146: System.err.println("while compiling " + code);
147: throw e;
148: }
149: }
150:
151: // TODO: [2007-08-20 ptw] Replace with Java 1.5 UUID
152: private Boolean usePredictable = null;
153: private Random rand = new Random();
154: private int uuidCounter = 1;
155:
156: protected Integer UUID() {
157: if (usePredictable == null) {
158: usePredictable = new Boolean(options
159: .getBoolean(Compiler.GENERATE_PREDICTABLE_TEMPS));
160: }
161: if (usePredictable.equals(Boolean.TRUE)) {
162: return new Integer(uuidCounter++);
163: } else {
164: return new Integer(rand.nextInt(Integer.MAX_VALUE));
165: }
166: }
167:
168: Boolean evaluateCompileTimeConditional(SimpleNode node) {
169: Object value = null;
170: if (node instanceof ASTIdentifier) {
171: String name = ((ASTIdentifier) node).getName();
172: Map constants = (Map) options
173: .get(Compiler.COMPILE_TIME_CONSTANTS);
174: if (constants != null) {
175: if (constants.containsKey(name)) {
176: value = constants.get(name);
177: // if (value != null) {
178: // + ": " + value + "(" + value.getClass() + ")");
179: // }
180: }
181: }
182: }
183: // if (value != null) {
184: // System.err.println(" => " + value + "(" + value.getClass() + ")");
185: // }
186: return (Boolean) value;
187: }
188:
189: static class ParseResult {
190: SimpleNode parse;
191: boolean hasIncludes;
192:
193: ParseResult(SimpleNode parse, boolean hasIncludes) {
194: this .parse = parse;
195: this .hasIncludes = hasIncludes;
196: }
197:
198: public boolean equals(Object o) {
199: if (o != null && o instanceof ParseResult) {
200: ParseResult pr = (ParseResult) o;
201: return parse.equals(pr.parse)
202: && hasIncludes == pr.hasIncludes;
203: }
204: return false;
205: }
206: }
207:
208: static java.util.regex.Pattern includePattern = java.util.regex.Pattern
209: .compile(".*#\\s*include\\s*\".*",
210: java.util.regex.Pattern.DOTALL);
211:
212: ParseResult parseFile(File file, String userfname, String source) {
213: if (Compiler.CachedParses == null) {
214: Compiler.CachedParses = new ScriptCompilerCache();
215: }
216: String sourceKey = file.getAbsolutePath();
217: String sourceChecksum = "" + file.lastModified(); // source;
218: ParseResult entry = (ParseResult) Compiler.CachedParses.get(
219: sourceKey, sourceChecksum);
220: if ((entry == null)
221: || options.getBoolean(Compiler.VALIDATE_CACHES)) {
222: boolean hasIncludes = includePattern.matcher(source)
223: .matches();
224: if (options.getBoolean(Compiler.PROGRESS)) {
225: // Even though code generation is re-run
226: // for every file, just print this for
227: // files that are re-parsed, to indicate
228: // what's being changed.
229: System.err.println("Compiling " + userfname + "...");
230: }
231: SimpleNode program = (new Compiler.Parser()).parse(source);
232: // Always cache the parse tree, since this
233: // helps even when the compilation is only one
234: // once. This is because each pass processes
235: // the #include again.
236: ParseResult realentry = new ParseResult(program,
237: hasIncludes);
238: Compiler.CachedParses.put(sourceKey, sourceChecksum,
239: realentry);
240: if ((entry != null)
241: && options.getBoolean(Compiler.VALIDATE_CACHES)) {
242: if (!realentry.equals(entry)) {
243: System.err.println("Bad parse cache for "
244: + sourceKey + ": " + entry + " != "
245: + realentry);
246: }
247: }
248: entry = realentry;
249: }
250: return entry;
251: }
252:
253: private String mapToString(Map map) {
254: StringBuffer result = new StringBuffer();
255: result.append("{");
256: TreeMap sorted = new TreeMap(map);
257: for (Iterator i = sorted.keySet().iterator(); i.hasNext();) {
258: Object key = i.next();
259: result.append(key);
260: result.append(": ");
261: result.append(sorted.get(key));
262: if (i.hasNext()) {
263: result.append(", ");
264: }
265: }
266: result.append("}");
267: return result.toString();
268: }
269:
270: String getCodeGenerationOptionsKey(List ignore) {
271: Map options = new HashMap(this .options);
272: options.keySet().removeAll(NonCodeGenerationOptions);
273: if (ignore != null) {
274: options.keySet().removeAll(ignore);
275: }
276: return mapToString(options);
277: }
278:
279: public SimpleNode visitPragmaDirective(SimpleNode node,
280: SimpleNode[] children) {
281: String key = (String) ((ASTLiteral) children[0]).getValue();
282: String value = "true";
283: int equals = key.indexOf('=');
284: if (equals > 0) {
285: value = key.substring(equals + 1);
286: key = key.substring(0, equals);
287: }
288: if ("false".equalsIgnoreCase(value)
289: || "true".equalsIgnoreCase(value)) {
290: options.putBoolean(key, value);
291: } else {
292: options.put(key, value);
293: }
294: return new ASTEmptyExpression(0);
295: }
296:
297: // Flatten nested StatementList structures
298: private List flatten(SimpleNode[] src) {
299: List dst = new ArrayList();
300: for (int i = 0; i < src.length; i++) {
301: SimpleNode node = src[i];
302: if (node instanceof ASTStatementList) {
303: dst.addAll(flatten(node.getChildren()));
304: } else {
305: dst.add(node);
306: }
307: }
308: return dst;
309: }
310:
311: public SimpleNode visitClassDefinition(SimpleNode node,
312: SimpleNode[] children) {
313: // System.err.println("enter visitClassDefinition: " + (new ParseTreePrinter()).visit(node));
314: ASTIdentifier classortrait = (ASTIdentifier) children[0];
315: ASTIdentifier classname = (ASTIdentifier) children[1];
316: String classnameString = classname.getName();
317: SimpleNode super class = children[2];
318: SimpleNode traits = children[3];
319: SimpleNode traitsandsuper ;
320: if (traits instanceof ASTEmptyExpression) {
321: if (super class instanceof ASTEmptyExpression) {
322: traitsandsuper = new ASTLiteral(null);
323: } else {
324: traitsandsuper = super class;
325: }
326: } else {
327: traitsandsuper = new ASTArrayLiteral(0);
328: traitsandsuper .setChildren(traits.getChildren());
329: if (!(super class instanceof ASTEmptyExpression)) {
330: traitsandsuper .set(traitsandsuper .size(), super class);
331: }
332: }
333:
334: SimpleNode[] dirs = (SimpleNode[]) (Arrays.asList(children)
335: .subList(4, children.length).toArray(new SimpleNode[0]));
336: List props = new ArrayList();
337: List classProps = new ArrayList();
338: List stmts = new ArrayList();
339: translateClassDirectivesBlock(dirs, classnameString, props,
340: classProps, stmts);
341:
342: SimpleNode instanceProperties;
343: if (props.isEmpty()) {
344: instanceProperties = new ASTLiteral(null);
345: } else {
346: instanceProperties = new ASTArrayLiteral(0);
347: instanceProperties.setChildren((SimpleNode[]) (props
348: .toArray(new SimpleNode[0])));
349: }
350: SimpleNode classProperties;
351: if (classProps.isEmpty()) {
352: classProperties = new ASTLiteral(null);
353: } else {
354: classProperties = new ASTArrayLiteral(0);
355: classProperties.setChildren((SimpleNode[]) (classProps
356: .toArray(new SimpleNode[0])));
357: }
358:
359: Map map = new HashMap();
360: String xtor = "class".equals(classortrait.getName()) ? "Class"
361: : "Trait";
362: map.put("_1", classname);
363: map.put("_2", traitsandsuper );
364: map.put("_3", instanceProperties);
365: map.put("_4", classProperties);
366: SimpleNode newNode = (new Compiler.Parser()).substitute(xtor
367: + ".make(" + ScriptCompiler.quote(classnameString)
368: + ", _2, _3, _4);", map);
369: SimpleNode varNode = new ASTVariableDeclaration(0);
370: varNode.set(0, classname);
371: varNode.set(1, newNode);
372: SimpleNode replNode = varNode;
373:
374: if (!stmts.isEmpty()) {
375: SimpleNode statements = new ASTStatementList(0);
376: statements.setChildren((SimpleNode[]) (stmts
377: .toArray(new SimpleNode[0])));
378: map.put("_5", statements);
379: SimpleNode stmtNode = (new Compiler.Parser())
380: .substitute(
381: "(function () { with(_1) with(_1.prototype) { _5 }})()",
382: map);
383: replNode = new ASTStatementList(0);
384: replNode.set(0, varNode);
385: replNode.set(1, stmtNode);
386: }
387: // System.err.println("exit visitClassDefinition: " + (new ParseTreePrinter()).visit(replNode));
388: return visitStatement(replNode);
389: }
390:
391: public void translateClassDirectivesBlock(SimpleNode[] dirs,
392: String classnameString, List props, List classProps,
393: List stmts) {
394: dirs = (SimpleNode[]) (flatten(dirs).toArray(new SimpleNode[0]));
395:
396: // Scope #pragma directives to block
397: Compiler.OptionMap savedOptions = options;
398: try {
399: options = options.copy();
400: for (int i = 0; i < dirs.length; i++) {
401: SimpleNode n = dirs[i];
402: List p = props;
403:
404: // any modifiers, like 'static', 'final' are kept in mod.
405: ASTModifiedDefinition mod = null;
406: if (n instanceof ASTModifiedDefinition) {
407: assert (n.getChildren().length == 1);
408: mod = (ASTModifiedDefinition) n;
409: if (mod.isStatic()) {
410: p = classProps;
411: }
412: n = n.get(0);
413: mod.verifyClassLevel(n);
414: }
415: if (n instanceof ASTFunctionDeclaration) {
416: SimpleNode[] c = n.getChildren();
417: assert c.length == 3;
418: String fname = ((ASTIdentifier) c[0]).getName();
419: // Transform constructor into '$lzsc$initialize' method
420: if (classnameString.equals(fname)) {
421: fname = "$lzsc$initialize";
422: c[0] = new ASTIdentifier(fname);
423: }
424: p.add(new ASTLiteral(fname));
425: SimpleNode funexpr = new ASTFunctionExpression(0);
426: funexpr.setBeginLocation(n.filename, n.beginLine,
427: n.beginColumn);
428: funexpr.setChildren(c);
429: p.add(funexpr);
430: } else if (n instanceof ASTVariableStatement) {
431: SimpleNode[] c = n.getChildren();
432: for (int j = 0, len = c.length; j < len; j++) {
433: SimpleNode v = c[j];
434: assert v instanceof ASTVariableDeclaration : v
435: .getClass();
436: p.add(new ASTLiteral(((ASTIdentifier) v.get(0))
437: .getName()));
438: if (v.getChildren().length > 1) {
439: p.add(v.get(1));
440: } else {
441: p.add(new ASTLiteral(null));
442: }
443: }
444: } else if (n instanceof ASTClassDirectiveBlock) {
445: translateClassDirectivesBlock(n.getChildren(),
446: classnameString, props, classProps, stmts);
447: } else if (n instanceof ASTClassIfDirective) {
448: Boolean value = evaluateCompileTimeConditional(n
449: .get(0));
450: if (value == null) {
451: stmts.add(n);
452: } else if (value.booleanValue()) {
453: SimpleNode clause = n.get(1);
454: translateClassDirectivesBlock(clause
455: .getChildren(), classnameString, props,
456: classProps, stmts);
457: } else if (n.size() > 2) {
458: SimpleNode clause = n.get(2);
459: translateClassDirectivesBlock(clause
460: .getChildren(), classnameString, props,
461: classProps, stmts);
462: }
463: } else if (n instanceof ASTPragmaDirective) {
464: visitPragmaDirective(n, n.getChildren());
465: } else {
466: stmts.add(n);
467: }
468: }
469: } finally {
470: options = savedOptions;
471: }
472: }
473:
474: //
475: // Statements
476: //
477:
478: public SimpleNode visitStatement(SimpleNode node) {
479: return visitStatement(node, node.getChildren());
480: }
481:
482: public SimpleNode visitStatement(SimpleNode node,
483: SimpleNode[] children) {
484: /* This function, unlike the other statement visitors, can be
485: applied to any statement node, so it dispatches based on the
486: node's class. */
487: assert context instanceof TranslationContext;
488: showStats(node);
489: SimpleNode newNode = node;
490:
491: if (this .debugVisit) {
492: System.err.println("visitStatement: " + node.getClass());
493: }
494:
495: // Are we doing OO programming yet?
496: if (node instanceof ASTPragmaDirective) {
497: newNode = visitPragmaDirective(node, children);
498: } else if (node instanceof ASTClassDefinition) {
499: newNode = visitClassDefinition(node, children);
500: } else if (node instanceof ASTStatementList) {
501: newNode = visitStatementList(node, children);
502: } else if (node instanceof ASTDirectiveBlock) {
503: newNode = visitDirectiveBlock(node, children);
504: } else if (node instanceof ASTFunctionDeclaration) {
505: newNode = visitFunctionDeclaration(node, children);
506: } else if (node instanceof ASTStatement) {
507: // an empty statement, introduced by an extra ";", has no children
508: if (children.length > 0) {
509: children[0] = visitStatement(children[0], children[0]
510: .getChildren());
511: } else {
512: newNode = new ASTEmptyExpression(0);
513: }
514: } else if (node instanceof ASTLabeledStatement) {
515: newNode = visitLabeledStatement(node, children);
516: } else if (node instanceof ASTVariableDeclaration) {
517: newNode = visitVariableDeclaration(node, children);
518: } else if (node instanceof ASTVariableDeclarationList) {
519: newNode = visitVariableDeclarationList(node, children);
520: } else if (node instanceof ASTVariableStatement) {
521: newNode = visitVariableStatement(node, children);
522: } else if (node instanceof ASTIfStatement) {
523: newNode = visitIfStatement(node, children);
524: } else if (node instanceof ASTIfDirective) {
525: newNode = visitIfDirective(node, children);
526: } else if (node instanceof ASTWhileStatement) {
527: newNode = visitWhileStatement(node, children);
528: } else if (node instanceof ASTDoWhileStatement) {
529: newNode = visitDoWhileStatement(node, children);
530: } else if (node instanceof ASTForStatement) {
531: newNode = visitForStatement(node, children);
532: } else if (node instanceof ASTForVarStatement) {
533: newNode = visitForVarStatement(node, children);
534: } else if (node instanceof ASTForInStatement) {
535: newNode = visitForInStatement(node, children);
536: } else if (node instanceof ASTForVarInStatement) {
537: newNode = visitForVarInStatement(node, children);
538: } else if (node instanceof ASTContinueStatement) {
539: newNode = visitContinueStatement(node, children);
540: } else if (node instanceof ASTBreakStatement) {
541: newNode = visitBreakStatement(node, children);
542: } else if (node instanceof ASTReturnStatement) {
543: newNode = visitReturnStatement(node, children);
544: } else if (node instanceof ASTWithStatement) {
545: newNode = visitWithStatement(node, children);
546: } else if (node instanceof ASTTryStatement) {
547: newNode = visitTryStatement(node, children);
548: } else if (node instanceof ASTThrowStatement) {
549: newNode = visitThrowStatement(node, children);
550: } else if (node instanceof ASTSwitchStatement) {
551: newNode = visitSwitchStatement(node, children);
552: } else if (node instanceof ASTModifiedDefinition) {
553: newNode = visitModifiedDefinition(node, children);
554: } else if (node instanceof Compiler.PassThroughNode) {
555: newNode = node;
556: } else {
557: // Not a statement, must be an expression
558: newNode = visitExpression(node, false);
559: }
560: // Check for elided statments
561: if (newNode == null) {
562: newNode = new ASTEmptyExpression(0);
563: }
564: if (this .debugVisit) {
565: if (!newNode.equals(node)) {
566: System.err.println("statement: " + node + " -> "
567: + newNode);
568: }
569: }
570: return newNode;
571: }
572:
573: public SimpleNode visitStatementList(SimpleNode node,
574: SimpleNode[] stmts) {
575: int i = 0;
576: // ensure dynamic extent of #pragma in a block
577: Compiler.OptionMap prevOptions = options;
578: Compiler.OptionMap newOptions = options.copy();
579: // TODO: [2003-04-15 ptw] bind context slot macro
580: try {
581: options = newOptions;
582: while (i < stmts.length) {
583: SimpleNode stmt = stmts[i];
584: stmts[i] = visitStatement(stmt);
585: i += 1;
586: }
587: } finally {
588: options = prevOptions;
589: }
590: return node;
591: }
592:
593: public SimpleNode visitVariableDeclarationList(SimpleNode node,
594: SimpleNode[] children) {
595: for (int i = 0, len = children.length; i < len; i++) {
596: children[i] = visitStatement(children[i]);
597: }
598: return node;
599: }
600:
601: // for function prefix/suffix parsing
602: public SimpleNode visitDirectiveBlock(SimpleNode node,
603: SimpleNode[] children) {
604: return visitStatementList(node, children);
605: }
606:
607: public SimpleNode visitModifiedDefinition(SimpleNode node,
608: SimpleNode[] children) {
609: // Modifiers, like 'final', are ignored unless this is handled
610: // by the runtime.
611:
612: assert children.length == 1;
613: SimpleNode child = children[0];
614:
615: ((ASTModifiedDefinition) node).verifyTopLevel(child);
616:
617: return visitStatement(child);
618: }
619:
620: public SimpleNode visitLabeledStatement(SimpleNode node,
621: SimpleNode[] children) {
622: ASTIdentifier name = (ASTIdentifier) children[0];
623: SimpleNode stmt = children[1];
624: // TODO: [2003-04-15 ptw] bind context slot macro
625: try {
626: context = new TranslationContext(ASTLabeledStatement.class,
627: context, name.getName());
628: // TODO: [2002 ows] throw semantic error for duplicate label
629: children[1] = visitStatement(stmt);
630: return node;
631: } finally {
632: context = context.parent;
633: }
634: }
635:
636: // for function prefix/suffix parsing
637: public SimpleNode visitIfDirective(SimpleNode node,
638: SimpleNode[] children) {
639: return visitIfStatement(node, children);
640: }
641:
642: public SimpleNode visitForVarInStatement(SimpleNode node,
643: SimpleNode[] children) {
644: SimpleNode var = children[0];
645: // SimpleNode _ = children[1];
646: SimpleNode obj = children[2];
647: SimpleNode body = children[3];
648: if (options.getBoolean(Compiler.ACTIVATION_OBJECT)) {
649: return translateForInStatement(node, var,
650: Instructions.SetVariable, obj, body);
651: }
652: return translateForInStatement(node, var,
653: Instructions.VarEquals, obj, body);
654: }
655:
656: public SimpleNode visitContinueStatement(SimpleNode node,
657: SimpleNode[] children) {
658: SimpleNode label = children.length > 0 ? children[0] : null;
659: return translateAbruptCompletion(node, "continue",
660: (ASTIdentifier) label);
661: }
662:
663: public SimpleNode visitBreakStatement(SimpleNode node,
664: SimpleNode[] children) {
665: SimpleNode label = children.length > 0 ? children[0] : null;
666: return translateAbruptCompletion(node, "break",
667: (ASTIdentifier) label);
668: }
669:
670: public SimpleNode visitAndExpressionSequence(SimpleNode node,
671: boolean isReferenced, SimpleNode[] children) {
672: SimpleNode a = children[0];
673: SimpleNode b = children[1];
674: return translateAndOrExpression(node, true, a, b);
675: }
676:
677: public SimpleNode visitOrExpressionSequence(SimpleNode node,
678: boolean isReferenced, SimpleNode[] children) {
679: SimpleNode a = children[0];
680: SimpleNode b = children[1];
681: return translateAndOrExpression(node, false, a, b);
682: }
683:
684: static class DoubleCollator implements Comparator {
685: public boolean equals(Object o1, Object o2) {
686: return ((Double) o1).equals((Double) o2);
687: }
688:
689: public int compare(Object o1, Object o2) {
690: return ((Double) o1).compareTo((Double) o2);
691: }
692: }
693:
694: public SimpleNode visitChildren(SimpleNode node) {
695: SimpleNode[] children = node.getChildren();
696: for (int i = 0, len = children.length; i < len; i++) {
697: SimpleNode child = children[i];
698: children[i] = visitStatement(child);
699: }
700: return node;
701: }
702:
703: public SimpleNode translateSuperCallExpression(SimpleNode node,
704: boolean isReferenced, SimpleNode[] children) {
705:
706: assert children.length == 3;
707: SimpleNode fname = children[0];
708: SimpleNode callapply = children[1];
709: SimpleNode args = children[2];
710: String name;
711: String ca = null;
712: String pattern = "(arguments.callee.superclass?arguments.callee.superclass.prototype[_1]:this.nextMethod(arguments.callee, _1)).call(this, _2)";
713: if (fname instanceof ASTEmptyExpression) {
714: // super with no selector is the constructor, which will be
715: // renamed to $lzsc$initialize in translateClassDirective to
716: // mesh with lfc/compiler/Class.lzs framework
717: name = "$lzsc$initialize";
718: } else {
719: name = ((ASTIdentifier) fname).getName();
720: }
721: if (callapply instanceof ASTIdentifier) {
722: ca = ((ASTIdentifier) callapply).getName();
723: }
724: // FIXME: [2005-03-09 ptw] (LPP-98 "Compiler source-source
725: // transformations should be in separate phase") This should be
726: // in a phase before the compiler, so that register analysis
727: // sees it. [Or this should be eliminated altogether and we
728: // should use swf7's real super call, but that will mean we
729: // have to solve the __proto__ vs. super in constructor
730: // problem.]
731: Map map = new HashMap();
732: map.put("_1", new ASTLiteral(name));
733: map.put("_2", new Compiler.Splice(args.getChildren()));
734: if (ca == null) {
735: ;
736: } else if ("call".equals(ca)) {
737: pattern = "(arguments.callee.superclass?arguments.callee.superclass.prototype[_1]:this.nextMethod(arguments.callee, _1)).call(_2)";
738: } else if ("apply".equals(ca)) {
739: pattern = "(arguments.callee.superclass?arguments.callee.superclass.prototype[_1]:this.nextMethod(arguments.callee, _1)).apply(_2)";
740: } else {
741: assert false : "Unhandled super call " + ca;
742: }
743: SimpleNode n = (new Compiler.Parser()).substitute(pattern, map);
744: return n;
745: }
746:
747: public SimpleNode visitVariableStatement(SimpleNode node,
748: SimpleNode[] children) {
749: return visitChildren(node);
750: }
751:
752: boolean isExpressionType(SimpleNode node) {
753: // There are several AST types that end with each of the names that
754: // endsWith tests for.
755: String name = node.getClass().getName();
756: return name.endsWith("Expression")
757: || name.endsWith("FunctionCallParameters")
758: || name.endsWith("ExpressionList")
759: || name.endsWith("ExpressionSequence")
760: || name.endsWith("Identifier")
761: || name.endsWith("Literal")
762: || name.endsWith("Reference");
763: }
764:
765: public SimpleNode dispatchExpression(SimpleNode node,
766: boolean isReferenced) {
767: // Are we doing OO programming yet?
768: SimpleNode[] children = node.getChildren();
769: SimpleNode newNode = null;
770:
771: if (node instanceof ASTIdentifier) {
772: newNode = visitIdentifier(node, isReferenced, children);
773: } else if (node instanceof ASTLiteral) {
774: newNode = visitLiteral(node, isReferenced, children);
775: } else if (node instanceof ASTExpressionList) {
776: newNode = visitExpressionList(node, isReferenced, children);
777: } else if (node instanceof ASTEmptyExpression) {
778: newNode = visitEmptyExpression(node, isReferenced, children);
779: } else if (node instanceof ASTThisReference) {
780: newNode = visitThisReference(node, isReferenced, children);
781: } else if (node instanceof ASTArrayLiteral) {
782: newNode = visitArrayLiteral(node, isReferenced, children);
783: } else if (node instanceof ASTObjectLiteral) {
784: newNode = visitObjectLiteral(node, isReferenced, children);
785: } else if (node instanceof ASTFunctionExpression) {
786: newNode = visitFunctionExpression(node, isReferenced,
787: children);
788: } else if (node instanceof ASTFunctionCallParameters) {
789: newNode = visitFunctionCallParameters(node, isReferenced,
790: children);
791: } else if (node instanceof ASTPropertyIdentifierReference) {
792: newNode = visitPropertyIdentifierReference(node,
793: isReferenced, children);
794: } else if (node instanceof ASTPropertyValueReference) {
795: newNode = visitPropertyValueReference(node, isReferenced,
796: children);
797: } else if (node instanceof ASTCallExpression) {
798: newNode = visitCallExpression(node, isReferenced, children);
799: } else if (node instanceof ASTSuperCallExpression) {
800: newNode = visitSuperCallExpression(node, isReferenced,
801: children);
802: } else if (node instanceof ASTNewExpression) {
803: newNode = visitNewExpression(node, isReferenced, children);
804: } else if (node instanceof ASTPostfixExpression) {
805: newNode = visitPostfixExpression(node, isReferenced,
806: children);
807: } else if (node instanceof ASTUnaryExpression) {
808: newNode = visitUnaryExpression(node, isReferenced, children);
809: } else if (node instanceof ASTBinaryExpressionSequence) {
810: newNode = visitBinaryExpressionSequence(node, isReferenced,
811: children);
812: } else if (node instanceof ASTAndExpressionSequence) {
813: newNode = visitAndExpressionSequence(node, isReferenced,
814: children);
815: } else if (node instanceof ASTOrExpressionSequence) {
816: newNode = visitOrExpressionSequence(node, isReferenced,
817: children);
818: } else if (node instanceof ASTConditionalExpression) {
819: newNode = visitConditionalExpression(node, isReferenced,
820: children);
821: } else if (node instanceof ASTAssignmentExpression) {
822: newNode = visitAssignmentExpression(node, isReferenced,
823: children);
824: } else if (node instanceof Compiler.PassThroughNode) {
825: newNode = node;
826: } else {
827: throw new CompilerImplementationError("unknown expression "
828: + node, node);
829: }
830: return newNode;
831: }
832:
833: abstract SimpleNode translateForInStatement(SimpleNode node,
834: SimpleNode var, Instructions.Instruction varset,
835: SimpleNode obj, SimpleNode body);
836:
837: abstract SimpleNode translateAbruptCompletion(SimpleNode node,
838: String type, ASTIdentifier label);
839:
840: abstract SimpleNode translateAndOrExpression(SimpleNode node,
841: boolean isand, SimpleNode a, SimpleNode b);
842:
843: /** Collect runtime statistics at this point in the program if asked for.
844: */
845: abstract void showStats(SimpleNode node);
846:
847: File includeNameToFile(String userfname) {
848: try {
849: String fname = userfname;
850:
851: if (options.containsKey(Compiler.RESOLVER)) {
852: fname = ((lzsc.Resolver) options.get(Compiler.RESOLVER))
853: .resolve(userfname);
854: }
855: return new File(new File(fname).getCanonicalPath());
856: } catch (IOException e) {
857: throw new CompilerError("error reading include: " + e);
858: }
859: }
860:
861: String includeFileToSourceString(File file, String userfname) {
862: String source;
863: try {
864: FileInputStream stream = new FileInputStream(file);
865: try {
866: int n = stream.available();
867: byte[] b = new byte[n];
868: stream.read(b);
869: source = "#file " + userfname + "\n#line 1\n"
870: + new String(b, "UTF-8");
871: } finally {
872: stream.close();
873: }
874: } catch (FileNotFoundException e) {
875: throw new CompilerError("error reading include: " + e);
876: } catch (UnsupportedEncodingException e) {
877: throw new CompilerError("error reading include: " + e);
878: } catch (IOException e) {
879: throw new CompilerError("error reading include: " + e);
880: }
881: return source;
882: }
883: }
884:
885: /* J_LZ_COPYRIGHT_BEGIN *******************************************************
886: * Copyright 2001-2008 Laszlo Systems, Inc. All Rights Reserved. *
887: * Use is subject to license terms. *
888: * J_LZ_COPYRIGHT_END *********************************************************/
|