diff options
Diffstat (limited to 'js/src/frontend/OptionalEmitter.h')
-rw-r--r-- | js/src/frontend/OptionalEmitter.h | 220 |
1 files changed, 220 insertions, 0 deletions
diff --git a/js/src/frontend/OptionalEmitter.h b/js/src/frontend/OptionalEmitter.h new file mode 100644 index 0000000000..4a4caa05c3 --- /dev/null +++ b/js/src/frontend/OptionalEmitter.h @@ -0,0 +1,220 @@ +/* -*- 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_OptionalEmitter_h +#define frontend_OptionalEmitter_h + +#include "mozilla/Attributes.h" + +#include "frontend/JumpList.h" +#include "frontend/TDZCheckCache.h" + +namespace js { +namespace frontend { + +struct BytecodeEmitter; + +// Class for emitting bytecode for optional expressions. +// +// Usage: (check for the return value is omitted for simplicity) +// +// `obj?.prop;` +// OptionalEmitter oe(this); +// PropOpEmitter poe(this, +// PropOpEmitter::Kind::Get, +// PropOpEmitter::ObjKind::Other); +// poe.prepareForObj(); +// emit(obj); +// oe.emitJumpShortCircuit(); +// poe.emitGet(atom_of_prop); +// oe.emitOptionalJumpTarget(JSOp::Undefined); +// +// `delete obj?.prop;` +// OptionalEmitter oe(this); +// OptionalPropOpEmitter poe(this, +// PropOpEmitter::Kind::Delete, +// PropOpEmitter::ObjKind::Other); +// poe.prepareForObj(); +// emit(obj); +// oe.emitJumpShortCircuit(); +// poe.emitDelete(atom_of_prop); +// oe.emitOptionalJumpTarget(JSOp:True); +// +// `obj?.[key];` +// OptionalEmitter oe(this); +// ElemOpEmitter eoe(this, +// ElemOpEmitter::Kind::Get, +// ElemOpEmitter::ObjKind::Other); +// eoe.prepareForObj(); +// emit(obj); +// oe.emitJumpShortCircuit(); +// eoe.prepareForKey(); +// emit(key); +// eoe.emitGet(); +// oe.emitOptionalJumpTarget(JSOp::Undefined); +// +// `delete obj?.[key];` +// OptionalEmitter oe(this); +// ElemOpEmitter eoe(this, +// ElemOpEmitter::Kind::Delete, +// ElemOpEmitter::ObjKind::Other); +// eoe.prepareForObj(); +// emit(obj); +// oe.emitJumpShortCircuit(); +// eoe.prepareForKey(); +// emit(key); +// eoe.emitDelete(); +// oe.emitOptionalJumpTarget(JSOp::True); +// +// `print?.(arg);` +// OptionalEmitter oe(this); +// CallOrNewEmitter cone(this, JSOp::Call, +// CallOrNewEmitter::ArgumentsKind::Other, +// ValueUsage::WantValue); +// cone.emitNameCallee(print); +// cone.emitThis(); +// oe.emitShortCircuitForCall(); +// cone.prepareForNonSpreadArguments(); +// emit(arg); +// cone.emitEnd(1, offset_of_callee); +// oe.emitOptionalJumpTarget(JSOp::Undefined); +// +// `callee.prop?.(arg1, arg2);` +// OptionalEmitter oe(this); +// CallOrNewEmitter cone(this, JSOp::Call, +// CallOrNewEmitter::ArgumentsKind::Other, +// ValueUsage::WantValue); +// PropOpEmitter& poe = cone.prepareForPropCallee(false); +// ... emit `callee.prop` with `poe` here... +// cone.emitThis(); +// oe.emitShortCircuitForCall(); +// cone.prepareForNonSpreadArguments(); +// emit(arg1); +// emit(arg2); +// cone.emitEnd(2, offset_of_callee); +// oe.emitOptionalJumpTarget(JSOp::Undefined); +// +// `callee[key]?.(arg);` +// OptionalEmitter oe(this); +// CallOrNewEmitter cone(this, JSOp::Call, +// CallOrNewEmitter::ArgumentsKind::Other, +// ValueUsage::WantValue); +// ElemOpEmitter& eoe = cone.prepareForElemCallee(false); +// ... emit `callee[key]` with `eoe` here... +// cone.emitThis(); +// oe.emitShortCircuitForCall(); +// cone.prepareForNonSpreadArguments(); +// emit(arg); +// cone.emitEnd(1, offset_of_callee); +// oe.emitOptionalJumpTarget(JSOp::Undefined); +// +// `(function() { ... })?.(arg);` +// OptionalEmitter oe(this); +// CallOrNewEmitter cone(this, JSOp::Call, +// CallOrNewEmitter::ArgumentsKind::Other, +// ValueUsage::WantValue); +// cone.prepareForFunctionCallee(); +// emit(function); +// cone.emitThis(); +// oe.emitShortCircuitForCall(); +// cone.prepareForNonSpreadArguments(); +// emit(arg); +// cone.emitEnd(1, offset_of_callee); +// oe.emitOptionalJumpTarget(JSOp::Undefined); +// +// `(a?b)();` +// OptionalEmitter oe(this); +// CallOrNewEmitter cone(this, JSOP_CALL, +// CallOrNewEmitter::ArgumentsKind::Other, +// ValueUsage::WantValue); +// cone.prepareForFunctionCallee(); +// emit(optionalChain); +// cone.emitThis(); +// oe.emitOptionalJumpTarget(JSOp::Undefined, +// OptionalEmitter::Kind::Reference); +// oe.emitShortCircuitForCall(); +// cone.prepareForNonSpreadArguments(); +// emit(arg); +// cone.emitEnd(1, offset_of_callee); +// oe.emitOptionalJumpTarget(JSOp::Undefined); +// +class MOZ_RAII OptionalEmitter { + public: + OptionalEmitter(BytecodeEmitter* bce, int32_t initialDepth); + + private: + BytecodeEmitter* bce_; + + TDZCheckCache tdzCache_; + + // jumptarget for ShortCircuiting code, which has null or undefined values + JumpList jumpShortCircuit_; + + // jumpTarget for code that does not shortCircuit + JumpList jumpFinish_; + + // jumpTarget for code that does not shortCircuit + int32_t initialDepth_; + + // The state of this emitter. + // + // +-------+ emitJumpShortCircuit +--------------+ + // | Start |-+---------------------------->| ShortCircuit |-----------+ + // +-------+ | +--------------+ | + // +----->| | + // | | emitJumpShortCircuitForCall +---------------------+ v + // | +---------------------------->| ShortCircuitForCall |--->+ + // | +---------------------+ | + // | | + // ---------------------------------------------------------------+ + // | + // | + // +------------------------------------------------------------------+ + // | + // | emitOptionalJumpTarget +---------+ + // +----------------------->| JumpEnd | + // +---------+ + // +#ifdef DEBUG + enum class State { + // The initial state. + Start, + + // for shortcircuiting in most cases. + ShortCircuit, + + // for shortcircuiting from references, which have two items on + // the stack. For example function calls. + ShortCircuitForCall, + + // internally used, end of the jump code + JumpEnd + }; + + State state_ = State::Start; +#endif + + public: + enum class Kind { + // Requires two values on the stack + Reference, + // Requires one value on the stack + Other + }; + + [[nodiscard]] bool emitJumpShortCircuit(); + [[nodiscard]] bool emitJumpShortCircuitForCall(); + + // JSOp is the op code to be emitted, Kind is if we are dealing with a + // reference (in which case we need two elements on the stack) or other value + // (which needs one element on the stack) + [[nodiscard]] bool emitOptionalJumpTarget(JSOp op, Kind kind = Kind::Other); +}; + +} /* namespace frontend */ +} /* namespace js */ + +#endif /* frontend_OptionalEmitter_h */ |