Codemod asserts to assertxs in the runtime
[hiphop-php.git] / hphp / runtime / ext / hh / ext_hh.cpp
blob3d8b736592c45e8058e3fb88e3e1e59cd0a09edf
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 | Copyright (c) 1997-2010 The PHP Group |
7 +----------------------------------------------------------------------+
8 | This source file is subject to version 3.01 of the PHP license, |
9 | that is bundled with this package in the file LICENSE, and is |
10 | available through the world-wide-web at the following url: |
11 | http://www.php.net/license/3_01.txt |
12 | If you did not receive a copy of the PHP license and are unable to |
13 | obtain it through the world-wide-web, please send a note to |
14 | license@php.net so we can mail you a copy immediately. |
15 +----------------------------------------------------------------------+
18 #include "hphp/runtime/ext/hh/ext_hh.h"
20 #include <sys/types.h>
21 #include <sys/stat.h>
23 #include "hphp/runtime/base/autoload-handler.h"
24 #include "hphp/runtime/base/container-functions.h"
25 #include "hphp/runtime/base/execution-context.h"
26 #include "hphp/runtime/base/unit-cache.h"
27 #include "hphp/runtime/ext/fb/ext_fb.h"
28 #include "hphp/runtime/ext/collections/ext_collections-pair.h"
29 #include "hphp/runtime/vm/runtime.h"
30 #include "hphp/runtime/vm/vm-regs.h"
32 namespace HPHP {
34 //////////////////////////////////////////////////////////////////////
36 const StaticString
37 s_86metadata("86metadata"),
38 // The following are used in serialize_memoize_tv to serialize objects that
39 // implement the IMemoizeParam interface
40 s_IMemoizeParam("HH\\IMemoizeParam"),
41 s_getInstanceKey("getInstanceKey"),
42 // The following are used in serialize_memoize_param(); they are what
43 // serialize_memoize_tv would have produced on some common inputs; having
44 // these lets us avoid creating a StringBuffer
45 s_nullMemoKey("\xf0"),
46 s_trueMemoKey("\xf1"),
47 s_falseMemoKey("\xf2");
49 ///////////////////////////////////////////////////////////////////////////////
50 bool HHVM_FUNCTION(autoload_set_paths,
51 const Variant& map,
52 const String& root) {
53 if (map.isArray()) {
54 return AutoloadHandler::s_instance->setMap(map.toCArrRef(), root);
56 if (!(map.isObject() && map.toObject()->isCollection())) {
57 return false;
59 // Assume we have Map<string, Map<string, string>> - convert to
60 // array<string, array<string, string>>
62 // Exception for 'failure' which should be a callable.
63 auto as_array = map.toArray();
64 for (auto it = as_array.begin(); !it.end(); it.next()) {
65 if (it.second().isObject() && it.second().toObject()->isCollection()) {
66 as_array.set(it.first(), it.second().toArray());
69 return AutoloadHandler::s_instance->setMap(as_array, root);
72 bool HHVM_FUNCTION(could_include, const String& file) {
73 return lookupUnit(file.get(), "", nullptr /* initial_opt */) != nullptr;
76 namespace {
78 /**
79 * Param serialization for memoization.
81 * This code is based on fb_compact_serialize; it was forked because
82 * fb_compact_serialize would sometimes ignore insertion order in arrays;
83 * e.g. [1 => 1, 2 => 2] would serialize the same as [2 => 2, 1 => 1], which
84 * is not acceptable for the memoization use case. The forked code is not
85 * compatible with fb_serialize or fb_compact_serialize. The values returned
86 * are not intended for deserialization or for shipping across the network,
87 * only for use as dict keys; this means it is possible to change it without
88 * notice, and it is also why it is safe to do everything in host byte-order.
90 * === Format ===
92 * Integer values are returned as-is. Strings where the first byte is < 0xf0
93 * are returned as-is. All other values are converted to strings of the form
94 * <c> <data> where c is a byte (0xf0 | code), and data is a sequence of 0 or
95 * more bytes. The codes are:
97 * 0 (NULL): KindOfUninit or KindOfNull; data has length 0
98 * 1 (TRUE),
99 * 2 (FALSE): KindOfBoolean; data has length 0
100 * 3 (INT8),
101 * 4 (INT16),
102 * 5 (INT32),
103 * 6 (INT64): KindOfInt64; data has length 1, 2, 4 or 8 respectively, and is
104 * a signed value in host byte order
105 * 7 (DOUBLE): KindOfDouble; data has length 8, is in host byte order
106 * 8 (STRING): string; data is a int serialized as above, followed by that
107 * many bytes of string data
108 * 9 (OBJECT): KindOfObject that extends IMemoizeParam; data is an int
109 * serialized as above, followed by that many bytes of serialized
110 * object data (obtained by calling getInstanceKey on the object)
111 * 10 (CONTAINER): any PHP array, collection, or hack array; data is the
112 * keys and values of the container in insertion order,
113 * serialized as above, followed by the STOP code
114 * 11 (STOP): terminates a CONTAINER encoding
117 enum SerializeMemoizeCode {
118 SER_MC_NULL = 0,
119 SER_MC_TRUE = 1,
120 SER_MC_FALSE = 2,
121 SER_MC_INT8 = 3,
122 SER_MC_INT16 = 4,
123 SER_MC_INT32 = 5,
124 SER_MC_INT64 = 6,
125 SER_MC_DOUBLE = 7,
126 SER_MC_STRING = 8,
127 SER_MC_OBJECT = 9,
128 SER_MC_CONTAINER = 10,
129 SER_MC_STOP = 11,
132 const uint64_t kCodeMask DEBUG_ONLY = 0x0f;
133 const uint64_t kCodePrefix = 0xf0;
135 ALWAYS_INLINE void serialize_memoize_code(StringBuffer& sb,
136 SerializeMemoizeCode code) {
137 assertx(code == (code & kCodeMask));
138 uint8_t v = (kCodePrefix | code);
139 sb.append(reinterpret_cast<char*>(&v), 1);
142 ALWAYS_INLINE void serialize_memoize_int64(StringBuffer& sb, int64_t val) {
143 if (val == (int8_t)val) {
144 serialize_memoize_code(sb, SER_MC_INT8);
145 uint8_t nval = val;
146 sb.append(reinterpret_cast<char*>(&nval), 1);
147 } else if (val == (int16_t)val) {
148 serialize_memoize_code(sb, SER_MC_INT16);
149 uint16_t nval = val;
150 sb.append(reinterpret_cast<char*>(&nval), 2);
151 } else if (val == (int32_t)val) {
152 serialize_memoize_code(sb, SER_MC_INT32);
153 uint32_t nval = val;
154 sb.append(reinterpret_cast<char*>(&nval), 4);
155 } else {
156 serialize_memoize_code(sb, SER_MC_INT64);
157 uint64_t nval = val;
158 sb.append(reinterpret_cast<char*>(&nval), 8);
162 ALWAYS_INLINE void serialize_memoize_string_data(StringBuffer& sb,
163 const StringData* str) {
164 int len = str->size();
165 serialize_memoize_int64(sb, len);
166 sb.append(str->data(), len);
169 void serialize_memoize_tv(StringBuffer& sb, int depth, TypedValue tv);
171 void serialize_memoize_tv(StringBuffer& sb, int depth, const TypedValue *tv) {
172 serialize_memoize_tv(sb, depth, *tv);
175 void serialize_memoize_array(StringBuffer& sb, int depth, const ArrayData* ad) {
176 serialize_memoize_code(sb, SER_MC_CONTAINER);
177 IterateKV(ad, [&] (Cell k, TypedValue v) {
178 serialize_memoize_tv(sb, depth, k);
179 serialize_memoize_tv(sb, depth, v);
180 return false;
182 serialize_memoize_code(sb, SER_MC_STOP);
185 void serialize_memoize_obj(StringBuffer& sb, int depth, ObjectData* obj) {
186 if (obj->isCollection()) {
187 const ArrayData* ad = collections::asArray(obj);
188 if (ad) {
189 serialize_memoize_array(sb, depth, ad);
190 } else {
191 assertx(obj->collectionType() == CollectionType::Pair);
193 auto const pair = reinterpret_cast<c_Pair*>(obj);
194 serialize_memoize_code(sb, SER_MC_CONTAINER);
195 serialize_memoize_int64(sb, 0);
196 serialize_memoize_tv(sb, depth, pair->get(0));
197 serialize_memoize_int64(sb, 1);
198 serialize_memoize_tv(sb, depth, pair->get(1));
199 serialize_memoize_code(sb, SER_MC_STOP);
201 } else if (obj->instanceof(s_IMemoizeParam)) {
202 Variant ser = obj->o_invoke_few_args(s_getInstanceKey, 0);
203 serialize_memoize_code(sb, SER_MC_OBJECT);
204 serialize_memoize_string_data(sb, ser.toString().get());
205 } else {
206 auto msg = folly::format(
207 "Cannot serialize object of type {} because it does not implement "
208 "HH\\IMemoizeParam",
209 obj->getClassName().asString()
210 ).str();
211 SystemLib::throwInvalidArgumentExceptionObject(msg);
215 void serialize_memoize_tv(StringBuffer& sb, int depth, TypedValue tv) {
216 if (depth > 256) {
217 SystemLib::throwInvalidArgumentExceptionObject("Array depth exceeded");
219 depth++;
221 switch (tv.m_type) {
222 case KindOfUninit:
223 case KindOfNull:
224 serialize_memoize_code(sb, SER_MC_NULL);
225 break;
227 case KindOfBoolean:
228 serialize_memoize_code(sb, tv.m_data.num ? SER_MC_TRUE : SER_MC_FALSE);
229 break;
231 case KindOfInt64:
232 serialize_memoize_int64(sb, tv.m_data.num);
233 break;
235 case KindOfDouble:
236 serialize_memoize_code(sb, SER_MC_DOUBLE);
237 sb.append(reinterpret_cast<const char*>(&tv.m_data.dbl), 8);
238 break;
240 case KindOfPersistentString:
241 case KindOfString:
242 serialize_memoize_code(sb, SER_MC_STRING);
243 serialize_memoize_string_data(sb, tv.m_data.pstr);
244 break;
246 case KindOfPersistentVec:
247 case KindOfVec:
248 case KindOfPersistentDict:
249 case KindOfDict:
250 case KindOfPersistentKeyset:
251 case KindOfKeyset:
252 case KindOfPersistentArray:
253 case KindOfArray:
254 serialize_memoize_array(sb, depth, tv.m_data.parr);
255 break;
257 case KindOfObject:
258 serialize_memoize_obj(sb, depth, tv.m_data.pobj);
259 break;
261 case KindOfResource:
262 case KindOfRef: {
263 auto msg = folly::format(
264 "Cannot Serialize unexpected type {}",
265 tname(tv.m_type)
266 ).str();
267 SystemLib::throwInvalidArgumentExceptionObject(msg);
268 break;
273 } // end anonymous namespace
275 TypedValue HHVM_FUNCTION(serialize_memoize_param, TypedValue param) {
276 // Memoize throws in the emitter if any function parameters are references, so
277 // we can just assert that the param is cell here
278 assertx(param.m_type != KindOfRef);
279 auto const type = param.m_type;
281 if (type == KindOfInt64) {
282 return param;
283 } else if (isStringType(type)) {
284 auto const str = param.m_data.pstr;
285 if (str->empty()) {
286 return make_tv<KindOfPersistentString>(staticEmptyString());
287 } else if ((unsigned char)str->data()[0] < 0xf0) {
288 // serialize_memoize_tv always returns a string with the first character
289 // >= 0xf0, so anything less than that can't collide. There's no worry
290 // about int-like strings because the key returned from this function is
291 // used in dicts (which don't perform key coercion).
292 str->incRefCount();
293 return param;
295 } else if (type == KindOfUninit || type == KindOfNull) {
296 return make_tv<KindOfPersistentString>(s_nullMemoKey.get());
297 } else if (type == KindOfBoolean) {
298 return make_tv<KindOfPersistentString>(
299 param.m_data.num ? s_trueMemoKey.get() : s_falseMemoKey.get()
303 StringBuffer sb;
304 serialize_memoize_tv(sb, 0, &param);
305 return tvReturn(sb.detach());
308 void HHVM_FUNCTION(set_frame_metadata, const Variant& metadata) {
309 VMRegAnchor _;
310 auto fp = vmfp();
311 if (UNLIKELY(!fp)) return;
312 if (fp->skipFrame()) fp = g_context->getPrevVMStateSkipFrame(fp);
313 if (UNLIKELY(!fp)) return;
315 if (LIKELY(!(fp->func()->attrs() & AttrMayUseVV)) ||
316 LIKELY(!fp->hasVarEnv())) {
317 auto const local = fp->func()->lookupVarId(s_86metadata.get());
318 if (LIKELY(local != kInvalidId)) {
319 cellSet(*metadata.asCell(), *tvAssertCell(frame_local(fp, local)));
320 } else {
321 SystemLib::throwInvalidArgumentExceptionObject(
322 "Unsupported dynamic call of set_frame_metadata()");
324 } else {
325 fp->getVarEnv()->set(s_86metadata.get(), metadata.asTypedValue());
329 static struct HHExtension final : Extension {
330 HHExtension(): Extension("hh", NO_EXTENSION_VERSION_YET) { }
331 void moduleInit() override {
332 HHVM_NAMED_FE(HH\\autoload_set_paths, HHVM_FN(autoload_set_paths));
333 HHVM_NAMED_FE(HH\\could_include, HHVM_FN(could_include));
334 HHVM_NAMED_FE(HH\\serialize_memoize_param,
335 HHVM_FN(serialize_memoize_param));
336 HHVM_NAMED_FE(HH\\set_frame_metadata, HHVM_FN(set_frame_metadata));
337 loadSystemlib();
339 } s_hh_extension;
341 static struct XHPExtension final : Extension {
342 XHPExtension(): Extension("xhp", NO_EXTENSION_VERSION_YET) { }
343 bool moduleEnabled() const override { return RuntimeOption::EnableXHP; }
344 } s_xhp_extension;
346 ///////////////////////////////////////////////////////////////////////////////