summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Carey <jcarey@argv.me>2015-07-09 14:05:20 -0400
committerJason Carey <jcarey@argv.me>2015-07-14 16:15:54 -0400
commite749ffad9b4fe3110d97f366ebe39e7c9a4edd9d (patch)
tree927e727645da9bfc80095bb124860e31e58e9d77
parent1af5f44f9ba2b7cff8e0457798b7a25b64e9fe69 (diff)
downloadmongo-e749ffad9b4fe3110d97f366ebe39e7c9a4edd9d.tar.gz
SERVER-18531 Integrate SpiderMonkey
Provides SpiderMonkey 38.0.1esr as a JS engine for mongo and mongod.
-rw-r--r--SConstruct14
-rwxr-xr-xbuildscripts/packager-enterprise.py2
-rwxr-xr-xbuildscripts/packager.py2
-rw-r--r--buildscripts/utils.py4
-rw-r--r--debian/mongodb-enterprise-server.docs1
-rw-r--r--debian/mongodb-enterprise-unstable-server.docs1
-rw-r--r--debian/mongodb-org-server.docs1
-rw-r--r--debian/mongodb-org-unstable-server.docs1
-rw-r--r--distsrc/MPL-2373
-rw-r--r--distsrc/THIRD-PARTY-NOTICES201
-rw-r--r--jstests/core/dbadmin.js2
-rw-r--r--jstests/core/hashtest1.js5
-rw-r--r--jstests/core/where5.js5
-rw-r--r--jstests/replsets/capped_id.js11
-rw-r--r--rpm/mongodb-enterprise-unstable.spec1
-rw-r--r--rpm/mongodb-enterprise.spec1
-rw-r--r--rpm/mongodb-org-unstable.spec1
-rw-r--r--rpm/mongodb-org.spec1
-rw-r--r--src/mongo/SConscript6
-rw-r--r--src/mongo/base/error_codes.err1
-rw-r--r--src/mongo/dbtests/jstests.cpp66
-rw-r--r--src/mongo/installer/msi/wxs/LicensingFragment.wxs5
-rw-r--r--src/mongo/scripting/SConscript78
-rw-r--r--src/mongo/scripting/deadline_monitor.h (renamed from src/mongo/scripting/v8_deadline_monitor.h)0
-rw-r--r--src/mongo/scripting/deadline_monitor_test.cpp (renamed from src/mongo/scripting/v8_deadline_monitor_test.cpp)2
-rw-r--r--src/mongo/scripting/engine_v8-3.25.h2
-rw-r--r--src/mongo/scripting/engine_v8.h2
-rw-r--r--src/mongo/scripting/mozjs/PosixNSPR.cpp259
-rw-r--r--src/mongo/scripting/mozjs/base.cpp68
-rw-r--r--src/mongo/scripting/mozjs/base.h92
-rw-r--r--src/mongo/scripting/mozjs/bindata.cpp216
-rw-r--r--src/mongo/scripting/mozjs/bindata.h63
-rw-r--r--src/mongo/scripting/mozjs/bson.cpp235
-rw-r--r--src/mongo/scripting/mozjs/bson.h77
-rw-r--r--src/mongo/scripting/mozjs/countdownlatch.cpp198
-rw-r--r--src/mongo/scripting/mozjs/countdownlatch.h61
-rw-r--r--src/mongo/scripting/mozjs/cursor.cpp122
-rw-r--r--src/mongo/scripting/mozjs/cursor.h76
-rw-r--r--src/mongo/scripting/mozjs/db.cpp139
-rw-r--r--src/mongo/scripting/mozjs/db.h56
-rw-r--r--src/mongo/scripting/mozjs/dbcollection.cpp85
-rw-r--r--src/mongo/scripting/mozjs/dbcollection.h56
-rw-r--r--src/mongo/scripting/mozjs/dbpointer.cpp66
-rw-r--r--src/mongo/scripting/mozjs/dbpointer.h52
-rw-r--r--src/mongo/scripting/mozjs/dbquery.cpp143
-rw-r--r--src/mongo/scripting/mozjs/dbquery.h53
-rw-r--r--src/mongo/scripting/mozjs/dbref.cpp68
-rw-r--r--src/mongo/scripting/mozjs/dbref.h53
-rw-r--r--src/mongo/scripting/mozjs/engine.cpp133
-rw-r--r--src/mongo/scripting/mozjs/engine.h93
-rw-r--r--src/mongo/scripting/mozjs/exception.cpp89
-rw-r--r--src/mongo/scripting/mozjs/exception.h67
-rw-r--r--src/mongo/scripting/mozjs/global.cpp103
-rw-r--r--src/mongo/scripting/mozjs/global.h56
-rw-r--r--src/mongo/scripting/mozjs/idwrapper.cpp74
-rw-r--r--src/mongo/scripting/mozjs/idwrapper.h71
-rw-r--r--src/mongo/scripting/mozjs/implscope.cpp728
-rw-r--r--src/mongo/scripting/mozjs/implscope.h323
-rw-r--r--src/mongo/scripting/mozjs/jscustomallocator.cpp234
-rw-r--r--src/mongo/scripting/mozjs/jsstringwrapper.cpp84
-rw-r--r--src/mongo/scripting/mozjs/jsstringwrapper.h61
-rw-r--r--src/mongo/scripting/mozjs/jsthread.cpp274
-rw-r--r--src/mongo/scripting/mozjs/jsthread.h74
-rw-r--r--src/mongo/scripting/mozjs/maxkey.cpp94
-rw-r--r--src/mongo/scripting/mozjs/maxkey.h59
-rw-r--r--src/mongo/scripting/mozjs/minkey.cpp94
-rw-r--r--src/mongo/scripting/mozjs/minkey.h59
-rw-r--r--src/mongo/scripting/mozjs/mongo.cpp565
-rw-r--r--src/mongo/scripting/mozjs/mongo.h88
-rw-r--r--src/mongo/scripting/mozjs/nativefunction.cpp124
-rw-r--r--src/mongo/scripting/mozjs/nativefunction.h73
-rw-r--r--src/mongo/scripting/mozjs/numberint.cpp112
-rw-r--r--src/mongo/scripting/mozjs/numberint.h61
-rw-r--r--src/mongo/scripting/mozjs/numberlong.cpp164
-rw-r--r--src/mongo/scripting/mozjs/numberlong.h68
-rw-r--r--src/mongo/scripting/mozjs/object.cpp87
-rw-r--r--src/mongo/scripting/mozjs/object.h56
-rw-r--r--src/mongo/scripting/mozjs/objectwrapper.cpp385
-rw-r--r--src/mongo/scripting/mozjs/objectwrapper.h181
-rw-r--r--src/mongo/scripting/mozjs/oid.cpp87
-rw-r--r--src/mongo/scripting/mozjs/oid.h54
-rw-r--r--src/mongo/scripting/mozjs/proxyscope.cpp318
-rw-r--r--src/mongo/scripting/mozjs/proxyscope.h195
-rw-r--r--src/mongo/scripting/mozjs/regexp.cpp39
-rw-r--r--src/mongo/scripting/mozjs/regexp.h49
-rw-r--r--src/mongo/scripting/mozjs/timestamp.cpp77
-rw-r--r--src/mongo/scripting/mozjs/timestamp.h53
-rw-r--r--src/mongo/scripting/mozjs/valuereader.cpp272
-rw-r--r--src/mongo/scripting/mozjs/valuereader.h61
-rw-r--r--src/mongo/scripting/mozjs/valuewriter.cpp252
-rw-r--r--src/mongo/scripting/mozjs/valuewriter.h84
-rw-r--r--src/mongo/scripting/mozjs/wraptype.h474
-rw-r--r--src/mongo/shell/bulk_api.js2
-rw-r--r--src/mongo/shell/types.js4
-rw-r--r--src/mongo/util/concurrency/threadlocal.h2
-rw-r--r--src/third_party/SConscript28
-rw-r--r--src/third_party/mozjs-38/SConscript98
-rw-r--r--src/third_party/mozjs-38/mongo_sources/jscustomallocator.h54
-rw-r--r--src/third_party/mozjs-38/mongo_sources/solaris_hacks.h44
-rw-r--r--src/third_party/mozjs-38/mongo_sources/vm/PosixNSPR.cpp5
-rw-r--r--src/third_party/mozjs-38/mongo_sources/vm/PosixNSPR.h137
-rw-r--r--src/third_party/shim_mozjs.cpp3
102 files changed, 10102 insertions, 51 deletions
diff --git a/SConstruct b/SConstruct
index 33312bc9439..c25ad2ce68b 100644
--- a/SConstruct
+++ b/SConstruct
@@ -214,7 +214,7 @@ add_option('wiredtiger',
)
# library choices
-js_engine_choices = ['v8-3.12', 'v8-3.25', 'none']
+js_engine_choices = ['v8-3.12', 'v8-3.25', 'mozjs', 'none']
add_option('js-engine',
choices=js_engine_choices,
default=js_engine_choices[0],
@@ -756,12 +756,14 @@ jsEngine = get_option( "js-engine")
serverJs = get_option( "server-js" ) == "on"
-usev8 = (jsEngine != 'none')
+usev8 = (jsEngine.startswith('v8'))
+
+usemozjs = (jsEngine.startswith('mozjs'))
v8version = jsEngine[3:] if jsEngine.startswith('v8-') else 'none'
v8suffix = '' if v8version == '3.12' else '-' + v8version
-if not serverJs and not usev8:
+if not serverJs and not usev8 and not usemozjs:
print("Warning: --server-js=off is not needed with --js-engine=none")
# We defer building the env until we have determined whether we want certain values. Some values
@@ -1197,12 +1199,9 @@ elif env.TargetOSIs('windows'):
'DbgHelp.lib',
'shell32.lib',
'Iphlpapi.lib',
+ 'winmm.lib',
'version.lib'])
- # v8 calls timeGetTime()
- if usev8:
- env.Append(LIBS=['winmm.lib'])
-
# When building on visual studio, this sets the name of the debug symbols file
if env.ToolchainIs('msvc'):
env['PDB'] = '${TARGET.base}.pdb'
@@ -2303,6 +2302,7 @@ Export("get_option")
Export("has_option use_system_version_of_library")
Export("serverJs")
Export("usev8")
+Export("usemozjs")
Export("v8version v8suffix")
Export("boostSuffix")
Export('module_sconscripts')
diff --git a/buildscripts/packager-enterprise.py b/buildscripts/packager-enterprise.py
index bbc9c28995e..26e10b553e5 100755
--- a/buildscripts/packager-enterprise.py
+++ b/buildscripts/packager-enterprise.py
@@ -335,7 +335,7 @@ def unpack_binaries_into(build_os, arch, spec, where):
try:
sysassert(["tar", "xvzf", rootdir+"/"+tarfile(build_os, arch, spec)])
release_dir = glob('mongodb-linux-*')[0]
- for releasefile in "bin", "snmp", "LICENSE.txt", "README", "THIRD-PARTY-NOTICES":
+ for releasefile in "bin", "snmp", "LICENSE.txt", "README", "THIRD-PARTY-NOTICES", "MPL-2":
os.rename("%s/%s" % (release_dir, releasefile), releasefile)
os.rmdir(release_dir)
except Exception:
diff --git a/buildscripts/packager.py b/buildscripts/packager.py
index 54b2c53ee22..6896eabb542 100755
--- a/buildscripts/packager.py
+++ b/buildscripts/packager.py
@@ -385,7 +385,7 @@ def unpack_binaries_into(build_os, arch, spec, where):
try:
sysassert(["tar", "xvzf", rootdir+"/"+tarfile(build_os, arch, spec)])
release_dir = glob('mongodb-linux-*')[0]
- for releasefile in "bin", "GNU-AGPL-3.0", "README", "THIRD-PARTY-NOTICES":
+ for releasefile in "bin", "GNU-AGPL-3.0", "README", "THIRD-PARTY-NOTICES", "MPL-2":
print "moving file: %s/%s" % (release_dir, releasefile)
os.rename("%s/%s" % (release_dir, releasefile), releasefile)
os.rmdir(release_dir)
diff --git a/buildscripts/utils.py b/buildscripts/utils.py
index 62a2b1d13eb..a05a4f6f9f9 100644
--- a/buildscripts/utils.py
+++ b/buildscripts/utils.py
@@ -24,10 +24,10 @@ def getAllSourceFiles( arr=None , prefix="." ):
for x in os.listdir( prefix ):
if x.startswith( "." ) or x.startswith( "pcre-" ) or x.startswith( "32bit" ) or x.startswith( "mongodb-" ) or x.startswith("debian") or x.startswith( "mongo-cxx-driver" ):
continue
- # XXX: Avoid conflict between v8 and v8-3.25 source files in
+ # XXX: Avoid conflict between v8, v8-3.25 and mozjs source files in
# src/mongo/scripting
# Remove after v8-3.25 migration.
- if x.find("v8-3.25") != -1:
+ if x.find("v8-3.25") != -1 or x.find("mozjs") != -1:
continue
full = prefix + "/" + x
if os.path.isdir( full ) and not os.path.islink( full ):
diff --git a/debian/mongodb-enterprise-server.docs b/debian/mongodb-enterprise-server.docs
index 862a424eb58..9b2620af0ce 100644
--- a/debian/mongodb-enterprise-server.docs
+++ b/debian/mongodb-enterprise-server.docs
@@ -6,3 +6,4 @@ snmp/MONGODBINC-MIB.txt
LICENSE.txt
README
THIRD-PARTY-NOTICES
+MPL-2
diff --git a/debian/mongodb-enterprise-unstable-server.docs b/debian/mongodb-enterprise-unstable-server.docs
index 862a424eb58..9b2620af0ce 100644
--- a/debian/mongodb-enterprise-unstable-server.docs
+++ b/debian/mongodb-enterprise-unstable-server.docs
@@ -6,3 +6,4 @@ snmp/MONGODBINC-MIB.txt
LICENSE.txt
README
THIRD-PARTY-NOTICES
+MPL-2
diff --git a/debian/mongodb-org-server.docs b/debian/mongodb-org-server.docs
index c01d53eb317..b1cea8cce6f 100644
--- a/debian/mongodb-org-server.docs
+++ b/debian/mongodb-org-server.docs
@@ -1,3 +1,4 @@
GNU-AGPL-3.0
README
THIRD-PARTY-NOTICES
+MPL-2
diff --git a/debian/mongodb-org-unstable-server.docs b/debian/mongodb-org-unstable-server.docs
index c01d53eb317..b1cea8cce6f 100644
--- a/debian/mongodb-org-unstable-server.docs
+++ b/debian/mongodb-org-unstable-server.docs
@@ -1,3 +1,4 @@
GNU-AGPL-3.0
README
THIRD-PARTY-NOTICES
+MPL-2
diff --git a/distsrc/MPL-2 b/distsrc/MPL-2
new file mode 100644
index 00000000000..14e2f777f6c
--- /dev/null
+++ b/distsrc/MPL-2
@@ -0,0 +1,373 @@
+Mozilla Public License Version 2.0
+==================================
+
+1. Definitions
+--------------
+
+1.1. "Contributor"
+ means each individual or legal entity that creates, contributes to
+ the creation of, or owns Covered Software.
+
+1.2. "Contributor Version"
+ means the combination of the Contributions of others (if any) used
+ by a Contributor and that particular Contributor's Contribution.
+
+1.3. "Contribution"
+ means Covered Software of a particular Contributor.
+
+1.4. "Covered Software"
+ means Source Code Form to which the initial Contributor has attached
+ the notice in Exhibit A, the Executable Form of such Source Code
+ Form, and Modifications of such Source Code Form, in each case
+ including portions thereof.
+
+1.5. "Incompatible With Secondary Licenses"
+ means
+
+ (a) that the initial Contributor has attached the notice described
+ in Exhibit B to the Covered Software; or
+
+ (b) that the Covered Software was made available under the terms of
+ version 1.1 or earlier of the License, but not also under the
+ terms of a Secondary License.
+
+1.6. "Executable Form"
+ means any form of the work other than Source Code Form.
+
+1.7. "Larger Work"
+ means a work that combines Covered Software with other material, in
+ a separate file or files, that is not Covered Software.
+
+1.8. "License"
+ means this document.
+
+1.9. "Licensable"
+ means having the right to grant, to the maximum extent possible,
+ whether at the time of the initial grant or subsequently, any and
+ all of the rights conveyed by this License.
+
+1.10. "Modifications"
+ means any of the following:
+
+ (a) any file in Source Code Form that results from an addition to,
+ deletion from, or modification of the contents of Covered
+ Software; or
+
+ (b) any new file in Source Code Form that contains any Covered
+ Software.
+
+1.11. "Patent Claims" of a Contributor
+ means any patent claim(s), including without limitation, method,
+ process, and apparatus claims, in any patent Licensable by such
+ Contributor that would be infringed, but for the grant of the
+ License, by the making, using, selling, offering for sale, having
+ made, import, or transfer of either its Contributions or its
+ Contributor Version.
+
+1.12. "Secondary License"
+ means either the GNU General Public License, Version 2.0, the GNU
+ Lesser General Public License, Version 2.1, the GNU Affero General
+ Public License, Version 3.0, or any later versions of those
+ licenses.
+
+1.13. "Source Code Form"
+ means the form of the work preferred for making modifications.
+
+1.14. "You" (or "Your")
+ means an individual or a legal entity exercising rights under this
+ License. For legal entities, "You" includes any entity that
+ controls, is controlled by, or is under common control with You. For
+ purposes of this definition, "control" means (a) the power, direct
+ or indirect, to cause the direction or management of such entity,
+ whether by contract or otherwise, or (b) ownership of more than
+ fifty percent (50%) of the outstanding shares or beneficial
+ ownership of such entity.
+
+2. License Grants and Conditions
+--------------------------------
+
+2.1. Grants
+
+Each Contributor hereby grants You a world-wide, royalty-free,
+non-exclusive license:
+
+(a) under intellectual property rights (other than patent or trademark)
+ Licensable by such Contributor to use, reproduce, make available,
+ modify, display, perform, distribute, and otherwise exploit its
+ Contributions, either on an unmodified basis, with Modifications, or
+ as part of a Larger Work; and
+
+(b) under Patent Claims of such Contributor to make, use, sell, offer
+ for sale, have made, import, and otherwise transfer either its
+ Contributions or its Contributor Version.
+
+2.2. Effective Date
+
+The licenses granted in Section 2.1 with respect to any Contribution
+become effective for each Contribution on the date the Contributor first
+distributes such Contribution.
+
+2.3. Limitations on Grant Scope
+
+The licenses granted in this Section 2 are the only rights granted under
+this License. No additional rights or licenses will be implied from the
+distribution or licensing of Covered Software under this License.
+Notwithstanding Section 2.1(b) above, no patent license is granted by a
+Contributor:
+
+(a) for any code that a Contributor has removed from Covered Software;
+ or
+
+(b) for infringements caused by: (i) Your and any other third party's
+ modifications of Covered Software, or (ii) the combination of its
+ Contributions with other software (except as part of its Contributor
+ Version); or
+
+(c) under Patent Claims infringed by Covered Software in the absence of
+ its Contributions.
+
+This License does not grant any rights in the trademarks, service marks,
+or logos of any Contributor (except as may be necessary to comply with
+the notice requirements in Section 3.4).
+
+2.4. Subsequent Licenses
+
+No Contributor makes additional grants as a result of Your choice to
+distribute the Covered Software under a subsequent version of this
+License (see Section 10.2) or under the terms of a Secondary License (if
+permitted under the terms of Section 3.3).
+
+2.5. Representation
+
+Each Contributor represents that the Contributor believes its
+Contributions are its original creation(s) or it has sufficient rights
+to grant the rights to its Contributions conveyed by this License.
+
+2.6. Fair Use
+
+This License is not intended to limit any rights You have under
+applicable copyright doctrines of fair use, fair dealing, or other
+equivalents.
+
+2.7. Conditions
+
+Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
+in Section 2.1.
+
+3. Responsibilities
+-------------------
+
+3.1. Distribution of Source Form
+
+All distribution of Covered Software in Source Code Form, including any
+Modifications that You create or to which You contribute, must be under
+the terms of this License. You must inform recipients that the Source
+Code Form of the Covered Software is governed by the terms of this
+License, and how they can obtain a copy of this License. You may not
+attempt to alter or restrict the recipients' rights in the Source Code
+Form.
+
+3.2. Distribution of Executable Form
+
+If You distribute Covered Software in Executable Form then:
+
+(a) such Covered Software must also be made available in Source Code
+ Form, as described in Section 3.1, and You must inform recipients of
+ the Executable Form how they can obtain a copy of such Source Code
+ Form by reasonable means in a timely manner, at a charge no more
+ than the cost of distribution to the recipient; and
+
+(b) You may distribute such Executable Form under the terms of this
+ License, or sublicense it under different terms, provided that the
+ license for the Executable Form does not attempt to limit or alter
+ the recipients' rights in the Source Code Form under this License.
+
+3.3. Distribution of a Larger Work
+
+You may create and distribute a Larger Work under terms of Your choice,
+provided that You also comply with the requirements of this License for
+the Covered Software. If the Larger Work is a combination of Covered
+Software with a work governed by one or more Secondary Licenses, and the
+Covered Software is not Incompatible With Secondary Licenses, this
+License permits You to additionally distribute such Covered Software
+under the terms of such Secondary License(s), so that the recipient of
+the Larger Work may, at their option, further distribute the Covered
+Software under the terms of either this License or such Secondary
+License(s).
+
+3.4. Notices
+
+You may not remove or alter the substance of any license notices
+(including copyright notices, patent notices, disclaimers of warranty,
+or limitations of liability) contained within the Source Code Form of
+the Covered Software, except that You may alter any license notices to
+the extent required to remedy known factual inaccuracies.
+
+3.5. Application of Additional Terms
+
+You may choose to offer, and to charge a fee for, warranty, support,
+indemnity or liability obligations to one or more recipients of Covered
+Software. However, You may do so only on Your own behalf, and not on
+behalf of any Contributor. You must make it absolutely clear that any
+such warranty, support, indemnity, or liability obligation is offered by
+You alone, and You hereby agree to indemnify every Contributor for any
+liability incurred by such Contributor as a result of warranty, support,
+indemnity or liability terms You offer. You may include additional
+disclaimers of warranty and limitations of liability specific to any
+jurisdiction.
+
+4. Inability to Comply Due to Statute or Regulation
+---------------------------------------------------
+
+If it is impossible for You to comply with any of the terms of this
+License with respect to some or all of the Covered Software due to
+statute, judicial order, or regulation then You must: (a) comply with
+the terms of this License to the maximum extent possible; and (b)
+describe the limitations and the code they affect. Such description must
+be placed in a text file included with all distributions of the Covered
+Software under this License. Except to the extent prohibited by statute
+or regulation, such description must be sufficiently detailed for a
+recipient of ordinary skill to be able to understand it.
+
+5. Termination
+--------------
+
+5.1. The rights granted under this License will terminate automatically
+if You fail to comply with any of its terms. However, if You become
+compliant, then the rights granted under this License from a particular
+Contributor are reinstated (a) provisionally, unless and until such
+Contributor explicitly and finally terminates Your grants, and (b) on an
+ongoing basis, if such Contributor fails to notify You of the
+non-compliance by some reasonable means prior to 60 days after You have
+come back into compliance. Moreover, Your grants from a particular
+Contributor are reinstated on an ongoing basis if such Contributor
+notifies You of the non-compliance by some reasonable means, this is the
+first time You have received notice of non-compliance with this License
+from such Contributor, and You become compliant prior to 30 days after
+Your receipt of the notice.
+
+5.2. If You initiate litigation against any entity by asserting a patent
+infringement claim (excluding declaratory judgment actions,
+counter-claims, and cross-claims) alleging that a Contributor Version
+directly or indirectly infringes any patent, then the rights granted to
+You by any and all Contributors for the Covered Software under Section
+2.1 of this License shall terminate.
+
+5.3. In the event of termination under Sections 5.1 or 5.2 above, all
+end user license agreements (excluding distributors and resellers) which
+have been validly granted by You or Your distributors under this License
+prior to termination shall survive termination.
+
+************************************************************************
+* *
+* 6. Disclaimer of Warranty *
+* ------------------------- *
+* *
+* Covered Software is provided under this License on an "as is" *
+* basis, without warranty of any kind, either expressed, implied, or *
+* statutory, including, without limitation, warranties that the *
+* Covered Software is free of defects, merchantable, fit for a *
+* particular purpose or non-infringing. The entire risk as to the *
+* quality and performance of the Covered Software is with You. *
+* Should any Covered Software prove defective in any respect, You *
+* (not any Contributor) assume the cost of any necessary servicing, *
+* repair, or correction. This disclaimer of warranty constitutes an *
+* essential part of this License. No use of any Covered Software is *
+* authorized under this License except under this disclaimer. *
+* *
+************************************************************************
+
+************************************************************************
+* *
+* 7. Limitation of Liability *
+* -------------------------- *
+* *
+* Under no circumstances and under no legal theory, whether tort *
+* (including negligence), contract, or otherwise, shall any *
+* Contributor, or anyone who distributes Covered Software as *
+* permitted above, be liable to You for any direct, indirect, *
+* special, incidental, or consequential damages of any character *
+* including, without limitation, damages for lost profits, loss of *
+* goodwill, work stoppage, computer failure or malfunction, or any *
+* and all other commercial damages or losses, even if such party *
+* shall have been informed of the possibility of such damages. This *
+* limitation of liability shall not apply to liability for death or *
+* personal injury resulting from such party's negligence to the *
+* extent applicable law prohibits such limitation. Some *
+* jurisdictions do not allow the exclusion or limitation of *
+* incidental or consequential damages, so this exclusion and *
+* limitation may not apply to You. *
+* *
+************************************************************************
+
+8. Litigation
+-------------
+
+Any litigation relating to this License may be brought only in the
+courts of a jurisdiction where the defendant maintains its principal
+place of business and such litigation shall be governed by laws of that
+jurisdiction, without reference to its conflict-of-law provisions.
+Nothing in this Section shall prevent a party's ability to bring
+cross-claims or counter-claims.
+
+9. Miscellaneous
+----------------
+
+This License represents the complete agreement concerning the subject
+matter hereof. If any provision of this License is held to be
+unenforceable, such provision shall be reformed only to the extent
+necessary to make it enforceable. Any law or regulation which provides
+that the language of a contract shall be construed against the drafter
+shall not be used to construe this License against a Contributor.
+
+10. Versions of the License
+---------------------------
+
+10.1. New Versions
+
+Mozilla Foundation is the license steward. Except as provided in Section
+10.3, no one other than the license steward has the right to modify or
+publish new versions of this License. Each version will be given a
+distinguishing version number.
+
+10.2. Effect of New Versions
+
+You may distribute the Covered Software under the terms of the version
+of the License under which You originally received the Covered Software,
+or under the terms of any subsequent version published by the license
+steward.
+
+10.3. Modified Versions
+
+If you create software not governed by this License, and you want to
+create a new license for such software, you may create and use a
+modified version of this License if you rename the license and remove
+any references to the name of the license steward (except to note that
+such modified license differs from this License).
+
+10.4. Distributing Source Code Form that is Incompatible With Secondary
+Licenses
+
+If You choose to distribute Source Code Form that is Incompatible With
+Secondary Licenses under the terms of this version of the License, the
+notice described in Exhibit B of this License must be attached.
+
+Exhibit A - Source Code Form License Notice
+-------------------------------------------
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ License, v. 2.0. If a copy of the MPL was not distributed with this
+ file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+If it is not possible or desirable to put the notice in a particular
+file, then You may include the notice in a location (such as a LICENSE
+file in a relevant directory) where a recipient would be likely to look
+for such a notice.
+
+You may add additional accurate notices of copyright ownership.
+
+Exhibit B - "Incompatible With Secondary Licenses" Notice
+---------------------------------------------------------
+
+ This Source Code Form is "Incompatible With Secondary Licenses", as
+ defined by the Mozilla Public License, v. 2.0.
diff --git a/distsrc/THIRD-PARTY-NOTICES b/distsrc/THIRD-PARTY-NOTICES
index 54d3f4d4886..164d1838974 100644
--- a/distsrc/THIRD-PARTY-NOTICES
+++ b/distsrc/THIRD-PARTY-NOTICES
@@ -8,6 +8,9 @@ please bring it to our attention through any of the ways detailed here :
The attached notices are provided for information only.
+For any licenses that require disclosure of source, sources are available at
+https://github.com/mongodb/mongo.
+
1) License Notice for Boost
---------------------------
@@ -498,4 +501,202 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+13) License Notice for SpiderMonkey
+-----------------------------------
+
+|------------------------------------------------|------------------|---------------|
+| SpiderMonkey Distribution Files | Copyright Holder | License |
+|------------------------------------------------|------------------|---------------|
+| js/src/jit/shared/AssemblerBuffer-x86-shared.h | Apple, Inc | BSD-2-Clause |
+| js/src/jit/shared/BaseAssembler-x86-shared.h | | |
+|------------------------------------------------|------------------|---------------|
+| js/src/builtin/ | Google, Inc | BSD-3-Clause |
+| js/src/irregexp/ | | |
+| js/src/jit/arm/ | | |
+| js/src/jit/mips/ | | |
+| mfbt/double-conversion/ | | |
+|------------------------------------------------|------------------|---------------|
+| intl/icu/source/common/unicode/ | IBM, Inc | ICU |
+|------------------------------------------------|------------------|---------------|
+| js/src/asmjs/ | Mozilla, Inc | Apache2 |
+|------------------------------------------------|------------------|---------------|
+| js/public/ | Mozilla, Inc | MPL2 |
+| js/src/ | | |
+| mfbt | | |
+|------------------------------------------------|------------------|---------------|
+| js/src/vm/Unicode.cpp | None | Public Domain |
+|------------------------------------------------|------------------|---------------|
+| mfbt/lz4.c | Yann Collet | BSD-2-Clause |
+| mfbt/lz4.h | | |
+|------------------------------------------------|------------------|---------------|
+
+Other optional 3rd party software included in the SpiderMonkey distribution is removed by MongoDB.
+
+
+Apple, Inc: BSD-2-Clause
+------------------------
+
+Copyright (C) 2008 Apple Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+Google, Inc: BSD-3-Clause
+-------------------------
+
+Copyright 2012 the V8 project authors. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of Google Inc. nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+ICU License - ICU 1.8.1 and later
+---------------------------------
+
+COPYRIGHT AND PERMISSION NOTICE
+
+Copyright (c) 1995-2012 International Business Machines Corporation and
+others
+
+All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, provided that the above copyright notice(s) and this
+permission notice appear in all copies of the Software and that both the
+above copyright notice(s) and this permission notice appear in supporting
+documentation.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE
+BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES,
+OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder shall
+not be used in advertising or otherwise to promote the sale, use or other
+dealings in this Software without prior written authorization of the
+copyright holder.
+
+All trademarks and registered trademarks mentioned herein are the property
+of their respective owners.
+
+
+Mozilla, Inc: Apache 2
+----------------------
+
+Copyright 2014 Mozilla Foundation
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+
+Mozilla, Inc: MPL 2
+-------------------
+
+Copyright 2014 Mozilla Foundation
+
+This Source Code Form is subject to the terms of the Mozilla Public
+License, v. 2.0. If a copy of the MPL was not distributed with this
+file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+Public Domain
+-------------
+
+Any copyright is dedicated to the Public Domain.
+http://creativecommons.org/licenses/publicdomain/
+
+
+LZ4: BSD-2-Clause
+-----------------
+
+Copyright (C) 2011-2014, Yann Collet.
+BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+You can contact the author at :
+- LZ4 source repository : http://code.google.com/p/lz4/
+- LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c
+
+
End
diff --git a/jstests/core/dbadmin.js b/jstests/core/dbadmin.js
index dbf195e056c..93e7dea308f 100644
--- a/jstests/core/dbadmin.js
+++ b/jstests/core/dbadmin.js
@@ -99,5 +99,5 @@ for (var i = 0; i < (versionArray.length - 1); i++) if (versionArray[i] >= 0) {
assert.eq(serverStatus.version, latestStartUpLog.buildinfo.version, "Mongo version doesn't match that from ServerStatus");
assert.eq(version, versionArrayCleaned.join('.'), "version doesn't match that from the versionArray");
var jsEngine = latestStartUpLog.buildinfo.javascriptEngine;
-assert((jsEngine.startsWith("v8") || jsEngine == "none"));
+assert((jsEngine.startsWith("v8") || jsEngine == "none" || jsEngine.startsWith("mozjs")));
assert.eq(isMaster.maxBsonObjectSize, latestStartUpLog.buildinfo.maxBsonObjectSize, "maxBsonObjectSize doesn't match one from ismaster");
diff --git a/jstests/core/hashtest1.js b/jstests/core/hashtest1.js
index 981a0c36877..d0307c2e59e 100644
--- a/jstests/core/hashtest1.js
+++ b/jstests/core/hashtest1.js
@@ -34,7 +34,8 @@ var nullHash = hash( null );
assert(! friendlyEqual( falseHash , nullHash ) , "false and null should hash to different things");
var dateHash = hash( new Date() );
-sleep(1);
+// Sleep so we get a new date. Sleeping for 1 sometimes returns the same date, so 2
+sleep(2);
var isodateHash = hash( ISODate() );
assert(! friendlyEqual( dateHash, isodateHash) , "different dates should hash to different things");
@@ -75,4 +76,4 @@ assert.eq( nanHash , zeroHash , "NaN and Zero should hash to the same thing");
//should also test that CodeWScope hashes correctly
-//but waiting for SERVER-3391 (CodeWScope support in shell) \ No newline at end of file
+//but waiting for SERVER-3391 (CodeWScope support in shell)
diff --git a/jstests/core/where5.js b/jstests/core/where5.js
index d7140cd21f6..1c0ea8d0076 100644
--- a/jstests/core/where5.js
+++ b/jstests/core/where5.js
@@ -1,4 +1,4 @@
-// Tests toString() on _v8_function in object constructor.
+// Tests toString() in object constructor.
// Verifies that native functions do not expose the _native_function and _native_data properties.
var t = db.where5;
@@ -14,9 +14,6 @@ function printIdConstructor(doc) {
doc = this;
}
- // This used to crash.
- doc._id.constructor._v8_function.toString();
-
// Verify that function and data fields are hidden.
assert(!('_native_function' in sleep));
assert(!('_native_data' in sleep));
diff --git a/jstests/replsets/capped_id.js b/jstests/replsets/capped_id.js
index 520b7c4cca5..cd866fb0234 100644
--- a/jstests/replsets/capped_id.js
+++ b/jstests/replsets/capped_id.js
@@ -39,6 +39,12 @@ var masterdb = master.getDB( dbname );
var slave1db = slave1.getDB( dbname );
var slave2db = slave2.getDB( dbname );
+function countIdIndexes(theDB, coll) {
+ return theDB[coll].getIndexes().filter(function(idx) {
+ return friendlyEqual(idx.key, {_id: 1});
+ }).length;
+}
+
var numtests = 4;
for( testnum=0; testnum < numtests; testnum++ ){
@@ -70,11 +76,6 @@ for( testnum=0; testnum < numtests; testnum++ ){
}
replTest.awaitReplication();
- function countIdIndexes(theDB, coll) {
- return theDB[coll].getIndexes().filter(function(idx) {
- return friendlyEqual(idx.key, {_id: 1});
- }).length;
- }
// make sure _id index exists on primary
assert.eq( 1 ,
countIdIndexes(masterdb, coll),
diff --git a/rpm/mongodb-enterprise-unstable.spec b/rpm/mongodb-enterprise-unstable.spec
index 0bb9794c0cb..f15346f2758 100644
--- a/rpm/mongodb-enterprise-unstable.spec
+++ b/rpm/mongodb-enterprise-unstable.spec
@@ -220,6 +220,7 @@ fi
%doc LICENSE.txt
%doc README
%doc THIRD-PARTY-NOTICES
+%doc MPL-2
%files shell
%defattr(-,root,root,-)
diff --git a/rpm/mongodb-enterprise.spec b/rpm/mongodb-enterprise.spec
index 784af693c92..2e8e265bab0 100644
--- a/rpm/mongodb-enterprise.spec
+++ b/rpm/mongodb-enterprise.spec
@@ -229,6 +229,7 @@ fi
%doc LICENSE.txt
%doc README
%doc THIRD-PARTY-NOTICES
+%doc MPL-2
diff --git a/rpm/mongodb-org-unstable.spec b/rpm/mongodb-org-unstable.spec
index 22b66e73e28..692f6ac8557 100644
--- a/rpm/mongodb-org-unstable.spec
+++ b/rpm/mongodb-org-unstable.spec
@@ -214,6 +214,7 @@ fi
%doc GNU-AGPL-3.0
%doc README
%doc THIRD-PARTY-NOTICES
+%doc MPL-2
%files shell
%defattr(-,root,root,-)
diff --git a/rpm/mongodb-org.spec b/rpm/mongodb-org.spec
index 382e3f1c6db..8117b9341a2 100644
--- a/rpm/mongodb-org.spec
+++ b/rpm/mongodb-org.spec
@@ -224,6 +224,7 @@ fi
%doc GNU-AGPL-3.0
%doc README
%doc THIRD-PARTY-NOTICES
+%doc MPL-2
diff --git a/src/mongo/SConscript b/src/mongo/SConscript
index 72388e5a6a0..c66798e4e0c 100644
--- a/src/mongo/SConscript
+++ b/src/mongo/SConscript
@@ -15,6 +15,7 @@ Import("env")
Import("has_option")
Import("get_option")
Import("usev8")
+Import("usemozjs")
Import("use_system_version_of_library")
# Boost we need everywhere. 's2' is spammed in all over the place by
@@ -269,7 +270,7 @@ if env.TargetOSIs('osx') or env["_HAVEPCAP"]:
# --- shell ---
-if not has_option('noshell') and usev8:
+if not has_option('noshell') and (usev8 or usemozjs):
shell_core_env = env.Clone()
if has_option("safeshell"):
shell_core_env.Append(CPPDEFINES=["MONGO_SAFE_SHELL"])
@@ -415,7 +416,8 @@ env.Alias( "core", [ '#/%s' % b for b in [ add_exe( "mongo" ), add_exe( "mongod"
# Stage the top-level mongodb banners
distsrc = env.Dir('#distsrc')
env.Append(MODULE_BANNERS = [distsrc.File('README'),
- distsrc.File('THIRD-PARTY-NOTICES')])
+ distsrc.File('THIRD-PARTY-NOTICES'),
+ distsrc.File('MPL-2')])
# If no module has introduced a file named LICENSE.txt, then inject the AGPL.
if sum(itertools.imap(lambda x: x.name == "LICENSE.txt", env['MODULE_BANNERS'])) == 0:
diff --git a/src/mongo/base/error_codes.err b/src/mongo/base/error_codes.err
index 68311bc4296..f518cfd07d9 100644
--- a/src/mongo/base/error_codes.err
+++ b/src/mongo/base/error_codes.err
@@ -138,6 +138,7 @@ error_code("StaleTerm", 135)
error_code("CappedPositionLost", 136)
error_code("IncompatibleShardingConfigVersion", 137)
error_code("RemoteOplogStale", 138)
+error_code("JSInterpreterFailure", 139)
# Non-sequential error codes (for compatibility only)
error_code("NotMaster", 10107) #this comes from assert_util.h
diff --git a/src/mongo/dbtests/jstests.cpp b/src/mongo/dbtests/jstests.cpp
index 68c6e330258..e5216d5842d 100644
--- a/src/mongo/dbtests/jstests.cpp
+++ b/src/mongo/dbtests/jstests.cpp
@@ -208,7 +208,14 @@ public:
// An error is logged for an invalid statement when reportError == true.
ASSERT(!scope->exec("notAFunction()", "foo", false, true, false));
- ASSERT(_logger.logged());
+
+ // Don't check if we're using SpiderMonkey. Our threading model breaks
+ // this test
+ // TODO: figure out a way to check for SpiderMonkey
+ auto ivs = globalScriptEngine->getInterpreterVersionString();
+ if (ivs.compare(0, ivs.length(), "MozJS") != 0) {
+ ASSERT(_logger.logged());
+ }
}
private:
@@ -231,7 +238,14 @@ public:
} catch (const DBException&) {
// ignore the exception; just test that we logged something
}
- ASSERT(_logger.logged());
+
+ // Don't check if we're using SpiderMonkey. Our threading model breaks
+ // this test
+ // TODO: figure out a way to check for SpiderMonkey
+ auto ivs = globalScriptEngine->getInterpreterVersionString();
+ if (ivs.compare(0, ivs.length(), "MozJS") != 0) {
+ ASSERT(_logger.logged());
+ }
}
private:
@@ -389,25 +403,43 @@ public:
<< "zz" << BSONObj());
s->setObject("blah", o, true);
- s->invoke("blah.y = 'e'", 0, 0);
- BSONObj out = s->getObject("blah");
- ASSERT(strlen(out["y"].valuestr()) > 1);
+ BSONObj out;
+
+ /**
+ * TODO remove the v8 tests after we switch over
+ *
+ * Note that we've changed behavior so that uncaught js exceptions that
+ * bubble up actually convert into user exceptions, instead of just
+ * logging to stdout and silently failing otherwise.
+ */
+ auto ivs = globalScriptEngine->getInterpreterVersionString();
+ if (ivs.compare(0, ivs.length(), "MozJS") == 0) {
+ ASSERT_THROWS(s->invoke("blah.y = 'e'", 0, 0), mongo::UserException);
+ ASSERT_THROWS(s->invoke("blah.a = 19;", 0, 0), mongo::UserException);
+ ASSERT_THROWS(s->invoke("blah.zz.a = 19;", 0, 0), mongo::UserException);
+ ASSERT_THROWS(s->setObject("blah.zz", BSON("a" << 19)), mongo::UserException);
+ ASSERT_THROWS(s->invoke("delete blah['x']", 0, 0), mongo::UserException);
+ } else {
+ s->invoke("blah.y = 'e'", 0, 0);
+ out = s->getObject("blah");
+ ASSERT(strlen(out["y"].valuestr()) > 1);
- s->invoke("blah.a = 19;", 0, 0);
- out = s->getObject("blah");
- ASSERT(out["a"].eoo());
+ s->invoke("blah.a = 19;", 0, 0);
+ out = s->getObject("blah");
+ ASSERT(out["a"].eoo());
- s->invoke("blah.zz.a = 19;", 0, 0);
- out = s->getObject("blah");
- ASSERT(out["zz"].embeddedObject()["a"].eoo());
+ s->invoke("blah.zz.a = 19;", 0, 0);
+ out = s->getObject("blah");
+ ASSERT(out["zz"].embeddedObject()["a"].eoo());
- s->setObject("blah.zz", BSON("a" << 19));
- out = s->getObject("blah");
- ASSERT(out["zz"].embeddedObject()["a"].eoo());
+ s->setObject("blah.zz", BSON("a" << 19));
+ out = s->getObject("blah");
+ ASSERT(out["zz"].embeddedObject()["a"].eoo());
- s->invoke("delete blah['x']", 0, 0);
- out = s->getObject("blah");
- ASSERT(!out["x"].eoo());
+ s->invoke("delete blah['x']", 0, 0);
+ out = s->getObject("blah");
+ ASSERT(!out["x"].eoo());
+ }
// read-only object itself can be overwritten
s->invoke("blah = {}", 0, 0);
diff --git a/src/mongo/installer/msi/wxs/LicensingFragment.wxs b/src/mongo/installer/msi/wxs/LicensingFragment.wxs
index 41c7b4bb010..7e2ff5ba8cf 100644
--- a/src/mongo/installer/msi/wxs/LicensingFragment.wxs
+++ b/src/mongo/installer/msi/wxs/LicensingFragment.wxs
@@ -26,6 +26,10 @@
<File Id="f_Readme" Name="README" Source="$(var.LicenseSource)\README"
DiskId ="1" />
</Component>
+ <Component Id="c_MPL2" Guid="326EAE1B-6AF2-45D2-90FC-8660C50C7271">
+ <File Id="f_MPL2" Name="MPL-2" Source="$(var.LicenseSource)\MPL-2"
+ DiskId ="1" />
+ </Component>
<Component Id="c_InstallKey" Guid="31635E6D-CCE1-43AD-8AB3-4F5607D75755">
<RegistryKey Root="HKLM"
Key="Software\MongoDB\Server\$(var.MongoDBMajorVersion)">
@@ -39,6 +43,7 @@
<ComponentRef Id="c_Thirdparty"/>
<ComponentRef Id="c_License"/>
<ComponentRef Id="c_InstallKey"/>
+ <ComponentRef Id="c_MPL2"/>
</ComponentGroup>
<?if $(var.Edition) = Enterprise ?>
diff --git a/src/mongo/scripting/SConscript b/src/mongo/scripting/SConscript
index f05572b98c8..f3c3d9a05fd 100644
--- a/src/mongo/scripting/SConscript
+++ b/src/mongo/scripting/SConscript
@@ -4,6 +4,7 @@ Import([
'env',
'serverJs',
'usev8',
+ 'usemozjs',
'v8suffix',
])
@@ -68,6 +69,79 @@ if usev8:
'$BUILD_DIR/mongo/db/service_context',
],
)
+elif usemozjs:
+ scriptingEnv = env.Clone()
+ scriptingEnv.InjectThirdPartyIncludePaths(libraries=['mozjs'])
+
+ # TODO: get rid of all of this /FI and -include stuff and migrate to a shim
+ # header we include in all of our files.
+ if env.TargetOSIs('windows'):
+ scriptingEnv.Append(CCFLAGS=[
+ '/FI', 'js-config.h',
+ '/FI', 'js/RequiredDefines.h',
+ ])
+ else:
+ scriptingEnv.Append(
+ CCFLAGS=[
+ '-include', 'js-config.h',
+ '-include', 'js/RequiredDefines.h',
+ '-Wno-invalid-offsetof',
+ ],
+ CXXFLAGS=[
+ '-Wno-non-virtual-dtor',
+ ],
+ )
+
+ scriptingEnv.Prepend(CPPDEFINES=[
+ 'JS_USE_CUSTOM_ALLOCATOR',
+ 'STATIC_JS_API=1',
+ ])
+
+ scriptingEnv.Library(
+ target='scripting',
+ source=[
+ 'mozjs/base.cpp',
+ 'mozjs/bindata.cpp',
+ 'mozjs/bson.cpp',
+ 'mozjs/countdownlatch.cpp',
+ 'mozjs/cursor.cpp',
+ 'mozjs/dbcollection.cpp',
+ 'mozjs/db.cpp',
+ 'mozjs/dbpointer.cpp',
+ 'mozjs/dbquery.cpp',
+ 'mozjs/dbref.cpp',
+ 'mozjs/engine.cpp',
+ 'mozjs/exception.cpp',
+ 'mozjs/global.cpp',
+ 'mozjs/idwrapper.cpp',
+ 'mozjs/implscope.cpp',
+ 'mozjs/jscustomallocator.cpp',
+ 'mozjs/jsstringwrapper.cpp',
+ 'mozjs/jsthread.cpp',
+ 'mozjs/maxkey.cpp',
+ 'mozjs/minkey.cpp',
+ 'mozjs/mongo.cpp',
+ 'mozjs/nativefunction.cpp',
+ 'mozjs/numberint.cpp',
+ 'mozjs/numberlong.cpp',
+ 'mozjs/object.cpp',
+ 'mozjs/objectwrapper.cpp',
+ 'mozjs/oid.cpp',
+ 'mozjs/PosixNSPR.cpp',
+ 'mozjs/proxyscope.cpp',
+ 'mozjs/regexp.cpp',
+ 'mozjs/timestamp.cpp',
+ 'mozjs/valuereader.cpp',
+ 'mozjs/valuewriter.cpp',
+ ],
+ LIBDEPS=[
+ 'bson_template_evaluator',
+ 'scripting_common',
+ '$BUILD_DIR/third_party/shim_mozjs',
+ '$BUILD_DIR/mongo/shell/mongojs',
+ '$BUILD_DIR/mongo/db/service_context',
+ ],
+ )
else:
env.Library(
target='scripting',
@@ -88,9 +162,9 @@ env.Library(
)
env.CppUnitTest(
- target='v8_deadline_monitor_test',
+ target='deadline_monitor_test',
source=[
- 'v8_deadline_monitor_test.cpp',
+ 'deadline_monitor_test.cpp',
],
LIBDEPS=[
],
diff --git a/src/mongo/scripting/v8_deadline_monitor.h b/src/mongo/scripting/deadline_monitor.h
index 89f7e9d1b84..89f7e9d1b84 100644
--- a/src/mongo/scripting/v8_deadline_monitor.h
+++ b/src/mongo/scripting/deadline_monitor.h
diff --git a/src/mongo/scripting/v8_deadline_monitor_test.cpp b/src/mongo/scripting/deadline_monitor_test.cpp
index 702160cd1ed..98f4008b234 100644
--- a/src/mongo/scripting/v8_deadline_monitor_test.cpp
+++ b/src/mongo/scripting/deadline_monitor_test.cpp
@@ -30,7 +30,7 @@
#include "mongo/platform/basic.h"
-#include "mongo/scripting/v8_deadline_monitor.h"
+#include "mongo/scripting/deadline_monitor.h"
#include "mongo/unittest/unittest.h"
diff --git a/src/mongo/scripting/engine_v8-3.25.h b/src/mongo/scripting/engine_v8-3.25.h
index f3e594c33ee..0865ab59ac3 100644
--- a/src/mongo/scripting/engine_v8-3.25.h
+++ b/src/mongo/scripting/engine_v8-3.25.h
@@ -40,7 +40,7 @@
#include "mongo/client/dbclientcursor.h"
#include "mongo/platform/unordered_map.h"
#include "mongo/scripting/engine.h"
-#include "mongo/scripting/v8_deadline_monitor.h"
+#include "mongo/scripting/deadline_monitor.h"
#include "mongo/scripting/v8-3.25_profiler.h"
/**
diff --git a/src/mongo/scripting/engine_v8.h b/src/mongo/scripting/engine_v8.h
index 8825580c987..d70439c45ff 100644
--- a/src/mongo/scripting/engine_v8.h
+++ b/src/mongo/scripting/engine_v8.h
@@ -38,7 +38,7 @@
#include "mongo/client/dbclientcursor.h"
#include "mongo/platform/unordered_map.h"
#include "mongo/scripting/engine.h"
-#include "mongo/scripting/v8_deadline_monitor.h"
+#include "mongo/scripting/deadline_monitor.h"
#include "mongo/scripting/v8_profiler.h"
/**
diff --git a/src/mongo/scripting/mozjs/PosixNSPR.cpp b/src/mongo/scripting/mozjs/PosixNSPR.cpp
new file mode 100644
index 00000000000..2e6bbe93fe0
--- /dev/null
+++ b/src/mongo/scripting/mozjs/PosixNSPR.cpp
@@ -0,0 +1,259 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * This file was copied out of the firefox 38.0.1esr source tree from
+ * js/src/vm/PosixNSPR.cpp and modified to use the MongoDB threading
+ * primitives.
+ *
+ * The point of this file is to shim the posix emulation of nspr that Mozilla
+ * ships with firefox. We force configuration such that the SpiderMonkey build
+ * looks for these symbols and we provide them from within our object code
+ * rather than attempting to build it in there's so we can take advantage of
+ * the cross platform abstractions that we rely upon.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include <array>
+#include <js/Utility.h>
+#include <vm/PosixNSPR.h>
+
+#include "mongo/stdx/chrono.h"
+#include "mongo/stdx/condition_variable.h"
+#include "mongo/stdx/mutex.h"
+#include "mongo/stdx/thread.h"
+#include "mongo/util/concurrency/thread_name.h"
+#include "mongo/util/concurrency/threadlocal.h"
+
+class nspr::Thread {
+ mongo::stdx::thread thread_;
+ void (*start)(void* arg);
+ void* arg;
+ bool joinable;
+
+public:
+ Thread(void (*start)(void* arg), void* arg, bool joinable)
+ : start(start), arg(arg), joinable(joinable) {}
+
+ static void* ThreadRoutine(void* arg);
+
+ mongo::stdx::thread& thread() {
+ return thread_;
+ }
+};
+
+namespace {
+MONGO_TRIVIALLY_CONSTRUCTIBLE_THREAD_LOCAL nspr::Thread* kCurrentThread;
+} // namespace
+
+void* nspr::Thread::ThreadRoutine(void* arg) {
+ Thread* self = static_cast<Thread*>(arg);
+ kCurrentThread = self;
+ self->start(self->arg);
+ if (!self->joinable)
+ js_delete(self);
+ return nullptr;
+}
+
+PRThread* PR_CreateThread(PRThreadType type,
+ void (*start)(void* arg),
+ void* arg,
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ uint32_t stackSize) {
+ MOZ_ASSERT(type == PR_USER_THREAD);
+ MOZ_ASSERT(priority == PR_PRIORITY_NORMAL);
+
+ try {
+ std::unique_ptr<nspr::Thread, void (*)(nspr::Thread*)> t(
+ js_new<nspr::Thread>(start, arg, state != PR_UNJOINABLE_THREAD),
+ js_delete<nspr::Thread>);
+
+ t->thread() = mongo::stdx::thread(&nspr::Thread::ThreadRoutine, t.get());
+
+ if (state == PR_UNJOINABLE_THREAD) {
+ t->thread().detach();
+ }
+
+ return t.release();
+ } catch (...) {
+ return nullptr;
+ }
+}
+
+PRStatus PR_JoinThread(PRThread* thread) {
+ try {
+ thread->thread().join();
+
+ js_delete(thread);
+
+ return PR_SUCCESS;
+ } catch (...) {
+ return PR_FAILURE;
+ }
+}
+
+PRThread* PR_GetCurrentThread() {
+ return kCurrentThread;
+}
+
+PRStatus PR_SetCurrentThreadName(const char* name) {
+ mongo::setThreadName(name);
+
+ return PR_SUCCESS;
+}
+
+static const size_t MaxTLSKeyCount = 32;
+static size_t gTLSKeyCount;
+namespace {
+MONGO_TRIVIALLY_CONSTRUCTIBLE_THREAD_LOCAL std::array<void*, MaxTLSKeyCount> gTLSArray;
+
+} // namespace
+
+PRStatus PR_NewThreadPrivateIndex(unsigned* newIndex, PRThreadPrivateDTOR destructor) {
+ /*
+ * We only call PR_NewThreadPrivateIndex from the main thread, so there's no
+ * need to lock the table of TLS keys.
+ */
+ MOZ_ASSERT(gTLSKeyCount + 1 < MaxTLSKeyCount);
+
+ *newIndex = gTLSKeyCount;
+ gTLSKeyCount++;
+
+ return PR_SUCCESS;
+}
+
+PRStatus PR_SetThreadPrivate(unsigned index, void* priv) {
+ if (index >= gTLSKeyCount)
+ return PR_FAILURE;
+
+ gTLSArray[index] = priv;
+
+ return PR_SUCCESS;
+}
+
+void* PR_GetThreadPrivate(unsigned index) {
+ if (index >= gTLSKeyCount)
+ return nullptr;
+
+ return gTLSArray[index];
+}
+
+PRStatus PR_CallOnce(PRCallOnceType* once, PRCallOnceFN func) {
+ MOZ_CRASH("PR_CallOnce unimplemented");
+}
+
+PRStatus PR_CallOnceWithArg(PRCallOnceType* once, PRCallOnceWithArgFN func, void* arg) {
+ MOZ_CRASH("PR_CallOnceWithArg unimplemented");
+}
+
+class nspr::Lock {
+ mongo::stdx::mutex mutex_;
+
+public:
+ Lock() {}
+ mongo::stdx::mutex& mutex() {
+ return mutex_;
+ }
+};
+
+PRLock* PR_NewLock() {
+ return js_new<nspr::Lock>();
+}
+
+void PR_DestroyLock(PRLock* lock) {
+ js_delete(lock);
+}
+
+void PR_Lock(PRLock* lock) {
+ lock->mutex().lock();
+}
+
+PRStatus PR_Unlock(PRLock* lock) {
+ lock->mutex().unlock();
+
+ return PR_SUCCESS;
+}
+
+class nspr::CondVar {
+ mongo::stdx::condition_variable cond_;
+ nspr::Lock* lock_;
+
+public:
+ CondVar(nspr::Lock* lock) : lock_(lock) {}
+ mongo::stdx::condition_variable& cond() {
+ return cond_;
+ }
+ nspr::Lock* lock() {
+ return lock_;
+ }
+};
+
+PRCondVar* PR_NewCondVar(PRLock* lock) {
+ return js_new<nspr::CondVar>(lock);
+}
+
+void PR_DestroyCondVar(PRCondVar* cvar) {
+ js_delete(cvar);
+}
+
+PRStatus PR_NotifyCondVar(PRCondVar* cvar) {
+ cvar->cond().notify_one();
+
+ return PR_SUCCESS;
+}
+
+PRStatus PR_NotifyAllCondVar(PRCondVar* cvar) {
+ cvar->cond().notify_all();
+
+ return PR_SUCCESS;
+}
+
+uint32_t PR_MillisecondsToInterval(uint32_t milli) {
+ return milli;
+}
+
+uint32_t PR_MicrosecondsToInterval(uint32_t micro) {
+ return (micro + 999) / 1000;
+}
+
+static const uint64_t TicksPerSecond = 1000;
+static const uint64_t NanoSecondsInSeconds = 1000000000;
+static const uint64_t MicroSecondsInSeconds = 1000000;
+
+uint32_t PR_TicksPerSecond() {
+ return TicksPerSecond;
+}
+
+PRStatus PR_WaitCondVar(PRCondVar* cvar, uint32_t timeout) {
+ if (timeout == PR_INTERVAL_NO_TIMEOUT) {
+ try {
+ mongo::stdx::unique_lock<mongo::stdx::mutex> lk(cvar->lock()->mutex(),
+ mongo::stdx::adopt_lock_t());
+
+ cvar->cond().wait(lk);
+ lk.release();
+
+ return PR_SUCCESS;
+ } catch (...) {
+ return PR_FAILURE;
+ }
+ } else {
+ try {
+ mongo::stdx::unique_lock<mongo::stdx::mutex> lk(cvar->lock()->mutex(),
+ mongo::stdx::adopt_lock_t());
+
+ cvar->cond().wait_for(lk, mongo::stdx::chrono::microseconds(timeout));
+ lk.release();
+
+ return PR_SUCCESS;
+ } catch (...) {
+ return PR_FAILURE;
+ }
+ }
+}
diff --git a/src/mongo/scripting/mozjs/base.cpp b/src/mongo/scripting/mozjs/base.cpp
new file mode 100644
index 00000000000..b85229ae354
--- /dev/null
+++ b/src/mongo/scripting/mozjs/base.cpp
@@ -0,0 +1,68 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/scripting/mozjs/base.h"
+
+namespace mongo {
+namespace mozjs {
+
+const JSFunctionSpec* BaseInfo::freeFunctions = nullptr;
+const JSFunctionSpec* BaseInfo::methods = nullptr;
+
+const char* const BaseInfo::inheritFrom = nullptr;
+
+void BaseInfo::addProperty(JSContext* cx,
+ JS::HandleObject obj,
+ JS::HandleId id,
+ JS::MutableHandleValue v) {}
+void BaseInfo::call(JSContext* cx, JS::CallArgs args) {}
+void BaseInfo::construct(JSContext* cx, JS::CallArgs args) {}
+void BaseInfo::convert(JSContext* cx,
+ JS::HandleObject obj,
+ JSType type,
+ JS::MutableHandleValue vp) {}
+void BaseInfo::delProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* succeeded) {}
+void BaseInfo::enumerate(JSContext* cx, JS::HandleObject obj, JS::AutoIdVector& properties) {}
+void BaseInfo::finalize(JSFreeOp* fop, JSObject* obj) {}
+void BaseInfo::getProperty(JSContext* cx,
+ JS::HandleObject obj,
+ JS::HandleId id,
+ JS::MutableHandleValue vp) {}
+void BaseInfo::hasInstance(JSContext* cx,
+ JS::HandleObject obj,
+ JS::MutableHandleValue vp,
+ bool* bp) {}
+void BaseInfo::postInstall(JSContext* cx, JS::HandleObject global, JS::HandleObject proto) {}
+void BaseInfo::resolve(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* resolvedp) {}
+void BaseInfo::setProperty(
+ JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool strict, JS::MutableHandleValue vp) {}
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/base.h b/src/mongo/scripting/mozjs/base.h
new file mode 100644
index 00000000000..fe90ea65b97
--- /dev/null
+++ b/src/mongo/scripting/mozjs/base.h
@@ -0,0 +1,92 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#pragma once
+
+#include <jsapi.h>
+
+namespace mongo {
+namespace mozjs {
+
+/**
+ * InstallType represents how we want this type overlayed in the JS world.
+ *
+ * Global is for regular types that get installed into the global scope
+ * Private gives us the type, but doesn't make the prototype publicly available
+ * OverNative is used to attach functionality to prototypes that are already there.
+ */
+enum class InstallType : char {
+ Global = 0,
+ Private,
+ OverNative,
+};
+
+/**
+ * The Base object for all info types
+ *
+ * It's difficult to access the array types correctly in a non constexpr world,
+ * so we just stash some nullptrs that are universally available.
+ */
+struct BaseInfo {
+ static const char* const inheritFrom;
+ static const InstallType installType = InstallType::Global;
+ static const JSFunctionSpec* freeFunctions;
+ static const JSFunctionSpec* methods;
+ static const unsigned classFlags = 0;
+ static void addProperty(JSContext* cx,
+ JS::HandleObject obj,
+ JS::HandleId id,
+ JS::MutableHandleValue v);
+ static void call(JSContext* cx, JS::CallArgs args);
+ static void construct(JSContext* cx, JS::CallArgs args);
+ static void convert(JSContext* cx,
+ JS::HandleObject obj,
+ JSType type,
+ JS::MutableHandleValue vp);
+ static void delProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* succeeded);
+ static void enumerate(JSContext* cx, JS::HandleObject obj, JS::AutoIdVector& properties);
+ static void finalize(JSFreeOp* fop, JSObject* obj);
+ static void getProperty(JSContext* cx,
+ JS::HandleObject obj,
+ JS::HandleId id,
+ JS::MutableHandleValue vp);
+ static void hasInstance(JSContext* cx,
+ JS::HandleObject obj,
+ JS::MutableHandleValue vp,
+ bool* bp);
+ static void postInstall(JSContext* cx, JS::HandleObject global, JS::HandleObject proto);
+ static void resolve(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* resolvedp);
+ static void setProperty(JSContext* cx,
+ JS::HandleObject obj,
+ JS::HandleId id,
+ bool strict,
+ JS::MutableHandleValue vp);
+};
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/bindata.cpp b/src/mongo/scripting/mozjs/bindata.cpp
new file mode 100644
index 00000000000..10a3045f103
--- /dev/null
+++ b/src/mongo/scripting/mozjs/bindata.cpp
@@ -0,0 +1,216 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/scripting/mozjs/bindata.h"
+
+#include <iomanip>
+
+#include "mongo/scripting/mozjs/implscope.h"
+#include "mongo/scripting/mozjs/objectwrapper.h"
+#include "mongo/scripting/mozjs/valuereader.h"
+#include "mongo/scripting/mozjs/valuewriter.h"
+#include "mongo/util/base64.h"
+#include "mongo/util/hex.h"
+#include "mongo/util/mongoutils/str.h"
+
+namespace mongo {
+namespace mozjs {
+
+const JSFunctionSpec BinDataInfo::methods[4] = {
+ MONGO_ATTACH_JS_FUNCTION(base64),
+ MONGO_ATTACH_JS_FUNCTION(hex),
+ MONGO_ATTACH_JS_FUNCTION(toString),
+ JS_FS_END,
+};
+
+const JSFunctionSpec BinDataInfo::freeFunctions[4] = {
+ MONGO_ATTACH_JS_FUNCTION_WITH_FLAGS(HexData, JSFUN_CONSTRUCTOR),
+ MONGO_ATTACH_JS_FUNCTION_WITH_FLAGS(MD5, JSFUN_CONSTRUCTOR),
+ MONGO_ATTACH_JS_FUNCTION_WITH_FLAGS(UUID, JSFUN_CONSTRUCTOR),
+ JS_FS_END,
+};
+
+const char* const BinDataInfo::className = "BinData";
+
+namespace {
+
+void hexToBinData(JSContext* cx, int type, StringData hexstr, JS::MutableHandleValue out) {
+ auto scope = getScope(cx);
+
+ // SERVER-9686: This function does not correctly check to make sure hexstr is actually made
+ // up of valid hex digits, and fails in the hex utility functions
+
+ int len = hexstr.size() / 2;
+ std::unique_ptr<char[]> data(new char[len]);
+ const char* src = hexstr.rawData();
+ for (int i = 0; i < len; i++) {
+ data[i] = fromHex(src + i * 2);
+ }
+
+ std::string encoded = base64::encode(data.get(), len);
+ JS::AutoValueArray<2> args(cx);
+
+ args[0].setInt32(type);
+ ValueReader(cx, args[1]).fromStringData(encoded);
+ return scope->getBinDataProto().newInstance(args, out);
+}
+
+std::string* getEncoded(JS::HandleValue thisv) {
+ return static_cast<std::string*>(JS_GetPrivate(thisv.toObjectOrNull()));
+}
+
+std::string* getEncoded(JSObject* thisv) {
+ return static_cast<std::string*>(JS_GetPrivate(thisv));
+}
+
+} // namespace
+
+void BinDataInfo::finalize(JSFreeOp* fop, JSObject* obj) {
+ auto str = getEncoded(obj);
+
+ if (str) {
+ delete str;
+ }
+}
+
+void BinDataInfo::Functions::UUID(JSContext* cx, JS::CallArgs args) {
+ if (args.length() != 1)
+ uasserted(ErrorCodes::BadValue, "UUID needs 1 argument");
+
+ auto str = ValueWriter(cx, args.get(0)).toString();
+
+ if (str.length() != 32)
+ uasserted(ErrorCodes::BadValue, "UUID string must have 32 characters");
+
+ hexToBinData(cx, bdtUUID, str, args.rval());
+}
+
+void BinDataInfo::Functions::MD5(JSContext* cx, JS::CallArgs args) {
+ if (args.length() != 1)
+ uasserted(ErrorCodes::BadValue, "MD5 needs 1 argument");
+
+ auto str = ValueWriter(cx, args.get(0)).toString();
+
+ if (str.length() != 32)
+ uasserted(ErrorCodes::BadValue, "MD5 string must have 32 characters");
+
+ hexToBinData(cx, MD5Type, str, args.rval());
+}
+
+void BinDataInfo::Functions::HexData(JSContext* cx, JS::CallArgs args) {
+ if (args.length() != 2)
+ uasserted(ErrorCodes::BadValue, "HexData needs 2 arguments");
+
+ JS::RootedValue type(cx, args.get(0));
+
+ if (!type.isNumber() || type.toInt32() < 0 || type.toInt32() > 255)
+ uasserted(ErrorCodes::BadValue,
+ "HexData subtype must be a Number between 0 and 255 inclusive");
+
+ auto str = ValueWriter(cx, args.get(1)).toString();
+
+ hexToBinData(cx, type.toInt32(), str, args.rval());
+}
+
+void BinDataInfo::Functions::toString(JSContext* cx, JS::CallArgs args) {
+ ObjectWrapper o(cx, args.thisv());
+
+ auto str = getEncoded(args.thisv());
+
+ str::stream ss;
+
+ ss << "BinData(" << o.getNumber("type") << ",\"" << *str << "\")";
+
+ ValueReader(cx, args.rval()).fromStringData(ss.operator std::string());
+}
+
+void BinDataInfo::Functions::base64(JSContext* cx, JS::CallArgs args) {
+ auto str = getEncoded(args.thisv());
+
+ ValueReader(cx, args.rval()).fromStringData(*str);
+}
+
+void BinDataInfo::Functions::hex(JSContext* cx, JS::CallArgs args) {
+ auto str = getEncoded(args.thisv());
+
+ std::string data = base64::decode(*str);
+ std::stringstream ss;
+ ss.setf(std::ios_base::hex, std::ios_base::basefield);
+ ss.fill('0');
+ ss.setf(std::ios_base::right, std::ios_base::adjustfield);
+ for (auto it = data.begin(); it != data.end(); ++it) {
+ unsigned v = (unsigned char)*it;
+ ss << std::setw(2) << v;
+ }
+
+ ValueReader(cx, args.rval()).fromStringData(ss.str());
+}
+
+void BinDataInfo::construct(JSContext* cx, JS::CallArgs args) {
+ auto scope = getScope(cx);
+
+ if (args.length() != 2) {
+ uasserted(ErrorCodes::BadValue, "BinData takes 2 arguments -- BinData(subtype,data)");
+ }
+
+ auto type = args.get(0);
+
+ if (!type.isNumber() || type.toInt32() < 0 || type.toInt32() > 255) {
+ uasserted(ErrorCodes::BadValue,
+ "BinData subtype must be a Number between 0 and 255 inclusive");
+ }
+
+ auto utf = args.get(1);
+
+ if (!utf.isString()) {
+ uasserted(ErrorCodes::BadValue, "BinData data must be a String");
+ }
+
+ auto str = ValueWriter(cx, utf).toString();
+
+ auto tmpBase64 = base64::decode(str);
+
+ JS::RootedObject thisv(cx);
+ scope->getBinDataProto().newObject(&thisv);
+ ObjectWrapper o(cx, thisv);
+
+ JS::RootedValue len(cx);
+ len.setInt32(tmpBase64.length());
+
+ o.defineProperty("len", len, JSPROP_READONLY);
+ o.defineProperty("type", type, JSPROP_READONLY);
+
+ JS_SetPrivate(thisv, new std::string(std::move(str)));
+
+ args.rval().setObjectOrNull(thisv);
+}
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/bindata.h b/src/mongo/scripting/mozjs/bindata.h
new file mode 100644
index 00000000000..85b504230ff
--- /dev/null
+++ b/src/mongo/scripting/mozjs/bindata.h
@@ -0,0 +1,63 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#pragma once
+
+#include "mongo/scripting/mozjs/wraptype.h"
+
+namespace mongo {
+namespace mozjs {
+
+/**
+ * Wrapper for the BinData bson type
+ *
+ * It offers some simple methods and a handful of specialized constructors
+ */
+struct BinDataInfo : public BaseInfo {
+ static void construct(JSContext* cx, JS::CallArgs args);
+ static void finalize(JSFreeOp* fop, JSObject* obj);
+
+ struct Functions {
+ MONGO_DEFINE_JS_FUNCTION(base64);
+ MONGO_DEFINE_JS_FUNCTION(hex);
+ MONGO_DEFINE_JS_FUNCTION(toString);
+
+ MONGO_DEFINE_JS_FUNCTION(HexData);
+ MONGO_DEFINE_JS_FUNCTION(MD5);
+ MONGO_DEFINE_JS_FUNCTION(UUID);
+ };
+
+ static const JSFunctionSpec methods[4];
+ static const JSFunctionSpec freeFunctions[4];
+
+ static const char* const className;
+ static const unsigned classFlags = JSCLASS_HAS_PRIVATE;
+};
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/bson.cpp b/src/mongo/scripting/mozjs/bson.cpp
new file mode 100644
index 00000000000..308e29d90f3
--- /dev/null
+++ b/src/mongo/scripting/mozjs/bson.cpp
@@ -0,0 +1,235 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/scripting/mozjs/bson.h"
+
+#include <set>
+
+#include "mongo/scripting/mozjs/idwrapper.h"
+#include "mongo/scripting/mozjs/implscope.h"
+#include "mongo/scripting/mozjs/objectwrapper.h"
+#include "mongo/scripting/mozjs/valuereader.h"
+#include "mongo/scripting/mozjs/valuewriter.h"
+
+namespace mongo {
+namespace mozjs {
+
+const char* const BSONInfo::className = "BSON";
+
+const JSFunctionSpec BSONInfo::freeFunctions[2] = {
+ MONGO_ATTACH_JS_FUNCTION(bsonWoCompare), JS_FS_END,
+};
+
+namespace {
+
+/**
+ * Holder for bson objects which tracks state for the js wrapper
+ *
+ * Basically, we have read only and read/write variants, and a need to manage
+ * the appearance of mutable state on the read/write versions.
+ */
+struct BSONHolder {
+ BSONHolder(const BSONObj& obj, bool ro)
+ : _obj(obj.getOwned()), _resolved(false), _readOnly(ro), _altered(false) {}
+
+ BSONObj _obj;
+ bool _resolved;
+ bool _readOnly;
+ bool _altered;
+ std::set<std::string> _removed;
+};
+
+BSONHolder* getHolder(JSObject* obj) {
+ return static_cast<BSONHolder*>(JS_GetPrivate(obj));
+}
+
+} // namespace
+
+void BSONInfo::make(JSContext* cx, JS::MutableHandleObject obj, BSONObj bson, bool ro) {
+ auto scope = getScope(cx);
+
+ scope->getBsonProto().newInstance(obj);
+ JS_SetPrivate(obj, new BSONHolder(bson, ro));
+}
+
+void BSONInfo::finalize(JSFreeOp* fop, JSObject* obj) {
+ auto holder = getHolder(obj);
+
+ if (!holder)
+ return;
+
+ delete holder;
+}
+
+void BSONInfo::enumerate(JSContext* cx, JS::HandleObject obj, JS::AutoIdVector& properties) {
+ auto holder = getHolder(obj);
+
+ if (!holder)
+ return;
+
+ BSONObjIterator i(holder->_obj);
+
+ ObjectWrapper o(cx, obj);
+ JS::RootedValue val(cx);
+ JS::RootedId id(cx);
+
+ while (i.more()) {
+ BSONElement e = i.next();
+
+ // TODO: when we get heterogenous set lookup, switch to StringData
+ // rather than involving the temporary string
+ if (holder->_removed.count(e.fieldName()))
+ continue;
+
+ ValueReader(cx, &val).fromStringData(e.fieldNameStringData());
+
+ if (!JS_ValueToId(cx, val, &id))
+ uasserted(ErrorCodes::JSInterpreterFailure, "Failed to invoke JS_ValueToId");
+
+ properties.append(id);
+ }
+}
+
+void BSONInfo::setProperty(
+ JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool strict, JS::MutableHandleValue vp) {
+ auto holder = getHolder(obj);
+
+ if (holder) {
+ if (holder->_readOnly) {
+ uasserted(ErrorCodes::BadValue, "Read only object");
+ }
+
+ auto iter = holder->_removed.find(IdWrapper(cx, id).toString());
+
+ if (iter != holder->_removed.end()) {
+ holder->_removed.erase(iter);
+ }
+
+ holder->_altered = true;
+ }
+
+ ObjectWrapper(cx, obj).defineProperty(id, vp, JSPROP_ENUMERATE);
+}
+
+void BSONInfo::delProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* succeeded) {
+ auto holder = getHolder(obj);
+
+ if (holder) {
+ if (holder->_readOnly) {
+ uasserted(ErrorCodes::BadValue, "Read only object");
+ }
+
+ holder->_altered = true;
+
+ holder->_removed.insert(IdWrapper(cx, id).toString());
+ }
+
+ *succeeded = true;
+}
+
+void BSONInfo::resolve(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* resolvedp) {
+ auto holder = getHolder(obj);
+
+ *resolvedp = false;
+
+ if (!holder) {
+ return;
+ }
+
+ IdWrapper idw(cx, id);
+
+ if (!holder->_readOnly && holder->_removed.count(idw.toString())) {
+ return;
+ }
+
+ ObjectWrapper o(cx, obj);
+
+ std::string sname = IdWrapper(cx, id).toString();
+
+ if (holder->_obj.hasField(sname)) {
+ auto elem = holder->_obj[sname];
+
+ JS::RootedValue vp(cx);
+
+ ValueReader(cx, &vp).fromBSONElement(elem, holder->_readOnly);
+
+ o.defineProperty(id, vp, JSPROP_ENUMERATE);
+
+ if (!holder->_readOnly && (elem.type() == mongo::Object || elem.type() == mongo::Array)) {
+ // if accessing a subobject, we have no way to know if
+ // modifications are being made on writable objects
+
+ holder->_altered = true;
+ }
+
+ *resolvedp = true;
+ }
+}
+
+void BSONInfo::construct(JSContext* cx, JS::CallArgs args) {
+ auto scope = getScope(cx);
+
+ scope->getBsonProto().newObject(args.rval());
+}
+
+std::tuple<BSONObj*, bool> BSONInfo::originalBSON(JSContext* cx, JS::HandleObject obj) {
+ std::tuple<BSONObj*, bool> out(nullptr, false);
+
+ if (auto holder = getHolder(obj))
+ out = std::make_tuple(&holder->_obj, holder->_altered);
+
+ return out;
+}
+
+void BSONInfo::Functions::bsonWoCompare(JSContext* cx, JS::CallArgs args) {
+ if (args.length() != 2)
+ uasserted(ErrorCodes::BadValue, "bsonWoCompare needs 2 argument");
+
+ if (!args.get(0).isObject())
+ uasserted(ErrorCodes::BadValue, "first argument to bsonWoCompare must be an object");
+
+ if (!args.get(1).isObject())
+ uasserted(ErrorCodes::BadValue, "second argument to bsonWoCompare must be an object");
+
+ BSONObj firstObject = ValueWriter(cx, args.get(0)).toBSON();
+ BSONObj secondObject = ValueWriter(cx, args.get(1)).toBSON();
+
+ args.rval().setInt32(firstObject.woCompare(secondObject));
+}
+
+void BSONInfo::postInstall(JSContext* cx, JS::HandleObject global, JS::HandleObject proto) {
+ JS::RootedValue value(cx);
+ value.setBoolean(true);
+
+ ObjectWrapper(cx, proto).defineProperty("_bson", value, 0);
+}
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/bson.h b/src/mongo/scripting/mozjs/bson.h
new file mode 100644
index 00000000000..bcf57b5eda4
--- /dev/null
+++ b/src/mongo/scripting/mozjs/bson.h
@@ -0,0 +1,77 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#pragma once
+
+#include <tuple>
+
+#include "mongo/db/jsobj.h"
+#include "mongo/scripting/mozjs/wraptype.h"
+
+namespace mongo {
+namespace mozjs {
+
+/**
+ * Provides a wrapper for BSONObj's in JS. The main idea here is that BSONObj's
+ * can be read only, or read write when we shim them in, and in all cases we
+ * lazily load their member elements into JS. So a bunch of these lifecycle
+ * methods are set up to wrap field access (enumerate, resolve and
+ * del/setProperty).
+ *
+ * Note that installType is private. So you can only get BSON types in JS via
+ * ::make() from C++.
+ */
+struct BSONInfo : public BaseInfo {
+ static void construct(JSContext* cx, JS::CallArgs args);
+ static void delProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* succeeded);
+ static void enumerate(JSContext* cx, JS::HandleObject obj, JS::AutoIdVector& properties);
+ static void finalize(JSFreeOp* fop, JSObject* obj);
+ static void resolve(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* resolvedp);
+ static void setProperty(JSContext* cx,
+ JS::HandleObject obj,
+ JS::HandleId id,
+ bool strict,
+ JS::MutableHandleValue vp);
+
+ static const char* const className;
+ static const unsigned classFlags = JSCLASS_HAS_PRIVATE;
+ static const InstallType installType = InstallType::Private;
+ static void postInstall(JSContext* cx, JS::HandleObject global, JS::HandleObject proto);
+
+ struct Functions {
+ MONGO_DEFINE_JS_FUNCTION(bsonWoCompare);
+ };
+
+ static const JSFunctionSpec freeFunctions[2];
+
+ static std::tuple<BSONObj*, bool> originalBSON(JSContext* cx, JS::HandleObject obj);
+ static void make(JSContext* cx, JS::MutableHandleObject obj, BSONObj bson, bool ro);
+};
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/countdownlatch.cpp b/src/mongo/scripting/mozjs/countdownlatch.cpp
new file mode 100644
index 00000000000..bf7ae4ff551
--- /dev/null
+++ b/src/mongo/scripting/mozjs/countdownlatch.cpp
@@ -0,0 +1,198 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/scripting/mozjs/countdownlatch.h"
+
+#include <unordered_map>
+
+#include "mongo/scripting/mozjs/implscope.h"
+#include "mongo/scripting/mozjs/objectwrapper.h"
+#include "mongo/stdx/condition_variable.h"
+#include "mongo/stdx/mutex.h"
+
+namespace mongo {
+namespace mozjs {
+
+const char* const CountDownLatchInfo::className = "CountDownLatch";
+
+const JSFunctionSpec CountDownLatchInfo::methods[5] = {
+ MONGO_ATTACH_JS_FUNCTION(_new),
+ MONGO_ATTACH_JS_FUNCTION(_await),
+ MONGO_ATTACH_JS_FUNCTION(_countDown),
+ MONGO_ATTACH_JS_FUNCTION(_getCount),
+ JS_FS_END,
+};
+
+/**
+ * The global CountDownLatch holder.
+ *
+ * Provides an interface for communicating between JSThread's
+ */
+class CountDownLatchHolder {
+public:
+ CountDownLatchHolder() : _counter(0) {}
+
+ int32_t make(int32_t count) {
+ uassert(ErrorCodes::JSInterpreterFailure, "argument must be >= 0", count >= 0);
+ stdx::lock_guard<stdx::mutex> lock(_mutex);
+
+ int32_t desc = ++_counter;
+ _latches.insert(std::make_pair(desc, std::make_shared<Latch>(count)));
+
+ return desc;
+ }
+
+ void await(int32_t desc) {
+ std::shared_ptr<Latch> latch = get(desc);
+ stdx::unique_lock<stdx::mutex> lock(latch->mutex);
+
+ while (latch->count != 0) {
+ latch->cv.wait(lock);
+ }
+ }
+
+ void countDown(int32_t desc) {
+ std::shared_ptr<Latch> latch = get(desc);
+ stdx::unique_lock<stdx::mutex> lock(latch->mutex);
+
+ if (latch->count > 0)
+ latch->count--;
+
+ if (latch->count == 0)
+ latch->cv.notify_all();
+ }
+
+ int32_t getCount(int32_t desc) {
+ std::shared_ptr<Latch> latch = get(desc);
+ stdx::unique_lock<stdx::mutex> lock(latch->mutex);
+
+ return latch->count;
+ }
+
+private:
+ /**
+ * Latches for communication between threads
+ */
+ struct Latch {
+ Latch(int32_t count) : count(count) {}
+
+ stdx::mutex mutex;
+ stdx::condition_variable cv;
+ int32_t count;
+ };
+
+ std::shared_ptr<Latch> get(int32_t desc) {
+ stdx::lock_guard<stdx::mutex> lock(_mutex);
+
+ auto iter = _latches.find(desc);
+ uassert(ErrorCodes::JSInterpreterFailure,
+ "not a valid CountDownLatch descriptor",
+ iter != _latches.end());
+
+ return iter->second;
+ }
+
+ using Map = std::unordered_map<int32_t, std::shared_ptr<Latch>>;
+
+ stdx::mutex _mutex;
+ Map _latches;
+ int32_t _counter;
+};
+
+namespace {
+CountDownLatchHolder globalCountDownLatchHolder;
+} // namespace
+
+void CountDownLatchInfo::Functions::_new(JSContext* cx, JS::CallArgs args) {
+ uassert(ErrorCodes::JSInterpreterFailure, "need exactly one argument", args.length() == 1);
+ uassert(
+ ErrorCodes::JSInterpreterFailure, "argument must be an integer", args.get(0).isNumber());
+
+ args.rval().setInt32(globalCountDownLatchHolder.make(args.get(0).toNumber()));
+}
+
+void CountDownLatchInfo::Functions::_await(JSContext* cx, JS::CallArgs args) {
+ uassert(ErrorCodes::JSInterpreterFailure, "need exactly one argument", args.length() == 1);
+ uassert(
+ ErrorCodes::JSInterpreterFailure, "argument must be an integer", args.get(0).isNumber());
+
+ globalCountDownLatchHolder.await(args.get(0).toNumber());
+
+ args.rval().setUndefined();
+}
+
+void CountDownLatchInfo::Functions::_countDown(JSContext* cx, JS::CallArgs args) {
+ uassert(ErrorCodes::JSInterpreterFailure, "need exactly one argument", args.length() == 1);
+ uassert(
+ ErrorCodes::JSInterpreterFailure, "argument must be an integer", args.get(0).isNumber());
+
+ globalCountDownLatchHolder.countDown(args.get(0).toNumber());
+
+ args.rval().setUndefined();
+}
+
+void CountDownLatchInfo::Functions::_getCount(JSContext* cx, JS::CallArgs args) {
+ uassert(ErrorCodes::JSInterpreterFailure, "need exactly one argument", args.length() == 1);
+ uassert(
+ ErrorCodes::JSInterpreterFailure, "argument must be an integer", args.get(0).isNumber());
+
+ args.rval().setInt32(globalCountDownLatchHolder.getCount(args.get(0).toNumber()));
+}
+
+/**
+ * We have to do this odd dance here because we need the methods from
+ * CountDownLatch to be installed in a plain object as enumerable properties.
+ * This is due to the way CountDownLatch is invoked, specifically after being
+ * transmitted across our js fork(). So we can't inherit and can't rely on the
+ * type. Practically, we also end up wrapping up all of these functions in pure
+ * js variants that call down, which makes them bson <-> js safe.
+ */
+void CountDownLatchInfo::postInstall(JSContext* cx,
+ JS::HandleObject global,
+ JS::HandleObject proto) {
+ auto objPtr = JS_NewPlainObject(cx);
+ uassert(ErrorCodes::JSInterpreterFailure, "Failed to JS_NewPlainObject", objPtr);
+
+ JS::RootedObject obj(cx, objPtr);
+ ObjectWrapper objWrapper(cx, obj);
+ ObjectWrapper protoWrapper(cx, proto);
+
+ JS::RootedValue val(cx);
+ for (auto iter = methods; iter->name; ++iter) {
+ protoWrapper.getValue(iter->name, &val);
+ objWrapper.setValue(iter->name, val);
+ }
+
+ val.setObjectOrNull(obj);
+ ObjectWrapper(cx, global).setValue("CountDownLatch", val);
+}
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/countdownlatch.h b/src/mongo/scripting/mozjs/countdownlatch.h
new file mode 100644
index 00000000000..07001ebf473
--- /dev/null
+++ b/src/mongo/scripting/mozjs/countdownlatch.h
@@ -0,0 +1,61 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#pragma once
+
+#include "mongo/scripting/mozjs/wraptype.h"
+
+namespace mongo {
+namespace mozjs {
+
+/**
+ * The "CountDownLatch" javascript object.
+ *
+ * Installs a global "CountDownLatch" object with associated methods.
+ *
+ * Note that there is only one instance of this class and it is used to
+ * communicate between different C++ threads.
+ */
+struct CountDownLatchInfo : public BaseInfo {
+ struct Functions {
+ MONGO_DEFINE_JS_FUNCTION(_new);
+ MONGO_DEFINE_JS_FUNCTION(_await);
+ MONGO_DEFINE_JS_FUNCTION(_countDown);
+ MONGO_DEFINE_JS_FUNCTION(_getCount);
+ };
+
+ static const JSFunctionSpec methods[5];
+
+ static const char* const className;
+ static const InstallType installType = InstallType::Private;
+
+ static void postInstall(JSContext* cx, JS::HandleObject global, JS::HandleObject proto);
+};
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/cursor.cpp b/src/mongo/scripting/mozjs/cursor.cpp
new file mode 100644
index 00000000000..8f6bb7b540c
--- /dev/null
+++ b/src/mongo/scripting/mozjs/cursor.cpp
@@ -0,0 +1,122 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/scripting/mozjs/cursor.h"
+
+#include "mongo/scripting/mozjs/bson.h"
+#include "mongo/scripting/mozjs/implscope.h"
+#include "mongo/scripting/mozjs/objectwrapper.h"
+#include "mongo/scripting/mozjs/valuereader.h"
+
+namespace mongo {
+namespace mozjs {
+
+const JSFunctionSpec CursorInfo::methods[5] = {
+ MONGO_ATTACH_JS_FUNCTION(hasNext),
+ MONGO_ATTACH_JS_FUNCTION(next),
+ MONGO_ATTACH_JS_FUNCTION(objsLeftInBatch),
+ MONGO_ATTACH_JS_FUNCTION(readOnly),
+ JS_FS_END,
+};
+
+const char* const CursorInfo::className = "Cursor";
+
+namespace {
+
+DBClientCursor* getCursor(JSObject* thisv) {
+ return static_cast<CursorInfo::CursorHolder*>(JS_GetPrivate(thisv))->cursor.get();
+}
+
+DBClientCursor* getCursor(JS::CallArgs& args) {
+ return getCursor(args.thisv().toObjectOrNull());
+}
+
+} // namespace
+
+void CursorInfo::finalize(JSFreeOp* fop, JSObject* obj) {
+ auto cursor = static_cast<CursorInfo::CursorHolder*>(JS_GetPrivate(obj));
+
+ if (cursor) {
+ delete cursor;
+ }
+}
+
+void CursorInfo::construct(JSContext* cx, JS::CallArgs args) {
+ auto scope = getScope(cx);
+
+ scope->getCursorProto().newObject(args.rval());
+}
+
+void CursorInfo::Functions::next(JSContext* cx, JS::CallArgs args) {
+ auto cursor = getCursor(args);
+
+ if (!cursor) {
+ args.rval().setUndefined();
+ return;
+ }
+
+ ObjectWrapper o(cx, args.thisv());
+
+ BSONObj bson = cursor->next();
+ bool ro = o.hasField("_ro") ? o.getBoolean("_ro") : false;
+
+ ValueReader(cx, args.rval()).fromBSON(bson, ro);
+}
+
+void CursorInfo::Functions::hasNext(JSContext* cx, JS::CallArgs args) {
+ auto cursor = getCursor(args);
+
+ if (!cursor) {
+ args.rval().setBoolean(false);
+ return;
+ }
+
+ args.rval().setBoolean(cursor->more());
+}
+
+void CursorInfo::Functions::objsLeftInBatch(JSContext* cx, JS::CallArgs args) {
+ auto cursor = getCursor(args);
+
+ if (!cursor) {
+ args.rval().setInt32(0);
+ return;
+ }
+
+ args.rval().setInt32(cursor->objsLeftInBatch());
+}
+
+void CursorInfo::Functions::readOnly(JSContext* cx, JS::CallArgs args) {
+ ObjectWrapper(cx, args.thisv()).setBoolean("_ro", true);
+
+ args.rval().set(args.thisv());
+}
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/cursor.h b/src/mongo/scripting/mozjs/cursor.h
new file mode 100644
index 00000000000..39c8ba56295
--- /dev/null
+++ b/src/mongo/scripting/mozjs/cursor.h
@@ -0,0 +1,76 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#pragma once
+
+#include "mongo/client/dbclientcursor.h"
+#include "mongo/client/dbclientinterface.h"
+#include "mongo/scripting/mozjs/wraptype.h"
+
+namespace mongo {
+namespace mozjs {
+
+/**
+ * Wraps a DBClientCursor in javascript
+ *
+ * Note that the install is private, so this class should only be constructible
+ * from C++. Current callers are all via the Mongo object.
+ */
+struct CursorInfo : public BaseInfo {
+ static void construct(JSContext* cx, JS::CallArgs args);
+ static void finalize(JSFreeOp* fop, JSObject* obj);
+
+ struct Functions {
+ MONGO_DEFINE_JS_FUNCTION(hasNext);
+ MONGO_DEFINE_JS_FUNCTION(next);
+ MONGO_DEFINE_JS_FUNCTION(objsLeftInBatch);
+ MONGO_DEFINE_JS_FUNCTION(readOnly);
+ };
+
+ static const JSFunctionSpec methods[5];
+
+ static const char* const className;
+ static const unsigned classFlags = JSCLASS_HAS_PRIVATE;
+ static const InstallType installType = InstallType::Private;
+
+ /**
+ * We need this because the DBClientBase can go out of scope before all of
+ * its children (as in global shutdown). So we have to manage object
+ * lifetimes in C++ land.
+ */
+ struct CursorHolder {
+ CursorHolder(std::unique_ptr<DBClientCursor> cursor, std::shared_ptr<DBClientBase> client)
+ : client(std::move(client)), cursor(std::move(cursor)) {}
+
+ std::shared_ptr<DBClientBase> client;
+ std::unique_ptr<DBClientCursor> cursor;
+ };
+};
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/db.cpp b/src/mongo/scripting/mozjs/db.cpp
new file mode 100644
index 00000000000..da0197cc293
--- /dev/null
+++ b/src/mongo/scripting/mozjs/db.cpp
@@ -0,0 +1,139 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/scripting/mozjs/db.h"
+
+#include "mongo/db/namespace_string.h"
+#include "mongo/db/operation_context.h"
+#include "mongo/scripting/mozjs/idwrapper.h"
+#include "mongo/scripting/mozjs/implscope.h"
+#include "mongo/scripting/mozjs/objectwrapper.h"
+#include "mongo/scripting/mozjs/valuereader.h"
+#include "mongo/scripting/mozjs/valuewriter.h"
+#include "mongo/s/d_state.h"
+
+namespace mongo {
+namespace mozjs {
+
+const char* const DBInfo::className = "DB";
+
+void DBInfo::getProperty(JSContext* cx,
+ JS::HandleObject obj,
+ JS::HandleId id,
+ JS::MutableHandleValue vp) {
+ JS::RootedObject parent(cx);
+ if (!JS_GetPrototype(cx, obj, &parent))
+ uasserted(ErrorCodes::JSInterpreterFailure, "Couldn't get prototype");
+
+ auto scope = getScope(cx);
+
+ ObjectWrapper parentWrapper(cx, parent);
+
+ std::string sname = IdWrapper(cx, id).toString();
+
+ // 2nd look into real values, may be cached collection object
+ if (!vp.isUndefined()) {
+ if (vp.isObject()) {
+ ObjectWrapper o(cx, vp);
+
+ if (o.hasField("_fullName")) {
+ auto opContext = scope->getOpContext();
+
+ // need to check every time that the collection did not get sharded
+ if (opContext &&
+ haveLocalShardingInfo(opContext->getClient(), o.getString("_fullName")))
+ uasserted(ErrorCodes::BadValue, "can't use sharded collection from db.eval");
+ }
+ }
+
+ return;
+ } else if (parentWrapper.hasField(id)) {
+ parentWrapper.getValue(id, vp);
+ return;
+ } else if (sname.length() == 0 || sname[0] == '_') {
+ // if starts with '_' we dont return collection, one must use getCollection()
+ return;
+ }
+
+ // no hit, create new collection
+ JS::RootedValue getCollection(cx);
+ parentWrapper.getValue("getCollection", &getCollection);
+
+ if (!(getCollection.isObject() && JS_ObjectIsFunction(cx, getCollection.toObjectOrNull()))) {
+ uasserted(ErrorCodes::BadValue, "getCollection is not a function");
+ }
+
+ JS::AutoValueArray<1> args(cx);
+
+ ValueReader(cx, args[0]).fromStringData(sname);
+
+ JS::RootedValue coll(cx);
+ ObjectWrapper(cx, obj).callMethod(getCollection, args, &coll);
+
+ uassert(16861,
+ "getCollection returned something other than a collection",
+ scope->getDbCollectionProto().instanceOf(coll));
+
+ // cache collection for reuse, don't enumerate
+ ObjectWrapper(cx, obj).defineProperty(sname.c_str(), coll, 0);
+
+ vp.set(coll);
+}
+
+void DBInfo::construct(JSContext* cx, JS::CallArgs args) {
+ auto scope = getScope(cx);
+
+ if (args.length() != 2)
+ uasserted(ErrorCodes::BadValue, "db constructor requires 2 arguments");
+
+ for (unsigned i = 0; i < args.length(); ++i) {
+ uassert(ErrorCodes::BadValue,
+ "db initializer called with undefined argument",
+ !args.get(i).isUndefined());
+ }
+
+ JS::RootedObject thisv(cx);
+ scope->getDbProto().newObject(&thisv);
+ ObjectWrapper o(cx, thisv);
+
+ o.setValue("_mongo", args.get(0));
+ o.setValue("_name", args.get(1));
+
+ std::string dbName = ValueWriter(cx, args.get(1)).toString();
+
+ if (!NamespaceString::validDBName(dbName))
+ uasserted(ErrorCodes::BadValue,
+ str::stream() << "[" << dbName << "] is not a valid database name");
+
+ args.rval().setObjectOrNull(thisv);
+}
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/db.h b/src/mongo/scripting/mozjs/db.h
new file mode 100644
index 00000000000..c752953236f
--- /dev/null
+++ b/src/mongo/scripting/mozjs/db.h
@@ -0,0 +1,56 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#pragma once
+
+#include "mongo/scripting/mozjs/wraptype.h"
+
+namespace mongo {
+namespace mozjs {
+
+/**
+ * The "DB" Javascript object.
+ *
+ * This maps to the 'db' global variable you can call db.COLLECTION_NAME.X() on
+ * in the shell.
+ *
+ * Its major magic is in its getProperty() callback, which threads through to
+ * a getCollection method installed in js
+ */
+struct DBInfo : public BaseInfo {
+ static void construct(JSContext* cx, JS::CallArgs args);
+ static void getProperty(JSContext* cx,
+ JS::HandleObject obj,
+ JS::HandleId id,
+ JS::MutableHandleValue vp);
+
+ static const char* const className;
+};
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/dbcollection.cpp b/src/mongo/scripting/mozjs/dbcollection.cpp
new file mode 100644
index 00000000000..fd8bc086676
--- /dev/null
+++ b/src/mongo/scripting/mozjs/dbcollection.cpp
@@ -0,0 +1,85 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/scripting/mozjs/dbcollection.h"
+
+#include "mongo/db/namespace_string.h"
+#include "mongo/db/operation_context.h"
+#include "mongo/scripting/mozjs/bson.h"
+#include "mongo/scripting/mozjs/db.h"
+#include "mongo/scripting/mozjs/implscope.h"
+#include "mongo/scripting/mozjs/objectwrapper.h"
+#include "mongo/scripting/mozjs/valuewriter.h"
+#include "mongo/s/d_state.h"
+
+namespace mongo {
+namespace mozjs {
+
+const char* const DBCollectionInfo::className = "DBCollection";
+
+void DBCollectionInfo::getProperty(JSContext* cx,
+ JS::HandleObject obj,
+ JS::HandleId id,
+ JS::MutableHandleValue vp) {
+ DBInfo::getProperty(cx, obj, id, vp);
+}
+
+void DBCollectionInfo::construct(JSContext* cx, JS::CallArgs args) {
+ auto scope = getScope(cx);
+
+ if (args.length() != 4)
+ uasserted(ErrorCodes::BadValue, "collection constructor requires 4 arguments");
+
+ for (unsigned i = 0; i < args.length(); ++i) {
+ uassert(ErrorCodes::BadValue,
+ "collection constructor called with undefined argument",
+ !args.get(i).isUndefined());
+ }
+
+ JS::RootedObject thisv(cx);
+ scope->getDbCollectionProto().newObject(&thisv);
+ ObjectWrapper o(cx, thisv);
+
+ o.setValue("_mongo", args.get(0));
+ o.setValue("_db", args.get(1));
+ o.setValue("_shortName", args.get(2));
+ o.setValue("_fullName", args.get(3));
+
+ std::string fullName = ValueWriter(cx, args.get(3)).toString();
+
+ auto context = scope->getOpContext();
+ if (context && haveLocalShardingInfo(context->getClient(), fullName))
+ uasserted(ErrorCodes::BadValue, "can't use sharded collection from db.eval");
+
+ args.rval().setObjectOrNull(thisv);
+}
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/dbcollection.h b/src/mongo/scripting/mozjs/dbcollection.h
new file mode 100644
index 00000000000..34be422dfd4
--- /dev/null
+++ b/src/mongo/scripting/mozjs/dbcollection.h
@@ -0,0 +1,56 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#pragma once
+
+#include "mongo/scripting/mozjs/wraptype.h"
+
+namespace mongo {
+namespace mozjs {
+
+/**
+ * The "DBCollection" Javascript object.
+ *
+ * This maps to the object you get after calling db.COLLECTION_NAME on the
+ * global 'db' object in the shell.
+ *
+ * Its major magic is in its getProperty() callback, which threads through to
+ * a getCollection method installed in js
+ */
+struct DBCollectionInfo : public BaseInfo {
+ static void construct(JSContext* cx, JS::CallArgs args);
+ static void getProperty(JSContext* cx,
+ JS::HandleObject obj,
+ JS::HandleId id,
+ JS::MutableHandleValue vp);
+
+ static const char* const className;
+};
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/dbpointer.cpp b/src/mongo/scripting/mozjs/dbpointer.cpp
new file mode 100644
index 00000000000..fbfb8282649
--- /dev/null
+++ b/src/mongo/scripting/mozjs/dbpointer.cpp
@@ -0,0 +1,66 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/scripting/mozjs/dbpointer.h"
+
+#include "mongo/scripting/mozjs/implscope.h"
+#include "mongo/scripting/mozjs/objectwrapper.h"
+#include "mongo/scripting/mozjs/valuereader.h"
+#include "mongo/util/mongoutils/str.h"
+
+namespace mongo {
+namespace mozjs {
+
+const char* const DBPointerInfo::className = "DBPointer";
+
+void DBPointerInfo::construct(JSContext* cx, JS::CallArgs args) {
+ auto scope = getScope(cx);
+
+ if (args.length() != 2)
+ uasserted(ErrorCodes::BadValue, "DBPointer needs 2 arguments");
+
+ if (!args.get(0).isString())
+ uasserted(ErrorCodes::BadValue, "DBPointer 1st parameter must be a string");
+
+ if (!scope->getOidProto().instanceOf(args.get(1)))
+ uasserted(ErrorCodes::BadValue, "DBPointer 2nd parameter must be an ObjectId");
+
+ JS::RootedObject thisv(cx);
+ scope->getDbPointerProto().newObject(&thisv);
+ ObjectWrapper o(cx, thisv);
+
+ o.setValue("ns", args.get(0));
+ o.setValue("id", args.get(1));
+
+ args.rval().setObjectOrNull(thisv);
+}
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/dbpointer.h b/src/mongo/scripting/mozjs/dbpointer.h
new file mode 100644
index 00000000000..d521eda4681
--- /dev/null
+++ b/src/mongo/scripting/mozjs/dbpointer.h
@@ -0,0 +1,52 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#pragma once
+
+#include "mongo/scripting/mozjs/wraptype.h"
+
+namespace mongo {
+namespace mozjs {
+
+/**
+ * The "DBPointer" Javascript Object
+ *
+ * These look like:
+ * {
+ * id : OID(),
+ * ns : String(),
+ * }
+ */
+struct DBPointerInfo : public BaseInfo {
+ static void construct(JSContext* cx, JS::CallArgs args);
+
+ static const char* const className;
+};
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/dbquery.cpp b/src/mongo/scripting/mozjs/dbquery.cpp
new file mode 100644
index 00000000000..e9bddf4b30e
--- /dev/null
+++ b/src/mongo/scripting/mozjs/dbquery.cpp
@@ -0,0 +1,143 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/scripting/mozjs/dbquery.h"
+
+#include "mongo/scripting/mozjs/idwrapper.h"
+#include "mongo/scripting/mozjs/implscope.h"
+#include "mongo/scripting/mozjs/objectwrapper.h"
+
+namespace mongo {
+namespace mozjs {
+
+const char* const DBQueryInfo::className = "DBQuery";
+
+void DBQueryInfo::construct(JSContext* cx, JS::CallArgs args) {
+ auto scope = getScope(cx);
+
+ if (args.length() < 4)
+ uasserted(ErrorCodes::BadValue, "dbQuery constructor requires at least 4 arguments");
+
+ JS::RootedObject thisv(cx);
+ scope->getDbQueryProto().newObject(&thisv);
+ ObjectWrapper o(cx, thisv);
+
+ o.setValue("_mongo", args.get(0));
+ o.setValue("_db", args.get(1));
+ o.setValue("_collection", args.get(2));
+ o.setValue("_ns", args.get(3));
+
+ JS::RootedObject emptyObj(cx);
+ JS::RootedValue emptyObjVal(cx);
+ emptyObjVal.setObjectOrNull(emptyObj);
+
+ JS::RootedValue nullVal(cx);
+ nullVal.setNull();
+
+ if (args.length() > 4 && args.get(4).isObject()) {
+ o.setValue("_query", args.get(4));
+ } else {
+ o.setValue("_query", emptyObjVal);
+ }
+
+ if (args.length() > 5 && args.get(5).isObject()) {
+ o.setValue("_fields", args.get(5));
+ } else {
+ o.setValue("_fields", nullVal);
+ }
+
+ if (args.length() > 6 && args.get(6).isNumber()) {
+ o.setValue("_limit", args.get(6));
+ } else {
+ o.setNumber("_limit", 0);
+ }
+
+ if (args.length() > 7 && args.get(7).isNumber()) {
+ o.setValue("_skip", args.get(7));
+ } else {
+ o.setNumber("_skip", 0);
+ }
+
+ if (args.length() > 8 && args.get(8).isNumber()) {
+ o.setValue("_batchSize", args.get(8));
+ } else {
+ o.setNumber("_batchSize", 0);
+ }
+
+ if (args.length() > 9 && args.get(9).isNumber()) {
+ o.setValue("_options", args.get(9));
+ } else {
+ o.setNumber("_options", 0);
+ }
+
+ o.setValue("_cursor", nullVal);
+ o.setNumber("_numReturned", 0);
+ o.setBoolean("_special", false);
+
+ args.rval().setObjectOrNull(thisv);
+}
+
+void DBQueryInfo::getProperty(JSContext* cx,
+ JS::HandleObject obj,
+ JS::HandleId id,
+ JS::MutableHandleValue vp) {
+ if (!vp.isUndefined()) {
+ return;
+ }
+
+ IdWrapper wid(cx, id);
+
+ // We only use this for index access
+ if (!wid.isInt()) {
+ return;
+ }
+
+ JS::RootedObject parent(cx);
+ if (!JS_GetPrototype(cx, obj, &parent))
+ uasserted(ErrorCodes::InternalError, "Couldn't get prototype");
+
+ ObjectWrapper parentWrapper(cx, parent);
+
+ JS::RootedValue arrayAccess(cx);
+ parentWrapper.getValue("arrayAccess", &arrayAccess);
+
+ if (arrayAccess.isObject() && JS_ObjectIsFunction(cx, arrayAccess.toObjectOrNull())) {
+ JS::AutoValueArray<1> args(cx);
+
+ args[0].setInt32(wid.toInt32());
+
+ ObjectWrapper(cx, obj).callMethod(arrayAccess, args, vp);
+ } else {
+ uasserted(ErrorCodes::BadValue, "arrayAccess is not a function");
+ }
+}
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/dbquery.h b/src/mongo/scripting/mozjs/dbquery.h
new file mode 100644
index 00000000000..dc844c3084c
--- /dev/null
+++ b/src/mongo/scripting/mozjs/dbquery.h
@@ -0,0 +1,53 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#pragma once
+
+#include "mongo/scripting/mozjs/wraptype.h"
+
+namespace mongo {
+namespace mozjs {
+
+/**
+ * The "DBQuery" Javascript object.
+ *
+ * This represents the result of a find() and uses its getProperty() callback
+ * to shim operator[]. I.e. db.test.find()[4]
+ */
+struct DBQueryInfo : public BaseInfo {
+ static void construct(JSContext* cx, JS::CallArgs args);
+ static void getProperty(JSContext* cx,
+ JS::HandleObject obj,
+ JS::HandleId id,
+ JS::MutableHandleValue vp);
+
+ static const char* const className;
+};
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/dbref.cpp b/src/mongo/scripting/mozjs/dbref.cpp
new file mode 100644
index 00000000000..16ad0058084
--- /dev/null
+++ b/src/mongo/scripting/mozjs/dbref.cpp
@@ -0,0 +1,68 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/scripting/mozjs/dbref.h"
+
+#include "mongo/scripting/mozjs/implscope.h"
+#include "mongo/scripting/mozjs/objectwrapper.h"
+
+namespace mongo {
+namespace mozjs {
+
+const char* const DBRefInfo::className = "DBRef";
+
+void DBRefInfo::construct(JSContext* cx, JS::CallArgs args) {
+ auto scope = getScope(cx);
+
+ if (!(args.length() == 2 || args.length() == 3))
+ uasserted(ErrorCodes::BadValue, "DBRef needs 2 or 3 arguments");
+
+ if (!args.get(0).isString())
+ uasserted(ErrorCodes::BadValue, "DBRef 1st parameter must be a string");
+
+ JS::RootedObject thisv(cx);
+ scope->getDbRefProto().newObject(&thisv);
+ ObjectWrapper o(cx, thisv);
+
+ o.setValue("$ref", args.get(0));
+ o.setValue("$id", args.get(1));
+
+ if (args.length() == 3) {
+ if (!args.get(2).isString())
+ uasserted(ErrorCodes::BadValue, "DBRef 3rd parameter must be a string");
+
+ o.setValue("$db", args.get(2));
+ }
+
+ args.rval().setObjectOrNull(thisv);
+}
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/dbref.h b/src/mongo/scripting/mozjs/dbref.h
new file mode 100644
index 00000000000..e556a61f765
--- /dev/null
+++ b/src/mongo/scripting/mozjs/dbref.h
@@ -0,0 +1,53 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#pragma once
+
+#include "mongo/scripting/mozjs/wraptype.h"
+
+namespace mongo {
+namespace mozjs {
+
+/**
+ * The "DBRef" Javascript object.
+ *
+ * These look like:
+ * {
+ * $ref : String(),
+ * $id : Any,
+ * $db : String(),
+ * }
+ */
+struct DBRefInfo : public BaseInfo {
+ static void construct(JSContext* cx, JS::CallArgs args);
+
+ static const char* const className;
+};
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/engine.cpp b/src/mongo/scripting/mozjs/engine.cpp
new file mode 100644
index 00000000000..15875da16e1
--- /dev/null
+++ b/src/mongo/scripting/mozjs/engine.cpp
@@ -0,0 +1,133 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kQuery
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/scripting/mozjs/engine.h"
+
+#include "mongo/db/operation_context.h"
+#include "mongo/scripting/mozjs/implscope.h"
+#include "mongo/scripting/mozjs/proxyscope.h"
+#include "mongo/util/log.h"
+
+namespace mongo {
+
+void ScriptEngine::setup() {
+ if (!globalScriptEngine) {
+ globalScriptEngine = new mozjs::MozJSScriptEngine();
+
+ if (hasGlobalServiceContext()) {
+ getGlobalServiceContext()->registerKillOpListener(globalScriptEngine);
+ }
+ }
+}
+
+std::string ScriptEngine::getInterpreterVersionString() {
+ return "MozJS-38";
+}
+
+namespace mozjs {
+
+MozJSScriptEngine::MozJSScriptEngine() {
+ uassert(ErrorCodes::JSInterpreterFailure, "Failed to JS_Init()", JS_Init());
+}
+
+MozJSScriptEngine::~MozJSScriptEngine() {
+ JS_ShutDown();
+}
+
+mongo::Scope* MozJSScriptEngine::createScope() {
+ return new MozJSProxyScope(this);
+}
+
+void MozJSScriptEngine::interrupt(unsigned opId) {
+ stdx::lock_guard<stdx::mutex> intLock(_globalInterruptLock);
+ OpIdToScopeMap::iterator iScope = _opToScopeMap.find(opId);
+ if (iScope == _opToScopeMap.end()) {
+ // got interrupt request for a scope that no longer exists
+ LOG(1) << "received interrupt request for unknown op: " << opId << printKnownOps_inlock();
+ return;
+ }
+
+ LOG(1) << "interrupting op: " << opId << printKnownOps_inlock();
+ iScope->second->kill();
+}
+
+std::string MozJSScriptEngine::printKnownOps_inlock() {
+ str::stream out;
+
+ if (shouldLog(logger::LogSeverity::Debug(2))) {
+ out << " known ops: \n";
+
+ for (auto&& iSc : _opToScopeMap) {
+ out << " " << iSc.first << "\n";
+ }
+ }
+
+ return out;
+}
+
+void MozJSScriptEngine::interruptAll() {
+ stdx::lock_guard<stdx::mutex> interruptLock(_globalInterruptLock);
+
+ for (auto&& iScope : _opToScopeMap) {
+ iScope.second->kill();
+ }
+}
+
+void MozJSScriptEngine::registerOperation(OperationContext* txn, MozJSImplScope* scope) {
+ stdx::lock_guard<stdx::mutex> giLock(_globalInterruptLock);
+
+ auto opId = txn->getOpID();
+
+ _opToScopeMap[opId] = scope;
+
+ LOG(2) << "SMScope " << static_cast<const void*>(scope) << " registered for op " << opId;
+ Status status = txn->checkForInterruptNoAssert();
+ if (!status.isOK()) {
+ scope->kill();
+ }
+}
+
+void MozJSScriptEngine::unregisterOperation(unsigned int opId) {
+ stdx::lock_guard<stdx::mutex> giLock(_globalInterruptLock);
+
+ LOG(2) << "ImplScope " << static_cast<const void*>(this) << " unregistered for op " << opId;
+
+ if (opId != 0) {
+ // scope is currently associated with an operation id
+ auto it = _opToScopeMap.find(opId);
+ if (it != _opToScopeMap.end())
+ _opToScopeMap.erase(it);
+ }
+}
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/engine.h b/src/mongo/scripting/mozjs/engine.h
new file mode 100644
index 00000000000..a337ef0bcdc
--- /dev/null
+++ b/src/mongo/scripting/mozjs/engine.h
@@ -0,0 +1,93 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#pragma once
+
+#include <jsapi.h>
+#include <unordered_map>
+
+#include "mongo/scripting/deadline_monitor.h"
+#include "mongo/scripting/engine.h"
+#include "mongo/stdx/mutex.h"
+#include "mongo/util/concurrency/mutex.h"
+
+namespace mongo {
+namespace mozjs {
+
+class MozJSImplScope;
+
+/**
+ * Implements the global ScriptEngine interface for MozJS. The associated TU
+ * pulls this in for the polymorphic globalScriptEngine.
+ */
+class MozJSScriptEngine final : public mongo::ScriptEngine {
+public:
+ MozJSScriptEngine();
+ ~MozJSScriptEngine() override;
+
+ mongo::Scope* createScope() override;
+
+ void runTest() override {}
+
+ bool utf8Ok() const override {
+ return true;
+ }
+
+ void interrupt(unsigned opId) override;
+
+ void interruptAll() override;
+
+ void registerOperation(OperationContext* ctx, MozJSImplScope* scope);
+ void unregisterOperation(unsigned int opId);
+
+ using ScopeCallback = void (*)(Scope&);
+ ScopeCallback getScopeInitCallback() {
+ return _scopeInitCallback;
+ };
+
+ DeadlineMonitor<MozJSImplScope>& getDeadlineMonitor() {
+ return _deadlineMonitor;
+ }
+
+private:
+ std::string printKnownOps_inlock();
+
+ /**
+ * This mutex protects _opToScopeMap
+ */
+ stdx::mutex _globalInterruptLock;
+
+ using OpIdToScopeMap = std::unordered_map<unsigned, MozJSImplScope*>;
+ OpIdToScopeMap _opToScopeMap; // map of mongo op ids to scopes (protected by
+ // _globalInterruptLock).
+
+ DeadlineMonitor<MozJSImplScope> _deadlineMonitor;
+};
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/exception.cpp b/src/mongo/scripting/mozjs/exception.cpp
new file mode 100644
index 00000000000..7f7349acee1
--- /dev/null
+++ b/src/mongo/scripting/mozjs/exception.cpp
@@ -0,0 +1,89 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/scripting/mozjs/exception.h"
+
+#include <jsfriendapi.h>
+
+#include "mongo/scripting/mozjs/jsstringwrapper.h"
+#include "mongo/util/assert_util.h"
+
+namespace mongo {
+namespace mozjs {
+
+namespace {
+
+JSErrorFormatString kFormatString = {"{0}", 1, JSEXN_ERR};
+const JSErrorFormatString* errorCallback(void* data, const unsigned code) {
+ return &kFormatString;
+}
+
+} // namespace
+
+void mongoToJSException(JSContext* cx) {
+ auto status = exceptionToStatus();
+
+ JS_ReportErrorNumber(cx, errorCallback, nullptr, status.code(), status.reason().c_str());
+}
+
+void setJSException(JSContext* cx, ErrorCodes::Error code, StringData sd) {
+ JS_ReportErrorNumber(cx, errorCallback, nullptr, code, sd.rawData());
+}
+
+Status currentJSExceptionToStatus(JSContext* cx, ErrorCodes::Error altCode, StringData altReason) {
+ JS::RootedValue vp(cx);
+ if (!JS_GetPendingException(cx, &vp))
+ return Status(altCode, altReason.rawData());
+
+ JS::RootedObject obj(cx, vp.toObjectOrNull());
+ JSErrorReport* report = JS_ErrorFromException(cx, obj);
+ if (!report)
+ return Status(altCode, altReason.rawData());
+
+ JSStringWrapper jsstr(cx, js::ErrorReportToString(cx, report));
+ if (!jsstr)
+ return Status(altCode, altReason.rawData());
+
+ /**
+ * errorNumber is only set by library consumers of MozJS, and then only via
+ * JS_ReportErrorNumber, so all of the codes we see here are ours.
+ */
+ return Status(report->errorNumber ? static_cast<ErrorCodes::Error>(report->errorNumber)
+ : altCode,
+ jsstr.toStringData().rawData());
+}
+
+void throwCurrentJSException(JSContext* cx, ErrorCodes::Error altCode, StringData altReason) {
+ auto status = currentJSExceptionToStatus(cx, altCode, altReason);
+ uasserted(status.code(), status.reason());
+}
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/exception.h b/src/mongo/scripting/mozjs/exception.h
new file mode 100644
index 00000000000..9ef48664db6
--- /dev/null
+++ b/src/mongo/scripting/mozjs/exception.h
@@ -0,0 +1,67 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#pragma once
+
+#include <jsapi.h>
+
+#include "mongo/base/error_codes.h"
+#include "mongo/base/string_data.h"
+#include "mongo/util/assert_util.h"
+
+namespace mongo {
+namespace mozjs {
+
+/**
+ * Turns a current C++ exception into a JS exception
+ */
+void mongoToJSException(JSContext* cx);
+
+/**
+ * Sets an exception for javascript
+ */
+void setJSException(JSContext* cx, ErrorCodes::Error code, StringData sd);
+
+/**
+ * Converts the current pending js expection into a status
+ *
+ * The altCode and altReason are used if no JS exception is pending
+ */
+Status currentJSExceptionToStatus(JSContext* cx, ErrorCodes::Error altCode, StringData altReason);
+
+/**
+ * Turns the current JS exception into a C++ exception
+ *
+ * The altCode and altReason are used if no JS exception is pending
+ */
+MONGO_COMPILER_NORETURN void throwCurrentJSException(JSContext* cx,
+ ErrorCodes::Error altCode,
+ StringData altReason);
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/global.cpp b/src/mongo/scripting/mozjs/global.cpp
new file mode 100644
index 00000000000..d8c6da94e35
--- /dev/null
+++ b/src/mongo/scripting/mozjs/global.cpp
@@ -0,0 +1,103 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/scripting/mozjs/global.h"
+
+#include <js/Conversions.h>
+
+#include "mongo/base/init.h"
+#include "mongo/logger/logstream_builder.h"
+#include "mongo/scripting/mozjs/implscope.h"
+#include "mongo/scripting/mozjs/jsstringwrapper.h"
+#include "mongo/scripting/mozjs/objectwrapper.h"
+#include "mongo/scripting/mozjs/valuereader.h"
+
+namespace mongo {
+namespace mozjs {
+
+const JSFunctionSpec GlobalInfo::freeFunctions[4] = {
+ MONGO_ATTACH_JS_FUNCTION(gc),
+ MONGO_ATTACH_JS_FUNCTION(print),
+ MONGO_ATTACH_JS_FUNCTION(version),
+ JS_FS_END,
+};
+
+const char* const GlobalInfo::className = "Global";
+
+namespace {
+
+logger::MessageLogDomain* jsPrintLogDomain;
+
+} // namespace
+
+void GlobalInfo::Functions::print(JSContext* cx, JS::CallArgs args) {
+ logger::LogstreamBuilder builder(jsPrintLogDomain, getThreadName(), logger::LogSeverity::Log());
+ std::ostream& ss = builder.stream();
+
+ bool first = true;
+ for (size_t i = 0; i < args.length(); i++) {
+ if (first)
+ first = false;
+ else
+ ss << " ";
+
+ if (args.get(i).isNullOrUndefined()) {
+ // failed to get object to convert
+ ss << "[unknown type]";
+ continue;
+ }
+
+ JSStringWrapper jsstr(cx, JS::ToString(cx, args.get(i)));
+ ss << jsstr.toStringData();
+ }
+ ss << std::endl;
+
+ args.rval().setUndefined();
+}
+
+void GlobalInfo::Functions::version(JSContext* cx, JS::CallArgs args) {
+ ValueReader(cx, args.rval()).fromStringData(JS_VersionToString(JS_GetVersion(cx)));
+}
+
+void GlobalInfo::Functions::gc(JSContext* cx, JS::CallArgs args) {
+ auto scope = getScope(cx);
+
+ scope->gc();
+
+ args.rval().setUndefined();
+}
+
+MONGO_INITIALIZER(JavascriptPrintDomain)(InitializerContext*) {
+ jsPrintLogDomain = logger::globalLogManager()->getNamedDomain("javascriptOutput");
+ return Status::OK();
+}
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/global.h b/src/mongo/scripting/mozjs/global.h
new file mode 100644
index 00000000000..1da83350137
--- /dev/null
+++ b/src/mongo/scripting/mozjs/global.h
@@ -0,0 +1,56 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#pragma once
+
+#include "mongo/scripting/mozjs/wraptype.h"
+
+namespace mongo {
+namespace mozjs {
+
+/**
+ * The global object for all of our JS.
+ *
+ * This function is super special and it's properties are the globally visible
+ * symbol for JS execution.
+ */
+struct GlobalInfo : public BaseInfo {
+ struct Functions {
+ MONGO_DEFINE_JS_FUNCTION(gc);
+ MONGO_DEFINE_JS_FUNCTION(print);
+ MONGO_DEFINE_JS_FUNCTION(version);
+ };
+
+ static const JSFunctionSpec freeFunctions[4];
+
+ static const char* const className;
+ static const unsigned classFlags = JSCLASS_GLOBAL_FLAGS;
+};
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/idwrapper.cpp b/src/mongo/scripting/mozjs/idwrapper.cpp
new file mode 100644
index 00000000000..2c701037a22
--- /dev/null
+++ b/src/mongo/scripting/mozjs/idwrapper.cpp
@@ -0,0 +1,74 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/scripting/mozjs/idwrapper.h"
+
+#include "mongo/base/error_codes.h"
+#include "mongo/scripting/mozjs/exception.h"
+#include "mongo/scripting/mozjs/jsstringwrapper.h"
+#include "mongo/util/assert_util.h"
+
+namespace mongo {
+namespace mozjs {
+
+IdWrapper::IdWrapper(JSContext* cx, JS::HandleId value) : _context(cx), _value(cx, value) {}
+
+std::string IdWrapper::toString() const {
+ if (JSID_IS_STRING(_value)) {
+ return JSStringWrapper(_context, JSID_TO_STRING(_value)).toString();
+ } else if (JSID_IS_INT(_value)) {
+ return std::to_string(JSID_TO_INT(_value));
+ } else {
+ throwCurrentJSException(_context,
+ ErrorCodes::TypeMismatch,
+ "Cannot toString() non-string and non-integer jsid");
+ }
+}
+
+uint32_t IdWrapper::toInt32() const {
+ uassert(ErrorCodes::TypeMismatch, "Cannot toInt32() non-integer jsid", JSID_IS_INT(_value));
+
+ return JSID_TO_INT(_value);
+}
+
+bool IdWrapper::equals(StringData sd) const {
+ return sd.compare(toString()) == 0;
+}
+
+bool IdWrapper::isInt() const {
+ return JSID_IS_INT(_value);
+}
+
+bool IdWrapper::isString() const {
+ return JSID_IS_STRING(_value);
+}
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/idwrapper.h b/src/mongo/scripting/mozjs/idwrapper.h
new file mode 100644
index 00000000000..4865e5e0aec
--- /dev/null
+++ b/src/mongo/scripting/mozjs/idwrapper.h
@@ -0,0 +1,71 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#pragma once
+
+#include <jsapi.h>
+#include <string>
+
+#include "mongo/base/string_data.h"
+
+namespace mongo {
+namespace mozjs {
+
+/**
+ * Wraps jsid's to make them slightly easier to use
+ *
+ * As these own a JS::RootedId they're not movable or copyable
+ *
+ * IdWrapper should only be used on the stack, never in a heap allocation
+ */
+class IdWrapper {
+public:
+ IdWrapper(JSContext* cx, JS::HandleId id);
+
+ /**
+ * Converts to a string. This coerces for integers
+ */
+ std::string toString() const;
+
+ /**
+ * Converts to an int. This throws if the id is not an integer
+ */
+ uint32_t toInt32() const;
+
+ bool isString() const;
+ bool isInt() const;
+
+ bool equals(StringData sd) const;
+
+private:
+ JSContext* _context;
+ JS::RootedId _value;
+};
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/implscope.cpp b/src/mongo/scripting/mozjs/implscope.cpp
new file mode 100644
index 00000000000..0d5d3675603
--- /dev/null
+++ b/src/mongo/scripting/mozjs/implscope.cpp
@@ -0,0 +1,728 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kQuery
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/scripting/mozjs/implscope.h"
+
+#include <jscustomallocator.h>
+#include <jsfriendapi.h>
+
+#include "mongo/base/error_codes.h"
+#include "mongo/db/operation_context.h"
+#include "mongo/scripting/mozjs/objectwrapper.h"
+#include "mongo/scripting/mozjs/valuereader.h"
+#include "mongo/scripting/mozjs/valuewriter.h"
+#include "mongo/stdx/mutex.h"
+#include "mongo/util/concurrency/threadlocal.h"
+#include "mongo/util/log.h"
+
+using namespace mongoutils;
+
+namespace mongo {
+
+// Generated symbols for JS files
+namespace JSFiles {
+extern const JSFile types;
+extern const JSFile assert;
+} // namespace
+
+namespace mozjs {
+
+const char* const MozJSImplScope::kExecResult = "__lastres__";
+const char* const MozJSImplScope::kInvokeResult = "__returnValue";
+
+namespace {
+
+/**
+ * The maximum amount of memory to be given out per thread to mozilla. We
+ * manage this by trapping all calls to malloc, free, etc. and keeping track of
+ * counts in some thread locals
+ */
+const size_t kMallocMemoryLimit = 1024ul * 1024 * 1024 * 1.1;
+
+/**
+ * The number of bytes to allocate after which garbage collection is run
+ */
+const int kMaxBytesBeforeGC = 8 * 1024 * 1024;
+
+/**
+ * The size, in bytes, of each "stack chunk". 8192 is the recommended amount
+ * from mozilla
+ */
+const int kStackChunkSize = 8192;
+
+/**
+ * Runtime's can race on first creation (on some function statics), so we just
+ * serialize the initial Runtime creation.
+ */
+stdx::mutex gRuntimeCreationMutex;
+bool gFirstRuntimeCreated = false;
+
+} // namespace
+
+MONGO_TRIVIALLY_CONSTRUCTIBLE_THREAD_LOCAL MozJSImplScope* kCurrentScope;
+
+struct MozJSImplScope::MozJSEntry {
+ MozJSEntry(MozJSImplScope* scope) : ar(scope->_context), ac(scope->_context, scope->_global) {}
+
+ JSAutoRequest ar;
+ JSAutoCompartment ac;
+};
+
+void MozJSImplScope::_reportError(JSContext* cx, const char* message, JSErrorReport* report) {
+ auto scope = getScope(cx);
+
+ if (!JSREPORT_IS_WARNING(report->flags)) {
+ scope->_status =
+ Status(report->errorNumber ? static_cast<ErrorCodes::Error>(report->errorNumber)
+ : ErrorCodes::JSInterpreterFailure,
+ str::stream() << message << ":\n"
+ << JS::FormatStackDump(cx, nullptr, true, true, false) << "\n");
+ }
+}
+
+std::string MozJSImplScope::getError() {
+ return "";
+}
+
+void MozJSImplScope::registerOperation(OperationContext* txn) {
+ invariant(_opId == 0);
+ _opId = txn->getOpID();
+
+ _engine->registerOperation(txn, this);
+}
+
+void MozJSImplScope::unregisterOperation() {
+ if (_opId != 0) {
+ _engine->unregisterOperation(_opId);
+
+ _opId = 0;
+ }
+}
+
+void MozJSImplScope::kill() {
+ _pendingKill.store(true);
+ JS_RequestInterruptCallback(_runtime);
+}
+
+bool MozJSImplScope::isKillPending() const {
+ return _pendingKill.load();
+}
+
+OperationContext* MozJSImplScope::getOpContext() const {
+ return _opCtx;
+}
+
+bool MozJSImplScope::_interruptCallback(JSContext* cx) {
+ auto scope = getScope(cx);
+
+ if (scope->_pendingGC.load()) {
+ JS_GC(scope->_runtime);
+ }
+
+ bool kill = scope->isKillPending();
+
+ if (kill) {
+ scope->_engine->getDeadlineMonitor().stopDeadline(scope);
+ scope->unregisterOperation();
+ }
+
+ return !kill;
+}
+
+void MozJSImplScope::_gcCallback(JSRuntime* rt, JSGCStatus status, void* data) {
+ if (!shouldLog(logger::LogSeverity::Debug(1))) {
+ // don't collect stats unless verbose
+ return;
+ }
+
+ log() << "MozJS GC " << (status == JSGC_BEGIN ? "prologue" : "epilogue") << " heap stats - "
+ << " total: " << mongo::sm::get_total_bytes() << " limit: " << mongo::sm::get_max_bytes()
+ << std::endl;
+}
+
+MozJSImplScope::MozRuntime::MozRuntime() {
+ mongo::sm::reset(kMallocMemoryLimit);
+
+ {
+ stdx::unique_lock<stdx::mutex> lk(gRuntimeCreationMutex);
+
+ if (gFirstRuntimeCreated) {
+ // If we've already made a runtime, just proceed
+ lk.unlock();
+ } else {
+ // If this is the first one, hold the lock until after the first
+ // one's done
+ gFirstRuntimeCreated = true;
+ }
+
+ _runtime = JS_NewRuntime(kMaxBytesBeforeGC);
+ }
+
+ uassert(ErrorCodes::JSInterpreterFailure, "Failed to initialize JSRuntime", _runtime);
+
+ _context = JS_NewContext(_runtime, kStackChunkSize);
+ uassert(ErrorCodes::JSInterpreterFailure, "Failed to initialize JSContext", _context);
+}
+
+MozJSImplScope::MozRuntime::~MozRuntime() {
+ JS_DestroyContext(_context);
+ JS_DestroyRuntime(_runtime);
+}
+
+MozJSImplScope::MozJSImplScope(MozJSScriptEngine* engine)
+ : _engine(engine),
+ _mr(),
+ _runtime(_mr._runtime),
+ _context(_mr._context),
+ _globalProto(_context),
+ _global(_globalProto.getProto()),
+ _funcs(),
+ _pendingKill(false),
+ _opId(0),
+ _opCtx(nullptr),
+ _pendingGC(false),
+ _connectState(ConnectState::Not),
+ _status(Status::OK()),
+ _binDataProto(_context),
+ _bsonProto(_context),
+ _countDownLatchProto(_context),
+ _cursorProto(_context),
+ _dbCollectionProto(_context),
+ _dbPointerProto(_context),
+ _dbQueryProto(_context),
+ _dbProto(_context),
+ _dbRefProto(_context),
+ _jsThreadProto(_context),
+ _maxKeyProto(_context),
+ _minKeyProto(_context),
+ _mongoExternalProto(_context),
+ _mongoLocalProto(_context),
+ _nativeFunctionProto(_context),
+ _numberIntProto(_context),
+ _numberLongProto(_context),
+ _objectProto(_context),
+ _oidProto(_context),
+ _regExpProto(_context),
+ _timestampProto(_context) {
+ kCurrentScope = this;
+
+ // The default is quite low and doesn't seem to directly correlate with
+ // malloc'd bytes. Set it to MAX_INT here and catching things in the
+ // jscustomallocator.cpp
+ JS_SetGCParameter(_runtime, JSGC_MAX_BYTES, 0xffffffff);
+
+ JS_SetInterruptCallback(_runtime, _interruptCallback);
+ JS_SetGCCallback(_runtime, _gcCallback, this);
+ JS_SetContextPrivate(_context, this);
+ JSAutoRequest ar(_context);
+
+ JS_SetErrorReporter(_runtime, _reportError);
+
+ JSAutoCompartment ac(_context, _global);
+
+ _checkErrorState(JS_InitStandardClasses(_context, _global));
+
+ installBSONTypes();
+ execSetup(JSFiles::assert);
+ execSetup(JSFiles::types);
+
+ // install process-specific utilities in the global scope (dependancy: types.js, assert.js)
+ if (_engine->getScopeInitCallback())
+ _engine->getScopeInitCallback()(*this);
+
+ // install global utility functions
+ installGlobalUtils(*this);
+}
+
+MozJSImplScope::~MozJSImplScope() {
+ for (auto&& x : _funcs) {
+ x.reset();
+ }
+
+ unregisterOperation();
+}
+
+bool MozJSImplScope::hasOutOfMemoryException() {
+ return false;
+}
+
+void MozJSImplScope::init(const BSONObj* data) {
+ if (!data)
+ return;
+
+ BSONObjIterator i(*data);
+ while (i.more()) {
+ BSONElement e = i.next();
+ setElement(e.fieldName(), e);
+ }
+}
+
+void MozJSImplScope::setNumber(const char* field, double val) {
+ MozJSEntry entry(this);
+
+ ObjectWrapper(_context, _global).setNumber(field, val);
+}
+
+void MozJSImplScope::setString(const char* field, StringData val) {
+ MozJSEntry entry(this);
+
+ ObjectWrapper(_context, _global).setString(field, val);
+}
+
+void MozJSImplScope::setBoolean(const char* field, bool val) {
+ MozJSEntry entry(this);
+
+ ObjectWrapper(_context, _global).setBoolean(field, val);
+}
+
+void MozJSImplScope::setElement(const char* field, const BSONElement& e) {
+ MozJSEntry entry(this);
+
+ ObjectWrapper(_context, _global).setBSONElement(field, e, false);
+}
+
+void MozJSImplScope::setObject(const char* field, const BSONObj& obj, bool readOnly) {
+ MozJSEntry entry(this);
+
+ ObjectWrapper(_context, _global).setBSON(field, obj, readOnly);
+}
+
+int MozJSImplScope::type(const char* field) {
+ MozJSEntry entry(this);
+
+ return ObjectWrapper(_context, _global).type(field);
+}
+
+double MozJSImplScope::getNumber(const char* field) {
+ MozJSEntry entry(this);
+
+ return ObjectWrapper(_context, _global).getNumber(field);
+}
+
+int MozJSImplScope::getNumberInt(const char* field) {
+ MozJSEntry entry(this);
+
+ return ObjectWrapper(_context, _global).getNumberInt(field);
+}
+
+long long MozJSImplScope::getNumberLongLong(const char* field) {
+ MozJSEntry entry(this);
+
+ return ObjectWrapper(_context, _global).getNumberLongLong(field);
+}
+
+std::string MozJSImplScope::getString(const char* field) {
+ MozJSEntry entry(this);
+
+ return ObjectWrapper(_context, _global).getString(field);
+}
+
+bool MozJSImplScope::getBoolean(const char* field) {
+ MozJSEntry entry(this);
+
+ return ObjectWrapper(_context, _global).getBoolean(field);
+}
+
+BSONObj MozJSImplScope::getObject(const char* field) {
+ MozJSEntry entry(this);
+
+ return ObjectWrapper(_context, _global).getObject(field);
+}
+
+void MozJSImplScope::newFunction(StringData raw, JS::MutableHandleValue out) {
+ MozJSEntry entry(this);
+
+ std::string code = str::stream() << "____MongoToSM_newFunction_temp = " << raw;
+
+ JS::CompileOptions co(_context);
+ setCompileOptions(&co);
+ _checkErrorState(JS::Evaluate(_context, _global, co, code.c_str(), code.length(), out));
+}
+
+BSONObj MozJSImplScope::callThreadArgs(const BSONObj& args) {
+ MozJSEntry entry(this);
+
+ JS::RootedValue function(_context);
+ ValueReader(_context, &function).fromBSONElement(args.firstElement(), true);
+
+ int argc = args.nFields() - 1;
+
+ JS::AutoValueVector argv(_context);
+ BSONObjIterator it(args);
+ it.next();
+ JS::RootedValue value(_context);
+
+ for (int i = 0; i < argc; ++i) {
+ ValueReader(_context, &value).fromBSONElement(*it, true);
+ argv.append(value);
+ it.next();
+ }
+
+ JS::RootedValue out(_context);
+ JS::RootedObject thisv(_context);
+
+ bool success = JS::Call(_context, thisv, function, argv, &out);
+
+ if (!success) {
+ auto status = currentJSExceptionToStatus(
+ _context, ErrorCodes::JSInterpreterFailure, "Unknown callThread failure");
+
+ log() << "js thread raised js exception: " << status;
+
+ uasserted(status.code(), status.reason());
+ }
+
+ BSONObjBuilder b;
+ ValueWriter(_context, out).writeThis(&b, "ret");
+
+ return b.obj();
+}
+
+bool hasFunctionIdentifier(StringData code) {
+ if (code.size() < 9 || code.find("function") != 0)
+ return false;
+
+ return code[8] == ' ' || code[8] == '(';
+}
+
+// TODO: This function identification code is broken. Fix it up to be more robust
+//
+// See: SERVER-16703 for more info
+void MozJSImplScope::_MozJSCreateFunction(const char* raw,
+ ScriptingFunction functionNumber,
+ JS::MutableHandleValue fun) {
+ std::string code = jsSkipWhiteSpace(raw);
+ if (!hasFunctionIdentifier(code)) {
+ if (code.find('\n') == std::string::npos && !hasJSReturn(code) &&
+ (code.find(';') == std::string::npos || code.find(';') == code.size() - 1)) {
+ code = "return " + code;
+ }
+ code = "function(){ " + code + "}";
+ }
+
+ code = str::stream() << "_funcs" << functionNumber << " = " << code;
+
+ JS::CompileOptions co(_context);
+ setCompileOptions(&co);
+
+ _checkErrorState(JS::Evaluate(_context, _global, co, code.c_str(), code.length(), fun));
+ uassert(10232,
+ "not a function",
+ fun.isObject() && JS_ObjectIsFunction(_context, fun.toObjectOrNull()));
+}
+
+ScriptingFunction MozJSImplScope::_createFunction(const char* raw,
+ ScriptingFunction functionNumber) {
+ MozJSEntry entry(this);
+
+ JS::RootedValue fun(_context);
+ _MozJSCreateFunction(raw, functionNumber, &fun);
+ _funcs.emplace_back(_context, fun.get());
+
+ return functionNumber;
+}
+
+void MozJSImplScope::setFunction(const char* field, const char* code) {
+ MozJSEntry entry(this);
+
+ JS::RootedValue fun(_context);
+
+ _MozJSCreateFunction(code, getFunctionCache().size() + 1, &fun);
+
+ ObjectWrapper(_context, _global).setValue(field, fun);
+}
+
+void MozJSImplScope::rename(const char* from, const char* to) {
+ MozJSEntry entry(this);
+
+ ObjectWrapper(_context, _global).rename(from, to);
+}
+
+int MozJSImplScope::invoke(ScriptingFunction func,
+ const BSONObj* argsObject,
+ const BSONObj* recv,
+ int timeoutMs,
+ bool ignoreReturn,
+ bool readOnlyArgs,
+ bool readOnlyRecv) {
+ MozJSEntry entry(this);
+
+ auto funcValue = _funcs[func - 1];
+ JS::RootedValue result(_context);
+
+ const int nargs = argsObject ? argsObject->nFields() : 0;
+
+ JS::AutoValueVector args(_context);
+
+ if (nargs) {
+ BSONObjIterator it(*argsObject);
+ for (int i = 0; i < nargs; i++) {
+ BSONElement next = it.next();
+
+ JS::RootedValue value(_context);
+ ValueReader(_context, &value).fromBSONElement(next, readOnlyArgs);
+
+ args.append(value);
+ }
+ }
+
+ JS::RootedValue smrecv(_context);
+ if (recv)
+ ValueReader(_context, &smrecv).fromBSON(*recv, readOnlyRecv);
+ else
+ smrecv.setObjectOrNull(_global);
+
+ if (timeoutMs)
+ _engine->getDeadlineMonitor().startDeadline(this, timeoutMs);
+
+ JS::RootedValue out(_context);
+ JS::RootedObject obj(_context, smrecv.toObjectOrNull());
+
+ bool success = JS::Call(_context, obj, funcValue, args, &out);
+
+ if (timeoutMs)
+ _engine->getDeadlineMonitor().stopDeadline(this);
+
+ _checkErrorState(success);
+
+ if (!ignoreReturn) {
+ // must validate the handle because TerminateExecution may have
+ // been thrown after the above checks
+ if (out.isObject() && _nativeFunctionProto.instanceOf(out)) {
+ warning() << "storing native function as return value";
+ _lastRetIsNativeCode = true;
+ } else {
+ _lastRetIsNativeCode = false;
+ }
+
+ ObjectWrapper(_context, _global).setValue(kInvokeResult, out);
+ }
+
+ return 0;
+}
+
+bool MozJSImplScope::exec(StringData code,
+ const std::string& name,
+ bool printResult,
+ bool reportError,
+ bool assertOnError,
+ int timeoutMs) {
+ MozJSEntry entry(this);
+
+ JS::CompileOptions co(_context);
+ setCompileOptions(&co);
+ JS::RootedScript script(_context);
+
+ bool success = JS::Compile(_context, _global, co, code.rawData(), code.size(), &script);
+
+ if (_checkErrorState(success, reportError, assertOnError))
+ return false;
+
+ if (timeoutMs)
+ _engine->getDeadlineMonitor().startDeadline(this, timeoutMs);
+
+ JS::RootedValue out(_context);
+
+ success = JS_ExecuteScript(_context, _global, script, &out);
+
+ if (timeoutMs)
+ _engine->getDeadlineMonitor().stopDeadline(this);
+
+ if (_checkErrorState(success, reportError, assertOnError))
+ return false;
+
+ ObjectWrapper(_context, _global).setValue(kExecResult, out);
+
+ if (printResult && !out.isUndefined()) {
+ // TODO: We seem to use this productively in v8, but it seems
+ // unecessary under sm. That probably means somethings off
+ //
+ // appears to only be used by shell
+ // std::cout << ValueWriter(_context, out).toString() << std::endl;
+ }
+
+ return true;
+}
+
+void MozJSImplScope::injectNative(const char* field, NativeFunction func, void* data) {
+ MozJSEntry entry(this);
+
+ JS::RootedObject obj(_context);
+
+ NativeFunctionInfo::make(_context, &obj, func, data);
+
+ JS::RootedValue value(_context);
+ value.setObjectOrNull(obj);
+ ObjectWrapper(_context, _global).setValue(field, value);
+}
+
+void MozJSImplScope::gc() {
+ _pendingGC.store(true);
+ JS_RequestInterruptCallback(_runtime);
+}
+
+void MozJSImplScope::localConnectForDbEval(OperationContext* txn, const char* dbName) {
+ MozJSEntry entry(this);
+
+ invariant(_opCtx == NULL);
+ _opCtx = txn;
+
+ if (_connectState == ConnectState::External)
+ uasserted(12510, "externalSetup already called, can't call localConnect");
+ if (_connectState == ConnectState::Local) {
+ if (_localDBName == dbName)
+ return;
+ uasserted(12511,
+ str::stream() << "localConnect previously called with name " << _localDBName);
+ }
+
+ // NOTE: order is important here. the following methods must be called after
+ // the above conditional statements.
+
+ // install db access functions in the global object
+ installDBAccess();
+
+ // install the Mongo function object and instantiate the 'db' global
+ _mongoLocalProto.install(_global);
+ execCoreFiles();
+
+ const char* const makeMongo = "_mongo = new Mongo()";
+ exec(makeMongo, "local connect 2", false, true, true, 0);
+
+ std::string makeDB = str::stream() << "db = _mongo.getDB(\"" << dbName << "\");";
+ exec(makeDB, "local connect 3", false, true, true, 0);
+
+ _connectState = ConnectState::Local;
+ _localDBName = dbName;
+
+ loadStored(txn);
+}
+
+void MozJSImplScope::externalSetup() {
+ MozJSEntry entry(this);
+
+ if (_connectState == ConnectState::External)
+ return;
+ if (_connectState == ConnectState::Local)
+ uasserted(12512, "localConnect already called, can't call externalSetup");
+
+ mongo::sm::reset(0);
+
+ // install db access functions in the global object
+ installDBAccess();
+
+ // install thread-related functions (e.g. _threadInject)
+ installFork();
+
+ // install the Mongo function object
+ _mongoExternalProto.install(_global);
+ execCoreFiles();
+ _connectState = ConnectState::External;
+}
+
+void MozJSImplScope::reset() {
+ unregisterOperation();
+ _pendingKill.store(false);
+ _pendingGC.store(false);
+}
+
+void MozJSImplScope::installBSONTypes() {
+ _binDataProto.install(_global);
+ _bsonProto.install(_global);
+ _dbPointerProto.install(_global);
+ _dbRefProto.install(_global);
+ _maxKeyProto.install(_global);
+ _minKeyProto.install(_global);
+ _nativeFunctionProto.install(_global);
+ _numberIntProto.install(_global);
+ _numberLongProto.install(_global);
+ _objectProto.install(_global);
+ _oidProto.install(_global);
+ _regExpProto.install(_global);
+ _timestampProto.install(_global);
+
+ // This builtin map is a javascript 6 thing. We want our version. so
+ // take theirs out
+ ObjectWrapper(_context, _global).deleteProperty("Map");
+}
+
+void MozJSImplScope::installDBAccess() {
+ _cursorProto.install(_global);
+ _dbProto.install(_global);
+ _dbQueryProto.install(_global);
+ _dbCollectionProto.install(_global);
+}
+
+void MozJSImplScope::installFork() {
+ _countDownLatchProto.install(_global);
+ _jsThreadProto.install(_global);
+}
+
+bool MozJSImplScope::_checkErrorState(bool success, bool reportError, bool assertOnError) {
+ if (success)
+ return false;
+
+ if (_status.isOK()) {
+ _status = Status(ErrorCodes::UnknownError, "Unknown Failure from JSInterpreter");
+ }
+
+ _error = _status.reason();
+
+ if (reportError)
+ error() << _error << std::endl;
+
+ // Clear the status state
+ auto status = std::move(_status);
+
+ if (assertOnError) {
+ // Throw if necessary
+ uassertStatusOK(status);
+ }
+
+ return true;
+}
+
+
+void MozJSImplScope::setCompileOptions(JS::CompileOptions* co) {
+ co->setUTF8(true);
+}
+
+MozJSImplScope* MozJSImplScope::getThreadScope() {
+ return kCurrentScope;
+}
+
+void MozJSImplScope::setOOM() {
+ _status = Status(ErrorCodes::JSInterpreterFailure, "Out of memory");
+}
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/implscope.h b/src/mongo/scripting/mozjs/implscope.h
new file mode 100644
index 00000000000..4bd29e76676
--- /dev/null
+++ b/src/mongo/scripting/mozjs/implscope.h
@@ -0,0 +1,323 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#pragma once
+
+#include <jsapi.h>
+
+#include "mongo/client/dbclientcursor.h"
+#include "mongo/scripting/mozjs/bindata.h"
+#include "mongo/scripting/mozjs/bson.h"
+#include "mongo/scripting/mozjs/countdownlatch.h"
+#include "mongo/scripting/mozjs/cursor.h"
+#include "mongo/scripting/mozjs/db.h"
+#include "mongo/scripting/mozjs/dbcollection.h"
+#include "mongo/scripting/mozjs/dbpointer.h"
+#include "mongo/scripting/mozjs/dbquery.h"
+#include "mongo/scripting/mozjs/dbref.h"
+#include "mongo/scripting/mozjs/engine.h"
+#include "mongo/scripting/mozjs/global.h"
+#include "mongo/scripting/mozjs/jsthread.h"
+#include "mongo/scripting/mozjs/maxkey.h"
+#include "mongo/scripting/mozjs/minkey.h"
+#include "mongo/scripting/mozjs/mongo.h"
+#include "mongo/scripting/mozjs/nativefunction.h"
+#include "mongo/scripting/mozjs/numberint.h"
+#include "mongo/scripting/mozjs/numberlong.h"
+#include "mongo/scripting/mozjs/object.h"
+#include "mongo/scripting/mozjs/oid.h"
+#include "mongo/scripting/mozjs/regexp.h"
+#include "mongo/scripting/mozjs/timestamp.h"
+
+namespace mongo {
+namespace mozjs {
+
+/**
+ * Implementation Scope for MozJS
+ *
+ * The Implementation scope holds the actual mozjs runtime and context objects,
+ * along with a number of global prototypes for mongoDB specific types. Each
+ * ImplScope requires it's own thread and cannot be accessed from any thread
+ * other than the one it was created on (this is a detail inherited from the
+ * JSRuntime). If you need a scope that can be accessed by different threads
+ * over the course of it's lifetime, see MozJSProxyScope
+ *
+ * For more information about overriden fields, see mongo::Scope
+ */
+class MozJSImplScope final : public Scope {
+ MONGO_DISALLOW_COPYING(MozJSImplScope);
+
+public:
+ explicit MozJSImplScope(MozJSScriptEngine* engine);
+ ~MozJSImplScope();
+
+ void init(const BSONObj* data) override;
+
+ void reset() override;
+
+ void kill();
+
+ bool isKillPending() const override;
+
+ OperationContext* getOpContext() const;
+
+ void registerOperation(OperationContext* txn) override;
+
+ void unregisterOperation() override;
+
+ void localConnectForDbEval(OperationContext* txn, const char* dbName) override;
+
+ void externalSetup() override;
+
+ std::string getError() override;
+
+ bool hasOutOfMemoryException() override;
+
+ void gc() override;
+
+ double getNumber(const char* field) override;
+ int getNumberInt(const char* field) override;
+ long long getNumberLongLong(const char* field) override;
+ std::string getString(const char* field) override;
+ bool getBoolean(const char* field) override;
+ BSONObj getObject(const char* field) override;
+
+ void setNumber(const char* field, double val) override;
+ void setString(const char* field, StringData val) override;
+ void setBoolean(const char* field, bool val) override;
+ void setElement(const char* field, const BSONElement& e) override;
+ void setObject(const char* field, const BSONObj& obj, bool readOnly) override;
+ void setFunction(const char* field, const char* code) override;
+
+ int type(const char* field) override;
+
+ void rename(const char* from, const char* to) override;
+
+ int invoke(ScriptingFunction func,
+ const BSONObj* args,
+ const BSONObj* recv,
+ int timeoutMs = 0,
+ bool ignoreReturn = false,
+ bool readOnlyArgs = false,
+ bool readOnlyRecv = false) override;
+
+ bool exec(StringData code,
+ const std::string& name,
+ bool printResult,
+ bool reportError,
+ bool assertOnError,
+ int timeoutMs) override;
+
+ void injectNative(const char* field, NativeFunction func, void* data = 0) override;
+
+ ScriptingFunction _createFunction(const char* code,
+ ScriptingFunction functionNumber = 0) override;
+
+ void newFunction(StringData code, JS::MutableHandleValue out);
+
+ BSONObj callThreadArgs(const BSONObj& obj);
+
+ WrapType<BinDataInfo>& getBinDataProto() {
+ return _binDataProto;
+ }
+
+ WrapType<BSONInfo>& getBsonProto() {
+ return _bsonProto;
+ }
+
+ WrapType<CountDownLatchInfo>& getCountDownLatchProto() {
+ return _countDownLatchProto;
+ }
+
+ WrapType<CursorInfo>& getCursorProto() {
+ return _cursorProto;
+ }
+
+ WrapType<DBCollectionInfo>& getDbCollectionProto() {
+ return _dbCollectionProto;
+ }
+
+ WrapType<DBPointerInfo>& getDbPointerProto() {
+ return _dbPointerProto;
+ }
+
+ WrapType<DBQueryInfo>& getDbQueryProto() {
+ return _dbQueryProto;
+ }
+
+ WrapType<DBInfo>& getDbProto() {
+ return _dbProto;
+ }
+
+ WrapType<DBRefInfo>& getDbRefProto() {
+ return _dbRefProto;
+ }
+
+ WrapType<JSThreadInfo>& getJSThreadProto() {
+ return _jsThreadProto;
+ }
+
+ WrapType<MaxKeyInfo>& getMaxKeyProto() {
+ return _maxKeyProto;
+ }
+
+ WrapType<MinKeyInfo>& getMinKeyProto() {
+ return _minKeyProto;
+ }
+
+ WrapType<MongoExternalInfo>& getMongoExternalProto() {
+ return _mongoExternalProto;
+ }
+
+ WrapType<MongoLocalInfo>& getMongoLocalProto() {
+ return _mongoLocalProto;
+ }
+
+ WrapType<NativeFunctionInfo>& getNativeFunctionProto() {
+ return _nativeFunctionProto;
+ }
+
+ WrapType<NumberIntInfo>& getNumberIntProto() {
+ return _numberIntProto;
+ }
+
+ WrapType<NumberLongInfo>& getNumberLongProto() {
+ return _numberLongProto;
+ }
+
+ WrapType<ObjectInfo>& getObjectProto() {
+ return _objectProto;
+ }
+
+ WrapType<OIDInfo>& getOidProto() {
+ return _oidProto;
+ }
+
+ WrapType<RegExpInfo>& getRegExpProto() {
+ return _regExpProto;
+ }
+
+ WrapType<TimestampInfo>& getTimestampProto() {
+ return _timestampProto;
+ }
+
+ static const char* const kExecResult;
+ static const char* const kInvokeResult;
+
+ static MozJSImplScope* getThreadScope();
+ void setOOM();
+
+private:
+ void _MozJSCreateFunction(const char* raw,
+ ScriptingFunction functionNumber,
+ JS::MutableHandleValue fun);
+
+ /**
+ * This structure exists exclusively to construct the runtime and context
+ * ahead of the various global prototypes in the ImplScope construction.
+ * Basically, we have to call some c apis on the way up and down and this
+ * takes care of that
+ */
+ struct MozRuntime {
+ public:
+ MozRuntime();
+ ~MozRuntime();
+
+ JSRuntime* _runtime;
+ JSContext* _context;
+ };
+
+ /**
+ * The connection state of the scope.
+ *
+ * This is for dbeval and the shell
+ */
+ enum class ConnectState : char {
+ Not,
+ Local,
+ External,
+ };
+
+ struct MozJSEntry;
+ friend struct MozJSEntry;
+
+ static void _reportError(JSContext* cx, const char* message, JSErrorReport* report);
+ static bool _interruptCallback(JSContext* cx);
+ static void _gcCallback(JSRuntime* rt, JSGCStatus status, void* data);
+ bool _checkErrorState(bool success, bool reportError = true, bool assertOnError = true);
+
+ void installDBAccess();
+ void installBSONTypes();
+ void installFork();
+
+ void setCompileOptions(JS::CompileOptions* co);
+
+ MozJSScriptEngine* _engine;
+ MozRuntime _mr;
+ JSRuntime* _runtime;
+ JSContext* _context;
+ WrapType<GlobalInfo> _globalProto;
+ JS::HandleObject _global;
+ std::vector<JS::PersistentRootedValue> _funcs;
+ std::atomic<bool> _pendingKill;
+ std::string _error;
+ unsigned int _opId; // op id for this scope
+ OperationContext* _opCtx; // Op context for DbEval
+ std::atomic<bool> _pendingGC;
+ ConnectState _connectState;
+ Status _status;
+
+ WrapType<BinDataInfo> _binDataProto;
+ WrapType<BSONInfo> _bsonProto;
+ WrapType<CountDownLatchInfo> _countDownLatchProto;
+ WrapType<CursorInfo> _cursorProto;
+ WrapType<DBCollectionInfo> _dbCollectionProto;
+ WrapType<DBPointerInfo> _dbPointerProto;
+ WrapType<DBQueryInfo> _dbQueryProto;
+ WrapType<DBInfo> _dbProto;
+ WrapType<DBRefInfo> _dbRefProto;
+ WrapType<JSThreadInfo> _jsThreadProto;
+ WrapType<MaxKeyInfo> _maxKeyProto;
+ WrapType<MinKeyInfo> _minKeyProto;
+ WrapType<MongoExternalInfo> _mongoExternalProto;
+ WrapType<MongoLocalInfo> _mongoLocalProto;
+ WrapType<NativeFunctionInfo> _nativeFunctionProto;
+ WrapType<NumberIntInfo> _numberIntProto;
+ WrapType<NumberLongInfo> _numberLongProto;
+ WrapType<ObjectInfo> _objectProto;
+ WrapType<OIDInfo> _oidProto;
+ WrapType<RegExpInfo> _regExpProto;
+ WrapType<TimestampInfo> _timestampProto;
+};
+
+inline MozJSImplScope* getScope(JSContext* cx) {
+ return static_cast<MozJSImplScope*>(JS_GetContextPrivate(cx));
+}
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/jscustomallocator.cpp b/src/mongo/scripting/mozjs/jscustomallocator.cpp
new file mode 100644
index 00000000000..adba220129d
--- /dev/null
+++ b/src/mongo/scripting/mozjs/jscustomallocator.cpp
@@ -0,0 +1,234 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include <cstddef>
+#include <type_traits>
+#include <jscustomallocator.h>
+
+#include "mongo/config.h"
+#include "mongo/util/concurrency/threadlocal.h"
+#include "mongo/scripting/mozjs/implscope.h"
+
+#ifdef __linux__
+#include <malloc.h>
+#elif defined(__APPLE__)
+#include <malloc/malloc.h>
+#elif defined(_WIN32)
+#include <malloc.h>
+#else
+#define MONGO_NO_MALLOC_USABLE_SIZE
+#endif
+
+/**
+ * This shim interface (which controls dynamic allocation within SpiderMonkey),
+ * consciously uses std::malloc and friends over mongoMalloc. It does this
+ * because SpiderMonkey has some plausible options in the event of OOM,
+ * specifically it can begin aggressive garbage collection. It would also be
+ * reasonable to go the other route and fail, but for the moment I erred on the
+ * side of maintaining the contract that SpiderMonkey expects.
+ *
+ * The overall strategy here is to keep track of allocations in a thread local,
+ * offering us the chance to enforce soft limits on memory use rather than
+ * waiting for the OS to OOM us.
+ */
+
+namespace mongo {
+namespace sm {
+
+namespace {
+/**
+ * These two variables track the total number of bytes handed out, and the
+ * maximum number of bytes we will consider handing out. They are set by
+ * MozJSImplScope on start up.
+ */
+MONGO_TRIVIALLY_CONSTRUCTIBLE_THREAD_LOCAL size_t total_bytes;
+MONGO_TRIVIALLY_CONSTRUCTIBLE_THREAD_LOCAL size_t max_bytes;
+
+/**
+ * When we don't have malloc_usable_size, we manage by adjusting our pointer by
+ * kMaxAlign bytes and storing the size of the allocation kMaxAlign bytes
+ * behind the pointer we hand back. That let's us get to the value at runtime.
+ * We know kMaxAlign is enough (generally 8 or 16 bytes), because that's
+ * literally the contract between malloc and std::max_align_t.
+ *
+ * This is commented out right now because std::max_align_t didn't seem to be
+ * available on our solaris builder. TODO: revisit in the future to see if that
+ * still holds.
+ */
+// const size_t kMaxAlign = std::alignment_of<std::max_align_t>::value;
+const size_t kMaxAlign = 16;
+} // namespace
+
+size_t get_total_bytes() {
+ return total_bytes;
+}
+
+void reset(size_t bytes) {
+ total_bytes = 0;
+ max_bytes = bytes;
+}
+
+size_t get_max_bytes() {
+ return max_bytes;
+}
+
+/**
+ * Wraps std::Xalloc functions
+ *
+ * The idea here is to abstract soft limits on allocations, as well as possibly
+ * necessary pointer adjustment (if we don't have a malloc_usable_size
+ * replacement).
+ *
+ */
+template <typename T>
+void* wrap_alloc(T&& func, void* ptr, size_t bytes) {
+ size_t mb = get_max_bytes();
+ size_t tb = get_total_bytes();
+
+ if (mb && (tb + bytes > mb)) {
+ auto scope = mongo::mozjs::MozJSImplScope::getThreadScope();
+ invariant(scope);
+
+ scope->setOOM();
+
+ return nullptr;
+ }
+
+#ifdef MONGO_NO_MALLOC_USABLE_SIZE
+ void* p = func(ptr ? static_cast<char*>(ptr) - kMaxAlign : nullptr, bytes + kMaxAlign);
+#else
+ void* p = func(ptr, bytes);
+#endif
+
+ if (!p) {
+ return nullptr;
+ }
+
+#ifdef MONGO_NO_MALLOC_USABLE_SIZE
+ *reinterpret_cast<size_t*>(p) = bytes;
+ p = static_cast<char*>(p) + kMaxAlign;
+#endif
+
+ total_bytes = tb + bytes;
+
+ return p;
+}
+
+size_t get_current(void* ptr) {
+#ifdef MONGO_NO_MALLOC_USABLE_SIZE
+ if (!ptr)
+ return 0;
+
+ return *reinterpret_cast<size_t*>(static_cast<char*>(ptr) - kMaxAlign);
+#elif defined(__linux__)
+ return malloc_usable_size(ptr);
+#elif defined(__APPLE__)
+ return malloc_size(ptr);
+#elif defined(_WIN32)
+ return _msize(ptr);
+#else
+#error "Should be unreachable"
+#endif
+}
+
+} // namespace sm
+} // namespace mongo
+
+void* js_malloc(size_t bytes) {
+ return mongo::sm::wrap_alloc(
+ [](void* ptr, size_t b) { return std::malloc(b); }, nullptr, bytes);
+}
+
+void* js_calloc(size_t bytes) {
+ return mongo::sm::wrap_alloc(
+ [](void* ptr, size_t b) { return std::calloc(b, 1); }, nullptr, bytes);
+}
+
+void* js_calloc(size_t nmemb, size_t size) {
+ return mongo::sm::wrap_alloc(
+ [](void* ptr, size_t b) { return std::calloc(b, 1); }, nullptr, nmemb * size);
+}
+
+void js_free(void* p) {
+ if (!p)
+ return;
+
+ size_t current = mongo::sm::get_current(p);
+ size_t tb = mongo::sm::get_total_bytes();
+
+ if (tb >= current) {
+ mongo::sm::total_bytes = tb - current;
+ }
+
+ mongo::sm::wrap_alloc([](void* ptr, size_t b) {
+ std::free(ptr);
+ return nullptr;
+ }, p, 0);
+}
+
+void* js_realloc(void* p, size_t bytes) {
+ if (!p) {
+ return js_malloc(bytes);
+ }
+
+ if (!bytes) {
+ js_free(p);
+ return nullptr;
+ }
+
+ size_t current = mongo::sm::get_current(p);
+
+ if (current >= bytes) {
+ return p;
+ }
+
+ size_t tb = mongo::sm::total_bytes;
+
+ if (tb >= current) {
+ mongo::sm::total_bytes = tb - current;
+ }
+
+ return mongo::sm::wrap_alloc(
+ [](void* ptr, size_t b) { return std::realloc(ptr, b); }, p, bytes);
+}
+
+char* js_strdup(const char* s) {
+ size_t bytes = std::strlen(s) + 1;
+
+ char* new_s = static_cast<char*>(js_malloc(bytes));
+
+ if (!new_s) {
+ return nullptr;
+ }
+
+ std::memcpy(new_s, s, bytes);
+
+ return new_s;
+}
diff --git a/src/mongo/scripting/mozjs/jsstringwrapper.cpp b/src/mongo/scripting/mozjs/jsstringwrapper.cpp
new file mode 100644
index 00000000000..8261b44a2fe
--- /dev/null
+++ b/src/mongo/scripting/mozjs/jsstringwrapper.cpp
@@ -0,0 +1,84 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/scripting/mozjs/jsstringwrapper.h"
+
+#include <js/CharacterEncoding.h>
+#include <jsapi.h>
+#include <utility>
+
+#include "mongo/base/error_codes.h"
+#include "mongo/scripting/mozjs/exception.h"
+#include "mongo/util/assert_util.h"
+
+namespace mongo {
+namespace mozjs {
+
+JSStringWrapper::JSStringWrapper(JSContext* cx, JSString* str) : _context(cx) {
+ if (!str)
+ throwCurrentJSException(cx, ErrorCodes::InternalError, "Cannot encode null JSString");
+
+ // We have to do this flatstring thing because no public api tells us
+ // how long the utf8 strings we get out are.
+ //
+ // Well, at least js/CharacterEncoding's GetDeflatedUTF8StringLength
+ // and JS_flattenString are all in the public headers...
+ JSFlatString* flat = JS_FlattenString(cx, str);
+ if (!flat)
+ throwCurrentJSException(cx, ErrorCodes::InternalError, "Failed to flatten JSString");
+
+ _length = JS::GetDeflatedUTF8StringLength(flat);
+
+ JS::RootedString rstr(cx, str);
+
+ JSAutoByteString abs;
+ abs.encodeUtf8(cx, rstr);
+
+ if (!abs)
+ throwCurrentJSException(cx, ErrorCodes::InternalError, "Failed to encode JSString");
+
+ _str.reset(new char[_length]);
+ std::memcpy(_str.get(), abs.ptr(), _length);
+}
+
+StringData JSStringWrapper::toStringData() const {
+ return StringData(_str.get(), _length);
+}
+
+std::string JSStringWrapper::toString() const {
+ return toStringData().toString();
+}
+
+JSStringWrapper::operator bool() const {
+ return _str.get();
+}
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/jsstringwrapper.h b/src/mongo/scripting/mozjs/jsstringwrapper.h
new file mode 100644
index 00000000000..9a0ee21e900
--- /dev/null
+++ b/src/mongo/scripting/mozjs/jsstringwrapper.h
@@ -0,0 +1,61 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#pragma once
+
+#include <jsapi.h>
+#include <memory>
+#include <string>
+
+#include "mongo/base/string_data.h"
+
+namespace mongo {
+namespace mozjs {
+
+/**
+ * Wraps JSStrings to simplify coercing them to and from C++ style StringData
+ * and std::strings.
+ */
+class JSStringWrapper {
+public:
+ JSStringWrapper() = default;
+ JSStringWrapper(JSContext* cx, JSString* str);
+
+ StringData toStringData() const;
+ std::string toString() const;
+
+ explicit operator bool() const;
+
+private:
+ JSContext* _context = nullptr;
+ std::unique_ptr<char[]> _str;
+ size_t _length = 0;
+};
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/jsthread.cpp b/src/mongo/scripting/mozjs/jsthread.cpp
new file mode 100644
index 00000000000..24b4c5d88af
--- /dev/null
+++ b/src/mongo/scripting/mozjs/jsthread.cpp
@@ -0,0 +1,274 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kQuery
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/scripting/mozjs/jsthread.h"
+
+#include <cstdio>
+
+#include "mongo/db/jsobj.h"
+#include "mongo/scripting/mozjs/implscope.h"
+#include "mongo/scripting/mozjs/valuereader.h"
+#include "mongo/scripting/mozjs/valuewriter.h"
+#include "mongo/stdx/condition_variable.h"
+#include "mongo/stdx/memory.h"
+#include "mongo/stdx/mutex.h"
+#include "mongo/stdx/thread.h"
+#include "mongo/util/log.h"
+#include "mongo/util/stacktrace.h"
+
+namespace mongo {
+namespace mozjs {
+
+const JSFunctionSpec JSThreadInfo::threadMethods[6] = {
+ MONGO_ATTACH_JS_FUNCTION(init),
+ MONGO_ATTACH_JS_FUNCTION(start),
+ MONGO_ATTACH_JS_FUNCTION(join),
+ MONGO_ATTACH_JS_FUNCTION(hasFailed),
+ MONGO_ATTACH_JS_FUNCTION(returnData),
+ JS_FS_END,
+};
+
+const JSFunctionSpec JSThreadInfo::freeFunctions[3] = {
+ MONGO_ATTACH_JS_FUNCTION(_threadInject),
+ MONGO_ATTACH_JS_FUNCTION(_scopedThreadInject),
+ JS_FS_END,
+};
+
+const char* const JSThreadInfo::className = "JSThread";
+
+/**
+ * Holder for JSThreads as exposed by fork() in the shell.
+ *
+ * The idea here is that we create a jsthread by taking a js function and its
+ * parameters and encoding them into a single bson object. Then we spawn a
+ * thread, have that thread do the work and join() it before checking it's
+ * result (serialized through bson). We can check errors at any time by
+ * checking a mutex guarded hasError().
+ */
+class JSThreadConfig {
+public:
+ JSThreadConfig(JSContext* cx, JS::CallArgs args)
+ : _started(false), _done(false), _sharedData(new SharedData()) {
+ uassert(ErrorCodes::JSInterpreterFailure, "need at least one argument", args.length() > 0);
+ uassert(ErrorCodes::JSInterpreterFailure,
+ "first argument must be a function",
+ args.get(0).isObject() && JS_ObjectIsFunction(cx, args.get(0).toObjectOrNull()));
+
+ BSONObjBuilder b;
+ for (unsigned i = 0; i < args.length(); ++i) {
+ // 10 decimal digits for a 32 bit unsigned, then 1 for the null
+ char buf[11];
+ std::sprintf(buf, "%i", i);
+
+ ValueWriter(cx, args.get(i)).writeThis(&b, buf);
+ }
+
+ _sharedData->_args = b.obj();
+ }
+
+ void start() {
+ uassert(ErrorCodes::JSInterpreterFailure, "Thread already started", !_started);
+
+ _thread = stdx::thread(JSThread(*this));
+ _started = true;
+ }
+
+ void join() {
+ uassert(ErrorCodes::JSInterpreterFailure, "Thread not running", _started && !_done);
+
+ _thread.join();
+ _done = true;
+ }
+
+ /**
+ * Returns true if the JSThread terminated as a result of an error
+ * during its execution, and false otherwise. This operation does
+ * not block, nor does it require join() to have been called.
+ */
+ bool hasFailed() const {
+ uassert(ErrorCodes::JSInterpreterFailure, "Thread not started", _started);
+
+ return _sharedData->getErrored();
+ }
+
+ BSONObj returnData() {
+ if (!_done)
+ join();
+
+ return _sharedData->_returnData;
+ }
+
+private:
+ /**
+ * SharedData between the calling thread and the callee
+ *
+ * JSThreadConfig doesn't always outlive its JSThread (for example, if the parent thread
+ * garbage collects the JSThreadConfig before the JSThread has finished running), so any
+ * data shared between them has to go in a shared_ptr.
+ */
+ class SharedData {
+ public:
+ SharedData() : _errored(false) {}
+
+ void setErrored(bool value) {
+ stdx::lock_guard<stdx::mutex> lck(_erroredMutex);
+ _errored = value;
+ }
+
+ bool getErrored() {
+ stdx::lock_guard<stdx::mutex> lck(_erroredMutex);
+ return _errored;
+ }
+
+ /**
+ * These two members aren't protected in any way, so you have to be
+ * mindful about how they're used. I.e. _args needs to be set before
+ * start() and _returnData can't be touched until after join().
+ */
+ BSONObj _args;
+ BSONObj _returnData;
+
+ private:
+ stdx::mutex _erroredMutex;
+ bool _errored;
+ };
+
+ /**
+ * The callable object used by stdx::thread
+ */
+ class JSThread {
+ public:
+ JSThread(JSThreadConfig& config) : _sharedData(config._sharedData) {}
+
+ void operator()() {
+ try {
+ MozJSImplScope scope(static_cast<MozJSScriptEngine*>(globalScriptEngine));
+
+ _sharedData->_returnData = scope.callThreadArgs(_sharedData->_args);
+ } catch (...) {
+ auto status = exceptionToStatus();
+
+ log() << "js thread threw c++ exception: " << status;
+ _sharedData->setErrored(true);
+ _sharedData->_returnData = BSON("ret" << BSONUndefined);
+ }
+ }
+
+ private:
+ std::shared_ptr<SharedData> _sharedData;
+ };
+
+ bool _started;
+ bool _done;
+ stdx::thread _thread;
+ std::shared_ptr<SharedData> _sharedData;
+};
+
+namespace {
+
+JSThreadConfig* getConfig(JSContext* cx, JS::CallArgs args) {
+ JS::RootedValue value(cx);
+ ObjectWrapper(cx, args.thisv()).getValue("_JSThreadConfig", &value);
+
+ if (!value.isObject())
+ uasserted(ErrorCodes::InternalError, "_JSThreadConfig not an object");
+
+ return static_cast<JSThreadConfig*>(JS_GetPrivate(value.toObjectOrNull()));
+}
+
+} // namespace
+
+void JSThreadInfo::finalize(JSFreeOp* fop, JSObject* obj) {
+ auto config = static_cast<JSThreadConfig*>(JS_GetPrivate(obj));
+
+ if (!config)
+ return;
+
+ delete config;
+}
+
+void JSThreadInfo::Functions::init(JSContext* cx, JS::CallArgs args) {
+ auto scope = getScope(cx);
+
+ JS::RootedObject obj(cx);
+ scope->getJSThreadProto().newObject(&obj);
+ JSThreadConfig* config = new JSThreadConfig(cx, args);
+ JS_SetPrivate(obj, config);
+
+ ObjectWrapper(cx, args.thisv()).setObject("_JSThreadConfig", obj);
+
+ args.rval().setUndefined();
+}
+
+void JSThreadInfo::Functions::start(JSContext* cx, JS::CallArgs args) {
+ getConfig(cx, args)->start();
+
+ args.rval().setUndefined();
+}
+
+void JSThreadInfo::Functions::join(JSContext* cx, JS::CallArgs args) {
+ getConfig(cx, args)->join();
+
+ args.rval().setUndefined();
+}
+
+void JSThreadInfo::Functions::hasFailed(JSContext* cx, JS::CallArgs args) {
+ args.rval().setBoolean(getConfig(cx, args)->hasFailed());
+}
+
+void JSThreadInfo::Functions::returnData(JSContext* cx, JS::CallArgs args) {
+ ValueReader(cx, args.rval())
+ .fromBSONElement(getConfig(cx, args)->returnData().firstElement(), true);
+}
+
+void JSThreadInfo::Functions::_threadInject(JSContext* cx, JS::CallArgs args) {
+ uassert(ErrorCodes::JSInterpreterFailure,
+ "threadInject takes exactly 1 argument",
+ args.length() == 1);
+ uassert(ErrorCodes::JSInterpreterFailure,
+ "threadInject needs to be passed a prototype",
+ args.get(0).isObject());
+
+ JS::RootedObject o(cx, args.get(0).toObjectOrNull());
+
+ if (!JS_DefineFunctions(cx, o, JSThreadInfo::threadMethods))
+ throwCurrentJSException(cx, ErrorCodes::JSInterpreterFailure, "Failed to define functions");
+
+ args.rval().setUndefined();
+}
+
+void JSThreadInfo::Functions::_scopedThreadInject(JSContext* cx, JS::CallArgs args) {
+ _threadInject(cx, args);
+}
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/jsthread.h b/src/mongo/scripting/mozjs/jsthread.h
new file mode 100644
index 00000000000..ff468d987ab
--- /dev/null
+++ b/src/mongo/scripting/mozjs/jsthread.h
@@ -0,0 +1,74 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#pragma once
+
+#include "mongo/scripting/mozjs/wraptype.h"
+
+namespace mongo {
+namespace mozjs {
+
+/**
+ * Helper for the JSThread javascript object
+ *
+ * The workflow is strange because we have a thing in javascript called a
+ * JSThread, but we don't actually get to construct it. Instead, we have to
+ * inject methods into that thing (via _threadInject) and hang our C++ thread
+ * separately (via init() on that type).
+ *
+ * To manage lifetime, we just add a field into the injected object that's our
+ * JSThread and add our holder in as our JSThread's private member.
+ */
+struct JSThreadInfo : public BaseInfo {
+ static void finalize(JSFreeOp* fop, JSObject* obj);
+
+ struct Functions {
+ MONGO_DEFINE_JS_FUNCTION(init);
+ MONGO_DEFINE_JS_FUNCTION(start);
+ MONGO_DEFINE_JS_FUNCTION(join);
+ MONGO_DEFINE_JS_FUNCTION(hasFailed);
+ MONGO_DEFINE_JS_FUNCTION(returnData);
+
+ MONGO_DEFINE_JS_FUNCTION(_threadInject);
+ MONGO_DEFINE_JS_FUNCTION(_scopedThreadInject);
+ };
+
+ /**
+ * Note that this isn't meant to supply methods for JSThread, it's just
+ * there to work with _threadInject. So the name isn't a mistake
+ */
+ static const JSFunctionSpec threadMethods[6];
+ static const JSFunctionSpec freeFunctions[3];
+
+ static const char* const className;
+ static const unsigned classFlags = JSCLASS_HAS_PRIVATE;
+ static const InstallType installType = InstallType::Private;
+};
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/maxkey.cpp b/src/mongo/scripting/mozjs/maxkey.cpp
new file mode 100644
index 00000000000..022ce347773
--- /dev/null
+++ b/src/mongo/scripting/mozjs/maxkey.cpp
@@ -0,0 +1,94 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/scripting/mozjs/maxkey.h"
+
+#include "mongo/scripting/mozjs/implscope.h"
+#include "mongo/scripting/mozjs/objectwrapper.h"
+#include "mongo/scripting/mozjs/valuereader.h"
+
+namespace mongo {
+namespace mozjs {
+
+const JSFunctionSpec MaxKeyInfo::methods[2] = {
+ MONGO_ATTACH_JS_FUNCTION(tojson), JS_FS_END,
+};
+
+const char* const MaxKeyInfo::className = "MaxKey";
+
+namespace {
+const char* const kSingleton = "singleton";
+} // namespace
+
+void MaxKeyInfo::construct(JSContext* cx, JS::CallArgs args) {
+ call(cx, args);
+}
+
+/**
+ * The idea here is that MinKey and MaxKey are singleton callable objects that
+ * return the singleton when called. This enables all instances to compare
+ * == and === to MinKey even if created by "new MinKey()" in JS.
+ */
+void MaxKeyInfo::call(JSContext* cx, JS::CallArgs args) {
+ auto scope = getScope(cx);
+
+ ObjectWrapper o(cx, scope->getMaxKeyProto().getProto());
+
+ JS::RootedValue val(cx);
+
+ if (!o.hasField(kSingleton)) {
+ JS::RootedObject thisv(cx);
+ scope->getMaxKeyProto().newObject(&thisv);
+
+ val.setObjectOrNull(thisv);
+ o.setValue(kSingleton, val);
+ } else {
+ o.getValue(kSingleton, &val);
+ }
+
+ args.rval().setObjectOrNull(val.toObjectOrNull());
+}
+
+void MaxKeyInfo::Functions::tojson(JSContext* cx, JS::CallArgs args) {
+ ValueReader(cx, args.rval()).fromStringData("{ \"$maxKey\" : 1 }");
+}
+
+void MaxKeyInfo::postInstall(JSContext* cx, JS::HandleObject global, JS::HandleObject proto) {
+ ObjectWrapper protoWrapper(cx, proto);
+
+ JS::RootedValue value(cx);
+ getScope(cx)->getMaxKeyProto().newObject(&value);
+
+ ObjectWrapper(cx, global).setValue("MaxKey", value);
+ protoWrapper.setValue(kSingleton, value);
+}
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/maxkey.h b/src/mongo/scripting/mozjs/maxkey.h
new file mode 100644
index 00000000000..ac5d937f157
--- /dev/null
+++ b/src/mongo/scripting/mozjs/maxkey.h
@@ -0,0 +1,59 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#pragma once
+
+#include "mongo/scripting/mozjs/wraptype.h"
+
+namespace mongo {
+namespace mozjs {
+
+/**
+ * The "MaxKey" Javascript object.
+ *
+ * These are slightly special, in that there is only one MaxKey object and
+ * whenever you call the constructor to make a new one you just get the
+ * "singleton" MaxKey from the prototype. See the postInstall for details.
+ */
+struct MaxKeyInfo : public BaseInfo {
+ static void call(JSContext* cx, JS::CallArgs args);
+ static void construct(JSContext* cx, JS::CallArgs args);
+
+ struct Functions {
+ MONGO_DEFINE_JS_FUNCTION(tojson);
+ };
+
+ static const JSFunctionSpec methods[2];
+
+ static void postInstall(JSContext* cx, JS::HandleObject global, JS::HandleObject proto);
+
+ static const char* const className;
+};
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/minkey.cpp b/src/mongo/scripting/mozjs/minkey.cpp
new file mode 100644
index 00000000000..fe06fa6bb49
--- /dev/null
+++ b/src/mongo/scripting/mozjs/minkey.cpp
@@ -0,0 +1,94 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/scripting/mozjs/minkey.h"
+
+#include "mongo/scripting/mozjs/implscope.h"
+#include "mongo/scripting/mozjs/objectwrapper.h"
+#include "mongo/scripting/mozjs/valuereader.h"
+
+namespace mongo {
+namespace mozjs {
+
+const JSFunctionSpec MinKeyInfo::methods[2] = {
+ MONGO_ATTACH_JS_FUNCTION(tojson), JS_FS_END,
+};
+
+const char* const MinKeyInfo::className = "MinKey";
+
+namespace {
+const char* const kSingleton = "singleton";
+} // namespace
+
+void MinKeyInfo::construct(JSContext* cx, JS::CallArgs args) {
+ call(cx, args);
+}
+
+/**
+ * The idea here is that MinKey and MaxKey are singleton callable objects that
+ * return the singleton when called. This enables all instances to compare
+ * == and === to MinKey even if created by "new MinKey()" in JS.
+ */
+void MinKeyInfo::call(JSContext* cx, JS::CallArgs args) {
+ auto scope = getScope(cx);
+
+ ObjectWrapper o(cx, scope->getMinKeyProto().getProto());
+
+ JS::RootedValue val(cx);
+
+ if (!o.hasField(kSingleton)) {
+ JS::RootedObject thisv(cx);
+ scope->getMinKeyProto().newObject(&thisv);
+
+ val.setObjectOrNull(thisv);
+ o.setValue(kSingleton, val);
+ } else {
+ o.getValue(kSingleton, &val);
+ }
+
+ args.rval().setObjectOrNull(val.toObjectOrNull());
+}
+
+void MinKeyInfo::Functions::tojson(JSContext* cx, JS::CallArgs args) {
+ ValueReader(cx, args.rval()).fromStringData("{ \"$minKey\" : 1 }");
+}
+
+void MinKeyInfo::postInstall(JSContext* cx, JS::HandleObject global, JS::HandleObject proto) {
+ ObjectWrapper protoWrapper(cx, proto);
+
+ JS::RootedValue value(cx);
+ getScope(cx)->getMinKeyProto().newObject(&value);
+
+ ObjectWrapper(cx, global).setValue("MinKey", value);
+ protoWrapper.setValue(kSingleton, value);
+}
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/minkey.h b/src/mongo/scripting/mozjs/minkey.h
new file mode 100644
index 00000000000..caea91d54ac
--- /dev/null
+++ b/src/mongo/scripting/mozjs/minkey.h
@@ -0,0 +1,59 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#pragma once
+
+#include "mongo/scripting/mozjs/wraptype.h"
+
+namespace mongo {
+namespace mozjs {
+
+/**
+ * The "MinKey" Javascript object.
+ *
+ * These are slightly special, in that there is only one MinKey object and
+ * whenever you call the constructor to make a new one you just get the
+ * "singleton" MinKey from the prototype. See the postInstall for details.
+ */
+struct MinKeyInfo : public BaseInfo {
+ static void call(JSContext* cx, JS::CallArgs args);
+ static void construct(JSContext* cx, JS::CallArgs args);
+
+ struct Functions {
+ MONGO_DEFINE_JS_FUNCTION(tojson);
+ };
+
+ static const JSFunctionSpec methods[2];
+
+ static void postInstall(JSContext* cx, JS::HandleObject global, JS::HandleObject proto);
+
+ static const char* const className;
+};
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/mongo.cpp b/src/mongo/scripting/mozjs/mongo.cpp
new file mode 100644
index 00000000000..f1f69d0d39c
--- /dev/null
+++ b/src/mongo/scripting/mozjs/mongo.cpp
@@ -0,0 +1,565 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/scripting/mozjs/mongo.h"
+
+#include "mongo/client/dbclientinterface.h"
+#include "mongo/client/native_sasl_client_session.h"
+#include "mongo/client/sasl_client_authenticate.h"
+#include "mongo/client/sasl_client_session.h"
+#include "mongo/db/namespace_string.h"
+#include "mongo/scripting/mozjs/cursor.h"
+#include "mongo/scripting/mozjs/implscope.h"
+#include "mongo/scripting/mozjs/objectwrapper.h"
+#include "mongo/scripting/mozjs/valuereader.h"
+#include "mongo/scripting/mozjs/valuewriter.h"
+#include "mongo/stdx/memory.h"
+
+namespace mongo {
+namespace mozjs {
+
+const JSFunctionSpec MongoBase::methods[13] = {
+ MONGO_ATTACH_JS_FUNCTION(auth),
+ MONGO_ATTACH_JS_FUNCTION(copyDatabaseWithSCRAM),
+ MONGO_ATTACH_JS_FUNCTION(cursorFromId),
+ MONGO_ATTACH_JS_FUNCTION(find),
+ MONGO_ATTACH_JS_FUNCTION(getClientRPCProtocols),
+ MONGO_ATTACH_JS_FUNCTION(getServerRPCProtocols),
+ MONGO_ATTACH_JS_FUNCTION(insert),
+ MONGO_ATTACH_JS_FUNCTION(logout),
+ MONGO_ATTACH_JS_FUNCTION(remove),
+ MONGO_ATTACH_JS_FUNCTION(runCommand),
+ MONGO_ATTACH_JS_FUNCTION(setClientRPCProtocols),
+ MONGO_ATTACH_JS_FUNCTION(update),
+ JS_FS_END,
+};
+
+const char* const MongoBase::className = "Mongo";
+
+const JSFunctionSpec MongoExternalInfo::freeFunctions[2] = {
+ MONGO_ATTACH_JS_FUNCTION(load), JS_FS_END,
+};
+
+namespace {
+DBClientBase* getConnection(JS::CallArgs& args) {
+ return static_cast<std::shared_ptr<DBClientBase>*>(JS_GetPrivate(args.thisv().toObjectOrNull()))
+ ->get();
+}
+
+void setCursor(JS::HandleObject target,
+ std::unique_ptr<DBClientCursor> cursor,
+ JS::CallArgs& args) {
+ auto client =
+ static_cast<std::shared_ptr<DBClientBase>*>(JS_GetPrivate(args.thisv().toObjectOrNull()));
+
+ // Copy the client shared pointer to up the refcount
+ JS_SetPrivate(target, new CursorInfo::CursorHolder(std::move(cursor), *client));
+}
+} // namespace
+
+void MongoBase::finalize(JSFreeOp* fop, JSObject* obj) {
+ auto conn = static_cast<std::shared_ptr<DBClientBase>*>(JS_GetPrivate(obj));
+
+ if (conn) {
+ delete conn;
+ }
+}
+
+void MongoBase::Functions::runCommand(JSContext* cx, JS::CallArgs args) {
+ if (args.length() != 3)
+ uasserted(ErrorCodes::BadValue, "runCommand needs 3 args");
+
+ if (!args.get(0).isString())
+ uasserted(ErrorCodes::BadValue, "the database parameter to runCommand must be a string");
+
+ if (!args.get(1).isObject())
+ uasserted(ErrorCodes::BadValue, "the cmdObj parameter to runCommand must be an object");
+
+ if (!args.get(2).isNumber())
+ uasserted(ErrorCodes::BadValue, "the options parameter to runCommand must be a number");
+
+ auto conn = getConnection(args);
+
+ std::string database = ValueWriter(cx, args.get(0)).toString();
+
+ BSONObj cmdObj = ValueWriter(cx, args.get(1)).toBSON();
+
+ int queryOptions = ValueWriter(cx, args.get(2)).toInt32();
+ BSONObj cmdRes;
+ conn->runCommand(database, cmdObj, cmdRes, queryOptions);
+
+ // the returned object is not read only as some of our tests depend on modifying it.
+ ValueReader(cx, args.rval()).fromBSON(cmdRes, false /* read only */);
+}
+
+void MongoBase::Functions::find(JSContext* cx, JS::CallArgs args) {
+ auto scope = getScope(cx);
+
+ if (args.length() != 7)
+ uasserted(ErrorCodes::BadValue, "find needs 7 args");
+
+ if (!args.get(1).isObject())
+ uasserted(ErrorCodes::BadValue, "needs to be an object");
+
+ auto conn = getConnection(args);
+
+ std::string ns = ValueWriter(cx, args.get(0)).toString();
+
+ BSONObj fields;
+ BSONObj q = ValueWriter(cx, args.get(1)).toBSON();
+
+ bool haveFields = false;
+
+ if (args.get(2).isObject()) {
+ size_t i = 0;
+
+ JS::RootedObject obj(cx, args.get(2).toObjectOrNull());
+
+ ObjectWrapper(cx, obj).enumerate([&i](jsid) { ++i; });
+
+ if (i > 0)
+ haveFields = true;
+ }
+
+ if (haveFields)
+ fields = ValueWriter(cx, args.get(2)).toBSON();
+
+ int nToReturn = ValueWriter(cx, args.get(3)).toInt32();
+ int nToSkip = ValueWriter(cx, args.get(4)).toInt32();
+ int batchSize = ValueWriter(cx, args.get(5)).toInt32();
+ int options = ValueWriter(cx, args.get(6)).toInt32();
+
+ std::unique_ptr<DBClientCursor> cursor(
+ conn->query(ns, q, nToReturn, nToSkip, haveFields ? &fields : NULL, options, batchSize));
+ if (!cursor.get()) {
+ uasserted(ErrorCodes::InternalError, "error doing query: failed");
+ }
+
+ JS::RootedObject c(cx);
+ scope->getCursorProto().newInstance(&c);
+
+ setCursor(c, std::move(cursor), args);
+
+ args.rval().setObjectOrNull(c);
+}
+
+void MongoBase::Functions::insert(JSContext* cx, JS::CallArgs args) {
+ auto scope = getScope(cx);
+
+ if (args.length() != 3)
+ uasserted(ErrorCodes::BadValue, "insert needs 3 args");
+
+ if (!args.get(1).isObject())
+ uasserted(ErrorCodes::BadValue, "attempted to insert a non-object");
+
+ ObjectWrapper o(cx, args.thisv());
+
+ if (o.hasField("readOnly") && o.getBoolean("readOnly"))
+ uasserted(ErrorCodes::BadValue, "js db in read only mode");
+
+ auto conn = getConnection(args);
+
+ std::string ns = ValueWriter(cx, args.get(0)).toString();
+
+ int flags = ValueWriter(cx, args.get(2)).toInt32();
+
+ auto addId = [cx, scope](JS::HandleValue value) {
+ if (!value.isObject())
+ uasserted(ErrorCodes::BadValue, "attempted to insert a non-object type");
+
+ JS::RootedObject elementObj(cx, value.toObjectOrNull());
+
+ ObjectWrapper ele(cx, elementObj);
+
+ if (!ele.hasField("_id")) {
+ JS::RootedValue value(cx);
+ scope->getOidProto().newInstance(&value);
+ ele.setValue("_id", value);
+ }
+
+ return ValueWriter(cx, value).toBSON();
+ };
+
+ if (args.get(1).isObject() && JS_IsArrayObject(cx, args.get(1))) {
+ JS::RootedObject obj(cx, args.get(1).toObjectOrNull());
+ ObjectWrapper array(cx, obj);
+
+ std::vector<BSONObj> bos;
+
+ bool foundElement = false;
+
+ array.enumerate([&](JS::HandleId id) {
+ foundElement = true;
+
+ JS::RootedValue value(cx);
+ array.getValue(id, &value);
+
+ bos.push_back(addId(value));
+ });
+
+ if (!foundElement)
+ uasserted(ErrorCodes::BadValue, "attempted to insert an empty array");
+
+ conn->insert(ns, bos, flags);
+ } else {
+ conn->insert(ns, addId(args.get(1)));
+ }
+
+ args.rval().setUndefined();
+}
+
+void MongoBase::Functions::remove(JSContext* cx, JS::CallArgs args) {
+ if (!(args.length() == 2 || args.length() == 3))
+ uasserted(ErrorCodes::BadValue, "remove needs 2 or 3 args");
+
+ if (!(args.get(1).isObject()))
+ uasserted(ErrorCodes::BadValue, "attempted to remove a non-object");
+
+ ObjectWrapper o(cx, args.thisv());
+
+ if (o.hasField("readOnly") && o.getBoolean("readOnly"))
+ uasserted(ErrorCodes::BadValue, "js db in read only mode");
+
+ auto conn = getConnection(args);
+ std::string ns = ValueWriter(cx, args.get(0)).toString();
+
+ BSONObj bson = ValueWriter(cx, args.get(1)).toBSON();
+
+ bool justOne = false;
+ if (args.length() > 2) {
+ justOne = args.get(2).toBoolean();
+ }
+
+ conn->remove(ns, bson, justOne);
+ args.rval().setUndefined();
+}
+
+void MongoBase::Functions::update(JSContext* cx, JS::CallArgs args) {
+ if (args.length() < 3)
+ uasserted(ErrorCodes::BadValue, "update needs at least 3 args");
+
+ if (!args.get(1).isObject())
+ uasserted(ErrorCodes::BadValue, "1st param to update has to be an object");
+
+ if (!args.get(2).isObject())
+ uasserted(ErrorCodes::BadValue, "2nd param to update has to be an object");
+
+ ObjectWrapper o(cx, args.thisv());
+
+ if (o.hasField("readOnly") && o.getBoolean("readOnly"))
+ uasserted(ErrorCodes::BadValue, "js db in read only mode");
+
+ auto conn = getConnection(args);
+ std::string ns = ValueWriter(cx, args.get(0)).toString();
+
+ BSONObj q1 = ValueWriter(cx, args.get(1)).toBSON();
+ BSONObj o1 = ValueWriter(cx, args.get(2)).toBSON();
+
+ bool upsert = args.length() > 3 && args.get(3).isBoolean() && args.get(3).toBoolean();
+ bool multi = args.length() > 4 && args.get(4).isBoolean() && args.get(4).toBoolean();
+
+ conn->update(ns, q1, o1, upsert, multi);
+ args.rval().setUndefined();
+}
+
+void MongoBase::Functions::auth(JSContext* cx, JS::CallArgs args) {
+ auto conn = getConnection(args);
+ if (!conn)
+ uasserted(ErrorCodes::BadValue, "no connection");
+
+ BSONObj params;
+ switch (args.length()) {
+ case 1:
+ params = ValueWriter(cx, args.get(0)).toBSON();
+ break;
+ case 3:
+ params = BSON(saslCommandMechanismFieldName
+ << "MONGODB-CR" << saslCommandUserDBFieldName
+ << ValueWriter(cx, args[0]).toString() << saslCommandUserFieldName
+ << ValueWriter(cx, args[1]).toString() << saslCommandPasswordFieldName
+ << ValueWriter(cx, args[2]).toString());
+ break;
+ default:
+ uasserted(ErrorCodes::BadValue, "mongoAuth takes 1 object or 3 string arguments");
+ }
+
+ conn->auth(params);
+
+ args.rval().setBoolean(true);
+}
+
+void MongoBase::Functions::logout(JSContext* cx, JS::CallArgs args) {
+ if (args.length() != 1)
+ uasserted(ErrorCodes::BadValue, "logout needs 1 arg");
+
+ BSONObj ret;
+
+ std::string db = ValueWriter(cx, args.get(0)).toString();
+
+ auto conn = getConnection(args);
+ if (conn) {
+ conn->logout(db, ret);
+ }
+
+ ValueReader(cx, args.rval()).fromBSON(ret, false);
+}
+
+void MongoBase::Functions::cursorFromId(JSContext* cx, JS::CallArgs args) {
+ auto scope = getScope(cx);
+
+ if (!(args.length() == 2 || args.length() == 3))
+ uasserted(ErrorCodes::BadValue, "cursorFromId needs 2 or 3 args");
+
+ if (!scope->getNumberLongProto().instanceOf(args.get(1)))
+ uasserted(ErrorCodes::BadValue, "2nd arg must be a NumberLong");
+
+ if (!(args.get(2).isNumber() || args.get(2).isUndefined()))
+ uasserted(ErrorCodes::BadValue, "3rd arg must be a js Number");
+
+ auto conn = getConnection(args);
+
+ std::string ns = ValueWriter(cx, args.get(0)).toString();
+
+ long long cursorId = NumberLongInfo::ToNumberLong(cx, args.get(1));
+
+ auto cursor = stdx::make_unique<DBClientCursor>(conn, ns, cursorId, 0, 0);
+
+ if (args.get(2).isNumber())
+ cursor->setBatchSize(ValueWriter(cx, args.get(2)).toInt32());
+
+ JS::RootedObject c(cx);
+ scope->getCursorProto().newInstance(&c);
+
+ setCursor(c, std::move(cursor), args);
+
+ args.rval().setObjectOrNull(c);
+}
+
+void MongoBase::Functions::copyDatabaseWithSCRAM(JSContext* cx, JS::CallArgs args) {
+ auto conn = getConnection(args);
+
+ if (!conn)
+ uasserted(ErrorCodes::BadValue, "no connection");
+
+ if (args.length() != 5)
+ uasserted(ErrorCodes::BadValue, "copyDatabase needs 5 arg");
+
+ // copyDatabase(fromdb, todb, fromhost, username, password);
+ std::string fromDb = ValueWriter(cx, args.get(0)).toString();
+ std::string toDb = ValueWriter(cx, args.get(1)).toString();
+ std::string fromHost = ValueWriter(cx, args.get(2)).toString();
+ std::string user = ValueWriter(cx, args.get(3)).toString();
+ std::string password = ValueWriter(cx, args.get(4)).toString();
+
+ std::string hashedPwd = DBClientWithCommands::createPasswordDigest(user, password);
+
+ std::unique_ptr<SaslClientSession> session(new NativeSaslClientSession());
+
+ session->setParameter(SaslClientSession::parameterMechanism, "SCRAM-SHA-1");
+ session->setParameter(SaslClientSession::parameterUser, user);
+ session->setParameter(SaslClientSession::parameterPassword, hashedPwd);
+ session->initialize();
+
+ BSONObj saslFirstCommandPrefix =
+ BSON("copydbsaslstart" << 1 << "fromhost" << fromHost << "fromdb" << fromDb
+ << saslCommandMechanismFieldName << "SCRAM-SHA-1");
+
+ BSONObj saslFollowupCommandPrefix =
+ BSON("copydb" << 1 << "fromhost" << fromHost << "fromdb" << fromDb << "todb" << toDb);
+
+ BSONObj saslCommandPrefix = saslFirstCommandPrefix;
+ BSONObj inputObj = BSON(saslCommandPayloadFieldName << "");
+ bool isServerDone = false;
+
+ while (!session->isDone()) {
+ std::string payload;
+ BSONType type;
+
+ Status status = saslExtractPayload(inputObj, &payload, &type);
+ uassertStatusOK(status);
+
+ std::string responsePayload;
+ status = session->step(payload, &responsePayload);
+ uassertStatusOK(status);
+
+ BSONObjBuilder commandBuilder;
+
+ commandBuilder.appendElements(saslCommandPrefix);
+ commandBuilder.appendBinData(saslCommandPayloadFieldName,
+ static_cast<int>(responsePayload.size()),
+ BinDataGeneral,
+ responsePayload.c_str());
+ BSONElement conversationId = inputObj[saslCommandConversationIdFieldName];
+ if (!conversationId.eoo())
+ commandBuilder.append(conversationId);
+
+ BSONObj command = commandBuilder.obj();
+
+ bool ok = conn->runCommand("admin", command, inputObj);
+
+ ErrorCodes::Error code =
+ ErrorCodes::fromInt(inputObj[saslCommandCodeFieldName].numberInt());
+
+ if (!ok || code != ErrorCodes::OK) {
+ if (code == ErrorCodes::OK)
+ code = ErrorCodes::UnknownError;
+
+ ValueReader(cx, args.rval()).fromBSON(inputObj, true);
+ return;
+ }
+
+ isServerDone = inputObj[saslCommandDoneFieldName].trueValue();
+ saslCommandPrefix = saslFollowupCommandPrefix;
+ }
+
+ if (!isServerDone) {
+ uasserted(ErrorCodes::InternalError, "copydb client finished before server.");
+ }
+
+ ValueReader(cx, args.rval()).fromBSON(inputObj, true);
+}
+
+void MongoBase::Functions::getClientRPCProtocols(JSContext* cx, JS::CallArgs args) {
+ auto conn = getConnection(args);
+
+ if (args.length() != 0)
+ uasserted(ErrorCodes::BadValue, "getClientRPCProtocols takes no args");
+
+ auto clientRPCProtocols = rpc::toString(conn->getClientRPCProtocols());
+ uassertStatusOK(clientRPCProtocols);
+
+ auto protoStr = clientRPCProtocols.getValue().toString();
+
+ ValueReader(cx, args.rval()).fromStringData(protoStr);
+}
+
+void MongoBase::Functions::setClientRPCProtocols(JSContext* cx, JS::CallArgs args) {
+ auto conn = getConnection(args);
+
+ if (args.length() != 1)
+ uasserted(ErrorCodes::BadValue, "setClientRPCProtocols needs 1 arg");
+ if (!args.get(0).isString())
+ uasserted(ErrorCodes::BadValue, "first argument to setClientRPCProtocols must be a string");
+
+ std::string rpcProtosStr = ValueWriter(cx, args.get(0)).toString();
+
+ auto clientRPCProtocols = rpc::parseProtocolSet(rpcProtosStr);
+ uassertStatusOK(clientRPCProtocols);
+
+ conn->setClientRPCProtocols(clientRPCProtocols.getValue());
+
+ args.rval().setUndefined();
+}
+
+void MongoBase::Functions::getServerRPCProtocols(JSContext* cx, JS::CallArgs args) {
+ auto conn = getConnection(args);
+
+ if (args.length() != 0)
+ uasserted(ErrorCodes::BadValue, "getServerRPCProtocols takes no args");
+
+ auto serverRPCProtocols = rpc::toString(conn->getServerRPCProtocols());
+ uassertStatusOK(serverRPCProtocols);
+
+ auto protoStr = serverRPCProtocols.getValue().toString();
+
+ ValueReader(cx, args.rval()).fromStringData(protoStr);
+}
+
+void MongoLocalInfo::construct(JSContext* cx, JS::CallArgs args) {
+ auto scope = getScope(cx);
+
+ if (args.length() != 0)
+ uasserted(ErrorCodes::BadValue, "local Mongo constructor takes no args");
+
+ std::unique_ptr<DBClientBase> conn;
+
+ conn.reset(createDirectClient(scope->getOpContext()));
+
+ JS::RootedObject thisv(cx);
+ scope->getMongoLocalProto().newObject(&thisv);
+ ObjectWrapper o(cx, thisv);
+
+ JS_SetPrivate(thisv, new std::shared_ptr<DBClientBase>(conn.release()));
+
+ o.setBoolean("slaveOk", false);
+ o.setString("host", "EMBEDDED");
+
+ args.rval().setObjectOrNull(thisv);
+}
+
+void MongoExternalInfo::construct(JSContext* cx, JS::CallArgs args) {
+ auto scope = getScope(cx);
+
+ std::string host("127.0.0.1");
+
+ if (args.length() > 0 && args.get(0).isString()) {
+ host = ValueWriter(cx, args.get(0)).toString();
+ }
+
+ auto statusWithHost = ConnectionString::parse(host);
+ uassertStatusOK(statusWithHost);
+
+ const ConnectionString cs(statusWithHost.getValue());
+
+ std::string errmsg;
+ std::unique_ptr<DBClientBase> conn(cs.connect(errmsg));
+
+ if (!conn.get()) {
+ uasserted(ErrorCodes::InternalError, errmsg);
+ }
+
+ JS::RootedObject thisv(cx);
+ scope->getMongoExternalProto().newObject(&thisv);
+ ObjectWrapper o(cx, thisv);
+
+ JS_SetPrivate(thisv, new std::shared_ptr<DBClientBase>(conn.release()));
+
+ o.setBoolean("slaveOk", false);
+ o.setString("host", host);
+
+ args.rval().setObjectOrNull(thisv);
+}
+
+void MongoExternalInfo::Functions::load(JSContext* cx, JS::CallArgs args) {
+ auto scope = getScope(cx);
+
+ for (unsigned i = 0; i < args.length(); ++i) {
+ std::string filename = ValueWriter(cx, args.get(i)).toString();
+
+ if (!scope->execFile(filename, false, true)) {
+ uasserted(ErrorCodes::BadValue, std::string("error loading js file: ") + filename);
+ }
+ }
+
+ args.rval().setBoolean(true);
+}
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/mongo.h b/src/mongo/scripting/mozjs/mongo.h
new file mode 100644
index 00000000000..35815da5455
--- /dev/null
+++ b/src/mongo/scripting/mozjs/mongo.h
@@ -0,0 +1,88 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#pragma once
+
+#include "mongo/scripting/mozjs/wraptype.h"
+
+namespace mongo {
+namespace mozjs {
+
+/**
+ * Shared code for the "Mongo" javascript object.
+ *
+ * The idea here is that there is a lot of shared functionality between the
+ * "Mongo" we see in the shell and the "Mongo" in dbeval. So we provide one
+ * info type with common code and differentiate with varying constructors.
+ */
+struct MongoBase : public BaseInfo {
+ static void finalize(JSFreeOp* fop, JSObject* obj);
+
+ struct Functions {
+ MONGO_DEFINE_JS_FUNCTION(auth);
+ MONGO_DEFINE_JS_FUNCTION(copyDatabaseWithSCRAM);
+ MONGO_DEFINE_JS_FUNCTION(cursorFromId);
+ MONGO_DEFINE_JS_FUNCTION(find);
+ MONGO_DEFINE_JS_FUNCTION(getClientRPCProtocols);
+ MONGO_DEFINE_JS_FUNCTION(getServerRPCProtocols);
+ MONGO_DEFINE_JS_FUNCTION(insert);
+ MONGO_DEFINE_JS_FUNCTION(logout);
+ MONGO_DEFINE_JS_FUNCTION(remove);
+ MONGO_DEFINE_JS_FUNCTION(runCommand);
+ MONGO_DEFINE_JS_FUNCTION(setClientRPCProtocols);
+ MONGO_DEFINE_JS_FUNCTION(update);
+ };
+
+ static const JSFunctionSpec methods[13];
+
+ static const char* const className;
+ static const unsigned classFlags = JSCLASS_HAS_PRIVATE;
+};
+
+/**
+ * The dbeval variant of "Mongo"
+ */
+struct MongoLocalInfo : public MongoBase {
+ static void construct(JSContext* cx, JS::CallArgs args);
+};
+
+/**
+ * The shell variant of "Mongo"
+ */
+struct MongoExternalInfo : public MongoBase {
+ static void construct(JSContext* cx, JS::CallArgs args);
+
+ struct Functions {
+ MONGO_DEFINE_JS_FUNCTION(load);
+ };
+
+ static const JSFunctionSpec freeFunctions[2];
+};
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/nativefunction.cpp b/src/mongo/scripting/mozjs/nativefunction.cpp
new file mode 100644
index 00000000000..010b6a13587
--- /dev/null
+++ b/src/mongo/scripting/mozjs/nativefunction.cpp
@@ -0,0 +1,124 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/scripting/mozjs/nativefunction.h"
+
+#include <cstdio>
+
+#include "mongo/scripting/mozjs/implscope.h"
+#include "mongo/scripting/mozjs/objectwrapper.h"
+#include "mongo/scripting/mozjs/valuereader.h"
+#include "mongo/scripting/mozjs/valuewriter.h"
+#include "mongo/util/mongoutils/str.h"
+
+namespace mongo {
+namespace mozjs {
+
+const char* const NativeFunctionInfo::inheritFrom = "Function";
+const char* const NativeFunctionInfo::className = "NativeFunction";
+
+const JSFunctionSpec NativeFunctionInfo::methods[2] = {
+ MONGO_ATTACH_JS_FUNCTION(toString), JS_FS_END,
+};
+
+namespace {
+
+/**
+ * Holder for the caller of ::make()'s callback function and context pointer
+ */
+class NativeHolder {
+public:
+ NativeHolder(NativeFunction func, void* ctx) : _func(func), _ctx(ctx) {}
+
+ NativeFunction _func;
+ void* _ctx;
+};
+
+NativeHolder* getHolder(JS::CallArgs args) {
+ return static_cast<NativeHolder*>(JS_GetPrivate(&args.callee()));
+}
+
+} // namespace
+
+void NativeFunctionInfo::construct(JSContext* cx, JS::CallArgs args) {
+ auto scope = getScope(cx);
+
+ scope->getNativeFunctionProto().newObject(args.rval());
+}
+
+void NativeFunctionInfo::call(JSContext* cx, JS::CallArgs args) {
+ auto holder = getHolder(args);
+
+ BSONObjBuilder bob;
+
+ for (unsigned i = 0; i < args.length(); i++) {
+ // 11 is enough here. unsigned's are only 32 bits, and 1 << 32 is only
+ // 10 decimal digits. +1 for the null and we're only at 11.
+ char buf[11];
+ std::sprintf(buf, "%i", i);
+
+ ValueWriter(cx, args.get(i)).writeThis(&bob, buf);
+ }
+
+ BSONObj out = holder->_func(bob.obj(), holder->_ctx);
+
+ ValueReader(cx, args.rval()).fromBSONElement(out.firstElement(), false);
+}
+
+void NativeFunctionInfo::finalize(JSFreeOp* fop, JSObject* obj) {
+ auto holder = static_cast<NativeHolder*>(JS_GetPrivate(obj));
+
+ if (holder)
+ delete holder;
+}
+
+void NativeFunctionInfo::Functions::toString(JSContext* cx, JS::CallArgs args) {
+ ObjectWrapper o(cx, args.thisv());
+
+ str::stream ss;
+ ss << "[native code]";
+
+ ValueReader(cx, args.rval()).fromStringData(ss.operator std::string());
+}
+
+
+void NativeFunctionInfo::make(JSContext* cx,
+ JS::MutableHandleObject obj,
+ NativeFunction function,
+ void* data) {
+ auto scope = getScope(cx);
+
+ scope->getNativeFunctionProto().newInstance(obj);
+
+ JS_SetPrivate(obj, new NativeHolder(function, data));
+}
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/nativefunction.h b/src/mongo/scripting/mozjs/nativefunction.h
new file mode 100644
index 00000000000..0467b2c4743
--- /dev/null
+++ b/src/mongo/scripting/mozjs/nativefunction.h
@@ -0,0 +1,73 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#pragma once
+
+#include "mongo/scripting/engine.h"
+#include "mongo/scripting/mozjs/wraptype.h"
+
+namespace mongo {
+namespace mozjs {
+
+/**
+ * Wrapper for JS Interpreter agnostic functions. Think mapReduce, or any use
+ * case that can tolerate automatic json <-> bson translation.
+ *
+ * The business end of the shim methods comes via ::call(). These types are
+ * invokable as js functions, with a little bit of automatic translation for
+ * arguments.
+ *
+ * This inherits from the global Function type.
+ *
+ * Also note that installType is private. So you can only get NativeFunctions
+ * in JS via ::make() from C++.
+ */
+struct NativeFunctionInfo : public BaseInfo {
+ static void construct(JSContext* cx, JS::CallArgs args);
+ static void call(JSContext* cx, JS::CallArgs args);
+ static void finalize(JSFreeOp* fop, JSObject* obj);
+
+ static const char* const inheritFrom;
+ static const char* const className;
+ static const unsigned classFlags = JSCLASS_HAS_PRIVATE;
+ static const InstallType installType = InstallType::Private;
+
+ struct Functions {
+ MONGO_DEFINE_JS_FUNCTION(toString);
+ };
+
+ static const JSFunctionSpec methods[2];
+
+ static void make(JSContext* cx,
+ JS::MutableHandleObject obj,
+ NativeFunction function,
+ void* data);
+};
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/numberint.cpp b/src/mongo/scripting/mozjs/numberint.cpp
new file mode 100644
index 00000000000..1704154ac74
--- /dev/null
+++ b/src/mongo/scripting/mozjs/numberint.cpp
@@ -0,0 +1,112 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/scripting/mozjs/numberint.h"
+
+#include "mongo/scripting/mozjs/implscope.h"
+#include "mongo/scripting/mozjs/objectwrapper.h"
+#include "mongo/scripting/mozjs/valuereader.h"
+#include "mongo/scripting/mozjs/valuewriter.h"
+#include "mongo/util/mongoutils/str.h"
+
+namespace mongo {
+namespace mozjs {
+
+const JSFunctionSpec NumberIntInfo::methods[4] = {
+ MONGO_ATTACH_JS_FUNCTION(toNumber),
+ MONGO_ATTACH_JS_FUNCTION(toString),
+ MONGO_ATTACH_JS_FUNCTION(valueOf),
+ JS_FS_END,
+};
+
+const char* const NumberIntInfo::className = "NumberInt";
+
+void NumberIntInfo::finalize(JSFreeOp* fop, JSObject* obj) {
+ auto x = static_cast<int*>(JS_GetPrivate(obj));
+
+ if (x)
+ delete x;
+}
+
+int NumberIntInfo::ToNumberInt(JSContext* cx, JS::HandleValue thisv) {
+ auto x = static_cast<int*>(JS_GetPrivate(thisv.toObjectOrNull()));
+
+ return x ? *x : 0;
+}
+
+int NumberIntInfo::ToNumberInt(JSContext* cx, JS::HandleObject thisv) {
+ auto x = static_cast<int*>(JS_GetPrivate(thisv));
+
+ return x ? *x : 0;
+}
+
+void NumberIntInfo::Functions::valueOf(JSContext* cx, JS::CallArgs args) {
+ int out = NumberIntInfo::ToNumberInt(cx, args.thisv());
+
+ args.rval().setInt32(out);
+}
+
+void NumberIntInfo::Functions::toNumber(JSContext* cx, JS::CallArgs args) {
+ valueOf(cx, args);
+}
+
+void NumberIntInfo::Functions::toString(JSContext* cx, JS::CallArgs args) {
+ int val = NumberIntInfo::ToNumberInt(cx, args.thisv());
+
+ str::stream ss;
+ ss << "NumberInt(" << val << ")";
+
+ ValueReader(cx, args.rval()).fromStringData(ss.operator std::string());
+}
+
+void NumberIntInfo::construct(JSContext* cx, JS::CallArgs args) {
+ auto scope = getScope(cx);
+
+ JS::RootedObject thisv(cx);
+
+ scope->getNumberIntProto().newObject(&thisv);
+
+ int32_t x = 0;
+
+ if (args.length() == 0) {
+ // Do nothing
+ } else if (args.length() == 1) {
+ x = ValueWriter(cx, args.get(0)).toInt32();
+ } else {
+ uasserted(ErrorCodes::BadValue, "NumberInt takes 0 or 1 arguments");
+ }
+
+ JS_SetPrivate(thisv, new int(x));
+
+ args.rval().setObjectOrNull(thisv);
+}
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/numberint.h b/src/mongo/scripting/mozjs/numberint.h
new file mode 100644
index 00000000000..378c3a0d57e
--- /dev/null
+++ b/src/mongo/scripting/mozjs/numberint.h
@@ -0,0 +1,61 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#pragma once
+
+#include "mongo/scripting/mozjs/wraptype.h"
+
+namespace mongo {
+namespace mozjs {
+
+/**
+ * The "NumberInt" Javascript object.
+ *
+ * Wraps an actual c++ 'int' as its private member
+ */
+struct NumberIntInfo : public BaseInfo {
+ static void construct(JSContext* cx, JS::CallArgs args);
+ static void finalize(JSFreeOp* fop, JSObject* obj);
+
+ struct Functions {
+ MONGO_DEFINE_JS_FUNCTION(toNumber);
+ MONGO_DEFINE_JS_FUNCTION(toString);
+ MONGO_DEFINE_JS_FUNCTION(valueOf);
+ };
+
+ static const JSFunctionSpec methods[4];
+
+ static const char* const className;
+ static const unsigned classFlags = JSCLASS_HAS_PRIVATE;
+
+ static int ToNumberInt(JSContext* cx, JS::HandleObject object);
+ static int ToNumberInt(JSContext* cx, JS::HandleValue value);
+};
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/numberlong.cpp b/src/mongo/scripting/mozjs/numberlong.cpp
new file mode 100644
index 00000000000..f8955689c9e
--- /dev/null
+++ b/src/mongo/scripting/mozjs/numberlong.cpp
@@ -0,0 +1,164 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/scripting/mozjs/numberlong.h"
+
+#include "mongo/scripting/mozjs/implscope.h"
+#include "mongo/scripting/mozjs/objectwrapper.h"
+#include "mongo/scripting/mozjs/valuereader.h"
+#include "mongo/scripting/mozjs/valuewriter.h"
+#include "mongo/util/mongoutils/str.h"
+#include "mongo/util/text.h"
+
+namespace mongo {
+namespace mozjs {
+
+const JSFunctionSpec NumberLongInfo::methods[4] = {
+ MONGO_ATTACH_JS_FUNCTION(toNumber),
+ MONGO_ATTACH_JS_FUNCTION(toString),
+ MONGO_ATTACH_JS_FUNCTION(valueOf),
+ JS_FS_END,
+};
+
+const char* const NumberLongInfo::className = "NumberLong";
+
+namespace {
+const char* const kTop = "top";
+const char* const kBottom = "bottom";
+const char* const kFloatApprox = "floatApprox";
+} // namespace
+
+long long NumberLongInfo::ToNumberLong(JSContext* cx, JS::HandleValue thisv) {
+ JS::RootedObject obj(cx, thisv.toObjectOrNull());
+ return ToNumberLong(cx, obj);
+}
+
+long long NumberLongInfo::ToNumberLong(JSContext* cx, JS::HandleObject thisv) {
+ ObjectWrapper o(cx, thisv);
+
+ if (!o.hasField(kTop)) {
+ if (!o.hasField(kFloatApprox))
+ uasserted(ErrorCodes::InternalError, "No top and no floatApprox fields");
+
+ return o.getNumber(kFloatApprox);
+ }
+
+ if (!o.hasField(kBottom))
+ uasserted(ErrorCodes::InternalError, "top but no bottom field");
+
+ return ((unsigned long long)((long long)o.getNumber(kTop) << 32) +
+ (unsigned)(o.getNumber(kBottom)));
+}
+
+void NumberLongInfo::Functions::valueOf(JSContext* cx, JS::CallArgs args) {
+ long long out = NumberLongInfo::ToNumberLong(cx, args.thisv());
+
+ args.rval().setDouble(out);
+}
+
+void NumberLongInfo::Functions::toNumber(JSContext* cx, JS::CallArgs args) {
+ valueOf(cx, args);
+}
+
+void NumberLongInfo::Functions::toString(JSContext* cx, JS::CallArgs args) {
+ str::stream ss;
+
+ long long val = NumberLongInfo::ToNumberLong(cx, args.thisv());
+
+ const long long limit = 2LL << 30;
+
+ if (val <= -limit || limit <= val)
+ ss << "NumberLong(\"" << val << "\")";
+ else
+ ss << "NumberLong(" << val << ")";
+
+ ValueReader(cx, args.rval()).fromStringData(ss.operator std::string());
+}
+
+void NumberLongInfo::construct(JSContext* cx, JS::CallArgs args) {
+ uassert(ErrorCodes::BadValue,
+ "NumberLong needs 0, 1 or 3 arguments",
+ args.length() == 0 || args.length() == 1 || args.length() == 3);
+
+ auto scope = getScope(cx);
+
+ JS::RootedObject thisv(cx);
+
+ scope->getNumberLongProto().newObject(&thisv);
+ ObjectWrapper o(cx, thisv);
+
+ JS::RootedValue floatApprox(cx);
+ JS::RootedValue top(cx);
+ JS::RootedValue bottom(cx);
+
+ if (args.length() == 0) {
+ o.setNumber(kFloatApprox, 0);
+ } else if (args.length() == 1) {
+ if (args.get(0).isNumber()) {
+ o.setValue(kFloatApprox, args.get(0));
+ } else {
+ std::string str = ValueWriter(cx, args.get(0)).toString();
+
+ unsigned long long val = parseLL(str.c_str());
+
+ // values above 2^53 are not accurately represented in JS
+ if ((long long)val == (long long)(double)(long long)(val) &&
+ val < 9007199254740992ULL) {
+ o.setNumber(kFloatApprox, val);
+ } else {
+ o.setNumber(kFloatApprox, val);
+ o.setNumber(kTop, val >> 32);
+ o.setNumber(kBottom, val & 0x00000000ffffffff);
+ }
+ }
+ } else {
+ if (!args.get(0).isNumber())
+ uasserted(ErrorCodes::BadValue, "floatApprox must be a number");
+
+ if (!args.get(1).isNumber() ||
+ args.get(1).toNumber() !=
+ static_cast<double>(static_cast<uint32_t>(args.get(1).toNumber())))
+ uasserted(ErrorCodes::BadValue, "top must be a 32 bit unsigned number");
+
+ if (!args.get(2).isNumber() ||
+ args.get(2).toNumber() !=
+ static_cast<double>(static_cast<uint32_t>(args.get(2).toNumber())))
+ uasserted(ErrorCodes::BadValue, "bottom must be a 32 bit unsigned number");
+
+ o.setValue(kFloatApprox, args.get(0));
+ o.setValue(kTop, args.get(1));
+ o.setValue(kBottom, args.get(2));
+ }
+
+ args.rval().setObjectOrNull(thisv);
+}
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/numberlong.h b/src/mongo/scripting/mozjs/numberlong.h
new file mode 100644
index 00000000000..bedfa558b2b
--- /dev/null
+++ b/src/mongo/scripting/mozjs/numberlong.h
@@ -0,0 +1,68 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#pragma once
+
+#include "mongo/scripting/mozjs/wraptype.h"
+
+namespace mongo {
+namespace mozjs {
+
+/**
+ * The "NumberLong" Javascript object.
+ *
+ * Represents a 64 integer with a JS representation like:
+ *
+ * {
+ * top : Double,
+ * bottom : Double,
+ * floatApprox : Double,
+ * }
+ *
+ * Where top is the high 32 bits, bottom the low 32 bits and floatApprox a
+ * floating point approximation.
+ */
+struct NumberLongInfo : public BaseInfo {
+ static void construct(JSContext* cx, JS::CallArgs args);
+
+ struct Functions {
+ MONGO_DEFINE_JS_FUNCTION(toNumber);
+ MONGO_DEFINE_JS_FUNCTION(toString);
+ MONGO_DEFINE_JS_FUNCTION(valueOf);
+ };
+
+ static const JSFunctionSpec methods[4];
+
+ static const char* const className;
+
+ static long long ToNumberLong(JSContext* cx, JS::HandleObject object);
+ static long long ToNumberLong(JSContext* cx, JS::HandleValue value);
+};
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/object.cpp b/src/mongo/scripting/mozjs/object.cpp
new file mode 100644
index 00000000000..a90bec36a72
--- /dev/null
+++ b/src/mongo/scripting/mozjs/object.cpp
@@ -0,0 +1,87 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/scripting/mozjs/object.h"
+
+#include "mongo/scripting/mozjs/implscope.h"
+#include "mongo/scripting/mozjs/objectwrapper.h"
+#include "mongo/scripting/mozjs/valuereader.h"
+#include "mongo/scripting/mozjs/valuewriter.h"
+
+namespace mongo {
+namespace mozjs {
+
+const JSFunctionSpec ObjectInfo::methods[3] = {
+ MONGO_ATTACH_JS_FUNCTION(bsonsize), MONGO_ATTACH_JS_FUNCTION(invalidForStorage), JS_FS_END,
+};
+
+const char* const ObjectInfo::className = "Object";
+
+void ObjectInfo::Functions::bsonsize(JSContext* cx, JS::CallArgs args) {
+ if (args.length() != 1)
+ uasserted(ErrorCodes::BadValue, "bsonsize needs 1 argument");
+
+ if (args.get(0).isNull()) {
+ args.rval().setInt32(0);
+ return;
+ }
+
+ if (!args.get(0).isObject())
+ uasserted(ErrorCodes::BadValue, "argument to bsonsize has to be an object");
+
+ args.rval().setInt32(ValueWriter(cx, args.get(0)).toBSON().objsize());
+}
+
+void ObjectInfo::Functions::invalidForStorage(JSContext* cx, JS::CallArgs args) {
+ if (args.length() != 1)
+ uasserted(ErrorCodes::BadValue, "invalidForStorage needs 1 argument");
+
+ if (args.get(0).isNull()) {
+ args.rval().setNull();
+ return;
+ }
+
+ if (!args.get(0).isObject())
+ uasserted(ErrorCodes::BadValue, "argument to invalidForStorage has to be an object");
+
+ Status validForStorage = ValueWriter(cx, args.get(0)).toBSON().storageValid(true);
+ if (validForStorage.isOK()) {
+ args.rval().setNull();
+ return;
+ }
+
+ std::string errmsg = str::stream() << validForStorage.codeString() << ": "
+ << validForStorage.reason();
+
+ ValueReader(cx, args.rval()).fromStringData(errmsg);
+}
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/object.h b/src/mongo/scripting/mozjs/object.h
new file mode 100644
index 00000000000..70f475c3f0e
--- /dev/null
+++ b/src/mongo/scripting/mozjs/object.h
@@ -0,0 +1,56 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#pragma once
+
+#include "mongo/scripting/mozjs/wraptype.h"
+
+namespace mongo {
+namespace mozjs {
+
+/**
+ * Adds some methods onto the JS type "Object"
+ *
+ * Note that this installs "overNative", so we don't actually do anything other
+ * than layer a couple of our own functions on top of the existing prototype.
+ */
+struct ObjectInfo : public BaseInfo {
+ struct Functions {
+ MONGO_DEFINE_JS_FUNCTION(bsonsize);
+ MONGO_DEFINE_JS_FUNCTION(invalidForStorage);
+ };
+
+ static const JSFunctionSpec methods[3];
+
+ static const char* const className;
+
+ static const InstallType installType = InstallType::OverNative;
+};
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/objectwrapper.cpp b/src/mongo/scripting/mozjs/objectwrapper.cpp
new file mode 100644
index 00000000000..0450eeee42b
--- /dev/null
+++ b/src/mongo/scripting/mozjs/objectwrapper.cpp
@@ -0,0 +1,385 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/scripting/mozjs/objectwrapper.h"
+
+#include "mongo/base/error_codes.h"
+#include "mongo/bson/bsonobjbuilder.h"
+#include "mongo/scripting/mozjs/idwrapper.h"
+#include "mongo/scripting/mozjs/implscope.h"
+#include "mongo/scripting/mozjs/valuereader.h"
+#include "mongo/scripting/mozjs/valuewriter.h"
+
+namespace mongo {
+namespace mozjs {
+
+void ObjectWrapper::Key::get(JSContext* cx, JS::HandleObject o, JS::MutableHandleValue value) {
+ switch (_type) {
+ case Type::Field:
+ if (JS_GetProperty(cx, o, _field, value))
+ return;
+ break;
+ case Type::Index:
+ if (JS_GetElement(cx, o, _idx, value))
+ return;
+ break;
+ case Type::Id: {
+ JS::RootedId id(cx, _id);
+
+ if (JS_GetPropertyById(cx, o, id, value))
+ return;
+ break;
+ }
+ }
+
+ throwCurrentJSException(cx, ErrorCodes::InternalError, "Failed to get value on a JSObject");
+}
+
+void ObjectWrapper::Key::set(JSContext* cx, JS::HandleObject o, JS::HandleValue value) {
+ switch (_type) {
+ case Type::Field:
+ if (JS_SetProperty(cx, o, _field, value))
+ return;
+ break;
+ case Type::Index:
+ if (JS_SetElement(cx, o, _idx, value))
+ return;
+ break;
+ case Type::Id: {
+ JS::RootedId id(cx, _id);
+
+ if (JS_SetPropertyById(cx, o, id, value))
+ return;
+ break;
+ }
+ }
+
+ throwCurrentJSException(cx, ErrorCodes::InternalError, "Failed to set value on a JSObject");
+}
+
+void ObjectWrapper::Key::define(JSContext* cx,
+ JS::HandleObject o,
+ JS::HandleValue value,
+ unsigned attrs) {
+ switch (_type) {
+ case Type::Field:
+ if (JS_DefineProperty(cx, o, _field, value, attrs))
+ return;
+ break;
+ case Type::Index:
+ if (JS_DefineElement(cx, o, _idx, value, attrs))
+ return;
+ break;
+ case Type::Id: {
+ JS::RootedId id(cx, _id);
+
+ if (JS_DefinePropertyById(cx, o, id, value, attrs))
+ return;
+ break;
+ }
+ }
+
+ throwCurrentJSException(cx, ErrorCodes::InternalError, "Failed to define value on a JSObject");
+}
+
+bool ObjectWrapper::Key::has(JSContext* cx, JS::HandleObject o) {
+ bool has;
+
+ switch (_type) {
+ case Type::Field:
+ if (JS_HasProperty(cx, o, _field, &has))
+ return has;
+ break;
+ case Type::Index:
+ if (JS_HasElement(cx, o, _idx, &has))
+ return has;
+ break;
+ case Type::Id: {
+ JS::RootedId id(cx, _id);
+
+ if (JS_HasPropertyById(cx, o, id, &has))
+ return has;
+ break;
+ }
+ }
+
+ throwCurrentJSException(cx, ErrorCodes::InternalError, "Failed to has value on a JSObject");
+}
+
+void ObjectWrapper::Key::del(JSContext* cx, JS::HandleObject o) {
+ switch (_type) {
+ case Type::Field:
+ if (JS_DeleteProperty(cx, o, _field))
+ return;
+ break;
+ case Type::Index:
+ if (JS_DeleteElement(cx, o, _idx))
+ return;
+ break;
+ case Type::Id: {
+ JS::RootedId id(cx, _id);
+
+ // For some reason JS_DeletePropertyById doesn't link
+ if (JS_DeleteProperty(cx, o, IdWrapper(cx, id).toString().c_str()))
+ return;
+ break;
+ }
+ }
+
+ throwCurrentJSException(cx, ErrorCodes::InternalError, "Failed to delete value on a JSObject");
+}
+
+std::string ObjectWrapper::Key::toString(JSContext* cx) {
+ switch (_type) {
+ case Type::Field:
+ return _field;
+ case Type::Index:
+ return std::to_string(_idx);
+ case Type::Id: {
+ JS::RootedId id(cx, _id);
+ return IdWrapper(cx, id).toString();
+ }
+ }
+
+ throwCurrentJSException(
+ cx, ErrorCodes::InternalError, "Failed to toString a ObjectWrapper::Key");
+}
+
+ObjectWrapper::ObjectWrapper(JSContext* cx, JS::HandleObject obj, int depth)
+ : _context(cx), _object(cx, obj), _depth(depth) {}
+
+ObjectWrapper::ObjectWrapper(JSContext* cx, JS::HandleValue value, int depth)
+ : _context(cx), _object(cx, value.toObjectOrNull()), _depth(depth) {}
+
+double ObjectWrapper::getNumber(Key key) {
+ JS::RootedValue x(_context);
+ getValue(key, &x);
+
+ return ValueWriter(_context, x).toNumber();
+}
+
+int ObjectWrapper::getNumberInt(Key key) {
+ JS::RootedValue x(_context);
+ getValue(key, &x);
+
+ return ValueWriter(_context, x).toInt32();
+}
+
+long long ObjectWrapper::getNumberLongLong(Key key) {
+ JS::RootedValue x(_context);
+ getValue(key, &x);
+
+ return ValueWriter(_context, x).toInt64();
+}
+
+std::string ObjectWrapper::getString(Key key) {
+ JS::RootedValue x(_context);
+ getValue(key, &x);
+
+ return ValueWriter(_context, x).toString();
+}
+
+bool ObjectWrapper::getBoolean(Key key) {
+ JS::RootedValue x(_context);
+ getValue(key, &x);
+
+ return ValueWriter(_context, x).toBoolean();
+}
+
+BSONObj ObjectWrapper::getObject(Key key) {
+ JS::RootedValue x(_context);
+ getValue(key, &x);
+
+ return ValueWriter(_context, x, _depth).toBSON();
+}
+
+void ObjectWrapper::getValue(Key key, JS::MutableHandleValue value) {
+ key.get(_context, _object, value);
+}
+
+void ObjectWrapper::setNumber(Key key, double val) {
+ JS::RootedValue jsValue(_context);
+ jsValue.setDouble(val);
+
+ setValue(key, jsValue);
+}
+
+void ObjectWrapper::setString(Key key, StringData val) {
+ JS::RootedValue jsValue(_context);
+ ValueReader(_context, &jsValue).fromStringData(val);
+
+ setValue(key, jsValue);
+}
+
+void ObjectWrapper::setBoolean(Key key, bool val) {
+ JS::RootedValue jsValue(_context);
+ jsValue.setBoolean(val);
+
+ setValue(key, jsValue);
+}
+
+void ObjectWrapper::setBSONElement(Key key, const BSONElement& elem, bool readOnly) {
+ JS::RootedValue value(_context);
+ ValueReader(_context, &value, _depth).fromBSONElement(elem, readOnly);
+
+ setValue(key, value);
+}
+
+void ObjectWrapper::setBSON(Key key, const BSONObj& obj, bool readOnly) {
+ JS::RootedValue value(_context);
+ ValueReader(_context, &value, _depth).fromBSON(obj, readOnly);
+
+ setValue(key, value);
+}
+
+void ObjectWrapper::setValue(Key key, JS::HandleValue val) {
+ key.set(_context, _object, val);
+}
+
+void ObjectWrapper::setObject(Key key, JS::HandleObject object) {
+ JS::RootedValue value(_context);
+ value.setObjectOrNull(object);
+
+ setValue(key, value);
+}
+
+void ObjectWrapper::defineProperty(Key key, JS::HandleValue val, unsigned attrs) {
+ key.define(_context, _object, val, attrs);
+}
+
+void ObjectWrapper::deleteProperty(Key key) {
+ key.del(_context, _object);
+}
+
+int ObjectWrapper::type(Key key) {
+ JS::RootedValue x(_context);
+ getValue(key, &x);
+
+ return ValueWriter(_context, x).type();
+}
+
+void ObjectWrapper::rename(Key from, const char* to) {
+ JS::RootedValue value(_context);
+
+ JS::RootedValue undefValue(_context);
+ undefValue.setUndefined();
+
+ getValue(from, &value);
+
+ setValue(to, value);
+ setValue(from, undefValue);
+}
+
+bool ObjectWrapper::hasField(Key key) {
+ return key.has(_context, _object);
+}
+
+void ObjectWrapper::callMethod(const char* field,
+ const JS::HandleValueArray& args,
+ JS::MutableHandleValue out) {
+ if (JS::Call(_context, _object, field, args, out))
+ return;
+
+ throwCurrentJSException(_context, ErrorCodes::InternalError, "Failed to call method");
+}
+
+void ObjectWrapper::callMethod(const char* field, JS::MutableHandleValue out) {
+ JS::AutoValueVector args(_context);
+
+ callMethod(field, args, out);
+}
+
+void ObjectWrapper::callMethod(JS::HandleValue fun,
+ const JS::HandleValueArray& args,
+ JS::MutableHandleValue out) {
+ if (JS::Call(_context, _object, fun, args, out))
+ return;
+
+ throwCurrentJSException(_context, ErrorCodes::InternalError, "Failed to call method");
+}
+
+void ObjectWrapper::callMethod(JS::HandleValue fun, JS::MutableHandleValue out) {
+ JS::AutoValueVector args(_context);
+
+ callMethod(fun, args, out);
+}
+
+void ObjectWrapper::writeThis(BSONObjBuilder* b) {
+ auto scope = getScope(_context);
+
+ BSONObj* originalBSON = nullptr;
+ if (scope->getBsonProto().instanceOf(_object)) {
+ bool altered;
+
+ std::tie(originalBSON, altered) = BSONInfo::originalBSON(_context, _object);
+
+ if (!altered) {
+ b->appendElements(*originalBSON);
+ return;
+ }
+ }
+
+ // We special case the _id field in top-level objects and move it to the front.
+ // This matches other drivers behavior and makes finding the _id field quicker in BSON.
+ if (_depth == 0 && hasField("_id")) {
+ _writeField(b, "_id", originalBSON);
+ }
+
+ enumerate([&](JS::HandleId id) {
+ JS::RootedValue x(_context);
+
+ IdWrapper idw(_context, id);
+
+ if (_depth == 0 && idw.isString() && idw.equals("_id"))
+ return;
+
+ _writeField(b, id, originalBSON);
+ });
+
+ const int sizeWithEOO = b->len() + 1 /*EOO*/ - 4 /*BSONObj::Holder ref count*/;
+ uassert(17260,
+ str::stream() << "Converting from JavaScript to BSON failed: "
+ << "Object size " << sizeWithEOO << " exceeds limit of "
+ << BSONObjMaxInternalSize << " bytes.",
+ sizeWithEOO <= BSONObjMaxInternalSize);
+}
+
+void ObjectWrapper::_writeField(BSONObjBuilder* b, Key key, BSONObj* originalParent) {
+ JS::RootedValue value(_context);
+ key.get(_context, _object, &value);
+
+ ValueWriter x(_context, value, _depth);
+ x.setOriginalBSON(originalParent);
+
+ x.writeThis(b, key.toString(_context));
+}
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/objectwrapper.h b/src/mongo/scripting/mozjs/objectwrapper.h
new file mode 100644
index 00000000000..efac05a1e71
--- /dev/null
+++ b/src/mongo/scripting/mozjs/objectwrapper.h
@@ -0,0 +1,181 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#pragma once
+
+#include <jsapi.h>
+#include <string>
+
+#include "mongo/scripting/mozjs/exception.h"
+
+namespace mongo {
+
+class BSONObj;
+class BSONObjBuilder;
+class BSONElement;
+
+namespace mozjs {
+
+class MozJSImplScope;
+
+/**
+ * Wraps JSObject's with helpers for accessing their properties
+ *
+ * This wraps a RootedObject, so should only be allocated on the stack and is
+ * not movable or copyable
+ */
+class ObjectWrapper {
+public:
+ /**
+ * Helper subclass that provides some easy boilerplate for accessing
+ * properties by string, index or id.
+ */
+ class Key {
+ friend class ObjectWrapper;
+
+ enum class Type : char {
+ Field,
+ Index,
+ Id,
+ };
+
+ public:
+ Key(const char* field) : _field(field), _type(Type::Field) {}
+ Key(uint32_t idx) : _idx(idx), _type(Type::Index) {}
+ Key(JS::HandleId id) : _id(id), _type(Type::Id) {}
+
+ private:
+ void get(JSContext* cx, JS::HandleObject o, JS::MutableHandleValue value);
+ void set(JSContext* cx, JS::HandleObject o, JS::HandleValue value);
+ bool has(JSContext* cx, JS::HandleObject o);
+ void define(JSContext* cx, JS::HandleObject o, JS::HandleValue value, unsigned attrs);
+ void del(JSContext* cx, JS::HandleObject o);
+ std::string toString(JSContext* cx);
+
+ union {
+ const char* _field;
+ uint32_t _idx;
+ jsid _id;
+ };
+ Type _type;
+ };
+
+ /**
+ * The depth parameter here allows us to detect overly nested or circular
+ * objects and bail without blowing the stack.
+ */
+ ObjectWrapper(JSContext* cx, JS::HandleObject obj, int depth = 0);
+ ObjectWrapper(JSContext* cx, JS::HandleValue value, int depth = 0);
+
+ double getNumber(Key key);
+ int getNumberInt(Key key);
+ long long getNumberLongLong(Key key);
+ std::string getString(Key key);
+ bool getBoolean(Key key);
+ BSONObj getObject(Key key);
+ void getValue(Key key, JS::MutableHandleValue value);
+
+ void setNumber(Key key, double val);
+ void setString(Key key, StringData val);
+ void setBoolean(Key key, bool val);
+ void setBSONElement(Key key, const BSONElement& elem, bool readOnly);
+ void setBSON(Key key, const BSONObj& obj, bool readOnly);
+ void setValue(Key key, JS::HandleValue value);
+ void setObject(Key key, JS::HandleObject value);
+
+ /**
+ * See JS_DefineProperty for what sort of attributes might be useful
+ */
+ void defineProperty(Key key, JS::HandleValue value, unsigned attrs);
+
+ void deleteProperty(Key key);
+
+ /**
+ * Returns the bson type of the property
+ */
+ int type(Key key);
+
+ void rename(Key key, const char* to);
+
+ bool hasField(Key key);
+
+ void callMethod(const char* name, const JS::HandleValueArray& args, JS::MutableHandleValue out);
+ void callMethod(const char* name, JS::MutableHandleValue out);
+ void callMethod(JS::HandleValue fun,
+ const JS::HandleValueArray& args,
+ JS::MutableHandleValue out);
+ void callMethod(JS::HandleValue fun, JS::MutableHandleValue out);
+
+ /**
+ * Safely enumerates fields in the object, invoking a callback for each id
+ */
+ template <typename T>
+ void enumerate(T&& callback) {
+ JS::AutoIdArray ids(_context, JS_Enumerate(_context, _object));
+
+ if (!ids)
+ throwCurrentJSException(
+ _context, ErrorCodes::JSInterpreterFailure, "Failure to enumerate object");
+
+ JS::RootedId rid(_context);
+ for (size_t i = 0; i < ids.length(); ++i) {
+ rid.set(ids[i]);
+ callback(rid);
+ }
+ }
+
+ /**
+ * concatenates all of the fields in the object into the associated builder
+ */
+ void writeThis(BSONObjBuilder* b);
+
+ JS::HandleObject thisv() {
+ return _object;
+ }
+
+private:
+ /**
+ * writes the field "key" into the associated builder
+ *
+ * optional originalBSON is used to track updates to types (NumberInt
+ * overwritten by a float, but coercible to the original type, etc.)
+ */
+ void _writeField(BSONObjBuilder* b, Key key, BSONObj* originalBSON);
+
+ JSContext* _context;
+ JS::RootedObject _object;
+
+ /**
+ * The depth of an object wrapper has to do with how many parents it has.
+ * Used to avoid circular object graphs and associate stack smashing.
+ */
+ int _depth;
+};
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/oid.cpp b/src/mongo/scripting/mozjs/oid.cpp
new file mode 100644
index 00000000000..cf391bf03e8
--- /dev/null
+++ b/src/mongo/scripting/mozjs/oid.cpp
@@ -0,0 +1,87 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/scripting/mozjs/oid.h"
+
+#include "mongo/scripting/mozjs/implscope.h"
+#include "mongo/scripting/mozjs/objectwrapper.h"
+#include "mongo/scripting/mozjs/valuereader.h"
+#include "mongo/scripting/mozjs/valuewriter.h"
+#include "mongo/stdx/memory.h"
+#include "mongo/util/mongoutils/str.h"
+
+namespace mongo {
+namespace mozjs {
+
+const JSFunctionSpec OIDInfo::methods[2] = {
+ MONGO_ATTACH_JS_FUNCTION(toString), JS_FS_END,
+};
+
+const char* const OIDInfo::className = "ObjectId";
+
+void OIDInfo::Functions::toString(JSContext* cx, JS::CallArgs args) {
+ ObjectWrapper o(cx, args.thisv());
+
+ if (!o.hasField("str"))
+ uasserted(ErrorCodes::BadValue, "Must have \"str\" field");
+
+ JS::RootedValue value(cx);
+ o.getValue("str", &value);
+
+ std::string str = str::stream() << "ObjectId(\"" << ValueWriter(cx, value).toString() << "\")";
+
+ ValueReader(cx, args.rval()).fromStringData(str);
+}
+
+void OIDInfo::construct(JSContext* cx, JS::CallArgs args) {
+ auto scope = getScope(cx);
+
+ auto oid = stdx::make_unique<OID>();
+
+ if (args.length() == 0) {
+ oid->init();
+ } else {
+ auto str = ValueWriter(cx, args.get(0)).toString();
+
+ Scope::validateObjectIdString(str);
+ oid->init(str);
+ }
+
+ JS::RootedObject thisv(cx);
+ scope->getOidProto().newObject(&thisv);
+ ObjectWrapper o(cx, thisv);
+
+ o.setString("str", oid->toString());
+
+ args.rval().setObjectOrNull(thisv);
+}
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/oid.h b/src/mongo/scripting/mozjs/oid.h
new file mode 100644
index 00000000000..109282be882
--- /dev/null
+++ b/src/mongo/scripting/mozjs/oid.h
@@ -0,0 +1,54 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#pragma once
+
+#include "mongo/scripting/mozjs/wraptype.h"
+
+namespace mongo {
+namespace mozjs {
+
+/**
+ * The "ObjectId" Javascript object.
+ *
+ * Holds a private bson OID
+ */
+struct OIDInfo : public BaseInfo {
+ static void construct(JSContext* cx, JS::CallArgs args);
+
+ struct Functions {
+ MONGO_DEFINE_JS_FUNCTION(toString);
+ };
+
+ static const JSFunctionSpec methods[2];
+
+ static const char* const className;
+};
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/proxyscope.cpp b/src/mongo/scripting/mozjs/proxyscope.cpp
new file mode 100644
index 00000000000..665d249f4c5
--- /dev/null
+++ b/src/mongo/scripting/mozjs/proxyscope.cpp
@@ -0,0 +1,318 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/scripting/mozjs/proxyscope.h"
+
+#include "mongo/db/client.h"
+#include "mongo/db/operation_context.h"
+#include "mongo/db/service_context.h"
+#include "mongo/scripting/mozjs/implscope.h"
+
+namespace mongo {
+namespace mozjs {
+
+MozJSProxyScope::MozJSProxyScope(MozJSScriptEngine* engine)
+ : _engine(engine),
+ _implScope(nullptr),
+ _mutex(),
+ _state(State::Idle),
+ _status(Status::OK()),
+ _condvar(),
+ _thread(&MozJSProxyScope::implThread, this) {
+ // Test the child on startup to make sure it's awake and that the
+ // implementation scope sucessfully constructed.
+ try {
+ runOnImplThread([] {});
+ } catch (...) {
+ shutdownThread();
+ throw;
+ }
+}
+
+MozJSProxyScope::~MozJSProxyScope() {
+ DESTRUCTOR_GUARD(kill(); shutdownThread(););
+}
+
+void MozJSProxyScope::init(const BSONObj* data) {
+ runOnImplThread([&] { _implScope->init(data); });
+}
+
+void MozJSProxyScope::reset() {
+ runOnImplThread([&] { _implScope->reset(); });
+}
+
+bool MozJSProxyScope::isKillPending() const {
+ return _implScope->isKillPending();
+}
+
+void MozJSProxyScope::registerOperation(OperationContext* txn) {
+ runOnImplThread([&] { _implScope->registerOperation(txn); });
+}
+
+void MozJSProxyScope::unregisterOperation() {
+ runOnImplThread([&] { _implScope->unregisterOperation(); });
+}
+
+void MozJSProxyScope::localConnectForDbEval(OperationContext* txn, const char* dbName) {
+ runOnImplThread([&] { _implScope->localConnectForDbEval(txn, dbName); });
+}
+
+void MozJSProxyScope::externalSetup() {
+ runOnImplThread([&] { _implScope->externalSetup(); });
+}
+
+std::string MozJSProxyScope::getError() {
+ std::string out;
+ runOnImplThread([&] { out = _implScope->getError(); });
+ return out;
+}
+
+/**
+ * This is an artifact of how out of memory errors were communicated in V8. We
+ * just throw out of memory errors from spidermonkey when we get them, rather
+ * than setting a flag and having to pick them up here.
+ */
+bool MozJSProxyScope::hasOutOfMemoryException() {
+ return false;
+}
+
+void MozJSProxyScope::gc() {
+ _implScope->gc();
+}
+
+double MozJSProxyScope::getNumber(const char* field) {
+ double out;
+ runOnImplThread([&] { out = _implScope->getNumber(field); });
+ return out;
+}
+
+int MozJSProxyScope::getNumberInt(const char* field) {
+ int out;
+ runOnImplThread([&] { out = _implScope->getNumberInt(field); });
+ return out;
+}
+
+long long MozJSProxyScope::getNumberLongLong(const char* field) {
+ long long out;
+ runOnImplThread([&] { out = _implScope->getNumberLongLong(field); });
+ return out;
+}
+
+std::string MozJSProxyScope::getString(const char* field) {
+ std::string out;
+ runOnImplThread([&] { out = _implScope->getString(field); });
+ return out;
+}
+
+bool MozJSProxyScope::getBoolean(const char* field) {
+ bool out;
+ runOnImplThread([&] { out = _implScope->getBoolean(field); });
+ return out;
+}
+
+BSONObj MozJSProxyScope::getObject(const char* field) {
+ BSONObj out;
+ runOnImplThread([&] { out = _implScope->getObject(field); });
+ return out;
+}
+
+void MozJSProxyScope::setNumber(const char* field, double val) {
+ runOnImplThread([&] { _implScope->setNumber(field, val); });
+}
+
+void MozJSProxyScope::setString(const char* field, StringData val) {
+ runOnImplThread([&] { _implScope->setString(field, val); });
+}
+
+void MozJSProxyScope::setBoolean(const char* field, bool val) {
+ runOnImplThread([&] { _implScope->setBoolean(field, val); });
+}
+
+void MozJSProxyScope::setElement(const char* field, const BSONElement& e) {
+ runOnImplThread([&] { _implScope->setElement(field, e); });
+}
+
+void MozJSProxyScope::setObject(const char* field, const BSONObj& obj, bool readOnly) {
+ runOnImplThread([&] { _implScope->setObject(field, obj, readOnly); });
+}
+
+void MozJSProxyScope::setFunction(const char* field, const char* code) {
+ runOnImplThread([&] { _implScope->setFunction(field, code); });
+}
+
+int MozJSProxyScope::type(const char* field) {
+ int out;
+ runOnImplThread([&] { out = _implScope->type(field); });
+ return out;
+}
+
+void MozJSProxyScope::rename(const char* from, const char* to) {
+ runOnImplThread([&] { _implScope->rename(from, to); });
+}
+
+int MozJSProxyScope::invoke(ScriptingFunction func,
+ const BSONObj* argsObject,
+ const BSONObj* recv,
+ int timeoutMs,
+ bool ignoreReturn,
+ bool readOnlyArgs,
+ bool readOnlyRecv) {
+ int out;
+ runOnImplThread([&] {
+ out = _implScope->invoke(
+ func, argsObject, recv, timeoutMs, ignoreReturn, readOnlyArgs, readOnlyRecv);
+ });
+
+ return out;
+}
+
+bool MozJSProxyScope::exec(StringData code,
+ const std::string& name,
+ bool printResult,
+ bool reportError,
+ bool assertOnError,
+ int timeoutMs) {
+ bool out;
+ runOnImplThread([&] {
+ out = _implScope->exec(code, name, printResult, reportError, assertOnError, timeoutMs);
+ });
+ return out;
+}
+
+void MozJSProxyScope::injectNative(const char* field, NativeFunction func, void* data) {
+ runOnImplThread([&] { _implScope->injectNative(field, func, data); });
+}
+
+ScriptingFunction MozJSProxyScope::_createFunction(const char* raw,
+ ScriptingFunction functionNumber) {
+ ScriptingFunction out;
+ runOnImplThread([&] { out = _implScope->_createFunction(raw, functionNumber); });
+ return out;
+}
+
+OperationContext* MozJSProxyScope::getOpContext() const {
+ return _implScope->getOpContext();
+}
+
+void MozJSProxyScope::kill() {
+ _implScope->kill();
+}
+
+/**
+ * Invokes a function on the implementation thread
+ *
+ * It does this by serializing the invocation through a stdx::function and
+ * capturing any exceptions through _status.
+ *
+ * We transition:
+ *
+ * Idle -> ProxyRequest -> ImplResponse -> Idle
+ */
+void MozJSProxyScope::runOnImplThread(std::function<void()> f) {
+ stdx::unique_lock<stdx::mutex> lk(_mutex);
+ _function = std::move(f);
+
+ invariant(_state == State::Idle);
+ _state = State::ProxyRequest;
+
+ _condvar.notify_one();
+
+ _condvar.wait(lk, [this] { return _state == State::ImplResponse; });
+
+ _state = State::Idle;
+
+ // Clear the _status state and throw it if necessary
+ auto status = std::move(_status);
+ uassertStatusOK(status);
+}
+
+void MozJSProxyScope::shutdownThread() {
+ {
+ stdx::lock_guard<stdx::mutex> lk(_mutex);
+
+ invariant(_state == State::Idle);
+
+ _state = State::Shutdown;
+ }
+
+ _condvar.notify_one();
+
+ _thread.join();
+}
+
+/**
+ * The main loop for the implementation thread
+ *
+ * This owns the actual implementation scope (which needs to be created on this
+ * child thread) and has essentially two transition paths:
+ *
+ * Standard: ProxyRequest -> ImplResponse
+ * Invoke _function. Serialize exceptions to _status.
+ *
+ * Shutdown: Shutdown -> _
+ * break out of the loop and return.
+ */
+void MozJSProxyScope::implThread() {
+ if (hasGlobalServiceContext())
+ Client::initThread("js");
+
+ std::unique_ptr<MozJSImplScope> scope;
+
+ // This will leave _status set for the first noop runOnImplThread(), which
+ // captures the startup exception that way
+ try {
+ scope.reset(new MozJSImplScope(_engine));
+ _implScope = scope.get();
+ } catch (...) {
+ _status = exceptionToStatus();
+ }
+
+ while (true) {
+ stdx::unique_lock<stdx::mutex> lk(_mutex);
+ _condvar.wait(
+ lk, [this] { return _state == State::ProxyRequest || _state == State::Shutdown; });
+
+ if (_state == State::Shutdown)
+ break;
+
+ try {
+ _function();
+ } catch (...) {
+ _status = exceptionToStatus();
+ }
+
+ _state = State::ImplResponse;
+
+ _condvar.notify_one();
+ }
+}
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/proxyscope.h b/src/mongo/scripting/mozjs/proxyscope.h
new file mode 100644
index 00000000000..8b874360a68
--- /dev/null
+++ b/src/mongo/scripting/mozjs/proxyscope.h
@@ -0,0 +1,195 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#pragma once
+
+#include "mongo/client/dbclientcursor.h"
+#include "mongo/scripting/mozjs/engine.h"
+#include "mongo/stdx/condition_variable.h"
+#include "mongo/stdx/functional.h"
+#include "mongo/stdx/mutex.h"
+#include "mongo/stdx/thread.h"
+
+namespace mongo {
+namespace mozjs {
+
+class MozJSImplScope;
+
+/**
+ * Proxies all methods on the implementation scope over a side channel that
+ * allows the SpiderMonkey runtime to operate entirely on one thread. This
+ * implements the public scope interface and is the right way to talk to an
+ * implementation scope from multiple threads.
+ *
+ * In terms of implementation, it does most of it's heavy lifting through a
+ * stdx::function. The proxy scope owns an implementation scope transitively
+ * through the thread it owns. They communicate by setting a variable, then
+ * signaling each other. That communication has to work, there's no fallback
+ * for timing out.
+ *
+ * There are probably performance gains to be had from making
+ * the argument capture and method dispatch explicit, but I'll wait until we've
+ * measured it before bothering.
+ *
+ * See mongo::Scope for details on all of the overridden functions
+ *
+ */
+class MozJSProxyScope final : public Scope {
+ MONGO_DISALLOW_COPYING(MozJSProxyScope);
+
+ /**
+ * The FSM is fairly tight:
+ *
+ * +----------+ shutdownThread() +--------------------+
+ * | Shutdown | <------------------ | Idle | <+
+ * +----------+ +--------------------+ |
+ * | |
+ * | runOnImplThread() |
+ * v |
+ * +--------------------+ |
+ * | ProxyRequest | | impl -> proxy
+ * +--------------------+ |
+ * | |
+ * | proxy -> impl |
+ * v |
+ * +--------------------+ |
+ * | ImplResponse | -+
+ * +--------------------+
+ *
+ * The regular flow:
+ * - We start at Idle and on the proxy thread.
+ * - runOnImplThread sets ProxyRequest and notifies the impl thread
+ * - The impl thread wakes up, invokes _function(), sets ImplResponse and notifies the proxy
+ * thread
+ * - The proxy thread wakes up and sets Idle
+ *
+ * Shutdown:
+ * - On destruction, The proxy thread sets Shutdown and notifies the impl thread
+ * - The impl thread wakes up, breaks out of it's loop and returns
+ * - The proxy thread joins the impl thread
+ *
+ */
+ enum class State : char {
+ Idle,
+ ProxyRequest,
+ ImplResponse,
+ Shutdown,
+ };
+
+public:
+ MozJSProxyScope(MozJSScriptEngine* engine);
+ ~MozJSProxyScope();
+
+ void init(const BSONObj* data) override;
+
+ void reset() override;
+
+ bool isKillPending() const override;
+
+ void registerOperation(OperationContext* txn) override;
+
+ void unregisterOperation() override;
+
+ void localConnectForDbEval(OperationContext* txn, const char* dbName) override;
+
+ void externalSetup() override;
+
+ std::string getError() override;
+
+ bool hasOutOfMemoryException() override;
+
+ void gc() override;
+
+ double getNumber(const char* field) override;
+ int getNumberInt(const char* field) override;
+ long long getNumberLongLong(const char* field) override;
+ std::string getString(const char* field) override;
+ bool getBoolean(const char* field) override;
+ BSONObj getObject(const char* field) override;
+
+ void setNumber(const char* field, double val) override;
+ void setString(const char* field, StringData val) override;
+ void setBoolean(const char* field, bool val) override;
+ void setElement(const char* field, const BSONElement& e) override;
+ void setObject(const char* field, const BSONObj& obj, bool readOnly) override;
+ void setFunction(const char* field, const char* code) override;
+
+ int type(const char* field) override;
+
+ void rename(const char* from, const char* to) override;
+
+ int invoke(ScriptingFunction func,
+ const BSONObj* args,
+ const BSONObj* recv,
+ int timeoutMs = 0,
+ bool ignoreReturn = false,
+ bool readOnlyArgs = false,
+ bool readOnlyRecv = false) override;
+
+ bool exec(StringData code,
+ const std::string& name,
+ bool printResult,
+ bool reportError,
+ bool assertOnError,
+ int timeoutMs) override;
+
+ void injectNative(const char* field, NativeFunction func, void* data = 0) override;
+
+ ScriptingFunction _createFunction(const char* code,
+ ScriptingFunction functionNumber = 0) override;
+
+ OperationContext* getOpContext() const;
+
+ /**
+ * Thread safe. Kills the running operation
+ */
+ void kill();
+
+private:
+ void runOnImplThread(std::function<void()> f);
+ void shutdownThread();
+ void implThread();
+
+ MozJSScriptEngine* const _engine;
+ MozJSImplScope* _implScope;
+
+ /**
+ * This mutex protects _function, _state and _status as channels for
+ * function invocation and exception handling
+ */
+ stdx::mutex _mutex;
+ stdx::function<void()> _function;
+ State _state;
+ Status _status;
+
+ stdx::condition_variable _condvar;
+ stdx::thread _thread;
+};
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/regexp.cpp b/src/mongo/scripting/mozjs/regexp.cpp
new file mode 100644
index 00000000000..b935a9fb354
--- /dev/null
+++ b/src/mongo/scripting/mozjs/regexp.cpp
@@ -0,0 +1,39 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/scripting/mozjs/regexp.h"
+
+namespace mongo {
+namespace mozjs {
+
+const char* const RegExpInfo::className = "RegExp";
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/regexp.h b/src/mongo/scripting/mozjs/regexp.h
new file mode 100644
index 00000000000..b8553518127
--- /dev/null
+++ b/src/mongo/scripting/mozjs/regexp.h
@@ -0,0 +1,49 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#pragma once
+
+#include "mongo/scripting/mozjs/wraptype.h"
+
+namespace mongo {
+namespace mozjs {
+
+/**
+ * The "RegExp" Javascript object.
+ *
+ * Note that this installs over native. We only use this to grab the regexp
+ * prototype early in case users overwrite it.
+ */
+struct RegExpInfo : public BaseInfo {
+ static const char* const className;
+
+ static const InstallType installType = InstallType::OverNative;
+};
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/timestamp.cpp b/src/mongo/scripting/mozjs/timestamp.cpp
new file mode 100644
index 00000000000..f81cc4050ce
--- /dev/null
+++ b/src/mongo/scripting/mozjs/timestamp.cpp
@@ -0,0 +1,77 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/scripting/mozjs/timestamp.h"
+
+#include "mongo/scripting/mozjs/implscope.h"
+#include "mongo/scripting/mozjs/objectwrapper.h"
+#include "mongo/scripting/mozjs/valuereader.h"
+#include "mongo/scripting/mozjs/valuewriter.h"
+#include "mongo/util/mongoutils/str.h"
+
+namespace mongo {
+namespace mozjs {
+
+const char* const TimestampInfo::className = "Timestamp";
+
+void TimestampInfo::construct(JSContext* cx, JS::CallArgs args) {
+ auto scope = getScope(cx);
+
+ JS::RootedObject thisv(cx);
+ scope->getTimestampProto().newObject(&thisv);
+ ObjectWrapper o(cx, thisv);
+
+ if (args.length() == 0) {
+ o.setNumber("t", 0);
+ o.setNumber("i", 0);
+ } else if (args.length() == 2) {
+ if (!args.get(0).isNumber())
+ uasserted(ErrorCodes::BadValue, "Timestamp time must be a number");
+ if (!args.get(1).isNumber())
+ uasserted(ErrorCodes::BadValue, "Timestamp increment must be a number");
+
+ int64_t t = ValueWriter(cx, args.get(0)).toInt64();
+ int64_t largestVal = int64_t(Timestamp::max().getSecs());
+ if (t > largestVal)
+ uasserted(ErrorCodes::BadValue,
+ str::stream() << "The first argument must be in seconds; " << t
+ << " is too large (max " << largestVal << ")");
+
+ o.setValue("t", args.get(0));
+ o.setValue("i", args.get(1));
+ } else {
+ uasserted(ErrorCodes::BadValue, "Timestamp needs 0 or 2 arguments");
+ }
+
+ args.rval().setObjectOrNull(thisv);
+}
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/timestamp.h b/src/mongo/scripting/mozjs/timestamp.h
new file mode 100644
index 00000000000..1dc4a998420
--- /dev/null
+++ b/src/mongo/scripting/mozjs/timestamp.h
@@ -0,0 +1,53 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#pragma once
+
+#include "mongo/scripting/mozjs/wraptype.h"
+
+namespace mongo {
+namespace mozjs {
+
+/**
+ * The "Timestamp" Javascript object.
+ *
+ * Represents a bson timestamp that looks like:
+ *
+ * {
+ * t : Double,
+ * i : Double,
+ * }
+ */
+struct TimestampInfo : public BaseInfo {
+ static void construct(JSContext* cx, JS::CallArgs args);
+
+ static const char* const className;
+};
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/valuereader.cpp b/src/mongo/scripting/mozjs/valuereader.cpp
new file mode 100644
index 00000000000..ad45fafc06e
--- /dev/null
+++ b/src/mongo/scripting/mozjs/valuereader.cpp
@@ -0,0 +1,272 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kQuery
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/scripting/mozjs/valuereader.h"
+
+#include <cstdio>
+#include <js/CharacterEncoding.h>
+
+#include "mongo/base/error_codes.h"
+#include "mongo/scripting/mozjs/implscope.h"
+#include "mongo/scripting/mozjs/objectwrapper.h"
+#include "mongo/util/base64.h"
+#include "mongo/util/log.h"
+
+namespace mongo {
+namespace mozjs {
+
+ValueReader::ValueReader(JSContext* cx, JS::MutableHandleValue value, int depth)
+ : _context(cx), _value(value), _depth(depth) {}
+
+void ValueReader::fromBSONElement(const BSONElement& elem, bool readOnly) {
+ auto scope = getScope(_context);
+
+ switch (elem.type()) {
+ case mongo::Code:
+ scope->newFunction(elem.valueStringData(), _value);
+ return;
+ case mongo::CodeWScope:
+ if (!elem.codeWScopeObject().isEmpty())
+ warning() << "CodeWScope doesn't transfer to db.eval";
+ scope->newFunction(StringData(elem.codeWScopeCode(), elem.codeWScopeCodeLen() - 1),
+ _value);
+ return;
+ case mongo::Symbol:
+ case mongo::String:
+ fromStringData(elem.valueStringData());
+ return;
+ case mongo::jstOID: {
+ JS::AutoValueArray<1> args(_context);
+
+ ValueReader(_context, args[0]).fromStringData(elem.OID().toString());
+
+ scope->getOidProto().newInstance(args, _value);
+ return;
+ }
+ case mongo::NumberDouble:
+ _value.setDouble(elem.Number());
+ return;
+ case mongo::NumberInt:
+ _value.setInt32(elem.Int());
+ return;
+ case mongo::Array: {
+ auto arrayPtr = JS_NewArrayObject(_context, 0);
+ uassert(ErrorCodes::JSInterpreterFailure, "Failed to JS_NewArrayObject", arrayPtr);
+ JS::RootedObject array(_context, arrayPtr);
+
+ unsigned i = 0;
+ BSONForEach(subElem, elem.embeddedObject()) {
+ // We use an unsigned 32 bit integer, so 10 base 10 digits and
+ // 1 null byte
+ char str[11];
+ sprintf(str, "%i", i++);
+ JS::RootedValue member(_context);
+
+ ValueReader(_context, &member, _depth + 1).fromBSONElement(subElem, readOnly);
+ ObjectWrapper(_context, array, _depth + 1).setValue(str, member);
+ }
+ _value.setObjectOrNull(array);
+ return;
+ }
+ case mongo::Object:
+ fromBSON(elem.embeddedObject(), readOnly);
+ return;
+ case mongo::Date:
+ _value.setObjectOrNull(
+ JS_NewDateObjectMsec(_context, elem.Date().toMillisSinceEpoch()));
+ return;
+ case mongo::Bool:
+ _value.setBoolean(elem.Bool());
+ return;
+ case mongo::EOO:
+ case mongo::jstNULL:
+ case mongo::Undefined:
+ _value.setNull();
+ return;
+ case mongo::RegEx: {
+ // TODO parse into a custom type that can support any patterns and flags SERVER-9803
+
+ JS::AutoValueArray<2> args(_context);
+
+ ValueReader(_context, args[0]).fromStringData(elem.regex());
+ ValueReader(_context, args[1]).fromStringData(elem.regexFlags());
+
+ JS::RootedObject obj(_context);
+ scope->getRegExpProto().newInstance(args, &obj);
+
+ _value.setObjectOrNull(obj);
+
+ return;
+ }
+ case mongo::BinData: {
+ int len;
+ const char* data = elem.binData(len);
+ std::stringstream ss;
+ base64::encode(ss, data, len);
+
+ JS::AutoValueArray<2> args(_context);
+
+ args[0].setInt32(elem.binDataType());
+
+ ValueReader(_context, args[1]).fromStringData(ss.str());
+
+ scope->getBinDataProto().newInstance(args, _value);
+ return;
+ }
+ case mongo::bsonTimestamp: {
+ JS::AutoValueArray<2> args(_context);
+
+ args[0].setDouble(elem.timestampTime().toMillisSinceEpoch() / 1000);
+ args[1].setNumber(elem.timestampInc());
+
+ scope->getTimestampProto().newInstance(args, _value);
+
+ return;
+ }
+ case mongo::NumberLong: {
+ unsigned long long nativeUnsignedLong = elem.numberLong();
+ // values above 2^53 are not accurately represented in JS
+ if (static_cast<long long>(nativeUnsignedLong) ==
+ static_cast<long long>(
+ static_cast<double>(static_cast<long long>(nativeUnsignedLong))) &&
+ nativeUnsignedLong < 9007199254740992ULL) {
+ JS::AutoValueArray<1> args(_context);
+ args[0].setNumber(static_cast<double>(static_cast<long long>(nativeUnsignedLong)));
+
+ scope->getNumberLongProto().newInstance(args, _value);
+ } else {
+ JS::AutoValueArray<3> args(_context);
+ args[0].setNumber(static_cast<double>(static_cast<long long>(nativeUnsignedLong)));
+ args[1].setDouble(nativeUnsignedLong >> 32);
+ args[2].setDouble(
+ static_cast<unsigned long>(nativeUnsignedLong & 0x00000000ffffffff));
+ scope->getNumberLongProto().newInstance(args, _value);
+ }
+
+ return;
+ }
+ case mongo::MinKey:
+ scope->getMinKeyProto().newInstance(_value);
+ return;
+ case mongo::MaxKey:
+ scope->getMaxKeyProto().newInstance(_value);
+ return;
+ case mongo::DBRef: {
+ JS::AutoValueArray<1> oidArgs(_context);
+ ValueReader(_context, oidArgs[0]).fromStringData(elem.dbrefOID().toString());
+
+ JS::AutoValueArray<2> dbPointerArgs(_context);
+ ValueReader(_context, dbPointerArgs[0]).fromStringData(elem.dbrefNS());
+ scope->getOidProto().newInstance(oidArgs, dbPointerArgs[1]);
+
+ scope->getDbPointerProto().newInstance(dbPointerArgs, _value);
+ return;
+ }
+ default:
+ massert(16661,
+ str::stream() << "can't handle type: " << elem.type() << " " << elem.toString(),
+ false);
+ break;
+ }
+
+ _value.setUndefined();
+}
+
+void ValueReader::fromBSON(const BSONObj& obj, bool readOnly) {
+ if (obj.firstElementType() == String && str::equals(obj.firstElementFieldName(), "$ref")) {
+ BSONObjIterator it(obj);
+ const BSONElement ref = it.next();
+ const BSONElement id = it.next();
+
+ if (id.ok() && str::equals(id.fieldName(), "$id")) {
+ JS::AutoValueArray<2> args(_context);
+
+ ValueReader(_context, args[0]).fromBSONElement(ref, readOnly);
+
+ // id can be a subobject
+ ValueReader(_context, args[1], _depth + 1).fromBSONElement(id, readOnly);
+
+ JS::RootedObject obj(_context);
+
+ auto scope = getScope(_context);
+
+ scope->getDbRefProto().newInstance(args, &obj);
+ ObjectWrapper o(_context, obj);
+
+ while (it.more()) {
+ BSONElement elem = it.next();
+ o.setBSONElement(elem.fieldName(), elem, readOnly);
+ }
+
+ _value.setObjectOrNull(obj);
+ return;
+ }
+ }
+
+ JS::RootedObject child(_context);
+ BSONInfo::make(_context, &child, obj, readOnly);
+
+ _value.setObjectOrNull(child);
+}
+
+/**
+ * SpiderMonkey doesn't have a direct entry point to create a jsstring from
+ * utf8, so we have to flow through some slightly less public interfaces.
+ *
+ * Basically, we have to use their routines to convert to utf16, then assign
+ * those bytes with JS_NewUCStringCopyN
+ */
+void ValueReader::fromStringData(StringData sd) {
+ size_t utf16Len;
+
+ // TODO: we have tests that involve dropping garbage in. Do we want to
+ // throw, or to take the lossy conversion?
+ auto utf16 = JS::LossyUTF8CharsToNewTwoByteCharsZ(
+ _context, JS::UTF8Chars(sd.rawData(), sd.size()), &utf16Len);
+
+ mozilla::UniquePtr<char16_t, JS::FreePolicy> utf16Deleter(utf16.get());
+
+ uassert(ErrorCodes::JSInterpreterFailure,
+ str::stream() << "Failed to encode \"" << sd << "\" as utf16",
+ utf16);
+
+ auto jsStr = JS_NewUCStringCopyN(_context, utf16.get(), utf16Len);
+
+ uassert(ErrorCodes::JSInterpreterFailure,
+ str::stream() << "Unable to copy \"" << sd << "\" into MozJS",
+ jsStr);
+
+ _value.setString(jsStr);
+}
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/valuereader.h b/src/mongo/scripting/mozjs/valuereader.h
new file mode 100644
index 00000000000..be916ccbc25
--- /dev/null
+++ b/src/mongo/scripting/mozjs/valuereader.h
@@ -0,0 +1,61 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#pragma once
+
+#include <jsapi.h>
+#include <string>
+
+#include "mongo/bson/bsonobj.h"
+
+namespace mongo {
+namespace mozjs {
+
+/**
+ * Reads into a JS Value from some Mongo C++ primitive
+ */
+class ValueReader {
+public:
+ /**
+ * Depth is used when readers are invoked from ObjectWrappers to avoid
+ * reading out overly nested objects
+ */
+ ValueReader(JSContext* cx, JS::MutableHandleValue value, int depth = 0);
+
+ void fromBSONElement(const BSONElement& elem, bool readOnly);
+ void fromBSON(const BSONObj& obj, bool readOnly);
+ void fromStringData(StringData sd);
+
+private:
+ JSContext* _context;
+ JS::MutableHandleValue _value;
+ int _depth;
+};
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/valuewriter.cpp b/src/mongo/scripting/mozjs/valuewriter.cpp
new file mode 100644
index 00000000000..31928f7ab44
--- /dev/null
+++ b/src/mongo/scripting/mozjs/valuewriter.cpp
@@ -0,0 +1,252 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/scripting/mozjs/valuewriter.h"
+
+#include <js/Conversions.h>
+
+#include "mongo/base/error_codes.h"
+#include "mongo/scripting/mozjs/exception.h"
+#include "mongo/scripting/mozjs/implscope.h"
+#include "mongo/scripting/mozjs/jsstringwrapper.h"
+#include "mongo/scripting/mozjs/objectwrapper.h"
+#include "mongo/scripting/mozjs/valuereader.h"
+#include "mongo/util/base64.h"
+
+namespace mongo {
+namespace mozjs {
+
+ValueWriter::ValueWriter(JSContext* cx, JS::HandleValue value, int depth)
+ : _context(cx), _value(value), _depth(depth), _originalParent(nullptr) {}
+
+void ValueWriter::setOriginalBSON(BSONObj* obj) {
+ _originalParent = obj;
+}
+
+int ValueWriter::type() {
+ if (_value.isNull())
+ return jstNULL;
+ if (_value.isUndefined())
+ return Undefined;
+ if (_value.isString())
+ return String;
+ if (JS_IsArrayObject(_context, _value))
+ return Array;
+ if (_value.isBoolean())
+ return Bool;
+
+ // We could do something more sophisticated here by checking to see if we
+ // round trip through int32_t, int64_t and double and picking a type that
+ // way, for now just always come back as double for numbers though (it's
+ // what we did for v8)
+ if (_value.isNumber())
+ return NumberDouble;
+
+ if (_value.isObject()) {
+ JS::RootedObject obj(_context, _value.toObjectOrNull());
+ if (JS_ObjectIsDate(_context, obj))
+ return Date;
+ if (JS_ObjectIsFunction(_context, obj))
+ return Code;
+
+ return Object;
+ }
+
+ uasserted(ErrorCodes::BadValue, "unable to get type");
+}
+
+BSONObj ValueWriter::toBSON() {
+ if (!_value.isObject())
+ return BSONObj();
+
+ JS::RootedObject obj(_context, _value.toObjectOrNull());
+
+ if (getScope(_context)->getBsonProto().instanceOf(obj)) {
+ BSONObj* originalBSON;
+ bool altered;
+
+ std::tie(originalBSON, altered) = BSONInfo::originalBSON(_context, obj);
+
+ if (!altered)
+ return *originalBSON;
+ }
+
+ BSONObjBuilder bob;
+ ObjectWrapper(_context, obj, _depth).writeThis(&bob);
+
+ return bob.obj();
+}
+
+std::string ValueWriter::toString() {
+ return JSStringWrapper(_context, JS::ToString(_context, _value)).toString();
+}
+
+double ValueWriter::toNumber() {
+ double out;
+ if (JS::ToNumber(_context, _value, &out))
+ return out;
+
+ throwCurrentJSException(_context, ErrorCodes::BadValue, "Failure to convert value to number");
+}
+
+bool ValueWriter::toBoolean() {
+ return JS::ToBoolean(_value);
+}
+
+int32_t ValueWriter::toInt32() {
+ int32_t out;
+ if (JS::ToInt32(_context, _value, &out))
+ return out;
+
+ throwCurrentJSException(_context, ErrorCodes::BadValue, "Failure to convert value to number");
+}
+
+int64_t ValueWriter::toInt64() {
+ int64_t out;
+ if (JS::ToInt64(_context, _value, &out))
+ return out;
+
+ throwCurrentJSException(_context, ErrorCodes::BadValue, "Failure to convert value to number");
+}
+
+void ValueWriter::writeThis(BSONObjBuilder* b, StringData sd) {
+ uassert(17279,
+ str::stream() << "Exceeded depth limit of " << 150
+ << " when converting js object to BSON. Do you have a cycle?",
+ _depth < 149);
+
+ // Null char should be at the end, not in the string
+ uassert(16985,
+ str::stream() << "JavaScript property (name) contains a null char "
+ << "which is not allowed in BSON. "
+ << (_originalParent ? _originalParent->jsonString() : ""),
+ (std::string::npos == sd.find('\0')));
+
+ if (_value.isString()) {
+ b->append(sd, toString());
+ } else if (_value.isNumber()) {
+ double val = toNumber();
+
+ // if previous type was integer, keep it
+ int intval = static_cast<int>(val);
+
+ if (val == intval && _originalParent) {
+ // This makes copying an object of numbers O(n**2) :(
+ BSONElement elmt = _originalParent->getField(sd);
+ if (elmt.type() == mongo::NumberInt) {
+ b->append(sd, intval);
+ return;
+ }
+ }
+
+ b->append(sd, val);
+ } else if (_value.isObject()) {
+ JS::RootedObject childObj(_context, _value.toObjectOrNull());
+ _writeObject(b, sd, childObj);
+ } else if (_value.isBoolean()) {
+ b->appendBool(sd, _value.toBoolean());
+ } else if (_value.isUndefined()) {
+ b->appendUndefined(sd);
+ } else if (_value.isNull()) {
+ b->appendNull(sd);
+ } else {
+ uasserted(16662,
+ str::stream() << "unable to convert JavaScript property to mongo element " << sd);
+ }
+}
+
+void ValueWriter::_writeObject(BSONObjBuilder* b, StringData sd, JS::HandleObject obj) {
+ auto scope = getScope(_context);
+
+ ObjectWrapper o(_context, obj, _depth);
+
+ if (JS_ObjectIsFunction(_context, _value.toObjectOrNull())) {
+ uassert(16716,
+ "cannot convert native function to BSON",
+ !scope->getNativeFunctionProto().instanceOf(obj));
+ b->appendCode(sd, ValueWriter(_context, _value).toString());
+ } else if (JS_ObjectIsRegExp(_context, obj)) {
+ JS::RootedValue v(_context);
+ v.setObjectOrNull(obj);
+
+ std::string regex = ValueWriter(_context, v).toString();
+ regex = regex.substr(1);
+ std::string r = regex.substr(0, regex.rfind('/'));
+ std::string o = regex.substr(regex.rfind('/') + 1);
+
+ b->appendRegex(sd, r, o);
+ } else if (JS_ObjectIsDate(_context, obj)) {
+ JS::RootedValue dateval(_context);
+ o.callMethod("getTime", &dateval);
+
+ auto d = Date_t::fromMillisSinceEpoch(ValueWriter(_context, dateval).toNumber());
+ b->appendDate(sd, d);
+ } else if (scope->getOidProto().instanceOf(obj)) {
+ b->append(sd, OID(o.getString("str")));
+ } else if (scope->getNumberLongProto().instanceOf(obj)) {
+ long long out = NumberLongInfo::ToNumberLong(_context, obj);
+ b->append(sd, out);
+ } else if (scope->getNumberIntProto().instanceOf(obj)) {
+ b->append(sd, NumberIntInfo::ToNumberInt(_context, obj));
+ } else if (scope->getDbPointerProto().instanceOf(obj)) {
+ JS::RootedValue id(_context);
+ o.getValue("id", &id);
+
+ b->appendDBRef(sd, o.getString("ns"), OID(ObjectWrapper(_context, id).getString("str")));
+ } else if (scope->getBinDataProto().instanceOf(obj)) {
+ auto str = static_cast<std::string*>(JS_GetPrivate(obj));
+
+ auto binData = base64::decode(*str);
+
+ b->appendBinData(sd,
+ binData.size(),
+ static_cast<mongo::BinDataType>(static_cast<int>(o.getNumber("type"))),
+ binData.c_str());
+ } else if (scope->getTimestampProto().instanceOf(obj)) {
+ Timestamp ot(o.getNumber("t"), o.getNumber("i"));
+ b->append(sd, ot);
+ } else if (scope->getMinKeyProto().instanceOf(obj)) {
+ b->appendMinKey(sd);
+ } else if (scope->getMaxKeyProto().instanceOf(obj)) {
+ b->appendMaxKey(sd);
+ } else {
+ // nested object or array
+
+ BSONObjBuilder subbob(JS_IsArrayObject(_context, obj) ? b->subarrayStart(sd)
+ : b->subobjStart(sd));
+
+ ObjectWrapper child(_context, obj, _depth + 1);
+
+ child.writeThis(b);
+ }
+}
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/valuewriter.h b/src/mongo/scripting/mozjs/valuewriter.h
new file mode 100644
index 00000000000..47358398f81
--- /dev/null
+++ b/src/mongo/scripting/mozjs/valuewriter.h
@@ -0,0 +1,84 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#pragma once
+
+#include <jsapi.h>
+#include <string>
+
+#include "mongo/bson/bsonobj.h"
+
+namespace mongo {
+namespace mozjs {
+
+/**
+ * Writes C++ values out of JS Values
+ *
+ * depth is used to trap circular objects in js and prevent stack smashing
+ *
+ * originalBSON is a hack to keep integer types in their original type when
+ * they're read out, manipulated in js and saved back.
+ */
+class ValueWriter {
+public:
+ ValueWriter(JSContext* cx, JS::HandleValue value, int depth = 0);
+
+ BSONObj toBSON();
+
+ /**
+ * These coercions flow through JS::To_X. I.e. they can call toString() or
+ * toNumber()
+ */
+ std::string toString();
+ int type();
+ double toNumber();
+ int32_t toInt32();
+ int64_t toInt64();
+ bool toBoolean();
+
+ /**
+ * Writes the value into a bsonobjbuilder under the name in sd.
+ */
+ void writeThis(BSONObjBuilder* b, StringData sd);
+
+ void setOriginalBSON(BSONObj* obj);
+
+private:
+ /**
+ * Writes the object into a bsonobjbuilder under the name in sd.
+ */
+ void _writeObject(BSONObjBuilder* b, StringData sd, JS::HandleObject obj);
+
+ JSContext* _context;
+ JS::HandleValue _value;
+ int _depth;
+ BSONObj* _originalParent;
+};
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/wraptype.h b/src/mongo/scripting/mozjs/wraptype.h
new file mode 100644
index 00000000000..c5ca6095e76
--- /dev/null
+++ b/src/mongo/scripting/mozjs/wraptype.h
@@ -0,0 +1,474 @@
+/**
+ * Copyright (C) 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#pragma once
+
+#include <cstddef>
+#include <jsapi.h>
+#include <type_traits>
+
+#include "mongo/scripting/mozjs/base.h"
+#include "mongo/scripting/mozjs/exception.h"
+#include "mongo/scripting/mozjs/objectwrapper.h"
+#include "mongo/util/assert_util.h"
+
+// The purpose of this class is to take in specially crafted types and generate
+// a wrapper which installs the type, along with any useful life cycle methods
+// and free functions that might be associated with it. The template magic in
+// here, along with some useful macros, hides a lot of the implementation
+// complexity of exposing C++ code into javascript. Most prominently, we have
+// to wrap every function that can be called from javascript to prevent any C++
+// exceptions from leaking out. We do this, with template and macro based
+// codegen, and turn mongo exceptions into instances of Status, then convert
+// those into javascript exceptions before returning. That allows all consumers
+// of this library to throw exceptions freely, with the understanding that
+// they'll be visible in javascript. Javascript exceptions are trapped at the
+// top level and converted back to mongo exceptions by an error handler on
+// ImplScope.
+
+// MONGO_*_JS_FUNCTION_* macros are public and allow wrapped types to install
+// their own functions on types and into the global scope
+#define MONGO_DEFINE_JS_FUNCTION(name) \
+ static void name(JSContext* cx, JS::CallArgs args); \
+ static bool WRAPPER_##name(JSContext* cx, unsigned argc, JS::Value* vp) { \
+ try { \
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp); \
+ name(cx, args); \
+ return true; \
+ } catch (...) { \
+ mongoToJSException(cx); \
+ return false; \
+ } \
+ }
+
+#define MONGO_ATTACH_JS_FUNCTION_WITH_FLAGS(name, flags) \
+ JS_FS(#name, Functions::WRAPPER_##name, 0, flags)
+
+#define MONGO_ATTACH_JS_FUNCTION(name) MONGO_ATTACH_JS_FUNCTION_WITH_FLAGS(name, 0)
+
+namespace mongo {
+namespace mozjs {
+
+namespace smUtils {
+
+// Now all the spidermonkey type methods
+template <typename T>
+static bool addProperty(JSContext* cx,
+ JS::HandleObject obj,
+ JS::HandleId id,
+ JS::MutableHandleValue v) {
+ try {
+ T::addProperty(cx, obj, id, v);
+ return true;
+ } catch (...) {
+ mongoToJSException(cx);
+ return false;
+ }
+};
+
+template <typename T>
+static bool call(JSContext* cx, unsigned argc, JS::Value* vp) {
+ try {
+ T::call(cx, JS::CallArgsFromVp(argc, vp));
+ return true;
+ } catch (...) {
+ mongoToJSException(cx);
+ return false;
+ }
+};
+
+template <typename T>
+static bool construct(JSContext* cx, unsigned argc, JS::Value* vp) {
+ try {
+ T::construct(cx, JS::CallArgsFromVp(argc, vp));
+ return true;
+ } catch (...) {
+ mongoToJSException(cx);
+ return false;
+ }
+};
+
+template <typename T>
+static bool convert(JSContext* cx, JS::HandleObject obj, JSType type, JS::MutableHandleValue vp) {
+ try {
+ T::convert(cx, obj, type, vp);
+ return true;
+ } catch (...) {
+ mongoToJSException(cx);
+ return false;
+ }
+};
+
+template <typename T>
+static bool delProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* succeeded) {
+ try {
+ T::delProperty(cx, obj, id, succeeded);
+ return true;
+ } catch (...) {
+ mongoToJSException(cx);
+ return false;
+ }
+};
+
+template <typename T>
+static bool enumerate(JSContext* cx, JS::HandleObject obj, JS::AutoIdVector& properties) {
+ try {
+ T::enumerate(cx, obj, properties);
+ return true;
+ } catch (...) {
+ mongoToJSException(cx);
+ return false;
+ }
+};
+
+template <typename T>
+static bool getProperty(JSContext* cx,
+ JS::HandleObject obj,
+ JS::HandleId id,
+ JS::MutableHandleValue vp) {
+ try {
+ T::getProperty(cx, obj, id, vp);
+ return true;
+ } catch (...) {
+ mongoToJSException(cx);
+ return false;
+ }
+};
+
+template <typename T>
+static bool hasInstance(JSContext* cx, JS::HandleObject obj, JS::MutableHandleValue vp, bool* bp) {
+ try {
+ T::hasInstance(cx, obj, vp, bp);
+ return true;
+ } catch (...) {
+ mongoToJSException(cx);
+ return false;
+ }
+};
+
+template <typename T>
+static bool setProperty(
+ JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool strict, JS::MutableHandleValue vp) {
+ try {
+ T::setProperty(cx, obj, id, strict, vp);
+ return true;
+ } catch (...) {
+ mongoToJSException(cx);
+ return false;
+ }
+};
+
+template <typename T>
+static bool resolve(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* resolvedp) {
+ try {
+ T::resolve(cx, obj, id, resolvedp);
+ return true;
+ } catch (...) {
+ mongoToJSException(cx);
+ return false;
+ }
+};
+
+} // namespace smUtils
+
+template <typename T>
+class WrapType : public T {
+public:
+ WrapType(JSContext* context)
+ : _context(context),
+ _proto(),
+ _jsclass({T::className,
+ T::classFlags,
+ T::addProperty != BaseInfo::addProperty ? smUtils::addProperty<T> : nullptr,
+ T::delProperty != BaseInfo::delProperty ? smUtils::delProperty<T> : nullptr,
+ T::getProperty != BaseInfo::getProperty ? smUtils::getProperty<T> : nullptr,
+ T::setProperty != BaseInfo::setProperty ? smUtils::setProperty<T> : nullptr,
+ // We don't use the regular enumerate because we want the fancy new one
+ nullptr,
+ T::resolve != BaseInfo::resolve ? smUtils::resolve<T> : nullptr,
+ T::convert != BaseInfo::convert ? smUtils::convert<T> : nullptr,
+ T::finalize != BaseInfo::finalize ? T::finalize : nullptr,
+ T::call != BaseInfo::call ? smUtils::call<T> : nullptr,
+ T::hasInstance != BaseInfo::hasInstance ? smUtils::hasInstance<T> : nullptr,
+ T::construct != BaseInfo::construct ? smUtils::construct<T> : nullptr,
+ nullptr}) {
+ _installEnumerate(T::enumerate != BaseInfo::enumerate ? smUtils::enumerate<T> : nullptr);
+
+ // The global object is different. We need it for basic setup
+ // before the other types are installed. Might as well just do it
+ // in the constructor.
+ if (T::classFlags & JSCLASS_GLOBAL_FLAGS) {
+ JS::RootedObject proto(_context);
+
+ _proto.init(_context,
+ _assertPtr(JS_NewGlobalObject(
+ _context, &_jsclass, nullptr, JS::DontFireOnNewGlobalHook)));
+
+ JSAutoCompartment ac(_context, _proto);
+ _installFunctions(_proto, T::freeFunctions);
+ }
+ }
+
+ ~WrapType() {
+ // Persistent globals don't RAII, you have to reset() them manually
+ _proto.reset();
+ }
+
+ void install(JS::HandleObject global) {
+ switch (static_cast<InstallType>(T::installType)) {
+ case InstallType::Global:
+ _installGlobal(global);
+ break;
+ case InstallType::Private:
+ _installPrivate(global);
+ break;
+ case InstallType::OverNative:
+ _installOverNative(global);
+ break;
+ }
+ }
+
+ /**
+ * newObject methods don't invoke the constructor. So they're good for
+ * types without a constructor or inside the constructor
+ */
+ void newObject(JS::MutableHandleObject out) {
+ // The regular form of JS_NewObject, where we pass proto as the
+ // third param, actually does a global object lookup for some
+ // reason. This way allows object creation with non-public
+ // prototypes and if someone deletes the symbol up the chain.
+ out.set(_assertPtr(JS_NewObject(_context, &_jsclass, JS::NullPtr())));
+
+ if (!JS_SetPrototype(_context, out, _proto))
+ throwCurrentJSException(
+ _context, ErrorCodes::JSInterpreterFailure, "Failed to set prototype");
+ }
+
+ void newObject(JS::MutableHandleValue out) {
+ JS::RootedObject obj(_context);
+ newObject(&obj);
+
+ out.setObjectOrNull(obj);
+ }
+
+ /**
+ * newInstance calls the constructor, a la new Type() in js
+ */
+ void newInstance(JS::MutableHandleObject out) {
+ JS::AutoValueVector args(_context);
+
+ newInstance(args, out);
+ }
+
+ void newInstance(const JS::HandleValueArray& args, JS::MutableHandleObject out) {
+ out.set(_assertPtr(JS_New(_context, _proto, args)));
+ }
+
+ void newInstance(JS::MutableHandleValue out) {
+ JS::AutoValueVector args(_context);
+
+ newInstance(args, out);
+ }
+
+ void newInstance(const JS::HandleValueArray& args, JS::MutableHandleValue out) {
+ out.setObjectOrNull(_assertPtr(JS_New(_context, _proto, args)));
+ }
+
+ // instanceOf doesn't go up the prototype tree. It's a lower level more specific match
+ bool instanceOf(JS::HandleObject obj) {
+ return JS_InstanceOf(_context, obj, &_jsclass, nullptr);
+ }
+
+ bool instanceOf(JS::HandleValue value) {
+ if (!value.isObject())
+ return false;
+
+ JS::RootedObject obj(_context, value.toObjectOrNull());
+
+ return instanceOf(obj);
+ }
+
+ const JSClass* getJSClass() const {
+ return &_jsclass;
+ }
+
+ JS::HandleObject getProto() const {
+ return _proto;
+ }
+
+private:
+ /**
+ * Use this if you want your types installed visibly in the global scope
+ */
+ void _installGlobal(JS::HandleObject global) {
+ JS::RootedObject parent(_context);
+ _inheritFrom(T::inheritFrom, global, &parent);
+
+ _proto.init(_context,
+ _assertPtr(JS_InitClass(
+ _context,
+ global,
+ parent,
+ &_jsclass,
+ T::construct != BaseInfo::construct ? smUtils::construct<T> : nullptr,
+ 0,
+ nullptr,
+ T::methods,
+ nullptr,
+ nullptr)));
+
+ _installFunctions(global, T::freeFunctions);
+ _postInstall(global, T::postInstall);
+ }
+
+ // Use this if you want your types installed, but not visible in the
+ // global scope
+ void _installPrivate(JS::HandleObject global) {
+ JS::RootedObject parent(_context);
+ _inheritFrom(T::inheritFrom, global, &parent);
+
+ // See newObject() for why we have to do this dance with the explicit
+ // SetPrototype
+ _proto.init(_context, _assertPtr(JS_NewObject(_context, &_jsclass, JS::NullPtr())));
+ if (parent.get() && !JS_SetPrototype(_context, _proto, parent))
+ throwCurrentJSException(
+ _context, ErrorCodes::JSInterpreterFailure, "Failed to set prototype");
+
+ _installFunctions(_proto, T::methods);
+ _installFunctions(global, T::freeFunctions);
+
+ _installConstructor(T::construct != BaseInfo::construct ? smUtils::construct<T> : nullptr);
+
+ _postInstall(global, T::postInstall);
+ }
+
+ // Use this to attach things to types that we don't provide like
+ // Object, or Array
+ void _installOverNative(JS::HandleObject global) {
+ JS::RootedValue value(_context);
+ if (!JS_GetProperty(_context, global, T::className, &value))
+ throwCurrentJSException(
+ _context, ErrorCodes::JSInterpreterFailure, "Couldn't get className property");
+
+ if (!value.isObject())
+ uasserted(ErrorCodes::BadValue, "className isn't object");
+
+ _proto.init(_context, value.toObjectOrNull());
+
+ _installFunctions(_proto, T::methods);
+ _installFunctions(global, T::freeFunctions);
+ _postInstall(global, T::postInstall);
+ }
+
+ void _installFunctions(JS::HandleObject global, const JSFunctionSpec* fs) {
+ if (!fs)
+ return;
+ if (JS_DefineFunctions(_context, global, fs))
+ return;
+
+ throwCurrentJSException(
+ _context, ErrorCodes::JSInterpreterFailure, "Failed to define functions");
+ }
+
+ // We have to do this awkward dance to set the new style enumeration.
+ // You used to be able to set this with JSCLASS_NEW_ENUMERATE in class
+ // flags, in the future you'll probably only set ObjectOps, but for now
+ // we have this. There are a host of static_asserts in js/Class.h that
+ // ensure that these two structures are equal.
+ //
+ // This is a landmine to watch out for during upgrades
+ using enumerateT = bool (*)(JSContext*, JS::HandleObject, JS::AutoIdVector&);
+ void _installEnumerate(enumerateT enumerate) {
+ if (!enumerate)
+ return;
+
+ auto implClass = reinterpret_cast<js::Class*>(&_jsclass);
+
+ implClass->ops.enumerate = enumerate;
+ }
+
+ // This is for inheriting from something other than Object
+ void _inheritFrom(const char* name, JS::HandleObject global, JS::MutableHandleObject out) {
+ if (!name)
+ return;
+
+ JS::RootedValue val(_context);
+
+ if (!JS_GetProperty(_context, global, name, &val)) {
+ throwCurrentJSException(
+ _context, ErrorCodes::JSInterpreterFailure, "Failed to get parent");
+ }
+
+ if (!val.isObject()) {
+ uasserted(ErrorCodes::JSInterpreterFailure, "Parent is not an object");
+ }
+
+ out.set(val.toObjectOrNull());
+ }
+
+ using postInstallT = void (*)(JSContext*, JS::HandleObject, JS::HandleObject);
+ void _postInstall(JS::HandleObject global, postInstallT postInstall) {
+ if (!postInstall)
+ return;
+
+ postInstall(_context, global, _proto);
+ }
+
+ void _installConstructor(JSNative ctor) {
+ if (!ctor)
+ return;
+
+ auto ptr = JS_NewFunction(_context, ctor, 0, JSFUN_CONSTRUCTOR, JS::NullPtr(), nullptr);
+ if (!ptr) {
+ throwCurrentJSException(
+ _context, ErrorCodes::JSInterpreterFailure, "Failed to install constructor");
+ }
+
+ JS::RootedObject ctorObj(_context, JS_GetFunctionObject(ptr));
+
+ if (!JS_LinkConstructorAndPrototype(_context, ctorObj, _proto))
+ throwCurrentJSException(_context,
+ ErrorCodes::JSInterpreterFailure,
+ "Failed to link constructor and prototype");
+ }
+
+ JSObject* _assertPtr(JSObject* ptr) {
+ if (!ptr)
+ throwCurrentJSException(
+ _context, ErrorCodes::JSInterpreterFailure, "Failed to JS_NewX");
+
+ return ptr;
+ }
+
+ JSContext* _context;
+ JS::PersistentRootedObject _proto;
+ JSClass _jsclass;
+};
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/shell/bulk_api.js b/src/mongo/shell/bulk_api.js
index df2c868fa5c..89cb762f1e8 100644
--- a/src/mongo/shell/bulk_api.js
+++ b/src/mongo/shell/bulk_api.js
@@ -357,7 +357,6 @@ var _bulk_api_module = (function() {
if(!(this instanceof BulkWriteError))
return new BulkWriteError(bulkResult, singleBatchType, writeConcern, message);
- Error.captureStackTrace(this, this.constructor);
this.name = 'BulkWriteError';
this.message = message || 'unknown bulk write error';
@@ -403,7 +402,6 @@ var _bulk_api_module = (function() {
defineReadOnlyProperty(this, "code", commandError.code);
defineReadOnlyProperty(this, "errmsg", commandError.errmsg);
- Error.captureStackTrace(this, this.constructor);
this.name = 'WriteCommandError';
this.message = this.errmsg;
diff --git a/src/mongo/shell/types.js b/src/mongo/shell/types.js
index af529d63a1a..8f860cac59d 100644
--- a/src/mongo/shell/types.js
+++ b/src/mongo/shell/types.js
@@ -219,7 +219,9 @@ Object.extend = function(dst, src, deep){
for (var k in src){
var v = src[k];
if (deep && typeof(v) == "object"){
- if ("floatApprox" in v) { // convert NumberLong properly
+ if (v.constructor === ObjectId) { // convert ObjectId properly
+ eval("v = " + tojson(v));
+ } else if ("floatApprox" in v) { // convert NumberLong properly
eval("v = " + tojson(v));
} else {
v = Object.extend(typeof (v.length) == "number" ? [] : {}, v, true);
diff --git a/src/mongo/util/concurrency/threadlocal.h b/src/mongo/util/concurrency/threadlocal.h
index 8973f267208..d6190bffb0f 100644
--- a/src/mongo/util/concurrency/threadlocal.h
+++ b/src/mongo/util/concurrency/threadlocal.h
@@ -32,6 +32,8 @@
#include "mongo/config.h"
+#include "mongo/base/disallow_copying.h"
+
#if defined(MONGO_CONFIG_HAVE_THREAD_LOCAL)
#define MONGO_TRIVIALLY_CONSTRUCTIBLE_THREAD_LOCAL thread_local
#elif defined(MONGO_CONFIG_HAVE___THREAD)
diff --git a/src/third_party/SConscript b/src/third_party/SConscript
index 594ea2725e3..0b7cf49d33c 100644
--- a/src/third_party/SConscript
+++ b/src/third_party/SConscript
@@ -1,11 +1,12 @@
# -*- mode: python -*-
-Import("env use_system_version_of_library usev8 v8suffix boostSuffix")
+Import("env use_system_version_of_library usemozjs usev8 v8suffix boostSuffix")
Import("wiredtiger")
snappySuffix = '-1.1.2'
zlibSuffix = '-1.2.8'
pcreSuffix = "-8.37"
+mozjsSuffix = '-38'
thirdPartyIncludePathList = [
('s2', '#/src/third_party/s2'),
@@ -36,6 +37,17 @@ if not use_system_version_of_library('v8'):
thirdPartyIncludePathList.append(
('v8', '#/src/third_party/v8' + v8suffix + '/include'))
+# TODO: figure out if we want to offer system versions of mozjs. Mozilla
+# hasn't offered a source tarball since 24, but in theory they could.
+#
+#if not use_system_version_of_library('mozjs'):
+if True:
+ thirdPartyIncludePathList.append(
+ ('mozjs', ['#/src/third_party/mozjs' + mozjsSuffix + '/include',
+ '#/src/third_party/mozjs' + mozjsSuffix + '/mongo_sources',
+ '#/src/third_party/mozjs' + mozjsSuffix + '/platform/' + env["TARGET_ARCH"] + "/" + env["TARGET_OS"] + "/include",
+ ]))
+
if not use_system_version_of_library('stemmer'):
thirdPartyIncludePathList.append(
('stemmer', '#/src/third_party/libstemmer_c/include'))
@@ -199,6 +211,20 @@ if usev8:
'shim_v8.cpp',
])
+if usemozjs:
+ mozjsEnv = env.Clone()
+ mozjsEnv.SConscript('mozjs' + mozjsSuffix + '/SConscript', exports={'env' : mozjsEnv })
+ mozjsEnv = mozjsEnv.Clone(
+ LIBDEPS=[
+ 'mozjs' + mozjsSuffix + '/mozjs',
+ 'shim_zlib',
+ ])
+
+ mozjsEnv.Library(
+ target="shim_mozjs",
+ source=[
+ 'shim_mozjs.cpp',
+ ])
gperftoolsEnv = env
if (GetOption("allocator") == "tcmalloc"):
diff --git a/src/third_party/mozjs-38/SConscript b/src/third_party/mozjs-38/SConscript
new file mode 100644
index 00000000000..3b197a8bbcf
--- /dev/null
+++ b/src/third_party/mozjs-38/SConscript
@@ -0,0 +1,98 @@
+# -*- mode: python -*-
+
+Import("env")
+
+env = env.Clone()
+env.InjectThirdPartyIncludePaths(libraries=['zlib'])
+
+def removeIfPresent(lst, item):
+ try:
+ lst.remove(item)
+ except ValueError:
+ pass
+
+for to_remove in ['-Werror', '-Wall', '-W']:
+ removeIfPresent(env['CCFLAGS'], to_remove)
+
+# See what -D's show up in make. The AB_CD one might change, but we're little
+# endian only for now so I think it's sane
+env.Prepend(CPPDEFINES=[
+ 'AB_CD',
+ 'IMPL_MFBT',
+ 'JS_USE_CUSTOM_ALLOCATOR',
+ 'NO_NSPR_10_SUPPORT',
+ 'STATIC_JS_API=1',
+ 'U_NO_DEFAULT_INCLUDE_UTF_HEADERS=1',
+ ])
+
+# js-confdefs.h has to get in front on windows or wherever
+if env.TargetOSIs('windows'):
+ env.Prepend(CCFLAGS=[
+ '/FI', 'js-confdefs.h'
+ ])
+else:
+ if env.TargetOSIs('solaris'):
+ env.Prepend(CCFLAGS=[
+ '-include', 'solaris_hacks.h'
+ ])
+
+ env.Append(
+ CCFLAGS=[
+ '-include', 'js-confdefs.h',
+ '-Wno-invalid-offsetof',
+ ],
+ CXXFLAGS=[
+ '-Wno-non-virtual-dtor',
+ ],
+ )
+
+# js/src, js/public and mfbt are the only required sources right now, that
+# could change in the future
+#
+# Also:
+# We pre-generate configs for platforms and just check them in. Running
+# mozilla's config requires a relatively huge portion of their tree.
+env.Prepend(CPPPATH=[
+ '#src',
+ '$BUILD_DIR',
+ 'extract/js/src',
+ 'extract/mfbt',
+ 'extract/intl/icu/source/common',
+ 'include',
+ 'mongo_sources',
+ 'platform/' + env["TARGET_ARCH"] + "/" + env["TARGET_OS"] + "/build",
+ 'platform/' + env["TARGET_ARCH"] + "/" + env["TARGET_OS"] + "/include",
+])
+
+sources = [
+ "extract/js/src/builtin/RegExp.cpp",
+ "extract/js/src/frontend/Parser.cpp",
+ "extract/js/src/jsarray.cpp",
+ "extract/js/src/jsatom.cpp",
+ "extract/js/src/jsmath.cpp",
+ "extract/js/src/jsutil.cpp",
+ "extract/js/src/mfbt/Unified_cpp_mfbt0.cpp",
+ "extract/js/src/perf/pm_stub.cpp",
+ "extract/js/src/vm/TraceLogging.cpp",
+ "extract/js/src/vm/TraceLoggingGraph.cpp",
+ "extract/js/src/vm/TraceLoggingTypes.cpp",
+ "extract/mfbt/Compression.cpp",
+]
+
+if env.TargetOSIs('windows'):
+ sources.extend([
+ "extract/js/src/jit/ExecutableAllocatorWin.cpp",
+ ])
+ env.Prepend(CPPDEFINES=[
+ ("_CRT_RAND_S", "1")
+ ])
+else:
+ sources.extend([
+ "extract/js/src/jit/ExecutableAllocatorPosix.cpp",
+ ])
+
+sources.extend(Glob('platform/' + env["TARGET_ARCH"] + "/" + env["TARGET_OS"] + "/build/*.cpp")),
+
+# All of those unified sources come in from configure. The files don't
+# actually build individually anymore.
+env.Library( "mozjs", sources )
diff --git a/src/third_party/mozjs-38/mongo_sources/jscustomallocator.h b/src/third_party/mozjs-38/mongo_sources/jscustomallocator.h
new file mode 100644
index 00000000000..bfaec9ee4ff
--- /dev/null
+++ b/src/third_party/mozjs-38/mongo_sources/jscustomallocator.h
@@ -0,0 +1,54 @@
+/* Copyright 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#include <cstdlib>
+#include <cstring>
+
+#include <jstypes.h>
+
+#define JS_OOM_POSSIBLY_FAIL() \
+ do { \
+ } while (0)
+
+#define JS_OOM_POSSIBLY_FAIL_BOOL() \
+ do { \
+ } while (0)
+
+namespace mongo {
+namespace sm {
+JS_PUBLIC_API(size_t) get_total_bytes();
+JS_PUBLIC_API(void) reset(size_t max_bytes);
+JS_PUBLIC_API(size_t) get_max_bytes();
+} // namespace sm
+} // namespace mongo
+
+JS_PUBLIC_API(void*) js_malloc(size_t bytes);
+JS_PUBLIC_API(void*) js_calloc(size_t bytes);
+JS_PUBLIC_API(void*) js_calloc(size_t nmemb, size_t size);
+JS_PUBLIC_API(void) js_free(void* p);
+JS_PUBLIC_API(void*) js_realloc(void* p, size_t bytes);
+JS_PUBLIC_API(char*) js_strdup(const char* s);
diff --git a/src/third_party/mozjs-38/mongo_sources/solaris_hacks.h b/src/third_party/mozjs-38/mongo_sources/solaris_hacks.h
new file mode 100644
index 00000000000..3e792f5427f
--- /dev/null
+++ b/src/third_party/mozjs-38/mongo_sources/solaris_hacks.h
@@ -0,0 +1,44 @@
+/* Copyright 2015 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#pragma once
+
+#include <cstddef>
+
+/* Solaris doesn't expose madvise to c++ compilers, so just define in
+ * posix_madvise
+ */
+#define madvise posix_madvise
+
+/* This doesn't seem to be provided on solaris. This no opt function is
+ * similiar to a patch that was introduced into firefox after 38
+ */
+namespace js {
+namespace gc {
+static void* MapAlignedPagesLastDitch(unsigned long, unsigned long alignment) { return nullptr; }
+}
+}
diff --git a/src/third_party/mozjs-38/mongo_sources/vm/PosixNSPR.cpp b/src/third_party/mozjs-38/mongo_sources/vm/PosixNSPR.cpp
new file mode 100644
index 00000000000..2abfcfe2afe
--- /dev/null
+++ b/src/third_party/mozjs-38/mongo_sources/vm/PosixNSPR.cpp
@@ -0,0 +1,5 @@
+/**
+ * This file is purposefully empty. We use it to satisfy the unity headers that
+ * mozilla's configure generates. It's fine that it's empty because we supply
+ * all the symbols we need in src/mongo/scripting/mozjs/PosixNSPR.cpp
+ */
diff --git a/src/third_party/mozjs-38/mongo_sources/vm/PosixNSPR.h b/src/third_party/mozjs-38/mongo_sources/vm/PosixNSPR.h
new file mode 100644
index 00000000000..ddd9aa2f6f9
--- /dev/null
+++ b/src/third_party/mozjs-38/mongo_sources/vm/PosixNSPR.h
@@ -0,0 +1,137 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef vm_PosixNSPR_h
+#define vm_PosixNSPR_h
+
+#include <stdint.h>
+
+namespace nspr {
+class Thread;
+class Lock;
+class CondVar;
+};
+
+typedef nspr::Thread PRThread;
+typedef nspr::Lock PRLock;
+typedef nspr::CondVar PRCondVar;
+
+enum PRThreadType {
+ PR_USER_THREAD,
+ PR_SYSTEM_THREAD
+};
+
+enum PRThreadPriority
+{
+ PR_PRIORITY_FIRST = 0,
+ PR_PRIORITY_LOW = 0,
+ PR_PRIORITY_NORMAL = 1,
+ PR_PRIORITY_HIGH = 2,
+ PR_PRIORITY_URGENT = 3,
+ PR_PRIORITY_LAST = 3
+};
+
+enum PRThreadScope {
+ PR_LOCAL_THREAD,
+ PR_GLOBAL_THREAD,
+ PR_GLOBAL_BOUND_THREAD
+};
+
+enum PRThreadState {
+ PR_JOINABLE_THREAD,
+ PR_UNJOINABLE_THREAD
+};
+
+PRThread*
+PR_CreateThread(PRThreadType type,
+ void (*start)(void* arg),
+ void* arg,
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ uint32_t stackSize);
+
+typedef enum { PR_FAILURE = -1, PR_SUCCESS = 0 } PRStatus;
+
+PRStatus
+PR_JoinThread(PRThread* thread);
+
+PRThread*
+PR_GetCurrentThread();
+
+PRStatus
+PR_SetCurrentThreadName(const char* name);
+
+typedef void (*PRThreadPrivateDTOR)(void* priv);
+
+PRStatus
+PR_NewThreadPrivateIndex(unsigned* newIndex, PRThreadPrivateDTOR destructor);
+
+PRStatus
+PR_SetThreadPrivate(unsigned index, void* priv);
+
+void*
+PR_GetThreadPrivate(unsigned index);
+
+struct PRCallOnceType {
+ int initialized;
+ int32_t inProgress;
+ PRStatus status;
+};
+
+typedef PRStatus (*PRCallOnceFN)();
+
+PRStatus
+PR_CallOnce(PRCallOnceType* once, PRCallOnceFN func);
+
+typedef PRStatus (*PRCallOnceWithArgFN)(void*);
+
+PRStatus
+PR_CallOnceWithArg(PRCallOnceType* once, PRCallOnceWithArgFN func, void* arg);
+
+PRLock*
+PR_NewLock();
+
+void
+PR_DestroyLock(PRLock* lock);
+
+void
+PR_Lock(PRLock* lock);
+
+PRStatus
+PR_Unlock(PRLock* lock);
+
+PRCondVar*
+PR_NewCondVar(PRLock* lock);
+
+void
+PR_DestroyCondVar(PRCondVar* cvar);
+
+PRStatus
+PR_NotifyCondVar(PRCondVar* cvar);
+
+PRStatus
+PR_NotifyAllCondVar(PRCondVar* cvar);
+
+#define PR_INTERVAL_MIN 1000UL
+#define PR_INTERVAL_MAX 100000UL
+
+#define PR_INTERVAL_NO_WAIT 0UL
+#define PR_INTERVAL_NO_TIMEOUT 0xffffffffUL
+
+uint32_t
+PR_MillisecondsToInterval(uint32_t milli);
+
+uint32_t
+PR_MicrosecondsToInterval(uint32_t micro);
+
+uint32_t
+PR_TicksPerSecond();
+
+PRStatus
+PR_WaitCondVar(PRCondVar* cvar, uint32_t timeout);
+
+#endif /* vm_PosixNSPR_h */
diff --git a/src/third_party/shim_mozjs.cpp b/src/third_party/shim_mozjs.cpp
new file mode 100644
index 00000000000..c1b9dbe8ec2
--- /dev/null
+++ b/src/third_party/shim_mozjs.cpp
@@ -0,0 +1,3 @@
+// This file intentionally blank. shim_mozjs.cpp is part of the
+// third_party/mozjs library, which is just a placeholder for forwarding
+// library dependencies.