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