trie_tree* trie_tree_create_end() { assert(be_creating); assert(!be_inserting); trie_tree* tree = (trie_tree*)malloc(sizeof(trie_tree)); init_array(&tree->node_array, sizeof(trie_node)); init_array(&successor_array, sizeof(trie_successor)); append_array(&tree->node_array, 2); memset(get_array_elem(&tree->node_array, 0), 0, sizeof(trie_node)*2); trie_node* head_node = (trie_node*)get_array_elem(&tree->node_array, HEAD_INDEX); head_node->check = HEAD_CHECK; head_node->base = HEAD_INDEX; for(int input_index = 0; input_index<input_cache.len; input_index++) { int prefix_index, node_index, base_index; trie_input* input_node = (trie_input*)get_array_elem(&input_cache, input_index); while( find_prefix(tree, input_node->str, &prefix_index, &node_index) ) { get_all_successors(input_node->str, prefix_index, input_index); base_index = find_base_index_by_successors(tree, node_index); insert_successors(tree, base_index, node_index); } mark_word_node(tree, node_index); } empty_array(&input_cache); empty_array(&successor_array); be_creating = false; return tree; }
void trie_tree_insert_end(trie_tree* tree) { assert(!be_creating); assert(be_inserting); init_array(&successor_array, sizeof(trie_successor)); for(int input_index = 0; input_index<input_cache.len; input_index++) { int prefix_index, node_index; trie_input* input_node = (trie_input*)get_array_elem(&input_cache, input_index); while( find_prefix(tree, input_node->str, &prefix_index, &node_index) ) { int base_index = abs(((trie_node*)get_array_elem(&tree->node_array, node_index))->base); get_all_successors(input_node->str, prefix_index, input_index); delete_existing_successors(tree, node_index); if(base_index == node_index || !check_insert_successors(tree, node_index) ) { reset_successors(tree, node_index); base_index = find_base_index_by_successors(tree, node_index); } insert_successors(tree, base_index, node_index); } mark_word_node(tree, node_index); } print_node(tree, 1, 0); empty_array(&input_cache); empty_array(&successor_array); be_inserting = false; }
Array HHVM_FUNCTION(heapgraph_node_in_edges, const Resource& resource, int64_t index ) { auto hgptr = get_valid_heapgraph_context_resource(resource, __FUNCTION__); if (!hgptr) return empty_array(); if (index < 0 || index >= (hgptr->hg.nodes.size())) return empty_array(); Array result; hgptr->hg.eachPredPtr(index, [&](int ptr) { result.append(createPhpEdge(hgptr, ptr)); }); return result; }
void AsioSession::onIOWaitExit() { runCallback( m_onIOWaitExitCallback, empty_array(), "WaitHandle::onIOWaitExit" ); }
void AsioSession::onIOWaitEnter() { runCallback( m_onIOWaitEnterCallback, empty_array(), "WaitHandle::onIOWaitEnter" ); }
Array Variant::toArrayHelper() const { switch (m_type) { case KindOfUninit: case KindOfNull: return empty_array(); case KindOfBoolean: return Array::Create(*this); case KindOfInt64: return Array::Create(m_data.num); case KindOfDouble: return Array::Create(*this); case KindOfStaticString: case KindOfString: return Array::Create(Variant{m_data.pstr}); case KindOfPersistentArray: case KindOfArray: return Array(m_data.parr); case KindOfObject: return m_data.pobj->toArray(); case KindOfResource: return m_data.pres->data()->o_toArray(); case KindOfRef: return m_data.pref->var()->toArray(); case KindOfClass: break; } not_reached(); }
Array c_WaitableWaitHandle::getDependencyStack() { if (isFinished()) return empty_array(); Array result = Array::Create(); hphp_hash_set<c_WaitableWaitHandle*> visited; auto current_handle = this; auto session = AsioSession::Get(); while (current_handle != nullptr) { result.append(Variant{current_handle}); visited.insert(current_handle); auto context_idx = current_handle->getContextIdx(); // 1. find parent in the same context auto p = current_handle->getParentChain().firstInContext(context_idx); if (p && visited.find(p) == visited.end()) { current_handle = p; continue; } // 2. cross the context boundary auto context = session->getContext(context_idx); if (!context) { break; } current_handle = c_ResumableWaitHandle::getRunning(context->getSavedFP()); auto target_context_idx = current_handle ? current_handle->getContextIdx() : 0; while (context_idx > target_context_idx) { --context_idx; result.append(null_object); } } return result; }
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.filtersAsArray()).toArray(); }
static Array HHVM_FUNCTION(xenon_get_data, void) { if (RuntimeOption::XenonForceAlwaysOn || RuntimeOption::XenonPeriodSeconds > 0) { TRACE(1, "xenon_get_data\n"); return s_xenonData->createResponse(); } return empty_array(); }
Array c_WaitableWaitHandle::t_getparents() { // no parent data available if finished if (isFinished()) { return empty_array(); } return getParentChain().toArray(); }
Array HHVM_FUNCTION(apache_response_headers) { Transport *transport = g_context->getTransport(); if (transport) { HeaderMap headers; transport->getResponseHeaders(headers); return get_headers(headers); } return empty_array(); }
Array HHVM_FUNCTION(get_headers_secure) { Transport *transport = g_context->getTransport(); if (transport) { HeaderMap headers; transport->getHeaders(headers); return get_headers(headers, true); } return empty_array(); }
Array getVar(int64_t type) { switch (type) { case k_INPUT_GET: return m_GET; case k_INPUT_POST: return m_POST; case k_INPUT_COOKIE: return m_COOKIE; case k_INPUT_SERVER: return m_SERVER; case k_INPUT_ENV: return m_ENV; } return empty_array(); }
Array HashCollection::toArray() { if (!m_size) { return empty_array(); } auto ad = arrayData()->toPHPArray(true); if (UNLIKELY(ad->size() < m_size)) warnOnStrIntDup(); assert(m_size); assert(ad->m_pos == 0); return Array::attach(ad); }
Array HHVM_FUNCTION(heapgraph_stats, const Resource& resource) { auto hgptr = get_valid_heapgraph_context_resource(resource, __FUNCTION__); if (!hgptr) return empty_array(); auto result = make_map_array( s_nodes, Variant(hgptr->hg.nodes.size()), s_edges, Variant(hgptr->hg.ptrs.size()), s_roots, Variant(hgptr->hg.roots.size()) ); return result; }
void EventHook::DoMemoryThresholdCallback() { clearSurpriseFlag(MemThresholdFlag); if (!g_context->m_memThresholdCallback.isNull()) { VMRegAnchor _; try { vm_call_user_func(g_context->m_memThresholdCallback, empty_array()); } catch (Object& ex) { raise_error("Uncaught exception escaping mem Threshold callback: %s", ex.toString().data()); } } }
Array HHVM_FUNCTION(libxml_get_errors) { xmlErrorVec* error_list = &tl_libxml_request_data->m_errors; const auto length = error_list->size(); if (!length) { return empty_array(); } PackedArrayInit ret(length); for (int64_t i = 0; i < length; i++) { ret.append(create_libxmlerror(error_list->at(i))); } return ret.toArray(); }
Array c_WaitableWaitHandle::t_getparents() { // no parent data available if finished if (isFinished()) { return empty_array(); } Array result = Array::Create(); c_BlockableWaitHandle* curr = m_firstParent; while (curr) { result.append(curr); curr = curr->getNextParent(); } return result; }
Array Variant::toArrayHelper() const { switch (m_type) { case KindOfUninit: case KindOfNull: return empty_array(); case KindOfInt64: return Array::Create(m_data.num); case KindOfStaticString: case KindOfString: return Array::Create(m_data.pstr); case KindOfArray: return Array(m_data.parr); case KindOfObject: return m_data.pobj->o_toArray(); case KindOfResource: return m_data.pres->o_toArray(); case KindOfRef: return m_data.pref->var()->toArray(); default: break; } return Array::Create(*this); }
Variant ArrayUtil::Reverse(const Array& input, bool preserve_keys /* = false */) { if (input.empty()) { return empty_array(); } auto ret = Array::Create(); auto pos_limit = input->iter_end(); for (ssize_t pos = input->iter_last(); pos != pos_limit; pos = input->iter_rewind(pos)) { auto const key = input->nvGetKey(pos); if (preserve_keys || isStringType(key.m_type)) { ret.setWithRef(key, input->atPos(pos), true); } else { ret.appendWithRef(input->atPos(pos)); } } return ret; }
Variant HHVM_FUNCTION(preg_replace_callback_array, const Variant& patterns_and_callbacks, const Variant& subject, int limit /* = -1 */, VRefParam count /* = uninit_null() */) { if (!patterns_and_callbacks.isArray()) { raise_warning( "%s() expects parameter 1 to be an array, %s given", __FUNCTION__+2 /* +2 removes the "f_" prefix */, getDataTypeString(patterns_and_callbacks.getType()).c_str() ); return init_null(); } // Now see if we need to raise any warnings because of not having a // valid callback function for (ArrayIter iter(patterns_and_callbacks.toArray()); iter; ++iter) { if (!is_callable(iter.second())) { raise_warning("Not a valid callback function %s", iter.second().toString().data()); return subject.isString() ? empty_string_variant() : Variant(empty_array()); } } if (subject.isString()) { Array subject_arr = Array::Create(); subject_arr.add(0, subject.toString()); Variant ret = preg_replace_callback_array_impl( patterns_and_callbacks, subject_arr, limit, count ); // ret[0] could be an empty string return ret.isArray() ? ret.toArray()[0] : init_null(); } else if (subject.isArray()) { return preg_replace_callback_array_impl( patterns_and_callbacks, subject.toArray(), limit, count ); } else { // No warning is given here, just return null return init_null(); } }
Variant HHVM_METHOD(SQLite3, querysingle, const String& sql, bool entire_row /* = false */) { auto *data = Native::data<SQLite3>(this_); SYNC_VM_REGS_SCOPED(); data->validate(); if (!sql.empty()) { Variant stmt = HHVM_MN(SQLite3, prepare)(this_, sql); if (!same(stmt, false)) { Object obj_stmt = stmt.toObject(); assert(obj_stmt.instanceof(SQLite3Stmt::getClass())); sqlite3_stmt *pstmt = Native::data<SQLite3Stmt>(obj_stmt)->m_raw_stmt; switch (sqlite3_step(pstmt)) { case SQLITE_ROW: /* Valid Row */ if (entire_row) { Array ret = Array::Create(); for (int i = 0; i < sqlite3_data_count(pstmt); i++) { ret.set(String((char*)sqlite3_column_name(pstmt, i), CopyString), get_column_value(pstmt, i)); } return ret; } return get_column_value(pstmt, 0); case SQLITE_DONE: /* Valid but no results */ if (entire_row) { return empty_array(); } else { return init_null(); } default: raise_warning("Unable to execute statement: %s", sqlite3_errmsg(data->m_raw_db)); } } } return false; }
Variant HHVM_FUNCTION(getaddrinfo, const String& host, const String& port, int family /* = 0 */, int socktype /* = 0 */, int protocol /* = 0 */, int flags /* = 0 */) { const char *hptr = NULL, *pptr = NULL; if (!host.empty()) { hptr = host.c_str(); } if (!port.empty()) { pptr = port.c_str(); } struct addrinfo hints, *res; struct addrinfo *res0 = NULL; int error; memset(&hints, 0, sizeof(hints)); hints.ai_family = family; hints.ai_socktype = socktype; hints.ai_protocol = protocol; hints.ai_flags = flags; error = getaddrinfo(hptr, pptr, &hints, &res0); if (error) { raise_warning("%s", gai_strerror(error)); if (res0) { freeaddrinfo(res0); } return false; } Array ret = Array::Create(); for (res = res0; res; res = res->ai_next) { Array data = make_map_array( s_family, res->ai_family, s_socktype, res->ai_socktype, s_protocol, res->ai_protocol ); switch (res->ai_addr->sa_family) { case AF_INET: { struct sockaddr_in *a; String buffer = ipaddr_convert(res->ai_addr, sizeof(*a)); if (!buffer.empty()) { a = (struct sockaddr_in *)res->ai_addr; data.set( s_sockaddr, make_map_array( s_address, buffer, s_port, ntohs(a->sin_port) ) ); } break; } case AF_INET6: { struct sockaddr_in6 *a; String buffer = ipaddr_convert(res->ai_addr, sizeof(*a)); if (!buffer.empty()) { a = (struct sockaddr_in6 *)res->ai_addr; data.set( s_sockaddr, make_map_array( s_address, buffer, s_port, ntohs(a->sin6_port), s_flow_info, (int32_t)a->sin6_flowinfo, s_scope_id, (int32_t)a->sin6_scope_id ) ); } break; } default: data.set(s_sockaddr, empty_array()); break; } ret.append(data); } if (res0) { freeaddrinfo(res0); } return ret; }
void ResourceData::serializeImpl(VariableSerializer *serializer) const { serializer->pushResourceInfo(o_getResourceName(), o_id); empty_array().serialize(serializer); serializer->popResourceInfo(); }
Array HHVM_FUNCTION(heapgraph_node, const Resource& resource, int64_t index) { auto hgptr = get_valid_heapgraph_context_resource(resource, __FUNCTION__); if (!hgptr) return empty_array(); if (index < 0 || index >= (hgptr->hg.nodes.size())) return empty_array(); return createPhpNode(hgptr, index); }
size_t check_request_surprise() { auto& info = TI(); auto& p = info.m_reqInjectionData; auto const flags = fetchAndClearSurpriseFlags(); auto const do_timedout = (flags & TimedOutFlag) && !p.getDebuggerAttached(); auto const do_memExceeded = flags & MemExceededFlag; auto const do_memThreshold = flags & MemThresholdFlag; auto const do_signaled = flags & SignaledFlag; auto const do_cpuTimedOut = (flags & CPUTimedOutFlag) && !p.getDebuggerAttached(); auto const do_GC = flags & PendingGCFlag; // Start with any pending exception that might be on the thread. auto pendingException = info.m_pendingException; info.m_pendingException = nullptr; if (do_timedout) { p.setCPUTimeout(0); // Stop CPU timer so we won't time out twice. if (pendingException) { setSurpriseFlag(TimedOutFlag); } else { pendingException = generate_request_timeout_exception(); } } // Don't bother with the CPU timeout if we're already handling a wall timeout. if (do_cpuTimedOut && !do_timedout) { p.setTimeout(0); // Stop wall timer so we won't time out twice. if (pendingException) { setSurpriseFlag(CPUTimedOutFlag); } else { pendingException = generate_request_cpu_timeout_exception(); } } if (do_memExceeded) { if (pendingException) { setSurpriseFlag(MemExceededFlag); } else { pendingException = generate_memory_exceeded_exception(); } } if (do_memThreshold) { clearSurpriseFlag(MemThresholdFlag); if (!g_context->m_memThresholdCallback.isNull()) { VMRegAnchor _; try { vm_call_user_func(g_context->m_memThresholdCallback, empty_array()); } catch (Object& ex) { raise_error("Uncaught exception escaping mem Threshold callback: %s", ex.toString().data()); } } } if (do_GC) { VMRegAnchor _; if (RuntimeOption::EvalEnableGC) { MM().collect("surprise"); } else { MM().checkHeap("surprise"); } } if (do_signaled) { HHVM_FN(pcntl_signal_dispatch)(); } if (pendingException) { pendingException->throwException(); } return flags; }
// This function takes an array of arrays, each of which is of the // form array($dbh, ...). The only thing that matters in the inner // arrays is the first element being a MySQL instance. It then // procedes to block for up to 'timeout' seconds, waiting for the // first actionable descriptor(s), which it then returns in the form // of the original arrays passed in. The intention is the caller // would include other information they care about in the tail of the // array so they can decide how to act on the // potentially-now-queryable descriptors. // // This function is a poor shadow of how the async library can be // used; for more complex cases, we'd use libevent and share our event // loop with other IO operations such as memcache ops, thrift calls, // etc. That said, this function is reasonably efficient for most use // cases. static Variant HHVM_FUNCTION(mysql_async_wait_actionable, const Array& items, double timeout) { size_t count = items.size(); if (count == 0 || timeout < 0) { return empty_array(); } struct pollfd* fds = (struct pollfd*)calloc(count, sizeof(struct pollfd)); SCOPE_EXIT { free(fds); }; // Walk our input, determine what kind of poll() operation is // necessary for the descriptor in question, and put an entry into // fds. int nfds = 0; for (ArrayIter iter(items); iter; ++iter) { Array entry = iter.second().toArray(); if (entry.size() < 1) { raise_warning("element %d did not have at least one entry", nfds); return empty_array(); } auto conn = cast<MySQLResource>(entry.rvalAt(0))->mysql()->get(); if (conn->async_op_status == ASYNC_OP_UNSET) { raise_warning("runtime/ext_mysql: no pending async operation in " "progress"); return empty_array(); } pollfd* fd = &fds[nfds++]; fd->fd = mysql_get_file_descriptor(conn); if (conn->net.async_blocking_state == NET_NONBLOCKING_READ) { fd->events = POLLIN; } else { fd->events = POLLOUT; } fd->revents = 0; } // The poll itself; either the timeout is hit or one or more of the // input fd's is ready. int timeout_millis = static_cast<long>(timeout * 1000); int res = poll(fds, nfds, timeout_millis); if (res == -1) { raise_warning("unable to poll [%d]: %s", errno, folly::errnoStr(errno).c_str()); return empty_array(); } // Now just find the ones that are ready, and copy the corresponding // arrays from our input array into our return value. Array ret = Array::Create(); nfds = 0; for (ArrayIter iter(items); iter; ++iter) { Array entry = iter.second().toArray(); if (entry.size() < 1) { raise_warning("element %d did not have at least one entry", nfds); return empty_array(); } auto conn = cast<MySQLResource>(entry.rvalAt(0))->mysql()->get(); pollfd* fd = &fds[nfds++]; if (fd->fd != mysql_get_file_descriptor(conn)) { raise_warning("poll returned events out of order wtf"); continue; } if (fd->revents != 0) { ret.append(iter.second()); } } return ret; }
void trie_tree_free(trie_tree* tree) { assert(tree); empty_array(&tree->node_array); free(tree); }
Array createBacktrace(const BacktraceArgs& btArgs) { auto bt = Array::Create(); // If there is a parser frame, put it at the beginning of the backtrace. if (btArgs.m_parserFrame) { bt.append( make_map_array( s_file, btArgs.m_parserFrame->filename, s_line, btArgs.m_parserFrame->lineNumber ) ); } VMRegAnchor _; // If there are no VM frames, we're done. if (!rds::header() || !vmfp()) return bt; int depth = 0; ActRec* fp = nullptr; Offset pc = 0; // Get the fp and pc of the top frame (possibly skipping one frame). if (btArgs.m_skipTop) { fp = getPrevActRec(vmfp(), &pc); // We skipped over the only VM frame, we're done. if (!fp) return bt; } else { fp = vmfp(); auto const unit = fp->func()->unit(); assert(unit); pc = unit->offsetOf(vmpc()); } // Handle the top frame. if (btArgs.m_withSelf) { // Builtins don't have a file and line number. if (!fp->func()->isBuiltin()) { auto const unit = fp->func()->unit(); assert(unit); auto const filename = fp->func()->filename(); ArrayInit frame(btArgs.m_parserFrame ? 4 : 2, ArrayInit::Map{}); frame.set(s_file, Variant{const_cast<StringData*>(filename)}); frame.set(s_line, unit->getLineNumber(pc)); if (btArgs.m_parserFrame) { frame.set(s_function, s_include); frame.set(s_args, Array::Create(btArgs.m_parserFrame->filename)); } bt.append(frame.toVariant()); depth++; } } // Handle the subsequent VM frames. Offset prevPc = 0; for (auto prevFp = getPrevActRec(fp, &prevPc); fp != nullptr && (btArgs.m_limit == 0 || depth < btArgs.m_limit); fp = prevFp, pc = prevPc, prevFp = getPrevActRec(fp, &prevPc)) { // Do not capture frame for HPHP only functions. if (fp->func()->isNoInjection()) continue; ArrayInit frame(7, ArrayInit::Map{}); auto const curUnit = fp->func()->unit(); auto const curOp = *reinterpret_cast<const Op*>(curUnit->at(pc)); auto const isReturning = curOp == Op::RetC || curOp == Op::RetV || curOp == Op::CreateCont || curOp == Op::Await || fp->localsDecRefd(); // Builtins and generators don't have a file and line number if (prevFp && !prevFp->func()->isBuiltin()) { auto const prevUnit = prevFp->func()->unit(); auto prevFile = prevUnit->filepath(); if (prevFp->func()->originalFilename()) { prevFile = prevFp->func()->originalFilename(); } assert(prevFile); frame.set(s_file, Variant{const_cast<StringData*>(prevFile)}); // In the normal method case, the "saved pc" for line number printing is // pointing at the cell conversion (Unbox/Pop) instruction, not the call // itself. For multi-line calls, this instruction is associated with the // subsequent line which results in an off-by-n. We're subtracting one // in order to look up the line associated with the FCall/FCallArray // instruction. Exception handling and the other opcodes (ex. BoxR) // already do the right thing. The emitter associates object access with // the subsequent expression and this would be difficult to modify. auto const opAtPrevPc = *reinterpret_cast<const Op*>(prevUnit->at(prevPc)); Offset pcAdjust = 0; if (opAtPrevPc == Op::PopR || opAtPrevPc == Op::UnboxR || opAtPrevPc == Op::UnboxRNop) { pcAdjust = 1; } frame.set(s_line, prevFp->func()->unit()->getLineNumber(prevPc - pcAdjust)); } // Check for include. String funcname{const_cast<StringData*>(fp->func()->name())}; if (fp->func()->isClosureBody()) { // Strip the file hash from the closure name. String fullName{const_cast<StringData*>(fp->func()->baseCls()->name())}; funcname = fullName.substr(0, fullName.find(';')); } // Check for pseudomain. if (funcname.empty()) { if (!prevFp && !btArgs.m_withPseudoMain) continue; else if (!prevFp) funcname = s_main; else funcname = s_include; } frame.set(s_function, funcname); if (!funcname.same(s_include)) { // Closures have an m_this but they aren't in object context. auto ctx = arGetContextClass(fp); if (ctx != nullptr && !fp->func()->isClosureBody()) { frame.set(s_class, Variant{const_cast<StringData*>(ctx->name())}); if (fp->hasThis() && !isReturning) { if (btArgs.m_withThis) { frame.set(s_object, Object(fp->getThis())); } frame.set(s_type, s_arrow); } else { frame.set(s_type, s_double_colon); } } } bool const mayUseVV = fp->func()->attrs() & AttrMayUseVV; auto const withNames = btArgs.m_withArgNames; auto const withValues = btArgs.m_withArgValues; if (!btArgs.m_withArgNames && !btArgs.m_withArgValues) { // do nothing } else if (funcname.same(s_include)) { if (depth != 0) { auto filepath = const_cast<StringData*>(curUnit->filepath()); frame.set(s_args, make_packed_array(filepath)); } } else if (!RuntimeOption::EnableArgsInBacktraces || isReturning) { // Provide an empty 'args' array to be consistent with hphpc. frame.set(s_args, empty_array()); } else { auto args = Array::Create(); auto const nparams = fp->func()->numNonVariadicParams(); auto const nargs = fp->numArgs(); auto const nformals = std::min<int>(nparams, nargs); if (UNLIKELY(mayUseVV) && UNLIKELY(fp->hasVarEnv() && fp->getVarEnv()->getFP() != fp)) { // VarEnv is attached to eval or debugger frame, other than the current // frame. Access locals thru VarEnv. auto varEnv = fp->getVarEnv(); auto func = fp->func(); for (int i = 0; i < nformals; i++) { auto const argname = func->localVarName(i); auto const tv = varEnv->lookup(argname); Variant val; if (tv != nullptr) { // the variable hasn't been unset val = withValues ? tvAsVariant(tv) : ""; } if (withNames) { args.set(String(const_cast<StringData*>(argname)), val); } else { args.append(val); } } } else { for (int i = 0; i < nformals; i++) { Variant val = withValues ? tvAsVariant(frame_local(fp, i)) : ""; if (withNames) { auto const argname = fp->func()->localVarName(i); args.set(String(const_cast<StringData*>(argname)), val); } else { args.append(val); } } } // Builtin extra args are not stored in varenv. if (UNLIKELY(mayUseVV) && nargs > nparams && fp->hasExtraArgs()) { for (int i = nparams; i < nargs; i++) { auto arg = fp->getExtraArg(i - nparams); args.append(tvAsVariant(arg)); } } frame.set(s_args, args); } if (btArgs.m_withMetadata && !isReturning) { if (UNLIKELY(mayUseVV) && UNLIKELY(fp->hasVarEnv())) { auto tv = fp->getVarEnv()->lookup(s_86metadata.get()); if (tv != nullptr && tv->m_type != KindOfUninit) { frame.set(s_metadata, tvAsVariant(tv)); } } else { auto local = fp->func()->lookupVarId(s_86metadata.get()); if (local != kInvalidId) { auto tv = frame_local(fp, local); if (tv->m_type != KindOfUninit) { frame.set(s_metadata, tvAsVariant(tv)); } } } } bt.append(frame.toVariant()); depth++; } return bt; }
Variant HHVM_FUNCTION(socket_select, VRefParam read, VRefParam write, VRefParam except, const Variant& vtv_sec, int tv_usec /* = 0 */) { int count = 0; if (!read.isNull()) { count += read.toArray().size(); } if (!write.isNull()) { count += write.toArray().size(); } if (!except.isNull()) { count += except.toArray().size(); } if (!count) { return false; } struct pollfd *fds = (struct pollfd *)calloc(count, sizeof(struct pollfd)); count = 0; if (!read.isNull()) { sock_array_to_fd_set(read.toArray(), fds, count, POLLIN); } if (!write.isNull()) { sock_array_to_fd_set(write.toArray(), fds, count, POLLOUT); } if (!except.isNull()) { sock_array_to_fd_set(except.toArray(), fds, count, POLLPRI); } if (!count) { raise_warning("no resource arrays were passed to select"); free(fds); return false; } IOStatusHelper io("socket_select"); int timeout_ms = -1; if (!vtv_sec.isNull()) { timeout_ms = vtv_sec.toInt32() * 1000 + tv_usec / 1000; } /* slight hack to support buffered data; if there is data sitting in the * read buffer of any of the streams in the read array, let's pretend * that we selected, but return only the readable sockets */ if (!read.isNull()) { auto hasData = Array::Create(); for (ArrayIter iter(read.toArray()); iter; ++iter) { auto file = cast<File>(iter.second()); if (file->bufferedLen() > 0) { hasData.append(iter.second()); } } if (hasData.size() > 0) { if (!write.isNull()) { write = empty_array(); } if (!except.isNull()) { except = empty_array(); } read = hasData; free(fds); return hasData.size(); } } int retval = poll(fds, count, timeout_ms); if (retval == -1) { raise_warning("unable to select [%d]: %s", errno, folly::errnoStr(errno).c_str()); free(fds); return false; } count = 0; int nfds = 0; if (!read.isNull()) { sock_array_from_fd_set(read, fds, nfds, count, POLLIN|POLLERR|POLLHUP); } if (!write.isNull()) { sock_array_from_fd_set(write, fds, nfds, count, POLLOUT|POLLERR); } if (!except.isNull()) { sock_array_from_fd_set(except, fds, nfds, count, POLLPRI|POLLERR); } free(fds); return count; }