@tishlang/tish-format 2.39.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/Cargo.toml +4 -1
  2. package/bin/tish-format +0 -0
  3. package/crates/tish/src/cli_help.rs +28 -0
  4. package/crates/tish/src/main.rs +41 -5
  5. package/crates/tish/tests/fixtures/cargo_example_project/crates/demo-shim/src/lib.rs +21 -0
  6. package/crates/tish/tests/fixtures/cargo_example_project/crates/demo-shim/tish.d.tish +4 -0
  7. package/crates/tish/tests/fixtures/cargo_example_project/src/main.tish +5 -1
  8. package/crates/tish/tests/regr_typed_externs.rs +59 -0
  9. package/crates/tish/tests/scope_merge.rs +79 -0
  10. package/crates/tish_build_utils/src/lib.rs +27 -1
  11. package/crates/tish_builtins/Cargo.toml +14 -6
  12. package/crates/tish_builtins/src/array.rs +130 -54
  13. package/crates/tish_builtins/src/collections.rs +9 -5
  14. package/crates/tish_builtins/src/construct.rs +7 -1
  15. package/crates/tish_builtins/src/date.rs +15 -1
  16. package/crates/tish_builtins/src/globals.rs +65 -3
  17. package/crates/tish_builtins/src/helpers.rs +7 -3
  18. package/crates/tish_builtins/src/iterator.rs +5 -1
  19. package/crates/tish_builtins/src/lib.rs +3 -0
  20. package/crates/tish_builtins/src/math.rs +14 -0
  21. package/crates/tish_builtins/src/number.rs +72 -6
  22. package/crates/tish_builtins/src/object.rs +6 -1
  23. package/crates/tish_builtins/src/string.rs +13 -2
  24. package/crates/tish_builtins/src/symbol.rs +17 -17
  25. package/crates/tish_builtins/src/typedarrays.rs +9 -2
  26. package/crates/tish_bytecode/src/compiler.rs +76 -2
  27. package/crates/tish_bytecode/src/lib.rs +1 -1
  28. package/crates/tish_bytecode/src/opcode.rs +77 -2
  29. package/crates/tish_compile/src/codegen.rs +2154 -225
  30. package/crates/tish_compile/src/infer.rs +37 -41
  31. package/crates/tish_compile/src/lib.rs +13 -1
  32. package/crates/tish_compile/src/platform_resolve.rs +361 -0
  33. package/crates/tish_compile/src/resolve.rs +195 -11
  34. package/crates/tish_compile/src/schemes.rs +333 -0
  35. package/crates/tish_compile/src/types.rs +205 -34
  36. package/crates/tish_compile/tests/perf_codegen_320.rs +2 -2
  37. package/crates/tish_compile/tests/perf_codegen_module_const_forof.rs +3 -1
  38. package/crates/tish_compile/tests/platform_resolve_cli.rs +164 -0
  39. package/crates/tish_compile/tests/regr_556_param_name_collision.rs +40 -0
  40. package/crates/tish_compile/tests/regr_558_struct_array_write.rs +65 -0
  41. package/crates/tish_compile/tests/regr_558b_struct_field_read.rs +21 -0
  42. package/crates/tish_compile/tests/regr_558c_wrapped_struct_array.rs +64 -0
  43. package/crates/tish_compile/tests/regr_558d_native_vec_mutators.rs +156 -0
  44. package/crates/tish_compile/tests/regr_562_wrapped_struct_array_push.rs +50 -0
  45. package/crates/tish_compile/tests/regr_fixed_native_coercions.rs +69 -0
  46. package/crates/tish_compile/tests/regr_gba_numerics_gated.rs +58 -0
  47. package/crates/tish_compile/tests/regr_gba_struct_reflection.rs +106 -0
  48. package/crates/tish_compile/tests/regression/name_collision_lib.tish +6 -0
  49. package/crates/tish_compile/tests/regression/name_collision_main.tish +13 -0
  50. package/crates/tish_compile/tests/regression/struct_array_write.tish +22 -0
  51. package/crates/tish_compile/tests/regression/struct_field_return_read.tish +10 -0
  52. package/crates/tish_compiler_wasm/Cargo.toml +1 -0
  53. package/crates/tish_compiler_wasm/src/resolve_virtual.rs +56 -6
  54. package/crates/tish_core/Cargo.toml +39 -4
  55. package/crates/tish_core/src/compat.rs +506 -0
  56. package/crates/tish_core/src/console_style.rs +23 -1
  57. package/crates/tish_core/src/json.rs +42 -10
  58. package/crates/tish_core/src/lib.rs +121 -25
  59. package/crates/tish_core/src/macros.rs +1 -1
  60. package/crates/tish_core/src/shape.rs +4 -2
  61. package/crates/tish_core/src/uri.rs +8 -1
  62. package/crates/tish_core/src/value.rs +305 -54
  63. package/crates/tish_core/src/vmref.rs +17 -5
  64. package/crates/tish_eval/Cargo.toml +2 -0
  65. package/crates/tish_eval/src/eval.rs +90 -3
  66. package/crates/tish_eval/src/value.rs +8 -0
  67. package/crates/tish_eval/src/value_convert.rs +316 -21
  68. package/crates/tish_fmt/Cargo.toml +1 -1
  69. package/crates/tish_lsp/Cargo.toml +2 -0
  70. package/crates/tish_lsp/src/import_goto.rs +41 -14
  71. package/crates/tish_lsp/src/main.rs +18 -0
  72. package/crates/tish_native/src/build.rs +246 -0
  73. package/crates/tish_native/src/config.rs +20 -0
  74. package/crates/tish_native/src/lib.rs +16 -6
  75. package/crates/tish_parser/src/lib.rs +28 -0
  76. package/crates/tish_parser/src/parser.rs +25 -4
  77. package/crates/tish_runtime/src/lib.rs +55 -9
  78. package/crates/tish_runtime_gba/Cargo.lock +837 -0
  79. package/crates/tish_runtime_gba/Cargo.toml +16 -0
  80. package/crates/tish_runtime_gba/src/gba.rs +211 -0
  81. package/crates/tish_runtime_gba/src/lib.rs +804 -0
  82. package/crates/tish_vm/src/jit.rs +1446 -164
  83. package/crates/tish_vm/src/vm.rs +583 -173
  84. package/justfile +9 -0
  85. package/package.json +1 -1
  86. package/platform/darwin-arm64/tish-fmt +0 -0
  87. package/platform/darwin-x64/tish-fmt +0 -0
  88. package/platform/linux-arm64/tish-fmt +0 -0
  89. package/platform/linux-x64/tish-fmt +0 -0
  90. package/platform/win32-x64/tish-fmt.exe +0 -0
@@ -1,12 +1,21 @@
1
1
  //! JSON parsing and stringification for Tish values.
2
2
 
3
- use crate::{Value, VmRef};
4
- use std::sync::Arc;
3
+ use crate::compat::{AHashMap, Arc};
4
+ use crate::{NumArrayBacking, Value, VmRef};
5
+ #[cfg(feature = "portable")]
6
+ use crate::compat::FloatExt;
7
+ #[cfg(feature = "portable")]
8
+ use alloc::{
9
+ boxed::Box,
10
+ format,
11
+ string::{String, ToString},
12
+ vec::Vec,
13
+ };
5
14
 
6
15
  /// Per-`json_parse`-call cache of object-key text → shared `Arc<str>`. A JSON array of records
7
16
  /// repeats the same handful of keys across thousands of objects; interning allocates each key
8
17
  /// once and `Arc::clone`s it thereafter, instead of a fresh `Arc<str>` allocation per occurrence.
9
- type KeyCache = ahash::AHashMap<Box<str>, Arc<str>>;
18
+ type KeyCache = AHashMap<Box<str>, Arc<str>>;
10
19
 
11
20
  #[inline]
12
21
  fn intern_key(cache: &mut KeyCache, s: &str) -> Arc<str> {
@@ -133,11 +142,11 @@ fn signal_circular_json_throw() {
133
142
  if !crate::has_pending_throw() {
134
143
  crate::set_pending_throw(Value::object_from_pairs([
135
144
  (
136
- std::sync::Arc::from("name"),
145
+ Arc::from("name"),
137
146
  Value::String("TypeError".into()),
138
147
  ),
139
148
  (
140
- std::sync::Arc::from("message"),
149
+ Arc::from("message"),
141
150
  Value::String("Converting circular structure to JSON".into()),
142
151
  ),
143
152
  ]));
@@ -178,11 +187,24 @@ fn json_stringify_into_guarded(buf: &mut String, value: &Value, ancestors: &mut
178
187
  Value::NumberArray(arr) => {
179
188
  let borrowed = arr.borrow();
180
189
  buf.push('[');
181
- for (i, n) in borrowed.iter().enumerate() {
182
- if i > 0 {
183
- buf.push(',');
190
+ match &*borrowed {
191
+ NumArrayBacking::Packed(nums) => {
192
+ for (i, n) in nums.iter().enumerate() {
193
+ if i > 0 {
194
+ buf.push(',');
195
+ }
196
+ write_json_number(buf, *n);
197
+ }
198
+ }
199
+ // A deopted numeric array serializes element-by-element like a boxed Array.
200
+ NumArrayBacking::Boxed(items) => {
201
+ for (i, item) in items.iter().enumerate() {
202
+ if i > 0 {
203
+ buf.push(',');
204
+ }
205
+ json_stringify_into_guarded(buf, item, ancestors);
206
+ }
184
207
  }
185
- write_json_number(buf, *n);
186
208
  }
187
209
  buf.push(']');
188
210
  }
@@ -217,6 +239,14 @@ fn json_stringify_into_guarded(buf: &mut String, value: &Value, ancestors: &mut
217
239
  }
218
240
  #[cfg(feature = "regex")]
219
241
  Value::RegExp(_) => buf.push_str("null"),
242
+ // A boxed typed struct serialises like the object it is: materialise once and recurse. (This is
243
+ // the generic path; codegen also has a direct `__tish_json_TishStruct_*` fast path that never
244
+ // boxes to a struct-Value in the first place, used when the static type is known.)
245
+ #[cfg(feature = "portable")]
246
+ Value::Struct(s) => {
247
+ let obj = s.borrow().tish_to_object();
248
+ json_stringify_into_guarded(buf, &obj, ancestors);
249
+ }
220
250
  }
221
251
  }
222
252
 
@@ -253,7 +283,7 @@ pub fn escape_json_string_into(buf: &mut String, s: &str) {
253
283
  b'\x08' => buf.push_str("\\b"),
254
284
  b'\x0c' => buf.push_str("\\f"),
255
285
  _ => {
256
- use std::fmt::Write;
286
+ use core::fmt::Write;
257
287
  let _ = write!(buf, "\\u{:04x}", b as u32);
258
288
  }
259
289
  }
@@ -509,6 +539,7 @@ fn parse_object<'a>(
509
539
  Value::Object(VmRef::new(crate::ObjectData {
510
540
  strings: map,
511
541
  symbols: None,
542
+ frozen: false,
512
543
  })),
513
544
  rest,
514
545
  ));
@@ -539,6 +570,7 @@ fn parse_object<'a>(
539
570
  Value::Object(VmRef::new(crate::ObjectData {
540
571
  strings: map,
541
572
  symbols: None,
573
+ frozen: false,
542
574
  })),
543
575
  &input[1..],
544
576
  ));
@@ -2,7 +2,34 @@
2
2
  //!
3
3
  //! This crate provides the unified Value type and utilities used by both
4
4
  //! the interpreter (tishlang_eval) and compiled runtime (tishlang_runtime).
5
+ #![cfg_attr(feature = "portable", no_std)]
5
6
 
7
+ // `portable` (no_std, Arc→Rc) and `send-values` (parking_lot, std-only) are mutually
8
+ // exclusive; unified by Cargo's additive features they'd try to compile parking_lot for a
9
+ // no_std target with a confusing far-from-root-cause error. Fail loudly at the source instead.
10
+ #[cfg(all(feature = "portable", feature = "send-values"))]
11
+ compile_error!("tishlang_core features `portable` and `send-values` are mutually exclusive");
12
+
13
+ // `portable` implies `#![no_std]`, so `std` must be OFF (depend on this crate as
14
+ // `default-features = false, features = ["portable"]`). If both are on — the usual slip of
15
+ // requesting `portable` without disabling the default `std` feature — the `no_std` attribute
16
+ // above still applies while the `std`-only deps (ahash/arcstr) are pulled in, producing a
17
+ // confusing cascade. Catch it at the source with a message that names the fix.
18
+ #[cfg(all(feature = "portable", feature = "std"))]
19
+ compile_error!(
20
+ "tishlang_core feature `portable` requires `default-features = false` (the default `std` feature must be disabled)"
21
+ );
22
+
23
+ extern crate alloc;
24
+
25
+ #[cfg(feature = "portable")]
26
+ use alloc::{
27
+ format,
28
+ string::{String, ToString},
29
+ vec::Vec,
30
+ };
31
+
32
+ mod compat;
6
33
  mod console_style;
7
34
  mod json;
8
35
  mod macros;
@@ -11,13 +38,38 @@ mod uri;
11
38
  mod value;
12
39
  mod vmref;
13
40
 
41
+ /// Hidden re-export so the exported `tish_module!` macro can name the right
42
+ /// smart pointer (`std::sync::Arc` on host, `Rc` under `portable`) at its
43
+ /// expansion sites without leaking the private `compat` module.
44
+ #[doc(hidden)]
45
+ pub use compat::Arc as __TishArc;
46
+
47
+ /// Portable-runtime surface consumed by `tishlang_builtins` and the GBA facade
48
+ /// (`tishlang_runtime_gba`): the libm-backed float trait and the installable
49
+ /// clock / RNG hooks. Only present under `portable`.
50
+ #[cfg(feature = "portable")]
51
+ pub use compat::{
52
+ install_clock, install_rng, next_u64, now_ms, random_f64, seed_rng, FloatExt, SingleCore,
53
+ };
54
+
14
55
  pub use console_style::{format_value_styled, format_values_for_console, use_console_colors};
15
56
  pub use json::{
16
57
  escape_json_string_into, json_parse, json_stringify, json_stringify_into, write_json_number,
17
58
  };
18
59
  pub use shape::{ShapeId, DICT_SHAPE, EMPTY_SHAPE};
19
60
  pub use uri::{percent_decode, percent_decode_component, percent_encode, percent_encode_component};
20
- pub use arcstr::ArcStr;
61
+ pub use compat::ArcStr;
62
+ /// The core type vocabulary, re-exported so `tishlang_builtins`, generated code,
63
+ /// and the GBA facade share one source of truth: `Arc` (std `Arc` / `Rc` under
64
+ /// `portable`), and the object hasher (`ahash` / `FxHasher`). Names match the std
65
+ /// originals so hosted call sites are unchanged.
66
+ pub use compat::{AHashMap, Arc, RandomState};
67
+ /// Lock / once-cell vocabulary, namespaced to avoid polluting the crate root:
68
+ /// std `sync` types on the host, single-core shims under `portable`. Used by
69
+ /// `tishlang_builtins` for its symbol registry.
70
+ pub mod sync {
71
+ pub use crate::compat::{Mutex, OnceLock, RwLock};
72
+ }
21
73
  pub use value::*;
22
74
  pub use vmref::{VmReadGuard, VmRef, VmWriteGuard};
23
75
 
@@ -26,20 +78,34 @@ pub use vmref::{VmReadGuard, VmRef, VmWriteGuard};
26
78
  /// `[tish-exe, <file>, args...]` so a script sees its own args — not the `run` subcommand. Compiled
27
79
  /// native binaries don't touch this; they read `std::env::args()` directly (which is already their
28
80
  /// own argv). #88
81
+ #[cfg(not(feature = "portable"))]
29
82
  static PROCESS_ARGV: std::sync::OnceLock<Vec<String>> = std::sync::OnceLock::new();
30
83
 
31
84
  /// Override `process.argv` for the interpreter/VM (set once by `tish run` before executing). A
32
85
  /// later call is ignored (the first override wins), matching the single-program-per-process model.
86
+ /// No-op under `portable` (an embedded GBA ROM has no process argv).
87
+ #[cfg(not(feature = "portable"))]
33
88
  pub fn set_process_argv(argv: Vec<String>) {
34
89
  let _ = PROCESS_ARGV.set(argv);
35
90
  }
36
91
 
92
+ /// See [`set_process_argv`]. No-op under `portable`.
93
+ #[cfg(feature = "portable")]
94
+ pub fn set_process_argv(_argv: Vec<String>) {}
95
+
37
96
  /// The argv a script should see as `process.argv`: the [`set_process_argv`] override if present,
38
- /// else the host process's `std::env::args()`.
97
+ /// else the host process's `std::env::args()`. Empty under `portable`.
39
98
  pub fn process_argv() -> Vec<String> {
40
- match PROCESS_ARGV.get() {
41
- Some(v) => v.clone(),
42
- None => std::env::args().collect(),
99
+ #[cfg(not(feature = "portable"))]
100
+ {
101
+ match PROCESS_ARGV.get() {
102
+ Some(v) => v.clone(),
103
+ None => std::env::args().collect(),
104
+ }
105
+ }
106
+ #[cfg(feature = "portable")]
107
+ {
108
+ Vec::new()
43
109
  }
44
110
  }
45
111
 
@@ -54,9 +120,14 @@ pub fn process_argv() -> Vec<String> {
54
120
  /// can poll it without a `builtins -> runtime` dependency cycle; `tishlang_runtime` and `tishlang_vm`
55
121
  /// share this one slot. First-throw-wins; drained exactly once by [`take_pending_throw`] at the frame
56
122
  /// that converts it back into a `Result`.
123
+ #[cfg(not(feature = "portable"))]
57
124
  thread_local! {
58
- static PENDING_THROW: std::cell::RefCell<Option<Value>> = const { std::cell::RefCell::new(None) };
125
+ static PENDING_THROW: core::cell::RefCell<Option<Value>> = const { core::cell::RefCell::new(None) };
59
126
  }
127
+ // Single-core: a plain static with the same `.with()` API. See compat::SingleCore.
128
+ #[cfg(feature = "portable")]
129
+ static PENDING_THROW: compat::SingleCore<core::cell::RefCell<Option<Value>>> =
130
+ compat::SingleCore::new(core::cell::RefCell::new(None));
60
131
 
61
132
  /// Park a thrown value to propagate across a non-`Result` boundary. First-throw-wins: if one is
62
133
  /// already pending (an erroneous continuation reached a second throw before the slot was drained),
@@ -80,13 +151,17 @@ pub fn take_pending_throw() -> Option<Value> {
80
151
  PENDING_THROW.with(|c| c.borrow_mut().take())
81
152
  }
82
153
 
154
+ // Current user-function call depth for the bytecode VM. The VM's recursive path builds a fresh
155
+ // `Vm` per call (so no shared struct field can accumulate) and its `Callable::call` signature is
156
+ // fixed — a thread-local is the VM's equivalent of the interpreter's shared `Rc<Cell>` counter.
157
+ // Lives here (not `tish_vm`) beside `PENDING_THROW` for the same layering reason. #381
158
+ #[cfg(not(feature = "portable"))]
83
159
  thread_local! {
84
- /// Current user-function call depth for the bytecode VM. The VM's recursive path builds a fresh
85
- /// `Vm` per call (so no shared struct field can accumulate) and its `Callable::call` signature is
86
- /// fixed — a thread-local is the VM's equivalent of the interpreter's shared `Rc<Cell>` counter.
87
- /// Lives here (not `tish_vm`) beside `PENDING_THROW` for the same layering reason. #381
88
- static CALL_DEPTH: std::cell::Cell<usize> = const { std::cell::Cell::new(0) };
160
+ static CALL_DEPTH: core::cell::Cell<usize> = const { core::cell::Cell::new(0) };
89
161
  }
162
+ #[cfg(feature = "portable")]
163
+ static CALL_DEPTH: compat::SingleCore<core::cell::Cell<usize>> =
164
+ compat::SingleCore::new(core::cell::Cell::new(0));
90
165
 
91
166
  /// Enter one VM call frame; returns the new depth. Pair every call with [`dec_call_depth`].
92
167
  #[inline]
@@ -107,14 +182,32 @@ pub fn dec_call_depth() {
107
182
  /// Default recursion ceiling shared by every backend that counts call depth (#381). Chosen with the
108
183
  /// interpreter: comfortably deep for real programs, but reached long before counted recursion can
109
184
  /// exhaust memory or the stack.
185
+ #[cfg(not(target_arch = "wasm32"))]
110
186
  pub const DEFAULT_MAX_CALL_DEPTH: usize = 20_000;
111
187
 
188
+ /// wasm32 ceiling (#531). On wasm the embedded VM runs on the single guest stack — there is no host
189
+ /// worker thread to grow into (`Vm` can't spawn on wasm), and the host runtime (wasmtime) bounds
190
+ /// guest call depth by its own `max_wasm_stack`. That limit is NOT reachable from the `.wasm`: it
191
+ /// overflows into an UNCATCHABLE module trap after only ~300 `Vm::run_chunk` frames — the tish
192
+ /// function locals live in the VM operand stack, so the per-frame host cost is ~constant regardless
193
+ /// of the program, and the trap depth is a stable ~310 under default wasmtime. A far lower ceiling
194
+ /// keeps the frame-counting guard firing first (measured safe with margin below the trap), so deep
195
+ /// recursion raises a CATCHABLE `RangeError` on WASI too — matching interp/vm/native rather than
196
+ /// aborting the process. WASI programs are thus capped much shallower than native; that is inherent
197
+ /// to wasm's host-bounded call stack, not a tish choice.
198
+ #[cfg(target_arch = "wasm32")]
199
+ pub const DEFAULT_MAX_CALL_DEPTH: usize = 256;
200
+
201
+ // The recursion ceiling, lazily initialized from `TISH_MAX_CALL_DEPTH` (0 = uninitialized). A
202
+ // thread-local Cell (not OnceLock) so tests can override it per-thread without racing the
203
+ // process-wide env. Shared by the VM's guard and the native backend's boxed-call guard. #381
204
+ #[cfg(not(feature = "portable"))]
112
205
  thread_local! {
113
- // The recursion ceiling, lazily initialized from `TISH_MAX_CALL_DEPTH` (0 = uninitialized). A
114
- // thread-local Cell (not OnceLock) so tests can override it per-thread without racing the
115
- // process-wide env. Shared by the VM's guard and the native backend's boxed-call guard. #381
116
- static MAX_CALL_DEPTH: std::cell::Cell<usize> = const { std::cell::Cell::new(0) };
206
+ static MAX_CALL_DEPTH: core::cell::Cell<usize> = const { core::cell::Cell::new(0) };
117
207
  }
208
+ #[cfg(feature = "portable")]
209
+ static MAX_CALL_DEPTH: compat::SingleCore<core::cell::Cell<usize>> =
210
+ compat::SingleCore::new(core::cell::Cell::new(0));
118
211
 
119
212
  /// The recursion ceiling for depth-counting guards: user-fn calls deeper than this raise a catchable
120
213
  /// `RangeError` instead of overflowing the native stack. `TISH_MAX_CALL_DEPTH` overrides (default
@@ -125,11 +218,14 @@ pub fn max_call_depth() -> usize {
125
218
  if v != 0 {
126
219
  return v;
127
220
  }
221
+ #[cfg(not(feature = "portable"))]
128
222
  let init = std::env::var("TISH_MAX_CALL_DEPTH")
129
223
  .ok()
130
224
  .and_then(|s| s.parse::<usize>().ok())
131
225
  .filter(|&n| n > 0)
132
226
  .unwrap_or(DEFAULT_MAX_CALL_DEPTH);
227
+ #[cfg(feature = "portable")]
228
+ let init = DEFAULT_MAX_CALL_DEPTH;
133
229
  c.set(init);
134
230
  init
135
231
  })
@@ -146,11 +242,11 @@ pub fn set_max_call_depth_for_test(n: usize) {
146
242
  pub fn stack_overflow_error() -> Value {
147
243
  let mut e = ObjectMap::default();
148
244
  e.insert(
149
- std::sync::Arc::from("name"),
245
+ Arc::from("name"),
150
246
  Value::String("RangeError".into()),
151
247
  );
152
248
  e.insert(
153
- std::sync::Arc::from("message"),
249
+ Arc::from("message"),
154
250
  Value::String("Maximum call stack size exceeded".into()),
155
251
  );
156
252
  Value::object(e)
@@ -163,11 +259,11 @@ pub fn stack_overflow_error() -> Value {
163
259
  pub fn not_a_function_error(message: impl Into<String>) -> Value {
164
260
  let mut e = ObjectMap::default();
165
261
  e.insert(
166
- std::sync::Arc::from("name"),
262
+ Arc::from("name"),
167
263
  Value::String("TypeError".into()),
168
264
  );
169
265
  e.insert(
170
- std::sync::Arc::from("message"),
266
+ Arc::from("message"),
171
267
  Value::String(message.into().into()),
172
268
  );
173
269
  Value::object(e)
@@ -179,11 +275,11 @@ pub fn not_a_function_error(message: impl Into<String>) -> Value {
179
275
  pub fn type_error(message: impl Into<String>) -> Value {
180
276
  let mut e = ObjectMap::default();
181
277
  e.insert(
182
- std::sync::Arc::from("name"),
278
+ Arc::from("name"),
183
279
  Value::String("TypeError".into()),
184
280
  );
185
281
  e.insert(
186
- std::sync::Arc::from("message"),
282
+ Arc::from("message"),
187
283
  Value::String(message.into().into()),
188
284
  );
189
285
  Value::object(e)
@@ -207,11 +303,11 @@ pub fn str_to_array_index(s: &str) -> Option<usize> {
207
303
  pub fn range_error(message: impl Into<String>) -> Value {
208
304
  let mut e = ObjectMap::default();
209
305
  e.insert(
210
- std::sync::Arc::from("name"),
306
+ Arc::from("name"),
211
307
  Value::String("RangeError".into()),
212
308
  );
213
309
  e.insert(
214
- std::sync::Arc::from("message"),
310
+ Arc::from("message"),
215
311
  Value::String(message.into().into()),
216
312
  );
217
313
  Value::object(e)
@@ -224,11 +320,11 @@ pub fn range_error(message: impl Into<String>) -> Value {
224
320
  pub fn cannot_read_property_error(prop: &str) -> Value {
225
321
  let mut e = ObjectMap::default();
226
322
  e.insert(
227
- std::sync::Arc::from("name"),
323
+ Arc::from("name"),
228
324
  Value::String("TypeError".into()),
229
325
  );
230
326
  e.insert(
231
- std::sync::Arc::from("message"),
327
+ Arc::from("message"),
232
328
  Value::String(format!("Cannot read property '{}' of null", prop).into()),
233
329
  );
234
330
  Value::object(e)
@@ -23,7 +23,7 @@
23
23
  #[macro_export]
24
24
  macro_rules! tish_module {
25
25
  ($($name:expr => $fn:expr),* $(,)?) => {{
26
- use std::sync::Arc;
26
+ use $crate::__TishArc as Arc;
27
27
  use $crate::{ObjectMap, Value};
28
28
  let mut map = ObjectMap::default();
29
29
  $(
@@ -12,7 +12,9 @@
12
12
  //! `PropMap` on a cache miss). Phase 1b will attach the ordered key list to each shape so objects can
13
13
  //! drop per-object key storage entirely (the butterfly representation).
14
14
 
15
- use std::sync::{Arc, OnceLock, RwLock};
15
+ use crate::compat::{AHashMap, Arc, OnceLock, RwLock};
16
+ #[cfg(feature = "portable")]
17
+ use alloc::{vec, vec::Vec};
16
18
 
17
19
  /// Identity of an object's ordered key-set.
18
20
  pub type ShapeId = u32;
@@ -31,7 +33,7 @@ pub const DICT_SHAPE: ShapeId = u32::MAX;
31
33
  /// ahash is ~2–3× faster on short string keys. Random-seeded per process, so no HashDoS regression.
32
34
  #[derive(Default)]
33
35
  struct ShapeNode {
34
- transitions: ahash::AHashMap<Arc<str>, ShapeId>,
36
+ transitions: AHashMap<Arc<str>, ShapeId>,
35
37
  }
36
38
 
37
39
  struct Registry {
@@ -1,5 +1,12 @@
1
1
  //! URI encoding/decoding utilities.
2
2
 
3
+ #[cfg(feature = "portable")]
4
+ use alloc::{
5
+ format,
6
+ string::{String, ToString},
7
+ vec::Vec,
8
+ };
9
+
3
10
  /// Percent-decode a string (for decodeURI).
4
11
  /// Does NOT decode reserved URI characters: ; / ? : @ & = + $ , #
5
12
  /// These are characters that encodeURI does not encode, so decodeURI won't decode them.
@@ -98,7 +105,7 @@ pub fn percent_decode_component(input: &str) -> Result<String, String> {
98
105
  while i < bytes.len() {
99
106
  if bytes[i] == b'%' {
100
107
  if i + 2 < bytes.len() {
101
- if let Some(b) = std::str::from_utf8(&bytes[i + 1..i + 3])
108
+ if let Some(b) = core::str::from_utf8(&bytes[i + 1..i + 3])
102
109
  .ok()
103
110
  .and_then(|s| u8::from_str_radix(s, 16).ok())
104
111
  {