Browse Source

Implementing Declaration Binding Instantiation

Sebastien Ros 12 years ago
parent
commit
9cc5450489

+ 1 - 1
Jint.Tests.Ecma/EcmaTest.cs

@@ -29,7 +29,7 @@ namespace Jint.Tests
             {
                 throw new ArgumentException("Could not find source file: " + fullName);
             }
-
+            
             string code = File.ReadAllText(fullName);
 
             var engine = new Engine(cfg => cfg

+ 23 - 1
Jint.Tests/Runtime/EngineTests.cs

@@ -306,12 +306,34 @@ namespace Jint.Tests.Runtime
         }
 
         [Fact]
-        public void Scratch()
+        public void ToNumberHandlesStringObject()
         {
             RunTest(@"
+                x = new String('1');
+                x *= undefined;
+                assert(isNaN(x));
             ");
         }
 
+        [Fact]
+        public void Scratch()
+        {
+            RunTest(@"
+                var x = 0;
+
+                function f1(){
+                  function f2(){
+                    return x;
+                  };
+                  return f2();
+  
+                  var x = 1;
+                }
+
+                assert(f1() === undefined);
+        ");
+        }
+
         /*
                         [Fact]
                         public void ()

+ 69 - 7
Jint/Engine.cs

@@ -1,5 +1,7 @@
 using System;
 using System.Collections.Generic;
+using System.Linq;
+using Jint.Native;
 using Jint.Native.Array;
 using Jint.Native.Boolean;
 using Jint.Native.Date;
@@ -24,7 +26,6 @@ namespace Jint
     {
         private readonly ExpressionInterpreter _expressions;
         private readonly StatementInterpreter _statements;
-        private readonly LexicalEnvironment _globalEnvironment;
         private readonly Stack<ExecutionContext> _executionContexts;
 
         public Engine() : this(null)
@@ -58,10 +59,10 @@ namespace Jint
             Global.Set("Math", Math);
 
             // create the global environment http://www.ecma-international.org/ecma-262/5.1/#sec-10.2.3
-            _globalEnvironment = LexicalEnvironment.NewObjectEnvironment(Global, null);
+            GlobalEnvironment = LexicalEnvironment.NewObjectEnvironment(Global, null, true);
             
             // create the global execution context http://www.ecma-international.org/ecma-262/5.1/#sec-10.4.1.1
-            EnterExecutionContext(_globalEnvironment, _globalEnvironment, Global);
+            EnterExecutionContext(GlobalEnvironment, GlobalEnvironment, Global);
 
             Options = new Options();
 
@@ -82,6 +83,8 @@ namespace Jint
             _expressions = new ExpressionInterpreter(this);
         }
 
+        public LexicalEnvironment GlobalEnvironment;
+
         public ObjectInstance RootObject { get; private set; }
         public FunctionInstance RootFunction { get; private set; }
 
@@ -160,7 +163,7 @@ namespace Jint
                     return _statements.ForInStatement(statement.As<ForInStatement>());
 
                 case SyntaxNodes.FunctionDeclaration:
-                    return _statements.ExecuteFunctionDeclaration(statement.As<FunctionDeclaration>());
+                    return new Completion(Completion.Normal, null, null);
                     
                 case SyntaxNodes.IfStatement:
                     return _statements.ExecuteIfStatement(statement.As<IfStatement>());
@@ -181,7 +184,7 @@ namespace Jint
                     return _statements.ExecuteTryStatement(statement.As<TryStatement>());
                     
                 case SyntaxNodes.VariableDeclaration:
-                    return _statements.ExecuteVariableDeclaration(statement.As<VariableDeclaration>(), true);
+                    return _statements.ExecuteVariableDeclaration(statement.As<VariableDeclaration>());
                     
                 case SyntaxNodes.WhileStatement:
                     return _statements.ExecuteWhileStatement(statement.As<WhileStatement>());
@@ -195,8 +198,6 @@ namespace Jint
                 default:
                     throw new ArgumentOutOfRangeException();
             }
-
-            return null;
         }
 
         public object EvaluateExpression(Expression expression)
@@ -348,5 +349,66 @@ namespace Jint
 
             return GetValue(Global.Get(propertyName));
         }
+
+        public void FunctionDeclarationBindings(IFunctionScope functionScope, LexicalEnvironment localEnv, bool configurableBindings, bool strict)
+        {
+            // Declaration Binding Instantiation http://www.ecma-international.org/ecma-262/5.1/#sec-10.5
+            var env = localEnv.Record;
+
+            // process all function declarations in the current parser scope
+            foreach (var functionDeclaration in functionScope.FunctionDeclarations)
+            {
+                var fn = functionDeclaration.Id.Name;
+                var fo = this.Function.CreateFunctionObject(functionDeclaration);
+                var funcAlreadyDeclared = env.HasBinding(fn);
+                if (!funcAlreadyDeclared)
+                {
+                    env.CreateMutableBinding(fn, configurableBindings);
+                }
+                else
+                {
+                    if (env == this.GlobalEnvironment.Record)
+                    {
+                        var go = this.Global;
+                        var existingProp = go.GetProperty(fn);
+                        if (existingProp.Configurable)
+                        {
+                            go.DefineOwnProperty(fn,
+                                                 new DataDescriptor(Undefined.Instance)
+                                                 {
+                                                     Writable = true,
+                                                     Enumerable = true,
+                                                     Configurable = configurableBindings
+                                                 }, true);
+                        }
+                        else
+                        {
+                            if (existingProp.IsAccessorDescriptor() || (!existingProp.Enumerable))
+                            {
+                                throw new TypeError();
+                            }
+                        }
+                    }
+                }
+
+                env.SetMutableBinding(fn, fo, strict);
+            }
+        }
+
+        public void VariableDeclarationBinding(IVariableScope variableScope, EnvironmentRecord env, bool configurableBindings, bool strict)
+        {
+            // process all variable declarations in the current parser scope
+            foreach (var d in variableScope.VariableDeclarations.SelectMany(x => x.Declarations))
+            {
+                var dn = d.Id.Name;
+                var varAlreadyDeclared = env.HasBinding(dn);
+                if (!varAlreadyDeclared)
+                {
+                    env.CreateMutableBinding(dn, configurableBindings);
+                    env.SetMutableBinding(dn, Undefined.Instance, strict);
+                }
+            }
+        }
+
     }
 }

+ 4 - 1
Jint/Jint.csproj

@@ -47,6 +47,7 @@
     <Compile Include="Native\Errors\SyntaxError.cs" />
     <Compile Include="Native\Errors\ReferenceError.cs" />
     <Compile Include="Native\Errors\TypeError.cs" />
+    <Compile Include="Native\Function\ArgumentsObject.cs" />
     <Compile Include="Native\Function\FunctionConstructor.cs" />
     <Compile Include="Native\Function\FunctionInstance.cs" />
     <Compile Include="Native\Function\FunctionShim.cs" />
@@ -85,8 +86,10 @@
     <Compile Include="Parser\Ast\FunctionExpression.cs" />
     <Compile Include="Parser\Ast\Identifier.cs" />
     <Compile Include="Parser\Ast\IfStatement.cs" />
+    <Compile Include="Parser\IFunctionDeclaration.cs" />
     <Compile Include="Parser\Ast\IPropertyKeyExpression.cs" />
-    <Compile Include="Parser\Ast\IVariableScope.cs" />
+    <Compile Include="Parser\IFunctionScope.cs" />
+    <Compile Include="Parser\IVariableScope.cs" />
     <Compile Include="Parser\Ast\LabeledStatement.cs" />
     <Compile Include="Parser\Ast\Literal.cs" />
     <Compile Include="Parser\Ast\MemberExpression.cs" />

+ 15 - 0
Jint/Native/Function/ArgumentsObject.cs

@@ -0,0 +1,15 @@
+using Jint.Native.Object;
+
+namespace Jint.Native.Function
+{
+    /// <summary>
+    /// http://www.ecma-international.org/ecma-262/5.1/#sec-10.6
+    /// </summary>
+    public class ArgumentsInstance : ObjectInstance
+    {
+        public ArgumentsInstance(ObjectInstance prototype) : base(prototype)
+        {
+            // todo: complete implementation
+        }
+    }
+}

+ 21 - 0
Jint/Native/Function/FunctionConstructor.cs

@@ -1,7 +1,9 @@
 using System.Collections.Generic;
 using Jint.Native.Object;
 using Jint.Parser.Ast;
+using Jint.Runtime;
 using Jint.Runtime.Descriptors;
+using Jint.Runtime.Environments;
 
 namespace Jint.Native.Function
 {
@@ -34,5 +36,24 @@ namespace Jint.Native.Function
 
             return instance;
         }
+
+        /// <summary>
+        /// http://www.ecma-international.org/ecma-262/5.1/#sec-13.2
+        /// </summary>
+        /// <param name="functionDeclaration"></param>
+        /// <returns></returns>
+        public FunctionInstance CreateFunctionObject(FunctionDeclaration functionDeclaration)
+        {
+            var functionObject = new ScriptFunctionInstance(
+                _engine,
+                functionDeclaration,
+                _engine.Function.Prototype /* instancePrototype */,
+                _engine.Object.Construct(Arguments.Empty) /* functionPrototype */,
+                LexicalEnvironment.NewDeclarativeEnvironment(_engine.ExecutionContext.LexicalEnvironment),
+                functionDeclaration.Strict
+                ) { Extensible = true };
+
+            return functionObject;
+        }
     }
 }

+ 1 - 0
Jint/Native/Function/FunctionInstance.cs

@@ -1,6 +1,7 @@
 using Jint.Native.Errors;
 using Jint.Native.Object;
 using Jint.Parser.Ast;
+using Jint.Runtime;
 using Jint.Runtime.Environments;
 
 namespace Jint.Native.Function

+ 56 - 21
Jint/Native/Function/ScriptFunctionInstance.cs

@@ -1,8 +1,8 @@
 using System;
-using System.Collections.Generic;
 using System.Linq;
+using Jint.Native.Errors;
 using Jint.Native.Object;
-using Jint.Parser.Ast;
+using Jint.Parser;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Environments;
@@ -15,22 +15,20 @@ namespace Jint.Native.Function
     public sealed class ScriptFunctionInstance : FunctionInstance, IConstructor
     {
         private readonly Engine _engine;
-        private readonly Statement _body;
-        private readonly IEnumerable<Identifier> _parameters;
+        private readonly IFunctionDeclaration _functionDeclaration;
         
-        public ScriptFunctionInstance(Engine engine, Statement body, string name, Identifier[] parameters, ObjectInstance instancePrototype, ObjectInstance functionPrototype, LexicalEnvironment scope, bool strict)
-            : base(engine, instancePrototype, parameters, scope, strict)
+        public ScriptFunctionInstance(Engine engine, IFunctionDeclaration functionDeclaration, ObjectInstance instancePrototype, ObjectInstance functionPrototype, LexicalEnvironment scope, bool strict)
+            : base(engine, instancePrototype, functionDeclaration.Parameters.ToArray(), scope, strict)
         {
             // http://www.ecma-international.org/ecma-262/5.1/#sec-13.2
 
             _engine = engine;
-            _body = body;
-            _parameters = parameters;
+            _functionDeclaration = functionDeclaration;
             Extensible = true;
-            var len = parameters.Count();
+            var len = functionDeclaration.Parameters.Count();
 
             DefineOwnProperty("length", new DataDescriptor(len) { Writable = false, Enumerable = false, Configurable = false }, false);
-            DefineOwnProperty("name", new DataDescriptor(name), false);
+            DefineOwnProperty("name", new DataDescriptor(_functionDeclaration.Id), false);
             instancePrototype.DefineOwnProperty("constructor", new DataDescriptor(this) { Writable = true, Enumerable = true, Configurable = true }, false);
             DefineOwnProperty("prototype", new DataDescriptor(functionPrototype) { Writable = true, Enumerable = true, Configurable = true }, false);
         }
@@ -38,44 +36,81 @@ namespace Jint.Native.Function
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-13.2.1
         /// </summary>
-        /// <param name="thisObject"></param>
+        /// <param name="thisArg"></param>
         /// <param name="arguments"></param>
         /// <returns></returns>
-        public override object Call(object thisObject, object[] arguments)
+        public override object Call(object thisArg, object[] arguments)
         {
             object thisBinding;
 
             // setup new execution context http://www.ecma-international.org/ecma-262/5.1/#sec-10.4.3
             if (_engine.Options.IsStrict())
             {
-                thisBinding = thisObject;
+                thisBinding = thisArg;
             }
-            else if (thisObject == Undefined.Instance || thisObject == Null.Instance)
+            else if (thisArg == Undefined.Instance || thisArg == Null.Instance)
             {
                 thisBinding = _engine.Global;
             }
-            else if (TypeConverter.GetType(thisObject) != TypeCode.Object)
+            else if (TypeConverter.GetType(thisArg) != TypeCode.Object)
             {
-                thisBinding = TypeConverter.ToObject(_engine, thisObject);
+                thisBinding = TypeConverter.ToObject(_engine, thisArg);
             }
             else
             {
-                thisBinding = thisObject;
+                thisBinding = thisArg;
             }
 
             var localEnv = LexicalEnvironment.NewDeclarativeEnvironment(Scope);
             
             _engine.EnterExecutionContext(localEnv, localEnv, thisBinding);
 
+            // Declaration Binding Instantiation http://www.ecma-international.org/ecma-262/5.1/#sec-10.5
             var env = localEnv.Record;
+            var configurableBindings = false;
 
-            int i = 0;
-            foreach (var parameter in _parameters)
+            var argCount = arguments.Length;
+            var n = 0;
+            foreach (var parameter in _functionDeclaration.Parameters)
             {
-                env.SetMutableBinding(parameter.Name, i < arguments.Length ? arguments[i++] : Undefined.Instance, false);
+                var argName = parameter.Name;
+                n++;
+                var v = n > argCount ? Undefined.Instance : arguments[n-1];
+                var argAlreadyDeclared = env.HasBinding(argName);
+                if (!argAlreadyDeclared)
+                {
+                    env.CreateMutableBinding(argName);
+                }
+
+                env.SetMutableBinding(argName, v, Strict);
+            }
+
+            _engine.FunctionDeclarationBindings(_functionDeclaration, localEnv, true, Strict);
+
+            var argumentsAlreadyDeclared = env.HasBinding("arguments");
+
+            if (!argumentsAlreadyDeclared)
+            {
+                // todo: ArgumentsInstance implementation
+                var argsObj = new ArgumentsInstance(null);
+
+                if (Strict)
+                {
+                    var declEnv = env as DeclarativeEnvironmentRecord;
+                    declEnv.CreateImmutableBinding("arguments");
+                    declEnv.InitializeImmutableBinding("arguments", argsObj);
+                }
+                else
+                {
+                    env.CreateMutableBinding("arguments");
+                    env.SetMutableBinding("arguments", argsObj, false);
+                }
             }
 
-            var result = _engine.ExecuteStatement(_body);
+            // process all variable declarations in the current parser scope
+            _engine.VariableDeclarationBinding(_functionDeclaration, env, configurableBindings, Strict);
+
+            var result = _engine.ExecuteStatement(_functionDeclaration.Body);
             
             _engine.LeaveExecutionContext();
 

+ 7 - 5
Jint/Parser/Ast/FunctionDeclaration.cs

@@ -2,17 +2,17 @@ using System.Collections.Generic;
 
 namespace Jint.Parser.Ast
 {
-    public class FunctionDeclaration : Statement, IVariableScope
+    public class FunctionDeclaration : Statement, IFunctionDeclaration, IVariableScope
     {
         public FunctionDeclaration()
         {
             VariableDeclarations = new List<VariableDeclaration>();
         }
 
-        public Identifier Id;
-        public IEnumerable<Identifier> Parameters;
-        public Statement Body;
-        public bool Strict;
+        public Identifier Id { get; set; }
+        public IEnumerable<Identifier> Parameters { get; set; }
+        public Statement Body { get; set; }
+        public bool Strict { get; set; }
 
         public IList<VariableDeclaration> VariableDeclarations { get; set; }
 
@@ -24,5 +24,7 @@ namespace Jint.Parser.Ast
         public bool Expression;
         
         #endregion
+
+        public IList<FunctionDeclaration> FunctionDeclarations { get; set; }
     }
 }

+ 6 - 5
Jint/Parser/Ast/FunctionExpression.cs

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

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

@@ -2,7 +2,7 @@ using System.Collections.Generic;
 
 namespace Jint.Parser.Ast
 {
-    public class Program : Statement, IVariableScope
+    public class Program : Statement, IVariableScope, IFunctionScope
     {
         public Program()
         {
@@ -16,5 +16,6 @@ namespace Jint.Parser.Ast
         public bool Strict;
 
         public IList<VariableDeclaration> VariableDeclarations { get; set; }
+        public IList<FunctionDeclaration> FunctionDeclarations { get; set; }
     }
 }

+ 13 - 0
Jint/Parser/IFunctionDeclaration.cs

@@ -0,0 +1,13 @@
+using System.Collections.Generic;
+using Jint.Parser.Ast;
+
+namespace Jint.Parser
+{
+    public interface IFunctionDeclaration : IFunctionScope
+    {
+        Identifier Id { get; }
+        IEnumerable<Identifier> Parameters { get; }
+        Statement Body { get; }
+        bool Strict { get; }
+    }
+}

+ 24 - 0
Jint/Parser/IFunctionScope.cs

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

+ 2 - 1
Jint/Parser/Ast/IVariableScope.cs → Jint/Parser/IVariableScope.cs

@@ -1,6 +1,7 @@
 using System.Collections.Generic;
+using Jint.Parser.Ast;
 
-namespace Jint.Parser.Ast
+namespace Jint.Parser
 {
     /// <summary>
     /// Used to safe references to all variable delcarations in a specific scope.

+ 55 - 33
Jint/Parser/JavascriptParser.cs

@@ -54,7 +54,8 @@ namespace Jint.Parser
         private State _state;
         private bool _strict;
 
-        private Stack<IVariableScope> _variableScopes = new Stack<IVariableScope>(); 
+        private readonly Stack<IVariableScope> _variableScopes = new Stack<IVariableScope>();
+        private readonly Stack<IFunctionScope> _functionScopes = new Stack<IFunctionScope>(); 
 
         private static bool IsDecimalDigit(char ch)
         {
@@ -405,7 +406,7 @@ namespace Jint.Parser
                     break;
                 }
                 ++_index;
-                id += ch;
+                id += ch.ToString();
 
                 // '\u' (char #92, char #117) denotes an escaped character.
                 if (ch == 92)
@@ -421,7 +422,7 @@ namespace Jint.Parser
                     {
                         throw new Exception(Messages.UnexpectedToken);
                     }
-                    id += ch;
+                    id += ch.ToString();
                 }
             }
 
@@ -689,7 +690,7 @@ namespace Jint.Parser
                 {
                     break;
                 }
-                number += _source.CharCodeAt(_index++);
+                number += _source.CharCodeAt(_index++).ToString();
             }
 
             if (number.Length == 0)
@@ -721,7 +722,7 @@ namespace Jint.Parser
                 {
                     break;
                 }
-                number += _source.CharCodeAt(_index++);
+                number += _source.CharCodeAt(_index++).ToString();
             }
 
             if (IsIdentifierStart(_source.CharCodeAt(_index)) || IsDecimalDigit(_source.CharCodeAt(_index)))
@@ -774,35 +775,35 @@ namespace Jint.Parser
 
                 while (IsDecimalDigit(_source.CharCodeAt(_index)))
                 {
-                    number += _source.CharCodeAt(_index++);
+                    number += _source.CharCodeAt(_index++).ToString();
                 }
                 ch = _source.CharCodeAt(_index);
             }
 
             if (ch == '.')
             {
-                number += _source.CharCodeAt(_index++);
+                number += _source.CharCodeAt(_index++).ToString();
                 while (IsDecimalDigit(_source.CharCodeAt(_index)))
                 {
-                    number += _source.CharCodeAt(_index++);
+                    number += _source.CharCodeAt(_index++).ToString();
                 }
                 ch = _source.CharCodeAt(_index);
             }
 
             if (ch == 'e' || ch == 'E')
             {
-                number += _source.CharCodeAt(_index++);
+                number += _source.CharCodeAt(_index++).ToString();
 
                 ch = _source.CharCodeAt(_index);
                 if (ch == '+' || ch == '-')
                 {
-                    number += _source.CharCodeAt(_index++);
+                    number += _source.CharCodeAt(_index++).ToString();
                 }
                 if (IsDecimalDigit(_source.CharCodeAt(_index)))
                 {
                     while (IsDecimalDigit(_source.CharCodeAt(_index)))
                     {
-                        number += _source.CharCodeAt(_index++);
+                        number += _source.CharCodeAt(_index++).ToString();
                     }
                 }
                 else
@@ -870,22 +871,22 @@ namespace Jint.Parser
                                 char unescaped = ScanHexEscape(ch);
                                 if (unescaped > 0)
                                 {
-                                    str += unescaped;
+                                    str += unescaped.ToString();
                                 }
                                 else
                                 {
                                     _index = restore;
-                                    str += ch;
+                                    str += ch.ToString();
                                 }
                                 break;
                             case 'b':
-                                str += '\b';
+                                str += "\b";
                                 break;
                             case 'f':
-                                str += '\f';
+                                str += "\f";
                                 break;
                             case 'v':
-                                str += '\x0B';
+                                str += "\x0B";
                                 break;
 
                             default:
@@ -913,11 +914,11 @@ namespace Jint.Parser
                                             code = code * 8 + "01234567".IndexOf(_source.CharCodeAt(_index++));
                                         }
                                     }
-                                    str += (char) code;
+                                    str += ((char)code).ToString();
                                 }
                                 else
                                 {
-                                    str += ch;
+                                    str += ch.ToString();
                                 }
                                 break;
                         }
@@ -937,7 +938,7 @@ namespace Jint.Parser
                 }
                 else
                 {
-                    str += ch;
+                    str += ch.ToString();
                 }
             }
 
@@ -973,7 +974,7 @@ namespace Jint.Parser
             while (_index < _length)
             {
                 ch = _source.CharCodeAt(_index++);
-                str += ch;
+                str += ch.ToString();
                 if (classMarker)
                 {
                     if (ch == ']')
@@ -991,7 +992,7 @@ namespace Jint.Parser
                         {
                             throw new Exception(Messages.UnterminatedRegExp);
                         }
-                        str += ch;
+                        str += ch.ToString();
                     }
                     else if (ch == '/')
                     {
@@ -1037,28 +1038,28 @@ namespace Jint.Parser
                         ch = ScanHexEscape('u');
                         if (ch > 0)
                         {
-                            flags += ch;
+                            flags += ch.ToString();
                             for (str += "\\u"; restore < _index; ++restore)
                             {
-                                str += _source.CharCodeAt(restore);
+                                str += _source.CharCodeAt(restore).ToString();
                             }
                         }
                         else
                         {
                             _index = restore;
-                            flags += 'u';
+                            flags += "u";
                             str += "\\u";
                         }
                     }
                     else
                     {
-                        str += '\\';
+                        str += "\\";
                     }
                 }
                 else
                 {
-                    flags += ch;
-                    str += ch;
+                    flags += ch.ToString();
+                    str += ch.ToString();
                 }
             }
 
@@ -1487,7 +1488,7 @@ namespace Jint.Parser
         public FunctionDeclaration CreateFunctionDeclaration(Identifier id, IEnumerable<Identifier> parameters,
                                                              IEnumerable<Expression> defaults, Statement body, bool strict)
         {
-            return new FunctionDeclaration
+            var functionDeclaration = new FunctionDeclaration
                 {
                     Type = SyntaxNodes.FunctionDeclaration,
                     Id = id,
@@ -1498,8 +1499,13 @@ namespace Jint.Parser
                     Rest = null,
                     Generator = false,
                     Expression = false,
-                    VariableDeclarations = LeaveVariableScope()
+                    VariableDeclarations = LeaveVariableScope(),
+                    FunctionDeclarations = LeaveFunctionScope()
                 };
+
+            _functionScopes.Peek().FunctionDeclarations.Add(functionDeclaration);
+
+            return functionDeclaration;
         }
 
         public FunctionExpression CreateFunctionExpression(Identifier id, IEnumerable<Identifier> parameters,
@@ -1516,7 +1522,8 @@ namespace Jint.Parser
                     Rest = null,
                     Generator = false,
                     Expression = false,
-                    VariableDeclarations = LeaveVariableScope()
+                    VariableDeclarations = LeaveVariableScope(),
+                    FunctionDeclarations = LeaveFunctionScope()
                 };
         }
 
@@ -1608,7 +1615,8 @@ namespace Jint.Parser
                     Type = SyntaxNodes.Program,
                     Body = body,
                     Strict = strict,
-                    VariableDeclarations = LeaveVariableScope()
+                    VariableDeclarations = LeaveVariableScope(),
+                    FunctionDeclarations = LeaveFunctionScope()
                 };
         }
 
@@ -1995,6 +2003,7 @@ namespace Jint.Parser
         private FunctionExpression ParsePropertyFunction(Identifier[] parameters, Token first = null)
         {
             EnterVariableScope();
+            EnterFunctionScope();
 
             bool previousStrict = _strict;
             SkipComment();
@@ -3572,6 +3581,7 @@ namespace Jint.Parser
         private Statement ParseFunctionDeclaration()
         {
             EnterVariableScope();
+            EnterFunctionScope();
 
             Token firstRestricted = Token.Empty;
             string message = null;
@@ -3637,9 +3647,21 @@ namespace Jint.Parser
         {
             return _variableScopes.Pop().VariableDeclarations;
         }
+
+        private void EnterFunctionScope()
+        {
+            _functionScopes.Push(new FunctionScope());
+        }
+
+        private IList<FunctionDeclaration> LeaveFunctionScope()
+        {
+            return _functionScopes.Pop().FunctionDeclarations;
+        }
+
         private FunctionExpression ParseFunctionExpression()
         {
             EnterVariableScope();
+            EnterFunctionScope();
 
             Token firstRestricted = Token.Empty;
             string message = null;
@@ -3778,8 +3800,8 @@ namespace Jint.Parser
 
         private Program ParseProgram()
         {
-            var variableScope = new VariableScope();
-            _variableScopes.Push(variableScope);
+            EnterVariableScope();
+            EnterFunctionScope();
             
             SkipComment();
             MarkStart();

+ 2 - 2
Jint/Runtime/Environments/LexicalEnvironment.cs

@@ -50,9 +50,9 @@ namespace Jint.Runtime.Environments
             return new LexicalEnvironment(new DeclarativeEnvironmentRecord(), outer);
         }
 
-        public static LexicalEnvironment NewObjectEnvironment(ObjectInstance objectInstance, LexicalEnvironment outer)
+        public static LexicalEnvironment NewObjectEnvironment(ObjectInstance objectInstance, LexicalEnvironment outer, bool provideThis)
         {
-            return new LexicalEnvironment(new ObjectEnvironmentRecord(objectInstance), outer);
+            return new LexicalEnvironment(new ObjectEnvironmentRecord(objectInstance, provideThis), outer);
         }
     }
 

+ 1 - 1
Jint/Runtime/Environments/ObjectEnvironmentRecord.cs

@@ -14,7 +14,7 @@ namespace Jint.Runtime.Environments
         private readonly ObjectInstance _bindingObject;
         private readonly bool _provideThis;
 
-        public ObjectEnvironmentRecord(ObjectInstance bindingObject, bool provideThis = false)
+        public ObjectEnvironmentRecord(ObjectInstance bindingObject, bool provideThis)
         {
             _bindingObject = bindingObject;
             _provideThis = provideThis;

+ 72 - 115
Jint/Runtime/ExpressionIntepreter.cs

@@ -50,104 +50,83 @@ namespace Jint.Runtime
             }
 
             object lval = _engine.GetValue(lref);
-            var type = TypeConverter.GetType(lval);
 
             switch (assignmentExpression.Operator)
             {
                 case "+=":
-                    switch (type)
+                    var lprim = TypeConverter.ToPrimitive(lval);
+                    var rprim = TypeConverter.ToPrimitive(rval);
+                    if (TypeConverter.GetType(lprim) == TypeCode.String ||
+                        TypeConverter.GetType(rprim) == TypeCode.String)
                     {
-                        case TypeCode.String:
-                            lval = TypeConverter.ToString(_engine.GetValue(lref)) + rval;
-                            break;
-
-                        case TypeCode.Double:
-                            lval = TypeConverter.ToNumber(_engine.GetValue(lref)) + TypeConverter.ToNumber(rval);
-                            break;
+                        lval = TypeConverter.ToString(lprim) + TypeConverter.ToString(rprim);
+                    }
+                    else
+                    {
+                        lval = TypeConverter.ToNumber(lprim) + TypeConverter.ToNumber(rprim);
                     }
-                    
                     break;
 
                 case "-=":
-                    switch (type)
-                    {
-                        case TypeCode.Double:
-                            lval = TypeConverter.ToNumber(_engine.GetValue(lref)) + TypeConverter.ToNumber(rval);
-                            break;
-                    }
+                    lval = TypeConverter.ToNumber(lval) + TypeConverter.ToNumber(rval);
                     break;
 
                 case "*=":
-                    switch (type)
+                    if (lval == Undefined.Instance || rval == Undefined.Instance)
                     {
-                        case TypeCode.Double:
-                            lval = TypeConverter.ToNumber(_engine.GetValue(lref)) *TypeConverter.ToNumber(rval);
-                            break;
+                        lval = Undefined.Instance;
+                    }
+                    else
+                    {
+                        lval = TypeConverter.ToNumber(lval) * TypeConverter.ToNumber(rval);
                     }
                     break;
 
                 case "/=":
-                    switch (type)
+                    if (lval == Undefined.Instance || rval == Undefined.Instance)
+                    {
+                        lval = Undefined.Instance;
+                    }
+                    else
                     {
-                        case TypeCode.Double:
-                            lval = TypeConverter.ToNumber(_engine.GetValue(lref)) / TypeConverter.ToNumber(rval);
-                            break;
+                        lval = TypeConverter.ToNumber(lval) / TypeConverter.ToNumber(rval);
                     }
                     break;
 
                 case "%=":
-                    switch (type)
+                    if (lval == Undefined.Instance || rval == Undefined.Instance)
+                    {
+                        lval = Undefined.Instance;
+                    }
+                    else
                     {
-                        case TypeCode.Double:
-                            lval = TypeConverter.ToNumber(_engine.GetValue(lref)) % TypeConverter.ToNumber(rval);
-                            break;
+                        lval = TypeConverter.ToNumber(lval) % TypeConverter.ToNumber(rval);
                     }
                     break;
 
                 case "&=":
-                    switch (type)
-                    {
-                        case TypeCode.Double:
-                            lval = TypeConverter.ToInt32(_engine.GetValue(lref)) & TypeConverter.ToInt32(rval);
-                            break;
-                    }
+                    lval = TypeConverter.ToInt32(lval) & TypeConverter.ToInt32(rval);
                     break;
 
                 case "|=":
-                    switch (type)
-                    {
-                        case TypeCode.Double:
-                            lval = TypeConverter.ToInt32(_engine.GetValue(lref)) | TypeConverter.ToInt32(rval);
-                            break;
-                    }
+                    lval = TypeConverter.ToInt32(lval) | TypeConverter.ToInt32(rval);
                     break;
 
                 case "^=":
-                    switch (type)
-                    {
-                        case TypeCode.Double:
-                            lval = TypeConverter.ToInt32(_engine.GetValue(lref)) ^ TypeConverter.ToInt32(rval);
-                            break;
-                    }
+                    lval = TypeConverter.ToInt32(lval) ^ TypeConverter.ToInt32(rval);
                     break;
 
                 case "<<=":
-                    switch (type)
-                    {
-                        case TypeCode.Double:
-                            lval = TypeConverter.ToInt32(_engine.GetValue(lref)) << (int)(TypeConverter.ToUint32(rval) & 0x1F);
-                            break;
-                    }
+                    lval = TypeConverter.ToInt32(lval) << (int)(TypeConverter.ToUint32(rval) & 0x1F);
                     break;
 
                 case ">>>=":
-                    switch (type)
-                    {
-                        case TypeCode.Double:
-                            lval = (uint)TypeConverter.ToInt32(_engine.GetValue(lref)) >> (int)(TypeConverter.ToUint32(rval) & 0x1F);
-                            break;
-                    }
+                    lval = (uint)TypeConverter.ToInt32(lval) >> (int)(TypeConverter.ToUint32(rval) & 0x1F);
                     break;
+                
+                default:
+                    throw new NotImplementedException();
+
             }
 
             _engine.PutValue(lref, lval);
@@ -159,59 +138,61 @@ namespace Jint.Runtime
         {
             object left = _engine.GetValue(EvaluateExpression(expression.Left));
             object right = _engine.GetValue(EvaluateExpression(expression.Right));
-            object value = Undefined.Instance;
-            var type = TypeConverter.GetType(left);
+            object value;
 
             switch (expression.Operator)
             {
                 case "+":
-                    switch (type)
+                    var lprim = TypeConverter.ToPrimitive(left);
+                    var rprim = TypeConverter.ToPrimitive(right);
+                    if (TypeConverter.GetType(lprim) == TypeCode.String ||
+                        TypeConverter.GetType(rprim) == TypeCode.String)
                     {
-                        case TypeCode.String:
-                            value = TypeConverter.ToString(left) + right;
-                            break;
-
-                        case TypeCode.Double:
-                            value = TypeConverter.ToNumber(left) + TypeConverter.ToNumber(right);
-                            break;
+                        value = TypeConverter.ToString(lprim) + TypeConverter.ToString(rprim);
+                    }
+                    else
+                    {
+                        value = TypeConverter.ToNumber(lprim) + TypeConverter.ToNumber(rprim);
                     }
                     break;
                 
                 case "-":
-                    switch (type)
-                    {
-                        case TypeCode.Double:
-                            value = TypeConverter.ToNumber(left) - TypeConverter.ToNumber(right);
-                            break;
-                    }
+                    value = TypeConverter.ToNumber(left) - TypeConverter.ToNumber(right);
                     break;
                 
                 case "*":
-                    switch (type)
+                    if (left == Undefined.Instance || right == Undefined.Instance)
+                    {
+                        value = Undefined.Instance;
+                    }
+                    else
                     {
-                        case TypeCode.Double:
-                            value = TypeConverter.ToNumber(left) * TypeConverter.ToNumber(right);
-                            break;
+                        value = TypeConverter.ToNumber(left) * TypeConverter.ToNumber(right);
                     }
                     break;
                 
                 case "/":
-                    switch (type)
+                    if (left == Undefined.Instance || right == Undefined.Instance)
                     {
-                        case TypeCode.Double:
-                            value = TypeConverter.ToNumber(left) / TypeConverter.ToNumber(right);
-                            break;
+                        value = Undefined.Instance;
+                    }
+                    else
+                    {
+                        value = TypeConverter.ToNumber(left) / TypeConverter.ToNumber(right);
                     }
                     break;
 
                 case "%":
-                    switch (type)
+                    if (left == Undefined.Instance || right == Undefined.Instance)
                     {
-                        case TypeCode.Double:
-                            value = TypeConverter.ToNumber(left) % TypeConverter.ToNumber(right);
-                            break;
+                        value = Undefined.Instance;
+                    }
+                    else
+                    {
+                        value = TypeConverter.ToNumber(left) % TypeConverter.ToNumber(right);
                     }
                     break;
+
                 case "==":
                     value = left.Equals(right);
                     break;
@@ -221,39 +202,19 @@ namespace Jint.Runtime
                     break;
                 
                 case ">":
-                    switch (type)
-                    {
-                        case TypeCode.Double:
-                            value = TypeConverter.ToNumber(left) > TypeConverter.ToNumber(right);
-                            break;
-                    }
+                    value = TypeConverter.ToNumber(left) > TypeConverter.ToNumber(right);
                     break;
 
                 case ">=":
-                    switch (type)
-                    {
-                        case TypeCode.Double:
-                            value = TypeConverter.ToNumber(left) >= TypeConverter.ToNumber(right);
-                            break;
-                    }
+                    value = TypeConverter.ToNumber(left) >= TypeConverter.ToNumber(right);
                     break;
                 
                 case "<":
-                    switch (type)
-                    {
-                        case TypeCode.Double:
-                            value = TypeConverter.ToNumber(left) < TypeConverter.ToNumber(right);
-                            break;
-                    }
+                    value = TypeConverter.ToNumber(left) < TypeConverter.ToNumber(right);
                     break;
                 
                 case "<=":
-                    switch (type)
-                    {
-                        case TypeCode.Double:
-                            value = TypeConverter.ToNumber(left) <= TypeConverter.ToNumber(right);
-                            break;
-                    }
+                    value = TypeConverter.ToNumber(left) <= TypeConverter.ToNumber(right);
                     break;
                 
                 case "===":
@@ -383,9 +344,7 @@ namespace Jint.Runtime
             string identifier = functionExpression.Id != null ? functionExpression.Id.Name : null;
             return new ScriptFunctionInstance(
                 _engine,
-                functionExpression.Body, 
-                identifier, 
-                functionExpression.Parameters.ToArray(), 
+                functionExpression,
                 _engine.Function.Prototype,
                 _engine.Object.Construct(Arguments.Empty),
                 LexicalEnvironment.NewDeclarativeEnvironment(_engine.ExecutionContext.LexicalEnvironment),
@@ -395,8 +354,6 @@ namespace Jint.Runtime
 
         public object EvaluateCallExpression(CallExpression callExpression)
         {
-            /// todo: read the spec as this is made up
-            
             var callee = EvaluateExpression(callExpression.Callee);
             var func = _engine.GetValue(callee);
             object thisObject;

+ 9 - 56
Jint/Runtime/StatementInterpreter.cs

@@ -2,7 +2,6 @@
 using System.Linq;
 using Jint.Native;
 using Jint.Native.Errors;
-using Jint.Native.Function;
 using Jint.Parser.Ast;
 using Jint.Runtime.Environments;
 using Jint.Runtime.References;
@@ -295,9 +294,7 @@ namespace Jint.Runtime
             var val = _engine.EvaluateExpression(withStatement.Object);
             var obj = TypeConverter.ToObject(_engine, _engine.GetValue(val));
             var oldEnv = _engine.ExecutionContext.LexicalEnvironment;
-            var newEnv = LexicalEnvironment.NewObjectEnvironment(obj, oldEnv);
-            // todo: handle ProvideThis
-            // newEnv.ProvideThis = true;
+            var newEnv = LexicalEnvironment.NewObjectEnvironment(obj, oldEnv, true);
             _engine.ExecutionContext.LexicalEnvironment = newEnv;
 
             Completion c;
@@ -449,15 +446,6 @@ 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)
         {
             if (program.Strict)
@@ -465,35 +453,26 @@ namespace Jint.Runtime
                 _engine.Options.Strict();
             }
 
-            EvaluateVariableScope(program);
+            _engine.FunctionDeclarationBindings(program, _engine.ExecutionContext.LexicalEnvironment, true, program.Strict);
+            _engine.VariableDeclarationBinding(program, _engine.ExecutionContext.LexicalEnvironment.Record, true, program.Strict);
+
             return ExecuteStatementList(program.Body);
         }
 
-        public Completion ExecuteVariableDeclaration(VariableDeclaration statement, bool initializeOnly)
+        public Completion ExecuteVariableDeclaration(VariableDeclaration statement)
         {
-            var env = _engine.ExecutionContext.VariableEnvironment.Record;
+            var env = _engine.ExecutionContext.LexicalEnvironment.Record;
             object value = Undefined.Instance;
 
             foreach (var declaration in statement.Declarations)
             {
                 var dn = declaration.Id.Name;
-                if (!initializeOnly)
+                if (declaration.Init != null)
                 {
-                    var varAlreadyDeclared = env.HasBinding(dn);
-                    if (!varAlreadyDeclared)
-                    {
-                        env.CreateMutableBinding(dn, true);
-                    }
+                    value = _engine.GetValue(_engine.EvaluateExpression(declaration.Init));
                 }
-                else
-                {
-                    if (declaration.Init != null)
-                    {
-                        value = _engine.GetValue(_engine.EvaluateExpression(declaration.Init));
-                    }
 
-                    env.SetMutableBinding(dn, value, false);
-                }
+                env.SetMutableBinding(dn, value, false);
             }
 
             return new Completion(Completion.Normal, value, null);
@@ -504,35 +483,9 @@ namespace Jint.Runtime
             return ExecuteStatementList(blockStatement.Body);
         }
 
-        public Completion ExecuteFunctionDeclaration(FunctionDeclaration functionDeclaration)
-        {
-            // create function objects
-            // http://www.ecma-international.org/ecma-262/5.1/#sec-13.2
-
-            var identifier = functionDeclaration.Id.Name;
-
-            // todo: should be declared in the current context
-            _engine.Global.Set(
-                identifier, 
-                new ScriptFunctionInstance(
-                    _engine,
-                    functionDeclaration.Body, 
-                    identifier, 
-                    functionDeclaration.Parameters.ToArray(),
-                    _engine.Function.Prototype,
-                    _engine.Object.Construct(Arguments.Empty),
-                    LexicalEnvironment.NewDeclarativeEnvironment(_engine.ExecutionContext.LexicalEnvironment),
-                    functionDeclaration.Strict
-                )
-            );
-
-            return new Completion(Completion.Normal, null, null);
-        }
-
         public Completion ExecuteDebuggerStatement(DebuggerStatement debuggerStatement)
         {
             throw new System.NotImplementedException();
         }
-
     }
 }