static QScriptValue contextInfoToScriptValue(QScriptEngine *eng, const QScriptContextInfo &in) { QScriptValue out = eng->newObject(); out.setProperty(QString::fromLatin1("scriptId"), QScriptValue(eng, qsreal(in.scriptId()))); out.setProperty(QString::fromLatin1("fileName"), QScriptValue(eng, in.fileName())); out.setProperty(QString::fromLatin1("lineNumber"), QScriptValue(eng, in.lineNumber())); out.setProperty(QString::fromLatin1("columnNumber"), QScriptValue(eng, in.columnNumber())); out.setProperty(QString::fromLatin1("functionName"), QScriptValue(eng, in.functionName())); return out; }
QScriptValue include( QScriptContext *context, QScriptEngine *engine ) { // Check argument count, include() call in global context? const QScriptContextInfo contextInfo( context->parentContext() ); if ( context->argumentCount() < 1 ) { context->throwError( i18nc("@info/plain", "One argument expected for <icode>include()</icode>") ); return engine->undefinedValue(); } else if ( context->parentContext() && context->parentContext()->parentContext() ) { QScriptContext *parentContext = context->parentContext()->parentContext(); bool error = false; while ( parentContext ) { const QScriptContextInfo parentContextInfo( parentContext ); if ( !parentContextInfo.fileName().isEmpty() && parentContextInfo.fileName() == contextInfo.fileName() ) { // Parent context is in the same file, error error = true; break; } parentContext = parentContext->parentContext(); } if ( error ) { context->throwError( i18nc("@info/plain", "<icode>include()</icode> calls must be in global context") ); return engine->undefinedValue(); } } // Check if this include() call is before all other statements QVariantHash includeData = context->callee().data().toVariant().toHash(); if ( includeData.contains(contextInfo.fileName()) ) { const quint16 maxIncludeLine = includeData[ contextInfo.fileName() ].toInt(); if ( contextInfo.lineNumber() > maxIncludeLine ) { context->throwError( i18nc("@info/plain", "<icode>include()</icode> calls must be the first statements") ); return engine->undefinedValue(); } } // Get argument and check that it's not pointing to another directory const QString fileName = context->argument(0).toString(); if ( fileName.contains('/') ) { context->throwError( i18nc("@info/plain", "Cannot include files from other directories") ); return engine->undefinedValue(); } // Get path of the main script QString path; QScriptContext *fileInfoContext = context; do { path = QFileInfo( QScriptContextInfo(fileInfoContext).fileName() ).path(); fileInfoContext = fileInfoContext->parentContext(); } while ( path.isEmpty() || path == QLatin1String(".") ); // Construct file path to the file to be included and check if the file is already included const QString filePath = path + '/' + fileName; QStringList includedFiles = engine->globalObject().property( "includedFiles" ).toVariant().toStringList(); if ( includedFiles.contains(filePath) ) { kWarning() << "File already included" << filePath; return engine->undefinedValue(); } // Try to open the file to be included QFile scriptFile( filePath ); if ( !scriptFile.open(QIODevice::ReadOnly) ) { context->throwError( i18nc("@info/plain", "Cannot find file to be included: " "<filename>%1</filename>", filePath) ); return engine->undefinedValue(); } // Read the file QTextStream stream( &scriptFile ); const QString program = stream.readAll(); scriptFile.close(); if ( !includeData.contains(scriptFile.fileName()) ) { includeData[ scriptFile.fileName() ] = maxIncludeLine( program ); QScriptValue includeFunction = engine->globalObject().property("include"); Q_ASSERT( includeFunction.isValid() ); includeFunction.setData( qScriptValueFromValue(engine, includeData) ); engine->globalObject().setProperty( "include", includeFunction, QScriptValue::KeepExistingFlags ); } // Set script context QScriptContext *parent = context->parentContext(); if ( parent ) { context->setActivationObject( parent->activationObject() ); context->setThisObject( parent->thisObject() ); } // Store included files in global property "includedFiles" includedFiles << filePath; includedFiles.removeDuplicates(); QScriptValue::PropertyFlags flags = QScriptValue::ReadOnly | QScriptValue::Undeletable; engine->globalObject().setProperty( "includedFiles", engine->newVariant(includedFiles), flags ); // Evaluate script return engine->evaluate( program, filePath ); }