/* * Helper for empty array -> packed transitions. Creates an array * with one element. The element is transferred into the array (should * already be incref'd). */ ALWAYS_INLINE arr_lval EmptyArray::MakePackedInl(TypedValue tv) { auto const ad = static_cast<ArrayData*>( tl_heap->objMallocIndex(PackedArray::SmallSizeIndex) ); ad->initHeader_16( HeaderKind::Packed, OneReference, PackedArray::packSizeIndexAndAuxBits( PackedArray::SmallSizeIndex, ArrayData::kNotDVArray ) ); ad->m_sizeAndPos = 1; // size=1, pos=0 auto elem = PackedArray::LvalUncheckedInt(ad, 0); tvCopy(tv, elem); assertx(ad->kind() == ArrayData::kPackedKind); assertx(ad->dvArray() == ArrayData::kNotDVArray); assertx(ad->m_size == 1); assertx(ad->m_pos == 0); assertx(ad->hasExactlyOneRef()); assertx(PackedArray::checkInvariants(ad)); return arr_lval { ad, elem }; }
void c_Closure::init(int numArgs, ActRec* ar, TypedValue* sp) { auto const invokeFunc = getVMClass()->lookupMethod(s_uuinvoke.get()); m_ctx = ar->m_this; if (ar->hasThis()) { if (invokeFunc->isStatic()) { // Only set the class for static closures. setClass(ar->getThis()->getVMClass()); } else { ar->getThis()->incRefCount(); } } /* * Copy the use vars to instance variables, and initialize any * instance properties that are for static locals to KindOfUninit. */ auto const numDeclProperties = getVMClass()->numDeclProperties(); assert(numDeclProperties - numArgs == getInvokeFunc()->numStaticLocals()); TypedValue* beforeCurUseVar = sp + numArgs; TypedValue* curProperty = propVec(); int i = 0; assert(numArgs <= numDeclProperties); for (; i < numArgs; i++) { // teleport the references in here so we don't incref tvCopy(*--beforeCurUseVar, *curProperty++); } for (; i < numDeclProperties; ++i) { tvWriteUninit(curProperty++); } }
void c_Closure::init(int numArgs, ActRec* ar, TypedValue* sp) { auto const cls = getVMClass(); auto const invokeFunc = getInvokeFunc(); if (invokeFunc->cls()) { setThisOrClass(ar->getThisOrClass()); if (invokeFunc->isStatic()) { if (!hasClass()) { setClass(getThisUnchecked()->getVMClass()); } } else if (!hasClass()) { getThisUnchecked()->incRefCount(); } } else { hdr()->ctx = nullptr; } /* * Copy the use vars to instance variables, and initialize any * instance properties that are for static locals to KindOfUninit. */ auto const numDeclProperties = cls->numDeclProperties(); assertx(numDeclProperties - numArgs == getInvokeFunc()->numStaticLocals()); auto beforeCurUseVar = sp + numArgs; auto curProperty = getUseVars(); int i = 0; assertx(numArgs <= numDeclProperties); for (; i < numArgs; i++) { // teleport the references in here so we don't incref tvCopy(*--beforeCurUseVar, *curProperty++); } for (; i < numDeclProperties; ++i) { tvWriteUninit(*curProperty++); } }
MixedArray* StructArray::ToMixed(StructArray* old) { auto const oldSize = old->size(); auto const ad = ToMixedHeader(oldSize + 1); auto const srcData = old->data(); auto shape = old->shape(); memset(ad->hashTab(), static_cast<uint8_t>(MixedArray::Empty), sizeof(int32_t) * ad->hashSize()); for (auto i = 0; i < oldSize; ++i) { auto key = const_cast<StringData*>(shape->keyForOffset(i)); auto& e = ad->addKeyAndGetElem(key); tvCopy(srcData[i], e.data); } old->m_size = 0; ad->m_pos = old->m_pos; if (debug) { // For debug builds, set m_pos to 0 as well to make the // asserts in checkInvariants() happy. old->m_pos = 0; } assert(ad->checkInvariants()); assert(!ad->isFull()); assert(ad->hasExactlyOneRef()); return ad; }
/* * Converts a packed array to mixed, leaving the packed array in an * empty state. You need ToMixedCopy in cases where the old array * needs to remain un-modified (usually if `copy' is true). * * The returned array is mixed, and is guaranteed not to be isFull(). * (Note: only unset can call ToMixed when we aren't about to insert.) */ MixedArray* PackedArray::ToMixed(ArrayData* old) { auto const oldSize = old->m_size; auto const ad = ToMixedHeader(old, oldSize + 1); auto const mask = ad->m_tableMask; auto dstData = ad->data(); auto dstHash = ad->hashTab(); auto const srcData = packedData(old); auto i = uint32_t{0}; for (; i < oldSize; ++i) { dstData->setIntKey(i); tvCopy(srcData[i], dstData->data); *dstHash = i; ++dstData; ++dstHash; } for (; i <= mask; ++i) { *dstHash++ = MixedArray::Empty; } old->m_size = 0; assert(ad->checkInvariants()); assert(!ad->isFull()); return ad; }
int SocketSet::wait( timeval *tv ) { //assert( _blockmain ); // can't call wait if we haven't ordered the feature (?) if (_blockmain_pending == 0) { int ret; do { if (tv == NULL) { ret = private_select( NULL ); } else { timeval tvCopy( *tv ); //select resets timeval. ret = private_select( &tvCopy ); } } while (ret == 1); return ret; } else { #ifdef VSNET_DEBUG std::ostringstream ostr; for (int i = 0; i < _blockmain_pending; i++) if ( FD_ISSET( i, &_blockmain_set ) ) ostr<<" "<<i; COUT<<"something pending for sockets:" <<ostr.str() <<" ("<<_blockmain_pending<<")"<<endl; #endif struct timeval zerotv; zerotv.tv_sec = 0; zerotv.tv_usec = 0; return private_select( &zerotv ); } }
void c_Closure::init(int numArgs, ActRec* ar, TypedValue* sp) { auto const invokeFunc = getVMClass()->lookupMethod(s_uuinvoke.get()); m_thisOrClass = ar->m_this; if (ar->hasThis()) { if (invokeFunc->attrs() & AttrStatic) { // Only set the class for static closures. m_thisOrClass = reinterpret_cast<ObjectData*>( reinterpret_cast<intptr_t>(ar->getThis()->getVMClass()) | 1LL ); } else { ar->getThis()->incRefCount(); } } // Change my __invoke's m_cls to be the same as my creator's Class* scope = ar->m_func->cls(); m_func = invokeFunc->cloneAndSetClass(scope); // copy the props to instance variables assert(m_cls->numDeclProperties() == numArgs); TypedValue* beforeCurUseVar = sp + numArgs; TypedValue* curProperty = propVec(); for (int i = 0; i < numArgs; i++) { // teleport the references in here so we don't incref tvCopy(*--beforeCurUseVar, *curProperty++); } }
static UBool enumCharNames_callback(CallCtx *ctx, UChar32 code, UCharNameChoice nameChoice, const char *name, int32_t length) { TypedValue args[3]; args[0].m_type = args[1].m_type = KindOfInt64; args[0].m_data.num = code; args[1].m_data.num = nameChoice; Variant charName = String(name, length, CopyString); tvCopy(*charName.asTypedValue(), args[2]); Variant retval; g_context->invokeFuncFew(retval.asTypedValue(), *ctx, 3, args); return true; }
TypedValue* methodWrapper(ActRec* ar) { auto func = ar->m_func; auto numArgs = func->numParams(); auto numNonDefault = ar->numArgs(); bool isStatic = func->isStatic(); assert(!func->hasVariadicCaptureParam()); TypedValue* args = ((TypedValue*)ar) - 1; TypedValue rv; rv.m_type = KindOfNull; if (LIKELY(numNonDefault == numArgs) || LIKELY(nativeWrapperCheckArgs(ar))) { if (coerceFCallArgs(args, numArgs, numNonDefault, func)) { // Prepend a context arg for methods // KindOfClass when it's being called statically Foo::bar() // KindOfObject when it's being called on an instance $foo->bar() TypedValue ctx; if (ar->hasThis()) { if (isStatic) { throw_instance_method_fatal(getInvokeName(ar)->data()); } ctx.m_type = KindOfObject; ctx.m_data.pobj = ar->getThis(); } else { if (!isStatic) { throw_instance_method_fatal(getInvokeName(ar)->data()); } ctx.m_type = KindOfClass; ctx.m_data.pcls = const_cast<Class*>(ar->getClass()); } callFunc(func, &ctx, args, numArgs, rv); } else if (func->attrs() & AttrParamCoerceModeFalse) { rv.m_type = KindOfBoolean; rv.m_data.num = 0; } } assert(rv.m_type != KindOfUninit); if (isStatic) { frame_free_locals_no_this_inl(ar, func->numLocals(), &rv); } else { frame_free_locals_inl(ar, func->numLocals(), &rv); } tvCopy(rv, ar->m_r); return &ar->m_r; }
void NameValueTable::detach(ActRec* fp) { assert(m_fp == fp); m_fp = nullptr; const auto func = fp->m_func; const Id numNames = func->numNamedLocals(); TypedValue* loc = frame_local(fp, 0); for (Id i = 0; i < numNames; ++i, --loc) { assert(func->lookupVarId(func->localVarName(i)) == i); auto elm = findElm(func->localVarName(i)); assert(elm && elm->m_tv.m_type == KindOfNamedLocal); tvCopy(*loc, elm->m_tv); tvDebugTrash(loc); } }
void c_ConditionWaitHandle::onUnblocked() { decRefObj(m_child); m_child = nullptr; // Unblocked after notification. if (LIKELY(isFinished())) { decRefObj(this); return; } auto parentChain = getParentChain(); setState(STATE_FAILED); tvCopy( make_tv<KindOfObject>(getNotNotifiedException().detach()), m_resultOrException ); parentChain.unblock(); decRefObj(this); }
ArrayData* StructArray::MakeUncounted(ArrayData* array) { auto structArray = asStructArray(array); // We don't need to copy the full capacity, since the array won't // change once it's uncounted. auto size = structArray->size(); StructArray* result = createUncounted(structArray->shape(), size); result->m_hdr.init(HeaderKind::Struct, UncountedValue); result->m_sizeAndPos = array->m_sizeAndPos; auto const srcData = structArray->data(); auto const stop = srcData + size; auto targetData = result->data(); for (auto ptr = srcData; ptr != stop; ++ptr, ++targetData) { auto srcVariant = MixedArray::CreateVarForUncountedArray(tvAsCVarRef(ptr)); tvCopy(*srcVariant.asTypedValue(), *targetData); } assert(result->m_pos == structArray->m_pos); assert(result->isUncounted()); return result; }
void NameValueTable::attach(ActRec* fp) { assert(m_fp == nullptr); m_fp = fp; const auto func = fp->m_func; const Id numNames = func->numNamedLocals(); TypedValue* loc = frame_local(fp, 0); for (Id i = 0; i < numNames; ++i, --loc) { assert(func->lookupVarId(func->localVarName(i)) == i); auto elm = insert(func->localVarName(i)); assert(elm); if (elm->m_tv.m_type != KindOfInvalid) { assert(elm->m_tv.m_type != KindOfNamedLocal); tvCopy(elm->m_tv, *loc); } elm->m_tv.m_type = KindOfNamedLocal; elm->m_tv.m_data.num = i; } }
TypedValue* functionWrapper(ActRec* ar) { auto func = ar->m_func; auto numArgs = func->numParams(); auto numNonDefault = ar->numArgs(); assert(!func->hasVariadicCaptureParam()); TypedValue* args = ((TypedValue*)ar) - 1; TypedValue rv; rv.m_type = KindOfNull; if (LIKELY(numNonDefault == numArgs) || LIKELY(nativeWrapperCheckArgs(ar))) { if (coerceFCallArgs(args, numArgs, numNonDefault, func)) { callFunc(func, nullptr, args, numArgs, rv); } else if (func->attrs() & AttrParamCoerceModeFalse) { rv.m_type = KindOfBoolean; rv.m_data.num = 0; } } assert(rv.m_type != KindOfUninit); frame_free_locals_no_this_inl(ar, func->numLocals(), &rv); tvCopy(rv, ar->m_r); return &ar->m_r; }
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); if (ar->func()->isReturnRef()) { if (!return_value_copy.m_data.pref->isReferenced()) { tvUnbox(&return_value_copy); } } else { tvUnbox(&return_value_copy); } auto ret_slot = ar->retSlot(); tvCopy(return_value_copy, *ret_slot); return ret_slot; }
PreClass* PreClassEmitter::create(Unit& unit) const { Attr attrs = m_attrs; if (attrs & AttrPersistent && !RuntimeOption::RepoAuthoritative && SystemLib::s_inited) { attrs = Attr(attrs & ~AttrPersistent); } auto pc = folly::make_unique<PreClass>( &unit, m_line1, m_line2, m_offset, m_name, attrs, m_parent, m_docComment, m_id, m_hoistable); pc->m_instanceCtor = m_instanceCtor; pc->m_instanceDtor = m_instanceDtor; pc->m_builtinObjSize = m_builtinObjSize; pc->m_builtinODOffset = m_builtinODOffset; pc->m_interfaces = m_interfaces; pc->m_usedTraits = m_usedTraits; pc->m_requirements = m_requirements; pc->m_traitPrecRules = m_traitPrecRules; pc->m_traitAliasRules = m_traitAliasRules; pc->m_enumBaseTy = m_enumBaseTy; pc->m_numDeclMethods = m_numDeclMethods; pc->m_ifaceVtableSlot = m_ifaceVtableSlot; // Set user attributes. [&] { pc->m_userAttributes = m_userAttributes; pc->m_nativeDataInfo = nullptr; if (!m_userAttributes.size()) return; // Check for <<__NativeData("Type")>>. auto it = m_userAttributes.find(s_nativedata.get()); if (it == m_userAttributes.end()) return; TypedValue ndiInfo = it->second; if (ndiInfo.m_type != KindOfArray) return; // Use the first string label which references a registered type. In // practice, there should generally only be one item and it should be a // string, but maybe that'll be extended... for (ArrayIter it(ndiInfo.m_data.parr); it; ++it) { Variant val = it.second(); if (!val.isString()) continue; pc->m_nativeDataInfo = Native::getNativeDataInfo(val.toString().get()); if (pc->m_nativeDataInfo) break; } }(); PreClass::MethodMap::Builder methodBuild; for (MethodVec::const_iterator it = m_methods.begin(); it != m_methods.end(); ++it) { Func* f = (*it)->create(unit, pc.get()); methodBuild.add(f->name(), f); } pc->m_methods.create(methodBuild); PreClass::PropMap::Builder propBuild; for (unsigned i = 0; i < m_propMap.size(); ++i) { const Prop& prop = m_propMap[i]; propBuild.add(prop.name(), PreClass::Prop(pc.get(), prop.name(), prop.attrs(), prop.typeConstraint(), prop.docComment(), prop.val(), prop.repoAuthType())); } pc->m_properties.create(propBuild); PreClass::ConstMap::Builder constBuild; for (unsigned i = 0; i < m_constMap.size(); ++i) { const Const& const_ = m_constMap[i]; TypedValueAux tvaux; if (const_.isAbstract()) { tvWriteUninit(&tvaux); tvaux.constModifiers().m_isAbstract = true; } else { tvCopy(const_.val(), tvaux); tvaux.constModifiers().m_isAbstract = false; } tvaux.constModifiers().m_isType = const_.isTypeconst(); constBuild.add(const_.name(), PreClass::Const(const_.name(), tvaux, const_.phpCode())); } if (auto nativeConsts = Native::getClassConstants(m_name)) { for (auto cnsMap : *nativeConsts) { TypedValueAux tvaux; tvCopy(cnsMap.second, tvaux); tvaux.constModifiers() = { false, false }; constBuild.add(cnsMap.first, PreClass::Const(cnsMap.first, tvaux, staticEmptyString())); } } pc->m_constants.create(constBuild); return pc.release(); }