You've already forked linux-packaging-mono
Imported Upstream version 4.0.0~alpha1
Former-commit-id: 806294f5ded97629b74c85c09952f2a74fe182d9
This commit is contained in:
37
external/referencesource/System.Core/Microsoft/Scripting/Compiler/AnalyzedTree.cs
vendored
Normal file
37
external/referencesource/System.Core/Microsoft/Scripting/Compiler/AnalyzedTree.cs
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
/* ****************************************************************************
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* This source code is subject to terms and conditions of the Apache License, Version 2.0. A
|
||||
* copy of the license can be found in the License.html file at the root of this distribution. If
|
||||
* you cannot locate the Apache License, Version 2.0, please send an email to
|
||||
* dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
|
||||
* by the terms of the Apache License, Version 2.0.
|
||||
*
|
||||
* You must not remove this notice, or any other, from this software.
|
||||
*
|
||||
*
|
||||
* ***************************************************************************/
|
||||
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.SymbolStore;
|
||||
using System.Dynamic.Utils;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
#if CLR2
|
||||
namespace Microsoft.Scripting.Ast.Compiler {
|
||||
#else
|
||||
namespace System.Linq.Expressions.Compiler {
|
||||
#endif
|
||||
internal sealed class AnalyzedTree {
|
||||
internal readonly Dictionary<object, CompilerScope> Scopes = new Dictionary<object, CompilerScope>();
|
||||
internal readonly Dictionary<LambdaExpression, BoundConstants> Constants = new Dictionary<LambdaExpression, BoundConstants>();
|
||||
|
||||
internal DebugInfoGenerator DebugInfoGenerator { get; set; }
|
||||
|
||||
// Created by VariableBinder
|
||||
internal AnalyzedTree() {
|
||||
}
|
||||
}
|
||||
}
|
179
external/referencesource/System.Core/Microsoft/Scripting/Compiler/AssemblyGen.cs
vendored
Normal file
179
external/referencesource/System.Core/Microsoft/Scripting/Compiler/AssemblyGen.cs
vendored
Normal file
@ -0,0 +1,179 @@
|
||||
/* ****************************************************************************
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* This source code is subject to terms and conditions of the Apache License, Version 2.0. A
|
||||
* copy of the license can be found in the License.html file at the root of this distribution. If
|
||||
* you cannot locate the Apache License, Version 2.0, please send an email to
|
||||
* dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
|
||||
* by the terms of the Apache License, Version 2.0.
|
||||
*
|
||||
* You must not remove this notice, or any other, from this software.
|
||||
*
|
||||
*
|
||||
* ***************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Dynamic.Utils;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using System.Security;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
#if CLR2
|
||||
namespace Microsoft.Scripting.Ast.Compiler {
|
||||
#else
|
||||
namespace System.Linq.Expressions.Compiler {
|
||||
#endif
|
||||
internal sealed class AssemblyGen {
|
||||
private static AssemblyGen _assembly;
|
||||
|
||||
// Testing options. Only ever set in CLR2 build
|
||||
// configurations, see SetSaveAssemblies
|
||||
#if CLR2
|
||||
private static string _saveAssembliesPath;
|
||||
private static bool _saveAssemblies;
|
||||
#endif
|
||||
|
||||
private readonly AssemblyBuilder _myAssembly;
|
||||
private readonly ModuleBuilder _myModule;
|
||||
|
||||
#if CLR2 && !SILVERLIGHT
|
||||
private readonly string _outFileName; // can be null iff !SaveAndReloadAssemblies
|
||||
private readonly string _outDir; // null means the current directory
|
||||
#endif
|
||||
private int _index;
|
||||
|
||||
private static AssemblyGen Assembly {
|
||||
get {
|
||||
if (_assembly == null) {
|
||||
Interlocked.CompareExchange(ref _assembly, new AssemblyGen(), null);
|
||||
}
|
||||
return _assembly;
|
||||
}
|
||||
}
|
||||
|
||||
private AssemblyGen() {
|
||||
var name = new AssemblyName("Snippets");
|
||||
|
||||
#if SILVERLIGHT // AssemblyBuilderAccess.RunAndSave, Environment.CurrentDirectory
|
||||
_myAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.Run);
|
||||
_myModule = _myAssembly.DefineDynamicModule(name.Name, false);
|
||||
#else
|
||||
|
||||
// mark the assembly transparent so that it works in partial trust:
|
||||
var attributes = new[] {
|
||||
new CustomAttributeBuilder(typeof(SecurityTransparentAttribute).GetConstructor(Type.EmptyTypes), new object[0])
|
||||
};
|
||||
|
||||
#if CLR2
|
||||
if (_saveAssemblies) {
|
||||
string outDir = _saveAssembliesPath ?? Directory.GetCurrentDirectory();
|
||||
try {
|
||||
outDir = Path.GetFullPath(outDir);
|
||||
} catch (Exception) {
|
||||
throw Error.InvalidOutputDir();
|
||||
}
|
||||
try {
|
||||
Path.Combine(outDir, name.Name + ".dll");
|
||||
} catch (ArgumentException) {
|
||||
throw Error.InvalidAsmNameOrExtension();
|
||||
}
|
||||
|
||||
_outFileName = name.Name + ".dll";
|
||||
_outDir = outDir;
|
||||
_myAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.RunAndSave, outDir,
|
||||
null, null, null, null, false, attributes);
|
||||
|
||||
_myModule = _myAssembly.DefineDynamicModule(name.Name, _outFileName, false);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
_myAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.Run, attributes);
|
||||
_myModule = _myAssembly.DefineDynamicModule(name.Name, false);
|
||||
}
|
||||
|
||||
_myAssembly.DefineVersionInfoResource();
|
||||
#endif
|
||||
}
|
||||
|
||||
private TypeBuilder DefineType(string name, Type parent, TypeAttributes attr) {
|
||||
ContractUtils.RequiresNotNull(name, "name");
|
||||
ContractUtils.RequiresNotNull(parent, "parent");
|
||||
|
||||
StringBuilder sb = new StringBuilder(name);
|
||||
|
||||
int index = Interlocked.Increment(ref _index);
|
||||
sb.Append("$");
|
||||
sb.Append(index);
|
||||
|
||||
// There is a bug in Reflection.Emit that leads to
|
||||
// Unhandled Exception: System.Runtime.InteropServices.COMException (0x80131130): Record not found on lookup.
|
||||
// if there is any of the characters []*&+,\ in the type name and a method defined on the type is called.
|
||||
sb.Replace('+', '_').Replace('[', '_').Replace(']', '_').Replace('*', '_').Replace('&', '_').Replace(',', '_').Replace('\\', '_');
|
||||
|
||||
name = sb.ToString();
|
||||
|
||||
return _myModule.DefineType(name, attr, parent);
|
||||
}
|
||||
|
||||
internal static TypeBuilder DefineDelegateType(string name) {
|
||||
return Assembly.DefineType(
|
||||
name,
|
||||
typeof(MulticastDelegate),
|
||||
TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.AnsiClass | TypeAttributes.AutoClass
|
||||
);
|
||||
}
|
||||
|
||||
#if CLR2
|
||||
//Return the location of the saved assembly file.
|
||||
//The file location is used by PE verification in Microsoft.Scripting.
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal string SaveAssembly() {
|
||||
#if !SILVERLIGHT // AssemblyBuilder.Save
|
||||
_myAssembly.Save(_outFileName, PortableExecutableKinds.ILOnly, ImageFileMachine.I386);
|
||||
return Path.Combine(_outDir, _outFileName);
|
||||
#else
|
||||
return null;
|
||||
#endif
|
||||
}
|
||||
|
||||
// NOTE: this method is called through reflection from Microsoft.Scripting
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal static void SetSaveAssemblies(bool enable, string directory) {
|
||||
_saveAssemblies = enable;
|
||||
_saveAssembliesPath = directory;
|
||||
}
|
||||
|
||||
// NOTE: this method is called through reflection from Microsoft.Scripting
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal static string[] SaveAssembliesToDisk() {
|
||||
if (!_saveAssemblies) {
|
||||
return new string[0];
|
||||
}
|
||||
|
||||
var assemlyLocations = new List<string>();
|
||||
|
||||
// first save all assemblies to disk:
|
||||
if (_assembly != null) {
|
||||
string assemblyLocation = _assembly.SaveAssembly();
|
||||
if (assemblyLocation != null) {
|
||||
assemlyLocations.Add(assemblyLocation);
|
||||
}
|
||||
_assembly = null;
|
||||
}
|
||||
|
||||
return assemlyLocations.ToArray();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
internal static class SymbolGuids {
|
||||
internal static readonly Guid DocumentType_Text =
|
||||
new Guid(0x5a869d0b, 0x6611, 0x11d3, 0xbd, 0x2a, 0, 0, 0xf8, 8, 0x49, 0xbd);
|
||||
}
|
||||
}
|
||||
|
193
external/referencesource/System.Core/Microsoft/Scripting/Compiler/BoundConstants.cs
vendored
Normal file
193
external/referencesource/System.Core/Microsoft/Scripting/Compiler/BoundConstants.cs
vendored
Normal file
@ -0,0 +1,193 @@
|
||||
/* ****************************************************************************
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* This source code is subject to terms and conditions of the Apache License, Version 2.0. A
|
||||
* copy of the license can be found in the License.html file at the root of this distribution. If
|
||||
* you cannot locate the Apache License, Version 2.0, please send an email to
|
||||
* dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
|
||||
* by the terms of the Apache License, Version 2.0.
|
||||
*
|
||||
* You must not remove this notice, or any other, from this software.
|
||||
*
|
||||
*
|
||||
* ***************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection.Emit;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Dynamic.Utils;
|
||||
|
||||
#if SILVERLIGHT
|
||||
using System.Core;
|
||||
#endif
|
||||
|
||||
#if CLR2
|
||||
namespace Microsoft.Scripting.Ast.Compiler {
|
||||
#else
|
||||
namespace System.Linq.Expressions.Compiler {
|
||||
#endif
|
||||
/// <summary>
|
||||
/// This type tracks "runtime" constants--live objects that appear in
|
||||
/// ConstantExpression nodes and must be bound to the delegate.
|
||||
/// </summary>
|
||||
internal sealed class BoundConstants {
|
||||
|
||||
/// <summary>
|
||||
/// Constants can emit themselves as different types
|
||||
/// For caching purposes, we need to treat each distinct Type as a
|
||||
/// seperate thing to cache. (If we have to cast it on the way out, it
|
||||
/// ends up using a JIT temp and defeats the purpose of caching the
|
||||
/// value in a local)
|
||||
/// </summary>
|
||||
private struct TypedConstant : IEquatable<TypedConstant> {
|
||||
internal readonly object Value;
|
||||
internal readonly Type Type;
|
||||
|
||||
internal TypedConstant(object value, Type type) {
|
||||
Value = value;
|
||||
Type = type;
|
||||
}
|
||||
|
||||
public override int GetHashCode() {
|
||||
return RuntimeHelpers.GetHashCode(Value) ^ Type.GetHashCode();
|
||||
}
|
||||
public bool Equals(TypedConstant other) {
|
||||
return object.ReferenceEquals(Value, other.Value) && Type.Equals(other.Type);
|
||||
}
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2231:OverloadOperatorEqualsOnOverridingValueTypeEquals")]
|
||||
public override bool Equals(object obj) {
|
||||
return (obj is TypedConstant) && Equals((TypedConstant)obj);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The list of constants in the order they appear in the constant array
|
||||
/// </summary>
|
||||
private readonly List<object> _values = new List<object>();
|
||||
|
||||
/// <summary>
|
||||
/// The index of each constant in the constant array
|
||||
/// </summary>
|
||||
private readonly Dictionary<object, int> _indexes = new Dictionary<object, int>(ReferenceEqualityComparer<object>.Instance);
|
||||
|
||||
/// <summary>
|
||||
/// Each constant referenced within this lambda, and how often it was referenced
|
||||
/// </summary>
|
||||
private readonly Dictionary<TypedConstant, int> _references = new Dictionary<TypedConstant, int>();
|
||||
|
||||
/// <summary>
|
||||
/// IL locals for storing frequently used constants
|
||||
/// </summary>
|
||||
private readonly Dictionary<TypedConstant, LocalBuilder> _cache = new Dictionary<TypedConstant, LocalBuilder>();
|
||||
|
||||
internal int Count {
|
||||
get { return _values.Count; }
|
||||
}
|
||||
|
||||
internal object[] ToArray() {
|
||||
return _values.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by VariableBinder. Adds the constant to the list (if needed)
|
||||
/// and increases the reference count by one
|
||||
/// </summary>
|
||||
internal void AddReference(object value, Type type) {
|
||||
if (!_indexes.ContainsKey(value)) {
|
||||
_indexes.Add(value, _values.Count);
|
||||
_values.Add(value);
|
||||
}
|
||||
Helpers.IncrementCount(new TypedConstant(value, type), _references);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Emits a live object as a constant
|
||||
/// </summary>
|
||||
internal void EmitConstant(LambdaCompiler lc, object value, Type type) {
|
||||
Debug.Assert(!ILGen.CanEmitConstant(value, type));
|
||||
|
||||
if (!lc.CanEmitBoundConstants) {
|
||||
throw Error.CannotCompileConstant(value);
|
||||
}
|
||||
|
||||
LocalBuilder local;
|
||||
if (_cache.TryGetValue(new TypedConstant(value, type), out local)) {
|
||||
lc.IL.Emit(OpCodes.Ldloc, local);
|
||||
return;
|
||||
}
|
||||
EmitConstantsArray(lc);
|
||||
EmitConstantFromArray(lc, value, type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Emit code to cache frequently used constants into IL locals,
|
||||
/// instead of pulling them out of the array each time
|
||||
/// </summary>
|
||||
internal void EmitCacheConstants(LambdaCompiler lc) {
|
||||
int count = 0;
|
||||
foreach (var reference in _references) {
|
||||
if (!lc.CanEmitBoundConstants) {
|
||||
throw Error.CannotCompileConstant(reference.Key.Value);
|
||||
}
|
||||
|
||||
if (ShouldCache(reference.Value)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (count == 0) {
|
||||
return;
|
||||
}
|
||||
EmitConstantsArray(lc);
|
||||
|
||||
// The same lambda can be in multiple places in the tree, so we
|
||||
// need to clear any locals from last time.
|
||||
_cache.Clear();
|
||||
|
||||
foreach (var reference in _references) {
|
||||
if (ShouldCache(reference.Value)) {
|
||||
if (--count > 0) {
|
||||
// Dup array to keep it on the stack
|
||||
lc.IL.Emit(OpCodes.Dup);
|
||||
}
|
||||
LocalBuilder local = lc.IL.DeclareLocal(reference.Key.Type);
|
||||
EmitConstantFromArray(lc, reference.Key.Value, local.LocalType);
|
||||
lc.IL.Emit(OpCodes.Stloc, local);
|
||||
_cache.Add(reference.Key, local);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool ShouldCache(int refCount) {
|
||||
// This caching is too aggressive in the face of conditionals and
|
||||
// switch. Also, it is too conservative for variables used inside
|
||||
// of loops.
|
||||
return refCount > 2;
|
||||
}
|
||||
|
||||
private static void EmitConstantsArray(LambdaCompiler lc) {
|
||||
Debug.Assert(lc.CanEmitBoundConstants); // this should've been checked already
|
||||
|
||||
lc.EmitClosureArgument();
|
||||
lc.IL.Emit(OpCodes.Ldfld, typeof(Closure).GetField("Constants"));
|
||||
}
|
||||
|
||||
private void EmitConstantFromArray(LambdaCompiler lc, object value, Type type) {
|
||||
int index;
|
||||
if (!_indexes.TryGetValue(value, out index)) {
|
||||
_indexes.Add(value, index = _values.Count);
|
||||
_values.Add(value);
|
||||
}
|
||||
|
||||
lc.IL.EmitInt(index);
|
||||
lc.IL.Emit(OpCodes.Ldelem_Ref);
|
||||
if (type.IsValueType) {
|
||||
lc.IL.Emit(OpCodes.Unbox_Any, type);
|
||||
} else if (type != typeof(object)) {
|
||||
lc.IL.Emit(OpCodes.Castclass, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
51
external/referencesource/System.Core/Microsoft/Scripting/Compiler/Closure.cs
vendored
Normal file
51
external/referencesource/System.Core/Microsoft/Scripting/Compiler/Closure.cs
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
/* ****************************************************************************
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* This source code is subject to terms and conditions of the Apache License, Version 2.0. A
|
||||
* copy of the license can be found in the License.html file at the root of this distribution. If
|
||||
* you cannot locate the Apache License, Version 2.0, please send an email to
|
||||
* dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
|
||||
* by the terms of the Apache License, Version 2.0.
|
||||
*
|
||||
* You must not remove this notice, or any other, from this software.
|
||||
*
|
||||
*
|
||||
* ***************************************************************************/
|
||||
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace System.Runtime.CompilerServices {
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the .NET Framework infrastructure and is not intended to be used directly from your code.
|
||||
/// Represents the runtime state of a dynamically generated method.
|
||||
/// </summary>
|
||||
[EditorBrowsable(EditorBrowsableState.Never), DebuggerStepThrough]
|
||||
public sealed class Closure {
|
||||
/// <summary>
|
||||
/// Represents the non-trivial constants and locally executable expressions that are referenced by a dynamically generated method.
|
||||
/// </summary>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2105:ArrayFieldsShouldNotBeReadOnly")]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")]
|
||||
public readonly object[] Constants;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the hoisted local variables from the parent context.
|
||||
/// </summary>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2105:ArrayFieldsShouldNotBeReadOnly")]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")]
|
||||
public readonly object[] Locals;
|
||||
|
||||
/// <summary>
|
||||
/// Creates an object to hold state of a dynamically generated method.
|
||||
/// </summary>
|
||||
/// <param name="constants">The constant values used by the method.</param>
|
||||
/// <param name="locals">The hoisted local variables from the parent context.</param>
|
||||
public Closure(object[] constants, object[] locals) {
|
||||
Constants = constants;
|
||||
Locals = locals;
|
||||
}
|
||||
}
|
||||
}
|
186
external/referencesource/System.Core/Microsoft/Scripting/Compiler/CompilerScope.Storage.cs
vendored
Normal file
186
external/referencesource/System.Core/Microsoft/Scripting/Compiler/CompilerScope.Storage.cs
vendored
Normal file
@ -0,0 +1,186 @@
|
||||
/* ****************************************************************************
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* This source code is subject to terms and conditions of the Apache License, Version 2.0. A
|
||||
* copy of the license can be found in the License.html file at the root of this distribution. If
|
||||
* you cannot locate the Apache License, Version 2.0, please send an email to
|
||||
* dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
|
||||
* by the terms of the Apache License, Version 2.0.
|
||||
*
|
||||
* You must not remove this notice, or any other, from this software.
|
||||
*
|
||||
*
|
||||
* ***************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
#if CLR2
|
||||
namespace Microsoft.Scripting.Ast.Compiler {
|
||||
#else
|
||||
namespace System.Linq.Expressions.Compiler {
|
||||
#endif
|
||||
|
||||
internal sealed partial class CompilerScope {
|
||||
|
||||
private abstract class Storage {
|
||||
internal readonly LambdaCompiler Compiler;
|
||||
internal readonly ParameterExpression Variable;
|
||||
|
||||
internal Storage(LambdaCompiler compiler, ParameterExpression variable) {
|
||||
Compiler = compiler;
|
||||
Variable = variable;
|
||||
}
|
||||
|
||||
internal abstract void EmitLoad();
|
||||
internal abstract void EmitAddress();
|
||||
internal abstract void EmitStore();
|
||||
|
||||
internal virtual void EmitStore(Storage value) {
|
||||
value.EmitLoad();
|
||||
EmitStore();
|
||||
}
|
||||
|
||||
internal virtual void FreeLocal() {
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class LocalStorage : Storage {
|
||||
private readonly LocalBuilder _local;
|
||||
|
||||
internal LocalStorage(LambdaCompiler compiler, ParameterExpression variable)
|
||||
: base(compiler, variable) {
|
||||
// ByRef variables are supported. This is used internally by
|
||||
// the compiler when emitting an inlined lambda invoke, to
|
||||
// handle ByRef parameters. BlockExpression prevents this
|
||||
// from being exposed to user created trees.
|
||||
_local = compiler.GetNamedLocal(variable.IsByRef ? variable.Type.MakeByRefType() : variable.Type, variable);
|
||||
}
|
||||
|
||||
internal override void EmitLoad() {
|
||||
Compiler.IL.Emit(OpCodes.Ldloc, _local);
|
||||
}
|
||||
|
||||
internal override void EmitStore() {
|
||||
Compiler.IL.Emit(OpCodes.Stloc, _local);
|
||||
}
|
||||
|
||||
internal override void EmitAddress() {
|
||||
Compiler.IL.Emit(OpCodes.Ldloca, _local);
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class ArgumentStorage : Storage {
|
||||
private readonly int _argument;
|
||||
|
||||
internal ArgumentStorage(LambdaCompiler compiler, ParameterExpression p)
|
||||
: base(compiler, p) {
|
||||
_argument = compiler.GetLambdaArgument(compiler.Parameters.IndexOf(p));
|
||||
}
|
||||
|
||||
internal override void EmitLoad() {
|
||||
Compiler.IL.EmitLoadArg(_argument);
|
||||
}
|
||||
|
||||
internal override void EmitStore() {
|
||||
Compiler.IL.EmitStoreArg(_argument);
|
||||
}
|
||||
|
||||
internal override void EmitAddress() {
|
||||
Compiler.IL.EmitLoadArgAddress(_argument);
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class ElementBoxStorage : Storage {
|
||||
private readonly int _index;
|
||||
private readonly Storage _array;
|
||||
private readonly Type _boxType;
|
||||
private readonly FieldInfo _boxValueField;
|
||||
|
||||
internal ElementBoxStorage(Storage array, int index, ParameterExpression variable)
|
||||
: base(array.Compiler, variable) {
|
||||
_array = array;
|
||||
_index = index;
|
||||
_boxType = typeof(StrongBox<>).MakeGenericType(variable.Type);
|
||||
_boxValueField = _boxType.GetField("Value");
|
||||
}
|
||||
|
||||
internal override void EmitLoad() {
|
||||
EmitLoadBox();
|
||||
Compiler.IL.Emit(OpCodes.Ldfld, _boxValueField);
|
||||
}
|
||||
|
||||
internal override void EmitStore() {
|
||||
LocalBuilder value = Compiler.GetLocal(Variable.Type);
|
||||
Compiler.IL.Emit(OpCodes.Stloc, value);
|
||||
EmitLoadBox();
|
||||
Compiler.IL.Emit(OpCodes.Ldloc, value);
|
||||
Compiler.FreeLocal(value);
|
||||
Compiler.IL.Emit(OpCodes.Stfld, _boxValueField);
|
||||
}
|
||||
|
||||
internal override void EmitStore(Storage value) {
|
||||
EmitLoadBox();
|
||||
value.EmitLoad();
|
||||
Compiler.IL.Emit(OpCodes.Stfld, _boxValueField);
|
||||
}
|
||||
|
||||
internal override void EmitAddress() {
|
||||
EmitLoadBox();
|
||||
Compiler.IL.Emit(OpCodes.Ldflda, _boxValueField);
|
||||
}
|
||||
|
||||
internal void EmitLoadBox() {
|
||||
_array.EmitLoad();
|
||||
Compiler.IL.EmitInt(_index);
|
||||
Compiler.IL.Emit(OpCodes.Ldelem_Ref);
|
||||
Compiler.IL.Emit(OpCodes.Castclass, _boxType);
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class LocalBoxStorage : Storage {
|
||||
private readonly LocalBuilder _boxLocal;
|
||||
private readonly Type _boxType;
|
||||
private readonly FieldInfo _boxValueField;
|
||||
|
||||
internal LocalBoxStorage(LambdaCompiler compiler, ParameterExpression variable)
|
||||
: base(compiler, variable) {
|
||||
_boxType = typeof(StrongBox<>).MakeGenericType(variable.Type);
|
||||
_boxValueField = _boxType.GetField("Value");
|
||||
_boxLocal = compiler.GetNamedLocal(_boxType, variable);
|
||||
}
|
||||
|
||||
internal override void EmitLoad() {
|
||||
Compiler.IL.Emit(OpCodes.Ldloc, _boxLocal);
|
||||
Compiler.IL.Emit(OpCodes.Ldfld, _boxValueField);
|
||||
}
|
||||
|
||||
internal override void EmitAddress() {
|
||||
Compiler.IL.Emit(OpCodes.Ldloc, _boxLocal);
|
||||
Compiler.IL.Emit(OpCodes.Ldflda, _boxValueField);
|
||||
}
|
||||
|
||||
internal override void EmitStore() {
|
||||
LocalBuilder value = Compiler.GetLocal(Variable.Type);
|
||||
Compiler.IL.Emit(OpCodes.Stloc, value);
|
||||
Compiler.IL.Emit(OpCodes.Ldloc, _boxLocal);
|
||||
Compiler.IL.Emit(OpCodes.Ldloc, value);
|
||||
Compiler.FreeLocal(value);
|
||||
Compiler.IL.Emit(OpCodes.Stfld, _boxValueField);
|
||||
}
|
||||
|
||||
internal override void EmitStore(Storage value) {
|
||||
Compiler.IL.Emit(OpCodes.Ldloc, _boxLocal);
|
||||
value.EmitLoad();
|
||||
Compiler.IL.Emit(OpCodes.Stfld, _boxValueField);
|
||||
}
|
||||
|
||||
internal void EmitStoreBox() {
|
||||
Compiler.IL.Emit(OpCodes.Stloc, _boxLocal);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
468
external/referencesource/System.Core/Microsoft/Scripting/Compiler/CompilerScope.cs
vendored
Normal file
468
external/referencesource/System.Core/Microsoft/Scripting/Compiler/CompilerScope.cs
vendored
Normal file
@ -0,0 +1,468 @@
|
||||
/* ****************************************************************************
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* This source code is subject to terms and conditions of the Apache License, Version 2.0. A
|
||||
* copy of the license can be found in the License.html file at the root of this distribution. If
|
||||
* you cannot locate the Apache License, Version 2.0, please send an email to
|
||||
* dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
|
||||
* by the terms of the Apache License, Version 2.0.
|
||||
*
|
||||
* You must not remove this notice, or any other, from this software.
|
||||
*
|
||||
*
|
||||
* ***************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection.Emit;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Dynamic;
|
||||
using System.Dynamic.Utils;
|
||||
|
||||
#if SILVERLIGHT
|
||||
using System.Core;
|
||||
#endif
|
||||
|
||||
#if CLR2
|
||||
namespace Microsoft.Scripting.Ast.Compiler {
|
||||
#else
|
||||
namespace System.Linq.Expressions.Compiler {
|
||||
#endif
|
||||
internal enum VariableStorageKind {
|
||||
Local,
|
||||
Hoisted
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// CompilerScope is the data structure which the Compiler keeps information
|
||||
/// related to compiling scopes. It stores the following information:
|
||||
/// 1. Parent relationship (for resolving variables)
|
||||
/// 2. Information about hoisted variables
|
||||
/// 3. Information for resolving closures
|
||||
///
|
||||
/// Instances are produced by VariableBinder, which does a tree walk
|
||||
/// looking for scope nodes: LambdaExpression and BlockExpression.
|
||||
/// </summary>
|
||||
internal sealed partial class CompilerScope {
|
||||
/// <summary>
|
||||
/// parent scope, if any
|
||||
/// </summary>
|
||||
private CompilerScope _parent;
|
||||
|
||||
/// <summary>
|
||||
/// The expression node for this scope
|
||||
/// Can be LambdaExpression, BlockExpression, or CatchBlock
|
||||
/// </summary>
|
||||
internal readonly object Node;
|
||||
|
||||
/// <summary>
|
||||
/// True if this node corresponds to an IL method.
|
||||
/// Can only be true if the Node is a LambdaExpression.
|
||||
/// But inlined lambdas will have it set to false.
|
||||
/// </summary>
|
||||
internal readonly bool IsMethod;
|
||||
|
||||
/// <summary>
|
||||
/// Does this scope (or any inner scope) close over variables from any
|
||||
/// parent scope?
|
||||
/// Populated by VariableBinder
|
||||
/// </summary>
|
||||
internal bool NeedsClosure;
|
||||
|
||||
/// <summary>
|
||||
/// Variables defined in this scope, and whether they're hoisted or not
|
||||
/// Populated by VariableBinder
|
||||
/// </summary>
|
||||
internal readonly Dictionary<ParameterExpression, VariableStorageKind> Definitions = new Dictionary<ParameterExpression, VariableStorageKind>();
|
||||
|
||||
/// <summary>
|
||||
/// Each variable referenced within this scope, and how often it was referenced
|
||||
/// Populated by VariableBinder
|
||||
/// </summary>
|
||||
internal Dictionary<ParameterExpression, int> ReferenceCount;
|
||||
|
||||
/// <summary>
|
||||
/// Scopes whose variables were merged into this one
|
||||
///
|
||||
/// Created lazily as we create hundreds of compiler scopes w/o merging scopes when compiling rules.
|
||||
/// </summary>
|
||||
internal Set<object> MergedScopes;
|
||||
|
||||
/// <summary>
|
||||
/// The scope's hoisted locals, if any.
|
||||
/// Provides storage for variables that are referenced from nested lambdas
|
||||
/// </summary>
|
||||
private HoistedLocals _hoistedLocals;
|
||||
|
||||
/// <summary>
|
||||
/// The closed over hoisted locals
|
||||
/// </summary>
|
||||
private HoistedLocals _closureHoistedLocals;
|
||||
|
||||
/// <summary>
|
||||
/// Mutable dictionary that maps non-hoisted variables to either local
|
||||
/// slots or argument slots
|
||||
/// </summary>
|
||||
private readonly Dictionary<ParameterExpression, Storage> _locals = new Dictionary<ParameterExpression, Storage>();
|
||||
|
||||
internal CompilerScope(object node, bool isMethod) {
|
||||
Node = node;
|
||||
IsMethod = isMethod;
|
||||
var variables = GetVariables(node);
|
||||
|
||||
Definitions = new Dictionary<ParameterExpression, VariableStorageKind>(variables.Count);
|
||||
foreach (var v in variables) {
|
||||
Definitions.Add(v, VariableStorageKind.Local);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This scope's hoisted locals, or the closed over locals, if any
|
||||
/// Equivalent to: _hoistedLocals ?? _closureHoistedLocals
|
||||
/// </summary>
|
||||
internal HoistedLocals NearestHoistedLocals {
|
||||
get { return _hoistedLocals ?? _closureHoistedLocals; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when entering a lambda/block. Performs all variable allocation
|
||||
/// needed, including creating hoisted locals and IL locals for accessing
|
||||
/// parent locals
|
||||
/// </summary>
|
||||
internal CompilerScope Enter(LambdaCompiler lc, CompilerScope parent) {
|
||||
SetParent(lc, parent);
|
||||
|
||||
AllocateLocals(lc);
|
||||
|
||||
if (IsMethod && _closureHoistedLocals != null) {
|
||||
EmitClosureAccess(lc, _closureHoistedLocals);
|
||||
}
|
||||
|
||||
EmitNewHoistedLocals(lc);
|
||||
|
||||
if (IsMethod) {
|
||||
EmitCachedVariables();
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Frees unnamed locals, clears state associated with this compiler
|
||||
/// </summary>
|
||||
internal CompilerScope Exit() {
|
||||
// free scope's variables
|
||||
if (!IsMethod) {
|
||||
foreach (Storage storage in _locals.Values) {
|
||||
storage.FreeLocal();
|
||||
}
|
||||
}
|
||||
|
||||
// Clear state that is associated with this parent
|
||||
// (because the scope can be reused in another context)
|
||||
CompilerScope parent = _parent;
|
||||
_parent = null;
|
||||
_hoistedLocals = null;
|
||||
_closureHoistedLocals = null;
|
||||
_locals.Clear();
|
||||
|
||||
return parent;
|
||||
}
|
||||
|
||||
#region LocalScopeExpression support
|
||||
|
||||
internal void EmitVariableAccess(LambdaCompiler lc, ReadOnlyCollection<ParameterExpression> vars) {
|
||||
if (NearestHoistedLocals != null) {
|
||||
// Find what array each variable is on & its index
|
||||
var indexes = new List<long>(vars.Count);
|
||||
|
||||
foreach (var variable in vars) {
|
||||
// For each variable, find what array it's defined on
|
||||
ulong parents = 0;
|
||||
HoistedLocals locals = NearestHoistedLocals;
|
||||
while (!locals.Indexes.ContainsKey(variable)) {
|
||||
parents++;
|
||||
locals = locals.Parent;
|
||||
Debug.Assert(locals != null);
|
||||
}
|
||||
|
||||
// combine the number of parents we walked, with the
|
||||
// real index of variable to get the index to emit.
|
||||
ulong index = (parents << 32) | (uint)locals.Indexes[variable];
|
||||
|
||||
indexes.Add((long)index);
|
||||
}
|
||||
|
||||
if (indexes.Count > 0) {
|
||||
EmitGet(NearestHoistedLocals.SelfVariable);
|
||||
lc.EmitConstantArray(indexes.ToArray());
|
||||
lc.IL.Emit(OpCodes.Call, typeof(RuntimeOps).GetMethod("CreateRuntimeVariables", new[] { typeof(object[]), typeof(long[]) }));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// No visible variables
|
||||
lc.IL.Emit(OpCodes.Call, typeof(RuntimeOps).GetMethod("CreateRuntimeVariables", Type.EmptyTypes));
|
||||
return;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Variable access
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new virtual variable corresponding to an IL local
|
||||
/// </summary>
|
||||
internal void AddLocal(LambdaCompiler gen, ParameterExpression variable) {
|
||||
_locals.Add(variable, new LocalStorage(gen, variable));
|
||||
}
|
||||
|
||||
internal void EmitGet(ParameterExpression variable) {
|
||||
ResolveVariable(variable).EmitLoad();
|
||||
}
|
||||
|
||||
internal void EmitSet(ParameterExpression variable) {
|
||||
ResolveVariable(variable).EmitStore();
|
||||
}
|
||||
|
||||
internal void EmitAddressOf(ParameterExpression variable) {
|
||||
ResolveVariable(variable).EmitAddress();
|
||||
}
|
||||
|
||||
private Storage ResolveVariable(ParameterExpression variable) {
|
||||
return ResolveVariable(variable, NearestHoistedLocals);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolve a local variable in this scope or a closed over scope
|
||||
/// Throws if the variable is defined
|
||||
/// </summary>
|
||||
private Storage ResolveVariable(ParameterExpression variable, HoistedLocals hoistedLocals) {
|
||||
// Search IL locals and arguments, but only in this lambda
|
||||
for (CompilerScope s = this; s != null; s = s._parent) {
|
||||
Storage storage;
|
||||
if (s._locals.TryGetValue(variable, out storage)) {
|
||||
return storage;
|
||||
}
|
||||
|
||||
// if this is a lambda, we're done
|
||||
if (s.IsMethod) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// search hoisted locals
|
||||
for (HoistedLocals h = hoistedLocals; h != null; h = h.Parent) {
|
||||
int index;
|
||||
if (h.Indexes.TryGetValue(variable, out index)) {
|
||||
return new ElementBoxStorage(
|
||||
ResolveVariable(h.SelfVariable, hoistedLocals),
|
||||
index,
|
||||
variable
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// If this is an unbound variable in the lambda, the error will be
|
||||
// thrown from VariableBinder. So an error here is generally caused
|
||||
// by an internal error, e.g. a scope was created but it bypassed
|
||||
// VariableBinder.
|
||||
//
|
||||
throw Error.UndefinedVariable(variable.Name, variable.Type, CurrentLambdaName);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void SetParent(LambdaCompiler lc, CompilerScope parent) {
|
||||
Debug.Assert(_parent == null && parent != this);
|
||||
_parent = parent;
|
||||
|
||||
if (NeedsClosure && _parent != null) {
|
||||
_closureHoistedLocals = _parent.NearestHoistedLocals;
|
||||
}
|
||||
|
||||
var hoistedVars = GetVariables().Where(p => Definitions[p] == VariableStorageKind.Hoisted).ToReadOnly();
|
||||
|
||||
if (hoistedVars.Count > 0) {
|
||||
_hoistedLocals = new HoistedLocals(_closureHoistedLocals, hoistedVars);
|
||||
AddLocal(lc, _hoistedLocals.SelfVariable);
|
||||
}
|
||||
}
|
||||
|
||||
// Emits creation of the hoisted local storage
|
||||
private void EmitNewHoistedLocals(LambdaCompiler lc) {
|
||||
if (_hoistedLocals == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// create the array
|
||||
lc.IL.EmitInt(_hoistedLocals.Variables.Count);
|
||||
lc.IL.Emit(OpCodes.Newarr, typeof(object));
|
||||
|
||||
// initialize all elements
|
||||
int i = 0;
|
||||
foreach (ParameterExpression v in _hoistedLocals.Variables) {
|
||||
// array[i] = new StrongBox<T>(...);
|
||||
lc.IL.Emit(OpCodes.Dup);
|
||||
lc.IL.EmitInt(i++);
|
||||
Type boxType = typeof(StrongBox<>).MakeGenericType(v.Type);
|
||||
|
||||
if (IsMethod && lc.Parameters.Contains(v)) {
|
||||
// array[i] = new StrongBox<T>(argument);
|
||||
int index = lc.Parameters.IndexOf(v);
|
||||
lc.EmitLambdaArgument(index);
|
||||
lc.IL.Emit(OpCodes.Newobj, boxType.GetConstructor(new Type[] { v.Type }));
|
||||
} else if (v == _hoistedLocals.ParentVariable) {
|
||||
// array[i] = new StrongBox<T>(closure.Locals);
|
||||
ResolveVariable(v, _closureHoistedLocals).EmitLoad();
|
||||
lc.IL.Emit(OpCodes.Newobj, boxType.GetConstructor(new Type[] { v.Type }));
|
||||
} else {
|
||||
#if CLR2
|
||||
// array[i] = new StrongBox<T>(default(T));
|
||||
lc.IL.EmitDefault(v.Type);
|
||||
lc.IL.Emit(OpCodes.Newobj, boxType.GetConstructor(new Type[] { v.Type }));
|
||||
#else
|
||||
// array[i] = new StrongBox<T>();
|
||||
lc.IL.Emit(OpCodes.Newobj, boxType.GetConstructor(Type.EmptyTypes));
|
||||
#endif
|
||||
}
|
||||
// if we want to cache this into a local, do it now
|
||||
if (ShouldCache(v)) {
|
||||
lc.IL.Emit(OpCodes.Dup);
|
||||
CacheBoxToLocal(lc, v);
|
||||
}
|
||||
lc.IL.Emit(OpCodes.Stelem_Ref);
|
||||
}
|
||||
|
||||
// store it
|
||||
EmitSet(_hoistedLocals.SelfVariable);
|
||||
}
|
||||
|
||||
// If hoisted variables are referenced "enough", we cache the
|
||||
// StrongBox<T> in an IL local, which saves an array index and a cast
|
||||
// when we go to look it up later
|
||||
private void EmitCachedVariables() {
|
||||
if (ReferenceCount == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var refCount in ReferenceCount) {
|
||||
if (ShouldCache(refCount.Key, refCount.Value)) {
|
||||
var storage = ResolveVariable(refCount.Key) as ElementBoxStorage;
|
||||
if (storage != null) {
|
||||
storage.EmitLoadBox();
|
||||
CacheBoxToLocal(storage.Compiler, refCount.Key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool ShouldCache(ParameterExpression v, int refCount) {
|
||||
// This caching is too aggressive in the face of conditionals and
|
||||
// switch. Also, it is too conservative for variables used inside
|
||||
// of loops.
|
||||
return refCount > 2 && !_locals.ContainsKey(v);
|
||||
}
|
||||
|
||||
private bool ShouldCache(ParameterExpression v) {
|
||||
if (ReferenceCount == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int refCount;
|
||||
return ReferenceCount.TryGetValue(v, out refCount) && ShouldCache(v, refCount);
|
||||
}
|
||||
|
||||
private void CacheBoxToLocal(LambdaCompiler lc, ParameterExpression v) {
|
||||
Debug.Assert(ShouldCache(v) && !_locals.ContainsKey(v));
|
||||
var local = new LocalBoxStorage(lc, v);
|
||||
local.EmitStoreBox();
|
||||
_locals.Add(v, local);
|
||||
}
|
||||
|
||||
// Creates IL locals for accessing closures
|
||||
private void EmitClosureAccess(LambdaCompiler lc, HoistedLocals locals) {
|
||||
if (locals == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
EmitClosureToVariable(lc, locals);
|
||||
|
||||
while ((locals = locals.Parent) != null) {
|
||||
var v = locals.SelfVariable;
|
||||
var local = new LocalStorage(lc, v);
|
||||
local.EmitStore(ResolveVariable(v));
|
||||
_locals.Add(v, local);
|
||||
}
|
||||
}
|
||||
|
||||
private void EmitClosureToVariable(LambdaCompiler lc, HoistedLocals locals) {
|
||||
lc.EmitClosureArgument();
|
||||
lc.IL.Emit(OpCodes.Ldfld, typeof(Closure).GetField("Locals"));
|
||||
AddLocal(lc, locals.SelfVariable);
|
||||
EmitSet(locals.SelfVariable);
|
||||
}
|
||||
|
||||
// Allocates slots for IL locals or IL arguments
|
||||
private void AllocateLocals(LambdaCompiler lc) {
|
||||
foreach (ParameterExpression v in GetVariables()) {
|
||||
if (Definitions[v] == VariableStorageKind.Local) {
|
||||
//
|
||||
// If v is in lc.Parameters, it is a parameter.
|
||||
// Otherwise, it is a local variable.
|
||||
//
|
||||
// Also, for inlined lambdas we'll create a local, which
|
||||
// is possibly a byref local if the parameter is byref.
|
||||
//
|
||||
Storage s;
|
||||
if (IsMethod && lc.Parameters.Contains(v)) {
|
||||
s = new ArgumentStorage(lc, v);
|
||||
} else {
|
||||
s = new LocalStorage(lc, v);
|
||||
}
|
||||
_locals.Add(v, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IList<ParameterExpression> GetVariables() {
|
||||
var vars = GetVariables(Node);
|
||||
if (MergedScopes == null) {
|
||||
return vars;
|
||||
}
|
||||
var list = new List<ParameterExpression>(vars);
|
||||
foreach (var scope in MergedScopes) {
|
||||
list.AddRange(GetVariables(scope));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private static IList<ParameterExpression> GetVariables(object scope) {
|
||||
var lambda = scope as LambdaExpression;
|
||||
if (lambda != null) {
|
||||
return lambda.Parameters;
|
||||
}
|
||||
var block = scope as BlockExpression;
|
||||
if (block != null) {
|
||||
return block.Variables;
|
||||
}
|
||||
return new[] { ((CatchBlock)scope).Variable };
|
||||
}
|
||||
|
||||
private string CurrentLambdaName {
|
||||
get {
|
||||
CompilerScope s = this;
|
||||
while (true) {
|
||||
var lambda = s.Node as LambdaExpression;
|
||||
if (lambda != null) {
|
||||
return lambda.Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
106
external/referencesource/System.Core/Microsoft/Scripting/Compiler/ConstantCheck.cs
vendored
Normal file
106
external/referencesource/System.Core/Microsoft/Scripting/Compiler/ConstantCheck.cs
vendored
Normal file
@ -0,0 +1,106 @@
|
||||
/* ****************************************************************************
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* This source code is subject to terms and conditions of the Apache License, Version 2.0. A
|
||||
* copy of the license can be found in the License.html file at the root of this distribution. If
|
||||
* you cannot locate the Apache License, Version 2.0, please send an email to
|
||||
* dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
|
||||
* by the terms of the Apache License, Version 2.0.
|
||||
*
|
||||
* You must not remove this notice, or any other, from this software.
|
||||
*
|
||||
*
|
||||
* ***************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Dynamic.Utils;
|
||||
|
||||
#if CLR2
|
||||
namespace Microsoft.Scripting.Ast {
|
||||
#else
|
||||
namespace System.Linq.Expressions {
|
||||
#endif
|
||||
|
||||
internal enum AnalyzeTypeIsResult {
|
||||
KnownFalse,
|
||||
KnownTrue,
|
||||
KnownAssignable, // need null check only
|
||||
Unknown, // need full runtime check
|
||||
}
|
||||
|
||||
internal static class ConstantCheck {
|
||||
|
||||
internal static bool IsNull(Expression e) {
|
||||
if (e.NodeType == ExpressionType.Constant) {
|
||||
return ((ConstantExpression)e).Value == null;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// If the result of a TypeBinaryExpression is known statically, this
|
||||
/// returns the result, otherwise it returns null, meaning we'll need
|
||||
/// to perform the IsInst instruction at runtime.
|
||||
///
|
||||
/// The result of this function must be equivalent to IsInst, or
|
||||
/// null.
|
||||
/// </summary>
|
||||
internal static AnalyzeTypeIsResult AnalyzeTypeIs(TypeBinaryExpression typeIs) {
|
||||
return AnalyzeTypeIs(typeIs.Expression, typeIs.TypeOperand);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If the result of an isinst opcode is known statically, this
|
||||
/// returns the result, otherwise it returns null, meaning we'll need
|
||||
/// to perform the IsInst instruction at runtime.
|
||||
///
|
||||
/// The result of this function must be equivalent to IsInst, or
|
||||
/// null.
|
||||
/// </summary>
|
||||
private static AnalyzeTypeIsResult AnalyzeTypeIs(Expression operand, Type testType) {
|
||||
Type operandType = operand.Type;
|
||||
|
||||
// Oddly, we allow void operands
|
||||
// This is LinqV1 behavior of TypeIs
|
||||
if (operandType == typeof(void)) {
|
||||
return AnalyzeTypeIsResult.KnownFalse;
|
||||
}
|
||||
|
||||
//
|
||||
// Type comparisons treat nullable types as if they were the
|
||||
// underlying type. The reason is when you box a nullable it
|
||||
// becomes a boxed value of the underlying type, or null.
|
||||
//
|
||||
Type nnOperandType = operandType.GetNonNullableType();
|
||||
Type nnTestType = testType.GetNonNullableType();
|
||||
|
||||
//
|
||||
// See if we can determine the answer based on the static types
|
||||
//
|
||||
// Extensive testing showed that Type.IsAssignableFrom,
|
||||
// Type.IsInstanceOfType, and the isinst instruction were all
|
||||
// equivalent when used against a live object
|
||||
//
|
||||
if (nnTestType.IsAssignableFrom(nnOperandType)) {
|
||||
// If the operand is a value type (other than nullable), we
|
||||
// know the result is always true.
|
||||
if (operandType.IsValueType && !operandType.IsNullableType()) {
|
||||
return AnalyzeTypeIsResult.KnownTrue;
|
||||
}
|
||||
|
||||
// For reference/nullable types, we need to compare to null at runtime
|
||||
return AnalyzeTypeIsResult.KnownAssignable;
|
||||
}
|
||||
|
||||
// We used to have an if IsSealed, return KnownFalse check here.
|
||||
// but that doesn't handle generic types & co/contravariance correctly.
|
||||
// So just use IsInst, which we know always gives us the right answer.
|
||||
|
||||
// Otherwise we need a full runtime check
|
||||
return AnalyzeTypeIsResult.Unknown;
|
||||
}
|
||||
}
|
||||
}
|
65
external/referencesource/System.Core/Microsoft/Scripting/Compiler/DebugInfoGenerator.cs
vendored
Normal file
65
external/referencesource/System.Core/Microsoft/Scripting/Compiler/DebugInfoGenerator.cs
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
/* ****************************************************************************
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* This source code is subject to terms and conditions of the Apache License, Version 2.0. A
|
||||
* copy of the license can be found in the License.html file at the root of this distribution. If
|
||||
* you cannot locate the Apache License, Version 2.0, please send an email to
|
||||
* dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
|
||||
* by the terms of the Apache License, Version 2.0.
|
||||
*
|
||||
* You must not remove this notice, or any other, from this software.
|
||||
*
|
||||
*
|
||||
* ***************************************************************************/
|
||||
|
||||
#if CLR2
|
||||
using Microsoft.Scripting.Ast;
|
||||
using Microsoft.Scripting.Ast.Compiler;
|
||||
#else
|
||||
using System.Linq.Expressions;
|
||||
using System.Linq.Expressions.Compiler;
|
||||
#endif
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Reflection.Emit;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.SymbolStore;
|
||||
using System.Reflection;
|
||||
|
||||
namespace System.Runtime.CompilerServices {
|
||||
#if CLR2 || SILVERLIGHT
|
||||
using ILGenerator = OffsetTrackingILGenerator;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Generates debug information for lambdas in an expression tree.
|
||||
/// </summary>
|
||||
public abstract class DebugInfoGenerator {
|
||||
/// <summary>
|
||||
/// Creates PDB symbol generator.
|
||||
/// </summary>
|
||||
/// <returns>PDB symbol generator.</returns>
|
||||
public static DebugInfoGenerator CreatePdbGenerator() {
|
||||
return new SymbolDocumentGenerator();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks a sequence point.
|
||||
/// </summary>
|
||||
/// <param name="method">The lambda being generated.</param>
|
||||
/// <param name="ilOffset">IL offset where to mark the sequence point.</param>
|
||||
/// <param name="sequencePoint">Debug informaton corresponding to the sequence point.</param>
|
||||
public abstract void MarkSequencePoint(LambdaExpression method, int ilOffset, DebugInfoExpression sequencePoint);
|
||||
|
||||
internal virtual void MarkSequencePoint(LambdaExpression method, MethodBase methodBase, ILGenerator ilg, DebugInfoExpression sequencePoint) {
|
||||
MarkSequencePoint(method, ilg.ILOffset, sequencePoint);
|
||||
}
|
||||
|
||||
internal virtual void SetLocalName(LocalBuilder localBuilder, string name) {
|
||||
// nop
|
||||
}
|
||||
}
|
||||
}
|
293
external/referencesource/System.Core/Microsoft/Scripting/Compiler/DelegateHelpers.Generated.cs
vendored
Normal file
293
external/referencesource/System.Core/Microsoft/Scripting/Compiler/DelegateHelpers.Generated.cs
vendored
Normal file
@ -0,0 +1,293 @@
|
||||
/* ****************************************************************************
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* This source code is subject to terms and conditions of the Apache License, Version 2.0. A
|
||||
* copy of the license can be found in the License.html file at the root of this distribution. If
|
||||
* you cannot locate the Apache License, Version 2.0, please send an email to
|
||||
* dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
|
||||
* by the terms of the Apache License, Version 2.0.
|
||||
*
|
||||
* You must not remove this notice, or any other, from this software.
|
||||
*
|
||||
*
|
||||
* ***************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Dynamic;
|
||||
using System.Dynamic.Utils;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
#if CLR2
|
||||
namespace Microsoft.Scripting.Ast.Compiler {
|
||||
using Microsoft.Scripting.Utils;
|
||||
#else
|
||||
namespace System.Linq.Expressions.Compiler {
|
||||
#endif
|
||||
internal static partial class DelegateHelpers {
|
||||
private static TypeInfo _DelegateCache = new TypeInfo();
|
||||
|
||||
#region Generated Maximum Delegate Arity
|
||||
|
||||
// *** BEGIN GENERATED CODE ***
|
||||
// generated by function: gen_max_delegate_arity from: generate_dynsites.py
|
||||
|
||||
private const int MaximumArity = 17;
|
||||
|
||||
// *** END GENERATED CODE ***
|
||||
|
||||
#endregion
|
||||
|
||||
internal class TypeInfo {
|
||||
public Type DelegateType;
|
||||
public Dictionary<Type, TypeInfo> TypeChain;
|
||||
|
||||
public Type MakeDelegateType(Type retType, params Expression[] args) {
|
||||
return MakeDelegateType(retType, (IList<Expression>)args);
|
||||
}
|
||||
|
||||
public Type MakeDelegateType(Type retType, IList<Expression> args) {
|
||||
// nope, go ahead and create it and spend the
|
||||
// cost of creating the array.
|
||||
Type[] paramTypes = new Type[args.Count + 2];
|
||||
paramTypes[0] = typeof(CallSite);
|
||||
paramTypes[paramTypes.Length - 1] = retType;
|
||||
for (int i = 0; i < args.Count; i++) {
|
||||
paramTypes[i + 1] = args[i].Type;
|
||||
}
|
||||
|
||||
return DelegateType = MakeNewDelegate(paramTypes);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Finds a delegate type using the types in the array.
|
||||
/// We use the cache to avoid copying the array, and to cache the
|
||||
/// created delegate type
|
||||
/// </summary>
|
||||
internal static Type MakeDelegateType(Type[] types) {
|
||||
lock (_DelegateCache) {
|
||||
TypeInfo curTypeInfo = _DelegateCache;
|
||||
|
||||
// arguments & return type
|
||||
for (int i = 0; i < types.Length; i++) {
|
||||
curTypeInfo = NextTypeInfo(types[i], curTypeInfo);
|
||||
}
|
||||
|
||||
// see if we have the delegate already
|
||||
if (curTypeInfo.DelegateType == null) {
|
||||
// clone because MakeCustomDelegate can hold onto the array.
|
||||
curTypeInfo.DelegateType = MakeNewDelegate((Type[])types.Clone());
|
||||
}
|
||||
|
||||
return curTypeInfo.DelegateType;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds a delegate type for a CallSite using the types in the ReadOnlyCollection of Expression.
|
||||
///
|
||||
/// We take the readonly collection of Expression explicitly to avoid allocating memory (an array
|
||||
/// of types) on lookup of delegate types.
|
||||
/// </summary>
|
||||
internal static Type MakeCallSiteDelegate(ReadOnlyCollection<Expression> types, Type returnType) {
|
||||
lock (_DelegateCache) {
|
||||
TypeInfo curTypeInfo = _DelegateCache;
|
||||
|
||||
// CallSite
|
||||
curTypeInfo = NextTypeInfo(typeof(CallSite), curTypeInfo);
|
||||
|
||||
// arguments
|
||||
for (int i = 0; i < types.Count; i++) {
|
||||
curTypeInfo = NextTypeInfo(types[i].Type, curTypeInfo);
|
||||
}
|
||||
|
||||
// return type
|
||||
curTypeInfo = NextTypeInfo(returnType, curTypeInfo);
|
||||
|
||||
// see if we have the delegate already
|
||||
if (curTypeInfo.DelegateType == null) {
|
||||
curTypeInfo.MakeDelegateType(returnType, types);
|
||||
}
|
||||
|
||||
return curTypeInfo.DelegateType;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds a delegate type for a CallSite using the MetaObject array.
|
||||
///
|
||||
/// We take the array of MetaObject explicitly to avoid allocating memory (an array of types) on
|
||||
/// lookup of delegate types.
|
||||
/// </summary>
|
||||
internal static Type MakeDeferredSiteDelegate(DynamicMetaObject[] args, Type returnType) {
|
||||
lock (_DelegateCache) {
|
||||
TypeInfo curTypeInfo = _DelegateCache;
|
||||
|
||||
// CallSite
|
||||
curTypeInfo = NextTypeInfo(typeof(CallSite), curTypeInfo);
|
||||
|
||||
// arguments
|
||||
for (int i = 0; i < args.Length; i++) {
|
||||
DynamicMetaObject mo = args[i];
|
||||
Type paramType = mo.Expression.Type;
|
||||
if (IsByRef(mo)) {
|
||||
paramType = paramType.MakeByRefType();
|
||||
}
|
||||
curTypeInfo = NextTypeInfo(paramType, curTypeInfo);
|
||||
}
|
||||
|
||||
// return type
|
||||
curTypeInfo = NextTypeInfo(returnType, curTypeInfo);
|
||||
|
||||
// see if we have the delegate already
|
||||
if (curTypeInfo.DelegateType == null) {
|
||||
// nope, go ahead and create it and spend the
|
||||
// cost of creating the array.
|
||||
Type[] paramTypes = new Type[args.Length + 2];
|
||||
paramTypes[0] = typeof(CallSite);
|
||||
paramTypes[paramTypes.Length - 1] = returnType;
|
||||
for (int i = 0; i < args.Length; i++) {
|
||||
DynamicMetaObject mo = args[i];
|
||||
Type paramType = mo.Expression.Type;
|
||||
if (IsByRef(mo)) {
|
||||
paramType = paramType.MakeByRefType();
|
||||
}
|
||||
paramTypes[i + 1] = paramType;
|
||||
}
|
||||
|
||||
curTypeInfo.DelegateType = MakeNewDelegate(paramTypes);
|
||||
}
|
||||
|
||||
return curTypeInfo.DelegateType;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsByRef(DynamicMetaObject mo) {
|
||||
ParameterExpression pe = mo.Expression as ParameterExpression;
|
||||
return pe != null && pe.IsByRef;
|
||||
}
|
||||
|
||||
internal static TypeInfo NextTypeInfo(Type initialArg) {
|
||||
lock (_DelegateCache) {
|
||||
return NextTypeInfo(initialArg, _DelegateCache);
|
||||
}
|
||||
}
|
||||
|
||||
internal static TypeInfo GetNextTypeInfo(Type initialArg, TypeInfo curTypeInfo) {
|
||||
lock (_DelegateCache) {
|
||||
return NextTypeInfo(initialArg, curTypeInfo);
|
||||
}
|
||||
}
|
||||
|
||||
private static TypeInfo NextTypeInfo(Type initialArg, TypeInfo curTypeInfo) {
|
||||
Type lookingUp = initialArg;
|
||||
TypeInfo nextTypeInfo;
|
||||
if (curTypeInfo.TypeChain == null) {
|
||||
curTypeInfo.TypeChain = new Dictionary<Type, TypeInfo>();
|
||||
}
|
||||
|
||||
if (!curTypeInfo.TypeChain.TryGetValue(lookingUp, out nextTypeInfo)) {
|
||||
nextTypeInfo = new TypeInfo();
|
||||
if (TypeUtils.CanCache(lookingUp)) {
|
||||
curTypeInfo.TypeChain[lookingUp] = nextTypeInfo;
|
||||
}
|
||||
}
|
||||
return nextTypeInfo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new delegate, or uses a func/action
|
||||
/// Note: this method does not cache
|
||||
/// </summary>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
|
||||
private static Type MakeNewDelegate(Type[] types) {
|
||||
Debug.Assert(types != null && types.Length > 0);
|
||||
|
||||
// Can only used predefined delegates if we have no byref types and
|
||||
// the arity is small enough to fit in Func<...> or Action<...>
|
||||
if (types.Length > MaximumArity || types.Any(t => t.IsByRef)) {
|
||||
return MakeNewCustomDelegate(types);
|
||||
}
|
||||
|
||||
Type result;
|
||||
if (types[types.Length - 1] == typeof(void)) {
|
||||
result = GetActionType(types.RemoveLast());
|
||||
} else {
|
||||
result = GetFuncType(types);
|
||||
}
|
||||
Debug.Assert(result != null);
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static Type GetFuncType(Type[] types) {
|
||||
switch (types.Length) {
|
||||
#region Generated Delegate Func Types
|
||||
|
||||
// *** BEGIN GENERATED CODE ***
|
||||
// generated by function: gen_delegate_func from: generate_dynsites.py
|
||||
|
||||
case 1: return typeof(Func<>).MakeGenericType(types);
|
||||
case 2: return typeof(Func<,>).MakeGenericType(types);
|
||||
case 3: return typeof(Func<,,>).MakeGenericType(types);
|
||||
case 4: return typeof(Func<,,,>).MakeGenericType(types);
|
||||
case 5: return typeof(Func<,,,,>).MakeGenericType(types);
|
||||
case 6: return typeof(Func<,,,,,>).MakeGenericType(types);
|
||||
case 7: return typeof(Func<,,,,,,>).MakeGenericType(types);
|
||||
case 8: return typeof(Func<,,,,,,,>).MakeGenericType(types);
|
||||
case 9: return typeof(Func<,,,,,,,,>).MakeGenericType(types);
|
||||
case 10: return typeof(Func<,,,,,,,,,>).MakeGenericType(types);
|
||||
case 11: return typeof(Func<,,,,,,,,,,>).MakeGenericType(types);
|
||||
case 12: return typeof(Func<,,,,,,,,,,,>).MakeGenericType(types);
|
||||
case 13: return typeof(Func<,,,,,,,,,,,,>).MakeGenericType(types);
|
||||
case 14: return typeof(Func<,,,,,,,,,,,,,>).MakeGenericType(types);
|
||||
case 15: return typeof(Func<,,,,,,,,,,,,,,>).MakeGenericType(types);
|
||||
case 16: return typeof(Func<,,,,,,,,,,,,,,,>).MakeGenericType(types);
|
||||
case 17: return typeof(Func<,,,,,,,,,,,,,,,,>).MakeGenericType(types);
|
||||
|
||||
// *** END GENERATED CODE ***
|
||||
|
||||
#endregion
|
||||
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
internal static Type GetActionType(Type[] types) {
|
||||
switch (types.Length) {
|
||||
case 0: return typeof(Action);
|
||||
#region Generated Delegate Action Types
|
||||
|
||||
// *** BEGIN GENERATED CODE ***
|
||||
// generated by function: gen_delegate_action from: generate_dynsites.py
|
||||
|
||||
case 1: return typeof(Action<>).MakeGenericType(types);
|
||||
case 2: return typeof(Action<,>).MakeGenericType(types);
|
||||
case 3: return typeof(Action<,,>).MakeGenericType(types);
|
||||
case 4: return typeof(Action<,,,>).MakeGenericType(types);
|
||||
case 5: return typeof(Action<,,,,>).MakeGenericType(types);
|
||||
case 6: return typeof(Action<,,,,,>).MakeGenericType(types);
|
||||
case 7: return typeof(Action<,,,,,,>).MakeGenericType(types);
|
||||
case 8: return typeof(Action<,,,,,,,>).MakeGenericType(types);
|
||||
case 9: return typeof(Action<,,,,,,,,>).MakeGenericType(types);
|
||||
case 10: return typeof(Action<,,,,,,,,,>).MakeGenericType(types);
|
||||
case 11: return typeof(Action<,,,,,,,,,,>).MakeGenericType(types);
|
||||
case 12: return typeof(Action<,,,,,,,,,,,>).MakeGenericType(types);
|
||||
case 13: return typeof(Action<,,,,,,,,,,,,>).MakeGenericType(types);
|
||||
case 14: return typeof(Action<,,,,,,,,,,,,,>).MakeGenericType(types);
|
||||
case 15: return typeof(Action<,,,,,,,,,,,,,,>).MakeGenericType(types);
|
||||
case 16: return typeof(Action<,,,,,,,,,,,,,,,>).MakeGenericType(types);
|
||||
|
||||
// *** END GENERATED CODE ***
|
||||
|
||||
#endregion
|
||||
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
42
external/referencesource/System.Core/Microsoft/Scripting/Compiler/DelegateHelpers.cs
vendored
Normal file
42
external/referencesource/System.Core/Microsoft/Scripting/Compiler/DelegateHelpers.cs
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
/* ****************************************************************************
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* This source code is subject to terms and conditions of the Apache License, Version 2.0. A
|
||||
* copy of the license can be found in the License.html file at the root of this distribution. If
|
||||
* you cannot locate the Apache License, Version 2.0, please send an email to
|
||||
* dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
|
||||
* by the terms of the Apache License, Version 2.0.
|
||||
*
|
||||
* You must not remove this notice, or any other, from this software.
|
||||
*
|
||||
*
|
||||
* ***************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Dynamic.Utils;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
#if CLR2
|
||||
namespace Microsoft.Scripting.Ast.Compiler {
|
||||
#else
|
||||
namespace System.Linq.Expressions.Compiler {
|
||||
#endif
|
||||
internal static partial class DelegateHelpers {
|
||||
private const MethodAttributes CtorAttributes = MethodAttributes.RTSpecialName | MethodAttributes.HideBySig | MethodAttributes.Public;
|
||||
private const MethodImplAttributes ImplAttributes = MethodImplAttributes.Runtime | MethodImplAttributes.Managed;
|
||||
private const MethodAttributes InvokeAttributes = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual;
|
||||
private static readonly Type[] _DelegateCtorSignature = new Type[] { typeof(object), typeof(IntPtr) };
|
||||
|
||||
private static Type MakeNewCustomDelegate(Type[] types) {
|
||||
Type returnType = types[types.Length - 1];
|
||||
Type[] parameters = types.RemoveLast();
|
||||
|
||||
TypeBuilder builder = AssemblyGen.DefineDelegateType("Delegate" + types.Length);
|
||||
builder.DefineConstructor(CtorAttributes, CallingConventions.Standard, _DelegateCtorSignature).SetImplementationFlags(ImplAttributes);
|
||||
builder.DefineMethod("Invoke", InvokeAttributes, returnType, parameters).SetImplementationFlags(ImplAttributes);
|
||||
return builder.CreateType();
|
||||
}
|
||||
}
|
||||
}
|
247
external/referencesource/System.Core/Microsoft/Scripting/Compiler/ExpressionQuoter.cs
vendored
Normal file
247
external/referencesource/System.Core/Microsoft/Scripting/Compiler/ExpressionQuoter.cs
vendored
Normal file
@ -0,0 +1,247 @@
|
||||
/* ****************************************************************************
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* This source code is subject to terms and conditions of the Apache License, Version 2.0. A
|
||||
* copy of the license can be found in the License.html file at the root of this distribution. If
|
||||
* you cannot locate the Apache License, Version 2.0, please send an email to
|
||||
* dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
|
||||
* by the terms of the Apache License, Version 2.0.
|
||||
*
|
||||
* You must not remove this notice, or any other, from this software.
|
||||
*
|
||||
*
|
||||
* ***************************************************************************/
|
||||
|
||||
#if CLR2
|
||||
using Microsoft.Scripting.Ast;
|
||||
using Microsoft.Scripting.Ast.Compiler;
|
||||
#else
|
||||
using System.Linq.Expressions;
|
||||
using System.Linq.Expressions.Compiler;
|
||||
#endif
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Dynamic.Utils;
|
||||
|
||||
namespace System.Runtime.CompilerServices {
|
||||
public partial class RuntimeOps {
|
||||
/// <summary>
|
||||
/// Quotes the provided expression tree.
|
||||
/// </summary>
|
||||
/// <param name="expression">The expression to quote.</param>
|
||||
/// <param name="hoistedLocals">The hoisted local state provided by the compiler.</param>
|
||||
/// <param name="locals">The actual hoisted local values.</param>
|
||||
/// <returns>The quoted expression.</returns>
|
||||
[Obsolete("do not use this method", true), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public static Expression Quote(Expression expression, object hoistedLocals, object[] locals) {
|
||||
Debug.Assert(hoistedLocals != null && locals != null);
|
||||
var quoter = new ExpressionQuoter((HoistedLocals)hoistedLocals, locals);
|
||||
return quoter.Visit(expression);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Combines two runtime variable lists and returns a new list.
|
||||
/// </summary>
|
||||
/// <param name="first">The first list.</param>
|
||||
/// <param name="second">The second list.</param>
|
||||
/// <param name="indexes">The index array indicating which list to get variables from.</param>
|
||||
/// <returns>The merged runtime variables.</returns>
|
||||
[Obsolete("do not use this method", true), EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public static IRuntimeVariables MergeRuntimeVariables(IRuntimeVariables first, IRuntimeVariables second, int[] indexes) {
|
||||
return new MergedRuntimeVariables(first, second, indexes);
|
||||
}
|
||||
|
||||
// Modifies a quoted Expression instance by changing hoisted variables and
|
||||
// parameters into hoisted local references. The variable's StrongBox is
|
||||
// burned as a constant, and all hoisted variables/parameters are rewritten
|
||||
// as indexing expressions.
|
||||
//
|
||||
// The behavior of Quote is indended to be like C# and VB expression quoting
|
||||
private sealed class ExpressionQuoter : ExpressionVisitor {
|
||||
private readonly HoistedLocals _scope;
|
||||
private readonly object[] _locals;
|
||||
|
||||
// A stack of variables that are defined in nested scopes. We search
|
||||
// this first when resolving a variable in case a nested scope shadows
|
||||
// one of our variable instances.
|
||||
private readonly Stack<Set<ParameterExpression>> _shadowedVars = new Stack<Set<ParameterExpression>>();
|
||||
|
||||
internal ExpressionQuoter(HoistedLocals scope, object[] locals) {
|
||||
_scope = scope;
|
||||
_locals = locals;
|
||||
}
|
||||
|
||||
protected internal override Expression VisitLambda<T>(Expression<T> node) {
|
||||
_shadowedVars.Push(new Set<ParameterExpression>(node.Parameters));
|
||||
Expression b = Visit(node.Body);
|
||||
_shadowedVars.Pop();
|
||||
if (b == node.Body) {
|
||||
return node;
|
||||
}
|
||||
return Expression.Lambda<T>(b, node.Name, node.TailCall, node.Parameters);
|
||||
}
|
||||
|
||||
protected internal override Expression VisitBlock(BlockExpression node) {
|
||||
if (node.Variables.Count > 0) {
|
||||
_shadowedVars.Push(new Set<ParameterExpression>(node.Variables));
|
||||
}
|
||||
var b = Visit(node.Expressions);
|
||||
if (node.Variables.Count > 0) {
|
||||
_shadowedVars.Pop();
|
||||
}
|
||||
if (b == node.Expressions) {
|
||||
return node;
|
||||
}
|
||||
return Expression.Block(node.Variables, b);
|
||||
}
|
||||
|
||||
protected override CatchBlock VisitCatchBlock(CatchBlock node) {
|
||||
if (node.Variable != null) {
|
||||
_shadowedVars.Push(new Set<ParameterExpression>(new[] { node.Variable }));
|
||||
}
|
||||
Expression b = Visit(node.Body);
|
||||
Expression f = Visit(node.Filter);
|
||||
if (node.Variable != null) {
|
||||
_shadowedVars.Pop();
|
||||
}
|
||||
if (b == node.Body && f == node.Filter) {
|
||||
return node;
|
||||
}
|
||||
return Expression.MakeCatchBlock(node.Test, node.Variable, b, f);
|
||||
}
|
||||
|
||||
protected internal override Expression VisitRuntimeVariables(RuntimeVariablesExpression node) {
|
||||
int count = node.Variables.Count;
|
||||
var boxes = new List<IStrongBox>();
|
||||
var vars = new List<ParameterExpression>();
|
||||
var indexes = new int[count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
IStrongBox box = GetBox(node.Variables[i]);
|
||||
if (box == null) {
|
||||
indexes[i] = vars.Count;
|
||||
vars.Add(node.Variables[i]);
|
||||
} else {
|
||||
indexes[i] = -1 - boxes.Count;
|
||||
boxes.Add(box);
|
||||
}
|
||||
}
|
||||
|
||||
// No variables were rewritten. Just return the original node
|
||||
if (boxes.Count == 0) {
|
||||
return node;
|
||||
}
|
||||
|
||||
var boxesConst = Expression.Constant(new RuntimeVariables(boxes.ToArray()), typeof(IRuntimeVariables));
|
||||
// All of them were rewritten. Just return the array as a constant
|
||||
if (vars.Count == 0) {
|
||||
return boxesConst;
|
||||
}
|
||||
|
||||
// Otherwise, we need to return an object that merges them
|
||||
return Expression.Call(
|
||||
typeof(RuntimeOps).GetMethod("MergeRuntimeVariables"),
|
||||
Expression.RuntimeVariables(new TrueReadOnlyCollection<ParameterExpression>(vars.ToArray())),
|
||||
boxesConst,
|
||||
Expression.Constant(indexes)
|
||||
);
|
||||
}
|
||||
|
||||
protected internal override Expression VisitParameter(ParameterExpression node) {
|
||||
IStrongBox box = GetBox(node);
|
||||
if (box == null) {
|
||||
return node;
|
||||
}
|
||||
return Expression.Field(Expression.Constant(box), "Value");
|
||||
}
|
||||
|
||||
private IStrongBox GetBox(ParameterExpression variable) {
|
||||
// Skip variables that are shadowed by a nested scope/lambda
|
||||
foreach (Set<ParameterExpression> hidden in _shadowedVars) {
|
||||
if (hidden.Contains(variable)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
HoistedLocals scope = _scope;
|
||||
object[] locals = _locals;
|
||||
while (true) {
|
||||
int hoistIndex;
|
||||
if (scope.Indexes.TryGetValue(variable, out hoistIndex)) {
|
||||
return (IStrongBox)locals[hoistIndex];
|
||||
}
|
||||
scope = scope.Parent;
|
||||
if (scope == null) {
|
||||
break;
|
||||
}
|
||||
locals = HoistedLocals.GetParent(locals);
|
||||
}
|
||||
|
||||
// Unbound variable: an error should've been thrown already
|
||||
// from VariableBinder
|
||||
throw ContractUtils.Unreachable;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class RuntimeVariables : IRuntimeVariables {
|
||||
private readonly IStrongBox[] _boxes;
|
||||
|
||||
internal RuntimeVariables(IStrongBox[] boxes) {
|
||||
_boxes = boxes;
|
||||
}
|
||||
|
||||
int IRuntimeVariables.Count {
|
||||
get { return _boxes.Length; }
|
||||
}
|
||||
|
||||
object IRuntimeVariables.this[int index] {
|
||||
get {
|
||||
return _boxes[index].Value;
|
||||
}
|
||||
set {
|
||||
_boxes[index].Value = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides a list of variables, supporing read/write of the values
|
||||
/// Exposed via RuntimeVariablesExpression
|
||||
/// </summary>
|
||||
private sealed class MergedRuntimeVariables : IRuntimeVariables {
|
||||
private readonly IRuntimeVariables _first;
|
||||
private readonly IRuntimeVariables _second;
|
||||
|
||||
// For reach item, the index into the first or second list
|
||||
// Positive values mean the first array, negative means the second
|
||||
private readonly int[] _indexes;
|
||||
|
||||
internal MergedRuntimeVariables(IRuntimeVariables first, IRuntimeVariables second, int[] indexes) {
|
||||
_first = first;
|
||||
_second = second;
|
||||
_indexes = indexes;
|
||||
}
|
||||
|
||||
public int Count {
|
||||
get { return _indexes.Length; }
|
||||
}
|
||||
|
||||
public object this[int index] {
|
||||
get {
|
||||
index = _indexes[index];
|
||||
return (index >= 0) ? _first[index] : _second[-1 - index];
|
||||
}
|
||||
set {
|
||||
index = _indexes[index];
|
||||
if (index >= 0) {
|
||||
_first[index] = value;
|
||||
} else {
|
||||
_second[-1 - index] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
101
external/referencesource/System.Core/Microsoft/Scripting/Compiler/HoistedLocals.cs
vendored
Normal file
101
external/referencesource/System.Core/Microsoft/Scripting/Compiler/HoistedLocals.cs
vendored
Normal file
@ -0,0 +1,101 @@
|
||||
/* ****************************************************************************
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* This source code is subject to terms and conditions of the Apache License, Version 2.0. A
|
||||
* copy of the license can be found in the License.html file at the root of this distribution. If
|
||||
* you cannot locate the Apache License, Version 2.0, please send an email to
|
||||
* dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
|
||||
* by the terms of the Apache License, Version 2.0.
|
||||
*
|
||||
* You must not remove this notice, or any other, from this software.
|
||||
*
|
||||
*
|
||||
* ***************************************************************************/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Dynamic.Utils;
|
||||
|
||||
#if CLR2
|
||||
namespace Microsoft.Scripting.Ast.Compiler {
|
||||
#else
|
||||
namespace System.Linq.Expressions.Compiler {
|
||||
#endif
|
||||
|
||||
// Suppose we have something like:
|
||||
//
|
||||
// (string s)=>()=>s.
|
||||
//
|
||||
// We wish to generate the outer as:
|
||||
//
|
||||
// Func<string> OuterMethod(Closure closure, string s)
|
||||
// {
|
||||
// object[] locals = new object[1];
|
||||
// locals[0] = new StrongBox<string>();
|
||||
// ((StrongBox<string>)locals[0]).Value = s;
|
||||
// return ((DynamicMethod)closure.Constants[0]).CreateDelegate(typeof(Func<string>), new Closure(null, locals));
|
||||
// }
|
||||
//
|
||||
// ... and the inner as:
|
||||
//
|
||||
// string InnerMethod(Closure closure)
|
||||
// {
|
||||
// object[] locals = closure.Locals;
|
||||
// return ((StrongBox<string>)locals[0]).Value;
|
||||
// }
|
||||
//
|
||||
// This class tracks that "s" was hoisted into a closure, as the 0th
|
||||
// element in the array
|
||||
//
|
||||
/// <summary>
|
||||
/// Stores information about locals and arguments that are hoisted into
|
||||
/// the closure array because they're referenced in an inner lambda.
|
||||
///
|
||||
/// This class is sometimes emitted as a runtime constant for internal
|
||||
/// use to hoist variables/parameters in quoted expressions
|
||||
///
|
||||
/// Invariant: this class stores no mutable state
|
||||
/// </summary>
|
||||
internal sealed class HoistedLocals {
|
||||
|
||||
// The parent locals, if any
|
||||
internal readonly HoistedLocals Parent;
|
||||
|
||||
// A mapping of hoisted variables to their indexes in the array
|
||||
internal readonly ReadOnlyDictionary<Expression, int> Indexes;
|
||||
|
||||
// The variables, in the order they appear in the array
|
||||
internal readonly ReadOnlyCollection<ParameterExpression> Variables;
|
||||
|
||||
// A virtual variable for accessing this locals array
|
||||
internal readonly ParameterExpression SelfVariable;
|
||||
|
||||
internal HoistedLocals(HoistedLocals parent, ReadOnlyCollection<ParameterExpression> vars) {
|
||||
|
||||
if (parent != null) {
|
||||
// Add the parent locals array as the 0th element in the array
|
||||
vars = new TrueReadOnlyCollection<ParameterExpression>(vars.AddFirst(parent.SelfVariable));
|
||||
}
|
||||
|
||||
Dictionary<Expression, int> indexes = new Dictionary<Expression, int>(vars.Count);
|
||||
for (int i = 0; i < vars.Count; i++) {
|
||||
indexes.Add(vars[i], i);
|
||||
}
|
||||
|
||||
SelfVariable = Expression.Variable(typeof(object[]), null);
|
||||
Parent = parent;
|
||||
Variables = vars;
|
||||
Indexes = new ReadOnlyDictionary<Expression, int>(indexes);
|
||||
}
|
||||
|
||||
internal ParameterExpression ParentVariable {
|
||||
get { return Parent != null ? Parent.SelfVariable : null; }
|
||||
}
|
||||
|
||||
internal static object[] GetParent(object[] locals) {
|
||||
return ((StrongBox<object[]>)locals[0]).Value;
|
||||
}
|
||||
}
|
||||
}
|
1049
external/referencesource/System.Core/Microsoft/Scripting/Compiler/ILGen.cs
vendored
Normal file
1049
external/referencesource/System.Core/Microsoft/Scripting/Compiler/ILGen.cs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
92
external/referencesource/System.Core/Microsoft/Scripting/Compiler/KeyedQueue.cs
vendored
Normal file
92
external/referencesource/System.Core/Microsoft/Scripting/Compiler/KeyedQueue.cs
vendored
Normal file
@ -0,0 +1,92 @@
|
||||
/* ****************************************************************************
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* This source code is subject to terms and conditions of the Apache License, Version 2.0. A
|
||||
* copy of the license can be found in the License.html file at the root of this distribution. If
|
||||
* you cannot locate the Apache License, Version 2.0, please send an email to
|
||||
* dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
|
||||
* by the terms of the Apache License, Version 2.0.
|
||||
*
|
||||
* You must not remove this notice, or any other, from this software.
|
||||
*
|
||||
*
|
||||
* ***************************************************************************/
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
#if SILVERLIGHT
|
||||
using System.Core;
|
||||
#endif
|
||||
|
||||
#if CLR2
|
||||
namespace Microsoft.Scripting.Ast.Compiler {
|
||||
#else
|
||||
namespace System.Linq.Expressions.Compiler {
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// A simple dictionary of queues, keyed off a particular type
|
||||
/// This is useful for storing free lists of variables
|
||||
/// </summary>
|
||||
internal sealed class KeyedQueue<K, V> {
|
||||
private readonly Dictionary<K, Queue<V>> _data;
|
||||
|
||||
internal KeyedQueue() {
|
||||
_data = new Dictionary<K, Queue<V>>();
|
||||
}
|
||||
|
||||
internal void Enqueue(K key, V value) {
|
||||
Queue<V> queue;
|
||||
if (!_data.TryGetValue(key, out queue)) {
|
||||
_data.Add(key, queue = new Queue<V>());
|
||||
}
|
||||
queue.Enqueue(value);
|
||||
}
|
||||
|
||||
internal V Dequeue(K key) {
|
||||
Queue<V> queue;
|
||||
if (!_data.TryGetValue(key, out queue)) {
|
||||
throw Error.QueueEmpty();
|
||||
}
|
||||
V result = queue.Dequeue();
|
||||
if (queue.Count == 0) {
|
||||
_data.Remove(key);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal bool TryDequeue(K key, out V value) {
|
||||
Queue<V> queue;
|
||||
if (_data.TryGetValue(key, out queue) && queue.Count > 0) {
|
||||
value = queue.Dequeue();
|
||||
if (queue.Count == 0) {
|
||||
_data.Remove(key);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
value = default(V);
|
||||
return false;
|
||||
}
|
||||
|
||||
internal V Peek(K key) {
|
||||
Queue<V> queue;
|
||||
if (!_data.TryGetValue(key, out queue)) {
|
||||
throw Error.QueueEmpty();
|
||||
}
|
||||
return queue.Peek();
|
||||
}
|
||||
|
||||
internal int GetCount(K key) {
|
||||
Queue<V> queue;
|
||||
if (!_data.TryGetValue(key, out queue)) {
|
||||
return 0;
|
||||
}
|
||||
return queue.Count;
|
||||
}
|
||||
|
||||
internal void Clear() {
|
||||
_data.Clear();
|
||||
}
|
||||
}
|
||||
}
|
361
external/referencesource/System.Core/Microsoft/Scripting/Compiler/LabelInfo.cs
vendored
Normal file
361
external/referencesource/System.Core/Microsoft/Scripting/Compiler/LabelInfo.cs
vendored
Normal file
@ -0,0 +1,361 @@
|
||||
/* ****************************************************************************
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* This source code is subject to terms and conditions of the Apache License, Version 2.0. A
|
||||
* copy of the license can be found in the License.html file at the root of this distribution. If
|
||||
* you cannot locate the Apache License, Version 2.0, please send an email to
|
||||
* dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
|
||||
* by the terms of the Apache License, Version 2.0.
|
||||
*
|
||||
* You must not remove this notice, or any other, from this software.
|
||||
*
|
||||
*
|
||||
* ***************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Dynamic.Utils;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
#if SILVERLIGHT
|
||||
using System.Core;
|
||||
#endif
|
||||
|
||||
#if CLR2
|
||||
namespace Microsoft.Scripting.Ast.Compiler {
|
||||
#else
|
||||
namespace System.Linq.Expressions.Compiler {
|
||||
#endif
|
||||
#if CLR2 || SILVERLIGHT
|
||||
using ILGenerator = OffsetTrackingILGenerator;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Contains compiler state corresponding to a LabelTarget
|
||||
/// See also LabelScopeInfo.
|
||||
/// </summary>
|
||||
internal sealed class LabelInfo {
|
||||
// The tree node representing this label
|
||||
private readonly LabelTarget _node;
|
||||
|
||||
// The IL label, will be mutated if Node is redefined
|
||||
private Label _label;
|
||||
private bool _labelDefined;
|
||||
|
||||
internal Label Label {
|
||||
get {
|
||||
EnsureLabelAndValue();
|
||||
return _label;
|
||||
}
|
||||
}
|
||||
|
||||
// The local that carries the label's value, if any
|
||||
private LocalBuilder _value;
|
||||
|
||||
// The blocks where this label is defined. If it has more than one item,
|
||||
// the blocks can't be jumped to except from a child block
|
||||
private readonly Set<LabelScopeInfo> _definitions = new Set<LabelScopeInfo>();
|
||||
|
||||
// Blocks that jump to this block
|
||||
private readonly List<LabelScopeInfo> _references = new List<LabelScopeInfo>();
|
||||
|
||||
// True if this label is the last thing in this block
|
||||
// (meaning we can emit a direct return)
|
||||
private readonly bool _canReturn;
|
||||
|
||||
// True if at least one jump is across blocks
|
||||
// If we have any jump across blocks to this label, then the
|
||||
// LabelTarget can only be defined in one place
|
||||
private bool _acrossBlockJump;
|
||||
|
||||
// Until we have more information, default to a leave instruction,
|
||||
// which always works. Note: leave spills the stack, so we need to
|
||||
// ensure that StackSpiller has guarenteed us an empty stack at this
|
||||
// point. Otherwise Leave and Branch are not equivalent
|
||||
private OpCode _opCode = OpCodes.Leave;
|
||||
|
||||
private readonly ILGenerator _ilg;
|
||||
|
||||
internal LabelInfo(ILGenerator il, LabelTarget node, bool canReturn) {
|
||||
_ilg = il;
|
||||
_node = node;
|
||||
_canReturn = canReturn;
|
||||
}
|
||||
|
||||
internal bool CanReturn {
|
||||
get { return _canReturn; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if it is legal to emit a "branch" instruction based on
|
||||
/// currently available information. Call the Reference method before
|
||||
/// using this property.
|
||||
/// </summary>
|
||||
internal bool CanBranch {
|
||||
get { return _opCode != OpCodes.Leave; }
|
||||
}
|
||||
|
||||
internal void Reference(LabelScopeInfo block) {
|
||||
_references.Add(block);
|
||||
if (_definitions.Count > 0) {
|
||||
ValidateJump(block);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if the label was successfully defined
|
||||
// or false if the label is now ambiguous
|
||||
internal void Define(LabelScopeInfo block) {
|
||||
// Prevent the label from being shadowed, which enforces cleaner
|
||||
// trees. Also we depend on this for simplicity (keeping only one
|
||||
// active IL Label per LabelInfo)
|
||||
for (LabelScopeInfo j = block; j != null; j = j.Parent) {
|
||||
if (j.ContainsTarget(_node)) {
|
||||
throw Error.LabelTargetAlreadyDefined(_node.Name);
|
||||
}
|
||||
}
|
||||
|
||||
_definitions.Add(block);
|
||||
block.AddLabelInfo(_node, this);
|
||||
|
||||
// Once defined, validate all jumps
|
||||
if (_definitions.Count == 1) {
|
||||
foreach (var r in _references) {
|
||||
ValidateJump(r);
|
||||
}
|
||||
} else {
|
||||
// Was just redefined, if we had any across block jumps, they're
|
||||
// now invalid
|
||||
if (_acrossBlockJump) {
|
||||
throw Error.AmbiguousJump(_node.Name);
|
||||
}
|
||||
// For local jumps, we need a new IL label
|
||||
// This is okay because:
|
||||
// 1. no across block jumps have been made or will be made
|
||||
// 2. we don't allow the label to be shadowed
|
||||
_labelDefined = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void ValidateJump(LabelScopeInfo reference) {
|
||||
// Assume we can do a ret/branch
|
||||
_opCode = _canReturn ? OpCodes.Ret : OpCodes.Br;
|
||||
|
||||
// look for a simple jump out
|
||||
for (LabelScopeInfo j = reference; j != null; j = j.Parent) {
|
||||
if (_definitions.Contains(j)) {
|
||||
// found it, jump is valid!
|
||||
return;
|
||||
}
|
||||
if (j.Kind == LabelScopeKind.Finally ||
|
||||
j.Kind == LabelScopeKind.Filter) {
|
||||
break;
|
||||
}
|
||||
if (j.Kind == LabelScopeKind.Try ||
|
||||
j.Kind == LabelScopeKind.Catch) {
|
||||
_opCode = OpCodes.Leave;
|
||||
}
|
||||
}
|
||||
|
||||
_acrossBlockJump = true;
|
||||
if (_node != null && _node.Type != typeof(void)) {
|
||||
throw Error.NonLocalJumpWithValue(_node.Name);
|
||||
}
|
||||
|
||||
if (_definitions.Count > 1) {
|
||||
throw Error.AmbiguousJump(_node.Name);
|
||||
}
|
||||
|
||||
// We didn't find an outward jump. Look for a jump across blocks
|
||||
LabelScopeInfo def = _definitions.First();
|
||||
LabelScopeInfo common = Helpers.CommonNode(def, reference, b => b.Parent);
|
||||
|
||||
// Assume we can do a ret/branch
|
||||
_opCode = _canReturn ? OpCodes.Ret : OpCodes.Br;
|
||||
|
||||
// Validate that we aren't jumping across a finally
|
||||
for (LabelScopeInfo j = reference; j != common; j = j.Parent) {
|
||||
if (j.Kind == LabelScopeKind.Finally) {
|
||||
throw Error.ControlCannotLeaveFinally();
|
||||
}
|
||||
if (j.Kind == LabelScopeKind.Filter) {
|
||||
throw Error.ControlCannotLeaveFilterTest();
|
||||
}
|
||||
if (j.Kind == LabelScopeKind.Try ||
|
||||
j.Kind == LabelScopeKind.Catch) {
|
||||
_opCode = OpCodes.Leave;
|
||||
}
|
||||
}
|
||||
|
||||
// Valdiate that we aren't jumping into a catch or an expression
|
||||
for (LabelScopeInfo j = def; j != common; j = j.Parent) {
|
||||
if (!j.CanJumpInto) {
|
||||
if (j.Kind == LabelScopeKind.Expression) {
|
||||
throw Error.ControlCannotEnterExpression();
|
||||
} else {
|
||||
throw Error.ControlCannotEnterTry();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void ValidateFinish() {
|
||||
// Make sure that if this label was jumped to, it is also defined
|
||||
if (_references.Count > 0 && _definitions.Count == 0) {
|
||||
throw Error.LabelTargetUndefined(_node.Name);
|
||||
}
|
||||
}
|
||||
|
||||
internal void EmitJump() {
|
||||
// Return directly if we can
|
||||
if (_opCode == OpCodes.Ret) {
|
||||
_ilg.Emit(OpCodes.Ret);
|
||||
} else {
|
||||
StoreValue();
|
||||
_ilg.Emit(_opCode, Label);
|
||||
}
|
||||
}
|
||||
|
||||
private void StoreValue() {
|
||||
EnsureLabelAndValue();
|
||||
if (_value != null) {
|
||||
_ilg.Emit(OpCodes.Stloc, _value);
|
||||
}
|
||||
}
|
||||
|
||||
internal void Mark() {
|
||||
if (_canReturn) {
|
||||
// Don't mark return labels unless they were actually jumped to
|
||||
// (returns are last so we know for sure if anyone jumped to it)
|
||||
if (!_labelDefined) {
|
||||
// We don't even need to emit the "ret" because
|
||||
// LambdaCompiler does that for us.
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, emit something like:
|
||||
// ret
|
||||
// <marked label>:
|
||||
// ldloc <value>
|
||||
_ilg.Emit(OpCodes.Ret);
|
||||
} else {
|
||||
|
||||
// For the normal case, we emit:
|
||||
// stloc <value>
|
||||
// <marked label>:
|
||||
// ldloc <value>
|
||||
StoreValue();
|
||||
}
|
||||
MarkWithEmptyStack();
|
||||
}
|
||||
|
||||
// Like Mark, but assumes the stack is empty
|
||||
internal void MarkWithEmptyStack() {
|
||||
_ilg.MarkLabel(Label);
|
||||
if (_value != null) {
|
||||
// We always read the value from a local, because we don't know
|
||||
// if there will be a "leave" instruction targeting it ("branch"
|
||||
// preserves its stack, but "leave" empties the stack)
|
||||
_ilg.Emit(OpCodes.Ldloc, _value);
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureLabelAndValue() {
|
||||
if (!_labelDefined) {
|
||||
_labelDefined = true;
|
||||
_label = _ilg.DefineLabel();
|
||||
if (_node != null && _node.Type != typeof(void)) {
|
||||
_value = _ilg.DeclareLocal(_node.Type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal enum LabelScopeKind {
|
||||
// any "statement like" node that can be jumped into
|
||||
Statement,
|
||||
|
||||
// these correspond to the node of the same name
|
||||
Block,
|
||||
Switch,
|
||||
Lambda,
|
||||
Try,
|
||||
|
||||
// these correspond to the part of the try block we're in
|
||||
Catch,
|
||||
Finally,
|
||||
Filter,
|
||||
|
||||
// the catch-all value for any other expression type
|
||||
// (means we can't jump into it)
|
||||
Expression,
|
||||
}
|
||||
|
||||
//
|
||||
// Tracks scoping information for LabelTargets. Logically corresponds to a
|
||||
// "label scope". Even though we have arbitrary goto support, we still need
|
||||
// to track what kinds of nodes that gotos are jumping through, both to
|
||||
// emit property IL ("leave" out of a try block), and for validation, and
|
||||
// to allow labels to be duplicated in the tree, as long as the jumps are
|
||||
// considered "up only" jumps.
|
||||
//
|
||||
// We create one of these for every Expression that can be jumped into, as
|
||||
// well as creating them for the first expression we can't jump into. The
|
||||
// "Kind" property indicates what kind of scope this is.
|
||||
//
|
||||
internal sealed class LabelScopeInfo {
|
||||
private Dictionary<LabelTarget, LabelInfo> Labels; // lazily allocated, we typically use this only once every 6th-7th block
|
||||
internal readonly LabelScopeKind Kind;
|
||||
internal readonly LabelScopeInfo Parent;
|
||||
|
||||
internal LabelScopeInfo(LabelScopeInfo parent, LabelScopeKind kind) {
|
||||
Parent = parent;
|
||||
Kind = kind;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if we can jump into this node
|
||||
/// </summary>
|
||||
internal bool CanJumpInto {
|
||||
get {
|
||||
switch (Kind) {
|
||||
case LabelScopeKind.Block:
|
||||
case LabelScopeKind.Statement:
|
||||
case LabelScopeKind.Switch:
|
||||
case LabelScopeKind.Lambda:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal bool ContainsTarget(LabelTarget target) {
|
||||
if (Labels == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Labels.ContainsKey(target);
|
||||
}
|
||||
|
||||
internal bool TryGetLabelInfo(LabelTarget target, out LabelInfo info) {
|
||||
if (Labels == null) {
|
||||
info = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
return Labels.TryGetValue(target, out info);
|
||||
}
|
||||
|
||||
internal void AddLabelInfo(LabelTarget target, LabelInfo info) {
|
||||
Debug.Assert(CanJumpInto);
|
||||
|
||||
if (Labels == null) {
|
||||
Labels = new Dictionary<LabelTarget, LabelInfo>();
|
||||
}
|
||||
|
||||
Labels.Add(target, info);
|
||||
}
|
||||
}
|
||||
}
|
334
external/referencesource/System.Core/Microsoft/Scripting/Compiler/LambdaCompiler.Address.cs
vendored
Normal file
334
external/referencesource/System.Core/Microsoft/Scripting/Compiler/LambdaCompiler.Address.cs
vendored
Normal file
@ -0,0 +1,334 @@
|
||||
/* ****************************************************************************
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* This source code is subject to terms and conditions of the Apache License, Version 2.0. A
|
||||
* copy of the license can be found in the License.html file at the root of this distribution. If
|
||||
* you cannot locate the Apache License, Version 2.0, please send an email to
|
||||
* dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
|
||||
* by the terms of the Apache License, Version 2.0.
|
||||
*
|
||||
* You must not remove this notice, or any other, from this software.
|
||||
*
|
||||
*
|
||||
* ***************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Dynamic.Utils;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
#if CLR2
|
||||
namespace Microsoft.Scripting.Ast.Compiler {
|
||||
#else
|
||||
namespace System.Linq.Expressions.Compiler {
|
||||
#endif
|
||||
partial class LambdaCompiler {
|
||||
private void EmitAddress(Expression node, Type type) {
|
||||
EmitAddress(node, type, CompilationFlags.EmitExpressionStart);
|
||||
}
|
||||
|
||||
// We don't want "ref" parameters to modify values of expressions
|
||||
// except where it would in IL: locals, args, fields, and array elements
|
||||
// (Unbox is an exception, it's intended to emit a ref to the orignal
|
||||
// boxed value)
|
||||
private void EmitAddress(Expression node, Type type, CompilationFlags flags) {
|
||||
Debug.Assert(node != null);
|
||||
bool emitStart = (flags & CompilationFlags.EmitExpressionStartMask) == CompilationFlags.EmitExpressionStart;
|
||||
CompilationFlags startEmitted = emitStart ? EmitExpressionStart(node) : CompilationFlags.EmitNoExpressionStart;
|
||||
|
||||
switch (node.NodeType) {
|
||||
default:
|
||||
EmitExpressionAddress(node, type);
|
||||
break;
|
||||
|
||||
case ExpressionType.ArrayIndex:
|
||||
AddressOf((BinaryExpression)node, type);
|
||||
break;
|
||||
|
||||
case ExpressionType.Parameter:
|
||||
AddressOf((ParameterExpression)node, type);
|
||||
break;
|
||||
|
||||
case ExpressionType.MemberAccess:
|
||||
AddressOf((MemberExpression)node, type);
|
||||
break;
|
||||
|
||||
case ExpressionType.Unbox:
|
||||
AddressOf((UnaryExpression)node, type);
|
||||
break;
|
||||
|
||||
case ExpressionType.Call:
|
||||
AddressOf((MethodCallExpression)node, type);
|
||||
break;
|
||||
|
||||
case ExpressionType.Index:
|
||||
AddressOf((IndexExpression)node, type);
|
||||
break;
|
||||
}
|
||||
|
||||
if (emitStart) {
|
||||
EmitExpressionEnd(startEmitted);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void AddressOf(BinaryExpression node, Type type) {
|
||||
Debug.Assert(node.NodeType == ExpressionType.ArrayIndex && node.Method == null);
|
||||
|
||||
if (TypeUtils.AreEquivalent(type, node.Type)) {
|
||||
EmitExpression(node.Left);
|
||||
EmitExpression(node.Right);
|
||||
Type rightType = node.Right.Type;
|
||||
if (TypeUtils.IsNullableType(rightType)) {
|
||||
LocalBuilder loc = GetLocal(rightType);
|
||||
_ilg.Emit(OpCodes.Stloc, loc);
|
||||
_ilg.Emit(OpCodes.Ldloca, loc);
|
||||
_ilg.EmitGetValue(rightType);
|
||||
FreeLocal(loc);
|
||||
}
|
||||
Type indexType = TypeUtils.GetNonNullableType(rightType);
|
||||
if (indexType != typeof(int)) {
|
||||
_ilg.EmitConvertToType(indexType, typeof(int), true);
|
||||
}
|
||||
_ilg.Emit(OpCodes.Ldelema, node.Type);
|
||||
} else {
|
||||
EmitExpressionAddress(node, type);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddressOf(ParameterExpression node, Type type) {
|
||||
if (TypeUtils.AreEquivalent(type, node.Type)) {
|
||||
if (node.IsByRef) {
|
||||
_scope.EmitGet(node);
|
||||
} else {
|
||||
_scope.EmitAddressOf(node);
|
||||
}
|
||||
} else {
|
||||
EmitExpressionAddress(node, type);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void AddressOf(MemberExpression node, Type type) {
|
||||
if (TypeUtils.AreEquivalent(type, node.Type)) {
|
||||
// emit "this", if any
|
||||
Type objectType = null;
|
||||
if (node.Expression != null) {
|
||||
EmitInstance(node.Expression, objectType = node.Expression.Type);
|
||||
}
|
||||
EmitMemberAddress(node.Member, objectType);
|
||||
} else {
|
||||
EmitExpressionAddress(node, type);
|
||||
}
|
||||
}
|
||||
|
||||
// assumes the instance is already on the stack
|
||||
private void EmitMemberAddress(MemberInfo member, Type objectType) {
|
||||
if (member.MemberType == MemberTypes.Field) {
|
||||
FieldInfo field = (FieldInfo)member;
|
||||
|
||||
// Verifiable code may not take the address of an init-only field.
|
||||
// If we are asked to do so then get the value out of the field, stuff it
|
||||
// into a local of the same type, and then take the address of the local.
|
||||
// Typically this is what we want to do anyway; if we are saying
|
||||
// Foo.bar.ToString() for a static value-typed field bar then we don't need
|
||||
// the address of field bar to do the call. The address of a local which
|
||||
// has the same value as bar is sufficient.
|
||||
|
||||
//
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if (!field.IsLiteral && !field.IsInitOnly) {
|
||||
_ilg.EmitFieldAddress(field);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
EmitMemberGet(member, objectType);
|
||||
LocalBuilder temp = GetLocal(GetMemberType(member));
|
||||
_ilg.Emit(OpCodes.Stloc, temp);
|
||||
_ilg.Emit(OpCodes.Ldloca, temp);
|
||||
}
|
||||
|
||||
|
||||
private void AddressOf(MethodCallExpression node, Type type) {
|
||||
// An array index of a multi-dimensional array is represented by a call to Array.Get,
|
||||
// rather than having its own array-access node. This means that when we are trying to
|
||||
// get the address of a member of a multi-dimensional array, we'll be trying to
|
||||
// get the address of a Get method, and it will fail to do so. Instead, detect
|
||||
// this situation and replace it with a call to the Address method.
|
||||
if (!node.Method.IsStatic &&
|
||||
node.Object.Type.IsArray &&
|
||||
node.Method == node.Object.Type.GetMethod("Get", BindingFlags.Public | BindingFlags.Instance)) {
|
||||
|
||||
MethodInfo mi = node.Object.Type.GetMethod("Address", BindingFlags.Public | BindingFlags.Instance);
|
||||
|
||||
EmitMethodCall(node.Object, mi, node);
|
||||
} else {
|
||||
EmitExpressionAddress(node, type);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddressOf(IndexExpression node, Type type) {
|
||||
if (!TypeUtils.AreEquivalent(type, node.Type) || node.Indexer != null) {
|
||||
EmitExpressionAddress(node, type);
|
||||
return;
|
||||
}
|
||||
|
||||
if (node.Arguments.Count == 1) {
|
||||
EmitExpression(node.Object);
|
||||
EmitExpression(node.Arguments[0]);
|
||||
_ilg.Emit(OpCodes.Ldelema, node.Type);
|
||||
} else {
|
||||
var address = node.Object.Type.GetMethod("Address", BindingFlags.Public | BindingFlags.Instance);
|
||||
EmitMethodCall(node.Object, address, node);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddressOf(UnaryExpression node, Type type) {
|
||||
Debug.Assert(node.NodeType == ExpressionType.Unbox);
|
||||
Debug.Assert(type.IsValueType && !TypeUtils.IsNullableType(type));
|
||||
|
||||
// Unbox leaves a pointer to the boxed value on the stack
|
||||
EmitExpression(node.Operand);
|
||||
_ilg.Emit(OpCodes.Unbox, type);
|
||||
}
|
||||
|
||||
private void EmitExpressionAddress(Expression node, Type type) {
|
||||
Debug.Assert(TypeUtils.AreReferenceAssignable(type, node.Type));
|
||||
|
||||
EmitExpression(node, CompilationFlags.EmitAsNoTail | CompilationFlags.EmitNoExpressionStart);
|
||||
LocalBuilder tmp = GetLocal(type);
|
||||
_ilg.Emit(OpCodes.Stloc, tmp);
|
||||
_ilg.Emit(OpCodes.Ldloca, tmp);
|
||||
}
|
||||
|
||||
|
||||
// Emits the address of the expression, returning the write back if necessary
|
||||
//
|
||||
// For properties, we want to write back into the property if it's
|
||||
// passed byref.
|
||||
private WriteBack EmitAddressWriteBack(Expression node, Type type) {
|
||||
CompilationFlags startEmitted = EmitExpressionStart(node);
|
||||
|
||||
WriteBack result = null;
|
||||
if (TypeUtils.AreEquivalent(type, node.Type)) {
|
||||
switch (node.NodeType) {
|
||||
case ExpressionType.MemberAccess:
|
||||
result = AddressOfWriteBack((MemberExpression)node);
|
||||
break;
|
||||
case ExpressionType.Index:
|
||||
result = AddressOfWriteBack((IndexExpression)node);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (result == null) {
|
||||
EmitAddress(node, type, CompilationFlags.EmitAsNoTail | CompilationFlags.EmitNoExpressionStart);
|
||||
}
|
||||
|
||||
EmitExpressionEnd(startEmitted);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private WriteBack AddressOfWriteBack(MemberExpression node) {
|
||||
if (node.Member.MemberType != MemberTypes.Property || !((PropertyInfo)node.Member).CanWrite) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// emit instance, if any
|
||||
LocalBuilder instanceLocal = null;
|
||||
Type instanceType = null;
|
||||
if (node.Expression != null) {
|
||||
EmitInstance(node.Expression, instanceType = node.Expression.Type);
|
||||
// store in local
|
||||
_ilg.Emit(OpCodes.Dup);
|
||||
_ilg.Emit(OpCodes.Stloc, instanceLocal = GetLocal(instanceType));
|
||||
}
|
||||
|
||||
PropertyInfo pi = (PropertyInfo)node.Member;
|
||||
|
||||
// emit the get
|
||||
EmitCall(instanceType, pi.GetGetMethod(true));
|
||||
|
||||
// emit the address of the value
|
||||
var valueLocal = GetLocal(node.Type);
|
||||
_ilg.Emit(OpCodes.Stloc, valueLocal);
|
||||
_ilg.Emit(OpCodes.Ldloca, valueLocal);
|
||||
|
||||
// Set the property after the method call
|
||||
// don't re-evaluate anything
|
||||
return delegate() {
|
||||
if (instanceLocal != null) {
|
||||
_ilg.Emit(OpCodes.Ldloc, instanceLocal);
|
||||
FreeLocal(instanceLocal);
|
||||
}
|
||||
_ilg.Emit(OpCodes.Ldloc, valueLocal);
|
||||
FreeLocal(valueLocal);
|
||||
EmitCall(instanceType, pi.GetSetMethod(true));
|
||||
};
|
||||
}
|
||||
|
||||
private WriteBack AddressOfWriteBack(IndexExpression node) {
|
||||
if (node.Indexer == null || !node.Indexer.CanWrite) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// emit instance, if any
|
||||
LocalBuilder instanceLocal = null;
|
||||
Type instanceType = null;
|
||||
if (node.Object != null) {
|
||||
EmitInstance(node.Object, instanceType = node.Object.Type);
|
||||
|
||||
_ilg.Emit(OpCodes.Dup);
|
||||
_ilg.Emit(OpCodes.Stloc, instanceLocal = GetLocal(instanceType));
|
||||
}
|
||||
|
||||
// Emit indexes. We don't allow byref args, so no need to worry
|
||||
// about writebacks or EmitAddress
|
||||
List<LocalBuilder> args = new List<LocalBuilder>();
|
||||
foreach (var arg in node.Arguments) {
|
||||
EmitExpression(arg);
|
||||
|
||||
var argLocal = GetLocal(arg.Type);
|
||||
_ilg.Emit(OpCodes.Dup);
|
||||
_ilg.Emit(OpCodes.Stloc, argLocal);
|
||||
args.Add(argLocal);
|
||||
}
|
||||
|
||||
// emit the get
|
||||
EmitGetIndexCall(node, instanceType);
|
||||
|
||||
// emit the address of the value
|
||||
var valueLocal = GetLocal(node.Type);
|
||||
_ilg.Emit(OpCodes.Stloc, valueLocal);
|
||||
_ilg.Emit(OpCodes.Ldloca, valueLocal);
|
||||
|
||||
// Set the property after the method call
|
||||
// don't re-evaluate anything
|
||||
return delegate() {
|
||||
if (instanceLocal != null) {
|
||||
_ilg.Emit(OpCodes.Ldloc, instanceLocal);
|
||||
FreeLocal(instanceLocal);
|
||||
}
|
||||
foreach (var arg in args) {
|
||||
_ilg.Emit(OpCodes.Ldloc, arg);
|
||||
FreeLocal(arg);
|
||||
}
|
||||
_ilg.Emit(OpCodes.Ldloc, valueLocal);
|
||||
FreeLocal(valueLocal);
|
||||
|
||||
EmitSetIndexCall(node, instanceType);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
705
external/referencesource/System.Core/Microsoft/Scripting/Compiler/LambdaCompiler.Binary.cs
vendored
Normal file
705
external/referencesource/System.Core/Microsoft/Scripting/Compiler/LambdaCompiler.Binary.cs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
257
external/referencesource/System.Core/Microsoft/Scripting/Compiler/LambdaCompiler.ControlFlow.cs
vendored
Normal file
257
external/referencesource/System.Core/Microsoft/Scripting/Compiler/LambdaCompiler.ControlFlow.cs
vendored
Normal file
@ -0,0 +1,257 @@
|
||||
/* ****************************************************************************
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* This source code is subject to terms and conditions of the Apache License, Version 2.0. A
|
||||
* copy of the license can be found in the License.html file at the root of this distribution. If
|
||||
* you cannot locate the Apache License, Version 2.0, please send an email to
|
||||
* dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
|
||||
* by the terms of the Apache License, Version 2.0.
|
||||
*
|
||||
* You must not remove this notice, or any other, from this software.
|
||||
*
|
||||
*
|
||||
* ***************************************************************************/
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Dynamic.Utils;
|
||||
|
||||
#if CLR2
|
||||
namespace Microsoft.Scripting.Ast.Compiler {
|
||||
#else
|
||||
namespace System.Linq.Expressions.Compiler {
|
||||
#endif
|
||||
|
||||
// The part of the LambdaCompiler dealing with low level control flow
|
||||
// break, contiue, return, exceptions, etc
|
||||
partial class LambdaCompiler {
|
||||
|
||||
private LabelInfo EnsureLabel(LabelTarget node) {
|
||||
LabelInfo result;
|
||||
if (!_labelInfo.TryGetValue(node, out result)) {
|
||||
_labelInfo.Add(node, result = new LabelInfo(_ilg, node, false));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private LabelInfo ReferenceLabel(LabelTarget node) {
|
||||
LabelInfo result = EnsureLabel(node);
|
||||
result.Reference(_labelBlock);
|
||||
return result;
|
||||
}
|
||||
|
||||
private LabelInfo DefineLabel(LabelTarget node) {
|
||||
if (node == null) {
|
||||
return new LabelInfo(_ilg, null, false);
|
||||
}
|
||||
LabelInfo result = EnsureLabel(node);
|
||||
result.Define(_labelBlock);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void PushLabelBlock(LabelScopeKind type) {
|
||||
_labelBlock = new LabelScopeInfo(_labelBlock, type);
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "kind")]
|
||||
private void PopLabelBlock(LabelScopeKind kind) {
|
||||
Debug.Assert(_labelBlock != null && _labelBlock.Kind == kind);
|
||||
_labelBlock = _labelBlock.Parent;
|
||||
}
|
||||
|
||||
private void EmitLabelExpression(Expression expr, CompilationFlags flags) {
|
||||
var node = (LabelExpression)expr;
|
||||
Debug.Assert(node.Target != null);
|
||||
|
||||
// If we're an immediate child of a block, our label will already
|
||||
// be defined. If not, we need to define our own block so this
|
||||
// label isn't exposed except to its own child expression.
|
||||
LabelInfo label = null;
|
||||
|
||||
if (_labelBlock.Kind == LabelScopeKind.Block) {
|
||||
_labelBlock.TryGetLabelInfo(node.Target, out label);
|
||||
|
||||
// We're in a block but didn't find our label, try switch
|
||||
if (label == null && _labelBlock.Parent.Kind == LabelScopeKind.Switch) {
|
||||
_labelBlock.Parent.TryGetLabelInfo(node.Target, out label);
|
||||
}
|
||||
|
||||
// if we're in a switch or block, we should've found the label
|
||||
Debug.Assert(label != null);
|
||||
}
|
||||
|
||||
if (label == null) {
|
||||
label = DefineLabel(node.Target);
|
||||
}
|
||||
|
||||
if (node.DefaultValue != null) {
|
||||
if (node.Target.Type == typeof(void)) {
|
||||
EmitExpressionAsVoid(node.DefaultValue, flags);
|
||||
} else {
|
||||
flags = UpdateEmitExpressionStartFlag(flags, CompilationFlags.EmitExpressionStart);
|
||||
EmitExpression(node.DefaultValue, flags);
|
||||
}
|
||||
}
|
||||
|
||||
label.Mark();
|
||||
}
|
||||
|
||||
private void EmitGotoExpression(Expression expr, CompilationFlags flags) {
|
||||
var node = (GotoExpression)expr;
|
||||
var labelInfo = ReferenceLabel(node.Target);
|
||||
|
||||
var tailCall = flags & CompilationFlags.EmitAsTailCallMask;
|
||||
if (tailCall != CompilationFlags.EmitAsNoTail) {
|
||||
// Since tail call flags are not passed into EmitTryExpression, CanReturn
|
||||
// means the goto will be emitted as Ret. Therefore we can emit the goto's
|
||||
// default value with tail call. This can be improved by detecting if the
|
||||
// target label is equivalent to the return label.
|
||||
tailCall = labelInfo.CanReturn ? CompilationFlags.EmitAsTail : CompilationFlags.EmitAsNoTail;
|
||||
flags = UpdateEmitAsTailCallFlag(flags, tailCall);
|
||||
}
|
||||
|
||||
if (node.Value != null) {
|
||||
if (node.Target.Type == typeof(void)) {
|
||||
EmitExpressionAsVoid(node.Value, flags);
|
||||
} else {
|
||||
flags = UpdateEmitExpressionStartFlag(flags, CompilationFlags.EmitExpressionStart);
|
||||
EmitExpression(node.Value, flags);
|
||||
}
|
||||
}
|
||||
|
||||
labelInfo.EmitJump();
|
||||
|
||||
EmitUnreachable(node, flags);
|
||||
}
|
||||
|
||||
// We need to push default(T), unless we're emitting ourselves as
|
||||
// void. Even though the code is unreachable, we still have to
|
||||
// generate correct IL. We can get rid of this once we have better
|
||||
// reachability analysis.
|
||||
private void EmitUnreachable(Expression node, CompilationFlags flags) {
|
||||
if (node.Type != typeof(void) && (flags & CompilationFlags.EmitAsVoidType) == 0) {
|
||||
_ilg.EmitDefault(node.Type);
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryPushLabelBlock(Expression node) {
|
||||
// Anything that is "statement-like" -- e.g. has no associated
|
||||
// stack state can be jumped into, with the exception of try-blocks
|
||||
// We indicate this by a "Block"
|
||||
//
|
||||
// Otherwise, we push an "Expression" to indicate that it can't be
|
||||
// jumped into
|
||||
switch (node.NodeType) {
|
||||
default:
|
||||
if (_labelBlock.Kind != LabelScopeKind.Expression) {
|
||||
PushLabelBlock(LabelScopeKind.Expression);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case ExpressionType.Label:
|
||||
// LabelExpression is a bit special, if it's directly in a
|
||||
// block it becomes associate with the block's scope. Same
|
||||
// thing if it's in a switch case body.
|
||||
if (_labelBlock.Kind == LabelScopeKind.Block) {
|
||||
var label = ((LabelExpression)node).Target;
|
||||
if (_labelBlock.ContainsTarget(label)) {
|
||||
return false;
|
||||
}
|
||||
if (_labelBlock.Parent.Kind == LabelScopeKind.Switch &&
|
||||
_labelBlock.Parent.ContainsTarget(label)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
PushLabelBlock(LabelScopeKind.Statement);
|
||||
return true;
|
||||
case ExpressionType.Block:
|
||||
if (node is SpilledExpressionBlock) {
|
||||
// treat it as an expression
|
||||
goto default;
|
||||
}
|
||||
|
||||
PushLabelBlock(LabelScopeKind.Block);
|
||||
// Labels defined immediately in the block are valid for
|
||||
// the whole block.
|
||||
if (_labelBlock.Parent.Kind != LabelScopeKind.Switch) {
|
||||
DefineBlockLabels(node);
|
||||
}
|
||||
return true;
|
||||
case ExpressionType.Switch:
|
||||
PushLabelBlock(LabelScopeKind.Switch);
|
||||
// Define labels inside of the switch cases so theyare in
|
||||
// scope for the whole switch. This allows "goto case" and
|
||||
// "goto default" to be considered as local jumps.
|
||||
var @switch = (SwitchExpression)node;
|
||||
foreach (SwitchCase c in @switch.Cases) {
|
||||
DefineBlockLabels(c.Body);
|
||||
}
|
||||
DefineBlockLabels(@switch.DefaultBody);
|
||||
return true;
|
||||
|
||||
// Remove this when Convert(Void) goes away.
|
||||
case ExpressionType.Convert:
|
||||
if (node.Type != typeof(void)) {
|
||||
// treat it as an expression
|
||||
goto default;
|
||||
}
|
||||
PushLabelBlock(LabelScopeKind.Statement);
|
||||
return true;
|
||||
|
||||
case ExpressionType.Conditional:
|
||||
case ExpressionType.Loop:
|
||||
case ExpressionType.Goto:
|
||||
PushLabelBlock(LabelScopeKind.Statement);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private void DefineBlockLabels(Expression node) {
|
||||
var block = node as BlockExpression;
|
||||
if (block == null || block is SpilledExpressionBlock) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0, n = block.ExpressionCount; i < n; i++) {
|
||||
Expression e = block.GetExpression(i);
|
||||
|
||||
var label = e as LabelExpression;
|
||||
if (label != null) {
|
||||
DefineLabel(label.Target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// See if this lambda has a return label
|
||||
// If so, we'll create it now and mark it as allowing the "ret" opcode
|
||||
// This allows us to generate better IL
|
||||
private void AddReturnLabel(LambdaExpression lambda) {
|
||||
var expression = lambda.Body;
|
||||
|
||||
while (true) {
|
||||
switch (expression.NodeType) {
|
||||
default:
|
||||
// Didn't find return label
|
||||
return;
|
||||
case ExpressionType.Label:
|
||||
// Found the label. We can directly return from this place
|
||||
// only if the label type is reference assignable to the lambda return type.
|
||||
var label = ((LabelExpression)expression).Target;
|
||||
_labelInfo.Add(label, new LabelInfo(_ilg, label, TypeUtils.AreReferenceAssignable(lambda.ReturnType, label.Type)));
|
||||
return;
|
||||
case ExpressionType.Block:
|
||||
// Look in the last significant expression of a block
|
||||
var body = (BlockExpression)expression;
|
||||
// omit empty and debuginfo at the end of the block since they
|
||||
// are not going to emit any IL
|
||||
for (int i = body.ExpressionCount - 1; i >= 0; i--) {
|
||||
expression = body.GetExpression(i);
|
||||
if (Significant(expression)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
1089
external/referencesource/System.Core/Microsoft/Scripting/Compiler/LambdaCompiler.Expressions.cs
vendored
Normal file
1089
external/referencesource/System.Core/Microsoft/Scripting/Compiler/LambdaCompiler.Expressions.cs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
282
external/referencesource/System.Core/Microsoft/Scripting/Compiler/LambdaCompiler.Generated.cs
vendored
Normal file
282
external/referencesource/System.Core/Microsoft/Scripting/Compiler/LambdaCompiler.Generated.cs
vendored
Normal file
@ -0,0 +1,282 @@
|
||||
/* ****************************************************************************
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* This source code is subject to terms and conditions of the Apache License, Version 2.0. A
|
||||
* copy of the license can be found in the License.html file at the root of this distribution. If
|
||||
* you cannot locate the Apache License, Version 2.0, please send an email to
|
||||
* dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
|
||||
* by the terms of the Apache License, Version 2.0.
|
||||
*
|
||||
* You must not remove this notice, or any other, from this software.
|
||||
*
|
||||
*
|
||||
* ***************************************************************************/
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Dynamic.Utils;
|
||||
|
||||
#if CLR2
|
||||
namespace Microsoft.Scripting.Ast.Compiler {
|
||||
#else
|
||||
namespace System.Linq.Expressions.Compiler {
|
||||
#endif
|
||||
partial class LambdaCompiler {
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1505:AvoidUnmaintainableCode"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
|
||||
private void EmitExpression(Expression node, CompilationFlags flags) {
|
||||
Debug.Assert(node != null);
|
||||
|
||||
bool emitStart = (flags & CompilationFlags.EmitExpressionStartMask) == CompilationFlags.EmitExpressionStart;
|
||||
|
||||
CompilationFlags startEmitted = emitStart ? EmitExpressionStart(node) : CompilationFlags.EmitNoExpressionStart;
|
||||
// only pass tail call flags to emit the expression
|
||||
flags = flags & CompilationFlags.EmitAsTailCallMask;
|
||||
|
||||
switch (node.NodeType) {
|
||||
#region Generated Expression Compiler
|
||||
|
||||
// *** BEGIN GENERATED CODE ***
|
||||
// generated by function: gen_compiler from: generate_tree.py
|
||||
|
||||
case ExpressionType.Add:
|
||||
EmitBinaryExpression(node, flags);
|
||||
break;
|
||||
case ExpressionType.AddChecked:
|
||||
EmitBinaryExpression(node, flags);
|
||||
break;
|
||||
case ExpressionType.And:
|
||||
EmitBinaryExpression(node, flags);
|
||||
break;
|
||||
case ExpressionType.AndAlso:
|
||||
EmitAndAlsoBinaryExpression(node, flags);
|
||||
break;
|
||||
case ExpressionType.ArrayLength:
|
||||
EmitUnaryExpression(node, flags);
|
||||
break;
|
||||
case ExpressionType.ArrayIndex:
|
||||
EmitBinaryExpression(node, flags);
|
||||
break;
|
||||
case ExpressionType.Call:
|
||||
EmitMethodCallExpression(node, flags);
|
||||
break;
|
||||
case ExpressionType.Coalesce:
|
||||
EmitCoalesceBinaryExpression(node);
|
||||
break;
|
||||
case ExpressionType.Conditional:
|
||||
EmitConditionalExpression(node, flags);
|
||||
break;
|
||||
case ExpressionType.Constant:
|
||||
EmitConstantExpression(node);
|
||||
break;
|
||||
case ExpressionType.Convert:
|
||||
EmitConvertUnaryExpression(node, flags);
|
||||
break;
|
||||
case ExpressionType.ConvertChecked:
|
||||
EmitConvertUnaryExpression(node, flags);
|
||||
break;
|
||||
case ExpressionType.Divide:
|
||||
EmitBinaryExpression(node, flags);
|
||||
break;
|
||||
case ExpressionType.Equal:
|
||||
EmitBinaryExpression(node, flags);
|
||||
break;
|
||||
case ExpressionType.ExclusiveOr:
|
||||
EmitBinaryExpression(node, flags);
|
||||
break;
|
||||
case ExpressionType.GreaterThan:
|
||||
EmitBinaryExpression(node, flags);
|
||||
break;
|
||||
case ExpressionType.GreaterThanOrEqual:
|
||||
EmitBinaryExpression(node, flags);
|
||||
break;
|
||||
case ExpressionType.Invoke:
|
||||
EmitInvocationExpression(node, flags);
|
||||
break;
|
||||
case ExpressionType.Lambda:
|
||||
EmitLambdaExpression(node);
|
||||
break;
|
||||
case ExpressionType.LeftShift:
|
||||
EmitBinaryExpression(node, flags);
|
||||
break;
|
||||
case ExpressionType.LessThan:
|
||||
EmitBinaryExpression(node, flags);
|
||||
break;
|
||||
case ExpressionType.LessThanOrEqual:
|
||||
EmitBinaryExpression(node, flags);
|
||||
break;
|
||||
case ExpressionType.ListInit:
|
||||
EmitListInitExpression(node);
|
||||
break;
|
||||
case ExpressionType.MemberAccess:
|
||||
EmitMemberExpression(node);
|
||||
break;
|
||||
case ExpressionType.MemberInit:
|
||||
EmitMemberInitExpression(node);
|
||||
break;
|
||||
case ExpressionType.Modulo:
|
||||
EmitBinaryExpression(node, flags);
|
||||
break;
|
||||
case ExpressionType.Multiply:
|
||||
EmitBinaryExpression(node, flags);
|
||||
break;
|
||||
case ExpressionType.MultiplyChecked:
|
||||
EmitBinaryExpression(node, flags);
|
||||
break;
|
||||
case ExpressionType.Negate:
|
||||
EmitUnaryExpression(node, flags);
|
||||
break;
|
||||
case ExpressionType.UnaryPlus:
|
||||
EmitUnaryExpression(node, flags);
|
||||
break;
|
||||
case ExpressionType.NegateChecked:
|
||||
EmitUnaryExpression(node, flags);
|
||||
break;
|
||||
case ExpressionType.New:
|
||||
EmitNewExpression(node);
|
||||
break;
|
||||
case ExpressionType.NewArrayInit:
|
||||
EmitNewArrayExpression(node);
|
||||
break;
|
||||
case ExpressionType.NewArrayBounds:
|
||||
EmitNewArrayExpression(node);
|
||||
break;
|
||||
case ExpressionType.Not:
|
||||
EmitUnaryExpression(node, flags);
|
||||
break;
|
||||
case ExpressionType.NotEqual:
|
||||
EmitBinaryExpression(node, flags);
|
||||
break;
|
||||
case ExpressionType.Or:
|
||||
EmitBinaryExpression(node, flags);
|
||||
break;
|
||||
case ExpressionType.OrElse:
|
||||
EmitOrElseBinaryExpression(node, flags);
|
||||
break;
|
||||
case ExpressionType.Parameter:
|
||||
EmitParameterExpression(node);
|
||||
break;
|
||||
case ExpressionType.Power:
|
||||
EmitBinaryExpression(node, flags);
|
||||
break;
|
||||
case ExpressionType.Quote:
|
||||
EmitQuoteUnaryExpression(node);
|
||||
break;
|
||||
case ExpressionType.RightShift:
|
||||
EmitBinaryExpression(node, flags);
|
||||
break;
|
||||
case ExpressionType.Subtract:
|
||||
EmitBinaryExpression(node, flags);
|
||||
break;
|
||||
case ExpressionType.SubtractChecked:
|
||||
EmitBinaryExpression(node, flags);
|
||||
break;
|
||||
case ExpressionType.TypeAs:
|
||||
EmitUnaryExpression(node, flags);
|
||||
break;
|
||||
case ExpressionType.TypeIs:
|
||||
EmitTypeBinaryExpression(node);
|
||||
break;
|
||||
case ExpressionType.Assign:
|
||||
EmitAssignBinaryExpression(node);
|
||||
break;
|
||||
case ExpressionType.Block:
|
||||
EmitBlockExpression(node, flags);
|
||||
break;
|
||||
case ExpressionType.DebugInfo:
|
||||
EmitDebugInfoExpression(node);
|
||||
break;
|
||||
case ExpressionType.Decrement:
|
||||
EmitUnaryExpression(node, flags);
|
||||
break;
|
||||
case ExpressionType.Dynamic:
|
||||
EmitDynamicExpression(node);
|
||||
break;
|
||||
case ExpressionType.Default:
|
||||
EmitDefaultExpression(node);
|
||||
break;
|
||||
case ExpressionType.Extension:
|
||||
EmitExtensionExpression(node);
|
||||
break;
|
||||
case ExpressionType.Goto:
|
||||
EmitGotoExpression(node, flags);
|
||||
break;
|
||||
case ExpressionType.Increment:
|
||||
EmitUnaryExpression(node, flags);
|
||||
break;
|
||||
case ExpressionType.Index:
|
||||
EmitIndexExpression(node);
|
||||
break;
|
||||
case ExpressionType.Label:
|
||||
EmitLabelExpression(node, flags);
|
||||
break;
|
||||
case ExpressionType.RuntimeVariables:
|
||||
EmitRuntimeVariablesExpression(node);
|
||||
break;
|
||||
case ExpressionType.Loop:
|
||||
EmitLoopExpression(node);
|
||||
break;
|
||||
case ExpressionType.Switch:
|
||||
EmitSwitchExpression(node, flags);
|
||||
break;
|
||||
case ExpressionType.Throw:
|
||||
EmitThrowUnaryExpression(node);
|
||||
break;
|
||||
case ExpressionType.Try:
|
||||
EmitTryExpression(node);
|
||||
break;
|
||||
case ExpressionType.Unbox:
|
||||
EmitUnboxUnaryExpression(node);
|
||||
break;
|
||||
case ExpressionType.TypeEqual:
|
||||
EmitTypeBinaryExpression(node);
|
||||
break;
|
||||
case ExpressionType.OnesComplement:
|
||||
EmitUnaryExpression(node, flags);
|
||||
break;
|
||||
case ExpressionType.IsTrue:
|
||||
EmitUnaryExpression(node, flags);
|
||||
break;
|
||||
case ExpressionType.IsFalse:
|
||||
EmitUnaryExpression(node, flags);
|
||||
break;
|
||||
|
||||
// *** END GENERATED CODE ***
|
||||
|
||||
#endregion
|
||||
|
||||
default:
|
||||
throw ContractUtils.Unreachable;
|
||||
}
|
||||
|
||||
if (emitStart) {
|
||||
EmitExpressionEnd(startEmitted);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsChecked(ExpressionType op) {
|
||||
switch (op) {
|
||||
#region Generated Checked Operations
|
||||
|
||||
// *** BEGIN GENERATED CODE ***
|
||||
// generated by function: gen_checked_ops from: generate_tree.py
|
||||
|
||||
case ExpressionType.AddChecked:
|
||||
case ExpressionType.ConvertChecked:
|
||||
case ExpressionType.MultiplyChecked:
|
||||
case ExpressionType.NegateChecked:
|
||||
case ExpressionType.SubtractChecked:
|
||||
case ExpressionType.AddAssignChecked:
|
||||
case ExpressionType.MultiplyAssignChecked:
|
||||
case ExpressionType.SubtractAssignChecked:
|
||||
|
||||
// *** END GENERATED CODE ***
|
||||
|
||||
#endregion
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user