375 lines
10 KiB
C++
375 lines
10 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
|
* 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_NameAnalysis_h
|
|
#define frontend_NameAnalysis_h
|
|
|
|
#include "jsopcode.h"
|
|
|
|
#include "vm/Scope.h"
|
|
|
|
namespace js {
|
|
|
|
// An "environment coordinate" describes how to get from head of the
|
|
// environment chain to a given lexically-enclosing variable. An environment
|
|
// coordinate has two dimensions:
|
|
// - hops: the number of environment objects on the scope chain to skip
|
|
// - slot: the slot on the environment object holding the variable's value
|
|
class EnvironmentCoordinate
|
|
{
|
|
uint32_t hops_;
|
|
uint32_t slot_;
|
|
|
|
// Technically, hops_/slot_ are ENVCOORD_(HOPS|SLOT)_BITS wide. Since
|
|
// EnvironmentCoordinate is a temporary value, don't bother with a bitfield as
|
|
// this only adds overhead.
|
|
static_assert(ENVCOORD_HOPS_BITS <= 32, "We have enough bits below");
|
|
static_assert(ENVCOORD_SLOT_BITS <= 32, "We have enough bits below");
|
|
|
|
public:
|
|
explicit inline EnvironmentCoordinate(jsbytecode* pc)
|
|
: hops_(GET_ENVCOORD_HOPS(pc)), slot_(GET_ENVCOORD_SLOT(pc + ENVCOORD_HOPS_LEN))
|
|
{
|
|
MOZ_ASSERT(JOF_OPTYPE(JSOp(*pc)) == JOF_ENVCOORD);
|
|
}
|
|
|
|
EnvironmentCoordinate() {}
|
|
|
|
void setHops(uint32_t hops) {
|
|
MOZ_ASSERT(hops < ENVCOORD_HOPS_LIMIT);
|
|
hops_ = hops;
|
|
}
|
|
|
|
void setSlot(uint32_t slot) {
|
|
MOZ_ASSERT(slot < ENVCOORD_SLOT_LIMIT);
|
|
slot_ = slot;
|
|
}
|
|
|
|
uint32_t hops() const {
|
|
MOZ_ASSERT(hops_ < ENVCOORD_HOPS_LIMIT);
|
|
return hops_;
|
|
}
|
|
|
|
uint32_t slot() const {
|
|
MOZ_ASSERT(slot_ < ENVCOORD_SLOT_LIMIT);
|
|
return slot_;
|
|
}
|
|
|
|
bool operator==(const EnvironmentCoordinate& rhs) const {
|
|
return hops() == rhs.hops() && slot() == rhs.slot();
|
|
}
|
|
};
|
|
|
|
namespace frontend {
|
|
|
|
// A detailed kind used for tracking declarations in the Parser. Used for
|
|
// specific early error semantics and better error messages.
|
|
enum class DeclarationKind : uint8_t
|
|
{
|
|
PositionalFormalParameter,
|
|
FormalParameter,
|
|
CoverArrowParameter,
|
|
Var,
|
|
ForOfVar,
|
|
Let,
|
|
Const,
|
|
Import,
|
|
BodyLevelFunction,
|
|
LexicalFunction,
|
|
VarForAnnexBLexicalFunction,
|
|
SimpleCatchParameter,
|
|
CatchParameter
|
|
};
|
|
|
|
static inline BindingKind
|
|
DeclarationKindToBindingKind(DeclarationKind kind)
|
|
{
|
|
switch (kind) {
|
|
case DeclarationKind::PositionalFormalParameter:
|
|
case DeclarationKind::FormalParameter:
|
|
case DeclarationKind::CoverArrowParameter:
|
|
return BindingKind::FormalParameter;
|
|
|
|
case DeclarationKind::Var:
|
|
case DeclarationKind::BodyLevelFunction:
|
|
case DeclarationKind::VarForAnnexBLexicalFunction:
|
|
case DeclarationKind::ForOfVar:
|
|
return BindingKind::Var;
|
|
|
|
case DeclarationKind::Let:
|
|
case DeclarationKind::LexicalFunction:
|
|
case DeclarationKind::SimpleCatchParameter:
|
|
case DeclarationKind::CatchParameter:
|
|
return BindingKind::Let;
|
|
|
|
case DeclarationKind::Const:
|
|
return BindingKind::Const;
|
|
|
|
case DeclarationKind::Import:
|
|
return BindingKind::Import;
|
|
}
|
|
|
|
MOZ_CRASH("Bad DeclarationKind");
|
|
}
|
|
|
|
static inline bool
|
|
DeclarationKindIsLexical(DeclarationKind kind)
|
|
{
|
|
return BindingKindIsLexical(DeclarationKindToBindingKind(kind));
|
|
}
|
|
|
|
// Used in Parser to track declared names.
|
|
class DeclaredNameInfo
|
|
{
|
|
uint32_t pos_;
|
|
DeclarationKind kind_;
|
|
|
|
// If the declared name is a binding, whether the binding is closed
|
|
// over. Its value is meaningless if the declared name is not a binding
|
|
// (i.e., a 'var' declared name in a non-var scope).
|
|
bool closedOver_;
|
|
|
|
public:
|
|
explicit DeclaredNameInfo(DeclarationKind kind, uint32_t pos)
|
|
: pos_(pos),
|
|
kind_(kind),
|
|
closedOver_(false)
|
|
{ }
|
|
|
|
// Needed for InlineMap.
|
|
DeclaredNameInfo() = default;
|
|
|
|
DeclarationKind kind() const {
|
|
return kind_;
|
|
}
|
|
|
|
static const uint32_t npos = uint32_t(-1);
|
|
|
|
uint32_t pos() const {
|
|
return pos_;
|
|
}
|
|
|
|
void alterKind(DeclarationKind kind) {
|
|
kind_ = kind;
|
|
}
|
|
|
|
void setClosedOver() {
|
|
closedOver_ = true;
|
|
}
|
|
|
|
bool closedOver() const {
|
|
return closedOver_;
|
|
}
|
|
};
|
|
|
|
// Used in BytecodeEmitter to map names to locations.
|
|
class NameLocation
|
|
{
|
|
public:
|
|
enum class Kind : uint8_t
|
|
{
|
|
// Cannot statically determine where the name lives. Needs to walk the
|
|
// environment chain to search for the name.
|
|
Dynamic,
|
|
|
|
// The name lives on the global or is a global lexical binding. Search
|
|
// for the name on the global scope.
|
|
Global,
|
|
|
|
// Special mode used only when emitting self-hosted scripts. See
|
|
// BytecodeEmitter::lookupName.
|
|
Intrinsic,
|
|
|
|
// In a named lambda, the name is the callee itself.
|
|
NamedLambdaCallee,
|
|
|
|
// The name is a positional formal parameter name and can be retrieved
|
|
// directly from the stack using slot_.
|
|
ArgumentSlot,
|
|
|
|
// The name is not closed over and lives on the frame in slot_.
|
|
FrameSlot,
|
|
|
|
// The name is closed over and lives on an environment hops_ away in slot_.
|
|
EnvironmentCoordinate,
|
|
|
|
// An imported name in a module.
|
|
Import,
|
|
|
|
// Cannot statically determine where the synthesized var for Annex
|
|
// B.3.3 lives.
|
|
DynamicAnnexBVar
|
|
};
|
|
|
|
private:
|
|
// Where the name lives.
|
|
Kind kind_;
|
|
|
|
// If the name is not Dynamic or DynamicAnnexBVar, the kind of the
|
|
// binding.
|
|
BindingKind bindingKind_;
|
|
|
|
// If the name is closed over and accessed via EnvironmentCoordinate, the
|
|
// number of dynamic environments to skip.
|
|
//
|
|
// Otherwise UINT8_MAX.
|
|
uint8_t hops_;
|
|
|
|
// If the name lives on the frame, the slot frame.
|
|
//
|
|
// If the name is closed over and accessed via EnvironmentCoordinate, the
|
|
// slot on the environment.
|
|
//
|
|
// Otherwise LOCALNO_LIMIT/ENVCOORD_SLOT_LIMIT.
|
|
uint32_t slot_ : ENVCOORD_SLOT_BITS;
|
|
|
|
static_assert(LOCALNO_BITS == ENVCOORD_SLOT_BITS,
|
|
"Frame and environment slots must be same sized.");
|
|
|
|
NameLocation(Kind kind, BindingKind bindingKind,
|
|
uint8_t hops = UINT8_MAX, uint32_t slot = ENVCOORD_SLOT_LIMIT)
|
|
: kind_(kind),
|
|
bindingKind_(bindingKind),
|
|
hops_(hops),
|
|
slot_(slot)
|
|
{ }
|
|
|
|
public:
|
|
// Default constructor for InlineMap.
|
|
NameLocation() = default;
|
|
|
|
static NameLocation Dynamic() {
|
|
return NameLocation();
|
|
}
|
|
|
|
static NameLocation Global(BindingKind bindKind) {
|
|
MOZ_ASSERT(bindKind != BindingKind::FormalParameter);
|
|
return NameLocation(Kind::Global, bindKind);
|
|
}
|
|
|
|
static NameLocation Intrinsic() {
|
|
return NameLocation(Kind::Intrinsic, BindingKind::Var);
|
|
}
|
|
|
|
static NameLocation NamedLambdaCallee() {
|
|
return NameLocation(Kind::NamedLambdaCallee, BindingKind::NamedLambdaCallee);
|
|
}
|
|
|
|
static NameLocation ArgumentSlot(uint16_t slot) {
|
|
return NameLocation(Kind::ArgumentSlot, BindingKind::FormalParameter, 0, slot);
|
|
}
|
|
|
|
static NameLocation FrameSlot(BindingKind bindKind, uint32_t slot) {
|
|
MOZ_ASSERT(slot < LOCALNO_LIMIT);
|
|
return NameLocation(Kind::FrameSlot, bindKind, 0, slot);
|
|
}
|
|
|
|
static NameLocation EnvironmentCoordinate(BindingKind bindKind, uint8_t hops, uint32_t slot) {
|
|
MOZ_ASSERT(slot < ENVCOORD_SLOT_LIMIT);
|
|
return NameLocation(Kind::EnvironmentCoordinate, bindKind, hops, slot);
|
|
}
|
|
|
|
static NameLocation Import() {
|
|
return NameLocation(Kind::Import, BindingKind::Import);
|
|
}
|
|
|
|
static NameLocation DynamicAnnexBVar() {
|
|
return NameLocation(Kind::DynamicAnnexBVar, BindingKind::Var);
|
|
}
|
|
|
|
static NameLocation fromBinding(BindingKind bindKind, const BindingLocation& bl) {
|
|
switch (bl.kind()) {
|
|
case BindingLocation::Kind::Global:
|
|
return Global(bindKind);
|
|
case BindingLocation::Kind::Argument:
|
|
return ArgumentSlot(bl.argumentSlot());
|
|
case BindingLocation::Kind::Frame:
|
|
return FrameSlot(bindKind, bl.slot());
|
|
case BindingLocation::Kind::Environment:
|
|
return EnvironmentCoordinate(bindKind, 0, bl.slot());
|
|
case BindingLocation::Kind::Import:
|
|
return Import();
|
|
case BindingLocation::Kind::NamedLambdaCallee:
|
|
return NamedLambdaCallee();
|
|
}
|
|
MOZ_CRASH("Bad BindingKind");
|
|
}
|
|
|
|
bool operator==(const NameLocation& other) const {
|
|
return kind_ == other.kind_ && bindingKind_ == other.bindingKind_ &&
|
|
hops_ == other.hops_ && slot_ == other.slot_;
|
|
}
|
|
|
|
bool operator!=(const NameLocation& other) const {
|
|
return !(*this == other);
|
|
}
|
|
|
|
Kind kind() const {
|
|
return kind_;
|
|
}
|
|
|
|
uint16_t argumentSlot() const {
|
|
MOZ_ASSERT(kind_ == Kind::ArgumentSlot);
|
|
return mozilla::AssertedCast<uint16_t>(slot_);
|
|
}
|
|
|
|
uint32_t frameSlot() const {
|
|
MOZ_ASSERT(kind_ == Kind::FrameSlot);
|
|
return slot_;
|
|
}
|
|
|
|
NameLocation addHops(uint8_t more) {
|
|
MOZ_ASSERT(hops_ < ENVCOORD_HOPS_LIMIT - more);
|
|
MOZ_ASSERT(kind_ == Kind::EnvironmentCoordinate);
|
|
return NameLocation(kind_, bindingKind_, hops_ + more, slot_);
|
|
}
|
|
|
|
class EnvironmentCoordinate environmentCoordinate() const {
|
|
MOZ_ASSERT(kind_ == Kind::EnvironmentCoordinate);
|
|
class EnvironmentCoordinate coord;
|
|
coord.setHops(hops_);
|
|
coord.setSlot(slot_);
|
|
return coord;
|
|
}
|
|
|
|
BindingKind bindingKind() const {
|
|
MOZ_ASSERT(kind_ != Kind::Dynamic);
|
|
return bindingKind_;
|
|
}
|
|
|
|
bool isLexical() const {
|
|
return BindingKindIsLexical(bindingKind());
|
|
}
|
|
|
|
bool isConst() const {
|
|
return bindingKind() == BindingKind::Const;
|
|
}
|
|
|
|
bool hasKnownSlot() const {
|
|
return kind_ == Kind::ArgumentSlot ||
|
|
kind_ == Kind::FrameSlot ||
|
|
kind_ == Kind::EnvironmentCoordinate;
|
|
}
|
|
};
|
|
|
|
// This type is declared here for LazyScript::Create.
|
|
using AtomVector = Vector<JSAtom*, 24, SystemAllocPolicy>;
|
|
|
|
} // namespace frontend
|
|
} // namespace js
|
|
|
|
namespace mozilla {
|
|
|
|
template <>
|
|
struct IsPod<js::frontend::DeclaredNameInfo> : TrueType {};
|
|
|
|
template <>
|
|
struct IsPod<js::frontend::NameLocation> : TrueType {};
|
|
|
|
} // namespace mozilla
|
|
|
|
#endif // frontend_NameAnalysis_h
|