System.String becomes undefined after use of an undefined variable

Oct 22, 2010 at 3:32 PM

Hi all,

I just upgraded from 0.8.8 to 0.8.9 of Jint, and I'm getting an error that I don't believe I was getting previously.  If I try to access a member of an undefined variable, I lose the ability to call System.String.Format.  Jint reports that System.String is null.  In a more complicated application, I'm getting this without the intervening exception, but I can't easily narrow it down.

Any ideas?

            Object nullObject = null;

            try
            {
                Console.Out.WriteLine("========== Test 1 ==========");

                Jint.JintEngine engine = new Jint.JintEngine();
                engine.SetParameter("nullObject", nullObject);
                Console.Out.WriteLine("===> First attempt: {0}", engine.Run("System.String.Format('{0}', 1)"));
                try
                {
                    engine.Run("nullObject.GetType()");
                }
                catch (Exception ex)
                {
                    //Console.Out.WriteLine("========== Exception 1-A ==========");
                    //Console.Out.WriteLine(ex);
                    //Console.Out.WriteLine(ex.Message);
                }
                Console.Out.WriteLine("===> Second attempt: {0}", engine.Run("System.String.Format('{0}', 2)"));
            }
            catch (Exception ex)
            {
                Console.Out.WriteLine("========== Exception 1-B ==========");
                //Console.Out.WriteLine(ex);
                Console.Out.WriteLine(ex.Message);
            }

            try
            {
                Console.Out.WriteLine("========== Test 2 ==========");

                Jint.JintEngine engine = new Jint.JintEngine();

                Console.Out.WriteLine("===> First attempt: {0}", engine.Run("System.String.Format('{0}', 1)"));
                try
                {
                    engine.Run("thisIsUndefined.GetType()");
                }
                catch (Exception ex)
                {
                    //Console.Out.WriteLine("========== Exception 2-A ==========");
                    //Console.Out.WriteLine(ex);
                    //Console.Out.WriteLine(ex.Message);
                }
                Console.Out.WriteLine("===> Second attempt: {0}", engine.Run("System.String.Format('{0}', 2)"));
            }
            catch (Exception ex)
            {
                Console.Out.WriteLine("========== Exception 2-B ==========");
                //Console.Out.WriteLine(ex);
                Console.Out.WriteLine(ex.Message);
            }

            try
            {
                Console.Out.WriteLine("========== Test 3 ==========");

                Jint.JintEngine engine = new Jint.JintEngine();
                
                Console.Out.WriteLine("===> First attempt: {0}", engine.Run("System.String.Format('{0}', 1)"));
                try
                {
                    nullObject.GetType();
                }
                catch (Exception)
                {
                    //Console.Out.WriteLine("========== Exception 3-A ==========");
                    //Console.Out.WriteLine(ex);
                    //Console.Out.WriteLine(ex.Message);
                }
                Console.Out.WriteLine("===> Second attempt: {0}", engine.Run("System.String.Format('{0}', 2)"));
            }
            catch (Exception ex)
            {
                Console.Out.WriteLine("========== Exception 3-B ==========");
                //Console.Out.WriteLine(ex);
                Console.Out.WriteLine(ex.Message);
            }
========== Test 1 ==========
===> First attempt: 1
===> Second attempt: 2
========== Test 2 ==========
===> First attempt: 1
========== Exception 2-B ==========
Object expected: Format
Line: 1 Char: 0
No source code available.
========== Test 3 ==========
===> First attempt: 1
===> Second attempt: 2

Oct 22, 2010 at 4:23 PM

Ah!  I figured out the cause in my non-exception situation.  I had an undefined variable.  I still don't think that the functionality I'm getting is correct.  1) I'd expect to be able to check if a variable is defined or not without getting into an error condition.  2) Having something be unexpectedly undefined should not prevent seemingly totally unrelated functionality from working.

Again, thank you for this excellent tool.  I really rely on it heavily, even if I contribute a random bug report from time to time.

Thanks!

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace QuickTests
{
    public class Program
    {
        Int32 Int32Property { set; get; }

        static private Boolean IsDefined(object maybeNull)
        {
            return maybeNull != null;
        }

        static void Main(String[] args)
        {
            try
            {
                JintTest();
            }
            catch (Exception ex)
            {
                Console.Write(ex);
            }
        }

        static void JintTest()
        {
            try
            {
                Console.Out.WriteLine("========== Test 4 ==========");

                Jint.JintEngine engine = new Jint.JintEngine();
                engine.SetFunction("IsDefined", new Func<Object, Boolean>(IsDefined));

                Console.Out.WriteLine("===> First attempt: {0}", engine.Run("System.String.Format('{0}', 1)"));
                try
                {
                    Console.Out.WriteLine("IsDefined(thisIsNotDefined) = {0}", engine.Run("IsDefined(thisIsNotDefined)"));
                }
                catch (Exception)
                {
                }
                Console.Out.WriteLine("===> Second attempt: {0}", engine.Run("System.String.Format('{0}', 2)"));
            }
            catch (Exception ex)
            {
                Console.Out.WriteLine("========== Exception 4-B ==========");
                Console.Out.WriteLine(ex.Message);
            }

            try
            {
                Console.Out.WriteLine("========== Test 5 ==========");

                Jint.JintEngine engine = new Jint.JintEngine();
                engine.SetFunction("IsDefined", new Func<Object, Boolean>(IsDefined));

                Console.Out.WriteLine("===> First attempt: {0}", engine.Run("System.String.Format('{0}', 1)"));
                try
                {
                    Console.Out.WriteLine("typeof thisIsNotDefined = {0}", engine.Run("typeof thisIsNotDefined"));
                }
                catch (Exception)
                {
                }
                Console.Out.WriteLine("===> Second attempt: {0}", engine.Run("System.String.Format('{0}', 2)"));
            }
            catch (Exception ex)
            {
                Console.Out.WriteLine("========== Exception 5-B ==========");
                Console.Out.WriteLine(ex.Message);
            }
        }
    }
}

========== Test 4 ==========
===> First attempt: 1
IsDefined(thisIsNotDefined) = False
========== Exception 4-B ==========
Object expected: Format
Line: 1 Char: 0
No source code available.
========== Test 5 ==========
===> First attempt: 1
typeof thisIsNotDefined = undefined
========== Exception 5-B ==========
Object expected: Format
Line: 1 Char: 0
No source code available.

Oct 22, 2010 at 5:03 PM

So, I was looking around in ExecutionVisitor.cs while running Jint.Shell, and it looks like the line commented "Try to record full path in case it's a type", ExecutionVisitor.Visit(Identifier expression) is not getting a clean typeFullName variable the second time.  If I run the shell program and type the following:

System.String.Format("{0}", 1);


foo;


System.String.Format("{0", 1);
the first time execution goes through that line, typeFullName has nothing in it.  The second time, it starts out with "foo", so Jint tries to evaluate "fooSystem.String.Format(...)", which obviously makes no sense.

I tried modifying ExecutionVisitor.Visit(Program program) to set typeFullName to a new StringBuilder, and it seems to work, but I don't know if that has any adverse effect somewhere else.

Cheers!

typeFullname
Oct 22, 2010 at 9:46 PM

Can you send the modification to me, and also a unit test so that I can incorporate it ?

Thanks
Sebastien

Oct 25, 2010 at 2:16 PM

Here's the new code:

 

        public void Visit(Program program)
        {
            this.typeFullname = new StringBuilder(); // I added this line.

            foreach (var statement in program.ReorderStatements())
            {
                CurrentStatement = statement;

                if (DebugMode)
                {
                    OnStep(CreateDebugInformation(statement));
                }

                statement.Accept(this);

                if (exit)
                {
                    exit = false;
                    return;
                }
            }
        }

As for a unit test, this test fails with the line I added commented it out and succeeds with the line added back in:

        [TestMethod]
        public void StaticMemberAfterUndefinedReference()
        {
            Jint.JintEngine engine = new Jint.JintEngine();

            Assert.AreEqual(System.String.Format("{0}", 1), engine.Run("System.String.Format('{0}', 1)"));
            Assert.AreEqual("undefined", engine.Run("typeof thisIsNotDefined"));
            Assert.AreEqual(System.String.Format("{0}", 1), engine.Run("System.String.Format('{0}', 1)"));
        }

Cheers!

     Don

Nov 11, 2010 at 8:35 PM

Sorry to bump this.  Have you integrated this fix?

I really appreciate this project and I'm glad to have contributed a little bit.

Nov 11, 2010 at 8:59 PM

Yes the fix is on the source code and a unit was added to ensure it doesn come again.