js/src/frontend/SharedContext.h

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.

     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 */

mercurial