| Commit message (Collapse) | Author | Age | Files | Lines |
|
|
|
|
|
|
|
|
|
|
|
|
| |
We don't have to pass types that don't fit if we actually have the
correct types in suitable wrappers.
This still invokes the internal conversions of the call frame setup if
we call with really generic types, for example if the same value is read
multiple times with different target types. However, that is acceptable.
Fixes: QTBUG-113465
Change-Id: I8ec4afeb39bbe6585e5268c0e9b0cfd2788d761a
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This way we can identify which entry in a stack frame to amend when
processing an exception in generated code. However, negative line
numbers are also used to signal the position of "Ret" instructions.
Since you cannot throw an exception from a "Ret" instruction, those
cannot collide, but we cannot qAbs() the line number anymore when saving
it in the stack trace. We have to qAbs() it in all the places where it's
read.
Pick-to: 6.5
Fixes: QTBUG-112946
Change-Id: I24dc4008fb7eab38e4d24e70211c22e46f1b72a7
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
|
|
|
|
|
|
|
|
|
|
|
| |
So far we can only deal with methods that don't change the source array
and don't use iterators or functions as parameters. We also omit
concat() for now. However, indexOf(), lastIndexOf(), includes(),
join(), slice() and toString() are possible already now.
Task-number: QTBUG-112722
Change-Id: Id19c74e8ad25af876bc954c040c767823b7e3259
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The optimization for object-to-string conversion would never trigger
because we were checking the wrong metatype for the PointerToQObject
flag.
Furthermore, we can provide a very simple optimization for the case when
we just want a QObject*. Finally, if we have both types, we can use
is_base_of_v to optimize cases where we are converting to a base class.
Pick-to: 6.5
Task-number: QTBUG-111986
Change-Id: I731fe0398730a2a83222d8c2fdff8aa3d21f7aec
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
|
|
|
|
|
|
|
|
| |
And add \since information.
Change-Id: I3634a3b2dbc48e63fac4810cfc0c1a5137e925db
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
Reviewed-by: Paul Wicking <paul.wicking@qt.io>
|
|
|
|
|
|
|
| |
This allows us to do some more specialized lookups in QmlCompiler.
Change-Id: I7947c8e7bccfa57aee949b080a531a88fd47c8af
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
|
|
|
|
|
|
|
|
| |
Otherwise the gc might collect them while we're still operating on them.
Pick-to: 6.2 6.5
Change-Id: I4644ff7b4b1221f3e58832a245d71215e77bd891
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
|
|
|
|
|
|
| |
Pick-to: 6.5 6.4 6.2
Change-Id: Ibd29739b894598e5d7837ed5f9150e08ca07fa35
Reviewed-by: Topi Reiniö <topi.reinio@qt.io>
|
|
|
|
|
|
|
|
|
|
|
|
| |
They should either be the same size as int/uint or the same size as
longlong/ulonglong, but for some reason we get them as separate types.
Pick-to: 6.5
Fixes: QTBUG-110767
Change-Id: I4c5826cfe6108e6f9722e6b3443bde13b2141b04
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Semih Yavuz <semih.yavuz@qt.io>
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
|
|
|
|
|
|
| |
Pick-to: 6.5
Change-Id: I7f700d64694c8651769841a0109d32e8f9a839b2
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
|
|
|
|
|
|
|
| |
Fixes: QTBUG-110585
Pick-to: 6.5
Change-Id: I6acd1ea2a171bd5978bed752f52b8af262936cc2
Reviewed-by: Marc Mutz <marc.mutz@qt.io>
|
|
|
|
|
|
|
|
|
|
|
| |
Add explicit where appropriate, and use more elegant constructs in
inline functions. Introduce removed_api.cpp for
QJSEngine::create(int, const void *).
Pick-to: 6.5
Change-Id: Ie54b0494fe3c5567f8a5ca361c3a583de3d97dd5
Reviewed-by: Marc Mutz <marc.mutz@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
|
|
|
|
|
|
|
|
|
| |
The JavaScript date and time conversions are different from Qt's. Add
them to coerceValue.
Task-number: QTBUG-109380
Change-Id: Ic0d7dd8ff51fb8e29d80d9084d4415becaa76259
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
|
|
|
|
|
|
|
|
|
|
|
| |
Ensure that negative number times positive zero yields negative zero,
not positive zero.
Do the same adjustment in QJSPrimitiveValue
Fixes: QTBUG-104582
Pick-to: 6.5
Change-Id: I8231bfb051b7d902e5e50bbd282410a572b1628a
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
|
|
|
|
|
|
|
|
|
|
| |
Since we can produce QJSPrimitiveValue in metaTypeFromJS, we should also
handle the other direction.
Fixes: QTBUG-109867
Pick-to: 6.5
Change-Id: I2c7598d19eba3e78d071ca3eceb32deda4d0ead8
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
|
|
|
|
|
|
|
|
| |
Amends commit 0925c51c5988bee7ee96944de2d857b2132ebe65.
Pick-to: 6.5
Change-Id: Ia0a42cd32813659a6e3d431e83506e82fac188bd
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
On android and on some other platforms, the upper bits of a pointer are
significant. We need to store them in our JS value encoding. Shift the
bits around to make this happen.
We now can store pointers of up to 57 bits. That's enough for everything
we've seen so far.
Fixes: QTBUG-101686
Fixes: QTBUG-91150
Pick-to: 6.5
Change-Id: I72e0fe63b27fca94840f82963e4d3936b3581b28
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Reviewed-by: Sami Shalayel <sami.shalayel@qt.io>
Reviewed-by: Ville Voutilainen <ville.voutilainen@qt.io>
|
|
|
|
|
|
|
|
|
| |
... and drop the notes about QMetaType<T>. You can get a metatype for
any type these days.
Pick-to: 6.5
Change-Id: Iaae53830dfb9cbe81975c3f73cf6cbe33494884a
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
|
|
|
|
|
|
|
|
|
|
|
|
| |
We need those in order to get correct results when using the more
generic conversions with QJSPrimitiveValue as source or target. Without
those extra methods, we frequently get garbage where it would be
possible to construct a QJSPrimitiveValue.
Pick-to: 6.5
Task-number: QTBUG-109111
Change-Id: I6ceb2a4ed73dae228dd2e5690cd608c58537b95f
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
We need to explicitly cast to double if we are wrapping a number type
that's not natively accepted by the ctors.
As a side effect, correctly run conversions from generic QVariant to
QJSPrimitiveValue through the engine now. For that we need another
clause in metaTypeFromJS().
Since we are calling methods that return list types in the test, we need
to add another clause that converts JS arrays to list types. Otherwise
we cannot run that test in interpreted mode.
Pick-to: 6.5 6.2 6.4 6.4.2
Task-number: QTBUG-109111
Change-Id: I87f7aafd24371d2c1ffe85569e1f2cd3a1979742
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This is commonly done for logging. With this in place we can have the
code generator use coerceType() for such constructs.
[ChangeLog][QML][Important Behavior Changes] You can implement custom
toString() methods for your QML objects in JavaScript or in C++. Those
methods don't actually have to return a string. Previously, whatever
return value the method generated was forwarded as-is. Now it is coerced
to a string.
Change-Id: I4a9721a6948be0c24a36b31d453a74bd747db729
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Reviewed-by: Sami Shalayel <sami.shalayel@qt.io>
|
|
|
|
|
|
|
| |
We won't need it anymore in Qt7.
Change-Id: Iff8bfd5192d80d7603aaa66da8cb00ab23e60f99
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
|
|
|
|
|
|
|
|
|
|
|
| |
JavaScript has its own type coercion rules. We already have a methods
that coerce QVariants, QJSValues and QJSManagedValues to specific types.
The new method is a generalization of all of those and can coerce
everything to everything (as far as JavaScript can).
Change-Id: I9b6877fb40f67b6f2354781bbd4cf18cf996c7b0
Reviewed-by: Sami Shalayel <sami.shalayel@qt.io>
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
|
|
|
|
|
|
|
|
|
|
| |
This is especially useful if you already have serialization code that
can perform strict checks on the data, rather than having to rewrite it
to work with QJSValue.
Pick-to: 6.2 6.4
Change-Id: I5b12aa9806e187586ac1b41995633a46621f76c9
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This is a semantic patch using ClangTidyTransformator to convert
sequences of Q_UNREACHABLE() + return into Q_UNREACHABLE_RETURN(),
newly added to qtbase.
const std::string unr = "unr", val = "val", ret = "ret";
auto makeUnreachableReturn = cat("Q_UNREACHABLE_RETURN(",
ifBound(val, cat(node(val)), cat("")),
")");
auto ignoringSwitchCases = [](auto stmt) {
return anyOf(stmt, switchCase(subStmt(stmt)));
};
makeRule(stmt(ignoringSwitchCases(stmt(isExpandedFromMacro("Q_UNREACHABLE")).bind(unr)),
nextStmt(returnStmt(optionally(hasReturnValue(expr().bind(val)))).bind(ret))),
{changeTo(node(unr), cat(makeUnreachableReturn,
";")), // TODO: why is the ; lost w/o this?
changeTo(node(ret), cat(""))},
cat("use ", makeUnreachableReturn));
a.k.a qt-use-unreachable-return.
subStmt() and nextStmt() are non-standard matchers.
There was one false positive, suppressed it with NOLINTNEXTLINE.
It's not really a false positiive, it's just that Clang sees the world
in one way and if conditonal compilation (#if) differs for other
compilers, Clang doesn't know better. This is an artifact of matching
two consecutive statements.
Change-Id: I3855b2dc8523db1ea860f72ad9818738162495c6
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This is a semantic patch using ClangTidyTransformator as in
qtbase/df9d882d41b741fef7c5beeddb0abe9d904443d8, but extended to
handle typedefs and accesses through pointers, too:
const std::string o = "object";
auto hasTypeIgnoringPointer = [](auto type) { return anyOf(hasType(type), hasType(pointsTo(type))); };
auto derivedFromAnyOfClasses = [&](ArrayRef<StringRef> classes) {
auto exprOfDeclaredType = [&](auto decl) {
return expr(hasTypeIgnoringPointer(hasUnqualifiedDesugaredType(recordType(hasDeclaration(decl))))).bind(o);
};
return exprOfDeclaredType(cxxRecordDecl(isSameOrDerivedFrom(hasAnyName(classes))));
};
auto renameMethod = [&] (ArrayRef<StringRef> classes,
StringRef from, StringRef to) {
return makeRule(cxxMemberCallExpr(on(derivedFromAnyOfClasses(classes)),
callee(cxxMethodDecl(hasName(from), parameterCountIs(0)))),
changeTo(cat(access(o, cat(to)), "()")),
cat("use '", to, "' instead of '", from, "'"));
};
renameMethod(<classes>, "count", "size");
renameMethod(<classes>, "length", "size");
except that on() was replaced with a matcher that doesn't ignoreParens().
a.k.a qt-port-to-std-compatible-api V5 with config Scope: 'Container'.
Change-Id: I58e1b41b91c34d2e860dbb5847b3752edbfc6fc9
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This is a semantic patch using ClangTidyTransformator as in
qtbase/df9d882d41b741fef7c5beeddb0abe9d904443d8:
auto QtContainerClass = anyOf(
expr(hasType(cxxRecordDecl(isSameOrDerivedFrom(hasAnyName(classes))))).bind(o),
expr(hasType(namedDecl(hasAnyName(<classes>)))).bind(o));
makeRule(cxxMemberCallExpr(on(QtContainerClass),
callee(cxxMethodDecl(hasAnyName({"count", "length"),
parameterCountIs(0))))),
changeTo(cat(access(o, cat("size"), "()"))),
cat("use 'size()' instead of 'count()/length()'"))
a.k.a qt-port-to-std-compatible-api with config Scope: 'Container',
with the extended set of container classes recognized.
Change-Id: Idb1f75dfe2323bd1d9e8b4d58d54f1b4b80c7ed7
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
|
|
|
|
|
|
|
|
|
| |
Wherever we need an engine in there, we also have a managed value to get
it from. This relieves us from the requirement to drag an engine around
wherever we want to call toVariant().
Change-Id: Ib95d02b5fbf5eaa494214e337c9b700e97e5e0df
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Drop unnecessary includes detected by clangd-iwyu.
Add new includes due to the transitive includes. Also, some of the
includes were detected as unused even if they were actually in use.
In those cases, use angular brackets instead of "" which deceives
the tool not to complain.
Affected subfolders: Debugger, Compiler, JsApi, JsRuntime, Memory,
Parser
Task-number: QTBUG-106473
Change-Id: I01d996a2a2ba31cbbc5f60f5454c8f850298f528
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
|
|
|
|
|
|
|
|
|
| |
So far, you could only use them from pure JavaScript programs. Also, fix
re-exporting parts of native modules.
Fixes: QTBUG-105901
Change-Id: I170017083284e6457b1aa0c6e606fd26227edae3
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
|
|
|
|
|
|
|
|
|
| |
Also, add missing positive infinity to test data.
Pick-to: 6.4
Fixes: QTBUG-104745
Change-Id: I958aca672cca8cc83c540ed3ea75b08e70eb90fd
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Arguments are now treated as registers "written" at the beginning of
the first basic block. By modeling them this way, we can avoid all the
complicated logic on whether to use a local or the arguments array when
accessing any particular one of them. Furthermore, we can detect whether
they are overwritten or not. If they are not overwritten, we can
initialize them as a const reference into the arguments array. This way
we save a copy.
Treating the arguments as generic registers causes the basic blocks pass
to aggressively adjust their types, pushing some conversions back into
the QML engine. This is good. Unused arguments become void, for example,
and don't have to be passed at all. However, we also need a special case
for QJSPrimitiveValue arguments now.
Pick-to: 6.4
Fixes: QTBUG-104462
Change-Id: I994bea0929bd508aa41db58dee4a7f12cd20f053
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Reviewed-by: Sami Shalayel <sami.shalayel@qt.io>
|
|
|
|
|
|
|
|
|
|
|
|
| |
Replace the current license disclaimer in files by
a SPDX-License-Identifier.
Files that have to be modified by hand are modified.
License files are organized under LICENSES directory.
Pick-to: 6.4
Task-number: QTBUG-67283
Change-Id: I63563bbeb6f60f89d2c99660400dca7fab78a294
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
|
|
|
|
|
|
|
|
|
|
|
| |
The isInterrupted flag is just that: a flag, so it doesn't require
acquire/release semantics when loading/storing.
Use relaxed loads and stores instead.
Change-Id: I6d733a6bebcfc7f2b786265fc28f9ba7e25bb1c7
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
|
|
|
|
|
| |
Change-Id: I6df08dca64ff8670e54c663912ebae980b316a5a
Reviewed-by: Andrei Golubev <andrei.golubev@qt.io>
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
In JavaScript we have a number of extra conversions not covered by
qvariant_cast. Therefore, add a method to perform a QVariant conversion
in JavaScript semantics to QJSEngine, and use that in the compiler.
Pick-to: 6.3
Fixes: QTBUG-100883
Change-Id: I8b0bfa0974bc6b339d2601fb373859bc710788c8
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Reviewed-by: Andrei Golubev <andrei.golubev@qt.io>
Reviewed-by: Jarkko Koivikko <jarkko.koivikko@code-q.fi>
|
|
|
|
|
|
|
|
|
| |
As a drive-by, replace qSwap with std::swap (simpler for the compiler,
type is known, so no need for ADL).
Change-Id: Id6f6a2e005dafb011bf8a91f7a9956c1aed6ff93
Reviewed-by: Andrei Golubev <andrei.golubev@qt.io>
Reviewed-by: Marc Mutz <marc.mutz@qt.io>
|
|
|
|
|
|
|
|
|
| |
It is just in line with the other propertyCache() methods, and should be
treated the same way. The comment made no sense anymore. This allows us
to drop more engine pointers.
Change-Id: I2e9b479b555c7f771b619e4693d59cbfcf244df6
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
|
|
|
|
|
|
|
| |
There is no reason to drag an engine around for those.
Change-Id: I02100b207f197e75dfd458ff1cce5c4920dd94bd
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
|
|
|
|
|
|
|
|
|
| |
It's only used for the image provider. All other uses are pointless
because the respective functions are either re-entrant or protected by
other mutexes.
Change-Id: Id1d2f58955e0439421081764dff6ce6152006fda
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
|
|
|
|
|
|
|
|
|
|
|
|
| |
If we pass in a const pointer, we're not allowed to modify the object,
so the create==true case does not make sense there. We therefore provide
now two versions of the function: One taking only a const pointer, and
one taking a non-const pointer and a bool. The latter no longer provides
a default parameter to encourage usage of the the const version wherever
possible.
Change-Id: Ifb5a7e0605127de429403982b31f754e154b8048
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
We generally want to use QQmlRefPointer for it, rather than manually
calling addref() and release() all over the place. Also, we can
completely inline its ctor and drop an unused member.
Also, do not keep property caches of dynamic meta objects in type
registry. The dynamic metaobjects will change, and the outdated
property caches will eventually be retrieved.
Change-Id: I8042c85b32f3031b554f97a35c1545a3412d2acb
Reviewed-by: Andrei Golubev <andrei.golubev@qt.io>
|
|
|
|
|
|
|
|
|
| |
QFlagPointer has been moved to qtbase. As a drive-by, remove includes
of qbipointer_p.h which are no longer needed and add them where the
type is actually used.
Change-Id: I067864e6c082dcbd422deb79812ea7c36412caba
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
|
|
|
|
|
|
|
|
|
|
|
|
| |
It was a plain QProperty before. Given that the property is exposed in
the global Qt object in the engine, this could be problematic as with
the QProperty it is possible to change the value without emitting the
uiLanguageChanged signal. By using QObjectBindableProperty, we ensure
that the signal is always emitted.
Pick-to: 6.2
Change-Id: I0f771a4e4d752704f469de27617835260b261052
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
|
|
|
|
|
|
|
|
| |
Pick-to: 6.2
Task-number: QTBUG-91163
Change-Id: I7fdac1ff11b4e1c5a6b0caa6cbeae67ddc3effc4
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
|
|
|
|
|
|
| |
Task-number: QTBUG-91163
Change-Id: Iee52fce16d899a2a72277e314d7dc65b27da3b88
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Previously each test would include and build sources from the shared
folder. Now we make those sources a library, build it once, then have
each test link to it instead.
We also take the opportunity to move some helpers that qtquickcontrols2
had added into the quicktestutils library where it makes sense, and
for the helpers that don't make sense to be there, move them into
quickcontrolstestutils.
We add the libraries to src/ so that they are internal modules built as
part of Qt, rather than tests. That way we can use them in a standalone
test outside of qtdeclarative.
Task-number: QTBUG-95621
Pick-to: 6.2
Change-Id: I0a2ab3976fdbff2e4414df7bdc0808f16453b80a
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
|
|
|
|
|
|
|
|
|
| |
Since e04822f3c2a6b69b7d75e2039256aa2433c59dd2 it behaves exactly the
same as QMutexLocker, and thus its class description was misleading and
its implementation superfluous.
Change-Id: Iba9b06e73d89314b9137914eb731eaee511058c0
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
|
|
|
|
|
|
|
|
|
|
|
|
| |
The toScriptValue function template called the exported create function.
Thus, we can not change the signature of create, even though it was
private. To avoid the BIC break, we keep the old version (and ifdef it
so that it will go away in Qt 7).
Task-number: QTBUG-94407
Pick-to: 6.2
Change-Id: I5b07f978dca156f52bdb529d3e15aea8c0c3c97e
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
|
|
|
|
|
|
|
| |
It is shorter and encapsulates the exception handling a bit.
Change-Id: I8e2dc0eb3b930e222b8cb4852b73d99ca18a0379
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
|