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.
5 use indexmap::IndexMap;
6 use std::{collections::hash_map::RandomState, fmt, iter::FromIterator};
8 use ast_class_expr_rust as ast_class_expr;
9 use ast_scope_rust as ast_scope;
10 use env::emitter::Emitter;
12 use hhbc_string_utils_rust as string_utils;
13 use naming_special_names_rust::{math, members, special_functions, typehints};
14 use options::HhvmFlags;
17 aast_visitor::{visit_mut, AstParams, NodeMut, VisitorMut},
18 ast as tast, ast_defs,
19 namespace_env::Env as Namespace,
22 use runtime::TypedValue;
24 use itertools::Itertools;
26 #[derive(Debug, PartialEq, Eq)]
30 Unrecoverable(String),
34 fn unrecoverable(s: impl Into<String>) -> Self {
35 Self::Unrecoverable(s.into())
39 impl fmt::Display for Error {
40 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42 Self::NotLiteral => write!(f, "NotLiteral"),
43 Self::UserDefinedConstant => write!(f, "UserDefinedConstant"),
44 Self::Unrecoverable(msg) => write!(f, "{}", msg),
56 fn radix(s: &str) -> Radix {
58 if s.len() > 1 && (s[0] as char) == '0' {
60 'b' | 'B' => Radix::Bin,
61 'x' | 'X' => Radix::Hex,
69 fn try_type_intlike(s: &str) -> Option<i64> {
71 Radix::Dec => s.parse().ok(),
72 Radix::Bin => i64::from_str_radix(&s[2..], 2).ok(),
75 let sb = s.as_bytes();
76 // Ocaml's version truncate if any digit is greater then 7.
78 if sb[i] >= b'0' && sb[i] <= b'7' {
86 i64::from_str_radix(std::str::from_utf8(sb).unwrap(), 8).ok()
91 Radix::Hex => i64::from_str_radix(&s[2..], 16).ok(),
95 fn class_const_to_typed_value(
99 ) -> Result<TypedValue, Error> {
100 if id.1 == members::M_CLASS {
101 let cexpr = ast_class_expr::ClassExpr::class_id_to_class_expr(
105 &ast_scope::Scope::toplevel(),
108 if let ast_class_expr::ClassExpr::Id(ast_defs::Id(_, cname)) = cexpr {
109 if emitter.options().emit_class_pointers() == 2 {
110 let cname = hhbc_id_rust::class::Type::from_ast_name_and_mangle(&cname).into();
111 return Ok(TypedValue::LazyClass(cname));
113 let cname = hhbc_id_rust::class::Type::from_ast_name(&cname).into();
114 return Ok(TypedValue::String(cname));
118 Err(Error::UserDefinedConstant)
121 fn varray_to_typed_value(
124 fields: &Vec<tast::Expr>,
125 ) -> Result<TypedValue, Error> {
126 let tv_fields: Vec<TypedValue> = fields
128 .map(|x| expr_to_typed_value(emitter, ns, x))
129 .collect::<Result<_, Error>>()?;
130 Ok(TypedValue::Vec(tv_fields))
133 fn darray_to_typed_value(
136 fields: &Vec<(tast::Expr, tast::Expr)>,
137 ) -> Result<TypedValue, Error> {
138 let tv_fields: Vec<(TypedValue, TypedValue)> = fields
142 key_expr_to_typed_value(emitter, ns, k)?,
143 expr_to_typed_value(emitter, ns, v)?,
146 .collect::<Result<_, Error>>()?;
147 Ok(TypedValue::Dict(update_duplicates_in_map(tv_fields)))
150 fn set_afield_to_typed_value_pair(
153 afield: &tast::Afield,
154 ) -> Result<(TypedValue, TypedValue), Error> {
156 tast::Afield::AFvalue(v) => set_afield_value_to_typed_value_pair(e, ns, v),
157 _ => Err(Error::unrecoverable(
158 "set_afield_to_typed_value_pair: unexpected key=>value",
163 fn set_afield_value_to_typed_value_pair(
167 ) -> Result<(TypedValue, TypedValue), Error> {
168 let tv = key_expr_to_typed_value(e, ns, v)?;
172 fn afield_to_typed_value_pair(
175 afield: &tast::Afield,
176 ) -> Result<(TypedValue, TypedValue), Error> {
178 tast::Afield::AFvalue(_) => Err(Error::unrecoverable(
179 "afield_to_typed_value_pair: unexpected value",
181 tast::Afield::AFkvalue(key, value) => kv_to_typed_value_pair(emitter, ns, key, value),
185 fn kv_to_typed_value_pair(
190 ) -> Result<(TypedValue, TypedValue), Error> {
192 key_expr_to_typed_value(emitter, ns, key)?,
193 expr_to_typed_value(emitter, ns, value)?,
197 fn value_afield_to_typed_value(
200 afield: &tast::Afield,
201 ) -> Result<TypedValue, Error> {
203 tast::Afield::AFvalue(e) => expr_to_typed_value(emitter, ns, e),
204 tast::Afield::AFkvalue(_, _) => Err(Error::unrecoverable(
205 "value_afield_to_typed_value: unexpected key=>value",
210 fn key_expr_to_typed_value(
214 ) -> Result<TypedValue, Error> {
215 let tv = expr_to_typed_value(emitter, ns, expr)?;
216 let fold_lc = emitter
220 .contains(HhvmFlags::FOLD_LAZY_CLASS_KEYS);
222 TypedValue::Int(_) | TypedValue::String(_) => Ok(tv),
223 TypedValue::LazyClass(_) if fold_lc => Ok(tv),
224 _ => Err(Error::NotLiteral),
228 fn keyset_value_afield_to_typed_value(
231 afield: &tast::Afield,
232 ) -> Result<TypedValue, Error> {
233 let tv = value_afield_to_typed_value(emitter, ns, afield)?;
234 let fold_lc = emitter
238 .contains(HhvmFlags::FOLD_LAZY_CLASS_KEYS);
240 TypedValue::Int(_) | TypedValue::String(_) => Ok(tv),
241 TypedValue::LazyClass(_) if fold_lc => Ok(tv),
242 _ => Err(Error::NotLiteral),
246 fn shape_to_typed_value(
249 fields: &Vec<(tast::ShapeFieldName, tast::Expr)>,
250 ) -> Result<TypedValue, Error> {
255 ast_defs::ShapeFieldName::SFlitInt((_, s)) => {
256 let tv = int_expr_to_typed_value(s)?;
258 TypedValue::Int(_) => tv,
260 return Err(Error::unrecoverable(format!(
261 "{} is not a valid integer index",
267 ast_defs::ShapeFieldName::SFlitStr(id) => {
268 // FIXME: This is not safe--string literals are binary
269 // strings. There's no guarantee that they're valid UTF-8.
270 TypedValue::String(unsafe { String::from_utf8_unchecked(id.1.clone().into()) })
272 ast_defs::ShapeFieldName::SFclassConst(class_id, id) => class_const_to_typed_value(
274 &tast::ClassId(Pos::make_none(), tast::ClassId_::CI(class_id.clone())),
278 Ok((key, expr_to_typed_value(emitter, ns, expr)?))
280 .collect::<Result<_, _>>()?;
281 Ok(TypedValue::Dict(a))
284 pub fn vec_to_typed_value(
287 fields: &[tast::Afield],
288 ) -> Result<TypedValue, Error> {
292 .map(|f| value_afield_to_typed_value(e, ns, f))
293 .collect::<Result<_, _>>()?,
297 pub fn expr_to_typed_value(
301 ) -> Result<TypedValue, Error> {
302 expr_to_typed_value_(
303 e, ns, expr, false, /*allow_maps*/
304 false, /*force_class_const*/
308 pub fn expr_to_typed_value_(
313 force_class_const: bool,
314 ) -> Result<TypedValue, Error> {
316 // TODO: ML equivalent has this as an implicit parameter that defaults to false.
318 Int(s) => int_expr_to_typed_value(s),
319 tast::Expr_::True => Ok(TypedValue::Bool(true)),
320 False => Ok(TypedValue::Bool(false)),
321 Null => Ok(TypedValue::Null),
323 // FIXME: This is not safe--string literals are binary strings.
324 // There's no guarantee that they're valid UTF-8.
325 Ok(TypedValue::String(unsafe {
326 std::string::String::from_utf8_unchecked(s.clone().into())
329 EnumAtom(s) => Ok(TypedValue::String(s.clone())),
332 Ok(TypedValue::float(std::f64::INFINITY))
333 } else if s == math::NEG_INF {
334 Ok(TypedValue::float(std::f64::NEG_INFINITY))
335 } else if s == math::NAN {
336 Ok(TypedValue::float(std::f64::NAN))
339 .map(|x| TypedValue::float(x))
340 .map_err(|_| Error::NotLiteral)
348 .map(|x| x.1 == special_functions::HHAS_ADATA)
352 &[tast::Expr(_, tast::Expr_::String(ref data))] => {
353 // FIXME: This is not safe--string literals are binary strings.
354 // There's no guarantee that they're valid UTF-8.
355 Ok(TypedValue::HhasAdata(unsafe {
356 std::string::String::from_utf8_unchecked(data.clone().into())
359 _ => Err(Error::NotLiteral),
363 Varray(fields) => varray_to_typed_value(emitter, ns, &fields.1),
364 Darray(fields) => darray_to_typed_value(emitter, ns, &fields.1),
366 Id(id) if id.1 == math::NAN => Ok(TypedValue::float(std::f64::NAN)),
367 Id(id) if id.1 == math::INF => Ok(TypedValue::float(std::f64::INFINITY)),
368 Id(_) => Err(Error::UserDefinedConstant),
370 Collection(x) if x.0.name().eq("vec") => vec_to_typed_value(emitter, ns, &x.2),
371 Collection(x) if x.0.name().eq("keyset") => Ok(TypedValue::Keyset(
373 .map(|x| keyset_value_afield_to_typed_value(emitter, ns, x))
374 .collect::<Result<Vec<_>, _>>()?
380 if x.0.name().eq("dict")
382 && (string_utils::cmp(&(x.0).1, "Map", false, true)
383 || string_utils::cmp(&(x.0).1, "ImmMap", false, true)) =>
387 .map(|x| afield_to_typed_value_pair(emitter, ns, x))
388 .collect::<Result<_, _>>()?;
389 Ok(TypedValue::Dict(update_duplicates_in_map(values)))
393 && (string_utils::cmp(&(x.0).1, "Set", false, true)
394 || string_utils::cmp(&(x.0).1, "ImmSet", false, true)) =>
398 .map(|x| set_afield_to_typed_value_pair(emitter, ns, x))
399 .collect::<Result<_, _>>()?;
400 Ok(TypedValue::Dict(update_duplicates_in_map(values)))
402 Tuple(x) => Ok(TypedValue::Vec(
404 .map(|e| expr_to_typed_value(emitter, ns, e))
405 .collect::<Result<_, _>>()?,
407 ValCollection(x) if x.0 == tast::VcKind::Vec || x.0 == tast::VcKind::Vector => {
410 .map(|e| expr_to_typed_value(emitter, ns, e))
411 .collect::<Result<_, _>>()?,
414 ValCollection(x) if x.0 == tast::VcKind::Keyset => Ok(TypedValue::Keyset(
417 expr_to_typed_value(emitter, ns, e).and_then(|tv| match tv {
418 TypedValue::Int(_) | TypedValue::String(_) => Ok(tv),
419 TypedValue::LazyClass(_)
424 .contains(HhvmFlags::FOLD_LAZY_CLASS_KEYS) =>
428 _ => Err(Error::NotLiteral),
431 .collect::<Result<Vec<_>, _>>()?
436 ValCollection(x) if x.0 == tast::VcKind::Set || x.0 == tast::VcKind::ImmSet => {
439 .map(|e| set_afield_value_to_typed_value_pair(emitter, ns, e))
440 .collect::<Result<_, _>>()?;
441 Ok(TypedValue::Dict(update_duplicates_in_map(values)))
444 KeyValCollection(x) => {
447 .map(|e| kv_to_typed_value_pair(emitter, ns, &e.0, &e.1))
448 .collect::<Result<_, _>>()?;
449 Ok(TypedValue::Dict(update_duplicates_in_map(values)))
452 Shape(fields) => shape_to_typed_value(emitter, ns, fields),
454 if emitter.options().emit_class_pointers() == 1 && !force_class_const {
455 Err(Error::NotLiteral)
457 class_const_to_typed_value(emitter, &x.0, &x.1)
460 ClassGet(_) => Err(Error::UserDefinedConstant),
461 As(x) if (x.1).1.is_hlike() => expr_to_typed_value_(emitter, ns, &x.0, allow_maps, false),
462 _ => Err(Error::NotLiteral),
466 fn int_expr_to_typed_value(s: &str) -> Result<TypedValue, Error> {
468 try_type_intlike(&s).unwrap_or(std::i64::MAX),
472 fn update_duplicates_in_map(kvs: Vec<(TypedValue, TypedValue)>) -> Vec<(TypedValue, TypedValue)> {
473 IndexMap::<_, _, RandomState>::from_iter(kvs.into_iter())
478 fn cast_value(hint: &tast::Hint_, v: TypedValue) -> Result<TypedValue, Error> {
480 tast::Hint_::Happly(ast_defs::Id(_, id), args) if args.is_empty() => {
481 let id = string_utils::strip_hh_ns(id);
482 if id == typehints::BOOL {
484 } else if id == typehints::STRING {
486 } else if id == typehints::FLOAT {
494 .ok_or(Error::NotLiteral)
497 fn unop_on_value(unop: &ast_defs::Uop, v: TypedValue) -> Result<TypedValue, Error> {
499 ast_defs::Uop::Unot => v.not(),
500 ast_defs::Uop::Uplus => v.add(&TypedValue::Int(0)),
501 ast_defs::Uop::Uminus => v.neg(),
502 ast_defs::Uop::Utild => v.bitwise_not(),
503 ast_defs::Uop::Usilence => Some(v.clone()),
506 .ok_or(Error::NotLiteral)
510 binop: &ast_defs::Bop,
513 ) -> Result<TypedValue, Error> {
514 use ast_defs::Bop::*;
516 Dot => v1.concat(v2),
518 Minus => v1.sub(&v2),
520 Ltlt => v1.shift_left(&v2),
521 Slash => v1.div(&v2),
522 Bar => v1.bitwise_or(&v2),
525 .ok_or(Error::NotLiteral)
528 fn value_to_expr_(v: TypedValue) -> Result<tast::Expr_, Error> {
532 Int(i) => Expr_::Int(i.to_string()),
533 Float(i) => Expr_::Float(hhbc_string_utils_rust::float::to_string(i)),
534 Bool(false) => Expr_::False,
535 Bool(true) => Expr_::True,
536 String(s) => Expr_::String(s.into()),
537 LazyClass(_) => return Err(Error::unrecoverable("value_to_expr: lazyclass NYI")),
539 Uninit => return Err(Error::unrecoverable("value_to_expr: uninit value")),
540 Vec(_) => return Err(Error::unrecoverable("value_to_expr: vec NYI")),
541 Keyset(_) => return Err(Error::unrecoverable("value_to_expr: keyset NYI")),
542 HhasAdata(_) => return Err(Error::unrecoverable("value_to_expr: HhasAdata NYI")),
543 Dict(_) => return Err(Error::unrecoverable("value_to_expr: dict NYI")),
547 struct FolderVisitor<'a> {
548 emitter: &'a Emitter,
549 empty_namespace: &'a Namespace,
552 impl<'a> FolderVisitor<'a> {
553 fn new(emitter: &'a Emitter, empty_namespace: &'a Namespace) -> Self {
561 impl<'ast> VisitorMut<'ast> for FolderVisitor<'_> {
562 type P = AstParams<(), Error>;
564 fn object(&mut self) -> &mut dyn VisitorMut<'ast, P = Self::P> {
568 fn visit_expr_(&mut self, c: &mut (), p: &mut tast::Expr_) -> Result<(), Error> {
569 p.recurse(c, self.object())?;
570 let new_p = match p {
571 tast::Expr_::Cast(e) => expr_to_typed_value(self.emitter, self.empty_namespace, &e.1)
572 .and_then(|v| cast_value(&(e.0).1, v))
575 tast::Expr_::Unop(e) => expr_to_typed_value(self.emitter, self.empty_namespace, &e.1)
576 .and_then(|v| unop_on_value(&e.0, v))
579 tast::Expr_::Binop(e) => expr_to_typed_value(self.emitter, self.empty_namespace, &e.1)
581 expr_to_typed_value(self.emitter, self.empty_namespace, &e.2)
582 .and_then(|v2| binop_on_values(&e.0, v1, v2).map(value_to_expr_))
587 if let Some(new_p) = new_p {
595 expr: &mut tast::Expr,
597 empty_namespace: &Namespace,
598 ) -> Result<(), Error> {
599 visit_mut(&mut FolderVisitor::new(e, empty_namespace), &mut (), expr)
603 p: &mut tast::Program,
605 empty_namespace: &Namespace,
606 ) -> Result<(), Error> {
607 visit_mut(&mut FolderVisitor::new(e, empty_namespace), &mut (), p)
610 pub fn literals_from_exprs(
612 exprs: &mut [tast::Expr],
614 ) -> Result<Vec<TypedValue>, Error> {
615 for expr in exprs.iter_mut() {
616 fold_expr(expr, e, ns)?;
620 .map(|expr| expr_to_typed_value_(e, ns, expr, false, true))
622 if let Err(Error::NotLiteral) = ret {
623 Err(Error::unrecoverable("literals_from_exprs: not literal"))