1 
2 /**
3  *    Copyright (C) 2018-present MongoDB, Inc.
4  *
5  *    This program is free software: you can redistribute it and/or modify
6  *    it under the terms of the Server Side Public License, version 1,
7  *    as published by MongoDB, Inc.
8  *
9  *    This program is distributed in the hope that it will be useful,
10  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *    Server Side Public License for more details.
13  *
14  *    You should have received a copy of the Server Side Public License
15  *    along with this program. If not, see
16  *    <http://www.mongodb.com/licensing/server-side-public-license>.
17  *
18  *    As a special exception, the copyright holders give permission to link the
19  *    code of portions of this program with the OpenSSL library under certain
20  *    conditions as described in each individual source file and distribute
21  *    linked combinations including the program with the OpenSSL library. You
22  *    must comply with the Server Side Public License in all respects for
23  *    all of the code used other than as permitted herein. If you modify file(s)
24  *    with this exception, you may extend this exception to your version of the
25  *    file(s), but you are not obligated to do so. If you do not wish to do so,
26  *    delete this exception statement from your version. If you delete this
27  *    exception statement from all source files in the program, then also delete
28  *    it in the license file.
29  */
30 
31 #pragma once
32 
33 #include <jsapi.h>
34 #include <vm/PosixNSPR.h>
35 
36 #include "mongo/client/dbclientcursor.h"
37 #include "mongo/scripting/mozjs/bindata.h"
38 #include "mongo/scripting/mozjs/bson.h"
39 #include "mongo/scripting/mozjs/code.h"
40 #include "mongo/scripting/mozjs/countdownlatch.h"
41 #include "mongo/scripting/mozjs/cursor.h"
42 #include "mongo/scripting/mozjs/cursor_handle.h"
43 #include "mongo/scripting/mozjs/db.h"
44 #include "mongo/scripting/mozjs/dbcollection.h"
45 #include "mongo/scripting/mozjs/dbpointer.h"
46 #include "mongo/scripting/mozjs/dbquery.h"
47 #include "mongo/scripting/mozjs/dbref.h"
48 #include "mongo/scripting/mozjs/engine.h"
49 #include "mongo/scripting/mozjs/error.h"
50 #include "mongo/scripting/mozjs/global.h"
51 #include "mongo/scripting/mozjs/internedstring.h"
52 #include "mongo/scripting/mozjs/jsthread.h"
53 #include "mongo/scripting/mozjs/maxkey.h"
54 #include "mongo/scripting/mozjs/minkey.h"
55 #include "mongo/scripting/mozjs/mongo.h"
56 #include "mongo/scripting/mozjs/mongohelpers.h"
57 #include "mongo/scripting/mozjs/nativefunction.h"
58 #include "mongo/scripting/mozjs/numberdecimal.h"
59 #include "mongo/scripting/mozjs/numberint.h"
60 #include "mongo/scripting/mozjs/numberlong.h"
61 #include "mongo/scripting/mozjs/object.h"
62 #include "mongo/scripting/mozjs/oid.h"
63 #include "mongo/scripting/mozjs/regexp.h"
64 #include "mongo/scripting/mozjs/session.h"
65 #include "mongo/scripting/mozjs/timestamp.h"
66 #include "mongo/scripting/mozjs/uri.h"
67 #include "mongo/stdx/unordered_set.h"
68 
69 namespace mongo {
70 namespace mozjs {
71 
72 /**
73  * Implementation Scope for MozJS
74  *
75  * The Implementation scope holds the actual mozjs runtime and context objects,
76  * along with a number of global prototypes for mongoDB specific types. Each
77  * ImplScope requires it's own thread and cannot be accessed from any thread
78  * other than the one it was created on (this is a detail inherited from the
79  * JSRuntime). If you need a scope that can be accessed by different threads
80  * over the course of it's lifetime, see MozJSProxyScope
81  *
82  * For more information about overriden fields, see mongo::Scope
83  */
84 class MozJSImplScope final : public Scope {
85     MONGO_DISALLOW_COPYING(MozJSImplScope);
86 
87 public:
88     explicit MozJSImplScope(MozJSScriptEngine* engine);
89     ~MozJSImplScope();
90 
91     void init(const BSONObj* data) override;
92 
93     void reset() override;
94 
95     void kill() override;
96 
97     void interrupt();
98 
99     bool isKillPending() const override;
100 
101     OperationContext* getOpContext() const;
102 
103     void registerOperation(OperationContext* opCtx) override;
104 
105     void unregisterOperation() override;
106 
107     void localConnectForDbEval(OperationContext* opCtx, const char* dbName) override;
108 
109     void externalSetup() override;
110 
111     std::string getError() override;
112 
113     bool hasOutOfMemoryException() override;
114 
115     void gc() override;
116 
117     void sleep(Milliseconds ms);
118 
119     bool isJavaScriptProtectionEnabled() const;
120 
121     double getNumber(const char* field) override;
122     int getNumberInt(const char* field) override;
123     long long getNumberLongLong(const char* field) override;
124     Decimal128 getNumberDecimal(const char* field) override;
125     std::string getString(const char* field) override;
126     bool getBoolean(const char* field) override;
127     BSONObj getObject(const char* field) override;
128 
129     void setNumber(const char* field, double val) override;
130     void setString(const char* field, StringData val) override;
131     void setBoolean(const char* field, bool val) override;
132     void setElement(const char* field, const BSONElement& e, const BSONObj& parent) override;
133     void setObject(const char* field, const BSONObj& obj, bool readOnly) override;
134     void setFunction(const char* field, const char* code) override;
135 
136     int type(const char* field) override;
137 
138     void rename(const char* from, const char* to) override;
139 
140     int invoke(ScriptingFunction func,
141                const BSONObj* args,
142                const BSONObj* recv,
143                int timeoutMs = 0,
144                bool ignoreReturn = false,
145                bool readOnlyArgs = false,
146                bool readOnlyRecv = false) override;
147 
148     bool exec(StringData code,
149               const std::string& name,
150               bool printResult,
151               bool reportError,
152               bool assertOnError,
153               int timeoutMs) override;
154 
155     void injectNative(const char* field, NativeFunction func, void* data = 0) override;
156 
157     ScriptingFunction _createFunction(const char* code) override;
158 
159     void newFunction(StringData code, JS::MutableHandleValue out);
160 
161     BSONObj callThreadArgs(const BSONObj& obj);
162 
163     template <typename T>
getProto()164     typename std::enable_if<std::is_same<T, BinDataInfo>::value, WrapType<T>&>::type getProto() {
165         return _binDataProto;
166     }
167 
168     template <typename T>
getProto()169     typename std::enable_if<std::is_same<T, BSONInfo>::value, WrapType<T>&>::type getProto() {
170         return _bsonProto;
171     }
172 
173     template <typename T>
174     typename std::enable_if<std::is_same<T, CountDownLatchInfo>::value, WrapType<T>&>::type
getProto()175     getProto() {
176         return _countDownLatchProto;
177     }
178 
179     template <typename T>
getProto()180     typename std::enable_if<std::is_same<T, CursorInfo>::value, WrapType<T>&>::type getProto() {
181         return _cursorProto;
182     }
183 
184     template <typename T>
185     typename std::enable_if<std::is_same<T, CursorHandleInfo>::value, WrapType<T>&>::type
getProto()186     getProto() {
187         return _cursorHandleProto;
188     }
189 
190     template <typename T>
191     typename std::enable_if<std::is_same<T, DBCollectionInfo>::value, WrapType<T>&>::type
getProto()192     getProto() {
193         return _dbCollectionProto;
194     }
195 
196     template <typename T>
getProto()197     typename std::enable_if<std::is_same<T, DBPointerInfo>::value, WrapType<T>&>::type getProto() {
198         return _dbPointerProto;
199     }
200 
201     template <typename T>
getProto()202     typename std::enable_if<std::is_same<T, DBQueryInfo>::value, WrapType<T>&>::type getProto() {
203         return _dbQueryProto;
204     }
205 
206     template <typename T>
getProto()207     typename std::enable_if<std::is_same<T, DBInfo>::value, WrapType<T>&>::type getProto() {
208         return _dbProto;
209     }
210 
211     template <typename T>
getProto()212     typename std::enable_if<std::is_same<T, DBRefInfo>::value, WrapType<T>&>::type getProto() {
213         return _dbRefProto;
214     }
215 
216     template <typename T>
getProto()217     typename std::enable_if<std::is_same<T, ErrorInfo>::value, WrapType<T>&>::type getProto() {
218         return _errorProto;
219     }
220 
221     template <typename T>
getProto()222     typename std::enable_if<std::is_same<T, JSThreadInfo>::value, WrapType<T>&>::type getProto() {
223         return _jsThreadProto;
224     }
225 
226     template <typename T>
getProto()227     typename std::enable_if<std::is_same<T, MaxKeyInfo>::value, WrapType<T>&>::type getProto() {
228         return _maxKeyProto;
229     }
230 
231     template <typename T>
getProto()232     typename std::enable_if<std::is_same<T, MinKeyInfo>::value, WrapType<T>&>::type getProto() {
233         return _minKeyProto;
234     }
235 
236     template <typename T>
237     typename std::enable_if<std::is_same<T, MongoExternalInfo>::value, WrapType<T>&>::type
getProto()238     getProto() {
239         return _mongoExternalProto;
240     }
241 
242     template <typename T>
243     typename std::enable_if<std::is_same<T, MongoHelpersInfo>::value, WrapType<T>&>::type
getProto()244     getProto() {
245         return _mongoHelpersProto;
246     }
247 
248     template <typename T>
getProto()249     typename std::enable_if<std::is_same<T, MongoLocalInfo>::value, WrapType<T>&>::type getProto() {
250         return _mongoLocalProto;
251     }
252 
253     template <typename T>
254     typename std::enable_if<std::is_same<T, NativeFunctionInfo>::value, WrapType<T>&>::type
getProto()255     getProto() {
256         return _nativeFunctionProto;
257     }
258 
259     template <typename T>
getProto()260     typename std::enable_if<std::is_same<T, NumberIntInfo>::value, WrapType<T>&>::type getProto() {
261         return _numberIntProto;
262     }
263 
264     template <typename T>
getProto()265     typename std::enable_if<std::is_same<T, NumberLongInfo>::value, WrapType<T>&>::type getProto() {
266         return _numberLongProto;
267     }
268 
269     template <typename T>
270     typename std::enable_if<std::is_same<T, NumberDecimalInfo>::value, WrapType<T>&>::type
getProto()271     getProto() {
272         return _numberDecimalProto;
273     }
274 
275     template <typename T>
getProto()276     typename std::enable_if<std::is_same<T, CodeInfo>::value, WrapType<T>&>::type getProto() {
277         return _codeProto;
278     }
279 
280     template <typename T>
getProto()281     typename std::enable_if<std::is_same<T, ObjectInfo>::value, WrapType<T>&>::type getProto() {
282         return _objectProto;
283     }
284 
285     template <typename T>
getProto()286     typename std::enable_if<std::is_same<T, OIDInfo>::value, WrapType<T>&>::type getProto() {
287         return _oidProto;
288     }
289 
290     template <typename T>
getProto()291     typename std::enable_if<std::is_same<T, RegExpInfo>::value, WrapType<T>&>::type getProto() {
292         return _regExpProto;
293     }
294 
295     template <typename T>
getProto()296     typename std::enable_if<std::is_same<T, SessionInfo>::value, WrapType<T>&>::type getProto() {
297         return _sessionProto;
298     }
299 
300     template <typename T>
getProto()301     typename std::enable_if<std::is_same<T, TimestampInfo>::value, WrapType<T>&>::type getProto() {
302         return _timestampProto;
303     }
304 
305     template <typename T>
getProto()306     typename std::enable_if<std::is_same<T, URIInfo>::value, WrapType<T>&>::type getProto() {
307         return _uriProto;
308     }
309 
310     static const char* const kExecResult;
311     static const char* const kInvokeResult;
312 
313     static MozJSImplScope* getThreadScope();
314     void setOOM();
315     void setParentStack(std::string);
316     const std::string& getParentStack() const;
317 
318     std::size_t getGeneration() const;
319 
320     void advanceGeneration() override;
321 
322     void requireOwnedObjects() override;
323 
324     bool requiresOwnedObjects() const;
325 
getInternedStringId(InternedString name)326     JS::HandleId getInternedStringId(InternedString name) {
327         return _internedStrings.getInternedString(name);
328     }
329 
330     std::string buildStackString();
331 
332     template <typename T, typename... Args>
trackedNew(Args &&...args)333     T* trackedNew(Args&&... args) {
334         T* t = new T(std::forward<Args>(args)...);
335         _asanHandles.addPointer(t);
336         return t;
337     }
338 
339     template <typename T>
trackedDelete(T * t)340     void trackedDelete(T* t) {
341         _asanHandles.removePointer(t);
342         delete (t);
343     }
344 
345     struct ASANHandles {
346         ASANHandles();
347         ~ASANHandles();
348 
349         void addPointer(void* ptr);
350         void removePointer(void* ptr);
351 
352         stdx::unordered_set<void*> _handles;
353 
354         static ASANHandles* getThreadASANHandles();
355     };
356 
357     void setStatus(Status status);
358 
359 private:
360     template <typename ImplScopeFunction>
361     auto _runSafely(ImplScopeFunction&& functionToRun) -> decltype(functionToRun());
362 
363     void _MozJSCreateFunction(StringData raw, JS::MutableHandleValue fun);
364 
365     /**
366      * This structure exists exclusively to construct the runtime and context
367      * ahead of the various global prototypes in the ImplScope construction.
368      * Basically, we have to call some c apis on the way up and down and this
369      * takes care of that
370      */
371     struct MozRuntime {
372     public:
373         MozRuntime(const MozJSScriptEngine* engine);
374 
375         std::unique_ptr<PRThread, std::function<void(PRThread*)>> _thread;
376         std::unique_ptr<JSRuntime, std::function<void(JSRuntime*)>> _runtime;
377         std::unique_ptr<JSContext, std::function<void(JSContext*)>> _context;
378     };
379 
380     /**
381      * The connection state of the scope.
382      *
383      * This is for dbeval and the shell
384      */
385     enum class ConnectState : char {
386         Not,
387         Local,
388         External,
389     };
390 
391     struct MozJSEntry;
392     friend struct MozJSEntry;
393 
394     static void _reportError(JSContext* cx, const char* message, JSErrorReport* report);
395     static bool _interruptCallback(JSContext* cx);
396     static void _gcCallback(JSRuntime* rt, JSGCStatus status, void* data);
397     bool _checkErrorState(bool success, bool reportError = true, bool assertOnError = true);
398 
399     void installDBAccess();
400     void installBSONTypes();
401     void installFork();
402 
403     void setCompileOptions(JS::CompileOptions* co);
404 
405     ASANHandles _asanHandles;
406     MozJSScriptEngine* _engine;
407     MozRuntime _mr;
408     JSRuntime* _runtime;
409     JSContext* _context;
410     WrapType<GlobalInfo> _globalProto;
411     JS::HandleObject _global;
412     std::vector<JS::PersistentRootedValue> _funcs;
413     InternedStringTable _internedStrings;
414     Status _killStatus;
415     mutable std::mutex _mutex;
416     std::condition_variable _sleepCondition;
417     std::string _error;
418     unsigned int _opId;        // op id for this scope
419     OperationContext* _opCtx;  // Op context for DbEval
420     std::size_t _inOp;
421     std::atomic<bool> _pendingGC;
422     ConnectState _connectState;
423     Status _status;
424     std::string _parentStack;
425     std::size_t _generation;
426     bool _requireOwnedObjects;
427     bool _hasOutOfMemoryException;
428 
429     WrapType<BinDataInfo> _binDataProto;
430     WrapType<BSONInfo> _bsonProto;
431     WrapType<CodeInfo> _codeProto;
432     WrapType<CountDownLatchInfo> _countDownLatchProto;
433     WrapType<CursorHandleInfo> _cursorHandleProto;
434     WrapType<CursorInfo> _cursorProto;
435     WrapType<DBCollectionInfo> _dbCollectionProto;
436     WrapType<DBInfo> _dbProto;
437     WrapType<DBPointerInfo> _dbPointerProto;
438     WrapType<DBQueryInfo> _dbQueryProto;
439     WrapType<DBRefInfo> _dbRefProto;
440     WrapType<ErrorInfo> _errorProto;
441     WrapType<JSThreadInfo> _jsThreadProto;
442     WrapType<MaxKeyInfo> _maxKeyProto;
443     WrapType<MinKeyInfo> _minKeyProto;
444     WrapType<MongoExternalInfo> _mongoExternalProto;
445     WrapType<MongoHelpersInfo> _mongoHelpersProto;
446     WrapType<MongoLocalInfo> _mongoLocalProto;
447     WrapType<NativeFunctionInfo> _nativeFunctionProto;
448     WrapType<NumberDecimalInfo> _numberDecimalProto;
449     WrapType<NumberIntInfo> _numberIntProto;
450     WrapType<NumberLongInfo> _numberLongProto;
451     WrapType<ObjectInfo> _objectProto;
452     WrapType<OIDInfo> _oidProto;
453     WrapType<RegExpInfo> _regExpProto;
454     WrapType<SessionInfo> _sessionProto;
455     WrapType<TimestampInfo> _timestampProto;
456     WrapType<URIInfo> _uriProto;
457 };
458 
getScope(JSContext * cx)459 inline MozJSImplScope* getScope(JSContext* cx) {
460     return static_cast<MozJSImplScope*>(JS_GetContextPrivate(cx));
461 }
462 
getScope(JSFreeOp * fop)463 inline MozJSImplScope* getScope(JSFreeOp* fop) {
464     return static_cast<MozJSImplScope*>(JS_GetRuntimePrivate(fop->runtime()));
465 }
466 
467 }  // namespace mozjs
468 }  // namespace mongo
469