0001: /* -*- mode: Java; c-basic-offset: 2; -*- */
0002:
0003: /***
0004: * CodeGenerator.java
0005: * Author: Oliver Steele, P T Withington
0006: * Description: JavaScript -> SWF bytecode compiler
0007: */package org.openlaszlo.sc;
0008:
0009: import java.io.*;
0010: import java.util.*;
0011: import java.nio.ByteBuffer;
0012:
0013: import org.openlaszlo.sc.parser.*;
0014: import org.openlaszlo.sc.Instructions;
0015: import org.openlaszlo.sc.Instructions.Instruction;
0016:
0017: import org.openlaszlo.cache.PersistentMap;
0018:
0019: // The code generator dispatches a node whose class is named ASTName to
0020: // a method visitName, passing the node, a context, and the node's
0021: // children as arguments. The context for a statement visitor is a
0022: // TranslationContext, defined above. The context for an expression
0023: // visitor is a boolean value, that is true iff the value of the
0024: // expression is used. The return value of a statement visitor is
0025: // ignored. The return value of an expression visitor is true iff it
0026: // generated code that did NOT leave a value on the stack. (This is so
0027: // that an expression visitor that ignores its context need do nothing
0028: // special to indicate that it ignored it: the default return value of
0029: // null signals this.)
0030: //
0031: // Methods of the form visitName are AST node visitors, and follow the
0032: // protocol described above. Methods of the form translateName are
0033: // helper functions for the visitors, and have arbitrary parameter
0034: // lists and return values.
0035:
0036: // TODO: [2006-01-17 ptw] Remove some day
0037: // Replace instruction subsequences by a BLOB instruction that
0038: // represents the same bytes. By default, the BLOB instructions are
0039: // separated by PUSH's (which depend on the constant pool), and
0040: // branches and targets (since they can't be resolved until the size of
0041: // the PUSH instructions is known). When noConstantPool=true, PUSH's
0042: // are compiled against a null constant pool, and branches and targets
0043: // are compiled, so the instructions combine to a single BLOB.
0044: // public void combineInstructions(instrsIn, noConstantPool=false) {
0045: // instrsOut = [];
0046: // buffer = ByteBuffer.allocate(64000);
0047: // public void flush(instrsOut=instrsOut,buffer=buffer) {
0048: // if (buffer.position()) {
0049: // import jarray;
0050: // bytes = jarray.zeros(buffer.position(), "b");
0051: // buffer.flip();
0052: // buffer.get(bytes);
0053: // buffer.clear();
0054: // instrsOut.append(BLOB("bytes", bytes));
0055: // for (instr in instrsIn) {
0056: // if (noConstantPool || instr.isPush || instr.isLabel || instr.hasTarget) {
0057: // flush();
0058: // instrsOut.append(instr);
0059: // } else {
0060: // instr.writeBytes(buffer, null);
0061: // flush();
0062: // return instrsOut;
0063: // }
0064:
0065: public class CodeGenerator extends CommonGenerator implements
0066: Translator {
0067:
0068: protected void setRuntime(String runtime) {
0069: assert org.openlaszlo.compiler.Compiler.SWF_RUNTIMES
0070: .contains(runtime) : "unknown runtime " + runtime;
0071: Instructions.setRuntime(runtime);
0072: }
0073:
0074: public SimpleNode translate(SimpleNode program) {
0075: // Make a new collector each time, since the old one may be
0076: // referenced by the instruction cache
0077: this .collector = new InstructionCollector(this .options
0078: .getBoolean(Compiler.DISABLE_CONSTANT_POOL), true);
0079: translateInternal(program, "b", true);
0080: return program;
0081: }
0082:
0083: public String newLabel(SimpleNode node) {
0084: return newLabel(node, null);
0085: }
0086:
0087: public String newLabel(SimpleNode node, String name) {
0088: return collector.newLabel((name != null ? name + ":" : "")
0089: + node.filename + ":" + node.beginLine);
0090: }
0091:
0092: static LessHalfAssedHashMap XfixInstrs = new LessHalfAssedHashMap();
0093: static {
0094: XfixInstrs.put(ParserConstants.INCR, Instructions.Increment);
0095: XfixInstrs.put(ParserConstants.DECR, Instructions.Decrement);
0096: };
0097:
0098: static LessHalfAssedHashMap UnopInstrs = new LessHalfAssedHashMap();
0099: static {
0100: UnopInstrs.put(ParserConstants.PLUS, new Instruction[] {});
0101: UnopInstrs.put(ParserConstants.MINUS, new Instruction[] {
0102: Instructions.PUSH.make(-1), Instructions.MULTIPLY });
0103: UnopInstrs.put(ParserConstants.BANG,
0104: new Instruction[] { Instructions.NOT });
0105: UnopInstrs.put(ParserConstants.TILDE, new Instruction[] {
0106: Instructions.PUSH.make(-1), Instructions.BitwiseXor });
0107: UnopInstrs.put(ParserConstants.TYPEOF,
0108: new Instruction[] { Instructions.TypeOf });
0109: UnopInstrs.put(ParserConstants.VOID, new Instruction[] {
0110: Instructions.POP,
0111: Instructions.PUSH.make(Values.Undefined) });
0112: };
0113:
0114: // Binop translation for swf6. visitBinaryExpression handles swf5
0115: // exceptions.
0116: // TODO: [2006-06-17 ptw] Remove swf6 kludges now that we only
0117: // support swf7 and above
0118: static LessHalfAssedHashMap BinopInstrs = new LessHalfAssedHashMap();
0119: static {
0120: BinopInstrs.put(ParserConstants.PLUS,
0121: new Instruction[] { Instructions.ADD });
0122: BinopInstrs.put(ParserConstants.MINUS,
0123: new Instruction[] { Instructions.SUBTRACT });
0124: BinopInstrs.put(ParserConstants.STAR,
0125: new Instruction[] { Instructions.MULTIPLY });
0126: BinopInstrs.put(ParserConstants.SLASH,
0127: new Instruction[] { Instructions.DIVIDE });
0128: BinopInstrs.put(ParserConstants.REM,
0129: new Instruction[] { Instructions.MODULO });
0130: BinopInstrs.put(ParserConstants.BIT_AND,
0131: new Instruction[] { Instructions.BitwiseAnd });
0132: BinopInstrs.put(ParserConstants.BIT_OR,
0133: new Instruction[] { Instructions.BitwiseOr });
0134: BinopInstrs.put(ParserConstants.XOR,
0135: new Instruction[] { Instructions.BitwiseXor });
0136: BinopInstrs.put(ParserConstants.LSHIFT,
0137: new Instruction[] { Instructions.ShiftLeft });
0138: BinopInstrs.put(ParserConstants.RSIGNEDSHIFT,
0139: new Instruction[] { Instructions.ShiftRight });
0140: BinopInstrs.put(ParserConstants.RUNSIGNEDSHIFT,
0141: new Instruction[] { Instructions.UShiftRight });
0142: // swf6 returns undefined for comparisons with NaN, it
0143: // is supposed to return false (note that you cannot
0144: // eliminate one NOT by inverting the sense of the
0145: // comparison
0146: BinopInstrs.put(ParserConstants.LT, new Instruction[] {
0147: Instructions.LessThan, Instructions.NOT,
0148: Instructions.NOT });
0149: BinopInstrs.put(ParserConstants.GT, new Instruction[] {
0150: Instructions.GreaterThan, Instructions.NOT,
0151: Instructions.NOT });
0152: // swf6 does not have GE or LE, but inverting the
0153: // complement operator does not work for NaN ordering
0154: // Luckily, LogicalOr coerces undefined to false, so we
0155: // don't have to play the NOT NOT trick above
0156: BinopInstrs.put(ParserConstants.LE, new Instruction[] {
0157: Instructions.SetRegister.make(0), // a b
0158: Instructions.POP, // a
0159: Instructions.DUP, // a a
0160: Instructions.PUSH.make(Values.Register(0)), // a a b
0161: Instructions.EQUALS, // a a==b
0162: Instructions.SWAP, // a==b a
0163: Instructions.PUSH.make(Values.Register(0)), // a==b a b
0164: Instructions.LessThan, // a==b a<b
0165: Instructions.LogicalOr // a==b||a<b
0166: });
0167: BinopInstrs.put(ParserConstants.GE, new Instruction[] {
0168: Instructions.SetRegister.make(0), // a b
0169: Instructions.POP, // a
0170: Instructions.DUP, // a a
0171: Instructions.PUSH.make(Values.Register(0)), // a a b
0172: Instructions.EQUALS, // a a==b
0173: Instructions.SWAP, // a==b a
0174: Instructions.PUSH.make(Values.Register(0)), // a==b a b
0175: Instructions.GreaterThan, // a==b a>b
0176: Instructions.LogicalOr // a==b||a>b
0177: });
0178: BinopInstrs.put(ParserConstants.EQ,
0179: new Instruction[] { Instructions.EQUALS });
0180: BinopInstrs.put(ParserConstants.SEQ,
0181: new Instruction[] { Instructions.StrictEquals });
0182: // swf6 does not have NE or SNE either, but inverting
0183: // the complement is correct for NaN
0184: BinopInstrs.put(ParserConstants.NE, new Instruction[] {
0185: Instructions.EQUALS, Instructions.NOT });
0186: BinopInstrs.put(ParserConstants.SNE, new Instruction[] {
0187: Instructions.StrictEquals, Instructions.NOT });
0188: BinopInstrs.put(ParserConstants.INSTANCEOF,
0189: new Instruction[] { Instructions.InstanceOf });
0190: // Approximate a in b as b.a =! void 0
0191: BinopInstrs.put(ParserConstants.IN, new Instruction[] {
0192: Instructions.SWAP, Instructions.GetMember,
0193: Instructions.PUSH.make(Values.Undefined),
0194: Instructions.StrictEquals, Instructions.NOT });
0195: };
0196:
0197: static LessHalfAssedHashMap AssignOpTable = new LessHalfAssedHashMap();
0198: static {
0199: AssignOpTable.put(ParserConstants.PLUSASSIGN,
0200: ParserConstants.PLUS);
0201: AssignOpTable.put(ParserConstants.MINUSASSIGN,
0202: ParserConstants.MINUS);
0203: AssignOpTable.put(ParserConstants.STARASSIGN,
0204: ParserConstants.STAR);
0205: AssignOpTable.put(ParserConstants.SLASHASSIGN,
0206: ParserConstants.SLASH);
0207: AssignOpTable.put(ParserConstants.ANDASSIGN,
0208: ParserConstants.BIT_AND);
0209: AssignOpTable.put(ParserConstants.ORASSIGN,
0210: ParserConstants.BIT_OR);
0211: AssignOpTable.put(ParserConstants.XORASSIGN,
0212: ParserConstants.XOR);
0213: AssignOpTable.put(ParserConstants.REMASSIGN,
0214: ParserConstants.REM);
0215: AssignOpTable.put(ParserConstants.LSHIFTASSIGN,
0216: ParserConstants.LSHIFT);
0217: AssignOpTable.put(ParserConstants.RSIGNEDSHIFTASSIGN,
0218: ParserConstants.RSIGNEDSHIFT);
0219: AssignOpTable.put(ParserConstants.RUNSIGNEDSHIFTASSIGN,
0220: ParserConstants.RUNSIGNEDSHIFT);
0221: };
0222:
0223: // Code to meter a function call. If name is set, uses that,
0224: // otherwise uses arguments.callee.name. This code must be appended
0225: // to the function prefix or suffix, as appropriate
0226: SimpleNode[] meterFunctionEvent(SimpleNode node, String event,
0227: String name) {
0228: String getname;
0229: if (name != null) {
0230: getname = "'" + name + "'";
0231: } else {
0232: getname = "arguments.callee.name";
0233: }
0234:
0235: // Note _root.$lzprofiler can be undedefined to disable profiling
0236: // at run time.
0237:
0238: // N.B., According to the Javascript spec, getTime() returns
0239: // the time in milliseconds, but we have observed that the
0240: // Flash player on some platforms tries to be accurate to
0241: // microseconds (by including fractional milliseconds). On
0242: // other platforms, the time is not even accurate to
0243: // milliseconds, hence the kludge to manually increment the
0244: // clock to create a monotonic ordering.
0245:
0246: // The choice of 0.01 to increment by is based on the
0247: // observation that when floats are used as member names in an
0248: // object they are coerced to strings with only 15 significant
0249: // digits. This should suffice for the next (10^13)-1
0250: // microseconds (about 300 years).
0251:
0252: // TODO [2005-05016 ptw] (LPP-350) $flasm can clobber registers, so
0253: // we have to refresh then for each event
0254: String code = "" + "{"
0255: + "\n#pragma 'warnUndefinedReferences=false'\n"
0256: + "var $lzsc$lzp = _root['$lzprofiler'];"
0257: + "if ($lzsc$lzp) {"
0258: + "var $lzsc$tick = $lzsc$lzp.tick;"
0259: + "var $lzsc$now = (new Date).getTime();"
0260: + "if ($lzsc$tick >= $lzsc$now) {"
0261: + "$lzsc$now = $lzsc$tick + 0.0078125;" + "}"
0262: + "$lzsc$lzp.tick = $lzsc$now;" + "$lzsc$lzp." + event
0263: + "[$lzsc$now] = " + getname + ";" + "}" + "}" + "";
0264: return (new Compiler.Parser()).parse(code).getChildren();
0265: }
0266:
0267: // Only used by warning generator, hence not metered.
0268: // FIXME: [2006-01-17 ptw] Regression compatibility Object -> String
0269: void report(String reportMethod, SimpleNode node, Object message) {
0270: collector.push(message);
0271: collector.push(node.beginLine);
0272: collector.push(node.filename);
0273: collector.push(3);
0274: collector.push(reportMethod);
0275: collector.emit(Instructions.CallFunction);
0276: }
0277:
0278: // Only used by warning generator, hence not metered.
0279: // FIXME: [2006-01-17 ptw] Regression compatibility Object -> String
0280: void report(String reportMethod, SimpleNode node, Object message,
0281: Instruction inst) {
0282: // the dup is already emitted, this is a kludge
0283: assert Instructions.DUP.equals(inst);
0284: collector.push(message);
0285: collector.push(node.beginLine);
0286: collector.push(node.filename);
0287: collector.push(4);
0288: collector.push(reportMethod);
0289: collector.emit(Instructions.CallFunction);
0290: }
0291:
0292: // Emits code to check that a function is defined. If reference is
0293: // set, expects the function reference to be at the top of the stack
0294: // when called, otherwise expects the function object.
0295: // TODO: [2006-01-04 ptw] Rewrite as a source transform
0296: void checkUndefinedFunction(SimpleNode node, String reference) {
0297: if (options.getBoolean(Compiler.WARN_UNDEFINED_REFERENCES)
0298: && node.filename != null) {
0299: String label = newLabel(node);
0300: collector.emit(Instructions.DUP); // ref ref
0301: // Get the value of a function reference
0302: if (reference != null) {
0303: collector.emit(Instructions.GetVariable); // ref val
0304: }
0305: collector.emit(Instructions.DUP); // ref val val
0306: collector.emit(Instructions.TypeOf); // ref val type
0307: collector.push("function"); // ref val type "function"
0308: collector.emit(Instructions.StringEqual); // ref val type=="function"
0309: collector.emit(Instructions.BranchIfTrue.make(label));
0310: // FIXME: [2006-01-17 ptw] Regression compatibility: 0 -> ""
0311: report("$reportNotFunction", node,
0312: reference != null ? (Object) reference
0313: : (Object) (new Integer(0)),
0314: Instructions.DUP);
0315: collector.emit(Instructions.LABEL.make(label));
0316: collector.emit(Instructions.POP); // pop error return
0317: }
0318: }
0319:
0320: // Emits code to check that an object method is defined. Expects the
0321: // object to be at the top of stack when called and does a trial
0322: // GetMember on methodName to verify that it is a function. Object is
0323: // left on the stack.
0324: // TODO: [2006-01-04 ptw] Rewrite as a source transform
0325: void checkUndefinedMethod(SimpleNode node, String methodName) {
0326: if (options.getBoolean(Compiler.WARN_UNDEFINED_REFERENCES)
0327: && node.filename != null) {
0328: // Check that object is not undefined
0329: String isUndefined = newLabel(node); // stack: object
0330: collector.emit(Instructions.DUP); // stack: object, object
0331: collector.emit(Instructions.TypeOf); // stack: object, TypeOf(object)
0332: collector.push("undefined"); // stack object, TypeOf(object), "undefined"
0333: collector.emit(Instructions.EQUALS); // stack: object, TypeOf(object) == "undefined"
0334: collector.emit(Instructions.BranchIfTrue.make(isUndefined)); // stack: object
0335: // Check that property is a function (i.e., it is a method)
0336: String isMethod = newLabel(node);
0337: collector.emit(Instructions.DUP); // stack: object, object
0338: collector.push(methodName); // stack: object, object, method
0339: collector.emit(Instructions.GetMember); // stack: object, object.method
0340: collector.emit(Instructions.DUP); // stack object, object.method, object.method
0341: collector.emit(Instructions.TypeOf); // stack object, object.method, TypeOf(object.method)
0342: collector.push("function"); // stack object, object.method, TypeOf(object.method), "function"
0343: collector.emit(Instructions.EQUALS); // stack object, object.method, TypeOf(object.method) == "function"
0344: collector.emit(Instructions.BranchIfTrue.make(isMethod)); // stack object, object.method
0345: report("$reportUndefinedMethod", node, methodName,
0346: Instructions.DUP); // stack: object, null
0347: collector.emit(Instructions.BRANCH.make(isMethod));
0348: collector.emit(Instructions.LABEL.make(isUndefined)); // stack: object
0349: report("$reportUndefinedObjectProperty", node, methodName); // stack: object, null
0350: collector.emit(Instructions.LABEL.make(isMethod));
0351: collector.emit(Instructions.POP); // stack: object
0352: }
0353: }
0354:
0355: void translateInternal(SimpleNode program, String cpass, boolean top) {
0356: assert program instanceof ASTProgram;
0357: this .context = new TranslationContext(ASTProgram.class, null);
0358: visitProgram(program, program.getChildren(), cpass, top);
0359: }
0360:
0361: String prevStatFile = null;
0362: int prevStatLine = -1;
0363:
0364: void showStats(SimpleNode node) {
0365: if (!options.getBoolean(Compiler.INSTR_STATS)) {
0366: return;
0367: }
0368: String statFile;
0369: int statLine;
0370: if (node != null) {
0371: statFile = node.filename;
0372: statLine = node.beginLine;
0373: } else if (prevStatFile != null) {
0374: statFile = prevStatFile;
0375: statLine = prevStatLine + 1;
0376: } else {
0377: return;
0378: }
0379: if (prevStatFile.equals(statFile) && prevStatLine == statLine) {
0380: return;
0381: }
0382: collector.emit(Instructions.CHECKPOINT.make(statFile + ":"
0383: + statLine));
0384: prevStatFile = statFile;
0385: prevStatLine = statLine;
0386: }
0387:
0388: public SimpleNode visitProgram(SimpleNode node,
0389: SimpleNode[] directives, String cpass) {
0390: return visitProgram(node, directives, cpass, false);
0391: }
0392:
0393: public SimpleNode visitProgram(SimpleNode node,
0394: SimpleNode[] directives, String cpass, boolean top) {
0395: // cpass is "b"oth, 1, or 2
0396: assert "b".equals(cpass) || "1".equals(cpass)
0397: || "2".equals(cpass) : "bad pass: " + cpass;
0398: if ("b".equals(cpass)) {
0399: visitProgram(node, directives, "1", top);
0400: // Everything is done in one pass for now.
0401: // visitProgram(node, directives, "2", top);
0402: return node;
0403: }
0404: if ("1".equals(cpass) && top &&
0405: // Here this means 'compiling the LFC' we only want to emit
0406: // the constants into the LFC
0407: // FIXME: There needs to be a way that the object writer
0408: // ensures that the constants the LZX is compiled with are the
0409: // same ones as are set in the LFC it is linked to
0410: options
0411: .getBoolean(Compiler.FLASH_COMPILER_COMPATABILITY)) {
0412: // emit compile-time contants to runtime
0413: Map constants = (Map) options
0414: .get(Compiler.COMPILE_TIME_CONSTANTS);
0415: if (constants != null) {
0416: for (Iterator i = constants.entrySet().iterator(); i
0417: .hasNext();) {
0418: Map.Entry entry = (Map.Entry) i.next();
0419: collector.push(entry.getKey());
0420: collector.push(entry.getValue());
0421: collector.emit(Instructions.VarEquals);
0422: }
0423: }
0424: }
0425: int index = 0;
0426: int len = directives.length;
0427: while (index < len) {
0428: SimpleNode directive = directives[index];
0429: index += 1;
0430: SimpleNode[] children = directive.getChildren();
0431: if (directive instanceof ASTDirectiveBlock) {
0432: Compiler.OptionMap savedOptions = options;
0433: try {
0434: options = options.copy();
0435: visitProgram(directive, children, cpass);
0436: } finally {
0437: options = savedOptions;
0438: }
0439: continue;
0440: } else if (directive instanceof ASTIfDirective) {
0441: if (!options
0442: .getBoolean(Compiler.CONDITIONAL_COMPILATION)) {
0443: // TBD: different type; change to CONDITIONALS
0444: throw new CompilerError("`if` at top level");
0445: }
0446: Boolean value = evaluateCompileTimeConditional(directive
0447: .get(0));
0448: if (value == null) {
0449: throw new CompilerError(
0450: "undefined compile-time conditional "
0451: + Compiler.nodeString(directive
0452: .get(0)));
0453: }
0454: if (value.booleanValue()) {
0455: visitProgram(directive, directive.get(1)
0456: .getChildren(), cpass);
0457: } else if (directive.size() > 2) {
0458: visitProgram(directive, directive.get(2)
0459: .getChildren(), cpass);
0460: }
0461: continue;
0462: } else if (directive instanceof ASTIncludeDirective) {
0463: // Disabled by default, since it isn't supported in the
0464: // product. (It doesn't go through the compilation
0465: // manager for dependency tracking.)
0466: if (!options.getBoolean(Compiler.INCLUDES)) {
0467: throw new UnimplementedError(
0468: "unimplemented: #include", directive);
0469: }
0470: String userfname = (String) ((ASTLiteral) directive
0471: .get(0)).getValue();
0472: translateInclude(userfname, cpass);
0473: continue;
0474: } else if (directive instanceof ASTPragmaDirective) {
0475: visitPragmaDirective(directive, directive.getChildren());
0476: continue;
0477: }
0478: if ("1".equals(cpass)) {
0479: // Function, class, and top-level expressions are processed in pass 1
0480: if (directive instanceof ASTFunctionDeclaration) {
0481: visitStatement(directive);
0482: } else if (directive instanceof ASTClassDefinition) {
0483: visitClassDefinition(directive, directive
0484: .getChildren());
0485: } else if (directive instanceof ASTModifiedDefinition) {
0486: visitModifiedDefinition(directive, directive
0487: .getChildren());
0488: } else if (directive instanceof ASTStatement) {
0489: // Statements are processed in pass 1 for now
0490: visitStatement(directive);
0491: } else {
0492: visitExpression(directive, false);
0493: }
0494: }
0495: if ("2".equals(cpass)) {
0496: // There is no pass 2 any more
0497: assert false : "bad pass " + cpass;
0498: }
0499: }
0500: showStats(node);
0501: return node;
0502: }
0503:
0504: public SimpleNode visitTryStatement(SimpleNode node,
0505: SimpleNode[] children) {
0506: SimpleNode block = children[0];
0507: int len = children.length;
0508: assert len == 2 || len == 3;
0509: SimpleNode catchNode = null;
0510: SimpleNode finallyNode = null;
0511: int flags = 0;
0512:
0513: if (len == 2) {
0514: // Could be catch or finally clause
0515: if (children[1] instanceof ASTCatchClause) {
0516: catchNode = children[1];
0517: } else {
0518: finallyNode = children[1];
0519: }
0520: } else {
0521: catchNode = children[1];
0522: finallyNode = children[2];
0523: }
0524:
0525: String catchVarName = "";
0526:
0527: // For catch and finally, reach down to get
0528: // the target node to visit.
0529: //
0530: if (catchNode != null) {
0531: SimpleNode[] catchchildren = catchNode.getChildren();
0532: SimpleNode id = catchchildren[0];
0533: assert id instanceof ASTIdentifier;
0534: catchVarName = ((ASTIdentifier) id).getName();
0535: catchNode = catchchildren[1];
0536: flags |= Instructions.TryInstruction.FLAGS_HAS_CATCH;
0537: }
0538: if (finallyNode != null) {
0539: finallyNode = finallyNode.getChildren()[0];
0540: flags |= Instructions.TryInstruction.FLAGS_HAS_FINALLY;
0541: }
0542:
0543: // Try statement code looks like this in the general case:
0544: //
0545: // try size0, size1, size2, 'varname'
0546: // label0:
0547: // ...try-block...
0548: // branch label2:
0549: // label1:
0550: // ...catch-block...
0551: // label2:
0552: // ...finally-block...
0553: // label3:
0554: //
0555: // where sizeN is the size of the code block between labelN and labelN+1.
0556: // either catch or finally blocks may be missing. Whether or not
0557: // they are missing, we push out all the labels, it just makes it
0558: // easier to calculate the block sizes.
0559:
0560: ArrayList code = new ArrayList();
0561:
0562: // The first six args are 3 pairs of labels to represent the size of
0563: // each code block, e.g. label1 - label0 => size0
0564: Object[] tryargs = { new Integer(1), new Integer(0), // label1 - label0
0565: new Integer(2), new Integer(1), // label2 - label1
0566: new Integer(3), new Integer(2), // label3 - label2
0567: catchVarName, new Integer(flags) };
0568: code.add(Instructions.TRY.make(tryargs));
0569: code.add(new Integer(0));
0570: code.add(block);
0571: if (catchNode != null) // if catch is missing, no need for branch.
0572: code.add(Instructions.BRANCH.make(2));
0573: code.add(new Integer(1));
0574: if (catchNode != null)
0575: code.add(catchNode);
0576: code.add(new Integer(2));
0577: if (finallyNode != null)
0578: code.add(finallyNode);
0579: code.add(new Integer(3));
0580:
0581: translateControlStructure(node, code.toArray());
0582:
0583: return node;
0584: }
0585:
0586: public SimpleNode visitThrowStatement(SimpleNode node,
0587: SimpleNode[] children) {
0588: assert children.length == 1 : "throw statement missing expression";
0589: SimpleNode expr = children[0];
0590:
0591: visitExpression(expr);
0592: collector.emit(Instructions.THROW);
0593: return node;
0594: }
0595:
0596: SimpleNode translateInclude(String userfname, String cpass) {
0597:
0598: if (Compiler.CachedInstructions == null) {
0599: Compiler.CachedInstructions = new ScriptCompilerCache();
0600: }
0601:
0602: File file = includeNameToFile(userfname);
0603: String source = includeFileToSourceString(file, userfname);
0604:
0605: try {
0606: String optionsKey = getCodeGenerationOptionsKey(Collections
0607: .singletonList(
0608: // The constant pool isn't cached, so it doesn't affect code
0609: // generation so far as the cache is concerned.
0610: Compiler.DISABLE_CONSTANT_POOL));
0611: // If these could be omitted from the key for files that didn't
0612: // reference them, then the cache could be shared between krank
0613: // and krank debug. (The other builds differ either on OBFUSCATE,
0614: // RUNTIME, NAMEFUNCTIONS, or PROFILE, so there isn't any other
0615: // possible sharing.)
0616: String instrsKey = file.getAbsolutePath() + cpass;
0617: // Only cache on file and pass, to keep cache size resonable,
0618: // but check against optionsKey
0619: String instrsChecksum = "" + file.lastModified()
0620: + optionsKey; // source;
0621: List instrs = null;
0622: if (options.getBoolean(Compiler.CACHE_COMPILES)) {
0623: instrs = (List) Compiler.CachedInstructions.get(
0624: instrsKey, instrsChecksum);
0625: }
0626: if ((instrs != null)
0627: && (!options.getBoolean(Compiler.VALIDATE_CACHES))) {
0628: collector.appendInstructions(instrs);
0629: } else {
0630: ParseResult result = parseFile(file, userfname, source);
0631: int startpos = collector.size();
0632: if (false && options.getBoolean(Compiler.PROGRESS)) {
0633: System.err.println("Translating " + userfname
0634: + " (pass " + cpass + ")...");
0635: }
0636: translateInternal(result.parse, cpass, false);
0637: if ((!result.hasIncludes)) { // && collector.size() != startpos
0638: assert (!collector.constantsGenerated);
0639: // Copy for cache
0640: List realinstrs = new ArrayList(collector.subList(
0641: startpos, collector.size()));
0642: if ((instrs != null)
0643: && options
0644: .getBoolean(Compiler.VALIDATE_CACHES)) {
0645: if ((!realinstrs.equals(instrs))) {
0646: System.err.println("Bad instr cache for "
0647: + instrsKey + ": " + instrs
0648: + " != " + realinstrs);
0649: }
0650: }
0651: // The following line only speeds up buildlfc when
0652: // noConstantPool=true, which produces vastly
0653: // larger binaries.
0654: //instrs = combineInstructions(instrs, true)
0655: if (options.getBoolean(Compiler.CACHE_COMPILES)) {
0656: Compiler.CachedInstructions.put(instrsKey,
0657: instrsChecksum, realinstrs);
0658: }
0659: }
0660: }
0661: } catch (ParseException e) {
0662: System.err.println("while compiling "
0663: + file.getAbsolutePath());
0664: throw e;
0665: }
0666: return null; // dummy return is ultimately ignored
0667: }
0668:
0669: public SimpleNode visitFunctionDeclaration(SimpleNode node,
0670: SimpleNode[] ast) {
0671: // Inner functions are handled by translateFunction
0672: if (ASTProgram.class.equals(context.type)) {
0673: assert (!options.getBoolean(Compiler.CONSTRAINT_FUNCTION));
0674: // Make sure all our top-level functions have root context
0675: String block;
0676: if (true) {
0677: block = newLabel(node);
0678: collector.push("_root");
0679: collector.emit(Instructions.GetVariable);
0680: collector.emit(Instructions.WITH.make(block));
0681: }
0682: translateFunction(node, true, ast);
0683: if (true) {
0684: collector.emit(Instructions.LABEL.make(block));
0685: }
0686: }
0687: return node;
0688: }
0689:
0690: //
0691: // Statements
0692: //
0693:
0694: public SimpleNode visitVariableDeclaration(SimpleNode node,
0695: SimpleNode[] children) {
0696: ASTIdentifier id = (ASTIdentifier) children[0];
0697: if (children.length > 1) {
0698: SimpleNode initValue = children[1];
0699: Reference ref = translateReference(id).preset();
0700: visitExpression(initValue);
0701: ref.init();
0702: } else if (ASTProgram.class.equals(context.type)) {
0703: // In a function, variable declarations will already be done
0704: Reference ref = translateReference(id).preset();
0705: ref.declare();
0706: }
0707: return node;
0708: }
0709:
0710: public SimpleNode visitIfStatement(SimpleNode node,
0711: SimpleNode[] children) {
0712: SimpleNode test = children[0];
0713: SimpleNode a = children[1];
0714: SimpleNode b = (children.length > 2) ? children[2] : null;
0715: // Compile-time conditional evaluations
0716: Boolean value = evaluateCompileTimeConditional(test);
0717: if (value != null) {
0718: if (value.booleanValue()) {
0719: visitStatement(a);
0720: } else if (b != null) {
0721: visitStatement(b);
0722: }
0723: } else if (b != null) {
0724: Object[] code = { new ForValue(test),
0725: Instructions.BranchIfFalse.make(0), a,
0726: Instructions.BRANCH.make(1), new Integer(0), b,
0727: new Integer(1) };
0728: translateControlStructure(node, code);
0729: } else {
0730: Object[] code = { new ForValue(test),
0731: Instructions.BranchIfFalse.make(0), a,
0732: new Integer(0) };
0733: translateControlStructure(node, code);
0734: }
0735: return node;
0736: }
0737:
0738: // for function prefix/suffix parsing
0739: public SimpleNode visitWhileStatement(SimpleNode node,
0740: SimpleNode[] children) {
0741: SimpleNode test = children[0];
0742: SimpleNode body = children[1];
0743: // TODO: [2003-04-15 ptw] bind context slot macro
0744: try {
0745: context = new TranslationContext(ASTWhileStatement.class,
0746: context);
0747: String continueLabel = newLabel(node);
0748: String breakLabel = newLabel(node);
0749: context.setTarget("break", breakLabel);
0750: context.setTarget("continue", continueLabel);
0751: Object[] code = { Instructions.LABEL.make(continueLabel),
0752: new ForValue(test),
0753: Instructions.BranchIfFalse.make(breakLabel), body,
0754: Instructions.BRANCH.make(continueLabel),
0755: Instructions.LABEL.make(breakLabel) };
0756: translateControlStructure(node, code);
0757: return node;
0758: } finally {
0759: context = context.parent;
0760: }
0761: }
0762:
0763: public SimpleNode visitDoWhileStatement(SimpleNode node,
0764: SimpleNode[] children) {
0765: SimpleNode body = children[0];
0766: SimpleNode test = children[1];
0767: // TODO: [2003-04-15 ptw] bind context slot macro
0768: try {
0769: context = new TranslationContext(ASTDoWhileStatement.class,
0770: context);
0771: String continueLabel = newLabel(node);
0772: String breakLabel = newLabel(node);
0773: context.setTarget("break", breakLabel);
0774: context.setTarget("continue", continueLabel);
0775: Object[] code = { Instructions.LABEL.make(continueLabel),
0776: body, new ForValue(test),
0777: Instructions.BranchIfTrue.make(continueLabel),
0778: Instructions.LABEL.make(breakLabel) };
0779: translateControlStructure(node, code);
0780: return node;
0781: } finally {
0782: context = context.parent;
0783: }
0784: }
0785:
0786: public SimpleNode visitForStatement(SimpleNode node,
0787: SimpleNode[] children) {
0788: return translateForStatement(node, children);
0789: }
0790:
0791: public SimpleNode visitForVarStatement(SimpleNode node,
0792: SimpleNode[] children) {
0793: return translateForStatement(node, children);
0794: }
0795:
0796: SimpleNode translateForStatement(SimpleNode node,
0797: SimpleNode[] children) {
0798: SimpleNode init = children[0];
0799: SimpleNode test = children[1];
0800: SimpleNode step = children[2];
0801: SimpleNode body = children[3];
0802: // TODO: [2003-04-15 ptw] bind context slot macro
0803: Compiler.OptionMap savedOptions = options;
0804: try {
0805: options = options.copy();
0806: context = new TranslationContext(ASTForStatement.class,
0807: context);
0808: String continueLabel = newLabel(node);
0809: String breakLabel = newLabel(node);
0810: context.setTarget("break", breakLabel);
0811: context.setTarget("continue", continueLabel);
0812: options.putBoolean(Compiler.WARN_GLOBAL_ASSIGNMENTS, true);
0813: visitStatement(init);
0814: options.putBoolean(Compiler.WARN_GLOBAL_ASSIGNMENTS, false);
0815: Object[] code = { new Integer(0), new ForValue(test),
0816: Instructions.BranchIfFalse.make(breakLabel), body,
0817: Instructions.LABEL.make(continueLabel), step,
0818: Instructions.BRANCH.make(0),
0819: Instructions.LABEL.make(breakLabel) };
0820: translateControlStructure(node, code);
0821: return node;
0822: } finally {
0823: context = context.parent;
0824: options = savedOptions;
0825: }
0826: }
0827:
0828: public SimpleNode visitForInStatement(SimpleNode node,
0829: SimpleNode[] children) {
0830: SimpleNode var = children[0];
0831: SimpleNode obj = children[1];
0832: SimpleNode body = children[2];
0833: translateForInStatement(node, var, Instructions.SetVariable,
0834: obj, body);
0835: return node;
0836: }
0837:
0838: // This works because keys are always strings, and enumerate pushes
0839: // a null before all the keys
0840: public void unwindEnumeration(SimpleNode node) {
0841: String label = newLabel(node);
0842: collector.emit(Instructions.LABEL.make(label));
0843: collector.push(Values.Null);
0844: collector.emit(Instructions.EQUALS);
0845: collector.emit(Instructions.NOT);
0846: collector.emit(Instructions.BranchIfTrue.make(label));
0847: }
0848:
0849: SimpleNode translateForInStatement(SimpleNode node, SimpleNode var,
0850: Instruction varset, SimpleNode obj, SimpleNode body) {
0851: // TODO: [2003-04-15 ptw] bind context slot macro
0852: try {
0853: String continueLabel = newLabel(node);
0854: String breakLabel = newLabel(node);
0855: context = new TranslationContext(ASTForInStatement.class,
0856: context);
0857: context.setTarget("break", breakLabel);
0858: context.setTarget("continue", continueLabel);
0859: context.isEnumeration = true;
0860: Integer r0 = new Integer(0);
0861: visitExpression(obj);
0862: Object[] code = { Instructions.EnumerateValue,
0863: Instructions.LABEL.make(continueLabel),
0864: Instructions.SetRegister.make(r0),
0865: Instructions.PUSH.make(Values.Null),
0866: Instructions.EQUALS,
0867: Instructions.BranchIfTrue.make(breakLabel) };
0868: translateControlStructure(node, code);
0869: Reference ref = translateReference(var).preset();
0870: collector.emit(Instructions.PUSH.make(Values.Register(0)));
0871: if (varset == Instructions.VarEquals) {
0872: ref.init();
0873: } else {
0874: ref.set(true);
0875: }
0876: Object[] moreCode = { body,
0877: Instructions.BRANCH.make(continueLabel),
0878: Instructions.LABEL.make(breakLabel) };
0879: translateControlStructure(node, moreCode);
0880: return node;
0881: } finally {
0882: context = context.parent;
0883: }
0884: }
0885:
0886: SimpleNode translateAbruptCompletion(SimpleNode node, String type,
0887: ASTIdentifier label) {
0888: TranslationContext targetContext = context
0889: .findLabeledContext(label != null ? label.getName()
0890: : null);
0891: if (targetContext == null) {
0892: if (label != null) {
0893: throw new SemanticError("unknown " + type + " target: "
0894: + label.getName(), node);
0895: } else {
0896: throw new SemanticError("can't " + type
0897: + " from current statement", node);
0898: }
0899: }
0900: String targetLabel = (String) targetContext.getTarget(type);
0901: if (targetLabel == null) {
0902: throw new SemanticError("can't " + type
0903: + " from current statement", node);
0904: }
0905: // For each intervening enumeration, pop the stack
0906: TranslationContext c = context;
0907: while (!targetContext.equals(c)) {
0908: c.emitBreakPreamble(node, this );
0909: c = c.getParentStatement();
0910: }
0911: if ("break".equals(type)) {
0912: targetContext.emitBreakPreamble(node, this );
0913: }
0914: collector.emit(Instructions.BRANCH.make(targetLabel));
0915: return node;
0916: }
0917:
0918: public SimpleNode visitReturnStatement(SimpleNode node,
0919: SimpleNode[] children) {
0920: SimpleNode value = children[0];
0921: TranslationContext c = context;
0922: while ((!c.isFunctionBoundary())) {
0923: c.emitBreakPreamble(node, this );
0924: c = c.getParentStatement();
0925: if (c == null) {
0926: throw new SemanticError(
0927: "return not within a function body");
0928: }
0929: }
0930: visitExpression(value);
0931: if (options.getBoolean(Compiler.PROFILE)
0932: || options.getBoolean(Compiler.DEBUG_BACKTRACE)) {
0933: collector.emit(Instructions.BRANCH.make(c.label));
0934: } else {
0935: collector.emit(Instructions.RETURN);
0936: }
0937: return node;
0938: }
0939:
0940: public SimpleNode visitWithStatement(SimpleNode node,
0941: SimpleNode[] children) {
0942: SimpleNode expr = children[0];
0943: SimpleNode stmt = children[1];
0944: Object[] code = { new ForValue(expr),
0945: Instructions.WITH.make(new Integer(0)), stmt,
0946: new Integer(0) };
0947: return translateControlStructure(node, code);
0948: }
0949:
0950: public SimpleNode visitSwitchStatement(SimpleNode node,
0951: SimpleNode[] children) {
0952: SimpleNode expr = children[0];
0953: LinkedHashMap tests = new LinkedHashMap();
0954: LinkedHashMap targets = new LinkedHashMap();
0955: String defaultLabel = null;
0956: String label = newLabel(node, "label" + 0);
0957: for (int i = 1, len = children.length; i < len; i++) {
0958: SimpleNode clause = children[i];
0959: if (clause instanceof ASTDefaultClause) {
0960: if (defaultLabel != null) {
0961: throw new SemanticError("duplicate default clause");
0962: }
0963: defaultLabel = label;
0964: // Empty cases share label with subsequent
0965: if (clause.size() > 0) {
0966: targets.put(label, clause.get(0));
0967: label = newLabel(node, "label" + i);
0968: }
0969: } else {
0970: assert clause instanceof ASTCaseClause : "case clause expected";
0971: tests.put(clause.get(0), label);
0972: // Empty cases share label with subsequent
0973: if (clause.size() > 1) {
0974: targets.put(label, clause.get(1));
0975: label = newLabel(node, "label" + i);
0976: }
0977: }
0978: }
0979: String finalLabel = newLabel(node, "finalLabel");
0980: // TODO: [2003-04-15 ptw] bind context slot macro
0981: try {
0982: context = new TranslationContext(ASTSwitchStatement.class,
0983: context);
0984: context.setTarget("break", finalLabel);
0985: visitExpression(expr);
0986: // TODO: [2002 ows] warn on duplicate tests
0987: for (Iterator i = tests.keySet().iterator(); i.hasNext();) {
0988: SimpleNode value = (SimpleNode) i.next();
0989: String l = (String) tests.get(value);
0990: collector.emit(Instructions.DUP);
0991: visitExpression(value);
0992: collector.emit(Instructions.EQUALS);
0993: collector.emit(Instructions.BranchIfTrue.make(l));
0994: }
0995: if (defaultLabel != null) {
0996: collector.emit(Instructions.BRANCH.make(defaultLabel));
0997: } else {
0998: collector.emit(Instructions.POP);
0999: collector.emit(Instructions.BRANCH.make(finalLabel));
1000: }
1001: String nextLabel = null;
1002: for (Iterator i = targets.keySet().iterator(); i.hasNext();) {
1003: String l = (String) i.next();
1004: SimpleNode stmt = (SimpleNode) targets.get(l);
1005: collector.emit(Instructions.LABEL.make(l));
1006: collector.emit(Instructions.POP);
1007: if (l.equals(defaultLabel)) {
1008: defaultLabel = null;
1009: }
1010: if (nextLabel != null) {
1011: collector.emit(Instructions.LABEL.make(nextLabel));
1012: nextLabel = null;
1013: }
1014: visitStatement(stmt);
1015: Instruction previous = (Instruction) collector
1016: .get(collector.size() - 1);
1017: if (!previous.isUnconditionalRedirect()) {
1018: nextLabel = newLabel(node, "nextLabel");
1019: collector.emit(Instructions.BRANCH.make(nextLabel));
1020: }
1021: }
1022: // Handle empty default as last clause
1023: if (defaultLabel != null) {
1024: collector.emit(Instructions.LABEL.make(defaultLabel));
1025: collector.emit(Instructions.POP);
1026: }
1027: // Handle fall-though in last clause
1028: if (nextLabel != null) {
1029: collector.emit(Instructions.LABEL.make(nextLabel));
1030: }
1031: collector.emit(Instructions.LABEL.make(finalLabel));
1032: } finally {
1033: context = context.parent;
1034: }
1035: return node;
1036: }
1037:
1038: static class LabelMap {
1039: Map labels = new HashMap();
1040: SimpleNode node;
1041: Translator translator;
1042:
1043: LabelMap(SimpleNode node, Translator translator) {
1044: this .node = node;
1045: this .translator = translator;
1046: }
1047:
1048: String lookupLabel(Object n) { // integer -> label
1049: if (labels.containsKey(n)) {
1050: return (String) labels.get(n);
1051: }
1052: String label = translator.newLabel(node);
1053: labels.put(n, label);
1054: return label;
1055: }
1056:
1057: String[] lookupLabels(Object[] vals) {
1058: String[] rets = new String[vals.length];
1059: for (int i = 0; i < vals.length; i++) {
1060: rets[i] = lookupLabel(vals[i]);
1061: }
1062: return rets;
1063: }
1064:
1065: Instruction resolveLocalLabel(Object instr) {
1066: if (instr instanceof Integer) {
1067: return Instructions.LABEL.make(lookupLabel(instr));
1068: }
1069: if (instr instanceof Instructions.TargetInstruction) {
1070: Instructions.TargetInstruction target = (Instructions.TargetInstruction) instr;
1071: Object targetval = target.getTarget();
1072: if (targetval instanceof Object[]) {
1073: return target
1074: .replaceTarget(lookupLabels((Object[]) targetval));
1075: }
1076: if (targetval instanceof Integer) {
1077: return target.replaceTarget(lookupLabel(targetval));
1078: }
1079: }
1080: return (Instruction) instr;
1081: }
1082: }
1083:
1084: // Used to mark items in the sequence to be evaluated for a value
1085: static class ForValue {
1086: SimpleNode node;
1087:
1088: ForValue(SimpleNode node) {
1089: this .node = node;
1090: }
1091: }
1092:
1093: // seq is a list whose items are interpreted thus:
1094: // - numbers are turned into labels
1095: // - target instructions have their targets, which are numbers,
1096: // resolved to labels
1097: // - ForValue's are evaluated as expressions (for value)
1098: // - Other nodes are compiled as statements
1099: // - all other instructions are emitted as is
1100: // Ensure context targets are not ambiguous
1101: SimpleNode translateControlStructure(SimpleNode node, Object[] seq) {
1102: for (Iterator i = context.targets.values().iterator(); i
1103: .hasNext();) {
1104: Object v = i.next();
1105: assert (!(v instanceof Integer)) : "Ambiguous context target "
1106: + v;
1107: }
1108: LabelMap lm = new LabelMap(node, this );
1109: for (int i = 0, len = seq.length; i < len; i++) {
1110: Object item = seq[i];
1111: if (item instanceof Integer || item instanceof Instruction) {
1112: // TODO [2004-03-04 ptw] Handle this in the assembler
1113: if (item instanceof Instructions.BranchIfFalseInstruction) {
1114: collector.emit(Instructions.NOT);
1115: item = Instructions.BranchIfTrue
1116: .make(((Instructions.BranchIfFalseInstruction) item)
1117: .getTarget());
1118: }
1119: collector.emit(lm.resolveLocalLabel(item));
1120: } else if (item instanceof ForValue) {
1121: visitExpression(((ForValue) item).node);
1122: } else {
1123: SimpleNode n = (SimpleNode) item;
1124: visitStatement(n, n.getChildren());
1125: }
1126: }
1127: return node;
1128: }
1129:
1130: //
1131: // Expressions
1132: //
1133:
1134: public SimpleNode visitExpression(SimpleNode node) {
1135: return visitExpression(node, true);
1136: }
1137:
1138: /* This function, unlike the other expression visitors, can be
1139: applied to any expression node, so it dispatches based on the
1140: node's class. */
1141: public SimpleNode visitExpression(SimpleNode node,
1142: boolean isReferenced) {
1143: assert isExpressionType(node) : "" + node + ": "
1144: + (new ParseTreePrinter()).visit(node)
1145: + " is not an expression";
1146:
1147: if (this .debugVisit) {
1148: System.err.println("visitExpression: " + node.getClass());
1149: }
1150:
1151: SimpleNode newNode = dispatchExpression(node, isReferenced);
1152:
1153: if ((!isReferenced) && (newNode != null)) {
1154: collector.emit(Instructions.POP);
1155: newNode = null;
1156: }
1157: if (this .debugVisit) {
1158: if (!newNode.equals(node)) {
1159: System.err.println("expression: " + node + " -> "
1160: + newNode);
1161: }
1162: }
1163: return newNode;
1164: }
1165:
1166: public SimpleNode visitIdentifier(SimpleNode node,
1167: boolean isReferenced, SimpleNode[] children) {
1168: // Side-effect free expressions can be suppressed if not referenced
1169: // Following is disabled by default for regression testing.
1170: // TODO: [2003-02-17 ows] enable this
1171: if ((!isReferenced)
1172: && options
1173: .getBoolean(Compiler.ELIMINATE_DEAD_EXPRESSIONS)) {
1174: return null;
1175: }
1176: if ("_root".equals(((ASTIdentifier) node).getName())
1177: && (!options.getBoolean(Compiler.ALLOW_ROOT))) {
1178: throw new SemanticError("Illegal variable name: " + node,
1179: node);
1180: }
1181: return translateReference(node).get().node;
1182: }
1183:
1184: public SimpleNode visitLiteral(SimpleNode node,
1185: boolean isReferenced, SimpleNode[] children) {
1186: // Side-effect free expressions can be suppressed if not referenced
1187: // Following is disabled by default for regression testing.
1188: // TODO: [2003-02-17 ows] enable this
1189: if ((!isReferenced)
1190: && options
1191: .getBoolean(Compiler.ELIMINATE_DEAD_EXPRESSIONS)) {
1192: return null;
1193: }
1194: Object value = translateLiteralNode(node);
1195: if (value instanceof String) {
1196: String str = (String) value;
1197: // Can't push a constant that will cause the instruction to have
1198: // a byte length > 2^16-1. String constant needs a type byte
1199: // and a (byte) 0 terminator, plus a 2-byte length field.
1200: // Strings are UTF-8, so may be more than one byte per
1201: // character.
1202: int maxBytes = (1 << 16) - 1 - 1 - 1 - 2;
1203: int byteLen = 0;
1204: // Assume worst case (that every character will take 4 bytes
1205: // when UTF-8 encoded), since the only way to be more exact is to
1206: // loop over the string trying different splits and measuring
1207: // the length of the encoded bytes.
1208: byteLen = str.length() * 4;
1209: if (byteLen > maxBytes) {
1210: // Find a split that makes it fit
1211: // wtf doesn't Java have ceil, etc. methods on frickin' ints?
1212: int nChunks = (byteLen + (maxBytes - 1)) / maxBytes; // (int)Math.ceil((double)l/(double)maxBytes);
1213: int strLen = str.length();
1214: int chunkLen = (strLen + (nChunks - 1)) / nChunks; // (int)Math.ceil((double)l/(double)nChunks);
1215: int start = 0, end = chunkLen, next;
1216: while (start < strLen) {
1217: collector.push(str.substring(start, end));
1218: start = end;
1219: next = end + chunkLen;
1220: end = (next > strLen) ? strLen : next;
1221: }
1222: while (--nChunks > 0) {
1223: collector.emit(Instructions.ADD);
1224: }
1225: return node;
1226: }
1227: }
1228: collector.push(value);
1229: return node;
1230: }
1231:
1232: public SimpleNode visitExpressionList(SimpleNode node,
1233: boolean isReferenced, SimpleNode[] children) {
1234: // all but last expression will not be referenced, so
1235: // visitExpression will pop it. If the list is not referenced,
1236: // then the last will be popped too
1237: int i = 0, len = children.length - 1;
1238: for (; i < len; i++) {
1239: visitExpression(children[i], false);
1240: }
1241: return visitExpression(children[len], isReferenced);
1242: }
1243:
1244: public SimpleNode visitEmptyExpression(SimpleNode node,
1245: boolean isReferenced, SimpleNode[] children) {
1246: // Side-effect free expressions can be suppressed if not referenced
1247: if ((!isReferenced)) {
1248: return null;
1249: }
1250: collector.push(Values.Undefined);
1251: return node;
1252: }
1253:
1254: public SimpleNode visitThisReference(SimpleNode node,
1255: boolean isReferenced, SimpleNode[] children) {
1256: // Side-effect free expressions can be suppressed if not referenced
1257: if ((!isReferenced)) {
1258: return null;
1259: }
1260: return translateReference(node).get().node;
1261: }
1262:
1263: public SimpleNode visitArrayLiteral(SimpleNode node,
1264: boolean isReferenced, SimpleNode[] children) {
1265: boolean suppressed = (!isReferenced);
1266: // Wrong evaluation order
1267: int len = 0;
1268: for (int i = children.length - 1; i >= 0; i--) {
1269: if (visitExpression(children[i], isReferenced) != null) {
1270: len++;
1271: suppressed = false;
1272: }
1273: }
1274: if (!suppressed) {
1275: collector.push(len);
1276: collector.emit(Instructions.InitArray);
1277: return node;
1278: } else
1279: return null;
1280: }
1281:
1282: public SimpleNode visitObjectLiteral(SimpleNode node,
1283: boolean isReferenced, SimpleNode[] children) {
1284: boolean isKey = true;
1285: for (int i = 0, len = children.length; i < len; i++) {
1286: SimpleNode item = children[i];
1287: if (isKey && item instanceof ASTIdentifier) {
1288: collector.push(((ASTIdentifier) item).getName());
1289: } else {
1290: visitExpression(item);
1291: }
1292: isKey = (!isKey);
1293: }
1294: collector.push(children.length / 2);
1295: collector.emit(Instructions.InitObject);
1296: return node;
1297: }
1298:
1299: public SimpleNode visitFunctionExpression(SimpleNode node,
1300: boolean isReferenced, SimpleNode[] children) {
1301: Compiler.OptionMap savedOptions = options;
1302: try {
1303: options = options.copy();
1304: options.putBoolean(Compiler.CONSTRAINT_FUNCTION, false);
1305: // Make sure all our top-level functions have root context
1306: String block = null;
1307: if (ASTProgram.class.equals(context.type)) {
1308: block = newLabel(node);
1309: collector.push("_root");
1310: collector.emit(Instructions.GetVariable);
1311: collector.emit(Instructions.WITH.make(block));
1312: }
1313: translateFunction(node, false, children);
1314: if (block != null) {
1315: collector.emit(Instructions.LABEL.make(block));
1316: }
1317: } finally {
1318: options = savedOptions;
1319: }
1320: return node;
1321: }
1322:
1323: public SimpleNode visitFunctionCallParameters(SimpleNode node,
1324: boolean isReferenced, SimpleNode[] children) {
1325: // FIXME: [2002-01-07 ows] This evaluates function call
1326: // parameters in the wrong order.
1327: for (int i = children.length - 1; i >= 0; i--) {
1328: visitExpression(children[i]);
1329: }
1330: collector.push(children.length);
1331: return node;
1332: }
1333:
1334: public SimpleNode visitPropertyIdentifierReference(SimpleNode node,
1335: boolean isReferenced, SimpleNode[] children) {
1336: // TODO: [2002-12-12 ows] consolidate with the code in for..in
1337: // TODO: [2002-12-12 ows] find out how this generalizes to a.b.c
1338: // TODO: [2002-12-18 ows] enabling this saves 2K of the LFC, but
1339: // doesn't seem to improve speed, and changes the background color
1340: // of the menu items in contacts to white (don't know why).
1341: if (false && children[0] instanceof ASTIdentifier
1342: && children[1] instanceof ASTIdentifier) {
1343: collector.push(((ASTIdentifier) children[0]).getName()
1344: + ":" + ((ASTIdentifier) children[1]).getName());
1345: collector.emit(Instructions.GetVariable);
1346: return node;
1347: }
1348: return translateReference(node).get().node;
1349: }
1350:
1351: public SimpleNode visitPropertyValueReference(SimpleNode node,
1352: boolean isReferenced, SimpleNode[] children) {
1353: return translateReference(node).get().node;
1354: }
1355:
1356: void noteCallSite(SimpleNode node) {
1357: // Note current call-site in a function context and backtracing
1358: if ((options.getBoolean(Compiler.DEBUG_BACKTRACE) && (node.beginLine != 0))
1359: && (context.findFunctionContext() != null)) {
1360: Map registers = (Map) context
1361: .get(TranslationContext.REGISTERS);
1362: // We know arguments register will exist if we are doing
1363: // bactraces because it will be referenced in the function
1364: // prefix.
1365: if (registers != null && registers.containsKey("arguments")) {
1366: collector.push(Values
1367: .Register(((Instructions.Register) registers
1368: .get("arguments")).regno));
1369: collector.push("lineno");
1370: collector.push(node.beginLine);
1371: collector.emit(Instructions.SetMember);
1372: }
1373: }
1374: }
1375:
1376: public SimpleNode visitCallExpression(SimpleNode node,
1377: boolean isReferenced, SimpleNode[] children) {
1378: SimpleNode fnexpr = children[0];
1379: SimpleNode[] args = children[1].getChildren();
1380: int arglen = args.length;
1381: if (fnexpr instanceof ASTIdentifier) {
1382: ASTIdentifier fn = (ASTIdentifier) fnexpr;
1383: String name = fn.getName();
1384: // Expose getTimer at our API
1385: //
1386: // FIXME: [2002-12-23 ows] This substitution is not correct
1387: // because it assumes that the value for "getTimer" that"s
1388: // in scope is the global variable.
1389: if ("getTimer".equals(name) && arglen == 0) {
1390: collector.emit(Instructions.GetTimer);
1391: return node;
1392: }
1393: if (options
1394: .getBoolean(Compiler.FLASH_COMPILER_COMPATABILITY)) {
1395: if ("trace".equals(name)) {
1396: if (options.get(Compiler.COMPILE_TRACE) == "flash") {
1397: // FIXME: [2003-01-08 ows] Nicer warning for trace()
1398: // FIXME: [2003-01-08 ows] Warn, at least, when
1399: // there's more than one arg.
1400: visitExpression(args[0]);
1401: // FIXME: [2003-03-13 ptw] Why doesn't the trace instruction work?
1402: collector.push(1);
1403: collector.push("trace");
1404: collector.emit(Instructions.CallFunction);
1405: return node; // was true for trace instruction?
1406: } else if (options.get(Compiler.COMPILE_TRACE) == "debug") {
1407: visitExpression(args[0]);
1408: collector.push("_root");
1409: collector.emit(Instructions.GetVariable);
1410: collector.push("Debug");
1411: collector.emit(Instructions.GetMember);
1412: collector.push("write");
1413: collector.emit(Instructions.CallMethod);
1414: return node;
1415: }
1416: // else fall through
1417: return null;
1418: }
1419: if ("fscommand".equals(name) && arglen == 2) {
1420: assert args[0] instanceof ASTLiteral;
1421: Object v = translateLiteralNode(args[0]);
1422: assert v instanceof String;
1423: collector.push("FSCommand:" + v);
1424: visitExpression(args[1]);
1425: collector.emit(Instructions.GetURL2.make(0));
1426: return null;
1427: }
1428: if ("FSCommand2".equals(name)) {
1429: visitFunctionCallParameters(node, isReferenced,
1430: args);
1431: collector.emit(Instructions.FSCommand2);
1432: return null;
1433: }
1434: if ("removeMovieClip".equals(name) && arglen == 1) {
1435: visitExpression(args[0]);
1436: collector.emit(Instructions.RemoveClip);
1437: return null; // no return value
1438: }
1439: if ("ord".equals(name) && arglen == 1) {
1440: visitExpression(args[0]);
1441: collector.emit(Instructions.ORD);
1442: return node;
1443: }
1444: if ("targetPath".equals(name) && arglen == 1) {
1445: visitExpression(args[0]);
1446: collector.emit(Instructions.TargetPath);
1447: return node;
1448: }
1449: // TODO: [2002-11-30 ows] The following clause needs to
1450: // swap the arguments. To preserve evaluation order,
1451: // it could visit them in reverse order if they don't
1452: // have side effects, otherwise emit SWAP.
1453: //- if "getURL".equals(name) && arglen == 2:
1454: //- collector.emit(Instructions.GetURL2.make(0)); return
1455: if ("getVersion".equals(name) && arglen == 0) {
1456: collector.push("/:$version");
1457: collector.emit(Instructions.GetVariable);
1458: return node;
1459: }
1460: if ("eval".equals(name) && arglen == 1) {
1461: visitExpression(args[0]);
1462: collector.emit(Instructions.GetVariable);
1463: return node;
1464: }
1465: }
1466: }
1467: // TODO: [2002-12-03 ptw] There should be a more general
1468: // mechanism for matching patterns against AST's and replacing
1469: // them.
1470: // FIXME: [2002-12-03 ptw] This substitution is not correct
1471: // because it does not verify that the method being inlined is
1472: // actually LzNode.setAttribute.
1473: if (
1474: // Here this means 'compiling the lfc'
1475: options.getBoolean(Compiler.FLASH_COMPILER_COMPATABILITY)
1476: && (!options.getBoolean("passThrough"))
1477: && (fnexpr instanceof ASTPropertyIdentifierReference)) {
1478: SimpleNode[] fnchildren = fnexpr.getChildren();
1479: String name = ((ASTIdentifier) fnchildren[1]).getName();
1480: // We can't expand this if an expression value is expected,
1481: // since we don't have 'let'
1482: if (name.equals("setAttribute") && (!isReferenced)) {
1483: SimpleNode scope = fnchildren[0];
1484: SimpleNode property = args[0];
1485: SimpleNode value = args[1];
1486: List newBody = new ArrayList();
1487: String this var = "$lzsc$" + UUID().toString();
1488: String propvar = "$lzsc$" + UUID().toString();
1489: String valvar = "$lzsc$" + UUID().toString();
1490: String changedvar = "$lzsc$" + UUID().toString();
1491: String svar = "$lzsc$" + UUID().toString();
1492: String evtvar = "$lzsc$" + UUID().toString();
1493: String decls = "";
1494: ParseTreePrinter ptp = new ParseTreePrinter();
1495: if (scope instanceof ASTIdentifier
1496: || scope instanceof ASTThisReference) {
1497: this var = ptp.visit(scope);
1498: } else {
1499: decls += "var " + this var + " = "
1500: + ptp.visit(scope) + ";";
1501: }
1502: if (property instanceof ASTLiteral
1503: || property instanceof ASTIdentifier) {
1504: propvar = ptp.visit(property);
1505: if (property instanceof ASTLiteral) {
1506: assert propvar.startsWith("\"")
1507: || propvar.startsWith("'");
1508: evtvar = propvar.substring(0, 1) + "on"
1509: + propvar.substring(1);
1510: }
1511: } else {
1512: decls += "var " + propvar + " = "
1513: + ptp.visit(property) + ";";
1514: }
1515: if (value instanceof ASTLiteral
1516: || value instanceof ASTIdentifier) {
1517: valvar = ptp.visit(value);
1518: } else {
1519: decls += "var " + valvar + " = " + ptp.visit(value)
1520: + ";";
1521: }
1522: if (arglen > 2) {
1523: SimpleNode ifchanged = args[2];
1524: if (ifchanged instanceof ASTLiteral
1525: || ifchanged instanceof ASTIdentifier) {
1526: changedvar = ptp.visit(ifchanged);
1527: } else {
1528: decls += "var " + changedvar + " = "
1529: + ptp.visit(ifchanged) + ";";
1530: }
1531: }
1532: newBody.add(parseFragment(decls));
1533: String fragment = "if (! ("
1534: + this var
1535: + ".__LZdeleted "
1536: + ((arglen > 2) ? ("|| (" + changedvar
1537: + " && (" + this var + "[" + propvar
1538: + "] == " + valvar + "))") : "")
1539: + ")) {"
1540: + "var "
1541: + svar
1542: + " = "
1543: + this var
1544: + ".setters;"
1545: + "if ("
1546: + svar
1547: + " && ("
1548: + propvar
1549: + " in "
1550: + svar
1551: + ")) {"
1552: + " "
1553: + this var
1554: + "["
1555: + svar
1556: + "["
1557: + propvar
1558: + "]]("
1559: + valvar
1560: + ");"
1561: + "} else {"
1562: + " if ($debug) {"
1563: + " if ("
1564: + svar
1565: + " == null) {"
1566: + " Debug.warn('null setters on', "
1567: + this var
1568: + ", "
1569: + propvar
1570: + ", "
1571: + valvar
1572: + ");"
1573: + " }"
1574: + " }"
1575: + " "
1576: + this var
1577: + "[ "
1578: + propvar
1579: + " ] = "
1580: + valvar
1581: + ";"
1582: + ((property instanceof ASTLiteral) ? ""
1583: : (" var " + evtvar
1584: + " = (\"on\" + " + propvar + ");"))
1585: + " if (" + evtvar + " in " + this var
1586: + ") {" + " if (" + this var + "["
1587: + evtvar + "].ready) {" + this var + "[ "
1588: + evtvar + " ].sendEvent( " + valvar + " ); }"
1589: + " }" + "}}";
1590: newBody.add(parseFragment(fragment));
1591: SimpleNode newStmts = new ASTStatementList(0);
1592: newStmts.setChildren((SimpleNode[]) newBody
1593: .toArray(new SimpleNode[0]));
1594: visitStatement(newStmts);
1595: return null;
1596: }
1597: }
1598:
1599: noteCallSite(node);
1600: // Okay, it is not going to be transformed. Just do it!
1601: visitFunctionCallParameters(node, isReferenced, args);
1602: boolean isref = translateReferenceForCall(fnexpr, true, node);
1603: if (isref) {
1604: if (fnexpr instanceof ASTPropertyIdentifierReference
1605: || fnexpr instanceof ASTPropertyValueReference) {
1606: collector.emit(Instructions.CallMethod);
1607: } else {
1608: collector.emit(Instructions.CallFunction);
1609: }
1610: } else {
1611: // This is how you invoke a function value
1612: collector.push(Values.Undefined);
1613: collector.emit(Instructions.CallMethod);
1614: }
1615: return node;
1616: }
1617:
1618: public SimpleNode visitSuperCallExpression(SimpleNode node,
1619: boolean isReferenced, SimpleNode[] children) {
1620: SimpleNode n = translateSuperCallExpression(node, isReferenced,
1621: children);
1622: visitCallExpression(n, isReferenced, n.getChildren());
1623: return n;
1624: }
1625:
1626: public SimpleNode visitNewExpression(SimpleNode node,
1627: boolean isReferenced, SimpleNode[] children) {
1628: SimpleNode ref = children[0];
1629: SimpleNode[] args = children[1].getChildren();
1630: noteCallSite(node);
1631: visitFunctionCallParameters(node, isReferenced, args);
1632: boolean isref = translateReferenceForCall(ref, true, node);
1633: if (isref) {
1634: if (ref instanceof ASTPropertyIdentifierReference
1635: || ref instanceof ASTPropertyValueReference) {
1636: collector.emit(Instructions.NewMethod);
1637: } else {
1638: collector.emit(Instructions.NEW);
1639: }
1640: } else {
1641: // This is how you invoke a function value
1642: collector.push(Values.Undefined);
1643: collector.emit(Instructions.NewMethod);
1644: }
1645: return node;
1646: }
1647:
1648: public SimpleNode visitPrefixExpression(SimpleNode node,
1649: boolean isReferenced, SimpleNode[] children) {
1650: SimpleNode op = children[0];
1651: SimpleNode ref = children[1];
1652: return translateXfixExpression(ref, op, true, isReferenced);
1653: }
1654:
1655: public SimpleNode visitPostfixExpression(SimpleNode node,
1656: boolean isReferenced, SimpleNode[] children) {
1657: SimpleNode ref = children[0];
1658: SimpleNode op = children[1];
1659: return translateXfixExpression(ref, op, false, isReferenced);
1660: }
1661:
1662: SimpleNode translateXfixExpression(SimpleNode refnode,
1663: SimpleNode opnode, boolean isPrefix, boolean isReferenced) {
1664: Instruction op = (Instruction) XfixInstrs
1665: .get(((ASTOperator) opnode).getOperator());
1666: if (isReferenced) {
1667: if (!isPrefix) {
1668: // Old value is left on stack
1669: Reference ref = translateReference(refnode, 3).get()
1670: .preset().get();
1671: collector.emit(op);
1672: ref.set();
1673: } else {
1674: // New value is left on stack
1675: Reference ref = translateReference(refnode, 2).preset()
1676: .get();
1677: collector.emit(op);
1678: collector.emit(Instructions.SetRegister.make(0));
1679: ref.set();
1680: collector.push(Values.Register(0));
1681: }
1682: return refnode;
1683: } else {
1684: // Not referenced, no value left on stack
1685: Reference ref = translateReference(refnode, 2).preset()
1686: .get();
1687: collector.emit(op);
1688: ref.set();
1689: return null;
1690: }
1691: }
1692:
1693: public SimpleNode visitUnaryExpression(SimpleNode node,
1694: boolean isReferenced, SimpleNode[] children) {
1695: int op = ((ASTOperator) children[0]).getOperator();
1696: // I guess the parser doesn't know the difference
1697: if (ParserConstants.INCR == (op)
1698: || ParserConstants.DECR == (op)) {
1699: return visitPrefixExpression(node, isReferenced, children);
1700: }
1701: SimpleNode arg = children[1];
1702: // a little bit of constant-folding, so that "-1" looks like a constant
1703: if (ParserConstants.MINUS == (op) && arg instanceof ASTLiteral) {
1704: Object v = translateLiteralNode(arg);
1705: if (v instanceof Number) {
1706: // This works because swf represents all numbers as doubles
1707: collector
1708: .push(new Double((-((Number) v).doubleValue())));
1709: return node;
1710: }
1711: }
1712: // special-cased, since this operates on a ref rather than a value
1713: if (ParserConstants.DELETE == (op)) {
1714: boolean isref = translateReferenceForCall(arg);
1715: if (isref) {
1716: collector.emit(Instructions.DELETE);
1717: } else {
1718: collector.emit(Instructions.DELETE2);
1719: }
1720: return node;
1721: }
1722: if (options.getBoolean(Compiler.FLASH_COMPILER_COMPATABILITY)
1723: && ParserConstants.MINUS == (op)) {
1724: collector.push(0);
1725: visitExpression(arg);
1726: collector.emit(Instructions.SUBTRACT);
1727: return node;
1728: }
1729: // special-case typeof(variable) to not emit undefined-variable
1730: // checks so there is a warning-free way to check for undefined
1731: if (ParserConstants.TYPEOF == (op)
1732: && (arg instanceof ASTIdentifier
1733: || arg instanceof ASTPropertyValueReference || arg instanceof ASTPropertyIdentifierReference)) {
1734: translateReference(arg).get(false);
1735: } else {
1736: visitExpression(arg);
1737: }
1738: Instruction[] instrs = (Instruction[]) UnopInstrs.get(op);
1739: assert instrs != null : "No instrr for op " + op;
1740: if (options.getBoolean(Compiler.FLASH_COMPILER_COMPATABILITY)
1741: && ParserConstants.TILDE == (op)) {
1742: instrs = new Instruction[] {
1743: Instructions.PUSH.make(new Long(0xffffffffL)),
1744: Instructions.BitwiseXor };
1745: }
1746: for (int i = 0, len = instrs.length; i < len; i++) {
1747: collector.emit(instrs[i]);
1748: }
1749: return node;
1750: }
1751:
1752: public SimpleNode visitBinaryExpressionSequence(SimpleNode node,
1753: boolean isReferenced, SimpleNode[] children) {
1754: SimpleNode a = children[0];
1755: SimpleNode op = children[1];
1756: SimpleNode b = children[2];
1757: return translateBinaryExpression(node, isReferenced,
1758: (ASTOperator) op, a, b);
1759: }
1760:
1761: SimpleNode translateBinaryExpression(SimpleNode node,
1762: boolean isReferenced, ASTOperator op, SimpleNode a,
1763: SimpleNode b) {
1764: if (ParserConstants.CAST == ((ASTOperator) op).getOperator()) {
1765: // Approximate a cast b as a
1766: // TODO: [2008-01-08 ptw] We could typecheck and throw an error
1767: // in debug mode
1768: visitExpression(a);
1769: return node;
1770: }
1771: visitExpression(a);
1772: visitExpression(b);
1773: if (ParserConstants.IS == ((ASTOperator) op).getOperator()) {
1774: // Approximate a is b as b['$lzsc$isa'] ? b.$lzsc$isa(a) : (a
1775: // instanceof b)
1776: ArrayList code = new ArrayList();
1777: code.add(Instructions.DUP); // a b b
1778: code.add(Instructions.PUSH.make("$lzsc$isa"));
1779: code.add(Instructions.GetMember); // a b b.$lzsc$isa
1780: code.add(Instructions.BranchIfTrue.make(1)); // a b
1781: code.add(Instructions.InstanceOf); // (a instanceof b)
1782: code.add(Instructions.BRANCH.make(2));
1783: code.add(new Integer(1)); // a b
1784: code.add(Instructions.PUSH.make(1)); // a b 1
1785: code.add(Instructions.SWAP); // a 1 b
1786: code.add(Instructions.PUSH.make("$lzsc$isa")); // a 1 b '$lzsc$isa'
1787: code.add(Instructions.CallMethod); // b.$lzsc$isa(a)
1788: code.add(new Integer(2));
1789: translateControlStructure(node, code.toArray());
1790: } else {
1791: Instruction[] instrs = (Instruction[]) BinopInstrs.get(op
1792: .getOperator());
1793: for (int i = 0, len = instrs.length; i < len; i++) {
1794: collector.emit(instrs[i]);
1795: }
1796: }
1797: return node;
1798: }
1799:
1800: SimpleNode translateAndOrExpression(SimpleNode node, boolean isand,
1801: SimpleNode a, SimpleNode b) {
1802: visitExpression(a);
1803: collector.emit(Instructions.DUP);
1804: if (isand) {
1805: collector.emit(Instructions.NOT);
1806: }
1807: String label = newLabel(node);
1808: collector.emit(Instructions.BranchIfTrue.make(label));
1809: collector.emit(Instructions.POP);
1810: visitExpression(b);
1811: collector.emit(Instructions.LABEL.make(label));
1812: return node;
1813: }
1814:
1815: public SimpleNode visitConditionalExpression(SimpleNode node,
1816: boolean isReferenced, SimpleNode[] children) {
1817: SimpleNode test = children[0];
1818: SimpleNode a = children[1];
1819: SimpleNode b = children[2];
1820: String l1 = newLabel(node);
1821: String l2 = newLabel(node);
1822: visitExpression(test);
1823: collector.emit(Instructions.BranchIfTrue.make(l1));
1824: visitExpression(b);
1825: collector.emit(Instructions.BRANCH.make(l2));
1826: collector.emit(Instructions.LABEL.make(l1));
1827: visitExpression(a);
1828: collector.emit(Instructions.LABEL.make(l2));
1829: return node;
1830: }
1831:
1832: public SimpleNode visitAssignmentExpression(SimpleNode node,
1833: boolean isReferenced, SimpleNode[] children) {
1834: SimpleNode lhs = children[0];
1835: ASTOperator opnode = (ASTOperator) children[1];
1836: SimpleNode rhs = children[2];
1837: int op = opnode.getOperator();
1838: Reference ref = null;
1839: if (ParserConstants.ASSIGN == (op)) {
1840: ref = translateReference(lhs).preset();
1841: visitExpression(rhs);
1842: } else {
1843: ref = translateReference(lhs, 2).preset();
1844: ref.get();
1845: visitExpression(rhs);
1846: Instruction[] instrs = (Instruction[]) BinopInstrs
1847: .get(AssignOpTable.get(op));
1848: for (int i = 0, len = instrs.length; i < len; i++) {
1849: collector.emit(instrs[i]);
1850: }
1851: }
1852: if (isReferenced) {
1853: collector.emit(Instructions.SetRegister.make(0));
1854: }
1855: ref.set();
1856: if (isReferenced) {
1857: collector.push(Values.Register(0));
1858: // Python version always returned true, but that is clearly wrong
1859: return node;
1860: }
1861: return null;
1862: }
1863:
1864: // useName => declaration not expression
1865: void translateFunction(SimpleNode node, boolean useName,
1866: SimpleNode[] children) {
1867: // label for profiling return
1868: String label = newLabel(node);
1869: // TODO: [2003-04-15 ptw] bind context slot macro
1870: SimpleNode dependencies = null;
1871: // methodName and scriptElement
1872: Compiler.OptionMap savedOptions = options;
1873: try {
1874: options = options.copy();
1875: context = new TranslationContext(
1876: ASTFunctionExpression.class, context, label);
1877: dependencies = translateFunctionInternal(node, useName,
1878: children);
1879: } finally {
1880: options = savedOptions;
1881: context = context.parent;
1882: }
1883: // Dependency function is not compiled in the function context
1884: if (dependencies != null) {
1885: collector.emit(Instructions.DUP);
1886: collector.push("dependencies");
1887: visitExpression(dependencies);
1888: collector.emit(Instructions.SetMember);
1889: }
1890: }
1891:
1892: // Internal helper function for above
1893: // useName => declaration not expression
1894: SimpleNode translateFunctionInternal(SimpleNode node,
1895: boolean useName, SimpleNode[] children) {
1896: // ast can be any of:
1897: // FunctionDefinition(name, args, body)
1898: // FunctionDeclaration(name, args, body)
1899: // FunctionDeclaration(args, body)
1900: // Handle the two arities:
1901: String functionName = null;
1902: SimpleNode params;
1903: SimpleNode stmts;
1904: SimpleNode depExpr = null;
1905: if (children.length == 3) {
1906: ASTIdentifier functionNameIdentifier = (ASTIdentifier) children[0];
1907: params = children[1];
1908: stmts = children[2];
1909: functionName = functionNameIdentifier.getName();
1910: } else {
1911: params = children[0];
1912: stmts = children[1];
1913: }
1914:
1915: // function block
1916: String block = newLabel(node);
1917: String userFunctionName = null;
1918: String filename = node.filename != null ? node.filename
1919: : "unknown file";
1920: String lineno = "" + node.beginLine;
1921: if (functionName != null) {
1922: userFunctionName = functionName;
1923: } else {
1924: // TODO: [2003-06-19 ptw] (krank) Sanitization of names to
1925: // identifiers moved to krank user, remove #- when it works
1926: //- from string import translate, maketrans
1927: //- trans = maketrans(" /.", "___")
1928: //- filename = translateInternal(node.filename or "unknown file", trans, """);
1929: // Why do .as filenames have quotes around the string?
1930: //- userFunctionName = "%s$%d_%d" % (filename, node.lineNumber, node.columnNumber)
1931: // FIXME: [2006-01-17 ptw] Regression compatibility \" ->
1932: userFunctionName = "" + filename + "#" + lineno + "/"
1933: + node.beginColumn;
1934: }
1935: if ((!useName)) {
1936: functionName = null;
1937: }
1938: // Tell metering to look up the name at runtime if it is not a
1939: // global name (this allows us to name closures more
1940: // mnemonically at runtime
1941: String meterFunctionName = functionName;
1942: Set pnames = new LinkedHashSet();
1943: SimpleNode[] paramIds = params.getChildren();
1944: for (int i = 0, len = paramIds.length; i < len; i++) {
1945: pnames.add(((ASTIdentifier) paramIds[i]).getName());
1946: }
1947: // Pull all the pragmas from the beginning of the
1948: // statement list: process them, and remove them
1949: assert stmts instanceof ASTStatementList;
1950: List stmtList = new ArrayList(Arrays
1951: .asList(stmts.getChildren()));
1952: while (stmtList.size() > 0) {
1953: SimpleNode stmt = (SimpleNode) stmtList.get(0);
1954: if (stmt instanceof ASTPragmaDirective) {
1955: visitStatement(stmt);
1956: stmtList.remove(0);
1957: } else {
1958: break;
1959: }
1960: }
1961: if (options.getBoolean(Compiler.CONSTRAINT_FUNCTION)) {
1962: // assert (functionName != null);
1963: if (ReferenceCollector.DebugConstraints) {
1964: System.err.println("stmts: " + stmts);
1965: }
1966: // Find dependencies.
1967: //
1968: // Compute this before any transformations on the function body.
1969: //
1970: // The job of a constraint function is to compute a value.
1971: // The current implementation inlines the call to set the
1972: // attribute that the constraint is attached to, within the
1973: // constraint function it Walking the statements of
1974: // the function will process the expression that computes
1975: // the value; it will also process the call to
1976: // setAttribute, but ReferenceCollector knows to ignore
1977: //
1978: ReferenceCollector dependencies = new ReferenceCollector(
1979: options.getBoolean(Compiler.COMPUTE_METAREFERENCES));
1980: // Only visit original body
1981: for (Iterator i = stmtList.iterator(); i.hasNext();) {
1982: SimpleNode stmt = (SimpleNode) i.next();
1983: dependencies.visit(stmt);
1984: }
1985: depExpr = dependencies.computeReferences(userFunctionName);
1986: if (options.getBoolean(Compiler.PRINT_CONSTRAINTS)) {
1987: (new ParseTreePrinter()).print(depExpr);
1988: }
1989: }
1990: List prefix = new ArrayList();
1991: List postfix = new ArrayList();
1992: if (options.getBoolean(Compiler.DEBUG_BACKTRACE)) {
1993: prefix
1994: .addAll(Arrays
1995: .asList((new Compiler.Parser())
1996: .parse(
1997: ""
1998: + "{"
1999: + "\n#pragma 'warnUndefinedReferences=false'\n"
2000: + "var $lzsc$s = Debug['backtraceStack'];"
2001: + "if ($lzsc$s) {"
2002: + "var $lzsc$l = $lzsc$s.length;"
2003: + "$lzsc$s.length = $lzsc$l + 1;"
2004: + "arguments['this'] = this;"
2005: + "$lzsc$s[$lzsc$l] = arguments;"
2006: + "if ($lzsc$l > $lzsc$s.maxDepth) {Debug.stackOverflow()};"
2007: + "}" + "}" + "")
2008: .getChildren()));
2009: postfix
2010: .addAll(Arrays
2011: .asList((new Compiler.Parser())
2012: .parse(
2013: ""
2014: + "{"
2015: + "\n#pragma 'warnUndefinedReferences=false'\n"
2016: + "if ($lzsc$s) {"
2017: + "$lzsc$s.length--;"
2018: + "}" + "}" + "")
2019: .getChildren()));
2020: }
2021: if (options.getBoolean(Compiler.PROFILE)) {
2022: prefix.addAll(Arrays.asList(meterFunctionEvent(node,
2023: "calls", meterFunctionName)));
2024: postfix.addAll(Arrays.asList(meterFunctionEvent(node,
2025: "returns", meterFunctionName)));
2026: }
2027:
2028: // Analyze local variables (and functions)
2029: VariableAnalyzer analyzer = new VariableAnalyzer(
2030: params,
2031: options
2032: .getBoolean(Compiler.FLASH_COMPILER_COMPATABILITY));
2033: for (Iterator i = prefix.iterator(); i.hasNext();) {
2034: analyzer.visit((SimpleNode) i.next());
2035: }
2036: for (Iterator i = stmtList.iterator(); i.hasNext();) {
2037: analyzer.visit((SimpleNode) i.next());
2038: }
2039: for (Iterator i = postfix.iterator(); i.hasNext();) {
2040: analyzer.visit((SimpleNode) i.next());
2041: }
2042: analyzer.computeReferences();
2043: // Parameter _must_ be in order
2044: LinkedHashSet parameters = analyzer.parameters;
2045: // Linked for determinism for regression testing
2046: Set variables = analyzer.variables;
2047: LinkedHashMap fundefs = analyzer.fundefs;
2048: Set closed = analyzer.closed;
2049: Set free = analyzer.free;
2050: // Note usage due to activation object and withThis
2051: if (!free.isEmpty()) {
2052: // TODO: [2005-06-29 ptw] with (_root) should not be
2053: // necessary for the activation object case now that it is
2054: // done at top level to get [[scope]] right.
2055: if (options.getBoolean(Compiler.ACTIVATION_OBJECT)) {
2056: analyzer.incrementUsed("_root");
2057: }
2058: if (options.getBoolean(Compiler.WITH_THIS)) {
2059: analyzer.incrementUsed("this");
2060: }
2061: }
2062: Map used = analyzer.used;
2063: // If this is a closure, annotate the Username for metering
2064: if ((!closed.isEmpty()) && (functionName != null)
2065: && options.getBoolean(Compiler.PROFILE)) {
2066: // Is there any other way to construct a closure in js
2067: // other than a function returning a function?
2068: if (context.findFunctionContext().parent
2069: .findFunctionContext() != null) {
2070: userFunctionName = "" + closed + "." + userFunctionName;
2071: }
2072: }
2073: if (false) {
2074: System.err.println(userFunctionName + ":: parameters: "
2075: + parameters + ", variables: " + variables
2076: + ", fundefs: " + fundefs + ", used: " + used
2077: + ", closed: " + closed + ", free: " + free);
2078: }
2079: // Deal with warnings
2080: if (options.getBoolean(Compiler.WARN_UNUSED_PARAMETERS)) {
2081: Set unusedParams = new LinkedHashSet(parameters);
2082: unusedParams.removeAll(used.keySet());
2083: for (Iterator i = unusedParams.iterator(); i.hasNext();) {
2084: System.err.println("Warning: parameter " + i.next()
2085: + " of " + userFunctionName + " unused in "
2086: + filename + "(" + lineno + ")");
2087: }
2088: }
2089: if (options.getBoolean(Compiler.WARN_UNUSED_LOCALS)) {
2090: Set unusedVariables = new LinkedHashSet(variables);
2091: unusedVariables.removeAll(used.keySet());
2092: for (Iterator i = unusedVariables.iterator(); i.hasNext();) {
2093: System.err.println("Warning: variable " + i.next()
2094: + " of " + userFunctionName + " unused in "
2095: + filename + "(" + lineno + ")");
2096: }
2097: }
2098: // auto-declared locals
2099: Set auto = new LinkedHashSet(Instructions.Register.AUTO_REG);
2100: auto.retainAll(used.keySet());
2101: // parameters, locals, and auto-registers
2102: Set known = new LinkedHashSet(parameters);
2103: known.addAll(variables);
2104: known.addAll(auto);
2105: // for now, ensure that super has a value
2106: known.remove("super");
2107: Set knownSet = new LinkedHashSet(known);
2108: Set lowerKnownSet = new LinkedHashSet();
2109: for (Iterator i = knownSet.iterator(); i.hasNext();) {
2110: lowerKnownSet.add(((String) i.next()).toLowerCase());
2111: }
2112: context.setProperty(TranslationContext.VARIABLES, knownSet);
2113: context.setProperty(TranslationContext.LOWERVARIABLES,
2114: lowerKnownSet);
2115:
2116: boolean scriptElement = options
2117: .getBoolean(Compiler.SCRIPT_ELEMENT);
2118: Map registerMap = new HashMap();
2119: Map lowerRegisterMap = new HashMap();
2120: // Always set register map. Inner functions should not see
2121: // parent registers (which they would if the setting of the
2122: // registermap were conditional on function vs. function2)
2123: context.setProperty(TranslationContext.REGISTERS, registerMap);
2124: context.setProperty(TranslationContext.LOWERREGISTERS,
2125: lowerRegisterMap);
2126: // TODO: [2004-03-24] Analyze register usage in $flasm and
2127: // account for it (or rename $flasm regs?)
2128: // NB: Only Flash Player 6r65 or better understands function2
2129: if (options.getBoolean(Compiler.GENERATE_FUNCTION_2)
2130: && (options
2131: .getBoolean(Compiler.FLASH_COMPILER_COMPATABILITY) || options
2132: .getBoolean(Compiler.GENERATE_FUNCTION_2_FOR_LZX))
2133: && (!scriptElement) && (!used.containsKey("eval"))
2134: && (!used.containsKey("$flasm"))) {
2135: Set autoRegisters = new LinkedHashSet();
2136: for (Iterator i = auto.iterator(); i.hasNext();) {
2137: autoRegisters.add(Instructions.Register
2138: .make(((String) i.next())));
2139: }
2140: // fnArgs _must_ be in order
2141: Set fnArgs = new LinkedHashSet(autoRegisters);
2142: SortedMap paramRegisters = new TreeMap();
2143: SortedMap varRegisters = new TreeMap(new DoubleCollator());
2144: // TODO: [2004-03-27 ptw] Should use threshold for
2145: // parameters be 0 or 1? Presumably there is a getVariable
2146: // cost to loading the register.
2147: int j = parameters.size();
2148: for (Iterator i = parameters.iterator(); i.hasNext(); j--) {
2149: String v = (String) i.next();
2150: if (used.containsKey(v) && (!closed.contains(v))) {
2151: Instructions.Register reg = Instructions.Register
2152: .make(v);
2153: fnArgs.add(reg);
2154: paramRegisters.put("" + j, reg);
2155: } else {
2156: // Always accept the arg, even if not used, since they are positional
2157: fnArgs.add(v);
2158: }
2159: }
2160: j = 0;
2161: // FIXME: [2006-01-17 ptw] TreeSet is for Regression compatibility
2162: variables = new TreeSet(variables);
2163: for (Iterator i = variables.iterator(); i.hasNext(); j++) {
2164: String v = (String) i.next();
2165: if (used.containsKey(v) && (!closed.contains(v))) {
2166: Instructions.Register reg = Instructions.Register
2167: .make(v);
2168: // Most used first, original order disambiguates
2169: varRegisters
2170: .put(
2171: new Double(
2172: -(((Integer) used.get(v))
2173: .doubleValue() + (double) j / 1000)),
2174: reg);
2175: } else {
2176: ;
2177: }
2178: }
2179: if ((!autoRegisters.isEmpty())
2180: || (!paramRegisters.isEmpty())
2181: || (!varRegisters.isEmpty())) {
2182: // Don't know how Flash assigns registers (one would
2183: // have thought the parameters should be in stack order
2184: // and others by frequency of use), but we do know the
2185: // auto registers always come first in order and r:0 is
2186: // never assigned. It appears the parameters are
2187: // assigned last.
2188: // TODO: [2004-03-29 ptw] Measure the cost of loading a
2189: // parameter register so we know whether to weight them
2190: // the same as var registers when there aren't enough
2191: // registers
2192: List registers = new ArrayList(autoRegisters);
2193: for (Iterator i = varRegisters.values().iterator(); i
2194: .hasNext();) {
2195: registers.add(i.next());
2196: }
2197: for (Iterator i = paramRegisters.values().iterator(); i
2198: .hasNext();) {
2199: registers.add(i.next());
2200: }
2201: // Assign register numbers [1, 255]
2202: if (registers.size() > 254) {
2203: registers = registers.subList(0, 254);
2204: }
2205: byte regno = 1;
2206: for (Iterator i = registers.iterator(); i.hasNext(); regno++) {
2207: Instructions.Register r = (Instructions.Register) i
2208: .next();
2209: r.regno = regno;
2210: registerMap.put(r.name, r);
2211: lowerRegisterMap.put(r.name.toLowerCase(), r);
2212: }
2213: // It appears you have to always allocate r:0, hence
2214: // regno, not len(registers)
2215: List args = new ArrayList();
2216: args.add(block);
2217: args.add(functionName);
2218: args.add(new Integer(regno));
2219: args.addAll(fnArgs);
2220: collector.emit(Instructions.DefineFunction2.make(args
2221: .toArray()));
2222: } else {
2223: List args = new ArrayList();
2224: args.add(block);
2225: args.add(functionName);
2226: args.addAll(parameters);
2227: collector.emit(Instructions.DefineFunction.make(args
2228: .toArray()));
2229: }
2230: } else {
2231: List args = new ArrayList();
2232: args.add(block);
2233: args.add(functionName);
2234: args.addAll(parameters);
2235: collector.emit(Instructions.DefineFunction.make(args
2236: .toArray()));
2237: }
2238:
2239: int activationObjectSize = 0;
2240: if (scriptElement) {
2241: // Create all variables (including inner functions) in global scope
2242: if (!variables.isEmpty()) {
2243: if (registerMap.containsKey("_root")) {
2244: collector
2245: .push(Values
2246: .Register(((Instructions.Register) registerMap
2247: .get("_root")).regno));
2248: } else {
2249: collector.push("_root");
2250: collector.emit(Instructions.GetVariable);
2251: }
2252: // Optimization dups fetch of root for all but last which consumes it
2253: int j = 0, len = variables.size() - 1;
2254: for (Iterator i = variables.iterator(); i.hasNext(); j++) {
2255: if (j < len) {
2256: collector.emit(Instructions.DUP);
2257: }
2258: collector.push((String) i.next());
2259: collector.push(Values.Undefined);
2260: collector.emit(Instructions.SetMember);
2261: }
2262: }
2263: } else {
2264: // create unregistered, used variables in activation context
2265: LinkedHashSet toCreate = new LinkedHashSet(variables);
2266: toCreate.retainAll(used.keySet());
2267: toCreate.removeAll(registerMap.keySet());
2268: if (options.getBoolean(Compiler.ACTIVATION_OBJECT)) {
2269: for (Iterator i = toCreate.iterator(); i.hasNext();) {
2270: Object var = i.next();
2271: collector.push(var);
2272: collector.push(Values.Undefined);
2273: activationObjectSize += 1;
2274: }
2275: } else {
2276: for (Iterator i = toCreate.iterator(); i.hasNext();) {
2277: Object var = i.next();
2278: collector.push(var);
2279: collector.emit(Instructions.VAR);
2280: }
2281: }
2282: }
2283: // create unregistered, used parameters in activation context
2284: // (only needed for activation object, they are already in context)
2285: if (options.getBoolean(Compiler.ACTIVATION_OBJECT)) {
2286: LinkedHashSet toCreate = new LinkedHashSet(parameters);
2287: toCreate.retainAll(used.keySet());
2288: toCreate.removeAll(registerMap.keySet());
2289: for (Iterator i = toCreate.iterator(); i.hasNext();) {
2290: Object param = i.next();
2291: collector.push(param);
2292: collector.push(param);
2293: collector.emit(Instructions.GetVariable);
2294: activationObjectSize += 1;
2295: }
2296: }
2297: if (activationObjectSize > 0) {
2298: collector.push(activationObjectSize);
2299: collector.emit(Instructions.InitObject);
2300: }
2301: // scriptElements must be compiled inside with(_root) -- that
2302: // is their required environment because all their local
2303: // bindings are transformed to _root bindings (and will not be
2304: // if they are loaded as snippets)
2305: // TODO: [2005-06-29 ptw] with (_root) should not be necessary
2306: // for the activation object case now that it is done at top
2307: // level to get [[scope]] right.
2308: if (((!free.isEmpty()) && activationObjectSize > 0)
2309: || scriptElement) {
2310: if (registerMap.containsKey("_root")) {
2311: collector.push(Values
2312: .Register(((Instructions.Register) registerMap
2313: .get("_root")).regno));
2314: } else {
2315: collector.push("_root");
2316: collector.emit(Instructions.GetVariable);
2317: }
2318: collector.emit(Instructions.WITH.make(block));
2319: }
2320: if ((!free.isEmpty()) && options.getBoolean(Compiler.WITH_THIS)) {
2321: if (registerMap.containsKey("this")) {
2322: collector.push(Values
2323: .Register(((Instructions.Register) registerMap
2324: .get("this")).regno));
2325: } else {
2326: collector.push("this");
2327: collector.emit(Instructions.GetVariable);
2328: }
2329: collector.emit(Instructions.WITH.make(block));
2330: }
2331: if (activationObjectSize > 0) {
2332: collector.emit(Instructions.WITH.make(block));
2333: }
2334: // inner functions do not get scriptElement treatment
2335: options.putBoolean(Compiler.SCRIPT_ELEMENT, false);
2336: // or the magic with(this) treatment
2337: options.putBoolean(Compiler.WITH_THIS, false);
2338: // Now emit functions in the activation context
2339: if (scriptElement) {
2340: // create functions in global scope
2341: // Note: variable has already been declared so SetVariable
2342: // does the right thing
2343: for (Iterator i = fundefs.keySet().iterator(); i.hasNext();) {
2344: String name = (String) i.next();
2345: SimpleNode fun = (SimpleNode) fundefs.get(name);
2346: // Make sure all our top-level functions have root context
2347: String withBlock = newLabel(node);
2348: collector.push("_root");
2349: collector.emit(Instructions.GetVariable);
2350: collector.emit(Instructions.WITH.make(withBlock));
2351: collector.push(name);
2352: translateFunction(fun, false, fun.getChildren());
2353: collector.emit(Instructions.SetVariable);
2354: collector.emit(Instructions.LABEL.make(withBlock));
2355: }
2356: } else {
2357: for (Iterator i = fundefs.keySet().iterator(); i.hasNext();) {
2358: String name = (String) i.next();
2359: SimpleNode fun = (SimpleNode) fundefs.get(name);
2360: if (used.containsKey(name)) {
2361: if ((!registerMap.containsKey(name))) {
2362: collector.push(name);
2363: }
2364: translateFunction(fun, false, fun.getChildren());
2365: if (registerMap.containsKey(name)) {
2366: collector
2367: .emit(Instructions.SetRegister
2368: .make(((Instructions.Register) registerMap
2369: .get(name)).regno));
2370: collector.emit(Instructions.POP);
2371: } else {
2372: collector.emit(Instructions.SetVariable);
2373: }
2374: }
2375: }
2376: } // end of else scriptElement
2377: if (!prefix.isEmpty()) {
2378: visitStatementList(node, (SimpleNode[]) prefix
2379: .toArray(new SimpleNode[0]));
2380: // label flushes optimizer
2381: collector.emit(Instructions.LABEL.make(newLabel(node)));
2382: }
2383: visitStatementList(node, (SimpleNode[]) stmtList
2384: .toArray(new SimpleNode[0]));
2385: // runtime handles implicit return except if postfix
2386: if (!postfix.isEmpty()) {
2387: collector.push(Values.Undefined);
2388: collector.emit(Instructions.LABEL.make(context
2389: .findFunctionContext().label));
2390: visitStatementList(node, (SimpleNode[]) postfix
2391: .toArray(new SimpleNode[0]));
2392: collector.emit(Instructions.RETURN);
2393: }
2394: // close function
2395: collector.emit(Instructions.LABEL.make(block));
2396: if (options.getBoolean(Compiler.NAME_FUNCTIONS)) {
2397: if (functionName != null) {
2398: // Named functions do not leave a value on the stack
2399: collector.push(functionName);
2400: collector.emit(Instructions.GetVariable);
2401: } else {
2402: // Function expression leaves function on stack
2403: collector.emit(Instructions.DUP);
2404: }
2405: collector.push("name");
2406: collector.push(userFunctionName);
2407: collector.emit(Instructions.SetMember);
2408: if (options.getBoolean(Compiler.DEBUG_BACKTRACE)) {
2409: // TODO: [2007-09-04 ptw] Come up with a better way to
2410: // distinguish LFC from user stack frames. See
2411: // lfc/debugger/LzBactrace
2412: String fn = (options
2413: .getBoolean(Compiler.FLASH_COMPILER_COMPATABILITY) ? "lfc/"
2414: : "")
2415: + filename;
2416: if (functionName != null) {
2417: collector.push(functionName);
2418: collector.emit(Instructions.GetVariable);
2419: } else {
2420: collector.emit(Instructions.DUP);
2421: }
2422: collector.push("_dbg_filename");
2423: collector.push(fn);
2424: collector.emit(Instructions.SetMember);
2425: if (functionName != null) {
2426: collector.push(functionName);
2427: collector.emit(Instructions.GetVariable);
2428: } else {
2429: collector.emit(Instructions.DUP);
2430: }
2431: collector.push("_dbg_lineno");
2432: collector.push(lineno);
2433: collector.emit(Instructions.SetMember);
2434: }
2435: }
2436: if (options.getBoolean(Compiler.CONSTRAINT_FUNCTION)) {
2437: return depExpr;
2438: }
2439: return null;
2440: }
2441:
2442: Object translateLiteralNode(SimpleNode node) {
2443: Object value = ((ASTLiteral) node).getValue();
2444: if (value == null) {
2445: return Values.Null;
2446: } else if (value instanceof Boolean) {
2447: return (((Boolean) value).booleanValue()) ? Values.True
2448: : Values.False;
2449: }
2450: return value;
2451: }
2452:
2453: boolean translateReferenceForCall(SimpleNode ast) {
2454: return translateReferenceForCall(ast, false, null);
2455: }
2456:
2457: /* Contract is to leave a reference on the stack that will be
2458: dereferenced by CallFunction, etc. Returns true if it
2459: succeeds. Returns false if the ast is such that only the
2460: value of the reference can be pushed. In this case, the
2461: callee, must use "CallMethod UNDEF" to call the value
2462: instead */
2463: boolean translateReferenceForCall(SimpleNode ast,
2464: boolean checkDefined, SimpleNode node) {
2465: if (checkDefined) {
2466: assert node != null : "Must supply node for checkDefined";
2467: }
2468: if (ast instanceof ASTPropertyIdentifierReference) {
2469: translateReference(ast.get(0)).get();
2470: String name = ((ASTIdentifier) ast.get(1)).getName();
2471: if (checkDefined) {
2472: checkUndefinedMethod(node, name);
2473: }
2474: collector.push(name);
2475: return true;
2476: }
2477: if (ast instanceof ASTPropertyValueReference) {
2478: // TODO: [2002-10-26 ptw] (undefined reference coverage) Check
2479: translateReference(ast.get(0)).get();
2480: visitExpression(ast.get(1));
2481: return true;
2482: }
2483: // The only other reason you visit a reference is to make a funcall
2484: boolean isref = true;
2485: if (ast instanceof ASTIdentifier) {
2486: Reference ref = translateReference(ast);
2487: if (ref instanceof VariableReference
2488: && ((VariableReference) ref).register != null) {
2489: ref.get();
2490: isref = false;
2491: } else {
2492: ref.preset();
2493: }
2494: } else {
2495: visitExpression(ast);
2496: isref = false;
2497: }
2498: if (checkDefined) {
2499: checkUndefinedFunction(
2500: node,
2501: isref && ast instanceof ASTIdentifier ? ((ASTIdentifier) ast)
2502: .getName()
2503: : null);
2504: }
2505: return isref;
2506: }
2507:
2508: /***
2509: * A Reference represents a variable, property, or array reference ---
2510: * a LeftHandSide in the grammar. References can be retrieved or
2511: * assigned.
2512: */
2513: static public abstract class Reference {
2514: public CodeGenerator translator;
2515: public SimpleNode node;
2516: public int referenceCount;
2517: protected Compiler.OptionMap options;
2518: protected InstructionCollector collector;
2519:
2520: public Reference(CodeGenerator translator, SimpleNode node,
2521: int referenceCount) {
2522: this .translator = translator;
2523: this .options = translator.getOptions();
2524: this .collector = translator.getCollector();
2525: this .node = node;
2526: this .referenceCount = referenceCount;
2527: }
2528:
2529: protected void report(String reportMethod, String message) {
2530: // TODO: [2005-12-21 ptw]
2531: // collector.emitCall(reportMethod,
2532: // fname, lineno, propertyName);
2533: collector.push(message);
2534: collector.push(node.beginLine);
2535: collector.push(node.filename);
2536: collector.push(3);
2537: collector.push(reportMethod);
2538: collector.emit(Instructions.CallFunction);
2539: //
2540: collector.emit(Instructions.POP); // pop error return
2541: }
2542:
2543: // Check that the reference count supplied at initialization
2544: // time was large enough.
2545: protected void _pop() {
2546: assert referenceCount > 0;
2547: referenceCount -= 1;
2548: }
2549:
2550: // Emit instructions that push this reference's value onto the
2551: // stack.
2552: public abstract Reference get(boolean checkUndefined);
2553:
2554: public Reference get() {
2555: return get(true);
2556: }
2557:
2558: // Emit instructions that set the stack up to set this
2559: // reference's value. Example use:
2560: // reference.preset()
2561: // generator.push(1)
2562: // reference.set().
2563: public abstract Reference preset();
2564:
2565: // Emit instructions that set the value of this object. See
2566: // preset() for an example.
2567: public abstract Reference set(Boolean warnGlobal);
2568:
2569: public Reference set() {
2570: return set(null);
2571: }
2572:
2573: public Reference set(boolean warnGlobal) {
2574: return set(Boolean.valueOf(warnGlobal));
2575: }
2576:
2577: public Reference declare() {
2578: throw new CompilerError(
2579: "unsupported reference operation: declare");
2580: }
2581:
2582: public Reference init() {
2583: throw new CompilerError(
2584: "unsupported reference operation: init");
2585: }
2586: }
2587:
2588: static public abstract class MemberReference extends Reference {
2589: protected SimpleNode object;
2590:
2591: public MemberReference(CodeGenerator translator,
2592: SimpleNode node, int referenceCount, SimpleNode object) {
2593: super (translator, node, referenceCount);
2594: this .object = object;
2595: }
2596:
2597: // Emits code to check that the object exists before making a
2598: // property reference. Expects the object to be at the top of stack
2599: // when called.
2600: protected void checkUndefinedObjectProperty(String propertyName) {
2601: if (options.getBoolean(Compiler.WARN_UNDEFINED_REFERENCES)
2602: && node.filename != null) {
2603: String label = translator.newLabel(node);
2604: collector.emit(Instructions.DUP);
2605: collector.emit(Instructions.TypeOf);
2606: collector.push("undefined");
2607: collector.emit(Instructions.EQUALS);
2608: collector.emit(Instructions.NOT);
2609: collector.emit(Instructions.BranchIfTrue.make(label));
2610: report("$reportUndefinedObjectProperty", propertyName);
2611: collector.emit(Instructions.LABEL.make(label));
2612: }
2613: }
2614:
2615: // Emits code to check that an object property selector is
2616: // defined. Note this test is a little looser than other undefined
2617: // tests -- we want to warn if someone is using null as a selector
2618: // too, hence the '==undefined' test. Expects the object member to
2619: // be at the top of stack when called.
2620: protected void checkUndefinedPropertySelector(
2621: String propertyName) {
2622: if (options.getBoolean(Compiler.WARN_UNDEFINED_REFERENCES)
2623: && node.filename != null) {
2624: String label = translator.newLabel(node);
2625: collector.emit(Instructions.DUP); // s s
2626: collector.push(Values.Undefined); // s s UNDEF
2627: collector.emit(Instructions.EQUALS); // s s==UNDEF
2628: collector.emit(Instructions.NOT); // s s!=UNDEF
2629: collector.emit(Instructions.BranchIfTrue.make(label));
2630: report("$reportUndefinedProperty", propertyName);
2631: collector.emit(Instructions.LABEL.make(label));
2632: }
2633: }
2634:
2635: // Emits code to check that an object property is defined.
2636: // Expects the object member to be at the top of stack when
2637: // called.
2638: protected void checkUndefinedProperty(String propertyName) {
2639: if (options.getBoolean(Compiler.WARN_UNDEFINED_REFERENCES)
2640: && node.filename != null) {
2641: String label = translator.newLabel(node);
2642: collector.emit(Instructions.DUP);
2643: collector.emit(Instructions.TypeOf);
2644: collector.push("undefined");
2645: collector.emit(Instructions.EQUALS);
2646: collector.emit(Instructions.NOT);
2647: collector.emit(Instructions.BranchIfTrue.make(label));
2648: report("$reportUndefinedProperty", propertyName);
2649: collector.emit(Instructions.LABEL.make(label));
2650: }
2651: }
2652:
2653: protected abstract void pushObject(boolean checkUndefined);
2654:
2655: public Reference preset() {
2656: _pop();
2657: pushObject(true);
2658: return this ;
2659: }
2660:
2661: public Reference set(Boolean warnGlobal) {
2662: collector.emit(Instructions.SetMember);
2663: return this ;
2664: }
2665: }
2666:
2667: static public class VariableReference extends Reference {
2668: TranslationContext context;
2669: public final String name;
2670: public final Instructions.Register register;
2671: boolean known;
2672:
2673: public VariableReference(CodeGenerator translator,
2674: SimpleNode node, int referenceCount, String name) {
2675: super (translator, node, referenceCount);
2676: this .name = name;
2677: this .context = (TranslationContext) translator.getContext();
2678: Map registers = (Map) context
2679: .get(TranslationContext.REGISTERS);
2680: if (registers != null) {
2681: this .register = (Instructions.Register) registers
2682: .get(name);
2683: if ("swf6".equals(Instructions.getRuntime())) {
2684: Map lowerRegisters = (Map) context
2685: .get(TranslationContext.LOWERREGISTERS);
2686: if (register != null
2687: && (!lowerRegisters.containsKey(name
2688: .toLowerCase()))) {
2689: System.err
2690: .println("Warning: Different case used for "
2691: + name
2692: + " in "
2693: + node.filename
2694: + " (" + node.beginLine + ")");
2695: }
2696: }
2697: } else {
2698: this .register = null;
2699: }
2700: Set variables = (Set) context
2701: .get(TranslationContext.VARIABLES);
2702: if (variables != null) {
2703: this .known = variables.contains(name);
2704: if ("swf6".equals(Instructions.getRuntime())) {
2705: Set lowerVariables = (Set) context
2706: .get(TranslationContext.LOWERVARIABLES);
2707: if (known
2708: && (!lowerVariables.contains(name
2709: .toLowerCase()))) {
2710: System.err
2711: .println("Warning: Different case used for "
2712: + name
2713: + " in "
2714: + node.filename
2715: + " (" + node.beginLine + ")");
2716: }
2717: }
2718: // TODO: [2005-12-22 ptw] Not true ECMAscript
2719: // Ensure undefined is "defined"
2720: known |= "undefined".equals(name);
2721: }
2722: }
2723:
2724: // Emits code to check that an object variable is defined.
2725: // Expects the value of the variable to be at the top of stack when
2726: // called.
2727: private void checkUndefinedVariable(SimpleNode node,
2728: String variableName) {
2729: if (options.getBoolean(Compiler.WARN_UNDEFINED_REFERENCES)
2730: && node.filename != null) {
2731: String label = translator.newLabel(node);
2732: collector.emit(Instructions.DUP);
2733: collector.emit(Instructions.TypeOf);
2734: collector.push("undefined");
2735: collector.emit(Instructions.EQUALS);
2736: collector.emit(Instructions.NOT);
2737:
2738: collector.emit(Instructions.BranchIfTrue.make(label));
2739: report("$reportUndefinedVariable", variableName);
2740: collector.emit(Instructions.LABEL.make(label));
2741: }
2742: }
2743:
2744: public Reference get(boolean checkUndefined) {
2745: _pop();
2746: if (register != null) {
2747: collector.emit(Instructions.PUSH.make(Values
2748: .Register(register.regno)));
2749: } else {
2750: collector.push(name);
2751: collector.emit(Instructions.GetVariable);
2752: }
2753: if (checkUndefined && (!known)) {
2754: checkUndefinedVariable(node, name);
2755: }
2756: return this ;
2757: }
2758:
2759: public Reference preset() {
2760: _pop();
2761: if (register == null) {
2762: if ("undefined".equals(name)) {
2763: throw new SemanticError("Invalid l-value", node);
2764: }
2765: collector.push(name);
2766: }
2767: return this ;
2768: }
2769:
2770: public Reference set(Boolean warnGlobal) {
2771: if (warnGlobal == null) {
2772: if (context.type instanceof ASTProgram) {
2773: warnGlobal = Boolean.FALSE;
2774: } else {
2775: warnGlobal = Boolean
2776: .valueOf(options
2777: .getBoolean(Compiler.WARN_GLOBAL_ASSIGNMENTS));
2778: }
2779: }
2780: if ((!known) && warnGlobal.booleanValue()) {
2781: System.err
2782: .println("Warning: Assignment to free variable "
2783: + name
2784: + " in "
2785: + node.filename
2786: + " ("
2787: + node.beginLine + ")");
2788: }
2789: if (register != null) {
2790: collector.emit(Instructions.SetRegister
2791: .make(new Integer(register.regno)));
2792: // TODO: [2004-03-24 ptw] Optimize this away if the value is used
2793: collector.emit(Instructions.POP);
2794: } else {
2795: collector.emit(Instructions.SetVariable);
2796: }
2797: return this ;
2798: }
2799:
2800: public Reference declare() {
2801: // If in a function, already declared
2802: if (!known) {
2803: collector.emit(Instructions.VAR);
2804: }
2805: return this ;
2806: }
2807:
2808: public Reference init() {
2809: // If in a function, already declared
2810: if (known) {
2811: set();
2812: } else {
2813: collector.emit(Instructions.VarEquals);
2814: }
2815: return this ;
2816: }
2817: }
2818:
2819: static public class PropertyReference extends MemberReference {
2820: String propertyName;
2821:
2822: public PropertyReference(CodeGenerator translator,
2823: SimpleNode node, int referenceCount, SimpleNode object,
2824: ASTIdentifier propertyName) {
2825: super (translator, node, referenceCount, object);
2826: this .propertyName = (String) propertyName.getName();
2827: }
2828:
2829: protected void pushObject(boolean checkUndefined) {
2830: translator.visitExpression(object);
2831: if (checkUndefined) {
2832: checkUndefinedObjectProperty(propertyName);
2833: if (propertyName == "undefined") {
2834: throw new SemanticError("Invalid l-value", node);
2835: }
2836: }
2837: collector.push(propertyName);
2838: }
2839:
2840: public Reference get(boolean checkUndefined) {
2841: _pop();
2842: pushObject(checkUndefined);
2843: collector.emit(Instructions.GetMember);
2844: if (checkUndefined) {
2845: checkUndefinedProperty(propertyName);
2846: }
2847: return this ;
2848: }
2849:
2850: }
2851:
2852: static public class IndexReference extends MemberReference {
2853: SimpleNode indexExpr;
2854:
2855: public IndexReference(CodeGenerator translator,
2856: SimpleNode node, int referenceCount, SimpleNode object,
2857: SimpleNode indexExpr) {
2858: super (translator, node, referenceCount, object);
2859: this .indexExpr = indexExpr;
2860: }
2861:
2862: protected void pushObject(boolean checkUndefined) {
2863: // incorrect semantics, but compatible with Flash
2864: translator.visitExpression(object);
2865: if (checkUndefined) {
2866: checkUndefinedObjectProperty("[]");
2867: }
2868: translator.visitExpression(indexExpr);
2869: if (checkUndefined) {
2870: // TODO: [2005-04-17 ptw] Perhaps use Compiler.nodeString(node)
2871: // instead of "[]"?
2872: checkUndefinedPropertySelector("[]");
2873: }
2874: }
2875:
2876: public Reference get(boolean checkUndefined) {
2877: _pop();
2878: pushObject(checkUndefined);
2879: collector.emit(Instructions.GetMember);
2880: // TODO: [2003-05-14 ptw] checkUndefined
2881: if (false) { // (checkUndefined) {
2882: checkUndefinedProperty("[]");
2883: }
2884: return this ;
2885: }
2886:
2887: }
2888:
2889: // NOTE: [2002-10-24 ptw] Not completely right, this handles the case
2890: // where a literal is the target of a method operation. It is like a
2891: // reference but it is not an lvalue.
2892: static public class LiteralReference extends Reference {
2893:
2894: public LiteralReference(CodeGenerator translator,
2895: SimpleNode node, int referenceCount) {
2896: super (translator, node, referenceCount);
2897: }
2898:
2899: public Reference get(boolean checkUndefined) {
2900: _pop();
2901: translator.visitExpression(node);
2902: return this ;
2903: }
2904:
2905: public Reference preset() {
2906: throw new SemanticError("Invalid literal operation", node);
2907: }
2908:
2909: public Reference set(Boolean warnGlobal) {
2910: throw new SemanticError("Invalid literal operation", node);
2911: }
2912: }
2913:
2914: Reference translateReference(SimpleNode node) {
2915: return translateReference(node, 1);
2916: }
2917:
2918: Reference translateReference(SimpleNode node, int referenceCount) {
2919: if (node instanceof ASTIdentifier) {
2920: return new VariableReference(this , node, referenceCount,
2921: ((ASTIdentifier) node).getName());
2922: }
2923: if (node instanceof ASTThisReference) {
2924: return new VariableReference(this , node, referenceCount,
2925: "this");
2926: }
2927: SimpleNode[] args = node.getChildren();
2928: if (node instanceof ASTPropertyIdentifierReference) {
2929: return new PropertyReference(this , node, referenceCount,
2930: args[0], (ASTIdentifier) args[1]);
2931: } else if (node instanceof ASTPropertyValueReference) {
2932: return new IndexReference(this , node, referenceCount,
2933: args[0], args[1]);
2934: }
2935:
2936: return new LiteralReference(this , node, referenceCount);
2937: }
2938: }
2939:
2940: /* J_LZ_COPYRIGHT_BEGIN *******************************************************
2941: * Copyright 2001-2008 Laszlo Systems, Inc. All Rights Reserved. *
2942: * Use is subject to license terms. *
2943: * J_LZ_COPYRIGHT_END *********************************************************/
|