forked from qt-creator/qt-creator
Add support for lazy prototypes.
Done-with: ckamm
This commit is contained in:
@@ -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);
|
||||
return lookupMember(name, context);
|
||||
}
|
||||
|
||||
void ObjectValue::setPrototype(const ObjectValue *prototype)
|
||||
{
|
||||
QSet<const ObjectValue *> processed;
|
||||
|
||||
if (! prototype || checkPrototype(prototype, &processed))
|
||||
_prototype = prototype;
|
||||
else
|
||||
qWarning() << "**** invalid prototype:";
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
Reference in New Issue
Block a user