001: /*
002: * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: * Free SoftwareFoundation, Inc.
023: * 59 Temple Place, Suite 330
024: * Boston, MA 02111-1307 USA
025: *
026: * @author Scott Ferguson
027: */
028:
029: package com.caucho.es.parser;
030:
031: import com.caucho.es.ESId;
032: import com.caucho.util.CharBuffer;
033: import com.caucho.util.IntMap;
034:
035: import java.io.IOException;
036: import java.util.ArrayList;
037: import java.util.HashMap;
038: import java.util.Iterator;
039:
040: /**
041: * Function is an intermediate form representing an expression.
042: */
043: class Function {
044: static ESId PROTOTYPE = ESId.intern("prototype");
045:
046: Parser parser;
047: ParseClass cl;
048: private Function parent;
049:
050: int funDepth;
051: private int lambdaCount = 0;
052:
053: ArrayList formals;
054: Expr returnType;
055:
056: ArrayList variables = new ArrayList();
057:
058: // child closures
059: ArrayList functions;
060: private IntMap funMap;
061:
062: boolean isClass;
063: ESId classProto; // null for non-classes
064: Function constructor;
065:
066: String name;
067: ESId id;
068: int num;
069: boolean isSilent;
070: boolean hasCall;
071: boolean hasThis;
072: boolean allowLocals;
073: boolean allowJavaLocals;
074: boolean needsScope;
075:
076: ArrayList data = new ArrayList();
077: private HashMap vars = new HashMap();
078: CharBuffer tail;
079: private int iterCount;
080: private int tempCount;
081: private int stmtCount;
082: private int stmtTop;
083: private HashMap usedVars;
084: private boolean isGlobal;
085: // True if an arguments variable needs to be created. eval, closures,
086: // the arguments variable and "with" will set this.
087: private boolean needsArguments;
088: private boolean needsStatementResults;
089: // True if any variable can be used even if not explicitly referenced
090: // e.g. if the "arguments" variable is used.
091: private boolean useAllVariables;
092: // True if the function contains an eval
093: private boolean isEval;
094: // True if the function contains a switch
095: private boolean hasSwitch;
096:
097: Function(ParseClass cl, Function parent, String name, ESId id,
098: boolean isClass) {
099: this .id = id;
100: this .name = name;
101: this .parent = parent;
102: this .cl = cl;
103:
104: if (parent == null || isClass)
105: funDepth = 0;
106: else
107: funDepth = parent.funDepth + 1;
108:
109: num = -1;
110:
111: isGlobal = parent == null;
112: allowLocals = funDepth >= 1 || isClass;
113: allowJavaLocals = allowLocals;
114: needsStatementResults = parent == null;
115: }
116:
117: void setFast(boolean isFast) {
118: if (isFast) {
119: allowLocals = true;
120: allowJavaLocals = true;
121: }
122: }
123:
124: Function getParent() {
125: return parent;
126: }
127:
128: boolean isGlobalScope() {
129: return !needsScope
130: && (getFunctionDepth() == 0 || getFunctionDepth() == 1
131: && !needsArguments());
132: }
133:
134: boolean needsStatementResults() {
135: return needsStatementResults;
136: }
137:
138: void setNeedsResults() {
139: needsStatementResults = true;
140: }
141:
142: void setEval() {
143: setArguments();
144: needsStatementResults = true;
145: isEval = true;
146: }
147:
148: boolean useAllVariables() {
149: return isEval || useAllVariables;
150: }
151:
152: void setUseAllVariables() {
153: useAllVariables = true;
154: }
155:
156: String getStatementVar() {
157: if (!needsStatementResults())
158: return null;
159: else
160: return "_val" + stmtCount;
161: }
162:
163: void pushStatementLoop() {
164: if (!needsStatementResults())
165: return;
166:
167: stmtCount++;
168: if (stmtCount > stmtTop)
169: stmtTop = stmtCount;
170: }
171:
172: void popStatementLoop() {
173: if (!needsStatementResults())
174: return;
175:
176: stmtCount--;
177: }
178:
179: void setCall() {
180: this .hasCall = true;
181: }
182:
183: void setThis() {
184: this .hasThis = true;
185: }
186:
187: /**
188: * Force all unassigned variables to become object vars.
189: */
190: void setVars() {
191: Iterator iter = vars.values().iterator();
192: while (iter.hasNext()) {
193: Variable var = (Variable) iter.next();
194: var.getType();
195: }
196: }
197:
198: boolean isGlobal() {
199: return isGlobal;
200: }
201:
202: int getFunctionDepth() {
203: return funDepth;
204: }
205:
206: void disableGlobal() {
207: isGlobal = false;
208: }
209:
210: void disallowLocal() {
211: if (parent != null) {
212: allowJavaLocals = false;
213: allowLocals = false;
214: needsScope = true;
215: }
216: }
217:
218: void disallowJavaLocal() {
219: allowJavaLocals = false;
220: }
221:
222: void setNeedsScope() {
223: if (parent != null)
224: needsScope = true;
225: }
226:
227: void setArguments() {
228: needsArguments = true;
229: setNeedsScope();
230: disallowLocal();
231: }
232:
233: boolean needsArguments() {
234: return needsArguments;
235: }
236:
237: boolean allowLocals() {
238: return allowLocals;
239: }
240:
241: boolean allowJavaLocals() {
242: return allowLocals && allowJavaLocals;
243: }
244:
245: void useClosureVar(ESId name) {
246: for (Function fun = parent; fun != null; fun = fun.parent) {
247: Variable var = (Variable) fun.vars.get(name);
248:
249: fun.needsArguments = true;
250:
251: if (var != null)
252: var.setUsedByClosure();
253: else {
254: if (fun.usedVars == null)
255: fun.usedVars = new HashMap();
256: fun.usedVars.put(name, name);
257: }
258: }
259: }
260:
261: /**
262: * Returns true if the variable is declared.
263: */
264: boolean hasVar(ESId name) {
265: return vars.get(name) != null;
266: }
267:
268: /**
269: * Returns a new variable.
270: */
271: IdExpr newVar(Block block, ESId name) {
272: return newVar(block, name, null);
273: }
274:
275: /**
276: * Returns a new variable.
277: */
278: IdExpr newVar(Block block, ESId name, Expr type) {
279: Variable var = (Variable) vars.get(name);
280:
281: if (var == null && type == null) {
282: var = cl.getVariable(name);
283: if (var != null) {
284: return new IdExpr(block, var);
285: }
286: }
287:
288: if (var == null) {
289:
290: var = new Variable(block, name, type, false);
291: vars.put(name, var);
292:
293: if (usedVars != null && usedVars.get(name) != null)
294: var.setUsedByClosure();
295: }
296:
297: useClosureVar(name);
298:
299: return new IdExpr(block, var);
300: }
301:
302: /**
303: * Add a variable to the function. If the function is the global
304: * function, add it to the class.
305: */
306: void addVariable(Block block, ESId id, Expr type) {
307: if (variables == null)
308: variables = new ArrayList();
309:
310: Variable var = (Variable) vars.get(id);
311: if (var == null) {
312: var = new Variable(block, id, type, allowLocals);
313: vars.put(id, var);
314:
315: if (usedVars != null && usedVars.get(id) != null)
316: var.setUsedByClosure();
317:
318: // Only add global variables if they're declared, not if defined
319: // by use.
320: if (parent == null && type != null)
321: cl.addVariable(id, var);
322: } else if (parent != null)
323: var.setLocal();
324:
325: if (!variables.contains(var)
326: && (formals == null || !formals.contains(var)))
327: variables.add(var);
328:
329: useClosureVar(id);
330: }
331:
332: int getIter() {
333: return iterCount++;
334: }
335:
336: String getTemp() {
337: return "temp" + tempCount++;
338: }
339:
340: void setConstructor(Function constructor) {
341: isClass = true;
342: this .constructor = constructor;
343: }
344:
345: void setClassProto(ESId classProto) {
346: isClass = true;
347: this .classProto = classProto;
348: }
349:
350: void setCodeNumber(int num) {
351: this .num = num;
352: }
353:
354: void addFormal(Block block, ESId id, Expr type) {
355: if (formals == null)
356: formals = new ArrayList();
357:
358: Variable var = new Variable(block, id, type, true);
359: var.setUsed();
360: formals.add(var);
361: vars.put(id, var);
362: }
363:
364: int getFormalSize() {
365: return formals == null ? 0 : formals.size();
366: }
367:
368: Variable getFormal(int j) {
369: return (Variable) formals.get(j);
370: }
371:
372: Expr getReturnType() {
373: return returnType;
374: }
375:
376: void setReturnType(Expr type) {
377: returnType = type;
378: }
379:
380: int getVariableSize() {
381: return variables == null ? 0 : variables.size();
382: }
383:
384: void addFunction(Function function) {
385: if (functions == null) {
386: functions = new ArrayList();
387: funMap = new IntMap();
388: }
389: int pos = funMap.get(function.id);
390:
391: if (pos < 0) {
392: funMap.put(function.id, functions.size());
393: functions.add(function);
394: } else
395: functions.set(pos, function);
396: }
397:
398: int getFunctionSize() {
399: return functions == null ? 0 : functions.size();
400: }
401:
402: Function getFunction(int i) {
403: return (Function) functions.get(i);
404: }
405:
406: Function getFunction(ESId id) {
407: if (funMap == null)
408: return null;
409:
410: int index = funMap.get(id);
411:
412: if (index >= 0) {
413: return (Function) functions.get(index);
414: } else
415: return null;
416: }
417:
418: void print(Object value) {
419: if (tail == null)
420: tail = CharBuffer.allocate();
421: tail.append(String.valueOf(value));
422: }
423:
424: void println(String value) {
425: if (tail == null)
426: tail = CharBuffer.allocate();
427: tail.append(String.valueOf(value));
428: tail.append('\n');
429: }
430:
431: void addExpr(Expr expr) {
432: if (tail != null)
433: data.add(tail);
434: tail = null;
435: data.add(expr);
436: expr.setUsed();
437: }
438:
439: void addBoolean(Expr expr) {
440: if (tail != null)
441: data.add(tail);
442: tail = null;
443:
444: data.add(expr.setBoolean());
445: expr.setUsed();
446: }
447:
448: Object getTop() {
449: if (tail != null)
450: return tail;
451: else if (data.size() > 0)
452: return data.get(data.size() - 1);
453: else {
454: tail = new CharBuffer();
455: return tail;
456: }
457: }
458:
459: Object getSwitch() {
460: if (tail != null)
461: data.add(tail);
462: tail = null;
463:
464: hasSwitch = true;
465:
466: return data.get(data.size() - 1);
467: }
468:
469: int mark() {
470: if (tail != null)
471: data.add(tail);
472: tail = null;
473:
474: return data.size();
475: }
476:
477: void moveChunk(Object topObject, int mark) {
478: if (tail != null)
479: data.add(tail);
480: tail = null;
481:
482: int top;
483: for (top = 0; top < mark; top++) {
484: if (data.get(top) == topObject)
485: break;
486: }
487: top++;
488:
489: int here = data.size();
490:
491: for (int i = 0; i < here - mark; i++) {
492: Object chunk = data.remove(data.size() - 1);
493: data.add(top, chunk);
494: }
495: }
496:
497: void writeCode(ParseClass cl) throws IOException {
498: cl.print("public ");
499:
500: if (returnType == null)
501: cl.print("ESBase ");
502: else if (returnType.getType() == Expr.TYPE_INTEGER)
503: cl.print("int ");
504: else
505: cl.print("ESBase ");
506:
507: cl.print(name + "(Call _env, int _length");
508:
509: for (int i = 0; i < getFormalSize(); i++) {
510: Variable formal = (Variable) formals.get(i);
511:
512: if (formal.getTypeExpr() instanceof TypeExpr) {
513: TypeExpr type = (TypeExpr) formal.getTypeExpr();
514:
515: if (type.getTypeName() != null)
516: cl.print(", " + type.getTypeName() + " "
517: + formal.getId());
518: }
519: }
520: cl.println(")");
521:
522: cl.println("throws Throwable");
523: cl.println("{");
524: cl.pushDepth();
525:
526: if (hasCall)
527: cl.println("Call _call = _env.getCall();");
528:
529: if (hasThis)
530: cl.println("ESObject _this = _env.getThis();");
531:
532: if (parent != null && functions != null && functions.size() > 0) {
533: needsArguments = true;
534: setNeedsScope();
535: }
536:
537: // Eval just uses the calling scope
538: if (isEval) {
539: cl.println("ESObject _arg = _env.getEval();");
540: } else {
541: if (needsScope && parent != null)
542: cl.println("_env.fillScope();");
543:
544: if (needsArguments && parent != null) {
545: cl.print("ESObject _arg = _env.createArg(");
546: if (getFormalSize() > 0)
547: cl.print("_js._a_" + num);
548: else
549: cl.print("_js._a_null");
550: cl.println(", _length);");
551: }
552: }
553:
554: printFormals();
555: printLocalVariables();
556:
557: // do closures
558: for (int i = 0; needsArguments && functions != null
559: && i < functions.size(); i++) {
560: if (i == 0)
561: cl.println("ESClosure _closure;");
562:
563: Function fun = (Function) functions.get(i);
564: Variable var = (Variable) vars.get(fun.id);
565:
566: if (!isGlobal && !isEval && var != null && !var.isUsed()
567: && !useAllVariables) {
568: continue;
569: }
570:
571: cl.print("_closure = new ESClosure(");
572: cl.printLiteral(fun.id);
573: cl.println(", _js, null, " + fun.num
574: + ", _js._a_null, null);");
575: cl.println("_closure.closure(_env);");
576:
577: if (!isEval && allowLocals && var != null && var.isUsed()
578: && var.isJavaLocal()) {
579: cl.println("ESBase " + fun.id + " = _closure;");
580: continue;
581: } else if (!isEval && parent != null)
582: cl.print("_arg.put(");
583: else
584: cl.print("_env.global.put(");
585: cl.printLiteral(fun.id);
586: cl.print(", _closure, ");
587: if (!isEval)
588: cl.print("ESBase.DONT_ENUM|ESBase.DONT_DELETE");
589: else
590: cl.print("0");
591: cl.println(");");
592: }
593:
594: for (int i = 0; i < iterCount; i++)
595: cl.println("java.util.Iterator iter" + i + ";");
596: for (int i = 0; i < tempCount; i++)
597: cl.println("ESBase temp" + i + ";");
598: if (needsStatementResults())
599: cl.println("ESBase _val0 = ESBase.esUndefined;");
600: for (int i = 1; i <= stmtTop; i++)
601: cl.println("ESBase _val" + i + " = ESBase.esUndefined;");
602: if (hasSwitch) {
603: cl.println("int _switchcode;");
604: cl.println("ESBase _switchtemp;");
605: }
606:
607: for (int i = 0; i < data.size(); i++) {
608: Object d = data.get(i);
609: if (d instanceof CharBuffer)
610: cl.print((CharBuffer) d);
611: else if (d instanceof Expr) {
612: Expr expr = (Expr) d;
613: //cl.setLine(expr.getFilename(), expr.getLine());
614: expr.printExpr();
615: }
616: }
617:
618: if (tail != null)
619: cl.print(tail);
620:
621: cl.popDepth();
622: cl.println("}");
623: }
624:
625: void printFormals() throws IOException {
626: formal: for (int i = 0; formals != null && i < formals.size(); i++) {
627: Variable formal = (Variable) formals.get(i);
628:
629: // Functions have priority
630: if (funMap != null && funMap.get(formal.getId()) >= 0)
631: continue formal;
632:
633: // Duplicate formals use the last one
634: for (int j = i + 1; j < formals.size(); j++) {
635: if (formal.getId() == ((Variable) formals.get(j))
636: .getId())
637: continue formal;
638: }
639:
640: if (!allowLocals) {
641: } else if (formal.getTypeExpr() instanceof JavaTypeExpr) {
642: } else if (formal.getType() != Expr.TYPE_INTEGER) {
643: cl.print("ESBase " + formal.getId());
644: cl.println(" = _env.getArg(" + i + ", _length);");
645: }
646: }
647: }
648:
649: /**
650: * Initializes the local variables for a function.
651: */
652: void printLocalVariables() throws IOException {
653: for (int i = 0; i < variables.size(); i++) {
654: Variable var = (Variable) variables.get(i);
655:
656: if (funMap != null && funMap.get(var.getId()) >= 0) {
657: var.killLocal();
658: continue;
659: }
660:
661: if (var.isJavaGlobal()) {
662: // already initialized
663: } else if ((!allowLocals || !var.isJavaLocal())
664: && isGlobal()) {
665: cl.print("_env.global.setProperty(");
666: cl.printLiteral(var.getId());
667: cl.println(", ESBase.esUndefined);");
668: } else if (!var.isUsed() && !useAllVariables()) {
669: } else if (!allowLocals || !var.isJavaLocal()) {
670: if (isEval) {
671: cl.print("if (_arg.getProperty(");
672: cl.printLiteral(var.getId());
673: cl.println(") == ESBase.esEmpty)");
674: cl.print(" ");
675: }
676: if (!var.hasInit()) {
677: cl.print("_arg.put(");
678: cl.printLiteral(var.getId());
679: cl.print(", ESBase.esUndefined, ");
680: if (!isEval)
681: cl.print("ESBase.DONT_ENUM|ESBase.DONT_DELETE");
682: else
683: cl.print("0");
684: cl.println(");");
685: }
686: } else if (var.getType() == Expr.TYPE_BOOLEAN) {
687: cl.println("boolean " + var.getId() + ";");
688: if (!var.hasInit())
689: cl.println(var.getId() + " = false;");
690: } else if (var.getType() == Expr.TYPE_INTEGER) {
691: cl.println("int " + var.getId() + ";");
692: if (!var.hasInit())
693: cl.println(var.getId() + " = 0;");
694: } else if (var.getType() == Expr.TYPE_NUMBER) {
695: cl.println("double " + var.getId() + ";");
696: if (!var.hasInit())
697: cl.println(var.getId() + " = Double.NaN;");
698: } else if (var.getType() == Expr.TYPE_STRING) {
699: cl.println("ESString " + var.getId() + ";");
700: if (!var.hasInit())
701: cl.println(var.getId() + " = null;");
702: } else if (var.getType() == Expr.TYPE_JAVA
703: && var.getTypeExpr() != null) {
704: TypeExpr type = (TypeExpr) var.getTypeExpr();
705:
706: cl.println(type.getTypeName() + " " + var.getId()
707: + " = null;");
708: } else if (var.isLocal()) {
709: if (!var.hasInit())
710: cl.println("ESBase " + var.getId()
711: + " = ESBase.esUndefined;");
712: else
713: cl.println("ESBase " + var.getId() + ";");
714: } else {
715: cl.print(" _env.global.setProperty(");
716: cl.printLiteral(var.getId());
717: cl.println(", ESBase.esUndefined);");
718: }
719: }
720: }
721: }
|