2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2013 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 +----------------------------------------------------------------------+
16 #include "hphp/runtime/vm/runtime.h"
17 #include "hphp/runtime/base/execution_context.h"
18 #include "hphp/runtime/base/complex_types.h"
19 #include "hphp/runtime/base/zend/zend_string.h"
20 #include "hphp/runtime/base/array/hphp_array.h"
21 #include "hphp/runtime/base/builtin_functions.h"
22 #include "hphp/runtime/ext/ext_closure.h"
23 #include "hphp/runtime/ext/ext_continuation.h"
24 #include "hphp/runtime/ext/ext_collections.h"
25 #include "hphp/runtime/vm/core_types.h"
26 #include "hphp/runtime/vm/bytecode.h"
27 #include "hphp/runtime/vm/repo.h"
28 #include "hphp/util/trace.h"
29 #include "hphp/runtime/vm/jit/translator-inline.h"
30 #include "hphp/runtime/vm/jit/translator-x64.h"
32 #include "hphp/runtime/base/zend/zend_functions.h"
33 #include "hphp/runtime/ext/ext_string.h"
39 static const Trace::Module TRACEMOD
= Trace::runtime
;
41 CompileStringFn g_hphp_compiler_parse
;
42 BuildNativeFuncUnitFn g_hphp_build_native_func_unit
;
43 BuildNativeClassUnitFn g_hphp_build_native_class_unit
;
46 * print_string will decRef the string
48 void print_string(StringData
* s
) {
49 g_context
->write(s
->data(), s
->size());
50 TRACE(1, "t-x64 output(str): (%p) %43s\n", s
->data(),
51 Util::escapeStringForCPP(s
->data(), s
->size()).data());
55 void print_int(int64_t i
) {
57 snprintf(buf
, 256, "%" PRId64
, i
);
59 TRACE(1, "t-x64 output(int): %" PRId64
"\n", i
);
62 void print_boolean(bool val
) {
69 ArrayData
* new_array(int capacity
) {
70 ArrayData
*a
= ArrayData::Make(capacity
);
72 TRACE(2, "newArrayHelper: capacity %d\n", capacity
);
76 ArrayData
* new_tuple(int n
, const TypedValue
* values
) {
77 auto a
= ArrayData::Make(n
, values
);
79 TRACE(2, "new_tuple: size %d\n", n
);
83 #define NEW_COLLECTION_HELPER(name) \
85 new##name##Helper(int nElms) { \
86 ObjectData *obj = NEWOBJ(c_##name)(); \
89 collectionReserve(obj, nElms); \
91 TRACE(2, "new" #name "Helper: capacity %d\n", nElms); \
95 NEW_COLLECTION_HELPER(Vector
)
96 NEW_COLLECTION_HELPER(Map
)
97 NEW_COLLECTION_HELPER(StableMap
)
98 NEW_COLLECTION_HELPER(Set
)
100 ObjectData
* newPairHelper() {
101 ObjectData
*obj
= NEWOBJ(c_Pair
)();
103 TRACE(2, "newPairHelper: capacity 2\n");
107 #undef NEW_COLLECTION_HELPER
110 tvPairToCString(DataType t
, uint64_t v
,
114 if (IS_STRING_TYPE(t
)) {
115 StringData
*strd
= (StringData
*)v
;
116 *outStr
= strd
->data();
117 *outSz
= strd
->size();
118 *outMustFree
= false;
124 String s
= tvAsVariant(&c
).toString();
125 *outStr
= (const char*)malloc(s
.size());
126 TRACE(1, "t-x64: stringified: %s -> %s\n", s
.data(), *outStr
);
127 memcpy((char*)*outStr
, s
.data(), s
.size());
133 * concat_ss will decRef the values passed in as appropriate, and it will
134 * incRef the output string
137 concat_ss(StringData
* v1
, StringData
* v2
) {
138 if (v1
->getCount() > 1) {
139 StringData
* ret
= NEW(StringData
)(v1
, v2
);
142 // Because v1->getCount() is greater than 1, we know we will never
143 // have to release the string here
147 v1
->append(v2
->slice());
154 * concat_is will decRef the string passed in as appropriate, and it will
155 * incRef the output string
158 concat_is(int64_t v1
, StringData
* v2
) {
162 // Convert the int to a string
165 intstart
= conv_10(v1
, &is_negative
, intbuf
+ sizeof(intbuf
), &len1
);
167 StringSlice
s1(intstart
, len1
);
168 StringSlice s2
= v2
->slice();
169 StringData
* ret
= NEW(StringData
)(s1
, s2
);
176 * concat_si will decRef the string passed in as appropriate, and it will
177 * incRef the output string
180 concat_si(StringData
* v1
, int64_t v2
) {
184 // Convert the int to a string
187 intstart
= conv_10(v2
, &is_negative
, intbuf
+ sizeof(intbuf
), &len2
);
189 StringSlice s1
= v1
->slice();
190 StringSlice
s2(intstart
, len2
);
191 StringData
* ret
= NEW(StringData
)(s1
, s2
);
198 * concat will decRef the values passed in as appropriate, and it will
199 * incRef the output string
202 concat_tv(DataType t1
, uint64_t v1
, DataType t2
, uint64_t v2
) {
206 tvPairToCString(t1
, v1
, &s1
, &s1len
, &free1
);
207 tvPairToCString(t2
, v2
, &s2
, &s2len
, &free2
);
208 StringSlice
r1(s1
, s1len
);
209 StringSlice
r2(s2
, s2len
);
210 StringData
* retval
= NEW(StringData
)(r1
, r2
);
211 retval
->incRefCount();
212 // If tvPairToCString allocated temporary buffers, free them now
213 if (free1
) free((void*)s1
);
214 if (free2
) free((void*)s2
);
215 // decRef the parameters as appropriate
216 tvRefcountedDecRefHelper(t2
, v2
);
217 tvRefcountedDecRefHelper(t1
, v1
);
222 int64_t eq_null_str(StringData
* v1
) {
223 int64_t retval
= v1
->empty();
228 int64_t eq_bool_str(int64_t v1
, StringData
* v2
) {
229 // The truth table for v2->toBoolean() ? v1 : !v1
235 // which is nothing but nxor.
236 int64_t v2i
= int64_t(v2
->toBoolean());
237 assert(v2i
== 0ll || v2i
== 1ll);
238 assert(v1
== 0ll || v1
== 1ll);
239 int64_t retval
= (v2i
^ v1
) ^ 1;
240 assert(retval
== 0ll || retval
== 1ll);
245 int64_t eq_int_str(int64_t v1
, StringData
* v2
) {
246 int64_t lval
; double dval
;
247 DataType ret
= is_numeric_string(v2
->data(), v2
->size(), &lval
, &dval
, 1);
249 if (ret
== KindOfInt64
) {
251 } else if (ret
== KindOfDouble
) {
252 return (double)v1
== dval
;
258 int64_t eq_str_str(StringData
* v1
, StringData
* v2
) {
259 int64_t retval
= v1
->equal(v2
);
265 int64_t same_str_str(StringData
* v1
, StringData
* v2
) {
266 int64_t retval
= v1
== v2
|| v1
->same(v2
);
272 int64_t str0_to_bool(StringData
* sd
) {
273 int64_t retval
= sd
->toBoolean();
277 int64_t str_to_bool(StringData
* sd
) {
278 int64_t retval
= str0_to_bool(sd
);
283 int64_t arr0_to_bool(ArrayData
* ad
) {
284 return ad
->size() != 0;
287 int64_t arr_to_bool(ArrayData
* ad
) {
288 assert(tx64
->stateIsDirty());
289 int64_t retval
= arr0_to_bool(ad
);
295 * tv_to_bool will decrement tv's refcount if tv is a refcounted type
298 tv_to_bool(TypedValue
* tv
) {
301 if (IS_STRING_TYPE(tv
->m_type
)) {
302 StringData
* sd
= tv
->m_data
.pstr
;
303 retval
= bool(str0_to_bool(sd
));
304 } else if (tv
->m_type
== KindOfArray
) {
305 ArrayData
* ad
= tv
->m_data
.parr
;
306 retval
= bool(arr0_to_bool(ad
));
308 retval
= bool(tvAsCVarRef(tv
));
310 TRACE(2, Trace::prettyNode("TvToBool", *tv
) + string(" -> ") +
311 string(retval
? "t" : "f") + string("\n"));
312 tvRefcountedDecRef(tv
);
313 return int64_t(retval
);
316 Unit
* compile_file(const char* s
, size_t sz
, const MD5
& md5
,
318 return g_hphp_compiler_parse(s
, sz
, md5
, fname
);
321 Unit
* build_native_func_unit(const HhbcExtFuncInfo
* builtinFuncs
,
322 ssize_t numBuiltinFuncs
) {
323 return g_hphp_build_native_func_unit(builtinFuncs
, numBuiltinFuncs
);
326 Unit
* build_native_class_unit(const HhbcExtClassInfo
* builtinClasses
,
327 ssize_t numBuiltinClasses
) {
328 return g_hphp_build_native_class_unit(builtinClasses
, numBuiltinClasses
);
331 Unit
* compile_string(const char* s
, size_t sz
, const char* fname
) {
334 md5
= MD5(string_md5(s
, sz
, false, out_len
));
336 Unit
* u
= Repo::get().loadUnit("", md5
);
340 return g_hphp_compiler_parse(s
, sz
, md5
, fname
);
343 // Returned array has refcount zero! Caller must refcount.
344 HphpArray
* pack_args_into_array(ActRec
* ar
, int nargs
) {
345 HphpArray
* argArray
= ArrayData::Make(nargs
);
346 for (int i
= 0; i
< nargs
; ++i
) {
347 TypedValue
* tv
= (TypedValue
*)(ar
) - (i
+1);
348 argArray
->HphpArray::appendWithRef(tvAsCVarRef(tv
), false);
350 if (!ar
->hasInvName()) {
351 // If this is not a magic call, we're done
354 // This is a magic call, so we need to shuffle the args
355 HphpArray
* magicArgs
= ArrayData::Make(2);
356 magicArgs
->append(ar
->getInvName(), false);
357 magicArgs
->append(argArray
, false);
361 HphpArray
* get_static_locals(const ActRec
* ar
) {
362 if (ar
->m_func
->isClosureBody()) {
363 TypedValue
* closureLoc
= frame_local(ar
, ar
->m_func
->numParams());
364 c_Closure
* closure
= static_cast<c_Closure
*>(closureLoc
->m_data
.pobj
);
365 assert(closure
!= nullptr);
366 return closure
->getStaticLocals();
367 } else if (ar
->m_func
->isGeneratorFromClosure()) {
368 TypedValue
* contLoc
= frame_local(ar
, 0);
369 c_Continuation
* cont
= static_cast<c_Continuation
*>(contLoc
->m_data
.pobj
);
370 assert(cont
!= nullptr);
371 return cont
->getStaticLocals();
373 return ar
->m_func
->getStaticLocals();
377 void collection_setm_wk1_v0(ObjectData
* obj
, TypedValue
* value
) {
379 collectionAppend(obj
, value
);
380 // TODO Task #1970153: It would be great if we had a version of
381 // collectionAppend() that didn't incRef the value so that we
382 // wouldn't have to decRef it here
383 tvRefcountedDecRef(value
);
386 void collection_setm_ik1_v0(ObjectData
* obj
, int64_t key
, TypedValue
* value
) {
388 switch (obj
->getCollectionType()) {
389 case Collection::VectorType
: {
390 c_Vector
* vec
= static_cast<c_Vector
*>(obj
);
391 vec
->set(key
, value
);
394 case Collection::MapType
: {
395 c_Map
* mp
= static_cast<c_Map
*>(obj
);
399 case Collection::StableMapType
: {
400 c_StableMap
* smp
= static_cast<c_StableMap
*>(obj
);
401 smp
->set(key
, value
);
404 case Collection::SetType
: {
405 Object
e(SystemLib::AllocRuntimeExceptionObject(
406 "Set does not support $c[$k] syntax"));
409 case Collection::PairType
: {
410 Object
e(SystemLib::AllocRuntimeExceptionObject(
411 "Cannot assign to an element of a Pair"));
417 tvRefcountedDecRef(value
);
420 void collection_setm_sk1_v0(ObjectData
* obj
, StringData
* key
,
422 switch (obj
->getCollectionType()) {
423 case Collection::VectorType
: {
424 Object
e(SystemLib::AllocInvalidArgumentExceptionObject(
425 "Only integer keys may be used with Vectors"));
428 case Collection::MapType
: {
429 c_Map
* mp
= static_cast<c_Map
*>(obj
);
433 case Collection::StableMapType
: {
434 c_StableMap
* smp
= static_cast<c_StableMap
*>(obj
);
435 smp
->set(key
, value
);
438 case Collection::SetType
: {
439 Object
e(SystemLib::AllocRuntimeExceptionObject(
440 "Set does not support $c[$k] syntax"));
443 case Collection::PairType
: {
444 Object
e(SystemLib::AllocRuntimeExceptionObject(
445 "Cannot assign to an element of a Pair"));
451 tvRefcountedDecRef(value
);
454 bool checkTv(const TypedValue
* tv
) {
455 return tv
&& tvIsPlausible(tv
) &&
456 (!IS_REFCOUNTED_TYPE(tv
->m_type
) ||
457 is_refcount_realistic(tv
->m_data
.pstr
->getCount()));
460 void assertTv(const TypedValue
* tv
) {
461 always_assert(checkTv(tv
));
464 int init_closure(ActRec
* ar
, TypedValue
* sp
) {
465 c_Closure
* closure
= static_cast<c_Closure
*>(ar
->getThis());
467 // Swap in the $this or late bound class
468 ar
->setThis(closure
->getThisOrClass());
471 ar
->getThis()->incRefCount();
474 // Put in the correct context
475 ar
->m_func
= closure
->getInvokeFunc();
477 // The closure is the first local.
478 // Similar to tvWriteObject() but we don't incref because it used to be $this
479 // and now it is a local, so they cancel out
480 TypedValue
* firstLocal
= --sp
;
481 firstLocal
->m_type
= KindOfObject
;
482 firstLocal
->m_data
.pobj
= closure
;
484 // Copy in all the use vars
485 TypedValue
* prop
= closure
->getUseVars();
486 int n
= closure
->getNumUseVars();
487 for (int i
=0; i
< n
; i
++) {
494 void raiseWarning(const StringData
* sd
) {
495 raise_warning("%s", sd
->data());
498 HOT_FUNC
int64_t modHelper(int64_t left
, int64_t right
) {
499 // We already dealt with divide-by-zero up in hhbctranslator.