diff options
author | Samuel Rødal <samuel.rodal@nokia.com> | 2011-08-30 11:51:17 +0200 |
---|---|---|
committer | Samuel Rødal <samuel.rodal@nokia.com> | 2011-08-30 11:51:20 +0200 |
commit | 42f2da5e6b543e698fc4f036c45ebb15078409c8 (patch) | |
tree | 2d688b1a812d8410d4ae85283d5e413957000e11 | |
parent | e9962cc300a805ce09a02ce0c9ae23ceaf8073bb (diff) | |
parent | 009f3d14045e2e2a6eff8c87a8da9eaa621c5249 (diff) |
Merge remote branch 'gerrit/master' into refactor
Conflicts:
src/src.pro
Change-Id: Ic04fb170b82e86dc3cef6fe979f8fb709db10daf
40 files changed, 6462 insertions, 433 deletions
diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000..183f5b46cc --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "src/3rdparty/v8"] + path = src/3rdparty/v8 + url = git://github.com/aaronkennedy/v8.git diff --git a/bin/syncqt b/bin/syncqt index 4eab929b30..f28b2bf60a 100755 --- a/bin/syncqt +++ b/bin/syncqt @@ -33,7 +33,7 @@ $qtbasedir = dirname(dirname($0)) if (!$qtbasedir); $qtbasedir =~ s=\\=/=g if (defined $qtbasedir); # will be defined based on the modules sync.profile -our (%modules, %moduleheaders, %classnames, %mastercontent, %modulepris); +our (%modules, %moduleheaders, @allmoduleheadersprivate, %classnames, %mastercontent, %modulepris); # global variables (modified by options) my $isunix = 0; @@ -792,6 +792,8 @@ loadSyncProfile(\$basedir, \$out_basedir); @modules_to_sync = keys(%modules) if($#modules_to_sync == -1); +my %allmoduleheadersprivate = map { $_ => 1 } @allmoduleheadersprivate; + $isunix = checkUnix; #cache checkUnix # create path @@ -828,6 +830,9 @@ foreach my $lib (@modules_to_sync) { my $pathtoheaders = ""; $pathtoheaders = $moduleheaders{$lib} if ($moduleheaders{$lib}); + my $allheadersprivate = 0; + $allheadersprivate = 1 if $allmoduleheadersprivate{$lib}; + #information used after the syncing my $pri_install_classes = ""; my $pri_install_files = ""; @@ -955,7 +960,7 @@ foreach my $lib (@modules_to_sync) { my $header_copies = 0; #figure out if it is a public header my $public_header = $header; - if($public_header =~ /_p.h$/ || $public_header =~ /_pch.h$/) { + if($allheadersprivate || $public_header =~ /_p.h$/ || $public_header =~ /_pch.h$/) { $public_header = 0; } else { foreach (@ignore_for_master_contents) { diff --git a/config.tests/x11/fontconfig/fontconfig.pro b/config.tests/x11/fontconfig/fontconfig.pro index 718a8204bc..8360774035 100644 --- a/config.tests/x11/fontconfig/fontconfig.pro +++ b/config.tests/x11/fontconfig/fontconfig.pro @@ -1,5 +1,4 @@ SOURCES = fontconfig.cpp -CONFIG += x11 CONFIG -= qt LIBS += -lfreetype -lfontconfig include(../../unix/freetype/freetype.pri) @@ -706,6 +706,7 @@ CFG_PHONON_BACKEND=yes CFG_MULTIMEDIA=auto CFG_AUDIO_BACKEND=auto CFG_SVG=auto +CFG_V8=auto CFG_DECLARATIVE=auto CFG_DECLARATIVE_DEBUG=yes CFG_WEBKIT=auto # (yes|no|auto|debug) @@ -2122,6 +2123,17 @@ while [ "$#" -gt 0 ]; do fi fi ;; + v8) + if [ "$VAL" = "yes" ]; then + CFG_V8="yes" + else + if [ "$VAL" = "no" ]; then + CFG_V8="no" + else + UNKNOWN_OPT=yes + fi + fi + ;; declarative) if [ "$VAL" = "yes" ]; then CFG_DECLARATIVE="yes" @@ -3896,6 +3908,9 @@ fi -no-scripttools .... Do not build the QtScriptTools module. + -scripttools ....... Build the QtScriptTools module. + -no-v8 ............. Do not build the V8 module. + + -v8 ................ Build the V8 module. + -no-declarative ..... Do not build the declarative module. + -declarative ....... Build the declarative module. @@ -7579,9 +7594,19 @@ fi #fi +if [ "$CFG_V8" = "auto" ]; then + CFG_V8=yes +fi + +if [ "$CFG_V8" = "no" ]; then + QCONFIG_FLAGS="$QCONFIG_FLAGS QT_NO_V8" +else + QT_CONFIG="$QT_CONFIG v8" +fi + if [ "$CFG_DECLARATIVE" = "yes" ]; then - if [ "$CFG_SCRIPT" = "no" -o "$CFG_GUI" = "no" ]; then - echo "Error: QtDeclarative was requested, but it can't be built due to QtScript or QtGui being disabled." + if [ "$CFG_V8" = "no" -o "$CFG_GUI" = "no" ]; then + echo "Error: QtDeclarative was requested, but it can't be built due to QtV8 or QtGui being disabled." exit 1 fi fi @@ -8629,6 +8654,7 @@ if [ "$CFG_WEBKIT" != "no" ] || [ "$CFG_SCRIPT" != "no" ]; then echo "JavaScriptCore JIT ..... $CFG_JAVASCRIPTCORE_JIT" fi fi +echo "V8 module .............. $CFG_V8" echo "Declarative module ..... $CFG_DECLARATIVE" if [ "$CFG_DECLARATIVE" = "yes" ]; then echo "Declarative debugging ...$CFG_DECLARATIVE_DEBUG" diff --git a/src/3rdparty/v8 b/src/3rdparty/v8 new file mode 160000 +Subproject dc2cad4f8fc88c52fcea09b8d0262d35cd32dc4 diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index 2d280f14f7..0f314d45f1 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -147,6 +147,7 @@ private: void (*QAbstractDeclarativeData::destroyed)(QAbstractDeclarativeData *, QObject *) = 0; void (*QAbstractDeclarativeData::parentChanged)(QAbstractDeclarativeData *, QObject *, QObject *) = 0; void (*QAbstractDeclarativeData::objectNameChanged)(QAbstractDeclarativeData *, QObject *) = 0; +void (*QAbstractDeclarativeData::signalEmitted)(QAbstractDeclarativeData *, QObject *, int, void **) = 0; QObjectData::~QObjectData() {} @@ -3271,6 +3272,10 @@ void QMetaObject::activate(QObject *sender, const QMetaObject *m, int local_sign int signal_index = signalOffset + local_signal_index; + if (sender->d_func()->declarativeData && QAbstractDeclarativeData::signalEmitted) + QAbstractDeclarativeData::signalEmitted(sender->d_func()->declarativeData, sender, + methodOffset + local_signal_index, argv); + if (!sender->d_func()->isSignalConnected(signal_index)) return; // nothing connected to these signals, and no spy diff --git a/src/corelib/kernel/qobject_p.h b/src/corelib/kernel/qobject_p.h index 9151e8eb7b..e7233c5ba9 100644 --- a/src/corelib/kernel/qobject_p.h +++ b/src/corelib/kernel/qobject_p.h @@ -91,6 +91,7 @@ public: static void (*destroyed)(QAbstractDeclarativeData *, QObject *); static void (*parentChanged)(QAbstractDeclarativeData *, QObject *, QObject *); static void (*objectNameChanged)(QAbstractDeclarativeData *, QObject *); + static void (*signalEmitted)(QAbstractDeclarativeData *, QObject *, int, void **); }; class Q_CORE_EXPORT QObjectPrivate : public QObjectData diff --git a/src/gui/math3d/qmatrix4x4.cpp b/src/gui/math3d/qmatrix4x4.cpp index cef92af66b..1797564a98 100644 --- a/src/gui/math3d/qmatrix4x4.cpp +++ b/src/gui/math3d/qmatrix4x4.cpp @@ -209,7 +209,7 @@ QMatrix4x4::QMatrix4x4(const QMatrix& matrix) m[3][1] = matrix.dy(); m[3][2] = 0.0f; m[3][3] = 1.0f; - flagBits = General; + flagBits = Translation | Scale | Rotation2D; } /*! @@ -316,6 +316,12 @@ QMatrix4x4::QMatrix4x4(const QTransform& transform) Fills all elements of this matrx with \a value. */ +static inline qreal matrixDet2(const qreal m[4][4], int col0, int col1, int row0, int row1) +{ + return m[col0][row0] * m[col1][row1] - m[col0][row1] * m[col1][row0]; +} + + // The 4x4 matrix inverse algorithm is based on that described at: // http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q24 // Some optimization has been done to avoid making copies of 3x3 @@ -329,15 +335,9 @@ static inline qreal matrixDet3 (const qreal m[4][4], int col0, int col1, int col2, int row0, int row1, int row2) { - return m[col0][row0] * - (m[col1][row1] * m[col2][row2] - - m[col1][row2] * m[col2][row1]) - - m[col1][row0] * - (m[col0][row1] * m[col2][row2] - - m[col0][row2] * m[col2][row1]) + - m[col2][row0] * - (m[col0][row1] * m[col1][row2] - - m[col0][row2] * m[col1][row1]); + return m[col0][row0] * matrixDet2(m, col1, col2, row1, row2) + - m[col1][row0] * matrixDet2(m, col0, col2, row1, row2) + + m[col2][row0] * matrixDet2(m, col0, col1, row1, row2); } // Calculate the determinant of a 4x4 matrix. @@ -356,7 +356,13 @@ static inline qreal matrixDet4(const qreal m[4][4]) */ qreal QMatrix4x4::determinant() const { - return qreal(matrixDet4(m)); + if ((flagBits & ~(Translation | Rotation2D | Rotation)) == Identity) + return 1; + if (flagBits < Rotation2D) + return m[0][0] * m[1][1] * m[2][2]; // Translation | Scale + if (flagBits < Perspective) + return matrixDet3(m, 0, 1, 2, 0, 1, 2); + return matrixDet4(m); } /*! @@ -387,10 +393,61 @@ QMatrix4x4 QMatrix4x4::inverted(bool *invertible) const if (invertible) *invertible = true; return inv; - } else if (flagBits == Rotation || flagBits == (Rotation | Translation)) { + } else if (flagBits < Rotation2D) { + // Translation | Scale + if (m[0][0] == 0 || m[1][1] == 0 || m[2][2] == 0) { + if (invertible) + *invertible = false; + return QMatrix4x4(); + } + QMatrix4x4 inv; + inv.m[0][0] = 1.0f / m[0][0]; + inv.m[1][1] = 1.0f / m[1][1]; + inv.m[2][2] = 1.0f / m[2][2]; + inv.m[3][0] = -m[3][0] * inv.m[0][0]; + inv.m[3][1] = -m[3][1] * inv.m[1][1]; + inv.m[3][2] = -m[3][2] * inv.m[2][2]; + inv.flagBits = flagBits; + + if (invertible) + *invertible = true; + return inv; + } else if ((flagBits & ~(Translation | Rotation2D | Rotation)) == Identity) { if (invertible) *invertible = true; return orthonormalInverse(); + } else if (flagBits < Perspective) { + QMatrix4x4 inv(1); // The "1" says to not load the identity. + + qreal det = matrixDet3(m, 0, 1, 2, 0, 1, 2); + if (det == 0.0f) { + if (invertible) + *invertible = false; + return QMatrix4x4(); + } + det = 1.0f / det; + + inv.m[0][0] = matrixDet2(m, 1, 2, 1, 2) * det; + inv.m[0][1] = -matrixDet2(m, 0, 2, 1, 2) * det; + inv.m[0][2] = matrixDet2(m, 0, 1, 1, 2) * det; + inv.m[0][3] = 0; + inv.m[1][0] = -matrixDet2(m, 1, 2, 0, 2) * det; + inv.m[1][1] = matrixDet2(m, 0, 2, 0, 2) * det; + inv.m[1][2] = -matrixDet2(m, 0, 1, 0, 2) * det; + inv.m[1][3] = 0; + inv.m[2][0] = matrixDet2(m, 1, 2, 0, 1) * det; + inv.m[2][1] = -matrixDet2(m, 0, 2, 0, 1) * det; + inv.m[2][2] = matrixDet2(m, 0, 1, 0, 1) * det; + inv.m[2][3] = 0; + inv.m[3][0] = -inv.m[0][0] * m[3][0] - inv.m[1][0] * m[3][1] - inv.m[2][0] * m[3][2]; + inv.m[3][1] = -inv.m[0][1] * m[3][0] - inv.m[1][1] * m[3][1] - inv.m[2][1] * m[3][2]; + inv.m[3][2] = -inv.m[0][2] * m[3][0] - inv.m[1][2] * m[3][1] - inv.m[2][2] * m[3][2]; + inv.m[3][3] = 1; + inv.flagBits = flagBits; + + if (invertible) + *invertible = true; + return inv; } QMatrix4x4 inv(1); // The "1" says to not load the identity. @@ -419,6 +476,7 @@ QMatrix4x4 QMatrix4x4::inverted(bool *invertible) const inv.m[3][1] = matrixDet3(m, 0, 2, 3, 0, 1, 2) * det; inv.m[3][2] = -matrixDet3(m, 0, 1, 3, 0, 1, 2) * det; inv.m[3][3] = matrixDet3(m, 0, 1, 2, 0, 1, 2) * det; + inv.flagBits = flagBits; if (invertible) *invertible = true; @@ -438,15 +496,29 @@ QMatrix3x3 QMatrix4x4::normalMatrix() const QMatrix3x3 inv; // Handle the simple cases first. - if (flagBits == Identity || flagBits == Translation) { + if (flagBits < Scale) { + // Translation return inv; - } else if (flagBits == Scale || flagBits == (Translation | Scale)) { + } else if (flagBits < Rotation2D) { + // Translation | Scale if (m[0][0] == 0.0f || m[1][1] == 0.0f || m[2][2] == 0.0f) return inv; inv.data()[0] = 1.0f / m[0][0]; inv.data()[4] = 1.0f / m[1][1]; inv.data()[8] = 1.0f / m[2][2]; return inv; + } else if ((flagBits & ~(Translation | Rotation2D | Rotation)) == Identity) { + qreal *invm = inv.data(); + invm[0 + 0 * 3] = m[0][0]; + invm[1 + 0 * 3] = m[0][1]; + invm[2 + 0 * 3] = m[0][2]; + invm[0 + 1 * 3] = m[1][0]; + invm[1 + 1 * 3] = m[1][1]; + invm[2 + 1 * 3] = m[1][2]; + invm[0 + 2 * 3] = m[2][0]; + invm[1 + 2 * 3] = m[2][1]; + invm[2 + 2 * 3] = m[2][2]; + return inv; } qreal det = matrixDet3(m, 0, 1, 2, 0, 1, 2); @@ -481,6 +553,8 @@ QMatrix4x4 QMatrix4x4::transposed() const result.m[col][row] = m[row][col]; } } + // When a translation is transposed, it becomes a perspective transformation. + result.flagBits = (flagBits & Translation ? General : flagBits); return result; } @@ -689,6 +763,7 @@ QMatrix4x4 operator/(const QMatrix4x4& matrix, qreal divisor) m.m[3][1] = matrix.m[3][1] / divisor; m.m[3][2] = matrix.m[3][2] / divisor; m.m[3][3] = matrix.m[3][3] / divisor; + m.flagBits = QMatrix4x4::General; return m; } @@ -713,20 +788,20 @@ void QMatrix4x4::scale(const QVector3D& vector) qreal vx = vector.x(); qreal vy = vector.y(); qreal vz = vector.z(); - if (flagBits == Identity) { + if (flagBits < Scale) { m[0][0] = vx; m[1][1] = vy; m[2][2] = vz; - flagBits = Scale; - } else if (flagBits == Scale || flagBits == (Scale | Translation)) { + } else if (flagBits < Rotation2D) { m[0][0] *= vx; m[1][1] *= vy; m[2][2] *= vz; - } else if (flagBits == Translation) { - m[0][0] = vx; - m[1][1] = vy; - m[2][2] = vz; - flagBits |= Scale; + } else if (flagBits < Rotation) { + m[0][0] *= vx; + m[0][1] *= vx; + m[1][0] *= vy; + m[1][1] *= vy; + m[2][2] *= vz; } else { m[0][0] *= vx; m[0][1] *= vx; @@ -740,9 +815,10 @@ void QMatrix4x4::scale(const QVector3D& vector) m[2][1] *= vz; m[2][2] *= vz; m[2][3] *= vz; - flagBits = General; } + flagBits |= Scale; } + #endif /*! @@ -755,17 +831,17 @@ void QMatrix4x4::scale(const QVector3D& vector) */ void QMatrix4x4::scale(qreal x, qreal y) { - if (flagBits == Identity) { + if (flagBits < Scale) { m[0][0] = x; m[1][1] = y; - flagBits = Scale; - } else if (flagBits == Scale || flagBits == (Scale | Translation)) { + } else if (flagBits < Rotation2D) { m[0][0] *= x; m[1][1] *= y; - } else if (flagBits == Translation) { - m[0][0] = x; - m[1][1] = y; - flagBits |= Scale; + } else if (flagBits < Rotation) { + m[0][0] *= x; + m[0][1] *= x; + m[1][0] *= y; + m[1][1] *= y; } else { m[0][0] *= x; m[0][1] *= x; @@ -775,8 +851,8 @@ void QMatrix4x4::scale(qreal x, qreal y) m[1][1] *= y; m[1][2] *= y; m[1][3] *= y; - flagBits = General; } + flagBits |= Scale; } /*! @@ -789,20 +865,20 @@ void QMatrix4x4::scale(qreal x, qreal y) */ void QMatrix4x4::scale(qreal x, qreal y, qreal z) { - if (flagBits == Identity) { + if (flagBits < Scale) { m[0][0] = x; m[1][1] = y; m[2][2] = z; - flagBits = Scale; - } else if (flagBits == Scale || flagBits == (Scale | Translation)) { + } else if (flagBits < Rotation2D) { m[0][0] *= x; m[1][1] *= y; m[2][2] *= z; - } else if (flagBits == Translation) { - m[0][0] = x; - m[1][1] = y; - m[2][2] = z; - flagBits |= Scale; + } else if (flagBits < Rotation) { + m[0][0] *= x; + m[0][1] *= x; + m[1][0] *= y; + m[1][1] *= y; + m[2][2] *= z; } else { m[0][0] *= x; m[0][1] *= x; @@ -816,8 +892,8 @@ void QMatrix4x4::scale(qreal x, qreal y, qreal z) m[2][1] *= z; m[2][2] *= z; m[2][3] *= z; - flagBits = General; } + flagBits |= Scale; } /*! @@ -830,20 +906,20 @@ void QMatrix4x4::scale(qreal x, qreal y, qreal z) */ void QMatrix4x4::scale(qreal factor) { - if (flagBits == Identity) { + if (flagBits < Scale) { m[0][0] = factor; m[1][1] = factor; m[2][2] = factor; - flagBits = Scale; - } else if (flagBits == Scale || flagBits == (Scale | Translation)) { + } else if (flagBits < Rotation2D) { m[0][0] *= factor; m[1][1] *= factor; m[2][2] *= factor; - } else if (flagBits == Translation) { - m[0][0] = factor; - m[1][1] = factor; - m[2][2] = factor; - flagBits |= Scale; + } else if (flagBits < Rotation) { + m[0][0] *= factor; + m[0][1] *= factor; + m[1][0] *= factor; + m[1][1] *= factor; + m[2][2] *= factor; } else { m[0][0] *= factor; m[0][1] *= factor; @@ -857,8 +933,8 @@ void QMatrix4x4::scale(qreal factor) m[2][1] *= factor; m[2][2] *= factor; m[2][3] *= factor; - flagBits = General; } + flagBits |= Scale; } #ifndef QT_NO_VECTOR3D @@ -868,6 +944,7 @@ void QMatrix4x4::scale(qreal factor) \sa scale(), rotate() */ + void QMatrix4x4::translate(const QVector3D& vector) { qreal vx = vector.x(); @@ -877,7 +954,6 @@ void QMatrix4x4::translate(const QVector3D& vector) m[3][0] = vx; m[3][1] = vy; m[3][2] = vz; - flagBits = Translation; } else if (flagBits == Translation) { m[3][0] += vx; m[3][1] += vy; @@ -886,23 +962,22 @@ void QMatrix4x4::translate(const QVector3D& vector) m[3][0] = m[0][0] * vx; m[3][1] = m[1][1] * vy; m[3][2] = m[2][2] * vz; - flagBits |= Translation; - } else if (flagBits == (Scale | Translation)) { + } else if (flagBits == (Translation | Scale)) { m[3][0] += m[0][0] * vx; m[3][1] += m[1][1] * vy; m[3][2] += m[2][2] * vz; + } else if (flagBits < Rotation) { + m[3][0] += m[0][0] * vx + m[1][0] * vy; + m[3][1] += m[0][1] * vx + m[1][1] * vy; + m[3][2] += m[2][2] * vz; } else { m[3][0] += m[0][0] * vx + m[1][0] * vy + m[2][0] * vz; m[3][1] += m[0][1] * vx + m[1][1] * vy + m[2][1] * vz; m[3][2] += m[0][2] * vx + m[1][2] * vy + m[2][2] * vz; m[3][3] += m[0][3] * vx + m[1][3] * vy + m[2][3] * vz; - if (flagBits == Rotation) - flagBits |= Translation; - else if (flagBits != (Rotation | Translation)) - flagBits = General; } + flagBits |= Translation; } - #endif /*! @@ -918,28 +993,25 @@ void QMatrix4x4::translate(qreal x, qreal y) if (flagBits == Identity) { m[3][0] = x; m[3][1] = y; - flagBits = Translation; } else if (flagBits == Translation) { m[3][0] += x; m[3][1] += y; } else if (flagBits == Scale) { m[3][0] = m[0][0] * x; m[3][1] = m[1][1] * y; - m[3][2] = 0.; - flagBits |= Translation; - } else if (flagBits == (Scale | Translation)) { + } else if (flagBits == (Translation | Scale)) { m[3][0] += m[0][0] * x; m[3][1] += m[1][1] * y; + } else if (flagBits < Rotation) { + m[3][0] += m[0][0] * x + m[1][0] * y; + m[3][1] += m[0][1] * x + m[1][1] * y; } else { m[3][0] += m[0][0] * x + m[1][0] * y; m[3][1] += m[0][1] * x + m[1][1] * y; m[3][2] += m[0][2] * x + m[1][2] * y; m[3][3] += m[0][3] * x + m[1][3] * y; - if (flagBits == Rotation) - flagBits |= Translation; - else if (flagBits != (Rotation | Translation)) - flagBits = General; } + flagBits |= Translation; } /*! @@ -956,7 +1028,6 @@ void QMatrix4x4::translate(qreal x, qreal y, qreal z) m[3][0] = x; m[3][1] = y; m[3][2] = z; - flagBits = Translation; } else if (flagBits == Translation) { m[3][0] += x; m[3][1] += y; @@ -965,31 +1036,31 @@ void QMatrix4x4::translate(qreal x, qreal y, qreal z) m[3][0] = m[0][0] * x; m[3][1] = m[1][1] * y; m[3][2] = m[2][2] * z; - flagBits |= Translation; - } else if (flagBits == (Scale | Translation)) { + } else if (flagBits == (Translation | Scale)) { m[3][0] += m[0][0] * x; m[3][1] += m[1][1] * y; m[3][2] += m[2][2] * z; + } else if (flagBits < Rotation) { + m[3][0] += m[0][0] * x + m[1][0] * y; + m[3][1] += m[0][1] * x + m[1][1] * y; + m[3][2] += m[2][2] * z; } else { m[3][0] += m[0][0] * x + m[1][0] * y + m[2][0] * z; m[3][1] += m[0][1] * x + m[1][1] * y + m[2][1] * z; m[3][2] += m[0][2] * x + m[1][2] * y + m[2][2] * z; m[3][3] += m[0][3] * x + m[1][3] * y + m[2][3] * z; - if (flagBits == Rotation) - flagBits |= Translation; - else if (flagBits != (Rotation | Translation)) - flagBits = General; } + flagBits |= Translation; } #ifndef QT_NO_VECTOR3D - /*! Multiples this matrix by another that rotates coordinates through \a angle degrees about \a vector. \sa scale(), translate() */ + void QMatrix4x4::rotate(qreal angle, const QVector3D& vector) { rotate(angle, vector.x(), vector.y(), vector.z()); @@ -1009,8 +1080,7 @@ void QMatrix4x4::rotate(qreal angle, qreal x, qreal y, qreal z) { if (angle == 0.0f) return; - QMatrix4x4 m(1); // The "1" says to not load the identity. - qreal c, s, ic; + qreal c, s; if (angle == 90.0f || angle == -270.0f) { s = 1.0f; c = 0.0f; @@ -1025,86 +1095,87 @@ void QMatrix4x4::rotate(qreal angle, qreal x, qreal y, qreal z) c = qCos(a); s = qSin(a); } - bool quick = false; if (x == 0.0f) { if (y == 0.0f) { if (z != 0.0f) { // Rotate around the Z axis. - m.setToIdentity(); - m.m[0][0] = c; - m.m[1][1] = c; - if (z < 0.0f) { - m.m[1][0] = s; - m.m[0][1] = -s; - } else { - m.m[1][0] = -s; - m.m[0][1] = s; - } - m.flagBits = General; - quick = true; + if (z < 0) + s = -s; + qreal tmp; + m[0][0] = (tmp = m[0][0]) * c + m[1][0] * s; + m[1][0] = m[1][0] * c - tmp * s; + m[0][1] = (tmp = m[0][1]) * c + m[1][1] * s; + m[1][1] = m[1][1] * c - tmp * s; + m[0][2] = (tmp = m[0][2]) * c + m[1][2] * s; + m[1][2] = m[1][2] * c - tmp * s; + m[0][3] = (tmp = m[0][3]) * c + m[1][3] * s; + m[1][3] = m[1][3] * c - tmp * s; + + flagBits |= Rotation2D; + return; } } else if (z == 0.0f) { // Rotate around the Y axis. - m.setToIdentity(); - m.m[0][0] = c; - m.m[2][2] = c; - if (y < 0.0f) { - m.m[2][0] = -s; - m.m[0][2] = s; - } else { - m.m[2][0] = s; - m.m[0][2] = -s; - } - m.flagBits = General; - quick = true; + if (y < 0) + s = -s; + qreal tmp; + m[2][0] = (tmp = m[2][0]) * c + m[0][0] * s; + m[0][0] = m[0][0] * c - tmp * s; + m[2][1] = (tmp = m[2][1]) * c + m[0][1] * s; + m[0][1] = m[0][1] * c - tmp * s; + m[2][2] = (tmp = m[2][2]) * c + m[0][2] * s; + m[0][2] = m[0][2] * c - tmp * s; + m[2][3] = (tmp = m[2][3]) * c + m[0][3] * s; + m[0][3] = m[0][3] * c - tmp * s; + + flagBits |= Rotation; + return; } } else if (y == 0.0f && z == 0.0f) { // Rotate around the X axis. - m.setToIdentity(); - m.m[1][1] = c; - m.m[2][2] = c; - if (x < 0.0f) { - m.m[2][1] = s; - m.m[1][2] = -s; - } else { - m.m[2][1] = -s; - m.m[1][2] = s; - } - m.flagBits = General; - quick = true; + if (x < 0) + s = -s; + qreal tmp; + m[1][0] = (tmp = m[1][0]) * c + m[2][0] * s; + m[2][0] = m[2][0] * c - tmp * s; + m[1][1] = (tmp = m[1][1]) * c + m[2][1] * s; + m[2][1] = m[2][1] * c - tmp * s; + m[1][2] = (tmp = m[1][2]) * c + m[2][2] * s; + m[2][2] = m[2][2] * c - tmp * s; + m[1][3] = (tmp = m[1][3]) * c + m[2][3] * s; + m[2][3] = m[2][3] * c - tmp * s; + + flagBits |= Rotation; + return; } - if (!quick) { - qreal len = x * x + y * y + z * z; - if (!qFuzzyIsNull(len - 1.0f) && !qFuzzyIsNull(len)) { - len = qSqrt(len); - x /= len; - y /= len; - z /= len; - } - ic = 1.0f - c; - m.m[0][0] = x * x * ic + c; - m.m[1][0] = x * y * ic - z * s; - m.m[2][0] = x * z * ic + y * s; - m.m[3][0] = 0.0f; - m.m[0][1] = y * x * ic + z * s; - m.m[1][1] = y * y * ic + c; - m.m[2][1] = y * z * ic - x * s; - m.m[3][1] = 0.0f; - m.m[0][2] = x * z * ic - y * s; - m.m[1][2] = y * z * ic + x * s; - m.m[2][2] = z * z * ic + c; - m.m[3][2] = 0.0f; - m.m[0][3] = 0.0f; - m.m[1][3] = 0.0f; - m.m[2][3] = 0.0f; - m.m[3][3] = 1.0f; + + qreal len = x * x + y * y + z * z; + if (!qFuzzyCompare(len, qreal(1)) && !qFuzzyIsNull(len)) { + len = qSqrt(len); + x /= len; + y /= len; + z /= len; } - int flags = flagBits; - *this *= m; - if (flags != Identity) - flagBits = flags | Rotation; - else - flagBits = Rotation; + qreal ic = 1.0f - c; + QMatrix4x4 rot(1); // The "1" says to not load the identity. + rot.m[0][0] = x * x * ic + c; + rot.m[1][0] = x * y * ic - z * s; + rot.m[2][0] = x * z * ic + y * s; + rot.m[3][0] = 0.0f; + rot.m[0][1] = y * x * ic + z * s; + rot.m[1][1] = y * y * ic + c; + rot.m[2][1] = y * z * ic - x * s; + rot.m[3][1] = 0.0f; + rot.m[0][2] = x * z * ic - y * s; + rot.m[1][2] = y * z * ic + x * s; + rot.m[2][2] = z * z * ic + c; + rot.m[3][2] = 0.0f; + rot.m[0][3] = 0.0f; + rot.m[1][3] = 0.0f; + rot.m[2][3] = 0.0f; + rot.m[3][3] = 1.0f; + rot.flagBits = Rotation; + *this *= rot; } /*! @@ -1116,8 +1187,7 @@ void QMatrix4x4::projectedRotate(qreal angle, qreal x, qreal y, qreal z) // and projection back to 2D in a single step. if (angle == 0.0f) return; - QMatrix4x4 m(1); // The "1" says to not load the identity. - qreal c, s, ic; + qreal c, s; if (angle == 90.0f || angle == -270.0f) { s = 1.0f; c = 0.0f; @@ -1132,82 +1202,74 @@ void QMatrix4x4::projectedRotate(qreal angle, qreal x, qreal y, qreal z) c = qCos(a); s = qSin(a); } - bool quick = false; if (x == 0.0f) { if (y == 0.0f) { if (z != 0.0f) { // Rotate around the Z axis. - m.setToIdentity(); - m.m[0][0] = c; - m.m[1][1] = c; - if (z < 0.0f) { - m.m[1][0] = s; - m.m[0][1] = -s; - } else { - m.m[1][0] = -s; - m.m[0][1] = s; - } - m.flagBits = General; - quick = true; + if (z < 0) + s = -s; + qreal tmp; + m[0][0] = (tmp = m[0][0]) * c + m[1][0] * s; + m[1][0] = m[1][0] * c - tmp * s; + m[0][1] = (tmp = m[0][1]) * c + m[1][1] * s; + m[1][1] = m[1][1] * c - tmp * s; + m[0][2] = (tmp = m[0][2]) * c + m[1][2] * s; + m[1][2] = m[1][2] * c - tmp * s; + m[0][3] = (tmp = m[0][3]) * c + m[1][3] * s; + m[1][3] = m[1][3] * c - tmp * s; + + flagBits |= Rotation2D; + return; } } else if (z == 0.0f) { // Rotate around the Y axis. - m.setToIdentity(); - m.m[0][0] = c; - m.m[2][2] = 1.0f; - if (y < 0.0f) { - m.m[0][3] = -s * inv_dist_to_plane; - } else { - m.m[0][3] = s * inv_dist_to_plane; - } - m.flagBits = General; - quick = true; + if (y < 0) + s = -s; + m[0][0] = m[0][0] * c + m[3][0] * s * inv_dist_to_plane; + m[0][1] = m[0][1] * c + m[3][1] * s * inv_dist_to_plane; + m[0][2] = m[0][2] * c + m[3][2] * s * inv_dist_to_plane; + m[0][3] = m[0][3] * c + m[3][3] * s * inv_dist_to_plane; + flagBits = General; + return; } } else if (y == 0.0f && z == 0.0f) { // Rotate around the X axis. - m.setToIdentity(); - m.m[1][1] = c; - m.m[2][2] = 1.0f; - if (x < 0.0f) { - m.m[1][3] = s * inv_dist_to_plane; - } else { - m.m[1][3] = -s * inv_dist_to_plane; - } - m.flagBits = General; - quick = true; + if (x < 0) + s = -s; + m[1][0] = m[1][0] * c - m[3][0] * s * inv_dist_to_plane; + m[1][1] = m[1][1] * c - m[3][1] * s * inv_dist_to_plane; + m[1][2] = m[1][2] * c - m[3][2] * s * inv_dist_to_plane; + m[1][3] = m[1][3] * c - m[3][3] * s * inv_dist_to_plane; + flagBits = General; + return; } - if (!quick) { - qreal len = x * x + y * y + z * z; - if (!qFuzzyIsNull(len - 1.0f) && !qFuzzyIsNull(len)) { - len = qSqrt(len); - x /= len; - y /= len; - z /= len; - } - ic = 1.0f - c; - m.m[0][0] = x * x * ic + c; - m.m[1][0] = x * y * ic - z * s; - m.m[2][0] = 0.0f; - m.m[3][0] = 0.0f; - m.m[0][1] = y * x * ic + z * s; - m.m[1][1] = y * y * ic + c; - m.m[2][1] = 0.0f; - m.m[3][1] = 0.0f; - m.m[0][2] = 0.0f; - m.m[1][2] = 0.0f; - m.m[2][2] = 1.0f; - m.m[3][2] = 0.0f; - m.m[0][3] = (x * z * ic - y * s) * -inv_dist_to_plane; - m.m[1][3] = (y * z * ic + x * s) * -inv_dist_to_plane; - m.m[2][3] = 0.0f; - m.m[3][3] = 1.0f; + qreal len = x * x + y * y + z * z; + if (!qFuzzyIsNull(len - 1.0f) && !qFuzzyIsNull(len)) { + len = qSqrt(len); + x /= len; + y /= len; + z /= len; } - int flags = flagBits; - *this *= m; - if (flags != Identity) - flagBits = flags | Rotation; - else - flagBits = Rotation; + qreal ic = 1.0f - c; + QMatrix4x4 rot(1); // The "1" says to not load the identity. + rot.m[0][0] = x * x * ic + c; + rot.m[1][0] = x * y * ic - z * s; + rot.m[2][0] = 0.0f; + rot.m[3][0] = 0.0f; + rot.m[0][1] = y * x * ic + z * s; + rot.m[1][1] = y * y * ic + c; + rot.m[2][1] = 0.0f; + rot.m[3][1] = 0.0f; + rot.m[0][2] = 0.0f; + rot.m[1][2] = 0.0f; + rot.m[2][2] = 1.0f; + rot.m[3][2] = 0.0f; + rot.m[0][3] = (x * z * ic - y * s) * -inv_dist_to_plane; + rot.m[1][3] = (y * z * ic + x * s) * -inv_dist_to_plane; + rot.m[2][3] = 0.0f; + rot.m[3][3] = 1.0f; + rot.flagBits = General; + *this *= rot; } #ifndef QT_NO_QUATERNION @@ -1249,12 +1311,8 @@ void QMatrix4x4::rotate(const QQuaternion& quaternion) m.m[1][3] = 0.0f; m.m[2][3] = 0.0f; m.m[3][3] = 1.0f; - int flags = flagBits; + m.flagBits = Rotation; *this *= m; - if (flags != Identity) - flagBits = flags | Rotation; - else - flagBits = Rotation; } #endif @@ -1309,22 +1367,6 @@ void QMatrix4x4::ortho(qreal left, qreal right, qreal bottom, qreal top, qreal n qreal width = right - left; qreal invheight = top - bottom; qreal clip = farPlane - nearPlane; -#ifndef QT_NO_VECTOR3D - if (clip == 2.0f && (nearPlane + farPlane) == 0.0f) { - // We can express this projection as a translate and scale - // which will be more efficient to modify with further - // transformations than producing a "General" matrix. - translate(QVector3D - (-(left + right) / width, - -(top + bottom) / invheight, - 0.0f)); - scale(QVector3D - (2.0f / width, - 2.0f / invheight, - -1.0f)); - return; - } -#endif QMatrix4x4 m(1); m.m[0][0] = 2.0f / width; m.m[1][0] = 0.0f; @@ -1342,10 +1384,10 @@ void QMatrix4x4::ortho(qreal left, qreal right, qreal bottom, qreal top, qreal n m.m[1][3] = 0.0f; m.m[2][3] = 0.0f; m.m[3][3] = 1.0f; + m.flagBits = Translation | Scale; // Apply the projection. *this *= m; - return; } /*! @@ -1383,6 +1425,7 @@ void QMatrix4x4::frustum(qreal left, qreal right, qreal bottom, qreal top, qreal m.m[1][3] = 0.0f; m.m[2][3] = -1.0f; m.m[3][3] = 0.0f; + m.flagBits = General; // Apply the projection. *this *= m; @@ -1426,6 +1469,7 @@ void QMatrix4x4::perspective(qreal angle, qreal aspect, qreal nearPlane, qreal f m.m[1][3] = 0.0f; m.m[2][3] = -1.0f; m.m[3][3] = 0.0f; + m.flagBits = General; // Apply the projection. *this *= m; @@ -1446,7 +1490,6 @@ void QMatrix4x4::lookAt(const QVector3D& eye, const QVector3D& center, const QVe QVector3D upVector = QVector3D::crossProduct(side, forward); QMatrix4x4 m(1); - m.m[0][0] = side.x(); m.m[1][0] = side.y(); m.m[2][0] = side.z(); @@ -1463,6 +1506,7 @@ void QMatrix4x4::lookAt(const QVector3D& eye, const QVector3D& center, const QVe m.m[1][3] = 0.0f; m.m[2][3] = 0.0f; m.m[3][3] = 1.0f; + m.flagBits = Rotation; *this *= m; translate(-eye); @@ -1471,6 +1515,8 @@ void QMatrix4x4::lookAt(const QVector3D& eye, const QVector3D& center, const QVe #endif /*! + \deprecated + Flips between right-handed and left-handed coordinate systems by multiplying the y and z co-ordinates by -1. This is normally used to create a left-handed orthographic view without scaling @@ -1480,17 +1526,13 @@ void QMatrix4x4::lookAt(const QVector3D& eye, const QVector3D& center, const QVe */ void QMatrix4x4::flipCoordinates() { - if (flagBits == Scale || flagBits == (Scale | Translation)) { + // Multiplying the y and z coordinates with -1 does NOT flip between right-handed and + // left-handed coordinate systems, it just rotates 180 degrees around the x axis, so + // I'm deprecating this function. + if (flagBits < Rotation2D) { + // Translation | Scale m[1][1] = -m[1][1]; m[2][2] = -m[2][2]; - } else if (flagBits == Translation) { - m[1][1] = -m[1][1]; - m[2][2] = -m[2][2]; - flagBits |= Scale; - } else if (flagBits == Identity) { - m[1][1] = -1.0f; - m[2][2] = -1.0f; - flagBits = Scale; } else { m[1][0] = -m[1][0]; m[1][1] = -m[1][1]; @@ -1500,8 +1542,8 @@ void QMatrix4x4::flipCoordinates() m[2][1] = -m[2][1]; m[2][2] = -m[2][2]; m[2][3] = -m[2][3]; - flagBits = General; } + flagBits |= Scale; } /*! @@ -1568,12 +1610,9 @@ QTransform QMatrix4x4::toTransform(qreal distanceToPlane) const { if (distanceToPlane == 1024.0f) { // Optimize the common case with constants. - return QTransform(m[0][0], m[0][1], - m[0][3] - m[0][2] * inv_dist_to_plane, - m[1][0], m[1][1], - m[1][3] - m[1][2] * inv_dist_to_plane, - m[3][0], m[3][1], - m[3][3] - m[3][2] * inv_dist_to_plane); + return QTransform(m[0][0], m[0][1], m[0][3] - m[0][2] * inv_dist_to_plane, + m[1][0], m[1][1], m[1][3] - m[1][2] * inv_dist_to_plane, + m[3][0], m[3][1], m[3][3] - m[3][2] * inv_dist_to_plane); } else if (distanceToPlane != 0.0f) { // The following projection matrix is pre-multiplied with "matrix": // | 1 0 0 0 | @@ -1654,7 +1693,13 @@ QTransform QMatrix4x4::toTransform(qreal distanceToPlane) const */ QRect QMatrix4x4::mapRect(const QRect& rect) const { - if (flagBits == (Translation | Scale) || flagBits == Scale) { + if (flagBits < Scale) { + // Translation + return QRect(qRound(rect.x() + m[3][0]), + qRound(rect.y() + m[3][1]), + rect.width(), rect.height()); + } else if (flagBits < Rotation2D) { + // Translation | Scale qreal x = rect.x() * m[0][0] + m[3][0]; qreal y = rect.y() * m[1][1] + m[3][1]; qreal w = rect.width() * m[0][0]; @@ -1668,10 +1713,6 @@ QRect QMatrix4x4::mapRect(const QRect& rect) const y -= h; } return QRect(qRound(x), qRound(y), qRound(w), qRound(h)); - } else if (flagBits == Translation) { - return QRect(qRound(rect.x() + m[3][0]), - qRound(rect.y() + m[3][1]), - rect.width(), rect.height()); } QPoint tl = map(rect.topLeft()); @@ -1698,7 +1739,11 @@ QRect QMatrix4x4::mapRect(const QRect& rect) const */ QRectF QMatrix4x4::mapRect(const QRectF& rect) const { - if (flagBits == (Translation | Scale) || flagBits == Scale) { + if (flagBits < Scale) { + // Translation + return rect.translated(m[3][0], m[3][1]); + } else if (flagBits < Rotation2D) { + // Translation | Scale qreal x = rect.x() * m[0][0] + m[3][0]; qreal y = rect.y() * m[1][1] + m[3][1]; qreal w = rect.width() * m[0][0]; @@ -1712,8 +1757,6 @@ QRectF QMatrix4x4::mapRect(const QRectF& rect) const y -= h; } return QRectF(x, y, w, h); - } else if (flagBits == Translation) { - return rect.translated(m[3][0], m[3][1]); } QPointF tl = map(rect.topLeft()); QPointF tr = map(rect.topRight()); @@ -1780,6 +1823,8 @@ QMatrix4x4 QMatrix4x4::orthonormalInverse() const result.m[3][2] = -(result.m[0][2] * m[3][0] + result.m[1][2] * m[3][1] + result.m[2][2] * m[3][2]); result.m[3][3] = 1.0f; + result.flagBits = flagBits; + return result; } @@ -1805,40 +1850,50 @@ QMatrix4x4 QMatrix4x4::orthonormalInverse() const */ void QMatrix4x4::optimize() { - // If the last element is not 1, then it can never be special. - if (m[3][3] != 1.0f) { - flagBits = General; + // If the last row is not (0, 0, 0, 1), the matrix is not a special type. + flagBits = General; + if (m[0][3] != 0 || m[1][3] != 0 || m[2][3] != 0 || m[3][3] != 1) return; - } - // If the upper three elements m12, m13, and m21 are not all zero, - // or the lower elements below the diagonal are not all zero, then - // the matrix can never be special. - if (m[1][0] != 0.0f || m[2][0] != 0.0f || m[2][1] != 0.0f) { - flagBits = General; - return; - } - if (m[0][1] != 0.0f || m[0][2] != 0.0f || m[0][3] != 0.0f || - m[1][2] != 0.0f || m[1][3] != 0.0f || m[2][3] != 0.0f) { - flagBits = General; - return; + flagBits &= ~Perspective; + + // If the last column is (0, 0, 0, 1), then there is no translation. + if (m[3][0] == 0 && m[3][1] == 0 && m[3][2] == 0) + flagBits &= ~Translation; + + // If the two first elements of row 3 and column 3 are 0, then any rotation must be about Z. + if (!m[0][2] && !m[1][2] && !m[2][0] && !m[2][1]) { + flagBits &= ~Rotation; + // If the six non-diagonal elements in the top left 3x3 matrix are 0, there is no rotation. + if (!m[0][1] && !m[1][0]) { + flagBits &= ~Rotation2D; + // Check for identity. + if (m[0][0] == 1 && m[1][1] == 1 && m[2][2] == 1) + flagBits &= ~Scale; + } else { + // If the columns are orthonormal and form a right-handed system, then there is no scale. + qreal det = matrixDet2(m, 0, 1, 0, 1); + qreal lenX = m[0][0] * m[0][0] + m[0][1] * m[0][1]; + qreal lenY = m[1][0] * m[1][0] + m[1][1] * m[1][1]; + qreal lenZ = m[2][2]; + if (qFuzzyCompare(det, qreal(1)) && qFuzzyCompare(lenX, qreal(1)) + && qFuzzyCompare(lenY, qreal(1)) && qFuzzyCompare(lenZ, qreal(1))) + { + flagBits &= ~Scale; + } + } + } else { + // If the columns are orthonormal and form a right-handed system, then there is no scale. + qreal det = matrixDet3(m, 0, 1, 2, 0, 1, 2); + qreal lenX = m[0][0] * m[0][0] + m[0][1] * m[0][1] + m[0][2] * m[0][2]; + qreal lenY = m[1][0] * m[1][0] + m[1][1] * m[1][1] + m[1][2] * m[1][2]; + qreal lenZ = m[2][0] * m[2][0] + m[2][1] * m[2][1] + m[2][2] * m[2][2]; + if (qFuzzyCompare(det, qreal(1)) && qFuzzyCompare(lenX, qreal(1)) + && qFuzzyCompare(lenY, qreal(1)) && qFuzzyCompare(lenZ, qreal(1))) + { + flagBits &= ~Scale; + } } - - // Determine what we have in the remaining regions of the matrix. - bool identityAlongDiagonal - = (m[0][0] == 1.0f && m[1][1] == 1.0f && m[2][2] == 1.0f); - bool translationPresent - = (m[3][0] != 0.0f || m[3][1] != 0.0f || m[3][2] != 0.0f); - - // Now determine the special matrix type. - if (translationPresent && identityAlongDiagonal) - flagBits = Translation; - else if (translationPresent) - flagBits = (Translation | Scale); - else if (identityAlongDiagonal) - flagBits = Identity; - else - flagBits = Scale; } /*! @@ -1855,18 +1910,24 @@ QDebug operator<<(QDebug dbg, const QMatrix4x4 &m) { // Create a string that represents the matrix type. QByteArray bits; - if ((m.flagBits & QMatrix4x4::Identity) != 0) - bits += "Identity,"; - if ((m.flagBits & QMatrix4x4::General) != 0) - bits += "General,"; - if ((m.flagBits & QMatrix4x4::Translation) != 0) - bits += "Translation,"; - if ((m.flagBits & QMatrix4x4::Scale) != 0) - bits += "Scale,"; - if ((m.flagBits & QMatrix4x4::Rotation) != 0) - bits += "Rotation,"; - if (bits.size() > 0) - bits = bits.left(bits.size() - 1); + if (m.flagBits == QMatrix4x4::Identity) { + bits = "Identity"; + } else if (m.flagBits == QMatrix4x4::General) { + bits = "General"; + } else { + if ((m.flagBits & QMatrix4x4::Translation) != 0) + bits += "Translation,"; + if ((m.flagBits & QMatrix4x4::Scale) != 0) + bits += "Scale,"; + if ((m.flagBits & QMatrix4x4::Rotation2D) != 0) + bits += "Rotation2D,"; + if ((m.flagBits & QMatrix4x4::Rotation) != 0) + bits += "Rotation,"; + if ((m.flagBits & QMatrix4x4::Perspective) != 0) + bits += "Perspective,"; + if (bits.size() > 0) + bits = bits.left(bits.size() - 1); + } // Output in row-major order because it is more human-readable. dbg.nospace() << "QMatrix4x4(type:" << bits.constData() << endl diff --git a/src/gui/math3d/qmatrix4x4.h b/src/gui/math3d/qmatrix4x4.h index bfbe4b9793..2c98dd03e3 100644 --- a/src/gui/math3d/qmatrix4x4.h +++ b/src/gui/math3d/qmatrix4x4.h @@ -80,11 +80,13 @@ public: inline const qreal& operator()(int row, int column) const; inline qreal& operator()(int row, int column); +#ifndef QT_NO_VECTOR4D inline QVector4D column(int index) const; inline void setColumn(int index, const QVector4D& value); inline QVector4D row(int index) const; inline void setRow(int index, const QVector4D& value); +#endif inline bool isIdentity() const; inline void setToIdentity(); @@ -188,16 +190,19 @@ private: qreal m[4][4]; // Column-major order to match OpenGL. int flagBits; // Flag bits from the enum below. + // When matrices are multiplied, the flag bits are or-ed together. enum { - Identity = 0x0001, // Identity matrix - General = 0x0002, // General matrix, unknown contents - Translation = 0x0004, // Contains a simple translation - Scale = 0x0008, // Contains a simple scale - Rotation = 0x0010 // Contains a simple rotation + Identity = 0x0000, // Identity matrix + Translation = 0x0001, // Contains a translation + Scale = 0x0002, // Contains a scale + Rotation2D = 0x0004, // Contains a rotation about the Z axis + Rotation = 0x0008, // Contains an arbitrary rotation + Perspective = 0x0010, // Last row is different from (0, 0, 0, 1) + General = 0x001f // General matrix, unknown contents }; // Construct without initializing identity matrix. - QMatrix4x4(int) { flagBits = General; } + QMatrix4x4(int) { } QMatrix4x4 orthonormalInverse() const; @@ -270,6 +275,7 @@ inline qreal& QMatrix4x4::operator()(int aRow, int aColumn) return m[aColumn][aRow]; } +#ifndef QT_NO_VECTOR4D inline QVector4D QMatrix4x4::column(int index) const { Q_ASSERT(index >= 0 && index < 4); @@ -301,6 +307,7 @@ inline void QMatrix4x4::setRow(int index, const QVector4D& value) m[3][index] = value.w(); flagBits = General; } +#endif Q_GUI_EXPORT QMatrix4x4 operator/(const QMatrix4x4& matrix, qreal divisor); @@ -409,15 +416,100 @@ inline QMatrix4x4& QMatrix4x4::operator-=(const QMatrix4x4& other) inline QMatrix4x4& QMatrix4x4::operator*=(const QMatrix4x4& other) { - if (flagBits == Identity) { - *this = other; - return *this; - } else if (other.flagBits == Identity) { - return *this; - } else { - *this = *this * other; + flagBits |= other.flagBits; + + if (flagBits < Rotation2D) { + m[3][0] += m[0][0] * other.m[3][0]; + m[3][1] += m[1][1] * other.m[3][1]; + m[3][2] += m[2][2] * other.m[3][2]; + + m[0][0] *= other.m[0][0]; + m[1][1] *= other.m[1][1]; + m[2][2] *= other.m[2][2]; return *this; } + + qreal m0, m1, m2; + m0 = m[0][0] * other.m[0][0] + + m[1][0] * other.m[0][1] + + m[2][0] * other.m[0][2] + + m[3][0] * other.m[0][3]; + m1 = m[0][0] * other.m[1][0] + + m[1][0] * other.m[1][1] + + m[2][0] * other.m[1][2] + + m[3][0] * other.m[1][3]; + m2 = m[0][0] * other.m[2][0] + + m[1][0] * other.m[2][1] + + m[2][0] * other.m[2][2] + + m[3][0] * other.m[2][3]; + m[3][0] = m[0][0] * other.m[3][0] + + m[1][0] * other.m[3][1] + + m[2][0] * other.m[3][2] + + m[3][0] * other.m[3][3]; + m[0][0] = m0; + m[1][0] = m1; + m[2][0] = m2; + + m0 = m[0][1] * other.m[0][0] + + m[1][1] * other.m[0][1] + + m[2][1] * other.m[0][2] + + m[3][1] * other.m[0][3]; + m1 = m[0][1] * other.m[1][0] + + m[1][1] * other.m[1][1] + + m[2][1] * other.m[1][2] + + m[3][1] * other.m[1][3]; + m2 = m[0][1] * other.m[2][0] + + m[1][1] * other.m[2][1] + + m[2][1] * other.m[2][2] + + m[3][1] * other.m[2][3]; + m[3][1] = m[0][1] * other.m[3][0] + + m[1][1] * other.m[3][1] + + m[2][1] * other.m[3][2] + + m[3][1] * other.m[3][3]; + m[0][1] = m0; + m[1][1] = m1; + m[2][1] = m2; + + m0 = m[0][2] * other.m[0][0] + + m[1][2] * other.m[0][1] + + m[2][2] * other.m[0][2] + + m[3][2] * other.m[0][3]; + m1 = m[0][2] * other.m[1][0] + + m[1][2] * other.m[1][1] + + m[2][2] * other.m[1][2] + + m[3][2] * other.m[1][3]; + m2 = m[0][2] * other.m[2][0] + + m[1][2] * other.m[2][1] + + m[2][2] * other.m[2][2] + + m[3][2] * other.m[2][3]; + m[3][2] = m[0][2] * other.m[3][0] + + m[1][2] * other.m[3][1] + + m[2][2] * other.m[3][2] + + m[3][2] * other.m[3][3]; + m[0][2] = m0; + m[1][2] = m1; + m[2][2] = m2; + + m0 = m[0][3] * other.m[0][0] + + m[1][3] * other.m[0][1] + + m[2][3] * other.m[0][2] + + m[3][3] * other.m[0][3]; + m1 = m[0][3] * other.m[1][0] + + m[1][3] * other.m[1][1] + + m[2][3] * other.m[1][2] + + m[3][3] * other.m[1][3]; + m2 = m[0][3] * other.m[2][0] + + m[1][3] * other.m[2][1] + + m[2][3] * other.m[2][2] + + m[3][3] * other.m[2][3]; + m[3][3] = m[0][3] * other.m[3][0] + + m[1][3] * other.m[3][1] + + m[2][3] * other.m[3][2] + + m[3][3] * other.m[3][3]; + m[0][3] = m0; + m[1][3] = m1; + m[2][3] = m2; + return *this; } inline QMatrix4x4& QMatrix4x4::operator*=(qreal factor) @@ -501,6 +593,7 @@ inline QMatrix4x4 operator+(const QMatrix4x4& m1, const QMatrix4x4& m2) m.m[3][1] = m1.m[3][1] + m2.m[3][1]; m.m[3][2] = m1.m[3][2] + m2.m[3][2]; m.m[3][3] = m1.m[3][3] + m2.m[3][3]; + m.flagBits = QMatrix4x4::General; return m; } @@ -523,81 +616,95 @@ inline QMatrix4x4 operator-(const QMatrix4x4& m1, const QMatrix4x4& m2) m.m[3][1] = m1.m[3][1] - m2.m[3][1]; m.m[3][2] = m1.m[3][2] - m2.m[3][2]; m.m[3][3] = m1.m[3][3] - m2.m[3][3]; + m.flagBits = QMatrix4x4::General; return m; } inline QMatrix4x4 operator*(const QMatrix4x4& m1, const QMatrix4x4& m2) { - if (m1.flagBits == QMatrix4x4::Identity) - return m2; - else if (m2.flagBits == QMatrix4x4::Identity) - return m1; + int flagBits = m1.flagBits | m2.flagBits; + if (flagBits < QMatrix4x4::Rotation2D) { + QMatrix4x4 m = m1; + m.m[3][0] += m.m[0][0] * m2.m[3][0]; + m.m[3][1] += m.m[1][1] * m2.m[3][1]; + m.m[3][2] += m.m[2][2] * m2.m[3][2]; + + m.m[0][0] *= m2.m[0][0]; + m.m[1][1] *= m2.m[1][1]; + m.m[2][2] *= m2.m[2][2]; + m.flagBits = flagBits; + return m; + } QMatrix4x4 m(1); - m.m[0][0] = m1.m[0][0] * m2.m[0][0] + - m1.m[1][0] * m2.m[0][1] + - m1.m[2][0] * m2.m[0][2] + - m1.m[3][0] * m2.m[0][3]; - m.m[0][1] = m1.m[0][1] * m2.m[0][0] + - m1.m[1][1] * m2.m[0][1] + - m1.m[2][1] * m2.m[0][2] + - m1.m[3][1] * m2.m[0][3]; - m.m[0][2] = m1.m[0][2] * m2.m[0][0] + - m1.m[1][2] * m2.m[0][1] + - m1.m[2][2] * m2.m[0][2] + - m1.m[3][2] * m2.m[0][3]; - m.m[0][3] = m1.m[0][3] * m2.m[0][0] + - m1.m[1][3] * m2.m[0][1] + - m1.m[2][3] * m2.m[0][2] + - m1.m[3][3] * m2.m[0][3]; - m.m[1][0] = m1.m[0][0] * m2.m[1][0] + - m1.m[1][0] * m2.m[1][1] + - m1.m[2][0] * m2.m[1][2] + - m1.m[3][0] * m2.m[1][3]; - m.m[1][1] = m1.m[0][1] * m2.m[1][0] + - m1.m[1][1] * m2.m[1][1] + - m1.m[2][1] * m2.m[1][2] + - m1.m[3][1] * m2.m[1][3]; - m.m[1][2] = m1.m[0][2] * m2.m[1][0] + - m1.m[1][2] * m2.m[1][1] + - m1.m[2][2] * m2.m[1][2] + - m1.m[3][2] * m2.m[1][3]; - m.m[1][3] = m1.m[0][3] * m2.m[1][0] + - m1.m[1][3] * m2.m[1][1] + - m1.m[2][3] * m2.m[1][2] + - m1.m[3][3] * m2.m[1][3]; - m.m[2][0] = m1.m[0][0] * m2.m[2][0] + - m1.m[1][0] * m2.m[2][1] + - m1.m[2][0] * m2.m[2][2] + - m1.m[3][0] * m2.m[2][3]; - m.m[2][1] = m1.m[0][1] * m2.m[2][0] + - m1.m[1][1] * m2.m[2][1] + - m1.m[2][1] * m2.m[2][2] + - m1.m[3][1] * m2.m[2][3]; - m.m[2][2] = m1.m[0][2] * m2.m[2][0] + - m1.m[1][2] * m2.m[2][1] + - m1.m[2][2] * m2.m[2][2] + - m1.m[3][2] * m2.m[2][3]; - m.m[2][3] = m1.m[0][3] * m2.m[2][0] + - m1.m[1][3] * m2.m[2][1] + - m1.m[2][3] * m2.m[2][2] + - m1.m[3][3] * m2.m[2][3]; - m.m[3][0] = m1.m[0][0] * m2.m[3][0] + - m1.m[1][0] * m2.m[3][1] + - m1.m[2][0] * m2.m[3][2] + - m1.m[3][0] * m2.m[3][3]; - m.m[3][1] = m1.m[0][1] * m2.m[3][0] + - m1.m[1][1] * m2.m[3][1] + - m1.m[2][1] * m2.m[3][2] + - m1.m[3][1] * m2.m[3][3]; - m.m[3][2] = m1.m[0][2] * m2.m[3][0] + - m1.m[1][2] * m2.m[3][1] + - m1.m[2][2] * m2.m[3][2] + - m1.m[3][2] * m2.m[3][3]; - m.m[3][3] = m1.m[0][3] * m2.m[3][0] + - m1.m[1][3] * m2.m[3][1] + - m1.m[2][3] * m2.m[3][2] + - m1.m[3][3] * m2.m[3][3]; + m.m[0][0] = m1.m[0][0] * m2.m[0][0] + + m1.m[1][0] * m2.m[0][1] + + m1.m[2][0] * m2.m[0][2] + + m1.m[3][0] * m2.m[0][3]; + m.m[0][1] = m1.m[0][1] * m2.m[0][0] + + m1.m[1][1] * m2.m[0][1] + + m1.m[2][1] * m2.m[0][2] + + m1.m[3][1] * m2.m[0][3]; + m.m[0][2] = m1.m[0][2] * m2.m[0][0] + + m1.m[1][2] * m2.m[0][1] + + m1.m[2][2] * m2.m[0][2] + + m1.m[3][2] * m2.m[0][3]; + m.m[0][3] = m1.m[0][3] * m2.m[0][0] + + m1.m[1][3] * m2.m[0][1] + + m1.m[2][3] * m2.m[0][2] + + m1.m[3][3] * m2.m[0][3]; + + m.m[1][0] = m1.m[0][0] * m2.m[1][0] + + m1.m[1][0] * m2.m[1][1] + + m1.m[2][0] * m2.m[1][2] + + m1.m[3][0] * m2.m[1][3]; + m.m[1][1] = m1.m[0][1] * m2.m[1][0] + + m1.m[1][1] * m2.m[1][1] + + m1.m[2][1] * m2.m[1][2] + + m1.m[3][1] * m2.m[1][3]; + m.m[1][2] = m1.m[0][2] * m2.m[1][0] + + m1.m[1][2] * m2.m[1][1] + + m1.m[2][2] * m2.m[1][2] + + m1.m[3][2] * m2.m[1][3]; + m.m[1][3] = m1.m[0][3] * m2.m[1][0] + + m1.m[1][3] * m2.m[1][1] + + m1.m[2][3] * m2.m[1][2] + + m1.m[3][3] * m2.m[1][3]; + + m.m[2][0] = m1.m[0][0] * m2.m[2][0] + + m1.m[1][0] * m2.m[2][1] + + m1.m[2][0] * m2.m[2][2] + + m1.m[3][0] * m2.m[2][3]; + m.m[2][1] = m1.m[0][1] * m2.m[2][0] + + m1.m[1][1] * m2.m[2][1] + + m1.m[2][1] * m2.m[2][2] + + m1.m[3][1] * m2.m[2][3]; + m.m[2][2] = m1.m[0][2] * m2.m[2][0] + + m1.m[1][2] * m2.m[2][1] + + m1.m[2][2] * m2.m[2][2] + + m1.m[3][2] * m2.m[2][3]; + m.m[2][3] = m1.m[0][3] * m2.m[2][0] + + m1.m[1][3] * m2.m[2][1] + + m1.m[2][3] * m2.m[2][2] + + m1.m[3][3] * m2.m[2][3]; + + m.m[3][0] = m1.m[0][0] * m2.m[3][0] + + m1.m[1][0] * m2.m[3][1] + + m1.m[2][0] * m2.m[3][2] + + m1.m[3][0] * m2.m[3][3]; + m.m[3][1] = m1.m[0][1] * m2.m[3][0] + + m1.m[1][1] * m2.m[3][1] + + m1.m[2][1] * m2.m[3][2] + + m1.m[3][1] * m2.m[3][3]; + m.m[3][2] = m1.m[0][2] * m2.m[3][0] + + m1.m[1][2] * m2.m[3][1] + + m1.m[2][2] * m2.m[3][2] + + m1.m[3][2] * m2.m[3][3]; + m.m[3][3] = m1.m[0][3] * m2.m[3][0] + + m1.m[1][3] * m2.m[3][1] + + m1.m[2][3] * m2.m[3][2] + + m1.m[3][3] * m2.m[3][3]; + m.flagBits = flagBits; return m; } @@ -633,19 +740,16 @@ inline QVector3D operator*(const QMatrix4x4& matrix, const QVector3D& vector) qreal x, y, z, w; if (matrix.flagBits == QMatrix4x4::Identity) { return vector; - } else if (matrix.flagBits == QMatrix4x4::Translation) { - return QVector3D(vector.x() + matrix.m[3][0], - vector.y() + matrix.m[3][1], - vector.z() + matrix.m[3][2]); - } else if (matrix.flagBits == - (QMatrix4x4::Translation | QMatrix4x4::Scale)) { + } else if (matrix.flagBits < QMatrix4x4::Rotation2D) { + // Translation | Scale return QVector3D(vector.x() * matrix.m[0][0] + matrix.m[3][0], vector.y() * matrix.m[1][1] + matrix.m[3][1], vector.z() * matrix.m[2][2] + matrix.m[3][2]); - } else if (matrix.flagBits == QMatrix4x4::Scale) { - return QVector3D(vector.x() * matrix.m[0][0], - vector.y() * matrix.m[1][1], - vector.z() * matrix.m[2][2]); + } else if (matrix.flagBits < QMatrix4x4::Rotation) { + // Translation | Scale | Rotation2D + return QVector3D(vector.x() * matrix.m[0][0] + vector.y() * matrix.m[1][0] + matrix.m[3][0], + vector.x() * matrix.m[0][1] + vector.y() * matrix.m[1][1] + matrix.m[3][1], + vector.z() * matrix.m[2][2] + matrix.m[3][2]); } else { x = vector.x() * matrix.m[0][0] + vector.y() * matrix.m[1][0] + @@ -771,16 +875,13 @@ inline QPoint operator*(const QMatrix4x4& matrix, const QPoint& point) yin = point.y(); if (matrix.flagBits == QMatrix4x4::Identity) { return point; - } else if (matrix.flagBits == QMatrix4x4::Translation) { - return QPoint(qRound(xin + matrix.m[3][0]), - qRound(yin + matrix.m[3][1])); - } else if (matrix.flagBits == - (QMatrix4x4::Translation | QMatrix4x4::Scale)) { + } else if (matrix.flagBits < QMatrix4x4::Rotation2D) { + // Translation | Scale return QPoint(qRound(xin * matrix.m[0][0] + matrix.m[3][0]), qRound(yin * matrix.m[1][1] + matrix.m[3][1])); - } else if (matrix.flagBits == QMatrix4x4::Scale) { - return QPoint(qRound(xin * matrix.m[0][0]), - qRound(yin * matrix.m[1][1])); + } else if (matrix.flagBits < QMatrix4x4::Perspective) { + return QPoint(qRound(xin * matrix.m[0][0] + yin * matrix.m[1][0] + matrix.m[3][0]), + qRound(xin * matrix.m[0][1] + yin * matrix.m[1][1] + matrix.m[3][1])); } else { x = xin * matrix.m[0][0] + yin * matrix.m[1][0] + @@ -806,16 +907,13 @@ inline QPointF operator*(const QMatrix4x4& matrix, const QPointF& point) yin = point.y(); if (matrix.flagBits == QMatrix4x4::Identity) { return point; - } else if (matrix.flagBits == QMatrix4x4::Translation) { - return QPointF(xin + matrix.m[3][0], - yin + matrix.m[3][1]); - } else if (matrix.flagBits == - (QMatrix4x4::Translation | QMatrix4x4::Scale)) { + } else if (matrix.flagBits < QMatrix4x4::Rotation2D) { + // Translation | Scale return QPointF(xin * matrix.m[0][0] + matrix.m[3][0], yin * matrix.m[1][1] + matrix.m[3][1]); - } else if (matrix.flagBits == QMatrix4x4::Scale) { - return QPointF(xin * matrix.m[0][0], - yin * matrix.m[1][1]); + } else if (matrix.flagBits < QMatrix4x4::Perspective) { + return QPointF(xin * matrix.m[0][0] + yin * matrix.m[1][0] + matrix.m[3][0], + xin * matrix.m[0][1] + yin * matrix.m[1][1] + matrix.m[3][1]); } else { x = xin * matrix.m[0][0] + yin * matrix.m[1][0] + @@ -853,6 +951,7 @@ inline QMatrix4x4 operator-(const QMatrix4x4& matrix) m.m[3][1] = -matrix.m[3][1]; m.m[3][2] = -matrix.m[3][2]; m.m[3][3] = -matrix.m[3][3]; + m.flagBits = QMatrix4x4::General; return m; } @@ -875,6 +974,7 @@ inline QMatrix4x4 operator*(qreal factor, const QMatrix4x4& matrix) m.m[3][1] = matrix.m[3][1] * factor; m.m[3][2] = matrix.m[3][2] * factor; m.m[3][3] = matrix.m[3][3] * factor; + m.flagBits = QMatrix4x4::General; return m; } @@ -897,6 +997,7 @@ inline QMatrix4x4 operator*(const QMatrix4x4& matrix, qreal factor) m.m[3][1] = matrix.m[3][1] * factor; m.m[3][2] = matrix.m[3][2] * factor; m.m[3][3] = matrix.m[3][3] * factor; + m.flagBits = QMatrix4x4::General; return m; } @@ -939,9 +1040,11 @@ inline QVector3D QMatrix4x4::map(const QVector3D& point) const inline QVector3D QMatrix4x4::mapVector(const QVector3D& vector) const { - if (flagBits == Identity || flagBits == Translation) { + if (flagBits < Scale) { + // Translation return vector; - } else if (flagBits == Scale || flagBits == (Translation | Scale)) { + } else if (flagBits < Rotation2D) { + // Translation | Scale return QVector3D(vector.x() * m[0][0], vector.y() * m[1][1], vector.z() * m[2][2]); diff --git a/src/modules/qt_v8.pri b/src/modules/qt_v8.pri new file mode 100644 index 0000000000..89d6c263e8 --- /dev/null +++ b/src/modules/qt_v8.pri @@ -0,0 +1,16 @@ +QT.v8.VERSION = 5.0.0 +QT.v8.MAJOR_VERSION = 5 +QT.v8.MINOR_VERSION = 0 +QT.v8.PATCH_VERSION = 0 + +QT.v8.name = QtV8 +QT.v8.bins = $$QT_MODULE_BIN_BASE +QT.v8.includes = $$QT_MODULE_INCLUDE_BASE/QtV8 +QT.v8.private_includes = $$QT_MODULE_INCLUDE_BASE/QtV8/$$QT.v8.VERSION +QT.v8.sources = $$QT_MODULE_BASE/src/v8 +QT.v8.libs = $$QT_MODULE_LIB_BASE +QT.v8.plugins = $$QT_MODULE_PLUGIN_BASE +QT.v8.imports = $$QT_MODULE_IMPORT_BASE +QT.v8.depends = +QT.v8.DEFINES = +!contains(QT_CONFIG, static): QT.v8.DEFINES += V8_SHARED USING_V8_SHARED diff --git a/src/src.pro b/src/src.pro index 1e072fbec7..a531355ae9 100644 --- a/src/src.pro +++ b/src/src.pro @@ -13,7 +13,9 @@ SRC_SUBDIRS += src_corelib SRC_SUBDIRS += src_network src_sql src_gui src_xml src_uitools src_widgets src_printsupport src_testlib src_platformsupport nacl: SRC_SUBDIRS -= src_network src_testlib !symbian:contains(QT_CONFIG, dbus):SRC_SUBDIRS += src_dbus + contains(QT_CONFIG, no-gui): SRC_SUBDIRS -= src_gui +!contains(QT_CONFIG, no-v8): SRC_SUBDIRS += src_v8 contains(QT_CONFIG, opengl)|contains(QT_CONFIG, opengles1)|contains(QT_CONFIG, opengles2): SRC_SUBDIRS += src_opengl SRC_SUBDIRS += src_plugins @@ -30,6 +32,8 @@ src_winmain.subdir = $$QT_SOURCE_TREE/src/winmain src_winmain.target = sub-winmain src_corelib.subdir = $$QT_SOURCE_TREE/src/corelib src_corelib.target = sub-corelib +src_v8.subdir = $$QT_SOURCE_TREE/src/v8 +src_v8.target = sub-v8 src_xml.subdir = $$QT_SOURCE_TREE/src/xml src_xml.target = sub-xml src_uitools.subdir = $$QT_SOURCE_TREE/src/uitools diff --git a/src/v8/0001-Add-hashing-and-comparison-methods-to-v8-String.patch b/src/v8/0001-Add-hashing-and-comparison-methods-to-v8-String.patch new file mode 100644 index 0000000000..54a35fda9f --- /dev/null +++ b/src/v8/0001-Add-hashing-and-comparison-methods-to-v8-String.patch @@ -0,0 +1,343 @@ +From e13ce09287a56c920d5ffdc5d4662d49f1838f16 Mon Sep 17 00:00:00 2001 +From: Aaron Kennedy <aaron.kennedy@nokia.com> +Date: Mon, 23 May 2011 15:47:20 +1000 +Subject: [PATCH 01/13] Add hashing and comparison methods to v8::String + +This allows us to more rapidly search for a v8::String inside +a hash of QStrings. +--- + include/v8.h | 44 ++++++++++++++++++++++++++++++ + src/api.cc | 43 +++++++++++++++++++++++++++++ + src/heap-inl.h | 2 + + src/heap.cc | 3 ++ + src/objects-inl.h | 1 + + src/objects.cc | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++- + src/objects.h | 15 +++++++++- + 7 files changed, 182 insertions(+), 3 deletions(-) + +diff --git a/include/v8.h b/include/v8.h +index d15d024..bbd29e9 100644 +--- a/include/v8.h ++++ b/include/v8.h +@@ -994,6 +994,48 @@ class String : public Primitive { + V8EXPORT int Utf8Length() const; + + /** ++ * Returns the hash of this string. ++ */ ++ V8EXPORT uint32_t Hash() const; ++ ++ struct CompleteHashData { ++ CompleteHashData() : length(0), hash(0), symbol_id(0) {} ++ int length; ++ uint32_t hash; ++ uint32_t symbol_id; ++ }; ++ ++ /** ++ * Returns the "complete" hash of the string. This is ++ * all the information about the string needed to implement ++ * a very efficient hash keyed on the string. ++ * ++ * The members of CompleteHashData are: ++ * length: The length of the string. Equivalent to Length() ++ * hash: The hash of the string. Equivalent to Hash() ++ * symbol_id: If the string is a sequential symbol, the symbol ++ * id, otherwise 0. If the symbol ids of two strings are ++ * the same (and non-zero) the two strings are identical. ++ * If the symbol ids are different the strings may still be ++ * identical, but an Equals() check must be performed. ++ */ ++ V8EXPORT CompleteHashData CompleteHash() const; ++ ++ /** ++ * Compute a hash value for the passed UTF16 string ++ * data. ++ */ ++ V8EXPORT static uint32_t ComputeHash(uint16_t *string, int length); ++ V8EXPORT static uint32_t ComputeHash(char *string, int length); ++ ++ /** ++ * Returns true if this string is equal to the external ++ * string data provided. ++ */ ++ V8EXPORT bool Equals(uint16_t *string, int length); ++ V8EXPORT bool Equals(char *string, int length); ++ ++ /** + * Write the contents of the string to an external buffer. + * If no arguments are given, expects the buffer to be large + * enough to hold the entire string and NULL terminator. Copies +@@ -1023,6 +1065,8 @@ class String : public Primitive { + HINT_MANY_WRITES_EXPECTED = 1 + }; + ++ V8EXPORT uint16_t GetCharacter(int index); ++ + V8EXPORT int Write(uint16_t* buffer, + int start = 0, + int length = -1, +diff --git a/src/api.cc b/src/api.cc +index a2373cd..381935b 100644 +--- a/src/api.cc ++++ b/src/api.cc +@@ -3284,6 +3284,49 @@ int String::Utf8Length() const { + return str->Utf8Length(); + } + ++uint32_t String::Hash() const { ++ i::Handle<i::String> str = Utils::OpenHandle(this); ++ if (IsDeadCheck(str->GetIsolate(), "v8::String::Hash()")) return 0; ++ return str->Hash(); ++} ++ ++String::CompleteHashData String::CompleteHash() const { ++ i::Handle<i::String> str = Utils::OpenHandle(this); ++ if (IsDeadCheck(str->GetIsolate(), "v8::String::CompleteHash()")) return CompleteHashData(); ++ CompleteHashData result; ++ result.length = str->length(); ++ result.hash = str->Hash(); ++ if (str->IsSeqString()) ++ result.symbol_id = i::SeqString::cast(*str)->symbol_id(); ++ return result; ++} ++ ++uint32_t String::ComputeHash(uint16_t *string, int length) { ++ return i::HashSequentialString<i::uc16>(string, length) >> i::String::kHashShift; ++} ++ ++uint32_t String::ComputeHash(char *string, int length) { ++ return i::HashSequentialString<char>(string, length) >> i::String::kHashShift; ++} ++ ++uint16_t String::GetCharacter(int index) ++{ ++ i::Handle<i::String> str = Utils::OpenHandle(this); ++ return str->Get(index); ++} ++ ++bool String::Equals(uint16_t *string, int length) { ++ i::Handle<i::String> str = Utils::OpenHandle(this); ++ if (IsDeadCheck(str->GetIsolate(), "v8::String::Equals()")) return 0; ++ return str->SlowEqualsExternal(string, length); ++} ++ ++bool String::Equals(char *string, int length) ++{ ++ i::Handle<i::String> str = Utils::OpenHandle(this); ++ if (IsDeadCheck(str->GetIsolate(), "v8::String::Equals()")) return 0; ++ return str->SlowEqualsExternal(string, length); ++} + + int String::WriteUtf8(char* buffer, + int capacity, +diff --git a/src/heap-inl.h b/src/heap-inl.h +index 99737ed..f4fce7b 100644 +--- a/src/heap-inl.h ++++ b/src/heap-inl.h +@@ -93,6 +93,7 @@ MaybeObject* Heap::AllocateAsciiSymbol(Vector<const char> str, + String* answer = String::cast(result); + answer->set_length(str.length()); + answer->set_hash_field(hash_field); ++ SeqString::cast(answer)->set_symbol_id(0); + + ASSERT_EQ(size, answer->Size()); + +@@ -126,6 +127,7 @@ MaybeObject* Heap::AllocateTwoByteSymbol(Vector<const uc16> str, + String* answer = String::cast(result); + answer->set_length(str.length()); + answer->set_hash_field(hash_field); ++ SeqString::cast(answer)->set_symbol_id(0); + + ASSERT_EQ(size, answer->Size()); + +diff --git a/src/heap.cc b/src/heap.cc +index 2b6c11f..930c97b 100644 +--- a/src/heap.cc ++++ b/src/heap.cc +@@ -3519,6 +3519,7 @@ MaybeObject* Heap::AllocateInternalSymbol(unibrow::CharacterStream* buffer, + String* answer = String::cast(result); + answer->set_length(chars); + answer->set_hash_field(hash_field); ++ SeqString::cast(result)->set_symbol_id(0); + + ASSERT_EQ(size, answer->Size()); + +@@ -3561,6 +3562,7 @@ MaybeObject* Heap::AllocateRawAsciiString(int length, PretenureFlag pretenure) { + HeapObject::cast(result)->set_map(ascii_string_map()); + String::cast(result)->set_length(length); + String::cast(result)->set_hash_field(String::kEmptyHashField); ++ SeqString::cast(result)->set_symbol_id(0); + ASSERT_EQ(size, HeapObject::cast(result)->Size()); + return result; + } +@@ -3596,6 +3598,7 @@ MaybeObject* Heap::AllocateRawTwoByteString(int length, + HeapObject::cast(result)->set_map(string_map()); + String::cast(result)->set_length(length); + String::cast(result)->set_hash_field(String::kEmptyHashField); ++ SeqString::cast(result)->set_symbol_id(0); + ASSERT_EQ(size, HeapObject::cast(result)->Size()); + return result; + } +diff --git a/src/objects-inl.h b/src/objects-inl.h +index 65aec5d..c82080d 100644 +--- a/src/objects-inl.h ++++ b/src/objects-inl.h +@@ -1924,6 +1924,7 @@ INT_ACCESSORS(ExternalArray, length, kLengthOffset) + + + SMI_ACCESSORS(String, length, kLengthOffset) ++SMI_ACCESSORS(SeqString, symbol_id, kSymbolIdOffset) + + + uint32_t String::hash_field() { +diff --git a/src/objects.cc b/src/objects.cc +index df61956..dc4b260 100644 +--- a/src/objects.cc ++++ b/src/objects.cc +@@ -5346,6 +5346,66 @@ static inline bool CompareStringContentsPartial(Isolate* isolate, + } + } + ++bool String::SlowEqualsExternal(uc16 *string, int length) { ++ int len = this->length(); ++ if (len != length) return false; ++ if (len == 0) return true; ++ ++ // We know the strings are both non-empty. Compare the first chars ++ // before we try to flatten the strings. ++ if (this->Get(0) != string[0]) return false; ++ ++ String* lhs = this->TryFlattenGetString(); ++ ++ if (lhs->IsFlat()) { ++ if (lhs->IsAsciiRepresentation()) { ++ Vector<const char> vec1 = lhs->ToAsciiVector(); ++ VectorIterator<char> buf1(vec1); ++ VectorIterator<uc16> ib(string, length); ++ return CompareStringContents(&buf1, &ib); ++ } else { ++ Vector<const uc16> vec1 = lhs->ToUC16Vector(); ++ Vector<const uc16> vec2(string, length); ++ return CompareRawStringContents(vec1, vec2); ++ } ++ } else { ++ Isolate* isolate = GetIsolate(); ++ isolate->objects_string_compare_buffer_a()->Reset(0, lhs); ++ VectorIterator<uc16> ib(string, length); ++ return CompareStringContents(isolate->objects_string_compare_buffer_a(), &ib); ++ } ++} ++ ++bool String::SlowEqualsExternal(char *string, int length) ++{ ++ int len = this->length(); ++ if (len != length) return false; ++ if (len == 0) return true; ++ ++ // We know the strings are both non-empty. Compare the first chars ++ // before we try to flatten the strings. ++ if (this->Get(0) != string[0]) return false; ++ ++ String* lhs = this->TryFlattenGetString(); ++ ++ if (StringShape(lhs).IsSequentialAscii()) { ++ const char* str1 = SeqAsciiString::cast(lhs)->GetChars(); ++ return CompareRawStringContents(Vector<const char>(str1, len), ++ Vector<const char>(string, len)); ++ } ++ ++ if (lhs->IsFlat()) { ++ Vector<const uc16> vec1 = lhs->ToUC16Vector(); ++ VectorIterator<const uc16> buf1(vec1); ++ VectorIterator<char> buf2(string, length); ++ return CompareStringContents(&buf1, &buf2); ++ } else { ++ Isolate* isolate = GetIsolate(); ++ isolate->objects_string_compare_buffer_a()->Reset(0, lhs); ++ VectorIterator<char> ib(string, length); ++ return CompareStringContents(isolate->objects_string_compare_buffer_a(), &ib); ++ } ++} + + bool String::SlowEquals(String* other) { + // Fast check: negative check with lengths. +@@ -8655,9 +8715,24 @@ class AsciiSymbolKey : public SequentialSymbolKey<char> { + + MaybeObject* AsObject() { + if (hash_field_ == 0) Hash(); +- return HEAP->AllocateAsciiSymbol(string_, hash_field_); ++ MaybeObject *result = HEAP->AllocateAsciiSymbol(string_, hash_field_); ++ if (!result->IsFailure() && result->ToObjectUnchecked()->IsSeqString()) { ++ while (true) { ++ Atomic32 my_symbol_id = next_symbol_id; ++ if (my_symbol_id > Smi::kMaxValue) ++ break; ++ if (my_symbol_id == NoBarrier_CompareAndSwap(&next_symbol_id, my_symbol_id, my_symbol_id + 1)) { ++ SeqString::cast(result->ToObjectUnchecked())->set_symbol_id(my_symbol_id); ++ break; ++ } ++ } ++ } ++ return result; + } ++ ++ static Atomic32 next_symbol_id; + }; ++Atomic32 AsciiSymbolKey::next_symbol_id = 1; + + + class TwoByteSymbolKey : public SequentialSymbolKey<uc16> { +diff --git a/src/objects.h b/src/objects.h +index e966b3d..6e26f57 100644 +--- a/src/objects.h ++++ b/src/objects.h +@@ -5359,6 +5359,9 @@ class String: public HeapObject { + bool IsAsciiEqualTo(Vector<const char> str); + bool IsTwoByteEqualTo(Vector<const uc16> str); + ++ bool SlowEqualsExternal(uc16 *string, int length); ++ bool SlowEqualsExternal(char *string, int length); ++ + // Return a UTF8 representation of the string. The string is null + // terminated but may optionally contain nulls. Length is returned + // in length_output if length_output is not a null pointer The string +@@ -5610,9 +5613,17 @@ class String: public HeapObject { + class SeqString: public String { + public: + ++ // Get and set the symbol id of the string ++ inline int symbol_id(); ++ inline void set_symbol_id(int value); ++ + // Casting. + static inline SeqString* cast(Object* obj); + ++ // Layout description. ++ static const int kSymbolIdOffset = String::kSize; ++ static const int kSize = kSymbolIdOffset + kPointerSize; ++ + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(SeqString); + }; +@@ -5647,7 +5658,7 @@ class SeqAsciiString: public SeqString { + } + + // Layout description. +- static const int kHeaderSize = String::kSize; ++ static const int kHeaderSize = SeqString::kSize; + static const int kAlignedSize = POINTER_SIZE_ALIGN(kHeaderSize); + + // Maximal memory usage for a single sequential ASCII string. +@@ -5701,7 +5712,7 @@ class SeqTwoByteString: public SeqString { + } + + // Layout description. +- static const int kHeaderSize = String::kSize; ++ static const int kHeaderSize = SeqString::kSize; + static const int kAlignedSize = POINTER_SIZE_ALIGN(kHeaderSize); + + // Maximal memory usage for a single sequential two-byte string. +-- +1.7.2.3 + diff --git a/src/v8/0002-Add-a-bit-field-3-to-Map.patch b/src/v8/0002-Add-a-bit-field-3-to-Map.patch new file mode 100644 index 0000000000..dda9fa0dff --- /dev/null +++ b/src/v8/0002-Add-a-bit-field-3-to-Map.patch @@ -0,0 +1,118 @@ +From 7c9cfff80b7864d5687432d424074e51712c4a07 Mon Sep 17 00:00:00 2001 +From: Aaron Kennedy <aaron.kennedy@nokia.com> +Date: Mon, 23 May 2011 15:55:26 +1000 +Subject: [PATCH 02/13] Add a bit field 3 to Map + +Bit field 3 will be used to add QML specific map flags. +--- + src/heap.cc | 2 ++ + src/objects-inl.h | 10 ++++++++++ + src/objects.cc | 2 ++ + src/objects.h | 9 ++++++++- + 4 files changed, 22 insertions(+), 1 deletions(-) + +diff --git a/src/heap.cc b/src/heap.cc +index 930c97b..900f462 100644 +--- a/src/heap.cc ++++ b/src/heap.cc +@@ -1573,6 +1573,7 @@ MaybeObject* Heap::AllocatePartialMap(InstanceType instance_type, + reinterpret_cast<Map*>(result)->set_unused_property_fields(0); + reinterpret_cast<Map*>(result)->set_bit_field(0); + reinterpret_cast<Map*>(result)->set_bit_field2(0); ++ reinterpret_cast<Map*>(result)->set_bit_field3(0); + return result; + } + +@@ -1599,6 +1600,7 @@ MaybeObject* Heap::AllocateMap(InstanceType instance_type, int instance_size) { + map->set_unused_property_fields(0); + map->set_bit_field(0); + map->set_bit_field2((1 << Map::kIsExtensible) | (1 << Map::kHasFastElements)); ++ map->set_bit_field3(0); + + // If the map object is aligned fill the padding area with Smi 0 objects. + if (Map::kPadStart < Map::kSize) { +diff --git a/src/objects-inl.h b/src/objects-inl.h +index c82080d..cce3edd 100644 +--- a/src/objects-inl.h ++++ b/src/objects-inl.h +@@ -2430,6 +2430,16 @@ void Map::set_bit_field2(byte value) { + } + + ++byte Map::bit_field3() { ++ return READ_BYTE_FIELD(this, kBitField3Offset); ++} ++ ++ ++void Map::set_bit_field3(byte value) { ++ WRITE_BYTE_FIELD(this, kBitField3Offset, value); ++} ++ ++ + void Map::set_non_instance_prototype(bool value) { + if (value) { + set_bit_field(bit_field() | (1 << kHasNonInstancePrototype)); +diff --git a/src/objects.cc b/src/objects.cc +index dc4b260..79d7240 100644 +--- a/src/objects.cc ++++ b/src/objects.cc +@@ -3614,6 +3614,7 @@ MaybeObject* Map::CopyDropDescriptors() { + } + Map::cast(result)->set_bit_field(bit_field()); + Map::cast(result)->set_bit_field2(bit_field2()); ++ Map::cast(result)->set_bit_field3(bit_field3()); + Map::cast(result)->set_is_shared(false); + Map::cast(result)->ClearCodeCache(heap); + return result; +@@ -3642,6 +3643,7 @@ MaybeObject* Map::CopyNormalized(PropertyNormalizationMode mode, + + Map::cast(result)->set_bit_field(bit_field()); + Map::cast(result)->set_bit_field2(bit_field2()); ++ Map::cast(result)->set_bit_field3(bit_field3()); + + Map::cast(result)->set_is_shared(sharing == SHARED_NORMALIZED_MAP); + +diff --git a/src/objects.h b/src/objects.h +index 6e26f57..07e1089 100644 +--- a/src/objects.h ++++ b/src/objects.h +@@ -3597,6 +3597,10 @@ class Map: public HeapObject { + inline byte bit_field2(); + inline void set_bit_field2(byte value); + ++ // Bit field 3. ++ inline byte bit_field3(); ++ inline void set_bit_field3(byte value); ++ + // Tells whether the object in the prototype property will be used + // for instances created from this function. If the prototype + // property is set to a value that is not a JSObject, the prototype +@@ -3844,7 +3848,7 @@ class Map: public HeapObject { + // Layout description. + static const int kInstanceSizesOffset = HeapObject::kHeaderSize; + static const int kInstanceAttributesOffset = kInstanceSizesOffset + kIntSize; +- static const int kPrototypeOffset = kInstanceAttributesOffset + kIntSize; ++ static const int kPrototypeOffset = POINTER_SIZE_ALIGN(kInstanceAttributesOffset + 2 * kIntSize); + static const int kConstructorOffset = kPrototypeOffset + kPointerSize; + static const int kInstanceDescriptorsOffset = + kConstructorOffset + kPointerSize; +@@ -3876,6 +3880,7 @@ class Map: public HeapObject { + static const int kUnusedPropertyFieldsOffset = kInstanceAttributesOffset + 1; + static const int kBitFieldOffset = kInstanceAttributesOffset + 2; + static const int kBitField2Offset = kInstanceAttributesOffset + 3; ++ static const int kBitField3Offset = kInstanceAttributesOffset + 4; + + STATIC_CHECK(kInstanceTypeOffset == Internals::kMapInstanceTypeOffset); + +@@ -3898,6 +3903,8 @@ class Map: public HeapObject { + static const int kIsShared = 5; + static const int kHasExternalArrayElements = 6; + ++ // Bit positions for bit field 3 ++ + // Layout of the default cache. It holds alternating name and code objects. + static const int kCodeCacheEntrySize = 2; + static const int kCodeCacheEntryNameOffset = 0; +-- +1.7.2.3 + diff --git a/src/v8/0003-Add-a-fallback-mode-for-named-property-interceptors.patch b/src/v8/0003-Add-a-fallback-mode-for-named-property-interceptors.patch new file mode 100644 index 0000000000..50f97c7de8 --- /dev/null +++ b/src/v8/0003-Add-a-fallback-mode-for-named-property-interceptors.patch @@ -0,0 +1,364 @@ +From ae8688b53d67044f2c9b0cce25fc282b078610c1 Mon Sep 17 00:00:00 2001 +From: Aaron Kennedy <aaron.kennedy@nokia.com> +Date: Mon, 23 May 2011 16:21:02 +1000 +Subject: [PATCH 03/13] Add a "fallback" mode for named property interceptors + +By default interceptors are called before the normal property +resolution on objects. When an interceptor is installed as a +"fallback" interceptor, it is only called if the object doesn't +already have the property. + +In the case of a global object having an fallback interceptor, +the interceptor is not invoked at all for var or function +declarations. +--- + include/v8.h | 8 ++++++++ + src/api.cc | 29 +++++++++++++++++++++++++++++ + src/factory.cc | 4 ++++ + src/handles.cc | 6 ++++-- + src/handles.h | 3 ++- + src/objects-inl.h | 16 ++++++++++++++++ + src/objects.cc | 22 ++++++++++++++++------ + src/objects.h | 18 ++++++++++++++---- + src/runtime.cc | 11 ++++++----- + 9 files changed, 99 insertions(+), 18 deletions(-) + +diff --git a/include/v8.h b/include/v8.h +index bbd29e9..85452aa 100644 +--- a/include/v8.h ++++ b/include/v8.h +@@ -2169,6 +2169,7 @@ class V8EXPORT FunctionTemplate : public Template { + NamedPropertyQuery query, + NamedPropertyDeleter remover, + NamedPropertyEnumerator enumerator, ++ bool is_fallback, + Handle<Value> data); + void SetIndexedInstancePropertyHandler(IndexedPropertyGetter getter, + IndexedPropertySetter setter, +@@ -2253,6 +2254,13 @@ class V8EXPORT ObjectTemplate : public Template { + NamedPropertyEnumerator enumerator = 0, + Handle<Value> data = Handle<Value>()); + ++ void SetFallbackPropertyHandler(NamedPropertyGetter getter, ++ NamedPropertySetter setter = 0, ++ NamedPropertyQuery query = 0, ++ NamedPropertyDeleter deleter = 0, ++ NamedPropertyEnumerator enumerator = 0, ++ Handle<Value> data = Handle<Value>()); ++ + /** + * Sets an indexed property handler on the object template. + * +diff --git a/src/api.cc b/src/api.cc +index 381935b..8b0b32a 100644 +--- a/src/api.cc ++++ b/src/api.cc +@@ -981,6 +981,7 @@ void FunctionTemplate::SetNamedInstancePropertyHandler( + NamedPropertyQuery query, + NamedPropertyDeleter remover, + NamedPropertyEnumerator enumerator, ++ bool is_fallback, + Handle<Value> data) { + i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate(); + if (IsDeadCheck(isolate, +@@ -999,6 +1000,7 @@ void FunctionTemplate::SetNamedInstancePropertyHandler( + if (query != 0) SET_FIELD_WRAPPED(obj, set_query, query); + if (remover != 0) SET_FIELD_WRAPPED(obj, set_deleter, remover); + if (enumerator != 0) SET_FIELD_WRAPPED(obj, set_enumerator, enumerator); ++ obj->set_is_fallback(i::Smi::FromInt(is_fallback)); + + if (data.IsEmpty()) data = v8::Undefined(); + obj->set_data(*Utils::OpenHandle(*data)); +@@ -1143,6 +1145,33 @@ void ObjectTemplate::SetNamedPropertyHandler(NamedPropertyGetter getter, + query, + remover, + enumerator, ++ false, ++ data); ++} ++ ++ ++void ObjectTemplate::SetFallbackPropertyHandler(NamedPropertyGetter getter, ++ NamedPropertySetter setter, ++ NamedPropertyQuery query, ++ NamedPropertyDeleter remover, ++ NamedPropertyEnumerator enumerator, ++ Handle<Value> data) { ++ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate(); ++ if (IsDeadCheck(isolate, "v8::ObjectTemplate::SetFallbackPropertyHandler()")) { ++ return; ++ } ++ ENTER_V8(isolate); ++ i::HandleScope scope(isolate); ++ EnsureConstructor(this); ++ i::FunctionTemplateInfo* constructor = ++ i::FunctionTemplateInfo::cast(Utils::OpenHandle(this)->constructor()); ++ i::Handle<i::FunctionTemplateInfo> cons(constructor); ++ Utils::ToLocal(cons)->SetNamedInstancePropertyHandler(getter, ++ setter, ++ query, ++ remover, ++ enumerator, ++ true, + data); + } + +diff --git a/src/factory.cc b/src/factory.cc +index 7dee66f..dcdc645 100644 +--- a/src/factory.cc ++++ b/src/factory.cc +@@ -1058,6 +1058,10 @@ Handle<JSFunction> Factory::CreateApiFunction( + // Set interceptor information in the map. + if (!obj->named_property_handler()->IsUndefined()) { + map->set_has_named_interceptor(); ++ ++ InterceptorInfo *nph = InterceptorInfo::cast(obj->named_property_handler()); ++ bool is_fallback = nph->is_fallback()->IsUndefined()?false:nph->is_fallback()->value(); ++ map->set_named_interceptor_is_fallback(is_fallback); + } + if (!obj->indexed_property_handler()->IsUndefined()) { + map->set_has_indexed_interceptor(); +diff --git a/src/handles.cc b/src/handles.cc +index 326de86..dd3a86c 100644 +--- a/src/handles.cc ++++ b/src/handles.cc +@@ -262,9 +262,11 @@ Handle<Object> SetProperty(Handle<JSObject> object, + Handle<String> key, + Handle<Object> value, + PropertyAttributes attributes, +- StrictModeFlag strict_mode) { ++ StrictModeFlag strict_mode, ++ bool skip_fallback_interceptor) { + CALL_HEAP_FUNCTION(object->GetIsolate(), +- object->SetProperty(*key, *value, attributes, strict_mode), ++ object->SetProperty(*key, *value, attributes, strict_mode, ++ skip_fallback_interceptor), + Object); + } + +diff --git a/src/handles.h b/src/handles.h +index 3839f37..4b42506 100644 +--- a/src/handles.h ++++ b/src/handles.h +@@ -188,7 +188,8 @@ Handle<Object> SetProperty(Handle<JSObject> object, + Handle<String> key, + Handle<Object> value, + PropertyAttributes attributes, +- StrictModeFlag strict_mode); ++ StrictModeFlag strict_mode, ++ bool skip_fallback_interceptor = false); + + Handle<Object> SetProperty(Handle<Object> object, + Handle<Object> key, +diff --git a/src/objects-inl.h b/src/objects-inl.h +index cce3edd..6aaca2f 100644 +--- a/src/objects-inl.h ++++ b/src/objects-inl.h +@@ -2521,6 +2521,21 @@ bool Map::is_shared() { + } + + ++void Map::set_named_interceptor_is_fallback(bool value) ++{ ++ if (value) { ++ set_bit_field3(bit_field3() | (1 << kNamedInterceptorIsFallback)); ++ } else { ++ set_bit_field3(bit_field3() & ~(1 << kNamedInterceptorIsFallback)); ++ } ++} ++ ++bool Map::named_interceptor_is_fallback() ++{ ++ return ((1 << kNamedInterceptorIsFallback) & bit_field3()) != 0; ++} ++ ++ + JSFunction* Map::unchecked_constructor() { + return reinterpret_cast<JSFunction*>(READ_FIELD(this, kConstructorOffset)); + } +@@ -2970,6 +2985,7 @@ ACCESSORS(InterceptorInfo, query, Object, kQueryOffset) + ACCESSORS(InterceptorInfo, deleter, Object, kDeleterOffset) + ACCESSORS(InterceptorInfo, enumerator, Object, kEnumeratorOffset) + ACCESSORS(InterceptorInfo, data, Object, kDataOffset) ++ACCESSORS(InterceptorInfo, is_fallback, Smi, kFallbackOffset) + + ACCESSORS(CallHandlerInfo, callback, Object, kCallbackOffset) + ACCESSORS(CallHandlerInfo, data, Object, kDataOffset) +diff --git a/src/objects.cc b/src/objects.cc +index 79d7240..15e2cdb 100644 +--- a/src/objects.cc ++++ b/src/objects.cc +@@ -1712,9 +1712,10 @@ MaybeObject* JSObject::SetPropertyWithInterceptor( + MaybeObject* JSObject::SetProperty(String* name, + Object* value, + PropertyAttributes attributes, +- StrictModeFlag strict_mode) { ++ StrictModeFlag strict_mode, ++ bool skip_fallback_interceptor) { + LookupResult result; +- LocalLookup(name, &result); ++ LocalLookup(name, &result, skip_fallback_interceptor); + return SetProperty(&result, name, value, attributes, strict_mode); + } + +@@ -3148,7 +3149,8 @@ AccessorDescriptor* Map::FindAccessor(String* name) { + } + + +-void JSObject::LocalLookup(String* name, LookupResult* result) { ++void JSObject::LocalLookup(String* name, LookupResult* result, ++ bool skip_fallback_interceptor) { + ASSERT(name->IsString()); + + Heap* heap = GetHeap(); +@@ -3174,22 +3176,30 @@ void JSObject::LocalLookup(String* name, LookupResult* result) { + } + + // Check for lookup interceptor except when bootstrapping. +- if (HasNamedInterceptor() && !heap->isolate()->bootstrapper()->IsActive()) { ++ bool wouldIntercept = HasNamedInterceptor() && !heap->isolate()->bootstrapper()->IsActive(); ++ if (wouldIntercept && !map()->named_interceptor_is_fallback()) { + result->InterceptorResult(this); + return; + } + + LocalLookupRealNamedProperty(name, result); ++ ++ if (wouldIntercept && !skip_fallback_interceptor && !result->IsProperty() && ++ map()->named_interceptor_is_fallback()) { ++ result->InterceptorResult(this); ++ return; ++ } + } + + +-void JSObject::Lookup(String* name, LookupResult* result) { ++void JSObject::Lookup(String* name, LookupResult* result, ++ bool skip_fallback_interceptor) { + // Ecma-262 3rd 8.6.2.4 + Heap* heap = GetHeap(); + for (Object* current = this; + current != heap->null_value(); + current = JSObject::cast(current)->GetPrototype()) { +- JSObject::cast(current)->LocalLookup(name, result); ++ JSObject::cast(current)->LocalLookup(name, result, skip_fallback_interceptor); + if (result->IsProperty()) return; + } + result->NotFound(); +diff --git a/src/objects.h b/src/objects.h +index 07e1089..a209cd0 100644 +--- a/src/objects.h ++++ b/src/objects.h +@@ -1405,7 +1405,8 @@ class JSObject: public HeapObject { + MUST_USE_RESULT MaybeObject* SetProperty(String* key, + Object* value, + PropertyAttributes attributes, +- StrictModeFlag strict_mode); ++ StrictModeFlag strict_mode, ++ bool skip_fallback_interceptor = false); + MUST_USE_RESULT MaybeObject* SetProperty(LookupResult* result, + String* key, + Object* value, +@@ -1637,8 +1638,8 @@ class JSObject: public HeapObject { + + // Lookup a property. If found, the result is valid and has + // detailed information. +- void LocalLookup(String* name, LookupResult* result); +- void Lookup(String* name, LookupResult* result); ++ void LocalLookup(String* name, LookupResult* result, bool skip_fallback_interceptor = false); ++ void Lookup(String* name, LookupResult* result, bool skip_fallback_interceptor = false); + + // The following lookup functions skip interceptors. + void LocalLookupRealNamedProperty(String* name, LookupResult* result); +@@ -3714,6 +3715,12 @@ class Map: public HeapObject { + inline void set_is_access_check_needed(bool access_check_needed); + inline bool is_access_check_needed(); + ++ ++ // Whether the named interceptor is a fallback interceptor or not ++ inline void set_named_interceptor_is_fallback(bool value); ++ inline bool named_interceptor_is_fallback(); ++ ++ + // [prototype]: implicit prototype object. + DECL_ACCESSORS(prototype, Object) + +@@ -3904,6 +3911,7 @@ class Map: public HeapObject { + static const int kHasExternalArrayElements = 6; + + // Bit positions for bit field 3 ++ static const int kNamedInterceptorIsFallback = 0; + + // Layout of the default cache. It holds alternating name and code objects. + static const int kCodeCacheEntrySize = 2; +@@ -6276,6 +6284,7 @@ class InterceptorInfo: public Struct { + DECL_ACCESSORS(deleter, Object) + DECL_ACCESSORS(enumerator, Object) + DECL_ACCESSORS(data, Object) ++ DECL_ACCESSORS(is_fallback, Smi) + + static inline InterceptorInfo* cast(Object* obj); + +@@ -6295,7 +6304,8 @@ class InterceptorInfo: public Struct { + static const int kDeleterOffset = kQueryOffset + kPointerSize; + static const int kEnumeratorOffset = kDeleterOffset + kPointerSize; + static const int kDataOffset = kEnumeratorOffset + kPointerSize; +- static const int kSize = kDataOffset + kPointerSize; ++ static const int kFallbackOffset = kDataOffset + kPointerSize; ++ static const int kSize = kFallbackOffset + kPointerSize; + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(InterceptorInfo); +diff --git a/src/runtime.cc b/src/runtime.cc +index 7335da8..660352c 100644 +--- a/src/runtime.cc ++++ b/src/runtime.cc +@@ -1097,7 +1097,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DeclareGlobals) { + // Lookup the property in the global object, and don't set the + // value of the variable if the property is already there. + LookupResult lookup; +- global->Lookup(*name, &lookup); ++ global->Lookup(*name, &lookup, true); + if (lookup.IsProperty()) { + // Determine if the property is local by comparing the holder + // against the global object. The information will be used to +@@ -1152,7 +1152,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DeclareGlobals) { + } + + LookupResult lookup; +- global->LocalLookup(*name, &lookup); ++ global->LocalLookup(*name, &lookup, true); + + PropertyAttributes attributes = is_const_property + ? static_cast<PropertyAttributes>(base | READ_ONLY) +@@ -1196,7 +1196,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DeclareGlobals) { + name, + value, + attributes, +- strict_mode)); ++ strict_mode, ++ true)); + } + } + +@@ -1343,7 +1344,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_InitializeVarGlobal) { + JSObject* real_holder = global; + LookupResult lookup; + while (true) { +- real_holder->LocalLookup(*name, &lookup); ++ real_holder->LocalLookup(*name, &lookup, true); + if (lookup.IsProperty()) { + // Determine if this is a redeclaration of something read-only. + if (lookup.IsReadOnly()) { +@@ -1400,7 +1401,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_InitializeVarGlobal) { + + global = isolate->context()->global(); + if (assign) { +- return global->SetProperty(*name, args[2], attributes, strict_mode); ++ return global->SetProperty(*name, args[2], attributes, strict_mode, true); + } + return isolate->heap()->undefined_value(); + } +-- +1.7.2.3 + diff --git a/src/v8/0004-Generalize-external-object-resources.patch b/src/v8/0004-Generalize-external-object-resources.patch new file mode 100644 index 0000000000..f44e7b2134 --- /dev/null +++ b/src/v8/0004-Generalize-external-object-resources.patch @@ -0,0 +1,894 @@ +From 4827116b12c50f6662794017c5a662b5dbb2da0b Mon Sep 17 00:00:00 2001 +From: Aaron Kennedy <aaron.kennedy@nokia.com> +Date: Mon, 23 May 2011 16:55:35 +1000 +Subject: [PATCH 04/13] Generalize external object resources + +V8 was already able to manage and finalize an external string +resource. This change generalizes that mechanism to handle a +single generic external resource - a v8::Object::ExternalResource +derived instance - on normal JSObject's. + +This is useful for mapping C++ objects to JS objects where the +C++ object's memory is effectively owned by the JS Object, and +thus needs to destroyed when the JS Object is garbage collected. +The V8 mailing list suggests using a weak persistent handle for +this purpose, but that seems to incur a fairly massive performance +penalty for short lived objects as weak persistent handle callbacks +are not called until the object has been promoted into the old +object space. +--- + include/v8.h | 25 ++++++ + src/api.cc | 64 ++++++++++++++- + src/extensions/externalize-string-extension.cc | 4 +- + src/factory.cc | 11 +++ + src/heap-inl.h | 101 +++++++++++++++--------- + src/heap.cc | 68 ++++++++-------- + src/heap.h | 42 +++++----- + src/liveobjectlist.cc | 4 +- + src/mark-compact.cc | 21 +++--- + src/objects-inl.h | 41 +++++++++- + src/objects.h | 14 +++- + 11 files changed, 280 insertions(+), 115 deletions(-) + +diff --git a/include/v8.h b/include/v8.h +index 85452aa..7f06ae7 100644 +--- a/include/v8.h ++++ b/include/v8.h +@@ -1630,6 +1630,25 @@ class Object : public Value { + /** Sets a native pointer in an internal field. */ + V8EXPORT void SetPointerInInternalField(int index, void* value); + ++ class V8EXPORT ExternalResource { // NOLINT ++ public: ++ ExternalResource() {} ++ virtual ~ExternalResource() {} ++ ++ protected: ++ virtual void Dispose() { delete this; } ++ ++ private: ++ // Disallow copying and assigning. ++ ExternalResource(const ExternalResource&); ++ void operator=(const ExternalResource&); ++ ++ friend class v8::internal::Heap; ++ }; ++ ++ V8EXPORT void SetExternalResource(ExternalResource *); ++ V8EXPORT ExternalResource *GetExternalResource(); ++ + // Testers for local properties. + V8EXPORT bool HasRealNamedProperty(Handle<String> key); + V8EXPORT bool HasRealIndexedProperty(uint32_t index); +@@ -2331,6 +2350,12 @@ class V8EXPORT ObjectTemplate : public Template { + */ + void SetInternalFieldCount(int value); + ++ /** ++ * Sets whether the object can store an "external resource" object. ++ */ ++ bool HasExternalResource(); ++ void SetHasExternalResource(bool value); ++ + private: + ObjectTemplate(); + static Local<ObjectTemplate> New(Handle<FunctionTemplate> constructor); +diff --git a/src/api.cc b/src/api.cc +index 8b0b32a..1a6fbbb 100644 +--- a/src/api.cc ++++ b/src/api.cc +@@ -1294,6 +1294,34 @@ void ObjectTemplate::SetInternalFieldCount(int value) { + } + + ++bool ObjectTemplate::HasExternalResource() ++{ ++ if (IsDeadCheck(Utils::OpenHandle(this)->GetIsolate(), ++ "v8::ObjectTemplate::HasExternalResource()")) { ++ return 0; ++ } ++ return !Utils::OpenHandle(this)->has_external_resource()->IsUndefined(); ++} ++ ++ ++void ObjectTemplate::SetHasExternalResource(bool value) ++{ ++ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate(); ++ if (IsDeadCheck(isolate, "v8::ObjectTemplate::SetHasExternalResource()")) { ++ return; ++ } ++ ENTER_V8(isolate); ++ if (value) { ++ EnsureConstructor(this); ++ } ++ if (value) { ++ Utils::OpenHandle(this)->set_has_external_resource(i::Smi::FromInt(1)); ++ } else { ++ Utils::OpenHandle(this)->set_has_external_resource(Utils::OpenHandle(this)->GetHeap()->undefined_value()); ++ } ++} ++ ++ + // --- S c r i p t D a t a --- + + +@@ -3652,6 +3680,34 @@ void v8::Object::SetPointerInInternalField(int index, void* value) { + } + + ++void v8::Object::SetExternalResource(v8::Object::ExternalResource *resource) { ++ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate(); ++ ENTER_V8(isolate); ++ i::Handle<i::JSObject> obj = Utils::OpenHandle(this); ++ if (CanBeEncodedAsSmi(resource)) { ++ obj->SetExternalResourceObject(EncodeAsSmi(resource)); ++ } else { ++ obj->SetExternalResourceObject(*isolate->factory()->NewProxy(static_cast<i::Address>((void *)resource))); ++ } ++ if (!obj->IsSymbol()) { ++ isolate->heap()->external_resource_table()->AddObject(*obj); ++ } ++} ++ ++ ++v8::Object::ExternalResource *v8::Object::GetExternalResource() { ++ i::Handle<i::JSObject> obj = Utils::OpenHandle(this); ++ i::Object* value = obj->GetExternalResourceObject(); ++ if (value->IsSmi()) { ++ return reinterpret_cast<v8::Object::ExternalResource*>(i::Internals::GetExternalPointerFromSmi(value)); ++ } else if (value->IsProxy()) { ++ return reinterpret_cast<v8::Object::ExternalResource*>(i::Proxy::cast(value)->proxy()); ++ } else { ++ return NULL; ++ } ++} ++ ++ + // --- E n v i r o n m e n t --- + + +@@ -4144,7 +4200,7 @@ Local<String> v8::String::NewExternal( + LOG_API(isolate, "String::NewExternal"); + ENTER_V8(isolate); + i::Handle<i::String> result = NewExternalStringHandle(isolate, resource); +- isolate->heap()->external_string_table()->AddString(*result); ++ isolate->heap()->external_resource_table()->AddString(*result); + return Utils::ToLocal(result); + } + +@@ -4162,7 +4218,7 @@ bool v8::String::MakeExternal(v8::String::ExternalStringResource* resource) { + } + bool result = obj->MakeExternal(resource); + if (result && !obj->IsSymbol()) { +- isolate->heap()->external_string_table()->AddString(*obj); ++ isolate->heap()->external_resource_table()->AddString(*obj); + } + return result; + } +@@ -4175,7 +4231,7 @@ Local<String> v8::String::NewExternal( + LOG_API(isolate, "String::NewExternal"); + ENTER_V8(isolate); + i::Handle<i::String> result = NewExternalAsciiStringHandle(isolate, resource); +- isolate->heap()->external_string_table()->AddString(*result); ++ isolate->heap()->external_resource_table()->AddString(*result); + return Utils::ToLocal(result); + } + +@@ -4194,7 +4250,7 @@ bool v8::String::MakeExternal( + } + bool result = obj->MakeExternal(resource); + if (result && !obj->IsSymbol()) { +- isolate->heap()->external_string_table()->AddString(*obj); ++ isolate->heap()->external_resource_table()->AddString(*obj); + } + return result; + } +diff --git a/src/extensions/externalize-string-extension.cc b/src/extensions/externalize-string-extension.cc +index b3f83fe..8e50904 100644 +--- a/src/extensions/externalize-string-extension.cc ++++ b/src/extensions/externalize-string-extension.cc +@@ -100,7 +100,7 @@ v8::Handle<v8::Value> ExternalizeStringExtension::Externalize( + data, string->length()); + result = string->MakeExternal(resource); + if (result && !string->IsSymbol()) { +- HEAP->external_string_table()->AddString(*string); ++ HEAP->external_resource_table()->AddString(*string); + } + if (!result) delete resource; + } else { +@@ -110,7 +110,7 @@ v8::Handle<v8::Value> ExternalizeStringExtension::Externalize( + data, string->length()); + result = string->MakeExternal(resource); + if (result && !string->IsSymbol()) { +- HEAP->external_string_table()->AddString(*string); ++ HEAP->external_resource_table()->AddString(*string); + } + if (!result) delete resource; + } +diff --git a/src/factory.cc b/src/factory.cc +index dcdc645..d530a75 100644 +--- a/src/factory.cc ++++ b/src/factory.cc +@@ -997,15 +997,21 @@ Handle<JSFunction> Factory::CreateApiFunction( + Handle<Code> construct_stub = isolate()->builtins()->JSConstructStubApi(); + + int internal_field_count = 0; ++ bool has_external_resource = false; ++ + if (!obj->instance_template()->IsUndefined()) { + Handle<ObjectTemplateInfo> instance_template = + Handle<ObjectTemplateInfo>( + ObjectTemplateInfo::cast(obj->instance_template())); + internal_field_count = + Smi::cast(instance_template->internal_field_count())->value(); ++ has_external_resource = ++ !instance_template->has_external_resource()->IsUndefined(); + } + + int instance_size = kPointerSize * internal_field_count; ++ if (has_external_resource) instance_size += kPointerSize; ++ + InstanceType type = INVALID_TYPE; + switch (instance_type) { + case JavaScriptObject: +@@ -1040,6 +1046,11 @@ Handle<JSFunction> Factory::CreateApiFunction( + + Handle<Map> map = Handle<Map>(result->initial_map()); + ++ // Mark as having external data object if needed ++ if (has_external_resource) { ++ map->set_has_external_resource(true); ++ } ++ + // Mark as undetectable if needed. + if (obj->undetectable()) { + map->set_is_undetectable(); +diff --git a/src/heap-inl.h b/src/heap-inl.h +index f4fce7b..58e7adf 100644 +--- a/src/heap-inl.h ++++ b/src/heap-inl.h +@@ -205,21 +205,36 @@ MaybeObject* Heap::NumberFromUint32(uint32_t value) { + } + + +-void Heap::FinalizeExternalString(String* string) { +- ASSERT(string->IsExternalString()); +- v8::String::ExternalStringResourceBase** resource_addr = +- reinterpret_cast<v8::String::ExternalStringResourceBase**>( +- reinterpret_cast<byte*>(string) + +- ExternalString::kResourceOffset - +- kHeapObjectTag); +- +- // Dispose of the C++ object if it has not already been disposed. +- if (*resource_addr != NULL) { +- (*resource_addr)->Dispose(); +- } ++void Heap::FinalizeExternalString(HeapObject* string) { ++ ASSERT(string->IsExternalString() || string->map()->has_external_resource()); ++ ++ if (string->IsExternalString()) { ++ v8::String::ExternalStringResourceBase** resource_addr = ++ reinterpret_cast<v8::String::ExternalStringResourceBase**>( ++ reinterpret_cast<byte*>(string) + ++ ExternalString::kResourceOffset - ++ kHeapObjectTag); ++ ++ // Dispose of the C++ object if it has not already been disposed. ++ if (*resource_addr != NULL) { ++ (*resource_addr)->Dispose(); ++ } + +- // Clear the resource pointer in the string. +- *resource_addr = NULL; ++ // Clear the resource pointer in the string. ++ *resource_addr = NULL; ++ } else { ++ JSObject *object = JSObject::cast(string); ++ Object *value = object->GetExternalResourceObject(); ++ v8::Object::ExternalResource *resource = 0; ++ if (value->IsSmi()) { ++ resource = reinterpret_cast<v8::Object::ExternalResource*>(Internals::GetExternalPointerFromSmi(value)); ++ } else if (value->IsProxy()) { ++ resource = reinterpret_cast<v8::Object::ExternalResource*>(Proxy::cast(value)->proxy()); ++ } ++ if (resource) { ++ resource->Dispose(); ++ } ++ } + } + + +@@ -556,53 +571,63 @@ inline bool Heap::allow_allocation(bool new_state) { + #endif + + +-void ExternalStringTable::AddString(String* string) { +- ASSERT(string->IsExternalString()); ++void ExternalResourceTable::AddString(String* string) { ++ ASSERT(string->IsExternalString() ); + if (heap_->InNewSpace(string)) { +- new_space_strings_.Add(string); ++ new_space_objects_.Add(string); ++ } else { ++ old_space_objects_.Add(string); ++ } ++} ++ ++ ++void ExternalResourceTable::AddObject(HeapObject* object) { ++ ASSERT(object->map()->has_external_resource()); ++ if (heap_->InNewSpace(object)) { ++ new_space_objects_.Add(object); + } else { +- old_space_strings_.Add(string); ++ old_space_objects_.Add(object); + } + } + + +-void ExternalStringTable::Iterate(ObjectVisitor* v) { +- if (!new_space_strings_.is_empty()) { +- Object** start = &new_space_strings_[0]; +- v->VisitPointers(start, start + new_space_strings_.length()); ++void ExternalResourceTable::Iterate(ObjectVisitor* v) { ++ if (!new_space_objects_.is_empty()) { ++ Object** start = &new_space_objects_[0]; ++ v->VisitPointers(start, start + new_space_objects_.length()); + } +- if (!old_space_strings_.is_empty()) { +- Object** start = &old_space_strings_[0]; +- v->VisitPointers(start, start + old_space_strings_.length()); ++ if (!old_space_objects_.is_empty()) { ++ Object** start = &old_space_objects_[0]; ++ v->VisitPointers(start, start + old_space_objects_.length()); + } + } + + + // Verify() is inline to avoid ifdef-s around its calls in release + // mode. +-void ExternalStringTable::Verify() { ++void ExternalResourceTable::Verify() { + #ifdef DEBUG +- for (int i = 0; i < new_space_strings_.length(); ++i) { +- ASSERT(heap_->InNewSpace(new_space_strings_[i])); +- ASSERT(new_space_strings_[i] != HEAP->raw_unchecked_null_value()); ++ for (int i = 0; i < new_space_objects_.length(); ++i) { ++ ASSERT(heap_->InNewSpace(new_space_objects_[i])); ++ ASSERT(new_space_objects_[i] != HEAP->raw_unchecked_null_value()); + } +- for (int i = 0; i < old_space_strings_.length(); ++i) { +- ASSERT(!heap_->InNewSpace(old_space_strings_[i])); +- ASSERT(old_space_strings_[i] != HEAP->raw_unchecked_null_value()); ++ for (int i = 0; i < old_space_objects_.length(); ++i) { ++ ASSERT(!heap_->InNewSpace(old_space_objects_[i])); ++ ASSERT(old_space_objects_[i] != HEAP->raw_unchecked_null_value()); + } + #endif + } + + +-void ExternalStringTable::AddOldString(String* string) { +- ASSERT(string->IsExternalString()); +- ASSERT(!heap_->InNewSpace(string)); +- old_space_strings_.Add(string); ++void ExternalResourceTable::AddOldObject(HeapObject* object) { ++ ASSERT(object->IsExternalString() || object->map()->has_external_resource()); ++ ASSERT(!heap_->InNewSpace(object)); ++ old_space_objects_.Add(object); + } + + +-void ExternalStringTable::ShrinkNewStrings(int position) { +- new_space_strings_.Rewind(position); ++void ExternalResourceTable::ShrinkNewObjects(int position) { ++ new_space_objects_.Rewind(position); + Verify(); + } + +diff --git a/src/heap.cc b/src/heap.cc +index 900f462..bf2940e 100644 +--- a/src/heap.cc ++++ b/src/heap.cc +@@ -155,7 +155,7 @@ Heap::Heap() + memset(roots_, 0, sizeof(roots_[0]) * kRootListLength); + global_contexts_list_ = NULL; + mark_compact_collector_.heap_ = this; +- external_string_table_.heap_ = this; ++ external_resource_table_.heap_ = this; + } + + +@@ -1030,8 +1030,8 @@ void Heap::Scavenge() { + + new_space_front = DoScavenge(&scavenge_visitor, new_space_front); + +- UpdateNewSpaceReferencesInExternalStringTable( +- &UpdateNewSpaceReferenceInExternalStringTableEntry); ++ UpdateNewSpaceReferencesInExternalResourceTable( ++ &UpdateNewSpaceReferenceInExternalResourceTableEntry); + + LiveObjectList::UpdateReferencesForScavengeGC(); + isolate()->runtime_profiler()->UpdateSamplesAfterScavenge(); +@@ -1053,38 +1053,38 @@ void Heap::Scavenge() { + } + + +-String* Heap::UpdateNewSpaceReferenceInExternalStringTableEntry(Heap* heap, +- Object** p) { ++HeapObject* Heap::UpdateNewSpaceReferenceInExternalResourceTableEntry(Heap* heap, ++ Object** p) { + MapWord first_word = HeapObject::cast(*p)->map_word(); + + if (!first_word.IsForwardingAddress()) { + // Unreachable external string can be finalized. +- heap->FinalizeExternalString(String::cast(*p)); ++ heap->FinalizeExternalString(HeapObject::cast(*p)); + return NULL; + } + + // String is still reachable. +- return String::cast(first_word.ToForwardingAddress()); ++ return HeapObject::cast(first_word.ToForwardingAddress()); + } + + +-void Heap::UpdateNewSpaceReferencesInExternalStringTable( +- ExternalStringTableUpdaterCallback updater_func) { +- external_string_table_.Verify(); ++void Heap::UpdateNewSpaceReferencesInExternalResourceTable( ++ ExternalResourceTableUpdaterCallback updater_func) { ++ external_resource_table_.Verify(); + +- if (external_string_table_.new_space_strings_.is_empty()) return; ++ if (external_resource_table_.new_space_objects_.is_empty()) return; + +- Object** start = &external_string_table_.new_space_strings_[0]; +- Object** end = start + external_string_table_.new_space_strings_.length(); ++ Object** start = &external_resource_table_.new_space_objects_[0]; ++ Object** end = start + external_resource_table_.new_space_objects_.length(); + Object** last = start; + + for (Object** p = start; p < end; ++p) { + ASSERT(InFromSpace(*p)); +- String* target = updater_func(this, p); ++ HeapObject* target = updater_func(this, p); + + if (target == NULL) continue; + +- ASSERT(target->IsExternalString()); ++ ASSERT(target->IsExternalString() || target->map()->has_external_resource()); + + if (InNewSpace(target)) { + // String is still in new space. Update the table entry. +@@ -1092,12 +1092,12 @@ void Heap::UpdateNewSpaceReferencesInExternalStringTable( + ++last; + } else { + // String got promoted. Move it to the old string list. +- external_string_table_.AddOldString(target); ++ external_resource_table_.AddOldObject(target); + } + } + + ASSERT(last <= end); +- external_string_table_.ShrinkNewStrings(static_cast<int>(last - start)); ++ external_resource_table_.ShrinkNewObjects(static_cast<int>(last - start)); + } + + +@@ -4468,7 +4468,7 @@ void Heap::IterateWeakRoots(ObjectVisitor* v, VisitMode mode) { + v->Synchronize("symbol_table"); + if (mode != VISIT_ALL_IN_SCAVENGE) { + // Scavenge collections have special processing for this. +- external_string_table_.Iterate(v); ++ external_resource_table_.Iterate(v); + } + v->Synchronize("external_string_table"); + } +@@ -4970,7 +4970,7 @@ void Heap::TearDown() { + + isolate_->global_handles()->TearDown(); + +- external_string_table_.TearDown(); ++ external_resource_table_.TearDown(); + + new_space_.TearDown(); + +@@ -5835,31 +5835,31 @@ void TranscendentalCache::Clear() { + } + + +-void ExternalStringTable::CleanUp() { ++void ExternalResourceTable::CleanUp() { + int last = 0; +- for (int i = 0; i < new_space_strings_.length(); ++i) { +- if (new_space_strings_[i] == heap_->raw_unchecked_null_value()) continue; +- if (heap_->InNewSpace(new_space_strings_[i])) { +- new_space_strings_[last++] = new_space_strings_[i]; ++ for (int i = 0; i < new_space_objects_.length(); ++i) { ++ if (new_space_objects_[i] == heap_->raw_unchecked_null_value()) continue; ++ if (heap_->InNewSpace(new_space_objects_[i])) { ++ new_space_objects_[last++] = new_space_objects_[i]; + } else { +- old_space_strings_.Add(new_space_strings_[i]); ++ old_space_objects_.Add(new_space_objects_[i]); + } + } +- new_space_strings_.Rewind(last); ++ new_space_objects_.Rewind(last); + last = 0; +- for (int i = 0; i < old_space_strings_.length(); ++i) { +- if (old_space_strings_[i] == heap_->raw_unchecked_null_value()) continue; +- ASSERT(!heap_->InNewSpace(old_space_strings_[i])); +- old_space_strings_[last++] = old_space_strings_[i]; ++ for (int i = 0; i < old_space_objects_.length(); ++i) { ++ if (old_space_objects_[i] == heap_->raw_unchecked_null_value()) continue; ++ ASSERT(!heap_->InNewSpace(old_space_objects_[i])); ++ old_space_objects_[last++] = old_space_objects_[i]; + } +- old_space_strings_.Rewind(last); ++ old_space_objects_.Rewind(last); + Verify(); + } + + +-void ExternalStringTable::TearDown() { +- new_space_strings_.Free(); +- old_space_strings_.Free(); ++void ExternalResourceTable::TearDown() { ++ new_space_objects_.Free(); ++ old_space_objects_.Free(); + } + + +diff --git a/src/heap.h b/src/heap.h +index ae4e9e7..8cbf378 100644 +--- a/src/heap.h ++++ b/src/heap.h +@@ -237,8 +237,8 @@ class Isolate; + class WeakObjectRetainer; + + +-typedef String* (*ExternalStringTableUpdaterCallback)(Heap* heap, +- Object** pointer); ++typedef HeapObject* (*ExternalResourceTableUpdaterCallback)(Heap* heap, ++ Object** pointer); + + typedef bool (*DirtyRegionCallback)(Heap* heap, + Address start, +@@ -284,43 +284,45 @@ class PromotionQueue { + }; + + +-// External strings table is a place where all external strings are +-// registered. We need to keep track of such strings to properly +-// finalize them. +-class ExternalStringTable { ++// External resource table is a place where all external strings and ++// objects with an external resource are registered. We need to keep ++// track of such strings to properly finalize them. ++class ExternalResourceTable { + public: + // Registers an external string. + inline void AddString(String* string); ++ // Registers an external object. ++ inline void AddObject(HeapObject* object); + + inline void Iterate(ObjectVisitor* v); + + // Restores internal invariant and gets rid of collected strings. +- // Must be called after each Iterate() that modified the strings. ++ // Must be called after each Iterate() that modified the objects. + void CleanUp(); + + // Destroys all allocated memory. + void TearDown(); + + private: +- ExternalStringTable() { } ++ ExternalResourceTable() { } + + friend class Heap; + + inline void Verify(); + +- inline void AddOldString(String* string); ++ inline void AddOldObject(HeapObject* object); + + // Notifies the table that only a prefix of the new list is valid. +- inline void ShrinkNewStrings(int position); ++ inline void ShrinkNewObjects(int position); + + // To speed up scavenge collections new space string are kept + // separate from old space strings. +- List<Object*> new_space_strings_; +- List<Object*> old_space_strings_; ++ List<Object*> new_space_objects_; ++ List<Object*> old_space_objects_; + + Heap* heap_; + +- DISALLOW_COPY_AND_ASSIGN(ExternalStringTable); ++ DISALLOW_COPY_AND_ASSIGN(ExternalResourceTable); + }; + + +@@ -753,7 +755,7 @@ class Heap { + + // Finalizes an external string by deleting the associated external + // data and clearing the resource pointer. +- inline void FinalizeExternalString(String* string); ++ inline void FinalizeExternalString(HeapObject* string); + + // Allocates an uninitialized object. The memory is non-executable if the + // hardware and OS allow. +@@ -1191,8 +1193,8 @@ class Heap { + survived_since_last_expansion_ += survived; + } + +- void UpdateNewSpaceReferencesInExternalStringTable( +- ExternalStringTableUpdaterCallback updater_func); ++ void UpdateNewSpaceReferencesInExternalResourceTable( ++ ExternalResourceTableUpdaterCallback updater_func); + + void ProcessWeakReferences(WeakObjectRetainer* retainer); + +@@ -1228,8 +1230,8 @@ class Heap { + return &mark_compact_collector_; + } + +- ExternalStringTable* external_string_table() { +- return &external_string_table_; ++ ExternalResourceTable* external_resource_table() { ++ return &external_resource_table_; + } + + inline Isolate* isolate(); +@@ -1462,7 +1464,7 @@ class Heap { + // Performs a minor collection in new generation. + void Scavenge(); + +- static String* UpdateNewSpaceReferenceInExternalStringTableEntry( ++ static HeapObject* UpdateNewSpaceReferenceInExternalResourceTableEntry( + Heap* heap, + Object** pointer); + +@@ -1593,7 +1595,7 @@ class Heap { + // configured through the API until it is setup. + bool configured_; + +- ExternalStringTable external_string_table_; ++ ExternalResourceTable external_resource_table_; + + bool is_safe_to_read_maps_; + +diff --git a/src/liveobjectlist.cc b/src/liveobjectlist.cc +index 5795a6b..8866e58 100644 +--- a/src/liveobjectlist.cc ++++ b/src/liveobjectlist.cc +@@ -1989,7 +1989,7 @@ Object* LiveObjectList::PrintObj(int obj_id) { + ASSERT(resource->IsAscii()); + Handle<String> dump_string = + Factory::NewExternalStringFromAscii(resource); +- ExternalStringTable::AddString(*dump_string); ++ ExternalResourceTable::AddString(*dump_string); + return *dump_string; + } else { + delete resource; +@@ -2193,7 +2193,7 @@ Object* LiveObjectList::GetPathPrivate(HeapObject* obj1, HeapObject* obj2) { + ASSERT(resource->IsAscii()); + Handle<String> path_string = + Factory::NewExternalStringFromAscii(resource); +- ExternalStringTable::AddString(*path_string); ++ ExternalResourceTable::AddString(*path_string); + return *path_string; + } else { + delete resource; +diff --git a/src/mark-compact.cc b/src/mark-compact.cc +index 68a5062..1b1e361 100644 +--- a/src/mark-compact.cc ++++ b/src/mark-compact.cc +@@ -163,7 +163,7 @@ void MarkCompactCollector::Finish() { + // objects (empty string, illegal builtin). + heap()->isolate()->stub_cache()->Clear(); + +- heap()->external_string_table_.CleanUp(); ++ heap()->external_resource_table_.CleanUp(); + + // If we've just compacted old space there's no reason to check the + // fragmentation limit. Just return. +@@ -1019,8 +1019,9 @@ class SymbolTableCleaner : public ObjectVisitor { + + // Since no objects have yet been moved we can safely access the map of + // the object. +- if ((*p)->IsExternalString()) { +- heap_->FinalizeExternalString(String::cast(*p)); ++ if ((*p)->IsExternalString() || ++ (*p)->IsHeapObject() && HeapObject::cast(*p)->map()->has_external_resource()) { ++ heap_->FinalizeExternalString(HeapObject::cast(*p)); + } + // Set the entry to null_value (as deleted). + *p = heap_->raw_unchecked_null_value(); +@@ -1433,8 +1434,8 @@ void MarkCompactCollector::MarkLiveObjects() { + SymbolTableCleaner v(heap()); + symbol_table->IterateElements(&v); + symbol_table->ElementsRemoved(v.PointersRemoved()); +- heap()->external_string_table_.Iterate(&v); +- heap()->external_string_table_.CleanUp(); ++ heap()->external_resource_table_.Iterate(&v); ++ heap()->external_resource_table_.CleanUp(); + + // Process the weak references. + MarkCompactWeakObjectRetainer mark_compact_object_retainer; +@@ -1948,11 +1949,11 @@ static void UpdatePointerToNewGen(HeapObject** p) { + } + + +-static String* UpdateNewSpaceReferenceInExternalStringTableEntry(Heap* heap, +- Object** p) { ++static HeapObject* UpdateNewSpaceReferenceInExternalResourceTableEntry(Heap* heap, ++ Object** p) { + Address old_addr = HeapObject::cast(*p)->address(); + Address new_addr = Memory::Address_at(old_addr); +- return String::cast(HeapObject::FromAddress(new_addr)); ++ return HeapObject::FromAddress(new_addr); + } + + +@@ -2083,8 +2084,8 @@ static void SweepNewSpace(Heap* heap, NewSpace* space) { + updating_visitor.VisitPointer(heap->global_contexts_list_address()); + + // Update pointers from external string table. +- heap->UpdateNewSpaceReferencesInExternalStringTable( +- &UpdateNewSpaceReferenceInExternalStringTableEntry); ++ heap->UpdateNewSpaceReferencesInExternalResourceTable( ++ &UpdateNewSpaceReferenceInExternalResourceTableEntry); + + // All pointers were updated. Update auxiliary allocation info. + heap->IncrementYoungSurvivorsCounter(survivors_size); +diff --git a/src/objects-inl.h b/src/objects-inl.h +index 6aaca2f..231b835 100644 +--- a/src/objects-inl.h ++++ b/src/objects-inl.h +@@ -1392,13 +1392,13 @@ int JSObject::GetInternalFieldCount() { + // Make sure to adjust for the number of in-object properties. These + // properties do contribute to the size, but are not internal fields. + return ((Size() - GetHeaderSize()) >> kPointerSizeLog2) - +- map()->inobject_properties(); ++ map()->inobject_properties() - map()->has_external_resource()?1:0; + } + + + int JSObject::GetInternalFieldOffset(int index) { + ASSERT(index < GetInternalFieldCount() && index >= 0); +- return GetHeaderSize() + (kPointerSize * index); ++ return GetHeaderSize() + (kPointerSize * (index + map()->has_external_resource()?1:0)); + } + + +@@ -1407,7 +1407,7 @@ Object* JSObject::GetInternalField(int index) { + // Internal objects do follow immediately after the header, whereas in-object + // properties are at the end of the object. Therefore there is no need + // to adjust the index here. +- return READ_FIELD(this, GetHeaderSize() + (kPointerSize * index)); ++ return READ_FIELD(this, GetHeaderSize() + (kPointerSize * (index + map()->has_external_resource()?1:0))); + } + + +@@ -1416,12 +1416,29 @@ void JSObject::SetInternalField(int index, Object* value) { + // Internal objects do follow immediately after the header, whereas in-object + // properties are at the end of the object. Therefore there is no need + // to adjust the index here. +- int offset = GetHeaderSize() + (kPointerSize * index); ++ int offset = GetHeaderSize() + (kPointerSize * (index + map()->has_external_resource()?1:0)); + WRITE_FIELD(this, offset, value); + WRITE_BARRIER(this, offset); + } + + ++void JSObject::SetExternalResourceObject(Object *value) { ++ ASSERT(map()->has_external_resource()); ++ int offset = GetHeaderSize(); ++ WRITE_FIELD(this, offset, value); ++ WRITE_BARRIER(this, offset); ++} ++ ++ ++Object *JSObject::GetExternalResourceObject() { ++ if (map()->has_external_resource()) { ++ return READ_FIELD(this, GetHeaderSize()); ++ } else { ++ return GetHeap()->undefined_value(); ++ } ++} ++ ++ + // Access fast-case object properties at index. The use of these routines + // is needed to correctly distinguish between properties stored in-object and + // properties stored in the properties array. +@@ -2521,6 +2538,20 @@ bool Map::is_shared() { + } + + ++void Map::set_has_external_resource(bool value) { ++ if (value) { ++ set_bit_field3(bit_field3() | (1 << kHasExternalResource)); ++ } else { ++ set_bit_field3(bit_field3() & ~(1 << kHasExternalResource)); ++ } ++} ++ ++bool Map::has_external_resource() ++{ ++ return ((1 << kHasExternalResource) & bit_field3()) != 0; ++} ++ ++ + void Map::set_named_interceptor_is_fallback(bool value) + { + if (value) { +@@ -3017,6 +3048,8 @@ ACCESSORS(FunctionTemplateInfo, flag, Smi, kFlagOffset) + ACCESSORS(ObjectTemplateInfo, constructor, Object, kConstructorOffset) + ACCESSORS(ObjectTemplateInfo, internal_field_count, Object, + kInternalFieldCountOffset) ++ACCESSORS(ObjectTemplateInfo, has_external_resource, Object, ++ kHasExternalResourceOffset) + + ACCESSORS(SignatureInfo, receiver, Object, kReceiverOffset) + ACCESSORS(SignatureInfo, args, Object, kArgsOffset) +diff --git a/src/objects.h b/src/objects.h +index a209cd0..1bdb5c7 100644 +--- a/src/objects.h ++++ b/src/objects.h +@@ -1636,6 +1636,9 @@ class JSObject: public HeapObject { + inline Object* GetInternalField(int index); + inline void SetInternalField(int index, Object* value); + ++ inline void SetExternalResourceObject(Object *); ++ inline Object *GetExternalResourceObject(); ++ + // Lookup a property. If found, the result is valid and has + // detailed information. + void LocalLookup(String* name, LookupResult* result, bool skip_fallback_interceptor = false); +@@ -3715,6 +3718,12 @@ class Map: public HeapObject { + inline void set_is_access_check_needed(bool access_check_needed); + inline bool is_access_check_needed(); + ++ ++ // Tells whether the instance has the space for an external resource ++ // object ++ inline void set_has_external_resource(bool value); ++ inline bool has_external_resource(); ++ + + // Whether the named interceptor is a fallback interceptor or not + inline void set_named_interceptor_is_fallback(bool value); +@@ -3912,6 +3921,7 @@ class Map: public HeapObject { + + // Bit positions for bit field 3 + static const int kNamedInterceptorIsFallback = 0; ++ static const int kHasExternalResource = 1; + + // Layout of the default cache. It holds alternating name and code objects. + static const int kCodeCacheEntrySize = 2; +@@ -6426,6 +6436,7 @@ class ObjectTemplateInfo: public TemplateInfo { + public: + DECL_ACCESSORS(constructor, Object) + DECL_ACCESSORS(internal_field_count, Object) ++ DECL_ACCESSORS(has_external_resource, Object) + + static inline ObjectTemplateInfo* cast(Object* obj); + +@@ -6442,7 +6453,8 @@ class ObjectTemplateInfo: public TemplateInfo { + static const int kConstructorOffset = TemplateInfo::kHeaderSize; + static const int kInternalFieldCountOffset = + kConstructorOffset + kPointerSize; +- static const int kSize = kInternalFieldCountOffset + kPointerSize; ++ static const int kHasExternalResourceOffset = kInternalFieldCountOffset + kPointerSize; ++ static const int kSize = kHasExternalResourceOffset + kPointerSize; + }; + + +-- +1.7.2.3 + diff --git a/src/v8/0005-Introduce-a-QML-compilation-mode.patch b/src/v8/0005-Introduce-a-QML-compilation-mode.patch new file mode 100644 index 0000000000..b464e61266 --- /dev/null +++ b/src/v8/0005-Introduce-a-QML-compilation-mode.patch @@ -0,0 +1,1777 @@ +From fd7d475e298e5b63cd6383c78cc900635c82aa38 Mon Sep 17 00:00:00 2001 +From: Aaron Kennedy <aaron.kennedy@nokia.com> +Date: Mon, 23 May 2011 18:26:19 +1000 +Subject: [PATCH 05/13] Introduce a QML compilation mode + +In QML mode, there is a second global object - known as the QML +global object. During property resolution, if a property is not +present on the JS global object, it is resolve on the QML global +object. + +This global object behavior is only enabled if a script is being +compiled in QML mode. The object to use as the QML global object +is passed as a parameter to the Script::Run() method. Any function +closures etc. created during the run will retain a reference to this +object, so different objects can be passed in different script +runs. +--- + include/v8.h | 18 ++++++++-- + src/api.cc | 52 ++++++++++++++++++++++++----- + src/arm/code-stubs-arm.cc | 4 ++ + src/arm/full-codegen-arm.cc | 26 ++++++++------ + src/arm/lithium-arm.cc | 2 +- + src/arm/lithium-arm.h | 6 +++- + src/arm/lithium-codegen-arm.cc | 7 ++-- + src/arm/macro-assembler-arm.h | 5 +++ + src/ast-inl.h | 5 +++ + src/ast.h | 1 + + src/code-stubs.h | 2 +- + src/compiler.cc | 15 +++++++- + src/compiler.h | 22 ++++++++++-- + src/contexts.cc | 23 +++++++++++++ + src/contexts.h | 4 ++ + src/execution.cc | 28 +++++++++++++-- + src/execution.h | 6 +++ + src/full-codegen.cc | 3 +- + src/full-codegen.h | 1 + + src/heap.cc | 2 + + src/hydrogen-instructions.h | 10 ++++- + src/hydrogen.cc | 2 + + src/ia32/code-stubs-ia32.cc | 7 ++++ + src/ia32/full-codegen-ia32.cc | 26 ++++++++------ + src/ia32/lithium-codegen-ia32.cc | 7 ++-- + src/ia32/lithium-ia32.cc | 2 +- + src/ia32/lithium-ia32.h | 6 +++- + src/ia32/macro-assembler-ia32.h | 5 +++ + src/objects-inl.h | 12 +++++++ + src/objects.h | 5 +++ + src/parser.cc | 27 +++++++++++++-- + src/parser.h | 4 ++- + src/prettyprinter.cc | 3 ++ + src/runtime.cc | 68 ++++++++++++++++++++++++------------- + src/runtime.h | 8 ++-- + src/scopes.cc | 10 +++++ + src/scopes.h | 7 ++++ + src/variables.cc | 3 +- + src/variables.h | 5 +++ + src/x64/code-stubs-x64.cc | 4 ++ + src/x64/full-codegen-x64.cc | 26 ++++++++------ + src/x64/lithium-codegen-x64.cc | 7 ++-- + src/x64/lithium-x64.cc | 2 +- + src/x64/lithium-x64.h | 6 +++ + src/x64/macro-assembler-x64.h | 5 +++ + 45 files changed, 391 insertions(+), 108 deletions(-) + +diff --git a/include/v8.h b/include/v8.h +index 7f06ae7..a858eae 100644 +--- a/include/v8.h ++++ b/include/v8.h +@@ -577,6 +577,10 @@ class ScriptOrigin { + */ + class V8EXPORT Script { + public: ++ enum CompileFlags { ++ Default = 0x00, ++ QmlMode = 0x01 ++ }; + + /** + * Compiles the specified script (context-independent). +@@ -596,7 +600,8 @@ class V8EXPORT Script { + static Local<Script> New(Handle<String> source, + ScriptOrigin* origin = NULL, + ScriptData* pre_data = NULL, +- Handle<String> script_data = Handle<String>()); ++ Handle<String> script_data = Handle<String>(), ++ CompileFlags = Default); + + /** + * Compiles the specified script using the specified file name +@@ -609,7 +614,8 @@ class V8EXPORT Script { + * will use the currently entered context). + */ + static Local<Script> New(Handle<String> source, +- Handle<Value> file_name); ++ Handle<Value> file_name, ++ CompileFlags = Default); + + /** + * Compiles the specified script (bound to current context). +@@ -630,7 +636,8 @@ class V8EXPORT Script { + static Local<Script> Compile(Handle<String> source, + ScriptOrigin* origin = NULL, + ScriptData* pre_data = NULL, +- Handle<String> script_data = Handle<String>()); ++ Handle<String> script_data = Handle<String>(), ++ CompileFlags = Default); + + /** + * Compiles the specified script using the specified file name +@@ -647,7 +654,8 @@ class V8EXPORT Script { + */ + static Local<Script> Compile(Handle<String> source, + Handle<Value> file_name, +- Handle<String> script_data = Handle<String>()); ++ Handle<String> script_data = Handle<String>(), ++ CompileFlags = Default); + + /** + * Runs the script returning the resulting value. If the script is +@@ -657,6 +665,7 @@ class V8EXPORT Script { + * compiled. + */ + Local<Value> Run(); ++ Local<Value> Run(Handle<Object> qml); + + /** + * Returns the script id value. +@@ -3326,6 +3335,7 @@ class V8EXPORT Context { + * JavaScript frames an empty handle is returned. + */ + static Local<Context> GetCalling(); ++ static Local<Object> GetCallingQmlGlobal(); + + /** + * Sets the security token for the context. To access an object in +diff --git a/src/api.cc b/src/api.cc +index 1a6fbbb..39767f4 100644 +--- a/src/api.cc ++++ b/src/api.cc +@@ -1372,7 +1372,8 @@ ScriptData* ScriptData::New(const char* data, int length) { + Local<Script> Script::New(v8::Handle<String> source, + v8::ScriptOrigin* origin, + v8::ScriptData* pre_data, +- v8::Handle<String> script_data) { ++ v8::Handle<String> script_data, ++ v8::Script::CompileFlags compile_flags) { + i::Isolate* isolate = i::Isolate::Current(); + ON_BAILOUT(isolate, "v8::Script::New()", return Local<Script>()); + LOG_API(isolate, "Script::New"); +@@ -1409,7 +1410,8 @@ Local<Script> Script::New(v8::Handle<String> source, + NULL, + pre_data_impl, + Utils::OpenHandle(*script_data), +- i::NOT_NATIVES_CODE); ++ i::NOT_NATIVES_CODE, ++ compile_flags); + has_pending_exception = result.is_null(); + EXCEPTION_BAILOUT_CHECK(isolate, Local<Script>()); + return Local<Script>(ToApi<Script>(result)); +@@ -1417,21 +1419,23 @@ Local<Script> Script::New(v8::Handle<String> source, + + + Local<Script> Script::New(v8::Handle<String> source, +- v8::Handle<Value> file_name) { ++ v8::Handle<Value> file_name, ++ v8::Script::CompileFlags compile_flags) { + ScriptOrigin origin(file_name); +- return New(source, &origin); ++ return New(source, &origin, 0, Handle<String>(), compile_flags); + } + + + Local<Script> Script::Compile(v8::Handle<String> source, + v8::ScriptOrigin* origin, + v8::ScriptData* pre_data, +- v8::Handle<String> script_data) { ++ v8::Handle<String> script_data, ++ v8::Script::CompileFlags compile_flags) { + i::Isolate* isolate = i::Isolate::Current(); + ON_BAILOUT(isolate, "v8::Script::Compile()", return Local<Script>()); + LOG_API(isolate, "Script::Compile"); + ENTER_V8(isolate); +- Local<Script> generic = New(source, origin, pre_data, script_data); ++ Local<Script> generic = New(source, origin, pre_data, script_data, compile_flags); + if (generic.IsEmpty()) + return generic; + i::Handle<i::Object> obj = Utils::OpenHandle(*generic); +@@ -1447,13 +1451,18 @@ Local<Script> Script::Compile(v8::Handle<String> source, + + Local<Script> Script::Compile(v8::Handle<String> source, + v8::Handle<Value> file_name, +- v8::Handle<String> script_data) { ++ v8::Handle<String> script_data, ++ v8::Script::CompileFlags compile_flags) { + ScriptOrigin origin(file_name); +- return Compile(source, &origin, 0, script_data); ++ return Compile(source, &origin, 0, script_data, compile_flags); + } + + + Local<Value> Script::Run() { ++ return Run(Handle<Object>()); ++} ++ ++Local<Value> Script::Run(Handle<Object> qml) { + i::Isolate* isolate = i::Isolate::Current(); + ON_BAILOUT(isolate, "v8::Script::Run()", return Local<Value>()); + LOG_API(isolate, "Script::Run"); +@@ -1472,10 +1481,11 @@ Local<Value> Script::Run() { + fun = i::Handle<i::JSFunction>(i::JSFunction::cast(*obj), isolate); + } + EXCEPTION_PREAMBLE(isolate); ++ i::Handle<i::Object> qmlglobal = Utils::OpenHandle(*qml); + i::Handle<i::Object> receiver( + isolate->context()->global_proxy(), isolate); + i::Handle<i::Object> result = +- i::Execution::Call(fun, receiver, 0, NULL, &has_pending_exception); ++ i::Execution::Call(fun, receiver, 0, NULL, &has_pending_exception, qmlglobal); + EXCEPTION_BAILOUT_CHECK(isolate, Local<Value>()); + raw_result = *result; + } +@@ -3943,6 +3953,30 @@ v8::Local<v8::Context> Context::GetCalling() { + } + + ++v8::Local<v8::Object> Context::GetCallingQmlGlobal() { ++ i::Isolate* isolate = i::Isolate::Current(); ++ if (IsDeadCheck(isolate, "v8::Context::GetCallingQmlGlobal()")) { ++ return Local<Object>(); ++ } ++ ++ i::Context *context = isolate->context(); ++ if (!context->qml_global()->IsUndefined()) { ++ i::Handle<i::Object> qmlglobal(context->qml_global()); ++ return Utils::ToLocal(i::Handle<i::JSObject>::cast(qmlglobal)); ++ } ++ ++ i::JavaScriptFrameIterator it; ++ if (it.done()) return Local<Object>(); ++ context = i::Context::cast(it.frame()->context()); ++ if (!context->qml_global()->IsUndefined()) { ++ i::Handle<i::Object> qmlglobal(context->qml_global()); ++ return Utils::ToLocal(i::Handle<i::JSObject>::cast(qmlglobal)); ++ } else { ++ return Local<Object>(); ++ } ++} ++ ++ + v8::Local<v8::Object> Context::Global() { + if (IsDeadCheck(i::Isolate::Current(), "v8::Context::Global()")) { + return Local<v8::Object>(); +diff --git a/src/arm/code-stubs-arm.cc b/src/arm/code-stubs-arm.cc +index 8c147f9..a2626bf 100644 +--- a/src/arm/code-stubs-arm.cc ++++ b/src/arm/code-stubs-arm.cc +@@ -166,6 +166,10 @@ void FastNewContextStub::Generate(MacroAssembler* masm) { + __ ldr(r1, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX))); + __ str(r1, MemOperand(r0, Context::SlotOffset(Context::GLOBAL_INDEX))); + ++ // Copy the qml global object from the surrounding context. ++ __ ldr(r1, MemOperand(cp, Context::SlotOffset(Context::QML_GLOBAL_INDEX))); ++ __ str(r1, MemOperand(r0, Context::SlotOffset(Context::QML_GLOBAL_INDEX))); ++ + // Initialize the rest of the slots to undefined. + __ LoadRoot(r1, Heap::kUndefinedValueRootIndex); + for (int i = Context::MIN_CONTEXT_SLOTS; i < length; i++) { +diff --git a/src/arm/full-codegen-arm.cc b/src/arm/full-codegen-arm.cc +index 871b453..a69f10d 100644 +--- a/src/arm/full-codegen-arm.cc ++++ b/src/arm/full-codegen-arm.cc +@@ -154,12 +154,13 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { + + // Possibly allocate a local context. + int heap_slots = scope()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS; +- if (heap_slots > 0) { ++ if (heap_slots > 0 || ++ (scope()->is_qml_mode() && scope()->is_global_scope())) { + Comment cmnt(masm_, "[ Allocate local context"); + // Argument to NewContext is the function, which is in r1. + __ push(r1); + if (heap_slots <= FastNewContextStub::kMaximumSlots) { +- FastNewContextStub stub(heap_slots); ++ FastNewContextStub stub((heap_slots < 0)?0:heap_slots); + __ CallStub(&stub); + } else { + __ CallRuntime(Runtime::kNewContext, 1); +@@ -1247,9 +1248,9 @@ void FullCodeGenerator::EmitLoadGlobalSlotCheckExtensions( + __ bind(&fast); + } + +- __ ldr(r0, GlobalObjectOperand()); ++ __ ldr(r0, slot->var()->is_qml_global()?QmlGlobalObjectOperand():GlobalObjectOperand()); + __ mov(r2, Operand(slot->var()->name())); +- RelocInfo::Mode mode = (typeof_state == INSIDE_TYPEOF) ++ RelocInfo::Mode mode = (typeof_state == INSIDE_TYPEOF || slot->var()->is_qml_global()) + ? RelocInfo::CODE_TARGET + : RelocInfo::CODE_TARGET_CONTEXT; + Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize(); +@@ -1268,10 +1269,10 @@ void FullCodeGenerator::EmitVariableLoad(Variable* var) { + Comment cmnt(masm_, "Global variable"); + // Use inline caching. Variable name is passed in r2 and the global + // object (receiver) in r0. +- __ ldr(r0, GlobalObjectOperand()); ++ __ ldr(r0, var->is_qml_global()?QmlGlobalObjectOperand():GlobalObjectOperand()); + __ mov(r2, Operand(var->name())); + Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize(); +- EmitCallIC(ic, RelocInfo::CODE_TARGET_CONTEXT); ++ EmitCallIC(ic, var->is_qml_global()?RelocInfo::CODE_TARGET:RelocInfo::CODE_TARGET_CONTEXT); + context()->Plug(r0); + + } else if (slot != NULL && slot->type() == Slot::LOOKUP) { +@@ -1893,11 +1894,11 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, + // assignment. Right-hand-side value is passed in r0, variable name in + // r2, and the global object in r1. + __ mov(r2, Operand(var->name())); +- __ ldr(r1, GlobalObjectOperand()); ++ __ ldr(r1, var->is_qml_global()?QmlGlobalObjectOperand():GlobalObjectOperand()); + Handle<Code> ic = is_strict_mode() + ? isolate()->builtins()->StoreIC_Initialize_Strict() + : isolate()->builtins()->StoreIC_Initialize(); +- EmitCallIC(ic, RelocInfo::CODE_TARGET_CONTEXT); ++ EmitCallIC(ic, var->is_qml_global()?RelocInfo::CODE_TARGET:RelocInfo::CODE_TARGET_CONTEXT); + + } else if (op == Token::INIT_CONST) { + // Like var declarations, const declarations are hoisted to function +@@ -2184,10 +2185,13 @@ void FullCodeGenerator::EmitResolvePossiblyDirectEval(ResolveEvalFlag flag, + // Push the strict mode flag. + __ mov(r1, Operand(Smi::FromInt(strict_mode_flag()))); + __ push(r1); ++ // Push the qml mode flag. ++ __ mov(r1, Operand(Smi::FromInt(is_qml_mode()))); ++ __ push(r1); + + __ CallRuntime(flag == SKIP_CONTEXT_LOOKUP + ? Runtime::kResolvePossiblyDirectEvalNoLookup +- : Runtime::kResolvePossiblyDirectEval, 4); ++ : Runtime::kResolvePossiblyDirectEval, 5); + } + + +@@ -2263,9 +2267,9 @@ void FullCodeGenerator::VisitCall(Call* expr) { + context()->DropAndPlug(1, r0); + } else if (var != NULL && !var->is_this() && var->is_global()) { + // Push global object as receiver for the call IC. +- __ ldr(r0, GlobalObjectOperand()); ++ __ ldr(r0, var->is_qml_global()?QmlGlobalObjectOperand():GlobalObjectOperand()); + __ push(r0); +- EmitCallWithIC(expr, var->name(), RelocInfo::CODE_TARGET_CONTEXT); ++ EmitCallWithIC(expr, var->name(), var->is_qml_global()?RelocInfo::CODE_TARGET:RelocInfo::CODE_TARGET_CONTEXT); + } else if (var != NULL && var->AsSlot() != NULL && + var->AsSlot()->type() == Slot::LOOKUP) { + // Call to a lookup slot (dynamically introduced variable). +diff --git a/src/arm/lithium-arm.cc b/src/arm/lithium-arm.cc +index 3f1d15b..8406a96 100644 +--- a/src/arm/lithium-arm.cc ++++ b/src/arm/lithium-arm.cc +@@ -1195,7 +1195,7 @@ LInstruction* LChunkBuilder::DoOuterContext(HOuterContext* instr) { + + LInstruction* LChunkBuilder::DoGlobalObject(HGlobalObject* instr) { + LOperand* context = UseRegisterAtStart(instr->value()); +- return DefineAsRegister(new LGlobalObject(context)); ++ return DefineAsRegister(new LGlobalObject(context, instr->qml_global())); + } + + +diff --git a/src/arm/lithium-arm.h b/src/arm/lithium-arm.h +index 6da7c86..10b901f 100644 +--- a/src/arm/lithium-arm.h ++++ b/src/arm/lithium-arm.h +@@ -1378,13 +1378,17 @@ class LOuterContext: public LTemplateInstruction<1, 1, 0> { + + class LGlobalObject: public LTemplateInstruction<1, 1, 0> { + public: +- explicit LGlobalObject(LOperand* context) { ++ explicit LGlobalObject(LOperand* context, bool qml_global) { + inputs_[0] = context; ++ qml_global_ = qml_global; + } + + DECLARE_CONCRETE_INSTRUCTION(GlobalObject, "global-object") + + LOperand* context() { return InputAt(0); } ++ bool qml_global() { return qml_global_; } ++ private: ++ bool qml_global_; + }; + + +diff --git a/src/arm/lithium-codegen-arm.cc b/src/arm/lithium-codegen-arm.cc +index 4912449..db114ea 100644 +--- a/src/arm/lithium-codegen-arm.cc ++++ b/src/arm/lithium-codegen-arm.cc +@@ -166,12 +166,13 @@ bool LCodeGen::GeneratePrologue() { + + // Possibly allocate a local context. + int heap_slots = scope()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS; +- if (heap_slots > 0) { ++ if (heap_slots > 0 || ++ (scope()->is_qml_mode() && scope()->is_global_scope())) { + Comment(";;; Allocate local context"); + // Argument to NewContext is the function, which is in r1. + __ push(r1); + if (heap_slots <= FastNewContextStub::kMaximumSlots) { +- FastNewContextStub stub(heap_slots); ++ FastNewContextStub stub((heap_slots < 0)?0:heap_slots); + __ CallStub(&stub); + } else { + __ CallRuntime(Runtime::kNewContext, 1); +@@ -2664,7 +2665,7 @@ void LCodeGen::DoOuterContext(LOuterContext* instr) { + void LCodeGen::DoGlobalObject(LGlobalObject* instr) { + Register context = ToRegister(instr->context()); + Register result = ToRegister(instr->result()); +- __ ldr(result, ContextOperand(cp, Context::GLOBAL_INDEX)); ++ __ ldr(result, ContextOperand(cp, instr->qml_global()?Context::QML_GLOBAL_INDEX:Context::GLOBAL_INDEX)); + } + + +diff --git a/src/arm/macro-assembler-arm.h b/src/arm/macro-assembler-arm.h +index ab5efb0..d40cdbc 100644 +--- a/src/arm/macro-assembler-arm.h ++++ b/src/arm/macro-assembler-arm.h +@@ -1056,6 +1056,11 @@ static inline MemOperand GlobalObjectOperand() { + } + + ++static inline MemOperand QmlGlobalObjectOperand() { ++ return ContextOperand(cp, Context::QML_GLOBAL_INDEX); ++} ++ ++ + #ifdef GENERATED_CODE_COVERAGE + #define CODE_COVERAGE_STRINGIFY(x) #x + #define CODE_COVERAGE_TOSTRING(x) CODE_COVERAGE_STRINGIFY(x) +diff --git a/src/ast-inl.h b/src/ast-inl.h +index d80684a..adc5a1f 100644 +--- a/src/ast-inl.h ++++ b/src/ast-inl.h +@@ -106,6 +106,11 @@ bool FunctionLiteral::strict_mode() const { + } + + ++bool FunctionLiteral::qml_mode() const { ++ return scope()->is_qml_mode(); ++} ++ ++ + } } // namespace v8::internal + + #endif // V8_AST_INL_H_ +diff --git a/src/ast.h b/src/ast.h +index 65a25a9..f790dc0 100644 +--- a/src/ast.h ++++ b/src/ast.h +@@ -1712,6 +1712,7 @@ class FunctionLiteral: public Expression { + int end_position() const { return end_position_; } + bool is_expression() const { return is_expression_; } + bool strict_mode() const; ++ bool qml_mode() const; + + int materialized_literal_count() { return materialized_literal_count_; } + int expected_property_count() { return expected_property_count_; } +diff --git a/src/code-stubs.h b/src/code-stubs.h +index 56ef072..37e5383 100644 +--- a/src/code-stubs.h ++++ b/src/code-stubs.h +@@ -303,7 +303,7 @@ class FastNewContextStub : public CodeStub { + static const int kMaximumSlots = 64; + + explicit FastNewContextStub(int slots) : slots_(slots) { +- ASSERT(slots_ > 0 && slots <= kMaximumSlots); ++ ASSERT(slots_ >= 0 && slots <= kMaximumSlots); + } + + void Generate(MacroAssembler* masm); +diff --git a/src/compiler.cc b/src/compiler.cc +index 86d5de3..d2191b9 100755 +--- a/src/compiler.cc ++++ b/src/compiler.cc +@@ -462,7 +462,8 @@ Handle<SharedFunctionInfo> Compiler::Compile(Handle<String> source, + v8::Extension* extension, + ScriptDataImpl* input_pre_data, + Handle<Object> script_data, +- NativesFlag natives) { ++ NativesFlag natives, ++ v8::Script::CompileFlags compile_flags) { + Isolate* isolate = source->GetIsolate(); + int source_length = source->length(); + isolate->counters()->total_load_size()->Increment(source_length); +@@ -523,6 +524,7 @@ Handle<SharedFunctionInfo> Compiler::Compile(Handle<String> source, + info.MarkAsGlobal(); + info.SetExtension(extension); + info.SetPreParseData(pre_data); ++ if (compile_flags & v8::Script::QmlMode) info.MarkAsQmlMode(); + if (natives == NATIVES_CODE) info.MarkAsAllowingNativesSyntax(); + result = MakeFunctionInfo(&info); + if (extension == NULL && !result.is_null()) { +@@ -543,7 +545,8 @@ Handle<SharedFunctionInfo> Compiler::Compile(Handle<String> source, + Handle<SharedFunctionInfo> Compiler::CompileEval(Handle<String> source, + Handle<Context> context, + bool is_global, +- StrictModeFlag strict_mode) { ++ StrictModeFlag strict_mode, ++ bool qml_mode) { + Isolate* isolate = source->GetIsolate(); + int source_length = source->length(); + isolate->counters()->total_eval_size()->Increment(source_length); +@@ -567,6 +570,7 @@ Handle<SharedFunctionInfo> Compiler::CompileEval(Handle<String> source, + CompilationInfo info(script); + info.MarkAsEval(); + if (is_global) info.MarkAsGlobal(); ++ if (qml_mode) info.MarkAsQmlMode(); + if (strict_mode == kStrictMode) info.MarkAsStrictMode(); + info.SetCallingContext(context); + result = MakeFunctionInfo(&info); +@@ -610,6 +614,12 @@ bool Compiler::CompileLazy(CompilationInfo* info) { + info->MarkAsStrictMode(); + } + ++ // After parsing we know function's qml mode. Remember it. ++ if (info->function()->qml_mode()) { ++ shared->set_qml_mode(true); ++ info->MarkAsQmlMode(); ++ } ++ + // Compile the code. + if (!MakeCode(info)) { + if (!isolate->has_pending_exception()) { +@@ -755,6 +765,7 @@ void Compiler::SetFunctionInfo(Handle<SharedFunctionInfo> function_info, + *lit->this_property_assignments()); + function_info->set_allows_lazy_compilation(lit->AllowsLazyCompilation()); + function_info->set_strict_mode(lit->strict_mode()); ++ function_info->set_qml_mode(lit->qml_mode()); + } + + +diff --git a/src/compiler.h b/src/compiler.h +index e75e869..17cd369 100644 +--- a/src/compiler.h ++++ b/src/compiler.h +@@ -54,6 +54,7 @@ class CompilationInfo BASE_EMBEDDED { + bool is_global() const { return (flags_ & IsGlobal::mask()) != 0; } + bool is_strict_mode() const { return (flags_ & IsStrictMode::mask()) != 0; } + bool is_in_loop() const { return (flags_ & IsInLoop::mask()) != 0; } ++ bool is_qml_mode() const { return (flags_ & IsQmlMode::mask()) != 0; } + FunctionLiteral* function() const { return function_; } + Scope* scope() const { return scope_; } + Handle<Code> code() const { return code_; } +@@ -83,6 +84,9 @@ class CompilationInfo BASE_EMBEDDED { + ASSERT(is_lazy()); + flags_ |= IsInLoop::encode(true); + } ++ void MarkAsQmlMode() { ++ flags_ |= IsQmlMode::encode(true); ++ } + void MarkAsAllowingNativesSyntax() { + flags_ |= IsNativesSyntaxAllowed::encode(true); + } +@@ -141,6 +145,7 @@ class CompilationInfo BASE_EMBEDDED { + + // Determine whether or not we can adaptively optimize. + bool AllowOptimize() { ++ // XXX - fix qml mode optimizations + return V8::UseCrankshaft() && !closure_.is_null(); + } + +@@ -163,8 +168,13 @@ class CompilationInfo BASE_EMBEDDED { + + void Initialize(Mode mode) { + mode_ = V8::UseCrankshaft() ? mode : NONOPT; +- if (!shared_info_.is_null() && shared_info_->strict_mode()) { +- MarkAsStrictMode(); ++ if (!shared_info_.is_null()) { ++ if (shared_info_->strict_mode()) { ++ MarkAsStrictMode(); ++ } ++ if (shared_info_->qml_mode()) { ++ MarkAsQmlMode(); ++ } + } + } + +@@ -187,6 +197,8 @@ class CompilationInfo BASE_EMBEDDED { + class IsStrictMode: public BitField<bool, 4, 1> {}; + // Native syntax (%-stuff) allowed? + class IsNativesSyntaxAllowed: public BitField<bool, 5, 1> {}; ++ // Qml mode ++ class IsQmlMode: public BitField<bool, 6, 1> {}; + + unsigned flags_; + +@@ -252,13 +264,15 @@ class Compiler : public AllStatic { + v8::Extension* extension, + ScriptDataImpl* pre_data, + Handle<Object> script_data, +- NativesFlag is_natives_code); ++ NativesFlag is_natives_code, ++ v8::Script::CompileFlags compile_flags = v8::Script::Default); + + // Compile a String source within a context for Eval. + static Handle<SharedFunctionInfo> CompileEval(Handle<String> source, + Handle<Context> context, + bool is_global, +- StrictModeFlag strict_mode); ++ StrictModeFlag strict_mode, ++ bool qml_mode); + + // Compile from function info (used for lazy compilation). Returns true on + // success and false if the compilation resulted in a stack overflow. +diff --git a/src/contexts.cc b/src/contexts.cc +index 520f3dd..da5cacb 100644 +--- a/src/contexts.cc ++++ b/src/contexts.cc +@@ -89,6 +89,8 @@ Handle<Object> Context::Lookup(Handle<String> name, ContextLookupFlags flags, + PrintF(")\n"); + } + ++ Handle<JSObject> qml_global; ++ + do { + if (FLAG_trace_contexts) { + PrintF(" - looking in context %p", reinterpret_cast<void*>(*context)); +@@ -119,6 +121,10 @@ Handle<Object> Context::Lookup(Handle<String> name, ContextLookupFlags flags, + } + } + ++ if (qml_global.is_null() && !context->qml_global()->IsUndefined()) { ++ qml_global = Handle<JSObject>(context->qml_global(), isolate); ++ } ++ + if (context->is_function_context()) { + // we have context-local slots + +@@ -198,6 +204,23 @@ Handle<Object> Context::Lookup(Handle<String> name, ContextLookupFlags flags, + } + } while (follow_context_chain); + ++ if (!qml_global.is_null()) { ++ if ((flags & FOLLOW_PROTOTYPE_CHAIN) == 0) { ++ *attributes = qml_global->GetLocalPropertyAttribute(*name); ++ } else { ++ *attributes = qml_global->GetPropertyAttribute(*name); ++ } ++ ++ if (*attributes != ABSENT) { ++ // property found ++ if (FLAG_trace_contexts) { ++ PrintF("=> found property in qml global object %p\n", ++ reinterpret_cast<void*>(*qml_global)); ++ } ++ return qml_global; ++ } ++ } ++ + // slot not found + if (FLAG_trace_contexts) { + PrintF("=> no property/slot found\n"); +diff --git a/src/contexts.h b/src/contexts.h +index e46619e..57d8e7b 100644 +--- a/src/contexts.h ++++ b/src/contexts.h +@@ -182,6 +182,7 @@ class Context: public FixedArray { + FCONTEXT_INDEX, + PREVIOUS_INDEX, + EXTENSION_INDEX, ++ QML_GLOBAL_INDEX, + GLOBAL_INDEX, + MIN_CONTEXT_SLOTS, + +@@ -273,6 +274,9 @@ class Context: public FixedArray { + } + void set_global(GlobalObject* global) { set(GLOBAL_INDEX, global); } + ++ JSObject *qml_global() { return reinterpret_cast<JSObject *>(get(QML_GLOBAL_INDEX)); } ++ void set_qml_global(JSObject *qml_global) { set(QML_GLOBAL_INDEX, qml_global); } ++ + // Returns a JSGlobalProxy object or null. + JSObject* global_proxy(); + void set_global_proxy(JSObject* global); +diff --git a/src/execution.cc b/src/execution.cc +index eb26438..1632076 100644 +--- a/src/execution.cc ++++ b/src/execution.cc +@@ -70,7 +70,8 @@ static Handle<Object> Invoke(bool construct, + Handle<Object> receiver, + int argc, + Object*** args, +- bool* has_pending_exception) { ++ bool* has_pending_exception, ++ Handle<Object> qml) { + Isolate* isolate = func->GetIsolate(); + + // Entering JavaScript. +@@ -107,6 +108,12 @@ static Handle<Object> Invoke(bool construct, + // make the current one is indeed a global object. + ASSERT(func->context()->global()->IsGlobalObject()); + ++ Handle<JSObject> oldqml; ++ if (!qml.is_null()) { ++ oldqml = Handle<JSObject>(func->context()->qml_global()); ++ func->context()->set_qml_global(JSObject::cast(*qml)); ++ } ++ + { + // Save and restore context around invocation and block the + // allocation of handles without explicit handle scopes. +@@ -122,6 +129,9 @@ static Handle<Object> Invoke(bool construct, + receiver_pointer, argc, args); + } + ++ if (!qml.is_null()) ++ func->context()->set_qml_global(*oldqml); ++ + #ifdef DEBUG + value->Verify(); + #endif +@@ -150,14 +160,24 @@ Handle<Object> Execution::Call(Handle<JSFunction> func, + int argc, + Object*** args, + bool* pending_exception) { +- return Invoke(false, func, receiver, argc, args, pending_exception); ++ return Invoke(false, func, receiver, argc, args, pending_exception, Handle<Object>()); ++} ++ ++ ++Handle<Object> Execution::Call(Handle<JSFunction> func, ++ Handle<Object> receiver, ++ int argc, ++ Object*** args, ++ bool* pending_exception, ++ Handle<Object> qml) { ++ return Invoke(false, func, receiver, argc, args, pending_exception, qml); + } + + + Handle<Object> Execution::New(Handle<JSFunction> func, int argc, + Object*** args, bool* pending_exception) { + return Invoke(true, func, Isolate::Current()->global(), argc, args, +- pending_exception); ++ pending_exception, Handle<Object>()); + } + + +@@ -175,7 +195,7 @@ Handle<Object> Execution::TryCall(Handle<JSFunction> func, + catcher.SetCaptureMessage(false); + + Handle<Object> result = Invoke(false, func, receiver, argc, args, +- caught_exception); ++ caught_exception, Handle<Object>()); + + if (*caught_exception) { + ASSERT(catcher.HasCaught()); +diff --git a/src/execution.h b/src/execution.h +index d4b80d2..a476eb4 100644 +--- a/src/execution.h ++++ b/src/execution.h +@@ -56,6 +56,12 @@ class Execution : public AllStatic { + int argc, + Object*** args, + bool* pending_exception); ++ static Handle<Object> Call(Handle<JSFunction> func, ++ Handle<Object> receiver, ++ int argc, ++ Object*** args, ++ bool* pending_exception, ++ Handle<Object> qml); + + // Construct object from function, the caller supplies an array of + // arguments. Arguments are Object* type. After function returns, +diff --git a/src/full-codegen.cc b/src/full-codegen.cc +index d6ba56e..2eaef0f 100644 +--- a/src/full-codegen.cc ++++ b/src/full-codegen.cc +@@ -542,7 +542,7 @@ void FullCodeGenerator::VisitDeclarations( + // Do nothing in case of no declared global functions or variables. + if (globals > 0) { + Handle<FixedArray> array = +- isolate()->factory()->NewFixedArray(2 * globals, TENURED); ++ isolate()->factory()->NewFixedArray(3 * globals, TENURED); + for (int j = 0, i = 0; i < length; i++) { + Declaration* decl = declarations->at(i); + Variable* var = decl->proxy()->var(); +@@ -567,6 +567,7 @@ void FullCodeGenerator::VisitDeclarations( + } + array->set(j++, *function); + } ++ array->set(j++, Smi::FromInt(var->is_qml_global())); + } + } + // Invoke the platform-dependent code generator to do the actual +diff --git a/src/full-codegen.h b/src/full-codegen.h +index d6ed1b9..e3241aa 100644 +--- a/src/full-codegen.h ++++ b/src/full-codegen.h +@@ -505,6 +505,7 @@ class FullCodeGenerator: public AstVisitor { + StrictModeFlag strict_mode_flag() { + return is_strict_mode() ? kStrictMode : kNonStrictMode; + } ++ bool is_qml_mode() { return function()->qml_mode(); } + FunctionLiteral* function() { return info_->function(); } + Scope* scope() { return info_->scope(); } + +diff --git a/src/heap.cc b/src/heap.cc +index bf2940e..da958c2 100644 +--- a/src/heap.cc ++++ b/src/heap.cc +@@ -3795,6 +3795,7 @@ MaybeObject* Heap::AllocateFunctionContext(int length, JSFunction* function) { + context->set_previous(NULL); + context->set_extension(NULL); + context->set_global(function->context()->global()); ++ context->set_qml_global(function->context()->qml_global()); + ASSERT(!context->IsGlobalContext()); + ASSERT(context->is_function_context()); + ASSERT(result->IsContext()); +@@ -3817,6 +3818,7 @@ MaybeObject* Heap::AllocateWithContext(Context* previous, + context->set_previous(previous); + context->set_extension(extension); + context->set_global(previous->global()); ++ context->set_qml_global(previous->qml_global()); + ASSERT(!context->IsGlobalContext()); + ASSERT(!context->is_function_context()); + ASSERT(result->IsContext()); +diff --git a/src/hydrogen-instructions.h b/src/hydrogen-instructions.h +index a623775..52455bc 100644 +--- a/src/hydrogen-instructions.h ++++ b/src/hydrogen-instructions.h +@@ -1148,7 +1148,7 @@ class HOuterContext: public HUnaryOperation { + + class HGlobalObject: public HUnaryOperation { + public: +- explicit HGlobalObject(HValue* context) : HUnaryOperation(context) { ++ explicit HGlobalObject(HValue* context) : HUnaryOperation(context), qml_global_(false) { + set_representation(Representation::Tagged()); + SetFlag(kUseGVN); + } +@@ -1159,8 +1159,14 @@ class HGlobalObject: public HUnaryOperation { + return Representation::Tagged(); + } + ++ bool qml_global() { return qml_global_; } ++ void set_qml_global(bool v) { qml_global_ = v; } ++ + protected: + virtual bool DataEquals(HValue* other) { return true; } ++ ++ private: ++ bool qml_global_; + }; + + +@@ -1177,7 +1183,7 @@ class HGlobalReceiver: public HUnaryOperation { + virtual Representation RequiredInputRepresentation(int index) const { + return Representation::Tagged(); + } +- ++ + protected: + virtual bool DataEquals(HValue* other) { return true; } + }; +diff --git a/src/hydrogen.cc b/src/hydrogen.cc +index 73ea97d..d17e304 100644 +--- a/src/hydrogen.cc ++++ b/src/hydrogen.cc +@@ -2918,6 +2918,7 @@ void HGraphBuilder::VisitVariableProxy(VariableProxy* expr) { + HContext* context = new(zone()) HContext; + AddInstruction(context); + HGlobalObject* global_object = new(zone()) HGlobalObject(context); ++ if (variable->is_qml_global()) global_object->set_qml_global(true); + AddInstruction(global_object); + HLoadGlobalGeneric* instr = + new(zone()) HLoadGlobalGeneric(context, +@@ -3307,6 +3308,7 @@ void HGraphBuilder::HandleGlobalVariableAssignment(Variable* var, + HContext* context = new(zone()) HContext; + AddInstruction(context); + HGlobalObject* global_object = new(zone()) HGlobalObject(context); ++ if (var->is_qml_global()) global_object->set_qml_global(true); + AddInstruction(global_object); + HStoreGlobalGeneric* instr = + new(zone()) HStoreGlobalGeneric(context, +diff --git a/src/ia32/code-stubs-ia32.cc b/src/ia32/code-stubs-ia32.cc +index 5d32095..afa599e 100644 +--- a/src/ia32/code-stubs-ia32.cc ++++ b/src/ia32/code-stubs-ia32.cc +@@ -147,6 +147,13 @@ void FastNewContextStub::Generate(MacroAssembler* masm) { + __ mov(ebx, Operand(ebx, Context::SlotOffset(Context::GLOBAL_INDEX))); + __ mov(Operand(eax, Context::SlotOffset(Context::GLOBAL_INDEX)), ebx); + ++ // Copy the qml global object from the surrounding context. We go through the ++ // context in the function (ecx) to match the allocation behavior we have ++ // in the runtime system (see Heap::AllocateFunctionContext). ++ __ mov(ebx, FieldOperand(ecx, JSFunction::kContextOffset)); ++ __ mov(ebx, Operand(ebx, Context::SlotOffset(Context::QML_GLOBAL_INDEX))); ++ __ mov(Operand(eax, Context::SlotOffset(Context::QML_GLOBAL_INDEX)), ebx); ++ + // Initialize the rest of the slots to undefined. + __ mov(ebx, factory->undefined_value()); + for (int i = Context::MIN_CONTEXT_SLOTS; i < length; i++) { +diff --git a/src/ia32/full-codegen-ia32.cc b/src/ia32/full-codegen-ia32.cc +index 5d153a8..0ddcde2 100644 +--- a/src/ia32/full-codegen-ia32.cc ++++ b/src/ia32/full-codegen-ia32.cc +@@ -142,12 +142,13 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { + + // Possibly allocate a local context. + int heap_slots = scope()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS; +- if (heap_slots > 0) { ++ if (heap_slots > 0 || ++ (scope()->is_qml_mode() && scope()->is_global_scope())) { + Comment cmnt(masm_, "[ Allocate local context"); + // Argument to NewContext is the function, which is still in edi. + __ push(edi); + if (heap_slots <= FastNewContextStub::kMaximumSlots) { +- FastNewContextStub stub(heap_slots); ++ FastNewContextStub stub((heap_slots < 0)?0:heap_slots); + __ CallStub(&stub); + } else { + __ CallRuntime(Runtime::kNewContext, 1); +@@ -1107,10 +1108,10 @@ void FullCodeGenerator::EmitLoadGlobalSlotCheckExtensions( + + // All extension objects were empty and it is safe to use a global + // load IC call. +- __ mov(eax, GlobalObjectOperand()); ++ __ mov(eax, slot->var()->is_qml_global()?QmlGlobalObjectOperand():GlobalObjectOperand()); + __ mov(ecx, slot->var()->name()); + Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize(); +- RelocInfo::Mode mode = (typeof_state == INSIDE_TYPEOF) ++ RelocInfo::Mode mode = (typeof_state == INSIDE_TYPEOF || slot->var()->is_qml_global()) + ? RelocInfo::CODE_TARGET + : RelocInfo::CODE_TARGET_CONTEXT; + EmitCallIC(ic, mode); +@@ -1214,10 +1215,10 @@ void FullCodeGenerator::EmitVariableLoad(Variable* var) { + Comment cmnt(masm_, "Global variable"); + // Use inline caching. Variable name is passed in ecx and the global + // object on the stack. +- __ mov(eax, GlobalObjectOperand()); ++ __ mov(eax, var->is_qml_global()?QmlGlobalObjectOperand():GlobalObjectOperand()); + __ mov(ecx, var->name()); + Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize(); +- EmitCallIC(ic, RelocInfo::CODE_TARGET_CONTEXT); ++ EmitCallIC(ic, var->is_qml_global()?RelocInfo::CODE_TARGET:RelocInfo::CODE_TARGET_CONTEXT); + context()->Plug(eax); + + } else if (slot != NULL && slot->type() == Slot::LOOKUP) { +@@ -1837,11 +1838,11 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, + // assignment. Right-hand-side value is passed in eax, variable name in + // ecx, and the global object on the stack. + __ mov(ecx, var->name()); +- __ mov(edx, GlobalObjectOperand()); ++ __ mov(edx, var->is_qml_global()?QmlGlobalObjectOperand():GlobalObjectOperand()); + Handle<Code> ic = is_strict_mode() + ? isolate()->builtins()->StoreIC_Initialize_Strict() + : isolate()->builtins()->StoreIC_Initialize(); +- EmitCallIC(ic, RelocInfo::CODE_TARGET_CONTEXT); ++ EmitCallIC(ic, var->is_qml_global()?RelocInfo::CODE_TARGET:RelocInfo::CODE_TARGET_CONTEXT); + + } else if (op == Token::INIT_CONST) { + // Like var declarations, const declarations are hoisted to function +@@ -2113,9 +2114,12 @@ void FullCodeGenerator::EmitResolvePossiblyDirectEval(ResolveEvalFlag flag, + // Push the strict mode flag. + __ push(Immediate(Smi::FromInt(strict_mode_flag()))); + ++ // Push the qml mode flag ++ __ push(Immediate(Smi::FromInt(is_qml_mode()))); ++ + __ CallRuntime(flag == SKIP_CONTEXT_LOOKUP + ? Runtime::kResolvePossiblyDirectEvalNoLookup +- : Runtime::kResolvePossiblyDirectEval, 4); ++ : Runtime::kResolvePossiblyDirectEval, 5); + } + + +@@ -2188,8 +2192,8 @@ void FullCodeGenerator::VisitCall(Call* expr) { + context()->DropAndPlug(1, eax); + } else if (var != NULL && !var->is_this() && var->is_global()) { + // Push global object as receiver for the call IC. +- __ push(GlobalObjectOperand()); +- EmitCallWithIC(expr, var->name(), RelocInfo::CODE_TARGET_CONTEXT); ++ __ push(var->is_qml_global()?QmlGlobalObjectOperand():GlobalObjectOperand()); ++ EmitCallWithIC(expr, var->name(), var->is_qml_global()?RelocInfo::CODE_TARGET:RelocInfo::CODE_TARGET_CONTEXT); + } else if (var != NULL && var->AsSlot() != NULL && + var->AsSlot()->type() == Slot::LOOKUP) { + // Call to a lookup slot (dynamically introduced variable). +diff --git a/src/ia32/lithium-codegen-ia32.cc b/src/ia32/lithium-codegen-ia32.cc +index 0f96f78..c1da075 100644 +--- a/src/ia32/lithium-codegen-ia32.cc ++++ b/src/ia32/lithium-codegen-ia32.cc +@@ -159,12 +159,13 @@ bool LCodeGen::GeneratePrologue() { + + // Possibly allocate a local context. + int heap_slots = scope()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS; +- if (heap_slots > 0) { ++ if (heap_slots > 0 || ++ (scope()->is_qml_mode() && scope()->is_global_scope())) { + Comment(";;; Allocate local context"); + // Argument to NewContext is the function, which is still in edi. + __ push(edi); + if (heap_slots <= FastNewContextStub::kMaximumSlots) { +- FastNewContextStub stub(heap_slots); ++ FastNewContextStub stub((heap_slots < 0)?0:heap_slots); + __ CallStub(&stub); + } else { + __ CallRuntime(Runtime::kNewContext, 1); +@@ -2525,7 +2526,7 @@ void LCodeGen::DoOuterContext(LOuterContext* instr) { + void LCodeGen::DoGlobalObject(LGlobalObject* instr) { + Register context = ToRegister(instr->context()); + Register result = ToRegister(instr->result()); +- __ mov(result, Operand(context, Context::SlotOffset(Context::GLOBAL_INDEX))); ++ __ mov(result, Operand(context, Context::SlotOffset(instr->qml_global()?Context::QML_GLOBAL_INDEX:Context::GLOBAL_INDEX))); + } + + +diff --git a/src/ia32/lithium-ia32.cc b/src/ia32/lithium-ia32.cc +index 9ccd189..8e98b73 100644 +--- a/src/ia32/lithium-ia32.cc ++++ b/src/ia32/lithium-ia32.cc +@@ -1205,7 +1205,7 @@ LInstruction* LChunkBuilder::DoOuterContext(HOuterContext* instr) { + + LInstruction* LChunkBuilder::DoGlobalObject(HGlobalObject* instr) { + LOperand* context = UseRegisterAtStart(instr->value()); +- return DefineAsRegister(new LGlobalObject(context)); ++ return DefineAsRegister(new LGlobalObject(context, instr->qml_global())); + } + + +diff --git a/src/ia32/lithium-ia32.h b/src/ia32/lithium-ia32.h +index 9ace8f8..95ed001 100644 +--- a/src/ia32/lithium-ia32.h ++++ b/src/ia32/lithium-ia32.h +@@ -1416,13 +1416,17 @@ class LOuterContext: public LTemplateInstruction<1, 1, 0> { + + class LGlobalObject: public LTemplateInstruction<1, 1, 0> { + public: +- explicit LGlobalObject(LOperand* context) { ++ explicit LGlobalObject(LOperand* context, bool qml_global) { + inputs_[0] = context; ++ qml_global_ = qml_global; + } + + DECLARE_CONCRETE_INSTRUCTION(GlobalObject, "global-object") + + LOperand* context() { return InputAt(0); } ++ bool qml_global() { return qml_global_; } ++ private: ++ bool qml_global_; + }; + + +diff --git a/src/ia32/macro-assembler-ia32.h b/src/ia32/macro-assembler-ia32.h +index b986264..f8479ae 100644 +--- a/src/ia32/macro-assembler-ia32.h ++++ b/src/ia32/macro-assembler-ia32.h +@@ -778,6 +778,11 @@ static inline Operand GlobalObjectOperand() { + } + + ++static inline Operand QmlGlobalObjectOperand() { ++ return ContextOperand(esi, Context::QML_GLOBAL_INDEX); ++} ++ ++ + // Generates an Operand for saving parameters after PrepareCallApiFunction. + Operand ApiParameterOperand(int index); + +diff --git a/src/objects-inl.h b/src/objects-inl.h +index 231b835..1c7f83e 100644 +--- a/src/objects-inl.h ++++ b/src/objects-inl.h +@@ -3242,6 +3242,18 @@ void SharedFunctionInfo::set_strict_mode(bool value) { + } + + ++bool SharedFunctionInfo::qml_mode() { ++ return BooleanBit::get(compiler_hints(), kQmlModeFunction); ++} ++ ++ ++void SharedFunctionInfo::set_qml_mode(bool value) { ++ set_compiler_hints(BooleanBit::set(compiler_hints(), ++ kQmlModeFunction, ++ value)); ++} ++ ++ + ACCESSORS(CodeCache, default_cache, FixedArray, kDefaultCacheOffset) + ACCESSORS(CodeCache, normal_type_cache, Object, kNormalTypeCacheOffset) + +diff --git a/src/objects.h b/src/objects.h +index 1bdb5c7..edbc47a 100644 +--- a/src/objects.h ++++ b/src/objects.h +@@ -4331,6 +4331,10 @@ class SharedFunctionInfo: public HeapObject { + inline bool strict_mode(); + inline void set_strict_mode(bool value); + ++ // Indicates whether the function is a qml mode function ++ inline bool qml_mode(); ++ inline void set_qml_mode(bool value); ++ + // Indicates whether or not the code in the shared function support + // deoptimization. + inline bool has_deoptimization_support(); +@@ -4511,6 +4515,7 @@ class SharedFunctionInfo: public HeapObject { + static const int kCodeAgeMask = 0x7; + static const int kOptimizationDisabled = 6; + static const int kStrictModeFunction = 7; ++ static const int kQmlModeFunction = 8; + + private: + #if V8_HOST_ARCH_32_BIT +diff --git a/src/parser.cc b/src/parser.cc +index a84ec6f..7f5c361 100644 +--- a/src/parser.cc ++++ b/src/parser.cc +@@ -593,7 +593,8 @@ Parser::Parser(Handle<Script> script, + + FunctionLiteral* Parser::ParseProgram(Handle<String> source, + bool in_global_context, +- StrictModeFlag strict_mode) { ++ StrictModeFlag strict_mode, ++ bool qml_mode) { + CompilationZoneScope zone_scope(DONT_DELETE_ON_EXIT); + + HistogramTimerScope timer(isolate()->counters()->parse()); +@@ -609,11 +610,11 @@ FunctionLiteral* Parser::ParseProgram(Handle<String> source, + ExternalTwoByteStringUC16CharacterStream stream( + Handle<ExternalTwoByteString>::cast(source), 0, source->length()); + scanner_.Initialize(&stream); +- return DoParseProgram(source, in_global_context, strict_mode, &zone_scope); ++ return DoParseProgram(source, in_global_context, strict_mode, qml_mode, &zone_scope); + } else { + GenericStringUC16CharacterStream stream(source, 0, source->length()); + scanner_.Initialize(&stream); +- return DoParseProgram(source, in_global_context, strict_mode, &zone_scope); ++ return DoParseProgram(source, in_global_context, strict_mode, qml_mode, &zone_scope); + } + } + +@@ -621,6 +622,7 @@ FunctionLiteral* Parser::ParseProgram(Handle<String> source, + FunctionLiteral* Parser::DoParseProgram(Handle<String> source, + bool in_global_context, + StrictModeFlag strict_mode, ++ bool qml_mode, + ZoneScope* zone_scope) { + ASSERT(target_stack_ == NULL); + if (pre_data_ != NULL) pre_data_->Initialize(); +@@ -641,6 +643,9 @@ FunctionLiteral* Parser::DoParseProgram(Handle<String> source, + if (strict_mode == kStrictMode) { + top_scope_->EnableStrictMode(); + } ++ if (qml_mode) { ++ scope->EnableQmlMode(); ++ } + ZoneList<Statement*>* body = new ZoneList<Statement*>(16); + bool ok = true; + int beg_loc = scanner().location().beg_pos; +@@ -729,6 +734,9 @@ FunctionLiteral* Parser::ParseLazy(CompilationInfo* info, + if (shared_info->strict_mode()) { + top_scope_->EnableStrictMode(); + } ++ if (shared_info->qml_mode()) { ++ top_scope_->EnableQmlMode(); ++ } + + FunctionLiteralType type = + shared_info->is_expression() ? EXPRESSION : DECLARATION; +@@ -1661,6 +1669,11 @@ Block* Parser::ParseVariableDeclarations(bool accept_IN, + arguments->Add(value); + value = NULL; // zap the value to avoid the unnecessary assignment + ++ int qml_mode = 0; ++ if (top_scope_->is_qml_mode() && !Isolate::Current()->global()->HasProperty(*name)) ++ qml_mode = 1; ++ arguments->Add(NewNumberLiteral(qml_mode)); ++ + // Construct the call to Runtime_InitializeConstGlobal + // and add it to the initialization statement block. + // Note that the function does different things depending on +@@ -1676,6 +1689,11 @@ Block* Parser::ParseVariableDeclarations(bool accept_IN, + arguments->Add(NewNumberLiteral( + top_scope_->is_strict_mode() ? kStrictMode : kNonStrictMode)); + ++ int qml_mode = 0; ++ if (top_scope_->is_qml_mode() && !Isolate::Current()->global()->HasProperty(*name)) ++ qml_mode = 1; ++ arguments->Add(NewNumberLiteral(qml_mode)); ++ + // Be careful not to assign a value to the global variable if + // we're in a with. The initialization value should not + // necessarily be stored in the global object in that case, +@@ -5157,7 +5175,8 @@ bool ParserApi::Parse(CompilationInfo* info) { + Handle<String> source = Handle<String>(String::cast(script->source())); + result = parser.ParseProgram(source, + info->is_global(), +- info->StrictMode()); ++ info->StrictMode(), ++ info->is_qml_mode()); + } + } + +diff --git a/src/parser.h b/src/parser.h +index 64f1303..4d45e45 100644 +--- a/src/parser.h ++++ b/src/parser.h +@@ -431,7 +431,8 @@ class Parser { + // Returns NULL if parsing failed. + FunctionLiteral* ParseProgram(Handle<String> source, + bool in_global_context, +- StrictModeFlag strict_mode); ++ StrictModeFlag strict_mode, ++ bool qml_mode); + + FunctionLiteral* ParseLazy(CompilationInfo* info); + +@@ -464,6 +465,7 @@ class Parser { + FunctionLiteral* DoParseProgram(Handle<String> source, + bool in_global_context, + StrictModeFlag strict_mode, ++ bool qml_mode, + ZoneScope* zone_scope); + + // Report syntax error +diff --git a/src/prettyprinter.cc b/src/prettyprinter.cc +index c777ab4..1964e02 100644 +--- a/src/prettyprinter.cc ++++ b/src/prettyprinter.cc +@@ -656,6 +656,9 @@ void AstPrinter::PrintLiteralWithModeIndented(const char* info, + EmbeddedVector<char, 256> buf; + int pos = OS::SNPrintF(buf, "%s (mode = %s", info, + Variable::Mode2String(var->mode())); ++ if (var->is_qml_global()) { ++ pos += OS::SNPrintF(buf + pos, ":QML"); ++ } + OS::SNPrintF(buf + pos, ")"); + PrintLiteralIndented(buf.start(), value, true); + } +diff --git a/src/runtime.cc b/src/runtime.cc +index 660352c..827d954 100644 +--- a/src/runtime.cc ++++ b/src/runtime.cc +@@ -1065,8 +1065,6 @@ static Failure* ThrowRedeclarationError(Isolate* isolate, + RUNTIME_FUNCTION(MaybeObject*, Runtime_DeclareGlobals) { + ASSERT(args.length() == 4); + HandleScope scope(isolate); +- Handle<GlobalObject> global = Handle<GlobalObject>( +- isolate->context()->global()); + + Handle<Context> context = args.at<Context>(0); + CONVERT_ARG_CHECKED(FixedArray, pairs, 1); +@@ -1075,6 +1073,9 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DeclareGlobals) { + static_cast<StrictModeFlag>(Smi::cast(args[3])->value()); + ASSERT(strict_mode == kStrictMode || strict_mode == kNonStrictMode); + ++ Handle<JSObject> js_global = Handle<JSObject>(isolate->context()->global()); ++ Handle<JSObject> qml_global = Handle<JSObject>(isolate->context()->qml_global()); ++ + // Compute the property attributes. According to ECMA-262, section + // 13, page 71, the property must be read-only and + // non-deletable. However, neither SpiderMonkey nor KJS creates the +@@ -1083,10 +1084,13 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DeclareGlobals) { + + // Traverse the name/value pairs and set the properties. + int length = pairs->length(); +- for (int i = 0; i < length; i += 2) { ++ for (int i = 0; i < length; i += 3) { + HandleScope scope(isolate); + Handle<String> name(String::cast(pairs->get(i))); + Handle<Object> value(pairs->get(i + 1), isolate); ++ Handle<Smi> is_qml_global(Smi::cast(pairs->get(i + 2))); ++ ++ Handle<JSObject> global = is_qml_global->value()?qml_global:js_global; + + // We have to declare a global const property. To capture we only + // assign to it when evaluating the assignment for "const x = +@@ -1316,20 +1320,25 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_InitializeVarGlobal) { + NoHandleAllocation nha; + // args[0] == name + // args[1] == strict_mode +- // args[2] == value (optional) ++ // args[2] == qml_mode ++ // args[3] == value (optional) + + // Determine if we need to assign to the variable if it already + // exists (based on the number of arguments). +- RUNTIME_ASSERT(args.length() == 2 || args.length() == 3); +- bool assign = args.length() == 3; ++ RUNTIME_ASSERT(args.length() == 3 || args.length() == 4); ++ bool assign = args.length() == 4; + + CONVERT_ARG_CHECKED(String, name, 0); +- GlobalObject* global = isolate->context()->global(); + RUNTIME_ASSERT(args[1]->IsSmi()); + StrictModeFlag strict_mode = + static_cast<StrictModeFlag>(Smi::cast(args[1])->value()); + ASSERT(strict_mode == kStrictMode || strict_mode == kNonStrictMode); + ++ RUNTIME_ASSERT(args[2]->IsSmi()); ++ int qml_mode = Smi::cast(args[2])->value(); ++ ++ JSObject* global = qml_mode?isolate->context()->qml_global():isolate->context()->global(); ++ + // According to ECMA-262, section 12.2, page 62, the property must + // not be deletable. + PropertyAttributes attributes = DONT_DELETE; +@@ -1350,7 +1359,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_InitializeVarGlobal) { + if (lookup.IsReadOnly()) { + // If we found readonly property on one of hidden prototypes, + // just shadow it. +- if (real_holder != isolate->context()->global()) break; ++ if (real_holder != global) break; + return ThrowRedeclarationError(isolate, "const", name); + } + +@@ -1372,7 +1381,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_InitializeVarGlobal) { + // overwrite it with a variable declaration we must throw a + // re-declaration error. However if we found readonly property + // on one of hidden prototypes, just shadow it. +- if (real_holder != isolate->context()->global()) break; ++ if (real_holder != global) break; + return ThrowRedeclarationError(isolate, "const", name); + } + } +@@ -1384,7 +1393,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_InitializeVarGlobal) { + } + + // Assign the value (or undefined) to the property. +- Object* value = (assign) ? args[2] : isolate->heap()->undefined_value(); ++ Object* value = (assign) ? args[3] : isolate->heap()->undefined_value(); + return real_holder->SetProperty( + &lookup, *name, value, attributes, strict_mode); + } +@@ -1399,9 +1408,9 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_InitializeVarGlobal) { + real_holder = JSObject::cast(proto); + } + +- global = isolate->context()->global(); ++ global = qml_mode?isolate->context()->qml_global():isolate->context()->global(); + if (assign) { +- return global->SetProperty(*name, args[2], attributes, strict_mode, true); ++ return global->SetProperty(*name, args[3], attributes, strict_mode, true); + } + return isolate->heap()->undefined_value(); + } +@@ -1411,12 +1420,15 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_InitializeConstGlobal) { + // All constants are declared with an initial value. The name + // of the constant is the first argument and the initial value + // is the second. +- RUNTIME_ASSERT(args.length() == 2); ++ RUNTIME_ASSERT(args.length() == 3); + CONVERT_ARG_CHECKED(String, name, 0); + Handle<Object> value = args.at<Object>(1); + ++ RUNTIME_ASSERT(args[2]->IsSmi()); ++ int qml_mode = Smi::cast(args[2])->value(); ++ + // Get the current global object from top. +- GlobalObject* global = isolate->context()->global(); ++ JSObject* global = qml_mode?isolate->context()->qml_global():isolate->context()->global(); + + // According to ECMA-262, section 12.2, page 62, the property must + // not be deletable. Since it's a const, it must be READ_ONLY too. +@@ -1456,7 +1468,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_InitializeConstGlobal) { + // with setting the value because the property is either absent or + // read-only. We also have to do redo the lookup. + HandleScope handle_scope(isolate); +- Handle<GlobalObject> global(isolate->context()->global()); ++ Handle<JSObject> global(qml_mode?isolate->context()->qml_global():isolate->context()->global()); + + // BUG 1213575: Handle the case where we have to set a read-only + // property through an interceptor and only do it if it's +@@ -8160,7 +8172,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_CompileString) { + Handle<SharedFunctionInfo> shared = Compiler::CompileEval(source, + context, + true, +- kNonStrictMode); ++ kNonStrictMode, ++ false); + if (shared.is_null()) return Failure::Exception(); + Handle<JSFunction> fun = + isolate->factory()->NewFunctionFromSharedFunctionInfo(shared, +@@ -8173,14 +8186,16 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_CompileString) { + static ObjectPair CompileGlobalEval(Isolate* isolate, + Handle<String> source, + Handle<Object> receiver, +- StrictModeFlag strict_mode) { ++ StrictModeFlag strict_mode, ++ bool qml_mode) { + // Deal with a normal eval call with a string argument. Compile it + // and return the compiled function bound in the local context. + Handle<SharedFunctionInfo> shared = Compiler::CompileEval( + source, + Handle<Context>(isolate->context()), + isolate->context()->IsGlobalContext(), +- strict_mode); ++ strict_mode, ++ qml_mode); + if (shared.is_null()) return MakePair(Failure::Exception(), NULL); + Handle<JSFunction> compiled = + isolate->factory()->NewFunctionFromSharedFunctionInfo( +@@ -8190,7 +8205,7 @@ static ObjectPair CompileGlobalEval(Isolate* isolate, + + + RUNTIME_FUNCTION(ObjectPair, Runtime_ResolvePossiblyDirectEval) { +- ASSERT(args.length() == 4); ++ ASSERT(args.length() == 5); + + HandleScope scope(isolate); + Handle<Object> callee = args.at<Object>(0); +@@ -8257,16 +8272,18 @@ RUNTIME_FUNCTION(ObjectPair, Runtime_ResolvePossiblyDirectEval) { + } + + ASSERT(args[3]->IsSmi()); ++ ASSERT(args[4]->IsSmi()); + return CompileGlobalEval(isolate, + args.at<String>(1), + args.at<Object>(2), + static_cast<StrictModeFlag>( +- Smi::cast(args[3])->value())); ++ Smi::cast(args[3])->value()), ++ Smi::cast(args[4])->value()); + } + + + RUNTIME_FUNCTION(ObjectPair, Runtime_ResolvePossiblyDirectEvalNoLookup) { +- ASSERT(args.length() == 4); ++ ASSERT(args.length() == 5); + + HandleScope scope(isolate); + Handle<Object> callee = args.at<Object>(0); +@@ -8280,11 +8297,13 @@ RUNTIME_FUNCTION(ObjectPair, Runtime_ResolvePossiblyDirectEvalNoLookup) { + } + + ASSERT(args[3]->IsSmi()); ++ ASSERT(args[4]->IsSmi()); + return CompileGlobalEval(isolate, + args.at<String>(1), + args.at<Object>(2), + static_cast<StrictModeFlag>( +- Smi::cast(args[3])->value())); ++ Smi::cast(args[3])->value()), ++ Smi::cast(args[4])->value()); + } + + +@@ -10633,7 +10652,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugEvaluate) { + Compiler::CompileEval(function_source, + context, + context->IsGlobalContext(), +- kNonStrictMode); ++ kNonStrictMode, ++ false); + if (shared.is_null()) return Failure::Exception(); + Handle<JSFunction> compiled_function = + isolate->factory()->NewFunctionFromSharedFunctionInfo(shared, context); +@@ -10722,7 +10742,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugEvaluateGlobal) { + // Currently, the eval code will be executed in non-strict mode, + // even in the strict code context. + Handle<SharedFunctionInfo> shared = +- Compiler::CompileEval(source, context, is_global, kNonStrictMode); ++ Compiler::CompileEval(source, context, is_global, kNonStrictMode, false); + if (shared.is_null()) return Failure::Exception(); + Handle<JSFunction> compiled_function = + Handle<JSFunction>( +diff --git a/src/runtime.h b/src/runtime.h +index bf1ba68..5e97173 100644 +--- a/src/runtime.h ++++ b/src/runtime.h +@@ -241,8 +241,8 @@ namespace internal { + \ + /* Eval */ \ + F(GlobalReceiver, 1, 1) \ +- F(ResolvePossiblyDirectEval, 4, 2) \ +- F(ResolvePossiblyDirectEvalNoLookup, 4, 2) \ ++ F(ResolvePossiblyDirectEval, 5, 2) \ ++ F(ResolvePossiblyDirectEvalNoLookup, 5, 2) \ + \ + F(SetProperty, -1 /* 4 or 5 */, 1) \ + F(DefineOrRedefineDataProperty, 4, 1) \ +@@ -296,8 +296,8 @@ namespace internal { + /* Declarations and initialization */ \ + F(DeclareGlobals, 4, 1) \ + F(DeclareContextSlot, 4, 1) \ +- F(InitializeVarGlobal, -1 /* 2 or 3 */, 1) \ +- F(InitializeConstGlobal, 2, 1) \ ++ F(InitializeVarGlobal, -1 /* 3 or 4 */, 1) \ ++ F(InitializeConstGlobal, 3, 1) \ + F(InitializeConstContextSlot, 3, 1) \ + F(OptimizeObjectForAddingMultipleProperties, 2, 1) \ + \ +diff --git a/src/scopes.cc b/src/scopes.cc +index 8df93c5..734a217 100644 +--- a/src/scopes.cc ++++ b/src/scopes.cc +@@ -198,6 +198,7 @@ void Scope::SetDefaults(Type type, + scope_calls_eval_ = false; + // Inherit the strict mode from the parent scope. + strict_mode_ = (outer_scope != NULL) && outer_scope->strict_mode_; ++ qml_mode_ = (outer_scope != NULL) && outer_scope->qml_mode_; + outer_scope_calls_eval_ = false; + inner_scope_calls_eval_ = false; + outer_scope_is_eval_scope_ = false; +@@ -796,6 +797,10 @@ void Scope::ResolveVariable(Scope* global_scope, + ASSERT(global_scope != NULL); + var = global_scope->DeclareGlobal(proxy->name()); + ++ if (qml_mode_ && !Isolate::Current()->global()->HasProperty(*(proxy->name()))) { ++ var->set_is_qml_global(true); ++ } ++ + } else if (scope_inside_with_) { + // If we are inside a with statement we give up and look up + // the variable at runtime. +@@ -816,6 +821,8 @@ void Scope::ResolveVariable(Scope* global_scope, + // variables. + if (context->GlobalIfNotShadowedByEval(proxy->name())) { + var = NonLocal(proxy->name(), Variable::DYNAMIC_GLOBAL); ++ if (qml_mode_ && !Isolate::Current()->global()->HasProperty(*(proxy->name()))) ++ var->set_is_qml_global(true); + + } else { + var = NonLocal(proxy->name(), Variable::DYNAMIC); +@@ -827,6 +834,9 @@ void Scope::ResolveVariable(Scope* global_scope, + // variable is global unless it is shadowed by eval-introduced + // variables. + var = NonLocal(proxy->name(), Variable::DYNAMIC_GLOBAL); ++ ++ if (qml_mode_ && !Isolate::Current()->global()->HasProperty(*(proxy->name()))) ++ var->set_is_qml_global(true); + } + } + } +diff --git a/src/scopes.h b/src/scopes.h +index a0e56a4..6dd3f65 100644 +--- a/src/scopes.h ++++ b/src/scopes.h +@@ -210,6 +210,11 @@ class Scope: public ZoneObject { + strict_mode_ = FLAG_strict_mode; + } + ++ // Enable qml mode for this scope ++ void EnableQmlMode() { ++ qml_mode_ = true; ++ } ++ + // --------------------------------------------------------------------------- + // Predicates. + +@@ -218,6 +223,7 @@ class Scope: public ZoneObject { + bool is_function_scope() const { return type_ == FUNCTION_SCOPE; } + bool is_global_scope() const { return type_ == GLOBAL_SCOPE; } + bool is_strict_mode() const { return strict_mode_; } ++ bool is_qml_mode() const { return qml_mode_; } + + // Information about which scopes calls eval. + bool calls_eval() const { return scope_calls_eval_; } +@@ -376,6 +382,7 @@ class Scope: public ZoneObject { + bool scope_contains_with_; // this scope contains a 'with' statement + bool scope_calls_eval_; // this scope contains an 'eval' call + bool strict_mode_; // this scope is a strict mode scope ++ bool qml_mode_; // this scope is a qml mode scope + + // Computed via PropagateScopeInfo. + bool outer_scope_calls_eval_; +diff --git a/src/variables.cc b/src/variables.cc +index 0502722..190baf6 100644 +--- a/src/variables.cc ++++ b/src/variables.cc +@@ -99,7 +99,8 @@ Variable::Variable(Scope* scope, + rewrite_(NULL), + is_valid_LHS_(is_valid_LHS), + is_accessed_from_inner_scope_(false), +- is_used_(false) { ++ is_used_(false), ++ is_qml_global_(false) { + // names must be canonicalized for fast equality checks + ASSERT(name->IsSymbol()); + } +diff --git a/src/variables.h b/src/variables.h +index b1ff0db..0b31d1a 100644 +--- a/src/variables.h ++++ b/src/variables.h +@@ -141,6 +141,8 @@ class Variable: public ZoneObject { + Expression* rewrite() const { return rewrite_; } + void set_rewrite(Expression* expr) { rewrite_ = expr; } + ++ bool is_qml_global() const { return is_qml_global_; } ++ void set_is_qml_global(bool is_qml_global) { is_qml_global_ = is_qml_global; } + private: + Scope* scope_; + Handle<String> name_; +@@ -159,6 +161,9 @@ class Variable: public ZoneObject { + // Usage info. + bool is_accessed_from_inner_scope_; // set by variable resolver + bool is_used_; ++ ++ // QML info ++ bool is_qml_global_; + }; + + +diff --git a/src/x64/code-stubs-x64.cc b/src/x64/code-stubs-x64.cc +index c365385..d923494 100644 +--- a/src/x64/code-stubs-x64.cc ++++ b/src/x64/code-stubs-x64.cc +@@ -140,6 +140,10 @@ void FastNewContextStub::Generate(MacroAssembler* masm) { + __ movq(rbx, Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX))); + __ movq(Operand(rax, Context::SlotOffset(Context::GLOBAL_INDEX)), rbx); + ++ // Copy the qml global object from the surrounding context. ++ __ movq(rbx, Operand(rsi, Context::SlotOffset(Context::QML_GLOBAL_INDEX))); ++ __ movq(Operand(rax, Context::SlotOffset(Context::QML_GLOBAL_INDEX)), rbx); ++ + // Initialize the rest of the slots to undefined. + __ LoadRoot(rbx, Heap::kUndefinedValueRootIndex); + for (int i = Context::MIN_CONTEXT_SLOTS; i < length; i++) { +diff --git a/src/x64/full-codegen-x64.cc b/src/x64/full-codegen-x64.cc +index 97168cd..c45cdb6 100644 +--- a/src/x64/full-codegen-x64.cc ++++ b/src/x64/full-codegen-x64.cc +@@ -141,12 +141,13 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { + + // Possibly allocate a local context. + int heap_slots = scope()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS; +- if (heap_slots > 0) { ++ if (heap_slots > 0 || ++ (scope()->is_qml_mode() && scope()->is_global_scope())) { + Comment cmnt(masm_, "[ Allocate local context"); + // Argument to NewContext is the function, which is still in rdi. + __ push(rdi); + if (heap_slots <= FastNewContextStub::kMaximumSlots) { +- FastNewContextStub stub(heap_slots); ++ FastNewContextStub stub((heap_slots < 0)?0:heap_slots); + __ CallStub(&stub); + } else { + __ CallRuntime(Runtime::kNewContext, 1); +@@ -1119,10 +1120,10 @@ void FullCodeGenerator::EmitLoadGlobalSlotCheckExtensions( + + // All extension objects were empty and it is safe to use a global + // load IC call. +- __ movq(rax, GlobalObjectOperand()); ++ __ movq(rax, slot->var()->is_qml_global() ? QmlGlobalObjectOperand() : GlobalObjectOperand()); + __ Move(rcx, slot->var()->name()); + Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize(); +- RelocInfo::Mode mode = (typeof_state == INSIDE_TYPEOF) ++ RelocInfo::Mode mode = (typeof_state == INSIDE_TYPEOF || slot->var()->is_qml_global()) + ? RelocInfo::CODE_TARGET + : RelocInfo::CODE_TARGET_CONTEXT; + EmitCallIC(ic, mode); +@@ -1227,9 +1228,9 @@ void FullCodeGenerator::EmitVariableLoad(Variable* var) { + // Use inline caching. Variable name is passed in rcx and the global + // object on the stack. + __ Move(rcx, var->name()); +- __ movq(rax, GlobalObjectOperand()); ++ __ movq(rax, var->is_qml_global() ? QmlGlobalObjectOperand() : GlobalObjectOperand()); + Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize(); +- EmitCallIC(ic, RelocInfo::CODE_TARGET_CONTEXT); ++ EmitCallIC(ic, var->is_qml_global() ? RelocInfo::CODE_TARGET : RelocInfo::CODE_TARGET_CONTEXT); + context()->Plug(rax); + + } else if (slot != NULL && slot->type() == Slot::LOOKUP) { +@@ -1806,11 +1807,11 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, + // assignment. Right-hand-side value is passed in rax, variable name in + // rcx, and the global object on the stack. + __ Move(rcx, var->name()); +- __ movq(rdx, GlobalObjectOperand()); ++ __ movq(rdx, var->is_qml_global() ? QmlGlobalObjectOperand() : GlobalObjectOperand()); + Handle<Code> ic = is_strict_mode() + ? isolate()->builtins()->StoreIC_Initialize_Strict() + : isolate()->builtins()->StoreIC_Initialize(); +- EmitCallIC(ic, RelocInfo::CODE_TARGET_CONTEXT); ++ EmitCallIC(ic, var->is_qml_global() ? RelocInfo::CODE_TARGET : RelocInfo::CODE_TARGET_CONTEXT); + + } else if (op == Token::INIT_CONST) { + // Like var declarations, const declarations are hoisted to function +@@ -2085,9 +2086,12 @@ void FullCodeGenerator::EmitResolvePossiblyDirectEval(ResolveEvalFlag flag, + // Push the strict mode flag. + __ Push(Smi::FromInt(strict_mode_flag())); + ++ // Push the qml mode flag ++ __ Push(Smi::FromInt(is_qml_mode())); ++ + __ CallRuntime(flag == SKIP_CONTEXT_LOOKUP + ? Runtime::kResolvePossiblyDirectEvalNoLookup +- : Runtime::kResolvePossiblyDirectEval, 4); ++ : Runtime::kResolvePossiblyDirectEval, 5); + } + + +@@ -2160,8 +2164,8 @@ void FullCodeGenerator::VisitCall(Call* expr) { + } else if (var != NULL && !var->is_this() && var->is_global()) { + // Call to a global variable. + // Push global object as receiver for the call IC lookup. +- __ push(GlobalObjectOperand()); +- EmitCallWithIC(expr, var->name(), RelocInfo::CODE_TARGET_CONTEXT); ++ __ push(var->is_qml_global() ? QmlGlobalObjectOperand() : GlobalObjectOperand()); ++ EmitCallWithIC(expr, var->name(), var->is_qml_global() ? RelocInfo::CODE_TARGET : RelocInfo::CODE_TARGET_CONTEXT); + } else if (var != NULL && var->AsSlot() != NULL && + var->AsSlot()->type() == Slot::LOOKUP) { + // Call to a lookup slot (dynamically introduced variable). +diff --git a/src/x64/lithium-codegen-x64.cc b/src/x64/lithium-codegen-x64.cc +index 202e7a2..45acbdf 100644 +--- a/src/x64/lithium-codegen-x64.cc ++++ b/src/x64/lithium-codegen-x64.cc +@@ -174,12 +174,13 @@ bool LCodeGen::GeneratePrologue() { + + // Possibly allocate a local context. + int heap_slots = scope()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS; +- if (heap_slots > 0) { ++ if (heap_slots > 0 || ++ (scope()->is_qml_mode() && scope()->is_global_scope())) { + Comment(";;; Allocate local context"); + // Argument to NewContext is the function, which is still in rdi. + __ push(rdi); + if (heap_slots <= FastNewContextStub::kMaximumSlots) { +- FastNewContextStub stub(heap_slots); ++ FastNewContextStub stub((heap_slots < 0)?0:heap_slots); + __ CallStub(&stub); + } else { + __ CallRuntime(Runtime::kNewContext, 1); +@@ -2540,7 +2541,7 @@ void LCodeGen::DoOuterContext(LOuterContext* instr) { + + void LCodeGen::DoGlobalObject(LGlobalObject* instr) { + Register result = ToRegister(instr->result()); +- __ movq(result, GlobalObjectOperand()); ++ __ movq(result, instr->qml_global()?QmlGlobalObjectOperand():GlobalObjectOperand()); + } + + +diff --git a/src/x64/lithium-x64.cc b/src/x64/lithium-x64.cc +index 07ca3a5..00feeac 100644 +--- a/src/x64/lithium-x64.cc ++++ b/src/x64/lithium-x64.cc +@@ -1194,7 +1194,7 @@ LInstruction* LChunkBuilder::DoOuterContext(HOuterContext* instr) { + + + LInstruction* LChunkBuilder::DoGlobalObject(HGlobalObject* instr) { +- return DefineAsRegister(new LGlobalObject); ++ return DefineAsRegister(new LGlobalObject(instr->qml_global())); + } + + +diff --git a/src/x64/lithium-x64.h b/src/x64/lithium-x64.h +index 15bb894..16f754c 100644 +--- a/src/x64/lithium-x64.h ++++ b/src/x64/lithium-x64.h +@@ -1365,7 +1365,13 @@ class LOuterContext: public LTemplateInstruction<1, 1, 0> { + + class LGlobalObject: public LTemplateInstruction<1, 0, 0> { + public: ++ explicit LGlobalObject(bool qml_global) : qml_global_(qml_global) {} ++ + DECLARE_CONCRETE_INSTRUCTION(GlobalObject, "global-object") ++ ++ bool qml_global() { return qml_global_; } ++ private: ++ bool qml_global_; + }; + + +diff --git a/src/x64/macro-assembler-x64.h b/src/x64/macro-assembler-x64.h +index 4c17720..aa284ed 100644 +--- a/src/x64/macro-assembler-x64.h ++++ b/src/x64/macro-assembler-x64.h +@@ -1233,6 +1233,11 @@ static inline Operand GlobalObjectOperand() { + } + + ++static inline Operand QmlGlobalObjectOperand() { ++ return ContextOperand(rsi, Context::QML_GLOBAL_INDEX); ++} ++ ++ + // Provides access to exit frame stack space (not GCed). + static inline Operand StackSpaceOperand(int index) { + #ifdef _WIN64 +-- +1.7.2.3 + diff --git a/src/v8/0006-Allow-access-to-the-calling-script-data.patch b/src/v8/0006-Allow-access-to-the-calling-script-data.patch new file mode 100644 index 0000000000..d4acdf7a42 --- /dev/null +++ b/src/v8/0006-Allow-access-to-the-calling-script-data.patch @@ -0,0 +1,48 @@ +From f890f0d1a1e5bd62711815489c87755a4f382436 Mon Sep 17 00:00:00 2001 +From: Aaron Kennedy <aaron.kennedy@nokia.com> +Date: Wed, 25 May 2011 10:36:13 +1000 +Subject: [PATCH 06/13] Allow access to the calling script data + +--- + include/v8.h | 1 + + src/api.cc | 12 ++++++++++++ + 2 files changed, 13 insertions(+), 0 deletions(-) + +diff --git a/include/v8.h b/include/v8.h +index a858eae..9aba4a8 100644 +--- a/include/v8.h ++++ b/include/v8.h +@@ -3336,6 +3336,7 @@ class V8EXPORT Context { + */ + static Local<Context> GetCalling(); + static Local<Object> GetCallingQmlGlobal(); ++ static Local<Value> GetCallingScriptData(); + + /** + * Sets the security token for the context. To access an object in +diff --git a/src/api.cc b/src/api.cc +index 39767f4..ff74efb 100644 +--- a/src/api.cc ++++ b/src/api.cc +@@ -3976,6 +3976,18 @@ v8::Local<v8::Object> Context::GetCallingQmlGlobal() { + } + } + ++v8::Local<v8::Value> Context::GetCallingScriptData() ++{ ++ i::Isolate* isolate = i::Isolate::Current(); ++ if (IsDeadCheck(isolate, "v8::Context::GetCallingScriptData()")) { ++ return Local<Object>(); ++ } ++ ++ i::JavaScriptFrameIterator it; ++ if (it.done()) return Local<Object>(); ++ i::Handle<i::Script> script(i::Script::cast(i::JSFunction::cast(it.frame()->function())->shared()->script())); ++ return Utils::ToLocal(i::Handle<i::Object>(script->data())); ++} + + v8::Local<v8::Object> Context::Global() { + if (IsDeadCheck(i::Isolate::Current(), "v8::Context::Global()")) { +-- +1.7.2.3 + diff --git a/src/v8/0007-Fix-warnings.patch b/src/v8/0007-Fix-warnings.patch new file mode 100644 index 0000000000..26141c8ae8 --- /dev/null +++ b/src/v8/0007-Fix-warnings.patch @@ -0,0 +1,46 @@ +From dac5d9db84cf20564621c679937ca7b9c6a8e880 Mon Sep 17 00:00:00 2001 +From: Aaron Kennedy <aaron.kennedy@nokia.com> +Date: Fri, 27 May 2011 13:04:15 +1000 +Subject: [PATCH 07/13] Fix warnings + +--- + include/v8.h | 16 ++++++++-------- + 1 files changed, 8 insertions(+), 8 deletions(-) + +diff --git a/include/v8.h b/include/v8.h +index 9aba4a8..8891dab 100644 +--- a/include/v8.h ++++ b/include/v8.h +@@ -2415,7 +2415,7 @@ class V8EXPORT Extension { // NOLINT + const char** deps = 0); + virtual ~Extension() { } + virtual v8::Handle<v8::FunctionTemplate> +- GetNativeFunction(v8::Handle<v8::String> name) { ++ GetNativeFunction(v8::Handle<v8::String>) { + return v8::Handle<v8::FunctionTemplate>(); + } + +@@ -3721,13 +3721,13 @@ class Internals { + return *reinterpret_cast<T*>(addr); + } + +- static inline bool CanCastToHeapObject(void* o) { return false; } +- static inline bool CanCastToHeapObject(Context* o) { return true; } +- static inline bool CanCastToHeapObject(String* o) { return true; } +- static inline bool CanCastToHeapObject(Object* o) { return true; } +- static inline bool CanCastToHeapObject(Message* o) { return true; } +- static inline bool CanCastToHeapObject(StackTrace* o) { return true; } +- static inline bool CanCastToHeapObject(StackFrame* o) { return true; } ++ static inline bool CanCastToHeapObject(void*) { return false; } ++ static inline bool CanCastToHeapObject(Context*) { return true; } ++ static inline bool CanCastToHeapObject(String*) { return true; } ++ static inline bool CanCastToHeapObject(Object*) { return true; } ++ static inline bool CanCastToHeapObject(Message*) { return true; } ++ static inline bool CanCastToHeapObject(StackTrace*) { return true; } ++ static inline bool CanCastToHeapObject(StackFrame*) { return true; } + }; + + } // namespace internal +-- +1.7.2.3 + diff --git a/src/v8/0008-Add-custom-object-compare-callback.patch b/src/v8/0008-Add-custom-object-compare-callback.patch new file mode 100644 index 0000000000..d7ef5c434d --- /dev/null +++ b/src/v8/0008-Add-custom-object-compare-callback.patch @@ -0,0 +1,489 @@ +From bec11b8b7f89d135e7d9a823ac4fe98c70d017cf Mon Sep 17 00:00:00 2001 +From: Aaron Kennedy <aaron.kennedy@nokia.com> +Date: Mon, 27 Jun 2011 14:57:28 +1000 +Subject: [PATCH 08/13] Add custom object compare callback + +A global custom object comparison callback can be set with: + V8::SetUserObjectComparisonCallbackFunction() +When two JSObjects are compared (== or !=), if either one has +the MarkAsUseUserObjectComparison() bit set, the custom comparison +callback is invoked to do the actual comparison. + +This is useful when you have "value" objects that you want to +compare as equal, even though they are actually different JS object +instances. +--- + include/v8.h | 13 +++++++++++++ + src/api.cc | 19 +++++++++++++++++++ + src/arm/code-stubs-arm.cc | 42 ++++++++++++++++++++++++++++++++++++++++-- + src/factory.cc | 8 ++++++++ + src/ia32/code-stubs-ia32.cc | 40 ++++++++++++++++++++++++++++++++++++++++ + src/isolate.h | 8 ++++++++ + src/objects-inl.h | 15 +++++++++++++++ + src/objects.h | 10 +++++++++- + src/runtime.cc | 23 +++++++++++++++++++++++ + src/runtime.h | 1 + + src/top.cc | 5 +++++ + src/x64/code-stubs-x64.cc | 37 +++++++++++++++++++++++++++++++++++++ + 12 files changed, 218 insertions(+), 3 deletions(-) + +diff --git a/include/v8.h b/include/v8.h +index 8891dab..d5d6972 100644 +--- a/include/v8.h ++++ b/include/v8.h +@@ -2365,6 +2365,12 @@ class V8EXPORT ObjectTemplate : public Template { + bool HasExternalResource(); + void SetHasExternalResource(bool value); + ++ /** ++ * Mark object instances of the template as using the user object ++ * comparison callback. ++ */ ++ void MarkAsUseUserObjectComparison(); ++ + private: + ObjectTemplate(); + static Local<ObjectTemplate> New(Handle<FunctionTemplate> constructor); +@@ -2565,6 +2571,10 @@ typedef void (*FailedAccessCheckCallback)(Local<Object> target, + AccessType type, + Local<Value> data); + ++// --- U s e r O b j e c t C o m p a r i s o n C a l l b a c k --- ++typedef bool (*UserObjectComparisonCallback)(Local<Object> lhs, ++ Local<Object> rhs); ++ + // --- G a r b a g e C o l l e c t i o n C a l l b a c k s + + /** +@@ -2815,6 +2825,9 @@ class V8EXPORT V8 { + /** Callback function for reporting failed access checks.*/ + static void SetFailedAccessCheckCallbackFunction(FailedAccessCheckCallback); + ++ /** Callback for user object comparisons */ ++ static void SetUserObjectComparisonCallbackFunction(UserObjectComparisonCallback); ++ + /** + * Enables the host application to receive a notification before a + * garbage collection. Allocations are not allowed in the +diff --git a/src/api.cc b/src/api.cc +index ff74efb..2436031 100644 +--- a/src/api.cc ++++ b/src/api.cc +@@ -1321,6 +1321,16 @@ void ObjectTemplate::SetHasExternalResource(bool value) + } + } + ++void ObjectTemplate::MarkAsUseUserObjectComparison() ++{ ++ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate(); ++ if (IsDeadCheck(isolate, "v8::ObjectTemplate::MarkAsUseUserObjectComparison()")) { ++ return; ++ } ++ ENTER_V8(isolate); ++ EnsureConstructor(this); ++ Utils::OpenHandle(this)->set_use_user_object_comparison(i::Smi::FromInt(1)); ++} + + // --- S c r i p t D a t a --- + +@@ -4632,6 +4642,15 @@ void V8::SetFailedAccessCheckCallbackFunction( + isolate->SetFailedAccessCheckCallback(callback); + } + ++void V8::SetUserObjectComparisonCallbackFunction( ++ UserObjectComparisonCallback callback) { ++ i::Isolate* isolate = i::Isolate::Current(); ++ if (IsDeadCheck(isolate, "v8::V8::SetUserObjectComparisonCallbackFunction()")) { ++ return; ++ } ++ isolate->SetUserObjectComparisonCallback(callback); ++} ++ + void V8::AddObjectGroup(Persistent<Value>* objects, + size_t length, + RetainedObjectInfo* info) { +diff --git a/src/arm/code-stubs-arm.cc b/src/arm/code-stubs-arm.cc +index a2626bf..749c9be 100644 +--- a/src/arm/code-stubs-arm.cc ++++ b/src/arm/code-stubs-arm.cc +@@ -1563,6 +1563,36 @@ void CompareStub::Generate(MacroAssembler* masm) { + // NOTICE! This code is only reached after a smi-fast-case check, so + // it is certain that at least one operand isn't a smi. + ++ { ++ Label not_user_equal, user_equal; ++ __ and_(r2, r1, Operand(r0)); ++ __ tst(r2, Operand(kSmiTagMask)); ++ __ b(eq, ¬_user_equal); ++ ++ __ CompareObjectType(r0, r2, r4, JS_OBJECT_TYPE); ++ __ b(ne, ¬_user_equal); ++ ++ __ CompareObjectType(r1, r3, r4, JS_OBJECT_TYPE); ++ __ b(ne, ¬_user_equal); ++ ++ __ ldrb(r2, FieldMemOperand(r2, Map::kBitField3Offset)); ++ __ and_(r2, r2, Operand(1 << Map::kUseUserObjectComparison)); ++ __ cmp(r2, Operand(1 << Map::kUseUserObjectComparison)); ++ __ b(eq, &user_equal); ++ ++ __ ldrb(r3, FieldMemOperand(r3, Map::kBitField3Offset)); ++ __ and_(r3, r3, Operand(1 << Map::kUseUserObjectComparison)); ++ __ cmp(r3, Operand(1 << Map::kUseUserObjectComparison)); ++ __ b(ne, ¬_user_equal); ++ ++ __ bind(&user_equal); ++ ++ __ Push(r0, r1); ++ __ TailCallRuntime(Runtime::kUserObjectEquals, 2, 1); ++ ++ __ bind(¬_user_equal); ++ } ++ + // Handle the case where the objects are identical. Either returns the answer + // or goes to slow. Only falls through if the objects were not identical. + EmitIdenticalObjectComparison(masm, &slow, cc_, never_nan_nan_); +@@ -5802,10 +5832,18 @@ void ICCompareStub::GenerateObjects(MacroAssembler* masm) { + __ tst(r2, Operand(kSmiTagMask)); + __ b(eq, &miss); + +- __ CompareObjectType(r0, r2, r2, JS_OBJECT_TYPE); ++ __ CompareObjectType(r0, r2, r3, JS_OBJECT_TYPE); + __ b(ne, &miss); +- __ CompareObjectType(r1, r2, r2, JS_OBJECT_TYPE); ++ __ ldrb(r2, FieldMemOperand(r2, Map::kBitField3Offset)); ++ __ and_(r2, r2, Operand(1 << Map::kUseUserObjectComparison)); ++ __ cmp(r2, Operand(1 << Map::kUseUserObjectComparison)); ++ __ b(eq, &miss); ++ __ CompareObjectType(r1, r2, r3, JS_OBJECT_TYPE); + __ b(ne, &miss); ++ __ ldrb(r2, FieldMemOperand(r2, Map::kBitField3Offset)); ++ __ and_(r2, r2, Operand(1 << Map::kUseUserObjectComparison)); ++ __ cmp(r2, Operand(1 << Map::kUseUserObjectComparison)); ++ __ b(eq, &miss); + + ASSERT(GetCondition() == eq); + __ sub(r0, r0, Operand(r1)); +diff --git a/src/factory.cc b/src/factory.cc +index d530a75..6f8c7de 100644 +--- a/src/factory.cc ++++ b/src/factory.cc +@@ -998,6 +998,7 @@ Handle<JSFunction> Factory::CreateApiFunction( + + int internal_field_count = 0; + bool has_external_resource = false; ++ bool use_user_object_comparison = false; + + if (!obj->instance_template()->IsUndefined()) { + Handle<ObjectTemplateInfo> instance_template = +@@ -1007,6 +1008,8 @@ Handle<JSFunction> Factory::CreateApiFunction( + Smi::cast(instance_template->internal_field_count())->value(); + has_external_resource = + !instance_template->has_external_resource()->IsUndefined(); ++ use_user_object_comparison = ++ !instance_template->use_user_object_comparison()->IsUndefined(); + } + + int instance_size = kPointerSize * internal_field_count; +@@ -1051,6 +1054,11 @@ Handle<JSFunction> Factory::CreateApiFunction( + map->set_has_external_resource(true); + } + ++ // Mark as using user object comparison if needed ++ if (use_user_object_comparison) { ++ map->set_use_user_object_comparison(true); ++ } ++ + // Mark as undetectable if needed. + if (obj->undetectable()) { + map->set_is_undetectable(); +diff --git a/src/ia32/code-stubs-ia32.cc b/src/ia32/code-stubs-ia32.cc +index afa599e..0964ab9 100644 +--- a/src/ia32/code-stubs-ia32.cc ++++ b/src/ia32/code-stubs-ia32.cc +@@ -3447,6 +3447,40 @@ void CompareStub::Generate(MacroAssembler* masm) { + __ Assert(not_zero, "Unexpected smi operands."); + } + ++ { ++ NearLabel not_user_equal, user_equal; ++ __ test(eax, Immediate(kSmiTagMask)); ++ __ j(zero, ¬_user_equal); ++ __ test(edx, Immediate(kSmiTagMask)); ++ __ j(zero, ¬_user_equal); ++ ++ __ CmpObjectType(eax, JS_OBJECT_TYPE, ebx); ++ __ j(not_equal, ¬_user_equal); ++ ++ __ CmpObjectType(edx, JS_OBJECT_TYPE, ecx); ++ __ j(not_equal, ¬_user_equal); ++ ++ __ test_b(FieldOperand(ebx, Map::kBitField3Offset), ++ 1 << Map::kUseUserObjectComparison); ++ __ j(not_zero, &user_equal); ++ __ test_b(FieldOperand(ecx, Map::kBitField3Offset), ++ 1 << Map::kUseUserObjectComparison); ++ __ j(not_zero, &user_equal); ++ ++ __ jmp(¬_user_equal); ++ ++ __ bind(&user_equal); ++ ++ __ pop(ebx); // Return address. ++ __ push(eax); ++ __ push(edx); ++ __ push(ebx); ++ __ TailCallRuntime(Runtime::kUserObjectEquals, 2, 1); ++ ++ __ bind(¬_user_equal); ++ } ++ ++ + // NOTICE! This code is only reached after a smi-fast-case check, so + // it is certain that at least one operand isn't a smi. + +@@ -5592,8 +5626,14 @@ void ICCompareStub::GenerateObjects(MacroAssembler* masm) { + + __ CmpObjectType(eax, JS_OBJECT_TYPE, ecx); + __ j(not_equal, &miss, not_taken); ++ __ test_b(FieldOperand(ecx, Map::kBitField3Offset), ++ 1 << Map::kUseUserObjectComparison); ++ __ j(not_zero, &miss); + __ CmpObjectType(edx, JS_OBJECT_TYPE, ecx); + __ j(not_equal, &miss, not_taken); ++ __ test_b(FieldOperand(ecx, Map::kBitField3Offset), ++ 1 << Map::kUseUserObjectComparison); ++ __ j(not_zero, &miss); + + ASSERT(GetCondition() == equal); + __ sub(eax, Operand(edx)); +diff --git a/src/isolate.h b/src/isolate.h +index 35ffcb4..8130397 100644 +--- a/src/isolate.h ++++ b/src/isolate.h +@@ -267,6 +267,9 @@ class ThreadLocalTop BASE_EMBEDDED { + // Call back function to report unsafe JS accesses. + v8::FailedAccessCheckCallback failed_access_check_callback_; + ++ // Call back function for user object comparisons ++ v8::UserObjectComparisonCallback user_object_comparison_callback_; ++ + private: + void InitializeInternal(); + +@@ -699,6 +702,11 @@ class Isolate { + void SetFailedAccessCheckCallback(v8::FailedAccessCheckCallback callback); + void ReportFailedAccessCheck(JSObject* receiver, v8::AccessType type); + ++ void SetUserObjectComparisonCallback(v8::UserObjectComparisonCallback callback); ++ inline v8::UserObjectComparisonCallback UserObjectComparisonCallback() { ++ return thread_local_top()->user_object_comparison_callback_; ++ } ++ + // Exception throwing support. The caller should use the result + // of Throw() as its return value. + Failure* Throw(Object* exception, MessageLocation* location = NULL); +diff --git a/src/objects-inl.h b/src/objects-inl.h +index 1c7f83e..1765441 100644 +--- a/src/objects-inl.h ++++ b/src/objects-inl.h +@@ -2552,6 +2552,19 @@ bool Map::has_external_resource() + } + + ++void Map::set_use_user_object_comparison(bool value) { ++ if (value) { ++ set_bit_field3(bit_field3() | (1 << kUseUserObjectComparison)); ++ } else { ++ set_bit_field3(bit_field3() & ~(1 << kUseUserObjectComparison)); ++ } ++} ++ ++bool Map::use_user_object_comparison() { ++ return ((1 << kUseUserObjectComparison) & bit_field3()) != 0; ++} ++ ++ + void Map::set_named_interceptor_is_fallback(bool value) + { + if (value) { +@@ -3050,6 +3063,8 @@ ACCESSORS(ObjectTemplateInfo, internal_field_count, Object, + kInternalFieldCountOffset) + ACCESSORS(ObjectTemplateInfo, has_external_resource, Object, + kHasExternalResourceOffset) ++ACCESSORS(ObjectTemplateInfo, use_user_object_comparison, Object, ++ kUseUserObjectComparisonOffset) + + ACCESSORS(SignatureInfo, receiver, Object, kReceiverOffset) + ACCESSORS(SignatureInfo, args, Object, kArgsOffset) +diff --git a/src/objects.h b/src/objects.h +index edbc47a..e75e9f1 100644 +--- a/src/objects.h ++++ b/src/objects.h +@@ -3724,6 +3724,11 @@ class Map: public HeapObject { + inline void set_has_external_resource(bool value); + inline bool has_external_resource(); + ++ ++ // Tells whether the user object comparison callback should be used for ++ // comparisons involving this object ++ inline void set_use_user_object_comparison(bool value); ++ inline bool use_user_object_comparison(); + + // Whether the named interceptor is a fallback interceptor or not + inline void set_named_interceptor_is_fallback(bool value); +@@ -3922,6 +3927,7 @@ class Map: public HeapObject { + // Bit positions for bit field 3 + static const int kNamedInterceptorIsFallback = 0; + static const int kHasExternalResource = 1; ++ static const int kUseUserObjectComparison = 2; + + // Layout of the default cache. It holds alternating name and code objects. + static const int kCodeCacheEntrySize = 2; +@@ -6442,6 +6448,7 @@ class ObjectTemplateInfo: public TemplateInfo { + DECL_ACCESSORS(constructor, Object) + DECL_ACCESSORS(internal_field_count, Object) + DECL_ACCESSORS(has_external_resource, Object) ++ DECL_ACCESSORS(use_user_object_comparison, Object) + + static inline ObjectTemplateInfo* cast(Object* obj); + +@@ -6459,7 +6466,8 @@ class ObjectTemplateInfo: public TemplateInfo { + static const int kInternalFieldCountOffset = + kConstructorOffset + kPointerSize; + static const int kHasExternalResourceOffset = kInternalFieldCountOffset + kPointerSize; +- static const int kSize = kHasExternalResourceOffset + kPointerSize; ++ static const int kUseUserObjectComparisonOffset = kHasExternalResourceOffset + kPointerSize; ++ static const int kSize = kUseUserObjectComparisonOffset + kPointerSize; + }; + + +diff --git a/src/runtime.cc b/src/runtime.cc +index 827d954..d552ddb 100644 +--- a/src/runtime.cc ++++ b/src/runtime.cc +@@ -6279,6 +6279,29 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringEquals) { + } + + ++RUNTIME_FUNCTION(MaybeObject*, Runtime_UserObjectEquals) { ++ NoHandleAllocation ha; ++ ASSERT(args.length() == 2); ++ ++ CONVERT_CHECKED(JSObject, lhs, args[1]); ++ CONVERT_CHECKED(JSObject, rhs, args[0]); ++ ++ bool result; ++ ++ v8::UserObjectComparisonCallback callback = isolate->UserObjectComparisonCallback(); ++ if (callback) { ++ HandleScope scope(isolate); ++ Handle<JSObject> lhs_handle(lhs); ++ Handle<JSObject> rhs_handle(rhs); ++ result = callback(v8::Utils::ToLocal(lhs_handle), v8::Utils::ToLocal(rhs_handle)); ++ } else { ++ result = (lhs == rhs); ++ } ++ ++ return Smi::FromInt(result?0:1); ++} ++ ++ + RUNTIME_FUNCTION(MaybeObject*, Runtime_NumberCompare) { + NoHandleAllocation ha; + ASSERT(args.length() == 3); +diff --git a/src/runtime.h b/src/runtime.h +index 5e97173..0d754f9 100644 +--- a/src/runtime.h ++++ b/src/runtime.h +@@ -146,6 +146,7 @@ namespace internal { + /* Comparisons */ \ + F(NumberEquals, 2, 1) \ + F(StringEquals, 2, 1) \ ++ F(UserObjectEquals, 2, 1) \ + \ + F(NumberCompare, 3, 1) \ + F(SmiLexicographicCompare, 2, 1) \ +diff --git a/src/top.cc b/src/top.cc +index e078ee9..c345383 100644 +--- a/src/top.cc ++++ b/src/top.cc +@@ -68,6 +68,7 @@ void ThreadLocalTop::InitializeInternal() { + thread_id_ = ThreadId::Invalid(); + external_caught_exception_ = false; + failed_access_check_callback_ = NULL; ++ user_object_comparison_callback_ = NULL; + save_context_ = NULL; + catcher_ = NULL; + } +@@ -387,6 +388,10 @@ void Isolate::SetFailedAccessCheckCallback( + thread_local_top()->failed_access_check_callback_ = callback; + } + ++void Isolate::SetUserObjectComparisonCallback( ++ v8::UserObjectComparisonCallback callback) { ++ thread_local_top()->user_object_comparison_callback_ = callback; ++} + + void Isolate::ReportFailedAccessCheck(JSObject* receiver, v8::AccessType type) { + if (!thread_local_top()->failed_access_check_callback_) return; +diff --git a/src/x64/code-stubs-x64.cc b/src/x64/code-stubs-x64.cc +index d923494..10b9b56 100644 +--- a/src/x64/code-stubs-x64.cc ++++ b/src/x64/code-stubs-x64.cc +@@ -2443,6 +2443,37 @@ void CompareStub::Generate(MacroAssembler* masm) { + __ bind(&ok); + } + ++ { ++ NearLabel not_user_equal, user_equal; ++ __ JumpIfSmi(rax, ¬_user_equal); ++ __ JumpIfSmi(rdx, ¬_user_equal); ++ ++ __ CmpObjectType(rax, JS_OBJECT_TYPE, rbx); ++ __ j(not_equal, ¬_user_equal); ++ ++ __ CmpObjectType(rdx, JS_OBJECT_TYPE, rcx); ++ __ j(not_equal, ¬_user_equal); ++ ++ __ testb(FieldOperand(rbx, Map::kBitField3Offset), ++ Immediate(1 << Map::kUseUserObjectComparison)); ++ __ j(not_zero, &user_equal); ++ __ testb(FieldOperand(rcx, Map::kBitField3Offset), ++ Immediate(1 << Map::kUseUserObjectComparison)); ++ __ j(not_zero, &user_equal); ++ ++ __ jmp(¬_user_equal); ++ ++ __ bind(&user_equal); ++ ++ __ pop(rbx); // Return address. ++ __ push(rax); ++ __ push(rdx); ++ __ push(rbx); ++ __ TailCallRuntime(Runtime::kUserObjectEquals, 2, 1); ++ ++ __ bind(¬_user_equal); ++ } ++ + // The compare stub returns a positive, negative, or zero 64-bit integer + // value in rax, corresponding to result of comparing the two inputs. + // NOTICE! This code is only reached after a smi-fast-case check, so +@@ -4471,8 +4502,14 @@ void ICCompareStub::GenerateObjects(MacroAssembler* masm) { + + __ CmpObjectType(rax, JS_OBJECT_TYPE, rcx); + __ j(not_equal, &miss, not_taken); ++ __ testb(FieldOperand(rcx, Map::kBitField3Offset), ++ Immediate(1 << Map::kUseUserObjectComparison)); ++ __ j(not_zero, &miss); + __ CmpObjectType(rdx, JS_OBJECT_TYPE, rcx); + __ j(not_equal, &miss, not_taken); ++ __ testb(FieldOperand(rcx, Map::kBitField3Offset), ++ Immediate(1 << Map::kUseUserObjectComparison)); ++ __ j(not_zero, &miss); + + ASSERT(GetCondition() == equal); + __ subq(rax, rdx); +-- +1.7.2.3 + diff --git a/src/v8/0009-Add-CallAsFunction-method-to-the-Object-class-in-the.patch b/src/v8/0009-Add-CallAsFunction-method-to-the-Object-class-in-the.patch new file mode 100644 index 0000000000..d197ac9e48 --- /dev/null +++ b/src/v8/0009-Add-CallAsFunction-method-to-the-Object-class-in-the.patch @@ -0,0 +1,286 @@ +From 4183b973ed3bd603784c798dfa63ba48f6b68003 Mon Sep 17 00:00:00 2001 +From: ager@chromium.org <ager@chromium.org> +Date: Wed, 4 May 2011 13:03:08 +0000 +Subject: [PATCH 09/13] Add CallAsFunction method to the Object class in the API + +Patch by Peter Varga. + +BUG=v8:1336 +TEST=cctest/test-api/CallAsFunction + +Review URL: http://codereview.chromium.org/6883045 + +git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@7781 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 +--- + include/v8.h | 8 +++ + src/api.cc | 31 +++++++++++ + src/execution.cc | 24 ++++++++ + src/execution.h | 2 + + test/cctest/test-api.cc | 135 ++++++++++++++++++++++++++++++++++------------- + 5 files changed, 163 insertions(+), 37 deletions(-) + +diff --git a/include/v8.h b/include/v8.h +index d5d6972..8a8e1cd 100644 +--- a/include/v8.h ++++ b/include/v8.h +@@ -1757,6 +1757,14 @@ class Object : public Value { + V8EXPORT ExternalArrayType GetIndexedPropertiesExternalArrayDataType(); + V8EXPORT int GetIndexedPropertiesExternalArrayDataLength(); + ++ /** ++ * Call an Object as a function if a callback is set by the ++ * ObjectTemplate::SetCallAsFunctionHandler method. ++ */ ++ V8EXPORT Local<Value> CallAsFunction(Handle<Object> recv, ++ int argc, ++ Handle<Value> argv[]); ++ + V8EXPORT static Local<Object> New(); + static inline Object* Cast(Value* obj); + private: +diff --git a/src/api.cc b/src/api.cc +index 2436031..e412e51 100644 +--- a/src/api.cc ++++ b/src/api.cc +@@ -3259,6 +3259,37 @@ int v8::Object::GetIndexedPropertiesExternalArrayDataLength() { + } + + ++Local<v8::Value> Object::CallAsFunction(v8::Handle<v8::Object> recv, int argc, ++ v8::Handle<v8::Value> argv[]) { ++ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate(); ++ ON_BAILOUT(isolate, "v8::Object::CallAsFunction()", ++ return Local<v8::Value>()); ++ LOG_API(isolate, "Object::CallAsFunction"); ++ ENTER_V8(isolate); ++ HandleScope scope; ++ i::Handle<i::JSObject> obj = Utils::OpenHandle(this); ++ i::Handle<i::Object> recv_obj = Utils::OpenHandle(*recv); ++ STATIC_ASSERT(sizeof(v8::Handle<v8::Value>) == sizeof(i::Object**)); ++ i::Object*** args = reinterpret_cast<i::Object***>(argv); ++ i::Handle<i::JSFunction> fun = i::Handle<i::JSFunction>(); ++ if (obj->IsJSFunction()) { ++ fun = i::Handle<i::JSFunction>::cast(obj); ++ } else { ++ EXCEPTION_PREAMBLE(isolate); ++ i::Handle<i::Object> delegate = ++ i::Execution::TryGetFunctionDelegate(obj, &has_pending_exception); ++ EXCEPTION_BAILOUT_CHECK(isolate, Local<Value>()); ++ fun = i::Handle<i::JSFunction>::cast(delegate); ++ recv_obj = obj; ++ } ++ EXCEPTION_PREAMBLE(isolate); ++ i::Handle<i::Object> returned = ++ i::Execution::Call(fun, recv_obj, argc, args, &has_pending_exception); ++ EXCEPTION_BAILOUT_CHECK(isolate, Local<Value>()); ++ return scope.Close(Utils::ToLocal(returned)); ++} ++ ++ + Local<v8::Object> Function::NewInstance() const { + return NewInstance(0, NULL); + } +diff --git a/src/execution.cc b/src/execution.cc +index 1632076..894d741 100644 +--- a/src/execution.cc ++++ b/src/execution.cc +@@ -254,6 +254,30 @@ Handle<Object> Execution::GetFunctionDelegate(Handle<Object> object) { + } + + ++Handle<Object> Execution::TryGetFunctionDelegate(Handle<Object> object, ++ bool* has_pending_exception) { ++ ASSERT(!object->IsJSFunction()); ++ Isolate* isolate = Isolate::Current(); ++ ++ // Objects created through the API can have an instance-call handler ++ // that should be used when calling the object as a function. ++ if (object->IsHeapObject() && ++ HeapObject::cast(*object)->map()->has_instance_call_handler()) { ++ return Handle<JSFunction>( ++ isolate->global_context()->call_as_function_delegate()); ++ } ++ ++ // If the Object doesn't have an instance-call handler we should ++ // throw a non-callable exception. ++ i::Handle<i::Object> error_obj = isolate->factory()->NewTypeError( ++ "called_non_callable", i::HandleVector<i::Object>(&object, 1)); ++ isolate->Throw(*error_obj); ++ *has_pending_exception = true; ++ ++ return isolate->factory()->undefined_value(); ++} ++ ++ + Handle<Object> Execution::GetConstructorDelegate(Handle<Object> object) { + ASSERT(!object->IsJSFunction()); + Isolate* isolate = Isolate::Current(); +diff --git a/src/execution.h b/src/execution.h +index a476eb4..0a0be51 100644 +--- a/src/execution.h ++++ b/src/execution.h +@@ -144,6 +144,8 @@ class Execution : public AllStatic { + // Get a function delegate (or undefined) for the given non-function + // object. Used for support calling objects as functions. + static Handle<Object> GetFunctionDelegate(Handle<Object> object); ++ static Handle<Object> TryGetFunctionDelegate(Handle<Object> object, ++ bool* has_pending_exception); + + // Get a function delegate (or undefined) for the given non-function + // object. Used for support calling objects as constructors. +diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc +index d7621d1..693d51e 100644 +--- a/test/cctest/test-api.cc ++++ b/test/cctest/test-api.cc +@@ -6962,50 +6962,111 @@ THREADED_TEST(CallAsFunction) { + v8::HandleScope scope; + LocalContext context; + +- Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); +- Local<ObjectTemplate> instance_template = t->InstanceTemplate(); +- instance_template->SetCallAsFunctionHandler(call_as_function); +- Local<v8::Object> instance = t->GetFunction()->NewInstance(); +- context->Global()->Set(v8_str("obj"), instance); +- v8::TryCatch try_catch; +- Local<Value> value; +- CHECK(!try_catch.HasCaught()); ++ { Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); ++ Local<ObjectTemplate> instance_template = t->InstanceTemplate(); ++ instance_template->SetCallAsFunctionHandler(call_as_function); ++ Local<v8::Object> instance = t->GetFunction()->NewInstance(); ++ context->Global()->Set(v8_str("obj"), instance); ++ v8::TryCatch try_catch; ++ Local<Value> value; ++ CHECK(!try_catch.HasCaught()); + +- value = CompileRun("obj(42)"); +- CHECK(!try_catch.HasCaught()); +- CHECK_EQ(42, value->Int32Value()); ++ value = CompileRun("obj(42)"); ++ CHECK(!try_catch.HasCaught()); ++ CHECK_EQ(42, value->Int32Value()); + +- value = CompileRun("(function(o){return o(49)})(obj)"); +- CHECK(!try_catch.HasCaught()); +- CHECK_EQ(49, value->Int32Value()); ++ value = CompileRun("(function(o){return o(49)})(obj)"); ++ CHECK(!try_catch.HasCaught()); ++ CHECK_EQ(49, value->Int32Value()); + +- // test special case of call as function +- value = CompileRun("[obj]['0'](45)"); +- CHECK(!try_catch.HasCaught()); +- CHECK_EQ(45, value->Int32Value()); ++ // test special case of call as function ++ value = CompileRun("[obj]['0'](45)"); ++ CHECK(!try_catch.HasCaught()); ++ CHECK_EQ(45, value->Int32Value()); + +- value = CompileRun("obj.call = Function.prototype.call;" +- "obj.call(null, 87)"); +- CHECK(!try_catch.HasCaught()); +- CHECK_EQ(87, value->Int32Value()); ++ value = CompileRun("obj.call = Function.prototype.call;" ++ "obj.call(null, 87)"); ++ CHECK(!try_catch.HasCaught()); ++ CHECK_EQ(87, value->Int32Value()); + +- // Regression tests for bug #1116356: Calling call through call/apply +- // must work for non-function receivers. +- const char* apply_99 = "Function.prototype.call.apply(obj, [this, 99])"; +- value = CompileRun(apply_99); +- CHECK(!try_catch.HasCaught()); +- CHECK_EQ(99, value->Int32Value()); ++ // Regression tests for bug #1116356: Calling call through call/apply ++ // must work for non-function receivers. ++ const char* apply_99 = "Function.prototype.call.apply(obj, [this, 99])"; ++ value = CompileRun(apply_99); ++ CHECK(!try_catch.HasCaught()); ++ CHECK_EQ(99, value->Int32Value()); + +- const char* call_17 = "Function.prototype.call.call(obj, this, 17)"; +- value = CompileRun(call_17); +- CHECK(!try_catch.HasCaught()); +- CHECK_EQ(17, value->Int32Value()); ++ const char* call_17 = "Function.prototype.call.call(obj, this, 17)"; ++ value = CompileRun(call_17); ++ CHECK(!try_catch.HasCaught()); ++ CHECK_EQ(17, value->Int32Value()); + +- // Check that the call-as-function handler can be called through +- // new. +- value = CompileRun("new obj(43)"); +- CHECK(!try_catch.HasCaught()); +- CHECK_EQ(-43, value->Int32Value()); ++ // Check that the call-as-function handler can be called through ++ // new. ++ value = CompileRun("new obj(43)"); ++ CHECK(!try_catch.HasCaught()); ++ CHECK_EQ(-43, value->Int32Value()); ++ ++ // Check that the call-as-function handler can be called through ++ // the API. ++ v8::Handle<Value> args[] = { v8_num(28) }; ++ value = instance->CallAsFunction(instance, 1, args); ++ CHECK(!try_catch.HasCaught()); ++ CHECK_EQ(28, value->Int32Value()); ++ } ++ ++ { Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); ++ Local<ObjectTemplate> instance_template = t->InstanceTemplate(); ++ Local<v8::Object> instance = t->GetFunction()->NewInstance(); ++ context->Global()->Set(v8_str("obj2"), instance); ++ v8::TryCatch try_catch; ++ Local<Value> value; ++ CHECK(!try_catch.HasCaught()); ++ ++ // Call an object without call-as-function handler through the JS ++ value = CompileRun("obj2(28)"); ++ CHECK(value.IsEmpty()); ++ CHECK(try_catch.HasCaught()); ++ String::AsciiValue exception_value1(try_catch.Exception()); ++ CHECK_EQ(*exception_value1, ++ "TypeError: Property 'obj2' of object " ++ "#<Object> is not a function"); ++ try_catch.Reset(); ++ ++ // Call an object without call-as-function handler through the API ++ value = CompileRun("obj2(28)"); ++ v8::Handle<Value> args[] = { v8_num(28) }; ++ value = instance->CallAsFunction(instance, 1, args); ++ CHECK(value.IsEmpty()); ++ CHECK(try_catch.HasCaught()); ++ String::AsciiValue exception_value2(try_catch.Exception()); ++ CHECK_EQ(*exception_value2, "TypeError: [object Object] is not a function"); ++ try_catch.Reset(); ++ } ++ ++ { Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); ++ Local<ObjectTemplate> instance_template = t->InstanceTemplate(); ++ instance_template->SetCallAsFunctionHandler(ThrowValue); ++ Local<v8::Object> instance = t->GetFunction()->NewInstance(); ++ context->Global()->Set(v8_str("obj3"), instance); ++ v8::TryCatch try_catch; ++ Local<Value> value; ++ CHECK(!try_catch.HasCaught()); ++ ++ // Catch the exception which is thrown by call-as-function handler ++ value = CompileRun("obj3(22)"); ++ CHECK(try_catch.HasCaught()); ++ String::AsciiValue exception_value1(try_catch.Exception()); ++ CHECK_EQ(*exception_value1, "22"); ++ try_catch.Reset(); ++ ++ v8::Handle<Value> args[] = { v8_num(23) }; ++ value = instance->CallAsFunction(instance, 1, args); ++ CHECK(try_catch.HasCaught()); ++ String::AsciiValue exception_value2(try_catch.Exception()); ++ CHECK_EQ(*exception_value2, "23"); ++ try_catch.Reset(); ++ } + } + + +-- +1.7.2.3 + diff --git a/src/v8/0010-Implement-CallAsConstructor-method-for-Object-in-the.patch b/src/v8/0010-Implement-CallAsConstructor-method-for-Object-in-the.patch new file mode 100644 index 0000000000..cb4dd186d6 --- /dev/null +++ b/src/v8/0010-Implement-CallAsConstructor-method-for-Object-in-the.patch @@ -0,0 +1,397 @@ +From 3d6d4249878f7960eac4c9c94e0f2529f9a58c4a Mon Sep 17 00:00:00 2001 +From: ager@chromium.org <ager@chromium.org> +Date: Fri, 6 May 2011 11:07:52 +0000 +Subject: [PATCH 10/13] Implement CallAsConstructor method for Object in the API + +Patch by Peter Varga. + +BUG=v8:1348 +TEST=cctest/test-api/ConstructorForObject + +Review URL: http://codereview.chromium.org/6902108 + +git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@7803 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 +--- + include/v8.h | 8 ++ + src/api.cc | 41 +++++++++- + src/execution.cc | 28 +++++++ + src/execution.h | 2 + + test/cctest/test-api.cc | 205 +++++++++++++++++++++++++++++++++++++++++++++-- + 5 files changed, 276 insertions(+), 8 deletions(-) + +diff --git a/include/v8.h b/include/v8.h +index 8a8e1cd..84462b5 100644 +--- a/include/v8.h ++++ b/include/v8.h +@@ -1765,6 +1765,14 @@ class Object : public Value { + int argc, + Handle<Value> argv[]); + ++ /** ++ * Call an Object as a consturctor if a callback is set by the ++ * ObjectTemplate::SetCallAsFunctionHandler method. ++ * Note: This method behaves like the Function::NewInstance method. ++ */ ++ V8EXPORT Local<Value> CallAsConstructor(int argc, ++ Handle<Value> argv[]); ++ + V8EXPORT static Local<Object> New(); + static inline Object* Cast(Value* obj); + private: +diff --git a/src/api.cc b/src/api.cc +index e412e51..1a585d6 100644 +--- a/src/api.cc ++++ b/src/api.cc +@@ -3266,7 +3266,7 @@ Local<v8::Value> Object::CallAsFunction(v8::Handle<v8::Object> recv, int argc, + return Local<v8::Value>()); + LOG_API(isolate, "Object::CallAsFunction"); + ENTER_V8(isolate); +- HandleScope scope; ++ i::HandleScope scope(isolate); + i::Handle<i::JSObject> obj = Utils::OpenHandle(this); + i::Handle<i::Object> recv_obj = Utils::OpenHandle(*recv); + STATIC_ASSERT(sizeof(v8::Handle<v8::Value>) == sizeof(i::Object**)); +@@ -3286,7 +3286,44 @@ Local<v8::Value> Object::CallAsFunction(v8::Handle<v8::Object> recv, int argc, + i::Handle<i::Object> returned = + i::Execution::Call(fun, recv_obj, argc, args, &has_pending_exception); + EXCEPTION_BAILOUT_CHECK(isolate, Local<Value>()); +- return scope.Close(Utils::ToLocal(returned)); ++ return Utils::ToLocal(scope.CloseAndEscape(returned)); ++} ++ ++ ++Local<v8::Value> Object::CallAsConstructor(int argc, ++ v8::Handle<v8::Value> argv[]) { ++ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate(); ++ ON_BAILOUT(isolate, "v8::Object::CallAsConstructor()", ++ return Local<v8::Object>()); ++ LOG_API(isolate, "Object::CallAsConstructor"); ++ ENTER_V8(isolate); ++ i::HandleScope scope(isolate); ++ i::Handle<i::JSObject> obj = Utils::OpenHandle(this); ++ STATIC_ASSERT(sizeof(v8::Handle<v8::Value>) == sizeof(i::Object**)); ++ i::Object*** args = reinterpret_cast<i::Object***>(argv); ++ if (obj->IsJSFunction()) { ++ i::Handle<i::JSFunction> fun = i::Handle<i::JSFunction>::cast(obj); ++ EXCEPTION_PREAMBLE(isolate); ++ i::Handle<i::Object> returned = ++ i::Execution::New(fun, argc, args, &has_pending_exception); ++ EXCEPTION_BAILOUT_CHECK(isolate, Local<v8::Object>()); ++ return Utils::ToLocal(scope.CloseAndEscape( ++ i::Handle<i::JSObject>::cast(returned))); ++ } ++ EXCEPTION_PREAMBLE(isolate); ++ i::Handle<i::Object> delegate = ++ i::Execution::TryGetConstructorDelegate(obj, &has_pending_exception); ++ EXCEPTION_BAILOUT_CHECK(isolate, Local<v8::Object>()); ++ if (!delegate->IsUndefined()) { ++ i::Handle<i::JSFunction> fun = i::Handle<i::JSFunction>::cast(delegate); ++ EXCEPTION_PREAMBLE(isolate); ++ i::Handle<i::Object> returned = ++ i::Execution::Call(fun, obj, argc, args, &has_pending_exception); ++ EXCEPTION_BAILOUT_CHECK(isolate, Local<v8::Object>()); ++ ASSERT(!delegate->IsUndefined()); ++ return Utils::ToLocal(scope.CloseAndEscape(returned)); ++ } ++ return Local<v8::Object>(); + } + + +diff --git a/src/execution.cc b/src/execution.cc +index 894d741..afb352c 100644 +--- a/src/execution.cc ++++ b/src/execution.cc +@@ -297,6 +297,34 @@ Handle<Object> Execution::GetConstructorDelegate(Handle<Object> object) { + } + + ++Handle<Object> Execution::TryGetConstructorDelegate( ++ Handle<Object> object, ++ bool* has_pending_exception) { ++ ASSERT(!object->IsJSFunction()); ++ Isolate* isolate = Isolate::Current(); ++ ++ // If you return a function from here, it will be called when an ++ // attempt is made to call the given object as a constructor. ++ ++ // Objects created through the API can have an instance-call handler ++ // that should be used when calling the object as a function. ++ if (object->IsHeapObject() && ++ HeapObject::cast(*object)->map()->has_instance_call_handler()) { ++ return Handle<JSFunction>( ++ isolate->global_context()->call_as_constructor_delegate()); ++ } ++ ++ // If the Object doesn't have an instance-call handler we should ++ // throw a non-callable exception. ++ i::Handle<i::Object> error_obj = isolate->factory()->NewTypeError( ++ "called_non_callable", i::HandleVector<i::Object>(&object, 1)); ++ isolate->Throw(*error_obj); ++ *has_pending_exception = true; ++ ++ return isolate->factory()->undefined_value(); ++} ++ ++ + bool StackGuard::IsStackOverflow() { + ExecutionAccess access(isolate_); + return (thread_local_.jslimit_ != kInterruptLimit && +diff --git a/src/execution.h b/src/execution.h +index 0a0be51..ec2a195 100644 +--- a/src/execution.h ++++ b/src/execution.h +@@ -150,6 +150,8 @@ class Execution : public AllStatic { + // Get a function delegate (or undefined) for the given non-function + // object. Used for support calling objects as constructors. + static Handle<Object> GetConstructorDelegate(Handle<Object> object); ++ static Handle<Object> TryGetConstructorDelegate(Handle<Object> object, ++ bool* has_pending_exception); + }; + + +diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc +index 693d51e..1334f63 100644 +--- a/test/cctest/test-api.cc ++++ b/test/cctest/test-api.cc +@@ -6746,6 +6746,200 @@ THREADED_TEST(Constructor) { + CHECK(value->BooleanValue()); + } + ++ ++static Handle<Value> ConstructorCallback(const Arguments& args) { ++ ApiTestFuzzer::Fuzz(); ++ Local<Object> This; ++ ++ if (args.IsConstructCall()) { ++ Local<Object> Holder = args.Holder(); ++ This = Object::New(); ++ Local<Value> proto = Holder->GetPrototype(); ++ if (proto->IsObject()) { ++ This->SetPrototype(proto); ++ } ++ } else { ++ This = args.This(); ++ } ++ ++ This->Set(v8_str("a"), args[0]); ++ return This; ++} ++ ++ ++static Handle<Value> FakeConstructorCallback(const Arguments& args) { ++ ApiTestFuzzer::Fuzz(); ++ return args[0]; ++} ++ ++ ++THREADED_TEST(ConstructorForObject) { ++ v8::HandleScope handle_scope; ++ LocalContext context; ++ ++ { Local<ObjectTemplate> instance_template = ObjectTemplate::New(); ++ instance_template->SetCallAsFunctionHandler(ConstructorCallback); ++ Local<Object> instance = instance_template->NewInstance(); ++ context->Global()->Set(v8_str("obj"), instance); ++ v8::TryCatch try_catch; ++ Local<Value> value; ++ CHECK(!try_catch.HasCaught()); ++ ++ // Call the Object's constructor with a 32-bit signed integer. ++ value = CompileRun("(function() { var o = new obj(28); return o.a; })()"); ++ CHECK(!try_catch.HasCaught()); ++ CHECK(value->IsInt32()); ++ CHECK_EQ(28, value->Int32Value()); ++ ++ Local<Value> args1[] = { v8_num(28) }; ++ Local<Value> value_obj1 = instance->CallAsConstructor(1, args1); ++ CHECK(value_obj1->IsObject()); ++ Local<Object> object1 = Local<Object>::Cast(value_obj1); ++ value = object1->Get(v8_str("a")); ++ CHECK(value->IsInt32()); ++ CHECK(!try_catch.HasCaught()); ++ CHECK_EQ(28, value->Int32Value()); ++ ++ // Call the Object's constructor with a String. ++ value = CompileRun( ++ "(function() { var o = new obj('tipli'); return o.a; })()"); ++ CHECK(!try_catch.HasCaught()); ++ CHECK(value->IsString()); ++ String::AsciiValue string_value1(value->ToString()); ++ CHECK_EQ("tipli", *string_value1); ++ ++ Local<Value> args2[] = { v8_str("tipli") }; ++ Local<Value> value_obj2 = instance->CallAsConstructor(1, args2); ++ CHECK(value_obj2->IsObject()); ++ Local<Object> object2 = Local<Object>::Cast(value_obj2); ++ value = object2->Get(v8_str("a")); ++ CHECK(!try_catch.HasCaught()); ++ CHECK(value->IsString()); ++ String::AsciiValue string_value2(value->ToString()); ++ CHECK_EQ("tipli", *string_value2); ++ ++ // Call the Object's constructor with a Boolean. ++ value = CompileRun("(function() { var o = new obj(true); return o.a; })()"); ++ CHECK(!try_catch.HasCaught()); ++ CHECK(value->IsBoolean()); ++ CHECK_EQ(true, value->BooleanValue()); ++ ++ Handle<Value> args3[] = { v8::Boolean::New(true) }; ++ Local<Value> value_obj3 = instance->CallAsConstructor(1, args3); ++ CHECK(value_obj3->IsObject()); ++ Local<Object> object3 = Local<Object>::Cast(value_obj3); ++ value = object3->Get(v8_str("a")); ++ CHECK(!try_catch.HasCaught()); ++ CHECK(value->IsBoolean()); ++ CHECK_EQ(true, value->BooleanValue()); ++ ++ // Call the Object's constructor with undefined. ++ Handle<Value> args4[] = { v8::Undefined() }; ++ Local<Value> value_obj4 = instance->CallAsConstructor(1, args4); ++ CHECK(value_obj4->IsObject()); ++ Local<Object> object4 = Local<Object>::Cast(value_obj4); ++ value = object4->Get(v8_str("a")); ++ CHECK(!try_catch.HasCaught()); ++ CHECK(value->IsUndefined()); ++ ++ // Call the Object's constructor with null. ++ Handle<Value> args5[] = { v8::Null() }; ++ Local<Value> value_obj5 = instance->CallAsConstructor(1, args5); ++ CHECK(value_obj5->IsObject()); ++ Local<Object> object5 = Local<Object>::Cast(value_obj5); ++ value = object5->Get(v8_str("a")); ++ CHECK(!try_catch.HasCaught()); ++ CHECK(value->IsNull()); ++ } ++ ++ // Check exception handling when there is no constructor set for the Object. ++ { Local<ObjectTemplate> instance_template = ObjectTemplate::New(); ++ Local<Object> instance = instance_template->NewInstance(); ++ context->Global()->Set(v8_str("obj2"), instance); ++ v8::TryCatch try_catch; ++ Local<Value> value; ++ CHECK(!try_catch.HasCaught()); ++ ++ value = CompileRun("new obj2(28)"); ++ CHECK(try_catch.HasCaught()); ++ String::AsciiValue exception_value1(try_catch.Exception()); ++ CHECK_EQ("TypeError: object is not a function", *exception_value1); ++ try_catch.Reset(); ++ ++ Local<Value> args[] = { v8_num(29) }; ++ value = instance->CallAsConstructor(1, args); ++ CHECK(try_catch.HasCaught()); ++ String::AsciiValue exception_value2(try_catch.Exception()); ++ CHECK_EQ("TypeError: #<Object> is not a function", *exception_value2); ++ try_catch.Reset(); ++ } ++ ++ // Check the case when constructor throws exception. ++ { Local<ObjectTemplate> instance_template = ObjectTemplate::New(); ++ instance_template->SetCallAsFunctionHandler(ThrowValue); ++ Local<Object> instance = instance_template->NewInstance(); ++ context->Global()->Set(v8_str("obj3"), instance); ++ v8::TryCatch try_catch; ++ Local<Value> value; ++ CHECK(!try_catch.HasCaught()); ++ ++ value = CompileRun("new obj3(22)"); ++ CHECK(try_catch.HasCaught()); ++ String::AsciiValue exception_value1(try_catch.Exception()); ++ CHECK_EQ("22", *exception_value1); ++ try_catch.Reset(); ++ ++ Local<Value> args[] = { v8_num(23) }; ++ value = instance->CallAsConstructor(1, args); ++ CHECK(try_catch.HasCaught()); ++ String::AsciiValue exception_value2(try_catch.Exception()); ++ CHECK_EQ("23", *exception_value2); ++ try_catch.Reset(); ++ } ++ ++ // Check whether constructor returns with an object or non-object. ++ { Local<FunctionTemplate> function_template = ++ FunctionTemplate::New(FakeConstructorCallback); ++ Local<Function> function = function_template->GetFunction(); ++ Local<Object> instance1 = function; ++ context->Global()->Set(v8_str("obj4"), instance1); ++ v8::TryCatch try_catch; ++ Local<Value> value; ++ CHECK(!try_catch.HasCaught()); ++ ++ CHECK(instance1->IsObject()); ++ CHECK(instance1->IsFunction()); ++ ++ value = CompileRun("new obj4(28)"); ++ CHECK(!try_catch.HasCaught()); ++ CHECK(value->IsObject()); ++ ++ Local<Value> args1[] = { v8_num(28) }; ++ value = instance1->CallAsConstructor(1, args1); ++ CHECK(!try_catch.HasCaught()); ++ CHECK(value->IsObject()); ++ ++ Local<ObjectTemplate> instance_template = ObjectTemplate::New(); ++ instance_template->SetCallAsFunctionHandler(FakeConstructorCallback); ++ Local<Object> instance2 = instance_template->NewInstance(); ++ context->Global()->Set(v8_str("obj5"), instance2); ++ CHECK(!try_catch.HasCaught()); ++ ++ CHECK(instance2->IsObject()); ++ CHECK(!instance2->IsFunction()); ++ ++ value = CompileRun("new obj5(28)"); ++ CHECK(!try_catch.HasCaught()); ++ CHECK(!value->IsObject()); ++ ++ Local<Value> args2[] = { v8_num(28) }; ++ value = instance2->CallAsConstructor(1, args2); ++ CHECK(!try_catch.HasCaught()); ++ CHECK(!value->IsObject()); ++ } ++} ++ ++ + THREADED_TEST(FunctionDescriptorException) { + v8::HandleScope handle_scope; + LocalContext context; +@@ -7028,9 +7222,8 @@ THREADED_TEST(CallAsFunction) { + CHECK(value.IsEmpty()); + CHECK(try_catch.HasCaught()); + String::AsciiValue exception_value1(try_catch.Exception()); +- CHECK_EQ(*exception_value1, +- "TypeError: Property 'obj2' of object " +- "#<Object> is not a function"); ++ CHECK_EQ("TypeError: Property 'obj2' of object #<Object> is not a function", ++ *exception_value1); + try_catch.Reset(); + + // Call an object without call-as-function handler through the API +@@ -7040,7 +7233,7 @@ THREADED_TEST(CallAsFunction) { + CHECK(value.IsEmpty()); + CHECK(try_catch.HasCaught()); + String::AsciiValue exception_value2(try_catch.Exception()); +- CHECK_EQ(*exception_value2, "TypeError: [object Object] is not a function"); ++ CHECK_EQ("TypeError: [object Object] is not a function", *exception_value2); + try_catch.Reset(); + } + +@@ -7057,14 +7250,14 @@ THREADED_TEST(CallAsFunction) { + value = CompileRun("obj3(22)"); + CHECK(try_catch.HasCaught()); + String::AsciiValue exception_value1(try_catch.Exception()); +- CHECK_EQ(*exception_value1, "22"); ++ CHECK_EQ("22", *exception_value1); + try_catch.Reset(); + + v8::Handle<Value> args[] = { v8_num(23) }; + value = instance->CallAsFunction(instance, 1, args); + CHECK(try_catch.HasCaught()); + String::AsciiValue exception_value2(try_catch.Exception()); +- CHECK_EQ(*exception_value2, "23"); ++ CHECK_EQ("23", *exception_value2); + try_catch.Reset(); + } + } +-- +1.7.2.3 + diff --git a/src/v8/0011-QtScript-V8-Add-new-v8-api-to-check-if-a-value-is-an.patch b/src/v8/0011-QtScript-V8-Add-new-v8-api-to-check-if-a-value-is-an.patch new file mode 100644 index 0000000000..9c0e68352f --- /dev/null +++ b/src/v8/0011-QtScript-V8-Add-new-v8-api-to-check-if-a-value-is-an.patch @@ -0,0 +1,63 @@ +From f22d0312faeb93ced8747d9aae8c6d77e11b4aba Mon Sep 17 00:00:00 2001 +From: Jedrzej Nowacki <jedrzej.nowacki@nokia.com> +Date: Tue, 7 Dec 2010 11:56:42 +0100 +Subject: [PATCH 11/13] QtScript/V8: Add new v8 api to check if a value is an error. + +New function v8::Value::IsError was created. + +This API is experimental and added only for the purposes of our +research. +--- + include/v8.h | 5 +++++ + src/api.cc | 6 ++++++ + src/heap.h | 1 + + 3 files changed, 12 insertions(+), 0 deletions(-) + +diff --git a/include/v8.h b/include/v8.h +index 84462b5..08b0ec2 100644 +--- a/include/v8.h ++++ b/include/v8.h +@@ -937,6 +937,11 @@ class Value : public Data { + */ + V8EXPORT bool IsRegExp() const; + ++ /** ++ * Returns true if this value is an Error. ++ */ ++ V8EXPORT bool IsError() const; ++ + V8EXPORT Local<Boolean> ToBoolean() const; + V8EXPORT Local<Number> ToNumber() const; + V8EXPORT Local<String> ToString() const; +diff --git a/src/api.cc b/src/api.cc +index 1a585d6..bd435eb 100644 +--- a/src/api.cc ++++ b/src/api.cc +@@ -2108,6 +2108,12 @@ bool Value::IsRegExp() const { + return obj->IsJSRegExp(); + } + ++bool Value::IsError() const { ++ if (IsDeadCheck(i::Isolate::Current(), "v8::Value::IsError()")) return false; ++ i::Handle<i::Object> obj = Utils::OpenHandle(this); ++ return obj->HasSpecificClassOf(HEAP->Error_symbol()); ++} ++ + + Local<String> Value::ToString() const { + i::Handle<i::Object> obj = Utils::OpenHandle(this); +diff --git a/src/heap.h b/src/heap.h +index 8cbf378..db90bb9 100644 +--- a/src/heap.h ++++ b/src/heap.h +@@ -169,6 +169,7 @@ inline Heap* _inline_get_heap_(); + V(string_symbol, "string") \ + V(String_symbol, "String") \ + V(Date_symbol, "Date") \ ++ V(Error_symbol, "Error") \ + V(this_symbol, "this") \ + V(to_string_symbol, "toString") \ + V(char_at_symbol, "CharAt") \ +-- +1.7.2.3 + diff --git a/src/v8/0012-Add-IsCallable-method-for-Object-in-the-API.patch b/src/v8/0012-Add-IsCallable-method-for-Object-in-the-API.patch new file mode 100644 index 0000000000..77589c8af3 --- /dev/null +++ b/src/v8/0012-Add-IsCallable-method-for-Object-in-the-API.patch @@ -0,0 +1,116 @@ +From 472c04c9e7a64e8734c76d2cf97a7cc5b773b788 Mon Sep 17 00:00:00 2001 +From: ager@chromium.org <ager@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00> +Date: Mon, 9 May 2011 15:24:48 +0000 +Subject: [PATCH 12/13] Add IsCallable method for Object in the API + +Patch by Peter Varga. + +BUG=none +TEST=cctest/test-api/CallableObject + +Review URL: http://codereview.chromium.org/6964005 + +git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@7828 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 +--- + include/v8.h | 7 +++++++ + src/api.cc | 11 +++++++++++ + test/cctest/test-api.cc | 43 +++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 61 insertions(+), 0 deletions(-) + +diff --git a/include/v8.h b/include/v8.h +index 08b0ec2..4194d4a 100644 +--- a/include/v8.h ++++ b/include/v8.h +@@ -1763,6 +1763,13 @@ class Object : public Value { + V8EXPORT int GetIndexedPropertiesExternalArrayDataLength(); + + /** ++ * Checks whether a callback is set by the ++ * ObjectTemplate::SetCallAsFunctionHandler method. ++ * When an Object is callable this method returns true. ++ */ ++ V8EXPORT bool IsCallable(); ++ ++ /** + * Call an Object as a function if a callback is set by the + * ObjectTemplate::SetCallAsFunctionHandler method. + */ +diff --git a/src/api.cc b/src/api.cc +index bd435eb..a5a637f 100644 +--- a/src/api.cc ++++ b/src/api.cc +@@ -3265,6 +3265,17 @@ int v8::Object::GetIndexedPropertiesExternalArrayDataLength() { + } + + ++bool v8::Object::IsCallable() { ++ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate(); ++ ON_BAILOUT(isolate, "v8::Object::IsCallable()", return false); ++ ENTER_V8(isolate); ++ i::HandleScope scope(isolate); ++ i::Handle<i::JSObject> obj = Utils::OpenHandle(this); ++ if (obj->IsJSFunction()) return true; ++ return i::Execution::GetFunctionDelegate(obj)->IsJSFunction(); ++} ++ ++ + Local<v8::Value> Object::CallAsFunction(v8::Handle<v8::Object> recv, int argc, + v8::Handle<v8::Value> argv[]) { + i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate(); +diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc +index 1334f63..45db5a1 100644 +--- a/test/cctest/test-api.cc ++++ b/test/cctest/test-api.cc +@@ -7263,6 +7263,49 @@ THREADED_TEST(CallAsFunction) { + } + + ++// Check whether a non-function object is callable. ++THREADED_TEST(CallableObject) { ++ v8::HandleScope scope; ++ LocalContext context; ++ ++ { Local<ObjectTemplate> instance_template = ObjectTemplate::New(); ++ instance_template->SetCallAsFunctionHandler(call_as_function); ++ Local<Object> instance = instance_template->NewInstance(); ++ v8::TryCatch try_catch; ++ ++ CHECK(instance->IsCallable()); ++ CHECK(!try_catch.HasCaught()); ++ } ++ ++ { Local<ObjectTemplate> instance_template = ObjectTemplate::New(); ++ Local<Object> instance = instance_template->NewInstance(); ++ v8::TryCatch try_catch; ++ ++ CHECK(!instance->IsCallable()); ++ CHECK(!try_catch.HasCaught()); ++ } ++ ++ { Local<FunctionTemplate> function_template = ++ FunctionTemplate::New(call_as_function); ++ Local<Function> function = function_template->GetFunction(); ++ Local<Object> instance = function; ++ v8::TryCatch try_catch; ++ ++ CHECK(instance->IsCallable()); ++ CHECK(!try_catch.HasCaught()); ++ } ++ ++ { Local<FunctionTemplate> function_template = FunctionTemplate::New(); ++ Local<Function> function = function_template->GetFunction(); ++ Local<Object> instance = function; ++ v8::TryCatch try_catch; ++ ++ CHECK(instance->IsCallable()); ++ CHECK(!try_catch.HasCaught()); ++ } ++} ++ ++ + static int CountHandles() { + return v8::HandleScope::NumberOfHandles(); + } +-- +1.7.2.3 + diff --git a/src/v8/0013-Remove-execute-flag-from-v8-debug.h.patch b/src/v8/0013-Remove-execute-flag-from-v8-debug.h.patch new file mode 100644 index 0000000000..6bad561273 --- /dev/null +++ b/src/v8/0013-Remove-execute-flag-from-v8-debug.h.patch @@ -0,0 +1,15 @@ +From dc2cad4f8fc88c52fcea09b8d0262d35cd32dc44 Mon Sep 17 00:00:00 2001 +From: Aaron Kennedy <aaron.kennedy@nokia.com> +Date: Thu, 25 Aug 2011 11:09:58 +1000 +Subject: [PATCH 13/13] Remove execute flag from v8-debug.h + +--- + 0 files changed, 0 insertions(+), 0 deletions(-) + mode change 100755 => 100644 include/v8-debug.h + +diff --git a/include/v8-debug.h b/include/v8-debug.h +old mode 100755 +new mode 100644 +-- +1.7.2.3 + diff --git a/src/v8/README b/src/v8/README new file mode 100644 index 0000000000..2dc39705aa --- /dev/null +++ b/src/v8/README @@ -0,0 +1 @@ +These patches apply cleanly against v8 at 2eaa4b29586fdbd5d41f7b7d9e72ecca6d53dbd2 diff --git a/src/v8/v8.pri b/src/v8/v8.pri new file mode 100644 index 0000000000..4d55bbfd5a --- /dev/null +++ b/src/v8/v8.pri @@ -0,0 +1,260 @@ +equals(QT_ARCH, x86_64)|contains(CONFIG, x86_64):CONFIG += arch_x86_64 +else:equals(QT_ARCH, "i386"):CONFIG += arch_i386 +else:equals(QT_ARCH, "arm"):CONFIG += arch_arm +else:equals(QMAKE_HOST.arch, armv7l):CONFIG += arch_arm +else:equals(QMAKE_HOST.arch, x86_64):CONFIG += arch_x86_64 +else:equals(QMAKE_HOST.arch, x86):CONFIG += arch_i386 +else:equals(QMAKE_HOST.arch, i386):CONFIG += arch_i386 +else:equals(QMAKE_HOST.arch, i686):CONFIG += arch_i386 +else:error("Couldn't detect supported architecture ($$QMAKE_HOST.arch/$$QT_ARCH). Currently supported architectures are: x64, x86 and arm") + +include($$PWD/v8base.pri) + +V8_GENERATED_SOURCES_DIR = generated + +!contains(QT_CONFIG, static): DEFINES += V8_SHARED BUILDING_V8_SHARED + +# this maybe removed in future +DEFINES += ENABLE_DEBUGGER_SUPPORT + +# this is needed by crankshaft ( http://code.google.com/p/v8/issues/detail?id=1271 ) +DEFINES += ENABLE_VMSTATE_TRACKING ENABLE_LOGGING_AND_PROFILING + +CONFIG(debug, debug|release) { + DEFINES += DEBUG V8_ENABLE_CHECKS OBJECT_PRINT ENABLE_DISASSEMBLER +} else { + DEFINES += NDEBUG +} + +V8SRC = $$V8DIR/src + +INCLUDEPATH += \ + $$V8SRC + +SOURCES += \ + $$V8SRC/accessors.cc \ + $$V8SRC/allocation.cc \ + $$V8SRC/api.cc \ + $$V8SRC/assembler.cc \ + $$V8SRC/ast.cc \ + $$V8SRC/atomicops_internals_x86_gcc.cc \ + $$V8SRC/bignum.cc \ + $$V8SRC/bignum-dtoa.cc \ + $$V8SRC/bootstrapper.cc \ + $$V8SRC/builtins.cc \ + $$V8SRC/cached-powers.cc \ + $$V8SRC/checks.cc \ + $$V8SRC/circular-queue.cc \ + $$V8SRC/code-stubs.cc \ + $$V8SRC/codegen.cc \ + $$V8SRC/compilation-cache.cc \ + $$V8SRC/compiler.cc \ + $$V8SRC/contexts.cc \ + $$V8SRC/conversions.cc \ + $$V8SRC/counters.cc \ + $$V8SRC/cpu-profiler.cc \ + $$V8SRC/data-flow.cc \ + $$V8SRC/dateparser.cc \ + $$V8SRC/debug-agent.cc \ + $$V8SRC/debug.cc \ + $$V8SRC/deoptimizer.cc \ + $$V8SRC/disassembler.cc \ + $$V8SRC/diy-fp.cc \ + $$V8SRC/dtoa.cc \ + $$V8SRC/execution.cc \ + $$V8SRC/factory.cc \ + $$V8SRC/flags.cc \ + $$V8SRC/frame-element.cc \ + $$V8SRC/frames.cc \ + $$V8SRC/full-codegen.cc \ + $$V8SRC/func-name-inferrer.cc \ + $$V8SRC/gdb-jit.cc \ + $$V8SRC/global-handles.cc \ + $$V8SRC/fast-dtoa.cc \ + $$V8SRC/fixed-dtoa.cc \ + $$V8SRC/handles.cc \ + $$V8SRC/hashmap.cc \ + $$V8SRC/heap-profiler.cc \ + $$V8SRC/heap.cc \ + $$V8SRC/hydrogen.cc \ + $$V8SRC/hydrogen-instructions.cc \ + $$V8SRC/ic.cc \ + $$V8SRC/inspector.cc \ + $$V8SRC/interpreter-irregexp.cc \ + $$V8SRC/isolate.cc \ + $$V8SRC/jsregexp.cc \ + $$V8SRC/lithium-allocator.cc \ + $$V8SRC/lithium.cc \ + $$V8SRC/liveedit.cc \ + $$V8SRC/liveobjectlist.cc \ + $$V8SRC/log-utils.cc \ + $$V8SRC/log.cc \ + $$V8SRC/mark-compact.cc \ + $$V8SRC/messages.cc \ + $$V8SRC/objects.cc \ + $$V8SRC/objects-printer.cc \ + $$V8SRC/objects-visiting.cc \ + $$V8SRC/parser.cc \ + $$V8SRC/preparser.cc \ + $$V8SRC/preparse-data.cc \ + $$V8SRC/profile-generator.cc \ + $$V8SRC/property.cc \ + $$V8SRC/regexp-macro-assembler-irregexp.cc \ + $$V8SRC/regexp-macro-assembler.cc \ + $$V8SRC/regexp-stack.cc \ + $$V8SRC/rewriter.cc \ + $$V8SRC/runtime.cc \ + $$V8SRC/runtime-profiler.cc \ + $$V8SRC/safepoint-table.cc \ + $$V8SRC/scanner-base.cc \ + $$V8SRC/scanner.cc \ + $$V8SRC/scopeinfo.cc \ + $$V8SRC/scopes.cc \ + $$V8SRC/serialize.cc \ + $$V8SRC/snapshot-common.cc \ + $$V8SRC/spaces.cc \ + $$V8SRC/string-search.cc \ + $$V8SRC/string-stream.cc \ + $$V8SRC/strtod.cc \ + $$V8SRC/stub-cache.cc \ + $$V8SRC/token.cc \ + $$V8SRC/top.cc \ + $$V8SRC/type-info.cc \ + $$V8SRC/unicode.cc \ + $$V8SRC/utils.cc \ + $$V8SRC/v8-counters.cc \ + $$V8SRC/v8.cc \ + $$V8SRC/v8threads.cc \ + $$V8SRC/variables.cc \ + $$V8SRC/version.cc \ + $$V8SRC/zone.cc \ + $$V8SRC/extensions/gc-extension.cc \ + $$V8SRC/extensions/externalize-string-extension.cc + +SOURCES += \ + $$V8SRC/snapshot-empty.cc \ + +arch_arm { +DEFINES += V8_TARGET_ARCH_ARM +SOURCES += \ + $$V8SRC/arm/builtins-arm.cc \ + $$V8SRC/arm/code-stubs-arm.cc \ + $$V8SRC/arm/codegen-arm.cc \ + $$V8SRC/arm/constants-arm.cc \ + $$V8SRC/arm/cpu-arm.cc \ + $$V8SRC/arm/debug-arm.cc \ + $$V8SRC/arm/deoptimizer-arm.cc \ + $$V8SRC/arm/disasm-arm.cc \ + $$V8SRC/arm/frames-arm.cc \ + $$V8SRC/arm/full-codegen-arm.cc \ + $$V8SRC/arm/ic-arm.cc \ + $$V8SRC/arm/lithium-arm.cc \ + $$V8SRC/arm/lithium-codegen-arm.cc \ + $$V8SRC/arm/lithium-gap-resolver-arm.cc \ + $$V8SRC/arm/macro-assembler-arm.cc \ + $$V8SRC/arm/regexp-macro-assembler-arm.cc \ + $$V8SRC/arm/stub-cache-arm.cc \ + $$V8SRC/arm/assembler-arm.cc +} + +arch_i386 { +DEFINES += V8_TARGET_ARCH_IA32 +SOURCES += \ + $$V8SRC/ia32/assembler-ia32.cc \ + $$V8SRC/ia32/builtins-ia32.cc \ + $$V8SRC/ia32/code-stubs-ia32.cc \ + $$V8SRC/ia32/codegen-ia32.cc \ + $$V8SRC/ia32/cpu-ia32.cc \ + $$V8SRC/ia32/debug-ia32.cc \ + $$V8SRC/ia32/deoptimizer-ia32.cc \ + $$V8SRC/ia32/disasm-ia32.cc \ + $$V8SRC/ia32/frames-ia32.cc \ + $$V8SRC/ia32/full-codegen-ia32.cc \ + $$V8SRC/ia32/ic-ia32.cc \ + $$V8SRC/ia32/lithium-codegen-ia32.cc \ + $$V8SRC/ia32/lithium-gap-resolver-ia32.cc \ + $$V8SRC/ia32/lithium-ia32.cc \ + $$V8SRC/ia32/macro-assembler-ia32.cc \ + $$V8SRC/ia32/regexp-macro-assembler-ia32.cc \ + $$V8SRC/ia32/stub-cache-ia32.cc +} + +# FIXME Should we use QT_CONFIG instead? What about 32 bit Macs? +arch_x86_64 { +DEFINES += V8_TARGET_ARCH_X64 +SOURCES += \ + $$V8SRC/x64/assembler-x64.cc \ + $$V8SRC/x64/builtins-x64.cc \ + $$V8SRC/x64/code-stubs-x64.cc \ + $$V8SRC/x64/codegen-x64.cc \ + $$V8SRC/x64/cpu-x64.cc \ + $$V8SRC/x64/debug-x64.cc \ + $$V8SRC/x64/deoptimizer-x64.cc \ + $$V8SRC/x64/disasm-x64.cc \ + $$V8SRC/x64/frames-x64.cc \ + $$V8SRC/x64/full-codegen-x64.cc \ + $$V8SRC/x64/ic-x64.cc \ + $$V8SRC/x64/lithium-codegen-x64.cc \ + $$V8SRC/x64/lithium-gap-resolver-x64.cc \ + $$V8SRC/x64/lithium-x64.cc \ + $$V8SRC/x64/macro-assembler-x64.cc \ + $$V8SRC/x64/regexp-macro-assembler-x64.cc \ + $$V8SRC/x64/stub-cache-x64.cc +} + +unix:!symbian:!macx { +SOURCES += \ + $$V8SRC/platform-linux.cc \ + $$V8SRC/platform-posix.cc +} + +#os:macos +macx { +SOURCES += \ + $$V8SRC/platform-macos.cc \ + $$V8SRC/platform-posix.cc +} + +win32 { +SOURCES += \ + $$V8SRC/platform-win32.cc +LIBS += Ws2_32.lib Winmm.lib +win32-msvc*: QMAKE_CXXFLAGS += -wd4100 -wd 4291 -wd4351 -wd4355 -wd4800 +win32-msvc*:arch_i386: DEFINES += _USE_32BIT_TIME_T +} + +#mode:debug +CONFIG(debug) { + SOURCES += \ + $$V8SRC/objects-debug.cc \ + $$V8SRC/prettyprinter.cc \ + $$V8SRC/regexp-macro-assembler-tracer.cc +} + +V8_LIBRARY_FILES = \ + $$V8SRC/runtime.js \ + $$V8SRC/v8natives.js \ + $$V8SRC/array.js \ + $$V8SRC/string.js \ + $$V8SRC/uri.js \ + $$V8SRC/math.js \ + $$V8SRC/messages.js \ + $$V8SRC/apinatives.js \ + $$V8SRC/date.js \ + $$V8SRC/regexp.js \ + $$V8SRC/json.js \ + $$V8SRC/liveedit-debugger.js \ + $$V8SRC/mirror-debugger.js \ + $$V8SRC/debug-debugger.js + +v8_js2c.commands = python $$V8DIR/tools/js2c.py $$V8_GENERATED_SOURCES_DIR/libraries.cpp $$V8_GENERATED_SOURCES_DIR/libraries-empty.cpp CORE +v8_js2c.commands += $$V8SRC/macros.py ${QMAKE_FILE_IN} +v8_js2c.output = $$V8_GENERATED_SOURCES_DIR/libraries.cpp +v8_js2c.input = V8_LIBRARY_FILES +v8_js2c.variable_out = SOURCES +v8_js2c.dependency_type = TYPE_C +v8_js2c.depends = $$V8DIR/tools/js2c.py $$V8SRC/macros.py +v8_js2c.CONFIG += combine +v8_js2c.name = generating[v8] ${QMAKE_FILE_IN} +silent:v8_js2c.commands = @echo generating[v8] ${QMAKE_FILE_IN} && $$v8_js2c.commands +QMAKE_EXTRA_COMPILERS += v8_js2c diff --git a/src/v8/v8.pro b/src/v8/v8.pro new file mode 100644 index 0000000000..eb14ca9a19 --- /dev/null +++ b/src/v8/v8.pro @@ -0,0 +1,24 @@ +load(qt_module) + +TARGET = QtV8 +QPRO_PWD = $$PWD +QT = + +CONFIG += module +MODULE_PRI = ../modules/qt_v8.pri + +win32-msvc*|win32-icc:QMAKE_LFLAGS += /BASE:0x66000000 + +load(qt_module_config) + +# Remove includepaths that were added by qt_module_config. +# These cause compilation of V8 to fail because they appear before +# 3rdparty/v8/src; 3rdparty/v8/src/v8.h will then be "shadowed" by +# the public v8.h API header (they are not the same!). +INCLUDEPATH -= $$MODULE_PRIVATE_INCLUDES +INCLUDEPATH -= $$MODULE_PRIVATE_INCLUDES/$$TARGET +INCLUDEPATH -= $$MODULE_INCLUDES $$MODULE_INCLUDES/.. + +HEADERS += $$QT_SOURCE_TREE/src/v8/qtv8version.h + +include(v8.pri) diff --git a/src/v8/v8base.pri b/src/v8/v8base.pri new file mode 100644 index 0000000000..f0d7c00451 --- /dev/null +++ b/src/v8/v8base.pri @@ -0,0 +1,19 @@ +V8DIR = $$(V8DIR) +isEmpty(V8DIR) { + V8DIR = $$PWD/../3rdparty/v8 +} else { + message(using external V8 from $$V8DIR) +} + +*-g++*: { + QMAKE_CFLAGS_WARN_ON += -Wno-unused-parameter + QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-parameter + + # mksnapshot hangs if gcc 4.5 is used + # for reference look at http://code.google.com/p/v8/issues/detail?id=884 + equals(QT_GCC_MAJOR_VERSION, 4): equals(QT_GCC_MINOR_VERSION, 5) { + message(because of a bug in gcc / v8 we need to add -fno-strict-aliasing) + QMAKE_CFLAGS += -fno-strict-aliasing + QMAKE_CXXFLAGS += -fno-strict-aliasing + } +} diff --git a/sync.profile b/sync.profile index c830612cd3..e0d23ac3bf 100644 --- a/sync.profile +++ b/sync.profile @@ -4,6 +4,7 @@ "QtPrintSupport" => "$basedir/src/printsupport", "QtOpenGL" => "$basedir/src/opengl", "QtCore" => "$basedir/src/corelib", + "QtV8" => "$basedir/src/v8", "QtXml" => "$basedir/src/xml", "QtSql" => "$basedir/src/sql", "QtNetwork" => "$basedir/src/network", @@ -14,6 +15,10 @@ "QtPlatformSupport" => "$basedir/src/platformsupport", ); %moduleheaders = ( # restrict the module headers to those found in relative path + "QtV8" => "../3rdparty/v8/include", +); +@allmoduleheadersprivate = ( + "QtV8" ); %classnames = ( "qglobal.h" => "QtGlobal", @@ -38,6 +43,7 @@ "qtopenvgversion.h" => "QtOpenVGVersion", "qtsqlversion.h" => "QtSqlVersion", "qttestversion.h" => "QtTestVersion", + "qtv8version.h" => "QtV8Version", "qtxmlversion.h" => "QtXmlVersion", ); %mastercontent = ( @@ -61,6 +67,7 @@ "QtOpenGL" => "$basedir/src/modules/qt_opengl.pri", "QtSql" => "$basedir/src/modules/qt_sql.pri", "QtTest" => "$basedir/src/modules/qt_testlib.pri", + "QtV8" => "$basedir/src/modules/qt_v8.pri", "QtXml" => "$basedir/src/modules/qt_xml.pri", "QtUiTools" => "$basedir/src/modules/qt_uitools.pri", "QtDesigner" => "$basedir/src/modules/qt_uilib.pri", diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro index 02e8b5bebb..fc51280c35 100644 --- a/tests/auto/auto.pro +++ b/tests/auto/auto.pro @@ -11,3 +11,4 @@ SUBDIRS += \ !cross_compile: SUBDIRS += host.pro contains(QT_CONFIG, opengl): SUBDIRS += opengl.pro unix:!embedded:contains(QT_CONFIG, dbus): SUBDIRS += dbus.pro +contains(QT_CONFIG, v8): SUBDIRS += v8.pro diff --git a/tests/auto/qmatrixnxn/tst_qmatrixnxn.cpp b/tests/auto/qmatrixnxn/tst_qmatrixnxn.cpp index 1b4d770ac6..98a559af85 100644 --- a/tests/auto/qmatrixnxn/tst_qmatrixnxn.cpp +++ b/tests/auto/qmatrixnxn/tst_qmatrixnxn.cpp @@ -1834,6 +1834,18 @@ void tst_QMatrixNxN::inverted4x4_data() QTest::newRow("invertible") << (void *)invertible.v << (void *)inverted.v << true; + static Matrix4 const invertible2 = { + {1.0f, 2.0f, 4.0f, 2.0f, + 8.0f, 3.0f, 5.0f, 3.0f, + 6.0f, 7.0f, 9.0f, 4.0f, + 0.0f, 0.0f, 0.0f, 1.0f} + }; + static Matrix4 inverted2; + m4Inverse(invertible2, inverted2); + + QTest::newRow("invertible2") + << (void *)invertible2.v << (void *)inverted2.v << true; + static Matrix4 const translate = { {1.0f, 0.0f, 0.0f, 2.0f, 0.0f, 1.0f, 0.0f, 3.0f, @@ -1907,12 +1919,12 @@ void tst_QMatrixNxN::orthonormalInverse4x4() m2.rotate(45.0, 1.0, 0.0, 0.0); m2.translate(10.0, 0.0, 0.0); - // Use optimize() to drop the internal flags that + // Use operator() to drop the internal flags that // mark the matrix as orthonormal. This will force inverted() // to compute m3.inverted() the long way. We can then compare // the result to what the faster algorithm produces on m2. QMatrix4x4 m3 = m2; - m3.optimize(); + m3(0, 0); bool invertible; QVERIFY(qFuzzyCompare(m2.inverted(&invertible), m3.inverted())); QVERIFY(invertible); @@ -1920,7 +1932,7 @@ void tst_QMatrixNxN::orthonormalInverse4x4() QMatrix4x4 m4; m4.rotate(45.0, 0.0, 1.0, 0.0); QMatrix4x4 m5 = m4; - m5.optimize(); + m5(0, 0); QVERIFY(qFuzzyCompare(m4.inverted(), m5.inverted())); QMatrix4x4 m6; @@ -1928,7 +1940,7 @@ void tst_QMatrixNxN::orthonormalInverse4x4() m1.translate(-20.0, 20.0, 15.0); m1.rotate(25, 1.0, 0.0, 0.0); QMatrix4x4 m7 = m6; - m7.optimize(); + m7(0, 0); QVERIFY(qFuzzyCompare(m6.inverted(), m7.inverted())); } @@ -2449,6 +2461,11 @@ void tst_QMatrixNxN::normalMatrix_data() 0.0f, 7.0f, 0.0f, 5.0f, 0.0f, 0.0f, 9.0f, -3.0f, 0.0f, 0.0f, 0.0f, 1.0f}; + static qreal const rotateValues[16] = + {0.0f, 0.0f, 1.0f, 0.0f, + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f}; static qreal const nullScaleValues1[16] = {0.0f, 0.0f, 0.0f, 4.0f, 0.0f, 7.0f, 0.0f, 5.0f, @@ -2468,6 +2485,7 @@ void tst_QMatrixNxN::normalMatrix_data() QTest::newRow("translate") << (void *)translateValues; QTest::newRow("scale") << (void *)scaleValues; QTest::newRow("both") << (void *)bothValues; + QTest::newRow("rotate") << (void *)rotateValues; QTest::newRow("null scale 1") << (void *)nullScaleValues1; QTest::newRow("null scale 2") << (void *)nullScaleValues2; QTest::newRow("null scale 3") << (void *)nullScaleValues3; @@ -2844,11 +2862,13 @@ void tst_QMatrixNxN::convertGeneric() // Copy of "flagBits" in qmatrix4x4.h. enum { - Identity = 0x0001, // Identity matrix - General = 0x0002, // General matrix, unknown contents - Translation = 0x0004, // Contains a simple translation - Scale = 0x0008, // Contains a simple scale - Rotation = 0x0010 // Contains a simple rotation + Identity = 0x0000, // Identity matrix + Translation = 0x0001, // Contains a translation + Scale = 0x0002, // Contains a scale + Rotation2D = 0x0004, // Contains a rotation about the Z axis + Rotation = 0x0008, // Contains an arbitrary rotation + Perspective = 0x0010, // Last row is different from (0, 0, 0, 1) + General = 0x001f // General matrix, unknown contents }; // Structure that allows direct access to "flagBits" for testing. @@ -2886,17 +2906,73 @@ void tst_QMatrixNxN::optimize_data() 0.0f, 0.0f, 1.0f, 4.0f, 0.0f, 0.0f, 0.0f, 1.0f }; - QTest::newRow("scale") + QTest::newRow("translate") << (void *)translateValues << (int)Translation; - static qreal bothValues[16] = { + static qreal scaleTranslateValues[16] = { 1.0f, 0.0f, 0.0f, 2.0f, 0.0f, 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 4.0f, 0.0f, 0.0f, 0.0f, 1.0f }; - QTest::newRow("both") - << (void *)bothValues << (int)(Scale | Translation); + QTest::newRow("scaleTranslate") + << (void *)scaleTranslateValues << (int)(Scale | Translation); + + static qreal rotateValues[16] = { + 0.0f, 1.0f, 0.0f, 0.0f, + -1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + }; + QTest::newRow("rotate") + << (void *)rotateValues << (int)Rotation2D; + + // Left-handed system, not a simple rotation. + static qreal scaleRotateValues[16] = { + 0.0f, 1.0f, 0.0f, 0.0f, + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + }; + QTest::newRow("scaleRotate") + << (void *)scaleRotateValues << (int)(Scale | Rotation2D); + + static qreal matrix2x2Values[16] = { + 1.0f, 2.0f, 0.0f, 0.0f, + 8.0f, 3.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 9.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + }; + QTest::newRow("matrix2x2") + << (void *)matrix2x2Values << (int)(Scale | Rotation2D); + + static qreal matrix3x3Values[16] = { + 1.0f, 2.0f, 4.0f, 0.0f, + 8.0f, 3.0f, 5.0f, 0.0f, + 6.0f, 7.0f, 9.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + }; + QTest::newRow("matrix3x3") + << (void *)matrix3x3Values << (int)(Scale | Rotation2D | Rotation); + + static qreal rotateTranslateValues[16] = { + 0.0f, 1.0f, 0.0f, 1.0f, + -1.0f, 0.0f, 0.0f, 2.0f, + 0.0f, 0.0f, 1.0f, 3.0f, + 0.0f, 0.0f, 0.0f, 1.0f + }; + QTest::newRow("rotateTranslate") + << (void *)rotateTranslateValues << (int)(Translation | Rotation2D); + + // Left-handed system, not a simple rotation. + static qreal scaleRotateTranslateValues[16] = { + 0.0f, 1.0f, 0.0f, 1.0f, + 1.0f, 0.0f, 0.0f, 2.0f, + 0.0f, 0.0f, 1.0f, 3.0f, + 0.0f, 0.0f, 0.0f, 1.0f + }; + QTest::newRow("scaleRotateTranslate") + << (void *)scaleRotateTranslateValues << (int)(Translation | Scale | Rotation2D); static qreal belowValues[16] = { 1.0f, 0.0f, 0.0f, 0.0f, @@ -3240,7 +3316,6 @@ void tst_QMatrixNxN::mapVector() QFETCH(void *, mValues); QMatrix4x4 m1((const qreal *)mValues); - m1.optimize(); QVector3D v(3.5f, -1.0f, 2.5f); @@ -3250,10 +3325,15 @@ void tst_QMatrixNxN::mapVector() v.x() * m1(2, 0) + v.y() * m1(2, 1) + v.z() * m1(2, 2)); QVector3D actual = m1.mapVector(v); + m1.optimize(); + QVector3D actual2 = m1.mapVector(v); QVERIFY(fuzzyCompare(actual.x(), expected.x())); QVERIFY(fuzzyCompare(actual.y(), expected.y())); QVERIFY(fuzzyCompare(actual.z(), expected.z())); + QVERIFY(fuzzyCompare(actual2.x(), expected.x())); + QVERIFY(fuzzyCompare(actual2.y(), expected.y())); + QVERIFY(fuzzyCompare(actual2.z(), expected.z())); } class tst_QMatrixNxN4x4Properties : public QObject diff --git a/tests/auto/v8.pro b/tests/auto/v8.pro new file mode 100644 index 0000000000..226ff7b118 --- /dev/null +++ b/tests/auto/v8.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs +SUBDIRS = \ + v8 diff --git a/tests/auto/v8/Makefile.nonqt b/tests/auto/v8/Makefile.nonqt new file mode 100644 index 0000000000..ded1ba3a74 --- /dev/null +++ b/tests/auto/v8/Makefile.nonqt @@ -0,0 +1,16 @@ +V8PATH = ../../../src/3rdparty/v8 +V8LIBPATH = $(V8PATH) +V8INCPATH = $(V8PATH)/include +SOURCES = v8main.cpp v8test.cpp + +release-m32: + g++ -o v8test_release_m32 -m32 -O2 -I$(V8INCPATH) $(SOURCES) -lpthread -L$(V8LIBPATH) -lv8 + +debug-m32: + g++ -o v8test_debug_m32 -m32 -g -I$(V8INCPATH) $(SOURCES) -lpthread -L$(V8LIBPATH) -lv8_g + +release: + g++ -o v8test_release -O2 -I$(V8INCPATH) $(SOURCES) -lpthread -L$(V8LIBPATH) -lv8 + +debug: + g++ -o v8test_debug -g -I$(V8INCPATH) $(SOURCES) -lpthread -L$(V8LIBPATH) -lv8_g diff --git a/tests/auto/v8/README.txt b/tests/auto/v8/README.txt new file mode 100644 index 0000000000..097c459036 --- /dev/null +++ b/tests/auto/v8/README.txt @@ -0,0 +1,13 @@ +The v8 tests are actually implemented in v8test.[h|cpp]. There are also QtTest +(tst_v8.cpp) and non-Qt (v8main.cpp) stubs provided to run these tests. This +is done to allow the tests to be run both in the Qt CI system, and manually +without a build of Qt. The latter is necessary to run them against more exotic +build of V8, like the ARM simulator. + +To build the non-Qt version of the tests, first build a debug or release V8 +library under src/3rdparty/v8 using scons, and then use the Makefile.nonqt +makefile selecting one of the following targets: + release: Build the tests with -O2 and link against libv8 + debug: Build the tests with -g and link against libv8_g + release-m32: Build the tests with -O2 -m32 and link against libv8 + debug-m32: Build the tests with -g -m32 and link against libv8_g diff --git a/tests/auto/v8/tst_v8.cpp b/tests/auto/v8/tst_v8.cpp new file mode 100644 index 0000000000..4ff80067c5 --- /dev/null +++ b/tests/auto/v8/tst_v8.cpp @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <qtest.h> +#include <private/v8.h> +#include "v8test.h" + +using namespace v8; + +class tst_v8 : public QObject +{ + Q_OBJECT +public: + tst_v8() {} + +private slots: + void initTestCase() {} + void cleanupTestCase() {} + + void eval(); + void userobjectcompare(); +}; + +void tst_v8::eval() +{ + QVERIFY(v8test_eval()); +} + +void tst_v8::userobjectcompare() +{ + QVERIFY(v8test_userobjectcompare()); +} + +int main(int argc, char *argv[]) +{ + V8::SetFlagsFromCommandLine(&argc, argv, true); + + QCoreApplication app(argc, argv); + tst_v8 tc; + return QTest::qExec(&tc, argc, argv); +} + +#include "tst_v8.moc" diff --git a/tests/auto/v8/v8.pro b/tests/auto/v8/v8.pro new file mode 100644 index 0000000000..feda53bf50 --- /dev/null +++ b/tests/auto/v8/v8.pro @@ -0,0 +1,9 @@ +load(qttest_p4) +macx:CONFIG -= app_bundle + +SOURCES += tst_v8.cpp v8test.cpp +HEADERS += v8test.h + +CONFIG += parallel_test + +QT += v8-private diff --git a/tests/auto/v8/v8main.cpp b/tests/auto/v8/v8main.cpp new file mode 100644 index 0000000000..fa0137938e --- /dev/null +++ b/tests/auto/v8/v8main.cpp @@ -0,0 +1,17 @@ +#include "v8test.h" +#include <stdio.h> + +#define RUN_TEST(testname) { \ + if (!v8test_ ## testname()) \ + printf ("Test %s FAILED\n", # testname); \ +} + +int main(int argc, char *argv[]) +{ + v8::V8::SetFlagsFromCommandLine(&argc, argv, true); + + RUN_TEST(eval); + RUN_TEST(userobjectcompare); + + return -1; +} diff --git a/tests/auto/v8/v8test.cpp b/tests/auto/v8/v8test.cpp new file mode 100644 index 0000000000..a712bf12b4 --- /dev/null +++ b/tests/auto/v8/v8test.cpp @@ -0,0 +1,254 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "v8test.h" + +using namespace v8; + +#define BEGINTEST() bool _testPassed = true; +#define ENDTEST() return _testPassed; + +#define VERIFY(expr) { \ + if (!(expr)) { \ + fprintf(stderr, "FAIL: %s:%d %s\n", __FILE__, __LINE__, # expr); \ + _testPassed = false; \ + goto cleanup; \ + } \ +} + + +bool v8test_eval() +{ + BEGINTEST(); + + HandleScope handle_scope; + Persistent<Context> context = Context::New(); + Context::Scope context_scope(context); + + Local<Object> qmlglobal = Object::New(); + qmlglobal->Set(String::New("a"), Integer::New(1922)); + + Local<Script> script = Script::Compile(String::New("eval(\"a\")"), NULL, NULL, + Handle<String>(), Script::QmlMode); + + TryCatch tc; + Local<Value> result = script->Run(qmlglobal); + + VERIFY(!tc.HasCaught()); + VERIFY(result->Int32Value() == 1922); + +cleanup: + context.Dispose(); + + ENDTEST(); +} + +static int userObjectComparisonCalled = 0; +static bool userObjectComparisonReturn = false; +static Local<Object> expectedLhs; +static Local<Object> expectedRhs; +static bool expectedObjectsCompared = false; + +#define SET_EXPECTED(lhs, rhs) { \ + expectedObjectsCompared = false; \ + expectedLhs = lhs; \ + expectedRhs = rhs; \ +} + +static bool UserObjectComparison(Local<Object> lhs, Local<Object> rhs) +{ + userObjectComparisonCalled++; + + expectedObjectsCompared = (lhs == expectedLhs && rhs == expectedRhs); + + return userObjectComparisonReturn; +} + +inline bool runscript(const char *source) { + Local<Script> script = Script::Compile(String::New(source)); + Local<Value> result = script->Run(); + return result->BooleanValue(); +} + +bool v8test_userobjectcompare() +{ + BEGINTEST(); + + HandleScope handle_scope; + Persistent<Context> context = Context::New(); + Context::Scope context_scope(context); + + V8::SetUserObjectComparisonCallbackFunction(UserObjectComparison); + + Local<ObjectTemplate> ot = ObjectTemplate::New(); + ot->MarkAsUseUserObjectComparison(); + + Local<Object> uoc1 = ot->NewInstance(); + Local<Object> uoc2 = ot->NewInstance(); + context->Global()->Set(String::New("uoc1a"), uoc1); + context->Global()->Set(String::New("uoc1b"), uoc1); + context->Global()->Set(String::New("uoc2"), uoc2); + Local<Object> obj1 = Object::New(); + context->Global()->Set(String::New("obj1a"), obj1); + context->Global()->Set(String::New("obj1b"), obj1); + context->Global()->Set(String::New("obj2"), Object::New()); + Local<String> string1 = String::New("Hello World"); + context->Global()->Set(String::New("string1a"), string1); + context->Global()->Set(String::New("string1b"), string1); + context->Global()->Set(String::New("string2"), v8::String::New("Goodbye World")); + + // XXX Opportunity for optimization - don't invoke user callback if objects are + // equal. +#if 0 + userObjectComparisonCalled = 0; userObjectComparisonReturn = false; + VERIFY(true == runscript("uoc1a == uoc1b")); + VERIFY(userObjectComparisonCalled == 0); +#endif + + // Comparing two uoc objects invokes uoc + userObjectComparisonCalled = 0; + userObjectComparisonReturn = false; + VERIFY(false == runscript("uoc1a == uoc2")); + VERIFY(userObjectComparisonCalled == 1); + + VERIFY(false == runscript("uoc2 == uoc1a")); + VERIFY(userObjectComparisonCalled == 2); + userObjectComparisonReturn = true; + VERIFY(true == runscript("uoc1a == uoc2")); + VERIFY(userObjectComparisonCalled == 3); + VERIFY(true == runscript("uoc2 == uoc1a")); + VERIFY(userObjectComparisonCalled == 4); + + // != on two uoc object invokes uoc + userObjectComparisonCalled = 0; + userObjectComparisonReturn = false; + VERIFY(true == runscript("uoc1a != uoc2")); + VERIFY(userObjectComparisonCalled == 1); + VERIFY(true == runscript("uoc2 != uoc1a")); + VERIFY(userObjectComparisonCalled == 2); + userObjectComparisonReturn = true; + VERIFY(false == runscript("uoc1a != uoc2")); + VERIFY(userObjectComparisonCalled == 3); + VERIFY(false == runscript("uoc2 != uoc1a")); + VERIFY(userObjectComparisonCalled == 4); + + // Comparison against a non-object doesn't invoke uoc + userObjectComparisonCalled = 0; + userObjectComparisonReturn = false; + VERIFY(false == runscript("uoc1a == string1a")); + VERIFY(userObjectComparisonCalled == 0); + VERIFY(false == runscript("string1a == uoc1a")); + VERIFY(userObjectComparisonCalled == 0); + VERIFY(false == runscript("2 == uoc1a")); + VERIFY(userObjectComparisonCalled == 0); + VERIFY(true == runscript("uoc1a != string1a")); + VERIFY(userObjectComparisonCalled == 0); + VERIFY(true == runscript("string1a != uoc1a")); + VERIFY(userObjectComparisonCalled == 0); + VERIFY(true == runscript("2 != uoc1a")); + VERIFY(userObjectComparisonCalled == 0); + + // Comparison against a non-uoc-object still invokes uoc + userObjectComparisonCalled = 0; + userObjectComparisonReturn = false; + VERIFY(false == runscript("uoc1a == obj1a")); + VERIFY(userObjectComparisonCalled == 1); + VERIFY(false == runscript("obj1a == uoc1a")); + VERIFY(userObjectComparisonCalled == 2); + userObjectComparisonReturn = true; + VERIFY(true == runscript("uoc1a == obj1a")); + VERIFY(userObjectComparisonCalled == 3); + VERIFY(true == runscript("obj1a == uoc1a")); + VERIFY(userObjectComparisonCalled == 4); + + // != comparison against a non-uoc-object still invokes uoc + userObjectComparisonCalled = 0; + userObjectComparisonReturn = false; + VERIFY(true == runscript("uoc1a != obj1a")); + VERIFY(userObjectComparisonCalled == 1); + VERIFY(true == runscript("obj1a != uoc1a")); + VERIFY(userObjectComparisonCalled == 2); + userObjectComparisonReturn = true; + VERIFY(false == runscript("uoc1a != obj1a")); + VERIFY(userObjectComparisonCalled == 3); + VERIFY(false == runscript("obj1a != uoc1a")); + VERIFY(userObjectComparisonCalled == 4); + + // Comparing two non-uoc objects does not invoke uoc + userObjectComparisonCalled = 0; + userObjectComparisonReturn = false; + VERIFY(true == runscript("obj1a == obj1a")); + VERIFY(true == runscript("obj1a == obj1b")); + VERIFY(false == runscript("obj1a == obj2")); + VERIFY(false == runscript("obj1a == string1a")); + VERIFY(true == runscript("string1a == string1a")); + VERIFY(true == runscript("string1a == string1b")); + VERIFY(false == runscript("string1a == string2")); + VERIFY(userObjectComparisonCalled == 0); + + // Correct lhs and rhs passed to uoc + userObjectComparisonCalled = 0; + userObjectComparisonReturn = false; + SET_EXPECTED(uoc1, uoc2); + VERIFY(false == runscript("uoc1a == uoc2")); + VERIFY(true == expectedObjectsCompared); + SET_EXPECTED(uoc2, uoc1); + VERIFY(false == runscript("uoc2 == uoc1a")); + VERIFY(true == expectedObjectsCompared); + SET_EXPECTED(uoc1, uoc2); + VERIFY(true == runscript("uoc1a != uoc2")); + VERIFY(true == expectedObjectsCompared); + SET_EXPECTED(uoc2, uoc1); + VERIFY(true == runscript("uoc2 != uoc1a")); + VERIFY(true == expectedObjectsCompared); + SET_EXPECTED(uoc1, obj1); + VERIFY(false == runscript("uoc1a == obj1a")); + VERIFY(true == expectedObjectsCompared); + SET_EXPECTED(obj1, uoc1); + VERIFY(false == runscript("obj1a == uoc1a")); + VERIFY(true == expectedObjectsCompared); + +cleanup: + V8::SetUserObjectComparisonCallbackFunction(0); + context.Dispose(); + + ENDTEST(); +} diff --git a/tests/auto/v8/v8test.h b/tests/auto/v8/v8test.h new file mode 100644 index 0000000000..812036dd66 --- /dev/null +++ b/tests/auto/v8/v8test.h @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef V8TEST_H +#define V8TEST_H + +#ifdef QT_CORE_LIB +#include <private/v8.h> +#else +#include <v8.h> +#endif + +bool v8test_eval(); +bool v8test_userobjectcompare(); + +#endif // V8TEST_H + diff --git a/tools/configure/configureapp.cpp b/tools/configure/configureapp.cpp index e980755305..eafc6690c8 100644 --- a/tools/configure/configureapp.cpp +++ b/tools/configure/configureapp.cpp @@ -282,6 +282,7 @@ Configure::Configure(int& argc, char** argv) dictionary[ "WMSDK" ] = "auto"; dictionary[ "DIRECTSHOW" ] = "no"; dictionary[ "WEBKIT" ] = "auto"; + dictionary[ "V8" ] = "auto"; dictionary[ "DECLARATIVE" ] = "auto"; dictionary[ "DECLARATIVE_DEBUG" ]= "yes"; dictionary[ "PLUGIN_MANIFESTS" ] = "yes"; @@ -991,6 +992,10 @@ void Configure::parseCmdLine() dictionary[ "WEBKIT" ] = "yes"; } else if (configCmdLine.at(i) == "-webkit-debug") { dictionary[ "WEBKIT" ] = "debug"; + } else if (configCmdLine.at(i) == "-no-v8") { + dictionary[ "V8" ] = "no"; + } else if (configCmdLine.at(i) == "-v8") { + dictionary[ "V8" ] = "yes"; } else if (configCmdLine.at(i) == "-no-declarative") { dictionary[ "DECLARATIVE" ] = "no"; } else if (configCmdLine.at(i) == "-declarative") { @@ -1876,6 +1881,8 @@ bool Configure::displayHelp() desc("SCRIPT", "yes", "-script", "Build the QtScript module."); desc("SCRIPTTOOLS", "no", "-no-scripttools", "Do not build the QtScriptTools module."); desc("SCRIPTTOOLS", "yes", "-scripttools", "Build the QtScriptTools module."); + desc("V8", "no", "-no-v8", "Do not build the V8 module."); + desc("V8", "yes", "-v8", "Build the V8 module."); desc("DECLARATIVE", "no", "-no-declarative", "Do not build the declarative module"); desc("DECLARATIVE", "yes", "-declarative", "Build the declarative module"); desc("DECLARATIVE_DEBUG", "no", "-no-declarative-debug", "Do not build the declarative debugging support"); @@ -2190,7 +2197,7 @@ bool Configure::checkAvailability(const QString &part) } } else if (part == "WMSDK") { available = findFile("wmsdk.h"); - } else if (part == "MULTIMEDIA" || part == "SCRIPT" || part == "SCRIPTTOOLS" || part == "DECLARATIVE") { + } else if (part == "MULTIMEDIA" || part == "SCRIPT" || part == "SCRIPTTOOLS" || part == "V8" || part == "DECLARATIVE") { available = true; } else if (part == "WEBKIT") { available = (dictionary.value("QMAKESPEC") == "win32-msvc2005") || (dictionary.value("QMAKESPEC") == "win32-msvc2008") || (dictionary.value("QMAKESPEC") == "win32-msvc2010") || (dictionary.value("QMAKESPEC") == "win32-g++"); @@ -2333,8 +2340,10 @@ void Configure::autoDetection() dictionary["PHONON"] = checkAvailability("PHONON") ? "yes" : "no"; if (dictionary["WEBKIT"] == "auto") dictionary["WEBKIT"] = checkAvailability("WEBKIT") ? "yes" : "no"; + if (dictionary["V8"] == "auto") + dictionary["V8"] = checkAvailability("V8") ? "yes" : "no"; if (dictionary["DECLARATIVE"] == "auto") - dictionary["DECLARATIVE"] = dictionary["SCRIPT"] == "yes" ? "yes" : "no"; + dictionary["DECLARATIVE"] = dictionary["V8"] == "yes" ? "yes" : "no"; if (dictionary["DECLARATIVE_DEBUG"] == "auto") dictionary["DECLARATIVE_DEBUG"] = dictionary["DECLARATIVE"] == "yes" ? "yes" : "no"; if (dictionary["AUDIO_BACKEND"] == "auto") @@ -2392,9 +2401,9 @@ bool Configure::verifyConfiguration() if (!(l.contains(dictionary["ARM_FPU_TYPE"]))) cout << QString("WARNING: Using unsupported fpu flag: %1").arg(dictionary["ARM_FPU_TYPE"]) << endl; } - if (dictionary["DECLARATIVE"] == "yes" && dictionary["SCRIPT"] == "no") { + if (dictionary["DECLARATIVE"] == "yes" && dictionary["V8"] == "no") { cout << "WARNING: To be able to compile QtDeclarative we need to also compile the" << endl - << "QtScript module. If you continue, we will turn on the QtScript module." << endl + << "V8 module. If you continue, we will turn on the V8 module." << endl << "(Press any key to continue..)"; if (_getch() == 3) // _Any_ keypress w/no echo(eat <Enter> for stdout) exit(0); // Exit cleanly for Ctrl+C @@ -2754,8 +2763,8 @@ void Configure::generateOutputVars() // No longer needed after modularization // if (dictionary["DECLARATIVE"] == "yes") { -// if (dictionary[ "SCRIPT" ] == "no") { -// cout << "QtDeclarative was requested, but it can't be built due to QtScript being " +// if (dictionary[ "V8" ] == "no") { +// cout << "QtDeclarative was requested, but it can't be built due to V8 being " // "disabled." << endl; // dictionary[ "DONE" ] = "error"; // } @@ -3186,6 +3195,7 @@ void Configure::generateConfigfiles() if (dictionary["OPENSSL"] == "linked") qconfigList += "QT_LINKED_OPENSSL"; if (dictionary["DBUS"] == "no") qconfigList += "QT_NO_DBUS"; if (dictionary["WEBKIT"] == "no") qconfigList += "QT_NO_WEBKIT"; + if (dictionary["V8"] == "no") qconfigList += "QT_NO_V8"; if (dictionary["DECLARATIVE"] == "no") qconfigList += "QT_NO_DECLARATIVE"; if (dictionary["DECLARATIVE_DEBUG"] == "no") qconfigList += "QDECLARATIVE_NO_DEBUG_PROTOCOL"; if (dictionary["PHONON"] == "no") qconfigList += "QT_NO_PHONON"; @@ -3480,6 +3490,7 @@ void Configure::displayConfig() if (declarative == "yes") cout << "Declarative debugging......." << dictionary[ "DECLARATIVE_DEBUG" ] << endl; } + cout << "V8 support.................." << dictionary[ "V8" ] << endl; cout << "QtScript support............" << dictionary[ "SCRIPT" ] << endl; cout << "QtScriptTools support......." << dictionary[ "SCRIPTTOOLS" ] << endl; cout << "Graphics System............." << dictionary[ "GRAPHICS_SYSTEM" ] << endl; |