Uh oh!
There was an error while loading. Please reload this page.
') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })();
There was an error while loading. Please reload this page.
Background and Motivation
In scenarios like reflection or interaction with other languages or platforms, there is not a generically usable and performant way to handle variable number of arbitrary arguments. C# provides syntax like
params object[]orparams T[](and might supportparams Span<T>in the future), but that works best in cases where the parameters are homogenous.refparameters are especially cumbersome to translate toparams, as it requires copying the value back and forth in case the called method modifies it.Prime targets are
Delegate.DynamicInvokeorMethodInfo.Invoke. Here the call could be translated to invoking a method with any number of parameters, normal ofref. Calling a method with a singleref intargument requires 3 heap allocations – one for the array, one for boxing the argument as the input, and one for boxing the argument as the output.This proposal tries to provide a way to solve this issue, without significant modifications to the runtime, and if that is deemed inappropriate, to instigate discussion about other and better options.
Proposed API
The core of this proposal is a type that is conceptually similar to
Span<TypedReference*>, if that were a valid type.This is also an example implementation in C# (if returning
TypedReferencewas possible in case of the indexer). Methods ofSpan<T>could also be ported toByRefSpan.Usage Examples
Cooperation from specific languages is needed to make the usage of this type more "beautiful", but this prepares the ground:
In a potential future where C# supports
params ByRefSpan, the boilerplate code inCallercould easily be simplified toDynamicMethod(ref arg1, ref arg2, ref arg3);Alternative Designs
At first I experimented with simple
stackalloc TypedReference[3], but the runtime doesn't track stack space allocated this way (meaning GC ignores any references to objects in it), so until such a change is made (which would enable things likestackalloc (int, string)[10]and more), or until it is possible to have a fixed-size by-value array as a local variable or another way to statically group locals together as a span, individualTypedReferencevariables must be used and the double reference is needed. In essence, a tuple type like(TypedReference, TypedReference, TypedReference)would be needed.There is also place for
ByRefSpan<T>storingT**in a similar fashion. However, there is no way in C# I am aware of to use such a type, so first something like the internalByReference<T>has to be exposed.Another option is to resurrect and improve
__arglist,RuntimeTypeHandleandArgIterator, however those are less efficient and exist only for interaction with unmanaged code. Being able to constructByRefSpanfromRuntimeArgumentHandlewould be nice though.Risks
This type is used to store a pointer to potentially stack-allocated data, like
Span<T>, but the risks of a value escaping the stack are solved by usingref struct. The only other risks stem from the way this type is used, which would, hopefully, be mitigated by improvements in the language.