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