Need to prohibit EcmaScript implied variable declaration

Aug 31, 2010 at 4:21 PM

Hi, while continuing to work my a javascript editor/debugger made with Avalon Edit and Jint I was asked from my boss not to permit implied global properties (i.e. the user must always declare variables with "var" keyword), because in our vocal application there is an engine that doesn't "like" them, or, better, we need to keep user scopes well divided from the global scope.

Does anybody have some idea on where to focus my attention? I was thinking about changing ES3.g ANTLR grammar implementation, but while debugging I saw that this code:

 

[...] //from ExecutionVisitor.cs

// resolve which CurrentScope to use
propertyName = ((Identifier)left.Member).Text;

Result = GlobalScope;

foreach (JsDictionaryObject scope in Scopes.ToArray())
{
    if (scope != GlobalScope && scope.HasProperty(propertyName))
    {
        Result = scope;
        break;
    }
}

and I saw that executing, for example:

var foo;
foo = 2;
bar = 3;
print(foo+bar);

the Result is assigned to GlobalScope for both variables, the explicit ("var foo") and the implicit one ("bar"), I mean, following the same behaviour and not taking care if an identifier has been previously declared.

Any suggestion will be greatly appreciated!

Thank you.

Aug 31, 2010 at 6:48 PM

Well using a local variable on the global scope is like declaring a global variable. Though the main difference is the STRICT mode.

What I would suggest to you would be to add a Strict() method to the JintEngine class, which would turn on the strict behavior, and during the processing of an assignment, if the variable is not found in the scope hierarchy, then throw a JsException.

Is it clear ? You might also do it on a fork, including some unit tests, and I would add it to the repository.

Sebastien

Sep 1, 2010 at 5:21 PM
Edited Sep 2, 2010 at 2:52 PM

Hi Sebastien, thank you very much for your reply.

My example wasn't a great one, indeed, but in this case:

function sum5(n)
{
    n2add=5;
    return n + n2add;
}
var n2add = 1;
var bar = sum5(1);
var foo = n2add;

the problem is more evident, as the variable "n2add" is an implied global and its value its changed by the called sum5 function: this is supposed to be an unwanted situation if we wrote "n2add=5" thinking that the function scope could hide the n2add (supposed) local variable.

I noticed that even if using Options.Ecmascript5 and Options.Strict, no exception is thrown (I was expecting this way after reading "Variables and Properties" in http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/ which is reported by many javascript related forums), so I did this modification to Options.cs:

 

    public enum Options
    {
        Strict=1,
        Ecmascript3=2,
        Ecmascript5=4,
        ExplicitVar=8
    }

 

and to ExecutionVisitor.cs:

 

                // resolve which CurrentScope to use
                propertyName = ((Identifier)left.Member).Text;

                //ADDED CODE START
                if (HasOption(Options.ExplicitVar))
                {
                    List<JsDictionaryObject> declaredPropertyScopes = new List<JsDictionaryObject>();
                    foreach (JsDictionaryObject scope in Scopes.ToArray())
                    {
                        if (scope.HasProperty(propertyName))
                        {
                            declaredPropertyScopes.Add(scope);
                        }
                    }
                    if (declaredPropertyScopes.Count == 0)
                    {
                        throw new JintException("Variable must be explicitly declared with \"var\".");
                    }
                }
                //ADDED CODE END

                Result = GlobalScope;

                foreach (JsDictionaryObject scope in Scopes.ToArray())
                {
                    if (scope != GlobalScope && scope.HasProperty(propertyName))
                    {
                        Result = scope;
                        break;
                    }
                }

 

I don't know if this was the best solution available, but before reading your answer that was the only way I found, and your suggestion isn't too far from this one.

Unfortunately, using this "runtime" solution, the editor tells me nothing while I'm writing, for example:

 

function sum(a,b)
{
	foo=3
	return a+b
}
return sum(1,2)

 

until I write the last line: the function must be called so that the visitor can go through it and launch the exception.

Do you think I could find something better?

Thank you again, 

Marco

Sep 10, 2010 at 4:55 PM

Like I said, you should implement another visitor class. This would visit the AST tree before the evaluation, and then you would get the syntax error messages without executing it. I will try to make an example, and modify the engine to accept extraneous visitors for handling syntax errors with explicit information.