/// <summary> /// Creates a new compiler scope for inner expressions that establish a scope. /// </summary> /// <param name="parent">The parent scope.</param> /// <param name="scope">The scope information obtained during analysis.</param> public CompilerScope(CompilerScope parent, Scope scope) { Parent = parent; _scope = scope; var locals = new List <ParameterExpression>(); var hoisted = new List <ParameterExpression>(); // // Get all variables that need hoisting. These will be registered in a // HoistedLocals instance for runtime access in Quote and RuntimeVariables // nodes. The remaining variables are kept as locals. // foreach (var local in scope.Locals) { var variable = local.Key; var storage = local.Value; if (storage == StorageKind.Local) { locals.Add(variable); } else { hoisted.Add(variable); } } _hoistedLocals = new HoistedLocals(parent._hoistedLocals, hoisted.AsReadOnly(), scope.Locals); Closure = _hoistedLocals.SelfVariable; Locals = locals; }
/// <summary> /// Creates a new compiler scope for the top-level lambda. /// </summary> /// <param name="methodTable">The method table parameter.</param> public CompilerScope(ParameterExpression methodTable) { Parent = null; _scope = null; Closure = methodTable; _hoistedLocals = new HoistedLocals(methodTable); Locals = new List <ParameterExpression>(); }
/// <summary> /// Creates a new object representing information about hoisted locals. /// </summary> /// <param name="parent">The parent scope.</param> /// <param name="variables">The hoisted locals in the current scope.</param> /// <param name="definitions">The storage kinds of the variables.</param> public HoistedLocals(HoistedLocals parent, ReadOnlyCollection <ParameterExpression> variables, Dictionary <ParameterExpression, StorageKind> definitions) { Parent = parent; Definitions = definitions; // // By convention, we'll store the parent's self-variable in the first slot of the closure. // // Note that the self-variable is statically typed, so the whole closure and all its storage // slots are statically typed too, no matter how far we have to ascend through the parent // chain. This differs from LINQ ETs at the point of writing (10/10/16). // if (parent != null) { variables = variables.AddFirst(parent.SelfVariable); } Variables = variables; // // Next, build the index map and gather all the storage kinds for the hoisted locals in // order to create a statically typed closure. The index map will be used for runtime // variable access; the closure will be used to emit code to access the locals in the // compiled expression tree. // var count = variables.Count; var indexes = new Dictionary <ParameterExpression, int>(count); var fields = new List <KeyValuePair <ParameterExpression, StorageKind> >(count); for (var i = 0; i < count; i++) { var variable = variables[i]; var storageKind = variable == parent?.SelfVariable ? StorageKind.Hoisted : definitions[variable]; indexes.Add(variable, i); fields.Add(new KeyValuePair <ParameterExpression, StorageKind>(variable, storageKind)); } // // Build the closure and store the closure information. The static type of the closure // is used for the self-variable, resulting in static typing across the closure and its // parent chain. // Closure = ClosureGenerator.Create(fields); SelfVariable = Expression.Variable(Closure.ClosureType, name: null); Indexes = new ReadOnlyDictionary <ParameterExpression, int>(indexes); }