summaryrefslogtreecommitdiff
path: root/scripting/engine.h
blob: 9dd5f1d322c8c692297566ea5207f5321d538abb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
// engine.h

/*    Copyright 2009 10gen Inc.
 *
 *    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.
 */

#pragma once

#include "../pch.h"
#include "../db/jsobj.h"

namespace mongo {

    struct JSFile {
        const char* name;
        const StringData& source;
    };

    typedef unsigned long long ScriptingFunction;
    typedef BSONObj (*NativeFunction) ( const BSONObj &args, void* data );

    class Scope : boost::noncopyable {
    public:
        Scope();
        virtual ~Scope();

        virtual void reset() = 0;
        virtual void init( const BSONObj * data ) = 0;
        void init( const char * data ) {
            BSONObj o( data );
            init( &o );
        }

        virtual void localConnect( const char * dbName ) = 0;
        virtual void externalSetup() = 0;

        class NoDBAccess {
            Scope * _s;
        public:
            NoDBAccess( Scope * s ) {
                _s = s;
            }
            ~NoDBAccess() {
                _s->rename( "____db____" , "db" );
            }
        };
        NoDBAccess disableDBAccess( const char * why ) {
            rename( "db" , "____db____" );
            return NoDBAccess( this );
        }

        virtual double getNumber( const char *field ) = 0;
        virtual int getNumberInt( const char *field ) { return (int)getNumber( field ); }
        virtual long long getNumberLongLong( const char *field ) { return (long long)getNumber( field ); }
        virtual string getString( const char *field ) = 0;
        virtual bool getBoolean( const char *field ) = 0;
        virtual BSONObj getObject( const char *field ) = 0;

        virtual int type( const char *field ) = 0;

        virtual void append( BSONObjBuilder & builder , const char * fieldName , const char * scopeName );

        virtual void setElement( const char *field , const BSONElement& e ) = 0;
        virtual void setNumber( const char *field , double val ) = 0;
        virtual void setString( const char *field , const char * val ) = 0;
        virtual void setObject( const char *field , const BSONObj& obj , bool readOnly=true ) = 0;
        virtual void setBoolean( const char *field , bool val ) = 0;
        virtual void setFunction( const char *field , const char * code ) = 0;
//        virtual void setThis( const BSONObj * obj ) = 0;

        virtual ScriptingFunction createFunction( const char * code );

        virtual void rename( const char * from , const char * to ) = 0;
        /**
         * @return 0 on success
         */
        virtual int invoke( ScriptingFunction func , const BSONObj* args, const BSONObj* recv, int timeoutMs = 0 , bool ignoreReturn = false, bool readOnlyArgs = false, bool readOnlyRecv = false ) = 0;
        void invokeSafe( ScriptingFunction func , const BSONObj* args, const BSONObj* recv, int timeoutMs = 0, bool readOnlyArgs = false, bool readOnlyRecv = false ) {
            int res = invoke( func , args , recv, timeoutMs, readOnlyArgs, readOnlyRecv );
            if ( res == 0 )
                return;
            throw UserException( 9004 , (string)"invoke failed: " + getError() );
        }
        virtual string getError() = 0;

        int invoke( const char* code , const BSONObj* args, const BSONObj* recv, int timeoutMs = 0 );
        void invokeSafe( const char* code , const BSONObj* args, const BSONObj* recv, int timeoutMs = 0 ) {
            if ( invoke( code , args , recv, timeoutMs ) == 0 )
                return;
            throw UserException( 9005 , (string)"invoke failed: " + getError() );
        }

        virtual bool exec( const StringData& code , const string& name , bool printResult , bool reportError , bool assertOnError, int timeoutMs = 0 ) = 0;
        virtual void execSetup( const StringData& code , const string& name = "setup" ) {
            exec( code , name , false , true , true , 0 );
        }

        void execSetup( const JSFile& file) {
            execSetup(file.source, file.name);
        }

        void execCoreFiles();

        virtual bool execFile( const string& filename , bool printResult , bool reportError , bool assertOnError, int timeoutMs = 0 );

        virtual void injectNative( const char *field, NativeFunction func, void* data = 0 ) = 0;

        virtual void gc() = 0;

        void loadStored( bool ignoreNotConnected = false );

        /**
         if any changes are made to .system.js, call this
         right now its just global - slightly inefficient, but a lot simpler
        */
        static void storedFuncMod();

        static int getNumScopes() {
            return _numScopes;
        }

        static void validateObjectIdString( const string &str );

        /** increments the number of times a scope was used */
        void incTimeUsed() { ++_numTimeUsed; }
        /** gets the number of times a scope was used */
        int getTimeUsed() { return _numTimeUsed; }

    protected:

        virtual ScriptingFunction _createFunction( const char * code ) = 0;

        string _localDBName;
        long long _loadedVersion;
        set<string> _storedNames;
        static long long _lastVersion;
        map<string,ScriptingFunction> _cachedFunctions;
        int _numTimeUsed;

        static int _numScopes;
    };

    void installGlobalUtils( Scope& scope );

    class DBClientWithCommands;

    class ScriptEngine : boost::noncopyable {
    public:
        ScriptEngine();
        virtual ~ScriptEngine();

        virtual Scope * newScope() {
            Scope *s = createScope();
            if ( s && _scopeInitCallback )
                _scopeInitCallback( *s );
            installGlobalUtils( *s );
            return s;
        }

        virtual void runTest() = 0;

        virtual bool utf8Ok() const = 0;

        static void setup();

        /** gets a scope from the pool or a new one if pool is empty
         * @param pool An identifier for the pool, usually the db name
         * @return the scope */
        auto_ptr<Scope> getPooledScope( const string& pool );

        /** call this method to release some JS resources when a thread is done */
        void threadDone();

        struct Unlocker { virtual ~Unlocker() {} };
        virtual auto_ptr<Unlocker> newThreadUnlocker() { return auto_ptr< Unlocker >( new Unlocker ); }

        void setScopeInitCallback( void ( *func )( Scope & ) ) { _scopeInitCallback = func; }
        static void setConnectCallback( void ( *func )( DBClientWithCommands& ) ) { _connectCallback = func; }
        static void runConnectCallback( DBClientWithCommands &c ) {
            if ( _connectCallback )
                _connectCallback( c );
        }

        // engine implementation may either respond to interrupt events or
        // poll for interrupts

        // the interrupt functions must not wait indefinitely on a lock
        virtual void interrupt( unsigned opSpec ) {}
        virtual void interruptAll() {}

        static void setGetInterruptSpecCallback( unsigned ( *func )() ) { _getInterruptSpecCallback = func; }
        static bool haveGetInterruptSpecCallback() { return _getInterruptSpecCallback; }
        static unsigned getInterruptSpec() {
            massert( 13474, "no _getInterruptSpecCallback", _getInterruptSpecCallback );
            return _getInterruptSpecCallback();
        }

        static void setCheckInterruptCallback( const char * ( *func )() ) { _checkInterruptCallback = func; }
        static bool haveCheckInterruptCallback() { return _checkInterruptCallback; }
        static const char * checkInterrupt() {
            return _checkInterruptCallback ? _checkInterruptCallback() : "";
        }
        static bool interrupted() {
            const char *r = checkInterrupt();
            return r && r[ 0 ];
        }

    protected:
        virtual Scope * createScope() = 0;

    private:
        void ( *_scopeInitCallback )( Scope & );
        static void ( *_connectCallback )( DBClientWithCommands & );
        static const char * ( *_checkInterruptCallback )();
        static unsigned ( *_getInterruptSpecCallback )();
    };

    bool hasJSReturn( const string& s );

    const char * jsSkipWhiteSpace( const char * raw );

    extern ScriptEngine * globalScriptEngine;
}