static Variant xml_call_handler(XmlParser *parser, CVarRef handler, CArrRef args) { if (parser && handler) { Variant retval; if (handler.isString()) { if (!parser->object.isObject()) { retval = invoke(handler.toString().c_str(), args, -1); } else { retval = parser->object.toObject()-> o_invoke(handler.toString(), args, -1); } } else if (handler.isArray() && handler.getArrayData()->size() == 2 && (handler[0].isString() || handler[0].isObject()) && handler[1].isString()) { if (handler[0].isString()) { // static method retval = ObjectData::os_invoke(handler[0].toString(), handler[1].toString(), args, -1); } else { // instance method retval = handler[0].toObject()->o_invoke(handler[1].toString(), args, -1); } } else { raise_warning("Handler is invalid"); } return retval; } return null; }
bool ArrayData::hasInternalReference(PointerSet &vars) const { if (supportValueRef()) { for (ArrayIter iter(this); iter; ++iter) { CVarRef var = iter.secondRef(); if (var.isReferenced()) { Variant *pvar = var.getVariantData(); if (vars.find(pvar) != vars.end()) { return true; } vars.insert(pvar); } if (var.isObject()) { ObjectData *pobj = var.getObjectData(); if (vars.find(pobj) != vars.end()) { return true; } vars.insert(pobj); if (pobj->o_toArray().get()->hasInternalReference(vars)) { return true; } } else if (var.isArray() && var.getArrayData()->hasInternalReference(vars)) { return true; } } } return false; }
bool ArrayData::hasInternalReference(PointerSet &vars, bool ds /* = false */) const { if (isSharedMap()) return false; for (ArrayIter iter(this); iter; ++iter) { CVarRef var = iter.secondRef(); if (var.isReferenced()) { Variant *pvar = var.getRefData(); if (vars.find(pvar) != vars.end()) { return true; } vars.insert(pvar); } if (var.isObject()) { ObjectData *pobj = var.getObjectData(); if (vars.find(pobj) != vars.end()) { return true; } vars.insert(pobj); if (ds && pobj->o_instanceof("Serializable")) { return true; } if (pobj->hasInternalReference(vars, ds)) { return true; } } else if (var.isArray() && var.getArrayData()->hasInternalReference(vars, ds)) { return true; } } return false; }
int64_t f_count(CVarRef var, bool recursive /* = false */) { switch (var.getType()) { case KindOfUninit: case KindOfNull: return 0; case KindOfObject: { Object obj = var.toObject(); if (obj->isCollection()) { return obj->getCollectionSize(); } if (obj.instanceof(SystemLib::s_CountableClass)) { return obj->o_invoke_few_args(s_count, 0).toInt64(); } } break; case KindOfArray: if (recursive) { CArrRef arr_var = var.toCArrRef(); return php_count_recursive(arr_var); } return var.getArrayData()->size(); default: break; } return 1; }
static void url_encode_array(StringBuffer &ret, CVarRef varr, std::set<void*> &seen_arrs, CStrRef num_prefix, CStrRef key_prefix, CStrRef key_suffix, CStrRef arg_sep) { void *id = varr.is(KindOfArray) ? (void*)varr.getArrayData() : (void*)varr.getObjectData(); if (!seen_arrs.insert(id).second) { return; // recursive } Array arr = varr.toArray(); for (ArrayIter iter(arr); iter; ++iter) { Variant data = iter.second(); if (data.isNull() || data.isResource()) continue; String key = iter.first(); bool numeric = key.isNumeric(); if (data.is(KindOfArray) || data.is(KindOfObject)) { String encoded; if (numeric) { encoded = key; } else { encoded = StringUtil::UrlEncode(key); } StringBuffer new_prefix(key_prefix.size() + num_prefix.size() + encoded.size() + key_suffix.size() + 4); new_prefix += key_prefix; if (numeric) new_prefix += num_prefix; new_prefix += encoded; new_prefix += key_suffix; new_prefix += "%5B"; url_encode_array(ret, data, seen_arrs, String(), new_prefix.detach(), String("%5D", AttachLiteral), arg_sep); } else { if (!ret.empty()) { ret += arg_sep; } ret += key_prefix; if (numeric) { ret += num_prefix; ret += key; } else { ret += StringUtil::UrlEncode(key); } ret += key_suffix; ret += "="; if (data.isInteger() || data.is(KindOfBoolean)) { ret += String(data.toInt64()); } else if (data.is(KindOfDouble)) { ret += String(data.toDouble()); } else { ret += StringUtil::UrlEncode(data.toString()); } } } }
bool same(CVarRef v1, CArrRef v2) { bool null1 = v1.isNull(); bool null2 = v2.isNull(); if (null1 && null2) return true; if (null1 || null2) return false; if (!v1.isArray()) return false; auto const ad = v1.getArrayData(); return v2->equal(ad, true); }
static void xml_set_handler(Variant * handler, CVarRef data) { if (same(data, false) || data.isString() || (data.isArray() && data.getArrayData()->size() == 2 && (data[0].isString() || data[0].isObject()) && data[1].isString())) { *handler = data; } else { raise_warning("Handler is invalid"); } }
static void compact(HPHP::VM::VarEnv* v, Array &ret, CVarRef var) { if (var.isArray()) { for (ArrayIter iter(var.getArrayData()); iter; ++iter) { compact(v, ret, iter.second()); } } else { String varname = var.toString(); if (!varname.empty() && v->lookup(varname.get()) != NULL) { ret.set(varname, *reinterpret_cast<Variant*>(v->lookup(varname.get()))); } } }
Array Array::intersect(CVarRef array, bool by_key, bool by_value, PFUNC_CMP key_cmp_function /* = NULL */, const void *key_data /* = NULL */, PFUNC_CMP value_cmp_function /* = NULL */, const void *value_data /* = NULL */) const { if (!array.isArray()) { throw_bad_array_exception(); return Array(); } return diffImpl(array.getArrayData(), by_key, by_value, true, key_cmp_function, key_data, value_cmp_function, value_data); }
int hphp_ffi_exportVariant(CVarRef v, void** result) { switch (v.getType()) { case KindOfNull: return 0; case KindOfBoolean: *result = (void*)v.toBoolean(); return 1; case KindOfByte: case KindOfInt16: case KindOfInt32: case KindOfInt64: { *result = (void*)v.toInt64(); return 2; } case KindOfDouble: { union { double d; void* p; } u; u.d = v.toDouble(); *result = u.p; return 3; } case LiteralString: *result = (void*)v.getLiteralString(); return 4; case KindOfString: { StringData *sd = v.getStringData(); sd->incRefCount(); *result = (void*)sd; return 5; } case KindOfArray: { ArrayData *ad = v.getArrayData(); ad->incRefCount(); *result = (void*)ad; return 6; } case KindOfObject: { ObjectData *od = v.getObjectData(); od->incRefCount(); *result = (void*)od; return 7; } default: ASSERT(false); return 0; } }
String f_serialize(CVarRef value) { switch (value.getType()) { case KindOfUninit: case KindOfNull: return "N;"; case KindOfBoolean: return value.getBoolean() ? "b:1;" : "b:0;"; case KindOfInt64: { StringBuffer sb; sb.append("i:"); sb.append(value.getInt64()); sb.append(';'); return sb.detach(); } case KindOfStaticString: case KindOfString: { StringData *str = value.getStringData(); StringBuffer sb; sb.append("s:"); sb.append(str->size()); sb.append(":\""); sb.append(str->data(), str->size()); sb.append("\";"); return sb.detach(); } case KindOfArray: { ArrayData *arr = value.getArrayData(); if (arr->empty()) return "a:0:{}"; // fall-through } case KindOfObject: case KindOfResource: case KindOfDouble: { VariableSerializer vs(VariableSerializer::Type::Serialize); return vs.serialize(value, true); } default: assert(false); break; } return ""; }
static Variant xml_call_handler(XmlParser *parser, CVarRef handler, CArrRef args) { if (parser && handler) { Variant retval; if (handler.isString()) { if (!parser->object.isObject()) { retval = invoke(handler.toString().c_str(), args, -1); } else { retval = parser->object.toObject()-> o_invoke(handler.toString(), args); } } else if (handler.isArray() && handler.getArrayData()->size() == 2 && (handler[0].isString() || handler[0].isObject()) && handler[1].isString()) { vm_call_user_func(handler, args); } else { raise_warning("Handler is invalid"); } return retval; } return uninit_null(); }
bool ArrayData::hasInternalReference(PointerSet &vars, bool ds /* = false */) const { if (isSharedMap()) return false; for (ArrayIter iter(this); iter; ++iter) { CVarRef var = iter.secondRef(); if (var.isReferenced()) { Variant *pvar = var.getVariantData(); if (vars.find(pvar) != vars.end()) { return true; } vars.insert(pvar); } if (var.isObject()) { ObjectData *pobj = var.getObjectData(); if (vars.find(pobj) != vars.end()) { return true; } vars.insert(pobj); if (pobj->o_instanceof("Serializable")) { if (ds) { // We want to detect Serializable object as well return true; } else { // Serializable object does not have internal reference issue return false; } } if (pobj->o_toArray().get()->hasInternalReference(vars, ds)) { return true; } } else if (var.isArray() && var.getArrayData()->hasInternalReference(vars, ds)) { return true; } } return false; }
SharedVariant::SharedVariant(CVarRef source, bool serialized, bool inner /* = false */, bool unserializeObj /* = false */) : m_shouldCache(false), m_flags(0) { assert(!serialized || source.isString()); m_count = 1; m_type = source.getType(); switch (m_type) { case KindOfBoolean: { m_data.num = source.toBoolean(); break; } case KindOfInt64: { m_type = KindOfInt64; m_data.num = source.toInt64(); break; } case KindOfDouble: { m_data.dbl = source.toDouble(); break; } case KindOfStaticString: { if (serialized) goto StringCase; m_data.str = source.getStringData(); break; } StringCase: case KindOfString: { String s = source.toString(); if (serialized) { m_type = KindOfObject; // It is priming, and there might not be the right class definitions // for unserialization. s = apc_reserialize(s); } StringData* st = StringData::LookupStaticString(s.get()); if (st) { m_data.str = st; m_type = KindOfStaticString; break; } m_data.str = s->copy(true); break; } case KindOfArray: { ArrayData *arr = source.getArrayData(); if (!inner) { // only need to call hasInternalReference() on the toplevel array PointerSet seen; if (arr->hasInternalReference(seen)) { setSerializedArray(); m_shouldCache = true; String s = apc_serialize(source); m_data.str = StringData::MakeMalloced(s.data(), s.size()); break; } } if (arr->isVectorData()) { setIsVector(); m_data.vec = new (arr->size()) VectorData(); for (ArrayIter it(arr); !it.end(); it.next()) { SharedVariant* val = Create(it.secondRef(), false, true, unserializeObj); if (val->m_shouldCache) m_shouldCache = true; m_data.vec->vals()[m_data.vec->m_size++] = val; } } else { m_data.map = ImmutableMap::Create(arr, unserializeObj, m_shouldCache); } break; } case KindOfUninit: case KindOfNull: { break; } case KindOfResource: { // TODO Task #2661075: Here and elsewhere in the runtime, we convert // Resources to the empty array during various serialization operations, // which does not match Zend behavior. We should fix this. m_type = KindOfArray; setIsVector(); m_data.vec = new (0) VectorData(); break; } default: { assert(source.isObject()); m_shouldCache = true; if (unserializeObj) { // This assumes hasInternalReference(seen, true) is false ImmutableObj* obj = new ImmutableObj(source.getObjectData()); m_data.obj = obj; setIsObj(); } else { String s = apc_serialize(source); m_data.str = StringData::MakeMalloced(s.data(), s.size()); } break; } } assert(m_type != KindOfResource); }
ThreadSharedVariant::ThreadSharedVariant(CVarRef source, bool serialized, bool inner /* = false */) { ASSERT(!serialized || source.isString()); setOwner(); m_ref = 1; switch (source.getType()) { case KindOfBoolean: { m_type = KindOfBoolean; m_data.num = source.toBoolean(); break; } case KindOfByte: case KindOfInt16: case KindOfInt32: case KindOfInt64: { m_type = KindOfInt64; m_data.num = source.toInt64(); break; } case KindOfDouble: { m_type = KindOfDouble; m_data.dbl = source.toDouble(); break; } case KindOfStaticString: case KindOfString: { String s = source.toString(); m_type = serialized ? KindOfObject : KindOfString; if (serialized) { // It is priming, and there might not be the right class definitions // for unserialization. s = apc_reserialize(s); } m_data.str = s->copy(true); break; } case KindOfArray: { m_type = KindOfArray; ArrayData *arr = source.getArrayData(); if (!inner) { // only need to call hasInternalReference() on the toplevel array PointerSet seen; if (arr->hasInternalReference(seen)) { setSerializedArray(); setShouldCache(); String s = apc_serialize(source); m_data.str = new StringData(s.data(), s.size(), CopyString); break; } } size_t size = arr->size(); if (arr->isVectorData()) { setIsVector(); m_data.vec = new VectorData(size); uint i = 0; for (ArrayIter it(arr); !it.end(); it.next(), i++) { ThreadSharedVariant* val = createAnother(it.second(), false, true); if (val->shouldCache()) setShouldCache(); m_data.vec->vals[i] = val; } } else { m_data.map = new ImmutableMap(size); uint i = 0; for (ArrayIter it(arr); !it.end(); it.next(), i++) { ThreadSharedVariant* key = createAnother(it.first(), false); ThreadSharedVariant* val = createAnother(it.second(), false, true); if (val->shouldCache()) setShouldCache(); m_data.map->add(key, val); } } break; } default: { m_type = KindOfObject; setShouldCache(); String s = apc_serialize(source); m_data.str = new StringData(s.data(), s.size(), CopyString); break; } } }
static void url_encode_array(StringBuffer &ret, CVarRef varr, std::set<void*> &seen_arrs, const String& num_prefix, const String& key_prefix, const String& key_suffix, const String& arg_sep) { void *id = varr.is(KindOfArray) ? (void*)varr.getArrayData() : (void*)varr.getObjectData(); if (!seen_arrs.insert(id).second) { return; // recursive } Array arr; if (varr.is(KindOfObject)) { Object o = varr.toObject(); arr = (o.objectForCall()->isCollection()) ? varr.toArray() : f_get_object_vars(o).toArray(); } else { arr = varr.toArray(); } for (ArrayIter iter(arr); iter; ++iter) { Variant data = iter.second(); if (data.isNull() || data.isResource()) continue; String key = iter.first(); bool numeric = key.isNumeric(); if (data.is(KindOfArray) || data.is(KindOfObject)) { String encoded; if (numeric) { encoded = key; } else { encoded = StringUtil::UrlEncode(key); } StringBuffer new_prefix(key_prefix.size() + num_prefix.size() + encoded.size() + key_suffix.size() + 4); new_prefix.append(key_prefix); if (numeric) new_prefix.append(num_prefix); new_prefix.append(encoded); new_prefix.append(key_suffix); new_prefix.append("%5B"); url_encode_array(ret, data, seen_arrs, String(), new_prefix.detach(), String("%5D", CopyString), arg_sep); } else { if (!ret.empty()) { ret.append(arg_sep); } ret.append(key_prefix); if (numeric) { ret.append(num_prefix); ret.append(key); } else { ret.append(StringUtil::UrlEncode(key)); } ret.append(key_suffix); ret.append("="); if (data.isInteger() || data.is(KindOfBoolean)) { ret.append(String(data.toInt64())); } else if (data.is(KindOfDouble)) { ret.append(String(data.toDouble())); } else { ret.append(StringUtil::UrlEncode(data.toString())); } } } }
Array Array::operator+(CVarRef var) const { if (var.getType() != KindOfArray) { throw BadArrayMergeException(); } return operator+(var.getArrayData()); }
SharedVariant::SharedVariant(CVarRef source, bool serialized, bool inner /* = false */, bool unserializeObj /* = false */) : m_count (1), m_shouldCache(false), m_flags(0){ ASSERT(!serialized || source.isString()); m_type = source.getType(); switch (m_type) { case KindOfBoolean: { m_data.num = source.toBoolean(); break; } case KindOfByte: case KindOfInt16: case KindOfInt32: case KindOfInt64: { m_type = KindOfInt64; m_data.num = source.toInt64(); break; } case KindOfDouble: { m_data.dbl = source.toDouble(); break; } case KindOfStaticString: case KindOfString: { String s = source.toString(); if (serialized) { m_type = KindOfObject; // It is priming, and there might not be the right class definitions // for unserialization. s = apc_reserialize(s); } m_data.str = s->copy(true); break; } case KindOfArray: { ArrayData *arr = source.getArrayData(); if (!inner) { // only need to call hasInternalReference() on the toplevel array PointerSet seen; if (arr->hasInternalReference(seen)) { setSerializedArray(); m_shouldCache = true; String s = apc_serialize(source); m_data.str = new StringData(s.data(), s.size(), CopyString); break; } } size_t size = arr->size(); if (arr->isVectorData()) { setIsVector(); m_data.vec = new VectorData(size); uint i = 0; for (ArrayIter it(arr); !it.end(); it.next(), i++) { SharedVariant* val = Create(it.secondRef(), false, true, unserializeObj); if (val->m_shouldCache) m_shouldCache = true; m_data.vec->vals[i] = val; } } else { m_data.map = new ImmutableMap(size); for (ArrayIter it(arr); !it.end(); it.next()) { SharedVariant* key = Create(it.first(), false, true, unserializeObj); SharedVariant* val = Create(it.secondRef(), false, true, unserializeObj); if (val->m_shouldCache) m_shouldCache = true; m_data.map->add(key, val); } } break; } case KindOfNull: { m_data.num = 0; break; } default: { ASSERT(source.isObject()); m_shouldCache = true; if (unserializeObj) { // This assumes hasInternalReference(seen, true) is false ImmutableObj* obj = new ImmutableObj(source.getObjectData()); m_data.obj = obj; setIsObj(); } else { String s = apc_serialize(source); m_data.str = new StringData(s.data(), s.size(), CopyString); } break; } } }