1 /****************************************************************************
2 **
3 ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the Qt Solutions component.
7 **
8 ** $QT_BEGIN_LICENSE:BSD$
9 ** You may use this file under the terms of the BSD license as follows:
10 **
11 ** "Redistribution and use in source and binary forms, with or without
12 ** modification, are permitted provided that the following conditions are
13 ** met:
14 **   * Redistributions of source code must retain the above copyright
15 **     notice, this list of conditions and the following disclaimer.
16 **   * Redistributions in binary form must reproduce the above copyright
17 **     notice, this list of conditions and the following disclaimer in
18 **     the documentation and/or other materials provided with the
19 **     distribution.
20 **   * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
21 **     of its contributors may be used to endorse or promote products derived
22 **     from this software without specific prior written permission.
23 **
24 **
25 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
36 **
37 ** $QT_END_LICENSE$
38 **
39 ****************************************************************************/
40 
41 
42 #include "qscriptengine_p.h"
43 
44 
45 #include "qscriptvalueimpl_p.h"
46 #include "qscriptcontext_p.h"
47 #include "qscriptmember_p.h"
48 #include "qscriptobject_p.h"
49 #include "qscriptlexer_p.h"
50 #include "qscriptnodepool_p.h"
51 #include "qscriptparser_p.h"
52 #include "qscriptcompiler_p.h"
53 #include "qscriptvalueiteratorimpl_p.h"
54 #include "qscriptecmaglobal_p.h"
55 #include "qscriptecmamath_p.h"
56 #include "qscriptecmaarray_p.h"
57 #include "qscriptextenumeration_p.h"
58 #include "qscriptsyntaxchecker_p.h"
59 #include "qscriptsyntaxcheckresult_p.h"
60 #include "qscriptclass.h"
61 #include "qscriptclass_p.h"
62 #include "qscriptengineagent.h"
63 
64 #include <QDate>
65 #include <QDateTime>
66 #include <QRegExp>
67 #include <QStringList>
68 #include <QVariant>
69 
70 #ifndef QT_NO_QOBJECT
71 #include "qscriptextensioninterface.h"
72 #include <QDir>
73 #include <QFile>
74 #include <QFileInfo>
75 #include <QTextStream>
76 #include <QCoreApplication>
77 #include <QPluginLoader>
78 #endif
79 
80 Q_DECLARE_METATYPE(QScriptValue)
81 #ifndef QT_NO_QOBJECT
82 Q_DECLARE_METATYPE(QObjectList)
83 #endif
84 Q_DECLARE_METATYPE(QList<int>)
85 
86 QT_BEGIN_NAMESPACE
87 
88 extern char *qdtoa(double d, int mode, int ndigits, int *decpt, int *sign, char **rve, char **digits_str);
89 extern double qstrtod(const char *s00, char const **se, bool *ok);
90 
91 namespace QScript {
92 
numberToString(qsreal value)93 QString numberToString(qsreal value)
94 {
95     if (qIsNaN(value))
96         return QLatin1String("NaN");
97 
98     else if (qIsInf(value))
99         return QLatin1String(value < 0 ? "-Infinity" : "Infinity");
100 
101     else if (value == 0)
102         return QLatin1String("0");
103 
104     QByteArray buf;
105     buf.reserve(80);
106 
107     int decpt;
108     int sign;
109     char *result = 0;
110     (void) qdtoa(value, 0, 0, &decpt, &sign, 0, &result);
111 
112     if (! result)
113         return QString();
114 
115     else if (decpt <= 0 && decpt > -6) {
116 
117         buf.fill('0', -decpt + 2 + sign);
118 
119         if (sign) // fix the sign.
120             buf[0] = '-';
121 
122         buf[sign + 1] = '.';
123         buf += result;
124     }
125 
126     else {
127         if (sign)
128             buf += '-';
129 
130         buf += result;
131         int length = buf.length() - sign;
132 
133         if (decpt <= 21 && decpt > 0) {
134             if (length <= decpt)
135                 buf += QByteArray().fill('0', decpt - length);
136             else
137                 buf.insert(decpt + sign, '.');
138         }
139 
140         else if (result[0] >= '0' && result[0] <= '9') {
141             if (length > 1)
142                 buf.insert(1 + sign, '.');
143 
144             buf += 'e';
145             buf += (decpt >= 0) ? '+' : '-';
146 
147             int e = decpt - 1;
148 
149             if (e < 0)
150                 e = -e;
151 
152             if (e >= 100)
153                 buf += '0' + e / 100;
154 
155             if (e >= 10)
156                 buf += '0' + (e % 100) / 10;
157 
158             buf += '0' + e % 10;
159         }
160     }
161 
162     free(result);
163 
164     return QString::fromLatin1(buf);
165 }
166 
toDigit(char c)167 static int toDigit(char c)
168 {
169     if ((c >= '0') && (c <= '9'))
170         return c - '0';
171     else if ((c >= 'a') && (c <= 'z'))
172         return 10 + c - 'a';
173     else if ((c >= 'A') && (c <= 'Z'))
174         return 10 + c - 'A';
175     return -1;
176 }
177 
integerFromString(const char * buf,int size,int radix)178 qsreal integerFromString(const char *buf, int size, int radix)
179 {
180     if (size == 0)
181         return qSNaN();
182 
183     qsreal sign = 1.0;
184     int i = 0;
185     if (buf[0] == '+') {
186         ++i;
187     } else if (buf[0] == '-') {
188         sign = -1.0;
189         ++i;
190     }
191 
192     if (((size-i) >= 2) && (buf[i] == '0')) {
193         if (((buf[i+1] == 'x') || (buf[i+1] == 'X'))
194             && (radix < 34)) {
195             if ((radix != 0) && (radix != 16))
196                 return 0;
197             radix = 16;
198             i += 2;
199         } else {
200             if (radix == 0) {
201                 radix = 8;
202                 ++i;
203             }
204         }
205     } else if (radix == 0) {
206         radix = 10;
207     }
208 
209     int j = i;
210     for ( ; i < size; ++i) {
211         int d = toDigit(buf[i]);
212         if ((d == -1) || (d >= radix))
213             break;
214     }
215     qsreal result;
216     if (j == i) {
217         if (!qstrcmp(buf, "Infinity"))
218             result = qInf();
219         else
220             result = qSNaN();
221     } else {
222         result = 0;
223         qsreal multiplier = 1;
224         for (--i ; i >= j; --i, multiplier *= radix)
225             result += toDigit(buf[i]) * multiplier;
226     }
227     result *= sign;
228     return result;
229 }
230 
integerFromString(const QString & str,int radix)231 qsreal integerFromString(const QString &str, int radix)
232 {
233     QByteArray ba = str.trimmed().toUtf8();
234     return integerFromString(ba.constData(), ba.size(), radix);
235 }
236 
numberFromString(const QString & repr)237 qsreal numberFromString(const QString &repr)
238 {
239     QString str = repr.trimmed();
240     if ((str.length() > 2) && (str.at(0) == QLatin1Char('0')) && (str.at(1).toUpper() == QLatin1Char('X')))
241         return integerFromString(str.mid(2), 16);
242     QByteArray latin1 = str.toLatin1();
243     const char *data = latin1.constData();
244     const char *eptr = 0;
245     qsreal result = qstrtod(data, &eptr, 0);
246     if (eptr == data) {
247         if (str == QLatin1String("Infinity"))
248             result = +qInf();
249         else if (str == QLatin1String("+Infinity"))
250             result = +qInf();
251         else if (str == QLatin1String("-Infinity"))
252             result = -qInf();
253         else if (str.isEmpty())
254             result = 0;
255         else
256             result = qSNaN();
257     } else if (eptr != (data + latin1.length())) {
258         result = qSNaN();
259     }
260     return result;
261 }
262 
NodePool(const QString & fileName,QScriptEnginePrivate * engine)263 NodePool::NodePool(const QString &fileName, QScriptEnginePrivate *engine)
264     : m_fileName(fileName), m_engine(engine)
265 {
266 #ifndef Q_SCRIPT_NO_EVENT_NOTIFY
267     m_id = engine->nextScriptId();
268 #endif
269 }
270 
~NodePool()271 NodePool::~NodePool()
272 {
273     qDeleteAll(m_codeCache);
274     m_codeCache.clear();
275 
276 #ifndef Q_SCRIPT_NO_EVENT_NOTIFY
277     m_engine->notifyScriptUnload(id());
278 #endif
279 }
280 
createCompiledCode(AST::Node * node,CompilationUnit & compilation)281 Code *NodePool::createCompiledCode(AST::Node *node, CompilationUnit &compilation)
282 {
283     QHash<AST::Node*, Code*>::const_iterator it = m_codeCache.constFind(node);
284     if (it != m_codeCache.constEnd())
285         return it.value();
286 
287     Code *code = new Code();
288     code->init(compilation, this);
289 
290     m_codeCache.insert(node, code);
291     return code;
292 }
293 
294 class EvalFunction : public QScriptFunction
295 {
296 public:
EvalFunction(QScriptEnginePrivate *)297     EvalFunction(QScriptEnginePrivate *)
298     { length = 1; }
299 
~EvalFunction()300     virtual ~EvalFunction() {}
301 
evaluate(QScriptContextPrivate * context,const QString & contents,int lineNo,const QString & fileName,bool calledFromScript)302     void evaluate(QScriptContextPrivate *context, const QString &contents,
303                   int lineNo, const QString &fileName, bool calledFromScript)
304     {
305         QScriptEnginePrivate *eng_p = context->engine();
306 
307         QExplicitlySharedDataPointer<NodePool> pool;
308         pool = new NodePool(fileName, eng_p);
309         eng_p->setNodePool(pool.data());
310 
311         QString errorMessage;
312         int errorLineNumber;
313         AST::Node *program = eng_p->createAbstractSyntaxTree(
314             contents, lineNo, &errorMessage, &errorLineNumber);
315 
316         eng_p->setNodePool(0);
317 
318 #ifndef Q_SCRIPT_NO_EVENT_NOTIFY
319         eng_p->notifyScriptLoad(pool->id(), contents, fileName, lineNo);
320 #endif
321 
322         Code *code = 0;
323         if (program) {
324             Compiler compiler(eng_p);
325             compiler.setTopLevelCompiler(true);
326             CompilationUnit compilation = compiler.compile(program);
327             if (!compilation.isValid()) {
328                 errorMessage = compilation.errorMessage();
329                 errorLineNumber = compilation.errorLineNumber();
330             } else {
331                 code = pool->createCompiledCode(program, compilation);
332             }
333         }
334 
335         if (!code) {
336             context->errorLineNumber = errorLineNumber;
337             context->currentLine = errorLineNumber;
338 #ifndef Q_SCRIPT_NO_EVENT_NOTIFY
339             Code *oldCode = context->m_code;
340             Code dummy;
341             dummy.astPool = pool.data();
342             context->m_code = &dummy; // so agents get the script ID
343             bool wasEvaluating = eng_p->m_evaluating;
344             eng_p->m_evaluating = true;
345             eng_p->notifyFunctionEntry(context);
346 #endif
347             context->throwError(QScriptContext::SyntaxError, errorMessage);
348 #ifndef Q_SCRIPT_NO_EVENT_NOTIFY
349             eng_p->notifyFunctionExit(context);
350             eng_p->m_evaluating = wasEvaluating;
351             context->m_code = oldCode;
352 #endif
353             return;
354         }
355 
356         if (calledFromScript) {
357             if (QScriptContextPrivate *pc = context->parentContext()) {
358                 context->setActivationObject(pc->activationObject());
359                 context->setThisObject(pc->thisObject());
360                 context->m_scopeChain = pc->m_scopeChain;
361             }
362         }
363 
364         const QScriptInstruction *iPtr = context->instructionPointer();
365         context->execute(code);
366         context->setInstructionPointer(iPtr);
367     }
368 
execute(QScriptContextPrivate * context)369     virtual void execute(QScriptContextPrivate *context)
370     {
371         QScriptEnginePrivate *eng = context->engine();
372         int lineNo = context->currentLine;
373         if (lineNo == -1) {
374             QScriptContextPrivate *pc = context->parentContext();
375             if (pc)
376                 lineNo = pc->currentLine;
377             else
378                 lineNo = 1;
379         }
380         QString fileName; // don't set this for now, we don't want to change the official eval() for now.
381 
382         if (context->argumentCount() == 0) {
383             context->setReturnValue(eng->undefinedValue());
384         } else {
385             QScriptValueImpl arg = context->argument(0);
386             if (arg.isString()) {
387                 QString contents = arg.toString();
388                 evaluate(context, contents, lineNo, fileName, /*calledFromScript=*/true);
389             } else {
390                 context->setReturnValue(arg);
391             }
392         }
393     }
394 
functionName() const395     QString functionName() const
396     {
397         return QLatin1String("eval");
398     }
399 };
400 
401 class ArgumentsClassData: public QScriptClassData
402 {
403 
404 public:
405 
get(const QScriptValueImpl & object)406     static inline QScript::ArgumentsObjectData *get(const QScriptValueImpl &object)
407         { return static_cast<QScript::ArgumentsObjectData*>(object.objectData()); }
408 
409     virtual bool resolve(const QScriptValueImpl &object, QScriptNameIdImpl *nameId,
410                          QScript::Member *member, QScriptValueImpl *base,
411                          QScript::AccessMode access);
412     virtual bool get(const QScriptValueImpl &object, const QScript::Member &member,
413                      QScriptValueImpl *out_value);
414     virtual bool put(QScriptValueImpl *object, const QScript::Member &member,
415                      const QScriptValueImpl &value);
416     virtual void mark(const QScriptValueImpl &object, int generation);
417     virtual QScriptClassDataIterator *newIterator(const QScriptValueImpl &object);
418 };
419 
420 class ArgumentsClassDataIterator: public QScriptClassDataIterator
421 {
422 public:
423     ArgumentsClassDataIterator(ArgumentsObjectData *data);
424     virtual ~ArgumentsClassDataIterator();
425 
426     virtual bool hasNext() const;
427     virtual void next(QScript::Member *member);
428 
429     virtual bool hasPrevious() const;
430     virtual void previous(QScript::Member *member);
431 
432     virtual void toFront();
433     virtual void toBack();
434 
435 private:
436     ArgumentsObjectData *m_data;
437     uint m_pos;
438 };
439 
resolve(const QScriptValueImpl & object,QScriptNameIdImpl * nameId,QScript::Member * member,QScriptValueImpl * base,QScript::AccessMode)440 bool ArgumentsClassData::resolve(const QScriptValueImpl &object, QScriptNameIdImpl *nameId,
441                                  QScript::Member *member, QScriptValueImpl *base,
442                                  QScript::AccessMode /*access*/)
443 {
444     QString propertyName = object.engine()->toString(nameId);
445     bool isNumber;
446     quint32 index = propertyName.toUInt(&isNumber);
447     if (isNumber) {
448         QScript::ArgumentsObjectData *data = ArgumentsClassData::get(object);
449         if (index < data->length) {
450             member->native(/*nameId=*/0, index, QScriptValue::SkipInEnumeration);
451             *base = object;
452             return true;
453         }
454     }
455 
456     return false;
457 }
458 
get(const QScriptValueImpl & object,const QScript::Member & member,QScriptValueImpl * out_value)459 bool ArgumentsClassData::get(const QScriptValueImpl &object, const QScript::Member &member,
460                              QScriptValueImpl *out_value)
461 {
462     QScript::ArgumentsObjectData *data = ArgumentsClassData::get(object);
463     if (member.nameId() == 0) {
464         QScriptObject *activation_data = data->activation.objectValue();
465         *out_value = activation_data->m_values[member.id()];
466         return true;
467     }
468     return false;
469 }
470 
put(QScriptValueImpl * object,const QScript::Member & member,const QScriptValueImpl & value)471 bool ArgumentsClassData::put(QScriptValueImpl *object, const QScript::Member &member,
472                              const QScriptValueImpl &value)
473 {
474     Q_ASSERT(member.nameId() == 0);
475     QScript::ArgumentsObjectData *data = ArgumentsClassData::get(*object);
476     QScriptObject *activation_data = data->activation.objectValue();
477     activation_data->m_values[member.id()] = value;
478     return true;
479 }
480 
mark(const QScriptValueImpl & object,int generation)481 void ArgumentsClassData::mark(const QScriptValueImpl &object, int generation)
482 {
483     QScript::ArgumentsObjectData *data = ArgumentsClassData::get(object);
484     data->activation.mark(generation);
485 }
486 
newIterator(const QScriptValueImpl & object)487 QScriptClassDataIterator *ArgumentsClassData::newIterator(const QScriptValueImpl &object)
488 {
489     QScript::ArgumentsObjectData *data = ArgumentsClassData::get(object);
490     return new ArgumentsClassDataIterator(data);
491 }
492 
ArgumentsClassDataIterator(ArgumentsObjectData * data)493 ArgumentsClassDataIterator::ArgumentsClassDataIterator(ArgumentsObjectData *data)
494     : m_data(data), m_pos(0)
495 {
496 }
497 
~ArgumentsClassDataIterator()498 ArgumentsClassDataIterator::~ArgumentsClassDataIterator()
499 {
500 }
501 
hasNext() const502 bool ArgumentsClassDataIterator::hasNext() const
503 {
504     return m_pos < m_data->length;
505 }
506 
next(QScript::Member * member)507 void ArgumentsClassDataIterator::next(QScript::Member *member)
508 {
509     if (m_pos == m_data->length) {
510         member->invalidate();
511     } else {
512         member->native(/*nameId=*/0, m_pos, QScriptValue::SkipInEnumeration);
513         ++m_pos;
514     }
515 }
516 
hasPrevious() const517 bool ArgumentsClassDataIterator::hasPrevious() const
518 {
519     return (m_pos != 0);
520 }
521 
previous(QScript::Member * member)522 void ArgumentsClassDataIterator::previous(QScript::Member *member)
523 {
524     if (m_pos == 0) {
525         member->invalidate();
526     } else {
527         --m_pos;
528         member->native(/*nameId=*/0, m_pos, QScriptValue::SkipInEnumeration);
529     }
530 }
531 
toFront()532 void ArgumentsClassDataIterator::toFront()
533 {
534     m_pos = 0;
535 }
536 
toBack()537 void ArgumentsClassDataIterator::toBack()
538 {
539     m_pos = m_data->length;
540 }
541 
542 } // namespace QScript
543 
544 const qsreal QScriptEnginePrivate::D16 = 65536.0;
545 const qsreal QScriptEnginePrivate::D32 = 4294967296.0;
546 
~QScriptEnginePrivate()547 QScriptEnginePrivate::~QScriptEnginePrivate()
548 {
549     while (!m_agents.isEmpty())
550         delete m_agents.takeFirst();
551 
552     // invalidate values that we have references to
553     {
554         QHash<QScriptObject*, QScriptValuePrivate*>::const_iterator it;
555         for (it = m_objectHandles.constBegin(); it != m_objectHandles.constEnd(); ++it)
556             (*it)->invalidate();
557     }
558     {
559         QHash<QScriptNameIdImpl*, QScriptValuePrivate*>::const_iterator it;
560         for (it = m_stringHandles.constBegin(); it != m_stringHandles.constEnd(); ++it)
561             (*it)->invalidate();
562     }
563     {
564         QVector<QScriptValuePrivate*>::const_iterator it;
565         for (it = m_otherHandles.constBegin(); it != m_otherHandles.constEnd(); ++it)
566             (*it)->invalidate();
567     }
568 
569     // invalidate interned strings that are known to the outside world
570     {
571         QHash<QScriptNameIdImpl*, QScriptStringPrivate*>::const_iterator it;
572         for (it = m_internedStrings.constBegin(); it != m_internedStrings.constEnd(); ++it)
573             it.value()->nameId = 0;
574     }
575 
576     delete[] m_string_hash_base;
577     qDeleteAll(m_stringRepository);
578     qDeleteAll(m_tempStringRepository);
579 
580     if (tempStackBegin)
581         delete[] tempStackBegin;
582 
583 #ifndef QT_NO_QOBJECT
584     deletePendingQObjects();
585     qDeleteAll(m_qobjectData);
586 # ifndef Q_SCRIPT_NO_QMETAOBJECT_CACHE
587     qDeleteAll(m_cachedMetaObjects);
588 # endif
589 #endif
590 
591     qDeleteAll(m_allocated_classes);
592 }
593 
changeAbstractSyntaxTree(QScript::AST::Node * prg)594 QScript::AST::Node *QScriptEnginePrivate::changeAbstractSyntaxTree(QScript::AST::Node *prg)
595 {
596     QScript::AST::Node *was = m_abstractSyntaxTree;
597     m_abstractSyntaxTree = prg;
598     return was;
599 }
600 
createAbstractSyntaxTree(const QString & source,int lineNumber,QString * errorMessage,int * errorLineNumber)601 QScript::AST::Node *QScriptEnginePrivate::createAbstractSyntaxTree(
602     const QString &source, int lineNumber, QString *errorMessage, int *errorLineNumber)
603 {
604     QScript::Lexer lex(this);
605     setLexer(&lex);
606     lex.setCode(source, lineNumber);
607 
608     QScriptParser parser;
609 
610     if (! parser.parse(this)) {
611         if (errorMessage)
612             *errorMessage = parser.errorMessage();
613         if (errorLineNumber)
614             *errorLineNumber = parser.errorLineNumber();
615         return 0;
616     }
617 
618     return abstractSyntaxTree();
619 }
620 
markObject(const QScriptValueImpl & object,int generation)621 void QScriptEnginePrivate::markObject(const QScriptValueImpl &object, int generation)
622 {
623     QScriptObject *instance = object.objectValue();
624     QScript::GCBlock *block = QScript::GCBlock::get(instance);
625 
626     enum { MAX_GC_DEPTH = 32 };
627 
628     if (block->generation + 1 != generation)
629         return;
630 
631     if (m_gc_depth >= MAX_GC_DEPTH) {
632         // do the marking later
633         m_markStack.append(object);
634         return;
635     }
636 
637     ++block->generation;
638     ++m_gc_depth;
639 
640     if (QScriptClassData *data = object.classInfo()->data())
641         data->mark(object, generation);
642 
643     if (instance->m_prototype.isObject())
644         markObject(instance->m_prototype, generation);
645 
646     if (instance->m_scope.isObject())
647         markObject(instance->m_scope, generation);
648 
649     const QScriptValueImpl &internalValue = instance->m_internalValue;
650 
651     if (internalValue.isValid()) {
652         if (internalValue.isObject())
653             markObject(internalValue, generation);
654 
655         else if (internalValue.isString())
656             markString(internalValue.m_string_value, generation);
657     }
658 
659     int garbage = 0;
660 
661     for (int i = 0; i < instance->memberCount(); ++i) {
662         QScript::Member m;
663         instance->member(i, &m);
664 
665         if (! m.isValid()) {
666             ++garbage;
667             continue;
668         }
669 
670         Q_ASSERT(m.isObjectProperty());
671 
672         QScriptValueImpl child;
673         instance->get(m, &child);
674 
675         if (m.nameId())
676             markString(m.nameId(), generation);
677 
678         if (! child.isValid())
679             continue;
680 
681         else if (child.isObject())
682             markObject(child, generation);
683 
684         else if (child.isString())
685             markString(child.m_string_value, generation);
686     }
687 
688     --m_gc_depth;
689 
690     if (garbage < 128) // ###
691         return;
692 
693     int j = 0;
694     for (int i = 0; i < instance->memberCount(); ++i) {
695         QScript::Member m;
696         instance->member(i, &m);
697 
698         if (! m.isValid())
699             continue;
700 
701         if (i != j) {
702             instance->m_members[j].object(m.nameId(), j, m.flags());
703             instance->m_values[j] = instance->m_values[i];
704         }
705         ++j;
706     }
707     //qDebug() << "==> old:" << instance->m_members.size() << "new:" << j;
708     instance->m_members.resize(j);
709     instance->m_values.resize(j);
710 }
711 
markFrame(QScriptContextPrivate * context,int generation)712 void QScriptEnginePrivate::markFrame(QScriptContextPrivate *context, int generation)
713 {
714     QScriptValueImpl activation = context->activationObject();
715     QScriptValueImpl thisObject = context->thisObject();
716     QScriptValueImpl scopeChain = context->m_scopeChain;
717     QScriptValueImpl callee = context->m_callee;
718     QScriptValueImpl arguments = context->m_arguments;
719 
720     if (activation.isObject())
721         markObject(activation, generation);
722 
723     if (scopeChain.isObject())
724         markObject(scopeChain, generation);
725 
726     if (thisObject.isObject())
727         markObject(thisObject, generation);
728 
729     if (callee.isObject())
730         markObject(callee, generation);
731 
732     if (arguments.isObject())
733         markObject(arguments, generation);
734 
735     if (context->returnValue().isValid()) {
736         if (context->returnValue().isObject())
737             markObject(context->returnValue(), generation);
738 
739         else if (context->returnValue().isString())
740             markString(context->returnValue().m_string_value, generation);
741     }
742 
743     if (context->baseStackPointer() != context->currentStackPointer()) {
744         // mark the temp stack
745 
746         for (const QScriptValueImpl *it = context->baseStackPointer(); it != (context->currentStackPointer() + 1); ++it) {
747             if (! it) {
748                 qWarning() << "no temp stack!!!";
749                 break;
750             }
751 
752             else if (! it->isValid()) // ### assert?
753                 continue;
754 
755             else if (it->isObject())
756                 markObject(*it, generation);
757 
758             else if (it->isString())
759                 markString(it->m_string_value, generation);
760         }
761     }
762 }
763 
isCollecting() const764 bool QScriptEnginePrivate::isCollecting() const
765 {
766     return (m_gc_depth != -1) || objectAllocator.sweeping();
767 }
768 
maybeGC_helper(bool do_string_gc)769 void QScriptEnginePrivate::maybeGC_helper(bool do_string_gc)
770 {
771     // qDebug() << "==>" << objectAllocator.newAllocatedBlocks() << "free:" << objectAllocator.freeBlocks();
772     Q_ASSERT(m_gc_depth == -1);
773     ++m_gc_depth;
774 
775     int generation = m_objectGeneration + 1;
776 
777     markObject(m_globalObject, generation);
778 
779     objectConstructor->mark(this, generation);
780     numberConstructor->mark(this, generation);
781     booleanConstructor->mark(this, generation);
782     stringConstructor->mark(this, generation);
783     dateConstructor->mark(this, generation);
784     functionConstructor->mark(this, generation);
785     arrayConstructor->mark(this, generation);
786     regexpConstructor->mark(this, generation);
787     errorConstructor->mark(this, generation);
788     enumerationConstructor->mark(this, generation);
789     variantConstructor->mark(this, generation);
790 #ifndef QT_NO_QOBJECT
791     qobjectConstructor->mark(this, generation);
792     qmetaObjectConstructor->mark(this, generation);
793 #endif
794 
795     {
796         QScriptContextPrivate *current = currentContext();
797         while (current != 0) {
798             markFrame (current, generation);
799             current = current->parentContext();
800         }
801     }
802 
803     {
804         QHash<QScriptObject*, QScriptValuePrivate*>::const_iterator it;
805         for (it = m_objectHandles.constBegin(); it != m_objectHandles.constEnd(); ++it)
806             markObject((*it)->value, generation);
807     }
808 
809     {
810         QHash<QScriptNameIdImpl*, QScriptValuePrivate*>::const_iterator it;
811         for (it = m_stringHandles.constBegin(); it != m_stringHandles.constEnd(); ++it)
812             markString((*it)->value.stringValue(), generation);
813     }
814 
815     {
816         QHash<int, QScriptCustomTypeInfo>::const_iterator it;
817         for (it = m_customTypes.constBegin(); it != m_customTypes.constEnd(); ++it)
818             (*it).prototype.mark(generation);
819     }
820 
821 #ifndef QT_NO_QOBJECT
822 # ifndef Q_SCRIPT_NO_QMETAOBJECT_CACHE
823     {
824         QHash<const QMetaObject*, QScriptMetaObject*>::const_iterator it;
825         for (it = m_cachedMetaObjects.constBegin(); it != m_cachedMetaObjects.constEnd(); ++it) {
826             {
827                 QList<QScriptNameIdImpl*> memberNames = (*it)->registeredMemberNames();
828                 QList<QScriptNameIdImpl*>::const_iterator it2;
829                 for (it2 = memberNames.constBegin(); it2 != memberNames.constEnd(); ++it2)
830                     markString(*it2, generation);
831             }
832             {
833                 QList<QScriptValueImpl> propertyAccessors = (*it)->registeredPropertyAccessors();
834                 QList<QScriptValueImpl>::const_iterator it2;
835                 for (it2 = propertyAccessors.constBegin(); it2 != propertyAccessors.constEnd(); ++it2)
836                     markObject(*it2, generation);
837             }
838         }
839     }
840 # endif
841     processMarkStack(generation); // make sure everything is marked before marking qobject data
842     {
843         QHash<QObject*, QScriptQObjectData*>::const_iterator it;
844         for (it = m_qobjectData.constBegin(); it != m_qobjectData.constEnd(); ++it) {
845             QScriptQObjectData *qdata = it.value();
846             qdata->mark(generation);
847         }
848     }
849 #endif
850     processMarkStack(generation);
851 
852     Q_ASSERT(m_gc_depth == 0);
853     --m_gc_depth;
854 
855     objectAllocator.sweep(generation);
856 
857     m_objectGeneration = generation;
858 
859     //qDebug() << "free blocks:" << objectAllocator.freeBlocks();
860 
861 #ifndef QT_NO_QOBJECT
862     deletePendingQObjects();
863 #endif
864 
865     if (! do_string_gc)
866         return;
867 
868     {
869         QHash<QScriptNameIdImpl*, QScriptStringPrivate*>::const_iterator it;
870         for (it = m_internedStrings.constBegin(); it != m_internedStrings.constEnd(); ++it) {
871             it.value()->nameId->used = true;
872         }
873     }
874 
875 #if 0
876     qDebug() << "do_string_gc:" << do_string_gc
877         << ((m_stringRepository.size() - m_oldStringRepositorySize) > 256)
878         << ((m_tempStringRepository.size() - m_oldTempStringRepositorySize) > 2048);
879 #endif
880 
881     QVector<QScriptNameIdImpl*> compressed;
882     compressed.reserve(m_stringRepository.size());
883 
884     for (int i = 0; i < m_stringRepository.size(); ++i) {
885         QScriptNameIdImpl *entry = m_stringRepository.at(i);
886 
887         if (entry->used || entry->persistent) {
888             compressed.append(entry);
889             entry->used = false;
890         }
891 
892         else {
893             //qDebug() << "deleted unique:" << entry->s;
894             delete entry;
895       }
896     }
897 
898     // qDebug() << "before:" << m_stringRepository.size() << "after:" << compressed.size() << globalObject.objectValue()->m_members.size();
899     m_stringRepository = compressed;
900     rehashStringRepository(/*resize=*/ false);
901     m_oldStringRepositorySize = m_stringRepository.size();
902     m_newAllocatedStringRepositoryChars = 0;
903 
904     compressed.clear();
905     for (int i = 0; i < m_tempStringRepository.size(); ++i) {
906         QScriptNameIdImpl *entry = m_tempStringRepository.at(i);
907 
908         if (entry->used || entry->persistent) {
909             compressed.append(entry);
910             entry->used = false;
911         }
912 
913         else {
914           //qDebug() << "deleted:" << entry->s;
915             delete entry;
916       }
917     }
918 
919     //qDebug() << "before:" << m_tempStringRepository.size() << "after:" << compressed.size();
920 
921     m_tempStringRepository = compressed;
922     m_oldTempStringRepositorySize = m_tempStringRepository.size();
923     m_newAllocatedTempStringRepositoryChars = 0;
924 }
925 
processMarkStack(int generation)926 void QScriptEnginePrivate::processMarkStack(int generation)
927 {
928     // mark the objects we couldn't process due to recursion depth
929     while (!m_markStack.isEmpty())
930         markObject(m_markStack.takeLast(), generation);
931 }
932 
evaluate(QScriptContextPrivate * context,const QString & contents,int lineNumber,const QString & fileName)933 void QScriptEnginePrivate::evaluate(QScriptContextPrivate *context, const QString &contents, int lineNumber, const QString &fileName)
934 {
935     // ### try to remove cast
936     QScript::EvalFunction *evalFunction = static_cast<QScript::EvalFunction*>(m_evalFunction);
937     evalFunction->evaluate(context, contents, lineNumber, fileName, /*calledFromScript=*/ false);
938 }
939 
convertToNativeDouble_helper(const QScriptValueImpl & value)940 qsreal QScriptEnginePrivate::convertToNativeDouble_helper(const QScriptValueImpl &value)
941 {
942     switch (value.type()) {
943     case QScript::InvalidType:
944         Q_ASSERT(value.isValid());
945         break;
946 
947     case QScript::UndefinedType:
948     case QScript::PointerType:
949         break;
950 
951     case QScript::NullType:
952         return 0;
953 
954     case QScript::BooleanType:
955         return value.m_bool_value;
956 
957     case QScript::IntegerType:
958     case QScript::ReferenceType:
959         return value.m_int_value;
960 
961     case QScript::NumberType:
962         return value.m_number_value;
963 
964     case QScript::StringType:
965         return QScript::numberFromString(toString(value.m_string_value));
966 
967     case QScript::ObjectType: {
968         QScriptValueImpl p = value.engine()->toPrimitive(value, QScriptValueImpl::NumberTypeHint);
969         if (! p.isValid() || p.isObject())
970             break;
971 
972         return convertToNativeDouble(p);
973     }
974 
975     case QScript::LazyStringType:
976         return QScript::numberFromString(*value.m_lazy_string_value);
977 
978     } // switch
979 
980     return qSNaN();
981 }
982 
convertToNativeBoolean_helper(const QScriptValueImpl & value)983 bool QScriptEnginePrivate::convertToNativeBoolean_helper(const QScriptValueImpl &value)
984 {
985     switch (value.type()) {
986     case QScript::InvalidType:
987         Q_ASSERT(value.isValid());
988         return false;
989 
990     case QScript::UndefinedType:
991     case QScript::PointerType:
992     case QScript::NullType:
993     case QScript::ReferenceType:
994         return false;
995 
996     case QScript::BooleanType:
997         return value.m_bool_value;
998 
999     case QScript::IntegerType:
1000         return value.m_int_value != 0;
1001 
1002     case QScript::NumberType:
1003         return value.m_number_value != 0 && !qIsNaN(value.m_number_value);
1004 
1005     case QScript::StringType:
1006         return toString(value.m_string_value).length() != 0;
1007 
1008     case QScript::ObjectType:
1009         return true;
1010 
1011     case QScript::LazyStringType:
1012         return value.m_lazy_string_value->length() != 0;
1013 
1014     } // switch
1015 
1016     return false;
1017 }
1018 
convertToNativeString_helper(const QScriptValueImpl & value)1019 QString QScriptEnginePrivate::convertToNativeString_helper(const QScriptValueImpl &value)
1020 {
1021     static QStringList predefined;
1022     if (predefined.isEmpty()) {
1023         predefined.append(QString::fromLatin1("undefined"));
1024         predefined.append(QString::fromLatin1("null"));
1025         predefined.append(QString::fromLatin1("true"));
1026         predefined.append(QString::fromLatin1("false"));
1027         predefined.append(QString::fromLatin1("pointer"));
1028     }
1029 
1030     switch (value.type()) {
1031     case QScript::InvalidType:
1032         Q_ASSERT(value.isValid());
1033         return QString();
1034 
1035     case QScript::UndefinedType:
1036         return predefined.at(0);
1037 
1038     case QScript::NullType:
1039         return predefined.at(1);
1040 
1041     case QScript::BooleanType:
1042         return value.m_bool_value ? predefined.at(2) : predefined.at(3);
1043 
1044     case QScript::IntegerType:
1045         return QString::number(value.m_int_value);
1046 
1047     case QScript::NumberType:
1048         return QScript::numberToString(value.m_number_value);
1049 
1050     case QScript::PointerType:
1051         return predefined.at(4);
1052 
1053     case QScript::StringType:
1054         return toString(value.m_string_value);
1055 
1056     case QScript::ReferenceType:
1057         return QString();
1058 
1059     case QScript::ObjectType: {
1060         QScriptValueImpl p = value.engine()->toPrimitive(value, QScriptValueImpl::StringTypeHint);
1061 
1062         if (!p.isValid() || strictlyEquals(p, value))
1063             return p.classInfo()->name();
1064 
1065         return convertToNativeString(p);
1066     }
1067 
1068     case QScript::LazyStringType:
1069         return *value.m_lazy_string_value;
1070 
1071     } // switch
1072 
1073     return QString();
1074 }
1075 
toObject_helper(const QScriptValueImpl & value)1076 QScriptValueImpl QScriptEnginePrivate::toObject_helper(const QScriptValueImpl &value)
1077 {
1078     QScriptValueImpl result;
1079     switch (value.type()) {
1080     case QScript::BooleanType:
1081         booleanConstructor->newBoolean(&result, value.m_bool_value);
1082         break;
1083 
1084     case QScript::NumberType:
1085         numberConstructor->newNumber(&result, value.m_number_value);
1086         break;
1087 
1088     case QScript::StringType:
1089         stringConstructor->newString(&result, value.m_string_value->s);
1090         break;
1091 
1092     case QScript::LazyStringType:
1093         stringConstructor->newString(&result, *value.m_lazy_string_value);
1094         break;
1095 
1096     case QScript::InvalidType:
1097     case QScript::UndefinedType:
1098     case QScript::NullType:
1099     case QScript::IntegerType:
1100     case QScript::ReferenceType:
1101     case QScript::PointerType:
1102     case QScript::ObjectType:
1103         break;
1104     } // switch
1105 
1106     return result;
1107 }
1108 
1109 // [[defaultValue]]
toPrimitive_helper(const QScriptValueImpl & object,QScriptValueImpl::TypeHint hint)1110 QScriptValueImpl QScriptEnginePrivate::toPrimitive_helper(const QScriptValueImpl &object,
1111                                                           QScriptValueImpl::TypeHint hint)
1112 {
1113     QScriptNameIdImpl *functionIds[2];
1114 
1115     if ((hint == QScriptValueImpl::NumberTypeHint)
1116         || (hint == QScriptValueImpl::NoTypeHint
1117             && object.classInfo() != dateConstructor->classInfo())) {
1118         functionIds[0] = idTable()->id_valueOf;
1119         functionIds[1] = idTable()->id_toString;
1120     } else {
1121         functionIds[0] = idTable()->id_toString;
1122         functionIds[1] = idTable()->id_valueOf;
1123     }
1124 
1125     for (int i = 0; i < 2; ++i) {
1126         QScriptValueImpl base;
1127         QScript::Member member;
1128 
1129         if (! object.resolve(functionIds[i], &member, &base, QScriptValue::ResolvePrototype, QScript::Read))
1130             return object;
1131 
1132         QScriptValueImpl f_valueOf;
1133         base.get(member, &f_valueOf);
1134 
1135         if (QScriptFunction *foo = convertToNativeFunction(f_valueOf)) {
1136             QScriptContextPrivate *me = pushContext();
1137             QScriptValueImpl activation;
1138             newActivation(&activation);
1139             if (f_valueOf.scope().isValid())
1140                 activation.setScope(f_valueOf.scope());
1141             else
1142                 activation.setScope(m_globalObject);
1143             me->setActivationObject(activation);
1144             me->setThisObject(object);
1145             me->m_callee = f_valueOf;
1146             foo->execute(me);
1147             QScriptValueImpl result = me->returnValue();
1148             bool exception = (me->state() == QScriptContext::ExceptionState);
1149             popContext();
1150             if (exception || (result.isValid() && !result.isObject()))
1151                 return result;
1152         }
1153     }
1154 
1155     return object;
1156 }
1157 
rehashStringRepository(bool resize)1158 void QScriptEnginePrivate::rehashStringRepository(bool resize)
1159 {
1160     if (resize) {
1161         delete[] m_string_hash_base;
1162         m_string_hash_size <<= 1; // ### use primes
1163 
1164         m_string_hash_base = new QScriptNameIdImpl* [m_string_hash_size];
1165     }
1166 
1167     memset(m_string_hash_base, 0, sizeof(QScriptNameIdImpl*) * m_string_hash_size);
1168 
1169     for (int index = 0; index < m_stringRepository.size(); ++index) {
1170         QScriptNameIdImpl *entry = m_stringRepository.at(index);
1171         uint h = _q_scriptHash(entry->s) % m_string_hash_size;
1172         entry->h = h;
1173         entry->next = m_string_hash_base[h];
1174         m_string_hash_base[h] = entry;
1175     }
1176 }
1177 
insertStringEntry(const QString & s)1178 QScriptNameIdImpl *QScriptEnginePrivate::insertStringEntry(const QString &s)
1179 {
1180     QScriptNameIdImpl *entry = new QScriptNameIdImpl(s);
1181     entry->unique = true;
1182     m_stringRepository.append(entry);
1183     m_newAllocatedStringRepositoryChars += s.length();
1184 
1185     uint h = _q_scriptHash(s) % m_string_hash_size;
1186     entry->h = h;
1187     entry->next = m_string_hash_base[h];
1188     m_string_hash_base[h] = entry;
1189 
1190     if (m_stringRepository.count() == m_string_hash_size)
1191         rehashStringRepository();
1192 
1193     return entry;
1194 }
1195 
call(const QScriptValueImpl & callee,const QScriptValueImpl & thisObject,const QScriptValueImplList & args,bool asConstructor)1196 QScriptValueImpl QScriptEnginePrivate::call(const QScriptValueImpl &callee,
1197                                         const QScriptValueImpl &thisObject,
1198                                         const QScriptValueImplList &args,
1199                                         bool asConstructor)
1200 {
1201     QScriptFunction *function = callee.toFunction();
1202     Q_ASSERT(function);
1203 
1204     if (++m_callDepth == m_maxCallDepth) {
1205         QScriptContextPrivate *ctx_p = currentContext();
1206         return ctx_p->throwError(QLatin1String("call stack overflow"));
1207     }
1208 
1209     QScriptContextPrivate *nested = pushContext();
1210     // set up the temp stack
1211     if (! nested->tempStack)
1212         nested->stackPtr = nested->tempStack = tempStackBegin;
1213 
1214     newActivation(&nested->m_activation);
1215     if (callee.m_object_value->m_scope.isValid())
1216         nested->m_activation.m_object_value->m_scope = callee.m_object_value->m_scope;
1217     else
1218         nested->m_activation.m_object_value->m_scope = m_globalObject;
1219 
1220     QScriptObject *activation_data = nested->m_activation.m_object_value;
1221 
1222     int formalCount = function->formals.count();
1223     int argc = args.count();
1224     int mx = qMax(formalCount, argc);
1225     activation_data->m_members.resize(mx);
1226     activation_data->m_values.resize(mx);
1227     for (int i = 0; i < mx; ++i) {
1228         QScriptNameIdImpl *nameId = 0;
1229         if (i < formalCount)
1230             nameId = function->formals.at(i);
1231 
1232         activation_data->m_members[i].object(nameId, i, QScriptValue::SkipInEnumeration);
1233         QScriptValueImpl arg = (i < argc) ? args.at(i) : m_undefinedValue;
1234         if (arg.isValid() && arg.engine() && (arg.engine() != this)) {
1235             qWarning("QScriptValue::call() failed: "
1236                      "cannot call function with argument created in "
1237                      "a different engine");
1238             popContext();
1239             return QScriptValueImpl();
1240         }
1241         activation_data->m_values[i] = arg.isValid() ? arg : m_undefinedValue;
1242     }
1243 
1244     nested->argc = argc;
1245     QVector<QScriptValueImpl> argsv = args.toVector();
1246     nested->args = const_cast<QScriptValueImpl*> (argsv.constData());
1247 
1248     if (thisObject.isObject())
1249         nested->m_thisObject = thisObject;
1250     else
1251         nested->m_thisObject = m_globalObject;
1252     nested->m_callee = callee;
1253     nested->m_calledAsConstructor = asConstructor;
1254 
1255     nested->m_result = m_undefinedValue;
1256     function->execute(nested);
1257     --m_callDepth;
1258     QScriptValueImpl result = nested->m_result;
1259     nested->args = 0;
1260     popContext();
1261 
1262     return result;
1263 }
1264 
call(const QScriptValueImpl & callee,const QScriptValueImpl & thisObject,const QScriptValueImpl & args,bool asConstructor)1265 QScriptValueImpl QScriptEnginePrivate::call(const QScriptValueImpl &callee,
1266                                         const QScriptValueImpl &thisObject,
1267                                         const QScriptValueImpl &args,
1268                                         bool asConstructor)
1269 {
1270     QScriptValueImplList argsList;
1271     if (QScript::Ecma::Array::Instance *arr = arrayConstructor->get(args)) {
1272         QScript::Array actuals = arr->value;
1273         for (quint32 i = 0; i < actuals.count(); ++i) {
1274             QScriptValueImpl a = actuals.at(i);
1275             if (! a.isValid())
1276                 argsList << undefinedValue();
1277             else
1278                 argsList << a;
1279         }
1280     } else if (args.classInfo() == m_class_arguments) {
1281         QScript::ArgumentsObjectData *arguments;
1282         arguments = static_cast<QScript::ArgumentsObjectData*> (args.objectData());
1283         QScriptObject *activation = arguments->activation.objectValue();
1284         for (uint i = 0; i < arguments->length; ++i)
1285             argsList << activation->m_values[i];
1286     } else if (!(args.isUndefined() || args.isNull())) {
1287         return currentContext()->throwError(
1288             QScriptContext::TypeError,
1289             QLatin1String("QScriptValue::call(): arguments must be an array"));
1290     }
1291     return call(callee, thisObject, argsList, asConstructor);
1292 }
1293 
arrayFromStringList(const QStringList & lst)1294 QScriptValueImpl QScriptEnginePrivate::arrayFromStringList(const QStringList &lst)
1295 {
1296     QScriptValueImpl arr = newArray(lst.size());
1297     for (int i = 0; i < lst.size(); ++i)
1298         arr.setProperty(i, QScriptValueImpl(this, lst.at(i)));
1299     return arr;
1300 }
1301 
stringListFromArray(const QScriptValueImpl & arr)1302 QStringList QScriptEnginePrivate::stringListFromArray(const QScriptValueImpl &arr)
1303 {
1304     QStringList lst;
1305     uint len = arr.property(QLatin1String("length")).toUInt32();
1306     for (uint i = 0; i < len; ++i)
1307         lst.append(arr.property(i).toString());
1308     return lst;
1309 }
1310 
arrayFromVariantList(const QVariantList & lst)1311 QScriptValueImpl QScriptEnginePrivate::arrayFromVariantList(const QVariantList &lst)
1312 {
1313     QScriptValueImpl arr = newArray(lst.size());
1314     for (int i = 0; i < lst.size(); ++i)
1315         arr.setProperty(i, valueFromVariant(lst.at(i)));
1316     return arr;
1317 }
1318 
variantListFromArray(const QScriptValueImpl & arr)1319 QVariantList QScriptEnginePrivate::variantListFromArray(const QScriptValueImpl &arr)
1320 {
1321     QVariantList lst;
1322     uint len = arr.property(QLatin1String("length")).toUInt32();
1323     for (uint i = 0; i < len; ++i)
1324         lst.append(arr.property(i).toVariant());
1325     return lst;
1326 }
1327 
objectFromVariantMap(const QVariantMap & vmap)1328 QScriptValueImpl QScriptEnginePrivate::objectFromVariantMap(const QVariantMap &vmap)
1329 {
1330     QScriptValueImpl obj = newObject();
1331     QVariantMap::const_iterator it;
1332     for (it = vmap.constBegin(); it != vmap.constEnd(); ++it)
1333         obj.setProperty(it.key(), valueFromVariant(it.value()));
1334     return obj;
1335 }
1336 
variantMapFromObject(const QScriptValueImpl & obj)1337 QVariantMap QScriptEnginePrivate::variantMapFromObject(const QScriptValueImpl &obj)
1338 {
1339     QVariantMap vmap;
1340     QScriptValueIteratorImpl it(obj);
1341     while (it.hasNext()) {
1342         it.next();
1343         vmap.insert(it.name(), it.value().toVariant());
1344     }
1345     return vmap;
1346 }
1347 
create(int type,const void * ptr)1348 QScriptValueImpl QScriptEnginePrivate::create(int type, const void *ptr)
1349 {
1350     Q_Q(QScriptEngine);
1351     Q_ASSERT(ptr);
1352     QScriptValueImpl result;
1353     QScriptCustomTypeInfo info = m_customTypes.value(type);
1354     if (info.marshal) {
1355         result = toImpl(info.marshal(q, ptr));
1356     } else {
1357         // check if it's one of the types we know
1358         switch (QMetaType::Type(type)) {
1359         case QMetaType::Void:
1360             result = m_undefinedValue;
1361             break;
1362         case QMetaType::Bool:
1363             result = QScriptValueImpl(*reinterpret_cast<const bool*>(ptr));
1364             break;
1365         case QMetaType::Int:
1366             result = QScriptValueImpl(*reinterpret_cast<const int*>(ptr));
1367             break;
1368         case QMetaType::UInt:
1369             result = QScriptValueImpl(*reinterpret_cast<const uint*>(ptr));
1370             break;
1371         case QMetaType::LongLong:
1372             result = QScriptValueImpl(qsreal(*reinterpret_cast<const qlonglong*>(ptr)));
1373             break;
1374         case QMetaType::ULongLong:
1375 #if defined(Q_OS_WIN) && defined(_MSC_FULL_VER) && _MSC_FULL_VER <= 12008804
1376 #pragma message("** NOTE: You need the Visual Studio Processor Pack to compile support for 64bit unsigned integers.")
1377             result = QScriptValueImpl(qsreal((qlonglong)*reinterpret_cast<const qulonglong*>(ptr)));
1378 #elif defined(Q_CC_MSVC) && !defined(Q_CC_MSVC_NET)
1379             result = QScriptValueImpl(qsreal((qlonglong)*reinterpret_cast<const qulonglong*>(ptr)));
1380 #else
1381             result = QScriptValueImpl(qsreal(*reinterpret_cast<const qulonglong*>(ptr)));
1382 #endif
1383             break;
1384         case QMetaType::Double:
1385             result = QScriptValueImpl(*reinterpret_cast<const double*>(ptr));
1386             break;
1387         case QMetaType::QString:
1388             result = QScriptValueImpl(this, *reinterpret_cast<const QString*>(ptr));
1389             break;
1390         case QMetaType::Float:
1391             result = QScriptValueImpl(*reinterpret_cast<const float*>(ptr));
1392             break;
1393         case QMetaType::Short:
1394             result = QScriptValueImpl(*reinterpret_cast<const short*>(ptr));
1395             break;
1396         case QMetaType::UShort:
1397             result = QScriptValueImpl(*reinterpret_cast<const unsigned short*>(ptr));
1398             break;
1399         case QMetaType::Char:
1400             result = QScriptValueImpl(*reinterpret_cast<const char*>(ptr));
1401             break;
1402         case QMetaType::UChar:
1403             result = QScriptValueImpl(*reinterpret_cast<const unsigned char*>(ptr));
1404             break;
1405         case QMetaType::QChar:
1406             result = QScriptValueImpl((*reinterpret_cast<const QChar*>(ptr)).unicode());
1407             break;
1408         case QMetaType::QStringList:
1409             result = arrayFromStringList(*reinterpret_cast<const QStringList *>(ptr));
1410             break;
1411         case QMetaType::QVariantList:
1412             result = arrayFromVariantList(*reinterpret_cast<const QVariantList *>(ptr));
1413             break;
1414         case QMetaType::QVariantMap:
1415             result = objectFromVariantMap(*reinterpret_cast<const QVariantMap *>(ptr));
1416             break;
1417         case QMetaType::QDateTime: {
1418             QDateTime dateTime = *reinterpret_cast<const QDateTime *>(ptr);
1419             dateConstructor->newDate(&result, dateTime);
1420         } break;
1421         case QMetaType::QDate: {
1422             QDate date = *reinterpret_cast<const QDate *>(ptr);
1423             dateConstructor->newDate(&result, date);
1424         } break;
1425 #ifndef QT_NO_REGEXP
1426         case QMetaType::QRegExp: {
1427             QRegExp rx = *reinterpret_cast<const QRegExp *>(ptr);
1428             regexpConstructor->newRegExp(&result, rx);
1429         } break;
1430 #endif
1431 #ifndef QT_NO_QOBJECT
1432         case QMetaType::QObjectStar:
1433         case QMetaType::QWidgetStar:
1434             newQObject(&result, *reinterpret_cast<QObject* const *>(ptr));
1435             break;
1436 #endif
1437         default:
1438             if (type == qMetaTypeId<QScriptValue>()) {
1439                 result = toImpl(*reinterpret_cast<const QScriptValue*>(ptr));
1440                 if (!result.isValid())
1441                     result = m_undefinedValue;
1442             }
1443 
1444 #ifndef QT_NO_QOBJECT
1445             // lazy registration of some common list types
1446             else if (type == qMetaTypeId<QObjectList>()) {
1447                 qScriptRegisterSequenceMetaType<QObjectList>(q);
1448                 return create(type, ptr);
1449             }
1450 #endif
1451             else if (type == qMetaTypeId<QList<int> >()) {
1452                 qScriptRegisterSequenceMetaType<QList<int> >(q);
1453                 return create(type, ptr);
1454             }
1455 
1456             else {
1457                 QByteArray typeName = QMetaType::typeName(type);
1458                 if (typeName == "QVariant")
1459                     result = valueFromVariant(*reinterpret_cast<const QVariant*>(ptr));
1460                 else if (typeName.endsWith('*') && !*reinterpret_cast<void* const *>(ptr))
1461                     result = nullValue();
1462                 else
1463                     newVariant(&result, QVariant(type, ptr));
1464             }
1465         }
1466     }
1467     if (result.isObject() && info.prototype.isValid()
1468         && strictlyEquals(result.prototype(), objectConstructor->publicPrototype)) {
1469         result.setPrototype(info.prototype);
1470     }
1471     return result;
1472 }
1473 
convert(const QScriptValueImpl & value,int type,void * ptr,QScriptEnginePrivate * eng)1474 bool QScriptEnginePrivate::convert(const QScriptValueImpl &value,
1475                                    int type, void *ptr,
1476                                    QScriptEnginePrivate *eng)
1477 {
1478     if (!eng)
1479         eng = value.engine();
1480     if (eng) {
1481         QScriptCustomTypeInfo info = eng->m_customTypes.value(type);
1482         if (info.demarshal) {
1483             info.demarshal(eng->toPublic(value), ptr);
1484             return true;
1485         }
1486     }
1487 
1488     // check if it's one of the types we know
1489     switch (QMetaType::Type(type)) {
1490     case QMetaType::Bool:
1491         *reinterpret_cast<bool*>(ptr) = value.toBoolean();
1492         return true;
1493     case QMetaType::Int:
1494         *reinterpret_cast<int*>(ptr) = value.toInt32();
1495         return true;
1496     case QMetaType::UInt:
1497         *reinterpret_cast<uint*>(ptr) = value.toUInt32();
1498         return true;
1499     case QMetaType::LongLong:
1500         *reinterpret_cast<qlonglong*>(ptr) = qlonglong(value.toInteger());
1501         return true;
1502     case QMetaType::ULongLong:
1503         *reinterpret_cast<qulonglong*>(ptr) = qulonglong(value.toInteger());
1504         return true;
1505     case QMetaType::Double:
1506         *reinterpret_cast<double*>(ptr) = value.toNumber();
1507         return true;
1508     case QMetaType::QString:
1509         if (value.isUndefined() || value.isNull())
1510             *reinterpret_cast<QString*>(ptr) = QString();
1511         else
1512             *reinterpret_cast<QString*>(ptr) = value.toString();
1513         return true;
1514     case QMetaType::Float:
1515         *reinterpret_cast<float*>(ptr) = value.toNumber();
1516         return true;
1517     case QMetaType::Short:
1518         *reinterpret_cast<short*>(ptr) = short(value.toInt32());
1519         return true;
1520     case QMetaType::UShort:
1521         *reinterpret_cast<unsigned short*>(ptr) = value.toUInt16();
1522         return true;
1523     case QMetaType::Char:
1524         *reinterpret_cast<char*>(ptr) = char(value.toInt32());
1525         return true;
1526     case QMetaType::UChar:
1527         *reinterpret_cast<unsigned char*>(ptr) = (unsigned char)(value.toInt32());
1528         return true;
1529     case QMetaType::QChar:
1530         if (value.isString()) {
1531             QString str = value.toString();
1532             *reinterpret_cast<QChar*>(ptr) = str.isEmpty() ? QChar() : str.at(0);
1533         } else {
1534             *reinterpret_cast<QChar*>(ptr) = QChar(value.toUInt16());
1535         }
1536         return true;
1537     case QMetaType::QDateTime:
1538         if (value.isDate()) {
1539             *reinterpret_cast<QDateTime *>(ptr) = value.toDateTime();
1540             return true;
1541         } break;
1542     case QMetaType::QDate:
1543         if (value.isDate()) {
1544             *reinterpret_cast<QDate *>(ptr) = value.toDateTime().date();
1545             return true;
1546         } break;
1547 #ifndef QT_NO_REGEXP
1548     case QMetaType::QRegExp:
1549         if (value.isRegExp()) {
1550             *reinterpret_cast<QRegExp *>(ptr) = value.toRegExp();
1551             return true;
1552         } break;
1553 #endif
1554 #ifndef QT_NO_QOBJECT
1555     case QMetaType::QObjectStar:
1556         if (value.isQObject() || value.isNull()) {
1557             *reinterpret_cast<QObject* *>(ptr) = value.toQObject();
1558             return true;
1559         } break;
1560     case QMetaType::QWidgetStar:
1561         if (value.isQObject() || value.isNull()) {
1562             QObject *qo = value.toQObject();
1563             if (!qo || qo->isWidgetType()) {
1564                 *reinterpret_cast<QWidget* *>(ptr) = reinterpret_cast<QWidget*>(qo);
1565                 return true;
1566             }
1567         } break;
1568 #endif
1569     case QMetaType::QStringList:
1570         if (value.isArray()) {
1571             *reinterpret_cast<QStringList *>(ptr) = stringListFromArray(value);
1572             return true;
1573         } break;
1574     case QMetaType::QVariantList:
1575         if (value.isArray()) {
1576             *reinterpret_cast<QVariantList *>(ptr) = variantListFromArray(value);
1577             return true;
1578         } break;
1579     case QMetaType::QVariantMap:
1580         if (value.isObject()) {
1581             *reinterpret_cast<QVariantMap *>(ptr) = variantMapFromObject(value);
1582             return true;
1583         } break;
1584     default:
1585     ;
1586     }
1587 
1588     QByteArray name = QMetaType::typeName(type);
1589 #ifndef QT_NO_QOBJECT
1590     if (convertToNativeQObject(value, name, reinterpret_cast<void* *>(ptr)))
1591         return true;
1592 #endif
1593     if (value.isVariant() && name.endsWith('*')) {
1594         int valueType = QMetaType::type(name.left(name.size()-1));
1595         QVariant &var = value.variantValue();
1596         if (valueType == var.userType()) {
1597             *reinterpret_cast<void* *>(ptr) = var.data();
1598             return true;
1599         } else {
1600             // look in the prototype chain
1601             QScriptValueImpl proto = value.prototype();
1602             while (proto.isObject()) {
1603                 bool canCast = false;
1604                 if (proto.isVariant()) {
1605                     canCast = (type == proto.variantValue().userType())
1606                               || (valueType && (valueType == proto.variantValue().userType()));
1607                 }
1608 #ifndef QT_NO_QOBJECT
1609                 else if (proto.isQObject()) {
1610                     QByteArray className = name.left(name.size()-1);
1611                     if (QObject *qobject = proto.toQObject())
1612                         canCast = qobject->qt_metacast(className) != 0;
1613                 }
1614 #endif
1615                 if (canCast) {
1616                     QByteArray varTypeName = QMetaType::typeName(var.userType());
1617                     if (varTypeName.endsWith('*'))
1618                         *reinterpret_cast<void* *>(ptr) = *reinterpret_cast<void* *>(var.data());
1619                     else
1620                         *reinterpret_cast<void* *>(ptr) = var.data();
1621                     return true;
1622                 }
1623                 proto = proto.prototype();
1624             }
1625         }
1626     } else if (value.isNull() && name.endsWith('*')) {
1627         *reinterpret_cast<void* *>(ptr) = 0;
1628         return true;
1629     } else if (type == qMetaTypeId<QScriptValue>()) {
1630         if (!eng)
1631             return false;
1632         *reinterpret_cast<QScriptValue*>(ptr) = eng->toPublic(value);
1633         return true;
1634     } else if (name == "QVariant") {
1635         *reinterpret_cast<QVariant*>(ptr) = value.toVariant();
1636         return true;
1637     }
1638 
1639     // lazy registration of some common list types
1640 #ifndef QT_NO_QOBJECT
1641     else if (type == qMetaTypeId<QObjectList>()) {
1642         if (!eng)
1643             return false;
1644         qScriptRegisterSequenceMetaType<QObjectList>(eng->q_func());
1645         return convert(value, type, ptr, eng);
1646     }
1647 #endif
1648     else if (type == qMetaTypeId<QList<int> >()) {
1649         if (!eng)
1650             return false;
1651         qScriptRegisterSequenceMetaType<QList<int> >(eng->q_func());
1652         return convert(value, type, ptr, eng);
1653     }
1654 
1655 #if 0
1656     if (!name.isEmpty()) {
1657         qWarning("QScriptEngine::convert: unable to convert value to type `%s'",
1658                  name.constData());
1659     }
1660 #endif
1661     return false;
1662 }
1663 
demarshalFunction(int type) const1664 QScriptEngine::DemarshalFunction QScriptEnginePrivate::demarshalFunction(int type) const
1665 {
1666     return m_customTypes.value(type).demarshal;
1667 }
1668 
registerValue(const QScriptValueImpl & value)1669 QScriptValuePrivate *QScriptEnginePrivate::registerValue(const QScriptValueImpl &value)
1670 {
1671     if (value.isString()) {
1672         QScriptNameIdImpl *id = value.stringValue();
1673         QScriptValuePrivate *p = m_stringHandles.value(id);
1674         if (p)
1675             return p;
1676         p = m_handleRepository.get();
1677         p->engine = q_func();
1678         p->value = value;
1679         m_stringHandles.insert(id, p);
1680         return p;
1681     } else if (value.isObject()) {
1682         QScriptObject *instance = value.objectValue();
1683         QScriptValuePrivate *p = m_objectHandles.value(instance);
1684         if (p)
1685             return p;
1686         p = m_handleRepository.get();
1687         p->engine = q_func();
1688         p->value = value;
1689         m_objectHandles.insert(instance, p);
1690         return p;
1691     }
1692     QScriptValuePrivate *p = m_handleRepository.get();
1693     p->engine = q_func();
1694     p->value = value;
1695     m_otherHandles.append(p);
1696     return p;
1697 }
1698 
QScriptEnginePrivate()1699 QScriptEnginePrivate::QScriptEnginePrivate()
1700 {
1701     m_undefinedValue = QScriptValueImpl(QScriptValue::UndefinedValue);
1702     m_nullValue = QScriptValueImpl(QScriptValue::NullValue);
1703 
1704     m_evaluating = false;
1705     m_abort = false;
1706     m_callDepth = 0;
1707 #if defined(Q_OS_WIN)
1708     m_maxCallDepth = 88;
1709 #elif defined(Q_OS_MAC)
1710     m_maxCallDepth = 640;
1711 #elif defined(QT_ARCH_ARM) || defined(QT_ARCH_ARMV6)
1712     m_maxCallDepth = 360;
1713 #else
1714     m_maxCallDepth = 512;
1715 #endif
1716     m_oldStringRepositorySize = 0;
1717     m_oldTempStringRepositorySize = 0;
1718     m_newAllocatedStringRepositoryChars = 0;
1719     m_newAllocatedTempStringRepositoryChars = 0;
1720     m_context = 0;
1721     m_abstractSyntaxTree = 0;
1722     m_lexer = 0;
1723     m_scriptCounter = 0;
1724     m_agent = 0;
1725     m_objectGeneration = 0;
1726     m_class_prev_id = QScriptClassInfo::CustomType;
1727     m_next_object_id = 0;
1728     m_gc_depth = -1;
1729 
1730     objectConstructor = 0;
1731     numberConstructor = 0;
1732     booleanConstructor = 0;
1733     stringConstructor = 0;
1734     dateConstructor = 0;
1735     functionConstructor = 0;
1736     arrayConstructor = 0;
1737     regexpConstructor = 0;
1738     errorConstructor = 0;
1739     enumerationConstructor = 0;
1740     variantConstructor = 0;
1741     qobjectConstructor = 0;
1742     qmetaObjectConstructor = 0;
1743 
1744     m_processEventsInterval = -1;
1745     m_nextProcessEvents = 0;
1746     m_processEventIncr = 0;
1747 
1748     m_stringRepository.reserve(DefaultHashSize);
1749     m_string_hash_size = DefaultHashSize;
1750     m_string_hash_base = new QScriptNameIdImpl* [m_string_hash_size];
1751     memset(m_string_hash_base, 0, sizeof(QScriptNameIdImpl*) * m_string_hash_size);
1752 
1753     tempStackBegin = 0;
1754 }
1755 
init()1756 void QScriptEnginePrivate::init()
1757 {
1758     qMetaTypeId<QScriptValue>();
1759     qMetaTypeId<QList<int> >();
1760 #ifndef QT_NO_QOBJECT
1761     qMetaTypeId<QObjectList>();
1762 #endif
1763 
1764     m_class_prev_id = QScriptClassInfo::CustomType;
1765     m_class_object = registerClass(QLatin1String("Object"), QScriptClassInfo::ObjectType);
1766     m_class_function = registerClass(QLatin1String("Function"), QScriptClassInfo::FunctionType);
1767     m_class_activation = registerClass(QLatin1String("activation"), QScriptClassInfo::ActivationType);
1768 
1769     m_class_arguments = registerClass(QLatin1String("arguments"), QScript::ObjectType);
1770     m_class_arguments->setData(new QScript::ArgumentsClassData());
1771 
1772     m_class_with = registerClass(QLatin1String("__qscript_internal_with"), QScript::ObjectType);
1773 
1774     // public name ids
1775     m_id_table.id_constructor = nameId(QLatin1String("constructor"), true);
1776     m_id_table.id_false       = nameId(QLatin1String("false"), true);
1777     m_id_table.id_null        = nameId(QLatin1String("null"), true);
1778     m_id_table.id_object      = nameId(QLatin1String("object"), true);
1779     m_id_table.id_pointer     = nameId(QLatin1String("pointer"), true);
1780     m_id_table.id_prototype   = nameId(QLatin1String("prototype"), true);
1781     m_id_table.id_arguments   = nameId(QLatin1String("arguments"), true);
1782     m_id_table.id_this        = nameId(QLatin1String("this"), true);
1783     m_id_table.id_toString    = nameId(QLatin1String("toString"), true);
1784     m_id_table.id_true        = nameId(QLatin1String("true"), true);
1785     m_id_table.id_undefined   = nameId(QLatin1String("undefined"), true);
1786     m_id_table.id_valueOf     = nameId(QLatin1String("valueOf"), true);
1787     m_id_table.id_length      = nameId(QLatin1String("length"), true);
1788     m_id_table.id_callee      = nameId(QLatin1String("callee"), true);
1789     m_id_table.id___proto__   = nameId(QLatin1String("__proto__"), true);
1790     m_id_table.id___qt_sender__  = nameId(QLatin1String("__qt_sender__"), true);
1791 
1792     const int TEMP_STACK_SIZE = 10 * 1024;
1793     tempStackBegin = new QScriptValueImpl[TEMP_STACK_SIZE];
1794     tempStackEnd = tempStackBegin + TEMP_STACK_SIZE;
1795     tempStackBegin[0] = m_undefinedValue;
1796 
1797     objectAllocator.blockGC(true);
1798 
1799     QScript::Ecma::Global::construct(&m_globalObject, this);
1800 
1801     // create the prototypes first...
1802     objectConstructor = new QScript::Ecma::Object(this, m_class_object);
1803     functionConstructor = new QScript::Ecma::Function(this, m_class_function);
1804     // ... then we can initialize
1805     functionConstructor->initialize();
1806     objectConstructor->initialize();
1807 
1808     numberConstructor = new QScript::Ecma::Number(this);
1809     booleanConstructor = new QScript::Ecma::Boolean(this);
1810     stringConstructor = new QScript::Ecma::String(this);
1811     dateConstructor = new QScript::Ecma::Date(this);
1812     arrayConstructor = new QScript::Ecma::Array(this);
1813     regexpConstructor = new QScript::Ecma::RegExp(this);
1814     errorConstructor = new QScript::Ecma::Error(this);
1815 
1816     QScript::Ecma::Global::initialize(&m_globalObject, this);
1817 
1818     const QScriptValue::PropertyFlags flags = QScriptValue::SkipInEnumeration;
1819 
1820     m_globalObject.setProperty(QLatin1String("Object"),
1821                              objectConstructor->ctor, flags);
1822     m_globalObject.setProperty(QLatin1String("Function"),
1823                              functionConstructor->ctor, flags);
1824     m_globalObject.setProperty(QLatin1String("Number"),
1825                              numberConstructor->ctor, flags);
1826     m_globalObject.setProperty(QLatin1String("Boolean"),
1827                              booleanConstructor->ctor, flags);
1828     m_globalObject.setProperty(QLatin1String("String"),
1829                              stringConstructor->ctor, flags);
1830     m_globalObject.setProperty(QLatin1String("Date"),
1831                              dateConstructor->ctor, flags);
1832     m_globalObject.setProperty(QLatin1String("Array"),
1833                              arrayConstructor->ctor, flags);
1834     m_globalObject.setProperty(QLatin1String("RegExp"),
1835                              regexpConstructor->ctor, flags);
1836     m_globalObject.setProperty(QLatin1String("Error"),
1837                              errorConstructor->ctor, flags);
1838 
1839     m_globalObject.setProperty(QLatin1String("EvalError"),
1840                              errorConstructor->evalErrorCtor, flags);
1841     m_globalObject.setProperty(QLatin1String("RangeError"),
1842                              errorConstructor->rangeErrorCtor, flags);
1843     m_globalObject.setProperty(QLatin1String("ReferenceError"),
1844                              errorConstructor->referenceErrorCtor, flags);
1845     m_globalObject.setProperty(QLatin1String("SyntaxError"),
1846                              errorConstructor->syntaxErrorCtor, flags);
1847     m_globalObject.setProperty(QLatin1String("TypeError"),
1848                              errorConstructor->typeErrorCtor, flags);
1849     m_globalObject.setProperty(QLatin1String("URIError"),
1850                              errorConstructor->uriErrorCtor, flags);
1851 
1852     QScriptValueImpl tmp; // ### fixme
1853     m_evalFunction = new QScript::EvalFunction(this);
1854     functionConstructor->newFunction(&tmp, m_evalFunction);
1855     m_globalObject.setProperty(QLatin1String("eval"), tmp, flags);
1856 
1857     QScriptValueImpl mathObject;
1858     QScript::Ecma::Math::construct(&mathObject, this);
1859     m_globalObject.setProperty(QLatin1String("Math"), mathObject, flags);
1860 
1861     enumerationConstructor = new QScript::Ext::Enumeration(this);
1862 
1863     variantConstructor = new QScript::Ext::Variant(this);
1864 
1865 #ifndef QT_NO_QOBJECT
1866     qobjectConstructor = new QScript::ExtQObject(this);
1867     qmetaObjectConstructor = new QScript::ExtQMetaObject(this);
1868 #endif
1869 
1870     objectAllocator.blockGC(false);
1871 
1872     QScriptContextPrivate *context_p = pushContext();
1873     context_p->setActivationObject(m_globalObject);
1874     context_p->setThisObject(m_globalObject);
1875 }
1876 
1877 #if !defined(QT_NO_QOBJECT) && !defined(QT_NO_LIBRARY)
__setupPackage__(QScriptContextPrivate * ctx,QScriptEnginePrivate * eng,QScriptClassInfo *)1878 static QScriptValueImpl __setupPackage__(QScriptContextPrivate *ctx,
1879                                          QScriptEnginePrivate *eng,
1880                                          QScriptClassInfo *)
1881 {
1882     QString path = ctx->argument(0).toString();
1883     QStringList components = path.split(QLatin1Char('.'));
1884     QScriptValueImpl o = eng->globalObject();
1885     for (int i = 0; i < components.count(); ++i) {
1886         QString name = components.at(i);
1887         QScriptValueImpl oo = o.property(name);
1888         if (!oo.isValid()) {
1889             oo = eng->newObject();
1890             o.setProperty(name, oo);
1891         }
1892         o = oo;
1893     }
1894     return o;
1895 }
1896 #endif
1897 
importExtension(const QString & extension)1898 QScriptValueImpl QScriptEnginePrivate::importExtension(const QString &extension)
1899 {
1900 #if defined(QT_NO_QOBJECT) || defined(QT_NO_LIBRARY) || defined(QT_NO_SETTINGS)
1901     Q_UNUSED(extension);
1902 #else
1903     Q_Q(QScriptEngine);
1904     if (m_importedExtensions.contains(extension))
1905         return undefinedValue(); // already imported
1906 
1907     QScriptContextPrivate *context = currentContext();
1908     QCoreApplication *app = QCoreApplication::instance();
1909     if (!app)
1910         return context->throwError(QLatin1String("No application object"));
1911 
1912     QObjectList staticPlugins = QPluginLoader::staticInstances();
1913     QStringList libraryPaths = app->libraryPaths();
1914     QString dot = QLatin1String(".");
1915     QStringList pathComponents = extension.split(dot);
1916     QString initDotJs = QLatin1String("__init__.js");
1917 
1918     QString ext;
1919     for (int i = 0; i < pathComponents.count(); ++i) {
1920         if (!ext.isEmpty())
1921             ext.append(dot);
1922         ext.append(pathComponents.at(i));
1923         if (m_importedExtensions.contains(ext))
1924             continue; // already imported
1925 
1926         if (m_extensionsBeingImported.contains(ext)) {
1927             return context->throwError(QString::fromLatin1("recursive import of %0")
1928                                        .arg(extension));
1929         }
1930         m_extensionsBeingImported.insert(ext);
1931 
1932         QScriptExtensionInterface *iface = 0;
1933         QString initjsContents;
1934         QString initjsFileName;
1935 
1936         // look for the extension in static plugins
1937         for (int j = 0; j < staticPlugins.size(); ++j) {
1938             iface = qobject_cast<QScriptExtensionInterface*>(staticPlugins.at(j));
1939             if (!iface)
1940                 continue;
1941             if (iface->keys().contains(ext))
1942                 break; // use this one
1943             else
1944                 iface = 0; // keep looking
1945         }
1946 
1947         {
1948             // look for __init__.js resource
1949             QString path = QString::fromLatin1(":/qtscriptextension");
1950             for (int j = 0; j <= i; ++j) {
1951                 path.append(QLatin1Char('/'));
1952                 path.append(pathComponents.at(j));
1953             }
1954             path.append(QLatin1Char('/'));
1955             path.append(initDotJs);
1956             QFile file(path);
1957             if (file.open(QIODevice::ReadOnly)) {
1958                 QTextStream ts(&file);
1959                 initjsContents = ts.readAll();
1960                 initjsFileName = path;
1961                 file.close();
1962             }
1963         }
1964 
1965         if (!iface && initjsContents.isEmpty()) {
1966             // look for the extension in library paths
1967             for (int j = 0; j < libraryPaths.count(); ++j) {
1968                 QString libPath = libraryPaths.at(j) + QDir::separator() + QLatin1String("script");
1969                 QDir dir(libPath);
1970                 if (!dir.exists(dot))
1971                     continue;
1972 
1973                 // look for C++ plugin
1974                 QFileInfoList files = dir.entryInfoList(QDir::Files);
1975                 for (int k = 0; k < files.count(); ++k) {
1976                     QFileInfo entry = files.at(k);
1977                     QString filePath = entry.canonicalFilePath();
1978                     QPluginLoader loader(filePath);
1979                     iface = qobject_cast<QScriptExtensionInterface*>(loader.instance());
1980                     if (iface) {
1981                         if (iface->keys().contains(ext))
1982                             break; // use this one
1983                         else
1984                             iface = 0; // keep looking
1985                     }
1986                 }
1987 
1988                 // look for __init__.js in the corresponding dir
1989                 QDir dirdir(libPath);
1990                 bool dirExists = dirdir.exists();
1991                 for (int k = 0; dirExists && (k <= i); ++k)
1992                     dirExists = dirdir.cd(pathComponents.at(k));
1993                 if (dirExists && dirdir.exists(initDotJs)) {
1994                     QFile file(dirdir.canonicalPath()
1995                                + QDir::separator() + initDotJs);
1996                     if (file.open(QIODevice::ReadOnly)) {
1997                         QTextStream ts(&file);
1998                         initjsContents = ts.readAll();
1999                         initjsFileName = file.fileName();
2000                         file.close();
2001                     }
2002                 }
2003 
2004                 if (iface || !initjsContents.isEmpty())
2005                     break;
2006             }
2007         }
2008 
2009         if (!iface && initjsContents.isEmpty()) {
2010             m_extensionsBeingImported.remove(ext);
2011             return context->throwError(
2012                 QString::fromLatin1("Unable to import %0: no such extension")
2013                 .arg(extension));
2014         }
2015 
2016         // initialize the extension in a new context
2017         QScriptContextPrivate *ctx_p = pushContext();
2018         ctx_p->setThisObject(globalObject());
2019         newActivation(&ctx_p->m_activation);
2020         QScriptObject *activation_data = ctx_p->m_activation.m_object_value;
2021         activation_data->m_scope = globalObject();
2022 
2023         activation_data->m_members.resize(4);
2024         activation_data->m_values.resize(4);
2025         activation_data->m_members[0].object(
2026             nameId(QLatin1String("__extension__")), 0,
2027             QScriptValue::ReadOnly | QScriptValue::Undeletable);
2028         activation_data->m_values[0] = QScriptValueImpl(this, ext);
2029         activation_data->m_members[1].object(
2030             nameId(QLatin1String("__setupPackage__")), 1, 0);
2031         activation_data->m_values[1] = createFunction(__setupPackage__, 0, 0);
2032         activation_data->m_members[2].object(
2033             nameId(QLatin1String("__all__")), 2, 0);
2034         activation_data->m_values[2] = undefinedValue();
2035         activation_data->m_members[3].object(
2036             nameId(QLatin1String("__postInit__")), 3, 0);
2037         activation_data->m_values[3] = undefinedValue();
2038 
2039         // the script is evaluated first
2040         if (!initjsContents.isEmpty()) {
2041             evaluate(ctx_p, initjsContents, /*lineNumber=*/1, initjsFileName);
2042             if (hasUncaughtException()) {
2043                 QScriptValueImpl r = ctx_p->returnValue();
2044                 popContext();
2045                 m_extensionsBeingImported.remove(ext);
2046                 return r;
2047             }
2048         }
2049 
2050         // next, the C++ plugin is called
2051         if (iface) {
2052             iface->initialize(ext, q);
2053             if (hasUncaughtException()) {
2054                 QScriptValueImpl r = ctx_p->returnValue();
2055                 popContext();
2056                 m_extensionsBeingImported.remove(ext);
2057                 return r;
2058             }
2059         }
2060 
2061         // if the __postInit__ function has been set, we call it
2062         QScriptValueImpl postInit = ctx_p->m_activation.property(QLatin1String("__postInit__"));
2063         if (postInit.isFunction()) {
2064             postInit.call(globalObject());
2065             if (hasUncaughtException()) {
2066                 QScriptValueImpl r = ctx_p->returnValue();
2067                 popContext();
2068                 m_extensionsBeingImported.remove(ext);
2069                 return r;
2070             }
2071         }
2072 
2073         popContext();
2074 
2075         m_importedExtensions.insert(ext);
2076         m_extensionsBeingImported.remove(ext);
2077     } // for (i)
2078 #endif // QT_NO_QOBJECT
2079     return undefinedValue();
2080 }
2081 
availableExtensions() const2082 QStringList QScriptEnginePrivate::availableExtensions() const
2083 {
2084 #if defined(QT_NO_QOBJECT) || defined(QT_NO_LIBRARY) || defined(QT_NO_SETTINGS)
2085     return QStringList();
2086 #else
2087     QCoreApplication *app = QCoreApplication::instance();
2088     if (!app)
2089         return QStringList();
2090 
2091     QSet<QString> result;
2092 
2093     QObjectList staticPlugins = QPluginLoader::staticInstances();
2094     for (int i = 0; i < staticPlugins.size(); ++i) {
2095         QScriptExtensionInterface *iface;
2096         iface = qobject_cast<QScriptExtensionInterface*>(staticPlugins.at(i));
2097         if (iface) {
2098             QStringList keys = iface->keys();
2099             for (int j = 0; j < keys.count(); ++j)
2100                 result << keys.at(j);
2101         }
2102     }
2103 
2104     QStringList libraryPaths = app->libraryPaths();
2105     for (int i = 0; i < libraryPaths.count(); ++i) {
2106         QString libPath = libraryPaths.at(i) + QDir::separator() + QLatin1String("script");
2107         QDir dir(libPath);
2108         if (!dir.exists())
2109             continue;
2110 
2111         // look for C++ plugins
2112         QFileInfoList files = dir.entryInfoList(QDir::Files);
2113         for (int j = 0; j < files.count(); ++j) {
2114             QFileInfo entry = files.at(j);
2115             QString filePath = entry.canonicalFilePath();
2116             QPluginLoader loader(filePath);
2117             QScriptExtensionInterface *iface;
2118             iface = qobject_cast<QScriptExtensionInterface*>(loader.instance());
2119             if (iface) {
2120                 QStringList keys = iface->keys();
2121                 for (int k = 0; k < keys.count(); ++k)
2122                     result << keys.at(k);
2123             }
2124         }
2125 
2126         // look for scripts
2127         QString initDotJs = QLatin1String("__init__.js");
2128         QList<QFileInfo> stack;
2129         stack << dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
2130         while (!stack.isEmpty()) {
2131             QFileInfo entry = stack.takeLast();
2132             QDir dd(entry.canonicalFilePath());
2133             if (dd.exists(initDotJs)) {
2134                 QString rpath = dir.relativeFilePath(dd.canonicalPath());
2135                 QStringList components = rpath.split(QLatin1Char('/'));
2136                 result << components.join(QLatin1String("."));
2137                 stack << dd.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
2138             }
2139         }
2140     }
2141 
2142     QStringList lst = result.toList();
2143     qSort(lst);
2144     return lst;
2145 #endif
2146 }
2147 
importedExtensions() const2148 QStringList QScriptEnginePrivate::importedExtensions() const
2149 {
2150     QStringList lst = m_importedExtensions.toList();
2151     qSort(lst);
2152     return lst;
2153 }
2154 
gc()2155 void QScriptEnginePrivate::gc()
2156 {
2157     if (!objectAllocator.blocked()) {
2158         // do the GC now
2159         maybeGC_helper(/*do_string_gc=*/true);
2160     } else {
2161         // GC will be performed the next time maybeGC()
2162         // is called and the allocator is not blocked
2163         objectAllocator.requestGC();
2164     }
2165 }
2166 
uncaughtExceptionBacktrace() const2167 QStringList QScriptEnginePrivate::uncaughtExceptionBacktrace() const
2168 {
2169     QScriptValueImpl value = uncaughtException();
2170     if (!value.isError())
2171         return m_exceptionBacktrace;
2172     return QScript::Ecma::Error::backtrace(value);
2173 }
2174 
clearExceptions()2175 void QScriptEnginePrivate::clearExceptions()
2176 {
2177     m_exceptionBacktrace = QStringList();
2178     QScriptContextPrivate *ctx_p = currentContext();
2179     while (ctx_p) {
2180         ctx_p->m_state = QScriptContext::NormalState;
2181         ctx_p = ctx_p->parentContext();
2182     }
2183 }
2184 
2185 #ifndef QT_NO_QOBJECT
emitSignalHandlerException()2186 void QScriptEnginePrivate::emitSignalHandlerException()
2187 {
2188     Q_Q(QScriptEngine);
2189     emit q->signalHandlerException(toPublic(uncaughtException()));
2190 }
2191 #endif
2192 
processEvents()2193 void QScriptEnginePrivate::processEvents()
2194 {
2195 #ifndef QT_NO_QOBJECT
2196     Q_ASSERT(m_processEventTracker.isValid());
2197     int elapsed = m_processEventTracker.elapsed();
2198     if (m_nextProcessEvents < elapsed) {
2199         do {
2200             m_nextProcessEvents = m_nextProcessEvents + m_processEventsInterval;
2201         } while (m_nextProcessEvents < elapsed);
2202         QCoreApplication::processEvents();
2203     }
2204 #endif
2205 }
2206 
setupProcessEvents()2207 void QScriptEnginePrivate::setupProcessEvents()
2208 {
2209     if (m_processEventsInterval > 0) {
2210         m_nextProcessEvents = m_processEventsInterval;
2211         m_processEventIncr = 0;
2212         m_processEventTracker.restart();
2213     }
2214 }
2215 
abortEvaluation(const QScriptValueImpl & result)2216 void QScriptEnginePrivate::abortEvaluation(const QScriptValueImpl &result)
2217 {
2218     m_abort = true;
2219     currentContext()->setReturnValue(result);
2220 }
2221 
2222 #ifndef QT_NO_QOBJECT
2223 
newQObject(QScriptValueImpl * out,QObject * object,QScriptEngine::ValueOwnership ownership,const QScriptEngine::QObjectWrapOptions & options,bool setDefaultPrototype)2224 void QScriptEnginePrivate::newQObject(QScriptValueImpl *out, QObject *object,
2225                                       QScriptEngine::ValueOwnership ownership,
2226                                       const QScriptEngine::QObjectWrapOptions &options,
2227                                       bool setDefaultPrototype)
2228 {
2229     if (!object) {
2230         *out = m_nullValue;
2231         return;
2232     }
2233     Q_ASSERT(qobjectConstructor != 0);
2234     QScriptQObjectData *data = qobjectData(object);
2235     bool preferExisting = (options & QScriptEngine::PreferExistingWrapperObject) != 0;
2236     QScriptEngine::QObjectWrapOptions opt = options & ~QScriptEngine::PreferExistingWrapperObject;
2237     QScriptValueImpl existingWrapper;
2238     bool hasExisting = data->findWrapper(ownership, opt, &existingWrapper);
2239     if (preferExisting) {
2240         if (hasExisting) {
2241             *out = existingWrapper;
2242         } else {
2243             qobjectConstructor->newQObject(out, object, ownership, opt);
2244             data->registerWrapper(*out, ownership, opt);
2245         }
2246     } else {
2247         qobjectConstructor->newQObject(out, object, ownership, opt);
2248         if (!hasExisting)
2249             data->registerWrapper(*out, ownership, opt);
2250     }
2251 
2252     if (setDefaultPrototype) {
2253         const QMetaObject *meta = object->metaObject();
2254         while (meta) {
2255             QByteArray typeString = meta->className();
2256             typeString.append('*');
2257             int typeId = QMetaType::type(typeString);
2258             if (typeId != 0) {
2259                 QScriptValueImpl proto = defaultPrototype(typeId);
2260                 if (proto.isValid()) {
2261                     out->setPrototype(proto);
2262                     break;
2263                 }
2264             }
2265             meta = meta->superClass();
2266         }
2267     }
2268 }
2269 
qobjectData(QObject * object)2270 QScriptQObjectData *QScriptEnginePrivate::qobjectData(QObject *object)
2271 {
2272     QHash<QObject*, QScriptQObjectData*>::const_iterator it;
2273     it = m_qobjectData.constFind(object);
2274     if (it != m_qobjectData.constEnd())
2275         return it.value();
2276 
2277     QScriptQObjectData *data = new QScriptQObjectData();
2278     m_qobjectData.insert(object, data);
2279     QObject::connect(object, SIGNAL(destroyed(QObject*)),
2280                      q_func(), SLOT(_q_objectDestroyed(QObject *)));
2281     return data;
2282 }
2283 
_q_objectDestroyed(QObject * object)2284 void QScriptEnginePrivate::_q_objectDestroyed(QObject *object)
2285 {
2286     QHash<QObject*, QScriptQObjectData*>::iterator it;
2287     it = m_qobjectData.find(object);
2288     Q_ASSERT(it != m_qobjectData.end());
2289     QScriptQObjectData *data = it.value();
2290     m_qobjectData.erase(it);
2291     delete data;
2292 }
2293 
disposeQObject(QObject * object)2294 void QScriptEnginePrivate::disposeQObject(QObject *object)
2295 {
2296     if (isCollecting()) {
2297         // wait until we're done with GC before deleting it
2298         int index = m_qobjectsToBeDeleted.indexOf(object);
2299         if (index == -1)
2300             m_qobjectsToBeDeleted.append(object);
2301     } else {
2302         delete object;
2303     }
2304 }
2305 
deletePendingQObjects()2306 void QScriptEnginePrivate::deletePendingQObjects()
2307 {
2308     while (!m_qobjectsToBeDeleted.isEmpty())
2309         delete m_qobjectsToBeDeleted.takeFirst();
2310 }
2311 
scriptConnect(QObject * sender,const char * signal,const QScriptValueImpl & receiver,const QScriptValueImpl & function,Qt::ConnectionType type)2312 bool QScriptEnginePrivate::scriptConnect(QObject *sender, const char *signal,
2313                                          const QScriptValueImpl &receiver,
2314                                          const QScriptValueImpl &function,
2315                                          Qt::ConnectionType type)
2316 {
2317     Q_ASSERT(sender);
2318     Q_ASSERT(signal);
2319     const QMetaObject *meta = sender->metaObject();
2320     int index = meta->indexOfSignal(QMetaObject::normalizedSignature(signal+1));
2321     if (index == -1)
2322         return false;
2323     return scriptConnect(sender, index, receiver, function, /*wrapper=*/QScriptValueImpl(), type);
2324 }
2325 
scriptDisconnect(QObject * sender,const char * signal,const QScriptValueImpl & receiver,const QScriptValueImpl & function)2326 bool QScriptEnginePrivate::scriptDisconnect(QObject *sender, const char *signal,
2327                                             const QScriptValueImpl &receiver,
2328                                             const QScriptValueImpl &function)
2329 {
2330     Q_ASSERT(sender);
2331     Q_ASSERT(signal);
2332     const QMetaObject *meta = sender->metaObject();
2333     int index = meta->indexOfSignal(QMetaObject::normalizedSignature(signal+1));
2334     if (index == -1)
2335         return false;
2336     return scriptDisconnect(sender, index, receiver, function);
2337 }
2338 
scriptConnect(QObject * sender,int signalIndex,const QScriptValueImpl & receiver,const QScriptValueImpl & function,const QScriptValueImpl & senderWrapper,Qt::ConnectionType type)2339 bool QScriptEnginePrivate::scriptConnect(QObject *sender, int signalIndex,
2340                                          const QScriptValueImpl &receiver,
2341                                          const QScriptValueImpl &function,
2342                                          const QScriptValueImpl &senderWrapper,
2343                                          Qt::ConnectionType type)
2344 {
2345     QScriptQObjectData *data = qobjectData(sender);
2346     return data->addSignalHandler(sender, signalIndex, receiver, function, senderWrapper, type);
2347 }
2348 
scriptDisconnect(QObject * sender,int signalIndex,const QScriptValueImpl & receiver,const QScriptValueImpl & function)2349 bool QScriptEnginePrivate::scriptDisconnect(QObject *sender, int signalIndex,
2350                                             const QScriptValueImpl &receiver,
2351                                             const QScriptValueImpl &function)
2352 {
2353     QScriptQObjectData *data = qobjectData(sender);
2354     if (!data)
2355         return false;
2356     return data->removeSignalHandler(sender, signalIndex, receiver, function);
2357 }
2358 
scriptConnect(const QScriptValueImpl & signal,const QScriptValueImpl & receiver,const QScriptValueImpl & function,Qt::ConnectionType type)2359 bool QScriptEnginePrivate::scriptConnect(const QScriptValueImpl &signal,
2360                                          const QScriptValueImpl &receiver,
2361                                          const QScriptValueImpl &function,
2362                                          Qt::ConnectionType type)
2363 {
2364     QScript::QtFunction *fun = static_cast<QScript::QtFunction*>(signal.toFunction());
2365     int index = fun->mostGeneralMethod();
2366     return scriptConnect(fun->qobject(), index, receiver, function, fun->object(), type);
2367 }
2368 
scriptDisconnect(const QScriptValueImpl & signal,const QScriptValueImpl & receiver,const QScriptValueImpl & function)2369 bool QScriptEnginePrivate::scriptDisconnect(const QScriptValueImpl &signal,
2370                                             const QScriptValueImpl &receiver,
2371                                             const QScriptValueImpl &function)
2372 {
2373     QScript::QtFunction *fun = static_cast<QScript::QtFunction*>(signal.toFunction());
2374     int index = fun->mostGeneralMethod();
2375     return scriptDisconnect(fun->qobject(), index, receiver, function);
2376 }
2377 
convertToNativeQObject(const QScriptValueImpl & value,const QByteArray & targetType,void ** result)2378 bool QScriptEnginePrivate::convertToNativeQObject(const QScriptValueImpl &value,
2379                                                   const QByteArray &targetType,
2380                                                   void **result)
2381 {
2382     if (!targetType.endsWith('*'))
2383         return false;
2384     if (QObject *qobject = value.toQObject()) {
2385         int start = targetType.startsWith("const ") ? 6 : 0;
2386         QByteArray className = targetType.mid(start, targetType.size()-start-1);
2387         if (void *instance = qobject->qt_metacast(className)) {
2388             *result = instance;
2389             return true;
2390         }
2391     }
2392     return false;
2393 }
2394 
2395 #endif // QT_NO_QOBJECT
2396 
setAgent(QScriptEngineAgent * agent)2397 void QScriptEnginePrivate::setAgent(QScriptEngineAgent *agent)
2398 {
2399     Q_Q(QScriptEngine);
2400     if (agent && (agent->engine() != q)) {
2401         qWarning("QScriptEngine::setAgent(): "
2402                  "cannot set agent belonging to different engine");
2403         return;
2404     }
2405     if (agent) {
2406         int index = m_agents.indexOf(agent);
2407         if (index == -1)
2408             m_agents.append(agent);
2409     }
2410     m_agent = agent;
2411 }
2412 
agent() const2413 QScriptEngineAgent *QScriptEnginePrivate::agent() const
2414 {
2415     return m_agent;
2416 }
2417 
agentDeleted(QScriptEngineAgent * agent)2418 void QScriptEnginePrivate::agentDeleted(QScriptEngineAgent *agent)
2419 {
2420     m_agents.removeOne(agent);
2421     if (m_agent == agent)
2422         m_agent = 0;
2423 }
2424 
2425 #ifndef Q_SCRIPT_NO_EVENT_NOTIFY
nextScriptId()2426 qint64 QScriptEnginePrivate::nextScriptId()
2427 {
2428     // ### reuse IDs by using a pool
2429     return m_scriptCounter++;
2430 }
2431 
notifyScriptLoad_helper(qint64 id,const QString & program,const QString & fileName,int lineNumber)2432 void QScriptEnginePrivate::notifyScriptLoad_helper(qint64 id, const QString &program,
2433                                                    const QString &fileName, int lineNumber)
2434 {
2435     m_agent->scriptLoad(id, program, fileName, lineNumber);
2436 }
2437 
notifyScriptUnload_helper(qint64 id)2438 void QScriptEnginePrivate::notifyScriptUnload_helper(qint64 id)
2439 {
2440     m_agent->scriptUnload(id);
2441 }
2442 
notifyPositionChange_helper(QScriptContextPrivate * ctx)2443 void QScriptEnginePrivate::notifyPositionChange_helper(QScriptContextPrivate *ctx)
2444 {
2445     m_agent->positionChange(ctx->scriptId(), ctx->currentLine, ctx->currentColumn);
2446 }
2447 
notifyContextPush_helper()2448 void QScriptEnginePrivate::notifyContextPush_helper()
2449 {
2450     m_agent->contextPush();
2451 }
2452 
notifyContextPop_helper()2453 void QScriptEnginePrivate::notifyContextPop_helper()
2454 {
2455     m_agent->contextPop();
2456 }
2457 
notifyFunctionEntry_helper(QScriptContextPrivate * ctx)2458 void QScriptEnginePrivate::notifyFunctionEntry_helper(QScriptContextPrivate *ctx)
2459 {
2460     m_agent->functionEntry(ctx->scriptId());
2461 }
2462 
notifyFunctionExit_helper(QScriptContextPrivate * ctx)2463 void QScriptEnginePrivate::notifyFunctionExit_helper(QScriptContextPrivate *ctx)
2464 {
2465     m_agent->functionExit(ctx->scriptId(), toPublic(ctx->returnValue()));
2466 }
2467 
notifyException_helper(QScriptContextPrivate * ctx)2468 void QScriptEnginePrivate::notifyException_helper(QScriptContextPrivate *ctx)
2469 {
2470     bool hasHandler = (ctx->exceptionHandlerContext() != 0);
2471     m_agent->exceptionThrow(ctx->scriptId(), toPublic(ctx->returnValue()), hasHandler);
2472 }
2473 
notifyExceptionCatch_helper(QScriptContextPrivate * ctx)2474 void QScriptEnginePrivate::notifyExceptionCatch_helper(QScriptContextPrivate *ctx)
2475 {
2476     m_agent->exceptionCatch(ctx->scriptId(), toPublic(ctx->returnValue()));
2477 }
2478 
notifyDebugger(QScriptContextPrivate * ctx)2479 void QScriptEnginePrivate::notifyDebugger(QScriptContextPrivate *ctx)
2480 {
2481     if (m_agent && m_agent->supportsExtension(QScriptEngineAgent::DebuggerInvocationRequest)) {
2482         QVariantList args;
2483         args.append(ctx->scriptId());
2484         args.append(ctx->currentLine);
2485         args.append(ctx->currentColumn);
2486         QVariant ret = m_agent->extension(QScriptEngineAgent::DebuggerInvocationRequest, args);
2487         QScriptValueImpl val = valueFromVariant(ret);
2488         if (val.isValid())
2489             ctx->m_result = val;
2490     }
2491 }
2492 
2493 #endif // Q_SCRIPT_NO_EVENT_NOTIFY
2494 
internedString(const QString & str)2495 QScriptString QScriptEnginePrivate::internedString(const QString &str)
2496 {
2497     return internedString(nameId(str, /*persistent=*/false));
2498 }
2499 
internedString(QScriptNameIdImpl * nid)2500 QScriptString QScriptEnginePrivate::internedString(QScriptNameIdImpl *nid)
2501 {
2502     if (!nid)
2503         return QScriptString();
2504     QScriptStringPrivate *d = m_internedStrings.value(nid);
2505     if (!d) {
2506         d = m_internedStringRepository.get();
2507         d->nameId = nid;
2508         d->engine = this;
2509         m_internedStrings.insert(d->nameId, d);
2510     }
2511     QScriptString result;
2512     QScriptStringPrivate::init(result, d);
2513     return result;
2514 }
2515 
uninternString(QScriptStringPrivate * d)2516 void QScriptEnginePrivate::uninternString(QScriptStringPrivate *d)
2517 {
2518     Q_ASSERT(d->nameId);
2519     QHash<QScriptNameIdImpl*, QScriptStringPrivate*>::iterator it;
2520     it = m_internedStrings.find(d->nameId);
2521     Q_ASSERT(it != m_internedStrings.end());
2522     m_internedStrings.erase(it);
2523     m_internedStringRepository.release(d);
2524 }
2525 
toImpl_helper(const QScriptValue & value)2526 QScriptValueImpl QScriptEnginePrivate::toImpl_helper(const QScriptValue &value)
2527 {
2528     QScriptValuePrivate *p = QScriptValuePrivate::get(value);
2529     Q_ASSERT(p != 0);
2530     Q_ASSERT(p->value.type() == QScript::LazyStringType);
2531     QString str = *p->value.m_lazy_string_value;
2532     if (!p->ref.deref())
2533         delete p;
2534     QScriptValueImpl v;
2535     newString(&v, str);
2536     p = registerValue(v);
2537     QScriptValuePrivate::init(const_cast<QScriptValue&>(value), p);
2538     return v;
2539 }
2540 
newObject(QScriptClass * scriptClass,const QScriptValueImpl & data)2541 QScriptValueImpl QScriptEnginePrivate::newObject(QScriptClass *scriptClass,
2542                                                  const QScriptValueImpl &data)
2543 {
2544     if (!scriptClass)
2545         return QScriptValueImpl();
2546     QScriptValueImpl v;
2547     QScriptValueImpl proto = toImpl(scriptClass->prototype());
2548     if (!proto.isObject())
2549         proto = objectConstructor->publicPrototype;
2550     newObject(&v, proto);
2551     QScriptClassPrivate *cls_p = QScriptClassPrivate::get(scriptClass);
2552     QScriptClassInfo *info = cls_p->classInfo();
2553     v.setClassInfo(info);
2554     if (info->type() & QScriptClassInfo::FunctionBased) {
2555         QScriptFunction *fun = cls_p->newFunction();
2556         v.setObjectData(fun);
2557     }
2558     v.setInternalValue(data);
2559     return v;
2560 }
2561 
registerCustomClassType()2562 int QScriptEnginePrivate::registerCustomClassType()
2563 {
2564     return ++m_class_prev_id;
2565 }
2566 
objectById(qint64 id) const2567 QScriptValueImpl QScriptEnginePrivate::objectById(qint64 id) const
2568 {
2569     QScript::GCAlloc<QScriptObject>::const_iterator it;
2570     for (it = objectAllocator.constBegin(); it != objectAllocator.constEnd(); ++it) {
2571         const QScriptObject *obj = it.data();
2572         if (obj->m_id == id) {
2573             QScriptValueImpl ret;
2574             ret.m_type = QScript::ObjectType;
2575             ret.m_object_value = const_cast<QScriptObject*>(obj);
2576             return ret;
2577         }
2578     }
2579     return QScriptValueImpl();
2580 }
2581 
2582 namespace QScript {
2583 
qsTranslate(QScriptContextPrivate * ctx,QScriptEnginePrivate * eng,QScriptClassInfo *)2584 static QScriptValueImpl qsTranslate(QScriptContextPrivate *ctx, QScriptEnginePrivate *eng, QScriptClassInfo *)
2585 {
2586     if (ctx->argumentCount() < 2)
2587         return ctx->throwError(QString::fromLatin1("qsTranslate() requires at least two arguments"));
2588     if (!ctx->argument(0).isString())
2589         return ctx->throwError(QString::fromLatin1("qsTranslate(): first argument (context) must be a string"));
2590     if (!ctx->argument(1).isString())
2591         return ctx->throwError(QString::fromLatin1("qsTranslate(): second argument (text) must be a string"));
2592     if ((ctx->argumentCount() > 2) && !ctx->argument(2).isString())
2593         return ctx->throwError(QString::fromLatin1("qsTranslate(): third argument (comment) must be a string"));
2594     if ((ctx->argumentCount() > 3) && !ctx->argument(3).isString())
2595         return ctx->throwError(QString::fromLatin1("qsTranslate(): fourth argument (encoding) must be a string"));
2596     if ((ctx->argumentCount() > 4) && !ctx->argument(4).isNumber())
2597         return ctx->throwError(QString::fromLatin1("qsTranslate(): fifth argument (n) must be a number"));
2598 #ifndef QT_NO_QOBJECT
2599     QString context = ctx->argument(0).toString();
2600 #endif
2601     QString text = ctx->argument(1).toString();
2602 #ifndef QT_NO_QOBJECT
2603     QString comment;
2604     if (ctx->argumentCount() > 2)
2605         comment = ctx->argument(2).toString();
2606     QCoreApplication::Encoding encoding = QCoreApplication::CodecForTr;
2607     if (ctx->argumentCount() > 3) {
2608         QString encStr = ctx->argument(3).toString();
2609         if (encStr == QLatin1String("CodecForTr"))
2610             encoding = QCoreApplication::CodecForTr;
2611         else if (encStr == QLatin1String("UnicodeUTF8"))
2612             encoding = QCoreApplication::UnicodeUTF8;
2613         else
2614             return ctx->throwError(QString::fromLatin1("qsTranslate(): invalid encoding '%s'").arg(encStr));
2615     }
2616     int n = -1;
2617     if (ctx->argumentCount() > 4)
2618         n = ctx->argument(4).toInt32();
2619 #endif
2620     QString result;
2621 #ifndef QT_NO_QOBJECT
2622     result = QCoreApplication::translate(context.toLatin1().constData(),
2623                                          text.toLatin1().constData(),
2624                                          comment.toLatin1().constData(),
2625                                          encoding, n);
2626 #else
2627     result = text;
2628 #endif
2629     return QScriptValueImpl(eng, result);
2630 }
2631 
qTranslateNoOp(QScriptContextPrivate * ctx,QScriptEnginePrivate *,QScriptClassInfo *)2632 static QScriptValueImpl qTranslateNoOp(QScriptContextPrivate *ctx, QScriptEnginePrivate *, QScriptClassInfo *)
2633 {
2634     return ctx->argument(1);
2635 }
2636 
qsTr(QScriptContextPrivate * ctx,QScriptEnginePrivate * eng,QScriptClassInfo *)2637 static QScriptValueImpl qsTr(QScriptContextPrivate *ctx, QScriptEnginePrivate *eng, QScriptClassInfo *)
2638 {
2639     if (ctx->argumentCount() < 1)
2640         return ctx->throwError(QString::fromLatin1("qsTr() requires at least one argument"));
2641     if (!ctx->argument(0).isString())
2642         return ctx->throwError(QString::fromLatin1("qsTr(): first argument (text) must be a string"));
2643     if ((ctx->argumentCount() > 1) && !ctx->argument(1).isString())
2644         return ctx->throwError(QString::fromLatin1("qsTr(): second argument (comment) must be a string"));
2645     if ((ctx->argumentCount() > 2) && !ctx->argument(2).isNumber())
2646         return ctx->throwError(QString::fromLatin1("qsTranslate(): third argument (n) must be a number"));
2647 #ifndef QT_NO_QOBJECT
2648     QString context;
2649     if (ctx->parentContext())
2650         context = QFileInfo(ctx->parentContext()->fileName()).baseName();
2651 #endif
2652     QString text = ctx->argument(0).toString();
2653 #ifndef QT_NO_QOBJECT
2654     QString comment;
2655     if (ctx->argumentCount() > 1)
2656         comment = ctx->argument(1).toString();
2657     int n = -1;
2658     if (ctx->argumentCount() > 2)
2659         n = ctx->argument(2).toInt32();
2660 #endif
2661     QString result;
2662 #ifndef QT_NO_QOBJECT
2663     result = QCoreApplication::translate(context.toLatin1().constData(),
2664                                          text.toLatin1().constData(),
2665                                          comment.toLatin1().constData(),
2666                                          QCoreApplication::CodecForTr, n);
2667 #else
2668     result = text;
2669 #endif
2670     return QScriptValueImpl(eng, result);
2671 }
2672 
qTrNoOp(QScriptContextPrivate * ctx,QScriptEnginePrivate *,QScriptClassInfo *)2673 static QScriptValueImpl qTrNoOp(QScriptContextPrivate *ctx, QScriptEnginePrivate *, QScriptClassInfo *)
2674 {
2675     return ctx->argument(0);
2676 }
2677 
2678 } // namespace QScript
2679 
installTranslatorFunctions(QScriptValueImpl & object)2680 void QScriptEnginePrivate::installTranslatorFunctions(QScriptValueImpl &object)
2681 {
2682     Q_ASSERT(object.isObject());
2683     const QScriptValue::PropertyFlags flags = QScriptValue::SkipInEnumeration;
2684     object.setProperty(QLatin1String("qsTranslate"),
2685                        createFunction(QScript::qsTranslate, /*length=*/5, /*classInfo=*/0),
2686                        flags);
2687     object.setProperty(QLatin1String("QT_TRANSLATE_NOOP"),
2688                        createFunction(QScript::qTranslateNoOp, /*length=*/2, /*classInfo=*/0),
2689                        flags);
2690     object.setProperty(QLatin1String("qsTr"),
2691                        createFunction(QScript::qsTr, /*length=*/3, /*classInfo=*/0),
2692                        flags);
2693     object.setProperty(QLatin1String("QT_TR_NOOP"),
2694                        createFunction(QScript::qTrNoOp, /*length=*/1, /*classInfo=*/0),
2695                        flags);
2696 
2697     stringConstructor->addPrototypeFunction(QLatin1String("arg"), QScript::Ecma::String::method_ext_arg, 1);
2698 }
2699 
canEvaluate(const QString & program)2700 bool QScriptEnginePrivate::canEvaluate(const QString &program)
2701 {
2702     QScript::SyntaxChecker checker;
2703     QScript::SyntaxChecker::Result result = checker.checkSyntax(program);
2704     return (result.state != QScript::SyntaxChecker::Intermediate);
2705 }
2706 
checkSyntax(const QString & program)2707 QScriptSyntaxCheckResult QScriptEnginePrivate::checkSyntax(const QString &program)
2708 {
2709     QScript::SyntaxChecker checker;
2710     QScript::SyntaxChecker::Result result = checker.checkSyntax(program);
2711     QScriptSyntaxCheckResultPrivate *p = new QScriptSyntaxCheckResultPrivate();
2712     switch (result.state) {
2713     case QScript::SyntaxChecker::Error:
2714         p->state = QScriptSyntaxCheckResult::Error;
2715         break;
2716     case QScript::SyntaxChecker::Intermediate:
2717         p->state = QScriptSyntaxCheckResult::Intermediate;
2718         break;
2719     case QScript::SyntaxChecker::Valid:
2720         p->state = QScriptSyntaxCheckResult::Valid;
2721         break;
2722     }
2723     p->errorLineNumber = result.errorLineNumber;
2724     p->errorColumnNumber = result.errorColumnNumber;
2725     p->errorMessage = result.errorMessage;
2726     return QScriptSyntaxCheckResult(p);
2727 }
2728 
2729 QT_END_NAMESPACE
2730 
2731