diff --git a/Source/Noesis.Javascript/JavascriptContext.h b/Source/Noesis.Javascript/JavascriptContext.h index 654f77d..0bcc7cc 100644 --- a/Source/Noesis.Javascript/JavascriptContext.h +++ b/Source/Noesis.Javascript/JavascriptContext.h @@ -115,6 +115,7 @@ public ref class JavascriptContext: public System::IDisposable //////////////////////////////////////////////////////////// // Data members //////////////////////////////////////////////////////////// + protected: // By entering an isolate before using a context, we can have multiple // contexts used simultaneously in different threads. diff --git a/Source/Noesis.Javascript/JavascriptFunction.cpp b/Source/Noesis.Javascript/JavascriptFunction.cpp new file mode 100644 index 0000000..94ac8dc --- /dev/null +++ b/Source/Noesis.Javascript/JavascriptFunction.cpp @@ -0,0 +1,93 @@ +#include "JavascriptFunction.h" +#include "JavascriptInterop.h" +#include "JavascriptContext.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +namespace Noesis { namespace Javascript { + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +JavascriptFunction::JavascriptFunction( v8::Handle iFunction, JavascriptContext^ context) +{ + if (!iFunction->IsFunction()) + throw gcnew System::ArgumentException("Trying to use non-function as function"); + + if(!context) + throw gcnew System::ArgumentException("Must provide a JavascriptContext"); + + mFuncHandle = new Persistent(); + *mFuncHandle = Persistent::New(Handle::Cast(iFunction)); + mContext = context; +} + +JavascriptFunction::~JavascriptFunction() +{ + if(mFuncHandle) + { + JavascriptScope scope(mContext); + mFuncHandle->Dispose(); + delete mFuncHandle; + mFuncHandle = nullptr; + } + System::GC::SuppressFinalize(this); +} + +JavascriptFunction::!JavascriptFunction() +{ + if(mFuncHandle) + { + JavascriptScope scope(mContext); + mFuncHandle->Dispose(); + delete mFuncHandle; + mFuncHandle = nullptr; + } +} + +System::Object^ JavascriptFunction::Call(... cli::array^ args) +{ + JavascriptScope scope(mContext); + HandleScope handleScope; + + Handle global = (*mFuncHandle)->CreationContext()->Global(); + + int argc = args->Length; + Handle *argv = new Handle[argc]; + for (int i = 0; i < argc; i++) + { + argv[i] = JavascriptInterop::ConvertToV8(args[i]); + } + + Local retVal = (*mFuncHandle)->Call(global, argc, argv); + + delete [] argv; + return JavascriptInterop::ConvertFromV8(retVal); +} + +bool JavascriptFunction::operator==( JavascriptFunction^ func1, JavascriptFunction^ func2 ) +{ + if(ReferenceEquals(func2, nullptr)) { + return false; + } + Handle jsFuncPtr1 = *(func1->mFuncHandle); + Handle jsFuncPtr2 = *(func2->mFuncHandle); + + return jsFuncPtr1->Equals(jsFuncPtr2); +} + +bool JavascriptFunction::Equals( JavascriptFunction^ other ) +{ + return this == other; +} + +bool JavascriptFunction::Equals(Object^ other ) +{ + JavascriptFunction^ otherFunc = dynamic_cast(other); + return (otherFunc && this->Equals(otherFunc)); +} + +} } // namespace Noesis::Javascript + +//////////////////////////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/Source/Noesis.Javascript/JavascriptFunction.h b/Source/Noesis.Javascript/JavascriptFunction.h new file mode 100644 index 0000000..387f03c --- /dev/null +++ b/Source/Noesis.Javascript/JavascriptFunction.h @@ -0,0 +1,45 @@ +#pragma once + +////////////////////////////////////////////////////////////////////////// + +#include + +#include "JavascriptContext.h" + +using namespace v8; + +////////////////////////////////////////////////////////////////////////// + +namespace Noesis { namespace Javascript { + +////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////// +// JavascriptFunction +// +// Wraps around JS function object and allow calling it in later time +////////////////////////////////////////////////////////////////////////// +public ref class JavascriptFunction +{ +public: + JavascriptFunction(v8::Handle iFunction, JavascriptContext^ context); + ~JavascriptFunction(); + !JavascriptFunction(); + + System::Object^ Call(... cli::array^ args); + + static bool operator== (JavascriptFunction^ func1, JavascriptFunction^ func2); + bool Equals(JavascriptFunction^ other); + + virtual bool Equals(Object^ other) override; + +private: + v8::Persistent* mFuncHandle; + JavascriptContext^ mContext; +}; + +////////////////////////////////////////////////////////////////////////// + +} } // namespace Noesis::Javascript + +////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/Source/Noesis.Javascript/JavascriptInterop.cpp b/Source/Noesis.Javascript/JavascriptInterop.cpp index ebfcfcf..c9e634a 100644 --- a/Source/Noesis.Javascript/JavascriptInterop.cpp +++ b/Source/Noesis.Javascript/JavascriptInterop.cpp @@ -33,6 +33,7 @@ #include "SystemInterop.h" #include "JavascriptException.h" #include "JavascriptExternal.h" +#include "JavascriptFunction.h" #include @@ -81,6 +82,8 @@ JavascriptInterop::ConvertFromV8(Handle iValue) return ConvertArrayFromV8(iValue); if (iValue->IsDate()) return ConvertDateFromV8(iValue); + if (iValue->IsFunction()) + return gcnew JavascriptFunction(iValue->ToObject(), JavascriptContext::GetCurrent()); if (iValue->IsObject()) { Handle object = iValue->ToObject(); diff --git a/Source/Noesis.Javascript/Noesis.Javascript.VS2012.vcxproj b/Source/Noesis.Javascript/Noesis.Javascript.VS2012.vcxproj index 1b7928d..c751847 100644 --- a/Source/Noesis.Javascript/Noesis.Javascript.VS2012.vcxproj +++ b/Source/Noesis.Javascript/Noesis.Javascript.VS2012.vcxproj @@ -185,6 +185,7 @@ + @@ -192,6 +193,7 @@ + diff --git a/Source/Noesis.Javascript/Noesis.Javascript.VS2012.vcxproj.filters b/Source/Noesis.Javascript/Noesis.Javascript.VS2012.vcxproj.filters index c3c8574..726ee0a 100644 --- a/Source/Noesis.Javascript/Noesis.Javascript.VS2012.vcxproj.filters +++ b/Source/Noesis.Javascript/Noesis.Javascript.VS2012.vcxproj.filters @@ -22,7 +22,10 @@ Source Files - + + Source Files + + Source Files @@ -42,6 +45,9 @@ Source Files + + Source Files + diff --git a/Tests/Noesis.Javascript.Tests/IsolationTests.cs b/Tests/Noesis.Javascript.Tests/IsolationTests.cs index e533fa0..ad43f2f 100644 --- a/Tests/Noesis.Javascript.Tests/IsolationTests.cs +++ b/Tests/Noesis.Javascript.Tests/IsolationTests.cs @@ -12,7 +12,7 @@ namespace Noesis.Javascript.Tests public class IsolationTests { [Test] - public string RunIsolatesTest() + public void RunIsolatesTest() { Stopwatch timer = new Stopwatch(); @@ -24,8 +24,6 @@ public string RunIsolatesTest() thread.Join(); Assert.That(timer.ElapsedMilliseconds, Is.LessThan(1500), "It took too long, they must not be running in parallel."); - - return null; } static void RunInstance() diff --git a/Tests/Noesis.Javascript.Tests/JavascriptFunctionTests.cs b/Tests/Noesis.Javascript.Tests/JavascriptFunctionTests.cs new file mode 100644 index 0000000..359f3fc --- /dev/null +++ b/Tests/Noesis.Javascript.Tests/JavascriptFunctionTests.cs @@ -0,0 +1,23 @@ +using NUnit.Framework; + +namespace Noesis.Javascript.Tests +{ + [TestFixture] + public class JavascriptFunctionTests + { + [Test] + public void GetFunctionFromJsContext() + { + JavascriptContext context = new JavascriptContext(); + context.Run("a = function(a,b) {return a+b;}"); + + JavascriptFunction funcObj = context.GetParameter("a") as JavascriptFunction; + + Assert.That(funcObj, Is.Not.Null); + + object result = funcObj.Call(1, 2); + + Assert.That(result, Is.EqualTo(3)); + } + } +} diff --git a/Tests/Noesis.Javascript.Tests/MemoryLeakTests.cs b/Tests/Noesis.Javascript.Tests/MemoryLeakTests.cs index e4d8f2a..a9fb6df 100644 --- a/Tests/Noesis.Javascript.Tests/MemoryLeakTests.cs +++ b/Tests/Noesis.Javascript.Tests/MemoryLeakTests.cs @@ -8,7 +8,7 @@ namespace Noesis.Javascript.Tests public class MemoryLeakTests { [Test] - public string RunMemoryLeakTest() + public void RunMemoryLeakTest() { MemoryUsageLoadInstance(); long mem = Process.GetCurrentProcess().PrivateMemorySize64; @@ -21,9 +21,7 @@ public string RunMemoryLeakTest() GC.Collect(); decimal diffMBytes = (Process.GetCurrentProcess().PrivateMemorySize64 - mem) / 1048576m; - if (diffMBytes >= 1) // Allow 1 MB - return String.Format("{0:0.00}MB left allocated", diffMBytes); - return null; + Assert.That(diffMBytes, Is.LessThan(1), "{0:0.00}MB left allocated", diffMBytes); } private static void MemoryUsageLoadInstance() diff --git a/Tests/Noesis.Javascript.Tests/Noesis.Javascript.Tests.VS2012.csproj b/Tests/Noesis.Javascript.Tests/Noesis.Javascript.Tests.VS2012.csproj index 849c6b4..8c24d37 100644 --- a/Tests/Noesis.Javascript.Tests/Noesis.Javascript.Tests.VS2012.csproj +++ b/Tests/Noesis.Javascript.Tests/Noesis.Javascript.Tests.VS2012.csproj @@ -106,6 +106,7 @@ + @@ -151,6 +152,7 @@ +