File indexing completed on 2025-01-05 05:23:45
0001 /* 0002 This file is part of the Okteta Kasten Framework, made within the KDE community. 0003 0004 SPDX-FileCopyrightText: 2010, 2011, 2012, 2013 Alex Richardson <alex.richardson@gmx.de> 0005 0006 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0007 */ 0008 0009 #include "scriptengineinitializer.hpp" 0010 0011 #include "../datatypes/primitivefactory.hpp" 0012 #include "../parsers/parserutils.hpp" 0013 #include <QStringList> 0014 #include <QFile> 0015 #include <QScriptValue> 0016 #include <QScriptEngine> 0017 #include <QScriptContext> 0018 #include <QScriptValueIterator> 0019 #include <QStandardPaths> 0020 0021 namespace ScriptEngineInitializer { 0022 0023 void addFuctionsToScriptEngine(QScriptEngine* engine) 0024 { 0025 engine->globalObject().setProperty(QStringLiteral("uint8"), 0026 engine->newFunction(Private::scriptNewUInt8)); 0027 engine->globalObject().setProperty(QStringLiteral("uint16"), 0028 engine->newFunction(Private::scriptNewUInt16)); 0029 engine->globalObject().setProperty(QStringLiteral("uint32"), 0030 engine->newFunction(Private::scriptNewUInt32)); 0031 engine->globalObject().setProperty(QStringLiteral("uint64"), 0032 engine->newFunction(Private::scriptNewUInt64)); 0033 0034 engine->globalObject().setProperty(QStringLiteral("int8"), 0035 engine->newFunction(Private::scriptNewInt8)); 0036 engine->globalObject().setProperty(QStringLiteral("int16"), 0037 engine->newFunction(Private::scriptNewInt16)); 0038 engine->globalObject().setProperty(QStringLiteral("int32"), 0039 engine->newFunction(Private::scriptNewInt32)); 0040 engine->globalObject().setProperty(QStringLiteral("int64"), 0041 engine->newFunction(Private::scriptNewInt64)); 0042 0043 engine->globalObject().setProperty(QStringLiteral("bool8"), 0044 engine->newFunction(Private::scriptNewBool8)); 0045 engine->globalObject().setProperty(QStringLiteral("bool16"), 0046 engine->newFunction(Private::scriptNewBool16)); 0047 engine->globalObject().setProperty(QStringLiteral("bool32"), 0048 engine->newFunction(Private::scriptNewBool32)); 0049 engine->globalObject().setProperty(QStringLiteral("bool64"), 0050 engine->newFunction(Private::scriptNewBool64)); 0051 0052 engine->globalObject().setProperty(QStringLiteral("float"), 0053 engine->newFunction(Private::scriptNewFloat)); 0054 engine->globalObject().setProperty(QStringLiteral("double"), 0055 engine->newFunction(Private::scriptNewDouble)); 0056 0057 engine->globalObject().setProperty(QStringLiteral("char"), 0058 engine->newFunction(Private::scriptNewChar)); 0059 0060 engine->globalObject().setProperty(QStringLiteral("bitfield"), 0061 engine->newFunction(Private::scriptNewBitfield)); 0062 0063 engine->globalObject().setProperty(QStringLiteral("array"), 0064 engine->newFunction(Private::scriptNewArray)); 0065 engine->globalObject().setProperty(QStringLiteral("struct"), 0066 engine->newFunction(Private::scriptNewStruct)); 0067 engine->globalObject().setProperty(QStringLiteral("union"), 0068 engine->newFunction(Private::scriptNewUnion)); 0069 0070 // enum is a reserved keyword in JavaScript, cannot use it 0071 engine->globalObject().setProperty(QStringLiteral("enumeration"), 0072 engine->newFunction(Private::scriptNewEnum)); 0073 engine->globalObject().setProperty(QStringLiteral("flags"), 0074 engine->newFunction(Private::scriptNewFlags)); 0075 engine->globalObject().setProperty(QStringLiteral("string"), 0076 engine->newFunction(Private::scriptNewString)); 0077 engine->globalObject().setProperty(QStringLiteral("pointer"), 0078 engine->newFunction(Private::scriptNewPointer)); 0079 engine->globalObject().setProperty(QStringLiteral("taggedUnion"), 0080 engine->newFunction(Private::scriptNewTaggedUnion)); 0081 0082 engine->globalObject().setProperty(QStringLiteral("alternative"), 0083 engine->newFunction(Private::alternativeFunc)); 0084 0085 engine->globalObject().setProperty(QStringLiteral("importScript"), 0086 engine->newFunction(Private::importScriptFunc)); 0087 } 0088 0089 QScriptEngine* newEngine() 0090 { 0091 auto* ret = new QScriptEngine(); 0092 addFuctionsToScriptEngine(ret); 0093 return ret; 0094 } 0095 0096 namespace Private { 0097 0098 QString setUpdatePropertyName() { return QStringLiteral("setUpdate"); } 0099 QString setValidationPropertyName() { return QStringLiteral("setValidation"); } 0100 QString setPropertyName() { return QStringLiteral("set"); } 0101 0102 namespace { 0103 0104 QScriptValue scriptNewCommon(QScriptContext* ctx, QScriptEngine* eng, const QString& typeName) 0105 { 0106 QScriptValue object = ctx->isCalledAsConstructor() ? ctx->thisObject() : eng->newObject(); 0107 object.setProperty(ParserStrings::PROPERTY_INTERNAL_TYPE(), typeName); 0108 // add the setUpdate() and setValidation() functions 0109 object.setProperty(setUpdatePropertyName(), eng->newFunction(addUpdateFunc, 1)); 0110 object.setProperty(setValidationPropertyName(), eng->newFunction(addValidationFunc, 1)); 0111 object.setProperty(setPropertyName(), eng->newFunction(addCustomPropertiesFunc, 1)); 0112 return object; 0113 } 0114 0115 /** create a new primitive of type @p type */ 0116 QScriptValue primitiveConstructor(QScriptContext* ctx, QScriptEngine* eng, const QString& type) 0117 { 0118 QScriptValue object = scriptNewCommon(ctx, eng, ParserStrings::TYPE_PRIMITIVE()); 0119 object.setProperty(ParserStrings::PROPERTY_TYPE(), type); 0120 return object; 0121 } 0122 0123 } 0124 #define PRIMITIVE_CONSTRUCTOR(type) QScriptValue scriptNew##type(QScriptContext* ctx, QScriptEngine* eng) \ 0125 { return primitiveConstructor(ctx, eng, QStringLiteral(#type)); } 0126 0127 PRIMITIVE_CONSTRUCTOR(UInt8) 0128 PRIMITIVE_CONSTRUCTOR(UInt16) 0129 PRIMITIVE_CONSTRUCTOR(UInt32) 0130 PRIMITIVE_CONSTRUCTOR(UInt64) 0131 PRIMITIVE_CONSTRUCTOR(Int8) 0132 PRIMITIVE_CONSTRUCTOR(Int16) 0133 PRIMITIVE_CONSTRUCTOR(Int32) 0134 PRIMITIVE_CONSTRUCTOR(Int64) 0135 PRIMITIVE_CONSTRUCTOR(Bool8) 0136 PRIMITIVE_CONSTRUCTOR(Bool16) 0137 PRIMITIVE_CONSTRUCTOR(Bool32) 0138 PRIMITIVE_CONSTRUCTOR(Bool64) 0139 PRIMITIVE_CONSTRUCTOR(Float) 0140 PRIMITIVE_CONSTRUCTOR(Double) 0141 PRIMITIVE_CONSTRUCTOR(Char) 0142 0143 #undef PRIMITIVE_CONSTRUCTOR 0144 0145 QScriptValue scriptNewBitfield(QScriptContext* ctx, QScriptEngine* eng) 0146 { 0147 QScriptValue object = scriptNewCommon(ctx, eng, ParserStrings::TYPE_BITFIELD()); 0148 0149 object.setProperty(ParserStrings::PROPERTY_TYPE(), ctx->argument(0)); // first argument is type 0150 object.setProperty(ParserStrings::PROPERTY_WIDTH(), ctx->argument(1)); // second argument is width 0151 return object; 0152 } 0153 0154 // with children: 0155 QScriptValue scriptNewStruct(QScriptContext* ctx, QScriptEngine* eng) 0156 { 0157 QScriptValue object = scriptNewCommon(ctx, eng, ParserStrings::TYPE_STRUCT()); 0158 object.setProperty(ParserStrings::PROPERTY_CHILD(), eng->newFunction(getChild)); 0159 0160 object.setProperty(ParserStrings::PROPERTY_CHILDREN(), ctx->argument(0)); // first argument is children 0161 return object; 0162 } 0163 0164 QScriptValue scriptNewUnion(QScriptContext* ctx, QScriptEngine* eng) 0165 { 0166 QScriptValue object = scriptNewCommon(ctx, eng, ParserStrings::TYPE_UNION()); 0167 object.setProperty(ParserStrings::PROPERTY_TYPE(), eng->newFunction(getChild)); 0168 0169 object.setProperty(ParserStrings::PROPERTY_CHILDREN(), ctx->argument(0)); // first argument is children 0170 return object; 0171 } 0172 0173 QScriptValue scriptNewArray(QScriptContext* ctx, QScriptEngine* eng) 0174 { 0175 QScriptValue object = scriptNewCommon(ctx, eng, ParserStrings::TYPE_ARRAY()); 0176 0177 object.setProperty(ParserStrings::PROPERTY_TYPE(), ctx->argument(0)); // first argument is child type 0178 object.setProperty(ParserStrings::PROPERTY_LENGTH(), ctx->argument(1)); // second argument is length 0179 return object; 0180 } 0181 0182 QScriptValue createEnumObject(QScriptContext* ctx, QScriptEngine* eng, const QString& typeName) 0183 { 0184 QScriptValue object = scriptNewCommon(ctx, eng, typeName); 0185 0186 object.setProperty(ParserStrings::PROPERTY_ENUM_NAME(), ctx->argument(0)); // first argument is the name of the underlying enum 0187 object.setProperty(ParserStrings::PROPERTY_TYPE(), ctx->argument(1)); // second argument is the type of the enum 0188 object.setProperty(ParserStrings::PROPERTY_ENUM_VALUES(), ctx->argument(2)); // third argument is the enum values 0189 return object; 0190 } 0191 0192 QScriptValue scriptNewEnum(QScriptContext* ctx, QScriptEngine* eng) 0193 { 0194 return createEnumObject(ctx, eng, ParserStrings::TYPE_ENUM()); 0195 } 0196 0197 QScriptValue scriptNewFlags(QScriptContext* ctx, QScriptEngine* eng) 0198 { 0199 return createEnumObject(ctx, eng, ParserStrings::TYPE_FLAGS()); 0200 } 0201 0202 QScriptValue scriptNewString(QScriptContext* ctx, QScriptEngine* eng) 0203 { 0204 QScriptValue object = scriptNewCommon(ctx, eng, ParserStrings::TYPE_STRING()); 0205 0206 object.setProperty(ParserStrings::PROPERTY_ENCODING(), ctx->argument(0)); 0207 return object; 0208 } 0209 0210 QScriptValue scriptNewPointer(QScriptContext* ctx, QScriptEngine* eng) 0211 { 0212 QScriptValue object = scriptNewCommon(ctx, eng, ParserStrings::TYPE_POINTER()); 0213 0214 object.setProperty(ParserStrings::PROPERTY_TYPE(), ctx->argument(0)); 0215 object.setProperty(ParserStrings::PROPERTY_TARGET(), ctx->argument(1)); 0216 0217 if (ctx->argumentCount() >= 3 && ctx->argument(2).isValid()) { 0218 if (ctx->argument(2).isNumber()) { 0219 object.setProperty(ParserStrings::PROPERTY_SCALE(), ctx->argument(2)); 0220 } else { 0221 return ctx->throwError(QStringLiteral("pointer(): if provided, scale must be integer!")); 0222 } 0223 } else { 0224 object.setProperty(ParserStrings::PROPERTY_SCALE(), eng->toScriptValue(1)); 0225 } 0226 0227 if (ctx->argumentCount() >= 4 && ctx->argument(3).isValid()) { 0228 if (ctx->argument(3).isFunction()) { 0229 object.setProperty(ParserStrings::PROPERTY_INTERPRET_FUNC(), ctx->argument(3)); 0230 } else { 0231 return ctx->throwError(QStringLiteral("pointer(): if provided, interpreterFunc must be a function!")); 0232 } 0233 } 0234 0235 return object; 0236 } 0237 0238 QScriptValue scriptNewTaggedUnion(QScriptContext* ctx, QScriptEngine* eng) 0239 { 0240 QScriptValue object = scriptNewCommon(ctx, eng, ParserStrings::TYPE_TAGGED_UNION()); 0241 object.setProperty(ParserStrings::PROPERTY_CHILD(), eng->newFunction(getChild)); 0242 0243 object.setProperty(ParserStrings::PROPERTY_CHILDREN(), ctx->argument(0)); 0244 object.setProperty(ParserStrings::PROPERTY_ALTERNATIVES(), ctx->argument(1)); 0245 object.setProperty(ParserStrings::PROPERTY_DEFAULT_CHILDREN(), ctx->argument(2)); 0246 return object; 0247 } 0248 0249 QScriptValue getChild(QScriptContext* ctx, QScriptEngine* eng) 0250 { 0251 Q_UNUSED(eng) 0252 if (ctx->argumentCount() < 1) { 0253 return ctx->throwError(QStringLiteral("child(): name of child must be passed as first parameter")); 0254 } 0255 QString nameString = ctx->argument(0).toString(); 0256 QScriptValue ret = ctx->thisObject().property(ParserStrings::PROPERTY_CHILDREN()).property(nameString); 0257 if (ret.isValid()) { 0258 return ret; 0259 } 0260 return ctx->throwError( 0261 QString(QLatin1String("child(): could not find child with name=") + nameString)); 0262 } 0263 0264 QScriptValue addUpdateFunc(QScriptContext* ctx, QScriptEngine*) 0265 { 0266 if (ctx->argumentCount() != 1) { 0267 return ctx->throwError(QStringLiteral("setUpdate(): needs one argument!")); 0268 } 0269 QScriptValue thisObj = ctx->thisObject(); 0270 Q_ASSERT(thisObj.isValid()); 0271 QScriptValue func = ctx->argument(0); 0272 if (!func.isFunction()) { 0273 return ctx->throwError(QScriptContext::TypeError, 0274 QStringLiteral("setUpdate(): argument must be a function!")); 0275 } 0276 thisObj.setProperty(ParserStrings::PROPERTY_UPDATE_FUNC(), func); 0277 return thisObj; 0278 } 0279 0280 QScriptValue addValidationFunc(QScriptContext* ctx, QScriptEngine*) 0281 { 0282 if (ctx->argumentCount() != 1) { 0283 return ctx->throwError(QStringLiteral("setValidation(): needs one argument!")); 0284 } 0285 QScriptValue thisObj = ctx->thisObject(); 0286 Q_ASSERT(thisObj.isValid()); 0287 QScriptValue func = ctx->argument(0); 0288 if (!func.isFunction()) { 0289 return ctx->throwError(QScriptContext::TypeError, 0290 QStringLiteral("setValidation(): argument must be a function!")); 0291 } 0292 thisObj.setProperty(ParserStrings::PROPERTY_VALIDATION_FUNC(), func); 0293 return thisObj; 0294 } 0295 0296 QScriptValue addCustomPropertiesFunc(QScriptContext* ctx, QScriptEngine*) 0297 { 0298 if (ctx->argumentCount() != 1) { 0299 return ctx->throwError(QStringLiteral("set(): needs one argument!")); 0300 } 0301 QScriptValue thisObj = ctx->thisObject(); 0302 Q_ASSERT(thisObj.isValid()); 0303 QScriptValue arg = ctx->argument(0); 0304 if (!arg.isValid() || !arg.isObject()) { 0305 return ctx->throwError(QScriptContext::TypeError, 0306 QStringLiteral("set(): argument must be an object!")); 0307 } 0308 int count = 0; 0309 QScriptValueIterator it(arg); 0310 while (it.hasNext()) { 0311 it.next(); 0312 thisObj.setProperty(it.scriptName(), it.value()); 0313 count++; 0314 } 0315 if (count == 0) { 0316 return ctx->throwError(QStringLiteral("set(): must set at least one property!")); 0317 } 0318 return thisObj; 0319 } 0320 0321 QScriptValue alternativeFunc(QScriptContext* ctx, QScriptEngine* eng) 0322 { 0323 if (ctx->argumentCount() < 2) { 0324 return ctx->throwError(QStringLiteral("alternative(): needs at least 2 arguments!")); 0325 } 0326 QScriptValue object = ctx->isCalledAsConstructor() ? ctx->thisObject() : eng->newObject(); 0327 object.setProperty(ParserStrings::PROPERTY_SELECT_IF(), ctx->argument(0)); 0328 object.setProperty(ParserStrings::PROPERTY_CHILDREN(), ctx->argument(1)); 0329 if (ctx->argumentCount() > 2) { 0330 object.setProperty(ParserStrings::PROPERTY_STRUCT_NAME(), ctx->argument(2)); 0331 } 0332 return object; 0333 } 0334 0335 QScriptValue importScriptFunc(QScriptContext* ctx, QScriptEngine* eng) 0336 { 0337 if (ctx->argumentCount() != 1) { 0338 return ctx->throwError(QStringLiteral("importScript(): expected one argument!")); 0339 } 0340 QString arg = ctx->argument(0).toString(); 0341 if (arg.contains(QLatin1String(".."))) { 0342 return ctx->throwError(QStringLiteral("importScript(): You may only access installed structure files! Path traversal detected.")); 0343 } 0344 const QString fileName = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("okteta/structures/") + arg); 0345 if (fileName.isEmpty()) { 0346 return ctx->throwError(QStringLiteral("importScript(): could not find file to import!")); 0347 } 0348 QFile file(fileName); 0349 if (!file.open(QFile::ReadOnly)) { 0350 return ctx->throwError(QStringLiteral("importScript(): failed to open file!")); 0351 } 0352 QTextStream s(&file); 0353 QString code = s.readAll(); 0354 file.close(); 0355 // now push context so that we don't conflict with the current execution 0356 QScriptContext* newCtx = eng->pushContext(); 0357 QScriptValue result = eng->evaluate(code); 0358 if (result.isError()) { 0359 result = QScriptValue(QLatin1String("importScript(): failed due to exception: ") + result.toString()); 0360 } else { 0361 result = newCtx->activationObject(); 0362 } 0363 eng->popContext(); 0364 return result; 0365 } 0366 0367 } // namespace Private 0368 0369 } // namespace ScriptEngine Initializer