js/src/frontend/SharedContext.h

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/js/src/frontend/SharedContext.h	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,513 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99:
     1.6 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#ifndef frontend_SharedContext_h
    1.11 +#define frontend_SharedContext_h
    1.12 +
    1.13 +#include "jsatom.h"
    1.14 +#include "jsopcode.h"
    1.15 +#include "jspubtd.h"
    1.16 +#include "jsscript.h"
    1.17 +#include "jstypes.h"
    1.18 +
    1.19 +#include "frontend/ParseMaps.h"
    1.20 +#include "frontend/ParseNode.h"
    1.21 +#include "frontend/TokenStream.h"
    1.22 +#include "vm/ScopeObject.h"
    1.23 +
    1.24 +namespace js {
    1.25 +namespace frontend {
    1.26 +
    1.27 +// These flags apply to both global and function contexts.
    1.28 +class AnyContextFlags
    1.29 +{
    1.30 +    // This class's data is all private and so only visible to these friends.
    1.31 +    friend class SharedContext;
    1.32 +
    1.33 +    // True if "use strict"; appears in the body instead of being inherited.
    1.34 +    bool            hasExplicitUseStrict:1;
    1.35 +
    1.36 +    // The (static) bindings of this script need to support dynamic name
    1.37 +    // read/write access. Here, 'dynamic' means dynamic dictionary lookup on
    1.38 +    // the scope chain for a dynamic set of keys. The primary examples are:
    1.39 +    //  - direct eval
    1.40 +    //  - function::
    1.41 +    //  - with
    1.42 +    // since both effectively allow any name to be accessed. Non-examples are:
    1.43 +    //  - upvars of nested functions
    1.44 +    //  - function statement
    1.45 +    // since the set of assigned name is known dynamically. 'with' could be in
    1.46 +    // the non-example category, provided the set of all free variables within
    1.47 +    // the with block was noted. However, we do not optimize 'with' so, for
    1.48 +    // simplicity, 'with' is treated like eval.
    1.49 +    //
    1.50 +    // Note: access through the arguments object is not considered dynamic
    1.51 +    // binding access since it does not go through the normal name lookup
    1.52 +    // mechanism. This is debatable and could be changed (although care must be
    1.53 +    // taken not to turn off the whole 'arguments' optimization). To answer the
    1.54 +    // more general "is this argument aliased" question, script->needsArgsObj
    1.55 +    // should be tested (see JSScript::argIsAlised).
    1.56 +    //
    1.57 +    bool            bindingsAccessedDynamically:1;
    1.58 +
    1.59 +    // Whether this script, or any of its inner scripts contains a debugger
    1.60 +    // statement which could potentially read or write anywhere along the
    1.61 +    // scope chain.
    1.62 +    bool            hasDebuggerStatement:1;
    1.63 +
    1.64 +  public:
    1.65 +    AnyContextFlags()
    1.66 +     :  hasExplicitUseStrict(false),
    1.67 +        bindingsAccessedDynamically(false),
    1.68 +        hasDebuggerStatement(false)
    1.69 +    { }
    1.70 +};
    1.71 +
    1.72 +class FunctionContextFlags
    1.73 +{
    1.74 +    // This class's data is all private and so only visible to these friends.
    1.75 +    friend class FunctionBox;
    1.76 +
    1.77 +    // The function or a function that encloses it may define new local names
    1.78 +    // at runtime through means other than calling eval.
    1.79 +    bool mightAliasLocals:1;
    1.80 +
    1.81 +    // This function does something that can extend the set of bindings in its
    1.82 +    // call objects --- it does a direct eval in non-strict code, or includes a
    1.83 +    // function statement (as opposed to a function definition).
    1.84 +    //
    1.85 +    // This flag is *not* inherited by enclosed or enclosing functions; it
    1.86 +    // applies only to the function in whose flags it appears.
    1.87 +    //
    1.88 +    bool hasExtensibleScope:1;
    1.89 +
    1.90 +    // This function refers directly to its name in a way which requires the
    1.91 +    // name to be a separate object on the scope chain.
    1.92 +    bool needsDeclEnvObject:1;
    1.93 +
    1.94 +    // Technically, every function has a binding named 'arguments'. Internally,
    1.95 +    // this binding is only added when 'arguments' is mentioned by the function
    1.96 +    // body. This flag indicates whether 'arguments' has been bound either
    1.97 +    // through implicit use:
    1.98 +    //   function f() { return arguments }
    1.99 +    // or explicit redeclaration:
   1.100 +    //   function f() { var arguments; return arguments }
   1.101 +    //
   1.102 +    // Note 1: overwritten arguments (function() { arguments = 3 }) will cause
   1.103 +    // this flag to be set but otherwise require no special handling:
   1.104 +    // 'arguments' is just a local variable and uses of 'arguments' will just
   1.105 +    // read the local's current slot which may have been assigned. The only
   1.106 +    // special semantics is that the initial value of 'arguments' is the
   1.107 +    // arguments object (not undefined, like normal locals).
   1.108 +    //
   1.109 +    // Note 2: if 'arguments' is bound as a formal parameter, there will be an
   1.110 +    // 'arguments' in Bindings, but, as the "LOCAL" in the name indicates, this
   1.111 +    // flag will not be set. This is because, as a formal, 'arguments' will
   1.112 +    // have no special semantics: the initial value is unconditionally the
   1.113 +    // actual argument (or undefined if nactual < nformal).
   1.114 +    //
   1.115 +    bool argumentsHasLocalBinding:1;
   1.116 +
   1.117 +    // In many cases where 'arguments' has a local binding (as described above)
   1.118 +    // we do not need to actually create an arguments object in the function
   1.119 +    // prologue: instead we can analyze how 'arguments' is used (using the
   1.120 +    // simple dataflow analysis in analyzeSSA) to determine that uses of
   1.121 +    // 'arguments' can just read from the stack frame directly. However, the
   1.122 +    // dataflow analysis only looks at how JSOP_ARGUMENTS is used, so it will
   1.123 +    // be unsound in several cases. The frontend filters out such cases by
   1.124 +    // setting this flag which eagerly sets script->needsArgsObj to true.
   1.125 +    //
   1.126 +    bool definitelyNeedsArgsObj:1;
   1.127 +
   1.128 +  public:
   1.129 +    FunctionContextFlags()
   1.130 +     :  mightAliasLocals(false),
   1.131 +        hasExtensibleScope(false),
   1.132 +        needsDeclEnvObject(false),
   1.133 +        argumentsHasLocalBinding(false),
   1.134 +        definitelyNeedsArgsObj(false)
   1.135 +    { }
   1.136 +};
   1.137 +
   1.138 +class GlobalSharedContext;
   1.139 +
   1.140 +// List of directives that may be encountered in a Directive Prologue (ES5 15.1).
   1.141 +class Directives
   1.142 +{
   1.143 +    bool strict_;
   1.144 +    bool asmJS_;
   1.145 +
   1.146 +  public:
   1.147 +    explicit Directives(bool strict) : strict_(strict), asmJS_(false) {}
   1.148 +    template <typename ParseHandler> explicit Directives(ParseContext<ParseHandler> *parent);
   1.149 +
   1.150 +    void setStrict() { strict_ = true; }
   1.151 +    bool strict() const { return strict_; }
   1.152 +
   1.153 +    void setAsmJS() { asmJS_ = true; }
   1.154 +    bool asmJS() const { return asmJS_; }
   1.155 +
   1.156 +    Directives &operator=(Directives rhs) {
   1.157 +        strict_ = rhs.strict_;
   1.158 +        asmJS_ = rhs.asmJS_;
   1.159 +        return *this;
   1.160 +    }
   1.161 +    bool operator==(const Directives &rhs) const {
   1.162 +        return strict_ == rhs.strict_ && asmJS_ == rhs.asmJS_;
   1.163 +    }
   1.164 +    bool operator!=(const Directives &rhs) const {
   1.165 +        return !(*this == rhs);
   1.166 +    }
   1.167 +};
   1.168 +
   1.169 +/*
   1.170 + * The struct SharedContext is part of the current parser context (see
   1.171 + * ParseContext). It stores information that is reused between the parser and
   1.172 + * the bytecode emitter. Note however, that this information is not shared
   1.173 + * between the two; they simply reuse the same data structure.
   1.174 + */
   1.175 +class SharedContext
   1.176 +{
   1.177 +  public:
   1.178 +    ExclusiveContext *const context;
   1.179 +    AnyContextFlags anyCxFlags;
   1.180 +    bool strict;
   1.181 +    bool extraWarnings;
   1.182 +
   1.183 +    // If it's function code, funbox must be non-nullptr and scopeChain must be
   1.184 +    // nullptr. If it's global code, funbox must be nullptr.
   1.185 +    SharedContext(ExclusiveContext *cx, Directives directives, bool extraWarnings)
   1.186 +      : context(cx),
   1.187 +        anyCxFlags(),
   1.188 +        strict(directives.strict()),
   1.189 +        extraWarnings(extraWarnings)
   1.190 +    {}
   1.191 +
   1.192 +    virtual ObjectBox *toObjectBox() = 0;
   1.193 +    inline bool isGlobalSharedContext() { return toObjectBox() == nullptr; }
   1.194 +    inline bool isFunctionBox() { return toObjectBox() && toObjectBox()->isFunctionBox(); }
   1.195 +    inline GlobalSharedContext *asGlobalSharedContext();
   1.196 +    inline FunctionBox *asFunctionBox();
   1.197 +
   1.198 +    bool hasExplicitUseStrict()        const { return anyCxFlags.hasExplicitUseStrict; }
   1.199 +    bool bindingsAccessedDynamically() const { return anyCxFlags.bindingsAccessedDynamically; }
   1.200 +    bool hasDebuggerStatement()        const { return anyCxFlags.hasDebuggerStatement; }
   1.201 +
   1.202 +    void setExplicitUseStrict()           { anyCxFlags.hasExplicitUseStrict        = true; }
   1.203 +    void setBindingsAccessedDynamically() { anyCxFlags.bindingsAccessedDynamically = true; }
   1.204 +    void setHasDebuggerStatement()        { anyCxFlags.hasDebuggerStatement        = true; }
   1.205 +
   1.206 +    inline bool allLocalsAliased();
   1.207 +
   1.208 +    // JSOPTION_EXTRA_WARNINGS warnings or strict mode errors.
   1.209 +    bool needStrictChecks() {
   1.210 +        return strict || extraWarnings;
   1.211 +    }
   1.212 +};
   1.213 +
   1.214 +class GlobalSharedContext : public SharedContext
   1.215 +{
   1.216 +  private:
   1.217 +    const RootedObject scopeChain_; /* scope chain object for the script */
   1.218 +
   1.219 +  public:
   1.220 +    GlobalSharedContext(ExclusiveContext *cx, JSObject *scopeChain,
   1.221 +                        Directives directives, bool extraWarnings)
   1.222 +      : SharedContext(cx, directives, extraWarnings),
   1.223 +        scopeChain_(cx, scopeChain)
   1.224 +    {}
   1.225 +
   1.226 +    ObjectBox *toObjectBox() { return nullptr; }
   1.227 +    JSObject *scopeChain() const { return scopeChain_; }
   1.228 +};
   1.229 +
   1.230 +inline GlobalSharedContext *
   1.231 +SharedContext::asGlobalSharedContext()
   1.232 +{
   1.233 +    JS_ASSERT(isGlobalSharedContext());
   1.234 +    return static_cast<GlobalSharedContext*>(this);
   1.235 +}
   1.236 +
   1.237 +class FunctionBox : public ObjectBox, public SharedContext
   1.238 +{
   1.239 +  public:
   1.240 +    Bindings        bindings;               /* bindings for this function */
   1.241 +    uint32_t        bufStart;
   1.242 +    uint32_t        bufEnd;
   1.243 +    uint32_t        startLine;
   1.244 +    uint32_t        startColumn;
   1.245 +    uint16_t        length;
   1.246 +
   1.247 +    uint8_t         generatorKindBits_;     /* The GeneratorKind of this function. */
   1.248 +    bool            inWith:1;               /* some enclosing scope is a with-statement */
   1.249 +    bool            inGenexpLambda:1;       /* lambda from generator expression */
   1.250 +    bool            hasDestructuringArgs:1; /* arguments list contains destructuring expression */
   1.251 +    bool            useAsm:1;               /* function contains "use asm" directive */
   1.252 +    bool            insideUseAsm:1;         /* nested function of function of "use asm" directive */
   1.253 +
   1.254 +    // Fields for use in heuristics.
   1.255 +    bool            usesArguments:1;  /* contains a free use of 'arguments' */
   1.256 +    bool            usesApply:1;      /* contains an f.apply() call */
   1.257 +
   1.258 +    FunctionContextFlags funCxFlags;
   1.259 +
   1.260 +    template <typename ParseHandler>
   1.261 +    FunctionBox(ExclusiveContext *cx, ObjectBox* traceListHead, JSFunction *fun,
   1.262 +                ParseContext<ParseHandler> *pc, Directives directives,
   1.263 +                bool extraWarnings, GeneratorKind generatorKind);
   1.264 +
   1.265 +    ObjectBox *toObjectBox() { return this; }
   1.266 +    JSFunction *function() const { return &object->as<JSFunction>(); }
   1.267 +
   1.268 +    GeneratorKind generatorKind() const { return GeneratorKindFromBits(generatorKindBits_); }
   1.269 +    bool isGenerator() const { return generatorKind() != NotGenerator; }
   1.270 +    bool isLegacyGenerator() const { return generatorKind() == LegacyGenerator; }
   1.271 +    bool isStarGenerator() const { return generatorKind() == StarGenerator; }
   1.272 +
   1.273 +    void setGeneratorKind(GeneratorKind kind) {
   1.274 +        // A generator kind can be set at initialization, or when "yield" is
   1.275 +        // first seen.  In both cases the transition can only happen from
   1.276 +        // NotGenerator.
   1.277 +        JS_ASSERT(!isGenerator());
   1.278 +        generatorKindBits_ = GeneratorKindAsBits(kind);
   1.279 +    }
   1.280 +
   1.281 +    bool mightAliasLocals()         const { return funCxFlags.mightAliasLocals; }
   1.282 +    bool hasExtensibleScope()       const { return funCxFlags.hasExtensibleScope; }
   1.283 +    bool needsDeclEnvObject()       const { return funCxFlags.needsDeclEnvObject; }
   1.284 +    bool argumentsHasLocalBinding() const { return funCxFlags.argumentsHasLocalBinding; }
   1.285 +    bool definitelyNeedsArgsObj()   const { return funCxFlags.definitelyNeedsArgsObj; }
   1.286 +
   1.287 +    void setMightAliasLocals()             { funCxFlags.mightAliasLocals         = true; }
   1.288 +    void setHasExtensibleScope()           { funCxFlags.hasExtensibleScope       = true; }
   1.289 +    void setNeedsDeclEnvObject()           { funCxFlags.needsDeclEnvObject       = true; }
   1.290 +    void setArgumentsHasLocalBinding()     { funCxFlags.argumentsHasLocalBinding = true; }
   1.291 +    void setDefinitelyNeedsArgsObj()       { JS_ASSERT(funCxFlags.argumentsHasLocalBinding);
   1.292 +                                             funCxFlags.definitelyNeedsArgsObj   = true; }
   1.293 +
   1.294 +    bool hasDefaults() const {
   1.295 +        return length != function()->nargs() - function()->hasRest();
   1.296 +    }
   1.297 +
   1.298 +    // Return whether this function has either specified "use asm" or is
   1.299 +    // (transitively) nested inside a function that has.
   1.300 +    bool useAsmOrInsideUseAsm() const {
   1.301 +        return useAsm || insideUseAsm;
   1.302 +    }
   1.303 +
   1.304 +    void setStart(const TokenStream &tokenStream) {
   1.305 +        bufStart = tokenStream.currentToken().pos.begin;
   1.306 +        startLine = tokenStream.getLineno();
   1.307 +        startColumn = tokenStream.getColumn();
   1.308 +    }
   1.309 +
   1.310 +    bool isHeavyweight()
   1.311 +    {
   1.312 +        // Note: this should be kept in sync with JSFunction::isHeavyweight().
   1.313 +        return bindings.hasAnyAliasedBindings() ||
   1.314 +               hasExtensibleScope() ||
   1.315 +               needsDeclEnvObject() ||
   1.316 +               isGenerator();
   1.317 +    }
   1.318 +};
   1.319 +
   1.320 +inline FunctionBox *
   1.321 +SharedContext::asFunctionBox()
   1.322 +{
   1.323 +    JS_ASSERT(isFunctionBox());
   1.324 +    return static_cast<FunctionBox*>(this);
   1.325 +}
   1.326 +
   1.327 +// In generators, we treat all locals as aliased so that they get stored on the
   1.328 +// heap.  This way there is less information to copy off the stack when
   1.329 +// suspending, and back on when resuming.  It also avoids the need to create and
   1.330 +// invalidate DebugScope proxies for unaliased locals in a generator frame, as
   1.331 +// the generator frame will be copied out to the heap and released only by GC.
   1.332 +inline bool
   1.333 +SharedContext::allLocalsAliased()
   1.334 +{
   1.335 +    return bindingsAccessedDynamically() || (isFunctionBox() && asFunctionBox()->isGenerator());
   1.336 +}
   1.337 +
   1.338 +
   1.339 +/*
   1.340 + * NB: If you add a new type of statement that is a scope, add it between
   1.341 + * STMT_WITH and STMT_CATCH, or you will break StmtInfoBase::linksScope. If you
   1.342 + * add a non-looping statement type, add it before STMT_DO_LOOP or you will
   1.343 + * break StmtInfoBase::isLoop().
   1.344 + *
   1.345 + * Also remember to keep the statementName array in BytecodeEmitter.cpp in
   1.346 + * sync.
   1.347 + */
   1.348 +enum StmtType {
   1.349 +    STMT_LABEL,                 /* labeled statement:  L: s */
   1.350 +    STMT_IF,                    /* if (then) statement */
   1.351 +    STMT_ELSE,                  /* else clause of if statement */
   1.352 +    STMT_SEQ,                   /* synthetic sequence of statements */
   1.353 +    STMT_BLOCK,                 /* compound statement: { s1[;... sN] } */
   1.354 +    STMT_SWITCH,                /* switch statement */
   1.355 +    STMT_WITH,                  /* with statement */
   1.356 +    STMT_CATCH,                 /* catch block */
   1.357 +    STMT_TRY,                   /* try block */
   1.358 +    STMT_FINALLY,               /* finally block */
   1.359 +    STMT_SUBROUTINE,            /* gosub-target subroutine body */
   1.360 +    STMT_DO_LOOP,               /* do/while loop statement */
   1.361 +    STMT_FOR_LOOP,              /* for loop statement */
   1.362 +    STMT_FOR_IN_LOOP,           /* for/in loop statement */
   1.363 +    STMT_FOR_OF_LOOP,           /* for/of loop statement */
   1.364 +    STMT_WHILE_LOOP,            /* while loop statement */
   1.365 +    STMT_LIMIT
   1.366 +};
   1.367 +
   1.368 +/*
   1.369 + * A comment on the encoding of the js::StmtType enum and StmtInfoBase
   1.370 + * type-testing methods:
   1.371 + *
   1.372 + * StmtInfoBase::maybeScope() tells whether a statement type is always, or may
   1.373 + * become, a lexical scope. It therefore includes block and switch (the two
   1.374 + * low-numbered "maybe" scope types) and excludes with (with has dynamic scope
   1.375 + * pending the "reformed with" in ES4/JS2). It includes all try-catch-finally
   1.376 + * types, which are high-numbered maybe-scope types.
   1.377 + *
   1.378 + * StmtInfoBase::linksScope() tells whether a js::StmtInfo{PC,BCE} of the given
   1.379 + * type eagerly links to other scoping statement info records. It excludes the
   1.380 + * two early "maybe" types, block and switch, as well as the try and both
   1.381 + * finally types, since try and the other trailing maybe-scope types don't need
   1.382 + * block scope unless they contain let declarations.
   1.383 + *
   1.384 + * We treat WITH as a static scope because it prevents lexical binding from
   1.385 + * continuing further up the static scope chain. With the lost "reformed with"
   1.386 + * proposal for ES4, we would be able to model it statically, too.
   1.387 + */
   1.388 +
   1.389 +// StmtInfoPC is used by the Parser.  StmtInfoBCE is used by the
   1.390 +// BytecodeEmitter.  The two types have some overlap, encapsulated by
   1.391 +// StmtInfoBase.  Several functions below (e.g. PushStatement) are templated to
   1.392 +// work with both types.
   1.393 +
   1.394 +struct StmtInfoBase {
   1.395 +    // Statement type (StmtType).
   1.396 +    uint16_t        type;
   1.397 +
   1.398 +    // True if type is STMT_BLOCK, STMT_TRY, STMT_SWITCH, or STMT_FINALLY and
   1.399 +    // the block contains at least one let-declaration, or if type is
   1.400 +    // STMT_CATCH.
   1.401 +    bool isBlockScope:1;
   1.402 +
   1.403 +    // True if isBlockScope or type == STMT_WITH.
   1.404 +    bool isNestedScope:1;
   1.405 +
   1.406 +    // for (let ...) induced block scope
   1.407 +    bool isForLetBlock:1;
   1.408 +
   1.409 +    // Block label.
   1.410 +    RootedAtom      label;
   1.411 +
   1.412 +    // Compile-time scope chain node for this scope.  Only set if
   1.413 +    // isNestedScope.
   1.414 +    Rooted<NestedScopeObject *> staticScope;
   1.415 +
   1.416 +    StmtInfoBase(ExclusiveContext *cx)
   1.417 +        : isBlockScope(false), isNestedScope(false), isForLetBlock(false),
   1.418 +          label(cx), staticScope(cx)
   1.419 +    {}
   1.420 +
   1.421 +    bool maybeScope() const {
   1.422 +        return STMT_BLOCK <= type && type <= STMT_SUBROUTINE && type != STMT_WITH;
   1.423 +    }
   1.424 +
   1.425 +    bool linksScope() const {
   1.426 +        return isNestedScope;
   1.427 +    }
   1.428 +
   1.429 +    StaticBlockObject& staticBlock() const {
   1.430 +        JS_ASSERT(isNestedScope);
   1.431 +        JS_ASSERT(isBlockScope);
   1.432 +        return staticScope->as<StaticBlockObject>();
   1.433 +    }
   1.434 +
   1.435 +    bool isLoop() const {
   1.436 +        return type >= STMT_DO_LOOP;
   1.437 +    }
   1.438 +
   1.439 +    bool isTrying() const {
   1.440 +        return STMT_TRY <= type && type <= STMT_SUBROUTINE;
   1.441 +    }
   1.442 +};
   1.443 +
   1.444 +// Push the C-stack-allocated struct at stmt onto the StmtInfoPC stack.
   1.445 +template <class ContextT>
   1.446 +void
   1.447 +PushStatement(ContextT *ct, typename ContextT::StmtInfo *stmt, StmtType type)
   1.448 +{
   1.449 +    stmt->type = type;
   1.450 +    stmt->isBlockScope = false;
   1.451 +    stmt->isNestedScope = false;
   1.452 +    stmt->isForLetBlock = false;
   1.453 +    stmt->label = nullptr;
   1.454 +    stmt->staticScope = nullptr;
   1.455 +    stmt->down = ct->topStmt;
   1.456 +    ct->topStmt = stmt;
   1.457 +    if (stmt->linksScope()) {
   1.458 +        stmt->downScope = ct->topScopeStmt;
   1.459 +        ct->topScopeStmt = stmt;
   1.460 +    } else {
   1.461 +        stmt->downScope = nullptr;
   1.462 +    }
   1.463 +}
   1.464 +
   1.465 +template <class ContextT>
   1.466 +void
   1.467 +FinishPushNestedScope(ContextT *ct, typename ContextT::StmtInfo *stmt, NestedScopeObject &staticScope)
   1.468 +{
   1.469 +    stmt->isNestedScope = true;
   1.470 +    stmt->downScope = ct->topScopeStmt;
   1.471 +    ct->topScopeStmt = stmt;
   1.472 +    ct->staticScope = &staticScope;
   1.473 +    stmt->staticScope = &staticScope;
   1.474 +}
   1.475 +
   1.476 +// Pop pc->topStmt. If the top StmtInfoPC struct is not stack-allocated, it
   1.477 +// is up to the caller to free it.  The dummy argument is just to make the
   1.478 +// template matching work.
   1.479 +template <class ContextT>
   1.480 +void
   1.481 +FinishPopStatement(ContextT *ct)
   1.482 +{
   1.483 +    typename ContextT::StmtInfo *stmt = ct->topStmt;
   1.484 +    ct->topStmt = stmt->down;
   1.485 +    if (stmt->linksScope()) {
   1.486 +        ct->topScopeStmt = stmt->downScope;
   1.487 +        if (stmt->isNestedScope) {
   1.488 +            JS_ASSERT(stmt->staticScope);
   1.489 +            ct->staticScope = stmt->staticScope->enclosingNestedScope();
   1.490 +        }
   1.491 +    }
   1.492 +}
   1.493 +
   1.494 +/*
   1.495 + * Find a lexically scoped variable (one declared by let, catch, or an array
   1.496 + * comprehension) named by atom, looking in sc's compile-time scopes.
   1.497 + *
   1.498 + * If a WITH statement is reached along the scope stack, return its statement
   1.499 + * info record, so callers can tell that atom is ambiguous. If slotp is not
   1.500 + * null, then if atom is found, set *slotp to its stack slot, otherwise to -1.
   1.501 + * This means that if slotp is not null, all the block objects on the lexical
   1.502 + * scope chain must have had their depth slots computed by the code generator,
   1.503 + * so the caller must be under EmitTree.
   1.504 + *
   1.505 + * In any event, directly return the statement info record in which atom was
   1.506 + * found. Otherwise return null.
   1.507 + */
   1.508 +template <class ContextT>
   1.509 +typename ContextT::StmtInfo *
   1.510 +LexicalLookup(ContextT *ct, HandleAtom atom, int *slotp, typename ContextT::StmtInfo *stmt);
   1.511 +
   1.512 +} // namespace frontend
   1.513 +
   1.514 +} // namespace js
   1.515 +
   1.516 +#endif /* frontend_SharedContext_h */

mercurial