PageRenderTime 56ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/hphp/hack/src/hhbc/typed_value.ml

http://github.com/facebook/hiphop-php
OCaml | 298 lines | 208 code | 34 blank | 56 comment | 9 complexity | dfcbbbba19ea29683c619da1900f6632 MD5 | raw file
Possible License(s): LGPL-2.1, BSD-2-Clause, BSD-3-Clause, MPL-2.0-no-copyleft-exception, MIT, LGPL-2.0, Apache-2.0
  1. (*
  2. * Copyright (c) 2017, Facebook, Inc.
  3. * All rights reserved.
  4. *
  5. * This source code is licensed under the MIT license found in the
  6. * LICENSE file in the "hack" directory of this source tree.
  7. *
  8. *)
  9. open Hh_prelude
  10. (* We introduce a type for Hack/PHP values, mimicking what happens at runtime.
  11. * Currently this is used for constant folding. By defining a special type, we
  12. * ensure independence from usage: for example, it can be used for optimization
  13. * on ASTs, or on bytecode, or (in future) on a compiler intermediate language.
  14. * HHVM takes a similar approach: see runtime/base/typed-value.h
  15. *)
  16. type t =
  17. (* Used for fields that are initialized in the 86pinit method *)
  18. | Uninit
  19. (* Hack/PHP integers are 64-bit *)
  20. | Int of Int64.t
  21. | Bool of bool
  22. (* Both Hack/PHP and Caml floats are IEEE754 64-bit *)
  23. | Float of float
  24. | String of string
  25. | Null
  26. (* Classic PHP arrays with explicit (key,value) entries *)
  27. | HhasAdata of string
  28. | Array of (t * t) list
  29. | VArray of t list * prov_tag
  30. | DArray of (t * t) list * prov_tag
  31. (* Hack arrays: vectors, keysets, and dictionaries *)
  32. | Vec of t list * prov_tag
  33. | Keyset of t list
  34. | Dict of (t * t) list * prov_tag
  35. and prov_tag = Pos.t option [@@deriving ord]
  36. let compare : t -> t -> int = compare
  37. let compare_prov_tag : prov_tag -> prov_tag -> int = compare_prov_tag
  38. module TVMap : WrappedMap.S with type key = t = WrappedMap.Make (struct
  39. type key = t
  40. type t = key
  41. let compare : t -> t -> int = compare
  42. end)
  43. (* Some useful constants *)
  44. let zero = Int Int64.zero
  45. let null = Null
  46. module StringOps = struct
  47. let bitwise_not s =
  48. let result = Bytes.create (String.length s) in
  49. Caml.String.iteri
  50. (fun i c ->
  51. (* keep only last byte *)
  52. let b = lnot (int_of_char c) land 0xFF in
  53. Bytes.set result i (char_of_int b))
  54. s;
  55. Bytes.to_string result
  56. end
  57. (* Cast to a boolean: the (bool) operator in PHP *)
  58. let to_bool v =
  59. match v with
  60. | Uninit -> false (* Should not happen *)
  61. | Bool b -> b
  62. | Null -> false
  63. | String "" -> false
  64. | String "0" -> false
  65. | String _ -> true
  66. | Int i -> Int64.(i <> Int64.zero)
  67. | Float f -> Float.(f <> 0.0)
  68. (* Empty collections cast to false *)
  69. | Dict ([], _)
  70. | Array []
  71. | VArray ([], _)
  72. | DArray ([], _)
  73. | Keyset []
  74. | Vec ([], _) ->
  75. false
  76. (* Non-empty collections cast to true *)
  77. | HhasAdata _
  78. | Dict (_, _)
  79. | Array _
  80. | VArray _
  81. | DArray _
  82. | Keyset _
  83. | Vec (_, _) ->
  84. true
  85. (* try to convert numeric *)
  86. let string_to_int_opt ~allow_inf s =
  87. let int_opt = (try Some (Int64.of_string s) with _ -> None) in
  88. match int_opt with
  89. | None ->
  90. (try
  91. let s = float_of_string s in
  92. if (not allow_inf) && Float.(s = infinity || s = neg_infinity) then
  93. None
  94. else
  95. Some (Int64.of_float s)
  96. with _ -> None)
  97. | x -> x
  98. (* Cast to an integer: the (int) operator in PHP. Return None if we can't
  99. * or won't produce the correct value *)
  100. let to_int v =
  101. match v with
  102. | Uninit -> None (* Should not happen *)
  103. (* Unreachable - the only callsite of to_int is cast_to_arraykey, which never
  104. * calls it with String *)
  105. | String _ -> None
  106. | Int i -> Some i
  107. | Float f ->
  108. let fpClass = Float.classify f in
  109. begin
  110. match fpClass with
  111. (* Here's a handy dandy chart of all possible values based on language
  112. * | float | PHP 5 | HHVM | PHP 7
  113. * ----------------------------------------
  114. * | NaN | int_min | int_min | 0
  115. * | INF | int_min | 0 | 0
  116. * | -INF | int_min | int_min | 0
  117. * For NaN, the value is min_int in HHVM
  118. * For positive infinity, the value is 0 in HHVM
  119. * For negative infinity the value is min_int in HHVM
  120. * For PHP7, the value is always 0
  121. * Thus if the float is infinity OR we're in PHP7, set it to 0
  122. *)
  123. | Float.Class.Nan
  124. | Float.Class.Infinite ->
  125. if Float.(f = infinity) then
  126. Some Int64.zero
  127. else
  128. Some Caml.Int64.min_int
  129. | _ ->
  130. (* mimic double-to-int64.h *)
  131. let cast v = (try Some (Int64.of_float v) with Failure _ -> None) in
  132. if Float.(f >= 0.0) then
  133. if Float.(f < Int64.to_float Caml.Int64.max_int) then
  134. cast f
  135. else
  136. Some 0L
  137. else
  138. cast f
  139. end
  140. | _ ->
  141. Some
  142. ( if to_bool v then
  143. Int64.one
  144. else
  145. Int64.zero )
  146. (* Cast to a float: the (float) operator in PHP. Return None if we can't
  147. * or won't produce the correct value *)
  148. let to_float v =
  149. match v with
  150. | Uninit -> None (* Should not happen *)
  151. | String _ ->
  152. None (* Not worth trying to replicate float printing sematics here *)
  153. | Int i -> (try Some (Int64.to_float i) with Failure _ -> None)
  154. | Float f -> Some f
  155. | _ ->
  156. Some
  157. ( if to_bool v then
  158. 1.0
  159. else
  160. 0.0 )
  161. (* Cast to a string: the (string) operator in PHP. Return None if we can't
  162. * or won't produce the correct value *)
  163. let to_string v =
  164. match v with
  165. | Uninit -> None (* Should not happen *)
  166. | Bool false -> Some ""
  167. | Bool true -> Some "1"
  168. | Null -> Some ""
  169. | Int i -> Some (Int64.to_string i)
  170. | String s -> Some s
  171. | Float _ ->
  172. None (* Not worth trying to replicate float printing sematics here *)
  173. | _ -> None
  174. (* Integer operations. For now, we don't attempt to implement the
  175. * overflow-to-float semantics *)
  176. let add_int i1 i2 = Some (Int (Int64.( + ) i1 i2))
  177. let neg i =
  178. match i with
  179. | Int i -> Some (Int (Int64.neg i))
  180. | Float f -> Some (Float (0.0 -. f))
  181. | _ -> None
  182. let sub_int i1 i2 = Some (Int (Int64.( - ) i1 i2))
  183. (* Arithmetic. For now, only on pure integer or float operands *)
  184. let sub v1 v2 =
  185. match (v1, v2) with
  186. | (Int i1, Int i2) -> sub_int i1 i2
  187. | (Float f1, Float f2) -> Some (Float (f1 -. f2))
  188. | _ -> None
  189. let mul_int i1 i2 = Some (Int (Int64.( * ) i1 i2))
  190. (* Arithmetic. For now, only on pure integer or float operands *)
  191. let mul v1 v2 =
  192. match (v1, v2) with
  193. | (Int i1, Int i2) -> mul_int i1 i2
  194. | (Float f1, Float f2) -> Some (Float (f1 *. f2))
  195. | (Int i1, Float f2) -> Some (Float (Int64.to_float i1 *. f2))
  196. | (Float f1, Int i2) -> Some (Float (f1 *. Int64.to_float i2))
  197. | _ -> None
  198. (* Arithmetic. For now, only on pure integer or float operands *)
  199. let div v1 v2 =
  200. match (v1, v2) with
  201. | (Int i1, Int i2) when Int64.(i2 <> 0L) ->
  202. if Int64.(rem i1 i2 = 0L) then
  203. Some (Int (Int64.( / ) i1 i2))
  204. else
  205. Some (Float (Int64.to_float i1 /. Int64.to_float i2))
  206. | (Float f1, Float f2) when Float.(f2 <> 0.0) -> Some (Float (f1 /. f2))
  207. | (Int i1, Float f2) when Float.(f2 <> 0.0) ->
  208. Some (Float (Int64.to_float i1 /. f2))
  209. | (Float f1, Int i2) when Int64.(i2 <> 0L) ->
  210. Some (Float (f1 /. Int64.to_float i2))
  211. | _ -> None
  212. (* Arithmetic. For now, only on pure integer or float operands *)
  213. let add v1 v2 =
  214. match (v1, v2) with
  215. | (Float f1, Float f2) -> Some (Float (f1 +. f2))
  216. | (Int i1, Int i2) -> add_int i1 i2
  217. | (Int i1, Float f2) -> Some (Float (Int64.to_float i1 +. f2))
  218. | (Float f1, Int i2) -> Some (Float (f1 +. Int64.to_float i2))
  219. | (_, _) -> None
  220. let shift_left v1 v2 =
  221. match (v1, v2) with
  222. | (Int i1, Int i2) when Int64.(i2 >= 0L) ->
  223. begin
  224. try
  225. let v = Int64.to_int_exn i2 in
  226. Some (Int (Int64.shift_left i1 v))
  227. with _ -> None
  228. end
  229. | _ -> None
  230. (* Arithmetic. For now, only on pure integer operands *)
  231. let bitwise_or v1 v2 =
  232. match (v1, v2) with
  233. | (Int i1, Int i2) -> Some (Int (Int64.( lor ) i1 i2))
  234. | _ -> None
  235. (* String concatenation *)
  236. let concat v1 v2 =
  237. match Option.both (to_string v1) (to_string v2) with
  238. | Some (l, r) -> Some (String (l ^ r))
  239. | None -> None
  240. (* Bitwise operations. *)
  241. let bitwise_not v =
  242. match v with
  243. | Int i -> Some (Int (Int64.lnot i))
  244. | String s -> Some (String (StringOps.bitwise_not s))
  245. | _ -> None
  246. (* Logical operators *)
  247. let not v = Some (Bool (not (to_bool v)))
  248. let cast_to_string v = Option.map (to_string v) (fun x -> String x)
  249. let cast_to_int v = Option.map (to_int v) (fun x -> Int x)
  250. let cast_to_bool v = Some (Bool (to_bool v))
  251. let cast_to_float v = Option.map (to_float v) (fun x -> Float x)
  252. let cast_to_arraykey v =
  253. match v with
  254. | String s -> Some (String s)
  255. | Null -> Some (String "")
  256. | Uninit
  257. | Array _
  258. | VArray _
  259. | DArray _
  260. | Vec _
  261. | Keyset _
  262. | Dict _ ->
  263. None
  264. | _ -> cast_to_int v