1 // Copyright (c) Facebook, Inc. and its affiliates.
3 // This source code is licensed under the MIT license found in the
4 // LICENSE file in the "hack" directory of this source tree.
6 use adata_state::AdataState;
7 use env::emitter::Emitter;
9 use hhas_adata::{HhasAdata, DARRAY_PREFIX, DICT_PREFIX, KEYSET_PREFIX, VARRAY_PREFIX, VEC_PREFIX};
11 use hhbc_string_utils as string_utils;
12 use instruction_sequence::{Error, InstrSeq};
13 use options::HhvmFlags;
14 use runtime::TypedValue;
16 pub fn rewrite_typed_values<'arena, 'decl>(
17 emitter: &mut Emitter<'arena, 'decl>,
18 instrseq: &mut InstrSeq<'arena>,
19 ) -> std::result::Result<(), instruction_sequence::Error> {
20 instrseq.map_result_mut(&mut |instr| rewrite_typed_value(emitter, instr))
23 fn rewrite_typed_value<'arena, 'decl>(
24 e: &mut Emitter<'arena, 'decl>,
25 instr: &mut Instruct<'arena>,
26 ) -> std::result::Result<(), instruction_sequence::Error> {
27 if let Instruct::ILitConst(InstructLitConst::TypedValue(tv)) = instr {
28 *instr = Instruct::ILitConst(match &tv {
29 TypedValue::Uninit => {
30 return Err(Error::Unrecoverable("rewrite_typed_value: uninit".into()));
32 TypedValue::Null => InstructLitConst::Null,
33 TypedValue::Bool(true) => InstructLitConst::True,
34 TypedValue::Bool(false) => InstructLitConst::False,
35 TypedValue::Int(i) => InstructLitConst::Int(*i),
36 TypedValue::String(s) => InstructLitConst::String(*s),
37 TypedValue::LazyClass(s) => {
38 let classid: hhbc_ast::ClassId<'arena> =
39 hhbc_id::class::ClassType::from_ast_name_and_mangle(e.alloc, s.unsafe_as_str());
40 InstructLitConst::LazyClass(classid)
42 TypedValue::Float(f) => {
43 let fstr = bumpalo::collections::String::from_str_in(
44 string_utils::float::to_string(*f).as_str(),
48 InstructLitConst::Double(Str::from(fstr))
50 TypedValue::Keyset(_) => {
51 let arrayid = Str::from(get_array_identifier(e, tv));
52 InstructLitConst::Keyset(arrayid)
54 TypedValue::Vec(_) => {
55 let arrayid = Str::from(get_array_identifier(e, tv));
56 InstructLitConst::Vec(arrayid)
58 TypedValue::Dict(_) => {
59 let arrayid = Str::from(get_array_identifier(e, tv));
60 InstructLitConst::Dict(arrayid)
62 TypedValue::HhasAdata(d) if d.is_empty() => {
63 return Err(Error::Unrecoverable("HhasAdata may not be empty".into()));
65 TypedValue::HhasAdata(d) => {
66 let arrayid = Str::from(get_array_identifier(e, tv));
67 let d = d.unsafe_as_str();
69 VARRAY_PREFIX | VEC_PREFIX => InstructLitConst::Vec(arrayid),
70 DARRAY_PREFIX | DICT_PREFIX => InstructLitConst::Dict(arrayid),
71 KEYSET_PREFIX => InstructLitConst::Keyset(arrayid),
73 return Err(Error::Unrecoverable(format!(
74 "Unknown HhasAdata data: {}",
85 pub fn get_array_identifier<'arena, 'decl>(
86 e: &mut Emitter<'arena, 'decl>,
87 tv: &TypedValue<'arena>,
89 if e.options().hhvm.flags.contains(HhvmFlags::ARRAY_PROVENANCE) {
92 match e.emit_adata_state_mut().array_identifier_map.get(tv) {
94 let id = next_adata_id(e, tv);
95 e.emit_adata_state_mut()
97 .insert(tv.clone(), id);
105 fn next_adata_id<'arena, 'decl>(
106 e: &mut Emitter<'arena, 'decl>,
107 value: &TypedValue<'arena>,
110 let mut state = e.emit_adata_state_mut();
111 let id: &str = alloc.alloc_str(format!("A_{}", state.array_identifier_counter).as_str());
112 state.array_identifier_counter += 1;
113 state.adata.push(HhasAdata {
115 value: value.clone(),
120 pub fn take<'arena, 'decl>(e: &mut Emitter<'arena, 'decl>) -> AdataState<'arena> {
121 let state = e.emit_adata_state_mut();
122 std::mem::take(state)
129 // verify it compiles (no test attribute)
131 #[allow(clippy::needless_lifetimes)]
132 fn ref_state_from_emiter<'arena, 'decl>(e: &Emitter<'arena, 'decl>) {
133 let _: &AdataState<'_> = e.emit_adata_state();
136 // verify it compiles (no test attribute)
138 #[allow(clippy::needless_lifetimes)]
139 fn mut_state_from_emiter<'arena, 'decl>(e: &mut Emitter<'arena, 'decl>) {
140 let _: &mut AdataState<'_> = e.emit_adata_state_mut();