Allow early initialization of classes if they don't have any [sp]init methods
[hiphop-php.git] / hphp / runtime / vm / runtime.cpp
blobb3712d00af2e91faa99e541d32d8155fb2690cb9
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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"
35 namespace HPHP {
37 using Transl::tx64;
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;
45 /**
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());
52 decRefStr(s);
55 void print_int(int64_t i) {
56 char buf[256];
57 snprintf(buf, 256, "%" PRId64, i);
58 echo(buf);
59 TRACE(1, "t-x64 output(int): %" PRId64 "\n", i);
62 void print_boolean(bool val) {
63 if (val) {
64 echo("1");
68 HOT_FUNC_VM
69 ArrayData* new_array(int capacity) {
70 ArrayData *a = ArrayData::Make(capacity);
71 a->incRefCount();
72 TRACE(2, "newArrayHelper: capacity %d\n", capacity);
73 return a;
76 ArrayData* new_tuple(int n, const TypedValue* values) {
77 auto a = ArrayData::Make(n, values);
78 a->incRefCount();
79 TRACE(2, "new_tuple: size %d\n", n);
80 return a;
83 #define NEW_COLLECTION_HELPER(name) \
84 ObjectData* \
85 new##name##Helper(int nElms) { \
86 ObjectData *obj = NEWOBJ(c_##name)(); \
87 obj->incRefCount(); \
88 if (nElms) { \
89 collectionReserve(obj, nElms); \
90 } \
91 TRACE(2, "new" #name "Helper: capacity %d\n", nElms); \
92 return obj; \
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)();
102 obj->incRefCount();
103 TRACE(2, "newPairHelper: capacity 2\n");
104 return obj;
107 #undef NEW_COLLECTION_HELPER
109 static inline void
110 tvPairToCString(DataType t, uint64_t v,
111 const char** outStr,
112 size_t* outSz,
113 bool* outMustFree) {
114 if (IS_STRING_TYPE(t)) {
115 StringData *strd = (StringData*)v;
116 *outStr = strd->data();
117 *outSz = strd->size();
118 *outMustFree = false;
119 return;
121 Cell c;
122 c.m_type = t;
123 c.m_data.num = v;
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());
128 *outSz = s.size();
129 *outMustFree = true;
133 * concat_ss will decRef the values passed in as appropriate, and it will
134 * incRef the output string
136 StringData*
137 concat_ss(StringData* v1, StringData* v2) {
138 if (v1->getCount() > 1) {
139 StringData* ret = NEW(StringData)(v1, v2);
140 ret->setRefCount(1);
141 decRefStr(v2);
142 // Because v1->getCount() is greater than 1, we know we will never
143 // have to release the string here
144 v1->decRefCount();
145 return ret;
146 } else {
147 v1->append(v2->slice());
148 decRefStr(v2);
149 return v1;
154 * concat_is will decRef the string passed in as appropriate, and it will
155 * incRef the output string
157 StringData*
158 concat_is(int64_t v1, StringData* v2) {
159 int len1;
160 char intbuf[21];
161 char* intstart;
162 // Convert the int to a string
164 int is_negative;
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);
170 ret->incRefCount();
171 decRefStr(v2);
172 return ret;
176 * concat_si will decRef the string passed in as appropriate, and it will
177 * incRef the output string
179 StringData*
180 concat_si(StringData* v1, int64_t v2) {
181 int len2;
182 char intbuf[21];
183 char* intstart;
184 // Convert the int to a string
186 int is_negative;
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);
192 ret->incRefCount();
193 decRefStr(v1);
194 return ret;
198 * concat will decRef the values passed in as appropriate, and it will
199 * incRef the output string
201 StringData*
202 concat_tv(DataType t1, uint64_t v1, DataType t2, uint64_t v2) {
203 const char *s1, *s2;
204 size_t s1len, s2len;
205 bool free1, free2;
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);
219 return retval;
222 int64_t eq_null_str(StringData* v1) {
223 int64_t retval = v1->empty();
224 decRefStr(v1);
225 return retval;
228 int64_t eq_bool_str(int64_t v1, StringData* v2) {
229 // The truth table for v2->toBoolean() ? v1 : !v1
230 // looks like:
231 // \ v2:0 | v2:1
232 // v1:0 | 1 | 0
233 // v1:1 | 0 | 1
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);
241 decRefStr(v2);
242 return retval;
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);
248 decRefStr(v2);
249 if (ret == KindOfInt64) {
250 return v1 == lval;
251 } else if (ret == KindOfDouble) {
252 return (double)v1 == dval;
253 } else {
254 return v1 == 0;
258 int64_t eq_str_str(StringData* v1, StringData* v2) {
259 int64_t retval = v1->equal(v2);
260 decRefStr(v2);
261 decRefStr(v1);
262 return retval;
265 int64_t same_str_str(StringData* v1, StringData* v2) {
266 int64_t retval = v1 == v2 || v1->same(v2);
267 decRefStr(v2);
268 decRefStr(v1);
269 return retval;
272 int64_t str0_to_bool(StringData* sd) {
273 int64_t retval = sd->toBoolean();
274 return retval;
277 int64_t str_to_bool(StringData* sd) {
278 int64_t retval = str0_to_bool(sd);
279 decRefStr(sd);
280 return retval;
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);
290 decRefArr(ad);
291 return retval;
295 * tv_to_bool will decrement tv's refcount if tv is a refcounted type
297 int64_t
298 tv_to_bool(TypedValue* tv) {
299 using std::string;
300 bool retval;
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));
307 } else {
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,
317 const char* fname) {
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) {
332 MD5 md5;
333 int out_len;
334 md5 = MD5(string_md5(s, sz, false, out_len));
336 Unit* u = Repo::get().loadUnit("", md5);
337 if (u != nullptr) {
338 return u;
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
352 return argArray;
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);
358 return magicArgs;
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();
372 } else {
373 return ar->m_func->getStaticLocals();
377 void collection_setm_wk1_v0(ObjectData* obj, TypedValue* value) {
378 assert(obj);
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) {
387 assert(obj);
388 switch (obj->getCollectionType()) {
389 case Collection::VectorType: {
390 c_Vector* vec = static_cast<c_Vector*>(obj);
391 vec->set(key, value);
392 break;
394 case Collection::MapType: {
395 c_Map* mp = static_cast<c_Map*>(obj);
396 mp->set(key, value);
397 break;
399 case Collection::StableMapType: {
400 c_StableMap* smp = static_cast<c_StableMap*>(obj);
401 smp->set(key, value);
402 break;
404 case Collection::SetType: {
405 Object e(SystemLib::AllocRuntimeExceptionObject(
406 "Set does not support $c[$k] syntax"));
407 throw e;
409 case Collection::PairType: {
410 Object e(SystemLib::AllocRuntimeExceptionObject(
411 "Cannot assign to an element of a Pair"));
412 throw e;
414 default:
415 assert(false);
417 tvRefcountedDecRef(value);
420 void collection_setm_sk1_v0(ObjectData* obj, StringData* key,
421 TypedValue* value) {
422 switch (obj->getCollectionType()) {
423 case Collection::VectorType: {
424 Object e(SystemLib::AllocInvalidArgumentExceptionObject(
425 "Only integer keys may be used with Vectors"));
426 throw e;
428 case Collection::MapType: {
429 c_Map* mp = static_cast<c_Map*>(obj);
430 mp->set(key, value);
431 break;
433 case Collection::StableMapType: {
434 c_StableMap* smp = static_cast<c_StableMap*>(obj);
435 smp->set(key, value);
436 break;
438 case Collection::SetType: {
439 Object e(SystemLib::AllocRuntimeExceptionObject(
440 "Set does not support $c[$k] syntax"));
441 throw e;
443 case Collection::PairType: {
444 Object e(SystemLib::AllocRuntimeExceptionObject(
445 "Cannot assign to an element of a Pair"));
446 throw e;
448 default:
449 assert(false);
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());
470 if (ar->hasThis()) {
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++) {
488 tvDup(prop++, --sp);
491 return n + 1;
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.
500 assert(right != 0);
501 return left % right;
504 } // HPHP::VM