ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

PlantUML 离线编译 TeaVM 依赖的占位方案:clide_stubs/teavm 编译期 Stub 设计、构建与集成

PlantUML 离线编译 TeaVM 依赖的占位方案:clide_stubs/teavm 编译期 Stub 设计、构建与集成 PlantUML 离线编译 TeaVM 依赖的占位方案clide_stubs/teavm 编译期 Stub 设计、构建与集成【免费下载链接】plantumlGenerate diagrams from textual description项目地址: https://gitcode.com/gh_mirrors/pl/plantuml本篇技术指南聚焦 PlantUML 仓库中tools/clide_stubs/teavm这一编译期专用 Stub 工程它用极小的org.teavm.*类型占位实现让clide/jdtls 在无网络、无法访问 Maven Central 的环境中也能编译 PlantUML 的 TeaVM 专用源码。读完本文你将掌握该 Stub jar 的设计原理为何能只编译、不运行、它所覆盖的 14 类 TeaVM 类型、ant构建与.clide/集成方式以及 PlantUML 侧对等源码如何实际消费这些类型。一、背景为什么 PlantUML 需要一份 TeaVM 编译期占位 jarPlantUML 具备一套基于 TeaVM 的浏览器端渲染前端相关源码集中在 src/main/java/net/sourceforge/plantuml/teavm并散落在PortableImageTeaVM.java、Emoji.java、OpenIconic.java、SignatureUtils.java、PathSystem.java等文件中。这些文件直接import org.teavm.*下的 JS 互操作类型例如 GraphVizjsTeaVMEngine.java 中的org.teavm.jso.JSObject。问题在于真实 TeaVM 依赖需要从 Maven Central 下载而离线沙箱环境没有网络。为了让clide/jdtls 这类基于 Eclipse JDT Language Server 的代码工具能够编译这些源文件仓库在 tools/clide_stubs/teavm 下维护了一组最小化替身。这份 Stub jar 的定位在 tools/clide_stubs/teavm/README.md 中写得很明确唯一目的是让clide/jdtls编译TeaVM 专用源码绝不用于实际运行不提供任何真实 JavaScript 互操作纯粹是为了满足 Java 编译器若真实 TeaVM API 无法到达沙箱无网络Stub 就作为编译期的类型占位。同一思路也体现在仓库的其他两个 Stub 工程上tools/clide_stubs/antApache Ant 类型占位与tools/clide_stubs/openpdfOpenPDF 类型占位说明这是一套体系化的离线编译解决方案。二、Stub 覆盖的类型清单Stub 源码位于 tools/clide_stubs/teavm/src/org/teavm共 16 个文件覆盖以下 14 种org.teavm.*类型见原文档类型表类型种类org.teavm.jso.JSObject标记接口marker interfaceorg.teavm.jso.JSBody注解org.teavm.jso.JSExport注解org.teavm.jso.JSFunctor注解org.teavm.interop.Async注解org.teavm.interop.AsyncCallbackT接口org.teavm.interop.PlatformMarker注解org.teavm.jso.dom.xml.Element接口org.teavm.jso.dom.xml.Document接口org.teavm.jso.dom.html.HTMLElement接口org.teavm.jso.dom.html.HTMLDocument接口含一个抛异常静态方法org.teavm.jso.dom.html.HTMLCanvasElement接口org.teavm.jso.canvas.CanvasRenderingContext2D接口org.teavm.jso.canvas.ImageData接口此外类型表中还有org.teavm.jso.typedarrays.Uint8ClampedArray接口JSObject标记、set(int,int)方法。这些类型并非照抄真实 TeaVM API 的全貌而是按 PlantUML 实际调用面裁剪接口只声明 PlantUML 源码真正用到的方法其余一概省略。例如 Element.java 只声明了setAttribute(String, String)、appendChild(Element)、setTextContent(String)三个方法CanvasRenderingContext2D.java 只声明createImageData(int, int)与putImageData(ImageData, int, int)对应 PlantUML PNG 导出路径的真实调用详见第五节。三、如何保持惰性Stub 的三个设计支柱原文档用 How it stays inert 一节解释了这份 jar 为何既能让编译通过、又不可能被误用于运行其原理可归纳为三点均有对应源码佐证3.1 绝大多数类型是纯接口抽象方法无方法体真实 TeaVM API 中org.teavm.*的 JS 后端对象基本都是接口JS-backed 对象没有真正的 Java 实现因此 Stub 同样以接口声明、方法无方法体。比如 JSObject.java 是刻意为空的标记接口——所有 JS 后端类型都继承它Document.java 同样为空仅继承JSObject。3.2 唯一带方法体的HTMLDocument.current()直接抛异常PlantUML 代码获取 DOM 文档的唯一入口是静态方法HTMLDocument.current()。Java 8 起接口允许静态方法所以它是整个 Stub 中唯一真正需要方法体的地方。其实现见 HTMLDocument.javastatic HTMLDocument current() { throw new UnsupportedOperationException( TeaVM stub: HTMLDocument.current() is only meant to satisfy the compiler, never to run.); }这一设计一举两得既然current()永远抛异常那么下游任何代码都无法获得这些类型的真实实例其余所有方法如createElement、getElementById保持抽象、在实践上永远不可达——编译期它提供了类型形状运行期它保证了绝对安全。3.3 注解全部是纯标记无任何逻辑JSBody、JSExport、JSFunctor、Async、PlatformMarker这五个注解在真实 TeaVM 中都带有编译器语义但在 Stub 里只是形状JSBody.java真实语义是将被注解的native方法替换为指定 JavaScript 片段Stub 中仅保留String[] params() default {}与String script()两个成员JSExport.java真实语义是把静态方法导出到生成的 JS 模块Stub 为空标记JSFunctor.java真实语义标记单方法JSObject接口为 JS 回调类型Stub 为空标记Async.java真实语义把异步 JS 调用变成貌似同步的native方法Stub 为空标记PlatformMarker.java真实语义让 TeaVM 编译器按目标平台替换方法体例如isTeaVM()编译期常量Stub 为空标记AsyncCallback.javaAsync方法底层接收的回调接口保留complete(T)与error(Throwable)两个抽象方法但同样没有任何实现——因为 jar 永远不会发出真实实例。由于这些注解不触发任何行为PlantUML 被它们标注的方法保持native或原样不变Stub 无需提供任何运行逻辑。四、构建与集成到 clide/jdtls4.1 构建命令在tools/clide_stubs/teavm目录下执行ant产物为teavm-stub.jar。这是典型的 Ant 工程结构src/源码目录 ant构建与tools/clide_stubs下另外两个 Stub 工程保持一致。4.2 集成到.clide/目录构建出 jar 后将它复制到项目的.clide/目录该机制详见clide项目的JDTLS.md文档。clide的open_project命令会自动拾取.clide/下的 jar 并加入 jdtls 的 classpath从而让 TeaVM 专用源码得以解析。4.3 验证方式原文档给出了明确的验证口径将 jar 放入.clide/后通过clide的print_diagnostics errors确认所有org.teavm相关的编译错误消失此时剩余的错误均来自其他无关依赖OpenPDF、XMLUnit、Mockito 等。这意味着 Stub 的覆盖范围已经完整满足 PlantUML TeaVM 源码的编译需求。五、PlantUML 侧的真实消费点为何够用要理解 Stub 为何按上述方法裁剪可以对照 PlantUML 侧的实际调用。下面这些使用点与 Stub 类型一一对应形成了声明-使用闭环5.1HTMLDocument.current()全局唯一文档工厂SvgGraphicsTeaVM.java 的构造函数第一行就是this.document HTMLDocument.current();随后通过createElement/appendChild构建 SVG 根节点PortableImageTeaVM.java 的toPngDataUrl()用HTMLDocument.current().createElement(canvas)创建离屏画布PlantUMLBrowser.java 用HTMLDocument.current().getElementById(elementId)定位渲染目标元素。正因为current()是唯一的实例入口把它做成抛异常的方法即可从根上杜绝 Stub 被运行时误用——这正是第三节第 3.2 点设计的用意。5.2JSBodyAsyncJSFunctorViz.js 异步渲染桥GraphVizjsTeaVMEngine.java 是典型组合JSBody提供vizMissingFallback()L73-L79检测window.Viz是否加载Async修饰renderDotToSvg(String)L81-L82配合AsyncCallbackString把 JS Promise 变成貌似同步的 Java 调用L88-L90JSFunctor声明StringCallback单方法回调接口L153-L156。5.3JSExport导出浏览器渲染入口PlantUMLBrowser.java 用JSExport导出render(String[], String, JSObject)L283-L284与renderToString(...)L315-L317供 JS 侧调用JSBody解析options中的dark、maxSvgSize等字段L339-L348。5.4PlatformMarker编译期死代码消除TeaVM.java 的isTeaVM()被org.teavm.interop.PlatformMarker标注。在 TeaVM 编译时该方法被静态解析为true于是if (TeaVM.isTeaVM())这类分支在编译期就被消解未分支整体从生成的 JS 中剔除在 JVM 上则始终返回false不触发任何消除。5.5 画布与像素数组PNG 导出路径PortableImageTeaVM.java 的toPngDataUrl()完整串起了 Canvas 相关 StubHTMLCanvasElement的setWidth/setHeight/getContext(2d)→CanvasRenderingContext2D的createImageData/putImageData→ImageData的getData()→Uint8ClampedArray的set(...)最后JSBody调用canvas.toDataURL(image/png)L197-L198。5.6 其他 TeaVM 消费点SvgGraphicsTeaVM.java 用JSBodyXMLSerializer序列化 SVG 为字符串用共享 canvas 做文本测量L443-L445TeaVMSvgDocument.java 用JSBody创建 SVG 命名空间元素Emoji.java 与 OpenIconic.java 用JSBody读取window.PLANTUML_EMOJI_SHORTCUT、window.PLANTUML_EMOJI、window.PLANTUML_OPENICONIC等全局对象PathSystem.java 使用org.teavm.jso.JSObject。从源码结构看Stub 类型的方法裁剪正是逐一对齐上述调用点的结果getContext在 Stub 中返回通用的JSObject而非真实 TeaVM 的更宽泛返回类型是因为 PlantUML 随即将其强转为CanvasRenderingContext2D而接口到接口的强转无论有无继承关系都能编译通过见 HTMLCanvasElement.java 的注释说明。六、使用边界与注意事项严禁用于运行时Stub jar 没有任何 JavaScript 互操作能力HTMLDocument.current()的调用会立即抛出UnsupportedOperationException。它只服务于编译期类型解析绝不可进入运行时 classpath。按需最小化Stub 方法签名是 PlantUML 实际调用面的最小集合并非完整 TeaVM API。若 PlantUML TeaVM 源码未来新增对其他org.teavm.*方法或类型的引用需要在tools/clide_stubs/teavm/src/org/teavm下补充对应占位。编译验证的局限Stub 只消除org.teavm相关错误。如原文档所述其余依赖OpenPDF、XMLUnit、Mockito 等造成的编译错误需要分别由tools/clide_stubs/openpdf等其他 Stub 或对应处理机制解决不能指望本 jar 一并覆盖。JAVA8 兼容性HTMLDocument.current()作为静态接口方法依赖 Java 8 语法这也与 PlantUML 源码中// ::remove file when JAVA8这类条件编译标记见 GraphVizjsTeaVMEngine.java所反映的 Java 版本策略一致。七、结语tools/clide_stubs/teavm是一个小而巧的工程用 16 个源文件、14 类类型就为离线环境解开了 PlantUML TeaVM 前端源码的编译死结。它的价值不在于实现任何功能而在于精准回答编译器需要看到什么——接口形状 注解骨架 一个抛异常的工厂方法。结合 tools/clide_stubs/teavm/README.md 与 tools/clide_stubs/teavm/src/org/teavm 的源码注释读者可以完整复现这套编译可用、运行必败的占位策略并理解它与 src/main/java/net/sourceforge/plantuml/teavm 真实使用点之间的映射关系。【免费下载链接】plantumlGenerate diagrams from textual description项目地址: https://gitcode.com/gh_mirrors/pl/plantuml创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表