public FunctionTransformPipeline(
            AssemblyTranslator translator,
            QualifiedMemberIdentifier identifier, JSFunctionExpression function,
            SpecialIdentifiers si
            )
        {
            Translator         = translator;
            Identifier         = identifier;
            Function           = function;
            SpecialIdentifiers = si;

            FillPipeline();

            if (!Translator.FunctionCache.ActiveTransformPipelines.TryAdd(Identifier, this))
            {
                throw new ThreadStateException();
            }

            if (CheckForStaticAnalysisChanges)
            {
                OriginalFunctionBody = Function.Body.ToString();
                OriginalSecondPass   = Translator.FunctionCache.GetSecondPass(function.Method, function.Method.QualifiedIdentifier);
            }
        }
        private void CloneArgumentsIfNecessary(
            IEnumerable<KeyValuePair<ParameterDefinition, JSExpression>> parameters,
            IList<JSExpression> argumentValues,
            FunctionAnalysis2ndPass sa
        ) {
            var parms = parameters.ToArray();

            for (int i = 0, c = parms.Length; i < c; i++)
            {
                var pd = parms[i].Key;
                var argument = parms[i].Value;

                string parameterName = null;
                if (pd != null)
                    parameterName = pd.Name;

                GenericParameter relevantParameter;
                if (IsParameterCopyNeeded(sa, parameterName, argument, out relevantParameter))
                {
                    if (Tracing)
                        Debug.WriteLine(String.Format("struct copy introduced for argument #{0}: {1}", i, argument));

                    argumentValues[i] = MakeCopyForExpression(argument, relevantParameter);
                }
                else
                {
                    if (Tracing && TypeUtil.IsStruct(argument.GetActualType(TypeSystem)))
                        Debug.WriteLine(String.Format("struct copy elided for argument #{0}: {1}", i, argument));
                }
            }
        }
        protected bool IsParameterCopyNeeded (FunctionAnalysis2ndPass sa, string parameterName, JSExpression expression, out GenericParameter relevantParameter) {
            if (!IsCopyNeeded(expression, out relevantParameter))
                return false;

            if (sa == null)
                return true;

            if (!OptimizeCopies)
                return true;

            bool modified = true, escapes = true, isResult = false;

            if (parameterName != null) {
                modified = sa.ModifiedVariables.Contains(parameterName);
                escapes = sa.EscapingVariables.Contains(parameterName);
                isResult = sa.ResultVariable == parameterName;
            }

            return modified || (escapes && !isResult);
        }
        public void VisitNode (JSFunctionExpression fn) {
            var countRefs = new CountVariableReferences(ReferenceCounts);
            countRefs.Visit(fn.Body);

            SecondPass = GetSecondPass(fn.Method);
            if (SecondPass == null)
                throw new InvalidDataException("No second-pass static analysis data for function '" + fn.Method.QualifiedIdentifier + "'");

            VisitChildren(fn);
        }
        public void VisitNode(JSFunctionExpression fn)
        {
            // Create a new visitor for nested function expressions
            if (Stack.OfType<JSFunctionExpression>().Skip(1).FirstOrDefault() != null) {
                var nested = new EmulateStructAssignment(TypeSystem, FunctionSource, CLR, OptimizeCopies);
                nested.Visit(fn);
                return;
            }

            var countRefs = new CountVariableReferences(ReferenceCounts);
            countRefs.Visit(fn.Body);

            SecondPass = FunctionSource.GetSecondPass(fn.Method);

            VisitChildren(fn);
        }
        public bool RunUntilCompletion()
        {
            const int lockTimeoutMs = 250;
            bool      completed     = false;

            var entry = Translator.FunctionCache.GetCacheEntry(Identifier);

            TrackedLockCollection.DeadlockInfo deadlock;
            var lockResult = entry.StaticAnalysisDataLock.TryBlockingEnter(out deadlock, timeoutMs: lockTimeoutMs);

            if (!lockResult.Success)
            {
                if (deadlock != null)
                {
                    Console.Error.WriteLine("Failed to lock '{0}' for transform pipeline: {1} {2}", Identifier, lockResult.FailureReason, deadlock);
                }

                return(false);
            }

            try {
                while (Pipeline.Count > 0)
                {
                    var currentStage = Pipeline.Peek();

                    try {
                        if (currentStage())
                        {
                            Pipeline.Dequeue();

                            if (CheckForStaticAnalysisChanges)
                            {
                                var currentSecondPass = Translator.FunctionCache.GetSecondPass(this.Function.Method, this.Function.Method.QualifiedIdentifier);

                                string[] differences;
                                if (!currentSecondPass.Equals(OriginalSecondPass, out differences))
                                {
                                    var currentFunctionBody = Function.Body.ToString();

                                    Console.WriteLine("// Second pass data changed by pipeline stage '" + currentStage.Method.Name + "' - " + String.Join(", ", differences));
                                    Console.WriteLine("// Original function body //");
                                    Console.WriteLine(OriginalFunctionBody);
                                    Console.WriteLine("// New function body //");
                                    Console.WriteLine(currentFunctionBody);

                                    OriginalSecondPass   = currentSecondPass;
                                    OriginalFunctionBody = currentFunctionBody;
                                }
                            }
                        }
                        else
                        {
                            SuspendCount += 1;

                            return(completed = false);
                        }
                    } catch (Exception exc) {
                        string functionName;

                        if ((Function.Method != null) && (Function.Method.Reference != null))
                        {
                            functionName = Function.Method.Reference.FullName;
                        }
                        else
                        {
                            functionName = Function.DisplayName ?? "<unknown>";
                        }

                        throw new FunctionTransformFailureException(functionName, currentStage.Method.Name, exc);
                    }
                }

                return(completed = true);
            } finally {
                entry.TransformPipelineHasCompleted |= completed;
                entry.StaticAnalysisDataLock.Exit();

                if (completed)
                {
                    FunctionTransformPipeline temp;
                    if (!Translator.FunctionCache.ActiveTransformPipelines.TryRemove(Identifier, out temp))
                    {
                        throw new ThreadStateException();
                    }
                }
                else
                {
                    if (!Translator.FunctionCache.PendingTransformsQueue.TryEnqueue(Identifier))
                    {
                        throw new ThreadStateException();
                    }
                }
            }
        }
        protected bool IsArgumentCopyNeeded (FunctionAnalysis2ndPass sa, string parameterName, JSExpression expression, out GenericParameter relevantParameter) {
            if (!IsCopyNeeded(expression, out relevantParameter))
                return false;

            if (sa == null)
                return true;

            if (!OptimizeCopies)
                return true;

            bool modified = true, escapes = true, isResult = false;

            if (parameterName != null) {
                modified = sa.IsVariableModified(parameterName);
                escapes = sa.DoesVariableEscape(parameterName, true);
                isResult = sa.ResultVariable == parameterName;
            }

            var result = modified || (escapes && !isResult);

            if (!result) {
                if (TraceElidedCopies)
                    Console.WriteLine("argument {0} ('{1}') needs no copy because it isn't modified and doesn't escape", expression, parameterName);
            }

            return result;
        }