Sat, 03 Jan 2015 20:18:00 +0100
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.
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef frontend_SharedContext_h
8 #define frontend_SharedContext_h
10 #include "jsatom.h"
11 #include "jsopcode.h"
12 #include "jspubtd.h"
13 #include "jsscript.h"
14 #include "jstypes.h"
16 #include "frontend/ParseMaps.h"
17 #include "frontend/ParseNode.h"
18 #include "frontend/TokenStream.h"
19 #include "vm/ScopeObject.h"
21 namespace js {
22 namespace frontend {
24 // These flags apply to both global and function contexts.
25 class AnyContextFlags
26 {
27 // This class's data is all private and so only visible to these friends.
28 friend class SharedContext;
30 // True if "use strict"; appears in the body instead of being inherited.
31 bool hasExplicitUseStrict:1;
33 // The (static) bindings of this script need to support dynamic name
34 // read/write access. Here, 'dynamic' means dynamic dictionary lookup on
35 // the scope chain for a dynamic set of keys. The primary examples are:
36 // - direct eval
37 // - function::
38 // - with
39 // since both effectively allow any name to be accessed. Non-examples are:
40 // - upvars of nested functions
41 // - function statement
42 // since the set of assigned name is known dynamically. 'with' could be in
43 // the non-example category, provided the set of all free variables within
44 // the with block was noted. However, we do not optimize 'with' so, for
45 // simplicity, 'with' is treated like eval.
46 //
47 // Note: access through the arguments object is not considered dynamic
48 // binding access since it does not go through the normal name lookup
49 // mechanism. This is debatable and could be changed (although care must be
50 // taken not to turn off the whole 'arguments' optimization). To answer the
51 // more general "is this argument aliased" question, script->needsArgsObj
52 // should be tested (see JSScript::argIsAlised).
53 //
54 bool bindingsAccessedDynamically:1;
56 // Whether this script, or any of its inner scripts contains a debugger
57 // statement which could potentially read or write anywhere along the
58 // scope chain.
59 bool hasDebuggerStatement:1;
61 public:
62 AnyContextFlags()
63 : hasExplicitUseStrict(false),
64 bindingsAccessedDynamically(false),
65 hasDebuggerStatement(false)
66 { }
67 };
69 class FunctionContextFlags
70 {
71 // This class's data is all private and so only visible to these friends.
72 friend class FunctionBox;
74 // The function or a function that encloses it may define new local names
75 // at runtime through means other than calling eval.
76 bool mightAliasLocals:1;
78 // This function does something that can extend the set of bindings in its
79 // call objects --- it does a direct eval in non-strict code, or includes a
80 // function statement (as opposed to a function definition).
81 //
82 // This flag is *not* inherited by enclosed or enclosing functions; it
83 // applies only to the function in whose flags it appears.
84 //
85 bool hasExtensibleScope:1;
87 // This function refers directly to its name in a way which requires the
88 // name to be a separate object on the scope chain.
89 bool needsDeclEnvObject:1;
91 // Technically, every function has a binding named 'arguments'. Internally,
92 // this binding is only added when 'arguments' is mentioned by the function
93 // body. This flag indicates whether 'arguments' has been bound either
94 // through implicit use:
95 // function f() { return arguments }
96 // or explicit redeclaration:
97 // function f() { var arguments; return arguments }
98 //
99 // Note 1: overwritten arguments (function() { arguments = 3 }) will cause
100 // this flag to be set but otherwise require no special handling:
101 // 'arguments' is just a local variable and uses of 'arguments' will just
102 // read the local's current slot which may have been assigned. The only
103 // special semantics is that the initial value of 'arguments' is the
104 // arguments object (not undefined, like normal locals).
105 //
106 // Note 2: if 'arguments' is bound as a formal parameter, there will be an
107 // 'arguments' in Bindings, but, as the "LOCAL" in the name indicates, this
108 // flag will not be set. This is because, as a formal, 'arguments' will
109 // have no special semantics: the initial value is unconditionally the
110 // actual argument (or undefined if nactual < nformal).
111 //
112 bool argumentsHasLocalBinding:1;
114 // In many cases where 'arguments' has a local binding (as described above)
115 // we do not need to actually create an arguments object in the function
116 // prologue: instead we can analyze how 'arguments' is used (using the
117 // simple dataflow analysis in analyzeSSA) to determine that uses of
118 // 'arguments' can just read from the stack frame directly. However, the
119 // dataflow analysis only looks at how JSOP_ARGUMENTS is used, so it will
120 // be unsound in several cases. The frontend filters out such cases by
121 // setting this flag which eagerly sets script->needsArgsObj to true.
122 //
123 bool definitelyNeedsArgsObj:1;
125 public:
126 FunctionContextFlags()
127 : mightAliasLocals(false),
128 hasExtensibleScope(false),
129 needsDeclEnvObject(false),
130 argumentsHasLocalBinding(false),
131 definitelyNeedsArgsObj(false)
132 { }
133 };
135 class GlobalSharedContext;
137 // List of directives that may be encountered in a Directive Prologue (ES5 15.1).
138 class Directives
139 {
140 bool strict_;
141 bool asmJS_;
143 public:
144 explicit Directives(bool strict) : strict_(strict), asmJS_(false) {}
145 template <typename ParseHandler> explicit Directives(ParseContext<ParseHandler> *parent);
147 void setStrict() { strict_ = true; }
148 bool strict() const { return strict_; }
150 void setAsmJS() { asmJS_ = true; }
151 bool asmJS() const { return asmJS_; }
153 Directives &operator=(Directives rhs) {
154 strict_ = rhs.strict_;
155 asmJS_ = rhs.asmJS_;
156 return *this;
157 }
158 bool operator==(const Directives &rhs) const {
159 return strict_ == rhs.strict_ && asmJS_ == rhs.asmJS_;
160 }
161 bool operator!=(const Directives &rhs) const {
162 return !(*this == rhs);
163 }
164 };
166 /*
167 * The struct SharedContext is part of the current parser context (see
168 * ParseContext). It stores information that is reused between the parser and
169 * the bytecode emitter. Note however, that this information is not shared
170 * between the two; they simply reuse the same data structure.
171 */
172 class SharedContext
173 {
174 public:
175 ExclusiveContext *const context;
176 AnyContextFlags anyCxFlags;
177 bool strict;
178 bool extraWarnings;
180 // If it's function code, funbox must be non-nullptr and scopeChain must be
181 // nullptr. If it's global code, funbox must be nullptr.
182 SharedContext(ExclusiveContext *cx, Directives directives, bool extraWarnings)
183 : context(cx),
184 anyCxFlags(),
185 strict(directives.strict()),
186 extraWarnings(extraWarnings)
187 {}
189 virtual ObjectBox *toObjectBox() = 0;
190 inline bool isGlobalSharedContext() { return toObjectBox() == nullptr; }
191 inline bool isFunctionBox() { return toObjectBox() && toObjectBox()->isFunctionBox(); }
192 inline GlobalSharedContext *asGlobalSharedContext();
193 inline FunctionBox *asFunctionBox();
195 bool hasExplicitUseStrict() const { return anyCxFlags.hasExplicitUseStrict; }
196 bool bindingsAccessedDynamically() const { return anyCxFlags.bindingsAccessedDynamically; }
197 bool hasDebuggerStatement() const { return anyCxFlags.hasDebuggerStatement; }
199 void setExplicitUseStrict() { anyCxFlags.hasExplicitUseStrict = true; }
200 void setBindingsAccessedDynamically() { anyCxFlags.bindingsAccessedDynamically = true; }
201 void setHasDebuggerStatement() { anyCxFlags.hasDebuggerStatement = true; }
203 inline bool allLocalsAliased();
205 // JSOPTION_EXTRA_WARNINGS warnings or strict mode errors.
206 bool needStrictChecks() {
207 return strict || extraWarnings;
208 }
209 };
211 class GlobalSharedContext : public SharedContext
212 {
213 private:
214 const RootedObject scopeChain_; /* scope chain object for the script */
216 public:
217 GlobalSharedContext(ExclusiveContext *cx, JSObject *scopeChain,
218 Directives directives, bool extraWarnings)
219 : SharedContext(cx, directives, extraWarnings),
220 scopeChain_(cx, scopeChain)
221 {}
223 ObjectBox *toObjectBox() { return nullptr; }
224 JSObject *scopeChain() const { return scopeChain_; }
225 };
227 inline GlobalSharedContext *
228 SharedContext::asGlobalSharedContext()
229 {
230 JS_ASSERT(isGlobalSharedContext());
231 return static_cast<GlobalSharedContext*>(this);
232 }
234 class FunctionBox : public ObjectBox, public SharedContext
235 {
236 public:
237 Bindings bindings; /* bindings for this function */
238 uint32_t bufStart;
239 uint32_t bufEnd;
240 uint32_t startLine;
241 uint32_t startColumn;
242 uint16_t length;
244 uint8_t generatorKindBits_; /* The GeneratorKind of this function. */
245 bool inWith:1; /* some enclosing scope is a with-statement */
246 bool inGenexpLambda:1; /* lambda from generator expression */
247 bool hasDestructuringArgs:1; /* arguments list contains destructuring expression */
248 bool useAsm:1; /* function contains "use asm" directive */
249 bool insideUseAsm:1; /* nested function of function of "use asm" directive */
251 // Fields for use in heuristics.
252 bool usesArguments:1; /* contains a free use of 'arguments' */
253 bool usesApply:1; /* contains an f.apply() call */
255 FunctionContextFlags funCxFlags;
257 template <typename ParseHandler>
258 FunctionBox(ExclusiveContext *cx, ObjectBox* traceListHead, JSFunction *fun,
259 ParseContext<ParseHandler> *pc, Directives directives,
260 bool extraWarnings, GeneratorKind generatorKind);
262 ObjectBox *toObjectBox() { return this; }
263 JSFunction *function() const { return &object->as<JSFunction>(); }
265 GeneratorKind generatorKind() const { return GeneratorKindFromBits(generatorKindBits_); }
266 bool isGenerator() const { return generatorKind() != NotGenerator; }
267 bool isLegacyGenerator() const { return generatorKind() == LegacyGenerator; }
268 bool isStarGenerator() const { return generatorKind() == StarGenerator; }
270 void setGeneratorKind(GeneratorKind kind) {
271 // A generator kind can be set at initialization, or when "yield" is
272 // first seen. In both cases the transition can only happen from
273 // NotGenerator.
274 JS_ASSERT(!isGenerator());
275 generatorKindBits_ = GeneratorKindAsBits(kind);
276 }
278 bool mightAliasLocals() const { return funCxFlags.mightAliasLocals; }
279 bool hasExtensibleScope() const { return funCxFlags.hasExtensibleScope; }
280 bool needsDeclEnvObject() const { return funCxFlags.needsDeclEnvObject; }
281 bool argumentsHasLocalBinding() const { return funCxFlags.argumentsHasLocalBinding; }
282 bool definitelyNeedsArgsObj() const { return funCxFlags.definitelyNeedsArgsObj; }
284 void setMightAliasLocals() { funCxFlags.mightAliasLocals = true; }
285 void setHasExtensibleScope() { funCxFlags.hasExtensibleScope = true; }
286 void setNeedsDeclEnvObject() { funCxFlags.needsDeclEnvObject = true; }
287 void setArgumentsHasLocalBinding() { funCxFlags.argumentsHasLocalBinding = true; }
288 void setDefinitelyNeedsArgsObj() { JS_ASSERT(funCxFlags.argumentsHasLocalBinding);
289 funCxFlags.definitelyNeedsArgsObj = true; }
291 bool hasDefaults() const {
292 return length != function()->nargs() - function()->hasRest();
293 }
295 // Return whether this function has either specified "use asm" or is
296 // (transitively) nested inside a function that has.
297 bool useAsmOrInsideUseAsm() const {
298 return useAsm || insideUseAsm;
299 }
301 void setStart(const TokenStream &tokenStream) {
302 bufStart = tokenStream.currentToken().pos.begin;
303 startLine = tokenStream.getLineno();
304 startColumn = tokenStream.getColumn();
305 }
307 bool isHeavyweight()
308 {
309 // Note: this should be kept in sync with JSFunction::isHeavyweight().
310 return bindings.hasAnyAliasedBindings() ||
311 hasExtensibleScope() ||
312 needsDeclEnvObject() ||
313 isGenerator();
314 }
315 };
317 inline FunctionBox *
318 SharedContext::asFunctionBox()
319 {
320 JS_ASSERT(isFunctionBox());
321 return static_cast<FunctionBox*>(this);
322 }
324 // In generators, we treat all locals as aliased so that they get stored on the
325 // heap. This way there is less information to copy off the stack when
326 // suspending, and back on when resuming. It also avoids the need to create and
327 // invalidate DebugScope proxies for unaliased locals in a generator frame, as
328 // the generator frame will be copied out to the heap and released only by GC.
329 inline bool
330 SharedContext::allLocalsAliased()
331 {
332 return bindingsAccessedDynamically() || (isFunctionBox() && asFunctionBox()->isGenerator());
333 }
336 /*
337 * NB: If you add a new type of statement that is a scope, add it between
338 * STMT_WITH and STMT_CATCH, or you will break StmtInfoBase::linksScope. If you
339 * add a non-looping statement type, add it before STMT_DO_LOOP or you will
340 * break StmtInfoBase::isLoop().
341 *
342 * Also remember to keep the statementName array in BytecodeEmitter.cpp in
343 * sync.
344 */
345 enum StmtType {
346 STMT_LABEL, /* labeled statement: L: s */
347 STMT_IF, /* if (then) statement */
348 STMT_ELSE, /* else clause of if statement */
349 STMT_SEQ, /* synthetic sequence of statements */
350 STMT_BLOCK, /* compound statement: { s1[;... sN] } */
351 STMT_SWITCH, /* switch statement */
352 STMT_WITH, /* with statement */
353 STMT_CATCH, /* catch block */
354 STMT_TRY, /* try block */
355 STMT_FINALLY, /* finally block */
356 STMT_SUBROUTINE, /* gosub-target subroutine body */
357 STMT_DO_LOOP, /* do/while loop statement */
358 STMT_FOR_LOOP, /* for loop statement */
359 STMT_FOR_IN_LOOP, /* for/in loop statement */
360 STMT_FOR_OF_LOOP, /* for/of loop statement */
361 STMT_WHILE_LOOP, /* while loop statement */
362 STMT_LIMIT
363 };
365 /*
366 * A comment on the encoding of the js::StmtType enum and StmtInfoBase
367 * type-testing methods:
368 *
369 * StmtInfoBase::maybeScope() tells whether a statement type is always, or may
370 * become, a lexical scope. It therefore includes block and switch (the two
371 * low-numbered "maybe" scope types) and excludes with (with has dynamic scope
372 * pending the "reformed with" in ES4/JS2). It includes all try-catch-finally
373 * types, which are high-numbered maybe-scope types.
374 *
375 * StmtInfoBase::linksScope() tells whether a js::StmtInfo{PC,BCE} of the given
376 * type eagerly links to other scoping statement info records. It excludes the
377 * two early "maybe" types, block and switch, as well as the try and both
378 * finally types, since try and the other trailing maybe-scope types don't need
379 * block scope unless they contain let declarations.
380 *
381 * We treat WITH as a static scope because it prevents lexical binding from
382 * continuing further up the static scope chain. With the lost "reformed with"
383 * proposal for ES4, we would be able to model it statically, too.
384 */
386 // StmtInfoPC is used by the Parser. StmtInfoBCE is used by the
387 // BytecodeEmitter. The two types have some overlap, encapsulated by
388 // StmtInfoBase. Several functions below (e.g. PushStatement) are templated to
389 // work with both types.
391 struct StmtInfoBase {
392 // Statement type (StmtType).
393 uint16_t type;
395 // True if type is STMT_BLOCK, STMT_TRY, STMT_SWITCH, or STMT_FINALLY and
396 // the block contains at least one let-declaration, or if type is
397 // STMT_CATCH.
398 bool isBlockScope:1;
400 // True if isBlockScope or type == STMT_WITH.
401 bool isNestedScope:1;
403 // for (let ...) induced block scope
404 bool isForLetBlock:1;
406 // Block label.
407 RootedAtom label;
409 // Compile-time scope chain node for this scope. Only set if
410 // isNestedScope.
411 Rooted<NestedScopeObject *> staticScope;
413 StmtInfoBase(ExclusiveContext *cx)
414 : isBlockScope(false), isNestedScope(false), isForLetBlock(false),
415 label(cx), staticScope(cx)
416 {}
418 bool maybeScope() const {
419 return STMT_BLOCK <= type && type <= STMT_SUBROUTINE && type != STMT_WITH;
420 }
422 bool linksScope() const {
423 return isNestedScope;
424 }
426 StaticBlockObject& staticBlock() const {
427 JS_ASSERT(isNestedScope);
428 JS_ASSERT(isBlockScope);
429 return staticScope->as<StaticBlockObject>();
430 }
432 bool isLoop() const {
433 return type >= STMT_DO_LOOP;
434 }
436 bool isTrying() const {
437 return STMT_TRY <= type && type <= STMT_SUBROUTINE;
438 }
439 };
441 // Push the C-stack-allocated struct at stmt onto the StmtInfoPC stack.
442 template <class ContextT>
443 void
444 PushStatement(ContextT *ct, typename ContextT::StmtInfo *stmt, StmtType type)
445 {
446 stmt->type = type;
447 stmt->isBlockScope = false;
448 stmt->isNestedScope = false;
449 stmt->isForLetBlock = false;
450 stmt->label = nullptr;
451 stmt->staticScope = nullptr;
452 stmt->down = ct->topStmt;
453 ct->topStmt = stmt;
454 if (stmt->linksScope()) {
455 stmt->downScope = ct->topScopeStmt;
456 ct->topScopeStmt = stmt;
457 } else {
458 stmt->downScope = nullptr;
459 }
460 }
462 template <class ContextT>
463 void
464 FinishPushNestedScope(ContextT *ct, typename ContextT::StmtInfo *stmt, NestedScopeObject &staticScope)
465 {
466 stmt->isNestedScope = true;
467 stmt->downScope = ct->topScopeStmt;
468 ct->topScopeStmt = stmt;
469 ct->staticScope = &staticScope;
470 stmt->staticScope = &staticScope;
471 }
473 // Pop pc->topStmt. If the top StmtInfoPC struct is not stack-allocated, it
474 // is up to the caller to free it. The dummy argument is just to make the
475 // template matching work.
476 template <class ContextT>
477 void
478 FinishPopStatement(ContextT *ct)
479 {
480 typename ContextT::StmtInfo *stmt = ct->topStmt;
481 ct->topStmt = stmt->down;
482 if (stmt->linksScope()) {
483 ct->topScopeStmt = stmt->downScope;
484 if (stmt->isNestedScope) {
485 JS_ASSERT(stmt->staticScope);
486 ct->staticScope = stmt->staticScope->enclosingNestedScope();
487 }
488 }
489 }
491 /*
492 * Find a lexically scoped variable (one declared by let, catch, or an array
493 * comprehension) named by atom, looking in sc's compile-time scopes.
494 *
495 * If a WITH statement is reached along the scope stack, return its statement
496 * info record, so callers can tell that atom is ambiguous. If slotp is not
497 * null, then if atom is found, set *slotp to its stack slot, otherwise to -1.
498 * This means that if slotp is not null, all the block objects on the lexical
499 * scope chain must have had their depth slots computed by the code generator,
500 * so the caller must be under EmitTree.
501 *
502 * In any event, directly return the statement info record in which atom was
503 * found. Otherwise return null.
504 */
505 template <class ContextT>
506 typename ContextT::StmtInfo *
507 LexicalLookup(ContextT *ct, HandleAtom atom, int *slotp, typename ContextT::StmtInfo *stmt);
509 } // namespace frontend
511 } // namespace js
513 #endif /* frontend_SharedContext_h */