Пример #1
0
 /// <summary>
 /// Creates a new instance of FunctionExpression.
 /// </summary>
 /// <param name="functionContext"> The function context to base this expression on. </param>
 public FunctionExpression(FunctionMethodGenerator functionContext)
 {
     if (functionContext == null)
     {
         throw new ArgumentNullException(nameof(functionContext));
     }
     this.context = functionContext;
 }
        //     INITIALIZATION
        //_________________________________________________________________________________________

        /// <summary>
        /// Creates a new instance of a user-defined function.
        /// </summary>
        /// <param name="prototype"> The next object in the prototype chain. </param>
        /// <param name="name"> The name of the function. </param>
        /// <param name="argumentNames"> The names of the arguments. </param>
        /// <param name="bodyText"> The source code for the body of the function. </param>
        internal UserDefinedFunction(ObjectInstance prototype, string name, IList<string> argumentNames, string bodyText)
            : base(prototype)
        {
            if (name == null)
                throw new ArgumentNullException("name");
            if (argumentNames == null)
                throw new ArgumentNullException("argumentNames");
            if (bodyText == null)
                throw new ArgumentNullException("bodyText");

            // Set up a new function scope.
            var scope = DeclarativeScope.CreateFunctionScope(this.Engine.CreateGlobalScope(), name, argumentNames);

            // Compile the code.
            var context = new FunctionMethodGenerator(this.Engine, scope, name, argumentNames, bodyText, new CompilerOptions());
            context.GenerateCode();

            // Create a new user defined function.
            Init(name, argumentNames, this.Engine.CreateGlobalScope(), bodyText, context.GeneratedMethod, context.StrictMode, true);
        }
 /// <summary>
 /// Compiles the function (if it hasn't already been compiled) and returns a delegate
 /// representing the compiled function.
 /// </summary>
 private FunctionDelegate Compile()
 {
     if (this.body == null)
     {
         // Compile the function.
         var scope = DeclarativeScope.CreateFunctionScope(this.Engine.CreateGlobalScope(), this.Name, this.ArgumentNames);
         var functionGenerator = new FunctionMethodGenerator(this.Engine, scope, this.Name, this.ArgumentNames, this.BodyText, new CompilerOptions());
         functionGenerator.GenerateCode();
         this.generatedMethod = functionGenerator.GeneratedMethod;
         this.body = (FunctionDelegate)this.generatedMethod.GeneratedDelegate;
     }
     return this.body;
 }
Пример #4
0
        /// <summary>
        /// Parses a function declaration or a function expression.
        /// </summary>
        /// <param name="functionType"> The type of function to parse. </param>
        /// <param name="parentScope"> The parent scope for the function. </param>
        /// <returns> A function expression. </returns>
        private FunctionExpression ParseFunction(FunctionType functionType, Scope parentScope)
        {
            if (functionType != FunctionType.Getter && functionType != FunctionType.Setter)
            {
                // Consume the function keyword.
                this.Expect(KeywordToken.Function);
            }

            // Read the function name.
            var functionName = string.Empty;
            if (functionType == FunctionType.Declaration)
            {
                functionName = this.ExpectIdentifier();
            }
            else if (functionType == FunctionType.Expression)
            {
                // The function name is optional for function expressions.
                if (this.nextToken is IdentifierToken)
                    functionName = this.ExpectIdentifier();
            }
            else if (functionType == FunctionType.Getter || functionType == FunctionType.Setter)
            {
                // Getters and setters can have any name that is allowed of a property.
                bool wasIdentifier;
                functionName = ReadPropertyName(out wasIdentifier);
            }
            else
                throw new ArgumentOutOfRangeException("functionType");
            ValidateVariableName(functionName);

            // Read the left parenthesis.
            this.Expect(PunctuatorToken.LeftParenthesis);

            // Read zero or more argument names.
            var argumentNames = new List<string>();

            // Read the first argument name.
            if (this.nextToken != PunctuatorToken.RightParenthesis)
            {
                var argumentName = this.ExpectIdentifier();
                ValidateVariableName(argumentName);
                argumentNames.Add(argumentName);
            }

            while (true)
            {
                if (this.nextToken == PunctuatorToken.Comma)
                {
                    // Consume the comma.
                    this.Consume();

                    // Read and validate the argument name.
                    var argumentName = this.ExpectIdentifier();
                    ValidateVariableName(argumentName);
                    argumentNames.Add(argumentName);
                }
                else if (this.nextToken == PunctuatorToken.RightParenthesis)
                    break;
                else
                    throw new JavaScriptException(this.engine, "SyntaxError", "Expected ',' or ')'", this.LineNumber, this.SourcePath);
            }

            // Getters must have zero arguments.
            if (functionType == FunctionType.Getter && argumentNames.Count != 0)
                throw new JavaScriptException(this.engine, "SyntaxError", "Getters cannot have arguments", this.LineNumber, this.SourcePath);

            // Setters must have one argument.
            if (functionType == FunctionType.Setter && argumentNames.Count != 1)
                throw new JavaScriptException(this.engine, "SyntaxError", "Setters must have a single argument", this.LineNumber, this.SourcePath);

            // Read the right parenthesis.
            this.Expect(PunctuatorToken.RightParenthesis);

            // Record the start of the function body.
            var startPosition = this.PositionBeforeWhitespace;

            // Since the parser reads one token in advance, start capturing the function body here.
            var bodyTextBuilder = new System.Text.StringBuilder();
            var originalBodyTextBuilder = this.lexer.InputCaptureStringBuilder;
            this.lexer.InputCaptureStringBuilder = bodyTextBuilder;

            // Read the start brace.
            this.Expect(PunctuatorToken.LeftBrace);

            // This context has a nested function.
            this.methodOptimizationHints.HasNestedFunction = true;

            // Create a new scope and assign variables within the function body to the scope.
            bool includeNameInScope = functionType != FunctionType.Getter && functionType != FunctionType.Setter;
            var scope = DeclarativeScope.CreateFunctionScope(parentScope, includeNameInScope ? functionName : string.Empty, argumentNames);

            // Read the function body.
            var functionParser = Parser.CreateFunctionBodyParser(this, scope);
            var body = functionParser.Parse();

            // Transfer state back from the function parser.
            this.nextToken = functionParser.nextToken;
            this.lexer.StrictMode = this.StrictMode;
            this.lexer.InputCaptureStringBuilder = originalBodyTextBuilder;
            if (originalBodyTextBuilder != null)
                originalBodyTextBuilder.Append(bodyTextBuilder);

            SourceCodePosition endPosition;
            if (functionType == FunctionType.Expression)
            {
                // The end token '}' will be consumed by the parent function.
                if (this.nextToken != PunctuatorToken.RightBrace)
                    throw new JavaScriptException(this.engine, "SyntaxError", "Expected '}'", this.LineNumber, this.SourcePath);

                // Record the end of the function body.
                endPosition = new SourceCodePosition(this.PositionAfterWhitespace.Line, this.PositionAfterWhitespace.Column + 1);
            }
            else
            {
                // Consume the '}'.
                this.Expect(PunctuatorToken.RightBrace);

                // Record the end of the function body.
                endPosition = new SourceCodePosition(this.PositionAfterWhitespace.Line, this.PositionAfterWhitespace.Column + 1);
            }

            // Create a new function expression.
            var options = this.options.Clone();
            options.ForceStrictMode = functionParser.StrictMode;
            var context = new FunctionMethodGenerator(this.engine, scope, functionName,
                includeNameInScope, argumentNames,
                bodyTextBuilder.ToString(0, bodyTextBuilder.Length - 1), body,
                this.SourcePath, options);
            context.MethodOptimizationHints = functionParser.methodOptimizationHints;
            return new FunctionExpression(context);
        }
Пример #5
0
 /// <summary>
 /// Creates a new instance of FunctionExpression.
 /// </summary>
 /// <param name="functionContext"> The function context to base this expression on. </param>
 public FunctionExpression(FunctionMethodGenerator functionContext)
 {
     if (functionContext == null)
         throw new ArgumentNullException("functionContext");
     this.context = functionContext;
 }
Пример #6
0
        /// <summary>
        /// Parses a function declaration or a function expression.
        /// </summary>
        /// <param name="functionType"> The type of function to parse. </param>
        /// <param name="parentScope"> The parent scope for the function. </param>
        /// <param name="functionName"> The name of the function (can be empty). </param>
        /// <returns> A function expression. </returns>
        private FunctionExpression ParseFunction(FunctionDeclarationType functionType, Scope parentScope, string functionName)
        {
            // Read the left parenthesis.
            this.Expect(PunctuatorToken.LeftParenthesis);

            // Create a new scope and assign variables within the function body to the scope.
            bool includeNameInScope = functionType != FunctionDeclarationType.Getter && functionType != FunctionDeclarationType.Setter;
            var scope = DeclarativeScope.CreateFunctionScope(parentScope, includeNameInScope ? functionName : string.Empty, null);

            // Replace scope and methodOptimizationHints.
            var originalScope = this.currentVarScope;
            var originalMethodOptimizationHints = this.methodOptimizationHints;
            var newMethodOptimizationHints = new MethodOptimizationHints();
            this.methodOptimizationHints = newMethodOptimizationHints;
            this.currentVarScope = scope;

            // Read zero or more arguments.
            var arguments = ParseFunctionArguments(PunctuatorToken.RightParenthesis);

            // Restore scope and methodOptimizationHints.
            this.methodOptimizationHints = originalMethodOptimizationHints;
            this.currentVarScope = originalScope;

            // Getters must have zero arguments.
            if (functionType == FunctionDeclarationType.Getter && arguments.Count != 0)
                throw new JavaScriptException(this.engine, ErrorType.SyntaxError, "Getters cannot have arguments", this.LineNumber, this.SourcePath);

            // Setters must have one argument.
            if (functionType == FunctionDeclarationType.Setter && arguments.Count != 1)
                throw new JavaScriptException(this.engine, ErrorType.SyntaxError, "Setters must have a single argument", this.LineNumber, this.SourcePath);

            // Read the right parenthesis.
            this.Expect(PunctuatorToken.RightParenthesis);

            // Record the start of the function body.
            var startPosition = this.PositionBeforeWhitespace;

            // Since the parser reads one token in advance, start capturing the function body here.
            var bodyTextBuilder = new System.Text.StringBuilder();
            var originalBodyTextBuilder = this.lexer.InputCaptureStringBuilder;
            this.lexer.InputCaptureStringBuilder = bodyTextBuilder;

            // Read the start brace.
            this.Expect(PunctuatorToken.LeftBrace);

            // This context has a nested function.
            this.methodOptimizationHints.HasNestedFunction = true;

            // Read the function body.
            var functionParser = CreateFunctionBodyParser(this, scope, newMethodOptimizationHints);
            var body = functionParser.Parse();

            // Transfer state back from the function parser.
            this.nextToken = functionParser.nextToken;
            this.lexer.StrictMode = this.StrictMode;
            this.lexer.InputCaptureStringBuilder = originalBodyTextBuilder;
            if (originalBodyTextBuilder != null)
                originalBodyTextBuilder.Append(bodyTextBuilder);

            SourceCodePosition endPosition;
            if (functionType == FunctionDeclarationType.Expression)
            {
                // The end token '}' will be consumed by the parent function.
                if (this.nextToken != PunctuatorToken.RightBrace)
                    throw new JavaScriptException(this.engine, ErrorType.SyntaxError, "Expected '}'", this.LineNumber, this.SourcePath);

                // Record the end of the function body.
                endPosition = new SourceCodePosition(this.PositionAfterWhitespace.Line, this.PositionAfterWhitespace.Column + 1);
            }
            else
            {
                // Consume the '}'.
                this.Expect(PunctuatorToken.RightBrace);

                // Record the end of the function body.
                endPosition = new SourceCodePosition(this.PositionAfterWhitespace.Line, this.PositionAfterWhitespace.Column + 1);
            }

            // Create a new function expression.
            var options = this.options.Clone();
            options.ForceStrictMode = functionParser.StrictMode;
            var context = new FunctionMethodGenerator(this.engine, scope, functionName,
                functionType, arguments,
                bodyTextBuilder.ToString(0, bodyTextBuilder.Length - 1), body,
                this.SourcePath, options);
            context.MethodOptimizationHints = functionParser.methodOptimizationHints;
            return new FunctionExpression(context);
        }
Пример #7
0
 /// <summary>
 /// Creates a new instance of FunctionExpression.
 /// </summary>
 /// <param name="functionContext"> The function context to base this expression on. </param>
 /// <param name="scope"> The scope that was in effect where the function was declared. </param>
 public FunctionExpression(FunctionMethodGenerator functionContext, Scope scope)
 {
     this.context = functionContext ?? throw new ArgumentNullException(nameof(functionContext));
     this.Scope   = scope;
 }