1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sw=4 et tw=99:
3 : *
4 : * ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
7 : * The contents of this file are subject to the Mozilla Public License Version
8 : * 1.1 (the "License"); you may not use this file except in compliance with
9 : * the License. You may obtain a copy of the License at
10 : * http://www.mozilla.org/MPL/
11 : *
12 : * Software distributed under the License is distributed on an "AS IS" basis,
13 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 : * for the specific language governing rights and limitations under the
15 : * License.
16 : *
17 : * The Original Code is Mozilla Communicator client code, released
18 : * March 31, 1998.
19 : *
20 : * The Initial Developer of the Original Code is
21 : * Netscape Communications Corporation.
22 : * Portions created by the Initial Developer are Copyright (C) 1998
23 : * the Initial Developer. All Rights Reserved.
24 : *
25 : * Contributor(s):
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either of the GNU General Public License Version 2 or later (the "GPL"),
29 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : #include "frontend/SemanticAnalysis.h"
42 :
43 : #include "jsfun.h"
44 :
45 : #include "frontend/BytecodeEmitter.h"
46 : #include "frontend/Parser.h"
47 :
48 : #include "jsobjinlines.h"
49 : #include "jsfuninlines.h"
50 :
51 : using namespace js;
52 : using namespace js::frontend;
53 :
54 : /*
55 : * Walk the function box list at |*funboxHead|, removing boxes for deleted
56 : * functions and cleaning up method lists. We do this once, before
57 : * performing function analysis, to avoid traversing possibly long function
58 : * lists repeatedly when recycling nodes.
59 : *
60 : * There are actually three possible states for function boxes and their
61 : * nodes:
62 : *
63 : * - Live: funbox->node points to the node, and funbox->node->pn_funbox
64 : * points back to the funbox.
65 : *
66 : * - Recycled: funbox->node points to the node, but funbox->node->pn_funbox
67 : * is NULL. When a function node is part of a tree that gets recycled, we
68 : * must avoid corrupting any method list the node is on, so we leave the
69 : * function node unrecycled until we call CleanFunctionList. At recycle
70 : * time, we clear such nodes' pn_funbox pointers to indicate that they
71 : * are deleted and should be recycled once we get here.
72 : *
73 : * - Mutated: funbox->node is NULL; the contents of the node itself could
74 : * be anything. When we mutate a function node into some other kind of
75 : * node, we lose all indication that the node was ever part of the
76 : * function box tree; it could later be recycled, reallocated, and turned
77 : * into anything at all. (Fortunately, method list members never get
78 : * mutated, so we don't have to worry about that case.)
79 : * ParseNodeAllocator::prepareNodeForMutation clears the node's function
80 : * box's node pointer, disconnecting it entirely from the function box tree,
81 : * and marking the function box to be trimmed out.
82 : */
83 : static void
84 474930 : CleanFunctionList(ParseNodeAllocator *allocator, FunctionBox **funboxHead)
85 : {
86 474930 : FunctionBox **link = funboxHead;
87 783554 : while (FunctionBox *box = *link) {
88 154312 : if (!box->node) {
89 : /*
90 : * This funbox's parse node was mutated into something else. Drop the box,
91 : * and stay at the same link.
92 : */
93 0 : *link = box->siblings;
94 154312 : } else if (!box->node->pn_funbox) {
95 : /*
96 : * This funbox's parse node is ready to be recycled. Drop the box, recycle
97 : * the node, and stay at the same link.
98 : */
99 0 : *link = box->siblings;
100 0 : allocator->freeNode(box->node);
101 : } else {
102 : /* The function is still live. */
103 :
104 : /* First, remove nodes for deleted functions from our methods list. */
105 : {
106 154312 : ParseNode **methodLink = &box->methods;
107 154690 : while (ParseNode *method = *methodLink) {
108 : /* Method nodes are never rewritten in place to be other kinds of nodes. */
109 189 : JS_ASSERT(method->isArity(PN_FUNC));
110 189 : if (!method->pn_funbox) {
111 : /* Deleted: drop the node, and stay on this link. */
112 0 : *methodLink = method->pn_link;
113 : } else {
114 : /* Live: keep the node, and move to the next link. */
115 189 : methodLink = &method->pn_link;
116 : }
117 : }
118 : }
119 :
120 : /* Second, remove boxes for deleted functions from our kids list. */
121 154312 : CleanFunctionList(allocator, &box->kids);
122 :
123 : /* Keep the box on the list, and move to the next link. */
124 154312 : link = &box->siblings;
125 : }
126 : }
127 474930 : }
128 :
129 : static void
130 11315 : FlagHeavyweights(Definition *dn, FunctionBox *funbox, uint32_t *tcflags)
131 : {
132 11315 : unsigned dnLevel = dn->frameLevel();
133 :
134 24043 : while ((funbox = funbox->parent) != NULL) {
135 : /*
136 : * Notice that funbox->level is the static level of the definition or
137 : * expression of the function parsed into funbox, not the static level
138 : * of its body. Therefore we must add 1 to match dn's level to find the
139 : * funbox whose body contains the dn definition.
140 : */
141 12448 : if (funbox->level + 1U == dnLevel || (dnLevel == 0 && dn->isLet())) {
142 11035 : funbox->tcflags |= TCF_FUN_HEAVYWEIGHT;
143 11035 : break;
144 : }
145 : }
146 :
147 11315 : if (!funbox && (*tcflags & TCF_IN_FUNCTION))
148 82 : *tcflags |= TCF_FUN_HEAVYWEIGHT;
149 11315 : }
150 :
151 : static void
152 149767 : SetFunctionKinds(FunctionBox *funbox, uint32_t *tcflags, bool isDirectEval)
153 : {
154 304079 : for (; funbox; funbox = funbox->siblings) {
155 154312 : ParseNode *fn = funbox->node;
156 154312 : ParseNode *pn = fn->pn_body;
157 :
158 154312 : if (funbox->kids)
159 8412 : SetFunctionKinds(funbox->kids, tcflags, isDirectEval);
160 :
161 154312 : JSFunction *fun = funbox->function();
162 :
163 154312 : JS_ASSERT(fun->kind() == JSFUN_INTERPRETED);
164 :
165 154312 : if (funbox->tcflags & TCF_FUN_HEAVYWEIGHT) {
166 : /* nothing to do */
167 146043 : } else if (isDirectEval || funbox->inAnyDynamicScope()) {
168 : /*
169 : * Either we are in a with-block or a function scope that is
170 : * subject to direct eval; or we are compiling strict direct eval
171 : * code.
172 : *
173 : * In either case, fun may reference names that are not bound but
174 : * are not necessarily global either. (In the strict direct eval
175 : * case, we could bind them, but currently do not bother; see
176 : * the comment about strict mode code in BindTopLevelVar.)
177 : */
178 10530 : JS_ASSERT(!fun->isNullClosure());
179 : } else {
180 135513 : bool hasUpvars = false;
181 :
182 135513 : if (pn->isKind(PNK_UPVARS)) {
183 45626 : AtomDefnMapPtr upvars = pn->pn_names;
184 45626 : JS_ASSERT(!upvars->empty());
185 :
186 : /* Determine whether the this function contains upvars. */
187 104592 : for (AtomDefnRange r = upvars->all(); !r.empty(); r.popFront()) {
188 63476 : if (!r.front().value()->resolve()->isFreeVar()) {
189 4510 : hasUpvars = true;
190 4510 : break;
191 : }
192 : }
193 : }
194 :
195 135513 : if (!hasUpvars) {
196 : /* No lexical dependencies => null closure, for best performance. */
197 131003 : fun->setKind(JSFUN_NULL_CLOSURE);
198 : }
199 : }
200 :
201 154312 : if (fun->kind() == JSFUN_INTERPRETED && pn->isKind(PNK_UPVARS)) {
202 : /*
203 : * We loop again over all upvars, and for each non-free upvar,
204 : * ensure that its containing function has been flagged as
205 : * heavyweight.
206 : *
207 : * The emitter must see TCF_FUN_HEAVYWEIGHT accurately before
208 : * generating any code for a tree of nested functions.
209 : */
210 19521 : AtomDefnMapPtr upvars = pn->pn_names;
211 19521 : JS_ASSERT(!upvars->empty());
212 :
213 55109 : for (AtomDefnRange r = upvars->all(); !r.empty(); r.popFront()) {
214 35588 : Definition *defn = r.front().value();
215 35588 : Definition *lexdep = defn->resolve();
216 35588 : if (!lexdep->isFreeVar())
217 11315 : FlagHeavyweights(lexdep, funbox, tcflags);
218 : }
219 : }
220 : }
221 149767 : }
222 :
223 : /*
224 : * Walk the FunctionBox tree looking for functions whose call objects may
225 : * acquire new bindings as they execute: non-strict functions that call eval,
226 : * and functions that contain function statements (definitions not appearing
227 : * within the top statement list, which don't take effect unless they are
228 : * evaluated). Such call objects may acquire bindings that shadow variables
229 : * defined in enclosing scopes, so any enclosed functions must have their
230 : * bindings' extensibleParents flags set, and enclosed compiler-created blocks
231 : * must have their OWN_SHAPE flags set; the comments for
232 : * js::Bindings::extensibleParents explain why.
233 : */
234 : static bool
235 149767 : MarkExtensibleScopeDescendants(JSContext *context, FunctionBox *funbox, bool hasExtensibleParent)
236 : {
237 304079 : for (; funbox; funbox = funbox->siblings) {
238 : /*
239 : * It would be nice to use fun->kind() here to recognize functions
240 : * that will never consult their parent chains, and thus don't need
241 : * their 'extensible parents' flag set. Filed as bug 619750.
242 : */
243 :
244 154312 : JS_ASSERT(!funbox->bindings.extensibleParents());
245 154312 : if (hasExtensibleParent) {
246 559 : if (!funbox->bindings.setExtensibleParents(context))
247 0 : return false;
248 : }
249 :
250 154312 : if (funbox->kids) {
251 8412 : if (!MarkExtensibleScopeDescendants(context, funbox->kids,
252 8412 : hasExtensibleParent || funbox->scopeIsExtensible())) {
253 0 : return false;
254 : }
255 : }
256 : }
257 :
258 149767 : return true;
259 : }
260 :
261 : bool
262 320618 : frontend::AnalyzeFunctions(TreeContext *tc)
263 : {
264 320618 : CleanFunctionList(&tc->parser->allocator, &tc->functionList);
265 320618 : if (!tc->functionList)
266 179263 : return true;
267 141355 : if (!MarkExtensibleScopeDescendants(tc->parser->context, tc->functionList, false))
268 0 : return false;
269 141355 : bool isDirectEval = !!tc->parser->callerFrame;
270 141355 : SetFunctionKinds(tc->functionList, &tc->flags, isDirectEval);
271 141355 : return true;
272 : }
|