BranchIfTrue() публичный Метод

Branches to the given label if the value on the top of the stack is non-zero, true or non-null.
public BranchIfTrue ( ILLabel label ) : void
label ILLabel The label to branch to.
Результат void
Пример #1
0
        /// <summary>
        /// Generates CIL for the logical operators.
        /// </summary>
        /// <param name="generator"> The generator to output the CIL to. </param>
        /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param>
        private void GenerateLogical(ILGenerator generator, OptimizationInfo optimizationInfo)
        {
            // Get the statically-determined types of the left and right operands.
            PrimitiveType leftType  = this.Left.ResultType;
            PrimitiveType rightType = this.Right.ResultType;

            // Load the left-hand side operand.
            this.Left.GenerateCode(generator, optimizationInfo);

            // Make sure the output type is consistant.
            if (leftType != rightType)
            {
                if (PrimitiveTypeUtilities.IsNumeric(leftType) == true && PrimitiveTypeUtilities.IsNumeric(rightType) == true)
                {
                    EmitConversion.ToNumber(generator, leftType);
                    leftType = PrimitiveType.Number;
                }
                else
                {
                    EmitConversion.ToAny(generator, leftType);
                    leftType = PrimitiveType.Any;
                }
            }

            // Duplicate and convert to a Boolean.
            generator.Duplicate();
            EmitConversion.ToBool(generator, leftType);

            // Stack contains "left, (bool)left"
            var endOfIf = generator.CreateLabel();

            if (this.OperatorType == OperatorType.LogicalAnd)
            {
                generator.BranchIfFalse(endOfIf);
            }
            else
            {
                generator.BranchIfTrue(endOfIf);
            }

            // Stack contains "left".  Load the right-hand side operand.
            generator.Pop();
            this.Right.GenerateCode(generator, optimizationInfo);

            // Make sure the output type is consistant.
            if (leftType != rightType)
            {
                if (PrimitiveTypeUtilities.IsNumeric(leftType) == true && PrimitiveTypeUtilities.IsNumeric(rightType) == true)
                {
                    EmitConversion.ToNumber(generator, rightType);
                }
                else
                {
                    EmitConversion.ToAny(generator, rightType);
                }
            }

            // Define the label used above.
            generator.DefineLabelPosition(endOfIf);
        }
Пример #2
0
        /// <summary>
        /// Pops the value on the stack, converts it from one type to another, then pushes the
        /// result onto the stack.  Undefined is converted to the given default value.
        /// </summary>
        /// <param name="generator"> The IL generator. </param>
        /// <param name="fromType"> The type to convert from. </param>
        /// <param name="targetParameter"> The type to convert to and the default value, if there is one. </param>
        private static void EmitTypeConversion(ILGenerator generator, Type fromType, BinderArgument argument)
        {
            // Emit either the default value if there is one, otherwise emit "undefined".
            if (argument.HasDefaultValue)
            {
                // Check if the input value is undefined.
                var elseClause = generator.CreateLabel();
                generator.Duplicate();
                generator.BranchIfNull(elseClause);
                generator.Duplicate();
                generator.LoadField(ReflectionHelpers.Undefined_Value);
                generator.CompareEqual();
                generator.BranchIfTrue(elseClause);

                // Convert as per normal.
                EmitTypeConversion(generator, fromType, argument.Type);

                // Jump to the end.
                var endOfIf = generator.CreateLabel();
                generator.Branch(endOfIf);
                generator.DefineLabelPosition(elseClause);

                // Pop the existing value and emit the default value.
                generator.Pop();
                EmitUndefined(generator, argument);

                // Define the end of the block.
                generator.DefineLabelPosition(endOfIf);
            }
            else
            {
                // Convert as per normal.
                EmitTypeConversion(generator, fromType, argument.Type);
            }
        }
Пример #3
0
        /// <summary>
        /// Generates CIL for the in operator.
        /// </summary>
        /// <param name="generator"> The generator to output the CIL to. </param>
        /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param>
        private void GenerateIn(ILGenerator generator, OptimizationInfo optimizationInfo)
        {
            // Emit the left-hand side expression and convert it to a string.
            this.Left.GenerateCode(generator, optimizationInfo);
            EmitConversion.ToString(generator, this.Left.ResultType);

            // Store the left-hand side expression in a temporary variable.
            var temp = generator.CreateTemporaryVariable(typeof(string));

            generator.StoreVariable(temp);

            // Emit the right-hand side expression.
            this.Right.GenerateCode(generator, optimizationInfo);
            EmitConversion.ToAny(generator, this.Right.ResultType);

            // Check the right-hand side is a javascript object - if not, throw an exception.
            generator.Duplicate();
            generator.IsInstance(typeof(ObjectInstance));
            var endOfTypeCheck = generator.CreateLabel();

            generator.BranchIfTrue(endOfTypeCheck);

            // Throw an nicely formatted exception.
            var rightValue = generator.CreateTemporaryVariable(typeof(object));

            generator.StoreVariable(rightValue);
            generator.LoadEnumValue(ErrorType.TypeError);
            generator.LoadString("The in operator expected an object, but found '{0}' instead");
            generator.LoadInt32(1);
            generator.NewArray(typeof(object));
            generator.Duplicate();
            generator.LoadInt32(0);
            generator.LoadVariable(rightValue);
            generator.Call(ReflectionHelpers.TypeUtilities_TypeOf);
            generator.StoreArrayElement(typeof(object));
            generator.Call(ReflectionHelpers.String_Format);
            generator.LoadInt32(optimizationInfo.SourceSpan.StartLine);
            generator.LoadStringOrNull(optimizationInfo.Source.Path);
            generator.LoadStringOrNull(optimizationInfo.FunctionName);
            generator.NewObject(ReflectionHelpers.JavaScriptException_Constructor_Error);
            generator.Throw();
            generator.DefineLabelPosition(endOfTypeCheck);
            generator.ReleaseTemporaryVariable(rightValue);
            generator.ReinterpretCast(typeof(ObjectInstance));

            // Load the left-hand side expression from the temporary variable.
            generator.LoadVariable(temp);

            // Call ObjectInstance.HasProperty(object)
            generator.Call(ReflectionHelpers.ObjectInstance_HasProperty);

            // Allow the temporary variable to be reused.
            generator.ReleaseTemporaryVariable(temp);
        }
        /// <summary>
        /// Generates CIL for the statement.
        /// </summary>
        /// <param name="generator"> The generator to output the CIL to. </param>
        /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param>
        public override void GenerateCode(ILGenerator generator, OptimizationInfo optimizationInfo)
        {
            // Generate code for the start of the statement.
            var statementLocals = new StatementLocals() { NonDefaultSourceSpanBehavior = true };
            GenerateStartOfStatement(generator, optimizationInfo, statementLocals);

            // Unlike in .NET, in javascript there are no restrictions on what can appear inside
            // try, catch and finally blocks.  The one restriction which causes problems is the
            // inability to jump out of .NET finally blocks.  This is required when break, continue
            // or return statements appear inside of a finally block.  To work around this, when
            // inside a finally block these instructions throw an exception instead.

            // Setting the InsideTryCatchOrFinally flag converts BR instructions into LEAVE
            // instructions so that the finally block is executed correctly.
            var previousInsideTryCatchOrFinally = optimizationInfo.InsideTryCatchOrFinally;
            optimizationInfo.InsideTryCatchOrFinally = true;

            // Finally requires two exception nested blocks.
            if (this.FinallyBlock != null)
                generator.BeginExceptionBlock();

            // Begin the exception block.
            generator.BeginExceptionBlock();

            // Generate code for the try block.
            this.TryBlock.GenerateCode(generator, optimizationInfo);

            // Generate code for the catch block.
            ILLocalVariable skipFinallyBlock = null;
           
            
            // Begin a catch block.  The exception is on the top of the stack.
            generator.BeginCatchBlock(typeof(Exception));

            // Check the exception is catchable by calling CanCatchException(ex).
            // We need to handle the case where JS code calls into .NET code which then throws
            // a JavaScriptException from a different ScriptEngine.
            // If CatchBlock is null, we need to rethrow the exception in every case.
            var endOfIfLabel = generator.CreateLabel();
            generator.Duplicate();  // ex
            var exceptionTemporary = generator.CreateTemporaryVariable(typeof(Exception));
            generator.StoreVariable(exceptionTemporary);
            EmitHelpers.LoadScriptEngine(generator);
            generator.LoadVariable(exceptionTemporary);
            generator.ReleaseTemporaryVariable(exceptionTemporary);
            generator.Call(ReflectionHelpers.ScriptEngine_CanCatchException);
            generator.BranchIfTrue(endOfIfLabel);
            if (this.FinallyBlock != null)
            {
                generator.LoadBoolean(true);
                skipFinallyBlock = generator.DeclareVariable(typeof(bool), "skipFinallyBlock");
                generator.StoreVariable(skipFinallyBlock);
            }
            if (this.CatchBlock == null)
                generator.DefineLabelPosition(endOfIfLabel);
            generator.Rethrow();
            if (this.CatchBlock != null)
                generator.DefineLabelPosition(endOfIfLabel);

            if (this.CatchBlock != null) {
                // Create a new DeclarativeScope.
                this.CatchScope.GenerateScopeCreation(generator, optimizationInfo);

                // Store the error object in the variable provided.
                generator.Call(ReflectionHelpers.JavaScriptException_ErrorObject);
                var catchVariable = new NameExpression(this.CatchScope, this.CatchVariableName);
                catchVariable.GenerateSet(generator, optimizationInfo, PrimitiveType.Any, false);

                // Make sure the scope is reverted even if an exception is thrown.
                generator.BeginExceptionBlock();

                // Emit code for the statements within the catch block.
                this.CatchBlock.GenerateCode(generator, optimizationInfo);

                // Revert the scope.
                generator.BeginFinallyBlock();
                this.CatchScope.GenerateScopeDestruction(generator, optimizationInfo);
                generator.EndExceptionBlock();
            }

            // Generate code for the finally block.
            if (this.FinallyBlock != null)
            {
                generator.BeginFinallyBlock();

                // If an exception was thrown that wasn't handled by the catch block, then don't
                // run the finally block either.  This prevents user code from being run when a
                // ThreadAbortException is thrown.
                var endOfFinallyBlock = generator.CreateLabel();                
                generator.LoadVariable(skipFinallyBlock);
                generator.BranchIfTrue(endOfFinallyBlock);

                var branches = new List<ILLabel>();
                var previousStackSize = optimizationInfo.LongJumpStackSizeThreshold;
                optimizationInfo.LongJumpStackSizeThreshold = optimizationInfo.BreakOrContinueStackSize;
                var previousCallback = optimizationInfo.LongJumpCallback;
                optimizationInfo.LongJumpCallback = (generator2, label) =>
                    {
                        // It is not possible to branch out of a finally block - therefore instead of
                        // generating LEAVE instructions we throw an exception then catch it to transfer
                        // control out of the finally block.
                        generator2.LoadInt32(branches.Count);
                        generator2.NewObject(ReflectionHelpers.LongJumpException_Constructor);
                        generator2.Throw();

                        // Record any branches that are made within the finally code.
                        branches.Add(label);
                    };

                // Emit code for the finally block.
                this.FinallyBlock.GenerateCode(generator, optimizationInfo);

                // Define the position at the end of the finally block.
                generator.DefineLabelPosition(endOfFinallyBlock);

                // End the main exception block.
                generator.EndExceptionBlock();

                // Begin a catch block to catch any LongJumpExceptions. The exception object is on
                // the top of the stack.
                generator.BeginCatchBlock(typeof(LongJumpException));

                if (branches.Count > 0)
                {
                    // switch (exception.RouteID)
                    // {
                    //    case 0: goto label1;
                    //    case 1: goto label2;
                    // }
                    ILLabel[] switchLabels = new ILLabel[branches.Count];
                    for (int i = 0; i < branches.Count; i++)
                        switchLabels[i] = generator.CreateLabel();
                    generator.Call(ReflectionHelpers.LongJumpException_RouteID);
                    generator.Switch(switchLabels);
                    for (int i = 0; i < branches.Count; i++)
                    {
                        generator.DefineLabelPosition(switchLabels[i]);
                        generator.Leave(branches[i]);
                    }
                }

                // Reset the state we clobbered.
                optimizationInfo.LongJumpStackSizeThreshold = previousStackSize;
                optimizationInfo.LongJumpCallback = previousCallback;
            }

            // End the exception block.
            generator.EndExceptionBlock();

            // Reset the InsideTryCatchOrFinally flag.
            optimizationInfo.InsideTryCatchOrFinally = previousInsideTryCatchOrFinally;

            // Generate code for the end of the statement.
            GenerateEndOfStatement(generator, optimizationInfo, statementLocals);
        }
Пример #5
0
        /// <summary>
        /// Generates code to push the "this" value for a function call.
        /// </summary>
        /// <param name="generator"> The generator to output the CIL to. </param>
        public void GenerateThis(ILGenerator generator)
        {
            // Optimization: if there are no with scopes, simply emit undefined.
            bool scopeChainHasWithScope = false;
            var  scope = this.Scope;

            do
            {
                if (scope is ObjectScope && ((ObjectScope)scope).ProvidesImplicitThisValue == true)
                {
                    scopeChainHasWithScope = true;
                    break;
                }
                scope = scope.ParentScope;
            } while (scope != null);

            if (scopeChainHasWithScope == false)
            {
                // No with scopes in the scope chain, use undefined as the "this" value.
                EmitHelpers.EmitUndefined(generator);
                return;
            }

            var end = generator.CreateLabel();

            scope = this.Scope;
            ILLocalVariable scopeVariable = generator.CreateTemporaryVariable(typeof(Scope));

            EmitHelpers.LoadScope(generator);
            generator.StoreVariable(scopeVariable);

            do
            {
                if (scope is DeclarativeScope)
                {
                    if (scope.HasDeclaredVariable(this.Name))
                    {
                        // The variable exists but declarative scopes always produce undefined for
                        // the "this" value.
                        EmitHelpers.EmitUndefined(generator);
                        break;
                    }
                }
                else
                {
                    var objectScope = (ObjectScope)scope;

                    // Check if the property exists by calling scope.ScopeObject.HasProperty(propertyName)
                    if (objectScope.ProvidesImplicitThisValue == false)
                    {
                        EmitHelpers.EmitUndefined(generator);
                    }
                    generator.LoadVariable(scopeVariable);
                    generator.CastClass(typeof(ObjectScope));
                    generator.Call(ReflectionHelpers.ObjectScope_ScopeObject);
                    if (objectScope.ProvidesImplicitThisValue == true)
                    {
                        generator.Duplicate();
                    }
                    generator.LoadString(this.Name);
                    generator.Call(ReflectionHelpers.ObjectInstance_HasProperty);
                    generator.BranchIfTrue(end);
                    generator.Pop();

                    // If the name is not defined, use undefined for the "this" value.
                    if (scope.ParentScope == null)
                    {
                        EmitHelpers.EmitUndefined(generator);
                    }
                }

                // Try the parent scope.
                if (scope.ParentScope != null && scope.ExistsAtRuntime == true)
                {
                    generator.LoadVariable(scopeVariable);
                    generator.Call(ReflectionHelpers.Scope_ParentScope);
                    generator.StoreVariable(scopeVariable);
                }
                scope = scope.ParentScope;
            } while (scope != null);

            // Release the temporary variable.
            generator.ReleaseTemporaryVariable(scopeVariable);

            // Define a label at the end.
            generator.DefineLabelPosition(end);
        }
Пример #6
0
        /// <summary>
        /// Stores the value on the top of the stack in the reference.
        /// </summary>
        /// <param name="generator"> The generator to output the CIL to. </param>
        /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param>
        /// <param name="valueType"> The primitive type of the value that is on the top of the stack. </param>
        /// <param name="throwIfUnresolvable"> <c>true</c> to throw a ReferenceError exception if
        /// the name is unresolvable; <c>false</c> to create a new property instead. </param>
        public void GenerateSet(ILGenerator generator, OptimizationInfo optimizationInfo, PrimitiveType valueType, bool throwIfUnresolvable)
        {
            // The value is initially on the top of the stack but is stored in this variable
            // at the last possible moment.
            ILLocalVariable value = null;

            var             scope         = this.Scope;
            ILLocalVariable scopeVariable = null;
            var             endOfSet      = generator.CreateLabel();

            do
            {
                if (scope is DeclarativeScope)
                {
                    // Get information about the variable.
                    var variable = scope.GetDeclaredVariable(this.Name);
                    if (variable != null)
                    {
                        // The variable was declared in this scope.

                        if (scope.ExistsAtRuntime == false)
                        {
                            // The scope has been optimized away.  The value of the variable is stored
                            // in an ILVariable.

                            // Declare an IL local variable if no storage location has been allocated yet.
                            if (variable.Store == null)
                            {
                                variable.Store = generator.DeclareVariable(typeof(object), variable.Name);
                            }

                            if (value == null)
                            {
                                // The value to store is on the top of the stack - convert it to the
                                // storage type of the variable.
                                EmitConversion.Convert(generator, valueType, variable.Type, optimizationInfo);
                            }
                            else
                            {
                                // The value to store is in a temporary variable.
                                generator.LoadVariable(value);
                                EmitConversion.Convert(generator, PrimitiveType.Any, variable.Type, optimizationInfo);
                            }

                            // Store the value in the variable.
                            generator.StoreVariable(variable.Store);
                        }
                        else if (variable.Writable == true)
                        {
                            if (value == null)
                            {
                                // The value to store is on the top of the stack - convert it to an
                                // object and store it in a temporary variable.
                                EmitConversion.Convert(generator, valueType, PrimitiveType.Any, optimizationInfo);
                                value = generator.CreateTemporaryVariable(typeof(object));
                                generator.StoreVariable(value);
                            }

                            // scope.Values[index] = value
                            if (scopeVariable == null)
                            {
                                EmitHelpers.LoadScope(generator);
                            }
                            else
                            {
                                generator.LoadVariable(scopeVariable);
                            }
                            generator.CastClass(typeof(DeclarativeScope));
                            generator.Call(ReflectionHelpers.DeclarativeScope_Values);
                            generator.LoadInt32(variable.Index);
                            generator.LoadVariable(value);
                            generator.StoreArrayElement(typeof(object));
                        }
                        else
                        {
                            // The variable exists, but is read-only.
                            // Pop the value off the stack (if it is still there).
                            if (value == null)
                            {
                                generator.Pop();
                            }
                        }

                        // The variable was found - no need to search any more parent scopes.
                        break;
                    }
                    else
                    {
                        // The variable was not defined at compile time, but may have been
                        // introduced by an eval() statement.
                        if (optimizationInfo.MethodOptimizationHints.HasEval == true)
                        {
                            if (value == null)
                            {
                                // The value to store is on the top of the stack - convert it to an
                                // object and store it in a temporary variable.
                                EmitConversion.Convert(generator, valueType, PrimitiveType.Any, optimizationInfo);
                                value = generator.CreateTemporaryVariable(typeof(object));
                                generator.StoreVariable(value);
                            }

                            // Check the variable exists: if (scope.HasValue(variableName) == true) {
                            if (scopeVariable == null)
                            {
                                EmitHelpers.LoadScope(generator);
                            }
                            else
                            {
                                generator.LoadVariable(scopeVariable);
                            }
                            generator.CastClass(typeof(DeclarativeScope));
                            generator.LoadString(this.Name);
                            generator.Call(ReflectionHelpers.Scope_HasValue);
                            var hasValueClause = generator.CreateLabel();
                            generator.BranchIfFalse(hasValueClause);

                            // Set the value of the variable.
                            if (scopeVariable == null)
                            {
                                EmitHelpers.LoadScope(generator);
                            }
                            else
                            {
                                generator.LoadVariable(scopeVariable);
                            }
                            generator.CastClass(typeof(DeclarativeScope));
                            generator.LoadString(this.Name);
                            generator.LoadVariable(value);
                            generator.Call(ReflectionHelpers.Scope_SetValue);
                            generator.Branch(endOfSet);

                            // }
                            generator.DefineLabelPosition(hasValueClause);
                        }
                    }
                }
                else
                {
                    if (value == null)
                    {
                        // The value to store is on the top of the stack - convert it to an
                        // object and store it in a temporary variable.
                        EmitConversion.Convert(generator, valueType, PrimitiveType.Any, optimizationInfo);
                        value = generator.CreateTemporaryVariable(typeof(object));
                        generator.StoreVariable(value);
                    }

                    if (scope.ParentScope == null)
                    {
                        // Optimization: if this is the global scope, use hidden classes to
                        // optimize variable access.

                        // Global variable modification
                        // ----------------------------
                        // __object_cacheKey = null;
                        // __object_property_cachedIndex = 0;
                        // ...
                        // if (__object_cacheKey != object.InlineCacheKey)
                        //     object.InlineSetPropertyValueIfExists("property", value, strictMode, out __object_property_cachedIndex, out __object_cacheKey)
                        // else
                        //     object.InlinePropertyValues[__object_property_cachedIndex] = value;

                        // Get a reference to the global object.
                        if (scopeVariable == null)
                        {
                            EmitHelpers.LoadScope(generator);
                        }
                        else
                        {
                            generator.LoadVariable(scopeVariable);
                        }
                        generator.CastClass(typeof(ObjectScope));
                        generator.Call(ReflectionHelpers.ObjectScope_ScopeObject);

                        // TODO: share these variables somehow.
                        var cacheKey    = generator.DeclareVariable(typeof(object));
                        var cachedIndex = generator.DeclareVariable(typeof(int));

                        // Store the object into a temp variable.
                        var objectInstance = generator.DeclareVariable(PrimitiveType.Object);
                        generator.StoreVariable(objectInstance);

                        // if (__object_cacheKey != object.InlineCacheKey)
                        generator.LoadVariable(cacheKey);
                        generator.LoadVariable(objectInstance);
                        generator.Call(ReflectionHelpers.ObjectInstance_InlineCacheKey);
                        var elseClause = generator.CreateLabel();
                        generator.BranchIfEqual(elseClause);

                        // xxx = object.InlineSetPropertyValueIfExists("property", value, strictMode, out __object_property_cachedIndex, out __object_cacheKey)
                        generator.LoadVariable(objectInstance);
                        generator.LoadString(this.Name);
                        generator.LoadVariable(value);
                        generator.LoadBoolean(optimizationInfo.StrictMode);
                        generator.LoadAddressOfVariable(cachedIndex);
                        generator.LoadAddressOfVariable(cacheKey);
                        if (throwIfUnresolvable == false)
                        {
                            // Set the property value unconditionally.
                            generator.Call(ReflectionHelpers.ObjectInstance_InlineSetPropertyValue);
                        }
                        else
                        {
                            // Set the property value if the property exists.
                            generator.Call(ReflectionHelpers.ObjectInstance_InlineSetPropertyValueIfExists);

                            // The return value is true if the property was defined, and false if it wasn't.
                            generator.BranchIfTrue(endOfSet);
                        }

                        var endOfIf = generator.CreateLabel();
                        generator.Branch(endOfIf);

                        // else
                        generator.DefineLabelPosition(elseClause);

                        // object.InlinePropertyValues[__object_property_cachedIndex] = value;
                        generator.LoadVariable(objectInstance);
                        generator.Call(ReflectionHelpers.ObjectInstance_InlinePropertyValues);
                        generator.LoadVariable(cachedIndex);
                        generator.LoadVariable(value);
                        generator.StoreArrayElement(typeof(object));
                        generator.Branch(endOfSet);

                        // End of the if statement
                        generator.DefineLabelPosition(endOfIf);
                    }
                    else
                    {
                        // Slow route.

                        if (scopeVariable == null)
                        {
                            EmitHelpers.LoadScope(generator);
                        }
                        else
                        {
                            generator.LoadVariable(scopeVariable);
                        }
                        generator.CastClass(typeof(ObjectScope));
                        generator.Call(ReflectionHelpers.ObjectScope_ScopeObject);
                        generator.LoadString(this.Name);
                        generator.LoadVariable(value);
                        generator.LoadBoolean(optimizationInfo.StrictMode);

                        if (scope.ParentScope == null && throwIfUnresolvable == false)
                        {
                            // Set the property value unconditionally.
                            generator.Call(ReflectionHelpers.ObjectInstance_SetPropertyValue_Object);
                        }
                        else
                        {
                            // Set the property value if the property exists.
                            generator.Call(ReflectionHelpers.ObjectInstance_SetPropertyValueIfExists);

                            // The return value is true if the property was defined, and false if it wasn't.
                            generator.BranchIfTrue(endOfSet);
                        }
                    }
                }

                // Try the parent scope.
                if (scope.ParentScope != null && scope.ExistsAtRuntime == true)
                {
                    if (scopeVariable == null)
                    {
                        scopeVariable = generator.CreateTemporaryVariable(typeof(Scope));
                        EmitHelpers.LoadScope(generator);
                    }
                    else
                    {
                        generator.LoadVariable(scopeVariable);
                    }
                    generator.Call(ReflectionHelpers.Scope_ParentScope);
                    generator.StoreVariable(scopeVariable);
                }
                scope = scope.ParentScope;
            } while (scope != null);

            // The value might be still on top of the stack.
            if (value == null && scope == null)
            {
                generator.Pop();
            }

            // Throw an error if the name does not exist and throwIfUnresolvable is true.
            if (scope == null && throwIfUnresolvable == true)
            {
                EmitHelpers.EmitThrow(generator, ErrorType.ReferenceError, this.Name + " is not defined", optimizationInfo);
            }

            // Release the temporary variables.
            if (value != null)
            {
                generator.ReleaseTemporaryVariable(value);
            }
            if (scopeVariable != null)
            {
                generator.ReleaseTemporaryVariable(scopeVariable);
            }

            // Define a label at the end.
            generator.DefineLabelPosition(endOfSet);
        }
Пример #7
0
        /// <summary>
        /// Generates CIL for the statement.
        /// </summary>
        /// <param name="generator"> The generator to output the CIL to. </param>
        /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param>
        public override void GenerateCode(ILGenerator generator, OptimizationInfo optimizationInfo)
        {
            // Generate code for the start of the statement.
            var statementLocals = new StatementLocals()
            {
                NonDefaultBreakStatementBehavior = true
            };

            GenerateStartOfStatement(generator, optimizationInfo, statementLocals);

            // We need a label for each case clause and one for the default case.
            var     jumpTargets  = new ILLabel[this.CaseClauses.Count];
            int     defaultIndex = -1;
            ILLabel endOfSwitch  = generator.CreateLabel();

            // Generate code for the switch value.
            var startOfSwitch = generator.CreateLabel();

            this.Value.GenerateCode(generator, optimizationInfo);
            EmitConversion.ToAny(generator, this.Value.ResultType);

            // Save the switch value in a variable.
            var switchValue = generator.CreateTemporaryVariable(typeof(object));

            generator.StoreVariable(switchValue);

            for (int i = 0; i < this.CaseClauses.Count; i++)
            {
                var caseClause = this.CaseClauses[i];

                // Create a label for each clause.
                jumpTargets[i] = generator.CreateLabel();

                if (caseClause.Value == null)
                {
                    // This is a default clause.
                    defaultIndex = i;
                    continue;
                }

                // TypeComparer.StrictEquals(switchValue, caseValue)
                generator.LoadVariable(switchValue);
                caseClause.Value.GenerateCode(generator, optimizationInfo);
                EmitConversion.ToAny(generator, caseClause.Value.ResultType);
                generator.Call(ReflectionHelpers.TypeComparer_StrictEquals);

                // if (TypeComparer.StrictEquals(switchValue, caseValue) == true)
                //     goto case i
                generator.BranchIfTrue(jumpTargets[i]);
            }

            // None of the cases matched, jump to the default clause or the end of the switch.
            if (defaultIndex >= 0)
            {
                generator.Branch(jumpTargets[defaultIndex]);
            }
            else
            {
                generator.Branch(endOfSwitch);
            }

            for (int i = 0; i < this.CaseClauses.Count; i++)
            {
                // Define a label at the start of the case clause.
                generator.DefineLabelPosition(jumpTargets[i]);

                // Set up the information needed by the break statement.
                optimizationInfo.PushBreakOrContinueInfo(this.Labels, endOfSwitch, null, labelledOnly: false);

                // Emit the case clause statements.
                foreach (var statement in this.CaseClauses[i].BodyStatements)
                {
                    statement.GenerateCode(generator, optimizationInfo);
                }

                // Revert the information needed by the break statement.
                optimizationInfo.PopBreakOrContinueInfo();
            }

            // Define a label for the end of the switch statement.
            generator.DefineLabelPosition(endOfSwitch);

            // Release the switch value variable for use elsewhere.
            generator.ReleaseTemporaryVariable(switchValue);

            // Generate code for the end of the statement.
            GenerateEndOfStatement(generator, optimizationInfo, statementLocals);
        }
        /// <summary>
        /// Generates CIL for the logical operators.
        /// </summary>
        /// <param name="generator"> The generator to output the CIL to. </param>
        /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param>
        private void GenerateLogical(ILGenerator generator, OptimizationInfo optimizationInfo)
        {
            // Get the statically-determined types of the left and right operands.
            PrimitiveType leftType = this.Left.ResultType;
            PrimitiveType rightType = this.Right.ResultType;

            // Load the left-hand side operand.
            this.Left.GenerateCode(generator, optimizationInfo);

            // Make sure the output type is consistant.
            if (leftType != rightType)
            {
                if (PrimitiveTypeUtilities.IsNumeric(leftType) == true && PrimitiveTypeUtilities.IsNumeric(rightType) == true)
                {
                    EmitConversion.ToNumber(generator, leftType);
                    leftType = PrimitiveType.Number;
                }
                else
                {
                    EmitConversion.ToAny(generator, leftType);
                    leftType = PrimitiveType.Any;
                }
            }

            // Duplicate and convert to a Boolean.
            generator.Duplicate();
            EmitConversion.ToBool(generator, leftType);

            // Stack contains "left, (bool)left"
            var endOfIf = generator.CreateLabel();
            if (this.OperatorType == OperatorType.LogicalAnd)
                generator.BranchIfFalse(endOfIf);
            else
                generator.BranchIfTrue(endOfIf);

            // Stack contains "left".  Load the right-hand side operand.
            generator.Pop();
            this.Right.GenerateCode(generator, optimizationInfo);

            // Make sure the output type is consistant.
            if (leftType != rightType)
            {
                if (PrimitiveTypeUtilities.IsNumeric(leftType) == true && PrimitiveTypeUtilities.IsNumeric(rightType) == true)
                    EmitConversion.ToNumber(generator, rightType);
                else
                    EmitConversion.ToAny(generator, rightType);
            }

            // Define the label used above.
            generator.DefineLabelPosition(endOfIf);
        }
        /// <summary>
        /// Generates CIL for the expression.
        /// </summary>
        /// <param name="generator"> The generator to output the CIL to. </param>
        /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param>
        public override void GenerateCode(ILGenerator generator, OptimizationInfo optimizationInfo)
        {
            // Check if this is a direct call to eval().
            if (this.Target is NameExpression && ((NameExpression)this.Target).Name == "eval")
            {
                GenerateEval(generator, optimizationInfo);
                return;
            }

            // Check if this is a super() call.
            if (this.Target is SuperExpression)
            {
                // executionContext.CallSuperClass(arguments)
                EmitHelpers.LoadExecutionContext(generator);
                GenerateArgumentsArray(generator, optimizationInfo);
                generator.Call(ReflectionHelpers.ExecutionContext_CallSuperClass);
                return;
            }

            // Emit the function instance first.
            ILLocalVariable targetBase = null;

            if (this.Target is MemberAccessExpression)
            {
                // The function is a member access expression (e.g. "Math.cos()").

                // Evaluate the left part of the member access expression.
                var baseExpression = ((MemberAccessExpression)this.Target).Base;
                baseExpression.GenerateCode(generator, optimizationInfo);
                EmitConversion.ToAny(generator, baseExpression.ResultType);
                targetBase = generator.CreateTemporaryVariable(typeof(object));
                generator.StoreVariable(targetBase);

                // Evaluate the right part of the member access expression.
                var memberAccessExpression = new MemberAccessExpression(((MemberAccessExpression)this.Target).Operator);
                memberAccessExpression.Push(new TemporaryVariableExpression(targetBase));
                memberAccessExpression.Push(((MemberAccessExpression)this.Target).GetOperand(1));
                memberAccessExpression.GenerateCode(generator, optimizationInfo);
                EmitConversion.ToAny(generator, this.Target.ResultType);
            }
            else
            {
                // Something else (e.g. "my_func()").
                this.Target.GenerateCode(generator, optimizationInfo);
                EmitConversion.ToAny(generator, this.Target.ResultType);
            }

            // Check the object really is a function - if not, throw an exception.
            generator.Duplicate();
            generator.IsInstance(typeof(FunctionInstance));
            var endOfTypeCheck = generator.CreateLabel();

            generator.BranchIfTrue(endOfTypeCheck);

            // Throw an nicely formatted exception.
            generator.Pop();
            EmitHelpers.EmitThrow(generator, ErrorType.TypeError, string.Format("'{0}' is not a function", this.Target.ToString()), optimizationInfo);
            generator.DefineLabelPosition(endOfTypeCheck);

            // Pass in the path, function name and line.
            generator.ReinterpretCast(typeof(FunctionInstance));
            generator.LoadStringOrNull(optimizationInfo.Source.Path);
            generator.LoadStringOrNull(optimizationInfo.FunctionName);
            generator.LoadInt32(optimizationInfo.SourceSpan.StartLine);

            // Generate code to produce the "this" value.  There are three cases.
            if (this.Target is NameExpression)
            {
                // 1. The function is a name expression (e.g. "parseInt()").
                //    If we are inside a with() block, then there is an implicit 'this' value,
                //    otherwise 'this' is undefined.
                Scope.GenerateReference(generator, optimizationInfo);
                generator.Call(ReflectionHelpers.RuntimeScope_ImplicitThis);
            }
            else if (this.Target is MemberAccessExpression targetMemberAccessExpression)
            {
                // 2. The function is a member access expression (e.g. "Math.cos()").
                //    In this case this = Math.
                //    Unless it's a super call like super.blah().
                if (targetMemberAccessExpression.Base is SuperExpression)
                {
                    EmitHelpers.LoadThis(generator);
                }
                else
                {
                    generator.LoadVariable(targetBase);
                }
            }
            else
            {
                // 3. Neither of the above (e.g. "(function() { return 5 })()")
                //    In this case this = undefined.
                EmitHelpers.EmitUndefined(generator);
            }

            // Emit an array containing the function arguments.
            GenerateArgumentsArray(generator, optimizationInfo);

            // Call FunctionInstance.CallLateBound(thisValue, argumentValues)
            generator.Call(ReflectionHelpers.FunctionInstance_CallWithStackTrace);

            // Allow reuse of the temporary variable.
            if (targetBase != null)
            {
                generator.ReleaseTemporaryVariable(targetBase);
            }
        }
Пример #10
0
        /// <summary>
        /// Pops the value on the stack, converts it from one type to another, then pushes the
        /// result onto the stack.  Undefined is converted to the given default value.
        /// </summary>
        /// <param name="generator"> The IL generator. </param>
        /// <param name="fromType"> The type to convert from. </param>
        /// <param name="targetParameter"> The type to convert to and the default value, if there is one. </param>
        private static void EmitTypeConversion(ILGenerator generator, Type fromType, BinderArgument argument)
        {
            // Emit either the default value if there is one, otherwise emit "undefined".
            if (argument.HasDefaultValue)
            {
                // Check if the input value is undefined.
                var elseClause = generator.CreateLabel();
                generator.Duplicate();
                generator.BranchIfNull(elseClause);
                generator.Duplicate();
                generator.LoadField(ReflectionHelpers.Undefined_Value);
                generator.CompareEqual();
                generator.BranchIfTrue(elseClause);

                // Convert as per normal.
                EmitTypeConversion(generator, fromType, argument.Type);

                // Jump to the end.
                var endOfIf = generator.CreateLabel();
                generator.Branch(endOfIf);
                generator.DefineLabelPosition(elseClause);

                // Pop the existing value and emit the default value.
                generator.Pop();
                EmitUndefined(generator, argument);

                // Define the end of the block.
                generator.DefineLabelPosition(endOfIf);
            }
            else
            {
                // Convert as per normal.
                EmitTypeConversion(generator, fromType, argument.Type);
            }
        }
Пример #11
0
        /// <summary>
        /// Stores the value on the top of the stack in the reference.
        /// </summary>
        /// <param name="generator"> The generator to output the CIL to. </param>
        /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param>
        /// <param name="valueType"> The primitive type of the value that is on the top of the stack. </param>
        /// <param name="throwIfUnresolvable"> <c>true</c> to throw a ReferenceError exception if
        /// the name is unresolvable; <c>false</c> to create a new property instead. </param>
        public void GenerateSet(ILGenerator generator, OptimizationInfo optimizationInfo, PrimitiveType valueType, bool throwIfUnresolvable)
        {
            // The value is initially on the top of the stack but is stored in this variable
            // at the last possible moment.
            ILLocalVariable value = null;

            var             scope         = this.Scope;
            ILLocalVariable scopeVariable = null;
            var             endOfSet      = generator.CreateLabel();

            do
            {
                if (scope is DeclarativeScope)
                {
                    // Get information about the variable.
                    var variable = scope.GetDeclaredVariable(this.Name);
                    if (variable != null)
                    {
                        // The variable was declared in this scope.

                        if (scope.ExistsAtRuntime == false)
                        {
                            // The scope has been optimized away.  The value of the variable is stored
                            // in an ILVariable.

                            // Declare an IL local variable if no storage location has been allocated yet.
                            if (variable.Store == null)
                            {
                                variable.Store = generator.DeclareVariable(typeof(object), variable.Name);
                            }

                            if (value == null)
                            {
                                // The value to store is on the top of the stack - convert it to the
                                // storage type of the variable.
                                EmitConversion.Convert(generator, valueType, variable.Type, optimizationInfo);
                            }
                            else
                            {
                                // The value to store is in a temporary variable.
                                generator.LoadVariable(value);
                                EmitConversion.Convert(generator, PrimitiveType.Any, variable.Type, optimizationInfo);
                            }

                            // Store the value in the variable.
                            generator.StoreVariable(variable.Store);
                        }
                        else if (variable.Writable == true)
                        {
                            if (value == null)
                            {
                                // The value to store is on the top of the stack - convert it to an
                                // object and store it in a temporary variable.
                                EmitConversion.Convert(generator, valueType, PrimitiveType.Any, optimizationInfo);
                                value = generator.CreateTemporaryVariable(typeof(object));
                                generator.StoreVariable(value);
                            }

                            // scope.Values[index] = value
                            if (scopeVariable == null)
                            {
                                EmitHelpers.LoadScope(generator);
                            }
                            else
                            {
                                generator.LoadVariable(scopeVariable);
                            }
                            generator.CastClass(typeof(DeclarativeScope));
                            generator.Call(ReflectionHelpers.DeclarativeScope_Values);
                            generator.LoadInt32(variable.Index);
                            generator.LoadVariable(value);
                            generator.StoreArrayElement(typeof(object));
                        }
                        else
                        {
                            // The variable exists, but is read-only.
                            // Pop the value off the stack (if it is still there).
                            if (value == null)
                            {
                                generator.Pop();
                            }
                        }

                        // The variable was found - no need to search any more parent scopes.
                        break;
                    }
                    else
                    {
                        // The variable was not defined at compile time, but may have been
                        // introduced by an eval() statement.
                        if (optimizationInfo.MethodOptimizationHints.HasEval == true)
                        {
                            if (value == null)
                            {
                                // The value to store is on the top of the stack - convert it to an
                                // object and store it in a temporary variable.
                                EmitConversion.Convert(generator, valueType, PrimitiveType.Any, optimizationInfo);
                                value = generator.CreateTemporaryVariable(typeof(object));
                                generator.StoreVariable(value);
                            }

                            // Check the variable exists: if (scope.HasValue(variableName) == true) {
                            if (scopeVariable == null)
                            {
                                EmitHelpers.LoadScope(generator);
                            }
                            else
                            {
                                generator.LoadVariable(scopeVariable);
                            }
                            generator.CastClass(typeof(DeclarativeScope));
                            generator.LoadString(this.Name);
                            generator.Call(ReflectionHelpers.Scope_HasValue);
                            var hasValueClause = generator.CreateLabel();
                            generator.BranchIfFalse(hasValueClause);

                            // Set the value of the variable.
                            if (scopeVariable == null)
                            {
                                EmitHelpers.LoadScope(generator);
                            }
                            else
                            {
                                generator.LoadVariable(scopeVariable);
                            }
                            generator.CastClass(typeof(DeclarativeScope));
                            generator.LoadString(this.Name);
                            generator.LoadVariable(value);
                            generator.Call(ReflectionHelpers.Scope_SetValue);
                            generator.Branch(endOfSet);

                            // }
                            generator.DefineLabelPosition(hasValueClause);
                        }
                    }
                }
                else
                {
                    if (value == null)
                    {
                        // The value to store is on the top of the stack - convert it to an
                        // object and store it in a temporary variable.
                        EmitConversion.Convert(generator, valueType, PrimitiveType.Any, optimizationInfo);
                        value = generator.CreateTemporaryVariable(typeof(object));
                        generator.StoreVariable(value);
                    }

                    if (scopeVariable == null)
                    {
                        EmitHelpers.LoadScope(generator);
                    }
                    else
                    {
                        generator.LoadVariable(scopeVariable);
                    }
                    generator.CastClass(typeof(ObjectScope));
                    generator.Call(ReflectionHelpers.ObjectScope_ScopeObject);

                    if (scope.ParentScope == null && throwIfUnresolvable == false)
                    {
                        // Sets the value of a global variable.
                        // JS: object.property = value
                        // C# ==>
                        // if (propertyName == null)
                        //     propertyName = new PropertyName("property");
                        // object.SetPropertyValue(propertyName, value, strictMode);

                        ILLocalVariable propertyName = optimizationInfo.GetGlobalPropertyReferenceVariable(generator, this.Name);
                        generator.LoadVariable(propertyName);
                        generator.Duplicate();
                        var afterIf = generator.CreateLabel();
                        generator.BranchIfNotNull(afterIf);
                        generator.Pop();
                        generator.LoadString(this.Name);
                        generator.NewObject(ReflectionHelpers.PropertyName_Constructor);
                        generator.Duplicate();
                        generator.StoreVariable(propertyName);
                        generator.DefineLabelPosition(afterIf);

                        generator.LoadVariable(value);
                        generator.LoadBoolean(optimizationInfo.StrictMode);
                        generator.Call(ReflectionHelpers.ObjectInstance_SetPropertyValue_PropertyReference);
                    }
                    else
                    {
                        // Setting a variable within a "with" scope.
                        generator.LoadString(this.Name);
                        generator.LoadVariable(value);
                        generator.LoadBoolean(optimizationInfo.StrictMode);

                        // Set the property value if the property exists.
                        generator.Call(ReflectionHelpers.ObjectInstance_SetPropertyValueIfExists);

                        // The return value is true if the property was defined, and false if it wasn't.
                        generator.BranchIfTrue(endOfSet);
                    }
                }

                // Try the parent scope.
                if (scope.ParentScope != null && scope.ExistsAtRuntime == true)
                {
                    if (scopeVariable == null)
                    {
                        scopeVariable = generator.CreateTemporaryVariable(typeof(Scope));
                        EmitHelpers.LoadScope(generator);
                    }
                    else
                    {
                        generator.LoadVariable(scopeVariable);
                    }
                    generator.Call(ReflectionHelpers.Scope_ParentScope);
                    generator.StoreVariable(scopeVariable);
                }
                scope = scope.ParentScope;
            } while (scope != null);

            // The value might be still on top of the stack.
            if (value == null && scope == null)
            {
                generator.Pop();
            }

            // Throw an error if the name does not exist and throwIfUnresolvable is true.
            if (scope == null && throwIfUnresolvable == true)
            {
                EmitHelpers.EmitThrow(generator, ErrorType.ReferenceError, this.Name + " is not defined", optimizationInfo);
            }

            // Release the temporary variables.
            if (value != null)
            {
                generator.ReleaseTemporaryVariable(value);
            }
            if (scopeVariable != null)
            {
                generator.ReleaseTemporaryVariable(scopeVariable);
            }

            // Define a label at the end.
            generator.DefineLabelPosition(endOfSet);
        }
Пример #12
0
        /// <summary>
        /// Pops the value on the stack, converts it to an object, then pushes the result onto the
        /// stack.
        /// </summary>
        /// <param name="generator"> The IL generator. </param>
        /// <param name="fromType"> The type to convert from. </param>
        internal static void EmitConversionToObject(ILGenerator generator, Type fromType)
        {
            // If the from type is a reference type, check for null.
            ILLabel endOfNullCheck = null;
            if (fromType.IsValueType == false)
            {
                var startOfElse = generator.CreateLabel();
                endOfNullCheck = generator.CreateLabel();
                generator.Duplicate();
                generator.BranchIfNotNull(startOfElse);
                generator.Pop();
                EmitHelpers.EmitNull(generator);
                generator.Branch(endOfNullCheck);
                generator.DefineLabelPosition(startOfElse);
            }

            switch (Type.GetTypeCode(fromType))
            {
                case TypeCode.Boolean:
                    generator.Box(typeof(bool));
                    break;
                case TypeCode.Byte:
                    generator.Box(typeof(int));
                    break;
                case TypeCode.Char:
                    generator.LoadInt32(1);
                    generator.NewObject(ReflectionHelpers.String_Constructor_Char_Int);
                    break;
                
                case TypeCode.DBNull:
                    throw new NotSupportedException("DBNull is not a supported return type.");
                case TypeCode.Decimal:
                    generator.Call(ReflectionHelpers.Decimal_ToDouble);
                    generator.Box(typeof(double));
                    break;
                case TypeCode.Double:
                    generator.Box(typeof(double));
                    break;
                case TypeCode.Empty:
                    throw new NotSupportedException("Empty is not a supported return type.");
                case TypeCode.Int16:
                    generator.Box(typeof(int));
                    break;
                case TypeCode.Int32:
                    generator.Box(typeof(int));
                    break;
                case TypeCode.Int64:
                    generator.ConvertToDouble();
                    generator.Box(typeof(double));
                    break;

                case TypeCode.DateTime:
                case TypeCode.Object:
                    // Check if the type must be wrapped with a ClrInstanceWrapper.
                    // Note: if the type is a value type it cannot be a primitive or it would
                    // have been handled elsewhere in the switch.
                    ILLabel endOfWrapCheck = null;
                    if (fromType.IsValueType == false)
                    {
                        generator.Duplicate();
                        generator.Call(ReflectionHelpers.TypeUtilities_IsPrimitiveOrObject);
                        endOfWrapCheck = generator.CreateLabel();
                        generator.BranchIfTrue(endOfWrapCheck);
                    }

                    // The type must be wrapped.
                    var temp = generator.CreateTemporaryVariable(fromType);
                    generator.StoreVariable(temp);
                    generator.LoadArgument(0);
                    generator.LoadVariable(temp);
                    if (fromType.IsValueType == true)
                        generator.Box(fromType);
                    generator.ReleaseTemporaryVariable(temp);
                    generator.NewObject(ReflectionHelpers.ClrInstanceWrapper_Constructor);
                    
                    // End of wrap check.
                    if (fromType.IsValueType == false)
                        generator.DefineLabelPosition(endOfWrapCheck);
                    break;

                case TypeCode.SByte:
                    generator.Box(typeof(int));
                    break;
                case TypeCode.Single:
                    generator.Box(typeof(double));
                    break;
                case TypeCode.String:
                    break;
                case TypeCode.UInt16:
                    generator.Box(typeof(int));
                    break;
                case TypeCode.UInt32:
                    generator.Box(typeof(uint));
                    break;
                case TypeCode.UInt64:
                    generator.ConvertUnsignedToDouble();
                    generator.Box(typeof(double));
                    break;
            }

            // Label the end of the null check.
            if (fromType.IsValueType == false)
                generator.DefineLabelPosition(endOfNullCheck);
        }
Пример #13
0
        /// <summary>
        /// Pops the value on the stack, converts it to an object, then pushes the result onto the
        /// stack.
        /// </summary>
        /// <param name="generator"> The IL generator. </param>
        /// <param name="fromType"> The type to convert from. </param>
        internal static void EmitConversionToObject(ILGenerator generator, Type fromType)
        {
            // If the from type is a reference type, check for null.
            ILLabel endOfNullCheck = null;

            if (fromType.IsValueType == false)
            {
                var startOfElse = generator.CreateLabel();
                endOfNullCheck = generator.CreateLabel();
                generator.Duplicate();
                generator.BranchIfNotNull(startOfElse);
                generator.Pop();
                EmitHelpers.EmitNull(generator);
                generator.Branch(endOfNullCheck);
                generator.DefineLabelPosition(startOfElse);
            }

            // Handle Nullable<>.
            var isNullable = fromType.IsGenericType && fromType.GetGenericTypeDefinition() == typeof(Nullable <>);

            if (isNullable)
            {
                endOfNullCheck = generator.CreateLabel();

                var v = generator.CreateTemporaryVariable(fromType);
                generator.StoreVariable(v);
                generator.LoadAddressOfVariable(v);

                var hasValue = ReflectionHelpers.GetInstanceMethod(fromType, "get_HasValue");
                generator.Call(hasValue);
                generator.BranchIfTrue(endOfNullCheck);

                // Return null.
                generator.LoadNull();
                generator.Return();

                // Jump here if it was NOT null.
                generator.DefineLabelPosition(endOfNullCheck);

                // Get the underlying value.
                generator.LoadAddressOfVariable(v);
                var getValue = ReflectionHelpers.GetInstanceMethod(fromType, "get_Value");
                generator.Call(getValue);

                // Now let the normal conversion work.
                fromType = fromType.GenericTypeArguments[0];
            }

            switch (Type.GetTypeCode(fromType))
            {
            case TypeCode.Boolean:
                generator.Box(typeof(bool));
                break;

            case TypeCode.Byte:
                generator.Box(typeof(int));
                break;

            case TypeCode.Char:
                generator.LoadInt32(1);
                generator.NewObject(ReflectionHelpers.String_Constructor_Char_Int);
                break;

            case TypeCode.DBNull:
                throw new NotSupportedException("DBNull is not a supported return type.");

            case TypeCode.Decimal:
                generator.Call(ReflectionHelpers.Decimal_ToDouble);
                generator.Box(typeof(double));
                break;

            case TypeCode.Double:
                generator.Box(typeof(double));
                break;

            case TypeCode.Empty:
                throw new NotSupportedException("Empty is not a supported return type.");

            case TypeCode.Int16:
                generator.Box(typeof(int));
                break;

            case TypeCode.Int32:
                generator.Box(typeof(int));
                break;

            case TypeCode.Int64:
                generator.ConvertToDouble();
                generator.Box(typeof(double));
                break;

            case TypeCode.DateTime:
            case TypeCode.Object:
                // Check if the type must be wrapped with a ClrInstanceWrapper.
                // Note: if the type is a value type it cannot be a primitive or it would
                // have been handled elsewhere in the switch.
                ILLabel endOfWrapCheck = null;
                if (fromType.IsValueType == false)
                {
                    generator.Duplicate();
                    generator.Call(ReflectionHelpers.TypeUtilities_IsPrimitiveOrObject);
                    endOfWrapCheck = generator.CreateLabel();
                    generator.BranchIfTrue(endOfWrapCheck);
                }

                // The type must be wrapped.
                var temp = generator.CreateTemporaryVariable(fromType);
                generator.StoreVariable(temp);
                generator.LoadArgument(0);
                generator.LoadVariable(temp);
                if (fromType.IsValueType == true)
                {
                    generator.Box(fromType);
                }
                generator.ReleaseTemporaryVariable(temp);
                generator.CallStatic(ReflectionHelpers.ClrInstanceWrapper_Create);

                // End of wrap check.
                if (fromType.IsValueType == false)
                {
                    generator.DefineLabelPosition(endOfWrapCheck);
                }
                break;

            case TypeCode.SByte:
                generator.Box(typeof(int));
                break;

            case TypeCode.Single:
                generator.Box(typeof(double));
                break;

            case TypeCode.String:
                break;

            case TypeCode.UInt16:
                generator.Box(typeof(int));
                break;

            case TypeCode.UInt32:
                generator.Box(typeof(uint));
                break;

            case TypeCode.UInt64:
                generator.ConvertUnsignedToDouble();
                generator.Box(typeof(double));
                break;
            }

            // Label the end of the null check.
            if (fromType.IsValueType == false)
            {
                generator.DefineLabelPosition(endOfNullCheck);
            }
        }
Пример #14
0
        /// <summary>
        /// Stores the value on the top of the stack in the reference.
        /// </summary>
        /// <param name="generator"> The generator to output the CIL to. </param>
        /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param>
        /// <param name="valueType"> The primitive type of the value that is on the top of the stack. </param>
        /// <param name="throwIfUnresolvable"> <c>true</c> to throw a ReferenceError exception if
        /// the name is unresolvable; <c>false</c> to create a new property instead. </param>
        public void GenerateSet(ILGenerator generator, OptimizationInfo optimizationInfo, PrimitiveType valueType, bool throwIfUnresolvable)
        {
            // The value is initially on the top of the stack but is stored in this variable
            // at the last possible moment.
            ILLocalVariable value = null;

            var scope = this.Scope;
            ILLocalVariable scopeVariable = null;
            var endOfSet = generator.CreateLabel();
            do
            {
                if (scope is DeclarativeScope)
                {
                    // Get information about the variable.
                    var variable = scope.GetDeclaredVariable(this.Name);
                    if (variable != null)
                    {
                        // The variable was declared in this scope.

                        if (scope.ExistsAtRuntime == false)
                        {
                            // The scope has been optimized away.  The value of the variable is stored
                            // in an ILVariable.

                            // Declare an IL local variable if no storage location has been allocated yet.
                            if (variable.Store == null)
                                variable.Store = generator.DeclareVariable(typeof(object), variable.Name);

                            if (value == null)
                            {
                                // The value to store is on the top of the stack - convert it to the
                                // storage type of the variable.
                                EmitConversion.Convert(generator, valueType, variable.Type, optimizationInfo);
                            }
                            else
                            {
                                // The value to store is in a temporary variable.
                                generator.LoadVariable(value);
                                EmitConversion.Convert(generator, PrimitiveType.Any, variable.Type, optimizationInfo);
                            }

                            // Store the value in the variable.
                            generator.StoreVariable(variable.Store);
                        }
                        else if (variable.Writable == true)
                        {
                            if (value == null)
                            {
                                // The value to store is on the top of the stack - convert it to an
                                // object and store it in a temporary variable.
                                EmitConversion.Convert(generator, valueType, PrimitiveType.Any, optimizationInfo);
                                value = generator.CreateTemporaryVariable(typeof(object));
                                generator.StoreVariable(value);
                            }

                            // scope.Values[index] = value
                            if (scopeVariable == null)
                                EmitHelpers.LoadScope(generator);
                            else
                                generator.LoadVariable(scopeVariable);
                            generator.CastClass(typeof(DeclarativeScope));
                            generator.Call(ReflectionHelpers.DeclarativeScope_Values);
                            generator.LoadInt32(variable.Index);
                            generator.LoadVariable(value);
                            generator.StoreArrayElement(typeof(object));
                        }
                        else
                        {
                            // The variable exists, but is read-only.
                            // Pop the value off the stack (if it is still there).
                            if (value == null)
                                generator.Pop();
                        }

                        // The variable was found - no need to search any more parent scopes.
                        break;
                    }
                    else
                    {
                        // The variable was not defined at compile time, but may have been
                        // introduced by an eval() statement.
                        if (optimizationInfo.MethodOptimizationHints.HasEval == true)
                        {
                            if (value == null)
                            {
                                // The value to store is on the top of the stack - convert it to an
                                // object and store it in a temporary variable.
                                EmitConversion.Convert(generator, valueType, PrimitiveType.Any, optimizationInfo);
                                value = generator.CreateTemporaryVariable(typeof(object));
                                generator.StoreVariable(value);
                            }

                            // Check the variable exists: if (scope.HasValue(variableName) == true) {
                            if (scopeVariable == null)
                                EmitHelpers.LoadScope(generator);
                            else
                                generator.LoadVariable(scopeVariable);
                            generator.CastClass(typeof(DeclarativeScope));
                            generator.LoadString(this.Name);
                            generator.Call(ReflectionHelpers.Scope_HasValue);
                            var hasValueClause = generator.CreateLabel();
                            generator.BranchIfFalse(hasValueClause);

                            // Set the value of the variable.
                            if (scopeVariable == null)
                                EmitHelpers.LoadScope(generator);
                            else
                                generator.LoadVariable(scopeVariable);
                            generator.CastClass(typeof(DeclarativeScope));
                            generator.LoadString(this.Name);
                            generator.LoadVariable(value);
                            generator.Call(ReflectionHelpers.Scope_SetValue);
                            generator.Branch(endOfSet);

                            // }
                            generator.DefineLabelPosition(hasValueClause);
                        }
                    }
                }
                else
                {
                    if (value == null)
                    {
                        // The value to store is on the top of the stack - convert it to an
                        // object and store it in a temporary variable.
                        EmitConversion.Convert(generator, valueType, PrimitiveType.Any, optimizationInfo);
                        value = generator.CreateTemporaryVariable(typeof(object));
                        generator.StoreVariable(value);
                    }

                    if (scope.ParentScope == null)
                    {
                        // Optimization: if this is the global scope, use hidden classes to
                        // optimize variable access.

                        // Global variable modification
                        // ----------------------------
                        // __object_cacheKey = null;
                        // __object_property_cachedIndex = 0;
                        // ...
                        // if (__object_cacheKey != object.InlineCacheKey)
                        //     object.InlineSetPropertyValueIfExists("property", value, strictMode, out __object_property_cachedIndex, out __object_cacheKey)
                        // else
                        //     object.InlinePropertyValues[__object_property_cachedIndex] = value;

                        // Get a reference to the global object.
                        if (scopeVariable == null)
                            EmitHelpers.LoadScope(generator);
                        else
                            generator.LoadVariable(scopeVariable);
                        generator.CastClass(typeof(ObjectScope));
                        generator.Call(ReflectionHelpers.ObjectScope_ScopeObject);

                        // TODO: share these variables somehow.
                        var cacheKey = generator.DeclareVariable(typeof(object));
                        var cachedIndex = generator.DeclareVariable(typeof(int));

                        // Store the object into a temp variable.
                        var objectInstance = generator.DeclareVariable(PrimitiveType.Object);
                        generator.StoreVariable(objectInstance);

                        // if (__object_cacheKey != object.InlineCacheKey)
                        generator.LoadVariable(cacheKey);
                        generator.LoadVariable(objectInstance);
                        generator.Call(ReflectionHelpers.ObjectInstance_InlineCacheKey);
                        var elseClause = generator.CreateLabel();
                        generator.BranchIfEqual(elseClause);

                        // xxx = object.InlineSetPropertyValueIfExists("property", value, strictMode, out __object_property_cachedIndex, out __object_cacheKey)
                        generator.LoadVariable(objectInstance);
                        generator.LoadString(this.Name);
                        generator.LoadVariable(value);
                        generator.LoadBoolean(optimizationInfo.StrictMode);
                        generator.LoadAddressOfVariable(cachedIndex);
                        generator.LoadAddressOfVariable(cacheKey);
                        if (throwIfUnresolvable == false)
                        {
                            // Set the property value unconditionally.
                            generator.Call(ReflectionHelpers.ObjectInstance_InlineSetPropertyValue);
                        }
                        else
                        {
                            // Set the property value if the property exists.
                            generator.Call(ReflectionHelpers.ObjectInstance_InlineSetPropertyValueIfExists);

                            // The return value is true if the property was defined, and false if it wasn't.
                            generator.BranchIfTrue(endOfSet);
                        }

                        var endOfIf = generator.CreateLabel();
                        generator.Branch(endOfIf);

                        // else
                        generator.DefineLabelPosition(elseClause);

                        // object.InlinePropertyValues[__object_property_cachedIndex] = value;
                        generator.LoadVariable(objectInstance);
                        generator.Call(ReflectionHelpers.ObjectInstance_InlinePropertyValues);
                        generator.LoadVariable(cachedIndex);
                        generator.LoadVariable(value);
                        generator.StoreArrayElement(typeof(object));
                        generator.Branch(endOfSet);

                        // End of the if statement
                        generator.DefineLabelPosition(endOfIf);

                    }
                    else
                    {
                        // Slow route.

                        if (scopeVariable == null)
                            EmitHelpers.LoadScope(generator);
                        else
                            generator.LoadVariable(scopeVariable);
                        generator.CastClass(typeof(ObjectScope));
                        generator.Call(ReflectionHelpers.ObjectScope_ScopeObject);
                        generator.LoadString(this.Name);
                        generator.LoadVariable(value);
                        generator.LoadBoolean(optimizationInfo.StrictMode);

                        if (scope.ParentScope == null && throwIfUnresolvable == false)
                        {
                            // Set the property value unconditionally.
                            generator.Call(ReflectionHelpers.ObjectInstance_SetPropertyValue_Object);
                        }
                        else
                        {
                            // Set the property value if the property exists.
                            generator.Call(ReflectionHelpers.ObjectInstance_SetPropertyValueIfExists);

                            // The return value is true if the property was defined, and false if it wasn't.
                            generator.BranchIfTrue(endOfSet);
                        }

                    }
                }

                // Try the parent scope.
                if (scope.ParentScope != null && scope.ExistsAtRuntime == true)
                {
                    if (scopeVariable == null)
                    {
                        scopeVariable = generator.CreateTemporaryVariable(typeof(Scope));
                        EmitHelpers.LoadScope(generator);
                    }
                    else
                    {
                        generator.LoadVariable(scopeVariable);
                    }
                    generator.Call(ReflectionHelpers.Scope_ParentScope);
                    generator.StoreVariable(scopeVariable);
                }
                scope = scope.ParentScope;

            } while (scope != null);

            // The value might be still on top of the stack.
            if (value == null && scope == null)
                generator.Pop();

            // Throw an error if the name does not exist and throwIfUnresolvable is true.
            if (scope == null && throwIfUnresolvable == true)
                EmitHelpers.EmitThrow(generator, ErrorType.ReferenceError, this.Name + " is not defined", optimizationInfo);

            // Release the temporary variables.
            if (value != null)
                generator.ReleaseTemporaryVariable(value);
            if (scopeVariable != null)
                generator.ReleaseTemporaryVariable(scopeVariable);

            // Define a label at the end.
            generator.DefineLabelPosition(endOfSet);
        }
Пример #15
0
        /// <summary>
        /// Generates CIL for the statement.
        /// </summary>
        /// <param name="generator"> The generator to output the CIL to. </param>
        /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param>
        public override void GenerateCode(ILGenerator generator, OptimizationInfo optimizationInfo)
        {
            // Generate code for the start of the statement.
            var statementLocals = new StatementLocals() { NonDefaultBreakStatementBehavior = true };
            GenerateStartOfStatement(generator, optimizationInfo, statementLocals);

            // We need a label for each case clause and one for the default case.
            var jumpTargets = new ILLabel[this.CaseClauses.Count];
            int defaultIndex = -1;
            ILLabel endOfSwitch = generator.CreateLabel();

            // Generate code for the switch value.
            var startOfSwitch = generator.CreateLabel();
            this.Value.GenerateCode(generator, optimizationInfo);
            EmitConversion.ToAny(generator, this.Value.ResultType);

            // Save the switch value in a variable.
            var switchValue = generator.CreateTemporaryVariable(typeof(object));
            generator.StoreVariable(switchValue);

            for (int i = 0; i < this.CaseClauses.Count; i ++)
            {
                var caseClause = this.CaseClauses[i];

                // Create a label for each clause.
                jumpTargets[i] = generator.CreateLabel();

                if (caseClause.Value == null)
                {
                    // This is a default clause.
                    defaultIndex = i;
                    continue;
                }

                // TypeComparer.StrictEquals(switchValue, caseValue)
                generator.LoadVariable(switchValue);
                caseClause.Value.GenerateCode(generator, optimizationInfo);
                EmitConversion.ToAny(generator, caseClause.Value.ResultType);
                generator.Call(ReflectionHelpers.TypeComparer_StrictEquals);

                // if (TypeComparer.StrictEquals(switchValue, caseValue) == true)
                //     goto case i
                generator.BranchIfTrue(jumpTargets[i]);
            }
            
            // None of the cases matched, jump to the default clause or the end of the switch.
            if (defaultIndex >= 0)
                generator.Branch(jumpTargets[defaultIndex]);
            else
                generator.Branch(endOfSwitch);

            for (int i = 0; i < this.CaseClauses.Count; i++)
            {
                // Define a label at the start of the case clause.
                generator.DefineLabelPosition(jumpTargets[i]);

                // Set up the information needed by the break statement.
                optimizationInfo.PushBreakOrContinueInfo(this.Labels, endOfSwitch, null, labelledOnly: false);

                // Emit the case clause statements.
                foreach (var statement in this.CaseClauses[i].BodyStatements)
                    statement.GenerateCode(generator, optimizationInfo);

                // Revert the information needed by the break statement.
                optimizationInfo.PopBreakOrContinueInfo();
            }

            // Define a label for the end of the switch statement.
            generator.DefineLabelPosition(endOfSwitch);

            // Release the switch value variable for use elsewhere.
            generator.ReleaseTemporaryVariable(switchValue);

            // Generate code for the end of the statement.
            GenerateEndOfStatement(generator, optimizationInfo, statementLocals);
        }
Пример #16
0
        /// <summary>
        /// Generates code to push the "this" value for a function call.
        /// </summary>
        /// <param name="generator"> The generator to output the CIL to. </param>
        public void GenerateThis(ILGenerator generator)
        {
            // Optimization: if there are no with scopes, simply emit undefined.
            bool scopeChainHasWithScope = false;
            var scope = this.Scope;
            do
            {
                if (scope is ObjectScope && ((ObjectScope)scope).ProvidesImplicitThisValue == true)
                {
                    scopeChainHasWithScope = true;
                    break;
                }
                scope = scope.ParentScope;
            } while (scope != null);

            if (scopeChainHasWithScope == false)
            {
                // No with scopes in the scope chain, use undefined as the "this" value.
                EmitHelpers.EmitUndefined(generator);
                return;
            }

            var end = generator.CreateLabel();

            scope = this.Scope;
            ILLocalVariable scopeVariable = generator.CreateTemporaryVariable(typeof(Scope));
            EmitHelpers.LoadScope(generator);
            generator.StoreVariable(scopeVariable);

            do
            {
                if (scope is DeclarativeScope)
                {
                    if (scope.HasDeclaredVariable(this.Name))
                    {
                        // The variable exists but declarative scopes always produce undefined for
                        // the "this" value.
                        EmitHelpers.EmitUndefined(generator);
                        break;
                    }
                }
                else
                {
                    var objectScope = (ObjectScope)scope;

                    // Check if the property exists by calling scope.ScopeObject.HasProperty(propertyName)
                    if (objectScope.ProvidesImplicitThisValue == false)
                        EmitHelpers.EmitUndefined(generator);
                    generator.LoadVariable(scopeVariable);
                    generator.CastClass(typeof(ObjectScope));
                    generator.Call(ReflectionHelpers.ObjectScope_ScopeObject);
                    if (objectScope.ProvidesImplicitThisValue == true)
                        generator.Duplicate();
                    generator.LoadString(this.Name);
                    generator.Call(ReflectionHelpers.ObjectInstance_HasProperty);
                    generator.BranchIfTrue(end);
                    generator.Pop();

                    // If the name is not defined, use undefined for the "this" value.
                    if (scope.ParentScope == null)
                    {
                        EmitHelpers.EmitUndefined(generator);
                    }
                }

                // Try the parent scope.
                if (scope.ParentScope != null && scope.ExistsAtRuntime == true)
                {
                    generator.LoadVariable(scopeVariable);
                    generator.Call(ReflectionHelpers.Scope_ParentScope);
                    generator.StoreVariable(scopeVariable);
                }
                scope = scope.ParentScope;

            } while (scope != null);

            // Release the temporary variable.
            generator.ReleaseTemporaryVariable(scopeVariable);

            // Define a label at the end.
            generator.DefineLabelPosition(end);
        }
Пример #17
0
        /// <summary>
        /// Generates CIL for the expression.
        /// </summary>
        /// <param name="generator"> The generator to output the CIL to. </param>
        /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param>
        public override void GenerateCode(ILGenerator generator, OptimizationInfo optimizationInfo)
        {
            // Note: we use GetRawOperand() so that grouping operators are not ignored.
            var operand = this.GetRawOperand(0);

            // There is only one operand, and it can be either a reference or a function call.
            // We need to split the operand into a function and some arguments.
            // If the operand is a reference, it is equivalent to a function call with no arguments.
            if (operand is FunctionCallExpression)
            {
                // Emit the function instance first.
                var function = ((FunctionCallExpression)operand).Target;
                function.GenerateCode(generator, optimizationInfo);
                EmitConversion.ToAny(generator, function.ResultType);
            }
            else
            {
                // Emit the function instance first.
                operand.GenerateCode(generator, optimizationInfo);
                EmitConversion.ToAny(generator, operand.ResultType);
            }

            // Check the object really is a function - if not, throw an exception.
            generator.Duplicate();
            generator.IsInstance(typeof(FunctionInstance));
            var endOfTypeCheck = generator.CreateLabel();

            generator.BranchIfTrue(endOfTypeCheck);

            // Throw an nicely formatted exception.
            var targetValue = generator.CreateTemporaryVariable(typeof(object));

            generator.StoreVariable(targetValue);
            generator.LoadEnumValue(ErrorType.TypeError);
            generator.LoadString("The new operator requires a function, found a '{0}' instead");
            generator.LoadInt32(1);
            generator.NewArray(typeof(object));
            generator.Duplicate();
            generator.LoadInt32(0);
            generator.LoadVariable(targetValue);
            generator.Call(ReflectionHelpers.TypeUtilities_TypeOf);
            generator.StoreArrayElement(typeof(object));
            generator.Call(ReflectionHelpers.String_Format);
            generator.LoadInt32(optimizationInfo.SourceSpan.StartLine);
            generator.LoadStringOrNull(optimizationInfo.Source.Path);
            generator.LoadStringOrNull(optimizationInfo.FunctionName);
            generator.NewObject(ReflectionHelpers.JavaScriptException_Constructor_Error);
            generator.Throw();
            generator.DefineLabelPosition(endOfTypeCheck);
            generator.ReleaseTemporaryVariable(targetValue);

            // Store the function reference.
            generator.ReinterpretCast(typeof(FunctionInstance));
            var functionReference = generator.CreateTemporaryVariable(typeof(FunctionInstance));

            generator.Duplicate();
            generator.StoreVariable(functionReference);

            // Pass in the path, function name and line.
            generator.LoadStringOrNull(optimizationInfo.Source.Path);
            generator.LoadStringOrNull(optimizationInfo.FunctionName);
            generator.LoadInt32(optimizationInfo.SourceSpan.StartLine);

            // Pass in the value of 'new.target'.
            generator.LoadVariable(functionReference);
            generator.ReleaseTemporaryVariable(functionReference);

            if (operand is FunctionCallExpression)
            {
                // Emit an array containing the function arguments.
                ((FunctionCallExpression)operand).GenerateArgumentsArray(generator, optimizationInfo);
            }
            else
            {
                // Emit an empty array.
                generator.LoadInt32(0);
                generator.NewArray(typeof(object));
            }

            // FunctionInstance.ConstructLateBound(string path, string function, int line, FunctionInstance newTarget, object[] argumentValues)
            generator.Call(ReflectionHelpers.FunctionInstance_ConstructWithStackTrace);
        }