SERVER-18531 Integrate SpiderMonkey
Provides SpiderMonkey 38.0.1esr as a JS engine for mongo and mongod.
This commit is contained in:
parent
1af5f44f9b
commit
e749ffad9b
14
SConstruct
14
SConstruct
@ -214,7 +214,7 @@ add_option('wiredtiger',
|
||||
)
|
||||
|
||||
# library choices
|
||||
js_engine_choices = ['v8-3.12', 'v8-3.25', 'none']
|
||||
js_engine_choices = ['v8-3.12', 'v8-3.25', 'mozjs', 'none']
|
||||
add_option('js-engine',
|
||||
choices=js_engine_choices,
|
||||
default=js_engine_choices[0],
|
||||
@ -756,12 +756,14 @@ jsEngine = get_option( "js-engine")
|
||||
|
||||
serverJs = get_option( "server-js" ) == "on"
|
||||
|
||||
usev8 = (jsEngine != 'none')
|
||||
usev8 = (jsEngine.startswith('v8'))
|
||||
|
||||
usemozjs = (jsEngine.startswith('mozjs'))
|
||||
|
||||
v8version = jsEngine[3:] if jsEngine.startswith('v8-') else 'none'
|
||||
v8suffix = '' if v8version == '3.12' else '-' + v8version
|
||||
|
||||
if not serverJs and not usev8:
|
||||
if not serverJs and not usev8 and not usemozjs:
|
||||
print("Warning: --server-js=off is not needed with --js-engine=none")
|
||||
|
||||
# We defer building the env until we have determined whether we want certain values. Some values
|
||||
@ -1197,12 +1199,9 @@ elif env.TargetOSIs('windows'):
|
||||
'DbgHelp.lib',
|
||||
'shell32.lib',
|
||||
'Iphlpapi.lib',
|
||||
'winmm.lib',
|
||||
'version.lib'])
|
||||
|
||||
# v8 calls timeGetTime()
|
||||
if usev8:
|
||||
env.Append(LIBS=['winmm.lib'])
|
||||
|
||||
# When building on visual studio, this sets the name of the debug symbols file
|
||||
if env.ToolchainIs('msvc'):
|
||||
env['PDB'] = '${TARGET.base}.pdb'
|
||||
@ -2303,6 +2302,7 @@ Export("get_option")
|
||||
Export("has_option use_system_version_of_library")
|
||||
Export("serverJs")
|
||||
Export("usev8")
|
||||
Export("usemozjs")
|
||||
Export("v8version v8suffix")
|
||||
Export("boostSuffix")
|
||||
Export('module_sconscripts')
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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 ):
|
||||
|
||||
1
debian/mongodb-enterprise-server.docs
vendored
1
debian/mongodb-enterprise-server.docs
vendored
@ -6,3 +6,4 @@ snmp/MONGODBINC-MIB.txt
|
||||
LICENSE.txt
|
||||
README
|
||||
THIRD-PARTY-NOTICES
|
||||
MPL-2
|
||||
|
||||
@ -6,3 +6,4 @@ snmp/MONGODBINC-MIB.txt
|
||||
LICENSE.txt
|
||||
README
|
||||
THIRD-PARTY-NOTICES
|
||||
MPL-2
|
||||
|
||||
1
debian/mongodb-org-server.docs
vendored
1
debian/mongodb-org-server.docs
vendored
@ -1,3 +1,4 @@
|
||||
GNU-AGPL-3.0
|
||||
README
|
||||
THIRD-PARTY-NOTICES
|
||||
MPL-2
|
||||
|
||||
1
debian/mongodb-org-unstable-server.docs
vendored
1
debian/mongodb-org-unstable-server.docs
vendored
@ -1,3 +1,4 @@
|
||||
GNU-AGPL-3.0
|
||||
README
|
||||
THIRD-PARTY-NOTICES
|
||||
MPL-2
|
||||
|
||||
373
distsrc/MPL-2
Normal file
373
distsrc/MPL-2
Normal 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.
|
||||
@ -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
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -220,6 +220,7 @@ fi
|
||||
%doc LICENSE.txt
|
||||
%doc README
|
||||
%doc THIRD-PARTY-NOTICES
|
||||
%doc MPL-2
|
||||
|
||||
%files shell
|
||||
%defattr(-,root,root,-)
|
||||
|
||||
@ -229,6 +229,7 @@ fi
|
||||
%doc LICENSE.txt
|
||||
%doc README
|
||||
%doc THIRD-PARTY-NOTICES
|
||||
%doc MPL-2
|
||||
|
||||
|
||||
|
||||
|
||||
@ -214,6 +214,7 @@ fi
|
||||
%doc GNU-AGPL-3.0
|
||||
%doc README
|
||||
%doc THIRD-PARTY-NOTICES
|
||||
%doc MPL-2
|
||||
|
||||
%files shell
|
||||
%defattr(-,root,root,-)
|
||||
|
||||
@ -224,6 +224,7 @@ fi
|
||||
%doc GNU-AGPL-3.0
|
||||
%doc README
|
||||
%doc THIRD-PARTY-NOTICES
|
||||
%doc MPL-2
|
||||
|
||||
|
||||
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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 ?>
|
||||
|
||||
@ -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=[
|
||||
],
|
||||
|
||||
@ -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"
|
||||
@ -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"
|
||||
|
||||
/**
|
||||
|
||||
@ -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"
|
||||
|
||||
/**
|
||||
|
||||
259
src/mongo/scripting/mozjs/PosixNSPR.cpp
Normal file
259
src/mongo/scripting/mozjs/PosixNSPR.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
68
src/mongo/scripting/mozjs/base.cpp
Normal file
68
src/mongo/scripting/mozjs/base.cpp
Normal 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
|
||||
92
src/mongo/scripting/mozjs/base.h
Normal file
92
src/mongo/scripting/mozjs/base.h
Normal 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
|
||||
216
src/mongo/scripting/mozjs/bindata.cpp
Normal file
216
src/mongo/scripting/mozjs/bindata.cpp
Normal 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
|
||||
63
src/mongo/scripting/mozjs/bindata.h
Normal file
63
src/mongo/scripting/mozjs/bindata.h
Normal 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
|
||||
235
src/mongo/scripting/mozjs/bson.cpp
Normal file
235
src/mongo/scripting/mozjs/bson.cpp
Normal 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
|
||||
77
src/mongo/scripting/mozjs/bson.h
Normal file
77
src/mongo/scripting/mozjs/bson.h
Normal 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
|
||||
198
src/mongo/scripting/mozjs/countdownlatch.cpp
Normal file
198
src/mongo/scripting/mozjs/countdownlatch.cpp
Normal 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
|
||||
61
src/mongo/scripting/mozjs/countdownlatch.h
Normal file
61
src/mongo/scripting/mozjs/countdownlatch.h
Normal 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
|
||||
122
src/mongo/scripting/mozjs/cursor.cpp
Normal file
122
src/mongo/scripting/mozjs/cursor.cpp
Normal 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
|
||||
76
src/mongo/scripting/mozjs/cursor.h
Normal file
76
src/mongo/scripting/mozjs/cursor.h
Normal 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
|
||||
139
src/mongo/scripting/mozjs/db.cpp
Normal file
139
src/mongo/scripting/mozjs/db.cpp
Normal 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
|
||||
56
src/mongo/scripting/mozjs/db.h
Normal file
56
src/mongo/scripting/mozjs/db.h
Normal 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
|
||||
85
src/mongo/scripting/mozjs/dbcollection.cpp
Normal file
85
src/mongo/scripting/mozjs/dbcollection.cpp
Normal 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
|
||||
56
src/mongo/scripting/mozjs/dbcollection.h
Normal file
56
src/mongo/scripting/mozjs/dbcollection.h
Normal 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
|
||||
66
src/mongo/scripting/mozjs/dbpointer.cpp
Normal file
66
src/mongo/scripting/mozjs/dbpointer.cpp
Normal 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
|
||||
52
src/mongo/scripting/mozjs/dbpointer.h
Normal file
52
src/mongo/scripting/mozjs/dbpointer.h
Normal 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
|
||||
143
src/mongo/scripting/mozjs/dbquery.cpp
Normal file
143
src/mongo/scripting/mozjs/dbquery.cpp
Normal 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
|
||||
53
src/mongo/scripting/mozjs/dbquery.h
Normal file
53
src/mongo/scripting/mozjs/dbquery.h
Normal 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
|
||||
68
src/mongo/scripting/mozjs/dbref.cpp
Normal file
68
src/mongo/scripting/mozjs/dbref.cpp
Normal 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
|
||||
53
src/mongo/scripting/mozjs/dbref.h
Normal file
53
src/mongo/scripting/mozjs/dbref.h
Normal 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
|
||||
133
src/mongo/scripting/mozjs/engine.cpp
Normal file
133
src/mongo/scripting/mozjs/engine.cpp
Normal 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
|
||||
93
src/mongo/scripting/mozjs/engine.h
Normal file
93
src/mongo/scripting/mozjs/engine.h
Normal 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
|
||||
89
src/mongo/scripting/mozjs/exception.cpp
Normal file
89
src/mongo/scripting/mozjs/exception.cpp
Normal 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
|
||||
67
src/mongo/scripting/mozjs/exception.h
Normal file
67
src/mongo/scripting/mozjs/exception.h
Normal 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
|
||||
103
src/mongo/scripting/mozjs/global.cpp
Normal file
103
src/mongo/scripting/mozjs/global.cpp
Normal 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
|
||||
56
src/mongo/scripting/mozjs/global.h
Normal file
56
src/mongo/scripting/mozjs/global.h
Normal 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
|
||||
74
src/mongo/scripting/mozjs/idwrapper.cpp
Normal file
74
src/mongo/scripting/mozjs/idwrapper.cpp
Normal 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
|
||||
71
src/mongo/scripting/mozjs/idwrapper.h
Normal file
71
src/mongo/scripting/mozjs/idwrapper.h
Normal 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
|
||||
728
src/mongo/scripting/mozjs/implscope.cpp
Normal file
728
src/mongo/scripting/mozjs/implscope.cpp
Normal 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
|
||||
323
src/mongo/scripting/mozjs/implscope.h
Normal file
323
src/mongo/scripting/mozjs/implscope.h
Normal 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
|
||||
234
src/mongo/scripting/mozjs/jscustomallocator.cpp
Normal file
234
src/mongo/scripting/mozjs/jscustomallocator.cpp
Normal 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;
|
||||
}
|
||||
84
src/mongo/scripting/mozjs/jsstringwrapper.cpp
Normal file
84
src/mongo/scripting/mozjs/jsstringwrapper.cpp
Normal 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
|
||||
61
src/mongo/scripting/mozjs/jsstringwrapper.h
Normal file
61
src/mongo/scripting/mozjs/jsstringwrapper.h
Normal 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
|
||||
274
src/mongo/scripting/mozjs/jsthread.cpp
Normal file
274
src/mongo/scripting/mozjs/jsthread.cpp
Normal 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
|
||||
74
src/mongo/scripting/mozjs/jsthread.h
Normal file
74
src/mongo/scripting/mozjs/jsthread.h
Normal 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
|
||||
94
src/mongo/scripting/mozjs/maxkey.cpp
Normal file
94
src/mongo/scripting/mozjs/maxkey.cpp
Normal 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
|
||||
59
src/mongo/scripting/mozjs/maxkey.h
Normal file
59
src/mongo/scripting/mozjs/maxkey.h
Normal 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
|
||||
94
src/mongo/scripting/mozjs/minkey.cpp
Normal file
94
src/mongo/scripting/mozjs/minkey.cpp
Normal 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
|
||||
59
src/mongo/scripting/mozjs/minkey.h
Normal file
59
src/mongo/scripting/mozjs/minkey.h
Normal 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
|
||||
565
src/mongo/scripting/mozjs/mongo.cpp
Normal file
565
src/mongo/scripting/mozjs/mongo.cpp
Normal 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
|
||||
88
src/mongo/scripting/mozjs/mongo.h
Normal file
88
src/mongo/scripting/mozjs/mongo.h
Normal 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
|
||||
124
src/mongo/scripting/mozjs/nativefunction.cpp
Normal file
124
src/mongo/scripting/mozjs/nativefunction.cpp
Normal 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
|
||||
73
src/mongo/scripting/mozjs/nativefunction.h
Normal file
73
src/mongo/scripting/mozjs/nativefunction.h
Normal 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
|
||||
112
src/mongo/scripting/mozjs/numberint.cpp
Normal file
112
src/mongo/scripting/mozjs/numberint.cpp
Normal 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
|
||||
61
src/mongo/scripting/mozjs/numberint.h
Normal file
61
src/mongo/scripting/mozjs/numberint.h
Normal 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
|
||||
164
src/mongo/scripting/mozjs/numberlong.cpp
Normal file
164
src/mongo/scripting/mozjs/numberlong.cpp
Normal 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
|
||||
68
src/mongo/scripting/mozjs/numberlong.h
Normal file
68
src/mongo/scripting/mozjs/numberlong.h
Normal 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
|
||||
87
src/mongo/scripting/mozjs/object.cpp
Normal file
87
src/mongo/scripting/mozjs/object.cpp
Normal 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
|
||||
56
src/mongo/scripting/mozjs/object.h
Normal file
56
src/mongo/scripting/mozjs/object.h
Normal 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
|
||||
385
src/mongo/scripting/mozjs/objectwrapper.cpp
Normal file
385
src/mongo/scripting/mozjs/objectwrapper.cpp
Normal 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
|
||||
181
src/mongo/scripting/mozjs/objectwrapper.h
Normal file
181
src/mongo/scripting/mozjs/objectwrapper.h
Normal 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
|
||||
87
src/mongo/scripting/mozjs/oid.cpp
Normal file
87
src/mongo/scripting/mozjs/oid.cpp
Normal 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
|
||||
54
src/mongo/scripting/mozjs/oid.h
Normal file
54
src/mongo/scripting/mozjs/oid.h
Normal 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
|
||||
318
src/mongo/scripting/mozjs/proxyscope.cpp
Normal file
318
src/mongo/scripting/mozjs/proxyscope.cpp
Normal 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
|
||||
195
src/mongo/scripting/mozjs/proxyscope.h
Normal file
195
src/mongo/scripting/mozjs/proxyscope.h
Normal 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
|
||||
39
src/mongo/scripting/mozjs/regexp.cpp
Normal file
39
src/mongo/scripting/mozjs/regexp.cpp
Normal 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
|
||||
49
src/mongo/scripting/mozjs/regexp.h
Normal file
49
src/mongo/scripting/mozjs/regexp.h
Normal 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
|
||||
77
src/mongo/scripting/mozjs/timestamp.cpp
Normal file
77
src/mongo/scripting/mozjs/timestamp.cpp
Normal 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
|
||||
53
src/mongo/scripting/mozjs/timestamp.h
Normal file
53
src/mongo/scripting/mozjs/timestamp.h
Normal 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
|
||||
272
src/mongo/scripting/mozjs/valuereader.cpp
Normal file
272
src/mongo/scripting/mozjs/valuereader.cpp
Normal 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
|
||||
61
src/mongo/scripting/mozjs/valuereader.h
Normal file
61
src/mongo/scripting/mozjs/valuereader.h
Normal 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
|
||||
252
src/mongo/scripting/mozjs/valuewriter.cpp
Normal file
252
src/mongo/scripting/mozjs/valuewriter.cpp
Normal 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
|
||||
84
src/mongo/scripting/mozjs/valuewriter.h
Normal file
84
src/mongo/scripting/mozjs/valuewriter.h
Normal 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
|
||||
474
src/mongo/scripting/mozjs/wraptype.h
Normal file
474
src/mongo/scripting/mozjs/wraptype.h
Normal 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
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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)
|
||||
|
||||
28
src/third_party/SConscript
vendored
28
src/third_party/SConscript
vendored
@ -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
98
src/third_party/mozjs-38/SConscript
vendored
Normal 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 )
|
||||
54
src/third_party/mozjs-38/mongo_sources/jscustomallocator.h
vendored
Normal file
54
src/third_party/mozjs-38/mongo_sources/jscustomallocator.h
vendored
Normal 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);
|
||||
44
src/third_party/mozjs-38/mongo_sources/solaris_hacks.h
vendored
Normal file
44
src/third_party/mozjs-38/mongo_sources/solaris_hacks.h
vendored
Normal 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; }
|
||||
}
|
||||
}
|
||||
5
src/third_party/mozjs-38/mongo_sources/vm/PosixNSPR.cpp
vendored
Normal file
5
src/third_party/mozjs-38/mongo_sources/vm/PosixNSPR.cpp
vendored
Normal 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
Loading…
Reference in New Issue
Block a user