<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Everett — personal blog · 中文</title>
    <description>Building software, exploring how things work, and lessons from work and life.</description>
    <language>zh-Hans</language>
    <link>https://asp.run/</link>
    <atom:link href="https://asp.run/feed-zh.xml" rel="self" type="application/rss+xml"/>
    <lastBuildDate>Sun, 15 Nov 2026 18:03:56 +0000</lastBuildDate>
    
      
      
      
      
      
      
      
      
      
      
      <item>
        <title>使用 deno_core 开发 JavaScript 运行时（编译）</title>
        <description>
        
        
        
        &lt;p&gt;编译：Everett。原文作者：Bartek Iwańczuk · 2022-07-29 · &lt;a href=&quot;https://deno.com/blog/roll-your-own-javascript-runtime&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; class=&quot;&quot;&gt;Roll your own JavaScript runtime &lt;span aria-hidden=&quot;true&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;
&lt;/p&gt;
        
        &lt;p&gt;历史文章：2022 年 7 月的教程，使用 Rust 1.62、deno_core 0.142.0 与 tokio 1.19.2。 未按当前版本重新验证。&lt;/p&gt;
        
&lt;blockquote&gt;
  &lt;p&gt;如果不了解Deno，可以先看看官网 https://deno.land/ 。简单来说，Deno是一个更安全的Node （名字都是反过来的,no_de -&amp;gt; de_no），和Node是一个创始人。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;前两天，Deno博客发布了一篇文章《Roll your own JavaScript runtime》&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;https://deno.com/blog/roll-your-own-javascript-runtime&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;https://asp.run/media/16592798557784.jpg&quot; alt=&quot;&quot; width=&quot;1592&quot; height=&quot;228&quot; /&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;看到略有“冲动”，今天就给大家翻译一波。
注意：翻译不是按字原版翻译，会略做小修改，保持原意不变。
所谓“略有冲动”，其实是我半年前也看了一眼Deno文档中的 Embedding Deno https://deno.land/manual/embedding_deno 然而这里啥也木有写，就直接让去看 deno_core 的文档 https://crates.io/crates/deno_core 也木有个入门教程啥的，当时由于我的Rust技术太菜，也就没继续玩耍下去。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;好了废话不多说，开始翻译。&lt;/p&gt;

&lt;h1 id=&quot;正文&quot;&gt;正文&lt;/h1&gt;

&lt;p&gt;这篇文章介绍如何创建一个自定义的JavaScript运行时。叫做runjs。可以把它想象成一个非常简单的Deno。这篇文章的一个目标是开发一个命令行程序，实现执行本地的JavaScript文件，可以读文件、写文件、删文件，以及一个console API。&lt;/p&gt;

&lt;p&gt;开始咯。&lt;/p&gt;

&lt;h1 id=&quot;必备条件&quot;&gt;必备条件&lt;/h1&gt;

&lt;p&gt;这篇教程假设读者掌握了以下知识：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Rust的基础知识&lt;/li&gt;
  &lt;li&gt;JavaScript事件循环的基础知识&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;确保电脑上安装了Rust（和cargo，cargo会自动安装），版本至少1.62.0 。可以访问 https://www.rust-lang.org/learn/get-started 来安装。&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ cargo --version
cargo 1.62.0 (a748cf5a3 2022-06-08)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;创建工程&quot;&gt;创建工程&lt;/h1&gt;

&lt;p&gt;首先，让我们创建一个新的Rust项目，名为runjs：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ cargo new runjs
     Created binary (application) package
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;进入runjs文件夹，并用编辑器打开。确保一切正常。&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ cd runjs
$ cargo run
   Compiling runjs v0.1.0 (/Users/ib/dev/runjs)
    Finished dev [unoptimized + debuginfo] target(s) in 1.76s
     Running `target/debug/runjs`
Hello, world!
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;哦啦！现在开始创建咱们自己的JavaScript运行时啦。&lt;/p&gt;

&lt;h1 id=&quot;依赖库&quot;&gt;依赖库&lt;/h1&gt;

&lt;p&gt;下一步，添加依赖 deno_core 和 tokio 。&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ cargo add deno_core
    Updating crates.io index
      Adding deno_core v0.142.0 to dependencies.
$ cargo add tokio --features=full
    Updating crates.io index
      Adding tokio v1.19.2 to dependencies.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;现在&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Cargo.toml&lt;/code&gt;文件的内容应该是下面这样：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[package]
name = &quot;runjs&quot;
version = &quot;0.1.0&quot;
edition = &quot;2021&quot;

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
deno_core = &quot;0.142.0&quot;
tokio = { version = &quot;1.19.2&quot;, features = [&quot;full&quot;] }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;deno_core&lt;/code&gt; 是Deno团队开发的一个Rust库（crate），它抽象了V8 JavaScript引擎的接口。V8是一个包含很多API的复杂工程，为了更简单的使用V8，deno_core提供了JsRuntime结构，封装了V8引擎的实例（也叫做Isolate），支持了事件循环。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tokio&lt;/code&gt;是一个异步Rust运行时，我们用它实现事件循环（event loop）。Tokio可以用来和系统的socket和文件系统交互。deno_core和tokio两者一起可以实现JavaScript的Promise映射到Rust的Future（也就是JS中的async/await映射到Rust的async/await）。&lt;/p&gt;

&lt;p&gt;有了JavaScript引擎和一个事件循环之后，我们就能创建一个JavaScript运行时了。&lt;/p&gt;

&lt;h1 id=&quot;hellorunjs&quot;&gt;Hello,runjs!&lt;/h1&gt;

&lt;p&gt;现在开始写一个异步的Rust函数，创建一个JsRuntime实例用于JavaScript的执行。&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;// main.rs
use std::rc::Rc;
use deno_core::error::AnyError;

async fn run_js(file_path: &amp;amp;str) -&amp;gt; Result&amp;lt;(), AnyError&amp;gt; {
  let main_module = deno_core::resolve_path(file_path)?;
  let mut js_runtime = deno_core::JsRuntime::new(deno_core::RuntimeOptions {
      module_loader: Some(Rc::new(deno_core::FsModuleLoader)),
      ..Default::default()
  });

  let mod_id = js_runtime.load_main_module(&amp;amp;main_module, None).await?;
  let result = js_runtime.mod_evaluate(mod_id);
  js_runtime.run_event_loop(false).await?;
  result.await?
}

fn main() {
  println!(&quot;Hello, world!&quot;);
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里有很多可以展开说的。异步的run_js函数创建了一个JsRuntime实例，这个实例使用了基于文件系统的模块加载器（deno_core::FsModuleLoader）。接着，我们用js_rutime加载了一个模块（main_module），然后执行（mod_evaluate），以及运行起一个事件循环（run_event_loop）。&lt;/p&gt;

&lt;p&gt;这个run_js函数包含了JavaScript代码执行的所有生命周期。但首先，我们需要创建一个单线程的tokio运行时来执行run_js函数：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;// main.rs
fn main() {
  let runtime = tokio::runtime::Builder::new_current_thread()
    .enable_all()
    .build()
    .unwrap();
  if let Err(error) = runtime.block_on(run_js(&quot;./example.js&quot;)) {
    eprintln!(&quot;error: {}&quot;, error);
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;让我们开始执行一些JavaScript代码。创建一个example.js，我们让它输出Hello runjs!：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;// example.js
Deno.core.print(&quot;Hello runjs!&quot;);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;注意：example.js在工程的根文件夹，cargo run 会让根文件夹作为工作目录(working directory)。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;https://asp.run/media/16592798777424.jpg&quot; alt=&quot;&quot; width=&quot;414&quot; height=&quot;388&quot; /&gt;&lt;/p&gt;

&lt;p&gt;注意这里我们用了&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Deno.core&lt;/code&gt;中的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;print&lt;/code&gt;函数，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Deno.core&lt;/code&gt;是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;deno_core&lt;/code&gt;提供的一个全局有效的内置对象。&lt;/p&gt;

&lt;p&gt;现在运行。&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.05s
     Running `target/debug/runjs`
Hello runjs!⏎
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;成功啦！我们仅用了25行代码就创建了一个可以执行本地文件的简单的JavaScript运行时。当然这个运行时现在还做不了太多事情（例如，不支持console.log）。现在我们已经把V8 JavaScript引擎和tokio集成到了我们的工程中。&lt;/p&gt;

&lt;h1 id=&quot;添加console-api&quot;&gt;添加console API&lt;/h1&gt;

&lt;p&gt;让我们开始实现console API。首先创建&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src/runtime.js&lt;/code&gt;文件，这个文件可以实现全局的console对象。&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;// src/runtime.js
((globalThis) =&amp;gt; {
  const core = Deno.core;

  function argsToMessage(...args) {
    return args.map((arg) =&amp;gt; JSON.stringify(arg)).join(&quot; &quot;);
  }

  globalThis.console = {
    log: (...args) =&amp;gt; {
      core.print(`[out]: ${argsToMessage(...args)}\n`, false);
    },
    error: (...args) =&amp;gt; {
      core.print(`[err]: ${argsToMessage(...args)}\n`, true);
    },
  };
})(globalThis);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;注意，这个runtime.js在src文件夹中。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;https://asp.run/media/16592798868051.jpg&quot; alt=&quot;&quot; width=&quot;410&quot; height=&quot;456&quot; /&gt;&lt;/p&gt;

&lt;p&gt;console.log和console.error函数可以接受多个参数，序列化为JSON（我们可以这样查看非原生的JS对象），并给每条消息加上前缀log或error。这是个“有点古老”的JavaScript文件了，就像在ES modules诞生之前在写浏览器里的JavaScript代码。&lt;/p&gt;

&lt;p&gt;这里用了&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/IIFE&quot;&gt;IIFE&lt;/a&gt;来执行代码是为了避免污染全局空间。否则，argsToMessage这个辅助函数会在我们运行时里全局有效了。&lt;/p&gt;

&lt;p&gt;现在让我们每次运行时都执行这段代码：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;let mut js_runtime = deno_core::JsRuntime::new(deno_core::RuntimeOptions {
  module_loader: Some(Rc::new(deno_core::FsModuleLoader)),
  ..Default::default()
});
+ js_runtime.execute_script(&quot;[runjs:runtime.js]&quot;,  include_str!(&quot;./runtime.js&quot;)).unwrap();
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;注意，这里include_str!是把main.rs同级目录下（也就是src目录下）的runtime.js的内容读取出来。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;最后，在example.js中可以调用新增的console API了。&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;- Deno.core.print(&quot;Hello runjs!&quot;);
+ console.log(&quot;Hello&quot;, &quot;runjs!&quot;);
+ console.error(&quot;Boom!&quot;);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;执行：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.05s
     Running `target/debug/runjs`
[out]: &quot;Hello&quot; &quot;runjs!&quot;
[err]: &quot;Boom!&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;添加文件系统api&quot;&gt;添加文件系统API&lt;/h1&gt;

&lt;p&gt;首先更新runtime.js文件：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;};

+ globalThis.runjs = {
+   readFile: (path) =&amp;gt; {
+     return core.opAsync(&quot;op_read_file&quot;, path);
+   },
+   writeFile: (path, contents) =&amp;gt; {
+     return core.opAsync(&quot;op_write_file&quot;, path, contents);
+   },
+   removeFile: (path) =&amp;gt; {
+     return core.opSync(&quot;op_remove_file&quot;, path);
+   },
+ };

})(globalThis);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我们添加了一个新的全局对象runjs，有三个方法：readFile writeFile和removeFile。前两个是异步的，最后一个是同步的。&lt;/p&gt;

&lt;p&gt;你估计要纳闷&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;core.opAsync&lt;/code&gt;和&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;core.opSync&lt;/code&gt;是什么了，它们是deno_core提供的绑定JavaScript和Rust函数的机制。当JavaScript中调用它们时，deno_core将查找有&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#[op]&lt;/code&gt;属性的同名Rust函数。&lt;/p&gt;

&lt;p&gt;让我们更新main.rs来看看实际效果：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;+ use deno_core::op;
+ use deno_core::Extension;
use deno_core::error::AnyError;
use std::rc::Rc;

+ #[op]
+ async fn op_read_file(path: String) -&amp;gt; Result&amp;lt;String, AnyError&amp;gt; {
+     let contents = tokio::fs::read_to_string(path).await?;
+     Ok(contents)
+ }
+
+ #[op]
+ async fn op_write_file(path: String, contents: String) -&amp;gt; Result&amp;lt;(), AnyError&amp;gt; {
+     tokio::fs::write(path, contents).await?;
+     Ok(())
+ }
+
+ #[op]
+ fn op_remove_file(path: String) -&amp;gt; Result&amp;lt;(), AnyError&amp;gt; {
+     std::fs::remove_file(path)?;
+     Ok(())
+ }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我们定义了三个JavaScript可以调用的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ops&lt;/code&gt;，但是要想让JavaScript代码可以调用，我们还需要给JsRuntime注册一个extension。&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;async fn run_js(file_path: &amp;amp;str) -&amp;gt; Result&amp;lt;(), AnyError&amp;gt; {
    let main_module = deno_core::resolve_path(file_path)?;
+    let runjs_extension = Extension::builder()
+        .ops(vec![
+            op_read_file::decl(),
+            op_write_file::decl(),
+            op_remove_file::decl(),
+        ])
+        .build();
    let mut js_runtime = deno_core::JsRuntime::new(deno_core::RuntimeOptions {
        module_loader: Some(Rc::new(deno_core::FsModuleLoader)),
+        extensions: vec![runjs_extension],
        ..Default::default()
    });
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我们可以通过&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Extensions&lt;/code&gt;来配置JsRuntime，暴露Rust函数给JavaScript，还可以做一些更高级的事情（加载额外的JavaScript代码等）。&lt;/p&gt;

&lt;p&gt;再次更新example.js：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;console.log(&quot;Hello&quot;, &quot;runjs!&quot;);
console.error(&quot;Boom!&quot;);
+
+ const path = &quot;./log.txt&quot;;
+ try {
+   const contents = await runjs.readFile(path);
+   console.log(&quot;Read from a file&quot;, contents);
+ } catch (err) {
+   console.error(&quot;Unable to read file&quot;, path, err);
+ }
+
+ await runjs.writeFile(path, &quot;I can write to a file.&quot;);
+ const contents = await runjs.readFile(path);
+ console.log(&quot;Read from a file&quot;, path, &quot;contents:&quot;, contents);
+ console.log(&quot;Removing file&quot;, path);
+ runjs.removeFile(path);
+ console.log(&quot;File removed&quot;);
+
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;运行它：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ cargo run
   Compiling runjs v0.1.0 (/Users/ib/dev/runjs)
    Finished dev [unoptimized + debuginfo] target(s) in 0.97s
     Running `target/debug/runjs`
[out]: &quot;Hello&quot; &quot;runjs!&quot;
[err]: &quot;Boom!&quot;
[err]: &quot;Unable to read file&quot; &quot;./log.txt&quot; {&quot;code&quot;:&quot;ENOENT&quot;}
[out]: &quot;Read from a file&quot; &quot;./log.txt&quot; &quot;contents:&quot; &quot;I can write to a file.&quot;
[out]: &quot;Removing file&quot; &quot;./log.txt&quot;
[out]: &quot;File removed&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;🎉恭喜，我们的runjs运行时现在支持文件系统了。注意我们这里用了很少的代码就实现了JavaScript调用Rust代码：deno_core把JavaScript和Rust之前的通信都搞定了。&lt;/p&gt;

&lt;h1 id=&quot;总结&quot;&gt;总结&lt;/h1&gt;

&lt;p&gt;在这个简短的例子中，我们实现了一个集成了强大JavaScript引擎（V8）和一个高效事件循环（tokio）的Rust项目。&lt;/p&gt;

&lt;p&gt;完整的例子代码，可以参考：denoland’s GitHub&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;https://github.com/denoland/roll-your-own-javascript-runtime&lt;/p&gt;
&lt;/blockquote&gt;

      </description>
        <pubDate>Sun, 31 Jul 2022 00:00:00 +0000</pubDate>
        <link>https://asp.run/2022/07/31/roll-your-own-javascript-runtime/?lang=zh</link>
        
        
        <guid isPermaLink="true">https://xnu.app/2022/07/31/roll-your-own-javascript-runtime/</guid>
        <category>rust</category><category>deno</category>
        <category>deno</category>
      </item>
    
      
      
      
      
      
      
      
      
      
      
      <item>
        <title>LLDB 断点 UIPasteboard 的所有方法</title>
        <description>
        
        
        
        
        
&lt;p&gt;iOS14 新增了一个保护隐私的功能，当前App读取其他App复制到剪贴板中的内容时，会有个短暂的提示。如下图：
&lt;img src=&quot;https://asp.run/media/15930990964673.jpg&quot; alt=&quot;-w431&quot; width=&quot;862&quot; height=&quot;250&quot; /&gt;&lt;/p&gt;

&lt;p&gt;可以通过断点 UIPasteboard 的所有方法来检查App中与剪贴板有关的所有行为。&lt;/p&gt;

&lt;p&gt;断点一个UIPasteboard的所有方法可以用如下命令：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;breakpoint set -r &apos;\[UIPasteboard .*\]$&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;先断点到main，然后lldb终端输入以上命令。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://asp.run/media/15930999653054.jpg&quot; alt=&quot;-w728&quot; width=&quot;1456&quot; height=&quot;520&quot; /&gt;&lt;/p&gt;

&lt;p&gt;（上图只截图了一部分UIPasteboard的方法）&lt;/p&gt;

&lt;h2 id=&quot;总结&quot;&gt;总结&lt;/h2&gt;

&lt;p&gt;嗯，大家小试一下～ 会发现除了我们自己调用，系统也偶尔会触发UIPasteboard相关调用。&lt;/p&gt;

&lt;p&gt;很有趣～&lt;/p&gt;

      </description>
        <pubDate>Thu, 25 Jun 2020 00:00:00 +0000</pubDate>
        <link>https://asp.run/2020/06/25/break-on-uipasteboard/?lang=zh</link>
        
        
        <guid isPermaLink="true">https://xnu.app/2020/06/25/break-on-uipasteboard/</guid>
        <category>iOS14</category>
        <category>lldb</category>
      </item>
    
      
      
      
      
      
      
      
      
      
      
      <item>
        <title>如何快速列出 App 的所有 +load 方法</title>
        <description>
        
        
        
        
        
&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Objective C +load&lt;/code&gt; 方法是个神奇又邪恶的方法。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;当新手得到它时，会惊讶于它的神奇。&lt;/li&gt;
  &lt;li&gt;当高手得到它时，会沉迷其中无法自拔。&lt;/li&gt;
  &lt;li&gt;当老手得到它时，会惊恐于它的邪恶。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;多数大型App都已经或者正在想办法去摆脱它。那么，如果快速看到你的App中有多少+load方法，看看中毒有多深。&lt;/p&gt;

&lt;p&gt;假设如下场景：&lt;/p&gt;

&lt;p&gt;一天你在愉快的用Xcode调试程序，&lt;/p&gt;

&lt;p&gt;打开Xcode，按下F5，&lt;/p&gt;

&lt;p&gt;突然，你想看一下App中的+load方法有多少？&lt;/p&gt;

&lt;p&gt;点击一下Pause，然后输入&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;br s -r &quot;\+\[.+ load\]$&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;https://asp.run/media/15880051383019.jpg&quot; alt=&quot;-w352&quot; width=&quot;704&quot; height=&quot;220&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然后输入&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;br list
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;https://asp.run/media/15880052076002.jpg&quot; alt=&quot;-w930&quot; width=&quot;1600&quot; height=&quot;808&quot; /&gt;&lt;/p&gt;

&lt;p&gt;或许你会惊讶，原来我的App中+load这么多（或者少）&lt;/p&gt;

&lt;h2 id=&quot;原理&quot;&gt;原理&lt;/h2&gt;

&lt;p&gt;使用了lldb的breakpoint命令。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://asp.run/media/15880053337758.jpg&quot; alt=&quot;-w576&quot; width=&quot;1152&quot; height=&quot;136&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;br s -r &quot;正则&quot;
就是 
breakpoint set -r &quot;正则&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;通过正则匹配符号设置断点。&lt;/p&gt;

&lt;h2 id=&quot;小问题&quot;&gt;小问题&lt;/h2&gt;

&lt;p&gt;那么想想，如果这些+load方法中的代码出现了crash，你的crash监控（bugly等等）能监控到吗？&lt;/p&gt;

&lt;p&gt;当然得到的90%的回答是：不会Crash的。&lt;/p&gt;

&lt;p&gt;让我想起了特朗普的一句话：我的”代码”很完美。&lt;/p&gt;

&lt;p&gt;哈哈 :)&lt;/p&gt;

&lt;h2 id=&quot;总结&quot;&gt;总结&lt;/h2&gt;

&lt;p&gt;很有趣:)&lt;/p&gt;

&lt;p&gt;哎，我好菜，我要去学习《基于桥的全量方法Hook》了&lt;/p&gt;

&lt;p&gt;http://satanwoo.github.io/2020/04/26/TrampolineHookOpenSource/&lt;/p&gt;

      </description>
        <pubDate>Tue, 28 Apr 2020 00:00:00 +0000</pubDate>
        <link>https://asp.run/2020/04/28/quick-list-load-method/?lang=zh</link>
        
        
        <guid isPermaLink="true">https://xnu.app/2020/04/28/quick-list-load-method/</guid>
        <category>lldb</category><category>load</category><category>breakpoint</category>
        <category>lldb</category>
      </item>
    
      
      
      
      
      
      
      
      
      
      
      <item>
        <title>LLDB：如何在函数 return 处下断点</title>
        <description>
        
        
        
        
        
&lt;p&gt;有一个代码行很多的复杂函数，内部有很多return，单步调试很慢，如何快速找到哪一行return了？&lt;/p&gt;

&lt;!-- more --&gt;

&lt;p&gt;例如代码：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;void foo() {
    int i = arc4random() %100;
    
    if (i &amp;gt; 30) {
        if (i &amp;lt; 40) {
            return;
        }
        if (i &amp;gt; 77) {
            return;
        }
        if (i &amp;lt; 66) {
            return;
        }
    }
    
    switch (i) {
        case 0:
            return;
        case 1:
            return;
        case 2:
            return;
        case 3:
            return;
        case 4:
            return;
        default:
            return;
    }
}

int main(int argc, const char * argv[]) {
    foo();
    return 0;
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;假设foo是个很长很复杂有很多return的函数，如何知道是通过哪一行return的呢？&lt;/p&gt;

&lt;p&gt;可以使用lldb的断点&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;breakpoint set -p return
或者
br set -p return
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;首先在foo第一行加断点&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://asp.run/media/15875739886610.jpg&quot; alt=&quot;&quot; width=&quot;520&quot; height=&quot;162&quot; /&gt;&lt;/p&gt;

&lt;p&gt;断点触发后，控制台输入 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;br set -p return&lt;/code&gt;
&lt;img src=&quot;https://asp.run/media/15875740456070.jpg&quot; alt=&quot;&quot; width=&quot;395&quot; height=&quot;190&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然后continue，就会断点到函数return的那一行了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://asp.run/media/15875741244090.jpg&quot; alt=&quot;&quot; width=&quot;479&quot; height=&quot;346&quot; /&gt;&lt;/p&gt;

&lt;p&gt;很有趣~&lt;/p&gt;

      </description>
        <pubDate>Thu, 23 Apr 2020 00:00:00 +0000</pubDate>
        <link>https://asp.run/2020/04/23/how-to-break-on-return/?lang=zh</link>
        
        
        <guid isPermaLink="true">https://xnu.app/2020/04/23/how-to-break-on-return/</guid>
        <category>lldb</category><category>return</category><category>breakpoint</category>
        <category>lldb</category>
      </item>
    
      
      
      
      
      
      
      
      
      
      
      <item>
        <title>C++ 类中持有的 Objective-C 对象何时 dealloc</title>
        <description>
        
        
        
        
        
&lt;p&gt;在使用Objective C++时，可以用C++的struct或者class存储Objective C对象。&lt;/p&gt;

&lt;!-- more --&gt;

&lt;p&gt;例如：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;struct CppStruct {
    MyObject *obj;
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;那么突然想，如果CppStruct析构了，MyObject会dealloc吗？不用多想，肯定也会。但如何做到的呢？&lt;/p&gt;

&lt;p&gt;写段代码验证下，&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
@interface MyObject : NSObject
@end
@implementation MyObject
- (instancetype)init {
    self = [super init];
    if (self) {
        NSLog(@&quot;MyObject init&quot;);
    }
    return self;
}
- (void)dealloc {
    NSLog(@&quot;MyObject dealloc&quot;);
}
@end

struct CppStruct {
    MyObject *obj;
    CppStruct() {
        NSLog(@&quot;CppStruct constructor&quot;);
    }
    ~CppStruct() {
        NSLog(@&quot;CppStruct destructor&quot;);
    }
};

CppStruct * CreateStruct() {
    CppStruct * s = new CppStruct();
    s-&amp;gt;obj = [[MyObject alloc] init];
    return s;
}

void FreeStruct(CppStruct *s) {
    delete s;
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        CppStruct *s = CreateStruct();
        FreeStruct(s);
    }
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如下输出&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://asp.run/media/15871350384682.jpg&quot; alt=&quot;&quot; width=&quot;292&quot; height=&quot;87&quot; /&gt;&lt;/p&gt;

&lt;p&gt;可知，MyObject确实dealloc了。&lt;/p&gt;

&lt;p&gt;那么，断点到dealloc，看看怎么调用到的。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://asp.run/media/15871351008102.jpg&quot; alt=&quot;&quot; width=&quot;331&quot; height=&quot;289&quot; /&gt;&lt;/p&gt;

&lt;p&gt;看到上图有两个 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~CppStruct()&lt;/code&gt;，其中一个调用了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;objc_storeStrong&lt;/code&gt;，objc_storeStrong 进一步触发了MyObject的dealloc。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://asp.run/media/15871352168452.jpg&quot; alt=&quot;&quot; width=&quot;721&quot; height=&quot;266&quot; /&gt;&lt;/p&gt;

&lt;p&gt;从上图可知，一定是编译器生成了objc_storeStrong调用的代码。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://asp.run/media/15871352826095.jpg&quot; alt=&quot;&quot; width=&quot;753&quot; height=&quot;310&quot; /&gt;&lt;/p&gt;

&lt;p&gt;看下Disassembly，确实生成了objc_storeStrong调用代码。
&lt;img src=&quot;https://asp.run/media/15871353987126.jpg&quot; alt=&quot;&quot; width=&quot;289&quot; height=&quot;103&quot; /&gt;&lt;/p&gt;

&lt;p&gt;进一步实锤。&lt;/p&gt;

&lt;p&gt;LLVM 怎么去生成的这个逻辑，就不去翻了哈。&lt;/p&gt;

&lt;h2 id=&quot;参考&quot;&gt;参考&lt;/h2&gt;

&lt;p&gt;上面调试中用了可编译的objc runtime，GitHub上有很多，可以如下搜索对应objc版本即可：&lt;/p&gt;

&lt;p&gt;https://github.com/search?q=objc+779.1
https://github.com/LGCooci/objc4_debug&lt;/p&gt;

&lt;h2 id=&quot;总结&quot;&gt;总结&lt;/h2&gt;

&lt;p&gt;以后可以大胆的用C++ struct 存储Objective C对象了。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;很有趣 :)&lt;/p&gt;

      </description>
        <pubDate>Fri, 17 Apr 2020 00:00:00 +0000</pubDate>
        <link>https://asp.run/2020/04/17/how-cpp-delete-dealloc-objc-object/?lang=zh</link>
        
        
        <guid isPermaLink="true">https://xnu.app/2020/04/17/how-cpp-delete-dealloc-objc-object/</guid>
        <category>objc</category><category>dealloc</category>
        <category>探索</category>
      </item>
    
      
      
      
      
      
      
      
      
      
      
      <item>
        <title>探索 WhatsApp 应用名称中的隐藏字符（LRM）</title>
        <description>
        
        
        
        
        
&lt;p&gt;欢迎大家观看这期的“走近科学之WhatsApp神秘符号”～&lt;/p&gt;

&lt;!-- more --&gt;

&lt;h2 id=&quot;案发现场&quot;&gt;案发现场&lt;/h2&gt;

&lt;p&gt;大概半年甚至更久之前，在使用frida-ios-dump时，偶然发现WhatsApp这个应用的名称有点奇怪。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;frida-ios-dump 是一款越狱iOS上的App砸壳工具（也可以列出iOS的应用列表）。
地址：https://github.com/AloneMonkey/frida-ios-dump&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;仔细看下图的WhatsApp：
&lt;img src=&quot;https://asp.run/media/15855012198617.jpg&quot; alt=&quot;&quot; width=&quot;371&quot; height=&quot;121&quot; /&gt;&lt;/p&gt;

&lt;p&gt;WhatsApp的名称左侧对齐与其他App不同。&lt;/p&gt;

&lt;p&gt;… 多少次匆匆擦肩而过 … 多少次视如不存在 …&lt;/p&gt;

&lt;p&gt;直到今天我终于好奇了一次，想看看这里为什么没有对齐。&lt;/p&gt;

&lt;h2 id=&quot;开始&quot;&gt;开始&lt;/h2&gt;

&lt;p&gt;砸，砸一下试试。&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;python dump.py net.whatsapp.WhatsApp
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;砸完，如下图。出现个问号。&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;?WhatsApp.ipa&lt;/code&gt;，问号是什么。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://asp.run/media/15855017374127.jpg&quot; alt=&quot;&quot; width=&quot;237&quot; height=&quot;86&quot; /&gt;&lt;/p&gt;

&lt;p&gt;准备&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mv&lt;/code&gt;到其他文件夹研究下，就在此刻…&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://asp.run/media/15855018704881.jpg&quot; alt=&quot;&quot; width=&quot;456&quot; height=&quot;81&quot; /&gt;&lt;/p&gt;

&lt;p&gt;神秘字符&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;\342\200\216&lt;/code&gt;出现了，正好奇着…&lt;/p&gt;

&lt;h2 id=&quot;一搜&quot;&gt;一搜&lt;/h2&gt;

&lt;p&gt;随手一搜 … 还真搜到了 :)&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://asp.run/media/15855020124999.jpg&quot; alt=&quot;&quot; width=&quot;725&quot; height=&quot;183&quot; /&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;https://graphemica.com/200E&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;打开一看，还真有含义咧～ &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;left-to-right mark&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://asp.run/media/15855021218820.jpg&quot; alt=&quot;&quot; width=&quot;920&quot; height=&quot;376&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;真相大白&quot;&gt;真相大白&lt;/h2&gt;

&lt;p&gt;Wikipedia也有解释&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The left-to-right mark (LRM) is a control character (an invisible formatting character) used in computerized typesetting (including word processing in a program like Microsoft Word) of text that contains a mixture of left-to-right text (such as English or Russian) and right-to-left text (such as Arabic, Persian or Hebrew). It is used to set the way adjacent characters are grouped with respect to text direction.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;https://en.wikipedia.org/wiki/Left-to-right_mark&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;贴个翻译：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;左至右符号（Left-to-right mark,LRM）是一种控制字符，或者说是不可见的排版符号。用于计算机的双向文稿排版中。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;我再大白话说一句。Left-to-right mark 是个不可见的符号，用来在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;从右向左&lt;/code&gt;的排版语言（例如阿拉伯语）中包含&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;从左向右&lt;/code&gt;的文字。&lt;/p&gt;

&lt;p&gt;下图的例子就比较清晰了：在使用了LRM符号后，阿拉伯语（从右向左）中包含了显示的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C++&lt;/code&gt;（从左向右）。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://asp.run/media/15855024011241.jpg&quot; alt=&quot;&quot; width=&quot;792&quot; height=&quot;236&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;扩展阅读&quot;&gt;扩展阅读&lt;/h2&gt;

&lt;p&gt;有 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Left-to-right mark&lt;/code&gt;，也有 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Right-to-left mark&lt;/code&gt;。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;https://en.wikipedia.org/wiki/Right-to-left_mark&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;进一步&quot;&gt;进一步&lt;/h2&gt;

&lt;p&gt;把WhatsApp的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Info.plist&lt;/code&gt;文件拿出来看一眼，似乎没什么特别。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://asp.run/media/15855026225466.jpg&quot; alt=&quot;&quot; width=&quot;487&quot; height=&quot;115&quot; /&gt;&lt;/p&gt;

&lt;p&gt;用二进制编辑器看一下。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://asp.run/media/15855027112567.jpg&quot; alt=&quot;&quot; width=&quot;617&quot; height=&quot;134&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;\342\200\216&lt;/code&gt; 就是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0xE2 0x80 0x8E (e2808e)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://asp.run/media/15855027969727.jpg&quot; alt=&quot;&quot; width=&quot;880&quot; height=&quot;444&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这样在代码中获取 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CFBundleDisplayName&lt;/code&gt; 并与其他本地化的语言拼接后，就能保证WhatsApp的顺序从左至右了。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;很有趣 :)&lt;/p&gt;

      </description>
        <pubDate>Mon, 30 Mar 2020 00:00:00 +0000</pubDate>
        <link>https://asp.run/2020/03/30/left-to-right-mark/?lang=zh</link>
        
        
        <guid isPermaLink="true">https://xnu.app/2020/03/30/left-to-right-mark/</guid>
        <category>unicode</category>
        <category>探索</category>
      </item>
    
      
      
      
      
      
      
      
      
      
      
      <item>
        <title>在 iPhone 上运行 Android 系统的体验</title>
        <description>
        
        
        
        &lt;p&gt;历史文章：2020 年 3 月在 iPhone 7 上体验的 Project Sandcastle。 未按当前版本重新验证。&lt;/p&gt;
        
&lt;p&gt;历史上第一次让Android系统运行到iPhone手机上。目前（2020年3月6日）的版本只支持iPhone7/7 Plus。（iOS系统版本无要求）&lt;/p&gt;

&lt;p&gt;Project Sandcastle: Android for the iPhone 
项目地址：https://projectsandcastle.org/&lt;/p&gt;

&lt;p&gt;正好手头有个iPhone7，体验了下。步骤简单总结下，分享给大家。理想的步骤如下，但由于macOS的安全机制，导致步骤2和3不那么轻松愉快。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;使用checkra1n越狱&lt;/li&gt;
  &lt;li&gt;运行start_mac.sh&lt;/li&gt;
  &lt;li&gt;运行setup_mac.sh&lt;/li&gt;
&lt;/ol&gt;

&lt;!-- more --&gt;

&lt;h2 id=&quot;越狱&quot;&gt;越狱&lt;/h2&gt;

&lt;p&gt;下载 https://checkra.in/ 按照步骤越狱。（iPhone使用USB连接Mac。）&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://asp.run/media/15834927122755.jpg&quot; alt=&quot;-w480&quot; width=&quot;960&quot; height=&quot;684&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://asp.run/media/15834927589010.jpg&quot; alt=&quot;-w480&quot; width=&quot;960&quot; height=&quot;684&quot; /&gt;
&lt;img src=&quot;https://asp.run/media/15834927957183.jpg&quot; alt=&quot;-w246&quot; width=&quot;492&quot; height=&quot;168&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://asp.run/media/15834928119092.jpg&quot; alt=&quot;-w480&quot; width=&quot;960&quot; height=&quot;684&quot; /&gt;&lt;/p&gt;

&lt;p&gt;越狱后，进入iOS。&lt;/p&gt;

&lt;h2 id=&quot;下载-android-build&quot;&gt;下载 Android Build&lt;/h2&gt;

&lt;p&gt;在 https://projectsandcastle.org/status 下载 Android Build。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://asp.run/media/15835109011989.jpg&quot; alt=&quot;&quot; width=&quot;890&quot; height=&quot;375&quot; /&gt;&lt;/p&gt;

&lt;p&gt;解压下载的文件。
&lt;img src=&quot;https://asp.run/media/15835110829022.jpg&quot; alt=&quot;-w234&quot; width=&quot;468&quot; height=&quot;404&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;setup_macsh-1&quot;&gt;setup_mac.sh&lt;/h2&gt;

&lt;p&gt;iPhone使用USB连接Mac。理想情况下，macOS上执行 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./setup_mac.sh&lt;/code&gt;即可。但我执行不太顺利。自带的iproxy和两个动态库的签名首次执行还需要信任。但其实功能就是iproxy的功能，因此下面手动把 setup_mac.sh 中的步骤执行。&lt;/p&gt;

&lt;p&gt;可以先ssh连接下iOS，确保手动连接成功。然后参考如下步骤。&lt;/p&gt;

&lt;p&gt;(1)&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;iproxy 2222 44
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;(2)
把isetup复制到iOS的/tmp/setup.sh&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;scp -P2222 -o StrictHostKeyChecking=no isetup root@localhost:/tmp/setup.sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;(3)
两个方法：&lt;/p&gt;

&lt;p&gt;一是，FQ。
或者，修改setup.sh中的网络连接测试的地址，比如专门测试网能不能上的baidu（看来老外也是对应的用google测试啊）&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://asp.run/media/15835114471189.jpg&quot; alt=&quot;-w627&quot; width=&quot;1254&quot; height=&quot;450&quot; /&gt;&lt;/p&gt;

&lt;p&gt;建议使用FQ的方法，因为这个脚本会下载470MB的文件，我家的Wi-Fi不FQ的话，下载这个文件很慢，FQ后貌似几分钟就好了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://asp.run/media/15835117141314.jpg&quot; alt=&quot;&quot; width=&quot;215&quot; height=&quot;37&quot; /&gt;&lt;/p&gt;

&lt;p&gt;(4) 执行 /tmp/setup.sh&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://asp.run/media/15835063026640.jpg&quot; alt=&quot;-w462&quot; width=&quot;924&quot; height=&quot;212&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;进入dfu-模式&quot;&gt;进入DFU 模式&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;iPhone关机。&lt;/li&gt;
  &lt;li&gt;同时按 音量下 + 关机键，10秒钟（尽量严格的10秒）。&lt;/li&gt;
  &lt;li&gt;松开 关机键，继续保持音量下。&lt;/li&gt;
  &lt;li&gt;此时手机屏幕会保持黑屏，表示进入了DFU模式。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;然后在DFU模式下，执行&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./start_mac.sh&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;理想情况下，执行完成就可以了。但现实很残酷，我见到了下面这个弹窗（内心一万匹羊驼飞奔而过，生怕把手机搞坏了……），当然要点Cancel。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://asp.run/media/15835119951796.jpg&quot; alt=&quot;-w420&quot; width=&quot;840&quot; height=&quot;394&quot; /&gt;&lt;/p&gt;

&lt;p&gt;进入 System Preferences -&amp;gt; Security &amp;amp; Privacy -&amp;gt; General， 点 Allow Anyway。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://asp.run/media/15835120714520.jpg&quot; alt=&quot;-w608&quot; width=&quot;1216&quot; height=&quot;306&quot; /&gt;&lt;/p&gt;

&lt;p&gt;此时还不行，为了预防万一，先命令后执行一次&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;./load-linux.mac
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然后就可以点Open了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://asp.run/media/15835077352704.jpg&quot; alt=&quot;-w469&quot; width=&quot;938&quot; height=&quot;422&quot; /&gt;&lt;/p&gt;

&lt;p&gt;但此时重新执行&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;start_mac.sh&lt;/code&gt;似乎不能”恢复继续”了，内心X#@%$$。“聪明”的我看了下start_mac.sh的代码，看来可以执行执行这一步。&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;./load-linux.mac Android.lzma dtbpack
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;终于成功，iPhone上启动了Android系统。&lt;/p&gt;

&lt;h2 id=&quot;截图&quot;&gt;截图&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;https://asp.run/media/15835124510615.jpg&quot; alt=&quot;&quot; width=&quot;375&quot; height=&quot;594&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://asp.run/media/15835124757092.jpg&quot; alt=&quot;&quot; width=&quot;387&quot; height=&quot;631&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;总结&quot;&gt;总结&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;运行比较卡。&lt;/li&gt;
  &lt;li&gt;重启就恢复iOS。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;还是挺有趣的，视频可抖音搜索 everettjf 查看。&lt;/p&gt;

      </description>
        <pubDate>Fri, 06 Mar 2020 00:00:00 +0000</pubDate>
        <link>https://asp.run/2020/03/06/android-on-iphone/?lang=zh</link>
        
        
        <guid isPermaLink="true">https://xnu.app/2020/03/06/android-on-iphone/</guid>
        <category>sandcastle</category>
        <category>尝鲜</category>
      </item>
    
      
      
      
      
      
      
      
      
      
      
      <item>
        <title>将 iOS 标准输出重定向到 UITextView</title>
        <description>
        
        
        
        &lt;p&gt;历史文章：2020 年的 iOS 标准输出实验，涉及 C 库的内部实现细节。 未按当前版本重新验证。&lt;/p&gt;
        
&lt;p&gt;看到一处代码挺有意思，iOS项目中把printf打印的内容重定向到了UITextView中。&lt;/p&gt;

&lt;!-- more --&gt;

&lt;p&gt;代码抽出来后，挺简单。&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;

static print_cbk_t gPrintFunc;

static int stdout_redirect(void* prefix, const char* buffer, int size)
{
    if (gPrintFunc)
        gPrintFunc(buffer, size);
    return size;
}

void set_redirect_output(print_cbk_t f)
{
    gPrintFunc = f;

    setvbuf(stdout, 0, _IOLBF, 0); // stdout: line-buffered
    setvbuf(stderr, 0, _IONBF, 0); // stderr: unbuffered

    stdout-&amp;gt;_write = stdout_redirect;
    stderr-&amp;gt;_write = stdout_redirect;
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然后在ViewController中类似如下使用：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
static UITextView *sTextView = nil;

static void current_output(const char* buff, int len) {
    NSString *text = [sTextView text];
    text = [text stringByAppendingFormat:@&quot;%s&quot;,buff];
    [sTextView setText:text];
}

@interface ViewController ()
@property (weak, nonatomic) IBOutlet UITextView *textView;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    sTextView = self.textView;
    
    set_redirect_output(current_output);
        
    printf(&quot;hello\n&quot;);
    printf(&quot;hello\n&quot;);
    printf(&quot;hello\n&quot;);
    printf(&quot;hello\n&quot;);

}
@end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;运行后，printf就到了UITextView中。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://asp.run/media/15832504339122.jpg&quot; alt=&quot;&quot; width=&quot;329&quot; height=&quot;144&quot; /&gt;&lt;/p&gt;

&lt;p&gt;原理似乎也没啥可讲的，本身c库就提供支持。&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    setvbuf(stdout, 0, _IOLBF, 0); // stdout: line-buffered
    setvbuf(stderr, 0, _IONBF, 0); // stderr: unbuffered
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然后这两个函数把标准输出设置为“按行缓冲”和“不缓冲”。也就是进入stdout和stderr的内容都按行或直接输出。这样UITextView就接收到的比较及时了。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;水文写起来就是轻松愉快啊~ 有趣就好~&lt;/p&gt;

      </description>
        <pubDate>Tue, 03 Mar 2020 00:00:00 +0000</pubDate>
        <link>https://asp.run/2020/03/03/redirect-printf-to-textview/?lang=zh</link>
        
        
        <guid isPermaLink="true">https://xnu.app/2020/03/03/redirect-printf-to-textview/</guid>
        <category>redirect</category><category>stdout</category><category>stderr</category>
        <category>stdout</category>
      </item>
    
      
      
      
      
      
      
      
      
      
      
      <item>
        <title>使用 __asm__ 重命名符号</title>
        <description>
        
        
        
        
        
&lt;p&gt;objc 源码中有如下代码 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__asm__(&quot;_objc_retain&quot;)&lt;/code&gt;，这个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__asm__&lt;/code&gt;是啥来？&lt;/p&gt;

&lt;!-- more --&gt;

&lt;h2 id=&quot;发现&quot;&gt;发现&lt;/h2&gt;

&lt;p&gt;源码中如下&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://asp.run/media/15820819662136.jpg&quot; alt=&quot;&quot; width=&quot;486&quot; height=&quot;337&quot; /&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;objc 源码下载 https://opensource.apple.com/tarballs/objc4/&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;搜索一番，发现是可以rename symbol，写个测试代码：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
void foo(void) __asm__(&quot;_objc_release&quot;);
void foo(void)  {
    printf(&quot;hello world&quot;);
}
int main(int argc, const char * argv[]) {
    foo();
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;调试看下&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://asp.run/media/15820826016870.jpg&quot; alt=&quot;&quot; width=&quot;659&quot; height=&quot;250&quot; /&gt;&lt;/p&gt;

&lt;p&gt;从上图可知，callstack中的symbol不是 foo，而是 _objc_release。&lt;/p&gt;

&lt;h2 id=&quot;变量&quot;&gt;变量&lt;/h2&gt;

&lt;p&gt;从下面的链接又可知，还可以rename变量的symbol。&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;int counter __asm__(&quot;counter_v1&quot;) = 0;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0491f/Cacgegch.html&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;总结&quot;&gt;总结&lt;/h2&gt;

&lt;p&gt;似乎可以拿来给dylib做个简单的export symbol混淆？&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://asp.run/media/15820829854361.jpg&quot; alt=&quot;&quot; width=&quot;787&quot; height=&quot;200&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;参考&quot;&gt;参考&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.kui0097a/armcc_bcfggcdh.htm&lt;/li&gt;
  &lt;li&gt;http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0491f/Cacgegch.html&lt;/li&gt;
  &lt;li&gt;https://stackoverflow.com/questions/1034852/adding-leading-underscores-to-assembly-symbols-with-gcc-on-win32&lt;/li&gt;
&lt;/ul&gt;

      </description>
        <pubDate>Wed, 19 Feb 2020 00:00:00 +0000</pubDate>
        <link>https://asp.run/2020/02/19/asm-rename-symbols/?lang=zh</link>
        
        
        <guid isPermaLink="true">https://xnu.app/2020/02/19/asm-rename-symbols/</guid>
        
        <category></category><category></category>
      </item>
    
      
      
      
      
      
      
      
      
      
      
      <item>
        <title>函数中的 C++ 静态变量初始化是线程安全的吗？</title>
        <description>
        
        
        
        
        
&lt;p&gt;先说结论，是线程安全，而且有double-checked locking，性能还不错。&lt;/p&gt;

&lt;!-- more --&gt;

&lt;h2 id=&quot;探索&quot;&gt;探索&lt;/h2&gt;

&lt;p&gt;通常写C++ Singleton模式时，会很流畅的写下如下代码：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;class Hello {
public:
    static Hello &amp;amp; instance() {
        static Hello obj;
        return obj;
    }
    
    Hello() {
        printf(&quot;constructor&quot;);
    }
    ~Hello() {
        printf(&quot;destructor&quot;);
    }
    
public:
    int x = 0;
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;那么当使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Hello::instance()&lt;/code&gt; 时，是线程安全吗？ Hello 的构造函数能确保执行一次吗？&lt;/p&gt;

&lt;p&gt;把代码扔到 Hopper 看一下&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://asp.run/media/15819530050437.jpg&quot; alt=&quot;-w1201&quot; width=&quot;1600&quot; height=&quot;237&quot; /&gt;&lt;/p&gt;

&lt;p&gt;看来是有锁。那就是线程安全咯 :)&lt;/p&gt;

&lt;h2 id=&quot;进一步&quot;&gt;进一步&lt;/h2&gt;

&lt;p&gt;看到这几个方法&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;__cxa_guard_acquire
__cxa_atexit
__cxa_guard_release
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__cxa_atexit&lt;/code&gt; 可以是为了在进程结束时调用析构函数。&lt;/p&gt;

&lt;p&gt;一番搜索可以找到这个源码 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cxa_guard.cxx&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;https://opensource.apple.com/source/libcppabi/libcppabi-14/src/cxa_guard.cxx&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://asp.run/media/15819532682759.jpg&quot; alt=&quot;-w609&quot; width=&quot;1218&quot; height=&quot;740&quot; /&gt;&lt;/p&gt;

&lt;p&gt;通过obj_guard 实现了一个double-checked locking&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://asp.run/media/15819533607278.jpg&quot; alt=&quot;-w647&quot; width=&quot;1294&quot; height=&quot;956&quot; /&gt;&lt;/p&gt;

&lt;p&gt;inUse会通过obj_guard指向内存的第一个地址存取0和1，实现第一次check。&lt;/p&gt;

&lt;p&gt;Hopper中找到这个obj_guard&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://asp.run/media/15819535672052.jpg&quot; alt=&quot;&quot; width=&quot;1600&quot; height=&quot;786&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://asp.run/media/15819535936394.jpg&quot; alt=&quot;-w705&quot; width=&quot;1410&quot; height=&quot;462&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这个guard variable 是个全局变量。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;总结&quot;&gt;总结&lt;/h2&gt;

&lt;p&gt;以后可以愉快的使用这个写法了&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    static Hello &amp;amp; instance() {
        static Hello obj;
        return obj;
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

      </description>
        <pubDate>Mon, 17 Feb 2020 00:00:00 +0000</pubDate>
        <link>https://asp.run/2020/02/17/how-cpp-static-object-in-function-work/?lang=zh</link>
        
        
        <guid isPermaLink="true">https://xnu.app/2020/02/17/how-cpp-static-object-in-function-work/</guid>
        <category>cpp</category><category>threadsafe</category><category>local-object</category>
        <category>cpp</category>
      </item>
    
  </channel>
</rss>

