diff options
author | Ulf Hermann <ulf.hermann@qt.io> | 2019-05-20 15:24:51 +0200 |
---|---|---|
committer | Ulf Hermann <ulf.hermann@qt.io> | 2019-05-31 15:03:33 +0200 |
commit | c8c2db3f5b157131542025ce556d248c7a916a00 (patch) | |
tree | 4c91465c8935b880c6dd42cb427fa3b173d52157 | |
parent | 0f035c0ad79ca41a1473b64a4c0077e7085d3700 (diff) |
Split QV4::Value into a static and a dynamic part
The static part can be used for compilation and won't resolve managed
objects. This allows us to remove all the remaining V4_BOOTSTRAP.
Change-Id: Id2f6feb64c48beb2a407697881aea8c0d791a532
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
28 files changed, 824 insertions, 639 deletions
diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 6f46648572..3e5798ba8b 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -39,7 +39,7 @@ #include "qqmlirbuilder_p.h" -#include <private/qv4value_p.h> +#include <private/qv4staticvalue_p.h> #include <private/qv4compileddata_p.h> #include <private/qqmljsparser_p.h> #include <private/qqmljslexer_p.h> diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index b145ceb51e..b7e3e20fd0 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -48,7 +48,7 @@ #include <private/qqmljslexer_p.h> #include <private/qqmljsparser_p.h> #include <private/qv4stringtoarrayindex_p.h> -#include <private/qv4value_p.h> +#include <private/qv4staticvalue_p.h> #include <private/qv4compilercontext_p.h> #include <private/qv4compilercontrolflow_p.h> #include <private/qv4bytecodegenerator_p.h> @@ -267,13 +267,26 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr) return exprResult(); if (expr.isConstant()) { - auto v = Value::fromReturnedValue(expr.constant); + auto v = StaticValue::fromReturnedValue(expr.constant); if (v.isNumber()) { switch (op) { case Not: return Reference::fromConst(this, Encode(!v.toBoolean())); case UMinus: - return Reference::fromConst(this, Runtime::UMinus::call(v)); + // This duplicates some of the logic from Runtime::UMinus::call() + ReturnedValue r; + if (v.isInteger()) { + int intVal = v.integerValue(); + if (intVal && intVal != std::numeric_limits<int>::min()) + r = QV4::Encode(-intVal); + else + r = QV4::Encode(-double(intVal)); + } else if (v.isDouble()) { + r = QV4::Encode(-v.doubleValue()); + } else { + r = QV4::Encode(-v.int_32()); + } + return Reference::fromConst(this, r); case UPlus: return expr; case Compl: @@ -1006,7 +1019,7 @@ bool Codegen::visit(ClassExpression *ast) return false; r.storeOnStack(heritage.stackSlot()); } else { - Reference::fromConst(this, Value::emptyValue().asReturnedValue()).loadInAccumulator(); + Reference::fromConst(this, StaticValue::emptyValue().asReturnedValue()).loadInAccumulator(); heritage.storeConsumeAccumulator(); } @@ -1083,7 +1096,7 @@ bool Codegen::visit(ArrayPattern *ast) if (args == -1) args = temp; if (!arg) { - auto c = Reference::fromConst(this, Value::emptyValue().asReturnedValue()); + auto c = Reference::fromConst(this, StaticValue::emptyValue().asReturnedValue()); (void) c.storeOnStack(temp); } else { RegisterScope scope(this); @@ -1144,7 +1157,8 @@ bool Codegen::visit(ArrayPattern *ast) while (it) { for (Elision *elision = it->elision; elision; elision = elision->next) { - Reference::fromConst(this, Value::emptyValue().asReturnedValue()).loadInAccumulator(); + Reference::fromConst( + this, StaticValue::emptyValue().asReturnedValue()).loadInAccumulator(); pushAccumulator(); } @@ -1538,9 +1552,9 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re } case QSOperator::BitAnd: if (right.isConstant()) { - int rightAsInt = Value::fromReturnedValue(right.constant).toInt32(); + int rightAsInt = StaticValue::fromReturnedValue(right.constant).toInt32(); if (left.isConstant()) { - int result = Value::fromReturnedValue(left.constant).toInt32() & rightAsInt; + int result = StaticValue::fromReturnedValue(left.constant).toInt32() & rightAsInt; return Reference::fromConst(this, Encode(result)); } left.loadInAccumulator(); @@ -1556,9 +1570,9 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re break; case QSOperator::BitOr: if (right.isConstant()) { - int rightAsInt = Value::fromReturnedValue(right.constant).toInt32(); + int rightAsInt = StaticValue::fromReturnedValue(right.constant).toInt32(); if (left.isConstant()) { - int result = Value::fromReturnedValue(left.constant).toInt32() | rightAsInt; + int result = StaticValue::fromReturnedValue(left.constant).toInt32() | rightAsInt; return Reference::fromConst(this, Encode(result)); } left.loadInAccumulator(); @@ -1574,9 +1588,9 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re break; case QSOperator::BitXor: if (right.isConstant()) { - int rightAsInt = Value::fromReturnedValue(right.constant).toInt32(); + int rightAsInt = StaticValue::fromReturnedValue(right.constant).toInt32(); if (left.isConstant()) { - int result = Value::fromReturnedValue(left.constant).toInt32() ^ rightAsInt; + int result = StaticValue::fromReturnedValue(left.constant).toInt32() ^ rightAsInt; return Reference::fromConst(this, Encode(result)); } left.loadInAccumulator(); @@ -1594,7 +1608,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re if (right.isConstant()) { left.loadInAccumulator(); Instruction::UShrConst ushr; - ushr.rhs = Value::fromReturnedValue(right.constant).toInt32() & 0x1f; + ushr.rhs = StaticValue::fromReturnedValue(right.constant).toInt32() & 0x1f; bytecodeGenerator->addInstruction(ushr); } else { right.loadInAccumulator(); @@ -1607,7 +1621,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re if (right.isConstant()) { left.loadInAccumulator(); Instruction::ShrConst shr; - shr.rhs = Value::fromReturnedValue(right.constant).toInt32() & 0x1f; + shr.rhs = StaticValue::fromReturnedValue(right.constant).toInt32() & 0x1f; bytecodeGenerator->addInstruction(shr); } else { right.loadInAccumulator(); @@ -1620,7 +1634,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re if (right.isConstant()) { left.loadInAccumulator(); Instruction::ShlConst shl; - shl.rhs = Value::fromReturnedValue(right.constant).toInt32() & 0x1f; + shl.rhs = StaticValue::fromReturnedValue(right.constant).toInt32() & 0x1f; bytecodeGenerator->addInstruction(shl); } else { right.loadInAccumulator(); @@ -1748,7 +1762,7 @@ Codegen::Reference Codegen::jumpBinop(QSOperator::Op oper, Reference &left, Refe qSwap(left, right); // null==a -> a==null if (right.isConstant()) { - Value c = Value::fromReturnedValue(right.constant); + StaticValue c = StaticValue::fromReturnedValue(right.constant); if (c.isNull() || c.isUndefined()) { left.loadInAccumulator(); if (oper == QSOperator::Equal) { @@ -2022,7 +2036,9 @@ Codegen::Arguments Codegen::pushArgs(ArgumentList *args) argc = 0; for (ArgumentList *it = args; it; it = it->next) { if (it->isSpreadElement) { - Reference::fromConst(this, Value::emptyValue().asReturnedValue()).storeOnStack(calldata + argc); + Reference::fromConst( + this, + StaticValue::emptyValue().asReturnedValue()).storeOnStack(calldata + argc); ++argc; } RegisterScope scope(this); @@ -2999,7 +3015,8 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, functionEndsWithReturn = endsWithReturn(_module, body); // reserve the js stack frame (Context & js Function & accumulator) - bytecodeGenerator->newRegisterArray(sizeof(CallData)/sizeof(Value) - 1 + _context->arguments.size()); + bytecodeGenerator->newRegisterArray( + sizeof(CallData) / sizeof(StaticValue) - 1 + _context->arguments.size()); bool _inFormalParameterList = false; qSwap(_inFormalParameterList, inFormalParameterList); @@ -4314,7 +4331,7 @@ QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // the loads below are empty str Instruction::LoadUndefined load; codegen->bytecodeGenerator->addInstruction(load); } else { - Value p = Value::fromReturnedValue(constant); + StaticValue p = StaticValue::fromReturnedValue(constant); if (p.isNumber()) { double d = p.asDouble(); int i = static_cast<int>(d); @@ -4325,7 +4342,7 @@ QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // the loads below are empty str return; } Instruction::LoadInt load; - load.value = Value::fromReturnedValue(constant).toInt32(); + load.value = StaticValue::fromReturnedValue(constant).toInt32(); codegen->bytecodeGenerator->addInstruction(load); return; } diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h index e519da0142..6d5f8c0951 100644 --- a/src/qml/compiler/qv4codegen_p.h +++ b/src/qml/compiler/qv4codegen_p.h @@ -258,7 +258,8 @@ public: } static Reference fromArgument(Codegen *cg, int index, bool isVolatile) { Reference r(cg, StackSlot); - r.theStackSlot = Moth::StackSlot::createRegister(index + sizeof(CallData)/sizeof(Value) - 1); + r.theStackSlot = Moth::StackSlot::createRegister( + index + sizeof(CallData) / sizeof(StaticValue) - 1); r.stackSlotIsLocalOrArgument = true; r.isVolatile = isVolatile; return r; diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 0fed0a03b2..813868b0ae 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -38,7 +38,7 @@ ****************************************************************************/ #include "qv4compileddata_p.h" -#include <private/qv4value_p.h> +#include <private/qv4staticvalue_p.h> #include <private/qqmlirbuilder_p.h> #include <QCoreApplication> #include <QCryptographicHash> @@ -144,13 +144,13 @@ void CompilationUnit::setUnitData(const Unit *unitData, const QmlUnit *qmlUnit, qmlData = qmlUnit ? qmlUnit : data->qmlUnit(); #if Q_BYTE_ORDER == Q_BIG_ENDIAN - Value *bigEndianConstants = new Value[data->constantTableSize]; + StaticValue *bigEndianConstants = new StaticValue[data->constantTableSize]; const quint64_le *littleEndianConstants = data->constants(); for (uint i = 0; i < data->constantTableSize; ++i) - bigEndianConstants[i] = Value::fromReturnedValue(littleEndianConstants[i]); + bigEndianConstants[i] = StaticValue::fromReturnedValue(littleEndianConstants[i]); constants = bigEndianConstants; #else - constants = reinterpret_cast<const Value*>(data->constants()); + constants = reinterpret_cast<const StaticValue*>(data->constants()); #endif m_fileName = !fileName.isEmpty() ? fileName : stringAt(data->sourceFileIndex); diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 94b64694ae..63738d6002 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -88,6 +88,7 @@ struct Document; } namespace QV4 { +struct StaticValue; namespace Heap { struct Module; @@ -1070,10 +1071,10 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnitBase // pointers either to data->constants() or little-endian memory copy. QV4::Heap::String **runtimeStrings = nullptr; // Array - const Value* constants = nullptr; - QV4::Value *runtimeRegularExpressions = nullptr; + const StaticValue* constants = nullptr; + QV4::StaticValue *runtimeRegularExpressions = nullptr; QV4::Heap::InternalClass **runtimeClasses = nullptr; - const Value** imports = nullptr; + const StaticValue** imports = nullptr; }; Q_STATIC_ASSERT(std::is_standard_layout<CompilationUnitBase>::value); diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index 4a54e5a44b..8722263b04 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -40,7 +40,7 @@ #include <qv4compiler_p.h> #include <qv4compileddata_p.h> #include <qv4codegen_p.h> -#include <private/qv4value_p.h> +#include <private/qv4staticvalue_p.h> #include <private/qv4alloca_p.h> #include <private/qqmljslexer_p.h> #include <private/qqmljsast_p.h> diff --git a/src/qml/compiler/qv4compilercontext.cpp b/src/qml/compiler/qv4compilercontext.cpp index d1a5fee92b..88837b0feb 100644 --- a/src/qml/compiler/qv4compilercontext.cpp +++ b/src/qml/compiler/qv4compilercontext.cpp @@ -156,7 +156,7 @@ Context::ResolvedName Context::resolveName(const QString &name, const QQmlJS::AS result.isConst = false; return result; } else { - result.index = argIdx + sizeof(CallData)/sizeof(Value) - 1; + result.index = argIdx + sizeof(CallData) / sizeof(StaticValue) - 1; result.scope = 0; result.type = ResolvedName::Stack; result.isConst = false; diff --git a/src/qml/compiler/qv4executablecompilationunit.cpp b/src/qml/compiler/qv4executablecompilationunit.cpp index 97c828d4d8..c68f6a7cbf 100644 --- a/src/qml/compiler/qv4executablecompilationunit.cpp +++ b/src/qml/compiler/qv4executablecompilationunit.cpp @@ -102,7 +102,7 @@ static QString toString(QV4::ReturnedValue v) return result; } -static void dumpConstantTable(const Value *constants, uint count) +static void dumpConstantTable(const StaticValue *constants, uint count) { QDebug d = qDebug(); d.nospace() << right; @@ -320,7 +320,7 @@ void ExecutableCompilationUnit::markObjects(QV4::MarkStack *markStack) } if (runtimeRegularExpressions) { for (uint i = 0; i < data->regexpTableSize; ++i) - runtimeRegularExpressions[i].mark(markStack); + Value::fromStaticValue(runtimeRegularExpressions[i]).mark(markStack); } if (runtimeClasses) { for (uint i = 0; i < data->jsClassTableSize; ++i) @@ -458,8 +458,8 @@ Heap::Module *ExecutableCompilationUnit::instantiate(ExecutionEngine *engine) const uint importCount = data->importEntryTableSize; if (importCount > 0) { - imports = new const Value *[importCount]; - memset(imports, 0, importCount * sizeof(Value *)); + imports = new const StaticValue *[importCount]; + memset(imports, 0, importCount * sizeof(StaticValue *)); } for (uint i = 0; i < importCount; ++i) { const CompiledData::ImportEntry &entry = data->importEntryTable()[i]; @@ -517,7 +517,7 @@ const Value *ExecutableCompilationUnit::resolveExportRecursively( if (index == UINT_MAX) return nullptr; if (index >= module()->scope->locals.size) - return imports[index - module()->scope->locals.size]; + return &(imports[index - module()->scope->locals.size]->asValue<Value>()); return &module()->scope->locals[index]; } diff --git a/src/qml/compiler/qv4instr_moth.cpp b/src/qml/compiler/qv4instr_moth.cpp index 8a9bd66103..640a908dd3 100644 --- a/src/qml/compiler/qv4instr_moth.cpp +++ b/src/qml/compiler/qv4instr_moth.cpp @@ -41,6 +41,8 @@ #include <private/qv4compileddata_p.h> #include <private/qv4calldata_p.h> +#include <QtCore/qdebug.h> + using namespace QV4; using namespace QV4::Moth; @@ -110,9 +112,9 @@ const int InstrInfo::argumentCount[] = { QString dumpRegister(int reg, int nFormals) { Q_STATIC_ASSERT(offsetof(CallData, function) == 0); - Q_STATIC_ASSERT(offsetof(CallData, context) == sizeof(Value)); - Q_STATIC_ASSERT(offsetof(CallData, accumulator) == 2*sizeof(Value)); - Q_STATIC_ASSERT(offsetof(CallData, thisObject) == 3*sizeof(Value)); + Q_STATIC_ASSERT(offsetof(CallData, context) == sizeof(StaticValue)); + Q_STATIC_ASSERT(offsetof(CallData, accumulator) == 2*sizeof(StaticValue)); + Q_STATIC_ASSERT(offsetof(CallData, thisObject) == 3*sizeof(StaticValue)); if (reg == CallData::Function) return QStringLiteral("(function)"); else if (reg == CallData::Context) diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h index 5338583164..ec81701160 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -51,7 +51,7 @@ // We mean it. // #include <private/qv4global_p.h> -#include <private/qv4runtime_p.h> +#include <private/qv4staticvalue_p.h> #include <private/qv4compileddata_p.h> // for CompiledData::CodeOffsetToLine used by the dumper #include <qendian.h> diff --git a/src/qml/jit/qv4baselinejit.cpp b/src/qml/jit/qv4baselinejit.cpp index f4807f1917..9dd2cdc43b 100644 --- a/src/qml/jit/qv4baselinejit.cpp +++ b/src/qml/jit/qv4baselinejit.cpp @@ -49,7 +49,7 @@ using namespace QV4::Moth; BaselineJIT::BaselineJIT(Function *function) : function(function) - , as(new BaselineAssembler(function->compilationUnit->constants)) + , as(new BaselineAssembler(&(function->compilationUnit->constants->asValue<Value>()))) {} BaselineJIT::~BaselineJIT() diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri index e6f1079aa7..74e893e570 100644 --- a/src/qml/jsruntime/jsruntime.pri +++ b/src/qml/jsruntime/jsruntime.pri @@ -58,7 +58,9 @@ SOURCES += \ $$PWD/qv4mapiterator.cpp \ $$PWD/qv4estable.cpp \ $$PWD/qv4module.cpp \ - $$PWD/qv4promiseobject.cpp + $$PWD/qv4promiseobject.cpp \ + $$PWD/qv4runtime.cpp \ + $$PWD/qv4value.cpp qtConfig(qml-debug): SOURCES += $$PWD/qv4profiling.cpp @@ -129,7 +131,9 @@ HEADERS += \ $$PWD/qv4estable_p.h \ $$PWD/qv4vtable_p.h \ $$PWD/qv4module_p.h \ - $$PWD/qv4promiseobject_p.h + $$PWD/qv4promiseobject_p.h \ + $$PWD/qv4runtime_p.h \ + $$PWD/qv4value_p.h qtConfig(qml-sequence-object) { HEADERS += \ @@ -144,17 +148,13 @@ qtConfig(qml-sequence-object) { HEADERS += \ $$PWD/qv4calldata_p.h \ - $$PWD/qv4runtime_p.h \ $$PWD/qv4runtimeapi_p.h \ - $$PWD/qv4value_p.h \ $$PWD/qv4stringtoarrayindex_p.h \ $$PWD/qv4util_p.h \ - $$PWD/qv4value_p.h \ - $$PWD/qv4functiontable_p.h + $$PWD/qv4functiontable_p.h \ + $$PWD/qv4staticvalue_p.h SOURCES += \ - $$PWD/qv4runtime.cpp \ - $$PWD/qv4value.cpp \ $$PWD/qv4executableallocator.cpp qmldevtools_build { diff --git a/src/qml/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp index b3e607d74a..af1a2d1de0 100644 --- a/src/qml/jsruntime/qv4arrayobject.cpp +++ b/src/qml/jsruntime/qv4arrayobject.cpp @@ -175,7 +175,8 @@ static ScopedObject createObjectFromCtorOrArray(Scope &scope, ScopedFunctionObje // this isn't completely kosher. for instance: // Array.from.call(Object, []).constructor == Object // is expected by the tests, but naturally, we get Number. - ScopedValue argument(scope, useLen ? QV4::Encode(len) : Value::undefinedValue()); + ScopedValue argument(scope, useLen ? Value::fromReturnedValue(QV4::Encode(len)) + : Value::undefinedValue()); a = ctor->callAsConstructor(argument, useLen ? 1 : 0); } else { a = scope.engine->newArrayObject(len); diff --git a/src/qml/jsruntime/qv4booleanobject.cpp b/src/qml/jsruntime/qv4booleanobject.cpp index 3e5f51c302..a9b4ecb607 100644 --- a/src/qml/jsruntime/qv4booleanobject.cpp +++ b/src/qml/jsruntime/qv4booleanobject.cpp @@ -103,7 +103,7 @@ ReturnedValue BooleanPrototype::method_toString(const FunctionObject *b, const V if (exception) return v4->throwTypeError(); - return Encode(result ? v4->id_true() : v4->id_false()); + return (result ? v4->id_true() : v4->id_false())->asReturnedValue(); } ReturnedValue BooleanPrototype::method_valueOf(const FunctionObject *b, const Value *thisObject, const Value *, int) diff --git a/src/qml/jsruntime/qv4calldata_p.h b/src/qml/jsruntime/qv4calldata_p.h index 8487872bf5..5a5280cb86 100644 --- a/src/qml/jsruntime/qv4calldata_p.h +++ b/src/qml/jsruntime/qv4calldata_p.h @@ -51,7 +51,7 @@ // We mean it. // -#include <private/qv4value_p.h> +#include <private/qv4staticvalue_p.h> QT_BEGIN_NAMESPACE @@ -71,12 +71,12 @@ struct CallData OffsetCount = LastOffset + 1 }; - Value function; - Value context; - Value accumulator; - Value thisObject; - Value newTarget; - Value _argc; + StaticValue function; + StaticValue context; + StaticValue accumulator; + StaticValue thisObject; + StaticValue newTarget; + StaticValue _argc; int argc() const { Q_ASSERT(_argc.isInteger()); @@ -89,22 +89,32 @@ struct CallData } inline ReturnedValue argument(int i) const { - return i < argc() ? args[i].asReturnedValue() : Value::undefinedValue().asReturnedValue(); + return i < argc() ? args[i].asReturnedValue() + : StaticValue::undefinedValue().asReturnedValue(); } - Value args[1]; + StaticValue args[1]; - static Q_DECL_CONSTEXPR int HeaderSize() { return offsetof(CallData, args) / sizeof(QV4::Value); } + static Q_DECL_CONSTEXPR int HeaderSize() + { + return offsetof(CallData, args) / sizeof(QV4::StaticValue); + } + + template<typename Value> + Value *argValues(); + + template<typename Value> + const Value *argValues() const; }; Q_STATIC_ASSERT(std::is_standard_layout<CallData>::value); -Q_STATIC_ASSERT(offsetof(CallData, function ) == CallData::Function * sizeof(Value)); -Q_STATIC_ASSERT(offsetof(CallData, context ) == CallData::Context * sizeof(Value)); -Q_STATIC_ASSERT(offsetof(CallData, accumulator) == CallData::Accumulator * sizeof(Value)); -Q_STATIC_ASSERT(offsetof(CallData, thisObject ) == CallData::This * sizeof(Value)); -Q_STATIC_ASSERT(offsetof(CallData, newTarget ) == CallData::NewTarget * sizeof(Value)); -Q_STATIC_ASSERT(offsetof(CallData, _argc ) == CallData::Argc * sizeof(Value)); -Q_STATIC_ASSERT(offsetof(CallData, args ) == 6 * sizeof(Value)); +Q_STATIC_ASSERT(offsetof(CallData, function ) == CallData::Function * sizeof(StaticValue)); +Q_STATIC_ASSERT(offsetof(CallData, context ) == CallData::Context * sizeof(StaticValue)); +Q_STATIC_ASSERT(offsetof(CallData, accumulator) == CallData::Accumulator * sizeof(StaticValue)); +Q_STATIC_ASSERT(offsetof(CallData, thisObject ) == CallData::This * sizeof(StaticValue)); +Q_STATIC_ASSERT(offsetof(CallData, newTarget ) == CallData::NewTarget * sizeof(StaticValue)); +Q_STATIC_ASSERT(offsetof(CallData, _argc ) == CallData::Argc * sizeof(StaticValue)); +Q_STATIC_ASSERT(offsetof(CallData, args ) == 6 * sizeof(StaticValue)); } // namespace QV4 diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp index 130727378d..018571e325 100644 --- a/src/qml/jsruntime/qv4context.cpp +++ b/src/qml/jsruntime/qv4context.cpp @@ -71,7 +71,8 @@ Heap::CallContext *ExecutionContext::newBlockContext(CppStackFrame *frame, int b Heap::ExecutionContext *outer = static_cast<Heap::ExecutionContext *>(frame->context()->m()); c->outer.set(v4, outer); - c->function.set(v4, static_cast<Heap::FunctionObject *>(frame->jsFrame->function.m())); + c->function.set(v4, static_cast<Heap::FunctionObject *>( + Value::fromStaticValue(frame->jsFrame->function).m())); c->locals.size = nLocals; c->locals.alloc = nLocals; @@ -108,7 +109,8 @@ Heap::CallContext *ExecutionContext::newCallContext(CppStackFrame *frame) c->init(); c->outer.set(v4, outer); - c->function.set(v4, static_cast<Heap::FunctionObject *>(frame->jsFrame->function.m())); + c->function.set(v4, static_cast<Heap::FunctionObject *>( + Value::fromStaticValue(frame->jsFrame->function).m())); const CompiledData::Function *compiledFunction = function->compiledFunction; uint nLocals = compiledFunction->nLocals; diff --git a/src/qml/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h index 42b6edb6e2..ebd21b3543 100644 --- a/src/qml/jsruntime/qv4global_p.h +++ b/src/qml/jsruntime/qv4global_p.h @@ -63,10 +63,6 @@ #include <qtqmlglobal.h> #include <private/qtqmlglobal_p.h> -#ifdef QT_QML_BOOTSTRAPPED -#define V4_BOOTSTRAP -#endif - #if defined(Q_CC_MSVC) #include <float.h> #include <math.h> diff --git a/src/qml/jsruntime/qv4math_p.h b/src/qml/jsruntime/qv4math_p.h index 90246c4229..bca4c2ef66 100644 --- a/src/qml/jsruntime/qv4math_p.h +++ b/src/qml/jsruntime/qv4math_p.h @@ -52,6 +52,7 @@ #include <qglobal.h> +#include <QtQml/private/qv4staticvalue_p.h> #include <QtCore/qnumeric.h> #include <QtCore/private/qnumeric_p.h> #include <cmath> @@ -70,24 +71,24 @@ static inline QMLJS_READONLY ReturnedValue add_int32(int a, int b) { int result; if (Q_UNLIKELY(add_overflow(a, b, &result))) - return Value::fromDouble(static_cast<double>(a) + b).asReturnedValue(); - return Value::fromInt32(result).asReturnedValue(); + return StaticValue::fromDouble(static_cast<double>(a) + b).asReturnedValue(); + return StaticValue::fromInt32(result).asReturnedValue(); } static inline QMLJS_READONLY ReturnedValue sub_int32(int a, int b) { int result; if (Q_UNLIKELY(sub_overflow(a, b, &result))) - return Value::fromDouble(static_cast<double>(a) - b).asReturnedValue(); - return Value::fromInt32(result).asReturnedValue(); + return StaticValue::fromDouble(static_cast<double>(a) - b).asReturnedValue(); + return StaticValue::fromInt32(result).asReturnedValue(); } static inline QMLJS_READONLY ReturnedValue mul_int32(int a, int b) { int result; if (Q_UNLIKELY(mul_overflow(a, b, &result))) - return Value::fromDouble(static_cast<double>(a) * b).asReturnedValue(); - return Value::fromInt32(result).asReturnedValue(); + return StaticValue::fromDouble(static_cast<double>(a) * b).asReturnedValue(); + return StaticValue::fromInt32(result).asReturnedValue(); } } diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 095f27279f..8b7de89d5b 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -1271,7 +1271,8 @@ static QV4::ReturnedValue CallMethod(const QQmlObjectOrGadget &object, int index QVarLengthArray<CallArgument, 9> args(argCount + 1); args[0].initAsType(returnType); for (int ii = 0; ii < argCount; ++ii) { - if (!args[ii + 1].fromValue(argTypes[ii], engine, callArgs->args[ii])) { + if (!args[ii + 1].fromValue(argTypes[ii], engine, + callArgs->args[ii].asValue<QV4::Value>())) { qWarning() << QString::fromLatin1("Could not convert argument %1 at").arg(ii); const StackTrace stack = engine->stackTrace(); for (const StackFrame &frame : stack) { @@ -1616,8 +1617,10 @@ static QV4::ReturnedValue CallOverloaded(const QQmlObjectOrGadget &object, const continue; // We already have a better option int methodMatchScore = 0; - for (int ii = 0; ii < methodArgumentCount; ++ii) - methodMatchScore += MatchScore((v = callArgs->args[ii]), methodArgTypes[ii]); + for (int ii = 0; ii < methodArgumentCount; ++ii) { + methodMatchScore += MatchScore((v = Value::fromStaticValue(callArgs->args[ii])), + methodArgTypes[ii]); + } if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) { best = *attempt; @@ -2253,8 +2256,10 @@ ReturnedValue QMetaObjectWrapper::callOverloadedConstructor(QV4::ExecutionEngine continue; // We already have a better option int methodMatchScore = 0; - for (int ii = 0; ii < methodArgumentCount; ++ii) - methodMatchScore += MatchScore((v = callArgs->args[ii]), methodArgTypes[ii]); + for (int ii = 0; ii < methodArgumentCount; ++ii) { + methodMatchScore += MatchScore((v = Value::fromStaticValue(callArgs->args[ii])), + methodArgTypes[ii]); + } if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) { best = attempt; diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 478114a38a..38cce2a7a9 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -39,7 +39,6 @@ #include "qv4global_p.h" #include "qv4runtime_p.h" -#ifndef V4_BOOTSTRAP #include "qv4engine_p.h" #include "qv4object_p.h" #include "qv4objectproto_p.h" @@ -63,7 +62,6 @@ #include "qv4qobjectwrapper_p.h" #include "qv4symbol_p.h" #include "qv4generatorobject_p.h" -#endif #include <QtCore/QDebug> #include <cassert> @@ -222,8 +220,6 @@ void RuntimeCounters::count(const char *func, uint tag1, uint tag2) #endif // QV4_COUNT_RUNTIME_FUNCTIONS -#ifndef V4_BOOTSTRAP - static QV4::Lookup *runtimeLookup(Function *f, uint i) { return f->executableCompilationUnit()->runtimeLookups + i; @@ -1026,7 +1022,8 @@ static Object *getSuperBase(Scope &scope) return nullptr; } - ScopedFunctionObject f(scope, scope.engine->currentStackFrame->jsFrame->function); + ScopedFunctionObject f( + scope, Value::fromStaticValue(scope.engine->currentStackFrame->jsFrame->function)); ScopedObject homeObject(scope, f->getHomeObject()); if (!homeObject) { ScopedContext ctx(scope, static_cast<ExecutionContext *>(&scope.engine->currentStackFrame->jsFrame->context)); @@ -1064,7 +1061,8 @@ ReturnedValue Runtime::LoadSuperProperty::call(ExecutionEngine *engine, const Va ScopedPropertyKey key(scope, property.toPropertyKey(engine)); if (engine->hasException) return Encode::undefined(); - return base->get(key, &engine->currentStackFrame->jsFrame->thisObject); + return base->get( + key, &(engine->currentStackFrame->jsFrame->thisObject.asValue<Value>())); } void Runtime::StoreSuperProperty::call(ExecutionEngine *engine, const Value &property, const Value &value) @@ -1076,7 +1074,8 @@ void Runtime::StoreSuperProperty::call(ExecutionEngine *engine, const Value &pro ScopedPropertyKey key(scope, property.toPropertyKey(engine)); if (engine->hasException) return; - bool result = base->put(key, value, &engine->currentStackFrame->jsFrame->thisObject); + bool result = base->put( + key, value, &(engine->currentStackFrame->jsFrame->thisObject.asValue<Value>())); if (!result && engine->currentStackFrame->v4Function->isStrict()) engine->throwTypeError(); } @@ -1128,8 +1127,6 @@ ReturnedValue Runtime::LoadSuperConstructor::call(ExecutionEngine *engine, const return c->asReturnedValue(); } -#endif // V4_BOOTSTRAP - uint RuntimeHelpers::equalHelper(const Value &x, const Value &y) { Q_ASSERT(x.type() != y.type() || (x.isManaged() && (x.isString() != y.isString()))); @@ -1151,9 +1148,6 @@ uint RuntimeHelpers::equalHelper(const Value &x, const Value &y) } else if (y.isBoolean()) { return Runtime::CompareEqual::call(x, Value::fromDouble((double) y.booleanValue())); } else { -#ifdef V4_BOOTSTRAP - Q_UNIMPLEMENTED(); -#else Object *xo = x.objectValue(); Object *yo = y.objectValue(); if (yo && (x.isNumber() || x.isString())) { @@ -1165,7 +1159,6 @@ uint RuntimeHelpers::equalHelper(const Value &x, const Value &y) ScopedValue px(scope, RuntimeHelpers::objectDefaultValue(xo, PREFERREDTYPE_HINT)); return Runtime::CompareEqual::call(px, y); } -#endif } return false; @@ -1182,12 +1175,7 @@ Bool RuntimeHelpers::strictEqual(const Value &x, const Value &y) if (x.isNumber()) return y.isNumber() && x.asDouble() == y.asDouble(); if (x.isManaged()) { -#ifdef V4_BOOTSTRAP - Q_UNIMPLEMENTED(); - return false; -#else return y.isManaged() && x.cast<Managed>()->isEqualTo(y.cast<Managed>()); -#endif } return false; } @@ -1202,26 +1190,17 @@ QV4::Bool Runtime::CompareGreaterThan::call(const Value &l, const Value &r) String *sl = l.stringValue(); String *sr = r.stringValue(); if (sl && sr) { -#ifdef V4_BOOTSTRAP - Q_UNIMPLEMENTED(); - return false; -#else return sr->lessThan(sl); -#endif } Object *ro = r.objectValue(); Object *lo = l.objectValue(); if (ro || lo) { -#ifdef V4_BOOTSTRAP - Q_UNIMPLEMENTED(); -#else QV4::ExecutionEngine *e = (lo ? lo : ro)->engine(); QV4::Scope scope(e); QV4::ScopedValue pl(scope, lo ? RuntimeHelpers::objectDefaultValue(lo, QV4::NUMBER_HINT) : l.asReturnedValue()); QV4::ScopedValue pr(scope, ro ? RuntimeHelpers::objectDefaultValue(ro, QV4::NUMBER_HINT) : r.asReturnedValue()); return Runtime::CompareGreaterThan::call(pl, pr); -#endif } double dl = RuntimeHelpers::toNumber(l); @@ -1239,26 +1218,17 @@ QV4::Bool Runtime::CompareLessThan::call(const Value &l, const Value &r) String *sl = l.stringValue(); String *sr = r.stringValue(); if (sl && sr) { -#ifdef V4_BOOTSTRAP - Q_UNIMPLEMENTED(); - return false; -#else return sl->lessThan(sr); -#endif } Object *ro = r.objectValue(); Object *lo = l.objectValue(); if (ro || lo) { -#ifdef V4_BOOTSTRAP - Q_UNIMPLEMENTED(); -#else QV4::ExecutionEngine *e = (lo ? lo : ro)->engine(); QV4::Scope scope(e); QV4::ScopedValue pl(scope, lo ? RuntimeHelpers::objectDefaultValue(lo, QV4::NUMBER_HINT) : l.asReturnedValue()); QV4::ScopedValue pr(scope, ro ? RuntimeHelpers::objectDefaultValue(ro, QV4::NUMBER_HINT) : r.asReturnedValue()); return Runtime::CompareLessThan::call(pl, pr); -#endif } double dl = RuntimeHelpers::toNumber(l); @@ -1276,26 +1246,17 @@ QV4::Bool Runtime::CompareGreaterEqual::call(const Value &l, const Value &r) String *sl = l.stringValue(); String *sr = r.stringValue(); if (sl && sr) { -#ifdef V4_BOOTSTRAP - Q_UNIMPLEMENTED(); - return false; -#else return !sl->lessThan(sr); -#endif } Object *ro = r.objectValue(); Object *lo = l.objectValue(); if (ro || lo) { -#ifdef V4_BOOTSTRAP - Q_UNIMPLEMENTED(); -#else QV4::ExecutionEngine *e = (lo ? lo : ro)->engine(); QV4::Scope scope(e); QV4::ScopedValue pl(scope, lo ? RuntimeHelpers::objectDefaultValue(lo, QV4::NUMBER_HINT) : l.asReturnedValue()); QV4::ScopedValue pr(scope, ro ? RuntimeHelpers::objectDefaultValue(ro, QV4::NUMBER_HINT) : r.asReturnedValue()); return Runtime::CompareGreaterEqual::call(pl, pr); -#endif } double dl = RuntimeHelpers::toNumber(l); @@ -1313,26 +1274,17 @@ QV4::Bool Runtime::CompareLessEqual::call(const Value &l, const Value &r) String *sl = l.stringValue(); String *sr = r.stringValue(); if (sl && sr) { -#ifdef V4_BOOTSTRAP - Q_UNIMPLEMENTED(); - return false; -#else return !sr->lessThan(sl); -#endif } Object *ro = r.objectValue(); Object *lo = l.objectValue(); if (ro || lo) { -#ifdef V4_BOOTSTRAP - Q_UNIMPLEMENTED(); -#else QV4::ExecutionEngine *e = (lo ? lo : ro)->engine(); QV4::Scope scope(e); QV4::ScopedValue pl(scope, lo ? RuntimeHelpers::objectDefaultValue(lo, QV4::NUMBER_HINT) : l.asReturnedValue()); QV4::ScopedValue pr(scope, ro ? RuntimeHelpers::objectDefaultValue(ro, QV4::NUMBER_HINT) : r.asReturnedValue()); return Runtime::CompareLessEqual::call(pl, pr); -#endif } double dl = RuntimeHelpers::toNumber(l); @@ -1340,7 +1292,6 @@ QV4::Bool Runtime::CompareLessEqual::call(const Value &l, const Value &r) return dl <= dr; } -#ifndef V4_BOOTSTRAP Bool Runtime::CompareInstanceof::call(ExecutionEngine *engine, const Value &left, const Value &right) { TRACE2(left, right); @@ -1623,7 +1574,8 @@ ReturnedValue Runtime::TailCall::call(CppStackFrame *frame, ExecutionEngine *eng } memcpy(frame->jsFrame->args, argv, argc * sizeof(Value)); - frame->init(engine, fo.function(), frame->jsFrame->args, argc, frame->callerCanHandleTailCall); + frame->init(engine, fo.function(), frame->jsFrame->argValues<Value>(), argc, + frame->callerCanHandleTailCall); frame->setupJSFrame(frame->savedStackTop, fo, fo.scope(), thisObject, Primitive::undefinedValue()); engine->jsStackTop = frame->savedStackTop + frame->requiredJSStackFrameSize(); frame->pendingTailCall = true; @@ -1685,12 +1637,12 @@ void Runtime::PushCallContext::call(CppStackFrame *frame) ReturnedValue Runtime::PushWithContext::call(ExecutionEngine *engine, const Value &acc) { CallData *jsFrame = engine->currentStackFrame->jsFrame; - Value &newAcc = jsFrame->accumulator; + Value &newAcc = jsFrame->accumulator.asValue<Value>(); newAcc = Value::fromHeapObject(acc.toObject(engine)); if (!engine->hasException) { Q_ASSERT(newAcc.isObject()); const Object &obj = static_cast<const Object &>(newAcc); - Value &context = jsFrame->context; + Value &context = jsFrame->context.asValue<Value>(); auto ec = static_cast<const ExecutionContext *>(&context); context = ec->newWithContext(obj.d())->asReturnedValue(); } @@ -1711,7 +1663,8 @@ void Runtime::PushBlockContext::call(ExecutionEngine *engine, int index) void Runtime::CloneBlockContext::call(ExecutionEngine *engine) { auto frame = engine->currentStackFrame; - auto context = static_cast<Heap::CallContext *>(frame->jsFrame->context.m()); + auto context = static_cast<Heap::CallContext *>( + Value::fromStaticValue(frame->jsFrame->context).m()); frame->jsFrame->context = ExecutionContext::cloneBlockContext(engine, context)->asReturnedValue(); } @@ -1971,7 +1924,9 @@ QV4::ReturnedValue Runtime::CreateRestParameter::call(ExecutionEngine *engine, i ReturnedValue Runtime::RegexpLiteral::call(ExecutionEngine *engine, int id) { - Heap::RegExpObject *ro = engine->newRegExpObject(engine->currentStackFrame->v4Function->compilationUnit->runtimeRegularExpressions[id].as<RegExp>()); + const auto val + = engine->currentStackFrame->v4Function->compilationUnit->runtimeRegularExpressions[id]; + Heap::RegExpObject *ro = engine->newRegExpObject(Value::fromStaticValue(val).as<RegExp>()); return ro->asReturnedValue(); } @@ -1992,7 +1947,6 @@ ReturnedValue Runtime::ToNumber::call(ExecutionEngine *, const Value &v) { return Encode(v.toNumber()); } -#endif // V4_BOOTSTRAP ReturnedValue Runtime::UMinus::call(const Value &value) { @@ -2010,7 +1964,6 @@ ReturnedValue Runtime::UMinus::call(const Value &value) // binary operators -#ifndef V4_BOOTSTRAP ReturnedValue Runtime::Add::call(ExecutionEngine *engine, const Value &left, const Value &right) { TRACE2(left, right); @@ -2156,8 +2109,6 @@ ReturnedValue Runtime::UShr::call(const Value &left, const Value &right) return Encode(res); } -#endif // V4_BOOTSTRAP - ReturnedValue Runtime::GreaterThan::call(const Value &left, const Value &right) { TRACE2(left, right); @@ -2190,7 +2141,6 @@ ReturnedValue Runtime::LessEqual::call(const Value &left, const Value &right) return Encode(r); } -#ifndef V4_BOOTSTRAP struct LazyScope { ExecutionEngine *engine = nullptr; @@ -2210,7 +2160,6 @@ struct LazyScope **scopedValue = value; } }; -#endif Bool Runtime::CompareEqual::call(const Value &left, const Value &right) { @@ -2219,11 +2168,9 @@ Bool Runtime::CompareEqual::call(const Value &left, const Value &right) Value lhs = left; Value rhs = right; -#ifndef V4_BOOTSTRAP LazyScope scope; Value *lhsGuard = nullptr; Value *rhsGuard = nullptr; -#endif redo: if (lhs.asReturnedValue() == rhs.asReturnedValue()) @@ -2253,7 +2200,6 @@ Bool Runtime::CompareEqual::call(const Value &left, const Value &right) case QV4::Value::QT_ManagedOrUndefined1: case QV4::Value::QT_ManagedOrUndefined2: case QV4::Value::QT_ManagedOrUndefined3: { -#ifndef V4_BOOTSTRAP // RHS: Managed Heap::Base *l = lhs.m(); Heap::Base *r = rhs.m(); @@ -2271,7 +2217,6 @@ Bool Runtime::CompareEqual::call(const Value &left, const Value &right) lhs = lhsGuard->asReturnedValue(); break; } -#endif return false; } case QV4::Value::QT_Empty: @@ -2283,16 +2228,12 @@ Bool Runtime::CompareEqual::call(const Value &left, const Value &right) rhs = Value::fromDouble(rhs.int_32()); // fall through default: // double -#ifndef V4_BOOTSTRAP if (lhs.m()->internalClass->vtable->isStringOrSymbol) { return lhs.m()->internalClass->vtable->isString ? (RuntimeHelpers::toNumber(lhs) == rhs.doubleValue()) : false; } else { scope.set(&lhsGuard, RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(lhs), PREFERREDTYPE_HINT), lhs.m()->internalClass->engine); lhs = lhsGuard->asReturnedValue(); } -#else - Q_UNIMPLEMENTED(); -#endif } goto redo; case QV4::Value::QT_Empty: diff --git a/src/qml/jsruntime/qv4runtime_p.h b/src/qml/jsruntime/qv4runtime_p.h index 349099f8d5..cb89b7e6c0 100644 --- a/src/qml/jsruntime/qv4runtime_p.h +++ b/src/qml/jsruntime/qv4runtime_p.h @@ -116,14 +116,12 @@ struct Q_QML_PRIVATE_EXPORT RuntimeHelpers { // type conversion and testing -#ifndef V4_BOOTSTRAP inline ReturnedValue RuntimeHelpers::toPrimitive(const Value &value, TypeHint typeHint) { if (!value.isObject()) return value.asReturnedValue(); return RuntimeHelpers::objectDefaultValue(&reinterpret_cast<const Object &>(value), typeHint); } -#endif inline double RuntimeHelpers::toNumber(const Value &value) { diff --git a/src/qml/jsruntime/qv4staticvalue_p.h b/src/qml/jsruntime/qv4staticvalue_p.h new file mode 100644 index 0000000000..c6b4bdb158 --- /dev/null +++ b/src/qml/jsruntime/qv4staticvalue_p.h @@ -0,0 +1,548 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4STATICVALUE_P_H +#define QV4STATICVALUE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQml/private/qtqmlglobal_p.h> +#include <QtQml/private/qv4global_p.h> +#include <QtCore/private/qnumeric_p.h> + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct Double { + quint64 d; + + Double(double dbl) { + memcpy(&d, &dbl, sizeof(double)); + } + + int sign() const { + return (d >> 63) ? -1 : 1; + } + + bool isDenormal() const { + return static_cast<int>((d << 1) >> 53) == 0; + } + + int exponent() const { + return static_cast<int>((d << 1) >> 53) - 1023; + } + + quint64 significant() const { + quint64 m = (d << 12) >> 12; + if (!isDenormal()) + m |= (static_cast<quint64>(1) << 52); + return m; + } + + static int toInt32(double d) { + int i = static_cast<int>(d); + if (i == d) + return i; + return Double(d).toInt32(); + } + + int toInt32() { + int e = exponent() - 52; + if (e < 0) { + if (e <= -53) + return 0; + return sign() * static_cast<int>(significant() >> -e); + } else { + if (e > 31) + return 0; + return sign() * (static_cast<int>(significant()) << e); + } + } +}; + +struct Q_QML_PRIVATE_EXPORT StaticValue +{ + StaticValue() = default; + constexpr StaticValue(quint64 val) : _val(val) {} + + StaticValue &operator=(ReturnedValue v) + { + _val = v; + return *this; + } + + template<typename Value> + StaticValue &operator=(const Value &); + + template<typename Value> + const Value &asValue() const; + + template<typename Value> + Value &asValue(); + + /* + We use 8 bytes for a value and a different variant of NaN boxing. A Double + NaN (actually -qNaN) is indicated by a number that has the top 13 bits set, and for a + signalling NaN it is the top 14 bits. The other values are usually set to 0 by the + processor, and are thus free for us to store other data. We keep pointers in there for + managed objects, and encode the other types using the free space given to use by the unused + bits for NaN values. This also works for pointers on 64 bit systems, as they all currently + only have 48 bits of addressable memory. (Note: we do leave the lower 49 bits available for + pointers.) + + We xor Doubles with (0xffff8000 << 32). That has the effect that no doubles will + get encoded with bits 63-49 all set to 0. We then use bit 48 to distinguish between + managed/undefined (0), or Null/Int/Bool/Empty (1). So, storing a 49 bit pointer will leave + the top 15 bits 0, which is exactly the 'natural' representation of pointers. If bit 49 is + set, bit 48 indicates Empty (0) or integer-convertible (1). Then the 3 bit below that are + used to encode Null/Int/Bool. + + Undefined is encoded as a managed pointer with value 0. This is the same as a nullptr. + + Specific bit-sequences: + 0 = always 0 + 1 = always 1 + x = stored value + a,b,c,d = specific bit values, see notes + + 32109876 54321098 76543210 98765432 10987654 32109876 54321098 76543210 | + 66665555 55555544 44444444 33333333 33222222 22221111 11111100 00000000 | JS Value + ------------------------------------------------------------------------+-------------- + 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 | Undefined + 00000000 0000000x xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | Managed (heap pointer) + a0000000 0000bc00 00000000 00000000 00000000 00000000 00000000 00000000 | NaN/Inf + dddddddd ddddddxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | double + 00000000 00000010 00000000 00000000 00000000 00000000 00000000 00000000 | empty (non-sparse array hole) + 00000000 00000010 10000000 00000000 00000000 00000000 00000000 00000000 | Null + 00000000 00000011 00000000 00000000 00000000 00000000 00000000 0000000x | Bool + 00000000 00000011 10000000 00000000 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | Int + + Notes: + - a: xor-ed signbit, always 1 for NaN + - bc, xor-ed values: 11 = inf, 10 = sNaN, 01 = qNaN, 00 = boxed value + - d: xor-ed bits, where at least one bit is set, so: (val >> (64-14)) > 0 + - Undefined maps to C++ nullptr, so the "default" initialization is the same for both C++ + and JS + - Managed has the left 15 bits set to 0, so: (val >> (64-15)) == 0 + - empty, Null, Bool, and Int have the left 14 bits set to 0, and bit 49 set to 1, + so: (val >> (64-15)) == 1 + - Null, Bool, and Int have bit 48 set, indicating integer-convertible + - xoring _val with NaNEncodeMask will convert to a double in "natural" representation, where + any non double results in a NaN + - on 32bit we can use the fact that addresses are 32bits wide, so the tag part (bits 32 to + 63) are zero. No need to shift. + */ + + quint64 _val; + + QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR quint64 &rawValueRef() { return _val; } + QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR quint64 rawValue() const { return _val; } + QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setRawValue(quint64 raw) { _val = raw; } + +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN + static inline int valueOffset() { return 0; } + static inline int tagOffset() { return 4; } +#else // !Q_LITTLE_ENDIAN + static inline int valueOffset() { return 4; } + static inline int tagOffset() { return 0; } +#endif + static inline constexpr quint64 tagValue(quint32 tag, quint32 value) { return quint64(tag) << 32 | value; } + QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setTagValue(quint32 tag, quint32 value) { _val = quint64(tag) << 32 | value; } + QML_NEARLY_ALWAYS_INLINE constexpr quint32 value() const { return _val & quint64(~quint32(0)); } + QML_NEARLY_ALWAYS_INLINE constexpr quint32 tag() const { return _val >> 32; } + QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setTag(quint32 tag) { setTagValue(tag, value()); } + + QML_NEARLY_ALWAYS_INLINE constexpr int int_32() const + { + return int(value()); + } + QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setInt_32(int i) + { + setTagValue(quint32(ValueTypeInternal::Integer), quint32(i)); + } + QML_NEARLY_ALWAYS_INLINE uint uint_32() const { return value(); } + + QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setEmpty() + { + setTagValue(quint32(ValueTypeInternal::Empty), 0); + } + + // ### Fix for 32 bit (easiest solution is to set highest bit to 1 for mananged/undefined/integercompatible + // and use negative numbers here + enum QuickType { + QT_ManagedOrUndefined = 0, + QT_ManagedOrUndefined1 = 1, + QT_ManagedOrUndefined2 = 2, + QT_ManagedOrUndefined3 = 3, + QT_Empty = 4, + QT_Null = 5, + QT_Bool = 6, + QT_Int = 7 + // all other values are doubles + }; + + enum Type { + Undefined_Type = 0, + Managed_Type = 1, + Empty_Type = 4, + Null_Type = 5, + Boolean_Type = 6, + Integer_Type = 7, + Double_Type = 8 + }; + + inline Type type() const { + int t = quickType(); + if (t < QT_Empty) + return _val ? Managed_Type : Undefined_Type; + if (t > QT_Int) + return Double_Type; + return static_cast<Type>(t); + } + + // Shared between 32-bit and 64-bit encoding + enum { + Tag_Shift = 32 + }; + + // Used only by 64-bit encoding + static const quint64 NaNEncodeMask = 0xfffc000000000000ull; + enum { + IsDouble_Shift = 64-14, + IsManagedOrUndefined_Shift = 64-15, + IsIntegerConvertible_Shift = 64-15, + IsIntegerOrBool_Shift = 64-16, + QuickType_Shift = 64 - 17, + IsPositiveIntShift = 31 + }; + + static const quint64 Immediate_Mask_64 = 0x00020000u; // bit 49 + + enum class ValueTypeInternal_64 { + Empty = Immediate_Mask_64 | 0, + Null = Immediate_Mask_64 | 0x08000u, + Boolean = Immediate_Mask_64 | 0x10000u, + Integer = Immediate_Mask_64 | 0x18000u + }; + + // Used only by 32-bit encoding + enum Masks { + SilentNaNBit = 0x00040000, + NotDouble_Mask = 0x7ffa0000, + }; + static const quint64 Immediate_Mask_32 = NotDouble_Mask | 0x00020000u | SilentNaNBit; + + enum class ValueTypeInternal_32 { + Empty = Immediate_Mask_32 | 0, + Null = Immediate_Mask_32 | 0x08000u, + Boolean = Immediate_Mask_32 | 0x10000u, + Integer = Immediate_Mask_32 | 0x18000u + }; + + enum { + Managed_Type_Internal = 0 + }; + + using ValueTypeInternal = ValueTypeInternal_64; + + enum { + NaN_Mask = 0x7ff80000, + }; + + inline quint64 quickType() const { return (_val >> QuickType_Shift); } + + // used internally in property + inline bool isEmpty() const { return tag() == quint32(ValueTypeInternal::Empty); } + inline bool isNull() const { return tag() == quint32(ValueTypeInternal::Null); } + inline bool isBoolean() const { return tag() == quint32(ValueTypeInternal::Boolean); } + inline bool isInteger() const { return tag() == quint32(ValueTypeInternal::Integer); } + inline bool isNullOrUndefined() const { return isNull() || isUndefined(); } + inline bool isNumber() const { return quickType() >= QT_Int; } + + inline bool isUndefined() const { return _val == 0; } + inline bool isDouble() const { return (_val >> IsDouble_Shift); } + inline bool isManaged() const + { +#if QT_POINTER_SIZE == 4 + return value() && tag() == Managed_Type_Internal; +#else + return _val && ((_val >> IsManagedOrUndefined_Shift) == 0); +#endif + } + inline bool isManagedOrUndefined() const + { +#if QT_POINTER_SIZE == 4 + return tag() == Managed_Type_Internal; +#else + return ((_val >> IsManagedOrUndefined_Shift) == 0); +#endif + } + + inline bool isIntOrBool() const { + return (_val >> IsIntegerOrBool_Shift) == 3; + } + + inline bool integerCompatible() const { + Q_ASSERT(!isEmpty()); + return (_val >> IsIntegerConvertible_Shift) == 1; + } + + static inline bool integerCompatible(StaticValue a, StaticValue b) { + return a.integerCompatible() && b.integerCompatible(); + } + + static inline bool bothDouble(StaticValue a, StaticValue b) { + return a.isDouble() && b.isDouble(); + } + + inline bool isNaN() const + { + return (tag() & 0x7ffc0000 ) == 0x00040000; + } + + inline bool isPositiveInt() const { +#if QT_POINTER_SIZE == 4 + return isInteger() && int_32() >= 0; +#else + return (_val >> IsPositiveIntShift) == (quint64(ValueTypeInternal::Integer) << 1); +#endif + } + + QML_NEARLY_ALWAYS_INLINE double doubleValue() const { + Q_ASSERT(isDouble()); + double d; + StaticValue v = *this; + v._val ^= NaNEncodeMask; + memcpy(&d, &v._val, 8); + return d; + } + + QML_NEARLY_ALWAYS_INLINE void setDouble(double d) { + if (qt_is_nan(d)) + d = qt_qnan(); + memcpy(&_val, &d, 8); + _val ^= NaNEncodeMask; + Q_ASSERT(isDouble()); + } + + inline bool isInt32() { + if (tag() == quint32(ValueTypeInternal::Integer)) + return true; + if (isDouble()) { + double d = doubleValue(); + if (isInt32(d)) { + setInt_32(int(d)); + return true; + } + } + return false; + } + + QML_NEARLY_ALWAYS_INLINE static bool isInt32(double d) { + int i = int(d); + return (i == d && !(d == 0 && std::signbit(d))); + } + + double asDouble() const { + if (tag() == quint32(ValueTypeInternal::Integer)) + return int_32(); + return doubleValue(); + } + + bool booleanValue() const { + return int_32(); + } + + int integerValue() const { + return int_32(); + } + + inline bool tryIntegerConversion() { + bool b = integerCompatible(); + if (b) + setTagValue(quint32(ValueTypeInternal::Integer), value()); + return b; + } + + bool toBoolean() const { + if (integerCompatible()) + return static_cast<bool>(int_32()); + + if (isManagedOrUndefined()) + return false; + + // double + const double d = doubleValue(); + return d && !std::isnan(d); + } + + inline int toInt32() const + { + switch (type()) { + case Null_Type: + case Boolean_Type: + case Integer_Type: + return int_32(); + case Double_Type: + return Double::toInt32(doubleValue()); + case Empty_Type: + case Undefined_Type: + case Managed_Type: + break; + } + return Double::toInt32(std::numeric_limits<double>::quiet_NaN()); + } + + ReturnedValue *data_ptr() { return &_val; } + constexpr ReturnedValue asReturnedValue() const { return _val; } + constexpr static StaticValue fromReturnedValue(ReturnedValue val) { return {val}; } + + inline static constexpr StaticValue emptyValue() { return { tagValue(quint32(ValueTypeInternal::Empty), 0) }; } + static inline constexpr StaticValue fromBoolean(bool b) { return { tagValue(quint32(ValueTypeInternal::Boolean), b) }; } + static inline constexpr StaticValue fromInt32(int i) { return { tagValue(quint32(ValueTypeInternal::Integer), quint32(i)) }; } + inline static constexpr StaticValue undefinedValue() { return { 0 }; } + static inline constexpr StaticValue nullValue() { return { tagValue(quint32(ValueTypeInternal::Null), 0) }; } + + static inline StaticValue fromDouble(double d) + { + StaticValue v; + v.setDouble(d); + return v; + } + + static inline StaticValue fromUInt32(uint i) + { + StaticValue v; + if (i < uint(std::numeric_limits<int>::max())) { + v.setTagValue(quint32(ValueTypeInternal::Integer), i); + } else { + v.setDouble(i); + } + return v; + } + + static double toInteger(double d) + { + if (std::isnan(d)) + return +0; + if (!d || std::isinf(d)) + return d; + return d >= 0 ? std::floor(d) : std::ceil(d); + } + + static int toInt32(double d) + { + return Double::toInt32(d); + } + + static unsigned int toUInt32(double d) + { + return static_cast<uint>(toInt32(d)); + } +}; +Q_STATIC_ASSERT(std::is_trivial<StaticValue>::value); + +struct Encode { + static constexpr ReturnedValue undefined() { + return StaticValue::undefinedValue().asReturnedValue(); + } + static constexpr ReturnedValue null() { + return StaticValue::nullValue().asReturnedValue(); + } + + explicit constexpr Encode(bool b) + : val(StaticValue::fromBoolean(b).asReturnedValue()) + { + } + explicit Encode(double d) { + val = StaticValue::fromDouble(d).asReturnedValue(); + } + explicit constexpr Encode(int i) + : val(StaticValue::fromInt32(i).asReturnedValue()) + { + } + explicit Encode(uint i) { + val = StaticValue::fromUInt32(i).asReturnedValue(); + } + explicit constexpr Encode(ReturnedValue v) + : val(v) + { + } + constexpr Encode(StaticValue v) + : val(v.asReturnedValue()) + { + } + + template<typename HeapBase> + explicit Encode(HeapBase *o); + + explicit Encode(StaticValue *o) { + Q_ASSERT(o); + val = o->asReturnedValue(); + } + + static ReturnedValue smallestNumber(double d) { + if (StaticValue::isInt32(d)) + return Encode(static_cast<int>(d)); + else + return Encode(d); + } + + constexpr operator ReturnedValue() const { + return val; + } + quint64 val; +private: + explicit Encode(void *); +}; + +} + +QT_END_NAMESPACE + +#endif // QV4STATICVALUE_P_H diff --git a/src/qml/jsruntime/qv4value.cpp b/src/qml/jsruntime/qv4value.cpp index 82cf11f148..d29b060b9e 100644 --- a/src/qml/jsruntime/qv4value.cpp +++ b/src/qml/jsruntime/qv4value.cpp @@ -39,13 +39,11 @@ #include <qv4runtime_p.h> #include <qv4propertykey_p.h> -#ifndef V4_BOOTSTRAP #include <qv4string_p.h> #include <qv4symbol_p.h> #include <qv4object_p.h> #include <qv4objectproto_p.h> #include <private/qv4mm_p.h> -#endif #include <wtf/MathExtras.h> @@ -83,12 +81,8 @@ bool Value::toBooleanImpl(Value val) Heap::Base *b = val.m(); if (!b) return false; -#ifdef V4_BOOTSTRAP - Q_UNIMPLEMENTED(); -#else if (b->internalClass->vtable->isString) return static_cast<Heap::String *>(b)->length() > 0; -#endif return true; } @@ -103,10 +97,6 @@ double Value::toNumberImpl(Value val) case QV4::Value::Undefined_Type: return std::numeric_limits<double>::quiet_NaN(); case QV4::Value::Managed_Type: -#ifdef V4_BOOTSTRAP - Q_UNIMPLEMENTED(); - Q_FALLTHROUGH(); -#else if (String *s = val.stringValue()) return RuntimeHelpers::stringToNumber(s->toQString()); if (val.isSymbol()) { @@ -114,16 +104,15 @@ double Value::toNumberImpl(Value val) m.engine()->throwTypeError(); return 0; } - { - Q_ASSERT(val.isObject()); - Scope scope(val.objectValue()->engine()); - ScopedValue protectThis(scope, val); - ScopedValue prim(scope, RuntimeHelpers::toPrimitive(val, NUMBER_HINT)); + { + Q_ASSERT(val.isObject()); + Scope scope(val.objectValue()->engine()); + ScopedValue protectThis(scope, val); + ScopedValue prim(scope, RuntimeHelpers::toPrimitive(val, NUMBER_HINT)); if (scope.engine->hasException) return 0; return prim->toNumber(); } -#endif case QV4::Value::Null_Type: case QV4::Value::Boolean_Type: case QV4::Value::Integer_Type: @@ -133,7 +122,6 @@ double Value::toNumberImpl(Value val) } } -#ifndef V4_BOOTSTRAP QString Value::toQStringNoThrow() const { switch (type()) { @@ -325,4 +313,3 @@ uint Value::asArrayLength(bool *ok) const } return idx; } -#endif // V4_BOOTSTRAP diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h index da08841026..a9c8ac66bd 100644 --- a/src/qml/jsruntime/qv4value_p.h +++ b/src/qml/jsruntime/qv4value_p.h @@ -57,8 +57,10 @@ #include "qv4global_p.h" #include <private/qv4heap_p.h> #include <private/qv4internalclass_p.h> +#include <private/qv4staticvalue_p.h> #include <private/qnumeric_p.h> +#include <private/qv4calldata_p.h> QT_BEGIN_NAMESPACE @@ -68,101 +70,40 @@ namespace Heap { struct Base; } -struct Q_QML_PRIVATE_EXPORT Value +struct Q_QML_PRIVATE_EXPORT Value : public StaticValue { - /* - We use 8 bytes for a value and a different variant of NaN boxing. A Double - NaN (actually -qNaN) is indicated by a number that has the top 13 bits set, and for a - signalling NaN it is the top 14 bits. The other values are usually set to 0 by the - processor, and are thus free for us to store other data. We keep pointers in there for - managed objects, and encode the other types using the free space given to use by the unused - bits for NaN values. This also works for pointers on 64 bit systems, as they all currently - only have 48 bits of addressable memory. (Note: we do leave the lower 49 bits available for - pointers.) - - We xor Doubles with (0xffff8000 << 32). That has the effect that no doubles will - get encoded with bits 63-49 all set to 0. We then use bit 48 to distinguish between - managed/undefined (0), or Null/Int/Bool/Empty (1). So, storing a 49 bit pointer will leave - the top 15 bits 0, which is exactly the 'natural' representation of pointers. If bit 49 is - set, bit 48 indicates Empty (0) or integer-convertible (1). Then the 3 bit below that are - used to encode Null/Int/Bool. - - Undefined is encoded as a managed pointer with value 0. This is the same as a nullptr. - - Specific bit-sequences: - 0 = always 0 - 1 = always 1 - x = stored value - a,b,c,d = specific bit values, see notes - - 32109876 54321098 76543210 98765432 10987654 32109876 54321098 76543210 | - 66665555 55555544 44444444 33333333 33222222 22221111 11111100 00000000 | JS Value - ------------------------------------------------------------------------+-------------- - 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 | Undefined - 00000000 0000000x xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | Managed (heap pointer) - a0000000 0000bc00 00000000 00000000 00000000 00000000 00000000 00000000 | NaN/Inf - dddddddd ddddddxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | double - 00000000 00000010 00000000 00000000 00000000 00000000 00000000 00000000 | empty (non-sparse array hole) - 00000000 00000010 10000000 00000000 00000000 00000000 00000000 00000000 | Null - 00000000 00000011 00000000 00000000 00000000 00000000 00000000 0000000x | Bool - 00000000 00000011 10000000 00000000 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | Int - - Notes: - - a: xor-ed signbit, always 1 for NaN - - bc, xor-ed values: 11 = inf, 10 = sNaN, 01 = qNaN, 00 = boxed value - - d: xor-ed bits, where at least one bit is set, so: (val >> (64-14)) > 0 - - Undefined maps to C++ nullptr, so the "default" initialization is the same for both C++ - and JS - - Managed has the left 15 bits set to 0, so: (val >> (64-15)) == 0 - - empty, Null, Bool, and Int have the left 14 bits set to 0, and bit 49 set to 1, - so: (val >> (64-15)) == 1 - - Null, Bool, and Int have bit 48 set, indicating integer-convertible - - xoring _val with NaNEncodeMask will convert to a double in "natural" representation, where - any non double results in a NaN - - on 32bit we can use the fact that addresses are 32bits wide, so the tag part (bits 32 to - 63) are zero. No need to shift. - */ - - quint64 _val; - - QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR quint64 &rawValueRef() { return _val; } - QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR quint64 rawValue() const { return _val; } - QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setRawValue(quint64 raw) { _val = raw; } - -#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN - static inline int valueOffset() { return 0; } - static inline int tagOffset() { return 4; } -#else // !Q_LITTLE_ENDIAN - static inline int valueOffset() { return 4; } - static inline int tagOffset() { return 0; } -#endif - static inline constexpr quint64 tagValue(quint32 tag, quint32 value) { return quint64(tag) << 32 | value; } - QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setTagValue(quint32 tag, quint32 value) { _val = quint64(tag) << 32 | value; } - QML_NEARLY_ALWAYS_INLINE constexpr quint32 value() const { return _val & quint64(~quint32(0)); } - QML_NEARLY_ALWAYS_INLINE constexpr quint32 tag() const { return _val >> 32; } - QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setTag(quint32 tag) { setTagValue(tag, value()); } + using HeapBasePtr = Heap::Base *; + using ManagedPtr = Managed *; + + Value() = default; + constexpr Value(quint64 val) : StaticValue(val) {} + + static constexpr Value fromStaticValue(StaticValue staticValue) + { + return {staticValue._val}; + } #if QT_POINTER_SIZE == 8 - QML_NEARLY_ALWAYS_INLINE Heap::Base *m() const + QML_NEARLY_ALWAYS_INLINE HeapBasePtr m() const { - Heap::Base *b; + HeapBasePtr b; memcpy(&b, &_val, 8); return b; } - QML_NEARLY_ALWAYS_INLINE void setM(Heap::Base *b) + QML_NEARLY_ALWAYS_INLINE void setM(HeapBasePtr b) { memcpy(&_val, &b, 8); } #elif QT_POINTER_SIZE == 4 - QML_NEARLY_ALWAYS_INLINE Heap::Base *m() const + QML_NEARLY_ALWAYS_INLINE HeapBasePtr m() const { - Q_STATIC_ASSERT(sizeof(Heap::Base*) == sizeof(quint32)); - Heap::Base *b; + Q_STATIC_ASSERT(sizeof(HeapBasePtr) == sizeof(quint32)); + HeapBasePtr b; quint32 v = value(); memcpy(&b, &v, 4); return b; } - QML_NEARLY_ALWAYS_INLINE void setM(Heap::Base *b) + QML_NEARLY_ALWAYS_INLINE void setM(HeapBasePtr b) { quint32 v; memcpy(&v, &b, 4); @@ -172,204 +113,11 @@ struct Q_QML_PRIVATE_EXPORT Value # error "unsupported pointer size" #endif - QML_NEARLY_ALWAYS_INLINE constexpr int int_32() const - { - return int(value()); - } - QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setInt_32(int i) - { - setTagValue(quint32(ValueTypeInternal::Integer), quint32(i)); - } - QML_NEARLY_ALWAYS_INLINE uint uint_32() const { return value(); } - - QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setEmpty() - { - setTagValue(quint32(ValueTypeInternal::Empty), 0); - } - - // ### Fix for 32 bit (easiest solution is to set highest bit to 1 for mananged/undefined/integercompatible - // and use negative numbers here - enum QuickType { - QT_ManagedOrUndefined = 0, - QT_ManagedOrUndefined1 = 1, - QT_ManagedOrUndefined2 = 2, - QT_ManagedOrUndefined3 = 3, - QT_Empty = 4, - QT_Null = 5, - QT_Bool = 6, - QT_Int = 7 - // all other values are doubles - }; - - enum Type { - Undefined_Type = 0, - Managed_Type = 1, - Empty_Type = 4, - Null_Type = 5, - Boolean_Type = 6, - Integer_Type = 7, - Double_Type = 8 - }; - - inline Type type() const { - int t = quickType(); - if (t < QT_Empty) - return _val ? Managed_Type : Undefined_Type; - if (t > QT_Int) - return Double_Type; - return static_cast<Type>(t); - } - - // Shared between 32-bit and 64-bit encoding - enum { - Tag_Shift = 32 - }; - - // Used only by 64-bit encoding - static const quint64 NaNEncodeMask = 0xfffc000000000000ull; - enum { - IsDouble_Shift = 64-14, - IsManagedOrUndefined_Shift = 64-15, - IsIntegerConvertible_Shift = 64-15, - IsIntegerOrBool_Shift = 64-16, - QuickType_Shift = 64 - 17, - IsPositiveIntShift = 31 - }; - - static const quint64 Immediate_Mask_64 = 0x00020000u; // bit 49 - - enum class ValueTypeInternal_64 { - Empty = Immediate_Mask_64 | 0, - Null = Immediate_Mask_64 | 0x08000u, - Boolean = Immediate_Mask_64 | 0x10000u, - Integer = Immediate_Mask_64 | 0x18000u - }; - - // Used only by 32-bit encoding - enum Masks { - SilentNaNBit = 0x00040000, - NotDouble_Mask = 0x7ffa0000, - }; - static const quint64 Immediate_Mask_32 = NotDouble_Mask | 0x00020000u | SilentNaNBit; - - enum class ValueTypeInternal_32 { - Empty = Immediate_Mask_32 | 0, - Null = Immediate_Mask_32 | 0x08000u, - Boolean = Immediate_Mask_32 | 0x10000u, - Integer = Immediate_Mask_32 | 0x18000u - }; - - enum { - Managed_Type_Internal = 0 - }; - - using ValueTypeInternal = ValueTypeInternal_64; - - enum { - NaN_Mask = 0x7ff80000, - }; - - inline quint64 quickType() const { return (_val >> QuickType_Shift); } - - // used internally in property - inline bool isEmpty() const { return tag() == quint32(ValueTypeInternal::Empty); } - inline bool isNull() const { return tag() == quint32(ValueTypeInternal::Null); } - inline bool isBoolean() const { return tag() == quint32(ValueTypeInternal::Boolean); } - inline bool isInteger() const { return tag() == quint32(ValueTypeInternal::Integer); } - inline bool isNullOrUndefined() const { return isNull() || isUndefined(); } - inline bool isNumber() const { return quickType() >= QT_Int; } - - inline bool isUndefined() const { return _val == 0; } - inline bool isDouble() const { return (_val >> IsDouble_Shift); } - inline bool isManaged() const - { -#if QT_POINTER_SIZE == 4 - return value() && tag() == Managed_Type_Internal; -#else - return _val && ((_val >> IsManagedOrUndefined_Shift) == 0); -#endif - } - inline bool isManagedOrUndefined() const - { -#if QT_POINTER_SIZE == 4 - return tag() == Managed_Type_Internal; -#else - return ((_val >> IsManagedOrUndefined_Shift) == 0); -#endif - } - - inline bool isIntOrBool() const { - return (_val >> IsIntegerOrBool_Shift) == 3; - } - - inline bool integerCompatible() const { - Q_ASSERT(!isEmpty()); - return (_val >> IsIntegerConvertible_Shift) == 1; - } - static inline bool integerCompatible(Value a, Value b) { - return a.integerCompatible() && b.integerCompatible(); - } - static inline bool bothDouble(Value a, Value b) { - return a.isDouble() && b.isDouble(); - } - inline bool isNaN() const { return (tag() & 0x7ffc0000 ) == 0x00040000; } - - inline bool isPositiveInt() const { -#if QT_POINTER_SIZE == 4 - return isInteger() && int_32() >= 0; -#else - return (_val >> IsPositiveIntShift) == (quint64(ValueTypeInternal::Integer) << 1); -#endif - } - - QML_NEARLY_ALWAYS_INLINE double doubleValue() const { - Q_ASSERT(isDouble()); - double d; - Value v = *this; - v._val ^= NaNEncodeMask; - memcpy(&d, &v._val, 8); - return d; - } - QML_NEARLY_ALWAYS_INLINE void setDouble(double d) { - if (qt_is_nan(d)) - d = qt_qnan(); - memcpy(&_val, &d, 8); - _val ^= NaNEncodeMask; - Q_ASSERT(isDouble()); - } inline bool isString() const; inline bool isStringOrSymbol() const; inline bool isSymbol() const; inline bool isObject() const; inline bool isFunctionObject() const; - inline bool isInt32() { - if (tag() == quint32(ValueTypeInternal::Integer)) - return true; - if (isDouble()) { - double d = doubleValue(); - if (isInt32(d)) { - setInt_32(int(d)); - return true; - } - } - return false; - } - QML_NEARLY_ALWAYS_INLINE static bool isInt32(double d) { - int i = int(d); - return (i == d && !(d == 0 && std::signbit(d))); - } - double asDouble() const { - if (tag() == quint32(ValueTypeInternal::Integer)) - return int_32(); - return doubleValue(); - } - - bool booleanValue() const { - return int_32(); - } - int integerValue() const { - return int_32(); - } QML_NEARLY_ALWAYS_INLINE String *stringValue() const { if (!isString()) @@ -391,16 +139,16 @@ struct Q_QML_PRIVATE_EXPORT Value return nullptr; return reinterpret_cast<Object*>(const_cast<Value *>(this)); } - QML_NEARLY_ALWAYS_INLINE Managed *managed() const { + QML_NEARLY_ALWAYS_INLINE ManagedPtr managed() const { if (!isManaged()) return nullptr; return reinterpret_cast<Managed*>(const_cast<Value *>(this)); } - QML_NEARLY_ALWAYS_INLINE Heap::Base *heapObject() const { + QML_NEARLY_ALWAYS_INLINE Value::HeapBasePtr heapObject() const { return isManagedOrUndefined() ? m() : nullptr; } - static inline Value fromHeapObject(Heap::Base *m) + static inline Value fromHeapObject(HeapBasePtr m) { Value v; v.setM(m); @@ -443,12 +191,6 @@ struct Q_QML_PRIVATE_EXPORT Value static Heap::Object *toObject(ExecutionEngine *e, Value val); inline bool isPrimitive() const; - inline bool tryIntegerConversion() { - bool b = integerCompatible(); - if (b) - setTagValue(quint32(ValueTypeInternal::Integer), value()); - return b; - } template <typename T> const T *as() const { @@ -482,13 +224,12 @@ struct Q_QML_PRIVATE_EXPORT Value return static_cast<const T *>(managed()); } -#ifndef V4_BOOTSTRAP uint asArrayLength(bool *ok) const; -#endif - ReturnedValue *data_ptr() { return &_val; } - constexpr ReturnedValue asReturnedValue() const { return _val; } - static Value fromReturnedValue(ReturnedValue val) { Value v; v._val = val; return v; } + static constexpr Value fromReturnedValue(ReturnedValue val) + { + return fromStaticValue(StaticValue::fromReturnedValue(val)); + } // As per ES specs bool sameValue(Value other) const; @@ -496,21 +237,45 @@ struct Q_QML_PRIVATE_EXPORT Value inline void mark(MarkStack *markStack); - inline static constexpr Value emptyValue() { return { tagValue(quint32(ValueTypeInternal::Empty), 0) }; } - static inline constexpr Value fromBoolean(bool b) { return { tagValue(quint32(ValueTypeInternal::Boolean), b) }; } - static inline constexpr Value fromInt32(int i) { return { tagValue(quint32(ValueTypeInternal::Integer), quint32(i)) }; } - inline static constexpr Value undefinedValue() { return { 0 }; } - static inline constexpr Value nullValue() { return { tagValue(quint32(ValueTypeInternal::Null), 0) }; } - static inline Value fromDouble(double d); - static inline Value fromUInt32(uint i); - - static double toInteger(double d); - static int toInt32(double d); - static unsigned int toUInt32(double d); + static double toInteger(double d) { return StaticValue::toInteger(d); } + static int toInt32(double d) { return StaticValue::toInt32(d); } + static unsigned int toUInt32(double d) { return StaticValue::toUInt32(d); } + inline static constexpr Value emptyValue() + { + return fromStaticValue(StaticValue::emptyValue()); + } + static inline constexpr Value fromBoolean(bool b) + { + return fromStaticValue(StaticValue::fromBoolean(b)); + } + static inline constexpr Value fromInt32(int i) + { + return fromStaticValue(StaticValue::fromInt32(i)); + } + inline static constexpr Value undefinedValue() + { + return fromStaticValue(StaticValue::undefinedValue()); + } + static inline constexpr Value nullValue() + { + return fromStaticValue(StaticValue::nullValue()); + } + static inline Value fromDouble(double d) + { + return fromStaticValue(StaticValue::fromDouble(d)); + } + static inline Value fromUInt32(uint i) + { + return fromStaticValue(StaticValue::fromUInt32(i)); + } Value &operator =(const ScopedValue &v); - Value &operator=(ReturnedValue v) { _val = v; return *this; } - Value &operator=(Managed *m) { + Value &operator=(ReturnedValue v) + { + StaticValue::operator=(v); + return *this; + } + Value &operator=(ManagedPtr m) { if (!m) { setM(nullptr); } else { @@ -518,7 +283,7 @@ struct Q_QML_PRIVATE_EXPORT Value } return *this; } - Value &operator=(Heap::Base *o) { + Value &operator=(HeapBasePtr o) { setM(o); return *this; } @@ -526,43 +291,88 @@ struct Q_QML_PRIVATE_EXPORT Value template<typename T> Value &operator=(const Scoped<T> &t); }; -Q_STATIC_ASSERT(std::is_trivial< Value >::value); +Q_STATIC_ASSERT(std::is_trivial<Value>::value); +Q_STATIC_ASSERT(sizeof(Value) == sizeof(StaticValue)); + +template<> +inline StaticValue &StaticValue::operator=<Value>(const Value &value) +{ + _val = value._val; + return *this; +} + +template<typename Managed> +inline StaticValue &StaticValue::operator=(const Managed &m) +{ + *static_cast<Value *>(this) = m; + return *this; +} + +template<> +inline Value &StaticValue::asValue<Value>() +{ + return *static_cast<Value *>(this); +} + +template<> +inline const Value &StaticValue::asValue<Value>() const +{ + return *static_cast<const Value *>(this); +} + +template<> +inline Value *CallData::argValues<Value>() +{ + return static_cast<Value *>(static_cast<StaticValue *>(args)); +} + +template<> +inline const Value *CallData::argValues<Value>() const +{ + return static_cast<const Value *>(static_cast<const StaticValue *>(args)); +} + +template<typename HeapBase> +inline Encode::Encode(HeapBase *o) +{ + val = Value::fromHeapObject(o).asReturnedValue(); +} inline void Value::mark(MarkStack *markStack) { - Heap::Base *o = heapObject(); + HeapBasePtr o = heapObject(); if (o) o->mark(markStack); } inline bool Value::isString() const { - Heap::Base *b = heapObject(); + HeapBasePtr b = heapObject(); return b && b->internalClass->vtable->isString; } bool Value::isStringOrSymbol() const { - Heap::Base *b = heapObject(); + HeapBasePtr b = heapObject(); return b && b->internalClass->vtable->isStringOrSymbol; } bool Value::isSymbol() const { - Heap::Base *b = heapObject(); + HeapBasePtr b = heapObject(); return b && b->internalClass->vtable->isStringOrSymbol && !b->internalClass->vtable->isString; } inline bool Value::isObject() const { - Heap::Base *b = heapObject(); + HeapBasePtr b = heapObject(); return b && b->internalClass->vtable->isObject; } inline bool Value::isFunctionObject() const { - Heap::Base *b = heapObject(); + HeapBasePtr b = heapObject(); return b && b->internalClass->vtable->isFunctionObject; } @@ -592,151 +402,12 @@ inline ReturnedValue Value::convertedToNumber() const inline ReturnedValue Heap::Base::asReturnedValue() const { - return Value::fromHeapObject(const_cast<Heap::Base *>(this)).asReturnedValue(); -} - -inline Value Value::fromDouble(double d) -{ - Value v; - v.setDouble(d); - return v; -} - -inline Value Value::fromUInt32(uint i) -{ - Value v; - if (i < INT_MAX) { - v.setTagValue(quint32(ValueTypeInternal::Integer), i); - } else { - v.setDouble(i); - } - return v; -} - -struct Double { - quint64 d; - - Double(double dbl) { - memcpy(&d, &dbl, sizeof(double)); - } - - int sign() const { - return (d >> 63) ? -1 : 1; - } - - bool isDenormal() const { - return static_cast<int>((d << 1) >> 53) == 0; - } - - int exponent() const { - return static_cast<int>((d << 1) >> 53) - 1023; - } - - quint64 significant() const { - quint64 m = (d << 12) >> 12; - if (!isDenormal()) - m |= (static_cast<quint64>(1) << 52); - return m; - } - - static int toInt32(double d) { - int i = static_cast<int>(d); - if (i == d) - return i; - return Double(d).toInt32(); - } - - int toInt32() { - int e = exponent() - 52; - if (e < 0) { - if (e <= -53) - return 0; - return sign() * static_cast<int>(significant() >> -e); - } else { - if (e > 31) - return 0; - return sign() * (static_cast<int>(significant()) << e); - } - } -}; - -inline double Value::toInteger(double d) -{ - if (std::isnan(d)) - return +0; - else if (!d || std::isinf(d)) - return d; - return d >= 0 ? std::floor(d) : std::ceil(d); -} - -inline int Value::toInt32(double value) -{ - return Double::toInt32(value); -} - -inline unsigned int Value::toUInt32(double d) -{ - return static_cast<uint>(toInt32(d)); + return Value::fromHeapObject(const_cast<Value::HeapBasePtr>(this)).asReturnedValue(); } // For source compat with older code in other modules using Primitive = Value; -struct Encode { - static constexpr ReturnedValue undefined() { - return Value::undefinedValue().asReturnedValue(); - } - static constexpr ReturnedValue null() { - return Value::nullValue().asReturnedValue(); - } - - explicit constexpr Encode(bool b) - : val(Value::fromBoolean(b).asReturnedValue()) - { - } - explicit Encode(double d) { - val = Value::fromDouble(d).asReturnedValue(); - } - explicit constexpr Encode(int i) - : val(Value::fromInt32(i).asReturnedValue()) - { - } - explicit Encode(uint i) { - val = Value::fromUInt32(i).asReturnedValue(); - } - explicit constexpr Encode(ReturnedValue v) - : val(v) - { - } - constexpr Encode(Value v) - : val(v.asReturnedValue()) - { - } - - explicit Encode(Heap::Base *o) { - val = Value::fromHeapObject(o).asReturnedValue(); - } - - explicit Encode(Value *o) { - Q_ASSERT(o); - val = o->asReturnedValue(); - } - - static ReturnedValue smallestNumber(double d) { - if (Value::isInt32(d)) - return Encode(static_cast<int>(d)); - else - return Encode(d); - } - - constexpr operator ReturnedValue() const { - return val; - } - quint64 val; -private: - explicit Encode(void *); -}; - template<typename T> ReturnedValue value_convert(ExecutionEngine *e, const Value &v); @@ -793,8 +464,8 @@ inline double Value::toInteger() const template <size_t o> struct HeapValue : Value { static Q_CONSTEXPR size_t offset = o; - Heap::Base *base() { - Heap::Base *base = reinterpret_cast<Heap::Base *>(this) - (offset/sizeof(Heap::Base)); + HeapBasePtr base() { + HeapBasePtr base = reinterpret_cast<HeapBasePtr>(this) - (offset/sizeof(Heap::Base)); Q_ASSERT(base->inUse()); return base; } @@ -802,7 +473,7 @@ struct HeapValue : Value { void set(EngineBase *e, const Value &newVal) { WriteBarrier::write(e, base(), data_ptr(), newVal.asReturnedValue()); } - void set(EngineBase *e, Heap::Base *b) { + void set(EngineBase *e, HeapBasePtr b) { WriteBarrier::write(e, base(), data_ptr(), b->asReturnedValue()); } }; @@ -814,8 +485,9 @@ struct ValueArray { uint alloc; Value values[1]; - Heap::Base *base() { - Heap::Base *base = reinterpret_cast<Heap::Base *>(this) - (offset/sizeof(Heap::Base)); + Value::HeapBasePtr base() { + Value::HeapBasePtr base = reinterpret_cast<Value::HeapBasePtr>(this) + - (offset/sizeof(Heap::Base)); Q_ASSERT(base->inUse()); return base; } @@ -823,7 +495,7 @@ struct ValueArray { void set(EngineBase *e, uint index, Value v) { WriteBarrier::write(e, base(), values[index].data_ptr(), v.asReturnedValue()); } - void set(EngineBase *e, uint index, Heap::Base *b) { + void set(EngineBase *e, uint index, Value::HeapBasePtr b) { WriteBarrier::write(e, base(), values[index].data_ptr(), Value::fromHeapObject(b).asReturnedValue()); } inline const Value &operator[] (uint index) const { @@ -852,12 +524,12 @@ struct ValueArray { const Value *end = v + alloc; if (alloc > 32*1024) { // drain from time to time to avoid overflows in the js stack - Heap::Base **currentBase = markStack->top; + Value::HeapBasePtr *currentBase = markStack->top; while (v < end) { v->mark(markStack); ++v; if (markStack->top >= currentBase + 32*1024) { - Heap::Base **oldBase = markStack->base; + Value::HeapBasePtr *oldBase = markStack->base; markStack->base = currentBase; markStack->drain(); markStack->base = oldBase; diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index c6322fb504..5b68725bcf 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -365,7 +365,7 @@ static inline Heap::CallContext *getScope(QV4::Value *stack, int level) static inline const QV4::Value &constant(Function *function, int index) { - return function->compilationUnit->constants[index]; + return function->compilationUnit->constants[index].asValue<Value>(); } static bool compareEqualInt(QV4::Value &accumulator, QV4::Value lhs, int rhs) @@ -463,7 +463,7 @@ ReturnedValue VME::exec(CppStackFrame *frame, ExecutionEngine *engine) QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, const char *code) { QV4::Function *function = frame->v4Function; - QV4::Value &accumulator = frame->jsFrame->accumulator; + QV4::Value &accumulator = frame->jsFrame->accumulator.asValue<Value>(); QV4::ReturnedValue acc = accumulator.asReturnedValue(); Value *stack = reinterpret_cast<Value *>(frame->jsFrame); diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp index e799267769..8661ebcc13 100644 --- a/src/qml/qml/qqmljavascriptexpression.cpp +++ b/src/qml/qml/qqmljavascriptexpression.cpp @@ -208,7 +208,10 @@ QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, b } Q_ASSERT(m_qmlScope.valueRef()); - QV4::ReturnedValue res = v4Function->call(&callData->thisObject, callData->args, callData->argc(), static_cast<QV4::ExecutionContext *>(m_qmlScope.valueRef())); + QV4::ReturnedValue res = v4Function->call( + &(callData->thisObject.asValue<QV4::Value>()), + callData->argValues<QV4::Value>(), callData->argc(), + static_cast<QV4::ExecutionContext *>(m_qmlScope.valueRef())); QV4::Scope scope(v4); QV4::ScopedValue result(scope, res); diff --git a/tools/qmlimportscanner/main.cpp b/tools/qmlimportscanner/main.cpp index 616de9e80d..57aeeee0a9 100644 --- a/tools/qmlimportscanner/main.cpp +++ b/tools/qmlimportscanner/main.cpp @@ -30,7 +30,7 @@ #include <private/qqmljsparser_p.h> #include <private/qqmljsast_p.h> #include <private/qv4codegen_p.h> -#include <private/qv4value_p.h> +#include <private/qv4staticvalue_p.h> #include <private/qqmlirbuilder_p.h> #include <QtCore/QCoreApplication> diff --git a/tools/qmllint/main.cpp b/tools/qmllint/main.cpp index 791fb71685..0fdc3e0130 100644 --- a/tools/qmllint/main.cpp +++ b/tools/qmllint/main.cpp @@ -34,7 +34,7 @@ #endif #include <QCoreApplication> -#include <private/qv4value_p.h> +#include <private/qv4staticvalue_p.h> #include <private/qqmljslexer_p.h> #include <private/qqmljsparser_p.h> #include <private/qqmljsengine_p.h> |