2 +----------------------------------------------------------------------+
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>
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"
34 //////////////////////////////////////////////////////////////////////
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
,
54 return AutoloadHandler::s_instance
->setMap(map
.toCArrRef(), root
);
56 if (!(map
.isObject() && map
.toObject()->isCollection())) {
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;
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.
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
99 * 2 (FALSE): KindOfBoolean; data has length 0
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
{
128 SER_MC_CONTAINER
= 10,
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
);
146 sb
.append(reinterpret_cast<char*>(&nval
), 1);
147 } else if (val
== (int16_t)val
) {
148 serialize_memoize_code(sb
, SER_MC_INT16
);
150 sb
.append(reinterpret_cast<char*>(&nval
), 2);
151 } else if (val
== (int32_t)val
) {
152 serialize_memoize_code(sb
, SER_MC_INT32
);
154 sb
.append(reinterpret_cast<char*>(&nval
), 4);
156 serialize_memoize_code(sb
, SER_MC_INT64
);
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
);
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
);
189 serialize_memoize_array(sb
, depth
, ad
);
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());
206 auto msg
= folly::format(
207 "Cannot serialize object of type {} because it does not implement "
209 obj
->getClassName().asString()
211 SystemLib::throwInvalidArgumentExceptionObject(msg
);
215 void serialize_memoize_tv(StringBuffer
& sb
, int depth
, TypedValue tv
) {
217 SystemLib::throwInvalidArgumentExceptionObject("Array depth exceeded");
224 serialize_memoize_code(sb
, SER_MC_NULL
);
228 serialize_memoize_code(sb
, tv
.m_data
.num
? SER_MC_TRUE
: SER_MC_FALSE
);
232 serialize_memoize_int64(sb
, tv
.m_data
.num
);
236 serialize_memoize_code(sb
, SER_MC_DOUBLE
);
237 sb
.append(reinterpret_cast<const char*>(&tv
.m_data
.dbl
), 8);
240 case KindOfPersistentString
:
242 serialize_memoize_code(sb
, SER_MC_STRING
);
243 serialize_memoize_string_data(sb
, tv
.m_data
.pstr
);
246 case KindOfPersistentVec
:
248 case KindOfPersistentDict
:
250 case KindOfPersistentKeyset
:
252 case KindOfPersistentArray
:
254 serialize_memoize_array(sb
, depth
, tv
.m_data
.parr
);
258 serialize_memoize_obj(sb
, depth
, tv
.m_data
.pobj
);
263 auto msg
= folly::format(
264 "Cannot Serialize unexpected type {}",
267 SystemLib::throwInvalidArgumentExceptionObject(msg
);
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
) {
283 } else if (isStringType(type
)) {
284 auto const str
= param
.m_data
.pstr
;
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).
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()
304 serialize_memoize_tv(sb
, 0, ¶m
);
305 return tvReturn(sb
.detach());
308 void HHVM_FUNCTION(set_frame_metadata
, const Variant
& metadata
) {
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
)));
321 SystemLib::throwInvalidArgumentExceptionObject(
322 "Unsupported dynamic call of set_frame_metadata()");
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
));
341 static struct XHPExtension final
: Extension
{
342 XHPExtension(): Extension("xhp", NO_EXTENSION_VERSION_YET
) { }
343 bool moduleEnabled() const override
{ return RuntimeOption::EnableXHP
; }
346 ///////////////////////////////////////////////////////////////////////////////