001 /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
002 *
003 * The contents of this file are subject to the Netscape Public
004 * License Version 1.1 (the "License"); you may not use this file
005 * except in compliance with the License. You may obtain a copy of
006 * the License at http://www.mozilla.org/NPL/
007 *
008 * Software distributed under the License is distributed on an "AS
009 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
010 * implied. See the License for the specific language governing
011 * rights and limitations under the License.
012 *
013 * The Original Code is Rhino code, released
014 * May 6, 1999.
015 *
016 * The Initial Developer of the Original Code is Netscape
017 * Communications Corporation. Portions created by Netscape are
018 * Copyright (C) 1997-1999 Netscape Communications Corporation. All
019 * Rights Reserved.
020 *
021 * Contributor(s):
022 * Norris Boyd
023 * Roger Lawrence
024 * Mike McCabe
025 *
026 * Alternatively, the contents of this file may be used under the
027 * terms of the GNU Public License (the "GPL"), in which case the
028 * provisions of the GPL are applicable instead of those above.
029 * If you wish to allow use of your version of this file only
030 * under the terms of the GPL and not to allow others to use your
031 * version of this file under the NPL, indicate your decision by
032 * deleting the provisions above and replace them with the notice
033 * and other provisions required by the GPL. If you do not delete
034 * the provisions above, a recipient may use your version of this
035 * file under either the NPL or the GPL.
036 */
037 // Modified by Google
038
039 package com.google.gwt.dev.js.rhino;
040
041 /**
042 * This class implements the root of the intermediate representation.
043 */
044
045 public class Node implements Cloneable {
046
047 private static class NumberNode extends Node {
048
049 NumberNode(int number) {
050 super(TokenStream.NUMBER_INT);
051 this.number = number;
052 }
053
054 NumberNode(double number) {
055 super(TokenStream.NUMBER);
056 this.number = number;
057 }
058
059 @Override
060 public double getDouble() {
061 return this.number;
062 }
063
064 @Override
065 public boolean equals(Object o) {
066 return o instanceof NumberNode
067 && getDouble() == ((NumberNode) o).getDouble();
068 }
069
070 private double number;
071 }
072
073 private static class StringNode extends Node {
074
075 StringNode(int type, String str) {
076 super(type);
077 if (null == str) {
078 throw new IllegalArgumentException("StringNode: str is null");
079 }
080 this.str = str;
081 }
082
083 /** returns the string content.
084 * @return non null.
085 */
086 @Override
087 public String getString() {
088 return this.str;
089 }
090
091 /** sets the string content.
092 * @param str the new value. Non null.
093 */
094 @Override
095 public void setString(String str) {
096 if (null == str) {
097 throw new IllegalArgumentException("StringNode: str is null");
098 }
099 this.str = str;
100 }
101
102 @Override
103 public boolean equals(Object o) {
104 if (!(o instanceof StringNode)) { return false; }
105 return o instanceof StringNode
106 && this.str.equals(((StringNode) o).str);
107 }
108
109 private String str;
110 }
111
112 public Node(int nodeType) {
113 type = nodeType;
114 }
115
116 public Node(int nodeType, Node child) {
117 type = nodeType;
118 first = last = child;
119 child.next = null;
120 }
121
122 public Node(int nodeType, Node left, Node right) {
123 type = nodeType;
124 first = left;
125 last = right;
126 left.next = right;
127 right.next = null;
128 }
129
130 public Node(int nodeType, Node left, Node mid, Node right) {
131 type = nodeType;
132 first = left;
133 last = right;
134 left.next = mid;
135 mid.next = right;
136 right.next = null;
137 }
138
139 public Node(int nodeType, Node left, Node mid, Node mid2, Node right) {
140 type = nodeType;
141 first = left;
142 last = right;
143 left.next = mid;
144 mid.next = mid2;
145 mid2.next = right;
146 right.next = null;
147 }
148
149 public Node(int nodeType, Node[] children) {
150 this.type = nodeType;
151 if (children.length != 0) {
152 this.first = children[0];
153 this.last = children[children.length - 1];
154
155 for (int i = 1; i < children.length; i++) {
156 if (null != children[i - 1].next) {
157 // fail early on loops. implies same node in array twice
158 throw new IllegalArgumentException("duplicate child");
159 }
160 children[i - 1].next = children[i];
161 }
162 if (null != this.last.next) {
163 // fail early on loops. implies same node in array twice
164 throw new IllegalArgumentException("duplicate child");
165 }
166 }
167 }
168
169 public Node(int nodeType, int value) {
170 type = nodeType;
171 intDatum = value;
172 }
173
174 public Node(int nodeType, Node child, int value) {
175 this(nodeType, child);
176 intDatum = value;
177 }
178
179 public Node(int nodeType, Node left, Node right, int value) {
180 this(nodeType, left, right);
181 intDatum = value;
182 }
183
184 public Node(int nodeType, Node left, Node mid, Node right, int value) {
185 this(nodeType, left, mid, right);
186 intDatum = value;
187 }
188
189 public static Node newNumber(int number) {
190 return new NumberNode(number);
191 }
192
193 public static Node newNumber(double number) {
194 return new NumberNode(number);
195 }
196
197 public static Node newString(String str) {
198 return new StringNode(TokenStream.STRING, str);
199 }
200
201 public static Node newString(int type, String str) {
202 return new StringNode(type, str);
203 }
204
205 public int getType() {
206 return type;
207 }
208
209 public void setType(int type) {
210 this.type = type;
211 }
212
213 public int getIntDatum() {
214 return this.intDatum;
215 }
216
217 public Node getFirstChild() {
218 return first;
219 }
220
221 public Node getNext() {
222 return next;
223 }
224
225 public int getChildCount() {
226 int c = 0;
227 for (Node n = first; n != null; n = n.next)
228 c++;
229
230 return c;
231 }
232
233 public Node getLastSibling() {
234 Node n = this;
235 while (n.next != null) {
236 n = n.next;
237 }
238 return n;
239 }
240
241 public void addChildToBack(Node child) {
242 child.next = null;
243 if (last == null) {
244 first = last = child;
245 return;
246 }
247 last.next = child;
248 last = child;
249 }
250
251 public void addChildrenToBack(Node children) {
252 if (last != null) {
253 last.next = children;
254 }
255 last = children.getLastSibling();
256 if (first == null) {
257 first = children;
258 }
259 }
260
261 public static final int
262 TARGET_PROP = 1,
263 BREAK_PROP = 2,
264 CONTINUE_PROP = 3,
265 ENUM_PROP = 4,
266 FUNCTION_PROP = 5,
267 TEMP_PROP = 6,
268 LOCAL_PROP = 7,
269 CODEOFFSET_PROP = 8,
270 FIXUPS_PROP = 9,
271 VARS_PROP = 10,
272 USES_PROP = 11,
273 REGEXP_PROP = 12,
274 CASES_PROP = 13,
275 DEFAULT_PROP = 14,
276 CASEARRAY_PROP = 15,
277 SOURCENAME_PROP = 16,
278 SOURCE_PROP = 17,
279 TYPE_PROP = 18,
280 SPECIAL_PROP_PROP = 19,
281 LABEL_PROP = 20,
282 FINALLY_PROP = 21,
283 LOCALCOUNT_PROP = 22,
284 /*
285 the following properties are defined and manipulated by the
286 optimizer -
287 TARGETBLOCK_PROP - the block referenced by a branch node
288 VARIABLE_PROP - the variable referenced by a BIND or NAME node
289 LASTUSE_PROP - that variable node is the last reference before
290 a new def or the end of the block
291 ISNUMBER_PROP - this node generates code on Number children and
292 delivers a Number result (as opposed to Objects)
293 DIRECTCALL_PROP - this call node should emit code to test the function
294 object against the known class and call diret if it
295 matches.
296 */
297
298 TARGETBLOCK_PROP = 23,
299 VARIABLE_PROP = 24,
300 LASTUSE_PROP = 25,
301 ISNUMBER_PROP = 26,
302 DIRECTCALL_PROP = 27,
303
304 BASE_LINENO_PROP = 28,
305 END_LINENO_PROP = 29,
306 SPECIALCALL_PROP = 30,
307 DEBUGSOURCE_PROP = 31;
308
309 public static final int // this value of the ISNUMBER_PROP specifies
310 BOTH = 0, // which of the children are Number types
311 LEFT = 1,
312 RIGHT = 2;
313
314 private static final String propToString(int propType) {
315 switch (propType) {
316 case TARGET_PROP: return "target";
317 case BREAK_PROP: return "break";
318 case CONTINUE_PROP: return "continue";
319 case ENUM_PROP: return "enum";
320 case FUNCTION_PROP: return "function";
321 case TEMP_PROP: return "temp";
322 case LOCAL_PROP: return "local";
323 case CODEOFFSET_PROP: return "codeoffset";
324 case FIXUPS_PROP: return "fixups";
325 case VARS_PROP: return "vars";
326 case USES_PROP: return "uses";
327 case REGEXP_PROP: return "regexp";
328 case CASES_PROP: return "cases";
329 case DEFAULT_PROP: return "default";
330 case CASEARRAY_PROP: return "casearray";
331 case SOURCENAME_PROP: return "sourcename";
332 case SOURCE_PROP: return "source";
333 case TYPE_PROP: return "type";
334 case SPECIAL_PROP_PROP: return "special_prop";
335 case LABEL_PROP: return "label";
336 case FINALLY_PROP: return "finally";
337 case LOCALCOUNT_PROP: return "localcount";
338
339 case TARGETBLOCK_PROP: return "targetblock";
340 case VARIABLE_PROP: return "variable";
341 case LASTUSE_PROP: return "lastuse";
342 case ISNUMBER_PROP: return "isnumber";
343 case DIRECTCALL_PROP: return "directcall";
344
345 case BASE_LINENO_PROP: return "base_lineno";
346 case END_LINENO_PROP: return "end_lineno";
347 case SPECIALCALL_PROP: return "specialcall";
348 case DEBUGSOURCE_PROP: return "debugsource";
349
350 default: Context.codeBug();
351
352 }
353 return null;
354 }
355
356 public Object getProp(int propType) {
357 if (props == null)
358 return null;
359 return props.getObject(propType);
360 }
361
362 public void putProp(int propType, Object prop) {
363 if (prop == null) {
364 removeProp(propType);
365 }
366 else {
367 if (props == null) {
368 props = new UintMap(2);
369 }
370 props.put(propType, prop);
371 }
372 }
373
374 public void putIntProp(int propType, int prop) {
375 if (props == null)
376 props = new UintMap(2);
377 props.put(propType, prop);
378 }
379
380 public void removeProp(int propType) {
381 if (props != null) {
382 props.remove(propType);
383 }
384 }
385
386 public int getOperation() {
387 switch (type) {
388 case TokenStream.EQOP:
389 case TokenStream.RELOP:
390 case TokenStream.UNARYOP:
391 case TokenStream.PRIMARY:
392 return intDatum;
393 }
394 Context.codeBug();
395 return 0;
396 }
397
398 public int getLineno() {
399 if (hasLineno()) {
400 return intDatum;
401 }
402 return -1;
403 }
404
405 private boolean hasLineno() {
406 switch (type) {
407 case TokenStream.EXPRSTMT:
408 case TokenStream.BLOCK:
409 case TokenStream.VAR:
410 case TokenStream.WHILE:
411 case TokenStream.DO:
412 case TokenStream.SWITCH:
413 case TokenStream.CATCH:
414 case TokenStream.THROW:
415 case TokenStream.RETURN:
416 case TokenStream.BREAK:
417 case TokenStream.CONTINUE:
418 case TokenStream.WITH:
419 return true;
420 }
421 return false;
422 }
423
424 /** Can only be called when <tt>getType() == TokenStream.NUMBER</tt> */
425 public double getDouble() throws UnsupportedOperationException {
426 throw new UnsupportedOperationException(this + " is not a number node");
427 }
428
429 /** Can only be called when node has String context. */
430 public String getString() throws UnsupportedOperationException {
431 throw new UnsupportedOperationException(this + " is not a string node");
432 }
433
434 /** Can only be called when node has String context. */
435 public void setString(String s) throws UnsupportedOperationException {
436 throw new UnsupportedOperationException(this + " is not a string node");
437 }
438
439 /**
440 * Not usefully implemented.
441 */
442 @Override
443 public final int hashCode() {
444 assert false : "hashCode not designed";
445 return 42; // any arbitrary constant will do
446 }
447
448 @Override
449 public boolean equals(Object o) {
450 if (!(o instanceof Node)) { return false; }
451 return hasLineno()
452 || this.getIntDatum() == ((Node) o).getIntDatum();
453 }
454
455 @Override
456 public String toString() {
457 if (Context.printTrees) {
458 StringBuffer sb = new StringBuffer(TokenStream.tokenToName(type));
459 if (this instanceof StringNode) {
460 sb.append(' ');
461 sb.append(getString());
462 } else {
463 switch (type) {
464 case TokenStream.TARGET:
465 sb.append(' ');
466 sb.append(hashCode());
467 break;
468 case TokenStream.NUMBER_INT:
469 sb.append(' ');
470 sb.append((int) getDouble());
471 break;
472 case TokenStream.NUMBER:
473 sb.append(' ');
474 sb.append(getDouble());
475 break;
476 case TokenStream.FUNCTION:
477 sb.append(' ');
478 sb.append(first.getString());
479 break;
480 }
481 }
482 if (intDatum != -1) {
483 sb.append(' ');
484 sb.append(intDatum);
485 }
486 if (props == null)
487 return sb.toString();
488
489 int[] keys = props.getKeys();
490 for (int i = 0; i != keys.length; ++i) {
491 int key = keys[i];
492 sb.append(" [");
493 sb.append(propToString(key));
494 sb.append(": ");
495 switch (key) {
496 case FIXUPS_PROP : // can't add this as it recurses
497 sb.append("fixups property");
498 break;
499 case SOURCE_PROP : // can't add this as it has unprintables
500 sb.append("source property");
501 break;
502 case TARGETBLOCK_PROP : // can't add this as it recurses
503 sb.append("target block property");
504 break;
505 case LASTUSE_PROP : // can't add this as it is dull
506 sb.append("last use property");
507 break;
508 default :
509 Object obj = props.getObject(key);
510 if (obj != null) {
511 sb.append(obj.toString());
512 } else {
513 sb.append(props.getExistingInt(key));
514 }
515 break;
516 }
517 sb.append(']');
518 }
519 return sb.toString();
520 }
521 return null;
522 }
523
524 public void setIsSyntheticBlock(boolean val) {
525 isSyntheticBlock = val;
526 }
527
528 int type; // type of the node; TokenStream.NAME for example
529 Node next; // next sibling
530 private Node first; // first element of a linked list of children
531 private Node last; // last element of a linked list of children
532 private int intDatum = -1; // encapsulated int data; depends on type
533 private UintMap props;
534 private boolean isSyntheticBlock = false;
535 }