Type native_function_return_type(const php::Func* f, bool include_coercion_failures) { assert(f->nativeInfo); // If the function returns by ref, we can't say much about it. It can be a ref // or null. if (f->attrs & AttrReference) { return union_of(TRef, TInitNull); } // Infer the type from the HNI declaration auto t = [&]{ auto const hni = f->nativeInfo->returnType; return hni ? from_DataType(*hni) : TInitCell; }(); if (t.subtypeOf(BArr)) { if (f->retTypeConstraint.isVArray()) { assertx(!RuntimeOption::EvalHackArrDVArrs); t = TVArr; } else if (f->retTypeConstraint.isDArray()) { assertx(!RuntimeOption::EvalHackArrDVArrs); t = TDArr; } else if (f->retTypeConstraint.isArray()) { t = TPArr; } } // Non-simple types (ones that are represented by pointers) can always // possibly be null. if (t.subtypeOfAny(TStr, TArr, TVec, TDict, TKeyset, TObj, TRes)) { t |= TInitNull; } else { // Otherwise it should be a simple type or possibly everything. assert(t == TInitCell || t.subtypeOfAny(TBool, TInt, TDbl, TNull)); } if (include_coercion_failures) { // If parameter coercion fails, we can also get null or false depending on // the function. if (f->attrs & AttrParamCoerceModeNull) { t |= TInitNull; } if (f->attrs & AttrParamCoerceModeFalse) { t |= TFalse; } } return remove_uninit(t); }
void emitPrint(HTS& env) { auto const type = topC(env)->type(); if (!type.subtypeOfAny(Type::Int, Type::Bool, Type::Null, Type::Str)) { interpOne(env, Type::Int, 1); return; } auto const cell = popC(env); Opcode op; if (type <= Type::Str) { op = PrintStr; } else if (type <= Type::Int) { op = PrintInt; } else if (type <= Type::Bool) { op = PrintBool; } else { assert(type <= Type::Null); op = Nop; } // the print helpers decref their arg, so don't decref pop'ed value if (op != Nop) { gen(env, op, cell); } push(env, cns(env, 1)); }
void IRTranslator::translateLtGtOp(const NormalizedInstruction& i) { auto const op = i.op(); assert(op == Op::Lt || op == Op::Lte || op == Op::Gt || op == Op::Gte); auto leftType = m_hhbcTrans.topType(1, DataTypeGeneric); auto rightType = m_hhbcTrans.topType(0, DataTypeGeneric); if (!leftType.isKnownDataType() || !rightType.isKnownDataType()) { HHIR_UNIMPLEMENTED(LtGtOp-UnknownInput); } bool ok = leftType.subtypeOfAny (Type::Null, Type::Bool, Type::Int, Type::Dbl) && rightType.subtypeOfAny(Type::Null, Type::Bool, Type::Int, Type::Dbl); HHIR_UNIMPLEMENTED_WHEN(!ok, LtGtOp); switch (op) { case Op::Lt : HHIR_EMIT(Lt); case Op::Lte : HHIR_EMIT(Lte); case Op::Gt : HHIR_EMIT(Gt); case Op::Gte : HHIR_EMIT(Gte); default : HHIR_UNIMPLEMENTED(LtGtOp); } }
Type native_function_return_type(borrowed_ptr<const php::Func> f, bool include_coercion_failures) { assert(f->nativeInfo); // If the function returns by ref, we can't say much about it. It can be a ref // or null. if (f->attrs & AttrReference) { return union_of(TRef, TInitNull); } // Infer the type from the HNI declaration auto const hni = f->nativeInfo->returnType; auto t = hni ? from_DataType(*hni) : TInitCell; // Non-simple types (ones that are represented by pointers) can always // possibly be null. if (t.subtypeOfAny(TStr, TArr, TVec, TDict, TKeyset, TObj, TRes)) { t = union_of(t, TInitNull); } else { // Otherwise it should be a simple type or possibly everything. assert(t == TInitCell || t.subtypeOfAny(TBool, TInt, TDbl, TNull)); } if (include_coercion_failures) { // If parameter coercion fails, we can also get null or false depending on // the function. if (f->attrs & AttrParamCoerceModeNull) { t = union_of(t, TInitNull); } if (f->attrs & AttrParamCoerceModeFalse) { t = union_of(t, TFalse); } } return remove_uninit(t); }
void emitDecodeCufIter(IRGS& env, int32_t iterId, Offset relOffset) { auto const src = popC(env); auto const type = src->type(); if (type.subtypeOfAny(TArr, TStr, TObj)) { auto const res = gen( env, DecodeCufIter, TBool, IterId(iterId), src, fp(env) ); decRef(env, src); implCondJmp(env, bcOff(env) + relOffset, true, res); } else { decRef(env, src); jmpImpl(env, bcOff(env) + relOffset); } }
void IRTranslator::translateLtGtOp(const NormalizedInstruction& i) { auto const op = i.op(); assert(op == Op::Lt || op == Op::Lte || op == Op::Gt || op == Op::Gte); auto leftType = m_hhbcTrans.topType(1, DataTypeGeneric); auto rightType = m_hhbcTrans.topType(0, DataTypeGeneric); bool ok = equivDataTypes(leftType.toDataType(), rightType.toDataType()) && leftType.subtypeOfAny(Type::Null, Type::Bool, Type::Int); HHIR_UNIMPLEMENTED_WHEN(!ok, LtGtOp); switch (op) { case Op::Lt : HHIR_EMIT(Lt); case Op::Lte : HHIR_EMIT(Lte); case Op::Gt : HHIR_EMIT(Gt); case Op::Gte : HHIR_EMIT(Gte); default : HHIR_UNIMPLEMENTED(LtGtOp); } }
void emitDecodeCufIter(IRGS& env, int32_t iterId, Offset relOffset) { auto const src = popC(env); auto const type = src->type(); if (type.subtypeOfAny(Type::Arr, Type::Str, Type::Obj)) { auto const res = gen( env, DecodeCufIter, Type::Bool, IterId(iterId), src, fp(env) ); gen(env, DecRef, src); implCondJmp(env, bcOff(env) + relOffset, true, res); } else { gen(env, DecRef, src); jmpImpl(env, bcOff(env) + relOffset, instrJmpFlags(*env.currentNormalizedInstruction)); } }