~kubuntu-members/kross-interpreters/4.11

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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
/***************************************************************************
 * falconscript.cpp
 * This file is part of the KDE project
 * copyright (C)2007-2008 by Giancarlo Niccolai (jonnymind@falconpl.org)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 * 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
 * Library General Public License for more details.
 * You should have received a copy of the GNU Library General Public License
 * along with this program; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 ***************************************************************************/

#include "falconscript.h"
#include "falconinterpreter.h"
#include <kross/core/action.h>

//TODO move that functionality to FalconExtension!
#include <QMetaObject>
#include <QMetaMethod>
#include "falconerrhand.h"

using namespace Kross;

namespace Kross {

    /// \internal
    class FalconScriptPrivate
    {
        public:
            /// The Falcon virtual machine running the script
            Falcon::VMachine *m_vm;
            
            /// The topmost (main) module
            Falcon::Module *m_mainModule;
            
            /// true when initialization has been performed.
            bool m_bInitialized;
            
            FalconScriptPrivate() :
                m_vm(0),
                m_mainModule(0),
                m_bInitialized( false )
            {}
    };

FalconScript::FalconScript(Kross::Interpreter* interpreter, Kross::Action* action)
    : Kross::Script(interpreter, action),
    d(new FalconScriptPrivate())
{
    #ifdef KROSS_FALCON_SCRIPT_CTOR_DEBUG
        krossdebug("FalconScript::Constructor.");
    #endif
    d->m_vm = new Falcon::VMachine;
    d->m_vm->errorHandler( new KErrHandler( this ), true );
    
    #ifdef KROSS_FALCON_SCRIPT_CTOR_DEBUG
        krossdebug("FalconScript::Constructor Linking base modules.");
    #endif
    
    d->m_vm->link( static_cast<FalconInterpreter *>( interpreter )->coreModule() );
    d->m_vm->link( static_cast<FalconInterpreter *>( interpreter )->rtlModule() );
}

FalconScript::~FalconScript()
{
    #ifdef KROSS_FALCON_SCRIPT_DTOR_DEBUG
        krossdebug("FalconScript::Destructor.");
    #endif
    
    if ( d->m_mainModule != 0 )
        d->m_mainModule->decref();
        
    delete d->m_vm;
    delete d;
}


bool FalconScript::initialize()
{
    #ifdef KROSS_FALCON_SCRIPT_INIT_DEBUG
        krossdebug( QString("FalconScript::initialize") );
    #endif

    if(action()->code().isNull()) {
        setError( QString("Invalid scripting code for script '%1'").arg(action()->objectName()) );
        return false;
    }
    if(action()->objectName().isNull()) {
        setError( QString("Name for the script is invalid!") );
        return false;
    }

    Q_ASSERT( ! action()->objectName().isNull() );
    QFileInfo fi( action()->objectName() );
    QString n = QFileInfo(fi.absolutePath(), fi.baseName()).absoluteFilePath();
    
    // Falcon supports UNICODE filenames, and it can import from both UTF8 and UTF16 (and other encodings).
    // utf8 is less efficient, but utf16 may not be complete under some platforms.
    QByteArray filename = n.isNull() ? action()->objectName().toUtf8() : n.toUtf8();
    filename.replace('.','_'); // points are used as module-delimiters
    char* name = filename.data();

    // We need a separate loader, as the interpreter loader will raise errors on the interpreter object
    // an alternate strategy may be that of switching the handler
    Falcon::FlcLoader scriptLoader( "." );
    scriptLoader.addFalconPath();
    // we can exploit the same handler as the VM.
    scriptLoader.errorHandler( d->m_vm->errorHandler() );
    
    // I suppose the input is utf8...
    scriptLoader.sourceEncoding( "utf-8" );
    
    #ifdef KROSS_FALCON_SCRIPT_INIT_DEBUG
        krossdebug( QString("FalconScript::initialize() module='%1':\n").
            arg( QString(action()->code()) ) );
    #endif
        
    // create an input stream containing the code.
    // This String constructor just takes the input data as reference; there is no copy involved.
    // However, the data is copied by StringStream, and that is good: it seems that data() returns
    // a transient data.
    Falcon::StringStream input( Falcon::String(action()->code().data(), action()->code().size()) );

    d->m_mainModule = scriptLoader.loadSource( &input );
    if ( d->m_mainModule == 0 )
    {
        #ifdef KROSS_FALCON_SCRIPT_INIT_DEBUG
            krossdebug( QString("FalconScript::initialize() module='%1' failed to load").
                arg( action()->objectName() ) );
        #endif
        // error already raised
        return false;
    }
    
    // Setting Falcon module informations
    Falcon::String modName, modPath;
    modName.fromUTF8( name );
    modPath.fromUTF8( action()->file().toUtf8().data() );
    d->m_mainModule->name( modName );
    d->m_mainModule->path( modPath );

    #ifdef KROSS_FALCON_SCRIPT_INIT_DEBUG
        krossdebug( QString("FalconScript::initialize() name=%1 loaded; resolving dependencies").arg(action()->objectName()) );
    #endif
    
    // the runtime is used to load user modules; it inherits the loader error handler
    Falcon::Runtime runtime( &scriptLoader );
    if( ! runtime.addModule( d->m_mainModule ) )
    {
        #ifdef KROSS_FALCON_SCRIPT_INIT_DEBUG
            krossdebug( QString("FalconScript::initialize() name=%1 falied resolving dependencies").arg(action()->objectName()) );
        #endif
        d->m_mainModule->decref();
        d->m_mainModule = 0;
        return false;
    }
    
    // We loaded the module and all the explicit dependencies; time to link
    #ifdef KROSS_FALCON_SCRIPT_INIT_DEBUG
        krossdebug( QString("FalconScript::initialize() name=%1 linking").arg(action()->objectName()) );
    #endif
    
    if ( ! d->m_vm->link( &runtime ) )
    {
        #ifdef KROSS_FALCON_SCRIPT_INIT_DEBUG
            krossdebug( QString("FalconScript::initialize() name=%1 linking failed").arg(action()->objectName()) );
        #endif
        return false;
    }
    
    // falcon modules are usually very interested in knowing about themselves...
    Falcon::Item *i_scriptName = d->m_vm->findGlobalItem( "scriptName" );  // is in core module...
    Q_ASSERT( i_scriptName != 0 );
    *i_scriptName = new Falcon::GarbageString( d->m_vm, modName );
    
    Falcon::Item *i_scriptPath = d->m_vm->findGlobalItem( "scriptPath" );  // is in core module...
    Q_ASSERT( i_scriptPath != 0 );
    *i_scriptPath = new Falcon::GarbageString( d->m_vm, modPath );
    
    //we're ready to fire.
    d->m_bInitialized = true;
    return true;
}


void FalconScript::execute()
{
    #ifdef KROSS_FALCON_SCRIPT_EXEC_DEBUG
        krossdebug( QString("FalconScript::execute") );
    #endif

    if( hadError() ) {
        #ifdef KROSS_FALCON_SCRIPT_EXEC_DEBUG
            krosswarning( QString("FalconScript::execute Abort cause of prev error: %1\n%2").arg(errorMessage()).arg(errorTrace()) );
        #endif
        return;
    }

    #ifdef KROSS_FALCON_SCRIPT_EXEC_DEBUG
        krossdebug( QString("FalconScript::execute Initializing") );
    #endif
    if( ! d->m_bInitialized ) { // initialize if not already done before.
        if(! initialize() )
            return;
    }
    
    Q_ASSERT( d->m_mainModule != 0 );

    #ifdef KROSS_FALCON_SCRIPT_EXEC_DEBUG
        krossdebug( QString("FalconScript::execute Launching") );
    #endif
    
    d->m_vm->launch();
}

QStringList FalconScript::functionNames()
{
    if(! d->m_bInitialized ) { // initialize if not already done before.
        if(! initialize())
            return QStringList();
    }
    
    QStringList functions;
    const Falcon::Map &stab = d->m_mainModule->symbolTable().map();
    
    Falcon::MapIterator iter = stab.begin();
    while( iter.hasCurrent() )
    {
        Falcon::Symbol *sym = *(Falcon::Symbol **) iter.currentValue();
        
        // We require the symbols to be functions and exported to be called.
        if( sym->isFunction() && sym->exported() )
        {
            Falcon::AutoCString funcName( sym->name() );
            functions.append( QString().fromUtf8(funcName.c_str()) );
        }
        iter.next();
    }
    
    return functions;
}

QVariant FalconScript::callFunction(const QString& name, const QVariantList& args)
{
    #ifdef KROSS_FALCON_SCRIPT_CALLFUNC_DEBUG
        QString s;
        foreach(QVariant v, args)
            s += v.toString() + ',';
        krossdebug( QString("FalconScript::callFunction() name=%1 args=[%2]").arg(name).arg(s) );
    #endif

    if( hadError() ) {
        #ifdef KROSS_FALCON_SCRIPT_CALLFUNC_DEBUG
            krosswarning( QString("FalconScript::callFunction() Abort cause of prev error: %1\n%2").arg(errorMessage()).arg(errorTrace()) );
        #endif
        return QVariant();
    }

    if(! d->m_bInitialized ) { // initialize if not already done before.
        #ifdef KROSS_FALCON_SCRIPT_CALLFUNC_DEBUG
            krossdebug( QString("FalconScript::callFunction() Initializing") );
        #endif
        if( ! initialize() )
            return QVariant();
    }
    
    // find the function in the main module.
    // Find the callable symbol
    Falcon::String funcName;
    funcName.fromUTF8( name.toUtf8() );
    Falcon::Item *callable = d->m_vm->findGlobalItem( funcName );
    if ( callable != 0 && callable->isCallable() )
    {
        //TODO convert the parameters.
        d->m_vm->callItem( *callable, 0 );
        // Todo: convert the a register into values.
    }

    return QVariant();
}

}