aboutsummaryrefslogtreecommitdiff
path: root/src/main_wasm.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/main_wasm.zig')
-rw-r--r--src/main_wasm.zig344
1 files changed, 322 insertions, 22 deletions
diff --git a/src/main_wasm.zig b/src/main_wasm.zig
index d58ce60a0..e7fd55d80 100644
--- a/src/main_wasm.zig
+++ b/src/main_wasm.zig
@@ -1,3 +1,4 @@
+const bun = @import("root").bun;
const JSParser = bun.js_parser;
const JSPrinter = bun.js_printer;
const JSAst = bun.JSAst;
@@ -181,26 +182,325 @@ var output_stream = std.io.fixedBufferStream(&output_stream_buf);
var error_stream_buf: [16384]u8 = undefined;
var error_stream = std.io.fixedBufferStream(&error_stream_buf);
var output_source: global.Output.Source = undefined;
-export fn init() void {
+var init_counter: usize = 0;
+export fn init(heapsize: u32) void {
const Mimalloc = @import("./allocators/mimalloc.zig");
- // reserve 256 MB upfront
- Mimalloc.mi_option_set(Mimalloc.mi_option_t.allow_decommit, 0);
- Mimalloc.mi_option_set(Mimalloc.mi_option_t.limit_os_alloc, 1);
- _ = Mimalloc.mi_reserve_os_memory(2.56e+8, false, true);
-
- output_source = global.Output.Source.init(output_stream, error_stream);
- global.Output.Source.set(&output_source);
- JSAst.Stmt.Data.Store.create(default_allocator);
- JSAst.Expr.Data.Store.create(default_allocator);
- buffer_writer = JSPrinter.BufferWriter.init(default_allocator) catch unreachable;
- buffer_writer.buffer.growBy(1024) catch unreachable;
- writer = JSPrinter.BufferPrinter.init(buffer_writer);
- define = Define.Define.init(default_allocator, null, null) catch unreachable;
+ defer init_counter +%= 1;
+ if (init_counter == 0) {
+
+ // reserve 256 MB upfront
+ Mimalloc.mi_option_set(.allow_decommit, 0);
+ Mimalloc.mi_option_set(.limit_os_alloc, 1);
+ _ = Mimalloc.mi_reserve_os_memory(heapsize, false, true);
+
+ JSAst.Stmt.Data.Store.create(default_allocator);
+ JSAst.Expr.Data.Store.create(default_allocator);
+ buffer_writer = JSPrinter.BufferWriter.init(default_allocator) catch unreachable;
+ buffer_writer.buffer.growBy(1024) catch unreachable;
+ writer = JSPrinter.BufferPrinter.init(buffer_writer);
+ define = Define.Define.init(default_allocator, null, null) catch unreachable;
+ output_source = global.Output.Source.init(output_stream, error_stream);
+ global.Output.Source.set(&output_source);
+ } else {
+ buffer_writer = writer.ctx;
+ }
}
const Arena = @import("./mimalloc_arena.zig").Arena;
var log: Logger.Log = undefined;
+const TestAnalyzer = struct {
+ string_buffer: std.ArrayList(u8),
+ items: std.ArrayList(Api.TestResponseItem),
+
+ pub fn visitExpr(this: *TestAnalyzer, parser: *bun.js_parser.TSXParser, expr: JSAst.Expr) !void {
+ switch (expr.data) {
+ .e_call => |call| {
+ if (call.target.isRef(parser.jest.@"test") or call.target.isRef(parser.jest.it) or call.target.isRef(parser.jest.describe)) {
+ if (call.args.len > 0) {
+ const label_expr: JSAst.Expr = call.args.slice()[0];
+ switch (label_expr.data) {
+ .e_string => |str| {
+ try str.toUTF8(this.string_buffer.allocator);
+ const ptr = Api.StringPointer{
+ .offset = this.string_buffer.items.len,
+ .length = str.data.len,
+ };
+ try this.string_buffer.appendSlice(str.data);
+ try this.items.append(Api.TestResponseItem{
+ .byte_offset = expr.loc.start,
+ .kind = if (call.target.isRef(parser.jest.describe)) Api.TestKind.describe_fn else .test_fn,
+ .label = ptr,
+ });
+ },
+ .e_dot => {},
+ else => {},
+ }
+
+ return;
+ }
+ } else if (call.target.data == .e_dot and bun.strings.eqlComptime(call.target.data.e_dot.name, "only")) {
+ const target = call.target.data.e_dot.target;
+ if (target.isRef(parser.jest.@"test") or target.isRef(parser.jest.it) or target.isRef(parser.jest.describe)) {
+ if (call.args.len > 0) {
+ const label_expr: JSAst.Expr = call.args.slice()[0];
+ switch (label_expr.data) {
+ .e_string => |str| {
+ try str.toUTF8(this.string_buffer.allocator);
+ const ptr = Api.StringPointer{
+ .offset = this.string_buffer.items.len,
+ .length = str.data.len,
+ };
+ try this.string_buffer.appendSlice(str.data);
+ try this.items.append(Api.TestResponseItem{
+ .byte_offset = expr.loc.start,
+ .kind = if (target.isRef(parser.jest.describe)) Api.TestKind.describe_fn else .test_fn,
+ .label = ptr,
+ });
+ },
+ .e_dot => {},
+ else => {},
+ }
+
+ return;
+ }
+ }
+ }
+
+ try this.visitExpr(parser, call.target);
+ for (call.args.slice()) |arg| {
+ try this.visitExpr(parser, arg);
+ }
+ },
+ .e_binary => |bin| {
+ try this.visitExpr(parser, bin.left);
+ try this.visitExpr(parser, bin.right);
+ },
+ .e_new => |new| {
+ try this.visitExpr(parser, new.target);
+ for (new.args.slice()) |arg| {
+ try this.visitExpr(parser, arg);
+ }
+ },
+
+ .e_array => |arr| {
+ for (arr.items.slice()) |item| {
+ try this.visitExpr(parser, item);
+ }
+ },
+
+ .e_if => |if_| {
+ try this.visitExpr(parser, if_.no);
+ try this.visitExpr(parser, if_.test_);
+ try this.visitExpr(parser, if_.yes);
+ },
+
+ .e_function => |func| {
+ for (func.func.body.stmts) |stmt| {
+ try this.visitStmt(parser, stmt);
+ }
+ },
+
+ .e_arrow => |arrow| {
+ for (arrow.body.stmts) |stmt| {
+ try this.visitStmt(parser, stmt);
+ }
+ },
+ else => {},
+ }
+ }
+
+ pub fn visitStmt(this: *TestAnalyzer, parser: *bun.js_parser.TSXParser, stmt: JSAst.Stmt) anyerror!void {
+ switch (stmt.data) {
+ .s_block => |s| {
+ for (s.stmts) |s2| {
+ try this.visitStmt(parser, s2);
+ }
+ },
+ .s_do_while => |s| {
+ try this.visitStmt(parser, s.body);
+ try this.visitExpr(parser, s.test_);
+ },
+ .s_expr => |s| {
+ try this.visitExpr(parser, s.value);
+ },
+ .s_for_in => |s| {
+ try this.visitStmt(parser, s.init);
+ try this.visitStmt(parser, s.body);
+ try this.visitExpr(parser, s.value);
+ },
+ .s_for_of => |s| {
+ try this.visitStmt(parser, s.init);
+ try this.visitStmt(parser, s.body);
+ try this.visitExpr(parser, s.value);
+ },
+ .s_for => |s| {
+ if (s.init) |i| {
+ try this.visitStmt(parser, i);
+ }
+ if (s.test_) |i| {
+ try this.visitExpr(parser, i);
+ }
+ if (s.update) |i| {
+ try this.visitExpr(parser, i);
+ }
+
+ try this.visitStmt(parser, s.body);
+ },
+ .s_function => |s| {
+ for (s.func.args) |arg| {
+ if (arg.default) |def| {
+ try this.visitExpr(parser, def);
+ }
+ }
+
+ for (s.func.body.stmts) |s2| {
+ try this.visitStmt(parser, s2);
+ }
+ },
+ .s_if => |s| {
+ try this.visitExpr(parser, s.test_);
+ try this.visitStmt(parser, s.yes);
+ if (s.no) |no| {
+ try this.visitStmt(parser, no);
+ }
+ },
+ .s_local => |s| {
+ for (s.decls.slice()) |decl| {
+ if (decl.value) |val| {
+ try this.visitExpr(parser, val);
+ }
+ }
+ },
+ .s_switch => |s| {
+ try this.visitExpr(parser, s.test_);
+ for (s.cases) |c| {
+ for (c.body) |t| {
+ try this.visitStmt(parser, t);
+ }
+ if (c.value) |e2| {
+ try this.visitExpr(parser, e2);
+ }
+ }
+ },
+ .s_throw => |s| {
+ try this.visitExpr(parser, s.value);
+ },
+ .s_try => |s| {
+ for (s.body) |s2| {
+ try this.visitStmt(parser, s2);
+ }
+ if (s.catch_) |c| {
+ for (c.body) |s2| {
+ try this.visitStmt(parser, s2);
+ }
+ }
+ if (s.finally) |f| {
+ for (f.stmts) |s2| {
+ try this.visitStmt(parser, s2);
+ }
+ }
+ },
+ .s_while => |s| {
+ try this.visitExpr(parser, s.test_);
+ try this.visitStmt(parser, s.body);
+ },
+
+ .s_import => |import| {
+ if (bun.strings.eqlComptime(parser.import_records.items[import.import_record_index].path.text, "bun:test")) {
+ for (import.items) |item| {
+ const clause: bun.JSAst.ClauseItem = item;
+ if (bun.strings.eqlComptime(clause.alias, "test")) {
+ parser.jest.@"test" = clause.name.ref.?;
+ } else if (bun.strings.eqlComptime(clause.alias, "it")) {
+ parser.jest.it = clause.name.ref.?;
+ } else if (bun.strings.eqlComptime(clause.alias, "describe")) {
+ parser.jest.describe = clause.name.ref.?;
+ }
+ }
+ }
+ },
+ else => {},
+ }
+ }
+
+ pub fn visitParts(
+ this: *TestAnalyzer,
+ parser: *bun.js_parser.TSXParser,
+ parts: []bun.JSAst.Part,
+ ) anyerror!void {
+ var jest = &parser.jest;
+ if (parser.symbols.items[jest.it.innerIndex()].use_count_estimate == 0) {
+ if (parser.symbols.items[jest.it.innerIndex()].use_count_estimate > 0) {
+ jest.@"test" = jest.it;
+ }
+ } else if (parser.symbols.items[jest.@"test".innerIndex()].use_count_estimate == 0) {
+ if (parser.symbols.items[jest.it.innerIndex()].use_count_estimate > 0) {
+ jest.@"test" = jest.it;
+ }
+ }
+
+ for (parts) |part| {
+ for (part.stmts) |stmt| {
+ try this.visitStmt(parser, stmt);
+ }
+ }
+ }
+};
+export fn getTests(opts_array: u64) u64 {
+ var arena = Arena.init() catch unreachable;
+ var allocator = arena.allocator();
+ defer arena.deinit();
+ var log_ = Logger.Log.init(allocator);
+ var reader = ApiReader.init(Uint8Array.fromJS(opts_array), allocator);
+ var opts = Api.GetTestsRequest.decode(&reader) catch @panic("out of memory");
+ var code = Logger.Source.initPathString(if (opts.path.len > 0) opts.path else "my-test-file.test.tsx", opts.contents);
+ code.contents_is_recycled = true;
+ defer {
+ JSAst.Stmt.Data.Store.reset();
+ JSAst.Expr.Data.Store.reset();
+ }
+
+ var parser = JSParser.Parser.init(.{
+ .jsx = .{},
+ .ts = true,
+ }, &log_, &code, define, allocator) catch @panic("out of memory");
+
+ var anaylzer = TestAnalyzer{
+ .items = std.ArrayList(
+ Api.TestResponseItem,
+ ).init(allocator),
+ .string_buffer = std.ArrayList(
+ u8,
+ ).init(allocator),
+ };
+ parser.options.features.inject_jest_globals = true;
+ parser.options.features.commonjs_at_runtime = true;
+ parser.options.features.top_level_await = true;
+
+ parser.analyze(&anaylzer, @ptrCast(&TestAnalyzer.visitParts)) catch |err| {
+ Output.print("Error: {s}\n", .{@errorName(err)});
+
+ if (@errorReturnTrace()) |trace| {
+ Output.print("{}\n", .{trace});
+ }
+ log_.printForLogLevel(Output.writer()) catch unreachable;
+ return 0;
+ };
+
+ var output = std.ArrayList(u8).init(default_allocator);
+ var output_writer = output.writer();
+ const Encoder = ApiWriter(@TypeOf(output_writer));
+ var encoder = Encoder.init(output_writer);
+ var response = Api.GetTestsResponse{
+ .tests = anaylzer.items.items,
+ .contents = anaylzer.string_buffer.items,
+ };
+
+ response.encode(&encoder) catch return 0;
+ return @as(u64, @bitCast([2]u32{ @intFromPtr(output.items.ptr), output.items.len }));
+}
+
export fn transform(opts_array: u64) u64 {
// var arena = @import("root").bun.ArenaAllocator.init(default_allocator);
var arena = Arena.init() catch unreachable;
@@ -235,8 +535,8 @@ export fn transform(opts_array: u64) u64 {
parser.options.tree_shaking = false;
parser.options.features.top_level_await = true;
const result = parser.parse() catch unreachable;
- if (result.ok) {
- var symbols: [][]JSAst.Symbol = &([_][]JSAst.Symbol{result.ast.symbols});
+ if (result == .ast and log.errors == 0) {
+ var symbols = JSAst.Symbol.NestedList.init(&[_]JSAst.Symbol.List{result.ast.symbols});
_ = JSPrinter.printAst(
@TypeOf(&writer),
@@ -246,8 +546,6 @@ export fn transform(opts_array: u64) u64 {
&code,
false,
.{},
- void,
- null,
false,
) catch 0;
@@ -260,7 +558,7 @@ export fn transform(opts_array: u64) u64 {
}
transform_response = Api.TransformResponse{
- .status = if (result.ok) Api.TransformResponseStatus.success else Api.TransformResponseStatus.fail,
+ .status = if (result == .ast and log.errors == 0) Api.TransformResponseStatus.success else Api.TransformResponseStatus.fail,
.files = &output_files,
.errors = (log.toAPI(allocator) catch unreachable).msgs,
};
@@ -269,7 +567,7 @@ export fn transform(opts_array: u64) u64 {
var output_writer = output.writer();
const Encoder = ApiWriter(@TypeOf(output_writer));
var encoder = Encoder.init(output_writer);
- transform_response.encode(&encoder) catch unreachable;
+ transform_response.encode(&encoder) catch {};
return @as(u64, @bitCast([2]u32{ @intFromPtr(output.items.ptr), output.items.len }));
}
@@ -311,10 +609,10 @@ export fn scan(opts_array: u64) u64 {
var output_writer = output.writer();
const Encoder = ApiWriter(@TypeOf(output_writer));
- if (result.ok) {
+ if (result == .ast) {
var scanned_imports = allocator.alloc(Api.ScannedImport, result.ast.import_records.len) catch unreachable;
var scanned_i: usize = 0;
- for (result.ast.import_records) |import_record| {
+ for (result.ast.import_records.slice()) |import_record| {
if (import_record.kind == .internal) continue;
scanned_imports[scanned_i] = Api.ScannedImport{ .path = import_record.path.text, .kind = import_record.kind.toAPI() };
scanned_i += 1;
@@ -337,6 +635,7 @@ export fn emsc_main() void {
_ = transform;
_ = bun_free;
_ = bun_malloc;
+ _ = getTests;
}
comptime {
@@ -347,4 +646,5 @@ comptime {
_ = bun_free;
_ = scan;
_ = bun_malloc;
+ _ = getTests;
}