diff options
Diffstat (limited to '')
-rw-r--r-- | js/src/frontend/CallOrNewEmitter.h | 318 |
1 files changed, 318 insertions, 0 deletions
diff --git a/js/src/frontend/CallOrNewEmitter.h b/js/src/frontend/CallOrNewEmitter.h new file mode 100644 index 0000000000..9469a751a2 --- /dev/null +++ b/js/src/frontend/CallOrNewEmitter.h @@ -0,0 +1,318 @@ +/* -*- 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_CallOrNewEmitter_h +#define frontend_CallOrNewEmitter_h + +#include "mozilla/Attributes.h" +#include "mozilla/Maybe.h" + +#include <stdint.h> + +#include "frontend/ElemOpEmitter.h" +#include "frontend/IfEmitter.h" +#include "frontend/PropOpEmitter.h" +#include "frontend/ValueUsage.h" +#include "js/TypeDecls.h" +#include "vm/BytecodeUtil.h" +#include "vm/Opcodes.h" + +namespace js { +namespace frontend { + +struct BytecodeEmitter; + +// Class for emitting bytecode for call or new expression. +// +// Usage: (check for the return value is omitted for simplicity) +// +// `print(arg);` +// CallOrNewEmitter cone(this, JSOp::Call, +// CallOrNewEmitter::ArgumentsKind::Other, +// ValueUsage::WantValue); +// cone.emitNameCallee(print); +// cone.emitThis(); +// cone.prepareForNonSpreadArguments(); +// emit(arg); +// cone.emitEnd(1, Some(offset_of_callee)); +// +// `callee.prop(arg1, arg2);` +// CallOrNewEmitter cone(this, JSOp::Call, +// CallOrNewEmitter::ArgumentsKind::Other, +// ValueUsage::WantValue); +// PropOpEmitter& poe = cone.prepareForPropCallee(false); +// ... emit `callee.prop` with `poe` here... +// cone.emitThis(); +// cone.prepareForNonSpreadArguments(); +// emit(arg1); +// emit(arg2); +// cone.emitEnd(2, Some(offset_of_callee)); +// +// `callee[key](arg);` +// CallOrNewEmitter cone(this, JSOp::Call, +// CallOrNewEmitter::ArgumentsKind::Other, +// ValueUsage::WantValue); +// ElemOpEmitter& eoe = cone.prepareForElemCallee(false); +// ... emit `callee[key]` with `eoe` here... +// cone.emitThis(); +// cone.prepareForNonSpreadArguments(); +// emit(arg); +// cone.emitEnd(1, Some(offset_of_callee)); +// +// `(function() { ... })(arg);` +// CallOrNewEmitter cone(this, JSOp::Call, +// CallOrNewEmitter::ArgumentsKind::Other, +// ValueUsage::WantValue); +// cone.prepareForFunctionCallee(); +// emit(function); +// cone.emitThis(); +// cone.prepareForNonSpreadArguments(); +// emit(arg); +// cone.emitEnd(1, Some(offset_of_callee)); +// +// `super(arg);` +// CallOrNewEmitter cone(this, JSOp::Call, +// CallOrNewEmitter::ArgumentsKind::Other, +// ValueUsage::WantValue); +// cone.emitSuperCallee(); +// cone.emitThis(); +// cone.prepareForNonSpreadArguments(); +// emit(arg); +// cone.emitEnd(1, Some(offset_of_callee)); +// +// `(some_other_expression)(arg);` +// CallOrNewEmitter cone(this, JSOp::Call, +// CallOrNewEmitter::ArgumentsKind::Other, +// ValueUsage::WantValue); +// cone.prepareForOtherCallee(); +// emit(some_other_expression); +// cone.emitThis(); +// cone.prepareForNonSpreadArguments(); +// emit(arg); +// cone.emitEnd(1, Some(offset_of_callee)); +// +// `print(...arg);` +// CallOrNewEmitter cone(this, JSOp::SpreadCall, +// CallOrNewEmitter::ArgumentsKind::Other, +// ValueUsage::WantValue); +// cone.emitNameCallee(print); +// cone.emitThis(); +// if (cone.wantSpreadOperand()) { +// emit(arg) +// } +// cone.emitSpreadArgumentsTest(); +// emit([...arg]); +// cone.emitEnd(1, Some(offset_of_callee)); +// +// `print(...rest);` +// where `rest` is rest parameter +// CallOrNewEmitter cone(this, JSOp::SpreadCall, +// CallOrNewEmitter::ArgumentsKind::SingleSpreadRest, +// ValueUsage::WantValue); +// cone.emitNameCallee(print); +// cone.emitThis(); +// if (cone.wantSpreadOperand()) { +// emit(arg) +// } +// cone.emitSpreadArgumentsTest(); +// emit([...arg]); +// cone.emitEnd(1, Some(offset_of_callee)); +// +// `new f(arg);` +// CallOrNewEmitter cone(this, JSOp::New, +// CallOrNewEmitter::ArgumentsKind::Other, +// ValueUsage::WantValue); +// cone.emitNameCallee(f); +// cone.emitThis(); +// cone.prepareForNonSpreadArguments(); +// emit(arg); +// cone.emitEnd(1, Some(offset_of_callee)); +// +class MOZ_STACK_CLASS CallOrNewEmitter { + public: + enum class ArgumentsKind { + Other, + + // Specify this for the following case: + // + // function f(...rest) { + // g(...rest); + // } + // + // This enables optimization to avoid allocating an intermediate array + // for spread operation. + // + // wantSpreadOperand() returns true when this is specified. + SingleSpreadRest + }; + + private: + BytecodeEmitter* bce_; + + // The opcode for the call or new. + JSOp op_; + + // Whether the call is a spread call with single rest parameter or not. + // See the comment in emitSpreadArgumentsTest for more details. + ArgumentsKind argumentsKind_; + + // The branch for spread call optimization. + mozilla::Maybe<InternalIfEmitter> ifNotOptimizable_; + + mozilla::Maybe<PropOpEmitter> poe_; + mozilla::Maybe<ElemOpEmitter> eoe_; + + // The state of this emitter. + // + // +-------+ emitNameCallee +------------+ + // | Start |-+------------------------->| NameCallee |------+ + // +-------+ | +------------+ | + // | | + // | prepareForPropCallee +------------+ v + // +------------------------->| PropCallee |----->+ + // | +------------+ | + // | | + // | prepareForElemCallee +------------+ v + // +------------------------->| ElemCallee |----->+ + // | +------------+ | + // | | + // | prepareForFunctionCallee +----------------+ v + // +------------------------->| FunctionCallee |->+ + // | +----------------+ | + // | | + // | emitSuperCallee +-------------+ v + // +------------------------->| SuperCallee |---->+ + // | +-------------+ | + // | | + // | prepareForOtherCallee +-------------+ v + // +------------------------->| OtherCallee |---->+ + // +-------------+ | + // | + // +--------------------------------------------------------+ + // | + // | emitThis +------+ + // +--------->| This |-+ + // +------+ | + // | + // +-------------------+ + // | + // | [!isSpread] + // | prepareForNonSpreadArguments +-----------+ emitEnd +-----+ + // +------------------------------->+->| Arguments |-------->| End | + // | ^ +-----------+ +-----+ + // | | + // | +----------------------------------+ + // | | + // | [isSpread] | + // | wantSpreadOperand +-------------------+ emitSpreadArgumentsTest | + // +-------------------->| WantSpreadOperand |-------------------------+ + // +-------------------+ + enum class State { + // The initial state. + Start, + + // After calling emitNameCallee. + NameCallee, + + // After calling prepareForPropCallee. + PropCallee, + + // After calling prepareForElemCallee. + ElemCallee, + + // After calling prepareForFunctionCallee. + FunctionCallee, + + // After calling emitSuperCallee. + SuperCallee, + + // After calling prepareForOtherCallee. + OtherCallee, + + // After calling emitThis. + This, + + // After calling wantSpreadOperand. + WantSpreadOperand, + + // After calling prepareForNonSpreadArguments. + Arguments, + + // After calling emitEnd. + End + }; + State state_ = State::Start; + + public: + CallOrNewEmitter(BytecodeEmitter* bce, JSOp op, ArgumentsKind argumentsKind, + ValueUsage valueUsage); + + private: + MOZ_MUST_USE bool isCall() const { + return op_ == JSOp::Call || op_ == JSOp::CallIgnoresRv || + op_ == JSOp::SpreadCall || isEval() || isFunApply() || isFunCall(); + } + + MOZ_MUST_USE bool isNew() const { + return op_ == JSOp::New || op_ == JSOp::SpreadNew; + } + + MOZ_MUST_USE bool isSuperCall() const { + return op_ == JSOp::SuperCall || op_ == JSOp::SpreadSuperCall; + } + + MOZ_MUST_USE bool isEval() const { + return op_ == JSOp::Eval || op_ == JSOp::StrictEval || + op_ == JSOp::SpreadEval || op_ == JSOp::StrictSpreadEval; + } + + MOZ_MUST_USE bool isFunApply() const { return op_ == JSOp::FunApply; } + + MOZ_MUST_USE bool isFunCall() const { return op_ == JSOp::FunCall; } + + MOZ_MUST_USE bool isSpread() const { return JOF_OPTYPE(op_) == JOF_BYTE; } + + MOZ_MUST_USE bool isSingleSpreadRest() const { + return argumentsKind_ == ArgumentsKind::SingleSpreadRest; + } + + public: + MOZ_MUST_USE bool emitNameCallee(const ParserAtom* name); + MOZ_MUST_USE PropOpEmitter& prepareForPropCallee(bool isSuperProp); + MOZ_MUST_USE ElemOpEmitter& prepareForElemCallee(bool isSuperElem, + bool isPrivateElem); + MOZ_MUST_USE bool prepareForFunctionCallee(); + MOZ_MUST_USE bool emitSuperCallee(); + MOZ_MUST_USE bool prepareForOtherCallee(); + + MOZ_MUST_USE bool emitThis(); + + // Used by BytecodeEmitter::emitPipeline to reuse CallOrNewEmitter instance + // across multiple chained calls. + void reset(); + + MOZ_MUST_USE bool prepareForNonSpreadArguments(); + + // See the usage in the comment at the top of the class. + MOZ_MUST_USE bool wantSpreadOperand(); + MOZ_MUST_USE bool emitSpreadArgumentsTest(); + + // Parameters are the offset in the source code for each character below: + // + // callee(arg); + // ^ + // | + // beginPos + // + // Can be Nothing() if not available. + MOZ_MUST_USE bool emitEnd(uint32_t argc, + const mozilla::Maybe<uint32_t>& beginPos); +}; + +} /* namespace frontend */ +} /* namespace js */ + +#endif /* frontend_CallOrNewEmitter_h */ |