1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qv4function_p.h"
5
6#include <private/qqmlpropertycachecreator_p.h>
7#include <private/qqmltype_p_p.h>
8
9#include <private/qv4engine_p.h>
10#include <private/qv4functiontable_p.h>
11#include <private/qv4identifiertable_p.h>
12#include <private/qv4jscall_p.h>
13#include <private/qv4vme_moth_p.h>
14
15#include <assembler/MacroAssemblerCodeRef.h>
16
17QT_BEGIN_NAMESPACE
18
19namespace QV4 {
20
21bool Function::call(QObject *thisObject, void **a, const QMetaType *types, int argc,
22 ExecutionContext *context)
23{
24 if (kind != AotCompiled) {
25 return QV4::convertAndCall(
26 engine: context->engine(), thisObject, a, types, argc,
27 call: [this, context](const Value *thisObject, const Value *argv, int argc) {
28 return call(thisObject, argv, argc, context);
29 });
30 }
31
32 ExecutionEngine *engine = context->engine();
33 MetaTypesStackFrame frame;
34 frame.init(v4Function: this, thisObject, context, returnAndArgs: a, metaTypes: types, argc);
35 frame.push(engine);
36 Moth::VME::exec(frame: &frame, engine);
37 frame.pop(engine);
38 return !frame.isReturnValueUndefined();
39}
40
41static ReturnedValue doCall(
42 QV4::Function *self, const QV4::Value *thisObject, const QV4::Value *argv, int argc,
43 QV4::ExecutionContext *context)
44{
45 ExecutionEngine *engine = context->engine();
46 JSTypesStackFrame frame;
47 frame.init(v4Function: self, argv, argc);
48 frame.setupJSFrame(stackSpace: engine->jsStackTop, function: Value::undefinedValue(), scope: context->d(),
49 thisObject: thisObject ? *thisObject : Value::undefinedValue());
50 engine->jsStackTop += frame.requiredJSStackFrameSize();
51 frame.push(engine);
52 ReturnedValue result = Moth::VME::exec(frame: &frame, engine);
53 frame.pop(engine);
54 return result;
55}
56
57ReturnedValue Function::call(
58 const Value *thisObject, const Value *argv, int argc, ExecutionContext *context) {
59 switch (kind) {
60 case AotCompiled:
61 return QV4::convertAndCall(
62 engine: context->engine(), aotFunction: &aotCompiledFunction, thisObject, argv, argc,
63 call: [this, context](
64 QObject *thisObject, void **a, const QMetaType *types, int argc) {
65 call(thisObject, a, types, argc, context);
66 });
67 case JsTyped:
68 return QV4::coerceAndCall(
69 engine: context->engine(), typedFunction: &jsTypedFunction, compiledFunction, argv, argc,
70 call: [this, context, thisObject](const Value *argv, int argc) {
71 return doCall(self: this, thisObject, argv, argc, context);
72 });
73 default:
74 break;
75 }
76
77 return doCall(self: this, thisObject, argv, argc, context);
78}
79
80Function *Function::create(ExecutionEngine *engine, ExecutableCompilationUnit *unit,
81 const CompiledData::Function *function,
82 const QQmlPrivate::AOTCompiledFunction *aotFunction)
83{
84 return new Function(engine, unit, function, aotFunction);
85}
86
87void Function::destroy()
88{
89 delete this;
90}
91
92void Function::mark(MarkStack *ms)
93{
94 if (internalClass)
95 internalClass->mark(markStack: ms);
96}
97
98static bool isSpecificType(const CompiledData::ParameterType &type)
99{
100 return type.typeNameIndexOrCommonType()
101 != (type.indexIsCommonType() ? quint32(CompiledData::CommonType::Invalid) : 0);
102}
103
104Function::Function(ExecutionEngine *engine, ExecutableCompilationUnit *unit,
105 const CompiledData::Function *function,
106 const QQmlPrivate::AOTCompiledFunction *aotFunction)
107 : FunctionData(engine, unit)
108 , compiledFunction(function)
109 , codeData(function->code())
110{
111 Scope scope(engine);
112 Scoped<InternalClass> ic(scope, engine->internalClasses(icType: EngineBase::Class_CallContext));
113
114 // first locals
115 const quint32_le *localsIndices = compiledFunction->localsTable();
116 for (quint32 i = 0; i < compiledFunction->nLocals; ++i)
117 ic = ic->addMember(identifier: engine->identifierTable->asPropertyKey(str: compilationUnit->runtimeStrings[localsIndices[i]]), data: Attr_NotConfigurable);
118
119 const CompiledData::Parameter *formalsIndices = compiledFunction->formalsTable();
120 bool enforceJsTypes = !unit->ignoresFunctionSignature();
121
122 for (quint32 i = 0; i < compiledFunction->nFormals; ++i) {
123 ic = ic->addMember(identifier: engine->identifierTable->asPropertyKey(str: compilationUnit->runtimeStrings[formalsIndices[i].nameIndex]), data: Attr_NotConfigurable);
124 if (enforceJsTypes && !isSpecificType(type: formalsIndices[i].type))
125 enforceJsTypes = false;
126 }
127 internalClass.set(e: engine, newVal: ic->d());
128
129 nFormals = compiledFunction->nFormals;
130
131 if (!enforceJsTypes)
132 return;
133
134 if (aotFunction) {
135 aotCompiledCode = aotFunction->functionPtr;
136 new (&aotCompiledFunction) AOTCompiledFunction;
137 kind = AotCompiled;
138 aotCompiledFunction.types.resize(sz: aotFunction->numArguments + 1);
139 aotFunction->signature(unit, aotCompiledFunction.types.data());
140 return;
141 }
142
143 // If a function has any typed arguments, but an untyped return value, the return value is void.
144 // If it doesn't have any arguments at all and the return value is untyped, the function is
145 // untyped. Users can specifically set the return type to "void" to have it enforced.
146 if (nFormals == 0 && !isSpecificType(type: compiledFunction->returnType))
147 return;
148
149 QQmlTypeLoader *typeLoader = engine->typeLoader();
150
151 const auto isEnumUsedAsType = [&](const QV4::ExecutableCompilationUnit *unit,
152 int elementNameId, QQmlTypeLoader *typeLoader,
153 const quint16 *parameter = nullptr) {
154 const QStringView name = unit->baseCompilationUnit()->stringAt(index: elementNameId);
155 const auto split = name.tokenize(needle: u'.').toContainer<QVarLengthArray<QStringView, 4>>();
156 if (split.size() != 2)
157 return false;
158
159 const QStringView scopeName = split[0];
160 const QStringView enumName = split[1];
161
162 auto *pengine = QQmlEnginePrivate::get(e: engine);
163 const auto warn = [&] {
164 QQmlError error;
165 auto where = parameter ? QStringLiteral("parameter ") + QString::number(*parameter)
166 : QStringLiteral("return type");
167 auto msg = QStringLiteral("Type annotation for %1 of function %2: Enumerations are "
168 "not types. Use underlying type (int or double) instead.")
169 .arg(args&: where, args: this->name()->toQString());
170 error.setDescription(msg);
171 error.setUrl(QUrl(sourceFile()));
172 error.setLine(this->sourceLocation().line);
173 error.setColumn(this->sourceLocation().column);
174 error.setMessageType(QtWarningMsg);
175 pengine->warning(error);
176 };
177
178 bool ok;
179 if (scopeName == QStringLiteral("Qt")) {
180 const QMetaObject *mo = &Qt::staticMetaObject;
181 for (int i = 0; i < mo->enumeratorCount(); ++i) {
182 if (mo->enumerator(index: i).name() == enumName.toLatin1()) {
183 warn();
184 return true;
185 }
186 }
187 return false;
188 }
189
190 const QQmlType scope = unit->typeNameCache()->query<QQmlImport::AllowRecursion>(
191 key: scopeName, typeLoader).type;
192 if (!scope.isValid())
193 return false;
194
195 scope.scopedEnumIndex(typeLoader, enumName.toString(), ok: &ok);
196 if (ok) {
197 warn();
198 return true;
199 }
200 scope.unscopedEnumIndex(typeLoader, enumName.toString(), ok: &ok);
201 if (ok) {
202 warn();
203 return true;
204 }
205
206 return false;
207 };
208
209 auto findQmlType = [&](const CompiledData::ParameterType &param,
210 const quint16 *parameter = nullptr) {
211 const quint32 type = param.typeNameIndexOrCommonType();
212 if (param.indexIsCommonType()) {
213 return QQmlMetaType::qmlType(metaType: QQmlPropertyCacheCreatorBase::metaTypeForPropertyType(
214 type: QV4::CompiledData::CommonType(type)));
215 }
216
217 if (type == 0 || !typeLoader)
218 return QQmlType();
219
220 if (isEnumUsedAsType(unit, type, typeLoader, parameter))
221 return QQmlType();
222
223 const QQmlType qmltype = QQmlTypePrivate::visibleQmlTypeByName(unit, elementNameId: type, typeLoader);
224 return qmltype.typeId().isValid() ? qmltype : QQmlType();
225 };
226
227 new (&jsTypedFunction) JSTypedFunction;
228 kind = JsTyped;
229 jsTypedFunction.types.reserve(sz: nFormals + 1);
230 jsTypedFunction.types.append(t: findQmlType(compiledFunction->returnType));
231 for (quint16 i = 0; i < nFormals; ++i)
232 jsTypedFunction.types.append(t: findQmlType(formalsIndices[i].type, &i));
233}
234
235Function::~Function()
236{
237 if (codeRef) {
238 destroyFunctionTable(function: this, codeRef);
239 delete codeRef;
240 }
241
242 switch (kind) {
243 case JsTyped:
244 jsTypedFunction.~JSTypedFunction();
245 break;
246 case AotCompiled:
247 aotCompiledFunction.~AOTCompiledFunction();
248 break;
249 case JsUntyped:
250 case Eval:
251 break;
252 }
253}
254
255void Function::updateInternalClass(ExecutionEngine *engine, const QList<QByteArray> &parameters)
256{
257 QStringList parameterNames;
258
259 // Resolve duplicate parameter names:
260 for (int i = 0, ei = parameters.size(); i != ei; ++i) {
261 const QByteArray &param = parameters.at(i);
262 int duplicate = -1;
263
264 for (int j = i - 1; j >= 0; --j) {
265 const QByteArray &prevParam = parameters.at(i: j);
266 if (param == prevParam) {
267 duplicate = j;
268 break;
269 }
270 }
271
272 if (duplicate == -1) {
273 parameterNames.append(t: QString::fromUtf8(ba: param));
274 } else {
275 const QString dup = parameterNames[duplicate];
276 parameterNames.append(t: dup);
277 parameterNames[duplicate] =
278 QString(QChar(0xfffe)) + QString::number(duplicate) + dup;
279 }
280
281 }
282
283 Scope scope(engine);
284 Scoped<InternalClass> ic(scope, engine->internalClasses(icType: EngineBase::Class_CallContext));
285
286 // first locals
287 const quint32_le *localsIndices = compiledFunction->localsTable();
288 for (quint32 i = 0; i < compiledFunction->nLocals; ++i) {
289 ic = ic->addMember(
290 identifier: engine->identifierTable->asPropertyKey(str: compilationUnit->runtimeStrings[localsIndices[i]]),
291 data: Attr_NotConfigurable);
292 }
293
294 ScopedString arg(scope);
295 for (const QString &parameterName : parameterNames) {
296 arg = engine->newIdentifier(text: parameterName);
297 ic = ic->addMember(identifier: arg->propertyKey(), data: Attr_NotConfigurable);
298 }
299 internalClass.set(e: engine, newVal: ic->d());
300 nFormals = parameters.size();
301}
302
303QString Function::prettyName(const Function *function, const void *code)
304{
305 QString prettyName = function ? function->name()->toQString() : QString();
306 if (prettyName.isEmpty()) {
307 prettyName = QString::number(reinterpret_cast<quintptr>(code), base: 16);
308 prettyName.prepend(s: QLatin1String("QV4::Function(0x"));
309 prettyName.append(c: QLatin1Char(')'));
310 }
311 return prettyName;
312}
313
314QQmlSourceLocation Function::sourceLocation() const
315{
316 return QQmlSourceLocation(
317 sourceFile(), compiledFunction->location.line(), compiledFunction->location.column());
318}
319
320FunctionData::FunctionData(EngineBase *engine, ExecutableCompilationUnit *compilationUnit_)
321{
322 compilationUnit.set(engine, heapObject: compilationUnit_);
323}
324
325} // namespace QV4
326
327QT_END_NAMESPACE
328

source code of qtdeclarative/src/qml/jsruntime/qv4function.cpp