Use Dependency Injection for global emit_* state
commitaf227285c07ef274fe81341e48a5407bc3635d05
authorLeo Osvald <leoo@fb.com>
Fri, 18 Oct 2019 22:51:26 +0000 (18 15:51 -0700)
committerFacebook Github Bot <facebook-github-bot@users.noreply.github.com>
Fri, 18 Oct 2019 22:53:24 +0000 (18 15:53 -0700)
treef3f99b42d7469803312a07671934777160b7500b
parent1293ef5a839d0243d75d3b9374ef0524befe87ca
Use Dependency Injection for global emit_* state

Summary:
OCaml conveniently avoids *circularly* depending on higher-level modules
(`emit_*`) by having each `emit_*` use **its own** global mutable state.

However, porting this directly to Rust would require mutexes or `unsafe`,
which impacts performance and/or safety and it is an anti-pattern.

Achieve a similar behavior safely via dynamic dispatch through `Any`,
and let each stateful `emit_*` crate attach its own hidden state
to the emitter by implementing a one-off trait via a convenience macro
(this is required due to the orphan rule for traits in Rust):

    struct FooState { ... }  // this is crate-private
    env::lazy_emit_state!(foo_state, FooState, FooState::init);

This essentially provides a dependency injection mechanism,
since emitter env does not need to know nor initialize any states.
Instead, each stateful crate that already depends on emitter/env
and has access to `e: &mut Emitter` will simply do:

    e.emit_state_mut()

*Note*: I made a design decision to panic if `DynState` slot is overtaken
(i.e., two crates using the same `foo_state` field,  as this is more fool proof
than silently stomping over a state from the other crate by re-initializing.

Reviewed By: shiqicao

Differential Revision: D17974004

fbshipit-source-id: aa59c5487343cf818e76c16ba6aaafd6d5288f97
hphp/hack/src/hhbc/emit_adata.rs [new file with mode: 0644]
hphp/hack/src/hhbc/emitter.rs