[banshee] [JavaScriptCore] Initial function support



commit a070c7187c7533d707419d8da62919ff496cf43a
Author: Aaron Bockover <abockover novell com>
Date:   Mon Oct 25 13:20:39 2010 -0400

    [JavaScriptCore] Initial function support
    
    Supports declaring 'native' functions (body written in .NET) and
    accessible from a JSC context. Also JSObject now sports the
    CallAsFunction method, enabling invoking JSC functions from .NET.

 .../Banshee.WebBrowser/Banshee.WebBrowser.csproj   |    2 +
 src/Core/Banshee.WebBrowser/Makefile.am            |    2 +
 .../Ossifer.JavaScriptCore/JSFunction.cs           |   73 ++++++++++++
 .../Ossifer.JavaScriptCore/JSObject.cs             |   22 ++++
 .../Tests/JSFunctionTests.cs                       |  120 ++++++++++++++++++++
 5 files changed, 219 insertions(+), 0 deletions(-)
---
diff --git a/src/Core/Banshee.WebBrowser/Banshee.WebBrowser.csproj b/src/Core/Banshee.WebBrowser/Banshee.WebBrowser.csproj
index ae0465f..19d59c6 100644
--- a/src/Core/Banshee.WebBrowser/Banshee.WebBrowser.csproj
+++ b/src/Core/Banshee.WebBrowser/Banshee.WebBrowser.csproj
@@ -125,5 +125,7 @@
     <Compile Include="Ossifer.JavaScriptCore\Tests\JSStringTests.cs" />
     <Compile Include="Ossifer.JavaScriptCore\Tests\JSObjectTests.cs" />
     <Compile Include="Ossifer.JavaScriptCore\JSPropertyNameArray.cs" />
+    <Compile Include="Ossifer.JavaScriptCore\JSFunction.cs" />
+    <Compile Include="Ossifer.JavaScriptCore\Tests\JSFunctionTests.cs" />
   </ItemGroup>
 </Project>
diff --git a/src/Core/Banshee.WebBrowser/Makefile.am b/src/Core/Banshee.WebBrowser/Makefile.am
index bee71ed..d571206 100644
--- a/src/Core/Banshee.WebBrowser/Makefile.am
+++ b/src/Core/Banshee.WebBrowser/Makefile.am
@@ -21,6 +21,7 @@ SOURCES =  \
 	Ossifer.JavaScriptCore/JSClassDefinition.cs \
 	Ossifer.JavaScriptCore/JSContext.cs \
 	Ossifer.JavaScriptCore/JSException.cs \
+	Ossifer.JavaScriptCore/JSFunction.cs \
 	Ossifer.JavaScriptCore/JSObject.cs \
 	Ossifer.JavaScriptCore/JSPropertyAttribute.cs \
 	Ossifer.JavaScriptCore/JSPropertyNameAccumulator.cs \
@@ -29,6 +30,7 @@ SOURCES =  \
 	Ossifer.JavaScriptCore/JSType.cs \
 	Ossifer.JavaScriptCore/JSValue.cs \
 	Ossifer.JavaScriptCore/ManagedPropertyBagClass.cs \
+	Ossifer.JavaScriptCore/Tests/JSFunctionTests.cs \
 	Ossifer.JavaScriptCore/Tests/JSObjectTests.cs \
 	Ossifer.JavaScriptCore/Tests/JSStringTests.cs \
 	Ossifer.JavaScriptCore/Tests/JSValueTests.cs
diff --git a/src/Core/Banshee.WebBrowser/Ossifer.JavaScriptCore/JSFunction.cs b/src/Core/Banshee.WebBrowser/Ossifer.JavaScriptCore/JSFunction.cs
new file mode 100644
index 0000000..0b4a63a
--- /dev/null
+++ b/src/Core/Banshee.WebBrowser/Ossifer.JavaScriptCore/JSFunction.cs
@@ -0,0 +1,73 @@
+// 
+// JSFunction.cs
+// 
+// Author:
+//   Aaron Bockover <abockover novell com>
+// 
+// Copyright 2010 Novell, Inc.
+// 
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+// 
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.Runtime.InteropServices;
+
+namespace Ossifer.JavaScriptCore
+{
+    public delegate JSValue JSFunctionHandler (JSFunction function, JSObject thisObject, JSValue [] args);
+
+    public class JSFunction : JSObject
+    {
+        [DllImport (JSContext.NATIVE_IMPORT)]
+        private static extern IntPtr JSObjectMakeFunctionWithCallback (IntPtr ctx, JSString name,
+            CallAsFunctionCallback callAsFunction);
+
+        #pragma warning disable 0414
+        private JSObject.CallAsFunctionCallback native_callback;
+        #pragma warning restore 0414
+
+        private JSFunctionHandler handler;
+
+        public JSFunction (JSContext context, string name, JSFunctionHandler handler) : base (context, IntPtr.Zero)
+        {
+            this.handler = handler;
+            var native_name = JSString.New (name);
+            Raw = JSObjectMakeFunctionWithCallback (context.Raw, native_name,
+                native_callback = new JSObject.CallAsFunctionCallback (JSCallback));
+            native_name.Release ();
+        }
+
+        private IntPtr JSCallback (IntPtr ctx, IntPtr function, IntPtr thisObject,
+            IntPtr argumentCount, IntPtr arguments, ref IntPtr exception)
+        {
+            var context = new JSContext (ctx);
+
+            if (handler == null) {
+                return JSValue.NewUndefined (context).Raw;
+            }
+
+            var args = new JSValue[argumentCount.ToInt32 ()];
+
+            for (int i = 0; i < args.Length; i++) {
+                args[i] = new JSValue (context, Marshal.ReadIntPtr (arguments, i * IntPtr.Size));
+            }
+
+            return handler (this, new JSObject (context, thisObject), args).Raw;
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/Core/Banshee.WebBrowser/Ossifer.JavaScriptCore/JSObject.cs b/src/Core/Banshee.WebBrowser/Ossifer.JavaScriptCore/JSObject.cs
index 980f0a0..f387b1d 100644
--- a/src/Core/Banshee.WebBrowser/Ossifer.JavaScriptCore/JSObject.cs
+++ b/src/Core/Banshee.WebBrowser/Ossifer.JavaScriptCore/JSObject.cs
@@ -186,5 +186,27 @@ namespace Ossifer.JavaScriptCore
         public bool IsFunction {
             get { return JSObjectIsFunction (Context.Raw, Raw); }
         }
+
+        [DllImport (JSContext.NATIVE_IMPORT)]
+        private static extern IntPtr JSObjectCallAsFunction (IntPtr ctx, IntPtr obj, IntPtr thisObject,
+            IntPtr argumentCount, IntPtr [] arguments, ref IntPtr exception);
+
+        public JSValue CallAsFunction (JSObject thisObject, JSValue [] args)
+        {
+            var exception = IntPtr.Zero;
+            var args_native = new IntPtr[args.Length];
+
+            for (int i = 0; i < args.Length; i++) {
+                args_native[i] = args[i].Raw;
+            }
+
+            var result = new JSValue (Context.Raw, JSObjectCallAsFunction (Context.Raw, Raw,
+                thisObject == null ? IntPtr.Zero : thisObject.Raw, new IntPtr (args.Length),
+                args_native, ref exception));
+
+            JSException.Proxy (Context, exception);
+
+            return result;
+        }
     }
 }
diff --git a/src/Core/Banshee.WebBrowser/Ossifer.JavaScriptCore/Tests/JSFunctionTests.cs b/src/Core/Banshee.WebBrowser/Ossifer.JavaScriptCore/Tests/JSFunctionTests.cs
new file mode 100644
index 0000000..5ec95ef
--- /dev/null
+++ b/src/Core/Banshee.WebBrowser/Ossifer.JavaScriptCore/Tests/JSFunctionTests.cs
@@ -0,0 +1,120 @@
+// 
+// JSFunctionTests.cs
+// 
+// Author:
+//   Aaron Bockover <abockover novell com>
+// 
+// Copyright 2010 Novell, Inc.
+// 
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+// 
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#if ENABLE_TESTS
+
+using System;
+using System.Collections.Generic;
+using NUnit.Framework;
+
+namespace Ossifer.JavaScriptCore.Tests
+{
+    [TestFixture]
+    public class JSFunctionTests
+    {
+        private JSContext context;
+
+        [TestFixtureSetUp]
+        public void Init ()
+        {
+            context = new JSContext ();
+        }
+
+        [Test]
+        public void TestArguments ()
+        {
+            var r = new List<JSValue> ();
+
+            context.GlobalObject.SetProperty ("go", new JSFunction (context, "go", (fn, ths, args) => {
+                r.AddRange (args);
+                return new JSValue (fn.Context, 42);
+            }));
+
+            var go = context.GlobalObject.GetProperty ("go");
+            Assert.AreEqual (JSType.Object, go.JSType);
+            Assert.AreEqual ("function go() {\n    [native code]\n}", go.ToString ());
+
+            context.EvaluateScript ("this.go = go (1, 'hi', false, go ({a:'str', b:go ()}, go (0.272, true, go)))");
+
+            Assert.AreEqual (JSType.Number, r[0].JSType);
+            Assert.AreEqual (0.272, r[0].NumberValue);
+            Assert.AreEqual (JSType.Boolean, r[1].JSType);
+            Assert.AreEqual (true, r[1].BooleanValue);
+            Assert.AreEqual (JSType.Object, r[2].JSType);
+            Assert.AreEqual ("function go() {\n    [native code]\n}", r[2].ToString ());
+
+            Assert.AreEqual (JSType.Object, r[3].JSType);
+            Assert.AreEqual ("{\"a\":\"str\",\"b\":42}", r[3].ToString ());
+            Assert.AreEqual (JSType.Number, r[4].JSType);
+            Assert.AreEqual (42, r[4].NumberValue);
+
+            Assert.AreEqual (JSType.Number, r[5].JSType);
+            Assert.AreEqual (1, r[5].NumberValue);
+            Assert.AreEqual (JSType.String, r[6].JSType);
+            Assert.AreEqual ("hi", r[6].StringValue);
+            Assert.AreEqual (JSType.Boolean, r[7].JSType);
+            Assert.AreEqual (false, r[7].BooleanValue);
+
+            go = context.GlobalObject.GetProperty ("go");
+            Assert.AreEqual (JSType.Number, go.JSType);
+            Assert.AreEqual (42, go.NumberValue);
+        }
+
+        private JSValue Fib_1 (JSFunction function, JSObject @this, JSValue [] args)
+        {
+            return args[0].NumberValue <= 1 ? new JSValue (@this.Context, 1) : new JSValue (@this.Context,
+                Fib_1 (function, @this, new [] { new JSValue (@this.Context, args[0].NumberValue - 1) }).NumberValue +
+                Fib_1 (function, @this, new [] { new JSValue (@this.Context, args[0].NumberValue - 2) }).NumberValue
+            );
+        }
+
+        private int Fib_2 (int n)
+        {
+            return n <= 1 ? 1 : Fib_2 (n - 1) + Fib_2 (n - 2);
+        }
+
+        [Test]
+        public void TestRecursion ()
+        {
+            context.GlobalObject.SetProperty ("fib", new JSFunction (context, null, Fib_1));
+            for (int i = 0; i <= 25; i++) {
+                context.EvaluateScript ("this.fib_" + i + " = fib (" + i + ")");
+                Assert.AreEqual (Fib_2 (i), context.GlobalObject.GetProperty ("fib_" + i).NumberValue);
+            }
+        }
+
+        [Test]
+        public void TestRecursionNoScript ()
+        {
+            var fib = new JSFunction (context, null, Fib_1);
+            for (int i = 0; i <= 25; i++) {
+                Assert.AreEqual (Fib_2 (i), fib.CallAsFunction (null, new [] { new JSValue (context, i) }).NumberValue);
+            }
+        }
+    }
+}
+
+#endif
\ No newline at end of file



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]