SERVER-18531 Integrate SpiderMonkey

Provides SpiderMonkey 38.0.1esr as a JS engine for mongo and mongod.
This commit is contained in:
Jason Carey 2015-07-09 14:05:20 -04:00
parent 1af5f44f9b
commit e749ffad9b
102 changed files with 10102 additions and 51 deletions

View File

@ -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')

View File

@ -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:

View File

@ -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)

View File

@ -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 ):

View File

@ -6,3 +6,4 @@ snmp/MONGODBINC-MIB.txt
LICENSE.txt
README
THIRD-PARTY-NOTICES
MPL-2

View File

@ -6,3 +6,4 @@ snmp/MONGODBINC-MIB.txt
LICENSE.txt
README
THIRD-PARTY-NOTICES
MPL-2

View File

@ -1,3 +1,4 @@
GNU-AGPL-3.0
README
THIRD-PARTY-NOTICES
MPL-2

View File

@ -1,3 +1,4 @@
GNU-AGPL-3.0
README
THIRD-PARTY-NOTICES
MPL-2

373
distsrc/MPL-2 Normal file
View File

@ -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.

View File

@ -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

View File

@ -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");

View File

@ -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)
//but waiting for SERVER-3391 (CodeWScope support in shell)

View File

@ -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));

View File

@ -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),

View File

@ -220,6 +220,7 @@ fi
%doc LICENSE.txt
%doc README
%doc THIRD-PARTY-NOTICES
%doc MPL-2
%files shell
%defattr(-,root,root,-)

View File

@ -229,6 +229,7 @@ fi
%doc LICENSE.txt
%doc README
%doc THIRD-PARTY-NOTICES
%doc MPL-2

View File

@ -214,6 +214,7 @@ fi
%doc GNU-AGPL-3.0
%doc README
%doc THIRD-PARTY-NOTICES
%doc MPL-2
%files shell
%defattr(-,root,root,-)

View File

@ -224,6 +224,7 @@ fi
%doc GNU-AGPL-3.0
%doc README
%doc THIRD-PARTY-NOTICES
%doc MPL-2

View File

@ -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:

View File

@ -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

View File

@ -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;
s->invoke("blah.a = 19;", 0, 0);
out = s->getObject("blah");
ASSERT(out["a"].eoo());
/**
* 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.zz.a = 19;", 0, 0);
out = s->getObject("blah");
ASSERT(out["zz"].embeddedObject()["a"].eoo());
s->invoke("blah.a = 19;", 0, 0);
out = s->getObject("blah");
ASSERT(out["a"].eoo());
s->setObject("blah.zz", BSON("a" << 19));
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->invoke("delete blah['x']", 0, 0);
out = s->getObject("blah");
ASSERT(!out["x"].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());
}
// read-only object itself can be overwritten
s->invoke("blah = {}", 0, 0);

View File

@ -25,6 +25,10 @@
<Component Id="c_Readme" Guid="12C17EA1-075C-4A1D-9554-F3B5A2A94874">
<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"
@ -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 ?>

View File

@ -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=[
],

View File

@ -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"

View File

@ -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"
/**

View File

@ -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"
/**

View File

@ -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;
}
}
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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)

View File

@ -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"):

98
src/third_party/mozjs-38/SConscript vendored Normal file
View File

@ -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 )

View File

@ -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);

View File

@ -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; }
}
}

View File

@ -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
*/

Some files were not shown because too many files have changed in this diff Show More