TypedValue* tg1_19GenericContinuation___construct(TypedValue* rv, HPHP::VM::ActRec* ar, long long count, ObjectData* this_) { TypedValue* args UNUSED = ((TypedValue*)ar) - 1; rv->m_data.num = 0LL; rv->_count = 0; rv->m_type = KindOfNull; switch (count) { default: // count >= 7 if ((args-6)->m_type != KindOfArray) { tvCastToArrayInPlace(args-6); } case 6: case 5: break; } if ((args-4)->m_type != KindOfArray) { tvCastToArrayInPlace(args-4); } if (!IS_STRING_TYPE((args-3)->m_type)) { tvCastToStringInPlace(args-3); } if ((args-2)->m_type != KindOfBoolean) { tvCastToBooleanInPlace(args-2); } if ((args-1)->m_type != KindOfInt64) { tvCastToInt64InPlace(args-1); } if ((args-0)->m_type != KindOfInt64) { tvCastToInt64InPlace(args-0); } Variant defVal5; th_19GenericContinuation___construct((this_), (long long)(args[-0].m_data.num), (long long)(args[-1].m_data.num), (bool)(args[-2].m_data.num), (Value*)(args-3), (Value*)(args-4), (count > 5) ? (args-5) : (TypedValue*)(&defVal5), (count > 6) ? (Value*)(args-6) : (Value*)(&null_array)); return rv; }
TypedValue * fg1_iterator_apply(TypedValue* rv, HPHP::VM::ActRec* ar, int64_t count) { TypedValue* args UNUSED = ((TypedValue*)ar) - 1; tvCastToArrayInPlace(args-2); fh_iterator_apply((rv), (args-0), (args-1), (count > 2) ? (Value*)(args-2) : (Value*)(&null_array)); if (rv->m_type == KindOfUninit) rv->m_type = KindOfNull; return rv; }
TypedValue * fg1_hphp_create_continuation(TypedValue* rv, HPHP::VM::ActRec* ar, long long count) { TypedValue* args UNUSED = ((TypedValue*)ar) - 1; rv->_count = 0; rv->m_type = KindOfObject; switch (count) { default: // count >= 4 if ((args-3)->m_type != KindOfArray) { tvCastToArrayInPlace(args-3); } case 3: break; } if (!IS_STRING_TYPE((args-2)->m_type)) { tvCastToStringInPlace(args-2); } if (!IS_STRING_TYPE((args-1)->m_type)) { tvCastToStringInPlace(args-1); } if (!IS_STRING_TYPE((args-0)->m_type)) { tvCastToStringInPlace(args-0); } fh_hphp_create_continuation((Value*)(rv), (Value*)(args-0), (Value*)(args-1), (Value*)(args-2), (count > 3) ? (Value*)(args-3) : (Value*)(&null_array)); if (rv->m_data.num == 0LL)rv->m_type = KindOfNull; return rv; }
TypedValue * fg1_forward_static_call_array(TypedValue* rv, HPHP::VM::ActRec* ar, int64_t count) { TypedValue* args UNUSED = ((TypedValue*)ar) - 1; tvCastToArrayInPlace(args-1); fh_forward_static_call_array((rv), (args-0), (Value*)(args-1)); if (rv->m_type == KindOfUninit) rv->m_type = KindOfNull; return rv; }
void fg1_call_user_func_array_async(TypedValue* rv, ActRec* ar, int32_t count) { TypedValue* args UNUSED = ((TypedValue*)ar) - 1; tvCastToArrayInPlace(args-1); rv->m_type = KindOfObject; fh_call_user_func_array_async(&(rv->m_data), (args-0), &args[-1].m_data); if (rv->m_data.num == 0LL) rv->m_type = KindOfNull; }
TypedValue * fg1_call_user_func_array_async(TypedValue* rv, HPHP::VM::ActRec* ar, int64_t count) { TypedValue* args UNUSED = ((TypedValue*)ar) - 1; rv->m_type = KindOfObject; tvCastToArrayInPlace(args-1); fh_call_user_func_array_async((Value*)(rv), (args-0), (Value*)(args-1)); if (rv->m_data.num == 0LL)rv->m_type = KindOfNull; return rv; }
TypedValue* tg1_18GenArrayWaitHandle_create(TypedValue* rv, HPHP::VM::ActRec* ar, int64_t count) { TypedValue* args UNUSED = ((TypedValue*)ar) - 1; rv->_count = 0; rv->m_type = KindOfObject; tvCastToArrayInPlace(args-0); th_18GenArrayWaitHandle_create((Value*)(rv), ("GenArrayWaitHandle"), (Value*)(args-0)); if (rv->m_data.num == 0LL)rv->m_type = KindOfNull; return rv; }
void tvCastToObjectInPlace(TypedValue* tv) { assert(tvIsPlausible(*tv)); tvUnboxIfNeeded(tv); ObjectData* o; do { switch (tv->m_type) { case KindOfUninit: case KindOfNull: o = SystemLib::AllocStdClassObject().detach(); continue; case KindOfBoolean: case KindOfInt64: case KindOfDouble: case KindOfPersistentString: case KindOfResource: o = SystemLib::AllocStdClassObject().detach(); o->o_set(s_scalar, tvAsVariant(tv)); continue; case KindOfString: o = SystemLib::AllocStdClassObject().detach(); o->o_set(s_scalar, tvAsVariant(tv)); tvDecRefStr(tv); continue; case KindOfPersistentVec: case KindOfVec: case KindOfPersistentDict: case KindOfDict: case KindOfPersistentKeyset: case KindOfKeyset: tvCastToArrayInPlace(tv); // Fall-through to array case case KindOfPersistentArray: case KindOfArray: // For arrays, we fall back on the Variant machinery tvAsVariant(tv) = ObjectData::FromArray(tv->m_data.parr); return; case KindOfObject: return; case KindOfRef: case KindOfClass: break; } not_reached(); } while (0); tv->m_data.pobj = o; tv->m_type = KindOfObject; assert(cellIsPlausible(*tv)); }
void TypeConstraint::verifyFail(const Func* func, int paramNum, TypedValue* tv) const { JIT::VMRegAnchor _; const StringData* tn = typeName(); if (isSelf()) { selfToTypeName(func, &tn); } else if (isParent()) { parentToTypeName(func, &tn); } auto const givenType = describe_actual_type(tv); auto c = tvToCell(tv); if (isArray() && !isSoft() && !func->mustBeRef(paramNum) && c->m_type == KindOfObject && c->m_data.pobj->isCollection()) { // To ease migration, the 'array' type constraint will implicitly cast // collections to arrays, provided the type constraint is not soft and // the parameter is not by reference. We raise a notice to let the user // know that there was a type mismatch and that an implicit conversion // was performed. raise_notice( folly::format( "Argument {} to {}() must be of type {}, {} given; argument {} was " "implicitly cast to array", paramNum + 1, func->fullName()->data(), fullName(), givenType, paramNum + 1 ).str() ); tvCastToArrayInPlace(tv); return; } if (isExtended() && isSoft()) { // Soft extended type hints raise warnings instead of recoverable // errors, to ease migration. raise_debugging( "Argument %d to %s() must be of type %s, %s given", paramNum + 1, func->fullName()->data(), fullName().c_str(), givenType); } else if (isExtended() && isNullable()) { raise_typehint_error( folly::format( "Argument {} to {}() must be of type {}, {} given", paramNum + 1, func->fullName()->data(), fullName(), givenType ).str() ); } else { raise_typehint_error( folly::format( "Argument {} passed to {}() must be an instance of {}, {} given", paramNum + 1, func->fullName()->data(), tn->data(), givenType ).str() ); } }
void fg1_call_user_method_array(TypedValue* rv, ActRec* ar, int32_t count) { TypedValue* args UNUSED = ((TypedValue*)ar) - 1; if ((args-2)->m_type != KindOfArray) { tvCastToArrayInPlace(args-2); } if (!IS_STRING_TYPE((args-0)->m_type)) { tvCastToStringInPlace(args-0); } fh_call_user_method_array(rv, &args[-0].m_data, (args-1), &args[-2].m_data); if (rv->m_type == KindOfUninit) rv->m_type = KindOfNull; }
TypedValue * fg1_call_user_method_array(TypedValue* rv, HPHP::VM::ActRec* ar, long long count) { TypedValue* args UNUSED = ((TypedValue*)ar) - 1; if ((args-2)->m_type != KindOfArray) { tvCastToArrayInPlace(args-2); } if (!IS_STRING_TYPE((args-0)->m_type)) { tvCastToStringInPlace(args-0); } fh_call_user_method_array((rv), (Value*)(args-0), (args-1), (Value*)(args-2)); if (rv->m_type == KindOfUninit) rv->m_type = KindOfNull; return rv; }
TypedValue * fg1_curl_setopt_array(TypedValue* rv, HPHP::VM::ActRec* ar, int64_t count) { TypedValue* args UNUSED = ((TypedValue*)ar) - 1; rv->m_type = KindOfBoolean; if ((args-1)->m_type != KindOfArray) { tvCastToArrayInPlace(args-1); } if ((args-0)->m_type != KindOfObject) { tvCastToObjectInPlace(args-0); } rv->m_data.num = (fh_curl_setopt_array(&args[-0].m_data, &args[-1].m_data)) ? 1LL : 0LL; return rv; }
ArrayData* convCellToArrHelper(TypedValue tv) { // Note: the call sites of this function all assume that // no user code will run and no recoverable exceptions will // occur while running this code. This seems trivially true // in all cases but converting objects to arrays. It also // seems true for that case as well, since the resulting array // is essentially metadata for the object. If that is not true, // you might end up looking at this code in a debugger and now // you know why. tvCastToArrayInPlace(&tv); // consumes a ref on counted values return tv.m_data.parr; }
TypedValue * fg1_hphp_create_object(TypedValue* rv, HPHP::VM::ActRec* ar, long long count) { TypedValue* args UNUSED = ((TypedValue*)ar) - 1; rv->_count = 0; rv->m_type = KindOfObject; if ((args-1)->m_type != KindOfArray) { tvCastToArrayInPlace(args-1); } if (!IS_STRING_TYPE((args-0)->m_type)) { tvCastToStringInPlace(args-0); } fh_hphp_create_object((Value*)(rv), (Value*)(args-0), (Value*)(args-1)); if (rv->m_data.num == 0LL)rv->m_type = KindOfNull; return rv; }
TypedValue* tg1_19GenericContinuation_update(TypedValue* rv, HPHP::VM::ActRec* ar, long long count, ObjectData* this_) { TypedValue* args UNUSED = ((TypedValue*)ar) - 1; rv->m_data.num = 0LL; rv->_count = 0; rv->m_type = KindOfNull; if ((args-2)->m_type != KindOfArray) { tvCastToArrayInPlace(args-2); } if ((args-0)->m_type != KindOfInt64) { tvCastToInt64InPlace(args-0); } th_19GenericContinuation_update((this_), (long long)(args[-0].m_data.num), (args-1), (Value*)(args-2)); return rv; }
void fg1_unserialize(TypedValue* rv, ActRec* ar, int32_t count) { TypedValue* args UNUSED = ((TypedValue*)ar) - 1; switch (count) { default: // count >= 2 if ((args-1)->m_type != KindOfArray) { tvCastToArrayInPlace(args-1); } case 1: break; } if (!IS_STRING_TYPE((args-0)->m_type)) { tvCastToStringInPlace(args-0); } Array defVal1 = empty_array; fh_unserialize(rv, &args[-0].m_data, (count > 1) ? &args[-1].m_data : (Value*)(&defVal1)); if (rv->m_type == KindOfUninit) rv->m_type = KindOfNull; }
void fg1_xhprof_enable(TypedValue* rv, ActRec* ar, int32_t count) { TypedValue* args UNUSED = ((TypedValue*)ar) - 1; switch (count) { default: // count >= 2 if ((args-1)->m_type != KindOfArray) { tvCastToArrayInPlace(args-1); } case 1: if ((args-0)->m_type != KindOfInt64) { tvCastToInt64InPlace(args-0); } case 0: break; } rv->m_type = KindOfNull; fh_xhprof_enable((count > 0) ? (int)(args[-0].m_data.num) : (int)(0), (count > 1) ? &args[-1].m_data : (Value*)(&null_array)); }
void fg1_call_user_func_array_rpc(TypedValue* rv, ActRec* ar, int32_t count) { TypedValue* args UNUSED = ((TypedValue*)ar) - 1; if ((args-5)->m_type != KindOfArray) { tvCastToArrayInPlace(args-5); } if ((args-3)->m_type != KindOfInt64) { tvCastToInt64InPlace(args-3); } if (!IS_STRING_TYPE((args-2)->m_type)) { tvCastToStringInPlace(args-2); } if ((args-1)->m_type != KindOfInt64) { tvCastToInt64InPlace(args-1); } if (!IS_STRING_TYPE((args-0)->m_type)) { tvCastToStringInPlace(args-0); } fh_call_user_func_array_rpc(rv, &args[-0].m_data, (int)(args[-1].m_data.num), &args[-2].m_data, (int)(args[-3].m_data.num), (args-4), &args[-5].m_data); if (rv->m_type == KindOfUninit) rv->m_type = KindOfNull; }
TypedValue * fg1_evhttp_async_get(TypedValue* rv, HPHP::VM::ActRec* ar, int64_t count) { TypedValue* args UNUSED = ((TypedValue*)ar) - 1; switch (count) { default: // count >= 3 if ((args-2)->m_type != KindOfInt64) { tvCastToInt64InPlace(args-2); } case 2: if ((args-1)->m_type != KindOfArray) { tvCastToArrayInPlace(args-1); } case 1: break; } if (!IS_STRING_TYPE((args-0)->m_type)) { tvCastToStringInPlace(args-0); } fh_evhttp_async_get((rv), &args[-0].m_data, (count > 1) ? &args[-1].m_data : (Value*)(&null_array), (count > 2) ? (int)(args[-2].m_data.num) : (int)(5)); if (rv->m_type == KindOfUninit) rv->m_type = KindOfNull; return rv; }
TypedValue * fg1_call_user_func_array_rpc(TypedValue* rv, HPHP::VM::ActRec* ar, int64_t count) { TypedValue* args UNUSED = ((TypedValue*)ar) - 1; if ((args-5)->m_type != KindOfArray) { tvCastToArrayInPlace(args-5); } if ((args-3)->m_type != KindOfInt64) { tvCastToInt64InPlace(args-3); } if (!IS_STRING_TYPE((args-2)->m_type)) { tvCastToStringInPlace(args-2); } if ((args-1)->m_type != KindOfInt64) { tvCastToInt64InPlace(args-1); } if (!IS_STRING_TYPE((args-0)->m_type)) { tvCastToStringInPlace(args-0); } fh_call_user_func_array_rpc((rv), (Value*)(args-0), (int)(args[-1].m_data.num), (Value*)(args-2), (int)(args[-3].m_data.num), (args-4), (Value*)(args-5)); if (rv->m_type == KindOfUninit) rv->m_type = KindOfNull; return rv; }
TypedValue * fg1_extract(TypedValue* rv, HPHP::VM::ActRec* ar, int64_t count) { TypedValue* args UNUSED = ((TypedValue*)ar) - 1; rv->m_type = KindOfInt64; switch (count) { default: // count >= 3 if (!IS_STRING_TYPE((args-2)->m_type)) { tvCastToStringInPlace(args-2); } case 2: if ((args-1)->m_type != KindOfInt64) { tvCastToInt64InPlace(args-1); } case 1: break; } if ((args-0)->m_type != KindOfArray) { tvCastToArrayInPlace(args-0); } rv->m_data.num = (int64_t)fh_extract(&args[-0].m_data, (count > 1) ? (int)(args[-1].m_data.num) : (int)(k_EXTR_OVERWRITE), (count > 2) ? &args[-2].m_data : (Value*)(&empty_string)); return rv; }
TypedValue * fg1_proc_open(TypedValue* rv, HPHP::VM::ActRec* ar, long long count) { TypedValue* args UNUSED = ((TypedValue*)ar) - 1; switch (count) { default: // count >= 6 case 5: case 4: if (!IS_STRING_TYPE((args-3)->m_type)) { tvCastToStringInPlace(args-3); } case 3: break; } if ((args-1)->m_type != KindOfArray) { tvCastToArrayInPlace(args-1); } if (!IS_STRING_TYPE((args-0)->m_type)) { tvCastToStringInPlace(args-0); } fh_proc_open((rv), (Value*)(args-0), (Value*)(args-1), (args-2), (count > 3) ? (Value*)(args-3) : (Value*)(&null_string), (count > 4) ? (args-4) : (TypedValue*)(&null_variant), (count > 5) ? (args-5) : (TypedValue*)(&null_variant)); if (rv->m_type == KindOfUninit) rv->m_type = KindOfNull; return rv; }
TypedValue* tg1_12Continuation___construct(TypedValue* rv, HPHP::VM::ActRec* ar, int64_t count, ObjectData* this_) { TypedValue* args UNUSED = ((TypedValue*)ar) - 1; rv->m_data.num = 0LL; rv->m_type = KindOfNull; switch (count) { default: // count >= 4 if ((args-3)->m_type != KindOfArray) { tvCastToArrayInPlace(args-3); } case 3: case 2: break; } if (!IS_STRING_TYPE((args-1)->m_type)) { tvCastToStringInPlace(args-1); } if ((args-0)->m_type != KindOfInt64) { tvCastToInt64InPlace(args-0); } Variant defVal2; th_12Continuation___construct((this_), (long)(args[-0].m_data.num), &args[-1].m_data, (count > 2) ? (args-2) : (TypedValue*)(&defVal2), (count > 3) ? &args[-3].m_data : (Value*)(&null_array)); return rv; }
void fg1_forward_static_call_array(TypedValue* rv, ActRec* ar, int32_t count) { TypedValue* args UNUSED = ((TypedValue*)ar) - 1; tvCastToArrayInPlace(args-1); fh_forward_static_call_array(rv, (args-0), &args[-1].m_data); if (rv->m_type == KindOfUninit) rv->m_type = KindOfNull; }
void TypeConstraint::verifyFail(const Func* func, TypedValue* tv, int id) const { VMRegAnchor _; std::string name = displayName(func); auto const givenType = describe_actual_type(tv, isHHType()); // Handle return type constraint failures if (id == ReturnId) { std::string msg; if (func->isClosureBody()) { msg = folly::format( "Value returned from {}closure must be of type {}, {} given", func->isAsync() ? "async " : "", name, givenType ).str(); } else { msg = folly::format( "Value returned from {}{} {}() must be of type {}, {} given", func->isAsync() ? "async " : "", func->preClass() ? "method" : "function", func->fullName()->data(), name, givenType ).str(); } if (RuntimeOption::EvalCheckReturnTypeHints >= 2 && !isSoft() && (!func->isClosureBody() || !RuntimeOption::EvalSoftClosureReturnTypeHints)) { raise_return_typehint_error(msg); } else { raise_debugging(msg); } return; } // Handle implicit collection->array conversion for array parameter type // constraints auto c = tvToCell(tv); if (isArray() && !isSoft() && !func->mustBeRef(id) && c->m_type == KindOfObject && c->m_data.pobj->isCollection()) { // To ease migration, the 'array' type constraint will implicitly cast // collections to arrays, provided the type constraint is not soft and // the parameter is not by reference. We raise a notice to let the user // know that there was a type mismatch and that an implicit conversion // was performed. raise_notice( folly::format( "Argument {} to {}() must be of type {}, {} given; argument {} was " "implicitly cast to array", id + 1, func->fullName()->data(), name, givenType, id + 1 ).str() ); tvCastToArrayInPlace(tv); return; } // Handle parameter type constraint failures if (isExtended() && isSoft()) { // Soft extended type hints raise warnings instead of recoverable // errors, to ease migration. raise_debugging( folly::format( "Argument {} to {}() must be of type {}, {} given", id + 1, func->fullName()->data(), name, givenType ).str() ); } else if (isExtended() && isNullable()) { raise_typehint_error( folly::format( "Argument {} to {}() must be of type {}, {} given", id + 1, func->fullName()->data(), name, givenType ).str() ); } else { auto cls = Unit::lookupClass(m_typeName); if (cls && isInterface(cls)) { raise_typehint_error( folly::format( "Argument {} passed to {}() must implement interface {}, {} given", id + 1, func->fullName()->data(), name, givenType ).str() ); } else { raise_typehint_error( folly::format( "Argument {} passed to {}() must be an instance of {}, {} given", id + 1, func->fullName()->data(), name, givenType ).str() ); } } }
void TypeConstraint::verifyFail(const Func* func, TypedValue* tv, int id, bool useStrictTypes) const { VMRegAnchor _; std::string name = displayName(func); auto const givenType = describe_actual_type(tv, isHHType()); if (UNLIKELY(!useStrictTypes)) { if (auto dt = underlyingDataType()) { // In non-strict mode we may be able to coerce a type failure. For object // typehints there is no possible coercion in the failure case, but HNI // builtins currently only guard on kind not class so the following wil // generate false positives for objects. if (*dt != KindOfObject) { // HNI conversions implicitly unbox references, this behavior is wrong, // in particular it breaks the way type conversion works for PHP 7 // scalar type hints if (tv->m_type == KindOfRef) { auto inner = tv->m_data.pref->var()->asTypedValue(); if (tvCoerceParamInPlace(inner, *dt)) { tvAsVariant(tv) = tvAsVariant(inner); return; } } else { if (tvCoerceParamInPlace(tv, *dt)) return; } } } } else if (UNLIKELY(!func->unit()->isHHFile() && !RuntimeOption::EnableHipHopSyntax)) { // PHP 7 allows for a widening conversion from Int to Float. We still ban // this in HH files. if (auto dt = underlyingDataType()) { if (*dt == KindOfDouble && tv->m_type == KindOfInt64 && tvCoerceParamToDoubleInPlace(tv)) { return; } } } // Handle return type constraint failures if (id == ReturnId) { std::string msg; if (func->isClosureBody()) { msg = folly::format( "Value returned from {}closure must be of type {}, {} given", func->isAsync() ? "async " : "", name, givenType ).str(); } else { msg = folly::format( "Value returned from {}{} {}() must be of type {}, {} given", func->isAsync() ? "async " : "", func->preClass() ? "method" : "function", func->fullName(), name, givenType ).str(); } if (RuntimeOption::EvalCheckReturnTypeHints >= 2 && !isSoft()) { raise_return_typehint_error(msg); } else { raise_warning_unsampled(msg); } return; } // Handle implicit collection->array conversion for array parameter type // constraints auto c = tvToCell(tv); if (isArray() && !isSoft() && !func->mustBeRef(id) && c->m_type == KindOfObject && c->m_data.pobj->isCollection()) { // To ease migration, the 'array' type constraint will implicitly cast // collections to arrays, provided the type constraint is not soft and // the parameter is not by reference. We raise a notice to let the user // know that there was a type mismatch and that an implicit conversion // was performed. raise_notice( folly::format( "Argument {} to {}() must be of type {}, {} given; argument {} was " "implicitly cast to array", id + 1, func->fullName(), name, givenType, id + 1 ).str() ); tvCastToArrayInPlace(tv); return; } // Handle parameter type constraint failures if (isExtended() && isSoft()) { // Soft extended type hints raise warnings instead of recoverable // errors, to ease migration. raise_warning_unsampled( folly::format( "Argument {} to {}() must be of type {}, {} given", id + 1, func->fullName(), name, givenType ).str() ); } else if (isExtended() && isNullable()) { raise_typehint_error( folly::format( "Argument {} to {}() must be of type {}, {} given", id + 1, func->fullName(), name, givenType ).str() ); } else { auto cls = Unit::lookupClass(m_typeName); if (cls && isInterface(cls)) { raise_typehint_error( folly::format( "Argument {} passed to {}() must implement interface {}, {} given", id + 1, func->fullName(), name, givenType ).str() ); } else { raise_typehint_error( folly::format( "Argument {} passed to {}() must be an instance of {}, {} given", id + 1, func->fullName(), name, givenType ).str() ); } } }