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