View Javadoc
1   /*
2    * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package jdk.nashorn.internal.ir;
27  
28  import jdk.nashorn.internal.ir.annotations.Immutable;
29  import jdk.nashorn.internal.ir.visitor.NodeVisitor;
30  
31  /**
32   * Node represents a var/let declaration.
33   */
34  @Immutable
35  public final class VarNode extends Statement implements Assignment<IdentNode> {
36      /** Var name. */
37      private final IdentNode name;
38  
39      /** Initialization expression. */
40      private final Expression init;
41  
42      /** Is this a var statement (as opposed to a "var" in a for loop statement) */
43      private final int flags;
44  
45      /** Flag that determines if this function node is a statement */
46      public static final int IS_STATEMENT = 1 << 0;
47  
48      /** Flag that determines if this is the last function declaration in a function
49       *  This is used to micro optimize the placement of return value assignments for
50       *  a program node */
51      public static final int IS_LAST_FUNCTION_DECLARATION = 1 << 1;
52  
53      /**
54       * Constructor
55       *
56       * @param lineNumber line number
57       * @param token      token
58       * @param finish     finish
59       * @param name       name of variable
60       * @param init       init node or null if just a declaration
61       */
62      public VarNode(final int lineNumber, final long token, final int finish, final IdentNode name, final Expression init) {
63          this(lineNumber, token, finish, name, init, IS_STATEMENT);
64      }
65  
66      private VarNode(final VarNode varNode, final IdentNode name, final Expression init, final int flags) {
67          super(varNode);
68          this.name = init == null ? name : name.setIsInitializedHere();
69          this.init = init;
70          this.flags = flags;
71      }
72  
73      /**
74       * Constructor
75       *
76       * @param lineNumber line number
77       * @param token      token
78       * @param finish     finish
79       * @param name       name of variable
80       * @param init       init node or null if just a declaration
81       * @param flags      flags
82       */
83      public VarNode(final int lineNumber, final long token, final int finish, final IdentNode name, final Expression init, final int flags) {
84          super(lineNumber, token, finish);
85  
86          this.name  = init == null ? name : name.setIsInitializedHere();
87          this.init  = init;
88          this.flags = flags;
89      }
90  
91      @Override
92      public boolean isAssignment() {
93          return hasInit();
94      }
95  
96      @Override
97      public IdentNode getAssignmentDest() {
98          return isAssignment() ? name : null;
99      }
100 
101     @Override
102     public VarNode setAssignmentDest(IdentNode n) {
103         return setName(n);
104     }
105 
106     @Override
107     public Expression getAssignmentSource() {
108         return isAssignment() ? getInit() : null;
109     }
110 
111     /**
112      * Does this variable declaration have an init value
113      * @return true if an init exists, false otherwise
114      */
115     public boolean hasInit() {
116         return init != null;
117     }
118 
119     /**
120      * Assist in IR navigation.
121      * @param visitor IR navigating visitor.
122      */
123     @Override
124     public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
125         if (visitor.enterVarNode(this)) {
126             final IdentNode  newName = (IdentNode)name.accept(visitor);
127             final Expression newInit = init == null ? null : (Expression)init.accept(visitor);
128             final VarNode    newThis;
129             if (name != newName || init != newInit) {
130                 newThis = new VarNode(this, newName, newInit, flags);
131             } else {
132                 newThis = this;
133             }
134             return visitor.leaveVarNode(newThis);
135         }
136         return this;
137     }
138 
139     @Override
140     public void toString(final StringBuilder sb) {
141         sb.append("var ");
142         name.toString(sb);
143 
144         if (init != null) {
145             sb.append(" = ");
146             init.toString(sb);
147         }
148     }
149 
150     /**
151      * If this is an assignment of the form {@code var x = init;}, get the init part.
152      * @return the expression to initialize the variable to, null if just a declaration
153      */
154     public Expression getInit() {
155         return init;
156     }
157 
158     /**
159      * Reset the initialization expression
160      * @param init new initialization expression
161      * @return a node equivalent to this one except for the requested change.
162      */
163     public VarNode setInit(final Expression init) {
164         if (this.init == init) {
165             return this;
166         }
167         return new VarNode(this, name, init, flags);
168     }
169 
170     /**
171      * Get the identifier for the variable
172      * @return IdentNode representing the variable being set or declared
173      */
174     public IdentNode getName() {
175         return name;
176     }
177 
178     /**
179      * Reset the identifier for this VarNode
180      * @param name new IdentNode representing the variable being set or declared
181      * @return a node equivalent to this one except for the requested change.
182      */
183     public VarNode setName(final IdentNode name) {
184         if (this.name == name) {
185             return this;
186         }
187         return new VarNode(this, name, init, flags);
188     }
189 
190     private VarNode setFlags(final int flags) {
191         if (this.flags == flags) {
192             return this;
193         }
194         return new VarNode(this, name, init, flags);
195     }
196 
197     /**
198      * Check if a flag is set for this var node
199      * @param flag flag
200      * @return true if flag is set
201      */
202     public boolean getFlag(final int flag) {
203         return (flags & flag) == flag;
204     }
205 
206     /**
207      * Set a flag for this var node
208      * @param flag flag
209      * @return new node if flags changed, same otherwise
210      */
211     public VarNode setFlag(final int flag) {
212         return setFlags(flags | flag);
213     }
214 
215     /**
216      * Returns true if this is a var statement (as opposed to a var initializer in a for loop).
217      * @return true if this is a var statement (as opposed to a var initializer in a for loop).
218      */
219     public boolean isStatement() {
220         return (flags & IS_STATEMENT) != 0;
221     }
222 
223     /**
224      * Returns true if this is a function declaration.
225      * @return true if this is a function declaration.
226      */
227     public boolean isFunctionDeclaration() {
228         return init instanceof FunctionNode && ((FunctionNode)init).isDeclared();
229     }
230 }