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>에 대한 특수화된 구현이 있습니다.