RefData* staticLocInitImpl(StringData* name, ActRec* fp, TypedValue val, TargetCache::CacheHandle ch) { assert(useTargetCache == (bool)ch); HphpArray* map; if (useTargetCache) { // If we have a cache handle, we know the current func isn't a // closure or generator closure so we can directly grab its static // locals map. const Func* func = fp->m_func; assert(!(func->isClosureBody() || func->isGeneratorFromClosure())); map = func->getStaticLocals(); } else { map = get_static_locals(fp); } TypedValue *mapVal = map->nvGet(name); if (!mapVal) { map->set(name, tvAsCVarRef(&val), false); mapVal = map->nvGet(name); } if (mapVal->m_type != KindOfRef) { tvBox(mapVal); } assert(mapVal->m_type == KindOfRef); RefData* ret = mapVal->m_data.pref; if (useTargetCache) { *TargetCache::handleToPtr<RefData*>(ch) = ret; } ret->incRefCount(); return ret; }
TypedValue* NameValueTable::bind(const StringData* name, TypedValue* val) { TypedValue* target = findTypedValue(name); if (val->m_type != KindOfRef) { tvBox(val); } tvBind(val, target); return target; }
void zBoxAndProxy(TypedValue* arg) { if (arg->m_type != KindOfRef) { tvBox(arg); } auto inner = arg->m_data.pref->tv(); if (inner->m_type == KindOfArray) { inner->m_data.parr = ProxyArray::Make(inner->m_data.parr); } }
Object c_SetResultToRefWaitHandle::ti_create(CObjRef wait_handle, VRefParam ref) { TypedValue* var_or_cell = ref->asTypedValue(); if (wait_handle.isNull()) { tvSetNull(*var_or_cell); return wait_handle; } if (!wait_handle.get()->getAttribute(ObjectData::IsWaitHandle)) { Object e(SystemLib::AllocInvalidArgumentExceptionObject( "Expected wait_handle to be an instance of WaitHandle or null")); throw e; } auto wh = static_cast<c_WaitHandle*>(wait_handle.get()); // succeeded? set result to ref and give back succeeded wait handle if (wh->isSucceeded()) { tvSet(wh->getResult(), *var_or_cell); return wh; } // failed? reset ref and give back failed wait handle if (wh->isFailed()) { tvSetNull(*var_or_cell); return wh; } // it's still running so it must be WaitableWaitHandle auto child = static_cast<c_WaitableWaitHandle*>(wh); // import child into the current context, detect cross-context cycles auto session = AsioSession::Get(); if (session->isInContext()) { child->enterContext(session->getCurrentContextIdx()); } // make sure the reference is properly boxed so that we can store cell pointer if (UNLIKELY(var_or_cell->m_type != KindOfRef)) { tvBox(var_or_cell); } p_SetResultToRefWaitHandle my_wh = NEWOBJ(c_SetResultToRefWaitHandle)(); my_wh->initialize(child, var_or_cell->m_data.pref); if (UNLIKELY(session->hasOnSetResultToRefCreateCallback())) { session->onSetResultToRefCreate(my_wh.get(), child); } return my_wh; }
void zBoxAndProxy(TypedValue* arg) { if (arg->m_type != KindOfRef) { tvBox(arg); } auto inner = arg->m_data.pref->tv(); if (inner->m_type == KindOfArray && !inner->m_data.parr->isProxyArray()) { ArrayData * inner_arr = inner->m_data.parr; if (inner_arr->isStatic() || inner_arr->hasMultipleRefs()) { ArrayData * tmp = inner_arr->copy(); tmp->incRefCount(); inner_arr->decRefAndRelease(); inner_arr = tmp; } inner->m_data.parr = ProxyArray::Make(inner_arr); } }
Object c_SetResultToRefWaitHandle::ti_create(CObjRef wait_handle, VRefParam ref) { TypedValue* var_or_cell = ref->asTypedValue(); if (wait_handle.isNull()) { tvSet(make_tv<KindOfNull>(), *var_or_cell); return wait_handle; } if (!wait_handle.get()->instanceof(c_WaitHandle::classof())) { Object e(SystemLib::AllocInvalidArgumentExceptionObject( "Expected wait_handle to be an instance of WaitHandle or null")); throw e; } c_WaitHandle* wh = static_cast<c_WaitHandle*>(wait_handle.get()); // succeeded? set result to ref and give back succeeded wait handle if (wh->isSucceeded()) { tvSet(wh->getResult(), *var_or_cell); return wh; } // failed? reset ref and give back failed wait handle if (wh->isFailed()) { tvSet(make_tv<KindOfNull>(), *var_or_cell); return wh; } // it's still running so it must be WaitableWaitHandle c_WaitableWaitHandle* child_wh = static_cast<c_WaitableWaitHandle*>(wh); // make sure the reference is properly boxed so that we can store cell pointer if (UNLIKELY(var_or_cell->m_type != KindOfRef)) { tvBox(var_or_cell); } p_SetResultToRefWaitHandle my_wh = NEWOBJ(c_SetResultToRefWaitHandle)(); my_wh->initialize(child_wh, var_or_cell->m_data.pref); AsioSession* session = AsioSession::Get(); if (UNLIKELY(session->hasOnSetResultToRefCreateCallback())) { session->onSetResultToRefCreate(my_wh.get(), child_wh); } return my_wh; }
void zBoxAndProxy(TypedValue* arg) { if (arg->m_type != KindOfRef) { tvBox(arg); } auto inner = arg->m_data.pref->tv(); assert(!isHackArrayType(inner->m_type)); if (isArrayType(inner->m_type) && !inner->m_data.parr->isProxyArray()) { ArrayData * inner_arr = inner->m_data.parr; if (inner_arr->cowCheck()) { ArrayData * tmp = inner_arr->copy(); if (inner_arr != tmp) { inner_arr->decRefAndRelease(); inner_arr = tmp; } } inner->m_data.parr = ProxyArray::Make(inner_arr); inner->m_type = KindOfArray; } }
// Defined here so it can be inlined below. RefData* lookupStaticFromClosure(ObjectData* closure, StringData* name, bool& inited) { assert(closure->instanceof(c_Closure::classof())); String str(StringData::Make(s_staticPrefix->slice(), name->slice())); auto const cls = closure->getVMClass(); auto const slot = cls->lookupDeclProp(str.get()); assert(slot != kInvalidSlot); auto const val = static_cast<c_Closure*>(closure)->getStaticVar(slot); if (val->m_type == KindOfUninit) { inited = false; val->m_type = KindOfNull; tvBox(val); return val->m_data.pref; } inited = true; assert(val->m_type == KindOfRef); return val->m_data.pref; }
TypedValue* zend_wrap_func(ActRec* ar) { // Sync the translator state. // We need to do this before a native function calls into a C library // compiled with -fomit-frame-pointer with the intention of having it call // back. Normal HHVM extensions have the luxury of only when such a thing // will be attempted, but we have no way to know in advance. VMRegAnchor _; TSRMLS_FETCH(); zend_ext_func native_func = reinterpret_cast<zend_ext_func>(ar->func()->nativeFuncPtr()); // Using Variant so exceptions will decref them Variant return_value_var(Variant::NullInit{}); auto const return_value = return_value_var.asTypedValue(); tvBox(return_value); Variant this_ptr_var(Variant::NullInit{}); auto const this_ptr = this_ptr_var.asTypedValue(); tvBox(this_ptr); if (ar->func()->cls() && ar->hasThis()) { tvWriteObject( ar->getThis(), this_ptr->m_data.pref->tv() ); } auto *return_value_ptr = &return_value->m_data.pref; // Clear any stored exception zend_clear_exception(TSRMLS_C); // Invoke the PHP extension function/method ZendExecutionStack::pushHHVMStack((void*)ar); try { native_func( ar->numArgs(), return_value->m_data.pref, return_value_ptr, this_ptr_var.isNull() ? nullptr : this_ptr->m_data.pref, 1 TSRMLS_CC ); } catch (...) { zend_wrap_func_cleanup(); throw; } zend_wrap_func_cleanup(); // If an exception was caught, rethrow it ZendExceptionStore& exceptionStore = ZendExceptionStore::getInstance(); if (!exceptionStore.empty()) { exceptionStore.rethrow(); } // Take care of freeing the args, tearing down the ActRec, and moving the // return value to the right place. Note that frame_free_locals expects to // be able to free return_value in the event of an exception, so we have to // take it out of our Variant /before/ calling that. TypedValue return_value_copy = *return_value; return_value->m_type = KindOfNull; // clear the Variant's copy frame_free_locals_inl(ar, ar->func()->numLocals(), &return_value_copy); memcpy(&ar->m_r, &return_value_copy, sizeof(TypedValue)); if (ar->func()->isReturnRef()) { if (!ar->m_r.m_data.pref->isReferenced()) { tvUnbox(&ar->m_r); } } else { tvUnbox(&ar->m_r); } return &ar->m_r; }
TypedValue* zend_wrap_func( ActRec* ar, zend_ext_func builtin_func, int numParams, bool isReturnRef) { TSRMLS_FETCH(); // Prepare the arguments and return value before they are // exposed to the PHP extension zPrepArgs(ar); // Using Variant so exceptions will decref them Variant return_value_var(Variant::NullInit{}); auto const return_value = return_value_var.asTypedValue(); tvBox(return_value); Variant this_ptr_var(Variant::NullInit{}); auto const this_ptr = this_ptr_var.asTypedValue(); tvBox(this_ptr); if (ar->hasThis()) { tvWriteObject( ar->getThis(), this_ptr->m_data.pref->tv() ); } auto *return_value_ptr = &return_value->m_data.pref; // Clear any stored exception ZendExceptionStore& exceptionStore = ZendExceptionStore::getInstance(); exceptionStore.clear(); // Invoke the PHP extension function/method ZendExecutionStack::pushHHVMStack(); try { builtin_func( ar->numArgs(), return_value->m_data.pref, return_value_ptr, this_ptr_var.isNull() ? nullptr : this_ptr->m_data.pref, 1 TSRMLS_CC ); } catch (...) { ZendExecutionStack::popHHVMStack(); throw; } ZendExecutionStack::popHHVMStack(); // If an exception was caught, rethrow it if (!exceptionStore.empty()) { exceptionStore.rethrow(); } // Take care of freeing the args, tearing down the ActRec, // and moving the return value to the right place frame_free_locals_inl(ar, numParams); memcpy(&ar->m_r, return_value, sizeof(TypedValue)); return_value->m_type = KindOfNull; if (isReturnRef) { if (!ar->m_r.m_data.pref->isReferenced()) { tvUnbox(&ar->m_r); } } else { tvUnbox(&ar->m_r); } return &ar->m_r; }