diff options
Diffstat (limited to 'js/src/frontend/CForEmitter.h')
-rw-r--r-- | js/src/frontend/CForEmitter.h | 175 |
1 files changed, 175 insertions, 0 deletions
diff --git a/js/src/frontend/CForEmitter.h b/js/src/frontend/CForEmitter.h new file mode 100644 index 0000000000..231dd318be --- /dev/null +++ b/js/src/frontend/CForEmitter.h @@ -0,0 +1,175 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef frontend_CForEmitter_h +#define frontend_CForEmitter_h + +#include "mozilla/Attributes.h" // MOZ_STACK_CLASS +#include "mozilla/Maybe.h" // mozilla::Maybe + +#include <stdint.h> // uint32_t + +#include "frontend/BytecodeControlStructures.h" // LoopControl +#include "frontend/TDZCheckCache.h" // TDZCheckCache + +namespace js { +namespace frontend { + +struct BytecodeEmitter; +class EmitterScope; + +// Class for emitting bytecode for c-style for block. +// +// Usage: (check for the return value is omitted for simplicity) +// +// `for (init; cond; update) body` +// CForEmitter cfor(this, headLexicalEmitterScopeForLet or nullptr); +// cfor.emitInit(Some(offset_of_init)); +// emit(init); // without pushing value +// cfor.emitCond(Some(offset_of_cond)); +// emit(cond); +// cfor.emitBody(CForEmitter::Cond::Present); +// emit(body); +// cfor.emitUpdate(CForEmitter::Update::Present, Some(offset_of_update))); +// emit(update); +// cfor.emitEnd(offset_of_for); +// +// `for (;;) body` +// CForEmitter cfor(this, nullptr); +// cfor.emitInit(Nothing()); +// cfor.emitCond(Nothing()); +// cfor.emitBody(CForEmitter::Cond::Missing); +// emit(body); +// cfor.emitUpdate(CForEmitter::Update::Missing, Nothing()); +// cfor.emitEnd(offset_of_for); +// +class MOZ_STACK_CLASS CForEmitter { + // Basic structure of the bytecode (not complete). + // + // If `cond` is not empty: + // {init} + // loop: + // JSOp::LoopHead + // {cond} + // JSOp::JumpIfFalse break + // {body} + // continue: + // {update} + // JSOp::Goto loop + // break: + // + // If `cond` is empty: + // {init} + // loop: + // JSOp::LoopHead + // {body} + // continue: + // {update} + // JSOp::Goto loop + // break: + // + public: + enum class Cond { Missing, Present }; + enum class Update { Missing, Present }; + + private: + BytecodeEmitter* bce_; + + // Whether the c-style for loop has `cond` and `update`. + Cond cond_ = Cond::Missing; + Update update_ = Update::Missing; + + mozilla::Maybe<LoopControl> loopInfo_; + + // The lexical scope to be freshened for each iteration. + // See the comment in `emitCond` for more details. + // + // ### Scope freshening + // + // Each iteration of a `for (let V...)` loop creates a fresh loop variable + // binding for V, even if the loop is a C-style `for(;;)` loop: + // + // var funcs = []; + // for (let i = 0; i < 2; i++) + // funcs.push(function() { return i; }); + // assertEq(funcs[0](), 0); // the two closures capture... + // assertEq(funcs[1](), 1); // ...two different `i` bindings + // + // This is implemented by "freshening" the implicit block -- changing the + // scope chain to a fresh clone of the instantaneous block object -- each + // iteration, just before evaluating the "update" in for(;;) loops. + // + // ECMAScript doesn't freshen in `for (const ...;;)`. Lack of freshening + // isn't directly observable in-language because `const`s can't be mutated, + // but it *can* be observed in the Debugger API. + const EmitterScope* headLexicalEmitterScopeForLet_; + + mozilla::Maybe<TDZCheckCache> tdzCache_; + +#ifdef DEBUG + // The state of this emitter. + // + // +-------+ emitInit +------+ emitCond +------+ emitBody +------+ + // | Start |--------->| Init |--------->| Cond |--------->| Body |-+ + // +-------+ +------+ +------+ +------+ | + // | + // +-------------------------------------+ + // | + // | emitUpdate +--------+ emitEnd +-----+ + // +----------->| Update |-------->| End | + // +--------+ +-----+ + enum class State { + // The initial state. + Start, + + // After calling emitInit. + Init, + + // After calling emitCond. + Cond, + + // After calling emitBody. + Body, + + // After calling emitUpdate. + Update, + + // After calling emitEnd. + End + }; + State state_ = State::Start; +#endif + + public: + CForEmitter(BytecodeEmitter* bce, + const EmitterScope* headLexicalEmitterScopeForLet); + + // Parameters are the offset in the source code for each character below: + // + // for ( x = 10 ; x < 20 ; x ++ ) { f(x); } + // ^ ^ ^ ^ + // | | | | + // | | | updatePos + // | | | + // | | condPos + // | | + // | initPos + // | + // forPos + // + // Can be Nothing() if not available. + [[nodiscard]] bool emitInit(const mozilla::Maybe<uint32_t>& initPos); + [[nodiscard]] bool emitCond(const mozilla::Maybe<uint32_t>& condPos); + [[nodiscard]] bool emitBody(Cond cond); + [[nodiscard]] bool emitUpdate(Update update, + const mozilla::Maybe<uint32_t>& updatePos); + [[nodiscard]] bool emitEnd(uint32_t forPos); +}; + +} /* namespace frontend */ +} /* namespace js */ + +#endif /* frontend_CForEmitter_h */ |