Skip to content

Capability APIs for runtime code generation #25959

@morganbr

Description

@morganbr

We believe we need to add reflection emit to .NET Standard.

We already have features in .NET Standard that imply support for dynamic code generation, for example Assembly.Load(byte[]), RegexOptions.Compiled, and LambdaExpression.Compile().

Today, developers have no way to test whether runtime code generation is actually supported -- the runtime might throw PlatformNotSupported. Furthermore, even if it is supported, it might be slower as the runtime is interpreting the code (for example, by walking the expressions trees or interpreting the IL). While that ensures that scenarios that depend on code generation just work, it makes it hard to support scenarios that use code generation as a performance optimization. In those cases, not using code generation is usually faster.

Hence, we need an API that allows developers to check for the support of code generation as well as an API that allows developers to check using runtime code generation as a performance optimization is sensible.

API Proposal

namespace System.Runtime.CompilerServices
{
    public static class RuntimeFeature
    {
        public static bool IsDynamicCodeSupported { get; }
        public static bool IsDynamicCodeCompiled { get; }
    }
}

The semantics are:

  • If the runtime supports loading and running dynamic code, IsDynamicCodeSupported returns true. This doesn't tell developers whether the code is interpreted or compiled.
  • If the runtime supports loading and running dynamic code by using a just-in-time compiler, IsDynamicCodeCompiled returns true.
  • RuntimeFeature.IsSupported(string) also returns the appropriate value when called with the strings "IsDynamicCodeSupported" and "IsDynamicCodeCompiled".

Affected features

  • Loading assemblies from bytes (Assembly.Load(byte[]))
  • Linq expression trees
  • Reflection emit
  • DynamicMethod (lightweight code generation, LCG)
  • DispatchProxy
  • Regex

Scenarios

Failing early with RuntimeFeature.IsDynamicCodeSupported

In some scenarios, performance isn't the primary goal but use to get stuff to work, for example, mocking. A mocking framework might provide an API like Proxy.Create<TInterface>() that returns you a type that implements TInterface. In this case, performance isn't the higher order and the only thing that matters is that the runtime supports generating code on the fly. Some environments, for example, iOS, disallow actual JITs. For that reason, the Xamarin team is considering providing an IL interpreter to make the scenarios work.

The author of the mocking framework could just use reflection emit and rely on the implementation of it to throw PlatformNotSupported but a much better developer experience would be to check early & fail early with a descriptive error message. For example:

public static class Proxy
{
    public static Proxy<TInterface> Create<TInterface>()
    {
        if (!RuntimeFeature.IsDynamicCodeSupported)
            throw new PlatformNotSupportedException("We cannot create proxies on your runtime because it doesn't allow dynamic code generation.");

        return UseReflectionEmitToCreateImplementation();
    }
}

Accelerating using RuntimeFeature.IsDynamicCodeCompiled

In most cases, dynamically generating code is meant as a performance optimization. This

var options = RegexOptions.IgnoreCase | RegexOptions.Singleline;

// We only want to use compiled regexes if the underlying runtime
// supports compiling IL with a JIT. Otherwise it's likely much slower
// than walking the state machine.

if (RuntimeFeature.IsDynamicCodeCompiled)
{
    options |= RegexOptions.Compiled;
}

Similar areas include using expression trees to accelerate setting properties and calling constructors in IoC systems.

Metadata

Metadata

Assignees

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions