Inheritance: JSIdentifier, IAnnotatedChildren
        protected void EliminateVariable (JSNode context, JSVariable variable, JSExpression replaceWith, QualifiedMemberIdentifier method) {
            {
                var replacer = new VariableEliminator(
                    variable,
                    JSChangeTypeExpression.New(replaceWith, variable.GetActualType(TypeSystem), TypeSystem)
                );
                replacer.Visit(context);
            }

            {
                var replacer = new VariableEliminator(variable, replaceWith);
                var assignments = (from a in FirstPass.Assignments where 
                                       variable.Equals(a.NewValue) ||
                                       a.NewValue.SelfAndChildrenRecursive.Any(variable.Equals)
                                       select a).ToArray();

                foreach (var a in assignments) {
                    if (!variable.Equals(a.NewValue))
                        replacer.Visit(a.NewValue);
                }
            }

            Variables.Remove(variable.Identifier);
            FunctionSource.InvalidateFirstPass(method);
        }
Exemple #2
0
 public JSTryCatchBlock(JSStatement body, JSVariable catchVariable = null, JSStatement @catch = null, JSStatement @finally = null)
 {
     Body          = body;
     CatchVariable = catchVariable;
     Catch         = @catch;
     Finally       = @finally;
 }
        protected bool MatchesConstructedReference (JSExpression lhs, JSVariable rhs) {
            var jsv = lhs as JSVariable;
            if ((jsv != null) && (jsv.Identifier == rhs.Identifier))
                return true;

            return false;
        }
        public ILBlockTranslator(AssemblyTranslator translator, DecompilerContext context, MethodReference methodReference, MethodDefinition methodDefinition, ILBlock ilb, IEnumerable<ILVariable> parameters, IEnumerable<ILVariable> allVariables)
        {
            Translator = translator;
            Context = context;
            ThisMethodReference = methodReference;
            ThisMethod = methodDefinition;
            Block = ilb;

            SpecialIdentifiers = new JSIL.SpecialIdentifiers(TypeSystem);

            if (methodReference.HasThis)
                Variables.Add("this", JSThisParameter.New(methodReference.DeclaringType, methodReference));

            foreach (var parameter in parameters) {
                if ((parameter.Name == "this") && (parameter.OriginalParameter.Index == -1))
                    continue;

                ParameterNames.Add(parameter.Name);
                Variables.Add(parameter.Name, new JSParameter(parameter.Name, parameter.Type, methodReference));
            }

            foreach (var variable in allVariables) {
                var v = JSVariable.New(variable, methodReference);
                if (Variables.ContainsKey(v.Identifier)) {
                    v = new JSVariable(variable.OriginalVariable.Name, variable.Type, methodReference);
                    RenamedVariables[variable] = v;
                    Variables.Add(v.Identifier, v);
                } else {
                    Variables.Add(v.Identifier, v);
                }
            }
        }
        public void VisitNode(JSVariable variable)
        {
            int count;
            if (ReferenceCounts.TryGetValue(variable.Identifier, out count))
                ReferenceCounts[variable.Identifier] = count + 1;
            else
                ReferenceCounts[variable.Identifier] = 1;

            VisitChildren(variable);
        }
        public void VisitNode(JSVariable variable)
        {
            // If our parent node is the function, we're in the argument list
            if (ParentNode is JSFunctionExpression)
                return;

            int count;
            if (ReferenceCounts.TryGetValue(variable.Identifier, out count))
                ReferenceCounts[variable.Identifier] = count + 1;
            else
                ReferenceCounts[variable.Identifier] = 1;
        }
Exemple #7
0
        public override void ReplaceChild(JSNode oldChild, JSNode newChild)
        {
            if (oldChild == null)
            {
                throw new ArgumentNullException("oldChild");
            }

            if (CatchVariable == oldChild)
            {
                CatchVariable = (JSVariable)newChild;
            }

            if (Catch == oldChild)
            {
                Catch = (JSStatement)newChild;
            }

            if (Finally == oldChild)
            {
                Finally = (JSStatement)newChild;
            }

            Body.ReplaceChild(oldChild, newChild);
        }
        protected bool IsEffectivelyConstant(JSVariable target, JSExpression source)
        {
            if ((source == null) || (source.IsNull))
                return false;

            // Handle special cases where our interpretation of 'constant' needs to be more flexible
            {
                var ie = source as JSIndexerExpression;
                if (ie != null) {
                    if (
                        IsEffectivelyConstant(target, ie.Target) &&
                        IsEffectivelyConstant(target, ie.Index)
                    )
                        return true;
                }
            }

            {
                var ae = source as JSArrayExpression;
                if (
                    (ae != null) &&
                    (from av in ae.Values select IsEffectivelyConstant(target, av)).All((b) => b)
                )
                    return true;
            }

            {
                var de = source as JSDotExpression;
                if (
                    (de != null) &&
                    IsEffectivelyConstant(target, de.Target) &&
                    IsEffectivelyConstant(target, de.Member)
                )
                    return true;
            }

            {
                var ie = source as JSInvocationExpression;
                if (
                    (ie != null) && ie.ConstantIfArgumentsAre &&
                    IsEffectivelyConstant(target, ie.ThisReference) &&
                    ie.Arguments.All((a) => IsEffectivelyConstant(target, a))
                )
                    return true;
            }

            if ((source is JSUnaryOperatorExpression) || (source is JSBinaryOperatorExpression)) {
                if (source.Children.OfType<JSExpression>().All((_v) => IsEffectivelyConstant(target, _v)))
                    return true;
            }

            if (source.IsConstant)
                return true;

            // Try to find a spot between the source variable's assignments where all of our
            //  copies and accesses can fit. If we find one, our variable is effectively constant.
            var v = source as JSVariable;
            if (v != null) {
                var assignments = (from a in FirstPass.Assignments where v.Equals(a.Target) select a).ToArray();
                if (assignments.Length < 1)
                    return v.IsParameter;

                var targetAssignments = (from a in FirstPass.Assignments where v.Equals(a.Target) select a).ToArray();
                if (targetAssignments.Length < 1)
                    return false;

                var targetAccesses = (from a in FirstPass.Accesses where target.Equals(a.Source) select a).ToArray();
                if (targetAccesses.Length < 1)
                    return false;

                var targetFirstAssigned = targetAssignments.FirstOrDefault();
                var targetLastAssigned = targetAssignments.LastOrDefault();

                var targetFirstAccessed = targetAccesses.FirstOrDefault();
                var targetLastAccessed = targetAccesses.LastOrDefault();

                bool foundAssignmentSlot = false;

                for (int i = 0, c = assignments.Length; i < c; i++) {
                    int assignment = assignments[i].StatementIndex, nextAssignment = int.MaxValue;
                    if (i < c - 1)
                        nextAssignment = assignments[i + 1].StatementIndex;

                    if (
                        (targetFirstAssigned.StatementIndex >= assignment) &&
                        (targetFirstAssigned.StatementIndex < nextAssignment) &&
                        (targetFirstAccessed.StatementIndex >= assignment) &&
                        (targetLastAccessed.StatementIndex <= nextAssignment)
                    ) {
                        foundAssignmentSlot = true;
                        break;
                    }
                }

                if (!foundAssignmentSlot)
                    return false;

                return true;
            }

            // TODO
            /*
            var accesses = (from a in FirstPass.Accesses where v.Equals(a.Source) select a).ToArray();
            if (accesses.Length < 1)
                return false;

            var firstAccess = accesses.FirstOrDefault();
            var lastAccess = accesses.LastOrDefault();

            if (firstAccess == firstAssignment + 1)
                return true;
             */

            return false;
        }
Exemple #9
0
        public override void ReplaceChild(JSNode oldChild, JSNode newChild)
        {
            if (oldChild == null)
                throw new ArgumentNullException("oldChild");

            if (CatchVariable == oldChild)
                CatchVariable = (JSVariable)newChild;

            if (Catch == oldChild)
                Catch = (JSStatement)newChild;

            if (Finally == oldChild)
                Finally = (JSStatement)newChild;

            Body.ReplaceChild(oldChild, newChild);
        }
 public VariableReferenceAccessTransformer(JSILIdentifier jsil, JSVariable variable)
 {
     JSIL = jsil;
     Variable = variable;
 }
        protected void TransformParameterIntoReference(JSVariable parameter, JSBlockStatement block)
        {
            var newParameter = new JSParameter("$" + parameter.Identifier, parameter.IdentifierType, parameter.Function);
            var newVariable = new JSVariable(parameter.Identifier, new ByReferenceType(parameter.IdentifierType), parameter.Function);
            var newDeclaration = new JSVariableDeclarationStatement(
                new JSBinaryOperatorExpression(
                    JSOperator.Assignment,
                    // We have to use parameter here, not newVariable or newParameter, otherwise the resulting
                    // assignment looks like 'x.value = initializer' instead of 'x = initializer'
                    parameter,
                    JSIL.NewReference(newParameter),
                    newVariable.IdentifierType
                )
            );

            if (Tracing)
                Debug.WriteLine(String.Format("Transformed {0} into {1}={2}", parameter, newVariable, newParameter));

            Variables[newVariable.Identifier] = newVariable;
            Variables.Add(newParameter.Identifier, newParameter);

            var enclosingFunction = Stack.OfType<JSFunctionExpression>().First();
            enclosingFunction.Body.Statements.Insert(0, newDeclaration);

            var oldIndex = Array.IndexOf(enclosingFunction.Parameters, parameter);
            enclosingFunction.Parameters[oldIndex] = newParameter;
        }
 public VariableReferenceAccessTransformer (JSILIdentifier jsil, JSVariable variable, IFunctionSource functionSource) {
     JSIL = jsil;
     Variable = variable;
     FunctionSource = functionSource;
 }
Exemple #13
0
        private void EmitFieldIntrinsics(int heapSize)
        {
            // FIXME: Gross
            var tis = (ITypeInfoSource)Translator.TypeInfoProvider;

            Formatter.WriteRaw(";; Compiler-generated field accessors");
            Formatter.NewLine();

            foreach (var kvp in FieldTable.OrderBy(kvp => kvp.Value.Offset)) {
                var fd   = kvp.Value.Field;
                var fi   = (FieldInfo)tis.Get(fd);
                var name = WasmUtil.FormatMemberName(fi.Member);
                var typeSystem = fd.FieldType.Module.TypeSystem;

                // HACK
                var baseAddressParam = new JSVariable("address", typeSystem.Int32, null);
                // HACK
                var valueParam = new JSVariable("value", fd.FieldType, null);

                JSExpression address;
                if (fd.IsStatic) {
                    address = JSLiteral.New(kvp.Value.Offset + heapSize);
                } else {
                    address = new JSBinaryOperatorExpression(
                        JSOperator.Add,
                        baseAddressParam,
                        JSLiteral.New(kvp.Value.Offset),
                        typeSystem.Int32
                    );
                }

                Formatter.ConditionalNewLine();
                Formatter.WriteRaw(
                    "(func $__get_{0} (result {1}){2}(return ",
                    name,
                    WasmUtil.PickTypeKeyword(fd.FieldType),
                    fd.IsStatic
                        ? " "
                        : " (param $address i32) "
                );

                var gm = new GetMemory(
                    fd.FieldType, /* FIXME: Align addresses */ false,
                    address
                );

                // HACK
                EntryPointAstEmitter.Emit(gm);

                Formatter.WriteRaw(") )");

                if (fd.IsInitOnly)
                    continue;

                Formatter.NewLine();
                Formatter.WriteRaw(
                    "(func $__set_{0}{2}(param $value {1}) ",
                    name,
                    WasmUtil.PickTypeKeyword(fd.FieldType),
                    fd.IsStatic
                        ? " "
                        : " (param $address i32) "
                );
                Formatter.Indent();
                Formatter.NewLine();
                var sm = new SetMemory(
                    fd.FieldType, /* FIXME: Align addresses */ false,
                    address, valueParam
                );

                // HACK
                EntryPointAstEmitter.Emit(sm);

                Formatter.Unindent();
                Formatter.ConditionalNewLine();
                Formatter.WriteRaw(")");
            }

            Formatter.NewLine();
            Formatter.NewLine();
        }
Exemple #14
0
 public JSVariableReference(JSVariable referent, MethodReference function)
     : base(referent.Identifier, null, function)
 {
     Referent = referent;
 }
        protected void TransformVariableIntoReference(JSVariable variable, JSVariableDeclarationStatement statement, int declarationIndex)
        {
            if (variable.IsReference)
                Debugger.Break();

            var oldDeclaration = statement.Declarations[declarationIndex];
            var newVariable = variable.Reference();
            var newDeclaration = new JSBinaryOperatorExpression(
                JSOperator.Assignment,
                // We have to use a constructed ref to the variable here, otherwise
                //  the declaration will look like 'var x.value = foo'
                new JSVariable(variable.Identifier, variable.Type),
                JSIL.NewReference(oldDeclaration.Right),
                newVariable.Type
            );

            if (Tracing)
                Debug.WriteLine(String.Format("Transformed {0} into {1} in {2}", variable, newVariable, statement));

            Variables[variable.Identifier] = newVariable;
            statement.Declarations[declarationIndex] = newDeclaration;
            TransformedVariables.Add(variable.Identifier);
        }
        public void VisitNode (JSVariable variable) {
            if (CurrentName == "FunctionSignature") {
                // In argument list
                return;
            }

            if (Variable.Equals(variable)) {
                ParentNode.ReplaceChild(variable, Replacement);
            } else {
                VisitChildren(variable);
            }
        }
        protected void TransformParameterIntoReference(JSVariable parameter, JSBlockStatement block)
        {
            var newParameter = new JSParameter("$" + parameter.Identifier, parameter.Type);
            var newVariable = new JSVariable(parameter.Identifier, new ByReferenceType(parameter.Type));
            var newDeclaration = new JSVariableDeclarationStatement(
                new JSBinaryOperatorExpression(
                    JSOperator.Assignment,
                    // We have to use parameter here, not newVariable or newParameter, otherwise the resulting
                    // assignment looks like 'x.value = initializer' instead of 'x = initializer'
                    parameter,
                    JSIL.NewReference(newParameter),
                    newVariable.Type
                )
            );

            if (Tracing)
                Debug.WriteLine(String.Format("Transformed {0} into {1}={2}", parameter, newVariable, newParameter));

            Variables[newVariable.Identifier] = newVariable;
            Variables.Add(newParameter.Identifier, newParameter);
            ParameterNames.Remove(parameter.Identifier);
            ParameterNames.Add(newParameter.Identifier);
            block.Statements.Insert(0, newDeclaration);
        }
        public void VisitNode(JSVariable v)
        {
            SeenAlready.Add(v);

            VisitChildren(v);
        }
        protected void TransformVariableIntoReference(JSVariable variable, JSVariableDeclarationStatement statement, int declarationIndex, JSBlockStatement enclosingBlock)
        {
            var oldDeclaration = statement.Declarations[declarationIndex];
            var valueType = oldDeclaration.Right.GetActualType(JSIL.TypeSystem);
            var newVariable = variable.Reference();
            var enclosingFunction = Stack.OfType<JSFunctionExpression>().First();

            JSExpression initialValue;
            // If the declaration was in function scope originally we can hoist the initial value
            //  into our new variable declaration. If not, we need to initialize the ref variable
            //  to the default value for its type. It will get the correct value assigned later.
            if (enclosingBlock == enclosingFunction.Body)
                initialValue = oldDeclaration.Right;
            else
                initialValue = new JSDefaultValueLiteral(valueType);

            var newDeclaration = new JSVariableDeclarationStatement(new JSBinaryOperatorExpression(
                JSOperator.Assignment,
                // We have to use a constructed ref to the variable here, otherwise
                //  the declaration will look like 'var x.value = foo'
                new JSVariable(variable.Identifier, variable.IdentifierType, variable.Function),
                JSIL.NewReference(initialValue),
                newVariable.IdentifierType
            ));

            if (Tracing)
                Debug.WriteLine(String.Format("Transformed {0} into {1} in {2}", variable, newVariable, statement));

            // Insert the new declaration directly before the top-level block containing the original
            //  declaration. This ensures that if its initial value has a dependency on external state,
            //  the declaration will not precede the values it depends on.
            // Note that for declarations that were hoisted out of inner blocks (conditionals, loops)
            //  it doesn't actually matter where the insert occurs, since we initialize with a default
            //  value in that case.
            enclosingFunction.Body.InsertNearChildRecursive(
                statement, newDeclaration, 0
            );

            // If the reference is being declared in function scope, it doesn't need a separate assignment
            //  for its initialization. Otherwise, we need to insert an assignment after the original variable
            //  declaration statement to ensure that the reference variable is initialized to the right value
            //  at the exact right point in the function's execution.
            if (enclosingBlock != enclosingFunction.Body) {
                var newAssignment = new JSExpressionStatement(
                    new JSBinaryOperatorExpression(
                        JSOperator.Assignment, newVariable, oldDeclaration.Right, valueType
                    )
                );
                var insertLocation = enclosingBlock.Statements.IndexOf(statement) + 1;
                enclosingBlock.Statements.Insert(insertLocation, newAssignment);
            }

            Variables[variable.Identifier] = newVariable;
            statement.Declarations.RemoveAt(declarationIndex);
            TransformedVariables.Add(variable.Identifier);
        }
        private bool IsVarEffectivelyConstantHere (JSVariable variable) {
            if (variable == null)
                return false;
            if (variable.IsParameter)
                return false;

            // If we're making a copy of a local variable, and this is the last reference to 
            //  the variable, we can eliminate the copy.
            if (
                !SecondPass.Data.VariablesPassedByRef.Contains(variable.Identifier) &&

                SecondPass.Data.Accesses
                    .Where(a => a.Source == variable.Identifier)
                    // FIXME: This should probably be NodeIndex but that gets out of sync somehow? Outdated analysis data?
                    .All(a => a.StatementIndex <= StatementIndex) &&

                SecondPass.Data.Assignments
                    .Where(a => a.Target == variable.Identifier)
                    // FIXME: This should probably be NodeIndex but that gets out of sync somehow? Outdated analysis data?
                    .All(a => a.StatementIndex < StatementIndex)
            ) {
                return true;
            }

            return false;
        }
        public void VisitNode(JSVariable variable)
        {
            if (
                (ParentNode is JSFunctionExpression) &&
                (this.CurrentName == "FunctionSignature")
            ) {
                VisitChildren(variable);
                return;
            }

            if (
                (variable.Identifier != Variable.Identifier) ||
                // Don't transform if we're inside a read-through already
                (ParentNode is JSReadThroughReferenceExpression) ||
                (
                    // If we're inside a write-through and on the LHS, don't transform
                    (ParentNode is JSWriteThroughReferenceExpression) &&
                    (this.CurrentName == "Left")
                )
            ) {
                VisitChildren(variable);
                return;
            }

            // If we're inside a pass-by-reference (ref x) then don't transform
            if (
                Stack.OfType<JSPassByReferenceExpression>().Any()
            ) {
                VisitChildren(variable);
                return;
            }

            var replacement = new JSReadThroughReferenceExpression(variable);
            ParentNode.ReplaceChild(variable, replacement);
            VisitReplacement(replacement);
        }
Exemple #22
0
 public JSVariableReference(JSVariable referent, MethodReference function)
     : base(referent.Identifier, null, function)
 {
     Referent = referent;
 }
Exemple #23
0
 public JSTryCatchBlock(JSStatement body, JSVariable catchVariable = null, JSStatement @catch = null, JSStatement @finally = null)
 {
     Body = body;
     CatchVariable = catchVariable;
     Catch = @catch;
     Finally = @finally;
 }
Exemple #24
0
        private JSVariable MakeTemporaryVariable (TypeReference type, out string id, JSExpression defaultValue = null) {
            string _id = id = string.Format("$hoisted{0:X2}", HoistedVariableCount++);
            MethodReference methodRef = null;
            if (Function.Method != null)
                methodRef = Function.Method.Reference;

            // So introspection knows about it
            var result = new JSVariable(id, type, methodRef);
            Function.AllVariables.Add(_id, result);
            ToDeclare.Add(new PendingDeclaration(id, type, result, defaultValue));

            return result;
        }
        protected void EliminateVariable(JSNode context, JSVariable variable, JSExpression replaceWith)
        {
            {
                var replacer = new VariableEliminator(
                    variable,
                    JSChangeTypeExpression.New(replaceWith, TypeSystem, variable.GetExpectedType(TypeSystem))
                );
                replacer.Visit(context);
            }

            {
                var replacer = new VariableEliminator(variable, replaceWith);
                var assignments = (from a in FirstPass.Assignments where
                                       variable.Equals(a.NewValue) ||
                                       a.NewValue.AllChildrenRecursive.Any((_n) => variable.Equals(_n))
                                       select a).ToArray();

                foreach (var a in assignments) {
                    if (variable.Equals(a.NewValue)) {
                        FirstPass.Assignments.Remove(a);
                        FirstPass.Assignments.Add(
                            new FunctionAnalysis1stPass.Assignment(
                                a.StatementIndex, a.NodeIndex,
                                a.Target, replaceWith, a.Operator,
                                a.TargetType, a.SourceType
                            )
                        );
                    } else {
                        replacer.Visit(a.NewValue);
                    }
                }
            }

            Variables.Remove(variable.Identifier);
        }
Exemple #26
0
        internal JSFunctionExpression Create (
            MethodInfo info, MethodDefinition methodDef, MethodReference method, 
            QualifiedMemberIdentifier identifier, ILBlockTranslator translator, 
            JSVariable[] parameters, JSBlockStatement body
        ) {
            var args = new PopulatedCacheEntryArgs {
                Info = info,
                Method = method,
                Translator = translator,
                Parameters = parameters,
                Body = body,
            };

            return Cache.GetOrCreate(identifier, args, MakePopulatedCacheEntry).Expression;
        }
        public void VisitNode(JSVariable variable)
        {
            if (ParentNode is JSFunctionExpression) {
                // In argument list
                return;
            }

            if (Variable.Equals(variable)) {
                ParentNode.ReplaceChild(variable, Replacement);
            } else {
                VisitChildren(variable);
            }
        }
Exemple #28
0
 public JSVariableDereference(JSVariable referent, MethodReference function)
     : base(referent.Identifier, DeReferenceType(referent.IdentifierType), function)
 {
     Referent = referent;
 }
        private JSForLoop ReplaceWhileLoopAndEnumerator(JSWhileLoop wl, JSExpression backingStore, JSExpression enumerator, TypeInfo enumeratorType, string arrayMember, string lengthMember)
        {
            var loopId = _NextLoopId++;
            var arrayVariableName = String.Format("a${0:x}", loopId);
            var indexVariableName = String.Format("i${0:x}", loopId);
            var lengthVariableName = String.Format("l${0:x}", loopId);

            var currentPropertyReference = enumeratorType.Definition.Properties.First((p) => p.Name == "Current");
            var currentPropertyInfo = enumeratorType.Source.GetProperty(currentPropertyReference);

            var itemType = currentPropertyInfo.ReturnType;
            var arrayType = new ArrayType(itemType);

            var arrayVariable = new JSVariable(
                arrayVariableName, arrayType, Function.Method.Reference,
                JSDotExpression.New(backingStore, new JSStringIdentifier(arrayMember, arrayType))
            );
            var indexVariable = new JSVariable(
                indexVariableName, TypeSystem.Int32, Function.Method.Reference,
                JSLiteral.New(0)
            );
            var lengthVariable = new JSVariable(
                lengthVariableName, TypeSystem.Int32, Function.Method.Reference,
                JSDotExpression.New(backingStore, new JSStringIdentifier(lengthMember, TypeSystem.Int32))
            );

            var initializer = new JSVariableDeclarationStatement(
                new JSBinaryOperatorExpression(
                    JSOperator.Assignment, arrayVariable, arrayVariable.DefaultValue, arrayVariable.IdentifierType
                ),
                new JSBinaryOperatorExpression(
                    JSOperator.Assignment, indexVariable, indexVariable.DefaultValue, indexVariable.IdentifierType
                ),
                new JSBinaryOperatorExpression(
                    JSOperator.Assignment, lengthVariable, lengthVariable.DefaultValue, lengthVariable.IdentifierType
                )
            );

            var condition = new JSBinaryOperatorExpression(
                JSBinaryOperator.LessThan,
                indexVariable, lengthVariable, TypeSystem.Boolean
            );

            var increment = new JSUnaryOperatorExpression(
                JSUnaryOperator.PostIncrement,
                indexVariable, TypeSystem.Int32
            );

            var result = new JSForLoop(
                initializer, condition, new JSExpressionStatement(increment),
                wl.Statements.ToArray()
            );
            result.Index = wl.Index;

            new PropertyAccessReplacer(
                enumerator, new JSProperty(currentPropertyReference, currentPropertyInfo),
                new JSIndexerExpression(
                    arrayVariable, indexVariable,
                    itemType
                )
            ).Visit(result);

            return result;
        }
Exemple #30
0
 public JSVariableReference(JSVariable referent, MethodReference function)
     : base(referent.Identifier, new ByReferenceType(referent.IdentifierType), function)
 {
     Referent = referent;
 }
 public VariableEliminator (JSVariable variable, JSExpression replacement) {
     Variable = variable;
     Replacement = replacement;
 }
        protected JSVariable DeclareVariable(JSVariable variable)
        {
            JSVariable existing;
            if (Variables.TryGetValue(variable.Identifier, out existing)) {
                if (!TypesAreEqual(variable.Type, existing.Type))
                    throw new InvalidOperationException("A variable with that name is already declared in this scope, with a different type.");
                if (!variable.DefaultValue.Equals(existing.DefaultValue))
                    throw new InvalidOperationException("A variable with that name is already declared in this scope, with a different default value.");

                return existing;
            }

            Variables[variable.Identifier] = variable;

            return variable;
        }
        protected bool IsEffectivelyConstant (JSVariable target, JSExpression source) {
            if ((source == null) || (source.IsNull))
                return false;

            // HACK: Can't eliminate struct temporaries, since that might eliminate some implied copies.
            if (TypeUtil.IsStruct(target.IdentifierType)) {
                // HACK: Allow optimizing out default(T) for structs,
                //  but only if they're referred to once.
                if (source is JSDefaultValueLiteral) {
                    var numUsages = FirstPass.Accesses.Where(a => a.Source == target.Identifier).Count();
                    return numUsages <= 1;
                } else {
                    return false;
                }
            }

            // Handle special cases where our interpretation of 'constant' needs to be more flexible
            {
                var ie = source as JSIndexerExpression;
                if (ie != null) {
                    if (
                        IsEffectivelyConstant(target, ie.Target) &&
                        IsEffectivelyConstant(target, ie.Index)
                    )
                        return true;
                }
            }

            {
                var ae = source as JSArrayExpression;
                if (
                    (ae != null) &&
                    (from av in ae.Values select IsEffectivelyConstant(target, av)).All((b) => b)
                )
                    return true;
            }

            {
                var de = source as JSDotExpressionBase;
                if (
                    (de != null) &&
                    IsEffectivelyConstant(target, de.Target) &&
                    IsEffectivelyConstant(target, de.Member)
                ) {
                    var pa = source as JSPropertyAccess;
                    if (pa != null) {
                        // Property accesses must not be treated as constant since they call functions
                        // TODO: Use static analysis information to figure out whether the accessor is pure/has state dependencies
                        return false;
                    }

                    return true;
                }
            }

            {
                var ie = source as JSInvocationExpression;
                if (
                    (ie != null) && ie.ConstantIfArgumentsAre &&
                    IsEffectivelyConstant(target, ie.ThisReference) &&
                    ie.Arguments.All((a) => IsEffectivelyConstant(target, a))
                )
                    return true;

                if ((ie != null) && (ie.JSMethod != null)) {
                    var sa = GetSecondPass(ie.JSMethod);
                    if (sa != null) {
                        if (sa.IsPure) {
                            if (ie.Arguments.All((a) => IsEffectivelyConstant(target, a)))
                                return true;
                            else
                                return false;
                        }
                    }
                }
            }

            if ((source is JSUnaryOperatorExpression) || (source is JSBinaryOperatorExpression)) {
                if (source.Children.OfType<JSExpression>().All((_v) => IsEffectivelyConstant(target, _v)))
                    return true;
            }

            if (source.IsConstant)
                return true;

            // Try to find a spot between the source variable's assignments where all of our
            //  copies and accesses can fit. If we find one, our variable is effectively constant.
            // FIXME: I think this section might be fundamentally flawed. Do let me know if you agree. :)
            var v = source as JSVariable;
            if (v != null) {
                // Ensure that we never treat a local variable as constant if functions we call allow it to escape
                //  or modify it, because that can completely invalidate our purity analysis.
                if (VariablesExemptedFromEffectivelyConstantStatus.Contains(v.Identifier))
                    return false;

                var sourceAssignments = (from a in FirstPass.Assignments where v.Identifier.Equals(a.Target) select a).ToArray();
                if (sourceAssignments.Length < 1)
                    return v.IsParameter;

                var sourceAccesses = (from a in FirstPass.Accesses where v.Identifier.Equals(a.Source) select a).ToArray();
                if (sourceAccesses.Length < 1)
                    return false;

                var targetAssignmentIndices = (from a in FirstPass.Assignments where target.Identifier.Equals(a.Target) select a.StatementIndex);
                var targetAccessIndices = (from a in FirstPass.Accesses where target.Identifier.Equals(a.Source) select a.StatementIndex).ToArray();
                var targetUseIndices = targetAccessIndices.Concat(targetAssignmentIndices).ToArray();

                if (sourceAssignments.Length == 1) {
                    if (targetAccessIndices.All((tai) => tai > sourceAssignments[0].StatementIndex))
                        return true;
                }

                var sourceFirstAssigned = sourceAssignments.First();
                var sourceLastAssigned = sourceAssignments.Last();

                var sourceFirstAccessed = sourceAccesses.First();
                var sourceLastAccessed = sourceAccesses.Last();

                bool foundAssignmentSlot = false;

                for (int i = 0, c = targetUseIndices.Length; i < c; i++) {
                    int assignment = targetUseIndices[i], nextAssignment = int.MaxValue;
                    if (i < c - 1)
                        nextAssignment = targetUseIndices[i + 1];

                    if (
                        (sourceFirstAssigned.StatementIndex >= assignment) &&
                        (sourceLastAssigned.StatementIndex < nextAssignment) &&
                        (sourceFirstAccessed.StatementIndex >= assignment) &&
                        (sourceLastAccessed.StatementIndex <= nextAssignment)
                    ) {
                        if (TraceLevel >= 5)
                            Console.WriteLine("Found assignment slot for {0} <- {1} between {2} and {3}", target, source, assignment, nextAssignment);

                        foundAssignmentSlot = true;
                        break;
                    }
                }

                if (!foundAssignmentSlot)
                    return false;

                return true;
            }

            return false;
        }
        protected bool IsEffectivelyConstant(JSVariable target, JSExpression source)
        {
            if ((source == null) || (source.IsNull))
                return false;

            // Can't eliminate struct temporaries, since that might eliminate some implied copies.
            if (TypeUtil.IsStruct(target.IdentifierType))
                return false;

            // Handle special cases where our interpretation of 'constant' needs to be more flexible
            {
                var ie = source as JSIndexerExpression;
                if (ie != null) {
                    if (
                        IsEffectivelyConstant(target, ie.Target) &&
                        IsEffectivelyConstant(target, ie.Index)
                    )
                        return true;
                }
            }

            {
                var ae = source as JSArrayExpression;
                if (
                    (ae != null) &&
                    (from av in ae.Values select IsEffectivelyConstant(target, av)).All((b) => b)
                )
                    return true;
            }

            {
                var de = source as JSDotExpressionBase;
                if (
                    (de != null) &&
                    IsEffectivelyConstant(target, de.Target) &&
                    IsEffectivelyConstant(target, de.Member)
                )
                    return true;
            }

            {
                var ie = source as JSInvocationExpression;
                if (
                    (ie != null) && ie.ConstantIfArgumentsAre &&
                    IsEffectivelyConstant(target, ie.ThisReference) &&
                    ie.Arguments.All((a) => IsEffectivelyConstant(target, a))
                )
                    return true;

                if ((ie != null) && (ie.JSMethod != null)) {
                    var sa = GetSecondPass(ie.JSMethod);
                    if (sa != null) {
                        if (sa.IsPure) {
                            if (ie.Arguments.All((a) => IsEffectivelyConstant(target, a)))
                                return true;
                            else
                                return false;
                        }
                    }
                }
            }

            if ((source is JSUnaryOperatorExpression) || (source is JSBinaryOperatorExpression)) {
                if (source.Children.OfType<JSExpression>().All((_v) => IsEffectivelyConstant(target, _v)))
                    return true;
            }

            if (source.IsConstant)
                return true;

            // Try to find a spot between the source variable's assignments where all of our
            //  copies and accesses can fit. If we find one, our variable is effectively constant.
            var v = source as JSVariable;
            if (v != null) {
                var assignments = (from a in FirstPass.Assignments where v.Equals(a.Target) select a).ToArray();
                if (assignments.Length < 1)
                    return v.IsParameter;

                var targetAssignments = (from a in FirstPass.Assignments where v.Equals(a.Target) select a).ToArray();
                if (targetAssignments.Length < 1)
                    return false;

                var targetAccesses = (from a in FirstPass.Accesses where target.Equals(a.Source) select a).ToArray();
                if (targetAccesses.Length < 1)
                    return false;

                var targetFirstAssigned = targetAssignments.FirstOrDefault();
                var targetLastAssigned = targetAssignments.LastOrDefault();

                var targetFirstAccessed = targetAccesses.FirstOrDefault();
                var targetLastAccessed = targetAccesses.LastOrDefault();

                bool foundAssignmentSlot = false;

                for (int i = 0, c = assignments.Length; i < c; i++) {
                    int assignment = assignments[i].StatementIndex, nextAssignment = int.MaxValue;
                    if (i < c - 1)
                        nextAssignment = assignments[i + 1].StatementIndex;

                    if (
                        (targetFirstAssigned.StatementIndex >= assignment) &&
                        (targetFirstAssigned.StatementIndex < nextAssignment) &&
                        (targetFirstAccessed.StatementIndex >= assignment) &&
                        (targetLastAccessed.StatementIndex <= nextAssignment)
                    ) {
                        foundAssignmentSlot = true;
                        break;
                    }
                }

                // If we didn't find a slot, check to see if all the assignments come before all the accesses.
                if (!foundAssignmentSlot) {
                    var minAccessIndex = targetAccesses.Min((a) => a.StatementIndex);

                    if (assignments.All((a) => a.StatementIndex < minAccessIndex))
                        foundAssignmentSlot = true;
                }

                if (!foundAssignmentSlot)
                    return false;

                return true;
            }

            return false;
        }