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 "qml/qqmlprivate.h" |
5 | #include "qv4function_p.h" |
6 | #include "qv4managed_p.h" |
7 | #include "qv4string_p.h" |
8 | #include "qv4value_p.h" |
9 | #include "qv4engine_p.h" |
10 | #include <private/qv4mm_p.h> |
11 | #include <private/qv4identifiertable_p.h> |
12 | #include <private/qv4functiontable_p.h> |
13 | #include <assembler/MacroAssemblerCodeRef.h> |
14 | #include <private/qv4vme_moth_p.h> |
15 | #include <private/qqmlglobal_p.h> |
16 | #include <private/qv4jscall_p.h> |
17 | #include <private/qqmlpropertycachecreator_p.h> |
18 | |
19 | QT_BEGIN_NAMESPACE |
20 | |
21 | namespace QV4 { |
22 | |
23 | bool Function::call(QObject *thisObject, void **a, const QMetaType *types, int argc, |
24 | ExecutionContext *context) |
25 | { |
26 | if (kind != AotCompiled) { |
27 | return QV4::convertAndCall( |
28 | engine: context->engine(), thisObject, a, types, argc, |
29 | call: [this, context](const Value *thisObject, const Value *argv, int argc) { |
30 | return call(thisObject, argv, argc, context); |
31 | }); |
32 | } |
33 | |
34 | ExecutionEngine *engine = context->engine(); |
35 | MetaTypesStackFrame frame; |
36 | frame.init(v4Function: this, thisObject, context, returnAndArgs: a, metaTypes: types, argc); |
37 | frame.push(engine); |
38 | Moth::VME::exec(frame: &frame, engine); |
39 | frame.pop(engine); |
40 | return !frame.isReturnValueUndefined(); |
41 | } |
42 | |
43 | static ReturnedValue doCall( |
44 | QV4::Function *self, const QV4::Value *thisObject, const QV4::Value *argv, int argc, |
45 | QV4::ExecutionContext *context) |
46 | { |
47 | ExecutionEngine *engine = context->engine(); |
48 | JSTypesStackFrame frame; |
49 | frame.init(v4Function: self, argv, argc); |
50 | frame.setupJSFrame(stackSpace: engine->jsStackTop, function: Value::undefinedValue(), scope: context->d(), |
51 | thisObject: thisObject ? *thisObject : Value::undefinedValue()); |
52 | engine->jsStackTop += frame.requiredJSStackFrameSize(); |
53 | frame.push(engine); |
54 | ReturnedValue result = Moth::VME::exec(frame: &frame, engine); |
55 | frame.pop(engine); |
56 | return result; |
57 | } |
58 | |
59 | ReturnedValue Function::call( |
60 | const Value *thisObject, const Value *argv, int argc, ExecutionContext *context) { |
61 | switch (kind) { |
62 | case AotCompiled: |
63 | return QV4::convertAndCall( |
64 | engine: context->engine(), aotFunction: aotCompiledFunction, thisObject, argv, argc, |
65 | call: [this, context]( |
66 | QObject *thisObject, void **a, const QMetaType *types, int argc) { |
67 | call(thisObject, a, types, argc, context); |
68 | }); |
69 | case JsTyped: |
70 | return QV4::coerceAndCall( |
71 | engine: context->engine(), typedFunction: aotCompiledFunction, thisObject, argv, argc, |
72 | call: [this, context](const Value *thisObject, const Value *argv, int argc) { |
73 | return doCall(self: this, thisObject, argv, argc, context); |
74 | }); |
75 | default: |
76 | break; |
77 | } |
78 | |
79 | return doCall(self: this, thisObject, argv, argc, context); |
80 | } |
81 | |
82 | Function *Function::create(ExecutionEngine *engine, ExecutableCompilationUnit *unit, |
83 | const CompiledData::Function *function, |
84 | const QQmlPrivate::AOTCompiledFunction *aotFunction) |
85 | { |
86 | return new Function(engine, unit, function, aotFunction); |
87 | } |
88 | |
89 | void Function::destroy() |
90 | { |
91 | delete this; |
92 | } |
93 | |
94 | Function::Function(ExecutionEngine *engine, ExecutableCompilationUnit *unit, |
95 | const CompiledData::Function *function, |
96 | const QQmlPrivate::AOTCompiledFunction *aotFunction) |
97 | : FunctionData(unit) |
98 | , compiledFunction(function) |
99 | , codeData(function->code()) |
100 | , jittedCode(nullptr) |
101 | , codeRef(nullptr) |
102 | , aotCompiledFunction(aotFunction) |
103 | , kind(aotFunction ? AotCompiled : JsUntyped) |
104 | { |
105 | Scope scope(engine); |
106 | Scoped<InternalClass> ic(scope, engine->internalClasses(icType: EngineBase::Class_CallContext)); |
107 | |
108 | // first locals |
109 | const quint32_le *localsIndices = compiledFunction->localsTable(); |
110 | for (quint32 i = 0; i < compiledFunction->nLocals; ++i) |
111 | ic = ic->addMember(identifier: engine->identifierTable->asPropertyKey(str: compilationUnit->runtimeStrings[localsIndices[i]]), data: Attr_NotConfigurable); |
112 | |
113 | const CompiledData::Parameter *formalsIndices = compiledFunction->formalsTable(); |
114 | const bool enforcesSignature = !aotFunction && unit->enforcesFunctionSignature(); |
115 | bool hasTypes = false; |
116 | for (quint32 i = 0; i < compiledFunction->nFormals; ++i) { |
117 | ic = ic->addMember(identifier: engine->identifierTable->asPropertyKey(str: compilationUnit->runtimeStrings[formalsIndices[i].nameIndex]), data: Attr_NotConfigurable); |
118 | if (enforcesSignature |
119 | && !hasTypes |
120 | && formalsIndices[i].type.typeNameIndexOrCommonType() |
121 | != quint32(QV4::CompiledData::CommonType::Invalid)) { |
122 | hasTypes = true; |
123 | } |
124 | } |
125 | internalClass = ic->d(); |
126 | |
127 | nFormals = compiledFunction->nFormals; |
128 | |
129 | if (!enforcesSignature) |
130 | return; |
131 | |
132 | if (!hasTypes |
133 | && compiledFunction->returnType.typeNameIndexOrCommonType() |
134 | == quint32(QV4::CompiledData::CommonType::Invalid)) { |
135 | return; |
136 | } |
137 | |
138 | QQmlPrivate::AOTCompiledFunction *synthesized = new QQmlPrivate::AOTCompiledFunction; |
139 | QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(e: engine->qmlEngine()); |
140 | |
141 | auto findMetaType = [&](const CompiledData::ParameterType ¶m) { |
142 | const quint32 type = param.typeNameIndexOrCommonType(); |
143 | if (param.indexIsCommonType()) { |
144 | if (param.isList()) { |
145 | return QQmlPropertyCacheCreatorBase::listTypeForPropertyType( |
146 | type: QV4::CompiledData::CommonType(type)); |
147 | } |
148 | return QQmlPropertyCacheCreatorBase::metaTypeForPropertyType( |
149 | type: QV4::CompiledData::CommonType(type)); |
150 | } |
151 | |
152 | if (type == 0) |
153 | return QMetaType(); |
154 | |
155 | const QQmlType qmltype = unit->typeNameCache->query(key: unit->stringAt(index: type)).type; |
156 | if (!qmltype.isValid()) |
157 | return QMetaType(); |
158 | |
159 | const QMetaType metaType = param.isList() ? qmltype.qListTypeId() : qmltype.typeId(); |
160 | if (metaType.isValid()) |
161 | return metaType; |
162 | |
163 | if (!qmltype.isComposite()) { |
164 | if (!qmltype.isInlineComponentType()) |
165 | return QMetaType(); |
166 | const CompositeMetaTypeIds typeIds = unit->typeIdsForComponent(inlineComponentName: qmltype.elementName()); |
167 | return param.isList() ? typeIds.listId : typeIds.id; |
168 | } |
169 | |
170 | const CompositeMetaTypeIds typeIds = enginePrivate->typeLoader.getType( |
171 | unNormalizedUrl: qmltype.sourceUrl())->compilationUnit()->typeIds; |
172 | return param.isList() ? typeIds.listId : typeIds.id; |
173 | }; |
174 | |
175 | for (quint16 i = 0; i < nFormals; ++i) |
176 | synthesized->argumentTypes.append(t: findMetaType(formalsIndices[i].type)); |
177 | |
178 | synthesized->returnType = findMetaType(compiledFunction->returnType); |
179 | aotCompiledFunction = synthesized; |
180 | kind = JsTyped; |
181 | } |
182 | |
183 | Function::~Function() |
184 | { |
185 | if (codeRef) { |
186 | destroyFunctionTable(function: this, codeRef); |
187 | delete codeRef; |
188 | } |
189 | if (kind == JsTyped) |
190 | delete aotCompiledFunction; |
191 | } |
192 | |
193 | void Function::updateInternalClass(ExecutionEngine *engine, const QList<QByteArray> ¶meters) |
194 | { |
195 | QStringList parameterNames; |
196 | |
197 | // Resolve duplicate parameter names: |
198 | for (int i = 0, ei = parameters.size(); i != ei; ++i) { |
199 | const QByteArray ¶m = parameters.at(i); |
200 | int duplicate = -1; |
201 | |
202 | for (int j = i - 1; j >= 0; --j) { |
203 | const QByteArray &prevParam = parameters.at(i: j); |
204 | if (param == prevParam) { |
205 | duplicate = j; |
206 | break; |
207 | } |
208 | } |
209 | |
210 | if (duplicate == -1) { |
211 | parameterNames.append(t: QString::fromUtf8(ba: param)); |
212 | } else { |
213 | const QString dup = parameterNames[duplicate]; |
214 | parameterNames.append(t: dup); |
215 | parameterNames[duplicate] = |
216 | QString(QChar(0xfffe)) + QString::number(duplicate) + dup; |
217 | } |
218 | |
219 | } |
220 | |
221 | internalClass = engine->internalClasses(icType: EngineBase::Class_CallContext); |
222 | |
223 | // first locals |
224 | const quint32_le *localsIndices = compiledFunction->localsTable(); |
225 | for (quint32 i = 0; i < compiledFunction->nLocals; ++i) { |
226 | internalClass = internalClass->addMember( |
227 | identifier: engine->identifierTable->asPropertyKey(str: compilationUnit->runtimeStrings[localsIndices[i]]), |
228 | data: Attr_NotConfigurable); |
229 | } |
230 | |
231 | Scope scope(engine); |
232 | ScopedString arg(scope); |
233 | for (const QString ¶meterName : parameterNames) { |
234 | arg = engine->newIdentifier(text: parameterName); |
235 | internalClass = internalClass->addMember(identifier: arg->propertyKey(), data: Attr_NotConfigurable); |
236 | } |
237 | nFormals = parameters.size(); |
238 | } |
239 | |
240 | QString Function::prettyName(const Function *function, const void *code) |
241 | { |
242 | QString prettyName = function ? function->name()->toQString() : QString(); |
243 | if (prettyName.isEmpty()) { |
244 | prettyName = QString::number(reinterpret_cast<quintptr>(code), base: 16); |
245 | prettyName.prepend(s: QLatin1String("QV4::Function(0x" )); |
246 | prettyName.append(c: QLatin1Char(')')); |
247 | } |
248 | return prettyName; |
249 | } |
250 | |
251 | QQmlSourceLocation Function::sourceLocation() const |
252 | { |
253 | return QQmlSourceLocation( |
254 | sourceFile(), compiledFunction->location.line(), compiledFunction->location.column()); |
255 | } |
256 | |
257 | } // namespace QV4 |
258 | |
259 | QT_END_NAMESPACE |
260 | |