@tishlang/tish 1.9.1 → 1.10.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 (79) hide show
  1. package/bin/tish +0 -0
  2. package/crates/js_to_tish/src/transform/expr.rs +8 -6
  3. package/crates/js_to_tish/src/transform/stmt.rs +12 -13
  4. package/crates/tish/Cargo.toml +1 -1
  5. package/crates/tish/src/cargo_native_registry.rs +4 -1
  6. package/crates/tish/src/main.rs +11 -8
  7. package/crates/tish/tests/integration_test.rs +145 -7
  8. package/crates/tish_ast/src/ast.rs +3 -9
  9. package/crates/tish_build_utils/src/lib.rs +43 -15
  10. package/crates/tish_builtins/src/array.rs +2 -3
  11. package/crates/tish_builtins/src/construct.rs +15 -28
  12. package/crates/tish_builtins/src/globals.rs +18 -16
  13. package/crates/tish_builtins/src/helpers.rs +1 -4
  14. package/crates/tish_builtins/src/lib.rs +1 -0
  15. package/crates/tish_builtins/src/object.rs +10 -10
  16. package/crates/tish_builtins/src/string.rs +1 -3
  17. package/crates/tish_builtins/src/symbol.rs +83 -0
  18. package/crates/tish_compile/src/codegen.rs +123 -138
  19. package/crates/tish_compile/src/lib.rs +25 -3
  20. package/crates/tish_compile/src/resolve.rs +6 -3
  21. package/crates/tish_compile/src/types.rs +6 -6
  22. package/crates/tish_compile_js/src/codegen.rs +50 -29
  23. package/crates/tish_compile_js/src/tests_jsx.rs +44 -0
  24. package/crates/tish_core/src/console_style.rs +9 -0
  25. package/crates/tish_core/src/json.rs +17 -7
  26. package/crates/tish_core/src/macros.rs +2 -2
  27. package/crates/tish_core/src/value.rs +192 -4
  28. package/crates/tish_cranelift_runtime/Cargo.toml +4 -0
  29. package/crates/tish_eval/src/eval.rs +135 -73
  30. package/crates/tish_eval/src/http.rs +18 -12
  31. package/crates/tish_eval/src/lib.rs +29 -0
  32. package/crates/tish_eval/src/regex.rs +1 -1
  33. package/crates/tish_eval/src/value.rs +89 -4
  34. package/crates/tish_eval/src/value_convert.rs +30 -8
  35. package/crates/tish_fmt/src/lib.rs +4 -1
  36. package/crates/tish_lexer/src/lib.rs +7 -2
  37. package/crates/tish_llvm/src/lib.rs +2 -2
  38. package/crates/tish_lsp/src/builtin_goto.rs +111 -10
  39. package/crates/tish_lsp/src/import_goto.rs +35 -22
  40. package/crates/tish_lsp/src/main.rs +118 -85
  41. package/crates/tish_native/src/build.rs +187 -10
  42. package/crates/tish_native/src/lib.rs +92 -8
  43. package/crates/tish_parser/src/lib.rs +77 -0
  44. package/crates/tish_parser/src/parser.rs +71 -74
  45. package/crates/tish_pg/src/error.rs +1 -1
  46. package/crates/tish_pg/src/lib.rs +61 -73
  47. package/crates/tish_resolve/src/lib.rs +283 -158
  48. package/crates/tish_resolve/src/pos.rs +10 -2
  49. package/crates/tish_runtime/Cargo.toml +3 -0
  50. package/crates/tish_runtime/src/http.rs +39 -39
  51. package/crates/tish_runtime/src/http_fetch.rs +12 -12
  52. package/crates/tish_runtime/src/lib.rs +26 -43
  53. package/crates/tish_runtime/src/native_promise.rs +0 -11
  54. package/crates/tish_runtime/src/promise.rs +14 -1
  55. package/crates/tish_runtime/src/promise_io.rs +1 -4
  56. package/crates/tish_runtime/src/ws.rs +40 -27
  57. package/crates/tish_runtime/tests/fetch_readable_stream.rs +10 -8
  58. package/crates/tish_ui/src/jsx.rs +6 -4
  59. package/crates/tish_ui/src/lib.rs +2 -2
  60. package/crates/tish_ui/src/runtime/hooks.rs +5 -15
  61. package/crates/tish_ui/src/runtime/mod.rs +16 -17
  62. package/crates/tish_vm/Cargo.toml +2 -0
  63. package/crates/tish_vm/src/vm.rs +218 -153
  64. package/crates/tish_wasm/src/lib.rs +33 -7
  65. package/crates/tish_wasm_runtime/Cargo.toml +4 -1
  66. package/crates/tish_wasm_runtime/src/lib.rs +2 -1
  67. package/crates/tishlang_cargo_bindgen/src/classify.rs +1 -3
  68. package/crates/tishlang_cargo_bindgen/src/discover.rs +10 -5
  69. package/crates/tishlang_cargo_bindgen/src/infer.rs +18 -8
  70. package/crates/tishlang_cargo_bindgen/src/lib.rs +25 -26
  71. package/crates/tishlang_cargo_bindgen/src/main.rs +41 -38
  72. package/crates/tishlang_cargo_bindgen/src/metadata.rs +4 -1
  73. package/justfile +3 -3
  74. package/package.json +1 -1
  75. package/platform/darwin-arm64/tish +0 -0
  76. package/platform/darwin-x64/tish +0 -0
  77. package/platform/linux-arm64/tish +0 -0
  78. package/platform/linux-x64/tish +0 -0
  79. package/platform/win32-x64/tish.exe +0 -0
@@ -560,15 +560,13 @@ pub fn compile_project_full(
560
560
  message: e,
561
561
  span: None,
562
562
  })?;
563
- let native_build = resolve::compute_native_build_artifacts(
564
- &merged.program,
565
- root,
566
- &native_modules,
567
- )
568
- .map_err(|e| CompileError {
569
- message: e,
570
- span: None,
571
- })?;
563
+ let native_build =
564
+ resolve::compute_native_build_artifacts(&merged.program, root, &native_modules).map_err(
565
+ |e| CompileError {
566
+ message: e,
567
+ span: None,
568
+ },
569
+ )?;
572
570
  let mut all_features: Vec<String> = features.to_vec();
573
571
  for f in resolve::extract_native_import_features(&merged.program) {
574
572
  if !all_features.contains(&f) {
@@ -731,12 +729,9 @@ impl Codegen {
731
729
  for _ in 0..8 {
732
730
  let mut changed = false;
733
731
  for (name, ann) in &raw {
734
- let resolved = crate::types::RustType::from_annotation_with_aliases(
735
- ann,
736
- &self.type_aliases,
737
- );
738
- let prev: Option<crate::types::RustType> =
739
- self.type_aliases.get(name).cloned();
732
+ let resolved =
733
+ crate::types::RustType::from_annotation_with_aliases(ann, &self.type_aliases);
734
+ let prev: Option<crate::types::RustType> = self.type_aliases.get(name).cloned();
740
735
  if prev.as_ref() != Some(&resolved) {
741
736
  self.type_aliases.insert(name.clone(), resolved);
742
737
  changed = true;
@@ -791,8 +786,11 @@ impl Codegen {
791
786
  fn emit_named_struct_decls(&mut self) {
792
787
  // Snapshot keys + values so we can mutate `self` (writing the
793
788
  // emitted source) inside the loop.
794
- let mut entries: Vec<(String, crate::types::RustType)> =
795
- self.type_aliases.iter().map(|(k, v)| (k.clone(), v.clone())).collect();
789
+ let mut entries: Vec<(String, crate::types::RustType)> = self
790
+ .type_aliases
791
+ .iter()
792
+ .map(|(k, v)| (k.clone(), v.clone()))
793
+ .collect();
796
794
  entries.sort_by(|a, b| a.0.cmp(&b.0));
797
795
  let mut emitted_any = false;
798
796
  for (name, ty) in entries {
@@ -928,7 +926,7 @@ impl Codegen {
928
926
  }
929
927
  };
930
928
  format!(
931
- "{{ let _ns = {}; match _ns {{ Value::Object(ref _o) => _o.borrow().get({:?}).cloned().unwrap_or(Value::Null), _ => Value::Null }} }}",
929
+ "{{ let _ns = {}; match _ns {{ Value::Object(ref _o) => _o.borrow().strings.get({:?}).cloned().unwrap_or(Value::Null), _ => Value::Null }} }}",
932
930
  init_expr, export_name
933
931
  )
934
932
  })
@@ -954,8 +952,9 @@ impl Codegen {
954
952
  // latter dispatches into `http_serve_per_worker`, which
955
953
  // calls onWorker once per accept thread to build that
956
954
  // thread's handler.
957
- "serve" => Some("Value::native(|args: &[Value]| { let handler = args.get(1).cloned().unwrap_or(Value::Null); match handler { Value::Function(f) => tish_http_serve(args, move |req_args| f(req_args)), Value::Object(ref opts) => { let factory = opts.borrow().get(&Arc::from(\"onWorker\")).cloned().unwrap_or(Value::Null); tishlang_runtime::http_serve_per_worker(args, factory) }, _ => Value::Null } })"),
955
+ "serve" => Some("Value::native(|args: &[Value]| { let handler = args.get(1).cloned().unwrap_or(Value::Null); match handler { Value::Function(f) => tish_http_serve(args, move |req_args| f(req_args)), Value::Object(ref opts) => { let factory = opts.borrow().strings.get(\"onWorker\").cloned().unwrap_or(Value::Null); tishlang_runtime::http_serve_per_worker(args, factory) }, _ => Value::Null } })"),
958
956
  "Promise" => Some("tish_promise_object()"),
957
+ "Symbol" => Some("tish_symbol_object()"),
959
958
  _ => None,
960
959
  },
961
960
  "tish:timers" if self.has_feature("timers") => match export_name {
@@ -970,8 +969,8 @@ impl Codegen {
970
969
  "cwd" => Some("Value::native(|args: &[Value]| tish_process_cwd(args))"),
971
970
  "exec" => Some("Value::native(|args: &[Value]| tish_process_exec(args))"),
972
971
  "argv" => Some("Value::Array(VmRef::new(std::env::args().map(|s| Value::String(s.into())).collect()))"),
973
- "env" => Some("Value::Object(VmRef::new(std::env::vars().map(|(k,v)| (Arc::from(k.as_str()), Value::String(v.into()))).collect()))"),
974
- "process" => Some("{ let mut m = ObjectMap::default(); m.insert(Arc::from(\"exit\"), Value::native(|args: &[Value]| tish_process_exit(args))); m.insert(Arc::from(\"cwd\"), Value::native(|args: &[Value]| tish_process_cwd(args))); m.insert(Arc::from(\"exec\"), Value::native(|args: &[Value]| tish_process_exec(args))); m.insert(Arc::from(\"argv\"), Value::Array(VmRef::new(std::env::args().map(|s| Value::String(s.into())).collect()))); m.insert(Arc::from(\"env\"), Value::Object(VmRef::new(std::env::vars().map(|(k,v)| (Arc::from(k.as_str()), Value::String(v.into()))).collect::<ObjectMap>()))); Value::Object(VmRef::new(m)) }"),
972
+ "env" => Some("Value::object(std::env::vars().map(|(k,v)| (Arc::from(k.as_str()), Value::String(v.into()))).collect())"),
973
+ "process" => Some("{ let mut m = ObjectMap::default(); m.insert(Arc::from(\"exit\"), Value::native(|args: &[Value]| tish_process_exit(args))); m.insert(Arc::from(\"cwd\"), Value::native(|args: &[Value]| tish_process_cwd(args))); m.insert(Arc::from(\"exec\"), Value::native(|args: &[Value]| tish_process_exec(args))); m.insert(Arc::from(\"argv\"), Value::Array(VmRef::new(std::env::args().map(|s| Value::String(s.into())).collect()))); m.insert(Arc::from(\"env\"), Value::object(std::env::vars().map(|(k,v)| (Arc::from(k.as_str()), Value::String(v.into()))).collect::<ObjectMap>())); Value::object(m) }"),
975
974
  _ => None,
976
975
  },
977
976
  "tish:ws" if self.has_feature("ws") => match export_name {
@@ -1226,7 +1225,7 @@ impl Codegen {
1226
1225
  self.write("use std::cell::RefCell;\n");
1227
1226
  self.write("use std::rc::Rc;\n");
1228
1227
  self.write("use std::sync::Arc;\n");
1229
- self.write("use tishlang_runtime::{console_debug as tish_console_debug, console_info as tish_console_info, console_log as tish_console_log, console_warn as tish_console_warn, console_error as tish_console_error, boolean as tish_boolean, decode_uri as tish_decode_uri, encode_uri as tish_encode_uri, string_escape_html_impl as tish_escape_html, in_operator as tish_in_operator, is_finite as tish_is_finite, is_nan as tish_is_nan, json_parse as tish_json_parse, json_stringify as tish_json_stringify, math_abs as tish_math_abs, math_ceil as tish_math_ceil, math_floor as tish_math_floor, math_max as tish_math_max, math_min as tish_math_min, math_round as tish_math_round, math_sqrt as tish_math_sqrt, parse_float as tish_parse_float, parse_int as tish_parse_int, math_random as tish_math_random, math_pow as tish_math_pow, math_sin as tish_math_sin, math_cos as tish_math_cos, math_tan as tish_math_tan, math_log as tish_math_log, math_exp as tish_math_exp, math_sign as tish_math_sign, math_trunc as tish_math_trunc, date_now as tish_date_now, array_is_array as tish_array_is_array, string_from_char_code as tish_string_from_char_code, object_assign as tish_object_assign, object_keys as tish_object_keys, object_values as tish_object_values, object_entries as tish_object_entries, object_from_entries as tish_object_from_entries, tish_construct, tish_uint8_array_constructor, tish_audio_context_constructor, register_static_route as tish_register_static_route, ObjectMap, TishError, Value, VmRef};\n");
1228
+ self.write("use tishlang_runtime::{console_debug as tish_console_debug, console_info as tish_console_info, console_log as tish_console_log, console_warn as tish_console_warn, console_error as tish_console_error, boolean as tish_boolean, decode_uri as tish_decode_uri, encode_uri as tish_encode_uri, string_escape_html_impl as tish_escape_html, in_operator as tish_in_operator, is_finite as tish_is_finite, is_nan as tish_is_nan, json_parse as tish_json_parse, json_stringify as tish_json_stringify, math_abs as tish_math_abs, math_ceil as tish_math_ceil, math_floor as tish_math_floor, math_max as tish_math_max, math_min as tish_math_min, math_round as tish_math_round, math_sqrt as tish_math_sqrt, parse_float as tish_parse_float, parse_int as tish_parse_int, math_random as tish_math_random, math_pow as tish_math_pow, math_sin as tish_math_sin, math_cos as tish_math_cos, math_tan as tish_math_tan, math_log as tish_math_log, math_exp as tish_math_exp, math_sign as tish_math_sign, math_trunc as tish_math_trunc, date_now as tish_date_now, array_is_array as tish_array_is_array, string_from_char_code as tish_string_from_char_code, object_assign as tish_object_assign, object_keys as tish_object_keys, object_values as tish_object_values, object_entries as tish_object_entries, object_from_entries as tish_object_from_entries, symbol_object as tish_symbol_object, tish_construct, tish_uint8_array_constructor, tish_audio_context_constructor, register_static_route as tish_register_static_route, ObjectMap, TishError, Value, VmRef};\n");
1230
1229
  if self.program_has_jsx {
1231
1230
  self.write("use tishlang_ui::{fragment_value, install_thread_local_host, native_create_root, native_use_state, ui_h, ui_text, HeadlessHost};\n");
1232
1231
  }
@@ -1295,7 +1294,7 @@ impl Codegen {
1295
1294
  self.indent += 1;
1296
1295
 
1297
1296
  // Initialize builtins
1298
- self.writeln("let mut console = Value::Object(VmRef::new(ObjectMap::from([");
1297
+ self.writeln("let mut console = Value::object(ObjectMap::from([");
1299
1298
  self.indent += 1;
1300
1299
  self.writeln("(Arc::from(\"debug\"), Value::native(|args: &[Value]| { tish_console_debug(args); Value::Null })),");
1301
1300
  self.writeln("(Arc::from(\"info\"), Value::native(|args: &[Value]| { tish_console_info(args); Value::Null })),");
@@ -1303,147 +1302,145 @@ impl Codegen {
1303
1302
  self.writeln("(Arc::from(\"warn\"), Value::native(|args: &[Value]| { tish_console_warn(args); Value::Null })),");
1304
1303
  self.writeln("(Arc::from(\"error\"), Value::native(|args: &[Value]| { tish_console_error(args); Value::Null })),");
1305
1304
  self.indent -= 1;
1306
- self.writeln("])));");
1307
- self.writeln(
1308
- "let Boolean = Value::native(|args: &[Value]| tish_boolean(args));",
1309
- );
1310
- self.writeln(
1311
- "let parseInt = Value::native(|args: &[Value]| tish_parse_int(args));",
1312
- );
1313
- self.writeln(
1314
- "let parseFloat = Value::native(|args: &[Value]| tish_parse_float(args));",
1315
- );
1316
- self.writeln(
1317
- "let decodeURI = Value::native(|args: &[Value]| tish_decode_uri(args));",
1318
- );
1319
- self.writeln(
1320
- "let encodeURI = Value::native(|args: &[Value]| tish_encode_uri(args));",
1321
- );
1305
+ self.writeln("]));");
1306
+ self.writeln("let Boolean = Value::native(|args: &[Value]| tish_boolean(args));");
1307
+ self.writeln("let parseInt = Value::native(|args: &[Value]| tish_parse_int(args));");
1308
+ self.writeln("let parseFloat = Value::native(|args: &[Value]| tish_parse_float(args));");
1309
+ self.writeln("let decodeURI = Value::native(|args: &[Value]| tish_decode_uri(args));");
1310
+ self.writeln("let encodeURI = Value::native(|args: &[Value]| tish_encode_uri(args));");
1322
1311
  self.writeln(
1323
1312
  r#"let registerStaticRoute = Value::native(|args: &[Value]| { let path = match args.get(0) { Some(Value::String(s)) => s.to_string(), _ => return Value::Null }; let body = match args.get(1) { Some(Value::String(s)) => s.as_bytes().to_vec(), _ => return Value::Null }; let ct = match args.get(2) { Some(Value::String(s)) => s.to_string(), _ => "application/octet-stream".to_string() }; tish_register_static_route(&path, &body, &ct); Value::Null });"#,
1324
1313
  );
1325
1314
  self.writeln(
1326
1315
  "let htmlEscape = Value::native(|args: &[Value]| tish_escape_html(args.first().unwrap_or(&Value::Null)));",
1327
1316
  );
1328
- self.writeln(
1329
- "let isFinite = Value::native(|args: &[Value]| tish_is_finite(args));",
1330
- );
1317
+ self.writeln("let isFinite = Value::native(|args: &[Value]| tish_is_finite(args));");
1331
1318
  self.writeln("let isNaN = Value::native(|args: &[Value]| tish_is_nan(args));");
1332
1319
  self.writeln("let Infinity = Value::Number(f64::INFINITY);");
1333
1320
  self.writeln("let NaN = Value::Number(f64::NAN);");
1334
- self.writeln("let Math = Value::Object(VmRef::new(ObjectMap::from([");
1321
+ self.writeln("let Math = Value::object(ObjectMap::from([");
1335
1322
  self.indent += 1;
1323
+ self.writeln("(Arc::from(\"abs\"), Value::native(|args: &[Value]| tish_math_abs(args))),");
1336
1324
  self.writeln(
1337
- "(Arc::from(\"abs\"), Value::native(|args: &[Value]| tish_math_abs(args))),",
1338
- );
1339
- self.writeln("(Arc::from(\"sqrt\"), Value::native(|args: &[Value]| tish_math_sqrt(args))),");
1340
- self.writeln(
1341
- "(Arc::from(\"min\"), Value::native(|args: &[Value]| tish_math_min(args))),",
1325
+ "(Arc::from(\"sqrt\"), Value::native(|args: &[Value]| tish_math_sqrt(args))),",
1342
1326
  );
1327
+ self.writeln("(Arc::from(\"min\"), Value::native(|args: &[Value]| tish_math_min(args))),");
1328
+ self.writeln("(Arc::from(\"max\"), Value::native(|args: &[Value]| tish_math_max(args))),");
1343
1329
  self.writeln(
1344
- "(Arc::from(\"max\"), Value::native(|args: &[Value]| tish_math_max(args))),",
1330
+ "(Arc::from(\"floor\"), Value::native(|args: &[Value]| tish_math_floor(args))),",
1345
1331
  );
1346
- self.writeln("(Arc::from(\"floor\"), Value::native(|args: &[Value]| tish_math_floor(args))),");
1347
- self.writeln("(Arc::from(\"ceil\"), Value::native(|args: &[Value]| tish_math_ceil(args))),");
1348
- self.writeln("(Arc::from(\"round\"), Value::native(|args: &[Value]| tish_math_round(args))),");
1349
- self.writeln("(Arc::from(\"random\"), Value::native(|args: &[Value]| tish_math_random(args))),");
1350
1332
  self.writeln(
1351
- "(Arc::from(\"pow\"), Value::native(|args: &[Value]| tish_math_pow(args))),",
1333
+ "(Arc::from(\"ceil\"), Value::native(|args: &[Value]| tish_math_ceil(args))),",
1352
1334
  );
1353
1335
  self.writeln(
1354
- "(Arc::from(\"sin\"), Value::native(|args: &[Value]| tish_math_sin(args))),",
1336
+ "(Arc::from(\"round\"), Value::native(|args: &[Value]| tish_math_round(args))),",
1355
1337
  );
1356
1338
  self.writeln(
1357
- "(Arc::from(\"cos\"), Value::native(|args: &[Value]| tish_math_cos(args))),",
1339
+ "(Arc::from(\"random\"), Value::native(|args: &[Value]| tish_math_random(args))),",
1358
1340
  );
1341
+ self.writeln("(Arc::from(\"pow\"), Value::native(|args: &[Value]| tish_math_pow(args))),");
1342
+ self.writeln("(Arc::from(\"sin\"), Value::native(|args: &[Value]| tish_math_sin(args))),");
1343
+ self.writeln("(Arc::from(\"cos\"), Value::native(|args: &[Value]| tish_math_cos(args))),");
1344
+ self.writeln("(Arc::from(\"tan\"), Value::native(|args: &[Value]| tish_math_tan(args))),");
1345
+ self.writeln("(Arc::from(\"log\"), Value::native(|args: &[Value]| tish_math_log(args))),");
1346
+ self.writeln("(Arc::from(\"exp\"), Value::native(|args: &[Value]| tish_math_exp(args))),");
1359
1347
  self.writeln(
1360
- "(Arc::from(\"tan\"), Value::native(|args: &[Value]| tish_math_tan(args))),",
1348
+ "(Arc::from(\"sign\"), Value::native(|args: &[Value]| tish_math_sign(args))),",
1361
1349
  );
1362
1350
  self.writeln(
1363
- "(Arc::from(\"log\"), Value::native(|args: &[Value]| tish_math_log(args))),",
1351
+ "(Arc::from(\"trunc\"), Value::native(|args: &[Value]| tish_math_trunc(args))),",
1364
1352
  );
1365
- self.writeln(
1366
- "(Arc::from(\"exp\"), Value::native(|args: &[Value]| tish_math_exp(args))),",
1367
- );
1368
- self.writeln("(Arc::from(\"sign\"), Value::native(|args: &[Value]| tish_math_sign(args))),");
1369
- self.writeln("(Arc::from(\"trunc\"), Value::native(|args: &[Value]| tish_math_trunc(args))),");
1370
1353
  self.writeln("(Arc::from(\"PI\"), Value::Number(std::f64::consts::PI)),");
1371
1354
  self.writeln("(Arc::from(\"E\"), Value::Number(std::f64::consts::E)),");
1372
1355
  self.indent -= 1;
1373
- self.writeln("])));");
1374
- self.writeln("let JSON = Value::Object(VmRef::new(ObjectMap::from([");
1356
+ self.writeln("]));");
1357
+ self.writeln("let JSON = Value::object(ObjectMap::from([");
1375
1358
  self.indent += 1;
1376
- self.writeln("(Arc::from(\"parse\"), Value::native(|args: &[Value]| tish_json_parse(args))),");
1359
+ self.writeln(
1360
+ "(Arc::from(\"parse\"), Value::native(|args: &[Value]| tish_json_parse(args))),",
1361
+ );
1377
1362
  self.writeln("(Arc::from(\"stringify\"), Value::native(|args: &[Value]| tish_json_stringify(args))),");
1378
1363
  self.indent -= 1;
1379
- self.writeln("])));");
1364
+ self.writeln("]));");
1380
1365
 
1381
- self.writeln("let Array = Value::Object(VmRef::new(ObjectMap::from([");
1366
+ self.writeln("let Array = Value::object(ObjectMap::from([");
1382
1367
  self.indent += 1;
1383
- self.writeln("(Arc::from(\"isArray\"), Value::native(|args: &[Value]| tish_array_is_array(args))),");
1368
+ self.writeln(
1369
+ "(Arc::from(\"isArray\"), Value::native(|args: &[Value]| tish_array_is_array(args))),",
1370
+ );
1384
1371
  self.indent -= 1;
1385
- self.writeln("])));");
1372
+ self.writeln("]));");
1386
1373
 
1387
- self.writeln("let String = Value::Object(VmRef::new(ObjectMap::from([");
1374
+ self.writeln("let String = Value::object(ObjectMap::from([");
1388
1375
  self.indent += 1;
1389
1376
  self.writeln("(Arc::from(\"fromCharCode\"), Value::native(|args: &[Value]| tish_string_from_char_code(args))),");
1390
1377
  self.indent -= 1;
1391
- self.writeln("])));");
1378
+ self.writeln("]));");
1392
1379
 
1393
- self.writeln("let Date = Value::Object(VmRef::new(ObjectMap::from([");
1380
+ self.writeln("let Date = Value::object(ObjectMap::from([");
1394
1381
  self.indent += 1;
1395
- self.writeln(
1396
- "(Arc::from(\"now\"), Value::native(|args: &[Value]| tish_date_now(args))),",
1397
- );
1382
+ self.writeln("(Arc::from(\"now\"), Value::native(|args: &[Value]| tish_date_now(args))),");
1398
1383
  self.indent -= 1;
1399
- self.writeln("])));");
1384
+ self.writeln("]));");
1400
1385
 
1401
- self.writeln("let Object = Value::Object(VmRef::new(ObjectMap::from([");
1386
+ self.writeln("let Symbol = tish_symbol_object();");
1387
+
1388
+ self.writeln("let Object = Value::object(ObjectMap::from([");
1402
1389
  self.indent += 1;
1403
- self.writeln("(Arc::from(\"assign\"), Value::native(|args: &[Value]| tish_object_assign(args))),");
1404
- self.writeln("(Arc::from(\"keys\"), Value::native(|args: &[Value]| tish_object_keys(args))),");
1405
- self.writeln("(Arc::from(\"values\"), Value::native(|args: &[Value]| tish_object_values(args))),");
1406
- self.writeln("(Arc::from(\"entries\"), Value::native(|args: &[Value]| tish_object_entries(args))),");
1390
+ self.writeln(
1391
+ "(Arc::from(\"assign\"), Value::native(|args: &[Value]| tish_object_assign(args))),",
1392
+ );
1393
+ self.writeln(
1394
+ "(Arc::from(\"keys\"), Value::native(|args: &[Value]| tish_object_keys(args))),",
1395
+ );
1396
+ self.writeln(
1397
+ "(Arc::from(\"values\"), Value::native(|args: &[Value]| tish_object_values(args))),",
1398
+ );
1399
+ self.writeln(
1400
+ "(Arc::from(\"entries\"), Value::native(|args: &[Value]| tish_object_entries(args))),",
1401
+ );
1407
1402
  self.writeln("(Arc::from(\"fromEntries\"), Value::native(|args: &[Value]| tish_object_from_entries(args))),");
1408
1403
  self.indent -= 1;
1409
- self.writeln("])));");
1404
+ self.writeln("]));");
1410
1405
 
1411
1406
  self.writeln("let Uint8Array = tish_uint8_array_constructor();");
1412
1407
  self.writeln("let AudioContext = tish_audio_context_constructor();");
1413
1408
 
1414
1409
  if self.has_feature("process") {
1415
- self.writeln("let process = Value::Object(VmRef::new({");
1410
+ self.writeln("let process = Value::object({");
1416
1411
  self.indent += 1;
1417
1412
  self.writeln("let mut p = ObjectMap::default();");
1418
1413
  self.writeln("p.insert(Arc::from(\"exit\"), Value::native(|args: &[Value]| tish_process_exit(args)));");
1419
1414
  self.writeln("p.insert(Arc::from(\"cwd\"), Value::native(|args: &[Value]| tish_process_cwd(args)));");
1420
1415
  self.writeln("p.insert(Arc::from(\"exec\"), Value::native(|args: &[Value]| tish_process_exec(args)));");
1421
1416
  self.writeln("let argv: Vec<Value> = std::env::args().map(|s| Value::String(s.into())).collect();");
1422
- self.writeln(
1423
- "p.insert(Arc::from(\"argv\"), Value::Array(VmRef::new(argv)));",
1424
- );
1417
+ self.writeln("p.insert(Arc::from(\"argv\"), Value::Array(VmRef::new(argv)));");
1425
1418
  self.writeln("let mut env_obj = ObjectMap::default();");
1426
1419
  self.writeln("for (key, value) in std::env::vars() {");
1427
1420
  self.indent += 1;
1428
1421
  self.writeln("env_obj.insert(Arc::from(key.as_str()), Value::String(value.into()));");
1429
1422
  self.indent -= 1;
1430
1423
  self.writeln("}");
1431
- self.writeln(
1432
- "p.insert(Arc::from(\"env\"), Value::Object(VmRef::new(env_obj)));",
1433
- );
1424
+ self.writeln("p.insert(Arc::from(\"env\"), Value::object(env_obj));");
1434
1425
  self.writeln("p");
1435
1426
  self.indent -= 1;
1436
- self.writeln("}));");
1427
+ self.writeln("});");
1437
1428
  }
1438
1429
 
1439
1430
  if self.has_feature("timers") {
1440
- self.writeln("let setTimeout = Value::native(|args: &[Value]| tish_timer_set_timeout(args));");
1431
+ self.writeln(
1432
+ "let setTimeout = Value::native(|args: &[Value]| tish_timer_set_timeout(args));",
1433
+ );
1441
1434
  self.writeln("let clearTimeout = Value::native(|args: &[Value]| tish_timer_clear_timeout(args));");
1442
- self.writeln("let setInterval = Value::native(|args: &[Value]| tish_timer_set_interval(args));");
1435
+ self.writeln(
1436
+ "let setInterval = Value::native(|args: &[Value]| tish_timer_set_interval(args));",
1437
+ );
1443
1438
  self.writeln("let clearInterval = Value::native(|args: &[Value]| tish_timer_clear_interval(args));");
1444
1439
  }
1445
1440
  if self.has_feature("http") {
1446
- self.writeln("let fetch = Value::native(|args: &[Value]| tish_fetch_promise(args.to_vec()));");
1441
+ self.writeln(
1442
+ "let fetch = Value::native(|args: &[Value]| tish_fetch_promise(args.to_vec()));",
1443
+ );
1447
1444
  self.writeln("let fetchAll = Value::native(|args: &[Value]| tish_fetch_all_promise(args.to_vec()));");
1448
1445
  if self.is_async {
1449
1446
  self.writeln("let Promise = tish_promise_object();");
@@ -1461,10 +1458,12 @@ impl Codegen {
1461
1458
  self.writeln("let handler = args.get(1).cloned().unwrap_or(Value::Null);");
1462
1459
  self.writeln("match handler {");
1463
1460
  self.indent += 1;
1464
- self.writeln("Value::Function(f) => tish_http_serve(args, move |req_args| f(req_args)),");
1461
+ self.writeln(
1462
+ "Value::Function(f) => tish_http_serve(args, move |req_args| f(req_args)),",
1463
+ );
1465
1464
  self.writeln("Value::Object(ref opts) => {");
1466
1465
  self.indent += 1;
1467
- self.writeln("let factory = opts.borrow().get(&Arc::from(\"onWorker\")).cloned().unwrap_or(Value::Null);");
1466
+ self.writeln("let factory = opts.borrow().strings.get(\"onWorker\").cloned().unwrap_or(Value::Null);");
1468
1467
  self.writeln("tishlang_runtime::http_serve_per_worker(args, factory)");
1469
1468
  self.indent -= 1;
1470
1469
  self.writeln("},");
@@ -1476,28 +1475,18 @@ impl Codegen {
1476
1475
  }
1477
1476
 
1478
1477
  if self.has_feature("fs") {
1478
+ self.writeln("let readFile = Value::native(|args: &[Value]| tish_read_file(args));");
1479
+ self.writeln("let writeFile = Value::native(|args: &[Value]| tish_write_file(args));");
1479
1480
  self.writeln(
1480
- "let readFile = Value::native(|args: &[Value]| tish_read_file(args));",
1481
- );
1482
- self.writeln(
1483
- "let writeFile = Value::native(|args: &[Value]| tish_write_file(args));",
1484
- );
1485
- self.writeln("let fileExists = Value::native(|args: &[Value]| tish_file_exists(args));");
1486
- self.writeln(
1487
- "let isDir = Value::native(|args: &[Value]| tish_is_dir(args));",
1488
- );
1489
- self.writeln(
1490
- "let readDir = Value::native(|args: &[Value]| tish_read_dir(args));",
1491
- );
1492
- self.writeln(
1493
- "let mkdir = Value::native(|args: &[Value]| tish_mkdir(args));",
1481
+ "let fileExists = Value::native(|args: &[Value]| tish_file_exists(args));",
1494
1482
  );
1483
+ self.writeln("let isDir = Value::native(|args: &[Value]| tish_is_dir(args));");
1484
+ self.writeln("let readDir = Value::native(|args: &[Value]| tish_read_dir(args));");
1485
+ self.writeln("let mkdir = Value::native(|args: &[Value]| tish_mkdir(args));");
1495
1486
  }
1496
1487
 
1497
1488
  if self.has_feature("regex") {
1498
- self.writeln(
1499
- "let RegExp = Value::native(|args: &[Value]| regexp_new(args));",
1500
- );
1489
+ self.writeln("let RegExp = Value::native(|args: &[Value]| regexp_new(args));");
1501
1490
  }
1502
1491
 
1503
1492
  if self.program_has_jsx {
@@ -1505,10 +1494,10 @@ impl Codegen {
1505
1494
  self.writeln("let Fragment = fragment_value();");
1506
1495
  self.writeln("let h = Value::native(|args: &[Value]| ui_h(args));");
1507
1496
  self.writeln("let text = Value::native(|args: &[Value]| ui_text(args));");
1497
+ self.writeln("let useState = Value::native(|args: &[Value]| native_use_state(args));");
1508
1498
  self.writeln(
1509
- "let useState = Value::native(|args: &[Value]| native_use_state(args));",
1499
+ "let createRoot = Value::native(|args: &[Value]| native_create_root(args));",
1510
1500
  );
1511
- self.writeln("let createRoot = Value::native(|args: &[Value]| native_create_root(args));");
1512
1501
  }
1513
1502
 
1514
1503
  // Polars, Egui etc. are emitted via VarDecl from import { X } from 'tish:...'
@@ -1622,10 +1611,7 @@ impl Codegen {
1622
1611
  };
1623
1612
  if self.refcell_wrapped_vars.contains(name.as_ref()) {
1624
1613
  // Closure-mutated: same Rc<RefCell<T>> pattern as Value (assignments use borrow_mut)
1625
- self.writeln(&format!(
1626
- "let {} = VmRef::new({});",
1627
- escaped_name, expr_str
1628
- ));
1614
+ self.writeln(&format!("let {} = VmRef::new({});", escaped_name, expr_str));
1629
1615
  self.rc_cell_storage_define(name.as_ref());
1630
1616
  } else {
1631
1617
  let type_str = rust_type.to_rust_type_str();
@@ -1653,10 +1639,7 @@ impl Codegen {
1653
1639
  } else {
1654
1640
  expr_str.to_string()
1655
1641
  };
1656
- self.writeln(&format!(
1657
- "let {} = VmRef::new({});",
1658
- escaped_name, init_val
1659
- ));
1642
+ self.writeln(&format!("let {} = VmRef::new({});", escaped_name, init_val));
1660
1643
  self.rc_cell_storage_define(name.as_ref());
1661
1644
  } else if clone_needed {
1662
1645
  self.writeln(&format!(
@@ -2036,6 +2019,7 @@ impl Codegen {
2036
2019
  "setInterval",
2037
2020
  "clearInterval",
2038
2021
  "Promise",
2022
+ "Symbol",
2039
2023
  "RegExp",
2040
2024
  "Polars",
2041
2025
  ]
@@ -2136,6 +2120,7 @@ impl Codegen {
2136
2120
  "setInterval",
2137
2121
  "clearInterval",
2138
2122
  "Promise",
2123
+ "Symbol",
2139
2124
  "RegExp",
2140
2125
  "Polars",
2141
2126
  // Free-standing global functions used inside user-defined
@@ -2409,7 +2394,7 @@ impl Codegen {
2409
2394
  match &prop.value {
2410
2395
  DestructElement::Ident(name, _) => {
2411
2396
  self.writeln(&format!(
2412
- "{} {} = match &({}) {{ Value::Object(ref _o) => _o.borrow().get({:?}).cloned().unwrap_or(Value::Null), _ => Value::Null }};",
2397
+ "{} {} = match &({}) {{ Value::Object(ref _o) => _o.borrow().strings.get({:?}).cloned().unwrap_or(Value::Null), _ => Value::Null }};",
2413
2398
  mutability,
2414
2399
  Self::escape_ident(name.as_ref()),
2415
2400
  value_expr,
@@ -2419,7 +2404,7 @@ impl Codegen {
2419
2404
  DestructElement::Pattern(nested) => {
2420
2405
  let nested_var = format!("_nested_obj_{}", key);
2421
2406
  self.writeln(&format!(
2422
- "let {} = match &({}) {{ Value::Object(ref _o) => _o.borrow().get({:?}).cloned().unwrap_or(Value::Null), _ => Value::Null }};",
2407
+ "let {} = match &({}) {{ Value::Object(ref _o) => _o.borrow().strings.get({:?}).cloned().unwrap_or(Value::Null), _ => Value::Null }};",
2423
2408
  nested_var, value_expr, key
2424
2409
  ));
2425
2410
  self.emit_destruct_bindings(nested, &nested_var, mutability, span)?;
@@ -2980,7 +2965,7 @@ impl Codegen {
2980
2965
  if has_spread {
2981
2966
  let args_code = self.emit_call_args(args)?;
2982
2967
  return Ok(format!(
2983
- "{{ let _callee = &{}; let _spread_args = {}; match _callee {{ Value::Function(cb) => cb(&_spread_args), other => panic!(\"Not a function: tried to call {{:?}} as a function (e.g. method on Null when read failed)\", other) }} }}",
2968
+ "{{ let _callee = ({}).clone(); let _spread_args = {}; tishlang_runtime::value_call(&_callee, _spread_args.as_slice()) }}",
2984
2969
  callee_expr, args_code
2985
2970
  ));
2986
2971
  }
@@ -2994,8 +2979,8 @@ impl Codegen {
2994
2979
  .join(", ");
2995
2980
  format!(
2996
2981
  "({{\n\
2997
- {} let _callee = &{};\n\
2998
- {} match _callee {{ Value::Function(cb) => cb(&[{}]), other => panic!(\"Not a function: tried to call {{:?}} as a function (e.g. method on Null when read failed)\", other) }}\n\
2982
+ {} let _callee = ({}).clone();\n\
2983
+ {} tishlang_runtime::value_call(&_callee, &[{}])\n\
2999
2984
  {}}})",
3000
2985
  " ".repeat(self.indent),
3001
2986
  callee_expr,
@@ -3160,11 +3145,11 @@ impl Codegen {
3160
3145
  }
3161
3146
  ObjectProp::Spread(e) => {
3162
3147
  let val = self.emit_expr(e)?;
3163
- parts.push(format!("if let Value::Object(ref _spread) = {} {{ for (k, v) in _spread.borrow().iter() {{ _obj.insert(Arc::clone(k), v.clone()); }} }}", val));
3148
+ parts.push(format!("if let Value::Object(ref _spread) = {} {{ for (k, v) in _spread.borrow().strings.iter() {{ _obj.insert(Arc::clone(k), v.clone()); }} }}", val));
3164
3149
  }
3165
3150
  }
3166
3151
  }
3167
- format!("{{ let mut _obj: ObjectMap = ObjectMap::default(); {} Value::Object(VmRef::new(_obj)) }}", parts.join(" "))
3152
+ format!("{{ let mut _obj: ObjectMap = ObjectMap::default(); {} Value::object(_obj) }}", parts.join(" "))
3168
3153
  } else {
3169
3154
  let mut parts = Vec::new();
3170
3155
  for prop in props {
@@ -3178,7 +3163,7 @@ impl Codegen {
3178
3163
  }
3179
3164
  }
3180
3165
  format!(
3181
- "Value::Object(VmRef::new(ObjectMap::from([{}])))",
3166
+ "Value::object(ObjectMap::from([{}]))",
3182
3167
  parts.join(", ")
3183
3168
  )
3184
3169
  }
@@ -3274,7 +3259,8 @@ impl Codegen {
3274
3259
  Value::Number(_) => \"number\".into(), Value::String(_) => \"string\".into(), \
3275
3260
  Value::Bool(_) => \"boolean\".into(), Value::Null => \"null\".into(), \
3276
3261
  Value::Array(_) => \"object\".into(), Value::Object(_) => \"object\".into(), \
3277
- Value::Function(_) => \"function\".into(), _ => \"object\".into() }})",
3262
+ Value::Function(_) => \"function\".into(), Value::Symbol(_) => \"symbol\".into(), \
3263
+ _ => \"object\".into() }})",
3278
3264
  o
3279
3265
  )
3280
3266
  }
@@ -4606,6 +4592,7 @@ impl Codegen {
4606
4592
  "setInterval",
4607
4593
  "clearInterval",
4608
4594
  "Promise",
4595
+ "Symbol",
4609
4596
  "RegExp",
4610
4597
  "Polars",
4611
4598
  ]
@@ -4679,6 +4666,7 @@ impl Codegen {
4679
4666
  "setInterval",
4680
4667
  "clearInterval",
4681
4668
  "Promise",
4669
+ "Symbol",
4682
4670
  "RegExp",
4683
4671
  "Polars",
4684
4672
  ] {
@@ -4886,14 +4874,11 @@ impl Codegen {
4886
4874
  // alias). Each property in source order is matched to a struct
4887
4875
  // field; missing fields fall back to `default_value()` so the
4888
4876
  // emit succeeds even on partial literals (rare, but harmless).
4889
- if let (RustType::Named { name, fields }, Expr::Object { props, .. }) =
4890
- (target_type, expr)
4877
+ if let (RustType::Named { name, fields }, Expr::Object { props, .. }) = (target_type, expr)
4891
4878
  {
4892
4879
  use std::collections::HashMap;
4893
- let field_types: HashMap<&str, &RustType> = fields
4894
- .iter()
4895
- .map(|(k, t)| (k.as_ref(), t))
4896
- .collect();
4880
+ let field_types: HashMap<&str, &RustType> =
4881
+ fields.iter().map(|(k, t)| (k.as_ref(), t)).collect();
4897
4882
  let mut field_inits: HashMap<String, String> = HashMap::new();
4898
4883
  let mut bail = false;
4899
4884
  for prop in props {
@@ -17,9 +17,9 @@ pub use resolve::{
17
17
  extract_native_import_features, format_rust_dependencies_toml, generate_native_wrapper_rs,
18
18
  has_external_native_imports, has_native_imports, infer_native_module_exports,
19
19
  is_builtin_native_spec, is_cargo_native_spec, is_native_import, merge_modules,
20
- normalize_builtin_spec, read_project_tish_config,
21
- resolve_bare_spec, resolve_native_modules, resolve_project, resolve_project_from_stdin,
22
- MergedProgram, NativeBuildArtifacts, NativeModuleInit, ResolvedNativeModule,
20
+ normalize_builtin_spec, read_project_tish_config, resolve_bare_spec, resolve_native_modules,
21
+ resolve_project, resolve_project_from_stdin, MergedProgram, NativeBuildArtifacts,
22
+ NativeModuleInit, ResolvedNativeModule,
23
23
  };
24
24
  pub use types::{RustType, TypeContext};
25
25
 
@@ -106,6 +106,28 @@ fn factory() {
106
106
  );
107
107
  }
108
108
 
109
+ /// `value_call` must take `&Value` to a **local** (`let _callee = (<expr>).clone(); … &_callee`):
110
+ /// `&<temporary>` can dangle in release, and `let _callee = <ident>` would move globals like `Symbol`.
111
+ #[test]
112
+ fn native_emit_value_call_materializes_callee() {
113
+ use std::path::PathBuf;
114
+ let manifest = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
115
+ let path = manifest.join("../../tests/core/symbol.tish").canonicalize().unwrap();
116
+ let (rust, _, _, _) = compile_project_full(&path, path.parent(), &[], true).unwrap();
117
+ assert!(
118
+ rust.contains("let _callee = (tishlang_runtime::get_index"),
119
+ "fixture should bracket-call via get_index with callee stored in a local"
120
+ );
121
+ assert!(
122
+ !rust.contains("let _callee = &tishlang_runtime::get_index"),
123
+ "expected callee materialization, found reference-to-temporary pattern"
124
+ );
125
+ assert!(
126
+ rust.contains("tishlang_runtime::value_call"),
127
+ "expected value_call via runtime re-export for nested Cargo builds"
128
+ );
129
+ }
130
+
109
131
  #[test]
110
132
  fn loop_var_decl_clone_via_project_full() {
111
133
  // With the inference pass, `let outerVar = 42` is inferred as f64 (Copy) — no clone needed.
@@ -67,8 +67,10 @@ pub fn normalize_builtin_spec(spec: &str) -> Option<String> {
67
67
 
68
68
  /// Built-in modules that come from tishlang_runtime, not from package.json.
69
69
  pub fn is_builtin_native_spec(spec: &str) -> bool {
70
- matches!(spec, "tish:fs" | "tish:http" | "tish:timers" | "tish:process" | "tish:ws")
71
- || matches!(spec, "fs" | "http" | "timers" | "process" | "ws")
70
+ matches!(
71
+ spec,
72
+ "tish:fs" | "tish:http" | "tish:timers" | "tish:process" | "tish:ws"
73
+ ) || matches!(spec, "fs" | "http" | "timers" | "process" | "ws")
72
74
  }
73
75
 
74
76
  /// Resolve all native imports in a merged program via package.json lookup.
@@ -774,7 +776,8 @@ fn resolve_package_root_to_entry(pkg_root: &Path, spec: &str) -> Option<PathBuf>
774
776
  pub fn resolve_bare_spec(spec: &str, from_dir: &Path, _project_root: &Path) -> Option<PathBuf> {
775
777
  let mut search = from_dir.to_path_buf();
776
778
  loop {
777
- if let Some(p) = resolve_package_root_to_entry(&search.join("node_modules").join(spec), spec)
779
+ if let Some(p) =
780
+ resolve_package_root_to_entry(&search.join("node_modules").join(spec), spec)
778
781
  {
779
782
  return Some(p);
780
783
  }
@@ -83,9 +83,9 @@ impl RustType {
83
83
  RustType::Value
84
84
  }
85
85
  },
86
- TypeAnnotation::Array(elem) => RustType::Vec(Box::new(
87
- Self::from_annotation_with_aliases(elem, aliases),
88
- )),
86
+ TypeAnnotation::Array(elem) => {
87
+ RustType::Vec(Box::new(Self::from_annotation_with_aliases(elem, aliases)))
88
+ }
89
89
  TypeAnnotation::Object(fields) => {
90
90
  let typed_fields: Vec<_> = fields
91
91
  .iter()
@@ -115,9 +115,9 @@ impl RustType {
115
115
  |t| !matches!(t, TypeAnnotation::Simple(s) if s.as_ref() == "null"),
116
116
  );
117
117
  if let Some(inner) = non_null {
118
- return RustType::Option(Box::new(
119
- Self::from_annotation_with_aliases(inner, aliases),
120
- ));
118
+ return RustType::Option(Box::new(Self::from_annotation_with_aliases(
119
+ inner, aliases,
120
+ )));
121
121
  }
122
122
  }
123
123
  }