82 lines
2.7 KiB
Markdown
82 lines
2.7 KiB
Markdown
# 12. ucode Programming Patterns & Conventions
|
|
|
|
A developer's guide to writing safe, high-performance, and idiomatic **ucode** modules for Tachyon on OpenWrt.
|
|
|
|
---
|
|
|
|
## 1. ucode vs JavaScript: Critical Distinctions
|
|
|
|
While ucode syntax resembles JavaScript, its runtime semantics differ significantly:
|
|
|
|
1. **Variables & Scope**:
|
|
* Always declare variables with `let` or `const`.
|
|
* Unassigned variables are `null` (not `undefined`).
|
|
2. **Arrays vs Dictionaries**:
|
|
* Arrays: `[1, 2, 3]`. Iterated with `for (let item in array)`.
|
|
* Dictionaries (Objects): `{"key": "value"}`. Iterated with `for (let key, val in dict)`.
|
|
3. **Regular Expressions**:
|
|
* Use `match(str, /regex/)` or `replace(str, /regex/g, "replacement")`.
|
|
* Global flag `/g` works with `replace()`, while `match()` returns capture groups array `[full_match, group1, ...]`.
|
|
4. **Boolean Coercion**:
|
|
* Empty strings `""`, `0`, and `null` are falsy. Everything else is truthy.
|
|
|
|
---
|
|
|
|
## 2. Mandatory Core Design Patterns
|
|
|
|
### 2.1. Atomic Configuration Writes
|
|
Never overwrite active configuration files in-place. Always write to a temporary file in `/tmp/` and atomically rename:
|
|
|
|
```ucode
|
|
let fs = require("fs");
|
|
|
|
function write_atomic(path, content) {
|
|
let tmp_path = path + ".tmp." + int(time());
|
|
let f = fs.open(tmp_path, "w");
|
|
if (!f) return false;
|
|
|
|
f.write(content);
|
|
f.close();
|
|
|
|
return fs.rename(tmp_path, path);
|
|
}
|
|
```
|
|
|
|
### 2.2. Shell Argument Quoting
|
|
When executing external shell commands, all user inputs must be quoted safely:
|
|
|
|
```ucode
|
|
function shell_quote(value) {
|
|
return "'" + replace("" + value, /'/g, "'\\''") + "'";
|
|
}
|
|
|
|
function safe_exec(cmd_args) {
|
|
let parts = [];
|
|
for (let arg in cmd_args)
|
|
push(parts, shell_quote(arg));
|
|
return system(join(" ", parts));
|
|
}
|
|
```
|
|
|
|
### 2.3. Safe Error Handling (`try-catch`)
|
|
All external library loading or file parsing must be wrapped in `try-catch` blocks to avoid unhandled script panics:
|
|
|
|
```ucode
|
|
let uci_data = null;
|
|
try {
|
|
let uci = require("uci").cursor();
|
|
uci_data = uci.get_all("tachyon", "settings");
|
|
}
|
|
catch (e) {
|
|
warn("Failed to load UCI settings: " + e + "\n");
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 3. Performance & Memory Optimization Guidelines
|
|
|
|
1. **Stream Large Files**: Avoid loading multi-megabyte blocklists entirely into RAM with `fs.readfile()`. Use line-by-line streaming via `fs.open()` and `f.read("line")`.
|
|
2. **Avoid Spawning Subprocesses in Loops**: Invoking `system()` or `fs.popen()` spawns a fork-exec sequence in Linux. Use native ucode functions for string manipulation and path operations whenever possible.
|
|
3. **Keep Modules Stateless**: Module files under `usr/lib/tachyon/` should export reusable pure functions or class definitions rather than maintaining mutable global state.
|