@tishlang/tish-format 1.0.12 → 2.0.1
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.
- package/Cargo.toml +51 -0
- package/LICENSE +13 -0
- package/bin/tish-format +0 -0
- package/crates/js_to_tish/Cargo.toml +11 -0
- package/crates/js_to_tish/README.md +18 -0
- package/crates/js_to_tish/src/error.rs +55 -0
- package/crates/js_to_tish/src/lib.rs +11 -0
- package/crates/js_to_tish/src/span_util.rs +35 -0
- package/crates/js_to_tish/src/transform/expr.rs +611 -0
- package/crates/js_to_tish/src/transform/stmt.rs +503 -0
- package/crates/js_to_tish/src/transform.rs +60 -0
- package/crates/tish/Cargo.toml +62 -0
- package/crates/tish/build.rs +21 -0
- package/crates/tish/src/cargo_native_registry.rs +32 -0
- package/crates/tish/src/cli_help.rs +576 -0
- package/crates/tish/src/main.rs +853 -0
- package/crates/tish/src/repl_completion.rs +199 -0
- package/crates/tish/tests/cargo_example_compile.rs +67 -0
- package/crates/tish/tests/error_source_location.rs +36 -0
- package/crates/tish/tests/fixtures/cargo_example_project/Cargo.toml +3 -0
- package/crates/tish/tests/fixtures/cargo_example_project/crates/demo-shim/Cargo.toml +11 -0
- package/crates/tish/tests/fixtures/cargo_example_project/crates/demo-shim/src/lib.rs +12 -0
- package/crates/tish/tests/fixtures/cargo_example_project/package.json +10 -0
- package/crates/tish/tests/fixtures/cargo_example_project/src/main.tish +3 -0
- package/crates/tish/tests/fixtures/runtime_error_location.tish +5 -0
- package/crates/tish/tests/fixtures/trycatch_runtime_errors.tish +15 -0
- package/crates/tish/tests/fixtures/tty_capability.tish +9 -0
- package/crates/tish/tests/integration_test.rs +1406 -0
- package/crates/tish/tests/run_optimize_stdout_parity.rs +50 -0
- package/crates/tish/tests/shortcircuit.rs +65 -0
- package/crates/tish/tests/trycatch_runtime_errors.rs +45 -0
- package/crates/tish/tests/tty_capability.rs +43 -0
- package/crates/tish_ast/Cargo.toml +9 -0
- package/crates/tish_ast/src/ast.rs +649 -0
- package/crates/tish_ast/src/lib.rs +5 -0
- package/crates/tish_build_utils/Cargo.toml +11 -0
- package/crates/tish_build_utils/src/lib.rs +577 -0
- package/crates/tish_builtins/Cargo.toml +22 -0
- package/crates/tish_builtins/src/array.rs +803 -0
- package/crates/tish_builtins/src/collections.rs +481 -0
- package/crates/tish_builtins/src/construct.rs +199 -0
- package/crates/tish_builtins/src/date.rs +538 -0
- package/crates/tish_builtins/src/globals.rs +293 -0
- package/crates/tish_builtins/src/helpers.rs +35 -0
- package/crates/tish_builtins/src/iterator.rs +129 -0
- package/crates/tish_builtins/src/lib.rs +21 -0
- package/crates/tish_builtins/src/math.rs +89 -0
- package/crates/tish_builtins/src/number.rs +96 -0
- package/crates/tish_builtins/src/object.rs +36 -0
- package/crates/tish_builtins/src/string.rs +646 -0
- package/crates/tish_builtins/src/symbol.rs +83 -0
- package/crates/tish_builtins/src/typedarrays.rs +298 -0
- package/crates/tish_bytecode/Cargo.toml +17 -0
- package/crates/tish_bytecode/src/chunk.rs +164 -0
- package/crates/tish_bytecode/src/compiler.rs +2604 -0
- package/crates/tish_bytecode/src/encoding.rs +102 -0
- package/crates/tish_bytecode/src/lib.rs +20 -0
- package/crates/tish_bytecode/src/opcode.rs +185 -0
- package/crates/tish_bytecode/src/peephole.rs +189 -0
- package/crates/tish_bytecode/src/serialize.rs +193 -0
- package/crates/tish_bytecode/tests/break_continue_bytecode.rs +44 -0
- package/crates/tish_bytecode/tests/constant_folding.rs +84 -0
- package/crates/tish_bytecode/tests/sort_optimization.rs +31 -0
- package/crates/tish_compile/Cargo.toml +27 -0
- package/crates/tish_compile/src/check.rs +774 -0
- package/crates/tish_compile/src/codegen.rs +7317 -0
- package/crates/tish_compile/src/infer.rs +1681 -0
- package/crates/tish_compile/src/lib.rs +206 -0
- package/crates/tish_compile/src/resolve.rs +1951 -0
- package/crates/tish_compile/src/types.rs +605 -0
- package/crates/tish_compile_js/Cargo.toml +18 -0
- package/crates/tish_compile_js/examples/jsx_vdom_smoke.tish +8 -0
- package/crates/tish_compile_js/src/codegen.rs +938 -0
- package/crates/tish_compile_js/src/error.rs +20 -0
- package/crates/tish_compile_js/src/lib.rs +26 -0
- package/crates/tish_compile_js/src/tests_jsx.rs +414 -0
- package/crates/tish_compiler_wasm/Cargo.toml +21 -0
- package/crates/tish_compiler_wasm/src/lib.rs +57 -0
- package/crates/tish_compiler_wasm/src/resolve_virtual.rs +473 -0
- package/crates/tish_core/Cargo.toml +32 -0
- package/crates/tish_core/src/console_style.rs +170 -0
- package/crates/tish_core/src/json.rs +430 -0
- package/crates/tish_core/src/lib.rs +20 -0
- package/crates/tish_core/src/macros.rs +36 -0
- package/crates/tish_core/src/shape.rs +85 -0
- package/crates/tish_core/src/uri.rs +118 -0
- package/crates/tish_core/src/value.rs +1350 -0
- package/crates/tish_core/src/vmref.rs +183 -0
- package/crates/tish_cranelift/Cargo.toml +19 -0
- package/crates/tish_cranelift/src/lib.rs +43 -0
- package/crates/tish_cranelift/src/link.rs +130 -0
- package/crates/tish_cranelift/src/lower.rs +85 -0
- package/crates/tish_cranelift_runtime/Cargo.toml +26 -0
- package/crates/tish_cranelift_runtime/src/lib.rs +45 -0
- package/crates/tish_eval/Cargo.toml +51 -0
- package/crates/tish_eval/src/eval.rs +4265 -0
- package/crates/tish_eval/src/http.rs +191 -0
- package/crates/tish_eval/src/lib.rs +99 -0
- package/crates/tish_eval/src/natives.rs +551 -0
- package/crates/tish_eval/src/promise.rs +179 -0
- package/crates/tish_eval/src/regex.rs +299 -0
- package/crates/tish_eval/src/timers.rs +120 -0
- package/crates/tish_eval/src/value.rs +336 -0
- package/crates/tish_eval/src/value_convert.rs +117 -0
- package/crates/tish_ffi/Cargo.toml +26 -0
- package/crates/tish_ffi/src/lib.rs +518 -0
- package/crates/tish_ffi/tests/fixtures/testmod/Cargo.toml +18 -0
- package/crates/tish_ffi/tests/fixtures/testmod/src/lib.rs +46 -0
- package/crates/tish_ffi/tests/loader.rs +65 -0
- package/crates/tish_fmt/Cargo.toml +16 -0
- package/crates/tish_fmt/src/bin/tish-fmt.rs +41 -0
- package/crates/tish_fmt/src/lib.rs +2157 -0
- package/crates/tish_jsx_web/Cargo.toml +9 -0
- package/crates/tish_jsx_web/README.md +5 -0
- package/crates/tish_jsx_web/src/lib.rs +2 -0
- package/crates/tish_lexer/Cargo.toml +9 -0
- package/crates/tish_lexer/src/lib.rs +1104 -0
- package/crates/tish_lexer/src/token.rs +170 -0
- package/crates/tish_lint/Cargo.toml +18 -0
- package/crates/tish_lint/src/bin/tish-lint.rs +195 -0
- package/crates/tish_lint/src/lib.rs +281 -0
- package/crates/tish_llvm/Cargo.toml +13 -0
- package/crates/tish_llvm/src/lib.rs +115 -0
- package/crates/tish_lsp/Cargo.toml +25 -0
- package/crates/tish_lsp/README.md +26 -0
- package/crates/tish_lsp/src/builtin_goto.rs +362 -0
- package/crates/tish_lsp/src/import_goto.rs +564 -0
- package/crates/tish_lsp/src/main.rs +1459 -0
- package/crates/tish_native/Cargo.toml +16 -0
- package/crates/tish_native/src/build.rs +481 -0
- package/crates/tish_native/src/config.rs +48 -0
- package/crates/tish_native/src/lib.rs +416 -0
- package/crates/tish_opt/Cargo.toml +13 -0
- package/crates/tish_opt/src/lib.rs +1046 -0
- package/crates/tish_parser/Cargo.toml +11 -0
- package/crates/tish_parser/src/lib.rs +386 -0
- package/crates/tish_parser/src/parser.rs +2726 -0
- package/crates/tish_pg/Cargo.toml +34 -0
- package/crates/tish_pg/README.md +38 -0
- package/crates/tish_pg/src/error.rs +52 -0
- package/crates/tish_pg/src/lib.rs +955 -0
- package/crates/tish_resolve/Cargo.toml +13 -0
- package/crates/tish_resolve/src/lib.rs +3601 -0
- package/crates/tish_resolve/src/pos.rs +141 -0
- package/crates/tish_runtime/Cargo.toml +100 -0
- package/crates/tish_runtime/src/http.rs +1347 -0
- package/crates/tish_runtime/src/http_fetch.rs +492 -0
- package/crates/tish_runtime/src/http_hyper.rs +441 -0
- package/crates/tish_runtime/src/http_prefork.rs +189 -0
- package/crates/tish_runtime/src/lib.rs +1447 -0
- package/crates/tish_runtime/src/native_promise.rs +15 -0
- package/crates/tish_runtime/src/promise.rs +558 -0
- package/crates/tish_runtime/src/promise_io.rs +38 -0
- package/crates/tish_runtime/src/timers.rs +172 -0
- package/crates/tish_runtime/src/tty.rs +226 -0
- package/crates/tish_runtime/src/ws.rs +778 -0
- package/crates/tish_runtime/tests/fetch_readable_stream.rs +102 -0
- package/crates/tish_ui/Cargo.toml +17 -0
- package/crates/tish_ui/src/jsx.rs +692 -0
- package/crates/tish_ui/src/lib.rs +20 -0
- package/crates/tish_ui/src/runtime/hooks.rs +573 -0
- package/crates/tish_ui/src/runtime/mod.rs +183 -0
- package/crates/tish_vm/Cargo.toml +60 -0
- package/crates/tish_vm/src/jit.rs +1050 -0
- package/crates/tish_vm/src/lib.rs +41 -0
- package/crates/tish_vm/src/vm.rs +3536 -0
- package/crates/tish_vm/tests/concurrent_shared_state.rs +140 -0
- package/crates/tish_vm/tests/fixtures/or_string_cmd.tish +2 -0
- package/crates/tish_vm/tests/lexical_scope_declare.rs +34 -0
- package/crates/tish_vm/tests/peephole_jump_chain_logical_or.rs +150 -0
- package/crates/tish_wasm/Cargo.toml +15 -0
- package/crates/tish_wasm/src/lib.rs +428 -0
- package/crates/tish_wasm_runtime/Cargo.toml +37 -0
- package/crates/tish_wasm_runtime/src/gpu.rs +429 -0
- package/crates/tish_wasm_runtime/src/lib.rs +42 -0
- package/crates/tishlang_cargo_bindgen/Cargo.toml +26 -0
- package/crates/tishlang_cargo_bindgen/src/classify.rs +261 -0
- package/crates/tishlang_cargo_bindgen/src/discover.rs +125 -0
- package/crates/tishlang_cargo_bindgen/src/infer.rs +382 -0
- package/crates/tishlang_cargo_bindgen/src/lib.rs +349 -0
- package/crates/tishlang_cargo_bindgen/src/main.rs +167 -0
- package/crates/tishlang_cargo_bindgen/src/metadata.rs +117 -0
- package/justfile +276 -0
- package/package.json +2 -2
- package/platform/darwin-arm64/tish-fmt +0 -0
- package/platform/darwin-x64/tish-fmt +0 -0
- package/platform/linux-arm64/tish-fmt +0 -0
- package/platform/linux-x64/tish-fmt +0 -0
- package/platform/win32-x64/tish-fmt.exe +0 -0
|
@@ -0,0 +1,481 @@
|
|
|
1
|
+
//! `Set` and `Map` — real constructors + methods for the non-JS targets (interpreter, VM, native).
|
|
2
|
+
//!
|
|
3
|
+
//! Like [`crate::date`], an instance is a plain `Value::Object` whose methods are per-instance
|
|
4
|
+
//! `Value::native` closures that capture a shared backing store, so the one implementation works
|
|
5
|
+
//! across every backend with no new `Value` variant. The backing store is an **insertion-ordered hash
|
|
6
|
+
//! map** ([`indexmap::IndexMap`]) keyed by [`Key`] (SameValueZero), so `get`/`set`/`has`/`add` are
|
|
7
|
+
//! O(1) average rather than the O(n) linear scan a `Vec`-of-pairs would force (a `Map` doing N
|
|
8
|
+
//! operations was previously O(N²) — see `docs/perf-benchmark-suite.md`). `delete` uses `shift_remove`
|
|
9
|
+
//! to preserve iteration order. Each instance also carries a hidden [`SIZE_SLOT`] opaque
|
|
10
|
+
//! ([`SizeProbe`]) wired to the live store so the runtimes can answer the computed `.size` property
|
|
11
|
+
//! via [`collection_size`].
|
|
12
|
+
//!
|
|
13
|
+
//! ## v1 scope / known gaps (documented in tishlang-web)
|
|
14
|
+
//! - `add`/`set` return `undefined` (no method chaining yet — native object methods receive no
|
|
15
|
+
//! `this`).
|
|
16
|
+
//! - `.values()` / `.keys()` / `.entries()` return real **iterators** (objects with a `next()`
|
|
17
|
+
//! that yields `{ value, done }`), so they work both directly (`it.next()`) and in `for…of` /
|
|
18
|
+
//! spread / `Array.from` via [`tishlang_core::drain_iterator`]. The iterator snapshots the
|
|
19
|
+
//! collection when created; mutating the source mid-iteration is not reflected (a live iterator
|
|
20
|
+
//! is a follow-up). Direct `for (x of set)` (iterating the collection itself) and `forEach`
|
|
21
|
+
//! (a callback the interpreter's closures cannot cross into a core native fn) are still
|
|
22
|
+
//! follow-ups — iterate via `.values()` / `.entries()`.
|
|
23
|
+
//! - Keys/values use **SameValueZero** equality (NaN equals NaN; `+0`/`-0` are the same; objects by
|
|
24
|
+
//! identity).
|
|
25
|
+
|
|
26
|
+
use std::any::Any;
|
|
27
|
+
use std::hash::{Hash, Hasher};
|
|
28
|
+
use std::sync::Arc;
|
|
29
|
+
|
|
30
|
+
use indexmap::IndexMap;
|
|
31
|
+
use tishlang_core::{NativeFn, ObjectMap, TishOpaque, Value, VmRef};
|
|
32
|
+
|
|
33
|
+
const CONSTRUCT: &str = "__construct";
|
|
34
|
+
|
|
35
|
+
/// Hidden instance slot holding a [`SizeProbe`] opaque so every runtime can answer `.size`. An
|
|
36
|
+
/// `Opaque` is *shared* (`Arc::clone`, not deep-copied) across the interpreter's core↔eval value
|
|
37
|
+
/// bridge, so the probe stays wired to the live backing store even after an instance is bridged —
|
|
38
|
+
/// a hidden `Value::Array` would be copied and go stale on the first mutation.
|
|
39
|
+
pub const SIZE_SLOT: &str = "__tish_size__";
|
|
40
|
+
|
|
41
|
+
/// SameValueZero-keyed entry wrapper, so a `Value` can key an insertion-ordered hash map with JS
|
|
42
|
+
/// `Map`/`Set` semantics: NaN equals NaN, `+0`/`-0` unify, primitives by value, references by
|
|
43
|
+
/// identity. `Hash` is kept consistent with this `Eq` (equal keys hash equal). Primitive keys
|
|
44
|
+
/// (number / string / bool / null — the common case) hash by value → true O(1). Reference keys hash
|
|
45
|
+
/// by a per-variant tag only and are disambiguated by `ptr_eq` in `Eq`; that keeps object-keyed maps
|
|
46
|
+
/// *correct* (if same-bucket within a variant) without needing a stable address out of `VmRef`.
|
|
47
|
+
#[derive(Clone)]
|
|
48
|
+
struct Key(Value);
|
|
49
|
+
|
|
50
|
+
impl PartialEq for Key {
|
|
51
|
+
fn eq(&self, other: &Self) -> bool {
|
|
52
|
+
same_value_zero(&self.0, &other.0)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
impl Eq for Key {}
|
|
56
|
+
|
|
57
|
+
impl Hash for Key {
|
|
58
|
+
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
59
|
+
match &self.0 {
|
|
60
|
+
Value::Number(n) => {
|
|
61
|
+
0u8.hash(state);
|
|
62
|
+
// Canonicalize so `Eq` partners hash together: `+0` and `-0` share a key, and every
|
|
63
|
+
// NaN is one key.
|
|
64
|
+
let bits = if *n == 0.0 {
|
|
65
|
+
0u64
|
|
66
|
+
} else if n.is_nan() {
|
|
67
|
+
0x7ff8_0000_0000_0000
|
|
68
|
+
} else {
|
|
69
|
+
n.to_bits()
|
|
70
|
+
};
|
|
71
|
+
bits.hash(state);
|
|
72
|
+
}
|
|
73
|
+
Value::String(s) => {
|
|
74
|
+
1u8.hash(state);
|
|
75
|
+
s.as_str().hash(state);
|
|
76
|
+
}
|
|
77
|
+
Value::Bool(b) => {
|
|
78
|
+
2u8.hash(state);
|
|
79
|
+
b.hash(state);
|
|
80
|
+
}
|
|
81
|
+
Value::Null => 3u8.hash(state),
|
|
82
|
+
// Reference / identity values: per-variant tag only; `ptr_eq` in `Eq` does the rest.
|
|
83
|
+
Value::Symbol(_) => 4u8.hash(state),
|
|
84
|
+
Value::Array(_) => 5u8.hash(state),
|
|
85
|
+
Value::NumberArray(_) => 6u8.hash(state),
|
|
86
|
+
Value::Object(_) => 7u8.hash(state),
|
|
87
|
+
_ => 8u8.hash(state),
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/// Shared backing for both `Set` and `Map`: an insertion-ordered hash map keyed by [`Key`]. A `Set`
|
|
93
|
+
/// stores `Value::Null` values and iterates its keys; a `Map` stores the mapped value. Uses `ahash`
|
|
94
|
+
/// (the same fast hasher `tish_core`'s `PropMap` uses) rather than the default SipHash — iteration
|
|
95
|
+
/// order is insertion order regardless of the hasher, so this is a pure constant-factor speedup.
|
|
96
|
+
type Store = VmRef<IndexMap<Key, Value, ahash::RandomState>>;
|
|
97
|
+
|
|
98
|
+
/// Opaque whose length reports a collection's live element count. Stored under [`SIZE_SLOT`] on every
|
|
99
|
+
/// `Set`/`Map` instance.
|
|
100
|
+
struct SizeProbe(Store);
|
|
101
|
+
|
|
102
|
+
impl TishOpaque for SizeProbe {
|
|
103
|
+
fn type_name(&self) -> &'static str {
|
|
104
|
+
"CollectionSize"
|
|
105
|
+
}
|
|
106
|
+
fn get_method(&self, _name: &str) -> Option<NativeFn> {
|
|
107
|
+
None
|
|
108
|
+
}
|
|
109
|
+
fn as_any(&self) -> &dyn Any {
|
|
110
|
+
self
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/// If `op` is a [`SizeProbe`], its live length. Lets the interpreter (which holds the probe as an
|
|
115
|
+
/// already-bridged `Opaque`) read `.size` without depending on this module's private `SizeProbe`.
|
|
116
|
+
pub fn size_probe_len(op: &dyn TishOpaque) -> Option<f64> {
|
|
117
|
+
op.as_any()
|
|
118
|
+
.downcast_ref::<SizeProbe>()
|
|
119
|
+
.map(|p| p.0.borrow().len() as f64)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/// Wrap a backing store as the hidden [`SIZE_SLOT`] opaque. `Value::Opaque`'s payload is always
|
|
123
|
+
/// `Arc<dyn TishOpaque>`, so on the single-threaded build (`Rc`-based `VmRef`) clippy's
|
|
124
|
+
/// `arc_with_non_send_sync` fires spuriously — the `Arc` is mandated by the API, not a thread choice.
|
|
125
|
+
#[allow(clippy::arc_with_non_send_sync)]
|
|
126
|
+
fn size_slot(store: &Store) -> Value {
|
|
127
|
+
Value::Opaque(Arc::new(SizeProbe(store.clone())))
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/// SameValueZero — the equality `Set`/`Map` use for membership. NaN equals NaN; `+0` and `-0` are
|
|
131
|
+
/// equal; primitives compare by value; reference types compare by identity.
|
|
132
|
+
fn same_value_zero(a: &Value, b: &Value) -> bool {
|
|
133
|
+
use Value::*;
|
|
134
|
+
match (a, b) {
|
|
135
|
+
(Number(x), Number(y)) => x == y || (x.is_nan() && y.is_nan()),
|
|
136
|
+
(String(x), String(y)) => x == y,
|
|
137
|
+
(Bool(x), Bool(y)) => x == y,
|
|
138
|
+
(Null, Null) => true,
|
|
139
|
+
(Symbol(x), Symbol(y)) => Arc::ptr_eq(x, y),
|
|
140
|
+
(Array(x), Array(y)) => VmRef::ptr_eq(x, y),
|
|
141
|
+
(NumberArray(x), NumberArray(y)) => VmRef::ptr_eq(x, y),
|
|
142
|
+
(Object(x), Object(y)) => VmRef::ptr_eq(x, y),
|
|
143
|
+
_ => false,
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/// Iterate a `Value` as a sequence: arrays yield their elements; everything else yields nothing.
|
|
148
|
+
fn iter_elements(v: &Value) -> Vec<Value> {
|
|
149
|
+
match v {
|
|
150
|
+
Value::Array(a) => a.borrow().iter().cloned().collect(),
|
|
151
|
+
Value::NumberArray(a) => a.borrow().iter().map(|n| Value::Number(*n)).collect(),
|
|
152
|
+
_ => Vec::new(),
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/// The live `.size` of a `Set`/`Map` instance, or `None` for any other value. Runtimes call this
|
|
157
|
+
/// from `get_prop` so `set.size` / `map.size` read as a plain number property.
|
|
158
|
+
pub fn collection_size(obj: &Value) -> Option<f64> {
|
|
159
|
+
if let Value::Object(o) = obj {
|
|
160
|
+
if let Some(Value::Opaque(op)) = o.borrow().strings.get(SIZE_SLOT) {
|
|
161
|
+
return size_probe_len(op.as_ref());
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
None
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// ─────────────────────────────────────────── Set ───────────────────────────────────────────────
|
|
168
|
+
|
|
169
|
+
/// Build a `Set` instance object over a fresh backing store seeded from `initial`.
|
|
170
|
+
pub fn set_instance(initial: &[Value]) -> Value {
|
|
171
|
+
let store: Store = VmRef::new(IndexMap::default());
|
|
172
|
+
{
|
|
173
|
+
let mut b = store.borrow_mut();
|
|
174
|
+
for v in initial {
|
|
175
|
+
// `or_insert` dedups and keeps first-insertion order.
|
|
176
|
+
b.entry(Key(v.clone())).or_insert(Value::Null);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
let mut m = ObjectMap::default();
|
|
180
|
+
// Hidden slot that drives `.size` (shared across the interp value bridge — see SIZE_SLOT).
|
|
181
|
+
m.insert(Arc::from(SIZE_SLOT), size_slot(&store));
|
|
182
|
+
|
|
183
|
+
{
|
|
184
|
+
let s = store.clone();
|
|
185
|
+
m.insert(
|
|
186
|
+
Arc::from("add"),
|
|
187
|
+
Value::native(move |args: &[Value]| {
|
|
188
|
+
let v = args.first().cloned().unwrap_or(Value::Null);
|
|
189
|
+
s.borrow_mut().entry(Key(v)).or_insert(Value::Null);
|
|
190
|
+
Value::Null
|
|
191
|
+
}),
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
{
|
|
195
|
+
let s = store.clone();
|
|
196
|
+
m.insert(
|
|
197
|
+
Arc::from("has"),
|
|
198
|
+
Value::native(move |args: &[Value]| {
|
|
199
|
+
let v = args.first().cloned().unwrap_or(Value::Null);
|
|
200
|
+
Value::Bool(s.borrow().contains_key(&Key(v)))
|
|
201
|
+
}),
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
{
|
|
205
|
+
let s = store.clone();
|
|
206
|
+
m.insert(
|
|
207
|
+
Arc::from("delete"),
|
|
208
|
+
Value::native(move |args: &[Value]| {
|
|
209
|
+
let v = args.first().cloned().unwrap_or(Value::Null);
|
|
210
|
+
// `shift_remove` preserves iteration order (vs `swap_remove`).
|
|
211
|
+
Value::Bool(s.borrow_mut().shift_remove(&Key(v)).is_some())
|
|
212
|
+
}),
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
{
|
|
216
|
+
let s = store.clone();
|
|
217
|
+
m.insert(
|
|
218
|
+
Arc::from("clear"),
|
|
219
|
+
Value::native(move |_args: &[Value]| {
|
|
220
|
+
s.borrow_mut().clear();
|
|
221
|
+
Value::Null
|
|
222
|
+
}),
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
// values()/keys() are identical for a Set (the elements are the keys); entries() yields [v, v].
|
|
226
|
+
{
|
|
227
|
+
let s = store.clone();
|
|
228
|
+
let values = move |_args: &[Value]| {
|
|
229
|
+
let out: Vec<Value> = s.borrow().keys().map(|k| k.0.clone()).collect();
|
|
230
|
+
crate::iterator::array_iterator(out)
|
|
231
|
+
};
|
|
232
|
+
m.insert(Arc::from("values"), Value::native(values.clone()));
|
|
233
|
+
m.insert(Arc::from("keys"), Value::native(values));
|
|
234
|
+
}
|
|
235
|
+
{
|
|
236
|
+
let s = store.clone();
|
|
237
|
+
m.insert(
|
|
238
|
+
Arc::from("entries"),
|
|
239
|
+
Value::native(move |_args: &[Value]| {
|
|
240
|
+
let pairs: Vec<Value> = s
|
|
241
|
+
.borrow()
|
|
242
|
+
.keys()
|
|
243
|
+
.map(|k| Value::Array(VmRef::new(vec![k.0.clone(), k.0.clone()])))
|
|
244
|
+
.collect();
|
|
245
|
+
crate::iterator::array_iterator(pairs)
|
|
246
|
+
}),
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
Value::object(m)
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/// The global `Set` constructor (`new Set()` / `new Set([1, 2, 2])`).
|
|
254
|
+
pub fn set_constructor_value() -> Value {
|
|
255
|
+
let mut m = ObjectMap::default();
|
|
256
|
+
m.insert(
|
|
257
|
+
Arc::from(CONSTRUCT),
|
|
258
|
+
Value::native(|args: &[Value]| {
|
|
259
|
+
let initial = args.first().map(iter_elements).unwrap_or_default();
|
|
260
|
+
set_instance(&initial)
|
|
261
|
+
}),
|
|
262
|
+
);
|
|
263
|
+
Value::object(m)
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// ─────────────────────────────────────────── Map ───────────────────────────────────────────────
|
|
267
|
+
|
|
268
|
+
/// Build a `Map` instance object over a fresh backing store seeded from `pairs` (each element an
|
|
269
|
+
/// iterable `[k, v]`).
|
|
270
|
+
pub fn map_instance(pairs: &[Value]) -> Value {
|
|
271
|
+
let store: Store = VmRef::new(IndexMap::default());
|
|
272
|
+
{
|
|
273
|
+
let mut b = store.borrow_mut();
|
|
274
|
+
for p in pairs {
|
|
275
|
+
let kv = iter_elements(p);
|
|
276
|
+
let k = kv.first().cloned().unwrap_or(Value::Null);
|
|
277
|
+
let v = kv.get(1).cloned().unwrap_or(Value::Null);
|
|
278
|
+
b.insert(Key(k), v);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
let mut m = ObjectMap::default();
|
|
282
|
+
m.insert(Arc::from(SIZE_SLOT), size_slot(&store));
|
|
283
|
+
|
|
284
|
+
{
|
|
285
|
+
let s = store.clone();
|
|
286
|
+
m.insert(
|
|
287
|
+
Arc::from("set"),
|
|
288
|
+
Value::native(move |args: &[Value]| {
|
|
289
|
+
let key = args.first().cloned().unwrap_or(Value::Null);
|
|
290
|
+
let val = args.get(1).cloned().unwrap_or(Value::Null);
|
|
291
|
+
// `insert` updates an existing key in place (keeps its position) or appends a new one.
|
|
292
|
+
s.borrow_mut().insert(Key(key), val);
|
|
293
|
+
Value::Null
|
|
294
|
+
}),
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
{
|
|
298
|
+
let s = store.clone();
|
|
299
|
+
m.insert(
|
|
300
|
+
Arc::from("get"),
|
|
301
|
+
Value::native(move |args: &[Value]| {
|
|
302
|
+
let key = args.first().cloned().unwrap_or(Value::Null);
|
|
303
|
+
s.borrow().get(&Key(key)).cloned().unwrap_or(Value::Null)
|
|
304
|
+
}),
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
{
|
|
308
|
+
let s = store.clone();
|
|
309
|
+
m.insert(
|
|
310
|
+
Arc::from("has"),
|
|
311
|
+
Value::native(move |args: &[Value]| {
|
|
312
|
+
let key = args.first().cloned().unwrap_or(Value::Null);
|
|
313
|
+
Value::Bool(s.borrow().contains_key(&Key(key)))
|
|
314
|
+
}),
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
{
|
|
318
|
+
let s = store.clone();
|
|
319
|
+
m.insert(
|
|
320
|
+
Arc::from("delete"),
|
|
321
|
+
Value::native(move |args: &[Value]| {
|
|
322
|
+
let key = args.first().cloned().unwrap_or(Value::Null);
|
|
323
|
+
Value::Bool(s.borrow_mut().shift_remove(&Key(key)).is_some())
|
|
324
|
+
}),
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
{
|
|
328
|
+
let s = store.clone();
|
|
329
|
+
m.insert(
|
|
330
|
+
Arc::from("clear"),
|
|
331
|
+
Value::native(move |_args: &[Value]| {
|
|
332
|
+
s.borrow_mut().clear();
|
|
333
|
+
Value::Null
|
|
334
|
+
}),
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
{
|
|
338
|
+
let s = store.clone();
|
|
339
|
+
m.insert(
|
|
340
|
+
Arc::from("keys"),
|
|
341
|
+
Value::native(move |_args: &[Value]| {
|
|
342
|
+
let out: Vec<Value> = s.borrow().keys().map(|k| k.0.clone()).collect();
|
|
343
|
+
crate::iterator::array_iterator(out)
|
|
344
|
+
}),
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
{
|
|
348
|
+
let s = store.clone();
|
|
349
|
+
m.insert(
|
|
350
|
+
Arc::from("values"),
|
|
351
|
+
Value::native(move |_args: &[Value]| {
|
|
352
|
+
let out: Vec<Value> = s.borrow().values().cloned().collect();
|
|
353
|
+
crate::iterator::array_iterator(out)
|
|
354
|
+
}),
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
{
|
|
358
|
+
let s = store.clone();
|
|
359
|
+
m.insert(
|
|
360
|
+
Arc::from("entries"),
|
|
361
|
+
Value::native(move |_args: &[Value]| {
|
|
362
|
+
let pairs: Vec<Value> = s
|
|
363
|
+
.borrow()
|
|
364
|
+
.iter()
|
|
365
|
+
.map(|(k, v)| Value::Array(VmRef::new(vec![k.0.clone(), v.clone()])))
|
|
366
|
+
.collect();
|
|
367
|
+
crate::iterator::array_iterator(pairs)
|
|
368
|
+
}),
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
Value::object(m)
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/// The global `Map` constructor (`new Map()` / `new Map([[k, v], …])`).
|
|
376
|
+
pub fn map_constructor_value() -> Value {
|
|
377
|
+
let mut m = ObjectMap::default();
|
|
378
|
+
m.insert(
|
|
379
|
+
Arc::from(CONSTRUCT),
|
|
380
|
+
Value::native(|args: &[Value]| {
|
|
381
|
+
let pairs = args.first().map(iter_elements).unwrap_or_default();
|
|
382
|
+
map_instance(&pairs)
|
|
383
|
+
}),
|
|
384
|
+
);
|
|
385
|
+
Value::object(m)
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
#[cfg(test)]
|
|
389
|
+
mod tests {
|
|
390
|
+
use super::*;
|
|
391
|
+
|
|
392
|
+
fn num(v: &Value) -> f64 {
|
|
393
|
+
match v {
|
|
394
|
+
Value::Number(n) => *n,
|
|
395
|
+
_ => f64::NAN,
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
#[test]
|
|
400
|
+
fn set_dedups_and_counts() {
|
|
401
|
+
let s = set_instance(&[Value::Number(1.0), Value::Number(2.0), Value::Number(2.0)]);
|
|
402
|
+
assert_eq!(collection_size(&s), Some(2.0));
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
#[test]
|
|
406
|
+
fn set_nan_is_one_member() {
|
|
407
|
+
let s = set_instance(&[Value::Number(f64::NAN), Value::Number(f64::NAN)]);
|
|
408
|
+
assert_eq!(collection_size(&s), Some(1.0));
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
#[test]
|
|
412
|
+
fn map_size_via_hook() {
|
|
413
|
+
let m = map_instance(&[
|
|
414
|
+
Value::Array(VmRef::new(vec![Value::String("x".into()), Value::Number(1.0)])),
|
|
415
|
+
Value::Array(VmRef::new(vec![Value::String("y".into()), Value::Number(2.0)])),
|
|
416
|
+
]);
|
|
417
|
+
assert_eq!(collection_size(&m), Some(2.0));
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
#[test]
|
|
421
|
+
fn key_same_value_zero_semantics() {
|
|
422
|
+
// `+0` and `-0` are one key; NaN is one key.
|
|
423
|
+
let mut map: IndexMap<Key, Value> = IndexMap::default();
|
|
424
|
+
map.insert(Key(Value::Number(0.0)), Value::Number(1.0));
|
|
425
|
+
map.insert(Key(Value::Number(-0.0)), Value::Number(2.0)); // updates the same entry
|
|
426
|
+
assert_eq!(map.len(), 1);
|
|
427
|
+
assert_eq!(num(map.get(&Key(Value::Number(0.0))).unwrap()), 2.0);
|
|
428
|
+
|
|
429
|
+
map.insert(Key(Value::Number(f64::NAN)), Value::Number(7.0));
|
|
430
|
+
map.insert(Key(Value::Number(f64::NAN)), Value::Number(8.0)); // same NaN key
|
|
431
|
+
assert_eq!(num(map.get(&Key(Value::Number(f64::NAN))).unwrap()), 8.0);
|
|
432
|
+
assert_eq!(map.len(), 2);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
#[test]
|
|
436
|
+
fn map_insert_updates_in_place_and_keeps_order() {
|
|
437
|
+
let store: Store = VmRef::new(IndexMap::default());
|
|
438
|
+
store
|
|
439
|
+
.borrow_mut()
|
|
440
|
+
.insert(Key(Value::String("a".into())), Value::Number(1.0));
|
|
441
|
+
store
|
|
442
|
+
.borrow_mut()
|
|
443
|
+
.insert(Key(Value::String("a".into())), Value::Number(9.0)); // update, not insert
|
|
444
|
+
store
|
|
445
|
+
.borrow_mut()
|
|
446
|
+
.insert(Key(Value::String("b".into())), Value::Number(2.0));
|
|
447
|
+
let b = store.borrow();
|
|
448
|
+
assert_eq!(b.len(), 2);
|
|
449
|
+
assert_eq!(num(b.get(&Key(Value::String("a".into()))).unwrap()), 9.0);
|
|
450
|
+
let order: Vec<&str> = b
|
|
451
|
+
.keys()
|
|
452
|
+
.map(|k| match &k.0 {
|
|
453
|
+
Value::String(s) => s.as_str(),
|
|
454
|
+
_ => "?",
|
|
455
|
+
})
|
|
456
|
+
.collect();
|
|
457
|
+
assert_eq!(order, vec!["a", "b"]); // insertion order preserved
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
#[test]
|
|
461
|
+
fn delete_preserves_order() {
|
|
462
|
+
let store: Store = VmRef::new(IndexMap::default());
|
|
463
|
+
for (k, v) in [("a", 1.0), ("b", 2.0), ("c", 3.0)] {
|
|
464
|
+
store
|
|
465
|
+
.borrow_mut()
|
|
466
|
+
.insert(Key(Value::String(k.into())), Value::Number(v));
|
|
467
|
+
}
|
|
468
|
+
store
|
|
469
|
+
.borrow_mut()
|
|
470
|
+
.shift_remove(&Key(Value::String("b".into())));
|
|
471
|
+
let b = store.borrow();
|
|
472
|
+
let order: Vec<&str> = b
|
|
473
|
+
.keys()
|
|
474
|
+
.map(|k| match &k.0 {
|
|
475
|
+
Value::String(s) => s.as_str(),
|
|
476
|
+
_ => "?",
|
|
477
|
+
})
|
|
478
|
+
.collect();
|
|
479
|
+
assert_eq!(order, vec!["a", "c"]); // "b" removed, order intact
|
|
480
|
+
}
|
|
481
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
//! `new` lowering for non-JS targets: `construct(callee, args)` approximates JS `[[Construct]]`.
|
|
2
|
+
//! Browser-exact behavior remains on `tish build --target js`.
|
|
3
|
+
|
|
4
|
+
use std::sync::Arc;
|
|
5
|
+
use tishlang_core::{ObjectMap, Value, VmRef};
|
|
6
|
+
|
|
7
|
+
const CONSTRUCT: &str = "__construct";
|
|
8
|
+
|
|
9
|
+
/// Host `new`: `Object` with `__construct` (or, failing that, `__call`), `Function` as plain call,
|
|
10
|
+
/// else `Null`. The `__call` fallback matters because builtin objects like `Promise` expose their
|
|
11
|
+
/// constructor as `__call` (so `Promise(f)` works) but have no `__construct`; without this fallback
|
|
12
|
+
/// `new Promise((resolve, reject) => …)` returned `Null` and never ran the executor on the VM family,
|
|
13
|
+
/// while the interpreter (which routes `new` through the same callable) worked — a cross-backend
|
|
14
|
+
/// divergence on the most common promise idiom.
|
|
15
|
+
pub fn construct(callee: &Value, args: &[Value]) -> Value {
|
|
16
|
+
match callee {
|
|
17
|
+
Value::Function(f) => f.call(args),
|
|
18
|
+
Value::Object(o) => {
|
|
19
|
+
let b = o.borrow();
|
|
20
|
+
if let Some(Value::Function(ctor)) = b.strings.get(CONSTRUCT) {
|
|
21
|
+
let c = ctor.clone();
|
|
22
|
+
drop(b);
|
|
23
|
+
return c.call(args);
|
|
24
|
+
}
|
|
25
|
+
if let Some(Value::Function(call)) = b.strings.get("__call") {
|
|
26
|
+
let c = call.clone();
|
|
27
|
+
drop(b);
|
|
28
|
+
return c.call(args);
|
|
29
|
+
}
|
|
30
|
+
Value::Null
|
|
31
|
+
}
|
|
32
|
+
_ => Value::Null,
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/// A JS-style error object `{ name, message }` (issue #60).
|
|
37
|
+
pub fn error_object(name: &str, message: &str) -> Value {
|
|
38
|
+
let mut e = ObjectMap::default();
|
|
39
|
+
e.insert(Arc::from("name"), Value::String(name.into()));
|
|
40
|
+
e.insert(Arc::from("message"), Value::String(message.into()));
|
|
41
|
+
Value::object(e)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
fn make_error_from_args(name: &str, args: &[Value]) -> Value {
|
|
45
|
+
let message = args.first().map(|v| v.to_js_string()).unwrap_or_default();
|
|
46
|
+
error_object(name, &message)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/// `Error(msg)` / `new Error(msg)` (and `TypeError` / `RangeError`) → `{ name, message }`
|
|
50
|
+
/// (issue #60). `__call` and `__construct` behave identically, matching JS where `Error(x)`
|
|
51
|
+
/// and `new Error(x)` produce the same object.
|
|
52
|
+
pub fn error_constructor_value(name: &'static str) -> Value {
|
|
53
|
+
let mut m = ObjectMap::default();
|
|
54
|
+
m.insert(
|
|
55
|
+
Arc::from(CONSTRUCT),
|
|
56
|
+
Value::native(move |args: &[Value]| make_error_from_args(name, args)),
|
|
57
|
+
);
|
|
58
|
+
m.insert(
|
|
59
|
+
Arc::from("__call"),
|
|
60
|
+
Value::native(move |args: &[Value]| make_error_from_args(name, args)),
|
|
61
|
+
);
|
|
62
|
+
Value::object(m)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/// `Array(...)` / `new Array(...)` (issue #72). A single non-negative integer argument is a
|
|
66
|
+
/// length — the array is filled with `null` holes (so `arr[i] = v` works and `Array(n).fill(v)`
|
|
67
|
+
/// can overwrite them, issue #76). Zero args → `[]`; any other args → an array of exactly those
|
|
68
|
+
/// elements (`new Array(1, 2, 3)` → `[1, 2, 3]`). A single non-integer / negative number is a
|
|
69
|
+
/// RangeError in JS; native builtins can't throw here, so it falls back to a one-element array —
|
|
70
|
+
/// the integer-length and element-list forms (the real uses) match JS exactly.
|
|
71
|
+
pub fn array_construct(args: &[Value]) -> Value {
|
|
72
|
+
if let [Value::Number(n)] = args {
|
|
73
|
+
let n = *n;
|
|
74
|
+
if n >= 0.0 && n.fract() == 0.0 && n <= 4_294_967_295.0 {
|
|
75
|
+
return Value::Array(VmRef::new(vec![Value::Null; n as usize]));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
Value::Array(VmRef::new(args.to_vec()))
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
fn param(initial: f64) -> Value {
|
|
82
|
+
let mut m = ObjectMap::default();
|
|
83
|
+
m.insert(Arc::from("value"), Value::Number(initial));
|
|
84
|
+
Value::object(m)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
fn connect_fn() -> Value {
|
|
88
|
+
Value::native(|_| Value::Null)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/// Shared audio-node shape: connect, gain, optional filter fields.
|
|
92
|
+
fn audio_node_stub() -> Value {
|
|
93
|
+
let mut m = ObjectMap::default();
|
|
94
|
+
m.insert(Arc::from("connect"), connect_fn());
|
|
95
|
+
m.insert(Arc::from("gain"), param(0.0));
|
|
96
|
+
m.insert(Arc::from("frequency"), param(440.0));
|
|
97
|
+
m.insert(Arc::from("Q"), param(1.0));
|
|
98
|
+
m.insert(Arc::from("type"), Value::String("peaking".into()));
|
|
99
|
+
Value::object(m)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
fn analyser_stub() -> Value {
|
|
103
|
+
let mut m = ObjectMap::default();
|
|
104
|
+
m.insert(Arc::from("connect"), connect_fn());
|
|
105
|
+
m.insert(Arc::from("fftSize"), Value::Number(2048.0));
|
|
106
|
+
Value::object(m)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
fn stereo_panner_stub() -> Value {
|
|
110
|
+
let mut m = ObjectMap::default();
|
|
111
|
+
m.insert(Arc::from("connect"), connect_fn());
|
|
112
|
+
m.insert(Arc::from("pan"), param(0.0));
|
|
113
|
+
Value::object(m)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
fn audio_buffer_stub(len: usize) -> Value {
|
|
117
|
+
let n = len.max(1);
|
|
118
|
+
let data = VmRef::new(vec![Value::Number(0.0); n]);
|
|
119
|
+
let data2 = data.clone();
|
|
120
|
+
let mut m = ObjectMap::default();
|
|
121
|
+
m.insert(
|
|
122
|
+
Arc::from("getChannelData"),
|
|
123
|
+
Value::native(move |_args| Value::Array(data2.clone())),
|
|
124
|
+
);
|
|
125
|
+
Value::object(m)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
fn buffer_source_stub() -> Value {
|
|
129
|
+
let mut m = ObjectMap::default();
|
|
130
|
+
m.insert(Arc::from("buffer"), Value::Null);
|
|
131
|
+
m.insert(Arc::from("loop"), Value::Bool(false));
|
|
132
|
+
m.insert(Arc::from("connect"), connect_fn());
|
|
133
|
+
m.insert(Arc::from("start"), Value::native(|_| Value::Null));
|
|
134
|
+
m.insert(Arc::from("stop"), Value::native(|_| Value::Null));
|
|
135
|
+
Value::object(m)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
fn oscillator_stub() -> Value {
|
|
139
|
+
let mut m = ObjectMap::default();
|
|
140
|
+
m.insert(Arc::from("frequency"), param(440.0));
|
|
141
|
+
m.insert(Arc::from("type"), Value::String("sine".into()));
|
|
142
|
+
m.insert(Arc::from("connect"), connect_fn());
|
|
143
|
+
m.insert(Arc::from("start"), Value::native(|_| Value::Null));
|
|
144
|
+
m.insert(Arc::from("stop"), Value::native(|_| Value::Null));
|
|
145
|
+
Value::object(m)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
fn audio_context_instance() -> Value {
|
|
149
|
+
let mut ctx = ObjectMap::default();
|
|
150
|
+
ctx.insert(Arc::from("sampleRate"), Value::Number(48_000.0));
|
|
151
|
+
ctx.insert(Arc::from("destination"), audio_node_stub());
|
|
152
|
+
|
|
153
|
+
ctx.insert(
|
|
154
|
+
Arc::from("createGain"),
|
|
155
|
+
Value::native(|_| audio_node_stub()),
|
|
156
|
+
);
|
|
157
|
+
ctx.insert(
|
|
158
|
+
Arc::from("createBiquadFilter"),
|
|
159
|
+
Value::native(|_| audio_node_stub()),
|
|
160
|
+
);
|
|
161
|
+
ctx.insert(
|
|
162
|
+
Arc::from("createStereoPanner"),
|
|
163
|
+
Value::native(|_| stereo_panner_stub()),
|
|
164
|
+
);
|
|
165
|
+
ctx.insert(
|
|
166
|
+
Arc::from("createAnalyser"),
|
|
167
|
+
Value::native(|_| analyser_stub()),
|
|
168
|
+
);
|
|
169
|
+
ctx.insert(
|
|
170
|
+
Arc::from("createBuffer"),
|
|
171
|
+
Value::native(|args: &[Value]| {
|
|
172
|
+
let len = args
|
|
173
|
+
.get(1)
|
|
174
|
+
.and_then(Value::as_number)
|
|
175
|
+
.unwrap_or(0.0)
|
|
176
|
+
.clamp(0.0, 1_000_000_000.0) as usize;
|
|
177
|
+
audio_buffer_stub(len)
|
|
178
|
+
}),
|
|
179
|
+
);
|
|
180
|
+
ctx.insert(
|
|
181
|
+
Arc::from("createBufferSource"),
|
|
182
|
+
Value::native(|_| buffer_source_stub()),
|
|
183
|
+
);
|
|
184
|
+
ctx.insert(
|
|
185
|
+
Arc::from("createOscillator"),
|
|
186
|
+
Value::native(|_| oscillator_stub()),
|
|
187
|
+
);
|
|
188
|
+
ctx.insert(Arc::from("decodeAudioData"), Value::native(|_| Value::Null));
|
|
189
|
+
|
|
190
|
+
Value::object(ctx)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/// Global `AudioContext` for native/VM: stub graph (no real audio).
|
|
194
|
+
pub fn audio_context_constructor_value() -> Value {
|
|
195
|
+
let ctor = Value::native(|_args: &[Value]| audio_context_instance());
|
|
196
|
+
let mut m = ObjectMap::default();
|
|
197
|
+
m.insert(Arc::from(CONSTRUCT), ctor);
|
|
198
|
+
Value::object(m)
|
|
199
|
+
}
|