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.ESBase;
032: import com.caucho.es.ESException;
033: import com.caucho.es.ESId;
034: import com.caucho.util.CharBuffer;
035:
036: import java.util.ArrayList;
037: import java.util.HashMap;
038:
039: /**
040: * Block is an intermediate form representing an expression.
041: */
042: class Block {
043: private static HashMap specialNames;
044:
045: Function function;
046: private Block parent;
047: private Expr lastExpr;
048: boolean isDead;
049: private boolean hasStatementValue;
050: private Parser parser;
051: private boolean isLoop;
052: private boolean canExit;
053: private boolean hasDefault;
054: private int withDepth;
055: private ESId id;
056: private Expr mark;
057:
058: private Object switchTop;
059: private Object top;
060: private int topMark;
061:
062: private Block() {
063: }
064:
065: Block create() throws ESException {
066: evalExpr();
067:
068: Block block = new Block();
069:
070: block.function = function;
071: block.parent = this ;
072: block.lastExpr = null;
073: block.isDead = false;
074: block.hasStatementValue = hasStatementValue;
075: block.parser = parser;
076: block.isLoop = false;
077: block.canExit = false;
078: block.withDepth = withDepth;
079: block.id = null;
080: block.mark = null;
081: block.switchTop = null;
082: block.top = null;
083:
084: block.setTop();
085:
086: return block;
087: }
088:
089: static Block create(Parser parser, Function function) {
090: Block block = new Block();
091:
092: block.function = function;
093: block.parent = null;
094: block.lastExpr = null;
095: block.isDead = false;
096: block.hasStatementValue = function.needsStatementResults();
097: block.parser = parser;
098: block.isLoop = false;
099: block.canExit = false;
100: block.withDepth = 0;
101: block.id = null;
102:
103: block.top = null;
104: block.topMark = 0;
105:
106: function.setVars();
107:
108: return block;
109: }
110:
111: ClassLoader getClassLoader() {
112: return parser.getClassLoader();
113: }
114:
115: void setTop() {
116: top = function.getTop();
117: if (top instanceof CharBuffer) {
118: CharBuffer cb = (CharBuffer) top;
119: topMark = cb.length();
120: } else
121: topMark = 0;
122: }
123:
124: Block pop() {
125: Block parent = this .parent;
126: function.setVars();
127: free();
128:
129: return parent;
130: }
131:
132: boolean isGlobal() {
133: return function.isGlobal();
134: }
135:
136: int getDepth() {
137: return withDepth;
138: }
139:
140: boolean allowSpecial() {
141: return parent == null;
142: }
143:
144: String getFilename() {
145: String filename = parser.lexer.getLastFilename();
146: int p = filename.lastIndexOf('/');
147: if (p > 0)
148: filename = filename.substring(p + 1);
149: p = filename.lastIndexOf('\\');
150: if (p > 0)
151: filename = filename.substring(p + 1);
152:
153: return filename;
154: }
155:
156: int getLine() {
157: int line = parser.lexer.getLastLine();
158:
159: return line;
160: }
161:
162: void setLine(int line) {
163: }
164:
165: /**
166: * Returns true if the variable is already declared.
167: */
168: boolean hasVar(ESId name) {
169: return function.hasVar(name) || specialNames.get(name) != null;
170: }
171:
172: /**
173: * Returns an expression for a new variable
174: */
175: IdExpr newVar(ESId name) {
176: return newVar(name, null);
177: }
178:
179: IdExpr newVar(ESId name, Expr type) {
180: if (withDepth > 0)
181: return new IdExpr(this , new Variable(this , name, type,
182: false));
183:
184: IdExpr expr = function.newVar(this , name, type);
185:
186: // Kill variables inside an if
187: if (parent != null)
188: expr.getType();
189:
190: return expr;
191: }
192:
193: /**
194: * Define a new variable.
195: */
196: void defVar(ESId name) {
197: function.addVariable(this , name, null);
198: }
199:
200: /**
201: * Define a new variable with the given type.
202: */
203: void defVar(ESId name, Expr type) {
204: function.addVariable(this , name, type);
205: }
206:
207: Expr newLiteral(ESBase value) {
208: return new LiteralExpr(this , value);
209: }
210:
211: Expr newRegexp(ESBase value, String flags) throws ESException {
212: return new RegexpExpr(this , value, flags);
213: }
214:
215: Expr newThis() {
216: return new SpecialExpr(this , SpecialExpr.THIS);
217: }
218:
219: Expr newArray(Expr expr) {
220: return new SpecialExpr(this , SpecialExpr.ARRAY, expr);
221: }
222:
223: Expr hasNext(String iter) {
224: return new SpecialExpr(this , SpecialExpr.HAS_NEXT, iter);
225: }
226:
227: Expr newType(ESId name) {
228: return TypeExpr.create(this , name);
229: }
230:
231: void addExpr(Expr expr) throws ESException {
232: if (isDead)
233: throw error("Statement is unreachable.");
234: if (lastExpr != null)
235: lastExpr.exprStatement(function);
236:
237: if (hasStatementValue
238: && !void.class.equals(expr.getJavaClass()))
239: lastExpr = expr;
240: else {
241: lastExpr = null;
242: expr.exprStatement(function);
243: }
244: }
245:
246: Block startBlock() throws ESException {
247: evalExpr();
248: function.println("{");
249: return create();
250: }
251:
252: Block startBlock(ESId id) throws ESException {
253: if (findBlock(id) != null)
254: throw error("duplicate label `" + id + "'");
255:
256: evalExpr();
257: Block block = create();
258: block.id = id;
259: function.println(id + ": {");
260: block.setTop();
261: return block;
262: }
263:
264: Block finishBlock() throws ESException {
265: evalExpr();
266: function.println("}");
267: this .id = null;
268: Block old = pop();
269: if (isDead && !canExit)
270: old.isDead = true;
271:
272: return old;
273: }
274:
275: void endBlock() throws ESException {
276: evalExpr();
277: function.println("}");
278: this .id = null;
279: }
280:
281: void startIf(Expr expr, boolean isElse) throws ESException {
282: evalExpr();
283: if (isElse)
284: function.print(" else ");
285: function.print("if (");
286: function.addBoolean(expr);
287: function.println(") {");
288: setTop();
289: }
290:
291: void startElse() throws ESException {
292: evalExpr();
293: function.println(" else {");
294: setTop();
295: }
296:
297: Block startWhile(ESId id, Expr expr) throws ESException {
298: evalExpr();
299: if (id != null)
300: function.println(id + ":");
301: function.print("while (");
302: function.addBoolean(expr);
303: function.println(") {");
304:
305: Block block = create();
306: startLoop(id);
307:
308: if (!(expr instanceof LiteralExpr)
309: || !((LiteralExpr) expr).getLiteral().toBoolean())
310: canExit = true;
311:
312: return block;
313: }
314:
315: Block startFor(ESId id, Expr test, Expr incr) throws ESException {
316: evalExpr();
317: if (id != null)
318: function.println(id + ":");
319: function.print("for (;");
320: if (test != null)
321: function.addBoolean(test);
322: function.print(";");
323: if (incr != null)
324: function.addExpr(incr);
325: function.println(") {");
326: function.cl.pushDepth();
327:
328: Block block = create();
329: startLoop(id);
330:
331: if (test == null)
332: canExit = false;
333: else if (!(test instanceof LiteralExpr)
334: || !((LiteralExpr) test).getLiteral().toBoolean())
335: canExit = true;
336:
337: return block;
338: }
339:
340: Block startDo(ESId id) throws ESException {
341: evalExpr();
342: if (id != null)
343: function.println(id + ":");
344: function.print("do {");
345:
346: Block block = create();
347: startLoop(id);
348: return block;
349: }
350:
351: Block endDo(Expr expr) throws ESException {
352: evalExpr();
353:
354: Block old = endLoop();
355:
356: if (!(expr instanceof LiteralExpr)
357: || !((LiteralExpr) expr).getLiteral().toBoolean())
358: old.canExit = true;
359:
360: if (old.canExit)
361: old.isDead = false;
362:
363: function.print("while (");
364: function.addBoolean(expr);
365: function.println(");");
366:
367: return old;
368: }
369:
370: void startLoop(ESId id) {
371: String oldVar = function.getStatementVar();
372: function.pushStatementLoop();
373: String newVar = function.getStatementVar();
374: if (oldVar != null)
375: function.println(newVar + " = " + oldVar + ";");
376: this .id = id;
377: isLoop = true;
378: canExit = false;
379: }
380:
381: Block endLoop() throws ESException {
382: evalExpr();
383: String newVar = function.getStatementVar();
384: function.popStatementLoop();
385: String oldVar = function.getStatementVar();
386: if (oldVar != null && !isDead)
387: function.println(oldVar + " = " + newVar + ";");
388: function.cl.popDepth();
389: function.println("}");
390:
391: Block old = pop();
392: if (!old.canExit)
393: old.isDead = true;
394:
395: return old;
396: }
397:
398: Block startSwitch(Expr test) throws ESException {
399: ESId id = ESId.intern("_switchtemp");
400:
401: function.print("_switchtemp = ");
402: function.addExpr(test);
403: function.println(";");
404:
405: Block block = create();
406: block.switchTop = function.getSwitch();
407: block.isLoop = true;
408: block.hasDefault = false;
409:
410: function.println("switch (_switchcode) {");
411: return block;
412: }
413:
414: void doCase(int i) throws ESException {
415: isDead = false;
416: evalExpr();
417: function.println("case " + i + ":");
418: }
419:
420: void doDefault() throws ESException {
421: isDead = false;
422: hasDefault = true;
423: evalExpr();
424: function.println("default:");
425: }
426:
427: Block fillSwitch(ArrayList exprs) throws ESException {
428: evalExpr();
429: if (!hasDefault && !isDead) {
430: function.println("default:");
431: function.println(" break;");
432: } else if (!isDead)
433: function.println("break;");
434: function.println("}");
435:
436: int mark = function.mark();
437:
438: for (int i = 0; i < exprs.size(); i++) {
439: if (i != 0)
440: function.print("else ");
441:
442: Expr test = (Expr) exprs.get(i);
443:
444: function.print("if (_switchtemp.equals(");
445: function.addExpr(test);
446: function.println(")) _switchcode = " + i + ";");
447: }
448: if (exprs.size() > 0)
449: function.print("else ");
450: function.println("_switchcode = -1;");
451:
452: function.moveChunk(switchTop, mark);
453:
454: Block old = pop();
455: if (isDead && !canExit && hasDefault)
456: old.isDead = true;
457:
458: return old;
459: }
460:
461: void doBreak(ESId id) throws ESException {
462: Block block = this ;
463: for (; block != null; block = block.parent) {
464: if (block.id == id) {
465: block.canExit = true;
466: break;
467: }
468: }
469:
470: if (block == null)
471: throw error("break needs enclosing loop");
472:
473: function.setVars();
474: evalExpr();
475: function.println("break " + id + ";");
476: isDead = true;
477: }
478:
479: void doBreak() throws ESException {
480: Block block = this ;
481: for (; block != null; block = block.parent) {
482: if (block.isLoop) {
483: block.canExit = true;
484: break;
485: }
486: }
487:
488: if (block == null)
489: throw error("break needs enclosing loop");
490:
491: function.setVars();
492: evalExpr();
493: function.println("break;");
494: isDead = true;
495: }
496:
497: void doContinue(ESId id) throws ESException {
498: Block block = this ;
499: for (; block != null; block = block.parent) {
500: if (block.id == id && block.isLoop)
501: break;
502: /*
503: else
504: block.canExit = true;
505: */
506: }
507: if (block == null)
508: throw error("continue needs enclosing loop");
509:
510: function.setVars();
511: evalExpr();
512: function.println("continue " + id + ";");
513: isDead = true;
514: }
515:
516: void doContinue() throws ESException {
517: if (findBlock(null) == null)
518: throw error("continue needs enclosing loop");
519:
520: function.setVars();
521: evalExpr();
522: function.println("continue;");
523: isDead = true;
524: }
525:
526: private Block findBlock(ESId id) {
527: for (Block block = this ; block != null; block = block.parent) {
528: if (id != null && block.id == id)
529: return block;
530: else if (id == null && block.isLoop)
531: return block;
532: }
533:
534: return null;
535: }
536:
537: Block startWith(Expr expr) throws ESException {
538: function.setArguments();
539: function.setUseAllVariables();
540: evalExpr();
541: withDepth++;
542: function.println("try {");
543: function.print("_env.pushScope(");
544: function.addExpr(expr);
545: function.println(");");
546: setTop();
547:
548: return this ;
549: }
550:
551: Block endWith() throws ESException {
552: evalExpr();
553: withDepth--;
554: function.println("} finally {");
555: function.println("_env.popScope();");
556: function.println("}");
557:
558: return this ;
559: }
560:
561: int getWithDepth() {
562: return withDepth;
563: }
564:
565: Block startTry() throws ESException {
566: function.setVars();
567: evalExpr();
568: function.println("try {");
569: return this ;
570: }
571:
572: Block endTry() throws ESException {
573: function.setVars();
574: evalExpr();
575: function.println("}");
576:
577: return this ;
578: }
579:
580: void doTry() throws ESException {
581: evalExpr();
582:
583: int i = 0;
584: for (; i < function.data.size(); i++) {
585: Object o = function.data.get(i);
586: if (o != top) {
587: } else if (o instanceof CharBuffer) {
588: CharBuffer cb = (CharBuffer) o;
589: cb.insert(topMark, " try {\n");
590: break;
591: } else {
592: function.data.add(i + 1, new CharBuffer(" try {\n"));
593: break;
594: }
595: }
596: if (i < function.data.size()) {
597: } else if (function.tail != null && top == function.tail)
598: function.tail.insert(topMark, " try {\n");
599: else
600: function.data.add(0, new CharBuffer(" try {\n"));
601:
602: function.println("}");
603: }
604:
605: Block startCatch(String exn, Expr var) throws ESException {
606: evalExpr();
607: String temp = "_e" + function.getTemp();
608: function.println("catch (" + exn + " " + temp + ") {");
609: if (var != null) {
610: Expr expr = new SpecialExpr(this , SpecialExpr.EXCEPTION,
611: temp);
612: var.assign(expr).exprStatement(function);
613: }
614:
615: isDead = false;
616: setTop();
617:
618: return this ;
619: }
620:
621: Block endCatch() throws ESException {
622: evalExpr();
623: function.println("}");
624: return this ;
625: }
626:
627: Block startFinally() throws ESException {
628: evalExpr();
629: Block block = create();
630: function.println("finally {");
631: function.pushStatementLoop();
632: block.setTop();
633:
634: return block;
635: }
636:
637: Block endFinally() throws ESException {
638: evalExpr();
639: function.println("}");
640: function.popStatementLoop();
641: return pop();
642: }
643:
644: Block startSynchronized(Expr expr) throws ESException {
645: evalExpr();
646: function.print("synchronized (");
647: function.addExpr(expr);
648: function.println(".toJavaObject()) {");
649:
650: return create();
651: }
652:
653: Block endSynchronized() throws ESException {
654: evalExpr();
655:
656: function.println("}");
657:
658: Block old = pop();
659: old.isDead = isDead;
660:
661: return old;
662: }
663:
664: void doThrow(Expr expr) throws ESException {
665: function.print("throw (Exception)");
666: function.addExpr(expr);
667: function.println(".toJavaObject();");
668: isDead = true;
669: }
670:
671: void doReturn(Expr value) throws ESException {
672: evalExpr();
673: function.print("return ");
674:
675: value.setUsed();
676: if (function.getReturnType() != null)
677: function.addExpr(new TopExpr(this , value, function
678: .getReturnType()));
679: else
680: function.addExpr(value);
681:
682: function.println(";");
683: isDead = true;
684: /* can't break
685: for (Block block = this; block != null; block = block.parent)
686: block.canExit = true;
687: */
688: }
689:
690: void doReturn() throws ESException {
691: evalExpr();
692:
693: if (function.getReturnType() != null)
694: function.print("return 0;");
695: else
696: function.print("return ESBase.esUndefined;");
697: isDead = true;
698: /*
699: for (Block block = this; block != null; block = block.parent)
700: block.canExit = true;
701: */
702: }
703:
704: void finish() throws ESException {
705: if (isDead)
706: return;
707:
708: if (lastExpr != null) {
709: function.print("return ");
710: function.addExpr(lastExpr);
711: function.println(";");
712: lastExpr = null;
713: } else if (hasStatementValue)
714: function.println("return _val0;");
715: else
716: function.println("return ESBase.esUndefined;");
717: }
718:
719: String newIterator(ESId id, Expr expr) throws ESException {
720: evalExpr();
721:
722: String iter = "iter" + function.getIter();
723: function.print(iter + " = ");
724: function.addExpr(expr);
725: function.println(".keys();");
726:
727: return iter;
728: }
729:
730: void evalExpr() throws ESException {
731: if (lastExpr == null)
732: return;
733:
734: function.print(function.getStatementVar() + " = ");
735: function.addExpr(lastExpr);
736: function.println(";");
737:
738: lastExpr = null;
739: }
740:
741: private static Block allocate() {
742: Block block = new Block();
743:
744: return block;
745: }
746:
747: ESException error(String message) {
748: return parser.lexer.error(message);
749: }
750:
751: void free() {
752: }
753:
754: static {
755: specialNames = new HashMap();
756: specialNames.put(ESId.intern("Object"), "Object");
757: specialNames.put(ESId.intern("Date"), "Date");
758: specialNames.put(ESId.intern("String"), "String");
759: specialNames.put(ESId.intern("Number"), "Number");
760: specialNames.put(ESId.intern("Array"), "Array");
761: specialNames.put(ESId.intern("Boolean"), "Boolean");
762: specialNames.put(ESId.intern("Math"), "Math");
763: };
764: }
|