Commit 976d74ca authored by Roberto Raggi's avatar Roberto Raggi
Browse files

Add support for lazy prototypes.

Done-with: ckamm
parent a4f8d388
......@@ -139,6 +139,9 @@ ObjectValue *Bind::bindObject(UiQualifiedId *qualifiedTypeNameId, UiObjectInitia
// normal component instance
ASTObjectValue *objectValue = new ASTObjectValue(qualifiedTypeNameId, initializer, &_interp);
QmlPrototypeReference *prototypeReference = new QmlPrototypeReference(qualifiedTypeNameId, &_interp);
objectValue->setPrototype(prototypeReference);
parentObjectValue = switchObjectValue(objectValue);
if (parentObjectValue)
......@@ -241,9 +244,9 @@ bool Bind::visit(FunctionDeclaration *ast)
{
if (!ast->name)
return false;
// the first declaration counts
if (_currentObjectValue->property(ast->name->asString()))
return false;
// ### FIXME: the first declaration counts
//if (_currentObjectValue->property(ast->name->asString(), 0))
// return false;
ASTFunctionValue *function = new ASTFunctionValue(ast, &_interp);
// ### set the function's scope.
......
......@@ -177,7 +177,7 @@ bool Check::visit(AST::UiQualifiedId *ast)
if (! name)
break;
const Value *value = base->property(name->asString());
const Value *value = base->property(name->asString(), _context);
if (! it->next)
_result = value;
else
......@@ -311,7 +311,7 @@ bool Check::visit(AST::FieldMemberExpression *ast)
if (const Interpreter::Value *base = _engine->convertToObject(check(ast->base))) {
if (const Interpreter::ObjectValue *obj = base->asObjectValue()) {
_result = obj->property(ast->name->asString());
_result = obj->property(ast->name->asString(), _context);
}
}
......
......@@ -162,9 +162,9 @@ QmlObjectValue::QmlObjectValue(const QMetaObject *metaObject, const QString &qml
QmlObjectValue::~QmlObjectValue() {}
const Value *QmlObjectValue::lookupMember(const QString &name) const
const Value *QmlObjectValue::lookupMember(const QString &name, Context *context) const
{
return ObjectValue::lookupMember(name);
return ObjectValue::lookupMember(name, context);
}
const Value *QmlObjectValue::findOrCreateSignature(int index, const QMetaMethod &method, QString *methodName) const
......@@ -708,6 +708,16 @@ void Context::setLookupMode(LookupMode lookupMode)
_lookupMode = lookupMode;
}
const ObjectValue *Context::typeEnvironment() const
{
return _typeEnvironment;
}
void Context::setTypeEnvironment(const ObjectValue *typeEnvironment)
{
_typeEnvironment = typeEnvironment;
}
void Context::pushScope(const ObjectValue *object)
{
_scopeChain.append(object);
......@@ -718,12 +728,12 @@ void Context::popScope()
_scopeChain.removeLast();
}
const Value *Context::lookup(const QString &name) const
const Value *Context::lookup(const QString &name)
{
for (int index = _scopeChain.size() - 1; index != -1; --index) {
const ObjectValue *scope = _scopeChain.at(index);
if (const Value *member = scope->lookupMember(name)) {
if (const Value *member = scope->lookupMember(name, this)) {
if (_lookupMode == JSLookup || ! dynamic_cast<const ASTVariableReference *>(member)) {
return member;
}
......@@ -733,6 +743,24 @@ const Value *Context::lookup(const QString &name) const
return _engine->undefinedValue();
}
const ObjectValue *Context::lookupType(AST::UiQualifiedId *qmlTypeName)
{
const ObjectValue *objectValue = _typeEnvironment;
for (UiQualifiedId *iter = qmlTypeName; objectValue && iter; iter = iter->next) {
if (! iter->name)
return 0;
const Value *value = objectValue->property(iter->name->asString(), this);
if (!value)
return 0;
objectValue = value->asObjectValue();
}
return objectValue;
}
const Value *Context::property(const ObjectValue *object, const QString &name) const
{
const Properties properties = _properties.value(object);
......@@ -833,9 +861,21 @@ void ObjectValue::setClassName(const QString &className)
_className = className;
}
const ObjectValue *ObjectValue::prototype() const
const ObjectValue *ObjectValue::prototype(Context *context) const
{
return _prototype;
const ObjectValue *prototypeObject = value_cast<const ObjectValue *>(_prototype);
if (! prototypeObject) {
if (const Reference *prototypeReference = value_cast<const Reference *>(_prototype)) {
prototypeObject = value_cast<const ObjectValue *>(prototypeReference->value(context));
}
}
return prototypeObject;
}
void ObjectValue::setPrototype(const Value *prototype)
{
// ### FIXME: Check for cycles.
_prototype = prototype;
}
void ObjectValue::setProperty(const QString &name, const Value *value)
......@@ -858,23 +898,14 @@ void ObjectValue::accept(ValueVisitor *visitor) const
visitor->visit(this);
}
const Value *ObjectValue::property(const QString &name) const
const Value *ObjectValue::property(const QString &name, Context *context) const
{
return lookupMember(name);
}
void ObjectValue::setPrototype(const ObjectValue *prototype)
{
QSet<const ObjectValue *> processed;
if (! prototype || checkPrototype(prototype, &processed))
_prototype = prototype;
else
qWarning() << "**** invalid prototype:";
return lookupMember(name, context);
}
bool ObjectValue::checkPrototype(const ObjectValue *proto, QSet<const ObjectValue *> *processed) const
bool ObjectValue::checkPrototype(const ObjectValue *, QSet<const ObjectValue *> *) const
{
#if 0
const int previousSize = processed->size();
processed->insert(this);
......@@ -887,7 +918,7 @@ bool ObjectValue::checkPrototype(const ObjectValue *proto, QSet<const ObjectValu
return true;
}
#endif
return false;
}
......@@ -903,7 +934,7 @@ void ObjectValue::processMembers(MemberProcessor *processor) const
}
}
const Value *ObjectValue::lookupMember(const QString &name) const
const Value *ObjectValue::lookupMember(const QString &name, Context *context) const
{
if (const Value *m = _members.value(name))
return m;
......@@ -914,17 +945,19 @@ const Value *ObjectValue::lookupMember(const QString &name) const
return slowLookup.value();
}
if (_prototype) {
if (const Value *m = _prototype->lookupMember(name))
const ObjectValue *prototypeObject = prototype(context);
if (prototypeObject) {
if (const Value *m = prototypeObject->lookupMember(name, context))
return m;
}
return 0;
}
Activation::Activation()
Activation::Activation(Context *parentContext)
: _thisObject(0),
_calledAsFunction(true)
_calledAsFunction(true),
_parentContext(parentContext)
{
}
......@@ -932,6 +965,17 @@ Activation::~Activation()
{
}
Context *Activation::parentContext() const
{
return _parentContext;
}
Context *Activation::context() const
{
// ### FIXME: Real context for activations.
return 0;
}
bool Activation::calledAsConstructor() const
{
return ! _calledAsFunction;
......@@ -1083,12 +1127,12 @@ const Value *Function::argument(int index) const
return _arguments.at(index);
}
const Value *Function::property(const QString &name) const
const Value *Function::property(const QString &name, Context *context) const
{
if (name == "length")
return engine()->numberValue();
return FunctionValue::property(name);
return FunctionValue::property(name, context);
}
const Value *Function::invoke(const Activation *activation) const
......@@ -1148,14 +1192,14 @@ void ConvertToNumber::visit(const StringValue *)
void ConvertToNumber::visit(const ObjectValue *object)
{
if (const FunctionValue *valueOfMember = value_cast<const FunctionValue *>(object->lookupMember("valueOf"))) {
if (const FunctionValue *valueOfMember = value_cast<const FunctionValue *>(object->lookupMember("valueOf", 0))) {
_result = value_cast<const NumberValue *>(valueOfMember->call(object)); // ### invoke convert-to-number?
}
}
void ConvertToNumber::visit(const FunctionValue *object)
{
if (const FunctionValue *valueOfMember = value_cast<const FunctionValue *>(object->lookupMember("valueOf"))) {
if (const FunctionValue *valueOfMember = value_cast<const FunctionValue *>(object->lookupMember("valueOf", 0))) {
_result = value_cast<const NumberValue *>(valueOfMember->call(object)); // ### invoke convert-to-number?
}
}
......@@ -1209,14 +1253,14 @@ void ConvertToString::visit(const StringValue *value)
void ConvertToString::visit(const ObjectValue *object)
{
if (const FunctionValue *toStringMember = value_cast<const FunctionValue *>(object->lookupMember("toString"))) {
if (const FunctionValue *toStringMember = value_cast<const FunctionValue *>(object->lookupMember("toString", 0))) {
_result = value_cast<const StringValue *>(toStringMember->call(object)); // ### invoke convert-to-string?
}
}
void ConvertToString::visit(const FunctionValue *object)
{
if (const FunctionValue *toStringMember = value_cast<const FunctionValue *>(object->lookupMember("toString"))) {
if (const FunctionValue *toStringMember = value_cast<const FunctionValue *>(object->lookupMember("toString", 0))) {
_result = value_cast<const StringValue *>(toStringMember->call(object)); // ### invoke convert-to-string?
}
}
......@@ -1947,3 +1991,24 @@ bool ASTFunctionValue::isVariadic() const
{
return true;
}
QmlPrototypeReference::QmlPrototypeReference(AST::UiQualifiedId *qmlTypeName, Engine *engine)
: Reference(engine),
_qmlTypeName(qmlTypeName)
{
}
QmlPrototypeReference::~QmlPrototypeReference()
{
}
UiQualifiedId *QmlPrototypeReference::qmlTypeName() const
{
return _qmlTypeName;
}
const Value *QmlPrototypeReference::value(Context *context) const
{
return context->lookupType(_qmlTypeName);
}
......@@ -229,10 +229,14 @@ public:
LookupMode lookupMode() const;
void setLookupMode(LookupMode lookupMode);
const ObjectValue *typeEnvironment() const;
void setTypeEnvironment(const ObjectValue *typeEnvironment);
void pushScope(const ObjectValue *object);
void popScope();
const Value *lookup(const QString &name) const;
const Value *lookup(const QString &name);
const ObjectValue *lookupType(AST::UiQualifiedId *qmlTypeName);
const Value *property(const ObjectValue *object, const QString &name) const;
void setProperty(const ObjectValue *object, const QString &name, const Value *value);
......@@ -242,6 +246,7 @@ private:
Engine *_engine;
LookupMode _lookupMode;
const ObjectValue *_typeEnvironment;
QHash<const ObjectValue *, Properties> _properties;
ScopeChain _scopeChain;
};
......@@ -274,16 +279,16 @@ public:
QString className() const;
void setClassName(const QString &className);
const ObjectValue *prototype() const;
void setPrototype(const ObjectValue *prototype);
const ObjectValue *prototype(Context *context) const;
void setPrototype(const Value *prototype);
virtual void processMembers(MemberProcessor *processor) const;
virtual const Value *property(const QString &name) const;
virtual const Value *property(const QString &name, Context *context) const;
virtual void setProperty(const QString &name, const Value *value);
virtual void removeProperty(const QString &name);
virtual const Value *lookupMember(const QString &name) const;
virtual const Value *lookupMember(const QString &name, Context *context) const;
// Value interface
virtual const ObjectValue *asObjectValue() const;
......@@ -294,7 +299,7 @@ private:
private:
Engine *_engine;
const ObjectValue *_prototype;
const Value *_prototype;
QHash<QString, const Value *> _members;
QString _className;
};
......@@ -307,7 +312,7 @@ public:
QmlObjectValue(const QMetaObject *metaObject, const QString &qmlTypeName, int majorVersion, int minorVersion, Engine *engine);
virtual ~QmlObjectValue();
virtual const Value *lookupMember(const QString &name) const;
virtual const Value *lookupMember(const QString &name, Context *context) const;
virtual void processMembers(MemberProcessor *processor) const;
const Value *propertyValue(const QMetaProperty &prop) const;
......@@ -336,9 +341,12 @@ private:
class QMLJS_EXPORT Activation
{
public:
Activation();
explicit Activation(Context *parentContext = 0);
virtual ~Activation();
Context *context() const;
Context *parentContext() const;
bool calledAsConstructor() const;
void setCalledAsConstructor(bool calledAsConstructor);
......@@ -355,6 +363,7 @@ private:
ObjectValue *_thisObject;
ValueList _arguments;
bool _calledAsFunction;
Context *_parentContext;
};
......@@ -398,7 +407,7 @@ public:
void setReturnValue(const Value *returnValue);
// ObjectValue interface
virtual const Value *property(const QString &name) const;
virtual const Value *property(const QString &name, Context *context) const;
// FunctionValue interface
virtual const Value *returnValue() const;
......@@ -614,6 +623,20 @@ private:
// internal
class QMLJS_EXPORT QmlPrototypeReference: public Reference
{
public:
QmlPrototypeReference(AST::UiQualifiedId *qmlTypeName, Engine *engine);
virtual ~QmlPrototypeReference();
AST::UiQualifiedId *qmlTypeName() const;
virtual const Value *value(Context *context) const;
private:
AST::UiQualifiedId *_qmlTypeName;
};
class QMLJS_EXPORT ASTObjectValue: public ObjectValue
{
AST::UiQualifiedId *_typeName;
......
......@@ -23,16 +23,6 @@ Link::Link(Document::Ptr currentDoc, const Snapshot &snapshot, Interpreter::Engi
Link::~Link()
{
// unset all prototypes
foreach (Document::Ptr doc, _docs) {
BindPtr bind = doc->bind();
if (doc->qmlProgram()) {
foreach (ObjectValue *object, bind->_qmlObjects) {
object->setPrototype(0);
}
}
}
}
Context *Link::context()
......@@ -90,31 +80,20 @@ void Link::scopeChainAt(Document::Ptr doc, Node *currentObject)
if (bind->_idEnvironment)
_context.pushScope(bind->_idEnvironment);
if (const ObjectValue *typeEnvironment = _typeEnvironments.value(doc.data()))
if (const ObjectValue *typeEnvironment = _typeEnvironments.value(doc.data())) {
_context.pushScope(typeEnvironment);
_context.setTypeEnvironment(typeEnvironment);
}
}
void Link::linkImports()
{
foreach (Document::Ptr doc, _docs) {
BindPtr bind = doc->bind();
ObjectValue *typeEnv = engine()->newObject(/*prototype =*/0); // ### FIXME
_typeEnvironments.insert(doc.data(), typeEnv);
// Populate the _typeEnvironment with imports.
populateImportedTypes(typeEnv, doc);
// Set the prototypes.
QHashIterator<Node *, ObjectValue *> it(bind->_qmlObjects);
while (it.hasNext()) {
it.next();
Node *binding = it.key();
ObjectValue *value = it.value();
if (UiQualifiedId *qualifiedId = qualifiedTypeNameId(binding))
value->setPrototype(lookupType(typeEnv, qualifiedId));
}
}
}
......@@ -260,24 +239,6 @@ void Link::importNonFile(Interpreter::ObjectValue *typeEnv, Document::Ptr doc, A
}
}
const ObjectValue *Link::lookupType(ObjectValue *env, UiQualifiedId *id)
{
const ObjectValue *objectValue = env;
for (UiQualifiedId *iter = id; objectValue && iter; iter = iter->next) {
if (! iter->name)
return 0;
const Value *value = objectValue->property(iter->name->asString());
if (!value)
return 0;
objectValue = value->asObjectValue();
}
return objectValue;
}
UiQualifiedId *Link::qualifiedTypeNameId(Node *node)
{
if (UiObjectBinding *binding = AST::cast<UiObjectBinding *>(node))
......
......@@ -31,7 +31,6 @@ public:
private:
static QList<Document::Ptr> reachableDocuments(Document::Ptr startDoc, const Snapshot &snapshot);
static const Interpreter::ObjectValue *lookupType(Interpreter::ObjectValue *env, AST::UiQualifiedId *id);
static AST::UiQualifiedId *qualifiedTypeNameId(AST::Node *node);
void linkImports();
......
......@@ -219,7 +219,7 @@ private:
return;
_processed.insert(object);
enumerateProperties(object->prototype());
enumerateProperties(object->prototype(_context));
object->processMembers(this);
}
......
......@@ -179,7 +179,7 @@ void QmlHoverHandler::updateHelpIdAndTooltip(TextEditor::ITextEditor *editor, in
const Interpreter::Value *value = check(node);
QStringList baseClasses;
m_toolTip = prettyPrint(value, &interp, &baseClasses);
m_toolTip = prettyPrint(value, link.context(), &baseClasses);
foreach (const QString &baseClass, baseClasses) {
QString helpId = QLatin1String("QML.");
......@@ -210,10 +210,10 @@ void QmlHoverHandler::updateHelpIdAndTooltip(TextEditor::ITextEditor *editor, in
}
}
QString QmlHoverHandler::prettyPrint(const QmlJS::Interpreter::Value *value, QmlJS::Interpreter::Engine *interp,
QString QmlHoverHandler::prettyPrint(const QmlJS::Interpreter::Value *value, QmlJS::Interpreter::Context *context,
QStringList *baseClasses) const
{
if (!value)
if (! value)
return QString();
if (const Interpreter::ObjectValue *objectValue = value->asObjectValue()) {
......@@ -223,14 +223,14 @@ QString QmlHoverHandler::prettyPrint(const QmlJS::Interpreter::Value *value, Qml
if (! className.isEmpty())
baseClasses->append(className);
objectValue = objectValue->prototype();
objectValue = objectValue->prototype(context);
} while (objectValue);
if (! baseClasses->isEmpty())
return baseClasses->first();
}
QString typeId = interp->typeId(value);
QString typeId = context->engine()->typeId(value);
if (typeId == QLatin1String("undefined"))
typeId.clear();
......
......@@ -47,6 +47,7 @@ class IEditor;
namespace QmlJS {
namespace Interpreter {
class Engine;
class Context;
class Value;
}
}
......@@ -74,7 +75,7 @@ private slots:
private:
void updateHelpIdAndTooltip(TextEditor::ITextEditor *editor, int pos);
QString prettyPrint(const QmlJS::Interpreter::Value *value, QmlJS::Interpreter::Engine *interp,
QString prettyPrint(const QmlJS::Interpreter::Value *value, QmlJS::Interpreter::Context *context,
QStringList *baseClasses) const;
private:
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment