Allow extension functions to match zend calling convention

Introducing `ZendParamMode` to as a idl flag. We are not consistent with zend on how they do their params for builtins. We cast to the expected data type. They do some checks, and if the checks don't pass they issue a warning and return (usually) `null`. This diff starts us down that path.

I'm introducing the param and using it in the places where we were emulating the calling convention in the `f_foo` functions. I'm going to follow up with converting as many as I can and then eventually this becomes the default. I also want this to be applied to php files in systemlib.

Many of the conversions are from https://github.com/php/php-src/blob/master/Zend/zend_API.c#L305
Esse commit está contido em:
Paul Tarjan
2013-06-05 11:06:51 -07:00
commit de Sara Golemon
commit 506f21c4b5
33 arquivos alterados com 361 adições e 99 exclusões
+6
Ver Arquivo
@@ -397,6 +397,12 @@ D:StkPtr = CastStk<T,offset> S0:StkPtr
Returns a new StkPtr that represents the same stack as S0, but with
the slot at offset (in cells) converted to type T.
D:StkPtr = CoerceStk<T,offset> S0:StkPtr -> L
Returns a new StkPtr that represents the same stack as S0, but with
the slot at offset (in cells) converted to type T. If the type conversion
can't be done then branches to label L.
CheckInit S0:Gen -> L
If S0's type is Uninit, branch to label L.
+1
Ver Arquivo
@@ -35,6 +35,7 @@ class ClassInfoHook;
class ClassInfo {
public:
enum Attribute { // class prop func method param
ZendParamMode = (1 << 0), // x x
IsRedeclared = (1 << 1), // x x
IsVolatile = (1 << 2), // x x
+18
Ver Arquivo
@@ -163,6 +163,24 @@ inline std::string tname(DataType t) {
}
}
inline const char* getDataTypeString(DataType t) {
switch (t) {
case KindOfUninit:
case KindOfNull: return "NULL";
case KindOfBoolean: return "boolean";
case KindOfInt64: return "integer";
case KindOfDouble: return "double";
case KindOfStaticString:
case KindOfString: return "string";
case KindOfArray: return "array";
case KindOfObject: return "object";
default:
assert(false);
break;
}
return "";
}
inline int getDataTypeIndex(DataType type) {
switch (type) {
case KindOfUninit : return 0;
+23 -1
Ver Arquivo
@@ -14,10 +14,10 @@
+----------------------------------------------------------------------+
*/
#include "hphp/runtime/base/runtime_error.h"
#include "hphp/runtime/base/execution_context.h"
#include "hphp/runtime/base/runtime_option.h"
#include "hphp/util/logger.h"
#include "hphp/runtime/base/runtime_error.h"
namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
@@ -196,6 +196,28 @@ void raise_notice(const char *fmt, ...) {
"HipHop Notice: ");
}
void raise_param_type_warning(
const char* func_name,
int param_num,
DataType expected_type,
DataType actual_type) {
// slice off fg1_
if (strncmp(func_name, "fg1_", 4) == 0) {
func_name += 4;
} else if (strncmp(func_name, "tg1_", 4) == 0) {
func_name += 4;
}
assert(param_num > 0);
raise_warning(
"%s() expects parameter %d to be %s, %s given",
func_name,
param_num,
getDataTypeString(expected_type),
getDataTypeString(actual_type)
);
}
///////////////////////////////////////////////////////////////////////////////
}
+6
Ver Arquivo
@@ -21,6 +21,7 @@
#include <string>
#include "hphp/util/base.h"
#include "hphp/runtime/base/datatype.h"
namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
@@ -73,6 +74,11 @@ void raise_notice(const std::string &msg);
void raise_notice(const char *fmt, ...) ATTRIBUTE_PRINTF(1, 2);
void raise_debugging(const std::string &msg);
void raise_debugging(const char *fmt, ...) ATTRIBUTE_PRINTF(1, 2);
void raise_param_type_warning(
const char* func_name,
int param_num,
DataType expected_type,
DataType actual_type);
template<bool Error>
void
+95 -16
Ver Arquivo
@@ -16,16 +16,21 @@
#include "hphp/runtime/base/complex_types.h"
#include "hphp/runtime/base/type_conversions.h"
#include "hphp/runtime/base/zend/zend_functions.h"
#include "hphp/system/systemlib.h"
namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
void tvCastToBooleanInPlace(TypedValue* tv) {
inline void tvUnboxIfNeeded(TypedValue *tv) {
if (tv->m_type == KindOfRef) {
tvUnbox(tv);
}
}
void tvCastToBooleanInPlace(TypedValue* tv) {
tvUnboxIfNeeded(tv);
bool b;
switch (tv->m_type) {
case KindOfUninit:
@@ -44,9 +49,7 @@ void tvCastToBooleanInPlace(TypedValue* tv) {
}
void tvCastToInt64InPlace(TypedValue* tv, int base /* = 10 */) {
if (tv->m_type == KindOfRef) {
tvUnbox(tv);
}
tvUnboxIfNeeded(tv);
int64_t i;
switch (tv->m_type) {
case KindOfUninit:
@@ -114,9 +117,7 @@ int64_t tvCastToInt64(TypedValue* tv, int base /* = 10 */) {
}
void tvCastToDoubleInPlace(TypedValue* tv) {
if (tv->m_type == KindOfRef) {
tvUnbox(tv);
}
tvUnboxIfNeeded(tv);
double d;
switch (tv->m_type) {
case KindOfUninit:
@@ -147,9 +148,7 @@ const StaticString
s_Array("Array");
void tvCastToStringInPlace(TypedValue* tv) {
if (tv->m_type == KindOfRef) {
tvUnbox(tv);
}
tvUnboxIfNeeded(tv);
StringData * s;
switch (tv->m_type) {
case KindOfUninit:
@@ -206,9 +205,7 @@ StringData* tvCastToString(TypedValue* tv) {
}
void tvCastToArrayInPlace(TypedValue* tv) {
if (tv->m_type == KindOfRef) {
tvUnbox(tv);
}
tvUnboxIfNeeded(tv);
ArrayData * a;
switch (tv->m_type) {
case KindOfUninit:
@@ -236,9 +233,7 @@ void tvCastToArrayInPlace(TypedValue* tv) {
}
void tvCastToObjectInPlace(TypedValue* tv) {
if (tv->m_type == KindOfRef) {
tvUnbox(tv);
}
tvUnboxIfNeeded(tv);
ObjectData* o;
switch (tv->m_type) {
case KindOfUninit:
@@ -270,6 +265,90 @@ void tvCastToObjectInPlace(TypedValue* tv) {
tv->m_data.pobj->incRefCount();
}
bool tvCoerceParamToBooleanInPlace(TypedValue* tv) {
tvUnboxIfNeeded(tv);
if (tv->m_type == KindOfArray || tv->m_type == KindOfObject) {
return false;
}
tvCastToBooleanInPlace(tv);
return true;
}
bool tvCanBeCoercedToNumber(TypedValue* tv) {
switch (tv->m_type) {
case KindOfStaticString:
case KindOfString:
StringData* s;
DataType type;
s = tv->m_data.pstr;
type = is_numeric_string(s->data(), s->size(), nullptr, nullptr);
if (type != KindOfDouble && type != KindOfInt64) {
return false;
}
break;
case KindOfArray:
case KindOfObject:
return false;
default:
break;
}
return true;
}
bool tvCoerceParamToInt64InPlace(TypedValue* tv) {
tvUnboxIfNeeded(tv);
if (!tvCanBeCoercedToNumber(tv)) {
return false;
}
tvCastToInt64InPlace(tv);
return true;
}
bool tvCoerceParamToDoubleInPlace(TypedValue* tv) {
tvUnboxIfNeeded(tv);
if (!tvCanBeCoercedToNumber(tv)) {
return false;
}
tvCastToDoubleInPlace(tv);
return true;
}
bool tvCoerceParamToStringInPlace(TypedValue* tv) {
tvUnboxIfNeeded(tv);
switch (tv->m_type) {
case KindOfArray:
return false;
case KindOfObject:
try {
tvAsVariant(tv) = tv->m_data.pobj->t___tostring();
return true;
} catch (BadTypeConversionException &e) {
}
return false;
default:
break;
}
tvCastToStringInPlace(tv);
return true;
}
bool tvCoerceParamToArrayInPlace(TypedValue* tv) {
tvUnboxIfNeeded(tv);
if (tv->m_type == KindOfArray) {
return true;
} else if (tv->m_type == KindOfObject) {
tvAsVariant(tv) = tv->m_data.pobj->o_toArray();
return true;
}
return false;
}
bool tvCoerceParamToObjectInPlace(TypedValue* tv) {
tvUnboxIfNeeded(tv);
return tv->m_type == KindOfObject;
}
bool tvIsPlausible(const TypedValue* tv) {
if (!tv) return false;
auto okPtr = [](void* ptr) {
+10
Ver Arquivo
@@ -446,6 +446,8 @@ inline bool tvIsString(const TypedValue* tv) {
return (tv->m_type & KindOfStringBit) != 0;
}
void tvUnboxIfNeeded(TypedValue* tv);
void tvCastToBooleanInPlace(TypedValue* tv);
void tvCastToInt64InPlace(TypedValue* tv, int base = 10);
int64_t tvCastToInt64(TypedValue* tv, int base = 10);
@@ -455,6 +457,14 @@ StringData* tvCastToString(TypedValue* tv);
void tvCastToArrayInPlace(TypedValue* tv);
void tvCastToObjectInPlace(TypedValue* tv);
bool tvCanBeCoercedToNumber(TypedValue* tv);
bool tvCoerceParamToBooleanInPlace(TypedValue* tv);
bool tvCoerceParamToInt64InPlace(TypedValue* tv);
bool tvCoerceParamToDoubleInPlace(TypedValue* tv);
bool tvCoerceParamToStringInPlace(TypedValue* tv);
bool tvCoerceParamToArrayInPlace(TypedValue* tv);
bool tvCoerceParamToObjectInPlace(TypedValue* tv);
typedef void(*RawDestructor)(void*);
extern const RawDestructor g_destructors[kDestrTableSize];
+2 -6
Ver Arquivo
@@ -301,12 +301,8 @@ Variant f_property_exists(CVarRef class_or_object, CStrRef property) {
return (propInd != kInvalidSlot);
}
Variant f_get_object_vars(CVarRef object) {
if (object.isObject()) {
return object.toObject()->o_toIterArray(ctxClassName());
}
raise_warning("get_object_vars() expects parameter 1 to be object");
return Variant(Variant::NullInit());
Variant f_get_object_vars(CObjRef object) {
return object->o_toIterArray(ctxClassName());
}
///////////////////////////////////////////////////////////////////////////////
+1 -1
Ver Arquivo
@@ -42,7 +42,7 @@ bool f_is_a(CVarRef class_or_object, CStrRef class_name, bool allow_string = fal
bool f_is_subclass_of(CVarRef class_or_object, CStrRef class_name, bool allow_string = true);
bool f_method_exists(CVarRef class_or_object, CStrRef method_name);
Variant f_property_exists(CVarRef class_or_object, CStrRef property);
Variant f_get_object_vars(CVarRef object);
Variant f_get_object_vars(CObjRef object);
///////////////////////////////////////////////////////////////////////////////
+3 -16
Ver Arquivo
@@ -244,23 +244,10 @@ static void add_registered_namespaces(Array &out, xmlNodePtr node,
static StaticString s_SimpleXMLElement("SimpleXMLElement");
Variant f_simplexml_import_dom(CVarRef node,
CVarRef class_name /* = "SimpleXMLElement" */) {
Variant f_simplexml_import_dom(CObjRef node,
CStrRef class_name /* = "SimpleXMLElement" */) {
if (!node.isObject()) {
raise_warning(
"simplexml_import_dom() expects parameter 1 to be object"
);
return uninit_null();
}
if (!class_name.isString()) {
raise_warning(
"simplexml_import_dom() expects parameter 2 to be string"
);
return uninit_null();
}
c_DOMNode *domnode = node.asCObjRef().getTyped<c_DOMNode>();
c_DOMNode *domnode = node.getTyped<c_DOMNode>();
xmlNodePtr nodep = domnode->m_node;
if (nodep) {
+2 -2
Ver Arquivo
@@ -25,8 +25,8 @@
namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
Variant f_simplexml_import_dom(CVarRef node,
CVarRef class_name = "SimpleXMLElement");
Variant f_simplexml_import_dom(CObjRef node,
CStrRef class_name = "SimpleXMLElement");
Variant f_simplexml_load_string(CStrRef data, CStrRef class_name = "SimpleXMLElement", int64_t options = 0, CStrRef ns = "", bool is_prefix = false);
Variant f_simplexml_load_file(CStrRef filename, CStrRef class_name = "SimpleXMLElement", int64_t options = 0, CStrRef ns = "", bool is_prefix = false);
Variant f_libxml_get_errors();
+1 -15
Ver Arquivo
@@ -25,21 +25,7 @@ namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
String f_gettype(CVarRef v) {
switch (v.getType()) {
case KindOfUninit:
case KindOfNull: return "NULL";
case KindOfBoolean: return "boolean";
case KindOfInt64: return "integer";
case KindOfDouble: return "double";
case KindOfStaticString:
case KindOfString: return "string";
case KindOfArray: return "array";
case KindOfObject: return "object";
default:
assert(false);
break;
}
return "";
return getDataTypeString(v.getType());
}
String f_get_resource_type(CObjRef handle) {
+46 -18
Ver Arquivo
@@ -27,6 +27,7 @@
#include "hphp/runtime/vm/jit/translator.h"
#include "hphp/runtime/vm/srckey.h"
#include "hphp/runtime/vm/member_operations.h"
#include "hphp/runtime/base/class_info.h"
#include "hphp/runtime/base/code_coverage.h"
#include "hphp/runtime/base/file_repository.h"
#include "hphp/runtime/base/base_includes.h"
@@ -5917,29 +5918,56 @@ inline void OPTBLD_INLINE VMExecutionContext::iopFCallBuiltin(PC& pc) {
}
TypedValue* args = m_stack.indTV(numArgs-1);
assert(numArgs == func->numParams());
bool zendParamMode = func->info()->attribute & ClassInfo::ZendParamMode;
TypedValue ret;
for (int i = 0; i < numNonDefault; i++) {
const Func::ParamInfo& pi = func->params()[i];
if (zendParamMode) {
#define CASE(kind) case KindOf ## kind : do { \
if (!tvCoerceParamTo ## kind ## InPlace(&args[-i])) { \
ret.m_type = KindOfNull; \
goto free_frame; \
} \
break; \
} while (0); break;
switch (pi.builtinType()) {
CASE(Boolean)
CASE(Int64)
CASE(Double)
CASE(String)
CASE(Array)
CASE(Object)
case KindOfUnknown:
break;
default:
not_reached();
}
#undef CASE
} else {
#define CASE(kind) case KindOf ## kind : do { \
tvCastTo ## kind ## InPlace(&args[-i]); break; \
} while (0); break;
switch (pi.builtinType()) {
CASE(Boolean)
CASE(Int64)
CASE(Double)
CASE(String)
CASE(Array)
CASE(Object)
case KindOfUnknown:
break;
default:
not_reached();
}
}
switch (pi.builtinType()) {
CASE(Boolean)
CASE(Int64)
CASE(Double)
CASE(String)
CASE(Array)
CASE(Object)
case KindOfUnknown:
break;
default:
not_reached();
}
#undef CASE
TypedValue ret;
}
}
ret.m_type = func->returnType();
switch (func->returnType()) {
case KindOfBoolean:
@@ -5968,10 +5996,10 @@ inline void OPTBLD_INLINE VMExecutionContext::iopFCallBuiltin(PC& pc) {
not_reached();
}
free_frame:
frame_free_args(args, numNonDefault);
m_stack.ndiscard(numArgs - 1);
memcpy(m_stack.top(), &ret, sizeof(TypedValue));
m_stack.ndiscard(numArgs);
memcpy(m_stack.allocTV(), &ret, sizeof(TypedValue));
}
bool VMExecutionContext::prepareArrayArgs(ActRec* ar,
+35
Ver Arquivo
@@ -3573,6 +3573,41 @@ void CodeGenerator::cgCastStk(IRInstruction *inst) {
kSyncPoint, args, DestType::None);
}
void CodeGenerator::cgCoerceStk(IRInstruction *inst) {
Type type = inst->typeParam();
SSATmp* sp = inst->src(0);
uint32_t offset = inst->extra<CoerceStk>()->offset;
Block* exit = inst->taken();
PhysReg spReg = m_regs[sp].reg();
ArgGroup args(m_regs);
args.addr(spReg, cellsToBytes(offset));
TCA tvCoerceHelper;
if (type.subtypeOf(Type::Bool)) {
tvCoerceHelper = (TCA)tvCoerceParamToBooleanInPlace;
} else if (type.subtypeOf(Type::Int)) {
// if casting to integer, pass 10 as the base for the conversion
args.imm(10);
tvCoerceHelper = (TCA)tvCoerceParamToInt64InPlace;
} else if (type.subtypeOf(Type::Dbl)) {
tvCoerceHelper = (TCA)tvCoerceParamToDoubleInPlace;
} else if (type.subtypeOf(Type::Arr)) {
tvCoerceHelper = (TCA)tvCoerceParamToArrayInPlace;
} else if (type.subtypeOf(Type::Str)) {
tvCoerceHelper = (TCA)tvCoerceParamToStringInPlace;
} else if (type.subtypeOf(Type::Obj)) {
tvCoerceHelper = (TCA)tvCoerceParamToObjectInPlace;
} else {
not_reached();
}
auto tmpReg = PhysReg(m_rScratch);
cgCallHelper(m_as, tvCoerceHelper, tmpReg, kSyncPoint, args);
m_as.testb(1, rbyte(tmpReg));
emitFwdJcc(m_as, CC_E, exit);
}
void CodeGenerator::cgCallBuiltin(IRInstruction* inst) {
SSATmp* f = inst->src(0);
auto args = inst->srcs().subpiece(2);
+1
Ver Arquivo
@@ -349,6 +349,7 @@ X(SpillFrame, ActRecInfo);
X(GuardStk, StackOffset);
X(CheckStk, StackOffset);
X(CastStk, StackOffset);
X(CoerceStk, StackOffset);
X(AssertStk, StackOffset);
X(AssertStkVal, StackOffset);
X(ReDefSP, StackOffset);
+18 -6
Ver Arquivo
@@ -2105,6 +2105,8 @@ void HhbcTranslator::emitFCallBuiltin(uint32_t numArgs,
// would be overkill.
spillStack();
bool zendParamMode = callee->info()->attribute & ClassInfo::ZendParamMode;
// Convert types if needed.
for (int i = 0; i < numNonDefault; i++) {
const Func::ParamInfo& pi = callee->params()[i];
@@ -2114,12 +2116,22 @@ void HhbcTranslator::emitFCallBuiltin(uint32_t numArgs,
case KindOfArray:
case KindOfObject:
case KindOfString:
gen(
CastStk,
Type::fromDataType(pi.builtinType(), KindOfInvalid),
StackOffset(numArgs - i - 1),
m_tb->sp()
);
if (zendParamMode) {
gen(
CoerceStk,
Type::fromDataType(pi.builtinType(), KindOfInvalid),
StackOffset(numArgs - i - 1),
getExitSlowTrace(),
m_tb->sp()
);
} else {
gen(
CastStk,
Type::fromDataType(pi.builtinType(), KindOfInvalid),
StackOffset(numArgs - i - 1),
m_tb->sp()
);
}
break;
case KindOfDouble: not_reached();
case KindOfUnknown: break;
+1
Ver Arquivo
@@ -169,6 +169,7 @@ O(GuardStk, D(StkPtr), S(StkPtr), E) \
O(CheckLoc, ND, S(FramePtr), E) \
O(CheckStk, D(StkPtr), S(StkPtr), E) \
O(CastStk, D(StkPtr), S(StkPtr), Mem|N|Er) \
O(CoerceStk, D(StkPtr), S(StkPtr), Mem|N|Er) \
O(AssertStk, D(StkPtr), S(StkPtr), E) \
O(AssertStkVal, D(StkPtr), S(StkPtr) S(Gen), E) \
O(CheckDefinedClsEq, ND, NA, E) \
+1
Ver Arquivo
@@ -466,6 +466,7 @@ void LinearScan::allocRegToInstruction(InstructionList::iterator it) {
opc == GuardStk ||
opc == AssertStk ||
opc == CastStk ||
opc == CoerceStk ||
opc == SideExitGuardStk ||
VectorEffects::supported(opc));
assignRegToTmp(&m_regs[int(rVmSp)], &dst, 0);
+13
Ver Arquivo
@@ -56,6 +56,8 @@ StackValueInfo getStackValue(SSATmp* sp, uint32_t index) {
// fallthrough
case CastStk:
// fallthrough
case CoerceStk:
// fallthrough
case CheckStk:
// fallthrough
case GuardStk:
@@ -343,6 +345,7 @@ SSATmp* Simplifier::simplify(IRInstruction* inst) {
case SpillStack: return simplifySpillStack(inst);
case Call: return simplifyCall(inst);
case CastStk: return simplifyCastStk(inst);
case CoerceStk: return simplifyCoerceStk(inst);
case AssertStk: return simplifyAssertStk(inst);
case LdStack: return simplifyLdStack(inst);
case LdStackAddr: return simplifyLdStackAddr(inst);
@@ -1611,6 +1614,16 @@ SSATmp* Simplifier::simplifyCastStk(IRInstruction* inst) {
return nullptr;
}
SSATmp* Simplifier::simplifyCoerceStk(IRInstruction* inst) {
auto const info = getStackValue(inst->src(0),
inst->extra<CoerceStk>()->offset);
if (info.knownType.subtypeOf(inst->typeParam())) {
// No need to cast---the type was as good or better.
inst->convertToNop();
}
return nullptr;
}
SSATmp* Simplifier::simplifyAssertStk(IRInstruction* inst) {
auto const info = getStackValue(inst->src(0),
inst->extra<AssertStk>()->offset);
+1
Ver Arquivo
@@ -123,6 +123,7 @@ private:
SSATmp* simplifyQueryJmp(IRInstruction*);
SSATmp* simplifyExitOnVarEnv(IRInstruction*);
SSATmp* simplifyCastStk(IRInstruction*);
SSATmp* simplifyCoerceStk(IRInstruction*);
SSATmp* simplifyAssertStk(IRInstruction*);
SSATmp* simplifyLdStack(IRInstruction*);
SSATmp* simplifyLdStackAddr(IRInstruction*);
+1
Ver Arquivo
@@ -213,6 +213,7 @@ void TraceBuilder::updateTrackedState(IRInstruction* inst) {
case AssertStk:
case AssertStkVal:
case CastStk:
case CoerceStk:
case CheckStk:
case GuardStk:
case ExceptionBarrier:
+1
Ver Arquivo
@@ -116,6 +116,7 @@ function get_php_name($type, $null = 'mixed') {
// flags
// ClassInfo attributes, and these numbers need to be consistent with them!
define('ZendParamMode', 1 << 0);
define('IsAbstract', 1 << 4);
define('IsFinal', 1 << 5);
define('IsPublic', 1 << 6);
+3 -2
Ver Arquivo
@@ -345,7 +345,8 @@
"name": "get_object_vars",
"desc": "Gets the accessible non-static properties of the given object according to scope.",
"flags": [
"HasDocComment"
"HasDocComment",
"ZendParamMode"
],
"return": {
"type": "Variant",
@@ -354,7 +355,7 @@
"args": [
{
"name": "object",
"type": "Variant",
"type": "Object",
"desc": "An object instance."
}
]
+2 -1
Ver Arquivo
@@ -1756,7 +1756,8 @@
"name": "pathinfo",
"desc": "pathinfo() returns an associative array containing information about path.",
"flags": [
"HasDocComment"
"HasDocComment",
"ZendParamMode"
],
"return": {
"type": "Variant",
+4 -3
Ver Arquivo
@@ -87,7 +87,8 @@
"name": "simplexml_import_dom",
"desc": "This function takes a node of a DOM document and makes it into a SimpleXML node. This new object can then be used as a native SimpleXML element.",
"flags": [
"HasDocComment"
"HasDocComment",
"ZendParamMode"
],
"return": {
"type": "Variant",
@@ -96,12 +97,12 @@
"args": [
{
"name": "node",
"type": "Variant",
"type": "Object",
"desc": "A DOM Element node"
},
{
"name": "class_name",
"type": "Variant",
"type": "String",
"value": "\"SimpleXMLElement\"",
"desc": "You may use this optional parameter so that simplexml_load_file() will return an object of the specified class. That class should extend the SimpleXMLElement class."
}
+9 -4
Ver Arquivo
@@ -1,6 +1,11 @@
<?php
var_dump(get_object_vars(false));
var_dump(get_object_vars(true));
var_dump(get_object_vars('hello'));
var_dump(get_object_vars(5));
class A {};
function main() {
var_dump(get_object_vars(new A));
var_dump(get_object_vars(false));
var_dump(get_object_vars(true));
var_dump(get_object_vars('hello'));
var_dump(get_object_vars(5));
}
main();
@@ -1,3 +1,5 @@
array(0) {
}
NULL
NULL
NULL
+16
Ver Arquivo
@@ -0,0 +1,16 @@
<?php
class A {
private $count = 0;
public function __toString() {
return (string) $this->count++;
}
}
function main() {
$a = new A;
var_dump((string)$a);
var_dump((string)$a);
var_dump((string) (new A));
}
main();
@@ -0,0 +1,3 @@
string(1) "0"
string(1) "1"
string(1) "0"
+1 -1
Ver Arquivo
@@ -173,7 +173,7 @@ static void outputConstants(const char *outputfn,
HipHopSpecific|VariableArguments|\
RefVariableArguments|MixedVariableArguments|\
NeedsActRec|FunctionIsFoldable|\
NoInjection|NoEffect|HasOptFunction)
NoInjection|NoEffect|HasOptFunction|ZendParamMode)
static void writeFunction(std::ostream& out, const PhpFunc& func) {
auto flags = (func.flags() & FUNC_FLAG_MASK) | IsSystem | IsNothing;
+12 -1
Ver Arquivo
@@ -160,7 +160,18 @@ void emitCast(const PhpParam& param, int32_t index, std::ostream& out,
ind -= 2;
}
if (param.kindOf() != KindOfAny) {
if (param.getParamMode() == ParamMode::Zend) {
out << ind << "if (!"
<< "tvCoerceParamTo" << kindOfString(param.kindOf())
<< "InPlace(args-" << index << ")) {\n"
<< ind << " raise_param_type_warning(__func__, " << index << " + 1, "
<< "KindOf" << kindOfString(param.kindOf()) << ", "
<< "(args-" << index << ")->m_type);\n"
<< ind << " rv->m_type = KindOfUninit;\n"
<< ind << " return;\n"
<< ind << "}\n";
} else if (param.kindOf() != KindOfAny) {
out << ind << "tvCastTo" << kindOfString(param.kindOf())
<< "InPlace(args-" << index << ");\n";
}
+12 -5
Ver Arquivo
@@ -77,6 +77,7 @@ static const std::unordered_map<int, fbstring> g_phpTypeMap =
static const std::unordered_map<fbstring, FuncFlags> g_flagsMap =
{
{"ZendParamMode", ZendParamMode},
{"IsAbstract", IsAbstract},
{"IsFinal", IsFinal},
{"IsPublic", IsPublic},
@@ -481,10 +482,12 @@ PhpConst::PhpConst(const folly::dynamic& cns,
// PhpParam
PhpParam::PhpParam(const folly::dynamic& param,
bool isMagicMethod /*= false */) :
bool isMagicMethod /*= false */,
ParamMode paramMode /*= CoerceAndCall */) :
m_name(param["name"].asString()),
m_param(param),
m_desc(getFollyDynamicDefaultString(param, "desc", "")) {
m_desc(getFollyDynamicDefaultString(param, "desc", "")),
m_paramMode(paramMode) {
if (isMagicMethod) {
m_kindOf = KindOfAny;
m_cppType = "HPHP::Variant";
@@ -576,8 +579,14 @@ PhpFunc::PhpFunc(const folly::dynamic& d,
}
bool magic = isMagicMethod();
m_flags = parseFlags(m_func["flags"]);
ParamMode paramMode = (m_flags & ZendParamMode) ?
ParamMode::Zend : ParamMode::CoerceAndCall;
for (auto &p : args->second) {
PhpParam param(p, magic);
PhpParam param(p, magic, paramMode);
m_params.push_back(param);
if (!param.hasDefault()) {
++m_minNumParams;
@@ -586,8 +595,6 @@ PhpFunc::PhpFunc(const folly::dynamic& d,
++m_numTypeChecks;
}
}
m_flags = parseFlags(m_func["flags"]);
}
fbstring PhpFunc::getCppSig() const {
+11 -1
Ver Arquivo
@@ -31,6 +31,7 @@ namespace HPHP { namespace IDL {
/////////////////////////////////////////////////////////////////////////////
enum FuncFlags {
ZendParamMode = (1 << 0),
IsAbstract = (1 << 4),
IsFinal = (1 << 5),
IsPublic = (1 << 6),
@@ -123,6 +124,11 @@ static inline fbstring strtolower(const fbstring& str) {
fbstring phpSerialize(const folly::dynamic& d);
enum class ParamMode {
CoerceAndCall,
Zend
};
class PhpConst {
public:
explicit PhpConst(const folly::dynamic& cns, fbstring cls = "");
@@ -160,7 +166,8 @@ class PhpConst {
class PhpParam {
public:
explicit PhpParam(const folly::dynamic& param, bool isMagicMethod = false);
explicit PhpParam(const folly::dynamic& param, bool isMagicMethod = false,
ParamMode paramMode = ParamMode::CoerceAndCall);
fbstring name() const { return m_name; }
fbstring getDesc() const { return m_desc; }
@@ -196,6 +203,8 @@ class PhpParam {
bool isIndirectPass() const { return isKindOfIndirect(kindOf()); }
ParamMode getParamMode() const { return m_paramMode; }
private:
fbstring m_name;
folly::dynamic m_param;
@@ -203,6 +212,7 @@ class PhpParam {
DataType m_kindOf;
fbstring m_cppType;
fbstring m_phpType;
ParamMode m_paramMode;
};
class PhpFunc {