001: /*
002: * $Id: SwitchStatement.java,v 1.27 2002/09/16 08:05:06 jkl Exp $
003: *
004: * Copyright (c) 2002 Njet Communications Ltd. All Rights Reserved.
005: *
006: * Use is subject to license terms, as defined in
007: * Anvil Sofware License, Version 1.1. See LICENSE
008: * file, or http://njet.org/license-1.1.txt
009: */
010: package anvil.script.statements;
011:
012: import anvil.core.Any;
013: import anvil.ErrorListener;
014: import anvil.Location;
015: import anvil.codec.Code;
016: import anvil.codec.ConstantPool;
017: import anvil.codec.Source;
018: import anvil.codec.Target;
019: import anvil.codec.Switch;
020: import anvil.parser.Tag;
021: import anvil.ErrorListener;
022: import anvil.script.compiler.ByteCompiler;
023: import anvil.script.Context;
024: import anvil.script.expression.Node;
025: import anvil.script.expression.Expression;
026: import anvil.script.expression.VariableNode;
027: import anvil.script.parser.TemplateParser;
028: import anvil.script.Grammar;
029: import anvil.java.util.BindingEnumeration;
030: import anvil.java.util.Hashlist;
031: import anvil.java.util.Holder;
032: import java.io.IOException;
033: import java.util.Enumeration;
034: import java.util.Iterator;
035: import java.util.ArrayList;
036:
037: /**
038: * class SwitchStatement
039: *
040: * @author: Jani Lehtimäki
041: */
042: public class SwitchStatement extends ScopedStatement implements Labeled {
043: public static final Any DEFAULT_MARKER = new Any() {
044: public int hashCode() {
045: return System.identityHashCode(this );
046: }
047:
048: public boolean equals(Object obj) {
049: return this == obj;
050: }
051: };
052:
053: public static int ON_SWITCH = 0;
054: public static int ON_CASE = 1;
055: public static int ON_DEFAULT = 2;
056:
057: private Expression _expression = null;
058: private VariableNode _switch;
059: private ArrayList _cases = new ArrayList();
060: private Hashlist _jumptable = new Hashlist();
061: private Case _currentCase = null;
062: private Case _defaultCase = null;
063: private int _state = ON_SWITCH;
064: private String _label = null;
065: private Source _startscope;
066: private Source _endscope;
067: private boolean _allconstant = true;
068:
069: public SwitchStatement(Statement parent, Location location) {
070: super (parent, location);
071: }
072:
073: public SwitchStatement(Statement parent, Location location,
074: Expression expression, String label) {
075: super (parent, location);
076: _expression = expression;
077: _label = label;
078: }
079:
080: public int typeOf() {
081: return Statement.ST_SWITCH;
082: }
083:
084: public String name() {
085: return "switch";
086: }
087:
088: public String getLabel() {
089: return _label;
090: }
091:
092: public Case getCase(Any key) {
093: if (_allconstant) {
094: return (Case) _jumptable.get(key);
095: }
096: return null;
097: }
098:
099: public Case getDefault() {
100: return _defaultCase;
101: }
102:
103: public void parse(TemplateParser parser, Tag tag) {
104: String s = tag.getValue("expr");
105: if (s == null) {
106: s = tag.getValue("by");
107: }
108: _expression = Grammar.parseExpression(s, getLocation(), parser);
109: _label = parseLabel(parser, tag);
110: }
111:
112: public BlockStatement getBlockStatement() {
113: if (_currentCase != null) {
114: return _currentCase.getBlock();
115: } else {
116: return null;
117: }
118: }
119:
120: public Statement getChildStatement() {
121: if (_currentCase != null) {
122: return _currentCase.getBlock();
123: } else {
124: return null;
125: }
126: }
127:
128: public Case onCase(ErrorListener listener, Expression expr) {
129: _state = ON_CASE;
130: _currentCase = new Case(expr, new ImplicitBlockStatement(this ,
131: getLocation()));
132: _cases.add(_currentCase);
133: return _currentCase;
134: }
135:
136: private Case onCase(TemplateParser parser, Tag tag) {
137: String s = tag.getValue("value");
138: if (s == null) {
139: s = tag.getValue("of");
140: }
141: Expression value = Grammar.parseExpression(s, parser
142: .getLocation(), parser);
143: return onCase(parser, value);
144: }
145:
146: public Case onDefault(ErrorListener listener, Location location) {
147: if (_defaultCase != null) {
148: listener.error(location, "Default already declared");
149: }
150: _state = ON_DEFAULT;
151: _defaultCase = new Case(null, new ImplicitBlockStatement(this ,
152: location));
153: _currentCase = _defaultCase;
154: _cases.add(_defaultCase);
155: return _currentCase;
156: }
157:
158: public boolean onTag(TemplateParser parser, int type, Tag tag) {
159: switch (type) {
160: case Statement.ST_ENDSWITCH:
161: parser.pop();
162: break;
163:
164: case Statement.ST_ENDCASE:
165: if (_state == ON_CASE) {
166: _state = ON_SWITCH;
167: } else {
168: return false;
169: }
170: break;
171:
172: case Statement.ST_ENDDEFAULT:
173: if (_state == ON_DEFAULT) {
174: _state = ON_SWITCH;
175: } else {
176: return false;
177: }
178: break;
179:
180: case ST_CASE:
181: onCase(parser, tag);
182: break;
183:
184: case ST_DEFAULT:
185: onDefault(parser, parser.getLocation());
186: break;
187:
188: case ST_TAG:
189: if (_state == ON_SWITCH) {
190: return true;
191: }
192:
193: default:
194: if (_state != ON_SWITCH) {
195: return super .onTag(parser, type, tag);
196: } else {
197: return false;
198: }
199:
200: }
201:
202: return true;
203: }
204:
205: public void onCharacters(TemplateParser parser, String cdata) {
206: if (_state != ON_SWITCH) {
207: super .onCharacters(parser, cdata);
208: }
209: }
210:
211: public void check(ErrorListener context) {
212: _expression.check(context);
213:
214: Iterator iter = _cases.iterator();
215: while (iter.hasNext()) {
216: ((Case) iter.next()).checkExpression(context);
217: }
218:
219: iter = _cases.iterator();
220: while (iter.hasNext()) {
221: Case caze = (Case) iter.next();
222: if (caze.isDefault()) {
223: _jumptable.put(DEFAULT_MARKER, caze);
224: } else {
225: Any value = caze.getConstant();
226: if (value != null) {
227: if (_jumptable.containsKey(value)) {
228: context.error(caze.getLocation(),
229: "Case value duplicated");
230: } else {
231: _jumptable.put(value, caze);
232: }
233: } else {
234: _allconstant = false;
235: _jumptable.clear();
236: break;
237: }
238: }
239: }
240:
241: iter = _cases.iterator();
242: while (iter.hasNext()) {
243: ((Case) iter.next()).check(context);
244: }
245:
246: if (!_allconstant) {
247: FunctionStatement function = getFunctionStatement();
248: if (function.isGenerator()) {
249: _switch = new VariableNode(function.declare("switch$"
250: + hashCode()));
251: }
252: }
253: }
254:
255: public Jumps eliminate(ErrorListener context) {
256: Jumps jumps = new Jumps();
257: Iterator iter = _cases.iterator();
258: while (iter.hasNext()) {
259: jumps.merge(((Case) iter.next()).eliminate(context));
260: }
261: if (_defaultCase != null) {
262: jumps.setBlocked(!jumps.hasBreak() && !jumps.hasContinue());
263: } else {
264: jumps.setBlocked(false);
265: }
266: return jumps.shift();
267: }
268:
269: public boolean isBlocked() {
270: if (_defaultCase == null) {
271: return false;
272: }
273: Iterator iter = _cases.iterator();
274: while (iter.hasNext()) {
275: if (!((Case) iter.next()).getBlock().isBlocked()) {
276: return false;
277: }
278: }
279: return true;
280: }
281:
282: public Source getStartOfScope() {
283: return _startscope;
284: }
285:
286: public Source getEndOfScope() {
287: return _endscope;
288: }
289:
290: public void compile(ByteCompiler context)
291: {
292: Code code = context.getCode();
293: ConstantPool pool = code.getPool();
294: Target start = code.getTarget();
295: _startscope = code.getSource();
296: _endscope = code.getSource();
297:
298: if (_allconstant) {
299: boolean allNumeric = true;
300: Enumeration enum = _jumptable.keys();
301: while(enum.hasMoreElements()) {
302: Any key = (Any)enum.nextElement();
303: if (key != DEFAULT_MARKER) {
304: if (!key.isInt()) {
305: allNumeric = false;
306: break;
307: }
308: }
309: }
310:
311: if (_expression.needLineNumbers()) {
312: context.location(_expression.getLocation());
313: }
314:
315: Switch select;
316:
317: if (allNumeric) {
318: _expression.compile(context, Expression.GET);
319: code.invokevirtual(pool.addMethodRef(context.TYPE_ANY, "toInt", "()I"));
320:
321: select = code.select();
322: enum = _jumptable.keys();
323: while(enum.hasMoreElements()) {
324: Any key = (Any)enum.nextElement();
325: if (key != DEFAULT_MARKER) {
326: select.addCase(key.toInt());
327: }
328: }
329: select.end();
330:
331: enum = _jumptable.keysAndElements();
332: while(enum.hasMoreElements()) {
333: Case caze = (Case)enum.nextElement();
334: if (caze.isDefault()) {
335: select.bindDefault();
336: } else {
337: select.bindCase(caze.getConstant().toInt());
338: }
339: caze.compile(context);
340: }
341:
342: } else {
343: int slot = context.addSwitch(_jumptable.keys());
344: code.getstatic(pool.addFieldRef(context.TYPE_MODULE, "_switch", "[Lanvil/java/util/Hashlist;"));
345: code.iconst(slot);
346: code.aaload();
347: _expression.compile(context, Expression.GET);
348: code.invokestatic(pool.addMethodRef("anvil/script/compiler/CompiledModule",
349: "switchCase", "(Lanvil/java/util/Hashlist;Lanvil/core/Any;)I"));
350:
351: select = code.select();
352: int size = _jumptable.size();
353: if (_defaultCase != null) {
354: size--;
355: }
356: for(int key=0; key<size; key++) {
357: select.addCase(key);
358: }
359: select.end();
360:
361: int key = 0;
362: enum = _jumptable.elements();
363: while(enum.hasMoreElements()) {
364: Case caze = (Case)enum.nextElement();
365: if (caze.isDefault()) {
366: select.bindDefault();
367: } else {
368: select.bindCase(key++);
369: }
370: caze.compile(context);
371: }
372: }
373:
374: if (_defaultCase == null) {
375: select.bindDefault();
376: }
377:
378: } else {
379: FunctionStatement function = getFunctionStatement();
380: boolean in_generator = function.isGenerator();
381: int l_key = 0;
382: if (in_generator) {
383: _switch.compile(context, new Node() {
384: public void compile(ByteCompiler context, int operation) {
385: _expression.compile(context, Expression.GET);
386: }
387: });
388: code.pop();
389: } else {
390: _expression.compile(context, Expression.GET);
391: l_key = code.addLocal();
392: code.astore(l_key);
393: }
394: int equalsmethod = pool.addMethodRef(context.TYPE_OBJECT, "equals", "(Ljava/lang/Object;)Z");
395:
396: Iterator iter = _cases.iterator();
397: while(iter.hasNext()) {
398: Case caze = (Case)iter.next();
399: if (!caze.isDefault()) {
400: Expression expression = caze.getExpression();
401: if (in_generator) {
402: _switch.compile(context, Expression.GET);
403: } else {
404: code.aload(l_key);
405: }
406: if (expression.isConstant()) {
407: context.constant(expression.eval(), false);
408: } else {
409: expression.compile(context, Expression.GET);
410: }
411: code.invokevirtual(equalsmethod);
412: caze.if_ne(code);
413: }
414: }
415:
416: Source isdefault = null;
417: if (_defaultCase != null) {
418: _defaultCase.go_to(code);
419: } else {
420: code.go_to(_endscope);
421: }
422:
423: iter = _cases.iterator();
424: while(iter.hasNext()) {
425: Case caze = (Case)iter.next();
426: caze.compile(context);
427: }
428:
429: if (!in_generator) {
430: code.endLocal(l_key);
431: }
432: }
433: _endscope.bind();
434: _startscope.bind(start);
435:
436: } }
|