From cfebb018f9618c002b7025de365ea9cd6343b33f Mon Sep 17 00:00:00 2001 From: teble Date: Tue, 11 Jun 2024 08:53:17 +0000 Subject: [PATCH] Auto deploy from Github Actions --- 404.html | 33 +++ assets/404.html-60b35caa.js | 1 + assets/404.html-fd1e0a9c.js | 1 + assets/about.html-8ea67162.js | 1 + assets/about.html-9df343ae.js | 1 + assets/about.html-a49d4a97.js | 1 + assets/about.html-b3c329f5.js | 1 + assets/app-75e8845f.js | 10 + assets/back-to-top-8efcbe56.svg | 1 + assets/contacts.html-1fbcc5af.js | 1 + assets/contacts.html-335338da.js | 1 + assets/contacts.html-6120246a.js | 1 + assets/contacts.html-ecff4547.js | 1 + assets/example.html-6629fd57.js | 214 +++++++++++++++ assets/example.html-76d3f343.js | 214 +++++++++++++++ assets/example.html-d65bcfc3.js | 1 + assets/example.html-de2cdc67.js | 1 + assets/home.html-4377e437.js | 1 + assets/home.html-44ee30de.js | 1 + assets/home.html-6d2f5099.js | 1 + assets/home.html-eff1e1a9.js | 1 + assets/index.html-1cabf326.js | 83 ++++++ assets/index.html-2a017d73.js | 1 + assets/index.html-3b5329ec.js | 1 + assets/index.html-4c5e4c4a.js | 1 + assets/index.html-749b8c09.js | 1 + assets/index.html-d4986fc6.js | 81 ++++++ assets/knowledge.html-168eee0c.js | 1 + assets/knowledge.html-8e7358a7.js | 1 + assets/knowledge.html-bf240073.js | 1 + assets/knowledge.html-fa621fe0.js | 1 + .../performance-optimization.html-0b660c19.js | 40 +++ .../performance-optimization.html-4f96b155.js | 1 + .../performance-optimization.html-69f9f3ed.js | 1 + .../performance-optimization.html-91684308.js | 40 +++ assets/quick-start.html-1c002ee2.js | 1 + assets/quick-start.html-4917d6af.js | 1 + assets/quick-start.html-ba5a064e.js | 16 ++ assets/quick-start.html-d0230eac.js | 16 ++ assets/run-on-desktop.html-426dfbb6.js | 5 + assets/run-on-desktop.html-6d699af5.js | 1 + assets/run-on-desktop.html-8df4653a.js | 1 + assets/run-on-desktop.html-b6246f6d.js | 5 + assets/structural-zoom-table.html-0b909054.js | 1 + assets/structural-zoom-table.html-60176671.js | 1 + assets/structural-zoom-table.html-9be6c1fa.js | 1 + assets/structural-zoom-table.html-ab94571a.js | 1 + assets/style-0b913742.css | 1 + en/about/about.html | 33 +++ en/about/contacts.html | 33 +++ en/guide/example.html | 246 ++++++++++++++++++ en/guide/home.html | 33 +++ en/guide/knowledge.html | 33 +++ en/guide/performance-optimization.html | 72 +++++ en/guide/quick-start.html | 48 ++++ en/guide/run-on-desktop.html | 37 +++ en/guide/structural-zoom-table.html | 33 +++ en/index.html | 115 ++++++++ index.html | 33 +++ zh-cn/about/about.html | 33 +++ zh-cn/about/contacts.html | 33 +++ zh-cn/guide/example.html | 246 ++++++++++++++++++ zh-cn/guide/home.html | 33 +++ zh-cn/guide/knowledge.html | 33 +++ zh-cn/guide/performance-optimization.html | 72 +++++ zh-cn/guide/quick-start.html | 48 ++++ zh-cn/guide/run-on-desktop.html | 37 +++ zh-cn/guide/structural-zoom-table.html | 33 +++ zh-cn/index.html | 113 ++++++++ 69 files changed, 2190 insertions(+) create mode 100644 404.html create mode 100644 assets/404.html-60b35caa.js create mode 100644 assets/404.html-fd1e0a9c.js create mode 100644 assets/about.html-8ea67162.js create mode 100644 assets/about.html-9df343ae.js create mode 100644 assets/about.html-a49d4a97.js create mode 100644 assets/about.html-b3c329f5.js create mode 100644 assets/app-75e8845f.js create mode 100644 assets/back-to-top-8efcbe56.svg create mode 100644 assets/contacts.html-1fbcc5af.js create mode 100644 assets/contacts.html-335338da.js create mode 100644 assets/contacts.html-6120246a.js create mode 100644 assets/contacts.html-ecff4547.js create mode 100644 assets/example.html-6629fd57.js create mode 100644 assets/example.html-76d3f343.js create mode 100644 assets/example.html-d65bcfc3.js create mode 100644 assets/example.html-de2cdc67.js create mode 100644 assets/home.html-4377e437.js create mode 100644 assets/home.html-44ee30de.js create mode 100644 assets/home.html-6d2f5099.js create mode 100644 assets/home.html-eff1e1a9.js create mode 100644 assets/index.html-1cabf326.js create mode 100644 assets/index.html-2a017d73.js create mode 100644 assets/index.html-3b5329ec.js create mode 100644 assets/index.html-4c5e4c4a.js create mode 100644 assets/index.html-749b8c09.js create mode 100644 assets/index.html-d4986fc6.js create mode 100644 assets/knowledge.html-168eee0c.js create mode 100644 assets/knowledge.html-8e7358a7.js create mode 100644 assets/knowledge.html-bf240073.js create mode 100644 assets/knowledge.html-fa621fe0.js create mode 100644 assets/performance-optimization.html-0b660c19.js create mode 100644 assets/performance-optimization.html-4f96b155.js create mode 100644 assets/performance-optimization.html-69f9f3ed.js create mode 100644 assets/performance-optimization.html-91684308.js create mode 100644 assets/quick-start.html-1c002ee2.js create mode 100644 assets/quick-start.html-4917d6af.js create mode 100644 assets/quick-start.html-ba5a064e.js create mode 100644 assets/quick-start.html-d0230eac.js create mode 100644 assets/run-on-desktop.html-426dfbb6.js create mode 100644 assets/run-on-desktop.html-6d699af5.js create mode 100644 assets/run-on-desktop.html-8df4653a.js create mode 100644 assets/run-on-desktop.html-b6246f6d.js create mode 100644 assets/structural-zoom-table.html-0b909054.js create mode 100644 assets/structural-zoom-table.html-60176671.js create mode 100644 assets/structural-zoom-table.html-9be6c1fa.js create mode 100644 assets/structural-zoom-table.html-ab94571a.js create mode 100644 assets/style-0b913742.css create mode 100644 en/about/about.html create mode 100644 en/about/contacts.html create mode 100644 en/guide/example.html create mode 100644 en/guide/home.html create mode 100644 en/guide/knowledge.html create mode 100644 en/guide/performance-optimization.html create mode 100644 en/guide/quick-start.html create mode 100644 en/guide/run-on-desktop.html create mode 100644 en/guide/structural-zoom-table.html create mode 100644 en/index.html create mode 100644 index.html create mode 100644 zh-cn/about/about.html create mode 100644 zh-cn/about/contacts.html create mode 100644 zh-cn/guide/example.html create mode 100644 zh-cn/guide/home.html create mode 100644 zh-cn/guide/knowledge.html create mode 100644 zh-cn/guide/performance-optimization.html create mode 100644 zh-cn/guide/quick-start.html create mode 100644 zh-cn/guide/run-on-desktop.html create mode 100644 zh-cn/guide/structural-zoom-table.html create mode 100644 zh-cn/index.html diff --git a/404.html b/404.html new file mode 100644 index 00000000..7bc64ddd --- /dev/null +++ b/404.html @@ -0,0 +1,33 @@ + + + + + + + + + DexKit + + + + +

404

Looks like we've got some broken links.
Take me home
+ + + diff --git a/assets/404.html-60b35caa.js b/assets/404.html-60b35caa.js new file mode 100644 index 00000000..7a25b17a --- /dev/null +++ b/assets/404.html-60b35caa.js @@ -0,0 +1 @@ +const t=JSON.parse('{"key":"v-3706649a","path":"/404.html","title":"","lang":"en-US","frontmatter":{"layout":"NotFound"},"headers":[],"git":{},"filePathRelative":null}');export{t as data}; diff --git a/assets/404.html-fd1e0a9c.js b/assets/404.html-fd1e0a9c.js new file mode 100644 index 00000000..3be19781 --- /dev/null +++ b/assets/404.html-fd1e0a9c.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-75e8845f.js";const _={};function o(r,n){return c(),t("div")}const a=e(_,[["render",o],["__file","404.html.vue"]]);export{a as default}; diff --git a/assets/about.html-8ea67162.js b/assets/about.html-8ea67162.js new file mode 100644 index 00000000..23e599e2 --- /dev/null +++ b/assets/about.html-8ea67162.js @@ -0,0 +1 @@ +import{_ as n,r,o as a,c as s,d as e,e as o,a as c}from"./app-75e8845f.js";const l={},_=e("h1",{id:"关于此文档",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#关于此文档","aria-hidden":"true"},"#"),o(" 关于此文档")],-1),d={href:"https://vuepress.vuejs.org/zh/",target:"_blank",rel:"noopener noreferrer"},u=e("p",null,"版权所有 © 2022-2023 LuckyPray",-1);function h(i,p){const t=r("ExternalLinkIcon");return a(),s("div",null,[_,e("blockquote",null,[e("p",null,[o("此文档由 "),e("a",d,[o("vuepress"),c(t)]),o(" 强力驱动。")])]),u])}const m=n(l,[["render",h],["__file","about.html.vue"]]);export{m as default}; diff --git a/assets/about.html-9df343ae.js b/assets/about.html-9df343ae.js new file mode 100644 index 00000000..5560f2f0 --- /dev/null +++ b/assets/about.html-9df343ae.js @@ -0,0 +1 @@ +const t=JSON.parse('{"key":"v-41967128","path":"/zh-cn/about/about.html","title":"关于此文档","lang":"zh-CN","frontmatter":{},"headers":[],"git":{"updatedTime":1718095560000,"contributors":[{"name":"teble","email":"me@teble.me","commits":1}]},"filePathRelative":"zh-cn/about/about.md"}');export{t as data}; diff --git a/assets/about.html-a49d4a97.js b/assets/about.html-a49d4a97.js new file mode 100644 index 00000000..dcf9414a --- /dev/null +++ b/assets/about.html-a49d4a97.js @@ -0,0 +1 @@ +import{_ as n,r,o as a,c as s,d as e,e as t,a as c}from"./app-75e8845f.js";const i={},l=e("h1",{id:"about-this-documentation",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#about-this-documentation","aria-hidden":"true"},"#"),t(" About This Documentation")],-1),u={href:"https://vuepress.vuejs.org/en/",target:"_blank",rel:"noopener noreferrer"},d=e("p",null,"All rights reserved © 2022-2023 LuckyPray",-1);function _(h,p){const o=r("ExternalLinkIcon");return a(),s("div",null,[l,e("blockquote",null,[e("p",null,[t("This documentation is powered by "),e("a",u,[t("vuepress"),c(o)]),t(".")])]),d])}const f=n(i,[["render",_],["__file","about.html.vue"]]);export{f as default}; diff --git a/assets/about.html-b3c329f5.js b/assets/about.html-b3c329f5.js new file mode 100644 index 00000000..31ecec46 --- /dev/null +++ b/assets/about.html-b3c329f5.js @@ -0,0 +1 @@ +const t=JSON.parse('{"key":"v-7a15fe3b","path":"/en/about/about.html","title":"About This Documentation","lang":"en-US","frontmatter":{},"headers":[],"git":{"updatedTime":1718095560000,"contributors":[{"name":"teble","email":"me@teble.me","commits":1}]},"filePathRelative":"en/about/about.md"}');export{t as data}; diff --git a/assets/app-75e8845f.js b/assets/app-75e8845f.js new file mode 100644 index 00000000..258fe64e --- /dev/null +++ b/assets/app-75e8845f.js @@ -0,0 +1,10 @@ +const Bl="modulepreload",jl=function(e){return"/DexKit/"+e},Yo={},ne=function(t,n,r){if(!n||n.length===0)return t();const o=document.getElementsByTagName("link");return Promise.all(n.map(s=>{if(s=jl(s),s in Yo)return;Yo[s]=!0;const i=s.endsWith(".css"),l=i?'[rel="stylesheet"]':"";if(!!r)for(let u=o.length-1;u>=0;u--){const f=o[u];if(f.href===s&&(!i||f.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${s}"]${l}`))return;const c=document.createElement("link");if(c.rel=i?"stylesheet":Bl,i||(c.as="script",c.crossOrigin=""),c.href=s,document.head.appendChild(c),i)return new Promise((u,f)=>{c.addEventListener("load",u),c.addEventListener("error",()=>f(new Error(`Unable to preload CSS for ${s}`)))})})).then(()=>t()).catch(s=>{const i=new Event("vite:preloadError",{cancelable:!0});if(i.payload=s,window.dispatchEvent(i),!i.defaultPrevented)throw s})};function fo(e,t){const n=Object.create(null),r=e.split(",");for(let o=0;o!!n[o.toLowerCase()]:o=>!!n[o]}const Ee={},tn=[],ot=()=>{},Ul=()=>!1,Vl=/^on[^a-z]/,Dn=e=>Vl.test(e),ho=e=>e.startsWith("onUpdate:"),Pe=Object.assign,po=(e,t)=>{const n=e.indexOf(t);n>-1&&e.splice(n,1)},Kl=Object.prototype.hasOwnProperty,ue=(e,t)=>Kl.call(e,t),J=Array.isArray,nn=e=>br(e)==="[object Map]",li=e=>br(e)==="[object Set]",se=e=>typeof e=="function",pe=e=>typeof e=="string",mo=e=>typeof e=="symbol",we=e=>e!==null&&typeof e=="object",ai=e=>we(e)&&se(e.then)&&se(e.catch),ci=Object.prototype.toString,br=e=>ci.call(e),Wl=e=>br(e).slice(8,-1),ui=e=>br(e)==="[object Object]",go=e=>pe(e)&&e!=="NaN"&&e[0]!=="-"&&""+parseInt(e,10)===e,Cn=fo(",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"),yr=e=>{const t=Object.create(null);return n=>t[n]||(t[n]=e(n))},ql=/-(\w)/g,ft=yr(e=>e.replace(ql,(t,n)=>n?n.toUpperCase():"")),Gl=/\B([A-Z])/g,qt=yr(e=>e.replace(Gl,"-$1").toLowerCase()),Er=yr(e=>e.charAt(0).toUpperCase()+e.slice(1)),$r=yr(e=>e?`on${Er(e)}`:""),Pn=(e,t)=>!Object.is(e,t),Nr=(e,t)=>{for(let n=0;n{Object.defineProperty(e,t,{configurable:!0,enumerable:!1,value:n})},Yl=e=>{const t=parseFloat(e);return isNaN(t)?e:t},Jl=e=>{const t=pe(e)?Number(e):NaN;return isNaN(t)?e:t};let Jo;const Yr=()=>Jo||(Jo=typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:typeof global<"u"?global:{});function Hn(e){if(J(e)){const t={};for(let n=0;n{if(n){const r=n.split(Zl);r.length>1&&(t[r[0].trim()]=r[1].trim())}}),t}function Ke(e){let t="";if(pe(e))t=e;else if(J(e))for(let n=0;npe(e)?e:e==null?"":J(e)||we(e)&&(e.toString===ci||!se(e.toString))?JSON.stringify(e,di,2):String(e),di=(e,t)=>t&&t.__v_isRef?di(e,t.value):nn(t)?{[`Map(${t.size})`]:[...t.entries()].reduce((n,[r,o])=>(n[`${r} =>`]=o,n),{})}:li(t)?{[`Set(${t.size})`]:[...t.values()]}:we(t)&&!J(t)&&!ui(t)?String(t):t;let Ge;class ra{constructor(t=!1){this.detached=t,this._active=!0,this.effects=[],this.cleanups=[],this.parent=Ge,!t&&Ge&&(this.index=(Ge.scopes||(Ge.scopes=[])).push(this)-1)}get active(){return this._active}run(t){if(this._active){const n=Ge;try{return Ge=this,t()}finally{Ge=n}}}on(){Ge=this}off(){Ge=this.parent}stop(t){if(this._active){let n,r;for(n=0,r=this.effects.length;n{const t=new Set(e);return t.w=0,t.n=0,t},pi=e=>(e.w&Rt)>0,mi=e=>(e.n&Rt)>0,ia=({deps:e})=>{if(e.length)for(let t=0;t{const{deps:t}=e;if(t.length){let n=0;for(let r=0;r{(u==="length"||u>=a)&&l.push(c)})}else switch(n!==void 0&&l.push(i.get(n)),t){case"add":J(e)?go(n)&&l.push(i.get("length")):(l.push(i.get(Ut)),nn(e)&&l.push(i.get(Qr)));break;case"delete":J(e)||(l.push(i.get(Ut)),nn(e)&&l.push(i.get(Qr)));break;case"set":nn(e)&&l.push(i.get(Ut));break}if(l.length===1)l[0]&&Zr(l[0]);else{const a=[];for(const c of l)c&&a.push(...c);Zr(vo(a))}}function Zr(e,t){const n=J(e)?e:[...e];for(const r of n)r.computed&&Zo(r);for(const r of n)r.computed||Zo(r)}function Zo(e,t){(e!==nt||e.allowRecurse)&&(e.scheduler?e.scheduler():e.run())}function aa(e,t){var n;return(n=lr.get(e))==null?void 0:n.get(t)}const ca=fo("__proto__,__v_isRef,__isVue"),_i=new Set(Object.getOwnPropertyNames(Symbol).filter(e=>e!=="arguments"&&e!=="caller").map(e=>Symbol[e]).filter(mo)),ua=bo(),fa=bo(!1,!0),da=bo(!0),Xo=ha();function ha(){const e={};return["includes","indexOf","lastIndexOf"].forEach(t=>{e[t]=function(...n){const r=de(this);for(let s=0,i=this.length;s{e[t]=function(...n){pn();const r=de(this)[t].apply(this,n);return mn(),r}}),e}function pa(e){const t=de(this);return We(t,"has",e),t.hasOwnProperty(e)}function bo(e=!1,t=!1){return function(r,o,s){if(o==="__v_isReactive")return!e;if(o==="__v_isReadonly")return e;if(o==="__v_isShallow")return t;if(o==="__v_raw"&&s===(e?t?Pa:xi:t?wi:Ei).get(r))return r;const i=J(r);if(!e){if(i&&ue(Xo,o))return Reflect.get(Xo,o,s);if(o==="hasOwnProperty")return pa}const l=Reflect.get(r,o,s);return(mo(o)?_i.has(o):ca(o))||(e||We(r,"get",o),t)?l:$e(l)?i&&go(o)?l:l.value:we(l)?e?Fn(l):zn(l):l}}const ma=bi(),ga=bi(!0);function bi(e=!1){return function(n,r,o,s){let i=n[r];if(ln(i)&&$e(i)&&!$e(o))return!1;if(!e&&(!ar(o)&&!ln(o)&&(i=de(i),o=de(o)),!J(n)&&$e(i)&&!$e(o)))return i.value=o,!0;const l=J(n)&&go(r)?Number(r)e,wr=e=>Reflect.getPrototypeOf(e);function Kn(e,t,n=!1,r=!1){e=e.__v_raw;const o=de(e),s=de(t);n||(t!==s&&We(o,"get",t),We(o,"get",s));const{has:i}=wr(o),l=r?yo:n?xo:Rn;if(i.call(o,t))return l(e.get(t));if(i.call(o,s))return l(e.get(s));e!==o&&e.get(t)}function Wn(e,t=!1){const n=this.__v_raw,r=de(n),o=de(e);return t||(e!==o&&We(r,"has",e),We(r,"has",o)),e===o?n.has(e):n.has(e)||n.has(o)}function qn(e,t=!1){return e=e.__v_raw,!t&&We(de(e),"iterate",Ut),Reflect.get(e,"size",e)}function es(e){e=de(e);const t=de(this);return wr(t).has.call(t,e)||(t.add(e),_t(t,"add",e,e)),this}function ts(e,t){t=de(t);const n=de(this),{has:r,get:o}=wr(n);let s=r.call(n,e);s||(e=de(e),s=r.call(n,e));const i=o.call(n,e);return n.set(e,t),s?Pn(t,i)&&_t(n,"set",e,t):_t(n,"add",e,t),this}function ns(e){const t=de(this),{has:n,get:r}=wr(t);let o=n.call(t,e);o||(e=de(e),o=n.call(t,e)),r&&r.call(t,e);const s=t.delete(e);return o&&_t(t,"delete",e,void 0),s}function rs(){const e=de(this),t=e.size!==0,n=e.clear();return t&&_t(e,"clear",void 0,void 0),n}function Gn(e,t){return function(r,o){const s=this,i=s.__v_raw,l=de(i),a=t?yo:e?xo:Rn;return!e&&We(l,"iterate",Ut),i.forEach((c,u)=>r.call(o,a(c),a(u),s))}}function Yn(e,t,n){return function(...r){const o=this.__v_raw,s=de(o),i=nn(s),l=e==="entries"||e===Symbol.iterator&&i,a=e==="keys"&&i,c=o[e](...r),u=n?yo:t?xo:Rn;return!t&&We(s,"iterate",a?Qr:Ut),{next(){const{value:f,done:h}=c.next();return h?{value:f,done:h}:{value:l?[u(f[0]),u(f[1])]:u(f),done:h}},[Symbol.iterator](){return this}}}}function xt(e){return function(...t){return e==="delete"?!1:this}}function wa(){const e={get(s){return Kn(this,s)},get size(){return qn(this)},has:Wn,add:es,set:ts,delete:ns,clear:rs,forEach:Gn(!1,!1)},t={get(s){return Kn(this,s,!1,!0)},get size(){return qn(this)},has:Wn,add:es,set:ts,delete:ns,clear:rs,forEach:Gn(!1,!0)},n={get(s){return Kn(this,s,!0)},get size(){return qn(this,!0)},has(s){return Wn.call(this,s,!0)},add:xt("add"),set:xt("set"),delete:xt("delete"),clear:xt("clear"),forEach:Gn(!0,!1)},r={get(s){return Kn(this,s,!0,!0)},get size(){return qn(this,!0)},has(s){return Wn.call(this,s,!0)},add:xt("add"),set:xt("set"),delete:xt("delete"),clear:xt("clear"),forEach:Gn(!0,!0)};return["keys","values","entries",Symbol.iterator].forEach(s=>{e[s]=Yn(s,!1,!1),n[s]=Yn(s,!0,!1),t[s]=Yn(s,!1,!0),r[s]=Yn(s,!0,!0)}),[e,n,t,r]}const[xa,Ca,La,Ta]=wa();function Eo(e,t){const n=t?e?Ta:La:e?Ca:xa;return(r,o,s)=>o==="__v_isReactive"?!e:o==="__v_isReadonly"?e:o==="__v_raw"?r:Reflect.get(ue(n,o)&&o in r?n:r,o,s)}const ka={get:Eo(!1,!1)},Sa={get:Eo(!1,!0)},Aa={get:Eo(!0,!1)},Ei=new WeakMap,wi=new WeakMap,xi=new WeakMap,Pa=new WeakMap;function Ra(e){switch(e){case"Object":case"Array":return 1;case"Map":case"Set":case"WeakMap":case"WeakSet":return 2;default:return 0}}function Oa(e){return e.__v_skip||!Object.isExtensible(e)?0:Ra(Wl(e))}function zn(e){return ln(e)?e:wo(e,!1,yi,ka,Ei)}function Ci(e){return wo(e,!1,Ea,Sa,wi)}function Fn(e){return wo(e,!0,ya,Aa,xi)}function wo(e,t,n,r,o){if(!we(e)||e.__v_raw&&!(t&&e.__v_isReactive))return e;const s=o.get(e);if(s)return s;const i=Oa(e);if(i===0)return e;const l=new Proxy(e,i===2?r:n);return o.set(e,l),l}function rn(e){return ln(e)?rn(e.__v_raw):!!(e&&e.__v_isReactive)}function ln(e){return!!(e&&e.__v_isReadonly)}function ar(e){return!!(e&&e.__v_isShallow)}function Li(e){return rn(e)||ln(e)}function de(e){const t=e&&e.__v_raw;return t?de(t):e}function Ti(e){return ir(e,"__v_skip",!0),e}const Rn=e=>we(e)?zn(e):e,xo=e=>we(e)?Fn(e):e;function Co(e){At&&nt&&(e=de(e),vi(e.dep||(e.dep=vo())))}function Lo(e,t){e=de(e);const n=e.dep;n&&Zr(n)}function $e(e){return!!(e&&e.__v_isRef===!0)}function xe(e){return ki(e,!1)}function To(e){return ki(e,!0)}function ki(e,t){return $e(e)?e:new Ia(e,t)}class Ia{constructor(t,n){this.__v_isShallow=n,this.dep=void 0,this.__v_isRef=!0,this._rawValue=n?t:de(t),this._value=n?t:Rn(t)}get value(){return Co(this),this._value}set value(t){const n=this.__v_isShallow||ar(t)||ln(t);t=n?t:de(t),Pn(t,this._rawValue)&&(this._rawValue=t,this._value=n?t:Rn(t),Lo(this))}}function Z(e){return $e(e)?e.value:e}const $a={get:(e,t,n)=>Z(Reflect.get(e,t,n)),set:(e,t,n,r)=>{const o=e[t];return $e(o)&&!$e(n)?(o.value=n,!0):Reflect.set(e,t,n,r)}};function Si(e){return rn(e)?e:new Proxy(e,$a)}class Na{constructor(t){this.dep=void 0,this.__v_isRef=!0;const{get:n,set:r}=t(()=>Co(this),()=>Lo(this));this._get=n,this._set=r}get value(){return this._get()}set value(t){this._set(t)}}function Ma(e){return new Na(e)}function ko(e){const t=J(e)?new Array(e.length):{};for(const n in e)t[n]=Ha(e,n);return t}class Da{constructor(t,n,r){this._object=t,this._key=n,this._defaultValue=r,this.__v_isRef=!0}get value(){const t=this._object[this._key];return t===void 0?this._defaultValue:t}set value(t){this._object[this._key]=t}get dep(){return aa(de(this._object),this._key)}}function Ha(e,t,n){const r=e[t];return $e(r)?r:new Da(e,t,n)}class za{constructor(t,n,r,o){this._setter=n,this.dep=void 0,this.__v_isRef=!0,this.__v_isReadonly=!1,this._dirty=!0,this.effect=new _o(t,()=>{this._dirty||(this._dirty=!0,Lo(this))}),this.effect.computed=this,this.effect.active=this._cacheable=!o,this.__v_isReadonly=r}get value(){const t=de(this);return Co(t),(t._dirty||!t._cacheable)&&(t._dirty=!1,t._value=t.effect.run()),t._value}set value(t){this._setter(t)}}function Fa(e,t,n=!1){let r,o;const s=se(e);return s?(r=e,o=ot):(r=e.get,o=e.set),new za(r,o,s||!o,n)}function Pt(e,t,n,r){let o;try{o=r?e(...r):e()}catch(s){Bn(s,t,n)}return o}function Ze(e,t,n,r){if(se(e)){const s=Pt(e,t,n,r);return s&&ai(s)&&s.catch(i=>{Bn(i,t,n)}),s}const o=[];for(let s=0;s>>1;In(Fe[r])ut&&Fe.splice(t,1)}function Va(e){J(e)?on.push(...e):(!mt||!mt.includes(e,e.allowRecurse?zt+1:zt))&&on.push(e),Pi()}function os(e,t=On?ut+1:0){for(;tIn(n)-In(r)),zt=0;zte.id==null?1/0:e.id,Ka=(e,t)=>{const n=In(e)-In(t);if(n===0){if(e.pre&&!t.pre)return-1;if(t.pre&&!e.pre)return 1}return n};function Ri(e){Xr=!1,On=!0,Fe.sort(Ka);const t=ot;try{for(ut=0;utpe(g)?g.trim():g)),f&&(o=n.map(Yl))}let l,a=r[l=$r(t)]||r[l=$r(ft(t))];!a&&s&&(a=r[l=$r(qt(t))]),a&&Ze(a,e,6,o);const c=r[l+"Once"];if(c){if(!e.emitted)e.emitted={};else if(e.emitted[l])return;e.emitted[l]=!0,Ze(c,e,6,o)}}function Oi(e,t,n=!1){const r=t.emitsCache,o=r.get(e);if(o!==void 0)return o;const s=e.emits;let i={},l=!1;if(!se(e)){const a=c=>{const u=Oi(c,t,!0);u&&(l=!0,Pe(i,u))};!n&&t.mixins.length&&t.mixins.forEach(a),e.extends&&a(e.extends),e.mixins&&e.mixins.forEach(a)}return!s&&!l?(we(e)&&r.set(e,null),null):(J(s)?s.forEach(a=>i[a]=null):Pe(i,s),we(e)&&r.set(e,i),i)}function Lr(e,t){return!e||!Dn(t)?!1:(t=t.slice(2).replace(/Once$/,""),ue(e,t[0].toLowerCase()+t.slice(1))||ue(e,qt(t))||ue(e,t))}let De=null,Ii=null;function ur(e){const t=De;return De=e,Ii=e&&e.type.__scopeId||null,t}function Me(e,t=De,n){if(!t||e._n)return e;const r=(...o)=>{r._d&&gs(-1);const s=ur(t);let i;try{i=e(...o)}finally{ur(s),r._d&&gs(1)}return i};return r._n=!0,r._c=!0,r._d=!0,r}function Mr(e){const{type:t,vnode:n,proxy:r,withProxy:o,props:s,propsOptions:[i],slots:l,attrs:a,emit:c,render:u,renderCache:f,data:h,setupState:g,ctx:y,inheritAttrs:w}=e;let T,v;const b=ur(e);try{if(n.shapeFlag&4){const A=o||r;T=tt(u.call(A,A,f,s,g,h,y)),v=a}else{const A=t;T=tt(A.length>1?A(s,{attrs:a,slots:l,emit:c}):A(s,null)),v=t.props?a:qa(a)}}catch(A){kn.length=0,Bn(A,e,1),T=ee(Ye)}let P=T;if(v&&w!==!1){const A=Object.keys(v),{shapeFlag:K}=P;A.length&&K&7&&(i&&A.some(ho)&&(v=Ga(v,i)),P=It(P,v))}return n.dirs&&(P=It(P),P.dirs=P.dirs?P.dirs.concat(n.dirs):n.dirs),n.transition&&(P.transition=n.transition),T=P,ur(b),T}const qa=e=>{let t;for(const n in e)(n==="class"||n==="style"||Dn(n))&&((t||(t={}))[n]=e[n]);return t},Ga=(e,t)=>{const n={};for(const r in e)(!ho(r)||!(r.slice(9)in t))&&(n[r]=e[r]);return n};function Ya(e,t,n){const{props:r,children:o,component:s}=e,{props:i,children:l,patchFlag:a}=t,c=s.emitsOptions;if(t.dirs||t.transition)return!0;if(n&&a>=0){if(a&1024)return!0;if(a&16)return r?ss(r,i,c):!!i;if(a&8){const u=t.dynamicProps;for(let f=0;fe.__isSuspense;function $i(e,t){t&&t.pendingBranch?J(e)?t.effects.push(...e):t.effects.push(e):Va(e)}function Ni(e,t){return Ao(e,null,t)}const Jn={};function st(e,t,n){return Ao(e,t,n)}function Ao(e,t,{immediate:n,deep:r,flush:o,onTrack:s,onTrigger:i}=Ee){var l;const a=hi()===((l=Re)==null?void 0:l.scope)?Re:null;let c,u=!1,f=!1;if($e(e)?(c=()=>e.value,u=ar(e)):rn(e)?(c=()=>e,r=!0):J(e)?(f=!0,u=e.some(A=>rn(A)||ar(A)),c=()=>e.map(A=>{if($e(A))return A.value;if(rn(A))return jt(A);if(se(A))return Pt(A,a,2)})):se(e)?t?c=()=>Pt(e,a,2):c=()=>{if(!(a&&a.isUnmounted))return h&&h(),Ze(e,a,3,[g])}:c=ot,t&&r){const A=c;c=()=>jt(A())}let h,g=A=>{h=b.onStop=()=>{Pt(A,a,4)}},y;if(un)if(g=ot,t?n&&Ze(t,a,3,[c(),f?[]:void 0,g]):c(),o==="sync"){const A=Wc();y=A.__watcherHandles||(A.__watcherHandles=[])}else return ot;let w=f?new Array(e.length).fill(Jn):Jn;const T=()=>{if(b.active)if(t){const A=b.run();(r||u||(f?A.some((K,X)=>Pn(K,w[X])):Pn(A,w)))&&(h&&h(),Ze(t,a,3,[A,w===Jn?void 0:f&&w[0]===Jn?[]:w,g]),w=A)}else b.run()};T.allowRecurse=!!t;let v;o==="sync"?v=T:o==="post"?v=()=>Ve(T,a&&a.suspense):(T.pre=!0,a&&(T.id=a.uid),v=()=>Cr(T));const b=new _o(c,v);t?n?T():w=b.run():o==="post"?Ve(b.run.bind(b),a&&a.suspense):b.run();const P=()=>{b.stop(),a&&a.scope&&po(a.scope.effects,b)};return y&&y.push(P),P}function Za(e,t,n){const r=this.proxy,o=pe(e)?e.includes(".")?Mi(r,e):()=>r[e]:e.bind(r,r);let s;se(t)?s=t:(s=t.handler,n=t);const i=Re;cn(this);const l=Ao(o,s.bind(r),n);return i?cn(i):Kt(),l}function Mi(e,t){const n=t.split(".");return()=>{let r=e;for(let o=0;o{jt(n,t)});else if(ui(e))for(const n in e)jt(e[n],t);return e}function fr(e,t){const n=De;if(n===null)return e;const r=Pr(n)||n.proxy,o=e.dirs||(e.dirs=[]);for(let s=0;s{e.isMounted=!0}),kr(()=>{e.isUnmounting=!0}),e}const Je=[Function,Array],Di={mode:String,appear:Boolean,persisted:Boolean,onBeforeEnter:Je,onEnter:Je,onAfterEnter:Je,onEnterCancelled:Je,onBeforeLeave:Je,onLeave:Je,onAfterLeave:Je,onLeaveCancelled:Je,onBeforeAppear:Je,onAppear:Je,onAfterAppear:Je,onAppearCancelled:Je},ec={name:"BaseTransition",props:Di,setup(e,{slots:t}){const n=Xi(),r=Xa();let o;return()=>{const s=t.default&&zi(t.default(),!0);if(!s||!s.length)return;let i=s[0];if(s.length>1){for(const w of s)if(w.type!==Ye){i=w;break}}const l=de(e),{mode:a}=l;if(r.isLeaving)return Dr(i);const c=is(i);if(!c)return Dr(i);const u=eo(c,l,r,n);to(c,u);const f=n.subTree,h=f&&is(f);let g=!1;const{getTransitionKey:y}=c.type;if(y){const w=y();o===void 0?o=w:w!==o&&(o=w,g=!0)}if(h&&h.type!==Ye&&(!Ft(c,h)||g)){const w=eo(h,l,r,n);if(to(h,w),a==="out-in")return r.isLeaving=!0,w.afterLeave=()=>{r.isLeaving=!1,n.update.active!==!1&&n.update()},Dr(i);a==="in-out"&&c.type!==Ye&&(w.delayLeave=(T,v,b)=>{const P=Hi(r,h);P[String(h.key)]=h,T._leaveCb=()=>{v(),T._leaveCb=void 0,delete u.delayedLeave},u.delayedLeave=b})}return i}}},tc=ec;function Hi(e,t){const{leavingVNodes:n}=e;let r=n.get(t.type);return r||(r=Object.create(null),n.set(t.type,r)),r}function eo(e,t,n,r){const{appear:o,mode:s,persisted:i=!1,onBeforeEnter:l,onEnter:a,onAfterEnter:c,onEnterCancelled:u,onBeforeLeave:f,onLeave:h,onAfterLeave:g,onLeaveCancelled:y,onBeforeAppear:w,onAppear:T,onAfterAppear:v,onAppearCancelled:b}=t,P=String(e.key),A=Hi(n,e),K=(m,B)=>{m&&Ze(m,r,9,B)},X=(m,B)=>{const D=B[1];K(m,B),J(m)?m.every(q=>q.length<=1)&&D():m.length<=1&&D()},M={mode:s,persisted:i,beforeEnter(m){let B=l;if(!n.isMounted)if(o)B=w||l;else return;m._leaveCb&&m._leaveCb(!0);const D=A[P];D&&Ft(e,D)&&D.el._leaveCb&&D.el._leaveCb(),K(B,[m])},enter(m){let B=a,D=c,q=u;if(!n.isMounted)if(o)B=T||a,D=v||c,q=b||u;else return;let L=!1;const O=m._enterCb=I=>{L||(L=!0,I?K(q,[m]):K(D,[m]),M.delayedLeave&&M.delayedLeave(),m._enterCb=void 0)};B?X(B,[m,O]):O()},leave(m,B){const D=String(e.key);if(m._enterCb&&m._enterCb(!0),n.isUnmounting)return B();K(f,[m]);let q=!1;const L=m._leaveCb=O=>{q||(q=!0,B(),O?K(y,[m]):K(g,[m]),m._leaveCb=void 0,A[D]===e&&delete A[D])};A[D]=e,h?X(h,[m,L]):L()},clone(m){return eo(m,t,n,r)}};return M}function Dr(e){if(jn(e))return e=It(e),e.children=null,e}function is(e){return jn(e)?e.children?e.children[0]:void 0:e}function to(e,t){e.shapeFlag&6&&e.component?to(e.component.subTree,t):e.shapeFlag&128?(e.ssContent.transition=t.clone(e.ssContent),e.ssFallback.transition=t.clone(e.ssFallback)):e.transition=t}function zi(e,t=!1,n){let r=[],o=0;for(let s=0;s1)for(let s=0;sPe({name:e.name},t,{setup:e}))():e}const sn=e=>!!e.type.__asyncLoader;function Le(e){se(e)&&(e={loader:e});const{loader:t,loadingComponent:n,errorComponent:r,delay:o=200,timeout:s,suspensible:i=!0,onError:l}=e;let a=null,c,u=0;const f=()=>(u++,a=null,h()),h=()=>{let g;return a||(g=a=t().catch(y=>{if(y=y instanceof Error?y:new Error(String(y)),l)return new Promise((w,T)=>{l(y,()=>w(f()),()=>T(y),u+1)});throw y}).then(y=>g!==a&&a?a:(y&&(y.__esModule||y[Symbol.toStringTag]==="Module")&&(y=y.default),c=y,y)))};return he({name:"AsyncComponentWrapper",__asyncLoader:h,get __asyncResolved(){return c},setup(){const g=Re;if(c)return()=>Hr(c,g);const y=b=>{a=null,Bn(b,g,13,!r)};if(i&&g.suspense||un)return h().then(b=>()=>Hr(b,g)).catch(b=>(y(b),()=>r?ee(r,{error:b}):null));const w=xe(!1),T=xe(),v=xe(!!o);return o&&setTimeout(()=>{v.value=!1},o),s!=null&&setTimeout(()=>{if(!w.value&&!T.value){const b=new Error(`Async component timed out after ${s}ms.`);y(b),T.value=b}},s),h().then(()=>{w.value=!0,g.parent&&jn(g.parent.vnode)&&Cr(g.parent.update)}).catch(b=>{y(b),T.value=b}),()=>{if(w.value&&c)return Hr(c,g);if(T.value&&r)return ee(r,{error:T.value});if(n&&!v.value)return ee(n)}}})}function Hr(e,t){const{ref:n,props:r,children:o,ce:s}=t.vnode,i=ee(e,r,o);return i.ref=n,i.ce=s,delete t.vnode.ce,i}const jn=e=>e.type.__isKeepAlive;function nc(e,t){Fi(e,"a",t)}function rc(e,t){Fi(e,"da",t)}function Fi(e,t,n=Re){const r=e.__wdc||(e.__wdc=()=>{let o=n;for(;o;){if(o.isDeactivated)return;o=o.parent}return e()});if(Tr(t,r,n),n){let o=n.parent;for(;o&&o.parent;)jn(o.parent.vnode)&&oc(r,t,n,o),o=o.parent}}function oc(e,t,n,r){const o=Tr(t,e,r,!0);Sr(()=>{po(r[t],o)},n)}function Tr(e,t,n=Re,r=!1){if(n){const o=n[e]||(n[e]=[]),s=t.__weh||(t.__weh=(...i)=>{if(n.isUnmounted)return;pn(),cn(n);const l=Ze(t,n,e,i);return Kt(),mn(),l});return r?o.unshift(s):o.push(s),s}}const yt=e=>(t,n=Re)=>(!un||e==="sp")&&Tr(e,(...r)=>t(...r),n),sc=yt("bm"),Xe=yt("m"),ic=yt("bu"),lc=yt("u"),kr=yt("bum"),Sr=yt("um"),ac=yt("sp"),cc=yt("rtg"),uc=yt("rtc");function fc(e,t=Re){Tr("ec",e,t)}const Bi="components";function bt(e,t){return hc(Bi,e,!0,t)||e}const dc=Symbol.for("v-ndc");function hc(e,t,n=!0,r=!1){const o=De||Re;if(o){const s=o.type;if(e===Bi){const l=Uc(s,!1);if(l&&(l===t||l===ft(t)||l===Er(ft(t))))return s}const i=ls(o[e]||s[e],t)||ls(o.appContext[e],t);return!i&&r?s:i}}function ls(e,t){return e&&(e[t]||e[ft(t)]||e[Er(ft(t))])}function Ot(e,t,n,r){let o;const s=n&&n[r];if(J(e)||pe(e)){o=new Array(e.length);for(let i=0,l=e.length;it(i,l,void 0,s&&s[l]));else{const i=Object.keys(e);o=new Array(i.length);for(let l=0,a=i.length;lmr(t)?!(t.type===Ye||t.type===ye&&!ji(t.children)):!0)?e:null}const no=e=>e?el(e)?Pr(e)||e.proxy:no(e.parent):null,Ln=Pe(Object.create(null),{$:e=>e,$el:e=>e.vnode.el,$data:e=>e.data,$props:e=>e.props,$attrs:e=>e.attrs,$slots:e=>e.slots,$refs:e=>e.refs,$parent:e=>no(e.parent),$root:e=>no(e.root),$emit:e=>e.emit,$options:e=>Po(e),$forceUpdate:e=>e.f||(e.f=()=>Cr(e.update)),$nextTick:e=>e.n||(e.n=xr.bind(e.proxy)),$watch:e=>Za.bind(e)}),zr=(e,t)=>e!==Ee&&!e.__isScriptSetup&&ue(e,t),pc={get({_:e},t){const{ctx:n,setupState:r,data:o,props:s,accessCache:i,type:l,appContext:a}=e;let c;if(t[0]!=="$"){const g=i[t];if(g!==void 0)switch(g){case 1:return r[t];case 2:return o[t];case 4:return n[t];case 3:return s[t]}else{if(zr(r,t))return i[t]=1,r[t];if(o!==Ee&&ue(o,t))return i[t]=2,o[t];if((c=e.propsOptions[0])&&ue(c,t))return i[t]=3,s[t];if(n!==Ee&&ue(n,t))return i[t]=4,n[t];ro&&(i[t]=0)}}const u=Ln[t];let f,h;if(u)return t==="$attrs"&&We(e,"get",t),u(e);if((f=l.__cssModules)&&(f=f[t]))return f;if(n!==Ee&&ue(n,t))return i[t]=4,n[t];if(h=a.config.globalProperties,ue(h,t))return h[t]},set({_:e},t,n){const{data:r,setupState:o,ctx:s}=e;return zr(o,t)?(o[t]=n,!0):r!==Ee&&ue(r,t)?(r[t]=n,!0):ue(e.props,t)||t[0]==="$"&&t.slice(1)in e?!1:(s[t]=n,!0)},has({_:{data:e,setupState:t,accessCache:n,ctx:r,appContext:o,propsOptions:s}},i){let l;return!!n[i]||e!==Ee&&ue(e,i)||zr(t,i)||(l=s[0])&&ue(l,i)||ue(r,i)||ue(Ln,i)||ue(o.config.globalProperties,i)},defineProperty(e,t,n){return n.get!=null?e._.accessCache[t]=0:ue(n,"value")&&this.set(e,t,n.value,null),Reflect.defineProperty(e,t,n)}};function as(e){return J(e)?e.reduce((t,n)=>(t[n]=null,t),{}):e}let ro=!0;function mc(e){const t=Po(e),n=e.proxy,r=e.ctx;ro=!1,t.beforeCreate&&cs(t.beforeCreate,e,"bc");const{data:o,computed:s,methods:i,watch:l,provide:a,inject:c,created:u,beforeMount:f,mounted:h,beforeUpdate:g,updated:y,activated:w,deactivated:T,beforeDestroy:v,beforeUnmount:b,destroyed:P,unmounted:A,render:K,renderTracked:X,renderTriggered:M,errorCaptured:m,serverPrefetch:B,expose:D,inheritAttrs:q,components:L,directives:O,filters:I}=t;if(c&&gc(c,r,null),i)for(const re in i){const oe=i[re];se(oe)&&(r[re]=oe.bind(n))}if(o){const re=o.call(n,n);we(re)&&(e.data=zn(re))}if(ro=!0,s)for(const re in s){const oe=s[re],He=se(oe)?oe.bind(n,n):se(oe.get)?oe.get.bind(n,n):ot,Ne=!se(oe)&&se(oe.set)?oe.set.bind(n):ot,Ue=j({get:He,set:Ne});Object.defineProperty(r,re,{enumerable:!0,configurable:!0,get:()=>Ue.value,set:ze=>Ue.value=ze})}if(l)for(const re in l)Ui(l[re],r,n,re);if(a){const re=se(a)?a.call(n):a;Reflect.ownKeys(re).forEach(oe=>{Vt(oe,re[oe])})}u&&cs(u,e,"c");function U(re,oe){J(oe)?oe.forEach(He=>re(He.bind(n))):oe&&re(oe.bind(n))}if(U(sc,f),U(Xe,h),U(ic,g),U(lc,y),U(nc,w),U(rc,T),U(fc,m),U(uc,X),U(cc,M),U(kr,b),U(Sr,A),U(ac,B),J(D))if(D.length){const re=e.exposed||(e.exposed={});D.forEach(oe=>{Object.defineProperty(re,oe,{get:()=>n[oe],set:He=>n[oe]=He})})}else e.exposed||(e.exposed={});K&&e.render===ot&&(e.render=K),q!=null&&(e.inheritAttrs=q),L&&(e.components=L),O&&(e.directives=O)}function gc(e,t,n=ot){J(e)&&(e=oo(e));for(const r in e){const o=e[r];let s;we(o)?"default"in o?s=ke(o.from||r,o.default,!0):s=ke(o.from||r):s=ke(o),$e(s)?Object.defineProperty(t,r,{enumerable:!0,configurable:!0,get:()=>s.value,set:i=>s.value=i}):t[r]=s}}function cs(e,t,n){Ze(J(e)?e.map(r=>r.bind(t.proxy)):e.bind(t.proxy),t,n)}function Ui(e,t,n,r){const o=r.includes(".")?Mi(n,r):()=>n[r];if(pe(e)){const s=t[e];se(s)&&st(o,s)}else if(se(e))st(o,e.bind(n));else if(we(e))if(J(e))e.forEach(s=>Ui(s,t,n,r));else{const s=se(e.handler)?e.handler.bind(n):t[e.handler];se(s)&&st(o,s,e)}}function Po(e){const t=e.type,{mixins:n,extends:r}=t,{mixins:o,optionsCache:s,config:{optionMergeStrategies:i}}=e.appContext,l=s.get(t);let a;return l?a=l:!o.length&&!n&&!r?a=t:(a={},o.length&&o.forEach(c=>dr(a,c,i,!0)),dr(a,t,i)),we(t)&&s.set(t,a),a}function dr(e,t,n,r=!1){const{mixins:o,extends:s}=t;s&&dr(e,s,n,!0),o&&o.forEach(i=>dr(e,i,n,!0));for(const i in t)if(!(r&&i==="expose")){const l=vc[i]||n&&n[i];e[i]=l?l(e[i],t[i]):t[i]}return e}const vc={data:us,props:fs,emits:fs,methods:xn,computed:xn,beforeCreate:Be,created:Be,beforeMount:Be,mounted:Be,beforeUpdate:Be,updated:Be,beforeDestroy:Be,beforeUnmount:Be,destroyed:Be,unmounted:Be,activated:Be,deactivated:Be,errorCaptured:Be,serverPrefetch:Be,components:xn,directives:xn,watch:bc,provide:us,inject:_c};function us(e,t){return t?e?function(){return Pe(se(e)?e.call(this,this):e,se(t)?t.call(this,this):t)}:t:e}function _c(e,t){return xn(oo(e),oo(t))}function oo(e){if(J(e)){const t={};for(let n=0;n1)return n&&se(t)?t.call(r&&r.proxy):t}}function wc(e,t,n,r=!1){const o={},s={};ir(s,Ar,1),e.propsDefaults=Object.create(null),Ki(e,t,o,s);for(const i in e.propsOptions[0])i in o||(o[i]=void 0);n?e.props=r?o:Ci(o):e.type.props?e.props=o:e.props=s,e.attrs=s}function xc(e,t,n,r){const{props:o,attrs:s,vnode:{patchFlag:i}}=e,l=de(o),[a]=e.propsOptions;let c=!1;if((r||i>0)&&!(i&16)){if(i&8){const u=e.vnode.dynamicProps;for(let f=0;f{a=!0;const[h,g]=Wi(f,t,!0);Pe(i,h),g&&l.push(...g)};!n&&t.mixins.length&&t.mixins.forEach(u),e.extends&&u(e.extends),e.mixins&&e.mixins.forEach(u)}if(!s&&!a)return we(e)&&r.set(e,tn),tn;if(J(s))for(let u=0;u-1,g[1]=w<0||y-1||ue(g,"default"))&&l.push(f)}}}const c=[i,l];return we(e)&&r.set(e,c),c}function ds(e){return e[0]!=="$"}function hs(e){const t=e&&e.toString().match(/^\s*(function|class) (\w+)/);return t?t[2]:e===null?"null":""}function ps(e,t){return hs(e)===hs(t)}function ms(e,t){return J(t)?t.findIndex(n=>ps(n,e)):se(t)&&ps(t,e)?0:-1}const qi=e=>e[0]==="_"||e==="$stable",Ro=e=>J(e)?e.map(tt):[tt(e)],Cc=(e,t,n)=>{if(t._n)return t;const r=Me((...o)=>Ro(t(...o)),n);return r._c=!1,r},Gi=(e,t,n)=>{const r=e._ctx;for(const o in e){if(qi(o))continue;const s=e[o];if(se(s))t[o]=Cc(o,s,r);else if(s!=null){const i=Ro(s);t[o]=()=>i}}},Yi=(e,t)=>{const n=Ro(t);e.slots.default=()=>n},Lc=(e,t)=>{if(e.vnode.shapeFlag&32){const n=t._;n?(e.slots=de(t),ir(t,"_",n)):Gi(t,e.slots={})}else e.slots={},t&&Yi(e,t);ir(e.slots,Ar,1)},Tc=(e,t,n)=>{const{vnode:r,slots:o}=e;let s=!0,i=Ee;if(r.shapeFlag&32){const l=t._;l?n&&l===1?s=!1:(Pe(o,t),!n&&l===1&&delete o._):(s=!t.$stable,Gi(t,o)),i=t}else t&&(Yi(e,t),i={default:1});if(s)for(const l in o)!qi(l)&&!(l in i)&&delete o[l]};function pr(e,t,n,r,o=!1){if(J(e)){e.forEach((h,g)=>pr(h,t&&(J(t)?t[g]:t),n,r,o));return}if(sn(r)&&!o)return;const s=r.shapeFlag&4?Pr(r.component)||r.component.proxy:r.el,i=o?null:s,{i:l,r:a}=e,c=t&&t.r,u=l.refs===Ee?l.refs={}:l.refs,f=l.setupState;if(c!=null&&c!==a&&(pe(c)?(u[c]=null,ue(f,c)&&(f[c]=null)):$e(c)&&(c.value=null)),se(a))Pt(a,l,12,[i,u]);else{const h=pe(a),g=$e(a);if(h||g){const y=()=>{if(e.f){const w=h?ue(f,a)?f[a]:u[a]:a.value;o?J(w)&&po(w,s):J(w)?w.includes(s)||w.push(s):h?(u[a]=[s],ue(f,a)&&(f[a]=u[a])):(a.value=[s],e.k&&(u[e.k]=a.value))}else h?(u[a]=i,ue(f,a)&&(f[a]=i)):g&&(a.value=i,e.k&&(u[e.k]=i))};i?(y.id=-1,Ve(y,n)):y()}}}let Ct=!1;const Qn=e=>/svg/.test(e.namespaceURI)&&e.tagName!=="foreignObject",Zn=e=>e.nodeType===8;function kc(e){const{mt:t,p:n,o:{patchProp:r,createText:o,nextSibling:s,parentNode:i,remove:l,insert:a,createComment:c}}=e,u=(v,b)=>{if(!b.hasChildNodes()){n(null,v,b),cr(),b._vnode=v;return}Ct=!1,f(b.firstChild,v,null,null,null),cr(),b._vnode=v,Ct&&console.error("Hydration completed but contains mismatches.")},f=(v,b,P,A,K,X=!1)=>{const M=Zn(v)&&v.data==="[",m=()=>w(v,b,P,A,K,M),{type:B,ref:D,shapeFlag:q,patchFlag:L}=b;let O=v.nodeType;b.el=v,L===-2&&(X=!1,b.dynamicChildren=null);let I=null;switch(B){case an:O!==3?b.children===""?(a(b.el=o(""),i(v),v),I=v):I=m():(v.data!==b.children&&(Ct=!0,v.data=b.children),I=s(v));break;case Ye:O!==8||M?I=m():I=s(v);break;case Tn:if(M&&(v=s(v),O=v.nodeType),O===1||O===3){I=v;const ie=!b.children.length;for(let U=0;U{X=X||!!b.dynamicChildren;const{type:M,props:m,patchFlag:B,shapeFlag:D,dirs:q}=b,L=M==="input"&&q||M==="option";if(L||B!==-1){if(q&&ct(b,null,P,"created"),m)if(L||!X||B&48)for(const I in m)(L&&I.endsWith("value")||Dn(I)&&!Cn(I))&&r(v,I,null,m[I],!1,void 0,P);else m.onClick&&r(v,"onClick",null,m.onClick,!1,void 0,P);let O;if((O=m&&m.onVnodeBeforeMount)&&Qe(O,P,b),q&&ct(b,null,P,"beforeMount"),((O=m&&m.onVnodeMounted)||q)&&$i(()=>{O&&Qe(O,P,b),q&&ct(b,null,P,"mounted")},A),D&16&&!(m&&(m.innerHTML||m.textContent))){let I=g(v.firstChild,b,v,P,A,K,X);for(;I;){Ct=!0;const ie=I;I=I.nextSibling,l(ie)}}else D&8&&v.textContent!==b.children&&(Ct=!0,v.textContent=b.children)}return v.nextSibling},g=(v,b,P,A,K,X,M)=>{M=M||!!b.dynamicChildren;const m=b.children,B=m.length;for(let D=0;D{const{slotScopeIds:M}=b;M&&(K=K?K.concat(M):M);const m=i(v),B=g(s(v),b,m,P,A,K,X);return B&&Zn(B)&&B.data==="]"?s(b.anchor=B):(Ct=!0,a(b.anchor=c("]"),m,B),B)},w=(v,b,P,A,K,X)=>{if(Ct=!0,b.el=null,X){const B=T(v);for(;;){const D=s(v);if(D&&D!==B)l(D);else break}}const M=s(v),m=i(v);return l(v),n(null,b,m,M,P,A,Qn(m),K),M},T=v=>{let b=0;for(;v;)if(v=s(v),v&&Zn(v)&&(v.data==="["&&b++,v.data==="]")){if(b===0)return s(v);b--}return v};return[u,f]}const Ve=$i;function Sc(e){return Ac(e,kc)}function Ac(e,t){const n=Yr();n.__VUE__=!0;const{insert:r,remove:o,patchProp:s,createElement:i,createText:l,createComment:a,setText:c,setElementText:u,parentNode:f,nextSibling:h,setScopeId:g=ot,insertStaticContent:y}=e,w=(d,p,_,E=null,C=null,k=null,H=!1,R=null,N=!!p.dynamicChildren)=>{if(d===p)return;d&&!Ft(d,p)&&(E=x(d),ze(d,C,k,!0),d=null),p.patchFlag===-2&&(N=!1,p.dynamicChildren=null);const{type:S,ref:G,shapeFlag:V}=p;switch(S){case an:T(d,p,_,E);break;case Ye:v(d,p,_,E);break;case Tn:d==null&&b(p,_,E,H);break;case ye:L(d,p,_,E,C,k,H,R,N);break;default:V&1?K(d,p,_,E,C,k,H,R,N):V&6?O(d,p,_,E,C,k,H,R,N):(V&64||V&128)&&S.process(d,p,_,E,C,k,H,R,N,$)}G!=null&&C&&pr(G,d&&d.ref,k,p||d,!p)},T=(d,p,_,E)=>{if(d==null)r(p.el=l(p.children),_,E);else{const C=p.el=d.el;p.children!==d.children&&c(C,p.children)}},v=(d,p,_,E)=>{d==null?r(p.el=a(p.children||""),_,E):p.el=d.el},b=(d,p,_,E)=>{[d.el,d.anchor]=y(d.children,p,_,E,d.el,d.anchor)},P=({el:d,anchor:p},_,E)=>{let C;for(;d&&d!==p;)C=h(d),r(d,_,E),d=C;r(p,_,E)},A=({el:d,anchor:p})=>{let _;for(;d&&d!==p;)_=h(d),o(d),d=_;o(p)},K=(d,p,_,E,C,k,H,R,N)=>{H=H||p.type==="svg",d==null?X(p,_,E,C,k,H,R,N):B(d,p,C,k,H,R,N)},X=(d,p,_,E,C,k,H,R)=>{let N,S;const{type:G,props:V,shapeFlag:Y,transition:te,dirs:le}=d;if(N=d.el=i(d.type,k,V&&V.is,V),Y&8?u(N,d.children):Y&16&&m(d.children,N,null,E,C,k&&G!=="foreignObject",H,R),le&&ct(d,null,E,"created"),M(N,d,d.scopeId,H,E),V){for(const ge in V)ge!=="value"&&!Cn(ge)&&s(N,ge,null,V[ge],k,d.children,E,C,Ie);"value"in V&&s(N,"value",null,V.value),(S=V.onVnodeBeforeMount)&&Qe(S,E,d)}le&&ct(d,null,E,"beforeMount");const _e=(!C||C&&!C.pendingBranch)&&te&&!te.persisted;_e&&te.beforeEnter(N),r(N,p,_),((S=V&&V.onVnodeMounted)||_e||le)&&Ve(()=>{S&&Qe(S,E,d),_e&&te.enter(N),le&&ct(d,null,E,"mounted")},C)},M=(d,p,_,E,C)=>{if(_&&g(d,_),E)for(let k=0;k{for(let S=N;S{const R=p.el=d.el;let{patchFlag:N,dynamicChildren:S,dirs:G}=p;N|=d.patchFlag&16;const V=d.props||Ee,Y=p.props||Ee;let te;_&&Nt(_,!1),(te=Y.onVnodeBeforeUpdate)&&Qe(te,_,p,d),G&&ct(p,d,_,"beforeUpdate"),_&&Nt(_,!0);const le=C&&p.type!=="foreignObject";if(S?D(d.dynamicChildren,S,R,_,E,le,k):H||oe(d,p,R,null,_,E,le,k,!1),N>0){if(N&16)q(R,p,V,Y,_,E,C);else if(N&2&&V.class!==Y.class&&s(R,"class",null,Y.class,C),N&4&&s(R,"style",V.style,Y.style,C),N&8){const _e=p.dynamicProps;for(let ge=0;ge<_e.length;ge++){const Se=_e[ge],et=V[Se],Jt=Y[Se];(Jt!==et||Se==="value")&&s(R,Se,et,Jt,C,d.children,_,E,Ie)}}N&1&&d.children!==p.children&&u(R,p.children)}else!H&&S==null&&q(R,p,V,Y,_,E,C);((te=Y.onVnodeUpdated)||G)&&Ve(()=>{te&&Qe(te,_,p,d),G&&ct(p,d,_,"updated")},E)},D=(d,p,_,E,C,k,H)=>{for(let R=0;R{if(_!==E){if(_!==Ee)for(const R in _)!Cn(R)&&!(R in E)&&s(d,R,_[R],null,H,p.children,C,k,Ie);for(const R in E){if(Cn(R))continue;const N=E[R],S=_[R];N!==S&&R!=="value"&&s(d,R,S,N,H,p.children,C,k,Ie)}"value"in E&&s(d,"value",_.value,E.value)}},L=(d,p,_,E,C,k,H,R,N)=>{const S=p.el=d?d.el:l(""),G=p.anchor=d?d.anchor:l("");let{patchFlag:V,dynamicChildren:Y,slotScopeIds:te}=p;te&&(R=R?R.concat(te):te),d==null?(r(S,_,E),r(G,_,E),m(p.children,_,G,C,k,H,R,N)):V>0&&V&64&&Y&&d.dynamicChildren?(D(d.dynamicChildren,Y,_,C,k,H,R),(p.key!=null||C&&p===C.subTree)&&Ji(d,p,!0)):oe(d,p,_,G,C,k,H,R,N)},O=(d,p,_,E,C,k,H,R,N)=>{p.slotScopeIds=R,d==null?p.shapeFlag&512?C.ctx.activate(p,_,E,H,N):I(p,_,E,C,k,H,N):ie(d,p,N)},I=(d,p,_,E,C,k,H)=>{const R=d.component=Hc(d,E,C);if(jn(d)&&(R.ctx.renderer=$),zc(R),R.asyncDep){if(C&&C.registerDep(R,U),!d.el){const N=R.subTree=ee(Ye);v(null,N,p,_)}return}U(R,d,p,_,C,k,H)},ie=(d,p,_)=>{const E=p.component=d.component;if(Ya(d,p,_))if(E.asyncDep&&!E.asyncResolved){re(E,p,_);return}else E.next=p,Ua(E.update),E.update();else p.el=d.el,E.vnode=p},U=(d,p,_,E,C,k,H)=>{const R=()=>{if(d.isMounted){let{next:G,bu:V,u:Y,parent:te,vnode:le}=d,_e=G,ge;Nt(d,!1),G?(G.el=le.el,re(d,G,H)):G=le,V&&Nr(V),(ge=G.props&&G.props.onVnodeBeforeUpdate)&&Qe(ge,te,G,le),Nt(d,!0);const Se=Mr(d),et=d.subTree;d.subTree=Se,w(et,Se,f(et.el),x(et),d,C,k),G.el=Se.el,_e===null&&Ja(d,Se.el),Y&&Ve(Y,C),(ge=G.props&&G.props.onVnodeUpdated)&&Ve(()=>Qe(ge,te,G,le),C)}else{let G;const{el:V,props:Y}=p,{bm:te,m:le,parent:_e}=d,ge=sn(p);if(Nt(d,!1),te&&Nr(te),!ge&&(G=Y&&Y.onVnodeBeforeMount)&&Qe(G,_e,p),Nt(d,!0),V&&ae){const Se=()=>{d.subTree=Mr(d),ae(V,d.subTree,d,C,null)};ge?p.type.__asyncLoader().then(()=>!d.isUnmounted&&Se()):Se()}else{const Se=d.subTree=Mr(d);w(null,Se,_,E,d,C,k),p.el=Se.el}if(le&&Ve(le,C),!ge&&(G=Y&&Y.onVnodeMounted)){const Se=p;Ve(()=>Qe(G,_e,Se),C)}(p.shapeFlag&256||_e&&sn(_e.vnode)&&_e.vnode.shapeFlag&256)&&d.a&&Ve(d.a,C),d.isMounted=!0,p=_=E=null}},N=d.effect=new _o(R,()=>Cr(S),d.scope),S=d.update=()=>N.run();S.id=d.uid,Nt(d,!0),S()},re=(d,p,_)=>{p.component=d;const E=d.vnode.props;d.vnode=p,d.next=null,xc(d,p.props,E,_),Tc(d,p.children,_),pn(),os(),mn()},oe=(d,p,_,E,C,k,H,R,N=!1)=>{const S=d&&d.children,G=d?d.shapeFlag:0,V=p.children,{patchFlag:Y,shapeFlag:te}=p;if(Y>0){if(Y&128){Ne(S,V,_,E,C,k,H,R,N);return}else if(Y&256){He(S,V,_,E,C,k,H,R,N);return}}te&8?(G&16&&Ie(S,C,k),V!==S&&u(_,V)):G&16?te&16?Ne(S,V,_,E,C,k,H,R,N):Ie(S,C,k,!0):(G&8&&u(_,""),te&16&&m(V,_,E,C,k,H,R,N))},He=(d,p,_,E,C,k,H,R,N)=>{d=d||tn,p=p||tn;const S=d.length,G=p.length,V=Math.min(S,G);let Y;for(Y=0;YG?Ie(d,C,k,!0,!1,V):m(p,_,E,C,k,H,R,N,V)},Ne=(d,p,_,E,C,k,H,R,N)=>{let S=0;const G=p.length;let V=d.length-1,Y=G-1;for(;S<=V&&S<=Y;){const te=d[S],le=p[S]=N?kt(p[S]):tt(p[S]);if(Ft(te,le))w(te,le,_,null,C,k,H,R,N);else break;S++}for(;S<=V&&S<=Y;){const te=d[V],le=p[Y]=N?kt(p[Y]):tt(p[Y]);if(Ft(te,le))w(te,le,_,null,C,k,H,R,N);else break;V--,Y--}if(S>V){if(S<=Y){const te=Y+1,le=teY)for(;S<=V;)ze(d[S],C,k,!0),S++;else{const te=S,le=S,_e=new Map;for(S=le;S<=Y;S++){const qe=p[S]=N?kt(p[S]):tt(p[S]);qe.key!=null&&_e.set(qe.key,S)}let ge,Se=0;const et=Y-le+1;let Jt=!1,Wo=0;const vn=new Array(et);for(S=0;S=et){ze(qe,C,k,!0);continue}let at;if(qe.key!=null)at=_e.get(qe.key);else for(ge=le;ge<=Y;ge++)if(vn[ge-le]===0&&Ft(qe,p[ge])){at=ge;break}at===void 0?ze(qe,C,k,!0):(vn[at-le]=S+1,at>=Wo?Wo=at:Jt=!0,w(qe,p[at],_,null,C,k,H,R,N),Se++)}const qo=Jt?Pc(vn):tn;for(ge=qo.length-1,S=et-1;S>=0;S--){const qe=le+S,at=p[qe],Go=qe+1{const{el:k,type:H,transition:R,children:N,shapeFlag:S}=d;if(S&6){Ue(d.component.subTree,p,_,E);return}if(S&128){d.suspense.move(p,_,E);return}if(S&64){H.move(d,p,_,$);return}if(H===ye){r(k,p,_);for(let V=0;VR.enter(k),C);else{const{leave:V,delayLeave:Y,afterLeave:te}=R,le=()=>r(k,p,_),_e=()=>{V(k,()=>{le(),te&&te()})};Y?Y(k,le,_e):_e()}else r(k,p,_)},ze=(d,p,_,E=!1,C=!1)=>{const{type:k,props:H,ref:R,children:N,dynamicChildren:S,shapeFlag:G,patchFlag:V,dirs:Y}=d;if(R!=null&&pr(R,null,_,d,!0),G&256){p.ctx.deactivate(d);return}const te=G&1&&Y,le=!sn(d);let _e;if(le&&(_e=H&&H.onVnodeBeforeUnmount)&&Qe(_e,p,d),G&6)lt(d.component,_,E);else{if(G&128){d.suspense.unmount(_,E);return}te&&ct(d,null,p,"beforeUnmount"),G&64?d.type.remove(d,p,_,C,$,E):S&&(k!==ye||V>0&&V&64)?Ie(S,p,_,!1,!0):(k===ye&&V&384||!C&&G&16)&&Ie(N,p,_),E&&Et(d)}(le&&(_e=H&&H.onVnodeUnmounted)||te)&&Ve(()=>{_e&&Qe(_e,p,d),te&&ct(d,null,p,"unmounted")},_)},Et=d=>{const{type:p,el:_,anchor:E,transition:C}=d;if(p===ye){wt(_,E);return}if(p===Tn){A(d);return}const k=()=>{o(_),C&&!C.persisted&&C.afterLeave&&C.afterLeave()};if(d.shapeFlag&1&&C&&!C.persisted){const{leave:H,delayLeave:R}=C,N=()=>H(_,k);R?R(d.el,k,N):N()}else k()},wt=(d,p)=>{let _;for(;d!==p;)_=h(d),o(d),d=_;o(p)},lt=(d,p,_)=>{const{bum:E,scope:C,update:k,subTree:H,um:R}=d;E&&Nr(E),C.stop(),k&&(k.active=!1,ze(H,d,p,_)),R&&Ve(R,p),Ve(()=>{d.isUnmounted=!0},p),p&&p.pendingBranch&&!p.isUnmounted&&d.asyncDep&&!d.asyncResolved&&d.suspenseId===p.pendingId&&(p.deps--,p.deps===0&&p.resolve())},Ie=(d,p,_,E=!1,C=!1,k=0)=>{for(let H=k;Hd.shapeFlag&6?x(d.component.subTree):d.shapeFlag&128?d.suspense.next():h(d.anchor||d.el),F=(d,p,_)=>{d==null?p._vnode&&ze(p._vnode,null,null,!0):w(p._vnode||null,d,p,null,null,null,_),os(),cr(),p._vnode=d},$={p:w,um:ze,m:Ue,r:Et,mt:I,mc:m,pc:oe,pbc:D,n:x,o:e};let W,ae;return t&&([W,ae]=t($)),{render:F,hydrate:W,createApp:Ec(F,W)}}function Nt({effect:e,update:t},n){e.allowRecurse=t.allowRecurse=n}function Ji(e,t,n=!1){const r=e.children,o=t.children;if(J(r)&&J(o))for(let s=0;s>1,e[n[l]]0&&(t[r]=n[s-1]),n[s]=r)}}for(s=n.length,i=n[s-1];s-- >0;)n[s]=i,i=t[i];return n}const Rc=e=>e.__isTeleport,ye=Symbol.for("v-fgt"),an=Symbol.for("v-txt"),Ye=Symbol.for("v-cmt"),Tn=Symbol.for("v-stc"),kn=[];let rt=null;function z(e=!1){kn.push(rt=e?null:[])}function Oc(){kn.pop(),rt=kn[kn.length-1]||null}let $n=1;function gs(e){$n+=e}function Qi(e){return e.dynamicChildren=$n>0?rt||tn:null,Oc(),$n>0&&rt&&rt.push(e),e}function Q(e,t,n,r,o,s){return Qi(fe(e,t,n,r,o,s,!0))}function Ae(e,t,n,r,o){return Qi(ee(e,t,n,r,o,!0))}function mr(e){return e?e.__v_isVNode===!0:!1}function Ft(e,t){return e.type===t.type&&e.key===t.key}const Ar="__vInternal",Zi=({key:e})=>e??null,or=({ref:e,ref_key:t,ref_for:n})=>(typeof e=="number"&&(e=""+e),e!=null?pe(e)||$e(e)||se(e)?{i:De,r:e,k:t,f:!!n}:e:null);function fe(e,t=null,n=null,r=0,o=null,s=e===ye?0:1,i=!1,l=!1){const a={__v_isVNode:!0,__v_skip:!0,type:e,props:t,key:t&&Zi(t),ref:t&&or(t),scopeId:Ii,slotScopeIds:null,children:n,component:null,suspense:null,ssContent:null,ssFallback:null,dirs:null,transition:null,el:null,anchor:null,target:null,targetAnchor:null,staticCount:0,shapeFlag:s,patchFlag:r,dynamicProps:o,dynamicChildren:null,appContext:null,ctx:De};return l?(Oo(a,n),s&128&&e.normalize(a)):n&&(a.shapeFlag|=pe(n)?8:16),$n>0&&!i&&rt&&(a.patchFlag>0||s&6)&&a.patchFlag!==32&&rt.push(a),a}const ee=Ic;function Ic(e,t=null,n=null,r=0,o=null,s=!1){if((!e||e===dc)&&(e=Ye),mr(e)){const l=It(e,t,!0);return n&&Oo(l,n),$n>0&&!s&&rt&&(l.shapeFlag&6?rt[rt.indexOf(e)]=l:rt.push(l)),l.patchFlag|=-2,l}if(Vc(e)&&(e=e.__vccOpts),t){t=$c(t);let{class:l,style:a}=t;l&&!pe(l)&&(t.class=Ke(l)),we(a)&&(Li(a)&&!J(a)&&(a=Pe({},a)),t.style=Hn(a))}const i=pe(e)?1:Qa(e)?128:Rc(e)?64:we(e)?4:se(e)?2:0;return fe(e,t,n,r,o,i,s,!0)}function $c(e){return e?Li(e)||Ar in e?Pe({},e):e:null}function It(e,t,n=!1){const{props:r,ref:o,patchFlag:s,children:i}=e,l=t?io(r||{},t):r;return{__v_isVNode:!0,__v_skip:!0,type:e.type,props:l,key:l&&Zi(l),ref:t&&t.ref?n&&o?J(o)?o.concat(or(t)):[o,or(t)]:or(t):o,scopeId:e.scopeId,slotScopeIds:e.slotScopeIds,children:i,target:e.target,targetAnchor:e.targetAnchor,staticCount:e.staticCount,shapeFlag:e.shapeFlag,patchFlag:t&&e.type!==ye?s===-1?16:s|16:s,dynamicProps:e.dynamicProps,dynamicChildren:e.dynamicChildren,appContext:e.appContext,dirs:e.dirs,transition:e.transition,component:e.component,suspense:e.suspense,ssContent:e.ssContent&&It(e.ssContent),ssFallback:e.ssFallback&&It(e.ssFallback),el:e.el,anchor:e.anchor,ctx:e.ctx,ce:e.ce}}function $t(e=" ",t=0){return ee(an,null,e,t)}function Nc(e,t){const n=ee(Tn,null,e);return n.staticCount=t,n}function Te(e="",t=!1){return t?(z(),Ae(Ye,null,e)):ee(Ye,null,e)}function tt(e){return e==null||typeof e=="boolean"?ee(Ye):J(e)?ee(ye,null,e.slice()):typeof e=="object"?kt(e):ee(an,null,String(e))}function kt(e){return e.el===null&&e.patchFlag!==-1||e.memo?e:It(e)}function Oo(e,t){let n=0;const{shapeFlag:r}=e;if(t==null)t=null;else if(J(t))n=16;else if(typeof t=="object")if(r&65){const o=t.default;o&&(o._c&&(o._d=!1),Oo(e,o()),o._c&&(o._d=!0));return}else{n=32;const o=t._;!o&&!(Ar in t)?t._ctx=De:o===3&&De&&(De.slots._===1?t._=1:(t._=2,e.patchFlag|=1024))}else se(t)?(t={default:t,_ctx:De},n=32):(t=String(t),r&64?(n=16,t=[$t(t)]):n=8);e.children=t,e.shapeFlag|=n}function io(...e){const t={};for(let n=0;nRe||De;let Io,Qt,vs="__VUE_INSTANCE_SETTERS__";(Qt=Yr()[vs])||(Qt=Yr()[vs]=[]),Qt.push(e=>Re=e),Io=e=>{Qt.length>1?Qt.forEach(t=>t(e)):Qt[0](e)};const cn=e=>{Io(e),e.scope.on()},Kt=()=>{Re&&Re.scope.off(),Io(null)};function el(e){return e.vnode.shapeFlag&4}let un=!1;function zc(e,t=!1){un=t;const{props:n,children:r}=e.vnode,o=el(e);wc(e,n,o,t),Lc(e,r);const s=o?Fc(e,t):void 0;return un=!1,s}function Fc(e,t){const n=e.type;e.accessCache=Object.create(null),e.proxy=Ti(new Proxy(e.ctx,pc));const{setup:r}=n;if(r){const o=e.setupContext=r.length>1?jc(e):null;cn(e),pn();const s=Pt(r,e,0,[e.props,o]);if(mn(),Kt(),ai(s)){if(s.then(Kt,Kt),t)return s.then(i=>{_s(e,i,t)}).catch(i=>{Bn(i,e,0)});e.asyncDep=s}else _s(e,s,t)}else tl(e,t)}function _s(e,t,n){se(t)?e.type.__ssrInlineRender?e.ssrRender=t:e.render=t:we(t)&&(e.setupState=Si(t)),tl(e,n)}let bs;function tl(e,t,n){const r=e.type;if(!e.render){if(!t&&bs&&!r.render){const o=r.template||Po(e).template;if(o){const{isCustomElement:s,compilerOptions:i}=e.appContext.config,{delimiters:l,compilerOptions:a}=r,c=Pe(Pe({isCustomElement:s,delimiters:l},i),a);r.render=bs(o,c)}}e.render=r.render||ot}cn(e),pn(),mc(e),mn(),Kt()}function Bc(e){return e.attrsProxy||(e.attrsProxy=new Proxy(e.attrs,{get(t,n){return We(e,"get","$attrs"),t[n]}}))}function jc(e){const t=n=>{e.exposed=n||{}};return{get attrs(){return Bc(e)},slots:e.slots,emit:e.emit,expose:t}}function Pr(e){if(e.exposed)return e.exposeProxy||(e.exposeProxy=new Proxy(Si(Ti(e.exposed)),{get(t,n){if(n in t)return t[n];if(n in Ln)return Ln[n](e)},has(t,n){return n in t||n in Ln}}))}function Uc(e,t=!0){return se(e)?e.displayName||e.name:e.name||t&&e.__name}function Vc(e){return se(e)&&"__vccOpts"in e}const j=(e,t)=>Fa(e,t,un);function ve(e,t,n){const r=arguments.length;return r===2?we(t)&&!J(t)?mr(t)?ee(e,null,[t]):ee(e,t):ee(e,null,t):(r>3?n=Array.prototype.slice.call(arguments,2):r===3&&mr(n)&&(n=[n]),ee(e,t,n))}const Kc=Symbol.for("v-scx"),Wc=()=>ke(Kc),qc="3.3.4",Gc="http://www.w3.org/2000/svg",Bt=typeof document<"u"?document:null,ys=Bt&&Bt.createElement("template"),Yc={insert:(e,t,n)=>{t.insertBefore(e,n||null)},remove:e=>{const t=e.parentNode;t&&t.removeChild(e)},createElement:(e,t,n,r)=>{const o=t?Bt.createElementNS(Gc,e):Bt.createElement(e,n?{is:n}:void 0);return e==="select"&&r&&r.multiple!=null&&o.setAttribute("multiple",r.multiple),o},createText:e=>Bt.createTextNode(e),createComment:e=>Bt.createComment(e),setText:(e,t)=>{e.nodeValue=t},setElementText:(e,t)=>{e.textContent=t},parentNode:e=>e.parentNode,nextSibling:e=>e.nextSibling,querySelector:e=>Bt.querySelector(e),setScopeId(e,t){e.setAttribute(t,"")},insertStaticContent(e,t,n,r,o,s){const i=n?n.previousSibling:t.lastChild;if(o&&(o===s||o.nextSibling))for(;t.insertBefore(o.cloneNode(!0),n),!(o===s||!(o=o.nextSibling)););else{ys.innerHTML=r?`${e}`:e;const l=ys.content;if(r){const a=l.firstChild;for(;a.firstChild;)l.appendChild(a.firstChild);l.removeChild(a)}t.insertBefore(l,n)}return[i?i.nextSibling:t.firstChild,n?n.previousSibling:t.lastChild]}};function Jc(e,t,n){const r=e._vtc;r&&(t=(t?[t,...r]:[...r]).join(" ")),t==null?e.removeAttribute("class"):n?e.setAttribute("class",t):e.className=t}function Qc(e,t,n){const r=e.style,o=pe(n);if(n&&!o){if(t&&!pe(t))for(const s in t)n[s]==null&&lo(r,s,"");for(const s in n)lo(r,s,n[s])}else{const s=r.display;o?t!==n&&(r.cssText=n):t&&e.removeAttribute("style"),"_vod"in e&&(r.display=s)}}const Es=/\s*!important$/;function lo(e,t,n){if(J(n))n.forEach(r=>lo(e,t,r));else if(n==null&&(n=""),t.startsWith("--"))e.setProperty(t,n);else{const r=Zc(e,t);Es.test(n)?e.setProperty(qt(r),n.replace(Es,""),"important"):e[r]=n}}const ws=["Webkit","Moz","ms"],Fr={};function Zc(e,t){const n=Fr[t];if(n)return n;let r=ft(t);if(r!=="filter"&&r in e)return Fr[t]=r;r=Er(r);for(let o=0;oBr||(su.then(()=>Br=0),Br=Date.now());function lu(e,t){const n=r=>{if(!r._vts)r._vts=Date.now();else if(r._vts<=n.attached)return;Ze(au(r,n.value),t,5,[r])};return n.value=e,n.attached=iu(),n}function au(e,t){if(J(t)){const n=e.stopImmediatePropagation;return e.stopImmediatePropagation=()=>{n.call(e),e._stopped=!0},t.map(r=>o=>!o._stopped&&r&&r(o))}else return t}const Ls=/^on[a-z]/,cu=(e,t,n,r,o=!1,s,i,l,a)=>{t==="class"?Jc(e,r,o):t==="style"?Qc(e,n,r):Dn(t)?ho(t)||ru(e,t,n,r,i):(t[0]==="."?(t=t.slice(1),!0):t[0]==="^"?(t=t.slice(1),!1):uu(e,t,r,o))?eu(e,t,r,s,i,l,a):(t==="true-value"?e._trueValue=r:t==="false-value"&&(e._falseValue=r),Xc(e,t,r,o))};function uu(e,t,n,r){return r?!!(t==="innerHTML"||t==="textContent"||t in e&&Ls.test(t)&&se(n)):t==="spellcheck"||t==="draggable"||t==="translate"||t==="form"||t==="list"&&e.tagName==="INPUT"||t==="type"&&e.tagName==="TEXTAREA"||Ls.test(t)&&pe(n)?!1:t in e}const Lt="transition",_n="animation",Un=(e,{slots:t})=>ve(tc,fu(e),t);Un.displayName="Transition";const nl={name:String,type:String,css:{type:Boolean,default:!0},duration:[String,Number,Object],enterFromClass:String,enterActiveClass:String,enterToClass:String,appearFromClass:String,appearActiveClass:String,appearToClass:String,leaveFromClass:String,leaveActiveClass:String,leaveToClass:String};Un.props=Pe({},Di,nl);const Mt=(e,t=[])=>{J(e)?e.forEach(n=>n(...t)):e&&e(...t)},Ts=e=>e?J(e)?e.some(t=>t.length>1):e.length>1:!1;function fu(e){const t={};for(const L in e)L in nl||(t[L]=e[L]);if(e.css===!1)return t;const{name:n="v",type:r,duration:o,enterFromClass:s=`${n}-enter-from`,enterActiveClass:i=`${n}-enter-active`,enterToClass:l=`${n}-enter-to`,appearFromClass:a=s,appearActiveClass:c=i,appearToClass:u=l,leaveFromClass:f=`${n}-leave-from`,leaveActiveClass:h=`${n}-leave-active`,leaveToClass:g=`${n}-leave-to`}=e,y=du(o),w=y&&y[0],T=y&&y[1],{onBeforeEnter:v,onEnter:b,onEnterCancelled:P,onLeave:A,onLeaveCancelled:K,onBeforeAppear:X=v,onAppear:M=b,onAppearCancelled:m=P}=t,B=(L,O,I)=>{Dt(L,O?u:l),Dt(L,O?c:i),I&&I()},D=(L,O)=>{L._isLeaving=!1,Dt(L,f),Dt(L,g),Dt(L,h),O&&O()},q=L=>(O,I)=>{const ie=L?M:b,U=()=>B(O,L,I);Mt(ie,[O,U]),ks(()=>{Dt(O,L?a:s),Tt(O,L?u:l),Ts(ie)||Ss(O,r,w,U)})};return Pe(t,{onBeforeEnter(L){Mt(v,[L]),Tt(L,s),Tt(L,i)},onBeforeAppear(L){Mt(X,[L]),Tt(L,a),Tt(L,c)},onEnter:q(!1),onAppear:q(!0),onLeave(L,O){L._isLeaving=!0;const I=()=>D(L,O);Tt(L,f),mu(),Tt(L,h),ks(()=>{L._isLeaving&&(Dt(L,f),Tt(L,g),Ts(A)||Ss(L,r,T,I))}),Mt(A,[L,I])},onEnterCancelled(L){B(L,!1),Mt(P,[L])},onAppearCancelled(L){B(L,!0),Mt(m,[L])},onLeaveCancelled(L){D(L),Mt(K,[L])}})}function du(e){if(e==null)return null;if(we(e))return[jr(e.enter),jr(e.leave)];{const t=jr(e);return[t,t]}}function jr(e){return Jl(e)}function Tt(e,t){t.split(/\s+/).forEach(n=>n&&e.classList.add(n)),(e._vtc||(e._vtc=new Set)).add(t)}function Dt(e,t){t.split(/\s+/).forEach(r=>r&&e.classList.remove(r));const{_vtc:n}=e;n&&(n.delete(t),n.size||(e._vtc=void 0))}function ks(e){requestAnimationFrame(()=>{requestAnimationFrame(e)})}let hu=0;function Ss(e,t,n,r){const o=e._endId=++hu,s=()=>{o===e._endId&&r()};if(n)return setTimeout(s,n);const{type:i,timeout:l,propCount:a}=pu(e,t);if(!i)return r();const c=i+"end";let u=0;const f=()=>{e.removeEventListener(c,h),s()},h=g=>{g.target===e&&++u>=a&&f()};setTimeout(()=>{u(n[y]||"").split(", "),o=r(`${Lt}Delay`),s=r(`${Lt}Duration`),i=As(o,s),l=r(`${_n}Delay`),a=r(`${_n}Duration`),c=As(l,a);let u=null,f=0,h=0;t===Lt?i>0&&(u=Lt,f=i,h=s.length):t===_n?c>0&&(u=_n,f=c,h=a.length):(f=Math.max(i,c),u=f>0?i>c?Lt:_n:null,h=u?u===Lt?s.length:a.length:0);const g=u===Lt&&/\b(transform|all)(,|$)/.test(r(`${Lt}Property`).toString());return{type:u,timeout:f,propCount:h,hasTransform:g}}function As(e,t){for(;e.lengthPs(n)+Ps(e[r])))}function Ps(e){return Number(e.slice(0,-1).replace(",","."))*1e3}function mu(){return document.body.offsetHeight}const gu={esc:"escape",space:" ",up:"arrow-up",left:"arrow-left",right:"arrow-right",down:"arrow-down",delete:"backspace"},vu=(e,t)=>n=>{if(!("key"in n))return;const r=qt(n.key);if(t.some(o=>o===r||gu[o]===r))return e(n)},gr={beforeMount(e,{value:t},{transition:n}){e._vod=e.style.display==="none"?"":e.style.display,n&&t?n.beforeEnter(e):bn(e,t)},mounted(e,{value:t},{transition:n}){n&&t&&n.enter(e)},updated(e,{value:t,oldValue:n},{transition:r}){!t!=!n&&(r?t?(r.beforeEnter(e),bn(e,!0),r.enter(e)):r.leave(e,()=>{bn(e,!1)}):bn(e,t))},beforeUnmount(e,{value:t}){bn(e,t)}};function bn(e,t){e.style.display=t?e._vod:"none"}const _u=Pe({patchProp:cu},Yc);let Ur,Rs=!1;function bu(){return Ur=Rs?Ur:Sc(_u),Rs=!0,Ur}const yu=(...e)=>{const t=bu().createApp(...e),{mount:n}=t;return t.mount=r=>{const o=Eu(r);if(o)return n(o,!0,o instanceof SVGElement)},t};function Eu(e){return pe(e)?document.querySelector(e):e}const wu={"v-8daa1a0e":()=>ne(()=>import("./index.html-3b5329ec.js"),[]).then(({data:e})=>e),"v-2d0a870d":()=>ne(()=>import("./index.html-749b8c09.js"),[]).then(({data:e})=>e),"v-c0c85b84":()=>ne(()=>import("./index.html-2a017d73.js"),[]).then(({data:e})=>e),"v-7b22efaf":()=>ne(()=>import("./example.html-de2cdc67.js"),[]).then(({data:e})=>e),"v-efb45d4c":()=>ne(()=>import("./home.html-4377e437.js"),[]).then(({data:e})=>e),"v-277b35ca":()=>ne(()=>import("./knowledge.html-fa621fe0.js"),[]).then(({data:e})=>e),"v-7de9012f":()=>ne(()=>import("./performance-optimization.html-69f9f3ed.js"),[]).then(({data:e})=>e),"v-72889797":()=>ne(()=>import("./quick-start.html-1c002ee2.js"),[]).then(({data:e})=>e),"v-206e5229":()=>ne(()=>import("./run-on-desktop.html-8df4653a.js"),[]).then(({data:e})=>e),"v-6fa10115":()=>ne(()=>import("./structural-zoom-table.html-60176671.js"),[]).then(({data:e})=>e),"v-7a15fe3b":()=>ne(()=>import("./about.html-b3c329f5.js"),[]).then(({data:e})=>e),"v-193cf592":()=>ne(()=>import("./contacts.html-6120246a.js"),[]).then(({data:e})=>e),"v-41967128":()=>ne(()=>import("./about.html-9df343ae.js"),[]).then(({data:e})=>e),"v-6cf86266":()=>ne(()=>import("./contacts.html-1fbcc5af.js"),[]).then(({data:e})=>e),"v-13b430a0":()=>ne(()=>import("./example.html-d65bcfc3.js"),[]).then(({data:e})=>e),"v-6a609e09":()=>ne(()=>import("./home.html-eff1e1a9.js"),[]).then(({data:e})=>e),"v-b4f1a468":()=>ne(()=>import("./knowledge.html-bf240073.js"),[]).then(({data:e})=>e),"v-7f55a05e":()=>ne(()=>import("./performance-optimization.html-4f96b155.js"),[]).then(({data:e})=>e),"v-24840ff0":()=>ne(()=>import("./quick-start.html-4917d6af.js"),[]).then(({data:e})=>e),"v-08f05018":()=>ne(()=>import("./run-on-desktop.html-6d699af5.js"),[]).then(({data:e})=>e),"v-aedd6f74":()=>ne(()=>import("./structural-zoom-table.html-9be6c1fa.js"),[]).then(({data:e})=>e),"v-3706649a":()=>ne(()=>import("./404.html-60b35caa.js"),[]).then(({data:e})=>e)},xu=JSON.parse('{"base":"/DexKit/","lang":"en-US","title":"DexKit","description":"A high-performance dex runtime parsing library implemented in C++","head":[],"locales":{"/en/":{"lang":"en-US","description":"A high-performance dex runtime parsing library implemented in C++"},"/zh-cn/":{"lang":"zh-CN","description":"一个使用 C++ 实现的高性能运行时 dex 解析库"}}}');var Cu=([e,t,n])=>e==="meta"&&t.name?`${e}.${t.name}`:["title","base"].includes(e)?e:e==="template"&&t.id?`${e}.${t.id}`:JSON.stringify([e,t,n]),Lu=e=>{const t=new Set,n=[];return e.forEach(r=>{const o=Cu(r);t.has(o)||(t.add(o),n.push(r))}),n},Vn=e=>/^(https?:)?\/\//.test(e),Tu=e=>/^mailto:/.test(e),ku=e=>/^tel:/.test(e),$o=e=>Object.prototype.toString.call(e)==="[object Object]",rl=e=>e[e.length-1]==="/"?e.slice(0,-1):e,ol=e=>e[0]==="/"?e.slice(1):e,sl=(e,t)=>{const n=Object.keys(e).sort((r,o)=>{const s=o.split("/").length-r.split("/").length;return s!==0?s:o.length-r.length});for(const r of n)if(t.startsWith(r))return r;return"/"};const il={"v-8daa1a0e":Le(()=>ne(()=>import("./index.html-4c5e4c4a.js"),[])),"v-2d0a870d":Le(()=>ne(()=>import("./index.html-1cabf326.js"),[])),"v-c0c85b84":Le(()=>ne(()=>import("./index.html-d4986fc6.js"),[])),"v-7b22efaf":Le(()=>ne(()=>import("./example.html-76d3f343.js"),[])),"v-efb45d4c":Le(()=>ne(()=>import("./home.html-44ee30de.js"),[])),"v-277b35ca":Le(()=>ne(()=>import("./knowledge.html-168eee0c.js"),[])),"v-7de9012f":Le(()=>ne(()=>import("./performance-optimization.html-0b660c19.js"),[])),"v-72889797":Le(()=>ne(()=>import("./quick-start.html-d0230eac.js"),[])),"v-206e5229":Le(()=>ne(()=>import("./run-on-desktop.html-426dfbb6.js"),[])),"v-6fa10115":Le(()=>ne(()=>import("./structural-zoom-table.html-ab94571a.js"),[])),"v-7a15fe3b":Le(()=>ne(()=>import("./about.html-a49d4a97.js"),[])),"v-193cf592":Le(()=>ne(()=>import("./contacts.html-ecff4547.js"),[])),"v-41967128":Le(()=>ne(()=>import("./about.html-8ea67162.js"),[])),"v-6cf86266":Le(()=>ne(()=>import("./contacts.html-335338da.js"),[])),"v-13b430a0":Le(()=>ne(()=>import("./example.html-6629fd57.js"),[])),"v-6a609e09":Le(()=>ne(()=>import("./home.html-6d2f5099.js"),[])),"v-b4f1a468":Le(()=>ne(()=>import("./knowledge.html-8e7358a7.js"),[])),"v-7f55a05e":Le(()=>ne(()=>import("./performance-optimization.html-91684308.js"),[])),"v-24840ff0":Le(()=>ne(()=>import("./quick-start.html-ba5a064e.js"),[])),"v-08f05018":Le(()=>ne(()=>import("./run-on-desktop.html-b6246f6d.js"),[])),"v-aedd6f74":Le(()=>ne(()=>import("./structural-zoom-table.html-0b909054.js"),[])),"v-3706649a":Le(()=>ne(()=>import("./404.html-fd1e0a9c.js"),[]))};var Su=Symbol(""),ll=Symbol(""),Au=Fn({key:"",path:"",title:"",lang:"",frontmatter:{},headers:[]}),Wt=()=>{const e=ke(ll);if(!e)throw new Error("pageData() is called without provider.");return e},al=Symbol(""),gt=()=>{const e=ke(al);if(!e)throw new Error("usePageFrontmatter() is called without provider.");return e},cl=Symbol(""),Pu=()=>{const e=ke(cl);if(!e)throw new Error("usePageHead() is called without provider.");return e},Ru=Symbol(""),ul=Symbol(""),Ou=()=>{const e=ke(ul);if(!e)throw new Error("usePageLang() is called without provider.");return e},fl=Symbol(""),Iu=()=>{const e=ke(fl);if(!e)throw new Error("usePageLayout() is called without provider.");return e},$u=xe(wu),No=Symbol(""),Rr=()=>{const e=ke(No);if(!e)throw new Error("useRouteLocale() is called without provider.");return e},en=xe(xu),dl=()=>en,hl=Symbol(""),Mo=()=>{const e=ke(hl);if(!e)throw new Error("useSiteLocaleData() is called without provider.");return e},Nu=Symbol(""),Mu="Layout",Du="NotFound",ht=zn({resolveLayouts:e=>e.reduce((t,n)=>({...t,...n.layouts}),{}),resolvePageData:async e=>{const t=$u.value[e];return await(t==null?void 0:t())??Au},resolvePageFrontmatter:e=>e.frontmatter,resolvePageHead:(e,t,n)=>{const r=pe(t.description)?t.description:n.description,o=[...J(t.head)?t.head:[],...n.head,["title",{},e],["meta",{name:"description",content:r}]];return Lu(o)},resolvePageHeadTitle:(e,t)=>[e.title,t.title].filter(n=>!!n).join(" | "),resolvePageLang:(e,t)=>e.lang||t.lang||"en-US",resolvePageLayout:(e,t)=>{let n;if(e.path){const r=e.frontmatter.layout;pe(r)?n=r:n=Mu}else n=Du;return t[n]},resolveRouteLocale:(e,t)=>sl(e,t),resolveSiteLocaleData:(e,t)=>({...e,...e.locales[t]})}),Do=he({name:"ClientOnly",setup(e,t){const n=xe(!1);return Xe(()=>{n.value=!0}),()=>{var r,o;return n.value?(o=(r=t.slots).default)==null?void 0:o.call(r):null}}}),Hu=he({name:"Content",props:{pageKey:{type:String,required:!1,default:""}},setup(e){const t=Wt(),n=j(()=>il[e.pageKey||t.value.key]);return()=>n.value?ve(n.value):ve("div","404 Not Found")}}),Gt=(e={})=>e,Ho=e=>Vn(e)?e:`/DexKit/${ol(e)}`;function pl(e,t,n){var r,o,s;t===void 0&&(t=50),n===void 0&&(n={});var i=(r=n.isImmediate)!=null&&r,l=(o=n.callback)!=null&&o,a=n.maxWait,c=Date.now(),u=[];function f(){if(a!==void 0){var g=Date.now()-c;if(g+t>=a)return a-g}return t}var h=function(){var g=[].slice.call(arguments),y=this;return new Promise(function(w,T){var v=i&&s===void 0;if(s!==void 0&&clearTimeout(s),s=setTimeout(function(){if(s=void 0,c=Date.now(),!i){var P=e.apply(y,g);l&&l(P),u.forEach(function(A){return(0,A.resolve)(P)}),u=[]}},f()),v){var b=e.apply(y,g);return l&&l(b),w(b)}u.push({resolve:w,reject:T})})};return h.cancel=function(g){s!==void 0&&clearTimeout(s),u.forEach(function(y){return(0,y.reject)(g)}),u=[]},h}/*! + * vue-router v4.2.5 + * (c) 2023 Eduardo San Martin Morote + * @license MIT + */const Xt=typeof window<"u";function zu(e){return e.__esModule||e[Symbol.toStringTag]==="Module"}const me=Object.assign;function Vr(e,t){const n={};for(const r in t){const o=t[r];n[r]=it(o)?o.map(e):e(o)}return n}const Sn=()=>{},it=Array.isArray,Fu=/\/$/,Bu=e=>e.replace(Fu,"");function Kr(e,t,n="/"){let r,o={},s="",i="";const l=t.indexOf("#");let a=t.indexOf("?");return l=0&&(a=-1),a>-1&&(r=t.slice(0,a),s=t.slice(a+1,l>-1?l:t.length),o=e(s)),l>-1&&(r=r||t.slice(0,l),i=t.slice(l,t.length)),r=Ku(r??t,n),{fullPath:r+(s&&"?")+s+i,path:r,query:o,hash:i}}function ju(e,t){const n=t.query?e(t.query):"";return t.path+(n&&"?")+n+(t.hash||"")}function Os(e,t){return!t||!e.toLowerCase().startsWith(t.toLowerCase())?e:e.slice(t.length)||"/"}function Uu(e,t,n){const r=t.matched.length-1,o=n.matched.length-1;return r>-1&&r===o&&fn(t.matched[r],n.matched[o])&&ml(t.params,n.params)&&e(t.query)===e(n.query)&&t.hash===n.hash}function fn(e,t){return(e.aliasOf||e)===(t.aliasOf||t)}function ml(e,t){if(Object.keys(e).length!==Object.keys(t).length)return!1;for(const n in e)if(!Vu(e[n],t[n]))return!1;return!0}function Vu(e,t){return it(e)?Is(e,t):it(t)?Is(t,e):e===t}function Is(e,t){return it(t)?e.length===t.length&&e.every((n,r)=>n===t[r]):e.length===1&&e[0]===t}function Ku(e,t){if(e.startsWith("/"))return e;if(!e)return t;const n=t.split("/"),r=e.split("/"),o=r[r.length-1];(o===".."||o===".")&&r.push("");let s=n.length-1,i,l;for(i=0;i1&&s--;else break;return n.slice(0,s).join("/")+"/"+r.slice(i-(i===r.length?1:0)).join("/")}var Nn;(function(e){e.pop="pop",e.push="push"})(Nn||(Nn={}));var An;(function(e){e.back="back",e.forward="forward",e.unknown=""})(An||(An={}));function Wu(e){if(!e)if(Xt){const t=document.querySelector("base");e=t&&t.getAttribute("href")||"/",e=e.replace(/^\w+:\/\/[^\/]+/,"")}else e="/";return e[0]!=="/"&&e[0]!=="#"&&(e="/"+e),Bu(e)}const qu=/^[^#]+#/;function Gu(e,t){return e.replace(qu,"#")+t}function Yu(e,t){const n=document.documentElement.getBoundingClientRect(),r=e.getBoundingClientRect();return{behavior:t.behavior,left:r.left-n.left-(t.left||0),top:r.top-n.top-(t.top||0)}}const Or=()=>({left:window.pageXOffset,top:window.pageYOffset});function Ju(e){let t;if("el"in e){const n=e.el,r=typeof n=="string"&&n.startsWith("#"),o=typeof n=="string"?r?document.getElementById(n.slice(1)):document.querySelector(n):n;if(!o)return;t=Yu(o,e)}else t=e;"scrollBehavior"in document.documentElement.style?window.scrollTo(t):window.scrollTo(t.left!=null?t.left:window.pageXOffset,t.top!=null?t.top:window.pageYOffset)}function $s(e,t){return(history.state?history.state.position-t:-1)+e}const ao=new Map;function Qu(e,t){ao.set(e,t)}function Zu(e){const t=ao.get(e);return ao.delete(e),t}let Xu=()=>location.protocol+"//"+location.host;function gl(e,t){const{pathname:n,search:r,hash:o}=t,s=e.indexOf("#");if(s>-1){let l=o.includes(e.slice(s))?e.slice(s).length:1,a=o.slice(l);return a[0]!=="/"&&(a="/"+a),Os(a,"")}return Os(n,e)+r+o}function ef(e,t,n,r){let o=[],s=[],i=null;const l=({state:h})=>{const g=gl(e,location),y=n.value,w=t.value;let T=0;if(h){if(n.value=g,t.value=h,i&&i===y){i=null;return}T=w?h.position-w.position:0}else r(g);o.forEach(v=>{v(n.value,y,{delta:T,type:Nn.pop,direction:T?T>0?An.forward:An.back:An.unknown})})};function a(){i=n.value}function c(h){o.push(h);const g=()=>{const y=o.indexOf(h);y>-1&&o.splice(y,1)};return s.push(g),g}function u(){const{history:h}=window;h.state&&h.replaceState(me({},h.state,{scroll:Or()}),"")}function f(){for(const h of s)h();s=[],window.removeEventListener("popstate",l),window.removeEventListener("beforeunload",u)}return window.addEventListener("popstate",l),window.addEventListener("beforeunload",u,{passive:!0}),{pauseListeners:a,listen:c,destroy:f}}function Ns(e,t,n,r=!1,o=!1){return{back:e,current:t,forward:n,replaced:r,position:window.history.length,scroll:o?Or():null}}function tf(e){const{history:t,location:n}=window,r={value:gl(e,n)},o={value:t.state};o.value||s(r.value,{back:null,current:r.value,forward:null,position:t.length-1,replaced:!0,scroll:null},!0);function s(a,c,u){const f=e.indexOf("#"),h=f>-1?(n.host&&document.querySelector("base")?e:e.slice(f))+a:Xu()+e+a;try{t[u?"replaceState":"pushState"](c,"",h),o.value=c}catch(g){console.error(g),n[u?"replace":"assign"](h)}}function i(a,c){const u=me({},t.state,Ns(o.value.back,a,o.value.forward,!0),c,{position:o.value.position});s(a,u,!0),r.value=a}function l(a,c){const u=me({},o.value,t.state,{forward:a,scroll:Or()});s(u.current,u,!0);const f=me({},Ns(r.value,a,null),{position:u.position+1},c);s(a,f,!1),r.value=a}return{location:r,state:o,push:l,replace:i}}function nf(e){e=Wu(e);const t=tf(e),n=ef(e,t.state,t.location,t.replace);function r(s,i=!0){i||n.pauseListeners(),history.go(s)}const o=me({location:"",base:e,go:r,createHref:Gu.bind(null,e)},t,n);return Object.defineProperty(o,"location",{enumerable:!0,get:()=>t.location.value}),Object.defineProperty(o,"state",{enumerable:!0,get:()=>t.state.value}),o}function rf(e){return typeof e=="string"||e&&typeof e=="object"}function vl(e){return typeof e=="string"||typeof e=="symbol"}const pt={path:"/",name:void 0,params:{},query:{},hash:"",fullPath:"/",matched:[],meta:{},redirectedFrom:void 0},_l=Symbol("");var Ms;(function(e){e[e.aborted=4]="aborted",e[e.cancelled=8]="cancelled",e[e.duplicated=16]="duplicated"})(Ms||(Ms={}));function dn(e,t){return me(new Error,{type:e,[_l]:!0},t)}function dt(e,t){return e instanceof Error&&_l in e&&(t==null||!!(e.type&t))}const Ds="[^/]+?",of={sensitive:!1,strict:!1,start:!0,end:!0},sf=/[.+*?^${}()[\]/\\]/g;function lf(e,t){const n=me({},of,t),r=[];let o=n.start?"^":"";const s=[];for(const c of e){const u=c.length?[]:[90];n.strict&&!c.length&&(o+="/");for(let f=0;ft.length?t.length===1&&t[0]===40+40?1:-1:0}function cf(e,t){let n=0;const r=e.score,o=t.score;for(;n0&&t[t.length-1]<0}const uf={type:0,value:""},ff=/[a-zA-Z0-9_]/;function df(e){if(!e)return[[]];if(e==="/")return[[uf]];if(!e.startsWith("/"))throw new Error(`Invalid path "${e}"`);function t(g){throw new Error(`ERR (${n})/"${c}": ${g}`)}let n=0,r=n;const o=[];let s;function i(){s&&o.push(s),s=[]}let l=0,a,c="",u="";function f(){c&&(n===0?s.push({type:0,value:c}):n===1||n===2||n===3?(s.length>1&&(a==="*"||a==="+")&&t(`A repeatable param (${c}) must be alone in its segment. eg: '/:ids+.`),s.push({type:1,value:c,regexp:u,repeatable:a==="*"||a==="+",optional:a==="*"||a==="?"})):t("Invalid state to consume buffer"),c="")}function h(){c+=a}for(;l{i(b)}:Sn}function i(u){if(vl(u)){const f=r.get(u);f&&(r.delete(u),n.splice(n.indexOf(f),1),f.children.forEach(i),f.alias.forEach(i))}else{const f=n.indexOf(u);f>-1&&(n.splice(f,1),u.record.name&&r.delete(u.record.name),u.children.forEach(i),u.alias.forEach(i))}}function l(){return n}function a(u){let f=0;for(;f=0&&(u.record.path!==n[f].record.path||!bl(u,n[f]));)f++;n.splice(f,0,u),u.record.name&&!Fs(u)&&r.set(u.record.name,u)}function c(u,f){let h,g={},y,w;if("name"in u&&u.name){if(h=r.get(u.name),!h)throw dn(1,{location:u});w=h.record.name,g=me(zs(f.params,h.keys.filter(b=>!b.optional).map(b=>b.name)),u.params&&zs(u.params,h.keys.map(b=>b.name))),y=h.stringify(g)}else if("path"in u)y=u.path,h=n.find(b=>b.re.test(y)),h&&(g=h.parse(y),w=h.record.name);else{if(h=f.name?r.get(f.name):n.find(b=>b.re.test(f.path)),!h)throw dn(1,{location:u,currentLocation:f});w=h.record.name,g=me({},f.params,u.params),y=h.stringify(g)}const T=[];let v=h;for(;v;)T.unshift(v.record),v=v.parent;return{name:w,path:y,params:g,matched:T,meta:vf(T)}}return e.forEach(u=>s(u)),{addRoute:s,resolve:c,removeRoute:i,getRoutes:l,getRecordMatcher:o}}function zs(e,t){const n={};for(const r of t)r in e&&(n[r]=e[r]);return n}function mf(e){return{path:e.path,redirect:e.redirect,name:e.name,meta:e.meta||{},aliasOf:void 0,beforeEnter:e.beforeEnter,props:gf(e),children:e.children||[],instances:{},leaveGuards:new Set,updateGuards:new Set,enterCallbacks:{},components:"components"in e?e.components||null:e.component&&{default:e.component}}}function gf(e){const t={},n=e.props||!1;if("component"in e)t.default=n;else for(const r in e.components)t[r]=typeof n=="object"?n[r]:n;return t}function Fs(e){for(;e;){if(e.record.aliasOf)return!0;e=e.parent}return!1}function vf(e){return e.reduce((t,n)=>me(t,n.meta),{})}function Bs(e,t){const n={};for(const r in e)n[r]=r in t?t[r]:e[r];return n}function bl(e,t){return t.children.some(n=>n===e||bl(e,n))}const yl=/#/g,_f=/&/g,bf=/\//g,yf=/=/g,Ef=/\?/g,El=/\+/g,wf=/%5B/g,xf=/%5D/g,wl=/%5E/g,Cf=/%60/g,xl=/%7B/g,Lf=/%7C/g,Cl=/%7D/g,Tf=/%20/g;function zo(e){return encodeURI(""+e).replace(Lf,"|").replace(wf,"[").replace(xf,"]")}function kf(e){return zo(e).replace(xl,"{").replace(Cl,"}").replace(wl,"^")}function co(e){return zo(e).replace(El,"%2B").replace(Tf,"+").replace(yl,"%23").replace(_f,"%26").replace(Cf,"`").replace(xl,"{").replace(Cl,"}").replace(wl,"^")}function Sf(e){return co(e).replace(yf,"%3D")}function Af(e){return zo(e).replace(yl,"%23").replace(Ef,"%3F")}function Pf(e){return e==null?"":Af(e).replace(bf,"%2F")}function vr(e){try{return decodeURIComponent(""+e)}catch{}return""+e}function Rf(e){const t={};if(e===""||e==="?")return t;const r=(e[0]==="?"?e.slice(1):e).split("&");for(let o=0;os&&co(s)):[r&&co(r)]).forEach(s=>{s!==void 0&&(t+=(t.length?"&":"")+n,s!=null&&(t+="="+s))})}return t}function Of(e){const t={};for(const n in e){const r=e[n];r!==void 0&&(t[n]=it(r)?r.map(o=>o==null?null:""+o):r==null?r:""+r)}return t}const If=Symbol(""),Us=Symbol(""),Ir=Symbol(""),Fo=Symbol(""),uo=Symbol("");function yn(){let e=[];function t(r){return e.push(r),()=>{const o=e.indexOf(r);o>-1&&e.splice(o,1)}}function n(){e=[]}return{add:t,list:()=>e.slice(),reset:n}}function St(e,t,n,r,o){const s=r&&(r.enterCallbacks[o]=r.enterCallbacks[o]||[]);return()=>new Promise((i,l)=>{const a=f=>{f===!1?l(dn(4,{from:n,to:t})):f instanceof Error?l(f):rf(f)?l(dn(2,{from:t,to:f})):(s&&r.enterCallbacks[o]===s&&typeof f=="function"&&s.push(f),i())},c=e.call(r&&r.instances[o],t,n,a);let u=Promise.resolve(c);e.length<3&&(u=u.then(a)),u.catch(f=>l(f))})}function Wr(e,t,n,r){const o=[];for(const s of e)for(const i in s.components){let l=s.components[i];if(!(t!=="beforeRouteEnter"&&!s.instances[i]))if($f(l)){const c=(l.__vccOpts||l)[t];c&&o.push(St(c,n,r,s,i))}else{let a=l();o.push(()=>a.then(c=>{if(!c)return Promise.reject(new Error(`Couldn't resolve component "${i}" at "${s.path}"`));const u=zu(c)?c.default:c;s.components[i]=u;const h=(u.__vccOpts||u)[t];return h&&St(h,n,r,s,i)()}))}}return o}function $f(e){return typeof e=="object"||"displayName"in e||"props"in e||"__vccOpts"in e}function Vs(e){const t=ke(Ir),n=ke(Fo),r=j(()=>t.resolve(Z(e.to))),o=j(()=>{const{matched:a}=r.value,{length:c}=a,u=a[c-1],f=n.matched;if(!u||!f.length)return-1;const h=f.findIndex(fn.bind(null,u));if(h>-1)return h;const g=Ks(a[c-2]);return c>1&&Ks(u)===g&&f[f.length-1].path!==g?f.findIndex(fn.bind(null,a[c-2])):h}),s=j(()=>o.value>-1&&Hf(n.params,r.value.params)),i=j(()=>o.value>-1&&o.value===n.matched.length-1&&ml(n.params,r.value.params));function l(a={}){return Df(a)?t[Z(e.replace)?"replace":"push"](Z(e.to)).catch(Sn):Promise.resolve()}return{route:r,href:j(()=>r.value.href),isActive:s,isExactActive:i,navigate:l}}const Nf=he({name:"RouterLink",compatConfig:{MODE:3},props:{to:{type:[String,Object],required:!0},replace:Boolean,activeClass:String,exactActiveClass:String,custom:Boolean,ariaCurrentValue:{type:String,default:"page"}},useLink:Vs,setup(e,{slots:t}){const n=zn(Vs(e)),{options:r}=ke(Ir),o=j(()=>({[Ws(e.activeClass,r.linkActiveClass,"router-link-active")]:n.isActive,[Ws(e.exactActiveClass,r.linkExactActiveClass,"router-link-exact-active")]:n.isExactActive}));return()=>{const s=t.default&&t.default(n);return e.custom?s:ve("a",{"aria-current":n.isExactActive?e.ariaCurrentValue:null,href:n.href,onClick:n.navigate,class:o.value},s)}}}),Mf=Nf;function Df(e){if(!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)&&!e.defaultPrevented&&!(e.button!==void 0&&e.button!==0)){if(e.currentTarget&&e.currentTarget.getAttribute){const t=e.currentTarget.getAttribute("target");if(/\b_blank\b/i.test(t))return}return e.preventDefault&&e.preventDefault(),!0}}function Hf(e,t){for(const n in t){const r=t[n],o=e[n];if(typeof r=="string"){if(r!==o)return!1}else if(!it(o)||o.length!==r.length||r.some((s,i)=>s!==o[i]))return!1}return!0}function Ks(e){return e?e.aliasOf?e.aliasOf.path:e.path:""}const Ws=(e,t,n)=>e??t??n,zf=he({name:"RouterView",inheritAttrs:!1,props:{name:{type:String,default:"default"},route:Object},compatConfig:{MODE:3},setup(e,{attrs:t,slots:n}){const r=ke(uo),o=j(()=>e.route||r.value),s=ke(Us,0),i=j(()=>{let c=Z(s);const{matched:u}=o.value;let f;for(;(f=u[c])&&!f.components;)c++;return c}),l=j(()=>o.value.matched[i.value]);Vt(Us,j(()=>i.value+1)),Vt(If,l),Vt(uo,o);const a=xe();return st(()=>[a.value,l.value,e.name],([c,u,f],[h,g,y])=>{u&&(u.instances[f]=c,g&&g!==u&&c&&c===h&&(u.leaveGuards.size||(u.leaveGuards=g.leaveGuards),u.updateGuards.size||(u.updateGuards=g.updateGuards))),c&&u&&(!g||!fn(u,g)||!h)&&(u.enterCallbacks[f]||[]).forEach(w=>w(c))},{flush:"post"}),()=>{const c=o.value,u=e.name,f=l.value,h=f&&f.components[u];if(!h)return qs(n.default,{Component:h,route:c});const g=f.props[u],y=g?g===!0?c.params:typeof g=="function"?g(c):g:null,T=ve(h,me({},y,t,{onVnodeUnmounted:v=>{v.component.isUnmounted&&(f.instances[u]=null)},ref:a}));return qs(n.default,{Component:T,route:c})||T}}});function qs(e,t){if(!e)return null;const n=e(t);return n.length===1?n[0]:n}const Ll=zf;function Ff(e){const t=pf(e.routes,e),n=e.parseQuery||Rf,r=e.stringifyQuery||js,o=e.history,s=yn(),i=yn(),l=yn(),a=To(pt);let c=pt;Xt&&e.scrollBehavior&&"scrollRestoration"in history&&(history.scrollRestoration="manual");const u=Vr.bind(null,x=>""+x),f=Vr.bind(null,Pf),h=Vr.bind(null,vr);function g(x,F){let $,W;return vl(x)?($=t.getRecordMatcher(x),W=F):W=x,t.addRoute(W,$)}function y(x){const F=t.getRecordMatcher(x);F&&t.removeRoute(F)}function w(){return t.getRoutes().map(x=>x.record)}function T(x){return!!t.getRecordMatcher(x)}function v(x,F){if(F=me({},F||a.value),typeof x=="string"){const _=Kr(n,x,F.path),E=t.resolve({path:_.path},F),C=o.createHref(_.fullPath);return me(_,E,{params:h(E.params),hash:vr(_.hash),redirectedFrom:void 0,href:C})}let $;if("path"in x)$=me({},x,{path:Kr(n,x.path,F.path).path});else{const _=me({},x.params);for(const E in _)_[E]==null&&delete _[E];$=me({},x,{params:f(_)}),F.params=f(F.params)}const W=t.resolve($,F),ae=x.hash||"";W.params=u(h(W.params));const d=ju(r,me({},x,{hash:kf(ae),path:W.path})),p=o.createHref(d);return me({fullPath:d,hash:ae,query:r===js?Of(x.query):x.query||{}},W,{redirectedFrom:void 0,href:p})}function b(x){return typeof x=="string"?Kr(n,x,a.value.path):me({},x)}function P(x,F){if(c!==x)return dn(8,{from:F,to:x})}function A(x){return M(x)}function K(x){return A(me(b(x),{replace:!0}))}function X(x){const F=x.matched[x.matched.length-1];if(F&&F.redirect){const{redirect:$}=F;let W=typeof $=="function"?$(x):$;return typeof W=="string"&&(W=W.includes("?")||W.includes("#")?W=b(W):{path:W},W.params={}),me({query:x.query,hash:x.hash,params:"path"in W?{}:x.params},W)}}function M(x,F){const $=c=v(x),W=a.value,ae=x.state,d=x.force,p=x.replace===!0,_=X($);if(_)return M(me(b(_),{state:typeof _=="object"?me({},ae,_.state):ae,force:d,replace:p}),F||$);const E=$;E.redirectedFrom=F;let C;return!d&&Uu(r,W,$)&&(C=dn(16,{to:E,from:W}),Ue(W,W,!0,!1)),(C?Promise.resolve(C):D(E,W)).catch(k=>dt(k)?dt(k,2)?k:Ne(k):oe(k,E,W)).then(k=>{if(k){if(dt(k,2))return M(me({replace:p},b(k.to),{state:typeof k.to=="object"?me({},ae,k.to.state):ae,force:d}),F||E)}else k=L(E,W,!0,p,ae);return q(E,W,k),k})}function m(x,F){const $=P(x,F);return $?Promise.reject($):Promise.resolve()}function B(x){const F=wt.values().next().value;return F&&typeof F.runWithContext=="function"?F.runWithContext(x):x()}function D(x,F){let $;const[W,ae,d]=Bf(x,F);$=Wr(W.reverse(),"beforeRouteLeave",x,F);for(const _ of W)_.leaveGuards.forEach(E=>{$.push(St(E,x,F))});const p=m.bind(null,x,F);return $.push(p),Ie($).then(()=>{$=[];for(const _ of s.list())$.push(St(_,x,F));return $.push(p),Ie($)}).then(()=>{$=Wr(ae,"beforeRouteUpdate",x,F);for(const _ of ae)_.updateGuards.forEach(E=>{$.push(St(E,x,F))});return $.push(p),Ie($)}).then(()=>{$=[];for(const _ of d)if(_.beforeEnter)if(it(_.beforeEnter))for(const E of _.beforeEnter)$.push(St(E,x,F));else $.push(St(_.beforeEnter,x,F));return $.push(p),Ie($)}).then(()=>(x.matched.forEach(_=>_.enterCallbacks={}),$=Wr(d,"beforeRouteEnter",x,F),$.push(p),Ie($))).then(()=>{$=[];for(const _ of i.list())$.push(St(_,x,F));return $.push(p),Ie($)}).catch(_=>dt(_,8)?_:Promise.reject(_))}function q(x,F,$){l.list().forEach(W=>B(()=>W(x,F,$)))}function L(x,F,$,W,ae){const d=P(x,F);if(d)return d;const p=F===pt,_=Xt?history.state:{};$&&(W||p?o.replace(x.fullPath,me({scroll:p&&_&&_.scroll},ae)):o.push(x.fullPath,ae)),a.value=x,Ue(x,F,$,p),Ne()}let O;function I(){O||(O=o.listen((x,F,$)=>{if(!lt.listening)return;const W=v(x),ae=X(W);if(ae){M(me(ae,{replace:!0}),W).catch(Sn);return}c=W;const d=a.value;Xt&&Qu($s(d.fullPath,$.delta),Or()),D(W,d).catch(p=>dt(p,12)?p:dt(p,2)?(M(p.to,W).then(_=>{dt(_,20)&&!$.delta&&$.type===Nn.pop&&o.go(-1,!1)}).catch(Sn),Promise.reject()):($.delta&&o.go(-$.delta,!1),oe(p,W,d))).then(p=>{p=p||L(W,d,!1),p&&($.delta&&!dt(p,8)?o.go(-$.delta,!1):$.type===Nn.pop&&dt(p,20)&&o.go(-1,!1)),q(W,d,p)}).catch(Sn)}))}let ie=yn(),U=yn(),re;function oe(x,F,$){Ne(x);const W=U.list();return W.length?W.forEach(ae=>ae(x,F,$)):console.error(x),Promise.reject(x)}function He(){return re&&a.value!==pt?Promise.resolve():new Promise((x,F)=>{ie.add([x,F])})}function Ne(x){return re||(re=!x,I(),ie.list().forEach(([F,$])=>x?$(x):F()),ie.reset()),x}function Ue(x,F,$,W){const{scrollBehavior:ae}=e;if(!Xt||!ae)return Promise.resolve();const d=!$&&Zu($s(x.fullPath,0))||(W||!$)&&history.state&&history.state.scroll||null;return xr().then(()=>ae(x,F,d)).then(p=>p&&Ju(p)).catch(p=>oe(p,x,F))}const ze=x=>o.go(x);let Et;const wt=new Set,lt={currentRoute:a,listening:!0,addRoute:g,removeRoute:y,hasRoute:T,getRoutes:w,resolve:v,options:e,push:A,replace:K,go:ze,back:()=>ze(-1),forward:()=>ze(1),beforeEach:s.add,beforeResolve:i.add,afterEach:l.add,onError:U.add,isReady:He,install(x){const F=this;x.component("RouterLink",Mf),x.component("RouterView",Ll),x.config.globalProperties.$router=F,Object.defineProperty(x.config.globalProperties,"$route",{enumerable:!0,get:()=>Z(a)}),Xt&&!Et&&a.value===pt&&(Et=!0,A(o.location).catch(ae=>{}));const $={};for(const ae in pt)Object.defineProperty($,ae,{get:()=>a.value[ae],enumerable:!0});x.provide(Ir,F),x.provide(Fo,Ci($)),x.provide(uo,a);const W=x.unmount;wt.add(x),x.unmount=function(){wt.delete(x),wt.size<1&&(c=pt,O&&O(),O=null,a.value=pt,Et=!1,re=!1),W()}}};function Ie(x){return x.reduce((F,$)=>F.then(()=>B($)),Promise.resolve())}return lt}function Bf(e,t){const n=[],r=[],o=[],s=Math.max(t.matched.length,e.matched.length);for(let i=0;ifn(c,l))?r.push(l):n.push(l));const a=e.matched[i];a&&(t.matched.find(c=>fn(c,a))||o.push(a))}return[n,r,o]}function gn(){return ke(Ir)}function Yt(){return ke(Fo)}const jf=({headerLinkSelector:e,headerAnchorSelector:t,delay:n,offset:r=5})=>{const o=gn(),i=pl(()=>{var w,T;const l=Math.max(window.scrollY,document.documentElement.scrollTop,document.body.scrollTop);if(Math.abs(l-0)h.some(b=>b.hash===v.hash));for(let v=0;v=(((w=b.parentElement)==null?void 0:w.offsetTop)??0)-r,K=!P||l<(((T=P.parentElement)==null?void 0:T.offsetTop)??0)-r;if(!(A&&K))continue;const M=decodeURIComponent(o.currentRoute.value.hash),m=decodeURIComponent(b.hash);if(M===m)return;if(f){for(let B=v+1;B{window.addEventListener("scroll",i)}),kr(()=>{window.removeEventListener("scroll",i)})},Gs=async(e,t)=>{const{scrollBehavior:n}=e.options;e.options.scrollBehavior=void 0,await e.replace({query:e.currentRoute.value.query,hash:t}).finally(()=>e.options.scrollBehavior=n)},Uf="a.sidebar-item",Vf=".header-anchor",Kf=300,Wf=5,qf=Gt({setup(){jf({headerLinkSelector:Uf,headerAnchorSelector:Vf,delay:Kf,offset:Wf})}}),Ys=()=>window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop||0,Gf=()=>window.scrollTo({top:0,behavior:"smooth"});const Yf=he({name:"BackToTop",setup(){const e=xe(0),t=j(()=>e.value>300),n=pl(()=>{e.value=Ys()},100);Xe(()=>{e.value=Ys(),window.addEventListener("scroll",()=>n())});const r=ve("div",{class:"back-to-top",onClick:Gf});return()=>ve(Un,{name:"back-to-top"},()=>t.value?r:null)}}),Jf=Gt({rootComponents:[Yf]});const Qf=ve("svg",{class:"external-link-icon",xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",x:"0px",y:"0px",viewBox:"0 0 100 100",width:"15",height:"15"},[ve("path",{fill:"currentColor",d:"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"}),ve("polygon",{fill:"currentColor",points:"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"})]),Zf=he({name:"ExternalLinkIcon",props:{locales:{type:Object,required:!1,default:()=>({})}},setup(e){const t=Rr(),n=j(()=>e.locales[t.value]??{openInNewWindow:"open in new window"});return()=>ve("span",[Qf,ve("span",{class:"external-link-icon-sr-only"},n.value.openInNewWindow)])}}),Xf={"/en/":{openInNewWindow:"open in new window"},"/zh-cn/":{openInNewWindow:"在新窗口中打开"},"/":{openInNewWindow:"open in new window"}},ed=Gt({enhance({app:e}){e.component("ExternalLinkIcon",ve(Zf,{locales:Xf}))}});/*! medium-zoom 1.0.8 | MIT License | https://github.com/francoischalifour/medium-zoom */var Ht=Object.assign||function(e){for(var t=1;t1&&arguments[1]!==void 0?arguments[1]:{},r=window.Promise||function(L){function O(){}L(O,O)},o=function(L){var O=L.target;if(O===B){y();return}P.indexOf(O)!==-1&&w({target:O})},s=function(){if(!(K||!m.original)){var L=window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop||0;Math.abs(X-L)>M.scrollOffset&&setTimeout(y,150)}},i=function(L){var O=L.key||L.keyCode;(O==="Escape"||O==="Esc"||O===27)&&y()},l=function(){var L=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},O=L;if(L.background&&(B.style.background=L.background),L.container&&L.container instanceof Object&&(O.container=Ht({},M.container,L.container)),L.template){var I=sr(L.template)?L.template:document.querySelector(L.template);O.template=I}return M=Ht({},M,O),P.forEach(function(ie){ie.dispatchEvent(Zt("medium-zoom:update",{detail:{zoom:D}}))}),D},a=function(){var L=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};return e(Ht({},M,L))},c=function(){for(var L=arguments.length,O=Array(L),I=0;I0?O.reduce(function(U,re){return[].concat(U,Qs(re))},[]):P;return ie.forEach(function(U){U.classList.remove("medium-zoom-image"),U.dispatchEvent(Zt("medium-zoom:detach",{detail:{zoom:D}}))}),P=P.filter(function(U){return ie.indexOf(U)===-1}),D},f=function(L,O){var I=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{};return P.forEach(function(ie){ie.addEventListener("medium-zoom:"+L,O,I)}),A.push({type:"medium-zoom:"+L,listener:O,options:I}),D},h=function(L,O){var I=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{};return P.forEach(function(ie){ie.removeEventListener("medium-zoom:"+L,O,I)}),A=A.filter(function(ie){return!(ie.type==="medium-zoom:"+L&&ie.listener.toString()===O.toString())}),D},g=function(){var L=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},O=L.target,I=function(){var U={width:document.documentElement.clientWidth,height:document.documentElement.clientHeight,left:0,top:0,right:0,bottom:0},re=void 0,oe=void 0;if(M.container)if(M.container instanceof Object)U=Ht({},U,M.container),re=U.width-U.left-U.right-M.margin*2,oe=U.height-U.top-U.bottom-M.margin*2;else{var He=sr(M.container)?M.container:document.querySelector(M.container),Ne=He.getBoundingClientRect(),Ue=Ne.width,ze=Ne.height,Et=Ne.left,wt=Ne.top;U=Ht({},U,{width:Ue,height:ze,left:Et,top:wt})}re=re||U.width-M.margin*2,oe=oe||U.height-M.margin*2;var lt=m.zoomedHd||m.original,Ie=Js(lt)?re:lt.naturalWidth||re,x=Js(lt)?oe:lt.naturalHeight||oe,F=lt.getBoundingClientRect(),$=F.top,W=F.left,ae=F.width,d=F.height,p=Math.min(Math.max(ae,Ie),re)/ae,_=Math.min(Math.max(d,x),oe)/d,E=Math.min(p,_),C=(-W+(re-ae)/2+M.margin+U.left)/E,k=(-$+(oe-d)/2+M.margin+U.top)/E,H="scale("+E+") translate3d("+C+"px, "+k+"px, 0)";m.zoomed.style.transform=H,m.zoomedHd&&(m.zoomedHd.style.transform=H)};return new r(function(ie){if(O&&P.indexOf(O)===-1){ie(D);return}var U=function Ue(){K=!1,m.zoomed.removeEventListener("transitionend",Ue),m.original.dispatchEvent(Zt("medium-zoom:opened",{detail:{zoom:D}})),ie(D)};if(m.zoomed){ie(D);return}if(O)m.original=O;else if(P.length>0){var re=P;m.original=re[0]}else{ie(D);return}if(m.original.dispatchEvent(Zt("medium-zoom:open",{detail:{zoom:D}})),X=window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop||0,K=!0,m.zoomed=rd(m.original),document.body.appendChild(B),M.template){var oe=sr(M.template)?M.template:document.querySelector(M.template);m.template=document.createElement("div"),m.template.appendChild(oe.content.cloneNode(!0)),document.body.appendChild(m.template)}if(m.original.parentElement&&m.original.parentElement.tagName==="PICTURE"&&m.original.currentSrc&&(m.zoomed.src=m.original.currentSrc),document.body.appendChild(m.zoomed),window.requestAnimationFrame(function(){document.body.classList.add("medium-zoom--opened")}),m.original.classList.add("medium-zoom-image--hidden"),m.zoomed.classList.add("medium-zoom-image--opened"),m.zoomed.addEventListener("click",y),m.zoomed.addEventListener("transitionend",U),m.original.getAttribute("data-zoom-src")){m.zoomedHd=m.zoomed.cloneNode(),m.zoomedHd.removeAttribute("srcset"),m.zoomedHd.removeAttribute("sizes"),m.zoomedHd.removeAttribute("loading"),m.zoomedHd.src=m.zoomed.getAttribute("data-zoom-src"),m.zoomedHd.onerror=function(){clearInterval(He),console.warn("Unable to reach the zoom image target "+m.zoomedHd.src),m.zoomedHd=null,I()};var He=setInterval(function(){m.zoomedHd.complete&&(clearInterval(He),m.zoomedHd.classList.add("medium-zoom-image--opened"),m.zoomedHd.addEventListener("click",y),document.body.appendChild(m.zoomedHd),I())},10)}else if(m.original.hasAttribute("srcset")){m.zoomedHd=m.zoomed.cloneNode(),m.zoomedHd.removeAttribute("sizes"),m.zoomedHd.removeAttribute("loading");var Ne=m.zoomedHd.addEventListener("load",function(){m.zoomedHd.removeEventListener("load",Ne),m.zoomedHd.classList.add("medium-zoom-image--opened"),m.zoomedHd.addEventListener("click",y),document.body.appendChild(m.zoomedHd),I()})}else I()})},y=function(){return new r(function(L){if(K||!m.original){L(D);return}var O=function I(){m.original.classList.remove("medium-zoom-image--hidden"),document.body.removeChild(m.zoomed),m.zoomedHd&&document.body.removeChild(m.zoomedHd),document.body.removeChild(B),m.zoomed.classList.remove("medium-zoom-image--opened"),m.template&&document.body.removeChild(m.template),K=!1,m.zoomed.removeEventListener("transitionend",I),m.original.dispatchEvent(Zt("medium-zoom:closed",{detail:{zoom:D}})),m.original=null,m.zoomed=null,m.zoomedHd=null,m.template=null,L(D)};K=!0,document.body.classList.remove("medium-zoom--opened"),m.zoomed.style.transform="",m.zoomedHd&&(m.zoomedHd.style.transform=""),m.template&&(m.template.style.transition="opacity 150ms",m.template.style.opacity=0),m.original.dispatchEvent(Zt("medium-zoom:close",{detail:{zoom:D}})),m.zoomed.addEventListener("transitionend",O)})},w=function(){var L=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},O=L.target;return m.original?y():g({target:O})},T=function(){return M},v=function(){return P},b=function(){return m.original},P=[],A=[],K=!1,X=0,M=n,m={original:null,zoomed:null,zoomedHd:null,template:null};Object.prototype.toString.call(t)==="[object Object]"?M=t:(t||typeof t=="string")&&c(t),M=Ht({margin:0,background:"#fff",scrollOffset:40,container:null,template:null},M);var B=nd(M.background);document.addEventListener("click",o),document.addEventListener("keyup",i),document.addEventListener("scroll",s),window.addEventListener("resize",y);var D={open:g,close:y,toggle:w,update:l,clone:a,attach:c,detach:u,on:f,off:h,getOptions:T,getImages:v,getZoomedImage:b};return D};function sd(e,t){t===void 0&&(t={});var n=t.insertAt;if(!(!e||typeof document>"u")){var r=document.head||document.getElementsByTagName("head")[0],o=document.createElement("style");o.type="text/css",n==="top"&&r.firstChild?r.insertBefore(o,r.firstChild):r.appendChild(o),o.styleSheet?o.styleSheet.cssText=e:o.appendChild(document.createTextNode(e))}}var id=".medium-zoom-overlay{position:fixed;top:0;right:0;bottom:0;left:0;opacity:0;transition:opacity .3s;will-change:opacity}.medium-zoom--opened .medium-zoom-overlay{cursor:pointer;cursor:zoom-out;opacity:1}.medium-zoom-image{cursor:pointer;cursor:zoom-in;transition:transform .3s cubic-bezier(.2,0,.2,1)!important}.medium-zoom-image--hidden{visibility:hidden}.medium-zoom-image--opened{position:relative;cursor:pointer;cursor:zoom-out;will-change:transform}";sd(id);const ld=od,ad=Symbol("mediumZoom");const cd=".theme-default-content > img, .theme-default-content :not(a) > img",ud={},fd=300,dd=Gt({enhance({app:e,router:t}){const n=ld(ud);n.refresh=(r=cd)=>{n.detach(),n.attach(r)},e.provide(ad,n),t.afterEach(()=>{setTimeout(()=>n.refresh(),fd)})}});/** + * NProgress, (c) 2013, 2014 Rico Sta. Cruz - http://ricostacruz.com/nprogress + * @license MIT + */const ce={settings:{minimum:.08,easing:"ease",speed:200,trickle:!0,trickleRate:.02,trickleSpeed:800,barSelector:'[role="bar"]',parent:"body",template:'
'},status:null,set:e=>{const t=ce.isStarted();e=qr(e,ce.settings.minimum,1),ce.status=e===1?null:e;const n=ce.render(!t),r=n.querySelector(ce.settings.barSelector),o=ce.settings.speed,s=ce.settings.easing;return n.offsetWidth,hd(i=>{er(r,{transform:"translate3d("+Zs(e)+"%,0,0)",transition:"all "+o+"ms "+s}),e===1?(er(n,{transition:"none",opacity:"1"}),n.offsetWidth,setTimeout(function(){er(n,{transition:"all "+o+"ms linear",opacity:"0"}),setTimeout(function(){ce.remove(),i()},o)},o)):setTimeout(()=>i(),o)}),ce},isStarted:()=>typeof ce.status=="number",start:()=>{ce.status||ce.set(0);const e=()=>{setTimeout(()=>{ce.status&&(ce.trickle(),e())},ce.settings.trickleSpeed)};return ce.settings.trickle&&e(),ce},done:e=>!e&&!ce.status?ce:ce.inc(.3+.5*Math.random()).set(1),inc:e=>{let t=ce.status;return t?(typeof e!="number"&&(e=(1-t)*qr(Math.random()*t,.1,.95)),t=qr(t+e,0,.994),ce.set(t)):ce.start()},trickle:()=>ce.inc(Math.random()*ce.settings.trickleRate),render:e=>{if(ce.isRendered())return document.getElementById("nprogress");Xs(document.documentElement,"nprogress-busy");const t=document.createElement("div");t.id="nprogress",t.innerHTML=ce.settings.template;const n=t.querySelector(ce.settings.barSelector),r=e?"-100":Zs(ce.status||0),o=document.querySelector(ce.settings.parent);return er(n,{transition:"all 0 linear",transform:"translate3d("+r+"%,0,0)"}),o!==document.body&&Xs(o,"nprogress-custom-parent"),o==null||o.appendChild(t),t},remove:()=>{ei(document.documentElement,"nprogress-busy"),ei(document.querySelector(ce.settings.parent),"nprogress-custom-parent");const e=document.getElementById("nprogress");e&&pd(e)},isRendered:()=>!!document.getElementById("nprogress")},qr=(e,t,n)=>en?n:e,Zs=e=>(-1+e)*100,hd=function(){const e=[];function t(){const n=e.shift();n&&n(t)}return function(n){e.push(n),e.length===1&&t()}}(),er=function(){const e=["Webkit","O","Moz","ms"],t={};function n(i){return i.replace(/^-ms-/,"ms-").replace(/-([\da-z])/gi,function(l,a){return a.toUpperCase()})}function r(i){const l=document.body.style;if(i in l)return i;let a=e.length;const c=i.charAt(0).toUpperCase()+i.slice(1);let u;for(;a--;)if(u=e[a]+c,u in l)return u;return i}function o(i){return i=n(i),t[i]??(t[i]=r(i))}function s(i,l,a){l=o(l),i.style[l]=a}return function(i,l){for(const a in l){const c=l[a];c!==void 0&&Object.prototype.hasOwnProperty.call(l,a)&&s(i,a,c)}}}(),Tl=(e,t)=>(typeof e=="string"?e:Bo(e)).indexOf(" "+t+" ")>=0,Xs=(e,t)=>{const n=Bo(e),r=n+t;Tl(n,t)||(e.className=r.substring(1))},ei=(e,t)=>{const n=Bo(e);if(!Tl(e,t))return;const r=n.replace(" "+t+" "," ");e.className=r.substring(1,r.length-1)},Bo=e=>(" "+(e.className||"")+" ").replace(/\s+/gi," "),pd=e=>{e&&e.parentNode&&e.parentNode.removeChild(e)};const md=()=>{Xe(()=>{const e=gn(),t=new Set;t.add(e.currentRoute.value.path),e.beforeEach(n=>{t.has(n.path)||ce.start()}),e.afterEach(n=>{t.add(n.path),ce.done()})})},gd=Gt({setup(){md()}}),vd=JSON.parse(`{"repo":"https://github.com/LuckyPray/DexKit","docsRepo":"https://github.com/LuckyPray/DexKit","docsBranch":"master","docsDir":"doc-source/src","editLinkPattern":":repo/edit/:branch/:path","sidebar":{"/en/":[{"text":"Get Started","collapsible":true,"children":["/en/guide/home","/en/guide/knowledge","/en/guide/quick-start","/en/guide/example","/en/guide/performance-optimization.md","/en/guide/structural-zoom-table","/en/guide/run-on-desktop"]},{"text":"About","collapsible":true,"children":["/en/about/contacts","/en/about/about"]}],"/zh-cn/":[{"text":"入门","collapsible":true,"children":["/zh-cn/guide/home","/zh-cn/guide/knowledge","/zh-cn/guide/quick-start","/zh-cn/guide/example","/zh-cn/guide/performance-optimization.md","/zh-cn/guide/structural-zoom-table","/zh-cn/guide/run-on-desktop"]},{"text":"关于","collapsible":true,"children":["/zh-cn/about/contacts","/zh-cn/about/about"]}]},"sidebarDepth":2,"locales":{"/en/":{"navbar":[{"text":"Navigation","children":[{"text":"Get Started","children":[{"text":"Introduce","link":"/en/guide/home"},{"text":"Basic Knowledge","link":"/en/guide/knowledge"},{"text":"Quick Start","link":"/en/guide/quick-start"},{"text":"Usage Example","link":"/en/guide/example"},{"text":"Performance optimization","link":"/en/guide/performance-optimization.md"},{"text":"Structural Zoom Table","link":"/en/guide/structural-zoom-table"},{"text":"Run on Desktop","link":"/en/guide/run-on-desktop"}]},{"text":"About","children":[{"text":"Contact Us","link":"/zh-cn/about/contacts"},{"text":"About this Document","link":"/zh-cn/about/about"}]}]}],"selectLanguageText":"English (US)","selectLanguageName":"English","editLinkText":"Edit this page on Github","tip":"Tips","warning":"Notice","danger":"Pay Attention"},"/zh-cn/":{"navbar":[{"text":"导航","children":[{"text":"入门","children":[{"text":"介绍","link":"/zh-cn/guide/home"},{"text":"基础知识","link":"/zh-cn/guide/knowledge"},{"text":"快速开始","link":"/zh-cn/guide/quick-start"},{"text":"用法示例","link":"/zh-cn/guide/example"},{"text":"性能优化","link":"/zh-cn/guide/performance-optimization.md"},{"text":"结构速查表","link":"/zh-cn/guide/structural-zoom-table"},{"text":"桌面平台运行","link":"/zh-cn/guide/run-on-desktop"}]},{"text":"关于","children":[{"text":"联系我们","link":"/zh-cn/about/contacts"},{"text":"关于此文档","link":"/zh-cn/about/about"}]}]}],"selectLanguageText":"简体中文 (CN)","selectLanguageName":"简体中文","editLinkText":"在 Github 上编辑此页","notFound":["这里什么都没有","我们怎么到这来了?","这是一个 404 页面","看起来我们进入了错误的链接"],"backToHome":"回到首页","contributorsText":"贡献者","lastUpdatedText":"上次更新","tip":"小提示","warning":"注意","danger":"特别注意","openInNewWindow":"在新窗口中打开","toggleColorMode":"切换颜色模式"},"/":{"selectLanguageName":"English"}},"colorMode":"auto","colorModeSwitch":true,"navbar":[],"logo":null,"selectLanguageText":"Languages","selectLanguageAriaLabel":"Select language","editLink":true,"editLinkText":"Edit this page","lastUpdated":true,"lastUpdatedText":"Last Updated","contributors":true,"contributorsText":"Contributors","notFound":["There's nothing here.","How did we get here?","That's a Four-Oh-Four.","Looks like we've got some broken links."],"backToHome":"Take me home","openInNewWindow":"open in new window","toggleColorMode":"toggle color mode","toggleSidebar":"toggle sidebar"}`),_d=xe(vd),kl=()=>_d,Sl=Symbol(""),bd=()=>{const e=ke(Sl);if(!e)throw new Error("useThemeLocaleData() is called without provider.");return e},yd=(e,t)=>{const{locales:n,...r}=e;return{...r,...n==null?void 0:n[t]}},Ed=Gt({enhance({app:e}){const t=kl(),n=e._context.provides[No],r=j(()=>yd(t.value,n.value));e.provide(Sl,r),Object.defineProperties(e.config.globalProperties,{$theme:{get(){return t.value}},$themeLocale:{get(){return r.value}}})}}),wd=he({__name:"Badge",props:{type:{type:String,required:!1,default:"tip"},text:{type:String,required:!1,default:""},vertical:{type:String,required:!1,default:void 0}},setup(e){return(t,n)=>(z(),Q("span",{class:Ke(["badge",e.type]),style:Hn({verticalAlign:e.vertical})},[be(t.$slots,"default",{},()=>[$t(Oe(e.text),1)])],6))}}),Ce=(e,t)=>{const n=e.__vccOpts||e;for(const[r,o]of t)n[r]=o;return n},xd=Ce(wd,[["__file","Badge.vue"]]),Cd=he({name:"CodeGroup",slots:Object,setup(e,{slots:t}){const n=xe(-1),r=xe([]),o=(l=n.value)=>{l{l>0?n.value=l-1:n.value=r.value.length-1,r.value[n.value].focus()},i=(l,a)=>{l.key===" "||l.key==="Enter"?(l.preventDefault(),n.value=a):l.key==="ArrowRight"?(l.preventDefault(),o(a)):l.key==="ArrowLeft"&&(l.preventDefault(),s(a))};return()=>{var a;const l=(((a=t.default)==null?void 0:a.call(t))||[]).filter(c=>c.type.name==="CodeGroupItem").map(c=>(c.props===null&&(c.props={}),c));return l.length===0?null:(n.value<0||n.value>l.length-1?(n.value=l.findIndex(c=>c.props.active===""||c.props.active===!0),n.value===-1&&(n.value=0)):l.forEach((c,u)=>{c.props.active=u===n.value}),ve("div",{class:"code-group"},[ve("div",{class:"code-group__nav"},ve("ul",{class:"code-group__ul"},l.map((c,u)=>{const f=u===n.value;return ve("li",{class:"code-group__li"},ve("button",{ref:h=>{h&&(r.value[u]=h)},class:{"code-group__nav-tab":!0,"code-group__nav-tab-active":f},ariaPressed:f,ariaExpanded:f,onClick:()=>n.value=u,onKeydown:h=>i(h,u)},c.props.title))}))),l]))}}}),Ld=["aria-selected"],Td=he({name:"CodeGroupItem"}),kd=he({...Td,props:{title:{type:String,required:!0},active:{type:Boolean,required:!1,default:!1}},setup(e){return(t,n)=>(z(),Q("div",{class:Ke(["code-group-item",{"code-group-item__active":e.active}]),"aria-selected":e.active},[be(t.$slots,"default")],10,Ld))}}),Sd=Ce(kd,[["__file","CodeGroupItem.vue"]]);function ti(e,t){var n;const r=To();return Ni(()=>{r.value=e()},{...t,flush:(n=t==null?void 0:t.flush)!=null?n:"sync"}),Fn(r)}function Ad(e,t){let n,r,o;const s=xe(!0),i=()=>{s.value=!0,o()};st(e,i,{flush:"sync"});const l=typeof t=="function"?t:t.get,a=typeof t=="function"?void 0:t.set,c=Ma((u,f)=>(r=u,o=f,{get(){return s.value&&(n=l(),s.value=!1),r(),n},set(h){a==null||a(h)}}));return Object.isExtensible(c)&&(c.trigger=i),c}function Al(e){return hi()?(sa(e),!0):!1}function hn(e){return typeof e=="function"?e():Z(e)}const Pd=typeof window<"u"&&typeof document<"u",Rd=Object.prototype.toString,Od=e=>Rd.call(e)==="[object Object]",Id=()=>{};function $d(e,t){function n(...r){return new Promise((o,s)=>{Promise.resolve(e(()=>t.apply(this,r),{fn:t,thisArg:this,args:r})).then(o).catch(s)})}return n}const Pl=e=>e();function Nd(e=Pl){const t=xe(!0);function n(){t.value=!1}function r(){t.value=!0}const o=(...s)=>{t.value&&e(...s)};return{isActive:Fn(t),pause:n,resume:r,eventFilter:o}}function Md(e,t,n={}){const{eventFilter:r=Pl,...o}=n;return st(e,$d(r,t),o)}function Dd(e,t,n={}){const{eventFilter:r,...o}=n,{eventFilter:s,pause:i,resume:l,isActive:a}=Nd(r);return{stop:Md(e,t,{...o,eventFilter:s}),pause:i,resume:l,isActive:a}}function Hd(e=!1,t={}){const{truthyValue:n=!0,falsyValue:r=!1}=t,o=$e(e),s=xe(e);function i(l){if(arguments.length)return s.value=l,s.value;{const a=hn(n);return s.value=s.value===a?hn(r):a,s.value}}return o?i:[s,i]}function zd(e){var t;const n=hn(e);return(t=n==null?void 0:n.$el)!=null?t:n}const _r=Pd?window:void 0;function ni(...e){let t,n,r,o;if(typeof e[0]=="string"||Array.isArray(e[0])?([n,r,o]=e,t=_r):[t,n,r,o]=e,!t)return Id;Array.isArray(n)||(n=[n]),Array.isArray(r)||(r=[r]);const s=[],i=()=>{s.forEach(u=>u()),s.length=0},l=(u,f,h,g)=>(u.addEventListener(f,h,g),()=>u.removeEventListener(f,h,g)),a=st(()=>[zd(t),hn(o)],([u,f])=>{if(i(),!u)return;const h=Od(f)?{...f}:f;s.push(...n.flatMap(g=>r.map(y=>l(u,g,y,h))))},{immediate:!0,flush:"post"}),c=()=>{a(),i()};return Al(c),c}function Fd(){const e=xe(!1);return Xi()&&Xe(()=>{e.value=!0}),e}function Bd(e){const t=Fd();return j(()=>(t.value,!!e()))}function jd(e,t={}){const{window:n=_r}=t,r=Bd(()=>n&&"matchMedia"in n&&typeof n.matchMedia=="function");let o;const s=xe(!1),i=c=>{s.value=c.matches},l=()=>{o&&("removeEventListener"in o?o.removeEventListener("change",i):o.removeListener(i))},a=Ni(()=>{r.value&&(l(),o=n.matchMedia(hn(e)),"addEventListener"in o?o.addEventListener("change",i):o.addListener(i),s.value=o.matches)});return Al(()=>{a(),l(),o=void 0}),s}const tr=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{},nr="__vueuse_ssr_handlers__",Ud=Vd();function Vd(){return nr in tr||(tr[nr]=tr[nr]||{}),tr[nr]}function Kd(e,t){return Ud[e]||t}function Wd(e){return e==null?"any":e instanceof Set?"set":e instanceof Map?"map":e instanceof Date?"date":typeof e=="boolean"?"boolean":typeof e=="string"?"string":typeof e=="object"?"object":Number.isNaN(e)?"any":"number"}const qd={boolean:{read:e=>e==="true",write:e=>String(e)},object:{read:e=>JSON.parse(e),write:e=>JSON.stringify(e)},number:{read:e=>Number.parseFloat(e),write:e=>String(e)},any:{read:e=>e,write:e=>String(e)},string:{read:e=>e,write:e=>String(e)},map:{read:e=>new Map(JSON.parse(e)),write:e=>JSON.stringify(Array.from(e.entries()))},set:{read:e=>new Set(JSON.parse(e)),write:e=>JSON.stringify(Array.from(e))},date:{read:e=>new Date(e),write:e=>e.toISOString()}},ri="vueuse-storage";function Gd(e,t,n,r={}){var o;const{flush:s="pre",deep:i=!0,listenToStorageChanges:l=!0,writeDefaults:a=!0,mergeDefaults:c=!1,shallow:u,window:f=_r,eventFilter:h,onError:g=m=>{console.error(m)}}=r,y=(u?To:xe)(t);if(!n)try{n=Kd("getDefaultStorage",()=>{var m;return(m=_r)==null?void 0:m.localStorage})()}catch(m){g(m)}if(!n)return y;const w=hn(t),T=Wd(w),v=(o=r.serializer)!=null?o:qd[T],{pause:b,resume:P}=Dd(y,()=>A(y.value),{flush:s,deep:i,eventFilter:h});return f&&l&&(ni(f,"storage",M),ni(f,ri,X)),M(),y;function A(m){try{if(m==null)n.removeItem(e);else{const B=v.write(m),D=n.getItem(e);D!==B&&(n.setItem(e,B),f&&f.dispatchEvent(new CustomEvent(ri,{detail:{key:e,oldValue:D,newValue:B,storageArea:n}})))}}catch(B){g(B)}}function K(m){const B=m?m.newValue:n.getItem(e);if(B==null)return a&&w!==null&&n.setItem(e,v.write(w)),w;if(!m&&c){const D=v.read(B);return typeof c=="function"?c(D,w):T==="object"&&!Array.isArray(D)?{...w,...D}:D}else return typeof B!="string"?B:v.read(B)}function X(m){M(m.detail)}function M(m){if(!(m&&m.storageArea!==n)){if(m&&m.key==null){y.value=w;return}if(!(m&&m.key!==e)){b();try{(m==null?void 0:m.newValue)!==v.write(y.value)&&(y.value=K(m))}catch(B){g(B)}finally{m?xr(P):P()}}}}}function Yd(e){return jd("(prefers-color-scheme: dark)",e)}const Jd=()=>kl(),je=()=>bd(),Rl=Symbol(""),jo=()=>{const e=ke(Rl);if(!e)throw new Error("useDarkMode() is called without provider.");return e},Qd=()=>{const e=je(),t=Yd(),n=Gd("vuepress-color-scheme",e.value.colorMode),r=j({get(){return e.value.colorModeSwitch?n.value==="auto"?t.value:n.value==="dark":e.value.colorMode==="dark"},set(o){o===t.value?n.value="auto":n.value=o?"dark":"light"}});Vt(Rl,r),Zd(r)},Zd=e=>{const t=(n=e.value)=>{const r=window==null?void 0:window.document.querySelector("html");r==null||r.classList.toggle("dark",n)};Xe(()=>{st(e,t,{immediate:!0})}),Sr(()=>t())},Ol=(...e)=>{const n=gn().resolve(...e),r=n.matched[n.matched.length-1];if(!(r!=null&&r.redirect))return n;const{redirect:o}=r,s=se(o)?o(n):o,i=pe(s)?{path:s}:s;return Ol({hash:n.hash,query:n.query,params:n.params,...i})},Uo=e=>{const t=Ol(encodeURI(e));return{text:t.meta.title||e,link:t.name==="404"?e:t.fullPath}};let Gr=null,En=null;const Xd={wait:()=>Gr,pending:()=>{Gr=new Promise(e=>En=e)},resolve:()=>{En==null||En(),Gr=null,En=null}},Il=()=>Xd,$l=Symbol("sidebarItems"),Vo=()=>{const e=ke($l);if(!e)throw new Error("useSidebarItems() is called without provider.");return e},eh=()=>{const e=je(),t=gt(),n=j(()=>th(t.value,e.value));Vt($l,n)},th=(e,t)=>{const n=e.sidebar??t.sidebar??"auto",r=e.sidebarDepth??t.sidebarDepth??2;return e.home||n===!1?[]:n==="auto"?rh(r):J(n)?Nl(n,r):$o(n)?oh(n,r):[]},nh=(e,t)=>({text:e.title,link:e.link,children:Ko(e.children,t)}),Ko=(e,t)=>t>0?e.map(n=>nh(n,t-1)):[],rh=e=>{const t=Wt();return[{text:t.value.title,children:Ko(t.value.headers,e)}]},Nl=(e,t)=>{const n=Yt(),r=Wt(),o=s=>{var l;let i;if(pe(s)?i=Uo(s):i=s,i.children)return{...i,children:i.children.map(a=>o(a))};if(i.link===n.path){const a=((l=r.value.headers[0])==null?void 0:l.level)===1?r.value.headers[0].children:r.value.headers;return{...i,children:Ko(a,t)}}return i};return e.map(s=>o(s))},oh=(e,t)=>{const n=Yt(),r=sl(e,n.path),o=e[r]??[];return Nl(o,t)},sh="719px",ih={mobile:sh};var Mn;(function(e){e.MOBILE="mobile"})(Mn||(Mn={}));var ii;const lh={[Mn.MOBILE]:Number.parseInt((ii=ih.mobile)==null?void 0:ii.replace("px",""),10)},Ml=(e,t)=>{const n=lh[e];Number.isInteger(n)&&Xe(()=>{t(n),window.addEventListener("resize",()=>t(n),!1),window.addEventListener("orientationchange",()=>t(n),!1)})},ah={},ch={class:"theme-default-content"};function uh(e,t){const n=bt("Content");return z(),Q("div",ch,[ee(n)])}const fh=Ce(ah,[["render",uh],["__file","HomeContent.vue"]]),dh={key:0,class:"features"},hh=he({__name:"HomeFeatures",setup(e){const t=gt(),n=j(()=>J(t.value.features)?t.value.features:[]);return(r,o)=>n.value.length?(z(),Q("div",dh,[(z(!0),Q(ye,null,Ot(n.value,s=>(z(),Q("div",{key:s.title,class:"feature"},[fe("h2",null,Oe(s.title),1),fe("p",null,Oe(s.details),1)]))),128))])):Te("v-if",!0)}}),ph=Ce(hh,[["__file","HomeFeatures.vue"]]),mh=["innerHTML"],gh=["textContent"],vh=he({__name:"HomeFooter",setup(e){const t=gt(),n=j(()=>t.value.footer),r=j(()=>t.value.footerHtml);return(o,s)=>n.value?(z(),Q(ye,{key:0},[Te(" eslint-disable-next-line vue/no-v-html "),r.value?(z(),Q("div",{key:0,class:"footer",innerHTML:n.value},null,8,mh)):(z(),Q("div",{key:1,class:"footer",textContent:Oe(n.value)},null,8,gh))],64)):Te("v-if",!0)}}),_h=Ce(vh,[["__file","HomeFooter.vue"]]),bh=["href","rel","target","aria-label"],yh=he({inheritAttrs:!1}),Eh=he({...yh,__name:"AutoLink",props:{item:{type:Object,required:!0}},setup(e){const t=e,n=Yt(),r=dl(),{item:o}=ko(t),s=j(()=>Vn(o.value.link)),i=j(()=>Tu(o.value.link)||ku(o.value.link)),l=j(()=>{if(!i.value){if(o.value.target)return o.value.target;if(s.value)return"_blank"}}),a=j(()=>l.value==="_blank"),c=j(()=>!s.value&&!i.value&&!a.value),u=j(()=>{if(!i.value){if(o.value.rel)return o.value.rel;if(a.value)return"noopener noreferrer"}}),f=j(()=>o.value.ariaLabel||o.value.text),h=j(()=>{const w=Object.keys(r.value.locales);return w.length?!w.some(T=>T===o.value.link):o.value.link!=="/"}),g=j(()=>h.value?n.path.startsWith(o.value.link):!1),y=j(()=>c.value?o.value.activeMatch?new RegExp(o.value.activeMatch).test(n.path):g.value:!1);return(w,T)=>{const v=bt("RouterLink"),b=bt("AutoLinkExternalIcon");return c.value?(z(),Ae(v,io({key:0,class:{"router-link-active":y.value},to:Z(o).link,"aria-label":f.value},w.$attrs),{default:Me(()=>[be(w.$slots,"before"),$t(" "+Oe(Z(o).text)+" ",1),be(w.$slots,"after")]),_:3},16,["class","to","aria-label"])):(z(),Q("a",io({key:1,class:"external-link",href:Z(o).link,rel:u.value,target:l.value,"aria-label":f.value},w.$attrs),[be(w.$slots,"before"),$t(" "+Oe(Z(o).text)+" ",1),a.value?(z(),Ae(b,{key:0})):Te("v-if",!0),be(w.$slots,"after")],16,bh))}}}),vt=Ce(Eh,[["__file","AutoLink.vue"]]),wh={class:"hero"},xh={key:0,id:"main-title"},Ch={key:1,class:"description"},Lh={key:2,class:"actions"},Th=he({__name:"HomeHero",setup(e){const t=gt(),n=Mo(),r=jo(),o=j(()=>r.value&&t.value.heroImageDark!==void 0?t.value.heroImageDark:t.value.heroImage),s=j(()=>t.value.heroAlt||l.value||"hero"),i=j(()=>t.value.heroHeight||280),l=j(()=>t.value.heroText===null?null:t.value.heroText||n.value.title||"Hello"),a=j(()=>t.value.tagline===null?null:t.value.tagline||n.value.description||"Welcome to your VuePress site"),c=j(()=>J(t.value.actions)?t.value.actions.map(({text:f,link:h,type:g="primary"})=>({text:f,link:h,type:g})):[]),u=()=>{if(!o.value)return null;const f=ve("img",{src:Ho(o.value),alt:s.value,height:i.value});return t.value.heroImageDark===void 0?f:ve(Do,()=>f)};return(f,h)=>(z(),Q("header",wh,[ee(u),l.value?(z(),Q("h1",xh,Oe(l.value),1)):Te("v-if",!0),a.value?(z(),Q("p",Ch,Oe(a.value),1)):Te("v-if",!0),c.value.length?(z(),Q("p",Lh,[(z(!0),Q(ye,null,Ot(c.value,g=>(z(),Ae(vt,{key:g.text,class:Ke(["action-button",[g.type]]),item:g},null,8,["class","item"]))),128))])):Te("v-if",!0)]))}}),kh=Ce(Th,[["__file","HomeHero.vue"]]),Sh={class:"home"},Ah=he({__name:"Home",setup(e){return(t,n)=>(z(),Q("main",Sh,[ee(kh),ee(ph),ee(fh),ee(_h)]))}}),Ph=Ce(Ah,[["__file","Home.vue"]]),Rh=he({__name:"NavbarBrand",setup(e){const t=Rr(),n=Mo(),r=je(),o=jo(),s=j(()=>r.value.home||t.value),i=j(()=>n.value.title),l=j(()=>o.value&&r.value.logoDark!==void 0?r.value.logoDark:r.value.logo),a=()=>{if(!l.value)return null;const c=ve("img",{class:"logo",src:Ho(l.value),alt:i.value});return r.value.logoDark===void 0?c:ve(Do,()=>c)};return(c,u)=>{const f=bt("RouterLink");return z(),Ae(f,{to:s.value},{default:Me(()=>[ee(a),i.value?(z(),Q("span",{key:0,class:Ke(["site-name",{"can-hide":l.value}])},Oe(i.value),3)):Te("v-if",!0)]),_:1},8,["to"])}}}),Oh=Ce(Rh,[["__file","NavbarBrand.vue"]]),Ih=he({__name:"DropdownTransition",setup(e){const t=r=>{r.style.height=r.scrollHeight+"px"},n=r=>{r.style.height=""};return(r,o)=>(z(),Ae(Un,{name:"dropdown",onEnter:t,onAfterEnter:n,onBeforeLeave:t},{default:Me(()=>[be(r.$slots,"default")]),_:3}))}}),Dl=Ce(Ih,[["__file","DropdownTransition.vue"]]),$h=["aria-label"],Nh={class:"title"},Mh=fe("span",{class:"arrow down"},null,-1),Dh=["aria-label"],Hh={class:"title"},zh={class:"navbar-dropdown"},Fh={class:"navbar-dropdown-subtitle"},Bh={key:1},jh={class:"navbar-dropdown-subitem-wrapper"},Uh=he({__name:"NavbarDropdown",props:{item:{type:Object,required:!0}},setup(e){const t=e,{item:n}=ko(t),r=j(()=>n.value.ariaLabel||n.value.text),o=xe(!1),s=Yt();st(()=>s.path,()=>{o.value=!1});const i=a=>{a.detail===0?o.value=!o.value:o.value=!1},l=(a,c)=>c[c.length-1]===a;return(a,c)=>(z(),Q("div",{class:Ke(["navbar-dropdown-wrapper",{open:o.value}])},[fe("button",{class:"navbar-dropdown-title",type:"button","aria-label":r.value,onClick:i},[fe("span",Nh,Oe(Z(n).text),1),Mh],8,$h),fe("button",{class:"navbar-dropdown-title-mobile",type:"button","aria-label":r.value,onClick:c[0]||(c[0]=u=>o.value=!o.value)},[fe("span",Hh,Oe(Z(n).text),1),fe("span",{class:Ke(["arrow",o.value?"down":"right"])},null,2)],8,Dh),ee(Dl,null,{default:Me(()=>[fr(fe("ul",zh,[(z(!0),Q(ye,null,Ot(Z(n).children,u=>(z(),Q("li",{key:u.text,class:"navbar-dropdown-item"},[u.children?(z(),Q(ye,{key:0},[fe("h4",Fh,[u.link?(z(),Ae(vt,{key:0,item:u,onFocusout:f=>l(u,Z(n).children)&&u.children.length===0&&(o.value=!1)},null,8,["item","onFocusout"])):(z(),Q("span",Bh,Oe(u.text),1))]),fe("ul",jh,[(z(!0),Q(ye,null,Ot(u.children,f=>(z(),Q("li",{key:f.link,class:"navbar-dropdown-subitem"},[ee(vt,{item:f,onFocusout:h=>l(f,u.children)&&l(u,Z(n).children)&&(o.value=!1)},null,8,["item","onFocusout"])]))),128))])],64)):(z(),Ae(vt,{key:1,item:u,onFocusout:f=>l(u,Z(n).children)&&(o.value=!1)},null,8,["item","onFocusout"]))]))),128))],512),[[gr,o.value]])]),_:1})],2))}}),Vh=Ce(Uh,[["__file","NavbarDropdown.vue"]]),oi=e=>decodeURI(e).replace(/#.*$/,"").replace(/(index)?\.(md|html)$/,""),Kh=(e,t)=>{if(t.hash===e)return!0;const n=oi(t.path),r=oi(e);return n===r},Hl=(e,t)=>e.link&&Kh(e.link,t)?!0:e.children?e.children.some(n=>Hl(n,t)):!1,zl=e=>!Vn(e)||/github\.com/.test(e)?"GitHub":/bitbucket\.org/.test(e)?"Bitbucket":/gitlab\.com/.test(e)?"GitLab":/gitee\.com/.test(e)?"Gitee":null,Wh={GitHub:":repo/edit/:branch/:path",GitLab:":repo/-/edit/:branch/:path",Gitee:":repo/edit/:branch/:path",Bitbucket:":repo/src/:branch/:path?mode=edit&spa=0&at=:branch&fileviewer=file-view-default"},qh=({docsRepo:e,editLinkPattern:t})=>{if(t)return t;const n=zl(e);return n!==null?Wh[n]:null},Gh=({docsRepo:e,docsBranch:t,docsDir:n,filePathRelative:r,editLinkPattern:o})=>{if(!r)return null;const s=qh({docsRepo:e,editLinkPattern:o});return s?s.replace(/:repo/,Vn(e)?e:`https://github.com/${e}`).replace(/:branch/,t).replace(/:path/,ol(`${rl(n)}/${r}`)):null},Yh={key:0,class:"navbar-items"},Jh=he({__name:"NavbarItems",setup(e){const t=()=>{const u=gn(),f=Rr(),h=dl(),g=Mo(),y=Jd(),w=je();return j(()=>{const T=Object.keys(h.value.locales);if(T.length<2)return[];const v=u.currentRoute.value.path,b=u.currentRoute.value.fullPath;return[{text:`${w.value.selectLanguageText}`,ariaLabel:`${w.value.selectLanguageAriaLabel??w.value.selectLanguageText}`,children:T.map(A=>{var D,q;const K=((D=h.value.locales)==null?void 0:D[A])??{},X=((q=y.value.locales)==null?void 0:q[A])??{},M=`${K.lang}`,m=X.selectLanguageName??M;let B;if(M===g.value.lang)B=b;else{const L=v.replace(f.value,A);u.getRoutes().some(O=>O.path===L)?B=b.replace(v,L):B=X.home??A}return{text:m,link:B}})}]})},n=()=>{const u=je(),f=j(()=>u.value.repo),h=j(()=>f.value?zl(f.value):null),g=j(()=>f.value&&!Vn(f.value)?`https://github.com/${f.value}`:f.value),y=j(()=>g.value?u.value.repoLabel?u.value.repoLabel:h.value===null?"Source":h.value:null);return j(()=>!g.value||!y.value?[]:[{text:y.value,link:g.value}])},r=u=>pe(u)?Uo(u):u.children?{...u,children:u.children.map(r)}:u,o=()=>{const u=je();return j(()=>(u.value.navbar||[]).map(r))},s=xe(!1),i=o(),l=t(),a=n(),c=j(()=>[...i.value,...l.value,...a.value]);return Ml(Mn.MOBILE,u=>{window.innerWidthc.value.length?(z(),Q("nav",Yh,[(z(!0),Q(ye,null,Ot(c.value,h=>(z(),Q("div",{key:h.text,class:"navbar-item"},[h.children?(z(),Ae(Vh,{key:0,item:h,class:Ke(s.value?"mobile":"")},null,8,["item","class"])):(z(),Ae(vt,{key:1,item:h},null,8,["item"]))]))),128))])):Te("v-if",!0)}}),Fl=Ce(Jh,[["__file","NavbarItems.vue"]]),Qh=["title"],Zh={class:"icon",focusable:"false",viewBox:"0 0 32 32"},Xh=Nc('',9),ep=[Xh],tp={class:"icon",focusable:"false",viewBox:"0 0 32 32"},np=fe("path",{d:"M13.502 5.414a15.075 15.075 0 0 0 11.594 18.194a11.113 11.113 0 0 1-7.975 3.39c-.138 0-.278.005-.418 0a11.094 11.094 0 0 1-3.2-21.584M14.98 3a1.002 1.002 0 0 0-.175.016a13.096 13.096 0 0 0 1.825 25.981c.164.006.328 0 .49 0a13.072 13.072 0 0 0 10.703-5.555a1.01 1.01 0 0 0-.783-1.565A13.08 13.08 0 0 1 15.89 4.38A1.015 1.015 0 0 0 14.98 3z",fill:"currentColor"},null,-1),rp=[np],op=he({__name:"ToggleColorModeButton",setup(e){const t=je(),n=jo(),r=()=>{n.value=!n.value};return(o,s)=>(z(),Q("button",{class:"toggle-color-mode-button",title:Z(t).toggleColorMode,onClick:r},[fr((z(),Q("svg",Zh,ep,512)),[[gr,!Z(n)]]),fr((z(),Q("svg",tp,rp,512)),[[gr,Z(n)]])],8,Qh))}}),sp=Ce(op,[["__file","ToggleColorModeButton.vue"]]),ip=["title"],lp=fe("div",{class:"icon","aria-hidden":"true"},[fe("span"),fe("span"),fe("span")],-1),ap=[lp],cp=he({__name:"ToggleSidebarButton",emits:["toggle"],setup(e){const t=je();return(n,r)=>(z(),Q("div",{class:"toggle-sidebar-button",title:Z(t).toggleSidebar,"aria-expanded":"false",role:"button",tabindex:"0",onClick:r[0]||(r[0]=o=>n.$emit("toggle"))},ap,8,ip))}}),up=Ce(cp,[["__file","ToggleSidebarButton.vue"]]),fp=he({__name:"Navbar",emits:["toggle-sidebar"],setup(e){const t=je(),n=xe(null),r=xe(null),o=xe(0),s=j(()=>o.value?{maxWidth:o.value+"px"}:{});Ml(Mn.MOBILE,l=>{var c;const a=i(n.value,"paddingLeft")+i(n.value,"paddingRight");window.innerWidth{const c=bt("NavbarSearch");return z(),Q("header",{ref_key:"navbar",ref:n,class:"navbar"},[ee(up,{onToggle:a[0]||(a[0]=u=>l.$emit("toggle-sidebar"))}),fe("span",{ref_key:"navbarBrand",ref:r},[ee(Oh)],512),fe("div",{class:"navbar-items-wrapper",style:Hn(s.value)},[be(l.$slots,"before"),ee(Fl,{class:"can-hide"}),be(l.$slots,"after"),Z(t).colorModeSwitch?(z(),Ae(sp,{key:0})):Te("v-if",!0),ee(c)],4)],512)}}}),dp=Ce(fp,[["__file","Navbar.vue"]]),hp={class:"page-meta"},pp={key:0,class:"meta-item edit-link"},mp={key:1,class:"meta-item last-updated"},gp={class:"meta-item-label"},vp={class:"meta-item-info"},_p={key:2,class:"meta-item contributors"},bp={class:"meta-item-label"},yp={class:"meta-item-info"},Ep=["title"],wp=he({__name:"PageMeta",setup(e){const t=()=>{const a=je(),c=Wt(),u=gt();return j(()=>{if(!(u.value.editLink??a.value.editLink??!0))return null;const{repo:h,docsRepo:g=h,docsBranch:y="main",docsDir:w="",editLinkText:T}=a.value;if(!g)return null;const v=Gh({docsRepo:g,docsBranch:y,docsDir:w,filePathRelative:c.value.filePathRelative,editLinkPattern:u.value.editLinkPattern??a.value.editLinkPattern});return v?{text:T??"Edit this page",link:v}:null})},n=()=>{const a=je(),c=Wt(),u=gt();return j(()=>{var g,y;return!(u.value.lastUpdated??a.value.lastUpdated??!0)||!((g=c.value.git)!=null&&g.updatedTime)?null:new Date((y=c.value.git)==null?void 0:y.updatedTime).toLocaleString()})},r=()=>{const a=je(),c=Wt(),u=gt();return j(()=>{var h;return u.value.contributors??a.value.contributors??!0?((h=c.value.git)==null?void 0:h.contributors)??null:null})},o=je(),s=t(),i=n(),l=r();return(a,c)=>{const u=bt("ClientOnly");return z(),Q("footer",hp,[Z(s)?(z(),Q("div",pp,[ee(vt,{class:"meta-item-label",item:Z(s)},null,8,["item"])])):Te("v-if",!0),Z(i)?(z(),Q("div",mp,[fe("span",gp,Oe(Z(o).lastUpdatedText)+": ",1),ee(u,null,{default:Me(()=>[fe("span",vp,Oe(Z(i)),1)]),_:1})])):Te("v-if",!0),Z(l)&&Z(l).length?(z(),Q("div",_p,[fe("span",bp,Oe(Z(o).contributorsText)+": ",1),fe("span",yp,[(z(!0),Q(ye,null,Ot(Z(l),(f,h)=>(z(),Q(ye,{key:h},[fe("span",{class:"contributor",title:`email: ${f.email}`},Oe(f.name),9,Ep),h!==Z(l).length-1?(z(),Q(ye,{key:0},[$t(", ")],64)):Te("v-if",!0)],64))),128))])])):Te("v-if",!0)])}}}),xp=Ce(wp,[["__file","PageMeta.vue"]]),Cp={key:0,class:"page-nav"},Lp={class:"inner"},Tp={key:0,class:"prev"},kp={key:1,class:"next"},Sp=he({__name:"PageNav",setup(e){const t=a=>a===!1?null:pe(a)?Uo(a):$o(a)?a:!1,n=(a,c,u)=>{const f=a.findIndex(h=>h.link===c);if(f!==-1){const h=a[f+u];return h!=null&&h.link?h:null}for(const h of a)if(h.children){const g=n(h.children,c,u);if(g)return g}return null},r=gt(),o=Vo(),s=Yt(),i=j(()=>{const a=t(r.value.prev);return a!==!1?a:n(o.value,s.path,-1)}),l=j(()=>{const a=t(r.value.next);return a!==!1?a:n(o.value,s.path,1)});return(a,c)=>i.value||l.value?(z(),Q("nav",Cp,[fe("p",Lp,[i.value?(z(),Q("span",Tp,[ee(vt,{item:i.value},null,8,["item"])])):Te("v-if",!0),l.value?(z(),Q("span",kp,[ee(vt,{item:l.value},null,8,["item"])])):Te("v-if",!0)])])):Te("v-if",!0)}}),Ap=Ce(Sp,[["__file","PageNav.vue"]]),Pp={class:"page"},Rp={class:"theme-default-content"},Op=he({__name:"Page",setup(e){return(t,n)=>{const r=bt("Content");return z(),Q("main",Pp,[be(t.$slots,"top"),fe("div",Rp,[be(t.$slots,"content-top"),ee(r),be(t.$slots,"content-bottom")]),ee(xp),ee(Ap),be(t.$slots,"bottom")])}}}),Ip=Ce(Op,[["__file","Page.vue"]]),$p=["onKeydown"],Np={class:"sidebar-item-children"},Mp=he({__name:"SidebarItem",props:{item:{type:Object,required:!0},depth:{type:Number,required:!1,default:0}},setup(e){const t=e,{item:n,depth:r}=ko(t),o=Yt(),s=gn(),i=j(()=>Hl(n.value,o)),l=j(()=>({"sidebar-item":!0,"sidebar-heading":r.value===0,active:i.value,collapsible:n.value.collapsible})),a=j(()=>n.value.collapsible?i.value:!0),[c,u]=Hd(a.value),f=g=>{n.value.collapsible&&(g.preventDefault(),u())},h=s.afterEach(g=>{xr(()=>{c.value=a.value})});return kr(()=>{h()}),(g,y)=>{var T;const w=bt("SidebarItem",!0);return z(),Q("li",null,[Z(n).link?(z(),Ae(vt,{key:0,class:Ke(l.value),item:Z(n)},null,8,["class","item"])):(z(),Q("p",{key:1,tabindex:"0",class:Ke(l.value),onClick:f,onKeydown:vu(f,["enter"])},[$t(Oe(Z(n).text)+" ",1),Z(n).collapsible?(z(),Q("span",{key:0,class:Ke(["arrow",Z(c)?"down":"right"])},null,2)):Te("v-if",!0)],42,$p)),(T=Z(n).children)!=null&&T.length?(z(),Ae(Dl,{key:2},{default:Me(()=>[fr(fe("ul",Np,[(z(!0),Q(ye,null,Ot(Z(n).children,v=>(z(),Ae(w,{key:`${Z(r)}${v.text}${v.link}`,item:v,depth:Z(r)+1},null,8,["item","depth"]))),128))],512),[[gr,Z(c)]])]),_:1})):Te("v-if",!0)])}}}),Dp=Ce(Mp,[["__file","SidebarItem.vue"]]),Hp={key:0,class:"sidebar-items"},zp=he({__name:"SidebarItems",setup(e){const t=Yt(),n=Vo();return Xe(()=>{st(()=>t.hash,r=>{const o=document.querySelector(".sidebar");if(!o)return;const s=document.querySelector(`.sidebar a.sidebar-item[href="${t.path}${r}"]`);if(!s)return;const{top:i,height:l}=o.getBoundingClientRect(),{top:a,height:c}=s.getBoundingClientRect();ai+l&&s.scrollIntoView(!1)})}),(r,o)=>Z(n).length?(z(),Q("ul",Hp,[(z(!0),Q(ye,null,Ot(Z(n),s=>(z(),Ae(Dp,{key:`${s.text}${s.link}`,item:s},null,8,["item"]))),128))])):Te("v-if",!0)}}),Fp=Ce(zp,[["__file","SidebarItems.vue"]]),Bp={class:"sidebar"},jp=he({__name:"Sidebar",setup(e){return(t,n)=>(z(),Q("aside",Bp,[ee(Fl),be(t.$slots,"top"),ee(Fp),be(t.$slots,"bottom")]))}}),Up=Ce(jp,[["__file","Sidebar.vue"]]),Vp=he({__name:"Layout",setup(e){const t=Wt(),n=gt(),r=je(),o=j(()=>n.value.navbar!==!1&&r.value.navbar!==!1),s=Vo(),i=xe(!1),l=T=>{i.value=typeof T=="boolean"?T:!i.value},a={x:0,y:0},c=T=>{a.x=T.changedTouches[0].clientX,a.y=T.changedTouches[0].clientY},u=T=>{const v=T.changedTouches[0].clientX-a.x,b=T.changedTouches[0].clientY-a.y;Math.abs(v)>Math.abs(b)&&Math.abs(v)>40&&(v>0&&a.x<=80?l(!0):l(!1))},f=j(()=>[{"no-navbar":!o.value,"no-sidebar":!s.value.length,"sidebar-open":i.value},n.value.pageClass]);let h;Xe(()=>{h=gn().afterEach(()=>{l(!1)})}),Sr(()=>{h()});const g=Il(),y=g.resolve,w=g.pending;return(T,v)=>(z(),Q("div",{class:Ke(["theme-container",f.value]),onTouchstart:c,onTouchend:u},[be(T.$slots,"navbar",{},()=>[o.value?(z(),Ae(dp,{key:0,onToggleSidebar:l},{before:Me(()=>[be(T.$slots,"navbar-before")]),after:Me(()=>[be(T.$slots,"navbar-after")]),_:3})):Te("v-if",!0)]),fe("div",{class:"sidebar-mask",onClick:v[0]||(v[0]=b=>l(!1))}),be(T.$slots,"sidebar",{},()=>[ee(Up,null,{top:Me(()=>[be(T.$slots,"sidebar-top")]),bottom:Me(()=>[be(T.$slots,"sidebar-bottom")]),_:3})]),be(T.$slots,"page",{},()=>[Z(n).home?(z(),Ae(Ph,{key:0})):(z(),Ae(Un,{key:1,name:"fade-slide-y",mode:"out-in",onBeforeEnter:Z(y),onBeforeLeave:Z(w)},{default:Me(()=>[(z(),Ae(Ip,{key:Z(t).path},{top:Me(()=>[be(T.$slots,"page-top")]),"content-top":Me(()=>[be(T.$slots,"page-content-top")]),"content-bottom":Me(()=>[be(T.$slots,"page-content-bottom")]),bottom:Me(()=>[be(T.$slots,"page-bottom")]),_:3}))]),_:3},8,["onBeforeEnter","onBeforeLeave"]))])],34))}}),Kp=Ce(Vp,[["__file","Layout.vue"]]),Wp={class:"theme-container"},qp={class:"page"},Gp={class:"theme-default-content"},Yp=fe("h1",null,"404",-1),Jp=he({__name:"NotFound",setup(e){const t=Rr(),n=je(),r=n.value.notFound??["Not Found"],o=()=>r[Math.floor(Math.random()*r.length)],s=n.value.home??t.value,i=n.value.backToHome??"Back to home";return(l,a)=>{const c=bt("RouterLink");return z(),Q("div",Wp,[fe("main",qp,[fe("div",Gp,[Yp,fe("blockquote",null,Oe(o()),1),ee(c,{to:Z(s)},{default:Me(()=>[$t(Oe(Z(i)),1)]),_:1},8,["to"])])])])}}}),Qp=Ce(Jp,[["__file","NotFound.vue"]]);const Zp=Gt({enhance({app:e,router:t}){e.component("Badge",xd),e.component("CodeGroup",Cd),e.component("CodeGroupItem",Sd),e.component("AutoLinkExternalIcon",()=>{const r=e.component("ExternalLinkIcon");return r?ve(r):null}),e.component("NavbarSearch",()=>{const r=e.component("Docsearch")||e.component("SearchBox");return r?ve(r):null});const n=t.options.scrollBehavior;t.options.scrollBehavior=async(...r)=>(await Il().wait(),n(...r))},setup(){Qd(),eh()},layouts:{Layout:Kp,NotFound:Qp}}),rr=[qf,Jf,ed,dd,gd,Ed,Zp],Xp=[["v-8daa1a0e","/",{title:""},["/index.md"]],["v-2d0a870d","/en/",{title:"Home"},["/en/index.md"]],["v-c0c85b84","/zh-cn/",{title:"首页"},["/zh-cn/index.md"]],["v-7b22efaf","/en/guide/example.html",{title:"Usage Examples"},[":md"]],["v-efb45d4c","/en/guide/home.html",{title:"Introduction"},[":md"]],["v-277b35ca","/en/guide/knowledge.html",{title:"Basic Knowledge"},[":md"]],["v-7de9012f","/en/guide/performance-optimization.html",{title:"Performance Optimization"},[":md"]],["v-72889797","/en/guide/quick-start.html",{title:"Quick Start"},[":md"]],["v-206e5229","/en/guide/run-on-desktop.html",{title:"Run on desktop platform"},[":md"]],["v-6fa10115","/en/guide/structural-zoom-table.html",{title:"Quick Reference Guide"},[":md"]],["v-7a15fe3b","/en/about/about.html",{title:"About This Documentation"},[":md"]],["v-193cf592","/en/about/contacts.html",{title:"Contact Us"},[":md"]],["v-41967128","/zh-cn/about/about.html",{title:"关于此文档"},[":md"]],["v-6cf86266","/zh-cn/about/contacts.html",{title:"联系我们"},[":md"]],["v-13b430a0","/zh-cn/guide/example.html",{title:"用法示例"},[":md"]],["v-6a609e09","/zh-cn/guide/home.html",{title:"介绍"},[":md"]],["v-b4f1a468","/zh-cn/guide/knowledge.html",{title:"基础知识"},[":md"]],["v-7f55a05e","/zh-cn/guide/performance-optimization.html",{title:"性能优化"},[":md"]],["v-24840ff0","/zh-cn/guide/quick-start.html",{title:"快速开始"},[":md"]],["v-08f05018","/zh-cn/guide/run-on-desktop.html",{title:"在桌面平台运行"},[":md"]],["v-aedd6f74","/zh-cn/guide/structural-zoom-table.html",{title:"结构速查表"},[":md"]],["v-3706649a","/404.html",{title:""},[]]];var si=he({name:"Vuepress",setup(){const e=Iu();return()=>ve(e.value)}}),em=()=>Xp.reduce((e,[t,n,r,o])=>(e.push({name:t,path:n,component:si,meta:r},{path:n.endsWith("/")?n+"index.html":n.substring(0,n.length-5),redirect:n},...o.map(s=>({path:s===":md"?n.substring(0,n.length-5)+".md":s,redirect:n}))),e),[{name:"404",path:"/:catchAll(.*)",component:si}]),tm=nf,nm=()=>{const e=Ff({history:tm(rl("/DexKit/")),routes:em(),scrollBehavior:(t,n,r)=>r||(t.hash?{el:t.hash}:{top:0})});return e.beforeResolve(async(t,n)=>{var r;(t.path!==n.path||n===pt)&&([t.meta._data]=await Promise.all([ht.resolvePageData(t.name),(r=il[t.name])==null?void 0:r.__asyncLoader()]))}),e},rm=e=>{e.component("ClientOnly",Do),e.component("Content",Hu)},om=(e,t,n)=>{const r=ti(()=>t.currentRoute.value.path),o=ti(()=>ht.resolveRouteLocale(en.value.locales,r.value)),s=Ad(r,()=>t.currentRoute.value.meta._data),i=j(()=>ht.resolveLayouts(n)),l=j(()=>ht.resolveSiteLocaleData(en.value,o.value)),a=j(()=>ht.resolvePageFrontmatter(s.value)),c=j(()=>ht.resolvePageHeadTitle(s.value,l.value)),u=j(()=>ht.resolvePageHead(c.value,a.value,l.value)),f=j(()=>ht.resolvePageLang(s.value,l.value)),h=j(()=>ht.resolvePageLayout(s.value,i.value));return e.provide(Su,i),e.provide(ll,s),e.provide(al,a),e.provide(Ru,c),e.provide(cl,u),e.provide(ul,f),e.provide(fl,h),e.provide(No,o),e.provide(hl,l),Object.defineProperties(e.config.globalProperties,{$frontmatter:{get:()=>a.value},$head:{get:()=>u.value},$headTitle:{get:()=>c.value},$lang:{get:()=>f.value},$page:{get:()=>s.value},$routeLocale:{get:()=>o.value},$site:{get:()=>en.value},$siteLocale:{get:()=>l.value},$withBase:{get:()=>Ho}}),{layouts:i,pageData:s,pageFrontmatter:a,pageHead:u,pageHeadTitle:c,pageLang:f,pageLayout:h,routeLocale:o,siteData:en,siteLocaleData:l}},sm=()=>{const e=Pu(),t=Ou(),n=xe([]),r=()=>{e.value.forEach(s=>{const i=im(s);i&&n.value.push(i)})},o=()=>{document.documentElement.lang=t.value,n.value.forEach(s=>{s.parentNode===document.head&&document.head.removeChild(s)}),n.value.splice(0,n.value.length),e.value.forEach(s=>{const i=lm(s);i!==null&&(document.head.appendChild(i),n.value.push(i))})};Vt(Nu,o),Xe(()=>{r(),o(),st(()=>e.value,o)})},im=([e,t,n=""])=>{const r=Object.entries(t).map(([l,a])=>pe(a)?`[${l}=${JSON.stringify(a)}]`:a===!0?`[${l}]`:"").join(""),o=`head > ${e}${r}`;return Array.from(document.querySelectorAll(o)).find(l=>l.innerText===n)||null},lm=([e,t,n])=>{if(!pe(e))return null;const r=document.createElement(e);return $o(t)&&Object.entries(t).forEach(([o,s])=>{pe(s)?r.setAttribute(o,s):s===!0&&r.setAttribute(o,"")}),pe(n)&&r.appendChild(document.createTextNode(n)),r},am=yu,cm=async()=>{var n;const e=am({name:"VuepressApp",setup(){var r;sm();for(const o of rr)(r=o.setup)==null||r.call(o);return()=>[ve(Ll),...rr.flatMap(({rootComponents:o=[]})=>o.map(s=>ve(s)))]}}),t=nm();rm(e),om(e,t,rr);for(const r of rr)await((n=r.enhance)==null?void 0:n.call(r,{app:e,router:t,siteData:en}));return e.use(t),{app:e,router:t}};cm().then(({app:e,router:t})=>{t.isReady().then(()=>{e.mount("#app")})});export{Ce as _,ee as a,Nc as b,Q as c,cm as createVueApp,fe as d,$t as e,z as o,bt as r,Me as w}; diff --git a/assets/back-to-top-8efcbe56.svg b/assets/back-to-top-8efcbe56.svg new file mode 100644 index 00000000..83236781 --- /dev/null +++ b/assets/back-to-top-8efcbe56.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/contacts.html-1fbcc5af.js b/assets/contacts.html-1fbcc5af.js new file mode 100644 index 00000000..dce8afb7 --- /dev/null +++ b/assets/contacts.html-1fbcc5af.js @@ -0,0 +1 @@ +const t=JSON.parse('{"key":"v-6cf86266","path":"/zh-cn/about/contacts.html","title":"联系我们","lang":"zh-CN","frontmatter":{},"headers":[],"git":{"updatedTime":1718095560000,"contributors":[{"name":"teble","email":"me@teble.me","commits":1}]},"filePathRelative":"zh-cn/about/contacts.md"}');export{t as data}; diff --git a/assets/contacts.html-335338da.js b/assets/contacts.html-335338da.js new file mode 100644 index 00000000..dccc30e6 --- /dev/null +++ b/assets/contacts.html-335338da.js @@ -0,0 +1 @@ +import{_ as a,r,o,c,d as e,e as t,a as s}from"./app-75e8845f.js";const _={},l=e("h1",{id:"联系我们",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#联系我们","aria-hidden":"true"},"#"),t(" 联系我们")],-1),d={href:"https://t.me/LuckyPray_DexKit",target:"_blank",rel:"noopener noreferrer"};function i(h,f){const n=r("ExternalLinkIcon");return o(),c("div",null,[l,e("p",null,[t("如果您有任何问题,可以通过以下方式联系我们: "),e("a",d,[t("点击加入 Telegram 群组"),s(n)])])])}const p=a(_,[["render",i],["__file","contacts.html.vue"]]);export{p as default}; diff --git a/assets/contacts.html-6120246a.js b/assets/contacts.html-6120246a.js new file mode 100644 index 00000000..3f9ffe31 --- /dev/null +++ b/assets/contacts.html-6120246a.js @@ -0,0 +1 @@ +const t=JSON.parse('{"key":"v-193cf592","path":"/en/about/contacts.html","title":"Contact Us","lang":"en-US","frontmatter":{},"headers":[],"git":{"updatedTime":1718095560000,"contributors":[{"name":"teble","email":"me@teble.me","commits":1}]},"filePathRelative":"en/about/contacts.md"}');export{t as data}; diff --git a/assets/contacts.html-ecff4547.js b/assets/contacts.html-ecff4547.js new file mode 100644 index 00000000..fe81794b --- /dev/null +++ b/assets/contacts.html-ecff4547.js @@ -0,0 +1 @@ +import{_ as n,r as a,o as r,c,d as e,e as t,a as s}from"./app-75e8845f.js";const l={},_=e("h1",{id:"contact-us",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#contact-us","aria-hidden":"true"},"#"),t(" Contact Us")],-1),i={href:"https://t.me/LuckyPray_DexKit",target:"_blank",rel:"noopener noreferrer"};function h(u,d){const o=a("ExternalLinkIcon");return r(),c("div",null,[_,e("p",null,[t("If you have any questions, feel free to reach out to us through the following means: "),e("a",i,[t("Click to join our Telegram group"),s(o)])])])}const m=n(l,[["render",h],["__file","contacts.html.vue"]]);export{m as default}; diff --git a/assets/example.html-6629fd57.js b/assets/example.html-6629fd57.js new file mode 100644 index 00000000..84912611 --- /dev/null +++ b/assets/example.html-6629fd57.js @@ -0,0 +1,214 @@ +import{_ as p,r as e,o,c,d as s,e as n,a as l,b as r}from"./app-75e8845f.js";const t={},i=s("h1",{id:"用法示例",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#用法示例","aria-hidden":"true"},"#"),n(" 用法示例")],-1),A=s("p",null,[n("在阅读本部分内容的过程中可能需要搭配"),s("a",{href:"./structural-zoom-table"},"结构速查表"),n("以获得更好的理解。")],-1),y=s("blockquote",null,[s("p",null,"您可以在下方获取 Demo 的源码以及部分测试用例")],-1),d={href:"https://github.com/LuckyPray/DexKit/tree/master/demo",target:"_blank",rel:"noopener noreferrer"},D={href:"https://github.com/LuckyPray/DexKit/blob/master/dexkit/src/test/java/org/luckypray/dexkit/UnitTest.kt",target:"_blank",rel:"noopener noreferrer"},B=r(`

Demo App

下面是一个简单的 Demo Activity。这个 PlayActivity 内部属性以及方法都是被混淆的,且每个版本都会发生变化。

四大组件默认不会混淆,我们假设这个 Activity 被混淆了。

package org.luckypray.dexkit.demo;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.h;
+import java.util.Random;
+import org.luckypray.dexkit.demo.annotations.Router;
+
+@Router(path = "/play")
+public class PlayActivity extends AppCompatActivity {
+    private static final String TAG = "PlayActivity";
+    private TextView a;
+    private Handler b;
+
+    public void d(View view) {
+        Handler handler;
+        int i;
+        Log.d("PlayActivity", "onClick: rollButton");
+        float nextFloat = new Random().nextFloat();
+        if (nextFloat < 0.01d) {
+            handler = this.b;
+            i = -1;
+        } else if (nextFloat < 0.987f) {
+            handler = this.b;
+            i = 0;
+        } else {
+            handler = this.b;
+            i = 114514;
+        }
+        handler.sendEmptyMessage(i);
+    }
+
+    public void e(boolean z) {
+        int i;
+        if (!z) {
+            i = RandomUtil.a();
+        } else {
+            i = 6;
+        }
+        String a = h.a("You rolled a ", i);
+        this.a.setText(a);
+        Log.d("PlayActivity", "rollDice: " + a);
+    }
+
+    protected void onCreate(Bundle bundle) {
+        super/*androidx.fragment.app.FragmentActivity*/.onCreate(bundle);
+        setContentView(0x7f0b001d);
+        Log.d("PlayActivity", "onCreate");
+        HandlerThread handlerThread = new HandlerThread("PlayActivity");
+        handlerThread.start();
+        this.b = new PlayActivity$1(this, handlerThread.getLooper());
+        this.a = (TextView) findViewById(0x7f080134);
+        ((Button) findViewById(0x7f08013a)).setOnClickListener(new a(this));
+    }
+}
+

多条件匹配类多条件用法示例

此时我们想得到这个 PlayActivity 可以使用如下代码:

这仅仅是个样例,实际使用中并不需要这么复杂且全面的条件,按需使用即可。

private fun findPlayActivity(bridge: DexKitBridge) {
+    val classData = bridge.findClass {
+        // 指定搜索的包名范围
+        searchPackages("org.luckypray.dexkit.demo")
+        // 排除指定的包名范围
+        excludePackages("org.luckypray.dexkit.demo.annotations")
+        // ClassMatcher 针对类的匹配器
+        matcher {
+            // FieldsMatcher 针对类中包含字段的匹配器
+            fields {
+                // 添加对于字段的匹配器
+                add {
+                    // 指定字段的修饰符
+                    modifiers = Modifier.PRIVATE or Modifier.STATIC or Modifier.FINAL
+                    // 指定字段的类型
+                    type = "java.lang.String"
+                    // 指定字段的名称
+                    name = "TAG"
+                }
+                // 添加指定字段的类型的字段匹配器
+                addForType("android.widget.TextView")
+                addForType("android.os.Handler")
+                // 指定类中字段的数量
+                count = 3
+            }
+            // MethodsMatcher 针对类中包含方法的匹配器
+            methods {
+                // 添加对于方法的匹配器
+                add {
+                    // 指定方法的修饰符
+                    modifiers = Modifier.PROTECTED
+                    // 指定方法的名称
+                    name = "onCreate"
+                    // 指定方法的返回值类型
+                    returnType = "void"
+                    // 指定方法的参数类型,如果参数类型不确定,使用 null,使用此方法会隐式声明参数个数
+                    paramTypes("android.os.Bundle")
+                    // 指定方法中使用的字符串
+                    usingStrings("onCreate")
+                }
+                add {
+                    paramTypes("android.view.View")
+                    // 指定方法中使用的数字,类型为 Byte, Short, Int, Long, Float, Double 之一
+                    usingNumbers(0.01, -1, 0.987, 0, 114514)
+                }
+                add {
+                    paramTypes("boolean")
+                    // 指定方法中调用的方法列表
+                    invokeMethods {
+                        add {
+                            modifiers = Modifier.PUBLIC or Modifier.STATIC
+                            returnType = "int"
+                            // 指定方法中调用的方法中使用的字符串,所有字符串均使用 Equals 匹配
+                            usingStrings(listOf("getRandomDice: "), StringMatchType.Equals)
+                        }
+                        // 只需要包含上述方法的调用即可
+                        matchType = MatchType.Contains
+                    }
+                }
+                // 指定类中方法的数量,最少不少于1个,最多不超过10个
+                count(1..10)
+            }
+            // AnnotationsMatcher 针对类中包含注解的匹配器
+            annotations {
+                // 添加对于注解的匹配器
+                add {
+                    // 指定注解的类型
+                    type = "org.luckypray.dexkit.demo.annotations.Router"
+                    // 该注解需要包含指定的 element
+                    addElement {
+                        // 指定 element 的名称
+                        name = "path"
+                        // 指定 element 的值
+                        stringValue("/play")
+                    }
+                }
+            }
+            // 类中所有方法使用的字符串
+            usingStrings("PlayActivity", "onClick", "onCreate")
+        }
+    }.single()
+    println(classData.name)
+}
+

获得的结果如下:

org.luckypray.dexkit.demo.PlayActivity
+

父类条件嵌套

如果存在这么一个类,它唯一的特征是祖辈没被混淆,中间的父类都被混淆了,我们也可以使用 DexKit 来找到它。

private fun findActivity(bridge: DexKitBridge) {
+    bridge.findClass {
+        matcher { // ClassMatcher
+            // androidx.appcompat.app.AppCompatActivity
+            superClass { // ClassMatcher
+                // androidx.fragment.app.FragmentActivity
+                superClass { // ClassMatcher
+                    // androidx.activity.ComponentActivity
+                    superClass { // ClassMatcher
+                        // androidx.core.app.ComponentActivity
+                        superClass { // ClassMatcher
+                            superClass = "android.app.Activity"
+                        }
+                    }
+                }
+            }
+        }
+    }.forEach {
+        // org.luckypray.dexkit.demo.MainActivity
+        // org.luckypray.dexkit.demo.PlayActivity
+        println(it.name)
+    }
+}
+

获得的结果如下:

org.luckypray.dexkit.demo.MainActivity
+org.luckypray.dexkit.demo.PlayActivity
+

小提示

DexKit 中,一切符合逻辑的关系都可以作为查询条件

模糊参数匹配

如果我们需要寻找的方法中存在一个被混淆的参数,我们可以使用 null 来替代,这样它就能匹配任意类型的参数。

private fun findMethodWithFuzzyParam(bridge: DexKitBridge) {
+    bridge.findMethod {
+        matcher {
+            modifiers = Modifier.PUBLIC or Modifier.STATIC
+            returnType = "void"
+            // 指定方法的参数类型,如果参数类型不确定,使用 null
+            paramTypes("android.view.View", null)
+            // paramCount = 2 // paramTypes 长度为 2 已经隐式确定了参数个数
+            usingStrings("onClick")
+        }
+    }.single().let {
+        println(it)
+    }
+}
+

查询结果保存与读取

使用 DexKit 查询到的结果如何序列化保存下来,以便下次使用呢?

DexKit 中对 Class、Method、Field 提供了相应的包装类,分别是 DexClassDexMethodDexField。 包装类继承了 ISerializable 接口,可以使用它将包装类与字符串自由转换。对于查询返回的对象,可以直接使用 toDexClass()toDexMethod()toDexField() 方法来转换为包装类。

private fun saveData(bridge: DexKitBridge) {
+    bridge.findMethod {
+        matcher {
+            modifiers = Modifier.PUBLIC or Modifier.STATIC
+            returnType = "void"
+            paramTypes("android.view.View", null)
+            usingStrings("onClick")
+        }
+    }.single().let {
+        val dexMethod = it.toDexMethod()
+        val serialize = dexMethod.serialize()
+        val sp = getSharedPreferences("dexkit", Context.MODE_PRIVATE)
+        sp.edit().putString("onClickMethod", serialize).apply()
+    }
+}
+
+private fun readData(): Method {
+    val sp = getSharedPreferences("dexkit", Context.MODE_PRIVATE)
+    val descriptor = sp.getString("onClickMethod", null)
+    if (descriptor != null) {
+        val dexMethod = DexMethod(descriptor)
+        // val dexMethod = DexMethod.deserialize(serialize)
+        // val dexMethod = ISerializable.deserialize(serialize) as DexMethod
+        // val dexMethod = ISerializable.deserializeAs<DexMethod>(serialize)
+        val method = dexMethod.getMethodInstance(hostClassLoader)
+        return method
+    }
+    error("No saved")
+}
+
`,23);function C(v,u){const a=e("ExternalLinkIcon");return o(),c("div",null,[i,A,y,s("ul",null,[s("li",null,[n("APP Demo "),s("a",d,[n("点击查看"),l(a)])]),s("li",null,[n("APP 测试用例 "),s("a",D,[n("点击查看"),l(a)])])]),B])}const m=p(t,[["render",C],["__file","example.html.vue"]]);export{m as default}; diff --git a/assets/example.html-76d3f343.js b/assets/example.html-76d3f343.js new file mode 100644 index 00000000..e7a01995 --- /dev/null +++ b/assets/example.html-76d3f343.js @@ -0,0 +1,214 @@ +import{_ as e,r as p,o,c,d as s,e as n,a as l,b as t}from"./app-75e8845f.js";const r={},i=s("h1",{id:"usage-examples",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#usage-examples","aria-hidden":"true"},"#"),n(" Usage Examples")],-1),A=s("p",null,[n("During the reading of this section, you may need to refer to the "),s("a",{href:"./structural-zoom-table"},"Structural Quick Reference Table"),n(" for a better understanding.")],-1),y=s("blockquote",null,[s("p",null,"You can get the source code and some test cases for the demo below.")],-1),d={href:"https://github.com/LuckyPray/DexKit/tree/master/demo",target:"_blank",rel:"noopener noreferrer"},D={href:"https://github.com/LuckyPray/DexKit/blob/master/dexkit/src/test/java/org/luckypray/dexkit/UnitTest.kt",target:"_blank",rel:"noopener noreferrer"},B=t(`

Demo App

Here is a simple Demo Activity, PlayActivity, where the internal properties and methods are obfuscated, and they change in each version.

The four major components are not obfuscated by default. We assume this Activity is obfuscated.

package org.luckypray.dexkit.demo;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.h;
+import java.util.Random;
+import org.luckypray.dexkit.demo.annotations.Router;
+
+@Router(path = "/play")
+public class PlayActivity extends AppCompatActivity {
+    private static final String TAG = "PlayActivity";
+    private TextView a;
+    private Handler b;
+
+    public void d(View view) {
+        Handler handler;
+        int i;
+        Log.d("PlayActivity", "onClick: rollButton");
+        float nextFloat = new Random().nextFloat();
+        if (nextFloat < 0.01d) {
+            handler = this.b;
+            i = -1;
+        } else if (nextFloat < 0.987f) {
+            handler = this.b;
+            i = 0;
+        } else {
+            handler = this.b;
+            i = 114514;
+        }
+        handler.sendEmptyMessage(i);
+    }
+
+    public void e(boolean z) {
+        int i;
+        if (!z) {
+            i = RandomUtil.a();
+        } else {
+            i = 6;
+        }
+        String a = h.a("You rolled a ", i);
+        this.a.setText(a);
+        Log.d("PlayActivity", "rollDice: " + a);
+    }
+
+    protected void onCreate(Bundle bundle) {
+        super/*androidx.fragment.app.FragmentActivity*/.onCreate(bundle);
+        setContentView(0x7f0b001d);
+        Log.d("PlayActivity", "onCreate");
+        HandlerThread handlerThread = new HandlerThread("PlayActivity");
+        handlerThread.start();
+        this.b = new PlayActivity$1(this, handlerThread.getLooper());
+        this.a = (TextView) findViewById(0x7f080134);
+        ((Button) findViewById(0x7f08013a)).setOnClickListener(new a(this));
+    }
+}
+

Multiple Conditions Matching Class - Example Usage

In this scenario, we want to find the PlayActivity using the following code:

This is just an example. In actual use, you don't need conditions as complex and comprehensive as this. Use as needed.

private fun findPlayActivity(bridge: DexKitBridge) {
+    val classData = bridge.findClass {
+        // Search within the specified package name range
+        searchPackages("org.luckypray.dexkit.demo")
+        // Exclude the specified package name range
+        excludePackages("org.luckypray.dexkit.demo.annotations")
+        // ClassMatcher Matcher for classes
+        matcher {
+            // FieldsMatcher Matcher for fields in a class
+            fields {
+                // Add a matcher for the field
+                add {
+                    // Specify the modifiers of the field
+                    modifiers = Modifier.PRIVATE or Modifier.STATIC or Modifier.FINAL
+                    // Specify the type of the field
+                    type = "java.lang.String"
+                    // Specify the name of the field
+                    name = "TAG"
+                }
+                // Add a matcher for the field of the specified type
+                addForType("android.widget.TextView")
+                addForType("android.os.Handler")
+                // Specify the number of fields in the class
+                count = 3
+            }
+            // MethodsMatcher Matcher for methods in a class
+            methods {
+                // Add a matcher for the method
+                add {
+                    // Specify the modifiers of the method
+                    modifiers = Modifier.PROTECTED
+                    // Specify the name of the method
+                    name = "onCreate"
+                    // Specify the return type of the method
+                    returnType = "void"
+                    // Specify the parameter types of the method, if the parameter types are uncertain,
+                    // use null, and this method will implicitly declare the number of parameters
+                    paramTypes("android.os.Bundle")
+                    // Specify the strings used in the method
+                    usingStrings("onCreate")
+                }
+                add {
+                    paramTypes("android.view.View")
+                    // Specify the numbers used in the method, the type is Byte, Short, Int, Long, Float, Double
+                    usingNumbers(0.01, -1, 0.987, 0, 114514)
+                }
+                add {
+                    paramTypes("boolean")
+                    // Specify the methods called in the method list
+                    invokeMethods {
+                        add {
+                            modifiers = Modifier.PUBLIC or Modifier.STATIC
+                            returnType = "int"
+                            // Specify the strings used in the method called in the method,
+                            usingStrings(listOf("getRandomDice: "), StringMatchType.Equals)
+                        }
+                        // Only need to contain the call to the above method
+                        matchType = MatchType.Contains
+                    }
+                }
+                count(1..10)
+            }
+            // AnnotationsMatcher Matcher for annotations in a class
+            annotations {
+                // Add a matcher for the annotation
+                add {
+                    // Specify the type of the annotation
+                    type = "org.luckypray.dexkit.demo.annotations.Router"
+                    // The annotation needs to contain the specified element
+                    addElement {
+                        // Specify the name of the element
+                        name = "path"
+                        // Specify the value of the element
+                        stringValue("/play")
+                    }
+                }
+            }
+            // Strings used by all methods in the class
+            usingStrings("PlayActivity", "onClick", "onCreate")
+        }
+    }.single()
+    println(classData.name)
+}
+

The result is as follows:

org.luckypray.dexkit.demo.PlayActivity
+

Parent Class Condition Nesting

if there is such a class, its only feature is that the ancestors are not obfuscated, and the middle parents are all obfuscated, we can also use DexKit to find it.

private fun findActivity(bridge: DexKitBridge) {
+    bridge.findClass {
+        matcher { // ClassMatcher
+            // androidx.appcompat.app.AppCompatActivity
+            superClass { // ClassMatcher
+                // androidx.fragment.app.FragmentActivity
+                superClass { // ClassMatcher
+                    // androidx.activity.ComponentActivity
+                    superClass { // ClassMatcher
+                        // androidx.core.app.ComponentActivity
+                        superClass { // ClassMatcher
+                            superClass = "android.app.Activity"
+                        }
+                    }
+                }
+            }
+        }
+    }.forEach {
+        // org.luckypray.dexkit.demo.MainActivity
+        // org.luckypray.dexkit.demo.PlayActivity
+        println(it.name)
+    }
+}
+

The result is as follows:

org.luckypray.dexkit.demo.MainActivity
+org.luckypray.dexkit.demo.PlayActivity
+

Tips

In DexKit, any logical relationship can be used as a query condition

Fuzzy Parameter Matching

If we need to find a method with an obfuscated parameter, we can use null to replace it, so that it can match any type of parameter.

private fun findMethodWithFuzzyParam(bridge: DexKitBridge) {
+    bridge.findMethod {
+        matcher {
+            modifiers = Modifier.PUBLIC or Modifier.STATIC
+            returnType = "void"
+            // Specify the parameters of the method, if the parameters are uncertain, use null
+            paramTypes("android.view.View", null)
+            // paramCount = 2 // paramTypes length is 2, which has implicitly determined the number of parameters
+            usingStrings("onClick")
+        }
+    }.single().let {
+        println(it)
+    }
+}
+

Saving and Retrieving Query Results

How can you serialize and save the results obtained from DexKit queries for later use?

DexKit provides corresponding wrapper classes for Class, Method, and Field, namely DexClass, DexMethod, and DexField. These wrapper classes inherit from the ISerializable interface, allowing them to be freely converted to and from strings. For objects returned by queries, you can directly use the toDexClass(), toDexMethod(), and toDexField() methods to convert them into the respective wrapper classes.

private fun saveData(bridge: DexKitBridge) {
+    bridge.findMethod {
+        matcher {
+            modifiers = Modifier.PUBLIC or Modifier.STATIC
+            returnType = "void"
+            paramTypes("android.view.View", null)
+            usingStrings("onClick")
+        }
+    }.single().let {
+        val dexMethod = it.toDexMethod()
+        val serialize = dexMethod.serialize()
+        val sp = getSharedPreferences("dexkit", Context.MODE_PRIVATE)
+        sp.edit().putString("onClickMethod", serialize).apply()
+    }
+}
+
+private fun readData(): Method {
+    val sp = getSharedPreferences("dexkit", Context.MODE_PRIVATE)
+    val descriptor = sp.getString("onClickMethod", null)
+    if (descriptor != null) {
+        val dexMethod = DexMethod(descriptor)
+        // val dexMethod = DexMethod.deserialize(serialize)
+        // val dexMethod = ISerializable.deserialize(serialize) as DexMethod
+        // val dexMethod = ISerializable.deserializeAs<DexMethod>(serialize)
+        val method = dexMethod.getMethodInstance(hostClassLoader)
+        return method
+    }
+    error("No saved")
+}
+
`,23);function C(v,u){const a=p("ExternalLinkIcon");return o(),c("div",null,[i,A,y,s("ul",null,[s("li",null,[n("APP Demo "),s("a",d,[n("Click to View"),l(a)])]),s("li",null,[n("APP Test Cases "),s("a",D,[n("Click to View"),l(a)])])]),B])}const F=e(r,[["render",C],["__file","example.html.vue"]]);export{F as default}; diff --git a/assets/example.html-d65bcfc3.js b/assets/example.html-d65bcfc3.js new file mode 100644 index 00000000..167aee70 --- /dev/null +++ b/assets/example.html-d65bcfc3.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-13b430a0","path":"/zh-cn/guide/example.html","title":"用法示例","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"Demo App","slug":"demo-app","link":"#demo-app","children":[]},{"level":2,"title":"多条件匹配类多条件用法示例","slug":"多条件匹配类多条件用法示例","link":"#多条件匹配类多条件用法示例","children":[]},{"level":2,"title":"父类条件嵌套","slug":"父类条件嵌套","link":"#父类条件嵌套","children":[]},{"level":2,"title":"模糊参数匹配","slug":"模糊参数匹配","link":"#模糊参数匹配","children":[]},{"level":2,"title":"查询结果保存与读取","slug":"查询结果保存与读取","link":"#查询结果保存与读取","children":[]}],"git":{"updatedTime":1718095560000,"contributors":[{"name":"teble","email":"me@teble.me","commits":1}]},"filePathRelative":"zh-cn/guide/example.md"}');export{e as data}; diff --git a/assets/example.html-de2cdc67.js b/assets/example.html-de2cdc67.js new file mode 100644 index 00000000..ecf8e563 --- /dev/null +++ b/assets/example.html-de2cdc67.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-7b22efaf","path":"/en/guide/example.html","title":"Usage Examples","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Demo App","slug":"demo-app","link":"#demo-app","children":[]},{"level":2,"title":"Multiple Conditions Matching Class - Example Usage","slug":"multiple-conditions-matching-class-example-usage","link":"#multiple-conditions-matching-class-example-usage","children":[]},{"level":2,"title":"Parent Class Condition Nesting","slug":"parent-class-condition-nesting","link":"#parent-class-condition-nesting","children":[]},{"level":2,"title":"Fuzzy Parameter Matching","slug":"fuzzy-parameter-matching","link":"#fuzzy-parameter-matching","children":[]},{"level":2,"title":"Saving and Retrieving Query Results","slug":"saving-and-retrieving-query-results","link":"#saving-and-retrieving-query-results","children":[]}],"git":{"updatedTime":1718095560000,"contributors":[{"name":"teble","email":"me@teble.me","commits":1}]},"filePathRelative":"en/guide/example.md"}');export{e as data}; diff --git a/assets/home.html-4377e437.js b/assets/home.html-4377e437.js new file mode 100644 index 00000000..cedd99d6 --- /dev/null +++ b/assets/home.html-4377e437.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-efb45d4c","path":"/en/guide/home.html","title":"Introduction","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Development Background","slug":"development-background","link":"#development-background","children":[]},{"level":2,"title":"Language Requirement","slug":"language-requirement","link":"#language-requirement","children":[]}],"git":{"updatedTime":1718095560000,"contributors":[{"name":"teble","email":"me@teble.me","commits":1}]},"filePathRelative":"en/guide/home.md"}');export{e as data}; diff --git a/assets/home.html-44ee30de.js b/assets/home.html-44ee30de.js new file mode 100644 index 00000000..ef414ebb --- /dev/null +++ b/assets/home.html-44ee30de.js @@ -0,0 +1 @@ +import{_ as t,r as a,o as n,c as i,d as r,e,a as s,w as d,b as c}from"./app-75e8845f.js";const l={},h=c('

Introduction

DexKit is a high-performance runtime parsing library for dex implemented in C++, used to search for obfuscated classes, methods, or properties.

Development Background

In the development of Xposed modules, we often need to hook specific methods. However, due to code obfuscation, module developers often have to maintain multiple versions of hook points to ensure compatibility of the module across different versions. This adaptation approach is cumbersome and error-prone.

Is there a better solution? Some developers may consider traversing all classes in the ClassLoader and traversing the characteristics of the classes through reflection, such as method names, parameter types, return value types, and annotations, and then adapting based on these features. However, this approach also has obvious drawbacks. First, due to the time-consuming nature of Java's reflection mechanism itself, the search speed is affected by device performance. Secondly, in complex conditions, the search may take a long time, and in extreme conditions, it may even exceed 30 seconds. In addition, forcibly loading some classes may cause unpredictable problems in the host APP.

Typically, developers decompile the host APP to obtain smali or decompiled Java code and search the code based on known features. The results are then written into the adaptation file. To simplify this process, we need an automated way. Currently, most solutions for parsing Dex files rely on dexlib2, but due to its development in Java, there are performance bottlenecks. Especially when the host application has a large number of dex files, the parsing time is long, affecting the user experience. Therefore, DexKit came into being. It is implemented in C++, providing superior performance, internal optimizations using multi-threading and various algorithms, enabling complex searches to be completed in a very short time.

Language Requirement

It is recommended to use Kotlin for development because it provides DSL support, allowing us to have a better experience when using DexKit. If you are not familiar with Kotlin, you don't need to worry either; the API also provides corresponding chain call support, which allows Java developers to have a good experience.

',8);function p(m,u){const o=a("RouterLink");return n(),i("div",null,[h,r("p",null,[e("All example code in the documentation will be written in Kotlin. You can easily understand the corresponding Java usage through the examples "),s(o,{to:"/zh-cn/"},{default:d(()=>[e("here")]),_:1}),e(".")])])}const g=t(l,[["render",p],["__file","home.html.vue"]]);export{g as default}; diff --git a/assets/home.html-6d2f5099.js b/assets/home.html-6d2f5099.js new file mode 100644 index 00000000..8ea132b7 --- /dev/null +++ b/assets/home.html-6d2f5099.js @@ -0,0 +1 @@ +import{_ as o,r as t,o as d,c,d as r,e,a as n,w as i,b as s}from"./app-75e8845f.js";const h={},l=s('

介绍

DexKit 是一个使用 C++ 实现的 dex 高性能运行时解析库,用于查找被混淆的类、方法或者属性。

开发背景

在 Xposed 模块的开发中,我们通常需要对特定的方法进行 hook。然而,由于代码混淆,模块开发者往往不得不维护多个版本的 hook 点,以确保模块在不同版本下的兼容性。这种适配方式繁琐且容易出错。

有没有更好的解决方案呢?有些开发者可能会考虑遍历 ClassLoader 中的所有类,并通过反射遍历类的特征,如方法名、参数类型、 返回值类型和注解等,然后根据这些特征进行适配。然而,这种方式也有明显的缺点。首先,由于 Java 的反射机制本身耗时较长, 查找速度受设备性能影响。其次,在复杂条件下,查找可能需要较长时间,极端条件下甚至可能超过 30 秒。另外, 强行加载部分类可能会导致宿主 APP 出现不可预知的问题。

通常,开发者会对宿主 APP 进行反编译,获取 smali 或反编译后的 Java 代码,并通过已知的特征对代码进行搜索。 然后将结果写入适配文件。为了简化这个过程,我们需要一种自动化的方式。目前针对 Dex 文件的解析方案大多依赖于 dexlib2,但由于其是用 Java 开发,性能存在瓶颈。特别是当宿主应用存在大量 dex 文件时,解析时间会很长, 影响用户体验。为此,DexKit 应运而生。它采用 C++ 实现,性能优越的同时且内部使用多线程和多种算法进行优化, 能够在极短时间内完成复杂的搜索。

语言要求

推荐使用 Kotlin 进行开发,因为其提供了 DSL 支持,可以让我们在使用 DexKit 时获得更好的体验。如果您不熟悉 Kotlin,也无需担心,API 也提供了相应的链式调用支持,同样能让 Java 开发者获得良好的体验。

',8);function p(_,x){const a=t("RouterLink");return d(),c("div",null,[l,r("p",null,[e("文档中的所有示例代码都将使用 Kotlin 编写。您可以通过 "),n(a,{to:"/zh-cn/"},{default:i(()=>[e("这里")]),_:1}),e(" 的示例,轻松了解相应的 Java 写法。")])])}const f=o(h,[["render",p],["__file","home.html.vue"]]);export{f as default}; diff --git a/assets/home.html-eff1e1a9.js b/assets/home.html-eff1e1a9.js new file mode 100644 index 00000000..de98ae8e --- /dev/null +++ b/assets/home.html-eff1e1a9.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-6a609e09","path":"/zh-cn/guide/home.html","title":"介绍","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"开发背景","slug":"开发背景","link":"#开发背景","children":[]},{"level":2,"title":"语言要求","slug":"语言要求","link":"#语言要求","children":[]}],"git":{"updatedTime":1718095560000,"contributors":[{"name":"teble","email":"me@teble.me","commits":1}]},"filePathRelative":"zh-cn/guide/home.md"}');export{e as data}; diff --git a/assets/index.html-1cabf326.js b/assets/index.html-1cabf326.js new file mode 100644 index 00000000..24611dcb --- /dev/null +++ b/assets/index.html-1cabf326.js @@ -0,0 +1,83 @@ +import{_ as t,r as o,o as r,c as p,a as e,w as n,b as i,d as s,e as l}from"./app-75e8845f.js";const A={},y=i(`

Ultimate Experience, Say No to Tediousness

Example Code

Assume this is obfuscated code from a host app. We need to dynamically adapt the hook for this method. Due to obfuscation, method names and class names may change with each version.

public class abc {
+
+    public boolean cvc() {
+        boolean b = false;
+        // ...
+        Log.d("VipCheckUtil", "userInfo: xxxx");
+        // ...
+        return b;
+    }
+}
+

DexKit can easily solve this problem!

Xposed hook code

By creating an instance of DexKitBridge, we can perform specific searches on the app's dex file.

warning

Only one instance of DexKitBridge needs to be created. If you do not wish to use try-with-resources or Kotlin's .use for automatic closing of the DexKitBridge instance, you need to manage its lifecycle manually. Ensure to call .close() when it's no longer needed to prevent memory leaks.

`,8),d=s("div",{class:"language-kotlin line-numbers-mode","data-ext":"kt"},[s("pre",{class:"shiki github-dark-dimmed",style:{"background-color":"#22272e"},tabindex:"0"},[s("code",null,[s("span",{class:"line"},[s("span",{style:{color:"#F47067"}},"class"),s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#F69D50"}},"AppHooker"),s("span",{style:{color:"#ADBAC7"}}," {")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#F47067"}},"companion"),s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#DCBDFB"}},"object"),s("span",{style:{color:"#ADBAC7"}}," {")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#DCBDFB"}},"init"),s("span",{style:{color:"#ADBAC7"}}," { System."),s("span",{style:{color:"#DCBDFB"}},"loadLibrary"),s("span",{style:{color:"#ADBAC7"}},"("),s("span",{style:{color:"#96D0FF"}},'"dexkit"'),s("span",{style:{color:"#ADBAC7"}},") }")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," }")]),l(` +`),s("span",{class:"line"}),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#F47067"}},"private"),s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#F47067"}},"var"),s("span",{style:{color:"#ADBAC7"}}," hostClassLoader: "),s("span",{style:{color:"#F69D50"}},"ClassLoader")]),l(` +`),s("span",{class:"line"}),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#F47067"}},"public"),s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#DCBDFB"}},"constructor"),s("span",{style:{color:"#ADBAC7"}},"(loadPackageParam: "),s("span",{style:{color:"#F69D50"}},"LoadPackageParam"),s("span",{style:{color:"#ADBAC7"}},") {")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#6CB6FF"}},"this"),s("span",{style:{color:"#ADBAC7"}},".hostClassLoader "),s("span",{style:{color:"#F47067"}},"="),s("span",{style:{color:"#ADBAC7"}}," loadPackageParam.classLoader")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#F47067"}},"val"),s("span",{style:{color:"#ADBAC7"}}," apkPath "),s("span",{style:{color:"#F47067"}},"="),s("span",{style:{color:"#ADBAC7"}}," loadPackageParam.appInfo.sourceDir")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#768390"}},"// DexKit creation is a time-consuming operation, please do not create the object repeatedly. ")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#768390"}},"// If you need to use it globally, please manage the life cycle yourself and ensure ")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#768390"}},"// that the .close() method is called when not needed to prevent memory leaks.")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#768390"}},"// Here we use `Closable.use` to automatically close the DexKitBridge instance.")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," DexKitBridge."),s("span",{style:{color:"#DCBDFB"}},"create"),s("span",{style:{color:"#ADBAC7"}},"(apkPath)."),s("span",{style:{color:"#DCBDFB"}},"use"),s("span",{style:{color:"#ADBAC7"}}," { bridge "),s("span",{style:{color:"#F47067"}},"->")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#DCBDFB"}},"isVipHook"),s("span",{style:{color:"#ADBAC7"}},"(bridge)")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#768390"}},"// Other hook ...")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," }")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," }")]),l(` +`),s("span",{class:"line"}),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#F47067"}},"private"),s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#F47067"}},"fun"),s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#DCBDFB"}},"isVipHook"),s("span",{style:{color:"#ADBAC7"}},"(bridge: "),s("span",{style:{color:"#F69D50"}},"DexKitBridge"),s("span",{style:{color:"#ADBAC7"}},") {")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#F47067"}},"val"),s("span",{style:{color:"#ADBAC7"}}," methodData "),s("span",{style:{color:"#F47067"}},"="),s("span",{style:{color:"#ADBAC7"}}," bridge."),s("span",{style:{color:"#DCBDFB"}},"findMethod"),s("span",{style:{color:"#ADBAC7"}}," {")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#DCBDFB"}},"matcher"),s("span",{style:{color:"#ADBAC7"}}," {")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#768390"}},"// All conditions are optional, you can freely combine them")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," modifiers "),s("span",{style:{color:"#F47067"}},"="),s("span",{style:{color:"#ADBAC7"}}," Modifier.PUBLIC")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," returnType "),s("span",{style:{color:"#F47067"}},"="),s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#96D0FF"}},'"boolean"')]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," paramCount "),s("span",{style:{color:"#F47067"}},"="),s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#6CB6FF"}},"0")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#DCBDFB"}},"usingStrings"),s("span",{style:{color:"#ADBAC7"}},"("),s("span",{style:{color:"#96D0FF"}},'"VipCheckUtil"'),s("span",{style:{color:"#ADBAC7"}},", "),s("span",{style:{color:"#96D0FF"}},'"userInfo:"'),s("span",{style:{color:"#ADBAC7"}},")")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," }")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," }."),s("span",{style:{color:"#DCBDFB"}},"single"),s("span",{style:{color:"#ADBAC7"}},"()")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#F47067"}},"val"),s("span",{style:{color:"#ADBAC7"}}," method: "),s("span",{style:{color:"#F69D50"}},"Method"),s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#F47067"}},"="),s("span",{style:{color:"#ADBAC7"}}," methodData."),s("span",{style:{color:"#DCBDFB"}},"getMethodInstance"),s("span",{style:{color:"#ADBAC7"}},"(hostClassLoader)")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," XposedBridge."),s("span",{style:{color:"#DCBDFB"}},"hookMethod"),s("span",{style:{color:"#ADBAC7"}},"(method, XC_MethodReplacement."),s("span",{style:{color:"#DCBDFB"}},"returnConstant"),s("span",{style:{color:"#ADBAC7"}},"("),s("span",{style:{color:"#6CB6FF"}},"true"),s("span",{style:{color:"#ADBAC7"}},"))")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," }")]),l(` +`),s("span",{class:"line"}),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#768390"}},"// ...")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}},"}")]),l(` +`),s("span",{class:"line"})])]),s("div",{class:"line-numbers","aria-hidden":"true"},[s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"})])],-1),D=s("div",{class:"language-java line-numbers-mode","data-ext":"java"},[s("pre",{class:"shiki github-dark-dimmed",style:{"background-color":"#22272e"},tabindex:"0"},[s("code",null,[s("span",{class:"line"},[s("span",{style:{color:"#F47067"}},"class"),s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#F69D50"}},"AppHooker"),s("span",{style:{color:"#ADBAC7"}}," {")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#F47067"}},"static"),s("span",{style:{color:"#ADBAC7"}}," {")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," System."),s("span",{style:{color:"#DCBDFB"}},"loadLibrary"),s("span",{style:{color:"#ADBAC7"}},"("),s("span",{style:{color:"#96D0FF"}},'"dexkit"'),s("span",{style:{color:"#ADBAC7"}},");")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," }")]),l(` +`),s("span",{class:"line"}),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#F47067"}},"private"),s("span",{style:{color:"#ADBAC7"}}," ClassLoader"),s("span",{style:{color:"#F69D50"}}," "),s("span",{style:{color:"#ADBAC7"}},"hostClassLoader;")]),l(` +`),s("span",{class:"line"}),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#F47067"}},"public"),s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#DCBDFB"}},"AppHooker"),s("span",{style:{color:"#ADBAC7"}},"(LoadPackageParam "),s("span",{style:{color:"#F69D50"}},"loadPackageParam"),s("span",{style:{color:"#ADBAC7"}},") {")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#6CB6FF"}},"this"),s("span",{style:{color:"#ADBAC7"}},".hostClassLoader "),s("span",{style:{color:"#F47067"}},"="),s("span",{style:{color:"#ADBAC7"}}," loadPackageParam.classLoader;")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," String"),s("span",{style:{color:"#F69D50"}}," "),s("span",{style:{color:"#ADBAC7"}},"apkPath"),s("span",{style:{color:"#F69D50"}}," "),s("span",{style:{color:"#F47067"}},"="),s("span",{style:{color:"#ADBAC7"}}," loadPackageParam.appInfo.sourceDir;")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#768390"}},"// DexKit creation is a time-consuming operation, please do not create the object repeatedly. ")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#768390"}},"// If you need to use it globally, please manage the life cycle yourself and ensure ")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#768390"}},"// that the .close() method is called when not needed to prevent memory leaks.")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#768390"}},"// Here we use `try-with-resources` to automatically close the DexKitBridge instance.")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#F47067"}},"try"),s("span",{style:{color:"#ADBAC7"}}," (DexKitBridge"),s("span",{style:{color:"#F69D50"}}," "),s("span",{style:{color:"#ADBAC7"}},"bridge"),s("span",{style:{color:"#F69D50"}}," "),s("span",{style:{color:"#F47067"}},"="),s("span",{style:{color:"#ADBAC7"}}," DexKitBridge."),s("span",{style:{color:"#DCBDFB"}},"create"),s("span",{style:{color:"#ADBAC7"}},"(apkPath)) {")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#DCBDFB"}},"isVipHook"),s("span",{style:{color:"#ADBAC7"}},"(bridge);")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#768390"}},"// Other hook ...")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," }")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," }")]),l(` +`),s("span",{class:"line"}),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#F47067"}},"private"),s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#F47067"}},"void"),s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#DCBDFB"}},"isVipHook"),s("span",{style:{color:"#ADBAC7"}},"(DexKitBridge "),s("span",{style:{color:"#F69D50"}},"bridge"),s("span",{style:{color:"#ADBAC7"}},") {")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," MethodData"),s("span",{style:{color:"#F69D50"}}," "),s("span",{style:{color:"#ADBAC7"}},"methodData"),s("span",{style:{color:"#F69D50"}}," "),s("span",{style:{color:"#F47067"}},"="),s("span",{style:{color:"#ADBAC7"}}," bridge."),s("span",{style:{color:"#DCBDFB"}},"findMethod"),s("span",{style:{color:"#ADBAC7"}},"(FindMethod."),s("span",{style:{color:"#DCBDFB"}},"create"),s("span",{style:{color:"#ADBAC7"}},"()")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," ."),s("span",{style:{color:"#DCBDFB"}},"matcher"),s("span",{style:{color:"#ADBAC7"}},"(MethodMatcher."),s("span",{style:{color:"#DCBDFB"}},"create"),s("span",{style:{color:"#ADBAC7"}},"()")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#768390"}},"// All conditions are optional, you can freely combine them")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," ."),s("span",{style:{color:"#DCBDFB"}},"modifiers"),s("span",{style:{color:"#ADBAC7"}},"(Modifier.PUBLIC)")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," ."),s("span",{style:{color:"#DCBDFB"}},"paramCount"),s("span",{style:{color:"#ADBAC7"}},"("),s("span",{style:{color:"#6CB6FF"}},"0"),s("span",{style:{color:"#ADBAC7"}},")")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," ."),s("span",{style:{color:"#DCBDFB"}},"returnType"),s("span",{style:{color:"#ADBAC7"}},"("),s("span",{style:{color:"#96D0FF"}},'"boolean"'),s("span",{style:{color:"#ADBAC7"}},")")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," ."),s("span",{style:{color:"#DCBDFB"}},"usingStrings"),s("span",{style:{color:"#ADBAC7"}},"("),s("span",{style:{color:"#96D0FF"}},'"VipCheckUtil"'),s("span",{style:{color:"#ADBAC7"}},", "),s("span",{style:{color:"#96D0FF"}},'"userInfo:"'),s("span",{style:{color:"#ADBAC7"}},")")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," )")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," )."),s("span",{style:{color:"#DCBDFB"}},"single"),s("span",{style:{color:"#ADBAC7"}},"();")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," Method"),s("span",{style:{color:"#F69D50"}}," "),s("span",{style:{color:"#ADBAC7"}},"method"),s("span",{style:{color:"#F69D50"}}," "),s("span",{style:{color:"#F47067"}},"="),s("span",{style:{color:"#ADBAC7"}}," methodData."),s("span",{style:{color:"#DCBDFB"}},"getMethodInstance"),s("span",{style:{color:"#ADBAC7"}},"(hostClassLoader);")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," XposedBridge."),s("span",{style:{color:"#DCBDFB"}},"hookMethod"),s("span",{style:{color:"#ADBAC7"}},"(method, XC_MethodReplacement."),s("span",{style:{color:"#DCBDFB"}},"returnConstant"),s("span",{style:{color:"#ADBAC7"}},"("),s("span",{style:{color:"#6CB6FF"}},"true"),s("span",{style:{color:"#ADBAC7"}},"));")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," }")]),l(` +`),s("span",{class:"line"}),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#768390"}},"// ...")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}},"}")]),l(` +`),s("span",{class:"line"})])]),s("div",{class:"line-numbers","aria-hidden":"true"},[s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"})])],-1),B=s("p",null,"It's that simple!",-1);function C(u,m){const a=o("CodeGroupItem"),c=o("CodeGroup");return r(),p("div",null,[y,e(c,null,{default:n(()=>[e(a,{title:"kotlin"},{default:n(()=>[d]),_:1}),e(a,{title:"java"},{default:n(()=>[D]),_:1})]),_:1}),B])}const F=t(A,[["render",C],["__file","index.html.vue"]]);export{F as default}; diff --git a/assets/index.html-2a017d73.js b/assets/index.html-2a017d73.js new file mode 100644 index 00000000..2a6738f7 --- /dev/null +++ b/assets/index.html-2a017d73.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-c0c85b84","path":"/zh-cn/","title":"首页","lang":"zh-CN","frontmatter":{"home":true,"title":"首页","actions":[{"text":"快速上手","link":"/zh-cn/guide/home","type":"primary"},{"text":"API KDoc","link":"https://luckypray.org/DexKit-Doc","type":"secondary"}],"features":[{"title":"优雅简洁","details":"完全使用 Kotlin DSL 打造的人性化 API,可以支持嵌套复杂查询,同时对 Java 也提供了良好的支持。"},{"title":"性能卓越","details":"底层使用 C++ 实现,性能卓越,同时在多线程的基础上使用多种算法对进行优化,能够在极短时间内完成复杂的搜索。"},{"title":"可跨平台","details":"提供多平台支持,例如在 Windows、Linux 或者 MacOS 中测试后,代码可以直接迁移至 Android 平台。"}],"footer":"LGPL-3.0 License | Copyright © 2022 LuckyPray"},"headers":[{"level":3,"title":"极致体验,拒绝繁琐","slug":"极致体验-拒绝繁琐","link":"#极致体验-拒绝繁琐","children":[]}],"git":{"updatedTime":1718095560000,"contributors":[{"name":"teble","email":"me@teble.me","commits":1}]},"filePathRelative":"zh-cn/index.md"}');export{e as data}; diff --git a/assets/index.html-3b5329ec.js b/assets/index.html-3b5329ec.js new file mode 100644 index 00000000..816bf10d --- /dev/null +++ b/assets/index.html-3b5329ec.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-8daa1a0e","path":"/","title":"","lang":"en-US","frontmatter":{"home":true,"navbar":false,"sidebar":false,"title":null,"heroAlt":null,"heroText":null,"tagline":"Select a language","actions":[{"text":"English","link":"/en/","type":"secondary"},{"text":"简体中文","link":"/zh-cn/","type":"secondary"}],"footer":"LGPL-3.0 License | Copyright © 2022 LuckyPray"},"headers":[],"git":{"updatedTime":1718095560000,"contributors":[{"name":"teble","email":"me@teble.me","commits":1}]},"filePathRelative":"index.md"}');export{e as data}; diff --git a/assets/index.html-4c5e4c4a.js b/assets/index.html-4c5e4c4a.js new file mode 100644 index 00000000..e6d4e16c --- /dev/null +++ b/assets/index.html-4c5e4c4a.js @@ -0,0 +1 @@ +import{_ as e,o as c,c as t}from"./app-75e8845f.js";const n={};function _(o,r){return c(),t("div")}const a=e(n,[["render",_],["__file","index.html.vue"]]);export{a as default}; diff --git a/assets/index.html-749b8c09.js b/assets/index.html-749b8c09.js new file mode 100644 index 00000000..82352aef --- /dev/null +++ b/assets/index.html-749b8c09.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-2d0a870d","path":"/en/","title":"Home","lang":"en-US","frontmatter":{"home":true,"title":"Home","actions":[{"text":"QuickStart","link":"/en/guide/home","type":"primary"},{"text":"API KDoc","link":"https://luckypray.org/DexKit-Doc","type":"secondary"}],"features":[{"title":"Elegant and Simple","details":"DexKit is built with a user-friendly API entirely using Kotlin DSL, supporting nested complex queries and providing good support for Java as well."},{"title":"High-Performance","details":"Implemented using C++ at its core, DexKit delivers superior performance. It utilizes multiple algorithms on top of multithreading, allowing it to complete complex searches in an extremely short time."},{"title":"Cross-platform","details":"It offers multi-platform support. After testing on Windows, Linux, or MacOS, the code can be directly migrated to the Android platform."}],"footer":"LGPL-3.0 License | Copyright © 2022 LuckyPray"},"headers":[{"level":3,"title":"Ultimate Experience, Say No to Tediousness","slug":"ultimate-experience-say-no-to-tediousness","link":"#ultimate-experience-say-no-to-tediousness","children":[]}],"git":{"updatedTime":1718095560000,"contributors":[{"name":"teble","email":"me@teble.me","commits":1}]},"filePathRelative":"en/index.md"}');export{e as data}; diff --git a/assets/index.html-d4986fc6.js b/assets/index.html-d4986fc6.js new file mode 100644 index 00000000..7f8399b4 --- /dev/null +++ b/assets/index.html-d4986fc6.js @@ -0,0 +1,81 @@ +import{_ as r,r as o,o as p,c as t,a as n,w as e,b as i,d as s,e as l}from"./app-75e8845f.js";const A={},y=i(`

极致体验,拒绝繁琐

样例 APP 代码

假设这是一个宿主 APP 的被混淆后的代码,我们需要对这个方法的 hook 进行动态适配,由于混淆的存在,可能每个版本方法名以及类名都会发生变化。

public class abc {
+    
+    public boolean cvc() {
+        boolean b = false;
+        // ...
+        Log.d("VipCheckUtil", "userInfo: xxxx");
+        // ...
+        return b;
+    }
+}
+

DexKit 可以轻松解决这个问题!

Hook 代码

通过创建 DexKitBridge 实例,我们可以对 APP 的 dex 进行特定的查找

注意

对于 DexKitBridge 只需要创建一个实例。 如果您不希望使用 try-with-resources 或者 kotlin .use 自动关闭 DexKitBridge 实例,需要自行维护其生命周期。请确保在不需要时调用 .close() 方法以防止内存泄漏。

`,8),D=s("div",{class:"language-kotlin line-numbers-mode","data-ext":"kt"},[s("pre",{class:"shiki github-dark-dimmed",style:{"background-color":"#22272e"},tabindex:"0"},[s("code",null,[s("span",{class:"line"},[s("span",{style:{color:"#F47067"}},"class"),s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#F69D50"}},"AppHooker"),s("span",{style:{color:"#ADBAC7"}}," {")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#F47067"}},"companion"),s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#DCBDFB"}},"object"),s("span",{style:{color:"#ADBAC7"}}," {")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#DCBDFB"}},"init"),s("span",{style:{color:"#ADBAC7"}}," { System."),s("span",{style:{color:"#DCBDFB"}},"loadLibrary"),s("span",{style:{color:"#ADBAC7"}},"("),s("span",{style:{color:"#96D0FF"}},'"dexkit"'),s("span",{style:{color:"#ADBAC7"}},") }")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," }")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," ")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#F47067"}},"private"),s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#F47067"}},"var"),s("span",{style:{color:"#ADBAC7"}}," hostClassLoader: "),s("span",{style:{color:"#F69D50"}},"ClassLoader")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," ")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#F47067"}},"public"),s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#DCBDFB"}},"constructor"),s("span",{style:{color:"#ADBAC7"}},"(loadPackageParam: "),s("span",{style:{color:"#F69D50"}},"LoadPackageParam"),s("span",{style:{color:"#ADBAC7"}},") {")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#6CB6FF"}},"this"),s("span",{style:{color:"#ADBAC7"}},".hostClassLoader "),s("span",{style:{color:"#F47067"}},"="),s("span",{style:{color:"#ADBAC7"}}," loadPackageParam.classLoader")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#F47067"}},"val"),s("span",{style:{color:"#ADBAC7"}}," apkPath "),s("span",{style:{color:"#F47067"}},"="),s("span",{style:{color:"#ADBAC7"}}," loadPackageParam.appInfo.sourceDir")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#768390"}},"// DexKit 创建是一项耗时操作,请不要重复创建。如果需要全局使用,")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#768390"}},"// 请自行管理生命周期,确保在不需要时调用 .close() 方法以防止内存泄漏。")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#768390"}},"// 这里使用 `Closable.use` 语法糖自动关闭 DexKitBridge 实例")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," DexKitBridge."),s("span",{style:{color:"#DCBDFB"}},"create"),s("span",{style:{color:"#ADBAC7"}},"(apkPath)."),s("span",{style:{color:"#DCBDFB"}},"use"),s("span",{style:{color:"#ADBAC7"}}," { bridge "),s("span",{style:{color:"#F47067"}},"->")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#DCBDFB"}},"isVipHook"),s("span",{style:{color:"#ADBAC7"}},"(bridge)")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#768390"}},"// Other hook ...")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," }")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," }")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," ")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#F47067"}},"private"),s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#F47067"}},"fun"),s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#DCBDFB"}},"isVipHook"),s("span",{style:{color:"#ADBAC7"}},"(bridge: "),s("span",{style:{color:"#F69D50"}},"DexKitBridge"),s("span",{style:{color:"#ADBAC7"}},") {")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#F47067"}},"val"),s("span",{style:{color:"#ADBAC7"}}," methodData "),s("span",{style:{color:"#F47067"}},"="),s("span",{style:{color:"#ADBAC7"}}," bridge."),s("span",{style:{color:"#DCBDFB"}},"findMethod"),s("span",{style:{color:"#ADBAC7"}}," {")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#DCBDFB"}},"matcher"),s("span",{style:{color:"#ADBAC7"}}," {")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#768390"}},"// 一切条件都是可选的,您可以自由的组合")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," modifiers "),s("span",{style:{color:"#F47067"}},"="),s("span",{style:{color:"#ADBAC7"}}," Modifier.PUBLIC")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," returnType "),s("span",{style:{color:"#F47067"}},"="),s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#96D0FF"}},'"boolean"')]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," paramCount "),s("span",{style:{color:"#F47067"}},"="),s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#6CB6FF"}},"0")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#DCBDFB"}},"usingStrings"),s("span",{style:{color:"#ADBAC7"}},"("),s("span",{style:{color:"#96D0FF"}},'"VipCheckUtil"'),s("span",{style:{color:"#ADBAC7"}},", "),s("span",{style:{color:"#96D0FF"}},'"userInfo:"'),s("span",{style:{color:"#ADBAC7"}},")")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," }")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," }."),s("span",{style:{color:"#DCBDFB"}},"single"),s("span",{style:{color:"#ADBAC7"}},"()")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#F47067"}},"val"),s("span",{style:{color:"#ADBAC7"}}," method: "),s("span",{style:{color:"#F69D50"}},"Method"),s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#F47067"}},"="),s("span",{style:{color:"#ADBAC7"}}," methodData."),s("span",{style:{color:"#DCBDFB"}},"getMethodInstance"),s("span",{style:{color:"#ADBAC7"}},"(hostClassLoader)")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," XposedBridge."),s("span",{style:{color:"#DCBDFB"}},"hookMethod"),s("span",{style:{color:"#ADBAC7"}},"(method, XC_MethodReplacement."),s("span",{style:{color:"#DCBDFB"}},"returnConstant"),s("span",{style:{color:"#ADBAC7"}},"("),s("span",{style:{color:"#6CB6FF"}},"true"),s("span",{style:{color:"#ADBAC7"}},"))")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," }")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," ")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#768390"}},"// ...")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}},"}")]),l(` +`),s("span",{class:"line"})])]),s("div",{class:"line-numbers","aria-hidden":"true"},[s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"})])],-1),B=s("div",{class:"language-java line-numbers-mode","data-ext":"java"},[s("pre",{class:"shiki github-dark-dimmed",style:{"background-color":"#22272e"},tabindex:"0"},[s("code",null,[s("span",{class:"line"},[s("span",{style:{color:"#F47067"}},"class"),s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#F69D50"}},"AppHooker"),s("span",{style:{color:"#ADBAC7"}}," {")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#F47067"}},"static"),s("span",{style:{color:"#ADBAC7"}}," {")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," System."),s("span",{style:{color:"#DCBDFB"}},"loadLibrary"),s("span",{style:{color:"#ADBAC7"}},"("),s("span",{style:{color:"#96D0FF"}},'"dexkit"'),s("span",{style:{color:"#ADBAC7"}},");")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," }")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," ")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#F47067"}},"private"),s("span",{style:{color:"#ADBAC7"}}," ClassLoader"),s("span",{style:{color:"#F69D50"}}," "),s("span",{style:{color:"#ADBAC7"}},"hostClassLoader;")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," ")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#F47067"}},"public"),s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#DCBDFB"}},"AppHooker"),s("span",{style:{color:"#ADBAC7"}},"(LoadPackageParam "),s("span",{style:{color:"#F69D50"}},"loadPackageParam"),s("span",{style:{color:"#ADBAC7"}},") {")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#6CB6FF"}},"this"),s("span",{style:{color:"#ADBAC7"}},".hostClassLoader "),s("span",{style:{color:"#F47067"}},"="),s("span",{style:{color:"#ADBAC7"}}," loadPackageParam.classLoader;")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," String"),s("span",{style:{color:"#F69D50"}}," "),s("span",{style:{color:"#ADBAC7"}},"apkPath"),s("span",{style:{color:"#F69D50"}}," "),s("span",{style:{color:"#F47067"}},"="),s("span",{style:{color:"#ADBAC7"}}," loadPackageParam.appInfo.sourceDir;")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#768390"}},"// DexKit 创建是一项耗时操作,请不要重复创建。如果需要全局使用,")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#768390"}},"// 请自行管理生命周期,确保在不需要时调用 .close() 方法以防止内存泄漏。")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#768390"}},"// 这里使用 `try-with-resources` 语法糖自动关闭 DexKitBridge 实例")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#F47067"}},"try"),s("span",{style:{color:"#ADBAC7"}}," (DexKitBridge"),s("span",{style:{color:"#F69D50"}}," "),s("span",{style:{color:"#ADBAC7"}},"bridge"),s("span",{style:{color:"#F69D50"}}," "),s("span",{style:{color:"#F47067"}},"="),s("span",{style:{color:"#ADBAC7"}}," DexKitBridge."),s("span",{style:{color:"#DCBDFB"}},"create"),s("span",{style:{color:"#ADBAC7"}},"(apkPath)) {")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#DCBDFB"}},"isVipHook"),s("span",{style:{color:"#ADBAC7"}},"(bridge);")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#768390"}},"// Other hook ...")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," }")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," }")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," ")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#F47067"}},"private"),s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#F47067"}},"void"),s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#DCBDFB"}},"isVipHook"),s("span",{style:{color:"#ADBAC7"}},"(DexKitBridge "),s("span",{style:{color:"#F69D50"}},"bridge"),s("span",{style:{color:"#ADBAC7"}},") {")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," MethodData"),s("span",{style:{color:"#F69D50"}}," "),s("span",{style:{color:"#ADBAC7"}},"methodData"),s("span",{style:{color:"#F69D50"}}," "),s("span",{style:{color:"#F47067"}},"="),s("span",{style:{color:"#ADBAC7"}}," bridge."),s("span",{style:{color:"#DCBDFB"}},"findMethod"),s("span",{style:{color:"#ADBAC7"}},"(FindMethod."),s("span",{style:{color:"#DCBDFB"}},"create"),s("span",{style:{color:"#ADBAC7"}},"()")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," ."),s("span",{style:{color:"#DCBDFB"}},"matcher"),s("span",{style:{color:"#ADBAC7"}},"(MethodMatcher."),s("span",{style:{color:"#DCBDFB"}},"create"),s("span",{style:{color:"#ADBAC7"}},"()")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#768390"}},"// 一切条件都是可选的,您可以自由的组合")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," ."),s("span",{style:{color:"#DCBDFB"}},"modifiers"),s("span",{style:{color:"#ADBAC7"}},"(Modifier.PUBLIC)")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," ."),s("span",{style:{color:"#DCBDFB"}},"paramCount"),s("span",{style:{color:"#ADBAC7"}},"("),s("span",{style:{color:"#6CB6FF"}},"0"),s("span",{style:{color:"#ADBAC7"}},")")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," ."),s("span",{style:{color:"#DCBDFB"}},"returnType"),s("span",{style:{color:"#ADBAC7"}},"("),s("span",{style:{color:"#96D0FF"}},'"boolean"'),s("span",{style:{color:"#ADBAC7"}},")")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," ."),s("span",{style:{color:"#DCBDFB"}},"usingStrings"),s("span",{style:{color:"#ADBAC7"}},"("),s("span",{style:{color:"#96D0FF"}},'"VipCheckUtil"'),s("span",{style:{color:"#ADBAC7"}},", "),s("span",{style:{color:"#96D0FF"}},'"userInfo:"'),s("span",{style:{color:"#ADBAC7"}},")")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," )")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," )."),s("span",{style:{color:"#DCBDFB"}},"single"),s("span",{style:{color:"#ADBAC7"}},"();")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," Method"),s("span",{style:{color:"#F69D50"}}," "),s("span",{style:{color:"#ADBAC7"}},"method"),s("span",{style:{color:"#F69D50"}}," "),s("span",{style:{color:"#F47067"}},"="),s("span",{style:{color:"#ADBAC7"}}," methodData."),s("span",{style:{color:"#DCBDFB"}},"getMethodInstance"),s("span",{style:{color:"#ADBAC7"}},"(hostClassLoader);")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," XposedBridge."),s("span",{style:{color:"#DCBDFB"}},"hookMethod"),s("span",{style:{color:"#ADBAC7"}},"(method, XC_MethodReplacement."),s("span",{style:{color:"#DCBDFB"}},"returnConstant"),s("span",{style:{color:"#ADBAC7"}},"("),s("span",{style:{color:"#6CB6FF"}},"true"),s("span",{style:{color:"#ADBAC7"}},"));")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," }")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," ")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}}," "),s("span",{style:{color:"#768390"}},"// ...")]),l(` +`),s("span",{class:"line"},[s("span",{style:{color:"#ADBAC7"}},"}")]),l(` +`),s("span",{class:"line"})])]),s("div",{class:"line-numbers","aria-hidden":"true"},[s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"}),s("div",{class:"line-number"})])],-1),d=s("p",null,"就是如此简单!",-1);function C(u,m){const a=o("CodeGroupItem"),c=o("CodeGroup");return p(),t("div",null,[y,n(c,null,{default:e(()=>[n(a,{title:"kotlin"},{default:e(()=>[D]),_:1}),n(a,{title:"java"},{default:e(()=>[B]),_:1})]),_:1}),d])}const F=r(A,[["render",C],["__file","index.html.vue"]]);export{F as default}; diff --git a/assets/knowledge.html-168eee0c.js b/assets/knowledge.html-168eee0c.js new file mode 100644 index 00000000..f6b99480 --- /dev/null +++ b/assets/knowledge.html-168eee0c.js @@ -0,0 +1 @@ +import{_ as l,r,o as d,c as n,d as e,e as t,a as s,b as a}from"./app-75e8845f.js";const o={},c=a('

Basic Knowledge

Here we provide some basic knowledge to help you better understand the usage of DexKit. Experienced developers can skip this section.

When using DexKit, there are some fundamental concepts you need to understand, including but not limited to:

  • Dex Decompilation Tools
  • JVM Signatures
    • Primitive Type Signatures
    • Reference Type Signatures
    • Array Type Signatures
    • Method Signatures
    • Field Signatures

Notice

The content in the basic knowledge is not necessarily completely accurate. Please read it according to your own understanding. If you find any inaccuracies, feel free to point them out and help improve.

Decompilation Tools

',6),h={href:"https://github.com/skylot/jadx",target:"_blank",rel:"noopener noreferrer"},p=a('

JVM Signatures

Primitive Types (PrimitiveType)

Type SignaturePrimitive TypeSize (Bytes)
Vvoid-
Zboolean1
Bbyte1
Cchar2
Sshort2
Iint4
Jlong8
Ffloat4
Ddouble8

Reference Types (ReferenceType)

Reference types are divided into classes and arrays.

Class (ClassType)

The type signature of a class starts with L, followed by the fully qualified name (FullClassName) of the class, and ends with ;. For example, Ljava/lang/String; represents the java.lang.String class.

For example:

Type SignatureJava Type Definition
Ljava/lang/String;java.lang.String
Ljava/util/List;java.util.List

Array (ArrayType)

The type signature of an array starts with [, followed by the type signature of the array elements. For example, [[I represents a two-dimensional array where the element type is int.

For example:

Type SignatureJava Type Definition
[Iint[]
[[Cchar[][]
[Ljava/lang/String;java.lang.String[]

Tips

The term 'class' and 'type' are not entirely equivalent: 'type' is Type, while 'class' is Class. 'Class' is a subset of 'type'. For example:

  • java.lang.Integer is a 'class' and also a 'type'
  • java.lang.Integer[] is an 'array type', but not a 'class'
  • int is a 'primitive type', but not a 'class'

For method parameters, return types, and field types, we use the term 'type,' specifically 'Type.'

Method Signatures

A method signature consists of the signature of the return type and the signature of the parameter types. For example, ()V represents a parameterless void method.

For example:

For ease of description, all methods in the table are named as function.

Method SignatureJava Method Definition
()Vvoid function()
(I)Vvoid function(int)
(II)Vvoid function(int, int)
(ILjava/lang/String;J)Vvoid function(int, java.lang.String, long)
(I[II)Vvoid function(int, int[], int)
([[Ljava/lang/String;)Vvoid function(java.lang.String[][])
()[Ljava/lang/String;java.lang.String[] function()

Dalvik Descriptor

In a Dex file, we can represent specific classes, methods, or fields using the 'Dalvik Descriptor.' In DexKit API, the term 'descriptor' is commonly used.

Class Descriptor

The format of a class descriptor is [class signature], such as Ljava/lang/String;.

Method Descriptor

The format of a method descriptor is [class signature]->[method name][method signature], such as Ljava/lang/String;->length()I.

Tips

In 'Dalvik Descriptor,' the method name for constructors is <init>, and for static initialization methods, it's <clinit>. Therefore, in DexKit, to find constructors, you need to use <init> as the method name.

Field Descriptor

The format of a field descriptor is [class signature]->[field name]:[type signature], such as Ljava/lang/String;->count:I.

Tips

In DexKit, the className/Type query parameter only supports the Java primitive syntax. For example:

  • For primitive types, use Java PrimitiveType forms like void, int, boolean.
  • For reference types, use FullClassName forms like java.lang.String or java/lang/String.
  • For array types, use ArrayTypeName forms like int[], java.lang.String[][] or java/lang/String[][].
',29);function y(g,f){const i=r("ExternalLinkIcon");return d(),n("div",null,[c,e("p",null,[t("Usually, you can use "),e("a",h,[t("jadx"),s(i)]),t(" to meet most of your needs. It can restore readable Java code in most cases.")]),p])}const x=l(o,[["render",y],["__file","knowledge.html.vue"]]);export{x as default}; diff --git a/assets/knowledge.html-8e7358a7.js b/assets/knowledge.html-8e7358a7.js new file mode 100644 index 00000000..7f4dad37 --- /dev/null +++ b/assets/knowledge.html-8e7358a7.js @@ -0,0 +1 @@ +import{_ as d,r as i,o as n,c as o,d as e,e as t,a as c,b as a}from"./app-75e8845f.js";const r={},s=a('

基础知识

这里提供了一些基础知识,帮助您更好的理解 DexKit 的使用,已经掌握的开发者可以跳过这一章节。

在使用 DexKit 时,有一些基础知识是必要的,其中包括但不限于以下内容:

  • Dex 反编译工具
  • JVM 签名
    • 原始类型签名
    • 引用类型签名
    • 数组类型签名
    • 方法签名
    • 字段签名

注意

基础知识中的内容不一定完全准确,请根据自己的见解酌情阅读,若发现内容有误,欢迎指正并帮助改进。

反编译工具

',6),g={href:"https://github.com/skylot/jadx",target:"_blank",rel:"noopener noreferrer"},h=a('

JVM 签名

原始类型(PrimitiveType)

类型签名原始类型大小(字节)
Vvoid-
Zboolean1
Bbyte1
Cchar2
Sshort2
Iint4
Jlong8
Ffloat4
Ddouble8

引用类型(ReferenceType)

引用类型分为类与数组。

类(ClassType)

类的类型签名都是以 L 开头,以 ; 结尾,中间是类的全限定名(FullClassName),如 Ljava/lang/String;

例如:

类型签名Java 中类型定义
Ljava/lang/String;java.lang.String
Ljava/util/List;java.util.List

数组(ArrayType)

数组类型的类型签名以 [ 开头,后面跟着数组元素的类型签名,如 [[I 表示一个二维数组,数组中的元素类型是 int

例如:

类型签名Java 中类型定义
[Iint[]
[[Cchar[][]
[Ljava/lang/String;java.lang.String[]

小提示

类型 并不完全等价:类型为 Type,而类为 Class。 类型 的子集。 例如:

  • java.lang.Integer,也是 类型
  • java.lang.Integer[]数组类型,但不是
  • int原始类型,但不是

对于方法参数,返回值类型以及字段的类型,我们统一称为 类型Type

方法签名

方法签名由方法的返回值类型签名和参数类型签名组成,如 ()V 表示一个无参的 void 方法。

例如:

为了方便表述,表格中所有的方法都命名为 function

方法签名Java 中方法定义
()Vvoid function()
(I)Vvoid function(int)
(II)Vvoid function(int, int)
(ILjava/lang/String;J)Vvoid function(int, java.lang.String, long)
(I[II)Vvoid function(int, int[], int)
([[Ljava/lang/String;)Vvoid function(java.lang.String[][])
()[Ljava/lang/String;java.lang.String[] function()

Dalvik 描述

在 Dex 文件中,我们可以通过 Dalvik 描述 的方式来表示特定的类、方法或字段。在 DexKit API中,通常使用 descriptor 来命名。

类描述

类描述的格式为 [类签名],如 Ljava/lang/String;

方法描述

方法描述的格式为 [类签名]->[方法名][方法签名],如 Ljava/lang/String;->length()I

小提示

Dalvik 描述 中,构造函数的方法名为 <init>,静态初始化函数的方法名为 <clinit>。 所以在 DexKit 中如果想要查找构造函数,需要使用 <init> 作为方法名。

字段描述

字段描述的格式为 [类签名]->[字段名]:[类型签名],如 Ljava/lang/String;->count:I

小提示

DexKit 中 className/Type 查询参数只支持 Java 原始写法,例如:

  • 对于基本类型,填写 voidintboolean 形式的 Java PrimitiveType
  • 对于引用类型,填写 java.lang.String 或者 java/lang/String 形式的 FullClassName
  • 对于数组类型,填写 int[]java.lang.String[][] 或者 java/lang/String[][] 形式的 ArrayTypeName
',29);function f(y,x){const l=i("ExternalLinkIcon");return n(),o("div",null,[s,e("p",null,[t("通常,您可以使用 "),e("a",g,[t("jadx"),c(l)]),t(" 来满足大部分需求, 它在大多数情况下能还原出可读的 Java 代码。")]),h])}const v=d(r,[["render",f],["__file","knowledge.html.vue"]]);export{v as default}; diff --git a/assets/knowledge.html-bf240073.js b/assets/knowledge.html-bf240073.js new file mode 100644 index 00000000..d753f810 --- /dev/null +++ b/assets/knowledge.html-bf240073.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-b4f1a468","path":"/zh-cn/guide/knowledge.html","title":"基础知识","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"反编译工具","slug":"反编译工具","link":"#反编译工具","children":[]},{"level":2,"title":"JVM 签名","slug":"jvm-签名","link":"#jvm-签名","children":[{"level":3,"title":"原始类型(PrimitiveType)","slug":"原始类型-primitivetype","link":"#原始类型-primitivetype","children":[]},{"level":3,"title":"引用类型(ReferenceType)","slug":"引用类型-referencetype","link":"#引用类型-referencetype","children":[]},{"level":3,"title":"方法签名","slug":"方法签名","link":"#方法签名","children":[]}]},{"level":2,"title":"Dalvik 描述","slug":"dalvik-描述","link":"#dalvik-描述","children":[{"level":3,"title":"类描述","slug":"类描述","link":"#类描述","children":[]},{"level":3,"title":"方法描述","slug":"方法描述","link":"#方法描述","children":[]},{"level":3,"title":"字段描述","slug":"字段描述","link":"#字段描述","children":[]}]}],"git":{"updatedTime":1718095560000,"contributors":[{"name":"teble","email":"me@teble.me","commits":1}]},"filePathRelative":"zh-cn/guide/knowledge.md"}');export{e as data}; diff --git a/assets/knowledge.html-fa621fe0.js b/assets/knowledge.html-fa621fe0.js new file mode 100644 index 00000000..8273e161 --- /dev/null +++ b/assets/knowledge.html-fa621fe0.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-277b35ca","path":"/en/guide/knowledge.html","title":"Basic Knowledge","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Decompilation Tools","slug":"decompilation-tools","link":"#decompilation-tools","children":[]},{"level":2,"title":"JVM Signatures","slug":"jvm-signatures","link":"#jvm-signatures","children":[{"level":3,"title":"Primitive Types (PrimitiveType)","slug":"primitive-types-primitivetype","link":"#primitive-types-primitivetype","children":[]},{"level":3,"title":"Reference Types (ReferenceType)","slug":"reference-types-referencetype","link":"#reference-types-referencetype","children":[]},{"level":3,"title":"Method Signatures","slug":"method-signatures","link":"#method-signatures","children":[]}]},{"level":2,"title":"Dalvik Descriptor","slug":"dalvik-descriptor","link":"#dalvik-descriptor","children":[{"level":3,"title":"Class Descriptor","slug":"class-descriptor","link":"#class-descriptor","children":[]},{"level":3,"title":"Method Descriptor","slug":"method-descriptor","link":"#method-descriptor","children":[]},{"level":3,"title":"Field Descriptor","slug":"field-descriptor","link":"#field-descriptor","children":[]}]}],"git":{"updatedTime":1718095560000,"contributors":[{"name":"teble","email":"me@teble.me","commits":1}]},"filePathRelative":"en/guide/knowledge.md"}');export{e as data}; diff --git a/assets/performance-optimization.html-0b660c19.js b/assets/performance-optimization.html-0b660c19.js new file mode 100644 index 00000000..31f0c84d --- /dev/null +++ b/assets/performance-optimization.html-0b660c19.js @@ -0,0 +1,40 @@ +import{_ as s,o as n,c as a,b as e}from"./app-75e8845f.js";const l={},o=e(`

Performance Optimization

In DexKit, various queries may achieve the same functionality, but the difference in performance can be significant, varying by several tens of times. This section will introduce some techniques for performance optimization.

At the native layer, DexKit maintains lists of classes, methods, and fields in the Dex file. How does DexKit scan these lists in several APIs? The traversal order of findClass, findMethod, and findField is based on the respective lists' sequential order. Then, each condition is matched one by one.

declaredClass condition is too heavy

Some users may use the declaredClass condition to write queries like the following when using:

private fun badCode(bridge: DexKitBridge) {
+    bridge.findMethod {
+        matcher {
+            declaredClass {
+                usingStrings("getUid", "", "_event")
+            }
+            modifiers = Modifier.PUBLIC or Modifier.STATIC
+            returnType = "long"
+            addInvoke {
+                name = "parseLong"
+            }
+            addInvoke {
+                name = "toString"
+            }
+        }
+    }.single().let {
+        println(it)
+    }
+}
+

This search takes 4310ms.

At first glance, this query seems fine, but in reality, its performance is very poor. Why? As mentioned earlier, the findMethod API traverses all methods and then matches each condition one by one. However, there is a many-to-one relationship between methods and classes, meaning a class may contain multiple methods, but a method can only belong to one class. Therefore, during the process of traversing all methods, each method will be matched once with the declaredClass condition, leading to performance waste.

So, let's change our approach. By first searching for declaredClass and then using chain calls, we can search for methods within the classes that meet the criteria. Won't this help avoid the issue?

private fun goodCode(bridge: DexKitBridge) {
+    bridge.findClass {
+        matcher {
+            usingStrings("getUid", "", "_event")
+        }
+    }.findMethod {
+        matcher {
+            modifiers = Modifier.PUBLIC or Modifier.STATIC
+            returnType = "long"
+            addInvoke {
+                name = "parseLong"
+            }
+            addInvoke {
+                name = "toString"
+            }
+        }
+    }.single().let {
+        println(it)
+    }
+}
+

This search takes 77ms, showing a performance improvement by several tens of times.

When using findMethod or findField, the declaredClass condition should be avoided as much as possible.

`,12),p=[o];function i(c,t){return n(),a("div",null,p)}const d=s(l,[["render",i],["__file","performance-optimization.html.vue"]]);export{d as default}; diff --git a/assets/performance-optimization.html-4f96b155.js b/assets/performance-optimization.html-4f96b155.js new file mode 100644 index 00000000..ceda3afd --- /dev/null +++ b/assets/performance-optimization.html-4f96b155.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-7f55a05e","path":"/zh-cn/guide/performance-optimization.html","title":"性能优化","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"declaredClass 条件过重","slug":"declaredclass-条件过重","link":"#declaredclass-条件过重","children":[]}],"git":{"updatedTime":1718095560000,"contributors":[{"name":"teble","email":"me@teble.me","commits":1}]},"filePathRelative":"zh-cn/guide/performance-optimization.md"}');export{e as data}; diff --git a/assets/performance-optimization.html-69f9f3ed.js b/assets/performance-optimization.html-69f9f3ed.js new file mode 100644 index 00000000..348f6e67 --- /dev/null +++ b/assets/performance-optimization.html-69f9f3ed.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-7de9012f","path":"/en/guide/performance-optimization.html","title":"Performance Optimization","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"declaredClass condition is too heavy","slug":"declaredclass-condition-is-too-heavy","link":"#declaredclass-condition-is-too-heavy","children":[]}],"git":{"updatedTime":1718095560000,"contributors":[{"name":"teble","email":"me@teble.me","commits":1}]},"filePathRelative":"en/guide/performance-optimization.md"}');export{e as data}; diff --git a/assets/performance-optimization.html-91684308.js b/assets/performance-optimization.html-91684308.js new file mode 100644 index 00000000..17037bbb --- /dev/null +++ b/assets/performance-optimization.html-91684308.js @@ -0,0 +1,40 @@ +import{_ as s,o as n,c as a,b as l}from"./app-75e8845f.js";const e={},o=l(`

性能优化

在 DexKit 中,多种查询或许能实现同样的功能,但是性能差距却可能相差几十倍。本节将介绍一些性能优化的技巧。

在 native 层,DexKit 会维护 Dex 中的类、方法以及字段的列表,那么在几个 API 中,DexKit 是如何扫描这些列表的呢?findClassfindMethodfindField 的遍历顺序均是按照各自列表的先后顺序进行遍历,然后再逐一对各个条件进行匹配。

declaredClass 条件过重

可能有些用户在使用 findMethodfindField 时,会使用 declaredClass 条件写出如下的查询:

private fun badCode(bridge: DexKitBridge) {
+    bridge.findMethod {
+        matcher {
+            declaredClass {
+                usingStrings("getUid", "", "_event")
+            }
+            modifiers = Modifier.PUBLIC or Modifier.STATIC
+            returnType = "long"
+            addInvoke {
+                name = "parseLong"
+            }
+            addInvoke {
+                name = "toString"
+            }
+        }
+    }.single().let {
+        println(it)
+    }
+}
+

这个搜索耗时 4310ms。 乍一看这个查询似乎没有什么问题,但是实际上这个查询的性能是非常差。为什么?前面提到过,findMethod API 会遍历一遍所有的方法,然后再逐一对各个条件进行匹配。而 method 与 class 之间却是一个多对一的关系,即一个 class 中可能包含多个 method,但是一个 method 只能属于一个 class。因此,遍历所有方法的过程中,每个 method 都会被匹配一次 declaredClass 条件,这就导致了性能的浪费。

那么,我们换一个思路,先搜索 declaredClass,配合链式调用就能在符合条件的类中再搜索 method,这样不就可以避免了吗?

private fun goodCode(bridge: DexKitBridge) {
+    bridge.findClass {
+        matcher {
+            usingStrings("getUid", "", "_event")
+        }
+    }.findMethod {
+        matcher {
+            modifiers = Modifier.PUBLIC or Modifier.STATIC
+            returnType = "long"
+            addInvoke {
+                name = "parseLong"
+            }
+            addInvoke {
+                name = "toString"
+            }
+        }
+    }.single().let {
+        println(it)
+    }
+}
+

这个搜索耗时 77ms, 性能提升了数十倍之多。

在使用 findMethodfindField 时,尽量避免使用 declaredClass 附带过于复杂的逻辑。

`,11),p=[o];function c(i,r){return n(),a("div",null,p)}const d=s(e,[["render",c],["__file","performance-optimization.html.vue"]]);export{d as default}; diff --git a/assets/quick-start.html-1c002ee2.js b/assets/quick-start.html-1c002ee2.js new file mode 100644 index 00000000..c8c3fe89 --- /dev/null +++ b/assets/quick-start.html-1c002ee2.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-72889797","path":"/en/guide/quick-start.html","title":"Quick Start","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Environment Requirements","slug":"environment-requirements","link":"#environment-requirements","children":[]},{"level":2,"title":"Integration Dependency","slug":"integration-dependency","link":"#integration-dependency","children":[]}],"git":{"updatedTime":1718095560000,"contributors":[{"name":"teble","email":"me@teble.me","commits":1}]},"filePathRelative":"en/guide/quick-start.md"}');export{e as data}; diff --git a/assets/quick-start.html-4917d6af.js b/assets/quick-start.html-4917d6af.js new file mode 100644 index 00000000..ab2f12f3 --- /dev/null +++ b/assets/quick-start.html-4917d6af.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-24840ff0","path":"/zh-cn/guide/quick-start.html","title":"快速开始","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"环境要求","slug":"环境要求","link":"#环境要求","children":[]},{"level":2,"title":"集成依赖","slug":"集成依赖","link":"#集成依赖","children":[]}],"git":{"updatedTime":1718095560000,"contributors":[{"name":"teble","email":"me@teble.me","commits":1}]},"filePathRelative":"zh-cn/guide/quick-start.md"}');export{e as data}; diff --git a/assets/quick-start.html-ba5a064e.js b/assets/quick-start.html-ba5a064e.js new file mode 100644 index 00000000..4c029091 --- /dev/null +++ b/assets/quick-start.html-ba5a064e.js @@ -0,0 +1,16 @@ +import{_ as d,r as a,o as r,c as p,d as e,e as s,a as n,w as l,b as i}from"./app-75e8845f.js";const u={},m=i(`

快速开始

集成 DexKit 到您的项目中。

环境要求

确保您的开发环境满足以下要求:

  • JDK 11 及以上
  • Kotlin 1.5 及以上
  • AGP 4.2 及以上
  • minSdkVersion 21 及以上(推荐 23 及以上)

注意

如果您的项目的 minSdkVersion 小于 23,在 Xposed 模块内使用 System.loadLibrary("dexkit") 时可能会抛出 java.lang.UnsatisfiedLinkError: xxx couldn't find "libdexkit.so" 的异常。这是因为打包时默认会压缩 lib/ 目录下的 so 文件,导致无法通过 System.loadLibrary 加载 so 文件。解决方案是在 app/build.gradle 中添加以下配置:

android {
+    packagingOptions {
+        jniLibs {
+            useLegacyPackaging true
+        }
+    }
+}
+

或者手动解压 apk 内的 lib/ 目录下的 libdexkit.so 文件至任意可读写目录,然后通过 System.load("/path/to/libdexkit.so") 加载。

集成依赖

在您的项目中的 app/build.gradle 或者 app/build.gradle.kts 添加 dexkit 的依赖。

`,8),b={href:"https://central.sonatype.com/search?q=dexkit&namespace=org.luckypray",target:"_blank",rel:"noopener noreferrer"},v=e("img",{src:"https://img.shields.io/maven-central/v/org.luckypray/dexkit.svg?label=Maven Central",alt:"Maven Central"},null,-1),y=e("div",{class:"language-groovy line-numbers-mode","data-ext":"groovy"},[e("pre",{class:"shiki github-dark-dimmed",style:{"background-color":"#22272e"},tabindex:"0"},[e("code",null,[e("span",{class:"line"},[e("span",{style:{color:"#ADBAC7"}},"dependencies {")]),s(` +`),e("span",{class:"line"},[e("span",{style:{color:"#ADBAC7"}}," "),e("span",{style:{color:"#768390"}},"// 将 替换为您需要的版本,例如 '2.0.0-rc1'")]),s(` +`),e("span",{class:"line"},[e("span",{style:{color:"#ADBAC7"}}," implementation "),e("span",{style:{color:"#96D0FF"}},"'org.luckypray:dexkit:'")]),s(` +`),e("span",{class:"line"},[e("span",{style:{color:"#ADBAC7"}},"}")]),s(` +`),e("span",{class:"line"})])]),e("div",{class:"line-numbers","aria-hidden":"true"},[e("div",{class:"line-number"}),e("div",{class:"line-number"}),e("div",{class:"line-number"}),e("div",{class:"line-number"})])],-1),k=e("div",{class:"language-kotlin line-numbers-mode","data-ext":"kt"},[e("pre",{class:"shiki github-dark-dimmed",style:{"background-color":"#22272e"},tabindex:"0"},[e("code",null,[e("span",{class:"line"},[e("span",{style:{color:"#DCBDFB"}},"dependencies"),e("span",{style:{color:"#ADBAC7"}}," {")]),s(` +`),e("span",{class:"line"},[e("span",{style:{color:"#ADBAC7"}}," "),e("span",{style:{color:"#768390"}},"// 将 替换为您需要的版本,例如 '2.0.0-rc1'")]),s(` +`),e("span",{class:"line"},[e("span",{style:{color:"#ADBAC7"}}," "),e("span",{style:{color:"#DCBDFB"}},"implementation"),e("span",{style:{color:"#ADBAC7"}},"("),e("span",{style:{color:"#96D0FF"}},'"org.luckypray:dexkit:"'),e("span",{style:{color:"#ADBAC7"}},")")]),s(` +`),e("span",{class:"line"},[e("span",{style:{color:"#ADBAC7"}},"}")]),s(` +`),e("span",{class:"line"})])]),e("div",{class:"line-numbers","aria-hidden":"true"},[e("div",{class:"line-number"}),e("div",{class:"line-number"}),e("div",{class:"line-number"}),e("div",{class:"line-number"})])],-1),h=i('

小提示

DexKit 2.0 开始,新的 ArtifactId 已从 DexKit 更改为 dexkit

现在您已经成功集成了 DexKit 到您的项目中,接下来我们将会介绍如何使用 DexKit 来完成一些常见的需求。

',2);function g(A,x){const c=a("ExternalLinkIcon"),o=a("CodeGroupItem"),t=a("CodeGroup");return r(),p("div",null,[m,e("p",null,[s("DexKit 当前版本: "),e("a",b,[v,n(c)])]),n(t,null,{default:l(()=>[n(o,{title:"gradle"},{default:l(()=>[y]),_:1}),n(o,{title:"gradle-kts"},{default:l(()=>[k]),_:1})]),_:1}),h])}const D=d(u,[["render",g],["__file","quick-start.html.vue"]]);export{D as default}; diff --git a/assets/quick-start.html-d0230eac.js b/assets/quick-start.html-d0230eac.js new file mode 100644 index 00000000..531806f3 --- /dev/null +++ b/assets/quick-start.html-d0230eac.js @@ -0,0 +1,16 @@ +import{_ as c,r as a,o as d,c as p,d as e,e as n,a as s,w as o,b as t}from"./app-75e8845f.js";const u={},m=t(`

Quick Start

Integrate DexKit into your project.

Environment Requirements

Make sure your development environment meets the following requirements:

  • JDK 11 and above
  • Kotlin 1.5 and above
  • AGP 4.2 and above
  • minSdkVersion 21 and above (recommended 23 and above)

Notice

If your project's minSdkVersion is less than 23, using System.loadLibrary("dexkit") within an Xposed module may throw a java.lang.UnsatisfiedLinkError: xxx couldn't find "libdexkit.so" exception. This is because the so files under the lib/ directory are compressed by default during packaging, making it impossible to load the so file via System.loadLibrary. The solution is to add the following configuration in app/build.gradle:

android {
+    packagingOptions {
+        jniLibs {
+            useLegacyPackaging true
+        }
+    }
+}
+

or manually extract the libdexkit.so file from the lib/ directory within the apk to any readable and writable directory, and then load it using System.load("/path/to/libdexkit.so").

Integration Dependency

Add the dependency for dexkit in your project's app/build.gradle or app/build.gradle.kts.

`,8),h={href:"https://central.sonatype.com/search?q=dexkit&namespace=org.luckypray",target:"_blank",rel:"noopener noreferrer"},v=e("img",{src:"https://img.shields.io/maven-central/v/org.luckypray/dexkit.svg?label=Maven Central",alt:"Maven Central"},null,-1),y=e("div",{class:"language-groovy line-numbers-mode","data-ext":"groovy"},[e("pre",{class:"shiki github-dark-dimmed",style:{"background-color":"#22272e"},tabindex:"0"},[e("code",null,[e("span",{class:"line"},[e("span",{style:{color:"#ADBAC7"}},"dependencies {")]),n(` +`),e("span",{class:"line"},[e("span",{style:{color:"#ADBAC7"}}," "),e("span",{style:{color:"#768390"}},"// replace with your desired version, e.g. '2.0.0'")]),n(` +`),e("span",{class:"line"},[e("span",{style:{color:"#ADBAC7"}}," implementation "),e("span",{style:{color:"#96D0FF"}},"'org.luckypray:dexkit:'")]),n(` +`),e("span",{class:"line"},[e("span",{style:{color:"#ADBAC7"}},"}")]),n(` +`),e("span",{class:"line"})])]),e("div",{class:"line-numbers","aria-hidden":"true"},[e("div",{class:"line-number"}),e("div",{class:"line-number"}),e("div",{class:"line-number"}),e("div",{class:"line-number"})])],-1),b=e("div",{class:"language-kotlin line-numbers-mode","data-ext":"kt"},[e("pre",{class:"shiki github-dark-dimmed",style:{"background-color":"#22272e"},tabindex:"0"},[e("code",null,[e("span",{class:"line"},[e("span",{style:{color:"#DCBDFB"}},"dependencies"),e("span",{style:{color:"#ADBAC7"}}," {")]),n(` +`),e("span",{class:"line"},[e("span",{style:{color:"#ADBAC7"}}," "),e("span",{style:{color:"#768390"}},"// replace with your desired version, e.g. '2.0.0'")]),n(` +`),e("span",{class:"line"},[e("span",{style:{color:"#ADBAC7"}}," "),e("span",{style:{color:"#DCBDFB"}},"implementation"),e("span",{style:{color:"#ADBAC7"}},"("),e("span",{style:{color:"#96D0FF"}},'"org.luckypray:dexkit:"'),e("span",{style:{color:"#ADBAC7"}},")")]),n(` +`),e("span",{class:"line"},[e("span",{style:{color:"#ADBAC7"}},"}")]),n(` +`),e("span",{class:"line"})])]),e("div",{class:"line-numbers","aria-hidden":"true"},[e("div",{class:"line-number"}),e("div",{class:"line-number"}),e("div",{class:"line-number"}),e("div",{class:"line-number"})])],-1),g=t('

Tips

Starting from DexKit 2.0, the new ArtifactId has been changed from DexKit to dexkit.

Now that you have successfully integrated DexKit into your project, next we will introduce how to use DexKit to achieve some common requirements.

',2);function k(x,A){const l=a("ExternalLinkIcon"),i=a("CodeGroupItem"),r=a("CodeGroup");return d(),p("div",null,[m,e("p",null,[n("Current DexKit version: "),e("a",h,[v,s(l)])]),s(r,null,{default:o(()=>[s(i,{title:"gradle"},{default:o(()=>[y]),_:1}),s(i,{title:"gradle-kts"},{default:o(()=>[b]),_:1})]),_:1}),g])}const f=c(u,[["render",k],["__file","quick-start.html.vue"]]);export{f as default}; diff --git a/assets/run-on-desktop.html-426dfbb6.js b/assets/run-on-desktop.html-426dfbb6.js new file mode 100644 index 00000000..728bd55c --- /dev/null +++ b/assets/run-on-desktop.html-426dfbb6.js @@ -0,0 +1,5 @@ +import{_ as i,r as t,o as r,c as d,d as n,e,a as o,b as a}from"./app-75e8845f.js";const l={},c=a('

Run on desktop platform

Starting from version 1.1.0, DexKit supports running on desktop platforms without the need for packaging as an APK for testing on Android.

Install environment

The basic runtime environment requires gcc/clang, cmake, and ninja/make.

Windows

',5),p=n("code",null,"Windows",-1),u={href:"https://www.msys2.org/",target:"_blank",rel:"noopener noreferrer"},h=n("code",null,"mingw64.exe",-1),m=a(`
pacman -S mingw-w64-x86_64-gcc mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja
+

After installation, we need to add the mingw64/bin directory to the environment variables for future use.

warning

DexKit will use ninja as the default build system by default. If you need to use make in mingw for building, you need to execute pacman -S mingw-w64-x86_64-make, after installation, you need to rename msys64\\mingw64\\bin\\mingw32-make.exe to make.exe or add it as a shortcut, otherwise the build will fail due to gradle-cmake-plugin not finding the make command. Additionally, delete generator.set(generators.ninja) in :dexkit/build.gradle, or modify it to generator.set(generators.unixMakefiles).

Linux

On Linux, you normally only need to install ninja to use it.

MacOS

`,6),g={href:"https://brew.sh/",target:"_blank",rel:"noopener noreferrer"},b=a(`
brew install cmake ninja
+

Clone DexKit

git clone https://github.com/LuckyPray/DexKit.git
+

To begin using

Execute the submodule :main to perform testing.

gradle :main:run
+
`,6);function y(k,x){const s=t("ExternalLinkIcon");return r(),d("div",null,[c,n("p",null,[p,e(" users can use "),n("a",u,[e("MSYS2"),o(s)]),e(" to set up the runtime environment. Since all Windows systems are currently 64-bit, we use "),h,e(" for dependency installation:")]),m,n("p",null,[e("It's recommended to use "),n("a",g,[e("HomeBrew"),o(s)]),e(" for dependency management.")]),b])}const v=i(l,[["render",y],["__file","run-on-desktop.html.vue"]]);export{v as default}; diff --git a/assets/run-on-desktop.html-6d699af5.js b/assets/run-on-desktop.html-6d699af5.js new file mode 100644 index 00000000..86aac9ab --- /dev/null +++ b/assets/run-on-desktop.html-6d699af5.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-08f05018","path":"/zh-cn/guide/run-on-desktop.html","title":"在桌面平台运行","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"安装环境","slug":"安装环境","link":"#安装环境","children":[{"level":3,"title":"Windows","slug":"windows","link":"#windows","children":[]},{"level":3,"title":"Linux","slug":"linux","link":"#linux","children":[]},{"level":3,"title":"MacOS","slug":"macos","link":"#macos","children":[]}]},{"level":2,"title":"克隆 DexKit","slug":"克隆-dexkit","link":"#克隆-dexkit","children":[]},{"level":2,"title":"开始使用","slug":"开始使用","link":"#开始使用","children":[]}],"git":{"updatedTime":1718095560000,"contributors":[{"name":"teble","email":"me@teble.me","commits":1}]},"filePathRelative":"zh-cn/guide/run-on-desktop.md"}');export{e as data}; diff --git a/assets/run-on-desktop.html-8df4653a.js b/assets/run-on-desktop.html-8df4653a.js new file mode 100644 index 00000000..3997214d --- /dev/null +++ b/assets/run-on-desktop.html-8df4653a.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-206e5229","path":"/en/guide/run-on-desktop.html","title":"Run on desktop platform","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Install environment","slug":"install-environment","link":"#install-environment","children":[{"level":3,"title":"Windows","slug":"windows","link":"#windows","children":[]},{"level":3,"title":"Linux","slug":"linux","link":"#linux","children":[]},{"level":3,"title":"MacOS","slug":"macos","link":"#macos","children":[]}]},{"level":2,"title":"Clone DexKit","slug":"clone-dexkit","link":"#clone-dexkit","children":[]},{"level":2,"title":"To begin using","slug":"to-begin-using","link":"#to-begin-using","children":[]}],"git":{"updatedTime":1718095560000,"contributors":[{"name":"teble","email":"me@teble.me","commits":1}]},"filePathRelative":"en/guide/run-on-desktop.md"}');export{e as data}; diff --git a/assets/run-on-desktop.html-b6246f6d.js b/assets/run-on-desktop.html-b6246f6d.js new file mode 100644 index 00000000..6b1c0cac --- /dev/null +++ b/assets/run-on-desktop.html-b6246f6d.js @@ -0,0 +1,5 @@ +import{_ as d,r as i,o as c,c as r,d as a,e,a as o,b as n}from"./app-75e8845f.js";const l={},t=n('

在桌面平台运行

1.1.0 开始,DexKit 支持桌面平台运行,无需打包成 apk 在 Android 进行测试工作。

安装环境

需要 gcc/clang、cmake 以及 ninja/make 作为基础运行环境。

Windows

',5),p=a("code",null,"Windows",-1),h={href:"https://www.msys2.org/",target:"_blank",rel:"noopener noreferrer"},m=a("code",null,"mingw64.exe",-1),u=n(`
pacman -S mingw-w64-x86_64-gcc mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja
+

安装完成后,我们需要将 mingw64/bin 目录添加进环境变量中便于后续使用。

warning

DexKit 默认将使用 ninja 作为默认构建系统,如果您需要在 mingw 中使用 make 进行构建, 需要执行 pacman -S mingw-w64-x86_64-make,安装完成后需要将 msys64\\mingw64\\bin\\mingw32-make.exe 重命名为 make.exe 或者添加为快捷方式,否则会由于 gradle-cmake-plugin 找不到 make 命令而构建失败。 同时删除 :dexkit/build.gradle 中的 generator.set(generators.ninja) ,或者修改为 generator.set(generators.unixMakefiles)

Linux

对于Linux用户,正常情况只需要安装 ninja 即可。

MacOS

`,6),b={href:"https://brew.sh/",target:"_blank",rel:"noopener noreferrer"},g=n(`
brew install cmake ninja
+

克隆 DexKit

git clone https://github.com/LuckyPray/DexKit.git
+

开始使用

执行子模块 :main 即可进行测试。

gradle :main:run
+
`,6);function x(k,_){const s=i("ExternalLinkIcon");return c(),r("div",null,[t,a("p",null,[p,e(" 用户可以使用 "),a("a",h,[e("MSYS2"),o(s)]),e(" 搭建运行环境。由于目前Windows系统均为64位, 所以我们使用 "),m,e(" 进行依赖安装:")]),u,a("p",null,[e("推荐使用 "),a("a",b,[e("HomeBrew"),o(s)]),e(" 进行依赖管理,")]),g])}const v=d(l,[["render",x],["__file","run-on-desktop.html.vue"]]);export{v as default}; diff --git a/assets/structural-zoom-table.html-0b909054.js b/assets/structural-zoom-table.html-0b909054.js new file mode 100644 index 00000000..a475fe12 --- /dev/null +++ b/assets/structural-zoom-table.html-0b909054.js @@ -0,0 +1 @@ +import{_ as d,r as n,o as i,c as r,d as t,e,a as s,b as l}from"./app-75e8845f.js";const h={},y=l('

结构速查表

这里介绍了 DexKit 查询的结构组成,匹配器对象可以进行递归嵌套,一切条件都是可以选的。

查询相关

FindClass

字段名类型说明
searchPackagesCollection<String>搜索的包名列表
excludePackagesCollection<String>排除的包名列表
ignorePackagesCaseBoolean是否忽略包名大小写
searchInCollection<ClassData>在指定的类列表中搜索类
findFirstBoolean查询到第一个结果后立即返回结果,由于多线程执行,不保证结果唯一
matcherClassMatcher匹配条件

FindField

字段名类型说明
searchPackagesCollection<String>搜索的包名列表
excludePackagesCollection<String>排除的包名列表
ignorePackagesCaseBoolean是否忽略包名大小写
searchInClassesCollection<ClassData>在指定的类列表中搜索字段
searchInFieldsCollection<FieldData>在指定的字段列表中搜索字段
findFirstBoolean查询到第一个结果后立即返回结果,由于多线程执行,不保证结果唯一
matcherFieldMatcher匹配条件

FindMethod

字段名类型说明
searchPackagesCollection<String>搜索的包名列表
excludePackagesCollection<String>排除的包名列表
ignorePackagesCaseBoolean是否忽略包名大小写
searchInClassesCollection<ClassData>在指定的类列表中搜索方法
searchInMethodsCollection<MethodData>在指定的方法列表中搜索方法
findFirstBoolean查询到第一个结果后立即返回结果,由于多线程执行,不保证结果唯一
matcherMethodMatcher匹配条件

BatchFindClassUsingStrings

字段名类型说明
searchPackagesCollection<String>搜索的包名列表
excludePackagesCollection<String>排除的包名列表
ignorePackagesCaseBoolean是否忽略包名大小写
searchInCollection<ClassData>在指定的类列表中搜索类
matchersCollection<StringMatchersGroup>查询分组列表

BatchFindMethodUsingStrings

字段名类型说明
searchPackagesCollection<String>搜索的包名列表
excludePackagesCollection<String>排除的包名列表
ignorePackagesCaseBoolean是否忽略包名大小写
searchInClassesCollection<ClassData>在指定的类列表中搜索方法
searchInMethodsCollection<MethodData>在指定的方法列表中搜索方法
matchersCollection<StringMatchersGroup>查询分组列表

匹配器相关

AccessFlagsMatcher

字段名类型说明
modifiersInt匹配的修饰符的 bit masks
matchTypeMatchType匹配模式

AnnotationEncodeValueMatcher

字段名类型说明
byteValueByte匹配的字节
shortValueShort匹配的短整型
charValueChar匹配的字符
intValueInt匹配的整型
longValueLong匹配的长整型
floatValueFloat匹配的浮点型
doubleValueDouble匹配的双精度浮点
stringValueStringMatcher匹配的字符串
methodValueMethodMatcher匹配的方法
enumValueFieldMatcher匹配的枚举
arrayValueAnnotationEncodeArrayMatcher匹配的数组
annotationValueAnnotationMatcher匹配的注解
nullValue匹配 null
boolValueBoolean匹配的布尔值

IntRange

字段名类型说明
minInt最小值,默认为 0
maxInt最大值,默认为 Int.MAX_VALUE

NumberEncodeValueMatcher

字段名类型说明
byteValueByte匹配的字节
shortValueShort匹配的短整型
charValueChar匹配的字符
intValueInt匹配的整型
longValueLong匹配的长整型
floatValueFloat匹配的浮点型
doubleValueDouble匹配的双精度浮点

OpcodesMatcher

字段名类型说明
opCodesCollection<Int>匹配的操作码对应的 Int 值
opNamesCollection<String>匹配的操作码对应的名称,即 smali 语法中的名称
matchTypeOpCodeMatchType匹配模式
sizeIntRange该操作码总长度范围

StringMatcher

字段名类型说明
valueString匹配的字符串
matchTypeStringMatchType匹配模式
ignoreCaseBoolean是否忽略大小写

TargetElementTypesMatcher

字段名类型说明
typesCollection<TargetElementType>匹配的注解声明的元素类型列表
matchTypeMatchType匹配模式

AnnotationElementMatcher

字段名类型说明
nameStringMatcher匹配的名称
valueAnnotationEncodeValueMatcher匹配的值

AnnotationElementsMatcher

字段名类型说明
elementsCollection<AnnotationElementMatcher>匹配的注解声明的元素列表
matchTypeMatchType匹配模式
countIntRange匹配的注解声明的元素数量

AnnotationEncodeArrayMatcher

字段名类型说明
valuesCollection<AnnotationEncodeValueMatcher>匹配的注解声明的元素列表
matchTypeMatchType匹配模式
countIntRange匹配的注解声明的元素数量

AnnotationMatcher

字段名类型说明
typeClassMatcher匹配的注解类型
targetElementTypesTargetElementTypesMatcher匹配的注解声明的元素类型列表
policyRetentionPolicyType匹配的注解声明的保留策略
elementsAnnotationElementsMatcher匹配的注解声明的元素列表
usingStringsCollection<StringMatcher>注解中使用的字符串列表

AnnotationsMatcher

字段名类型说明
annotationsCollection<AnnotationMatcher>匹配的注解列表
matchTypeMatchType匹配模式
countIntRange匹配的注解数量

ClassMatcher

字段名类型说明
sourceStringMatcher类的源码文件名,即 smali 中的 .source 字段
classNameStringMatcher类的名称
modifiersAccessFlagsMatcher类的修饰符
superClassClassMatcher类的父类
interfacesInterfacesMatcher类的接口列表
annotationsAnnotationsMatcher类的注解列表
fieldsFieldsMatcher类的字段列表
methodsMethodsMatcher类的方法列表
usingStringsCollection<StringMatcher>类中使用的字符串列表

InterfacesMatcher

字段名类型说明
interfacesCollection<ClassMatcher>匹配的接口列表
matchTypeMatchType匹配模式
countIntRange匹配的接口数量

FieldMatcher

字段名类型说明
nameStringMatcher字段的名称
modifiersAccessFlagsMatcher字段的修饰符
declaredClassClassMatcher字段的声明类
typeClassMatcher字段的类型
annotationsAnnotationsMatcher字段的注解
readMethodsMethodsMatcher读取该字段的方法列表
writeMethodsMethodsMatcher设置该字段的方法列表

FieldsMatcher

字段名类型说明
fieldsCollection<FieldMatcher>匹配的字段列表
matchTypeMatchType匹配模式
countIntRange匹配的字段数量

MethodMatcher

字段名类型说明
nameStringMatcher方法的名称
modifiersAccessFlagsMatcher方法的修饰符
declaredClassClassMatcher方法的声明类
returnTypeClassMatcher方法的返回值类型
paramsParametersMatcher方法的参数列表
annotationsAnnotationsMatcher方法的注解
opCodesOpcodesMatcher方法的操作码列表
usingStringsCollection<StringMatcher>方法中使用的字符串列表
usingFieldsCollection<UsingFieldMatcher>方法中使用的字段列表
usingNumbersCollection<Number>方法中使用的数字列表
invokeMethodsMethodsMatcher方法中调用的方法列表
callerMethodsMethodsMatcher调用了该方法的方法列表

MethodsMatcher

字段名类型说明
methodsCollection<MethodMatcher>匹配的方法列表
matchTypeMatchType匹配模式
countIntRange匹配的方法数量

ParameterMatcher

字段名类型说明
typeClassMatcher参数的类型
annotationsAnnotationsMatcher参数的注解

ParametersMatcher

字段名类型说明
paramsCollection<ParameterMatcher>匹配的参数列表
countIntRange匹配的参数数量

StringMatchersGroup

字段名类型说明
groupNameString分组名称
matchersCollection<StringMatcher>该查询分组匹配对象所使用的字符串列表

UsingFieldMatcher

字段名类型说明
fieldFieldMatcher匹配的字段
usingTypeUsingType使用类型

EncodeValueByte

字段名类型说明
valueByte字节

EncodeValueShort

字段名类型说明
valueShort短整型

EncodeValueChar

字段名类型说明
valueChar字符

EncodeValueInt

字段名类型说明
valueInt整型

EncodeValueLong

字段名类型说明
valueLong长整型

EncodeValueFloat

字段名类型说明
valueFloat浮点型

EncodeValueDouble

字段名类型说明
valueDouble双精度浮点

EncodeValueString

字段名类型说明
valueString字符串

EncodeValueNull

该对象无字段。

EncodeValueBoolean

字段名类型说明
valueBoolean布尔值

枚举相关

AnnotationEncodeValueType

字段名类型说明
ByteValueEnum字节类型
ShortValueEnum短整型类型
CharValueEnum字符类型
IntValueEnum整型类型
LongValueEnum长整型类型
FloatValueEnum浮点类型
DoubleValueEnum双精度浮点类型
StringValueEnum字符串类型
TypeValueEnum类型类型
MethodValueEnum方法类型
EnumValueEnum枚举类型
ArrayValueEnum数组类型
AnnotationValueEnum注解类型
NullValueEnum空类型
BoolValueEnum布尔类型

AnnotationVisibilityType

',82),g={href:"https://source.android.com/docs/core/runtime/dex-format#visibility",target:"_blank",rel:"noopener noreferrer"},f=l('
字段名类型说明
BuildEnum构建时可见
RuntimeEnum运行时可见
SystemEnum系统可见

MatchType

字段名类型说明
ContainsEnum包含
EqualsEnum等于

NumberEncodeValueType

字段名类型说明
ByteValueEnum字节类型
ShortValueEnum短整型类型
CharValueEnum字符类型
IntValueEnum整型类型
LongValueEnum长整型类型
FloatValueEnum浮点类型
DoubleValueEnum双精度浮点类型

OpCodeMatchType

字段名类型说明
ContainsEnum包含
StartsWithEnum以...开头
EndsWithEnum以...结尾
EqualsEnum等于

RetentionPolicyType

该 Enum 与 java.lang.annotation.RetentionPolicy 保持对应。

字段名类型说明
SourceEnum源码级别
ClassEnum类级别
RuntimeEnum运行时级别

StringMatchType

字段名类型说明
ContainsEnum包含
StartsWithEnum以...开头
EndsWithEnum以...结尾
SimilarRegexEnum类正则匹配,只支持 ^$
EqualsEnum等于

TargetElementType

该 Enum 与 java.lang.annotation.ElementType 保持对应。

字段名类型说明
TypeEnum类型
FieldEnum字段
MethodEnum方法
ParameterEnum参数
ConstructorEnum构造方法
LocalVariableEnum局部变量
AnnotationTypeEnum注解类型
PackageEnum
TypeParameterEnum类型参数
TypeUseEnum类型使用

UsingType

字段名类型说明
AnyEnum任意
GetEnum获取
SetEnum设置
',17);function x(o,c){const a=n("ExternalLinkIcon");return i(),r("div",null,[y,t("p",null,[e("详情参照 "),t("a",g,[e("Visibility values"),s(a)])]),f])}const m=d(h,[["render",x],["__file","structural-zoom-table.html.vue"]]);export{m as default}; diff --git a/assets/structural-zoom-table.html-60176671.js b/assets/structural-zoom-table.html-60176671.js new file mode 100644 index 00000000..2050b8c9 --- /dev/null +++ b/assets/structural-zoom-table.html-60176671.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-6fa10115","path":"/en/guide/structural-zoom-table.html","title":"Quick Reference Guide","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Query Related","slug":"query-related","link":"#query-related","children":[{"level":3,"title":"FindClass","slug":"findclass","link":"#findclass","children":[]},{"level":3,"title":"FindField","slug":"findfield","link":"#findfield","children":[]},{"level":3,"title":"FindMethod","slug":"findmethod","link":"#findmethod","children":[]},{"level":3,"title":"BatchFindClassUsingStrings","slug":"batchfindclassusingstrings","link":"#batchfindclassusingstrings","children":[]},{"level":3,"title":"BatchFindMethodUsingStrings","slug":"batchfindmethodusingstrings","link":"#batchfindmethodusingstrings","children":[]}]},{"level":2,"title":"Matcher Related","slug":"matcher-related","link":"#matcher-related","children":[{"level":3,"title":"AccessFlagsMatcher","slug":"accessflagsmatcher","link":"#accessflagsmatcher","children":[]},{"level":3,"title":"AnnotationEncodeValueMatcher","slug":"annotationencodevaluematcher","link":"#annotationencodevaluematcher","children":[]},{"level":3,"title":"IntRange","slug":"intrange","link":"#intrange","children":[]},{"level":3,"title":"NumberEncodeValueMatcher","slug":"numberencodevaluematcher","link":"#numberencodevaluematcher","children":[]},{"level":3,"title":"OpcodesMatcher","slug":"opcodesmatcher","link":"#opcodesmatcher","children":[]},{"level":3,"title":"StringMatcher","slug":"stringmatcher","link":"#stringmatcher","children":[]},{"level":3,"title":"TargetElementTypesMatcher","slug":"targetelementtypesmatcher","link":"#targetelementtypesmatcher","children":[]},{"level":3,"title":"AnnotationElementMatcher","slug":"annotationelementmatcher","link":"#annotationelementmatcher","children":[]},{"level":3,"title":"AnnotationElementsMatcher","slug":"annotationelementsmatcher","link":"#annotationelementsmatcher","children":[]},{"level":3,"title":"AnnotationEncodeArrayMatcher","slug":"annotationencodearraymatcher","link":"#annotationencodearraymatcher","children":[]},{"level":3,"title":"AnnotationMatcher","slug":"annotationmatcher","link":"#annotationmatcher","children":[]},{"level":3,"title":"AnnotationsMatcher","slug":"annotationsmatcher","link":"#annotationsmatcher","children":[]},{"level":3,"title":"ClassMatcher","slug":"classmatcher","link":"#classmatcher","children":[]},{"level":3,"title":"InterfacesMatcher","slug":"interfacesmatcher","link":"#interfacesmatcher","children":[]},{"level":3,"title":"FieldMatcher","slug":"fieldmatcher","link":"#fieldmatcher","children":[]},{"level":3,"title":"FieldsMatcher","slug":"fieldsmatcher","link":"#fieldsmatcher","children":[]},{"level":3,"title":"MethodMatcher","slug":"methodmatcher","link":"#methodmatcher","children":[]},{"level":3,"title":"MethodsMatcher","slug":"methodsmatcher","link":"#methodsmatcher","children":[]},{"level":3,"title":"ParameterMatcher","slug":"parametermatcher","link":"#parametermatcher","children":[]},{"level":3,"title":"ParametersMatcher","slug":"parametersmatcher","link":"#parametersmatcher","children":[]},{"level":3,"title":"StringMatchersGroup","slug":"stringmatchersgroup","link":"#stringmatchersgroup","children":[]},{"level":3,"title":"UsingFieldMatcher","slug":"usingfieldmatcher","link":"#usingfieldmatcher","children":[]},{"level":3,"title":"EncodeValueByte","slug":"encodevaluebyte","link":"#encodevaluebyte","children":[]},{"level":3,"title":"EncodeValueShort","slug":"encodevalueshort","link":"#encodevalueshort","children":[]},{"level":3,"title":"EncodeValueChar","slug":"encodevaluechar","link":"#encodevaluechar","children":[]},{"level":3,"title":"EncodeValueInt","slug":"encodevalueint","link":"#encodevalueint","children":[]},{"level":3,"title":"EncodeValueLong","slug":"encodevaluelong","link":"#encodevaluelong","children":[]},{"level":3,"title":"EncodeValueFloat","slug":"encodevaluefloat","link":"#encodevaluefloat","children":[]},{"level":3,"title":"EncodeValueDouble","slug":"encodevaluedouble","link":"#encodevaluedouble","children":[]},{"level":3,"title":"EncodeValueString","slug":"encodevaluestring","link":"#encodevaluestring","children":[]},{"level":3,"title":"EncodeValueNull","slug":"encodevaluenull","link":"#encodevaluenull","children":[]},{"level":3,"title":"EncodeValueBoolean","slug":"encodevalueboolean","link":"#encodevalueboolean","children":[]}]},{"level":2,"title":"Enumerations","slug":"enumerations","link":"#enumerations","children":[{"level":3,"title":"AnnotationEncodeValueType","slug":"annotationencodevaluetype","link":"#annotationencodevaluetype","children":[]},{"level":3,"title":"AnnotationVisibilityType","slug":"annotationvisibilitytype","link":"#annotationvisibilitytype","children":[]},{"level":3,"title":"MatchType","slug":"matchtype","link":"#matchtype","children":[]},{"level":3,"title":"NumberEncodeValueType","slug":"numberencodevaluetype","link":"#numberencodevaluetype","children":[]},{"level":3,"title":"OpCodeMatchType","slug":"opcodematchtype","link":"#opcodematchtype","children":[]},{"level":3,"title":"RetentionPolicyType","slug":"retentionpolicytype","link":"#retentionpolicytype","children":[]},{"level":3,"title":"StringMatchType","slug":"stringmatchtype","link":"#stringmatchtype","children":[]},{"level":3,"title":"TargetElementType","slug":"targetelementtype","link":"#targetelementtype","children":[]},{"level":3,"title":"UsingType","slug":"usingtype","link":"#usingtype","children":[]}]}],"git":{"updatedTime":1718095560000,"contributors":[{"name":"teble","email":"me@teble.me","commits":1}]},"filePathRelative":"en/guide/structural-zoom-table.md"}');export{e as data}; diff --git a/assets/structural-zoom-table.html-9be6c1fa.js b/assets/structural-zoom-table.html-9be6c1fa.js new file mode 100644 index 00000000..753c6a03 --- /dev/null +++ b/assets/structural-zoom-table.html-9be6c1fa.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-aedd6f74","path":"/zh-cn/guide/structural-zoom-table.html","title":"结构速查表","lang":"zh-CN","frontmatter":{},"headers":[{"level":2,"title":"查询相关","slug":"查询相关","link":"#查询相关","children":[{"level":3,"title":"FindClass","slug":"findclass","link":"#findclass","children":[]},{"level":3,"title":"FindField","slug":"findfield","link":"#findfield","children":[]},{"level":3,"title":"FindMethod","slug":"findmethod","link":"#findmethod","children":[]},{"level":3,"title":"BatchFindClassUsingStrings","slug":"batchfindclassusingstrings","link":"#batchfindclassusingstrings","children":[]},{"level":3,"title":"BatchFindMethodUsingStrings","slug":"batchfindmethodusingstrings","link":"#batchfindmethodusingstrings","children":[]}]},{"level":2,"title":"匹配器相关","slug":"匹配器相关","link":"#匹配器相关","children":[{"level":3,"title":"AccessFlagsMatcher","slug":"accessflagsmatcher","link":"#accessflagsmatcher","children":[]},{"level":3,"title":"AnnotationEncodeValueMatcher","slug":"annotationencodevaluematcher","link":"#annotationencodevaluematcher","children":[]},{"level":3,"title":"IntRange","slug":"intrange","link":"#intrange","children":[]},{"level":3,"title":"NumberEncodeValueMatcher","slug":"numberencodevaluematcher","link":"#numberencodevaluematcher","children":[]},{"level":3,"title":"OpcodesMatcher","slug":"opcodesmatcher","link":"#opcodesmatcher","children":[]},{"level":3,"title":"StringMatcher","slug":"stringmatcher","link":"#stringmatcher","children":[]},{"level":3,"title":"TargetElementTypesMatcher","slug":"targetelementtypesmatcher","link":"#targetelementtypesmatcher","children":[]},{"level":3,"title":"AnnotationElementMatcher","slug":"annotationelementmatcher","link":"#annotationelementmatcher","children":[]},{"level":3,"title":"AnnotationElementsMatcher","slug":"annotationelementsmatcher","link":"#annotationelementsmatcher","children":[]},{"level":3,"title":"AnnotationEncodeArrayMatcher","slug":"annotationencodearraymatcher","link":"#annotationencodearraymatcher","children":[]},{"level":3,"title":"AnnotationMatcher","slug":"annotationmatcher","link":"#annotationmatcher","children":[]},{"level":3,"title":"AnnotationsMatcher","slug":"annotationsmatcher","link":"#annotationsmatcher","children":[]},{"level":3,"title":"ClassMatcher","slug":"classmatcher","link":"#classmatcher","children":[]},{"level":3,"title":"InterfacesMatcher","slug":"interfacesmatcher","link":"#interfacesmatcher","children":[]},{"level":3,"title":"FieldMatcher","slug":"fieldmatcher","link":"#fieldmatcher","children":[]},{"level":3,"title":"FieldsMatcher","slug":"fieldsmatcher","link":"#fieldsmatcher","children":[]},{"level":3,"title":"MethodMatcher","slug":"methodmatcher","link":"#methodmatcher","children":[]},{"level":3,"title":"MethodsMatcher","slug":"methodsmatcher","link":"#methodsmatcher","children":[]},{"level":3,"title":"ParameterMatcher","slug":"parametermatcher","link":"#parametermatcher","children":[]},{"level":3,"title":"ParametersMatcher","slug":"parametersmatcher","link":"#parametersmatcher","children":[]},{"level":3,"title":"StringMatchersGroup","slug":"stringmatchersgroup","link":"#stringmatchersgroup","children":[]},{"level":3,"title":"UsingFieldMatcher","slug":"usingfieldmatcher","link":"#usingfieldmatcher","children":[]},{"level":3,"title":"EncodeValueByte","slug":"encodevaluebyte","link":"#encodevaluebyte","children":[]},{"level":3,"title":"EncodeValueShort","slug":"encodevalueshort","link":"#encodevalueshort","children":[]},{"level":3,"title":"EncodeValueChar","slug":"encodevaluechar","link":"#encodevaluechar","children":[]},{"level":3,"title":"EncodeValueInt","slug":"encodevalueint","link":"#encodevalueint","children":[]},{"level":3,"title":"EncodeValueLong","slug":"encodevaluelong","link":"#encodevaluelong","children":[]},{"level":3,"title":"EncodeValueFloat","slug":"encodevaluefloat","link":"#encodevaluefloat","children":[]},{"level":3,"title":"EncodeValueDouble","slug":"encodevaluedouble","link":"#encodevaluedouble","children":[]},{"level":3,"title":"EncodeValueString","slug":"encodevaluestring","link":"#encodevaluestring","children":[]},{"level":3,"title":"EncodeValueNull","slug":"encodevaluenull","link":"#encodevaluenull","children":[]},{"level":3,"title":"EncodeValueBoolean","slug":"encodevalueboolean","link":"#encodevalueboolean","children":[]}]},{"level":2,"title":"枚举相关","slug":"枚举相关","link":"#枚举相关","children":[{"level":3,"title":"AnnotationEncodeValueType","slug":"annotationencodevaluetype","link":"#annotationencodevaluetype","children":[]},{"level":3,"title":"AnnotationVisibilityType","slug":"annotationvisibilitytype","link":"#annotationvisibilitytype","children":[]},{"level":3,"title":"MatchType","slug":"matchtype","link":"#matchtype","children":[]},{"level":3,"title":"NumberEncodeValueType","slug":"numberencodevaluetype","link":"#numberencodevaluetype","children":[]},{"level":3,"title":"OpCodeMatchType","slug":"opcodematchtype","link":"#opcodematchtype","children":[]},{"level":3,"title":"RetentionPolicyType","slug":"retentionpolicytype","link":"#retentionpolicytype","children":[]},{"level":3,"title":"StringMatchType","slug":"stringmatchtype","link":"#stringmatchtype","children":[]},{"level":3,"title":"TargetElementType","slug":"targetelementtype","link":"#targetelementtype","children":[]},{"level":3,"title":"UsingType","slug":"usingtype","link":"#usingtype","children":[]}]}],"git":{"updatedTime":1718095560000,"contributors":[{"name":"teble","email":"me@teble.me","commits":1}]},"filePathRelative":"zh-cn/guide/structural-zoom-table.md"}');export{e as data}; diff --git a/assets/structural-zoom-table.html-ab94571a.js b/assets/structural-zoom-table.html-ab94571a.js new file mode 100644 index 00000000..05f67fdc --- /dev/null +++ b/assets/structural-zoom-table.html-ab94571a.js @@ -0,0 +1 @@ +import{_ as d,r as n,o as i,c as s,d as t,e,a as r,b as l}from"./app-75e8845f.js";const h={},f=l('

Quick Reference Guide

This section introduces the structure components of DexKit queries. Matcher objects can be recursively nested, and all conditions are optional.

FindClass

Field NameTypeDescription
searchPackagesCollection<String>List of package names to search
excludePackagesCollection<String>List of excluded package names
ignorePackagesCaseBooleanWhether to ignore package name case
searchInCollection<ClassData>Search for classes in the specified list of classes
findFirstBooleanReturn the first result found immediately; results are not guaranteed to be unique due to multi-threading
matcherClassMatcherMatching conditions

FindField

Field NameTypeDescription
searchPackagesCollection<String>List of package names to search
excludePackagesCollection<String>List of excluded package names
ignorePackagesCaseBooleanWhether to ignore package name case
searchInClassesCollection<ClassData>Search for fields in the specified list of classes
searchInFieldsCollection<FieldData>Search for fields in the specified list of fields
findFirstBooleanReturn the first result found immediately; results are not guaranteed to be unique due to multi-threading
matcherFieldMatcherMatching conditions

FindMethod

Field NameTypeDescription
searchPackagesCollection<String>List of package names to search
excludePackagesCollection<String>List of excluded package names
ignorePackagesCaseBooleanWhether to ignore package name case
searchInClassesCollection<ClassData>Search for methods in the specified list of classes
searchInMethodsCollection<MethodData>Search for methods in the specified list of methods
findFirstBooleanReturn the first result found immediately; results are not guaranteed to be unique due to multi-threading
matcherMethodMatcherMatching conditions

BatchFindClassUsingStrings

Field NameTypeDescription
searchPackagesCollection<String>List of package names to search
excludePackagesCollection<String>List of excluded package names
ignorePackagesCaseBooleanWhether to ignore package name case
searchInCollection<ClassData>Search for classes in the specified list of classes
matchersCollection<StringMatchersGroup>List of query groups using strings

BatchFindMethodUsingStrings

Field NameTypeDescription
searchPackagesCollection<String>List of package names to search
excludePackagesCollection<String>List of excluded package names
ignorePackagesCaseBooleanWhether to ignore package name case
searchInClassesCollection<ClassData>Search for methods in the specified list of classes
searchInMethodsCollection<MethodData>Search for methods in the specified list of methods
matchersCollection<StringMatchersGroup>List of query groups using strings

AccessFlagsMatcher

Field NameTypeDescription
modifiersIntBit masks for matching modifiers
matchTypeMatchTypeMatching mode

AnnotationEncodeValueMatcher

Field NameTypeDescription
byteValueByteMatched byte
shortValueShortMatched short integer
charValueCharMatched character
intValueIntMatched integer
longValueLongMatched long integer
floatValueFloatMatched float
doubleValueDoubleMatched double precision float
stringValueStringMatcherMatched string
methodValueMethodMatcherMatched method
enumValueFieldMatcherMatched enumeration
arrayValueAnnotationEncodeArrayMatcherMatched array
annotationValueAnnotationMatcherMatched annotation
nullValueNoneMatch null
boolValueBooleanMatched boolean

IntRange

Field NameTypeDescription
minIntMinimum value, default is 0
maxIntMaximum value, default is Int.MAX_VALUE

NumberEncodeValueMatcher

Field NameTypeDescription
byteValueByteMatched byte
shortValueShortMatched short integer
charValueCharMatched character
intValueIntMatched integer
longValueLongMatched long integer
floatValueFloatMatched float
doubleValueDoubleMatched double precision float

OpcodesMatcher

Field NameTypeDescription
opCodesCollection<Int>Matched Int values of opcodes
opNamesCollection<String>Matched names of opcodes (as per smali syntax)
matchTypeOpCodeMatchTypeMatching mode
sizeIntRangeTotal length range of the opcode

StringMatcher

Field NameTypeDescription
valueStringMatched string
matchTypeStringMatchTypeMatching mode
ignoreCaseBooleanIgnore case sensitivity

TargetElementTypesMatcher

Field NameTypeDescription
typesCollection<TargetElementType>List of matched annotation element types
matchTypeMatchTypeMatching mode

AnnotationElementMatcher

Field NameTypeDescription
nameStringMatcherMatched name
valueAnnotationEncodeValueMatcherMatched value

AnnotationElementsMatcher

Field NameTypeDescription
elementsCollection<AnnotationElementMatcher>List of matched annotation elements
matchTypeMatchTypeMatching mode
countIntRangeNumber of matched elements

AnnotationEncodeArrayMatcher

Field NameTypeDescription
valuesCollection<AnnotationEncodeValueMatcher>List of matched annotation elements
matchTypeMatchTypeMatching mode
countIntRangeNumber of matched elements

AnnotationMatcher

Field NameTypeDescription
typeClassMatcherMatched annotation type
targetElementTypesTargetElementTypesMatcherList of matched annotation element types
policyRetentionPolicyTypeMatched retention policy
elementsAnnotationElementsMatcherList of matched annotation elements
usingStringsCollection<StringMatcher>List of strings used in the annotation

AnnotationsMatcher

Field NameTypeDescription
annotationsCollection<AnnotationMatcher>List of matched annotations
matchTypeMatchTypeMatching mode
countIntRangeNumber of matched annotations

ClassMatcher

Field NameTypeDescription
sourceStringMatcherSource file name of the class, i.e., .source field in smali
classNameStringMatcherName of the class
modifiersAccessFlagsMatcherModifiers of the class
superClassClassMatcherSuperclass of the class
interfacesInterfacesMatcherList of interfaces implemented by the class
annotationsAnnotationsMatcherList of annotations for the class
fieldsFieldsMatcherList of fields in the class
methodsMethodsMatcherList of methods in the class
usingStringsCollection<StringMatcher>List of strings used in the class

InterfacesMatcher

Field NameTypeDescription
interfacesCollection<ClassMatcher>List of matched interfaces
matchTypeMatchTypeMatching mode
countIntRangeNumber of matched interfaces

FieldMatcher

Field NameTypeDescription
nameStringMatcherName of the field
modifiersAccessFlagsMatcherModifiers of the field
declaredClassClassMatcherDeclaring class of the field
typeClassMatcherType of the field
annotationsAnnotationsMatcherList of annotations for the field
readMethodsMethodsMatcherList of methods to read the field
writeMethodsMethodsMatcherList of methods to write the field

FieldsMatcher

Field NameTypeDescription
fieldsCollection<FieldMatcher>List of matched fields
matchTypeMatchTypeMatching mode
countIntRangeNumber of matched fields

MethodMatcher

Field NameTypeDescription
nameStringMatcherName of the method
modifiersAccessFlagsMatcherModifiers of the method
declaredClassClassMatcherDeclaring class of the method
returnTypeClassMatcherReturn type of the method
paramsParametersMatcherList of parameters
annotationsAnnotationsMatcherList of annotations for the method
opCodesOpcodesMatcherList of opcodes for the method
usingStringsCollection<StringMatcher>List of strings used in the method
usingFieldsCollection<UsingFieldMatcher>List of fields used in the method
usingNumbersCollection<Number>List of numbers used in the method
invokeMethodsMethodsMatcherList of methods invoked by the method
callerMethodsMethodsMatcherList of methods that call the method

MethodsMatcher

Field NameTypeDescription
methodsCollection<MethodMatcher>List of matched methods
matchTypeMatchTypeMatching mode
countIntRangeNumber of matched methods

ParameterMatcher

Field NameTypeDescription
typeClassMatcherType of the parameter
annotationsAnnotationsMatcherList of annotations for the parameter

ParametersMatcher

Field NameTypeDescription
paramsCollection<ParameterMatcher>List of matched parameters
countIntRangeNumber of matched parameters

StringMatchersGroup

Field NameTypeDescription
groupNameStringGroup name for the query
matchersCollection<StringMatcher>List of strings used in the query

UsingFieldMatcher

Field NameTypeDescription
fieldFieldMatcherMatched field
usingTypeUsingTypeType of usage

EncodeValueByte

Field NameTypeDescription
valueBytebyte value

EncodeValueShort

Field NameTypeDescription
valueShortshort integer value

EncodeValueChar

Field NameTypeDescription
valueCharcharacter value

EncodeValueInt

Field NameTypeDescription
valueIntinteger value

EncodeValueLong

Field NameTypeDescription
valueLonglong integer value

EncodeValueFloat

Field NameTypeDescription
valueFloatfloat value

EncodeValueDouble

Field NameTypeDescription
valueDoubledouble value

EncodeValueString

Field NameTypeDescription
valueStringstring value

EncodeValueNull

This object has no fields.

EncodeValueBoolean

Field NameTypeDescription
valueBooleanboolean value

Enumerations

AnnotationEncodeValueType

Field NameTypeDescription
ByteValueEnumByte type
ShortValueEnumShort integer type
CharValueEnumCharacter type
IntValueEnumInteger type
LongValueEnumLong integer type
FloatValueEnumFloat type
DoubleValueEnumDouble precision float type
StringValueEnumString type
TypeValueEnumType type
MethodValueEnumMethod type
EnumValueEnumEnum type
ArrayValueEnumArray type
AnnotationValueEnumAnnotation type
NullValueEnumNull type
BoolValueEnumBoolean type

AnnotationVisibilityType

',82),y={href:"https://source.android.com/docs/core/runtime/dex-format#visibility",target:"_blank",rel:"noopener noreferrer"},g=l('
Field NameTypeDescription
BuildEnumVisible at build time
RuntimeEnumVisible at runtime
SystemEnumVisible to the system

MatchType

Field NameTypeDescription
ContainsEnumContains
EqualsEnumEquals

NumberEncodeValueType

Field NameTypeDescription
ByteValueEnumByte type
ShortValueEnumShort integer type
CharValueEnumCharacter type
IntValueEnumInteger type
LongValueEnumLong integer type
FloatValueEnumFloat type
DoubleValueEnumDouble precision float type

OpCodeMatchType

Field NameTypeDescription
ContainsEnumContains
StartsWithEnumStarts with
EndsWithEnumEnds with
EqualsEnumEquals

RetentionPolicyType

This Enum corresponds to java.lang.annotation.RetentionPolicy.

Field NameTypeDescription
SourceEnumSource level
ClassEnumClass level
RuntimeEnumRuntime level

StringMatchType

Field NameTypeDescription
ContainsEnumContains
StartsWithEnumStarts with
EndsWithEnumEnds with
SimilarRegexEnumRegex-like pattern, supporting only ^ and $
EqualsEnumEquals

TargetElementType

This Enum corresponds to java.lang.annotation.ElementType.

Field NameTypeDescription
TypeEnumType
FieldEnumField
MethodEnumMethod
ParameterEnumParameter
ConstructorEnumConstructor
LocalVariableEnumLocal variable
AnnotationTypeEnumAnnotation type
PackageEnumPackage
TypeParameterEnumType parameter
TypeUseEnumType use

UsingType

Field NameTypeDescription
AnyEnumAny usage
GetEnumGet usage
SetEnumSet usage
',17);function o(c,x){const a=n("ExternalLinkIcon");return i(),s("div",null,[f,t("p",null,[e("Refer to "),t("a",y,[e("Visibility values"),r(a)])]),g])}const u=d(h,[["render",o],["__file","structural-zoom-table.html.vue"]]);export{u as default}; diff --git a/assets/style-0b913742.css b/assets/style-0b913742.css new file mode 100644 index 00000000..f02b5dfd --- /dev/null +++ b/assets/style-0b913742.css @@ -0,0 +1 @@ +:root{--back-to-top-z-index: 5;--back-to-top-color: #3eaf7c;--back-to-top-color-hover: #71cda3}.back-to-top{cursor:pointer;position:fixed;bottom:2rem;right:2.5rem;width:2rem;height:1.2rem;background-color:var(--back-to-top-color);-webkit-mask:url(/DexKit/assets/back-to-top-8efcbe56.svg) no-repeat;mask:url(/DexKit/assets/back-to-top-8efcbe56.svg) no-repeat;z-index:var(--back-to-top-z-index)}.back-to-top:hover{background-color:var(--back-to-top-color-hover)}@media (max-width: 959px){.back-to-top{display:none}}@media print{.back-to-top{display:none}}.back-to-top-enter-active,.back-to-top-leave-active{transition:opacity .3s}.back-to-top-enter-from,.back-to-top-leave-to{opacity:0}:root{--external-link-icon-color: #aaa}.external-link-icon{position:relative;display:inline-block;color:var(--external-link-icon-color);vertical-align:middle;top:-1px}@media print{.external-link-icon{display:none}}.external-link-icon-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0;-webkit-user-select:none;-moz-user-select:none;user-select:none}:root{--medium-zoom-z-index: 100;--medium-zoom-bg-color: #ffffff;--medium-zoom-opacity: 1}.medium-zoom-overlay{background-color:var(--medium-zoom-bg-color)!important;z-index:var(--medium-zoom-z-index)}.medium-zoom-overlay~img{z-index:calc(var(--medium-zoom-z-index) + 1)}.medium-zoom--opened .medium-zoom-overlay{opacity:var(--medium-zoom-opacity)}:root{--nprogress-color: #29d;--nprogress-z-index: 1031}#nprogress{pointer-events:none}#nprogress .bar{background:var(--nprogress-color);position:fixed;z-index:var(--nprogress-z-index);top:0;left:0;width:100%;height:2px}:root{--c-brand: #3eaf7c;--c-brand-light: #4abf8a;--c-bg: #ffffff;--c-bg-light: #f3f4f5;--c-bg-lighter: #eeeeee;--c-bg-dark: #ebebec;--c-bg-darker: #e6e6e6;--c-bg-navbar: var(--c-bg);--c-bg-sidebar: var(--c-bg);--c-bg-arrow: #cccccc;--c-text: #2c3e50;--c-text-accent: var(--c-brand);--c-text-light: #3a5169;--c-text-lighter: #4e6e8e;--c-text-lightest: #6a8bad;--c-text-quote: #999999;--c-border: #eaecef;--c-border-dark: #dfe2e5;--c-tip: #42b983;--c-tip-bg: var(--c-bg-light);--c-tip-title: var(--c-text);--c-tip-text: var(--c-text);--c-tip-text-accent: var(--c-text-accent);--c-warning: #ffc310;--c-warning-bg: #fffae3;--c-warning-bg-light: #fff3ba;--c-warning-bg-lighter: #fff0b0;--c-warning-border-dark: #f7dc91;--c-warning-details-bg: #fff5ca;--c-warning-title: #f1b300;--c-warning-text: #746000;--c-warning-text-accent: #edb100;--c-warning-text-light: #c1971c;--c-warning-text-quote: #ccab49;--c-danger: #f11e37;--c-danger-bg: #ffe0e0;--c-danger-bg-light: #ffcfde;--c-danger-bg-lighter: #ffc9c9;--c-danger-border-dark: #f1abab;--c-danger-details-bg: #ffd4d4;--c-danger-title: #ed1e2c;--c-danger-text: #660000;--c-danger-text-accent: #bd1a1a;--c-danger-text-light: #b5474d;--c-danger-text-quote: #c15b5b;--c-details-bg: #eeeeee;--c-badge-tip: var(--c-tip);--c-badge-warning: #ecc808;--c-badge-warning-text: var(--c-bg);--c-badge-danger: #dc2626;--c-badge-danger-text: var(--c-bg);--t-color: .3s ease;--t-transform: .3s ease;--code-bg-color: #282c34;--code-hl-bg-color: rgba(0, 0, 0, .66);--code-ln-color: #9e9e9e;--code-ln-wrapper-width: 3.5rem;--font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;--font-family-code: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;--navbar-height: 3.6rem;--navbar-padding-v: .7rem;--navbar-padding-h: 1.5rem;--sidebar-width: 20rem;--sidebar-width-mobile: calc(var(--sidebar-width) * .82);--content-width: 740px;--homepage-width: 960px}.back-to-top{--back-to-top-color: var(--c-brand);--back-to-top-color-hover: var(--c-brand-light)}.DocSearch{--docsearch-primary-color: var(--c-brand);--docsearch-text-color: var(--c-text);--docsearch-highlight-color: var(--c-brand);--docsearch-muted-color: var(--c-text-quote);--docsearch-container-background: rgba(9, 10, 17, .8);--docsearch-modal-background: var(--c-bg-light);--docsearch-searchbox-background: var(--c-bg-lighter);--docsearch-searchbox-focus-background: var(--c-bg);--docsearch-searchbox-shadow: inset 0 0 0 2px var(--c-brand);--docsearch-hit-color: var(--c-text-light);--docsearch-hit-active-color: var(--c-bg);--docsearch-hit-background: var(--c-bg);--docsearch-hit-shadow: 0 1px 3px 0 var(--c-border-dark);--docsearch-footer-background: var(--c-bg)}.external-link-icon{--external-link-icon-color: var(--c-text-quote)}.medium-zoom-overlay{--medium-zoom-bg-color: var(--c-bg)}#nprogress{--nprogress-color: var(--c-brand)}.pwa-popup{--pwa-popup-text-color: var(--c-text);--pwa-popup-bg-color: var(--c-bg);--pwa-popup-border-color: var(--c-brand);--pwa-popup-shadow: 0 4px 16px var(--c-brand);--pwa-popup-btn-text-color: var(--c-bg);--pwa-popup-btn-bg-color: var(--c-brand);--pwa-popup-btn-hover-bg-color: var(--c-brand-light)}.search-box{--search-bg-color: var(--c-bg);--search-accent-color: var(--c-brand);--search-text-color: var(--c-text);--search-border-color: var(--c-border);--search-item-text-color: var(--c-text-lighter);--search-item-focus-bg-color: var(--c-bg-light)}html.dark{--c-brand: #3aa675;--c-brand-light: #349469;--c-bg: #22272e;--c-bg-light: #2b313a;--c-bg-lighter: #262c34;--c-bg-dark: #343b44;--c-bg-darker: #37404c;--c-text: #adbac7;--c-text-light: #96a7b7;--c-text-lighter: #8b9eb0;--c-text-lightest: #8094a8;--c-border: #3e4c5a;--c-border-dark: #34404c;--c-tip: #318a62;--c-warning: #e0ad15;--c-warning-bg: #2d2f2d;--c-warning-bg-light: #423e2a;--c-warning-bg-lighter: #44442f;--c-warning-border-dark: #957c35;--c-warning-details-bg: #39392d;--c-warning-title: #fdca31;--c-warning-text: #d8d96d;--c-warning-text-accent: #ffbf00;--c-warning-text-light: #ddb84b;--c-warning-text-quote: #ccab49;--c-danger: #fc1e38;--c-danger-bg: #39232c;--c-danger-bg-light: #4b2b35;--c-danger-bg-lighter: #553040;--c-danger-border-dark: #a25151;--c-danger-details-bg: #482936;--c-danger-title: #fc2d3b;--c-danger-text: #ea9ca0;--c-danger-text-accent: #fd3636;--c-danger-text-light: #d9777c;--c-danger-text-quote: #d56b6b;--c-details-bg: #323843;--c-badge-warning: var(--c-warning);--c-badge-warning-text: #3c2e05;--c-badge-danger: var(--c-danger);--c-badge-danger-text: #401416;--code-hl-bg-color: #363b46}html.dark .DocSearch{--docsearch-logo-color: var(--c-text);--docsearch-modal-shadow: inset 1px 1px 0 0 #2c2e40, 0 3px 8px 0 #000309;--docsearch-key-shadow: inset 0 -2px 0 0 #282d55, inset 0 0 1px 1px #51577d, 0 2px 2px 0 rgba(3, 4, 9, .3);--docsearch-key-gradient: linear-gradient(-225deg, #444950, #1c1e21);--docsearch-footer-shadow: inset 0 1px 0 0 rgba(73, 76, 106, .5), 0 -4px 8px 0 rgba(0, 0, 0, .2)}html,body{padding:0;margin:0;background-color:var(--c-bg);transition:background-color var(--t-color)}html.dark{color-scheme:dark}html{font-size:16px}body{font-family:var(--font-family);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-size:1rem;color:var(--c-text)}a{font-weight:500;color:var(--c-text-accent);text-decoration:none;overflow-wrap:break-word}p a code{font-weight:400;color:var(--c-text-accent)}kbd{font-family:var(--font-family-code);color:var(--c-text);background:var(--c-bg-lighter);border:solid .15rem var(--c-border-dark);border-bottom:solid .25rem var(--c-border-dark);border-radius:.15rem;padding:0 .15em}code{font-family:var(--font-family-code);color:var(--c-text-lighter);padding:.25rem .5rem;margin:0;font-size:.85em;background-color:var(--c-bg-light);border-radius:3px;overflow-wrap:break-word;transition:background-color var(--t-color)}blockquote{font-size:1rem;color:var(--c-text-quote);border-left:.2rem solid var(--c-border-dark);margin:1rem 0;padding:.25rem 0 .25rem 1rem;overflow-wrap:break-word}blockquote>p{margin:0}ul,ol{padding-left:1.2em}strong{font-weight:600}h1,h2,h3,h4,h5,h6{font-weight:600;line-height:1.25;overflow-wrap:break-word}h1:focus-visible,h2:focus-visible,h3:focus-visible,h4:focus-visible,h5:focus-visible,h6:focus-visible{outline:none}h1:hover .header-anchor,h2:hover .header-anchor,h3:hover .header-anchor,h4:hover .header-anchor,h5:hover .header-anchor,h6:hover .header-anchor{opacity:1}h1{font-size:2.2rem}h2{font-size:1.65rem;padding-bottom:.3rem;border-bottom:1px solid var(--c-border);transition:border-color var(--t-color)}h3{font-size:1.35rem}h4{font-size:1.15rem}h5{font-size:1.05rem}h6{font-size:1rem}a.header-anchor{font-size:.85em;float:left;margin-left:-.87em;padding-right:.23em;margin-top:.125em;opacity:0;-webkit-user-select:none;-moz-user-select:none;user-select:none}@media print{a.header-anchor{display:none}}a.header-anchor:hover{text-decoration:none}a.header-anchor:focus-visible{opacity:1}@media print{a[href^="http://"]:after,a[href^="https://"]:after{content:" (" attr(href) ") "}}p,ul,ol{line-height:1.7;overflow-wrap:break-word}hr{border:0;border-top:1px solid var(--c-border)}table{border-collapse:collapse;margin:1rem 0;display:block;overflow-x:auto;transition:border-color var(--t-color)}tr{border-top:1px solid var(--c-border-dark);transition:border-color var(--t-color)}tr:nth-child(2n){background-color:var(--c-bg-light);transition:background-color var(--t-color)}tr:nth-child(2n) code{background-color:var(--c-bg-dark)}th,td{padding:.6em 1em;border:1px solid var(--c-border-dark);transition:border-color var(--t-color)}.arrow{display:inline-block;width:0;height:0}.arrow.up{border-left:4px solid transparent;border-right:4px solid transparent;border-bottom:6px solid var(--c-bg-arrow)}.arrow.down{border-left:4px solid transparent;border-right:4px solid transparent;border-top:6px solid var(--c-bg-arrow)}.arrow.right{border-top:4px solid transparent;border-bottom:4px solid transparent;border-left:6px solid var(--c-bg-arrow)}.arrow.left{border-top:4px solid transparent;border-bottom:4px solid transparent;border-right:6px solid var(--c-bg-arrow)}.badge{display:inline-block;font-size:14px;font-weight:600;height:18px;line-height:18px;border-radius:3px;padding:0 6px;color:var(--c-bg);vertical-align:top;transition:color var(--t-color),background-color var(--t-color)}.badge.tip{background-color:var(--c-badge-tip)}.badge.warning{background-color:var(--c-badge-warning);color:var(--c-badge-warning-text)}.badge.danger{background-color:var(--c-badge-danger);color:var(--c-badge-danger-text)}.badge+.badge{margin-left:5px}code[class*=language-],pre[class*=language-]{color:#ccc;background:none;font-family:var(--font-family-code);font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;hyphens:none}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#2d2d2d}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.comment,.token.block-comment,.token.prolog,.token.doctype,.token.cdata{color:#999}.token.punctuation{color:#ccc}.token.tag,.token.attr-name,.token.namespace,.token.deleted{color:#ec5975}.token.function-name{color:#6196cc}.token.boolean,.token.number,.token.function{color:#f08d49}.token.property,.token.class-name,.token.constant,.token.symbol{color:#f8c555}.token.selector,.token.important,.token.atrule,.token.keyword,.token.builtin{color:#cc99cd}.token.string,.token.char,.token.attr-value,.token.regex,.token.variable{color:#7ec699}.token.operator,.token.entity,.token.url{color:#67cdcc}.token.important,.token.bold{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.token.inserted{color:#3eaf7c}.theme-default-content pre,.theme-default-content pre[class*=language-]{line-height:1.375;padding:1.3rem 1.5rem;margin:.85rem 0;border-radius:6px;overflow:auto}.theme-default-content pre code,.theme-default-content pre[class*=language-] code{color:#fff;padding:0;background-color:transparent!important;border-radius:0;overflow-wrap:unset;-webkit-font-smoothing:auto;-moz-osx-font-smoothing:auto}.theme-default-content .line-number{font-family:var(--font-family-code)}div[class*=language-]{position:relative;background-color:var(--code-bg-color);border-radius:6px}div[class*=language-]:before{content:attr(data-ext);position:absolute;z-index:3;top:.8em;right:1em;font-size:.75rem;color:var(--code-ln-color)}div[class*=language-] pre,div[class*=language-] pre[class*=language-]{background:transparent!important;position:relative;z-index:1}div[class*=language-] .highlight-lines{-webkit-user-select:none;-moz-user-select:none;user-select:none;padding-top:1.3rem;position:absolute;top:0;left:0;width:100%;line-height:1.375}div[class*=language-] .highlight-lines .highlight-line{background-color:var(--code-hl-bg-color)}div[class*=language-]:not(.line-numbers-mode) .line-numbers{display:none}div[class*=language-].line-numbers-mode .highlight-lines .highlight-line{position:relative}div[class*=language-].line-numbers-mode .highlight-lines .highlight-line:before{content:" ";position:absolute;z-index:2;left:0;top:0;display:block;width:var(--code-ln-wrapper-width);height:100%}div[class*=language-].line-numbers-mode pre{margin-left:var(--code-ln-wrapper-width);padding-left:1rem;vertical-align:middle}div[class*=language-].line-numbers-mode .line-numbers{position:absolute;top:0;width:var(--code-ln-wrapper-width);text-align:center;color:var(--code-ln-color);padding-top:1.25rem;line-height:1.375;counter-reset:line-number}div[class*=language-].line-numbers-mode .line-numbers .line-number{position:relative;z-index:3;-webkit-user-select:none;-moz-user-select:none;user-select:none;height:1.375em}div[class*=language-].line-numbers-mode .line-numbers .line-number:before{counter-increment:line-number;content:counter(line-number);font-size:.85em}div[class*=language-].line-numbers-mode:after{content:"";position:absolute;top:0;left:0;width:var(--code-ln-wrapper-width);height:100%;border-radius:6px 0 0 6px;border-right:1px solid var(--code-hl-bg-color)}@media (max-width: 419px){.theme-default-content div[class*=language-]{margin:.85rem -1.5rem;border-radius:0}}.code-group__nav{margin-top:.85rem;margin-bottom:calc(-1.7rem - 6px);padding-bottom:calc(1.7rem - 6px);padding-left:10px;padding-top:10px;border-top-left-radius:6px;border-top-right-radius:6px;background-color:var(--code-bg-color)}.code-group__ul{margin:auto 0;padding-left:0;display:inline-flex;list-style:none}.code-group__nav-tab{border:0;padding:5px;cursor:pointer;background-color:transparent;font-size:.85em;line-height:1.4;color:#ffffffe6;font-weight:600}.code-group__nav-tab:focus{outline:none}.code-group__nav-tab:focus-visible{outline:1px solid rgba(255,255,255,.9)}.code-group__nav-tab-active{border-bottom:var(--c-brand) 1px solid}@media (max-width: 419px){.code-group__nav{margin-left:-1.5rem;margin-right:-1.5rem;border-radius:0}}.code-group-item{display:none}.code-group-item__active{display:block}.code-group-item>pre{background-color:orange}.custom-container{transition:color var(--t-color),border-color var(--t-color),background-color var(--t-color)}.custom-container .custom-container-title{font-weight:600}.custom-container .custom-container-title:not(:only-child){margin-bottom:-.4rem}.custom-container.tip,.custom-container.warning,.custom-container.danger{padding:.1rem 1.5rem;border-left-width:.5rem;border-left-style:solid;margin:1rem 0}.custom-container.tip{border-color:var(--c-tip);background-color:var(--c-tip-bg);color:var(--c-tip-text)}.custom-container.tip .custom-container-title{color:var(--c-tip-title)}.custom-container.tip a{color:var(--c-tip-text-accent)}.custom-container.tip code{background-color:var(--c-bg-dark)}.custom-container.warning{border-color:var(--c-warning);background-color:var(--c-warning-bg);color:var(--c-warning-text)}.custom-container.warning .custom-container-title{color:var(--c-warning-title)}.custom-container.warning a{color:var(--c-warning-text-accent)}.custom-container.warning blockquote{border-left-color:var(--c-warning-border-dark);color:var(--c-warning-text-quote)}.custom-container.warning code{color:var(--c-warning-text-light);background-color:var(--c-warning-bg-light)}.custom-container.warning details{background-color:var(--c-warning-details-bg)}.custom-container.warning details code{background-color:var(--c-warning-bg-lighter)}.custom-container.warning .external-link-icon{--external-link-icon-color: var(--c-warning-text-quote)}.custom-container.danger{border-color:var(--c-danger);background-color:var(--c-danger-bg);color:var(--c-danger-text)}.custom-container.danger .custom-container-title{color:var(--c-danger-title)}.custom-container.danger a{color:var(--c-danger-text-accent)}.custom-container.danger blockquote{border-left-color:var(--c-danger-border-dark);color:var(--c-danger-text-quote)}.custom-container.danger code{color:var(--c-danger-text-light);background-color:var(--c-danger-bg-light)}.custom-container.danger details{background-color:var(--c-danger-details-bg)}.custom-container.danger details code{background-color:var(--c-danger-bg-lighter)}.custom-container.danger .external-link-icon{--external-link-icon-color: var(--c-danger-text-quote)}.custom-container.details{display:block;position:relative;border-radius:2px;margin:1.6em 0;padding:1.6em;background-color:var(--c-details-bg)}.custom-container.details code{background-color:var(--c-bg-darker)}.custom-container.details h4{margin-top:0}.custom-container.details figure:last-child,.custom-container.details p:last-child{margin-bottom:0;padding-bottom:0}.custom-container.details summary{outline:none;cursor:pointer}.home{padding:var(--navbar-height) 2rem 0;max-width:var(--homepage-width);margin:0 auto;display:block}.home .hero{text-align:center}.home .hero img{max-width:100%;max-height:280px;display:block;margin:3rem auto 1.5rem}.home .hero h1{font-size:3rem}.home .hero h1,.home .hero .description,.home .hero .actions{margin:1.8rem auto}.home .hero .actions{display:flex;flex-wrap:wrap;gap:1rem;justify-content:center}.home .hero .description{max-width:35rem;font-size:1.6rem;line-height:1.3;color:var(--c-text-lightest)}.home .hero .action-button{display:inline-block;font-size:1.2rem;padding:.8rem 1.6rem;border-width:2px;border-style:solid;border-radius:4px;transition:background-color var(--t-color);box-sizing:border-box}.home .hero .action-button.primary{color:var(--c-bg);background-color:var(--c-brand);border-color:var(--c-brand)}.home .hero .action-button.primary:hover{background-color:var(--c-brand-light)}.home .hero .action-button.secondary{color:var(--c-brand);background-color:var(--c-bg);border-color:var(--c-brand)}.home .hero .action-button.secondary:hover{color:var(--c-bg);background-color:var(--c-brand-light)}.home .features{border-top:1px solid var(--c-border);transition:border-color var(--t-color);padding:1.2rem 0;margin-top:2.5rem;display:flex;flex-wrap:wrap;align-items:flex-start;align-content:stretch;justify-content:space-between}.home .feature{flex-grow:1;flex-basis:30%;max-width:30%}.home .feature h2{font-size:1.4rem;font-weight:500;border-bottom:none;padding-bottom:0;color:var(--c-text-light)}.home .feature p{color:var(--c-text-lighter)}.home .theme-default-content{padding:0;margin:0}.home .footer{padding:2.5rem;border-top:1px solid var(--c-border);text-align:center;color:var(--c-text-lighter);transition:border-color var(--t-color)}@media (max-width: 719px){.home .features{flex-direction:column}.home .feature{max-width:100%;padding:0 2.5rem}}@media (max-width: 419px){.home{padding-left:1.5rem;padding-right:1.5rem}.home .hero img{max-height:210px;margin:2rem auto 1.2rem}.home .hero h1{font-size:2rem}.home .hero h1,.home .hero .description,.home .hero .actions{margin:1.2rem auto}.home .hero .description{font-size:1.2rem}.home .hero .action-button{font-size:1rem;padding:.6rem 1.2rem}.home .feature h2{font-size:1.25rem}}.page{padding-top:var(--navbar-height);padding-left:var(--sidebar-width)}.navbar{position:fixed;z-index:20;top:0;left:0;right:0;height:var(--navbar-height);box-sizing:border-box;border-bottom:1px solid var(--c-border);background-color:var(--c-bg-navbar);transition:background-color var(--t-color),border-color var(--t-color)}.sidebar{font-size:16px;width:var(--sidebar-width);position:fixed;z-index:10;margin:0;top:var(--navbar-height);left:0;bottom:0;box-sizing:border-box;border-right:1px solid var(--c-border);overflow-y:auto;scrollbar-width:thin;scrollbar-color:var(--c-brand) var(--c-border);background-color:var(--c-bg-sidebar);transition:transform var(--t-transform),background-color var(--t-color),border-color var(--t-color)}.sidebar::-webkit-scrollbar{width:7px}.sidebar::-webkit-scrollbar-track{background-color:var(--c-border)}.sidebar::-webkit-scrollbar-thumb{background-color:var(--c-brand)}.sidebar-mask{position:fixed;z-index:9;top:0;left:0;width:100vw;height:100vh;display:none}.theme-container.sidebar-open .sidebar-mask{display:block}.theme-container.sidebar-open .navbar>.toggle-sidebar-button .icon span:nth-child(1){transform:rotate(45deg) translate3d(5.5px,5.5px,0)}.theme-container.sidebar-open .navbar>.toggle-sidebar-button .icon span:nth-child(2){transform:scale3d(0,1,1)}.theme-container.sidebar-open .navbar>.toggle-sidebar-button .icon span:nth-child(3){transform:rotate(-45deg) translate3d(6px,-6px,0)}.theme-container.sidebar-open .navbar>.toggle-sidebar-button .icon span:nth-child(1),.theme-container.sidebar-open .navbar>.toggle-sidebar-button .icon span:nth-child(3){transform-origin:center}.theme-container.no-navbar .theme-default-content h1,.theme-container.no-navbar .theme-default-content h2,.theme-container.no-navbar .theme-default-content h3,.theme-container.no-navbar .theme-default-content h4,.theme-container.no-navbar .theme-default-content h5,.theme-container.no-navbar .theme-default-content h6{margin-top:1.5rem;padding-top:0}.theme-container.no-navbar .page{padding-top:0}.theme-container.no-navbar .sidebar{top:0}.theme-container.no-sidebar .sidebar{display:none}@media (max-width: 719px){.theme-container.no-sidebar .sidebar{display:block}}.theme-container.no-sidebar .page{padding-left:0}.theme-default-content a:hover{text-decoration:underline}.theme-default-content img{max-width:100%}.theme-default-content h1,.theme-default-content h2,.theme-default-content h3,.theme-default-content h4,.theme-default-content h5,.theme-default-content h6{margin-top:calc(.5rem - var(--navbar-height));padding-top:calc(1rem + var(--navbar-height));margin-bottom:0}.theme-default-content h1:first-child,.theme-default-content h2:first-child,.theme-default-content h3:first-child,.theme-default-content h4:first-child,.theme-default-content h5:first-child,.theme-default-content h6:first-child{margin-bottom:1rem}.theme-default-content h1:first-child+p,.theme-default-content h1:first-child+pre,.theme-default-content h1:first-child+.custom-container,.theme-default-content h2:first-child+p,.theme-default-content h2:first-child+pre,.theme-default-content h2:first-child+.custom-container,.theme-default-content h3:first-child+p,.theme-default-content h3:first-child+pre,.theme-default-content h3:first-child+.custom-container,.theme-default-content h4:first-child+p,.theme-default-content h4:first-child+pre,.theme-default-content h4:first-child+.custom-container,.theme-default-content h5:first-child+p,.theme-default-content h5:first-child+pre,.theme-default-content h5:first-child+.custom-container,.theme-default-content h6:first-child+p,.theme-default-content h6:first-child+pre,.theme-default-content h6:first-child+.custom-container{margin-top:2rem}@media (max-width: 959px){.sidebar{font-size:15px;width:var(--sidebar-width-mobile)}.page{padding-left:var(--sidebar-width-mobile)}}@media (max-width: 719px){.sidebar{top:0;padding-top:var(--navbar-height);transform:translate(-100%)}.page{padding-left:0}.theme-container.sidebar-open .sidebar{transform:translate(0)}.theme-container.no-navbar .sidebar{padding-top:0}}@media (max-width: 419px){h1{font-size:1.9rem}}.navbar{--navbar-line-height: calc( var(--navbar-height) - 2 * var(--navbar-padding-v) );padding:var(--navbar-padding-v) var(--navbar-padding-h);line-height:var(--navbar-line-height)}.navbar .logo{height:var(--navbar-line-height);margin-right:var(--navbar-padding-v);vertical-align:top}.navbar .site-name{font-size:1.3rem;font-weight:600;color:var(--c-text);position:relative}.navbar .navbar-items-wrapper{display:flex;position:absolute;box-sizing:border-box;top:var(--navbar-padding-v);right:var(--navbar-padding-h);height:var(--navbar-line-height);padding-left:var(--navbar-padding-h);white-space:nowrap;font-size:.9rem}.navbar .navbar-items-wrapper .search-box{flex:0 0 auto;vertical-align:top}@media screen and (max-width: 719px){.navbar{padding-left:4rem}.navbar .site-name{display:block;width:calc(100vw - 11rem);overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.navbar .can-hide{display:none}}.navbar-items{display:inline-block}@media print{.navbar-items{display:none}}.navbar-items a{display:inline-block;line-height:1.4rem;color:inherit}.navbar-items a:hover,.navbar-items a.router-link-active{color:var(--c-text)}.navbar-items .navbar-item{position:relative;display:inline-block;margin-left:1.5rem;line-height:var(--navbar-line-height)}.navbar-items .navbar-item:first-child{margin-left:0}.navbar-items .navbar-item>a:hover,.navbar-items .navbar-item>a.router-link-active{margin-bottom:-2px;border-bottom:2px solid var(--c-text-accent)}@media (max-width: 719px){.navbar-items .navbar-item{margin-left:0}.navbar-items .navbar-item>a:hover,.navbar-items .navbar-item>a.router-link-active{margin-bottom:0;border-bottom:none}.navbar-items a:hover,.navbar-items a.router-link-active{color:var(--c-text-accent)}}.toggle-sidebar-button{position:absolute;top:.6rem;left:1rem;display:none;padding:.6rem;cursor:pointer}.toggle-sidebar-button .icon{display:flex;flex-direction:column;justify-content:center;align-items:center;width:1.25rem;height:1.25rem;cursor:inherit}.toggle-sidebar-button .icon span{display:inline-block;width:100%;height:2px;border-radius:2px;background-color:var(--c-text);transition:transform var(--t-transform)}.toggle-sidebar-button .icon span:nth-child(2){margin:6px 0}@media screen and (max-width: 719px){.toggle-sidebar-button{display:block}}.toggle-color-mode-button{display:flex;margin:auto;margin-left:1rem;border:0;background:none;color:var(--c-text);opacity:.8;cursor:pointer}@media print{.toggle-color-mode-button{display:none}}.toggle-color-mode-button:hover{opacity:1}.toggle-color-mode-button .icon{width:1.25rem;height:1.25rem}.DocSearch{transition:background-color var(--t-color)}.navbar-dropdown-wrapper{cursor:pointer}.navbar-dropdown-wrapper .navbar-dropdown-title,.navbar-dropdown-wrapper .navbar-dropdown-title-mobile{display:block;font-size:.9rem;font-family:inherit;cursor:inherit;padding:inherit;line-height:1.4rem;background:transparent;border:none;font-weight:500;color:var(--c-text)}.navbar-dropdown-wrapper .navbar-dropdown-title:hover,.navbar-dropdown-wrapper .navbar-dropdown-title-mobile:hover{border-color:transparent}.navbar-dropdown-wrapper .navbar-dropdown-title .arrow,.navbar-dropdown-wrapper .navbar-dropdown-title-mobile .arrow{vertical-align:middle;margin-top:-1px;margin-left:.4rem}.navbar-dropdown-wrapper .navbar-dropdown-title-mobile{display:none;font-weight:600;font-size:inherit}.navbar-dropdown-wrapper .navbar-dropdown-title-mobile:hover{color:var(--c-text-accent)}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item{color:inherit;line-height:1.7rem}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subtitle{margin:.45rem 0 0;border-top:1px solid var(--c-border);padding:1rem 0 .45rem;font-size:.9rem}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subtitle>span{padding:0 1.5rem 0 1.25rem}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subtitle>a{font-weight:inherit}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subtitle>a.router-link-active:after{display:none}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subitem-wrapper{padding:0;list-style:none}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subitem-wrapper .navbar-dropdown-subitem{font-size:.9em}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item a{display:block;line-height:1.7rem;position:relative;border-bottom:none;font-weight:400;margin-bottom:0;padding:0 1.5rem 0 1.25rem}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item a:hover,.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item a.router-link-active{color:var(--c-text-accent)}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item a.router-link-active:after{content:"";width:0;height:0;border-left:5px solid var(--c-text-accent);border-top:3px solid transparent;border-bottom:3px solid transparent;position:absolute;top:calc(50% - 2px);left:9px}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item:first-child .navbar-dropdown-subtitle{margin-top:0;padding-top:0;border-top:0}.navbar-dropdown-wrapper.mobile.open .navbar-dropdown-title,.navbar-dropdown-wrapper.mobile.open .navbar-dropdown-title-mobile{margin-bottom:.5rem}.navbar-dropdown-wrapper.mobile .navbar-dropdown-title,.navbar-dropdown-wrapper.mobile .navbar-dropdown-title-mobile{display:none}.navbar-dropdown-wrapper.mobile .navbar-dropdown-title-mobile{display:block}.navbar-dropdown-wrapper.mobile .navbar-dropdown{transition:height .1s ease-out;overflow:hidden}.navbar-dropdown-wrapper.mobile .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subtitle{border-top:0;margin-top:0;padding-top:0;padding-bottom:0}.navbar-dropdown-wrapper.mobile .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subtitle,.navbar-dropdown-wrapper.mobile .navbar-dropdown .navbar-dropdown-item>a{font-size:15px;line-height:2rem}.navbar-dropdown-wrapper.mobile .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subitem{font-size:14px;padding-left:1rem}.navbar-dropdown-wrapper:not(.mobile){height:1.8rem}.navbar-dropdown-wrapper:not(.mobile):hover .navbar-dropdown,.navbar-dropdown-wrapper:not(.mobile).open .navbar-dropdown{display:block!important}.navbar-dropdown-wrapper:not(.mobile).open:blur{display:none}.navbar-dropdown-wrapper:not(.mobile) .navbar-dropdown{display:none;height:auto!important;box-sizing:border-box;max-height:calc(100vh - 2.7rem);overflow-y:auto;position:absolute;top:100%;right:0;background-color:var(--c-bg-navbar);padding:.6rem 0;border:1px solid var(--c-border);border-bottom-color:var(--c-border-dark);text-align:left;border-radius:.25rem;white-space:nowrap;margin:0}.page{padding-bottom:2rem;display:block}.page .theme-default-content{max-width:var(--content-width);margin:0 auto;padding:2rem 2.5rem;padding-top:0}@media (max-width: 959px){.page .theme-default-content{padding:2rem}}@media (max-width: 419px){.page .theme-default-content{padding:1.5rem}}.page-meta{max-width:var(--content-width);margin:0 auto;padding:1rem 2.5rem;overflow:auto}@media (max-width: 959px){.page-meta{padding:2rem}}@media (max-width: 419px){.page-meta{padding:1.5rem}}.page-meta .meta-item{cursor:default;margin-top:.8rem}.page-meta .meta-item .meta-item-label{font-weight:500;color:var(--c-text-lighter)}.page-meta .meta-item .meta-item-info{font-weight:400;color:var(--c-text-quote)}.page-meta .edit-link{display:inline-block;margin-right:.25rem}@media print{.page-meta .edit-link{display:none}}.page-meta .last-updated{float:right}@media (max-width: 719px){.page-meta .last-updated{font-size:.8em;float:none}.page-meta .contributors{font-size:.8em}}.page-nav{max-width:var(--content-width);margin:0 auto;padding:1rem 2.5rem 2rem;padding-bottom:0}@media (max-width: 959px){.page-nav{padding:2rem}}@media (max-width: 419px){.page-nav{padding:1.5rem}}.page-nav .inner{min-height:2rem;margin-top:0;border-top:1px solid var(--c-border);transition:border-color var(--t-color);padding-top:1rem;overflow:auto}.page-nav .prev a:before{content:"←"}.page-nav .next{float:right}.page-nav .next a:after{content:"→"}.sidebar ul{padding:0;margin:0;list-style-type:none}.sidebar a{display:inline-block}.sidebar .navbar-items{display:none;border-bottom:1px solid var(--c-border);transition:border-color var(--t-color);padding:.5rem 0 .75rem}.sidebar .navbar-items a{font-weight:600}.sidebar .navbar-items .navbar-item{display:block;line-height:1.25rem;font-size:1.1em;padding:.5rem 0 .5rem 1.5rem}.sidebar .sidebar-items{padding:1.5rem 0}@media (max-width: 719px){.sidebar .navbar-items{display:block}.sidebar .navbar-items .navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item a.router-link-active:after{top:calc(1rem - 2px)}.sidebar .sidebar-items{padding:1rem 0}}.sidebar-item{cursor:default;border-left:.25rem solid transparent;color:var(--c-text)}.sidebar-item:focus-visible{outline-width:1px;outline-offset:-1px}.sidebar-item.active:not(p.sidebar-heading){font-weight:600;color:var(--c-text-accent);border-left-color:var(--c-text-accent)}.sidebar-item.sidebar-heading{transition:color .15s ease;font-size:1.1em;font-weight:700;padding:.35rem 1.5rem .35rem 1.25rem;width:100%;box-sizing:border-box;margin:0}.sidebar-item.sidebar-heading+.sidebar-item-children{transition:height .1s ease-out;overflow:hidden;margin-bottom:.75rem}.sidebar-item.collapsible{cursor:pointer}.sidebar-item.collapsible .arrow{position:relative;top:-.12em;left:.5em}.sidebar-item:not(.sidebar-heading){font-size:1em;font-weight:400;display:inline-block;margin:0;padding:.35rem 1rem .35rem 2rem;line-height:1.4;width:100%;box-sizing:border-box}.sidebar-item:not(.sidebar-heading)+.sidebar-item-children{padding-left:1rem;font-size:.95em}.sidebar-item-children .sidebar-item-children .sidebar-item:not(.sidebar-heading){padding:.25rem 1rem .25rem 1.75rem}.sidebar-item-children .sidebar-item-children .sidebar-item:not(.sidebar-heading).active{font-weight:500;border-left-color:transparent}a.sidebar-heading+.sidebar-item-children .sidebar-item:not(.sidebar-heading).active{border-left-color:transparent}a.sidebar-item{cursor:pointer}a.sidebar-item:hover{color:var(--c-text-accent)}.table-of-contents .badge{vertical-align:middle}.dropdown-enter-from,.dropdown-leave-to{height:0!important}.fade-slide-y-enter-active{transition:all .2s ease}.fade-slide-y-leave-active{transition:all .2s cubic-bezier(1,.5,.8,1)}.fade-slide-y-enter-from,.fade-slide-y-leave-to{transform:translateY(10px);opacity:0} diff --git a/en/about/about.html b/en/about/about.html new file mode 100644 index 00000000..0c14dcdb --- /dev/null +++ b/en/about/about.html @@ -0,0 +1,33 @@ + + + + + + + + + About This Documentation | DexKit + + + + + + + + diff --git a/en/about/contacts.html b/en/about/contacts.html new file mode 100644 index 00000000..53d7a7ab --- /dev/null +++ b/en/about/contacts.html @@ -0,0 +1,33 @@ + + + + + + + + + Contact Us | DexKit + + + + + + + + diff --git a/en/guide/example.html b/en/guide/example.html new file mode 100644 index 00000000..f99bfb85 --- /dev/null +++ b/en/guide/example.html @@ -0,0 +1,246 @@ + + + + + + + + + Usage Examples | DexKit + + + + +

Usage Examples

During the reading of this section, you may need to refer to the Structural Quick Reference Table for a better understanding.

You can get the source code and some test cases for the demo below.

Demo App

Here is a simple Demo Activity, PlayActivity, where the internal properties and methods are obfuscated, and they change in each version.

The four major components are not obfuscated by default. We assume this Activity is obfuscated.

package org.luckypray.dexkit.demo;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.h;
+import java.util.Random;
+import org.luckypray.dexkit.demo.annotations.Router;
+
+@Router(path = "/play")
+public class PlayActivity extends AppCompatActivity {
+    private static final String TAG = "PlayActivity";
+    private TextView a;
+    private Handler b;
+
+    public void d(View view) {
+        Handler handler;
+        int i;
+        Log.d("PlayActivity", "onClick: rollButton");
+        float nextFloat = new Random().nextFloat();
+        if (nextFloat < 0.01d) {
+            handler = this.b;
+            i = -1;
+        } else if (nextFloat < 0.987f) {
+            handler = this.b;
+            i = 0;
+        } else {
+            handler = this.b;
+            i = 114514;
+        }
+        handler.sendEmptyMessage(i);
+    }
+
+    public void e(boolean z) {
+        int i;
+        if (!z) {
+            i = RandomUtil.a();
+        } else {
+            i = 6;
+        }
+        String a = h.a("You rolled a ", i);
+        this.a.setText(a);
+        Log.d("PlayActivity", "rollDice: " + a);
+    }
+
+    protected void onCreate(Bundle bundle) {
+        super/*androidx.fragment.app.FragmentActivity*/.onCreate(bundle);
+        setContentView(0x7f0b001d);
+        Log.d("PlayActivity", "onCreate");
+        HandlerThread handlerThread = new HandlerThread("PlayActivity");
+        handlerThread.start();
+        this.b = new PlayActivity$1(this, handlerThread.getLooper());
+        this.a = (TextView) findViewById(0x7f080134);
+        ((Button) findViewById(0x7f08013a)).setOnClickListener(new a(this));
+    }
+}
+

Multiple Conditions Matching Class - Example Usage

In this scenario, we want to find the PlayActivity using the following code:

This is just an example. In actual use, you don't need conditions as complex and comprehensive as this. Use as needed.

private fun findPlayActivity(bridge: DexKitBridge) {
+    val classData = bridge.findClass {
+        // Search within the specified package name range
+        searchPackages("org.luckypray.dexkit.demo")
+        // Exclude the specified package name range
+        excludePackages("org.luckypray.dexkit.demo.annotations")
+        // ClassMatcher Matcher for classes
+        matcher {
+            // FieldsMatcher Matcher for fields in a class
+            fields {
+                // Add a matcher for the field
+                add {
+                    // Specify the modifiers of the field
+                    modifiers = Modifier.PRIVATE or Modifier.STATIC or Modifier.FINAL
+                    // Specify the type of the field
+                    type = "java.lang.String"
+                    // Specify the name of the field
+                    name = "TAG"
+                }
+                // Add a matcher for the field of the specified type
+                addForType("android.widget.TextView")
+                addForType("android.os.Handler")
+                // Specify the number of fields in the class
+                count = 3
+            }
+            // MethodsMatcher Matcher for methods in a class
+            methods {
+                // Add a matcher for the method
+                add {
+                    // Specify the modifiers of the method
+                    modifiers = Modifier.PROTECTED
+                    // Specify the name of the method
+                    name = "onCreate"
+                    // Specify the return type of the method
+                    returnType = "void"
+                    // Specify the parameter types of the method, if the parameter types are uncertain,
+                    // use null, and this method will implicitly declare the number of parameters
+                    paramTypes("android.os.Bundle")
+                    // Specify the strings used in the method
+                    usingStrings("onCreate")
+                }
+                add {
+                    paramTypes("android.view.View")
+                    // Specify the numbers used in the method, the type is Byte, Short, Int, Long, Float, Double
+                    usingNumbers(0.01, -1, 0.987, 0, 114514)
+                }
+                add {
+                    paramTypes("boolean")
+                    // Specify the methods called in the method list
+                    invokeMethods {
+                        add {
+                            modifiers = Modifier.PUBLIC or Modifier.STATIC
+                            returnType = "int"
+                            // Specify the strings used in the method called in the method,
+                            usingStrings(listOf("getRandomDice: "), StringMatchType.Equals)
+                        }
+                        // Only need to contain the call to the above method
+                        matchType = MatchType.Contains
+                    }
+                }
+                count(1..10)
+            }
+            // AnnotationsMatcher Matcher for annotations in a class
+            annotations {
+                // Add a matcher for the annotation
+                add {
+                    // Specify the type of the annotation
+                    type = "org.luckypray.dexkit.demo.annotations.Router"
+                    // The annotation needs to contain the specified element
+                    addElement {
+                        // Specify the name of the element
+                        name = "path"
+                        // Specify the value of the element
+                        stringValue("/play")
+                    }
+                }
+            }
+            // Strings used by all methods in the class
+            usingStrings("PlayActivity", "onClick", "onCreate")
+        }
+    }.single()
+    println(classData.name)
+}
+

The result is as follows:

org.luckypray.dexkit.demo.PlayActivity
+

Parent Class Condition Nesting

if there is such a class, its only feature is that the ancestors are not obfuscated, and the middle parents are all obfuscated, we can also use DexKit to find it.

private fun findActivity(bridge: DexKitBridge) {
+    bridge.findClass {
+        matcher { // ClassMatcher
+            // androidx.appcompat.app.AppCompatActivity
+            superClass { // ClassMatcher
+                // androidx.fragment.app.FragmentActivity
+                superClass { // ClassMatcher
+                    // androidx.activity.ComponentActivity
+                    superClass { // ClassMatcher
+                        // androidx.core.app.ComponentActivity
+                        superClass { // ClassMatcher
+                            superClass = "android.app.Activity"
+                        }
+                    }
+                }
+            }
+        }
+    }.forEach {
+        // org.luckypray.dexkit.demo.MainActivity
+        // org.luckypray.dexkit.demo.PlayActivity
+        println(it.name)
+    }
+}
+

The result is as follows:

org.luckypray.dexkit.demo.MainActivity
+org.luckypray.dexkit.demo.PlayActivity
+

Tips

In DexKit, any logical relationship can be used as a query condition

Fuzzy Parameter Matching

If we need to find a method with an obfuscated parameter, we can use null to replace it, so that it can match any type of parameter.

private fun findMethodWithFuzzyParam(bridge: DexKitBridge) {
+    bridge.findMethod {
+        matcher {
+            modifiers = Modifier.PUBLIC or Modifier.STATIC
+            returnType = "void"
+            // Specify the parameters of the method, if the parameters are uncertain, use null
+            paramTypes("android.view.View", null)
+            // paramCount = 2 // paramTypes length is 2, which has implicitly determined the number of parameters
+            usingStrings("onClick")
+        }
+    }.single().let {
+        println(it)
+    }
+}
+

Saving and Retrieving Query Results

How can you serialize and save the results obtained from DexKit queries for later use?

DexKit provides corresponding wrapper classes for Class, Method, and Field, namely DexClass, DexMethod, and DexField. These wrapper classes inherit from the ISerializable interface, allowing them to be freely converted to and from strings. For objects returned by queries, you can directly use the toDexClass(), toDexMethod(), and toDexField() methods to convert them into the respective wrapper classes.

private fun saveData(bridge: DexKitBridge) {
+    bridge.findMethod {
+        matcher {
+            modifiers = Modifier.PUBLIC or Modifier.STATIC
+            returnType = "void"
+            paramTypes("android.view.View", null)
+            usingStrings("onClick")
+        }
+    }.single().let {
+        val dexMethod = it.toDexMethod()
+        val serialize = dexMethod.serialize()
+        val sp = getSharedPreferences("dexkit", Context.MODE_PRIVATE)
+        sp.edit().putString("onClickMethod", serialize).apply()
+    }
+}
+
+private fun readData(): Method {
+    val sp = getSharedPreferences("dexkit", Context.MODE_PRIVATE)
+    val descriptor = sp.getString("onClickMethod", null)
+    if (descriptor != null) {
+        val dexMethod = DexMethod(descriptor)
+        // val dexMethod = DexMethod.deserialize(serialize)
+        // val dexMethod = ISerializable.deserialize(serialize) as DexMethod
+        // val dexMethod = ISerializable.deserializeAs<DexMethod>(serialize)
+        val method = dexMethod.getMethodInstance(hostClassLoader)
+        return method
+    }
+    error("No saved")
+}
+
+ + + diff --git a/en/guide/home.html b/en/guide/home.html new file mode 100644 index 00000000..236b4f38 --- /dev/null +++ b/en/guide/home.html @@ -0,0 +1,33 @@ + + + + + + + + + Introduction | DexKit + + + + +

Introduction

DexKit is a high-performance runtime parsing library for dex implemented in C++, used to search for obfuscated classes, methods, or properties.

Development Background

In the development of Xposed modules, we often need to hook specific methods. However, due to code obfuscation, module developers often have to maintain multiple versions of hook points to ensure compatibility of the module across different versions. This adaptation approach is cumbersome and error-prone.

Is there a better solution? Some developers may consider traversing all classes in the ClassLoader and traversing the characteristics of the classes through reflection, such as method names, parameter types, return value types, and annotations, and then adapting based on these features. However, this approach also has obvious drawbacks. First, due to the time-consuming nature of Java's reflection mechanism itself, the search speed is affected by device performance. Secondly, in complex conditions, the search may take a long time, and in extreme conditions, it may even exceed 30 seconds. In addition, forcibly loading some classes may cause unpredictable problems in the host APP.

Typically, developers decompile the host APP to obtain smali or decompiled Java code and search the code based on known features. The results are then written into the adaptation file. To simplify this process, we need an automated way. Currently, most solutions for parsing Dex files rely on dexlib2, but due to its development in Java, there are performance bottlenecks. Especially when the host application has a large number of dex files, the parsing time is long, affecting the user experience. Therefore, DexKit came into being. It is implemented in C++, providing superior performance, internal optimizations using multi-threading and various algorithms, enabling complex searches to be completed in a very short time.

Language Requirement

It is recommended to use Kotlin for development because it provides DSL support, allowing us to have a better experience when using DexKit. If you are not familiar with Kotlin, you don't need to worry either; the API also provides corresponding chain call support, which allows Java developers to have a good experience.

All example code in the documentation will be written in Kotlin. You can easily understand the corresponding Java usage through the examples here.

+ + + diff --git a/en/guide/knowledge.html b/en/guide/knowledge.html new file mode 100644 index 00000000..9a06e141 --- /dev/null +++ b/en/guide/knowledge.html @@ -0,0 +1,33 @@ + + + + + + + + + Basic Knowledge | DexKit + + + + +

Basic Knowledge

Here we provide some basic knowledge to help you better understand the usage of DexKit. Experienced developers can skip this section.

When using DexKit, there are some fundamental concepts you need to understand, including but not limited to:

  • Dex Decompilation Tools
  • JVM Signatures
    • Primitive Type Signatures
    • Reference Type Signatures
    • Array Type Signatures
    • Method Signatures
    • Field Signatures

Notice

The content in the basic knowledge is not necessarily completely accurate. Please read it according to your own understanding. If you find any inaccuracies, feel free to point them out and help improve.

Decompilation Tools

Usually, you can use jadxopen in new window to meet most of your needs. It can restore readable Java code in most cases.

JVM Signatures

Primitive Types (PrimitiveType)

Type SignaturePrimitive TypeSize (Bytes)
Vvoid-
Zboolean1
Bbyte1
Cchar2
Sshort2
Iint4
Jlong8
Ffloat4
Ddouble8

Reference Types (ReferenceType)

Reference types are divided into classes and arrays.

Class (ClassType)

The type signature of a class starts with L, followed by the fully qualified name (FullClassName) of the class, and ends with ;. For example, Ljava/lang/String; represents the java.lang.String class.

For example:

Type SignatureJava Type Definition
Ljava/lang/String;java.lang.String
Ljava/util/List;java.util.List

Array (ArrayType)

The type signature of an array starts with [, followed by the type signature of the array elements. For example, [[I represents a two-dimensional array where the element type is int.

For example:

Type SignatureJava Type Definition
[Iint[]
[[Cchar[][]
[Ljava/lang/String;java.lang.String[]

Tips

The term 'class' and 'type' are not entirely equivalent: 'type' is Type, while 'class' is Class. 'Class' is a subset of 'type'. For example:

  • java.lang.Integer is a 'class' and also a 'type'
  • java.lang.Integer[] is an 'array type', but not a 'class'
  • int is a 'primitive type', but not a 'class'

For method parameters, return types, and field types, we use the term 'type,' specifically 'Type.'

Method Signatures

A method signature consists of the signature of the return type and the signature of the parameter types. For example, ()V represents a parameterless void method.

For example:

For ease of description, all methods in the table are named as function.

Method SignatureJava Method Definition
()Vvoid function()
(I)Vvoid function(int)
(II)Vvoid function(int, int)
(ILjava/lang/String;J)Vvoid function(int, java.lang.String, long)
(I[II)Vvoid function(int, int[], int)
([[Ljava/lang/String;)Vvoid function(java.lang.String[][])
()[Ljava/lang/String;java.lang.String[] function()

Dalvik Descriptor

In a Dex file, we can represent specific classes, methods, or fields using the 'Dalvik Descriptor.' In DexKit API, the term 'descriptor' is commonly used.

Class Descriptor

The format of a class descriptor is [class signature], such as Ljava/lang/String;.

Method Descriptor

The format of a method descriptor is [class signature]->[method name][method signature], such as Ljava/lang/String;->length()I.

Tips

In 'Dalvik Descriptor,' the method name for constructors is <init>, and for static initialization methods, it's <clinit>. Therefore, in DexKit, to find constructors, you need to use <init> as the method name.

Field Descriptor

The format of a field descriptor is [class signature]->[field name]:[type signature], such as Ljava/lang/String;->count:I.

Tips

In DexKit, the className/Type query parameter only supports the Java primitive syntax. For example:

  • For primitive types, use Java PrimitiveType forms like void, int, boolean.
  • For reference types, use FullClassName forms like java.lang.String or java/lang/String.
  • For array types, use ArrayTypeName forms like int[], java.lang.String[][] or java/lang/String[][].
+ + + diff --git a/en/guide/performance-optimization.html b/en/guide/performance-optimization.html new file mode 100644 index 00000000..98a227f9 --- /dev/null +++ b/en/guide/performance-optimization.html @@ -0,0 +1,72 @@ + + + + + + + + + Performance Optimization | DexKit + + + + +

Performance Optimization

In DexKit, various queries may achieve the same functionality, but the difference in performance can be significant, varying by several tens of times. This section will introduce some techniques for performance optimization.

At the native layer, DexKit maintains lists of classes, methods, and fields in the Dex file. How does DexKit scan these lists in several APIs? The traversal order of findClass, findMethod, and findField is based on the respective lists' sequential order. Then, each condition is matched one by one.

declaredClass condition is too heavy

Some users may use the declaredClass condition to write queries like the following when using:

private fun badCode(bridge: DexKitBridge) {
+    bridge.findMethod {
+        matcher {
+            declaredClass {
+                usingStrings("getUid", "", "_event")
+            }
+            modifiers = Modifier.PUBLIC or Modifier.STATIC
+            returnType = "long"
+            addInvoke {
+                name = "parseLong"
+            }
+            addInvoke {
+                name = "toString"
+            }
+        }
+    }.single().let {
+        println(it)
+    }
+}
+

This search takes 4310ms.

At first glance, this query seems fine, but in reality, its performance is very poor. Why? As mentioned earlier, the findMethod API traverses all methods and then matches each condition one by one. However, there is a many-to-one relationship between methods and classes, meaning a class may contain multiple methods, but a method can only belong to one class. Therefore, during the process of traversing all methods, each method will be matched once with the declaredClass condition, leading to performance waste.

So, let's change our approach. By first searching for declaredClass and then using chain calls, we can search for methods within the classes that meet the criteria. Won't this help avoid the issue?

private fun goodCode(bridge: DexKitBridge) {
+    bridge.findClass {
+        matcher {
+            usingStrings("getUid", "", "_event")
+        }
+    }.findMethod {
+        matcher {
+            modifiers = Modifier.PUBLIC or Modifier.STATIC
+            returnType = "long"
+            addInvoke {
+                name = "parseLong"
+            }
+            addInvoke {
+                name = "toString"
+            }
+        }
+    }.single().let {
+        println(it)
+    }
+}
+

This search takes 77ms, showing a performance improvement by several tens of times.

When using findMethod or findField, the declaredClass condition should be avoided as much as possible.

+ + + diff --git a/en/guide/quick-start.html b/en/guide/quick-start.html new file mode 100644 index 00000000..8a91eb8f --- /dev/null +++ b/en/guide/quick-start.html @@ -0,0 +1,48 @@ + + + + + + + + + Quick Start | DexKit + + + + +

Quick Start

Integrate DexKit into your project.

Environment Requirements

Make sure your development environment meets the following requirements:

  • JDK 11 and above
  • Kotlin 1.5 and above
  • AGP 4.2 and above
  • minSdkVersion 21 and above (recommended 23 and above)

Notice

If your project's minSdkVersion is less than 23, using System.loadLibrary("dexkit") within an Xposed module may throw a java.lang.UnsatisfiedLinkError: xxx couldn't find "libdexkit.so" exception. This is because the so files under the lib/ directory are compressed by default during packaging, making it impossible to load the so file via System.loadLibrary. The solution is to add the following configuration in app/build.gradle:

android {
+    packagingOptions {
+        jniLibs {
+            useLegacyPackaging true
+        }
+    }
+}
+

or manually extract the libdexkit.so file from the lib/ directory within the apk to any readable and writable directory, and then load it using System.load("/path/to/libdexkit.so").

Integration Dependency

Add the dependency for dexkit in your project's app/build.gradle or app/build.gradle.kts.

Current DexKit version: Maven Centralopen in new window

dependencies {
+    // replace <version> with your desired version, e.g. '2.0.0'
+    implementation 'org.luckypray:dexkit:<version>'
+}
+
dependencies {
+    // replace <version> with your desired version, e.g. '2.0.0'
+    implementation("org.luckypray:dexkit:<version>")
+}
+

Tips

Starting from DexKit 2.0, the new ArtifactId has been changed from DexKit to dexkit.

Now that you have successfully integrated DexKit into your project, next we will introduce how to use DexKit to achieve some common requirements.

+ + + diff --git a/en/guide/run-on-desktop.html b/en/guide/run-on-desktop.html new file mode 100644 index 00000000..6bed0905 --- /dev/null +++ b/en/guide/run-on-desktop.html @@ -0,0 +1,37 @@ + + + + + + + + + Run on desktop platform | DexKit + + + + +

Run on desktop platform

Starting from version 1.1.0, DexKit supports running on desktop platforms without the need for packaging as an APK for testing on Android.

Install environment

The basic runtime environment requires gcc/clang, cmake, and ninja/make.

Windows

Windows users can use MSYS2open in new window to set up the runtime environment. Since all Windows systems are currently 64-bit, we use mingw64.exe for dependency installation:

pacman -S mingw-w64-x86_64-gcc mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja
+

After installation, we need to add the mingw64/bin directory to the environment variables for future use.

warning

DexKit will use ninja as the default build system by default. If you need to use make in mingw for building, you need to execute pacman -S mingw-w64-x86_64-make, after installation, you need to rename msys64\mingw64\bin\mingw32-make.exe to make.exe or add it as a shortcut, otherwise the build will fail due to gradle-cmake-plugin not finding the make command. Additionally, delete generator.set(generators.ninja) in :dexkit/build.gradle, or modify it to generator.set(generators.unixMakefiles).

Linux

On Linux, you normally only need to install ninja to use it.

MacOS

It's recommended to use HomeBrewopen in new window for dependency management.

brew install cmake ninja
+

Clone DexKit

git clone https://github.com/LuckyPray/DexKit.git
+

To begin using

Execute the submodule :main to perform testing.

gradle :main:run
+
+ + + diff --git a/en/guide/structural-zoom-table.html b/en/guide/structural-zoom-table.html new file mode 100644 index 00000000..285296f4 --- /dev/null +++ b/en/guide/structural-zoom-table.html @@ -0,0 +1,33 @@ + + + + + + + + + Quick Reference Guide | DexKit + + + + +

Quick Reference Guide

This section introduces the structure components of DexKit queries. Matcher objects can be recursively nested, and all conditions are optional.

FindClass

Field NameTypeDescription
searchPackagesCollection<String>List of package names to search
excludePackagesCollection<String>List of excluded package names
ignorePackagesCaseBooleanWhether to ignore package name case
searchInCollection<ClassData>Search for classes in the specified list of classes
findFirstBooleanReturn the first result found immediately; results are not guaranteed to be unique due to multi-threading
matcherClassMatcherMatching conditions

FindField

Field NameTypeDescription
searchPackagesCollection<String>List of package names to search
excludePackagesCollection<String>List of excluded package names
ignorePackagesCaseBooleanWhether to ignore package name case
searchInClassesCollection<ClassData>Search for fields in the specified list of classes
searchInFieldsCollection<FieldData>Search for fields in the specified list of fields
findFirstBooleanReturn the first result found immediately; results are not guaranteed to be unique due to multi-threading
matcherFieldMatcherMatching conditions

FindMethod

Field NameTypeDescription
searchPackagesCollection<String>List of package names to search
excludePackagesCollection<String>List of excluded package names
ignorePackagesCaseBooleanWhether to ignore package name case
searchInClassesCollection<ClassData>Search for methods in the specified list of classes
searchInMethodsCollection<MethodData>Search for methods in the specified list of methods
findFirstBooleanReturn the first result found immediately; results are not guaranteed to be unique due to multi-threading
matcherMethodMatcherMatching conditions

BatchFindClassUsingStrings

Field NameTypeDescription
searchPackagesCollection<String>List of package names to search
excludePackagesCollection<String>List of excluded package names
ignorePackagesCaseBooleanWhether to ignore package name case
searchInCollection<ClassData>Search for classes in the specified list of classes
matchersCollection<StringMatchersGroup>List of query groups using strings

BatchFindMethodUsingStrings

Field NameTypeDescription
searchPackagesCollection<String>List of package names to search
excludePackagesCollection<String>List of excluded package names
ignorePackagesCaseBooleanWhether to ignore package name case
searchInClassesCollection<ClassData>Search for methods in the specified list of classes
searchInMethodsCollection<MethodData>Search for methods in the specified list of methods
matchersCollection<StringMatchersGroup>List of query groups using strings

AccessFlagsMatcher

Field NameTypeDescription
modifiersIntBit masks for matching modifiers
matchTypeMatchTypeMatching mode

AnnotationEncodeValueMatcher

Field NameTypeDescription
byteValueByteMatched byte
shortValueShortMatched short integer
charValueCharMatched character
intValueIntMatched integer
longValueLongMatched long integer
floatValueFloatMatched float
doubleValueDoubleMatched double precision float
stringValueStringMatcherMatched string
methodValueMethodMatcherMatched method
enumValueFieldMatcherMatched enumeration
arrayValueAnnotationEncodeArrayMatcherMatched array
annotationValueAnnotationMatcherMatched annotation
nullValueNoneMatch null
boolValueBooleanMatched boolean

IntRange

Field NameTypeDescription
minIntMinimum value, default is 0
maxIntMaximum value, default is Int.MAX_VALUE

NumberEncodeValueMatcher

Field NameTypeDescription
byteValueByteMatched byte
shortValueShortMatched short integer
charValueCharMatched character
intValueIntMatched integer
longValueLongMatched long integer
floatValueFloatMatched float
doubleValueDoubleMatched double precision float

OpcodesMatcher

Field NameTypeDescription
opCodesCollection<Int>Matched Int values of opcodes
opNamesCollection<String>Matched names of opcodes (as per smali syntax)
matchTypeOpCodeMatchTypeMatching mode
sizeIntRangeTotal length range of the opcode

StringMatcher

Field NameTypeDescription
valueStringMatched string
matchTypeStringMatchTypeMatching mode
ignoreCaseBooleanIgnore case sensitivity

TargetElementTypesMatcher

Field NameTypeDescription
typesCollection<TargetElementType>List of matched annotation element types
matchTypeMatchTypeMatching mode

AnnotationElementMatcher

Field NameTypeDescription
nameStringMatcherMatched name
valueAnnotationEncodeValueMatcherMatched value

AnnotationElementsMatcher

Field NameTypeDescription
elementsCollection<AnnotationElementMatcher>List of matched annotation elements
matchTypeMatchTypeMatching mode
countIntRangeNumber of matched elements

AnnotationEncodeArrayMatcher

Field NameTypeDescription
valuesCollection<AnnotationEncodeValueMatcher>List of matched annotation elements
matchTypeMatchTypeMatching mode
countIntRangeNumber of matched elements

AnnotationMatcher

Field NameTypeDescription
typeClassMatcherMatched annotation type
targetElementTypesTargetElementTypesMatcherList of matched annotation element types
policyRetentionPolicyTypeMatched retention policy
elementsAnnotationElementsMatcherList of matched annotation elements
usingStringsCollection<StringMatcher>List of strings used in the annotation

AnnotationsMatcher

Field NameTypeDescription
annotationsCollection<AnnotationMatcher>List of matched annotations
matchTypeMatchTypeMatching mode
countIntRangeNumber of matched annotations

ClassMatcher

Field NameTypeDescription
sourceStringMatcherSource file name of the class, i.e., .source field in smali
classNameStringMatcherName of the class
modifiersAccessFlagsMatcherModifiers of the class
superClassClassMatcherSuperclass of the class
interfacesInterfacesMatcherList of interfaces implemented by the class
annotationsAnnotationsMatcherList of annotations for the class
fieldsFieldsMatcherList of fields in the class
methodsMethodsMatcherList of methods in the class
usingStringsCollection<StringMatcher>List of strings used in the class

InterfacesMatcher

Field NameTypeDescription
interfacesCollection<ClassMatcher>List of matched interfaces
matchTypeMatchTypeMatching mode
countIntRangeNumber of matched interfaces

FieldMatcher

Field NameTypeDescription
nameStringMatcherName of the field
modifiersAccessFlagsMatcherModifiers of the field
declaredClassClassMatcherDeclaring class of the field
typeClassMatcherType of the field
annotationsAnnotationsMatcherList of annotations for the field
readMethodsMethodsMatcherList of methods to read the field
writeMethodsMethodsMatcherList of methods to write the field

FieldsMatcher

Field NameTypeDescription
fieldsCollection<FieldMatcher>List of matched fields
matchTypeMatchTypeMatching mode
countIntRangeNumber of matched fields

MethodMatcher

Field NameTypeDescription
nameStringMatcherName of the method
modifiersAccessFlagsMatcherModifiers of the method
declaredClassClassMatcherDeclaring class of the method
returnTypeClassMatcherReturn type of the method
paramsParametersMatcherList of parameters
annotationsAnnotationsMatcherList of annotations for the method
opCodesOpcodesMatcherList of opcodes for the method
usingStringsCollection<StringMatcher>List of strings used in the method
usingFieldsCollection<UsingFieldMatcher>List of fields used in the method
usingNumbersCollection<Number>List of numbers used in the method
invokeMethodsMethodsMatcherList of methods invoked by the method
callerMethodsMethodsMatcherList of methods that call the method

MethodsMatcher

Field NameTypeDescription
methodsCollection<MethodMatcher>List of matched methods
matchTypeMatchTypeMatching mode
countIntRangeNumber of matched methods

ParameterMatcher

Field NameTypeDescription
typeClassMatcherType of the parameter
annotationsAnnotationsMatcherList of annotations for the parameter

ParametersMatcher

Field NameTypeDescription
paramsCollection<ParameterMatcher>List of matched parameters
countIntRangeNumber of matched parameters

StringMatchersGroup

Field NameTypeDescription
groupNameStringGroup name for the query
matchersCollection<StringMatcher>List of strings used in the query

UsingFieldMatcher

Field NameTypeDescription
fieldFieldMatcherMatched field
usingTypeUsingTypeType of usage

EncodeValueByte

Field NameTypeDescription
valueBytebyte value

EncodeValueShort

Field NameTypeDescription
valueShortshort integer value

EncodeValueChar

Field NameTypeDescription
valueCharcharacter value

EncodeValueInt

Field NameTypeDescription
valueIntinteger value

EncodeValueLong

Field NameTypeDescription
valueLonglong integer value

EncodeValueFloat

Field NameTypeDescription
valueFloatfloat value

EncodeValueDouble

Field NameTypeDescription
valueDoubledouble value

EncodeValueString

Field NameTypeDescription
valueStringstring value

EncodeValueNull

This object has no fields.

EncodeValueBoolean

Field NameTypeDescription
valueBooleanboolean value

Enumerations

AnnotationEncodeValueType

Field NameTypeDescription
ByteValueEnumByte type
ShortValueEnumShort integer type
CharValueEnumCharacter type
IntValueEnumInteger type
LongValueEnumLong integer type
FloatValueEnumFloat type
DoubleValueEnumDouble precision float type
StringValueEnumString type
TypeValueEnumType type
MethodValueEnumMethod type
EnumValueEnumEnum type
ArrayValueEnumArray type
AnnotationValueEnumAnnotation type
NullValueEnumNull type
BoolValueEnumBoolean type

AnnotationVisibilityType

Refer to Visibility valuesopen in new window

Field NameTypeDescription
BuildEnumVisible at build time
RuntimeEnumVisible at runtime
SystemEnumVisible to the system

MatchType

Field NameTypeDescription
ContainsEnumContains
EqualsEnumEquals

NumberEncodeValueType

Field NameTypeDescription
ByteValueEnumByte type
ShortValueEnumShort integer type
CharValueEnumCharacter type
IntValueEnumInteger type
LongValueEnumLong integer type
FloatValueEnumFloat type
DoubleValueEnumDouble precision float type

OpCodeMatchType

Field NameTypeDescription
ContainsEnumContains
StartsWithEnumStarts with
EndsWithEnumEnds with
EqualsEnumEquals

RetentionPolicyType

This Enum corresponds to java.lang.annotation.RetentionPolicy.

Field NameTypeDescription
SourceEnumSource level
ClassEnumClass level
RuntimeEnumRuntime level

StringMatchType

Field NameTypeDescription
ContainsEnumContains
StartsWithEnumStarts with
EndsWithEnumEnds with
SimilarRegexEnumRegex-like pattern, supporting only ^ and $
EqualsEnumEquals

TargetElementType

This Enum corresponds to java.lang.annotation.ElementType.

Field NameTypeDescription
TypeEnumType
FieldEnumField
MethodEnumMethod
ParameterEnumParameter
ConstructorEnumConstructor
LocalVariableEnumLocal variable
AnnotationTypeEnumAnnotation type
PackageEnumPackage
TypeParameterEnumType parameter
TypeUseEnumType use

UsingType

Field NameTypeDescription
AnyEnumAny usage
GetEnumGet usage
SetEnumSet usage
+ + + diff --git a/en/index.html b/en/index.html new file mode 100644 index 00000000..486d9480 --- /dev/null +++ b/en/index.html @@ -0,0 +1,115 @@ + + + + + + + + + Home | DexKit + + + + +

DexKit

A high-performance dex runtime parsing library implemented in C++

QuickStart API KDoc open in new window

Elegant and Simple

DexKit is built with a user-friendly API entirely using Kotlin DSL, supporting nested complex queries and providing good support for Java as well.

High-Performance

Implemented using C++ at its core, DexKit delivers superior performance. It utilizes multiple algorithms on top of multithreading, allowing it to complete complex searches in an extremely short time.

Cross-platform

It offers multi-platform support. After testing on Windows, Linux, or MacOS, the code can be directly migrated to the Android platform.

Ultimate Experience, Say No to Tediousness

Example Code

Assume this is obfuscated code from a host app. We need to dynamically adapt the hook for this method. Due to obfuscation, method names and class names may change with each version.

public class abc {
+
+    public boolean cvc() {
+        boolean b = false;
+        // ...
+        Log.d("VipCheckUtil", "userInfo: xxxx");
+        // ...
+        return b;
+    }
+}
+

DexKit can easily solve this problem!

Xposed hook code

By creating an instance of DexKitBridge, we can perform specific searches on the app's dex file.

warning

Only one instance of DexKitBridge needs to be created. If you do not wish to use try-with-resources or Kotlin's .use for automatic closing of the DexKitBridge instance, you need to manage its lifecycle manually. Ensure to call .close() when it's no longer needed to prevent memory leaks.

class AppHooker {
+    companion object {
+        init { System.loadLibrary("dexkit") }
+    }
+
+    private var hostClassLoader: ClassLoader
+
+    public constructor(loadPackageParam: LoadPackageParam) {
+        this.hostClassLoader = loadPackageParam.classLoader
+        val apkPath = loadPackageParam.appInfo.sourceDir
+        // DexKit creation is a time-consuming operation, please do not create the object repeatedly. 
+        // If you need to use it globally, please manage the life cycle yourself and ensure 
+        // that the .close() method is called when not needed to prevent memory leaks.
+        // Here we use `Closable.use` to automatically close the DexKitBridge instance.
+        DexKitBridge.create(apkPath).use { bridge ->
+            isVipHook(bridge)
+            // Other hook ...
+        }
+    }
+
+    private fun isVipHook(bridge: DexKitBridge) {
+        val methodData = bridge.findMethod {
+            matcher {
+                // All conditions are optional, you can freely combine them
+                modifiers = Modifier.PUBLIC
+                returnType = "boolean"
+                paramCount = 0
+                usingStrings("VipCheckUtil", "userInfo:")
+            }
+        }.single()
+        val method: Method = methodData.getMethodInstance(hostClassLoader)
+        XposedBridge.hookMethod(method, XC_MethodReplacement.returnConstant(true))
+    }
+
+    // ...
+}
+
class AppHooker {
+    static {
+        System.loadLibrary("dexkit");
+    }
+
+    private ClassLoader hostClassLoader;
+
+    public AppHooker(LoadPackageParam loadPackageParam) {
+        this.hostClassLoader = loadPackageParam.classLoader;
+        String apkPath = loadPackageParam.appInfo.sourceDir;
+        // DexKit creation is a time-consuming operation, please do not create the object repeatedly. 
+        // If you need to use it globally, please manage the life cycle yourself and ensure 
+        // that the .close() method is called when not needed to prevent memory leaks.
+        // Here we use `try-with-resources` to automatically close the DexKitBridge instance.
+        try (DexKitBridge bridge = DexKitBridge.create(apkPath)) {
+            isVipHook(bridge);
+            // Other hook ...
+        }
+    }
+
+    private void isVipHook(DexKitBridge bridge) {
+        MethodData methodData = bridge.findMethod(FindMethod.create()
+                .matcher(MethodMatcher.create()
+                        // All conditions are optional, you can freely combine them
+                        .modifiers(Modifier.PUBLIC)
+                        .paramCount(0)
+                        .returnType("boolean")
+                        .usingStrings("VipCheckUtil", "userInfo:")
+                )
+        ).single();
+        Method method = methodData.getMethodInstance(hostClassLoader);
+        XposedBridge.hookMethod(method, XC_MethodReplacement.returnConstant(true));
+    }
+
+    // ...
+}
+

It's that simple!

+ + + diff --git a/index.html b/index.html new file mode 100644 index 00000000..df96a3c9 --- /dev/null +++ b/index.html @@ -0,0 +1,33 @@ + + + + + + + + + DexKit + + + + +

Select a language

English 简体中文

+ + + diff --git a/zh-cn/about/about.html b/zh-cn/about/about.html new file mode 100644 index 00000000..8a9fa00e --- /dev/null +++ b/zh-cn/about/about.html @@ -0,0 +1,33 @@ + + + + + + + + + 关于此文档 | DexKit + + + + + + + + diff --git a/zh-cn/about/contacts.html b/zh-cn/about/contacts.html new file mode 100644 index 00000000..eae1d864 --- /dev/null +++ b/zh-cn/about/contacts.html @@ -0,0 +1,33 @@ + + + + + + + + + 联系我们 | DexKit + + + + + + + + diff --git a/zh-cn/guide/example.html b/zh-cn/guide/example.html new file mode 100644 index 00000000..9fc7e502 --- /dev/null +++ b/zh-cn/guide/example.html @@ -0,0 +1,246 @@ + + + + + + + + + 用法示例 | DexKit + + + + +

用法示例

在阅读本部分内容的过程中可能需要搭配结构速查表以获得更好的理解。

您可以在下方获取 Demo 的源码以及部分测试用例

Demo App

下面是一个简单的 Demo Activity。这个 PlayActivity 内部属性以及方法都是被混淆的,且每个版本都会发生变化。

四大组件默认不会混淆,我们假设这个 Activity 被混淆了。

package org.luckypray.dexkit.demo;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.h;
+import java.util.Random;
+import org.luckypray.dexkit.demo.annotations.Router;
+
+@Router(path = "/play")
+public class PlayActivity extends AppCompatActivity {
+    private static final String TAG = "PlayActivity";
+    private TextView a;
+    private Handler b;
+
+    public void d(View view) {
+        Handler handler;
+        int i;
+        Log.d("PlayActivity", "onClick: rollButton");
+        float nextFloat = new Random().nextFloat();
+        if (nextFloat < 0.01d) {
+            handler = this.b;
+            i = -1;
+        } else if (nextFloat < 0.987f) {
+            handler = this.b;
+            i = 0;
+        } else {
+            handler = this.b;
+            i = 114514;
+        }
+        handler.sendEmptyMessage(i);
+    }
+
+    public void e(boolean z) {
+        int i;
+        if (!z) {
+            i = RandomUtil.a();
+        } else {
+            i = 6;
+        }
+        String a = h.a("You rolled a ", i);
+        this.a.setText(a);
+        Log.d("PlayActivity", "rollDice: " + a);
+    }
+
+    protected void onCreate(Bundle bundle) {
+        super/*androidx.fragment.app.FragmentActivity*/.onCreate(bundle);
+        setContentView(0x7f0b001d);
+        Log.d("PlayActivity", "onCreate");
+        HandlerThread handlerThread = new HandlerThread("PlayActivity");
+        handlerThread.start();
+        this.b = new PlayActivity$1(this, handlerThread.getLooper());
+        this.a = (TextView) findViewById(0x7f080134);
+        ((Button) findViewById(0x7f08013a)).setOnClickListener(new a(this));
+    }
+}
+

多条件匹配类多条件用法示例

此时我们想得到这个 PlayActivity 可以使用如下代码:

这仅仅是个样例,实际使用中并不需要这么复杂且全面的条件,按需使用即可。

private fun findPlayActivity(bridge: DexKitBridge) {
+    val classData = bridge.findClass {
+        // 指定搜索的包名范围
+        searchPackages("org.luckypray.dexkit.demo")
+        // 排除指定的包名范围
+        excludePackages("org.luckypray.dexkit.demo.annotations")
+        // ClassMatcher 针对类的匹配器
+        matcher {
+            // FieldsMatcher 针对类中包含字段的匹配器
+            fields {
+                // 添加对于字段的匹配器
+                add {
+                    // 指定字段的修饰符
+                    modifiers = Modifier.PRIVATE or Modifier.STATIC or Modifier.FINAL
+                    // 指定字段的类型
+                    type = "java.lang.String"
+                    // 指定字段的名称
+                    name = "TAG"
+                }
+                // 添加指定字段的类型的字段匹配器
+                addForType("android.widget.TextView")
+                addForType("android.os.Handler")
+                // 指定类中字段的数量
+                count = 3
+            }
+            // MethodsMatcher 针对类中包含方法的匹配器
+            methods {
+                // 添加对于方法的匹配器
+                add {
+                    // 指定方法的修饰符
+                    modifiers = Modifier.PROTECTED
+                    // 指定方法的名称
+                    name = "onCreate"
+                    // 指定方法的返回值类型
+                    returnType = "void"
+                    // 指定方法的参数类型,如果参数类型不确定,使用 null,使用此方法会隐式声明参数个数
+                    paramTypes("android.os.Bundle")
+                    // 指定方法中使用的字符串
+                    usingStrings("onCreate")
+                }
+                add {
+                    paramTypes("android.view.View")
+                    // 指定方法中使用的数字,类型为 Byte, Short, Int, Long, Float, Double 之一
+                    usingNumbers(0.01, -1, 0.987, 0, 114514)
+                }
+                add {
+                    paramTypes("boolean")
+                    // 指定方法中调用的方法列表
+                    invokeMethods {
+                        add {
+                            modifiers = Modifier.PUBLIC or Modifier.STATIC
+                            returnType = "int"
+                            // 指定方法中调用的方法中使用的字符串,所有字符串均使用 Equals 匹配
+                            usingStrings(listOf("getRandomDice: "), StringMatchType.Equals)
+                        }
+                        // 只需要包含上述方法的调用即可
+                        matchType = MatchType.Contains
+                    }
+                }
+                // 指定类中方法的数量,最少不少于1个,最多不超过10个
+                count(1..10)
+            }
+            // AnnotationsMatcher 针对类中包含注解的匹配器
+            annotations {
+                // 添加对于注解的匹配器
+                add {
+                    // 指定注解的类型
+                    type = "org.luckypray.dexkit.demo.annotations.Router"
+                    // 该注解需要包含指定的 element
+                    addElement {
+                        // 指定 element 的名称
+                        name = "path"
+                        // 指定 element 的值
+                        stringValue("/play")
+                    }
+                }
+            }
+            // 类中所有方法使用的字符串
+            usingStrings("PlayActivity", "onClick", "onCreate")
+        }
+    }.single()
+    println(classData.name)
+}
+

获得的结果如下:

org.luckypray.dexkit.demo.PlayActivity
+

父类条件嵌套

如果存在这么一个类,它唯一的特征是祖辈没被混淆,中间的父类都被混淆了,我们也可以使用 DexKit 来找到它。

private fun findActivity(bridge: DexKitBridge) {
+    bridge.findClass {
+        matcher { // ClassMatcher
+            // androidx.appcompat.app.AppCompatActivity
+            superClass { // ClassMatcher
+                // androidx.fragment.app.FragmentActivity
+                superClass { // ClassMatcher
+                    // androidx.activity.ComponentActivity
+                    superClass { // ClassMatcher
+                        // androidx.core.app.ComponentActivity
+                        superClass { // ClassMatcher
+                            superClass = "android.app.Activity"
+                        }
+                    }
+                }
+            }
+        }
+    }.forEach {
+        // org.luckypray.dexkit.demo.MainActivity
+        // org.luckypray.dexkit.demo.PlayActivity
+        println(it.name)
+    }
+}
+

获得的结果如下:

org.luckypray.dexkit.demo.MainActivity
+org.luckypray.dexkit.demo.PlayActivity
+

小提示

DexKit 中,一切符合逻辑的关系都可以作为查询条件

模糊参数匹配

如果我们需要寻找的方法中存在一个被混淆的参数,我们可以使用 null 来替代,这样它就能匹配任意类型的参数。

private fun findMethodWithFuzzyParam(bridge: DexKitBridge) {
+    bridge.findMethod {
+        matcher {
+            modifiers = Modifier.PUBLIC or Modifier.STATIC
+            returnType = "void"
+            // 指定方法的参数类型,如果参数类型不确定,使用 null
+            paramTypes("android.view.View", null)
+            // paramCount = 2 // paramTypes 长度为 2 已经隐式确定了参数个数
+            usingStrings("onClick")
+        }
+    }.single().let {
+        println(it)
+    }
+}
+

查询结果保存与读取

使用 DexKit 查询到的结果如何序列化保存下来,以便下次使用呢?

DexKit 中对 Class、Method、Field 提供了相应的包装类,分别是 DexClassDexMethodDexField。 包装类继承了 ISerializable 接口,可以使用它将包装类与字符串自由转换。对于查询返回的对象,可以直接使用 toDexClass()toDexMethod()toDexField() 方法来转换为包装类。

private fun saveData(bridge: DexKitBridge) {
+    bridge.findMethod {
+        matcher {
+            modifiers = Modifier.PUBLIC or Modifier.STATIC
+            returnType = "void"
+            paramTypes("android.view.View", null)
+            usingStrings("onClick")
+        }
+    }.single().let {
+        val dexMethod = it.toDexMethod()
+        val serialize = dexMethod.serialize()
+        val sp = getSharedPreferences("dexkit", Context.MODE_PRIVATE)
+        sp.edit().putString("onClickMethod", serialize).apply()
+    }
+}
+
+private fun readData(): Method {
+    val sp = getSharedPreferences("dexkit", Context.MODE_PRIVATE)
+    val descriptor = sp.getString("onClickMethod", null)
+    if (descriptor != null) {
+        val dexMethod = DexMethod(descriptor)
+        // val dexMethod = DexMethod.deserialize(serialize)
+        // val dexMethod = ISerializable.deserialize(serialize) as DexMethod
+        // val dexMethod = ISerializable.deserializeAs<DexMethod>(serialize)
+        val method = dexMethod.getMethodInstance(hostClassLoader)
+        return method
+    }
+    error("No saved")
+}
+
+ + + diff --git a/zh-cn/guide/home.html b/zh-cn/guide/home.html new file mode 100644 index 00000000..2616f364 --- /dev/null +++ b/zh-cn/guide/home.html @@ -0,0 +1,33 @@ + + + + + + + + + 介绍 | DexKit + + + + +

介绍

DexKit 是一个使用 C++ 实现的 dex 高性能运行时解析库,用于查找被混淆的类、方法或者属性。

开发背景

在 Xposed 模块的开发中,我们通常需要对特定的方法进行 hook。然而,由于代码混淆,模块开发者往往不得不维护多个版本的 hook 点,以确保模块在不同版本下的兼容性。这种适配方式繁琐且容易出错。

有没有更好的解决方案呢?有些开发者可能会考虑遍历 ClassLoader 中的所有类,并通过反射遍历类的特征,如方法名、参数类型、 返回值类型和注解等,然后根据这些特征进行适配。然而,这种方式也有明显的缺点。首先,由于 Java 的反射机制本身耗时较长, 查找速度受设备性能影响。其次,在复杂条件下,查找可能需要较长时间,极端条件下甚至可能超过 30 秒。另外, 强行加载部分类可能会导致宿主 APP 出现不可预知的问题。

通常,开发者会对宿主 APP 进行反编译,获取 smali 或反编译后的 Java 代码,并通过已知的特征对代码进行搜索。 然后将结果写入适配文件。为了简化这个过程,我们需要一种自动化的方式。目前针对 Dex 文件的解析方案大多依赖于 dexlib2,但由于其是用 Java 开发,性能存在瓶颈。特别是当宿主应用存在大量 dex 文件时,解析时间会很长, 影响用户体验。为此,DexKit 应运而生。它采用 C++ 实现,性能优越的同时且内部使用多线程和多种算法进行优化, 能够在极短时间内完成复杂的搜索。

语言要求

推荐使用 Kotlin 进行开发,因为其提供了 DSL 支持,可以让我们在使用 DexKit 时获得更好的体验。如果您不熟悉 Kotlin,也无需担心,API 也提供了相应的链式调用支持,同样能让 Java 开发者获得良好的体验。

文档中的所有示例代码都将使用 Kotlin 编写。您可以通过 这里 的示例,轻松了解相应的 Java 写法。

+ + + diff --git a/zh-cn/guide/knowledge.html b/zh-cn/guide/knowledge.html new file mode 100644 index 00000000..98a5db1e --- /dev/null +++ b/zh-cn/guide/knowledge.html @@ -0,0 +1,33 @@ + + + + + + + + + 基础知识 | DexKit + + + + +

基础知识

这里提供了一些基础知识,帮助您更好的理解 DexKit 的使用,已经掌握的开发者可以跳过这一章节。

在使用 DexKit 时,有一些基础知识是必要的,其中包括但不限于以下内容:

  • Dex 反编译工具
  • JVM 签名
    • 原始类型签名
    • 引用类型签名
    • 数组类型签名
    • 方法签名
    • 字段签名

注意

基础知识中的内容不一定完全准确,请根据自己的见解酌情阅读,若发现内容有误,欢迎指正并帮助改进。

反编译工具

通常,您可以使用 jadx在新窗口中打开 来满足大部分需求, 它在大多数情况下能还原出可读的 Java 代码。

JVM 签名

原始类型(PrimitiveType)

类型签名原始类型大小(字节)
Vvoid-
Zboolean1
Bbyte1
Cchar2
Sshort2
Iint4
Jlong8
Ffloat4
Ddouble8

引用类型(ReferenceType)

引用类型分为类与数组。

类(ClassType)

类的类型签名都是以 L 开头,以 ; 结尾,中间是类的全限定名(FullClassName),如 Ljava/lang/String;

例如:

类型签名Java 中类型定义
Ljava/lang/String;java.lang.String
Ljava/util/List;java.util.List

数组(ArrayType)

数组类型的类型签名以 [ 开头,后面跟着数组元素的类型签名,如 [[I 表示一个二维数组,数组中的元素类型是 int

例如:

类型签名Java 中类型定义
[Iint[]
[[Cchar[][]
[Ljava/lang/String;java.lang.String[]

小提示

类型 并不完全等价:类型为 Type,而类为 Class。 类型 的子集。 例如:

  • java.lang.Integer,也是 类型
  • java.lang.Integer[]数组类型,但不是
  • int原始类型,但不是

对于方法参数,返回值类型以及字段的类型,我们统一称为 类型Type

方法签名

方法签名由方法的返回值类型签名和参数类型签名组成,如 ()V 表示一个无参的 void 方法。

例如:

为了方便表述,表格中所有的方法都命名为 function

方法签名Java 中方法定义
()Vvoid function()
(I)Vvoid function(int)
(II)Vvoid function(int, int)
(ILjava/lang/String;J)Vvoid function(int, java.lang.String, long)
(I[II)Vvoid function(int, int[], int)
([[Ljava/lang/String;)Vvoid function(java.lang.String[][])
()[Ljava/lang/String;java.lang.String[] function()

Dalvik 描述

在 Dex 文件中,我们可以通过 Dalvik 描述 的方式来表示特定的类、方法或字段。在 DexKit API中,通常使用 descriptor 来命名。

类描述

类描述的格式为 [类签名],如 Ljava/lang/String;

方法描述

方法描述的格式为 [类签名]->[方法名][方法签名],如 Ljava/lang/String;->length()I

小提示

Dalvik 描述 中,构造函数的方法名为 <init>,静态初始化函数的方法名为 <clinit>。 所以在 DexKit 中如果想要查找构造函数,需要使用 <init> 作为方法名。

字段描述

字段描述的格式为 [类签名]->[字段名]:[类型签名],如 Ljava/lang/String;->count:I

小提示

DexKit 中 className/Type 查询参数只支持 Java 原始写法,例如:

  • 对于基本类型,填写 voidintboolean 形式的 Java PrimitiveType
  • 对于引用类型,填写 java.lang.String 或者 java/lang/String 形式的 FullClassName
  • 对于数组类型,填写 int[]java.lang.String[][] 或者 java/lang/String[][] 形式的 ArrayTypeName
+ + + diff --git a/zh-cn/guide/performance-optimization.html b/zh-cn/guide/performance-optimization.html new file mode 100644 index 00000000..2273e568 --- /dev/null +++ b/zh-cn/guide/performance-optimization.html @@ -0,0 +1,72 @@ + + + + + + + + + 性能优化 | DexKit + + + + +

性能优化

在 DexKit 中,多种查询或许能实现同样的功能,但是性能差距却可能相差几十倍。本节将介绍一些性能优化的技巧。

在 native 层,DexKit 会维护 Dex 中的类、方法以及字段的列表,那么在几个 API 中,DexKit 是如何扫描这些列表的呢?findClassfindMethodfindField 的遍历顺序均是按照各自列表的先后顺序进行遍历,然后再逐一对各个条件进行匹配。

declaredClass 条件过重

可能有些用户在使用 findMethodfindField 时,会使用 declaredClass 条件写出如下的查询:

private fun badCode(bridge: DexKitBridge) {
+    bridge.findMethod {
+        matcher {
+            declaredClass {
+                usingStrings("getUid", "", "_event")
+            }
+            modifiers = Modifier.PUBLIC or Modifier.STATIC
+            returnType = "long"
+            addInvoke {
+                name = "parseLong"
+            }
+            addInvoke {
+                name = "toString"
+            }
+        }
+    }.single().let {
+        println(it)
+    }
+}
+

这个搜索耗时 4310ms。 乍一看这个查询似乎没有什么问题,但是实际上这个查询的性能是非常差。为什么?前面提到过,findMethod API 会遍历一遍所有的方法,然后再逐一对各个条件进行匹配。而 method 与 class 之间却是一个多对一的关系,即一个 class 中可能包含多个 method,但是一个 method 只能属于一个 class。因此,遍历所有方法的过程中,每个 method 都会被匹配一次 declaredClass 条件,这就导致了性能的浪费。

那么,我们换一个思路,先搜索 declaredClass,配合链式调用就能在符合条件的类中再搜索 method,这样不就可以避免了吗?

private fun goodCode(bridge: DexKitBridge) {
+    bridge.findClass {
+        matcher {
+            usingStrings("getUid", "", "_event")
+        }
+    }.findMethod {
+        matcher {
+            modifiers = Modifier.PUBLIC or Modifier.STATIC
+            returnType = "long"
+            addInvoke {
+                name = "parseLong"
+            }
+            addInvoke {
+                name = "toString"
+            }
+        }
+    }.single().let {
+        println(it)
+    }
+}
+

这个搜索耗时 77ms, 性能提升了数十倍之多。

在使用 findMethodfindField 时,尽量避免使用 declaredClass 附带过于复杂的逻辑。

+ + + diff --git a/zh-cn/guide/quick-start.html b/zh-cn/guide/quick-start.html new file mode 100644 index 00000000..4fcf0ea8 --- /dev/null +++ b/zh-cn/guide/quick-start.html @@ -0,0 +1,48 @@ + + + + + + + + + 快速开始 | DexKit + + + + +

快速开始

集成 DexKit 到您的项目中。

环境要求

确保您的开发环境满足以下要求:

  • JDK 11 及以上
  • Kotlin 1.5 及以上
  • AGP 4.2 及以上
  • minSdkVersion 21 及以上(推荐 23 及以上)

注意

如果您的项目的 minSdkVersion 小于 23,在 Xposed 模块内使用 System.loadLibrary("dexkit") 时可能会抛出 java.lang.UnsatisfiedLinkError: xxx couldn't find "libdexkit.so" 的异常。这是因为打包时默认会压缩 lib/ 目录下的 so 文件,导致无法通过 System.loadLibrary 加载 so 文件。解决方案是在 app/build.gradle 中添加以下配置:

android {
+    packagingOptions {
+        jniLibs {
+            useLegacyPackaging true
+        }
+    }
+}
+

或者手动解压 apk 内的 lib/ 目录下的 libdexkit.so 文件至任意可读写目录,然后通过 System.load("/path/to/libdexkit.so") 加载。

集成依赖

在您的项目中的 app/build.gradle 或者 app/build.gradle.kts 添加 dexkit 的依赖。

DexKit 当前版本: Maven Central在新窗口中打开

dependencies {
+    // 将 <version> 替换为您需要的版本,例如 '2.0.0-rc1'
+    implementation 'org.luckypray:dexkit:<version>'
+}
+
dependencies {
+    // 将 <version> 替换为您需要的版本,例如 '2.0.0-rc1'
+    implementation("org.luckypray:dexkit:<version>")
+}
+

小提示

DexKit 2.0 开始,新的 ArtifactId 已从 DexKit 更改为 dexkit

现在您已经成功集成了 DexKit 到您的项目中,接下来我们将会介绍如何使用 DexKit 来完成一些常见的需求。

+ + + diff --git a/zh-cn/guide/run-on-desktop.html b/zh-cn/guide/run-on-desktop.html new file mode 100644 index 00000000..f3a58e62 --- /dev/null +++ b/zh-cn/guide/run-on-desktop.html @@ -0,0 +1,37 @@ + + + + + + + + + 在桌面平台运行 | DexKit + + + + +

在桌面平台运行

1.1.0 开始,DexKit 支持桌面平台运行,无需打包成 apk 在 Android 进行测试工作。

安装环境

需要 gcc/clang、cmake 以及 ninja/make 作为基础运行环境。

Windows

Windows 用户可以使用 MSYS2在新窗口中打开 搭建运行环境。由于目前Windows系统均为64位, 所以我们使用 mingw64.exe 进行依赖安装:

pacman -S mingw-w64-x86_64-gcc mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja
+

安装完成后,我们需要将 mingw64/bin 目录添加进环境变量中便于后续使用。

warning

DexKit 默认将使用 ninja 作为默认构建系统,如果您需要在 mingw 中使用 make 进行构建, 需要执行 pacman -S mingw-w64-x86_64-make,安装完成后需要将 msys64\mingw64\bin\mingw32-make.exe 重命名为 make.exe 或者添加为快捷方式,否则会由于 gradle-cmake-plugin 找不到 make 命令而构建失败。 同时删除 :dexkit/build.gradle 中的 generator.set(generators.ninja) ,或者修改为 generator.set(generators.unixMakefiles)

Linux

对于Linux用户,正常情况只需要安装 ninja 即可。

MacOS

推荐使用 HomeBrew在新窗口中打开 进行依赖管理,

brew install cmake ninja
+

克隆 DexKit

git clone https://github.com/LuckyPray/DexKit.git
+

开始使用

执行子模块 :main 即可进行测试。

gradle :main:run
+
+ + + diff --git a/zh-cn/guide/structural-zoom-table.html b/zh-cn/guide/structural-zoom-table.html new file mode 100644 index 00000000..85aa5621 --- /dev/null +++ b/zh-cn/guide/structural-zoom-table.html @@ -0,0 +1,33 @@ + + + + + + + + + 结构速查表 | DexKit + + + + +

结构速查表

这里介绍了 DexKit 查询的结构组成,匹配器对象可以进行递归嵌套,一切条件都是可以选的。

查询相关

FindClass

字段名类型说明
searchPackagesCollection<String>搜索的包名列表
excludePackagesCollection<String>排除的包名列表
ignorePackagesCaseBoolean是否忽略包名大小写
searchInCollection<ClassData>在指定的类列表中搜索类
findFirstBoolean查询到第一个结果后立即返回结果,由于多线程执行,不保证结果唯一
matcherClassMatcher匹配条件

FindField

字段名类型说明
searchPackagesCollection<String>搜索的包名列表
excludePackagesCollection<String>排除的包名列表
ignorePackagesCaseBoolean是否忽略包名大小写
searchInClassesCollection<ClassData>在指定的类列表中搜索字段
searchInFieldsCollection<FieldData>在指定的字段列表中搜索字段
findFirstBoolean查询到第一个结果后立即返回结果,由于多线程执行,不保证结果唯一
matcherFieldMatcher匹配条件

FindMethod

字段名类型说明
searchPackagesCollection<String>搜索的包名列表
excludePackagesCollection<String>排除的包名列表
ignorePackagesCaseBoolean是否忽略包名大小写
searchInClassesCollection<ClassData>在指定的类列表中搜索方法
searchInMethodsCollection<MethodData>在指定的方法列表中搜索方法
findFirstBoolean查询到第一个结果后立即返回结果,由于多线程执行,不保证结果唯一
matcherMethodMatcher匹配条件

BatchFindClassUsingStrings

字段名类型说明
searchPackagesCollection<String>搜索的包名列表
excludePackagesCollection<String>排除的包名列表
ignorePackagesCaseBoolean是否忽略包名大小写
searchInCollection<ClassData>在指定的类列表中搜索类
matchersCollection<StringMatchersGroup>查询分组列表

BatchFindMethodUsingStrings

字段名类型说明
searchPackagesCollection<String>搜索的包名列表
excludePackagesCollection<String>排除的包名列表
ignorePackagesCaseBoolean是否忽略包名大小写
searchInClassesCollection<ClassData>在指定的类列表中搜索方法
searchInMethodsCollection<MethodData>在指定的方法列表中搜索方法
matchersCollection<StringMatchersGroup>查询分组列表

匹配器相关

AccessFlagsMatcher

字段名类型说明
modifiersInt匹配的修饰符的 bit masks
matchTypeMatchType匹配模式

AnnotationEncodeValueMatcher

字段名类型说明
byteValueByte匹配的字节
shortValueShort匹配的短整型
charValueChar匹配的字符
intValueInt匹配的整型
longValueLong匹配的长整型
floatValueFloat匹配的浮点型
doubleValueDouble匹配的双精度浮点
stringValueStringMatcher匹配的字符串
methodValueMethodMatcher匹配的方法
enumValueFieldMatcher匹配的枚举
arrayValueAnnotationEncodeArrayMatcher匹配的数组
annotationValueAnnotationMatcher匹配的注解
nullValue匹配 null
boolValueBoolean匹配的布尔值

IntRange

字段名类型说明
minInt最小值,默认为 0
maxInt最大值,默认为 Int.MAX_VALUE

NumberEncodeValueMatcher

字段名类型说明
byteValueByte匹配的字节
shortValueShort匹配的短整型
charValueChar匹配的字符
intValueInt匹配的整型
longValueLong匹配的长整型
floatValueFloat匹配的浮点型
doubleValueDouble匹配的双精度浮点

OpcodesMatcher

字段名类型说明
opCodesCollection<Int>匹配的操作码对应的 Int 值
opNamesCollection<String>匹配的操作码对应的名称,即 smali 语法中的名称
matchTypeOpCodeMatchType匹配模式
sizeIntRange该操作码总长度范围

StringMatcher

字段名类型说明
valueString匹配的字符串
matchTypeStringMatchType匹配模式
ignoreCaseBoolean是否忽略大小写

TargetElementTypesMatcher

字段名类型说明
typesCollection<TargetElementType>匹配的注解声明的元素类型列表
matchTypeMatchType匹配模式

AnnotationElementMatcher

字段名类型说明
nameStringMatcher匹配的名称
valueAnnotationEncodeValueMatcher匹配的值

AnnotationElementsMatcher

字段名类型说明
elementsCollection<AnnotationElementMatcher>匹配的注解声明的元素列表
matchTypeMatchType匹配模式
countIntRange匹配的注解声明的元素数量

AnnotationEncodeArrayMatcher

字段名类型说明
valuesCollection<AnnotationEncodeValueMatcher>匹配的注解声明的元素列表
matchTypeMatchType匹配模式
countIntRange匹配的注解声明的元素数量

AnnotationMatcher

字段名类型说明
typeClassMatcher匹配的注解类型
targetElementTypesTargetElementTypesMatcher匹配的注解声明的元素类型列表
policyRetentionPolicyType匹配的注解声明的保留策略
elementsAnnotationElementsMatcher匹配的注解声明的元素列表
usingStringsCollection<StringMatcher>注解中使用的字符串列表

AnnotationsMatcher

字段名类型说明
annotationsCollection<AnnotationMatcher>匹配的注解列表
matchTypeMatchType匹配模式
countIntRange匹配的注解数量

ClassMatcher

字段名类型说明
sourceStringMatcher类的源码文件名,即 smali 中的 .source 字段
classNameStringMatcher类的名称
modifiersAccessFlagsMatcher类的修饰符
superClassClassMatcher类的父类
interfacesInterfacesMatcher类的接口列表
annotationsAnnotationsMatcher类的注解列表
fieldsFieldsMatcher类的字段列表
methodsMethodsMatcher类的方法列表
usingStringsCollection<StringMatcher>类中使用的字符串列表

InterfacesMatcher

字段名类型说明
interfacesCollection<ClassMatcher>匹配的接口列表
matchTypeMatchType匹配模式
countIntRange匹配的接口数量

FieldMatcher

字段名类型说明
nameStringMatcher字段的名称
modifiersAccessFlagsMatcher字段的修饰符
declaredClassClassMatcher字段的声明类
typeClassMatcher字段的类型
annotationsAnnotationsMatcher字段的注解
readMethodsMethodsMatcher读取该字段的方法列表
writeMethodsMethodsMatcher设置该字段的方法列表

FieldsMatcher

字段名类型说明
fieldsCollection<FieldMatcher>匹配的字段列表
matchTypeMatchType匹配模式
countIntRange匹配的字段数量

MethodMatcher

字段名类型说明
nameStringMatcher方法的名称
modifiersAccessFlagsMatcher方法的修饰符
declaredClassClassMatcher方法的声明类
returnTypeClassMatcher方法的返回值类型
paramsParametersMatcher方法的参数列表
annotationsAnnotationsMatcher方法的注解
opCodesOpcodesMatcher方法的操作码列表
usingStringsCollection<StringMatcher>方法中使用的字符串列表
usingFieldsCollection<UsingFieldMatcher>方法中使用的字段列表
usingNumbersCollection<Number>方法中使用的数字列表
invokeMethodsMethodsMatcher方法中调用的方法列表
callerMethodsMethodsMatcher调用了该方法的方法列表

MethodsMatcher

字段名类型说明
methodsCollection<MethodMatcher>匹配的方法列表
matchTypeMatchType匹配模式
countIntRange匹配的方法数量

ParameterMatcher

字段名类型说明
typeClassMatcher参数的类型
annotationsAnnotationsMatcher参数的注解

ParametersMatcher

字段名类型说明
paramsCollection<ParameterMatcher>匹配的参数列表
countIntRange匹配的参数数量

StringMatchersGroup

字段名类型说明
groupNameString分组名称
matchersCollection<StringMatcher>该查询分组匹配对象所使用的字符串列表

UsingFieldMatcher

字段名类型说明
fieldFieldMatcher匹配的字段
usingTypeUsingType使用类型

EncodeValueByte

字段名类型说明
valueByte字节

EncodeValueShort

字段名类型说明
valueShort短整型

EncodeValueChar

字段名类型说明
valueChar字符

EncodeValueInt

字段名类型说明
valueInt整型

EncodeValueLong

字段名类型说明
valueLong长整型

EncodeValueFloat

字段名类型说明
valueFloat浮点型

EncodeValueDouble

字段名类型说明
valueDouble双精度浮点

EncodeValueString

字段名类型说明
valueString字符串

EncodeValueNull

该对象无字段。

EncodeValueBoolean

字段名类型说明
valueBoolean布尔值

枚举相关

AnnotationEncodeValueType

字段名类型说明
ByteValueEnum字节类型
ShortValueEnum短整型类型
CharValueEnum字符类型
IntValueEnum整型类型
LongValueEnum长整型类型
FloatValueEnum浮点类型
DoubleValueEnum双精度浮点类型
StringValueEnum字符串类型
TypeValueEnum类型类型
MethodValueEnum方法类型
EnumValueEnum枚举类型
ArrayValueEnum数组类型
AnnotationValueEnum注解类型
NullValueEnum空类型
BoolValueEnum布尔类型

AnnotationVisibilityType

详情参照 Visibility values在新窗口中打开

字段名类型说明
BuildEnum构建时可见
RuntimeEnum运行时可见
SystemEnum系统可见

MatchType

字段名类型说明
ContainsEnum包含
EqualsEnum等于

NumberEncodeValueType

字段名类型说明
ByteValueEnum字节类型
ShortValueEnum短整型类型
CharValueEnum字符类型
IntValueEnum整型类型
LongValueEnum长整型类型
FloatValueEnum浮点类型
DoubleValueEnum双精度浮点类型

OpCodeMatchType

字段名类型说明
ContainsEnum包含
StartsWithEnum以...开头
EndsWithEnum以...结尾
EqualsEnum等于

RetentionPolicyType

该 Enum 与 java.lang.annotation.RetentionPolicy 保持对应。

字段名类型说明
SourceEnum源码级别
ClassEnum类级别
RuntimeEnum运行时级别

StringMatchType

字段名类型说明
ContainsEnum包含
StartsWithEnum以...开头
EndsWithEnum以...结尾
SimilarRegexEnum类正则匹配,只支持 ^$
EqualsEnum等于

TargetElementType

该 Enum 与 java.lang.annotation.ElementType 保持对应。

字段名类型说明
TypeEnum类型
FieldEnum字段
MethodEnum方法
ParameterEnum参数
ConstructorEnum构造方法
LocalVariableEnum局部变量
AnnotationTypeEnum注解类型
PackageEnum
TypeParameterEnum类型参数
TypeUseEnum类型使用

UsingType

字段名类型说明
AnyEnum任意
GetEnum获取
SetEnum设置
+ + + diff --git a/zh-cn/index.html b/zh-cn/index.html new file mode 100644 index 00000000..dcfb9379 --- /dev/null +++ b/zh-cn/index.html @@ -0,0 +1,113 @@ + + + + + + + + + 首页 | DexKit + + + + +

DexKit

一个使用 C++ 实现的高性能运行时 dex 解析库

快速上手 API KDoc 在新窗口中打开

优雅简洁

完全使用 Kotlin DSL 打造的人性化 API,可以支持嵌套复杂查询,同时对 Java 也提供了良好的支持。

性能卓越

底层使用 C++ 实现,性能卓越,同时在多线程的基础上使用多种算法对进行优化,能够在极短时间内完成复杂的搜索。

可跨平台

提供多平台支持,例如在 Windows、Linux 或者 MacOS 中测试后,代码可以直接迁移至 Android 平台。

极致体验,拒绝繁琐

样例 APP 代码

假设这是一个宿主 APP 的被混淆后的代码,我们需要对这个方法的 hook 进行动态适配,由于混淆的存在,可能每个版本方法名以及类名都会发生变化。

public class abc {
+    
+    public boolean cvc() {
+        boolean b = false;
+        // ...
+        Log.d("VipCheckUtil", "userInfo: xxxx");
+        // ...
+        return b;
+    }
+}
+

DexKit 可以轻松解决这个问题!

Hook 代码

通过创建 DexKitBridge 实例,我们可以对 APP 的 dex 进行特定的查找

注意

对于 DexKitBridge 只需要创建一个实例。 如果您不希望使用 try-with-resources 或者 kotlin .use 自动关闭 DexKitBridge 实例,需要自行维护其生命周期。请确保在不需要时调用 .close() 方法以防止内存泄漏。

class AppHooker {
+    companion object {
+        init { System.loadLibrary("dexkit") }
+    }
+    
+    private var hostClassLoader: ClassLoader
+    
+    public constructor(loadPackageParam: LoadPackageParam) {
+        this.hostClassLoader = loadPackageParam.classLoader
+        val apkPath = loadPackageParam.appInfo.sourceDir
+        // DexKit 创建是一项耗时操作,请不要重复创建。如果需要全局使用,
+        // 请自行管理生命周期,确保在不需要时调用 .close() 方法以防止内存泄漏。
+        // 这里使用 `Closable.use` 语法糖自动关闭 DexKitBridge 实例
+        DexKitBridge.create(apkPath).use { bridge ->
+            isVipHook(bridge)
+            // Other hook ...
+        }
+    }
+    
+    private fun isVipHook(bridge: DexKitBridge) {
+        val methodData = bridge.findMethod {
+            matcher {
+                // 一切条件都是可选的,您可以自由的组合
+                modifiers = Modifier.PUBLIC
+                returnType = "boolean"
+                paramCount = 0
+                usingStrings("VipCheckUtil", "userInfo:")
+            }
+        }.single()
+        val method: Method = methodData.getMethodInstance(hostClassLoader)
+        XposedBridge.hookMethod(method, XC_MethodReplacement.returnConstant(true))
+    }
+    
+    // ...
+}
+
class AppHooker {
+    static {
+        System.loadLibrary("dexkit");
+    }
+    
+    private ClassLoader hostClassLoader;
+    
+    public AppHooker(LoadPackageParam loadPackageParam) {
+        this.hostClassLoader = loadPackageParam.classLoader;
+        String apkPath = loadPackageParam.appInfo.sourceDir;
+        // DexKit 创建是一项耗时操作,请不要重复创建。如果需要全局使用,
+        // 请自行管理生命周期,确保在不需要时调用 .close() 方法以防止内存泄漏。
+        // 这里使用 `try-with-resources` 语法糖自动关闭 DexKitBridge 实例
+        try (DexKitBridge bridge = DexKitBridge.create(apkPath)) {
+            isVipHook(bridge);
+            // Other hook ...
+        }
+    }
+    
+    private void isVipHook(DexKitBridge bridge) {
+        MethodData methodData = bridge.findMethod(FindMethod.create()
+            .matcher(MethodMatcher.create()
+                // 一切条件都是可选的,您可以自由的组合
+                .modifiers(Modifier.PUBLIC)
+                .paramCount(0)
+                .returnType("boolean")
+                .usingStrings("VipCheckUtil", "userInfo:")
+            )
+        ).single();
+        Method method = methodData.getMethodInstance(hostClassLoader);
+        XposedBridge.hookMethod(method, XC_MethodReplacement.returnConstant(true));
+    }
+    
+    // ...
+}
+

就是如此简单!

+ + +