MIRROR: javascript for ๐Ÿœ's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

use OXC for handling hoisted

+412 -77
+2
include/config.h
··· 61 61 SLOT_OBSERVABLE_SUBSCRIBER, 62 62 SLOT_SUBSCRIPTION_OBSERVER, 63 63 SLOT_SUBSCRIPTION_CLEANUP, 64 + SLOT_HOISTED_VARS, 65 + SLOT_HOISTED_VARS_LEN, 64 66 SLOT_MAX = 255 65 67 } internal_slot_t; 66 68
+2
include/config.h.in
··· 50 50 SLOT_OBSERVABLE_SUBSCRIBER, 51 51 SLOT_SUBSCRIPTION_OBSERVER, 52 52 SLOT_SUBSCRIPTION_CLEANUP, 53 + SLOT_HOISTED_VARS, 54 + SLOT_HOISTED_VARS_LEN, 53 55 SLOT_MAX = 255 54 56 } internal_slot_t; 55 57
+86 -15
src/ant.c
··· 454 454 [TOK_SEMICOLON] = 1, [TOK_COMMA] = 1, [TOK_EOF] = 1, 455 455 }; 456 456 457 + static const uint8_t expr_context_tok[TOK_MAX] = { 458 + [TOK_ASSIGN] = 1, [TOK_LPAREN] = 1, [TOK_COLON] = 1, [TOK_LBRACKET] = 1, 459 + [TOK_COMMA] = 1, [TOK_NOT] = 1, [TOK_Q] = 1, [TOK_OR] = 1, [TOK_AND] = 1, 460 + [TOK_RETURN] = 1, [TOK_ARROW] = 1, [TOK_LAND] = 1, [TOK_LOR] = 1, 461 + [TOK_PLUS_ASSIGN] = 1, [TOK_MINUS_ASSIGN] = 1, [TOK_MUL_ASSIGN] = 1, 462 + [TOK_DIV_ASSIGN] = 1, [TOK_REM_ASSIGN] = 1, [TOK_AND_ASSIGN] = 1, 463 + [TOK_OR_ASSIGN] = 1, [TOK_XOR_ASSIGN] = 1, [TOK_NULLISH] = 1, 464 + }; 465 + 457 466 static const char *typestr_raw(uint8_t t) { 458 467 const char *names[] = { 459 468 "object", "prop", "string", "undefined", "null", "number", ··· 3393 3402 static void set_func_code(struct js *js, jsval_t func_obj, const char *code, size_t len) { 3394 3403 const char *arena_code = code_arena_alloc(code, len); 3395 3404 if (!arena_code) return; 3405 + 3396 3406 set_func_code_ptr(js, func_obj, arena_code, len); 3407 + if (!memmem(code, len, "var", 3)) return; 3408 + 3409 + size_t vars_buf_len; 3410 + char *vars = OXC_get_func_hoisted_vars(code, len, &vars_buf_len); 3411 + 3412 + if (vars) { 3413 + set_slot(js, func_obj, SLOT_HOISTED_VARS, mkval(T_CFUNC, (size_t)vars)); 3414 + set_slot(js, func_obj, SLOT_HOISTED_VARS_LEN, tov((double)vars_buf_len)); 3415 + } 3397 3416 } 3398 3417 3399 3418 static const char *get_func_code(struct js *js, jsval_t func_obj, jsoff_t *len) { ··· 4943 4962 jsval_t saved_scope = js->scope; 4944 4963 4945 4964 int depth = 0; 4946 - uint8_t tok; 4965 + uint8_t tok, prev_tok = TOK_EOF; 4947 4966 4948 4967 while ((tok = next(js)) != TOK_EOF && !(tok == TOK_RBRACE && depth == 0)) { 4949 - if (tok == TOK_LBRACE) { depth++; js->consumed = 1; continue; } 4950 - if (tok == TOK_RBRACE) { depth--; js->consumed = 1; continue; } 4951 - if (depth > 0) { js->consumed = 1; continue; } 4968 + if (tok == TOK_LBRACE) { depth++; prev_tok = tok; js->consumed = 1; continue; } 4969 + if (tok == TOK_RBRACE) { depth--; prev_tok = tok; js->consumed = 1; continue; } 4970 + if (depth > 0) { prev_tok = tok; js->consumed = 1; continue; } 4952 4971 4953 4972 if (tok == TOK_EXPORT) { 4954 4973 js->consumed = 1; ··· 4963 4982 js->consumed = 1; 4964 4983 } 4965 4984 4966 - skip_export: 4967 - continue; 4985 + skip_export: { 4986 + prev_tok = tok; 4987 + continue; 4988 + } 4968 4989 } 4969 4990 4970 4991 if (depth == 0 && tok == TOK_FUNC) { 4992 + if (expr_context_tok[prev_tok]) { 4993 + prev_tok = tok; 4994 + js->consumed = 1; 4995 + continue; 4996 + } 4997 + 4971 4998 jsoff_t after_func = js->pos; 4972 4999 js->consumed = 1; 5000 + 4973 5001 if (next(js) == TOK_IDENTIFIER) { 4974 5002 js->pos = after_func; 4975 5003 js->tok = TOK_FUNC; 4976 5004 js->consumed = 1; 4977 5005 js_func_decl(js); 4978 5006 } 5007 + 5008 + prev_tok = tok; 4979 5009 continue; 4980 5010 } 4981 5011 4982 5012 if (depth == 0 && tok == TOK_ASYNC) { 5013 + if (expr_context_tok[prev_tok]) { 5014 + prev_tok = tok; 5015 + js->consumed = 1; 5016 + continue; 5017 + } 5018 + 4983 5019 js->consumed = 1; 4984 5020 if (next(js) != TOK_FUNC) goto skip_async; 4985 5021 jsoff_t func_pos = js->pos; ··· 4989 5025 js->tok = TOK_FUNC; 4990 5026 js->consumed = 0; 4991 5027 js_func_decl_async(js); 4992 - skip_async: 4993 - continue; 5028 + 5029 + skip_async: { 5030 + prev_tok = tok; 5031 + continue; 5032 + } 4994 5033 } 4995 5034 5035 + prev_tok = tok; 4996 5036 js->consumed = 1; 4997 5037 } 4998 5038 4999 5039 js->is_hoisting = false; 5000 5040 JS_RESTORE_STATE(js, saved); 5001 5041 js->scope = saved_scope; 5042 + } 5043 + 5044 + static void declare_hoisted_vars(struct js *js, jsval_t var_scope, const char *var_names) { 5045 + const char *ptr = var_names; 5046 + while (*ptr) { 5047 + size_t len = strlen(ptr); 5048 + jsoff_t existing = lkp(js, var_scope, ptr, len); 5049 + if (existing == 0) mkprop(js, var_scope, js_mkstr(js, ptr, len), js_mkundef(), 0); 5050 + ptr += len + 1; 5051 + } 5052 + } 5053 + 5054 + static void hoist_var_declarations_from_slot(struct js *js, jsval_t var_scope, jsval_t func_obj) { 5055 + jsval_t vars_val = get_slot(js, func_obj, SLOT_HOISTED_VARS); 5056 + if (vtype(vars_val) != T_CFUNC) return; 5057 + const char *var_names = (const char *)vdata(vars_val); 5058 + if (!var_names) return; 5059 + declare_hoisted_vars(js, var_scope, var_names); 5060 + } 5061 + 5062 + static void hoist_var_declarations(struct js *js, jsval_t var_scope) { 5063 + if (js->flags & F_NOEXEC) return; 5064 + if (js->clen == 0) return; 5065 + if (!memmem(js->code, js->clen, "var", 3)) return; 5066 + 5067 + size_t buf_len; 5068 + char *var_names = OXC_get_hoisted_vars(js->code, (size_t)js->clen, &buf_len); 5069 + if (!var_names) return; 5070 + 5071 + declare_hoisted_vars(js, var_scope, var_names); 5072 + OXC_free_hoisted_vars(var_names, buf_len); 5002 5073 } 5003 5074 5004 5075 static jsval_t js_block(struct js *js, bool create_scope) { ··· 6633 6704 6634 6705 jsval_t slot_name = get_slot(js, func_val, SLOT_NAME); 6635 6706 if (vtype(slot_name) == T_STR && vtype(func_val) == T_FUNC) { 6636 - jsoff_t len; 6637 - (void)vstr(js, slot_name, &len); 6638 - if (len > 0) { 6639 - jsval_t prop = mkprop(js, function_scope, slot_name, func_val, CONSTMASK); 6640 - (void)prop; 6641 - } 6707 + jsoff_t len; vstr(js, slot_name, &len); 6708 + if (len > 0) mkprop(js, function_scope, slot_name, func_val, CONSTMASK); 6642 6709 } 6710 + 6711 + jsval_t func_obj = mkval(T_OBJ, vdata(func_val)); 6712 + hoist_var_declarations_from_slot(js, function_scope, func_obj); 6643 6713 6644 6714 jsoff_t fnpos = 1; 6645 6715 int arg_idx = 0; ··· 22714 22784 } 22715 22785 22716 22786 hoist_function_declarations(js); 22717 - 22787 + if (!(js->flags & F_CALL)) hoist_var_declarations(js, js->scope); 22788 + 22718 22789 while (next(js) != TOK_EOF && !is_err(res)) { 22719 22790 res = js_stmt(js); 22720 22791 if (js->needs_gc && js->eval_depth == 1 && !js->gc_suppress) {
+43 -46
src/strip/Cargo.lock
··· 52 52 ] 53 53 54 54 [[package]] 55 - name = "bumpalo" 56 - version = "3.19.1" 57 - source = "registry+https://github.com/rust-lang/crates.io-index" 58 - checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" 59 - dependencies = [ 60 - "allocator-api2", 61 - ] 62 - 63 - [[package]] 64 55 name = "castaway" 65 56 version = "0.2.4" 66 57 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 153 144 154 145 [[package]] 155 146 name = "dragonbox_ecma" 156 - version = "0.0.5" 147 + version = "0.1.0" 157 148 source = "registry+https://github.com/rust-lang/crates.io-index" 158 - checksum = "d742b56656e8b14d63e7ea9806597b1849ae25412584c8adf78c0f67bd985e66" 149 + checksum = "6a5577f010d4e1bb3f3c4d6081e05718eb6992cf20119cab4d3abadff198b5ae" 159 150 160 151 [[package]] 161 152 name = "either" ··· 335 326 version = "0.0.0" 336 327 dependencies = [ 337 328 "oxc_allocator", 329 + "oxc_ast", 330 + "oxc_ast_visit", 338 331 "oxc_codegen", 339 332 "oxc_parser", 340 333 "oxc_semantic", ··· 386 379 387 380 [[package]] 388 381 name = "oxc_allocator" 389 - version = "0.107.0" 382 + version = "0.110.0" 390 383 source = "registry+https://github.com/rust-lang/crates.io-index" 391 - checksum = "377063b29ba9762002f07bd4bc5cdd60373a4e3b094e15ea201eec07556d65ff" 384 + checksum = "2174c7c8f77137b1bd1c653d7a5a531ae41f3b8fec1dd0251c801689784e7a2e" 392 385 dependencies = [ 393 386 "allocator-api2", 394 - "bumpalo", 395 387 "hashbrown", 396 388 "oxc_data_structures", 397 389 "rustc-hash", ··· 399 391 400 392 [[package]] 401 393 name = "oxc_ast" 402 - version = "0.107.0" 394 + version = "0.110.0" 403 395 source = "registry+https://github.com/rust-lang/crates.io-index" 404 - checksum = "ff4d2aed570578cfe5249d1e50736971b85c4eedc39f9777f735914e10d42dd7" 396 + checksum = "62f1902f97a5cac8767b76a1d8a1b3124e9db80c176ebbc98f75143dcc124a15" 405 397 dependencies = [ 406 398 "bitflags", 407 399 "oxc_allocator", ··· 416 408 417 409 [[package]] 418 410 name = "oxc_ast_macros" 419 - version = "0.107.0" 411 + version = "0.110.0" 420 412 source = "registry+https://github.com/rust-lang/crates.io-index" 421 - checksum = "d7d5f0089ba8705ef065f8acb371c3ab44e3a7fb90372cccac6788b677e763f1" 413 + checksum = "c5a31bd55516a98a35b2d99fa5813a3d3a5b798bad3262c819dfe7344bc6f390" 422 414 dependencies = [ 423 415 "phf", 424 416 "proc-macro2", ··· 428 420 429 421 [[package]] 430 422 name = "oxc_ast_visit" 431 - version = "0.107.0" 423 + version = "0.110.0" 432 424 source = "registry+https://github.com/rust-lang/crates.io-index" 433 - checksum = "45305537949b2471ce9afe251a2051af6ed2188492cd079b1d83b96caa6714b5" 425 + checksum = "e2c520a488c04ba5267223edd0bb245fb7f10e2358e8955802a5d962bb95b50a" 434 426 dependencies = [ 435 427 "oxc_allocator", 436 428 "oxc_ast", ··· 440 432 441 433 [[package]] 442 434 name = "oxc_codegen" 443 - version = "0.107.0" 435 + version = "0.110.0" 444 436 source = "registry+https://github.com/rust-lang/crates.io-index" 445 - checksum = "bcf3d7a97d49d6557d315f7b806a246a6627aec4942b9bd50e670686be6d3b00" 437 + checksum = "abfd3d146e6e0d340c183aa0e98f29ab1bba876c282350e5e06ab9d6f536eacd" 446 438 dependencies = [ 447 439 "bitflags", 448 440 "cow-utils", ··· 461 453 462 454 [[package]] 463 455 name = "oxc_compat" 464 - version = "0.107.0" 456 + version = "0.110.0" 465 457 source = "registry+https://github.com/rust-lang/crates.io-index" 466 - checksum = "8d772c9461d67a0bd1b8177674f73d7d666f8120bab90bfc37383eace93bcd82" 458 + checksum = "7319f12eb8d4a05737a7f71642d7a97aee210488dc4041a7a452352a31ac0fe6" 467 459 dependencies = [ 468 460 "cow-utils", 469 461 "oxc-browserslist", ··· 474 466 475 467 [[package]] 476 468 name = "oxc_data_structures" 477 - version = "0.107.0" 469 + version = "0.110.0" 478 470 source = "registry+https://github.com/rust-lang/crates.io-index" 479 - checksum = "b0264dbee1c186f744a433955a900a34f7a8bf6e4381d00100528d2b23043b21" 471 + checksum = "a42840ce8d83a08a92823dda6189e4d97359feca24a4fa732f3256c4614bb5a4" 480 472 dependencies = [ 481 473 "ropey", 482 474 ] 483 475 484 476 [[package]] 485 477 name = "oxc_diagnostics" 486 - version = "0.107.0" 478 + version = "0.110.0" 487 479 source = "registry+https://github.com/rust-lang/crates.io-index" 488 - checksum = "cd5d75130504c4b6792eba9a687349ec92cc7e220ddfc1cba35ef540c194b6ed" 480 + checksum = "b4f7b09c1563a67ede53af131f717b31ba89a992959ebad188b5158c21d4dc0a" 489 481 dependencies = [ 490 482 "cow-utils", 491 483 "oxc-miette", ··· 494 486 495 487 [[package]] 496 488 name = "oxc_ecmascript" 497 - version = "0.107.0" 489 + version = "0.110.0" 498 490 source = "registry+https://github.com/rust-lang/crates.io-index" 499 - checksum = "12400e655a18fe9ba1359324d7a634964f62d9a151008a48671e46abd9b728e1" 491 + checksum = "4813b352bd5b0b05badf0c9e6c5ec7ea58a6a7ab49bec8d18ead262624c6ef8d" 500 492 dependencies = [ 501 493 "cow-utils", 502 494 "num-bigint", 503 495 "num-traits", 504 496 "oxc_allocator", 505 497 "oxc_ast", 498 + "oxc_regular_expression", 506 499 "oxc_span", 507 500 "oxc_syntax", 508 501 ] 509 502 510 503 [[package]] 511 504 name = "oxc_estree" 512 - version = "0.107.0" 505 + version = "0.110.0" 513 506 source = "registry+https://github.com/rust-lang/crates.io-index" 514 - checksum = "e2059abb4c194b588d128c1933b6c20fde1b443b053e51da818caed17c272f65" 507 + checksum = "e54fb3effe995e6538d68070bf0a450b5ffd11dd41b62f11a4d01efa1f40e278" 515 508 516 509 [[package]] 517 510 name = "oxc_index" ··· 525 518 526 519 [[package]] 527 520 name = "oxc_parser" 528 - version = "0.107.0" 521 + version = "0.110.0" 529 522 source = "registry+https://github.com/rust-lang/crates.io-index" 530 - checksum = "090741583620244210f9988d13e7ddec78d8c22b13f885eb8d31c6566528c943" 523 + checksum = "5592bf8b64743944eb46528f9eabdde2b2435c8293cd502f5c183f9dff644e16" 531 524 dependencies = [ 532 525 "bitflags", 533 526 "cow-utils", ··· 548 541 549 542 [[package]] 550 543 name = "oxc_regular_expression" 551 - version = "0.107.0" 544 + version = "0.110.0" 552 545 source = "registry+https://github.com/rust-lang/crates.io-index" 553 - checksum = "47fe9458baea58cfe0d514b32a3258839f9cad1af738a92c1c55ccea97c5ceb7" 546 + checksum = "09de7f7e0fb82f54750e3a95346a828fd354b9aeac00f131719008733e66a18d" 554 547 dependencies = [ 555 548 "bitflags", 556 549 "oxc_allocator", ··· 564 557 565 558 [[package]] 566 559 name = "oxc_semantic" 567 - version = "0.107.0" 560 + version = "0.110.0" 568 561 source = "registry+https://github.com/rust-lang/crates.io-index" 569 - checksum = "e425a107259ff3417682df38a9964491013de0080aecbaab7e43eb1f452b6de1" 562 + checksum = "8c2269186b4f1510a76daf02914cb70e82a78549de451b8276bba0a419c62ac3" 570 563 dependencies = [ 571 564 "itertools", 572 565 "memchr", ··· 581 574 "oxc_syntax", 582 575 "rustc-hash", 583 576 "self_cell", 577 + "smallvec", 584 578 ] 585 579 586 580 [[package]] ··· 598 592 599 593 [[package]] 600 594 name = "oxc_span" 601 - version = "0.107.0" 595 + version = "0.110.0" 602 596 source = "registry+https://github.com/rust-lang/crates.io-index" 603 - checksum = "4eb492189296521cf459f4caaee8d7525c70341daf10083c53c962c13e4748ee" 597 + checksum = "2a42c0759b745eca0fe776890af46ce12e79e61796995e51a8eb9dcdf5516ab0" 604 598 dependencies = [ 605 599 "compact_str", 606 600 "oxc-miette", ··· 611 605 612 606 [[package]] 613 607 name = "oxc_syntax" 614 - version = "0.107.0" 608 + version = "0.110.0" 615 609 source = "registry+https://github.com/rust-lang/crates.io-index" 616 - checksum = "c09691f16fb7ed90acb442e88faca68751f2222dbed892122a9b50fc7d161c8d" 610 + checksum = "b63eac2e04a75a10c5714aeb753cdfa06b1abc66bbaa748b7994700f52c9b184" 617 611 dependencies = [ 618 612 "bitflags", 619 613 "cow-utils", ··· 631 625 632 626 [[package]] 633 627 name = "oxc_transformer" 634 - version = "0.107.0" 628 + version = "0.110.0" 635 629 source = "registry+https://github.com/rust-lang/crates.io-index" 636 - checksum = "11b2b72d1e073c39b4993d7dfd71daeb7ea6fb445f5c8b445be6998022ee4f76" 630 + checksum = "0e394bc5221c9e228fc06f54b7f7a3e2d63ed135a50b8678e8485b5b49222bb5" 637 631 dependencies = [ 638 632 "base64", 639 633 "compact_str", ··· 660 654 661 655 [[package]] 662 656 name = "oxc_traverse" 663 - version = "0.107.0" 657 + version = "0.110.0" 664 658 source = "registry+https://github.com/rust-lang/crates.io-index" 665 - checksum = "5dcaa9168b7ae1f7df2461e33210605bbe60dd2b638af08a7093310c232f1bda" 659 + checksum = "4473bf963b351d5b744b75aee9ff6aa41d62f8ca662012b03dc315cac9f1f2e5" 666 660 dependencies = [ 667 661 "itoa", 668 662 "oxc_allocator", ··· 872 866 version = "1.15.1" 873 867 source = "registry+https://github.com/rust-lang/crates.io-index" 874 868 checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" 869 + dependencies = [ 870 + "serde", 871 + ] 875 872 876 873 [[package]] 877 874 name = "smawk"
+8 -6
src/strip/Cargo.toml
··· 7 7 crate-type = ["staticlib", "cdylib"] 8 8 9 9 [dependencies] 10 - oxc_allocator = "0.107.0" 11 - oxc_parser = "0.107.0" 12 - oxc_span = "0.107.0" 13 - oxc_codegen = "0.107.0" 14 - oxc_transformer = "0.107.0" 15 - oxc_semantic = "0.107.0" 10 + oxc_allocator = "0.110.0" 11 + oxc_parser = "0.110.0" 12 + oxc_span = "0.110.0" 13 + oxc_codegen = "0.110.0" 14 + oxc_transformer = "0.110.0" 15 + oxc_semantic = "0.110.0" 16 + oxc_ast = "0.110.0" 17 + oxc_ast_visit = "0.110.0" 16 18 17 19 [profile.release] 18 20 lto = true
+14
src/strip/oxc.h
··· 16 16 size_t output_len 17 17 ); 18 18 19 + char *OXC_get_hoisted_vars( 20 + const char *input, 21 + size_t input_len, 22 + size_t *out_len 23 + ); 24 + 25 + char *OXC_get_func_hoisted_vars( 26 + const char *input, 27 + size_t input_len, 28 + size_t *out_len 29 + ); 30 + 31 + void OXC_free_hoisted_vars(char *ptr, size_t len); 32 + 19 33 #endif
+219 -10
src/strip/oxc.rs
··· 1 - use std::ffi::{c_char, c_int, CStr}; 1 + use std::ffi::{CStr, c_char, c_int}; 2 2 use std::path::Path; 3 3 use std::ptr; 4 4 5 5 use oxc_allocator::Allocator; 6 + use oxc_ast::ast::{BindingPattern, VariableDeclarationKind}; 7 + use oxc_ast_visit::{Visit, walk}; 6 8 use oxc_codegen::Codegen; 7 9 use oxc_parser::Parser; 8 - use oxc_semantic::SemanticBuilder; 10 + use oxc_semantic::{ScopeFlags, SemanticBuilder}; 9 11 use oxc_span::SourceType; 10 12 use oxc_transformer::{TransformOptions, Transformer, TypeScriptOptions}; 11 13 ··· 15 17 pub const OXC_ERR_OUTPUT_TOO_LARGE: c_int = -5; 16 18 17 19 #[unsafe(no_mangle)] 18 - pub unsafe extern "C" fn OXC_strip_types( 19 - input: *const c_char, 20 - filename: *const c_char, 21 - output: *mut c_char, 22 - output_len: usize, 23 - ) -> c_int { 20 + pub unsafe extern "C" fn OXC_strip_types(input: *const c_char, filename: *const c_char, output: *mut c_char, output_len: usize) -> c_int { 24 21 if input.is_null() || output.is_null() { 25 22 return OXC_ERR_NULL_INPUT; 26 23 } 27 - 24 + 28 25 let filename_str = match unsafe { CStr::from_ptr(filename).to_str() } { 29 26 Ok(s) => s, 30 27 Err(_) => return OXC_ERR_INVALID_UTF8, ··· 38 35 match strip_types_internal(input_str, filename_str) { 39 36 Ok(result) => { 40 37 let bytes = result.as_bytes(); 41 - if bytes.len() + 1 > output_len { return OXC_ERR_OUTPUT_TOO_LARGE; } 38 + if bytes.len() + 1 > output_len { 39 + return OXC_ERR_OUTPUT_TOO_LARGE; 40 + } 42 41 unsafe { 43 42 ptr::copy_nonoverlapping(bytes.as_ptr(), output as *mut u8, bytes.len()); 44 43 *output.add(bytes.len()) = 0; ··· 88 87 let output = Codegen::new().build(&program).code; 89 88 Ok(output) 90 89 } 90 + 91 + struct VarCollector<'a> { 92 + vars: Vec<&'a str>, 93 + func_depth: usize, 94 + target_depth: usize, 95 + } 96 + 97 + impl<'a> VarCollector<'a> { 98 + fn new() -> Self { 99 + Self { 100 + vars: Vec::new(), 101 + func_depth: 0, 102 + target_depth: 0, 103 + } 104 + } 105 + fn for_func_body() -> Self { 106 + Self { 107 + vars: Vec::new(), 108 + func_depth: 0, 109 + target_depth: 1, 110 + } 111 + } 112 + } 113 + 114 + impl<'a> Visit<'a> for VarCollector<'a> { 115 + fn visit_function(&mut self, func: &oxc_ast::ast::Function<'a>, flags: ScopeFlags) { 116 + self.func_depth += 1; 117 + walk::walk_function(self, func, flags); 118 + self.func_depth -= 1; 119 + } 120 + 121 + fn visit_arrow_function_expression(&mut self, expr: &oxc_ast::ast::ArrowFunctionExpression<'a>) { 122 + self.func_depth += 1; 123 + walk::walk_arrow_function_expression(self, expr); 124 + self.func_depth -= 1; 125 + } 126 + 127 + fn visit_variable_declaration(&mut self, decl: &oxc_ast::ast::VariableDeclaration<'a>) { 128 + if decl.kind == VariableDeclarationKind::Var && self.func_depth == self.target_depth { 129 + for declarator in &decl.declarations { 130 + self.extract_binding_names(&declarator.id); 131 + } 132 + } 133 + walk::walk_variable_declaration(self, decl); 134 + } 135 + } 136 + 137 + impl<'a> VarCollector<'a> { 138 + fn extract_binding_names(&mut self, pattern: &BindingPattern<'a>) { 139 + use BindingPattern::*; 140 + match pattern { 141 + BindingIdentifier(id) => self.vars.push(id.name.as_str()), 142 + ObjectPattern(obj) => { 143 + for prop in &obj.properties { 144 + self.extract_binding_names(&prop.value); 145 + } 146 + if let Some(rest) = &obj.rest { 147 + self.extract_binding_names(&rest.argument); 148 + } 149 + } 150 + ArrayPattern(arr) => { 151 + for elem in arr.elements.iter().flatten() { 152 + self.extract_binding_names(elem); 153 + } 154 + if let Some(rest) = &arr.rest { 155 + self.extract_binding_names(&rest.argument); 156 + } 157 + } 158 + AssignmentPattern(assign) => { 159 + self.extract_binding_names(&assign.left); 160 + } 161 + } 162 + } 163 + } 164 + 165 + fn collect_var_names(source: &str) -> Result<Vec<String>, String> { 166 + let allocator = Allocator::default(); 167 + let source_type = SourceType::mjs(); 168 + let parser_ret = Parser::new(&allocator, source, source_type).parse(); 169 + 170 + if !parser_ret.errors.is_empty() { 171 + let errors: Vec<String> = parser_ret.errors.iter().map(|e| e.to_string()).collect(); 172 + return Err(format!("Parse errors: {}", errors.join("; "))); 173 + } 174 + 175 + let mut collector = VarCollector::new(); 176 + collector.visit_program(&parser_ret.program); 177 + 178 + Ok(collector.vars.into_iter().map(String::from).collect()) 179 + } 180 + 181 + fn collect_var_names_from_func(source: &str) -> Result<Vec<String>, String> { 182 + let allocator = Allocator::default(); 183 + let source_type = SourceType::mjs(); 184 + 185 + let wrapped = format!("(function{})", source); 186 + let parser_ret = Parser::new(&allocator, &wrapped, source_type).parse(); 187 + 188 + if !parser_ret.errors.is_empty() { 189 + let errors: Vec<String> = parser_ret.errors.iter().map(|e| e.to_string()).collect(); 190 + return Err(format!("Parse errors: {}", errors.join("; "))); 191 + } 192 + 193 + let mut collector = VarCollector::for_func_body(); 194 + collector.visit_program(&parser_ret.program); 195 + 196 + Ok(collector.vars.into_iter().map(String::from).collect()) 197 + } 198 + 199 + #[unsafe(no_mangle)] 200 + pub unsafe extern "C" fn OXC_get_hoisted_vars(input: *const c_char, input_len: usize, out_len: *mut usize) -> *mut c_char { 201 + if input.is_null() || out_len.is_null() { 202 + return ptr::null_mut(); 203 + } 204 + 205 + let input_slice = unsafe { std::slice::from_raw_parts(input as *const u8, input_len) }; 206 + let input_str = match std::str::from_utf8(input_slice) { 207 + Ok(s) => s, 208 + Err(_) => return ptr::null_mut(), 209 + }; 210 + 211 + match collect_var_names(input_str) { 212 + Ok(vars) => { 213 + if vars.is_empty() { 214 + return ptr::null_mut(); 215 + } 216 + let total_len: usize = vars.iter().map(|s| s.len() + 1).sum::<usize>() + 1; 217 + 218 + let layout = std::alloc::Layout::from_size_align(total_len, 1).unwrap(); 219 + let ptr = unsafe { std::alloc::alloc(layout) as *mut c_char }; 220 + if ptr.is_null() { 221 + return ptr::null_mut(); 222 + } 223 + 224 + let mut offset = 0; 225 + for v in &vars { 226 + unsafe { 227 + ptr::copy_nonoverlapping(v.as_ptr(), ptr.add(offset) as *mut u8, v.len()); 228 + *ptr.add(offset + v.len()) = 0; 229 + } 230 + offset += v.len() + 1; 231 + } 232 + unsafe { 233 + *ptr.add(offset) = 0; 234 + } 235 + 236 + unsafe { 237 + *out_len = total_len; 238 + } 239 + ptr 240 + } 241 + Err(_) => ptr::null_mut(), 242 + } 243 + } 244 + 245 + #[unsafe(no_mangle)] 246 + pub unsafe extern "C" fn OXC_get_func_hoisted_vars(input: *const c_char, input_len: usize, out_len: *mut usize) -> *mut c_char { 247 + if input.is_null() || out_len.is_null() { 248 + return ptr::null_mut(); 249 + } 250 + 251 + let input_slice = unsafe { std::slice::from_raw_parts(input as *const u8, input_len) }; 252 + let input_str = match std::str::from_utf8(input_slice) { 253 + Ok(s) => s, 254 + Err(_) => return ptr::null_mut(), 255 + }; 256 + 257 + match collect_var_names_from_func(input_str) { 258 + Ok(vars) => { 259 + if vars.is_empty() { 260 + return ptr::null_mut(); 261 + } 262 + let total_len: usize = vars.iter().map(|s| s.len() + 1).sum::<usize>() + 1; 263 + 264 + let layout = std::alloc::Layout::from_size_align(total_len, 1).unwrap(); 265 + let ptr = unsafe { std::alloc::alloc(layout) as *mut c_char }; 266 + if ptr.is_null() { 267 + return ptr::null_mut(); 268 + } 269 + 270 + let mut offset = 0; 271 + for v in &vars { 272 + unsafe { 273 + ptr::copy_nonoverlapping(v.as_ptr(), ptr.add(offset) as *mut u8, v.len()); 274 + *ptr.add(offset + v.len()) = 0; 275 + } 276 + offset += v.len() + 1; 277 + } 278 + unsafe { 279 + *ptr.add(offset) = 0; 280 + } 281 + unsafe { 282 + *out_len = total_len; 283 + } 284 + ptr 285 + } 286 + Err(_) => ptr::null_mut(), 287 + } 288 + } 289 + 290 + #[unsafe(no_mangle)] 291 + pub unsafe extern "C" fn OXC_free_hoisted_vars(ptr: *mut c_char, len: usize) { 292 + if ptr.is_null() || len == 0 { 293 + return; 294 + } 295 + let layout = std::alloc::Layout::from_size_align(len, 1).unwrap(); 296 + unsafe { 297 + std::alloc::dealloc(ptr as *mut u8, layout); 298 + } 299 + }
+38
tests/hoist.js
··· 1 + function hoist1() { 2 + b(); // b is hoisted from below 3 + function b() { 4 + console.log('b'); 5 + } 6 + } 7 + 8 + function hoist2() { 9 + b(); // b exists but is undefined 10 + var b = function b() { 11 + console.log('b'); 12 + }; 13 + } 14 + 15 + function hoist3() { 16 + b(); // b does not exist 17 + let b = function b() { 18 + console.log('b'); 19 + }; 20 + } 21 + 22 + try { 23 + hoist1(); 24 + } catch (err) { 25 + console.log(err); 26 + } 27 + 28 + try { 29 + hoist2(); 30 + } catch (err) { 31 + console.log(err); 32 + } 33 + 34 + try { 35 + hoist3(); 36 + } catch (err) { 37 + console.log(err); 38 + }