浏览代码

Implementing hoisting

Sebastien Ros 12 年之前
父节点
当前提交
4210c94a57

+ 0 - 6
Jint.Tests/Runtime/EngineTests.cs

@@ -309,12 +309,6 @@ namespace Jint.Tests.Runtime
         public void Scratch()
         {
             RunTest(@"
-try {                
-x=x;
-}
-catch(e) {
-assert(false);
-}
             ");
         }
 

+ 1 - 1
Jint/Engine.cs

@@ -181,7 +181,7 @@ namespace Jint
                     return _statements.ExecuteTryStatement(statement.As<TryStatement>());
                     
                 case SyntaxNodes.VariableDeclaration:
-                    return _statements.ExecuteVariableDeclaration(statement.As<VariableDeclaration>());
+                    return _statements.ExecuteVariableDeclaration(statement.As<VariableDeclaration>(), true);
                     
                 case SyntaxNodes.WhileStatement:
                     return _statements.ExecuteWhileStatement(statement.As<WhileStatement>());

+ 1 - 0
Jint/Jint.csproj

@@ -86,6 +86,7 @@
     <Compile Include="Parser\Ast\Identifier.cs" />
     <Compile Include="Parser\Ast\IfStatement.cs" />
     <Compile Include="Parser\Ast\IPropertyKeyExpression.cs" />
+    <Compile Include="Parser\Ast\IVariableScope.cs" />
     <Compile Include="Parser\Ast\LabeledStatement.cs" />
     <Compile Include="Parser\Ast\Literal.cs" />
     <Compile Include="Parser\Ast\MemberExpression.cs" />

+ 8 - 1
Jint/Parser/Ast/FunctionDeclaration.cs

@@ -2,12 +2,19 @@ using System.Collections.Generic;
 
 namespace Jint.Parser.Ast
 {
-    public class FunctionDeclaration : Statement
+    public class FunctionDeclaration : Statement, IVariableScope
     {
+        public FunctionDeclaration()
+        {
+            VariableDeclarations = new List<VariableDeclaration>();
+        }
+
         public Identifier Id;
         public IEnumerable<Identifier> Parameters;
         public Statement Body;
 
+        public IList<VariableDeclaration> VariableDeclarations { get; set; }
+
         #region ECMA6
         
         public IEnumerable<Expression> Defaults;

+ 9 - 1
Jint/Parser/Ast/FunctionExpression.cs

@@ -2,17 +2,25 @@ using System.Collections.Generic;
 
 namespace Jint.Parser.Ast
 {
-    public class FunctionExpression : Expression
+    public class FunctionExpression : Expression, IVariableScope
     {
+        public FunctionExpression()
+        {
+            VariableDeclarations = new List<VariableDeclaration>();
+        }
+
         public Identifier Id;
         public IEnumerable<Identifier> Parameters;
         public Statement Body;
 
+        public IList<VariableDeclaration> VariableDeclarations { get; set; }
+
         #region ECMA6
         public IEnumerable<Expression> Defaults;
         public SyntaxNode Rest;
         public bool Generator;
         public bool Expression;
         #endregion
+
     }
 }

+ 23 - 0
Jint/Parser/Ast/IVariableScope.cs

@@ -0,0 +1,23 @@
+using System.Collections.Generic;
+
+namespace Jint.Parser.Ast
+{
+    /// <summary>
+    /// Used to safe references to all variable delcarations in a specific scope.
+    /// Hoisting.
+    /// </summary>
+    public interface IVariableScope
+    {
+        IList<VariableDeclaration> VariableDeclarations { get; set; }
+    }
+
+    public class VariableScope : IVariableScope
+    {
+        public VariableScope()
+        {
+            VariableDeclarations = new List<VariableDeclaration>();
+        }
+
+        public IList<VariableDeclaration> VariableDeclarations { get; set; }
+    }
+}

+ 6 - 1
Jint/Parser/Ast/Program.cs

@@ -2,13 +2,18 @@ using System.Collections.Generic;
 
 namespace Jint.Parser.Ast
 {
-    public class Program : Statement
+    public class Program : Statement, IVariableScope
     {
+        public Program()
+        {
+            VariableDeclarations = new List<VariableDeclaration>();
+        }
         public ICollection<Statement> Body;
 
         public List<Comment> Comments;
         public List<Token> Tokens;
         public List<ParserError> Errors;
 
+        public IList<VariableDeclaration> VariableDeclarations { get; set; }
     }
 }

+ 31 - 4
Jint/Parser/JavascriptParser.cs

@@ -54,6 +54,8 @@ namespace Jint.Parser
         private State _state;
         private bool _strict;
 
+        private Stack<IVariableScope> _variableScopes = new Stack<IVariableScope>(); 
+
         private static bool IsDecimalDigit(char ch)
         {
             return (ch >= '0' && ch <= '9');
@@ -1494,7 +1496,8 @@ namespace Jint.Parser
                     Body = body,
                     Rest = null,
                     Generator = false,
-                    Expression = false
+                    Expression = false,
+                    VariableDeclarations = LeaveVariableScope()
                 };
         }
 
@@ -1510,7 +1513,8 @@ namespace Jint.Parser
                     Body = body,
                     Rest = null,
                     Generator = false,
-                    Expression = false
+                    Expression = false,
+                    VariableDeclarations = LeaveVariableScope()
                 };
         }
 
@@ -1600,7 +1604,8 @@ namespace Jint.Parser
             return new Program
                 {
                     Type = SyntaxNodes.Program,
-                    Body = body
+                    Body = body,
+                    VariableDeclarations = LeaveVariableScope()
                 };
         }
 
@@ -1706,12 +1711,16 @@ namespace Jint.Parser
 
         public VariableDeclaration CreateVariableDeclaration(IEnumerable<VariableDeclarator> declarations, string kind)
         {
-            return new VariableDeclaration
+            var variableDeclaration = new VariableDeclaration
                 {
                     Type = SyntaxNodes.VariableDeclaration,
                     Declarations = declarations,
                     Kind = kind
                 };
+
+            _variableScopes.Peek().VariableDeclarations.Add(variableDeclaration);
+
+            return variableDeclaration;
         }
 
         public VariableDeclarator CreateVariableDeclarator(Identifier id, Expression init)
@@ -1982,6 +1991,8 @@ namespace Jint.Parser
 
         private FunctionExpression ParsePropertyFunction(Identifier[] parameters, Token first = null)
         {
+            EnterVariableScope();
+
             bool previousStrict = _strict;
             SkipComment();
             MarkStart();
@@ -3556,6 +3567,8 @@ namespace Jint.Parser
 
         private Statement ParseFunctionDeclaration()
         {
+            EnterVariableScope();
+
             Token firstRestricted = Token.Empty;
             string message = null;
 
@@ -3610,8 +3623,19 @@ namespace Jint.Parser
             return MarkEnd(CreateFunctionDeclaration(id, parameters, new Expression[0], body));
         }
 
+        private void EnterVariableScope()
+        {
+            _variableScopes.Push(new VariableScope());
+        }
+
+        private IList<VariableDeclaration> LeaveVariableScope()
+        {
+            return _variableScopes.Pop().VariableDeclarations;
+        }
         private FunctionExpression ParseFunctionExpression()
         {
+            EnterVariableScope();
+
             Token firstRestricted = Token.Empty;
             string message = null;
             Identifier id = null;
@@ -3748,6 +3772,9 @@ namespace Jint.Parser
 
         private Program ParseProgram()
         {
+            var variableScope = new VariableScope();
+            _variableScopes.Push(variableScope);
+            
             SkipComment();
             MarkStart();
             _strict = false;

+ 25 - 10
Jint/Runtime/StatementInterpreter.cs

@@ -449,31 +449,46 @@ namespace Jint.Runtime
             return b;
         }
 
+        public void EvaluateVariableScope(IVariableScope variableScope)
+        {
+            foreach (var variableDeclaration in variableScope.VariableDeclarations)
+            {
+                // declare the variables only
+                ExecuteVariableDeclaration(variableDeclaration, false);
+            }    
+        }
+
         public Completion ExecuteProgram(Program program)
         {
+            EvaluateVariableScope(program);
             return ExecuteStatementList(program.Body);
         }
 
-        public Completion ExecuteVariableDeclaration(VariableDeclaration statement)
+        public Completion ExecuteVariableDeclaration(VariableDeclaration statement, bool initializeOnly)
         {
             var env = _engine.ExecutionContext.VariableEnvironment.Record;
             object value = Undefined.Instance;
 
             foreach (var declaration in statement.Declarations)
             {
-
-                if (declaration.Init != null)
+                var dn = declaration.Id.Name;
+                if (!initializeOnly)
                 {
-                    value = _engine.GetValue(_engine.EvaluateExpression(declaration.Init));
+                    var varAlreadyDeclared = env.HasBinding(dn);
+                    if (!varAlreadyDeclared)
+                    {
+                        env.CreateMutableBinding(dn, true);
+                    }
                 }
-
-                var dn = declaration.Id.Name;
-                var varAlreadyDeclared = env.HasBinding(dn);
-                if (!varAlreadyDeclared)
+                else
                 {
-                    env.CreateMutableBinding(declaration.Id.Name, true);
+                    if (declaration.Init != null)
+                    {
+                        value = _engine.GetValue(_engine.EvaluateExpression(declaration.Init));
+                    }
+
+                    env.SetMutableBinding(dn, value, false);
                 }
-                env.SetMutableBinding(declaration.Id.Name, value, false);
             }
 
             return new Completion(Completion.Normal, value, null);