Example #1
0
        static void Main(string[] args)
        {
            AppDomain.CurrentDomain.FirstChanceException += CurrentDomain_FirstChanceException;
            AppDomain.CurrentDomain.UnhandledException   += CurrentDomain_UnhandledException;

            try
            {
                Console.WriteLine("V8.Net Version: " + V8Engine.Version);

                Console.Write(Environment.NewLine + "Creating a V8Engine instance ...");
                _JSServer = new V8Engine();
                _JSServer.SetFlagsFromCommandLine(args);
                Console.WriteLine(" Done!");

                Console.Write("Testing marshalling compatibility...");
                _JSServer.RunMarshallingTests();
                Console.WriteLine(" Pass!");

                _TitleUpdateTimer           = new System.Timers.Timer(500);
                _TitleUpdateTimer.AutoReset = true;
                _TitleUpdateTimer.Elapsed  += (_o, _e) =>
                {
                    if (!_JSServer.IsDisposed)
                    {
                        Console.Title = "V8.Net Console - " + (IntPtr.Size == 4 ? "32-bit" : "64-bit") + " mode (Handles: " + _JSServer.TotalHandles
                                        + " / Pending Disposal: " + _JSServer.TotalHandlesPendingDisposal
                                        + " / Cached: " + _JSServer.TotalHandlesCached
                                        + " / In Use: " + (_JSServer.TotalHandlesInUse) + ")";
                    }
                    else
                    {
                        Console.Title = "V8.Net Console - Shutting down...";
                    }
                };
                _TitleUpdateTimer.Start();

                ((Action)(() =>
                {
                    _JSServer.DynamicGlobalObject.mo = _JSServer.CreateObjectTemplate().CreateObject(); // (need to keep a reference to the object so the GC doesn't claim it)
                }))();

                Console.WriteLine(Environment.NewLine + @"Ready - just enter script to execute. Type '\' or '\help' for a list of console specific commands.");

                string input, lcInput;

                while (true)
                {
                    var ok = ((Func <bool>)(() => // (this forces a scope to close so the GC can collect objects while in debug mode)
                    {
                        try
                        {
                            Console.Write(Environment.NewLine + "> ");

                            input = Console.ReadLine();
                            lcInput = input.Trim().ToLower();

                            if (lcInput == @"\help" || lcInput == @"\")
                            {
                                Console.WriteLine(@"Special console commands (all commands are triggered via a preceding '\' character so as not to confuse it with script code):");
                                Console.WriteLine(@"\init - Setup environment for testing (adds 'dump()' and 'assert()'.");
                                Console.WriteLine(@"\flags --flag1 --flag2 --etc... - Sets one or more flags (use '\flags --help' for more details).");
                                Console.WriteLine(@"\cls - Clears the screen.");
                                Console.WriteLine(@"\gc - Triggers garbage collection (for testing purposes).");
                                Console.WriteLine(@"\v8gc - Triggers garbage collection in V8 (for testing purposes).");
                                Console.WriteLine(@"\gctest - Runs a simple GC test against V8.NET and the native V8 engine.");
                                Console.WriteLine(@"\handles - Dumps the current list of known handles.");
                                Console.WriteLine(@"\speedtest - Runs a simple test script to test V8.NET performance with the V8 engine.");
                                Console.WriteLine(@"\exit - Exits the console.");
                            }
                            else if (lcInput == @"\init")
                            {
                                Console.WriteLine(Environment.NewLine + "Creating a global 'dump(obj)' function to dump properties of objects (one level only) ...");
                                _JSServer.ConsoleExecute(@"dump = function(o) { var s=''; if (typeof(o)=='undefined') return 'undefined';"
                                                         + @" if (typeof o.valueOf=='undefined') return ""'valueOf()' is missing on '""+(typeof o)+""' - if you are inheriting from V8ManagedObject, make sure you are not blocking the property."";"
                                                         + @" if (typeof o.toString=='undefined') return ""'toString()' is missing on '""+o.valueOf()+""' - if you are inheriting from V8ManagedObject, make sure you are not blocking the property."";"
                                                         + @" for (var p in o) {var ov='', pv=''; try{ov=o.valueOf();}catch(e){ov='{error: '+e.message+': '+dump(o)+'}';} try{pv=o[p];}catch(e){pv=e.message;} s+='* '+ov+'.'+p+' = ('+pv+')\r\n'; } return s; }");

                                Console.WriteLine(Environment.NewLine + "Creating a global 'assert(msg, a,b)' function for property value assertion ...");
                                _JSServer.ConsoleExecute(@"assert = function(msg,a,b) { msg += ' ('+a+'==='+b+'?)'; if (a === b) return msg+' ... Ok.'; else throw msg+' ... Failed!'; }");
                            }
                            else if (lcInput == @"\cls")
                            {
                                Console.Clear();
                            }
                            else if (lcInput == @"\flags" || lcInput.StartsWith(@"\flags "))
                            {
                                string flags = lcInput.Substring(6).Trim();
                                if (flags.Length > 0)
                                {
                                    _JSServer.SetFlagsFromString(flags);
                                }
                                else
                                {
                                    Console.WriteLine(@"You did not specify any options.");
                                }
                            }
                            else if (lcInput == @"\exit")
                            {
                                Console.WriteLine("User requested exit, disposing the engine instance ...");
                                _JSServer.Dispose();
                                Console.WriteLine("Engine disposed successfully. Press any key to continue ...");
                                Console.ReadKey();
                                Console.WriteLine("Goodbye. :)");
                                return(false);
                            }
                            else if (lcInput == @"\gc")
                            {
                                Console.Write(Environment.NewLine + "Forcing garbage collection ... ");
                                GC.AddMemoryPressure(100000000000);
                                GC.Collect(3, GCCollectionMode.Forced);
                                GC.WaitForPendingFinalizers();
                                GC.RemoveMemoryPressure(100000000000);
                                Console.WriteLine("Done.\r\n");
                                Console.WriteLine("Currently Used Memory: " + GC.GetTotalMemory(true));
                            }
                            else if (lcInput == @"\v8gc")
                            {
                                Console.Write(Environment.NewLine + "Forcing V8 garbage collection ... ");
                                _JSServer.ForceV8GarbageCollection();
                                Console.WriteLine("Done.\r\n");
                            }
                            else if (lcInput == @"\handles")
                            {
                                Console.Write(Environment.NewLine + "Active handles list ... " + Environment.NewLine);

                                foreach (var h in _JSServer.Handles_Active)
                                {
                                    Console.WriteLine(" * " + h.Description.Replace(Environment.NewLine, "\\r\\n"));
                                }

                                Console.Write(Environment.NewLine + "Managed side dispose-ready handles or non-tracked internal handles (usually due to a GC attempt) ... " + Environment.NewLine);

                                foreach (var h in _JSServer.Handles_ManagedSideDisposed)
                                {
                                    Console.WriteLine(" * " + h.Description.Replace(Environment.NewLine, "\\r\\n"));
                                }

                                Console.Write(Environment.NewLine + "Native side V8 handles now marked as disposing (in the queue) ... " + Environment.NewLine);

                                foreach (var h in _JSServer.Handles_Disposing)
                                {
                                    Console.WriteLine(" * " + h.Description.Replace(Environment.NewLine, "\\r\\n"));
                                }

                                Console.Write(Environment.NewLine + "Native side V8 handles that are now disposed (cached) for reuse ... " + Environment.NewLine);

                                foreach (var h in _JSServer.Handles_DisposedAndCached)
                                {
                                    Console.WriteLine(" * " + h.Description.Replace(Environment.NewLine, "\\r\\n"));
                                }

                                Console.WriteLine(Environment.NewLine + "Done." + Environment.NewLine);
                            }
                            else if (lcInput == @"\gctest")
                            {
                                Console.WriteLine("\r\nTesting garbage collection ... ");

                                int objectId = -1;

                                InternalHandle internalHandle = ((Func <V8Engine, InternalHandle>)((engine) =>
                                {
                                    V8NativeObject tempObj;

                                    Console.WriteLine("Setting 'tempObj' to a new managed object ...");

                                    engine.DynamicGlobalObject.tempObj = tempObj = engine.CreateObject <V8NativeObject>();
                                    InternalHandle ih = InternalHandle.GetUntrackedHandleFromObject(tempObj);

                                    objectId = tempObj.ID;

                                    Console.WriteLine("Generation of test instance before collect: " + GC.GetGeneration(tempObj));

                                    Console.WriteLine("Releasing the object on the managed side ...");
                                    tempObj = null;

                                    return(ih);
                                }))(_JSServer);

                                // (we wait for the object to be sent for disposal by the worker)

                                GC.Collect();
                                GC.WaitForPendingFinalizers();

                                var testobj = _JSServer.GetObjectByID(objectId);
                                if (testobj != null)
                                {
                                    Console.WriteLine("Generation of test instance after collect: " + GC.GetGeneration(testobj));
                                }
                                else
                                {
                                    Console.WriteLine("Generation of test instance after collect: Object null for ID: " + objectId);
                                }
                                testobj = null;

                                int i;

                                for (i = 0; i < 3000 && !internalHandle.IsDisposed; i++)
                                {
                                    System.Threading.Thread.Sleep(1); // (just wait for the worker)
                                }
                                if (!internalHandle.IsDisposed)
                                {
                                    throw new Exception("The temp object's handle is still not pending disposal ... something is wrong.");
                                }

                                Console.WriteLine("Success!");
                                //Console.WriteLine("Success! The test object's handle is going through the disposal process.");
                                ////Console.WriteLine("Clearing the handle object reference next ...");

                                //// object handles will finally be disposed when the native V8 GC calls back regarding them ...

                                //Console.WriteLine("Waiting on the worker to make the object weak on the native V8 side ... ");

                                //for (i = 0; i < 6000 && !internalHandle.IsNativeDisposed; i++)
                                //    System.Threading.Thread.Sleep(1);

                                //if (!internalHandle.IsNativeDisposed)
                                //    throw new Exception("Object is not weak yet ... something is wrong.");

                                //Console.WriteLine("The native side object is now weak and ready to be collected by V8.");

                                //Console.WriteLine("Forcing V8 garbage collection ... ");
                                //_JSServer.DynamicGlobalObject.tempObj = null;
                                //for (i = 0; i < 3000 && !internalHandle.IsDisposed; i++)
                                //{
                                //    _JSServer.ForceV8GarbageCollection();
                                //    System.Threading.Thread.Sleep(1);
                                //}

                                //Console.WriteLine("Looking for object ...");

                                //if (!internalHandle.IsDisposed) throw new Exception("Managed object's handle did not dispose.");
                                //// (note: this call is only valid as long as no more objects are created before this point)
                                //Console.WriteLine("Success! The managed V8NativeObject native handle is now disposed.");
                                //Console.WriteLine("\r\nDone.\r\n");
                            }
                            else if (lcInput == @"\speedtest")
                            {
                                var timer = new Stopwatch();
                                long startTime, elapsed;
                                long count;
                                double result1, result2, result3, result4;
#if DEBUG
                                Console.WriteLine(Environment.NewLine + "WARNING: You are running in debug mode, so the speed will be REALLY slow compared to release.");
#endif
                                Console.WriteLine(Environment.NewLine + "Running the speed tests ... ");

                                timer.Start();

                                //??Console.WriteLine(Environment.NewLine + "Running the property access speed tests ... ");
                                Console.WriteLine("(Note: 'V8NativeObject' objects are always faster than using the 'V8ManagedObject' objects because native objects store values within the V8 engine and managed objects store theirs on the .NET side.)");

#if DEBUG
                                count = 20000000;
#else
                                count = 200000000;
#endif

                                Console.WriteLine("\r\nTesting native property write speed ... ");
                                startTime = timer.ElapsedMilliseconds;
                                _JSServer.Execute("o={i:0}; for (o.i=0; o.i<" + count + "; o.i++) n = 0;", throwExceptionOnError: true); // (o={i:0}; is used in case the global object is managed, which will greatly slow down the loop)
                                elapsed = timer.ElapsedMilliseconds - startTime;
                                result1 = (double)elapsed / count;
                                Console.WriteLine(count + " loops @ " + elapsed + "ms total = " + result1.ToString("0.0#########") + " ms each pass.");

                                Console.WriteLine("\r\nTesting native property read speed ... ");
                                startTime = timer.ElapsedMilliseconds;
                                _JSServer.Execute("for (o.i=0; o.i<" + count + "; o.i++) n;", throwExceptionOnError: true);
                                elapsed = timer.ElapsedMilliseconds - startTime;
                                result2 = (double)elapsed / count;
                                Console.WriteLine(count + " loops @ " + elapsed + "ms total = " + result2.ToString("0.0#########") + " ms each pass.");

#if DEBUG
                                count = 10000;
#else
                                count = 2000000;
#endif

                                Console.WriteLine("\r\nTesting property write speed on a managed object (with interceptors) ... ");
                                var o = _JSServer.CreateObjectTemplate().CreateObject(); // (need to keep a reference to the object so the GC doesn't claim it)
                                _JSServer.DynamicGlobalObject.mo = o;
                                startTime = timer.ElapsedMilliseconds;
                                _JSServer.Execute("o={i:0}; for (o.i=0; o.i<" + count + "; o.i++) mo.n = 0;", throwExceptionOnError: true);
                                elapsed = timer.ElapsedMilliseconds - startTime;
                                result3 = (double)elapsed / count;
                                Console.WriteLine(count + " loops @ " + elapsed + "ms total = " + result3.ToString("0.0#########") + " ms each pass.");

                                Console.WriteLine("\r\nTesting property read speed on a managed object (with interceptors) ... ");
                                startTime = timer.ElapsedMilliseconds;
                                _JSServer.Execute("for (o.i=0; o.i<" + count + "; o.i++) mo.n;", throwExceptionOnError: true);
                                elapsed = timer.ElapsedMilliseconds - startTime;
                                result4 = (double)elapsed / count;
                                Console.WriteLine(count + " loops @ " + elapsed + "ms total = " + result4.ToString("0.0#########") + " ms each pass.");

                                Console.WriteLine("\r\nUpdating native properties is {0:N2}x faster than managed ones.", result3 / result1);
                                Console.WriteLine("\r\nReading native properties is {0:N2}x faster than managed ones.", result4 / result2);

                                Console.WriteLine("\r\nDone.\r\n");

                                GC.KeepAlive(o);
                            }
                            else if (lcInput.StartsWith(@"\"))
                            {
                                Console.WriteLine(@"Invalid console command. Type '\help' to see available commands.");
                            }
                            else
                            {
                                Console.WriteLine();

                                try
                                {
                                    var result = _JSServer.Execute(input, "V8.NET Console", false, 5000);

                                    Console.WriteLine(result.AsString);

                                    if (result.WasTerminated)
                                    {
                                        Console.WriteLine("The script took longer than 5 seconds to run and was aborted.");
                                    }

                                    result.Dispose();
                                }
                                catch (Exception ex)
                                {
                                    Console.WriteLine();
                                    Console.WriteLine();
                                    Console.WriteLine(Exceptions.GetFullErrorMessage(ex));
                                    Console.WriteLine();
                                    Console.WriteLine("Error!  Press any key to continue ...");
                                    Console.ReadKey();
                                }
                            }
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine();
                            Console.WriteLine();
                            Console.WriteLine(Exceptions.GetFullErrorMessage(ex));
                            Console.WriteLine();
                            Console.WriteLine("Error!  Press any key to continue ...");
                            Console.ReadKey();
                        }

                        return(true);
                    }))();
                    if (!ok)
                    {
                        break;
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine(Exceptions.GetFullErrorMessage(ex));
                Console.WriteLine();
                Console.WriteLine("Error!  Press any key to exit ...");
                Console.ReadKey();
            }

            if (_TitleUpdateTimer != null)
            {
                _TitleUpdateTimer.Dispose();
            }
        }