Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

rustc의 직렬화

rustc는 컴파일 중에 다양한 데이터를 직렬화하고 역직렬화해야 합니다. 구체적으로는 다음과 같습니다:

  • 주로 쿼리 출력으로 구성된 “크레이트 메타데이터“는 라이브러리 크레이트를 컴파일할 때 출력되는 rlib 및 rmeta 파일에 바이너리 형식으로 직렬화됩니다. 이후 이 rlib 및 rmeta 파일은 해당 라이브러리에 의존하는 크레이트에 의해 역직렬화됩니다.
  • 일부 쿼리 출력은 증분 컴파일 결과를 영속화하기 위해 바이너리 형식으로 직렬화됩니다.
  • CrateInfo는 -Z no-link 플래그를 사용하면 JSON으로 직렬화되고, -Z link-only 플래그를 사용하면 JSON에서 역직렬화됩니다.

Encodable 및 Decodable 트레이트

rustc_serialize 크레이트는 직렬화할 수 있는 타입을 위한 두 가지 트레이트를 정의합니다:

pub trait Encodable<S: Encoder> {
    fn encode(&self, s: &mut S) -> Result<(), S::Error>;
}

pub trait Decodable<D: Decoder>: Sized {
    fn decode(d: &mut D) -> Result<Self, D::Error>;
}

또한 정수 타입, 부동소수점 타입, bool, char, str 등 흔히 쓰이는 여러 표준 라이브러리 기본 타입에 대한 이 트레이트들의 구현도 정의합니다.

이러한 타입으로 구성된 타입의 경우, Encodable과 Decodable은 보통 derive로 구현됩니다. 이 derive는 역직렬화를 구조체나 열거형의 필드로 전달하는 구현을 생성합니다. 구조체의 경우 해당 impl은 대략 다음과 같습니다.

#![feature(rustc_private)]
extern crate rustc_serialize;
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};

struct MyStruct {
    int: u32,
    float: f32,
}

impl<E: Encoder> Encodable<E> for MyStruct {
    fn encode(&self, s: &mut E) -> Result<(), E::Error> {
        s.emit_struct("MyStruct", 2, |s| {
            s.emit_struct_field("int", 0, |s| self.int.encode(s))?;
            s.emit_struct_field("float", 1, |s| self.float.encode(s))
        })
    }
}

impl<D: Decoder> Decodable<D> for MyStruct {
    fn decode(s: &mut D) -> Result<MyStruct, D::Error> {
        s.read_struct("MyStruct", 2, |d| {
            let int = d.read_struct_field("int", 0, Decodable::decode)?;
            let float = d.read_struct_field("float", 1, Decodable::decode)?;

            Ok(MyStruct { int, float })
        })
    }
}

아레나 할당 타입의 인코딩과 디코딩

rustc에는 아레나 할당 타입이 많이 있습니다. 이러한 타입은 할당될 아레나에 접근하지 않고서는 역직렬화할 수 없습니다. TyDecoder와 TyEncoder 트레이트는 TyCtxt에 접근할 수 있게 해 주는 Decoder와 Encoder의 하위 트레이트입니다.

따라서 arena 할당 타입을 포함하는 타입은 자신의 Encodable 및 Decodable 구현의 타입 매개변수를 이 트레이트들로 한정할 수 있습니다. 예를 들면 다음과 같습니다.

impl<'tcx, D: TyDecoder<'tcx>> Decodable<D> for MyStruct<'tcx> {
    /* ... */
}

TyEncodable 및 TyDecodable derive 매크로는 이러한 구현으로 확장됩니다.

실제 arena 할당 타입을 디코딩하는 것은 더 어렵습니다. 고아 규칙 때문에 일부 구현을 작성할 수 없기 때문입니다. 이 문제를 우회하기 위해 rustc_middle에 RefDecodable 트레이트가 정의되어 있습니다. 이 트레이트는 어떤 타입에 대해서든 구현할 수 있습니다. TyDecodable 매크로는 참조를 디코딩하기 위해 RefDecodable을 호출하지만, 여러 제네릭 코드에서는 타입이 특정 디코더에 대해 실제로 Decodable이어야 합니다.

인턴된 타입의 경우 RefDecodable을 직접 구현하는 대신 ty::Predicate처럼 뉴타입 래퍼를 사용하고 Encodable과 Decodable을 직접 구현하는 편이 더 간단할 수 있습니다.

Derive 매크로

rustc_macros 크레이트는 Decodable과 Encodable 구현을 돕는 다양한 derive를 정의합니다.

  • Encodable 및 Decodable 매크로는 모든 Encoders와 Decoders에 적용되는 구현을 생성합니다. 이 매크로들은 rustc_middle에 의존하지 않는 크레이트나, TyEncoder를 구현하지 않는 타입으로 직렬화해야 하는 크레이트에서 사용해야 합니다.
  • MetadataEncodable은 rustc_metadata::rmeta::encoder::EncodeContext로만 디코딩할 수 있는 구현을 생성합니다.
  • BlobDecodable과 LazyDecodable은 MetadataEncodable에 대응하는 디코딩 측 매크로입니다. 이 매크로들은 rustc_metadata::rmeta의 메타데이터 블롭 디코더로 디코딩하는 구현을 생성합니다. 타입에 지연(lazy) 메타데이터 핸들이 없으면 BlobDecodable을, 있으면 LazyDecodable을 사용하십시오.
  • TyEncodable과 TyDecodable은 모든 TyEncoder 또는 TyDecoder에 적용되는 구현을 생성합니다. 이들은 크레이트 메타데이터 및/또는 증분 캐시에서만 직렬화되는 타입에 사용해야 하며, rustc_middle에 있는 직렬화 가능한 타입 대부분이 여기에 해당합니다.

축약 표현

Ty는 깊게 재귀적일 수 있으므로, 각 Ty를 단순하게 인코딩하면 크레이트 메타데이터가 매우 커집니다. 이를 처리하기 위해 각 TyEncoder는 출력에서 타입을 직렬화한 위치의 캐시를 가지고 있습니다. 인코딩하려는 타입이 캐시에 있으면 평소처럼 타입을 직렬화하는 대신, 작성 중인 파일 내의 바이트 오프셋을 인코딩합니다. ty::Predicate에도 비슷한 방식이 사용됩니다.

LazyValue<T>

크레이트 메타데이터는 TyCtxt<'tcx>가 생성되기 전에 처음 로드되므로, 일부 역직렬화는 메타데이터의 초기 로드 이후로 미뤄야 합니다. LazyValue<T> 타입은 T가 직렬화된 크레이트 메타데이터 내의 (상대) 오프셋을 감쌉니다. 몇 가지 변형으로 LazyArray<T>와 LazyTable<I, T>도 있습니다.

LazyArray<[T]>와 LazyTable<I, T> 타입은 Lazy<Vec<T>>와 Lazy<HashMap<I, T>>에 비해 다음과 같은 기능을 더 제공합니다:

  • 먼저 Vec<T>로 수집하지 않고도 Iterator에서 LazyArray<T>를 직접 인코딩할 수 있습니다.
  • LazyTable<I, T>를 인덱싱할 때 읽고 있는 항목 외의 다른 항목은 디코딩할 필요가 없습니다.

참고: LazyValue<T>는 처음 역직렬화된 후 그 값을 캐시하지 않습니다. 대신 쿼리 시스템 자체가 이러한 결과를 캐싱하는 주된 방법입니다.

특수화

몇몇 타입, 특히 DefId는 Encoder마다 서로 다른 구현이 필요합니다. 현재 이는 임시방편적인 특수화로 처리됩니다. 예를 들어 DefId에는 Encodable<E>의 default 구현과 Encodable<CacheEncoder>에 대한 특수화된 구현이 있습니다.