[SERVER-9739] Stored JavaScript fails when starting with var keyword Created: 21/May/13  Updated: 07/Oct/15  Resolved: 17/Sep/15

Status: Closed
Project: Core Server
Component/s: JavaScript
Affects Version/s: 2.4.1, 2.4.3
Fix Version/s: 3.1.9

Type: Bug Priority: Minor - P4
Reporter: Scott Allen Assignee: Jonathan Reams
Resolution: Done Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Related
is related to SERVER-8632 System.js functions are not available... Closed
Backwards Compatibility: Fully Compatible
Operating System: ALL
Steps To Reproduce:

db.system.js.save({_id:"foo", value:"var root=this;"})
db.loadServerScripts()

Sprint: Build 9 (09/18/15)
Participants:

 Description   

When saving script into system.js, if the script starts with the var keyword (example, var root = this;), the script will fail to load when trying to use the script from a map reduce or even db.loadServerScripts() from the shell produces:

JavaScript execution failed: Error: 16722 JavaScript execution failed: SyntaxError: Unexpected token var at src/mongo/shell/db.js:L1001

The was not a problem in 2.2.x, and doesn't appear to be a problem in a 64-bit Linux build (2.4.3).



 Comments   
Comment by Githook User [ 17/Sep/15 ]

Author:

{u'username': u'jbreams', u'name': u'Jonathan Reams', u'email': u'jbreams@mongodb.com'}

Message: SERVER-9739 db.loadServerScripts shouldn't use eval
Branch: master
https://github.com/mongodb/mongo/commit/4ea1fa9899eb5d8f2d529fd4344c628f4792e4de

Comment by Andrew Ryder (Inactive) [ 22/Jan/14 ]

The fixup functions for user supplied Javascript are a little different between 2.2 and 2.4. The differences appear to account for the change in behaviour.

r2.4.6 V8 engine fixup code:
https://github.com/mongodb/mongo/blob/r2.4.6/src/mongo/scripting/engine_v8.cpp#L942

r2.2.4 SpiderMonkey fixup code:
https://github.com/mongodb/mongo/blob/r2.2.4/src/mongo/scripting/engine_spidermonkey.cpp#L438

Comment by Scott Allen [ 22/May/13 ]

Hi Tad:

Yes, sorry for the confusion.

Here is a sample using the official C# driver that will work in 2.2.2 and 2.2.3 (prints the value 42), but not in 2.4.3 (myScript is not defined).

var db = new MongoClient().GetServer().GetDatabase("test");
 
var script = new BsonDocument("value",  
               new BsonJavaScript("var root = this; root.foo = function() { return 42; }"));
script.Add(new BsonElement("_id", "myScript"));
 
 
var sysJs = db.GetCollection("system.js");                      
sysJs.RemoveAll();
sysJs.Insert(script);
 
 
var things = db.GetCollection("things");
things.RemoveAll();
things.Insert(new BsonDocument("name", new BsonString("thing 1")));
 
var result = things.MapReduce("function() { myScript(); emit(this.name, foo()); }",
                              "function(key, values) { return Array.sum(values); }")
                   .GetResults();
                 
foreach (var document in result)
{
    Console.WriteLine(document["value"].AsDouble);
}               

Note: changing the opening line of the script from "var root = " to "root =" allows the program to work successfully in 2.4.3.

Let me know if I can provide more info.

Comment by Tad Marshall [ 21/May/13 ]

Hi Scott,

You might want to look at SERVER-8632, which was fixed for version 2.4.0 (in release candidate 2). I suspect that you are hitting a variant case that is not yet fixed, but that is just a guess. I think we need a full steps-to-reproduce where the steps work in version 2.2 but don't work in version 2.4 in order to address your problem.

Tad

Comment by Tad Marshall [ 21/May/13 ]

Hi Scott,

I think I need a little more context to tell where the problem lies.

The "this" variable, when used in a function member of an object, refers to the object of which the function is a member:

> var myObject = {}
> myObject.name = "Some name"
Some name
> myObject.fun = function(){ var root = this; return root.name; }
function (){ var root = this; return root.name; }
> myObject
{
        "name" : "Some name",
        "fun" : function (){ var root = this; return root.name; }
}
> myObject.fun()
Some name

When used at global scope (e.g. directly at the shell prompt), "this" refers to the global scope:

> var name = "Another name"
> var fun = function(){ var root = this; return root.name; }
> fun()
Another name

It looks like the code in map_reduce_utils.js defines a map() function that could be passed to a MapReduce command, and it stores it in a map() function, which might be a global or might be a member function, depending on the context in which the code is run.

There were changes in 2.4 that affected stored JavaScript, related to the change from SpiderMonkey to V8 as the JavaScript engine, but I don't understand how you were storing this such that it did what you want in version 2.2.

Tad

Comment by Scott Allen [ 21/May/13 ]

Yes, perhaps my shell example wasn't well thought out. I was trying to simplify the actual scenario which is the product loading this script into system.js:

https://github.com/pophealth/quality-measure-engine/blob/master/test/fixtures/library_functions/map_reduce_utils.js

Operations with the above script started throwing an error during map reduce after upgrading from 2.2.2 to 2.4.3, until we removed "var" from the first line of code leaving just "root = this;"

Comment by Tad Marshall [ 21/May/13 ]

I tried your example in version 2.2.4 and got the same failure:

> db.system.js.save({_id:"foo", value:"var root=this;"})
> db.loadServerScripts()
Tue May 21 14:47:24.577 JavaScript execution failed: SyntaxError: Unexpected token var
> db.version()
2.2.4

Comment by Tad Marshall [ 21/May/13 ]

Hi Scott,

The "value" stored should be a JavaScript function, not a string (even if the contents of the string represent a function).

> db.system.js.find()
> db.system.js.save({_id:"foo", value:function(){var root=this;}})
> db.eval("foo()")
null
> db.system.js.save({_id:"foo", value:function(){var root=this;return "Running function foo()";}})
> db.eval("foo()")
Running function foo()

Do you have an example of stored JavaScript that worked correctly in an earlier version of MongoDB but does not work in the current version(s)?

Tad

P.S. The example given produces the same error for me on Windows and Ubuntu 12.04.

Generated at Thu Feb 08 03:21:19 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.