@tishlang/tish 1.0.28 → 1.0.33

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 (65) hide show
  1. package/Cargo.toml +1 -0
  2. package/crates/js_to_tish/src/transform/expr.rs +15 -6
  3. package/crates/tish/Cargo.toml +1 -1
  4. package/crates/tish/src/main.rs +8 -55
  5. package/crates/tish/tests/integration_test.rs +4 -3
  6. package/crates/tish_ast/src/ast.rs +65 -2
  7. package/crates/tish_build_utils/src/lib.rs +10 -2
  8. package/crates/tish_builtins/src/construct.rs +177 -0
  9. package/crates/tish_builtins/src/globals.rs +3 -5
  10. package/crates/tish_builtins/src/helpers.rs +2 -3
  11. package/crates/tish_builtins/src/lib.rs +1 -0
  12. package/crates/tish_builtins/src/object.rs +3 -4
  13. package/crates/tish_bytecode/src/compiler.rs +85 -11
  14. package/crates/tish_bytecode/src/opcode.rs +7 -3
  15. package/crates/tish_compile/Cargo.toml +1 -0
  16. package/crates/tish_compile/src/codegen.rs +233 -71
  17. package/crates/tish_compile/src/lib.rs +35 -0
  18. package/crates/tish_compile_js/Cargo.toml +1 -1
  19. package/crates/tish_compile_js/src/codegen.rs +43 -147
  20. package/crates/tish_compile_js/src/lib.rs +4 -7
  21. package/crates/tish_compile_js/src/tests_jsx.rs +89 -19
  22. package/crates/tish_compiler_wasm/src/lib.rs +2 -3
  23. package/crates/tish_core/Cargo.toml +4 -0
  24. package/crates/tish_core/src/console_style.rs +7 -1
  25. package/crates/tish_core/src/json.rs +1 -2
  26. package/crates/tish_core/src/macros.rs +2 -3
  27. package/crates/tish_core/src/value.rs +10 -5
  28. package/crates/tish_eval/Cargo.toml +2 -0
  29. package/crates/tish_eval/src/eval.rs +149 -72
  30. package/crates/tish_eval/src/http.rs +3 -4
  31. package/crates/tish_eval/src/regex.rs +3 -2
  32. package/crates/tish_eval/src/value.rs +11 -13
  33. package/crates/tish_eval/src/value_convert.rs +4 -8
  34. package/crates/tish_fmt/src/lib.rs +49 -10
  35. package/crates/tish_jsx_web/Cargo.toml +1 -1
  36. package/crates/tish_jsx_web/README.md +3 -16
  37. package/crates/tish_jsx_web/src/lib.rs +2 -157
  38. package/crates/tish_lexer/src/token.rs +2 -0
  39. package/crates/tish_lint/src/lib.rs +9 -0
  40. package/crates/tish_lsp/README.md +1 -1
  41. package/crates/tish_native/src/build.rs +16 -2
  42. package/crates/tish_opt/src/lib.rs +15 -0
  43. package/crates/tish_parser/src/lib.rs +101 -1
  44. package/crates/tish_parser/src/parser.rs +161 -50
  45. package/crates/tish_runtime/src/http.rs +4 -5
  46. package/crates/tish_runtime/src/http_fetch.rs +9 -10
  47. package/crates/tish_runtime/src/lib.rs +9 -2
  48. package/crates/tish_runtime/src/promise.rs +2 -3
  49. package/crates/tish_runtime/src/promise_io.rs +2 -3
  50. package/crates/tish_runtime/src/ws.rs +7 -7
  51. package/crates/tish_ui/Cargo.toml +17 -0
  52. package/crates/tish_ui/src/jsx.rs +390 -0
  53. package/crates/tish_ui/src/lib.rs +16 -0
  54. package/crates/tish_ui/src/runtime/hooks.rs +122 -0
  55. package/crates/tish_ui/src/runtime/mod.rs +173 -0
  56. package/crates/tish_vm/src/vm.rs +121 -27
  57. package/justfile +3 -3
  58. package/package.json +1 -1
  59. package/platform/darwin-arm64/tish +0 -0
  60. package/platform/darwin-x64/tish +0 -0
  61. package/platform/linux-arm64/tish +0 -0
  62. package/platform/linux-x64/tish +0 -0
  63. package/platform/win32-x64/tish.exe +0 -0
  64. package/crates/tish_compile_js/src/js_intrinsics.rs +0 -82
  65. package/crates/tish_jsx_web/vendor/Lattish.tish +0 -362
@@ -1,17 +1,17 @@
1
1
  //! Stack-based bytecode VM.
2
2
 
3
3
  use std::cell::RefCell;
4
- use std::collections::HashMap;
5
4
  use std::rc::Rc;
6
5
  use std::sync::Arc;
7
6
 
8
7
  use tishlang_ast::{BinOp, UnaryOp};
9
8
  use tishlang_builtins::array as arr_builtins;
9
+ use tishlang_builtins::construct as construct_builtin;
10
10
  use tishlang_builtins::string as str_builtins;
11
11
  use tishlang_builtins::globals as globals_builtins;
12
12
  use tishlang_builtins::math as math_builtins;
13
13
  use tishlang_bytecode::{u8_to_binop, u8_to_unaryop, Chunk, Constant, Opcode, NO_REST_PARAM};
14
- use tishlang_core::Value;
14
+ use tishlang_core::{ObjectMap, Value};
15
15
 
16
16
  type ArrayMethodFn = Rc<dyn Fn(&[Value]) -> Value>;
17
17
 
@@ -69,7 +69,7 @@ fn get_builtin_export(spec: &str, export_name: &str) -> Option<Value> {
69
69
  .collect(),
70
70
  )))),
71
71
  "process" => {
72
- let mut m = HashMap::new();
72
+ let mut m = ObjectMap::default();
73
73
  m.insert("exit".into(), Value::Function(Rc::new(|args: &[Value]| tishlang_runtime::process_exit(args))));
74
74
  m.insert("cwd".into(), Value::Function(Rc::new(|args: &[Value]| tishlang_runtime::process_cwd(args))));
75
75
  m.insert("exec".into(), Value::Function(Rc::new(|args: &[Value]| tishlang_runtime::process_exec(args))));
@@ -145,10 +145,10 @@ fn vm_log_err(s: &str) {
145
145
  }
146
146
 
147
147
  /// Initialize default globals (console, Math, JSON, etc.)
148
- fn init_globals() -> HashMap<Arc<str>, Value> {
149
- let mut g = HashMap::new();
148
+ fn init_globals() -> ObjectMap {
149
+ let mut g = ObjectMap::default();
150
150
 
151
- let mut console = HashMap::new();
151
+ let mut console = ObjectMap::default();
152
152
  console.insert(
153
153
  "debug".into(),
154
154
  Value::Function(Rc::new(|args: &[Value]| {
@@ -191,7 +191,7 @@ fn init_globals() -> HashMap<Arc<str>, Value> {
191
191
  );
192
192
  g.insert("console".into(), Value::Object(Rc::new(RefCell::new(console))));
193
193
 
194
- let mut math = HashMap::new();
194
+ let mut math = ObjectMap::default();
195
195
  math.insert(
196
196
  "abs".into(),
197
197
  Value::Function(Rc::new(|args: &[Value]| {
@@ -257,7 +257,7 @@ fn init_globals() -> HashMap<Arc<str>, Value> {
257
257
  math.insert("E".into(), Value::Number(std::f64::consts::E));
258
258
  g.insert("Math".into(), Value::Object(Rc::new(RefCell::new(math))));
259
259
 
260
- let mut json = HashMap::new();
260
+ let mut json = ObjectMap::default();
261
261
  json.insert(
262
262
  "parse".into(),
263
263
  Value::Function(Rc::new(|args: &[Value]| {
@@ -292,7 +292,7 @@ fn init_globals() -> HashMap<Arc<str>, Value> {
292
292
  );
293
293
 
294
294
  // Date - at minimum Date.now() for timing
295
- let mut date = HashMap::new();
295
+ let mut date = ObjectMap::default();
296
296
  date.insert(
297
297
  "now".into(),
298
298
  Value::Function(Rc::new(|_args: &[Value]| {
@@ -305,8 +305,17 @@ fn init_globals() -> HashMap<Arc<str>, Value> {
305
305
  );
306
306
  g.insert("Date".into(), Value::Object(Rc::new(RefCell::new(date))));
307
307
 
308
+ g.insert(
309
+ "Uint8Array".into(),
310
+ construct_builtin::uint8_array_constructor_value(),
311
+ );
312
+ g.insert(
313
+ "AudioContext".into(),
314
+ construct_builtin::audio_context_constructor_value(),
315
+ );
316
+
308
317
  // Object methods - delegate to tishlang_builtins::globals
309
- let mut object_methods = HashMap::new();
318
+ let mut object_methods = ObjectMap::default();
310
319
  object_methods.insert(
311
320
  "assign".into(),
312
321
  Value::Function(Rc::new(|args: &[Value]| globals_builtins::object_assign(args))),
@@ -321,7 +330,7 @@ fn init_globals() -> HashMap<Arc<str>, Value> {
321
330
  g.insert("Object".into(), Value::Object(Rc::new(RefCell::new(object_methods))));
322
331
 
323
332
  // Array.isArray
324
- let mut array_static = HashMap::new();
333
+ let mut array_static = ObjectMap::default();
325
334
  array_static.insert(
326
335
  "isArray".into(),
327
336
  Value::Function(Rc::new(|args: &[Value]| globals_builtins::array_is_array(args))),
@@ -330,7 +339,7 @@ fn init_globals() -> HashMap<Arc<str>, Value> {
330
339
 
331
340
  // String(value) as callable + String.fromCharCode
332
341
  let string_convert_fn = Value::Function(Rc::new(|args: &[Value]| globals_builtins::string_convert(args)));
333
- let mut string_static = HashMap::new();
342
+ let mut string_static = ObjectMap::default();
334
343
  string_static.insert("fromCharCode".into(), Value::Function(Rc::new(|args: &[Value]| globals_builtins::string_from_char_code(args))));
335
344
  string_static.insert(Arc::from("__call"), string_convert_fn);
336
345
  g.insert("String".into(), Value::Object(Rc::new(RefCell::new(string_static))));
@@ -342,12 +351,12 @@ fn init_globals() -> HashMap<Arc<str>, Value> {
342
351
  );
343
352
  g.insert(
344
353
  "Fragment".into(),
345
- Value::Object(Rc::new(RefCell::new(HashMap::new()))),
354
+ Value::Object(Rc::new(RefCell::new(ObjectMap::default()))),
346
355
  );
347
356
  g.insert(
348
357
  "createRoot".into(),
349
358
  Value::Function(Rc::new(|_args: &[Value]| {
350
- let mut render_obj = HashMap::new();
359
+ let mut render_obj = ObjectMap::default();
351
360
  render_obj.insert(
352
361
  "render".into(),
353
362
  Value::Function(Rc::new(|_args: &[Value]| Value::Null)),
@@ -363,13 +372,13 @@ fn init_globals() -> HashMap<Arc<str>, Value> {
363
372
  Value::Array(Rc::new(RefCell::new(arr)))
364
373
  })),
365
374
  );
366
- let mut document_obj = HashMap::new();
375
+ let mut document_obj = ObjectMap::default();
367
376
  document_obj.insert("body".into(), Value::Null);
368
377
  g.insert("document".into(), Value::Object(Rc::new(RefCell::new(document_obj))));
369
378
 
370
379
  #[cfg(feature = "process")]
371
380
  {
372
- let mut process_obj = HashMap::new();
381
+ let mut process_obj = ObjectMap::default();
373
382
  process_obj.insert(
374
383
  "exit".into(),
375
384
  Value::Function(Rc::new(|args: &[Value]| tishlang_runtime::process_exit(args))),
@@ -418,21 +427,21 @@ fn init_globals() -> HashMap<Arc<str>, Value> {
418
427
  }
419
428
 
420
429
  /// Shared scope for closure capture (parent frame's locals).
421
- type ScopeMap = Rc<RefCell<HashMap<Arc<str>, Value>>>;
430
+ type ScopeMap = Rc<RefCell<ObjectMap>>;
422
431
 
423
432
  pub struct Vm {
424
433
  stack: Vec<Value>,
425
- scope: HashMap<Arc<str>, Value>,
434
+ scope: ObjectMap,
426
435
  /// Enclosing scope for closures (captured parent frame locals).
427
436
  enclosing: Option<ScopeMap>,
428
- globals: Rc<RefCell<HashMap<Arc<str>, Value>>>,
437
+ globals: Rc<RefCell<ObjectMap>>,
429
438
  }
430
439
 
431
440
  impl Vm {
432
441
  pub fn new() -> Self {
433
442
  Self {
434
443
  stack: Vec::new(),
435
- scope: HashMap::new(),
444
+ scope: ObjectMap::default(),
436
445
  enclosing: None,
437
446
  globals: Rc::new(RefCell::new(init_globals())),
438
447
  }
@@ -477,7 +486,7 @@ impl Vm {
477
486
  let names = &chunk.names;
478
487
 
479
488
  let mut ip = 0;
480
- let local_scope: ScopeMap = Rc::new(RefCell::new(HashMap::new()));
489
+ let local_scope: ScopeMap = Rc::new(RefCell::new(ObjectMap::default()));
481
490
  {
482
491
  let mut ls = local_scope.borrow_mut();
483
492
  let param_count = chunk.param_count as usize;
@@ -538,7 +547,7 @@ impl Vm {
538
547
  Value::Function(Rc::new(move |args: &[Value]| {
539
548
  let mut vm = Vm {
540
549
  stack: Vec::new(),
541
- scope: HashMap::new(),
550
+ scope: ObjectMap::default(),
542
551
  enclosing: enclosing.clone(),
543
552
  globals: Rc::clone(&globals),
544
553
  };
@@ -723,6 +732,45 @@ impl Vm {
723
732
  let result = f(&args);
724
733
  self.stack.push(result);
725
734
  }
735
+ Opcode::Construct => {
736
+ let argc = Self::read_u16(code, &mut ip) as usize;
737
+ let mut args = Vec::with_capacity(argc);
738
+ for _ in 0..argc {
739
+ args.push(
740
+ self.stack
741
+ .pop()
742
+ .ok_or_else(|| "Stack underflow in construct".to_string())?,
743
+ );
744
+ }
745
+ args.reverse();
746
+ let callee = self
747
+ .stack
748
+ .pop()
749
+ .ok_or_else(|| "Stack underflow: no callee for construct".to_string())?;
750
+ let result = construct_builtin::construct(&callee, &args);
751
+ self.stack.push(result);
752
+ }
753
+ Opcode::ConstructSpread => {
754
+ let callee = self
755
+ .stack
756
+ .pop()
757
+ .ok_or_else(|| "Stack underflow: callee in ConstructSpread".to_string())?;
758
+ let args_array = self
759
+ .stack
760
+ .pop()
761
+ .ok_or_else(|| "Stack underflow in ConstructSpread".to_string())?;
762
+ let args: Vec<Value> = match &args_array {
763
+ Value::Array(a) => a.borrow().clone(),
764
+ _ => {
765
+ return Err(format!(
766
+ "ConstructSpread: args must be array, got {}",
767
+ args_array.to_display_string()
768
+ ));
769
+ }
770
+ };
771
+ let result = construct_builtin::construct(&callee, &args);
772
+ self.stack.push(result);
773
+ }
726
774
  Opcode::Return => {
727
775
  let v = self.stack.pop().unwrap_or(Value::Null);
728
776
  return Ok(v);
@@ -861,7 +909,7 @@ impl Vm {
861
909
  }
862
910
  Opcode::NewObject => {
863
911
  let n = Self::read_u16(code, &mut ip) as usize;
864
- let mut map = HashMap::new();
912
+ let mut map = ObjectMap::with_capacity(n.max(1));
865
913
  for _ in 0..n {
866
914
  let val = self
867
915
  .stack
@@ -926,7 +974,11 @@ impl Vm {
926
974
  .stack
927
975
  .pop()
928
976
  .ok_or_else(|| "Stack underflow".to_string())?;
929
- let mut merged: HashMap<Arc<str>, Value> = HashMap::new();
977
+ let cap = match (&left, &right) {
978
+ (Value::Object(l), Value::Object(r)) => l.borrow().len() + r.borrow().len(),
979
+ _ => 0,
980
+ };
981
+ let mut merged: ObjectMap = ObjectMap::with_capacity(cap.max(8));
930
982
  if let Value::Object(l) = &left {
931
983
  merged.extend(l.borrow().iter().map(|(k, v)| (Arc::clone(k), v.clone())));
932
984
  } else {
@@ -1112,6 +1164,40 @@ impl Default for Vm {
1112
1164
  }
1113
1165
  }
1114
1166
 
1167
+ /// Rough byte capacity for string coercion (matches hot paths like `"x" + n + "ms"`).
1168
+ fn estimate_string_concat_len(v: &Value) -> usize {
1169
+ match v {
1170
+ Value::String(s) => s.len(),
1171
+ Value::Number(_) => 24,
1172
+ Value::Bool(_) => 5,
1173
+ Value::Null => 4,
1174
+ _ => 32,
1175
+ }
1176
+ }
1177
+
1178
+ /// Append JS-style string conversion without an intermediate `String` per operand (unlike
1179
+ /// `format!("{}{}", a.to_display_string(), b.to_display_string())`, which triple-allocates).
1180
+ fn append_value_for_string_concat(out: &mut String, v: &Value) {
1181
+ use std::fmt::Write;
1182
+ match v {
1183
+ Value::Number(n) => {
1184
+ if n.is_nan() {
1185
+ out.push_str("NaN");
1186
+ } else if *n == f64::INFINITY {
1187
+ out.push_str("Infinity");
1188
+ } else if *n == f64::NEG_INFINITY {
1189
+ out.push_str("-Infinity");
1190
+ } else {
1191
+ let _ = write!(out, "{n}");
1192
+ }
1193
+ }
1194
+ Value::String(s) => out.push_str(s.as_ref()),
1195
+ Value::Bool(b) => out.push_str(if *b { "true" } else { "false" }),
1196
+ Value::Null => out.push_str("null"),
1197
+ _ => out.push_str(&v.to_display_string()),
1198
+ }
1199
+ }
1200
+
1115
1201
  fn eval_binop(op: BinOp, l: &Value, r: &Value) -> Result<Value, String> {
1116
1202
  use tishlang_ast::BinOp::*;
1117
1203
  use tishlang_core::Value::*;
@@ -1120,7 +1206,11 @@ fn eval_binop(op: BinOp, l: &Value, r: &Value) -> Result<Value, String> {
1120
1206
  match op {
1121
1207
  Add => {
1122
1208
  if matches!(l, Value::String(_)) || matches!(r, Value::String(_)) {
1123
- Ok(String(format!("{}{}", l.to_display_string(), r.to_display_string()).into()))
1209
+ let cap = estimate_string_concat_len(l) + estimate_string_concat_len(r);
1210
+ let mut buf = std::string::String::with_capacity(cap);
1211
+ append_value_for_string_concat(&mut buf, l);
1212
+ append_value_for_string_concat(&mut buf, r);
1213
+ Ok(String(buf.into()))
1124
1214
  } else {
1125
1215
  Ok(Number(ln + rn))
1126
1216
  }
@@ -1146,9 +1236,13 @@ fn eval_binop(op: BinOp, l: &Value, r: &Value) -> Result<Value, String> {
1146
1236
  Shl => Ok(Number(((ln as i32) << (rn as i32)) as f64)),
1147
1237
  Shr => Ok(Number(((ln as i32) >> (rn as i32)) as f64)),
1148
1238
  In => {
1149
- let key_s: Arc<str> = l.to_display_string().into();
1239
+ let key_s: Arc<str> = match l {
1240
+ Value::String(s) => Arc::clone(s),
1241
+ Value::Number(n) => n.to_string().into(),
1242
+ _ => l.to_display_string().into(),
1243
+ };
1150
1244
  Ok(Bool(match r {
1151
- Value::Object(m) => m.borrow().contains_key(&key_s),
1245
+ Value::Object(m) => m.borrow().contains_key(key_s.as_ref()),
1152
1246
  Value::Array(a) => {
1153
1247
  if key_s.as_ref() == "length" {
1154
1248
  true
package/justfile CHANGED
@@ -125,15 +125,15 @@ test *ARGS:
125
125
 
126
126
  # Run only tish package tests (same as CI: integration tests only)
127
127
  test-tish *ARGS:
128
- cargo nextest run -p tish --features full -- {{ARGS}}
128
+ cargo nextest run -p tishlang--features full -- {{ARGS}}
129
129
 
130
130
  # Skip slow backend tests (native/cranelift/wasi) for fast local iteration
131
131
  test-quick:
132
- cargo nextest run -p tish --features full -- --skip test_mvp_programs_native --skip test_mvp_programs_cranelift --skip test_mvp_programs_wasi
132
+ cargo nextest run -p tishlang--features full -- --skip test_mvp_programs_native --skip test_mvp_programs_cranelift --skip test_mvp_programs_wasi
133
133
 
134
134
  # Run tests with coverage (requires llvm-tools: rustup component add llvm-tools-preview)
135
135
  test-coverage:
136
- cargo llvm-cov nextest -p tish --features full --lcov --output-path lcov.info --html coverage-html
136
+ cargo llvm-cov nextest -p tishlang--features full --lcov --output-path lcov.info --html coverage-html
137
137
 
138
138
  # Plain cargo test (whole workspace)
139
139
  test-cargo:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tishlang/tish",
3
- "version": "1.0.28",
3
+ "version": "1.0.33",
4
4
  "description": "Tish - minimal TS/JS-compatible language. Run, REPL, compile to native.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "repository": {
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -1,82 +0,0 @@
1
- //! JS-only call sites that require `new` (Tish has no `new`).
2
- //! Intrinsic names, validation, and runtime preamble live here — main codegen only dispatches.
3
-
4
- use tishlang_ast::{CallArg, Expr};
5
-
6
- use crate::error::CompileError;
7
-
8
- /// Built-in calls lowered to `new ...` in the JS emit.
9
- #[derive(Debug, Clone, Copy, PartialEq, Eq)]
10
- pub enum JsIntrinsic {
11
- WebAudioCreateContext,
12
- Uint8Array,
13
- }
14
-
15
- #[derive(Debug, Default)]
16
- pub struct JsIntrinsics {
17
- pub needs_web_audio: bool,
18
- pub needs_uint8_array: bool,
19
- }
20
-
21
- impl JsIntrinsics {
22
- pub fn new() -> Self {
23
- Self::default()
24
- }
25
-
26
- /// Recognize `webAudioCreateContext()` / `jsUint8Array(n)` and validate arguments.
27
- pub fn classify_call(callee: &Expr, args: &[CallArg]) -> Result<Option<JsIntrinsic>, CompileError> {
28
- let Expr::Ident { name, .. } = callee else {
29
- return Ok(None);
30
- };
31
- match name.as_ref() {
32
- "webAudioCreateContext" => {
33
- if !args.is_empty() {
34
- return Err(CompileError::new(
35
- "webAudioCreateContext() takes no arguments (JS target only)",
36
- ));
37
- }
38
- Ok(Some(JsIntrinsic::WebAudioCreateContext))
39
- }
40
- "jsUint8Array" => {
41
- if args.len() != 1 {
42
- return Err(CompileError::new(
43
- "jsUint8Array(length) expects one argument (JS target only)",
44
- ));
45
- }
46
- Ok(Some(JsIntrinsic::Uint8Array))
47
- }
48
- _ => Ok(None),
49
- }
50
- }
51
-
52
- pub fn mark(&mut self, kind: JsIntrinsic) {
53
- match kind {
54
- JsIntrinsic::WebAudioCreateContext => self.needs_web_audio = true,
55
- JsIntrinsic::Uint8Array => self.needs_uint8_array = true,
56
- }
57
- }
58
-
59
- pub fn emit_expr(kind: JsIntrinsic, uint8_length_js: &str) -> String {
60
- match kind {
61
- JsIntrinsic::WebAudioCreateContext => "(__tishWebAudioCreateContext() ?? null)".to_string(),
62
- JsIntrinsic::Uint8Array => format!("(__tishUint8Array({}) ?? null)", uint8_length_js),
63
- }
64
- }
65
-
66
- /// Prepend runtime helpers (Uint8Array above AudioContext when both are used).
67
- pub fn prepend_runtime_preamble(&self, mut output: String) -> String {
68
- if self.needs_web_audio {
69
- output = format!(
70
- "function __tishWebAudioCreateContext(){{ return new AudioContext(); }}\n{}",
71
- output
72
- );
73
- }
74
- if self.needs_uint8_array {
75
- output = format!(
76
- "function __tishUint8Array(n){{ return new Uint8Array(n); }}\n{}",
77
- output
78
- );
79
- }
80
- output
81
- }
82
- }