PageRenderTime 45ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/hphp/hhbbc/interp-builtin.cpp

https://gitlab.com/alvinahmadov2/hhvm
C++ | 374 lines | 263 code | 54 blank | 57 comment | 31 complexity | 6128cbc79b629347f67266c02b2094f8 MD5 | raw file
  1. /*
  2. +----------------------------------------------------------------------+
  3. | HipHop for PHP |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 2010-2015 Facebook, Inc. (http://www.facebook.com) |
  6. +----------------------------------------------------------------------+
  7. | This source file is subject to version 3.01 of the PHP license, |
  8. | that is bundled with this package in the file LICENSE, and is |
  9. | available through the world-wide-web at the following url: |
  10. | http://www.php.net/license/3_01.txt |
  11. | If you did not receive a copy of the PHP license and are unable to |
  12. | obtain it through the world-wide-web, please send a note to |
  13. | license@php.net so we can mail you a copy immediately. |
  14. +----------------------------------------------------------------------+
  15. */
  16. #include "hphp/hhbbc/interp.h"
  17. #include "hphp/util/trace.h"
  18. #include "hphp/runtime/base/execution-context.h"
  19. #include "hphp/hhbbc/eval-cell.h"
  20. #include "hphp/hhbbc/type-builtins.h"
  21. #include "hphp/hhbbc/type-system.h"
  22. #include "hphp/hhbbc/interp-internal.h"
  23. namespace HPHP { namespace HHBBC {
  24. namespace {
  25. //////////////////////////////////////////////////////////////////////
  26. #define X(x) const StaticString s_##x(#x);
  27. X(array_combine)
  28. X(array_fill)
  29. X(array_fill_keys)
  30. X(array_flip)
  31. X(array_reverse)
  32. X(array_search)
  33. X(array_slice)
  34. X(array_sum)
  35. X(array_values)
  36. X(base64decode)
  37. X(base64_encode)
  38. X(base_convert)
  39. X(bindec)
  40. X(ceil)
  41. X(chr)
  42. X(count)
  43. X(decbin)
  44. X(dechex)
  45. X(decoct)
  46. X(explode)
  47. X(floor)
  48. X(getrandmax)
  49. X(gettype)
  50. X(hexdec)
  51. X(implode)
  52. X(in_array)
  53. X(log)
  54. X(log10)
  55. X(max)
  56. X(mt_rand)
  57. X(mt_getrandmax)
  58. X(octdec)
  59. X(ord)
  60. X(pow)
  61. X(preg_quote)
  62. X(range)
  63. X(rawurldecode)
  64. X(rawurlencode)
  65. X(round)
  66. X(serialize)
  67. X(sha1)
  68. X(strlen)
  69. X(str_repeat)
  70. X(str_split)
  71. X(substr)
  72. X(trim)
  73. X(urldecode)
  74. X(urlencode)
  75. X(utf8_encode)
  76. X(version_compare)
  77. X(sqrt)
  78. X(abs)
  79. #undef X
  80. //////////////////////////////////////////////////////////////////////
  81. folly::Optional<Type> const_fold(ISS& env,
  82. const bc::FCallBuiltin& op,
  83. const StaticString& name) {
  84. FTRACE(1, "invoking: {}\n", name.get()->data());
  85. assert(!RuntimeOption::EvalJit);
  86. return eval_cell([&] {
  87. // Don't pop the args yet---if the builtin throws at compile time (because
  88. // it would raise a warning or something at runtime) we're going to leave
  89. // the call alone.
  90. std::vector<Cell> args(op.arg1);
  91. for (auto i = uint32_t{0}; i < op.arg1; ++i) {
  92. args[op.arg1 - i - 1] = *tv(topT(env, i));
  93. }
  94. Cell retVal;
  95. auto const func = Unit::lookupFunc(name.get());
  96. always_assert_flog(func, "func not found for builtin {}\n", name.get());
  97. g_context->invokeFuncFew(&retVal, func, nullptr, nullptr,
  98. args.size(), &args[0]);
  99. // If we got here, we didn't throw, so we can pop the inputs.
  100. for (auto i = uint32_t{0}; i < op.arg1; ++i) popT(env);
  101. assert(cellIsPlausible(retVal));
  102. return retVal;
  103. });
  104. }
  105. //////////////////////////////////////////////////////////////////////
  106. folly::Optional<Type> const_fold(ISS& env, const bc::FCallBuiltin& op) {
  107. for (auto i = uint32_t{0}; i < op.arg1; ++i) {
  108. auto const val = tv(topT(env, i));
  109. if (!val || val->m_type == KindOfUninit) return folly::none;
  110. }
  111. #define X(x) if (op.str3->isame(x.get())) return const_fold(env, op, x);
  112. X(s_array_combine)
  113. X(s_array_fill)
  114. X(s_array_flip)
  115. X(s_array_reverse)
  116. X(s_array_search)
  117. X(s_array_slice)
  118. X(s_array_sum)
  119. X(s_array_values)
  120. X(s_base64decode)
  121. X(s_base64_encode)
  122. X(s_base_convert)
  123. X(s_bindec)
  124. X(s_ceil)
  125. X(s_chr)
  126. X(s_count)
  127. X(s_decbin)
  128. X(s_dechex)
  129. X(s_decoct)
  130. X(s_explode)
  131. X(s_floor)
  132. X(s_getrandmax)
  133. X(s_gettype)
  134. X(s_hexdec)
  135. X(s_implode)
  136. X(s_in_array)
  137. X(s_log)
  138. X(s_log10)
  139. X(s_mt_getrandmax)
  140. X(s_octdec)
  141. X(s_ord)
  142. X(s_pow)
  143. X(s_preg_quote)
  144. X(s_range)
  145. X(s_rawurldecode)
  146. X(s_rawurlencode)
  147. X(s_round)
  148. X(s_sha1)
  149. X(s_strlen)
  150. X(s_str_repeat)
  151. X(s_str_split)
  152. X(s_substr)
  153. X(s_trim)
  154. X(s_urldecode)
  155. X(s_urlencode)
  156. X(s_utf8_encode)
  157. X(s_version_compare)
  158. X(s_sqrt)
  159. X(s_abs)
  160. // Note serialize can only run user-defined code if its argument is an
  161. // object, which will never be a constant type, so this is safe.
  162. X(s_serialize)
  163. // We turn this on in case we'd be omitting a warning after constant-folding
  164. // array_fill_keys. Even if at runtime this option will be off, it's fine to
  165. // compile as if it were on: we just won't fold the calls that could've
  166. // raised this error.
  167. RuntimeOption::StrictArrayFillKeys = HackStrictOption::ON;
  168. X(s_array_fill_keys)
  169. // A few that you can't do:
  170. // These read the current locale, so they can't be constant folded:
  171. // X(s_ucfirst)
  172. // X(s_ucwords)
  173. // X(s_strtolower)
  174. // This sets the last json error code, so it has observable effects:
  175. // X(s_json_encode)
  176. #undef X
  177. return folly::none;
  178. }
  179. //////////////////////////////////////////////////////////////////////
  180. const StaticString s_get_class("get_class");
  181. bool builtin_get_class(ISS& env, const bc::FCallBuiltin& op) {
  182. if (op.arg1 != 1) return false;
  183. auto const ty = topT(env);
  184. if (!ty.subtypeOf(TObj)) return false;
  185. auto unknown_class = [&] {
  186. popT(env);
  187. push(env, TStr);
  188. return true;
  189. };
  190. if (!is_specialized_obj(ty)) return unknown_class();
  191. auto const d = dobj_of(ty);
  192. switch (d.type) {
  193. case DObj::Sub: return unknown_class();
  194. case DObj::Exact: break;
  195. }
  196. constprop(env);
  197. popT(env);
  198. push(env, sval(d.cls.name()));
  199. return true;
  200. }
  201. bool builtin_abs(ISS& env, const bc::FCallBuiltin& op) {
  202. if (op.arg1 != 1) return false;
  203. auto const ty = popC(env);
  204. push(env, ty.subtypeOf(TInt) ? TInt :
  205. ty.subtypeOf(TDbl) ? TDbl :
  206. TInitUnc);
  207. return true;
  208. }
  209. /**
  210. * if the input to these functions is known to be integer or double,
  211. * the result will be a double. Otherwise, the result is conditional
  212. * on a successful conversion and an accurate number of arguments.
  213. */
  214. bool floatIfNumeric(ISS& env, const bc::FCallBuiltin& op) {
  215. if (op.arg1 != 1) return false;
  216. auto const ty = popC(env);
  217. push(env, ty.subtypeOf(TNum) ? TDbl : TInitUnc);
  218. return true;
  219. }
  220. bool builtin_ceil(ISS& env, const bc::FCallBuiltin& op) {
  221. return floatIfNumeric(env, op);
  222. }
  223. bool builtin_floor(ISS& env, const bc::FCallBuiltin& op) {
  224. return floatIfNumeric(env, op);
  225. }
  226. bool builtin_mt_rand(ISS& env, const bc::FCallBuiltin& op) {
  227. // In PHP, the two arg version can return false on input failure, but we don't
  228. // behave the same as PHP. we allow 1-arg calls and we allow the params to
  229. // come in any order.
  230. auto success = [&] {
  231. popT(env);
  232. popT(env);
  233. push(env, TInt);
  234. return true;
  235. };
  236. switch (op.arg1) {
  237. case 0:
  238. return success();
  239. case 1:
  240. return topT(env, 0).subtypeOf(TNum) ? success() : false;
  241. case 2:
  242. if (topT(env, 0).subtypeOf(TNum) &&
  243. topT(env, 1).subtypeOf(TNum)) {
  244. return success();
  245. }
  246. break;
  247. }
  248. return false;
  249. }
  250. /**
  251. * The compiler specializes the two-arg version of min() and max()
  252. * into an HNI provided helper. If both arguments are an integer
  253. * or both arguments are a double, we know the exact type of the
  254. * return value. If they're both numeric, the result is at least
  255. * numeric.
  256. */
  257. bool minmax2(ISS& env, const bc::FCallBuiltin& op) {
  258. // this version takes exactly two arguments.
  259. if (op.arg1 != 2) return false;
  260. auto const t0 = topT(env, 0);
  261. auto const t1 = topT(env, 1);
  262. if (!t0.subtypeOf(TNum) || !t1.subtypeOf(TNum)) return false;
  263. popC(env);
  264. popC(env);
  265. push(env, t0 == t1 ? t0 : TNum);
  266. return true;
  267. }
  268. bool builtin_max2(ISS& env, const bc::FCallBuiltin& op) {
  269. return minmax2(env, op);
  270. }
  271. bool builtin_min2(ISS& env, const bc::FCallBuiltin& op) {
  272. return minmax2(env, op);
  273. }
  274. bool builtin_strlen(ISS& env, const bc::FCallBuiltin& op) {
  275. if (op.arg1 != 1) return false;
  276. auto const ty = popC(env);
  277. // Returns null and raises a warning when input is an array, resource, or
  278. // object.
  279. if (ty.subtypeOfAny(TPrim, TStr)) nothrow(env);
  280. push(env, ty.subtypeOfAny(TPrim, TStr) ? TInt : TOptInt);
  281. return true;
  282. }
  283. const StaticString
  284. s_max2("__SystemLib\\max2"),
  285. s_min2("__SystemLib\\min2");
  286. bool handle_builtin(ISS& env, const bc::FCallBuiltin& op) {
  287. #define X(x) if (op.str3->isame(s_##x.get())) return builtin_##x(env, op);
  288. X(abs)
  289. X(ceil)
  290. X(floor)
  291. X(get_class)
  292. X(max2)
  293. X(min2)
  294. X(mt_rand)
  295. X(strlen)
  296. #undef X
  297. return false;
  298. }
  299. //////////////////////////////////////////////////////////////////////
  300. }
  301. namespace interp_step {
  302. void in(ISS& env, const bc::FCallBuiltin& op) {
  303. if (options.ConstantFoldBuiltins) {
  304. if (auto const val = const_fold(env, op)) {
  305. constprop(env);
  306. return push(env, *val);
  307. }
  308. }
  309. // Try to handle the builtin at the type level.
  310. if (handle_builtin(env, op)) return;
  311. auto const name = op.str3;
  312. auto const func = env.index.resolve_func(env.ctx, name);
  313. auto const rt = env.index.lookup_return_type(env.ctx, func);
  314. for (auto i = uint32_t{0}; i < op.arg1; ++i) popT(env);
  315. specialFunctionEffects(env, name);
  316. push(env, rt);
  317. }
  318. }
  319. //////////////////////////////////////////////////////////////////////
  320. }}