QmlJS: Introduce UnknownValue.

To distinguish known-to-be-undefined from a genuinely unknown value.

Change-Id: I606b4ea4d726f94553400b8950d3c0a4e76564a8
Reviewed-by: Fawzi Mohamed <fawzi.mohamed@nokia.com>
This commit is contained in:
Christian Kamm
2011-10-10 12:53:28 +02:00
parent 4cb8ec6a32
commit 89ff3cebe6
10 changed files with 105 additions and 76 deletions

View File

@@ -325,7 +325,7 @@ bool Bind::visit(FunctionExpression *ast)
// 1. Function formal arguments
for (FormalParameterList *it = ast->formals; it; it = it->next) {
if (!it->name.isEmpty())
functionScope->setMember(it->name.toString(), _valueOwner.undefinedValue());
functionScope->setMember(it->name.toString(), _valueOwner.unknownValue());
}
// 2. Functions defined inside the function body

View File

@@ -89,7 +89,7 @@ public:
setMessage(ErrInvalidEnumValue);
}
} else if (! _rhsValue->asStringValue() && ! _rhsValue->asNumberValue()
&& ! _rhsValue->asUndefinedValue()) {
&& ! _rhsValue->asUnknownValue()) {
setMessage(ErrEnumValueMustBeStringOrNumber);
}
} else {
@@ -155,7 +155,7 @@ public:
virtual void visit(const AnchorLineValue *)
{
if (! (_rhsValue->asAnchorLineValue() || _rhsValue->asUndefinedValue()))
if (! (_rhsValue->asAnchorLineValue() || _rhsValue->asUnknownValue()))
setMessage(ErrAnchorLineExpected);
}
@@ -842,9 +842,8 @@ bool Check::visit(FunctionExpression *ast)
static bool shouldAvoidNonStrictEqualityCheck(const Value *lhs, const Value *rhs)
{
// we currently use undefined as a "we don't know" value
if (lhs->asUndefinedValue() || rhs->asUndefinedValue())
return true;
if (lhs->asUnknownValue() || rhs->asUnknownValue())
return true; // may coerce or not
if (lhs->asStringValue() && rhs->asNumberValue())
return true; // coerces string to number
@@ -855,7 +854,8 @@ static bool shouldAvoidNonStrictEqualityCheck(const Value *lhs, const Value *rhs
if (lhs->asObjectValue() && rhs->asStringValue())
return true; // coerces object to primitive
if (lhs->asBooleanValue() && !rhs->asBooleanValue())
if (lhs->asBooleanValue() && (!rhs->asBooleanValue()
&& !rhs->asUndefinedValue()))
return true; // coerces bool to number
return false;
@@ -1156,7 +1156,7 @@ bool Check::visit(NewMemberExpression *ast)
if (ast->arguments && ast->arguments->expression && !ast->arguments->next) {
Evaluate evaluate(&_scopeChain);
const Value *arg = evaluate(ast->arguments->expression);
if (arg->asNumberValue() || arg->asUndefinedValue())
if (arg->asNumberValue() || arg->asUnknownValue())
ok = true;
}
if (!ok)

View File

@@ -69,8 +69,9 @@ const Value *Evaluate::value(AST::Node *ast)
result = _context->lookupReference(ref);
}
// if evaluation fails, return an unknown value
if (! result)
result = _valueOwner->undefinedValue();
result = _valueOwner->unknownValue();
return result;
}

View File

@@ -123,21 +123,11 @@ public:
{
}
virtual const Value *returnValue() const
{
return valueOwner()->undefinedValue();
}
virtual int argumentCount() const
{
return _method.parameterNames().size();
}
virtual const Value *argument(int) const
{
return valueOwner()->undefinedValue();
}
virtual QString argumentName(int index) const
{
if (index < _method.parameterNames().size())
@@ -153,7 +143,7 @@ public:
virtual const Value *invoke(const Activation *) const
{
return valueOwner()->undefinedValue();
return valueOwner()->unknownValue();
}
};
@@ -264,7 +254,7 @@ void CppComponentValue::processMembers(MemberProcessor *processor) const
if (!explicitSignals.contains(signalName)) {
// process the generated slot
const QString &slotName = generatedSlotName(signalName);
processor->processGeneratedSlot(slotName, valueOwner()->undefinedValue());
processor->processGeneratedSlot(slotName, valueOwner()->unknownValue());
}
}
@@ -337,7 +327,8 @@ const Value *CppComponentValue::valueForCppName(const QString &typeName) const
return value;
}
return valueOwner()->undefinedValue();
// may still be a cpp based value
return valueOwner()->unknownValue();
}
const CppComponentValue *CppComponentValue::prototype() const
@@ -566,6 +557,10 @@ void ValueVisitor::visit(const UndefinedValue *)
{
}
void ValueVisitor::visit(const UnknownValue *)
{
}
void ValueVisitor::visit(const NumberValue *)
{
}
@@ -624,6 +619,11 @@ const UndefinedValue *Value::asUndefinedValue() const
return 0;
}
const UnknownValue *Value::asUnknownValue() const
{
return 0;
}
const NumberValue *Value::asNumberValue() const
{
return 0;
@@ -727,11 +727,20 @@ const UndefinedValue *UndefinedValue::asUndefinedValue() const
return this;
}
void UndefinedValue::accept(ValueVisitor *visitor) const
void UnknownValue::accept(ValueVisitor *visitor) const
{
visitor->visit(this);
}
const UnknownValue *UnknownValue::asUnknownValue() const
{
return this;
}
void UndefinedValue::accept(ValueVisitor *visitor) const
{
visitor->visit(this);
}
const NumberValue *NumberValue::asNumberValue() const
{
return this;
@@ -1168,7 +1177,7 @@ const Value *FunctionValue::call(const ObjectValue *thisObject, const ValueList
const Value *FunctionValue::returnValue() const
{
return valueOwner()->undefinedValue();
return valueOwner()->unknownValue();
}
int FunctionValue::argumentCount() const
@@ -1178,7 +1187,7 @@ int FunctionValue::argumentCount() const
const Value *FunctionValue::argument(int) const
{
return valueOwner()->undefinedValue();
return valueOwner()->unknownValue();
}
QString FunctionValue::argumentName(int index) const
@@ -1812,8 +1821,9 @@ ASTVariableReference::~ASTVariableReference()
const Value *ASTVariableReference::value(ReferenceContext *referenceContext) const
{
// may be assigned to later
if (!_ast->expression)
return valueOwner()->undefinedValue();
return valueOwner()->unknownValue();
Document::Ptr doc = _doc->ptr();
ScopeChain scopeChain(doc, referenceContext->context());
@@ -1850,21 +1860,11 @@ FunctionExpression *ASTFunctionValue::ast() const
return _ast;
}
const Value *ASTFunctionValue::returnValue() const
{
return valueOwner()->undefinedValue();
}
int ASTFunctionValue::argumentCount() const
{
return _argumentNames.size();
}
const Value *ASTFunctionValue::argument(int) const
{
return valueOwner()->undefinedValue();
}
QString ASTFunctionValue::argumentName(int index) const
{
if (index < _argumentNames.size()) {
@@ -1876,11 +1876,6 @@ QString ASTFunctionValue::argumentName(int index) const
return FunctionValue::argumentName(index);
}
bool ASTFunctionValue::isVariadic() const
{
return true;
}
bool ASTFunctionValue::getSourceLocation(QString *fileName, int *line, int *column) const
{
*fileName = _doc->fileName();
@@ -1963,10 +1958,7 @@ const Value *ASTPropertyReference::value(ReferenceContext *referenceContext) con
return evaluator(_ast->statement);
}
if (!_ast->memberType.isEmpty())
return valueOwner()->defaultValueForBuiltinType(_ast->memberType.toString());
return valueOwner()->undefinedValue();
return valueOwner()->defaultValueForBuiltinType(_ast->memberType.toString());
}
ASTSignal::ASTSignal(UiPublicMember *ast, const Document *doc, ValueOwner *valueOwner)
@@ -2006,7 +1998,7 @@ const Value *ASTSignal::argument(int index) const
for (int i = 0; param && i < index; ++i)
param = param->next;
if (!param || param->type.isEmpty())
return valueOwner()->undefinedValue();
return valueOwner()->unknownValue();
return valueOwner()->defaultValueForBuiltinType(param->type.toString());
}

View File

@@ -58,6 +58,7 @@ class ValueOwner;
class Value;
class NullValue;
class UndefinedValue;
class UnknownValue;
class NumberValue;
class IntValue;
class RealValue;
@@ -95,6 +96,7 @@ public:
virtual void visit(const NullValue *);
virtual void visit(const UndefinedValue *);
virtual void visit(const UnknownValue *);
virtual void visit(const NumberValue *);
virtual void visit(const BooleanValue *);
virtual void visit(const StringValue *);
@@ -119,6 +121,7 @@ public:
virtual const NullValue *asNullValue() const;
virtual const UndefinedValue *asUndefinedValue() const;
virtual const UnknownValue *asUnknownValue() const;
virtual const NumberValue *asNumberValue() const;
virtual const IntValue *asIntValue() const;
virtual const RealValue *asRealValue() const;
@@ -161,6 +164,12 @@ template <> Q_INLINE_TEMPLATE const UndefinedValue *value_cast(const Value *v)
else return 0;
}
template <> Q_INLINE_TEMPLATE const UnknownValue *value_cast(const Value *v)
{
if (v) return v->asUnknownValue();
else return 0;
}
template <> Q_INLINE_TEMPLATE const NumberValue *value_cast(const Value *v)
{
if (v) return v->asNumberValue();
@@ -280,6 +289,13 @@ public:
virtual void accept(ValueVisitor *visitor) const;
};
class QMLJS_EXPORT UnknownValue: public Value
{
public:
virtual const UnknownValue *asUnknownValue() const;
virtual void accept(ValueVisitor *) const;
};
class QMLJS_EXPORT NumberValue: public Value
{
public:
@@ -793,11 +809,8 @@ public:
AST::FunctionExpression *ast() const;
virtual const Value *returnValue() const;
virtual int argumentCount() const;
virtual const Value *argument(int) const;
virtual QString argumentName(int index) const;
virtual bool isVariadic() const;
virtual bool getSourceLocation(QString *fileName, int *line, int *column) const;
};

View File

@@ -165,7 +165,7 @@ Link::Link(const Snapshot &snapshot, const QStringList &importPaths, const Libra
if (!cppTypeName.isEmpty())
value = d->valueOwner->cppQmlTypes().objectByCppName(cppTypeName);
if (!value)
value = d->valueOwner->undefinedValue();
value = d->valueOwner->unknownValue();
global->setMember(it.key(), value);
}
}

View File

@@ -112,6 +112,8 @@ const Value * ScopeChain::lookup(const QString &name, const ObjectValue **foundI
if (foundInScope)
*foundInScope = 0;
// we're confident to implement global lookup correctly, so return 'undefined'
return m_context->valueOwner()->undefinedValue();
}

View File

@@ -292,6 +292,11 @@ const UndefinedValue *ValueOwner::undefinedValue() const
return &_undefinedValue;
}
const UnknownValue *ValueOwner::unknownValue() const
{
return &_unknownValue;
}
const NumberValue *ValueOwner::numberValue() const
{
return &_numberValue;
@@ -488,7 +493,7 @@ Function *ValueOwner::addFunction(ObjectValue *object, const QString &name, cons
Function *function = newFunction();
function->setReturnValue(result);
for (int i = 0; i < argumentCount; ++i)
function->addArgument(undefinedValue()); // ### introduce unknownValue
function->addArgument(unknownValue());
object->setMember(name, function);
return function;
}
@@ -497,7 +502,7 @@ Function *ValueOwner::addFunction(ObjectValue *object, const QString &name, int
{
Function *function = newFunction();
for (int i = 0; i < argumentCount; ++i)
function->addArgument(undefinedValue()); // ### introduce unknownValue
function->addArgument(unknownValue());
object->setMember(name, function);
return function;
}
@@ -775,7 +780,7 @@ void ValueOwner::initializePrototypes()
f->addArgument(stringValue(), "header");
f->addArgument(stringValue(), "value");
f = addFunction(xmlHttpRequest, "send");
f->addArgument(undefinedValue(), "data");
f->addArgument(unknownValue(), "data");
f = addFunction(xmlHttpRequest, "abort");
xmlHttpRequest->setMember("status", numberValue());
xmlHttpRequest->setMember("statusText", stringValue());
@@ -783,7 +788,7 @@ void ValueOwner::initializePrototypes()
f->addArgument(stringValue(), "header");
f = addFunction(xmlHttpRequest, "getAllResponseHeaders");
xmlHttpRequest->setMember("responseText", stringValue());
xmlHttpRequest->setMember("responseXML", undefinedValue());
xmlHttpRequest->setMember("responseXML", unknownValue());
f = addFunction(_globalObject, "XMLHttpRequest", xmlHttpRequest);
f->setMember("prototype", xmlHttpRequest);
@@ -814,9 +819,9 @@ void ValueOwner::initializePrototypes()
f->addArgument(stringValue(), "text");
f->addArgument(functionPrototype(), "reviver");
f = addFunction(json, "stringify", stringValue());
f->addArgument(undefinedValue(), "value");
f->addArgument(undefinedValue(), "replacer");
f->addArgument(undefinedValue(), "space");
f->addArgument(unknownValue(), "value");
f->addArgument(unknownValue(), "replacer");
f->addArgument(unknownValue(), "space");
_globalObject->setMember("JSON", json);
// global Qt object, in alphabetic order
@@ -864,8 +869,8 @@ void ValueOwner::initializePrototypes()
_qmlFontObject = newObject(/*prototype =*/ 0);
_qmlFontObject->setClassName(QLatin1String("Font"));
_qmlFontObject->setMember("family", stringValue());
_qmlFontObject->setMember("weight", undefinedValue()); // ### make me an object
_qmlFontObject->setMember("capitalization", undefinedValue()); // ### make me an object
_qmlFontObject->setMember("weight", unknownValue()); // ### make me an object
_qmlFontObject->setMember("capitalization", unknownValue()); // ### make me an object
_qmlFontObject->setMember("bold", booleanValue());
_qmlFontObject->setMember("italic", booleanValue());
_qmlFontObject->setMember("underline", booleanValue());
@@ -948,7 +953,9 @@ const Value *ValueOwner::defaultValueForBuiltinType(const QString &name) const
return colorValue();
} else if (name == QLatin1String("date")) {
return datePrototype();
} else if (name == QLatin1String("var")
|| name == QLatin1String("variant")) {
return unknownValue();
}
// ### variant or var
return undefinedValue();
}

View File

@@ -46,6 +46,7 @@ namespace QmlJS {
class Value;
class NullValue;
class UndefinedValue;
class UnknownValue;
class NumberValue;
class IntValue;
class RealValue;
@@ -72,6 +73,7 @@ public:
const NullValue *nullValue() const;
const UndefinedValue *undefinedValue() const;
const UnknownValue *unknownValue() const;
const NumberValue *numberValue() const;
const RealValue *realValue() const;
const IntValue *intValue() const;
@@ -174,6 +176,7 @@ private:
NullValue _nullValue;
UndefinedValue _undefinedValue;
UnknownValue _unknownValue;
NumberValue _numberValue;
RealValue _realValue;
IntValue _intValue;