* @brief Compiles the user JS, looks up paint() and (optional) onFrame(). The evaluate runs * under the armed watchdog so a top-level infinite loop in a painter script cannot * freeze the GUI thread. */
| 676 | * freeze the GUI thread. |
| 677 | */ |
| 678 | bool Widgets::Painter::compile(const QString& code) |
| 679 | { |
| 680 | m_compileDirty = false; |
| 681 | m_paintFn = QJSValue(); |
| 682 | m_onFrameFn = QJSValue(); |
| 683 | m_onPressFn = QJSValue(); |
| 684 | m_onDragFn = QJSValue(); |
| 685 | m_onReleaseFn = QJSValue(); |
| 686 | m_onMoveFn = QJSValue(); |
| 687 | m_onWheelFn = QJSValue(); |
| 688 | m_onDoubleClickFn = QJSValue(); |
| 689 | m_hasOnFrame = false; |
| 690 | |
| 691 | if (!m_bootstrapOk) [[unlikely]] { |
| 692 | setRuntimeOk(false); |
| 693 | return false; |
| 694 | } |
| 695 | |
| 696 | const QString effectiveCode = code.isEmpty() ? fallbackTemplate() : code; |
| 697 | m_watchdog.arm(); |
| 698 | auto result = m_engine.evaluate(effectiveCode, QStringLiteral("painter.js")); |
| 699 | m_watchdog.disarm(); |
| 700 | |
| 701 | if (m_engine.isInterrupted()) { |
| 702 | m_engine.setInterrupted(false); |
| 703 | setLastError(QStringLiteral("compile: script did not finish evaluating within %1 ms " |
| 704 | "(infinite loop at the top level?)") |
| 705 | .arg(kPainterWatchdogMs)); |
| 706 | setRuntimeOk(false); |
| 707 | return false; |
| 708 | } |
| 709 | |
| 710 | if (result.isError()) { |
| 711 | setLastError(QStringLiteral("compile: ") + result.property(QStringLiteral("message")).toString() |
| 712 | + QStringLiteral(" (line ") |
| 713 | + result.property(QStringLiteral("lineNumber")).toString() + QStringLiteral(")")); |
| 714 | setRuntimeOk(false); |
| 715 | return false; |
| 716 | } |
| 717 | |
| 718 | auto paintFn = m_engine.globalObject().property(QStringLiteral("paint")); |
| 719 | if (!paintFn.isCallable()) { |
| 720 | setLastError(QStringLiteral("missing paint(ctx, w, h) function")); |
| 721 | setRuntimeOk(false); |
| 722 | return false; |
| 723 | } |
| 724 | |
| 725 | m_paintFn = paintFn; |
| 726 | |
| 727 | auto onFrameFn = m_engine.globalObject().property(QStringLiteral("onFrame")); |
| 728 | if (onFrameFn.isCallable()) { |
| 729 | m_onFrameFn = onFrameFn; |
| 730 | m_hasOnFrame = true; |
| 731 | } |
| 732 | |
| 733 | const auto global = m_engine.globalObject(); |
| 734 | auto lookupHandler = [&global](const char* name) { |
| 735 | auto fn = global.property(QString::fromLatin1(name)); |