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

/hphp/hhbbc/interp-builtin.cpp

https://gitlab.com/Blueprint-Marketing/hhvm
C++ | 324 lines | 229 code | 48 blank | 47 comment | 23 complexity | fe6edff90265dbb84d14f0f9b3ea1fd8 MD5 | raw file
  1. /*
  2. +----------------------------------------------------------------------+
  3. | HipHop for PHP |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 2010-2014 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(mt_rand)
  56. X(mt_getrandmax)
  57. X(octdec)
  58. X(ord)
  59. X(pow)
  60. X(preg_quote)
  61. X(range)
  62. X(rawurldecode)
  63. X(rawurlencode)
  64. X(round)
  65. X(serialize)
  66. X(sha1)
  67. X(str_repeat)
  68. X(str_split)
  69. X(substr)
  70. X(trim)
  71. X(urldecode)
  72. X(urlencode)
  73. X(utf8_encode)
  74. X(version_compare)
  75. X(sqrt)
  76. X(abs)
  77. #undef X
  78. //////////////////////////////////////////////////////////////////////
  79. folly::Optional<Type> const_fold(ISS& env,
  80. const bc::FCallBuiltin& op,
  81. const StaticString& name) {
  82. FTRACE(1, "invoking: {}\n", name.get()->data());
  83. assert(!RuntimeOption::EvalJit);
  84. return eval_cell([&] {
  85. // Don't pop the args yet---if the builtin throws at compile time (because
  86. // it would raise a warning or something at runtime) we're going to leave
  87. // the call alone.
  88. std::vector<Cell> args(op.arg1);
  89. for (auto i = uint32_t{0}; i < op.arg1; ++i) {
  90. args[op.arg1 - i - 1] = *tv(topT(env, i));
  91. }
  92. Cell retVal;
  93. auto const func = Unit::lookupFunc(name.get());
  94. always_assert_flog(func, "func not found for builtin {}\n", name.get());
  95. g_context->invokeFuncFew(&retVal, func, nullptr, nullptr,
  96. args.size(), &args[0]);
  97. // If we got here, we didn't throw, so we can pop the inputs.
  98. for (auto i = uint32_t{0}; i < op.arg1; ++i) popT(env);
  99. assert(cellIsPlausible(retVal));
  100. return retVal;
  101. });
  102. }
  103. //////////////////////////////////////////////////////////////////////
  104. folly::Optional<Type> const_fold(ISS& env, const bc::FCallBuiltin& op) {
  105. for (auto i = uint32_t{0}; i < op.arg1; ++i) {
  106. auto const val = tv(topT(env, i));
  107. if (!val || val->m_type == KindOfUninit) return folly::none;
  108. }
  109. #define X(x) if (op.str3->isame(x.get())) return const_fold(env, op, x);
  110. X(s_array_combine)
  111. X(s_array_fill)
  112. X(s_array_flip)
  113. X(s_array_reverse)
  114. X(s_array_search)
  115. X(s_array_slice)
  116. X(s_array_sum)
  117. X(s_array_values)
  118. X(s_base64decode)
  119. X(s_base64_encode)
  120. X(s_base_convert)
  121. X(s_bindec)
  122. X(s_ceil)
  123. X(s_chr)
  124. X(s_count)
  125. X(s_decbin)
  126. X(s_dechex)
  127. X(s_decoct)
  128. X(s_explode)
  129. X(s_floor)
  130. X(s_getrandmax)
  131. X(s_gettype)
  132. X(s_hexdec)
  133. X(s_implode)
  134. X(s_in_array)
  135. X(s_log)
  136. X(s_log10)
  137. X(s_mt_getrandmax)
  138. X(s_octdec)
  139. X(s_ord)
  140. X(s_pow)
  141. X(s_preg_quote)
  142. X(s_range)
  143. X(s_rawurldecode)
  144. X(s_rawurlencode)
  145. X(s_round)
  146. X(s_sha1)
  147. X(s_str_repeat)
  148. X(s_str_split)
  149. X(s_substr)
  150. X(s_trim)
  151. X(s_urldecode)
  152. X(s_urlencode)
  153. X(s_utf8_encode)
  154. X(s_version_compare)
  155. X(s_sqrt)
  156. X(s_abs)
  157. // Note serialize can only run user-defined code if its argument is an
  158. // object, which will never be a constant type, so this is safe.
  159. X(s_serialize)
  160. // We turn this on in case we'd be omitting a warning after constant-folding
  161. // array_fill_keys. Even if at runtime this option will be off, it's fine to
  162. // compile as if it were on: we just won't fold the calls that could've
  163. // raised this error.
  164. RuntimeOption::StrictArrayFillKeys = HackStrictOption::ON;
  165. X(s_array_fill_keys)
  166. // A few that you can't do:
  167. // These read the current locale, so they can't be constant folded:
  168. // X(s_ucfirst)
  169. // X(s_ucwords)
  170. // X(s_strtolower)
  171. // This sets the last json error code, so it has observable effects:
  172. // X(s_json_encode)
  173. #undef X
  174. return folly::none;
  175. }
  176. //////////////////////////////////////////////////////////////////////
  177. const StaticString s_get_class("get_class");
  178. bool builtin_get_class(ISS& env, const bc::FCallBuiltin& op) {
  179. if (op.arg1 != 1) return false;
  180. auto const ty = topT(env);
  181. if (!ty.subtypeOf(TObj)) return false;
  182. auto unknown_class = [&] {
  183. popT(env);
  184. push(env, TStr);
  185. return true;
  186. };
  187. if (!is_specialized_obj(ty)) return unknown_class();
  188. auto const d = dobj_of(ty);
  189. switch (d.type) {
  190. case DObj::Sub: return unknown_class();
  191. case DObj::Exact: break;
  192. }
  193. constprop(env);
  194. popT(env);
  195. push(env, sval(d.cls.name()));
  196. return true;
  197. }
  198. bool builtin_abs(ISS& env, const bc::FCallBuiltin& op) {
  199. if (op.arg1 != 1) return false;
  200. auto const ty = popC(env);
  201. push(env, ty.subtypeOf(TInt) ? TInt :
  202. ty.subtypeOf(TDbl) ? TDbl :
  203. TInitUnc);
  204. return true;
  205. }
  206. /**
  207. * if the input to these functions is known to be integer or double,
  208. * the result will be a double. Otherwise, the result is conditional
  209. * on a successful conversion and an accurate number of arguments.
  210. */
  211. bool floatIfNumeric(ISS& env, const bc::FCallBuiltin& op) {
  212. if (op.arg1 != 1) return false;
  213. auto const ty = popC(env);
  214. push(env, ty.subtypeOf(TNum) ? TDbl : TInitUnc);
  215. return true;
  216. }
  217. bool builtin_ceil(ISS& env, const bc::FCallBuiltin& op) {
  218. return floatIfNumeric(env, op);
  219. }
  220. bool builtin_floor(ISS& env, const bc::FCallBuiltin& op) {
  221. return floatIfNumeric(env, op);
  222. }
  223. bool builtin_mt_rand(ISS& env, const bc::FCallBuiltin& op) {
  224. // In PHP, the two arg version can return false on input failure, but we don't
  225. // behave the same as PHP. we allow 1-arg calls and we allow the params to
  226. // come in any order.
  227. auto success = [&] {
  228. popT(env);
  229. popT(env);
  230. push(env, TInt);
  231. return true;
  232. };
  233. switch (op.arg1) {
  234. case 0:
  235. return success();
  236. case 1:
  237. return topT(env, 0).subtypeOf(TNum) ? success() : false;
  238. case 2:
  239. if (topT(env, 0).subtypeOf(TNum) &&
  240. topT(env, 1).subtypeOf(TNum)) {
  241. return success();
  242. }
  243. break;
  244. }
  245. return false;
  246. }
  247. bool handle_builtin(ISS& env, const bc::FCallBuiltin& op) {
  248. #define X(x) if (op.str3->isame(s_##x.get())) return builtin_##x(env, op);
  249. X(abs)
  250. X(ceil)
  251. X(floor)
  252. X(get_class)
  253. X(mt_rand)
  254. #undef X
  255. return false;
  256. }
  257. //////////////////////////////////////////////////////////////////////
  258. }
  259. void builtin(ISS& env, const bc::FCallBuiltin& op) {
  260. if (options.ConstantFoldBuiltins) {
  261. if (auto const val = const_fold(env, op)) {
  262. constprop(env);
  263. return push(env, *val);
  264. }
  265. }
  266. // Try to handle the builtin at the type level.
  267. if (handle_builtin(env, op)) return;
  268. auto const name = op.str3;
  269. auto const func = env.index.resolve_func(env.ctx, name);
  270. auto const rt = env.index.lookup_return_type(env.ctx, func);
  271. for (auto i = uint32_t{0}; i < op.arg1; ++i) popT(env);
  272. specialFunctionEffects(env, name);
  273. push(env, rt);
  274. }
  275. //////////////////////////////////////////////////////////////////////
  276. }}