Преглед изворни кода

Implementing Array constructor

Sebastien Ros пре 12 година
родитељ
комит
fb00fd7ddc

+ 32 - 3
Jint.Tests/Runtime/EngineTests.cs

@@ -196,14 +196,43 @@ namespace Jint.Tests.Runtime
             ");
         }
 
-/*
+
+        [Fact]
+        public void ShouldConstructArray()
+        {
+            var engine = RunTest(@"
+                var o = [];
+                assert(o.length == 0);
+            ");
+        }
+
+        [Fact]
+        public void ArrayPushShouldIncrementLength()
+        {
+            var engine = RunTest(@"
+                var o = [];
+                o.push(1);
+                assert(o.length == 1);
+            ");
+        }
+
         [Fact]
-        public void ()
+        public void ArrayConstructor()
         {
             var engine = RunTest(@"
+                var o = [];
+                assert(o.constructor == Array);
+                assert(o.hasOwnProperty('constructor') == false);
             ");
         }
-*/
+        /*
+                        [Fact]
+                        public void ()
+                        {
+                            var engine = RunTest(@"
+                            ");
+                        }
+                */
 
     }
 }

+ 13 - 5
Jint/Engine.cs

@@ -1,6 +1,7 @@
 using System;
 using System.Collections.Generic;
 using Jint.Native;
+using Jint.Native.Array;
 using Jint.Native.Errors;
 using Jint.Native.Function;
 using Jint.Native.Object;
@@ -31,18 +32,20 @@ namespace Jint
         {
             _executionContexts = new Stack<ExecutionContext>();
 
-            var rootObject = new ObjectInstance(null);
-            var rootFunction = new FunctionShim(rootObject, null, null);
+            RootObject = new ObjectInstance(null);
+            RootFunction = new FunctionShim(this, RootObject, null, null);
 
-            Object = new ObjectConstructor(rootFunction);
+            Object = new ObjectConstructor(this);
             Global = new ObjectInstance(Object);
-            Function = new FunctionConstructor(rootFunction);
+            Function = new FunctionConstructor(this);
+            Array = new ArrayConstructor(this);
 
             //Object.Prototype.DefineOwnProperty("hasOwnProperty", new DataDescriptor(new BuiltInPropertyWrapper((Func<ObjectInstance, string, bool>)ObjectConstructor.HasOwnProperty, rootObject)), false);
             //Object.Prototype.DefineOwnProperty("toString", new DataDescriptor(new BuiltInPropertyWrapper((Func<ObjectInstance, string>)ObjectConstructor.ToString, rootObject)), false);
 
             Global.Set("Object", Object);
             Global.Set("Function", Function);
+            Global.Set("Array", Array);
 
             // create the global environment http://www.ecma-international.org/ecma-262/5.1/#sec-10.2.3
             _globalEnvironment = LexicalEnvironment.NewObjectEnvironment(Global, null);
@@ -61,7 +64,7 @@ namespace Jint
             {
                 foreach (var entry in Options.GetDelegates())
                 {
-                    Global.DefineOwnProperty(entry.Key, new DataDescriptor(new DelegateWrapper(entry.Value, rootFunction)), false);
+                    Global.DefineOwnProperty(entry.Key, new DataDescriptor(new DelegateWrapper(this, entry.Value, RootFunction)), false);
                 }
             }
 
@@ -69,9 +72,13 @@ namespace Jint
             _expressions = new ExpressionInterpreter(this);
         }
 
+        public ObjectInstance RootObject { get; private set; }
+        public FunctionInstance RootFunction { get; private set; }
+
         public ObjectInstance Global { get; private set; }
         public ObjectConstructor Object { get; private set; }
         public FunctionConstructor Function { get; private set; }
+        public ArrayConstructor Array { get; private set; }
 
         public ExecutionContext CurrentExecutionContext { get { return _executionContexts.Peek(); } }
 
@@ -181,6 +188,7 @@ namespace Jint
                     _result = _expressions.EvaluateAssignmentExpression(expression.As<AssignmentExpression>());
                     break;
                 case SyntaxNodes.ArrayExpression:
+                    _result = _expressions.EvaluateArrayExpression(expression.As<ArrayExpression>());
                     break;
                 case SyntaxNodes.BinaryExpression:
                     _result = _expressions.EvaluateBinaryExpression(expression.As<BinaryExpression>());

+ 3 - 1
Jint/Jint.csproj

@@ -44,6 +44,8 @@
     <Reference Include="System.Xml" />
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="Native\Array\ArrayConstructor.cs" />
+    <Compile Include="Native\Array\ArrayInstance.cs" />
     <Compile Include="Native\Errors\TypeError.cs" />
     <Compile Include="Native\Errors\ReferenceError.cs" />
     <Compile Include="Native\Function\ScriptFunctionInstance.cs" />
@@ -121,7 +123,7 @@
     <Compile Include="Runtime\Interop\BuiltInPropertyWrapper.cs" />
     <Compile Include="Runtime\Interop\DelegateWrapper.cs" />
     <Compile Include="Options.cs" />
-    <Compile Include="Runtime\Descriptors\MethodProperty.cs" />
+    <Compile Include="Runtime\Descriptors\Specialized\MethodProperty.cs" />
     <Compile Include="Engine.cs" />
     <Compile Include="Runtime\Descriptors\PropertyDescriptor.cs" />
     <Compile Include="Runtime\References\Reference.cs" />

+ 51 - 0
Jint/Native/Array/ArrayConstructor.cs

@@ -0,0 +1,51 @@
+using System;
+using Jint.Native.Function;
+using Jint.Native.Object;
+using Jint.Runtime.Descriptors;
+using Jint.Runtime.Descriptors.Specialized;
+using Jint.Runtime.Interop;
+
+namespace Jint.Native.Array
+{
+    public class ArrayConstructor : FunctionInstance
+    {
+        private readonly Engine _engine;
+
+        public ArrayConstructor(Engine engine) :  base(engine, new ObjectInstance(engine.RootFunction), null, null)
+        {
+            _engine = engine;
+
+            // the constructor is the function constructor of an object
+            this.Prototype.DefineOwnProperty("constructor", new DataDescriptor(this) { Writable = true, Enumerable = false, Configurable = false }, false);
+            this.Prototype.DefineOwnProperty("prototype", new DataDescriptor(this.Prototype) { Writable = true, Enumerable = false, Configurable = false }, false);
+
+            // Array method
+            this.Prototype.DefineOwnProperty("push", new DataDescriptor(new BuiltInPropertyWrapper(engine, (Action<ArrayInstance, object>)Push, engine.RootFunction)), false);
+        }
+
+        public override dynamic Call(object thisObject, dynamic[] arguments)
+        {
+            return Construct(arguments);
+        }
+
+        public virtual ObjectInstance Construct(dynamic[] arguments)
+        {
+            var instance = new ArrayInstance(Prototype);
+
+            instance.DefineOwnProperty("length", new DataDescriptor((double)0), false);
+
+            foreach (var arg in arguments)
+            {
+                instance.Push(arg);
+            }
+
+            return instance;
+        }
+
+        private static void Push(ArrayInstance thisObject, object o)
+        {
+            thisObject.Push(o);
+            thisObject.Set("length", (double)thisObject.Get("length") + 1);
+        }
+    }
+}

+ 33 - 0
Jint/Native/Array/ArrayInstance.cs

@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Jint.Native.Object;
+
+namespace Jint.Native.Array
+{
+    public class ArrayInstance : ObjectInstance
+    {
+        private readonly List<object> _array = new List<object>();
+ 
+        public ArrayInstance(ObjectInstance prototype) : base(prototype)
+        {
+        }
+
+        public override string Class
+        {
+            get
+            {
+                return "Array";
+            }
+        }
+
+        public int Length { get { return _array.Count; } }
+
+        public void Push(object o)
+        {
+            _array.Add(o);
+        }
+    }
+}

+ 6 - 5
Jint/Native/Function/FunctionConstructor.cs

@@ -14,25 +14,26 @@ namespace Jint.Runtime
     /// </summary>
     public class FunctionConstructor : FunctionInstance
     {
+        private readonly Engine _engine;
         private readonly IEnumerable<Identifier> _parameters;
 
-        public FunctionConstructor(ObjectInstance prototype)
-            : base(prototype, null, null)
+        public FunctionConstructor(Engine engine)
+            : base(engine, engine.RootFunction, null, null)
         {
+            _engine = engine;
             // http://www.ecma-international.org/ecma-262/5.1/#sec-13.2
 
             Extensible = true;
-            
         }
 
-        public override dynamic Call(Engine engine, object thisObject, dynamic[] arguments)
+        public override dynamic Call(object thisObject, dynamic[] arguments)
         {
             return Construct(arguments);
         }
 
         public virtual ObjectInstance Construct(dynamic[] arguments)
         {
-            var instance = new FunctionShim(Prototype, null, null);
+            var instance = new FunctionShim(_engine, Prototype, null, null);
             instance.DefineOwnProperty("constructor", new DataDescriptor(Prototype) { Writable = true, Enumerable = false, Configurable = false }, false);
 
             return instance;

+ 5 - 2
Jint/Native/Function/FunctionInstance.cs

@@ -8,8 +8,11 @@ namespace Jint.Native.Function
 {
     public abstract class FunctionInstance : ObjectInstance
     {
-        protected FunctionInstance(ObjectInstance prototype, Identifier[] parameters, LexicalEnvironment scope) : base(prototype)
+        private readonly Engine _engine;
+
+        protected FunctionInstance(Engine engine, ObjectInstance prototype, Identifier[] parameters, LexicalEnvironment scope) : base(prototype)
         {
+            _engine = engine;
             Parameters = parameters;
             Scope = scope;
         }
@@ -21,7 +24,7 @@ namespace Jint.Native.Function
         /// <param name="state"></param>
         /// <param name="arguments"></param>
         /// <returns></returns>
-        public abstract dynamic Call(Engine interpreter, object thisObject, dynamic[] arguments);
+        public abstract dynamic Call(object thisObject, dynamic[] arguments);
 
         public LexicalEnvironment Scope { get; private set; }
         public Identifier[] Parameters { get; private set; }

+ 5 - 2
Jint/Native/Function/FunctionShim.cs

@@ -7,11 +7,14 @@ namespace Jint.Native.Function
 {
     public class FunctionShim : FunctionInstance
     {
-        public FunctionShim(ObjectInstance prototype, Identifier[] parameters, LexicalEnvironment scope) : base(prototype, parameters, scope)
+        private readonly Engine _engine;
+
+        public FunctionShim(Engine engine, ObjectInstance prototype, Identifier[] parameters, LexicalEnvironment scope) : base(engine, prototype, parameters, scope)
         {
+            _engine = engine;
         }
 
-        public override dynamic Call(Engine interpreter, object thisObject, dynamic[] arguments)
+        public override dynamic Call(object thisObject, dynamic[] arguments)
         {
             return Undefined.Instance;
         }

+ 11 - 9
Jint/Native/Function/ScriptFunctionInstance.cs

@@ -14,14 +14,16 @@ namespace Jint.Runtime
     /// </summary>
     public class ScriptFunctionInstance : FunctionInstance
     {
+        private readonly Engine _engine;
         private readonly Statement _body;
         private readonly IEnumerable<Identifier> _parameters;
         
-        public ScriptFunctionInstance(Statement body, string name, Identifier[] parameters, ObjectInstance instancePrototype, ObjectInstance functionPrototype, LexicalEnvironment scope)
-            : base(instancePrototype, parameters, scope)
+        public ScriptFunctionInstance(Engine engine, Statement body, string name, Identifier[] parameters, ObjectInstance instancePrototype, ObjectInstance functionPrototype, LexicalEnvironment scope)
+            : base(engine, instancePrototype, parameters, scope)
         {
             // http://www.ecma-international.org/ecma-262/5.1/#sec-13.2
 
+            _engine = engine;
             _body = body;
             _parameters = parameters;
             Extensible = true;
@@ -33,7 +35,7 @@ namespace Jint.Runtime
             DefineOwnProperty("prototype", new DataDescriptor(functionPrototype) { Writable = true, Enumerable = true, Configurable = true }, false);
         }
 
-        public override dynamic Call(Engine engine, object thisObject, dynamic[] arguments)
+        public override dynamic Call(object thisObject, dynamic[] arguments)
         {
             // todo: http://www.ecma-international.org/ecma-262/5.1/#sec-13.2.1
 
@@ -42,14 +44,14 @@ namespace Jint.Runtime
             object thisArg;
             if (thisObject == Undefined.Instance || thisObject == Null.Instance)
             {
-                thisArg = engine.Global;
+                thisArg = _engine.Global;
             }
             else
             {
                 thisArg = thisObject;
             }
 
-            engine.EnterExecutionContext(localEnv, localEnv, thisArg);
+            _engine.EnterExecutionContext(localEnv, localEnv, thisArg);
 
             var env = localEnv.Record;
 
@@ -59,10 +61,10 @@ namespace Jint.Runtime
                 env.SetMutableBinding(parameter.Name, i < arguments.Length ? arguments[i++] : Undefined.Instance, false);
             }
 
-            engine.ExecuteStatement(_body);
-            var result = engine.CurrentExecutionContext.Return;
+            _engine.ExecuteStatement(_body);
+            var result = _engine.CurrentExecutionContext.Return;
 
-            engine.LeaveExecutionContext();
+            _engine.LeaveExecutionContext();
 
             return result;
         }
@@ -71,7 +73,7 @@ namespace Jint.Runtime
         {
             /// todo: http://www.ecma-international.org/ecma-262/5.1/#sec-13.2.2
 
-            var instance = new FunctionShim(this.Prototype, null, null);
+            var instance = new FunctionShim(_engine, this.Prototype, null, null);
             return instance;
         }
     }

+ 7 - 4
Jint/Native/Object/ObjectConstructor.cs

@@ -8,13 +8,16 @@ namespace Jint.Native.Object
 {
     public class ObjectConstructor : FunctionInstance
     {
-        public ObjectConstructor(ObjectInstance prototype) : base(prototype, null, null)
+        private readonly Engine _engine;
+
+        public ObjectConstructor(Engine engine) : base(engine, engine.RootFunction, null, null)
         {
-            prototype.DefineOwnProperty("hasOwnProperty", new DataDescriptor(new BuiltInPropertyWrapper((Func<ObjectInstance, string, bool>)HasOwnProperty, prototype)), false);
-            prototype.DefineOwnProperty("toString", new DataDescriptor(new BuiltInPropertyWrapper((Func<ObjectInstance, string>)ToString, prototype)), false);
+            _engine = engine;
+            engine.RootFunction.DefineOwnProperty("hasOwnProperty", new DataDescriptor(new BuiltInPropertyWrapper(engine, (Func<ObjectInstance, string, bool>)HasOwnProperty, engine.RootFunction)), false);
+            engine.RootFunction.DefineOwnProperty("toString", new DataDescriptor(new BuiltInPropertyWrapper(engine, (Func<ObjectInstance, string>)ToString, engine.RootFunction)), false);
         }
 
-        public override dynamic Call(Engine interpreter, object thisObject, dynamic[] arguments)
+        public override dynamic Call(object thisObject, dynamic[] arguments)
         {
             return Undefined.Instance;
         }

+ 1 - 0
Jint/Runtime/Descriptors/DataDescriptor.cs

@@ -7,6 +7,7 @@
         public DataDescriptor(object value)
         {
             _value = value;
+            Writable = true;
         }
 
         public override object Get()

+ 0 - 34
Jint/Runtime/Descriptors/MethodProperty.cs

@@ -1,34 +0,0 @@
-using System;
-using Jint.Runtime.Interop;
-
-namespace Jint.Runtime.Descriptors
-{
-    public class MethodProperty : PropertyDescriptor
-    {
-        private readonly Delegate _d;
-
-        public MethodProperty(Delegate d)
-        {
-            _d = d;
-        }
-
-        public override object Get()
-        {
-            return new DelegateWrapper(_d, null);
-        }
-
-        public override void Set(object value)
-        {
-        }
-
-        public override bool IsAccessorDescriptor()
-        {
-            return false;
-        }
-
-        public override bool IsDataDescriptor()
-        {
-            return false;
-        }
-    }
-}

+ 38 - 0
Jint/Runtime/Descriptors/Specialized/MethodProperty.cs

@@ -0,0 +1,38 @@
+using System;
+
+namespace Jint.Runtime.Descriptors.Specialized
+{
+    public class MethodProperty : PropertyDescriptor
+    {
+        private readonly Engine _engine;
+        private readonly Action<object> _setter;
+        private readonly Func<object> _getter;
+
+        public MethodProperty(Engine engine, Func<object> getter, Action<object> setter)
+        {
+            _engine = engine;
+            _setter = setter;
+            _getter = getter;
+        }
+
+        public override object Get()
+        {
+            return _getter();
+        }
+
+        public override void Set(object value)
+        {
+            _setter(value);
+        }
+
+        public override bool IsAccessorDescriptor()
+        {
+            return true;
+        }
+
+        public override bool IsDataDescriptor()
+        {
+            return false;
+        }
+    }
+}

+ 14 - 4
Jint/Runtime/ExpressionIntepreter.cs

@@ -2,7 +2,6 @@
 using System.Linq;
 using Jint.Native;
 using Jint.Native.Function;
-using Jint.Native.Object;
 using Jint.Parser.Ast;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Environments;
@@ -184,6 +183,7 @@ namespace Jint.Runtime
         {
             string identifier = functionExpression.Id != null ? functionExpression.Id.Name : null;
             return new ScriptFunctionInstance(
+                _engine,
                 functionExpression.Body, 
                 identifier, 
                 functionExpression.Parameters.ToArray(), 
@@ -203,13 +203,13 @@ namespace Jint.Runtime
             {
                 // x.hasOwnProperty
                 FunctionInstance callee = _engine.GetValue(r);
-                return callee.Call(_engine, r.GetBase(), arguments.ToArray());
+                return callee.Call(r.GetBase(), arguments.ToArray());
             }
             else
             {
                 // assert(...)
                 FunctionInstance callee = _engine.GetValue(r);
-                return callee.Call(_engine, _engine.CurrentExecutionContext.ThisBinding, arguments.ToArray());
+                return callee.Call(_engine.CurrentExecutionContext.ThisBinding, arguments.ToArray());
             }
 
         }
@@ -264,7 +264,17 @@ namespace Jint.Runtime
             var instance = callee.Construct(arguments);
 
             // initializes the new instance by executing the Function
-            callee.Call(_engine, instance, arguments.ToArray());
+            callee.Call(instance, arguments.ToArray());
+
+            return instance;
+        }
+
+        public dynamic EvaluateArrayExpression(ArrayExpression arrayExpression)
+        {
+            var arguments = arrayExpression.Elements.Select(EvaluateExpression).ToArray();
+
+            // construct the new instance using the Function's constructor method
+            var instance = _engine.Array.Construct(arguments);
 
             return instance;
         }

+ 6 - 4
Jint/Runtime/Interop/BuiltInPropertyWrapper.cs

@@ -11,18 +11,20 @@ namespace Jint.Runtime.Interop
     /// </summary>
     public class BuiltInPropertyWrapper : FunctionInstance
     {
+        private readonly Engine _engine;
         private readonly Delegate _d;
 
-        public BuiltInPropertyWrapper(Delegate d, ObjectInstance prototype)
-            : base(prototype, null, null)
+        public BuiltInPropertyWrapper(Engine engine, Delegate d, ObjectInstance prototype)
+            : base(engine, prototype, null, null)
         {
+            _engine = engine;
             _d = d;
         }
 
-        public override dynamic Call(Engine engine, object thisObject, dynamic[] arguments)
+        public override dynamic Call(object thisObject, dynamic[] arguments)
         {
             // initialize Return flag
-            engine.CurrentExecutionContext.Return = Undefined.Instance;
+            _engine.CurrentExecutionContext.Return = Undefined.Instance;
 
             // built-in static method must have their first parameter as 'this'
             var allArguments = new object[arguments.Length + 1];

+ 6 - 4
Jint/Runtime/Interop/DelegateWrapper.cs

@@ -11,21 +11,23 @@ namespace Jint.Runtime.Interop
     /// </summary>
     public class DelegateWrapper : FunctionInstance
     {
+        private readonly Engine _engine;
         private readonly Delegate _d;
 
-        public DelegateWrapper(Delegate d, ObjectInstance prototype) : base(prototype, null, null)
+        public DelegateWrapper(Engine engine, Delegate d, ObjectInstance prototype) : base(engine, prototype, null, null)
         {
+            _engine = engine;
             _d = d;
         }
 
-        public override dynamic Call(Engine engine, object thisObject, dynamic[] arguments)
+        public override dynamic Call(object thisObject, dynamic[] arguments)
         {
             // initialize Return flag
-            engine.CurrentExecutionContext.Return = Undefined.Instance;
+            _engine.CurrentExecutionContext.Return = Undefined.Instance;
 
             _d.DynamicInvoke(arguments);
 
-            return engine.CurrentExecutionContext.Return;
+            return _engine.CurrentExecutionContext.Return;
         }
     }
 }

+ 1 - 0
Jint/Runtime/StatementInterpreter.cs

@@ -109,6 +109,7 @@ namespace Jint.Runtime
             _engine.Global.Set(
                 identifier, 
                 new ScriptFunctionInstance(
+                    _engine,
                     functionDeclaration.Body, 
                     identifier, 
                     functionDeclaration.Parameters.ToArray(),