Used for code generation.
Used for code generation. During codegen, the AST is transformed into an Intermediate Representation (IR) in which loops, ifs, switches and other control-flow statements are rewritten as labeled jumps. If the parser is set to IDE-mode, the resulting AST will not contain any instances of this class.
Inheritance: AstNode
Example #1
0
		public virtual void SetJumpStatement(Rhino.Ast.Jump jumpStatement)
		{
			if (type != Token.BREAK && type != Token.CONTINUE)
			{
				CodeBug();
			}
			if (jumpStatement == null)
			{
				CodeBug();
			}
			if (this.jumpNode != null)
			{
				CodeBug();
			}
			//only once
			this.jumpNode = jumpStatement;
		}
Example #2
0
		private void VisitSwitch(Jump switchNode, Node child)
		{
			// See comments in IRFactory.createSwitch() for description
			// of SWITCH node
			GenerateExpression(child, switchNode);
			// save selector value
			short selector = GetNewWordLocal();
			cfw.AddAStore(selector);
			for (Jump caseNode = (Jump)child.GetNext(); caseNode != null; caseNode = (Jump)caseNode.GetNext())
			{
				if (caseNode.GetType() != Token.CASE)
				{
					throw Codegen.BadTree();
				}
				Node test = caseNode.GetFirstChild();
				GenerateExpression(test, caseNode);
				cfw.AddALoad(selector);
				AddScriptRuntimeInvoke("shallowEq", "(Ljava/lang/Object;" + "Ljava/lang/Object;" + ")Z");
				AddGoto(caseNode.target, ByteCode.IFNE);
			}
			ReleaseWordLocal(selector);
		}
Example #3
0
		public virtual void SetLoop(Rhino.Ast.Jump loop)
		{
			if (type != Token.LABEL)
			{
				CodeBug();
			}
			if (loop == null)
			{
				CodeBug();
			}
			if (jumpNode != null)
			{
				CodeBug();
			}
			//only once
			jumpNode = loop;
		}
Example #4
0
			/// <summary>Push a new try block onto the exception information stack.</summary>
			/// <remarks>Push a new try block onto the exception information stack.</remarks>
			/// <param name="node">
			/// an exception handling node (node.getType() ==
			/// Token.TRY)
			/// </param>
			internal virtual void PushExceptionInfo(Jump node)
			{
				Node fBlock = this._enclosing.GetFinallyAtTarget(node.GetFinally());
				BodyCodegen.ExceptionManager.ExceptionInfo ei = new BodyCodegen.ExceptionManager.ExceptionInfo(this, node, fBlock);
				this.exceptionInfo.Add(ei);
			}
Example #5
0
				internal ExceptionInfo(ExceptionManager _enclosing, Jump node, Node finallyBlock)
				{
					this._enclosing = _enclosing;
					this.node = node;
					this.finallyBlock = finallyBlock;
					this.handlerLabels = new int[BodyCodegen.EXCEPTION_MAX];
					this.exceptionStarts = new int[BodyCodegen.EXCEPTION_MAX];
					this.currentFinally = null;
				}
Example #6
0
		private void VisitGoto(Jump node, int type, Node child)
		{
			Node target = node.target;
			if (type == Token.IFEQ || type == Token.IFNE)
			{
				if (child == null)
				{
					throw Codegen.BadTree();
				}
				int targetLabel = GetTargetLabel(target);
				int fallThruLabel = cfw.AcquireLabel();
				if (type == Token.IFEQ)
				{
					GenerateIfJump(child, node, targetLabel, fallThruLabel);
				}
				else
				{
					GenerateIfJump(child, node, fallThruLabel, targetLabel);
				}
				cfw.MarkLabel(fallThruLabel);
			}
			else
			{
				if (type == Token.JSR)
				{
					if (isGenerator)
					{
						AddGotoWithReturn(target);
					}
					else
					{
						// This assumes that JSR is only ever used for finally
						InlineFinally(target);
					}
				}
				else
				{
					AddGoto(target, ByteCode.GOTO);
				}
			}
		}
Example #7
0
		private void VisitTryCatchFinally(Jump node, Node child)
		{
			// OPT we only need to do this if there are enclosed WITH
			// statements; could statically check and omit this if there aren't any.
			// XXX OPT Maybe instead do syntactic transforms to associate
			// each 'with' with a try/finally block that does the exitwith.
			short savedVariableObject = GetNewWordLocal();
			cfw.AddALoad(variableObjectLocal);
			cfw.AddAStore(savedVariableObject);
			int startLabel = cfw.AcquireLabel();
			cfw.MarkLabel(startLabel, (short)0);
			Node catchTarget = node.target;
			Node finallyTarget = node.GetFinally();
			int[] handlerLabels = new int[EXCEPTION_MAX];
			exceptionManager.PushExceptionInfo(node);
			if (catchTarget != null)
			{
				handlerLabels[JAVASCRIPT_EXCEPTION] = cfw.AcquireLabel();
				handlerLabels[EVALUATOR_EXCEPTION] = cfw.AcquireLabel();
				handlerLabels[ECMAERROR_EXCEPTION] = cfw.AcquireLabel();
				Context cx = Context.GetCurrentContext();
				if (cx != null && cx.HasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS))
				{
					handlerLabels[THROWABLE_EXCEPTION] = cfw.AcquireLabel();
				}
			}
			if (finallyTarget != null)
			{
				handlerLabels[FINALLY_EXCEPTION] = cfw.AcquireLabel();
			}
			exceptionManager.SetHandlers(handlerLabels, startLabel);
			// create a table for the equivalent of JSR returns
			if (isGenerator && finallyTarget != null)
			{
				BodyCodegen.FinallyReturnPoint ret = new BodyCodegen.FinallyReturnPoint();
				if (finallys == null)
				{
					finallys = new Dictionary<Node, BodyCodegen.FinallyReturnPoint>();
				}
				// add the finally target to hashtable
				finallys.Put(finallyTarget, ret);
				// add the finally node as well to the hash table
				finallys.Put(finallyTarget.GetNext(), ret);
			}
			while (child != null)
			{
				if (child == catchTarget)
				{
					int catchLabel = GetTargetLabel(catchTarget);
					exceptionManager.RemoveHandler(JAVASCRIPT_EXCEPTION, catchLabel);
					exceptionManager.RemoveHandler(EVALUATOR_EXCEPTION, catchLabel);
					exceptionManager.RemoveHandler(ECMAERROR_EXCEPTION, catchLabel);
					exceptionManager.RemoveHandler(THROWABLE_EXCEPTION, catchLabel);
				}
				GenerateStatement(child);
				child = child.GetNext();
			}
			// control flow skips the handlers
			int realEnd = cfw.AcquireLabel();
			cfw.Add(ByteCode.GOTO, realEnd);
			int exceptionLocal = GetLocalBlockRegister(node);
			// javascript handler; unwrap exception and GOTO to javascript
			// catch area.
			if (catchTarget != null)
			{
				// get the label to goto
				int catchLabel = catchTarget.LabelId();
				// If the function is a generator, then handlerLabels will consist
				// of zero labels. generateCatchBlock will create its own label
				// in this case. The extra parameter for the label is added for
				// the case of non-generator functions that inline finally blocks.
				GenerateCatchBlock(JAVASCRIPT_EXCEPTION, savedVariableObject, catchLabel, exceptionLocal, handlerLabels[JAVASCRIPT_EXCEPTION]);
				GenerateCatchBlock(EVALUATOR_EXCEPTION, savedVariableObject, catchLabel, exceptionLocal, handlerLabels[EVALUATOR_EXCEPTION]);
				GenerateCatchBlock(ECMAERROR_EXCEPTION, savedVariableObject, catchLabel, exceptionLocal, handlerLabels[ECMAERROR_EXCEPTION]);
				Context cx = Context.GetCurrentContext();
				if (cx != null && cx.HasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS))
				{
					GenerateCatchBlock(THROWABLE_EXCEPTION, savedVariableObject, catchLabel, exceptionLocal, handlerLabels[THROWABLE_EXCEPTION]);
				}
			}
			// finally handler; catch all exceptions, store to a local; JSR to
			// the finally, then re-throw.
			if (finallyTarget != null)
			{
				int finallyHandler = cfw.AcquireLabel();
				int finallyEnd = cfw.AcquireLabel();
				cfw.MarkHandler(finallyHandler);
				if (!isGenerator)
				{
					cfw.MarkLabel(handlerLabels[FINALLY_EXCEPTION]);
				}
				cfw.AddAStore(exceptionLocal);
				// reset the variable object local
				cfw.AddALoad(savedVariableObject);
				cfw.AddAStore(variableObjectLocal);
				// get the label to JSR to
				int finallyLabel = finallyTarget.LabelId();
				if (isGenerator)
				{
					AddGotoWithReturn(finallyTarget);
				}
				else
				{
					InlineFinally(finallyTarget, handlerLabels[FINALLY_EXCEPTION], finallyEnd);
				}
				// rethrow
				cfw.AddALoad(exceptionLocal);
				if (isGenerator)
				{
					cfw.Add(ByteCode.CHECKCAST, "java/lang/Throwable");
				}
				cfw.Add(ByteCode.ATHROW);
				cfw.MarkLabel(finallyEnd);
				// mark the handler
				if (isGenerator)
				{
					cfw.AddExceptionHandler(startLabel, finallyLabel, finallyHandler, null);
				}
			}
			// catch any
			ReleaseWordLocal(savedVariableObject);
			cfw.MarkLabel(realEnd);
			if (!isGenerator)
			{
				exceptionManager.PopExceptionInfo();
			}
		}
Example #8
0
		private Jump MakeJump(int type, Node target)
		{
			Jump n = new Jump(type);
			n.target = target;
			return n;
		}
Example #9
0
		/// <summary>Sets the statement to break to.</summary>
		/// <remarks>Sets the statement to break to.</remarks>
		/// <param name="target">the statement to break to</param>
		/// <exception cref="System.ArgumentException">
		/// if target is
		/// <code>null</code>
		/// </exception>
		public virtual void SetBreakTarget(Jump target)
		{
			AssertNotNull(target);
			this.target = target;
			SetJumpStatement(target);
		}
Example #10
0
		private Node CreateIf(Node cond, Node ifTrue, Node ifFalse, int lineno)
		{
			int condStatus = IsAlwaysDefinedBoolean(cond);
			if (condStatus == ALWAYS_TRUE_BOOLEAN)
			{
				return ifTrue;
			}
			else
			{
				if (condStatus == ALWAYS_FALSE_BOOLEAN)
				{
					if (ifFalse != null)
					{
						return ifFalse;
					}
					// Replace if (false) xxx by empty block
					return new Node(Token.BLOCK, lineno);
				}
			}
			Node result = new Node(Token.BLOCK, lineno);
			Node ifNotTarget = Node.NewTarget();
			Jump IFNE = new Jump(Token.IFNE, cond);
			IFNE.target = ifNotTarget;
			result.AddChildToBack(IFNE);
			result.AddChildrenToBack(ifTrue);
			if (ifFalse != null)
			{
				Node endTarget = Node.NewTarget();
				result.AddChildToBack(MakeJump(Token.GOTO, endTarget));
				result.AddChildToBack(ifNotTarget);
				result.AddChildrenToBack(ifFalse);
				result.AddChildToBack(endTarget);
			}
			else
			{
				result.AddChildToBack(ifNotTarget);
			}
			return result;
		}
Example #11
0
		/// <summary>
		/// Try/Catch/Finally
		/// The IRFactory tries to express as much as possible in the tree;
		/// the responsibilities remaining for Codegen are to add the Java
		/// handlers: (Either (but not both) of TARGET and FINALLY might not
		/// be defined)
		/// - a catch handler for javascript exceptions that unwraps the
		/// exception onto the stack and GOTOes to the catch target
		/// - a finally handler
		/// ...
		/// </summary>
		/// <remarks>
		/// Try/Catch/Finally
		/// The IRFactory tries to express as much as possible in the tree;
		/// the responsibilities remaining for Codegen are to add the Java
		/// handlers: (Either (but not both) of TARGET and FINALLY might not
		/// be defined)
		/// - a catch handler for javascript exceptions that unwraps the
		/// exception onto the stack and GOTOes to the catch target
		/// - a finally handler
		/// ... and a goto to GOTO around these handlers.
		/// </remarks>
		private Node CreateTryCatchFinally(Node tryBlock, Node catchBlocks, Node finallyBlock, int lineno)
		{
			bool hasFinally = (finallyBlock != null) && (finallyBlock.GetType() != Token.BLOCK || finallyBlock.HasChildren());
			// short circuit
			if (tryBlock.GetType() == Token.BLOCK && !tryBlock.HasChildren() && !hasFinally)
			{
				return tryBlock;
			}
			bool hasCatch = catchBlocks.HasChildren();
			// short circuit
			if (!hasFinally && !hasCatch)
			{
				// bc finally might be an empty block...
				return tryBlock;
			}
			Node handlerBlock = new Node(Token.LOCAL_BLOCK);
			Jump pn = new Jump(Token.TRY, tryBlock, lineno);
			pn.PutProp(Node.LOCAL_BLOCK_PROP, handlerBlock);
			if (hasCatch)
			{
				// jump around catch code
				Node endCatch = Node.NewTarget();
				pn.AddChildToBack(MakeJump(Token.GOTO, endCatch));
				// make a TARGET for the catch that the tcf node knows about
				Node catchTarget = Node.NewTarget();
				pn.target = catchTarget;
				// mark it
				pn.AddChildToBack(catchTarget);
				//
				//  Given
				//
				//   try {
				//       tryBlock;
				//   } catch (e if condition1) {
				//       something1;
				//   ...
				//
				//   } catch (e if conditionN) {
				//       somethingN;
				//   } catch (e) {
				//       somethingDefault;
				//   }
				//
				//  rewrite as
				//
				//   try {
				//       tryBlock;
				//       goto after_catch:
				//   } catch (x) {
				//       with (newCatchScope(e, x)) {
				//           if (condition1) {
				//               something1;
				//               goto after_catch;
				//           }
				//       }
				//   ...
				//       with (newCatchScope(e, x)) {
				//           if (conditionN) {
				//               somethingN;
				//               goto after_catch;
				//           }
				//       }
				//       with (newCatchScope(e, x)) {
				//           somethingDefault;
				//           goto after_catch;
				//       }
				//   }
				// after_catch:
				//
				// If there is no default catch, then the last with block
				// arround  "somethingDefault;" is replaced by "rethrow;"
				// It is assumed that catch handler generation will store
				// exeception object in handlerBlock register
				// Block with local for exception scope objects
				Node catchScopeBlock = new Node(Token.LOCAL_BLOCK);
				// expects catchblocks children to be (cond block) pairs.
				Node cb = catchBlocks.GetFirstChild();
				bool hasDefault = false;
				int scopeIndex = 0;
				while (cb != null)
				{
					int catchLineNo = cb.GetLineno();
					Node name = cb.GetFirstChild();
					Node cond = name.GetNext();
					Node catchStatement = cond.GetNext();
					cb.RemoveChild(name);
					cb.RemoveChild(cond);
					cb.RemoveChild(catchStatement);
					// Add goto to the catch statement to jump out of catch
					// but prefix it with LEAVEWITH since try..catch produces
					// "with"code in order to limit the scope of the exception
					// object.
					catchStatement.AddChildToBack(new Node(Token.LEAVEWITH));
					catchStatement.AddChildToBack(MakeJump(Token.GOTO, endCatch));
					// Create condition "if" when present
					Node condStmt;
					if (cond.GetType() == Token.EMPTY)
					{
						condStmt = catchStatement;
						hasDefault = true;
					}
					else
					{
						condStmt = CreateIf(cond, catchStatement, null, catchLineNo);
					}
					// Generate code to create the scope object and store
					// it in catchScopeBlock register
					Node catchScope = new Node(Token.CATCH_SCOPE, name, CreateUseLocal(handlerBlock));
					catchScope.PutProp(Node.LOCAL_BLOCK_PROP, catchScopeBlock);
					catchScope.PutIntProp(Node.CATCH_SCOPE_PROP, scopeIndex);
					catchScopeBlock.AddChildToBack(catchScope);
					// Add with statement based on catch scope object
					catchScopeBlock.AddChildToBack(CreateWith(CreateUseLocal(catchScopeBlock), condStmt, catchLineNo));
					// move to next cb
					cb = cb.GetNext();
					++scopeIndex;
				}
				pn.AddChildToBack(catchScopeBlock);
				if (!hasDefault)
				{
					// Generate code to rethrow if no catch clause was executed
					Node rethrow = new Node(Token.RETHROW);
					rethrow.PutProp(Node.LOCAL_BLOCK_PROP, handlerBlock);
					pn.AddChildToBack(rethrow);
				}
				pn.AddChildToBack(endCatch);
			}
			if (hasFinally)
			{
				Node finallyTarget = Node.NewTarget();
				pn.SetFinally(finallyTarget);
				// add jsr finally to the try block
				pn.AddChildToBack(MakeJump(Token.JSR, finallyTarget));
				// jump around finally code
				Node finallyEnd = Node.NewTarget();
				pn.AddChildToBack(MakeJump(Token.GOTO, finallyEnd));
				pn.AddChildToBack(finallyTarget);
				Node fBlock = new Node(Token.FINALLY, finallyBlock);
				fBlock.PutProp(Node.LOCAL_BLOCK_PROP, handlerBlock);
				pn.AddChildToBack(fBlock);
				pn.AddChildToBack(finallyEnd);
			}
			handlerBlock.AddChildToBack(pn);
			return handlerBlock;
		}
Example #12
0
		private Node CreateLoop(Jump loop, int loopType, Node body, Node cond, Node init, Node incr)
		{
			Node bodyTarget = Node.NewTarget();
			Node condTarget = Node.NewTarget();
			if (loopType == LOOP_FOR && cond.GetType() == Token.EMPTY)
			{
				cond = new Node(Token.TRUE);
			}
			Jump IFEQ = new Jump(Token.IFEQ, cond);
			IFEQ.target = bodyTarget;
			Node breakTarget = Node.NewTarget();
			loop.AddChildToBack(bodyTarget);
			loop.AddChildrenToBack(body);
			if (loopType == LOOP_WHILE || loopType == LOOP_FOR)
			{
				// propagate lineno to condition
				loop.AddChildrenToBack(new Node(Token.EMPTY, loop.GetLineno()));
			}
			loop.AddChildToBack(condTarget);
			loop.AddChildToBack(IFEQ);
			loop.AddChildToBack(breakTarget);
			loop.target = breakTarget;
			Node continueTarget = condTarget;
			if (loopType == LOOP_WHILE || loopType == LOOP_FOR)
			{
				// Just add a GOTO to the condition in the do..while
				loop.AddChildToFront(MakeJump(Token.GOTO, condTarget));
				if (loopType == LOOP_FOR)
				{
					int initType = init.GetType();
					if (initType != Token.EMPTY)
					{
						if (initType != Token.VAR && initType != Token.LET)
						{
							init = new Node(Token.EXPR_VOID, init);
						}
						loop.AddChildToFront(init);
					}
					Node incrTarget = Node.NewTarget();
					loop.AddChildAfter(incrTarget, body);
					if (incr.GetType() != Token.EMPTY)
					{
						incr = new Node(Token.EXPR_VOID, incr);
						loop.AddChildAfter(incr, incrTarget);
					}
					continueTarget = incrTarget;
				}
			}
			loop.SetContinue(continueTarget);
			return loop;
		}
Example #13
0
		/// <summary>If caseExpression argument is null it indicates a default label.</summary>
		/// <remarks>If caseExpression argument is null it indicates a default label.</remarks>
		private void AddSwitchCase(Node switchBlock, Node caseExpression, Node statements)
		{
			if (switchBlock.GetType() != Token.BLOCK)
			{
				throw Kit.CodeBug();
			}
			Jump switchNode = (Jump)switchBlock.GetFirstChild();
			if (switchNode.GetType() != Token.SWITCH)
			{
				throw Kit.CodeBug();
			}
			Node gotoTarget = Node.NewTarget();
			if (caseExpression != null)
			{
				Jump caseNode = new Jump(Token.CASE, caseExpression);
				caseNode.target = gotoTarget;
				switchNode.AddChildToBack(caseNode);
			}
			else
			{
				switchNode.SetDefault(gotoTarget);
			}
			switchBlock.AddChildToBack(gotoTarget);
			switchBlock.AddChildToBack(statements);
		}
Example #14
0
		private void TransformCompilationUnit_r(ScriptNode tree, Node parent, Scope scope, bool createScopeObjects, bool inStrictMode)
		{
			Node node = null;
			for (; ; )
			{
				Node previous = null;
				if (node == null)
				{
					node = parent.GetFirstChild();
				}
				else
				{
					previous = node;
					node = node.GetNext();
				}
				if (node == null)
				{
					break;
				}
				int type = node.GetType();
				if (createScopeObjects && (type == Token.BLOCK || type == Token.LOOP || type == Token.ARRAYCOMP) && (node is Scope))
				{
					Scope newScope = (Scope)node;
					if (newScope.GetSymbolTable() != null)
					{
						// transform to let statement so we get a with statement
						// created to contain scoped let variables
						Node let = new Node(type == Token.ARRAYCOMP ? Token.LETEXPR : Token.LET);
						Node innerLet = new Node(Token.LET);
						let.AddChildToBack(innerLet);
						foreach (string name in newScope.GetSymbolTable().Keys)
						{
							innerLet.AddChildToBack(Node.NewString(Token.NAME, name));
						}
						newScope.SetSymbolTable(null);
						// so we don't transform again
						Node oldNode = node;
						node = ReplaceCurrent(parent, previous, node, let);
						type = node.GetType();
						let.AddChildToBack(oldNode);
					}
				}
				switch (type)
				{
					case Token.LABEL:
					case Token.SWITCH:
					case Token.LOOP:
					{
						loops.Push(node);
						loopEnds.Push(((Jump)node).target);
						break;
					}

					case Token.WITH:
					{
						loops.Push(node);
						Node leave = node.GetNext();
						if (leave.GetType() != Token.LEAVEWITH)
						{
							Kit.CodeBug();
						}
						loopEnds.Push(leave);
						break;
					}

					case Token.TRY:
					{
						Jump jump = (Jump)node;
						Node finallytarget = jump.GetFinally();
						if (finallytarget != null)
						{
							hasFinally = true;
							loops.Push(node);
							loopEnds.Push(finallytarget);
						}
						break;
					}

					case Token.TARGET:
					case Token.LEAVEWITH:
					{
						if (!loopEnds.IsEmpty() && loopEnds.Peek() == node)
						{
							loopEnds.Pop();
							loops.Pop();
						}
						break;
					}

					case Token.YIELD:
					{
						((FunctionNode)tree).AddResumptionPoint(node);
						break;
					}

					case Token.RETURN:
					{
						bool isGenerator = tree.GetType() == Token.FUNCTION && ((FunctionNode)tree).IsGenerator();
						if (isGenerator)
						{
							node.PutIntProp(Node.GENERATOR_END_PROP, 1);
						}
						if (!hasFinally)
						{
							break;
						}
						// skip the whole mess.
						Node unwindBlock = null;
						for (int i = loops.Size() - 1; i >= 0; i--)
						{
							Node n = (Node)loops.Get(i);
							int elemtype = n.GetType();
							if (elemtype == Token.TRY || elemtype == Token.WITH)
							{
								Node unwind;
								if (elemtype == Token.TRY)
								{
									Jump jsrnode = new Jump(Token.JSR);
									Node jsrtarget = ((Jump)n).GetFinally();
									jsrnode.target = jsrtarget;
									unwind = jsrnode;
								}
								else
								{
									unwind = new Node(Token.LEAVEWITH);
								}
								if (unwindBlock == null)
								{
									unwindBlock = new Node(Token.BLOCK, node.GetLineno());
								}
								unwindBlock.AddChildToBack(unwind);
							}
						}
						if (unwindBlock != null)
						{
							Node returnNode = node;
							Node returnExpr = returnNode.GetFirstChild();
							node = ReplaceCurrent(parent, previous, node, unwindBlock);
							if (returnExpr == null || isGenerator)
							{
								unwindBlock.AddChildToBack(returnNode);
							}
							else
							{
								Node store = new Node(Token.EXPR_RESULT, returnExpr);
								unwindBlock.AddChildToFront(store);
								returnNode = new Node(Token.RETURN_RESULT);
								unwindBlock.AddChildToBack(returnNode);
								// transform return expression
								TransformCompilationUnit_r(tree, store, scope, createScopeObjects, inStrictMode);
							}
							// skip transformCompilationUnit_r to avoid infinite loop
							goto siblingLoop_continue;
						}
						break;
					}

					case Token.BREAK:
					case Token.CONTINUE:
					{
						Jump jump = (Jump)node;
						Jump jumpStatement = jump.GetJumpStatement();
						if (jumpStatement == null)
						{
							Kit.CodeBug();
						}
						for (int i = loops.Size(); ; )
						{
							if (i == 0)
							{
								// Parser/IRFactory ensure that break/continue
								// always has a jump statement associated with it
								// which should be found
								throw Kit.CodeBug();
							}
							--i;
							Node n = (Node)loops.Get(i);
							if (n == jumpStatement)
							{
								break;
							}
							int elemtype = n.GetType();
							if (elemtype == Token.WITH)
							{
								Node leave = new Node(Token.LEAVEWITH);
								previous = AddBeforeCurrent(parent, previous, node, leave);
							}
							else
							{
								if (elemtype == Token.TRY)
								{
									Jump tryNode = (Jump)n;
									Jump jsrFinally = new Jump(Token.JSR);
									jsrFinally.target = tryNode.GetFinally();
									previous = AddBeforeCurrent(parent, previous, node, jsrFinally);
								}
							}
						}
						if (type == Token.BREAK)
						{
							jump.target = jumpStatement.target;
						}
						else
						{
							jump.target = jumpStatement.GetContinue();
						}
						jump.SetType(Token.GOTO);
						break;
					}

					case Token.CALL:
					{
						VisitCall(node, tree);
						break;
					}

					case Token.NEW:
					{
						VisitNew(node, tree);
						break;
					}

					case Token.LETEXPR:
					case Token.LET:
					{
						Node child = node.GetFirstChild();
						if (child.GetType() == Token.LET)
						{
							// We have a let statement or expression rather than a
							// let declaration
							bool createWith = tree.GetType() != Token.FUNCTION || ((FunctionNode)tree).RequiresActivation();
							node = VisitLet(createWith, parent, previous, node);
							break;
						}
						goto case Token.CONST;
					}

					case Token.CONST:
					case Token.VAR:
					{
						// fall through to process let declaration...
						Node result = new Node(Token.BLOCK);
						for (Node cursor = node.GetFirstChild(); cursor != null; )
						{
							// Move cursor to next before createAssignment gets chance
							// to change n.next
							Node n = cursor;
							cursor = cursor.GetNext();
							if (n.GetType() == Token.NAME)
							{
								if (!n.HasChildren())
								{
									continue;
								}
								Node init = n.GetFirstChild();
								n.RemoveChild(init);
								n.SetType(Token.BINDNAME);
								n = new Node(type == Token.CONST ? Token.SETCONST : Token.SETNAME, n, init);
							}
							else
							{
								// May be a destructuring assignment already transformed
								// to a LETEXPR
								if (n.GetType() != Token.LETEXPR)
								{
									throw Kit.CodeBug();
								}
							}
							Node pop = new Node(Token.EXPR_VOID, n, node.GetLineno());
							result.AddChildToBack(pop);
						}
						node = ReplaceCurrent(parent, previous, node, result);
						break;
					}

					case Token.TYPEOFNAME:
					{
						Scope defining = scope.GetDefiningScope(node.GetString());
						if (defining != null)
						{
							node.SetScope(defining);
						}
						break;
					}

					case Token.TYPEOF:
					case Token.IFNE:
					{
						Node child = node.GetFirstChild();
						if (type == Token.IFNE)
						{
							while (child.GetType() == Token.NOT)
							{
								child = child.GetFirstChild();
							}
							if (child.GetType() == Token.EQ || child.GetType() == Token.NE)
							{
								Node first = child.GetFirstChild();
								Node last = child.GetLastChild();
								if (first.GetType() == Token.NAME && first.GetString().Equals("undefined"))
								{
									child = last;
								}
								else
								{
									if (last.GetType() == Token.NAME && last.GetString().Equals("undefined"))
									{
										child = first;
									}
								}
							}
						}
						if (child.GetType() == Token.GETPROP)
						{
							child.SetType(Token.GETPROPNOWARN);
						}
						break;
					}

					case Token.SETNAME:
					{
						if (inStrictMode)
						{
							node.SetType(Token.STRICT_SETNAME);
						}
						goto case Token.NAME;
					}

					case Token.NAME:
					case Token.SETCONST:
					case Token.DELPROP:
					{
						// Turn name to var for faster access if possible
						if (createScopeObjects)
						{
							break;
						}
						Node nameSource;
						if (type == Token.NAME)
						{
							nameSource = node;
						}
						else
						{
							nameSource = node.GetFirstChild();
							if (nameSource.GetType() != Token.BINDNAME)
							{
								if (type == Token.DELPROP)
								{
									break;
								}
								throw Kit.CodeBug();
							}
						}
						if (nameSource.GetScope() != null)
						{
							break;
						}
						// already have a scope set
						string name = nameSource.GetString();
						Scope defining = scope.GetDefiningScope(name);
						if (defining != null)
						{
							nameSource.SetScope(defining);
							if (type == Token.NAME)
							{
								node.SetType(Token.GETVAR);
							}
							else
							{
								if (type == Token.SETNAME || type == Token.STRICT_SETNAME)
								{
									node.SetType(Token.SETVAR);
									nameSource.SetType(Token.STRING);
								}
								else
								{
									if (type == Token.SETCONST)
									{
										node.SetType(Token.SETCONSTVAR);
										nameSource.SetType(Token.STRING);
									}
									else
									{
										if (type == Token.DELPROP)
										{
											// Local variables are by definition permanent
											Node n = new Node(Token.FALSE);
											node = ReplaceCurrent(parent, previous, node, n);
										}
										else
										{
											throw Kit.CodeBug();
										}
									}
								}
							}
						}
						break;
					}
				}
				TransformCompilationUnit_r(tree, node, node is Scope ? (Scope)node : scope, createScopeObjects, inStrictMode);
siblingLoop_continue: ;
			}
siblingLoop_break: ;
		}