예제 #1
0
        private void Reference(ParameterExpression node, VariableStorageKind storage)
        {
            CompilerScope?definition = null;

            foreach (CompilerScope scope in _scopes)
            {
                if (scope.Definitions.ContainsKey(node))
                {
                    definition = scope;
                    break;
                }
                scope.NeedsClosure = true;
                if (scope.IsMethod)
                {
                    storage = VariableStorageKind.Hoisted;
                }
            }
            if (definition == null)
            {
                throw Error.UndefinedVariable(node.Name, node.Type, CurrentLambdaName);
            }
            if (storage == VariableStorageKind.Hoisted)
            {
                if (node.IsByRef)
                {
                    throw Error.CannotCloseOverByRef(node.Name, CurrentLambdaName);
                }
                definition.Definitions[node] = VariableStorageKind.Hoisted;
            }
        }
예제 #2
0
        protected internal override Expression VisitRuntimeVariables(RuntimeVariablesExpression node)
        {
            foreach (var v in node.Variables)
            {
                // Force hoisting of these variables
                CompilerScope?definition = null;
                foreach (var scope in _scopes)
                {
                    if (scope.Definitions.ContainsKey(v))
                    {
                        definition = scope;
                        break;
                    }

                    scope.NeedsClosure = true;
                }

                if (definition == null)
                {
                    throw new InvalidOperationException($"variable '{v.Name}' of type '{v.Type}' referenced from scope '{CurrentLambdaName}', but it is not defined");
                }

                if (v.IsByRef)
                {
                    throw new InvalidOperationException($"Cannot close over byref parameter '{v.Name}' referenced in lambda '{CurrentLambdaName}'");
                }

                definition.Definitions[v] = VariableStorageKind.Hoisted;
            }

            return(node);
        }
예제 #3
0
        protected internal override Expression VisitParameter(ParameterExpression node)
        {
            Reference(node, VariableStorageKind.Local);

            //
            // Track reference count so we can emit it in a more optimal way if
            // it is used a lot.
            //
            CompilerScope?referenceScope = null;

            foreach (CompilerScope scope in _scopes)
            {
                //
                // There are two times we care about references:
                //   1. When we enter a lambda, we want to cache frequently
                //      used variables
                //   2. When we enter a scope with closed-over variables, we
                //      want to cache it immediately when we allocate the
                //      closure slot for it
                //
                if (scope.IsMethod || scope.Definitions.ContainsKey(node))
                {
                    referenceScope = scope;
                    break;
                }
            }

            Debug.Assert(referenceScope != null);
            referenceScope.ReferenceCount ??= new Dictionary <ParameterExpression, int>();

            Helpers.IncrementCount(node, referenceScope.ReferenceCount);
            return(node);
        }
예제 #4
0
        protected internal override Expression VisitParameter(ParameterExpression node)
        {
            var           storage        = VariableStorageKind.Local;
            CompilerScope?definition     = null;
            CompilerScope?referenceScope = null;

            foreach (var scope in _scopes)
            {
                if (scope.Definitions.ContainsKey(node))
                {
                    definition = scope;
                    break;
                }

                scope.NeedsClosure = true;
                if (!scope.IsMethod)
                {
                    continue;
                }

                storage        = VariableStorageKind.Hoisted;
                referenceScope = scope;
            }

            if (definition == null)
            {
                throw new InvalidOperationException($"variable '{node.Name}' of type '{node.Type}' referenced from scope '{CurrentLambdaName}', but it is not defined");
            }

            if (storage == VariableStorageKind.Hoisted)
            {
                if (node.IsByRef)
                {
                    throw new InvalidOperationException($"Cannot close over byref parameter '{node.Name}' referenced in lambda '{CurrentLambdaName}'");
                }

                definition.Definitions[node] = VariableStorageKind.Hoisted;
            }

            referenceScope ??= definition;

            (referenceScope.ReferenceCount ?? (referenceScope.ReferenceCount = new Dictionary <ParameterExpression, int>())).TryGetValue(node, out var count);
            referenceScope.ReferenceCount[node] = count + 1;
            return(node);
        }
예제 #5
0
        private void EmitLambdaBody(CompilerScope?parent, bool inlined, CompilationFlags flags)
        {
            _scope.Enter(this, parent);

            if (inlined)
            {
                // The arguments were already pushed onto the IL stack.
                // Store them into locals, popping in reverse order.
                //
                // If any arguments were ByRef, the address is on the stack and
                // we'll be storing it into the variable, which has a ref type.
                for (var i = _lambda.ParameterCount - 1; i >= 0; i--)
                {
                    _scope.EmitSet(_lambda.GetParameter(i));
                }
            }

            // Need to emit the expression start for the lambda body
            flags = UpdateEmitExpressionStartFlag(flags, CompilationFlags.EmitExpressionStart);
            if (_lambda.ReturnType == typeof(void))
            {
                EmitExpressionAsVoid(_lambda.Body, flags);
            }
            else
            {
                EmitExpression(_lambda.Body, flags);
            }

            // Return must be the last instruction in a CLI method.
            // But if we're inlining the lambda, we want to leave the return
            // value on the IL stack.
            if (!inlined)
            {
                IL.Emit(OpCodes.Ret);
            }

            _scope.Exit();

            // Validate labels
            Debug.Assert(_labelBlock.Parent == null && _labelBlock.Kind == LabelScopeKind.Lambda);
            foreach (var label in _labelInfo.Values)
            {
                label.ValidateFinish();
            }
        }
예제 #6
0
        private void SetParent(LambdaCompiler lc, CompilerScope?parent)
        {
            Debug.Assert(_parent == null && parent != this);
            _parent = parent;

            if (NeedsClosure && _parent != null)
            {
                _closureHoistedLocals = _parent.NearestHoistedLocals;
            }

            ReadOnlyCollection <ParameterExpression> hoistedVars = GetVariables().Where(p => Definitions[p] == VariableStorageKind.Hoisted).ToReadOnly();

            if (hoistedVars.Count > 0)
            {
                _hoistedLocals = new HoistedLocals(_closureHoistedLocals, hoistedVars);
                AddLocal(lc, _hoistedLocals.SelfVariable);
            }
        }
예제 #7
0
        /// <summary>
        ///     Frees unnamed locals, clears state associated with this compiler
        /// </summary>
        internal void Exit()
        {
            // free scope's variables
            if (!IsMethod)
            {
                foreach (var storage in _locals.Values)
                {
                    storage.FreeLocal();
                }
            }

            // Clear state that is associated with this parent
            // (because the scope can be reused in another context)
            _parent               = null;
            _hoistedLocals        = null;
            _closureHoistedLocals = null;
            _locals.Clear();
        }
예제 #8
0
        /// <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);
        }
예제 #9
0
        /// <summary>
        /// Resolve a local variable in this scope or a closed over scope
        /// Throws if the variable is not 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)
            {
                if (s._locals.TryGetValue(variable, out Storage? 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);
        }