diff options
Diffstat (limited to 'js/src/frontend/NameOpEmitter.cpp')
-rw-r--r-- | js/src/frontend/NameOpEmitter.cpp | 403 |
1 files changed, 403 insertions, 0 deletions
diff --git a/js/src/frontend/NameOpEmitter.cpp b/js/src/frontend/NameOpEmitter.cpp new file mode 100644 index 0000000000..19d4433b82 --- /dev/null +++ b/js/src/frontend/NameOpEmitter.cpp @@ -0,0 +1,403 @@ +/* -*- 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/. */ + +#include "frontend/NameOpEmitter.h" + +#include "frontend/AbstractScopePtr.h" +#include "frontend/BytecodeEmitter.h" +#include "frontend/SharedContext.h" +#include "frontend/TDZCheckCache.h" +#include "vm/Opcodes.h" +#include "vm/Scope.h" +#include "vm/StringType.h" + +using namespace js; +using namespace js::frontend; + +NameOpEmitter::NameOpEmitter(BytecodeEmitter* bce, const ParserAtom* name, + Kind kind) + : bce_(bce), kind_(kind), name_(name), loc_(bce_->lookupName(name_)) {} + +NameOpEmitter::NameOpEmitter(BytecodeEmitter* bce, const ParserAtom* name, + const NameLocation& loc, Kind kind) + : bce_(bce), kind_(kind), name_(name), loc_(loc) {} + +bool NameOpEmitter::emitGet() { + MOZ_ASSERT(state_ == State::Start); + + switch (loc_.kind()) { + case NameLocation::Kind::Dynamic: + if (!bce_->emitAtomOp(JSOp::GetName, name_)) { + // [stack] VAL + return false; + } + break; + case NameLocation::Kind::Global: + if (!bce_->emitAtomOp(JSOp::GetGName, name_)) { + // [stack] VAL + return false; + } + break; + case NameLocation::Kind::Intrinsic: + if (!bce_->emitAtomOp(JSOp::GetIntrinsic, name_)) { + // [stack] VAL + return false; + } + break; + case NameLocation::Kind::NamedLambdaCallee: + if (!bce_->emit1(JSOp::Callee)) { + // [stack] VAL + return false; + } + break; + case NameLocation::Kind::Import: + if (!bce_->emitAtomOp(JSOp::GetImport, name_)) { + // [stack] VAL + return false; + } + break; + case NameLocation::Kind::ArgumentSlot: + if (!bce_->emitArgOp(JSOp::GetArg, loc_.argumentSlot())) { + // [stack] VAL + return false; + } + break; + case NameLocation::Kind::FrameSlot: + if (!bce_->emitLocalOp(JSOp::GetLocal, loc_.frameSlot())) { + // [stack] VAL + return false; + } + if (loc_.isLexical()) { + if (!bce_->emitTDZCheckIfNeeded(name_, loc_, ValueIsOnStack::Yes)) { + // [stack] VAL + return false; + } + } + break; + case NameLocation::Kind::EnvironmentCoordinate: + if (!bce_->emitEnvCoordOp(JSOp::GetAliasedVar, + loc_.environmentCoordinate())) { + // [stack] VAL + return false; + } + if (loc_.isLexical()) { + if (!bce_->emitTDZCheckIfNeeded(name_, loc_, ValueIsOnStack::Yes)) { + // [stack] VAL + return false; + } + } + break; + case NameLocation::Kind::DynamicAnnexBVar: + MOZ_CRASH( + "Synthesized vars for Annex B.3.3 should only be used in " + "initialization"); + } + + if (isCall()) { + switch (loc_.kind()) { + case NameLocation::Kind::Dynamic: { + JSOp thisOp = bce_->needsImplicitThis() ? JSOp::ImplicitThis + : JSOp::GImplicitThis; + if (!bce_->emitAtomOp(thisOp, name_)) { + // [stack] CALLEE THIS + return false; + } + break; + } + case NameLocation::Kind::Global: + if (!bce_->emitAtomOp(JSOp::GImplicitThis, name_)) { + // [stack] CALLEE THIS + return false; + } + break; + case NameLocation::Kind::Intrinsic: + case NameLocation::Kind::NamedLambdaCallee: + case NameLocation::Kind::Import: + case NameLocation::Kind::ArgumentSlot: + case NameLocation::Kind::FrameSlot: + case NameLocation::Kind::EnvironmentCoordinate: + if (!bce_->emit1(JSOp::Undefined)) { + // [stack] CALLEE UNDEF + return false; + } + break; + case NameLocation::Kind::DynamicAnnexBVar: + MOZ_CRASH( + "Synthesized vars for Annex B.3.3 should only be used in " + "initialization"); + } + } + +#ifdef DEBUG + state_ = State::Get; +#endif + return true; +} + +bool NameOpEmitter::prepareForRhs() { + MOZ_ASSERT(state_ == State::Start); + + switch (loc_.kind()) { + case NameLocation::Kind::Dynamic: + case NameLocation::Kind::Import: + case NameLocation::Kind::DynamicAnnexBVar: + if (!bce_->makeAtomIndex(name_, &atomIndex_)) { + return false; + } + if (loc_.kind() == NameLocation::Kind::DynamicAnnexBVar) { + // Annex B vars always go on the nearest variable environment, + // even if lexical environments in between contain same-named + // bindings. + if (!bce_->emit1(JSOp::BindVar)) { + // [stack] ENV + return false; + } + } else { + if (!bce_->emitAtomOp(JSOp::BindName, atomIndex_)) { + // [stack] ENV + return false; + } + } + emittedBindOp_ = true; + break; + case NameLocation::Kind::Global: + if (!bce_->makeAtomIndex(name_, &atomIndex_)) { + return false; + } + if (loc_.isLexical() && isInitialize()) { + // InitGLexical always gets the global lexical scope. It doesn't + // need a BindGName. + MOZ_ASSERT(bce_->innermostScope().is<GlobalScope>()); + } else { + if (!bce_->emitAtomOp(JSOp::BindGName, atomIndex_)) { + // [stack] ENV + return false; + } + emittedBindOp_ = true; + } + break; + case NameLocation::Kind::Intrinsic: + break; + case NameLocation::Kind::NamedLambdaCallee: + break; + case NameLocation::Kind::ArgumentSlot: + break; + case NameLocation::Kind::FrameSlot: + break; + case NameLocation::Kind::EnvironmentCoordinate: + break; + } + + // For compound assignments, first get the LHS value, then emit + // the RHS and the op. + if (isCompoundAssignment() || isIncDec()) { + if (loc_.kind() == NameLocation::Kind::Dynamic) { + // For dynamic accesses we need to emit GetBoundName instead of + // GetName for correctness: looking up @@unscopables on the + // environment chain (due to 'with' environments) must only happen + // once. + // + // GetBoundName uses the environment already pushed on the stack + // from the earlier BindName. + if (!bce_->emit1(JSOp::Dup)) { + // [stack] ENV ENV + return false; + } + if (!bce_->emitAtomOp(JSOp::GetBoundName, name_)) { + // [stack] ENV V + return false; + } + } else { + if (!emitGet()) { + // [stack] ENV? V + return false; + } + } + } + +#ifdef DEBUG + state_ = State::Rhs; +#endif + return true; +} + +#if defined(__clang__) && defined(XP_WIN) && \ + (defined(_M_X64) || defined(__x86_64__)) +// Work around a CPU bug. See bug 1524257. +__attribute__((__aligned__(32))) +#endif +bool NameOpEmitter::emitAssignment() { + MOZ_ASSERT(state_ == State::Rhs); + + switch (loc_.kind()) { + case NameLocation::Kind::Dynamic: + case NameLocation::Kind::Import: + case NameLocation::Kind::DynamicAnnexBVar: + if (!bce_->emitAtomOp(bce_->strictifySetNameOp(JSOp::SetName), + atomIndex_)) { + return false; + } + break; + case NameLocation::Kind::Global: { + JSOp op; + if (emittedBindOp_) { + op = bce_->strictifySetNameOp(JSOp::SetGName); + } else { + op = JSOp::InitGLexical; + } + if (!bce_->emitAtomOp(op, atomIndex_)) { + return false; + } + break; + } + case NameLocation::Kind::Intrinsic: + if (!bce_->emitAtomOp(JSOp::SetIntrinsic, name_)) { + return false; + } + break; + case NameLocation::Kind::NamedLambdaCallee: + // Assigning to the named lambda is a no-op in sloppy mode but + // throws in strict mode. + if (bce_->sc->strict()) { + if (!bce_->emitAtomOp(JSOp::ThrowSetConst, name_)) { + return false; + } + } + break; + case NameLocation::Kind::ArgumentSlot: + if (!bce_->emitArgOp(JSOp::SetArg, loc_.argumentSlot())) { + return false; + } + break; + case NameLocation::Kind::FrameSlot: { + JSOp op = JSOp::SetLocal; + if (loc_.isLexical()) { + if (isInitialize()) { + op = JSOp::InitLexical; + } else { + if (loc_.isConst()) { + op = JSOp::ThrowSetConst; + } + if (!bce_->emitTDZCheckIfNeeded(name_, loc_, ValueIsOnStack::No)) { + return false; + } + } + } + if (op == JSOp::ThrowSetConst) { + if (!bce_->emitAtomOp(op, name_)) { + return false; + } + } else { + if (!bce_->emitLocalOp(op, loc_.frameSlot())) { + return false; + } + } + if (op == JSOp::InitLexical) { + if (!bce_->innermostTDZCheckCache->noteTDZCheck(bce_, name_, + DontCheckTDZ)) { + return false; + } + } + break; + } + case NameLocation::Kind::EnvironmentCoordinate: { + JSOp op = JSOp::SetAliasedVar; + if (loc_.isLexical()) { + if (isInitialize()) { + op = JSOp::InitAliasedLexical; + } else { + if (loc_.isConst()) { + op = JSOp::ThrowSetConst; + } + if (!bce_->emitTDZCheckIfNeeded(name_, loc_, ValueIsOnStack::No)) { + return false; + } + } + } + if (loc_.bindingKind() == BindingKind::NamedLambdaCallee) { + // Assigning to the named lambda is a no-op in sloppy mode and throws + // in strict mode. + op = JSOp::ThrowSetConst; + if (bce_->sc->strict()) { + if (!bce_->emitAtomOp(op, name_)) { + return false; + } + } + } else { + if (op == JSOp::ThrowSetConst) { + if (!bce_->emitAtomOp(op, name_)) { + return false; + } + } else { + if (!bce_->emitEnvCoordOp(op, loc_.environmentCoordinate())) { + return false; + } + } + } + if (op == JSOp::InitAliasedLexical) { + if (!bce_->innermostTDZCheckCache->noteTDZCheck(bce_, name_, + DontCheckTDZ)) { + return false; + } + } + break; + } + } + +#ifdef DEBUG + state_ = State::Assignment; +#endif + return true; +} + +bool NameOpEmitter::emitIncDec() { + MOZ_ASSERT(state_ == State::Start); + + JSOp incOp = isInc() ? JSOp::Inc : JSOp::Dec; + if (!prepareForRhs()) { + // [stack] ENV? V + return false; + } + if (!bce_->emit1(JSOp::ToNumeric)) { + // [stack] ENV? N + return false; + } + if (isPostIncDec()) { + if (!bce_->emit1(JSOp::Dup)) { + // [stack] ENV? N? N + return false; + } + } + if (!bce_->emit1(incOp)) { + // [stack] ENV? N? N+1 + return false; + } + if (isPostIncDec() && emittedBindOp()) { + if (!bce_->emit2(JSOp::Pick, 2)) { + // [stack] N? N+1 ENV? + return false; + } + if (!bce_->emit1(JSOp::Swap)) { + // [stack] N? ENV? N+1 + return false; + } + } + if (!emitAssignment()) { + // [stack] N? N+1 + return false; + } + if (isPostIncDec()) { + if (!bce_->emit1(JSOp::Pop)) { + // [stack] N + return false; + } + } + +#ifdef DEBUG + state_ = State::IncDec; +#endif + return true; +} |