void StandardExtension::initMisc() { HHVM_FALIAS(HH\\server_warmup_status, server_warmup_status); HHVM_FE(connection_aborted); HHVM_FE(connection_status); HHVM_FE(connection_timeout); HHVM_FE(constant); HHVM_FE(define); HHVM_FE(defined); HHVM_FE(ignore_user_abort); HHVM_FE(pack); HHVM_FE(sleep); HHVM_FE(usleep); HHVM_FE(time_nanosleep); HHVM_FE(time_sleep_until); HHVM_FE(uniqid); HHVM_FE(unpack); HHVM_FE(sys_getloadavg); HHVM_FE(token_get_all); HHVM_FE(token_name); HHVM_FE(hphp_to_string); HHVM_FALIAS(__SystemLib\\max2, SystemLib_max2); HHVM_FALIAS(__SystemLib\\min2, SystemLib_min2); Native::registerConstant<KindOfDouble>(makeStaticString("INF"), k_INF); Native::registerConstant<KindOfDouble>(makeStaticString("NAN"), k_NAN); Native::registerConstant<KindOfInt64>( makeStaticString("PHP_MAXPATHLEN"), MAXPATHLEN); Native::registerConstant<KindOfBoolean>(makeStaticString("PHP_DEBUG"), #if DEBUG true #else false #endif ); bindTokenConstants(); Native::registerConstant<KindOfInt64>(s_T_PAAMAYIM_NEKUDOTAYIM.get(), get_user_token_id(T_DOUBLE_COLON)); HHVM_RC_STR(PHP_BINARY, current_executable_path()); HHVM_RC_STR(PHP_BINDIR, current_executable_directory()); HHVM_RC_STR(PHP_OS, HHVM_FN(php_uname)("s").toString().toCppString()); HHVM_RC_STR(PHP_SAPI, RuntimeOption::ExecutionMode); HHVM_RC_INT(PHP_INT_SIZE, sizeof(int64_t)); HHVM_RC_INT(PHP_INT_MIN, k_PHP_INT_MIN); HHVM_RC_INT(PHP_INT_MAX, k_PHP_INT_MAX); HHVM_RC_INT_SAME(PHP_MAJOR_VERSION); HHVM_RC_INT_SAME(PHP_MINOR_VERSION); HHVM_RC_INT_SAME(PHP_RELEASE_VERSION); HHVM_RC_STR_SAME(PHP_EXTRA_VERSION); HHVM_RC_STR_SAME(PHP_VERSION); HHVM_RC_INT_SAME(PHP_VERSION_ID); // FIXME: These values are hardcoded from their previous IDL values // Grab their correct values from the system as appropriate HHVM_RC_STR(PHP_EOL, "\n"); HHVM_RC_STR(PHP_CONFIG_FILE_PATH, ""); HHVM_RC_STR(PHP_CONFIG_FILE_SCAN_DIR, ""); HHVM_RC_STR(PHP_DATADIR, ""); HHVM_RC_STR(PHP_EXTENSION_DIR, ""); HHVM_RC_STR(PHP_LIBDIR, ""); HHVM_RC_STR(PHP_LOCALSTATEDIR, ""); HHVM_RC_STR(PHP_PREFIX, ""); HHVM_RC_STR(PHP_SHLIB_SUFFIX, "so"); HHVM_RC_STR(PHP_SYSCONFDIR, ""); HHVM_RC_STR(PEAR_EXTENSION_DIR, ""); HHVM_RC_STR(PEAR_INSTALL_DIR, ""); loadSystemlib("std_misc"); }
namespace HPHP { namespace HHBBC { ////////////////////////////////////////////////////////////////////// const StaticString s_reified_generics_var("0ReifiedGenerics"); ////////////////////////////////////////////////////////////////////// uint32_t closure_num_use_vars(const php::Func* f) { // Properties on the closure object are use vars. return f->cls->properties.size(); } bool is_pseudomain(const php::Func* f) { return f->unit->pseudomain.get() == f; } bool is_methcaller(const StringData* name) { return Func::isMethCallerName(name); } bool is_volatile_local(const php::Func* func, LocalId lid) { auto const& l = func->locals[lid]; if (!l.name) return false; // Named pseudomain locals are bound to $GLOBALS. if (is_pseudomain(func)) return true; return (RuntimeOption::EnableArgsInBacktraces && l.name->same(s_reified_generics_var.get())) || l.name->same(s_86metadata.get()); } SString memoize_impl_name(const php::Func* func) { always_assert(func->isMemoizeWrapper); return Func::genMemoizeImplName(func->name); } bool check_nargs_in_range(const php::Func* func, uint32_t nArgs) { while (nArgs < func->dvEntries.size()) { if (func->dvEntries[nArgs++] == NoBlockId) return false; } return true; } ////////////////////////////////////////////////////////////////////// namespace { using ExnNode = php::ExnNode; void copy_into(php::FuncBase* dst, const php::FuncBase& other) { hphp_fast_map<ExnNode*, ExnNode*> processed; BlockId delta = dst->blocks.size(); always_assert(!dst->exnNodes.size() || !other.exnNodes.size()); dst->exnNodes.reserve(dst->exnNodes.size() + other.exnNodes.size()); for (auto en : other.exnNodes) { en.region.catchEntry += delta; dst->exnNodes.push_back(std::move(en)); } for (auto theirs : other.blocks) { if (delta) { auto const ours = theirs.mutate(); if (ours->fallthrough != NoBlockId) ours->fallthrough += delta; if (ours->throwExit != NoBlockId) ours->throwExit += delta; for (auto& bc : ours->hhbcs) { // When merging functions (used for 86xints) we have to drop // the src info, because it might reference a different unit // (and as a generated function, the src info isn't very // meaningful anyway). bc.srcLoc = -1; bc.forEachTarget([&] (BlockId& b) { b += delta; }); } } dst->blocks.push_back(std::move(theirs)); } } ////////////////////////////////////////////////////////////////////// } ////////////////////////////////////////////////////////////////////// bool append_func(php::Func* dst, const php::Func& src) { if (src.numIters || src.locals.size()) return false; if (src.exnNodes.size() && dst->exnNodes.size()) return false; bool ok = false; for (auto& b : dst->blocks) { if (b->hhbcs.back().op != Op::RetC) continue; auto const blk = b.mutate(); blk->hhbcs.back() = bc::PopC {}; blk->fallthrough = dst->blocks.size(); ok = true; } if (!ok) return false; copy_into(dst, src); return true; } php::FuncBase::FuncBase(const FuncBase& other) { copy_into(this, other); assertx(!other.nativeInfo); } ////////////////////////////////////////////////////////////////////// }}
void moduleInit() override { SOCK_CONST(AF_UNIX); SOCK_CONST(AF_INET); SOCK_CONST(AF_INET6); SOCK_CONST(SOCK_STREAM); SOCK_CONST(SOCK_DGRAM); SOCK_CONST(SOCK_RAW); SOCK_CONST(SOCK_SEQPACKET); SOCK_CONST(SOCK_RDM); SOCK_CONST(MSG_OOB); SOCK_CONST(MSG_WAITALL); SOCK_CONST(MSG_CTRUNC); SOCK_CONST(MSG_TRUNC); SOCK_CONST(MSG_PEEK); SOCK_CONST(MSG_DONTROUTE); #ifdef MSG_EOR SOCK_CONST(MSG_EOR); #endif #ifdef MSG_EOF SOCK_CONST(MSG_EOF); #endif #ifdef MSG_CONFIRM SOCK_CONST(MSG_CONFIRM); #endif #ifdef MSG_ERRQUEUE SOCK_CONST(MSG_ERRQUEUE); #endif #ifdef MSG_NOSIGNAL SOCK_CONST(MSG_NOSIGNAL); #endif #ifdef MSG_DONTWAIT SOCK_CONST(MSG_DONTWAIT); #endif #ifdef MSG_MORE SOCK_CONST(MSG_MORE); #endif #ifdef MSG_WAITFORONE SOCK_CONST(MSG_WAITFORONE); #endif #ifdef MSG_CMSG_CLOEXEC SOCK_CONST(MSG_CMSG_CLOEXEC); #endif SOCK_CONST(SO_DEBUG); SOCK_CONST(SO_REUSEADDR); #ifdef SO_REUSEPORT SOCK_CONST(SO_REUSEPORT); #endif SOCK_CONST(SO_KEEPALIVE); SOCK_CONST(SO_DONTROUTE); SOCK_CONST(SO_LINGER); SOCK_CONST(SO_BROADCAST); SOCK_CONST(SO_OOBINLINE); SOCK_CONST(SO_SNDBUF); SOCK_CONST(SO_RCVBUF); SOCK_CONST(SO_SNDLOWAT); SOCK_CONST(SO_RCVLOWAT); SOCK_CONST(SO_SNDTIMEO); SOCK_CONST(SO_RCVTIMEO); SOCK_CONST(SO_TYPE); #ifdef SO_FAMILY SOCK_CONST(SO_FAMILY); #endif SOCK_CONST(SO_ERROR); #ifdef SO_BINDTODEVICE SOCK_CONST(SO_BINDTODEVICE); #endif SOCK_CONST(SOL_SOCKET); SOCK_CONST(SOMAXCONN); #ifdef TCP_NODELAY SOCK_CONST(TCP_NODELAY); #endif SOCK_CONST(PHP_NORMAL_READ); SOCK_CONST(PHP_BINARY_READ); /* TODO: MCAST_* constants and logic to handle them */ SOCK_CONST(IP_MULTICAST_IF); SOCK_CONST(IP_MULTICAST_TTL); SOCK_CONST(IP_MULTICAST_LOOP); SOCK_CONST(IPV6_MULTICAST_IF); SOCK_CONST(IPV6_MULTICAST_HOPS); SOCK_CONST(IPV6_MULTICAST_LOOP); SOCK_CONST(IPPROTO_IP); SOCK_CONST(IPPROTO_IPV6); Native::registerConstant<KindOfInt64>(s_SOL_TCP.get(), IPPROTO_TCP); Native::registerConstant<KindOfInt64>(s_SOL_UDP.get(), IPPROTO_UDP); SOCK_CONST(IPV6_UNICAST_HOPS); HHVM_FE(socket_create); HHVM_FE(socket_create_listen); HHVM_FE(socket_create_pair); HHVM_FE(socket_get_option); HHVM_FE(socket_getpeername); HHVM_FE(socket_getsockname); HHVM_FE(socket_set_block); HHVM_FE(socket_set_nonblock); HHVM_FE(socket_set_option); HHVM_FE(socket_connect); HHVM_FE(socket_bind); HHVM_FE(socket_listen); HHVM_FE(socket_select); HHVM_FE(socket_server); HHVM_FE(socket_accept); HHVM_FE(socket_read); HHVM_FE(socket_write); HHVM_FE(socket_send); HHVM_FE(socket_sendto); HHVM_FE(socket_recv); HHVM_FE(socket_recvfrom); HHVM_FE(socket_shutdown); HHVM_FE(socket_close); HHVM_FE(socket_strerror); HHVM_FE(socket_last_error); HHVM_FE(socket_clear_error); HHVM_FE(getaddrinfo); loadSystemlib(); }
string toHex(const StaticString &data) { string result(data.size() * 2, '\0'); toHex(data, (char *) result.data()); return result; }
void c_Continuation::call_next() { const HPHP::Func* func = m_cls->lookupMethod(s_next.get()); g_vmContext->invokeContFunc(func, this); }
namespace HPHP { namespace HHBBC { ////////////////////////////////////////////////////////////////////// const StaticString s_Vector("HH\\Vector"); const StaticString s_Map("HH\\Map"); const StaticString s_Set("HH\\Set"); const StaticString s_add("add"); const StaticString s_addall("addall"); const StaticString s_append("append"); const StaticString s_clear("clear"); const StaticString s_remove("remove"); const StaticString s_removeall("removeall"); const StaticString s_removekey("removekey"); const StaticString s_set("set"); const StaticString s_setall("setall"); ////////////////////////////////////////////////////////////////////// bool is_collection_method_returning_this(const php::Class* cls, const php::Func* func) { if (!cls) return false; if (cls->name->isame(s_Vector.get())) { return func->name->isame(s_add.get()) || func->name->isame(s_addall.get()) || func->name->isame(s_append.get()) || func->name->isame(s_clear.get()) || func->name->isame(s_removekey.get()) || func->name->isame(s_set.get()) || func->name->isame(s_setall.get()); } if (cls->name->isame(s_Map.get())) { return func->name->isame(s_add.get()) || func->name->isame(s_addall.get()) || func->name->isame(s_clear.get()) || func->name->isame(s_remove.get()) || func->name->isame(s_set.get()) || func->name->isame(s_setall.get()); } if (cls->name->isame(s_Set.get())) { return func->name->isame(s_add.get()) || func->name->isame(s_addall.get()) || func->name->isame(s_clear.get()) || func->name->isame(s_remove.get()) || func->name->isame(s_removeall.get()); } return false; } 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); } }}
namespace HPHP { /////////////////////////////////////////////////////////////////////////////// const StaticString s_unknown_type("unknown type"), s_boolean("boolean"), s_bool("bool"), s_integer("integer"), s_int("int"), s_float("float"), s_double("double"), s_string("string"), s_object("object"), s_array("array"), s_dict("dict"), s_vec("vec"), s_keyset("keyset"), s_NULL("NULL"), s_null("null"); String HHVM_FUNCTION(gettype, const Variant& v) { if (v.getType() == KindOfResource && v.toCResRef().isInvalid()) { return s_unknown_type; } /* Although getDataTypeString also handles the null type, it returns "null" * (lower case). Unfortunately, PHP returns "NULL" (upper case) for * gettype(). So we make an exception here. */ if (v.isNull()) { return s_NULL; } if (v.isArray()) { if (v.toArray()->isDict()) return s_dict; if (v.toArray()->isVecArray()) return s_vec; if (v.toArray()->isKeyset()) return s_keyset; } return getDataTypeString(v.getType()); } String HHVM_FUNCTION(get_resource_type, const Resource& handle) { return handle->o_getResourceName(); } bool HHVM_FUNCTION(boolval, const Variant& v) { return v.toBoolean(); } int64_t HHVM_FUNCTION(intval, const Variant& v, int64_t base /* = 10 */) { return v.toInt64(base); } double HHVM_FUNCTION(floatval, const Variant& v) { return v.toDouble(); } String HHVM_FUNCTION(strval, const Variant& v) { return v.toString(); } bool HHVM_FUNCTION(settype, VRefParam var, const String& type) { Variant val; if (type == s_boolean) val = var.toBoolean(); else if (type == s_bool ) val = var.toBoolean(); else if (type == s_integer) val = var.toInt64(); else if (type == s_int ) val = var.toInt64(); else if (type == s_float ) val = var.toDouble(); else if (type == s_double ) val = var.toDouble(); else if (type == s_string ) val = var.toString(); else if (type == s_array ) val = var.toArray(); else if (type == s_object ) val = var.toObject(); else if (type == s_null ) val = uninit_null(); else return false; var.assignIfRef(val); return true; } bool HHVM_FUNCTION(is_null, const Variant& v) { return is_null(v); } bool HHVM_FUNCTION(is_bool, const Variant& v) { return is_bool(v); } bool HHVM_FUNCTION(is_int, const Variant& v) { return is_int(v); } bool HHVM_FUNCTION(is_float, const Variant& v) { return is_double(v); } bool HHVM_FUNCTION(is_numeric, const Variant& v) { return v.isNumeric(true); } bool HHVM_FUNCTION(is_string, const Variant& v) { return is_string(v); } bool HHVM_FUNCTION(is_scalar, const Variant& v) { return v.isScalar(); } bool HHVM_FUNCTION(is_array, const Variant& v) { return is_array(v); } bool HHVM_FUNCTION(HH_is_vec, const Variant& v) { return is_vec(v); } bool HHVM_FUNCTION(HH_is_dict, const Variant& v) { return is_dict(v); } bool HHVM_FUNCTION(HH_is_keyset, const Variant& v) { return is_keyset(v); } bool HHVM_FUNCTION(is_object, const Variant& v) { return is_object(v); } bool HHVM_FUNCTION(is_resource, const Variant& v) { return (v.getType() == KindOfResource && !v.toCResRef().isInvalid()); } /////////////////////////////////////////////////////////////////////////////// // input/output Variant HHVM_FUNCTION(print_r, const Variant& expression, bool ret /* = false */) { Variant res; try { VariableSerializer vs(VariableSerializer::Type::PrintR); if (ret) { res = vs.serialize(expression, ret); } else { vs.serialize(expression, ret); res = true; } } catch (StringBufferLimitException &e) { raise_notice("print_r() exceeded max bytes limit"); res = e.m_result; } return res; } Variant HHVM_FUNCTION(var_export, const Variant& expression, bool ret /* = false */) { Variant res; try { VariableSerializer vs(VariableSerializer::Type::VarExport); if (ret) { res = vs.serialize(expression, ret); } else { vs.serialize(expression, ret); res = true; } } catch (StringBufferLimitException &e) { raise_notice("var_export() exceeded max bytes limit"); } return res; } static ALWAYS_INLINE void do_var_dump(VariableSerializer vs, const Variant& expression) { // manipulate maxCount to match PHP behavior if (!expression.isObject()) { vs.incMaxCount(); } vs.serialize(expression, false); } void HHVM_FUNCTION(var_dump, const Variant& expression, const Array& _argv /*=null_array */) { #ifdef ENABLE_EXTENSION_XDEBUG if (UNLIKELY(XDEBUG_GLOBAL(OverloadVarDump) && XDEBUG_GLOBAL(DefaultEnable))) { HHVM_FN(xdebug_var_dump)(expression, _argv); return; } #endif VariableSerializer vs(VariableSerializer::Type::VarDump, 0, 2); do_var_dump(vs, expression); auto sz = _argv.size(); for (int i = 0; i < sz; i++) { do_var_dump(vs, _argv[i]); } } void HHVM_FUNCTION(debug_zval_dump, const Variant& variable) { VariableSerializer vs(VariableSerializer::Type::DebugDump); vs.serialize(variable, false); } const StaticString s_Null("N;"), s_True("b:1;"), s_False("b:0;"), s_Res("i:0;"), s_EmptyArray("a:0:{}"), s_EmptyVecArray("v:0:{}"), s_EmptyDictArray("D:0:{}"); String HHVM_FUNCTION(serialize, const Variant& value) { switch (value.getType()) { case KindOfUninit: case KindOfNull: return s_Null; case KindOfBoolean: return value.getBoolean() ? s_True : s_False; case KindOfInt64: { StringBuffer sb; sb.append("i:"); sb.append(value.getInt64()); sb.append(';'); return sb.detach(); } case KindOfPersistentString: 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 KindOfResource: return s_Res; case KindOfPersistentArray: case KindOfArray: { ArrayData *arr = value.getArrayData(); if (arr->empty()) { if (arr->isVecArray()) return s_EmptyVecArray; if (arr->isDict()) return s_EmptyDictArray; return s_EmptyArray; } // fall-through } case KindOfDouble: case KindOfObject: { VariableSerializer vs(VariableSerializer::Type::Serialize); return vs.serialize(value, true); } case KindOfRef: case KindOfClass: break; } not_reached(); } Variant HHVM_FUNCTION(unserialize, const String& str, const Array& options /* =[] */) { return unserialize_from_string(str, options); } /////////////////////////////////////////////////////////////////////////////// // variable table ALWAYS_INLINE static Array get_defined_vars() { VarEnv* v = g_context->getOrCreateVarEnv(); return v ? v->getDefinedVariables() : empty_array(); } Array HHVM_FUNCTION(get_defined_vars) { raise_disallowed_dynamic_call("get_defined_vars should not be " "called dynamically"); return get_defined_vars(); } // accessible as __SystemLib\\get_defined_vars Array HHVM_FUNCTION(SystemLib_get_defined_vars) { return get_defined_vars(); } const StaticString s_GLOBALS("GLOBALS"), s_this("this"); static const Func* arGetContextFunc(const ActRec* ar) { if (ar == nullptr) { return nullptr; } if (ar->m_func->isPseudoMain() || ar->m_func->isBuiltin()) { // Pseudomains inherit the context of their caller auto const context = g_context.getNoCheck(); ar = context->getPrevVMState(ar); while (ar != nullptr && (ar->m_func->isPseudoMain() || ar->m_func->isBuiltin())) { ar = context->getPrevVMState(ar); } if (ar == nullptr) { return nullptr; } } return ar->m_func; } static bool modify_extract_name(VarEnv* v, String& name, int64_t extract_type, const String& prefix) { switch (extract_type) { case EXTR_SKIP: if (v->lookup(name.get()) != nullptr) { return false; } break; case EXTR_IF_EXISTS: if (v->lookup(name.get()) == nullptr) { return false; } else { goto namechecks; } break; case EXTR_PREFIX_SAME: if (v->lookup(name.get()) != nullptr) { name = prefix + "_" + name; } else { goto namechecks; } break; case EXTR_PREFIX_ALL: name = prefix + "_" + name; break; case EXTR_PREFIX_INVALID: if (!is_valid_var_name(name.get()->data(), name.size())) { name = prefix + "_" + name; } else { goto namechecks; } break; case EXTR_PREFIX_IF_EXISTS: if (v->lookup(name.get()) == nullptr) { return false; } name = prefix + "_" + name; break; case EXTR_OVERWRITE: namechecks: if (name == s_GLOBALS) { return false; } if (name == s_this) { // Only disallow $this when inside a non-static method, or a static method // that has defined $this (matches Zend) auto const func = arGetContextFunc(GetCallerFrame()); if (func && func->isMethod() && v->lookup(s_this.get()) != nullptr) { return false; } } default: break; } // skip invalid variable names, as in PHP return is_valid_var_name(name.get()->data(), name.size()); } ALWAYS_INLINE static int64_t extract_impl(VRefParam vref_array, int extract_type /* = EXTR_OVERWRITE */, const String& prefix /* = "" */) { auto arrByRef = false; auto arr_tv = vref_array.wrapped().asTypedValue(); if (arr_tv->m_type == KindOfRef) { arr_tv = arr_tv->m_data.pref->tv(); arrByRef = true; } if (!isArrayType(arr_tv->m_type)) { raise_warning("extract() expects parameter 1 to be array"); return 0; } bool reference = extract_type & EXTR_REFS; extract_type &= ~EXTR_REFS; VMRegAnchor _; auto const varEnv = g_context->getOrCreateVarEnv(); if (!varEnv) return 0; auto& carr = tvAsCVarRef(arr_tv).asCArrRef(); if (UNLIKELY(reference)) { auto extr_refs = [&](Array& arr) { if (arr.size() > 0) { // force arr to escalate (if necessary) by getting an lvalue to the // first element. ArrayData* ad = arr.get(); auto const& first_key = ad->getKey(ad->iter_begin()); arr.lvalAt(first_key); } int count = 0; for (ArrayIter iter(arr); iter; ++iter) { auto name = iter.first().toString(); if (!modify_extract_name(varEnv, name, extract_type, prefix)) continue; // The const_cast is safe because we escalated the array. We can't use // arr.lvalAt(name), because arr may have been modified as a side // effect of an earlier iteration. auto& ref = const_cast<Variant&>(iter.secondRef()); g_context->bindVar(name.get(), ref.asTypedValue()); ++count; } return count; }; if (arrByRef) { return extr_refs(tvAsVariant(vref_array.getRefData()->tv()).asArrRef()); } Array tmp = carr; return extr_refs(tmp); } int count = 0; for (ArrayIter iter(carr); iter; ++iter) { auto name = iter.first().toString(); if (!modify_extract_name(varEnv, name, extract_type, prefix)) continue; g_context->setVar(name.get(), iter.secondRef().asTypedValue()); ++count; } return count; } int64_t HHVM_FUNCTION(extract, VRefParam vref_array, int64_t extract_type /* = EXTR_OVERWRITE */, const String& prefix /* = "" */) { raise_disallowed_dynamic_call("extract should not be called dynamically"); return extract_impl(vref_array, extract_type, prefix); } int64_t HHVM_FUNCTION(SystemLib_extract, VRefParam vref_array, int64_t extract_type = EXTR_OVERWRITE, const String& prefix = "") { return extract_impl(vref_array, extract_type, prefix); } static void parse_str_impl(const String& str, VRefParam arr) { Array result = Array::Create(); HttpProtocol::DecodeParameters(result, str.data(), str.size()); if (!arr.isReferenced()) { HHVM_FN(SystemLib_extract)(result); return; } arr.assignIfRef(result); } void HHVM_FUNCTION(parse_str, const String& str, VRefParam arr /* = null */) { raise_disallowed_dynamic_call("parse_str should not be called dynamically"); parse_str_impl(str, arr); } void HHVM_FUNCTION(SystemLib_parse_str, const String& str, VRefParam arr /* = null */) { parse_str_impl(str, arr); } ///////////////////////////////////////////////////////////////////////////// void StandardExtension::initVariable() { HHVM_RC_INT_SAME(EXTR_IF_EXISTS); HHVM_RC_INT_SAME(EXTR_OVERWRITE); HHVM_RC_INT_SAME(EXTR_PREFIX_ALL); HHVM_RC_INT_SAME(EXTR_PREFIX_IF_EXISTS); HHVM_RC_INT_SAME(EXTR_PREFIX_INVALID); HHVM_RC_INT_SAME(EXTR_PREFIX_SAME); HHVM_RC_INT_SAME(EXTR_REFS); HHVM_RC_INT_SAME(EXTR_SKIP); HHVM_FE(is_null); HHVM_FE(is_bool); HHVM_FE(is_int); HHVM_FALIAS(is_integer, is_int); HHVM_FALIAS(is_long, is_int); HHVM_FE(is_float); HHVM_FALIAS(is_double, is_float); HHVM_FALIAS(is_real, is_float); HHVM_FE(is_numeric); HHVM_FE(is_string); HHVM_FE(is_scalar); HHVM_FE(is_array); HHVM_FALIAS(HH\\is_vec, HH_is_vec); HHVM_FALIAS(HH\\is_dict, HH_is_dict); HHVM_FALIAS(HH\\is_keyset, HH_is_keyset); HHVM_FE(is_object); HHVM_FE(is_resource); HHVM_FE(boolval); HHVM_FE(intval); HHVM_FE(floatval); HHVM_FALIAS(doubleval, floatval); HHVM_FE(strval); HHVM_FE(gettype); HHVM_FE(get_resource_type); HHVM_FE(settype); HHVM_FE(print_r); HHVM_FE(var_export); HHVM_FE(debug_zval_dump); HHVM_FE(var_dump); HHVM_FE(serialize); HHVM_FE(unserialize); HHVM_FE(get_defined_vars); HHVM_FALIAS(__SystemLib\\get_defined_vars, SystemLib_get_defined_vars); HHVM_FE(extract); HHVM_FE(parse_str); HHVM_FALIAS(__SystemLib\\extract, SystemLib_extract); HHVM_FALIAS(__SystemLib\\parse_str, SystemLib_parse_str); loadSystemlib("std_variable"); } /////////////////////////////////////////////////////////////////////////////// } // namespace HPHP
namespace HPHP { /////////////////////////////////////////////////////////////////////////////// // json_encode() options const int64_t k_JSON_HEX_TAG = 1<<0; const int64_t k_JSON_HEX_AMP = 1<<1; const int64_t k_JSON_HEX_APOS = 1<<2; const int64_t k_JSON_HEX_QUOT = 1<<3; const int64_t k_JSON_FORCE_OBJECT = 1<<4; const int64_t k_JSON_NUMERIC_CHECK = 1<<5; const int64_t k_JSON_UNESCAPED_SLASHES = 1<<6; const int64_t k_JSON_PRETTY_PRINT = 1<<7; const int64_t k_JSON_UNESCAPED_UNICODE = 1<<8; const int64_t k_JSON_PARTIAL_OUTPUT_ON_ERROR = 1<<9; // json_decode() options const int64_t k_JSON_BIGINT_AS_STRING = 1<<0; // FB json_decode() options // intentionally higher so when PHP adds more options we're fine const int64_t k_JSON_FB_LOOSE = 1<<20; const int64_t k_JSON_FB_UNLIMITED = 1<<21; const int64_t k_JSON_FB_EXTRA_ESCAPES = 1<<22; const int64_t k_JSON_FB_COLLECTIONS = 1<<23; const int64_t k_JSON_FB_STABLE_MAPS = 1<<24; const int64_t k_JSON_ERROR_NONE = json_error_codes::JSON_ERROR_NONE; const int64_t k_JSON_ERROR_DEPTH = json_error_codes::JSON_ERROR_DEPTH; const int64_t k_JSON_ERROR_STATE_MISMATCH = json_error_codes::JSON_ERROR_STATE_MISMATCH; const int64_t k_JSON_ERROR_CTRL_CHAR = json_error_codes::JSON_ERROR_CTRL_CHAR; const int64_t k_JSON_ERROR_SYNTAX = json_error_codes::JSON_ERROR_SYNTAX; const int64_t k_JSON_ERROR_UTF8 = json_error_codes::JSON_ERROR_UTF8; const int64_t k_JSON_ERROR_RECURSION = json_error_codes::JSON_ERROR_RECURSION; const int64_t k_JSON_ERROR_INF_OR_NAN = json_error_codes::JSON_ERROR_INF_OR_NAN; const int64_t k_JSON_ERROR_UNSUPPORTED_TYPE = json_error_codes::JSON_ERROR_UNSUPPORTED_TYPE; /////////////////////////////////////////////////////////////////////////////// int64_t HHVM_FUNCTION(json_last_error) { return (int) json_get_last_error_code(); } String HHVM_FUNCTION(json_last_error_msg) { return json_get_last_error_msg(); } Variant HHVM_FUNCTION(json_encode, const Variant& value, int64_t options /* = 0 */, int64_t depth /* = 512 */) { // Special case for resource since VariableSerializer does not take care of it if (value.isResource()) { json_set_last_error_code(json_error_codes::JSON_ERROR_UNSUPPORTED_TYPE); if (options & k_JSON_PARTIAL_OUTPUT_ON_ERROR) { return "null"; } return false; } json_set_last_error_code(json_error_codes::JSON_ERROR_NONE); VariableSerializer vs(VariableSerializer::Type::JSON, options); vs.setDepthLimit(depth); String json = vs.serializeValue(value, !(options & k_JSON_FB_UNLIMITED)); if ((json_get_last_error_code() != json_error_codes::JSON_ERROR_NONE) && !(options & k_JSON_PARTIAL_OUTPUT_ON_ERROR)) { return false; } return json; } Variant HHVM_FUNCTION(json_decode, const String& json, bool assoc /* = false */, int64_t depth /* = 512 */, int64_t options /* = 0 */) { json_set_last_error_code(json_error_codes::JSON_ERROR_NONE); if (json.empty()) { return init_null(); } const int64_t supported_options = k_JSON_FB_LOOSE | k_JSON_FB_COLLECTIONS | k_JSON_FB_STABLE_MAPS | k_JSON_BIGINT_AS_STRING; int64_t parser_options = options & supported_options; Variant z; if (JSON_parser(z, json.data(), json.size(), assoc, depth, parser_options)) { return z; } String trimmed = f_trim(json, "\t\n\r "); if (trimmed.size() == 4) { if (!strcasecmp(trimmed.data(), "null")) { json_set_last_error_code(json_error_codes::JSON_ERROR_NONE); return init_null(); } if (!strcasecmp(trimmed.data(), "true")) { json_set_last_error_code(json_error_codes::JSON_ERROR_NONE); return true; } } else if (trimmed.size() == 5 && !strcasecmp(trimmed.data(), "false")) { json_set_last_error_code(json_error_codes::JSON_ERROR_NONE); return false; } int64_t p; double d; DataType type = json.get()->isNumericWithVal(p, d, 0); if (type == KindOfInt64) { json_set_last_error_code(json_error_codes::JSON_ERROR_NONE); return p; } else if (type == KindOfDouble) { json_set_last_error_code(json_error_codes::JSON_ERROR_NONE); if ((options & k_JSON_BIGINT_AS_STRING) && (json.toInt64() == LLONG_MAX || json.toInt64() == LLONG_MIN) && errno == ERANGE) { // Overflow bool is_float = false; for (int i = (trimmed[0] == '-' ? 1 : 0); i < trimmed.size(); ++i) { if (trimmed[i] < '0' || trimmed[i] > '9') { is_float = true; break; } } if (!is_float) { return trimmed; } } return d; } char ch0 = json.charAt(0); if (json.size() > 1 && ch0 == '"' && json.charAt(json.size() - 1) == '"') { json_set_last_error_code(json_error_codes::JSON_ERROR_NONE); // Wrap the string in an array to allow the JSON_parser to handle // things like unicode escape sequences, then unwrap to get result String wrapped("["); wrapped += json + "]"; // Stick to a normal hhvm array for the wrapper const int64_t mask = ~(k_JSON_FB_COLLECTIONS | k_JSON_FB_STABLE_MAPS); if (JSON_parser(z, wrapped.data(), wrapped.size(), false, depth, parser_options & mask) && z.isArray()) { Array arr = z.toArray(); if ((arr.size() == 1) && arr.exists(0)) { return arr[0]; } // The input string could be something like: "foo","bar" // Which will parse inside the [] wrapper, but be invalid json_set_last_error_code(json_error_codes::JSON_ERROR_SYNTAX); } } if ((options & k_JSON_FB_LOOSE) && json.size() > 1 && ch0 == '\'' && json.charAt(json.size() - 1) == '\'') { json_set_last_error_code(json_error_codes::JSON_ERROR_NONE); return json.substr(1, json.size() - 2); } assert(json_get_last_error_code() != json_error_codes::JSON_ERROR_NONE); return init_null(); } /////////////////////////////////////////////////////////////////////////////// const StaticString s_JSON_HEX_TAG("JSON_HEX_TAG"), s_JSON_HEX_AMP("JSON_HEX_AMP"), s_JSON_HEX_APOS("JSON_HEX_APOS"), s_JSON_HEX_QUOT("JSON_HEX_QUOT"), s_JSON_FORCE_OBJECT("JSON_FORCE_OBJECT"), s_JSON_NUMERIC_CHECK("JSON_NUMERIC_CHECK"), s_JSON_UNESCAPED_SLASHES("JSON_UNESCAPED_SLASHES"), s_JSON_PRETTY_PRINT("JSON_PRETTY_PRINT"), s_JSON_UNESCAPED_UNICODE("JSON_UNESCAPED_UNICODE"), s_JSON_PARTIAL_OUTPUT_ON_ERROR("JSON_PARTIAL_OUTPUT_ON_ERROR"), s_JSON_BIGINT_AS_STRING("JSON_BIGINT_AS_STRING"), s_JSON_FB_LOOSE("JSON_FB_LOOSE"), s_JSON_FB_UNLIMITED("JSON_FB_UNLIMITED"), s_JSON_FB_EXTRA_ESCAPES("JSON_FB_EXTRA_ESCAPES"), s_JSON_FB_COLLECTIONS("JSON_FB_COLLECTIONS"), s_JSON_FB_STABLE_MAPS("JSON_FB_STABLE_MAPS"), s_JSON_ERROR_NONE("JSON_ERROR_NONE"), s_JSON_ERROR_DEPTH("JSON_ERROR_DEPTH"), s_JSON_ERROR_STATE_MISMATCH("JSON_ERROR_STATE_MISMATCH"), s_JSON_ERROR_CTRL_CHAR("JSON_ERROR_CTRL_CHAR"), s_JSON_ERROR_SYNTAX("JSON_ERROR_SYNTAX"), s_JSON_ERROR_UTF8("JSON_ERROR_UTF8"), s_JSON_ERROR_RECURSION("JSON_ERROR_RECURSION"), s_JSON_ERROR_INF_OR_NAN("JSON_ERROR_INF_OR_NAN"), s_JSON_ERROR_UNSUPPORTED_TYPE("JSON_ERROR_UNSUPPORTED_TYPE"); class JsonExtension : public Extension { public: JsonExtension() : Extension("json", "1.2.1") {} virtual void moduleInit() { Native::registerConstant<KindOfInt64>( s_JSON_HEX_TAG.get(), k_JSON_HEX_TAG ); Native::registerConstant<KindOfInt64>( s_JSON_HEX_AMP.get(), k_JSON_HEX_AMP ); Native::registerConstant<KindOfInt64>( s_JSON_HEX_APOS.get(), k_JSON_HEX_APOS ); Native::registerConstant<KindOfInt64>( s_JSON_HEX_QUOT.get(), k_JSON_HEX_QUOT ); Native::registerConstant<KindOfInt64>( s_JSON_FORCE_OBJECT.get(), k_JSON_FORCE_OBJECT ); Native::registerConstant<KindOfInt64>( s_JSON_NUMERIC_CHECK.get(), k_JSON_NUMERIC_CHECK ); Native::registerConstant<KindOfInt64>( s_JSON_UNESCAPED_SLASHES.get(), k_JSON_UNESCAPED_SLASHES ); Native::registerConstant<KindOfInt64>( s_JSON_PRETTY_PRINT.get(), k_JSON_PRETTY_PRINT ); Native::registerConstant<KindOfInt64>( s_JSON_UNESCAPED_UNICODE.get(), k_JSON_UNESCAPED_UNICODE ); Native::registerConstant<KindOfInt64>( s_JSON_PARTIAL_OUTPUT_ON_ERROR.get(), k_JSON_PARTIAL_OUTPUT_ON_ERROR ); Native::registerConstant<KindOfInt64>( s_JSON_BIGINT_AS_STRING.get(), k_JSON_BIGINT_AS_STRING ); Native::registerConstant<KindOfInt64>( s_JSON_FB_LOOSE.get(), k_JSON_FB_LOOSE ); Native::registerConstant<KindOfInt64>( s_JSON_FB_UNLIMITED.get(), k_JSON_FB_UNLIMITED ); Native::registerConstant<KindOfInt64>( s_JSON_FB_EXTRA_ESCAPES.get(), k_JSON_FB_EXTRA_ESCAPES ); Native::registerConstant<KindOfInt64>( s_JSON_FB_COLLECTIONS.get(), k_JSON_FB_COLLECTIONS ); Native::registerConstant<KindOfInt64>( s_JSON_FB_STABLE_MAPS.get(), k_JSON_FB_STABLE_MAPS ); Native::registerConstant<KindOfInt64>( s_JSON_ERROR_NONE.get(), k_JSON_ERROR_NONE ); Native::registerConstant<KindOfInt64>( s_JSON_ERROR_DEPTH.get(), k_JSON_ERROR_DEPTH ); Native::registerConstant<KindOfInt64>( s_JSON_ERROR_STATE_MISMATCH.get(), k_JSON_ERROR_STATE_MISMATCH ); Native::registerConstant<KindOfInt64>( s_JSON_ERROR_CTRL_CHAR.get(), k_JSON_ERROR_CTRL_CHAR ); Native::registerConstant<KindOfInt64>( s_JSON_ERROR_SYNTAX.get(), k_JSON_ERROR_SYNTAX ); Native::registerConstant<KindOfInt64>( s_JSON_ERROR_UTF8.get(), k_JSON_ERROR_UTF8 ); Native::registerConstant<KindOfInt64>( s_JSON_ERROR_RECURSION.get(), k_JSON_ERROR_RECURSION ); Native::registerConstant<KindOfInt64>( s_JSON_ERROR_INF_OR_NAN.get(), k_JSON_ERROR_INF_OR_NAN ); Native::registerConstant<KindOfInt64>( s_JSON_ERROR_UNSUPPORTED_TYPE.get(), k_JSON_ERROR_UNSUPPORTED_TYPE ); HHVM_FE(json_last_error); HHVM_FE(json_last_error_msg); HHVM_FE(json_encode); HHVM_FE(json_decode); loadSystemlib(); } } s_json_extension; }
virtual void moduleInit() { Native::registerConstant<KindOfInt64>( s_JSON_HEX_TAG.get(), k_JSON_HEX_TAG ); Native::registerConstant<KindOfInt64>( s_JSON_HEX_AMP.get(), k_JSON_HEX_AMP ); Native::registerConstant<KindOfInt64>( s_JSON_HEX_APOS.get(), k_JSON_HEX_APOS ); Native::registerConstant<KindOfInt64>( s_JSON_HEX_QUOT.get(), k_JSON_HEX_QUOT ); Native::registerConstant<KindOfInt64>( s_JSON_FORCE_OBJECT.get(), k_JSON_FORCE_OBJECT ); Native::registerConstant<KindOfInt64>( s_JSON_NUMERIC_CHECK.get(), k_JSON_NUMERIC_CHECK ); Native::registerConstant<KindOfInt64>( s_JSON_UNESCAPED_SLASHES.get(), k_JSON_UNESCAPED_SLASHES ); Native::registerConstant<KindOfInt64>( s_JSON_PRETTY_PRINT.get(), k_JSON_PRETTY_PRINT ); Native::registerConstant<KindOfInt64>( s_JSON_UNESCAPED_UNICODE.get(), k_JSON_UNESCAPED_UNICODE ); Native::registerConstant<KindOfInt64>( s_JSON_PARTIAL_OUTPUT_ON_ERROR.get(), k_JSON_PARTIAL_OUTPUT_ON_ERROR ); Native::registerConstant<KindOfInt64>( s_JSON_BIGINT_AS_STRING.get(), k_JSON_BIGINT_AS_STRING ); Native::registerConstant<KindOfInt64>( s_JSON_FB_LOOSE.get(), k_JSON_FB_LOOSE ); Native::registerConstant<KindOfInt64>( s_JSON_FB_UNLIMITED.get(), k_JSON_FB_UNLIMITED ); Native::registerConstant<KindOfInt64>( s_JSON_FB_EXTRA_ESCAPES.get(), k_JSON_FB_EXTRA_ESCAPES ); Native::registerConstant<KindOfInt64>( s_JSON_FB_COLLECTIONS.get(), k_JSON_FB_COLLECTIONS ); Native::registerConstant<KindOfInt64>( s_JSON_FB_STABLE_MAPS.get(), k_JSON_FB_STABLE_MAPS ); Native::registerConstant<KindOfInt64>( s_JSON_ERROR_NONE.get(), k_JSON_ERROR_NONE ); Native::registerConstant<KindOfInt64>( s_JSON_ERROR_DEPTH.get(), k_JSON_ERROR_DEPTH ); Native::registerConstant<KindOfInt64>( s_JSON_ERROR_STATE_MISMATCH.get(), k_JSON_ERROR_STATE_MISMATCH ); Native::registerConstant<KindOfInt64>( s_JSON_ERROR_CTRL_CHAR.get(), k_JSON_ERROR_CTRL_CHAR ); Native::registerConstant<KindOfInt64>( s_JSON_ERROR_SYNTAX.get(), k_JSON_ERROR_SYNTAX ); Native::registerConstant<KindOfInt64>( s_JSON_ERROR_UTF8.get(), k_JSON_ERROR_UTF8 ); Native::registerConstant<KindOfInt64>( s_JSON_ERROR_RECURSION.get(), k_JSON_ERROR_RECURSION ); Native::registerConstant<KindOfInt64>( s_JSON_ERROR_INF_OR_NAN.get(), k_JSON_ERROR_INF_OR_NAN ); Native::registerConstant<KindOfInt64>( s_JSON_ERROR_UNSUPPORTED_TYPE.get(), k_JSON_ERROR_UNSUPPORTED_TYPE ); HHVM_FE(json_last_error); HHVM_FE(json_last_error_msg); HHVM_FE(json_encode); HHVM_FE(json_decode); loadSystemlib(); }
namespace HPHP { StaticString s_call("__call"); UserFSNode::UserFSNode(Class* cls, const Variant& context /*= null */) { JIT::VMRegAnchor _; const Func* ctor; m_cls = cls; if (LookupResult::MethodFoundWithThis != g_context->lookupCtorMethod(ctor, m_cls)) { throw InvalidArgumentException(0, "Unable to call %s's constructor", m_cls->name()->data()); } m_obj = ObjectData::newInstance(m_cls); m_obj.o_set("context", context); Variant ret; g_context->invokeFuncFew(ret.asTypedValue(), ctor, m_obj.get()); m_Call = lookupMethod(s_call.get()); } Variant UserFSNode::invoke(const Func* func, const String& name, const Array& args, bool& invoked) { JIT::VMRegAnchor _; // Assume failure invoked = false; // Public method, no private ancestor, no need for further checks (common) if (func && !(func->attrs() & (AttrPrivate|AttrProtected|AttrAbstract)) && !func->hasPrivateAncestor()) { Variant ret; g_context->invokeFunc(ret.asTypedValue(), func, args, m_obj.get()); invoked = true; return ret; } // No explicitly defined function, no __call() magic method // Give up. if (!func && !m_Call) { return uninit_null(); } HPHP::JIT::CallerFrame cf; Class* ctx = arGetContextClass(cf()); switch(g_context->lookupObjMethod(func, m_cls, name.get(), ctx)) { case LookupResult::MethodFoundWithThis: { Variant ret; g_context->invokeFunc(ret.asTypedValue(), func, args, m_obj.get()); invoked = true; return ret; } case LookupResult::MagicCallFound: { Variant ret; g_context->invokeFunc(ret.asTypedValue(), func, make_packed_array(name, args), m_obj.get()); invoked = true; return ret; } case LookupResult::MethodNotFound: // There's a method somewhere in the hierarchy, but none // which are accessible. /* fallthrough */ case LookupResult::MagicCallStaticFound: // We're not calling statically, so this result is unhelpful // Also, it's never produced by lookupObjMethod, so it'll // never happen, but we must handle all enums return uninit_null(); case LookupResult::MethodFoundNoThis: // Should never happen (Attr::Static check in ctor) assert(false); raise_error("%s::%s() must not be declared static", m_cls->name()->data(), name.data()); return uninit_null(); } NOT_REACHED(); return uninit_null(); } const Func* UserFSNode::lookupMethod(const StringData* name) { const Func* f = m_cls->lookupMethod(name); if (!f) return nullptr; if (f->attrs() & AttrStatic) { throw InvalidArgumentException(0, "%s::%s() must not be declared static", m_cls->name()->data(), name->data()); } return f; } }
void DeviceListWidget::OnPaintItem(Canvas &canvas, const PixelRect rc, unsigned idx) { assert(idx < NUMDEV); const DeviceConfig &config = CommonInterface::SetSystemSettings().devices[idx]; const Flags flags(*items[idx]); const UPixelScalar margin = Layout::GetTextPadding(); TCHAR port_name_buffer[128]; const TCHAR *port_name = config.GetPortName(port_name_buffer, ARRAY_SIZE(port_name_buffer)); StaticString<256> text(_T("A: ")); text[0u] += idx; if (config.UsesDriver()) { const TCHAR *driver_name = FindDriverDisplayName(config.driver_name); text.AppendFormat(_("%s on %s"), driver_name, port_name); } else { text.append(port_name); } canvas.Select(*look.list.font); canvas.DrawText(rc.left + margin, rc.top + margin, text); /* show a list of features that are available in the second row */ StaticString<256> buffer; const TCHAR *status; if (flags.alive) { if (flags.location) { buffer = _("GPS fix"); } else if (flags.gps) { /* device sends GPGGA, but no valid location */ buffer = _("Bad GPS"); } else { buffer = _("Connected"); } if (flags.baro) { buffer.append(_T("; ")); buffer.append(_("Baro")); } if (flags.airspeed) { buffer.append(_T("; ")); buffer.append(_("Airspeed")); } if (flags.vario) { buffer.append(_T("; ")); buffer.append(_("Vario")); } if (flags.traffic) buffer.append(_T("; FLARM")); status = buffer; } else if (config.IsDisabled()) { status = _("Disabled"); } else if (is_simulator() || !config.IsAvailable()) { status = _("N/A"); } else if (flags.open) { status = _("No data"); #ifdef ANDROID } else if ((config.port_type == DeviceConfig::PortType::RFCOMM || config.port_type == DeviceConfig::PortType::RFCOMM_SERVER) && !BluetoothHelper::isEnabled(Java::GetEnv())) { status = _("Bluetooth is disabled"); #endif } else if (flags.error) { status = _("Error"); } else { status = _("Not connected"); } canvas.Select(*look.small_font); canvas.DrawText(rc.left + margin, rc.top + 2 * margin + font_height, status); }
void Clear() { bssid.clear(); ssid.clear(); }
void ClearName() { name.clear(); }
namespace HPHP { IMPLEMENT_THREAD_LOCAL(std::string, s_misc_highlight_default_string); IMPLEMENT_THREAD_LOCAL(std::string, s_misc_highlight_default_comment); IMPLEMENT_THREAD_LOCAL(std::string, s_misc_highlight_default_keyword); IMPLEMENT_THREAD_LOCAL(std::string, s_misc_highlight_default_default); IMPLEMENT_THREAD_LOCAL(std::string, s_misc_highlight_default_html); IMPLEMENT_THREAD_LOCAL(std::string, s_misc_display_errors); const std::string s_1("1"), s_2("2"), s_stdout("stdout"), s_stderr("stderr"); const double k_INF = std::numeric_limits<double>::infinity(); const double k_NAN = std::numeric_limits<double>::quiet_NaN(); static String HHVM_FUNCTION(server_warmup_status) { // Fail if we jitted more than 25kb of code. size_t begin, end; jit::mcg->codeEmittedThisRequest(begin, end); auto const diff = end - begin; auto constexpr kMaxTCBytes = 25 << 10; if (diff > kMaxTCBytes) { return folly::format("Translation cache grew by {} bytes to {} bytes.", diff, begin).str(); } // Fail if we spent more than 0.5ms in the JIT. auto const jittime = jit::Timer::CounterValue(jit::Timer::translate); auto constexpr kMaxJitTimeNS = 500000; if (jittime.total > kMaxJitTimeNS) { return folly::format("Spent {}us in the JIT.", jittime.total / 1000).str(); } if (!isStandardRequest()) { return "Warmup is still in progress."; } if (requestCount() <= RuntimeOption::EvalJitProfileRequests) { return "PGO profiling translations are still enabled."; } auto tpc_diff = jit::s_perfCounters[jit::tpc_interp_bb] - jit::s_perfCounters[jit::tpc_interp_bb_force]; if (tpc_diff) { return folly::sformat("Interpreted {} non-forced basic blocks.", tpc_diff); } return empty_string(); } void StandardExtension::threadInitMisc() { IniSetting::Bind( this, IniSetting::PHP_INI_ALL, "highlight.string", "#DD0000", s_misc_highlight_default_string.get() ); IniSetting::Bind( this, IniSetting::PHP_INI_ALL, "highlight.comment", "#FF8000", s_misc_highlight_default_comment.get() ); IniSetting::Bind( this, IniSetting::PHP_INI_ALL, "highlight.keyword", "#007700", s_misc_highlight_default_keyword.get() ); IniSetting::Bind( this, IniSetting::PHP_INI_ALL, "highlight.default", "#0000BB", s_misc_highlight_default_default.get() ); IniSetting::Bind( this, IniSetting::PHP_INI_ALL, "highlight.html", "#000000", s_misc_highlight_default_html.get() ); IniSetting::Bind( this, IniSetting::PHP_INI_ALL, "display_errors", RuntimeOption::EnableHipHopSyntax ? "stderr" : "1", IniSetting::SetAndGet<std::string>( [](const std::string& value) { if (value == s_1 || value == s_stdout) { Logger::SetStandardOut(stdout); return true; } if (value == s_2 || value == s_stderr) { Logger::SetStandardOut(stderr); return true; } return false; }, nullptr ), s_misc_display_errors.get() ); } static void bindTokenConstants(); static int get_user_token_id(int internal_id); const StaticString s_T_PAAMAYIM_NEKUDOTAYIM("T_PAAMAYIM_NEKUDOTAYIM"); #define PHP_MAJOR_VERSION 5 #define PHP_MINOR_VERSION 6 #define PHP_RELEASE_VERSION 99 #define PHP_EXTRA_VERSION "hhvm" #define PHP_VERSION "5.6.99-hhvm" #define PHP_VERSION_ID 50699 const StaticString k_PHP_VERSION(PHP_VERSION); void StandardExtension::initMisc() { HHVM_FALIAS(HH\\server_warmup_status, server_warmup_status); HHVM_FE(connection_aborted); HHVM_FE(connection_status); HHVM_FE(connection_timeout); HHVM_FE(constant); HHVM_FE(define); HHVM_FE(defined); HHVM_FE(ignore_user_abort); HHVM_FE(pack); HHVM_FE(sleep); HHVM_FE(usleep); HHVM_FE(time_nanosleep); HHVM_FE(time_sleep_until); HHVM_FE(uniqid); HHVM_FE(unpack); HHVM_FE(sys_getloadavg); HHVM_FE(token_get_all); HHVM_FE(token_name); HHVM_FE(hphp_to_string); HHVM_FALIAS(__SystemLib\\max2, SystemLib_max2); HHVM_FALIAS(__SystemLib\\min2, SystemLib_min2); Native::registerConstant<KindOfDouble>(makeStaticString("INF"), k_INF); Native::registerConstant<KindOfDouble>(makeStaticString("NAN"), k_NAN); Native::registerConstant<KindOfInt64>( makeStaticString("PHP_MAXPATHLEN"), MAXPATHLEN); Native::registerConstant<KindOfBoolean>(makeStaticString("PHP_DEBUG"), #if DEBUG true #else false #endif ); bindTokenConstants(); Native::registerConstant<KindOfInt64>(s_T_PAAMAYIM_NEKUDOTAYIM.get(), get_user_token_id(T_DOUBLE_COLON)); HHVM_RC_STR(PHP_BINARY, current_executable_path()); HHVM_RC_STR(PHP_BINDIR, current_executable_directory()); HHVM_RC_STR(PHP_OS, HHVM_FN(php_uname)("s").toString().toCppString()); HHVM_RC_STR(PHP_SAPI, RuntimeOption::ExecutionMode); HHVM_RC_INT(PHP_INT_SIZE, sizeof(int64_t)); HHVM_RC_INT(PHP_INT_MIN, k_PHP_INT_MIN); HHVM_RC_INT(PHP_INT_MAX, k_PHP_INT_MAX); HHVM_RC_INT_SAME(PHP_MAJOR_VERSION); HHVM_RC_INT_SAME(PHP_MINOR_VERSION); HHVM_RC_INT_SAME(PHP_RELEASE_VERSION); HHVM_RC_STR_SAME(PHP_EXTRA_VERSION); HHVM_RC_STR_SAME(PHP_VERSION); HHVM_RC_INT_SAME(PHP_VERSION_ID); // FIXME: These values are hardcoded from their previous IDL values // Grab their correct values from the system as appropriate HHVM_RC_STR(PHP_EOL, "\n"); HHVM_RC_STR(PHP_CONFIG_FILE_PATH, ""); HHVM_RC_STR(PHP_CONFIG_FILE_SCAN_DIR, ""); HHVM_RC_STR(PHP_DATADIR, ""); HHVM_RC_STR(PHP_EXTENSION_DIR, ""); HHVM_RC_STR(PHP_LIBDIR, ""); HHVM_RC_STR(PHP_LOCALSTATEDIR, ""); HHVM_RC_STR(PHP_PREFIX, ""); HHVM_RC_STR(PHP_SHLIB_SUFFIX, "so"); HHVM_RC_STR(PHP_SYSCONFDIR, ""); HHVM_RC_STR(PEAR_EXTENSION_DIR, ""); HHVM_RC_STR(PEAR_INSTALL_DIR, ""); loadSystemlib("std_misc"); } // Make sure "tokenizer" gets added to the list of extensions IMPLEMENT_DEFAULT_EXTENSION_VERSION(tokenizer, 0.1); /////////////////////////////////////////////////////////////////////////////// int64_t HHVM_FUNCTION(connection_aborted) { return HHVM_FN(connection_status)() == k_CONNECTION_ABORTED; } int64_t HHVM_FUNCTION(connection_status) { return k_CONNECTION_NORMAL; } int64_t HHVM_FUNCTION(connection_timeout) { return HHVM_FN(connection_status)() == k_CONNECTION_TIMEOUT; } static Class* getClassByName(const char* name, int len) { Class* cls = nullptr; // translate "self" or "parent" if (len == 4 && !memcmp(name, "self", 4)) { cls = g_context->getContextClass(); if (!cls) { throw FatalErrorException("Cannot access self:: " "when no class scope is active"); } } else if (len == 6 && !memcmp(name, "parent", 6)) { cls = g_context->getParentContextClass(); if (!cls) { throw FatalErrorException("Cannot access parent"); } } else if (len == 6 && !memcmp(name, "static", 6)) { CallerFrame cf; auto ar = cf(); if (ar) { if (ar->hasThis()) { cls = ar->getThis()->getVMClass(); } else if (ar->hasClass()) { cls = ar->getClass(); } } if (!cls) { throw FatalErrorException("Cannot access static:: " "when no class scope is active"); } } else { String className(name, len, CopyString); cls = Unit::loadClass(className.get()); } return cls; } Variant HHVM_FUNCTION(constant, const String& name) { if (!name.get()) return init_null(); const char *data = name.data(); int len = name.length(); char *colon; if ((colon = (char*)memchr(data, ':', len)) && colon[1] == ':') { // class constant int classNameLen = colon - data; char *constantName = colon + 2; Class* cls = getClassByName(data, classNameLen); if (cls) { String cnsName(constantName, data + len - constantName, CopyString); Cell cns = cls->clsCnsGet(cnsName.get()); if (cns.m_type != KindOfUninit) { return cellAsCVarRef(cns); } } } else { auto const cns = Unit::loadCns(name.get()); if (cns) return tvAsCVarRef(cns); } raise_warning("constant(): Couldn't find constant %s", data); return init_null(); } bool HHVM_FUNCTION(define, const String& name, const Variant& value, bool case_insensitive /* = false */) { if (case_insensitive) { raise_warning(Strings::CONSTANTS_CASE_SENSITIVE); } return Unit::defCns(name.get(), value.asCell()); } bool HHVM_FUNCTION(defined, const String& name, bool autoload /* = true */) { if (!name.get()) return false; const char *data = name.data(); int len = name.length(); char *colon; if ((colon = (char*)memchr(data, ':', len)) && colon[1] == ':') { // class constant int classNameLen = colon - data; char *constantName = colon + 2; Class* cls = getClassByName(data, classNameLen); if (cls) { String cnsName(constantName, data + len - constantName, CopyString); return cls->clsCnsGet(cnsName.get()).m_type != KindOfUninit; } return false; } else { auto* cb = autoload ? Unit::loadCns : Unit::lookupCns; return cb(name.get()); } } int64_t HHVM_FUNCTION(ignore_user_abort, bool setting /* = false */) { return 0; } Variant HHVM_FUNCTION(pack, const String& format, const Array& argv) { // pack() returns false if there was an error, String otherwise return ZendPack().pack(format, argv); } int64_t HHVM_FUNCTION(sleep, int seconds) { IOStatusHelper io("sleep"); Transport *transport = g_context->getTransport(); if (transport) { transport->incSleepTime(seconds); } sleep(seconds); return 0; } void HHVM_FUNCTION(usleep, int micro_seconds) { IOStatusHelper io("usleep"); Transport *transport = g_context->getTransport(); if (transport) { transport->incuSleepTime(micro_seconds); } usleep(micro_seconds); } static void recordNanosleepTime( const struct timespec &req, const struct timespec *rem ) { Transport *transport = g_context->getTransport(); if (transport) { int64_t req_s = req.tv_sec; int32_t req_n = req.tv_nsec; int64_t rem_s = 0; int32_t rem_n = 0; if (rem) { rem_s = rem->tv_sec; rem_n = rem->tv_nsec; } int32_t nanos = req_n - rem_n; int64_t seconds = req_s - rem_s; if (nanos < 0) { nanos += 1000000000; seconds--; } transport->incnSleepTime(seconds, nanos); } } const StaticString s_seconds("seconds"), s_nanoseconds("nanoseconds"); Variant HHVM_FUNCTION(time_nanosleep, int seconds, int nanoseconds) { if (seconds < 0) { throw_invalid_argument("seconds: cannot be negative"); return false; } if (nanoseconds < 0 || nanoseconds > 999999999) { throw_invalid_argument("nanoseconds: has to be 0 to 999999999"); return false; } struct timespec req, rem; req.tv_sec = (time_t)seconds; req.tv_nsec = nanoseconds; IOStatusHelper io("nanosleep"); if (!nanosleep(&req, &rem)) { recordNanosleepTime(req, nullptr); return true; } recordNanosleepTime(req, &rem); if (errno == EINTR) { return make_map_array(s_seconds, (int64_t)rem.tv_sec, s_nanoseconds, (int64_t)rem.tv_nsec); } return false; } bool HHVM_FUNCTION(time_sleep_until, double timestamp) { struct timeval tm; if (gettimeofday((struct timeval *)&tm, NULL) != 0) { return false; } double c_ts = (double)(timestamp - tm.tv_sec - tm.tv_usec / 1000000.0); if (c_ts < 0) { throw_invalid_argument ("timestamp: Sleep until to time is less than current time"); return false; } struct timespec req, rem; req.tv_sec = (time_t)c_ts; req.tv_nsec = (long)((c_ts - req.tv_sec) * 1000000000.0); IOStatusHelper io("nanosleep"); while (nanosleep(&req, &rem)) { recordNanosleepTime(req, &rem); if (errno != EINTR) return false; req.tv_sec = rem.tv_sec; req.tv_nsec = rem.tv_nsec; } recordNanosleepTime(req, nullptr); return true; } String HHVM_FUNCTION(uniqid, const String& prefix /* = null_string */, bool more_entropy /* = false */) { if (!more_entropy) { Transport *transport = g_context->getTransport(); if (transport) { transport->incuSleepTime(1); } usleep(1); } struct timeval tv; gettimeofday((struct timeval *)&tv, NULL); int sec = (int)tv.tv_sec; int usec = (int)(tv.tv_usec % 0x100000); String uniqid(prefix.size() + 64, ReserveString); auto ptr = uniqid.mutableData(); // StringData::capacity() returns the buffer size without the null // terminator. snprintf expects a the buffer capacity including room // for the null terminator, writes the null termintor, and returns // the full length not counting the null terminator. auto capacity = uniqid.capacity() + 1; int64_t len; if (more_entropy) { len = snprintf(ptr, capacity, "%s%08x%05x%.8F", prefix.c_str(), sec, usec, math_combined_lcg() * 10); } else { len = snprintf(ptr, capacity, "%s%08x%05x", prefix.c_str(), sec, usec); } uniqid.setSize(len); return uniqid; } Variant HHVM_FUNCTION(unpack, const String& format, const String& data) { return ZendPack().unpack(format, data); } Array HHVM_FUNCTION(sys_getloadavg) { #if (defined(__CYGWIN__) || defined(__MINGW__) || defined(_MSC_VER)) return make_packed_array(0, 0, 0); #else double load[3]; getloadavg(load, 3); return make_packed_array(load[0], load[1], load[2]); #endif } // We want token IDs to remain stable regardless of how we change the // internals of the parser. Thus, we maintain a mapping from internal // token IDs to stable "user token IDs" and only expose the user token // IDs to the PHP application. const int UserTokenId_T_REQUIRE_ONCE = 258; const int UserTokenId_T_REQUIRE = 259; const int UserTokenId_T_EVAL = 260; const int UserTokenId_T_INCLUDE_ONCE = 261; const int UserTokenId_T_INCLUDE = 262; const int UserTokenId_T_LOGICAL_OR = 263; const int UserTokenId_T_LOGICAL_XOR = 264; const int UserTokenId_T_LOGICAL_AND = 265; const int UserTokenId_T_PRINT = 266; const int UserTokenId_T_SR_EQUAL = 267; const int UserTokenId_T_SL_EQUAL = 268; const int UserTokenId_T_XOR_EQUAL = 269; const int UserTokenId_T_OR_EQUAL = 270; const int UserTokenId_T_AND_EQUAL = 271; const int UserTokenId_T_MOD_EQUAL = 272; const int UserTokenId_T_CONCAT_EQUAL = 273; const int UserTokenId_T_DIV_EQUAL = 274; const int UserTokenId_T_MUL_EQUAL = 275; const int UserTokenId_T_MINUS_EQUAL = 276; const int UserTokenId_T_PLUS_EQUAL = 277; const int UserTokenId_T_BOOLEAN_OR = 278; const int UserTokenId_T_BOOLEAN_AND = 279; const int UserTokenId_T_IS_NOT_IDENTICAL = 280; const int UserTokenId_T_IS_IDENTICAL = 281; const int UserTokenId_T_IS_NOT_EQUAL = 282; const int UserTokenId_T_IS_EQUAL = 283; const int UserTokenId_T_IS_GREATER_OR_EQUAL = 284; const int UserTokenId_T_IS_SMALLER_OR_EQUAL = 285; const int UserTokenId_T_SR = 286; const int UserTokenId_T_SL = 287; const int UserTokenId_T_INSTANCEOF = 288; const int UserTokenId_T_UNSET_CAST = 289; const int UserTokenId_T_BOOL_CAST = 290; const int UserTokenId_T_OBJECT_CAST = 291; const int UserTokenId_T_ARRAY_CAST = 292; const int UserTokenId_T_STRING_CAST = 293; const int UserTokenId_T_DOUBLE_CAST = 294; const int UserTokenId_T_INT_CAST = 295; const int UserTokenId_T_DEC = 296; const int UserTokenId_T_INC = 297; const int UserTokenId_T_CLONE = 298; const int UserTokenId_T_NEW = 299; const int UserTokenId_T_EXIT = 300; const int UserTokenId_T_IF = 301; const int UserTokenId_T_ELSEIF = 302; const int UserTokenId_T_ELSE = 303; const int UserTokenId_T_ENDIF = 304; const int UserTokenId_T_LNUMBER = 305; const int UserTokenId_T_DNUMBER = 306; const int UserTokenId_T_STRING = 307; const int UserTokenId_T_STRING_VARNAME = 308; const int UserTokenId_T_VARIABLE = 309; const int UserTokenId_T_NUM_STRING = 310; const int UserTokenId_T_INLINE_HTML = 311; const int UserTokenId_T_CHARACTER = 312; const int UserTokenId_T_BAD_CHARACTER = 313; const int UserTokenId_T_ENCAPSED_AND_WHITESPACE = 314; const int UserTokenId_T_CONSTANT_ENCAPSED_STRING = 315; const int UserTokenId_T_ECHO = 316; const int UserTokenId_T_DO = 317; const int UserTokenId_T_WHILE = 318; const int UserTokenId_T_ENDWHILE = 319; const int UserTokenId_T_FOR = 320; const int UserTokenId_T_ENDFOR = 321; const int UserTokenId_T_FOREACH = 322; const int UserTokenId_T_ENDFOREACH = 323; const int UserTokenId_T_DECLARE = 324; const int UserTokenId_T_ENDDECLARE = 325; const int UserTokenId_T_AS = 326; const int UserTokenId_T_SWITCH = 327; const int UserTokenId_T_ENDSWITCH = 328; const int UserTokenId_T_CASE = 329; const int UserTokenId_T_DEFAULT = 330; const int UserTokenId_T_BREAK = 331; const int UserTokenId_T_GOTO = 332; const int UserTokenId_T_CONTINUE = 333; const int UserTokenId_T_FUNCTION = 334; const int UserTokenId_T_CONST = 335; const int UserTokenId_T_RETURN = 336; const int UserTokenId_T_TRY = 337; const int UserTokenId_T_CATCH = 338; const int UserTokenId_T_THROW = 339; const int UserTokenId_T_USE = 340; const int UserTokenId_T_GLOBAL = 341; const int UserTokenId_T_PUBLIC = 342; const int UserTokenId_T_PROTECTED = 343; const int UserTokenId_T_PRIVATE = 344; const int UserTokenId_T_FINAL = 345; const int UserTokenId_T_ABSTRACT = 346; const int UserTokenId_T_STATIC = 347; const int UserTokenId_T_VAR = 348; const int UserTokenId_T_UNSET = 349; const int UserTokenId_T_ISSET = 350; const int UserTokenId_T_EMPTY = 351; const int UserTokenId_T_HALT_COMPILER = 352; const int UserTokenId_T_CLASS = 353; const int UserTokenId_T_INTERFACE = 354; const int UserTokenId_T_EXTENDS = 355; const int UserTokenId_T_IMPLEMENTS = 356; const int UserTokenId_T_OBJECT_OPERATOR = 357; const int UserTokenId_T_DOUBLE_ARROW = 358; const int UserTokenId_T_LIST = 359; const int UserTokenId_T_ARRAY = 360; const int UserTokenId_T_CLASS_C = 361; const int UserTokenId_T_METHOD_C = 362; const int UserTokenId_T_FUNC_C = 363; const int UserTokenId_T_LINE = 364; const int UserTokenId_T_FILE = 365; const int UserTokenId_T_COMMENT = 366; const int UserTokenId_T_DOC_COMMENT = 367; const int UserTokenId_T_OPEN_TAG = 368; const int UserTokenId_T_OPEN_TAG_WITH_ECHO = 369; const int UserTokenId_T_CLOSE_TAG = 370; const int UserTokenId_T_WHITESPACE = 371; const int UserTokenId_T_START_HEREDOC = 372; const int UserTokenId_T_END_HEREDOC = 373; const int UserTokenId_T_DOLLAR_OPEN_CURLY_BRACES = 374; const int UserTokenId_T_CURLY_OPEN = 375; const int UserTokenId_T_PAAMAYIM_NEKUDOTAYIM UNUSED = 376; const int UserTokenId_T_NAMESPACE = 377; const int UserTokenId_T_NS_C = 378; const int UserTokenId_T_DIR = 379; const int UserTokenId_T_NS_SEPARATOR = 380; const int UserTokenId_T_YIELD = 381; const int UserTokenId_T_XHP_LABEL = 382; const int UserTokenId_T_XHP_TEXT = 383; const int UserTokenId_T_XHP_ATTRIBUTE = 384; const int UserTokenId_T_XHP_CATEGORY = 385; const int UserTokenId_T_XHP_CATEGORY_LABEL = 386; const int UserTokenId_T_XHP_CHILDREN = 387; const int UserTokenId_T_ENUM = 388; const int UserTokenId_T_XHP_REQUIRED = 389; const int UserTokenId_T_TRAIT = 390; const int UserTokenId_T_INSTEADOF = 391; const int UserTokenId_T_TRAIT_C = 392; const int UserTokenId_T_ELLIPSIS = 393; const int UserTokenId_T_HH_ERROR = 394; const int UserTokenId_T_FINALLY = 395; const int UserTokenId_T_XHP_TAG_LT = 396; const int UserTokenId_T_XHP_TAG_GT = 397; const int UserTokenId_T_TYPELIST_LT = 398; const int UserTokenId_T_TYPELIST_GT = 399; const int UserTokenId_T_UNRESOLVED_LT = 400; const int UserTokenId_T_COLLECTION = 401; const int UserTokenId_T_SHAPE = 402; const int UserTokenId_T_TYPE = 403; const int UserTokenId_T_UNRESOLVED_TYPE = 404; const int UserTokenId_T_NEWTYPE = 405; const int UserTokenId_T_UNRESOLVED_NEWTYPE = 406; const int UserTokenId_T_COMPILER_HALT_OFFSET = 407; const int UserTokenId_T_AWAIT = 408; const int UserTokenId_T_ASYNC = 409; const int UserTokenId_T_FROM = 411; const int UserTokenId_T_WHERE = 412; const int UserTokenId_T_JOIN = 413; const int UserTokenId_T_IN = 414; const int UserTokenId_T_ON = 415; const int UserTokenId_T_EQUALS = 416; const int UserTokenId_T_INTO = 417; const int UserTokenId_T_LET = 418; const int UserTokenId_T_ORDERBY = 419; const int UserTokenId_T_ASCENDING = 420; const int UserTokenId_T_DESCENDING = 421; const int UserTokenId_T_SELECT = 422; const int UserTokenId_T_GROUP = 423; const int UserTokenId_T_BY = 424; const int UserTokenId_T_LAMBDA_ARROW = 425; const int UserTokenId_T_DOUBLE_COLON = 426; const int UserTokenId_T_LAMBDA_OP = 427; const int UserTokenId_T_LAMBDA_CP = 428; const int UserTokenId_T_UNRESOLVED_OP = 429; const int UserTokenId_T_CALLABLE = 430; const int UserTokenId_T_ONUMBER = 431; const int UserTokenId_T_POW = 432; const int UserTokenId_T_POW_EQUAL = 433; const int UserTokenId_T_NULLSAFE_OBJECT_OPERATOR = 434; const int UserTokenId_T_HASHBANG = 435; const int UserTokenId_T_SUPER = 436; const int UserTokenId_T_SPACESHIP = 437; const int MaxUserTokenId = 438; // Marker, not a real user token ID #undef YYTOKENTYPE #undef YYTOKEN_MAP #undef YYTOKEN #define YYTOKEN(num, name) UserTokenId_##name, #define YYTOKEN_MAP static const int user_token_ids[] = #include "hphp/parser/hphp.tab.hpp" #undef YYTOKEN_MAP #undef YYTOKEN // Converts an internal token ID to a user token ID static int get_user_token_id(int internal_id) { assert(internal_id >= 0); if (internal_id < 256) { return internal_id; } if (internal_id >= YYTOKEN_MIN && internal_id <= YYTOKEN_MAX) { return user_token_ids[internal_id - YYTOKEN_MIN]; } return MaxUserTokenId; } /** * We cheat slightly in the lexer by turning * T_ELSE T_WHITESPACE T_IF into T_ELSEIF * * This makes the AST flatter and avoids bugs like * https://github.com/facebook/hhvm/issues/2699 */ static String token_get_all_fix_elseif(Array& res, int& tokVal, const std::string& tokText, Location& loc) { if (!strcasecmp(tokText.c_str(), "elseif")) { // Actual T_ELSEIF, continue on. return String(tokText); } // Otherwise, it's a fake elseif made from "else\s+if" auto tokCStr = tokText.c_str(); auto tokCEnd = tokCStr + tokText.size(); const auto DEBUG_ONLY checkWhitespace = [](const char* s, const char* e) { while (s < e) { if (!isspace(*(s++))) return false; } return true; }; assert(tokText.size() > strlen("elseif")); assert(!strncasecmp(tokCStr, "else", strlen("else"))); assert(checkWhitespace(tokCStr + strlen("else"), tokCEnd - strlen("if"))); assert(!strcasecmp(tokCEnd - strlen("if"), "if")); // Shove in the T_ELSE and T_WHITESPACE, then return the remaining T_IF res.append(make_packed_array( UserTokenId_T_ELSE, String(tokCStr, strlen("else"), CopyString), loc.r.line0 )); res.append(make_packed_array( UserTokenId_T_WHITESPACE, String(tokCStr + strlen("else"), tokText.size() - strlen("elseif"), CopyString), loc.r.line0 )); tokVal = UserTokenId_T_IF; // To account for newlines in the T_WHITESPACE loc.r.line0 = loc.r.line1; return String(tokCEnd - strlen("if"), CopyString); } Array HHVM_FUNCTION(token_get_all, const String& source) { Scanner scanner(source.data(), source.size(), RuntimeOption::GetScannerType() | Scanner::ReturnAllTokens); ScannerToken tok; Location loc; int tokid; Array res = Array::Create(); while ((tokid = scanner.getNextToken(tok, loc))) { loop_start: // For after seeing a T_INLINE_HTML, see below if (tokid < 256) { res.append(String::FromChar((char)tokid)); } else { String value; int tokVal = get_user_token_id(tokid); switch (tokVal) { case UserTokenId_T_XHP_LABEL: value = String(":" + tok.text()); break; case UserTokenId_T_XHP_CATEGORY_LABEL: value = String("%" + tok.text()); break; case UserTokenId_T_ELSEIF: value = token_get_all_fix_elseif(res, tokVal, tok.text(), loc); break; case UserTokenId_T_HASHBANG: // Convert T_HASHBANG to T_INLINE_HTML for Zend compatibility tokVal = UserTokenId_T_INLINE_HTML; // Fall through to merge it with following T_INLINE_HTML tokens case UserTokenId_T_INLINE_HTML: { // Consecutive T_INLINE_HTML tokens should be merged together to // match Zend behaviour. value = String(tok.text()); int line = loc.r.line0; tokid = scanner.getNextToken(tok, loc); while (tokid == T_INLINE_HTML) { value += String(tok.text()); tokid = scanner.getNextToken(tok, loc); } Array p = make_packed_array( tokVal, value, line ); res.append(p); if (tokid) { // We have a new token to deal with, jump to the beginning // of the loop, but don't fetch the next token, hence the // goto. goto loop_start; } else { // Break out otherwise we end up appending an empty token to // the end of the array return res; } break; } default: value = String(tok.text()); break; } Array p = make_packed_array( tokVal, value, loc.r.line0 ); res.append(p); } } return res; } // User token ID => Token name mapping static const char** getTokenNameTable() { static const char* table[MaxUserTokenId+1]; for (int i = 0; i <= MaxUserTokenId; ++i) { table[i] = "UNKNOWN"; } #undef YYTOKENTYPE #undef YYTOKEN_MAP #undef YYTOKEN #define YYTOKEN(num, name) table[get_user_token_id(num)] = #name; #define YYTOKEN_MAP #include "hphp/parser/hphp.tab.hpp" // nolint #undef YYTOKEN_MAP #undef YYTOKEN return table; } // Converts a user token ID to a token name String HHVM_FUNCTION(token_name, int64_t token) { static const char** table = getTokenNameTable(); if (token >= 0 && token < MaxUserTokenId) { return table[token]; } return "UNKNOWN"; } String HHVM_FUNCTION(hphp_to_string, const Variant& v) { return v.toString(); } // Adds an optimized FCallBuiltin for max with 2 operands to SystemLib Variant HHVM_FUNCTION(SystemLib_max2, const Variant& value1, const Variant& value2) { return less(value1, value2) ? value2 : value1; } // Adds an optimized FCallBuiltin for min with 2 operands to SystemLib Variant HHVM_FUNCTION(SystemLib_min2, const Variant& value1, const Variant& value2) { return more(value1, value2) ? value2 : value1; } #undef YYTOKENTYPE #undef YYTOKEN_MAP #undef YYTOKEN #define YYTOKEN_MAP static void bindTokenConstants() #define YYTOKEN(num, name) Native::registerConstant<KindOfInt64> \ (makeStaticString(#name), get_user_token_id(num)); #include "hphp/parser/hphp.tab.hpp" // nolint #undef YYTOKEN_MAP #undef YYTOKEN /////////////////////////////////////////////////////////////////////////////// }
void TrackingConfigPanel::Prepare(ContainerWindow &parent, const PixelRect &rc) { const TrackingSettings &settings = CommonInterface::GetComputerSettings().tracking; RowFormWidget::Prepare(parent, rc); #ifdef HAVE_SKYLINES_TRACKING AddBoolean(_T("SkyLines"), nullptr, settings.skylines.enabled, this); #ifdef HAVE_NET_STATE_ROAMING AddBoolean(_T("Roaming"), nullptr, settings.skylines.roaming, this); #endif AddEnum(_("Tracking Interval"), nullptr, tracking_intervals, settings.skylines.interval); AddBoolean(_("Track friends"), _("Download the position of your friends live from the SkyLines server."), settings.skylines.traffic_enabled, this); AddBoolean(_("Show nearby traffic"), _("Download the position of your nearby traffic live from the SkyLines server."), settings.skylines.near_traffic_enabled, this); StaticString<64> buffer; if (settings.skylines.key != 0) buffer.UnsafeFormat(_T("%llX"), (unsigned long long)settings.skylines.key); else buffer.clear(); AddText(_T("Key"), nullptr, buffer); #endif #if defined(HAVE_SKYLINES_TRACKING) && defined(HAVE_LIVETRACK24) AddSpacer(); #endif #ifdef HAVE_LIVETRACK24 AddBoolean(_T("LiveTrack24"), _T(""), settings.livetrack24.enabled, this); AddTime(_("Tracking Interval"), _T(""), 5, 3600, 5, settings.interval); AddEnum(_("Vehicle Type"), _("Type of vehicle used."), vehicle_type_list, (unsigned) settings.vehicleType); AddText(_("Vehicle Name"), _T("Name of vehicle used."), settings.vehicle_name); WndProperty *edit = AddEnum(_("Server"), _T(""), server_list, 0); ((DataFieldEnum *)edit->GetDataField())->Set(settings.livetrack24.server); edit->RefreshDisplay(); AddText(_("Username"), _T(""), settings.livetrack24.username); AddPassword(_("Password"), _T(""), settings.livetrack24.password); #endif #ifdef HAVE_SKYLINES_TRACKING SetSkyLinesEnabled(settings.skylines.enabled); #endif #ifdef HAVE_LIVETRACK24 SetEnabled(settings.livetrack24.enabled); #endif }
void writeExact(int fd, const StaticString &data, unsigned long long *timeout) { const char * restrict data_ptr = data.data(); writeExact(fd, data_ptr, data.size(), timeout); }
namespace HPHP { ////////////////////////////////////////////////////////////////////////////// // class Locale #define ULOC_CHECK(err, ret) \ if (U_FAILURE(err)) { \ s_intl_error->set(err, "%s", u_errorName(err)); \ return ret; \ } #define ULOC_DEFAULT(loc) (loc.empty() ? Intl::GetDefaultLocale() : loc) #define MAX_LOCALE_LEN 80 /*returns TRUE if a is an ID separator FALSE otherwise*/ #define isIDSeparator(a) (a == '_' || a == '-') #define isKeywordSeparator(a) (a == '@' ) #define isEndOfTag(a) (a == '\0' ) #define isPrefixLetter(a) ((a=='x')||(a=='X')||(a=='i')||(a=='I')) /*returns TRUE if one of the special prefixes is here (s=string) * 'x-' or 'i-' */ #define isIDPrefix(s) (isPrefixLetter(s[0])&&isIDSeparator(s[1])) #define isKeywordPrefix(s) ( isKeywordSeparator(s[0]) ) /* Dot terminates it because of POSIX form where dot precedes the codepage * * except for variant */ #define isTerminator(a) ((a==0)||(a=='.')||(a=='@')) static std::vector<std::string> g_grandfathered = { "art-lojban", "i-klingon", "i-lux", "i-navajo", "no-bok", "no-nyn", "cel-gaulish", "en-GB-oed", "i-ami", "i-bnn", "i-default", "i-enochian", "i-mingo", "i-pwn", "i-tao", "i-tay", "i-tsu", "sgn-BE-fr", "sgn-BE-nl", "sgn-CH-de", "zh-cmn", "zh-cmn-Hans", "zh-cmn-Hant", "zh-gan", "zh-guoyu", "zh-hakka", "zh-min", "zh-min-nan", "zh-wuu", "zh-xiang", "zh-yue" }; /* Preferred locale codes for the first N entries of g_grandfathered * Must be kept in sync with above. */ static std::vector<std::string> g_grandfathered_preferred = { "jbo", "tlh", "lb", "nv", "nb", "nn" }; static int getGrandfatheredOffset(const String& locale) { auto it = std::find(g_grandfathered.begin(), g_grandfathered.end(), locale.data()); if (it == g_grandfathered.end()) return -1; return (it - g_grandfathered.begin()); } static String getGrandfatheredPreferred(int ofs) { if ((ofs < 0) || (ofs >= g_grandfathered.size())) { return null_string; } if (ofs < g_grandfathered_preferred.size()) { return g_grandfathered_preferred[ofs]; } return g_grandfathered[ofs]; } enum LocaleTag { LOC_SCRIPT, LOC_LANG, LOC_REGION, LOC_VARIANT, LOC_CANONICALIZE, LOC_PRIVATE, LOC_DISPLAY, LOC_EXTLANG, }; const StaticString s_DEFAULT_LOCALE("DEFAULT_LOCALE"), s_LANG_TAG("LANG_TAG"), s_EXTLANG_TAG("EXTLANG_TAG"), s_SCRIPT_TAG("SCRIPT_TAG"), s_REGION_TAG("REGION_TAG"), s_VARIANT_TAG("VARIANT_TAG"), s_GRANDFATHERED_LANG_TAG("GRANDFATHERED_LANG_TAG"), s_PRIVATE_TAG("PRIVATE_TAG"), s_LOC_SCRIPT("script"), s_LOC_LANG("language"), s_LOC_REGION("region"), s_LOC_VARIANT("variant"), s_LOC_CANONICALIZE("canonicalize"), s_LOC_PRIVATE("private"), s_LOC_DISPLAY("display"), s_LOC_EXTLANG("extlang"), s_GRANDFATHERED("grandfathered"), s_SEPARATOR("_"), s_SEPARATOR_x("_x"); static const StaticString LocaleName(LocaleTag tag) { switch (tag) { case LOC_SCRIPT: return s_LOC_SCRIPT; case LOC_LANG: return s_LOC_LANG; case LOC_REGION: return s_LOC_REGION; case LOC_VARIANT: return s_LOC_VARIANT; case LOC_CANONICALIZE: return s_LOC_CANONICALIZE; case LOC_PRIVATE: return s_LOC_PRIVATE; case LOC_DISPLAY: return s_LOC_DISPLAY; case LOC_EXTLANG: return s_LOC_EXTLANG; } not_reached(); } static int singleton_pos(const String& str) { auto len = str.size(); for (int i = 0; i < (len - 2); ++i) { if (!isIDSeparator(str[i])) continue; if (i == 1) return 0; if (isIDSeparator(str[i+2])) return i+1; } return -1; } static Variant get_icu_value(const String &locale, LocaleTag tag, bool fromParseLocale = false) { String locale_name(locale); if (tag != LOC_CANONICALIZE) { if (getGrandfatheredOffset(locale) >= 0) { if (tag == LOC_LANG) { return locale; } return false; } if (fromParseLocale) { auto localecstr = locale.c_str(); if (tag == LOC_LANG && locale.size() > 1 && isIDPrefix(localecstr)) { return locale; } int pos = singleton_pos(locale); if (pos == 0) { return null_string; } else if (pos > 0) { locale_name = f_substr(locale, 0, pos - 1); } } } int32_t (*ulocfunc)(const char *loc, char *val, int32_t len, UErrorCode *err); switch (tag) { case LOC_SCRIPT: ulocfunc = uloc_getScript; break; case LOC_LANG: ulocfunc = uloc_getLanguage; break; case LOC_REGION: ulocfunc = uloc_getCountry; break; case LOC_VARIANT: ulocfunc = uloc_getVariant; break; case LOC_CANONICALIZE: ulocfunc = uloc_canonicalize; break; default: assert(false); } String buf(64, ReserveString); do { UErrorCode error = U_ZERO_ERROR; int32_t len = ulocfunc(locale_name.c_str(), buf->mutableData(), buf->capacity(), &error); if (error != U_BUFFER_OVERFLOW_ERROR && error != U_STRING_NOT_TERMINATED_WARNING) { if (U_FAILURE(error)) { s_intl_error->set(error, "unable to get locale info"); return false; } buf.setSize(len); return buf; } if (len <= buf->capacity()) { // Avoid infinite loop s_intl_error->set(U_INTERNAL_PROGRAM_ERROR, "Got invalid response from ICU"); return false; } buf = String(len, ReserveString); } while (true); not_reached(); return false; } static Variant get_icu_display_value(const String &locale, const String &disp_locale, LocaleTag tag) { String locname(locale); if (tag != LOC_DISPLAY) { int ofs = getGrandfatheredOffset(locale); if (ofs >= 0) { if (tag == LOC_LANG) { locname = getGrandfatheredPreferred(ofs); } else { return false; } } } int32_t (*ulocfunc)(const char *loc, const char *dloc, UChar *dest, int32_t destcap, UErrorCode *err); switch (tag) { case LOC_LANG: ulocfunc = uloc_getDisplayLanguage; break; case LOC_SCRIPT: ulocfunc = uloc_getDisplayScript; break; case LOC_REGION: ulocfunc = uloc_getDisplayCountry; break; case LOC_VARIANT: ulocfunc = uloc_getDisplayVariant; break; case LOC_DISPLAY: ulocfunc = uloc_getDisplayName; break; default: assert(false); } String buf(64 * sizeof(UChar), ReserveString); do { UErrorCode error = U_ZERO_ERROR; int32_t len = ulocfunc(locname.c_str(), disp_locale.c_str(), (UChar*)buf->mutableData(), buf->capacity() / sizeof(UChar), &error); if (error != U_BUFFER_OVERFLOW_ERROR && error != U_STRING_NOT_TERMINATED_WARNING) { if (U_FAILURE(error)) { s_intl_error->set(error, "locale_get_display_%s : unable to " "get locale %s", LocaleName(tag).c_str(), LocaleName(tag).c_str()); return false; } buf.setSize(len * sizeof(UChar)); error = U_ZERO_ERROR; String out(Intl::u8(buf, error)); if (U_FAILURE(error)) { s_intl_error->set(error, "Unable to convert result from " "locale_get_display_%s to UTF-8", LocaleName(tag).c_str()); return false; } return out; } if (len <= (buf->capacity() / sizeof(UChar))) { // Avoid infinite loop s_intl_error->set(U_INTERNAL_PROGRAM_ERROR, "Got invalid response from ICU"); return false; } buf = String(len * sizeof(UChar), ReserveString); } while (true); not_reached(); return false; } static Variant HHVM_STATIC_METHOD(Locale, acceptFromHttp, const String& header) { UErrorCode error = U_ZERO_ERROR; UEnumeration *avail = ures_openAvailableLocales(nullptr, &error); ULOC_CHECK(error, false); char out[MAX_LOCALE_LEN]; UAcceptResult result; error = U_ZERO_ERROR; int len = uloc_acceptLanguageFromHTTP(out, sizeof(out), &result, header.c_str(), avail, &error); uenum_close(avail); ULOC_CHECK(error, false); if (len < 0 || result == ULOC_ACCEPT_FAILED) { return false; } return String(out, len, CopyString); } static Variant HHVM_STATIC_METHOD(Locale, canonicalize, const String& locale) { return get_icu_value(ULOC_DEFAULT(locale), LOC_CANONICALIZE); } inline void element_not_string() { s_intl_error->set(U_ILLEGAL_ARGUMENT_ERROR, "locale_compose: parameter array element is not a string: " "U_ILLEGAL_ARGUMENT_ERROR"); } static bool append_key_value(String& ret, CArrRef subtags, LocaleTag tag) { auto name = LocaleName(tag); if (!subtags.exists(name)) return true; auto val = subtags[name]; if (!val.isString()) { element_not_string(); return false; } ret += s_SEPARATOR + val.toString(); return true; } static bool append_multiple_key_values(String& ret, CArrRef subtags, LocaleTag tag) { auto name = LocaleName(tag); if (subtags.exists(name)) { // Sane version: [tag] => string, [tag] => array<tring> auto val = subtags[name]; if (val.isString()) { if (tag == LOC_PRIVATE) { ret += s_SEPARATOR_x; } ret += s_SEPARATOR + val.toString(); return true; } if (!val.isArray()) { return false; } bool first = true; for (ArrayIter it(val.toArray()); it; ++it) { auto v = it.second(); if (!v.isString()) { element_not_string(); return false; } if (first) { if (tag == LOC_PRIVATE) { ret += s_SEPARATOR + "x"; } first = false; } ret += s_SEPARATOR + v.toString(); } return true; } // clowny version [tag$n] => string // Only extlang, variant, and private if (tag != LOC_EXTLANG && tag != LOC_VARIANT && tag != LOC_PRIVATE) { return true; } int max = (tag == LOC_EXTLANG) ? 3 : 15; bool first = true; for (int i = 0; i < max; ++i) { auto namenum = name + String(i, CopyString); if (!subtags.exists(namenum)) continue; auto val = subtags[namenum]; if (!val.isString()) { element_not_string(); return false; } if (first) { if (tag == LOC_PRIVATE) { ret += s_SEPARATOR + "x"; } first = false; } ret += s_SEPARATOR + val.toString(); } return true; } static Variant HHVM_STATIC_METHOD(Locale, composeLocale, CArrRef subtags) { s_intl_error->clear(); if (subtags.exists(s_GRANDFATHERED)) { auto val = subtags[s_GRANDFATHERED]; if (val.isString()) { return val; } } if (!subtags.exists(s_LOC_LANG)) { s_intl_error->set(U_ILLEGAL_ARGUMENT_ERROR, "locale_compose: " "parameter array does not contain 'language' tag.: " "U_ILLEGAL_ARGUMENT_ERROR"); return false; } String ret(subtags[s_LOC_LANG].toString()); if (!append_multiple_key_values(ret, subtags, LOC_EXTLANG) || !append_key_value(ret, subtags, LOC_SCRIPT) || !append_key_value(ret, subtags, LOC_REGION) || !append_multiple_key_values(ret, subtags, LOC_VARIANT) || !append_multiple_key_values(ret, subtags, LOC_PRIVATE)) { return false; } return ret; } static Array HHVM_STATIC_METHOD(Locale, getAllVariants, const String& locale) { Variant val = get_icu_value(ULOC_DEFAULT(locale), LOC_VARIANT); String strval = val.toString(); if (strval.empty()) { return null_array; } Array ret = Array::Create(); const char *s = strval.c_str(), *e = s + strval.size(), *p; for (p = s; p < e; ++p) { if (!isIDSeparator(*p)) continue; if ((p - s) <= 1) { return ret; } ret.append(String(s, p - s, CopyString)); s = p + 1; } if ((e - s) > 1) { ret.append(String(s, e - s, CopyString)); } return ret; } static String HHVM_STATIC_METHOD(Locale, getDefault) { return Intl::GetDefaultLocale(); } static String HHVM_STATIC_METHOD(Locale, getDisplayLanguage, const String& locale, const String& in_locale) { return get_icu_display_value(ULOC_DEFAULT(locale), ULOC_DEFAULT(in_locale), LOC_LANG); } static String HHVM_STATIC_METHOD(Locale, getDisplayName, const String& locale, const String& in_locale) { return get_icu_display_value(ULOC_DEFAULT(locale), ULOC_DEFAULT(in_locale), LOC_DISPLAY); } static String HHVM_STATIC_METHOD(Locale, getDisplayRegion, const String& locale, const String& in_locale) { return get_icu_display_value(ULOC_DEFAULT(locale), ULOC_DEFAULT(in_locale), LOC_REGION); } static String HHVM_STATIC_METHOD(Locale, getDisplayScript, const String& locale, const String& in_locale) { return get_icu_display_value(ULOC_DEFAULT(locale), ULOC_DEFAULT(in_locale), LOC_SCRIPT); } static String HHVM_STATIC_METHOD(Locale, getDisplayVariant, const String& locale, const String& in_locale) { return get_icu_display_value(ULOC_DEFAULT(locale), ULOC_DEFAULT(in_locale), LOC_VARIANT); } static Array HHVM_STATIC_METHOD(Locale, getKeywords, const String& locale) { UErrorCode error = U_ZERO_ERROR; String locname = ULOC_DEFAULT(locale); UEnumeration *e = uloc_openKeywords(locname.c_str(), &error); if (!e) return null_array; Array ret = Array::Create(); const char *key; int key_len; String val(128, ReserveString); char *ptr = val->mutableData(); error = U_ZERO_ERROR; while ((key = uenum_next(e, &key_len, &error))) { tryagain: error = U_ZERO_ERROR; int val_len = uloc_getKeywordValue(locname.c_str(), key, ptr, val->capacity(), &error); if (error == U_BUFFER_OVERFLOW_ERROR) { val = String(val_len + 128, ReserveString); ptr = val->mutableData(); goto tryagain; } if (U_FAILURE(error)) { s_intl_error->set(error, "locale_get_keywords: Error encountered while " "getting the keyword value for the keyword"); return null_array; } ret.set(String(key, key_len, CopyString), String(ptr, val_len, CopyString)); } return ret; } static String HHVM_STATIC_METHOD(Locale, getPrimaryLanguage, const String& locale) { return get_icu_value(ULOC_DEFAULT(locale), LOC_LANG); } static Variant HHVM_STATIC_METHOD(Locale, getRegion, const String& locale) { return get_icu_value(ULOC_DEFAULT(locale), LOC_REGION); } static Variant HHVM_STATIC_METHOD(Locale, getScript, const String& locale) { return get_icu_value(ULOC_DEFAULT(locale), LOC_SCRIPT); } static String locale_suffix_strip(const String& locale) { for (int i = locale.size(); i >= 0; --i) { if (isIDSeparator(locale[i])) { if ((i>=2) && isIDSeparator(locale[i-2])) { return f_substr(locale, 0, i - 2); } else { return f_substr(locale, 0, i); } } } return null_string; } inline void normalize_for_match(String& v) { for (char *ptr = v->mutableData(), *end = ptr + v.size(); ptr < end; ++ptr) { if (*ptr == '-') { *ptr = '_'; } else { *ptr = tolower(*ptr); } } v->invalidateHash(); } static String HHVM_STATIC_METHOD(Locale, lookup, CArrRef langtag, const String& locale, bool canonicalize, const String& def) { String locname(ULOC_DEFAULT(locale), CopyString); std::vector<std::pair<String,String>> cur_arr; for (ArrayIter iter(langtag); iter; ++iter) { auto val = iter.second(); if (!val.isString()) { s_intl_error->set(U_ILLEGAL_ARGUMENT_ERROR, "lookup_loc_range: " "locale array element is not a string"); return def; } String normalized(val.toString(), CopyString); normalize_for_match(normalized); if (canonicalize) { normalized = get_icu_value(normalized, LOC_CANONICALIZE); if (normalized.isNull()) { s_intl_error->set(U_ILLEGAL_ARGUMENT_ERROR, "lookup_loc_range: " "unable to canonicalize lang_tag"); return def; } normalize_for_match(normalized); } cur_arr.push_back(std::make_pair(val,normalized)); } if (canonicalize) { locname = get_icu_value(locname, LOC_CANONICALIZE); if (locname.isNull()) { s_intl_error->set(U_ILLEGAL_ARGUMENT_ERROR, "lookup_loc_range: " "unable to canonicalize loc_range"); return def; } } normalize_for_match(locname); while (locname.size() > 0) { for (auto &p : cur_arr) { if (locname.same(p.second)) { return canonicalize ? p.second : p.first; } } locname = locale_suffix_strip(locname); } return def; } static Variant get_private_subtags(const String& locname) { if (locname.empty()) return uninit_null(); String locale(locname); int pos; while ((pos = singleton_pos(locale)) >= 0) { if ((locale[pos] == 'x') || (locale[pos] == 'X')) { if ((pos + 2) == locale.size()) { /* loc_name ends with '-x-' */ return uninit_null(); } return f_substr(locale, pos); } if ((pos + 1) >= locale.size()) { return uninit_null(); } locale = f_substr(locale, pos + 1); } return uninit_null(); } static void add_array_entry(Array& ret, const String& locname, LocaleTag tag) { Variant val; if (tag == LOC_PRIVATE) { val = get_private_subtags(locname); } else { val = get_icu_value(locname, tag, true); } if (val.isNull()) return; String strval = val.toString(); if (strval.empty()) { return; } auto name = LocaleName(tag); if ((tag != LOC_PRIVATE) && (tag != LOC_VARIANT)) { ret.set(name, strval); return; } const char *s = strval.c_str(), *e = s + strval.size(), *p; int cnt; for (cnt = 0, p = s; p < e; ++p) { if (!isIDSeparator(*p)) continue; if ((p - s) > 1) { ret.set(name + String(cnt++), String(s, p - s, CopyString)); } s = p + 1; } if ((e - s) > 1) { ret.set(name + String(cnt++), String(s, e - s, CopyString)); } } static Array HHVM_STATIC_METHOD(Locale, parseLocale, const String& locale) { String locname = ULOC_DEFAULT(locale); Array ret = Array::Create(); if (std::find(g_grandfathered.begin(), g_grandfathered.end(), locale.data()) != g_grandfathered.end()) { ret.set(s_GRANDFATHERED, locname); return ret; } add_array_entry(ret, locname, LOC_LANG); add_array_entry(ret, locname, LOC_SCRIPT); add_array_entry(ret, locname, LOC_REGION); add_array_entry(ret, locname, LOC_VARIANT); add_array_entry(ret, locname, LOC_PRIVATE); return ret; } static bool HHVM_STATIC_METHOD(Locale, setDefault, const String& locale) { return Intl::SetDefaultLocale(locale); } ////////////////////////////////////////////////////////////////////////////// const StaticString s_Locale("Locale"); void Intl::IntlExtension::initLocale() { HHVM_STATIC_ME(Locale, acceptFromHttp); HHVM_STATIC_ME(Locale, canonicalize); HHVM_STATIC_ME(Locale, composeLocale); HHVM_STATIC_ME(Locale, getAllVariants); HHVM_STATIC_ME(Locale, getDefault); HHVM_STATIC_ME(Locale, getDisplayLanguage); HHVM_STATIC_ME(Locale, getDisplayName); HHVM_STATIC_ME(Locale, getDisplayRegion); HHVM_STATIC_ME(Locale, getDisplayScript); HHVM_STATIC_ME(Locale, getDisplayVariant); HHVM_STATIC_ME(Locale, getKeywords); HHVM_STATIC_ME(Locale, getPrimaryLanguage); HHVM_STATIC_ME(Locale, getRegion); HHVM_STATIC_ME(Locale, getScript); HHVM_STATIC_ME(Locale, lookup); HHVM_STATIC_ME(Locale, parseLocale); HHVM_STATIC_ME(Locale, setDefault); #define ULOC_CONST(nm,val) Native::registerClassConstant<KindOfStaticString>\ (s_Locale.get(), s_##nm.get(), s_##val.get()) Native::registerClassConstant<KindOfNull>(s_Locale.get(), s_DEFAULT_LOCALE.get()); ULOC_CONST(LANG_TAG, LOC_LANG); ULOC_CONST(EXTLANG_TAG, LOC_EXTLANG); ULOC_CONST(SCRIPT_TAG, LOC_SCRIPT); ULOC_CONST(REGION_TAG, LOC_REGION); ULOC_CONST(VARIANT_TAG, LOC_VARIANT); ULOC_CONST(GRANDFATHERED_LANG_TAG, GRANDFATHERED); ULOC_CONST(PRIVATE_TAG, LOC_PRIVATE); #undef ULOC_CONST loadSystemlib("icu_locale"); } ////////////////////////////////////////////////////////////////////////////// } // namespace HPHP
namespace HPHP { Class* Generator::s_class = nullptr; const StaticString Generator::s_className("Generator"); Generator::Generator() : m_index(-1LL) , m_key(make_tv<KindOfInt64>(-1LL)) , m_value(make_tv<KindOfNull>()) , m_delegate(make_tv<KindOfNull>()) { } Generator::~Generator() { if (LIKELY(getState() == State::Done)) { return; } assert(getState() != State::Running); tvRefcountedDecRef(m_key); tvRefcountedDecRef(m_value); tvRefcountedDecRef(m_delegate); // Free locals, but don't trigger the EventHook for FunctionReturn since // the generator has already been exited. We don't want redundant calls. ActRec* ar = actRec(); frame_free_locals_inl_no_hook<false>(ar, ar->func()->numLocals()); } Generator& Generator::operator=(const Generator& other) { auto const fp = other.actRec(); const size_t numSlots = fp->func()->numSlotsInFrame(); const size_t frameSz = Resumable::getFrameSize(numSlots); const size_t genSz = genSize(sizeof(Generator), frameSz); resumable()->initialize<true>(fp, other.resumable()->resumeAddr(), other.resumable()->resumeOffset(), frameSz, genSz); copyVars(fp); setState(other.getState()); m_index = other.m_index; cellSet(other.m_key, m_key); cellSet(other.m_value, m_value); cellSet(other.m_delegate, m_delegate); return *this; } void Generator::copyVars(const ActRec* srcFp) { const auto dstFp = actRec(); const auto func = dstFp->func(); assert(srcFp->func() == dstFp->func()); for (Id i = 0; i < func->numLocals(); ++i) { tvDupFlattenVars(frame_local(srcFp, i), frame_local(dstFp, i)); } if (dstFp->hasThis()) { dstFp->getThis()->incRefCount(); } if (LIKELY(!(srcFp->func()->attrs() & AttrMayUseVV))) return; if (LIKELY(srcFp->m_varEnv == nullptr)) return; if (srcFp->hasExtraArgs()) { dstFp->setExtraArgs(srcFp->getExtraArgs()->clone(dstFp)); } else { assert(srcFp->hasVarEnv()); dstFp->setVarEnv(srcFp->getVarEnv()->clone(dstFp)); } } void Generator::yield(Offset resumeOffset, const Cell* key, const Cell value) { assert(isRunning()); resumable()->setResumeAddr(nullptr, resumeOffset); if (key) { cellSet(*key, m_key); tvRefcountedDecRefNZ(*key); if (m_key.m_type == KindOfInt64) { int64_t new_index = m_key.m_data.num; m_index = new_index > m_index ? new_index : m_index; } } else { cellSet(make_tv<KindOfInt64>(++m_index), m_key); } cellSet(value, m_value); tvRefcountedDecRefNZ(value); setState(State::Started); } void Generator::done(TypedValue tv) { assert(isRunning()); cellSetNull(m_key); cellSet(*tvToCell(&tv), m_value); setState(State::Done); } bool Generator::successfullyFinishedExecuting() { // `getReturn` needs to know whether a generator finished successfully or // whether an exception occurred during its execution. For every other use // case a failed generator was identical to one that finished executing, but // `getReturn` wants to throw an exception if the generator threw an // exception. Since we use the same variable to store the yield result and // the return value, and since we dont have a separate state to represent a // failed generator, we use an unintialized value to flag that the generator // failed (rather than NULL, which we use for a successful generator without // a return value). return getState() == State::Done && m_value.m_type != KindOfUninit; } const StaticString s__closure_("{closure}"); String HHVM_METHOD(Generator, getOrigFuncName) { Generator* gen = Native::data<Generator>(this_); const Func* origFunc = gen->actRec()->func(); auto const origName = origFunc->isClosureBody() ? s__closure_.get() : origFunc->name(); assert(origName->isStatic()); return String(const_cast<StringData*>(origName)); } String HHVM_METHOD(Generator, getCalledClass) { Generator* gen = Native::data<Generator>(this_); String called_class; if (gen->actRec()->hasThis()) { called_class = gen->actRec()->getThis()->getVMClass()->name()->data(); } else if (gen->actRec()->hasClass()) { called_class = gen->actRec()->getClass()->name()->data(); } else { called_class = empty_string(); } return called_class; } /////////////////////////////////////////////////////////////////////////////// class GeneratorExtension final : public Extension { public: GeneratorExtension() : Extension("generator") {} void moduleInit() override { HHVM_ME(Generator, getOrigFuncName); HHVM_ME(Generator, getCalledClass); Native::registerNativeDataInfo<Generator>( Generator::s_className.get(), Native::NDIFlags::NO_SWEEP); loadSystemlib("generator"); Generator::s_class = Unit::lookupClass(Generator::s_className.get()); assert(Generator::s_class); } }; static GeneratorExtension s_generator_extension; /////////////////////////////////////////////////////////////////////////////// }
bool is_collection_method_returning_this(const php::Class* cls, const php::Func* func) { if (!cls) return false; if (cls->name->isame(s_Vector.get())) { return func->name->isame(s_add.get()) || func->name->isame(s_addall.get()) || func->name->isame(s_append.get()) || func->name->isame(s_clear.get()) || func->name->isame(s_removekey.get()) || func->name->isame(s_set.get()) || func->name->isame(s_setall.get()); } if (cls->name->isame(s_Map.get())) { return func->name->isame(s_add.get()) || func->name->isame(s_addall.get()) || func->name->isame(s_clear.get()) || func->name->isame(s_remove.get()) || func->name->isame(s_set.get()) || func->name->isame(s_setall.get()); } if (cls->name->isame(s_Set.get())) { return func->name->isame(s_add.get()) || func->name->isame(s_addall.get()) || func->name->isame(s_clear.get()) || func->name->isame(s_remove.get()) || func->name->isame(s_removeall.get()); } return false; }
bool interface_supports_string(const StringData* s) { return s->isame(s_XHPChild.get()) || s->isame(s_Stringish.get()); }
static bool modify_extract_name(VarEnv* v, String& name, int64_t extract_type, const String& prefix) { switch (extract_type) { case EXTR_SKIP: if (v->lookup(name.get()) != nullptr) { return false; } break; case EXTR_IF_EXISTS: if (v->lookup(name.get()) == nullptr) { return false; } else { goto namechecks; } break; case EXTR_PREFIX_SAME: if (v->lookup(name.get()) != nullptr) { name = prefix + "_" + name; } else { goto namechecks; } break; case EXTR_PREFIX_ALL: name = prefix + "_" + name; break; case EXTR_PREFIX_INVALID: if (!is_valid_var_name(name.get()->data(), name.size())) { name = prefix + "_" + name; } else { goto namechecks; } break; case EXTR_PREFIX_IF_EXISTS: if (v->lookup(name.get()) == nullptr) { return false; } name = prefix + "_" + name; break; case EXTR_OVERWRITE: namechecks: if (name == s_GLOBALS) { return false; } if (name == s_this) { // Only disallow $this when inside a non-static method, or a static method // that has defined $this (matches Zend) auto const func = arGetContextFunc(GetCallerFrame()); if (func && func->isMethod() && v->lookup(s_this.get()) != nullptr) { return false; } } default: break; } // skip invalid variable names, as in PHP return is_valid_var_name(name.get()->data(), name.size()); }
bool interface_supports_double(const StringData* s) { return (s->isame(s_XHPChild.get())); }
static void LoadString(const char *bytes, size_t length, StaticString<size> &dest) { return LoadString(bytes, length, dest.buffer(), dest.MAX_SIZE); }
namespace HPHP { /////////////////////////////////////////////////////////////////////////////// const StaticString s_filter("filter"); const StaticString s_onCreate("onCreate"); const StaticString s_onClose("onClose"); const StaticString s_bucket_class("__SystemLib\\StreamFilterBucket"); /////////////////////////////////////////////////////////////////////////////// const StaticString s_default_filters_register_func( "__SystemLib\\register_default_stream_filters"); class StreamUserFilters : public RequestEventHandler { public: virtual ~StreamUserFilters() {} Array m_registeredFilters; bool registerFilter(const String& name, const String& class_name) { if (m_registeredFilters.exists(name)) { return false; } m_registeredFilters.add(name, class_name); return true; } Variant prepend(const Resource& stream, const String& filtername, const Variant& readwrite, const Variant& params) { return appendOrPrependFilter(stream, filtername, readwrite, params, /* append = */ false); } Variant append(const Resource& stream, const String& filtername, const Variant& readwrite, const Variant& params) { return appendOrPrependFilter(stream, filtername, readwrite, params, /* append = */ true); } virtual void requestInit() { vm_call_user_func(s_default_filters_register_func, empty_array_ref); } virtual void requestShutdown() { m_registeredFilters.detach(); } private: Variant appendOrPrependFilter(const Resource& stream, const String& filtername, const Variant& readwrite, const Variant& params, bool append) { const char* func_name = append ? "stream_filter_append()" : "stream_filter_prepend()"; if (!m_registeredFilters.exists(filtername)) { raise_warning("%s: unable to locate filter \"%s\"", func_name, filtername.data()); return false; } auto file = stream.getTyped<File>(); assert(file); int mode = readwrite.toInt32(); if (!mode) { auto str = file->getMode(); /* The documentation says a read filter is only created for 'r' and '+' * modes, but the implementation will always create one unless * STREAM_FILTER_WRITE is passed. * * This branch is only executed if no STREAM_FILTER* args were passed, * so we always create a READ filter. */ mode = k_STREAM_FILTER_READ; if (str.find('+') != -1 || str.find('w') != -1 || str.find('a') != -1) { mode |= k_STREAM_FILTER_WRITE; } } if (!(mode & k_STREAM_FILTER_ALL)) { return false; } // If it's ALL we create two resources, but only return one - this // matches Zend, and is the documented behavior. Resource ret; if (mode & k_STREAM_FILTER_READ) { auto resource = createInstance(func_name, stream, filtername, params); if (resource.isNull()) { return false; } ret = resource; if (append) { file->appendReadFilter(resource); } else { file->prependReadFilter(resource); } } if (mode & k_STREAM_FILTER_WRITE) { auto resource = createInstance(func_name, stream, filtername, params); if (resource.isNull()) { return false; } ret = resource; if (append) { file->appendWriteFilter(resource); } else { file->prependWriteFilter(resource); } } return ret; } Resource createInstance(const char* php_func, const Resource& stream, const String& filter, const Variant& params) { auto class_name = m_registeredFilters.rvalAt(filter).asCStrRef(); Class* class_ = Unit::getClass(class_name.get(), true); Object obj = Object(); if (LIKELY(class_ != nullptr)) { PackedArrayInit ctor_args(3); ctor_args.append(stream); ctor_args.append(filter); ctor_args.append(params); obj = g_context->createObject(class_name.get(), ctor_args.toArray()); auto created = obj->o_invoke(s_onCreate, Array::Create()); /* - true: documented value for success * - null: undocumented default successful value * - false: documented value for failure */ if (!(created.isNull() || created.toBoolean())) { obj.reset(); } } else { raise_warning("%s: user-filter \"%s\" requires class \"%s\", but that " "class " "is not defined", php_func, filter.data(), class_name.data()); // Fall through, as to match Zend, the warning below should also be raised } if (obj.isNull()) { raise_warning("%s: unable to create or locate filter \"%s\"", php_func, filter.data()); return Resource(); } return Resource(newres<StreamFilter>(obj, stream)); } }; IMPLEMENT_STATIC_REQUEST_LOCAL(StreamUserFilters, s_stream_user_filters); /////////////////////////////////////////////////////////////////////////////// // StreamFilter int64_t StreamFilter::invokeFilter(Resource in, Resource out, bool closing) { auto consumedTV = make_tv<KindOfInt64>(0); auto consumedRef = RefData::Make(consumedTV); PackedArrayInit params(4); params.append(in); params.append(out); params.append(consumedRef); params.append(closing); return m_filter->o_invoke(s_filter, params.toArray()).toInt64(); } void StreamFilter::invokeOnClose() { m_filter->o_invoke(s_onClose, Array::Create()); } bool StreamFilter::remove() { if (m_stream.isNull()) { return false; } auto file = m_stream.getTyped<File>(); assert(file); Resource rthis(this); auto ret = file->removeFilter(rthis); m_stream.reset(); return ret; } /////////////////////////////////////////////////////////////////////////////// // BucketBrigade BucketBrigade::BucketBrigade(const String& data) { PackedArrayInit ai(2); ai.append(data); ai.append(data.length()); auto bucket = g_context->createObject(s_bucket_class.get(), ai.toArray()); appendBucket(bucket); } void BucketBrigade::appendBucket(const Object& bucket) { m_buckets.push_back(bucket); } void BucketBrigade::prependBucket(const Object& bucket) { m_buckets.push_front(bucket); } Object BucketBrigade::popFront() { if (m_buckets.empty()) { return Object(); } auto bucket = m_buckets.front(); m_buckets.pop_front(); return bucket; } String BucketBrigade::createString() { StringBuffer buffer; for (auto& bucket_obj: m_buckets) { buffer.append(bucket_obj.toString()); } return buffer.detach(); } /////////////////////////////////////////////////////////////////////////////// bool HHVM_FUNCTION(stream_filter_register, const String& name, const String& classname) { return s_stream_user_filters.get()->registerFilter(name, classname); } Array HHVM_FUNCTION(stream_get_filters) { auto filters = s_stream_user_filters.get()->m_registeredFilters; if (UNLIKELY(filters.isNull())) { return empty_array(); } return array_keys_helper(filters).toArray(); } Variant HHVM_FUNCTION(stream_filter_append, const Resource& stream, const String& filtername, const Variant& readwrite, const Variant& params) { return s_stream_user_filters.get()->append(stream, filtername, readwrite, params); } Variant HHVM_FUNCTION(stream_filter_prepend, const Resource& stream, const String& filtername, const Variant& readwrite, const Variant& params) { return s_stream_user_filters.get()->prepend(stream, filtername, readwrite, params); } bool HHVM_FUNCTION(stream_filter_remove, const Resource& resource) { auto filter = resource.getTyped<StreamFilter>(); assert(filter); return filter->remove(); } Variant HHVM_FUNCTION(stream_bucket_make_writeable, const Resource& bb_res) { auto brigade = bb_res.getTyped<BucketBrigade>(); assert(brigade); auto ret = brigade->popFront(); return ret; } void HHVM_FUNCTION(stream_bucket_append, const Resource& bb_res, const Object& bucket) { auto brigade = bb_res.getTyped<BucketBrigade>(); assert(brigade); brigade->appendBucket(bucket); } void HHVM_FUNCTION(stream_bucket_prepend, const Resource& bb_res, const Object& bucket) { auto brigade = bb_res.getTyped<BucketBrigade>(); assert(brigade); brigade->prependBucket(bucket); } const StaticString s_STREAM_FILTER_READ("STREAM_FILTER_READ"), s_STREAM_FILTER_WRITE("STREAM_FILTER_WRITE"), s_STREAM_FILTER_ALL("STREAM_FILTER_ALL"); void StandardExtension::initStreamUserFilters() { #define SFCNS(v) Native::registerConstant<KindOfInt64> \ (s_STREAM_FILTER_##v.get(), k_STREAM_FILTER_##v) SFCNS(READ); SFCNS(WRITE); SFCNS(ALL); #undef SFCNS HHVM_FE(stream_get_filters); HHVM_FE(stream_filter_register); HHVM_FE(stream_filter_append); HHVM_FE(stream_filter_prepend); HHVM_FE(stream_filter_remove); HHVM_FE(stream_bucket_make_writeable); HHVM_FE(stream_bucket_append); HHVM_FE(stream_bucket_prepend); loadSystemlib("stream-user-filters"); } /////////////////////////////////////////////////////////////////////////////// }
namespace HPHP { /////////////////////////////////////////////////////////////////////////////// p_Continuation f_hphp_create_continuation(CStrRef clsname, CStrRef funcname, CStrRef origFuncName, CArrRef args /* = null_array */) { throw_fatal("Invalid call hphp_create_continuation"); return NULL; } /////////////////////////////////////////////////////////////////////////////// static StaticString s___cont__("__cont__"); c_Continuation::c_Continuation(Class* cb) : ExtObjectData(cb), m_label(0), m_index(-1LL), m_value(Variant::NullInit()), m_received(Variant::NullInit()), m_origFunc(nullptr) { o_subclassData.u16 = 0; } c_Continuation::~c_Continuation() { ActRec* ar = actRec(); // The first local is the object itself, and it wasn't increffed at creation // time (see createContinuation()). Overwrite its type to exempt it from // refcounting here. TypedValue* contLocal = frame_local(ar, 0); assert(contLocal->m_data.pobj == this); contLocal->m_type = KindOfNull; if (ar->hasVarEnv()) { ar->getVarEnv()->detach(ar); } else { frame_free_locals_inl(ar, ar->m_func->numLocals()); } } void c_Continuation::t___construct() {} void c_Continuation::t_update(int64_t label, CVarRef value) { m_label = label; assert(m_label == label); // check m_label for truncation m_value.assignVal(value); } Object c_Continuation::t_getwaithandle() { if (m_waitHandle.isNull()) { c_ContinuationWaitHandle::Create(this); assert(!m_waitHandle.isNull()); } return m_waitHandle; } int64_t c_Continuation::t_getlabel() { return m_label; } Variant c_Continuation::t_current() { const_assert(false); return m_value; } int64_t c_Continuation::t_key() { startedCheck(); return m_index; } bool c_Continuation::php_sleep(Variant &ret) { ret = false; return true; } void c_Continuation::t_next() { const_assert(false); } static StaticString s_next("next"); void c_Continuation::t_rewind() { this->o_invoke_few_args(s_next, 0); } bool c_Continuation::t_valid() { const_assert(false); return !done(); } void c_Continuation::t_send(CVarRef v) { const_assert(false); } void c_Continuation::t_raise(CVarRef v) { const_assert(false); } String c_Continuation::t_getorigfuncname() { static auto const closureName = StringData::GetStaticString("{closure}"); auto const origName = m_origFunc->isClosureBody() ? closureName : m_origFunc->name(); assert(origName->isStatic()); return String(const_cast<StringData*>(origName)); } String c_Continuation::t_getcalledclass() { String called_class; if (actRec()->hasThis()) { called_class = actRec()->getThis()->getVMClass()->name()->data(); } else if (actRec()->hasClass()) { called_class = actRec()->getClass()->name()->data(); } else { called_class = empty_string; } return called_class; } Variant c_Continuation::t___clone() { throw_fatal( "Trying to clone an uncloneable object of class Continuation"); return uninit_null(); } namespace { StaticString s_send("send"); StaticString s_raise("raise"); } void c_Continuation::call_next() { const HPHP::Func* func = m_cls->lookupMethod(s_next.get()); g_vmContext->invokeContFunc(func, this); } void c_Continuation::call_send(TypedValue* v) { const HPHP::Func* func = m_cls->lookupMethod(s_send.get()); g_vmContext->invokeContFunc(func, this, v); } void c_Continuation::call_raise(ObjectData* e) { assert(e); assert(e->instanceof(SystemLib::s_ExceptionClass)); const HPHP::Func* func = m_cls->lookupMethod(s_raise.get()); TypedValue arg; arg.m_type = KindOfObject; arg.m_data.pobj = e; g_vmContext->invokeContFunc(func, this, &arg); } /////////////////////////////////////////////////////////////////////////////// c_DummyContinuation::c_DummyContinuation(Class* cb) : ExtObjectData(cb) { } c_DummyContinuation::~c_DummyContinuation() {} void c_DummyContinuation::t___construct() { } Variant c_DummyContinuation::t_current() { throw_fatal("Tring to use a DummyContinuation"); return uninit_null(); } int64_t c_DummyContinuation::t_key() { throw_fatal("Tring to use a DummyContinuation"); return 0; } void c_DummyContinuation::t_next() { throw_fatal("Tring to use a DummyContinuation"); } void c_DummyContinuation::t_rewind() { throw_fatal("Tring to use a DummyContinuation"); } bool c_DummyContinuation::t_valid() { throw_fatal("Tring to use a DummyContinuation"); return false; } /////////////////////////////////////////////////////////////////////////////// }
namespace HPHP { /////////////////////////////////////////////////////////////////////////////// namespace { size_t getMemSize(const TypedValue* tv) { const auto& v = tvAsCVarRef(tv); auto type = v.getType(); if (!IS_REFCOUNTED_TYPE(type)) { return sizeof(Variant); } if (type == KindOfString) { return getMemSize(v.getStringData()); } if (type == KindOfArray) { return getMemSize(v.getArrayData()); } assert(!"Unsupported Variant type for getMemSize()"); return 0; } } size_t getMemSize(const APCHandle* handle) { auto t = handle->getType(); if (!IS_REFCOUNTED_TYPE(t)) { return sizeof(APCHandle); } if (t == KindOfString) { if (handle->isUncounted()) { return sizeof(APCTypedValue) + getMemSize(APCTypedValue::fromHandle(handle)->getStringData()); } return getMemSize(APCString::fromHandle(handle)); } if (t == KindOfArray) { if (handle->isSerializedArray()) { return getMemSize(APCString::fromHandle(handle)); } if (handle->isUncounted()) { return sizeof(APCTypedValue) + getMemSize(APCTypedValue::fromHandle(handle)->getArrayData()); } return getMemSize(APCArray::fromHandle(handle)); } if (t == KindOfObject) { if (handle->isCollection()) { return getMemSize(APCCollection::fromHandle(handle)); } if (handle->isObj()) { return getMemSize(APCObject::fromHandle(handle)); } return getMemSize(APCString::fromHandle(handle)); } assert(!"Unsupported APCHandle Type in getMemSize"); return 0; } size_t getMemSize(const APCArray* arr) { auto memSize = sizeof(APCArray); auto size = arr->size(); if (arr->isPacked()) { memSize += sizeof(APCHandle*) * size; for (auto i = 0; i < size; i++) { memSize += getMemSize(arr->getValue(i)); } } else { memSize += sizeof(int) * (arr->m.m_capacity_mask + 1) + sizeof(APCArray::Bucket) * size; auto b = arr->buckets(); for (auto i = 0; i < size; i++) { memSize += getMemSize(b[i].key); memSize += getMemSize(b[i].val); } } return memSize; } size_t getMemSize(const APCObject* obj) { auto size = sizeof(APCObject) + sizeof(APCObject::Prop) * obj->m_propCount; auto prop = obj->props(); auto const propEnd = prop + obj->m_propCount; // we don't add property names and class names (or Class*) in Prop // assuming that is static data not owned or accounted by the APCObject for (; prop != propEnd; ++prop) { if (prop->val) size += getMemSize(prop->val); } return size; } inline size_t getMemSize(const APCCollection* obj) { return sizeof(APCCollection) + obj->m_size; } size_t getMemSize(const ArrayData* arr) { switch (arr->kind()) { case ArrayData::ArrayKind::kPackedKind: { auto size = sizeof(ArrayData) + (packedCodeToCap(arr->m_packedCapCode) - arr->m_size) * sizeof(TypedValue); auto const values = reinterpret_cast<const TypedValue*>(arr + 1); auto const last = values + arr->m_size; for (auto ptr = values; ptr != last; ++ptr) { size += getMemSize(ptr); } return size; } case ArrayData::ArrayKind::kIntMapKind: case ArrayData::ArrayKind::kMixedKind: { auto const mixed = MixedArray::asMixed(arr); auto size = sizeof(MixedArray) + sizeof(MixedArray::Elm) * (mixed->m_cap - mixed->m_used); auto elms = mixed->data(); auto last = elms + mixed->m_used; for (auto ptr = elms; ptr != last; ++ptr) { if (MixedArray::isTombstone(ptr->data.m_type)) { size += sizeof(MixedArray::Elm); continue; } size += ptr->hasStrKey() ? getMemSize(ptr->skey) : sizeof(int64_t); size += getMemSize(&ptr->data); } return size; } case ArrayData::ArrayKind::kEmptyKind: return sizeof(ArrayData); default: assert(!"Unsupported Array type in getMemSize"); } return 0; } /////////////////////////////////////////////////////////////////////////////// std::unique_ptr<APCStats> APCStats::s_apcStats = nullptr; void APCStats::Create() { s_apcStats = folly::make_unique<APCStats>(); } APCStats::APCStats() : m_valueSize(nullptr) , m_keySize(nullptr) , m_inFileSize(nullptr) , m_livePrimedSize(nullptr) , m_pendingDeleteSize(nullptr) , m_entries(nullptr) , m_primedEntries(nullptr) , m_livePrimedEntries(nullptr) , m_detailedStats(nullptr) { m_valueSize = ServiceData::createTimeseries( "apc.value_size", {ServiceData::StatsType::SUM}); m_keySize = ServiceData::createTimeseries( "apc.key_size", {ServiceData::StatsType::SUM}); m_inFileSize = ServiceData::createTimeseries( "apc.in_file_size", {ServiceData::StatsType::SUM}); m_livePrimedSize = ServiceData::createTimeseries( "apc.primed_live_size", {ServiceData::StatsType::SUM}); m_pendingDeleteSize = ServiceData::createTimeseries( "apc.pending_delete_size", {ServiceData::StatsType::SUM}); m_entries = ServiceData::createCounter("apc.entries"); m_primedEntries = ServiceData::createCounter("apc.primed_entries"); m_livePrimedEntries = ServiceData::createCounter("apc.primed_live_entries"); if (RuntimeOption::EnableAPCStats) { m_detailedStats = new APCDetailedStats(); } } APCStats::~APCStats() { if (m_detailedStats) delete m_detailedStats; } std::string APCStats::getStatsInfo() const { std::string info("APC info\nValue size: "); info += std::to_string(m_valueSize->getSum()) + "\nKey size: " + std::to_string(m_keySize->getSum()) + "\nMapped to file data size: " + std::to_string(m_inFileSize->getSum()) + "\nIn memory primed data size: " + std::to_string(m_livePrimedSize->getSum()) + "\nEntries count: " + std::to_string(m_entries->getValue()) + "\nPrimed entries count: " + std::to_string(m_primedEntries->getValue()) + "\nIn memory primed entries count: " + std::to_string(m_livePrimedEntries->getValue()); if (apcExtension::UseUncounted) { info += "\nPending deletes via treadmill size: " + std::to_string(m_pendingDeleteSize->getSum()); } if (m_detailedStats) { info += m_detailedStats->getStatsInfo(); } return info + "\n"; } const StaticString s_entries("entries"); const StaticString s_primedEntries("primed_entries"); const StaticString s_primedLiveEntries("primed_live_entries"); const StaticString s_valuesSize("values_size"); const StaticString s_keysSize("keys_size"); const StaticString s_primedInFileSize("primed_in_file_size"); const StaticString s_primeLiveSize("primed_live_size"); const StaticString s_pendingDeleteSize("pending_delete_size"); void APCStats::collectStats(std::map<const StringData*, int64_t>& stats) const { stats.insert( std::pair<const StringData*, int64_t>(s_entries.get(), m_entries->getValue())); stats.insert( std::pair<const StringData*, int64_t>(s_primedEntries.get(), m_primedEntries->getValue())); stats.insert( std::pair<const StringData*, int64_t>(s_primedLiveEntries.get(), m_livePrimedEntries->getValue())); stats.insert( std::pair<const StringData*, int64_t>(s_valuesSize.get(), m_valueSize->getSum())); stats.insert( std::pair<const StringData*, int64_t>(s_keysSize.get(), m_keySize->getSum())); stats.insert( std::pair<const StringData*, int64_t>(s_primedInFileSize.get(), m_inFileSize->getSum())); stats.insert( std::pair<const StringData*, int64_t>(s_primeLiveSize.get(), m_livePrimedSize->getSum())); stats.insert( std::pair<const StringData*, int64_t>(s_pendingDeleteSize.get(), m_pendingDeleteSize->getSum())); if (m_detailedStats) { m_detailedStats->collectStats(stats); } } APCDetailedStats::APCDetailedStats() : m_uncounted(nullptr) , m_apcString(nullptr) , m_uncString(nullptr) , m_serArray(nullptr) , m_apcArray(nullptr) , m_uncArray(nullptr) , m_serObject(nullptr) , m_apcObject(nullptr) , m_apcColl(nullptr) , m_setValues(nullptr) , m_delValues(nullptr) , m_replValues(nullptr) , m_expValues(nullptr) { m_uncounted = ServiceData::createCounter("apc.type_uncounted"); m_apcString = ServiceData::createCounter("apc.type_apc_string"); m_uncString = ServiceData::createCounter("apc.type_unc_string"); m_serArray = ServiceData::createCounter("apc.type_ser_array"); m_apcArray = ServiceData::createCounter("apc.type_apc_array"); m_uncArray = ServiceData::createCounter("apc.type_unc_array"); m_serObject = ServiceData::createCounter("apc.type_ser_object"); m_apcObject = ServiceData::createCounter("apc.type_apc_object"); m_apcColl = ServiceData::createCounter("apc.type_apc_collection"); m_setValues = ServiceData::createCounter("apc.set_values"); m_delValues = ServiceData::createCounter("apc.deleted_values"); m_replValues = ServiceData::createCounter("apc.replaced_values"); m_expValues = ServiceData::createCounter("apc.expired_values"); } const StaticString s_typeUncounted("type_uncounted"); const StaticString s_typeAPCString("type_apc_string"); const StaticString s_typeUncountedString("type_unc_string"); const StaticString s_typeSerArray("type_ser_array"); const StaticString s_typeAPCArray("type_apc_array"); const StaticString s_typUncountedArray("type_unc_array"); const StaticString s_typeSerObject("type_ser_object"); const StaticString s_typeAPCObject("type_apc_object"); const StaticString s_setValueCount("set_values_count"); const StaticString s_deleteValuesCount("deleted_values_count"); const StaticString s_replacedValueCount("replaced_values_count"); const StaticString s_expiredValueCount("expired_values_count"); std::string APCDetailedStats::getStatsInfo() const { return "\nPrimitve and static strings count: " + std::to_string(m_uncounted->getValue()) + "\nAPC strings count: " + std::to_string(m_apcString->getValue()) + "\nUncounted strings count: " + std::to_string(m_uncString->getValue()) + "\nSerialized array count: " + std::to_string(m_serArray->getValue()) + "\nAPC array count: " + std::to_string(m_apcArray->getValue()) + "\nUncounted array count: " + std::to_string(m_uncArray->getValue()) + "\nSerialized object count: " + std::to_string(m_serObject->getValue()) + "\nAPC object count: " + std::to_string(m_apcObject->getValue()) + "\add count: " + std::to_string(m_setValues->getValue()) + "\ndelete count: " + std::to_string(m_delValues->getValue()) + "\nreplaced count: " + std::to_string(m_replValues->getValue()) + "\nexpired count: " + std::to_string(m_expValues->getValue()) + "\n"; } void APCDetailedStats::collectStats( std::map<const StringData*, int64_t>& stats) const { stats.insert( std::pair<const StringData*, int64_t>(s_typeUncounted.get(), m_uncounted->getValue())); stats.insert( std::pair<const StringData*, int64_t>(s_typeAPCString.get(), m_apcString->getValue())); stats.insert( std::pair<const StringData*, int64_t>(s_typeUncountedString.get(), m_uncString->getValue())); stats.insert( std::pair<const StringData*, int64_t>(s_typeSerArray.get(), m_serArray->getValue())); stats.insert( std::pair<const StringData*, int64_t>(s_typeAPCArray.get(), m_apcArray->getValue())); stats.insert( std::pair<const StringData*, int64_t>(s_typUncountedArray.get(), m_uncArray->getValue())); stats.insert( std::pair<const StringData*, int64_t>(s_typeSerObject.get(), m_serObject->getValue())); stats.insert( std::pair<const StringData*, int64_t>(s_typeAPCObject.get(), m_apcObject->getValue())); stats.insert( std::pair<const StringData*, int64_t>(s_setValueCount.get(), m_setValues->getValue())); stats.insert( std::pair<const StringData*, int64_t>(s_deleteValuesCount.get(), m_delValues->getValue())); stats.insert( std::pair<const StringData*, int64_t>(s_replacedValueCount.get(), m_replValues->getValue())); stats.insert( std::pair<const StringData*, int64_t>(s_expiredValueCount.get(), m_expValues->getValue())); } void APCDetailedStats::addAPCValue(APCHandle* handle) { m_setValues->increment(); addType(handle); } void APCDetailedStats::updateAPCValue(APCHandle* handle, APCHandle* oldHandle, bool expired) { removeType(oldHandle); addType(handle); if (expired) { m_expValues->increment(); } else { m_replValues->increment(); } } void APCDetailedStats::removeAPCValue(APCHandle* handle, bool expired) { removeType(handle); if (expired) { m_expValues->increment(); } else { m_delValues->increment(); } } void APCDetailedStats::addType(APCHandle* handle) { DataType type = handle->getType(); assert(!IS_REFCOUNTED_TYPE(type) || type == KindOfString || type == KindOfArray || type == KindOfObject); if (!IS_REFCOUNTED_TYPE(type)) { m_uncounted->increment(); return; } switch (type) { case KindOfString: if (handle->isUncounted()) { m_uncString->increment(); } else { m_apcString->increment(); } return; case KindOfArray: if (handle->isUncounted()) { m_uncArray->increment(); } else if (handle->isSerializedArray()) { m_serArray->increment(); } else { m_apcArray->increment(); } return; case KindOfObject: if (handle->isCollection()) { m_apcColl->increment(); } else if (handle->isObj()) { m_apcObject->increment(); } else { m_serObject->increment(); } return; default: return; } } void APCDetailedStats::removeType(APCHandle* handle) { DataType type = handle->getType(); assert(!IS_REFCOUNTED_TYPE(type) || type == KindOfString || type == KindOfArray || type == KindOfObject); if (!IS_REFCOUNTED_TYPE(type)) { m_uncounted->decrement(); return; } switch (type) { case KindOfString: if (handle->isUncounted()) { m_uncString->decrement(); } else { m_apcString->decrement(); } return; case KindOfArray: if (handle->isUncounted()) { m_uncArray->decrement(); } else if (handle->isSerializedArray()) { m_serArray->decrement(); } else { m_apcArray->decrement(); } return; case KindOfObject: if (handle->isCollection()) { m_apcColl->decrement(); } else if (handle->isObj()) { m_apcObject->decrement(); } else { m_serObject->decrement(); } return; default: return; } } /////////////////////////////////////////////////////////////////////////////// }
namespace Transl { ////////////////////////////////////////////////////////////////////// ArrayData* addElemIntKeyHelper(ArrayData* ad, int64_t key, TypedValue value) { // this does not re-enter // set will decRef any old value that may have been overwritten // if appropriate ArrayData* retval = ad->set(key, tvAsCVarRef(&value), ad->getCount() > 1); // TODO Task #1970153: It would be great if there were set() // methods that didn't bump up the refcount so that we didn't // have to decrement it here tvRefcountedDecRef(&value); return arrayRefShuffle<false>(ad, retval, nullptr); } ArrayData* addElemStringKeyHelper(ArrayData* ad, StringData* key, TypedValue value) { // this does not re-enter bool copy = ad->getCount() > 1; // set will decRef any old value that may have been overwritten // if appropriate int64_t intkey; ArrayData* retval = UNLIKELY(key->isStrictlyInteger(intkey)) ? ad->set(intkey, tvAsCVarRef(&value), copy) : ad->set(key, tvAsCVarRef(&value), copy); // TODO Task #1970153: It would be great if there were set() // methods that didn't bump up the refcount so that we didn't // have to decrement it here tvRefcountedDecRef(&value); return arrayRefShuffle<false>(ad, retval, nullptr); } ArrayData* array_add(ArrayData* a1, ArrayData* a2) { if (!a2->empty()) { if (a1->empty()) { decRefArr(a1); return a2; } if (a1 != a2) { ArrayData *escalated = a1->plus(a2, a1->getCount() > 1); if (escalated != a1) { escalated->incRefCount(); decRefArr(a2); decRefArr(a1); return escalated; } } } decRefArr(a2); return a1; } HOT_FUNC_VM void setNewElem(TypedValue* base, Cell val) { SetNewElem<false>(base, &val); } HOT_FUNC_VM void setNewElemArray(TypedValue* base, Cell val) { SetNewElemArray(base, &val); } void bindNewElemIR(TypedValue* base, RefData* val, MInstrState* mis) { base = NewElem(mis->tvScratch, mis->tvRef, base); if (!(base == &mis->tvScratch && base->m_type == KindOfUninit)) { tvBindRef(val, base); } } // TODO: Kill this #2031980 HOT_FUNC_VM RefData* box_value(TypedValue tv) { return tvBoxHelper(tv.m_type, tv.m_data.num); } inline int64_t reinterpretDblAsInt(double d) { union { int64_t intval; double dblval; } u; u.dblval = d; return u.intval; } inline double reinterpretIntAsDbl(int64_t i) { union { int64_t intval; double dblval; } u; u.intval = i; return u.dblval; } 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; } int64_t convArrToBoolHelper(const ArrayData* a) { return a->size() != 0; } int64_t convObjToBoolHelper(const ObjectData* o) { return o->o_toBoolean(); } int64_t convArrToDblHelper(ArrayData* a) { return reinterpretDblAsInt(a->empty() ? 0 : 1); } int64_t convStrToDblHelper(const StringData* s) { return reinterpretDblAsInt(s->toDouble()); } int64_t convCellToDblHelper(TypedValue tv) { return reinterpretDblAsInt(tvCastToDouble(&tv)); } int64_t convArrToIntHelper(ArrayData* a) { return a->empty() ? 0 : 1; } ObjectData* convCellToObjHelper(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 arrays to objects. It also // seems true for that case as well, since the source 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. tvCastToObjectInPlace(&tv); // consumes a ref on counted values return tv.m_data.pobj; } StringData* convDblToStrHelper(int64_t i) { double d = reinterpretIntAsDbl(i); auto r = buildStringData(d); r->incRefCount(); return r; } StringData* convIntToStrHelper(int64_t i) { auto r = buildStringData(i); r->incRefCount(); return r; } StringData* convObjToStrHelper(ObjectData* o) { auto s = o->invokeToString(); auto r = s.get(); if (!r->isStatic()) r->incRefCount(); return r; } StringData* convResToStrHelper(ResourceData* o) { auto s = o->o_toString(); auto r = s.get(); if (!r->isStatic()) r->incRefCount(); return r; } const StaticString s_empty(""), s_1("1"), s_Array("Array"); StringData* convCellToStrHelper(TypedValue tv) { switch (tv.m_type) { case KindOfUninit: case KindOfNull: return s_empty.get(); case KindOfBoolean: return tv.m_data.num ? s_1.get() : s_empty.get(); case KindOfInt64: return convIntToStrHelper(tv.m_data.num); case KindOfDouble: return convDblToStrHelper(tv.m_data.num); case KindOfString: tv.m_data.pstr->incRefCount(); /* fallthrough */ case KindOfStaticString: return tv.m_data.pstr; case KindOfArray: return s_Array.get(); case KindOfObject: return convObjToStrHelper(tv.m_data.pobj); case KindOfResource: return convResToStrHelper(tv.m_data.pres); default: not_reached(); } } void raisePropertyOnNonObject() { raise_warning("Cannot access property on non-object"); } void raiseUndefProp(ObjectData* base, const StringData* name) { base->raiseUndefProp(name); } void raise_error_sd(const StringData *msg) { raise_error("%s", msg->data()); } void VerifyParamTypeFail(int paramNum) { VMRegAnchor _; const ActRec* ar = liveFrame(); const Func* func = ar->m_func; const TypeConstraint& tc = func->params()[paramNum].typeConstraint(); TypedValue* tv = frame_local(ar, paramNum); assert(!tc.check(tv, func)); tc.verifyFail(func, paramNum, tv); } void VerifyParamTypeCallable(TypedValue value, int param) { if (UNLIKELY(!f_is_callable(tvAsCVarRef(&value)))) { VerifyParamTypeFail(param); } } HOT_FUNC_VM void VerifyParamTypeSlow(const Class* cls, const Class* constraint, int param, const TypeConstraint* expected) { if (LIKELY(constraint && cls->classof(constraint))) { return; } // Check a typedef for a class. We interp'd if the param wasn't an // object, so if it's a typedef for something non-objecty we're // failing anyway. if (auto namedEntity = expected->namedEntity()) { auto def = namedEntity->getCachedTypedef(); if (UNLIKELY(!def)) { VMRegAnchor _; String nameStr(const_cast<StringData*>(expected->typeName())); if (AutoloadHandler::s_instance->autoloadType(nameStr)) { def = namedEntity->getCachedTypedef(); } } if (def) { // There's no need to handle nullable typedefs specially here: // we already know we're checking a non-null object with the // class `cls'. We do however need to check for typedefs to // mixed. if (def->kind == KindOfObject) { constraint = def->klass; if (constraint && cls->classof(constraint)) return; } else if (def->kind == KindOfAny) { return; } } } VerifyParamTypeFail(param); } RefData* closureStaticLocInit(StringData* name, ActRec* fp, TypedValue val) { auto const func = fp->m_func; assert(func->isClosureBody() || func->isGeneratorFromClosure()); auto const closureLoc = LIKELY(func->isClosureBody()) ? frame_local(fp, func->numParams()) : frame_local(fp, frame_continuation(fp)->m_origFunc->numParams()); bool inited; auto const refData = lookupStaticFromClosure( closureLoc->m_data.pobj, name, inited); if (!inited) { cellCopy(val, *refData->tv()); } refData->incRefCount(); return refData; } HOT_FUNC_VM bool instanceOfHelper(const Class* objClass, const Class* testClass) { return testClass && objClass->classof(testClass); } ALWAYS_INLINE static int64_t ak_exist_string_impl(ArrayData* arr, StringData* key) { int64_t n; if (key->isStrictlyInteger(n)) { return arr->exists(n); } return arr->exists(key); } HOT_FUNC_VM int64_t ak_exist_string(ArrayData* arr, StringData* key) { return ak_exist_string_impl(arr, key); } HOT_FUNC_VM int64_t ak_exist_int(ArrayData* arr, int64_t key) { bool res = arr->exists(key); return res; } HOT_FUNC_VM int64_t ak_exist_string_obj(ObjectData* obj, StringData* key) { if (obj->isCollection()) { return collectionOffsetContains(obj, key); } CArrRef arr = obj->o_toArray(); int64_t res = ak_exist_string_impl(arr.get(), key); return res; } HOT_FUNC_VM int64_t ak_exist_int_obj(ObjectData* obj, int64_t key) { if (obj->isCollection()) { return collectionOffsetContains(obj, key); } CArrRef arr = obj->o_toArray(); bool res = arr.get()->exists(key); return res; } ALWAYS_INLINE TypedValue& getDefaultIfNullCell(TypedValue* tv, TypedValue& def) { if (UNLIKELY(nullptr == tv)) { // refcount is already correct since def was never decrefed return def; } tvRefcountedDecRef(&def); TypedValue* ret = tvToCell(tv); tvRefcountedIncRef(ret); return *ret; } HOT_FUNC_VM TypedValue arrayIdxS(ArrayData* a, StringData* key, TypedValue def) { return getDefaultIfNullCell(a->nvGet(key), def); } HOT_FUNC_VM TypedValue arrayIdxSi(ArrayData* a, StringData* key, TypedValue def) { int64_t i; return UNLIKELY(key->isStrictlyInteger(i)) ? getDefaultIfNullCell(a->nvGet(i), def) : getDefaultIfNullCell(a->nvGet(key), def); } HOT_FUNC_VM TypedValue arrayIdxI(ArrayData* a, int64_t key, TypedValue def) { return getDefaultIfNullCell(a->nvGet(key), def); } HOT_FUNC_VM TypedValue* ldGblAddrHelper(StringData* name) { return g_vmContext->m_globalVarEnv->lookup(name); } HOT_FUNC_VM TypedValue* ldGblAddrDefHelper(StringData* name) { TypedValue* r = g_vmContext->m_globalVarEnv->lookupAdd(name); decRefStr(name); return r; } TCA sswitchHelperFast(const StringData* val, const SSwitchMap* table, TCA* def) { TCA* dest = table->find(val); return dest ? *dest : *def; } // TODO(#2031980): clear these out void tv_release_generic(TypedValue* tv) { assert(Transl::tx64->stateIsDirty()); assert(tv->m_type == KindOfString || tv->m_type == KindOfArray || tv->m_type == KindOfObject || tv->m_type == KindOfResource || tv->m_type == KindOfRef); g_destructors[typeToDestrIndex(tv->m_type)](tv->m_data.pref); } void tv_release_typed(RefData* pv, DataType dt) { assert(Transl::tx64->stateIsDirty()); assert(dt == KindOfString || dt == KindOfArray || dt == KindOfObject || dt == KindOfResource || dt == KindOfRef); g_destructors[typeToDestrIndex(dt)](pv); } Cell lookupCnsHelper(const TypedValue* tv, StringData* nm, bool error) { assert(tv->m_type == KindOfUninit); // Deferred constants such as SID if (UNLIKELY(tv->m_data.pref != nullptr)) { ClassInfo::ConstantInfo* ci = (ClassInfo::ConstantInfo*)(void*)tv->m_data.pref; Cell *cns = const_cast<Variant&>(ci->getDeferredValue()).asTypedValue(); if (LIKELY(cns->m_type != KindOfUninit)) { Cell c1; cellDup(*cns, c1); return c1; } } Cell *cns = nullptr; if (UNLIKELY(TargetCache::s_constants().get() != nullptr)) { cns = TargetCache::s_constants()->nvGet(nm); } if (!cns) { cns = Unit::loadCns(const_cast<StringData*>(nm)); } if (LIKELY(cns != nullptr)) { Cell c1; c1.m_type = cns->m_type; c1.m_data = cns->m_data; return c1; } // Undefined constants if (error) { raise_error("Undefined constant '%s'", nm->data()); } else { raise_notice(Strings::UNDEFINED_CONSTANT, nm->data(), nm->data()); Cell c1; c1.m_data.pstr = const_cast<StringData*>(nm); c1.m_type = KindOfStaticString; return c1; } not_reached(); } Cell lookupCnsUHelper(const TypedValue* tv, StringData* nm, StringData* fallback) { Cell *cns = nullptr; Cell c1; // lookup qualified name in thread-local constants bool cacheConsts = TargetCache::s_constants().get() != nullptr; if (UNLIKELY(cacheConsts)) { cns = TargetCache::s_constants()->nvGet(nm); } if (!cns) { cns = Unit::loadCns(const_cast<StringData*>(nm)); } // try cache handle for unqualified name if (UNLIKELY(!cns && tv->m_type != KindOfUninit)) { cns = const_cast<Cell*>(tv); } // lookup unqualified name in thread-local constants if (UNLIKELY(!cns)) { if (UNLIKELY(cacheConsts)) { cns = TargetCache::s_constants()->nvGet(fallback); } if (!cns) { cns = Unit::loadCns(const_cast<StringData*>(fallback)); } if (UNLIKELY(!cns)) { raise_notice(Strings::UNDEFINED_CONSTANT, fallback->data(), fallback->data()); c1.m_data.pstr = const_cast<StringData*>(fallback); c1.m_type = KindOfStaticString; } } else { c1.m_type = cns->m_type; c1.m_data = cns->m_data; } return c1; } void iterFreeHelper(Iter* iter) { iter->free(); } void miterFreeHelper(Iter* iter) { iter->mfree(); } void citerFreeHelper(Iter* iter) { iter->cfree(); } ////////////////////////////////////////////////////////////////////// }}
void APCDetailedStats::collectStats( std::map<const StringData*, int64_t>& stats) const { stats.insert( std::pair<const StringData*, int64_t>(s_typeUncounted.get(), m_uncounted->getValue())); stats.insert( std::pair<const StringData*, int64_t>(s_typeAPCString.get(), m_apcString->getValue())); stats.insert( std::pair<const StringData*, int64_t>(s_typeUncountedString.get(), m_uncString->getValue())); stats.insert( std::pair<const StringData*, int64_t>(s_typeSerArray.get(), m_serArray->getValue())); stats.insert( std::pair<const StringData*, int64_t>(s_typeAPCArray.get(), m_apcArray->getValue())); stats.insert( std::pair<const StringData*, int64_t>(s_typUncountedArray.get(), m_uncArray->getValue())); stats.insert( std::pair<const StringData*, int64_t>(s_typeSerObject.get(), m_serObject->getValue())); stats.insert( std::pair<const StringData*, int64_t>(s_typeAPCObject.get(), m_apcObject->getValue())); stats.insert( std::pair<const StringData*, int64_t>(s_setValueCount.get(), m_setValues->getValue())); stats.insert( std::pair<const StringData*, int64_t>(s_deleteValuesCount.get(), m_delValues->getValue())); stats.insert( std::pair<const StringData*, int64_t>(s_replacedValueCount.get(), m_replValues->getValue())); stats.insert( std::pair<const StringData*, int64_t>(s_expiredValueCount.get(), m_expValues->getValue())); }
namespace HPHP { /////////////////////////////////////////////////////////////////////////////// using HPHP::JIT::CallerFrame; using HPHP::JIT::EagerCallerFrame; using std::string; const StaticString s_internal("internal"), s_user("user"); Array f_get_defined_functions() { return make_map_array(s_internal, ClassInfo::GetSystemFunctions(), s_user, ClassInfo::GetUserFunctions()); } bool f_function_exists(const String& function_name, bool autoload /* = true */) { return function_exists(function_name) || (autoload && AutoloadHandler::s_instance->autoloadFunc(function_name.get()) && function_exists(function_name)); } const StaticString s__invoke("__invoke"), s_Closure__invoke("Closure::__invoke"), s_colon2("::"); bool f_is_callable(CVarRef v, bool syntax /* = false */, VRefParam name /* = null */) { bool ret = true; if (LIKELY(!syntax)) { CallerFrame cf; ObjectData* obj = NULL; HPHP::Class* cls = NULL; StringData* invName = NULL; const HPHP::Func* f = vm_decode_function(v, cf(), false, obj, cls, invName, false); if (f == NULL) { ret = false; } if (invName != NULL) { decRefStr(invName); } if (!name.isReferenced()) return ret; } auto const tv_func = v.asCell(); if (IS_STRING_TYPE(tv_func->m_type)) { if (name.isReferenced()) name = tv_func->m_data.pstr; return ret; } if (tv_func->m_type == KindOfArray) { CArrRef arr = tv_func->m_data.parr; CVarRef clsname = arr.rvalAtRef(int64_t(0)); CVarRef mthname = arr.rvalAtRef(int64_t(1)); if (arr.size() != 2 || &clsname == &null_variant || &mthname == &null_variant) { name = v.toString(); return false; } auto const tv_meth = mthname.asCell(); if (!IS_STRING_TYPE(tv_meth->m_type)) { if (name.isReferenced()) name = v.toString(); return false; } auto const tv_cls = clsname.asCell(); if (tv_cls->m_type == KindOfObject) { name = tv_cls->m_data.pobj->o_getClassName(); } else if (IS_STRING_TYPE(tv_cls->m_type)) { name = tv_cls->m_data.pstr; } else { name = v.toString(); return false; } name = concat3(name, s_colon2, tv_meth->m_data.pstr); return ret; } if (tv_func->m_type == KindOfObject) { ObjectData *d = tv_func->m_data.pobj; const Func* invoke = d->getVMClass()->lookupMethod(s__invoke.get()); if (name.isReferenced()) { if (d->instanceof(c_Closure::classof())) { // Hack to stop the mangled name from showing up name = s_Closure__invoke; } else { name = d->o_getClassName() + "::__invoke"; } } return invoke != NULL; } return false; } Variant f_call_user_func(int _argc, CVarRef function, CArrRef _argv /* = null_array */) { return vm_call_user_func(function, _argv); } Variant f_call_user_func_array(CVarRef function, CVarRef params) { return vm_call_user_func(function, params); } Variant f_check_user_func_async(CVarRef handles, int timeout /* = -1 */) { raise_error("%s is no longer supported", __func__); return uninit_null(); } Variant f_end_user_func_async(CObjRef handle, int default_strategy /*= k_GLOBAL_STATE_IGNORE*/, CVarRef additional_strategies /* = null */) { raise_error("%s is no longer supported", __func__); return uninit_null(); } const StaticString s_func("func"), s_args("args"), s_exception("exception"), s_ret("ret"); String f_call_user_func_serialized(const String& input) { Variant out; try { Variant in = unserialize_from_string(input); out.set(s_ret, vm_call_user_func(in[s_func], in[s_args].toArray())); } catch (Object &e) { out.set(s_exception, e); } return f_serialize(out); } Variant f_call_user_func_array_rpc(const String& host, int port, const String& auth, int timeout, CVarRef function, CArrRef params) { return f_call_user_func_rpc(0, host, port, auth, timeout, function, params); } Variant f_call_user_func_rpc(int _argc, const String& host, int port, const String& auth, int timeout, CVarRef function, CArrRef _argv /* = null_array */) { std::string shost = host.data(); if (!RuntimeOption::DebuggerRpcHostDomain.empty()) { unsigned int pos = shost.find(RuntimeOption::DebuggerRpcHostDomain); if (pos != shost.length() - RuntimeOption::DebuggerRpcHostDomain.size()) { shost += RuntimeOption::DebuggerRpcHostDomain; } } std::string url = "http://"; url += shost; url += ":"; url += boost::lexical_cast<std::string>(port); url += "/call_user_func_serialized?auth="; url += auth.data(); Array blob = make_map_array(s_func, function, s_args, _argv); String message = f_serialize(blob); std::vector<string> headers; LibEventHttpClientPtr http = LibEventHttpClient::Get(shost, port); if (!http->send(url, headers, timeout < 0 ? 0 : timeout, false, message.data(), message.size())) { raise_error("Unable to send RPC request"); return false; } int code = http->getCode(); if (code <= 0) { raise_error("Server timed out or unable to find specified URL: %s", url.c_str()); return false; } int len = 0; char *response = http->recv(len); String sresponse(response, len, AttachString); if (code != 200) { raise_error("Internal server error: %d %s", code, HttpProtocol::GetReasonString(code)); return false; } // This double decoding can be avoided by modifying RPC server to directly // take PHP serialization format. Variant res = unserialize_from_string(HHVM_FN(json_decode)(sresponse)); if (!res.isArray()) { raise_error("Internal protocol error"); return false; } if (res.toArray().exists(s_exception)) { throw res[s_exception]; } return res[s_ret]; } Variant f_forward_static_call_array(CVarRef function, CArrRef params) { return f_forward_static_call(0, function, params); } Variant f_forward_static_call(int _argc, CVarRef function, CArrRef _argv /* = null_array */) { // Setting the bound parameter to true tells vm_call_user_func() // propogate the current late bound class return vm_call_user_func(function, _argv, true); } Variant f_get_called_class() { EagerCallerFrame cf; ActRec* ar = cf(); if (ar) { if (ar->hasThis()) return Variant(ar->getThis()->o_getClassName()); if (ar->hasClass()) return Variant(ar->getClass()->preClass()->name()); } return Variant(false); } String f_create_function(const String& args, const String& code) { return g_vmContext->createFunction(args, code); } /////////////////////////////////////////////////////////////////////////////// Variant f_func_get_arg(int arg_num) { CallerFrame cf; ActRec* ar = cf.actRecForArgs(); if (ar == NULL) { return false; } if (ar->hasVarEnv() && ar->getVarEnv()->isGlobalScope()) { raise_warning( "func_get_arg(): Called from the global scope - no function context" ); return false; } if (arg_num < 0) { raise_warning( "func_get_arg(): The argument number should be >= 0" ); return false; } if (arg_num >= ar->numArgs()) { raise_warning( "func_get_arg(): Argument %d not passed to function", arg_num ); return false; } const int numParams = ar->m_func->numParams(); if (arg_num < numParams) { // Formal parameter. Value is on the stack. TypedValue* loc = (TypedValue*)(uintptr_t(ar) - (arg_num + 1) * sizeof(TypedValue)); return tvAsVariant(loc); } const int numArgs = ar->numArgs(); const int extraArgs = numArgs - numParams; // Not a formal parameter. Value is potentially in the // ExtraArgs/VarEnv. const int extraArgNum = arg_num - numParams; if (extraArgNum < extraArgs) { return tvAsVariant(ar->getExtraArg(extraArgNum)); } return false; } Array hhvm_get_frame_args(const ActRec* ar, int offset) { if (ar == NULL) { return Array(); } int numParams = ar->m_func->numParams(); int numArgs = ar->numArgs(); PackedArrayInit retInit(std::max(numArgs - offset, 0)); auto local = reinterpret_cast<TypedValue*>( uintptr_t(ar) - sizeof(TypedValue) ); local -= offset; for (int i = offset; i < numArgs; ++i) { if (i < numParams) { // This corresponds to one of the function's formal parameters, so it's // on the stack. retInit.append(tvAsCVarRef(local)); --local; } else { // This is not a formal parameter, so it's in the ExtraArgs. retInit.append(tvAsCVarRef(ar->getExtraArg(i - numParams))); } } return retInit.toArray(); } #define FUNC_GET_ARGS_IMPL(offset) do { \ EagerCallerFrame cf; \ ActRec* ar = cf.actRecForArgs(); \ if (ar && ar->hasVarEnv() && ar->getVarEnv()->isGlobalScope()) { \ raise_warning( \ "func_get_args(): Called from the global scope - no function context" \ ); \ return false; \ } \ return hhvm_get_frame_args(ar, offset); \ } while(0) Variant f_func_get_args() { FUNC_GET_ARGS_IMPL(0); } Variant f_hphp_func_slice_args(int offset) { if (offset < 0) { offset = 0; } FUNC_GET_ARGS_IMPL(offset); } int64_t f_func_num_args() { EagerCallerFrame cf; ActRec* ar = cf.actRecForArgs(); if (ar == NULL) { return -1; } if (ar->hasVarEnv() && ar->getVarEnv()->isGlobalScope()) { raise_warning( "func_num_args(): Called from the global scope - no function context" ); return -1; } return ar->numArgs(); } /////////////////////////////////////////////////////////////////////////////// void f_register_postsend_function(int _argc, CVarRef function, CArrRef _argv /* = null_array */) { g_context->registerShutdownFunction(function, _argv, ExecutionContext::PostSend); } void f_register_shutdown_function(int _argc, CVarRef function, CArrRef _argv /* = null_array */) { g_context->registerShutdownFunction(function, _argv, ExecutionContext::ShutDown); } void f_register_cleanup_function(int _argc, CVarRef function, CArrRef _argv /* = null_array */) { g_context->registerShutdownFunction(function, _argv, ExecutionContext::CleanUp); } /////////////////////////////////////////////////////////////////////////////// }
void moduleInit() override { Native::registerConstant<KindOfStaticString>( s_MCRYPT_3DES.get(), StaticString("tripledes").get() ); Native::registerConstant<KindOfStaticString>( s_MCRYPT_ARCFOUR.get(), StaticString("arcfour").get() ); Native::registerConstant<KindOfStaticString>( s_MCRYPT_ARCFOUR_IV.get(), StaticString("arcfour-iv").get() ); Native::registerConstant<KindOfStaticString>( s_MCRYPT_BLOWFISH.get(), StaticString("blowfish").get() ); Native::registerConstant<KindOfStaticString>( s_MCRYPT_BLOWFISH_COMPAT.get(), StaticString("blowfish-compat").get() ); Native::registerConstant<KindOfStaticString>( s_MCRYPT_CAST_128.get(), StaticString("cast-128").get() ); Native::registerConstant<KindOfStaticString>( s_MCRYPT_CAST_256.get(), StaticString("cast-256").get() ); Native::registerConstant<KindOfStaticString>( s_MCRYPT_CRYPT.get(), StaticString("crypt").get() ); Native::registerConstant<KindOfInt64>( s_MCRYPT_DECRYPT.get(), 1 ); Native::registerConstant<KindOfStaticString>( s_MCRYPT_DES.get(), StaticString("des").get() ); Native::registerConstant<KindOfInt64>( s_MCRYPT_DEV_RANDOM.get(), RANDOM ); Native::registerConstant<KindOfInt64>( s_MCRYPT_DEV_URANDOM.get(), URANDOM ); Native::registerConstant<KindOfInt64>( s_MCRYPT_ENCRYPT.get(), 0 ); Native::registerConstant<KindOfStaticString>( s_MCRYPT_ENIGNA.get(), StaticString("crypt").get() ); Native::registerConstant<KindOfStaticString>( s_MCRYPT_GOST.get(), StaticString("gost").get() ); Native::registerConstant<KindOfStaticString>( s_MCRYPT_IDEA.get(), StaticString("idea").get() ); Native::registerConstant<KindOfStaticString>( s_MCRYPT_LOKI97.get(), StaticString("loki97").get() ); Native::registerConstant<KindOfStaticString>( s_MCRYPT_MARS.get(), StaticString("mars").get() ); Native::registerConstant<KindOfStaticString>( s_MCRYPT_MODE_CBC.get(), StaticString("cbc").get() ); Native::registerConstant<KindOfStaticString>( s_MCRYPT_MODE_CFB.get(), StaticString("cfb").get() ); Native::registerConstant<KindOfStaticString>( s_MCRYPT_MODE_ECB.get(), StaticString("ecb").get() ); Native::registerConstant<KindOfStaticString>( s_MCRYPT_MODE_NOFB.get(), StaticString("nofb").get() ); Native::registerConstant<KindOfStaticString>( s_MCRYPT_MODE_OFB.get(), StaticString("ofb").get() ); Native::registerConstant<KindOfStaticString>( s_MCRYPT_MODE_STREAM.get(), StaticString("stream").get() ); Native::registerConstant<KindOfStaticString>( s_MCRYPT_PANAMA.get(), StaticString("panama").get() ); Native::registerConstant<KindOfInt64>( s_MCRYPT_RAND.get(), RAND ); Native::registerConstant<KindOfStaticString>( s_MCRYPT_RC2.get(), StaticString("rc2").get() ); Native::registerConstant<KindOfStaticString>( s_MCRYPT_RC6.get(), StaticString("rc6").get() ); Native::registerConstant<KindOfStaticString>( s_MCRYPT_RIJNDAEL_128.get(), StaticString("rijndael-128").get() ); Native::registerConstant<KindOfStaticString>( s_MCRYPT_RIJNDAEL_192.get(), StaticString("rijndael-192").get() ); Native::registerConstant<KindOfStaticString>( s_MCRYPT_RIJNDAEL_256.get(), StaticString("rijndael-256").get() ); Native::registerConstant<KindOfStaticString>( s_MCRYPT_SAFER128.get(), StaticString("safer-sk128").get() ); Native::registerConstant<KindOfStaticString>( s_MCRYPT_SAFER64.get(), StaticString("safer-sk64").get() ); Native::registerConstant<KindOfStaticString>( s_MCRYPT_SAFERPLUS.get(), StaticString("saferplus").get() ); Native::registerConstant<KindOfStaticString>( s_MCRYPT_SERPENT.get(), StaticString("serpent").get() ); Native::registerConstant<KindOfStaticString>( s_MCRYPT_SKIPJACK.get(), StaticString("skipjack").get() ); Native::registerConstant<KindOfStaticString>( s_MCRYPT_THREEWAY.get(), StaticString("threeway").get() ); Native::registerConstant<KindOfStaticString>( s_MCRYPT_TRIPLEDES.get(), StaticString("tripledes").get() ); Native::registerConstant<KindOfStaticString>( s_MCRYPT_TWOFISH.get(), StaticString("twofish").get() ); Native::registerConstant<KindOfStaticString>( s_MCRYPT_WAKE.get(), StaticString("wake").get() ); Native::registerConstant<KindOfStaticString>( s_MCRYPT_XTEA.get(), StaticString("xtea").get() ); HHVM_FE(mcrypt_module_open); HHVM_FE(mcrypt_module_close); HHVM_FE(mcrypt_list_algorithms); HHVM_FE(mcrypt_list_modes); HHVM_FE(mcrypt_module_get_algo_block_size); HHVM_FE(mcrypt_module_get_algo_key_size); HHVM_FE(mcrypt_module_get_supported_key_sizes); HHVM_FE(mcrypt_module_is_block_algorithm_mode); HHVM_FE(mcrypt_module_is_block_algorithm); HHVM_FE(mcrypt_module_is_block_mode); HHVM_FE(mcrypt_module_self_test); HHVM_FE(mcrypt_create_iv); HHVM_FE(mcrypt_encrypt); HHVM_FE(mcrypt_decrypt); HHVM_FE(mcrypt_cbc); HHVM_FE(mcrypt_cfb); HHVM_FE(mcrypt_ecb); HHVM_FE(mcrypt_ofb); HHVM_FE(mcrypt_get_block_size); HHVM_FE(mcrypt_get_cipher_name); HHVM_FE(mcrypt_get_iv_size); HHVM_FE(mcrypt_get_key_size); HHVM_FE(mcrypt_enc_get_algorithms_name); HHVM_FE(mcrypt_enc_get_block_size); HHVM_FE(mcrypt_enc_get_iv_size); HHVM_FE(mcrypt_enc_get_key_size); HHVM_FE(mcrypt_enc_get_modes_name); HHVM_FE(mcrypt_enc_get_supported_key_sizes); HHVM_FE(mcrypt_enc_is_block_algorithm_mode); HHVM_FE(mcrypt_enc_is_block_algorithm); HHVM_FE(mcrypt_enc_is_block_mode); HHVM_FE(mcrypt_enc_self_test); HHVM_FE(mcrypt_generic_init); HHVM_FE(mcrypt_generic); HHVM_FE(mdecrypt_generic); HHVM_FE(mcrypt_generic_deinit); HHVM_FE(mcrypt_generic_end); loadSystemlib(); }