From bf36eebd4f32728d4ff0de6cc4a3a3605ab0af1d Mon Sep 17 00:00:00 2001 From: Aaron Date: Tue, 22 Jun 2010 17:53:11 -0700 Subject: [PATCH] SERVER-776 NumberLong in sm --- dbtests/jstests.cpp | 2 +- jstests/numberlong.js | 46 +++++++++++++++++++++++++++++++ mongo.xcodeproj/project.pbxproj | 2 ++ scripting/engine_spidermonkey.cpp | 34 ++++++++++++++--------- scripting/sm_db.cpp | 34 ++++++++++++++++++++--- shell/utils.js | 34 ++++++++++++++--------- 6 files changed, 121 insertions(+), 31 deletions(-) create mode 100644 jstests/numberlong.js diff --git a/dbtests/jstests.cpp b/dbtests/jstests.cpp index 103db49fbeb..235e70f6d04 100644 --- a/dbtests/jstests.cpp +++ b/dbtests/jstests.cpp @@ -541,7 +541,7 @@ namespace JSTests { ASSERT( s->exec( "c = {c:a.a.toString()}", "foo", false, true, false ) ); out = s->getObject( "c" ); stringstream ss; - ss << val; + ss << "NumberLong( \"" << val << "\" )"; ASSERT_EQUALS( ss.str(), out.firstElement().valuestr() ); ASSERT( s->exec( "d = {d:a.a.toNumber()}", "foo", false, true, false ) ); diff --git a/jstests/numberlong.js b/jstests/numberlong.js new file mode 100644 index 00000000000..706fe7eef49 --- /dev/null +++ b/jstests/numberlong.js @@ -0,0 +1,46 @@ +n = new NumberLong( 4 ); +assert.eq.automsg( "4", "n" ); +assert.eq.automsg( "4", "n.toNumber()" ); +assert.eq.automsg( "8", "n + 4" ); +assert.eq.automsg( "'NumberLong( 4 )'", "n.toString()" ); +assert.eq.automsg( "'NumberLong( 4 )'", "tojson( n )" ); +a = {} +a.a = n; +p = tojson( a ); +assert.eq.automsg( "'{ \"a\" : NumberLong( 4 ) }'", "p" ); + +n = new NumberLong( -4 ); +assert.eq.automsg( "-4", "n" ); +assert.eq.automsg( "-4", "n.toNumber()" ); +assert.eq.automsg( "0", "n + 4" ); +assert.eq.automsg( "'NumberLong( -4 )'", "n.toString()" ); +assert.eq.automsg( "'NumberLong( -4 )'", "tojson( n )" ); +a = {} +a.a = n; +p = tojson( a ); +assert.eq.automsg( "'{ \"a\" : NumberLong( -4 ) }'", "p" ); + +// too big to fit in double +n = new NumberLong( "11111111111111111" ); +assert.eq.automsg( "11111111111111112", "n.toNumber()" ); +assert.eq.automsg( "11111111111111116", "n + 4" ); +assert.eq.automsg( "'NumberLong( \"11111111111111111\" )'", "n.toString()" ); +assert.eq.automsg( "'NumberLong( \"11111111111111111\" )'", "tojson( n )" ); +a = {} +a.a = n; +p = tojson( a ); +assert.eq.automsg( "'{ \"a\" : NumberLong( \"11111111111111111\" ) }'", "p" ); + +n = new NumberLong( "-11111111111111111" ); +assert.eq.automsg( "-11111111111111112", "n.toNumber()" ); +assert.eq.automsg( "-11111111111111108", "n + 4" ); +assert.eq.automsg( "'NumberLong( \"-11111111111111111\" )'", "n.toString()" ); +assert.eq.automsg( "'NumberLong( \"-11111111111111111\" )'", "tojson( n )" ); +a = {} +a.a = n; +p = tojson( a ); +assert.eq.automsg( "'{ \"a\" : NumberLong( \"-11111111111111111\" ) }'", "p" ); + +// parsing +assert.throws.automsg( function() { new NumberLong( "y" ); } ); +assert.throws.automsg( function() { new NumberLong( "11111111111111111111" ); } ); diff --git a/mongo.xcodeproj/project.pbxproj b/mongo.xcodeproj/project.pbxproj index 8da938bd3f9..5aa2d7c75da 100644 --- a/mongo.xcodeproj/project.pbxproj +++ b/mongo.xcodeproj/project.pbxproj @@ -452,6 +452,7 @@ 937D14AB0F2A225F0071FFA9 /* nonce.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nonce.h; sourceTree = ""; }; 937D14AC0F2A226E0071FFA9 /* nonce.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = nonce.cpp; sourceTree = ""; }; 938A748A11D140EC005265E1 /* in4.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = in4.js; sourceTree = ""; }; + 938A74BF11D17ECE005265E1 /* numberlong.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = numberlong.js; sourceTree = ""; }; 938A7A420F54871000FB7A07 /* storage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = storage.cpp; sourceTree = ""; }; 938A7A430F54873600FB7A07 /* concurrency.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = concurrency.h; sourceTree = ""; }; 938A7A440F54873600FB7A07 /* queryutil.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = queryutil.cpp; sourceTree = ""; }; @@ -822,6 +823,7 @@ 934BEB9A10DFFA9600178102 /* jstests */ = { isa = PBXGroup; children = ( + 938A74BF11D17ECE005265E1 /* numberlong.js */, 938A748A11D140EC005265E1 /* in4.js */, 93C529C511D047CF00CF42F7 /* repair2.js */, 937884E811C80B22007E85F5 /* or8.js */, diff --git a/scripting/engine_spidermonkey.cpp b/scripting/engine_spidermonkey.cpp index 14153d11f1d..51651ab3133 100644 --- a/scripting/engine_spidermonkey.cpp +++ b/scripting/engine_spidermonkey.cpp @@ -532,6 +532,24 @@ namespace mongo { return OBJECT_TO_JSVAL( o ); } + void makeLongObj( long long n, JSObject * o ) { + boost::uint64_t val = (boost::uint64_t)n; + CHECKNEWOBJECT(o,_context,"NumberLong1"); + setProperty( o , "floatApprox" , toval( (double)(boost::int64_t)( val ) ) ); + if ( (boost::int64_t)val != (boost::int64_t)(double)(boost::int64_t)( val ) ) { + // using 2 doubles here instead of a single double because certain double + // bit patterns represent undefined values and sm might trash them + setProperty( o , "top" , toval( (double)(boost::uint32_t)( val >> 32 ) ) ); + setProperty( o , "bottom" , toval( (double)(boost::uint32_t)( val & 0x00000000ffffffff ) ) ); + } + } + + jsval toval( long long n ) { + JSObject * o = JS_NewObject( _context , &numberlong_class , 0 , 0 ); + makeLongObj( n, o ); + return OBJECT_TO_JSVAL( o ); + } + jsval toval( const BSONElement& e ){ switch( e.type() ){ @@ -633,17 +651,7 @@ namespace mongo { return OBJECT_TO_JSVAL( o ); } case NumberLong: { - boost::uint64_t val = (boost::uint64_t)e.numberLong(); - JSObject * o = JS_NewObject( _context , &numberlong_class , 0 , 0 ); - CHECKNEWOBJECT(o,_context,"NumberLong1"); - setProperty( o , "floatApprox" , toval( (double)(boost::int64_t)( val ) ) ); - if ( (boost::int64_t)val != (boost::int64_t)(double)(boost::int64_t)( val ) ) { - // using 2 doubles here instead of a single double because certain double - // bit patterns represent undefined values and sm might trash them - setProperty( o , "top" , toval( (double)(boost::uint32_t)( val >> 32 ) ) ); - setProperty( o , "bottom" , toval( (double)(boost::uint32_t)( val & 0x00000000ffffffff ) ) ); - } - return OBJECT_TO_JSVAL( o ); + return toval( e.numberLong() ); } case DBRef: { JSObject * o = JS_NewObject( _context , &dbpointer_class , 0 , 0 ); @@ -664,8 +672,8 @@ namespace mongo { const char * data = e.binData( len ); assert( JS_SetPrivate( _context , o , new BinDataHolder( data ) ) ); - setProperty( o , "len" , toval( len ) ); - setProperty( o , "type" , toval( (int)e.binDataType() ) ); + setProperty( o , "len" , toval( (double)len ) ); + setProperty( o , "type" , toval( (double)e.binDataType() ) ); return OBJECT_TO_JSVAL( o ); } } diff --git a/scripting/sm_db.cpp b/scripting/sm_db.cpp index 8d91ff7a2d8..1fcfa62ebd4 100644 --- a/scripting/sm_db.cpp +++ b/scripting/sm_db.cpp @@ -593,8 +593,8 @@ namespace mongo { string decoded = base64::decode( encoded ); assert( JS_SetPrivate( cx, obj, new BinDataHolder( decoded.data(), decoded.length() ) ) ); - c.setProperty( obj, "len", c.toval( decoded.length() ) ); - c.setProperty( obj, "type", c.toval( type ) ); + c.setProperty( obj, "len", c.toval( (double)decoded.length() ) ); + c.setProperty( obj, "type", c.toval( (double)type ) ); return JS_TRUE; } @@ -699,6 +699,27 @@ namespace mongo { JSCLASS_NO_OPTIONAL_MEMBERS }; + JSBool numberlong_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ){ + smuassert( cx , "NumberLong needs 0 or 1 args" , argc == 0 || argc == 1 ); + + Convertor c( cx ); + if ( argc == 0 ) { + c.setProperty( obj, "floatApprox", c.toval( 0.0 ) ); + } else if ( JSVAL_IS_NUMBER( argv[ 0 ] ) ) { + c.setProperty( obj, "floatApprox", argv[ 0 ] ); + } else { + string num = c.toString( argv[ 0 ] ); + const char *numStr = num.c_str(); + char *endPtr = 0; + errno = 0; + long long n = strtoll( numStr, &endPtr, 10 ); + smuassert( cx , "could not convert numeric representation of string to long" , *endPtr == 0 && errno != ERANGE ); + c.makeLongObj( n, obj ); + } + + return JS_TRUE; + } + JSBool numberlong_valueof(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){ Convertor c(cx); return *rval = c.toval( double( c.toNumberLongUnsafe( obj ) ) ); @@ -711,7 +732,12 @@ namespace mongo { JSBool numberlong_tostring(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){ Convertor c(cx); stringstream ss; - ss << c.toNumberLongUnsafe( obj ); + long long val = c.toNumberLongUnsafe( obj ); + if ( val == (long long)(double)( val ) ) { + ss << "NumberLong( " << double( val ) << " )"; + } else { + ss << "NumberLong( \"" << val << "\" )"; + } string ret = ss.str(); return *rval = c.toval( ret.c_str() ); } @@ -821,7 +847,7 @@ namespace mongo { assert( JS_InitClass( cx , global , 0 , &bindata_class , bindata_constructor , 0 , 0 , bindata_functions , 0 , 0 ) ); assert( JS_InitClass( cx , global , 0 , ×tamp_class , 0 , 0 , 0 , 0 , 0 , 0 ) ); - assert( JS_InitClass( cx , global , 0 , &numberlong_class , 0 , 0 , 0 , numberlong_functions , 0 , 0 ) ); + assert( JS_InitClass( cx , global , 0 , &numberlong_class , numberlong_constructor , 0 , 0 , numberlong_functions , 0 , 0 ) ); assert( JS_InitClass( cx , global , 0 , &minkey_class , 0 , 0 , 0 , 0 , 0 , 0 ) ); assert( JS_InitClass( cx , global , 0 , &maxkey_class , 0 , 0 , 0 , 0 , 0 , 0 ) ); diff --git a/shell/utils.js b/shell/utils.js index 42ecdebd617..acd06e8b5ba 100644 --- a/shell/utils.js +++ b/shell/utils.js @@ -15,15 +15,15 @@ friendlyEqual = function( a , b ){ return true; return false; -} - - -doassert = function (msg) { - if (msg.indexOf("assert") == 0) - print(msg); - else - print("assert: " + msg); - throw msg; +} + + +doassert = function (msg) { + if (msg.indexOf("assert") == 0) + print(msg); + else + print("assert: " + msg); + throw msg; } assert = function( b , msg ){ @@ -344,6 +344,14 @@ Object.keySet = function( o ) { return ret; } +if ( ! NumberLong.prototype ) { + NumberLong.prototype = {} +} + +NumberLong.prototype.tojson = function() { + return this.toString(); +} + if ( ! ObjectId.prototype ) ObjectId.prototype = {} @@ -816,10 +824,10 @@ shellHelper = function( command , rest , shouldPrint ){ help = shellHelper.help = function (x) { if (x == "admin") { print("\tls([path]) list files"); - print("\tpwd() returns current directory"); - print("\tlistFiles([path]) returns file list"); - print("\thostname() returns name of this host"); - print("\tcat(fname) returns contents of text file as a string"); + print("\tpwd() returns current directory"); + print("\tlistFiles([path]) returns file list"); + print("\thostname() returns name of this host"); + print("\tcat(fname) returns contents of text file as a string"); print("\tremoveFile(f) delete a file"); print("\tload(jsfilename) load and execute a .js file"); print("\trun(program[, args...]) spawn a program and wait for its completion");