Qt 4.8
qdeclarativescriptparser.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtDeclarative module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include "private/qdeclarativescriptparser_p.h"
43 
44 #include "private/qdeclarativeparser_p.h"
51 #include "private/qdeclarativerewrite_p.h"
52 
53 #include <QStack>
54 #include <QCoreApplication>
55 #include <QtDebug>
56 
58 
59 using namespace QDeclarativeJS;
60 using namespace QDeclarativeParser;
61 
62 namespace {
63 
64 class ProcessAST: protected AST::Visitor
65 {
66  struct State {
67  State() : object(0), property(0) {}
68  State(QDeclarativeParser::Object *o) : object(o), property(0) {}
69  State(QDeclarativeParser::Object *o, Property *p) : object(o), property(p) {}
70 
73  };
74 
75  struct StateStack : public QStack<State>
76  {
77  void pushObject(QDeclarativeParser::Object *obj)
78  {
79  push(State(obj));
80  }
81 
82  void pushProperty(const QString &name, const LocationSpan &location)
83  {
84  const State &state = top();
85  if (state.property) {
86  State s(state.property->getValue(location),
87  state.property->getValue(location)->getProperty(name.toUtf8()));
88  s.property->location = location;
89  push(s);
90  } else {
91  State s(state.object,
92  state.object->getProperty(name.toUtf8()));
93 
94  s.property->location = location;
95  push(s);
96  }
97  }
98  };
99 
100 public:
101  ProcessAST(QDeclarativeScriptParser *parser);
102  virtual ~ProcessAST();
103 
104  void operator()(const QString &code, AST::Node *node);
105 
106 protected:
107 
108  QDeclarativeParser::Object *defineObjectBinding(AST::UiQualifiedId *propertyName, bool onAssignment,
109  const QString &objectType,
110  AST::SourceLocation typeLocation,
111  LocationSpan location,
112  AST::UiObjectInitializer *initializer = 0);
113 
115 
118 
119  using AST::Visitor::visit;
121 
122  virtual bool visit(AST::UiProgram *node);
123  virtual bool visit(AST::UiImport *node);
124  virtual bool visit(AST::UiObjectDefinition *node);
125  virtual bool visit(AST::UiPublicMember *node);
126  virtual bool visit(AST::UiObjectBinding *node);
127 
128  virtual bool visit(AST::UiScriptBinding *node);
129  virtual bool visit(AST::UiArrayBinding *node);
130  virtual bool visit(AST::UiSourceElement *node);
131 
132  void accept(AST::Node *node);
133 
134  QString asString(AST::UiQualifiedId *node) const;
135 
136  const State state() const;
137  QDeclarativeParser::Object *currentObject() const;
138  Property *currentProperty() const;
139 
140  QString qualifiedNameId() const;
141 
142  QString textAt(const AST::SourceLocation &loc) const
143  { return _contents.mid(loc.offset, loc.length); }
144 
145 
146  QString textAt(const AST::SourceLocation &first,
147  const AST::SourceLocation &last) const
148  { return _contents.mid(first.offset, last.offset + last.length - first.offset); }
149 
150  QString asString(AST::ExpressionNode *expr)
151  {
152  if (! expr)
153  return QString();
154 
155  return textAt(expr->firstSourceLocation(), expr->lastSourceLocation());
156  }
157 
158  QString asString(AST::Statement *stmt)
159  {
160  if (! stmt)
161  return QString();
162 
163  QString s = textAt(stmt->firstSourceLocation(), stmt->lastSourceLocation());
164  s += QLatin1Char('\n');
165  return s;
166  }
167 
168 private:
169  QDeclarativeScriptParser *_parser;
170  StateStack _stateStack;
171  QStringList _scope;
172  QString _contents;
173 };
174 
175 ProcessAST::ProcessAST(QDeclarativeScriptParser *parser)
176  : _parser(parser)
177 {
178 }
179 
180 ProcessAST::~ProcessAST()
181 {
182 }
183 
184 void ProcessAST::operator()(const QString &code, AST::Node *node)
185 {
186  _contents = code;
187  accept(node);
188 }
189 
190 void ProcessAST::accept(AST::Node *node)
191 {
192  AST::Node::acceptChild(node, this);
193 }
194 
195 const ProcessAST::State ProcessAST::state() const
196 {
197  if (_stateStack.isEmpty())
198  return State();
199 
200  return _stateStack.back();
201 }
202 
203 QDeclarativeParser::Object *ProcessAST::currentObject() const
204 {
205  return state().object;
206 }
207 
208 Property *ProcessAST::currentProperty() const
209 {
210  return state().property;
211 }
212 
213 QString ProcessAST::qualifiedNameId() const
214 {
215  return _scope.join(QLatin1String("/"));
216 }
217 
218 QString ProcessAST::asString(AST::UiQualifiedId *node) const
219 {
220  QString s;
221 
222  for (AST::UiQualifiedId *it = node; it; it = it->next) {
223  s.append(it->name->asString());
224 
225  if (it->next)
226  s.append(QLatin1Char('.'));
227  }
228 
229  return s;
230 }
231 
233 ProcessAST::defineObjectBinding(AST::UiQualifiedId *propertyName,
234  bool onAssignment,
235  const QString &objectType,
236  AST::SourceLocation typeLocation,
237  LocationSpan location,
238  AST::UiObjectInitializer *initializer)
239 {
240  int lastTypeDot = objectType.lastIndexOf(QLatin1Char('.'));
241  bool isType = !objectType.isEmpty() &&
242  (objectType.at(0).isUpper() ||
243  (lastTypeDot >= 0 && objectType.at(lastTypeDot+1).isUpper()));
244 
245  int propertyCount = 0;
246  for (AST::UiQualifiedId *name = propertyName; name; name = name->next){
247  ++propertyCount;
248  _stateStack.pushProperty(name->name->asString(),
249  this->location(name));
250  }
251 
252  if (!onAssignment && propertyCount && currentProperty() && currentProperty()->values.count()) {
254  error.setDescription(QCoreApplication::translate("QDeclarativeParser","Property value set multiple times"));
255  error.setLine(this->location(propertyName).start.line);
256  error.setColumn(this->location(propertyName).start.column);
257  _parser->_errors << error;
258  return 0;
259  }
260 
261  if (!isType) {
262 
263  if(propertyCount || !currentObject()) {
265  error.setDescription(QCoreApplication::translate("QDeclarativeParser","Expected type name"));
266  error.setLine(typeLocation.startLine);
267  error.setColumn(typeLocation.startColumn);
268  _parser->_errors << error;
269  return 0;
270  }
271 
272  LocationSpan loc = ProcessAST::location(typeLocation, typeLocation);
273  if (propertyName)
274  loc = ProcessAST::location(propertyName);
275 
276  _stateStack.pushProperty(objectType, loc);
277  accept(initializer);
278  _stateStack.pop();
279 
280  return 0;
281 
282  } else {
283  // Class
284 
285  QString resolvableObjectType = objectType;
286  if (lastTypeDot >= 0)
287  resolvableObjectType.replace(QLatin1Char('.'),QLatin1Char('/'));
288 
290 
291  QDeclarativeScriptParser::TypeReference *typeRef = _parser->findOrCreateType(resolvableObjectType);
292  obj->type = typeRef->id;
293 
294  typeRef->refObjects.append(obj);
295 
296  // XXX this doesn't do anything (_scope never builds up)
297  _scope.append(resolvableObjectType);
298  obj->typeName = qualifiedNameId().toUtf8();
299  _scope.removeLast();
300 
301  obj->location = location;
302 
303  if (propertyCount) {
304  Property *prop = currentProperty();
306  v->object = obj;
307  v->location = obj->location;
308  if (onAssignment)
309  prop->addOnValue(v);
310  else
311  prop->addValue(v);
312 
313  while (propertyCount--)
314  _stateStack.pop();
315 
316  } else {
317 
318  if (! _parser->tree()) {
319  _parser->setTree(obj);
320  } else {
321  const State state = _stateStack.top();
323  v->object = obj;
324  v->location = obj->location;
325  if (state.property) {
326  state.property->addValue(v);
327  } else {
328  Property *defaultProp = state.object->getDefaultProperty();
329  if (defaultProp->location.start.line == -1) {
330  defaultProp->location = v->location;
331  defaultProp->location.end = defaultProp->location.start;
332  defaultProp->location.range.length = 0;
333  }
334  defaultProp->addValue(v);
335  }
336  }
337  }
338 
339  _stateStack.pushObject(obj);
340  accept(initializer);
341  _stateStack.pop();
342 
343  return obj;
344  }
345 }
346 
348 {
349  return location(id->identifierToken, id->identifierToken);
350 }
351 
353 {
354  LocationSpan rv;
355  rv.start.line = start.startLine;
356  rv.start.column = start.startColumn;
357  rv.end.line = end.startLine;
358  rv.end.column = end.startColumn + end.length - 1;
359  rv.range.offset = start.offset;
360  rv.range.length = end.offset + end.length - start.offset;
361  return rv;
362 }
363 
364 // UiProgram: UiImportListOpt UiObjectMemberList ;
365 bool ProcessAST::visit(AST::UiProgram *node)
366 {
367  accept(node->imports);
368  accept(node->members->member);
369  return false;
370 }
371 
372 // UiImport: T_IMPORT T_STRING_LITERAL ;
373 bool ProcessAST::visit(AST::UiImport *node)
374 {
375  QString uri;
377 
378  if (node->fileName) {
379  uri = node->fileName->asString();
380 
381  if (uri.endsWith(QLatin1String(".js"))) {
383  } else {
385  }
386  } else {
388  uri = asString(node->importUri);
389  }
390 
391  AST::SourceLocation startLoc = node->importToken;
392  AST::SourceLocation endLoc = node->semicolonToken;
393 
394  // Qualifier
395  if (node->importId) {
396  import.qualifier = node->importId->asString();
397  if (!import.qualifier.at(0).isUpper()) {
399  error.setDescription(QCoreApplication::translate("QDeclarativeParser","Invalid import qualifier ID"));
400  error.setLine(node->importIdToken.startLine);
401  error.setColumn(node->importIdToken.startColumn);
402  _parser->_errors << error;
403  return false;
404  }
405  if (import.qualifier == QLatin1String("Qt")) {
407  error.setDescription(QCoreApplication::translate("QDeclarativeParser","Reserved name \"Qt\" cannot be used as an qualifier"));
408  error.setLine(node->importIdToken.startLine);
409  error.setColumn(node->importIdToken.startColumn);
410  _parser->_errors << error;
411  return false;
412  }
413 
414  // Check for script qualifier clashes
415  bool isScript = import.type == QDeclarativeScriptParser::Import::Script;
416  for (int ii = 0; ii < _parser->_imports.count(); ++ii) {
417  const QDeclarativeScriptParser::Import &other = _parser->_imports.at(ii);
418  bool otherIsScript = other.type == QDeclarativeScriptParser::Import::Script;
419 
420  if ((isScript || otherIsScript) && import.qualifier == other.qualifier) {
422  error.setDescription(QCoreApplication::translate("QDeclarativeParser","Script import qualifiers must be unique."));
423  error.setLine(node->importIdToken.startLine);
424  error.setColumn(node->importIdToken.startColumn);
425  _parser->_errors << error;
426  return false;
427  }
428  }
429 
430  } else if (import.type == QDeclarativeScriptParser::Import::Script) {
432  error.setDescription(QCoreApplication::translate("QDeclarativeParser","Script import requires a qualifier"));
433  error.setLine(node->fileNameToken.startLine);
434  error.setColumn(node->fileNameToken.startColumn);
435  _parser->_errors << error;
436  return false;
437  }
438 
439  if (node->versionToken.isValid()) {
440  import.version = textAt(node->versionToken);
441  } else if (import.type == QDeclarativeScriptParser::Import::Library) {
443  error.setDescription(QCoreApplication::translate("QDeclarativeParser","Library import requires a version"));
444  error.setLine(node->importIdToken.startLine);
445  error.setColumn(node->importIdToken.startColumn);
446  _parser->_errors << error;
447  return false;
448  }
449 
450 
451  import.location = location(startLoc, endLoc);
452  import.uri = uri;
453 
454  _parser->_imports << import;
455 
456  return false;
457 }
458 
459 bool ProcessAST::visit(AST::UiPublicMember *node)
460 {
461  const struct TypeNameToType {
462  const char *name;
464  const char *qtName;
465  } propTypeNameToTypes[] = {
466  { "int", Object::DynamicProperty::Int, "int" },
467  { "bool", Object::DynamicProperty::Bool, "bool" },
468  { "double", Object::DynamicProperty::Real, "double" },
469  { "real", Object::DynamicProperty::Real, "qreal" },
470  { "string", Object::DynamicProperty::String, "QString" },
471  { "url", Object::DynamicProperty::Url, "QUrl" },
472  { "color", Object::DynamicProperty::Color, "QColor" },
473  // Internally QTime, QDate and QDateTime are all supported.
474  // To be more consistent with JavaScript we expose only
475  // QDateTime as it matches closely with the Date JS type.
476  // We also call it "date" to match.
477  // { "time", Object::DynamicProperty::Time, "QTime" },
478  // { "date", Object::DynamicProperty::Date, "QDate" },
479  { "date", Object::DynamicProperty::DateTime, "QDateTime" },
480  { "variant", Object::DynamicProperty::Variant, "QVariant" }
481  };
482  const int propTypeNameToTypesCount = sizeof(propTypeNameToTypes) /
483  sizeof(propTypeNameToTypes[0]);
484 
485  if(node->type == AST::UiPublicMember::Signal) {
486  const QString name = node->name->asString();
487 
488  Object::DynamicSignal signal;
489  signal.name = name.toUtf8();
490 
491  AST::UiParameterList *p = node->parameters;
492  while (p) {
493  const QString memberType = p->type->asString();
494  const char *qtType = 0;
495  for(int ii = 0; !qtType && ii < propTypeNameToTypesCount; ++ii) {
496  if(QLatin1String(propTypeNameToTypes[ii].name) == memberType)
497  qtType = propTypeNameToTypes[ii].qtName;
498  }
499 
500  if (!qtType) {
502  error.setDescription(QCoreApplication::translate("QDeclarativeParser","Expected parameter type"));
503  error.setLine(node->typeToken.startLine);
504  error.setColumn(node->typeToken.startColumn);
505  _parser->_errors << error;
506  return false;
507  }
508 
509  signal.parameterTypes << qtType;
510  signal.parameterNames << p->name->asString().toUtf8();
511  p = p->finish();
512  }
513 
514  _stateStack.top().object->dynamicSignals << signal;
515  } else {
516  const QString memberType = node->memberType->asString();
517  const QString name = node->name->asString();
518 
519  bool typeFound = false;
521 
522  if (memberType == QLatin1String("alias")) {
523  type = Object::DynamicProperty::Alias;
524  typeFound = true;
525  }
526 
527  for(int ii = 0; !typeFound && ii < propTypeNameToTypesCount; ++ii) {
528  if(QLatin1String(propTypeNameToTypes[ii].name) == memberType) {
529  type = propTypeNameToTypes[ii].type;
530  typeFound = true;
531  }
532  }
533 
534  if (!typeFound && memberType.at(0).isUpper()) {
535  QString typemodifier;
536  if(node->typeModifier)
537  typemodifier = node->typeModifier->asString();
538  if (typemodifier.isEmpty()) {
539  type = Object::DynamicProperty::Custom;
540  } else if(typemodifier == QLatin1String("list")) {
541  type = Object::DynamicProperty::CustomList;
542  } else {
544  error.setDescription(QCoreApplication::translate("QDeclarativeParser","Invalid property type modifier"));
545  error.setLine(node->typeModifierToken.startLine);
547  _parser->_errors << error;
548  return false;
549  }
550  typeFound = true;
551  } else if (node->typeModifier) {
553  error.setDescription(QCoreApplication::translate("QDeclarativeParser","Unexpected property type modifier"));
554  error.setLine(node->typeModifierToken.startLine);
556  _parser->_errors << error;
557  return false;
558  }
559 
560  if(!typeFound) {
562  error.setDescription(QCoreApplication::translate("QDeclarativeParser","Expected property type"));
563  error.setLine(node->typeToken.startLine);
564  error.setColumn(node->typeToken.startColumn);
565  _parser->_errors << error;
566  return false;
567  }
568 
569  if (node->isReadonlyMember) {
571  error.setDescription(QCoreApplication::translate("QDeclarativeParser","Readonly not yet supported"));
572  error.setLine(node->readonlyToken.startLine);
573  error.setColumn(node->readonlyToken.startColumn);
574  _parser->_errors << error;
575  return false;
576 
577  }
579  property.isDefaultProperty = node->isDefaultMember;
580  property.type = type;
581  if (type >= Object::DynamicProperty::Custom) {
583  _parser->findOrCreateType(memberType);
584  typeRef->refObjects.append(_stateStack.top().object);
585  }
586  property.customType = memberType.toUtf8();
587  property.name = name.toUtf8();
588  property.location = location(node->firstSourceLocation(),
589  node->lastSourceLocation());
590 
591  if (node->expression) { // default value
592  property.defaultValue = new Property;
593  property.defaultValue->parent = _stateStack.top().object;
594  property.defaultValue->location =
596  node->expression->lastSourceLocation());
599  node->expression->lastSourceLocation());
600  value->value = getVariant(node->expression);
601  property.defaultValue->values << value;
602  }
603 
604  _stateStack.top().object->dynamicProperties << property;
605 
606  // process QML-like initializers (e.g. property Object o: Object {})
607  accept(node->binding);
608  }
609 
610  return false;
611 }
612 
613 
614 // UiObjectMember: UiQualifiedId UiObjectInitializer ;
615 bool ProcessAST::visit(AST::UiObjectDefinition *node)
616 {
618  node->lastSourceLocation());
619 
620  const QString objectType = asString(node->qualifiedTypeNameId);
621  const AST::SourceLocation typeLocation = node->qualifiedTypeNameId->identifierToken;
622 
623  defineObjectBinding(/*propertyName = */ 0, false, objectType,
624  typeLocation, l, node->initializer);
625 
626  return false;
627 }
628 
629 
630 // UiObjectMember: UiQualifiedId T_COLON UiQualifiedId UiObjectInitializer ;
631 bool ProcessAST::visit(AST::UiObjectBinding *node)
632 {
634  node->initializer->rbraceToken);
635 
636  const QString objectType = asString(node->qualifiedTypeNameId);
637  const AST::SourceLocation typeLocation = node->qualifiedTypeNameId->identifierToken;
638 
639  defineObjectBinding(node->qualifiedId, node->hasOnToken, objectType,
640  typeLocation, l, node->initializer);
641 
642  return false;
643 }
644 
645 QDeclarativeParser::Variant ProcessAST::getVariant(AST::ExpressionNode *expr)
646 {
647  if (AST::StringLiteral *lit = AST::cast<AST::StringLiteral *>(expr)) {
648  return QDeclarativeParser::Variant(lit->value->asString());
649  } else if (expr->kind == AST::Node::Kind_TrueLiteral) {
650  return QDeclarativeParser::Variant(true);
651  } else if (expr->kind == AST::Node::Kind_FalseLiteral) {
652  return QDeclarativeParser::Variant(false);
653  } else if (AST::NumericLiteral *lit = AST::cast<AST::NumericLiteral *>(expr)) {
654  return QDeclarativeParser::Variant(lit->value, asString(expr));
655  } else {
656 
657  if (AST::UnaryMinusExpression *unaryMinus = AST::cast<AST::UnaryMinusExpression *>(expr)) {
658  if (AST::NumericLiteral *lit = AST::cast<AST::NumericLiteral *>(unaryMinus->expression)) {
659  return QDeclarativeParser::Variant(-lit->value, asString(expr));
660  }
661  }
662 
663  return QDeclarativeParser::Variant(asString(expr), expr);
664  }
665 }
666 
667 
668 // UiObjectMember: UiQualifiedId T_COLON Statement ;
669 bool ProcessAST::visit(AST::UiScriptBinding *node)
670 {
671  int propertyCount = 0;
672  AST::UiQualifiedId *propertyName = node->qualifiedId;
673  for (AST::UiQualifiedId *name = propertyName; name; name = name->next){
674  ++propertyCount;
675  _stateStack.pushProperty(name->name->asString(),
676  location(name));
677  }
678 
679  Property *prop = currentProperty();
680 
681  if (prop->values.count()) {
683  error.setDescription(QCoreApplication::translate("QDeclarativeParser","Property value set multiple times"));
684  error.setLine(this->location(propertyName).start.line);
685  error.setColumn(this->location(propertyName).start.column);
686  _parser->_errors << error;
687  return 0;
688  }
689 
690  QDeclarativeParser::Variant primitive;
691 
692  if (AST::ExpressionStatement *stmt = AST::cast<AST::ExpressionStatement *>(node->statement)) {
693  primitive = getVariant(stmt->expression);
694  } else { // do binding
695  primitive = QDeclarativeParser::Variant(asString(node->statement),
696  node->statement);
697  }
698 
702  v->value = primitive;
704  node->statement->lastSourceLocation());
705 
706  prop->addValue(v);
707 
708  while (propertyCount--)
709  _stateStack.pop();
710 
711  return true;
712 }
713 
714 static QList<int> collectCommas(AST::UiArrayMemberList *members)
715 {
716  QList<int> commas;
717 
718  if (members) {
719  for (AST::UiArrayMemberList *it = members->next; it; it = it->next) {
720  commas.append(it->commaToken.offset);
721  }
722  }
723 
724  return commas;
725 }
726 
727 // UiObjectMember: UiQualifiedId T_COLON T_LBRACKET UiArrayMemberList T_RBRACKET ;
728 bool ProcessAST::visit(AST::UiArrayBinding *node)
729 {
730  int propertyCount = 0;
731  AST::UiQualifiedId *propertyName = node->qualifiedId;
732  for (AST::UiQualifiedId *name = propertyName; name; name = name->next){
733  ++propertyCount;
734  _stateStack.pushProperty(name->name->asString(),
735  location(name));
736  }
737 
738  Property* prop = currentProperty();
739 
740  if (prop->values.count()) {
742  error.setDescription(QCoreApplication::translate("QDeclarativeParser","Property value set multiple times"));
743  error.setLine(this->location(propertyName).start.line);
744  error.setColumn(this->location(propertyName).start.column);
745  _parser->_errors << error;
746  return 0;
747  }
748 
749  accept(node->members);
750 
751  // For the DOM, store the position of the T_LBRACKET upto the T_RBRACKET as the range:
754 
755  // Store the positions of the comma token too, again for the DOM to be able to retrieve it.
756  prop->listCommaPositions = collectCommas(node->members);
757 
758  while (propertyCount--)
759  _stateStack.pop();
760 
761  return false;
762 }
763 
764 bool ProcessAST::visit(AST::UiSourceElement *node)
765 {
766  QDeclarativeParser::Object *obj = currentObject();
767 
768  if (AST::FunctionDeclaration *funDecl = AST::cast<AST::FunctionDeclaration *>(node->sourceElement)) {
769 
770  Object::DynamicSlot slot;
771  slot.location = location(funDecl->firstSourceLocation(), funDecl->lastSourceLocation());
772 
773  AST::FormalParameterList *f = funDecl->formals;
774  while (f) {
775  slot.parameterNames << f->name->asString().toUtf8();
776  f = f->finish();
777  }
778 
779  AST::SourceLocation loc = funDecl->rparenToken;
780  loc.offset = loc.end();
781  loc.startColumn += 1;
782  QString body = textAt(loc, funDecl->rbraceToken);
783  slot.name = funDecl->name->asString().toUtf8();
784  slot.body = body;
785  obj->dynamicSlots << slot;
786 
787  } else {
789  error.setDescription(QCoreApplication::translate("QDeclarativeParser","JavaScript declaration outside Script element"));
790  error.setLine(node->firstSourceLocation().startLine);
792  _parser->_errors << error;
793  }
794  return false;
795 }
796 
797 } // end of anonymous namespace
798 
799 
801 : root(0), data(0)
802 {
803 
804 }
805 
807 {
808  clear();
809 }
810 
812 {
813 public:
815  : nodePool(filename, &engine) {}
816 
819 };
820 
821 bool QDeclarativeScriptParser::parse(const QByteArray &qmldata, const QUrl &url)
822 {
823  clear();
824 
825  const QString fileName = url.toString();
827 
829 #ifndef QT_NO_TEXTCODEC
830  stream.setCodec("UTF-8");
831 #endif
832  const QString code = stream.readAll();
833 
834  data = new QDeclarativeScriptParserJsASTData(fileName);
835 
836  Lexer lexer(&data->engine);
837  lexer.setCode(code, /*line = */ 1);
838 
839  Parser parser(&data->engine);
840 
841  if (! parser.parse() || !_errors.isEmpty()) {
842 
843  // Extract errors from the parser
844  foreach (const DiagnosticMessage &m, parser.diagnosticMessages()) {
845 
846  if (m.isWarning())
847  continue;
848 
850  error.setUrl(url);
851  error.setDescription(m.message);
852  error.setLine(m.loc.startLine);
853  error.setColumn(m.loc.startColumn);
854  _errors << error;
855 
856  }
857  }
858 
859  if (_errors.isEmpty()) {
860  ProcessAST process(this);
861  process(code, parser.ast());
862 
863  // Set the url for process errors
864  for(int ii = 0; ii < _errors.count(); ++ii)
865  _errors[ii].setUrl(url);
866  }
867 
868  return _errors.isEmpty();
869 }
870 
872 {
873  return _refTypes;
874 }
875 
877 {
878  return root;
879 }
880 
882 {
883  return _imports;
884 }
885 
887 {
888  return _errors;
889 }
890 
891 static void replaceWithSpace(QString &str, int idx, int n)
892 {
893  QChar *data = str.data() + idx;
894  const QChar space(QLatin1Char(' '));
895  for (int ii = 0; ii < n; ++ii)
896  *data++ = space;
897 }
898 
899 /*
900 Searches for ".pragma <value>" declarations within \a script. Currently supported pragmas
901 are:
902  library
903 */
904 QDeclarativeParser::Object::ScriptBlock::Pragmas QDeclarativeScriptParser::extractPragmas(QString &script)
905 {
906  QDeclarativeParser::Object::ScriptBlock::Pragmas rv = QDeclarativeParser::Object::ScriptBlock::None;
907 
908  const QString pragma(QLatin1String("pragma"));
909  const QString library(QLatin1String("library"));
910 
912  l.setCode(script, 0);
913 
914  int token = l.lex();
915 
916  while (true) {
917  if (token != QDeclarativeJSGrammar::T_DOT)
918  return rv;
919 
920  int startOffset = l.tokenOffset();
921  int startLine = l.currentLineNo();
922 
923  token = l.lex();
924 
926  l.currentLineNo() != startLine ||
927  script.mid(l.tokenOffset(), l.tokenLength()) != pragma)
928  return rv;
929 
930  token = l.lex();
931 
933  l.currentLineNo() != startLine)
934  return rv;
935 
936  QString pragmaValue = script.mid(l.tokenOffset(), l.tokenLength());
937  int endOffset = l.tokenLength() + l.tokenOffset();
938 
939  token = l.lex();
940  if (l.currentLineNo() == startLine)
941  return rv;
942 
943  if (pragmaValue == library) {
945  replaceWithSpace(script, startOffset, endOffset - startOffset);
946  } else {
947  return rv;
948  }
949  }
950  return rv;
951 }
952 
953 #define CHECK_LINE if(l.currentLineNo() != startLine) return rv;
954 #define CHECK_TOKEN(t) if (token != QDeclarativeJSGrammar:: t) return rv;
955 
956 static const int uriTokens[] = {
993 
995 };
996 static inline bool isUriToken(int token)
997 {
998  const int *current = uriTokens;
999  while (*current != QDeclarativeJSGrammar::EOF_SYMBOL) {
1000  if (*current == token)
1001  return true;
1002  ++current;
1003  }
1004  return false;
1005 }
1006 
1008 {
1009  JavaScriptMetaData rv;
1010 
1011  QDeclarativeParser::Object::ScriptBlock::Pragmas &pragmas = rv.pragmas;
1012 
1013  const QString pragma(QLatin1String("pragma"));
1014  const QString js(QLatin1String(".js"));
1015  const QString library(QLatin1String("library"));
1016 
1018  l.setCode(script, 0);
1019 
1020  int token = l.lex();
1021 
1022  while (true) {
1023  if (token != QDeclarativeJSGrammar::T_DOT)
1024  return rv;
1025 
1026  int startOffset = l.tokenOffset();
1027  int startLine = l.currentLineNo();
1028 
1029  token = l.lex();
1030 
1031  CHECK_LINE;
1032 
1033  if (token == QDeclarativeJSGrammar::T_IMPORT) {
1034 
1035  // .import <URI> <Version> as <Identifier>
1036  // .import <file.js> as <Identifier>
1037 
1038  token = l.lex();
1039 
1040  CHECK_LINE;
1041 
1043 
1044  QString file(l.characterBuffer(), l.characterCount());
1045  if (!file.endsWith(js))
1046  return rv;
1047 
1048  token = l.lex();
1049 
1050  CHECK_TOKEN(T_AS);
1051  CHECK_LINE;
1052 
1053  token = l.lex();
1054 
1055  CHECK_TOKEN(T_IDENTIFIER);
1056  CHECK_LINE;
1057 
1058  int endOffset = l.tokenLength() + l.tokenOffset();
1059 
1060  QString importId = script.mid(l.tokenOffset(), l.tokenLength());
1061 
1062  if (!importId.at(0).isUpper())
1063  return rv;
1064 
1065  token = l.lex();
1066  if (l.currentLineNo() == startLine)
1067  return rv;
1068 
1069  replaceWithSpace(script, startOffset, endOffset - startOffset);
1070 
1071  Import import;
1072  import.type = Import::Script;
1073  import.uri = file;
1074  import.qualifier = importId;
1075 
1076  rv.imports << import;
1077 
1078  } else {
1079  // URI
1080  QString uri;
1081  QString version;
1082 
1083  while (true) {
1084  if (!isUriToken(token))
1085  return rv;
1086 
1088 
1089  token = l.lex();
1090  CHECK_LINE;
1091  if (token != QDeclarativeJSGrammar::T_DOT)
1092  break;
1093 
1094  uri.append(QLatin1Char('.'));
1095 
1096  token = l.lex();
1097  CHECK_LINE;
1098  }
1099 
1100  CHECK_TOKEN(T_NUMERIC_LITERAL);
1101  version = script.mid(l.tokenOffset(), l.tokenLength());
1102 
1103  token = l.lex();
1104 
1105  CHECK_TOKEN(T_AS);
1106  CHECK_LINE;
1107 
1108  token = l.lex();
1109 
1110  CHECK_TOKEN(T_IDENTIFIER);
1111  CHECK_LINE;
1112 
1113  int endOffset = l.tokenLength() + l.tokenOffset();
1114 
1115  QString importId = script.mid(l.tokenOffset(), l.tokenLength());
1116 
1117  if (!importId.at(0).isUpper())
1118  return rv;
1119 
1120  token = l.lex();
1121  if (l.currentLineNo() == startLine)
1122  return rv;
1123 
1124  replaceWithSpace(script, startOffset, endOffset - startOffset);
1125 
1126  Import import;
1127  import.type = Import::Library;
1128  import.uri = uri;
1129  import.version = version;
1130  import.qualifier = importId;
1131 
1132  rv.imports << import;
1133  }
1134 
1135  } else if (token == QDeclarativeJSGrammar::T_IDENTIFIER &&
1136  script.mid(l.tokenOffset(), l.tokenLength()) == pragma) {
1137 
1138  token = l.lex();
1139 
1140  CHECK_TOKEN(T_IDENTIFIER);
1141  CHECK_LINE;
1142 
1143  QString pragmaValue = script.mid(l.tokenOffset(), l.tokenLength());
1144  int endOffset = l.tokenLength() + l.tokenOffset();
1145 
1146  if (pragmaValue == QLatin1String("library")) {
1148  replaceWithSpace(script, startOffset, endOffset - startOffset);
1149  } else {
1150  return rv;
1151  }
1152 
1153  token = l.lex();
1154  if (l.currentLineNo() == startLine)
1155  return rv;
1156 
1157  } else {
1158  return rv;
1159  }
1160  }
1161  return rv;
1162 }
1163 
1165 {
1166  if (root) {
1167  root->release();
1168  root = 0;
1169  }
1170  _imports.clear();
1172  _refTypes.clear();
1173  _errors.clear();
1174 
1175  if (data) {
1176  delete data;
1177  data = 0;
1178  }
1179 }
1180 
1182 {
1183  TypeReference *type = 0;
1184  int i = 0;
1185  for (; i < _refTypes.size(); ++i) {
1186  if (_refTypes.at(i)->name == name) {
1187  type = _refTypes.at(i);
1188  break;
1189  }
1190  }
1191  if (!type) {
1192  type = new TypeReference(i, name);
1193  _refTypes.append(type);
1194  }
1195 
1196  return type;
1197 }
1198 
1200 {
1201  Q_ASSERT(! root);
1202 
1203  root = tree;
1204 }
1205 
enum QDeclarativeJS::AST::UiPublicMember::@102 type
const QString asString() const
int type
Definition: qmetatype.cpp:239
#define QT_END_NAMESPACE
This macro expands to.
Definition: qglobal.h:90
void setDescription(const QString &)
Sets the error description.
const QChar at(int i) const
Returns the character at the given index position in the string.
Definition: qstring.h:698
virtual SourceLocation firstSourceLocation() const
QString toString(FormattingOptions options=None) const
Returns the human-displayable string representation of the URL.
Definition: qurl.cpp:5896
void setTree(QDeclarativeParser::Object *tree)
#define it(className, varName)
QByteArray toUtf8() const Q_REQUIRED_RESULT
Returns a UTF-8 representation of the string as a QByteArray.
Definition: qstring.cpp:4074
QList< TypeReference * > referencedTypes() const
virtual SourceLocation firstSourceLocation() const
#define error(msg)
QString & replace(int i, int len, QChar after)
Definition: qstring.cpp:2005
The QByteArray class provides an array of bytes.
Definition: qbytearray.h:135
void setCodec(QTextCodec *codec)
Sets the codec for this stream to codec.
static bool isUriToken(int token)
virtual SourceLocation firstSourceLocation() const =0
void setColumn(int)
Sets the error column number.
static const int uriTokens[]
QList< TypeReference * > _refTypes
QList< QDeclarativeError > errors() const
QLatin1String(DBUS_INTERFACE_DBUS))) Q_GLOBAL_STATIC_WITH_ARGS(QString
The QStack class is a template class that provides a stack.
Definition: qcontainerfwd.h:63
int count(const T &t) const
Returns the number of occurrences of value in the list.
Definition: qlist.h:891
The QUrl class provides a convenient interface for working with URLs.
Definition: qurl.h:61
The QString class provides a Unicode character string.
Definition: qstring.h:83
virtual SourceLocation lastSourceLocation() const =0
AST::UiProgram * ast() const
#define CHECK_TOKEN(t)
#define Q_ASSERT(cond)
Definition: qglobal.h:1823
static QDeclarativeParser::Object::ScriptBlock::Pragmas extractPragmas(QString &)
The QChar class provides a 16-bit Unicode character.
Definition: qchar.h:72
QChar * data()
Returns a pointer to the data stored in the QString.
Definition: qstring.h:710
virtual SourceLocation firstSourceLocation() const =0
bool isEmpty() const
Returns true if the list contains no items; otherwise returns false.
Definition: qlist.h:152
static QString translate(const char *context, const char *key, const char *disambiguation=0, Encoding encoding=CodecForTr)
static JavaScriptMetaData extractMetaData(QString &)
void append(const T &t)
Inserts value at the end of the list.
Definition: qlist.h:507
#define QT_BEGIN_NAMESPACE
This macro expands to.
Definition: qglobal.h:89
QList< DynamicSlot > dynamicSlots
static FILE * stream
const QChar * characterBuffer() const
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition: qstring.h:704
QList< QDeclarativeParser::Object * > refObjects
virtual SourceLocation lastSourceLocation() const =0
const char * name
The QStringList class provides a list of strings.
Definition: qstringlist.h:66
static const char * data(const QByteArray &arr)
TypeReference * findOrCreateType(const QString &name)
The QLatin1String class provides a thin wrapper around an US-ASCII/Latin-1 encoded string literal...
Definition: qstring.h:654
The QDeclarativeError class encapsulates a QML error.
void clear()
Removes all items from the list.
Definition: qlist.h:764
The State element defines configurations of objects and properties.
Q_CORE_EXPORT int QT_FASTCALL script(uint ucs4)
bool isUpper() const
Returns true if the character is an uppercase letter, i.
Definition: qchar.h:273
void setLine(int)
Sets the error line number.
virtual void endVisit(UiProgram *)
QString mid(int position, int n=-1) const Q_REQUIRED_RESULT
Returns a string that contains n characters of this string, starting at the specified position index...
Definition: qstring.cpp:3706
virtual SourceLocation lastSourceLocation() const
QList< QDeclarativeError > _errors
QString & append(QChar c)
Definition: qstring.cpp:1777
void setUrl(const QUrl &)
Sets the url for the file that caused this error.
The QTextStream class provides a convenient interface for reading and writing text.
Definition: qtextstream.h:73
State
Definition: qaudio.h:59
int lastIndexOf(QChar c, int from=-1, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition: qstring.cpp:3000
#define CHECK_LINE
static QTestResult::TestLocation location
Definition: qtestresult.cpp:63
bool parse(const QByteArray &data, const QUrl &url=QUrl())
const char * property
Definition: qwizard.cpp:138
QFactoryLoader * l
QString readAll()
Reads the entire content of the stream, and returns it as a QString.
QDeclarativeScriptParserJsASTData * data
static void replaceWithSpace(QString &str, int idx, int n)
QDeclarativeParser::Object * root
bool endsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string ends with s; otherwise returns false.
Definition: qstring.cpp:3796
QDeclarativeParser::Object::ScriptBlock::Pragmas pragmas
static const KeyPair *const end
virtual SourceLocation firstSourceLocation() const
QDeclarativeScriptParserJsASTData(const QString &filename)
virtual SourceLocation lastSourceLocation() const
QList< DiagnosticMessage > diagnosticMessages() const
static QString fileName(const QString &fileUrl)
Q_OUTOFLINE_TEMPLATE void qDeleteAll(ForwardIterator begin, ForwardIterator end)
Definition: qalgorithms.h:319
The QLatin1Char class provides an 8-bit ASCII/Latin-1 character.
Definition: qchar.h:55
QDeclarativeParser::Object * tree() const
void setCode(const QString &c, int lineno)