source: branches/4.5.1/src/script/qscriptvalueimpl.cpp@ 921

Last change on this file since 921 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 14.3 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the QtScript module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qscriptvalueimpl_p.h"
43
44#ifndef QT_NO_SCRIPT
45
46#include "qscriptengine_p.h"
47#include "qscriptvalueimpl_p.h"
48#include "qscriptcontext_p.h"
49#include "qscriptmember_p.h"
50#include "qscriptobject_p.h"
51
52QT_BEGIN_NAMESPACE
53
54static void dfs(QScriptObject *instance, QHash<QScriptObject*, int> &dfn, int n)
55{
56 bool found = dfn.contains(instance);
57 dfn[instance] = n;
58
59 if (found)
60 return;
61
62 if (instance->m_prototype.isObject())
63 dfs (instance->m_prototype.m_object_value, dfn, n + 1);
64
65 if (instance->m_scope.isObject())
66 dfs (instance->m_scope.m_object_value, dfn, n + 1);
67}
68
69
70static bool checkCycle(QScriptObject *instance, const QHash<QScriptObject*, int> &dfn)
71{
72 int n = dfn.value(instance);
73
74 if (instance->m_prototype.isObject()) {
75 if (n >= dfn.value(instance->m_prototype.m_object_value))
76 return true;
77 }
78
79 if (instance->m_scope.isObject()) {
80 if (n >= dfn.value(instance->m_scope.m_object_value))
81 return true;
82 }
83
84 return false;
85}
86
87bool QScriptValueImpl::detectedCycle() const
88{
89 QHash<QScriptObject*, int> dfn;
90 dfs(m_object_value, dfn, 0);
91 return checkCycle(m_object_value, dfn);
92}
93
94bool QScriptValueImpl::instanceOf(const QScriptValueImpl &value) const
95{
96 if (! isObject() || ! value.isObject() || !value.implementsHasInstance())
97 return false;
98 return value.hasInstance(*this);
99}
100
101bool QScriptValueImpl::implementsHasInstance() const
102{
103 Q_ASSERT(isObject());
104 if (isFunction())
105 return true;
106 if (QScriptClassData *odata = classInfo()->data()) {
107 return odata->implementsHasInstance(*this);
108 }
109 return false;
110}
111
112bool QScriptValueImpl::hasInstance(const QScriptValueImpl &value) const
113{
114 Q_ASSERT(isObject());
115
116 if (QScriptClassData *odata = classInfo()->data()) {
117 if (odata->implementsHasInstance(*this))
118 return odata->hasInstance(*this, value);
119 }
120 if (!isFunction())
121 return false;
122
123 // [[HasInstance] for function objects
124
125 if (!value.isObject())
126 return false;
127
128 QScriptEnginePrivate *eng = engine();
129 QScriptValueImpl proto = property(eng->idTable()->id_prototype);
130 if (!proto.isObject()) {
131 QScriptContextPrivate *ctx = eng->currentContext();
132 ctx->throwTypeError(QLatin1String("instanceof: 'prototype' property is not an object"));
133 return false;
134 }
135
136 QScriptObject *target = proto.m_object_value;
137 QScriptValueImpl v = value;
138 while (true) {
139 v = v.prototype();
140 if (!v.isObject())
141 break;
142 if (target == v.m_object_value)
143 return true;
144 }
145 return false;
146}
147
148bool QScriptValueImpl::resolve_helper(QScriptNameIdImpl *nameId, QScript::Member *member,
149 QScriptValueImpl *object, QScriptValue::ResolveFlags mode,
150 QScript::AccessMode access) const
151{
152 QScriptObject *object_data = m_object_value;
153
154 QScriptEnginePrivate *eng_p = engine();
155
156 if (nameId == eng_p->idTable()->id___proto__) {
157 member->native(nameId, /*id=*/0, QScriptValue::Undeletable);
158 *object = *this;
159 return true;
160 }
161
162 // If not found anywhere else, search in the extra members.
163 if (QScriptClassData *odata = classInfo()->data()) {
164 *object = *this;
165
166 if (odata->resolve(*this, nameId, member, object, access))
167 return true;
168 }
169
170 if (mode & QScriptValue::ResolvePrototype) {
171 // For values and other non object based types, search in class's prototype
172 const QScriptValueImpl &proto = object_data->m_prototype;
173
174 if (proto.isObject()
175 && proto.resolve(nameId, member, object, mode, access)) {
176 return true;
177 }
178 }
179
180 if ((mode & QScriptValue::ResolveScope) && object_data->m_scope.isValid())
181 return object_data->m_scope.resolve(nameId, member, object, mode, access);
182
183 return false;
184}
185
186void QScriptValueImpl::setProperty(QScriptNameIdImpl *nameId,
187 const QScriptValueImpl &value,
188 const QScriptValue::PropertyFlags &flags)
189{
190 if (!isObject())
191 return;
192
193 QScriptValueImpl base;
194 QScript::Member member;
195
196 QScriptValue::ResolveFlags mode = QScriptValue::ResolveLocal;
197 // if we are not setting a setter or getter, look in prototype too
198 if (!(flags & (QScriptValue::PropertyGetter | QScriptValue::PropertySetter)))
199 mode |= QScriptValue::ResolvePrototype;
200
201 if (resolve(nameId, &member, &base, mode, QScript::ReadWrite)) {
202 // we resolved an existing property with that name
203 if (flags & (QScriptValue::PropertyGetter | QScriptValue::PropertySetter)) {
204 // setting the getter or setter of a property in this object
205 if (member.isNativeProperty()) {
206 if (value.isValid()) {
207 qWarning("QScriptValue::setProperty() failed: "
208 "cannot set getter or setter of native property `%s'",
209 qPrintable(nameId->s));
210 }
211 return;
212 }
213 if (member.isSetter()) {
214 // the property we resolved is a setter
215 if (!(flags & QScriptValue::PropertySetter) && !member.isGetter()) {
216 // find the getter, if not, create one
217 if (!m_object_value->findGetter(&member)) {
218 if (!value.isValid())
219 return; // don't create property for invalid value
220 createMember(nameId, &member, flags);
221 }
222 }
223 } else if (member.isGetter()) {
224 // the property we resolved is a getter
225 if (!(flags & QScriptValue::PropertyGetter)) {
226 // find the setter, if not, create one
227 if (!m_object_value->findSetter(&member)) {
228 if (!value.isValid())
229 return; // don't create property for invalid value
230 createMember(nameId, &member, flags);
231 }
232 }
233 } else {
234 // the property is a normal property -- change the flags
235 uint newFlags = flags & ~QScript::Member::InternalRange;
236 newFlags |= QScript::Member::ObjectProperty;
237 member.resetFlags(newFlags);
238 base.m_object_value->m_members[member.id()].resetFlags(newFlags);
239 }
240 Q_ASSERT(member.isValid());
241 if (!value.isValid()) {
242 // remove the property
243 removeMember(member);
244 return;
245 }
246 } else {
247 // setting the value
248 if (member.isGetterOrSetter()) {
249 // call the setter
250 QScriptValueImpl setter;
251 if (member.isObjectProperty() && !member.isSetter()) {
252 if (!base.m_object_value->findSetter(&member)) {
253 qWarning("QScriptValue::setProperty() failed: "
254 "property '%s' has a getter but no setter",
255 qPrintable(nameId->s));
256 return;
257 }
258 }
259 base.get(member, &setter);
260 setter.call(*this, QScriptValueImplList() << value);
261 return;
262 } else {
263 if (base.m_object_value != m_object_value) {
264 if (!value.isValid())
265 return; // don't create property for invalid value
266 createMember(nameId, &member, flags);
267 base = *this;
268 } else {
269 if (!value.isValid()) {
270 // remove the property
271 removeMember(member);
272 return;
273 }
274 }
275 if (flags != QScriptValue::KeepExistingFlags) {
276 // change flags
277 if (member.isNativeProperty()) {
278 qWarning("QScriptValue::setProperty(%s): "
279 "cannot change flags of a native property",
280 qPrintable(nameId->s));
281 } else {
282 uint newFlags = member.flags() & QScript::Member::InternalRange;
283 newFlags |= flags & ~QScript::Member::InternalRange;
284 base.m_object_value->m_members[member.id()].resetFlags(newFlags);
285 }
286 }
287 }
288 }
289 } else {
290 // property does not exist
291 if (!value.isValid())
292 return; // don't create property for invalid value
293 createMember(nameId, &member, flags & ~QScript::Member::InternalRange);
294 base = *this;
295 }
296
297 base.put(member, value);
298}
299
300QVariant QScriptValueImpl::toVariant() const
301{
302 switch (m_type) {
303 case QScript::InvalidType:
304 return QVariant();
305
306 case QScript::UndefinedType:
307 case QScript::NullType:
308 case QScript::PointerType:
309 case QScript::ReferenceType:
310 break;
311
312 case QScript::BooleanType:
313 return QVariant(m_bool_value);
314
315 case QScript::IntegerType:
316 return QVariant(m_int_value);
317
318 case QScript::NumberType:
319 return QVariant(m_number_value);
320
321 case QScript::StringType:
322 return QVariant(m_string_value->s);
323
324 case QScript::LazyStringType:
325 return QVariant(*m_lazy_string_value);
326
327 case QScript::ObjectType:
328 if (isDate())
329 return QVariant(toDateTime());
330
331#ifndef QT_NO_REGEXP
332 if (isRegExp())
333 return QVariant(toRegExp());
334#endif
335 if (isVariant())
336 return variantValue();
337
338#ifndef QT_NO_QOBJECT
339 if (isQObject())
340 return qVariantFromValue(toQObject());
341#endif
342
343 QScriptValueImpl v = engine()->toPrimitive(*this);
344 if (!v.isObject())
345 return v.toVariant();
346 break;
347 } // switch
348 return QVariant();
349}
350
351QDebug &operator<<(QDebug &d, const QScriptValueImpl &object)
352{
353 d.nospace() << "QScriptValue(";
354
355 switch (object.type()) {
356 case QScript::InvalidType:
357 d.nospace() << "Invalid)";
358 return d;
359
360 case QScript::BooleanType:
361 d.nospace() << "bool=" << object.toBoolean();
362 break;
363
364 case QScript::IntegerType:
365 d.nospace() << "int=" << object.toInt32();
366 break;
367
368 case QScript::NumberType:
369 d.nospace() << "qsreal=" << object.toNumber();
370 break;
371
372 case QScript::LazyStringType:
373 case QScript::StringType:
374 d.nospace() << "string=" << object.toString();
375 break;
376
377 case QScript::ReferenceType:
378 d.nospace() << "reference";
379 break;
380
381 case QScript::NullType:
382 d.nospace() << "null";
383 break;
384
385 case QScript::UndefinedType:
386 d.nospace() << "undefined";
387 break;
388
389 case QScript::PointerType:
390 d.nospace() << "pointer";
391 break;
392
393 case QScript::ObjectType:
394 d.nospace() << object.classInfo()->name() << ",{";
395 QScriptObject *od = object.objectValue();
396 for (int i=0; i<od->memberCount(); ++i) {
397 if (i != 0)
398 d << ",";
399
400 QScript::Member m;
401 od->member(i, &m);
402
403 if (m.isValid() && m.isObjectProperty()) {
404 d << object.engine()->toString(m.nameId());
405 QScriptValueImpl o;
406 od->get(m, &o);
407 d.nospace() << QLatin1String(":")
408 << (o.classInfo()
409 ? o.classInfo()->name()
410 : QLatin1String("?"));
411 }
412 }
413
414 d.nospace() << "} scope={";
415 QScriptValueImpl scope = object.scope();
416 while (scope.isValid()) {
417 Q_ASSERT(scope.isObject());
418 d.nospace() << " " << scope.objectValue();
419 scope = scope.scope();
420 }
421 d.nospace() << "}";
422 break;
423 }
424
425 d << ")";
426 return d;
427}
428
429void QScriptValueImpl::destroyObjectData()
430{
431 Q_ASSERT(isObject());
432 m_object_value->finalizeData();
433}
434
435bool QScriptValueImpl::isMarked(int generation) const
436{
437 if (isString())
438 return (m_string_value->used != 0);
439 else if (isObject()) {
440 QScript::GCBlock *block = QScript::GCBlock::get(m_object_value);
441 return (block->generation == generation);
442 }
443 return false;
444}
445
446QT_END_NAMESPACE
447
448#endif // QT_NO_SCRIPT
Note: See TracBrowser for help on using the repository browser.