0001: /* -*- mode: Java; c-basic-offset: 2; -*- */
0002:
0003: /**
0004: * Javascript Generation
0005: *
0006: * @author steele@osteele.com
0007: * @author ptw@openlaszlo.org
0008: * @description: JavaScript -> JavaScript translator
0009: *
0010: * Transform the parse tree from ECMA ~4 to ECMA 3. Includes
0011: * analyzing constraint functions and generating their dependencies.
0012: */package org.openlaszlo.sc;
0013:
0014: import java.io.*;
0015: import java.util.*;
0016:
0017: import org.openlaszlo.sc.parser.*;
0018:
0019: public class JavascriptGenerator extends CommonGenerator implements
0020: Translator {
0021:
0022: protected void setRuntime(String runtiem) {
0023: assert org.openlaszlo.compiler.Compiler.SCRIPT_RUNTIMES
0024: .contains(runtime) : "unknown runtime " + runtime;
0025: }
0026:
0027: // Make Javascript globals 'known'
0028: Set globals = new HashSet(Arrays.asList(new String[] { "NaN",
0029: "Infinity", "undefined", "eval", "parseInt", "parseFloat",
0030: "isNaN", "isFinite", "decodeURI", "decodeURIComponent",
0031: "encodeURI", "encodeURIComponent", "Object", "Function",
0032: "Array", "String", "Boolean", "Number", "Date", "RegExp",
0033: "Error", "EvalError", "RangeError", "ReferenceError",
0034: "SyntaxError", "TypeError", "URIError", "Math" }));
0035:
0036: public SimpleNode translate(SimpleNode program) {
0037: // TODO: [2003-04-15 ptw] bind context slot macro
0038: try {
0039: context = new TranslationContext(ASTProgram.class, context);
0040: context.setProperty(TranslationContext.VARIABLES, globals);
0041: return translateInternal(program, "b", true);
0042: } finally {
0043: context = context.parent;
0044: }
0045: }
0046:
0047: public String newLabel(SimpleNode node) {
0048: throw new CompilerImplementationError("nyi: newLabel");
0049: }
0050:
0051: int tempNum = 0;
0052:
0053: String newTemp() {
0054: return newTemp("$lzsc$");
0055: }
0056:
0057: String newTemp(String prefix) {
0058: return prefix + tempNum++;
0059: }
0060:
0061: static LessHalfAssedHashMap XfixInstrs = new LessHalfAssedHashMap();
0062: static {
0063: XfixInstrs.put(ParserConstants.INCR, "+");
0064: XfixInstrs.put(ParserConstants.DECR, "-");
0065: };
0066:
0067: static LessHalfAssedHashMap AssignOpTable = new LessHalfAssedHashMap();
0068: static {
0069: AssignOpTable.put(ParserConstants.PLUSASSIGN, "+");
0070: AssignOpTable.put(ParserConstants.MINUSASSIGN, "-");
0071: AssignOpTable.put(ParserConstants.STARASSIGN, "*");
0072: AssignOpTable.put(ParserConstants.SLASHASSIGN, "/");
0073: AssignOpTable.put(ParserConstants.ANDASSIGN, "&");
0074: AssignOpTable.put(ParserConstants.ORASSIGN, "|");
0075: AssignOpTable.put(ParserConstants.XORASSIGN, "^");
0076: AssignOpTable.put(ParserConstants.REMASSIGN, "%");
0077: AssignOpTable.put(ParserConstants.LSHIFTASSIGN, "<<");
0078: AssignOpTable.put(ParserConstants.RSIGNEDSHIFTASSIGN, ">>");
0079: AssignOpTable.put(ParserConstants.RUNSIGNEDSHIFTASSIGN, ">>>");
0080: };
0081:
0082: // Code to meter a function call. If name is set, uses that,
0083: // otherwise uses arguments.callee._dbg_name. This code must be appended
0084: // to the function prefix or suffix, as appropriate.
0085: //
0086: // NOTE: [2006-06-24 ptw] This is an inline version of the LFC
0087: // `LzProfile.event` method and must be kept in sync with that.
0088: SimpleNode meterFunctionEvent(SimpleNode node, String event,
0089: String name) {
0090: String getname;
0091: if (name != null) {
0092: getname = "'" + name + "'";
0093: } else {
0094: getname = "arguments.callee._dbg_name";
0095: }
0096:
0097: // Note _root.$lzprofiler can be undedefined to disable profiling
0098: // at run time.
0099:
0100: // N.B., According to the Javascript spec, getTime() returns
0101: // the time in milliseconds, but we have observed that the
0102: // Flash player on some platforms tries to be accurate to
0103: // microseconds (by including fractional milliseconds). On
0104: // other platforms, the time is not even accurate to
0105: // milliseconds, hence the kludge to manually increment the
0106: // clock to create a monotonic ordering.
0107:
0108: // The choice of 0.01 to increment by is based on the
0109: // observation that when floats are used as member names in an
0110: // object they are coerced to strings with only 15 significant
0111: // digits. This should suffice for the next (10^13)-1
0112: // microseconds (about 300 years).
0113:
0114: return parseFragment("var $lzsc$lzp = global['$lzprofiler'];"
0115: + "if ($lzsc$lzp) {"
0116: + " var $lzsc$tick = $lzsc$lzp.tick;"
0117: + " var $lzsc$now = (new Date).getTime();"
0118: + " if ($lzsc$tick >= $lzsc$now) {"
0119: + " $lzsc$now = $lzsc$tick + 0.0078125;" + " }"
0120: + " $lzsc$lzp.tick = $lzsc$now;" + " $lzsc$lzp."
0121: + event + "[$lzsc$now] = " + getname + ";" + "}");
0122: }
0123:
0124: // Only used by warning generator, hence not metered.
0125: // FIXME: [2006-01-17 ptw] Regression compatibility Object -> String
0126: String report(String reportMethod, SimpleNode node, Object message) {
0127: return reportMethod + "(" + message + "," + node.filename + ","
0128: + node.beginLine + ")";
0129: }
0130:
0131: // Only used by warning generator, hence not metered.
0132: // FIXME: [2006-01-17 ptw] Regression compatibility Object -> String
0133: String report(String reportMethod, SimpleNode node, Object message,
0134: String extraArg) {
0135: return reportMethod + "(" + message + "," + node.filename + ","
0136: + node.beginLine + "," + extraArg + ")";
0137: }
0138:
0139: // Emits code to check that a function is defined. If reference is
0140: // set, expects the function reference to be at the top of the stack
0141: // when called, otherwise expects the function object.
0142: // TODO: [2006-01-04 ptw] Rewrite as a source transform
0143: SimpleNode checkUndefinedFunction(SimpleNode node,
0144: JavascriptReference reference) {
0145: if (options.getBoolean(Compiler.DEBUG)
0146: && options
0147: .getBoolean(Compiler.WARN_UNDEFINED_REFERENCES)
0148: && node.filename != null) {
0149: return parseFragment("typeof "
0150: + reference.get()
0151: + " != 'function' ? "
0152: + report("$reportNotFunction", node, reference
0153: .get()) + " : " + reference.get());
0154: }
0155: return null;
0156: }
0157:
0158: // Emits code to check that an object method is defined. Does a trial
0159: // fetch of methodName to verify that it is a function.
0160: SimpleNode checkUndefinedMethod(SimpleNode node,
0161: JavascriptReference reference, String methodName) {
0162: if (options.getBoolean(Compiler.DEBUG)
0163: && options
0164: .getBoolean(Compiler.WARN_UNDEFINED_REFERENCES)
0165: && node.filename != null) {
0166: String o = newTemp();
0167: String om = newTemp();
0168: return parseFragment("var "
0169: + o
0170: + " = "
0171: + reference.get()
0172: + ";"
0173: + "if (typeof("
0174: + o
0175: + ") == undefined) {"
0176: + " "
0177: + report("$reportUndefinedObjectProperty", node,
0178: methodName)
0179: + "}"
0180: + "var "
0181: + om
0182: + " = "
0183: + o
0184: + "["
0185: + methodName
0186: + "];"
0187: + "if (typeof("
0188: + om
0189: + ") != 'function') {"
0190: + " "
0191: + report("$reportUndefinedMethod", node,
0192: methodName, om) + "}");
0193: }
0194: return null;
0195: }
0196:
0197: SimpleNode translateInternal(SimpleNode program, String cpass,
0198: boolean top) {
0199: assert program instanceof ASTProgram;
0200: // TODO: [2003-04-15 ptw] bind context slot macro
0201: try {
0202: context = new TranslationContext(ASTProgram.class, context);
0203: return visitProgram(program, program.getChildren(), cpass,
0204: top);
0205: } finally {
0206: context = context.parent;
0207: }
0208: }
0209:
0210: void showStats(SimpleNode node) {
0211: // No implementation to collect stats for Javascript
0212: }
0213:
0214: public SimpleNode visitProgram(SimpleNode node,
0215: SimpleNode[] directives, String cpass) {
0216: return visitProgram(node, directives, cpass, false);
0217: }
0218:
0219: public SimpleNode visitProgram(SimpleNode node,
0220: SimpleNode[] directives, String cpass, boolean top) {
0221: // cpass is "b"oth, 1, or 2
0222: assert "b".equals(cpass) || "1".equals(cpass)
0223: || "2".equals(cpass) : "bad pass: " + cpass;
0224: if ("b".equals(cpass)) {
0225: node = visitProgram(node, directives, "1", top);
0226: // Everything is done in one pass for now.
0227: // directives = node.getChildren();
0228: // node = visitProgram(node, directives, "2", top);
0229: return node;
0230: }
0231: if ("1".equals(cpass) && top) {
0232: // emit compile-time contants to runtime
0233: Map constants = (Map) options
0234: .get(Compiler.COMPILE_TIME_CONSTANTS);
0235: if (constants != null) {
0236: String code = "";
0237: for (Iterator i = constants.entrySet().iterator(); i
0238: .hasNext();) {
0239: Map.Entry entry = (Map.Entry) i.next();
0240: Object value = entry.getValue();
0241: // Python cruft
0242: if (value instanceof String) {
0243: value = "\"" + value + "\"";
0244: } else if ((new Integer(0)).equals(value)) {
0245: value = "false";
0246: } else if ((new Integer(1)).equals(value)) {
0247: value = "true";
0248: }
0249: code += "var " + entry.getKey() + " = " + value
0250: + ";";
0251: }
0252: List c = new ArrayList();
0253: c.add(parseFragment(code));
0254: c.addAll(Arrays.asList(directives));
0255: directives = (SimpleNode[]) c.toArray(directives);
0256: node.setChildren(directives);
0257: }
0258: }
0259: // System.err.println("visitProgram: " + cpass);
0260: for (int index = 0, len = directives.length; index < len; index++) {
0261: SimpleNode directive = directives[index];
0262: SimpleNode newDirective = directive;
0263: SimpleNode[] children = directive.getChildren();
0264: if (directive instanceof ASTDirectiveBlock) {
0265: Compiler.OptionMap savedOptions = options;
0266: try {
0267: options = options.copy();
0268: newDirective = visitProgram(directive, children,
0269: cpass);
0270: } finally {
0271: options = savedOptions;
0272: }
0273: } else if (directive instanceof ASTIfDirective) {
0274: if (!options
0275: .getBoolean(Compiler.CONDITIONAL_COMPILATION)) {
0276: // TBD: different type; change to CONDITIONALS
0277: throw new CompilerError("`if` at top level");
0278: }
0279: Boolean value = evaluateCompileTimeConditional(directive
0280: .get(0));
0281: if (value == null) {
0282: newDirective = visitIfStatement(directive, children);
0283: } else if (value.booleanValue()) {
0284: SimpleNode clause = directive.get(1);
0285: newDirective = visitProgram(clause, clause
0286: .getChildren(), cpass);
0287: } else if (directive.size() > 2) {
0288: SimpleNode clause = directive.get(2);
0289: newDirective = visitProgram(clause, clause
0290: .getChildren(), cpass);
0291: } else {
0292: newDirective = new ASTEmptyExpression(0);
0293: }
0294: } else if (directive instanceof ASTIncludeDirective) {
0295: // Disabled by default, since it isn't supported in the
0296: // product. (It doesn't go through the compilation
0297: // manager for dependency tracking.)
0298: if (!options.getBoolean(Compiler.INCLUDES)) {
0299: throw new UnimplementedError(
0300: "unimplemented: #include", directive);
0301: }
0302: String userfname = (String) ((ASTLiteral) directive
0303: .get(0)).getValue();
0304: newDirective = translateInclude(userfname, cpass);
0305: } else if (directive instanceof ASTProgram) {
0306: // This is what an include looks like in pass 2
0307: newDirective = visitProgram(directive, children, cpass);
0308: } else if (directive instanceof ASTPragmaDirective) {
0309: newDirective = visitPragmaDirective(directive,
0310: directive.getChildren());
0311: } else {
0312: if ("1".equals(cpass)) {
0313: // Function, class, and top-level expressions are processed in pass 1
0314: if (directive instanceof ASTFunctionDeclaration) {
0315: newDirective = visitStatement(directive);
0316: } else if (directive instanceof ASTClassDefinition) {
0317: newDirective = visitStatement(directive);
0318: } else if (directive instanceof ASTModifiedDefinition) {
0319: newDirective = visitModifiedDefinition(
0320: directive, directive.getChildren());
0321: } else if (directive instanceof ASTStatement) {
0322: // Statements are processed in pass 1 for now
0323: newDirective = visitStatement(directive);
0324: ;
0325: } else {
0326: newDirective = visitExpression(directive, false);
0327: }
0328: }
0329: if ("2".equals(cpass)) {
0330: // There is no pass 2 any more
0331: assert false : "bad pass " + cpass;
0332: }
0333: }
0334: if (!newDirective.equals(directive)) {
0335: // System.err.println("directive: " + directive + " -> " + newDirective);
0336: directives[index] = newDirective;
0337: }
0338: }
0339: showStats(node);
0340: return node;
0341: }
0342:
0343: SimpleNode translateInclude(String userfname, String cpass) {
0344:
0345: if (Compiler.CachedInstructions == null) {
0346: Compiler.CachedInstructions = new ScriptCompilerCache();
0347: }
0348:
0349: File file = includeNameToFile(userfname);
0350: String source = includeFileToSourceString(file, userfname);
0351:
0352: try {
0353: String optionsKey = getCodeGenerationOptionsKey(Collections
0354: .singletonList(
0355: // The constant pool isn't cached, so it doesn't affect code
0356: // generation so far as the cache is concerned.
0357: Compiler.DISABLE_CONSTANT_POOL));
0358: // If these could be omitted from the key for files that didn't
0359: // reference them, then the cache could be shared between krank
0360: // and krank debug. (The other builds differ either on OBFUSCATE,
0361: // RUNTIME, NAMEFUNCTIONS, or PROFILE, so there isn't any other
0362: // possible sharing.)
0363: String instrsKey = file.getAbsolutePath();
0364: // Only cache on file and pass, to keep cache size resonable,
0365: // but check against optionsKey
0366: String instrsChecksum = "" + file.lastModified()
0367: + optionsKey; // source;
0368: // Use previously modified parse tree if it exists
0369: SimpleNode instrs = (SimpleNode) Compiler.CachedInstructions
0370: .get(instrsKey + cpass, instrsChecksum);
0371: if (instrs == null) {
0372: ParseResult result = parseFile(file, userfname, source);
0373: if ("1".equals(cpass)) {
0374: instrs = result.parse;
0375: instrs = translateInternal(instrs, cpass, false);
0376: } else if ("2".equals(cpass)) {
0377: instrs = (SimpleNode) Compiler.CachedInstructions
0378: .get(instrsKey + "1", instrsChecksum);
0379: assert instrs != null : "pass 2 before pass 1?";
0380: instrs = translateInternal(instrs, cpass, false);
0381: } else {
0382: assert false : "bad pass " + cpass;
0383: }
0384: if (!result.hasIncludes) {
0385: if (options.getBoolean(Compiler.CACHE_COMPILES)) {
0386: Compiler.CachedInstructions.put(instrsKey
0387: + cpass, instrsChecksum, instrs);
0388: }
0389: }
0390: }
0391: return instrs;
0392: } catch (ParseException e) {
0393: System.err.println("while compiling "
0394: + file.getAbsolutePath());
0395: throw e;
0396: }
0397: }
0398:
0399: public SimpleNode visitFunctionDeclaration(SimpleNode node,
0400: SimpleNode[] ast) {
0401: // Inner functions are handled by translateFunction
0402: if (context.findFunctionContext() != null) {
0403: return null;
0404: } else {
0405: assert (!options.getBoolean(Compiler.CONSTRAINT_FUNCTION));
0406: // Make sure all our top-level functions have root context
0407: if (false && ASTProgram.class.equals(context.type)) {
0408: Map map = new HashMap();
0409: map.put("_1", new Compiler.Splice(ast));
0410: SimpleNode newNode = (new Compiler.Parser())
0411: .substitute("with (_root) { _1 }", map);
0412: return visitStatement(newNode);
0413: } else {
0414: return translateFunction(node, true, ast);
0415: }
0416: }
0417: }
0418:
0419: //
0420: // Statements
0421: //
0422:
0423: public SimpleNode visitVariableStatement(SimpleNode node,
0424: SimpleNode[] children) {
0425: boolean scriptElement = options
0426: .getBoolean(Compiler.SCRIPT_ELEMENT);
0427: if (scriptElement) {
0428: assert children.length == 1;
0429: // In script, variables are declared at the top of the function
0430: // so we convert the variableStatement into a Statement here.
0431: node = new ASTStatement(0);
0432: node.set(0, children[0]);
0433: }
0434: return visitChildren(node);
0435: }
0436:
0437: public SimpleNode visitVariableDeclaration(SimpleNode node,
0438: SimpleNode[] children) {
0439: ASTIdentifier id = (ASTIdentifier) children[0];
0440: boolean scriptElement = options
0441: .getBoolean(Compiler.SCRIPT_ELEMENT);
0442: if (scriptElement) {
0443: if (children.length > 1) {
0444: // In script, variables are declared at the top of the
0445: // function so we convert the declaration into an assignment
0446: // here.
0447: SimpleNode newNode = new ASTAssignmentExpression(0);
0448: newNode.set(0, children[0]);
0449: ASTOperator assign = new ASTOperator(0);
0450: assign.setOperator(ParserConstants.ASSIGN);
0451: newNode.set(1, assign);
0452: newNode.set(2, children[1]);
0453: return visitExpression(newNode);
0454: } else {
0455: // Declarations already handled in a script
0456: return new ASTEmptyExpression(0);
0457: }
0458: } else {
0459: if (children.length > 1) {
0460: SimpleNode initValue = children[1];
0461: JavascriptReference ref = translateReference(id);
0462: children[1] = visitExpression(initValue);
0463: children[0] = ref.init();
0464: return node;
0465: } else {
0466: JavascriptReference ref = translateReference(id);
0467: children[0] = ref.declare();
0468: return node;
0469: }
0470: }
0471: }
0472:
0473: public SimpleNode visitIfStatement(SimpleNode node,
0474: SimpleNode[] children) {
0475: SimpleNode test = children[0];
0476: SimpleNode a = children[1];
0477: SimpleNode b = (children.length > 2) ? children[2] : null;
0478: // Compile-time conditional evaluations
0479: // System.err.println("visitIfStatement: " + (new ParseTreePrinter()).visit(node));
0480: Boolean value = evaluateCompileTimeConditional(test);
0481: if (value != null) {
0482: // System.err.println("" + test + " == " + value);
0483: if (value.booleanValue()) {
0484: return visitStatement(a);
0485: } else if (b != null) {
0486: return visitStatement(b);
0487: } else {
0488: return new ASTEmptyExpression(0);
0489: }
0490: } else if (b != null) {
0491: children[0] = visitExpression(test);
0492: children[1] = visitStatement(a);
0493: children[2] = visitStatement(b);
0494: } else {
0495: children[0] = visitExpression(test);
0496: children[1] = visitStatement(a);
0497: }
0498: return node;
0499: }
0500:
0501: public SimpleNode visitWhileStatement(SimpleNode node,
0502: SimpleNode[] children) {
0503: SimpleNode test = children[0];
0504: SimpleNode body = children[1];
0505: // TODO: [2003-04-15 ptw] bind context slot macro
0506: try {
0507: context = new TranslationContext(ASTWhileStatement.class,
0508: context);
0509: children[0] = visitExpression(test);
0510: children[1] = visitStatement(body);
0511: return node;
0512: } finally {
0513: context = context.parent;
0514: }
0515: }
0516:
0517: public SimpleNode visitDoWhileStatement(SimpleNode node,
0518: SimpleNode[] children) {
0519: SimpleNode body = children[0];
0520: SimpleNode test = children[1];
0521: // TODO: [2003-04-15 ptw] bind context slot macro
0522: try {
0523: context = new TranslationContext(ASTDoWhileStatement.class,
0524: context);
0525: children[0] = visitStatement(body);
0526: children[1] = visitExpression(test);
0527: return node;
0528: } finally {
0529: context = context.parent;
0530: }
0531: }
0532:
0533: public SimpleNode visitForStatement(SimpleNode node,
0534: SimpleNode[] children) {
0535: return translateForStatement(node, children);
0536: }
0537:
0538: public SimpleNode visitForVarStatement(SimpleNode node,
0539: SimpleNode[] children) {
0540: return translateForStatement(node, children);
0541: }
0542:
0543: SimpleNode translateForStatement(SimpleNode node,
0544: SimpleNode[] children) {
0545: SimpleNode init = children[0];
0546: SimpleNode test = children[1];
0547: SimpleNode step = children[2];
0548: SimpleNode body = children[3];
0549: // TODO: [2003-04-15 ptw] bind context slot macro
0550: Compiler.OptionMap savedOptions = options;
0551: try {
0552: options = options.copy();
0553: context = new TranslationContext(ASTForStatement.class,
0554: context);
0555: options.putBoolean(Compiler.WARN_GLOBAL_ASSIGNMENTS, true);
0556: children[0] = visitStatement(init);
0557: options.putBoolean(Compiler.WARN_GLOBAL_ASSIGNMENTS, false);
0558: children[1] = visitExpression(test);
0559: children[3] = visitStatement(body);
0560: children[2] = visitStatement(step);
0561: return node;
0562: } finally {
0563: context = context.parent;
0564: options = savedOptions;
0565: }
0566: }
0567:
0568: public SimpleNode visitForInStatement(SimpleNode node,
0569: SimpleNode[] children) {
0570: SimpleNode var = children[0];
0571: SimpleNode obj = children[1];
0572: SimpleNode body = children[2];
0573: // TODO: [2003-04-15 ptw] bind context slot macro
0574: try {
0575: context = new TranslationContext(ASTForInStatement.class,
0576: context);
0577: children[1] = visitExpression(obj);
0578: JavascriptReference ref = translateReference(var);
0579: children[0] = ref.set(true);
0580: children[2] = visitStatement(body);
0581: return node;
0582: } finally {
0583: context = context.parent;
0584: }
0585: }
0586:
0587: // This works because keys are always strings, and enumerate pushes
0588: // a null before all the keys
0589: public void unwindEnumeration(SimpleNode node) {
0590: }
0591:
0592: SimpleNode translateForInStatement(SimpleNode node, SimpleNode var,
0593: Instructions.Instruction varset, SimpleNode obj,
0594: SimpleNode body) {
0595: // TODO: [2003-04-15 ptw] bind context slot macro
0596: try {
0597: SimpleNode[] children = node.getChildren();
0598: context = new TranslationContext(ASTForInStatement.class,
0599: context);
0600: children[2] = visitExpression(obj);
0601: JavascriptReference ref = translateReference(var);
0602: if (varset == Instructions.VarEquals) {
0603: children[0] = ref.init();
0604: } else {
0605: children[0] = ref.set(true);
0606: }
0607: children[3] = visitStatement(body);
0608: return node;
0609: } finally {
0610: context = context.parent;
0611: }
0612: }
0613:
0614: SimpleNode translateAbruptCompletion(SimpleNode node, String type,
0615: ASTIdentifier label) {
0616: return node;
0617: }
0618:
0619: public SimpleNode visitReturnStatement(SimpleNode node,
0620: SimpleNode[] children) {
0621: SimpleNode value = children[0];
0622: children[0] = visitExpression(value);
0623: return node;
0624: }
0625:
0626: public SimpleNode visitWithStatement(SimpleNode node,
0627: SimpleNode[] children) {
0628: SimpleNode expr = children[0];
0629: SimpleNode stmt = children[1];
0630: children[0] = visitExpression(expr);
0631: children[1] = visitStatement(stmt);
0632: return node;
0633: }
0634:
0635: public SimpleNode visitTryStatement(SimpleNode node,
0636: SimpleNode[] children) {
0637: SimpleNode block = children[0];
0638: int len = children.length;
0639: assert len == 2 || len == 3;
0640: children[0] = visitStatement(block);
0641: if (len == 2) {
0642: // Could be catch or finally clause
0643: SimpleNode catfin = children[1];
0644: if (catfin instanceof ASTCatchClause) {
0645: // Treat the catch identifier as a binding. This is not quite
0646: // right, need to integrate with variable analyzer, but this is
0647: // the one case in ECMAScript where a variable does have block
0648: // extent.
0649: catfin.set(0, translateReference(catfin.get(0))
0650: .declare());
0651: catfin.set(1, visitStatement(catfin.get(1)));
0652: } else {
0653: assert catfin instanceof ASTFinallyClause;
0654: catfin.set(0, visitStatement(catfin.get(0)));
0655: }
0656: } else if (len == 3) {
0657: SimpleNode cat = children[1];
0658: SimpleNode fin = children[2];
0659: assert cat instanceof ASTCatchClause;
0660: // Treat the catch identifier as a binding. This is not quite
0661: // right, need to integrate with variable analyzer, but this is
0662: // the one case in ECMAScript where a variable does have block
0663: // extent.
0664: cat.set(0, translateReference(cat.get(0)).declare());
0665: cat.set(1, visitStatement(cat.get(1)));
0666: assert fin instanceof ASTFinallyClause;
0667: fin.set(0, visitStatement(fin.get(0)));
0668: }
0669: return node;
0670: }
0671:
0672: public SimpleNode visitThrowStatement(SimpleNode node,
0673: SimpleNode[] children) {
0674: SimpleNode expr = children[0];
0675: children[0] = visitExpression(expr);
0676: return node;
0677: }
0678:
0679: public SimpleNode visitSwitchStatement(SimpleNode node,
0680: SimpleNode[] children) {
0681: SimpleNode expr = children[0];
0682: // TODO: [2003-04-15 ptw] bind context slot macro
0683: try {
0684: context = new TranslationContext(ASTSwitchStatement.class,
0685: context);
0686: children[0] = visitExpression(expr);
0687: for (int i = 1, len = children.length; i < len; i++) {
0688: SimpleNode clause = children[i];
0689: if (clause instanceof ASTDefaultClause) {
0690: if (clause.size() > 0) {
0691: clause.set(0, visitStatement(clause.get(0)));
0692: }
0693: } else {
0694: assert clause instanceof ASTCaseClause : "case clause expected";
0695: clause.set(0, visitExpression(clause.get(0)));
0696: if (clause.size() > 1) {
0697: clause.set(1, visitStatement(clause.get(1)));
0698: }
0699: }
0700: }
0701: return node;
0702: } finally {
0703: context = context.parent;
0704: }
0705: }
0706:
0707: //
0708: // Expressions
0709: //
0710:
0711: boolean isExpressionType(SimpleNode node) {
0712: if (node instanceof Compiler.PassThroughNode) {
0713: node = ((Compiler.PassThroughNode) node).realNode;
0714: }
0715: return super .isExpressionType(node);
0716: }
0717:
0718: public SimpleNode visitExpression(SimpleNode node) {
0719: return visitExpression(node, true);
0720: }
0721:
0722: /* This function, unlike the other expression visitors, can be
0723: applied to any expression node, so it dispatches based on the
0724: node's class. */
0725: public SimpleNode visitExpression(SimpleNode node,
0726: boolean isReferenced) {
0727: assert isExpressionType(node) : "" + node + ": "
0728: + (new ParseTreePrinter()).visit(node)
0729: + " is not an expression";
0730:
0731: if (this .debugVisit) {
0732: System.err.println("visitExpression: " + node.getClass());
0733: }
0734:
0735: SimpleNode newNode = dispatchExpression(node, isReferenced);
0736:
0737: if ((!isReferenced) && (newNode == null)) {
0738: newNode = new ASTEmptyExpression(0);
0739: }
0740: if (this .debugVisit) {
0741: if (!newNode.equals(node)) {
0742: System.err.println("expression: " + node + " -> "
0743: + newNode);
0744: }
0745: }
0746: return newNode;
0747: }
0748:
0749: public SimpleNode visitIdentifier(SimpleNode node,
0750: boolean isReferenced, SimpleNode[] children) {
0751: // Side-effect free expressions can be suppressed if not referenced
0752: // Following is disabled by default for regression testing.
0753: // TODO: [2003-02-17 ows] enable this
0754: if ((!isReferenced)
0755: && options
0756: .getBoolean(Compiler.ELIMINATE_DEAD_EXPRESSIONS)) {
0757: return null;
0758: }
0759: if ("_root".equals(((ASTIdentifier) node).getName())
0760: && (!options.getBoolean(Compiler.ALLOW_ROOT))) {
0761: throw new SemanticError("Illegal variable name: " + node,
0762: node);
0763: }
0764: return translateReference(node).get();
0765: }
0766:
0767: public SimpleNode visitLiteral(SimpleNode node,
0768: boolean isReferenced, SimpleNode[] children) {
0769: // Side-effect free expressions can be suppressed if not referenced
0770: // Following is disabled by default for regression testing.
0771: // TODO: [2003-02-17 ows] enable this
0772: if ((!isReferenced)
0773: && options
0774: .getBoolean(Compiler.ELIMINATE_DEAD_EXPRESSIONS)) {
0775: return null;
0776: }
0777: return translateLiteralNode(node);
0778: }
0779:
0780: public SimpleNode visitExpressionList(SimpleNode node,
0781: boolean isReferenced, SimpleNode[] children) {
0782: // all but last expression will not be referenced, so
0783: // visitExpression will pop it. If the list is not referenced,
0784: // then the last will be popped too
0785: int i = 0, len = children.length - 1;
0786: for (; i < len; i++) {
0787: children[i] = visitExpression(children[i], false);
0788: }
0789: children[len] = visitExpression(children[len], isReferenced);
0790: return node;
0791: }
0792:
0793: public SimpleNode visitEmptyExpression(SimpleNode node,
0794: boolean isReferenced, SimpleNode[] children) {
0795: // Side-effect free expressions can be suppressed if not referenced
0796: if ((!isReferenced)) {
0797: return null;
0798: }
0799: return node;
0800: }
0801:
0802: public SimpleNode visitThisReference(SimpleNode node,
0803: boolean isReferenced, SimpleNode[] children) {
0804: // Side-effect free expressions can be suppressed if not referenced
0805: if ((!isReferenced)) {
0806: return null;
0807: }
0808: return translateReference(node).get();
0809: }
0810:
0811: public SimpleNode visitArrayLiteral(SimpleNode node,
0812: boolean isReferenced, SimpleNode[] children) {
0813: boolean suppressed = (!isReferenced);
0814: for (int i = 0, len = children.length; i < len; i++) {
0815: children[i] = visitExpression(children[i], isReferenced);
0816: }
0817: return node;
0818: }
0819:
0820: public SimpleNode visitObjectLiteral(SimpleNode node,
0821: boolean isReferenced, SimpleNode[] children) {
0822: boolean isKey = true;
0823: for (int i = 0, len = children.length; i < len; i++) {
0824: SimpleNode item = children[i];
0825: if (isKey && item instanceof ASTIdentifier) {
0826: // Identifiers are a shorthand for a literal string, should
0827: // not be evaluated (or remapped).
0828: ;
0829: } else {
0830: children[i] = visitExpression(item);
0831: }
0832: isKey = (!isKey);
0833: }
0834: return node;
0835: }
0836:
0837: public SimpleNode visitFunctionExpression(SimpleNode node,
0838: boolean isReferenced, SimpleNode[] children) {
0839: Compiler.OptionMap savedOptions = options;
0840: try {
0841: options = options.copy();
0842: options.putBoolean(Compiler.CONSTRAINT_FUNCTION, false);
0843: // Make sure all our top-level functions have root context
0844: // if (ASTProgram.class.equals(context.type)) {
0845: // Map map = new HashMap();
0846: // map.put("_1", new Compiler.Splice(children));
0847: // SimpleNode newNode = (new Compiler.Parser()).substitute("with (_root) { _1 }", map);
0848: // visitStatement(newNode);
0849: // } else {
0850: return translateFunction(node, false, children);
0851: // }
0852: } finally {
0853: options = savedOptions;
0854: }
0855: }
0856:
0857: public SimpleNode visitFunctionCallParameters(SimpleNode node,
0858: boolean isReferenced, SimpleNode[] children) {
0859: translateFunctionCallParameters(node, isReferenced, children);
0860: return node;
0861: }
0862:
0863: public SimpleNode[] translateFunctionCallParameters(
0864: SimpleNode node, boolean isReferenced, SimpleNode[] children) {
0865: for (int i = 0, len = children.length; i < len; i++) {
0866: children[i] = visitExpression(children[i]);
0867: }
0868: return children;
0869: }
0870:
0871: public SimpleNode visitPropertyIdentifierReference(SimpleNode node,
0872: boolean isReferenced, SimpleNode[] children) {
0873: return translateReference(node).get();
0874: }
0875:
0876: public SimpleNode visitPropertyValueReference(SimpleNode node,
0877: boolean isReferenced, SimpleNode[] children) {
0878: return translateReference(node).get();
0879: }
0880:
0881: public SimpleNode makeCheckedNode(SimpleNode node) {
0882: if (options.getBoolean(Compiler.DEBUG)
0883: && options
0884: .getBoolean(Compiler.WARN_UNDEFINED_REFERENCES)) {
0885: String file = "null";
0886: String line = "null";
0887: if (node.filename != null) {
0888: file = ScriptCompiler.quote(node.filename);
0889: line = "" + node.beginLine;
0890: }
0891: Map map = new HashMap();
0892: map.put("_1", node);
0893: return new Compiler.PassThroughNode((new Compiler.Parser())
0894: .substitute("(Debug.evalCarefully(" + file + ", "
0895: + line
0896: + ", function () { return _1; }, this))",
0897: map));
0898: }
0899: return node;
0900: }
0901:
0902: SimpleNode noteCallSite(SimpleNode node) {
0903: // Note current call-site in a function context and backtracing
0904: if ((options.getBoolean(Compiler.DEBUG_BACKTRACE) && (node.beginLine != 0))
0905: && (context.findFunctionContext() != null)) {
0906: SimpleNode newNode = new ASTExpressionList(0);
0907: newNode
0908: .set(0, (new Compiler.Parser()).parse(
0909: "$lzsc$a.lineno = " + node.beginLine)
0910: .get(0).get(0));
0911: newNode.set(1, new Compiler.PassThroughNode(node));
0912: return visitExpression(newNode);
0913: }
0914: return node;
0915: }
0916:
0917: // Could do inline expansions here, like setAttribute
0918: public SimpleNode visitCallExpression(SimpleNode node,
0919: boolean isReferenced, SimpleNode[] children) {
0920: SimpleNode fnexpr = children[0];
0921: SimpleNode[] args = children[1].getChildren();
0922: int arglen = args.length;
0923:
0924: // TODO: [2002-12-03 ptw] There should be a more general
0925: // mechanism for matching patterns against AST's and replacing
0926: // them.
0927: // FIXME: [2002-12-03 ptw] This substitution is not correct
0928: // because it does not verify that the method being inlined is
0929: // actually LzNode.setAttribute.
0930: if (
0931: // Here this means 'compiling the lfc'
0932: options.getBoolean(Compiler.FLASH_COMPILER_COMPATABILITY)
0933: && (!options.getBoolean("passThrough"))
0934: && (fnexpr instanceof ASTPropertyIdentifierReference)) {
0935: SimpleNode[] fnchildren = fnexpr.getChildren();
0936: String name = ((ASTIdentifier) fnchildren[1]).getName();
0937: // We can't expand this if an expression value is expected,
0938: // since we don't have 'let'
0939: if (name.equals("setAttribute") && (!isReferenced)) {
0940: SimpleNode scope = fnchildren[0];
0941: SimpleNode property = args[0];
0942: SimpleNode value = args[1];
0943: List newBody = new ArrayList();
0944: String this var = "$lzsc$" + UUID().toString();
0945: String propvar = "$lzsc$" + UUID().toString();
0946: String valvar = "$lzsc$" + UUID().toString();
0947: String changedvar = "$lzsc$" + UUID().toString();
0948: String svar = "$lzsc$" + UUID().toString();
0949: String evtvar = "$lzsc$" + UUID().toString();
0950: String decls = "";
0951: ParseTreePrinter ptp = new ParseTreePrinter();
0952: if (scope instanceof ASTIdentifier
0953: || scope instanceof ASTThisReference) {
0954: this var = ptp.visit(scope);
0955: } else {
0956: decls += "var " + this var + " = "
0957: + ptp.visit(scope) + ";";
0958: }
0959: if (property instanceof ASTLiteral
0960: || property instanceof ASTIdentifier) {
0961: propvar = ptp.visit(property);
0962: if (property instanceof ASTLiteral) {
0963: assert propvar.startsWith("\"")
0964: || propvar.startsWith("'");
0965: evtvar = propvar.substring(0, 1) + "on"
0966: + propvar.substring(1);
0967: }
0968: } else {
0969: decls += "var " + propvar + " = "
0970: + ptp.visit(property) + ";";
0971: }
0972: if (value instanceof ASTLiteral
0973: || value instanceof ASTIdentifier) {
0974: valvar = ptp.visit(value);
0975: } else {
0976: decls += "var " + valvar + " = " + ptp.visit(value)
0977: + ";";
0978: }
0979: if (arglen > 2) {
0980: SimpleNode ifchanged = args[2];
0981: if (ifchanged instanceof ASTLiteral
0982: || ifchanged instanceof ASTIdentifier) {
0983: changedvar = ptp.visit(ifchanged);
0984: } else {
0985: decls += "var " + changedvar + " = "
0986: + ptp.visit(ifchanged) + ";";
0987: }
0988: }
0989: newBody.add(parseFragment(decls));
0990: String fragment = "if (! ("
0991: + this var
0992: + ".__LZdeleted "
0993: + ((arglen > 2) ? ("|| (" + changedvar
0994: + " && (" + this var + "[" + propvar
0995: + "] == " + valvar + "))") : "")
0996: + ")) {"
0997: + "var "
0998: + svar
0999: + " = "
1000: + this var
1001: + ".setters;"
1002: + "if ("
1003: + svar
1004: + " && ("
1005: + propvar
1006: + " in "
1007: + svar
1008: + ")) {"
1009: + " "
1010: + this var
1011: + "["
1012: + svar
1013: + "["
1014: + propvar
1015: + "]]("
1016: + valvar
1017: + ");"
1018: + "} else {"
1019: + " if ($debug) {"
1020: + " if ("
1021: + svar
1022: + " == null) {"
1023: + " Debug.warn('null setters on', "
1024: + this var
1025: + ", "
1026: + propvar
1027: + ", "
1028: + valvar
1029: + ");"
1030: + " }"
1031: + " }"
1032: + " "
1033: + this var
1034: + "[ "
1035: + propvar
1036: + " ] = "
1037: + valvar
1038: + ";"
1039: + ((property instanceof ASTLiteral) ? ""
1040: : (" var " + evtvar
1041: + " = (\"on\" + " + propvar + ");"))
1042: + " if (" + evtvar + " in " + this var
1043: + ") {" + " if (" + this var + "["
1044: + evtvar + "].ready) {" + this var + "[ "
1045: + evtvar + " ].sendEvent( " + valvar + " ); }"
1046: + " }" + "}}";
1047: newBody.add(parseFragment(fragment));
1048: SimpleNode newStmts = new ASTStatementList(0);
1049: newStmts.setChildren((SimpleNode[]) newBody
1050: .toArray(new SimpleNode[0]));
1051: return visitStatement(newStmts);
1052: }
1053: }
1054:
1055: children[1].setChildren(translateFunctionCallParameters(node,
1056: isReferenced, args));
1057: children[0] = translateReferenceForCall(fnexpr, true, node);
1058: // if (options.getBoolean(Compiler.WARN_UNDEFINED_REFERENCES)) {
1059: // return makeCheckedNode(node);
1060: // }
1061: return noteCallSite(node);
1062: }
1063:
1064: public SimpleNode visitSuperCallExpression(SimpleNode node,
1065: boolean isReferenced, SimpleNode[] children) {
1066: SimpleNode n = translateSuperCallExpression(node, isReferenced,
1067: children);
1068: return visitExpression(n, isReferenced);
1069: }
1070:
1071: public SimpleNode visitNewExpression(SimpleNode node,
1072: boolean isReferenced, SimpleNode[] children) {
1073: for (int i = 0, len = children.length; i < len; i++) {
1074: SimpleNode child = children[i];
1075: children[i] = visitExpression(child, isReferenced);
1076: }
1077: return noteCallSite(node);
1078: }
1079:
1080: public SimpleNode visitPrefixExpression(SimpleNode node,
1081: boolean isReferenced, SimpleNode[] children) {
1082: int op = ((ASTOperator) children[0]).getOperator();
1083: SimpleNode ref = children[1];
1084: if (translateReference(ref).isChecked()) {
1085: // The undefined reference checker needs to have this expanded
1086: // to work
1087: Map map = new HashMap();
1088: map.put("_1", ref);
1089: String pattern = "(function () { var $lzsc$tmp = _1; return _1 = $lzsc$tmp "
1090: + XfixInstrs.get(op) + " 1; })()";
1091: SimpleNode n = (new Compiler.Parser()).substitute(pattern,
1092: map);
1093: return visitExpression(n);
1094: }
1095: children[1] = translateReference(ref, 2).get();
1096: return node;
1097: }
1098:
1099: public SimpleNode visitPostfixExpression(SimpleNode node,
1100: boolean isReferenced, SimpleNode[] children) {
1101: SimpleNode ref = children[0];
1102: int op = ((ASTOperator) children[1]).getOperator();
1103: if (translateReference(ref).isChecked()) {
1104: // The undefined reference checker needs to have this expanded
1105: // to work
1106: Map map = new HashMap();
1107: map.put("_1", ref);
1108: String pattern = "(function () { var $lzsc$tmp = _1; _1 = $lzsc$tmp "
1109: + XfixInstrs.get(op) + " 1; return $lzsc$tmp; })()";
1110: SimpleNode n = (new Compiler.Parser()).substitute(pattern,
1111: map);
1112: return visitExpression(n);
1113: }
1114: children[0] = translateReference(ref, 2).get();
1115: return node;
1116: }
1117:
1118: public SimpleNode visitUnaryExpression(SimpleNode node,
1119: boolean isReferenced, SimpleNode[] children) {
1120: int op = ((ASTOperator) children[0]).getOperator();
1121: // I guess the parser doesn't know the difference
1122: if (ParserConstants.INCR == (op)
1123: || ParserConstants.DECR == (op)) {
1124: return visitPrefixExpression(node, isReferenced, children);
1125: }
1126: SimpleNode arg = children[1];
1127: // special-case typeof(variable) to not emit undefined-variable
1128: // checks so there is a warning-free way to check for undefined
1129: if (ParserConstants.TYPEOF == (op)
1130: && (arg instanceof ASTIdentifier
1131: || arg instanceof ASTPropertyValueReference || arg instanceof ASTPropertyIdentifierReference)) {
1132: children[1] = translateReference(arg).get(false);
1133: } else {
1134: children[1] = visitExpression(arg);
1135: }
1136: return node;
1137: }
1138:
1139: public SimpleNode visitBinaryExpressionSequence(SimpleNode node,
1140: boolean isReferenced, SimpleNode[] children) {
1141: SimpleNode a = children[0];
1142: SimpleNode op = children[1];
1143: SimpleNode b = children[2];
1144: if (ParserConstants.CAST == ((ASTOperator) op).getOperator()) {
1145: // Approximate a cast b as a
1146: // TODO: [2008-01-08 ptw] We could typecheck and throw an error
1147: // in debug mode
1148: return visitExpression(a);
1149: }
1150: if (ParserConstants.IS == ((ASTOperator) op).getOperator()) {
1151: // Approximate a is b as b['$lzsc$isa'] ? b.$lzsc$isa(a) : (a
1152: // instanceof b)
1153: Map map = new HashMap();
1154: map.put("_1", a);
1155: map.put("_2", b);
1156: String pattern;
1157: if ((a instanceof ASTIdentifier
1158: || a instanceof ASTPropertyValueReference || a instanceof ASTPropertyIdentifierReference)
1159: && (b instanceof ASTIdentifier
1160: || b instanceof ASTPropertyValueReference || b instanceof ASTPropertyIdentifierReference)) {
1161: pattern = "(_2['$lzsc$isa'] ? _2.$lzsc$isa(_1) : (_1 instanceof _2))";
1162: } else {
1163: pattern = "((function (a, b) {return b['$lzsc$isa'] ? b.$lzsc$isa(a) : (a instanceof b)})(_1, _2))";
1164: }
1165: SimpleNode n = (new Compiler.Parser()).substitute(pattern,
1166: map);
1167: return visitExpression(n);
1168: }
1169: children[0] = visitExpression(a);
1170: children[2] = visitExpression(b);
1171: return node;
1172: }
1173:
1174: SimpleNode translateAndOrExpression(SimpleNode node, boolean isand,
1175: SimpleNode a, SimpleNode b) {
1176: SimpleNode[] children = node.getChildren();
1177: children[0] = visitExpression(a);
1178: children[1] = visitExpression(b);
1179: return node;
1180: }
1181:
1182: public SimpleNode visitConditionalExpression(SimpleNode node,
1183: boolean isReferenced, SimpleNode[] children) {
1184: SimpleNode test = children[0];
1185: SimpleNode a = children[1];
1186: SimpleNode b = children[2];
1187: children[0] = visitExpression(test);
1188: children[1] = visitExpression(a);
1189: children[2] = visitExpression(b);
1190: return node;
1191: }
1192:
1193: public SimpleNode visitAssignmentExpression(SimpleNode node,
1194: boolean isReferenced, SimpleNode[] children) {
1195: JavascriptReference lhs = translateReference(children[0]);
1196: int op = ((ASTOperator) children[1]).getOperator();
1197: SimpleNode rhs = visitExpression(children[2]);
1198: if (op != ParserConstants.ASSIGN && lhs.isChecked()) {
1199: // The undefined reference checker needs to have this expanded
1200: // to work
1201: Map map = new HashMap();
1202: map.put("_1", lhs.get());
1203: map.put("_2", rhs);
1204: map.put("_3", lhs.set());
1205: String pattern = "(function () { var $lzsc$tmp = _1; return _3 = $lzsc$tmp "
1206: + AssignOpTable.get(op) + " _2; })()";
1207: SimpleNode n = (new Compiler.Parser()).substitute(pattern,
1208: map);
1209: return visitExpression(n);
1210: }
1211: children[2] = rhs;
1212: children[0] = lhs.set();
1213: return node;
1214: }
1215:
1216: // useName => declaration not expression
1217: SimpleNode translateFunction(SimpleNode node, boolean useName,
1218: SimpleNode[] children) {
1219: // TODO: [2003-04-15 ptw] bind context slot macro
1220: SimpleNode[] result;
1221: // methodName and scriptElement
1222: Compiler.OptionMap savedOptions = options;
1223: try {
1224: options = options.copy();
1225: context = new TranslationContext(
1226: ASTFunctionExpression.class, context);
1227: result = translateFunctionInternal(node, useName, children);
1228: } finally {
1229: options = savedOptions;
1230: context = context.parent;
1231: }
1232: node = result[0];
1233: // Dependency function is not compiled in the function context
1234: if (result[1] != null) {
1235: SimpleNode dependencies = result[1];
1236: Map map = new HashMap();
1237: map.put("_1", node);
1238: map.put("_2", translateFunction(dependencies, false,
1239: dependencies.getChildren()));
1240: SimpleNode newNode = (new Compiler.Parser())
1241: .substitute(
1242: "(function () {var $lzsc$f = _1; $lzsc$f.dependencies = _2; return $lzsc$f })();",
1243: map);
1244: return newNode;
1245: }
1246: return node;
1247: }
1248:
1249: static java.util.regex.Pattern identifierPattern = java.util.regex.Pattern
1250: .compile("[\\w$_]+");
1251:
1252: // Internal helper function for above
1253: // useName => declaration not expression
1254: SimpleNode[] translateFunctionInternal(SimpleNode node,
1255: boolean useName, SimpleNode[] children) {
1256: // ast can be any of:
1257: // FunctionDefinition(name, args, body)
1258: // FunctionDeclaration(name, args, body)
1259: // FunctionDeclaration(args, body)
1260: // Handle the two arities:
1261: String functionName = null;
1262: SimpleNode params;
1263: SimpleNode stmts;
1264: SimpleNode depExpr = null;
1265: int stmtsIndex;
1266: ASTIdentifier functionNameIdentifier = null;
1267: if (children.length == 3) {
1268: functionNameIdentifier = (ASTIdentifier) children[0];
1269: params = children[1];
1270: stmts = children[stmtsIndex = 2];
1271: functionName = functionNameIdentifier.getName();
1272: } else {
1273: params = children[0];
1274: stmts = children[stmtsIndex = 1];
1275: }
1276: // inner functions do not get scriptElement treatment, shadow any
1277: // outer declaration
1278: options.putBoolean(Compiler.SCRIPT_ELEMENT, false);
1279: // or the magic with(this) treatment
1280: options.putBoolean(Compiler.WITH_THIS, false);
1281: // function block
1282: String userFunctionName = null;
1283: String filename = node.filename != null ? node.filename
1284: : "unknown file";
1285: String lineno = "" + node.beginLine;
1286: if (functionName != null) {
1287: userFunctionName = functionName;
1288: if (!useName) {
1289: if ((!identifierPattern.matcher(functionName).matches())
1290: // Some JS engines die if you name function expressions
1291: || options.getBoolean(Compiler.DEBUG_SIMPLE)) {
1292: // This is a function-expression that has been annotated
1293: // with a non-legal function name, so remove that and put it
1294: // in _dbg_name (below)
1295: functionName = null;
1296: children[0] = new ASTEmptyExpression(0);
1297: }
1298: }
1299: } else {
1300: userFunctionName = "" + filename + "#" + lineno + "/"
1301: + node.beginColumn;
1302: }
1303: // Tell metering to look up the name at runtime if it is not a
1304: // global name (this allows us to name closures more
1305: // mnemonically at runtime
1306: String meterFunctionName = useName ? functionName : null;
1307: Set pnames = new LinkedHashSet();
1308: SimpleNode[] paramIds = params.getChildren();
1309: for (int i = 0, len = paramIds.length; i < len; i++) {
1310: pnames.add(((ASTIdentifier) paramIds[i]).getName());
1311: }
1312: // Pull all the pragmas from the beginning of the
1313: // statement list: process them, and remove them
1314: assert stmts instanceof ASTStatementList;
1315: List stmtList = new ArrayList(Arrays
1316: .asList(stmts.getChildren()));
1317: for (int i = 0, len = stmtList.size(); i < len; i++) {
1318: SimpleNode stmt = (SimpleNode) stmtList.get(i);
1319: if (stmt instanceof ASTPragmaDirective) {
1320: SimpleNode newNode = visitStatement(stmt);
1321: if (!newNode.equals(stmt)) {
1322: stmtList.set(i, newNode);
1323: }
1324: } else {
1325: break;
1326: }
1327: }
1328: String methodName = (String) options.get(Compiler.METHOD_NAME);
1329: // Backwards compatibility with tag compiler
1330: if (methodName != null && functionNameIdentifier != null) {
1331: functionNameIdentifier.setName(functionName = methodName);
1332: }
1333: if (options.getBoolean(Compiler.CONSTRAINT_FUNCTION)) {
1334: // assert (functionName != null);
1335: if (ReferenceCollector.DebugConstraints) {
1336: System.err.println("stmts: " + stmts);
1337: }
1338: // Find dependencies.
1339: //
1340: // Compute this before any transformations on the function body.
1341: //
1342: // The job of a constraint function is to compute a value.
1343: // The current implementation inlines the call to set the
1344: // attribute that the constraint is attached to, within the
1345: // constraint function it Walking the statements of
1346: // the function will process the expression that computes
1347: // the value; it will also process the call to
1348: // setAttribute, but ReferenceCollector knows to ignore
1349: //
1350: ReferenceCollector dependencies = new ReferenceCollector(
1351: options.getBoolean(Compiler.COMPUTE_METAREFERENCES));
1352: // Only visit original body
1353: for (Iterator i = stmtList.iterator(); i.hasNext();) {
1354: SimpleNode stmt = (SimpleNode) i.next();
1355: dependencies.visit(stmt);
1356: }
1357: depExpr = dependencies.computeReferences(userFunctionName);
1358: if (options.getBoolean(Compiler.PRINT_CONSTRAINTS)) {
1359: (new ParseTreePrinter()).print(depExpr);
1360: }
1361: }
1362: List prefix = new ArrayList();
1363: List error = new ArrayList();
1364: List suffix = new ArrayList();
1365: if (options.getBoolean(Compiler.DEBUG_BACKTRACE)) {
1366: prefix
1367: .add(parseFragment("var $lzsc$d = Debug, $lzsc$s = $lzsc$d.backtraceStack;"
1368: + "if ($lzsc$s) {"
1369: + " var $lzsc$a = Array.prototype.slice.call(arguments, 0);"
1370: + " $lzsc$a.callee = arguments.callee;"
1371: + " $lzsc$a['this'] = this;"
1372: + " $lzsc$s.push($lzsc$a);"
1373: + " if ($lzsc$s.length > $lzsc$s.maxDepth) {$lzsc$d.stackOverflow()};"
1374: + "}"));
1375: error
1376: .add(parseFragment("if ($lzsc$s && (! $lzsc$d.uncaughtBacktraceStack)) {"
1377: + " $lzsc$d.uncaughtBacktraceStack = $lzsc$s.slice(0);"
1378: + "}" + "throw($lzsc$e);"));
1379: suffix.add(parseFragment("if ($lzsc$s) {"
1380: + " $lzsc$s.pop();" + "}"));
1381: }
1382: if (options.getBoolean(Compiler.PROFILE)) {
1383: prefix.add((meterFunctionEvent(node, "calls",
1384: meterFunctionName)));
1385: suffix.add((meterFunctionEvent(node, "returns",
1386: meterFunctionName)));
1387: }
1388:
1389: // Analyze local variables (and functions)
1390: VariableAnalyzer analyzer = new VariableAnalyzer(
1391: params,
1392: options
1393: .getBoolean(Compiler.FLASH_COMPILER_COMPATABILITY));
1394: for (Iterator i = prefix.iterator(); i.hasNext();) {
1395: analyzer.visit((SimpleNode) i.next());
1396: }
1397: for (Iterator i = stmtList.iterator(); i.hasNext();) {
1398: analyzer.visit((SimpleNode) i.next());
1399: }
1400: for (Iterator i = suffix.iterator(); i.hasNext();) {
1401: analyzer.visit((SimpleNode) i.next());
1402: }
1403: analyzer.computeReferences();
1404: // Parameter _must_ be in order
1405: LinkedHashSet parameters = analyzer.parameters;
1406: // Linked for determinism for regression testing
1407: Set variables = analyzer.variables;
1408: LinkedHashMap fundefs = analyzer.fundefs;
1409: Set closed = analyzer.closed;
1410: Set free = analyzer.free;
1411: // Note usage due to activation object and withThis
1412: if (!free.isEmpty()) {
1413: // TODO: [2005-06-29 ptw] with (_root) should not be
1414: // necessary for the activation object case now that it is
1415: // done at top level to get [[scope]] right.
1416: if (options.getBoolean(Compiler.ACTIVATION_OBJECT)) {
1417: analyzer.incrementUsed("_root");
1418: }
1419: if (options.getBoolean(Compiler.WITH_THIS)) {
1420: analyzer.incrementUsed("this");
1421: }
1422: }
1423: Map used = analyzer.used;
1424: // If this is a closure, annotate the Username for metering
1425: if ((!closed.isEmpty()) && (userFunctionName != null)
1426: && options.getBoolean(Compiler.PROFILE)) {
1427: // Is there any other way to construct a closure in js
1428: // other than a function returning a function?
1429: if (context.findFunctionContext().parent
1430: .findFunctionContext() != null) {
1431: userFunctionName = "" + closed + "." + userFunctionName;
1432: }
1433: }
1434: if (false) {
1435: System.err.println(userFunctionName + ":: parameters: "
1436: + parameters + ", variables: " + variables
1437: + ", fundefs: " + fundefs + ", used: " + used
1438: + ", closed: " + closed + ", free: " + free);
1439: }
1440: // Deal with warnings
1441: if (options.getBoolean(Compiler.WARN_UNUSED_PARAMETERS)) {
1442: Set unusedParams = new LinkedHashSet(parameters);
1443: unusedParams.removeAll(used.keySet());
1444: for (Iterator i = unusedParams.iterator(); i.hasNext();) {
1445: System.err.println("Warning: parameter " + i.next()
1446: + " of " + userFunctionName + " unused in "
1447: + filename + "(" + lineno + ")");
1448: }
1449: }
1450: if (options.getBoolean(Compiler.WARN_UNUSED_LOCALS)) {
1451: Set unusedVariables = new LinkedHashSet(variables);
1452: unusedVariables.removeAll(used.keySet());
1453: for (Iterator i = unusedVariables.iterator(); i.hasNext();) {
1454: System.err.println("Warning: variable " + i.next()
1455: + " of " + userFunctionName + " unused in "
1456: + filename + "(" + lineno + ")");
1457: }
1458: }
1459: // auto-declared locals
1460: Set auto = new LinkedHashSet();
1461: auto.add("this");
1462: auto.add("arguments");
1463: auto.retainAll(used.keySet());
1464: // parameters, locals, and auto-registers
1465: Set known = new LinkedHashSet(parameters);
1466: known.addAll(variables);
1467: known.addAll(auto);
1468: // for now, ensure that super has a value
1469: known.remove("super");
1470:
1471: // Look for #pragma
1472: boolean scriptElement = options
1473: .getBoolean(Compiler.SCRIPT_ELEMENT);
1474: Map registerMap = new HashMap();
1475: if (!scriptElement) {
1476: // All parameters and locals are remapped to 'registers' of the
1477: // form `$n`. This prevents them from colliding with member
1478: // slots due to implicit `with (this)` added below, and also makes
1479: // the emitted code more compact.
1480: int regno = 1;
1481: boolean debug = options.getBoolean(Compiler.NAME_FUNCTIONS);
1482: for (Iterator i = (new LinkedHashSet(known)).iterator(); i
1483: .hasNext();) {
1484: String k = (String) i.next();
1485: String r;
1486: if (auto.contains(k) || closed.contains(k)) {
1487: ;
1488: } else {
1489: if (debug) {
1490: r = "$" + regno++ + "_" + k;
1491: } else {
1492: r = "$" + regno++;
1493: }
1494: registerMap.put(k, r);
1495: // remove from known map
1496: known.remove(k);
1497: }
1498: }
1499: }
1500: // Always set register map. Inner functions should not see
1501: // parent registers (which they would if the setting of the
1502: // registermap were conditional on function vs. function2)
1503: context.setProperty(TranslationContext.REGISTERS, registerMap);
1504: // Set the knownSet. This includes the parent's known set, so
1505: // closed over variables are not treated as free.
1506: Set knownSet = new LinkedHashSet(known);
1507: // Add parent known
1508: Set parentKnown = (Set) context.parent
1509: .get(TranslationContext.VARIABLES);
1510: if (parentKnown != null) {
1511: knownSet.addAll(parentKnown);
1512: }
1513: context.setProperty(TranslationContext.VARIABLES, knownSet);
1514:
1515: // Replace params
1516: for (int i = 0, len = paramIds.length; i < len; i++) {
1517: ASTIdentifier oldParam = (ASTIdentifier) paramIds[i];
1518: SimpleNode newParam = translateReference(oldParam)
1519: .declare();
1520: params.set(i, newParam);
1521: }
1522:
1523: List newBody = new ArrayList();
1524:
1525: int activationObjectSize = 0;
1526: if (scriptElement) {
1527: // Create all variables (including inner functions) in global scope
1528: if (!variables.isEmpty()) {
1529: String code = "";
1530: for (Iterator i = variables.iterator(); i.hasNext();) {
1531: code += "_root." + (String) i.next() + "= void 0;";
1532: }
1533: newBody.add(parseFragment(code));
1534: }
1535: } else {
1536: // Leave var declarations as is
1537: // Emit function declarations here
1538: if (!fundefs.isEmpty()) {
1539: String code = "";
1540: for (Iterator i = fundefs.keySet().iterator(); i
1541: .hasNext();) {
1542: code += "var " + (String) i.next() + ";";
1543: }
1544: newBody.add(parseFragment(code));
1545: }
1546: }
1547:
1548: // Cf. LPP-4850: Prefix has to come after declarations (above).
1549: // FIXME: (LPP-2075) [2006-05-19 ptw] Wrap body in try and make
1550: // suffix be a finally clause, so suffix will not be skipped by
1551: // inner returns.
1552: newBody.addAll(prefix);
1553:
1554: // Now emit functions in the activation context
1555: // Note: variable has already been declared so assignment does the
1556: // right thing (either assigns to global or local
1557: for (Iterator i = fundefs.keySet().iterator(); i.hasNext();) {
1558: String name = (String) i.next();
1559: if (scriptElement || used.containsKey(name)) {
1560: SimpleNode fundecl = (SimpleNode) fundefs.get(name);
1561: SimpleNode funexpr = new ASTFunctionExpression(0);
1562: funexpr.setBeginLocation(fundecl.filename,
1563: fundecl.beginLine, fundecl.beginColumn);
1564: funexpr.setChildren(fundecl.getChildren());
1565: Map map = new HashMap();
1566: map.put("_1", funexpr);
1567: // Do I need a new one of these each time?
1568: newBody.add((new Compiler.Parser()).substitute(name
1569: + " = _1;", map));
1570: }
1571: }
1572: if ((!free.isEmpty()) && options.getBoolean(Compiler.WITH_THIS)) {
1573: SimpleNode newStmts = new ASTStatementList(0);
1574: newStmts.setChildren((SimpleNode[]) stmtList
1575: .toArray(new SimpleNode[0]));
1576: SimpleNode withNode = new ASTWithStatement(0);
1577: SimpleNode id = new ASTThisReference(0);
1578: withNode.set(0, id);
1579: withNode.set(1, newStmts);
1580: newBody.add(withNode);
1581: } else {
1582: newBody.addAll(stmtList);
1583: }
1584: // FIXME: (LPP-2075) [2006-05-19 ptw] Wrap body in try and make
1585: // suffix be a finally clause, so suffix will not be skipped by
1586: // inner returns.
1587: if (!suffix.isEmpty() || !error.isEmpty()) {
1588: int i = 0;
1589: SimpleNode newStmts = new ASTStatementList(0);
1590: newStmts.setChildren((SimpleNode[]) newBody
1591: .toArray(new SimpleNode[0]));
1592: SimpleNode tryNode = new ASTTryStatement(0);
1593: tryNode.set(i++, newStmts);
1594: if (!error.isEmpty()) {
1595: SimpleNode catchNode = new ASTCatchClause(0);
1596: SimpleNode catchStmts = new ASTStatementList(0);
1597: catchStmts.setChildren((SimpleNode[]) error
1598: .toArray(new SimpleNode[0]));
1599: catchNode.set(0, new ASTIdentifier("$lzsc$e"));
1600: catchNode.set(1, catchStmts);
1601: tryNode.set(i++, catchNode);
1602: }
1603: if (!suffix.isEmpty()) {
1604: SimpleNode finallyNode = new ASTFinallyClause(0);
1605: SimpleNode suffixStmts = new ASTStatementList(0);
1606: suffixStmts.setChildren((SimpleNode[]) suffix
1607: .toArray(new SimpleNode[0]));
1608: finallyNode.set(0, suffixStmts);
1609: tryNode.set(i, finallyNode);
1610: }
1611: newBody = new ArrayList();
1612: newBody.add(tryNode);
1613: }
1614: // Process amended body
1615: SimpleNode newStmts = new ASTStatementList(0);
1616: newStmts.setChildren((SimpleNode[]) newBody
1617: .toArray(new SimpleNode[0]));
1618: newStmts = visitStatement(newStmts);
1619: // Finally replace the function body with that whole enchilada
1620: children[stmtsIndex] = newStmts;
1621: if (options.getBoolean(Compiler.NAME_FUNCTIONS)) {
1622: // TODO: [2007-09-04 ptw] Come up with a better way to
1623: // distinguish LFC from user stack frames. See
1624: // lfc/debugger/LzBactrace
1625: String fn = (options
1626: .getBoolean(Compiler.FLASH_COMPILER_COMPATABILITY) ? "lfc/"
1627: : "")
1628: + filename;
1629: if (functionName != null &&
1630: // Either it is a declaration or we are not doing
1631: // backtraces, so the name will be available from the
1632: // runtime
1633: (useName || (!(options
1634: .getBoolean(Compiler.DEBUG_BACKTRACE))))) {
1635: if (options.getBoolean(Compiler.DEBUG_BACKTRACE)) {
1636: SimpleNode newNode = new ASTStatementList(0);
1637: newNode.set(0, new Compiler.PassThroughNode(node));
1638: newNode.set(1, parseFragment(functionName
1639: + "._dbg_filename = "
1640: + ScriptCompiler.quote(fn)));
1641: newNode.set(2, parseFragment(functionName
1642: + "._dbg_lineno = " + lineno));
1643: node = visitStatement(newNode);
1644: }
1645: } else {
1646: Map map = new HashMap();
1647: map.put("_1", node);
1648: SimpleNode newNode = new Compiler.PassThroughNode(
1649: (new Compiler.Parser())
1650: .substitute(
1651: "(function () {"
1652: + " var $lzsc$temp = _1;"
1653: + " $lzsc$temp._dbg_name = "
1654: + ScriptCompiler
1655: .quote(userFunctionName)
1656: + ";"
1657: + ((options
1658: .getBoolean(Compiler.DEBUG_BACKTRACE)) ? (" $lzsc$temp._dbg_filename = "
1659: + ScriptCompiler
1660: .quote(fn)
1661: + ";"
1662: + " $lzsc$temp._dbg_lineno = "
1663: + lineno + ";")
1664: : "")
1665: + " return $lzsc$temp})()",
1666: map));
1667: node = newNode;
1668: }
1669: }
1670: if (options.getBoolean(Compiler.CONSTRAINT_FUNCTION)) {
1671: return new SimpleNode[] { node, depExpr };
1672: }
1673: return new SimpleNode[] { node, null };
1674: }
1675:
1676: SimpleNode translateLiteralNode(SimpleNode node) {
1677: return node;
1678: }
1679:
1680: SimpleNode translateReferenceForCall(SimpleNode ast) {
1681: return translateReferenceForCall(ast, false, null);
1682: }
1683:
1684: /* Contract is to leave a reference on the stack that will be
1685: dereferenced by CallFunction, etc. Returns true if it
1686: succeeds. Returns false if the ast is such that only the
1687: value of the reference can be pushed. In this case, the
1688: callee, must use "CallMethod UNDEF" to call the value
1689: instead */
1690: SimpleNode translateReferenceForCall(SimpleNode ast,
1691: boolean checkDefined, SimpleNode node) {
1692: SimpleNode[] children = ast.getChildren();
1693: if (checkDefined) {
1694: assert node != null : "Must supply node for checkDefined";
1695: }
1696: if (ast instanceof ASTPropertyIdentifierReference) {
1697: JavascriptReference ref = translateReference(children[0]);
1698: String name = ((ASTIdentifier) children[1]).getName();
1699: // if (checkDefined) {
1700: // // TODO: needs to transform node
1701: // checkUndefinedMethod(node, ref, name);
1702: // }
1703: children[0] = ref.get();
1704: }
1705: if (ast instanceof ASTPropertyValueReference) {
1706: // TODO: [2002-10-26 ptw] (undefined reference coverage) Check
1707: JavascriptReference ref = translateReference(children[0]);
1708: children[1] = visitExpression(children[1]);
1709: children[0] = ref.get();
1710: }
1711: // The only other reason you visit a reference is to make a funcall
1712: boolean isref = true;
1713: if (ast instanceof ASTIdentifier) {
1714: JavascriptReference ref = translateReference(ast);
1715: ast = ref.preset();
1716: } else {
1717: ast = visitExpression(ast);
1718: }
1719: // TODO: wrap into node
1720: // if (checkDefined) {
1721: // checkUndefinedFunction(
1722: // node,
1723: // isref && ast instanceof ASTIdentifier ? ((ASTIdentifier)ast).getName() : null);
1724: // }
1725: return ast;
1726: }
1727:
1728: JavascriptReference translateReference(SimpleNode node) {
1729: return translateReference(node, 1);
1730: }
1731:
1732: static public class JavascriptReference {
1733: protected Compiler.OptionMap options;
1734: SimpleNode node;
1735: SimpleNode checkedNode = null;
1736:
1737: public JavascriptReference(Translator translator,
1738: SimpleNode node, int referenceCount) {
1739: this .options = translator.getOptions();
1740: this .node = node;
1741: }
1742:
1743: public boolean isChecked() {
1744: return checkedNode != null;
1745: }
1746:
1747: public SimpleNode get(boolean checkUndefined) {
1748: if (checkUndefined && checkedNode != null) {
1749: return checkedNode;
1750: }
1751: return this .node;
1752: }
1753:
1754: public SimpleNode get() {
1755: return get(true);
1756: }
1757:
1758: public SimpleNode preset() {
1759: return this .node;
1760: }
1761:
1762: public SimpleNode set(Boolean warnGlobal) {
1763: return this .node;
1764: }
1765:
1766: public SimpleNode set() {
1767: return set(null);
1768: }
1769:
1770: public SimpleNode set(boolean warnGlobal) {
1771: return set(Boolean.valueOf(warnGlobal));
1772: }
1773:
1774: public SimpleNode declare() {
1775: return this .node;
1776: }
1777:
1778: public SimpleNode init() {
1779: return this .node;
1780: }
1781: }
1782:
1783: static public abstract class MemberReference extends
1784: JavascriptReference {
1785: protected SimpleNode object;
1786:
1787: public MemberReference(Translator translator, SimpleNode node,
1788: int referenceCount, SimpleNode object) {
1789: super (translator, node, referenceCount);
1790: this .object = object;
1791: }
1792: }
1793:
1794: static public class VariableReference extends JavascriptReference {
1795: TranslationContext context;
1796: public final String name;
1797:
1798: public VariableReference(Translator translator,
1799: SimpleNode node, int referenceCount, String name) {
1800: super (translator, node, referenceCount);
1801: this .name = name;
1802: this .context = (TranslationContext) translator.getContext();
1803: Map registers = (Map) context
1804: .get(TranslationContext.REGISTERS);
1805: // Replace identifiers with their 'register' (i.e. rename them)
1806: if (registers != null && registers.containsKey(name)) {
1807: String register = (String) registers.get(name);
1808: ASTIdentifier newNode = new ASTIdentifier(0);
1809: newNode.setName(register);
1810: this .node = new Compiler.PassThroughNode(newNode);
1811: return;
1812: }
1813: if (options.getBoolean(Compiler.WARN_UNDEFINED_REFERENCES)) {
1814: Set variables = (Set) context
1815: .get(TranslationContext.VARIABLES);
1816: if (variables != null) {
1817: boolean known = variables.contains(name);
1818: // Ensure undefined is "defined"
1819: known |= "undefined".equals(name);
1820: if (!known) {
1821: this .checkedNode = ((JavascriptGenerator) translator)
1822: .makeCheckedNode(node);
1823: }
1824: }
1825: }
1826: }
1827:
1828: public SimpleNode declare() {
1829: Set variables = (Set) context
1830: .get(TranslationContext.VARIABLES);
1831: if (variables != null) {
1832: variables.add(this .name);
1833: }
1834: return this .node;
1835: }
1836:
1837: public SimpleNode init() {
1838: Set variables = (Set) context
1839: .get(TranslationContext.VARIABLES);
1840: if (variables != null) {
1841: variables.add(this .name);
1842: }
1843: return this .node;
1844: }
1845:
1846: public SimpleNode get(boolean checkUndefined) {
1847: if (checkUndefined && checkedNode != null) {
1848: return checkedNode;
1849: }
1850: return node;
1851: }
1852:
1853: public SimpleNode set(Boolean warnGlobal) {
1854: if (warnGlobal == null) {
1855: if (context.type instanceof ASTProgram) {
1856: warnGlobal = Boolean.FALSE;
1857: } else {
1858: warnGlobal = Boolean
1859: .valueOf(options
1860: .getBoolean(Compiler.WARN_GLOBAL_ASSIGNMENTS));
1861: }
1862: }
1863: if ((checkedNode != null) && warnGlobal.booleanValue()) {
1864: System.err
1865: .println("Warning: Assignment to free variable "
1866: + name
1867: + " in "
1868: + node.filename
1869: + " ("
1870: + node.beginLine + ")");
1871: }
1872: return node;
1873: }
1874: }
1875:
1876: static public Set uncheckedProperties = new HashSet(Arrays
1877: .asList(new String[] { "call", "apply", "prototype" }));
1878:
1879: static public class PropertyReference extends MemberReference {
1880: String propertyName;
1881:
1882: public PropertyReference(Translator translator,
1883: SimpleNode node, int referenceCount, SimpleNode object,
1884: ASTIdentifier propertyName) {
1885: super (translator, node, referenceCount, object);
1886: this .propertyName = (String) propertyName.getName();
1887: // TODO: [2006-04-24 ptw] Don't make checkedNode when you know
1888: // that the member exists
1889: // This is not right, but Opera does not support [[Call]] on
1890: // call or apply, so we can't check for them
1891: // if (! uncheckedProperties.contains(this.propertyName)) {
1892: // this.checkedNode = ((JavascriptGenerator)translator).makeCheckedNode(node);
1893: // }
1894: }
1895: }
1896:
1897: static public class IndexReference extends MemberReference {
1898: SimpleNode indexExpr;
1899:
1900: public IndexReference(Translator translator, SimpleNode node,
1901: int referenceCount, SimpleNode object,
1902: SimpleNode indexExpr) {
1903: super (translator, node, referenceCount, object);
1904: this .indexExpr = indexExpr;
1905: // We don't check index references for compatibility with SWF compiler
1906: }
1907: }
1908:
1909: JavascriptReference translateReference(SimpleNode node,
1910: int referenceCount) {
1911: if (node instanceof ASTIdentifier) {
1912: return new VariableReference(this , node, referenceCount,
1913: ((ASTIdentifier) node).getName());
1914: }
1915:
1916: SimpleNode[] args = node.getChildren();
1917: if (node instanceof ASTPropertyIdentifierReference) {
1918: args[0] = visitExpression(args[0]);
1919: // If args[1] is an identifier, it is a literal, otherwise
1920: // translate it.
1921: if (!(args[1] instanceof ASTIdentifier)) {
1922: args[1] = visitExpression(args[1]);
1923: }
1924: return new PropertyReference(this , node, referenceCount,
1925: args[0], (ASTIdentifier) args[1]);
1926: } else if (node instanceof ASTPropertyValueReference) {
1927: args[0] = visitExpression(args[0]);
1928: args[1] = visitExpression(args[1]);
1929: return new IndexReference(this , node, referenceCount,
1930: args[0], args[1]);
1931: }
1932:
1933: return new JavascriptReference(this , node, referenceCount);
1934: }
1935: }
1936:
1937: /**
1938: * @copyright Copyright 2006-2008 Laszlo Systems, Inc. All Rights
1939: * Reserved. Use is subject to license terms.
1940: */
|