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#ifndef QV4VALUE_P_H
4#define QV4VALUE_P_H
5
6//
7// W A R N I N G
8// -------------
9//
10// This file is not part of the Qt API. It exists purely as an
11// implementation detail. This header file may change from version to
12// version without notice, or even be removed.
13//
14// We mean it.
15//
16
17#include <limits.h>
18#include <cmath>
19
20#include <QtCore/QString>
21#include "qv4global_p.h"
22#include <private/qv4heap_p.h>
23#include <private/qv4internalclass_p.h>
24#include <private/qv4staticvalue_p.h>
25
26#include <private/qnumeric_p.h>
27#include <private/qv4calldata_p.h>
28
29QT_BEGIN_NAMESPACE
30
31namespace QV4 {
32
33namespace Heap {
34 struct Base;
35}
36
37struct Q_QML_PRIVATE_EXPORT Value : public StaticValue
38{
39 using ManagedPtr = Managed *;
40
41 Value() = default;
42 constexpr Value(quint64 val) : StaticValue(val) {}
43
44 static constexpr Value fromStaticValue(StaticValue staticValue)
45 {
46 return {staticValue._val};
47 }
48
49 inline bool isString() const;
50 inline bool isStringOrSymbol() const;
51 inline bool isSymbol() const;
52 inline bool isObject() const;
53 inline bool isFunctionObject() const;
54
55 QML_NEARLY_ALWAYS_INLINE String *stringValue() const {
56 if (!isString())
57 return nullptr;
58 return reinterpret_cast<String *>(const_cast<Value *>(this));
59 }
60 QML_NEARLY_ALWAYS_INLINE StringOrSymbol *stringOrSymbolValue() const {
61 if (!isStringOrSymbol())
62 return nullptr;
63 return reinterpret_cast<StringOrSymbol *>(const_cast<Value *>(this));
64 }
65 QML_NEARLY_ALWAYS_INLINE Symbol *symbolValue() const {
66 if (!isSymbol())
67 return nullptr;
68 return reinterpret_cast<Symbol *>(const_cast<Value *>(this));
69 }
70 QML_NEARLY_ALWAYS_INLINE Object *objectValue() const {
71 if (!isObject())
72 return nullptr;
73 return reinterpret_cast<Object*>(const_cast<Value *>(this));
74 }
75 QML_NEARLY_ALWAYS_INLINE ManagedPtr managed() const {
76 if (!isManaged())
77 return nullptr;
78 return reinterpret_cast<Managed*>(const_cast<Value *>(this));
79 }
80 QML_NEARLY_ALWAYS_INLINE Value::HeapBasePtr heapObject() const {
81 return isManagedOrUndefined() ? m() : nullptr;
82 }
83
84 static inline Value fromHeapObject(HeapBasePtr m)
85 {
86 Value v;
87 v.setM(m);
88 return v;
89 }
90
91 int toUInt16() const;
92 inline int toInt32() const;
93 inline unsigned int toUInt32() const;
94 qint64 toLength() const;
95 inline qint64 toIndex() const;
96
97 bool toBoolean() const {
98 if (integerCompatible())
99 return static_cast<bool>(int_32());
100
101 return toBooleanImpl(val: *this);
102 }
103 static bool toBooleanImpl(Value val);
104 double toInteger() const;
105 inline ReturnedValue convertedToNumber() const;
106 inline double toNumber() const;
107 static double toNumberImpl(Value v);
108 double toNumberImpl() const { return toNumberImpl(v: *this); }
109
110 QString toQStringNoThrow() const;
111 QString toQString() const;
112 QString toQString(bool *ok) const;
113
114 Heap::String *toString(ExecutionEngine *e) const {
115 if (isString())
116 return reinterpret_cast<Heap::String *>(m());
117 return toString(e, val: *this);
118 }
119 QV4::PropertyKey toPropertyKey(ExecutionEngine *e) const;
120
121 static Heap::String *toString(ExecutionEngine *e, Value val);
122 Heap::Object *toObject(ExecutionEngine *e) const {
123 if (isObject())
124 return reinterpret_cast<Heap::Object *>(m());
125 return toObject(e, val: *this);
126 }
127 static Heap::Object *toObject(ExecutionEngine *e, Value val);
128
129 inline bool isPrimitive() const;
130
131 template <typename T>
132 const T *as() const {
133 if (!isManaged())
134 return nullptr;
135
136 Q_ASSERT(m()->internalClass->vtable);
137#if !defined(QT_NO_QOBJECT_CHECK)
138 static_cast<const T *>(this)->qt_check_for_QMANAGED_macro(static_cast<const T *>(this));
139#endif
140 const VTable *vt = m()->internalClass->vtable;
141 while (vt) {
142 if (vt == T::staticVTable())
143 return static_cast<const T *>(this);
144 vt = vt->parent;
145 }
146 return nullptr;
147 }
148 template <typename T>
149 T *as() {
150 if (isManaged())
151 return const_cast<T *>(const_cast<const Value *>(this)->as<T>());
152 else
153 return nullptr;
154 }
155
156 template<typename T> inline T *cast() {
157 return static_cast<T *>(managed());
158 }
159 template<typename T> inline const T *cast() const {
160 return static_cast<const T *>(managed());
161 }
162
163 uint asArrayLength(bool *ok) const;
164
165 static constexpr Value fromReturnedValue(ReturnedValue val)
166 {
167 return fromStaticValue(staticValue: StaticValue::fromReturnedValue(val));
168 }
169
170 // As per ES specs
171 bool sameValue(Value other) const;
172 bool sameValueZero(Value other) const;
173
174 inline void mark(MarkStack *markStack);
175
176 static double toInteger(double d) { return StaticValue::toInteger(d); }
177 static int toInt32(double d) { return StaticValue::toInt32(d); }
178 static unsigned int toUInt32(double d) { return StaticValue::toUInt32(d); }
179 inline static constexpr Value emptyValue()
180 {
181 return fromStaticValue(staticValue: StaticValue::emptyValue());
182 }
183 static inline constexpr Value fromBoolean(bool b)
184 {
185 return fromStaticValue(staticValue: StaticValue::fromBoolean(b));
186 }
187 static inline constexpr Value fromInt32(int i)
188 {
189 return fromStaticValue(staticValue: StaticValue::fromInt32(i));
190 }
191 inline static constexpr Value undefinedValue()
192 {
193 return fromStaticValue(staticValue: StaticValue::undefinedValue());
194 }
195 static inline constexpr Value nullValue()
196 {
197 return fromStaticValue(staticValue: StaticValue::nullValue());
198 }
199 static inline Value fromDouble(double d)
200 {
201 return fromStaticValue(staticValue: StaticValue::fromDouble(d));
202 }
203 static inline Value fromUInt32(uint i)
204 {
205 return fromStaticValue(staticValue: StaticValue::fromUInt32(i));
206 }
207
208 Value &operator =(const ScopedValue &v);
209 Value &operator=(ReturnedValue v)
210 {
211 StaticValue::operator=(v);
212 return *this;
213 }
214 Value &operator=(ManagedPtr m) {
215 if (!m) {
216 setM(nullptr);
217 } else {
218 _val = reinterpret_cast<Value *>(m)->_val;
219 }
220 return *this;
221 }
222 Value &operator=(HeapBasePtr o) {
223 setM(o);
224 return *this;
225 }
226
227 template<typename T>
228 Value &operator=(const Scoped<T> &t);
229};
230Q_STATIC_ASSERT(std::is_trivial_v<Value>);
231Q_STATIC_ASSERT(sizeof(Value) == sizeof(StaticValue));
232
233template<>
234inline StaticValue &StaticValue::operator=<Value>(const Value &value)
235{
236 _val = value._val;
237 return *this;
238}
239
240template<typename Managed>
241inline StaticValue &StaticValue::operator=(const Managed &m)
242{
243 *static_cast<Value *>(this) = m;
244 return *this;
245}
246
247template<>
248inline Value &StaticValue::asValue<Value>()
249{
250 return *static_cast<Value *>(this);
251}
252
253template<>
254inline const Value &StaticValue::asValue<Value>() const
255{
256 return *static_cast<const Value *>(this);
257}
258
259template<>
260inline Value *CallData::argValues<Value>()
261{
262 return static_cast<Value *>(static_cast<StaticValue *>(args));
263}
264
265template<>
266inline const Value *CallData::argValues<Value>() const
267{
268 return static_cast<const Value *>(static_cast<const StaticValue *>(args));
269}
270
271template<typename HeapBase>
272inline Encode::Encode(HeapBase *o)
273{
274 val = Value::fromHeapObject(m: o).asReturnedValue();
275}
276
277inline void Value::mark(MarkStack *markStack)
278{
279 HeapBasePtr o = heapObject();
280 if (o)
281 o->mark(markStack);
282}
283
284inline bool Value::isString() const
285{
286 HeapBasePtr b = heapObject();
287 return b && b->internalClass->vtable->isString;
288}
289
290bool Value::isStringOrSymbol() const
291{
292 HeapBasePtr b = heapObject();
293 return b && b->internalClass->vtable->isStringOrSymbol;
294}
295
296bool Value::isSymbol() const
297{
298 HeapBasePtr b = heapObject();
299 return b && b->internalClass->vtable->isStringOrSymbol && !b->internalClass->vtable->isString;
300}
301
302inline bool Value::isObject() const
303
304{
305 HeapBasePtr b = heapObject();
306 return b && b->internalClass->vtable->isObject;
307}
308
309inline bool Value::isFunctionObject() const
310{
311 HeapBasePtr b = heapObject();
312 return b && b->internalClass->vtable->isFunctionObject;
313}
314
315inline bool Value::isPrimitive() const
316{
317 return !isObject();
318}
319
320inline double Value::toNumber() const
321{
322 if (isInteger())
323 return int_32();
324 if (isDouble())
325 return doubleValue();
326 return toNumberImpl();
327}
328
329inline ReturnedValue Value::convertedToNumber() const
330{
331 if (isInteger() || isDouble())
332 return asReturnedValue();
333 Value v;
334 v.setDouble(toNumberImpl());
335 return v.asReturnedValue();
336}
337
338inline
339ReturnedValue Heap::Base::asReturnedValue() const
340{
341 return Value::fromHeapObject(m: const_cast<Value::HeapBasePtr>(this)).asReturnedValue();
342}
343
344// For source compat with older code in other modules
345using Primitive = Value;
346
347template<typename T>
348ReturnedValue value_convert(ExecutionEngine *e, const Value &v);
349
350inline int Value::toInt32() const
351{
352 if (Q_LIKELY(integerCompatible()))
353 return int_32();
354
355 if (Q_LIKELY(isDouble()))
356 return QJSNumberCoercion::toInteger(d: doubleValue());
357
358 return QJSNumberCoercion::toInteger(d: toNumberImpl());
359}
360
361inline unsigned int Value::toUInt32() const
362{
363 return static_cast<unsigned int>(toInt32());
364}
365
366inline qint64 Value::toLength() const
367{
368 if (Q_LIKELY(integerCompatible()))
369 return int_32() < 0 ? 0 : int_32();
370 double i = Value::toInteger(d: isDouble() ? doubleValue() : toNumberImpl());
371 if (i <= 0)
372 return 0;
373 if (i > (static_cast<qint64>(1) << 53) - 1)
374 return (static_cast<qint64>(1) << 53) - 1;
375 return static_cast<qint64>(i);
376}
377
378inline qint64 Value::toIndex() const
379{
380 qint64 idx;
381 if (Q_LIKELY(integerCompatible())) {
382 idx = int_32();
383 } else {
384 idx = static_cast<qint64>(Value::toInteger(d: isDouble() ? doubleValue() : toNumberImpl()));
385 }
386 if (idx > (static_cast<qint64>(1) << 53) - 1)
387 idx = -1;
388 return idx;
389}
390
391inline double Value::toInteger() const
392{
393 if (integerCompatible())
394 return int_32();
395
396 return Value::toInteger(d: isDouble() ? doubleValue() : toNumberImpl());
397}
398
399
400template <size_t o>
401struct HeapValue : Value {
402 static constexpr size_t offset = o;
403 HeapBasePtr base() {
404 HeapBasePtr base = reinterpret_cast<HeapBasePtr>(this) - (offset/sizeof(Heap::Base));
405 Q_ASSERT(base->inUse());
406 return base;
407 }
408
409 void set(EngineBase *e, const Value &newVal) {
410 WriteBarrier::write(e, base(), data_ptr(), newVal.asReturnedValue());
411 }
412 void set(EngineBase *e, HeapBasePtr b) {
413 WriteBarrier::write(e, base(), data_ptr(), b->asReturnedValue());
414 }
415};
416
417template <size_t o>
418struct ValueArray {
419 static constexpr size_t offset = o;
420 uint size;
421 uint alloc;
422 Value values[1];
423
424 Value::HeapBasePtr base() {
425 Value::HeapBasePtr base = reinterpret_cast<Value::HeapBasePtr>(this)
426 - (offset/sizeof(Heap::Base));
427 Q_ASSERT(base->inUse());
428 return base;
429 }
430
431 void set(EngineBase *e, uint index, Value v) {
432 WriteBarrier::write(e, base(), values[index].data_ptr(), v.asReturnedValue());
433 }
434 void set(EngineBase *e, uint index, Value::HeapBasePtr b) {
435 WriteBarrier::write(e, base(), values[index].data_ptr(), Value::fromHeapObject(m: b).asReturnedValue());
436 }
437 inline const Value &operator[] (uint index) const {
438 Q_ASSERT(index < alloc);
439 return values[index];
440 }
441 inline const Value *data() const {
442 return values;
443 }
444
445 void insertData(EngineBase *e, uint index, Value v) {
446 for (uint i = size - 1; i > index; --i) {
447 values[i] = values[i - 1];
448 }
449 set(e, index, v);
450 }
451 void removeData(EngineBase *e, uint index, int n = 1) {
452 Q_UNUSED(e);
453 for (uint i = index; i < size - n; ++i) {
454 values[i] = values[i + n];
455 }
456 }
457
458 void mark(MarkStack *markStack) {
459 for (Value *v = values, *end = values + alloc; v < end; ++v)
460 v->mark(markStack);
461 }
462};
463
464// It's really important that the offset of values in this structure is
465// constant across all architecture, otherwise JIT cross-compiled code will
466// have wrong offsets between host and target.
467Q_STATIC_ASSERT(offsetof(ValueArray<0>, values) == 8);
468
469class OptionalReturnedValue {
470 ReturnedValue value;
471public:
472
473 OptionalReturnedValue() : value(Value::emptyValue().asReturnedValue()) {}
474 explicit OptionalReturnedValue(ReturnedValue v)
475 : value(v)
476 {
477 Q_ASSERT(!Value::fromReturnedValue(v).isEmpty());
478 }
479
480 ReturnedValue operator->() const { return value; }
481 ReturnedValue operator*() const { return value; }
482 explicit operator bool() const { return !Value::fromReturnedValue(val: value).isEmpty(); }
483};
484
485}
486
487QT_END_NAMESPACE
488
489#endif // QV4VALUE_DEF_P_H
490

source code of qtdeclarative/src/qml/jsruntime/qv4value_p.h