js/src/frontend/NameFunctions.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
michael@0 2 * vim: set ts=8 sts=4 et sw=4 tw=99:
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "frontend/NameFunctions.h"
michael@0 8
michael@0 9 #include "jsfun.h"
michael@0 10 #include "jsprf.h"
michael@0 11
michael@0 12 #include "frontend/BytecodeCompiler.h"
michael@0 13 #include "frontend/ParseNode.h"
michael@0 14 #include "frontend/SharedContext.h"
michael@0 15 #include "vm/StringBuffer.h"
michael@0 16
michael@0 17 using namespace js;
michael@0 18 using namespace js::frontend;
michael@0 19
michael@0 20 namespace {
michael@0 21
michael@0 22 class NameResolver
michael@0 23 {
michael@0 24 static const size_t MaxParents = 100;
michael@0 25
michael@0 26 ExclusiveContext *cx;
michael@0 27 size_t nparents; /* number of parents in the parents array */
michael@0 28 ParseNode *parents[MaxParents]; /* history of ParseNodes we've been looking at */
michael@0 29 StringBuffer *buf; /* when resolving, buffer to append to */
michael@0 30
michael@0 31 /* Test whether a ParseNode represents a function invocation */
michael@0 32 bool call(ParseNode *pn) {
michael@0 33 return pn && pn->isKind(PNK_CALL);
michael@0 34 }
michael@0 35
michael@0 36 /*
michael@0 37 * Append a reference to a property named |name| to |buf|. If |name| is
michael@0 38 * a proper identifier name, then we append '.name'; otherwise, we
michael@0 39 * append '["name"]'.
michael@0 40 *
michael@0 41 * Note that we need the IsIdentifier check for atoms from both
michael@0 42 * PNK_NAME nodes and PNK_STRING nodes: given code like a["b c"], the
michael@0 43 * front end will produce a PNK_DOT with a PNK_NAME child whose name
michael@0 44 * contains spaces.
michael@0 45 */
michael@0 46 bool appendPropertyReference(JSAtom *name) {
michael@0 47 if (IsIdentifier(name))
michael@0 48 return buf->append(".") && buf->append(name);
michael@0 49
michael@0 50 /* Quote the string as needed. */
michael@0 51 JSString *source = js_QuoteString(cx, name, '"');
michael@0 52 return source && buf->append("[") && buf->append(source) && buf->append("]");
michael@0 53 }
michael@0 54
michael@0 55 /* Append a number to buf. */
michael@0 56 bool appendNumber(double n) {
michael@0 57 char number[30];
michael@0 58 int digits = JS_snprintf(number, sizeof(number), "%g", n);
michael@0 59 return buf->appendInflated(number, digits);
michael@0 60 }
michael@0 61
michael@0 62 /* Append "[<n>]" to buf, referencing a property named by a numeric literal. */
michael@0 63 bool appendNumericPropertyReference(double n) {
michael@0 64 return buf->append("[") && appendNumber(n) && buf->append("]");
michael@0 65 }
michael@0 66
michael@0 67 /*
michael@0 68 * Walk over the given ParseNode, converting it to a stringified name that
michael@0 69 * respresents where the function is being assigned to.
michael@0 70 */
michael@0 71 bool nameExpression(ParseNode *n) {
michael@0 72 switch (n->getKind()) {
michael@0 73 case PNK_DOT:
michael@0 74 return nameExpression(n->expr()) && appendPropertyReference(n->pn_atom);
michael@0 75
michael@0 76 case PNK_NAME:
michael@0 77 return buf->append(n->pn_atom);
michael@0 78
michael@0 79 case PNK_THIS:
michael@0 80 return buf->append("this");
michael@0 81
michael@0 82 case PNK_ELEM:
michael@0 83 return nameExpression(n->pn_left) &&
michael@0 84 buf->append("[") &&
michael@0 85 nameExpression(n->pn_right) &&
michael@0 86 buf->append("]");
michael@0 87
michael@0 88 case PNK_NUMBER:
michael@0 89 return appendNumber(n->pn_dval);
michael@0 90
michael@0 91 default:
michael@0 92 /*
michael@0 93 * Technically this isn't an "abort" situation, we're just confused
michael@0 94 * on what to call this function, but failures in naming aren't
michael@0 95 * treated as fatal.
michael@0 96 */
michael@0 97 return false;
michael@0 98 }
michael@0 99 }
michael@0 100
michael@0 101 /*
michael@0 102 * When naming an anonymous function, the process works loosely by walking
michael@0 103 * up the AST and then translating that to a string. The stringification
michael@0 104 * happens from some far-up assignment and then going back down the parse
michael@0 105 * tree to the function definition point.
michael@0 106 *
michael@0 107 * This function will walk up the parse tree, gathering relevant nodes used
michael@0 108 * for naming, and return the assignment node if there is one. The provided
michael@0 109 * array and size will be filled in, and the returned node could be nullptr
michael@0 110 * if no assignment is found. The first element of the array will be the
michael@0 111 * innermost node relevant to naming, and the last element will be the
michael@0 112 * outermost node.
michael@0 113 */
michael@0 114 ParseNode *gatherNameable(ParseNode **nameable, size_t *size) {
michael@0 115 *size = 0;
michael@0 116
michael@0 117 for (int pos = nparents - 1; pos >= 0; pos--) {
michael@0 118 ParseNode *cur = parents[pos];
michael@0 119 if (cur->isAssignment())
michael@0 120 return cur;
michael@0 121
michael@0 122 switch (cur->getKind()) {
michael@0 123 case PNK_NAME: return cur; /* found the initialized declaration */
michael@0 124 case PNK_THIS: return cur; /* Setting a property of 'this'. */
michael@0 125 case PNK_FUNCTION: return nullptr; /* won't find an assignment or declaration */
michael@0 126
michael@0 127 case PNK_RETURN:
michael@0 128 /*
michael@0 129 * Normally the relevant parent of a node is its direct parent, but
michael@0 130 * sometimes with code like:
michael@0 131 *
michael@0 132 * var foo = (function() { return function() {}; })();
michael@0 133 *
michael@0 134 * the outer function is just a helper to create a scope for the
michael@0 135 * returned function. Hence the name of the returned function should
michael@0 136 * actually be 'foo'. This loop sees if the current node is a
michael@0 137 * PNK_RETURN, and if there is a direct function call we skip to
michael@0 138 * that.
michael@0 139 */
michael@0 140 for (int tmp = pos - 1; tmp > 0; tmp--) {
michael@0 141 if (isDirectCall(tmp, cur)) {
michael@0 142 pos = tmp;
michael@0 143 break;
michael@0 144 } else if (call(cur)) {
michael@0 145 /* Don't skip too high in the tree */
michael@0 146 break;
michael@0 147 }
michael@0 148 cur = parents[tmp];
michael@0 149 }
michael@0 150 break;
michael@0 151
michael@0 152 case PNK_COLON:
michael@0 153 /*
michael@0 154 * Record the PNK_COLON but skip the PNK_OBJECT so we're not
michael@0 155 * flagged as a contributor.
michael@0 156 */
michael@0 157 pos--;
michael@0 158 /* fallthrough */
michael@0 159
michael@0 160 default:
michael@0 161 /* Save any other nodes we encounter on the way up. */
michael@0 162 JS_ASSERT(*size < MaxParents);
michael@0 163 nameable[(*size)++] = cur;
michael@0 164 break;
michael@0 165 }
michael@0 166 }
michael@0 167
michael@0 168 return nullptr;
michael@0 169 }
michael@0 170
michael@0 171 /*
michael@0 172 * Resolve the name of a function. If the function already has a name
michael@0 173 * listed, then it is skipped. Otherwise an intelligent name is guessed to
michael@0 174 * assign to the function's displayAtom field
michael@0 175 */
michael@0 176 bool resolveFun(ParseNode *pn, HandleAtom prefix, MutableHandleAtom retAtom) {
michael@0 177 JS_ASSERT(pn != nullptr && pn->isKind(PNK_FUNCTION));
michael@0 178 RootedFunction fun(cx, pn->pn_funbox->function());
michael@0 179
michael@0 180 StringBuffer buf(cx);
michael@0 181 this->buf = &buf;
michael@0 182
michael@0 183 retAtom.set(nullptr);
michael@0 184
michael@0 185 /* If the function already has a name, use that */
michael@0 186 if (fun->displayAtom() != nullptr) {
michael@0 187 if (prefix == nullptr) {
michael@0 188 retAtom.set(fun->displayAtom());
michael@0 189 return true;
michael@0 190 }
michael@0 191 if (!buf.append(prefix) ||
michael@0 192 !buf.append("/") ||
michael@0 193 !buf.append(fun->displayAtom()))
michael@0 194 return false;
michael@0 195 retAtom.set(buf.finishAtom());
michael@0 196 return !!retAtom;
michael@0 197 }
michael@0 198
michael@0 199 /* If a prefix is specified, then it is a form of namespace */
michael@0 200 if (prefix != nullptr && (!buf.append(prefix) || !buf.append("/")))
michael@0 201 return false;
michael@0 202
michael@0 203 /* Gather all nodes relevant to naming */
michael@0 204 ParseNode *toName[MaxParents];
michael@0 205 size_t size;
michael@0 206 ParseNode *assignment = gatherNameable(toName, &size);
michael@0 207
michael@0 208 /* If the function is assigned to something, then that is very relevant */
michael@0 209 if (assignment) {
michael@0 210 if (assignment->isAssignment())
michael@0 211 assignment = assignment->pn_left;
michael@0 212 if (!nameExpression(assignment))
michael@0 213 return true;
michael@0 214 }
michael@0 215
michael@0 216 /*
michael@0 217 * Other than the actual assignment, other relevant nodes to naming are
michael@0 218 * those in object initializers and then particular nodes marking a
michael@0 219 * contribution.
michael@0 220 */
michael@0 221 for (int pos = size - 1; pos >= 0; pos--) {
michael@0 222 ParseNode *node = toName[pos];
michael@0 223
michael@0 224 if (node->isKind(PNK_COLON)) {
michael@0 225 ParseNode *left = node->pn_left;
michael@0 226 if (left->isKind(PNK_NAME) || left->isKind(PNK_STRING)) {
michael@0 227 if (!appendPropertyReference(left->pn_atom))
michael@0 228 return false;
michael@0 229 } else if (left->isKind(PNK_NUMBER)) {
michael@0 230 if (!appendNumericPropertyReference(left->pn_dval))
michael@0 231 return false;
michael@0 232 }
michael@0 233 } else {
michael@0 234 /*
michael@0 235 * Don't have consecutive '<' characters, and also don't start
michael@0 236 * with a '<' character.
michael@0 237 */
michael@0 238 if (!buf.empty() && *(buf.end() - 1) != '<' && !buf.append("<"))
michael@0 239 return false;
michael@0 240 }
michael@0 241 }
michael@0 242
michael@0 243 /*
michael@0 244 * functions which are "genuinely anonymous" but are contained in some
michael@0 245 * other namespace are rather considered as "contributing" to the outer
michael@0 246 * function, so give them a contribution symbol here.
michael@0 247 */
michael@0 248 if (!buf.empty() && *(buf.end() - 1) == '/' && !buf.append("<"))
michael@0 249 return false;
michael@0 250
michael@0 251 if (buf.empty())
michael@0 252 return true;
michael@0 253
michael@0 254 retAtom.set(buf.finishAtom());
michael@0 255 if (!retAtom)
michael@0 256 return false;
michael@0 257 fun->setGuessedAtom(retAtom);
michael@0 258 return true;
michael@0 259 }
michael@0 260
michael@0 261 /*
michael@0 262 * Tests whether parents[pos] is a function call whose callee is cur.
michael@0 263 * This is the case for functions which do things like simply create a scope
michael@0 264 * for new variables and then return an anonymous function using this scope.
michael@0 265 */
michael@0 266 bool isDirectCall(int pos, ParseNode *cur) {
michael@0 267 return pos >= 0 && call(parents[pos]) && parents[pos]->pn_head == cur;
michael@0 268 }
michael@0 269
michael@0 270 public:
michael@0 271 explicit NameResolver(ExclusiveContext *cx) : cx(cx), nparents(0), buf(nullptr) {}
michael@0 272
michael@0 273 /*
michael@0 274 * Resolve all names for anonymous functions recursively within the
michael@0 275 * ParseNode instance given. The prefix is for each subsequent name, and
michael@0 276 * should initially be nullptr.
michael@0 277 */
michael@0 278 bool resolve(ParseNode *cur, HandleAtom prefixArg = js::NullPtr()) {
michael@0 279 RootedAtom prefix(cx, prefixArg);
michael@0 280 if (cur == nullptr)
michael@0 281 return true;
michael@0 282
michael@0 283 if (cur->isKind(PNK_FUNCTION) && cur->isArity(PN_CODE)) {
michael@0 284 RootedAtom prefix2(cx);
michael@0 285 if (!resolveFun(cur, prefix, &prefix2))
michael@0 286 return false;
michael@0 287
michael@0 288 /*
michael@0 289 * If a function looks like (function(){})() where the parent node
michael@0 290 * of the definition of the function is a call, then it shouldn't
michael@0 291 * contribute anything to the namespace, so don't bother updating
michael@0 292 * the prefix to whatever was returned.
michael@0 293 */
michael@0 294 if (!isDirectCall(nparents - 1, cur))
michael@0 295 prefix = prefix2;
michael@0 296 }
michael@0 297 if (nparents >= MaxParents)
michael@0 298 return true;
michael@0 299 parents[nparents++] = cur;
michael@0 300
michael@0 301 switch (cur->getArity()) {
michael@0 302 case PN_NULLARY:
michael@0 303 break;
michael@0 304 case PN_NAME:
michael@0 305 if (!resolve(cur->maybeExpr(), prefix))
michael@0 306 return false;
michael@0 307 break;
michael@0 308 case PN_UNARY:
michael@0 309 if (!resolve(cur->pn_kid, prefix))
michael@0 310 return false;
michael@0 311 break;
michael@0 312 case PN_BINARY:
michael@0 313 case PN_BINARY_OBJ:
michael@0 314 if (!resolve(cur->pn_left, prefix))
michael@0 315 return false;
michael@0 316
michael@0 317 /*
michael@0 318 * FIXME? Occasionally pn_left == pn_right for something like
michael@0 319 * destructuring assignment in (function({foo}){}), so skip the
michael@0 320 * duplicate here if this is the case because we want to traverse
michael@0 321 * everything at most once.
michael@0 322 */
michael@0 323 if (cur->pn_left != cur->pn_right)
michael@0 324 if (!resolve(cur->pn_right, prefix))
michael@0 325 return false;
michael@0 326 break;
michael@0 327 case PN_TERNARY:
michael@0 328 if (!resolve(cur->pn_kid1, prefix))
michael@0 329 return false;
michael@0 330 if (!resolve(cur->pn_kid2, prefix))
michael@0 331 return false;
michael@0 332 if (!resolve(cur->pn_kid3, prefix))
michael@0 333 return false;
michael@0 334 break;
michael@0 335 case PN_CODE:
michael@0 336 JS_ASSERT(cur->isKind(PNK_FUNCTION));
michael@0 337 if (!resolve(cur->pn_body, prefix))
michael@0 338 return false;
michael@0 339 break;
michael@0 340 case PN_LIST:
michael@0 341 for (ParseNode *nxt = cur->pn_head; nxt; nxt = nxt->pn_next)
michael@0 342 if (!resolve(nxt, prefix))
michael@0 343 return false;
michael@0 344 break;
michael@0 345 }
michael@0 346 nparents--;
michael@0 347 return true;
michael@0 348 }
michael@0 349 };
michael@0 350
michael@0 351 } /* anonymous namespace */
michael@0 352
michael@0 353 bool
michael@0 354 frontend::NameFunctions(ExclusiveContext *cx, ParseNode *pn)
michael@0 355 {
michael@0 356 NameResolver nr(cx);
michael@0 357 return nr.resolve(pn);
michael@0 358 }

mercurial