1 // jstests.cpp
2 //
3 
4 
5 /**
6  *    Copyright (C) 2018-present MongoDB, Inc.
7  *
8  *    This program is free software: you can redistribute it and/or modify
9  *    it under the terms of the Server Side Public License, version 1,
10  *    as published by MongoDB, Inc.
11  *
12  *    This program is distributed in the hope that it will be useful,
13  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *    Server Side Public License for more details.
16  *
17  *    You should have received a copy of the Server Side Public License
18  *    along with this program. If not, see
19  *    <http://www.mongodb.com/licensing/server-side-public-license>.
20  *
21  *    As a special exception, the copyright holders give permission to link the
22  *    code of portions of this program with the OpenSSL library under certain
23  *    conditions as described in each individual source file and distribute
24  *    linked combinations including the program with the OpenSSL library. You
25  *    must comply with the Server Side Public License in all respects for
26  *    all of the code used other than as permitted herein. If you modify file(s)
27  *    with this exception, you may extend this exception to your version of the
28  *    file(s), but you are not obligated to do so. If you do not wish to do so,
29  *    delete this exception statement from your version. If you delete this
30  *    exception statement from all source files in the program, then also delete
31  *    it in the license file.
32  */
33 
34 #define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kDefault
35 
36 #include "mongo/platform/basic.h"
37 
38 #include <iostream>
39 #include <limits>
40 
41 #include "mongo/base/parse_number.h"
42 #include "mongo/db/client.h"
43 #include "mongo/db/dbdirectclient.h"
44 #include "mongo/db/json.h"
45 #include "mongo/dbtests/dbtests.h"
46 #include "mongo/platform/decimal128.h"
47 #include "mongo/scripting/engine.h"
48 #include "mongo/util/concurrency/thread_name.h"
49 #include "mongo/util/future.h"
50 #include "mongo/util/log.h"
51 #include "mongo/util/time_support.h"
52 #include "mongo/util/timer.h"
53 
54 using std::cout;
55 using std::endl;
56 using std::string;
57 using std::stringstream;
58 using std::unique_ptr;
59 using std::vector;
60 
61 namespace JSTests {
62 
63 class BuiltinTests {
64 public:
run()65     void run() {
66         // Run any tests included with the scripting engine
67         getGlobalScriptEngine()->runTest();
68     }
69 };
70 
71 class BasicScope {
72 public:
run()73     void run() {
74         unique_ptr<Scope> s;
75         s.reset(getGlobalScriptEngine()->newScope());
76 
77         s->setNumber("x", 5);
78         ASSERT(5 == s->getNumber("x"));
79 
80         s->setNumber("x", 1.67);
81         ASSERT(1.67 == s->getNumber("x"));
82 
83         s->setString("s", "eliot was here");
84         ASSERT("eliot was here" == s->getString("s"));
85 
86         s->setBoolean("b", true);
87         ASSERT(s->getBoolean("b"));
88 
89         s->setBoolean("b", false);
90         ASSERT(!s->getBoolean("b"));
91     }
92 };
93 
94 class ResetScope {
95 public:
run()96     void run() {
97         /* Currently reset does not clear data in v8 or spidermonkey scopes.  See SECURITY-10
98         unique_ptr<Scope> s;
99         s.reset( getGlobalScriptEngine()->newScope() );
100 
101         s->setBoolean( "x" , true );
102         ASSERT( s->getBoolean( "x" ) );
103 
104         s->reset();
105         ASSERT( !s->getBoolean( "x" ) );
106         */
107     }
108 };
109 
110 class FalseTests {
111 public:
run()112     void run() {
113         // Test falsy javascript values
114         unique_ptr<Scope> s;
115         s.reset(getGlobalScriptEngine()->newScope());
116 
117         ASSERT(!s->getBoolean("notSet"));
118 
119         s->setString("emptyString", "");
120         ASSERT(!s->getBoolean("emptyString"));
121 
122         s->setNumber("notANumberVal", std::numeric_limits<double>::quiet_NaN());
123         ASSERT(!s->getBoolean("notANumberVal"));
124 
125         auto obj = BSONObjBuilder().appendNull("null").obj();
126         s->setElement("nullVal", obj.getField("null"), obj);
127         ASSERT(!s->getBoolean("nullVal"));
128 
129         s->setNumber("zeroVal", 0);
130         ASSERT(!s->getBoolean("zeroVal"));
131     }
132 };
133 
134 class SimpleFunctions {
135 public:
run()136     void run() {
137         unique_ptr<Scope> s(getGlobalScriptEngine()->newScope());
138 
139         s->invoke("x=5;", 0, 0);
140         ASSERT(5 == s->getNumber("x"));
141 
142         s->invoke("return 17;", 0, 0);
143         ASSERT(17 == s->getNumber("__returnValue"));
144 
145         s->invoke("function(){ return 17; }", 0, 0);
146         ASSERT(17 == s->getNumber("__returnValue"));
147 
148         s->setNumber("x", 1.76);
149         s->invoke("return x == 1.76; ", 0, 0);
150         ASSERT(s->getBoolean("__returnValue"));
151 
152         s->setNumber("x", 1.76);
153         s->invoke("return x == 1.79; ", 0, 0);
154         ASSERT(!s->getBoolean("__returnValue"));
155 
156         BSONObj obj = BSON("" << 11.0);
157         s->invoke("function( z ){ return 5 + z; }", &obj, 0);
158         ASSERT_EQUALS(16, s->getNumber("__returnValue"));
159     }
160 };
161 
162 /** Installs a tee for auditing log messages in the same thread. */
163 class LogRecordingScope {
164 public:
LogRecordingScope()165     LogRecordingScope()
166         : _logged(false),
167           _threadName(mongo::getThreadName().toString()),
168           _handle(mongo::logger::globalLogDomain()->attachAppender(
169               mongo::logger::MessageLogDomain::AppenderAutoPtr(new Tee(this)))) {}
~LogRecordingScope()170     ~LogRecordingScope() {
171         mongo::logger::globalLogDomain()->detachAppender(_handle);
172     }
173     /** @return most recent log entry. */
logged() const174     bool logged() const {
175         return _logged;
176     }
177 
178 private:
179     class Tee : public mongo::logger::MessageLogDomain::EventAppender {
180     public:
Tee(LogRecordingScope * scope)181         Tee(LogRecordingScope* scope) : _scope(scope) {}
~Tee()182         virtual ~Tee() {}
append(const logger::MessageEventEphemeral & event)183         virtual Status append(const logger::MessageEventEphemeral& event) {
184             // Don't want to consider logging by background threads.
185             if (mongo::getThreadName() == _scope->_threadName) {
186                 _scope->_logged = true;
187             }
188             return Status::OK();
189         }
190 
191     private:
192         LogRecordingScope* _scope;
193     };
194     bool _logged;
195     const string _threadName;
196     mongo::logger::MessageLogDomain::AppenderHandle _handle;
197 };
198 
199 /** Error logging in Scope::exec(). */
200 class ExecLogError {
201 public:
run()202     void run() {
203         unique_ptr<Scope> scope(getGlobalScriptEngine()->newScope());
204 
205         // No error is logged when reportError == false.
206         ASSERT(!scope->exec("notAFunction()", "foo", false, false, false));
207         ASSERT(!_logger.logged());
208 
209         // No error is logged for a valid statement.
210         ASSERT(scope->exec("validStatement = true", "foo", false, true, false));
211         ASSERT(!_logger.logged());
212 
213         // An error is logged for an invalid statement when reportError == true.
214         ASSERT(!scope->exec("notAFunction()", "foo", false, true, false));
215 
216         // Don't check if we're using SpiderMonkey. Our threading model breaks
217         // this test
218         // TODO: figure out a way to check for SpiderMonkey
219         auto ivs = getGlobalScriptEngine()->getInterpreterVersionString();
220         std::string prefix("MozJS");
221         if (ivs.compare(0, prefix.length(), prefix) != 0) {
222             ASSERT(_logger.logged());
223         }
224     }
225 
226 private:
227     LogRecordingScope _logger;
228 };
229 
230 /** Error logging in Scope::invoke(). */
231 class InvokeLogError {
232 public:
run()233     void run() {
234         unique_ptr<Scope> scope(getGlobalScriptEngine()->newScope());
235 
236         // No error is logged for a valid statement.
237         ASSERT_EQUALS(0, scope->invoke("validStatement = true", 0, 0));
238         ASSERT(!_logger.logged());
239 
240         // An error is logged for an invalid statement.
241         try {
242             scope->invoke("notAFunction()", 0, 0);
243         } catch (const DBException&) {
244             // ignore the exception; just test that we logged something
245         }
246 
247         // Don't check if we're using SpiderMonkey. Our threading model breaks
248         // this test
249         // TODO: figure out a way to check for SpiderMonkey
250         auto ivs = getGlobalScriptEngine()->getInterpreterVersionString();
251         std::string prefix("MozJS");
252         if (ivs.compare(0, prefix.length(), prefix) != 0) {
253             ASSERT(_logger.logged());
254         }
255     }
256 
257 private:
258     LogRecordingScope _logger;
259 };
260 
261 class ObjectMapping {
262 public:
run()263     void run() {
264         unique_ptr<Scope> s(getGlobalScriptEngine()->newScope());
265 
266         BSONObj o = BSON("x" << 17.0 << "y"
267                              << "eliot"
268                              << "z"
269                              << "sara");
270         s->setObject("blah", o);
271 
272         s->invoke("return blah.x;", 0, 0);
273         ASSERT_EQUALS(17, s->getNumber("__returnValue"));
274         s->invoke("return blah.y;", 0, 0);
275         ASSERT_EQUALS("eliot", s->getString("__returnValue"));
276 
277         s->invoke("return this.z;", 0, &o);
278         ASSERT_EQUALS("sara", s->getString("__returnValue"));
279 
280         s->invoke("return this.z == 'sara';", 0, &o);
281         ASSERT_EQUALS(true, s->getBoolean("__returnValue"));
282 
283         s->invoke("this.z == 'sara';", 0, &o);
284         ASSERT_EQUALS(true, s->getBoolean("__returnValue"));
285 
286         s->invoke("this.z == 'asara';", 0, &o);
287         ASSERT_EQUALS(false, s->getBoolean("__returnValue"));
288 
289         s->invoke("return this.x == 17;", 0, &o);
290         ASSERT_EQUALS(true, s->getBoolean("__returnValue"));
291 
292         s->invoke("return this.x == 18;", 0, &o);
293         ASSERT_EQUALS(false, s->getBoolean("__returnValue"));
294 
295         s->invoke("function(){ return this.x == 17; }", 0, &o);
296         ASSERT_EQUALS(true, s->getBoolean("__returnValue"));
297 
298         s->invoke("function(){ return this.x == 18; }", 0, &o);
299         ASSERT_EQUALS(false, s->getBoolean("__returnValue"));
300 
301         s->invoke("function (){ return this.x == 17; }", 0, &o);
302         ASSERT_EQUALS(true, s->getBoolean("__returnValue"));
303 
304         s->invoke("function z(){ return this.x == 18; }", 0, &o);
305         ASSERT_EQUALS(false, s->getBoolean("__returnValue"));
306 
307         s->invoke("function (){ this.x == 17; }", 0, &o);
308         ASSERT_EQUALS(false, s->getBoolean("__returnValue"));
309 
310         s->invoke("function z(){ this.x == 18; }", 0, &o);
311         ASSERT_EQUALS(false, s->getBoolean("__returnValue"));
312 
313         s->invoke("x = 5; for( ; x <10; x++){ a = 1; }", 0, &o);
314         ASSERT_EQUALS(10, s->getNumber("x"));
315     }
316 };
317 
318 class ObjectDecoding {
319 public:
run()320     void run() {
321         unique_ptr<Scope> s(getGlobalScriptEngine()->newScope());
322 
323         s->invoke("z = { num : 1 };", 0, 0);
324         BSONObj out = s->getObject("z");
325         ASSERT_EQUALS(1, out["num"].number());
326         ASSERT_EQUALS(1, out.nFields());
327 
328         s->invoke("z = { x : 'eliot' };", 0, 0);
329         out = s->getObject("z");
330         ASSERT_EQUALS((string) "eliot", out["x"].valuestr());
331         ASSERT_EQUALS(1, out.nFields());
332 
333         BSONObj o = BSON("x" << 17);
334         s->setObject("blah", o);
335         out = s->getObject("blah");
336         ASSERT_EQUALS(17, out["x"].number());
337     }
338 };
339 
340 class JSOIDTests {
341 public:
run()342     void run() {
343 #ifdef MOZJS
344         unique_ptr<Scope> s(getGlobalScriptEngine()->newScope());
345 
346         s->localConnect("blah");
347 
348         s->invoke("z = { _id : new ObjectId() , a : 123 };", 0, 0);
349         BSONObj out = s->getObject("z");
350         ASSERT_EQUALS(123, out["a"].number());
351         ASSERT_EQUALS(jstOID, out["_id"].type());
352 
353         OID save = out["_id"].__oid();
354 
355         s->setObject("a", out);
356 
357         s->invoke("y = { _id : a._id , a : 124 };", 0, 0);
358         out = s->getObject("y");
359         ASSERT_EQUALS(124, out["a"].number());
360         ASSERT_EQUALS(jstOID, out["_id"].type());
361         ASSERT_EQUALS(out["_id"].__oid().str(), save.str());
362 
363         s->invoke("y = { _id : new ObjectId( a._id ) , a : 125 };", 0, 0);
364         out = s->getObject("y");
365         ASSERT_EQUALS(125, out["a"].number());
366         ASSERT_EQUALS(jstOID, out["_id"].type());
367         ASSERT_EQUALS(out["_id"].__oid().str(), save.str());
368 #endif
369     }
370 };
371 
372 class SetImplicit {
373 public:
run()374     void run() {
375         unique_ptr<Scope> s(getGlobalScriptEngine()->newScope());
376 
377         BSONObj o = BSON("foo"
378                          << "bar");
379         s->setObject("a.b", o);
380         ASSERT(s->getObject("a").isEmpty());
381 
382         BSONObj o2 = BSONObj();
383         s->setObject("a", o2);
384         s->setObject("a.b", o);
385         ASSERT(s->getObject("a").isEmpty());
386 
387         o2 = fromjson("{b:{}}");
388         s->setObject("a", o2);
389         s->setObject("a.b", o);
390         ASSERT(!s->getObject("a").isEmpty());
391     }
392 };
393 
394 class ObjectModReadonlyTests {
395 public:
run()396     void run() {
397         unique_ptr<Scope> s(getGlobalScriptEngine()->newScope());
398 
399         BSONObj o = BSON("x" << 17 << "y"
400                              << "eliot"
401                              << "z"
402                              << "sara"
403                              << "zz"
404                              << BSONObj());
405         s->setObject("blah", o, true);
406 
407         BSONObj out;
408 
409         ASSERT_THROWS(s->invoke("blah.y = 'e'", 0, 0), mongo::AssertionException);
410         ASSERT_THROWS(s->invoke("blah.a = 19;", 0, 0), mongo::AssertionException);
411         ASSERT_THROWS(s->invoke("blah.zz.a = 19;", 0, 0), mongo::AssertionException);
412         ASSERT_THROWS(s->invoke("blah.zz = { a : 19 };", 0, 0), mongo::AssertionException);
413         ASSERT_THROWS(s->invoke("delete blah['x']", 0, 0), mongo::AssertionException);
414 
415         // read-only object itself can be overwritten
416         s->invoke("blah = {}", 0, 0);
417         out = s->getObject("blah");
418         ASSERT(out.isEmpty());
419 
420         // test array - can't implement this in v8
421         //            o = fromjson( "{a:[1,2,3]}" );
422         //            s->setObject( "blah", o, true );
423         //            out = s->getObject( "blah" );
424         //            s->invoke( "blah.a[ 0 ] = 4;", BSONObj() );
425         //            s->invoke( "delete blah['a'][ 2 ];", BSONObj() );
426         //            out = s->getObject( "blah" );
427         //            ASSERT_EQUALS( 1.0, out[ "a" ].embeddedObject()[ 0 ].number() );
428         //            ASSERT_EQUALS( 3.0, out[ "a" ].embeddedObject()[ 2 ].number() );
429     }
430 };
431 
432 class OtherJSTypes {
433 public:
run()434     void run() {
435         unique_ptr<Scope> s(getGlobalScriptEngine()->newScope());
436 
437         {
438             // date
439             BSONObj o;
440             {
441                 BSONObjBuilder b;
442                 b.appendDate("d", Date_t::fromMillisSinceEpoch(123456789));
443                 o = b.obj();
444             }
445             s->setObject("x", o);
446 
447             s->invoke("return x.d.getTime() != 12;", 0, 0);
448             ASSERT_EQUALS(true, s->getBoolean("__returnValue"));
449 
450             s->invoke("z = x.d.getTime();", 0, 0);
451             ASSERT_EQUALS(123456789, s->getNumber("z"));
452 
453             s->invoke("z = { z : x.d }", 0, 0);
454             BSONObj out = s->getObject("z");
455             ASSERT(out["z"].type() == Date);
456         }
457 
458         {
459             // regex
460             BSONObj o;
461             {
462                 BSONObjBuilder b;
463                 b.appendRegex("r", "^a", "i");
464                 o = b.obj();
465             }
466             s->setObject("x", o);
467 
468             s->invoke("z = x.r.test( 'b' );", 0, 0);
469             ASSERT_EQUALS(false, s->getBoolean("z"));
470 
471             s->invoke("z = x.r.test( 'a' );", 0, 0);
472             ASSERT_EQUALS(true, s->getBoolean("z"));
473 
474             s->invoke("z = x.r.test( 'ba' );", 0, 0);
475             ASSERT_EQUALS(false, s->getBoolean("z"));
476 
477             s->invoke("z = { a : x.r };", 0, 0);
478 
479             BSONObj out = s->getObject("z");
480             ASSERT_EQUALS((string) "^a", out["a"].regex());
481             ASSERT_EQUALS((string) "i", out["a"].regexFlags());
482 
483             // This regex used to cause a segfault because x isn't a valid flag for a js RegExp.
484             // Now it throws a JS exception.
485             BSONObj invalidRegex = BSON_ARRAY(BSON("regex" << BSONRegEx("asdf", "x")));
486             const char* code =
487                 "function (obj) {"
488                 "    var threw = false;"
489                 "    try {"
490                 "        obj.regex;"  // should throw
491                 "    } catch(e) {"
492                 "         threw = true;"
493                 "    }"
494                 "    assert(threw);"
495                 "}";
496             ASSERT_EQUALS(s->invoke(code, &invalidRegex, NULL), 0);
497         }
498 
499         // array
500         {
501             BSONObj o = fromjson("{r:[1,2,3]}");
502             s->setObject("x", o, false);
503             BSONObj out = s->getObject("x");
504             ASSERT_EQUALS(Array, out.firstElement().type());
505 
506             s->setObject("x", o, true);
507             out = s->getObject("x");
508             ASSERT_EQUALS(Array, out.firstElement().type());
509         }
510 
511         // symbol
512         {
513             // test mutable object with symbol type
514             BSONObjBuilder builder;
515             builder.appendSymbol("sym", "value");
516             BSONObj in = builder.done();
517             s->setObject("x", in, false);
518             BSONObj out = s->getObject("x");
519             ASSERT_EQUALS(Symbol, out.firstElement().type());
520 
521             // readonly
522             s->setObject("x", in, true);
523             out = s->getObject("x");
524             ASSERT_EQUALS(Symbol, out.firstElement().type());
525         }
526     }
527 };
528 
529 class SpecialDBTypes {
530 public:
run()531     void run() {
532         unique_ptr<Scope> s(getGlobalScriptEngine()->newScope());
533 
534         BSONObjBuilder b;
535         b.appendTimestamp("a", 123456789);
536         b.appendMinKey("b");
537         b.appendMaxKey("c");
538         b.append("d", Timestamp(1234, 9876));
539 
540 
541         {
542             BSONObj t = b.done();
543             ASSERT_EQUALS(Date_t::fromMillisSinceEpoch(1234000), t["d"].timestampTime());
544             ASSERT_EQUALS(9876U, t["d"].timestampInc());
545         }
546 
547         s->setObject("z", b.obj());
548 
549         ASSERT(s->invoke("y = { a : z.a , b : z.b , c : z.c , d: z.d }", 0, 0) == 0);
550 
551         BSONObj out = s->getObject("y");
552         ASSERT_EQUALS(bsonTimestamp, out["a"].type());
553         ASSERT_EQUALS(MinKey, out["b"].type());
554         ASSERT_EQUALS(MaxKey, out["c"].type());
555         ASSERT_EQUALS(bsonTimestamp, out["d"].type());
556 
557         ASSERT_EQUALS(9876U, out["d"].timestampInc());
558         ASSERT_EQUALS(Date_t::fromMillisSinceEpoch(1234000), out["d"].timestampTime());
559         ASSERT_EQUALS(Date_t::fromMillisSinceEpoch(123456789), out["a"].date());
560     }
561 };
562 
563 class TypeConservation {
564 public:
run()565     void run() {
566         unique_ptr<Scope> s(getGlobalScriptEngine()->newScope());
567 
568         //  --  A  --
569 
570         BSONObj o;
571         {
572             BSONObjBuilder b;
573             b.append("a", (int)5);
574             b.append("b", 5.6);
575             o = b.obj();
576         }
577         ASSERT_EQUALS(NumberInt, o["a"].type());
578         ASSERT_EQUALS(NumberDouble, o["b"].type());
579 
580         s->setObject("z", o);
581         s->invoke("return z", 0, 0);
582         BSONObj out = s->getObject("__returnValue");
583         ASSERT_EQUALS(5, out["a"].number());
584         ASSERT_EQUALS(5.6, out["b"].number());
585 
586         ASSERT_EQUALS(NumberDouble, out["b"].type());
587         ASSERT_EQUALS(NumberInt, out["a"].type());
588 
589         //  --  B  --
590 
591         {
592             BSONObjBuilder b;
593             b.append("a", (int)5);
594             b.append("b", 5.6);
595             o = b.obj();
596         }
597 
598         s->setObject("z", o, false);
599         s->invoke("return z", 0, 0);
600         out = s->getObject("__returnValue");
601         ASSERT_EQUALS(5, out["a"].number());
602         ASSERT_EQUALS(5.6, out["b"].number());
603 
604         ASSERT_EQUALS(NumberDouble, out["b"].type());
605         ASSERT_EQUALS(NumberInt, out["a"].type());
606 
607 
608         //  -- C --
609 
610         {
611             BSONObjBuilder b;
612 
613             {
614                 BSONObjBuilder c;
615                 c.append("0", 5.5);
616                 c.append("1", 6);
617                 b.appendArray("a", c.obj());
618             }
619 
620             o = b.obj();
621         }
622 
623         ASSERT_EQUALS(NumberDouble, o["a"].embeddedObjectUserCheck()["0"].type());
624         ASSERT_EQUALS(NumberInt, o["a"].embeddedObjectUserCheck()["1"].type());
625 
626         s->setObject("z", o, false);
627         out = s->getObject("z");
628 
629         ASSERT_EQUALS(NumberDouble, out["a"].embeddedObjectUserCheck()["0"].type());
630         ASSERT_EQUALS(NumberInt, out["a"].embeddedObjectUserCheck()["1"].type());
631 
632         s->invokeSafe("z.z = 5;", 0, 0);
633         out = s->getObject("z");
634         ASSERT_EQUALS(5, out["z"].number());
635         ASSERT_EQUALS(NumberDouble, out["a"].embeddedObjectUserCheck()["0"].type());
636         // Commenting so that v8 tests will work
637         // TODO: this is technically bad, but here to make sure that i understand the behavior
638         // ASSERT_EQUALS( NumberDouble , out["a"].embeddedObjectUserCheck()["1"].type() );
639 
640 
641         // Eliot says I don't have to worry about this case
642 
643         //            // -- D --
644         //
645         //            o = fromjson( "{a:3.0,b:4.5}" );
646         //            ASSERT_EQUALS( NumberDouble , o["a"].type() );
647         //            ASSERT_EQUALS( NumberDouble , o["b"].type() );
648         //
649         //            s->setObject( "z" , o , false );
650         //            s->invoke( "return z" , BSONObj() );
651         //            out = s->getObject( "__returnValue" );
652         //            ASSERT_EQUALS( 3 , out["a"].number() );
653         //            ASSERT_EQUALS( 4.5 , out["b"].number() );
654         //
655         //            ASSERT_EQUALS( NumberDouble , out["b"].type() );
656         //            ASSERT_EQUALS( NumberDouble , out["a"].type() );
657         //
658     }
659 };
660 
661 class NumberLong {
662 public:
run()663     void run() {
664         unique_ptr<Scope> s(getGlobalScriptEngine()->newScope());
665         BSONObjBuilder b;
666         long long val = (long long)(0xbabadeadbeefbaddULL);
667         b.append("a", val);
668         BSONObj in = b.obj();
669         s->setObject("a", in);
670         BSONObj out = s->getObject("a");
671         ASSERT_EQUALS(mongo::NumberLong, out.firstElement().type());
672 
673         ASSERT(s->exec("b = {b:a.a}", "foo", false, true, false));
674         out = s->getObject("b");
675         ASSERT_EQUALS(mongo::NumberLong, out.firstElement().type());
676         if (val != out.firstElement().numberLong()) {
677             cout << val << endl;
678             cout << out.firstElement().numberLong() << endl;
679             cout << out.toString() << endl;
680             ASSERT_EQUALS(val, out.firstElement().numberLong());
681         }
682 
683         ASSERT(s->exec("c = {c:a.a.toString()}", "foo", false, true, false));
684         out = s->getObject("c");
685         stringstream ss;
686         ss << "NumberLong(\"" << val << "\")";
687         ASSERT_EQUALS(ss.str(), out.firstElement().valuestr());
688 
689         ASSERT(s->exec("d = {d:a.a.toNumber()}", "foo", false, true, false));
690         out = s->getObject("d");
691         ASSERT_EQUALS(NumberDouble, out.firstElement().type());
692         ASSERT_EQUALS(double(val), out.firstElement().number());
693 
694         ASSERT(s->exec("e = {e:a.a.floatApprox}", "foo", false, true, false));
695         out = s->getObject("e");
696         ASSERT_EQUALS(NumberDouble, out.firstElement().type());
697         ASSERT_EQUALS(double(val), out.firstElement().number());
698 
699         ASSERT(s->exec("f = {f:a.a.top}", "foo", false, true, false));
700         out = s->getObject("f");
701         ASSERT(NumberDouble == out.firstElement().type() || NumberInt == out.firstElement().type());
702 
703         s->setObject("z", BSON("z" << (long long)(4)));
704         ASSERT(s->exec("y = {y:z.z.top}", "foo", false, true, false));
705         out = s->getObject("y");
706         ASSERT_EQUALS(NumberDouble, out.firstElement().type());
707 
708         ASSERT(s->exec("x = {x:z.z.floatApprox}", "foo", false, true, false));
709         out = s->getObject("x");
710         ASSERT(NumberDouble == out.firstElement().type() || NumberInt == out.firstElement().type());
711         ASSERT_EQUALS(double(4), out.firstElement().number());
712 
713         ASSERT(s->exec("w = {w:z.z}", "foo", false, true, false));
714         out = s->getObject("w");
715         ASSERT_EQUALS(mongo::NumberLong, out.firstElement().type());
716         ASSERT_EQUALS(4, out.firstElement().numberLong());
717     }
718 };
719 
720 class NumberLong2 {
721 public:
run()722     void run() {
723         unique_ptr<Scope> s(getGlobalScriptEngine()->newScope());
724 
725         BSONObj in;
726         {
727             BSONObjBuilder b;
728             b.append("a", 5);
729             b.append("b", (long long)5);
730             b.append("c", (long long)pow(2.0, 29));
731             b.append("d", (long long)pow(2.0, 30));
732             b.append("e", (long long)pow(2.0, 31));
733             b.append("f", (long long)pow(2.0, 45));
734             in = b.obj();
735         }
736         s->setObject("a", in);
737 
738         ASSERT(s->exec("x = tojson( a ); ", "foo", false, true, false));
739         string outString = s->getString("x");
740 
741         ASSERT(s->exec((string) "y = " + outString, "foo2", false, true, false));
742         BSONObj out = s->getObject("y");
743         ASSERT_BSONOBJ_EQ(in, out);
744     }
745 };
746 
747 class NumberLongUnderLimit {
748 public:
run()749     void run() {
750         unique_ptr<Scope> s(getGlobalScriptEngine()->newScope());
751 
752         BSONObjBuilder b;
753         // limit is 2^53
754         long long val = (long long)(9007199254740991ULL);
755         b.append("a", val);
756         BSONObj in = b.obj();
757         s->setObject("a", in);
758         BSONObj out = s->getObject("a");
759         ASSERT_EQUALS(mongo::NumberLong, out.firstElement().type());
760 
761         ASSERT(s->exec("b = {b:a.a}", "foo", false, true, false));
762         out = s->getObject("b");
763         ASSERT_EQUALS(mongo::NumberLong, out.firstElement().type());
764         if (val != out.firstElement().numberLong()) {
765             cout << val << endl;
766             cout << out.firstElement().numberLong() << endl;
767             cout << out.toString() << endl;
768             ASSERT_EQUALS(val, out.firstElement().numberLong());
769         }
770 
771         ASSERT(s->exec("c = {c:a.a.toString()}", "foo", false, true, false));
772         out = s->getObject("c");
773         stringstream ss;
774         ss << "NumberLong(\"" << val << "\")";
775         ASSERT_EQUALS(ss.str(), out.firstElement().valuestr());
776 
777         ASSERT(s->exec("d = {d:a.a.toNumber()}", "foo", false, true, false));
778         out = s->getObject("d");
779         ASSERT_EQUALS(NumberDouble, out.firstElement().type());
780         ASSERT_EQUALS(double(val), out.firstElement().number());
781 
782         ASSERT(s->exec("e = {e:a.a.floatApprox}", "foo", false, true, false));
783         out = s->getObject("e");
784         ASSERT_EQUALS(NumberDouble, out.firstElement().type());
785         ASSERT_EQUALS(double(val), out.firstElement().number());
786 
787         ASSERT(s->exec("f = {f:a.a.top}", "foo", false, true, false));
788         out = s->getObject("f");
789         ASSERT(Undefined == out.firstElement().type());
790     }
791 };
792 
793 class NumberDecimal {
794 public:
run()795     void run() {
796         unique_ptr<Scope> s(getGlobalScriptEngine()->newScope());
797         BSONObjBuilder b;
798         Decimal128 val = Decimal128("2.010");
799         b.append("a", val);
800         BSONObj in = b.obj();
801         s->setObject("a", in);
802 
803         // Test the scope object
804         BSONObj out = s->getObject("a");
805         ASSERT_EQUALS(mongo::NumberDecimal, out.firstElement().type());
806         ASSERT_TRUE(val.isEqual(out.firstElement().numberDecimal()));
807 
808         ASSERT(s->exec("b = {b:a.a}", "foo", false, true, false));
809         out = s->getObject("b");
810         ASSERT_EQUALS(mongo::NumberDecimal, out.firstElement().type());
811         ASSERT_TRUE(val.isEqual(out.firstElement().numberDecimal()));
812 
813         // Test that the appropriate string output is generated
814         ASSERT(s->exec("c = {c:a.a.toString()}", "foo", false, true, false));
815         out = s->getObject("c");
816         stringstream ss;
817         ss << "NumberDecimal(\"" << val.toString() << "\")";
818         ASSERT_EQUALS(ss.str(), out.firstElement().valuestr());
819     }
820 };
821 
822 class NumberDecimalGetFromScope {
823 public:
run()824     void run() {
825         unique_ptr<Scope> s(getGlobalScriptEngine()->newScope());
826         ASSERT(s->exec("a = 5;", "a", false, true, false));
827         ASSERT_TRUE(Decimal128(5).isEqual(s->getNumberDecimal("a")));
828     }
829 };
830 
831 class NumberDecimalBigObject {
832 public:
run()833     void run() {
834         unique_ptr<Scope> s(getGlobalScriptEngine()->newScope());
835 
836         BSONObj in;
837         {
838             BSONObjBuilder b;
839             b.append("a", 5);
840             b.append("b", Decimal128("1.5E-3000"));
841             b.append("c", Decimal128("1.5E-1"));
842             b.append("d", Decimal128("1.5E3000"));
843             b.append("e", Decimal128("Infinity"));
844             b.append("f", Decimal128("NaN"));
845             in = b.obj();
846         }
847         s->setObject("a", in);
848 
849         ASSERT(s->exec("x = tojson( a ); ", "foo", false, true, false));
850         string outString = s->getString("x");
851 
852         ASSERT(s->exec((string) "y = " + outString, "foo2", false, true, false));
853         BSONObj out = s->getObject("y");
854         ASSERT_BSONOBJ_EQ(in, out);
855     }
856 };
857 
858 class MaxTimestamp {
859 public:
run()860     void run() {
861         unique_ptr<Scope> s(getGlobalScriptEngine()->newScope());
862 
863         // Timestamp 't' component can exceed max for int32_t.
864         BSONObj in;
865         {
866             BSONObjBuilder b;
867             b.bb().appendNum(static_cast<char>(bsonTimestamp));
868             b.bb().appendStr("a");
869             b.bb().appendNum(std::numeric_limits<unsigned long long>::max());
870 
871             in = b.obj();
872         }
873         s->setObject("a", in);
874 
875         ASSERT(s->exec("x = tojson( a ); ", "foo", false, true, false));
876     }
877 };
878 
879 class WeirdObjects {
880 public:
build(int depth)881     BSONObj build(int depth) {
882         BSONObjBuilder b;
883         b.append("0", depth);
884         if (depth > 0)
885             b.appendArray("1", build(depth - 1));
886         return b.obj();
887     }
888 
run()889     void run() {
890         unique_ptr<Scope> s(getGlobalScriptEngine()->newScope());
891 
892         for (int i = 5; i < 100; i += 10) {
893             s->setObject("a", build(i), false);
894             s->invokeSafe("tojson( a )", 0, 0);
895 
896             s->setObject("a", build(5), true);
897             s->invokeSafe("tojson( a )", 0, 0);
898         }
899     }
900 };
901 
902 /**
903  * Test exec() timeout value terminates execution (SERVER-8053)
904  */
905 class ExecTimeout {
906 public:
run()907     void run() {
908         unique_ptr<Scope> scope(getGlobalScriptEngine()->newScope());
909 
910         // assert timeout occurred
911         ASSERT(!scope->exec("var a = 1; while (true) { ; }", "ExecTimeout", false, true, false, 1));
912     }
913 };
914 
915 /**
916  * Test exec() timeout value terminates execution (SERVER-8053)
917  */
918 class ExecNoTimeout {
919 public:
run()920     void run() {
921         unique_ptr<Scope> scope(getGlobalScriptEngine()->newScope());
922 
923         // assert no timeout occurred
924         ASSERT(scope->exec("var a = function() { return 1; }",
925                            "ExecNoTimeout",
926                            false,
927                            true,
928                            false,
929                            5 * 60 * 1000));
930     }
931 };
932 
933 /**
934  * Test invoke() timeout value terminates execution (SERVER-8053)
935  */
936 class InvokeTimeout {
937 public:
run()938     void run() {
939         unique_ptr<Scope> scope(getGlobalScriptEngine()->newScope());
940 
941         // scope timeout after 500ms
942         bool caught = false;
943         try {
944             scope->invokeSafe(
945                 "function() {         "
946                 "    while (true) { } "
947                 "}                    ",
948                 0,
949                 0,
950                 1);
951         } catch (const DBException&) {
952             caught = true;
953         }
954         ASSERT(caught);
955     }
956 };
957 
958 class SleepInterruption {
959 public:
run()960     void run() {
961         std::shared_ptr<Scope> scope(getGlobalScriptEngine()->newScope());
962 
963         Promise<void> sleepPromise{};
964         auto sleepFuture = sleepPromise.getFuture();
965 
966         Promise<void> awakenedPromise{};
967         auto awakenedFuture = awakenedPromise.getFuture();
968 
969         // Spawn a thread which attempts to sleep indefinitely.
970         stdx::thread([
971             preSleep = std::move(sleepPromise),
972             onAwake = std::move(awakenedPromise),
973             scope
974         ]() mutable {
975             preSleep.emplaceValue();
976             onAwake.setWith([&] {
977                 scope->exec(
978                     ""
979                     "  try {"
980                     "    sleep(99999999999);"
981                     "  } finally {"
982                     "    throw \"FAILURE\";"
983                     "  }"
984                     "",
985                     "test",
986                     false,
987                     false,
988                     true);
989             });
990         }).detach();
991 
992         // Wait until just before the sleep begins.
993         sleepFuture.get();
994 
995         // Attempt to wait until Javascript enters the sleep.
996         // It's OK if we kill the function prematurely, before it begins sleeping. Either cause of
997         // death will emit an error with the Interrupted code.
998         sleepsecs(1);
999 
1000         // Send the operation a kill signal.
1001         scope->kill();
1002 
1003         // Wait for the error.
1004         auto result = awakenedFuture.getNoThrow();
1005         ASSERT_EQ(ErrorCodes::Interrupted, result);
1006     }
1007 };
1008 
1009 /**
1010  * Test invoke() timeout value does not terminate execution (SERVER-8053)
1011  */
1012 class InvokeNoTimeout {
1013 public:
run()1014     void run() {
1015         unique_ptr<Scope> scope(getGlobalScriptEngine()->newScope());
1016 
1017         // invoke completes before timeout
1018         scope->invokeSafe(
1019             "function() { "
1020             "  for (var i=0; i<1; i++) { ; } "
1021             "} ",
1022             0,
1023             0,
1024             5 * 60 * 1000);
1025     }
1026 };
1027 
1028 
1029 class Utf8Check {
1030 public:
Utf8Check()1031     Utf8Check() {
1032         reset();
1033     }
~Utf8Check()1034     ~Utf8Check() {
1035         reset();
1036     }
run()1037     void run() {
1038         if (!getGlobalScriptEngine()->utf8Ok()) {
1039             mongo::unittest::log() << "warning: utf8 not supported" << endl;
1040             return;
1041         }
1042         string utf8ObjSpec = "{'_id':'\\u0001\\u007f\\u07ff\\uffff'}";
1043         BSONObj utf8Obj = fromjson(utf8ObjSpec);
1044 
1045         const ServiceContext::UniqueOperationContext opCtxPtr = cc().makeOperationContext();
1046         OperationContext& opCtx = *opCtxPtr;
1047         DBDirectClient client(&opCtx);
1048 
1049         client.insert(ns(), utf8Obj);
1050         client.eval("unittest",
1051                     "v = db.jstests.utf8check.findOne(); db.jstests.utf8check.remove( {} ); "
1052                     "db.jstests.utf8check.insert( v );");
1053         check(utf8Obj, client.findOne(ns(), BSONObj()));
1054     }
1055 
1056 private:
check(const BSONObj & one,const BSONObj & two)1057     void check(const BSONObj& one, const BSONObj& two) {
1058         if (one.woCompare(two) != 0) {
1059             static string fail =
1060                 string("Assertion failure expected ") + one.toString() + ", got " + two.toString();
1061             FAIL(fail.c_str());
1062         }
1063     }
1064 
reset()1065     void reset() {
1066         const ServiceContext::UniqueOperationContext opCtxPtr = cc().makeOperationContext();
1067         OperationContext& opCtx = *opCtxPtr;
1068         DBDirectClient client(&opCtx);
1069 
1070         client.dropCollection(ns());
1071     }
1072 
ns()1073     static const char* ns() {
1074         return "unittest.jstests.utf8check";
1075     }
1076 };
1077 
1078 class LongUtf8String {
1079 public:
LongUtf8String()1080     LongUtf8String() {
1081         reset();
1082     }
~LongUtf8String()1083     ~LongUtf8String() {
1084         reset();
1085     }
run()1086     void run() {
1087         if (!getGlobalScriptEngine()->utf8Ok())
1088             return;
1089 
1090         const ServiceContext::UniqueOperationContext opCtxPtr = cc().makeOperationContext();
1091         OperationContext& opCtx = *opCtxPtr;
1092         DBDirectClient client(&opCtx);
1093 
1094         client.eval("unittest",
1095                     "db.jstests.longutf8string.save( {_id:'\\uffff\\uffff\\uffff\\uffff'} )");
1096     }
1097 
1098 private:
reset()1099     void reset() {
1100         const ServiceContext::UniqueOperationContext opCtxPtr = cc().makeOperationContext();
1101         OperationContext& opCtx = *opCtxPtr;
1102         DBDirectClient client(&opCtx);
1103 
1104         client.dropCollection(ns());
1105     }
1106 
ns()1107     static const char* ns() {
1108         return "unittest.jstests.longutf8string";
1109     }
1110 };
1111 
1112 class InvalidUTF8Check {
1113 public:
run()1114     void run() {
1115         if (!getGlobalScriptEngine()->utf8Ok())
1116             return;
1117 
1118         unique_ptr<Scope> s;
1119         s.reset(getGlobalScriptEngine()->newScope());
1120 
1121         BSONObj b;
1122         {
1123             char crap[5];
1124 
1125             crap[0] = (char)128;
1126             crap[1] = 17;
1127             crap[2] = (char)128;
1128             crap[3] = 17;
1129             crap[4] = 0;
1130 
1131             BSONObjBuilder bb;
1132             bb.append("x", crap);
1133             b = bb.obj();
1134         }
1135 
1136         // cout << "ELIOT: " << b.jsonString() << endl;
1137         // its ok  if this is handled by js, just can't create a c++ exception
1138         s->invoke("x=this.x.length;", 0, &b);
1139     }
1140 };
1141 
1142 class CodeTests {
1143 public:
run()1144     void run() {
1145         unique_ptr<Scope> s(getGlobalScriptEngine()->newScope());
1146 
1147         {
1148             BSONObjBuilder b;
1149             b.append("a", 1);
1150             b.appendCode("b", "function(){ out.b = 11; }");
1151             b.appendCodeWScope("c", "function(){ out.c = 12; }", BSONObj());
1152             b.appendCodeWScope("d", "function(){ out.d = 13 + bleh; }", BSON("bleh" << 5));
1153             s->setObject("foo", b.obj());
1154         }
1155 
1156         s->invokeSafe("out = {}; out.a = foo.a; foo.b(); foo.c();", 0, 0);
1157         BSONObj out = s->getObject("out");
1158 
1159         ASSERT_EQUALS(1, out["a"].number());
1160         ASSERT_EQUALS(11, out["b"].number());
1161         ASSERT_EQUALS(12, out["c"].number());
1162 
1163         // Guess we don't care about this
1164         // s->invokeSafe( "foo.d() " , BSONObj() );
1165         // out = s->getObject( "out" );
1166         // ASSERT_EQUALS( 18 , out["d"].number() );
1167     }
1168 };
1169 
1170 namespace RoundTripTests {
1171 
1172 // Inherit from this class to test round tripping of JSON objects
1173 class TestRoundTrip {
1174 public:
~TestRoundTrip()1175     virtual ~TestRoundTrip() {}
run()1176     void run() {
1177         // Insert in Javascript -> Find using DBDirectClient
1178 
1179         // Drop the collection
1180         const ServiceContext::UniqueOperationContext opCtxPtr = cc().makeOperationContext();
1181         OperationContext& opCtx = *opCtxPtr;
1182         DBDirectClient client(&opCtx);
1183 
1184         client.dropCollection("unittest.testroundtrip");
1185 
1186         // Insert in Javascript
1187         stringstream jsInsert;
1188         jsInsert << "db.testroundtrip.insert(" << jsonIn() << ")";
1189         ASSERT_TRUE(client.eval("unittest", jsInsert.str()));
1190 
1191         // Find using DBDirectClient
1192         BSONObj excludeIdProjection = BSON("_id" << 0);
1193         BSONObj directFind = client.findOne("unittest.testroundtrip", "", &excludeIdProjection);
1194         bsonEquals(bson(), directFind);
1195 
1196 
1197         // Insert using DBDirectClient -> Find in Javascript
1198 
1199         // Drop the collection
1200         client.dropCollection("unittest.testroundtrip");
1201 
1202         // Insert using DBDirectClient
1203         client.insert("unittest.testroundtrip", bson());
1204 
1205         // Find in Javascript
1206         stringstream jsFind;
1207         jsFind << "dbref = db.testroundtrip.findOne( { } , { _id : 0 } )\n"
1208                << "assert.eq(dbref, " << jsonOut() << ")";
1209         ASSERT_TRUE(client.eval("unittest", jsFind.str()));
1210     }
1211 
1212 protected:
1213     // Methods that must be defined by child classes
1214     virtual BSONObj bson() const = 0;
1215     virtual string json() const = 0;
1216 
1217     // This can be overriden if a different meaning of equality besides woCompare is needed
bsonEquals(const BSONObj & expected,const BSONObj & actual)1218     virtual void bsonEquals(const BSONObj& expected, const BSONObj& actual) {
1219         if (expected.woCompare(actual)) {
1220             ::mongo::log() << "want:" << expected.jsonString() << " size: " << expected.objsize()
1221                            << endl;
1222             ::mongo::log() << "got :" << actual.jsonString() << " size: " << actual.objsize()
1223                            << endl;
1224             ::mongo::log() << expected.hexDump() << endl;
1225             ::mongo::log() << actual.hexDump() << endl;
1226         }
1227         ASSERT(!expected.woCompare(actual));
1228     }
1229 
1230     // This can be overriden if the JSON representation is altered on the round trip
jsonIn() const1231     virtual string jsonIn() const {
1232         return json();
1233     }
jsonOut() const1234     virtual string jsonOut() const {
1235         return json();
1236     }
1237 };
1238 
1239 class DBRefTest : public TestRoundTrip {
bson() const1240     virtual BSONObj bson() const {
1241         BSONObjBuilder b;
1242         OID o;
1243         memset(&o, 0, 12);
1244         BSONObjBuilder subBuilder(b.subobjStart("a"));
1245         subBuilder.append("$ref", "ns");
1246         subBuilder.append("$id", o);
1247         subBuilder.done();
1248         return b.obj();
1249     }
json() const1250     virtual string json() const {
1251         return "{ \"a\" : DBRef( \"ns\", ObjectId( \"000000000000000000000000\" ) ) }";
1252     }
1253 
1254     // A "fetch" function is added to the DBRef object when it is inserted using the
1255     // constructor, so we need to compare the fields individually
bsonEquals(const BSONObj & expected,const BSONObj & actual)1256     virtual void bsonEquals(const BSONObj& expected, const BSONObj& actual) {
1257         ASSERT_EQUALS(expected["a"].type(), actual["a"].type());
1258         ASSERT_EQUALS(expected["a"]["$id"].OID(), actual["a"]["$id"].OID());
1259         ASSERT_EQUALS(expected["a"]["$ref"].String(), actual["a"]["$ref"].String());
1260     }
1261 };
1262 
1263 class DBPointerTest : public TestRoundTrip {
bson() const1264     virtual BSONObj bson() const {
1265         BSONObjBuilder b;
1266         OID o;
1267         memset(&o, 0, 12);
1268         b.appendDBRef("a", "ns", o);
1269         return b.obj();
1270     }
json() const1271     virtual string json() const {
1272         return "{ \"a\" : DBPointer( \"ns\", ObjectId( \"000000000000000000000000\" ) ) }";
1273     }
1274 };
1275 
1276 class InformalDBRefTest : public TestRoundTrip {
bson() const1277     virtual BSONObj bson() const {
1278         BSONObjBuilder b;
1279         BSONObjBuilder subBuilder(b.subobjStart("a"));
1280         subBuilder.append("$ref", "ns");
1281         subBuilder.append("$id", "000000000000000000000000");
1282         subBuilder.done();
1283         return b.obj();
1284     }
1285 
1286     // Don't need to return anything because we are overriding both jsonOut and jsonIn
json() const1287     virtual string json() const {
1288         return "";
1289     }
1290 
1291     // Need to override these because the JSON doesn't actually round trip.
1292     // An object with "$ref" and "$id" fields is handled specially and different on the way out.
jsonOut() const1293     virtual string jsonOut() const {
1294         return "{ \"a\" : DBRef( \"ns\", \"000000000000000000000000\" ) }";
1295     }
jsonIn() const1296     virtual string jsonIn() const {
1297         stringstream ss;
1298         ss << "{ \"a\" : { \"$ref\" : \"ns\" , "
1299            << "\"$id\" : \"000000000000000000000000\" } }";
1300         return ss.str();
1301     }
1302 };
1303 
1304 class InformalDBRefOIDTest : public TestRoundTrip {
bson() const1305     virtual BSONObj bson() const {
1306         BSONObjBuilder b;
1307         OID o;
1308         memset(&o, 0, 12);
1309         BSONObjBuilder subBuilder(b.subobjStart("a"));
1310         subBuilder.append("$ref", "ns");
1311         subBuilder.append("$id", o);
1312         subBuilder.done();
1313         return b.obj();
1314     }
1315 
1316     // Don't need to return anything because we are overriding both jsonOut and jsonIn
json() const1317     virtual string json() const {
1318         return "";
1319     }
1320 
1321     // Need to override these because the JSON doesn't actually round trip.
1322     // An object with "$ref" and "$id" fields is handled specially and different on the way out.
jsonOut() const1323     virtual string jsonOut() const {
1324         return "{ \"a\" : DBRef( \"ns\", ObjectId( \"000000000000000000000000\" ) ) }";
1325     }
jsonIn() const1326     virtual string jsonIn() const {
1327         stringstream ss;
1328         ss << "{ \"a\" : { \"$ref\" : \"ns\" , "
1329            << "\"$id\" : ObjectId( \"000000000000000000000000\" ) } }";
1330         return ss.str();
1331     }
1332 };
1333 
1334 class InformalDBRefExtraFieldTest : public TestRoundTrip {
bson() const1335     virtual BSONObj bson() const {
1336         BSONObjBuilder b;
1337         OID o;
1338         memset(&o, 0, 12);
1339         BSONObjBuilder subBuilder(b.subobjStart("a"));
1340         subBuilder.append("$ref", "ns");
1341         subBuilder.append("$id", o);
1342         subBuilder.append("otherfield", "value");
1343         subBuilder.done();
1344         return b.obj();
1345     }
1346 
1347     // Don't need to return anything because we are overriding both jsonOut and jsonIn
json() const1348     virtual string json() const {
1349         return "";
1350     }
1351 
1352     // Need to override these because the JSON doesn't actually round trip.
1353     // An object with "$ref" and "$id" fields is handled specially and different on the way out.
jsonOut() const1354     virtual string jsonOut() const {
1355         return "{ \"a\" : DBRef( \"ns\", ObjectId( \"000000000000000000000000\" ) ) }";
1356     }
jsonIn() const1357     virtual string jsonIn() const {
1358         stringstream ss;
1359         ss << "{ \"a\" : { \"$ref\" : \"ns\" , "
1360            << "\"$id\" : ObjectId( \"000000000000000000000000\" ) , "
1361            << "\"otherfield\" : \"value\" } }";
1362         return ss.str();
1363     }
1364 };
1365 
1366 class Empty : public TestRoundTrip {
bson() const1367     virtual BSONObj bson() const {
1368         BSONObjBuilder b;
1369         return b.obj();
1370     }
json() const1371     virtual string json() const {
1372         return "{}";
1373     }
1374 };
1375 
1376 class EmptyWithSpace : public TestRoundTrip {
bson() const1377     virtual BSONObj bson() const {
1378         BSONObjBuilder b;
1379         return b.obj();
1380     }
json() const1381     virtual string json() const {
1382         return "{ }";
1383     }
1384 };
1385 
1386 class SingleString : public TestRoundTrip {
bson() const1387     virtual BSONObj bson() const {
1388         BSONObjBuilder b;
1389         b.append("a", "b");
1390         return b.obj();
1391     }
json() const1392     virtual string json() const {
1393         return "{ \"a\" : \"b\" }";
1394     }
1395 };
1396 
1397 class EmptyStrings : public TestRoundTrip {
bson() const1398     virtual BSONObj bson() const {
1399         BSONObjBuilder b;
1400         b.append("", "");
1401         return b.obj();
1402     }
json() const1403     virtual string json() const {
1404         return "{ \"\" : \"\" }";
1405     }
1406 };
1407 
1408 class SingleNumber : public TestRoundTrip {
bson() const1409     virtual BSONObj bson() const {
1410         BSONObjBuilder b;
1411         b.append("a", 1);
1412         return b.obj();
1413     }
json() const1414     virtual string json() const {
1415         return "{ \"a\" : 1 }";
1416     }
1417 };
1418 
1419 class RealNumber : public TestRoundTrip {
bson() const1420     virtual BSONObj bson() const {
1421         BSONObjBuilder b;
1422         double d;
1423         ASSERT_OK(parseNumberFromString("0.7", &d));
1424         b.append("a", d);
1425         return b.obj();
1426     }
json() const1427     virtual string json() const {
1428         return "{ \"a\" : 0.7 }";
1429     }
1430 };
1431 
1432 class FancyNumber : public TestRoundTrip {
bson() const1433     virtual BSONObj bson() const {
1434         BSONObjBuilder b;
1435         double d;
1436         ASSERT_OK(parseNumberFromString("-4.4433e-2", &d));
1437         b.append("a", d);
1438         return b.obj();
1439     }
json() const1440     virtual string json() const {
1441         return "{ \"a\" : -4.4433e-2 }";
1442     }
1443 };
1444 
1445 class TwoElements : public TestRoundTrip {
bson() const1446     virtual BSONObj bson() const {
1447         BSONObjBuilder b;
1448         b.append("a", 1);
1449         b.append("b", "foo");
1450         return b.obj();
1451     }
json() const1452     virtual string json() const {
1453         return "{ \"a\" : 1, \"b\" : \"foo\" }";
1454     }
1455 };
1456 
1457 class Subobject : public TestRoundTrip {
bson() const1458     virtual BSONObj bson() const {
1459         BSONObjBuilder b;
1460         b.append("a", 1);
1461         BSONObjBuilder c;
1462         c.append("z", b.done());
1463         return c.obj();
1464     }
json() const1465     virtual string json() const {
1466         return "{ \"z\" : { \"a\" : 1 } }";
1467     }
1468 };
1469 
1470 class DeeplyNestedObject : public TestRoundTrip {
buildJson(int depth) const1471     virtual string buildJson(int depth) const {
1472         if (depth == 0) {
1473             return "{\"0\":true}";
1474         } else {
1475             std::stringstream ss;
1476             ss << "{\"" << depth << "\":" << buildJson(depth - 1) << "}";
1477             depth--;
1478             return ss.str();
1479         }
1480     }
buildBson(int depth) const1481     virtual BSONObj buildBson(int depth) const {
1482         BSONObjBuilder builder;
1483         if (depth == 0) {
1484             builder.append("0", true);
1485             return builder.obj();
1486         } else {
1487             std::stringstream ss;
1488             ss << depth;
1489             depth--;
1490             builder.append(ss.str(), buildBson(depth));
1491             return builder.obj();
1492         }
1493     }
bson() const1494     virtual BSONObj bson() const {
1495         return buildBson(35);
1496     }
json() const1497     virtual string json() const {
1498         return buildJson(35);
1499     }
1500 };
1501 
1502 class ArrayEmpty : public TestRoundTrip {
bson() const1503     virtual BSONObj bson() const {
1504         vector<int> arr;
1505         BSONObjBuilder b;
1506         b.append("a", arr);
1507         return b.obj();
1508     }
json() const1509     virtual string json() const {
1510         return "{ \"a\" : [] }";
1511     }
1512 };
1513 
1514 class Array : public TestRoundTrip {
bson() const1515     virtual BSONObj bson() const {
1516         vector<int> arr;
1517         arr.push_back(1);
1518         arr.push_back(2);
1519         arr.push_back(3);
1520         BSONObjBuilder b;
1521         b.append("a", arr);
1522         return b.obj();
1523     }
json() const1524     virtual string json() const {
1525         return "{ \"a\" : [ 1, 2, 3 ] }";
1526     }
1527 };
1528 
1529 class True : public TestRoundTrip {
bson() const1530     virtual BSONObj bson() const {
1531         BSONObjBuilder b;
1532         b.appendBool("a", true);
1533         return b.obj();
1534     }
json() const1535     virtual string json() const {
1536         return "{ \"a\" : true }";
1537     }
1538 };
1539 
1540 class False : public TestRoundTrip {
bson() const1541     virtual BSONObj bson() const {
1542         BSONObjBuilder b;
1543         b.appendBool("a", false);
1544         return b.obj();
1545     }
json() const1546     virtual string json() const {
1547         return "{ \"a\" : false }";
1548     }
1549 };
1550 
1551 class Null : public TestRoundTrip {
bson() const1552     virtual BSONObj bson() const {
1553         BSONObjBuilder b;
1554         b.appendNull("a");
1555         return b.obj();
1556     }
json() const1557     virtual string json() const {
1558         return "{ \"a\" : null }";
1559     }
1560 };
1561 
1562 class Undefined : public TestRoundTrip {
bson() const1563     virtual BSONObj bson() const {
1564         BSONObjBuilder b;
1565         b.appendUndefined("a");
1566         return b.obj();
1567     }
1568 
json() const1569     virtual string json() const {
1570         return "{ \"a\" : undefined }";
1571     }
1572 };
1573 
1574 class EscapedCharacters : public TestRoundTrip {
bson() const1575     virtual BSONObj bson() const {
1576         BSONObjBuilder b;
1577         b.append("a", "\" \\ / \b \f \n \r \t \v");
1578         return b.obj();
1579     }
json() const1580     virtual string json() const {
1581         return "{ \"a\" : \"\\\" \\\\ \\/ \\b \\f \\n \\r \\t \\v\" }";
1582     }
1583 };
1584 
1585 class NonEscapedCharacters : public TestRoundTrip {
bson() const1586     virtual BSONObj bson() const {
1587         BSONObjBuilder b;
1588         b.append("a", "% { a z $ # '  ");
1589         return b.obj();
1590     }
json() const1591     virtual string json() const {
1592         return "{ \"a\" : \"\\% \\{ \\a \\z \\$ \\# \\' \\ \" }";
1593     }
1594 };
1595 
1596 class AllowedControlCharacter : public TestRoundTrip {
bson() const1597     virtual BSONObj bson() const {
1598         BSONObjBuilder b;
1599         b.append("a", "\x7f");
1600         return b.obj();
1601     }
json() const1602     virtual string json() const {
1603         return "{ \"a\" : \"\x7f\" }";
1604     }
1605 };
1606 
1607 class NumbersInFieldName : public TestRoundTrip {
bson() const1608     virtual BSONObj bson() const {
1609         BSONObjBuilder b;
1610         b.append("b1", "b");
1611         return b.obj();
1612     }
json() const1613     virtual string json() const {
1614         return "{ b1 : \"b\" }";
1615     }
1616 };
1617 
1618 class EscapeFieldName : public TestRoundTrip {
bson() const1619     virtual BSONObj bson() const {
1620         BSONObjBuilder b;
1621         b.append("\n", "b");
1622         return b.obj();
1623     }
json() const1624     virtual string json() const {
1625         return "{ \"\\n\" : \"b\" }";
1626     }
1627 };
1628 
1629 class EscapedUnicodeToUtf8 : public TestRoundTrip {
bson() const1630     virtual BSONObj bson() const {
1631         BSONObjBuilder b;
1632         unsigned char u[7];
1633         u[0] = 0xe0 | 0x0a;
1634         u[1] = 0x80;
1635         u[2] = 0x80;
1636         u[3] = 0xe0 | 0x0a;
1637         u[4] = 0x80;
1638         u[5] = 0x80;
1639         u[6] = 0;
1640         b.append("a", (char*)u);
1641         BSONObj built = b.obj();
1642         ASSERT_EQUALS(string((char*)u), built.firstElement().valuestr());
1643         return built;
1644     }
json() const1645     virtual string json() const {
1646         return "{ \"a\" : \"\\ua000\\uA000\" }";
1647     }
1648 };
1649 
1650 class Utf8AllOnes : public TestRoundTrip {
bson() const1651     virtual BSONObj bson() const {
1652         BSONObjBuilder b;
1653         unsigned char u[8];
1654         u[0] = 0x01;
1655 
1656         u[1] = 0x7f;
1657 
1658         u[2] = 0xdf;
1659         u[3] = 0xbf;
1660 
1661         u[4] = 0xef;
1662         u[5] = 0xbf;
1663         u[6] = 0xbf;
1664 
1665         u[7] = 0;
1666 
1667         b.append("a", (char*)u);
1668         return b.obj();
1669     }
json() const1670     virtual string json() const {
1671         return "{ \"a\" : \"\\u0001\\u007f\\u07ff\\uffff\" }";
1672     }
1673 };
1674 
1675 class Utf8FirstByteOnes : public TestRoundTrip {
bson() const1676     virtual BSONObj bson() const {
1677         BSONObjBuilder b;
1678         unsigned char u[6];
1679         u[0] = 0xdc;
1680         u[1] = 0x80;
1681 
1682         u[2] = 0xef;
1683         u[3] = 0xbc;
1684         u[4] = 0x80;
1685 
1686         u[5] = 0;
1687 
1688         b.append("a", (char*)u);
1689         return b.obj();
1690     }
json() const1691     virtual string json() const {
1692         return "{ \"a\" : \"\\u0700\\uff00\" }";
1693     }
1694 };
1695 
1696 class BinData : public TestRoundTrip {
bson() const1697     virtual BSONObj bson() const {
1698         char z[3];
1699         z[0] = 'a';
1700         z[1] = 'b';
1701         z[2] = 'c';
1702         BSONObjBuilder b;
1703         b.appendBinData("a", 3, BinDataGeneral, z);
1704         return b.obj();
1705     }
json() const1706     virtual string json() const {
1707         return "{ \"a\" : BinData( 0 , \"YWJj\" ) }";
1708     }
1709 };
1710 
1711 class BinDataPaddedSingle : public TestRoundTrip {
bson() const1712     virtual BSONObj bson() const {
1713         char z[2];
1714         z[0] = 'a';
1715         z[1] = 'b';
1716         BSONObjBuilder b;
1717         b.appendBinData("a", 2, BinDataGeneral, z);
1718         return b.obj();
1719     }
json() const1720     virtual string json() const {
1721         return "{ \"a\" : BinData( 0 , \"YWI=\" ) }";
1722     }
1723 };
1724 
1725 class BinDataPaddedDouble : public TestRoundTrip {
bson() const1726     virtual BSONObj bson() const {
1727         char z[1];
1728         z[0] = 'a';
1729         BSONObjBuilder b;
1730         b.appendBinData("a", 1, BinDataGeneral, z);
1731         return b.obj();
1732     }
json() const1733     virtual string json() const {
1734         return "{ \"a\" : BinData( 0 , \"YQ==\" ) }";
1735     }
1736 };
1737 
1738 class BinDataAllChars : public TestRoundTrip {
bson() const1739     virtual BSONObj bson() const {
1740         unsigned char z[] = {0x00, 0x10, 0x83, 0x10, 0x51, 0x87, 0x20, 0x92, 0x8B, 0x30,
1741                              0xD3, 0x8F, 0x41, 0x14, 0x93, 0x51, 0x55, 0x97, 0x61, 0x96,
1742                              0x9B, 0x71, 0xD7, 0x9F, 0x82, 0x18, 0xA3, 0x92, 0x59, 0xA7,
1743                              0xA2, 0x9A, 0xAB, 0xB2, 0xDB, 0xAF, 0xC3, 0x1C, 0xB3, 0xD3,
1744                              0x5D, 0xB7, 0xE3, 0x9E, 0xBB, 0xF3, 0xDF, 0xBF};
1745         BSONObjBuilder b;
1746         b.appendBinData("a", 48, BinDataGeneral, z);
1747         return b.obj();
1748     }
json() const1749     virtual string json() const {
1750         stringstream ss;
1751         ss << "{ \"a\" : BinData( 0 , \"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1752            << "abcdefghijklmnopqrstuvwxyz0123456789+/\" ) }";
1753         return ss.str();
1754     }
1755 };
1756 
1757 class Date : public TestRoundTrip {
bson() const1758     virtual BSONObj bson() const {
1759         BSONObjBuilder b;
1760         b.appendDate("a", Date_t());
1761         return b.obj();
1762     }
json() const1763     virtual string json() const {
1764         return "{ \"a\" : new Date( 0 ) }";
1765     }
1766 };
1767 
1768 class DateNonzero : public TestRoundTrip {
bson() const1769     virtual BSONObj bson() const {
1770         BSONObjBuilder b;
1771         b.appendDate("a", Date_t::fromMillisSinceEpoch(100));
1772         return b.obj();
1773     }
json() const1774     virtual string json() const {
1775         return "{ \"a\" : new Date( 100 ) }";
1776     }
1777 };
1778 
1779 class DateNegative : public TestRoundTrip {
bson() const1780     virtual BSONObj bson() const {
1781         BSONObjBuilder b;
1782         b.appendDate("a", Date_t::fromMillisSinceEpoch(-1));
1783         return b.obj();
1784     }
json() const1785     virtual string json() const {
1786         return "{ \"a\" : new Date( -1 ) }";
1787     }
1788 };
1789 
1790 class JSTimestamp : public TestRoundTrip {
bson() const1791     virtual BSONObj bson() const {
1792         BSONObjBuilder b;
1793         b.append("a", Timestamp(20, 5));
1794         return b.obj();
1795     }
json() const1796     virtual string json() const {
1797         return "{ \"a\" : Timestamp( 20, 5 ) }";
1798     }
1799 };
1800 
1801 class TimestampMax : public TestRoundTrip {
bson() const1802     virtual BSONObj bson() const {
1803         BSONObjBuilder b;
1804         b.appendMaxForType("a", mongo::bsonTimestamp);
1805         BSONObj o = b.obj();
1806         return o;
1807     }
json() const1808     virtual string json() const {
1809         Timestamp opTime = Timestamp::max();
1810         stringstream ss;
1811         ss << "{ \"a\" : Timestamp( " << opTime.getSecs() << ", " << opTime.getInc() << " ) }";
1812         return ss.str();
1813     }
1814 };
1815 
1816 class Regex : public TestRoundTrip {
bson() const1817     virtual BSONObj bson() const {
1818         BSONObjBuilder b;
1819         b.appendRegex("a", "b", "");
1820         return b.obj();
1821     }
json() const1822     virtual string json() const {
1823         return "{ \"a\" : /b/ }";
1824     }
1825 };
1826 
1827 class RegexWithQuotes : public TestRoundTrip {
bson() const1828     virtual BSONObj bson() const {
1829         BSONObjBuilder b;
1830         b.appendRegex("a", "\"", "");
1831         return b.obj();
1832     }
json() const1833     virtual string json() const {
1834         return "{ \"a\" : /\"/ }";
1835     }
1836 };
1837 
1838 class UnquotedFieldName : public TestRoundTrip {
bson() const1839     virtual BSONObj bson() const {
1840         BSONObjBuilder b;
1841         b.append("a_b", 1);
1842         return b.obj();
1843     }
json() const1844     virtual string json() const {
1845         return "{ a_b : 1 }";
1846     }
1847 };
1848 
1849 class SingleQuotes : public TestRoundTrip {
bson() const1850     virtual BSONObj bson() const {
1851         BSONObjBuilder b;
1852         b.append("ab'c\"", "bb\b '\"");
1853         return b.obj();
1854     }
json() const1855     virtual string json() const {
1856         return "{ 'ab\\'c\"' : 'bb\\b \\'\"' }";
1857     }
1858 };
1859 
1860 class ObjectId : public TestRoundTrip {
bson() const1861     virtual BSONObj bson() const {
1862         OID id;
1863         id.init("deadbeeff00ddeadbeeff00d");
1864         BSONObjBuilder b;
1865         b.appendOID("foo", &id);
1866         return b.obj();
1867     }
json() const1868     virtual string json() const {
1869         return "{ \"foo\": ObjectId( \"deadbeeff00ddeadbeeff00d\" ) }";
1870     }
1871 };
1872 
1873 class NumberLong : public TestRoundTrip {
1874 public:
bson() const1875     virtual BSONObj bson() const {
1876         return BSON("long" << 4611686018427387904ll);  // 2**62
1877     }
json() const1878     virtual string json() const {
1879         return "{ \"long\": NumberLong(4611686018427387904) }";
1880     }
1881 };
1882 
1883 class NumberInt : public TestRoundTrip {
1884 public:
bson() const1885     virtual BSONObj bson() const {
1886         return BSON("int" << static_cast<int>(100));
1887     }
json() const1888     virtual string json() const {
1889         return "{ \"int\": NumberInt(100) }";
1890     }
1891 };
1892 
1893 class Number : public TestRoundTrip {
1894 public:
bson() const1895     virtual BSONObj bson() const {
1896         return BSON("double" << 3.14);
1897     }
json() const1898     virtual string json() const {
1899         return "{ \"double\": Number(3.14) }";
1900     }
1901 };
1902 
1903 class NumberDecimal : public TestRoundTrip {
1904 public:
bson() const1905     virtual BSONObj bson() const {
1906         return BSON("decimal" << Decimal128("2.010"));
1907     }
json() const1908     virtual string json() const {
1909         return "{ \"decimal\": NumberDecimal(\"+2.010\") }";
1910     }
1911 };
1912 
1913 class NumberDecimalNegative : public TestRoundTrip {
1914 public:
bson() const1915     virtual BSONObj bson() const {
1916         return BSON("decimal" << Decimal128("-4.018"));
1917     }
json() const1918     virtual string json() const {
1919         return "{ \"decimal\": NumberDecimal(\"-4018E-3\") }";
1920     }
1921 };
1922 
1923 class NumberDecimalMax : public TestRoundTrip {
1924 public:
bson() const1925     virtual BSONObj bson() const {
1926         return BSON("decimal" << Decimal128("+9.999999999999999999999999999999999E6144"));
1927     }
json() const1928     virtual string json() const {
1929         return "{ \"decimal\": NumberDecimal(\"+9999999999999999999999999999999999E6111\") }";
1930     }
1931 };
1932 
1933 class NumberDecimalMin : public TestRoundTrip {
1934 public:
bson() const1935     virtual BSONObj bson() const {
1936         return BSON("decimal" << Decimal128("0.000000000000000000000000000000001E-6143"));
1937     }
json() const1938     virtual string json() const {
1939         return "{ \"decimal\": NumberDecimal(\"+1E-6176\") }";
1940     }
1941 };
1942 
1943 class NumberDecimalPositiveZero : public TestRoundTrip {
1944 public:
bson() const1945     virtual BSONObj bson() const {
1946         return BSON("decimal" << Decimal128("0"));
1947     }
json() const1948     virtual string json() const {
1949         return "{ \"decimal\": NumberDecimal(\"0\") }";
1950     }
1951 };
1952 
1953 class NumberDecimalNegativeZero : public TestRoundTrip {
1954 public:
bson() const1955     virtual BSONObj bson() const {
1956         return BSON("decimal" << Decimal128("-0"));
1957     }
json() const1958     virtual string json() const {
1959         return "{ \"decimal\": NumberDecimal(\"-0\") }";
1960     }
1961 };
1962 
1963 class NumberDecimalPositiveNaN : public TestRoundTrip {
1964 public:
bson() const1965     virtual BSONObj bson() const {
1966         return BSON("decimal" << Decimal128("NaN"));
1967     }
json() const1968     virtual string json() const {
1969         return "{ \"decimal\": NumberDecimal(\"NaN\") }";
1970     }
1971 };
1972 
1973 class NumberDecimalNegativeNaN : public TestRoundTrip {
1974 public:
bson() const1975     virtual BSONObj bson() const {
1976         return BSON("decimal" << Decimal128("-NaN"));
1977     }
json() const1978     virtual string json() const {
1979         return "{ \"decimal\": NumberDecimal(\"-NaN\") }";
1980     }
1981 };
1982 
1983 class NumberDecimalPositiveInfinity : public TestRoundTrip {
1984 public:
bson() const1985     virtual BSONObj bson() const {
1986         return BSON("decimal" << Decimal128("1E999999"));
1987     }
json() const1988     virtual string json() const {
1989         return "{ \"decimal\": NumberDecimal(\"+Inf\") }";
1990     }
1991 };
1992 
1993 class NumberDecimalNegativeInfinity : public TestRoundTrip {
1994 public:
bson() const1995     virtual BSONObj bson() const {
1996         return BSON("decimal" << Decimal128("-1E999999"));
1997     }
json() const1998     virtual string json() const {
1999         return "{ \"decimal\": NumberDecimal(\"-Inf\") }";
2000     }
2001 };
2002 
2003 class NumberDecimalPrecision : public TestRoundTrip {
2004 public:
bson() const2005     virtual BSONObj bson() const {
2006         return BSON("decimal" << Decimal128("5.00"));
2007     }
json() const2008     virtual string json() const {
2009         return "{ \"decimal\": NumberDecimal(\"+500E-2\") }";
2010     }
2011 };
2012 
2013 class UUID : public TestRoundTrip {
bson() const2014     virtual BSONObj bson() const {
2015         BSONObjBuilder b;
2016         unsigned char z[] = {0xAB,
2017                              0xCD,
2018                              0xEF,
2019                              0xAB,
2020                              0xCD,
2021                              0xEF,
2022                              0xAB,
2023                              0xCD,
2024                              0xEF,
2025                              0xAB,
2026                              0xCD,
2027                              0xEF,
2028                              0x00,
2029                              0x00,
2030                              0x00,
2031                              0x00};
2032         b.appendBinData("a", 16, bdtUUID, z);
2033         return b.obj();
2034     }
2035 
2036     // Don't need to return anything because we are overriding both jsonOut and jsonIn
json() const2037     virtual string json() const {
2038         return "";
2039     }
2040 
2041     // The UUID constructor corresponds to a special BinData type
jsonIn() const2042     virtual string jsonIn() const {
2043         return "{ \"a\" : UUID(\"abcdefabcdefabcdefabcdef00000000\") }";
2044     }
jsonOut() const2045     virtual string jsonOut() const {
2046         return "{ \"a\" : BinData(3,\"q83vq83vq83vq83vAAAAAA==\") }";
2047     }
2048 };
2049 
2050 class HexData : public TestRoundTrip {
bson() const2051     virtual BSONObj bson() const {
2052         BSONObjBuilder b;
2053         unsigned char z[] = {0xAB,
2054                              0xCD,
2055                              0xEF,
2056                              0xAB,
2057                              0xCD,
2058                              0xEF,
2059                              0xAB,
2060                              0xCD,
2061                              0xEF,
2062                              0xAB,
2063                              0xCD,
2064                              0xEF,
2065                              0x00,
2066                              0x00,
2067                              0x00,
2068                              0x00};
2069         b.appendBinData("a", 16, BinDataGeneral, z);
2070         return b.obj();
2071     }
2072 
2073     // Don't need to return anything because we are overriding both jsonOut and jsonIn
json() const2074     virtual string json() const {
2075         return "";
2076     }
2077 
2078     // The HexData constructor creates a BinData type from a hex string
jsonIn() const2079     virtual string jsonIn() const {
2080         return "{ \"a\" : HexData(0,\"abcdefabcdefabcdefabcdef00000000\") }";
2081     }
jsonOut() const2082     virtual string jsonOut() const {
2083         return "{ \"a\" : BinData(0,\"q83vq83vq83vq83vAAAAAA==\") }";
2084     }
2085 };
2086 
2087 class MD5 : public TestRoundTrip {
bson() const2088     virtual BSONObj bson() const {
2089         BSONObjBuilder b;
2090         unsigned char z[] = {0xAB,
2091                              0xCD,
2092                              0xEF,
2093                              0xAB,
2094                              0xCD,
2095                              0xEF,
2096                              0xAB,
2097                              0xCD,
2098                              0xEF,
2099                              0xAB,
2100                              0xCD,
2101                              0xEF,
2102                              0x00,
2103                              0x00,
2104                              0x00,
2105                              0x00};
2106         b.appendBinData("a", 16, MD5Type, z);
2107         return b.obj();
2108     }
2109 
2110     // Don't need to return anything because we are overriding both jsonOut and jsonIn
json() const2111     virtual string json() const {
2112         return "";
2113     }
2114 
2115     // The HexData constructor creates a BinData type from a hex string
jsonIn() const2116     virtual string jsonIn() const {
2117         return "{ \"a\" : MD5(\"abcdefabcdefabcdefabcdef00000000\") }";
2118     }
jsonOut() const2119     virtual string jsonOut() const {
2120         return "{ \"a\" : BinData(5,\"q83vq83vq83vq83vAAAAAA==\") }";
2121     }
2122 };
2123 
2124 class NullString : public TestRoundTrip {
bson() const2125     virtual BSONObj bson() const {
2126         BSONObjBuilder b;
2127         b.append("x", "a\0b", 4);
2128         return b.obj();
2129     }
json() const2130     virtual string json() const {
2131         return "{ \"x\" : \"a\\u0000b\" }";
2132     }
2133 };
2134 
2135 }  // namespace RoundTripTests
2136 
2137 class BinDataType {
2138 public:
pp(const char * s,BSONElement e)2139     void pp(const char* s, BSONElement e) {
2140         int len;
2141         const char* data = e.binData(len);
2142         cout << s << ":" << e.binDataType() << "\t" << len << endl;
2143         cout << "\t";
2144         for (int i = 0; i < len; i++)
2145             cout << (int)(data[i]) << " ";
2146         cout << endl;
2147     }
2148 
run()2149     void run() {
2150         unique_ptr<Scope> s(getGlobalScriptEngine()->newScope());
2151 
2152         const char* foo = "asdas\0asdasd";
2153         const char* base64 = "YXNkYXMAYXNkYXNk";
2154 
2155         BSONObj in;
2156         {
2157             BSONObjBuilder b;
2158             b.append("a", 7);
2159             b.appendBinData("b", 12, BinDataGeneral, foo);
2160             in = b.obj();
2161             s->setObject("x", in);
2162         }
2163 
2164         s->invokeSafe("myb = x.b; print( myb ); printjson( myb );", 0, 0);
2165         s->invokeSafe("y = { c : myb };", 0, 0);
2166 
2167         BSONObj out = s->getObject("y");
2168         ASSERT_EQUALS(BinData, out["c"].type());
2169         //            pp( "in " , in["b"] );
2170         //            pp( "out" , out["c"] );
2171         ASSERT_EQUALS(0, in["b"].woCompare(out["c"], false));
2172 
2173         // check that BinData js class is utilized
2174         s->invokeSafe("q = x.b.toString();", 0, 0);
2175         stringstream expected;
2176         expected << "BinData(" << BinDataGeneral << ",\"" << base64 << "\")";
2177         ASSERT_EQUALS(expected.str(), s->getString("q"));
2178 
2179         stringstream scriptBuilder;
2180         scriptBuilder << "z = { c : new BinData( " << BinDataGeneral << ", \"" << base64
2181                       << "\" ) };";
2182         string script = scriptBuilder.str();
2183         s->invokeSafe(script.c_str(), 0, 0);
2184         out = s->getObject("z");
2185         //            pp( "out" , out["c"] );
2186         ASSERT_EQUALS(0, in["b"].woCompare(out["c"], false));
2187 
2188         s->invokeSafe("a = { f: new BinData( 128, \"\" ) };", 0, 0);
2189         out = s->getObject("a");
2190         int len = -1;
2191         out["f"].binData(len);
2192         ASSERT_EQUALS(0, len);
2193         ASSERT_EQUALS(128, out["f"].binDataType());
2194     }
2195 };
2196 
2197 class VarTests {
2198 public:
run()2199     void run() {
2200         unique_ptr<Scope> s(getGlobalScriptEngine()->newScope());
2201 
2202         ASSERT(s->exec("a = 5;", "a", false, true, false));
2203         ASSERT_EQUALS(5, s->getNumber("a"));
2204 
2205         ASSERT(s->exec("var b = 6;", "b", false, true, false));
2206         ASSERT_EQUALS(6, s->getNumber("b"));
2207     }
2208 };
2209 
2210 class Speed1 {
2211 public:
run()2212     void run() {
2213         BSONObj start = BSON("x" << 5.0);
2214         BSONObj empty;
2215 
2216         unique_ptr<Scope> s;
2217         s.reset(getGlobalScriptEngine()->newScope());
2218 
2219         ScriptingFunction f = s->createFunction("return this.x + 6;");
2220 
2221         Timer t;
2222         double n = 0;
2223         for (; n < 10000; n++) {
2224             s->invoke(f, &empty, &start);
2225             ASSERT_EQUALS(11, s->getNumber("__returnValue"));
2226         }
2227         // cout << "speed1: " << ( n / t.millis() ) << " ops/ms" << endl;
2228     }
2229 };
2230 
2231 class ScopeOut {
2232 public:
run()2233     void run() {
2234         unique_ptr<Scope> s;
2235         s.reset(getGlobalScriptEngine()->newScope());
2236 
2237         s->invokeSafe("x = 5;", 0, 0);
2238         {
2239             BSONObjBuilder b;
2240             s->append(b, "z", "x");
2241             ASSERT_BSONOBJ_EQ(BSON("z" << 5), b.obj());
2242         }
2243 
2244         s->invokeSafe("x = function(){ return 17; }", 0, 0);
2245         BSONObj temp;
2246         {
2247             BSONObjBuilder b;
2248             s->append(b, "z", "x");
2249             temp = b.obj();
2250         }
2251 
2252         s->invokeSafe("foo = this.z();", 0, &temp);
2253         ASSERT_EQUALS(17, s->getNumber("foo"));
2254     }
2255 };
2256 
2257 class RenameTest {
2258 public:
run()2259     void run() {
2260         unique_ptr<Scope> s;
2261         s.reset(getGlobalScriptEngine()->newScope());
2262 
2263         s->setNumber("x", 5);
2264         ASSERT_EQUALS(5, s->getNumber("x"));
2265         ASSERT_EQUALS(Undefined, s->type("y"));
2266 
2267         s->rename("x", "y");
2268         ASSERT_EQUALS(5, s->getNumber("y"));
2269         ASSERT_EQUALS(Undefined, s->type("x"));
2270 
2271         s->rename("y", "x");
2272         ASSERT_EQUALS(5, s->getNumber("x"));
2273         ASSERT_EQUALS(Undefined, s->type("y"));
2274     }
2275 };
2276 
2277 
2278 class InvalidStoredJS {
2279 public:
run()2280     void run() {
2281         BSONObjBuilder query;
2282         query.append("_id", "invalidstoredjs1");
2283 
2284         BSONObjBuilder update;
2285         update.append("_id", "invalidstoredjs1");
2286         update.appendCode("value",
2287                           "function () { db.test.find().forEach(function(obj) { continue; }); }");
2288 
2289         const ServiceContext::UniqueOperationContext opCtxPtr = cc().makeOperationContext();
2290         OperationContext& opCtx = *opCtxPtr;
2291         DBDirectClient client(&opCtx);
2292         client.update("test.system.js", query.obj(), update.obj(), true /* upsert */);
2293 
2294         unique_ptr<Scope> s(getGlobalScriptEngine()->newScope());
2295         client.eval("test", "invalidstoredjs1()");
2296 
2297         BSONObj info;
2298         BSONElement ret;
2299         ASSERT(client.eval("test", "return 5 + 12", info, ret));
2300         ASSERT_EQUALS(17, ret.number());
2301     }
2302 };
2303 
2304 /**
2305  * This tests a bug discovered in SERVER-24054, where certain interesting nan patterns crash
2306  * spidermonkey by looking like non-double type puns.  This verifies that we put that particular
2307  * interesting nan in and that we still get a nan out.
2308  */
2309 class NovelNaN {
2310 public:
run()2311     void run() {
2312         uint8_t bits[] = {
2313             16, 0, 0, 0, 0x01, 'a', '\0', 0x61, 0x79, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0,
2314         };
2315         unique_ptr<Scope> s(getGlobalScriptEngine()->newScope());
2316 
2317         s->setObject("val", BSONObj(reinterpret_cast<char*>(bits)).getOwned());
2318 
2319         s->invoke("val[\"a\"];", 0, 0);
2320         ASSERT_TRUE(std::isnan(s->getNumber("__returnValue")));
2321     }
2322 };
2323 
2324 class NoReturnSpecified {
2325 public:
run()2326     void run() {
2327         unique_ptr<Scope> s(getGlobalScriptEngine()->newScope());
2328 
2329         s->invoke("x=5;", 0, 0);
2330         ASSERT_EQUALS(5, s->getNumber("__returnValue"));
2331 
2332         s->invoke("x='test'", 0, 0);
2333         ASSERT_EQUALS("test", s->getString("__returnValue"));
2334 
2335         s->invoke("x='return'", 0, 0);
2336         ASSERT_EQUALS("return", s->getString("__returnValue"));
2337 
2338         s->invoke("return 'return'", 0, 0);
2339         ASSERT_EQUALS("return", s->getString("__returnValue"));
2340 
2341         s->invoke("x = ' return '", 0, 0);
2342         ASSERT_EQUALS(" return ", s->getString("__returnValue"));
2343 
2344         s->invoke("x = \" return \"", 0, 0);
2345         ASSERT_EQUALS(" return ", s->getString("__returnValue"));
2346 
2347         s->invoke("x = \"' return '\"", 0, 0);
2348         ASSERT_EQUALS("' return '", s->getString("__returnValue"));
2349 
2350         s->invoke("x = '\" return \"'", 0, 0);
2351         ASSERT_EQUALS("\" return \"", s->getString("__returnValue"));
2352 
2353         s->invoke(";return 5", 0, 0);
2354         ASSERT_EQUALS(5, s->getNumber("__returnValue"));
2355 
2356         s->invoke("String('return')", 0, 0);
2357         ASSERT_EQUALS("return", s->getString("__returnValue"));
2358 
2359         s->invoke("String(' return ')", 0, 0);
2360         ASSERT_EQUALS(" return ", s->getString("__returnValue"));
2361 
2362         s->invoke("String(\"'return\")", 0, 0);
2363         ASSERT_EQUALS("'return", s->getString("__returnValue"));
2364 
2365         s->invoke("String('\"return')", 0, 0);
2366         ASSERT_EQUALS("\"return", s->getString("__returnValue"));
2367     }
2368 };
2369 
2370 class RecursiveInvoke {
2371 public:
callback(const BSONObj & args,void * data)2372     static BSONObj callback(const BSONObj& args, void* data) {
2373         auto scope = static_cast<Scope*>(data);
2374 
2375         scope->invoke("x = 10;", 0, 0);
2376 
2377         return BSONObj();
2378     }
2379 
run()2380     void run() {
2381         unique_ptr<Scope> s(getGlobalScriptEngine()->newScope());
2382 
2383         s->injectNative("foo", callback, s.get());
2384         s->invoke("var x = 1; foo();", 0, 0);
2385         ASSERT_EQUALS(s->getNumberInt("x"), 10);
2386     }
2387 };
2388 
2389 class ErrorCodeFromInvoke {
2390 public:
run()2391     void run() {
2392         unique_ptr<Scope> s(getGlobalScriptEngine()->newScope());
2393 
2394         {
2395             bool threwException = false;
2396             try {
2397                 s->invoke("\"use strict\"; x = 10;", 0, 0);
2398             } catch (...) {
2399                 threwException = true;
2400 
2401                 auto status = exceptionToStatus();
2402 
2403                 ASSERT_EQUALS(status.code(), ErrorCodes::JSInterpreterFailure);
2404             }
2405 
2406             ASSERT(threwException);
2407         }
2408 
2409         {
2410             bool threwException = false;
2411             try {
2412                 s->invoke("UUID(1,2,3,4,5);", 0, 0);
2413             } catch (...) {
2414                 threwException = true;
2415 
2416                 auto status = exceptionToStatus();
2417 
2418                 ASSERT_EQUALS(status.code(), ErrorCodes::BadValue);
2419             }
2420 
2421             ASSERT(threwException);
2422         }
2423     }
2424 };
2425 
2426 class RequiresOwnedObjects {
2427 public:
run()2428     void run() {
2429         char buf[] = {5, 0, 0, 0, 0};
2430         BSONObj unowned(buf);
2431         BSONObj owned = unowned.getOwned();
2432 
2433         ASSERT(!unowned.isOwned());
2434         ASSERT(owned.isOwned());
2435 
2436         // Ensure that by default we can bind owned and unowned
2437         {
2438             unique_ptr<Scope> s(getGlobalScriptEngine()->newScope());
2439             s->setObject("unowned", unowned, true);
2440             s->setObject("owned", owned, true);
2441         }
2442 
2443         // After we set the flag, we should only be able to set owned
2444         {
2445             unique_ptr<Scope> s(getGlobalScriptEngine()->newScope());
2446             s->requireOwnedObjects();
2447             s->setObject("owned", owned, true);
2448 
2449             bool threwException = false;
2450             try {
2451                 s->setObject("unowned", unowned, true);
2452             } catch (...) {
2453                 threwException = true;
2454 
2455                 auto status = exceptionToStatus();
2456 
2457                 ASSERT_EQUALS(status.code(), ErrorCodes::BadValue);
2458             }
2459 
2460             ASSERT(threwException);
2461 
2462             // after resetting, we can set unowned's again
2463             s->reset();
2464             s->setObject("unowned", unowned, true);
2465         }
2466     }
2467 };
2468 
2469 class All : public Suite {
2470 public:
All()2471     All() : Suite("js") {}
2472 
setupTests()2473     void setupTests() {
2474         add<BuiltinTests>();
2475         add<BasicScope>();
2476         add<ResetScope>();
2477         add<FalseTests>();
2478         add<SimpleFunctions>();
2479         add<ExecLogError>();
2480         add<InvokeLogError>();
2481         add<ExecTimeout>();
2482         add<ExecNoTimeout>();
2483         add<InvokeTimeout>();
2484         add<SleepInterruption>();
2485         add<InvokeNoTimeout>();
2486 
2487         add<ObjectMapping>();
2488         add<ObjectDecoding>();
2489         add<JSOIDTests>();
2490         add<SetImplicit>();
2491         add<ObjectModReadonlyTests>();
2492         add<OtherJSTypes>();
2493         add<SpecialDBTypes>();
2494         add<TypeConservation>();
2495         add<NumberLong>();
2496         add<NumberLong2>();
2497 
2498         add<NumberDecimal>();
2499         add<NumberDecimalGetFromScope>();
2500         add<NumberDecimalBigObject>();
2501 
2502         add<MaxTimestamp>();
2503         add<RenameTest>();
2504 
2505         add<WeirdObjects>();
2506         add<CodeTests>();
2507         add<BinDataType>();
2508 
2509         add<VarTests>();
2510 
2511         add<Speed1>();
2512 
2513         add<InvalidUTF8Check>();
2514         add<Utf8Check>();
2515         add<LongUtf8String>();
2516 
2517         add<ScopeOut>();
2518         add<InvalidStoredJS>();
2519 
2520         add<NovelNaN>();
2521 
2522         add<NoReturnSpecified>();
2523 
2524         add<RecursiveInvoke>();
2525         add<ErrorCodeFromInvoke>();
2526         add<RequiresOwnedObjects>();
2527 
2528         add<RoundTripTests::DBRefTest>();
2529         add<RoundTripTests::DBPointerTest>();
2530         add<RoundTripTests::InformalDBRefTest>();
2531         add<RoundTripTests::InformalDBRefOIDTest>();
2532         add<RoundTripTests::InformalDBRefExtraFieldTest>();
2533         add<RoundTripTests::Empty>();
2534         add<RoundTripTests::EmptyWithSpace>();
2535         add<RoundTripTests::SingleString>();
2536         add<RoundTripTests::EmptyStrings>();
2537         add<RoundTripTests::SingleNumber>();
2538         add<RoundTripTests::RealNumber>();
2539         add<RoundTripTests::FancyNumber>();
2540         add<RoundTripTests::TwoElements>();
2541         add<RoundTripTests::Subobject>();
2542         add<RoundTripTests::DeeplyNestedObject>();
2543         add<RoundTripTests::ArrayEmpty>();
2544         add<RoundTripTests::Array>();
2545         add<RoundTripTests::True>();
2546         add<RoundTripTests::False>();
2547         add<RoundTripTests::Null>();
2548         add<RoundTripTests::Undefined>();
2549         add<RoundTripTests::EscapedCharacters>();
2550         add<RoundTripTests::NonEscapedCharacters>();
2551         add<RoundTripTests::AllowedControlCharacter>();
2552         add<RoundTripTests::NumbersInFieldName>();
2553         add<RoundTripTests::EscapeFieldName>();
2554         add<RoundTripTests::EscapedUnicodeToUtf8>();
2555         add<RoundTripTests::Utf8AllOnes>();
2556         add<RoundTripTests::Utf8FirstByteOnes>();
2557         add<RoundTripTests::BinData>();
2558         add<RoundTripTests::BinDataPaddedSingle>();
2559         add<RoundTripTests::BinDataPaddedDouble>();
2560         add<RoundTripTests::BinDataAllChars>();
2561         add<RoundTripTests::Date>();
2562         add<RoundTripTests::DateNonzero>();
2563         add<RoundTripTests::DateNegative>();
2564         add<RoundTripTests::JSTimestamp>();
2565         add<RoundTripTests::TimestampMax>();
2566         add<RoundTripTests::Regex>();
2567         add<RoundTripTests::RegexWithQuotes>();
2568         add<RoundTripTests::UnquotedFieldName>();
2569         add<RoundTripTests::SingleQuotes>();
2570         add<RoundTripTests::ObjectId>();
2571         add<RoundTripTests::NumberLong>();
2572         add<RoundTripTests::NumberInt>();
2573         add<RoundTripTests::Number>();
2574 
2575         add<RoundTripTests::NumberDecimal>();
2576         add<RoundTripTests::NumberDecimalNegative>();
2577         add<RoundTripTests::NumberDecimalMax>();
2578         add<RoundTripTests::NumberDecimalMin>();
2579         add<RoundTripTests::NumberDecimalPositiveZero>();
2580         add<RoundTripTests::NumberDecimalNegativeZero>();
2581         add<RoundTripTests::NumberDecimalPositiveNaN>();
2582         add<RoundTripTests::NumberDecimalNegativeNaN>();
2583         add<RoundTripTests::NumberDecimalPositiveInfinity>();
2584         add<RoundTripTests::NumberDecimalNegativeInfinity>();
2585         add<RoundTripTests::NumberDecimalPrecision>();
2586 
2587         add<RoundTripTests::UUID>();
2588         add<RoundTripTests::HexData>();
2589         add<RoundTripTests::MD5>();
2590         add<RoundTripTests::NullString>();
2591     }
2592 };
2593 
2594 SuiteInstance<All> myall;
2595 
2596 }  // namespace JSTests
2597