///|
// A resolved identifier reference is runtime state, not a JavaScript value.
// Its closed representation retains the selected binding cell or object base
// and keeps source-sensitive PutValue policy with that selection.
priv enum ResolvedBindingReferenceKind {
  ResolvedBindingLexical(EnvironmentSlot)
  ResolvedBindingWithObject(Value, Bool)
  ResolvedBindingGlobal
  ResolvedBindingUnresolvable
}

///|
pub struct ResolvedBindingReference {
  priv kind : ResolvedBindingReferenceKind
  priv name : String
  priv strict : Bool
  priv loc : @token.Loc
}

///|
pub struct ExecutorBindingReferenceRequest {
  priv ctx : ExecContext
  priv env : Environment
  priv name : String
  priv loc : @token.Loc
}

///|
fn ExecutorBindingReferenceRequest::ExecutorBindingReferenceRequest(
  ctx~ : ExecContext,
  env~ : Environment,
  name~ : String,
  loc~ : @token.Loc,
) -> ExecutorBindingReferenceRequest {
  { ctx, env, name, loc, }
}

///|
#warnings("-unused_constructor")
priv enum ExecutorBindingReferenceStart {
  ExecutorBindingReferenceCompleted(ResolvedBindingReference)
  ExecutorBindingReferenceSuspended(ExecutorBindingReferenceRequest)
}

///|
#warnings("-unused_constructor")
priv enum ExecutorBindingReferenceGetStart {
  ExecutorBindingReferenceGetCompleted(Value)
  ExecutorBindingReferenceGetSuspended(ExecutorBindingReferenceGetOperation)
}

///|
priv enum ExecutorBindingReferenceGetPhase {
  ExecutorBindingReferenceGetNeedHas(Value)
  ExecutorBindingReferenceGetAwaitHas(Value, ExecutorDeleteHasPropertyOperation)
  ExecutorBindingReferenceGetNeedGet(Value)
  ExecutorBindingReferenceGetAwaitGet
  ExecutorBindingReferenceGetComplete(Value)
  ExecutorBindingReferenceGetDone
}

///|
priv struct ExecutorBindingReferenceGetOperation {
  reference : ResolvedBindingReference
  mut phase : ExecutorBindingReferenceGetPhase
}

///|
fn ExecutorBindingReferenceGetOperation::ExecutorBindingReferenceGetOperation(
  reference : ResolvedBindingReference,
  obj : Value,
  recheck : Bool,
) -> ExecutorBindingReferenceGetOperation {
  {
    reference,
    phase: if recheck {
      ExecutorBindingReferenceGetNeedHas(obj)
    } else {
      ExecutorBindingReferenceGetNeedGet(obj)
    },
  }
}

///|
fn ExecutorBindingReferenceGetOperation::after_has(
  self : ExecutorBindingReferenceGetOperation,
  obj : Value,
  has_property : Bool,
  interp : Interpreter,
) -> ExecutorActivationStep raise Error {
  if !has_property {
    if self.reference.strict {
      raise @errors.ReferenceError(
        message="\{self.reference.name} is not defined",
      )
    }
    self.phase = ExecutorBindingReferenceGetComplete(Undefined)
    return ExecutorActivationNormal(Undefined)
  }
  self.phase = ExecutorBindingReferenceGetNeedGet(obj)
  self.step(interp)
}

///|
impl ExecutorActivationFrame for ExecutorBindingReferenceGetOperation with fn step(
  self,
  interp,
) {
  match self.phase {
    ExecutorBindingReferenceGetNeedHas(obj) => {
      let operation = ExecutorDeleteHasPropertyOperation(
        target=obj,
        key=String_(self.reference.name),
        loc=self.reference.loc,
      )
      match operation.step(interp) {
        ExecutorActivationNormal(Bool(has_property)) =>
          self.after_has(obj, has_property, interp)
        ExecutorActivationNormal(_) =>
          raise @errors.InternalError(
            message="binding reference GetValue HasProperty returned a non-boolean",
          )
        step => {
          self.phase = ExecutorBindingReferenceGetAwaitHas(obj, operation)
          step
        }
      }
    }
    ExecutorBindingReferenceGetAwaitHas(obj, operation) =>
      match operation.step(interp) {
        ExecutorActivationNormal(Bool(has_property)) =>
          self.after_has(obj, has_property, interp)
        ExecutorActivationNormal(_) =>
          raise @errors.InternalError(
            message="binding reference GetValue HasProperty returned a non-boolean",
          )
        step => {
          self.phase = ExecutorBindingReferenceGetAwaitHas(obj, operation)
          step
        }
      }
    ExecutorBindingReferenceGetNeedGet(obj) => {
      self.phase = ExecutorBindingReferenceGetAwaitGet
      executor_activation_managed_property_get(
        obj,
        self.reference.name,
        self.reference.loc,
      )
    }
    ExecutorBindingReferenceGetAwaitGet =>
      raise @errors.InternalError(
        message="binding reference GetValue stepped while awaiting Get",
      )
    ExecutorBindingReferenceGetComplete(value) => {
      self.phase = ExecutorBindingReferenceGetDone
      ExecutorActivationNormal(value)
    }
    ExecutorBindingReferenceGetDone =>
      raise @errors.InternalError(
        message="binding reference GetValue stepped after completion",
      )
  }
}

///|
impl ExecutorActivationFrame for ExecutorBindingReferenceGetOperation with fn deliver_activation_completion(
  self,
  completion,
) {
  match completion {
    ExecutorActivationCompletionAbrupt(error) => raise error
    ExecutorActivationCompletionReference(_) =>
      raise @errors.InternalError(
        message="binding reference GetValue received a nested reference",
      )
    ExecutorActivationCompletionNormal(value) =>
      match self.phase {
        ExecutorBindingReferenceGetAwaitHas(_, operation) =>
          operation.deliver_activation_completion(
            ExecutorActivationCompletionNormal(value),
          )
        ExecutorBindingReferenceGetAwaitGet =>
          self.phase = ExecutorBindingReferenceGetComplete(value)
        ExecutorBindingReferenceGetNeedHas(_)
        | ExecutorBindingReferenceGetNeedGet(_)
        | ExecutorBindingReferenceGetComplete(_)
        | ExecutorBindingReferenceGetDone =>
          raise @errors.InternalError(
            message="binding reference GetValue received an unexpected child completion",
          )
      }
  }
}

///|
extend ExecutorBindingReferenceGetOperation with ExecutorActivationFrame::{
  step,
  deliver_activation_completion,
}

///|
#warnings("-unused_constructor")
priv enum ExecutorBindingReferencePutStart {
  ExecutorBindingReferencePutCompleted(Value)
  ExecutorBindingReferencePutSuspended(ExecutorBindingReferencePutOperation)
}

///|
priv enum ExecutorBindingReferencePutPhase {
  ExecutorBindingReferencePutNeedHas(Value)
  ExecutorBindingReferencePutAwaitHas(Value, ExecutorDeleteHasPropertyOperation)
  ExecutorBindingReferencePutAwaitSet
  ExecutorBindingReferencePutComplete
  ExecutorBindingReferencePutDone
}

///|
priv struct ExecutorBindingReferencePutOperation {
  reference : ResolvedBindingReference
  value : Value
  mut phase : ExecutorBindingReferencePutPhase
}

///|
fn ExecutorBindingReferencePutOperation::ExecutorBindingReferencePutOperation(
  reference : ResolvedBindingReference,
  value : Value,
  obj : Value,
) -> ExecutorBindingReferencePutOperation {
  { reference, value, phase: ExecutorBindingReferencePutNeedHas(obj), }
}

///|
fn ExecutorBindingReferencePutOperation::after_has(
  self : ExecutorBindingReferencePutOperation,
  obj : Value,
  has_property : Bool,
) -> ExecutorActivationStep raise Error {
  if !has_property && self.reference.strict {
    raise @errors.ReferenceError(
      message="\{self.reference.name} is not defined",
    )
  }
  self.phase = ExecutorBindingReferencePutAwaitSet
  executor_activation_managed_property_set(
    obj,
    self.reference.name,
    self.value,
    self.reference.loc,
    self.reference.strict,
  )
}

///|
impl ExecutorActivationFrame for ExecutorBindingReferencePutOperation with fn step(
  self,
  interp,
) {
  match self.phase {
    ExecutorBindingReferencePutNeedHas(obj) => {
      let operation = ExecutorDeleteHasPropertyOperation(
        target=obj,
        key=String_(self.reference.name),
        loc=self.reference.loc,
      )
      match operation.step(interp) {
        ExecutorActivationNormal(Bool(has_property)) =>
          self.after_has(obj, has_property)
        ExecutorActivationNormal(_) =>
          raise @errors.InternalError(
            message="binding reference PutValue HasProperty returned a non-boolean",
          )
        step => {
          self.phase = ExecutorBindingReferencePutAwaitHas(obj, operation)
          step
        }
      }
    }
    ExecutorBindingReferencePutAwaitHas(obj, operation) =>
      match operation.step(interp) {
        ExecutorActivationNormal(Bool(has_property)) =>
          self.after_has(obj, has_property)
        ExecutorActivationNormal(_) =>
          raise @errors.InternalError(
            message="binding reference PutValue HasProperty returned a non-boolean",
          )
        step => {
          self.phase = ExecutorBindingReferencePutAwaitHas(obj, operation)
          step
        }
      }
    ExecutorBindingReferencePutAwaitSet =>
      raise @errors.InternalError(
        message="binding reference PutValue stepped while awaiting Set",
      )
    ExecutorBindingReferencePutComplete => {
      self.phase = ExecutorBindingReferencePutDone
      ExecutorActivationNormal(self.value)
    }
    ExecutorBindingReferencePutDone =>
      raise @errors.InternalError(
        message="binding reference PutValue stepped after completion",
      )
  }
}

///|
impl ExecutorActivationFrame for ExecutorBindingReferencePutOperation with fn deliver_activation_completion(
  self,
  completion,
) {
  match completion {
    ExecutorActivationCompletionAbrupt(error) => raise error
    ExecutorActivationCompletionReference(_) =>
      raise @errors.InternalError(
        message="binding reference PutValue received a nested reference",
      )
    ExecutorActivationCompletionNormal(value) =>
      match self.phase {
        ExecutorBindingReferencePutAwaitHas(_, operation) =>
          operation.deliver_activation_completion(
            ExecutorActivationCompletionNormal(value),
          )
        ExecutorBindingReferencePutAwaitSet =>
          self.phase = ExecutorBindingReferencePutComplete
        ExecutorBindingReferencePutNeedHas(_)
        | ExecutorBindingReferencePutComplete
        | ExecutorBindingReferencePutDone =>
          raise @errors.InternalError(
            message="binding reference PutValue received an unexpected child completion",
          )
      }
  }
}

///|
extend ExecutorBindingReferencePutOperation with ExecutorActivationFrame::{
  step,
  deliver_activation_completion,
}

///|
priv enum ExecutorBindingReferenceAccessKind {
  ExecutorBindingReferenceAccessGet
  ExecutorBindingReferenceAccessPut(Value)
}

///|
pub struct ExecutorBindingReferenceAccessRequest {
  priv reference : ResolvedBindingReference
  priv kind : ExecutorBindingReferenceAccessKind
}

///|
priv enum ExecutorBindingReferenceAccessPhase {
  ExecutorBindingReferenceAccessNeedOperation
  ExecutorBindingReferenceAccessAwaitGet(ExecutorBindingReferenceGetOperation)
  ExecutorBindingReferenceAccessAwaitPut(ExecutorBindingReferencePutOperation)
  ExecutorBindingReferenceAccessComplete(Value)
  ExecutorBindingReferenceAccessDone
}

///|
priv struct ExecutorBindingReferenceAccessFrame {
  request : ExecutorBindingReferenceAccessRequest
  mut phase : ExecutorBindingReferenceAccessPhase
}

///|
fn ExecutorBindingReferenceAccessFrame::ExecutorBindingReferenceAccessFrame(
  request : ExecutorBindingReferenceAccessRequest,
) -> ExecutorBindingReferenceAccessFrame {
  { request, phase: ExecutorBindingReferenceAccessNeedOperation, }
}

///|
fn ExecutorBindingReferenceAccessFrame::advance_get(
  self : ExecutorBindingReferenceAccessFrame,
  operation : ExecutorBindingReferenceGetOperation,
  interp : Interpreter,
) -> ExecutorActivationStep raise Error {
  match operation.step(interp) {
    ExecutorActivationNormal(value) => {
      self.phase = ExecutorBindingReferenceAccessComplete(value)
      ExecutorActivationNormal(value)
    }
    step => {
      self.phase = ExecutorBindingReferenceAccessAwaitGet(operation)
      step
    }
  }
}

///|
fn ExecutorBindingReferenceAccessFrame::advance_put(
  self : ExecutorBindingReferenceAccessFrame,
  operation : ExecutorBindingReferencePutOperation,
  interp : Interpreter,
) -> ExecutorActivationStep raise Error {
  match operation.step(interp) {
    ExecutorActivationNormal(value) => {
      self.phase = ExecutorBindingReferenceAccessComplete(value)
      ExecutorActivationNormal(value)
    }
    step => {
      self.phase = ExecutorBindingReferenceAccessAwaitPut(operation)
      step
    }
  }
}

///|
impl ExecutorActivationFrame for ExecutorBindingReferenceAccessFrame with fn step(
  self,
  interp,
) {
  match self.phase {
    ExecutorBindingReferenceAccessNeedOperation =>
      match self.request.kind {
        ExecutorBindingReferenceAccessGet =>
          match
            begin_executor_binding_reference_get(interp, self.request.reference) {
            ExecutorBindingReferenceGetCompleted(value) => {
              self.phase = ExecutorBindingReferenceAccessComplete(value)
              ExecutorActivationNormal(value)
            }
            ExecutorBindingReferenceGetSuspended(operation) =>
              self.advance_get(operation, interp)
          }
        ExecutorBindingReferenceAccessPut(value) =>
          match
            begin_executor_binding_reference_put(
              interp,
              self.request.reference,
              value,
            ) {
            ExecutorBindingReferencePutCompleted(result) => {
              self.phase = ExecutorBindingReferenceAccessComplete(result)
              ExecutorActivationNormal(result)
            }
            ExecutorBindingReferencePutSuspended(operation) =>
              self.advance_put(operation, interp)
          }
      }
    ExecutorBindingReferenceAccessAwaitGet(operation) =>
      self.advance_get(operation, interp)
    ExecutorBindingReferenceAccessAwaitPut(operation) =>
      self.advance_put(operation, interp)
    ExecutorBindingReferenceAccessComplete(value) => {
      self.phase = ExecutorBindingReferenceAccessDone
      ExecutorActivationNormal(value)
    }
    ExecutorBindingReferenceAccessDone =>
      raise @errors.InternalError(
        message="binding reference access stepped after completion",
      )
  }
}

///|
impl ExecutorActivationFrame for ExecutorBindingReferenceAccessFrame with fn deliver_activation_completion(
  self,
  completion,
) {
  match self.phase {
    ExecutorBindingReferenceAccessAwaitGet(operation) =>
      operation.deliver_activation_completion(completion)
    ExecutorBindingReferenceAccessAwaitPut(operation) =>
      operation.deliver_activation_completion(completion)
    ExecutorBindingReferenceAccessNeedOperation
    | ExecutorBindingReferenceAccessComplete(_)
    | ExecutorBindingReferenceAccessDone =>
      raise @errors.InternalError(
        message="binding reference access received an unexpected child completion",
      )
  }
}

///|
#warnings("-unused_value")
extend ExecutorBindingReferenceAccessFrame with ExecutorActivationFrame::{
  step,
  deliver_activation_completion,
}

///|
pub fn executor_activation_binding_reference_resolution(
  ctx : ExecContext,
  env : Environment,
  name : String,
  loc : @token.Loc,
) -> ExecutorActivationStep raise Error {
  @static_semantics.validate_strict_identifier_reference(ctx.strict, name)
  ExecutorActivationBindingReference(
    ExecutorBindingReferenceRequest(ctx~, env~, name~, loc~),
  )
}

///|
pub fn executor_activation_binding_reference_get(
  reference : ResolvedBindingReference,
) -> ExecutorActivationStep {
  ExecutorActivationBindingReferenceAccess({
    reference,
    kind: ExecutorBindingReferenceAccessGet,
  })
}

///|
pub fn executor_activation_binding_reference_put(
  reference : ResolvedBindingReference,
  value : Value,
) -> ExecutorActivationStep {
  ExecutorActivationBindingReferenceAccess({
    reference,
    kind: ExecutorBindingReferenceAccessPut(value),
  })
}

///|
fn resolved_binding_reference(
  kind : ResolvedBindingReferenceKind,
  name : String,
  ctx : ExecContext,
  loc : @token.Loc,
) -> ResolvedBindingReference {
  { kind, name, strict: ctx.strict, loc, }
}

///|
fn try_executor_binding_reference_plain(
  interp : Interpreter,
  request : ExecutorBindingReferenceRequest,
) -> ResolvedBindingReference? {
  let mut current : Environment? = Some(request.env)
  while true {
    match current {
      Some(env) =>
        match env.with_object {
          Some(_) => return None
          None =>
            match env.resolve_slot(request.name) {
              Some(slot) => {
                let kind = if env.bindings.get(request.name)
                  is Some({ kind: VarBinding, .. }) &&
                  env.parent is None &&
                  interp.is_immutable_global(request.name) {
                  ResolvedBindingGlobal
                } else {
                  ResolvedBindingLexical(slot)
                }
                return Some(
                  resolved_binding_reference(
                    kind,
                    request.name,
                    request.ctx,
                    request.loc,
                  ),
                )
              }
              None => current = env.parent
            }
        }
      None => return None
    }
  }
  None
}

///|
#warnings("-unused_value")
fn begin_executor_binding_reference(
  interp : Interpreter,
  ctx : ExecContext,
  env : Environment,
  name : String,
  loc : @token.Loc,
) -> ExecutorBindingReferenceStart raise Error {
  @static_semantics.validate_strict_identifier_reference(ctx.strict, name)
  let request = ExecutorBindingReferenceRequest(ctx~, env~, name~, loc~)
  match try_executor_binding_reference_plain(interp, request) {
    Some(reference) => ExecutorBindingReferenceCompleted(reference)
    None => ExecutorBindingReferenceSuspended(request)
  }
}

///|
#warnings("-unused_value")
fn begin_executor_binding_reference_get(
  interp : Interpreter,
  reference : ResolvedBindingReference,
) -> ExecutorBindingReferenceGetStart raise Error {
  match reference.kind {
    ResolvedBindingLexical(slot) =>
      ExecutorBindingReferenceGetCompleted(slot.read())
    ResolvedBindingWithObject(obj, _) =>
      ExecutorBindingReferenceGetSuspended(
        ExecutorBindingReferenceGetOperation(reference, obj, true),
      )
    ResolvedBindingGlobal =>
      ExecutorBindingReferenceGetSuspended(
        ExecutorBindingReferenceGetOperation(
          reference,
          interp.global_this,
          true,
        ),
      )
    ResolvedBindingUnresolvable =>
      raise @errors.ReferenceError(message="\{reference.name} is not defined")
  }
}

///|
fn executor_binding_reference_put_unresolved(
  interp : Interpreter,
  reference : ResolvedBindingReference,
  value : Value,
) -> Value raise Error {
  if interp.is_immutable_global(reference.name) {
    if reference.strict {
      raise @errors.TypeError(
        message="Cannot assign to read only property '\{reference.name}' of object '[object global]'",
      )
    }
    return value
  }
  if reference.strict {
    raise @errors.ReferenceError(message="\{reference.name} is not defined")
  }
  interp.global.def(reference.name, value, VarBinding)
  interp.mirror_to_global(reference.name, value, configurable=true)
  value
}

///|
#warnings("-unused_value")
fn begin_executor_binding_reference_put(
  interp : Interpreter,
  reference : ResolvedBindingReference,
  value : Value,
) -> ExecutorBindingReferencePutStart raise Error {
  @static_semantics.validate_strict_assignment_target_name(
    reference.strict,
    reference.name,
  )
  match reference.kind {
    ResolvedBindingLexical(slot) => {
      slot.assign(value, strict=reference.strict)
      ExecutorBindingReferencePutCompleted(value)
    }
    ResolvedBindingWithObject(obj, _) =>
      ExecutorBindingReferencePutSuspended(
        ExecutorBindingReferencePutOperation(reference, value, obj),
      )
    ResolvedBindingGlobal =>
      ExecutorBindingReferencePutSuspended(
        ExecutorBindingReferencePutOperation(
          reference,
          value,
          interp.global_this,
        ),
      )
    ResolvedBindingUnresolvable =>
      ExecutorBindingReferencePutCompleted(
        executor_binding_reference_put_unresolved(interp, reference, value),
      )
  }
}

///|
priv enum ExecutorBindingReferencePhase {
  ExecutorBindingReferenceNeedEnvironment(Environment)
  ExecutorBindingReferenceAwaitGlobalHas(
    Value,
    ExecutorDeleteHasPropertyOperation
  )
  ExecutorBindingReferenceAwaitHas(
    Environment,
    Value,
    ExecutorDeleteHasPropertyOperation
  )
  ExecutorBindingReferenceAwaitUnscopables(Environment, Value)
  ExecutorBindingReferenceHaveUnscopables(Environment, Value, Value)
  ExecutorBindingReferenceAwaitUnscopablesName(Environment, Value)
  ExecutorBindingReferenceHaveUnscopablesName(Environment, Value, Value)
  ExecutorBindingReferenceComplete(ResolvedBindingReference)
  ExecutorBindingReferenceDone
}

///|
priv struct ExecutorBindingReferenceFrame {
  request : ExecutorBindingReferenceRequest
  mut phase : ExecutorBindingReferencePhase
}

///|
fn ExecutorBindingReferenceFrame::ExecutorBindingReferenceFrame(
  request : ExecutorBindingReferenceRequest,
) -> ExecutorBindingReferenceFrame {
  { request, phase: ExecutorBindingReferenceNeedEnvironment(request.env), }
}

///|
fn ExecutorBindingReferenceFrame::parent_or_global(
  self : ExecutorBindingReferenceFrame,
  env : Environment,
  interp : Interpreter,
) -> ExecutorActivationStep raise Error {
  match env.parent {
    Some(parent) => {
      self.phase = ExecutorBindingReferenceNeedEnvironment(parent)
      ExecutorActivationContinue
    }
    None => self.begin_global_has(interp)
  }
}

///|
fn ExecutorBindingReferenceFrame::begin_global_has(
  self : ExecutorBindingReferenceFrame,
  interp : Interpreter,
) -> ExecutorActivationStep raise Error {
  let obj = interp.global_this
  let operation = ExecutorDeleteHasPropertyOperation(
    target=obj,
    key=String_(self.request.name),
    loc=self.request.loc,
  )
  match operation.step(interp) {
    ExecutorActivationNormal(Bool(has_property)) =>
      self.complete_global(has_property)
    ExecutorActivationNormal(_) =>
      raise @errors.InternalError(
        message="global binding reference HasProperty returned a non-boolean",
      )
    step => {
      self.phase = ExecutorBindingReferenceAwaitGlobalHas(obj, operation)
      step
    }
  }
}

///|
fn ExecutorBindingReferenceFrame::complete_global(
  self : ExecutorBindingReferenceFrame,
  has_property : Bool,
) -> ExecutorActivationStep {
  let kind = if has_property {
    ResolvedBindingGlobal
  } else {
    ResolvedBindingUnresolvable
  }
  let reference = resolved_binding_reference(
    kind,
    self.request.name,
    self.request.ctx,
    self.request.loc,
  )
  self.phase = ExecutorBindingReferenceComplete(reference)
  ExecutorActivationReference(reference)
}

///|
fn ExecutorBindingReferenceFrame::complete_lexical(
  self : ExecutorBindingReferenceFrame,
  env : Environment,
) -> ExecutorActivationStep {
  let slot = env.resolve_slot(self.request.name).unwrap()
  let reference = resolved_binding_reference(
    ResolvedBindingLexical(slot),
    self.request.name,
    self.request.ctx,
    self.request.loc,
  )
  self.phase = ExecutorBindingReferenceComplete(reference)
  ExecutorActivationReference(reference)
}

///|
fn ExecutorBindingReferenceFrame::complete_with_object(
  self : ExecutorBindingReferenceFrame,
  obj : Value,
  missing : Bool,
) -> ExecutorActivationStep {
  let reference = resolved_binding_reference(
    ResolvedBindingWithObject(obj, missing),
    self.request.name,
    self.request.ctx,
    self.request.loc,
  )
  self.phase = ExecutorBindingReferenceComplete(reference)
  ExecutorActivationReference(reference)
}

///|
fn ExecutorBindingReferenceFrame::after_has(
  self : ExecutorBindingReferenceFrame,
  env : Environment,
  obj : Value,
  has_property : Bool,
  interp : Interpreter,
) -> ExecutorActivationStep raise Error {
  if !has_property {
    return self.parent_or_global(env, interp)
  }
  self.phase = ExecutorBindingReferenceAwaitUnscopables(env, obj)
  executor_activation_symbol_property_get(
    obj,
    interp.realm_state.well_known_symbols.unscopables,
    self.request.loc,
  )
}

///|
fn ExecutorBindingReferenceFrame::after_unscopables(
  self : ExecutorBindingReferenceFrame,
  env : Environment,
  obj : Value,
  unscopables : Value,
) -> ExecutorActivationStep {
  if is_object_value(unscopables) {
    self.phase = ExecutorBindingReferenceAwaitUnscopablesName(env, obj)
    return executor_activation_managed_property_get(
      unscopables,
      self.request.name,
      self.request.loc,
    )
  }
  self.complete_with_object(obj, false)
}

///|
fn ExecutorBindingReferenceFrame::after_unscopables_name(
  self : ExecutorBindingReferenceFrame,
  env : Environment,
  obj : Value,
  unscopables_value : Value,
  interp : Interpreter,
) -> ExecutorActivationStep raise Error {
  if is_truthy(unscopables_value) {
    self.parent_or_global(env, interp)
  } else {
    self.complete_with_object(obj, false)
  }
}

///|
fn ExecutorBindingReferenceFrame::step_environment(
  self : ExecutorBindingReferenceFrame,
  env : Environment,
  interp : Interpreter,
) -> ExecutorActivationStep raise Error {
  match env.with_object {
    Some(obj) => {
      let operation = ExecutorDeleteHasPropertyOperation(
        target=obj,
        key=String_(self.request.name),
        loc=self.request.loc,
      )
      match operation.step(interp) {
        ExecutorActivationNormal(Bool(has_property)) =>
          self.after_has(env, obj, has_property, interp)
        ExecutorActivationNormal(_) =>
          raise @errors.InternalError(
            message="binding reference HasProperty returned a non-boolean",
          )
        step => {
          self.phase = ExecutorBindingReferenceAwaitHas(env, obj, operation)
          step
        }
      }
    }
    None =>
      match env.bindings.get(self.request.name) {
        Some({ kind: VarBinding, .. }) if env.parent is None &&
          interp.is_immutable_global(self.request.name) =>
          self.complete_global(true)
        Some(_) => self.complete_lexical(env)
        None => self.parent_or_global(env, interp)
      }
  }
}

///|
impl ExecutorActivationFrame for ExecutorBindingReferenceFrame with fn step(
  self,
  interp,
) {
  match self.phase {
    ExecutorBindingReferenceNeedEnvironment(env) =>
      self.step_environment(env, interp)
    ExecutorBindingReferenceAwaitGlobalHas(obj, operation) =>
      match operation.step(interp) {
        ExecutorActivationNormal(Bool(has_property)) =>
          self.complete_global(has_property)
        ExecutorActivationNormal(_) =>
          raise @errors.InternalError(
            message="global binding reference HasProperty returned a non-boolean",
          )
        step => {
          self.phase = ExecutorBindingReferenceAwaitGlobalHas(obj, operation)
          step
        }
      }
    ExecutorBindingReferenceAwaitHas(env, obj, operation) =>
      match operation.step(interp) {
        ExecutorActivationNormal(Bool(has_property)) =>
          self.after_has(env, obj, has_property, interp)
        ExecutorActivationNormal(_) =>
          raise @errors.InternalError(
            message="binding reference HasProperty returned a non-boolean",
          )
        step => {
          self.phase = ExecutorBindingReferenceAwaitHas(env, obj, operation)
          step
        }
      }
    ExecutorBindingReferenceAwaitUnscopables(_, _) =>
      raise @errors.InternalError(
        message="binding reference frame stepped while awaiting unscopables",
      )
    ExecutorBindingReferenceHaveUnscopables(env, obj, value) =>
      self.after_unscopables(env, obj, value)
    ExecutorBindingReferenceAwaitUnscopablesName(_, _) =>
      raise @errors.InternalError(
        message="binding reference frame stepped while awaiting unscopables name",
      )
    ExecutorBindingReferenceHaveUnscopablesName(env, obj, value) =>
      self.after_unscopables_name(env, obj, value, interp)
    ExecutorBindingReferenceComplete(reference) => {
      self.phase = ExecutorBindingReferenceDone
      ExecutorActivationReference(reference)
    }
    ExecutorBindingReferenceDone =>
      raise @errors.InternalError(
        message="binding reference frame stepped after completion",
      )
  }
}

///|
impl ExecutorActivationFrame for ExecutorBindingReferenceFrame with fn deliver_activation_completion(
  self,
  completion,
) {
  match completion {
    ExecutorActivationCompletionAbrupt(error) => raise error
    ExecutorActivationCompletionReference(_) =>
      raise @errors.InternalError(
        message="binding reference frame received a nested reference",
      )
    ExecutorActivationCompletionNormal(value) =>
      match self.phase {
        ExecutorBindingReferenceAwaitGlobalHas(_, operation) =>
          operation.deliver_activation_completion(
            ExecutorActivationCompletionNormal(value),
          )
        ExecutorBindingReferenceAwaitHas(_, _, operation) =>
          operation.deliver_activation_completion(
            ExecutorActivationCompletionNormal(value),
          )
        ExecutorBindingReferenceAwaitUnscopables(env, obj) =>
          self.phase = ExecutorBindingReferenceHaveUnscopables(env, obj, value)
        ExecutorBindingReferenceAwaitUnscopablesName(env, obj) =>
          self.phase = ExecutorBindingReferenceHaveUnscopablesName(
            env, obj, value,
          )
        ExecutorBindingReferenceNeedEnvironment(_)
        | ExecutorBindingReferenceHaveUnscopables(_, _, _)
        | ExecutorBindingReferenceHaveUnscopablesName(_, _, _)
        | ExecutorBindingReferenceComplete(_)
        | ExecutorBindingReferenceDone =>
          raise @errors.InternalError(
            message="binding reference received an unexpected child completion",
          )
      }
  }
}

///|
#warnings("-unused_value")
fn executor_activation_binding_reference_request(
  request : ExecutorBindingReferenceRequest,
) -> ExecutorActivationStep {
  ExecutorActivationBindingReference(request)
}