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 }