Source code
Revision control
Copy as Markdown
Other Tools
/* -*- 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
#ifndef frontend_NameAnalysisTypes_h
#define frontend_NameAnalysisTypes_h
#include "mozilla/Assertions.h" // MOZ_ASSERT, MOZ_CRASH
#include "mozilla/Casting.h" // mozilla::AssertedCast
#include <stdint.h> // uint8_t, uint16_t, uint32_t
#include "vm/BindingKind.h" // BindingKind, BindingLocation
#include "vm/BytecodeFormatFlags.h" // JOF_ENVCOORD
#include "vm/BytecodeUtil.h" // ENVCOORD_HOPS_BITS, ENVCOORD_SLOT_BITS, GET_ENVCOORD_HOPS, GET_ENVCOORD_SLOT, ENVCOORD_HOPS_LEN, JOF_OPTYPE, JSOp, LOCALNO_LIMIT
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 ||
JOF_OPTYPE(JSOp(*pc)) == JOF_DEBUGCOORD);
}
EnvironmentCoordinate() = default;
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 {
enum class ParseGoal : uint8_t { Script, Module };
// 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,
Let,
Const,
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
Using,
AwaitUsing,
#endif
Class, // Handled as same as `let` after parsing.
Import,
BodyLevelFunction,
ModuleBodyLevelFunction,
LexicalFunction,
SloppyLexicalFunction,
VarForAnnexBLexicalFunction,
SimpleCatchParameter,
CatchParameter,
PrivateName,
Synthetic,
PrivateMethod, // slot to store nonstatic private method
};
// Class field kind.
enum class FieldPlacement : uint8_t { Unspecified, Instance, Static };
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::ModuleBodyLevelFunction:
case DeclarationKind::VarForAnnexBLexicalFunction:
return BindingKind::Var;
case DeclarationKind::Let:
case DeclarationKind::Class:
case DeclarationKind::LexicalFunction:
case DeclarationKind::SloppyLexicalFunction:
case DeclarationKind::SimpleCatchParameter:
case DeclarationKind::CatchParameter:
return BindingKind::Let;
case DeclarationKind::Const:
return BindingKind::Const;
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
case DeclarationKind::AwaitUsing:
case DeclarationKind::Using:
return BindingKind::Using;
#endif
case DeclarationKind::Import:
return BindingKind::Import;
case DeclarationKind::Synthetic:
case DeclarationKind::PrivateName:
return BindingKind::Synthetic;
case DeclarationKind::PrivateMethod:
return BindingKind::PrivateMethod;
}
MOZ_CRASH("Bad DeclarationKind");
}
static inline bool DeclarationKindIsLexical(DeclarationKind kind) {
return BindingKindIsLexical(DeclarationKindToBindingKind(kind));
}
// Used in Parser and BytecodeEmitter to track the kind of a private name.
enum class PrivateNameKind : uint8_t {
None,
Field,
Method,
Getter,
Setter,
GetterSetter,
};
enum class ClosedOver : bool { No = false, Yes = true };
// 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_;
PrivateNameKind privateNameKind_;
// Only updated for private names (see noteDeclaredPrivateName),
// tracks if declaration was instance or static to allow issuing
// early errors in the case where we mismatch instance and static
// private getter/setters.
FieldPlacement placement_;
public:
explicit DeclaredNameInfo(DeclarationKind kind, uint32_t pos,
ClosedOver closedOver = ClosedOver::No)
: pos_(pos),
kind_(kind),
closedOver_(bool(closedOver)),
privateNameKind_(PrivateNameKind::None),
placement_(FieldPlacement::Unspecified) {
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
// TODO: present we are brute forcing our way to
// enforce creating an environment object whenever we encounter
// a using declaration. This is temporary for prototyping
if (kind == DeclarationKind::Using || kind == DeclarationKind::AwaitUsing) {
closedOver_ = true;
}
#endif
}
// 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_; }
void setPrivateNameKind(PrivateNameKind privateNameKind) {
privateNameKind_ = privateNameKind;
}
void setFieldPlacement(FieldPlacement placement) {
MOZ_ASSERT(placement != FieldPlacement::Unspecified);
placement_ = placement;
}
PrivateNameKind privateNameKind() const { return privateNameKind_; }
FieldPlacement placement() const { return placement_; }
};
// 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,
// The name is closed over and lives on an environment hops_ away in slot_,
// where one or more of the environments may be a DebugEnvironmentProxy
DebugEnvironmentCoordinate,
// 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 0.
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 = 0)
: kind_(kind), bindingKind_(bindingKind), hops_(hops), slot_(slot) {}
public:
// Default constructor for InlineMap.
NameLocation() = default;
static NameLocation Dynamic() {
return NameLocation(Kind::Dynamic, BindingKind::Import);
}
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 DebugEnvironmentCoordinate(BindingKind bindKind,
uint8_t hops, uint32_t slot) {
MOZ_ASSERT(slot < ENVCOORD_SLOT_LIMIT);
return NameLocation(Kind::DebugEnvironmentCoordinate, bindKind, hops, slot);
}
static NameLocation Import() {
return NameLocation(Kind::Import, BindingKind::Import);
}
static NameLocation DynamicAnnexBVar() {
return NameLocation(Kind::DynamicAnnexBVar, BindingKind::Var);
}
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 ||
kind_ == Kind::DebugEnvironmentCoordinate);
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 isSynthetic() const { return bindingKind() == BindingKind::Synthetic; }
bool isPrivateMethod() const {
return bindingKind() == BindingKind::PrivateMethod;
}
bool hasKnownSlot() const {
return kind_ == Kind::ArgumentSlot || kind_ == Kind::FrameSlot ||
kind_ == Kind::EnvironmentCoordinate;
}
};
} // namespace frontend
} // namespace js
#endif // frontend_NameAnalysisTypes_h